前言
vue-element-admin-plus对于复杂业务的支持程度确实不怎么样,我这里就遇到了编辑页面中还要嵌套列表的真实案例,比如字典,主字典嵌套子信息,类似于一个树状结构。目前vue-element-admin-plus给出的例子是无法满足这个需求的。

修改Write
修改Edit和修改Write都是可以实现的,毕竟程序员只有不想做,哪有做不出来的道理。但是按照各司其职来说,修改components中的Write更好,毕竟同为内部编辑。
引入表格
由于只有新增完了主字典,才能新增其子内容,因此这里表格显示条件是只有编辑的情况才显示,新增的情况不显示。另外要加上标题和按钮,用于新增和删除子内容。
html
<template>
<Form :rules="rules" @register="formRegister" :schema="schema" />
<div v-if="props.editType === 'edit'">
<ElText type="primary" size="large" style="width: 100%">字典所属数据</ElText>
<div style="display: block; margin-top: 20px">
<ElButton type="primary" @click="addChild">新增</ElButton>
<ElButton type="danger" @click="delChildList">删除</ElButton>
</div>
<ElTable :data="dictData" stripe style="width: 100%" @selection-change="selectionChange">
<ElTableColumn type="selection" />
<ElTableColumn label="字典值" prop="name" />
<ElTableColumn label="字典码" prop="code" />
<ElTableColumn label="字典说明" prop="remark" />
<ElTableColumn label="操作">
<template #default="slot">
<ElButton type="primary" size="small" @click="editChild(slot.row)">编辑</ElButton>
<ElButton type="danger" size="small" @click="delChildRow(slot.row)">删除</ElButton>
</template>
</ElTableColumn>
</ElTable>
</div>
</template>
注意了,在这里template标签中引入的ElTable必须是大驼峰格式的,不能是小写且用-号连接的
props.editType是从Edit页面传进来的。
TypeScript
const props = defineProps({
currentRow: {
type: Object as PropType<Nullable<DictDetailVo>>,
default: () => null
},
editType: {
type: String,
default: 'add'
},
editId: {
type: Number,
default: -1
}
})
dictData需要自行定义,是一个ref引用的数组。
TypeScript
const dictData = ref<Array<DictDetailVo>>([])
addChild和delChildList是点击按钮时调用,这个放后面再说
列表中的editChild和delChildRow也是点击按钮时调用,放后面再说
准备子数据接口
由于我们需要对子列表进行操作,因此还需要这个列表的增删改查接口。
TypeScript
export const dictAddApi = (data: DictEditBo): Promise<IResponse<number>> => {
return request.post({ url: '/dict/add.adw', data })
}
export const dictUpdateApi = (data: DictEditBo): Promise<IResponse<any>> => {
return request.post({ url: '/dict/update.adw', data })
}
export const dictDeleteApi = (data: BaseDeleteBo): Promise<IResponse<any>> => {
return request.post({ url: '/dict/delete.adw', data })
}
export const dictChildrenApi = (data: DictSearchBo): Promise<IResponse<number>> => {
return request.post({ url: '/dict/children.adw', data })
}
直接查询子数据接口:
TypeScript
const childrenSearch: DictSearchBo = {
parentId: props.editId
}
const refreshChildren = () => {
dictChildrenApi(childrenSearch).then((res) => {
dictData.value = res.d as unknown as Array<DictDetailVo>
})
}
refreshChildren()
进入页面时就需要调用refreshChildren接口了,入参需要将parentId传入,通过Edit页面传进来
新增、编辑对话框
在Write页面内部新增和编辑子列表就不要再跳转新的页面了,这里我使用ElDialog搞定,当然这里写的也必须是大驼峰形式的ElDialog,不能使用小写用-号拼接的,不然会识别不了。
html
<ElDialog v-model="showEditDictChildDialog" title="编辑字典项">
<div>
<ElInput v-model="editDictDetail.name" placeholder="字典值" style="margin-bottom: 20px" />
<ElInput v-model="editDictDetail.code" placeholder="字典码" style="margin-bottom: 20px" />
<ElInput v-model="editDictDetail.remark" placeholder="字典说明" />
</div>
<template #footer>
<ElButton @click="showEditDictChildDialog = false">取消</ElButton>
<ElButton type="primary" @click="handleSaveEdit">保存</ElButton>
</template>
</ElDialog>
其中showEditDictChildDialog是ref引用的布尔类型对象,用于显示和隐藏对话框。
TypeScript
const showEditDictChildDialog = ref(false)
editDictDetail就是新增和编辑用的对象,使用ref引用。
TypeScript
const editDictDetail = ref<DictEditBo>({
id: 0,
name: '',
parentId: '',
code: '',
remark: ''
})
handleSaveEdit由保存按钮调用
TypeScript
const handleSaveEdit = () => {
showEditDictChildDialog.value = false
if (!editDictDetail.value.id) {
dictAddApi(editDictDetail.value).then(() => {
refreshChildren()
})
} else {
dictUpdateApi(editDictDetail.value).then(() => {
refreshChildren()
})
}
}
关闭弹窗后,如果编辑的对象不存在id,则调用新增接口,否则调用编辑接口。
剩余方法
剩余的方法就是addChild、delChildList、editChild、delChildRow和selectionChange了。
addChild和editChild很像,其中add需要将弹窗内的数据重置,防止新增的数据还是之前的,其次就是弹出编辑弹窗了
TypeScript
const editChild = (row: DictDetailVo) => {
showEditDictChildDialog.value = true
editDictDetail.value = { ...row }
}
const addChild = () => {
// 重置新增对象
editDictDetail.value = {
id: undefined,
name: '',
parentId: props.editId,
code: '',
remark: ''
}
showEditDictChildDialog.value = true
}
两个删除方法也差不多,最多是弹出一个模态框用于提示删除警告,外加调用删除接口。
由于我写的后端删除接口只有传入id列表的,因此及时是删除单行,也被我作为列表传入了
TypeScript
const delChildRow = (row: DictDetailVo) => {
ElMessageBox.confirm('是否要删除这条数据?', '删除警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const nowDate = Date.now()
const deleteBo: BaseDeleteBo = {
ids: [row.id],
rt: nowDate
}
dictDeleteApi(deleteBo).then(() => {
refreshChildren()
})
})
}
const delChildList = () => {
ElMessageBox.confirm('是否要删除这些数据?', '删除警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const nowDate = Date.now()
const rowIds = selectList.value.map((row) => {
return row.id
})
const deleteBo: BaseDeleteBo = {
ids: rowIds,
rt: nowDate
}
dictDeleteApi(deleteBo).then(() => {
refreshChildren()
})
})
}
完成效果

修改Page
修改Table
给的默认例子的Page是无法做成树状图的,这里需要对Table标签进行修改,增加tree-props和row-key
html
<Table
v-model:pageSize="pageInfo.pageSize"
v-model:currentPage="pageInfo.currentPage"
:columns="allSchemas.tableColumns"
:data="dataList"
:loading="loading"
:stripe="true"
:pagination="{
total: pageInfo.totalCount
}"
@register="pageView.tableRegister"
:tree-props="treeProps"
row-key="id"
/>
tree-props用于指定树状图的子列表的字段名称
TypeScript
const treeProps = {
children: 'children'
}
指定子列表的字段名为children,也就是说如果这个字段中存在一个列表,那就可以展开了。
row-key用于指定列表唯一值,防止展开混乱。
后端需要保证其传来的子列表字段也必须为children,否则树状列表不生效。
完成效果

完成效果如下,可以做到字典列表无限嵌套

编辑页面也能够嵌套列表了
结语
本篇文章给大家展示了在vue-element-plus-admin中如何在编辑页面嵌套子列表,给大家提供一个编辑思路。当然这里只是一个介绍思路的demo,并不是完整代码,如果真的要完整实现这个功能,还需要后端的协助,当然这里的前后端都我一个人开发就对了,哈哈😎