refactor:工艺路线查看统一到Vue Flow只读视图scFlowViewer,与编辑器共用MaterialNode节点/dagre布局;route_show与route页弃用dagre-d3版scDegra
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
46ed8b0c54
commit
63dfe7e948
|
|
@ -0,0 +1,106 @@
|
||||||
|
<template>
|
||||||
|
<div class="mnode" :class="'mnode--' + (data.kind || 'mid')">
|
||||||
|
<Handle type="target" :position="Position.Top" class="mnode__handle" />
|
||||||
|
<span class="mnode__tag">{{ kindLabel }}</span>
|
||||||
|
<div class="mnode__name" :title="data.label">{{ data.label }}</div>
|
||||||
|
<Handle type="source" :position="Position.Bottom" class="mnode__handle" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Handle, Position } from "@vue-flow/core";
|
||||||
|
|
||||||
|
// 物料节点卡片:scFlowEditor(编辑)与 scFlowViewer(查看)共用,保证两端视觉一致
|
||||||
|
export default {
|
||||||
|
name: "MaterialNode",
|
||||||
|
components: { Handle },
|
||||||
|
props: {
|
||||||
|
// { label, kind: raw|mid|final }
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { Position };
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
kindLabel() {
|
||||||
|
return { raw: "原料", final: "成品", mid: "半成品" }[this.data.kind] || "半成品";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mnode {
|
||||||
|
position: relative;
|
||||||
|
width: 150px;
|
||||||
|
padding: 6px 10px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 2px 8px rgba(31, 45, 61, 0.08);
|
||||||
|
text-align: center;
|
||||||
|
transition: box-shadow 0.15s, transform 0.15s;
|
||||||
|
}
|
||||||
|
.mnode:hover {
|
||||||
|
box-shadow: 0 4px 14px rgba(31, 45, 61, 0.16);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.mnode__tag {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.mnode__name {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: #303133;
|
||||||
|
word-break: break-all;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.mnode--raw {
|
||||||
|
border-color: #f0c78a;
|
||||||
|
background: #fffaf2;
|
||||||
|
}
|
||||||
|
.mnode--raw .mnode__tag {
|
||||||
|
background: #e6a23c;
|
||||||
|
}
|
||||||
|
.mnode--mid {
|
||||||
|
border-color: #a3d0ff;
|
||||||
|
background: #f5faff;
|
||||||
|
}
|
||||||
|
.mnode--mid .mnode__tag {
|
||||||
|
background: #409eff;
|
||||||
|
}
|
||||||
|
.mnode--final {
|
||||||
|
border-color: #a4da89;
|
||||||
|
background: #f6fcf1;
|
||||||
|
}
|
||||||
|
.mnode--final .mnode__tag {
|
||||||
|
background: #67c23a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Vue Flow 内部元素(非 scoped) */
|
||||||
|
.vue-flow__node-material {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.vue-flow__handle {
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #409eff;
|
||||||
|
}
|
||||||
|
.vue-flow__handle:hover {
|
||||||
|
background: #409eff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import dagre from "dagre";
|
||||||
|
|
||||||
|
export const NODE_W = 150;
|
||||||
|
export const NODE_H = 54;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dagre 自动布局,返回带 position 的 nodes
|
||||||
|
* @param {Array} nodes Vue Flow 节点(id 必须为字符串)
|
||||||
|
* @param {Array} edges Vue Flow 边
|
||||||
|
* @param {Object} opts { rankdir: TB|BT|LR|RL, nodeW, nodeH }
|
||||||
|
*/
|
||||||
|
export function layoutGraph(nodes, edges, opts = {}) {
|
||||||
|
const { rankdir = "TB", nodeW = NODE_W, nodeH = NODE_H } = opts;
|
||||||
|
const g = new dagre.graphlib.Graph();
|
||||||
|
g.setGraph({ rankdir, nodesep: 30, ranksep: 56, marginx: 20, marginy: 20 });
|
||||||
|
g.setDefaultEdgeLabel(() => ({}));
|
||||||
|
nodes.forEach((n) => g.setNode(n.id, { width: nodeW, height: nodeH }));
|
||||||
|
edges.forEach((e) => g.setEdge(e.source, e.target));
|
||||||
|
dagre.layout(g);
|
||||||
|
return nodes.map((n) => {
|
||||||
|
const gn = g.node(n.id);
|
||||||
|
return { ...n, position: { x: gn.x - nodeW / 2, y: gn.y - nodeH / 2 } };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -72,14 +72,9 @@
|
||||||
@edge-context-menu="onEdgeContextMenu"
|
@edge-context-menu="onEdgeContextMenu"
|
||||||
@node-context-menu="onNodeContextMenu"
|
@node-context-menu="onNodeContextMenu"
|
||||||
>
|
>
|
||||||
<!-- 自定义物料节点 -->
|
<!-- 自定义物料节点(与 scFlowViewer 共用) -->
|
||||||
<template #node-material="{ data }">
|
<template #node-material="{ data }">
|
||||||
<div class="mnode" :class="'mnode--' + (data.kind || 'mid')">
|
<material-node :data="data" />
|
||||||
<Handle type="target" :position="Position.Top" class="mnode__handle" />
|
|
||||||
<span class="mnode__tag">{{ kindLabel(data.kind) }}</span>
|
|
||||||
<div class="mnode__name" :title="data.label">{{ data.label }}</div>
|
|
||||||
<Handle type="source" :position="Position.Bottom" class="mnode__handle" />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<Background :gap="18" pattern-color="#e6e9ef" />
|
<Background :gap="18" pattern-color="#e6e9ef" />
|
||||||
|
|
@ -105,24 +100,22 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { VueFlow, useVueFlow, Handle, Position } from "@vue-flow/core";
|
import { VueFlow, useVueFlow } from "@vue-flow/core";
|
||||||
import { Background } from "@vue-flow/background";
|
import { Background } from "@vue-flow/background";
|
||||||
import { Controls } from "@vue-flow/controls";
|
import { Controls } from "@vue-flow/controls";
|
||||||
import dagre from "dagre";
|
|
||||||
import routeForm from "@/views/mtm/route_form.vue";
|
import routeForm from "@/views/mtm/route_form.vue";
|
||||||
|
import MaterialNode from "./flow/MaterialNode.vue";
|
||||||
|
import { layoutGraph } from "./flow/dagreLayout";
|
||||||
import "@vue-flow/core/dist/style.css";
|
import "@vue-flow/core/dist/style.css";
|
||||||
import "@vue-flow/core/dist/theme-default.css";
|
import "@vue-flow/core/dist/theme-default.css";
|
||||||
import "@vue-flow/controls/dist/style.css";
|
import "@vue-flow/controls/dist/style.css";
|
||||||
|
|
||||||
const NODE_W = 150;
|
|
||||||
const NODE_H = 54;
|
|
||||||
|
|
||||||
// 每个编辑器实例分配唯一 flowId,避免 Vue Flow 全局 store 按固定 id 缓存导致跨次残留
|
// 每个编辑器实例分配唯一 flowId,避免 Vue Flow 全局 store 按固定 id 缓存导致跨次残留
|
||||||
let flowSeq = 0;
|
let flowSeq = 0;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "scFlowEditor",
|
name: "scFlowEditor",
|
||||||
components: { VueFlow, Background, Controls, routeForm, Handle },
|
components: { VueFlow, Background, Controls, routeForm, MaterialNode },
|
||||||
props: {
|
props: {
|
||||||
// 工艺包 id
|
// 工艺包 id
|
||||||
routepack: { type: String, default: "" },
|
routepack: { type: String, default: "" },
|
||||||
|
|
@ -134,7 +127,7 @@ export default {
|
||||||
const flowId = "route-flow-editor-" + ++flowSeq;
|
const flowId = "route-flow-editor-" + ++flowSeq;
|
||||||
const { fitView, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes } =
|
const { fitView, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes } =
|
||||||
useVueFlow(flowId);
|
useVueFlow(flowId);
|
||||||
return { flowId, Position, fitView, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes };
|
return { flowId, fitView, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes };
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -176,10 +169,6 @@ export default {
|
||||||
if (this.routepack) this.reload();
|
if (this.routepack) this.reload();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
kindLabel(kind) {
|
|
||||||
return { raw: "原料", final: "成品", mid: "半成品" }[kind] || "半成品";
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 加载左侧物料面板列表 */
|
/** 加载左侧物料面板列表 */
|
||||||
loadMaterials() {
|
loadMaterials() {
|
||||||
this.matLoading = true;
|
this.matLoading = true;
|
||||||
|
|
@ -265,7 +254,7 @@ export default {
|
||||||
this.seedNodes = this.seedNodes.filter((s) => !routeNodeIds.has(s.id));
|
this.seedNodes = this.seedNodes.filter((s) => !routeNodeIds.has(s.id));
|
||||||
|
|
||||||
this.edges = edges;
|
this.edges = edges;
|
||||||
this.nodes = this.doLayout(routeNodes.concat(this.seedNodes), edges);
|
this.nodes = layoutGraph(routeNodes.concat(this.seedNodes), edges);
|
||||||
this.syncStore();
|
this.syncStore();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -276,23 +265,9 @@ export default {
|
||||||
this.setEdges(this.edges);
|
this.setEdges(this.edges);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** dagre 布局,返回带 position 的 nodes(自上而下 TB) */
|
|
||||||
doLayout(nodes, edges, rankdir = "TB") {
|
|
||||||
const g = new dagre.graphlib.Graph();
|
|
||||||
g.setGraph({ rankdir, nodesep: 30, ranksep: 56, marginx: 20, marginy: 20 });
|
|
||||||
g.setDefaultEdgeLabel(() => ({}));
|
|
||||||
nodes.forEach((n) => g.setNode(n.id, { width: NODE_W, height: NODE_H }));
|
|
||||||
edges.forEach((e) => g.setEdge(e.source, e.target));
|
|
||||||
dagre.layout(g);
|
|
||||||
return nodes.map((n) => {
|
|
||||||
const gn = g.node(n.id);
|
|
||||||
return { ...n, position: { x: gn.x - NODE_W / 2, y: gn.y - NODE_H / 2 } };
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 重新布局并适应画布 */
|
/** 重新布局并适应画布 */
|
||||||
relayout() {
|
relayout() {
|
||||||
this.nodes = this.doLayout([...this.nodes], this.edges);
|
this.nodes = layoutGraph([...this.nodes], this.edges);
|
||||||
this.setNodes(this.nodes);
|
this.setNodes(this.nodes);
|
||||||
this.doFit();
|
this.doFit();
|
||||||
},
|
},
|
||||||
|
|
@ -668,79 +643,9 @@ export default {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 自定义物料节点 ===== */
|
|
||||||
.mnode {
|
|
||||||
position: relative;
|
|
||||||
width: 150px;
|
|
||||||
padding: 6px 10px 8px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 2px 8px rgba(31, 45, 61, 0.08);
|
|
||||||
text-align: center;
|
|
||||||
transition: box-shadow 0.15s, transform 0.15s;
|
|
||||||
}
|
|
||||||
.mnode:hover {
|
|
||||||
box-shadow: 0 4px 14px rgba(31, 45, 61, 0.16);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
.mnode__tag {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 11px;
|
|
||||||
line-height: 1;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.mnode__name {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.3;
|
|
||||||
color: #303133;
|
|
||||||
word-break: break-all;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.mnode--raw {
|
|
||||||
border-color: #f0c78a;
|
|
||||||
background: #fffaf2;
|
|
||||||
}
|
|
||||||
.mnode--raw .mnode__tag {
|
|
||||||
background: #e6a23c;
|
|
||||||
}
|
|
||||||
.mnode--mid {
|
|
||||||
border-color: #a3d0ff;
|
|
||||||
background: #f5faff;
|
|
||||||
}
|
|
||||||
.mnode--mid .mnode__tag {
|
|
||||||
background: #409eff;
|
|
||||||
}
|
|
||||||
.mnode--final {
|
|
||||||
border-color: #a4da89;
|
|
||||||
background: #f6fcf1;
|
|
||||||
}
|
|
||||||
.mnode--final .mnode__tag {
|
|
||||||
background: #67c23a;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
/* Vue Flow 内部元素(非 scoped) */
|
/* Vue Flow 内部元素(非 scoped);物料节点样式在 flow/MaterialNode.vue */
|
||||||
.vue-flow__handle {
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
background: #fff;
|
|
||||||
border: 2px solid #409eff;
|
|
||||||
}
|
|
||||||
.vue-flow__handle:hover {
|
|
||||||
background: #409eff;
|
|
||||||
}
|
|
||||||
.vue-flow__node-material {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.vue-flow__controls {
|
.vue-flow__controls {
|
||||||
box-shadow: 0 2px 10px rgba(31, 45, 61, 0.12);
|
box-shadow: 0 2px 10px rgba(31, 45, 61, 0.12);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
<template>
|
||||||
|
<div class="sc-flow-viewer">
|
||||||
|
<VueFlow
|
||||||
|
:id="flowId"
|
||||||
|
:min-zoom="0.2"
|
||||||
|
:max-zoom="2"
|
||||||
|
:nodes-draggable="false"
|
||||||
|
:nodes-connectable="false"
|
||||||
|
:elements-selectable="false"
|
||||||
|
@nodes-initialized="onNodesInit"
|
||||||
|
>
|
||||||
|
<template #node-material="{ data }">
|
||||||
|
<material-node :data="data" />
|
||||||
|
</template>
|
||||||
|
<Background :gap="18" pattern-color="#e6e9ef" />
|
||||||
|
<Controls :show-interactive="false" />
|
||||||
|
</VueFlow>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { VueFlow, useVueFlow } from "@vue-flow/core";
|
||||||
|
import { Background } from "@vue-flow/background";
|
||||||
|
import { Controls } from "@vue-flow/controls";
|
||||||
|
import MaterialNode from "./flow/MaterialNode.vue";
|
||||||
|
import { layoutGraph } from "./flow/dagreLayout";
|
||||||
|
import "@vue-flow/core/dist/style.css";
|
||||||
|
import "@vue-flow/core/dist/theme-default.css";
|
||||||
|
import "@vue-flow/controls/dist/style.css";
|
||||||
|
|
||||||
|
// 每个实例分配唯一 flowId,避免 Vue Flow 全局 store 按固定 id 缓存导致多实例互相覆盖
|
||||||
|
let viewerSeq = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺路线只读流程图(Vue Flow 版),与 scFlowEditor 共用节点样式与布局。
|
||||||
|
* props 与 scDegra 对齐:nodes [{id,label}]、edges [{id,source,target,label}]
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: "scFlowViewer",
|
||||||
|
components: { VueFlow, Background, Controls, MaterialNode },
|
||||||
|
props: {
|
||||||
|
nodes: { type: Array, default: () => [] },
|
||||||
|
edges: { type: Array, default: () => [] },
|
||||||
|
rankdir: { type: String, default: "TB" },
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const flowId = "sc-flow-viewer-" + ++viewerSeq;
|
||||||
|
const { fitView, setNodes, setEdges } = useVueFlow(flowId);
|
||||||
|
return { flowId, fitView, setNodes, setEdges };
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
nodes() {
|
||||||
|
this.rebuild();
|
||||||
|
},
|
||||||
|
edges() {
|
||||||
|
this.rebuild();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.rebuild();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** nodes/edges -> Vue Flow 图;节点类型按出入度推断(无入边=原料,无出边=成品) */
|
||||||
|
rebuild() {
|
||||||
|
const sourceIds = new Set();
|
||||||
|
const targetIds = new Set();
|
||||||
|
const rawEdges = (this.edges || [])
|
||||||
|
.filter((e) => e.source != null && e.target != null)
|
||||||
|
.map((e) => {
|
||||||
|
const sid = String(e.source);
|
||||||
|
const tid = String(e.target);
|
||||||
|
sourceIds.add(sid);
|
||||||
|
targetIds.add(tid);
|
||||||
|
return {
|
||||||
|
id: e.id != null ? String(e.id) : sid + "-" + tid,
|
||||||
|
source: sid,
|
||||||
|
target: tid,
|
||||||
|
label: e.label || "",
|
||||||
|
type: "smoothstep",
|
||||||
|
markerEnd: "arrowclosed",
|
||||||
|
style: { stroke: "#409eff", strokeWidth: 2 },
|
||||||
|
labelStyle: { fill: "#5b6370", fontSize: "12px", fontWeight: 500 },
|
||||||
|
labelBgStyle: { fill: "#ffffff", stroke: "#e4e7ed", strokeWidth: 1 },
|
||||||
|
labelBgPadding: [7, 4],
|
||||||
|
labelBgBorderRadius: 6,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const vNodes = (this.nodes || [])
|
||||||
|
.filter((n) => n.id != null && n.label)
|
||||||
|
.map((n) => {
|
||||||
|
const id = String(n.id);
|
||||||
|
const isSrc = sourceIds.has(id);
|
||||||
|
const isTgt = targetIds.has(id);
|
||||||
|
let kind = "mid";
|
||||||
|
if (isTgt && !isSrc) kind = "final";
|
||||||
|
else if (isSrc && !isTgt) kind = "raw";
|
||||||
|
return { id, type: "material", data: { label: n.label, kind }, position: { x: 0, y: 0 } };
|
||||||
|
});
|
||||||
|
const nodeIds = new Set(vNodes.map((n) => n.id));
|
||||||
|
const vEdges = rawEdges.filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target));
|
||||||
|
|
||||||
|
this.setEdges([]);
|
||||||
|
this.setNodes(layoutGraph(vNodes, vEdges, { rankdir: this.rankdir }));
|
||||||
|
this.setEdges(vEdges);
|
||||||
|
this.doFit();
|
||||||
|
},
|
||||||
|
onNodesInit() {
|
||||||
|
this.doFit();
|
||||||
|
},
|
||||||
|
doFit() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
try {
|
||||||
|
this.fitView({ padding: 0.2, maxZoom: 1, duration: 200 });
|
||||||
|
} catch (e) {
|
||||||
|
/* noop */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sc-flow-viewer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 320px;
|
||||||
|
border: 1px solid #eef1f6;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fbfcfe;
|
||||||
|
}
|
||||||
|
/* 只读视图:隐藏连接点 */
|
||||||
|
.sc-flow-viewer :deep(.vue-flow__handle) {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sc-flow-viewer .vue-flow__controls {
|
||||||
|
box-shadow: 0 2px 10px rgba(31, 45, 61, 0.12);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -17,14 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
</el-header>
|
</el-header>
|
||||||
<el-main class="nopadding">
|
<el-main class="nopadding">
|
||||||
<scDegra
|
<scFlowViewer :nodes="nodes" :edges="edges"></scFlowViewer>
|
||||||
style="margin-top: 50px;"
|
|
||||||
ref="degraDialogs"
|
|
||||||
:nodes="nodes"
|
|
||||||
:edges="edges"
|
|
||||||
:rankdir="'DL'"
|
|
||||||
>
|
|
||||||
</scDegra>
|
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
|
|
@ -130,6 +123,9 @@
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { defineAsyncComponent } from "vue";
|
||||||
|
// 异步加载:Vue Flow 单独分包
|
||||||
|
const scFlowViewer = defineAsyncComponent(() => import("@/components/scFlowViewer.vue"));
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
process: null,
|
process: null,
|
||||||
sort: 1,
|
sort: 1,
|
||||||
|
|
@ -139,7 +135,7 @@ const defaultForm = {
|
||||||
};
|
};
|
||||||
export default {
|
export default {
|
||||||
name: "route",
|
name: "route",
|
||||||
components: {},
|
components: { scFlowViewer },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
apiObj: null,
|
apiObj: null,
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,17 @@
|
||||||
</el-header>
|
</el-header>
|
||||||
<el-main>
|
<el-main>
|
||||||
<el-container>
|
<el-container>
|
||||||
<el-aside style="width: 50%;overflow: scroll;">
|
<el-aside style="width: 50%;overflow: hidden;display: flex;flex-direction: column;">
|
||||||
<div
|
<div
|
||||||
style="font-weight: 600;color: #303133;font-size: 16px;padding: 10px 0;position: fixed;">
|
style="font-weight: 600;color: #303133;font-size: 16px;padding: 10px 0;">
|
||||||
工艺路线流程图</div>
|
工艺路线流程图</div>
|
||||||
<el-tabs v-if="tabsTitle.length > 1" v-model="activeName" :tab-position="'left'"
|
<el-tabs v-if="tabsTitle.length > 1" v-model="activeName" :tab-position="'top'"
|
||||||
style="flex-direction: row;" @tab-click="tabshandleClick">
|
@tab-click="tabshandleClick">
|
||||||
<el-tab-pane :label="item" v-for="item in tabsTitle" :key="item"></el-tab-pane>
|
<el-tab-pane :label="item" v-for="item in tabsTitle" :key="item"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<scDegra style="margin-top: 50px;" v-if="limitedWatch" ref="degraDialogs" :nodes="nodes"
|
<scFlowViewer v-if="limitedWatch" :nodes="nodes" :edges="edges"
|
||||||
:edges="edges" :rankdir="'DL'" @closeDialog="limitedWatch = false">
|
style="flex: 1;min-height: 0;">
|
||||||
</scDegra>
|
</scFlowViewer>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
<el-main class="padding: 1px">
|
<el-main class="padding: 1px">
|
||||||
<scTable ref="tables" :data="tableData" row-key="id" hidePagination hideDo stripe border>
|
<scTable ref="tables" :data="tableData" row-key="id" hidePagination hideDo stripe border>
|
||||||
|
|
@ -70,10 +70,13 @@
|
||||||
</el-container>
|
</el-container>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { defineAsyncComponent } from "vue";
|
||||||
import ticketd from '@/views/wf/ticketd.vue'
|
import ticketd from '@/views/wf/ticketd.vue'
|
||||||
|
// 异步加载:Vue Flow 单独分包,仅打开详情时才下载
|
||||||
|
const scFlowViewer = defineAsyncComponent(() => import("@/components/scFlowViewer.vue"));
|
||||||
export default {
|
export default {
|
||||||
emits: ["success", "closed"],
|
emits: ["success", "closed"],
|
||||||
components: { ticketd },
|
components: { ticketd, scFlowViewer },
|
||||||
props: {
|
props: {
|
||||||
t_id: { type: String, default: null },
|
t_id: { type: String, default: null },
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue