feat(测试用例): 评审详情支持高级搜索

This commit is contained in:
teukkk 2024-09-09 15:01:49 +08:00 committed by Craftsman
parent 8d65802488
commit 4a480830f3
9 changed files with 182 additions and 99 deletions

View File

@ -158,7 +158,6 @@
const props = defineProps<{
moduleId: string;
viewFlag: boolean; //
viewStatusFlag: boolean; //
reviewProgress: string;
reviewPassRule: ReviewPassRule; //
@ -277,7 +276,6 @@
projectId: appStore.currentProjectId,
moduleId: node.data?.id,
reviewId: route.query.id as string,
viewFlag: props.viewFlag,
viewStatusFlag: props.viewStatusFlag,
});
//

View File

@ -404,7 +404,7 @@
const conditions = formModel.value.list.map(({ type, value, operator, customField, dataIndex }) => {
let timeValue;
//
if (type === FilterType.DATE_PICKER) {
if (type === FilterType.DATE_PICKER && value?.[0] && value?.[1]) {
timeValue =
operator === OperatorEnum.BETWEEN
? [new Date(value[0]).getTime(), new Date(value[1]).getTime()]

View File

@ -185,4 +185,5 @@ export function getAllDataDefaultConditions(viewType: ViewTypeEnum) {
// 系统视图对应不显示的第一列下拉条件
export const internalViewsHiddenConditionsMap: Record<string, string[]> = {
my_create: ['createUser'],
my_review: ['reviewId'],
};

View File

@ -117,7 +117,6 @@ export interface ReviewListQueryParams extends TableQueryParams {
}
// 评审详情-用例列表查询参数
export interface ReviewDetailCaseListQueryParams extends TableQueryParams {
viewFlag: boolean; // 是否只看我的
reviewId: string;
viewStatusFlag?: boolean; // 我的评审状态
}
@ -132,7 +131,6 @@ export interface SortReviewCaseParams {
// 评审详情-批量评审用例
export interface BatchReviewCaseParams extends BatchApiParams {
reviewId: string; // 评审id
userId: string; // 用户id, 用来判断是否只看我的
reviewPassRule: ReviewPassRule; // 评审规则
status: StartReviewStatus; // 评审结果
content: string; // 评论内容
@ -142,14 +140,12 @@ export interface BatchReviewCaseParams extends BatchApiParams {
// 评审详情-批量修改评审人
export interface BatchChangeReviewerParams extends BatchApiParams {
reviewId: string; // 评审id
userId: string; // 用户id, 用来判断是否只看我的
reviewerId: string[]; // 评审人员id
append: boolean; // 是否追加
}
// 评审详情-批量取消关联用例
export interface BatchCancelReviewCaseParams extends BatchApiParams {
reviewId: string; // 评审id
userId: string; // 用户id, 用来判断是否只看我的
}
export interface ReviewDetailReviewersItem {
avatar: string;
@ -275,7 +271,6 @@ export interface CaseReviewMinderParams {
moduleId: string;
current?: number;
reviewId: string;
viewFlag: boolean; // 是否只看我的
viewStatusFlag: boolean; // 我的评审结果
}

View File

@ -690,6 +690,7 @@
watch(
() => [props.activeModule, props.selectedProtocols],
() => {
if (isAdvancedSearchMode.value) return;
resetSelector();
loadApiList(true);
}

View File

@ -357,7 +357,6 @@
});
});
const viewFlag = ref(false);
const onlyMineStatus = ref(false);
const keyword = ref('');
const caseList = ref<ReviewCaseItem[]>([]);
@ -376,7 +375,6 @@
const res = await getReviewDetailCasePage({
projectId: appStore.currentProjectId,
reviewId: reviewId.value,
viewFlag: viewFlag.value,
viewStatusFlag: onlyMineStatus.value,
keyword: keyword.value,
current: pageNation.value.current || 1,
@ -620,12 +618,11 @@
total,
pageSize,
current,
viewFlag: _onlyMine,
keyword: _keyword,
viewId,
combineSearch,
filter,
combine,
sort,
searchMode,
moduleIds,
} = lastPageParams;
pageNation.value = {
@ -633,14 +630,13 @@
pageSize,
current,
};
viewFlag.value = !!_onlyMine;
keyword.value = _keyword;
tableFilter.value = filter;
type.value = filter.status;
otherListQueryParams.value = {
combine,
sort,
searchMode,
viewId,
combineSearch,
moduleIds,
};
} else {

View File

@ -2,15 +2,17 @@
<div class="h-full px-[24px] py-[16px]">
<div class="mb-[16px]">
<MsAdvanceFilter
ref="msAdvanceFilterRef"
v-model:keyword="keyword"
:view-type="ViewTypeEnum.REVIEW_FUNCTIONAL_CASE"
:filter-config-list="filterConfigList"
:row-count="filterRowCount"
:custom-fields-config-list="searchCustomFields"
:count="modulesCount[props.activeFolder] || 0"
:name="moduleNamePath"
:not-show-input-search="showType !== 'list'"
:search-placeholder="t('caseManagement.caseReview.searchPlaceholder')"
@keyword-search="searchCase()"
@adv-search="searchCase"
@adv-search="handleAdvSearch"
@refresh="handleRefreshAll"
>
<template v-if="showType !== 'list'" #nameRight>
@ -47,6 +49,7 @@
:action-config="batchActions"
no-disable
filter-icon-align-left
:not-show-table-filter="isAdvancedSearchMode"
v-on="propsEvent"
@filter-change="getModuleCount"
@batch-action="handleTableBatch"
@ -170,7 +173,6 @@
<MsCaseReviewMinder
ref="msCaseReviewMinderRef"
:module-id="props.activeFolder"
:view-flag="props.onlyMine"
:view-status-flag="onlyMineStatus"
:module-tree="moduleTree"
:review-progress="props.reviewProgress"
@ -333,7 +335,7 @@
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem, FilterResult } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
@ -358,8 +360,11 @@
getReviewDetailCasePage,
getReviewUsers,
} from '@/api/modules/case-management/caseReview';
import { editorUploadFile, getCaseDefaultFields } from '@/api/modules/case-management/featureCase';
import { getProjectMemberCommentOptions } from '@/api/modules/project-management/projectMember';
import {
editorUploadFile,
getCaseDefaultFields,
getCustomFieldsTable,
} from '@/api/modules/case-management/featureCase';
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
import { reviewResultMap } from '@/config/caseManagement';
import { useI18n } from '@/hooks/useI18n';
@ -374,13 +379,13 @@
import { ReviewCaseItem, ReviewItem, ReviewPassRule, ReviewResult } from '@/models/caseManagement/caseReview';
import { BatchApiParams, TableQueryParams } from '@/models/common';
import { FilterType } from '@/enums/advancedFilterEnum';
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
import { StartReviewStatus } from '@/enums/caseEnum';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { getCaseLevels } from '@/views/case-management/caseManagementFeature/components/utils';
import { executionResultMap, getCaseLevels } from '@/views/case-management/caseManagementFeature/components/utils';
const caseLevelFields = ref<Record<string, any>>({});
const caseLevelList = computed(() => {
@ -389,12 +394,11 @@
const props = defineProps<{
activeFolder: string;
onlyMine: boolean;
reviewPassRule: ReviewPassRule; //
offspringIds: string[]; // id
reviewProgress: string; //
}>();
const emit = defineEmits(['refresh', 'link', 'selectParentNode']);
const emit = defineEmits(['refresh', 'link', 'selectParentNode', 'handleAdvSearch']);
const router = useRouter();
const route = useRoute();
@ -409,8 +413,8 @@
const minderSelectData = ref<MinderJsonNodeData>(); //
const minderParams = ref();
const keyword = ref('');
const filterRowCount = ref(0);
const filterConfigList = ref<FilterFormItem[]>([]);
const msAdvanceFilterRef = ref<InstanceType<typeof MsAdvanceFilter>>();
const isAdvancedSearchMode = computed(() => msAdvanceFilterRef.value?.isAdvancedSearchMode);
const tableParams = ref<Record<string, any>>({});
const onlyMineStatus = ref(false);
const showType = ref<'list' | 'minder'>('list');
@ -432,6 +436,14 @@
hasAnyPermission(['CASE_REVIEW:READ+REVIEW', 'CASE_REVIEW:READ+RELEVANCE'])
);
const executeResultOptions = computed(() => {
return Object.keys(executionResultMap).map((key) => {
return {
value: key,
label: executionResultMap[key].statusText,
};
});
});
const reviewResultOptions = computed(() => {
return Object.keys(reviewResultMap).map((key) => {
return {
@ -522,7 +534,17 @@
},
];
const tableStore = useTableStore();
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, getTableQueryParams } = useTable(
const {
propsRes,
propsEvent,
viewId,
advanceFilter,
setAdvanceFilter,
loadList,
setLoadListParams,
resetSelector,
getTableQueryParams,
} = useTable(
getReviewDetailCasePage,
{
scroll: { x: '100%' },
@ -574,6 +596,7 @@
const modulesCount = computed(() => caseReviewStore.modulesCount);
async function getModuleCount() {
if (isAdvancedSearchMode.value) return;
let params: TableQueryParams;
if (showType.value === 'list') {
params = {
@ -588,7 +611,6 @@
await caseReviewStore.getModuleCount({
...params,
moduleIds: [],
viewFlag: props.onlyMine,
reviewId: route.query.id as string,
});
}
@ -597,9 +619,11 @@
tableParams.value = {
projectId: appStore.currentProjectId,
reviewId: route.query.id,
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
moduleIds:
props.activeFolder === 'all' || isAdvancedSearchMode.value ? [] : [props.activeFolder, ...props.offspringIds],
keyword: keyword.value,
viewFlag: props.onlyMine,
viewId: viewId.value,
combineSearch: advanceFilter,
};
setLoadListParams(tableParams.value);
resetSelector();
@ -655,17 +679,10 @@
refresh();
}
watch(
() => props.onlyMine,
() => {
refresh();
}
);
watch(
() => props.activeFolder,
() => {
if (showType.value === 'list') {
if (showType.value === 'list' && !isAdvancedSearchMode.value) {
searchCase();
}
}
@ -793,7 +810,6 @@
dialogLoading.value = true;
await batchDisassociateReviewCase({
reviewId: route.query.id as string,
userId: props.onlyMine ? userStore.id || '' : '',
...(showType.value === 'list'
? {
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
@ -856,7 +872,6 @@
dialogLoading.value = true;
await batchReview({
reviewId: route.query.id as string,
userId: props.onlyMine ? userStore.id || '' : '',
reviewPassRule: props.reviewPassRule,
status: 'RE_REVIEWED',
content: dialogForm.value.reason,
@ -894,7 +909,6 @@
dialogLoading.value = true;
await batchChangeReviewer({
reviewId: route.query.id as string,
userId: props.onlyMine ? userStore.id || '' : '',
reviewerId: dialogForm.value.reviewer.length > 0 ? dialogForm.value.reviewer : record.reviewers,
append: dialogForm.value.isAppend, //
...(showType.value === 'list'
@ -937,7 +951,6 @@
dialogLoading.value = true;
await batchReview({
reviewId: route.query.id as string,
userId: props.onlyMine ? userStore.id || '' : '',
reviewPassRule: props.reviewPassRule,
status: dialogForm.value.result as StartReviewStatus,
content: dialogForm.value.reason,
@ -991,6 +1004,129 @@
}
}
const filterConfigList = computed<FilterFormItem[]>(() => [
{
title: 'caseManagement.featureCase.tableColumnID',
dataIndex: 'num',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnName',
dataIndex: 'name',
type: FilterType.INPUT,
},
{
title: 'common.belongModule',
dataIndex: 'moduleId',
type: FilterType.TREE_SELECT,
treeSelectData: moduleTree.value,
treeSelectProps: {
fieldNames: {
title: 'name',
key: 'id',
children: 'children',
},
multiple: true,
treeCheckable: true,
treeCheckStrictly: true,
maxTagCount: 1,
},
},
{
title: 'caseManagement.featureCase.tableColumnReviewResult',
dataIndex: 'status',
type: FilterType.SELECT,
selectProps: {
multiple: true,
options: reviewResultOptions.value,
},
},
{
title: 'caseManagement.caseReview.reviewer',
dataIndex: 'reviewers',
type: FilterType.SELECT,
selectProps: {
multiple: true,
options: reviewersOptions.value,
},
},
{
title: 'caseManagement.featureCase.tableColumnExecutionResult',
dataIndex: 'lastExecuteResult',
type: FilterType.SELECT,
selectProps: {
multiple: true,
options: executeResultOptions.value,
},
},
{
title: 'caseManagement.featureCase.associatedDemand',
dataIndex: 'demand',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.relatedAttachments',
dataIndex: 'attachment',
type: FilterType.INPUT,
},
{
title: 'common.creator',
dataIndex: 'createUser',
type: FilterType.MEMBER,
},
{
title: 'common.createTime',
dataIndex: 'createTime',
type: FilterType.DATE_PICKER,
},
{
title: 'common.updateUserName',
dataIndex: 'updateUser',
type: FilterType.MEMBER,
},
{
title: 'common.updateTime',
dataIndex: 'updateTime',
type: FilterType.DATE_PICKER,
},
{
title: 'common.tag',
dataIndex: 'tags',
type: FilterType.TAGS_INPUT,
},
]);
const searchCustomFields = ref<FilterFormItem[]>([]);
async function initFilter() {
const result = await getCustomFieldsTable(appStore.currentProjectId);
searchCustomFields.value = result.map((item: any) => {
const FilterTypeKey: keyof typeof FilterType = CustomTypeMaps[item.type].type;
const formType = FilterType[FilterTypeKey];
const formObject = CustomTypeMaps[item.type];
const { props: formProps } = formObject;
const currentItem: any = {
title: item.name,
dataIndex: item.id,
type: formType,
customField: true,
};
if (formObject.propsKey && formProps.options) {
formProps.options = item.options;
currentItem[formObject.propsKey] = {
...formProps,
};
}
return currentItem;
});
}
//
const handleAdvSearch = async (filter: FilterResult, id: string, isStartAdvance: boolean) => {
emit('handleAdvSearch', isStartAdvance);
keyword.value = '';
setAdvanceFilter(filter, id);
searchCase();
};
function handleOperation(type?: string) {
switch (type) {
case 'review':
@ -1045,53 +1181,8 @@
}
async function mountedLoad() {
const [, memberRes] = await Promise.all([
initReviewers(),
getProjectMemberCommentOptions(appStore.currentProjectId, keyword.value),
]);
const memberOptions = memberRes.map((e) => ({ label: e.name, value: e.id }));
filterConfigList.value = [
{
title: 'ID',
dataIndex: 'id',
type: FilterType.INPUT,
},
{
title: 'caseManagement.caseReview.caseName',
dataIndex: 'name',
type: FilterType.INPUT,
},
{
title: 'caseManagement.caseReview.reviewer',
dataIndex: 'reviewers',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: reviewersOptions.value,
},
},
{
title: 'caseManagement.caseReview.reviewResult',
dataIndex: 'status',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: Object.keys(reviewResultMap).map((e) => ({
label: t(reviewResultMap[e as ReviewResult].label),
value: e,
})),
},
},
{
title: 'caseManagement.caseReview.creator',
dataIndex: 'createUser',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions,
},
},
];
initReviewers();
await initFilter();
}
onBeforeMount(() => {

View File

@ -164,6 +164,7 @@
defineExpose({
initModules,
setActiveFolder,
selectParentNode,
});
</script>

View File

@ -29,10 +29,6 @@
<MsStatusTag :status="(reviewDetail.status as ReviewStatus)" class="mx-[16px]" />
</template>
<template #headerRight>
<div class="mr-[16px] flex items-center">
<a-switch v-model:model-value="onlyMine" size="small" class="mr-[8px]" type="line" />
{{ t('caseManagement.caseReview.onlyMine') }}
</div>
<MsButton
v-permission="['CASE_REVIEW:READ+UPDATE']"
type="button"
@ -106,7 +102,7 @@
</MsCard>
<!-- special-height的170: 上面卡片高度105 + mt的16 -->
<MsCard class="mt-[16px]" :special-height="121" simple has-breadcrumb no-content-padding>
<MsSplitBox>
<MsSplitBox :not-show-first="isAdvancedSearchMode">
<template #first>
<div class="p-[16px]">
<CaseTree
@ -121,7 +117,6 @@
<CaseTable
ref="caseTableRef"
:active-folder="activeFolderId"
:only-mine="onlyMine"
:review-pass-rule="reviewDetail.reviewPassRule"
:offspring-ids="offspringIds"
:modules-count="modulesCount"
@ -129,6 +124,7 @@
@refresh="initDetail()"
@link="associateDrawerVisible = true"
@select-parent-node="selectParentNode"
@handle-adv-search="handleAdvSearch"
></CaseTable>
</template>
</MsSplitBox>
@ -204,8 +200,6 @@
}
}
const onlyMine = ref(false);
// const showTab = ref(0);
// const tabList = ref([
// {
@ -225,6 +219,12 @@
});
const caseTableRef = ref<InstanceType<typeof CaseTable>>();
const isAdvancedSearchMode = ref(false);
function handleAdvSearch(isStartAdvance: boolean) {
isAdvancedSearchMode.value = isStartAdvance;
folderTreeRef.value?.setActiveFolder('all');
}
function selectParentNode(folderTree: ModuleTreeNode[]) {
folderTreeRef.value?.selectParentNode(folderTree);
}