mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	feat(ui): 新增系统分类菜单
This commit is contained in:
		
							
								
								
									
										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>;
 | 
			
		||||
  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;
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user