添加菜单页面.
This commit is contained in:
31
orion-ops-ui/package-lock.json
generated
31
orion-ops-ui/package-lock.json
generated
@@ -10,6 +10,8 @@
|
|||||||
"license": "Apache 2.0",
|
"license": "Apache 2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@arco-design/web-vue": "^2.44.7",
|
"@arco-design/web-vue": "^2.44.7",
|
||||||
|
"@dangojs/a-query-header": "^0.0.31",
|
||||||
|
"@sanqi377/arco-vue-icon-picker": "^1.0.7",
|
||||||
"@vueuse/core": "^9.3.0",
|
"@vueuse/core": "^9.3.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"dayjs": "^1.11.5",
|
"dayjs": "^1.11.5",
|
||||||
@@ -1077,6 +1079,27 @@
|
|||||||
"postcss-selector-parser": "^6.0.10"
|
"postcss-selector-parser": "^6.0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@dangojs/a-query-header": {
|
||||||
|
"version": "0.0.31",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@dangojs/a-query-header/-/a-query-header-0.0.31.tgz",
|
||||||
|
"integrity": "sha512-hXLAU8oIQ1ULDY+qXKoKstqo3/m4HTjXcxNaFbPatTVMHwox4tBAIc5Nox1crvlWkNagUyk7PpnQuzLH73j9AQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@arco-design/web-vue": "^2.47.1",
|
||||||
|
"@dangojs/digitforce-ui-utils": "^0.0.9",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"vue": "^3.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dangojs/digitforce-ui-utils": {
|
||||||
|
"version": "0.0.9",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@dangojs/digitforce-ui-utils/-/digitforce-ui-utils-0.0.9.tgz",
|
||||||
|
"integrity": "sha512-ujmlm/dHteE0+EPuMWQLcGt68BtA4uiZYdlCvpc6tyYOw4cNWKZ94bleBuL+qjxzvnyj04aDpz1dq8wiyre7WQ==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"vue": "^3.2.47"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.15.18",
|
"version": "0.15.18",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
|
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
|
||||||
@@ -1457,6 +1480,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sanqi377/arco-vue-icon-picker": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@sanqi377/arco-vue-icon-picker/-/arco-vue-icon-picker-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-KxafhphhK63UW/cNb7I/aowDFhRyu5UvxQkrs60z/EaEbAqHgRIxOJKIaPmm1K6K5moU8GSMY3u6DdfB4aiDRg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sindresorhus/is": {
|
"node_modules/@sindresorhus/is": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.7.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.7.0.tgz",
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@arco-design/web-vue": "^2.44.7",
|
"@arco-design/web-vue": "^2.44.7",
|
||||||
|
"@dangojs/a-query-header": "^0.0.31",
|
||||||
|
"@sanqi377/arco-vue-icon-picker": "^1.0.7",
|
||||||
"@vueuse/core": "^9.3.0",
|
"@vueuse/core": "^9.3.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"dayjs": "^1.11.5",
|
"dayjs": "^1.11.5",
|
||||||
|
|||||||
78
orion-ops-ui/src/api/system/menu.ts
Normal file
78
orion-ops-ui/src/api/system/menu.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单创建请求
|
||||||
|
*/
|
||||||
|
export interface MenuCreateRequest {
|
||||||
|
parentId?: number;
|
||||||
|
name?: string;
|
||||||
|
permission?: string;
|
||||||
|
type?: number;
|
||||||
|
sort?: number;
|
||||||
|
cache?: number;
|
||||||
|
icon?: string;
|
||||||
|
path?: string;
|
||||||
|
component?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单更新请求
|
||||||
|
*/
|
||||||
|
export interface MenuUpdateRequest extends MenuCreateRequest {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单查询请求
|
||||||
|
*/
|
||||||
|
export interface MenuQueryRequest {
|
||||||
|
name?: string;
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单查询响应
|
||||||
|
*/
|
||||||
|
export interface MenuQueryResponse {
|
||||||
|
id?: number;
|
||||||
|
parentId?: number;
|
||||||
|
name?: string;
|
||||||
|
permission?: string;
|
||||||
|
type?: number;
|
||||||
|
sort?: number;
|
||||||
|
visible?: number;
|
||||||
|
status?: number;
|
||||||
|
cache?: number;
|
||||||
|
icon?: string;
|
||||||
|
path?: string;
|
||||||
|
component?: string;
|
||||||
|
children?: Array<MenuQueryResponse>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询菜单列表
|
||||||
|
*/
|
||||||
|
export function getMenuList(request?: MenuQueryRequest) {
|
||||||
|
return axios.post<MenuQueryResponse[]>('/infra/system-menu/list', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建菜单
|
||||||
|
*/
|
||||||
|
export function createMenu(request: MenuCreateRequest) {
|
||||||
|
return axios.post('/infra/system-menu/create', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改菜单
|
||||||
|
*/
|
||||||
|
export function updateMenu(request: MenuUpdateRequest) {
|
||||||
|
return axios.put<MenuQueryResponse[]>('/infra/system-menu/update', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除菜单
|
||||||
|
*/
|
||||||
|
export function deleteMenu(id: number) {
|
||||||
|
return axios.delete<MenuQueryResponse[]>('/infra/system-menu/delete', { params: { id } });
|
||||||
|
}
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { UserState } from '@/store/modules/user/types';
|
import { UserState } from '@/store/modules/user/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登陆请求
|
||||||
|
*/
|
||||||
export interface LoginRequest {
|
export interface LoginRequest {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登陆响应
|
||||||
|
*/
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.echarts-tooltip-diy {
|
.echarts-tooltip-diy {
|
||||||
background: linear-gradient(
|
background: linear-gradient(304.17deg,
|
||||||
304.17deg,
|
|
||||||
rgba(253, 254, 255, 0.6) -6.04%,
|
rgba(253, 254, 255, 0.6) -6.04%,
|
||||||
rgba(244, 247, 252, 0.6) 85.2%
|
rgba(244, 247, 252, 0.6) 85.2%) !important;
|
||||||
) !important;
|
|
||||||
border: none !important;
|
border: none !important;
|
||||||
backdrop-filter: blur(10px) !important;
|
backdrop-filter: blur(10px) !important;
|
||||||
/* Note: backdrop-filter has minimal browser support */
|
/* Note: backdrop-filter has minimal browser support */
|
||||||
@@ -75,12 +73,20 @@ body {
|
|||||||
|
|
||||||
& > .arco-card-header {
|
& > .arco-card-header {
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 20px;
|
padding: 16px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .arco-card-body {
|
& > .arco-card-body {
|
||||||
padding: 0 20px 20px 20px;
|
padding: 0 16px 16px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.a-query-header-grid {
|
||||||
|
.arco-grid-item {
|
||||||
|
&:last-child {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,102 @@
|
|||||||
background-color: var(--color-fill-2);
|
background-color: var(--color-fill-2);
|
||||||
padding: 16px 16px 0 16px;
|
padding: 16px 16px 0 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-search-card {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-option-wrapper {
|
||||||
|
.arco-btn-text {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml4 {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr4 {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt4 {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb4 {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx4 {
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my4 {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml8 {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr8 {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt8 {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb8 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx8 {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my8 {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml16 {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr16 {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt16 {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb16 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx16 {
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my16 {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { App } from 'vue';
|
import { App } from 'vue';
|
||||||
import { use } from 'echarts/core';
|
import { use } from 'echarts/core';
|
||||||
|
import AQueryHeader from '@dangojs/a-query-header';
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts';
|
import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts';
|
||||||
import {
|
import {
|
||||||
@@ -29,5 +30,6 @@ export default {
|
|||||||
install(Vue: App) {
|
install(Vue: App) {
|
||||||
Vue.component('Chart', Chart);
|
Vue.component('Chart', Chart);
|
||||||
Vue.component('Breadcrumb', Breadcrumb);
|
Vue.component('Breadcrumb', Breadcrumb);
|
||||||
|
Vue.component('a-query-header', AQueryHeader);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default function useUser() {
|
|||||||
await userStore.logout();
|
await userStore.logout();
|
||||||
const currentRoute = router.currentRoute.value;
|
const currentRoute = router.currentRoute.value;
|
||||||
Message.success('已退出登录');
|
Message.success('已退出登录');
|
||||||
router.push({
|
await router.push({
|
||||||
name: logoutTo || 'login',
|
name: logoutTo || 'login',
|
||||||
query: {
|
query: {
|
||||||
...router.currentRoute.value.query,
|
...router.currentRoute.value.query,
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const REDIRECT_ROUTER: RouteRecordRaw = {
|
|||||||
export const FORBIDDEN_ROUTE: RouteRecordRaw = {
|
export const FORBIDDEN_ROUTE: RouteRecordRaw = {
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
name: FORBIDDEN_ROUTER_NAME,
|
name: FORBIDDEN_ROUTER_NAME,
|
||||||
component: () => import('@/views/base/forbidden/index.vue'),
|
component: () => import('@/views/exception/forbidden/index.vue'),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,7 +64,7 @@ export const FORBIDDEN_ROUTE: RouteRecordRaw = {
|
|||||||
export const NOT_FOUND_ROUTE: RouteRecordRaw = {
|
export const NOT_FOUND_ROUTE: RouteRecordRaw = {
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
name: NOT_FOUND_ROUTER_NAME,
|
name: NOT_FOUND_ROUTER_NAME,
|
||||||
component: () => import('@/views/base/not-found/index.vue'),
|
component: () => import('@/views/exception/not-found/index.vue'),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
|||||||
17
orion-ops-ui/src/router/routes/modules/system.menu.ts
Normal file
17
orion-ops-ui/src/router/routes/modules/system.menu.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
|
import { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
|
const MENU: AppRouteRecordRaw = {
|
||||||
|
name: 'menu',
|
||||||
|
path: '/system',
|
||||||
|
component: DEFAULT_LAYOUT,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'systemMenu',
|
||||||
|
path: '/system/menu',
|
||||||
|
component: () => import('@/views/system/menu/index.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MENU;
|
||||||
@@ -2,13 +2,15 @@ import { createPinia } from 'pinia';
|
|||||||
import useAppStore from './modules/app';
|
import useAppStore from './modules/app';
|
||||||
import useUserStore from './modules/user';
|
import useUserStore from './modules/user';
|
||||||
import useTabBarStore from './modules/tab-bar';
|
import useTabBarStore from './modules/tab-bar';
|
||||||
|
import useMenuStore from './modules/system/menu';
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useAppStore,
|
useAppStore,
|
||||||
useUserStore,
|
useUserStore,
|
||||||
useTabBarStore
|
useTabBarStore,
|
||||||
|
useMenuStore
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pinia;
|
export default pinia;
|
||||||
|
|||||||
80
orion-ops-ui/src/store/modules/system/menu/index.ts
Normal file
80
orion-ops-ui/src/store/modules/system/menu/index.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { MenuState } from './types';
|
||||||
|
import { MenuQueryResponse } from '@/api/system/menu';
|
||||||
|
import { TreeNodeData } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
const useMenuStore = defineStore('menu', {
|
||||||
|
state: (): MenuState => ({
|
||||||
|
menus: []
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
menuState(state: MenuState): MenuState {
|
||||||
|
return { ...state };
|
||||||
|
},
|
||||||
|
treeData(state: MenuState): TreeNodeData[] {
|
||||||
|
let render = (arr: any[]): TreeNodeData[] => {
|
||||||
|
return arr.map((s) => {
|
||||||
|
// 非 function
|
||||||
|
if (s.type === 3) {
|
||||||
|
return null as unknown as TreeNodeData;
|
||||||
|
}
|
||||||
|
// 当前节点
|
||||||
|
const node = {
|
||||||
|
key: s.id,
|
||||||
|
title: s.name,
|
||||||
|
children: undefined as unknown
|
||||||
|
} as TreeNodeData;
|
||||||
|
// 子节点
|
||||||
|
if (s.children && s.children.length) {
|
||||||
|
node.children = render(s.children);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}).filter(Boolean);
|
||||||
|
};
|
||||||
|
return render([{ name: '根目录', id: 0 }, ...state.menus]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* 更新菜单
|
||||||
|
*/
|
||||||
|
updateMenu(menus: MenuQueryResponse[]) {
|
||||||
|
this.menus = menus;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取父菜单
|
||||||
|
*/
|
||||||
|
findParentMenu(arr: any, id: number): any {
|
||||||
|
if (!arr || !arr.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 当前级
|
||||||
|
for (let e of arr) {
|
||||||
|
if (e.id === id) {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 子级
|
||||||
|
for (let e of arr) {
|
||||||
|
if (e.children && e.children.length) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (this.findParent(e.children, id) !== null) {
|
||||||
|
return e.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空菜单
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
this.menus = [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useMenuStore;
|
||||||
7
orion-ops-ui/src/store/modules/system/menu/types.ts
Normal file
7
orion-ops-ui/src/store/modules/system/menu/types.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { MenuQueryResponse } from '@/api/system/menu';
|
||||||
|
|
||||||
|
export interface MenuState {
|
||||||
|
menus: MenuQueryResponse[],
|
||||||
|
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
@@ -24,8 +24,11 @@ export interface PostData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Pagination {
|
export interface Pagination {
|
||||||
page: number;
|
page?: number;
|
||||||
limit: number;
|
limit?: number;
|
||||||
|
current?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
total?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TimeRanger = [string, string];
|
export type TimeRanger = [string, string];
|
||||||
|
|||||||
37
orion-ops-ui/src/utils/enum.ts
Normal file
37
orion-ops-ui/src/utils/enum.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 转为 select options
|
||||||
|
*/
|
||||||
|
export const toOptions = (enums: any) => {
|
||||||
|
const arr = [];
|
||||||
|
for (let k in enums) {
|
||||||
|
arr.push(enums[k]);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取枚举值
|
||||||
|
*/
|
||||||
|
export const getEnumValue = (value: any,
|
||||||
|
enums: any,
|
||||||
|
key = 'label',
|
||||||
|
defaultValue = value) => {
|
||||||
|
for (let k in enums) {
|
||||||
|
if (enums[k].value === value) {
|
||||||
|
return enums[k][key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取枚举对象
|
||||||
|
*/
|
||||||
|
export const getEnum = (value: any, enums: any) => {
|
||||||
|
for (let k in enums) {
|
||||||
|
if (enums[k].value === value) {
|
||||||
|
return enums[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="content">
|
|
||||||
<a-result class="result" status="404" subtitle="not found"></a-result>
|
|
||||||
<div class="operation-row">
|
|
||||||
<a-button key="back" type="primary" @click="back"> 返回工作台</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const back = () => {
|
|
||||||
router.push({ name: 'workplace' });
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.content {
|
|
||||||
// padding-top: 100px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -95px;
|
|
||||||
margin-top: -121px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
36
orion-ops-ui/src/views/exception/forbidden/index.vue
Normal file
36
orion-ops-ui/src/views/exception/forbidden/index.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content">
|
||||||
|
<a-result class="result" status="403" subtitle="您没有访问该资源的权限" />
|
||||||
|
<div class="operation-row">
|
||||||
|
<a-button class="mr8" key="back" type="primary" @click="to('login')">重新登录</a-button>
|
||||||
|
<a-button key="back" type="primary" @click="to('workplace')">返回工作台</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const to = (name: string) => {
|
||||||
|
router.push({ name: name });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'forbidden',
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.content {
|
||||||
|
// padding-top: 100px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -95px;
|
||||||
|
margin-top: -121px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<a-result class="result" status="403" subtitle="forbidden"></a-result>
|
<a-result class="result" status="404" subtitle="糟糕! 页面不见了!" />
|
||||||
<div class="operation-row">
|
<div class="operation-row">
|
||||||
<a-button key="back" type="primary" @click="back"> 返回工作台</a-button>
|
<a-button class="mr8" key="back" type="primary" @click="to('login')">重新登录</a-button>
|
||||||
|
<a-button key="back" type="primary" @click="to('workplace')">返回工作台</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -11,8 +12,14 @@
|
|||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const back = () => {
|
const to = (name: string) => {
|
||||||
router.push({ name: 'workplace' });
|
router.push({ name: name });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'not-found',
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal v-model:visible="visible"
|
||||||
|
title-align="start"
|
||||||
|
:title="title"
|
||||||
|
:top="80"
|
||||||
|
:align-center="false"
|
||||||
|
:draggable="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
:ok-button-props="{ disabled: loading }"
|
||||||
|
:cancel-button-props="{ disabled: loading }"
|
||||||
|
:on-before-ok="handlerOk"
|
||||||
|
@close="handleClose">
|
||||||
|
<a-spin :loading="loading">
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="right"
|
||||||
|
:style="{ width: '440px' }"
|
||||||
|
:label-col-props="{ span: 5 }"
|
||||||
|
:wrapper-col-props="{ span: 19 }"
|
||||||
|
:rules="formRules">
|
||||||
|
<!-- 上级菜单 -->
|
||||||
|
<a-form-item field="parentId" label="上级菜单">
|
||||||
|
<menu-tree-selector v-model="formModel.parentId"
|
||||||
|
:disabled="!!(isAddHandle && formModel.id)" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 名称 -->
|
||||||
|
<a-form-item field="name" label="菜单名称">
|
||||||
|
<a-input v-model="formModel.name" placeholder="请输入菜单名称" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 菜单类型 -->
|
||||||
|
<a-form-item field="type" label="菜单类型">
|
||||||
|
<a-radio-group type="button"
|
||||||
|
v-model="formModel.type"
|
||||||
|
:options="toOptions(MenuTypeEnum)" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 菜单图标 -->
|
||||||
|
<a-form-item field="icon" label="菜单图标">
|
||||||
|
<icon-picker v-model:icon="formModel.icon">
|
||||||
|
<template #iconSelect>
|
||||||
|
<a-input v-model="formModel.icon" placeholder="请选择菜单图标" />
|
||||||
|
</template>
|
||||||
|
</icon-picker>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 菜单权限 -->
|
||||||
|
<a-form-item field="permission" label="菜单权限">
|
||||||
|
<a-input v-model="formModel.permission"
|
||||||
|
placeholder="菜单权限 infra:system-menu:query"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 外链地址 -->
|
||||||
|
<a-form-item field="path" label="外链地址">
|
||||||
|
<a-input v-model="formModel.path"
|
||||||
|
placeholder="外链地址与组件名称二选一"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 组件名称 -->
|
||||||
|
<a-form-item field="component" label="组件名称">
|
||||||
|
<a-input v-model="formModel.component"
|
||||||
|
placeholder="路由组件名称"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 菜单排序 -->
|
||||||
|
<a-form-item field="sort" label="菜单排序">
|
||||||
|
<a-input-number v-model="formModel.sort"
|
||||||
|
placeholder="排序"
|
||||||
|
:style="{ width: '120px' }"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 可见状态 -->
|
||||||
|
<a-form-item field="type" label="可见状态">
|
||||||
|
<a-radio-group type="button"
|
||||||
|
v-model="formModel.visible"
|
||||||
|
:options="toOptions(MenuVisibleEnum)" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 缓存状态 -->
|
||||||
|
<a-form-item field="type" label="缓存状态">
|
||||||
|
<a-radio-group type="button"
|
||||||
|
v-model="formModel.cache"
|
||||||
|
:options="toOptions(MenuCacheEnum)" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'menu-form-modal'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import useVisible from '@/hooks/visible';
|
||||||
|
import formRules from '../type/form.rules';
|
||||||
|
import { MenuTypeEnum, MenuVisibleEnum, MenuCacheEnum } from '../type/enum.types';
|
||||||
|
import { toOptions } from '@/utils/enum';
|
||||||
|
import IconPicker from '@sanqi377/arco-vue-icon-picker';
|
||||||
|
import MenuTreeSelector from '@/views/system/menu/components/menu-tree-selector.vue';
|
||||||
|
import { createMenu, updateMenu } from '@/api/system/menu';
|
||||||
|
|
||||||
|
const { visible, setVisible } = useVisible();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
|
||||||
|
const title = ref<string>();
|
||||||
|
const isAddHandle = ref<boolean>(true);
|
||||||
|
|
||||||
|
const formRef = ref<any>();
|
||||||
|
const formModel = reactive<Record<string, any>>({
|
||||||
|
id: undefined,
|
||||||
|
parentId: undefined,
|
||||||
|
name: undefined,
|
||||||
|
type: undefined,
|
||||||
|
permission: undefined,
|
||||||
|
sort: undefined,
|
||||||
|
visible: undefined,
|
||||||
|
cache: undefined,
|
||||||
|
icon: undefined,
|
||||||
|
path: undefined,
|
||||||
|
component: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['added', 'updated']);
|
||||||
|
|
||||||
|
// 打开新增
|
||||||
|
const openAdd = (record: any) => {
|
||||||
|
title.value = '添加菜单';
|
||||||
|
isAddHandle.value = true;
|
||||||
|
renderForm(record);
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开修改
|
||||||
|
const openUpdate = (record: any) => {
|
||||||
|
title.value = '修改菜单';
|
||||||
|
isAddHandle.value = false;
|
||||||
|
renderForm(record);
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ openAdd, openUpdate });
|
||||||
|
|
||||||
|
// 渲染表单
|
||||||
|
const renderForm = (record: any) => {
|
||||||
|
Object.keys(formModel).forEach(k => {
|
||||||
|
formModel[k] = record[k];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
const handlerOk = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 验证参数
|
||||||
|
const error = await formRef.value.validate();
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isAddHandle.value) {
|
||||||
|
// 新增
|
||||||
|
await createMenu(formModel as any);
|
||||||
|
emits('added', { ...formModel });
|
||||||
|
} else {
|
||||||
|
// 修改
|
||||||
|
await updateMenu(formModel as any);
|
||||||
|
emits('updated', { ...formModel });
|
||||||
|
}
|
||||||
|
// 清空
|
||||||
|
// handlerClear();
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
const handleClose = () => {
|
||||||
|
handlerClear();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
const handlerClear = () => {
|
||||||
|
setLoading(false);
|
||||||
|
setVisible(false);
|
||||||
|
renderForm({});
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.arco-form) {
|
||||||
|
&-item {
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
249
orion-ops-ui/src/views/system/menu/components/menu-table.vue
Normal file
249
orion-ops-ui/src/views/system/menu/components/menu-table.vue
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 搜索 -->
|
||||||
|
<a-card class="general-card table-search-card">
|
||||||
|
<a-row style="margin-top: 16px">
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="left"
|
||||||
|
@keydown.enter="loadMenuData">
|
||||||
|
<a-row :gutter="32">
|
||||||
|
<!-- 菜单名称 -->
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item field="name" label="菜单名称" label-col-flex="60px">
|
||||||
|
<a-input v-model="formModel.name" placeholder="请输入菜单名称" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<!-- 菜单状态 -->
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item field="status" label="菜单状态" label-col-flex="60px">
|
||||||
|
<a-select
|
||||||
|
v-model="formModel.status"
|
||||||
|
:options="toOptions(MenuStatusEnum)"
|
||||||
|
placeholder="请选择菜单状态"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-col>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<a-col :span="12" class="form-option">
|
||||||
|
<a-space>
|
||||||
|
<!-- 新增 -->
|
||||||
|
<a-button type="primary"
|
||||||
|
@click="emits('openAdd',{ parentId: 0 })"
|
||||||
|
v-permission="['infra:system-menu:create']">
|
||||||
|
新增
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<!-- 查询 -->
|
||||||
|
<a-button type="primary" :loading="fetchLoading" @click="loadMenuData">
|
||||||
|
查询
|
||||||
|
<template #icon>
|
||||||
|
<icon-search />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<!-- 重置 -->
|
||||||
|
<a-button @click="resetForm" :loading="fetchLoading">
|
||||||
|
重置
|
||||||
|
<template #icon>
|
||||||
|
<icon-refresh />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<!-- 展开 -->
|
||||||
|
<a-button @click="toggleExpand">
|
||||||
|
折叠/展开
|
||||||
|
<template #icon>
|
||||||
|
<icon-expand />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
<!-- 表格 -->
|
||||||
|
<a-card class="general-card table-card">
|
||||||
|
<a-table row-key="id"
|
||||||
|
class="table-wrapper"
|
||||||
|
ref="tableRef"
|
||||||
|
label-align="left"
|
||||||
|
:loading="fetchLoading"
|
||||||
|
:pagination="false"
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableRenderData"
|
||||||
|
:bordered="false">
|
||||||
|
<!-- 菜单名称 -->
|
||||||
|
<template #menuName="{ record }">
|
||||||
|
<span class="ml8">{{ record.name }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 图标 -->
|
||||||
|
<template #icon="{ record }">
|
||||||
|
<component v-if="record.icon" :is="record.icon" />
|
||||||
|
</template>
|
||||||
|
<!-- 类型 -->
|
||||||
|
<template #type="{ record }">
|
||||||
|
<a-tag>{{ getEnumValue(record.type, MenuTypeEnum) }}</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 状态 -->
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<!-- 菜单状态 -->
|
||||||
|
<a-tag :color="getEnumValue(record.status, MenuStatusEnum,'color')">
|
||||||
|
{{ getEnumValue(record.status, MenuStatusEnum) }}
|
||||||
|
</a-tag>
|
||||||
|
<!-- 显示状态 -->
|
||||||
|
<a-tag :color="getEnumValue(record.visible, MenuVisibleEnum,'color')">
|
||||||
|
{{ getEnumValue(record.visible, MenuVisibleEnum) }}
|
||||||
|
</a-tag>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<template #option="{ record }">
|
||||||
|
<div class="table-option-wrapper">
|
||||||
|
<!-- 新增 -->
|
||||||
|
<a-button type="text"
|
||||||
|
size="mini"
|
||||||
|
v-if="record.type !== MenuTypeEnum.FUNCTION.value"
|
||||||
|
v-permission="['infra:system-menu:create']"
|
||||||
|
@click="emits('openAdd', { id: record.id, parentId: record.id })">
|
||||||
|
新增
|
||||||
|
</a-button>
|
||||||
|
<!-- 修改 -->
|
||||||
|
<a-button type="text"
|
||||||
|
size="mini"
|
||||||
|
v-permission="['infra:system-menu:update']"
|
||||||
|
@click="emits('openUpdate', record)">
|
||||||
|
修改
|
||||||
|
</a-button>
|
||||||
|
<!-- 删除 -->
|
||||||
|
<a-popconfirm content="确认删除这条记录吗?"
|
||||||
|
position="left"
|
||||||
|
type="warning"
|
||||||
|
@ok="doDeleteMenu(record)">
|
||||||
|
<a-button type="text" size="mini"
|
||||||
|
status="danger"
|
||||||
|
v-permission="['infra:system-menu:delete']">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'menu-table'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, onUnmounted } from 'vue';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import { getMenuList, deleteMenu, MenuQueryResponse } from '@/api/system/menu';
|
||||||
|
import { toOptions, getEnumValue } from '@/utils/enum';
|
||||||
|
import { MenuStatusEnum, MenuVisibleEnum, MenuTypeEnum } from '../type/enum.types';
|
||||||
|
import columns from '../type/table.columns';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import { useMenuStore } from '@/store';
|
||||||
|
|
||||||
|
const menuStore = useMenuStore();
|
||||||
|
|
||||||
|
const formRef = ref<any>();
|
||||||
|
const formModel = reactive({
|
||||||
|
name: undefined,
|
||||||
|
status: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableRef = ref<any>();
|
||||||
|
const expandStatus = ref<boolean>(false);
|
||||||
|
|
||||||
|
const tableRenderData = ref<MenuQueryResponse[]>([]);
|
||||||
|
const { loading: fetchLoading, setLoading: setFetchLoading } = useLoading();
|
||||||
|
|
||||||
|
const emits = defineEmits(['openAdd', 'openUpdate']);
|
||||||
|
|
||||||
|
// 删除菜单
|
||||||
|
const doDeleteMenu = async ({ id }: any) => {
|
||||||
|
try {
|
||||||
|
setFetchLoading(true);
|
||||||
|
// 调用删除接口
|
||||||
|
await deleteMenu(id);
|
||||||
|
// 获取父级容器
|
||||||
|
const parent = menuStore.findParentMenu(tableRenderData.value, id) as unknown as MenuQueryResponse[];
|
||||||
|
if (parent) {
|
||||||
|
// 删除
|
||||||
|
for (let i = 0; i < parent.length; i++) {
|
||||||
|
if (parent[i].id === id) {
|
||||||
|
parent.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message.success({ content: '删除成功' });
|
||||||
|
} finally {
|
||||||
|
setFetchLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加后回调
|
||||||
|
const addedCallback = (record: any) => {
|
||||||
|
console.log('addCallback', record);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新后回调
|
||||||
|
const updatedCallback = (record: any) => {
|
||||||
|
console.log('updateCallback', record);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
addedCallback, updatedCallback
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载菜单
|
||||||
|
const loadMenuData = async () => {
|
||||||
|
try {
|
||||||
|
setFetchLoading(true);
|
||||||
|
const { data } = await getMenuList(formModel);
|
||||||
|
tableRenderData.value = data as MenuQueryResponse[];
|
||||||
|
menuStore.updateMenu(tableRenderData.value);
|
||||||
|
} finally {
|
||||||
|
setFetchLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadMenuData();
|
||||||
|
|
||||||
|
// 重置菜单
|
||||||
|
const resetForm = () => {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
loadMenuData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换展开/折叠
|
||||||
|
const toggleExpand = () => {
|
||||||
|
tableRef.value.expandAll(expandStatus.value = !expandStatus.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 卸载时清除 store
|
||||||
|
onUnmounted(() => {
|
||||||
|
menuStore.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.form-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-form-item) {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<a-tree-select :default-value="modelValue"
|
||||||
|
:data="treeData"
|
||||||
|
:disabled="disabled"
|
||||||
|
:allow-search="true"
|
||||||
|
:filter-tree-node="filterTreeNode"
|
||||||
|
placeholder="请选择上级菜单"
|
||||||
|
@change="onChange" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'menu-tree-selector'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useMenuStore } from '@/store';
|
||||||
|
|
||||||
|
defineProps(['modelValue', 'disabled']);
|
||||||
|
const emits = defineEmits(['update:modelValue']);
|
||||||
|
const onChange = (e: any) => {
|
||||||
|
emits('update:modelValue', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树数据
|
||||||
|
const menuStore = useMenuStore();
|
||||||
|
const treeData = menuStore.treeData;
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const filterTreeNode = (searchValue: string, nodeData: { title: string; }) => {
|
||||||
|
return nodeData.title.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>;
|
||||||
28
orion-ops-ui/src/views/system/menu/index.vue
Normal file
28
orion-ops-ui/src/views/system/menu/index.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div class="layout-container">
|
||||||
|
<!-- 表格 -->
|
||||||
|
<menu-table ref="table"
|
||||||
|
@openAdd="(e) => modal.openAdd(e)"
|
||||||
|
@openUpdate="(e) => modal.openUpdate(e)" />
|
||||||
|
<!-- 添加修改框 -->
|
||||||
|
<menu-form-modal ref="modal"
|
||||||
|
@added="(e) => table.addedCallback(e)"
|
||||||
|
@updated="(e) => table.updatedCallback(e)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'systemMenu'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import MenuTable from '@/views/system/menu/components/menu-table.vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import MenuFormModal from '@/views/system/menu/components/menu-form-modal.vue';
|
||||||
|
|
||||||
|
const table = ref<any>();
|
||||||
|
const modal = ref<any>();
|
||||||
|
|
||||||
|
</script>
|
||||||
63
orion-ops-ui/src/views/system/menu/type/enum.types.ts
Normal file
63
orion-ops-ui/src/views/system/menu/type/enum.types.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* 菜单类型
|
||||||
|
*/
|
||||||
|
export const MenuTypeEnum = {
|
||||||
|
PARENT_MENU: {
|
||||||
|
value: 1,
|
||||||
|
label: '父菜单'
|
||||||
|
},
|
||||||
|
SUB_MENU: {
|
||||||
|
value: 2,
|
||||||
|
label: '子菜单'
|
||||||
|
},
|
||||||
|
FUNCTION: {
|
||||||
|
value: 3,
|
||||||
|
label: '功能'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单状态
|
||||||
|
*/
|
||||||
|
export const MenuStatusEnum = {
|
||||||
|
DISABLED: {
|
||||||
|
value: 0,
|
||||||
|
label: '停用',
|
||||||
|
color: 'orange'
|
||||||
|
},
|
||||||
|
ENABLED: {
|
||||||
|
value: 1,
|
||||||
|
label: '启用',
|
||||||
|
color: 'blue'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单是否可见
|
||||||
|
*/
|
||||||
|
export const MenuVisibleEnum = {
|
||||||
|
HIDE: {
|
||||||
|
value: 0,
|
||||||
|
label: '隐藏',
|
||||||
|
color: 'orange'
|
||||||
|
},
|
||||||
|
SHOW: {
|
||||||
|
value: 1,
|
||||||
|
label: '显示',
|
||||||
|
color: 'blue'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单缓存状态
|
||||||
|
*/
|
||||||
|
export const MenuCacheEnum = {
|
||||||
|
HIDE: {
|
||||||
|
value: 0,
|
||||||
|
label: '不缓存',
|
||||||
|
},
|
||||||
|
SHOW: {
|
||||||
|
value: 1,
|
||||||
|
label: '缓存',
|
||||||
|
}
|
||||||
|
};
|
||||||
11
orion-ops-ui/src/views/system/menu/type/form.rules.ts
Normal file
11
orion-ops-ui/src/views/system/menu/type/form.rules.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { FieldRule } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
export const name = [{
|
||||||
|
required: true,
|
||||||
|
|
||||||
|
}] as FieldRule[];
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name
|
||||||
|
} as Record<string, FieldRule | FieldRule[]>;
|
||||||
58
orion-ops-ui/src/views/system/menu/type/table.columns.ts
Normal file
58
orion-ops-ui/src/views/system/menu/type/table.columns.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '菜单名称',
|
||||||
|
dataIndex: 'menuName',
|
||||||
|
slotName: 'menuName',
|
||||||
|
fixed: 'left',
|
||||||
|
width: 250,
|
||||||
|
}, {
|
||||||
|
title: '图标',
|
||||||
|
dataIndex: 'icon',
|
||||||
|
slotName: 'icon',
|
||||||
|
align: 'center',
|
||||||
|
width: 60,
|
||||||
|
}, {
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
slotName: 'type',
|
||||||
|
width: 80,
|
||||||
|
}, {
|
||||||
|
title: '排序',
|
||||||
|
dataIndex: 'sort',
|
||||||
|
slotName: 'sort',
|
||||||
|
width: 70,
|
||||||
|
}, {
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
slotName: 'status',
|
||||||
|
width: 120,
|
||||||
|
}, {
|
||||||
|
title: '权限标识',
|
||||||
|
dataIndex: 'permission',
|
||||||
|
slotName: 'permission',
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true
|
||||||
|
}, {
|
||||||
|
title: '组件名称',
|
||||||
|
dataIndex: 'component',
|
||||||
|
slotName: 'component',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true,
|
||||||
|
}, {
|
||||||
|
title: '链接路径',
|
||||||
|
dataIndex: 'path',
|
||||||
|
slotName: 'path',
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true,
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'option',
|
||||||
|
width: 158,
|
||||||
|
fixed: 'right',
|
||||||
|
}
|
||||||
|
] as TableColumnData[];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
Reference in New Issue
Block a user