chore: 优化代码

This commit is contained in:
dap 2025-01-04 18:30:20 +08:00
parent 543fb0cf91
commit e838537356
3 changed files with 145 additions and 105 deletions

View File

@ -0,0 +1,79 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { ID } from '#/api/common';
import type { MenuOption } from '#/api/system/menu/model';
import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons';
import { h, markRaw } from 'vue';
export interface Permission {
checked: boolean;
id: ID;
label: string;
}
export interface MenuPermissionOption extends MenuOption {
permissions: Permission[];
}
const menuTypes = {
C: { icon: markRaw(MenuIcon), value: '菜单' },
F: { icon: markRaw(OkButtonIcon), value: '按钮' },
M: { icon: markRaw(FolderIcon), value: '目录' },
};
export const columns: VxeGridProps['columns'] = [
{
type: 'checkbox',
title: '菜单名称',
field: 'label',
treeNode: true,
headerAlign: 'left',
align: 'left',
width: 230,
},
{
title: '图标',
field: 'icon',
width: 80,
slots: {
default: ({ row }) => {
if (row?.icon === '#') {
return '';
}
return (
<span class={'flex justify-center'}>
<VbenIcon icon={row.icon} />
</span>
);
},
},
},
{
title: '类型',
field: 'menuType',
width: 80,
slots: {
default: ({ row }) => {
const current = menuTypes[row.menuType as 'C' | 'F' | 'M'];
if (!current) {
return '未知';
}
return (
<span class="flex items-center justify-center gap-1">
{h(current.icon, { class: 'size-[18px]' })}
<span>{current.value}</span>
</span>
);
},
},
},
{
title: '权限标识',
field: 'permissions',
headerAlign: 'left',
align: 'left',
slots: {
default: 'permissions',
},
},
];

View File

@ -1,15 +1,16 @@
<script setup lang="tsx">
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { ID } from '#/api/common';
import type { MenuOption } from '#/api/system/menu/model';
import type { PropType } from 'vue';
import type { MenuPermissionOption, Permission } from './data';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons';
import { cloneDeep, eachTree, findGroupParentIds } from '@vben/utils';
import { Alert, Checkbox, RadioGroup, Space } from 'ant-design-vue';
import { difference, uniq } from 'lodash-es';
import { h, markRaw, nextTick, onMounted, watch } from 'vue';
import { nextTick, onMounted, ref, watch } from 'vue';
import { columns } from './data';
defineOptions({
name: 'MenuSelectTable',
@ -17,27 +18,23 @@ defineOptions({
});
const props = withDefaults(
defineProps<{ defaultExpandAll?: boolean; menus: MenuOption[] }>(),
defineProps<{
checkedKeys: (number | string)[];
defaultExpandAll?: boolean;
menus: MenuOption[];
}>(),
{
/**
* 是否默认展开全部
*/
defaultExpandAll: true,
/**
* 注意这里不是双向绑定 需要调用getCheckedKeys实例方法来获取真正选中的节点
*/
checkedKeys: () => [],
},
);
interface Permission {
checked: boolean;
id: ID;
label: string;
}
interface MenuPermissionOption extends MenuOption {
permissions: Permission[];
}
const checkedKeys = defineModel('checkedKeys', {
type: Array as PropType<(number | string)[]>,
default: () => [],
});
/**
* 是否节点关联
*/
@ -46,12 +43,6 @@ const association = defineModel('association', {
default: true,
});
const menuTypes = {
C: { icon: markRaw(MenuIcon), value: '菜单' },
F: { icon: markRaw(OkButtonIcon), value: '按钮' },
M: { icon: markRaw(FolderIcon), value: '目录' },
};
const gridOptions: VxeGridProps = {
checkboxConfig: {
// checkbox
@ -60,60 +51,7 @@ const gridOptions: VxeGridProps = {
checkStrictly: !association.value,
},
size: 'small',
columns: [
{
type: 'checkbox',
title: '菜单名称',
field: 'label',
treeNode: true,
width: 230,
},
{
title: '图标',
field: 'icon',
width: 80,
slots: {
default: ({ row }) => {
if (row?.icon === '#') {
return '';
}
return (
<span class={'flex justify-center'}>
<VbenIcon icon={row.icon} />
</span>
);
},
},
},
{
title: '类型',
field: 'menuType',
width: 80,
slots: {
default: ({ row }) => {
const current = menuTypes[row.menuType as 'C' | 'F' | 'M'];
if (!current) {
return '未知';
}
return (
<span class="flex items-center justify-center gap-1">
{h(current.icon, { class: 'size-[18px]' })}
<span>{current.value}</span>
</span>
);
},
},
},
{
title: '权限标识',
field: 'permissions',
headerAlign: 'left',
align: 'left',
slots: {
default: 'permissions',
},
},
],
columns,
height: 'auto',
keepSource: true,
pagerConfig: {
@ -174,37 +112,59 @@ function allChecked(record: MenuPermissionOption, checked: boolean) {
});
}
/**
* 用于界面显示选中的数量
*/
const checkedLength = ref(0);
/**
* 更新选中的数量
*/
function updateCheckedLength() {
checkedLength.value = getCheckedKeys().length;
}
const [BasicTable, tableApi] = useVbenVxeGrid({
gridOptions,
gridEvents: {
checkboxChange: (params) => {
//
if (!association.value) {
updateCheckedLength();
return;
}
console.log('params', params);
//
const checked = params.checked;
//
const record = params.row;
//
allChecked(record, checked);
updateCheckedLength();
},
checkboxAll: (params) => {
const records = params.$grid.getData();
records.forEach((item) => {
allChecked(item, params.checked);
});
updateCheckedLength();
},
},
});
/**
* void方法 会直接修改原始数据
* 将树结构转为 tree+permissions结构
* @param menus 后台返回的menu
*/
function menusWithPermissions(menus: MenuOption[]) {
eachTree(menus, (item: MenuPermissionOption) => {
if (item.children && item.children.length > 0) {
//
/**
* 所有为按钮的节点提取出来
* 需要注意 这里需要过滤目录下直接是按钮的情况item.menuType !== 'M'
* 将按钮往children添加而非加到permissions
*/
const permissions = item.children.filter(
(child: MenuOption) => child.menuType === 'F',
(child: MenuOption) => child.menuType === 'F' && item.menuType !== 'M',
);
//
const diffCollection = difference(item.children, permissions);
@ -289,12 +249,15 @@ onMounted(() => {
/**
* checkedKeys依赖menus 要在外部确保menus先加载
*/
watch(checkedKeys, (value) => {
watch(
() => props.checkedKeys,
(value) => {
const allCheckedKeys = uniq([...value]);
//
const records = tableApi.grid.getData();
setCheckedByKeys(records, allCheckedKeys, association.value);
});
},
);
});
const options = [
@ -310,6 +273,7 @@ async function handleAssociationChange() {
});
//
await tableApi.grid.clearCheckboxRow();
updateCheckedLength();
}
/**
@ -336,6 +300,7 @@ function handlePermissionChange(row: any) {
}
}
//
updateCheckedLength();
}
/**
@ -373,13 +338,11 @@ function getCheckedKeys() {
//
if (association.value) {
const records = tableApi?.grid?.getCheckboxRecords?.() ?? [];
console.log(records);
//
const nodeKeys = getKeys(records, true);
//
const parentIds = findGroupParentIds(props.menus, nodeKeys as number[]);
const realKeys = uniq([...parentIds, ...nodeKeys]);
console.log(realKeys);
return realKeys;
}
//
@ -391,7 +354,6 @@ function getCheckedKeys() {
const ids = records.map((item) => item.id);
const permissions = getKeys(allRecords, false);
const allIds = uniq([...ids, ...permissions]);
console.log(allIds);
return allIds;
}
@ -403,18 +365,6 @@ defineExpose({
<template>
<div class="flex h-full flex-col">
<Alert class="mx-2 mb-2" message="beta功能" type="warning" show-icon />
<Alert class="mx-2" type="info" show-icon>
<template #message>
<div v-if="tableApi?.grid">
已选中
<span class="text-primary mx-1 font-semibold">
{{ getCheckedKeys().length }}
</span>
个节点
</div>
</template>
</Alert>
<BasicTable>
<template #toolbar-actions>
<RadioGroup
@ -424,6 +374,17 @@ defineExpose({
option-type="button"
@change="handleAssociationChange"
/>
<Alert class="mx-2" type="info" show-icon>
<template #message>
<div v-if="tableApi?.grid">
已选中
<span class="text-primary mx-1 font-semibold">
{{ checkedLength }}
</span>
个节点
</div>
</template>
</Alert>
</template>
<template #toolbar-tools>
<Space>

View File

@ -130,7 +130,7 @@ function handleMenuCheckStrictlyChange(value: boolean) {
<!-- check-strictly为readonly 不能通过v-model绑定 -->
<MenuSelectTable
ref="menuSelectRef"
v-model:checked-keys="slotProps.value"
:checked-keys="slotProps.value"
:association="formApi.form.values.menuCheckStrictly"
:menus="menuTree"
@update:association="handleMenuCheckStrictlyChange"