feat(项目管理): 菜单管理列表页静态页面开发

This commit is contained in:
RubyLiu 2023-10-10 20:06:38 +08:00 committed by Craftsman
parent ae33b0d83d
commit d9cfcec39d
29 changed files with 477 additions and 153 deletions

View File

@ -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
}
}

View File

@ -27,6 +27,7 @@ module.exports = {
'plugin:import/typescript',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
'./.eslintrc-auto-import.json',
],
settings: {
'import/resolver': {

View File

@ -7,6 +7,7 @@ import svgLoader from 'vite-svg-loader';
// import configArcoResolverPlugin from './plugin/arcoResolver';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import AutoImport from 'unplugin-auto-import/vite';
export default defineConfig({
plugins: [
@ -22,6 +23,19 @@ export default defineConfig({
// 指定symbolId格式
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: {
alias: [

View File

@ -124,6 +124,7 @@
"stylelint-order": "^5.0.0",
"tailwindcss": "^3.3.3",
"typescript": "^4.9.5",
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.24.1",
"vite": "^3.2.7",
"vite-plugin-compression": "^0.5.1",

View File

@ -1,28 +1,19 @@
import MSR from '@/api/http/index';
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 { TableQueryParams, CommonList } from '@/models/common';
const tableList: MenuTableListItem[] = [
'workstation',
'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 });
export async function postTabletList(params: TableQueryParams): Promise<CommonList<MenuTableListItem>> {
const list = await MSR.get<MenuTableListItem[]>({ url: `${Url.getMenuListUrl}${params.projectId}` });
const result: CommonList<MenuTableListItem> = {
list: tableList,
total: tableList.length,
total: list.length,
list,
pageSize: 10,
current: 1,
};
@ -54,7 +45,33 @@ export function postUpdateMenu(data: MenuTableListParams) {
suffix = 'performance-test';
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 });
}

77
frontend/src/auto-import.d.ts vendored Normal file
View File

@ -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';
}

View File

@ -37,7 +37,7 @@
import { addOrgUserToUserGroup, addUserToUserGroup } from '@/api/modules/setting/usergroup';
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-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';
const { t } = useI18n();
@ -52,7 +52,7 @@
const userSelectorProps = computed(() => {
if (systemType === AuthScopeEnum.SYSTEM) {
return {
type: UserRequesetTypeEnum.SYSTEM_USER_GROUP,
type: UserRequestTypeEnum.SYSTEM_USER_GROUP,
loadOptionParams: {
roleId: props.currentId,
},
@ -60,7 +60,7 @@
};
}
return {
type: UserRequesetTypeEnum.ORGANIZATION_USER_GROUP,
type: UserRequestTypeEnum.ORGANIZATION_USER_GROUP,
loadOptionParams: {
roleId: props.currentId,
organizationId: currentOrgId.value,

View File

@ -25,7 +25,7 @@
<script setup lang="ts">
import { useI18n } from '@/hooks/useI18n';
import { ref, onMounted, computed } from 'vue';
import initOptionsFunc, { UserRequesetTypeEnum } from './utils';
import initOptionsFunc, { UserRequestTypeEnum } from './utils';
import { debounce } from 'lodash-es';
export interface MsUserSelectorOption {
@ -46,7 +46,7 @@
firstLabelKey?: string; // key
secondLabelKey?: string; // key
loadOptionParams?: Record<string, any>; //
type?: UserRequesetTypeEnum; //
type?: UserRequestTypeEnum; //
}>(),
{
disabled: false,
@ -54,7 +54,7 @@
valueKey: 'id',
firstLabelKey: 'name',
secondLabelKey: 'email',
type: UserRequesetTypeEnum.SYSTEM_USER_GROUP,
type: UserRequestTypeEnum.SYSTEM_USER_GROUP,
}
);

View File

@ -10,7 +10,7 @@ import { getProjectMemberOptions } from '@/api/modules/project-management/projec
import { getProjectUserGroupOptions } from '@/api/modules/project-management/usergroup';
// eslint-disable-next-line no-shadow
export enum UserRequesetTypeEnum {
export enum UserRequestTypeEnum {
SYSTEM_USER_GROUP = 'SYSTEM_USER_GROUP',
SYSTEM_ORGANIZATION = 'SYSTEM_ORGANIZATION',
SYSTEM_ORGANIZATION_ADMIN = 'SYSTEM_ORGANIZATION_ADMIN',
@ -26,43 +26,43 @@ export enum UserRequesetTypeEnum {
PROJECT_USER_GROUP = 'PROJECT_USER_GROUP',
}
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);
}
if (type === UserRequesetTypeEnum.ORGANIZATION_USER_GROUP) {
if (type === UserRequestTypeEnum.ORGANIZATION_USER_GROUP) {
// 组织 - 用户组-添加成员-下拉选项
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);
}
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION || type === UserRequesetTypeEnum.SYSTEM_PROJECT) {
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION || type === UserRequestTypeEnum.SYSTEM_PROJECT) {
// 系统 -【组织 或 项目】-添加成员-下拉选项
return getUserByOrganizationOrProject(params.sourceId, params.keyword);
}
if (type === UserRequesetTypeEnum.ORGANIZATION_PROJECT) {
if (type === UserRequestTypeEnum.ORGANIZATION_PROJECT) {
// 组织 - 项目-添加成员-下拉选项
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);
}
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_PROJECT) {
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT) {
// 系统-组织-组织项目
return getProjectList(params.organizationId, params.keyword);
}
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_MEMBER) {
if (type === UserRequestTypeEnum.SYSTEM_ORGANIZATION_MEMBER) {
// 系统-组织-组织成员
return getUser(params.organizationId, params.keyword);
}
if (type === UserRequesetTypeEnum.PROJECT_PERMISSION_MEMBER) {
if (type === UserRequestTypeEnum.PROJECT_PERMISSION_MEMBER) {
// 项目-项目成员
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);
}

View File

@ -5,7 +5,7 @@
:row-class="getRowClass"
:span-method="spanMethod"
:columns="currentColumns"
:expanded-keys="props.expandedKeys"
@expand="(rowKey, record) => emit('expand', record)"
@sorter-change="(dataIndex: string,direction: string) => handleSortChange(dataIndex, direction)"
>
<template #optional="{ rowIndex, record }">
@ -81,7 +81,7 @@
<template v-else-if="item.isTag">
<div class="one-line-text max-w-[456px]">
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
{{ record[item.dataIndex as string] || '-' }}
{{ record[item.dataIndex as string] || (attrs.emptyDataShowLine ? '-' : '') }}
</slot>
</div>
</template>
@ -106,7 +106,7 @@
<a-tooltip v-else placement="top" :content="String(record[item.dataIndex as string])">
<div class="one-line-text max-w-[300px]">
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
{{ record[item.dataIndex as string] || '-' }}
{{ record[item.dataIndex as string] || (attrs.emptyDataShowLine ? '-' : '') }}
</slot>
</div>
</a-tooltip>
@ -130,7 +130,7 @@
</template>
<template v-else>
<slot :name="item.slotName" v-bind="{ record, rowIndex, column }">
{{ record[item.dataIndex as string] || '-' }}
{{ record[item.dataIndex as string] || (attrs.emptyDataShowLine ? '-' : '') }}
</slot>
</template>
</div>
@ -145,21 +145,9 @@
</div>
</slot>
</template>
<template #expand-icon="{ expanded, record }">
<MsIcon
v-if="!expanded"
: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 #expand-icon="{ expanded }">
<MsIcon v-if="!expanded" :size="8" type="icon-icon_right_outlined" class="text-[var(--color-text-4)]" />
<MsIcon v-else :size="8" class="text-[rgb(var(--primary-6))]" type="icon-icon_down_outlined" />
</template>
</a-table>
<div
@ -208,7 +196,7 @@
import type { TableData } from '@arco-design/web-vue';
import MsCheckbox from '../ms-checkbox/MsCheckbox.vue';
const batchleft = ref('10px');
const batchLeft = ref('10px');
const { t } = useI18n();
const tableStore = useTableStore();
const currentColumns = ref<MsTableColumn>([]);
@ -233,8 +221,8 @@
(e: 'rowSelectChange', key: string): void;
(e: 'selectAllChange', value: SelectAllEnum): void;
(e: 'sorterChange', value: { [key: string]: string }): void;
(e: 'expand', record: TableData): void;
(e: 'clearSelector'): void;
(e: 'expandChange', key: string): void;
}>();
const attrs = useAttrs();
@ -418,7 +406,7 @@
onMounted(() => {
initColumn();
batchleft.value = getBatchLeft();
batchLeft.value = getBatchLeft();
});
</script>

View File

@ -1,6 +1,6 @@
import { ColumnEditTypeEnum, SelectAllEnum } from '@/enums/tableEnum';
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 {
current: number;
@ -77,7 +77,7 @@ export interface MsTableProps<T> {
/** 展开行相关 */
showExpand?: boolean; // 是否显示展开行
expandedKeys?: string[]; // 显示的展开行、子树(受控模式)
emptyDataShowLine?: boolean; // 空数据是否显示 "-"
[key: string]: any;
}
@ -107,7 +107,7 @@ export interface BatchActionConfig {
moreAction?: BatchActionParams[];
}
export interface renamePopconfirmVisibleType {
export interface renamePopConfirmVisibleType {
[key: string]: boolean;
}

View File

@ -36,7 +36,7 @@ export default function useTableProps<T>(
data: [], // 表格数据
/**
*
* showSetting为true时,TableStore.initColumnsk(tableKey: string, column: MsTableColumn)
* showSetting为true时,TableStore.initColumn(tableKey: string, column: MsTableColumn)
* showSetting为false时
*/
columns: [] as MsTableColumn,
@ -59,7 +59,7 @@ export default function useTableProps<T>(
showFirstOperation: false, // 展示第一行的操作
/** 展开行相关 */
showExpand: false, // 是否显示展开行
expandedKeys: [], // 显示的展开行、子树(受控模式)
emptyDataShowLine: true, // 空数据是否显示 "-"
...props,
};
@ -351,18 +351,6 @@ export default function useTableProps<T>(
propsRes.value.selectedKeys = selectedKeys;
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(() => {

View File

@ -0,0 +1,5 @@
<template>
<a-input>
<template #append> </template>
</a-input>
</template>

View File

@ -0,0 +1 @@
export default {};

View File

@ -0,0 +1 @@
export default {};

View File

@ -1,14 +1,19 @@
import { MenuEnum } from '@/enums/commonEnum';
export interface MenuTableConfigItem {
projectId: string;
type: string;
typeValue: string;
}
export interface MenuTableListItem {
module: string;
moduleEnable: boolean;
moduleDesc?: string;
children?: MenuTableListItem[];
children?: MenuTableConfigItem[];
}
export interface MenuTableListParams {
projectId: string;
type: MenuEnum;
typeValue: boolean;
type?: MenuEnum;
typeValue?: boolean;
}

View File

@ -18,7 +18,7 @@
<MsUserSelector
v-model:value="form.userIds"
:load-option-params="{ projectId: lastProjectId }"
:type="UserRequesetTypeEnum.PROJECT_PERMISSION_MEMBER"
:type="UserRequestTypeEnum.PROJECT_PERMISSION_MEMBER"
placeholder="project.member.selectMemberScope"
disabled-key="memberFlag"
/>
@ -51,7 +51,7 @@
ActionProjectMember,
AddProjectMember,
} 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 userStore = useUserStore();

View File

@ -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>

View File

@ -0,0 +1,3 @@
<template> todo </template>
<script lang="ts" setup></script>

View File

@ -1,40 +1,182 @@
<template>
<div class="flex flex-row items-center">
<div class="text-[var(--color-text-1)]"> {{ t('project.menu.management') }}</div>
<a-tooltip :content="t('project.menu.manageTip')">
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
<a-tooltip :content="t('project.menu.manageTip')" position="bl">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
</div>
</a-tooltip>
</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 }">
<MsIcon v-if="record.children" class="text-[var(--color-text-4)]" :type="getMenuIcon(record.module)" />
<span class="ml-[4px]">{{ t(`menu.${record.module}`) }}</span>
<div v-if="record.children">
<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 #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>
</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>
<script setup lang="ts">
/**
* @description 项目管理-项目与权限-菜单管理
*/
import { onMounted, computed } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import useTable from '@/components/pure/ms-table/useTable';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
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 { MenuTableListItem } from '@/models/projectManagement/menuManagement';
import { MenuEnum } from '@/enums/commonEnum';
import { Message } from '@arco-design/web-vue';
import { Message, TableData } from '@arco-design/web-vue';
const appStore = useAppStore();
const currentProjectId = computed(() => appStore.currentProjectId);
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 = [
{
@ -68,18 +210,12 @@
noDisable: true,
rowKey: 'module',
showExpand: true,
emptyDataShowLine: false,
},
(item) => {
item = {
...item,
children: [
{
...item,
},
],
};
return item;
}
(item) => ({
...item,
children: [],
})
);
const getMenuIcon = (type: MenuEnum) => {
@ -118,6 +254,19 @@
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(() => {
setLoadListParams({ projectId: currentProjectId.value });
fetchData();

View File

@ -4,4 +4,40 @@ export default {
'可根据使用场景配置各功能开关关闭后,将隐藏功能入口,成员无法访问该功能和数据;已产生的数据不够此规则影响;再次开启时,即恢复至关闭前状态',
'project.menu.name': '菜单名称',
'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',
};

View File

@ -17,7 +17,7 @@
>
<MsUserSelector
v-model:value="form.name"
:type="UserRequesetTypeEnum.PROJECT_USER_GROUP"
:type="UserRequestTypeEnum.PROJECT_USER_GROUP"
:load-option-params="{ projectId: props.projectId, userRoleId: props.userRoleId }"
disabled-key="checkRoleFlag"
/>
@ -41,7 +41,7 @@
import { addUserToUserGroup } from '@/api/modules/project-management/usergroup';
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-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 props = defineProps<{

View File

@ -22,7 +22,7 @@
<MsUserSelector
v-model:value="form.projectIds"
:load-option-params="{ organizationId: lastOrganizationId }"
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_PROJECT"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT"
placeholder="organization.member.selectProjectScope"
/>
</a-form-item>
@ -37,7 +37,7 @@
<MsUserSelector
v-model:value="form.memberIds"
:load-option-params="{ organizationId: lastOrganizationId }"
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_MEMBER"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_MEMBER"
placeholder="organization.member.selectMemberScope"
/>
</a-form-item>
@ -75,7 +75,7 @@
import { useUserStore } from '@/store';
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
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 userStore = useUserStore();

View File

@ -41,7 +41,7 @@
<a-form-item field="userIds" :label="t('system.project.projectAdmin')">
<MsUserSelector
v-model:value="form.userIds"
:type="UserRequesetTypeEnum.ORGANIZATION_PROJECT_ADMIN"
:type="UserRequestTypeEnum.ORGANIZATION_PROJECT_ADMIN"
placeholder="system.project.projectAdminPlaceholder"
:load-option-params="{
organizationId: currentOrgId,
@ -101,7 +101,7 @@
import { CreateOrUpdateSystemProjectParams, SystemOrgOption } from '@/models/setting/system/orgAndProject';
import useLicenseStore from '@/store/modules/setting/license';
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';
const { t } = useI18n();

View File

@ -17,7 +17,7 @@
>
<MsUserSelector
v-model:value="form.name"
:type="UserRequesetTypeEnum.ORGANIZATION_PROJECT"
:type="UserRequestTypeEnum.ORGANIZATION_PROJECT"
:load-option-params="{ organizationId: currentOrgId, projectId: props.projectId }"
disabled-key="memberFlag"
/>
@ -41,7 +41,7 @@
import { addProjectMemberByOrg } from '@/api/modules/setting/organizationAndProject';
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-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';
const { t } = useI18n();

View File

@ -30,7 +30,7 @@
<MsUserSelector
v-model:value="form.memberIds"
placeholder="system.organization.organizationAdminPlaceholder"
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_ADMIN"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_ADMIN"
/>
</a-form-item>
<a-form-item field="description" :label="t('system.organization.description')">
@ -57,7 +57,7 @@
import { createOrUpdateOrg } from '@/api/modules/setting/organizationAndProject';
import { Message } from '@arco-design/web-vue';
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 props = defineProps<{

View File

@ -46,7 +46,7 @@
<a-form-item field="userIds" :label="t('system.project.projectAdmin')">
<MsUserSelector
v-model:value="form.userIds"
:type="UserRequesetTypeEnum.SYSTEM_PROJECT_ADMIN"
:type="UserRequestTypeEnum.SYSTEM_PROJECT_ADMIN"
placeholder="system.project.projectAdminPlaceholder"
/>
</a-form-item>
@ -102,7 +102,7 @@
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { CreateOrUpdateSystemProjectParams, SystemOrgOption } from '@/models/setting/system/orgAndProject';
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';
const { t } = useI18n();

View File

@ -17,7 +17,7 @@
>
<MsUserSelector
v-model:value="form.name"
:type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION"
disabled-key="memberFlag"
:load-option-params="{ sourceId: props.organizationId || props.projectId }"
/>
@ -41,7 +41,7 @@
import { addUserToOrgOrProject } from '@/api/modules/setting/organizationAndProject';
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-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 props = defineProps<{

View File

@ -5,7 +5,7 @@
"src/**/*.tsx",
"src/**/*.vue",
"src/components.d.ts",
"auto-imports.d.ts",
"src/auto-imports.d.ts",
"types/**/*.d.ts",
"types/**/*.ts",
"build/**/*.ts",
@ -28,23 +28,17 @@
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": [
"esnext",
"dom"
],
"lib": ["esnext", "dom"],
"skipLibCheck": true, // node
"types": [
// "vitest/globals",
// "vite-plugin-svg-icons/client"
], // TS
"baseUrl": ".",
"paths": { //
"@/*": [
"./src/*"
],
"#/*": [
"types/*"
]
"paths": {
//
"@/*": ["./src/*"],
"#/*": ["types/*"]
}
},
}
}
}