Ben Lin
2024-11-08 3d2c48733b86a03fc2e5a1f12ac3667ab0863b80
src/components/FlowChart/src/FlowChart.vue
@@ -1,347 +1,285 @@
<template>
  <div class="h-full" :class="prefixCls">
    <FlowChartToolbar :prefixCls="prefixCls" v-if="toolbar" @view-data="handlePreview"
    <FlowChartToolbar
      :prefixCls="prefixCls"
      v-if="toolbar"
      @view-data="handlePreview"
      @save-data="handleSave"
      @add-data="handleAdd"/>
      <a-layout style="min-height: 100vh">
      <a-layout-content>
        <div ref="lfElRef" class="h-full"></div>
      </a-layout-content>
      <a-layout-sider
        width="450"
        style="background: #fafafa; border-left: 1px solid #d9d9d9; padding: 20px"
        ><BasicForm
          autoFocusFirstItem
          :labelWidth="80"
          :schemas="schemas"
          :actionColOptions="{ span: 24 }"
          :submitButtonOptions="{ text: '确定' }"
          @submit="handleSubmit"
          @reset="handleReset"
      /></a-layout-sider>
    </a-layout>
      @add-data="handleAdd"
      @addlf="handleAddlf"
      @undo="handleUndo"
      @redo="handleRedo"
    />
    <div ref="lfElRef" class="h-full"></div>
    <BasicModal @register="register" title="流程数据" width="50%">
      <JsonPreview :data="graphData" />
    </BasicModal>
  </div>
</template>
<script lang="ts">
<script lang="ts" setup>
  import type { Ref } from 'vue';
  import type { Definition } from '@logicflow/core';
  import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue';
  import { ref, onMounted, unref, nextTick, computed, watch } from 'vue';
  import FlowChartToolbar from './FlowChartToolbar.vue';
  import LogicFlow from '@logicflow/core';
  import { Snapshot, BpmnElement, Menu, DndPanel, SelectionSelect } from '@logicflow/extension';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { useAppStore } from '/@/store/modules/app';
  import { useDesign } from '@/hooks/web/useDesign';
  import { useAppStore } from '@/store/modules/app';
  import { createFlowChartContext } from './useFlowContext';
  // import { toLogicFlowData } from './adpterForTurbo';
  import { useModal, BasicModal } from '/@/components/Modal';
  import { JsonPreview } from '/@/components/CodeEditor';
  import { toLogicFlowData } from './adpterForTurbo';
  import { useModal, BasicModal } from '@/components/Modal';
  import { JsonPreview } from '@/components/CodeEditor';
  import { configDefaultDndPanel } from './config';
  import '@logicflow/core/dist/style/index.css';
  import '@logicflow/core/lib/style/index.css';
  // import '@logicflow/core/dist/style/index.css';
  import '@logicflow/extension/lib/style/index.css';
  import { BasicForm, FormSchema } from '/@/components/Form/index';
  import { getOperAllList } from '/@/api/tigerapi/mes/oper';
  import { useGlobSetting } from '/@/hooks/setting';
  import customEdge from './customEdge';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { useI18n } from '/@/hooks/web/useI18n';
  import actionRect from './actionRect';
  import TestNode from './TestNode';
  import CollectNode from './CollectNode';
  import AssemblyNode from './AssemblyNode';
  import PackingNode from './PackingNode';
  import RepairNode from './RepairNode';
import SpecPackingNode from './SpecPackingNode';
  const isRuter = (type: string) => type === '0';
  const isOper = (type: string) => type === '1';
  const schemas: FormSchema[] = [
    // {
    //   field: 'divider-basic',
    //   component: 'Divider',
    //   label: '属性页',
    // },
    {
      field: 'func_type',
      label: '属性页',
      component: 'RadioButtonGroup',
      defaultValue: '0',
      componentProps: {
        options: [
          { label: '工艺流程', value: '0' },
          { label: '工序', value: '1' },
        ],
      },
      colProps: { lg: 24, md: 24 },
  defineOptions({ name: 'FlowChart' });
  const props = defineProps({
    flowOptions: {
      type: Object as PropType<Definition>,
      default: () => ({}),
    },
    {
      field: 'OPER_CODE',
      component: 'ApiSelect',
      label: '选择工序',
      componentProps: {
        api: getOperAllList,
        // params: {
        //   id: 1,
        // },
        resultField: 'items',
        labelField: 'OPER_NAME',
        valueField: 'OPER_CODE',
        // not request untill to select
        immediate: false,
        onChange: (e) => {
          console.log('selected:', e);
        },
        // atfer request callback
        onOptionsChange: (options) => {
          console.log('get options', options.length, options);
        },
      },
      ifShow: ({ values }) => isOper(values.func_type),
    data: {
      type: Object as PropType<any>,
      default: () => ({}),
    },
    {
      field: 'field2',
      component: 'Input',
      label: '工序类型',
      // componentProps:{},
      // can func
      componentProps: ({ schema, formModel }) => {
        console.log('form:', schema);
        console.log('formModel:', formModel);
        return {
          placeholder: '测试',
          onChange: (e: any) => {
            console.log(e);
          },
        };
      },
      renderComponentContent: () => {
        return {
          prefix: () => '工序',
          suffix: () => '类型',
        };
      },
    toolbar: {
      type: Boolean,
      default: true,
    },
    {
      field: 'field3',
      component: 'Select',
      label: '工序行为',
      componentProps: {
        options: [
          {
            label: '存储过程',
            value: '1',
            key: '1',
          },
          {
            label: '条码规则',
            value: '2',
            key: '2',
          },
          {
            label: '行为一',
            value: '3',
            key: '3',
          },
          {
            label: '行为二',
            value: '4',
            key: '4',
          },
          {
            label: '行为三',
            value: '5',
            key: '5',
          },
        ],
      },
    },
    {
      field: 'field4',
      component: 'Switch',
      label: '是否计算直通率',
      colProps: {
        span: 16,
      },
      labelWidth: 200,
    },
    {
      field: 'field5',
      component: 'Switch',
      label: '是否可跳站',
      colProps: {
        span: 16,
      },
      labelWidth: 200,
    },
  ];
  export default defineComponent({
    name: 'FlowChart',
    components: { BasicModal, FlowChartToolbar, JsonPreview, BasicForm },
    props: {
      flowOptions: {
        type: Object as PropType<Definition>,
        default: () => ({}),
      },
      data: {
        type: Object as PropType<any>,
        default: () => ({}),
      },
      toolbar: {
        type: Boolean,
        default: true,
      },
      patternItems: {
        type: Array,
      },
    },
    setup(props) {
      const globSetting = useGlobSetting();
      const lfElRef = ref(null);
      const graphData = ref({});
      const lfInstance = ref(null) as Ref<LogicFlow | null>;
      const { prefixCls } = useDesign('flow-chart');
      const appStore = useAppStore();
      const [register, { openModal }] = useModal();
      createFlowChartContext({
        logicFlow: lfInstance as unknown as LogicFlow,
      });
      const getFlowOptions = computed(() => {
        const { flowOptions } = props;
        const defaultOptions: Partial<Definition> = {
          grid: true,
          background: {
            color: appStore.getDarkMode === 'light' ? '#f7f9ff' : '#151515',
          },
          keyboard: {
            enabled: true,
          },
          edgeType: 'bezier',
          ...flowOptions,
        };
        return defaultOptions as Definition;
      });
      watch(
        () => props.data,
        () => {
          onRender();
        },
      );
      // TODO
      // watch(
      //   () => appStore.getDarkMode,
      //   () => {
      //     init();
      //   }
      // );
      watch(
        () => unref(getFlowOptions),
        (options) => {
          unref(lfInstance)?.updateEditConfig(options);
        },
      );
      // init logicFlow
      async function init() {
        await nextTick();
        const lfEl = unref(lfElRef);
        if (!lfEl) {
          return;
        }
        LogicFlow.use(DndPanel);
        // Canvas configuration
        LogicFlow.use(Snapshot);
        // Use the bpmn plug-in to introduce bpmn elements, which can be used after conversion in turbo
        LogicFlow.use(BpmnElement);
        // Start the right-click menu
        LogicFlow.use(Menu);
        LogicFlow.use(SelectionSelect);
        lfInstance.value = new LogicFlow({
          ...unref(getFlowOptions),
          container: lfEl,
          edgeGenerator: (sourceNode) => {
            // console.log('a');
            // 起始节点类型 rect 时使用 自定义的边 custom-edge
            if (sourceNode.properties.isReturn) return 'custom-edge';
            // if (sourceNode.type === 'rect') return 'custom-edge';
            // return 'custom-edge';
          },
        });
        const lf = unref(lfInstance)!;
        // lf?.setDefaultEdgeType('line');
        onRender();
        lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf));
      }
      async function onRender() {
        await nextTick();
        const lf = unref(lfInstance);
        if (!lf) {
          return;
        }
        lf.register(customEdge);
        // const lFData = toLogicFlowData(props.data);
        lf.render(props.data);
        if (globSetting.apiUrl == 'http://localhost:9528/api') {
          lf.on('anchor:drop', (data) => {
            const nodeData = data.nodeModel.getData();
            if (nodeData.properties.isReturn === true) {
              data.nodeModel.graphModel.edges.forEach((element) => {
                if (element.sourceNodeId === data.nodeModel.id) {
                  lf.changeEdgeType(element.id, 'custom-edge');
                }
              });
            }
          });
        }
      }
      function handlePreview() {
        const lf = unref(lfInstance);
        if (!lf) {
          return;
        }
        graphData.value = unref(lf).getGraphData();
        openModal();
      }
      function handleSave() {
        console.log('handleSave');
        const lf = unref(lfInstance);
        if (!lf) {
          return;
        }
      }
      function handleAdd() {
        const lf = unref(lfInstance);
        console.log('handleAdd');
        if (!lf) {
          return;
        }
        // lf.clearData();
        lf.render({});
      }
      onMounted(init);
      return {
        register,
        prefixCls,
        lfElRef,
        handlePreview,
        graphData,
        schemas,
        handleReset: () => {},
        handleSubmit: (_values: any) => {},
        handleSave,
        handleAdd,
      };
    patternItems: {
      type: Array,
    },
  });
  const emit = defineEmits([
    'view-data',
    'save-data',
    'add-lf',
    'select-node',
    'click-blank',
    'init',
    'undo',
    'redo',
    'anchor-drop',
    'anchor-dragend',
  ]);
  const lfElRef = ref(null);
  const graphData = ref({});
  const lfInstance = ref(null) as Ref<LogicFlow | null>;
  const { prefixCls } = useDesign('flow-chart');
  const appStore = useAppStore();
  const [register, { openModal }] = useModal();
  createFlowChartContext({
    logicFlow: lfInstance as unknown as LogicFlow,
  });
  const getFlowOptions = computed(() => {
    const { flowOptions } = props;
    const defaultOptions: Partial<Definition> = {
      grid: true,
      background: {
        color: appStore.getDarkMode === 'light' ? '#f7f9ff' : '#151515',
      },
      keyboard: {
        enabled: true,
      },
      edgeType: 'polyline',
      ...flowOptions,
    };
    return defaultOptions as Definition;
  });
  watch(
    () => props.data,
    () => {
      onRender();
    },
  );
  // TODO
  // watch(
  //   () => appStore.getDarkMode,
  //   () => {
  //     init();
  //   }
  // );
  watch(
    () => unref(getFlowOptions),
    (options) => {
      unref(lfInstance)?.updateEditConfig(options);
    },
  );
  // init logicFlow
  async function init() {
    await nextTick();
    const lfEl = unref(lfElRef);
    if (!lfEl) {
      return;
    }
    LogicFlow.use(DndPanel);
    // Canvas configuration
    LogicFlow.use(Snapshot);
    // Use the bpmn plug-in to introduce bpmn elements, which can be used after conversion in turbo
    LogicFlow.use(BpmnElement);
    // Start the right-click menu
    LogicFlow.use(Menu);
    LogicFlow.use(SelectionSelect);
    lfInstance.value = new LogicFlow({
      ...unref(getFlowOptions),
      container: lfEl,
      edgeGenerator: (sourceNode) => {
        // console.log('a');
        // 起始节点类型 rect 时使用 自定义的边 custom-edge
        if (sourceNode.properties.isReturn) return 'custom-edge';
        // if (sourceNode.type === 'rect') return 'custom-edge';
        // return 'custom-edge';
      },
    });
    const lf = unref(lfInstance)!;
    lf?.setDefaultEdgeType('polyline');
    lf.register(actionRect);
    lf.register(TestNode);
    lf.register(CollectNode);
    lf.register(AssemblyNode);
    lf.register(PackingNode);
    lf.register(RepairNode);
    lf.register(customEdge);
    lf.register(SpecPackingNode);
    onRender();
    lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf));
  }
  async function onRender() {
    await nextTick();
    const lf = unref(lfInstance);
    if (!lf) {
      return;
    }
    const lFData = toLogicFlowData(props.data);
    lf.render(lFData);
    lf.on('anchor:drop', (data) => {
      const nodeData = data.nodeModel.getData();
      if (nodeData.properties.isReturn === true) {
        console.log(11, nodeData.properties.isReturn);
        data.nodeModel.graphModel.edges.forEach((element) => {
          if (element.sourceNodeId === data.nodeModel.id) {
            lf.changeEdgeType(element.id, 'custom-edge');
          }
        });
      }
      emit('anchor-drop', data, lf);
    });
    /**
     * @description: 锚点连线结束,不管是否创建连线都会触发。
     * @return {*}
     */
    lf.on('anchor:dragend', (data, e, nodeModel) => {
      emit('anchor-dragend',data, e, nodeModel, lf);
    });
    lf.on('node:click,edge:click', (data) => {
      if (data.isSelected) {
        console.log(data.data.text.value, data.isSelected);
        // notification.success({
        //   message: t('点击了节点'),
        //   description: `${data.data.text.value}: ${data.isSelected}`,
        //   duration: 3,
        // });
        emit('select-node', data, lf);
      } else {
        console.log(data.data.type);
      }
    });
    lf.on('blank:mousedown', (e) => {
      emit('click-blank');
    });
    lf.on('node:dnd-add', (data, e) => {
      console.log('node:dnd-add', data);
      emit('select-node', data, lf);
    });
    emit('init', lf);
  }
  function handlePreview() {
    const lf = unref(lfInstance);
    if (!lf) {
      return;
    }
    graphData.value = unref(lf).getGraphData();
    openModal();
  }
  function handleSave() {
    // console.log('handleSave');
    const lf = unref(lfInstance);
    if (!lf) {
      return;
    }
    emit('save-data', lf);
  }
  function handleAdd() {
    const lf = unref(lfInstance);
    console.log('handleAdd');
    if (!lf) {
      return;
    }
    // lf.clearData();
    lf.render({});
  }
  function handleAddlf() {
    const lf = unref(lfInstance);
    if (!lf) {
      return;
    }
    emit('add-lf', lf);
  }
  function handleUndo() {
    const lf = unref(lfInstance);
    if (!lf) {
      return;
    }
    emit('undo', lf);
  }
  function handleRedo() {
    const lf = unref(lfInstance);
    if (!lf) {
      return;
    }
    emit('redo', lf);
  }
  onMounted(init);
</script>