feat(项目管理): 菜单管理列表页静态页面开发
This commit is contained in:
parent
ae33b0d83d
commit
d9cfcec39d
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"globals": {
|
||||||
|
"Component": true,
|
||||||
|
"ComponentPublicInstance": true,
|
||||||
|
"ComputedRef": true,
|
||||||
|
"EffectScope": true,
|
||||||
|
"ExtractDefaultPropTypes": true,
|
||||||
|
"ExtractPropTypes": true,
|
||||||
|
"ExtractPublicPropTypes": true,
|
||||||
|
"InjectionKey": true,
|
||||||
|
"PropType": true,
|
||||||
|
"Ref": true,
|
||||||
|
"VNode": true,
|
||||||
|
"WritableComputedRef": true,
|
||||||
|
"computed": true,
|
||||||
|
"createApp": true,
|
||||||
|
"customRef": true,
|
||||||
|
"defineAsyncComponent": true,
|
||||||
|
"defineComponent": true,
|
||||||
|
"effectScope": true,
|
||||||
|
"getCurrentInstance": true,
|
||||||
|
"getCurrentScope": true,
|
||||||
|
"h": true,
|
||||||
|
"inject": true,
|
||||||
|
"isProxy": true,
|
||||||
|
"isReactive": true,
|
||||||
|
"isReadonly": true,
|
||||||
|
"isRef": true,
|
||||||
|
"markRaw": true,
|
||||||
|
"nextTick": true,
|
||||||
|
"onActivated": true,
|
||||||
|
"onBeforeMount": true,
|
||||||
|
"onBeforeUnmount": true,
|
||||||
|
"onBeforeUpdate": true,
|
||||||
|
"onDeactivated": true,
|
||||||
|
"onErrorCaptured": true,
|
||||||
|
"onMounted": true,
|
||||||
|
"onRenderTracked": true,
|
||||||
|
"onRenderTriggered": true,
|
||||||
|
"onScopeDispose": true,
|
||||||
|
"onServerPrefetch": true,
|
||||||
|
"onUnmounted": true,
|
||||||
|
"onUpdated": true,
|
||||||
|
"provide": true,
|
||||||
|
"reactive": true,
|
||||||
|
"readonly": true,
|
||||||
|
"ref": true,
|
||||||
|
"resolveComponent": true,
|
||||||
|
"shallowReactive": true,
|
||||||
|
"shallowReadonly": true,
|
||||||
|
"shallowRef": true,
|
||||||
|
"toRaw": true,
|
||||||
|
"toRef": true,
|
||||||
|
"toRefs": true,
|
||||||
|
"toValue": true,
|
||||||
|
"triggerRef": true,
|
||||||
|
"unref": true,
|
||||||
|
"useAttrs": true,
|
||||||
|
"useCssModule": true,
|
||||||
|
"useCssVars": true,
|
||||||
|
"useSlots": true,
|
||||||
|
"watch": true,
|
||||||
|
"watchEffect": true,
|
||||||
|
"watchPostEffect": true,
|
||||||
|
"watchSyncEffect": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ module.exports = {
|
||||||
'plugin:import/typescript',
|
'plugin:import/typescript',
|
||||||
'plugin:vue/vue3-recommended',
|
'plugin:vue/vue3-recommended',
|
||||||
'plugin:prettier/recommended',
|
'plugin:prettier/recommended',
|
||||||
|
'./.eslintrc-auto-import.json',
|
||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import svgLoader from 'vite-svg-loader';
|
||||||
// import configArcoResolverPlugin from './plugin/arcoResolver';
|
// import configArcoResolverPlugin from './plugin/arcoResolver';
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||||
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
|
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -22,6 +23,19 @@ export default defineConfig({
|
||||||
// 指定symbolId格式
|
// 指定symbolId格式
|
||||||
symbolId: 'icon-[name]',
|
symbolId: 'icon-[name]',
|
||||||
}),
|
}),
|
||||||
|
AutoImport({
|
||||||
|
include: [
|
||||||
|
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
|
||||||
|
/\.vue$/,
|
||||||
|
/\.vue\?vue/, // .vue
|
||||||
|
/\.md$/, // .md
|
||||||
|
],
|
||||||
|
imports: ['vue'],
|
||||||
|
dts: 'src/auto-import.d.ts',
|
||||||
|
eslintrc: {
|
||||||
|
enabled: true, // <-- this
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [
|
alias: [
|
||||||
|
|
|
@ -124,6 +124,7 @@
|
||||||
"stylelint-order": "^5.0.0",
|
"stylelint-order": "^5.0.0",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
|
"unplugin-auto-import": "^0.16.6",
|
||||||
"unplugin-vue-components": "^0.24.1",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
"vite": "^3.2.7",
|
"vite": "^3.2.7",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
|
|
@ -1,28 +1,19 @@
|
||||||
import MSR from '@/api/http/index';
|
import MSR from '@/api/http/index';
|
||||||
import * as Url from '@/api/requrls/project-management/menuManagement';
|
import * as Url from '@/api/requrls/project-management/menuManagement';
|
||||||
import type { MenuTableListItem, MenuTableListParams } from '@/models/projectManagement/menuManagement';
|
import type {
|
||||||
|
MenuTableListItem,
|
||||||
|
MenuTableListParams,
|
||||||
|
MenuTableConfigItem,
|
||||||
|
} from '@/models/projectManagement/menuManagement';
|
||||||
import { MenuEnum } from '@/enums/commonEnum';
|
import { MenuEnum } from '@/enums/commonEnum';
|
||||||
|
|
||||||
import { TableQueryParams, CommonList } from '@/models/common';
|
import { TableQueryParams, CommonList } from '@/models/common';
|
||||||
|
|
||||||
const tableList: MenuTableListItem[] = [
|
export async function postTabletList(params: TableQueryParams): Promise<CommonList<MenuTableListItem>> {
|
||||||
'workstation',
|
const list = await MSR.get<MenuTableListItem[]>({ url: `${Url.getMenuListUrl}${params.projectId}` });
|
||||||
'loadTest',
|
|
||||||
'testPlan',
|
|
||||||
'bugManagement',
|
|
||||||
'caseManagement',
|
|
||||||
'apiTest',
|
|
||||||
'uiTest',
|
|
||||||
].map((item, index) => {
|
|
||||||
return { module: item, moduleEnable: index % 2 === 0 };
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
export function postTabletList(data: TableQueryParams) {
|
|
||||||
// return MSR.post<CommonList<MenuTableListItem>>({ url: Url.getMenuListUrl, data });
|
|
||||||
const result: CommonList<MenuTableListItem> = {
|
const result: CommonList<MenuTableListItem> = {
|
||||||
list: tableList,
|
total: list.length,
|
||||||
total: tableList.length,
|
list,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
current: 1,
|
current: 1,
|
||||||
};
|
};
|
||||||
|
@ -54,7 +45,33 @@ export function postUpdateMenu(data: MenuTableListParams) {
|
||||||
suffix = 'performance-test';
|
suffix = 'performance-test';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return MSR.post<string>({ url: `${Url.updateConfigByMenuTypeUrl}${suffix}`, data });
|
return MSR.post<MenuTableListItem>({ url: `${Url.updateConfigByMenuTypeUrl}${suffix}`, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {};
|
export function getConfigByMenuItem(data: MenuTableListParams) {
|
||||||
|
let suffix = '';
|
||||||
|
switch (data.type) {
|
||||||
|
case MenuEnum.workstation:
|
||||||
|
suffix = 'workstation';
|
||||||
|
break;
|
||||||
|
case MenuEnum.testPlan:
|
||||||
|
suffix = 'test-plan';
|
||||||
|
break;
|
||||||
|
case MenuEnum.bugManagement:
|
||||||
|
suffix = 'issue';
|
||||||
|
break;
|
||||||
|
case MenuEnum.caseManagement:
|
||||||
|
suffix = 'case';
|
||||||
|
break;
|
||||||
|
case MenuEnum.apiTest:
|
||||||
|
suffix = 'api';
|
||||||
|
break;
|
||||||
|
case MenuEnum.uiTest:
|
||||||
|
suffix = 'ui';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
suffix = 'performance-test';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return MSR.post<MenuTableConfigItem[]>({ url: `${Url.getConfigByMenuTypeUrl}${suffix}`, data });
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope'];
|
||||||
|
const computed: typeof import('vue')['computed'];
|
||||||
|
const createApp: typeof import('vue')['createApp'];
|
||||||
|
const customRef: typeof import('vue')['customRef'];
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'];
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent'];
|
||||||
|
const effectScope: typeof import('vue')['effectScope'];
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance'];
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope'];
|
||||||
|
const h: typeof import('vue')['h'];
|
||||||
|
const inject: typeof import('vue')['inject'];
|
||||||
|
const isProxy: typeof import('vue')['isProxy'];
|
||||||
|
const isReactive: typeof import('vue')['isReactive'];
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly'];
|
||||||
|
const isRef: typeof import('vue')['isRef'];
|
||||||
|
const markRaw: typeof import('vue')['markRaw'];
|
||||||
|
const nextTick: typeof import('vue')['nextTick'];
|
||||||
|
const onActivated: typeof import('vue')['onActivated'];
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount'];
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'];
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'];
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated'];
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured'];
|
||||||
|
const onMounted: typeof import('vue')['onMounted'];
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked'];
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered'];
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose'];
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch'];
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted'];
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated'];
|
||||||
|
const provide: typeof import('vue')['provide'];
|
||||||
|
const reactive: typeof import('vue')['reactive'];
|
||||||
|
const readonly: typeof import('vue')['readonly'];
|
||||||
|
const ref: typeof import('vue')['ref'];
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent'];
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive'];
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly'];
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef'];
|
||||||
|
const toRaw: typeof import('vue')['toRaw'];
|
||||||
|
const toRef: typeof import('vue')['toRef'];
|
||||||
|
const toRefs: typeof import('vue')['toRefs'];
|
||||||
|
const toValue: typeof import('vue')['toValue'];
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef'];
|
||||||
|
const unref: typeof import('vue')['unref'];
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs'];
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule'];
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars'];
|
||||||
|
const useSlots: typeof import('vue')['useSlots'];
|
||||||
|
const watch: typeof import('vue')['watch'];
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect'];
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect'];
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect'];
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type {
|
||||||
|
Component,
|
||||||
|
ComponentPublicInstance,
|
||||||
|
ComputedRef,
|
||||||
|
ExtractDefaultPropTypes,
|
||||||
|
ExtractPropTypes,
|
||||||
|
ExtractPublicPropTypes,
|
||||||
|
InjectionKey,
|
||||||
|
PropType,
|
||||||
|
Ref,
|
||||||
|
VNode,
|
||||||
|
WritableComputedRef,
|
||||||
|
} from 'vue';
|
||||||
|
}
|
|
@ -37,7 +37,7 @@
|
||||||
import { addOrgUserToUserGroup, addUserToUserGroup } from '@/api/modules/setting/usergroup';
|
import { addOrgUserToUserGroup, addUserToUserGroup } from '@/api/modules/setting/usergroup';
|
||||||
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/business/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
import { AuthScopeEnum } from '@/enums/commonEnum';
|
import { AuthScopeEnum } from '@/enums/commonEnum';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
const userSelectorProps = computed(() => {
|
const userSelectorProps = computed(() => {
|
||||||
if (systemType === AuthScopeEnum.SYSTEM) {
|
if (systemType === AuthScopeEnum.SYSTEM) {
|
||||||
return {
|
return {
|
||||||
type: UserRequesetTypeEnum.SYSTEM_USER_GROUP,
|
type: UserRequestTypeEnum.SYSTEM_USER_GROUP,
|
||||||
loadOptionParams: {
|
loadOptionParams: {
|
||||||
roleId: props.currentId,
|
roleId: props.currentId,
|
||||||
},
|
},
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: UserRequesetTypeEnum.ORGANIZATION_USER_GROUP,
|
type: UserRequestTypeEnum.ORGANIZATION_USER_GROUP,
|
||||||
loadOptionParams: {
|
loadOptionParams: {
|
||||||
roleId: props.currentId,
|
roleId: props.currentId,
|
||||||
organizationId: currentOrgId.value,
|
organizationId: currentOrgId.value,
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import { ref, onMounted, computed } from 'vue';
|
||||||
import initOptionsFunc, { UserRequesetTypeEnum } from './utils';
|
import initOptionsFunc, { UserRequestTypeEnum } from './utils';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
export interface MsUserSelectorOption {
|
export interface MsUserSelectorOption {
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
firstLabelKey?: string; // 首要的的字段key
|
firstLabelKey?: string; // 首要的的字段key
|
||||||
secondLabelKey?: string; // 次要的字段key
|
secondLabelKey?: string; // 次要的字段key
|
||||||
loadOptionParams?: Record<string, any>; // 加载选项的参数
|
loadOptionParams?: Record<string, any>; // 加载选项的参数
|
||||||
type?: UserRequesetTypeEnum; // 加载选项的类型
|
type?: UserRequestTypeEnum; // 加载选项的类型
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
valueKey: 'id',
|
valueKey: 'id',
|
||||||
firstLabelKey: 'name',
|
firstLabelKey: 'name',
|
||||||
secondLabelKey: 'email',
|
secondLabelKey: 'email',
|
||||||
type: UserRequesetTypeEnum.SYSTEM_USER_GROUP,
|
type: UserRequestTypeEnum.SYSTEM_USER_GROUP,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { getProjectMemberOptions } from '@/api/modules/project-management/projec
|
||||||
import { getProjectUserGroupOptions } from '@/api/modules/project-management/usergroup';
|
import { getProjectUserGroupOptions } from '@/api/modules/project-management/usergroup';
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
export enum UserRequesetTypeEnum {
|
export enum UserRequestTypeEnum {
|
||||||
SYSTEM_USER_GROUP = 'SYSTEM_USER_GROUP',
|
SYSTEM_USER_GROUP = 'SYSTEM_USER_GROUP',
|
||||||
SYSTEM_ORGANIZATION = 'SYSTEM_ORGANIZATION',
|
SYSTEM_ORGANIZATION = 'SYSTEM_ORGANIZATION',
|
||||||
SYSTEM_ORGANIZATION_ADMIN = 'SYSTEM_ORGANIZATION_ADMIN',
|
SYSTEM_ORGANIZATION_ADMIN = 'SYSTEM_ORGANIZATION_ADMIN',
|
||||||
|
@ -26,43 +26,43 @@ export enum UserRequesetTypeEnum {
|
||||||
PROJECT_USER_GROUP = 'PROJECT_USER_GROUP',
|
PROJECT_USER_GROUP = 'PROJECT_USER_GROUP',
|
||||||
}
|
}
|
||||||
export default function initOptionsFunc(type: string, params: Record<string, any>) {
|
export default function initOptionsFunc(type: string, params: Record<string, any>) {
|
||||||
if (type === UserRequesetTypeEnum.SYSTEM_USER_GROUP) {
|
if (type === UserRequestTypeEnum.SYSTEM_USER_GROUP) {
|
||||||
// 系统 - 用户组-添加成员-下拉选项
|
// 系统 - 用户组-添加成员-下拉选项
|
||||||
return getSystemUserGroupOption(params.roleId, params.keyword);
|
return getSystemUserGroupOption(params.roleId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.ORGANIZATION_USER_GROUP) {
|
if (type === UserRequestTypeEnum.ORGANIZATION_USER_GROUP) {
|
||||||
// 组织 - 用户组-添加成员-下拉选项
|
// 组织 - 用户组-添加成员-下拉选项
|
||||||
return getOrgUserGroupOption(params.organizationId, params.roleId, params.keyword);
|
return getOrgUserGroupOption(params.organizationId, params.roleId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_ADMIN || type === UserRequesetTypeEnum.SYSTEM_PROJECT_ADMIN) {
|
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION_ADMIN || type === UserRequestTypeEnum.SYSTEM_PROJECT_ADMIN) {
|
||||||
// 系统 - 【组织 或 项目】-添加管理员-下拉选项
|
// 系统 - 【组织 或 项目】-添加管理员-下拉选项
|
||||||
return getAdminByOrganizationOrProject(params.keyword);
|
return getAdminByOrganizationOrProject(params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION || type === UserRequesetTypeEnum.SYSTEM_PROJECT) {
|
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION || type === UserRequestTypeEnum.SYSTEM_PROJECT) {
|
||||||
// 系统 -【组织 或 项目】-添加成员-下拉选项
|
// 系统 -【组织 或 项目】-添加成员-下拉选项
|
||||||
return getUserByOrganizationOrProject(params.sourceId, params.keyword);
|
return getUserByOrganizationOrProject(params.sourceId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.ORGANIZATION_PROJECT) {
|
if (type === UserRequestTypeEnum.ORGANIZATION_PROJECT) {
|
||||||
// 组织 - 项目-添加成员-下拉选项
|
// 组织 - 项目-添加成员-下拉选项
|
||||||
return getUserByProjectByOrg(params.organizationId, params.projectId, params.keyword);
|
return getUserByProjectByOrg(params.organizationId, params.projectId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.ORGANIZATION_PROJECT_ADMIN) {
|
if (type === UserRequestTypeEnum.ORGANIZATION_PROJECT_ADMIN) {
|
||||||
// 组织 - 项目-添加管理员-下拉选项
|
// 组织 - 项目-添加管理员-下拉选项
|
||||||
return getAdminByProjectByOrg(params.organizationId, params.keyword);
|
return getAdminByProjectByOrg(params.organizationId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_PROJECT) {
|
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT) {
|
||||||
// 系统-组织-组织项目
|
// 系统-组织-组织项目
|
||||||
return getProjectList(params.organizationId, params.keyword);
|
return getProjectList(params.organizationId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_MEMBER) {
|
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION_MEMBER) {
|
||||||
// 系统-组织-组织成员
|
// 系统-组织-组织成员
|
||||||
return getUser(params.organizationId, params.keyword);
|
return getUser(params.organizationId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.PROJECT_PERMISSION_MEMBER) {
|
if (type === UserRequestTypeEnum.PROJECT_PERMISSION_MEMBER) {
|
||||||
// 项目-项目成员
|
// 项目-项目成员
|
||||||
return getProjectMemberOptions(params.projectId, params.keyword);
|
return getProjectMemberOptions(params.projectId, params.keyword);
|
||||||
}
|
}
|
||||||
if (type === UserRequesetTypeEnum.PROJECT_USER_GROUP) {
|
if (type === UserRequestTypeEnum.PROJECT_USER_GROUP) {
|
||||||
// 项目-用户组
|
// 项目-用户组
|
||||||
return getProjectUserGroupOptions(params.projectId, params.userRoleId, params.keyword);
|
return getProjectUserGroupOptions(params.projectId, params.userRoleId, params.keyword);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
:row-class="getRowClass"
|
:row-class="getRowClass"
|
||||||
:span-method="spanMethod"
|
:span-method="spanMethod"
|
||||||
:columns="currentColumns"
|
:columns="currentColumns"
|
||||||
:expanded-keys="props.expandedKeys"
|
@expand="(rowKey, record) => emit('expand', record)"
|
||||||
@sorter-change="(dataIndex: string,direction: string) => handleSortChange(dataIndex, direction)"
|
@sorter-change="(dataIndex: string,direction: string) => handleSortChange(dataIndex, direction)"
|
||||||
>
|
>
|
||||||
<template #optional="{ rowIndex, record }">
|
<template #optional="{ rowIndex, record }">
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
<template v-else-if="item.isTag">
|
<template v-else-if="item.isTag">
|
||||||
<div class="one-line-text max-w-[456px]">
|
<div class="one-line-text max-w-[456px]">
|
||||||
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
|
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
|
||||||
{{ record[item.dataIndex as string] || '-' }}
|
{{ record[item.dataIndex as string] || (attrs.emptyDataShowLine ? '-' : '') }}
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
<a-tooltip v-else placement="top" :content="String(record[item.dataIndex as string])">
|
<a-tooltip v-else placement="top" :content="String(record[item.dataIndex as string])">
|
||||||
<div class="one-line-text max-w-[300px]">
|
<div class="one-line-text max-w-[300px]">
|
||||||
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
|
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
|
||||||
{{ record[item.dataIndex as string] || '-' }}
|
{{ record[item.dataIndex as string] || (attrs.emptyDataShowLine ? '-' : '') }}
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
|
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
|
||||||
{{ record[item.dataIndex as string] || '-' }}
|
{{ record[item.dataIndex as string] || (attrs.emptyDataShowLine ? '-' : '') }}
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -145,21 +145,9 @@
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #expand-icon="{ expanded, record }">
|
<template #expand-icon="{ expanded }">
|
||||||
<MsIcon
|
<MsIcon v-if="!expanded" :size="8" type="icon-icon_right_outlined" class="text-[var(--color-text-4)]" />
|
||||||
v-if="!expanded"
|
<MsIcon v-else :size="8" class="text-[rgb(var(--primary-6))]" type="icon-icon_down_outlined" />
|
||||||
:size="8"
|
|
||||||
type="icon-icon_right_outlined"
|
|
||||||
class="text-[rgb(var(--primary-6))]"
|
|
||||||
@click="emit('expandChange', record[rowKey || 'id'])"
|
|
||||||
/>
|
|
||||||
<MsIcon
|
|
||||||
v-else
|
|
||||||
:size="8"
|
|
||||||
class="text-[var(--color-text-4)]"
|
|
||||||
type="icon-icon_down_outlined"
|
|
||||||
@click="emit('expandChange', record[rowKey || 'id'])"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
<div
|
<div
|
||||||
|
@ -208,7 +196,7 @@
|
||||||
import type { TableData } from '@arco-design/web-vue';
|
import type { TableData } from '@arco-design/web-vue';
|
||||||
import MsCheckbox from '../ms-checkbox/MsCheckbox.vue';
|
import MsCheckbox from '../ms-checkbox/MsCheckbox.vue';
|
||||||
|
|
||||||
const batchleft = ref('10px');
|
const batchLeft = ref('10px');
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
const currentColumns = ref<MsTableColumn>([]);
|
const currentColumns = ref<MsTableColumn>([]);
|
||||||
|
@ -233,8 +221,8 @@
|
||||||
(e: 'rowSelectChange', key: string): void;
|
(e: 'rowSelectChange', key: string): void;
|
||||||
(e: 'selectAllChange', value: SelectAllEnum): void;
|
(e: 'selectAllChange', value: SelectAllEnum): void;
|
||||||
(e: 'sorterChange', value: { [key: string]: string }): void;
|
(e: 'sorterChange', value: { [key: string]: string }): void;
|
||||||
|
(e: 'expand', record: TableData): void;
|
||||||
(e: 'clearSelector'): void;
|
(e: 'clearSelector'): void;
|
||||||
(e: 'expandChange', key: string): void;
|
|
||||||
}>();
|
}>();
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
|
|
||||||
|
@ -418,7 +406,7 @@
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initColumn();
|
initColumn();
|
||||||
batchleft.value = getBatchLeft();
|
batchLeft.value = getBatchLeft();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ColumnEditTypeEnum, SelectAllEnum } from '@/enums/tableEnum';
|
import { ColumnEditTypeEnum, SelectAllEnum } from '@/enums/tableEnum';
|
||||||
import { TableQueryParams } from '@/models/common';
|
import { TableQueryParams } from '@/models/common';
|
||||||
import { TableColumnData, TableData, TableDraggable, TableChangeExtra, TableExpandable } from '@arco-design/web-vue';
|
import { TableColumnData, TableData, TableDraggable, TableChangeExtra } from '@arco-design/web-vue';
|
||||||
|
|
||||||
export interface MsPaginationI {
|
export interface MsPaginationI {
|
||||||
current: number;
|
current: number;
|
||||||
|
@ -77,7 +77,7 @@ export interface MsTableProps<T> {
|
||||||
/** 展开行相关 */
|
/** 展开行相关 */
|
||||||
showExpand?: boolean; // 是否显示展开行
|
showExpand?: boolean; // 是否显示展开行
|
||||||
expandedKeys?: string[]; // 显示的展开行、子树(受控模式)
|
expandedKeys?: string[]; // 显示的展开行、子树(受控模式)
|
||||||
|
emptyDataShowLine?: boolean; // 空数据是否显示 "-"
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ export interface BatchActionConfig {
|
||||||
moreAction?: BatchActionParams[];
|
moreAction?: BatchActionParams[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface renamePopconfirmVisibleType {
|
export interface renamePopConfirmVisibleType {
|
||||||
[key: string]: boolean;
|
[key: string]: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default function useTableProps<T>(
|
||||||
data: [], // 表格数据
|
data: [], // 表格数据
|
||||||
/**
|
/**
|
||||||
* 表格列配置
|
* 表格列配置
|
||||||
* 当showSetting为true时,此配置无效,通过TableStore.initColumnsk(tableKey: string, column: MsTableColumn)初始化。
|
* 当showSetting为true时,此配置无效,通过TableStore.initColumn(tableKey: string, column: MsTableColumn)初始化。
|
||||||
* 当showSetting为false时,此配置生效
|
* 当showSetting为false时,此配置生效
|
||||||
*/
|
*/
|
||||||
columns: [] as MsTableColumn,
|
columns: [] as MsTableColumn,
|
||||||
|
@ -59,7 +59,7 @@ export default function useTableProps<T>(
|
||||||
showFirstOperation: false, // 展示第一行的操作
|
showFirstOperation: false, // 展示第一行的操作
|
||||||
/** 展开行相关 */
|
/** 展开行相关 */
|
||||||
showExpand: false, // 是否显示展开行
|
showExpand: false, // 是否显示展开行
|
||||||
expandedKeys: [], // 显示的展开行、子树(受控模式)
|
emptyDataShowLine: true, // 空数据是否显示 "-"
|
||||||
...props,
|
...props,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -351,18 +351,6 @@ export default function useTableProps<T>(
|
||||||
propsRes.value.selectedKeys = selectedKeys;
|
propsRes.value.selectedKeys = selectedKeys;
|
||||||
propsRes.value.excludeKeys = excludeKeys;
|
propsRes.value.excludeKeys = excludeKeys;
|
||||||
},
|
},
|
||||||
// 展开收起
|
|
||||||
expandChange: (key: string) => {
|
|
||||||
const { expandedKeys: oldExpandedKeys } = propsRes.value;
|
|
||||||
if (!oldExpandedKeys) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (oldExpandedKeys.includes(key)) {
|
|
||||||
propsRes.value.expandedKeys = oldExpandedKeys.filter((item) => item !== key);
|
|
||||||
} else {
|
|
||||||
propsRes.value.expandedKeys = [...oldExpandedKeys, key];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<a-input>
|
||||||
|
<template #append> </template>
|
||||||
|
</a-input>
|
||||||
|
</template>
|
|
@ -0,0 +1 @@
|
||||||
|
export default {};
|
|
@ -0,0 +1 @@
|
||||||
|
export default {};
|
|
@ -1,14 +1,19 @@
|
||||||
import { MenuEnum } from '@/enums/commonEnum';
|
import { MenuEnum } from '@/enums/commonEnum';
|
||||||
|
|
||||||
|
export interface MenuTableConfigItem {
|
||||||
|
projectId: string;
|
||||||
|
type: string;
|
||||||
|
typeValue: string;
|
||||||
|
}
|
||||||
export interface MenuTableListItem {
|
export interface MenuTableListItem {
|
||||||
module: string;
|
module: string;
|
||||||
moduleEnable: boolean;
|
moduleEnable: boolean;
|
||||||
moduleDesc?: string;
|
moduleDesc?: string;
|
||||||
children?: MenuTableListItem[];
|
children?: MenuTableConfigItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MenuTableListParams {
|
export interface MenuTableListParams {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
type: MenuEnum;
|
type?: MenuEnum;
|
||||||
typeValue: boolean;
|
typeValue?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.userIds"
|
v-model:value="form.userIds"
|
||||||
:load-option-params="{ projectId: lastProjectId }"
|
:load-option-params="{ projectId: lastProjectId }"
|
||||||
:type="UserRequesetTypeEnum.PROJECT_PERMISSION_MEMBER"
|
:type="UserRequestTypeEnum.PROJECT_PERMISSION_MEMBER"
|
||||||
placeholder="project.member.selectMemberScope"
|
placeholder="project.member.selectMemberScope"
|
||||||
disabled-key="memberFlag"
|
disabled-key="memberFlag"
|
||||||
/>
|
/>
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
ActionProjectMember,
|
ActionProjectMember,
|
||||||
AddProjectMember,
|
AddProjectMember,
|
||||||
} from '@/models/projectManagement/projectAndPermission';
|
} from '@/models/projectManagement/projectAndPermission';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="flex h-[54px] flex-row items-center pl-[61px]">
|
|
||||||
<div class="title w-[251px] py-[16px] pl-[36px] pr-[8px]"> 报告保留时间范围 </div>
|
|
||||||
<div class="w-[547px] px-[16px]">
|
|
||||||
<a-input :style="{ width: '320px' }" placeholder="Please enter something" allow-clear>
|
|
||||||
<template #append> RMB </template>
|
|
||||||
</a-input>
|
|
||||||
</div>
|
|
||||||
<div class="w-[90px] p-[16px]">
|
|
||||||
<a-switch />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { MenuTableListItem } from '@/models/projectManagement/menuManagement';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
record: MenuTableListItem;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<template> todo </template>
|
||||||
|
|
||||||
|
<script lang="ts" setup></script>
|
|
@ -1,40 +1,182 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-row items-center">
|
<div class="flex flex-row items-center">
|
||||||
<div class="text-[var(--color-text-1)]"> {{ t('project.menu.management') }}</div>
|
<div class="text-[var(--color-text-1)]"> {{ t('project.menu.management') }}</div>
|
||||||
<a-tooltip :content="t('project.menu.manageTip')">
|
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
|
||||||
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
|
<div>
|
||||||
|
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
|
||||||
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
|
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent" @expand="expandChange">
|
||||||
<template #module="{ record }">
|
<template #module="{ record }">
|
||||||
<MsIcon v-if="record.children" class="text-[var(--color-text-4)]" :type="getMenuIcon(record.module)" />
|
<div v-if="record.children">
|
||||||
<span class="ml-[4px]">{{ t(`menu.${record.module}`) }}</span>
|
<MsIcon class="text-[var(--color-text-4)]" :type="getMenuIcon(record.module)" />
|
||||||
|
<span class="ml-[4px]">{{ t(`menu.${record.module}`) }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span class="ml-[4px]">{{ t(`project.menu.${record.type}`) }}</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #moduleEnable="{ record }">
|
<template #moduleEnable="{ record }">
|
||||||
<a-switch v-model="record.moduleEnable" @change="handleMenuStatusChange(record)" />
|
<a-switch v-if="record.children" v-model="record.moduleEnable" @change="handleMenuStatusChange(record)" />
|
||||||
|
<a-switch
|
||||||
|
v-else-if="showEnableConfigList.includes(record.type)"
|
||||||
|
v-model="record.moduleEnable"
|
||||||
|
@change="handleMenuStatusChange(record)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #description="{ record }">
|
||||||
|
<div v-if="record.type === 'WORKSTATION_SYNC_RULE'">
|
||||||
|
<!-- 工作台 接口测试待更新同步规则 -->
|
||||||
|
{{ t('project.menu.row1') }}
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'TEST_PLAN_CLEAN_REPORT'">
|
||||||
|
<!-- 测试计划 报告保留时间范围 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'TEST_PLAN_SHARE_REPORT'">
|
||||||
|
<!-- 测试计划 报告链接有效期 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'ISSUE_SYNC'">
|
||||||
|
<!-- 缺陷同步 -->
|
||||||
|
<span>{{ t('project.menu.row2') }}</span>
|
||||||
|
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.sd') }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'CASE_PUBLIC'">
|
||||||
|
<!-- 用例 公共用例库 -->
|
||||||
|
{{ t('project.menu.row3') }}
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'CASE_RE_REVIEW'" class="flex flex-row">
|
||||||
|
<!-- 用例 关联需求 -->
|
||||||
|
<div>{{ t('project.menu.row4') }}</div>
|
||||||
|
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.rr') }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'CASE_RELATED'">
|
||||||
|
<!-- 用例 重新提审 -->
|
||||||
|
{{ t('project.menu.row5') }}
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_URL_REPEATABLE'">
|
||||||
|
<!-- 接口 -->
|
||||||
|
{{ t('project.menu.row6') }}
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_CLEAN_REPORT'">
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_SHARE_REPORT'">
|
||||||
|
<!-- 报告链接有效期 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_RESOURCE_POOL'" class="flex flex-row items-center">
|
||||||
|
<!-- 执行资源池 -->
|
||||||
|
<a-select v-model="record.typeValue" />
|
||||||
|
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
|
||||||
|
<div>
|
||||||
|
<MsIcon class="ml-[4px] text-[var(--color-text-4)]" type="icon-icon-maybe_outlined" />
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_SCRIPT_REVIEWER'" class="flex flex-row items-center">
|
||||||
|
<!-- 脚本审核 -->
|
||||||
|
<a-select v-model="record.typeValue" />
|
||||||
|
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
|
||||||
|
<div>
|
||||||
|
<MsIcon class="ml-[4px] text-[var(--color-text-4)]" type="icon-icon-maybe_outlined" />
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_ERROR_REPORT_RULE'" class="flex w-[100%] flex-row items-center">
|
||||||
|
<!-- 误报规则 -->
|
||||||
|
<a-select v-model="record.typeValue" class="w-[290px]" />
|
||||||
|
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.far') }}</div>
|
||||||
|
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
|
||||||
|
<div>
|
||||||
|
<MsIcon class="ml-[4px] text-[var(--color-text-4)]" type="icon-icon-maybe_outlined" />
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'API_SYNC_CASE'">{{ t('project.menu.row7') }} </div>
|
||||||
|
<div v-if="record.type === 'UI_CLEAN_REPORT'">
|
||||||
|
<!--UI 报告保留时间范围 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'UI_SHARE_REPORT'">
|
||||||
|
<!--UI 报告链接有效期 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'UI_RESOURCE_POOL'" class="flex flex-row items-center">
|
||||||
|
<!--UI 执行资源池 -->
|
||||||
|
<a-select v-model="record.typeValue" />
|
||||||
|
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
|
||||||
|
<div>
|
||||||
|
<MsIcon class="ml-[4px] text-[var(--color-text-4)]" type="icon-icon-maybe_outlined" />
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'PERFORMANCE_TEST_CLEAN_REPORT'">
|
||||||
|
<!--性能测试 报告保留时间范围 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'PERFORMANCE_TEST_SHARE_REPORT'">
|
||||||
|
<!--UI 报告链接有效期 -->
|
||||||
|
<a-input v-model="record.typeValue" />
|
||||||
|
</div>
|
||||||
|
<div v-if="record.type === 'PERFORMANCE_TEST_SCRIPT_REVIEWER'" class="flex flex-row items-center">
|
||||||
|
<!--UI 脚本审核 -->
|
||||||
|
<a-select v-model="record.typeValue" />
|
||||||
|
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
|
||||||
|
<div>
|
||||||
|
<MsIcon class="ml-[4px] text-[var(--color-text-4)]" type="icon-icon-maybe_outlined" />
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MsBaseTable>
|
</MsBaseTable>
|
||||||
|
<MsDrawer
|
||||||
|
v-model:visible="defectDrawerVisible"
|
||||||
|
title="缺陷同步"
|
||||||
|
:width="600"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:closable="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
:footer="null"
|
||||||
|
:get-container="false"
|
||||||
|
:body-style="{ padding: '0px' }"
|
||||||
|
>
|
||||||
|
<DefectSync />
|
||||||
|
</MsDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/**
|
/**
|
||||||
* @description 项目管理-项目与权限-菜单管理
|
* @description 项目管理-项目与权限-菜单管理
|
||||||
*/
|
*/
|
||||||
import { onMounted, computed } from 'vue';
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import { postTabletList, postUpdateMenu } from '@/api/modules/project-management/menuManagement';
|
import { postTabletList, postUpdateMenu, getConfigByMenuItem } from '@/api/modules/project-management/menuManagement';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { MenuTableListItem } from '@/models/projectManagement/menuManagement';
|
import { MenuTableListItem } from '@/models/projectManagement/menuManagement';
|
||||||
import { MenuEnum } from '@/enums/commonEnum';
|
import { MenuEnum } from '@/enums/commonEnum';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message, TableData } from '@arco-design/web-vue';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const defectDrawerVisible = ref(false);
|
||||||
|
|
||||||
|
const showEnableConfigList = [
|
||||||
|
'WORKSTATION_SYNC_RULE',
|
||||||
|
'ISSUE_SYNC',
|
||||||
|
'CASE_PUBLIC',
|
||||||
|
'CASE_RE_REVIEW',
|
||||||
|
'CASE_RELATED',
|
||||||
|
'API_URL_REPEATABLE',
|
||||||
|
'API_SYNC_CASE',
|
||||||
|
'PERFORMANCE_TEST_SCRIPT_REVIEWER',
|
||||||
|
];
|
||||||
|
|
||||||
const columns: MsTableColumn = [
|
const columns: MsTableColumn = [
|
||||||
{
|
{
|
||||||
|
@ -68,18 +210,12 @@
|
||||||
noDisable: true,
|
noDisable: true,
|
||||||
rowKey: 'module',
|
rowKey: 'module',
|
||||||
showExpand: true,
|
showExpand: true,
|
||||||
|
emptyDataShowLine: false,
|
||||||
},
|
},
|
||||||
(item) => {
|
(item) => ({
|
||||||
item = {
|
...item,
|
||||||
...item,
|
children: [],
|
||||||
children: [
|
})
|
||||||
{
|
|
||||||
...item,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const getMenuIcon = (type: MenuEnum) => {
|
const getMenuIcon = (type: MenuEnum) => {
|
||||||
|
@ -118,6 +254,19 @@
|
||||||
await loadList();
|
await loadList();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const expandChange = async (record: TableData) => {
|
||||||
|
if (record.children && record.children.length > 0) {
|
||||||
|
// 有子项
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
record.children =
|
||||||
|
(await getConfigByMenuItem({ projectId: currentProjectId.value, type: record.module as MenuEnum })) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const showDefectDrawer = () => {
|
||||||
|
defectDrawerVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setLoadListParams({ projectId: currentProjectId.value });
|
setLoadListParams({ projectId: currentProjectId.value });
|
||||||
fetchData();
|
fetchData();
|
||||||
|
|
|
@ -4,4 +4,40 @@ export default {
|
||||||
'可根据使用场景配置各功能开关关闭后,将隐藏功能入口,成员无法访问该功能和数据;已产生的数据不够此规则影响;再次开启时,即恢复至关闭前状态',
|
'可根据使用场景配置各功能开关关闭后,将隐藏功能入口,成员无法访问该功能和数据;已产生的数据不够此规则影响;再次开启时,即恢复至关闭前状态',
|
||||||
'project.menu.name': '菜单名称',
|
'project.menu.name': '菜单名称',
|
||||||
'project.menu.description': '描述',
|
'project.menu.description': '描述',
|
||||||
|
|
||||||
|
'project.menu.WORKSTATION_SYNC_RULE': '接口测试待更新同步规则',
|
||||||
|
'project.menu.TEST_PLAN_CLEAN_REPORT': '报告保留时间范围',
|
||||||
|
'project.menu.TEST_PLAN_SHARE_REPORT': '报告链接有效期',
|
||||||
|
'project.menu.UI_CLEAN_REPORT': '报告保留时间范围',
|
||||||
|
'project.menu.UI_SHARE_REPORT': '报告链接有效期',
|
||||||
|
'project.menu.UI_RESOURCE_POOL': '执行资源池',
|
||||||
|
'project.menu.PERFORMANCE_TEST_CLEAN_REPORT': '报告保留时间范围',
|
||||||
|
'project.menu.PERFORMANCE_TEST_SHARE_REPORT': '报告链接有效期',
|
||||||
|
'project.menu.PERFORMANCE_TEST_SCRIPT_REVIEWER': '脚本审核',
|
||||||
|
'project.menu.API_URL_REPEATABLE': '接口定义 URL 可重复 ',
|
||||||
|
'project.menu.API_CLEAN_REPORT': '报告保留时间范围',
|
||||||
|
'project.menu.API_SHARE_REPORT': '报告链接有效期',
|
||||||
|
'project.menu.API_RESOURCE_POOL': '执行资源池',
|
||||||
|
'project.menu.API_SCRIPT_REVIEWER': '脚本审核',
|
||||||
|
'project.menu.API_ERROR_REPORT_RULE': '误报规则',
|
||||||
|
'project.menu.API_SYNC_CASE': '变更同步CASE',
|
||||||
|
|
||||||
|
'project.menu.CASE_PUBLIC': '公共用例库',
|
||||||
|
'project.menu.CASE_RE_REVIEW': '关联需求',
|
||||||
|
'project.menu.CASE_RELATED': '重新提审',
|
||||||
|
'project.menu.CASE_ENABLE': '接口测试待更新同步规则',
|
||||||
|
'project.menu.ISSUE_SYNC': '同步缺陷',
|
||||||
|
'project.menu.CRON_EXPRESSION': '同步频率',
|
||||||
|
'project.menu.SYNC_ENABLE': '状态',
|
||||||
|
'project.menu.MECHANISM': '接口测试待更新同步规则',
|
||||||
|
'project.menu.sd': '同步缺陷',
|
||||||
|
'project.menu.rr': '关联需求',
|
||||||
|
'project.menu.far': '误报规则',
|
||||||
|
'project.menu.row1': '系统根据设定的规则将符合的数据展示在我的待办-待更新列表',
|
||||||
|
'project.menu.row2': '将平台创建的缺陷同步至第三方项目管理平台',
|
||||||
|
'project.menu.row3': '可将用例添加至公共用例库共用',
|
||||||
|
'project.menu.row4': '可将用例与第三方项目管理平台进行关联',
|
||||||
|
'project.menu.row5': '评审活动中用例发生变更,用例状态自动切换为重新提审',
|
||||||
|
'project.menu.row6': '开启后,接口定义模块重复性校验将不校验 URL',
|
||||||
|
'project.menu.row7': '当接口定义发生变更后,自动同步接口CASE',
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
>
|
>
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.name"
|
v-model:value="form.name"
|
||||||
:type="UserRequesetTypeEnum.PROJECT_USER_GROUP"
|
:type="UserRequestTypeEnum.PROJECT_USER_GROUP"
|
||||||
:load-option-params="{ projectId: props.projectId, userRoleId: props.userRoleId }"
|
:load-option-params="{ projectId: props.projectId, userRoleId: props.userRoleId }"
|
||||||
disabled-key="checkRoleFlag"
|
disabled-key="checkRoleFlag"
|
||||||
/>
|
/>
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
import { addUserToUserGroup } from '@/api/modules/project-management/usergroup';
|
import { addUserToUserGroup } from '@/api/modules/project-management/usergroup';
|
||||||
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/business/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.projectIds"
|
v-model:value="form.projectIds"
|
||||||
:load-option-params="{ organizationId: lastOrganizationId }"
|
:load-option-params="{ organizationId: lastOrganizationId }"
|
||||||
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_PROJECT"
|
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT"
|
||||||
placeholder="organization.member.selectProjectScope"
|
placeholder="organization.member.selectProjectScope"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.memberIds"
|
v-model:value="form.memberIds"
|
||||||
:load-option-params="{ organizationId: lastOrganizationId }"
|
:load-option-params="{ organizationId: lastOrganizationId }"
|
||||||
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_MEMBER"
|
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_MEMBER"
|
||||||
placeholder="organization.member.selectMemberScope"
|
placeholder="organization.member.selectMemberScope"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
import { useUserStore } from '@/store';
|
import { useUserStore } from '@/store';
|
||||||
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import type { MemberItem, LinkList } from '@/models/setting/member';
|
import type { MemberItem, LinkList } from '@/models/setting/member';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
<a-form-item field="userIds" :label="t('system.project.projectAdmin')">
|
<a-form-item field="userIds" :label="t('system.project.projectAdmin')">
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.userIds"
|
v-model:value="form.userIds"
|
||||||
:type="UserRequesetTypeEnum.ORGANIZATION_PROJECT_ADMIN"
|
:type="UserRequestTypeEnum.ORGANIZATION_PROJECT_ADMIN"
|
||||||
placeholder="system.project.projectAdminPlaceholder"
|
placeholder="system.project.projectAdminPlaceholder"
|
||||||
:load-option-params="{
|
:load-option-params="{
|
||||||
organizationId: currentOrgId,
|
organizationId: currentOrgId,
|
||||||
|
@ -101,7 +101,7 @@
|
||||||
import { CreateOrUpdateSystemProjectParams, SystemOrgOption } from '@/models/setting/system/orgAndProject';
|
import { CreateOrUpdateSystemProjectParams, SystemOrgOption } from '@/models/setting/system/orgAndProject';
|
||||||
import useLicenseStore from '@/store/modules/setting/license';
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
import MsSystemPool from '@/components/business/ms-system-pool/MsSystemPool.vue';
|
import MsSystemPool from '@/components/business/ms-system-pool/MsSystemPool.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
>
|
>
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.name"
|
v-model:value="form.name"
|
||||||
:type="UserRequesetTypeEnum.ORGANIZATION_PROJECT"
|
:type="UserRequestTypeEnum.ORGANIZATION_PROJECT"
|
||||||
:load-option-params="{ organizationId: currentOrgId, projectId: props.projectId }"
|
:load-option-params="{ organizationId: currentOrgId, projectId: props.projectId }"
|
||||||
disabled-key="memberFlag"
|
disabled-key="memberFlag"
|
||||||
/>
|
/>
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
import { addProjectMemberByOrg } from '@/api/modules/setting/organizationAndProject';
|
import { addProjectMemberByOrg } from '@/api/modules/setting/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/business/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.memberIds"
|
v-model:value="form.memberIds"
|
||||||
placeholder="system.organization.organizationAdminPlaceholder"
|
placeholder="system.organization.organizationAdminPlaceholder"
|
||||||
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_ADMIN"
|
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_ADMIN"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="description" :label="t('system.organization.description')">
|
<a-form-item field="description" :label="t('system.organization.description')">
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
import { createOrUpdateOrg } from '@/api/modules/setting/organizationAndProject';
|
import { createOrUpdateOrg } from '@/api/modules/setting/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';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<a-form-item field="userIds" :label="t('system.project.projectAdmin')">
|
<a-form-item field="userIds" :label="t('system.project.projectAdmin')">
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.userIds"
|
v-model:value="form.userIds"
|
||||||
:type="UserRequesetTypeEnum.SYSTEM_PROJECT_ADMIN"
|
:type="UserRequestTypeEnum.SYSTEM_PROJECT_ADMIN"
|
||||||
placeholder="system.project.projectAdminPlaceholder"
|
placeholder="system.project.projectAdminPlaceholder"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import { CreateOrUpdateSystemProjectParams, SystemOrgOption } from '@/models/setting/system/orgAndProject';
|
import { CreateOrUpdateSystemProjectParams, SystemOrgOption } from '@/models/setting/system/orgAndProject';
|
||||||
import useLicenseStore from '@/store/modules/setting/license';
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
import MsSystemPool from '@/components/business/ms-system-pool/MsSystemPool.vue';
|
import MsSystemPool from '@/components/business/ms-system-pool/MsSystemPool.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
>
|
>
|
||||||
<MsUserSelector
|
<MsUserSelector
|
||||||
v-model:value="form.name"
|
v-model:value="form.name"
|
||||||
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION"
|
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION"
|
||||||
disabled-key="memberFlag"
|
disabled-key="memberFlag"
|
||||||
:load-option-params="{ sourceId: props.organizationId || props.projectId }"
|
:load-option-params="{ sourceId: props.organizationId || props.projectId }"
|
||||||
/>
|
/>
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
import { addUserToOrgOrProject } from '@/api/modules/setting/organizationAndProject';
|
import { addUserToOrgOrProject } from '@/api/modules/setting/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/business/ms-user-selector/index.vue';
|
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
|
||||||
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
|
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"src/**/*.tsx",
|
"src/**/*.tsx",
|
||||||
"src/**/*.vue",
|
"src/**/*.vue",
|
||||||
"src/components.d.ts",
|
"src/components.d.ts",
|
||||||
"auto-imports.d.ts",
|
"src/auto-imports.d.ts",
|
||||||
"types/**/*.d.ts",
|
"types/**/*.d.ts",
|
||||||
"types/**/*.ts",
|
"types/**/*.ts",
|
||||||
"build/**/*.ts",
|
"build/**/*.ts",
|
||||||
|
@ -28,23 +28,17 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": [
|
"lib": ["esnext", "dom"],
|
||||||
"esnext",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"skipLibCheck": true, // 跳过node依赖包语法检查
|
"skipLibCheck": true, // 跳过node依赖包语法检查
|
||||||
"types": [
|
"types": [
|
||||||
// "vitest/globals",
|
// "vitest/globals",
|
||||||
// "vite-plugin-svg-icons/client"
|
// "vite-plugin-svg-icons/client"
|
||||||
], // 手动导入TS类型声明文件
|
], // 手动导入TS类型声明文件
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": { // 路径映射
|
"paths": {
|
||||||
"@/*": [
|
// 路径映射
|
||||||
"./src/*"
|
"@/*": ["./src/*"],
|
||||||
],
|
"#/*": ["types/*"]
|
||||||
"#/*": [
|
|
||||||
"types/*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue