admin-vben/apps/web-ele/src/views/system/dept/components/dept-select-modal.vue

142 lines
3.6 KiB
Vue

// TODO @芋艿:是否有更好的组织形式?!
<script lang="ts" setup>
import type { SystemDeptApi } from '#/api/system/dept';
import { nextTick, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { handleTree } from '@vben/utils';
import { ElCard, ElCol, ElRow, ElTree } from 'element-plus';
import { getSimpleDeptList } from '#/api/system/dept';
defineOptions({ name: 'DeptSelectModal' });
const props = withDefaults(
defineProps<{
// 取消按钮文本
cancelText?: string;
// checkable 状态下节点选择完全受控
checkStrictly?: boolean;
// 确认按钮文本
confirmText?: string;
// 是否支持多选
multiple?: boolean;
// 标题
title?: string;
}>(),
{
cancelText: '取消',
checkStrictly: false,
confirmText: '确认',
multiple: true,
title: '部门选择',
},
);
const emit = defineEmits<{
confirm: [deptList: SystemDeptApi.Dept[]];
}>();
// 部门树形结构
const deptTree = ref<any[]>([]);
// 选中的部门 ID 列表
const selectedDeptIds = ref<number[]>([]);
// 部门数据
const deptData = ref<SystemDeptApi.Dept[]>([]);
// Tree 组件引用
const treeRef = ref();
// 对话框配置
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
// 获取选中的部门ID
const selectedIds: number[] = props.checkStrictly
? treeRef.value?.getCheckedKeys() || []
: selectedDeptIds.value;
const deptArray = deptData.value.filter((dept) =>
selectedIds.includes(dept.id!),
);
emit('confirm', deptArray);
// 关闭并提示
await modalApi.close();
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
deptTree.value = [];
selectedDeptIds.value = [];
return;
}
// 加载数据
const data = modalApi.getData();
if (!data) {
return;
}
modalApi.lock();
try {
deptData.value = await getSimpleDeptList();
deptTree.value = handleTree(deptData.value);
// 等待 DOM 更新后再设置选中的节点
await nextTick();
// 设置已选择的部门
if (data.selectedList?.length) {
const selectedIds = data.selectedList
.map((dept: SystemDeptApi.Dept) => dept.id)
.filter((id: number) => id !== undefined);
selectedDeptIds.value = selectedIds;
treeRef.value.setCheckedKeys(selectedIds);
}
} finally {
modalApi.unlock();
}
},
destroyOnClose: true,
});
/** 处理选中状态变化 */
function handleCheck(
_data: any,
{ checkedKeys }: { checkedKeys: (number | string)[] },
) {
// 确保 checkedKeys 都是 number 类型
const keys = checkedKeys.map((key) =>
typeof key === 'string' ? Number(key) : key,
);
if (props.multiple) {
selectedDeptIds.value = keys;
} else {
// 单选模式下,只保留最后选择的节点
const lastSelectedId = keys[keys.length - 1];
if (lastSelectedId) {
selectedDeptIds.value = [lastSelectedId];
treeRef.value?.setCheckedKeys([lastSelectedId]);
}
}
}
</script>
<template>
<Modal :title="title" key="dept-select-modal" class="w-3/5">
<ElRow class="h-full">
<ElCol :span="24">
<ElCard class="h-full">
<ElTree
v-if="deptTree.length > 0"
ref="treeRef"
:data="deptTree"
:props="{ label: 'name', children: 'children' }"
:check-strictly="checkStrictly"
:default-expand-all="true"
show-checkbox
check-on-click-node
node-key="id"
@check="handleCheck"
/>
</ElCard>
</ElCol>
</ElRow>
</Modal>
</template>