综述:纯前端树形表格增删改查 样式:
话不多说,直接上完整代码(直接在删除和表单确认方法内部执行打印localFunnelLevelList数组即可):
<template>
<div>
<div style="display: flex;justify-content: space-between;margin-top: 3px;">
<label class="required-label">层级数据(不小于2层)</label>
<div>
<el-button type="primary" style="height: 25px;" size="mini" @click="openModuleFilterModal(null, 'add')">
+ 添加
</el-button>
<el-button type="primary" style="height: 25px;" size="mini" @click="handleDeleteAll">
清空
</el-button>
</div>
</div>
<el-table :data="localFunnelLevelList" style="width: 100%;margin-top: 10px;border-radius: 10px;" row-key="id" border
ref="treeTable" default-expand-all :tree-props="{ children: 'Details', }" class="compact-tree-table">
<el-table-column prop="LevelSort" label="排序" width="80"></el-table-column>
<el-table-column prop="LevelName" label="层级名称" ></el-table-column>
<el-table-column prop="Unit" label="单位" width="80"></el-table-column>
<el-table-column prop="IsSql" label="数据来源"
:formatter="(row) => row.IsSql === 1 ? 'SQL语句' : '模板数据'"></el-table-column>
<el-table-column fixed="right" label="操作" width="130">
<template slot-scope="scope">
<el-button @click="handleDelete(scope.row)" type="text" size="small">删除</el-button>
<el-button type="text" size="small" @click="openModuleFilterModal(scope.row, 'edit')">编辑</el-button>
<el-button type="text" size="small" @click="openModuleFilterModal(scope.row, 'add')"
v-if="!scope.row.parentId || isTopLevel(scope.row)">
添加
</el-button>
</template>
</el-table-column>
</el-table>
<!--表单弹窗-->
<!-- <funnel-plot width="600px" @FunnelConfirm="handleFunnelConfirm" @cancel="handleDialogCancel"
@close="handleDialogCancel" :filterList="filterList" :data="dataDetail" :currentDataSourceType="FormNo"
:visible.sync="screeningVisible" /> -->
</div>
</template>
<script>
import FunnelPlot from "./FunnelPlot.vue";
export default {
components: { FunnelPlot },
props: {
FunnelLevelList: {
type: Array,
default: () => []
},
FormNo: {
type: String,
default: ''
}
},
data() {
return {
screeningVisible: false,
filterList: [],
dataDetail: {},
localFunnelLevelList: [],
operateType: '',
currentRow: null,
}
},
watch: {
FunnelLevelList: {
handler(newVal) {
const copiedVal = JSON.parse(JSON.stringify(newVal || []));
// 为每个元素添加唯一ID和parentId
this.addIdsToTreeData(copiedVal);
this.localFunnelLevelList = copiedVal;
},
immediate: true,
deep: true
}
},
methods: {
// 生成唯一ID
generateId() {
return 'id_' + Date.now() + '_' + Math.floor(Math.random() * 100000);
},
isTopLevel(row) {
// 判断是否为顶层节点(根节点,parentId为null)
return !row.parentId;
},
isNodeInRootLevel(id) {
// 兼容原有逻辑,判断是否是根节点
return this.localFunnelLevelList.some(item => item.id === id);
},
// 递归为树形数据添加唯一ID和parentId
addIdsToTreeData(treeData) {
if (!treeData || !Array.isArray(treeData) || treeData.length === 0) {
return treeData;
}
// 重构addId方法,添加parentId参数
const addId = (node, parentId = null) => {
if (!node || typeof node !== 'object') {
return;
}
if (!node.id) {
node.id = this.generateId();
}
if (parentId !== null) {
node.parentId = parentId;
} else if (!node.hasOwnProperty('parentId')) {
node.parentId = null; // 根节点显式设置parentId为null
}
if (!node.hasOwnProperty('Details') || !Array.isArray(node.Details)) {
node.Details = [];
}
if (node.Details && Array.isArray(node.Details) && node.Details.length > 0) {
node.Details.forEach(child => {
addId(child, node.id);
});
}
};
treeData.forEach(item => {
addId(item);
});
return treeData;
},
handleDialogCancel() {
this.screeningVisible = false;
},
// 获取搜索字段
async getSearchField() {
if (!this.FormNo) return [];
try {
const res = await this.$h.api.System_Report.querySearchFields({ FormNo: this.FormNo });
this.filterList = res?.Data || [];
return this.filterList;
} catch (e) {
this.filterList = [];
return [];
}
},
// 打开弹窗
async openModuleFilterModal(row, type) {
this.operateType = type;
this.currentRow = row;
this.dataDetail = {};
await this.getSearchField();
if (type === 'edit') {
this.dataDetail = JSON.parse(JSON.stringify(row));
} else if (type === 'add') {
this.dataDetail = {
id: this.generateId(),
LevelName: '',
LevelSort: 1,
Unit: '',
ValueField: '',
IsSql: 0,
Aggregate: '',
LevelRemark: '',
Details: [],
parentId: row?.id || null //设置父节点ID
};
}
this.screeningVisible = true;
},
// 处理新增/编辑确认
handleFunnelConfirm(formData) {
try {
const newList = JSON.parse(JSON.stringify(this.localFunnelLevelList));
const submitData = {
...formData,
Details: formData.Details || [],
// 确保parentId存在
parentId: formData.parentId || null
};
// 确保ID存在
if (!submitData.id) {
submitData.id = this.generateId();
}
let success = false;
if (this.operateType === 'edit') {
success = this.updateTreeNode(newList, submitData);
} else if (this.operateType === 'add') {
if (this.currentRow?.id) {
success = this.addChildNode(newList, this.currentRow.id, submitData);
} else {
newList.push(submitData);
success = true;
}
}
if (success) {
this.localFunnelLevelList = JSON.parse(JSON.stringify(newList));
this.$nextTick(() => {
if (this.$refs.treeTable) {
this.$refs.treeTable.doLayout();
}
});
this.$emit("FunnelConfirmEnd", newList);
this.handleDialogCancel();
}
} catch (error) {
console.error('处理数据失败:', error);
}
},
// 新增子节点
addChildNode(tree, parentId, childData) {
for (let i = 0; i < tree.length; i++) {
if (tree[i].id === parentId) {
tree[i].Details = tree[i].Details || [];
tree[i].Details.push(childData);
return true;
}
if (tree[i].Details && tree[i].Details.length) {
const added = this.addChildNode(tree[i].Details, parentId, childData);
if (added) return true;
}
}
return false;
},
// 递归更新树形节点
updateTreeNode(tree, data) {
for (let i = 0; i < tree.length; i++) {
if (tree[i].id === data.id) {
// 保留子节点数据,只更新当前节点属性(包含parentId)
tree[i] = {
...tree[i],
...data,
Details: tree[i].Details || []
};
return true;
}
if (tree[i].Details && tree[i].Details.length) {
const updated = this.updateTreeNode(tree[i].Details, data);
if (updated) return true;
}
}
return false;
},
// 删除全部
handleDeleteAll() {
this.localFunnelLevelList = [];
this.$emit("FunnelConfirmEnd", this.localFunnelLevelList);
},
// 删除节点
handleDelete(row) {
this.$confirm('确定删除该层级(含子层级)?', '提示', { type: 'warning' })
.then(() => {
const newList = JSON.parse(JSON.stringify(this.localFunnelLevelList));
const isDeleted = this.deleteTreeNode(newList, row.id);
if (isDeleted) {
this.localFunnelLevelList = [...newList];
this.$message.success('删除成功');
this.$emit("FunnelConfirmEnd", newList);
} else {
this.$message.warning('未找到该层级');
}
})
.catch(() => {
// 取消删除
});
},
// 递归删除节点
deleteTreeNode(tree, id) {
for (let i = 0; i < tree.length; i++) {
if (tree[i].id === id) {
tree.splice(i, 1);
return true;
}
if (tree[i].Details && tree[i].Details.length) {
const deleted = this.deleteTreeNode(tree[i].Details, id);
if (deleted) {
return true;
}
}
}
return false;
}
}
}
</script>