feat(测试用例): 添加功能用例列表缓存

This commit is contained in:
xinxin.wu 2024-09-10 15:22:55 +08:00 committed by Craftsman
parent 8e10e32ab3
commit 6e10709fdb
3 changed files with 244 additions and 231 deletions

View File

@ -3,6 +3,7 @@ export enum CacheTabTypeEnum {
API_TEST_CASE_TABLE = 'API_TEST_CASE_TABLE', // 接口测试-定义-CASE列表 API_TEST_CASE_TABLE = 'API_TEST_CASE_TABLE', // 接口测试-定义-CASE列表
API_TEST_MOCK_TABLE = 'API_TEST_MOCK_TABLE', // 接口测试-定义-MOCK列表 API_TEST_MOCK_TABLE = 'API_TEST_MOCK_TABLE', // 接口测试-定义-MOCK列表
API_SCENARIO_TABLE = 'API_SCENARIO_TABLE', // 接口测试-场景-场景列表 API_SCENARIO_TABLE = 'API_SCENARIO_TABLE', // 接口测试-场景-场景列表
CASE_MANAGEMENT_TABLE_FILTER = 'CASE_MANAGEMENT_TABLE_FILTER', // 功能用例-列表
} }
export default {}; export default {};

View File

@ -1,241 +1,252 @@
<!-- eslint-disable prefer-destructuring --> <!-- eslint-disable prefer-destructuring -->
<template> <template>
<div class="h-full"> <div class="h-full">
<!-- 用例表开始 --> <keep-alive :include="[CacheTabTypeEnum.CASE_MANAGEMENT_TABLE_FILTER]">
<template v-if="showType === 'list'"> <MsCacheWrapper
<MsAdvanceFilter v-if="showType === 'list'"
ref="msAdvanceFilterRef" :key="CacheTabTypeEnum.CASE_MANAGEMENT_TABLE_FILTER"
v-model:keyword="keyword" :cache-name="CacheTabTypeEnum.CASE_MANAGEMENT_TABLE_FILTER"
:view-type="ViewTypeEnum.FUNCTIONAL_CASE"
:filter-config-list="filterConfigList"
:custom-fields-config-list="searchCustomFields"
:search-placeholder="t('caseManagement.featureCase.searchPlaceholder')"
:count="props.modulesCount[props.activeFolder] || 0"
:name="moduleNamePath"
@keyword-search="fetchData"
@adv-search="handleAdvSearch"
@refresh="fetchData()"
> >
<template #left> <!-- 用例表开始 -->
<div> <MsAdvanceFilter
<a-button v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="mr-[12px]" type="primary" @click="caseDetail"> ref="msAdvanceFilterRef"
{{ t('common.newCreate') }} v-model:keyword="keyword"
</a-button> :view-type="ViewTypeEnum.FUNCTIONAL_CASE"
<ImportCase ref="importCaseRef" @init-modules="emit('initModules')" @confirm-import="confirmImport" /> :filter-config-list="filterConfigList"
</div> :custom-fields-config-list="searchCustomFields"
</template> :search-placeholder="t('caseManagement.featureCase.searchPlaceholder')"
<template #right> :count="props.modulesCount[props.activeFolder] || 0"
<a-radio-group :name="moduleNamePath"
v-model:model-value="showType" @keyword-search="fetchData"
type="button" @adv-search="handleAdvSearch"
size="small" @refresh="fetchData()"
class="list-show-type" >
@change="handleShowTypeChange" <template #left>
> <div>
<a-radio value="list" class="show-type-icon !m-[2px]"> <a-button
<MsIcon :size="14" type="icon-icon_view-list_outlined" /> v-permission="['FUNCTIONAL_CASE:READ+ADD']"
</a-radio> class="mr-[12px]"
<a-radio value="minder" class="show-type-icon !m-[2px]"> type="primary"
<MsIcon :size="14" type="icon-icon_mindnote_outlined" /> @click="caseDetail"
</a-radio> >
</a-radio-group> {{ t('common.newCreate') }}
</template> </a-button>
</MsAdvanceFilter> <ImportCase ref="importCaseRef" @init-modules="emit('initModules')" @confirm-import="confirmImport" />
<ms-base-table
v-bind="propsRes"
ref="tableRef"
filter-icon-align-left
class="mt-[16px]"
:action-config="tableBatchActions"
:not-show-table-filter="isAdvancedSearchMode"
@selected-change="handleTableSelect"
v-on="propsEvent"
@batch-action="handleTableBatch"
@change="changeHandler"
@cell-click="handleCellClick"
@filter-change="filterChange"
>
<template #num="{ record }">
<span type="text" class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">
{{ record.num }}
</span>
</template>
<template #name="{ record }">
<div class="one-line-text">{{ record.name }}</div>
</template>
<template #caseLevel="{ record }">
<a-select
v-model:model-value="record.caseLevel"
:placeholder="t('common.pleaseSelect')"
class="param-input w-full"
@click.stop
@change="() => handleStatusChange(record)"
>
<template #label>
<span class="text-[var(--color-text-2)]">
<caseLevel :case-level="record.caseLevel" />
</span>
</template>
<a-option v-for="item of caseLevelList" :key="item.value" :value="item.value">
<caseLevel :case-level="item.text" />
</a-option>
</a-select>
</template>
<!-- 用例等级 -->
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<caseLevel :case-level="filterContent.text" />
</template>
<!-- 执行结果 -->
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<ExecuteStatusTag :execute-result="filterContent.value" />
</template>
<!-- 评审结果 -->
<template #reviewStatus="{ record }">
<MsIcon
:type="statusIconMap[record.reviewStatus]?.icon || ''"
class="mr-1"
:class="[statusIconMap[record.reviewStatus].color]"
></MsIcon>
<span>{{ statusIconMap[record.reviewStatus]?.statusText || '' }} </span>
</template>
<template #lastExecuteResult="{ record }">
<ExecuteStatusTag v-if="record.lastExecuteResult" :execute-result="record.lastExecuteResult" />
<span v-else>-</span>
</template>
<template #moduleId="{ record }">
<a-tree-select
v-if="record.showModuleTree"
v-model:modelValue="record.moduleId"
dropdown-class-name="tree-dropdown"
class="param-input w-full"
:data="caseTreeData"
:allow-search="true"
:field-names="{
title: 'name',
key: 'id',
children: 'children',
}"
:filter-tree-node="filterTreeNode"
:tree-props="{
virtualListProps: {
height: 200,
},
}"
@click.stop
@change="(value) => handleChangeModule(record, value)"
>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text max-w-[200px]">{{ node.name }}</div>
</a-tooltip>
</template>
</a-tree-select>
<a-tooltip v-else :content="getModules(record.moduleId)" position="top">
<span class="one-line-text inline-block" @click.stop="record.showModuleTree = true">
{{ getModules(record.moduleId) }}
</span>
</a-tooltip>
</template>
<!-- 渲染自定义字段开始TODO -->
<template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
<a-tooltip
:content="getTableFields(record.customFields, item as MsTableColumn, record.createUser)"
position="top"
:mouse-enter-delay="100"
mini
>
<div class="one-line-text max-w-[300px]">{{
getTableFields(record.customFields, item as MsTableColumn, record.createUser)
}}</div>
</a-tooltip>
</template>
<!-- 渲染自定义字段结束 -->
<template #operation="{ record }">
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="!mr-0" @click="operateCase(record, true)">
{{ t('common.edit') }}
</MsButton>
<a-divider
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
class="!mx-2 h-[12px]"
direction="vertical"
:margin="8"
></a-divider>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mr-0" @click="operateCase(record, false)">
{{ t('caseManagement.featureCase.copy') }}
</MsButton>
<a-divider
v-permission="['FUNCTIONAL_CASE:READ+ADD']"
class="!mx-2 h-[12px]"
direction="vertical"
:margin="8"
></a-divider>
<span v-permission="['FUNCTIONAL_CASE:READ+DELETE']">
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
</span>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mx-[8px]" @click="createCase">
{{ t('caseManagement.featureCase.creatingCase') }}
</MsButton>
<span v-permission="['FUNCTIONAL_CASE:READ+IMPORT']"> {{ t('caseManagement.featureCase.or') }} </span>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="!mx-[8px]" @click="importCase()">
{{ t('common.import') }}
</MsButton>
</div>
</template>
</ms-base-table>
</template>
<!-- 用例表结束 -->
<div v-else class="h-full">
<div class="flex flex-row items-center justify-between">
<a-popover title="" position="bottom">
<div class="show-table-top-title">
<div class="one-line-text max-h-[32px] max-w-[300px] text-[var(--color-text-1)]">
{{ moduleNamePath }}
</div>
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
</div>
<template #content>
<div class="max-w-[400px] text-[14px] font-medium text-[var(--color-text-1)]">
{{ moduleNamePath }}
<span class="text-[var(--color-text-4)]">({{ props.modulesCount[props.activeFolder] || 0 }})</span>
</div> </div>
</template> </template>
</a-popover> <template #right>
<div class="flex items-center gap-[12px]"> <a-radio-group
<a-radio-group v-model:model-value="showType"
v-model:model-value="showType" type="button"
type="button" size="small"
size="small" class="list-show-type"
class="list-show-type" @change="handleShowTypeChange"
@change="handleShowTypeChange" >
> <a-radio value="list" class="show-type-icon !m-[2px]">
<a-radio value="list" class="show-type-icon !m-[2px]"> <MsIcon :size="14" type="icon-icon_view-list_outlined" />
<MsIcon :size="14" type="icon-icon_view-list_outlined" /> </a-radio>
</a-radio> <a-radio value="minder" class="show-type-icon !m-[2px]">
<a-radio value="minder" class="show-type-icon !m-[2px]"> <MsIcon :size="14" type="icon-icon_mindnote_outlined" />
<MsIcon :size="14" type="icon-icon_mindnote_outlined" /> </a-radio>
</a-radio> </a-radio-group>
</a-radio-group> </template>
</MsAdvanceFilter>
<ms-base-table
v-bind="propsRes"
ref="tableRef"
filter-icon-align-left
class="mt-[16px]"
:action-config="tableBatchActions"
:not-show-table-filter="isAdvancedSearchMode"
@selected-change="handleTableSelect"
v-on="propsEvent"
@batch-action="handleTableBatch"
@change="changeHandler"
@cell-click="handleCellClick"
@filter-change="filterChange"
>
<template #num="{ record }">
<span type="text" class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">
{{ record.num }}
</span>
</template>
<template #name="{ record }">
<div class="one-line-text">{{ record.name }}</div>
</template>
<template #caseLevel="{ record }">
<a-select
v-model:model-value="record.caseLevel"
:placeholder="t('common.pleaseSelect')"
class="param-input w-full"
@click.stop
@change="() => handleStatusChange(record)"
>
<template #label>
<span class="text-[var(--color-text-2)]">
<caseLevel :case-level="record.caseLevel" />
</span>
</template>
<a-option v-for="item of caseLevelList" :key="item.value" :value="item.value">
<caseLevel :case-level="item.text" />
</a-option>
</a-select>
</template>
<!-- 用例等级 -->
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<caseLevel :case-level="filterContent.text" />
</template>
<!-- 执行结果 -->
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<ExecuteStatusTag :execute-result="filterContent.value" />
</template>
<!-- 评审结果 -->
<template #reviewStatus="{ record }">
<MsIcon
:type="statusIconMap[record.reviewStatus]?.icon || ''"
class="mr-1"
:class="[statusIconMap[record.reviewStatus].color]"
></MsIcon>
<span>{{ statusIconMap[record.reviewStatus]?.statusText || '' }} </span>
</template>
<template #lastExecuteResult="{ record }">
<ExecuteStatusTag v-if="record.lastExecuteResult" :execute-result="record.lastExecuteResult" />
<span v-else>-</span>
</template>
<template #moduleId="{ record }">
<a-tree-select
v-if="record.showModuleTree"
v-model:modelValue="record.moduleId"
dropdown-class-name="tree-dropdown"
class="param-input w-full"
:data="caseTreeData"
:allow-search="true"
:field-names="{
title: 'name',
key: 'id',
children: 'children',
}"
:filter-tree-node="filterTreeNode"
:tree-props="{
virtualListProps: {
height: 200,
},
}"
@click.stop
@change="(value) => handleChangeModule(record, value)"
>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text max-w-[200px]">{{ node.name }}</div>
</a-tooltip>
</template>
</a-tree-select>
<a-tooltip v-else :content="getModules(record.moduleId)" position="top">
<span class="one-line-text inline-block" @click.stop="record.showModuleTree = true">
{{ getModules(record.moduleId) }}
</span>
</a-tooltip>
</template>
<!-- 渲染自定义字段开始TODO -->
<template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
<a-tooltip
:content="getTableFields(record.customFields, item as MsTableColumn, record.createUser)"
position="top"
:mouse-enter-delay="100"
mini
>
<div class="one-line-text max-w-[300px]">{{
getTableFields(record.customFields, item as MsTableColumn, record.createUser)
}}</div>
</a-tooltip>
</template>
<!-- 渲染自定义字段结束 -->
<template #operation="{ record }">
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="!mr-0" @click="operateCase(record, true)">
{{ t('common.edit') }}
</MsButton>
<a-divider
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
class="!mx-2 h-[12px]"
direction="vertical"
:margin="8"
></a-divider>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mr-0" @click="operateCase(record, false)">
{{ t('caseManagement.featureCase.copy') }}
</MsButton>
<a-divider
v-permission="['FUNCTIONAL_CASE:READ+ADD']"
class="!mx-2 h-[12px]"
direction="vertical"
:margin="8"
></a-divider>
<span v-permission="['FUNCTIONAL_CASE:READ+DELETE']">
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
</span>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mx-[8px]" @click="createCase">
{{ t('caseManagement.featureCase.creatingCase') }}
</MsButton>
<span v-permission="['FUNCTIONAL_CASE:READ+IMPORT']"> {{ t('caseManagement.featureCase.or') }} </span>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="!mx-[8px]" @click="importCase()">
{{ t('common.import') }}
</MsButton>
</div>
</template>
</ms-base-table>
</MsCacheWrapper>
<!-- 用例表结束 -->
<div v-else class="h-full">
<div class="flex flex-row items-center justify-between">
<a-popover title="" position="bottom">
<div class="show-table-top-title">
<div class="one-line-text max-h-[32px] max-w-[300px] text-[var(--color-text-1)]">
{{ moduleNamePath }}
</div>
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
</div>
<template #content>
<div class="max-w-[400px] text-[14px] font-medium text-[var(--color-text-1)]">
{{ moduleNamePath }}
<span class="text-[var(--color-text-4)]">({{ props.modulesCount[props.activeFolder] || 0 }})</span>
</div>
</template>
</a-popover>
<div class="flex items-center gap-[12px]">
<a-radio-group
v-model:model-value="showType"
type="button"
size="small"
class="list-show-type"
@change="handleShowTypeChange"
>
<a-radio value="list" class="show-type-icon !m-[2px]">
<MsIcon :size="14" type="icon-icon_view-list_outlined" />
</a-radio>
<a-radio value="minder" class="show-type-icon !m-[2px]">
<MsIcon :size="14" type="icon-icon_mindnote_outlined" />
</a-radio>
</a-radio-group>
</div>
</div>
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]">
<!-- 脑图开始 -->
<MsFeatureCaseMinder
:module-id="props.activeFolder"
:modules-count="props.modulesCount"
:module-name="props.moduleName"
@save="handleMinderSave"
/>
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
{{ nodeData.text }}
</MsDrawer>
<!-- 脑图结束 -->
</div> </div>
</div> </div>
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]"> </keep-alive>
<!-- 脑图开始 -->
<MsFeatureCaseMinder
:module-id="props.activeFolder"
:modules-count="props.modulesCount"
:module-name="props.moduleName"
@save="handleMinderSave"
/>
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
{{ nodeData.text }}
</MsDrawer>
<!-- 脑图结束 -->
</div>
</div>
</div> </div>
<a-modal <a-modal
v-model:visible="showBatchMoveDrawer" v-model:visible="showBatchMoveDrawer"
@ -371,6 +382,7 @@
import { getFilterCustomFields, MsAdvanceFilter } from '@/components/pure/ms-advance-filter'; import { getFilterCustomFields, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem, FilterResult } from '@/components/pure/ms-advance-filter/type'; import { FilterFormItem, FilterResult } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsCacheWrapper from '@/components/pure/ms-cache-wrapper/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue'; import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import { MsExportDrawerMap, MsExportDrawerOption } from '@/components/pure/ms-export-drawer/types'; import { MsExportDrawerMap, MsExportDrawerOption } from '@/components/pure/ms-export-drawer/types';
import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
@ -443,6 +455,7 @@
} from '@/models/caseManagement/featureCase'; } from '@/models/caseManagement/featureCase';
import { ModuleTreeNode } from '@/models/common'; import { ModuleTreeNode } from '@/models/common';
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum'; import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
import { CacheTabTypeEnum } from '@/enums/cacheTabEnum';
import { CaseManagementRouteEnum, RouteEnum } from '@/enums/routeEnum'; import { CaseManagementRouteEnum, RouteEnum } from '@/enums/routeEnum';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum'; import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum'; import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';

View File

@ -92,7 +92,6 @@
</template> </template>
</MsSplitBox> </MsSplitBox>
</MsCard> </MsCard>
<!-- </div> -->
</template> </template>
<script setup lang="ts"> <script setup lang="ts">