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"> <script setup lang="tsx">
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import type { ID } from '#/api/common';
import type { MenuOption } from '#/api/system/menu/model'; 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 { useVbenVxeGrid } from '#/adapter/vxe-table';
import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons';
import { cloneDeep, eachTree, findGroupParentIds } from '@vben/utils'; import { cloneDeep, eachTree, findGroupParentIds } from '@vben/utils';
import { Alert, Checkbox, RadioGroup, Space } from 'ant-design-vue'; import { Alert, Checkbox, RadioGroup, Space } from 'ant-design-vue';
import { difference, uniq } from 'lodash-es'; 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({ defineOptions({
name: 'MenuSelectTable', name: 'MenuSelectTable',
@ -17,27 +18,23 @@ defineOptions({
}); });
const props = withDefaults( const props = withDefaults(
defineProps<{ defaultExpandAll?: boolean; menus: MenuOption[] }>(), defineProps<{
checkedKeys: (number | string)[];
defaultExpandAll?: boolean;
menus: MenuOption[];
}>(),
{ {
/**
* 是否默认展开全部
*/
defaultExpandAll: true, 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, default: true,
}); });
const menuTypes = {
C: { icon: markRaw(MenuIcon), value: '菜单' },
F: { icon: markRaw(OkButtonIcon), value: '按钮' },
M: { icon: markRaw(FolderIcon), value: '目录' },
};
const gridOptions: VxeGridProps = { const gridOptions: VxeGridProps = {
checkboxConfig: { checkboxConfig: {
// checkbox // checkbox
@ -60,60 +51,7 @@ const gridOptions: VxeGridProps = {
checkStrictly: !association.value, checkStrictly: !association.value,
}, },
size: 'small', size: 'small',
columns: [ 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',
},
},
],
height: 'auto', height: 'auto',
keepSource: true, keepSource: true,
pagerConfig: { 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({ const [BasicTable, tableApi] = useVbenVxeGrid({
gridOptions, gridOptions,
gridEvents: { gridEvents: {
checkboxChange: (params) => { checkboxChange: (params) => {
// //
if (!association.value) { if (!association.value) {
updateCheckedLength();
return; return;
} }
console.log('params', params);
// //
const checked = params.checked; const checked = params.checked;
// //
const record = params.row; const record = params.row;
// //
allChecked(record, checked); allChecked(record, checked);
updateCheckedLength();
}, },
checkboxAll: (params) => { checkboxAll: (params) => {
const records = params.$grid.getData(); const records = params.$grid.getData();
records.forEach((item) => { records.forEach((item) => {
allChecked(item, params.checked); allChecked(item, params.checked);
}); });
updateCheckedLength();
}, },
}, },
}); });
/**
* void方法 会直接修改原始数据
* 将树结构转为 tree+permissions结构
* @param menus 后台返回的menu
*/
function menusWithPermissions(menus: MenuOption[]) { function menusWithPermissions(menus: MenuOption[]) {
eachTree(menus, (item: MenuPermissionOption) => { eachTree(menus, (item: MenuPermissionOption) => {
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
// /**
* 所有为按钮的节点提取出来
* 需要注意 这里需要过滤目录下直接是按钮的情况item.menuType !== 'M'
* 将按钮往children添加而非加到permissions
*/
const permissions = item.children.filter( const permissions = item.children.filter(
(child: MenuOption) => child.menuType === 'F', (child: MenuOption) => child.menuType === 'F' && item.menuType !== 'M',
); );
// //
const diffCollection = difference(item.children, permissions); const diffCollection = difference(item.children, permissions);
@ -289,12 +249,15 @@ onMounted(() => {
/** /**
* checkedKeys依赖menus 要在外部确保menus先加载 * checkedKeys依赖menus 要在外部确保menus先加载
*/ */
watch(checkedKeys, (value) => { watch(
() => props.checkedKeys,
(value) => {
const allCheckedKeys = uniq([...value]); const allCheckedKeys = uniq([...value]);
// //
const records = tableApi.grid.getData(); const records = tableApi.grid.getData();
setCheckedByKeys(records, allCheckedKeys, association.value); setCheckedByKeys(records, allCheckedKeys, association.value);
}); },
);
}); });
const options = [ const options = [
@ -310,6 +273,7 @@ async function handleAssociationChange() {
}); });
// //
await tableApi.grid.clearCheckboxRow(); await tableApi.grid.clearCheckboxRow();
updateCheckedLength();
} }
/** /**
@ -336,6 +300,7 @@ function handlePermissionChange(row: any) {
} }
} }
// //
updateCheckedLength();
} }
/** /**
@ -373,13 +338,11 @@ function getCheckedKeys() {
// //
if (association.value) { if (association.value) {
const records = tableApi?.grid?.getCheckboxRecords?.() ?? []; const records = tableApi?.grid?.getCheckboxRecords?.() ?? [];
console.log(records);
// //
const nodeKeys = getKeys(records, true); const nodeKeys = getKeys(records, true);
// //
const parentIds = findGroupParentIds(props.menus, nodeKeys as number[]); const parentIds = findGroupParentIds(props.menus, nodeKeys as number[]);
const realKeys = uniq([...parentIds, ...nodeKeys]); const realKeys = uniq([...parentIds, ...nodeKeys]);
console.log(realKeys);
return realKeys; return realKeys;
} }
// //
@ -391,7 +354,6 @@ function getCheckedKeys() {
const ids = records.map((item) => item.id); const ids = records.map((item) => item.id);
const permissions = getKeys(allRecords, false); const permissions = getKeys(allRecords, false);
const allIds = uniq([...ids, ...permissions]); const allIds = uniq([...ids, ...permissions]);
console.log(allIds);
return allIds; return allIds;
} }
@ -403,18 +365,6 @@ defineExpose({
<template> <template>
<div class="flex h-full flex-col"> <div class="flex h-full flex-col">
<Alert class="mx-2 mb-2" message="beta功能" type="warning" show-icon /> <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> <BasicTable>
<template #toolbar-actions> <template #toolbar-actions>
<RadioGroup <RadioGroup
@ -424,6 +374,17 @@ defineExpose({
option-type="button" option-type="button"
@change="handleAssociationChange" @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>
<template #toolbar-tools> <template #toolbar-tools>
<Space> <Space>

View File

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