mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
feat(ui): 新增系统分类菜单
This commit is contained in:
parent
ec5ad809f2
commit
3866319373
8
new-ui/projects/admin/env.d.ts
vendored
8
new-ui/projects/admin/env.d.ts
vendored
@ -5,5 +5,13 @@ declare module "*.vue" {
|
|||||||
const component: DefineComponent<{}, {}, any>;
|
const component: DefineComponent<{}, {}, any>;
|
||||||
export default component;
|
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;
|
declare const __AUTH_KEY: string;
|
@ -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>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import showMenu from "@/router/menu";
|
import menu from "@/router/menu";
|
||||||
|
import { hasPermission } from "@/directives/permission";
|
||||||
defineProps({
|
defineProps({
|
||||||
width: {
|
width: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
@ -12,18 +49,16 @@ defineProps({
|
|||||||
});
|
});
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const goto = (name: string) => router.push({ name });
|
const goto = (name: string) => router.push({ name });
|
||||||
|
|
||||||
const selectedKeys = computed(() => [route.name]);
|
const selectedKeys = computed(() => [route.name]);
|
||||||
|
|
||||||
|
const showMenu = computed(() => menu.filter((item: any) => hasPermission(item.meta?.permission)));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ALayoutSider :style="{ width, height: '100%' }">
|
<ALayoutSider :style="{ width, height: '100%' }">
|
||||||
<AMenu :selected-keys="selectedKeys" @menu-item-click="goto">
|
<AMenu :selectedKeys="selectedKeys" auto-open-selected @menu-item-click="goto">
|
||||||
<AMenuItem v-for="item in showMenu" :key="item.name">
|
<CustomMenuItem :tree="showMenu" />
|
||||||
<template #icon>
|
|
||||||
<component :is="item.meta?.icon" />
|
|
||||||
</template>
|
|
||||||
{{ item.meta.title }}
|
|
||||||
</AMenuItem>
|
|
||||||
</AMenu>
|
</AMenu>
|
||||||
</ALayoutSider>
|
</ALayoutSider>
|
||||||
</template>
|
</template>
|
||||||
|
@ -4,13 +4,6 @@ import CustomLayout from '@/components/CustomLayout.vue'
|
|||||||
import { hasPermission } from "@/directives/permission";
|
import { hasPermission } from "@/directives/permission";
|
||||||
import menu from './menu'
|
import menu from './menu'
|
||||||
|
|
||||||
declare module 'vue-router' {
|
|
||||||
interface RouteMeta {
|
|
||||||
title?: string
|
|
||||||
permission?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const whiteListRoutes = [
|
const whiteListRoutes = [
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: "/login",
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
|
import type { RouteRecordRaw } from "vue-router";
|
||||||
import {
|
import {
|
||||||
IconUser,
|
IconUser,
|
||||||
IconDashboard,
|
IconDashboard,
|
||||||
IconOrderedList,
|
IconOrderedList,
|
||||||
IconCalendar,
|
|
||||||
IconHeartFill,
|
IconHeartFill,
|
||||||
|
IconCodeSandbox,
|
||||||
IconCodeSquare,
|
IconCodeSquare,
|
||||||
IconMessage,
|
IconMessage,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
IconUserGroup,
|
|
||||||
IconLock,
|
IconLock,
|
||||||
IconCodepen,
|
IconCodepen,
|
||||||
IconWechatpay,
|
IconWechatpay,
|
||||||
IconRobot,
|
IconRobot,
|
||||||
IconSafe,
|
|
||||||
} from "@arco-design/web-vue/es/icon";
|
} from "@arco-design/web-vue/es/icon";
|
||||||
|
|
||||||
const menu = [
|
import system from "./system";
|
||||||
|
|
||||||
|
const menu: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: "/dashboard",
|
path: "/dashboard",
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
@ -38,8 +39,8 @@ const menu = [
|
|||||||
path: "/role",
|
path: "/role",
|
||||||
name: "Role",
|
name: "Role",
|
||||||
meta: {
|
meta: {
|
||||||
title: "角色管理",
|
title: "角色模型",
|
||||||
icon: IconUserGroup,
|
icon: IconCodeSandbox,
|
||||||
},
|
},
|
||||||
component: () => import("@/views/Role/RoleContainer.vue"),
|
component: () => import("@/views/Role/RoleContainer.vue"),
|
||||||
},
|
},
|
||||||
@ -111,38 +112,22 @@ const menu = [
|
|||||||
path: "/system",
|
path: "/system",
|
||||||
name: "System",
|
name: "System",
|
||||||
meta: {
|
meta: {
|
||||||
title: "系统设置",
|
title: "网站设置",
|
||||||
icon: IconSettings,
|
icon: IconSettings,
|
||||||
},
|
},
|
||||||
component: () => import("@/views/System/SystemContainer.vue"),
|
component: () => import("@/views/System/SystemContainer.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/loginLog",
|
path: "/sys",
|
||||||
name: "LoginLog",
|
name: "Sys",
|
||||||
meta: {
|
meta: {
|
||||||
title: "登录日志",
|
title: "系统设置",
|
||||||
icon: IconCalendar,
|
|
||||||
},
|
|
||||||
component: () => import("@/views/LoginLog.vue"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/sysAdmin",
|
|
||||||
name: "SysAdmin",
|
|
||||||
meta: {
|
|
||||||
title: "系统管理员",
|
|
||||||
icon: IconRobot,
|
icon: IconRobot,
|
||||||
},
|
},
|
||||||
component: () => import("@/views/SysAdmin/SysAdminContainer.vue"),
|
redirect: () => system[0].path,
|
||||||
},
|
children: system
|
||||||
{
|
|
||||||
path: "/sysPermission",
|
|
||||||
name: "SysPermission",
|
|
||||||
meta: {
|
|
||||||
title: "权限配置",
|
|
||||||
icon: IconSafe,
|
|
||||||
},
|
|
||||||
component: () => import("@/views/SysPermission/SysPermissionContainer.vue"),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default menu;
|
export default menu;
|
||||||
|
37
new-ui/projects/admin/src/router/system.ts
Normal file
37
new-ui/projects/admin/src/router/system.ts
Normal 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
|
78
new-ui/projects/admin/src/views/SysRole/SysRoleContainer.vue
Normal file
78
new-ui/projects/admin/src/views/SysRole/SysRoleContainer.vue
Normal 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
|
51
new-ui/projects/admin/src/views/SysRole/SysRoleForm.vue
Normal file
51
new-ui/projects/admin/src/views/SysRole/SysRoleForm.vue
Normal 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>
|
25
new-ui/projects/admin/src/views/SysRole/api.ts
Normal file
25
new-ui/projects/admin/src/views/SysRole/api.ts
Normal 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
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user