feat(全局): 命名错误调整&搜索选择器组件&系统/组织/项目日志&部分组件调整
This commit is contained in:
parent
2e689901e2
commit
e4d6864afd
|
@ -486,7 +486,7 @@ export enum ContentTypeEnum {
|
||||||
|
|
||||||
## -locale
|
## -locale
|
||||||
|
|
||||||
国际化模块,存放项目声明的国际化配置,按语种划分模块。模块入口文件为`index.ts`,负责定义菜单、导航栏等公共的国际化配置,其他的按系统功能声明并导入`index.ts`,还有项目内页面组件的国际化配置也需要导入至`index.ts`,页面组件的国际化配置在页面的目录下声明,如:`views/dashboard/workplace/locale`。
|
国际化模块,存放项目声明的国际化配置,按语种划分模块。模块入口文件为`index.ts`,负责定义菜单、导航栏等公共的国际化配置,其他的按系统功能声明并导入`index.ts`,还有项目内页面组件的国际化配置也需要导入至`index.ts`,页面组件的国际化配置在页面的目录下声明,如:`views/dashboard/workbench/locale`。
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import MSR from '@/api/http/index';
|
import MSR from '@/api/http/index';
|
||||||
import { GetLogListUrl, GetLogOptionstUrl } from '@/api/requrls/setting/log';
|
import { GetLogListUrl, GetLogOptionsUrl, GetLogUserUrl } from '@/api/requrls/setting/log';
|
||||||
|
|
||||||
import type { CommonList } from '@/models/common';
|
import type { CommonList } from '@/models/common';
|
||||||
import type { LogOptions, LogItem } from '@/models/setting/log';
|
import type { LogOptions, LogItem, UserItem } from '@/models/setting/log';
|
||||||
|
|
||||||
// 获取日志列表
|
// 获取日志列表
|
||||||
export function getLogList(data: any) {
|
export function getLogList(data: any) {
|
||||||
|
@ -11,5 +11,10 @@ export function getLogList(data: any) {
|
||||||
|
|
||||||
// 获取日志操作范围选项
|
// 获取日志操作范围选项
|
||||||
export function getLogOptions() {
|
export function getLogOptions() {
|
||||||
return MSR.get<LogOptions>({ url: GetLogOptionstUrl });
|
return MSR.get<LogOptions>({ url: GetLogOptionsUrl });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取日志操作用户列表
|
||||||
|
export function getLogUsers() {
|
||||||
|
return MSR.get<UserItem[]>({ url: GetLogUserUrl });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export const GetLogListUrl = '/operation/log/list';
|
export const GetLogListUrl = '/operation/log/list';
|
||||||
export const GetLogOptionstUrl = '/operation/log/get/options';
|
export const GetLogOptionsUrl = '/operation/log/get/options'; // 获取组织/项目级联下拉框选项
|
||||||
|
export const GetLogUserUrl = '/operation/log/user/list'; // 搜索操作用户
|
||||||
|
|
|
@ -89,6 +89,7 @@
|
||||||
multiple?: boolean; // 是否多选
|
multiple?: boolean; // 是否多选
|
||||||
strictly?: boolean; // 是否严格模式
|
strictly?: boolean; // 是否严格模式
|
||||||
virtualListProps?: VirtualListProps; // 传入开启虚拟滚动
|
virtualListProps?: VirtualListProps; // 传入开启虚拟滚动
|
||||||
|
panelWidth?: number; // 下拉框宽度,默认为 150px
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
|
@ -167,7 +168,10 @@
|
||||||
const getOptionComputedStyle = computed(() => {
|
const getOptionComputedStyle = computed(() => {
|
||||||
// 减去 80px 是为了防止溢出,因为会出现单选框、右侧箭头
|
// 减去 80px 是为了防止溢出,因为会出现单选框、右侧箭头
|
||||||
return {
|
return {
|
||||||
width: cascaderDeep.value <= 2 ? `${cascaderWidth.value / cascaderDeep.value - 80}px` : '150px',
|
width:
|
||||||
|
cascaderDeep.value <= 2
|
||||||
|
? `${cascaderWidth.value / cascaderDeep.value - 80 - cascaderDeep.value * 4}px`
|
||||||
|
: `${props.panelWidth}px` || '150px',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -248,6 +252,10 @@
|
||||||
.arco-virtual-list {
|
.arco-virtual-list {
|
||||||
.ms-scroll-bar();
|
.ms-scroll-bar();
|
||||||
}
|
}
|
||||||
|
.arco-cascader-option {
|
||||||
|
margin: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.arco-cascader-panel-column:first-child {
|
.arco-cascader-panel-column:first-child {
|
||||||
.arco-checkbox {
|
.arco-checkbox {
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { watch, ref, h, defineComponent } from 'vue';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { SelectOptionData } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
export type ModelType = string | number | Record<string, any> | (string | number | Record<string, any>)[];
|
||||||
|
|
||||||
|
export interface MsSearchSelectProps {
|
||||||
|
modelValue: ModelType;
|
||||||
|
allowClear?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
prefix?: string;
|
||||||
|
searchKeys: string[]; // 需要搜索的 key 名,关键字会遍历这个 key 数组,然后取 item[key] 进行模糊匹配
|
||||||
|
options: SelectOptionData[];
|
||||||
|
optionLabelRender?: (item: SelectOptionData) => string; // 自定义 option 的 label 渲染,返回一个 html 字符串,默认使用 item.label
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent(
|
||||||
|
(props: MsSearchSelectProps, { emit }) => {
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const innerValue = ref(props.modelValue);
|
||||||
|
const filterOptions = ref<SelectOptionData[]>([...props.options]);
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
innerValue.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.options,
|
||||||
|
(arr) => {
|
||||||
|
filterOptions.value = [...arr];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleUserSearch(val: string) {
|
||||||
|
if (val.trim() === '') {
|
||||||
|
filterOptions.value = [...props.options];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const highlightedKeyword = `<span class="text-[rgb(var(--primary-4))]">${val}</span>`;
|
||||||
|
filterOptions.value = props.options
|
||||||
|
.map((e) => {
|
||||||
|
const item = { ...e };
|
||||||
|
let hasMatch = false;
|
||||||
|
for (let i = 0; i < props.searchKeys.length; i++) {
|
||||||
|
// 遍历传入的搜索字段
|
||||||
|
const key = props.searchKeys[i];
|
||||||
|
|
||||||
|
if (e[key].includes(val)) {
|
||||||
|
// 是否匹配
|
||||||
|
hasMatch = true;
|
||||||
|
item[key] = e[key].replace(new RegExp(val, 'gi'), highlightedKeyword); // 高亮关键字替换
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasMatch) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((e) => e) as SelectOptionData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<a-select
|
||||||
|
default-value={innerValue}
|
||||||
|
placeholder={t(props.placeholder || '')}
|
||||||
|
allow-clear={props.allowClear}
|
||||||
|
allow-search
|
||||||
|
filter-option={false}
|
||||||
|
onUpdate:model-value={(value: ModelType) => emit('update:modelValue', value)}
|
||||||
|
onInputValueChange={debounce(handleUserSearch, 300)}
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
prefix: () => t(props.prefix || ''),
|
||||||
|
default: () =>
|
||||||
|
filterOptions.value.map((item) => (
|
||||||
|
<a-option key={item.id} value={item.value}>
|
||||||
|
{typeof props.optionLabelRender === 'function'
|
||||||
|
? h('div', { innerHTML: props.optionLabelRender(item) })
|
||||||
|
: item.label}
|
||||||
|
</a-option>
|
||||||
|
)),
|
||||||
|
}}
|
||||||
|
</a-select>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line vue/require-prop-types
|
||||||
|
props: ['modelValue', 'allowClear', 'placeholder', 'prefix', 'searchKeys', 'options', 'optionLabelRender'],
|
||||||
|
emits: ['update:modelValue'],
|
||||||
|
}
|
||||||
|
);
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, ref, watch } from 'vue';
|
import { Ref, ref, watch } from 'vue';
|
||||||
import { useRouter, RouteRecordRaw, RouteRecordNormalized, RouteRecordName } from 'vue-router';
|
import { useRouter, RouteRecordRaw, RouteRecordName } from 'vue-router';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { listenerRouteChange } from '@/utils/route-listener';
|
import { listenerRouteChange } from '@/utils/route-listener';
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
import appClientMenus from '@/router/app-menus';
|
import appClientMenus from '@/router/app-menus';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
const copyRouter = cloneDeep(appClientMenus) as RouteRecordNormalized[];
|
const copyRouters = cloneDeep(appClientMenus) as RouteRecordRaw[];
|
||||||
const permission = usePermission();
|
const permission = usePermission();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -41,13 +41,13 @@
|
||||||
|
|
||||||
function setCurrentTopMenu(key: string) {
|
function setCurrentTopMenu(key: string) {
|
||||||
// 先判断全等,避免同级路由出现命名包含情况
|
// 先判断全等,避免同级路由出现命名包含情况
|
||||||
const secParentFullSame = appStore.topMenus.find((el: RouteRecordRaw) => {
|
const secParentFullSame = appStore.topMenus.find((route: RouteRecordRaw) => {
|
||||||
return key === el?.name;
|
return key === route?.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 非全等的情况下,一定是父子路由包含关系
|
// 非全等的情况下,一定是父子路由包含关系
|
||||||
const secParentLike = appStore.topMenus.find((el: RouteRecordRaw) => {
|
const secParentLike = appStore.topMenus.find((route: RouteRecordRaw) => {
|
||||||
return key.includes(el?.name as string);
|
return key.includes(route?.name as string);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (secParentFullSame) {
|
if (secParentFullSame) {
|
||||||
|
@ -58,22 +58,35 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听路由变化,存储打开的三级子路由
|
* 监听路由变化,存储打开的顶部菜单
|
||||||
*/
|
*/
|
||||||
listenerRouteChange((newRoute) => {
|
listenerRouteChange((newRoute) => {
|
||||||
const { name } = newRoute;
|
const { name } = newRoute;
|
||||||
copyRouter.forEach((el: RouteRecordRaw) => {
|
for (let i = 0; i < copyRouters.length; i++) {
|
||||||
|
const firstRoute = copyRouters[i];
|
||||||
// 权限校验通过
|
// 权限校验通过
|
||||||
if (permission.accessRouter(el)) {
|
if (permission.accessRouter(firstRoute)) {
|
||||||
if (name && (name as string).includes((el?.name as string) || '')) {
|
if (name && firstRoute?.name && (name as string).includes(firstRoute.name as string)) {
|
||||||
const currentParent = el?.children?.find(
|
// 先判断二级菜单是否顶部菜单
|
||||||
(item) => name && (name as string).includes((item?.name as string) || '')
|
let currentParent = firstRoute?.children?.some((item) => item.meta?.isTopMenu)
|
||||||
|
? (firstRoute as RouteRecordRaw)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!currentParent) {
|
||||||
|
// 二级菜单非顶部菜单,则判断三级菜单是否有顶部菜单
|
||||||
|
currentParent = firstRoute?.children?.find(
|
||||||
|
(item) => name && item?.name && (name as string).includes(item.name as string)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
appStore.setTopMenus(currentParent?.children?.filter((item) => item.meta?.isTopMenu));
|
appStore.setTopMenus(currentParent?.children?.filter((item) => item.meta?.isTopMenu));
|
||||||
setCurrentTopMenu(name as string);
|
setCurrentTopMenu(name as string);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 切换到没有顶部菜单的路由时,清空顶部菜单
|
||||||
|
appStore.setTopMenus([]);
|
||||||
|
setCurrentTopMenu('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
function jumpPath(route: RouteRecordName | undefined) {
|
function jumpPath(route: RouteRecordName | undefined) {
|
|
@ -1,19 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-timeline v-if="mode === 'static'" class="ms-timeline">
|
|
||||||
<a-timeline-item
|
|
||||||
v-for="item of props.list"
|
|
||||||
:key="item.id || item.label"
|
|
||||||
:dot-color="item.dotColor || 'var(--color-text-input-border)'"
|
|
||||||
:label="item.label"
|
|
||||||
:line-type="item.lineType"
|
|
||||||
>
|
|
||||||
<slot name="content">
|
|
||||||
<span class="timeline-text">{{ item.time }}</span>
|
|
||||||
</slot>
|
|
||||||
</a-timeline-item>
|
|
||||||
</a-timeline>
|
|
||||||
<a-timeline
|
<a-timeline
|
||||||
v-else
|
|
||||||
:class="[
|
:class="[
|
||||||
'ms-timeline',
|
'ms-timeline',
|
||||||
isArrivedTop ? 'ms-timeline--hidden-top-shadow' : '',
|
isArrivedTop ? 'ms-timeline--hidden-top-shadow' : '',
|
||||||
|
@ -28,7 +14,7 @@
|
||||||
@reach-bottom="handleReachBottom"
|
@reach-bottom="handleReachBottom"
|
||||||
>
|
>
|
||||||
<template #item="{ item, index }">
|
<template #item="{ item, index }">
|
||||||
<div>
|
<div :class="index === 0 ? 'pt-[12px]' : ''">
|
||||||
<a-list-item :key="item.id">
|
<a-list-item :key="item.id">
|
||||||
<a-timeline-item :dot-color="item.dotColor || 'var(--color-text-input-border)'" :line-type="item.lineType">
|
<a-timeline-item :dot-color="item.dotColor || 'var(--color-text-input-border)'" :line-type="item.lineType">
|
||||||
<slot name="time" :item="item">
|
<slot name="time" :item="item">
|
||||||
|
@ -39,7 +25,10 @@
|
||||||
</slot>
|
</slot>
|
||||||
</a-timeline-item>
|
</a-timeline-item>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
<div v-if="index === props.list.length - 1" class="flex h-[32px] items-center justify-center">
|
<div
|
||||||
|
v-if="props.mode === 'remote' && index === props.list.length - 1"
|
||||||
|
class="flex h-[32px] items-center justify-center"
|
||||||
|
>
|
||||||
<div v-if="noMoreData" class="text-[var(--color-text-4)]">{{ t('ms.timeline.noMoreData') }}</div>
|
<div v-if="noMoreData" class="text-[var(--color-text-4)]">{{ t('ms.timeline.noMoreData') }}</div>
|
||||||
<a-spin v-else />
|
<a-spin v-else />
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,7 +97,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleReachBottom() {
|
function handleReachBottom() {
|
||||||
if (!props.noMoreData && props.list.length > 0) {
|
if (props.mode === 'remote' && !props.noMoreData && props.list.length > 0) {
|
||||||
emit('reachBottom');
|
emit('reachBottom');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,6 +136,10 @@
|
||||||
.timeline-text {
|
.timeline-text {
|
||||||
color: @color-text-5;
|
color: @color-text-5;
|
||||||
}
|
}
|
||||||
|
.ms-timeline-content {
|
||||||
|
@apply overflow-auto;
|
||||||
|
.ms-scroll-bar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ms-timeline--hidden-top-shadow {
|
.ms-timeline--hidden-top-shadow {
|
||||||
box-shadow: inset 0 -10px 6px -10px rgb(0 0 0 / 15%);
|
box-shadow: inset 0 -10px 6px -10px rgb(0 0 0 / 15%);
|
||||||
|
|
|
@ -185,7 +185,7 @@
|
||||||
import { LOCALE_OPTIONS } from '@/locale';
|
import { LOCALE_OPTIONS } from '@/locale';
|
||||||
import useLocale from '@/locale/useLocale';
|
import useLocale from '@/locale/useLocale';
|
||||||
// import useUser from '@/hooks/useUser';
|
// import useUser from '@/hooks/useUser';
|
||||||
import TopMenu from '@/components/bussiness/ms-top-menu/index.vue';
|
import TopMenu from '@/components/business/ms-top-menu/index.vue';
|
||||||
import MessageBox from '../message-box/index.vue';
|
import MessageBox from '../message-box/index.vue';
|
||||||
import { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants';
|
import { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants';
|
||||||
// import { getProjectList } from '@/api/modules/setting/project';
|
// import { getProjectList } from '@/api/modules/setting/project';
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
import { RouteEnum } from '@/enums/routeEnum';
|
||||||
|
import { TreeNode, mapTree } from '@/utils';
|
||||||
|
|
||||||
|
export const MENU_LEVEL = ['SYSTEM', 'ORGANIZATION', 'PROJECT'] as const; // 菜单级别
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由与菜单、tab、权限、国际化信息的映射关系,用于通过路由直接跳转到各页面及携带 tab 参数
|
||||||
|
* key 是与后台商定的映射 key
|
||||||
|
* locale 是国际化的 key
|
||||||
|
* route 是路由的 name
|
||||||
|
* permission 是权限的 key 集合
|
||||||
|
* level 是菜单级别
|
||||||
|
* children 是子路由/tab集合
|
||||||
|
*/
|
||||||
|
export const pathMap = [
|
||||||
|
{
|
||||||
|
key: 'SETTING', // 系统设置
|
||||||
|
locale: 'menu.settings',
|
||||||
|
route: RouteEnum.SETTING,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM', // 系统设置-系统
|
||||||
|
locale: 'menu.settings.system',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_USER', // 系统设置-系统-用户
|
||||||
|
locale: 'menu.settings.system.user',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_USER,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_USER_GROUP', // 系统设置-系统-用户组
|
||||||
|
locale: 'menu.settings.system.usergroup',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_USER_GROUP,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_ORGANIZATION', // 系统设置-系统-组织与项目
|
||||||
|
locale: 'menu.settings.system.organizationAndProject',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_ORGANIZATION,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_PARAMETER', // 系统设置-系统-系统参数
|
||||||
|
locale: 'menu.settings.system.parameter',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_PARAMETER,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_PARAMETER', // 系统设置-系统-系统参数-基础设置
|
||||||
|
locale: 'system.config.baseConfig',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_PARAMETER,
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_PARAMETER_PAGE_CONFIG', // 系统设置-系统-系统参数-界面设置
|
||||||
|
locale: 'system.config.pageConfig',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_PARAMETER,
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_PARAMETER_AUTH_CONFIG', // 系统设置-系统-系统参数-认证设置
|
||||||
|
locale: 'system.config.authConfig',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_PARAMETER,
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_RESOURCE_POOL', // 系统设置-系统-资源池
|
||||||
|
locale: 'menu.settings.system.resourcePool',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_RESOURCE_POOL,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_AUTHORIZED_MANAGEMENT', // 系统设置-系统-授权管理
|
||||||
|
locale: 'menu.settings.system.authorizedManagement',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_AUTHORIZED_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_LOG', // 系统设置-系统-日志
|
||||||
|
locale: 'menu.settings.system.log',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_LOG,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_SYSTEM_PLUGIN_MANAGEMENT', // 系统设置-系统-插件管理
|
||||||
|
locale: 'menu.settings.system.pluginManager',
|
||||||
|
route: RouteEnum.SETTING_SYSTEM_PLUGIN_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_ORGANIZATION', // 系统设置-组织
|
||||||
|
locale: 'menu.settings.organization',
|
||||||
|
route: RouteEnum.SETTING_ORGANIZATION,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[1],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'SETTING_ORGANIZATION_MEMBER', // 系统设置-组织-成员
|
||||||
|
locale: 'menu.settings.organization.member',
|
||||||
|
route: RouteEnum.SETTING_ORGANIZATION_MEMBER,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SETTING_ORGANIZATION_SERVICE', // 系统设置-组织-服务集成
|
||||||
|
locale: 'menu.settings.organization.serviceIntegration',
|
||||||
|
route: RouteEnum.SETTING_ORGANIZATION_SERVICE,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[1],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'PROJECT_MANAGEMENT', // 项目管理
|
||||||
|
locale: 'menu.projectManagement',
|
||||||
|
route: RouteEnum.PROJECT_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'PROJECT_MANAGEMENT_LOG', // 项目管理-日志
|
||||||
|
locale: 'menu.projectManagement.log',
|
||||||
|
route: RouteEnum.PROJECT_MANAGEMENT_LOG,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据菜单级别过滤映射树
|
||||||
|
* @param level 菜单级别
|
||||||
|
* @param customNodeFn 自定义过滤函数
|
||||||
|
* @returns 过滤后的映射树
|
||||||
|
*/
|
||||||
|
export const getPathMapByLevel = <T>(
|
||||||
|
level: (typeof MENU_LEVEL)[number],
|
||||||
|
customNodeFn: (node: TreeNode<T>) => TreeNode<T> | null = (node) => node
|
||||||
|
) => {
|
||||||
|
return mapTree(pathMap, (e) => {
|
||||||
|
let isValid = true; // 默认是系统级别
|
||||||
|
if (level === MENU_LEVEL[1]) {
|
||||||
|
// 组织级别只展示组织、项目
|
||||||
|
isValid = e.level !== MENU_LEVEL[0];
|
||||||
|
} else if (level === MENU_LEVEL[2]) {
|
||||||
|
// 项目级别只展示项目
|
||||||
|
isValid = e.level !== MENU_LEVEL[0] && e.level !== MENU_LEVEL[1];
|
||||||
|
}
|
||||||
|
if (isValid) {
|
||||||
|
return typeof customNodeFn === 'function' ? customNodeFn(e) : e;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,13 +0,0 @@
|
||||||
enum menuEnum {
|
|
||||||
APITEST = 'apiTest',
|
|
||||||
BUGMANAGEMENT = 'bugManagement',
|
|
||||||
FEATURETEST = 'featureTest',
|
|
||||||
PERFORMANCE_TEST = 'performanceTest',
|
|
||||||
PROJECTMANAGEMENT = 'projectManagement',
|
|
||||||
SETTING = 'setting',
|
|
||||||
TESTPLAN = 'testPlan',
|
|
||||||
UITEST = 'uiTest',
|
|
||||||
WORKPLACE = 'workplace',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default menuEnum;
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
export enum ApiTestRouteEnum {
|
||||||
|
API_TEST = 'apiTest',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BugManagementRouteEnum {
|
||||||
|
BUG_MANAGEMENT = 'bugManagement',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FeatureTestRouteEnum {
|
||||||
|
FEATURE_TEST = 'featureTest',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PerformanceTestRouteEnum {
|
||||||
|
PERFORMANCE_TEST = 'performanceTest',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProjectManagementRouteEnum {
|
||||||
|
PROJECT_MANAGEMENT = 'projectManagement',
|
||||||
|
PROJECT_MANAGEMENT_INDEX = 'projectManagementIndex',
|
||||||
|
PROJECT_MANAGEMENT_LOG = 'projectManagementLog',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TestPlanRouteEnum {
|
||||||
|
TEST_PLAN = 'testPlan',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum UITestRouteEnum {
|
||||||
|
UI_TEST = 'uiTest',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WorkbenchRouteEnum {
|
||||||
|
WORKBENCH = 'workbench',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SettingRouteEnum {
|
||||||
|
SETTING = 'setting',
|
||||||
|
SETTING_SYSTEM = 'settingSystem',
|
||||||
|
SETTING_SYSTEM_USER = 'settingSystemUser',
|
||||||
|
SETTING_SYSTEM_USER_GROUP = 'settingSystemUserGroup',
|
||||||
|
SETTING_SYSTEM_ORGANIZATION = 'settingSystemOrganization',
|
||||||
|
SETTING_SYSTEM_PARAMETER = 'settingSystemParameter',
|
||||||
|
SETTING_SYSTEM_RESOURCE_POOL = 'settingSystemResourcePool',
|
||||||
|
SETTING_SYSTEM_RESOURCE_POOL_DETAIL = 'settingSystemResourcePoolDetail',
|
||||||
|
SETTING_SYSTEM_AUTHORIZED_MANAGEMENT = 'settingSystemAuthorizedManagement',
|
||||||
|
SETTING_SYSTEM_LOG = 'settingSystemLog',
|
||||||
|
SETTING_SYSTEM_PLUGIN_MANAGEMENT = 'settingSystemPluginManagement',
|
||||||
|
SETTING_ORGANIZATION = 'settingOrganization',
|
||||||
|
SETTING_ORGANIZATION_MEMBER = 'settingOrganizationMember',
|
||||||
|
SETTING_ORGANIZATION_SERVICE = 'settingOrganizationService',
|
||||||
|
SETTING_ORGANIZATION_LOG = 'settingOrganizationLog',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RouteEnum = {
|
||||||
|
...ApiTestRouteEnum,
|
||||||
|
...SettingRouteEnum,
|
||||||
|
...BugManagementRouteEnum,
|
||||||
|
...FeatureTestRouteEnum,
|
||||||
|
...PerformanceTestRouteEnum,
|
||||||
|
...ProjectManagementRouteEnum,
|
||||||
|
...TestPlanRouteEnum,
|
||||||
|
...UITestRouteEnum,
|
||||||
|
...WorkbenchRouteEnum,
|
||||||
|
};
|
|
@ -64,7 +64,7 @@
|
||||||
import Footer from '@/components/pure/footer/index.vue';
|
import Footer from '@/components/pure/footer/index.vue';
|
||||||
import usePermission from '@/hooks/usePermission';
|
import usePermission from '@/hooks/usePermission';
|
||||||
import PageLayout from './page-layout.vue';
|
import PageLayout from './page-layout.vue';
|
||||||
import MsBreadCrumb from '@/components/bussiness/ms-breadcrumb/index.vue';
|
import MsBreadCrumb from '@/components/business/ms-breadcrumb/index.vue';
|
||||||
import { GetTitleImgUrl } from '@/api/requrls/setting/config';
|
import { GetTitleImgUrl } from '@/api/requrls/setting/config';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
@ -19,7 +19,7 @@ Object.keys(_Vmodules).forEach((key) => {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
message: {
|
message: {
|
||||||
'menu.workplace': 'Workplace',
|
'menu.workbench': 'Workbench',
|
||||||
'menu.testPlan': 'Test plan',
|
'menu.testPlan': 'Test plan',
|
||||||
'menu.bugManagement': 'Bug management',
|
'menu.bugManagement': 'Bug management',
|
||||||
'menu.featureTest': 'Feature test',
|
'menu.featureTest': 'Feature test',
|
||||||
|
@ -27,21 +27,23 @@ export default {
|
||||||
'menu.uiTest': 'UI test',
|
'menu.uiTest': 'UI test',
|
||||||
'menu.performanceTest': 'Performance test',
|
'menu.performanceTest': 'Performance test',
|
||||||
'menu.projectManagement': 'Project management',
|
'menu.projectManagement': 'Project management',
|
||||||
|
'menu.projectManagement.log': 'Project Log',
|
||||||
'menu.settings': 'Settings',
|
'menu.settings': 'Settings',
|
||||||
'menu.settings.system': 'System',
|
'menu.settings.system': 'System',
|
||||||
'menu.settings.organization': 'Organization',
|
|
||||||
'menu.settings.organization.member': 'Member',
|
|
||||||
'menu.settings.organization.serviceIntegration': 'Service Integration',
|
|
||||||
'menu.settings.system.usergroup': 'User Group',
|
'menu.settings.system.usergroup': 'User Group',
|
||||||
'menu.settings.system.authorizedManagement': 'Authorized Management',
|
'menu.settings.system.authorizedManagement': 'Authorized Management',
|
||||||
'menu.settings.system.pluginmanger': 'Plugin Manger',
|
'menu.settings.system.pluginManager': 'Plugin Manger',
|
||||||
'menu.settings.system.user': 'User',
|
'menu.settings.system.user': 'User',
|
||||||
'menu.settings.system.organizationAndProject': 'Org & Project',
|
'menu.settings.system.organizationAndProject': 'Org & Project',
|
||||||
'menu.settings.system.resourcePool': 'Resource Pool',
|
'menu.settings.system.resourcePool': 'Resource Pool',
|
||||||
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
|
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
|
||||||
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
|
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
|
||||||
'menu.settings.system.parameter': 'System parameter',
|
'menu.settings.system.parameter': 'System parameter',
|
||||||
'menu.settings.system.log': 'Log',
|
'menu.settings.system.log': 'System Log',
|
||||||
|
'menu.settings.organization': 'Organization',
|
||||||
|
'menu.settings.organization.member': 'Member',
|
||||||
|
'menu.settings.organization.serviceIntegration': 'Service Integration',
|
||||||
|
'menu.settings.organization.log': 'Org Log',
|
||||||
'navbar.action.locale': 'Switch to English',
|
'navbar.action.locale': 'Switch to English',
|
||||||
...sys,
|
...sys,
|
||||||
...localeSettings,
|
...localeSettings,
|
||||||
|
|
|
@ -18,7 +18,7 @@ Object.keys(_Vmodules).forEach((key) => {
|
||||||
});
|
});
|
||||||
export default {
|
export default {
|
||||||
message: {
|
message: {
|
||||||
'menu.workplace': '工作台',
|
'menu.workbench': '工作台',
|
||||||
'menu.testPlan': '测试计划',
|
'menu.testPlan': '测试计划',
|
||||||
'menu.bugManagement': '缺陷管理',
|
'menu.bugManagement': '缺陷管理',
|
||||||
'menu.featureTest': '功能测试',
|
'menu.featureTest': '功能测试',
|
||||||
|
@ -26,21 +26,23 @@ export default {
|
||||||
'menu.uiTest': 'UI测试',
|
'menu.uiTest': 'UI测试',
|
||||||
'menu.performanceTest': '性能测试',
|
'menu.performanceTest': '性能测试',
|
||||||
'menu.projectManagement': '项目管理',
|
'menu.projectManagement': '项目管理',
|
||||||
|
'menu.projectManagement.log': '项目日志',
|
||||||
'menu.settings': '系统设置',
|
'menu.settings': '系统设置',
|
||||||
'menu.settings.system': '系统',
|
'menu.settings.system': '系统',
|
||||||
'menu.settings.organization': '组织',
|
|
||||||
'menu.settings.organization.member': '成员',
|
|
||||||
'menu.settings.organization.serviceIntegration': '服务集成',
|
|
||||||
'menu.settings.system.user': '用户',
|
'menu.settings.system.user': '用户',
|
||||||
'menu.settings.system.usergroup': '用户组',
|
'menu.settings.system.usergroup': '用户组',
|
||||||
'menu.settings.system.authorizedManagement': '授权管理',
|
'menu.settings.system.authorizedManagement': '授权管理',
|
||||||
'menu.settings.system.pluginmanger': '插件管理',
|
'menu.settings.system.pluginManager': '插件管理',
|
||||||
'menu.settings.system.organizationAndProject': '组织与项目',
|
'menu.settings.system.organizationAndProject': '组织与项目',
|
||||||
'menu.settings.system.resourcePool': '资源池',
|
'menu.settings.system.resourcePool': '资源池',
|
||||||
'menu.settings.system.resourcePoolDetail': '添加资源池',
|
'menu.settings.system.resourcePoolDetail': '添加资源池',
|
||||||
'menu.settings.system.resourcePoolEdit': '编辑资源池',
|
'menu.settings.system.resourcePoolEdit': '编辑资源池',
|
||||||
'menu.settings.system.parameter': '系统参数',
|
'menu.settings.system.parameter': '系统参数',
|
||||||
'menu.settings.system.log': '日志',
|
'menu.settings.system.log': '系统日志',
|
||||||
|
'menu.settings.organization': '组织',
|
||||||
|
'menu.settings.organization.member': '成员',
|
||||||
|
'menu.settings.organization.serviceIntegration': '服务集成',
|
||||||
|
'menu.settings.organization.log': '组织日志',
|
||||||
'navbar.action.locale': '切换为中文',
|
'navbar.action.locale': '切换为中文',
|
||||||
...sys,
|
...sys,
|
||||||
...localeSettings,
|
...localeSettings,
|
||||||
|
|
|
@ -11,7 +11,7 @@ export interface LogOptions {
|
||||||
export interface LogItem {
|
export interface LogItem {
|
||||||
id: string;
|
id: string;
|
||||||
createUser: string;
|
createUser: string;
|
||||||
userName: string;
|
userName: string; // 操作人
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
|
@ -22,3 +22,21 @@ export interface LogItem {
|
||||||
createTime: number;
|
createTime: number;
|
||||||
sourceId: string;
|
sourceId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
enable: boolean;
|
||||||
|
createTime: number;
|
||||||
|
updateTime: number;
|
||||||
|
language: string;
|
||||||
|
lastOrganizationId: string;
|
||||||
|
phone: string;
|
||||||
|
source: string;
|
||||||
|
lastProjectId: string;
|
||||||
|
createUser: string;
|
||||||
|
updateUser: string;
|
||||||
|
deleted: boolean;
|
||||||
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ export const NOT_FOUND = {
|
||||||
export const REDIRECT_ROUTE_NAME = 'Redirect';
|
export const REDIRECT_ROUTE_NAME = 'Redirect';
|
||||||
|
|
||||||
// 首页路由
|
// 首页路由
|
||||||
export const DEFAULT_ROUTE_NAME = 'Workplace';
|
export const DEFAULT_ROUTE_NAME = 'Workbench';
|
||||||
|
|
||||||
// 默认 tab-bar 路,多页签模式下,打开的第一个页面
|
// 默认 tab-bar 路,多页签模式下,打开的第一个页面
|
||||||
export const DEFAULT_ROUTE = {
|
export const DEFAULT_ROUTE = {
|
||||||
title: 'menu.dashboard.workplace',
|
title: 'menu.dashboard.workbench',
|
||||||
name: DEFAULT_ROUTE_NAME,
|
name: DEFAULT_ROUTE_NAME,
|
||||||
fullPath: '/dashboard/workplace',
|
fullPath: '/dashboard/workbench',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 不需要显示项目选择器的模块,数组项为一级路由的path
|
// 不需要显示项目选择器的模块,数组项为一级路由的path
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const ApiTest: AppRouteRecordRaw = {
|
const ApiTest: AppRouteRecordRaw = {
|
||||||
path: '/api-test',
|
path: '/api-test',
|
||||||
name: menuEnum.APITEST,
|
name: ApiTestRouteEnum.API_TEST,
|
||||||
redirect: '/api-test/index',
|
redirect: '/api-test/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const BugManagement: AppRouteRecordRaw = {
|
const BugManagement: AppRouteRecordRaw = {
|
||||||
path: '/bug-management',
|
path: '/bug-management',
|
||||||
name: menuEnum.BUGMANAGEMENT,
|
name: BugManagementRouteEnum.BUG_MANAGEMENT,
|
||||||
redirect: '/bug-management/index',
|
redirect: '/bug-management/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { FeatureTestRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const FeatureTest: AppRouteRecordRaw = {
|
const FeatureTest: AppRouteRecordRaw = {
|
||||||
path: '/feature-test',
|
path: '/feature-test',
|
||||||
name: menuEnum.FEATURETEST,
|
name: FeatureTestRouteEnum.FEATURE_TEST,
|
||||||
redirect: '/feature-test/index',
|
redirect: '/feature-test/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { PerformanceTestRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const PerformanceTest: AppRouteRecordRaw = {
|
const PerformanceTest: AppRouteRecordRaw = {
|
||||||
path: '/performance-test',
|
path: '/performance-test',
|
||||||
name: menuEnum.PERFORMANCE_TEST,
|
name: PerformanceTestRouteEnum.PERFORMANCE_TEST,
|
||||||
redirect: '/performance-test/index',
|
redirect: '/performance-test/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const ProjectManagement: AppRouteRecordRaw = {
|
const ProjectManagement: AppRouteRecordRaw = {
|
||||||
path: '/project-management',
|
path: '/project-management',
|
||||||
name: menuEnum.PROJECTMANAGEMENT,
|
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT,
|
||||||
redirect: '/project-management/index',
|
redirect: '/project-management/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
@ -16,12 +17,22 @@ const ProjectManagement: AppRouteRecordRaw = {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index',
|
path: 'index',
|
||||||
name: 'ProjectManagementIndex',
|
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_INDEX,
|
||||||
component: () => import('@/views/project-management/index.vue'),
|
component: () => import('@/views/project-management/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
roles: ['*'],
|
roles: ['*'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'log',
|
||||||
|
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_LOG,
|
||||||
|
component: () => import('@/views/project-management/log/index.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.projectManagement.log',
|
||||||
|
roles: ['*'],
|
||||||
|
isTopMenu: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const Setting: AppRouteRecordRaw = {
|
const Setting: AppRouteRecordRaw = {
|
||||||
path: '/setting',
|
path: '/setting',
|
||||||
name: 'setting',
|
name: SettingRouteEnum.SETTING,
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings',
|
locale: 'menu.settings',
|
||||||
|
@ -13,7 +15,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'system',
|
path: 'system',
|
||||||
name: 'settingSystem',
|
name: SettingRouteEnum.SETTING_SYSTEM,
|
||||||
redirect: '/setting/system/user',
|
redirect: '/setting/system/user',
|
||||||
component: null,
|
component: null,
|
||||||
meta: {
|
meta: {
|
||||||
|
@ -24,7 +26,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'user',
|
path: 'user',
|
||||||
name: 'settingSystemUser',
|
name: SettingRouteEnum.SETTING_SYSTEM_USER,
|
||||||
component: () => import('@/views/setting/system/user/index.vue'),
|
component: () => import('@/views/setting/system/user/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.user',
|
locale: 'menu.settings.system.user',
|
||||||
|
@ -34,7 +36,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'usergroup',
|
path: 'usergroup',
|
||||||
name: 'settingSystemUsergroup',
|
name: SettingRouteEnum.SETTING_SYSTEM_USER_GROUP,
|
||||||
component: () => import('@/views/setting/system/usergroup/index.vue'),
|
component: () => import('@/views/setting/system/usergroup/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.usergroup',
|
locale: 'menu.settings.system.usergroup',
|
||||||
|
@ -44,7 +46,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'organization-and-project',
|
path: 'organization-and-project',
|
||||||
name: 'settingSystemOrganization',
|
name: SettingRouteEnum.SETTING_SYSTEM_ORGANIZATION,
|
||||||
component: () => import('@/views/setting/system/organizationAndProject/index.vue'),
|
component: () => import('@/views/setting/system/organizationAndProject/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.organizationAndProject',
|
locale: 'menu.settings.system.organizationAndProject',
|
||||||
|
@ -54,7 +56,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'parameter',
|
path: 'parameter',
|
||||||
name: 'settingSystemParameter',
|
name: SettingRouteEnum.SETTING_SYSTEM_PARAMETER,
|
||||||
component: () => import('@/views/setting/system/config/index.vue'),
|
component: () => import('@/views/setting/system/config/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.parameter',
|
locale: 'menu.settings.system.parameter',
|
||||||
|
@ -64,34 +66,28 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'resourcePool',
|
path: 'resourcePool',
|
||||||
name: 'settingSystemResourcePool',
|
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL,
|
||||||
component: () => import('@/views/setting/system/resourcePool/index.vue'),
|
component: () => import('@/views/setting/system/resourcePool/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.resourcePool',
|
locale: 'menu.settings.system.resourcePool',
|
||||||
roles: ['*'],
|
roles: ['*'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
name: 'settingSystemResourcePool',
|
|
||||||
locale: 'menu.settings.system.resourcePool',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'resourcePoolDetail',
|
path: 'resourcePoolDetail',
|
||||||
name: 'settingSystemResourcePoolDetail',
|
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL_DETAIL,
|
||||||
component: () => import('@/views/setting/system/resourcePool/detail.vue'),
|
component: () => import('@/views/setting/system/resourcePool/detail.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.resourcePoolDetail',
|
locale: 'menu.settings.system.resourcePoolDetail',
|
||||||
roles: ['*'],
|
roles: ['*'],
|
||||||
breadcrumbs: [
|
breadcrumbs: [
|
||||||
{
|
{
|
||||||
name: 'settingSystemResourcePool',
|
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL,
|
||||||
locale: 'menu.settings.system.resourcePool',
|
locale: 'menu.settings.system.resourcePool',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'settingSystemResourcePoolDetail',
|
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL_DETAIL,
|
||||||
locale: 'menu.settings.system.resourcePoolDetail',
|
locale: 'menu.settings.system.resourcePoolDetail',
|
||||||
editTag: 'id',
|
editTag: 'id',
|
||||||
editLocale: 'menu.settings.system.resourcePoolEdit',
|
editLocale: 'menu.settings.system.resourcePoolEdit',
|
||||||
|
@ -101,7 +97,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'authorizedmanagement',
|
path: 'authorizedmanagement',
|
||||||
name: 'settingSystemAuthorizedManagement',
|
name: SettingRouteEnum.SETTING_SYSTEM_AUTHORIZED_MANAGEMENT,
|
||||||
component: () => import('@/views/setting/system/authorizedManagement/index.vue'),
|
component: () => import('@/views/setting/system/authorizedManagement/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.authorizedManagement',
|
locale: 'menu.settings.system.authorizedManagement',
|
||||||
|
@ -111,7 +107,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'log',
|
path: 'log',
|
||||||
name: 'settingSystemLog',
|
name: SettingRouteEnum.SETTING_SYSTEM_LOG,
|
||||||
component: () => import('@/views/setting/system/log/index.vue'),
|
component: () => import('@/views/setting/system/log/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.log',
|
locale: 'menu.settings.system.log',
|
||||||
|
@ -120,11 +116,11 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pluginmanger',
|
path: 'pluginManager',
|
||||||
name: 'settingSystemPluginManger',
|
name: SettingRouteEnum.SETTING_SYSTEM_PLUGIN_MANAGEMENT,
|
||||||
component: () => import('@/views/setting/system/pluginManager/index.vue'),
|
component: () => import('@/views/setting/system/pluginManager/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.pluginmanger',
|
locale: 'menu.settings.system.pluginManager',
|
||||||
roles: ['*'],
|
roles: ['*'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
|
@ -133,7 +129,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'organization',
|
path: 'organization',
|
||||||
name: 'settingOrganization',
|
name: SettingRouteEnum.SETTING_ORGANIZATION,
|
||||||
redirect: '/setting/organization/member',
|
redirect: '/setting/organization/member',
|
||||||
component: null,
|
component: null,
|
||||||
meta: {
|
meta: {
|
||||||
|
@ -144,7 +140,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'member',
|
path: 'member',
|
||||||
name: 'settingOrganizationMember',
|
name: SettingRouteEnum.SETTING_ORGANIZATION_MEMBER,
|
||||||
component: () => import('@/views/setting/organization/member/index.vue'),
|
component: () => import('@/views/setting/organization/member/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.organization.member',
|
locale: 'menu.settings.organization.member',
|
||||||
|
@ -154,7 +150,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'serviceIntegration',
|
path: 'serviceIntegration',
|
||||||
name: 'settingOrganizationService',
|
name: SettingRouteEnum.SETTING_ORGANIZATION_SERVICE,
|
||||||
component: () => import('@/views/setting/organization/serviceIntegration/index.vue'),
|
component: () => import('@/views/setting/organization/serviceIntegration/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.organization.serviceIntegration',
|
locale: 'menu.settings.organization.serviceIntegration',
|
||||||
|
@ -162,6 +158,16 @@ const Setting: AppRouteRecordRaw = {
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'log',
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_LOG,
|
||||||
|
component: () => import('@/views/setting/organization/log/index.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.settings.organization.log',
|
||||||
|
roles: ['*'],
|
||||||
|
isTopMenu: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const TestPlan: AppRouteRecordRaw = {
|
const TestPlan: AppRouteRecordRaw = {
|
||||||
path: '/test-plan',
|
path: '/test-plan',
|
||||||
name: menuEnum.TESTPLAN,
|
name: TestPlanRouteEnum.TEST_PLAN,
|
||||||
redirect: '/test-plan/index',
|
redirect: '/test-plan/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import { AppRouteRecordRaw } from '../types';
|
import { UITestRouteEnum } from '@/enums/routeEnum';
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
const UiTest: AppRouteRecordRaw = {
|
const UiTest: AppRouteRecordRaw = {
|
||||||
path: '/ui-test',
|
path: '/ui-test',
|
||||||
name: menuEnum.UITEST,
|
name: UITestRouteEnum.UI_TEST,
|
||||||
redirect: '/ui-test/index',
|
redirect: '/ui-test/index',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
|
import { WorkbenchRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
|
const Workbench: AppRouteRecordRaw = {
|
||||||
|
path: '/workbench',
|
||||||
|
name: WorkbenchRouteEnum.WORKBENCH,
|
||||||
|
redirect: '/workbench/index',
|
||||||
|
component: DEFAULT_LAYOUT,
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.workbench',
|
||||||
|
icon: 'icon-icon_pc_filled',
|
||||||
|
order: 0,
|
||||||
|
hideChildrenInMenu: true,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
name: 'WorkbenchIndex',
|
||||||
|
component: () => import('@/views/workbench/index.vue'),
|
||||||
|
meta: {
|
||||||
|
roles: ['*'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Workbench;
|
|
@ -1,28 +0,0 @@
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
|
||||||
import { AppRouteRecordRaw } from '../types';
|
|
||||||
import menuEnum from '@/enums/menuEnum';
|
|
||||||
|
|
||||||
const WorkPlace: AppRouteRecordRaw = {
|
|
||||||
path: '/workplace',
|
|
||||||
name: menuEnum.WORKPLACE,
|
|
||||||
redirect: '/workplace/index',
|
|
||||||
component: DEFAULT_LAYOUT,
|
|
||||||
meta: {
|
|
||||||
locale: 'menu.workplace',
|
|
||||||
icon: 'icon-icon_pc_filled',
|
|
||||||
order: 0,
|
|
||||||
hideChildrenInMenu: true,
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index',
|
|
||||||
name: 'WorkPlaceIndex',
|
|
||||||
component: () => import('@/views/workplace/index.vue'),
|
|
||||||
meta: {
|
|
||||||
roles: ['*'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WorkPlace;
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import type { NavigationGuard } from 'vue-router';
|
import type { NavigationGuard } from 'vue-router';
|
||||||
import type { BreadcrumbItem } from '@/components/bussiness/ms-breadcrumb/types';
|
import type { BreadcrumbItem } from '@/components/business/ms-breadcrumb/types';
|
||||||
|
|
||||||
export type Component<T = any> =
|
export type Component<T = any> =
|
||||||
| ReturnType<typeof defineComponent>
|
| ReturnType<typeof defineComponent>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { watchStyle, watchTheme } from '@/utils/theme';
|
||||||
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
|
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
|
||||||
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||||
import type { AppState } from './types';
|
import type { AppState } from './types';
|
||||||
import type { BreadcrumbItem } from '@/components/bussiness/ms-breadcrumb/types';
|
import type { BreadcrumbItem } from '@/components/business/ms-breadcrumb/types';
|
||||||
import type { PageConfig, PageConfigKeys, Style, Theme } from '@/models/setting/config';
|
import type { PageConfig, PageConfigKeys, Style, Theme } from '@/models/setting/config';
|
||||||
|
|
||||||
const defaultThemeConfig = {
|
const defaultThemeConfig = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||||
import type { BreadcrumbItem } from '@/components/bussiness/ms-breadcrumb/types';
|
import type { BreadcrumbItem } from '@/components/business/ms-breadcrumb/types';
|
||||||
import type { PageConfig, ThemeConfig, LoginConfig, PlatformConfig } from '@/models/setting/config';
|
import type { PageConfig, ThemeConfig, LoginConfig, PlatformConfig } from '@/models/setting/config';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
|
|
|
@ -172,3 +172,37 @@ export function calculateMaxDepth(arr?: Node[], depth = 0) {
|
||||||
|
|
||||||
return maxDepth;
|
return maxDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TreeNode<T> {
|
||||||
|
[key: string]: any;
|
||||||
|
children?: TreeNode<T>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归遍历树形数组或树
|
||||||
|
* @param tree 树形数组或树
|
||||||
|
* @param customNodeFn 自定义节点函数
|
||||||
|
* @param customChildrenKey 自定义子节点的key
|
||||||
|
* @returns 遍历后的树形数组
|
||||||
|
*/
|
||||||
|
export function mapTree<T>(
|
||||||
|
tree: TreeNode<T> | TreeNode<T>[],
|
||||||
|
customNodeFn: (node: TreeNode<T>) => TreeNode<T> | null = (node) => node,
|
||||||
|
customChildrenKey = 'children'
|
||||||
|
): TreeNode<T>[] {
|
||||||
|
if (!Array.isArray(tree)) {
|
||||||
|
tree = [tree];
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree
|
||||||
|
.map((node: TreeNode<T>) => {
|
||||||
|
const newNode = typeof customNodeFn === 'function' ? customNodeFn(node) : node;
|
||||||
|
|
||||||
|
if (newNode && newNode[customChildrenKey] && newNode[customChildrenKey].length > 0) {
|
||||||
|
newNode[customChildrenKey] = mapTree(newNode[customChildrenKey], customNodeFn, customChildrenKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,6 @@
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const back = () => {
|
const back = () => {
|
||||||
// warning: Go to the node that has the permission
|
// warning: Go to the node that has the permission
|
||||||
router.push({ name: 'Workplace' });
|
router.push({ name: 'Workbench' });
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<logCards mode="PROJECT"></logCards>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup>
|
||||||
|
import logCards from '@/views/setting/system/log/components/logCards.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<logCards mode="ORGANIZATION"></logCards>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup>
|
||||||
|
import logCards from '@/views/setting/system/log/components/logCards.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -139,7 +139,7 @@
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import AddMemberModal from './components/addMemberModal.vue';
|
import AddMemberModal from './components/addMemberModal.vue';
|
||||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
import MsRemoveButton from '@/components/bussiness/ms-remove-button/MsRemoveButton.vue';
|
import MsRemoveButton from '@/components/business/ms-remove-button/MsRemoveButton.vue';
|
||||||
import {
|
import {
|
||||||
getMemberList,
|
getMemberList,
|
||||||
deleteMemberReq,
|
deleteMemberReq,
|
||||||
|
@ -150,7 +150,7 @@
|
||||||
getGlobalUserGroup,
|
getGlobalUserGroup,
|
||||||
} from '@/api/modules/setting/member';
|
} from '@/api/modules/setting/member';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import MSBatchModal from '@/components/bussiness/ms-batch-modal/index.vue';
|
import MSBatchModal from '@/components/business/ms-batch-modal/index.vue';
|
||||||
import { useTableStore, useUserStore } from '@/store';
|
import { useTableStore, useUserStore } from '@/store';
|
||||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import type {
|
import type {
|
||||||
|
|
|
@ -499,7 +499,7 @@
|
||||||
updateAuthStatus,
|
updateAuthStatus,
|
||||||
deleteAuth,
|
deleteAuth,
|
||||||
} from '@/api/modules/setting/config';
|
} from '@/api/modules/setting/config';
|
||||||
import MsFormItemSub from '@/components/bussiness/ms-form-item-sub/index.vue';
|
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||||
import { scrollIntoView } from '@/utils/dom';
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@
|
||||||
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
|
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
import MsFormItemSub from '@/components/bussiness/ms-form-item-sub/index.vue';
|
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||||
import { validateEmail } from '@/utils/validate';
|
import { validateEmail } from '@/utils/validate';
|
||||||
import { testEmail, saveBaseInfo, saveEmailInfo, getBaseInfo, getEmailInfo } from '@/api/modules/setting/config';
|
import { testEmail, saveBaseInfo, saveEmailInfo, getBaseInfo, getEmailInfo } from '@/api/modules/setting/config';
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,7 @@
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import loginForm from '@/views/login/components/login-form.vue';
|
import loginForm from '@/views/login/components/login-form.vue';
|
||||||
import banner from '@/views/login/components/banner.vue';
|
import banner from '@/views/login/components/banner.vue';
|
||||||
import MsFormItemSub from '@/components/bussiness/ms-form-item-sub/index.vue';
|
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||||
import defaultLayout from '@/layout/default-layout.vue';
|
import defaultLayout from '@/layout/default-layout.vue';
|
||||||
|
|
|
@ -0,0 +1,436 @@
|
||||||
|
<template>
|
||||||
|
<MsCard simple auto-height>
|
||||||
|
<div class="filter-box">
|
||||||
|
<MsSearchSelect
|
||||||
|
v-model:model-value="operUser"
|
||||||
|
placeholder="system.log.operatorPlaceholder"
|
||||||
|
prefix="system.log.operator"
|
||||||
|
:options="userList"
|
||||||
|
:search-keys="['label', 'email']"
|
||||||
|
:option-label-render="
|
||||||
|
(item) => `${item.label}<span class='text-[var(--color-text-2)]'>(${item.email})</span>`
|
||||||
|
"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:model-value="time"
|
||||||
|
show-time
|
||||||
|
:time-picker-props="{
|
||||||
|
defaultValue: ['00:00:00', '00:00:00'],
|
||||||
|
}"
|
||||||
|
:disabled-date="disabledDate"
|
||||||
|
class="filter-item"
|
||||||
|
:allow-clear="false"
|
||||||
|
:disabled-input="false"
|
||||||
|
value-format="timestamp"
|
||||||
|
@select="selectTime"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
{{ t('system.log.operateTime') }}
|
||||||
|
</template>
|
||||||
|
</a-range-picker>
|
||||||
|
<MsCascader
|
||||||
|
v-model:model-value="operateRange"
|
||||||
|
v-model:level="level"
|
||||||
|
:options="rangeOptions"
|
||||||
|
:prefix="t('system.log.operateRange')"
|
||||||
|
:level-top="[...MENU_LEVEL]"
|
||||||
|
:virtual-list-props="{ height: 200 }"
|
||||||
|
:loading="rangeLoading"
|
||||||
|
/>
|
||||||
|
<a-select v-model:model-value="type" class="filter-item">
|
||||||
|
<template #prefix>
|
||||||
|
{{ t('system.log.operateType') }}
|
||||||
|
</template>
|
||||||
|
<a-option v-for="opt of typeOptions" :key="opt.value" :value="opt.value">{{ t(opt.label) }}</a-option>
|
||||||
|
</a-select>
|
||||||
|
<MsCascader
|
||||||
|
v-model="_module"
|
||||||
|
:options="moduleOptions"
|
||||||
|
mode="native"
|
||||||
|
:prefix="t('system.log.operateTarget')"
|
||||||
|
:virtual-list-props="{ height: 200 }"
|
||||||
|
:placeholder="t('system.log.operateTargetPlaceholder')"
|
||||||
|
:panel-width="100"
|
||||||
|
strictly
|
||||||
|
/>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="content"
|
||||||
|
class="filter-item"
|
||||||
|
:placeholder="t('system.log.operateNamePlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
{{ t('system.log.operateName') }}
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</div>
|
||||||
|
<a-button type="outline" @click="searchLog">{{ t('system.log.search') }}</a-button>
|
||||||
|
<a-button type="outline" class="arco-btn-outline--secondary ml-[8px]" @click="resetFilter">
|
||||||
|
{{ t('system.log.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</MsCard>
|
||||||
|
<div class="log-card">
|
||||||
|
<div class="log-card-header">
|
||||||
|
<div class="text-[var(--color-text-000)]">{{ t('system.log.log') }}</div>
|
||||||
|
</div>
|
||||||
|
<ms-base-table v-bind="propsRes" no-disable sticky-header v-on="propsEvent">
|
||||||
|
<template #range="{ record }">
|
||||||
|
{{ `${record.organizationName}${record.projectName ? `/${record.projectName}` : ''}` }}
|
||||||
|
</template>
|
||||||
|
<template #content="{ record }">
|
||||||
|
<MsButton @click="handleNameClick(record)">{{ record.content }}</MsButton>
|
||||||
|
</template>
|
||||||
|
</ms-base-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup>
|
||||||
|
import { onBeforeMount, ref } from 'vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { getLogList, getLogOptions, getLogUsers } from '@/api/modules/setting/log';
|
||||||
|
import MsCascader from '@/components/business/ms-cascader/index.vue';
|
||||||
|
import useTableStore from '@/store/modules/ms-table';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import { MENU_LEVEL, getPathMapByLevel } from '@/config/pathMap';
|
||||||
|
import MsSearchSelect from '@/components/business/ms-search-select/index';
|
||||||
|
|
||||||
|
import type { CascaderOption, SelectOptionData } from '@arco-design/web-vue';
|
||||||
|
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import type { MsTimeLineListItem } from '@/components/pure/ms-timeline/types';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mode: (typeof MENU_LEVEL)[number]; // 日志展示模式,系统/组织/项目
|
||||||
|
}>();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const operUser = ref(''); // 操作人
|
||||||
|
const userList = ref<SelectOptionData[]>([]); // 操作人列表
|
||||||
|
|
||||||
|
async function initUserList() {
|
||||||
|
try {
|
||||||
|
const res = await getLogUsers();
|
||||||
|
userList.value = res.map((e) => ({
|
||||||
|
id: e.id,
|
||||||
|
value: e.id,
|
||||||
|
label: e.name,
|
||||||
|
email: e.email,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultLevelIndex = MENU_LEVEL.findIndex((e) => e === props.mode); // 默认操作范围级别索引,根据传入的 mode 确定
|
||||||
|
const operateRange = ref<(string | number | Record<string, any>)[]>([MENU_LEVEL[defaultLevelIndex]]); // 操作范围
|
||||||
|
const rangeOptions = ref<CascaderOption[]>( // 系统级别才展示系统级别选项
|
||||||
|
props.mode === 'SYSTEM'
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
value: {
|
||||||
|
level: 0,
|
||||||
|
value: MENU_LEVEL[0],
|
||||||
|
},
|
||||||
|
label: t('system.log.system'),
|
||||||
|
isLeaf: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
);
|
||||||
|
const rangeLoading = ref(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化操作范围级联选项
|
||||||
|
*/
|
||||||
|
async function initRangeOptions() {
|
||||||
|
try {
|
||||||
|
rangeLoading.value = true;
|
||||||
|
const res = await getLogOptions();
|
||||||
|
if (props.mode === 'SYSTEM' || props.mode === 'ORGANIZATION') {
|
||||||
|
// 系统和组织级别才展示,项目级别不展示
|
||||||
|
rangeOptions.value.push({
|
||||||
|
value: {
|
||||||
|
level: 0,
|
||||||
|
value: MENU_LEVEL[1], // 顶级范围-组织,单选
|
||||||
|
},
|
||||||
|
label: t('system.log.organization'),
|
||||||
|
children: res.organizationList.map((e) => ({
|
||||||
|
// 组织列表,多选
|
||||||
|
value: {
|
||||||
|
level: MENU_LEVEL[1],
|
||||||
|
value: e.id,
|
||||||
|
},
|
||||||
|
label: e.name,
|
||||||
|
isLeaf: true,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
rangeOptions.value.push({
|
||||||
|
value: {
|
||||||
|
level: 0,
|
||||||
|
value: MENU_LEVEL[2], // 顶级范围-项目,单选
|
||||||
|
},
|
||||||
|
label: t('system.log.project'),
|
||||||
|
children: res.projectList.map((e) => ({
|
||||||
|
// 项目列表,多选
|
||||||
|
value: {
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
value: e.id,
|
||||||
|
},
|
||||||
|
label: e.name,
|
||||||
|
isLeaf: true,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
rangeLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const level = ref<(typeof MENU_LEVEL)[number]>('SYSTEM'); // 操作范围级别,系统/组织/项目
|
||||||
|
const type = ref(''); // 操作类型
|
||||||
|
const _module = ref(''); // 操作对象
|
||||||
|
const content = ref(''); // 名称
|
||||||
|
const time = ref<(Date | string | number)[]>([dayjs().subtract(1, 'M').valueOf(), dayjs().valueOf()]); // 操作时间
|
||||||
|
const selectedTime = ref<Date | string | number | undefined>(''); // 日期选择器选中但未确认的时间
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不可选日期
|
||||||
|
* @param current 日期选择器当前日期
|
||||||
|
* @return boolean true-不可选,false-可选
|
||||||
|
*/
|
||||||
|
function disabledDate(current?: Date) {
|
||||||
|
const now = dayjs();
|
||||||
|
const endDate = dayjs(current);
|
||||||
|
// 限制不可选日期范围:明天以后&点击的日期前后 6 个月以外的日期(也就是可选时间跨度为 6 个月)
|
||||||
|
return (
|
||||||
|
(!!current && endDate.isAfter(now)) ||
|
||||||
|
(selectedTime.value ? Math.abs(dayjs(selectedTime.value).diff(endDate, 'months')) > 5 : false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期选择器点击选中的日期但未确认时
|
||||||
|
* @param val 点击的日期
|
||||||
|
*/
|
||||||
|
function selectTime(val: (Date | string | number | undefined)[] | undefined) {
|
||||||
|
const arr = val?.filter((e) => e) || [];
|
||||||
|
if (arr.length === 1) {
|
||||||
|
[selectedTime.value] = arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleOptions = ref<CascaderOption[]>([]);
|
||||||
|
const moduleLocaleMap = ref<Record<string, string>>({});
|
||||||
|
|
||||||
|
function initModuleOptions() {
|
||||||
|
moduleOptions.value = getPathMapByLevel(props.mode, (e) => {
|
||||||
|
moduleLocaleMap.value[e.key] = e.locale;
|
||||||
|
return {
|
||||||
|
value: e.key,
|
||||||
|
label: t(e.locale),
|
||||||
|
children: e.children,
|
||||||
|
isLeaf: e.children?.length === 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeOptions = [
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.all',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.add',
|
||||||
|
value: 'ADD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.delete',
|
||||||
|
value: 'DELETE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.update',
|
||||||
|
value: 'UPDATE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.debug',
|
||||||
|
value: 'DEBUG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.review',
|
||||||
|
value: 'REVIEW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.copy',
|
||||||
|
value: 'COPY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.execute',
|
||||||
|
value: 'EXECUTE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.share',
|
||||||
|
value: 'SHARE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.restore',
|
||||||
|
value: 'RESTORE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.import',
|
||||||
|
value: 'IMPORT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.export',
|
||||||
|
value: 'EXPORT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.login',
|
||||||
|
value: 'LOGIN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.select',
|
||||||
|
value: 'SELECT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.recover',
|
||||||
|
value: 'RECOVER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.log.operateType.logout',
|
||||||
|
value: 'LOGOUT',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function resetFilter() {
|
||||||
|
operUser.value = '';
|
||||||
|
operateRange.value = [MENU_LEVEL[0]];
|
||||||
|
type.value = '';
|
||||||
|
_module.value = '';
|
||||||
|
content.value = '';
|
||||||
|
time.value = [dayjs().subtract(1, 'M').valueOf(), dayjs().valueOf()];
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'system.log.operator',
|
||||||
|
dataIndex: 'userName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.log.operateRange',
|
||||||
|
dataIndex: 'operateRange',
|
||||||
|
slotName: 'range',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.log.operateTarget',
|
||||||
|
dataIndex: 'module',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.log.operateType',
|
||||||
|
dataIndex: 'type',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.log.operateName',
|
||||||
|
dataIndex: 'content',
|
||||||
|
slotName: 'content',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.log.time',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 160,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const tableStore = useTableStore();
|
||||||
|
tableStore.initColumn(TableKeyEnum.SYSTEM_LOG, columns, 'drawer');
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams, resetPagination } = useTable(
|
||||||
|
getLogList,
|
||||||
|
{
|
||||||
|
tableKey: TableKeyEnum.SYSTEM_LOG,
|
||||||
|
columns,
|
||||||
|
selectable: false,
|
||||||
|
showSelectAll: false,
|
||||||
|
},
|
||||||
|
(record) => ({
|
||||||
|
...record,
|
||||||
|
type: t(typeOptions.find((e) => e.value === record.type)?.label || ''),
|
||||||
|
module: t(moduleLocaleMap.value[record.module] || ''),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
function searchLog() {
|
||||||
|
const ranges = operateRange.value.map((e) => e);
|
||||||
|
setLoadListParams({
|
||||||
|
operUser: operUser.value,
|
||||||
|
projectIds: level.value === 'PROJECT' && ranges[0] !== 'PROJECT' ? ranges : [],
|
||||||
|
organizationIds: level.value === 'ORGANIZATION' && ranges[0] !== 'ORGANIZATION' ? ranges : [],
|
||||||
|
type: type.value,
|
||||||
|
module: _module.value,
|
||||||
|
content: content.value,
|
||||||
|
startTime: time.value[0],
|
||||||
|
endTime: time.value[1],
|
||||||
|
level: level.value,
|
||||||
|
});
|
||||||
|
resetPagination();
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNameClick(record: MsTimeLineListItem) {
|
||||||
|
console.log(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
initUserList();
|
||||||
|
initRangeOptions();
|
||||||
|
initModuleOptions();
|
||||||
|
searchLog();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.filter-box {
|
||||||
|
@apply grid;
|
||||||
|
|
||||||
|
margin-bottom: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 1400px) {
|
||||||
|
.filter-box {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1400px) {
|
||||||
|
.filter-box {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.log-card {
|
||||||
|
@apply bg-white;
|
||||||
|
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: var(--border-radius-large);
|
||||||
|
.log-card-header {
|
||||||
|
@apply flex items-center justify-between;
|
||||||
|
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.log-card--list {
|
||||||
|
padding: 0 0 24px;
|
||||||
|
.log-card-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 24px 24px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,240 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<MsCard simple auto-height>
|
<logCards mode="SYSTEM"></logCards>
|
||||||
<div class="filter-box">
|
|
||||||
<a-input
|
|
||||||
v-model:model-value="operUser"
|
|
||||||
class="filter-item"
|
|
||||||
:placeholder="t('system.log.operatorPlaceholder')"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
{{ t('system.log.operator') }}
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
<a-range-picker
|
|
||||||
v-model:model-value="time"
|
|
||||||
show-time
|
|
||||||
:time-picker-props="{
|
|
||||||
defaultValue: ['00:00:00', '00:00:00'],
|
|
||||||
}"
|
|
||||||
:disabled-date="disabledDate"
|
|
||||||
class="filter-item"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
{{ t('system.log.operateTime') }}
|
|
||||||
</template>
|
|
||||||
</a-range-picker>
|
|
||||||
<MsCascader
|
|
||||||
v-model="operateRange"
|
|
||||||
:options="rangeOptions"
|
|
||||||
:prefix="t('system.log.operateRange')"
|
|
||||||
:level-top="levelTop"
|
|
||||||
:virtual-list-props="{ height: 200 }"
|
|
||||||
/>
|
|
||||||
<a-select v-model:model-value="type" class="filter-item">
|
|
||||||
<template #prefix>
|
|
||||||
{{ t('system.log.operateType') }}
|
|
||||||
</template>
|
|
||||||
<a-option v-for="opt of typeOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</a-option>
|
|
||||||
</a-select>
|
|
||||||
<MsCascader
|
|
||||||
v-model="_module"
|
|
||||||
:options="moduleOptions"
|
|
||||||
mode="native"
|
|
||||||
:prefix="t('system.log.operateTarget')"
|
|
||||||
:virtual-list-props="{ height: 200 }"
|
|
||||||
:placeholder="t('system.log.operateTargetPlaceholder')"
|
|
||||||
/>
|
|
||||||
<a-input
|
|
||||||
v-model:model-value="content"
|
|
||||||
class="filter-item"
|
|
||||||
:placeholder="t('system.log.operateNamePlaceholder')"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
{{ t('system.log.operateName') }}
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</div>
|
|
||||||
<a-button type="outline">{{ t('system.log.search') }}</a-button>
|
|
||||||
<a-button type="outline" class="arco-btn-outline--secondary ml-[8px]" @click="resetFilter">
|
|
||||||
{{ t('system.log.reset') }}
|
|
||||||
</a-button>
|
|
||||||
</MsCard>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="tsx" setup>
|
||||||
import { onBeforeMount, ref } from 'vue';
|
import logCards from './components/logCards.vue';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
|
||||||
import { getLogOptions } from '@/api/modules/setting/log';
|
|
||||||
import MsCascader from '@/components/bussiness/ms-cascader/index.vue';
|
|
||||||
|
|
||||||
import type { CascaderOption } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const operUser = ref(''); // 操作人
|
|
||||||
const levelTop = ['SYSTEM', 'ORGANIZATION', 'PROJECT']; // 操作范围顶级
|
|
||||||
const operateRange = ref<(string | number | Record<string, any>)[]>([levelTop[0]]); // 操作范围
|
|
||||||
const type = ref(''); // 操作类型
|
|
||||||
const _module = ref(''); // 操作对象
|
|
||||||
const content = ref(''); // 名称
|
|
||||||
const time = ref<(Date | string | number)[]>([]); // 操作时间
|
|
||||||
const rangeOptions = ref<CascaderOption[]>([
|
|
||||||
{
|
|
||||||
value: {
|
|
||||||
level: 0,
|
|
||||||
value: levelTop[0],
|
|
||||||
},
|
|
||||||
label: t('system.log.system'),
|
|
||||||
isLeaf: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const moduleOptions = ref<CascaderOption[]>([
|
|
||||||
{
|
|
||||||
value: 'system',
|
|
||||||
label: t('system.log.system'),
|
|
||||||
isLeaf: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const typeOptions = [
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.all'),
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.add'),
|
|
||||||
value: 'ADD',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.delete'),
|
|
||||||
value: 'DELETE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.update'),
|
|
||||||
value: 'UPDATE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.debug'),
|
|
||||||
value: 'DEBUG',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.review'),
|
|
||||||
value: 'REVIEW',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.copy'),
|
|
||||||
value: 'COPY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.execute'),
|
|
||||||
value: 'EXECUTE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.share'),
|
|
||||||
value: 'SHARE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.restore'),
|
|
||||||
value: 'RESTORE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.import'),
|
|
||||||
value: 'IMPORT',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.export'),
|
|
||||||
value: 'EXPORT',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.login'),
|
|
||||||
value: 'LOGIN',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.select'),
|
|
||||||
value: 'SELECT',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.recover'),
|
|
||||||
value: 'RECOVER',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('system.log.operateType.logout'),
|
|
||||||
value: 'LOGOUT',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
try {
|
|
||||||
const res = await getLogOptions();
|
|
||||||
rangeOptions.value.push({
|
|
||||||
value: {
|
|
||||||
level: 0,
|
|
||||||
value: levelTop[1],
|
|
||||||
},
|
|
||||||
label: t('system.log.organization'),
|
|
||||||
children: res.organizationList.map((e) => ({
|
|
||||||
value: {
|
|
||||||
level: levelTop[1],
|
|
||||||
value: e.id,
|
|
||||||
},
|
|
||||||
label: e.name,
|
|
||||||
isLeaf: true,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
rangeOptions.value.push({
|
|
||||||
value: {
|
|
||||||
level: 0,
|
|
||||||
value: levelTop[2],
|
|
||||||
},
|
|
||||||
label: t('system.log.project'),
|
|
||||||
children: res.projectList.map((e) => ({
|
|
||||||
value: {
|
|
||||||
level: levelTop[2],
|
|
||||||
value: e.id,
|
|
||||||
},
|
|
||||||
label: e.name,
|
|
||||||
isLeaf: true,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function disabledDate(current?: Date) {
|
|
||||||
const now = dayjs();
|
|
||||||
const endDate = dayjs(current);
|
|
||||||
return !!current && endDate.isAfter(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetFilter() {
|
|
||||||
operUser.value = '';
|
|
||||||
operateRange.value = [levelTop[0]];
|
|
||||||
type.value = '';
|
|
||||||
_module.value = '';
|
|
||||||
content.value = '';
|
|
||||||
time.value = [];
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped></style>
|
||||||
.filter-box {
|
|
||||||
@apply grid;
|
|
||||||
|
|
||||||
margin-bottom: 16px;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1400px) {
|
|
||||||
.filter-box {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (min-width: 1400px) {
|
|
||||||
.filter-box {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1 +1,35 @@
|
||||||
export default {};
|
export default {
|
||||||
|
'system.log.operator': 'Operator',
|
||||||
|
'system.log.operatorPlaceholder': 'Please enter username/email to search',
|
||||||
|
'system.log.operateTime': 'Operation time',
|
||||||
|
'system.log.operateRange': 'Operating range',
|
||||||
|
'system.log.operateType': 'Operation type',
|
||||||
|
'system.log.operateTarget': 'Operation object',
|
||||||
|
'system.log.operateTargetPlaceholder': 'Please select',
|
||||||
|
'system.log.operateName': 'Name',
|
||||||
|
'system.log.operateNamePlaceholder': 'Please enter use case/environment/menu name',
|
||||||
|
'system.log.system': 'System',
|
||||||
|
'system.log.organization': 'Organization',
|
||||||
|
'system.log.project': 'Project',
|
||||||
|
'system.log.search': 'Search',
|
||||||
|
'system.log.reset': 'Reset',
|
||||||
|
'system.log.operateType.all': 'All',
|
||||||
|
'system.log.operateType.add': 'Add',
|
||||||
|
'system.log.operateType.delete': 'Delete',
|
||||||
|
'system.log.operateType.update': 'Update',
|
||||||
|
'system.log.operateType.debug': 'Debug',
|
||||||
|
'system.log.operateType.review': 'Review',
|
||||||
|
'system.log.operateType.copy': 'Copy',
|
||||||
|
'system.log.operateType.execute': 'Execute',
|
||||||
|
'system.log.operateType.share': 'Share',
|
||||||
|
'system.log.operateType.restore': 'Restore',
|
||||||
|
'system.log.operateType.import': 'Import',
|
||||||
|
'system.log.operateType.export': 'Export',
|
||||||
|
'system.log.operateType.login': 'Login',
|
||||||
|
'system.log.operateType.select': 'Select',
|
||||||
|
'system.log.operateType.recover': 'Recover',
|
||||||
|
'system.log.operateType.logout': 'Logout',
|
||||||
|
'system.log.log': 'Operation log',
|
||||||
|
'system.log.time': 'Operation time',
|
||||||
|
'system.log.content': 'in {module} under {range}',
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
'system.log.operator': '操作人',
|
'system.log.operator': '操作人',
|
||||||
'system.log.operatorPlaceholder': '请输入用户名/邮箱',
|
'system.log.operatorPlaceholder': '请输入用户名/邮箱搜索',
|
||||||
'system.log.operateTime': '操作时间',
|
'system.log.operateTime': '操作时间',
|
||||||
'system.log.operateRange': '操作范围',
|
'system.log.operateRange': '操作范围',
|
||||||
'system.log.operateType': '操作类型',
|
'system.log.operateType': '操作类型',
|
||||||
|
@ -29,4 +29,7 @@ export default {
|
||||||
'system.log.operateType.select': '选择',
|
'system.log.operateType.select': '选择',
|
||||||
'system.log.operateType.recover': '恢复',
|
'system.log.operateType.recover': '恢复',
|
||||||
'system.log.operateType.logout': '登出',
|
'system.log.operateType.logout': '登出',
|
||||||
|
'system.log.log': '操作日志',
|
||||||
|
'system.log.time': '操作时间',
|
||||||
|
'system.log.content': '{operator} 在 {range} 下的 {module} 中 ',
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { reactive, ref, watchEffect, computed } from 'vue';
|
import { reactive, ref, watchEffect, computed } from 'vue';
|
||||||
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
||||||
import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import { createOrUpdateOrg } from '@/api/modules/setting/system/organizationAndProject';
|
import { createOrUpdateOrg } from '@/api/modules/setting/system/organizationAndProject';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';
|
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { reactive, ref, watchEffect, computed } from 'vue';
|
import { reactive, ref, watchEffect, computed } from 'vue';
|
||||||
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
||||||
import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import { createOrUpdateOrg } from '@/api/modules/setting/system/organizationAndProject';
|
import { createOrUpdateOrg } from '@/api/modules/setting/system/organizationAndProject';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { CreateOrUpdateSystemProjectParams } from '@/models/setting/system/orgAndProject';
|
import { CreateOrUpdateSystemProjectParams } from '@/models/setting/system/orgAndProject';
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
import { reactive, ref, watchEffect, onUnmounted } from 'vue';
|
import { reactive, ref, watchEffect, onUnmounted } from 'vue';
|
||||||
import { addUserToOrgOrProject } from '@/api/modules/setting/system/organizationAndProject';
|
import { addUserToOrgOrProject } from '@/api/modules/setting/system/organizationAndProject';
|
||||||
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-vue';
|
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-vue';
|
||||||
import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import AddUserModal from './addUserModal.vue';
|
import AddUserModal from './addUserModal.vue';
|
||||||
import { TableData, Message } from '@arco-design/web-vue';
|
import { TableData, Message } from '@arco-design/web-vue';
|
||||||
import MsRemoveButton from '@/components/bussiness/ms-remove-button/MsRemoveButton.vue';
|
import MsRemoveButton from '@/components/business/ms-remove-button/MsRemoveButton.vue';
|
||||||
|
|
||||||
export interface projectDrawerProps {
|
export interface projectDrawerProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
|
|
@ -338,7 +338,7 @@
|
||||||
import useVisit from '@/hooks/useVisit';
|
import useVisit from '@/hooks/useVisit';
|
||||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsBatchForm from '@/components/bussiness/ms-batch-form/index.vue';
|
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||||
import JobTemplateDrawer from './components/jobTemplateDrawer.vue';
|
import JobTemplateDrawer from './components/jobTemplateDrawer.vue';
|
||||||
import { getYaml, YamlType, job } from './template';
|
import { getYaml, YamlType, job } from './template';
|
||||||
|
@ -348,7 +348,7 @@
|
||||||
import { getAllOrgList } from '@/api/modules/setting/orgnization';
|
import { getAllOrgList } from '@/api/modules/setting/orgnization';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import type { MsBatchFormInstance, FormItemModel } from '@/components/bussiness/ms-batch-form/types';
|
import type { MsBatchFormInstance, FormItemModel } from '@/components/business/ms-batch-form/types';
|
||||||
import type { UpdateResourcePoolParams, NodesListItem } from '@/models/setting/resourcePool';
|
import type { UpdateResourcePoolParams, NodesListItem } from '@/models/setting/resourcePool';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MsCard :loading="loading" has-breadcrumb simple>
|
<MsCard :loading="loading" simple>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<a-button type="primary" @click="addPool">
|
<a-button type="primary" @click="addPool">
|
||||||
{{ t('system.resourcePool.createPool') }}
|
{{ t('system.resourcePool.createPool') }}
|
||||||
|
|
|
@ -216,7 +216,7 @@
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
import MsBatchForm from '@/components/bussiness/ms-batch-form/index.vue';
|
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||||
import {
|
import {
|
||||||
getUserList,
|
getUserList,
|
||||||
batchCreateUser,
|
batchCreateUser,
|
||||||
|
@ -240,7 +240,7 @@
|
||||||
import type { MsTableColumn, BatchActionParams } from '@/components/pure/ms-table/type';
|
import type { MsTableColumn, BatchActionParams } from '@/components/pure/ms-table/type';
|
||||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
import type { SimpleUserInfo, SystemRole, UserListItem } from '@/models/setting/user';
|
import type { SimpleUserInfo, SystemRole, UserListItem } from '@/models/setting/user';
|
||||||
import type { FormItemModel, MsBatchFormInstance } from '@/components/bussiness/ms-batch-form/types';
|
import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
import { useUserGroupStore } from '@/store';
|
import { useUserGroupStore } from '@/store';
|
||||||
import { getUserList, addUserToUserGroup } from '@/api/modules/setting/usergroup';
|
import { getUserList, addUserToUserGroup } from '@/api/modules/setting/usergroup';
|
||||||
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
||||||
import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import AddUserModal from './addUserModal.vue';
|
import AddUserModal from './addUserModal.vue';
|
||||||
import MsRemoveButton from '@/components/bussiness/ms-remove-button/MsRemoveButton.vue';
|
import MsRemoveButton from '@/components/business/ms-remove-button/MsRemoveButton.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const store = useUserGroupStore();
|
const store = useUserGroupStore();
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<template> Workbench is waiting for development </template>
|
||||||
|
|
||||||
|
<script setup></script>
|
|
@ -1,3 +0,0 @@
|
||||||
<template> Workplace is waiting for development </template>
|
|
||||||
|
|
||||||
<script setup></script>
|
|
Loading…
Reference in New Issue