ludc
2024-08-14 24fa2e63f7c155c87457980e6c656f3891132a47
Source/plt-web/plt-web-ui/src/components/flow-cycle/flowchartEditor.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,294 @@
<template>
  <vue-flowchart-editor ref="flowChart" class="vue-flowchart-editor">
    <div class="vfe-chart">
      <!-- é¡¶éƒ¨èœå• -->
      <div v-if="type !== 'detail'" class="vfe-chart-header">
        <editor-toolbar @handler-save="handlerSave" @reset-tree="handleResetTree"  :chart-data="chartData"/>
      </div>
      <div class="vfe-chart-container">
        <!-- å·¦ä¾§é¡¹ç›®æ  -->
        <div v-if="disabledBtn" class="vfe-chart-sidebar">
          <editor-item-panel  :node-items="chartDataNodeItems"/>
        </div>
        <!-- ä¸»å›¾è¡¨ -->
        <div class="vfe-chart-main">
          <flow
            :data="chartData"
            :onAfterChange="onAfterChange"
            :onAfterItemSelected="onAfterItemSelected"
          />
          <!-- æç¤ºæ¡† -->
          <div class="tooltip">
            <template v-for="item in tooltipData">
              <p>{{ item.name }}: {{ item.value }}</p>
            </template>
          </div>
        </div>
        <div v-if="type !== 'detail'" class="vfe-chart-panel">
          <div class="vfe-chart-panel-detail">
            <editor-detail-panel
              ref="EditorDetailPanel"
              :currentSelectedLine="currentSelectedLine"
              :existEdges="existEdges"
              :existNodes="existNodes"
              :rowData="rowData"
              :type="type"
            />
          </div>
        </div>
      </div>
    </div>
    <!-- å³é”®èœå• -->
    <editor-context-menu v-if="type !== 'detail'"/>
    <!-- è‡ªå®šä¹‰è¾¹é…ç½® -->
    <register-edge
      :config="customEdgeConfig"
      extend="flow-polyline"
      name="custom-polyline"
    />
    <!-- è‡ªå®šä¹‰å‘½ä»¤ç»„ä»¶ -->
    <custom-command :download="downloadImage"/>
  </vue-flowchart-editor>
</template>
<script>
import VueFlowchartEditor, {Flow, RegisterEdge} from "vue-flowchart-editor";
import EditorToolbar from "./components/Toolbar";
import EditorItemPanel from "./components/ItemPanel";
import EditorDetailPanel from "./components/DetailPanel";
import EditorMinimap from "./components/EditorMinimap";
import EditorContextMenu from "./components/ContextMenu";
import CustomCommand from "./components/CustomCommand";
export default {
  name: "FlowchartEditor",
  components: {
    VueFlowchartEditor,
    Flow,
    EditorToolbar,
    EditorItemPanel,
    EditorDetailPanel,
    EditorMinimap,
    EditorContextMenu,
    CustomCommand,
    RegisterEdge,
  },
  props: ["chartData", "chartDataNodeItems", "saveData", "rowData", "type"],
  data() {
    return {
      // å½“前图表的数据
      flowChartData: {},
      // è‡ªå®šä¹‰è¾¹çš„配置
      customEdgeConfig: {
        getActivedStyle(/*item*/) {
          return {
            lineWidth: 3, // æ´»è·ƒçŠ¶æ€ä¸‹çš„è¾¹æ ·å¼
          };
        },
        getSelectedStyle(/*item*/) {
          return {
            lineWidth: 3, // é€‰ä¸­çŠ¶æ€ä¸‹çš„è¾¹æ ·å¼
          };
        },
      },
      // å·¥å…·æç¤ºæ˜¾ç¤ºå¼€å…³
      tooltipShow: true,
      // å·¥å…·æç¤ºæ•°æ®
      tooltipData: [],
      // å·²å­˜åœ¨çš„节点和边
      existNodes: this.chartData.nodes || [],
      existEdges: this.chartData.edges || [],
      // å½“前选中的边
      currentSelectedLine: {}
    };
  },
  computed: {
    disabledBtn() {
      return this.$store.state.flow.methodBtn;
    }
  },
  mounted() {
    // ç»„件挂载完成后,自动缩放画布(仅在添加模式下)
    if (this.type === "add") {
      this.$nextTick(() => {
        this.$refs.flowChart.propsAPI.executeCommand("autoZoom");
      });
    }
  },
  methods: {
    update(){
      this.$refs.flowChart.propsAPI.read(this.flowChartData);
    },
    // å¤„理图表数据更改的事件
    onAfterChange(e) {
      console.log(e)
      try {
        if('edit' === this.$store.state.flow.type){
          this.$refs.flowChart.propsAPI.remove(e.item);
          if(e.action === 'remove'){
            this.$message.error('编辑状态下不能切换选中项!');
          }
          return;
        }
        // å¦‚果添加了节点且节点没有被更改过
        if (e.action === "add" && e.model.type === "node") {
          if (!e.model.change) {
            this.$refs.flowChart.propsAPI.remove(e.item); // ç§»é™¤åŽŸæœ‰çš„èŠ‚ç‚¹
            e.model.id = e.model.label; // ä½¿ç”¨æ ‡ç­¾ä½œä¸ºèŠ‚ç‚¹çš„ ID
            e.model.change = true;
            this.$refs.flowChart.propsAPI.add("node", e.model); // æ·»åŠ æ–°çš„èŠ‚ç‚¹
          }
        }
      } catch (err) {
        console.log(err)
        // å¤„理节点 ID å†²çªé”™è¯¯
        if (
          err.message ===
          `id:${e.model.label} has already been set, please set new one`
        ) {
          this.$message.error("不能添加已存在的节点!");
        }
      }
      // ä¿å­˜å½“前的节点和边数据
      const {nodes, edges} = this.$refs.flowChart.propsAPI.save();
      this.existNodes = nodes || [];
      this.existEdges = edges || [];
    },
    // å¤„理选中线变化的事件 å›žå¡«å½“前线信息
    onAfterItemSelected({item}) {
      if (item.target) {
        // æŸ¥æ‰¾å½“前选中的边
        const currentEdge = this.existEdges.filter(itm => itm.id === item.id)[0] || {};
        if (!Array.isArray(currentEdge.events)) {
          currentEdge.events = []
        }
        this.currentSelectedLine = currentEdge;
      }
    },
    // ä¸‹è½½å›¾ç‰‡
    _downloadImage(data, filename = "flowchart.png") {
      const a = document.createElement("a");
      a.href = data;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
    },
    // è°ƒç”¨ä¸‹è½½å›¾ç‰‡æ–¹æ³•
    downloadImage() {
      const page = this.$refs["flowChart"].propsAPI.editor.getCurrentPage();
      this._downloadImage(page.saveImage().toDataURL("image/png"));
    },
    // èŽ·å–æ–°çš„è¡Œæ•°æ®ï¼ˆä»Žè¯¦ç»†é¢æ¿ä¸­ï¼‰
    getNewRowData() {
      return this.$refs.EditorDetailPanel.getNewRowDate();
    },
    // èŽ·å–å½“å‰å›¾è¡¨çš„æ•°æ®
    getFlowData() {
      return this.$refs.flowChart.propsAPI.save();
    },
    // èŽ·å–è¾¹äº‹ä»¶åˆ—è¡¨ï¼ˆä»Žè¯¦ç»†é¢æ¿ä¸­ï¼‰
    getEdgesEvents() {
      return this.$refs.EditorDetailPanel.edgeEventList;
    },
    // è°ƒç”¨çˆ¶ç»„件重置
    handleResetTree(){
      this.$emit('reset-tree');
    },
    handlerSave(){
      this.$emit('handler-save')
    }
  },
};
</script>
<style lang="scss" scoped>
.vue-flowchart-editor {
  display: flex;
  flex: 1;
  flex-direction: column;
  width: 100%;
  height: 100%;
  background: #fff;
}
.vfe-chart {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .vfe-chart-header {
    border: 1px solid #e6e9ed;
    padding: 8px;
  }
  .vfe-chart-container {
    flex: 1;
    display: flex;
    height: 550px;
    overflow: hidden;
    .vfe-chart-main {
      position: relative;
      flex: 1;
      height: 80vh; // fix scroll show
      width: 65%;
      .tooltip {
        position: absolute;
        display: none;
        top: 0;
        left: 0;
        width: 100px;
        height: auto;
        padding: 15px;
        border-radius: 10px;
        z-index: 999;
        opacity: 0.8;
        color: #ffffff;
        font-size: 12px;
        background-color: #000;
        p {
          margin: 0;
        }
      }
    }
    .vfe-chart-sidebar {
      margin-top: 10px;
      position: relative;
      display: flex;
      justify-content: center;
      width: 210px;
      background-color: #fafafa;
      border-right: 1px solid #e6e9ed;
    }
    .vfe-chart-panel {
      position: relative;
      width: 260px;
      background-color: #fafafa;
      border-left: 1px solid #e6e9ed;
      overflow-y: scroll;
      .vfe-chart-panel-detail {
        box-sizing: border-box;
        padding: 10px;
      }
    }
  }
}
</style>