手把手教你實(shí)現(xiàn)一個Vue無限級聯(lián)樹形表格(增刪改)
作者: maomin9761
實(shí)現(xiàn)一個無限級聯(lián)樹形表格,什么叫做無限級聯(lián)樹形表格呢?就是下圖所展示的內(nèi)容,有一個祖元素,然后下面可能有很多子孫元素,你可以實(shí)現(xiàn)添加、編輯、刪除這樣幾個功能。
本文轉(zhuǎn)載自微信公眾號「 前端歷劫之路」,作者maomin9761。轉(zhuǎn)載本文請聯(lián)系 前端歷劫之路公眾號。
前言
平時我們可能在做項(xiàng)目時,會遇到一個業(yè)務(wù)邏輯。實(shí)現(xiàn)一個無限級聯(lián)樹形表格,什么叫做無限級聯(lián)樹形表格呢?就是下圖所展示的內(nèi)容,有一個祖元素,然后下面可能有很多子孫元素,你可以實(shí)現(xiàn)添加、編輯、刪除這樣幾個功能。
資源
- JavaScript框架:vue.js
- UI框架:Element UI
源碼
這里需要重點(diǎn)說明的是,主要使用了遞歸的算法以及給數(shù)據(jù)標(biāo)識的重要性。詳細(xì)說明可以在源碼中查看注釋,也可以通過刪改代碼融會貫通。
- <template>
- <div class="container">
- <div class="btn-r">
- <el-button
- type="primary"
- size="small"
- @click="addView = true"
- icon="el-icon-circle-plus-outline"
- class="add"
- >添加</el-button
- >
- </div>
- <el-table
- :data="tableData"
- style="width: 100%; margin-bottom: 20px"
- row-key="value"
- border
- default-expand-all
- size="medium"
- :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
- >
- <el-table-column prop="label" label="名稱" sortable>
- </el-table-column>
- <el-table-column label="操作" align="center" width="180">
- <template slot-scope="scope">
- <el-button
- type="text"
- size="small"
- @click="handleClick(scope.row, scope.$index)"
- >編輯</el-button
- >
- <el-button
- type="text"
- size="small"
- @click="deleteClick(scope.row, scope.$index)"
- >刪除</el-button
- >
- </template>
- </el-table-column>
- </el-table>
- <!-- 添加窗口 -->
- <el-dialog
- title="添加"
- :visible.sync="addView"
- :close-on-click-modal="false"
- width="30%"
- @close="closeView"
- >
- <el-form :model="form" ref="form" :rules="rules">
- <el-form-item
- label="位置"
- :label-width="formLabelWidth"
- prop="location"
- >
- <el-select
- v-model="form.location"
- placeholder="請選擇位置"
- @change="locationChange"
- size="small"
- >
- <el-option
- v-for="item in locationData"
- :key="item.id"
- :label="item.name"
- :value="item.id"
- />
- </el-select>
- </el-form-item>
- <el-form-item
- v-if="sonStatus"
- label="子位置"
- :label-width="formLabelWidth"
- prop="childArr"
- >
- <el-cascader
- size="small"
- :key="isResouceShow"
- v-model="form.childArr"
- placeholder="請選擇子位置"
- :label="'name'"
- :value="'id'"
- :options="tableData"
- :props="{ checkStrictly: true }"
- clearable
- @change="getCasVal"
- ></el-cascader>
- </el-form-item>
- <el-form-item
- label="名稱"
- :label-width="formLabelWidth"
- prop="label"
- >
- <el-input
- v-model="form.label"
- size="small"
- autocomplete="off"
- placeholder="請輸入名稱"
- ></el-input>
- </el-form-item>
- </el-form>
- <span slot="footer" class="dialog-footer">
- <el-button @click="addView = false" size="small"
- >取 消</el-button
- >
- <el-button type="primary" @click="okAdd('form')" size="small"
- >確 定</el-button
- >
- </span>
- </el-dialog>
- <!-- 編輯窗口 -->
- <el-dialog
- title="編輯"
- :visible.sync="editView"
- :close-on-click-modal="false"
- width="30%"
- >
- <el-form :model="data" ref="data" :rules="rules">
- <el-form-item
- label="位置"
- :label-width="formLabelWidth"
- prop="location"
- >
- <el-select
- v-model="data.location"
- placeholder="請選擇位置"
- size="small"
- @change="locationChange"
- >
- <el-option
- v-for="item in locationData"
- :key="item.id"
- :label="item.name"
- :value="item.id"
- />
- </el-select>
- </el-form-item>
- <el-form-item
- v-if="sonStatus"
- label="子位置"
- :label-width="formLabelWidth"
- prop="childArr"
- >
- <el-cascader
- :key="isResouceShow"
- v-model="data.childArr"
- placeholder="請選擇子位置"
- size="small"
- :label="'name'"
- :value="'id'"
- :options="tableData"
- :props="{ checkStrictly: true }"
- clearable
- @change="getCasVal"
- ></el-cascader>
- </el-form-item>
- <el-form-item
- label="名稱"
- :label-width="formLabelWidth"
- prop="label"
- >
- <el-input
- v-model="data.label"
- autocomplete="off"
- placeholder="請輸入名稱"
- size="small"
- ></el-input>
- </el-form-item>
- </el-form>
- <span slot="footer" class="dialog-footer">
- <el-button @click="editView = false" size="small"
- >取 消</el-button
- >
- <el-button type="primary" @click="okEdit('data')" size="small"
- >確 定</el-button
- >
- </span>
- </el-dialog>
- </div>
- </template>
- <script>
- export default {
- name: 'Tag',
- data() {
- return {
- location: '',
- isResouceShow: 1,
- addView: false,
- sonStatus: false,
- editView: false,
- casArr: [],
- childArr: [],
- form: {},
- data: {},
- idx: '',
- childkey: [],
- formLabelWidth: '80px',
- rules: {
- label: [
- { required: true, message: '請輸入名稱', trigger: 'blur' }
- ]
- },
- locationData: [
- {
- id: 1,
- name: '頂'
- },
- {
- id: 2,
- name: '子'
- }
- ],
- tableData: []
- };
- },
- methods: {
- // 監(jiān)聽關(guān)閉窗口
- closeView() {
- this.$refs['form'].resetFields(); // 關(guān)閉窗口,清空填寫的內(nèi)容
- },
- // 打開編輯
- handleClick(item, index) {
- item.value.length != 1
- ? (this.sonStatus = true)
- : (this.sonStatus = false);
- this.editView = true;
- const obj = Object.assign({}, item);
- this.childkey = item.childkey;
- this.casArr = item.childArr;
- this.idx = index;
- this.data = obj;
- },
- // 遞歸表格數(shù)據(jù)(編輯)
- findSd(arr, i, casArr) {
- if (i == casArr.length - 1) {
- let index = casArr[i].substr(casArr[i].length - 1, 1);
- return arr.splice(index, 1, this.data);
- } else {
- return this.findSd(
- arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
- (i += 1),
- casArr
- );
- }
- },
- // 確定編輯
- okEdit(data) {
- this.$refs[data].validate(valid => {
- if (valid) {
- if (this.data.value.length == 1) {
- this.tableData.splice(this.idx, 1, this.data);
- this.$message({
- type: 'success',
- message: '編輯成功'
- });
- this.editView = false;
- } else {
- this.findSd(this.tableData, 0, this.childkey);
- this.$message({
- type: 'success',
- message: '編輯成功'
- });
- this.editView = false;
- }
- } else {
- return false;
- }
- });
- },
- // 遞歸表格數(shù)據(jù)(刪除)
- findDel(arr, i, item) {
- let casArr = item.childkey;
- if (i == casArr.length - 1) {
- let index = casArr[i].substr(casArr[i].length - 1, 1);
- return arr.splice(index, 1);
- } else {
- return this.findDel(
- arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
- (i += 1),
- item
- );
- }
- },
- // 刪除
- deleteClick(item) {
- this.$confirm(`此操作將刪除該項(xiàng), 是否繼續(xù)?`, '提示', {
- confirmButtonText: '確定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- .then(() => {
- if (item.children.length != 0) {
- this.$message.warning({
- message: '請刪除子節(jié)點(diǎn)',
- duration: 1000
- });
- } else {
- this.casArr = item.childArr;
- ++this.isResouceShow; // 給級聯(lián)控件綁定一個key,防止報(bào)錯。
- if (item.value.length == 1) { // 刪除的是頂節(jié)點(diǎn)
- console.log(1);
- this.tableData.splice(item.value, 1);
- this.$message({
- type: 'success',
- message: '刪除成功'
- });
- } else { // 刪除的是子節(jié)點(diǎn)
- console.log(2);
- this.findDel(this.tableData, 0, item);
- this.$message({
- type: 'success',
- message: '刪除成功'
- });
- }
- }
- })
- .catch(err => {
- console.log(err);
- this.$message({
- type: 'info',
- message: '已取消刪除'
- });
- });
- },
- // 是否顯示次位置
- locationChange(v) {
- if (v == 2) {
- this.sonStatus = true;
- } else {
- this.sonStatus = false;
- }
- },
- // 獲取次位置
- getCasVal(v) {
- this.casArr = v;
- this.form.childArr = v;
- },
- // 遞歸表格數(shù)據(jù)(添加)
- find(arr, i) {
- if (i == this.casArr.length - 1) {
- return arr[this.casArr[i].substr(this.casArr[i].length - 1, 1)]
- .children;
- } else {
- return this.find(
- arr[this.casArr[i].substr(this.casArr[i].length - 1, 1)]
- .children,
- (i += 1)
- );
- }
- },
- // 確定添加
- okAdd(form) {
- this.$refs[form].validate(valid => {
- if (valid) {
- if (this.sonStatus == false) {
- this.form.value = String(this.tableData.length);
- const obj = Object.assign({}, this.form);
- obj.children = [];
- obj.childArr = [];
- this.tableData.push(obj);
- this.$message({
- type: 'success',
- message: '添加成功'
- });
- this.addView = false;
- } else {
- let arr = this.find(this.tableData, 0);
- this.childArr = [...this.casArr, String(arr.length)];
- this.form.value =
- String(this.casArr[this.casArr.length - 1]) +
- String(arr.length);
- delete this.form.children;
- const obj = Object.assign({}, this.form);
- obj.children = [];
- obj.childkey = [...this.casArr, String(arr.length)];
- arr.push(obj);
- this.$message({
- type: 'success',
- message: '添加成功'
- });
- this.addView = false;
- }
- } else {
- return false;
- }
- });
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- ::v-deep .el-form-item__content {
- width: 203px;
- }
- </style>
責(zé)任編輯:武曉燕
來源:
前端歷劫之路