小白入门请多关照.如有问题,还望指点!
如果觉得不错请支持一下!
核心代码
更新树状节点
注意修改tableRefName
updateTableTree(pid, nodes) {
//更新节点数据
//pid为父级id,nodes为修改后的子集内容
//以下面数据结构举例
//pid=3 子集为id等于3的children数据
this.$set(this.$refs.tableRefName.store.states.lazyTreeNodeMap,pid, nodes );
},
refTable() {
/**
*作用:更新树状节点
* 因树状原因需更新外层
*所以递归更新所有节点
*/
let _this = this;
function dg(data) {
for (let i in data) {
if (data[i].children) {
_this.updateTableTree(data[i].id, data[i].children);
dg(data[i].children);
}
}
}
dg(this.tableData);
}
数据结构
sourceData: [{
id: 3,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄',
pid: null,
children: [{
id: 31,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1520 弄',
pid: 3
}, {
id: 32,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1521 弄',
pid: 3
}]
}, {
id: 4,
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}],
全部源码
增删改查的地方已修改为相应的注释
<template>
<div style="width:100%;height:100%">
<div class="cd-tool">
<el-input size="mini" style="width:220px" v-model="searchValue" placeholder="请输入地址关键字"></el-input>
<el-button type size="mini" @click="search">搜索</el-button>
<el-button type size="mini" @click="enableSel=!enableSel">{{enableSel?'关闭':'启用'}}选择</el-button>
<el-button type size="mini" @click="relDict">关联字典</el-button>
<el-button type size="mini" @click="addTool">新增</el-button>
<el-button type size="mini" @click="batchEdit">批量编辑</el-button>
<el-button type size="mini" @click="batchDel">批量删除</el-button>
<el-button type size="mini" @click="saveTbale">保存</el-button>
</div>
<el-table
:data="tableData"
style="width: 100%"
row-key="id"
border
stripe
size="mini"
class="data-table"
ref="cimsDictTable"
tooltip-effect="dark"
:height="tableMaxHeight"
@selection-change="handleSelectionChange"
@select="select"
@select-all="selectAll"
header-row-class-name="data-table-header"
lazy
:show-overflow-tooltip="true"
:load="load"
:tree-props="{children:'children'}"
>
<el-table-column v-if="enableSel" type="selection" width="55"></el-table-column>
<el-table-column label="日期">
<template slot-scope="scope">
<el-input v-model="scope.row.date" placeholder="请输入字典名称" v-if="scope.row.edit"></el-input>
<span v-else>{{scope.row.date}}</span>
</template>
</el-table-column>
<el-table-column label="姓名">
<template slot-scope="scope">
<el-input v-model="scope.row.name" placeholder="请输入字典名称" v-if="scope.row.edit"></el-input>
<span v-else>{{scope.row.name}}</span>
</template>
</el-table-column>
<el-table-column label="地址">
<template slot-scope="scope">
<el-input v-model="scope.row.address" placeholder="请填写备注" v-if="scope.row.edit"></el-input>
<span v-else>{{scope.row.address}}</span>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="editRow(scope.row,scope.$index)">编辑</el-button>
<el-button type="text" size="mini" @click="delRow(scope.row,scope.$index)">删除</el-button>
<el-button type="text" size="mini" @click="addRow(scope.row,scope.$index)">新增</el-button>
<!-- <el-button type="text" size="mini" @click="upRow(scope,scope.row,scope.$index)">上移</el-button>
<el-button type="text" size="mini" @click="downRow(scope.row,scope.$index)">下移</el-button>-->
</template>
</el-table-column>
</el-table>
<el-dialog title="菜单选择" :visible.sync="dialogVisibleMenu" width="30%" :before-close="Menuclose">
<el-tree
class="filter-tree"
:data="menuSource"
node-key="id"
:props="defaultProps"
:default-expand-all="false"
:expand-on-click-node="false"
ref="tree"
@node-click="onMenuSl"
>
<span class="custom-tree-node" slot-scope="{ node}">
<span>{{ node.label }}</span>
</span>
</el-tree>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleMenu = false">取 消</el-button>
<el-button type="primary" @click="cnfAsMenu">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
function filterNodes(nodes, query, filterName) {
// 条件就是节点的filterName过滤关键字
let predicate = function (node) {
if (node[filterName].indexOf(query) > -1) {
return true;
} else {
return false;
}
};
if (!(nodes && nodes.length)) {
return [];
}
let newChildren = [];
for (let node of nodes) {
let subs = filterNodes(node.children, query, filterName);
if (predicate(node)) {
newChildren.push(node);
} else if (subs && subs.length) {
node.children = subs;
newChildren.push(node);
}
}
return newChildren.length ? newChildren : [];
}
export default {
name: "tree-table",
data() {
return {
dialogVisibleMenu: false,
ref: "cimsDictTable",
tableMaxHeight: 400,
enableSel: false,
tableData: [],
cimsDictTable: [],
searchSource: [],
searchValue: "",
menuSource: [],
defaultProps: {
children: "children",
label: "name",
}, //菜单节点
sourceData: [
{
id: 1,
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
pid: null,
},
{
id: 2,
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄",
pid: null,
},
{
id: 3,
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
pid: null,
children: [
{
id: 31,
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1520 弄",
pid: 3,
},
{
id: 32,
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1521 弄",
pid: 3,
},
],
},
{
id: 4,
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄",
},
],
};
},
methods: {
addTool() {
let addrow = this.setRowData();
this.tableData.push(addrow);
},
onMenuSl(obj, node, data) {
this.id = obj.id;
},
cnfAsMenu() {
if (this.id == null) {
this.$message({
message: "请先选择关联菜单! ",
type: "warning",
});
} else {
let arr = this.getSelectedList();
let p = arr.map((m) => m.id);
//关联字典接口
this.$message({
message: "关联成功! ",
type: "success",
});
this.dialogVisibleMenu = false;
this.initData();
this.id = null;
}
},
Menuclose() {
this.dialogVisibleMenu = false;
},
search() {
this.tableData = filterNodes(
this.searchSource,
this.searchValue,
"address"
);
this.refTable();
},
setRowData(pid) {
return {
id: new Date().valueOf(),
name: "",
pid: pid ? pid : null,
address: "",
edit: true,
add: true,
};
},
relDict() {
//关联字典
if (!this.checkSelected(this.cimsDictTable, "至少选择一项")) {
return;
}
//模仿数据请求接口
let res = this.sourceData;
this.menuSource = JSON.parse(JSON.stringify(res));
this.dialogVisibleMenu = true;
},
tableRules(list) {
/**
* 验证规则
* name不为空
*/
for (let i in list) {
if (list[i].name) {
return false;
}
}
return true;
},
saveTbale() {
//保存
let saveList = [];
let saveTableData = JSON.parse(JSON.stringify(this.tableData));
function dg(saveTableData) {
for (let i in saveTableData) {
if (saveTableData[i].edit) {
//设置序号
saveTableData[i].cdOrderNum = parseInt(i) + 1;
//2.重置新增数据的id为null
if (saveTableData[i].add) {
saveTableData[i].id = null;
}
//3.保存编辑的数据
saveList.push(saveTableData[i]);
}
if (saveTableData[i].children) {
dg(saveTableData[i].children);
}
}
}
dg(saveTableData);
if (!this.checkSelected(saveList, "至少编辑一项")) {
return;
}
if (this.tableRules(saveList)) {
this.$message.error("编辑项中存在空数据,请填写或者删除");
return;
}
this.$confirm(`操作确认,是否继续?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
//批量保存与更新接口
this.$message({
message: "操作成功! ",
type: "success",
});
this.initData();
})
.catch(() => {});
},
//表格选择操作方法
// 手动勾选数据行
select(selection, row) {
// 判断当前行是否选中
// 不需要判断 id, 因为引用地址相同
const selected = selection.some((item) => item === row);
// 处理所有子级
this.selectChildren(row, selected);
},
selectAll(selection) {
/*
* 这里引用别人的代码:
* selectAll 只有两种状态: 全选和全不选
* 所以我们只需要判断一种状态即可
* 而且也不需要判断 id, 因为 selection 和 this.data 中对象引用地址是相同的
*/
// tableData 第一层只要有在 selection 里面就是全选
const isSelect = this.tableData.some((item) => selection.includes(item));
if (isSelect) {
selection.forEach((item) => {
this.selectChildren(item, isSelect);
});
} else {
this.tableData.forEach((item) => {
this.selectChildren(item, isSelect);
});
}
},
selectChildren(row, selected) {
if (row["children"] && Array.isArray(row["children"])) {
row["children"].forEach((item) => {
this.toggleSelection(item, selected);
this.selectChildren(item, selected);
});
}
},
selectionChange(selection) {
this.debounce(this.tableSelectChange, 100, selection);
},
toggleSelection(row, select) {
row &&
this.$nextTick(() => {
this.$refs[this.ref] &&
this.$refs[this.ref].toggleRowSelection(row, select);
});
},
cancelAll() {
this.$refs[this.ref] && this.$refs[this.ref].clearSelection();
},
// 防抖
debounce(fun, wait, params) {
clearTimeout(this.timeout);
this.timeout = setTimeout(fun, wait, params);
},
getSelectedList() {
//获取选中数据源
let list = JSON.parse(JSON.stringify(this.cimsDictTable));
list.forEach((e) => (e.children = null));
return list;
},
checkSelected(data, mange) {
//批量操作检查
let v = data.length > 0;
if (!v) {
this.$message({
type: "warning",
message: mange,
});
}
return v;
},
batchEdit() {
//批量编辑
if (!this.checkSelected(this.cimsDictTable, "至少选择一项")) {
return;
}
for (let i in this.cimsDictTable) {
this.$set(this.cimsDictTable[i], "edit", true);
}
},
batchDel() {
//批量删除
if (!this.checkSelected(this.cimsDictTable, "至少选择一项")) {
return;
}
let arr = this.getSelectedList();
this.$confirm(`删除选中的${arr.length}项目数据?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
let p = [];
arr.forEach((d) => {
if (!d.add) {
p.push(d.id);
}
});
//删除接口
//todo:需判断2种状态提示 前端删除 和 需要传回后端
this.initData();
})
.catch(() => {});
},
addRow(row, index) {
if (!row.add) {
//新增行数据
let addrow = this.setRowData(row.id);
//新增
if (row.children) {
row.children.push(addrow);
} else {
//添加数据
this.$set(row, "children", [addrow]);
}
//展开行
this.$nextTick(() => {
//更新后打开节点
this.$refs.cimsDictTable.toggleRowExpansion(row, true);
//刷新树
this.refTable();
});
} else {
this.$message({
message: "请保存后再继续添加子节点!",
type: "warning",
});
}
},
updateTableTree(pid, nodes) {
//更新需要先更新上级节点
this.$set(
this.$refs.cimsDictTable.store.states.lazyTreeNodeMap,
pid,
nodes
);
},
refTable() {
let _this = this;
function dg(data) {
for (let i in data) {
if (data[i].children) {
_this.updateTableTree(data[i].id, data[i].children);
dg(data[i].children);
}
}
}
dg(this.tableData);
},
editRow(row, index) {
//编辑
this.$set(row, "edit", true);
},
delRow(row, index) {
//删除行
let delArr = [];
function dg(data) {
for (let i in data) {
//过滤当前新增的数据
if (!data[i].add) {
delArr.push(data[i].id);
}
if (data[i].children) {
dg(data[i].children);
}
}
}
dg([row]);
//删除
this.$confirm(
`删除选中的 ${row.name} 数据项?如果有子项,也会一并删除!`,
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(() => {
if (p.length > 0) {
//删除接口
this.$message({
message: "删除成功! ",
type: "success",
});
this.initData();
} else {
this.$message({
message: "删除成功! ",
type: "success",
});
this.initData();
}
})
.catch(() => {});
},
upRow(scope, row, index) {
console.info(scope, row, index);
//上移
},
downRow(row, index) {
//下移
},
proTableData(data) {
let _this = this;
//处理数据
function dg(data) {
for (let i in data) {
_this.$set(data[i], "edit", false);
if (data[i].children) {
//重要:树状节点数据刷新
_this.updateTableTree(data[i].id, data[i].children);
dg(data[i].children);
}
}
}
dg(data);
},
initData() {
//数据加载 模仿数据请求
let res = JSON.parse(JSON.stringify(this.sourceData));
//数据处理 添加编辑标识
this.proTableData(res);
this.searchSource = JSON.parse(JSON.stringify(res));
this.tableData = res;
},
load(tree, treeNode, resolve) {
//节点加载
setTimeout(() => {
resolve(tree.children);
}, 1000);
},
handleSelectionChange(val) {
//全选
this.cimsDictTable = val;
},
},
mounted() {
this.initData();
},
beforeMount() {
this.tableMaxHeight = document.body.clientHeight;
},
};
</script>
<style scoped lang="scss">
.cd-tool {
display: flex;
flex-direction: row;
}
.data-table {
/deep/ .cell {
display: flex;
align-items: center;
}
}
</style>