Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2
This commit is contained in:
36
docs/功能测试-反向追溯.md
Normal file
36
docs/功能测试-反向追溯.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# 功能测试 - 反向追溯
|
||||||
|
|
||||||
|
> 模块:数据中台 / 基础追溯 / 反向追溯 (Backward Traceability)
|
||||||
|
> 路由:`/data_middleground/basic_traceability/reverse_direction_traceability`
|
||||||
|
|
||||||
|
## 测试前置条件
|
||||||
|
|
||||||
|
- 测试账号具备访问“反向追溯”的菜单权限。
|
||||||
|
- 准备至少 2 个电池条码:
|
||||||
|
- 一个存在完整反向追溯关系图的电池条码。
|
||||||
|
- 一个不存在追溯数据或数据为空的电池条码。
|
||||||
|
- 后端接口 `data_middleground/basic_traceability/reverse_direction_traceability/get_data` 和 `export_tree` 可正常访问。
|
||||||
|
|
||||||
|
## 测试任务列表
|
||||||
|
|
||||||
|
| 序号 | 测试项 | 操作步骤 | 预期结果 |
|
||||||
|
|---:|---|---|---|
|
||||||
|
| 1 | 页面入口 | 从菜单进入“反向追溯”,或直接访问 `/data_middleground/basic_traceability/reverse_direction_traceability` | 页面正常打开,显示电池条码输入框、查询、导出、重置按钮和空态区域 |
|
||||||
|
| 2 | 空输入查询 | 不输入电池条码,点击“查询” | 页面提示请输入电池条码,不发起有效查询 |
|
||||||
|
| 3 | 有效条码查询 | 输入存在追溯数据的电池条码,点击“查询” | 页面展示追溯节点树,节点包含物料名称、编码、批次、工序、时间、设备等信息 |
|
||||||
|
| 4 | 无数据条码查询 | 输入不存在追溯数据的电池条码,点击“查询” | 页面保持空态或显示无节点,不出现脚本错误 |
|
||||||
|
| 5 | 横向/纵向切换 | 查询出数据后,切换“横向图谱”和“纵向图谱” | 追溯节点展示方向切换,页面不丢失数据 |
|
||||||
|
| 6 | 物料编码定位 | 查询出数据后,输入某个节点的物料编码,点击定位确认 | 匹配节点高亮并滚动到可视区域 |
|
||||||
|
| 7 | 物料批次定位 | 输入某个节点的物料批次,点击定位确认 | 匹配节点高亮并滚动到可视区域 |
|
||||||
|
| 8 | 工序单元定位 | 输入某个制成品节点的工序单元编码,点击定位确认 | 匹配节点高亮并滚动到可视区域;不存在时提示未找到匹配节点 |
|
||||||
|
| 9 | 导出前校验 | 未输入电池条码时点击“导出” | 页面提示请输入电池条码 |
|
||||||
|
| 10 | 查询前导出 | 输入电池条码但未查询出追溯树,点击“导出” | 页面提示请先查询追溯数据后再导出 |
|
||||||
|
| 11 | 正常导出 | 查询出追溯树后点击“导出” | 调用导出接口,浏览器开始下载或打开导出文件 |
|
||||||
|
| 12 | 重置功能 | 查询出追溯树后点击“重置” | 输入框、追溯树、定位输入和高亮状态全部清空 |
|
||||||
|
| 13 | 国际化检查 | 切换中英文语言后重新进入页面 | 页面按钮、节点字段和提示文案随语言切换显示 |
|
||||||
|
|
||||||
|
## 回归关注点
|
||||||
|
|
||||||
|
- 接口返回 `relation_graph.nodes` 或 `relation_graph.lines` 为空时页面不能报错。
|
||||||
|
- 导出接口参数中的 `tree_list` 应为当前追溯树节点数组 JSON。
|
||||||
|
- 路由参数携带 `battery_id` 时,页面应能自动查询对应追溯数据。
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
|
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
|
||||||
|
|
||||||
- 总功能数:79
|
- 总功能数:79
|
||||||
- 已迁移:27
|
- 已迁移:28
|
||||||
- 未迁移:52
|
- 未迁移:51
|
||||||
|
|
||||||
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|
||||||
|:---:|---|---|---|---|---|
|
|:---:|---|---|---|---|---|
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
| ⬜ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | NPChart (NP Chart) | | 待确认 |
|
| ⬜ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | NPChart (NP Chart) | | 待确认 |
|
||||||
| ⬜ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | CChart (C Chart) | | 待确认 |
|
| ⬜ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | CChart (C Chart) | | 待确认 |
|
||||||
| ⬜ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | UChart (U Chart) | | 待确认 |
|
| ⬜ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | UChart (U Chart) | | 待确认 |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 反向追溯 (Backward Traceability) | | 待确认 |
|
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 反向追溯 (Backward Traceability) | 反向追溯 | `src/views/data-platform/traceability/backward/` |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 正向追溯 (Forward Traceability) | | 待确认 |
|
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 正向追溯 (Forward Traceability) | | 待确认 |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池曲线 (Battery Curve) | | 待确认 |
|
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池曲线 (Battery Curve) | | 待确认 |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 托盘追溯 (Tray Traceability) | | 待确认 |
|
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 托盘追溯 (Tray Traceability) | | 待确认 |
|
||||||
|
|||||||
27
src/api/data-platform/traceability/backward.js
Normal file
27
src/api/data-platform/traceability/backward.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'data_middleground/basic_traceability/reverse_direction_traceability/'
|
||||||
|
|
||||||
|
function apiParams (method, data = {}) {
|
||||||
|
return {
|
||||||
|
method,
|
||||||
|
platform: 'background',
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBackwardTraceabilityData (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'get_data',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('data_middleground_basic_traceability_reverse_direction_traceability_get_data', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportBackwardTraceabilityTree (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'export_tree',
|
||||||
|
method: 'post',
|
||||||
|
data: apiParams('data_middleground_basic_traceability_reverse_direction_traceability_export_tree', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1255,6 +1255,44 @@
|
|||||||
"accumulated_detail": "{device_name} ({device_code}): 10 Cumulative Error Details",
|
"accumulated_detail": "{device_name} ({device_code}): 10 Cumulative Error Details",
|
||||||
"workstation_detail": "Workstation [{device_name}] Channel Error Details"
|
"workstation_detail": "Workstation [{device_name}] Channel Error Details"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"data_platform": {
|
||||||
|
"traceability": {
|
||||||
|
"backward": {
|
||||||
|
"query": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"export": "Export",
|
||||||
|
"battery_id": "Battery Barcode",
|
||||||
|
"enter_battery_id": "Enter battery barcode",
|
||||||
|
"view_type": "Graph Layout",
|
||||||
|
"horizontal": "Horizontal",
|
||||||
|
"vertical": "Vertical",
|
||||||
|
"position": "Locate",
|
||||||
|
"enter_item_code": "Enter material code",
|
||||||
|
"enter_item_batch": "Enter material batch",
|
||||||
|
"enter_work_unit": "Enter process unit",
|
||||||
|
"item_name": "Material Name",
|
||||||
|
"item_code": "Material Code",
|
||||||
|
"item_batch": "Material Batch",
|
||||||
|
"work_unit": "Process Unit",
|
||||||
|
"work_unit_name": "Process Unit Name",
|
||||||
|
"process_code": "Process Code",
|
||||||
|
"operator": "Material Handler",
|
||||||
|
"start_time": "Feeding Time",
|
||||||
|
"finish_time": "Completion Time",
|
||||||
|
"confirm_position": "Confirm Location",
|
||||||
|
"reverse": "Reverse Traceability",
|
||||||
|
"center": "Center",
|
||||||
|
"inspection_person": "Inspector",
|
||||||
|
"inspection_time": "Inspection Time",
|
||||||
|
"quality_person": "Quality Reviewer",
|
||||||
|
"quality_time": "Quality Review Time",
|
||||||
|
"device_name": "Device Name",
|
||||||
|
"device_code": "Device Code",
|
||||||
|
"node_not_found": "No matching node found",
|
||||||
|
"query_before_export": "Query traceability data before export"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"__MENU_TEMP_BEGIN__": "===== 以下为临时菜单翻译,后续统一删除 =====",
|
"__MENU_TEMP_BEGIN__": "===== 以下为临时菜单翻译,后续统一删除 =====",
|
||||||
|
|||||||
@@ -1255,6 +1255,44 @@
|
|||||||
"accumulated_detail": "{device_name}({device_code}) 10次累计通道异常详情",
|
"accumulated_detail": "{device_name}({device_code}) 10次累计通道异常详情",
|
||||||
"workstation_detail": "【{device_name}】工作站设备通道异常详情"
|
"workstation_detail": "【{device_name}】工作站设备通道异常详情"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"data_platform": {
|
||||||
|
"traceability": {
|
||||||
|
"backward": {
|
||||||
|
"query": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"export": "导出",
|
||||||
|
"battery_id": "电池条码",
|
||||||
|
"enter_battery_id": "请输入电池条码",
|
||||||
|
"view_type": "图谱形态",
|
||||||
|
"horizontal": "横向图谱",
|
||||||
|
"vertical": "纵向图谱",
|
||||||
|
"position": "定位",
|
||||||
|
"enter_item_code": "请输入物料编码",
|
||||||
|
"enter_item_batch": "请输入物料批次",
|
||||||
|
"enter_work_unit": "请输入工序单元",
|
||||||
|
"item_name": "物料名称",
|
||||||
|
"item_code": "物料编码",
|
||||||
|
"item_batch": "物料批次",
|
||||||
|
"work_unit": "工序单元",
|
||||||
|
"work_unit_name": "工序单元名称",
|
||||||
|
"process_code": "工序编码",
|
||||||
|
"operator": "卸料人",
|
||||||
|
"start_time": "投料时间",
|
||||||
|
"finish_time": "完成时间",
|
||||||
|
"confirm_position": "定位确认",
|
||||||
|
"reverse": "反向追溯",
|
||||||
|
"center": "中心",
|
||||||
|
"inspection_person": "点检人员",
|
||||||
|
"inspection_time": "点检时间",
|
||||||
|
"quality_person": "品质确认人员",
|
||||||
|
"quality_time": "品质确认时间",
|
||||||
|
"device_name": "设备名称",
|
||||||
|
"device_code": "设备编码",
|
||||||
|
"node_not_found": "未找到匹配节点",
|
||||||
|
"query_before_export": "请先查询追溯数据后再导出"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"__MENU_TEMP_BEGIN__": "===== 以下为临时菜单翻译,后续统一删除 =====",
|
"__MENU_TEMP_BEGIN__": "===== 以下为临时菜单翻译,后续统一删除 =====",
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ export default {
|
|||||||
name: `${pre}index`,
|
name: `${pre}index`,
|
||||||
meta: { ...meta, title: '数据中台', root: '/data_middleground' },
|
meta: { ...meta, title: '数据中台', root: '/data_middleground' },
|
||||||
component: _import('system/function/module-index')
|
component: _import('system/function/module-index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'basic_traceability/reverse_direction_traceability',
|
||||||
|
name: `${pre}basic_traceability-reverse_direction_traceability`,
|
||||||
|
meta: { ...meta, cache: true, title: '反向追溯' },
|
||||||
|
component: _import('data-platform/traceability/backward')
|
||||||
}
|
}
|
||||||
])('data_middleground-')
|
])('data_middleground-')
|
||||||
}
|
}
|
||||||
|
|||||||
335
src/views/data-platform/traceability/backward/index.vue
Normal file
335
src/views/data-platform/traceability/backward/index.vue
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form ref="form" :inline="true" :model="form" size="mini">
|
||||||
|
<el-form-item :label="$t(key('battery_id'))" prop="battery_id">
|
||||||
|
<el-input
|
||||||
|
v-model="form.battery_id"
|
||||||
|
:placeholder="$t(key('enter_battery_id'))"
|
||||||
|
clearable
|
||||||
|
style="width:260px"
|
||||||
|
@keyup.enter.native="fetchData"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :disabled="loading" :loading="loading" @click="fetchData">
|
||||||
|
{{ $t(key('query')) }}
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-download" :disabled="loading" @click="exportTree">
|
||||||
|
{{ $t(key('export')) }}
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" :disabled="loading" @click="resetForm">
|
||||||
|
{{ $t(key('reset')) }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
<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>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import {
|
||||||
|
exportBackwardTraceabilityTree,
|
||||||
|
getBackwardTraceabilityData
|
||||||
|
} from '@/api/data-platform/traceability/backward'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'data-platform-traceability-backward',
|
||||||
|
mixins: [i18nMixin('page.data_platform.traceability.backward')],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
form: {
|
||||||
|
battery_id: this.$route.params.battery_id || ''
|
||||||
|
},
|
||||||
|
traceData: {},
|
||||||
|
viewType: 'horizontal',
|
||||||
|
activeNodeId: '',
|
||||||
|
locator: {
|
||||||
|
item_code: '',
|
||||||
|
item_batch: '',
|
||||||
|
workingsubclass_code: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
graph () {
|
||||||
|
return this.traceData.relation_graph || {}
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.form.battery_id) this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
ensureBatteryInput () {
|
||||||
|
if (this.form.battery_id) return true
|
||||||
|
this.$message.error(this.$t(this.key('enter_battery_id')))
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
async fetchData () {
|
||||||
|
if (!this.ensureBatteryInput()) return
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getBackwardTraceabilityData({ ...this.form })
|
||||||
|
const data = res && res.data ? res.data : res
|
||||||
|
this.traceData = data || {}
|
||||||
|
const firstNode = this.graph.nodes && this.graph.nodes[0]
|
||||||
|
this.activeNodeId = firstNode ? String(firstNode.id) : ''
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetForm () {
|
||||||
|
this.form.battery_id = ''
|
||||||
|
this.traceData = {}
|
||||||
|
this.activeNodeId = ''
|
||||||
|
this.locator = {
|
||||||
|
item_code: '',
|
||||||
|
item_batch: '',
|
||||||
|
workingsubclass_code: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
locateNode (field, value, onlyFinalProduct = false) {
|
||||||
|
if (!value) return
|
||||||
|
const nodes = Array.isArray(this.graph.nodes) ? this.graph.nodes : []
|
||||||
|
const target = nodes.find(node => {
|
||||||
|
const data = node.data || {}
|
||||||
|
if (String(data[field] || '') !== String(value)) return false
|
||||||
|
if (!onlyFinalProduct) return true
|
||||||
|
return data.type_name === '制成品' && Number(data.status) === -1
|
||||||
|
})
|
||||||
|
if (!target) {
|
||||||
|
this.$message.warning(this.$t(this.key('node_not_found')))
|
||||||
|
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' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async exportTree () {
|
||||||
|
if (!this.ensureBatteryInput()) return
|
||||||
|
if (!this.hasGraph) {
|
||||||
|
this.$message.warning(this.$t(this.key('query_before_export')))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await exportBackwardTraceabilityTree({
|
||||||
|
...this.form,
|
||||||
|
tree_list: JSON.stringify(this.graph.nodes || [])
|
||||||
|
})
|
||||||
|
const data = res && res.data ? res.data : res
|
||||||
|
this.downloadExport(data)
|
||||||
|
},
|
||||||
|
downloadExport (url) {
|
||||||
|
if (!url) return
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `${this.form.battery_id}${this.$t(this.key('reverse'))}`
|
||||||
|
link.target = '_blank'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search-bar {
|
||||||
|
margin-bottom: -18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.traceability-page {
|
||||||
|
min-height: 560px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 18px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-toolbar__group,
|
||||||
|
.trace-toolbar__locate {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-toolbar__locate .el-input {
|
||||||
|
width: 190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-label {
|
||||||
|
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-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-node {
|
||||||
|
width: 300px;
|
||||||
|
min-height: 300px;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 8px 0;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fff;
|
||||||
|
color: #303133;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-node.is-active {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-node__title {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-node__body p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
line-height: 1.35;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user