feat(全局): 命名错误调整&搜索选择器组件&系统/组织/项目日志&部分组件调整
This commit is contained in:
parent
2e689901e2
commit
e4d6864afd
|
@ -486,7 +486,7 @@ export enum ContentTypeEnum {
|
|||
|
||||
## -locale
|
||||
|
||||
国际化模块,存放项目声明的国际化配置,按语种划分模块。模块入口文件为`index.ts`,负责定义菜单、导航栏等公共的国际化配置,其他的按系统功能声明并导入`index.ts`,还有项目内页面组件的国际化配置也需要导入至`index.ts`,页面组件的国际化配置在页面的目录下声明,如:`views/dashboard/workplace/locale`。
|
||||
国际化模块,存放项目声明的国际化配置,按语种划分模块。模块入口文件为`index.ts`,负责定义菜单、导航栏等公共的国际化配置,其他的按系统功能声明并导入`index.ts`,还有项目内页面组件的国际化配置也需要导入至`index.ts`,页面组件的国际化配置在页面的目录下声明,如:`views/dashboard/workbench/locale`。
|
||||
|
||||
```typescript
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
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 { LogOptions, LogItem } from '@/models/setting/log';
|
||||
import type { LogOptions, LogItem, UserItem } from '@/models/setting/log';
|
||||
|
||||
// 获取日志列表
|
||||
export function getLogList(data: any) {
|
||||
|
@ -11,5 +11,10 @@ export function getLogList(data: any) {
|
|||
|
||||
// 获取日志操作范围选项
|
||||
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 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; // 是否多选
|
||||
strictly?: boolean; // 是否严格模式
|
||||
virtualListProps?: VirtualListProps; // 传入开启虚拟滚动
|
||||
panelWidth?: number; // 下拉框宽度,默认为 150px
|
||||
placeholder?: string;
|
||||
loading?: boolean;
|
||||
}>(),
|
||||
|
@ -167,7 +168,10 @@
|
|||
const getOptionComputedStyle = computed(() => {
|
||||
// 减去 80px 是为了防止溢出,因为会出现单选框、右侧箭头
|
||||
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 {
|
||||
.ms-scroll-bar();
|
||||
}
|
||||
.arco-cascader-option {
|
||||
margin: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.arco-cascader-panel-column:first-child {
|
||||
.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">
|
||||
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 { useAppStore } from '@/store';
|
||||
import { listenerRouteChange } from '@/utils/route-listener';
|
||||
|
@ -22,7 +22,7 @@
|
|||
import appClientMenus from '@/router/app-menus';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const copyRouter = cloneDeep(appClientMenus) as RouteRecordNormalized[];
|
||||
const copyRouters = cloneDeep(appClientMenus) as RouteRecordRaw[];
|
||||
const permission = usePermission();
|
||||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
|
@ -41,13 +41,13 @@
|
|||
|
||||
function setCurrentTopMenu(key: string) {
|
||||
// 先判断全等,避免同级路由出现命名包含情况
|
||||
const secParentFullSame = appStore.topMenus.find((el: RouteRecordRaw) => {
|
||||
return key === el?.name;
|
||||
const secParentFullSame = appStore.topMenus.find((route: RouteRecordRaw) => {
|
||||
return key === route?.name;
|
||||
});
|
||||
|
||||
// 非全等的情况下,一定是父子路由包含关系
|
||||
const secParentLike = appStore.topMenus.find((el: RouteRecordRaw) => {
|
||||
return key.includes(el?.name as string);
|
||||
const secParentLike = appStore.topMenus.find((route: RouteRecordRaw) => {
|
||||
return key.includes(route?.name as string);
|
||||
});
|
||||
|
||||
if (secParentFullSame) {
|
||||
|
@ -58,22 +58,35 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* 监听路由变化,存储打开的三级子路由
|
||||
* 监听路由变化,存储打开的顶部菜单
|
||||
*/
|
||||
listenerRouteChange((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 (name && (name as string).includes((el?.name as string) || '')) {
|
||||
const currentParent = el?.children?.find(
|
||||
(item) => name && (name as string).includes((item?.name as string) || '')
|
||||
);
|
||||
if (permission.accessRouter(firstRoute)) {
|
||||
if (name && firstRoute?.name && (name as string).includes(firstRoute.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));
|
||||
setCurrentTopMenu(name as string);
|
||||
return;
|
||||
}
|
||||
// 切换到没有顶部菜单的路由时,清空顶部菜单
|
||||
appStore.setTopMenus([]);
|
||||
setCurrentTopMenu('');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
|
||||
function jumpPath(route: RouteRecordName | undefined) {
|
|
@ -1,19 +1,5 @@
|
|||
<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
|
||||
v-else
|
||||
:class="[
|
||||
'ms-timeline',
|
||||
isArrivedTop ? 'ms-timeline--hidden-top-shadow' : '',
|
||||
|
@ -28,7 +14,7 @@
|
|||
@reach-bottom="handleReachBottom"
|
||||
>
|
||||
<template #item="{ item, index }">
|
||||
<div>
|
||||
<div :class="index === 0 ? 'pt-[12px]' : ''">
|
||||
<a-list-item :key="item.id">
|
||||
<a-timeline-item :dot-color="item.dotColor || 'var(--color-text-input-border)'" :line-type="item.lineType">
|
||||
<slot name="time" :item="item">
|
||||
|
@ -39,7 +25,10 @@
|
|||
</slot>
|
||||
</a-timeline-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>
|
||||
<a-spin v-else />
|
||||
</div>
|
||||
|
@ -108,7 +97,7 @@
|
|||
});
|
||||
|
||||
function handleReachBottom() {
|
||||
if (!props.noMoreData && props.list.length > 0) {
|
||||
if (props.mode === 'remote' && !props.noMoreData && props.list.length > 0) {
|
||||
emit('reachBottom');
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +136,10 @@
|
|||
.timeline-text {
|
||||
color: @color-text-5;
|
||||
}
|
||||
.ms-timeline-content {
|
||||
@apply overflow-auto;
|
||||
.ms-scroll-bar();
|
||||
}
|
||||
}
|
||||
.ms-timeline--hidden-top-shadow {
|
||||
box-shadow: inset 0 -10px 6px -10px rgb(0 0 0 / 15%);
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
import { LOCALE_OPTIONS } from '@/locale';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
// 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 { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants';
|
||||
// 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 usePermission from '@/hooks/usePermission';
|
||||
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';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -19,7 +19,7 @@ Object.keys(_Vmodules).forEach((key) => {
|
|||
|
||||
export default {
|
||||
message: {
|
||||
'menu.workplace': 'Workplace',
|
||||
'menu.workbench': 'Workbench',
|
||||
'menu.testPlan': 'Test plan',
|
||||
'menu.bugManagement': 'Bug management',
|
||||
'menu.featureTest': 'Feature test',
|
||||
|
@ -27,21 +27,23 @@ export default {
|
|||
'menu.uiTest': 'UI test',
|
||||
'menu.performanceTest': 'Performance test',
|
||||
'menu.projectManagement': 'Project management',
|
||||
'menu.projectManagement.log': 'Project Log',
|
||||
'menu.settings': 'Settings',
|
||||
'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.authorizedManagement': 'Authorized Management',
|
||||
'menu.settings.system.pluginmanger': 'Plugin Manger',
|
||||
'menu.settings.system.pluginManager': 'Plugin Manger',
|
||||
'menu.settings.system.user': 'User',
|
||||
'menu.settings.system.organizationAndProject': 'Org & Project',
|
||||
'menu.settings.system.resourcePool': 'Resource Pool',
|
||||
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
|
||||
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
|
||||
'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',
|
||||
...sys,
|
||||
...localeSettings,
|
||||
|
|
|
@ -18,7 +18,7 @@ Object.keys(_Vmodules).forEach((key) => {
|
|||
});
|
||||
export default {
|
||||
message: {
|
||||
'menu.workplace': '工作台',
|
||||
'menu.workbench': '工作台',
|
||||
'menu.testPlan': '测试计划',
|
||||
'menu.bugManagement': '缺陷管理',
|
||||
'menu.featureTest': '功能测试',
|
||||
|
@ -26,21 +26,23 @@ export default {
|
|||
'menu.uiTest': 'UI测试',
|
||||
'menu.performanceTest': '性能测试',
|
||||
'menu.projectManagement': '项目管理',
|
||||
'menu.projectManagement.log': '项目日志',
|
||||
'menu.settings': '系统设置',
|
||||
'menu.settings.system': '系统',
|
||||
'menu.settings.organization': '组织',
|
||||
'menu.settings.organization.member': '成员',
|
||||
'menu.settings.organization.serviceIntegration': '服务集成',
|
||||
'menu.settings.system.user': '用户',
|
||||
'menu.settings.system.usergroup': '用户组',
|
||||
'menu.settings.system.authorizedManagement': '授权管理',
|
||||
'menu.settings.system.pluginmanger': '插件管理',
|
||||
'menu.settings.system.pluginManager': '插件管理',
|
||||
'menu.settings.system.organizationAndProject': '组织与项目',
|
||||
'menu.settings.system.resourcePool': '资源池',
|
||||
'menu.settings.system.resourcePoolDetail': '添加资源池',
|
||||
'menu.settings.system.resourcePoolEdit': '编辑资源池',
|
||||
'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': '切换为中文',
|
||||
...sys,
|
||||
...localeSettings,
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface LogOptions {
|
|||
export interface LogItem {
|
||||
id: string;
|
||||
createUser: string;
|
||||
userName: string;
|
||||
userName: string; // 操作人
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
organizationId: string;
|
||||
|
@ -22,3 +22,21 @@ export interface LogItem {
|
|||
createTime: number;
|
||||
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 DEFAULT_ROUTE_NAME = 'Workplace';
|
||||
export const DEFAULT_ROUTE_NAME = 'Workbench';
|
||||
|
||||
// 默认 tab-bar 路,多页签模式下,打开的第一个页面
|
||||
export const DEFAULT_ROUTE = {
|
||||
title: 'menu.dashboard.workplace',
|
||||
title: 'menu.dashboard.workbench',
|
||||
name: DEFAULT_ROUTE_NAME,
|
||||
fullPath: '/dashboard/workplace',
|
||||
fullPath: '/dashboard/workbench',
|
||||
};
|
||||
|
||||
// 不需要显示项目选择器的模块,数组项为一级路由的path
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const ApiTest: AppRouteRecordRaw = {
|
||||
path: '/api-test',
|
||||
name: menuEnum.APITEST,
|
||||
name: ApiTestRouteEnum.API_TEST,
|
||||
redirect: '/api-test/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const BugManagement: AppRouteRecordRaw = {
|
||||
path: '/bug-management',
|
||||
name: menuEnum.BUGMANAGEMENT,
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT,
|
||||
redirect: '/bug-management/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { FeatureTestRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const FeatureTest: AppRouteRecordRaw = {
|
||||
path: '/feature-test',
|
||||
name: menuEnum.FEATURETEST,
|
||||
name: FeatureTestRouteEnum.FEATURE_TEST,
|
||||
redirect: '/feature-test/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { PerformanceTestRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const PerformanceTest: AppRouteRecordRaw = {
|
||||
path: '/performance-test',
|
||||
name: menuEnum.PERFORMANCE_TEST,
|
||||
name: PerformanceTestRouteEnum.PERFORMANCE_TEST,
|
||||
redirect: '/performance-test/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const ProjectManagement: AppRouteRecordRaw = {
|
||||
path: '/project-management',
|
||||
name: menuEnum.PROJECTMANAGEMENT,
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT,
|
||||
redirect: '/project-management/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
@ -16,12 +17,22 @@ const ProjectManagement: AppRouteRecordRaw = {
|
|||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: 'ProjectManagementIndex',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_INDEX,
|
||||
component: () => import('@/views/project-management/index.vue'),
|
||||
meta: {
|
||||
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 { AppRouteRecordRaw } from '../types';
|
||||
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const Setting: AppRouteRecordRaw = {
|
||||
path: '/setting',
|
||||
name: 'setting',
|
||||
name: SettingRouteEnum.SETTING,
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
locale: 'menu.settings',
|
||||
|
@ -13,7 +15,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
children: [
|
||||
{
|
||||
path: 'system',
|
||||
name: 'settingSystem',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM,
|
||||
redirect: '/setting/system/user',
|
||||
component: null,
|
||||
meta: {
|
||||
|
@ -24,7 +26,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
name: 'settingSystemUser',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_USER,
|
||||
component: () => import('@/views/setting/system/user/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.user',
|
||||
|
@ -34,7 +36,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'usergroup',
|
||||
name: 'settingSystemUsergroup',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_USER_GROUP,
|
||||
component: () => import('@/views/setting/system/usergroup/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.usergroup',
|
||||
|
@ -44,7 +46,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'organization-and-project',
|
||||
name: 'settingSystemOrganization',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_ORGANIZATION,
|
||||
component: () => import('@/views/setting/system/organizationAndProject/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.organizationAndProject',
|
||||
|
@ -54,7 +56,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'parameter',
|
||||
name: 'settingSystemParameter',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_PARAMETER,
|
||||
component: () => import('@/views/setting/system/config/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.parameter',
|
||||
|
@ -64,34 +66,28 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'resourcePool',
|
||||
name: 'settingSystemResourcePool',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL,
|
||||
component: () => import('@/views/setting/system/resourcePool/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.resourcePool',
|
||||
roles: ['*'],
|
||||
isTopMenu: true,
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: 'settingSystemResourcePool',
|
||||
locale: 'menu.settings.system.resourcePool',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'resourcePoolDetail',
|
||||
name: 'settingSystemResourcePoolDetail',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL_DETAIL,
|
||||
component: () => import('@/views/setting/system/resourcePool/detail.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.resourcePoolDetail',
|
||||
roles: ['*'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: 'settingSystemResourcePool',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL,
|
||||
locale: 'menu.settings.system.resourcePool',
|
||||
},
|
||||
{
|
||||
name: 'settingSystemResourcePoolDetail',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL_DETAIL,
|
||||
locale: 'menu.settings.system.resourcePoolDetail',
|
||||
editTag: 'id',
|
||||
editLocale: 'menu.settings.system.resourcePoolEdit',
|
||||
|
@ -101,7 +97,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'authorizedmanagement',
|
||||
name: 'settingSystemAuthorizedManagement',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_AUTHORIZED_MANAGEMENT,
|
||||
component: () => import('@/views/setting/system/authorizedManagement/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.authorizedManagement',
|
||||
|
@ -111,7 +107,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'log',
|
||||
name: 'settingSystemLog',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_LOG,
|
||||
component: () => import('@/views/setting/system/log/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.log',
|
||||
|
@ -120,11 +116,11 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
},
|
||||
{
|
||||
path: 'pluginmanger',
|
||||
name: 'settingSystemPluginManger',
|
||||
path: 'pluginManager',
|
||||
name: SettingRouteEnum.SETTING_SYSTEM_PLUGIN_MANAGEMENT,
|
||||
component: () => import('@/views/setting/system/pluginManager/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.system.pluginmanger',
|
||||
locale: 'menu.settings.system.pluginManager',
|
||||
roles: ['*'],
|
||||
isTopMenu: true,
|
||||
},
|
||||
|
@ -133,7 +129,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'organization',
|
||||
name: 'settingOrganization',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION,
|
||||
redirect: '/setting/organization/member',
|
||||
component: null,
|
||||
meta: {
|
||||
|
@ -144,7 +140,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
children: [
|
||||
{
|
||||
path: 'member',
|
||||
name: 'settingOrganizationMember',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_MEMBER,
|
||||
component: () => import('@/views/setting/organization/member/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.organization.member',
|
||||
|
@ -154,7 +150,7 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
path: 'serviceIntegration',
|
||||
name: 'settingOrganizationService',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_SERVICE,
|
||||
component: () => import('@/views/setting/organization/serviceIntegration/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.organization.serviceIntegration',
|
||||
|
@ -162,6 +158,16 @@ const Setting: AppRouteRecordRaw = {
|
|||
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 { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const TestPlan: AppRouteRecordRaw = {
|
||||
path: '/test-plan',
|
||||
name: menuEnum.TESTPLAN,
|
||||
name: TestPlanRouteEnum.TEST_PLAN,
|
||||
redirect: '/test-plan/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import menuEnum from '@/enums/menuEnum';
|
||||
import { UITestRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const UiTest: AppRouteRecordRaw = {
|
||||
path: '/ui-test',
|
||||
name: menuEnum.UITEST,
|
||||
name: UITestRouteEnum.UI_TEST,
|
||||
redirect: '/ui-test/index',
|
||||
component: DEFAULT_LAYOUT,
|
||||
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 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> =
|
||||
| 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 { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||
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';
|
||||
|
||||
const defaultThemeConfig = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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';
|
||||
|
||||
export interface AppState {
|
||||
|
|
|
@ -172,3 +172,37 @@ export function calculateMaxDepth(arr?: Node[], depth = 0) {
|
|||
|
||||
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 back = () => {
|
||||
// warning: Go to the node that has the permission
|
||||
router.push({ name: 'Workplace' });
|
||||
router.push({ name: 'Workbench' });
|
||||
};
|
||||
</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 AddMemberModal from './components/addMemberModal.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 {
|
||||
getMemberList,
|
||||
deleteMemberReq,
|
||||
|
@ -150,7 +150,7 @@
|
|||
getGlobalUserGroup,
|
||||
} from '@/api/modules/setting/member';
|
||||
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 type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import type {
|
||||
|
|
|
@ -499,7 +499,7 @@
|
|||
updateAuthStatus,
|
||||
deleteAuth,
|
||||
} 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 { characterLimit } from '@/utils';
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@
|
|||
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/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 { testEmail, saveBaseInfo, saveEmailInfo, getBaseInfo, getEmailInfo } from '@/api/modules/setting/config';
|
||||
|
||||
|
|
|
@ -312,7 +312,7 @@
|
|||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import loginForm from '@/views/login/components/login-form.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 MsUpload from '@/components/pure/ms-upload/index.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>
|
||||
<MsCard simple auto-height>
|
||||
<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>
|
||||
<logCards mode="SYSTEM"></logCards>
|
||||
</template>
|
||||
|
||||
<script lang="ts" 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 { 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 lang="tsx" setup>
|
||||
import logCards from './components/logCards.vue';
|
||||
</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);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped></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 {
|
||||
'system.log.operator': '操作人',
|
||||
'system.log.operatorPlaceholder': '请输入用户名/邮箱',
|
||||
'system.log.operatorPlaceholder': '请输入用户名/邮箱搜索',
|
||||
'system.log.operateTime': '操作时间',
|
||||
'system.log.operateRange': '操作范围',
|
||||
'system.log.operateType': '操作类型',
|
||||
|
@ -29,4 +29,7 @@ export default {
|
|||
'system.log.operateType.select': '选择',
|
||||
'system.log.operateType.recover': '恢复',
|
||||
'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 { reactive, ref, watchEffect, computed } from '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 { Message } from '@arco-design/web-vue';
|
||||
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { reactive, ref, watchEffect, computed } from '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 { Message } from '@arco-design/web-vue';
|
||||
import { CreateOrUpdateSystemProjectParams } from '@/models/setting/system/orgAndProject';
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
import { reactive, ref, watchEffect, onUnmounted } from 'vue';
|
||||
import { addUserToOrgOrProject } from '@/api/modules/setting/system/organizationAndProject';
|
||||
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 props = defineProps<{
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import AddUserModal from './addUserModal.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 {
|
||||
visible: boolean;
|
||||
|
|
|
@ -338,7 +338,7 @@
|
|||
import useVisit from '@/hooks/useVisit';
|
||||
import MsCard from '@/components/pure/ms-card/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 JobTemplateDrawer from './components/jobTemplateDrawer.vue';
|
||||
import { getYaml, YamlType, job } from './template';
|
||||
|
@ -348,7 +348,7 @@
|
|||
import { getAllOrgList } from '@/api/modules/setting/orgnization';
|
||||
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';
|
||||
|
||||
const route = useRoute();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<MsCard :loading="loading" has-breadcrumb simple>
|
||||
<MsCard :loading="loading" simple>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<a-button type="primary" @click="addPool">
|
||||
{{ t('system.resourcePool.createPool') }}
|
||||
|
|
|
@ -216,7 +216,7 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsButton from '@/components/pure/ms-button/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 {
|
||||
getUserList,
|
||||
batchCreateUser,
|
||||
|
@ -240,7 +240,7 @@
|
|||
import type { MsTableColumn, BatchActionParams } from '@/components/pure/ms-table/type';
|
||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
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();
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
import { useUserGroupStore } from '@/store';
|
||||
import { getUserList, addUserToUserGroup } from '@/api/modules/setting/usergroup';
|
||||
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 props = defineProps<{
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
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 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