<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>
|