<script lang="ts" setup> import type { DataNode } from 'ant-design-vue/es/tree'; import type { Recordable } from '@vben/types'; import type { RoleApi } from '#/api'; import { computed, ref } from 'vue'; import { useVbenDrawer, VbenTree } from '@vben/common-ui'; // import { IconifyIcon } from '@vben/icons'; import { Spin } from 'ant-design-vue'; import { useVbenForm } from '#/adapter/form'; import { createRole, queryMenuList, roleDetail, updateRole } from '#/api'; import { $t } from '#/locales'; import { useFormSchema } from '../data'; const emits = defineEmits(['success']); const formData = ref<RoleApi.Role>(); const [Form, formApi] = useVbenForm({ schema: useFormSchema(), showDefaultActions: false, }); const menuIds = ref<DataNode[]>([]); const loadingPermissions = ref(false); const id = ref(); const [Drawer, drawerApi] = useVbenDrawer({ async onConfirm() { const { valid } = await formApi.validate(); if (!valid) return; const values = await formApi.getValues(); drawerApi.lock(); (id.value ? updateRole(id.value, values) : createRole(values)) .then(() => { emits('success'); drawerApi.close(); }) .catch(() => { drawerApi.unlock(); }); }, async onOpenChange(isOpen) { if (isOpen) { const data = drawerApi.getData<RoleApi.Role>(); await formApi.resetForm(); if (data.id) { const roleDetailData = await roleDetail(data.id); formData.value = roleDetailData; // 提取 menus 中的 id 并赋值给 menuIds formData.value.menuIds = roleDetailData.menus.map((menu) => menu.id); id.value = data.id; formApi.setValues(roleDetailData); } else { id.value = undefined; } if (menuIds.value.length === 0) { loadPermissions(); } } }, }); async function loadPermissions() { loadingPermissions.value = true; try { const res = await queryMenuList('all'); menuIds.value = res.map((item) => ({ key: item.id, title: item.name, children: item.children || null, meta: item.meta, })); } finally { loadingPermissions.value = false; } } const getDrawerTitle = computed(() => { return formData.value?.id ? $t('common.edit', '角色') : $t('common.create', '角色'); }); function getNodeClass(node: Recordable<any>) { const classes: string[] = []; if (node.value?.type === 'button') { classes.push('inline-flex'); if (node.index % 3 >= 1) { classes.push('!pl-0'); } } return classes.join(' '); } </script> <template> <Drawer :title="getDrawerTitle"> <Form> <template #menuIds="slotProps"> <Spin :spinning="loadingPermissions" wrapper-class-name="w-full"> <VbenTree :tree-data="menuIds" multiple bordered :default-expanded-level="2" :get-node-class="getNodeClass" v-bind="slotProps" value-field="id" label-field="meta.title" icon-field="meta.icon" > <template #node="{ value }"> <!-- <IconifyIcon v-if="value.meta.icon" :icon="value.meta.icon" />--> {{ value.meta.locale }} </template> </VbenTree> </Spin> </template> </Form> </Drawer> </template> <style lang="css" scoped> :deep(.ant-tree-title) { .tree-actions { display: none; margin-left: 20px; } } :deep(.ant-tree-title:hover) { .tree-actions { display: flex; flex: auto; justify-content: flex-end; margin-left: 20px; } } </style>