|
|
@@ -1,16 +1,18 @@
|
|
|
<script lang="ts" setup>
|
|
|
+import type { DataNode } from 'ant-design-vue/es/tree';
|
|
|
+
|
|
|
import type { Recordable } from '@vben/types';
|
|
|
|
|
|
import type { RelationRequest } from '#/api/model';
|
|
|
|
|
|
import { onMounted, reactive, ref, unref } from 'vue';
|
|
|
|
|
|
-import { useVbenDrawer } from '@vben/common-ui';
|
|
|
+import { useVbenDrawer, VbenTree } from '@vben/common-ui';
|
|
|
|
|
|
-import { message } from 'ant-design-vue';
|
|
|
+import { message, Spin } from 'ant-design-vue';
|
|
|
|
|
|
+import { useVbenForm } from '#/adapter';
|
|
|
import { MenuApi, RoleApi, TenantApi } from '#/api';
|
|
|
-import { BcTree } from '#/components/bc-tree';
|
|
|
import { Icon } from '#/components/icon';
|
|
|
|
|
|
defineOptions({
|
|
|
@@ -21,9 +23,24 @@ const emit = defineEmits(['success']);
|
|
|
const modelRef = ref<Recordable<any>>({});
|
|
|
|
|
|
const state = reactive<{
|
|
|
- checkedKeys: number[] | string[];
|
|
|
+ loadingTree: boolean;
|
|
|
+ treeData: DataNode[];
|
|
|
}>({
|
|
|
- checkedKeys: [],
|
|
|
+ treeData: [],
|
|
|
+ loadingTree: false,
|
|
|
+});
|
|
|
+
|
|
|
+const [Form, { setValues, validate, getValues }] = useVbenForm({
|
|
|
+ schema: [
|
|
|
+ {
|
|
|
+ component: 'Input',
|
|
|
+ fieldName: 'permissions',
|
|
|
+ formItemClass: 'items-start',
|
|
|
+ label: '菜单列表',
|
|
|
+ modelPropName: 'modelValue',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ showDefaultActions: false,
|
|
|
});
|
|
|
|
|
|
const getRoleDetailData = async () => {
|
|
|
@@ -31,16 +48,21 @@ const getRoleDetailData = async () => {
|
|
|
modelRef.value.type === 'tenant'
|
|
|
? await TenantApi.getMenuIds(unref(modelRef).id)
|
|
|
: await RoleApi.getMenuIds(unref(modelRef).id);
|
|
|
- state.checkedKeys = data || [];
|
|
|
+
|
|
|
+ setValues({ permissions: data });
|
|
|
};
|
|
|
|
|
|
-const [Drawer, { close, setState, getData }] = useVbenDrawer({
|
|
|
+const [Drawer, { close, setState, getData, lock, unlock }] = useVbenDrawer({
|
|
|
onConfirm: async () => {
|
|
|
+ const { valid } = await validate();
|
|
|
+ if (!valid) return;
|
|
|
+
|
|
|
+ const values = await getValues();
|
|
|
try {
|
|
|
- setState({ confirmLoading: true });
|
|
|
+ lock();
|
|
|
const postData: RelationRequest = {
|
|
|
id: unref(modelRef).id,
|
|
|
- relationIds: state.checkedKeys,
|
|
|
+ relationIds: values.permissions,
|
|
|
};
|
|
|
modelRef.value.type === 'tenant'
|
|
|
? await TenantApi.updateGrant(postData)
|
|
|
@@ -51,7 +73,7 @@ const [Drawer, { close, setState, getData }] = useVbenDrawer({
|
|
|
} catch {
|
|
|
message.error('操作失败');
|
|
|
} finally {
|
|
|
- setState({ confirmLoading: false });
|
|
|
+ unlock();
|
|
|
}
|
|
|
},
|
|
|
onOpenChange: async (isOpen: boolean) => {
|
|
|
@@ -59,35 +81,65 @@ const [Drawer, { close, setState, getData }] = useVbenDrawer({
|
|
|
setState({ loading: true });
|
|
|
const data = getData<Recordable<any>>();
|
|
|
modelRef.value = { ...data.baseData };
|
|
|
+
|
|
|
+ if (state.treeData.length === 0) {
|
|
|
+ loadTreeData();
|
|
|
+ }
|
|
|
+
|
|
|
await getRoleDetailData();
|
|
|
+
|
|
|
setState({ loading: false });
|
|
|
}
|
|
|
},
|
|
|
});
|
|
|
|
|
|
onMounted(async () => {});
|
|
|
-const handleCheck = (value: any) => {
|
|
|
- state.checkedKeys = [...value];
|
|
|
+
|
|
|
+function getNodeClass(node: Recordable<any>) {
|
|
|
+ const classes: string[] = [];
|
|
|
+ if (node.value?.type === 2) {
|
|
|
+ classes.push('inline-flex');
|
|
|
+ if (node.index % 5 >= 1) {
|
|
|
+ classes.push('!pl-0');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return classes.join(' ');
|
|
|
+}
|
|
|
+const loadTreeData = async () => {
|
|
|
+ state.loadingTree = true;
|
|
|
+ try {
|
|
|
+ const res = await MenuApi.getList({});
|
|
|
+ state.treeData = res as unknown as DataNode[];
|
|
|
+ } finally {
|
|
|
+ state.loadingTree = false;
|
|
|
+ }
|
|
|
};
|
|
|
</script>
|
|
|
<template>
|
|
|
<Drawer class="w-[1000px]" title="授权菜单">
|
|
|
- <div class="h-full">
|
|
|
- <BcTree
|
|
|
- :api="{ url: MenuApi.getList }"
|
|
|
- :field-names="{ label: 'name', value: 'id', children: 'children' }"
|
|
|
- :checkable="true"
|
|
|
- :checked-keys="state.checkedKeys"
|
|
|
- @check="handleCheck"
|
|
|
- title="菜单列表"
|
|
|
- >
|
|
|
- <template #title="{ meta }">
|
|
|
- <div class="flex items-center">
|
|
|
- <Icon v-if="meta.icon" :icon="meta.icon" class="mr-1" />
|
|
|
- <span>{{ meta.title }}</span>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </BcTree>
|
|
|
- </div>
|
|
|
+ <Form>
|
|
|
+ <template #permissions="slotProps">
|
|
|
+ <Spin :spinning="state.loadingTree">
|
|
|
+ <VbenTree
|
|
|
+ :tree-data="state.treeData"
|
|
|
+ multiple
|
|
|
+ :bordered="false"
|
|
|
+ v-bind="slotProps"
|
|
|
+ :default-expanded-level="2"
|
|
|
+ :get-node-class="getNodeClass"
|
|
|
+ value-field="id"
|
|
|
+ label-field="meta.title"
|
|
|
+ icon-field="meta.icon"
|
|
|
+ >
|
|
|
+ <template #node="{ value }">
|
|
|
+ <Icon v-if="value.meta.icon" :icon="value.meta.icon" />
|
|
|
+ {{ $t(value.meta.title) }}
|
|
|
+ </template>
|
|
|
+ </VbenTree>
|
|
|
+ </Spin>
|
|
|
+ </template>
|
|
|
+ </Form>
|
|
|
</Drawer>
|
|
|
</template>
|
|
|
+<style lang="less" scoped></style>
|