<script setup lang="ts"> import { reactive, onMounted } from 'vue'; import { useQuasar } from 'quasar'; import { cloneDeep, isEmpty, omit } from 'src/common/utils'; import nestedDraggable from './element/nested.vue'; import { ComDialogTip } from 'src/components'; const $q = useQuasar(); const SpcGroupItem = { show: true, title: null, children: [], }; const data = [ { title: '1', children: [ { title: '1-1', children: [], }, { title: '1-2', children: [], }, { title: '1-3', children: [], }, ], }, { title: '2', children: [], }, ]; const state = reactive({ originalData: [] as any[], list: [] as any[], itemKey: 'key', childrenKey: 'children', labelKey: 'title', dataStacks: [] as any, }); onMounted(() => { initData(data); }); function initData(data: any) { state.originalData = data; const _data = cloneDeep(data); handleData(_data); state.list = _data; pushDataToStacks(); // console.log('state.list', state.list); } function pushDataToStacks() { state.dataStacks.push({ data: cloneDeep(state.list), }); } function handleData(data: any, key = state.childrenKey, before = 0) { let index = 0; data.map((item: any) => { let indexstr; if (before) { indexstr = String(before) + '-' + String(index); } else { indexstr = String(index); } if (isEmpty(item.show)) { item.show = true; } item['test_index'] = indexstr; item[state.itemKey] = indexstr; if (data.length - 1 === index) { item.lastChild = true; } else { item.lastChild = false; } index++; if (!isEmpty(item[key])) { handleData(item[key], key, item['test_index']); } else { item[key] = []; } }); } function handelDel(listdata: any[]) { const chiledKey = state.childrenKey; let newArr = [] as any[]; listdata.map((item) => { if (!isEmpty(item[chiledKey])) { item[chiledKey] = handelDel(item[chiledKey]); } if (!item['test_delete']) { newArr.push(item); } }); return newArr; } function getTarget(list: any, index: number) { const target = list[index]; const parent = list; return { target, parent }; } function delTestKey(listdata: any[]) { const chiledKey = state.childrenKey; let seq = 1; return listdata.map((item: any) => { if (!isEmpty(item[chiledKey])) { item[chiledKey] = delTestKey(item[chiledKey]); } const newObj = omit(item, ['key', 'test_index', 'show', 'lastChild']); newObj.seq = seq; seq++; return newObj; }); } // 查看 function viewData() { const data = cloneDeep(state.list); const resdata = delTestKey(data); console.log('最后处理格式', resdata); } // 重置 function onReset() { state.dataStacks = []; initData(cloneDeep(state.originalData)); } // 拖拽结束 function dragEnd() { handleData(state.list); pushDataToStacks(); } // 添加 function onAdd() { let obj = { ...SpcGroupItem } as any; obj[state.itemKey] = Date.now(); state.list.push(obj); handleData(state.list); pushDataToStacks(); } // 删除 function delItem(data: any) { let msg = ''; if (!isEmpty(data.children)) { msg = '确定删除 ' + <string>data.title + '及其所有子项?'; } else { msg = '确定删除 ' + <string>data.title + '?'; } $q.dialog({ component: ComDialogTip, componentProps: { persistent: true, text: msg, color: 'negative', }, }).onOk(() => { data['test_delete'] = true; let res = handelDel(state.list); handleData(res); state.list = res; pushDataToStacks(); }); } // 同级添加 function addItem(data: any) { const key = state.childrenKey; const indexList = data['test_index'].split('-'); let re: any; for (let i = 0; i < indexList.length; i++) { const item = indexList[i]; if (i === 0) { re = getTarget(state.list, Number(item)); } else { re = getTarget(re.target[key], Number(item)); } } let obj = { ...SpcGroupItem } as any; obj[state.itemKey] = Date.now(); re.parent.push(obj); handleData(state.list); pushDataToStacks(); } // 子级添加 function addNest(data: any) { const key = state.childrenKey; const indexList = data['test_index'].split('-'); let re: any; for (let i = 0; i < indexList.length; i++) { const item = indexList[i]; if (i === 0) { re = getTarget(state.list, Number(item)); } else { re = getTarget(re.target[key], Number(item)); } } let obj = { ...SpcGroupItem } as any; obj[state.itemKey] = Date.now(); re.target[key].push(obj); handleData(state.list); pushDataToStacks(); } // 撤销 function onUndo() { const lastindex = state.dataStacks.length - 2; const lastdata = state.dataStacks[lastindex]; state.list = cloneDeep(lastdata.data); state.dataStacks.splice(lastindex + 1, 1); } </script> <template> <div class="box"> <div class="actions"> <q-btn unelevated color="primary" label="查看" @click="viewData" /> <q-btn unelevated color="primary" label="重置" @click="onReset" /> <q-btn unelevated color="primary" label="添加" @click="onAdd" /> <q-btn unelevated color="primary" label="撤销" @click="onUndo" :disable="state.dataStacks.length < 2" /> </div> <div class="tree-box"> <nested-draggable :tasks="state.list" :item-key="state.itemKey" :children-key="state.childrenKey" :label-key="state.labelKey" @onEnd="dragEnd" @addItem="addItem" @addNest="addNest" @delItem="delItem" /> </div> </div> </template> <style lang="scss" scoped> .actions { padding: 20px 10px 10px 20px; display: flex; flex-direction: row; justify-content: flex-start; align-items: center; column-gap: 10px; } .tree-box { transform: translateX(-20px); } </style>