xyc
3 天以前 53052625c7c520744a4ca061e204ec9594ca67f8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<template>
  <div class="page">
    <div class="page-title">
      <div>
        <h2>{{ isProduct ? '产品结构树管理' : '设备基础库管理' }}</h2>
        <p>{{ isProduct ? '型号 → X号 → 一级系统 → 二级系统 → 设备' : '一级系统 → 二级系统 → 设备' }}</p>
      </div>
      <div>
        <el-button icon="el-icon-plus" @click="addRoot">新增一级节点</el-button>
        <el-button icon="el-icon-refresh" @click="load">刷新</el-button>
      </div>
    </div>
 
    <div class="tree-layout">
      <section class="tree-panel">
        <el-input v-model="keyword" placeholder="筛选节点" clearable prefix-icon="el-icon-search" style="margin-bottom:10px"></el-input>
        <el-tree
          ref="tree"
          :data="tree"
          node-key="id"
          default-expand-all
          highlight-current
          :filter-node-method="filterNode"
          :props="{ label: 'name', children: 'children' }"
          @current-change="selectNode"
        >
          <span slot-scope="{ data }" class="tree-node">
            <span class="level-badge">{{ levelText(data.nodeLevel) }}</span>
            <span>{{ data.name }}</span>
            <small>{{ data.code }}</small>
          </span>
        </el-tree>
      </section>
 
      <section class="detail-panel">
        <template v-if="form.nodeLevel">
          <div class="detail-head">
            <h3>{{ form.id ? '节点信息' : '新增节点' }}</h3>
            <div>
              <el-button v-if="form.id && form.nodeLevel < maxLevel" size="mini" @click="addChild">新增下级</el-button>
              <el-button v-if="form.id" size="mini" type="danger" plain @click="remove">删除</el-button>
            </div>
          </div>
          <el-form :model="form" label-width="100px">
            <el-form-item label="节点层级"><el-tag type="info">{{ levelText(form.nodeLevel) }}</el-tag></el-form-item>
            <el-form-item label="名称"><el-input v-model="form.name"></el-input></el-form-item>
            <el-form-item label="编码"><el-input v-model="form.code"></el-input></el-form-item>
            <el-form-item v-if="isProduct" label="节点类型">
              <el-select v-model="form.nodeType" :disabled="form.nodeLevel < 5" style="width:100%">
                <el-option label="型号" value="MODEL"></el-option>
                <el-option label="X号" value="X_NO"></el-option>
                <el-option label="一级系统" value="SYSTEM1"></el-option>
                <el-option label="二级系统" value="SYSTEM2"></el-option>
                <el-option label="设备" value="DEVICE"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item v-if="isProduct && form.nodeLevel === 5" label="基础设备">
              <el-select v-model="form.baseEquipmentId" filterable placeholder="关联设备基础库设备" style="width:100%">
                <el-option v-for="item in baseDevices" :key="item.id" :label="item.path + ' / ' + item.name" :value="item.id"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="排序"><el-input-number v-model="form.sortNo" :min="1" style="width:180px"></el-input-number></el-form-item>
            <el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="4"></el-input></el-form-item>
            <el-form-item><el-button type="primary" :loading="saving" @click="save">保存节点</el-button></el-form-item>
          </el-form>
        </template>
        <el-empty v-else description="从左侧选择节点,或新增一级节点"></el-empty>
      </section>
    </div>
  </div>
</template>
<script>
import api from '@/api'
export default {
  name: 'TreeManage',
  data() {
    return { tree: [], keyword: '', form: {}, saving: false, baseDevices: [] }
  },
  computed: {
    treeType() { return this.$route.meta.treeType },
    isProduct() { return this.treeType === 'product' },
    maxLevel() { return this.isProduct ? 5 : 3 },
    levelNames() { return this.isProduct ? ['型号', 'X号', '一级系统', '二级系统', '设备'] : ['一级系统', '二级系统', '设备'] }
  },
  watch: {
    keyword(value) { this.$refs.tree && this.$refs.tree.filter(value) },
    '$route.meta.treeType'() { this.form = {}; this.load() }
  },
  created() { this.load() },
  methods: {
    load() {
      api.get('/system/tree', { params: { type: this.treeType } }).then(res => { this.tree = res.data || [] })
    },
    levelText(level) { return this.levelNames[(level || 1) - 1] || ('第' + level + '级') },
    filterNode(value, data) {
      if (!value) return true
      return (data.name || '').includes(value) || (data.code || '').includes(value)
    },
    selectNode(node) {
      this.form = { ...node }
      if (this.isProduct && node.nodeLevel === 5) this.loadBaseDevices()
    },
    nodeTypeForLevel(level) {
      return this.isProduct ? ['MODEL', 'X_NO', 'SYSTEM1', 'SYSTEM2', 'DEVICE'][level - 1] : 'BASE_' + level
    },
    addRoot() {
      this.form = { parentId: 0, nodeLevel: 1, nodeType: this.nodeTypeForLevel(1), name: '', code: '', sortNo: 1, remark: '' }
      if (this.isProduct) this.form.baseEquipmentId = null
    },
    addChild() {
      const level = (this.form.nodeLevel || 1) + 1
      this.form = { parentId: this.form.id, nodeLevel: level, nodeType: this.nodeTypeForLevel(level), name: '', code: '', sortNo: 1, remark: '', baseEquipmentId: null }
      if (this.isProduct && level === 5) this.loadBaseDevices()
    },
    loadBaseDevices() {
      api.get('/system/tree', { params: { type: 'base' } }).then(res => { this.baseDevices = this.flattenBase(res.data || [], []) })
    },
    flattenBase(nodes, parents) {
      const result = []
      nodes.forEach(node => {
        const path = parents.concat(node.name)
        if (node.nodeLevel === 3) result.push({ ...node, path: path.slice(0, -1).join(' / ') })
        if (node.children) result.push(...this.flattenBase(node.children, path))
      })
      return result
    },
    save() {
      if (!this.form.name) return this.$message.warning('请输入节点名称')
      this.saving = true
      const base = this.isProduct ? '/system/tree/product' : '/system/tree/base'
      const request = this.form.id ? api.put(base + '/' + this.form.id, this.form) : api.post(base, this.form)
      request.then(() => {
        this.$message.success('节点已保存')
        const currentId = this.form.id
        this.load()
        if (!currentId) this.form = {}
      }).finally(() => { this.saving = false })
    },
    remove() {
      this.$confirm(`确定删除节点“${this.form.name}”吗?`, '删除确认', { type: 'warning' }).then(() => {
        const base = this.isProduct ? '/system/tree/product/' : '/system/tree/base/'
        return api.delete(base + this.form.id)
      }).then(() => { this.$message.success('删除成功'); this.form = {}; this.load() }).catch(() => {})
    }
  }
}
</script>
<style scoped>
.tree-layout { display: grid; grid-template-columns: 420px minmax(0, 1fr); gap: 12px; }
.tree-panel, .detail-panel { min-height: calc(100vh - 140px); padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; }
.tree-node { display: flex; align-items: center; gap: 7px; }
.tree-node small { color: #95a2b0; }
.level-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 50px; height: 20px; padding: 0 5px; border-radius: 3px; color: #2f658f; background: #e9f3fb; font-size: 11px; }
.detail-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.detail-head h3 { margin: 0; color: #31465d; font-size: 15px; }
@media (max-width: 900px) { .tree-layout { grid-template-columns: 1fr; } .tree-panel, .detail-panel { min-height: auto; } }
</style>