feat(项目管理): 菜单管理时间选择器enter保存blur取消

This commit is contained in:
RubyLiu 2023-10-16 20:09:03 +08:00 committed by Craftsman
parent c83ccb456e
commit bc9cada46e
2 changed files with 214 additions and 203 deletions

View File

@ -4,11 +4,10 @@
v-model="current.value" v-model="current.value"
class="w-[120px]" class="w-[120px]"
:min="0" :min="0"
:max="current.max"
hide-button hide-button
size="small" size="small"
@blur="handleBlur" @press-enter="handleEnter(false)"
@press-enter="handleBlur" @blur="handleEnter(true)"
> >
<template #suffix> <template #suffix>
<a-select v-model="current.type" size="small" class="max-w-[64px]" :options="option"> </a-select> <a-select v-model="current.type" size="small" class="max-w-[64px]" :options="option"> </a-select>
@ -28,39 +27,41 @@
(e: 'change', value: string): void; (e: 'change', value: string): void;
}>(); }>();
// enter
const isEnter = ref<boolean>(false);
function parseValue(v?: string) { function parseValue(v?: string) {
// 使 // 使
if (!v) { if (!v) {
return { type: 'H', value: undefined, max: Number.MAX_VALUE }; return { type: 'H', value: undefined };
} }
const match = v.match(/^(\d+)([MYHD])$/); const match = v.match(/^(\d+)([MYHD])$/);
if (match) { if (match) {
const value = parseInt(match[1], 10); // const value = parseInt(match[1], 10); //
const type = match[2]; // const type = match[2]; //
let max = 0; return { type, value };
//
switch (type) {
case 'H':
max = 24;
break;
case 'M':
max = 12;
break;
default:
max = Number.MAX_VALUE;
}
return { type, value, max };
} }
// //
return { type: 'H', value: undefined, max: Number.MAX_VALUE }; return { type: 'H', value: undefined };
} }
const current = reactive(parseValue(props.modelValue || props.defaultValue)); const current = reactive(parseValue(props.modelValue || props.defaultValue));
const handleBlur = () => { const handleEnter = (isBlur: boolean) => {
const result = current.value ? `${current.value}${current.type}` : ''; if (isBlur) {
emit('update:modelValue', current.value ? `${current.value}${current.type}` : ''); if (!isEnter.value) {
emit('change', result); // Enterblur
const { type, value } = parseValue(props.modelValue || props.defaultValue);
current.value = value;
current.type = type;
}
isEnter.value = false;
} else {
// Enter
const result = current.value ? `${current.value}${current.type}` : '';
emit('update:modelValue', current.value ? `${current.value}${current.type}` : '');
emit('change', result);
isEnter.value = true;
}
}; };
const option = computed(() => [ const option = computed(() => [
{ {

View File

@ -17,7 +17,178 @@
<span class="ml-[4px]">{{ t(`project.menu.${record.type}`) }}</span> <span class="ml-[4px]">{{ t(`project.menu.${record.type}`) }}</span>
</div> </div>
</template> </template>
<template #moduleEnable="{ record }"> <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'">
<!-- 测试计划 报告保留时间范围 -->
<MsTimeSelectorVue
v-model="allValueMap['TEST_PLAN_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('TEST_PLAN_CLEAN_REPORT',v,MenuEnum.testPlan)"
/>
</div>
<div v-if="record.type === 'TEST_PLAN_SHARE_REPORT'">
<!-- 测试计划 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['TEST_PLAN_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('TEST_PLAN_SHARE_REPORT',v,MenuEnum.testPlan)"
/>
</div>
<template v-if="record.type === 'BUG_SYNC'">
<!-- 同步缺陷 -->
<span>{{ t('project.menu.row2') }}</span>
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.sd') }}</div>
</template>
<div v-if="record.type === 'CASE_PUBLIC'">
<!-- 用例 公共用例库 -->
{{ t('project.menu.row3') }}
</div>
<div v-if="record.type === 'CASE_RELATED'" 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_RE_REVIEW'">
<!-- 用例 重新提审 -->
{{ t('project.menu.row5') }}
</div>
<div v-if="record.type === 'API_URL_REPEATABLE'">
<!-- 接口测试 接口定义URL可重复 -->
{{ t('project.menu.row6') }}
</div>
<div v-if="record.type === 'API_CLEAN_REPORT'">
<MsTimeSelectorVue
v-model="allValueMap['API_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('API_CLEAN_REPORT',v,MenuEnum.apiTest)"
/>
</div>
<div v-if="record.type === 'API_SHARE_REPORT'">
<!--接口测试 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['API_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('API_SHARE_REPORT',v,MenuEnum.apiTest)"
/>
</div>
<div v-if="record.type === 'API_RESOURCE_POOL'" class="flex flex-row items-center">
<!--接口测试 执行资源池 -->
<a-select
v-model="allValueMap['API_RESOURCE_POOL_ID']"
:field-names="{ label: 'name', value: 'id' }"
:options="apiPoolOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('API_RESOURCE_POOL_ID',v as string,MenuEnum.apiTest)"
/>
<a-tooltip
:content="t('project.menu.API_RESOURCE_POOL_TIP', { name: allValueMap['API_RESOURCE_POOL'] })"
position="right"
>
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" 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="allValueMap['API_SCRIPT_REVIEWER_ID']"
:field-names="{ label: 'name', value: 'id' }"
:options="apiAuditorOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('API_SCRIPT_REVIEWER_ID',v as string,MenuEnum.apiTest)"
/>
<a-tooltip :content="t('project.menu.API_SCRIPT_REVIEWER_TIP')" position="right">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" 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">
<!--接口测试 误报规则 -->
<div class="error-report">
<a-input
v-model="allValueMap['FAKE_ERROR_NUM']"
class="w-[120px]"
disabled
:placeholder="t('project.menu.pleaseConfig')"
>
<template #append>
<div>{{ t('project.menu.count') }}</div>
</template>
</a-input>
</div>
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.far') }}</div>
<a-tooltip :content="t('project.menu.API_ERROR_REPORT_RULE_TIP')" position="right">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" 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 报告保留时间范围 -->
<MsTimeSelectorVue
v-model="allValueMap['UI_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('UI_CLEAN_REPORT',v,MenuEnum.uiTest)"
/>
</div>
<div v-if="record.type === 'UI_SHARE_REPORT'">
<!--UI 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['UI_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('UI_SHARE_REPORT',v,MenuEnum.uiTest)"
/>
</div>
<div v-if="record.type === 'UI_RESOURCE_POOL'" class="flex flex-row items-center">
<!--UI 执行资源池 -->
<a-select
v-model="allValueMap['UI_RESOURCE_POOL_ID']"
:field-names="{ label: 'name', value: 'id' }"
:options="uiPoolOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('UI_RESOURCE_POOL_ID',v as string,MenuEnum.uiTest)"
/>
<a-tooltip
:content="t('project.menu.UI_RESOURCE_POOL_TIP', { name: allValueMap['UI_RESOURCE_POOL_ID'] })"
position="right"
>
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
</div>
</a-tooltip>
</div>
<div v-if="record.type === 'PERFORMANCE_TEST_CLEAN_REPORT'">
<!--性能测试 报告保留时间范围 -->
<MsTimeSelectorVue
v-model="allValueMap['PERFORMANCE_TEST_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('PERFORMANCE_TEST_CLEAN_REPORT',v,MenuEnum.loadTest)"
/>
</div>
<div v-if="record.type === 'PERFORMANCE_TEST_SHARE_REPORT'">
<!--性能测试 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['PERFORMANCE_TEST_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('PERFORMANCE_TEST_SHARE_REPORT',v,MenuEnum.loadTest)"
/>
</div>
<div v-if="record.type === 'PERFORMANCE_TEST_SCRIPT_REVIEWER'" class="flex flex-row items-center">
<!--性能测试 脚本审核 -->
<a-select
v-model="allValueMap['PERFORMANCE_TEST_SCRIPT_REVIEWER_ID']"
:field-names="{ label: 'name', value: 'id' }"
:options="performanceAuditorOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('PERFORMANCE_TEST_SCRIPT_REVIEWER_ID',v as string,MenuEnum.loadTest)"
/>
<a-tooltip :content="t('project.menu.PERFORMANCE_TEST_SCRIPT_REVIEWER_TIP')" position="right">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
</div>
</a-tooltip>
</div>
</template>
<template #operation="{ record }">
<!-- 父级菜单是否展示 --> <!-- 父级菜单是否展示 -->
<a-switch <a-switch
v-if="record.children" v-if="record.children"
@ -124,184 +295,13 @@
<!-- 性能测试 脚本审核 Switch--> <!-- 性能测试 脚本审核 Switch-->
<a-switch <a-switch
v-if="record.type === 'PERFORMANCE_TEST_SCRIPT_REVIEWER'" v-if="record.type === 'PERFORMANCE_TEST_SCRIPT_REVIEWER'"
v-model="allValueMap['PERFORMANCE_TEST_SCRIPT_REVIEWER']" v-model="allValueMap['PERFORMANCE_TEST_SCRIPT_REVIEWER_ENABLE']"
checked-value="true" checked-value="true"
unchecked-value="false" unchecked-value="false"
size="small" size="small"
@change="(v: boolean | string| number) => handleMenuStatusChange(record.type,v as boolean,MenuEnum.loadTest)" @change="(v: boolean | string| number) => handleMenuStatusChange('PERFORMANCE_TEST_SCRIPT_REVIEWER_ENABLE',v as boolean,MenuEnum.loadTest)"
/> />
</template> </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'">
<!-- 测试计划 报告保留时间范围 -->
<MsTimeSelectorVue
v-model="allValueMap['TEST_PLAN_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('TEST_PLAN_CLEAN_REPORT',v,MenuEnum.testPlan)"
/>
</div>
<div v-if="record.type === 'TEST_PLAN_SHARE_REPORT'">
<!-- 测试计划 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['TEST_PLAN_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('TEST_PLAN_SHARE_REPORT',v,MenuEnum.testPlan)"
/>
</div>
<template v-if="record.type === 'BUG_SYNC'">
<!-- 同步缺陷 -->
<span>{{ t('project.menu.row2') }}</span>
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.sd') }}</div>
</template>
<div v-if="record.type === 'CASE_PUBLIC'">
<!-- 用例 公共用例库 -->
{{ t('project.menu.row3') }}
</div>
<div v-if="record.type === 'CASE_RELATED'" 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_RE_REVIEW'">
<!-- 用例 重新提审 -->
{{ t('project.menu.row5') }}
</div>
<div v-if="record.type === 'API_URL_REPEATABLE'">
<!-- 接口测试 接口定义URL可重复 -->
{{ t('project.menu.row6') }}
</div>
<div v-if="record.type === 'API_CLEAN_REPORT'">
<MsTimeSelectorVue
v-model="allValueMap['API_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('API_CLEAN_REPORT',v,MenuEnum.apiTest)"
/>
</div>
<div v-if="record.type === 'API_SHARE_REPORT'">
<!--接口测试 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['API_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('API_SHARE_REPORT',v,MenuEnum.apiTest)"
/>
</div>
<div v-if="record.type === 'API_RESOURCE_POOL'" class="flex flex-row items-center">
<!--接口测试 执行资源池 -->
<a-select
v-model="allValueMap['API_RESOURCE_POOL']"
:field-names="{ label: 'name', value: 'id' }"
:options="apiPoolOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('API_RESOURCE_POOL',v as string,MenuEnum.apiTest)"
/>
<a-tooltip
:content="t('project.menu.API_RESOURCE_POOL_TIP', { name: allValueMap['API_RESOURCE_POOL'] })"
position="right"
>
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" 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="allValueMap['API_SCRIPT_REVIEWER']"
:field-names="{ label: 'name', value: 'id' }"
:options="apiAuditorOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('API_SCRIPT_REVIEWER',v as string,MenuEnum.apiTest)"
/>
<a-tooltip :content="t('project.menu.API_SCRIPT_REVIEWER_TIP')" position="right">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" 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">
<!--接口测试 误报规则 -->
<div class="error-report">
<a-input
v-model="allValueMap['API_ERROR_REPORT_RULE']"
class="w-[120px]"
disabled
:placeholder="t('project.menu.pleaseConfig')"
>
<template #append>
<div>{{ t('project.menu.count') }}</div>
</template>
</a-input>
</div>
<div class="ml-[8px] text-[rgb(var(--primary-7))]" @click="showDefectDrawer">{{ t('project.menu.far') }}</div>
<a-tooltip :content="t('project.menu.API_ERROR_REPORT_RULE_TIP')" position="right">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" 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 报告保留时间范围 -->
<MsTimeSelectorVue
v-model="allValueMap['UI_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('UI_CLEAN_REPORT',v,MenuEnum.uiTest)"
/>
</div>
<div v-if="record.type === 'UI_SHARE_REPORT'">
<!--UI 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['UI_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('UI_SHARE_REPORT',v,MenuEnum.uiTest)"
/>
</div>
<div v-if="record.type === 'UI_RESOURCE_POOL'" class="flex flex-row items-center">
<!--UI 执行资源池 -->
<a-select
v-model="allValueMap['UI_RESOURCE_POOL_ID']"
:field-names="{ label: 'name', value: 'id' }"
:options="uiPoolOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('UI_RESOURCE_POOL_ID',v as string,MenuEnum.uiTest)"
/>
<a-tooltip
:content="t('project.menu.UI_RESOURCE_POOL_TIP', { name: allValueMap['UI_RESOURCE_POOL_ID'] })"
position="right"
>
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
</div>
</a-tooltip>
</div>
<div v-if="record.type === 'PERFORMANCE_TEST_CLEAN_REPORT'">
<!--性能测试 报告保留时间范围 -->
<MsTimeSelectorVue
v-model="allValueMap['PERFORMANCE_TEST_CLEAN_REPORT']"
@change="(v: string) => handleMenuStatusChange('PERFORMANCE_TEST_CLEAN_REPORT',v,MenuEnum.loadTest)"
/>
</div>
<div v-if="record.type === 'PERFORMANCE_TEST_SHARE_REPORT'">
<!--性能测试 报告链接有效期 -->
<MsTimeSelectorVue
v-model="allValueMap['PERFORMANCE_TEST_SHARE_REPORT']"
@change="(v: string) => handleMenuStatusChange('PERFORMANCE_TEST_SHARE_REPORT',v,MenuEnum.loadTest)"
/>
</div>
<div v-if="record.type === 'PERFORMANCE_TEST_SCRIPT_REVIEWER'" class="flex flex-row items-center">
<!--性能测试 脚本审核 -->
<a-select
v-model="allValueMap['PERFORMANCE_TEST_SCRIPT_REVIEWER__ID']"
:field-names="{ label: 'name', value: 'id' }"
:options="performanceAuditorOption"
class="w-[120px]"
@change="(v: SelectValue) => handleMenuStatusChange('PERFORMANCE_TEST_SCRIPT_REVIEWER__ID',v as string,MenuEnum.loadTest)"
/>
<a-tooltip :content="t('project.menu.PERFORMANCE_TEST_SCRIPT_REVIEWER_TIP')" position="right">
<div>
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
</div>
</a-tooltip>
</div>
</template>
</MsBaseTable> </MsBaseTable>
<MsDrawer <MsDrawer
v-model:visible="defectDrawerVisible" v-model:visible="defectDrawerVisible"
@ -391,7 +391,7 @@
}, },
{ {
title: 'common.operation', title: 'common.operation',
slotName: 'moduleEnable', slotName: 'operation',
dataIndex: 'moduleEnable', dataIndex: 'moduleEnable',
width: 58, width: 58,
}, },
@ -518,6 +518,19 @@
} }
); );
const getMenuConfig = async (type: string) => {
try {
const resultObj = await getConfigByMenuItem({
projectId: currentProjectId.value,
type,
});
allValueMap.value = { ...allValueMap.value, ...resultObj };
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
};
const expandChange = async (record: TableData) => { const expandChange = async (record: TableData) => {
try { try {
if (expandedKeys.value.includes(record.module)) { if (expandedKeys.value.includes(record.module)) {
@ -526,11 +539,7 @@
return; return;
} }
expandedKeys.value = [...expandedKeys.value, record.module]; expandedKeys.value = [...expandedKeys.value, record.module];
const resultObj = await getConfigByMenuItem({ getMenuConfig(record.module);
projectId: currentProjectId.value,
type: record.module as MenuEnum,
});
allValueMap.value = { ...allValueMap.value, ...resultObj };
if (record.module === MenuEnum.apiTest && !apiPoolOption.value.length) { if (record.module === MenuEnum.apiTest && !apiPoolOption.value.length) {
apiPoolOption.value = await getPoolOptions(currentProjectId.value, record.module); apiPoolOption.value = await getPoolOptions(currentProjectId.value, record.module);
} }
@ -578,6 +587,7 @@
suffix suffix
); );
Message.success(t('common.operationSuccess')); Message.success(t('common.operationSuccess'));
getMenuConfig(suffix);
} catch (e) { } catch (e) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(e); console.log(e);