This commit is contained in:
王创世 2025-04-27 16:53:17 +08:00
parent bb37a7a997
commit 26b5c7e78f
7 changed files with 105 additions and 86 deletions

21
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\vite.config.ts",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}

View File

@ -46,19 +46,8 @@ function refresh() {
<template #icon> <template #icon>
<icon-ic-round-plus class="text-icon" /> <icon-ic-round-plus class="text-icon" />
</template> </template>
{{ $t('common.add') }} 新增
</NButton> </NButton>
<NPopconfirm @positive-click="batchDelete">
<template #trigger>
<NButton size="small" ghost type="error" :disabled="disabledDelete">
<template #icon>
<icon-ic-round-delete class="text-icon" />
</template>
{{ $t('common.batchDelete') }}
</NButton>
</template>
{{ $t('common.confirmDelete') }}
</NPopconfirm>
</slot> </slot>
<NButton size="small" @click="refresh"> <NButton size="small" @click="refresh">
<template #icon> <template #icon>

View File

@ -191,7 +191,7 @@ init();
@contextmenu="handleContextMenu($event, tab.id)" @contextmenu="handleContextMenu($event, tab.id)"
> >
<template #prefix> <template #prefix>
<SvgIcon :icon="tab.icon" :local-icon="tab.localIcon" class="inline-block align-text-bottom text-16px" /> <SvgIcon :icon="tab.icon" class="inline-block align-text-bottom text-16px" />
</template> </template>
<div class="max-w-240px ellipsis-text">{{ tab.label }}</div> <div class="max-w-240px ellipsis-text">{{ tab.label }}</div>
</PageTab> </PageTab>

View File

@ -10,11 +10,12 @@ export function fetchGetUserRoutes() {
return request<Api.Route.UserRoute>({ url: '/adminapi/admin/menu?is_tree=1' }); return request<Api.Route.UserRoute>({ url: '/adminapi/admin/menu?is_tree=1' });
} }
/**
* whether the route is exist
* export function getSubmitParams(item) {
* @param routeName route name return request({
*/ url: '/adminapi/rule/add',
export function fetchIsRouteExist(routeName: string) { method: 'post',
return request<boolean>({ url: '/route/isRouteExist', params: { routeName } }); data:item
});
} }

View File

@ -4,7 +4,7 @@ import { defineStore } from 'pinia';
import { useBoolean } from '@sa/hooks'; import { useBoolean } from '@sa/hooks';
import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types'; import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types';
import { router } from '@/router'; import { router } from '@/router';
import { fetchGetConstantRoutes, fetchGetUserRoutes, fetchIsRouteExist } from '@/service/api'; import { fetchGetConstantRoutes, fetchGetUserRoutes } from '@/service/api';
import { SetupStoreId } from '@/enum'; import { SetupStoreId } from '@/enum';
import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes'; import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes';
import { ROOT_ROUTE } from '@/router/routes/builtin'; import { ROOT_ROUTE } from '@/router/routes/builtin';
@ -328,9 +328,9 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
return isRouteExistByRouteName(routeName, staticAuthRoutes); return isRouteExistByRouteName(routeName, staticAuthRoutes);
} }
const { data } = await fetchIsRouteExist(routeName); //const { data } = await fetchIsRouteExist(routeName);
return data; return false;
} }
/** /**

View File

@ -20,8 +20,10 @@ declare module 'vue' {
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default'] IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default'] IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default'] IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
'IconIc:roundPlus': typeof import('~icons/ic/round-plus')['default']
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default'] IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default'] IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
IconIcRoundRemove: typeof import('~icons/ic/round-remove')['default']
IconLocalBanner: typeof import('~icons/local/banner')['default'] IconLocalBanner: typeof import('~icons/local/banner')['default']
IconLocalLogo: typeof import('~icons/local/logo')['default'] IconLocalLogo: typeof import('~icons/local/logo')['default']
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default'] IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
@ -47,9 +49,11 @@ declare module 'vue' {
NDrawer: typeof import('naive-ui')['NDrawer'] NDrawer: typeof import('naive-ui')['NDrawer']
NDrawerContent: typeof import('naive-ui')['NDrawerContent'] NDrawerContent: typeof import('naive-ui')['NDrawerContent']
NDropdown: typeof import('naive-ui')['NDropdown'] NDropdown: typeof import('naive-ui')['NDropdown']
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
NEmpty: typeof import('naive-ui')['NEmpty'] NEmpty: typeof import('naive-ui')['NEmpty']
NForm: typeof import('naive-ui')['NForm'] NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem'] NFormItem: typeof import('naive-ui')['NFormItem']
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
NGi: typeof import('naive-ui')['NGi'] NGi: typeof import('naive-ui')['NGi']
NGrid: typeof import('naive-ui')['NGrid'] NGrid: typeof import('naive-ui')['NGrid']
NInput: typeof import('naive-ui')['NInput'] NInput: typeof import('naive-ui')['NInput']
@ -64,6 +68,8 @@ declare module 'vue' {
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopconfirm: typeof import('naive-ui')['NPopconfirm'] NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NPopover: typeof import('naive-ui')['NPopover'] NPopover: typeof import('naive-ui')['NPopover']
NRadio: typeof import('naive-ui')['NRadio']
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
NScrollbar: typeof import('naive-ui')['NScrollbar'] NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect'] NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace'] NSpace: typeof import('naive-ui')['NSpace']

View File

@ -8,14 +8,23 @@ 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 SvgIcon from '@/components/custom/svg-icon.vue'; import SvgIcon from '@/components/custom/svg-icon.vue';
import { fetchGetUserRoutes } from '@/service/api/route'; import { fetchGetUserRoutes,getSubmitParams } from '@/service/api/route';
const appStore = useAppStore(); const appStore = useAppStore();
const { bool: visible, setTrue: openModal } = useBoolean(); const { bool: visible, setTrue: openModal } = useBoolean();
const wrapperRef = ref<HTMLElement | null>(null); const wrapperRef = ref<HTMLElement | null>(null);
const title=ref('');
const model = ref({
title: '',
name:'',
url:'',
condition:'',
icon:'',
weigh:'',
});
const rules = {};
const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useTable({ const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useTable({
apiFn: fetchGetUserRoutes, apiFn: fetchGetUserRoutes,
columns: () => [ columns: () => [
@ -66,24 +75,6 @@ const { columns, columnChecks, data, loading, pagination, getData, getDataByPage
align: 'center', align: 'center',
width: 200, width: 200,
}, },
{
key: 'hideInMenu',
title: '是否隐藏',
align: 'center',
width: 120,
render: row => {
const hide: CommonType.YesOrNo = row.hideInMenu ? 'Y' : 'N';
const tagMap: Record<CommonType.YesOrNo, NaiveUI.ThemeColor> = {
Y: 'error',
N: 'default'
};
const label = $t(yesOrNoRecord[hide]);
return <NTag type={tagMap[hide]}>{label}</NTag>;
}
},
{ {
key: 'weigh', key: 'weigh',
title: '排序', title: '排序',
@ -110,7 +101,7 @@ const { columns, columnChecks, data, loading, pagination, getData, getDataByPage
default: () => '确定删除吗?', default: () => '确定删除吗?',
trigger: () => ( trigger: () => (
<NButton type="error" ghost size="small"> <NButton type="error" ghost size="small">
'删除' 删除
</NButton> </NButton>
) )
}} }}
@ -123,10 +114,11 @@ const { columns, columnChecks, data, loading, pagination, getData, getDataByPage
const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData); const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData);
const operateType = ref<OperateType>('add'); const operateType = ref('add');
function handleAdd() { function handleAdd() {
operateType.value = 'add'; operateType.value = 'add';
title.value = '新增菜单';
openModal(); openModal();
} }
@ -145,72 +137,82 @@ function handleDelete(id: number) {
} }
/** the edit menu data or the parent menu data when adding a child menu */ /** the edit menu data or the parent menu data when adding a child menu */
const editingData: Ref<Api.SystemManage.Menu | null> = ref(null); const editingData = ref(null);
function handleEdit(item: Api.SystemManage.Menu) { function handleEdit(item) {
operateType.value = 'edit'; operateType.value = 'edit';
editingData.value = { ...item }; editingData.value = { ...item };
openModal(); openModal();
} }
function handleAddChildMenu(item: Api.SystemManage.Menu) { function handleAddChildMenu(item) {
operateType.value = 'addChild'; operateType.value = 'addChild';
editingData.value = { ...item }; editingData.value = { ...item };
openModal(); openModal();
} }
const allPages = ref<string[]>([]);
async function getAllPages() {
//const { data: pages } = await fetchGetAllPages();
//allPages.value = pages || [];
}
function init() { function init() {
getAllPages();
} }
// init // init
init(); init();
async function handleSubmit() {
const params = getSubmitParams(model.value);
console.log('params: ', params);
// request
window.$message?.success($t('common.updateSuccess'));
visible.value=false;
}
</script> </script>
<template> <template>
<div ref="wrapperRef" class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto"> <div ref="wrapperRef" class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<NCard title="菜单管理" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper"> <NCard title="菜单管理" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<template #header-extra> <template #header-extra>
<TableHeaderOperation <TableHeaderOperation v-model:columns="columnChecks" :disabled-delete="checkedRowKeys.length === 0"
v-model:columns="columnChecks" :loading="loading" @add="handleAdd" @delete="handleBatchDelete" @refresh="getData" />
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
/>
</template> </template>
<NDataTable <NDataTable v-model:checked-row-keys="checkedRowKeys" :columns="columns" :data="data" size="small"
v-model:checked-row-keys="checkedRowKeys" :flex-height="!appStore.isMobile" :scroll-x="1088" :loading="loading" :row-key="row => row.id" remote
:columns="columns" :pagination="pagination" class="sm:h-full" />
:data="data"
size="small"
:flex-height="!appStore.isMobile"
:scroll-x="1088"
:loading="loading"
:row-key="row => row.id"
remote
:pagination="pagination"
class="sm:h-full"
/>
<MenuOperateModal
v-model:visible="visible"
:operate-type="operateType"
:row-data="editingData"
:all-pages="allPages"
@submitted="getDataByPage"
/>
</NCard> </NCard>
<NModal v-model:show="visible" :title="title" preset="card" class="w-800px">
<NScrollbar class="h-480px pr-20px">
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="100">
<NGrid responsive="screen" item-responsive>
<NFormItemGi span="24" label="菜单名称" path="title">
<NInput v-model:value="model.title" placeholder="请输入菜单名称" />
</NFormItemGi>
<NFormItemGi span="24" label="路由名称" path="name">
<NInput v-model:value="model.name" placeholder="请输入路由名称" />
</NFormItemGi>
<NFormItemGi span="24" label="路由路径" path="url">
<NInput v-model:value="model.url" placeholder="请输入路由路径" />
</NFormItemGi>
<NFormItemGi span="24" label="路由组件" path="condition">
<NInput v-model:value="model.condition" placeholder="请输入路由组件" />
</NFormItemGi>
<NFormItemGi span="24 m:12" label="icon" path="icon">
<NInput v-model:value="model.icon" placeholder="请输入icon" />
</NFormItemGi>
<NFormItemGi span="24 m:12" label="排序" path="weigh">
<NInput v-model:value="model.weigh" placeholder="请输入排序" />
</NFormItemGi>
</NGrid>
</NForm>
</NScrollbar>
<template #footer>
<NSpace justify="end" :size="16">
<NButton @click="visible = false">取消</NButton>
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
</NSpace>
</template>
</NModal>
</div> </div>
</template> </template>