From 757dcb3fcad665eaef93074a4981c4134f7a027e Mon Sep 17 00:00:00 2001
From: zzw <228544128@qq.com>
Date: 星期日, 04 十月 2026 17:33:43 +0800
Subject: [PATCH] feat: complete system management and repair workflow updates

---
 emergency-repair-web/src/components/ProductTreePicker.vue |   89 ++++++++++++++++++++++++++++----------------
 1 files changed, 56 insertions(+), 33 deletions(-)

diff --git a/emergency-repair-web/src/components/ProductTreePicker.vue b/emergency-repair-web/src/components/ProductTreePicker.vue
index 2e82ab6..e578ed2 100644
--- a/emergency-repair-web/src/components/ProductTreePicker.vue
+++ b/emergency-repair-web/src/components/ProductTreePicker.vue
@@ -7,7 +7,9 @@
           ref="tree"
           :data="tree"
           node-key="id"
-          default-expand-all
+          v-loading="loading"
+          lazy
+          :load="loadNode"
           show-checkbox
           check-strictly
           :filter-node-method="filterNode"
@@ -51,7 +53,7 @@
     selectedIds: { type: Array, default: () => [] }
   },
   data() {
-    return { localVisible: false, keyword: '', tree: [], checkedCount: 0, selectedDevices: [] }
+    return { localVisible: false, keyword: '', tree: [], checkedCount: 0, selectedDevices: [], selectedMap: {}, loading: false }
   },
   computed: {
     treeProps() {
@@ -74,52 +76,71 @@
   methods: {
     load() {
       const lockedXNo = String(this.xNo || '').trim()
-      api.get('/system/tree', { params: { type: 'product' } }).then(res => {
-        const source = res.data || []
-        this.tree = lockedXNo ? this.filterTreeByXNo(source, lockedXNo) : source
+      this.loading = true
+      const ids = (this.selectedIds || []).map(String).filter(Boolean)
+      const treeRequest = lockedXNo
+        ? api.get('/system/tree/product/side-roots', { params: { xNo: lockedXNo } })
+        : api.get('/system/tree/product/roots')
+      const selectedRequest = ids.length
+        ? api.get('/system/tree/product/selected', { params: { ids: ids.join(',') } })
+        : Promise.resolve({ data: [] })
+      Promise.all([treeRequest, selectedRequest]).then(([treeRes, selectedRes]) => {
+        this.tree = treeRes.data || []
+        this.selectedMap = (selectedRes.data || []).reduce((map, node) => {
+          map[String(node.id)] = node
+          return map
+        }, {})
         this.keyword = ''
-        this.$nextTick(this.restoreChecked)
-      })
+        this.$nextTick(() => { this.restoreChecked(); this.syncSelected() })
+      }).finally(() => { this.loading = false })
     },
-    filterTreeByXNo(nodes, xNo) {
-      const result = []
-      ;(nodes || []).forEach(node => {
-        if (Number(node.nodeLevel) === 2) {
-          if (String(node.name || '').trim() === xNo) result.push(node)
-          return
-        }
-        const children = this.filterTreeByXNo(node.children || [], xNo)
-        if (children.length || node.nodeType === 'DEVICE') {
-          result.push(children.length ? { ...node, children } : node)
-        }
-      })
-      return result
+    loadNode(node, resolve) {
+      if (!node || !node.data || !node.data.id) {
+        resolve([])
+        return
+      }
+      api.get('/system/tree/product/children', { params: { parentId: node.data.id } })
+        .then(res => {
+          resolve(res.data || [])
+          this.$nextTick(this.restoreChecked)
+        })
+        .catch(() => resolve([]))
     },
     restoreChecked() {
       if (!this.$refs.tree) return
-      const ids = (this.selectedIds || []).map(String).filter(Boolean)
+      const ids = Object.keys(this.selectedMap).filter(id => Boolean(this.$refs.tree.getNode(id)))
       this.$refs.tree.setCheckedKeys(ids)
       this.syncSelected()
     },
     handleCheck(node) {
       const tree = this.$refs.tree
       const treeNode = tree && tree.getNode(node.id)
+      this.syncSelected()
       if (treeNode && treeNode.checked) {
-        const selected = this.deviceNodes().map(item => this.decorateNode(item))
-        const xNos = Array.from(new Set(selected.map(item => item.xNo).filter(Boolean)))
+        const xNos = Array.from(new Set(this.selectedDevices.map(item => item.xNo).filter(Boolean)))
         if (xNos.length > 1) {
           tree.setChecked(node.id, false, true)
-          this.$message.warning('鍚屼竴鏁呴殰鍙兘閫夋嫨鍚屼竴涓猉鍙蜂笅鐨勮澶�')
+          this.syncSelected()
+          this.$message.warning('鍚屼竴鏁呴殰鍙兘閫夋嫨鍚屼竴涓猺k_X鍙蜂笅鐨勮澶�')
         }
       }
-      this.syncSelected()
     },
     syncSelected() {
-      const selected = this.deviceNodes().map(node => this.decorateNode(node))
-      this.selectedDevices = selected
-      this.checkedCount = selected.length
+      const tree = this.$refs.tree
+      if (!tree) return
+      const selectedMap = { ...this.selectedMap }
+      const checked = tree.getCheckedNodes().filter(node => node.nodeType === 'DEVICE')
+      const checkedIds = new Set(checked.map(node => String(node.id)))
+      Object.keys(selectedMap).forEach(id => {
+        if (tree.getNode(id) && !checkedIds.has(id)) delete selectedMap[id]
+      })
+      checked.forEach(node => { selectedMap[String(node.id)] = this.decorateNode(node) })
+      this.selectedMap = selectedMap
+      this.selectedDevices = Object.values(selectedMap)
+      this.checkedCount = this.selectedDevices.length
     },
     decorateNode(node) {
+      if (!this.$refs.tree) return { ...node }
       const pathNodes = []
       let current = this.$refs.tree.getNode(node.id)
       while (current && current.data) {
@@ -134,8 +155,10 @@
       return { ...node, pathNames: pathNodes.map(item => item.name), parentPathNames, xNo: xNode ? xNode.name : '' }
     },
     removeSelected(device) {
-      if (!this.$refs.tree) return
-      this.$refs.tree.setChecked(device.id, false, true)
+      const selectedMap = { ...this.selectedMap }
+      delete selectedMap[String(device.id)]
+      this.selectedMap = selectedMap
+      if (this.$refs.tree && this.$refs.tree.getNode(device.id)) this.$refs.tree.setChecked(device.id, false, true)
       this.syncSelected()
     },
     deviceNodes() {
@@ -147,12 +170,12 @@
       return (data.name || '').includes(value) || (data.code || '').includes(value)
     },
     confirm() {
-      const nodes = this.deviceNodes()
+      const nodes = this.selectedDevices
       if (!nodes.length) return this.$message.warning('璇烽�夋嫨璁惧')
-      const selected = nodes.map(node => this.decorateNode(node))
+      const selected = nodes
       const xNos = Array.from(new Set(selected.map(item => item.xNo).filter(Boolean)))
       if (xNos.length > 1) {
-        return this.$message.warning('鍚屼竴鏁呴殰鍙兘閫夋嫨鍚屼竴涓猉鍙蜂笅鐨勮澶�')
+        return this.$message.warning('鍚屼竴鏁呴殰鍙兘閫夋嫨鍚屼竴涓猺k_X鍙蜂笅鐨勮澶�')
       }
       this.$emit('select', selected)
       this.localVisible = false

--
Gitblit v1.9.1