修复反向追溯图谱迁移问题
This commit is contained in:
@@ -29,78 +29,79 @@
|
||||
|
||||
<div v-loading="loading" class="traceability-page">
|
||||
<el-empty v-if="!hasGraph" :description="$t(key('enter_battery_id'))" />
|
||||
<template v-else>
|
||||
<div class="trace-toolbar">
|
||||
<div class="trace-toolbar__group">
|
||||
<span class="toolbar-label">{{ $t(key('view_type')) }}</span>
|
||||
<el-radio-group v-model="viewType" size="mini">
|
||||
<RelationGraph
|
||||
v-else
|
||||
ref="graphRef"
|
||||
class="trace-graph"
|
||||
:options="currentGraphOptions"
|
||||
:on-node-click="onNodeClick"
|
||||
>
|
||||
<template #graph-plug>
|
||||
<div class="trace-panel">
|
||||
<div class="trace-panel__label">{{ $t(key('view_type')) }}:</div>
|
||||
<el-radio-group v-model="currentCase" size="small" @change="renderGraph">
|
||||
<el-radio-button label="horizontal">{{ $t(key('horizontal')) }}</el-radio-button>
|
||||
<el-radio-button label="vertical">{{ $t(key('vertical')) }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="trace-toolbar__locate">
|
||||
<span class="toolbar-label">{{ $t(key('position')) }}</span>
|
||||
<el-input v-model.trim="locator.item_code" size="mini" clearable :placeholder="$t(key('enter_item_code'))" />
|
||||
<el-button size="mini" type="primary" @click="locateNode('item_code', locator.item_code)">
|
||||
{{ $t(key('confirm_position')) }}
|
||||
</el-button>
|
||||
<el-input v-model.trim="locator.item_batch" size="mini" clearable :placeholder="$t(key('enter_item_batch'))" />
|
||||
<el-button size="mini" type="primary" @click="locateNode('item_batch', locator.item_batch)">
|
||||
{{ $t(key('confirm_position')) }}
|
||||
</el-button>
|
||||
<el-input
|
||||
v-model.trim="locator.workingsubclass_code"
|
||||
size="mini"
|
||||
clearable
|
||||
:placeholder="$t(key('enter_work_unit'))"
|
||||
/>
|
||||
<el-button size="mini" type="primary" @click="locateNode('workingsubclass_code', locator.workingsubclass_code, true)">
|
||||
{{ $t(key('confirm_position')) }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="['trace-tree-wrap', 'trace-tree-wrap--' + viewType]">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
class="trace-tree"
|
||||
:data="treeData"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<div :class="['trace-node', { 'is-active': activeNodeId === data.id }]" :data-node-id="data.id" @click="activeNodeId = data.id">
|
||||
<div class="trace-node__title">
|
||||
<el-tag size="mini" type="warning" effect="dark">{{ data.data.type_name || '-' }}</el-tag>
|
||||
<strong>{{ data.text || data.id }}</strong>
|
||||
</div>
|
||||
<div class="trace-node__body">
|
||||
<p>{{ $t(key('item_name')) }}: {{ data.data.item_name || '-' }}</p>
|
||||
<p>{{ $t(key('item_code')) }}: {{ data.data.item_code || '-' }}</p>
|
||||
<p>{{ $t(key('item_batch')) }}: {{ data.data.item_batch || '-' }}</p>
|
||||
<p>{{ $t(key('work_unit')) }}: {{ data.data.workingsubclass_code || '-' }}</p>
|
||||
<p>{{ $t(key('work_unit_name')) }}: {{ data.data.workingsubclass_name || '-' }}</p>
|
||||
<p>{{ $t(key('process_code')) }}: {{ data.data.process_code || '-' }}</p>
|
||||
<p>{{ $t(key('start_time')) }}: {{ data.data.start_time || '-' }}</p>
|
||||
<p>{{ $t(key('finish_time')) }}: {{ data.data.finish_time || '-' }}</p>
|
||||
<p>{{ $t(key('inspection_person')) }}: {{ data.data.inspection_person || '-' }}</p>
|
||||
<p>{{ $t(key('inspection_time')) }}: {{ data.data.inspection_time || '-' }}</p>
|
||||
<p>{{ $t(key('quality_person')) }}: {{ data.data.quality_person || '-' }}</p>
|
||||
<p>{{ $t(key('quality_time')) }}: {{ data.data.quality_time || '-' }}</p>
|
||||
<p>{{ $t(key('device_name')) }}: {{ data.data.device_name || '-' }}</p>
|
||||
<p>{{ $t(key('device_code')) }}: {{ data.data.device_code || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</template>
|
||||
<div class="trace-panel__label trace-panel__label--spaced">{{ $t(key('position')) }}:</div>
|
||||
<div class="trace-panel__row">
|
||||
<el-input v-model.trim="locator.item_code" size="small" clearable :placeholder="$t(key('enter_item_code'))" />
|
||||
<el-button size="small" type="primary" @click="locateNode('item_code', locator.item_code)">
|
||||
{{ $t(key('confirm_position')) }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="trace-panel__row">
|
||||
<el-input v-model.trim="locator.item_batch" size="small" clearable :placeholder="$t(key('enter_item_batch'))" />
|
||||
<el-button size="small" type="primary" @click="locateNode('item_batch', locator.item_batch)">
|
||||
{{ $t(key('confirm_position')) }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="trace-panel__row">
|
||||
<el-input
|
||||
v-model.trim="locator.workingsubclass_code"
|
||||
size="small"
|
||||
clearable
|
||||
:placeholder="$t(key('enter_work_unit'))"
|
||||
/>
|
||||
<el-button size="small" type="primary" @click="locateNode('workingsubclass_code', locator.workingsubclass_code, true)">
|
||||
{{ $t(key('confirm_position')) }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #node="{ node }">
|
||||
<div class="trace-node" :class="{ 'is-active': activeNodeId === String(node.id) }">
|
||||
<div class="trace-node__tag">
|
||||
<el-tag type="warning" effect="dark" size="small">{{ node.data.type_name || '-' }}</el-tag>
|
||||
</div>
|
||||
<div class="trace-node__title">{{ node.text || node.id }}</div>
|
||||
<div class="trace-node__body">
|
||||
<div class="trace-node__line">{{ $t(key('item_name')) }}: {{ node.data.item_name || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('item_code')) }}: {{ node.data.item_code || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('item_batch')) }}: {{ node.data.item_batch || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('work_unit')) }}: {{ node.data.workingsubclass_code || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('work_unit_name')) }}: {{ node.data.workingsubclass_name || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('process_code')) }}: {{ node.data.process_code || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('start_time')) }}: {{ node.data.start_time || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('finish_time')) }}: {{ node.data.finish_time || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('inspection_person')) }}: {{ node.data.inspection_person || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('inspection_time')) }}: {{ node.data.inspection_time || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('quality_person')) }}: {{ node.data.quality_person || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('quality_time')) }}: {{ node.data.quality_time || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('device_name')) }}: {{ node.data.device_name || '-' }}</div>
|
||||
<div class="trace-node__line">{{ $t(key('device_code')) }}: {{ node.data.device_code || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</RelationGraph>
|
||||
</div>
|
||||
</d2-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RelationGraph from 'relation-graph'
|
||||
import { i18nMixin } from '@/composables/useI18n'
|
||||
import {
|
||||
exportBackwardTraceabilityTree,
|
||||
@@ -109,6 +110,9 @@ import {
|
||||
|
||||
export default {
|
||||
name: 'data-platform-traceability-backward',
|
||||
components: {
|
||||
RelationGraph
|
||||
},
|
||||
mixins: [i18nMixin('page.data_platform.traceability.backward')],
|
||||
data () {
|
||||
return {
|
||||
@@ -117,7 +121,7 @@ export default {
|
||||
battery_id: this.$route.params.battery_id || ''
|
||||
},
|
||||
traceData: {},
|
||||
viewType: 'horizontal',
|
||||
currentCase: 'horizontal',
|
||||
activeNodeId: '',
|
||||
locator: {
|
||||
item_code: '',
|
||||
@@ -133,31 +137,85 @@ export default {
|
||||
hasGraph () {
|
||||
return Array.isArray(this.graph.nodes) && this.graph.nodes.length > 0
|
||||
},
|
||||
treeData () {
|
||||
if (!this.hasGraph) return []
|
||||
const nodeMap = new Map()
|
||||
this.graph.nodes.forEach(node => {
|
||||
nodeMap.set(String(node.id), {
|
||||
...node,
|
||||
id: String(node.id),
|
||||
data: node.data || {},
|
||||
children: []
|
||||
})
|
||||
})
|
||||
const childIds = new Set()
|
||||
const lines = Array.isArray(this.graph.lines) ? this.graph.lines : []
|
||||
lines.forEach(line => {
|
||||
const from = nodeMap.get(String(line.from))
|
||||
const to = nodeMap.get(String(line.to))
|
||||
if (from && to) {
|
||||
from.children.push(to)
|
||||
childIds.add(to.id)
|
||||
horizontalOptions () {
|
||||
return {
|
||||
backgroundImageNoRepeat: true,
|
||||
defaultNodeBorderWidth: 0,
|
||||
defaultLineShape: 3,
|
||||
defaultNodeShape: 0,
|
||||
defaultJunctionPoint: 'tb',
|
||||
defaultNodeWidth: 330,
|
||||
defaultNodeHeight: 430,
|
||||
defaultFocusRootNode: false,
|
||||
downloadImageFileName: this.downloadName,
|
||||
defaultLineColor: 'rgba(0, 186, 189, 1)',
|
||||
defaultNodeColor: 'rgba(0, 206, 209, 1)',
|
||||
moveToCenterWhenResize: true,
|
||||
defaultLineMarker: {
|
||||
markerWidth: 12,
|
||||
markerHeight: 12,
|
||||
refX: '10',
|
||||
refY: 6,
|
||||
data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
|
||||
},
|
||||
layouts: [
|
||||
{
|
||||
label: this.$t(this.key('center')),
|
||||
layoutName: 'tree',
|
||||
layoutClassName: 'seeks-layout-center',
|
||||
centerOffset_x: 0,
|
||||
centerOffset_y: 0,
|
||||
distance_coefficient: 1,
|
||||
layoutDirection: 'v',
|
||||
from: 'top',
|
||||
min_per_width: 560,
|
||||
max_per_width: 1100,
|
||||
min_per_height: 560,
|
||||
max_per_height: 1100
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
verticalOptions () {
|
||||
return {
|
||||
defaultNodeShape: 1,
|
||||
defaultNodeBorderWidth: 0,
|
||||
defaultNodeWidth: 330,
|
||||
defaultNodeHeight: 430,
|
||||
defaultLineShape: 3,
|
||||
defaultJunctionPoint: 'lr',
|
||||
defaultFocusRootNode: false,
|
||||
downloadImageFileName: this.downloadName,
|
||||
defaultLineColor: 'rgba(0, 186, 189, 1)',
|
||||
defaultNodeColor: 'rgba(0, 206, 209, 1)',
|
||||
moveToCenterWhenResize: true,
|
||||
layout: {
|
||||
label: this.$t(this.key('center')),
|
||||
layoutName: 'tree',
|
||||
layoutClassName: 'seeks-layout-center',
|
||||
defaultNodeShape: 0,
|
||||
defaultLineShape: 1,
|
||||
from: 'left',
|
||||
min_per_width: 660,
|
||||
max_per_width: 1100,
|
||||
min_per_height: 560,
|
||||
max_per_height: 700
|
||||
},
|
||||
defaultLineMarker: {
|
||||
markerWidth: 12,
|
||||
markerHeight: 12,
|
||||
refX: 6,
|
||||
refY: 6,
|
||||
data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
|
||||
}
|
||||
})
|
||||
const rootId = this.graph.rootId ? String(this.graph.rootId) : ''
|
||||
const root = rootId && nodeMap.get(rootId)
|
||||
if (root) return [root]
|
||||
return Array.from(nodeMap.values()).filter(node => !childIds.has(node.id))
|
||||
}
|
||||
},
|
||||
currentGraphOptions () {
|
||||
return this.currentCase === 'horizontal' ? this.horizontalOptions : this.verticalOptions
|
||||
},
|
||||
downloadName () {
|
||||
const firstNode = this.graph.nodes && this.graph.nodes[0]
|
||||
return firstNode && firstNode.text ? firstNode.text : this.$t(this.key('reverse'))
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -178,6 +236,7 @@ export default {
|
||||
this.traceData = data || {}
|
||||
const firstNode = this.graph.nodes && this.graph.nodes[0]
|
||||
this.activeNodeId = firstNode ? String(firstNode.id) : ''
|
||||
this.$nextTick(this.renderGraph)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
@@ -192,10 +251,17 @@ export default {
|
||||
workingsubclass_code: ''
|
||||
}
|
||||
},
|
||||
renderGraph () {
|
||||
if (!this.hasGraph || !this.$refs.graphRef) return
|
||||
this.$refs.graphRef.setOptions(this.currentGraphOptions)
|
||||
this.$refs.graphRef.setJsonData(this.graph)
|
||||
},
|
||||
onNodeClick (nodeObject) {
|
||||
this.activeNodeId = String(nodeObject.id)
|
||||
},
|
||||
locateNode (field, value, onlyFinalProduct = false) {
|
||||
if (!value) return
|
||||
const nodes = Array.isArray(this.graph.nodes) ? this.graph.nodes : []
|
||||
const target = nodes.find(node => {
|
||||
if (!value || !this.hasGraph) return
|
||||
const target = this.graph.nodes.find(node => {
|
||||
const data = node.data || {}
|
||||
if (String(data[field] || '') !== String(value)) return false
|
||||
if (!onlyFinalProduct) return true
|
||||
@@ -206,14 +272,11 @@ export default {
|
||||
return
|
||||
}
|
||||
this.activeNodeId = String(target.id)
|
||||
if (this.$refs.tree) {
|
||||
this.$refs.tree.setCurrentKey(this.activeNodeId)
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
const element = this.$el.querySelector(`[data-node-id="${this.activeNodeId}"]`)
|
||||
if (element && element.scrollIntoView) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' })
|
||||
}
|
||||
const graphInstance = this.$refs.graphRef && this.$refs.graphRef.getInstance()
|
||||
if (!graphInstance) return
|
||||
graphInstance.setZoom(200)
|
||||
graphInstance.focusNodeById(target.id)
|
||||
})
|
||||
},
|
||||
async exportTree () {
|
||||
@@ -249,87 +312,96 @@ export default {
|
||||
}
|
||||
|
||||
.traceability-page {
|
||||
min-height: 560px;
|
||||
height: calc(100vh - 170px);
|
||||
min-height: 620px;
|
||||
}
|
||||
|
||||
.trace-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 18px;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
.trace-graph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 620px;
|
||||
border: 1px solid #ebeef5;
|
||||
background: #f7f9fb;
|
||||
}
|
||||
|
||||
.trace-toolbar__group,
|
||||
.trace-toolbar__locate {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
.trace-panel {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
z-index: 20;
|
||||
width: 360px;
|
||||
padding: 12px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.trace-toolbar__locate .el-input {
|
||||
width: 190px;
|
||||
}
|
||||
|
||||
.toolbar-label {
|
||||
.trace-panel__label {
|
||||
margin-bottom: 8px;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.trace-tree-wrap {
|
||||
height: calc(100vh - 230px);
|
||||
min-height: 520px;
|
||||
overflow: auto;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
background: #f7f9fb;
|
||||
padding: 12px;
|
||||
.trace-panel__label--spaced {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.trace-tree-wrap--horizontal {
|
||||
::v-deep .el-tree-node__children {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.trace-tree {
|
||||
min-width: max-content;
|
||||
background: transparent;
|
||||
.trace-panel__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 92px;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.trace-node {
|
||||
width: 300px;
|
||||
min-height: 300px;
|
||||
padding: 10px;
|
||||
margin: 8px 0;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
width: 330px;
|
||||
height: 410px;
|
||||
padding: 28px 12px 12px;
|
||||
color: #303133;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.trace-node.is-active {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15);
|
||||
.trace-node.is-active .trace-node__body {
|
||||
border-color: #f56c6c;
|
||||
box-shadow: 0 0 0 2px rgba(245, 108, 108, 0.18);
|
||||
}
|
||||
|
||||
.trace-node__tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.trace-node__title {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
min-height: 22px;
|
||||
margin-bottom: 8px;
|
||||
padding-right: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.trace-node__body p {
|
||||
margin: 0;
|
||||
.trace-node__body {
|
||||
height: 340px;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.trace-node__line {
|
||||
min-height: 24px;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
line-height: 1.35;
|
||||
border-bottom: 1px solid #efefef;
|
||||
line-height: 1.4;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user