perf(projects): perf manage menu
This commit is contained in:
parent
49558ca048
commit
71f2c5535b
@ -212,14 +212,15 @@ export function useTableOperate<T extends TableData = TableData>(data: Ref<T[]>,
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
drawerVisible,
|
drawerVisible,
|
||||||
|
openDrawer,
|
||||||
|
closeDrawer,
|
||||||
operateType,
|
operateType,
|
||||||
handleAdd,
|
handleAdd,
|
||||||
editingData,
|
editingData,
|
||||||
handleEdit,
|
handleEdit,
|
||||||
checkedRowKeys,
|
checkedRowKeys,
|
||||||
onBatchDeleted,
|
onBatchDeleted,
|
||||||
onDeleted,
|
onDeleted
|
||||||
closeDrawer
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,8 +319,9 @@ const local: App.I18n.Schema = {
|
|||||||
menuName: 'Menu Name',
|
menuName: 'Menu Name',
|
||||||
routeName: 'Route Name',
|
routeName: 'Route Name',
|
||||||
routePath: 'Route Path',
|
routePath: 'Route Path',
|
||||||
page: 'Page Component',
|
routeParams: 'Route Params',
|
||||||
layout: 'Layout Component',
|
layout: 'Layout Component',
|
||||||
|
page: 'Page Component',
|
||||||
i18nKey: 'I18n Key',
|
i18nKey: 'I18n Key',
|
||||||
icon: 'Icon',
|
icon: 'Icon',
|
||||||
localIcon: 'Local Icon',
|
localIcon: 'Local Icon',
|
||||||
|
@ -319,8 +319,9 @@ const local: App.I18n.Schema = {
|
|||||||
menuName: '菜单名称',
|
menuName: '菜单名称',
|
||||||
routeName: '路由名称',
|
routeName: '路由名称',
|
||||||
routePath: '路由路径',
|
routePath: '路由路径',
|
||||||
page: '页面组件',
|
routeParams: '路由参数',
|
||||||
layout: '布局',
|
layout: '布局',
|
||||||
|
page: '页面组件',
|
||||||
i18nKey: '国际化key',
|
i18nKey: '国际化key',
|
||||||
icon: '图标',
|
icon: '图标',
|
||||||
localIcon: '本地图标',
|
localIcon: '本地图标',
|
||||||
|
@ -49,3 +49,11 @@ export function fetchGetMenuList() {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** get all pages */
|
||||||
|
export function fetchGetAllPages() {
|
||||||
|
return request<string[]>({
|
||||||
|
url: '/systemManage/getAllPages',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
5
src/typings/app.d.ts
vendored
5
src/typings/app.d.ts
vendored
@ -502,8 +502,9 @@ declare namespace App {
|
|||||||
menuName: string;
|
menuName: string;
|
||||||
routeName: string;
|
routeName: string;
|
||||||
routePath: string;
|
routePath: string;
|
||||||
page: string;
|
routeParams: string;
|
||||||
layout: string;
|
layout: string;
|
||||||
|
page: string;
|
||||||
i18nKey: string;
|
i18nKey: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
localIcon: string;
|
localIcon: string;
|
||||||
@ -524,8 +525,8 @@ declare namespace App {
|
|||||||
menuName: string;
|
menuName: string;
|
||||||
routeName: string;
|
routeName: string;
|
||||||
routePath: string;
|
routePath: string;
|
||||||
page: string;
|
|
||||||
layout: string;
|
layout: string;
|
||||||
|
page: string;
|
||||||
i18nKey: string;
|
i18nKey: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
localIcon: string;
|
localIcon: string;
|
||||||
|
2
src/typings/common.d.ts
vendored
2
src/typings/common.d.ts
vendored
@ -14,7 +14,7 @@ declare namespace CommonType {
|
|||||||
* @property value: The option value
|
* @property value: The option value
|
||||||
* @property label: The option label
|
* @property label: The option label
|
||||||
*/
|
*/
|
||||||
type Option<K> = { value: K; label: string };
|
type Option<K = string> = { value: K; label: string };
|
||||||
|
|
||||||
type YesOrNo = 'Y' | 'N';
|
type YesOrNo = 'Y' | 'N';
|
||||||
|
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
import { fetchGetMenuList } from '@/service/api';
|
import { useBoolean } from '@sa/hooks';
|
||||||
|
import { fetchGetAllPages, fetchGetMenuList } from '@/service/api';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
import { useTable, useTableOperate } from '@/hooks/common/table';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { yesOrNoRecord } from '@/constants/common';
|
import { yesOrNoRecord } from '@/constants/common';
|
||||||
import { enableStatusRecord, menuTypeRecord } from '@/constants/business';
|
import { enableStatusRecord, menuTypeRecord } from '@/constants/business';
|
||||||
import SvgIcon from '@/components/custom/svg-icon.vue';
|
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||||
import MenuOperateDrawer from './modules/menu-operate-drawer.vue';
|
import MenuOperateDrawer, { type OperateType } from './modules/menu-operate-drawer.vue';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const { bool: drawerVisible, setTrue: openDrawer, setFalse: _closeDrawer } = useBoolean();
|
||||||
|
|
||||||
const wrapperRef = ref<HTMLElement | null>(null);
|
const wrapperRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const { columns, columnChecks, data, loading, pagination, getData } = useTable({
|
const { columns, columnChecks, data, loading, pagination, getData } = useTable({
|
||||||
@ -143,11 +147,11 @@ const { columns, columnChecks, data, loading, pagination, getData } = useTable({
|
|||||||
render: row => (
|
render: row => (
|
||||||
<div class="flex-center justify-end gap-8px">
|
<div class="flex-center justify-end gap-8px">
|
||||||
{row.menuType === '1' && (
|
{row.menuType === '1' && (
|
||||||
<NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row.id)}>
|
<NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row)}>
|
||||||
{$t('page.manage.menu.addChildMenu')}
|
{$t('page.manage.menu.addChildMenu')}
|
||||||
</NButton>
|
</NButton>
|
||||||
)}
|
)}
|
||||||
<NButton type="primary" ghost size="small" onClick={() => edit(row.id)}>
|
<NButton type="primary" ghost size="small" onClick={() => handleEdit(row)}>
|
||||||
{$t('common.edit')}
|
{$t('common.edit')}
|
||||||
</NButton>
|
</NButton>
|
||||||
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
||||||
@ -166,17 +170,14 @@ const { columns, columnChecks, data, loading, pagination, getData } = useTable({
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData);
|
||||||
drawerVisible,
|
|
||||||
operateType,
|
const operateType = ref<OperateType>('add');
|
||||||
editingData,
|
|
||||||
handleAdd,
|
function handleAdd() {
|
||||||
handleEdit,
|
operateType.value = 'add';
|
||||||
checkedRowKeys,
|
openDrawer();
|
||||||
onBatchDeleted,
|
}
|
||||||
onDeleted
|
|
||||||
// closeDrawer
|
|
||||||
} = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
async function handleBatchDelete() {
|
||||||
// request
|
// request
|
||||||
@ -192,15 +193,37 @@ function handleDelete(id: number) {
|
|||||||
onDeleted();
|
onDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAddChildMenu(id: number) {
|
/** the edit menu data or the parent menu data when adding a child menu */
|
||||||
console.log(id);
|
const editingData: Ref<Api.SystemManage.Menu | null> = ref(null);
|
||||||
|
|
||||||
handleAdd();
|
function handleEdit(item: Api.SystemManage.Menu) {
|
||||||
|
operateType.value = 'edit';
|
||||||
|
editingData.value = { ...item };
|
||||||
|
|
||||||
|
openDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function edit(id: number) {
|
function handleAddChildMenu(item: Api.SystemManage.Menu) {
|
||||||
handleEdit(id);
|
operateType.value = 'addChild';
|
||||||
|
|
||||||
|
editingData.value = { ...item };
|
||||||
|
|
||||||
|
openDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allPages = ref<string[]>([]);
|
||||||
|
|
||||||
|
async function getAllPages() {
|
||||||
|
const { data: pages } = await fetchGetAllPages();
|
||||||
|
allPages.value = pages || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
getAllPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
init();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -233,6 +256,7 @@ function edit(id: number) {
|
|||||||
v-model:visible="drawerVisible"
|
v-model:visible="drawerVisible"
|
||||||
:operate-type="operateType"
|
:operate-type="operateType"
|
||||||
:row-data="editingData"
|
:row-data="editingData"
|
||||||
|
:all-pages="allPages"
|
||||||
@submitted="getData"
|
@submitted="getData"
|
||||||
/>
|
/>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
@ -6,16 +6,21 @@ import { $t } from '@/locales';
|
|||||||
import { enableStatusOptions, menuIconTypeOptions, menuTypeOptions } from '@/constants/business';
|
import { enableStatusOptions, menuIconTypeOptions, menuTypeOptions } from '@/constants/business';
|
||||||
import SvgIcon from '@/components/custom/svg-icon.vue';
|
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||||
import { getLocalIcons } from '@/utils/icon';
|
import { getLocalIcons } from '@/utils/icon';
|
||||||
|
import { getLayoutAndPage } from './shared';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MenuOperateDrawer'
|
name: 'MenuOperateDrawer'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type OperateType = NaiveUI.TableOperateType | 'addChild';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** the type of operation */
|
/** the type of operation */
|
||||||
operateType: NaiveUI.TableOperateType;
|
operateType: OperateType;
|
||||||
/** the edit row data */
|
/** the edit menu data or the parent menu data when adding a child menu */
|
||||||
rowData?: Api.SystemManage.Menu | null;
|
rowData?: Api.SystemManage.Menu | null;
|
||||||
|
/** all pages */
|
||||||
|
allPages: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
@ -34,8 +39,9 @@ const { formRef, validate, restoreValidation } = useNaiveForm();
|
|||||||
const { defaultRequiredRule } = useFormRules();
|
const { defaultRequiredRule } = useFormRules();
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
const titles: Record<OperateType, string> = {
|
||||||
add: $t('page.manage.menu.addMenu'),
|
add: $t('page.manage.menu.addMenu'),
|
||||||
|
addChild: $t('page.manage.menu.addChildMenu'),
|
||||||
edit: $t('page.manage.menu.editMenu')
|
edit: $t('page.manage.menu.editMenu')
|
||||||
};
|
};
|
||||||
return titles[props.operateType];
|
return titles[props.operateType];
|
||||||
@ -43,8 +49,21 @@ const title = computed(() => {
|
|||||||
|
|
||||||
type Model = Pick<
|
type Model = Pick<
|
||||||
Api.SystemManage.Menu,
|
Api.SystemManage.Menu,
|
||||||
'menuType' | 'menuName' | 'icon' | 'iconType' | 'routeName' | 'routePath' | 'status' | 'hideInMenu' | 'order'
|
| 'menuType'
|
||||||
>;
|
| 'menuName'
|
||||||
|
| 'icon'
|
||||||
|
| 'iconType'
|
||||||
|
| 'routeName'
|
||||||
|
| 'routePath'
|
||||||
|
| 'component'
|
||||||
|
| 'status'
|
||||||
|
| 'hideInMenu'
|
||||||
|
| 'order'
|
||||||
|
| 'parentId'
|
||||||
|
> & {
|
||||||
|
layout: string;
|
||||||
|
page: string;
|
||||||
|
};
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
const model: Model = reactive(createDefaultModel());
|
||||||
|
|
||||||
@ -56,9 +75,12 @@ function createDefaultModel(): Model {
|
|||||||
iconType: '1',
|
iconType: '1',
|
||||||
routeName: '',
|
routeName: '',
|
||||||
routePath: '',
|
routePath: '',
|
||||||
|
layout: '',
|
||||||
|
page: '',
|
||||||
status: null,
|
status: null,
|
||||||
hideInMenu: false,
|
hideInMenu: false,
|
||||||
order: 0
|
order: 0,
|
||||||
|
parentId: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +91,8 @@ const rules: Record<RuleKey, App.Global.FormRule> = {
|
|||||||
userStatus: defaultRequiredRule
|
userStatus: defaultRequiredRule
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const disabledMenuType = computed(() => props.operateType === 'edit');
|
||||||
|
|
||||||
const localIcons = getLocalIcons();
|
const localIcons = getLocalIcons();
|
||||||
const localIconOptions = localIcons.map<SelectOption>(item => ({
|
const localIconOptions = localIcons.map<SelectOption>(item => ({
|
||||||
label: () => (
|
label: () => (
|
||||||
@ -80,14 +104,55 @@ const localIconOptions = localIcons.map<SelectOption>(item => ({
|
|||||||
value: item
|
value: item
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function handleUpdateModelWhenEdit() {
|
const showLayout = computed(() => model.parentId === 0);
|
||||||
|
|
||||||
|
const showPage = computed(() => model.menuType === '2');
|
||||||
|
|
||||||
|
const pageOptions = computed(() => {
|
||||||
|
const allPages = [...props.allPages];
|
||||||
|
|
||||||
|
if (model.routeName && !allPages.includes(model.routeName)) {
|
||||||
|
allPages.unshift(model.routeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts: CommonType.Option[] = allPages.map(page => ({
|
||||||
|
label: page,
|
||||||
|
value: page
|
||||||
|
}));
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
});
|
||||||
|
|
||||||
|
const layoutOptions: CommonType.Option[] = [
|
||||||
|
{
|
||||||
|
label: 'base',
|
||||||
|
value: 'base'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'blank',
|
||||||
|
value: 'blank'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function handleUpdateModel() {
|
||||||
if (props.operateType === 'add') {
|
if (props.operateType === 'add') {
|
||||||
Object.assign(model, createDefaultModel());
|
Object.assign(model, createDefaultModel());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.operateType === 'addChild' && props.rowData) {
|
||||||
|
const { id } = props.rowData;
|
||||||
|
|
||||||
|
Object.assign(model, createDefaultModel(), { parentId: id });
|
||||||
|
}
|
||||||
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
if (props.operateType === 'edit' && props.rowData) {
|
||||||
Object.assign(model, props.rowData);
|
const { component, ...rest } = props.rowData;
|
||||||
|
|
||||||
|
const { layout, page } = getLayoutAndPage(component);
|
||||||
|
|
||||||
|
Object.assign(model, rest, { layout, page });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +170,7 @@ async function handleSubmit() {
|
|||||||
|
|
||||||
watch(visible, () => {
|
watch(visible, () => {
|
||||||
if (visible.value) {
|
if (visible.value) {
|
||||||
handleUpdateModelWhenEdit();
|
handleUpdateModel();
|
||||||
restoreValidation();
|
restoreValidation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -116,7 +181,7 @@ watch(visible, () => {
|
|||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||||
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="80">
|
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="80">
|
||||||
<NFormItem :label="$t('page.manage.menu.menuType')" path="menuType">
|
<NFormItem :label="$t('page.manage.menu.menuType')" path="menuType">
|
||||||
<NRadioGroup v-model:value="model.menuType">
|
<NRadioGroup v-model:value="model.menuType" :disabled="disabledMenuType">
|
||||||
<NRadio v-for="item in menuTypeOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
<NRadio v-for="item in menuTypeOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
||||||
</NRadioGroup>
|
</NRadioGroup>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
@ -150,6 +215,16 @@ watch(visible, () => {
|
|||||||
<NFormItem :label="$t('page.manage.menu.routePath')" path="routePath">
|
<NFormItem :label="$t('page.manage.menu.routePath')" path="routePath">
|
||||||
<NInput v-model:value="model.routePath" :placeholder="$t('page.manage.menu.form.routePath')" />
|
<NInput v-model:value="model.routePath" :placeholder="$t('page.manage.menu.form.routePath')" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem v-if="showLayout" :label="$t('page.manage.menu.layout')" path="layout">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.layout"
|
||||||
|
:options="layoutOptions"
|
||||||
|
:placeholder="$t('page.manage.menu.form.layout')"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem v-if="showPage" :label="$t('page.manage.menu.page')" path="page">
|
||||||
|
<NSelect v-model:value="model.page" :options="pageOptions" :placeholder="$t('page.manage.menu.form.page')" />
|
||||||
|
</NFormItem>
|
||||||
<NFormItem :label="$t('page.manage.menu.menuStatus')" path="status">
|
<NFormItem :label="$t('page.manage.menu.menuStatus')" path="status">
|
||||||
<NRadioGroup v-model:value="model.status">
|
<NRadioGroup v-model:value="model.status">
|
||||||
<NRadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
<NRadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
||||||
|
24
src/views/manage/menu/modules/shared.ts
Normal file
24
src/views/manage/menu/modules/shared.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const LAYOUT_PREFIX = 'layout.';
|
||||||
|
const VIEW_PREFIX = 'view.';
|
||||||
|
|
||||||
|
export function getLayoutAndPage(component?: string | null) {
|
||||||
|
const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
|
||||||
|
|
||||||
|
let layout = '';
|
||||||
|
let page = '';
|
||||||
|
|
||||||
|
const [layoutOrPage, pageItem] = component?.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT) || [];
|
||||||
|
|
||||||
|
layout = getLayout(layoutOrPage);
|
||||||
|
page = getPage(pageItem || layoutOrPage);
|
||||||
|
|
||||||
|
return { layout, page };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLayout(layout: string) {
|
||||||
|
return layout.startsWith(LAYOUT_PREFIX) ? layout.replace(LAYOUT_PREFIX, '') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPage(page: string) {
|
||||||
|
return page.startsWith(VIEW_PREFIX) ? page.replace(VIEW_PREFIX, '') : '';
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user