feat(ui): 新增系统分类菜单

This commit is contained in:
廖彦棋 2024-03-14 15:17:53 +08:00
parent ec5ad809f2
commit 3866319373
8 changed files with 257 additions and 45 deletions

View File

@ -5,5 +5,13 @@ declare module "*.vue" {
const component: DefineComponent<{}, {}, any>;
export default component;
}
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
title?: string
icon?: any
permission?: boolean | string | string[]
}
}
declare const __AUTH_KEY: string;

View File

@ -1,9 +1,46 @@
<script lang="ts">
import { defineComponent, h } from "vue";
import type { Component, PropType } from "vue";
import type { RouteRecordRaw } from "vue-router";
import { SubMenu, MenuItem } from "@arco-design/web-vue";
const CustomMenuItem: Component = defineComponent({
props: {
tree: {
type: Array as PropType<RouteRecordRaw[]>,
default: () => [],
},
},
setup: (props) => {
return () =>
props.tree?.map((item) => {
const _icon = item.meta?.icon ? h(item.meta.icon) : undefined;
const hasChildren = Array.isArray(item.children) && item.children.length;
if (hasChildren) {
return h(
SubMenu,
{ title: item.meta.title, key: item.name },
{
default: () => h(CustomMenuItem, { tree: item.children }),
icon: () => _icon,
}
);
}
return h(
MenuItem,
{ key: item.name },
{ default: () => item.meta.title, icon: () => _icon }
);
});
},
});
</script>
<script lang="ts" setup>
import { computed } from "vue";
import { useRoute } from "vue-router";
import router from "@/router";
import showMenu from "@/router/menu";
import menu from "@/router/menu";
import { hasPermission } from "@/directives/permission";
defineProps({
width: {
type: [Number, String],
@ -12,18 +49,16 @@ defineProps({
});
const route = useRoute();
const goto = (name: string) => router.push({ name });
const selectedKeys = computed(() => [route.name]);
const showMenu = computed(() => menu.filter((item: any) => hasPermission(item.meta?.permission)));
</script>
<template>
<ALayoutSider :style="{ width, height: '100%' }">
<AMenu :selected-keys="selectedKeys" @menu-item-click="goto">
<AMenuItem v-for="item in showMenu" :key="item.name">
<template #icon>
<component :is="item.meta?.icon" />
</template>
{{ item.meta.title }}
</AMenuItem>
<AMenu :selectedKeys="selectedKeys" auto-open-selected @menu-item-click="goto">
<CustomMenuItem :tree="showMenu" />
</AMenu>
</ALayoutSider>
</template>

View File

@ -4,13 +4,6 @@ import CustomLayout from '@/components/CustomLayout.vue'
import { hasPermission } from "@/directives/permission";
import menu from './menu'
declare module 'vue-router' {
interface RouteMeta {
title?: string
permission?: string
}
}
const whiteListRoutes = [
{
path: "/login",

View File

@ -1,21 +1,22 @@
import type { RouteRecordRaw } from "vue-router";
import {
IconUser,
IconDashboard,
IconOrderedList,
IconCalendar,
IconHeartFill,
IconCodeSandbox,
IconCodeSquare,
IconMessage,
IconSettings,
IconUserGroup,
IconLock,
IconCodepen,
IconWechatpay,
IconRobot,
IconSafe,
} from "@arco-design/web-vue/es/icon";
const menu = [
import system from "./system";
const menu: RouteRecordRaw[] = [
{
path: "/dashboard",
name: "Dashboard",
@ -38,8 +39,8 @@ const menu = [
path: "/role",
name: "Role",
meta: {
title: "角色管理",
icon: IconUserGroup,
title: "角色模型",
icon: IconCodeSandbox,
},
component: () => import("@/views/Role/RoleContainer.vue"),
},
@ -111,38 +112,22 @@ const menu = [
path: "/system",
name: "System",
meta: {
title: "系统设置",
title: "网站设置",
icon: IconSettings,
},
component: () => import("@/views/System/SystemContainer.vue"),
},
{
path: "/loginLog",
name: "LoginLog",
path: "/sys",
name: "Sys",
meta: {
title: "登录日志",
icon: IconCalendar,
},
component: () => import("@/views/LoginLog.vue"),
},
{
path: "/sysAdmin",
name: "SysAdmin",
meta: {
title: "系统管理员",
title: "系统设置",
icon: IconRobot,
},
component: () => import("@/views/SysAdmin/SysAdminContainer.vue"),
},
{
path: "/sysPermission",
name: "SysPermission",
meta: {
title: "权限配置",
icon: IconSafe,
},
component: () => import("@/views/SysPermission/SysPermissionContainer.vue"),
redirect: () => system[0].path,
children: system
},
];
export default menu;

View File

@ -0,0 +1,37 @@
import type { RouteRecordRaw } from "vue-router";
const system: RouteRecordRaw[] = [
{
path: "admin",
name: "SysAdmin",
meta: {
title: "系统管理员",
},
component: () => import("@/views/SysAdmin/SysAdminContainer.vue"),
},
{
path: "permission",
name: "SysPermission",
meta: {
title: "权限配置",
},
component: () => import("@/views/SysPermission/SysPermissionContainer.vue"),
},
{
path: "role",
name: "SysRole",
meta: {
title: "角色管理",
},
component: () => import("@/views/SysRole/SysRoleContainer.vue"),
},
{
path: "loginLog",
name: "LoginLog",
meta: {
title: "登录日志",
},
component: () => import("@/views/LoginLog.vue"),
},
]
export default system

View File

@ -0,0 +1,78 @@
<script lang="ts" setup>
import { Message } from "@arco-design/web-vue";
import SearchTable from "@/components/SearchTable/SearchTable.vue";
import type { SearchTableColumns } from "@/components/SearchTable/type";
import usePopup from "@/composables/usePopup";
import SysRoleForm from "./SysRoleForm.vue";
import { getList, save, remove } from "./api";
const columns: SearchTableColumns[] = [
{
title: "角色名称",
dataIndex: "name",
search: {
valueType: "input",
},
},
{
title: "角色描述",
dataIndex: "description",
},
{
title: "创建时间",
dataIndex: "created_at",
},
{
title: "操作",
slotName: "actions",
width: 120,
},
];
const openFormModal = usePopup(SysRoleForm, {
nodeProps: ([record]) => ({ record }),
popupProps: ([record, reload], exposed) => ({
title: `${record?.id ? "编辑" : "新增"}角色`,
width: 800,
onBeforeOk: async (done) => {
await exposed()?.handleSubmit(save, {
id: record?.id,
});
Message.success("操作成功");
await reload();
done(true);
},
}),
});
const handleRemove = async (id, reload) => {
await remove({ id });
Message.success("删除成功");
await reload();
return true;
};
</script>
<template>
<SearchTable :request="getList" :columns="columns">
<template #header-option="{ reload }">
<a-button type="primary" @click="openFormModal({}, reload)">
<template #icon>
<icon-plus />
</template>
新增
</a-button>
</template>
<template #actions="{ record, reload }">
<a-link @click="openFormModal(record, reload)">编辑</a-link>
<a-popconfirm
content="是否删除?"
position="left"
type="warning"
:on-before-ok="() => handleRemove(record.id, reload)"
>
<a-link status="danger">删除</a-link>
</a-popconfirm>
</template>
</SearchTable>
</template>
./SysRoleForm.vue

View File

@ -0,0 +1,51 @@
<template>
<a-form ref="formRef" :model="formData" autoLabelWidth>
<a-formItem
label="角色名称"
field="name"
:rules="[{ required: true, message: '请输入角色名称' }]"
>
<a-input v-model="formData.name" />
</a-formItem>
<a-formItem
label="角色描述"
field="description"
:rules="[{ required: true, message: '请输入角色描述' }]"
>
<a-input v-model="formData.description" />
</a-formItem>
<a-formItem
label="平台权限"
field="permissions"
:rules="[{ required: true, message: '请选择平台权限' }]"
>
<a-tree
v-model:checked-keys="formData.permissions"
:data="options"
:field-names="{ key: 'id', title: 'name' }"
/>
</a-formItem>
</a-form>
</template>
<script lang="ts" setup>
import { getList } from "@/views/SysPermission/api";
import useRequest from "@/composables/useRequest";
import useSubmit from "@/composables/useSubmit";
const props = defineProps({
record: Object,
});
const [getOptions, options, loading] = useRequest(getList);
const { formRef, formData, handleSubmit } = useSubmit({
name: "",
description: "",
permissions: [],
});
Object.assign(formData, props.record);
getOptions();
defineExpose({ handleSubmit });
</script>

View File

@ -0,0 +1,25 @@
import http from "@/http/config";
export const getList = (params) => {
return http({
url: "/api/admin/sysRole/list",
method: "get",
params
})
}
export const save = (data) => {
return http({
url: "/api/admin/sysRole/save",
method: "post",
data
})
}
export const remove = (data) => {
return http({
url: "/api/admin/sysRole/remove",
method: "post",
data
})
}