diff --git a/src/api/model/wf.js b/src/api/model/wf.js index 64264196..6b479b2b 100644 --- a/src/api/model/wf.js +++ b/src/api/model/wf.js @@ -188,6 +188,12 @@ export default { return await http.get( `${config.API_URL}/wf/ticket/${id}/transitions/`); } }, + ticketAvailableActions: { + name: "获取当前用户可执行的工单操作", + req: async function(id){ + return await http.get(`${config.API_URL}/wf/ticket/${id}/available_actions/`); + } + }, dutyAgg: { url: `${config.API_URL}/wf/ticket/duty_agg/`, name: "待办审批聚合", diff --git a/src/components/flow/DagreEdge.vue b/src/components/flow/DagreEdge.vue new file mode 100644 index 00000000..1bf9152f --- /dev/null +++ b/src/components/flow/DagreEdge.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/src/components/flow/MaterialNode.vue b/src/components/flow/MaterialNode.vue index 1edf847d..0102e6c2 100644 --- a/src/components/flow/MaterialNode.vue +++ b/src/components/flow/MaterialNode.vue @@ -1,9 +1,27 @@ @@ -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; @@ -95,12 +117,29 @@ export default { padding: 0; } .vue-flow__handle { + display: flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 50%; + background: transparent; + border: 0; + cursor: crosshair; +} +.vue-flow__handle::after { + content: ""; width: 9px; height: 9px; - background: #fff; border: 2px solid #409eff; + border-radius: 50%; + background: #fff; + transition: width 0.15s, height 0.15s, background 0.15s, box-shadow 0.15s; } -.vue-flow__handle:hover { +.vue-flow__handle:hover::after { + width: 13px; + height: 13px; background: #409eff; + box-shadow: 0 0 0 4px rgba(64, 158, 255, 0.18); } diff --git a/src/components/flow/dagreLayout.js b/src/components/flow/dagreLayout.js index a7275036..ffbb1ac6 100644 --- a/src/components/flow/dagreLayout.js +++ b/src/components/flow/dagreLayout.js @@ -7,18 +7,114 @@ 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 } + * @param {Object} opts { rankdir: TB|BT|LR|RL, nodeW, nodeH, nodesep, edgesep, ranksep, marginx, marginy } */ 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 }); + return layoutGraphWithEdges(nodes, edges, opts).nodes; +} + +/** + * dagre 同时计算节点位置、边的绕行点和标签位置。 + * Vue Flow 默认边只使用节点坐标,会让跨层边穿过节点;因此查看/编辑视图使用此完整结果。 + */ +export function layoutGraphWithEdges(nodes, edges, opts = {}) { + const { + rankdir = "TB", + nodeW = NODE_W, + nodeH = NODE_H, + nodesep = 30, + edgesep = 20, + ranksep = 56, + marginx = 20, + marginy = 20, + } = opts; + 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 }; } diff --git a/src/components/scTable/index.vue b/src/components/scTable/index.vue index 306e5d02..9c78221a 100644 --- a/src/components/scTable/index.vue +++ b/src/components/scTable/index.vue @@ -13,7 +13,7 @@ + @filter-change="filterChange" @selection-change="selectionChange" @expand-change="handleExpandChange"> + @@ -108,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 } 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"; @@ -119,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: "" }, @@ -132,9 +138,9 @@ export default { emits: ["changed"], setup() { const flowId = "route-flow-editor-" + ++flowSeq; - const { fitView, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes } = + const { setViewport, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes } = useVueFlow(flowId); - return { flowId, fitView, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes }; + return { flowId, setViewport, screenToFlowCoordinate, project, setNodes, setEdges, addNodes, removeNodes }; }, data() { return { @@ -160,6 +166,8 @@ export default { connectStartPos: null, // 仅初次加载自动适配一次,避免编辑时视图跳动 hasFitted: false, + resizeObserver: null, + resizeTimer: null, }; }, computed: { @@ -180,6 +188,19 @@ export default { mounted() { this.loadMaterials(); if (this.routepack) this.reload(); + if (typeof ResizeObserver !== "undefined") { + this.resizeObserver = new ResizeObserver(() => { + clearTimeout(this.resizeTimer); + this.resizeTimer = setTimeout(() => { + if (this.hasFitted) this.fitToWidth(false); + }, 80); + }); + this.resizeObserver.observe(this.$refs.canvas); + } + }, + beforeUnmount() { + if (this.resizeObserver) this.resizeObserver.disconnect(); + clearTimeout(this.resizeTimer); }, methods: { /** 加载左侧物料面板列表 */ @@ -252,11 +273,11 @@ export default { target: tid, label: (r.process_name || "") + (locked ? " 🔒" : ""), data: { route: r, locked }, - type: "smoothstep", + type: "dagre", markerEnd: "arrowclosed", style: { stroke: locked ? "#c0c4cc" : "#409eff", - strokeWidth: locked ? 1.5 : 2, + strokeWidth: locked ? 1.7 : 2.2, strokeDasharray: locked ? "5 4" : undefined, }, labelStyle: { fill: "#5b6370", fontSize: "12px", fontWeight: 500 }, @@ -290,11 +311,24 @@ export default { this.seedNodes = this.seedNodes.filter((s) => !routeNodeIds.has(s.id)); const prevCount = this.nodes.length; - this.edges = edges; - this.nodes = layoutGraph(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.doFit(); + if (this.hasFitted && this.nodes.length !== prevCount) this.fitToWidth(); + }, + + /** 编辑与查看使用一致的 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(先节点后边,避免边找不到端点) */ @@ -306,15 +340,36 @@ export default { /** 重新布局并适应画布 */ relayout() { - this.nodes = layoutGraph([...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.doFit(); + this.setEdges(this.edges); + this.fitToWidth(); }, - doFit() { + /** 按宽度保持节点可读,长流程通过滚轮或拖动画布纵向浏览 */ + fitToWidth(animated = true) { this.$nextTick(() => { try { - this.fitView({ padding: 0.25, maxZoom: 1, duration: 300 }); + const canvas = this.$refs.canvas; + if (!canvas || !this.nodes.length) return; + const xs = this.nodes.map((n) => n.position.x); + const ys = this.nodes.map((n) => n.position.y); + const minX = Math.min(...xs); + const maxX = Math.max(...xs) + NODE_W; + const minY = Math.min(...ys); + const paddingX = 48; + const paddingTop = 36; + const graphWidth = Math.max(maxX - minX, NODE_W); + const zoom = Math.min(1, Math.max(0.2, (canvas.clientWidth - paddingX * 2) / graphWidth)); + const x = (canvas.clientWidth - graphWidth * zoom) / 2 - minX * zoom; + const y = paddingTop - minY * zoom; + this.setViewport( + { x, y, zoom }, + { duration: animated && this.hasFitted ? 200 : 0 } + ); + this.hasFitted = true; } catch (e) { /* noop */ } @@ -325,8 +380,7 @@ export default { onNodesInit() { if (this.hasFitted) return; if (this.nodes.length === 0) return; - this.hasFitted = true; - this.doFit(); + this.fitToWidth(); }, /* ============ 左侧面板拖拽入画布 ============ */ diff --git a/src/components/xtFlowViewer.vue b/src/components/xtFlowViewer.vue index 15119b9a..938f70f6 100644 --- a/src/components/xtFlowViewer.vue +++ b/src/components/xtFlowViewer.vue @@ -3,7 +3,7 @@
- 适应画布 + 适应宽度
原料 @@ -11,11 +11,13 @@ 成品
-
+
+ @@ -39,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 } 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"; @@ -54,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: () => [] }, @@ -62,14 +68,17 @@ export default { }, setup() { const flowId = "xt-flow-viewer-" + ++viewerSeq; - const { fitView, setNodes, setEdges } = useVueFlow(flowId); - return { flowId, fitView, setNodes, setEdges }; + const { setViewport, setNodes, setEdges } = useVueFlow(flowId); + return { flowId, setViewport, setNodes, setEdges }; }, data() { return { // 首次 fit 不带动画,避免打开时视图跳动 hasFitted: false, rebuildPending: false, + renderedNodes: [], + resizeObserver: null, + resizeTimer: null, }; }, computed: { @@ -87,6 +96,19 @@ export default { }, mounted() { this.rebuild(); + if (typeof ResizeObserver !== "undefined") { + this.resizeObserver = new ResizeObserver(() => { + clearTimeout(this.resizeTimer); + this.resizeTimer = setTimeout(() => { + if (this.hasFitted) this.fitToWidth(false); + }, 80); + }); + this.resizeObserver.observe(this.$refs.canvas); + } + }, + beforeUnmount() { + if (this.resizeObserver) this.resizeObserver.disconnect(); + clearTimeout(this.resizeTimer); }, methods: { /** nodes/edges 通常被先后赋值,用微任务合并成一次 rebuild,避免双次 dagre 布局 */ @@ -104,19 +126,19 @@ export default { const targetIds = new Set(); 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); 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: "smoothstep", + type: "dagre", markerEnd: "arrowclosed", - style: { stroke: "#409eff", strokeWidth: 2 }, + style: { stroke: "#409eff", strokeWidth: 2.2 }, labelStyle: { fill: "#5b6370", fontSize: "12px", fontWeight: 500 }, labelBgStyle: { fill: "#ffffff", stroke: "#e4e7ed", strokeWidth: 1 }, labelBgPadding: [7, 4], @@ -137,19 +159,49 @@ export default { const nodeIds = new Set(vNodes.map((n) => n.id)); const vEdges = rawEdges.filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target)); + const laidOutGraph = layoutGraphWithEdges(vNodes, vEdges, { + rankdir: this.rankdir, + 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(layoutGraph(vNodes, vEdges, { rankdir: this.rankdir })); - this.setEdges(vEdges); - // 首次挂载交给 nodes-initialized(尺寸测量完成)再 fit;数据更新时尺寸已就绪,直接 fit - if (this.hasFitted) this.doFit(); + this.setNodes(this.renderedNodes); + this.setEdges(routedGraph.edges); + // 首次挂载交给 nodes-initialized(尺寸测量完成)再定位;数据更新时直接重新适配宽度 + if (this.hasFitted) this.fitToWidth(); }, onNodesInit() { - this.doFit(); + this.fitToWidth(); }, - doFit() { + /** + * 只按宽度缩放,长流程不再为了塞进高度而缩成缩略图。 + * 图从画布顶部开始展示,用户可拖动画布继续向下查看。 + */ + fitToWidth(animated = true) { this.$nextTick(() => { try { - this.fitView({ padding: 0.2, maxZoom: 1, duration: this.hasFitted ? 200 : 0 }); + const canvas = this.$refs.canvas; + if (!canvas || !this.renderedNodes.length) return; + const xs = this.renderedNodes.map((n) => n.position.x); + const ys = this.renderedNodes.map((n) => n.position.y); + const minX = Math.min(...xs); + const maxX = Math.max(...xs) + NODE_W; + const minY = Math.min(...ys); + const paddingX = 48; + const paddingTop = 36; + const graphWidth = Math.max(maxX - minX, NODE_W); + const zoom = Math.min(1, Math.max(0.2, (canvas.clientWidth - paddingX * 2) / graphWidth)); + const x = (canvas.clientWidth - graphWidth * zoom) / 2 - minX * zoom; + const y = paddingTop - minY * zoom; + this.setViewport( + { x, y, zoom }, + { duration: animated && this.hasFitted ? 200 : 0 } + ); this.hasFitted = true; } catch (e) { /* noop */ diff --git a/src/views/mtm/process.vue b/src/views/mtm/process.vue index 2ab1573d..8873f000 100644 --- a/src/views/mtm/process.vue +++ b/src/views/mtm/process.vue @@ -59,14 +59,9 @@ - + @@ -211,6 +206,11 @@ export default { 20:'切分', 30:'结合' }, + wmScopes:{ + 10:'工段', + 20:'部门', + 30:'全局' + }, currentItem:{}, selectValue:[], visible:false, diff --git a/src/views/mtm/process_form.vue b/src/views/mtm/process_form.vue index 405d00d0..0b4cef92 100644 --- a/src/views/mtm/process_form.vue +++ b/src/views/mtm/process_form.vue @@ -84,10 +84,15 @@ - - + + + + @@ -133,7 +138,7 @@ const defaultForm = { mtype:10, belong_dept: "", wpr_number_rule: "", - into_wm_mgroup: true, + into_wm_scope: 20, store_notok: true, has_ok_b_defect:false, }; @@ -165,6 +170,11 @@ export default { {name:'切分',value:20}, {name:'结合',value:30}, ], + wmScopeOptions:[ + {name:'工段',value:10}, + {name:'部门',value:20}, + {name:'全局',value:30}, + ], visible: false, isSaveing: false, deptOptions: [], diff --git a/src/views/mtm/route_form.vue b/src/views/mtm/route_form.vue index aefbcd5d..5b04df8c 100644 --- a/src/views/mtm/route_form.vue +++ b/src/views/mtm/route_form.vue @@ -13,6 +13,7 @@ ref="dialogForm" :model="form" :rules="rules" + :disabled="mode === 'show'" label-position="right" label-width="100px" > @@ -197,7 +198,7 @@ - + @@ -226,9 +227,10 @@ - + - + + + +
@@ -66,16 +82,16 @@ + \ No newline at end of file + diff --git a/src/views/wf/ticketdetail.vue b/src/views/wf/ticketdetail.vue index 740bb269..bc1e7b3e 100644 --- a/src/views/wf/ticketdetail.vue +++ b/src/views/wf/ticketdetail.vue @@ -316,6 +316,14 @@
+ 接单 { that.mainLoading = false; that.ticketDetail = res; @@ -791,11 +802,11 @@ export default { if (this.ticketDetail.create_by == this.userId) { this.isOwn = true; } - let participant = this.ticketDetail.participant; - if ( - participant == this.userId || - participant.indexOf(this.userId) > -1 - ) { + const participant = this.ticketDetail.participant; + const participants = Array.isArray(participant) + ? participant + : [participant]; + if (participants.map(String).includes(String(this.userId))) { this.isDuty = true; } }).catch((e) => { @@ -806,21 +817,37 @@ export default { getBtns() { let that = this; that.audit_imgs_show = false; - that.$API.wf.ticket.ticketTransitions.req(that.ticketId).then((res) => { - that.operationBtn = res; + that.audit_work_show = false; + that.needAccept = false; + that.$API.wf.ticket.ticketAvailableActions.req(that.ticketId).then((res) => { + that.operationBtn = res.transitions; + that.needAccept = res.need_accept; + if (res.permission || res.need_accept) { + that.isDuty = true; + } console.log("operationBtn", that.operationBtn); - if(res.length>0){ - for (let i = 0; i < res.length; i++) { - if(res[i].on_submit_func=="apps.opm.services.check_opl_audit_imgs"){ + if(that.operationBtn.length>0){ + for (let i = 0; i < that.operationBtn.length; i++) { + if(that.operationBtn[i].on_submit_func=="apps.opm.services.check_opl_audit_imgs"){ that.audit_imgs_show = true; } - if(res[i].on_submit_func=="apps.opm.services.check_opl_work_imgs"){ + if(that.operationBtn[i].on_submit_func=="apps.opm.services.check_opl_work_imgs"){ that.audit_work_show = true; } } } }); }, + handleAccept() { + this.acceptLoading = true; + this.$API.wf.ticket.ticketAccept.req(this.ticketId, {}).then(() => { + this.$message.success("接单成功"); + this.getticketItem(); + this.getBtns(); + }).finally(() => { + this.acceptLoading = false; + }); + }, //访客详情 getVisit() { this.$API.vm.visit.read.req(this.projectId).then((res) => { diff --git a/src/views/wpm_bx/mlog_detail.vue b/src/views/wpm_bx/mlog_detail.vue index c123253b..7e22bbde 100644 --- a/src/views/wpm_bx/mlog_detail.vue +++ b/src/views/wpm_bx/mlog_detail.vue @@ -957,7 +957,7 @@ export default { if(that.mlogItem.material_out_&&that.mlogItem.material_out_.tracking==10){ that.$refs.tableOut.refresh(); }else{ - that.$refs.checkTable.refreshfun(); + that.$refs.checkTable?.refreshfun?.(); } //删除in记录后,out也要删除相应的记录 }); @@ -1060,7 +1060,7 @@ export default { if(this.mlogItem.material_out_&&this.mlogItem.material_out_.tracking==10){ this.$refs.tableOut.refresh(); }else{ - this.$refs.checkTable.refreshfun(); + this.$refs.checkTable?.refreshfun?.(); } }, handleCheckSuccess() { @@ -1081,7 +1081,7 @@ export default { if(this.mlogItem.material_out_&&this.mlogItem.material_out_.tracking==10){ this.$refs.tableOut.refresh(); }else{ - this.$refs.checkTable.refreshfun(); + this.$refs.checkTable?.refreshfun?.(); } }, //编辑成功后的方法调用 @@ -1099,7 +1099,7 @@ export default { if(this.mlogItem.material_out_&&this.mlogItem.material_out_.tracking==10){ this.$refs.tableOut.refresh(); }else{ - this.$refs.checkTable.refreshfun(); + this.$refs.checkTable?.refreshfun?.(); } }, checkDialogClose(){