Refine route flow routing and step details
This commit is contained in:
parent
a1fda76330
commit
64d606c3a5
|
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<BaseEdge
|
||||
:id="id"
|
||||
:path="edgePath"
|
||||
:marker-end="markerEnd"
|
||||
:style="style"
|
||||
:interaction-width="20"
|
||||
/>
|
||||
<EdgeLabelRenderer v-if="label">
|
||||
<div
|
||||
class="dagre-edge-label nodrag nopan"
|
||||
:style="{
|
||||
transform: `translate(-50%, -50%) translate(${labelPoint.x}px, ${labelPoint.y}px)`,
|
||||
color: labelStyle && labelStyle.fill,
|
||||
fontSize: labelStyle && labelStyle.fontSize,
|
||||
fontWeight: labelStyle && labelStyle.fontWeight,
|
||||
}"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</EdgeLabelRenderer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BaseEdge, EdgeLabelRenderer } from "@vue-flow/core";
|
||||
import { curveCatmullRom, line } from "d3";
|
||||
|
||||
export default {
|
||||
name: "DagreEdge",
|
||||
components: { BaseEdge, EdgeLabelRenderer },
|
||||
props: {
|
||||
id: { type: String, required: true },
|
||||
sourceX: { type: Number, required: true },
|
||||
sourceY: { type: Number, required: true },
|
||||
targetX: { type: Number, required: true },
|
||||
targetY: { type: Number, required: true },
|
||||
data: { type: Object, default: () => ({}) },
|
||||
label: { type: [String, Number], default: "" },
|
||||
markerEnd: { type: String, default: "" },
|
||||
style: { type: Object, default: () => ({}) },
|
||||
labelStyle: { type: Object, default: () => ({}) },
|
||||
},
|
||||
computed: {
|
||||
points() {
|
||||
const routed = this.data.dagrePoints || [];
|
||||
const points = [
|
||||
{ x: this.sourceX, y: this.sourceY },
|
||||
...routed.slice(1, -1),
|
||||
{ x: this.targetX, y: this.targetY },
|
||||
];
|
||||
const label = this.data.dagreLabel;
|
||||
if (!label || points.some((point) => Math.abs(point.x - label.x) < 1 && Math.abs(point.y - label.y) < 1)) {
|
||||
return points;
|
||||
}
|
||||
// TB 工艺图按 y 单调前进,把标签中心插入对应线段,确保线条从标签正中穿过。
|
||||
const insertAt = points.findIndex((point, index) => index > 0 && point.y >= label.y);
|
||||
points.splice(insertAt > 0 ? insertAt : Math.ceil(points.length / 2), 0, label);
|
||||
return points;
|
||||
},
|
||||
edgePath() {
|
||||
return (
|
||||
line()
|
||||
.x((point) => point.x)
|
||||
.y((point) => point.y)
|
||||
.curve(curveCatmullRom.alpha(0.8))(this.points) || ""
|
||||
);
|
||||
},
|
||||
labelPoint() {
|
||||
if (this.data.dagreLabel) return this.data.dagreLabel;
|
||||
const middle = this.points[Math.floor(this.points.length / 2)];
|
||||
return middle || { x: (this.sourceX + this.targetX) / 2, y: (this.sourceY + this.targetY) / 2 };
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dagre-edge-label {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
max-width: 190px;
|
||||
padding: 5px 9px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 7px;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 1px 3px rgba(31, 45, 61, 0.08);
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
white-space: normal;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,9 +1,27 @@
|
|||
<template>
|
||||
<div class="mnode" :class="'mnode--' + (data.kind || 'mid')">
|
||||
<Handle type="target" :position="Position.Top" class="mnode__handle" />
|
||||
<Handle
|
||||
v-for="port in data.targetPorts || []"
|
||||
:key="port.id"
|
||||
:id="port.id"
|
||||
type="target"
|
||||
:position="Position.Top"
|
||||
:style="{ left: port.left + '%' }"
|
||||
class="mnode__handle mnode__route-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" />
|
||||
<Handle
|
||||
v-for="port in data.sourcePorts || []"
|
||||
:key="port.id"
|
||||
:id="port.id"
|
||||
type="source"
|
||||
:position="Position.Bottom"
|
||||
:style="{ left: port.left + '%' }"
|
||||
class="mnode__handle mnode__route-handle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -64,6 +82,10 @@ export default {
|
|||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mnode__route-handle {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mnode--raw {
|
||||
border-color: #f0c78a;
|
||||
background: #fffaf2;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@ export const NODE_H = 54;
|
|||
* @param {Object} opts { rankdir: TB|BT|LR|RL, nodeW, nodeH, nodesep, edgesep, ranksep, marginx, marginy }
|
||||
*/
|
||||
export function layoutGraph(nodes, edges, opts = {}) {
|
||||
return layoutGraphWithEdges(nodes, edges, opts).nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* dagre 同时计算节点位置、边的绕行点和标签位置。
|
||||
* Vue Flow 默认边只使用节点坐标,会让跨层边穿过节点;因此查看/编辑视图使用此完整结果。
|
||||
*/
|
||||
export function layoutGraphWithEdges(nodes, edges, opts = {}) {
|
||||
const {
|
||||
rankdir = "TB",
|
||||
nodeW = NODE_W,
|
||||
|
|
@ -20,14 +28,93 @@ export function layoutGraph(nodes, edges, opts = {}) {
|
|||
marginx = 20,
|
||||
marginy = 20,
|
||||
} = opts;
|
||||
const g = new dagre.graphlib.Graph();
|
||||
const g = new dagre.graphlib.Graph({ multigraph: true });
|
||||
g.setGraph({ rankdir, nodesep, edgesep, ranksep, marginx, marginy });
|
||||
g.setDefaultEdgeLabel(() => ({}));
|
||||
nodes.forEach((n) => g.setNode(n.id, { width: nodeW, height: nodeH }));
|
||||
edges.forEach((e) => g.setEdge(e.source, e.target));
|
||||
const parallelCount = new Map();
|
||||
const edgeRefs = [];
|
||||
edges.forEach((e, index) => {
|
||||
const key = `${e.source}->${e.target}`;
|
||||
const parallelIndex = parallelCount.get(key) || 0;
|
||||
parallelCount.set(key, parallelIndex + 1);
|
||||
// 参考原 dagre-d3:基础边跨 2 个 rank,同组边逐条增加长度。
|
||||
const minlen = e.layoutMinlen || 2 + parallelIndex;
|
||||
const name = `route-${index}`;
|
||||
const labelWidth = e.label ? Math.min(190, Math.max(56, String(e.label).length * 13 + 20)) : 0;
|
||||
g.setEdge(
|
||||
e.source,
|
||||
e.target,
|
||||
{
|
||||
minlen,
|
||||
width: labelWidth,
|
||||
height: e.label ? 30 : 0,
|
||||
// 标签位于工序线本身;平行边已由独立锚点和 dagre edgesep 分开。
|
||||
labelpos: "c",
|
||||
labeloffset: 0,
|
||||
},
|
||||
name
|
||||
);
|
||||
edgeRefs.push({ v: e.source, w: e.target, name });
|
||||
});
|
||||
dagre.layout(g);
|
||||
return nodes.map((n) => {
|
||||
const laidOutNodes = nodes.map((n) => {
|
||||
const gn = g.node(n.id);
|
||||
return { ...n, position: { x: gn.x - nodeW / 2, y: gn.y - nodeH / 2 } };
|
||||
});
|
||||
const laidOutEdges = edges.map((edge, index) => {
|
||||
const ge = g.edge(edgeRefs[index]) || {};
|
||||
return {
|
||||
...edge,
|
||||
data: {
|
||||
...edge.data,
|
||||
dagrePoints: ge.points || [],
|
||||
dagreLabel: Number.isFinite(ge.x) && Number.isFinite(ge.y) ? { x: ge.x, y: ge.y } : null,
|
||||
},
|
||||
};
|
||||
});
|
||||
return { nodes: laidOutNodes, edges: laidOutEdges };
|
||||
}
|
||||
|
||||
/**
|
||||
* 为每条边分配独立的节点锚点,避免多条工序共用中心 Handle 产生重叠。
|
||||
* 同一节点上的锚点按相邻节点横坐标排序,可同时减少分支交叉。
|
||||
*/
|
||||
export function assignEdgePorts(nodes, edges) {
|
||||
const nodeMap = new Map(nodes.map((node) => [node.id, node]));
|
||||
const outgoing = new Map();
|
||||
const incoming = new Map();
|
||||
|
||||
const routedEdges = edges.map((edge, index) => {
|
||||
const sourceHandle = `route-out-${index}`;
|
||||
const targetHandle = `route-in-${index}`;
|
||||
if (!outgoing.has(edge.source)) outgoing.set(edge.source, []);
|
||||
if (!incoming.has(edge.target)) incoming.set(edge.target, []);
|
||||
outgoing.get(edge.source).push({ edgeIndex: index, handleId: sourceHandle, neighborId: edge.target });
|
||||
incoming.get(edge.target).push({ edgeIndex: index, handleId: targetHandle, neighborId: edge.source });
|
||||
return { ...edge, sourceHandle, targetHandle };
|
||||
});
|
||||
|
||||
const getCenterX = (id) => {
|
||||
const node = nodeMap.get(id);
|
||||
return node ? node.position.x + NODE_W / 2 : 0;
|
||||
};
|
||||
const makePorts = (items = []) =>
|
||||
[...items]
|
||||
.sort((a, b) => getCenterX(a.neighborId) - getCenterX(b.neighborId) || a.edgeIndex - b.edgeIndex)
|
||||
.map((item, index, list) => ({
|
||||
id: item.handleId,
|
||||
left: ((index + 1) * 100) / (list.length + 1),
|
||||
}));
|
||||
|
||||
const routedNodes = nodes.map((node) => ({
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
sourcePorts: makePorts(outgoing.get(node.id)),
|
||||
targetPorts: makePorts(incoming.get(node.id)),
|
||||
},
|
||||
}));
|
||||
|
||||
return { nodes: routedNodes, edges: routedEdges };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@
|
|||
<template #node-material="{ data }">
|
||||
<material-node :data="data" />
|
||||
</template>
|
||||
<template #edge-dagre="edgeProps">
|
||||
<dagre-edge v-bind="edgeProps" />
|
||||
</template>
|
||||
|
||||
<Background :gap="18" pattern-color="#e6e9ef" />
|
||||
<Controls :show-interactive="false" />
|
||||
|
|
@ -110,8 +113,9 @@ import { VueFlow, useVueFlow } from "@vue-flow/core";
|
|||
import { Background } from "@vue-flow/background";
|
||||
import { Controls } from "@vue-flow/controls";
|
||||
import routeForm from "@/views/mtm/route_form.vue";
|
||||
import DagreEdge from "./flow/DagreEdge.vue";
|
||||
import MaterialNode from "./flow/MaterialNode.vue";
|
||||
import { layoutGraph, NODE_W } from "./flow/dagreLayout";
|
||||
import { assignEdgePorts, layoutGraphWithEdges, NODE_W } 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";
|
||||
|
|
@ -121,7 +125,7 @@ let flowSeq = 0;
|
|||
|
||||
export default {
|
||||
name: "xtFlowEditor",
|
||||
components: { VueFlow, Background, Controls, routeForm, MaterialNode },
|
||||
components: { VueFlow, Background, Controls, routeForm, DagreEdge, MaterialNode },
|
||||
props: {
|
||||
// 工艺包 id
|
||||
routepack: { type: String, default: "" },
|
||||
|
|
@ -251,7 +255,6 @@ export default {
|
|||
const matKind = {};
|
||||
const targetIds = new Set();
|
||||
const edges = [];
|
||||
const parallelEdgeCount = {};
|
||||
|
||||
this.routes.forEach((r) => {
|
||||
if (r.material_in == null || r.material_out == null) return;
|
||||
|
|
@ -264,17 +267,13 @@ export default {
|
|||
targetIds.add(tid);
|
||||
|
||||
const locked = !!r.from_route;
|
||||
const edgeKey = sid + "->" + tid;
|
||||
const edgeIndex = parallelEdgeCount[edgeKey] || 0;
|
||||
parallelEdgeCount[edgeKey] = edgeIndex + 1;
|
||||
edges.push({
|
||||
id: String(r.id),
|
||||
source: sid,
|
||||
target: tid,
|
||||
label: (r.process_name || "") + (locked ? " 🔒" : ""),
|
||||
data: { route: r, locked },
|
||||
type: "default",
|
||||
pathOptions: { curvature: 0.25 + edgeIndex * 0.12 },
|
||||
type: "dagre",
|
||||
markerEnd: "arrowclosed",
|
||||
style: {
|
||||
stroke: locked ? "#c0c4cc" : "#409eff",
|
||||
|
|
@ -312,22 +311,24 @@ export default {
|
|||
this.seedNodes = this.seedNodes.filter((s) => !routeNodeIds.has(s.id));
|
||||
|
||||
const prevCount = this.nodes.length;
|
||||
this.edges = edges;
|
||||
this.nodes = this.layoutNodes(routeNodes.concat(this.seedNodes), edges);
|
||||
const routedGraph = this.layoutAndRoute(routeNodes.concat(this.seedNodes), edges);
|
||||
this.nodes = routedGraph.nodes;
|
||||
this.edges = routedGraph.edges;
|
||||
this.syncStore();
|
||||
// 节点增减后 dagre 会整体重排,重新适配视野,避免“看着是空白其实有节点”导致拉线误吸附
|
||||
if (this.hasFitted && this.nodes.length !== prevCount) this.fitToWidth();
|
||||
},
|
||||
|
||||
/** 编辑与查看使用一致的疏朗布局参数,给工序标签和分支留出空间 */
|
||||
layoutNodes(nodes, edges) {
|
||||
return layoutGraph(nodes, edges, {
|
||||
nodesep: 58,
|
||||
edgesep: 32,
|
||||
ranksep: 96,
|
||||
marginx: 32,
|
||||
marginy: 32,
|
||||
/** 编辑与查看使用一致的 dagre-d3 布局参数,并保留边绕行点 */
|
||||
layoutAndRoute(nodes, edges) {
|
||||
const laidOutGraph = layoutGraphWithEdges(nodes, edges, {
|
||||
nodesep: 40,
|
||||
edgesep: 25,
|
||||
ranksep: 20,
|
||||
marginx: 80,
|
||||
marginy: 10,
|
||||
});
|
||||
return assignEdgePorts(laidOutGraph.nodes, laidOutGraph.edges);
|
||||
},
|
||||
|
||||
/** 把本地 nodes/edges 命令式同步到 Vue Flow store(先节点后边,避免边找不到端点) */
|
||||
|
|
@ -339,8 +340,11 @@ export default {
|
|||
|
||||
/** 重新布局并适应画布 */
|
||||
relayout() {
|
||||
this.nodes = this.layoutNodes([...this.nodes], this.edges);
|
||||
const routedGraph = this.layoutAndRoute([...this.nodes], this.edges);
|
||||
this.nodes = routedGraph.nodes;
|
||||
this.edges = routedGraph.edges;
|
||||
this.setNodes(this.nodes);
|
||||
this.setEdges(this.edges);
|
||||
this.fitToWidth();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
<template #node-material="{ data }">
|
||||
<material-node :data="data" />
|
||||
</template>
|
||||
<template #edge-dagre="edgeProps">
|
||||
<dagre-edge v-bind="edgeProps" />
|
||||
</template>
|
||||
<Background :gap="18" pattern-color="#e6e9ef" />
|
||||
<Controls :show-interactive="false" />
|
||||
</VueFlow>
|
||||
|
|
@ -41,8 +44,9 @@
|
|||
import { VueFlow, useVueFlow } from "@vue-flow/core";
|
||||
import { Background } from "@vue-flow/background";
|
||||
import { Controls } from "@vue-flow/controls";
|
||||
import DagreEdge from "./flow/DagreEdge.vue";
|
||||
import MaterialNode from "./flow/MaterialNode.vue";
|
||||
import { layoutGraph, NODE_W } from "./flow/dagreLayout";
|
||||
import { assignEdgePorts, layoutGraphWithEdges, NODE_W } 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";
|
||||
|
|
@ -56,7 +60,7 @@ let viewerSeq = 0;
|
|||
*/
|
||||
export default {
|
||||
name: "xtFlowViewer",
|
||||
components: { VueFlow, Background, Controls, MaterialNode },
|
||||
components: { VueFlow, Background, Controls, DagreEdge, MaterialNode },
|
||||
props: {
|
||||
nodes: { type: Array, default: () => [] },
|
||||
edges: { type: Array, default: () => [] },
|
||||
|
|
@ -120,25 +124,19 @@ export default {
|
|||
rebuild() {
|
||||
const sourceIds = new Set();
|
||||
const targetIds = new Set();
|
||||
const parallelEdgeCount = {};
|
||||
const rawEdges = (this.edges || [])
|
||||
.filter((e) => e.source != null && e.target != null)
|
||||
.map((e) => {
|
||||
.map((e, index) => {
|
||||
const sid = String(e.source);
|
||||
const tid = String(e.target);
|
||||
const edgeKey = sid + "->" + tid;
|
||||
const edgeIndex = parallelEdgeCount[edgeKey] || 0;
|
||||
parallelEdgeCount[edgeKey] = edgeIndex + 1;
|
||||
sourceIds.add(sid);
|
||||
targetIds.add(tid);
|
||||
return {
|
||||
id: e.id != null ? String(e.id) : sid + "-" + tid,
|
||||
id: e.id != null ? String(e.id) : `${sid}-${tid}-${edgeIndex}`,
|
||||
source: sid,
|
||||
target: tid,
|
||||
label: e.label || "",
|
||||
// 贝塞尔线会在分叉后立即分离;同一对节点有多道工序时用不同曲率错开。
|
||||
type: "default",
|
||||
pathOptions: { curvature: 0.25 + edgeIndex * 0.12 },
|
||||
type: "dagre",
|
||||
markerEnd: "arrowclosed",
|
||||
style: { stroke: "#409eff", strokeWidth: 2.2 },
|
||||
labelStyle: { fill: "#5b6370", fontSize: "12px", fontWeight: 500 },
|
||||
|
|
@ -161,17 +159,19 @@ export default {
|
|||
const nodeIds = new Set(vNodes.map((n) => n.id));
|
||||
const vEdges = rawEdges.filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target));
|
||||
|
||||
this.renderedNodes = layoutGraph(vNodes, vEdges, {
|
||||
const laidOutGraph = layoutGraphWithEdges(vNodes, vEdges, {
|
||||
rankdir: this.rankdir,
|
||||
nodesep: 58,
|
||||
edgesep: 32,
|
||||
ranksep: 96,
|
||||
marginx: 32,
|
||||
marginy: 32,
|
||||
nodesep: 40,
|
||||
edgesep: 25,
|
||||
ranksep: 20,
|
||||
marginx: 80,
|
||||
marginy: 10,
|
||||
});
|
||||
const routedGraph = assignEdgePorts(laidOutGraph.nodes, laidOutGraph.edges);
|
||||
this.renderedNodes = routedGraph.nodes;
|
||||
this.setEdges([]);
|
||||
this.setNodes(this.renderedNodes);
|
||||
this.setEdges(vEdges);
|
||||
this.setEdges(routedGraph.edges);
|
||||
// 首次挂载交给 nodes-initialized(尺寸测量完成)再定位;数据更新时直接重新适配宽度
|
||||
if (this.hasFitted) this.fitToWidth();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
ref="dialogForm"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:disabled="mode === 'show'"
|
||||
label-position="right"
|
||||
label-width="100px"
|
||||
>
|
||||
|
|
@ -197,7 +198,7 @@
|
|||
</el-col>
|
||||
<el-col>
|
||||
<el-form-item label="工艺图纸">
|
||||
<sc-upload-multiple v-model="fileurl" @imagesDel="imagesDel" @imagesChange="imagesChange" draggable :limit="9" tip="最多上传9个文件,单个文件不要超过10M,请上传图像格式文件"></sc-upload-multiple>
|
||||
<sc-upload-multiple v-model="fileurl" @imagesDel="imagesDel" @imagesChange="imagesChange" draggable :disabled="mode === 'show'" :limit="9" tip="最多上传9个文件,单个文件不要超过10M,请上传图像格式文件"></sc-upload-multiple>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -226,9 +227,10 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="project_code=='bxerp'&&mode=='edit'">
|
||||
<el-row v-if="project_code=='bxerp'&&(mode=='edit'||mode=='show')">
|
||||
<sc-form-table
|
||||
hideDelete
|
||||
:hideAdd="mode === 'show'"
|
||||
ref="routematTable"
|
||||
:tableHeight="300"
|
||||
v-model="routemats"
|
||||
|
|
@ -253,7 +255,7 @@
|
|||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="open" label="操作" width="125" align="center">
|
||||
<el-table-column v-if="mode !== 'show'" prop="open" label="操作" width="125" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
text
|
||||
|
|
@ -279,8 +281,8 @@
|
|||
</el-form>
|
||||
</el-main>
|
||||
<el-footer>
|
||||
<el-button type="primary" :loading="isSaveing" @click="submit">提交</el-button>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button v-if="mode !== 'show'" type="primary" :loading="isSaveing" @click="submit">提交</el-button>
|
||||
<el-button @click="visible = false">{{ mode === "show" ? "关闭" : "取消" }}</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-dialog>
|
||||
|
|
@ -347,7 +349,7 @@ export default {
|
|||
fileurl_form:[],
|
||||
paramsJsonFileurl:[],
|
||||
materialOptions:[],
|
||||
titleMap: { add: "新增", edit: "编辑" },
|
||||
titleMap: { add: "新增", edit: "编辑", show: "工序详情" },
|
||||
setFiltersVisible: false,
|
||||
routeId: "",
|
||||
processName: "",
|
||||
|
|
@ -505,6 +507,9 @@ export default {
|
|||
let that = this;
|
||||
that.mode = mode;
|
||||
that.resetForm();
|
||||
if (that.project_code == "bxerp" && (mode === "edit" || mode === "show")) {
|
||||
that.getMaterialOptions();
|
||||
}
|
||||
if (that.prefill) that.setData(that.prefill);
|
||||
that.visible = true;
|
||||
that.$nextTick(() => {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,17 @@
|
|||
<div class="rs-card rs-steps">
|
||||
<div class="rs-card__title">工序步骤</div>
|
||||
<div class="rs-steps__table">
|
||||
<scTable ref="tables" :data="tableData" row-key="id" hidePagination hideDo stripe border>
|
||||
<scTable
|
||||
ref="tables"
|
||||
:data="tableData"
|
||||
row-key="id"
|
||||
hidePagination
|
||||
hideDo
|
||||
stripe
|
||||
border
|
||||
class="rs-steps__clickable"
|
||||
@row-click="showStepDetail"
|
||||
>
|
||||
<el-table-column label="排序" prop="sort" width="50">
|
||||
</el-table-column>
|
||||
<el-table-column label="工序" prop="process_name">
|
||||
|
|
@ -58,6 +68,13 @@
|
|||
<el-tag v-if="scope.row.batch_bind" type="success">是</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="64">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click.stop="showStepDetail(scope.row)">
|
||||
查看
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</scTable>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -66,14 +83,16 @@
|
|||
<el-aside width="20%" v-if="form.ticket" class="rs-aside">
|
||||
<ticketd :ticket_="form.ticket_"></ticketd>
|
||||
</el-aside>
|
||||
<route-form ref="routeDetail" :routepack="form.id || ''"></route-form>
|
||||
</el-container>
|
||||
</template>
|
||||
<script>
|
||||
import ticketd from '@/views/wf/ticketd.vue'
|
||||
import xtFlowViewer from "@/components/xtFlowViewer.vue";
|
||||
import RouteForm from "./route_form.vue";
|
||||
export default {
|
||||
emits: ["success", "closed"],
|
||||
components: { ticketd, xtFlowViewer },
|
||||
components: { ticketd, xtFlowViewer, RouteForm },
|
||||
props: {
|
||||
t_id: { type: String, default: null },
|
||||
},
|
||||
|
|
@ -181,6 +200,11 @@ export default {
|
|||
this.showGraph(graph);
|
||||
}
|
||||
},
|
||||
async showStepDetail(row) {
|
||||
if (!row || !row.id) return;
|
||||
const detail = await this.$API.mtm.route.item.req(row.id);
|
||||
this.$refs.routeDetail.open("show").setData(detail);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -283,6 +307,9 @@ export default {
|
|||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.rs-steps__clickable :deep(.el-table__row) {
|
||||
cursor: pointer;
|
||||
}
|
||||
/* 右侧工单/审批面板:与主体同底色,间距交给 ticketd 自身的内边距 */
|
||||
.rs-aside {
|
||||
background: #f5f7fa;
|
||||
|
|
|
|||
Loading…
Reference in New Issue