更新
This commit is contained in:
parent
bb37a7a997
commit
26b5c7e78f
21
.vscode/launch.json
vendored
Normal file
21
.vscode/launch.json
vendored
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
6
src/typings/components.d.ts
vendored
6
src/typings/components.d.ts
vendored
@ -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']
|
||||||
|
@ -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: '排序',
|
||||||
@ -97,7 +88,7 @@ const { columns, columnChecks, data, loading, pagination, getData, getDataByPage
|
|||||||
width: 230,
|
width: 230,
|
||||||
render: row => (
|
render: row => (
|
||||||
<div class="flex-center justify-end gap-8px">
|
<div class="flex-center justify-end gap-8px">
|
||||||
{row.pid ==0 && (
|
{row.pid == 0 && (
|
||||||
<NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row)}>
|
<NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row)}>
|
||||||
添加子菜单
|
添加子菜单
|
||||||
</NButton>
|
</NButton>
|
||||||
@ -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>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user