feat(功能用例): 修改表Filter和替换功能用例表格筛选过滤
This commit is contained in:
parent
68a79b030d
commit
01041aa309
|
@ -65,7 +65,7 @@
|
|||
}
|
||||
);
|
||||
|
||||
const currentValue = defineModel<string[] | string>({ default: [] });
|
||||
const currentValue = defineModel<(string | number)[] | string>({ default: [] });
|
||||
const loading = ref(true);
|
||||
|
||||
const loadList = async (params: Record<string, any>) => {
|
||||
|
|
|
@ -67,7 +67,10 @@
|
|||
:filterable="item.filterable"
|
||||
:cell-class="item.cellClass"
|
||||
:header-cell-class="`${
|
||||
item.headerCellClass || (item.filterConfig && isHighlightFilterBackground) ? 'header-cell-filter' : ''
|
||||
item.headerCellClass ||
|
||||
(item.filterConfig && isHighlightFilterBackground && activeDataIndex === item.dataIndex)
|
||||
? 'header-cell-filter'
|
||||
: ''
|
||||
}`"
|
||||
:body-cell-class="item.bodyCellClass"
|
||||
:summary-cell-class="item.summaryCellClass"
|
||||
|
@ -104,9 +107,11 @@
|
|||
v-else-if="item.filterConfig"
|
||||
class="ml-[4px]"
|
||||
:options="item.filterConfig.options"
|
||||
:data-index="item.dataIndex"
|
||||
v-bind="item.filterConfig"
|
||||
@handle-confirm="(v) => handleFilterConfirm(v, item.dataIndex as string, item.isCustomParam || false)"
|
||||
@show="showFilter(true)"
|
||||
@hide="showFilter(false)"
|
||||
@show="showFilter(true, item.dataIndex)"
|
||||
@hide="showFilter(false, item.dataIndex)"
|
||||
>
|
||||
<template #item="{ filterItem }">
|
||||
<slot :name="item.filterConfig.filterSlotName" :filter-content="filterItem"> </slot>
|
||||
|
@ -348,7 +353,12 @@
|
|||
(e: 'expand', record: TableData): void | Promise<any>;
|
||||
(e: 'cell-click', record: TableData, column: TableColumnData, ev: Event): void | Promise<any>;
|
||||
(e: 'clearSelector'): void;
|
||||
(e: 'filterChange', dataIndex: string, value: (string | number)[], isCustomParam: boolean): void;
|
||||
(
|
||||
e: 'filterChange',
|
||||
dataIndex: string,
|
||||
value: string[] | (string | number)[] | undefined,
|
||||
isCustomParam: boolean
|
||||
): void;
|
||||
(e: 'moduleChange'): void;
|
||||
(e: 'initEnd'): void;
|
||||
}>();
|
||||
|
@ -630,7 +640,11 @@
|
|||
columnSelectorVisible.value = true;
|
||||
};
|
||||
|
||||
const handleFilterConfirm = (value: (string | number)[], dataIndex: string, isCustomParam: boolean) => {
|
||||
const handleFilterConfirm = (
|
||||
value: string[] | (string | number)[] | undefined,
|
||||
dataIndex: string,
|
||||
isCustomParam: boolean
|
||||
) => {
|
||||
emit('filterChange', dataIndex, value, isCustomParam);
|
||||
};
|
||||
|
||||
|
@ -640,7 +654,9 @@
|
|||
});
|
||||
|
||||
const isHighlightFilterBackground = ref<boolean>(false);
|
||||
function showFilter(visible: boolean) {
|
||||
const activeDataIndex = ref<string | undefined>('');
|
||||
function showFilter(visible: boolean, dataIndex: string | undefined) {
|
||||
activeDataIndex.value = dataIndex;
|
||||
isHighlightFilterBackground.value = visible;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="arco-table-filters-content-list">
|
||||
<div class="max-h-[300px] overflow-y-auto px-[12px] py-[4px]">
|
||||
<a-checkbox-group v-model="checkedList" size="mini" direction="vertical">
|
||||
<div class="arco-table-filters-content-wrap max-h-[300px] px-[12px] py-[4px]">
|
||||
<a-checkbox-group v-if="props.mode === 'static'" v-model="checkedList" size="mini" direction="vertical">
|
||||
<a-checkbox
|
||||
v-for="(item, index) of props.options"
|
||||
:key="item[props.valueKey || 'value']"
|
||||
|
@ -32,7 +32,31 @@
|
|||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="arco-table-filters-bottom">
|
||||
<div v-if="props.mode === 'remote'" class="w-[200px] p-[12px] pb-[8px]">
|
||||
<MsSelect
|
||||
v-model:model-value="checkedList"
|
||||
mode="remote"
|
||||
:options="[]"
|
||||
:placeholder="props.placeholderText"
|
||||
multiple
|
||||
:value-key="props.valueKey || 'id'"
|
||||
:label-key="props.labelKey"
|
||||
:filter-option="false"
|
||||
allow-clear
|
||||
:search-keys="['name']"
|
||||
:loading="loading"
|
||||
:remote-func="loadList"
|
||||
:remote-extra-params="{ ...props.loadOptionParams }"
|
||||
:option-label-render="optionLabelRender"
|
||||
:should-calculate-max-tag="false"
|
||||
>
|
||||
</MsSelect>
|
||||
</div>
|
||||
<div
|
||||
:class="`${
|
||||
props.mode === 'static' ? 'justify-between' : 'justify-end'
|
||||
} arco-table-filters-bottom flex h-[38px] items-center`"
|
||||
>
|
||||
<a-button size="mini" type="secondary" @click="handleFilterReset">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
|
@ -47,20 +71,37 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { SelectOptionData } from '@arco-design/web-vue';
|
||||
|
||||
import MsSelect from '@/components/business/ms-select/index';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { FilterRemoteMethodsEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import { initRemoteOptionsFunc } from './filterConfig';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export interface FilterListItem {
|
||||
[key: string]: any;
|
||||
}
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
options?: FilterListItem[];
|
||||
valueKey?: string;
|
||||
labelKey?: string;
|
||||
}>();
|
||||
mode?: 'static' | 'remote';
|
||||
remoteMethod?: FilterRemoteMethodsEnum; // 加载选项方法
|
||||
loadOptionParams?: Record<string, any>; // 请求下拉的参数
|
||||
placeholderText?: string;
|
||||
}>(),
|
||||
{
|
||||
mode: 'static',
|
||||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'handleConfirm', value: (string | number)[]): void;
|
||||
(e: 'handleConfirm', value: (string | number)[] | string[] | undefined): void;
|
||||
}>();
|
||||
|
||||
const visible = ref(false);
|
||||
|
@ -77,4 +118,43 @@
|
|||
emit('handleConfirm', checkedList.value);
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
const optionLabelRender = (option: SelectOptionData) => {
|
||||
if (option.email !== '') {
|
||||
return `<span class='text-[var(--color-text-1)]'>${option.name}</span><span class='text-[var(--color-text-4)] ml-[4px]'>(${option.email})</span>`;
|
||||
}
|
||||
return `<span class='text-[var(--color-text-1)]'>${option.name}</span>`;
|
||||
};
|
||||
|
||||
const loadList = async (params: Record<string, any>) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { keyword, ...rest } = params;
|
||||
if (props.remoteMethod) {
|
||||
const list = (await initRemoteOptionsFunc(props.remoteMethod, { keyword, ...rest })) || [];
|
||||
list.forEach((item: any) => {
|
||||
if (props.valueKey) {
|
||||
item.id = item[props.valueKey || 'id'] as string;
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
return [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.arco-table-filters-content-wrap {
|
||||
@apply overflow-y-auto;
|
||||
.ms-scroll-bar();
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { getProjectMemberOptions } from '@/api/modules/project-management/projectMember';
|
||||
|
||||
import { FilterRemoteMethodsEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
export function initRemoteOptionsFunc(remoteMethod: string, params: Record<string, any>) {
|
||||
switch (remoteMethod) {
|
||||
case FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER:
|
||||
return getProjectMemberOptions(params.projectId, params.keyword);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
|
@ -1,6 +1,6 @@
|
|||
import type { TableQueryParams } from '@/models/common';
|
||||
import { ColumnEditTypeEnum, SelectAllEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import type { TableChangeExtra, TableColumnData, TableData, TableDraggable } from '@arco-design/web-vue';
|
||||
|
||||
|
@ -20,6 +20,12 @@ export interface MsTableColumnFilterConfig {
|
|||
options?: Record<string, any>[]; // 筛选数据
|
||||
valueKey?: string;
|
||||
labelKey?: string;
|
||||
mode?: 'static' | 'remote';
|
||||
remoteMethod?: FilterRemoteMethodsEnum; // 加载选项的类型
|
||||
loadOptionParams?: Record<string, any>; // 请求下拉的参数
|
||||
placeholderText?: string;
|
||||
firstLabelKey?: string;
|
||||
secondLabelKey?: string;
|
||||
}
|
||||
|
||||
export interface MsTableColumnData extends TableColumnData {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// hook/table-props.ts
|
||||
|
||||
import { ref, watchEffect } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useAppStore, useTableStore } from '@/store';
|
||||
|
@ -356,15 +357,12 @@ export default function useTableProps<T>(
|
|||
multiple: boolean,
|
||||
isCustomParma: boolean
|
||||
) => {
|
||||
if (filteredValues.length > 0) {
|
||||
if (isCustomParma) {
|
||||
filterItem.value = { [`custom_${multiple ? 'multiple' : 'single'}_${dataIndex}`]: filteredValues };
|
||||
} else {
|
||||
filterItem.value = { [dataIndex]: filteredValues };
|
||||
}
|
||||
} else {
|
||||
filterItem.value = {};
|
||||
filterItem.value = { ...getTableQueryParams().filter, [dataIndex]: filteredValues };
|
||||
}
|
||||
propsRes.value.filter = cloneDeep(filterItem.value);
|
||||
loadList();
|
||||
},
|
||||
// 分页触发
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
export enum FilterSlotNameEnum {
|
||||
TEST_PLAN_STATUS_FILTER = 'TEST_PLAN_STATUS_FILTER',
|
||||
TEST_PLAN_STATUS_FILTER = 'TEST_PLAN_STATUS_FILTER', // 测试计划执行结果
|
||||
CASE_MANAGEMENT_EXECUTE_RESULT = 'CASE_MANAGEMENT_EXECUTE_RESULT', // 功能用例执行结果
|
||||
CASE_MANAGEMENT_REVIEW_RESULT = 'CASE_MANAGEMENT_REVIEW_RESULT', // 功能用例评审结果
|
||||
CASE_MANAGEMENT_REVIEW_STATUS = 'CASE_MANAGEMENT_REVIEW_STATUS', // 用例评审评审状态
|
||||
CASE_MANAGEMENT_CASE_LEVEL = 'CASE_MANAGEMENT_CASE_LEVEL', // 用例等级
|
||||
CASE_MANAGEMENT_BUG_STATE = 'CASE_MANAGEMENT_BUG_STATE', // 缺陷状态
|
||||
API_TEST_API_REQUEST_METHODS = 'API_TEST_API_REQUEST_METHODS', // 接口测试请求方式
|
||||
API_TEST_API_REQUEST_API_STATUS = 'API_TEST_API_REQUEST_API_STATUS', // 接口测试接口状态
|
||||
}
|
||||
|
||||
export enum FilterRemoteMethodsEnum {
|
||||
PROJECT_PERMISSION_MEMBER = 'PROJECT_PERMISSION_MEMBER', // 项目下成员
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -71,92 +71,22 @@
|
|||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template #caseLevelFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="caseFilterVisible"
|
||||
v-model:status-filters="caseFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="caseLevelList"
|
||||
value-key="value"
|
||||
@search="initData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<div class="flex"> <caseLevel :case-level="item.text" /></div>
|
||||
<!-- 用例等级 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
||||
<caseLevel :case-level="filterContent.text" />
|
||||
</template>
|
||||
</TableFilter>
|
||||
<!-- 执行结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
|
||||
<ExecuteStatusTag :status="filterContent.value" />
|
||||
</template>
|
||||
<template #executeResultFilter="{ columnConfig }">
|
||||
<a-trigger
|
||||
v-model:popup-visible="executeResultFilterVisible"
|
||||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px]"
|
||||
size="mini"
|
||||
@click="executeResultFilterVisible = true"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
</div>
|
||||
<icon-down :class="executeResultFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="arco-table-filters-content-list">
|
||||
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="executeResultFilters" direction="vertical" size="mini">
|
||||
<a-checkbox v-for="key of Object.keys(executionResultMap)" :key="key" :value="key">
|
||||
<!-- 评审结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_RESULT]="{ filterContent }">
|
||||
<MsIcon
|
||||
:type="executionResultMap[key]?.icon || ''"
|
||||
:type="statusIconMap[filterContent.value]?.icon"
|
||||
class="mr-1"
|
||||
:class="[executionResultMap[key].color]"
|
||||
:class="[statusIconMap[filterContent.value].color]"
|
||||
></MsIcon>
|
||||
<span>{{ executionResultMap[key]?.statusText || '' }} </span>
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="filter-button">
|
||||
<a-button size="mini" class="mr-[8px]" @click="resetExecuteResultFilter">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
<a-button type="primary" size="mini" @click="handleFilterHidden(false)">
|
||||
{{ t('system.orgTemplate.confirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</template>
|
||||
<template #updateUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="updateUserFilterVisible"
|
||||
v-model:status-filters="updateUserFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="memberOptions"
|
||||
label-key="label"
|
||||
@search="initData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
<template #createUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="createUserFilterVisible"
|
||||
v-model:status-filters="createUserFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="memberOptions"
|
||||
label-key="label"
|
||||
@search="initData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
<span>{{ statusIconMap[filterContent.value]?.statusText }} </span>
|
||||
</template>
|
||||
<template #reviewStatus="{ record }">
|
||||
<MsIcon
|
||||
|
@ -166,45 +96,6 @@
|
|||
></MsIcon>
|
||||
<span>{{ statusIconMap[record.reviewStatus]?.statusText || '' }} </span>
|
||||
</template>
|
||||
<template #reviewStatusFilter="{ columnConfig }">
|
||||
<a-trigger v-model:popup-visible="statusFilterVisible" trigger="click" @popup-visible-change="handleFilterHidden">
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px]"
|
||||
size="mini"
|
||||
@click="statusFilterVisible = true"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
</div>
|
||||
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of Object.keys(statusIconMap)" :key="key" :value="key">
|
||||
<MsIcon
|
||||
:type="statusIconMap[key]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[statusIconMap[key].color]"
|
||||
></MsIcon>
|
||||
<span>{{ statusIconMap[key]?.statusText || '' }} </span>
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="filter-button">
|
||||
<a-button size="mini" class="mr-[8px]" @click="resetReviewStatusFilter">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
<a-button type="primary" size="mini" @click="handleFilterHidden(false)">
|
||||
{{ t('system.orgTemplate.confirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</template>
|
||||
<template #lastExecuteResult="{ record }">
|
||||
<executeResult :execute-result="record.lastExecuteResult" />
|
||||
</template>
|
||||
|
@ -394,6 +285,7 @@
|
|||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Message, TableChangeExtra, TableData, TreeNodeData } from '@arco-design/web-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import { FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
|
||||
|
@ -406,16 +298,15 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import type { TagType, Theme } from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import executeResult from '@/components/business/ms-case-associate/executeResult.vue';
|
||||
import BatchEditModal from './batchEditModal.vue';
|
||||
import CaseDetailDrawer from './caseDetailDrawer.vue';
|
||||
import FeatureCaseTree from './caseTree.vue';
|
||||
import ExecuteStatusTag from './excuteStatusTag.vue';
|
||||
import ExportExcelDrawer from './exportExcelDrawer.vue';
|
||||
import AddDemandModal from './tabContent/tabDemand/addDemandModal.vue';
|
||||
import ThirdDemandDrawer from './tabContent/tabDemand/thirdDemandDrawer.vue';
|
||||
import TableFilter from './tableFilter.vue';
|
||||
|
||||
import {
|
||||
batchAssociationDemand,
|
||||
|
@ -451,6 +342,7 @@
|
|||
import { ModuleTreeNode } from '@/models/common';
|
||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import { executionResultMap, getCaseLevels, getTableFields, statusIconMap } from './utils';
|
||||
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
|
||||
|
@ -559,7 +451,24 @@
|
|||
hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE', 'FUNCTIONAL_CASE:READ+DELETE'])
|
||||
);
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
const executeResultOptions = computed(() => {
|
||||
return Object.keys(executionResultMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: executionResultMap[key].statusText,
|
||||
};
|
||||
});
|
||||
});
|
||||
const reviewResultOptions = computed(() => {
|
||||
return Object.keys(statusIconMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: statusIconMap[key].statusText,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const firstStaticColumn: MsTableColumn = [
|
||||
{
|
||||
'title': 'caseManagement.featureCase.tableColumnID',
|
||||
'slotName': 'num',
|
||||
|
@ -592,20 +501,43 @@
|
|||
showDrag: false,
|
||||
columnSelectorDisabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
const caseLevelColumn: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnLevel',
|
||||
slotName: 'caseLevel',
|
||||
dataIndex: 'caseLevel',
|
||||
titleSlotName: 'caseLevelFilter',
|
||||
filterConfig: {
|
||||
options: [],
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL,
|
||||
},
|
||||
showInTable: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
},
|
||||
];
|
||||
const operationColumn: MsTableColumn = [
|
||||
{
|
||||
title: hasOperationPermission.value ? 'caseManagement.featureCase.tableColumnActions' : '',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
width: hasOperationPermission.value ? 140 : 50,
|
||||
},
|
||||
];
|
||||
|
||||
const lastStaticColumn: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnReviewResult',
|
||||
dataIndex: 'reviewStatus',
|
||||
slotName: 'reviewStatus',
|
||||
titleSlotName: 'reviewStatusFilter',
|
||||
filterConfig: {
|
||||
options: reviewResultOptions.value,
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_RESULT,
|
||||
},
|
||||
showInTable: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
|
@ -615,6 +547,10 @@
|
|||
dataIndex: 'lastExecuteResult',
|
||||
slotName: 'lastExecuteResult',
|
||||
titleSlotName: 'executeResultFilter',
|
||||
filterConfig: {
|
||||
options: executeResultOptions.value,
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT,
|
||||
},
|
||||
showInTable: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
|
@ -650,7 +586,14 @@
|
|||
slotName: 'updateUserName',
|
||||
showTooltip: true,
|
||||
dataIndex: 'updateUserName',
|
||||
titleSlotName: 'updateUserFilter',
|
||||
filterConfig: {
|
||||
mode: 'remote',
|
||||
loadOptionParams: {
|
||||
projectId: appStore.currentProjectId,
|
||||
},
|
||||
remoteMethod: FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER,
|
||||
placeholderText: t('caseManagement.featureCase.PleaseSelect'),
|
||||
},
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
|
@ -671,7 +614,14 @@
|
|||
title: 'caseManagement.featureCase.tableColumnCreateUser',
|
||||
slotName: 'createUserName',
|
||||
dataIndex: 'createUserName',
|
||||
titleSlotName: 'createUserFilter',
|
||||
filterConfig: {
|
||||
mode: 'remote',
|
||||
loadOptionParams: {
|
||||
projectId: appStore.currentProjectId,
|
||||
},
|
||||
remoteMethod: FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER,
|
||||
placeholderText: t('caseManagement.featureCase.PleaseSelect'),
|
||||
},
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
|
@ -689,16 +639,8 @@
|
|||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: hasOperationPermission.value ? 'caseManagement.featureCase.tableColumnActions' : '',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
width: hasOperationPermission.value ? 140 : 50,
|
||||
},
|
||||
];
|
||||
|
||||
const platformInfo = ref<Record<string, any>>({});
|
||||
const tableBatchActions = {
|
||||
baseAction: [
|
||||
|
@ -766,8 +708,7 @@
|
|||
const filterConfigList = ref<FilterFormItem[]>([]);
|
||||
const searchCustomFields = ref<FilterFormItem[]>([]);
|
||||
const memberOptions = ref<{ label: string; value: string }[]>([]);
|
||||
const updateUserFilters = ref<string[]>([]);
|
||||
const createUserFilters = ref<string[]>([]);
|
||||
|
||||
async function initFilter() {
|
||||
const result = await getCustomFieldsTable(currentProjectId.value);
|
||||
memberOptions.value = await getProjectOptions(appStore.currentProjectId, keyword.value);
|
||||
|
@ -932,9 +873,6 @@
|
|||
excludeIds: [],
|
||||
currentSelectCount: 0,
|
||||
});
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const caseFilters = ref<string[]>([]);
|
||||
const executeResultFilters = ref<string[]>([]);
|
||||
|
||||
const conditionParams = ref({
|
||||
keyword: '',
|
||||
|
@ -964,13 +902,6 @@
|
|||
selectAll: batchParams.value.selectAll,
|
||||
selectIds: batchParams.value.selectedIds as string[],
|
||||
keyword: keyword.value,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
},
|
||||
combine: batchParams.value.condition,
|
||||
};
|
||||
}
|
||||
|
@ -994,39 +925,16 @@
|
|||
return (nodeValue as ModuleTreeNode).name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
|
||||
}
|
||||
|
||||
const searchParams = ref<TableQueryParams>({
|
||||
projectId: currentProjectId.value,
|
||||
moduleIds: [],
|
||||
});
|
||||
|
||||
const caseLevelFields = ref<Record<string, any>>({});
|
||||
// 用例等级表头检索
|
||||
const caseFilterVisible = ref(false);
|
||||
|
||||
const caseLevelList = computed(() => {
|
||||
return caseLevelFields.value?.options || [];
|
||||
});
|
||||
|
||||
function getExecuteResultList() {
|
||||
const list: any = [];
|
||||
Object.keys(executionResultMap).forEach((key) => {
|
||||
list.push({
|
||||
...executionResultMap[key],
|
||||
});
|
||||
});
|
||||
return list;
|
||||
}
|
||||
const executeResultFilterList = ref(getExecuteResultList());
|
||||
|
||||
async function getLoadListParams() {
|
||||
setLoadListParams(await initTableParams());
|
||||
}
|
||||
|
||||
// 执行结果表头检索
|
||||
const executeResultFilterVisible = ref(false);
|
||||
const updateUserFilterVisible = ref(false);
|
||||
const createUserFilterVisible = ref(false);
|
||||
|
||||
// 初始化列表
|
||||
async function initData() {
|
||||
await getLoadListParams();
|
||||
|
@ -1123,13 +1031,7 @@
|
|||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
condition: {
|
||||
keyword: keyword.value,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
},
|
||||
filter: propsRes.value.filter,
|
||||
combine: batchParams.value.condition,
|
||||
},
|
||||
projectId: currentProjectId.value,
|
||||
|
@ -1210,13 +1112,7 @@
|
|||
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
|
||||
condition: {
|
||||
keyword: keyword.value,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
},
|
||||
filter: propsRes.value.filter,
|
||||
combine: batchParams.value.condition,
|
||||
},
|
||||
selectAll,
|
||||
|
@ -1349,23 +1245,20 @@
|
|||
caseLevelFields.value = result.customFields.find(
|
||||
(item: any) => item.internal && (item.fieldName === 'Case Priority' || item.fieldName === '用例等级')
|
||||
);
|
||||
if (caseLevelColumn[0].filterConfig?.options) {
|
||||
caseLevelColumn[0].filterConfig.options = cloneDeep(unref(caseLevelFields.value?.options)) || [];
|
||||
}
|
||||
|
||||
fullColumns = [
|
||||
...columns.slice(0, columns.length - 1),
|
||||
...firstStaticColumn,
|
||||
...caseLevelColumn,
|
||||
...lastStaticColumn,
|
||||
...customFieldsColumns,
|
||||
...columns.slice(columns.length - 1, columns.length),
|
||||
...operationColumn,
|
||||
];
|
||||
await tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_TABLE, fullColumns, 'drawer', true);
|
||||
}
|
||||
|
||||
// 如果是用例等级
|
||||
function isCaseLevel(slotFieldId: string) {
|
||||
const currentItem = initDefaultFields.value.find((item: any) => item.fieldId === slotFieldId);
|
||||
return {
|
||||
name: currentItem?.fieldName,
|
||||
type: currentItem?.type,
|
||||
options: currentItem?.options,
|
||||
};
|
||||
}
|
||||
// 获取更新自定义字段参数
|
||||
function getCustomsParams(detailResult: CaseManagementTable, record: CaseManagementTable) {
|
||||
const customFieldsList = Object.keys(record).filter((item) => item.includes('rule-'));
|
||||
|
@ -1402,28 +1295,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 如果是用例状态
|
||||
function getCaseState(caseState: string | undefined): { type: TagType; theme: Theme } {
|
||||
switch (caseState) {
|
||||
case '已完成':
|
||||
return {
|
||||
type: 'success',
|
||||
theme: 'default',
|
||||
};
|
||||
case '进行中':
|
||||
return {
|
||||
type: 'link',
|
||||
theme: 'default',
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
type: 'default',
|
||||
theme: 'default',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 模块树改变回调
|
||||
async function handleChangeModule(
|
||||
record: CaseManagementTable,
|
||||
|
@ -1460,13 +1331,7 @@
|
|||
condition: {
|
||||
keyword: keyword.value,
|
||||
searchMode: accordBelow,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
},
|
||||
filter: propsRes.value.filter,
|
||||
combine,
|
||||
},
|
||||
};
|
||||
|
@ -1588,22 +1453,10 @@
|
|||
moduleId: selectedModuleKeys.value[0],
|
||||
demandPlatform,
|
||||
demandList,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
},
|
||||
filter: propsRes.value.filter,
|
||||
condition: {
|
||||
keyword: keyword.value,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
},
|
||||
filter: propsRes.value.filter,
|
||||
combine: batchParams.value.condition,
|
||||
},
|
||||
functionalDemandBatchRequest,
|
||||
|
@ -1622,26 +1475,6 @@
|
|||
|
||||
const statusFilterVisible = ref(false);
|
||||
|
||||
function handleFilterHidden(val: boolean) {
|
||||
if (!val) {
|
||||
initData();
|
||||
statusFilterVisible.value = false;
|
||||
executeResultFilterVisible.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetReviewStatusFilter() {
|
||||
statusFilters.value = [];
|
||||
statusFilterVisible.value = false;
|
||||
initData();
|
||||
}
|
||||
|
||||
function resetExecuteResultFilter() {
|
||||
executeResultFilters.value = [];
|
||||
executeResultFilterVisible.value = false;
|
||||
initData();
|
||||
}
|
||||
|
||||
// 获取三方需求
|
||||
onBeforeMount(async () => {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<div class="flex items-center justify-start">
|
||||
<MsIcon
|
||||
:type="executionResultMap[props.status]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[executionResultMap[props.status].color]"
|
||||
></MsIcon>
|
||||
<span>{{ executionResultMap[props.status]?.statusText || '' }} </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @desc 功能用例的执行结果状态
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { StatusType } from '@/enums/caseEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
status: StatusType;
|
||||
}>();
|
||||
|
||||
const executionResultMap: Record<string, any> = {
|
||||
UN_EXECUTED: {
|
||||
key: 'UN_EXECUTED',
|
||||
icon: StatusType.UN_EXECUTED,
|
||||
statusText: t('caseManagement.featureCase.nonExecution'),
|
||||
color: 'text-[var(--color-text-brand)]',
|
||||
},
|
||||
PASSED: {
|
||||
key: 'PASSED',
|
||||
icon: StatusType.PASSED,
|
||||
statusText: t('caseManagement.featureCase.passed'),
|
||||
color: '',
|
||||
},
|
||||
/* SKIPPED: {
|
||||
key: 'SKIPPED',
|
||||
icon: StatusType.SKIPPED,
|
||||
statusText: t('caseManagement.featureCase.skip'),
|
||||
color: 'text-[rgb(var(--link-6))]',
|
||||
}, */
|
||||
BLOCKED: {
|
||||
key: 'BLOCKED',
|
||||
icon: StatusType.BLOCKED,
|
||||
statusText: t('caseManagement.featureCase.chokeUp'),
|
||||
color: 'text-[rgb(var(--warning-6))]',
|
||||
},
|
||||
FAILED: {
|
||||
key: 'FAILED',
|
||||
icon: StatusType.FAILED,
|
||||
statusText: t('caseManagement.featureCase.failure'),
|
||||
color: '',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
|
@ -106,73 +106,22 @@
|
|||
<template #caseLevel="{ record }">
|
||||
<caseLevel :case-level="(getCaseLevels(record.customFields) as CaseLevel)" />
|
||||
</template>
|
||||
<template #caseLevelFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="caseFilterVisible"
|
||||
v-model:status-filters="caseFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="caseLevelList"
|
||||
value-key="value"
|
||||
@search="initRecycleList()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<div class="flex"> <caseLevel :case-level="item.text" /></div>
|
||||
<!-- 用例等级 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
||||
<caseLevel :case-level="filterContent.text" />
|
||||
</template>
|
||||
</TableFilter>
|
||||
<!-- 执行结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
|
||||
<ExecuteStatusTag :status="filterContent.value" />
|
||||
</template>
|
||||
<template #executeResultFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="executeResultFilterVisible"
|
||||
v-model:status-filters="executeResultFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="executeResultFilterList"
|
||||
value-key="key"
|
||||
@search="initRecycleList()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<MsIcon :type="item.icon || ''" class="mr-1" :class="[item.color]"></MsIcon>
|
||||
<span>{{ item.statusText || '' }}</span>
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
<template #updateUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="updateUserFilterVisible"
|
||||
v-model:status-filters="updateUserFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="memberOptions"
|
||||
@search="initRecycleList()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
<template #createUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="createUserFilterVisible"
|
||||
v-model:status-filters="createUserFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="memberOptions"
|
||||
@search="initRecycleList()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
<template #deleteUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="deleteUserFilterVisible"
|
||||
v-model:status-filters="deleteUserFilters"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="memberOptions"
|
||||
@search="initRecycleList()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.label }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
<!-- 评审结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_RESULT]="{ filterContent }">
|
||||
<MsIcon
|
||||
:type="statusIconMap[filterContent.value]?.icon"
|
||||
class="mr-1"
|
||||
:class="[statusIconMap[filterContent.value].color]"
|
||||
></MsIcon>
|
||||
<span>{{ statusIconMap[filterContent.value]?.statusText }} </span>
|
||||
</template>
|
||||
<template #reviewStatus="{ record }">
|
||||
<MsIcon
|
||||
|
@ -182,44 +131,6 @@
|
|||
></MsIcon>
|
||||
<span>{{ statusIconMap[record.reviewStatus]?.statusText || '' }} </span>
|
||||
</template>
|
||||
<template #reviewStatusFilter="{ columnConfig }">
|
||||
<a-trigger
|
||||
v-model:popup-visible="statusFilterVisible"
|
||||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="statusFilterVisible = true">
|
||||
<div class="font-medium">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
</div>
|
||||
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of Object.keys(statusIconMap)" :key="key" :value="key">
|
||||
<MsIcon
|
||||
:type="statusIconMap[key]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[statusIconMap[key].color]"
|
||||
></MsIcon>
|
||||
<span>{{ statusIconMap[key]?.statusText || '' }} </span>
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="filter-button">
|
||||
<a-button size="mini" class="mr-[8px]" @click="resetReviewStatusFilter">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
<a-button type="primary" size="mini" @click="handleFilterHidden(false)">
|
||||
{{ t('system.orgTemplate.confirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</template>
|
||||
<template #lastExecuteResult="{ record }">
|
||||
<MsIcon
|
||||
:type="executionResultMap[record.lastExecuteResult]?.icon || ''"
|
||||
|
@ -277,6 +188,7 @@
|
|||
*/
|
||||
import { computed, ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
|
@ -287,12 +199,11 @@
|
|||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import type { TagType, Theme } from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||
import TableFilter from './tableFilter.vue';
|
||||
import ExecuteStatusTag from './excuteStatusTag.vue';
|
||||
|
||||
import {
|
||||
batchDeleteRecycleCase,
|
||||
|
@ -315,6 +226,7 @@
|
|||
import type { CaseManagementTable, CustomAttributes } from '@/models/caseManagement/featureCase';
|
||||
import type { ModuleTreeNode, TableQueryParams } from '@/models/common';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import { executionResultMap, getCaseLevels, getTableFields, statusIconMap } from './utils';
|
||||
|
||||
|
@ -354,8 +266,15 @@
|
|||
);
|
||||
|
||||
const hasOperationPermission = computed(() => hasAnyPermission(['FUNCTIONAL_CASE:READ+DELETE']));
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
const reviewResultOptions = computed(() => {
|
||||
return Object.keys(statusIconMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: statusIconMap[key].statusText,
|
||||
};
|
||||
});
|
||||
});
|
||||
const firstStaticColumn: MsTableColumn = [
|
||||
{
|
||||
'title': 'caseManagement.featureCase.tableColumnID',
|
||||
'slotName': 'num',
|
||||
|
@ -387,19 +306,30 @@
|
|||
showDrag: false,
|
||||
columnSelectorDisabled: true,
|
||||
},
|
||||
];
|
||||
const caseLevelColumn: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnLevel',
|
||||
slotName: 'caseLevel',
|
||||
titleSlotName: 'caseLevelFilter',
|
||||
dataIndex: 'caseLevel',
|
||||
filterConfig: {
|
||||
options: [],
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL,
|
||||
},
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
},
|
||||
];
|
||||
const lastStaticColumn: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnReviewResult',
|
||||
dataIndex: 'reviewStatus',
|
||||
slotName: 'reviewStatus',
|
||||
titleSlotName: 'reviewStatusFilter',
|
||||
filterConfig: {
|
||||
options: reviewResultOptions.value,
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_RESULT,
|
||||
},
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
|
@ -442,7 +372,14 @@
|
|||
title: 'caseManagement.featureCase.tableColumnUpdateUser',
|
||||
slotName: 'updateUserName',
|
||||
dataIndex: 'updateUser',
|
||||
titleSlotName: 'updateUserFilter',
|
||||
filterConfig: {
|
||||
mode: 'remote',
|
||||
loadOptionParams: {
|
||||
projectId: appStore.currentProjectId,
|
||||
},
|
||||
remoteMethod: FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER,
|
||||
placeholderText: t('caseManagement.featureCase.PleaseSelect'),
|
||||
},
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
|
@ -465,7 +402,14 @@
|
|||
slotName: 'createUserName',
|
||||
dataIndex: 'createUser',
|
||||
showTooltip: true,
|
||||
titleSlotName: 'createUserFilter',
|
||||
filterConfig: {
|
||||
mode: 'remote',
|
||||
loadOptionParams: {
|
||||
projectId: appStore.currentProjectId,
|
||||
},
|
||||
remoteMethod: FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER,
|
||||
placeholderText: t('caseManagement.featureCase.PleaseSelect'),
|
||||
},
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
|
@ -485,7 +429,14 @@
|
|||
{
|
||||
title: 'caseManagement.featureCase.tableColumnDeleteUser',
|
||||
dataIndex: 'deleteUserName',
|
||||
titleSlotName: 'deleteUserFilter',
|
||||
filterConfig: {
|
||||
mode: 'remote',
|
||||
loadOptionParams: {
|
||||
projectId: appStore.currentProjectId,
|
||||
},
|
||||
remoteMethod: FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER,
|
||||
placeholderText: t('caseManagement.featureCase.PleaseSelect'),
|
||||
},
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
|
@ -503,6 +454,8 @@
|
|||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
];
|
||||
const operationColumn: MsTableColumn = [
|
||||
{
|
||||
title: hasOperationPermission.value ? 'caseManagement.featureCase.tableColumnActions' : '',
|
||||
slotName: 'operation',
|
||||
|
@ -643,29 +596,7 @@
|
|||
currentSelectCount: 0,
|
||||
});
|
||||
|
||||
// 用例等级表头检索
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const caseLevelFields = ref<Record<string, any>>({});
|
||||
const caseFilterVisible = ref(false);
|
||||
const caseLevelList = computed(() => {
|
||||
return caseLevelFields.value?.options || [];
|
||||
});
|
||||
const caseFilters = ref<string[]>([]);
|
||||
const executeResultFilters = ref<string[]>([]);
|
||||
const updateUserFilters = ref<string[]>([]);
|
||||
const createUserFilters = ref<string[]>([]);
|
||||
const deleteUserFilters = ref<string[]>([]);
|
||||
|
||||
function getExecuteResultList() {
|
||||
const list: any = [];
|
||||
Object.keys(executionResultMap).forEach((key) => {
|
||||
list.push({
|
||||
...executionResultMap[key],
|
||||
});
|
||||
});
|
||||
return list;
|
||||
}
|
||||
const executeResultFilterList = ref(getExecuteResultList());
|
||||
|
||||
// 获取批量操作参数
|
||||
function getBatchParams(): TableQueryParams {
|
||||
|
@ -678,12 +609,7 @@
|
|||
condition: {
|
||||
keyword: keyword.value,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
deleteUserName: deleteUserFilters.value,
|
||||
...propsRes.value.filter,
|
||||
},
|
||||
combine: batchParams.value.condition,
|
||||
},
|
||||
|
@ -703,14 +629,6 @@
|
|||
keyword: keyword.value,
|
||||
moduleIds,
|
||||
projectId: currentProjectId.value,
|
||||
filter: {
|
||||
reviewStatus: statusFilters.value,
|
||||
caseLevel: caseFilters.value,
|
||||
lastExecuteResult: executeResultFilters.value,
|
||||
updateUserName: updateUserFilters.value,
|
||||
createUserName: createUserFilters.value,
|
||||
deleteUserName: deleteUserFilters.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -739,12 +657,6 @@
|
|||
setLoadListParams(await initTableParams());
|
||||
}
|
||||
|
||||
// 执行结果表头检索
|
||||
const executeResultFilterVisible = ref(false);
|
||||
const updateUserFilterVisible = ref(false);
|
||||
const createUserFilterVisible = ref(false);
|
||||
const deleteUserFilterVisible = ref(false);
|
||||
|
||||
// 初始化回收站列表
|
||||
async function initRecycleList() {
|
||||
await getLoadListParams();
|
||||
|
@ -901,10 +813,15 @@
|
|||
};
|
||||
});
|
||||
caseLevelFields.value = result.customFields.find((item: any) => item.internal && item.fieldName === '用例等级');
|
||||
if (caseLevelColumn[0].filterConfig?.options) {
|
||||
caseLevelColumn[0].filterConfig.options = cloneDeep(unref(caseLevelFields.value?.options)) || [];
|
||||
}
|
||||
fullColumns = [
|
||||
...columns.slice(0, columns.length - 1),
|
||||
...firstStaticColumn,
|
||||
...caseLevelColumn,
|
||||
...lastStaticColumn,
|
||||
...customFieldsColumns,
|
||||
...columns.slice(columns.length - 1, columns.length),
|
||||
...operationColumn,
|
||||
];
|
||||
await tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_RECYCLE_TABLE, fullColumns, 'drawer');
|
||||
}
|
||||
|
@ -1011,50 +928,13 @@
|
|||
condition: {
|
||||
keyword: keyword.value,
|
||||
searchMode: accordBelow,
|
||||
filter: propsRes.value.filter,
|
||||
filter: { ...propsRes.value.filter },
|
||||
combine,
|
||||
},
|
||||
};
|
||||
initRecycleList();
|
||||
};
|
||||
|
||||
// 如果是用例状态
|
||||
function getCaseState(caseState: string | undefined): { type: TagType; theme: Theme } {
|
||||
switch (caseState) {
|
||||
case '已完成':
|
||||
return {
|
||||
type: 'success',
|
||||
theme: 'default',
|
||||
};
|
||||
case '进行中':
|
||||
return {
|
||||
type: 'link',
|
||||
theme: 'default',
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
type: 'default',
|
||||
theme: 'default',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
|
||||
function handleFilterHidden(val: boolean) {
|
||||
if (!val) {
|
||||
initRecycleList();
|
||||
statusFilterVisible.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetReviewStatusFilter() {
|
||||
statusFilters.value = [];
|
||||
statusFilterVisible.value = false;
|
||||
initRecycleList();
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getRecycleModules();
|
||||
await initFilter();
|
||||
|
|
|
@ -56,9 +56,9 @@
|
|||
></a-input-search>
|
||||
</div>
|
||||
</div>
|
||||
<ms-base-table v-if="showType === 'link'" ref="tableRef" v-bind="linkPropsRes" v-on="linkTableEvent">
|
||||
<ms-base-table v-if="showType === 'link'" ref="bugTableRef" v-bind="linkPropsRes" v-on="linkTableEvent">
|
||||
<template #name="{ record }">
|
||||
<span class="one-line-text max-w-[300px]"> {{ record.name }}</span>
|
||||
<span class="one-line-text max-w-[150px]"> {{ characterLimit(record.name) }}</span>
|
||||
<a-popover title="" position="right" style="width: 480px">
|
||||
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
<template #content>
|
||||
|
@ -66,36 +66,6 @@
|
|||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template #handleUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="handleUserFilterVisible"
|
||||
v-model:status-filters="handleUserFilterValue"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="handleUserFilterOptions"
|
||||
value-key="value"
|
||||
@search="searchData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.text }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
|
||||
<template #statusFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="statusFilterVisible"
|
||||
v-model:status-filters="statusFilterValue"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="statusFilterOptions"
|
||||
value-key="value"
|
||||
@search="searchData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.text }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
|
||||
<template #severityFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="severityFilterVisible"
|
||||
|
@ -110,6 +80,14 @@
|
|||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
<template #statusName="{ record }">
|
||||
<div class="one-line-text">{{ record.statusName }}</div>
|
||||
</template>
|
||||
<template #handleUserName="{ record }">
|
||||
<a-tooltip :content="record.handleUserName">
|
||||
<div class="one-line-text max-w-[200px]">{{ characterLimit(record.handleUserName) }}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template #operation="{ record }">
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" @click="cancelLink(record.id)">{{
|
||||
|
@ -137,9 +115,9 @@
|
|||
</div>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<ms-base-table v-else v-bind="testPlanPropsRes" v-on="testPlanTableEvent">
|
||||
<ms-base-table v-else v-bind="testPlanPropsRes" ref="planTableRef" v-on="testPlanTableEvent">
|
||||
<template #name="{ record }">
|
||||
<span class="one-line-text max-w-[300px]"> {{ record.name }}</span>
|
||||
<div class="one-line-text max-w-[300px]"> {{ record.name }}</div>
|
||||
<a-popover title="" position="right">
|
||||
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
<template #content>
|
||||
|
@ -149,37 +127,14 @@
|
|||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template #handleUserName="{ record }">
|
||||
<a-tooltip :content="record.handleUserName">
|
||||
<div class="one-line-text max-w-[200px]">{{ characterLimit(record.handleUserName) }}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #testPlanName="{ record }">
|
||||
<a-button type="text" class="px-0" @click="goToPlan(record)">{{ record.testPlanName }}</a-button>
|
||||
</template>
|
||||
<template #handleUserFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="handleUserFilterVisible"
|
||||
v-model:status-filters="handleUserFilterValue"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="handleUserFilterOptions"
|
||||
value-key="value"
|
||||
@search="searchData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.text }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
<template #statusFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
v-model:visible="statusFilterVisible"
|
||||
v-model:status-filters="statusFilterValue"
|
||||
:title="(columnConfig.title as string)"
|
||||
:list="statusFilterOptions"
|
||||
value-key="value"
|
||||
@search="searchData()"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ item.text }}
|
||||
</template>
|
||||
</TableFilter>
|
||||
</template>
|
||||
|
||||
<template #severityFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
|
@ -239,11 +194,13 @@
|
|||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
import { characterLimit } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { BugListItem, BugOptionItem } from '@/models/bug-management';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
|
||||
|
@ -257,10 +214,8 @@
|
|||
const showType = ref('link');
|
||||
|
||||
const keyword = ref<string>('');
|
||||
const handleUserFilterVisible = ref(false);
|
||||
const handleUserFilterValue = ref<string[]>([]);
|
||||
const handleUserFilterOptions = ref<BugOptionItem[]>([]);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilterValue = ref<string[]>([]);
|
||||
const statusFilterOptions = ref<BugOptionItem[]>([]);
|
||||
const severityFilterOptions = ref<BugOptionItem[]>([]);
|
||||
|
@ -275,10 +230,11 @@
|
|||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'num',
|
||||
width: 200,
|
||||
width: 100,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
showDrag: false,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectName',
|
||||
|
@ -286,29 +242,33 @@
|
|||
dataIndex: 'name',
|
||||
showInTable: true,
|
||||
showTooltip: false,
|
||||
width: 300,
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectState',
|
||||
slotName: 'statusName',
|
||||
dataIndex: 'statusName',
|
||||
titleSlotName: 'statusFilter',
|
||||
dataIndex: 'status',
|
||||
filterConfig: {
|
||||
options: [],
|
||||
labelKey: 'text',
|
||||
},
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.updateUser',
|
||||
slotName: 'handleUserName',
|
||||
dataIndex: 'handleUserName',
|
||||
titleSlotName: 'handleUserFilter',
|
||||
dataIndex: 'handleUser',
|
||||
filterConfig: {
|
||||
options: [],
|
||||
labelKey: 'text',
|
||||
},
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
|
@ -317,7 +277,7 @@
|
|||
dataIndex: 'source',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
|
@ -326,7 +286,7 @@
|
|||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
width: 100,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
|
@ -386,8 +346,11 @@
|
|||
{
|
||||
title: 'caseManagement.featureCase.updateUser',
|
||||
slotName: 'handleUserName',
|
||||
dataIndex: 'handleUserName',
|
||||
titleSlotName: 'handleUserFilter',
|
||||
dataIndex: 'handleUser',
|
||||
filterConfig: {
|
||||
options: [],
|
||||
labelKey: 'text',
|
||||
},
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
|
@ -402,7 +365,6 @@
|
|||
setLoadListParams: setTestPlanListParams,
|
||||
} = useTable(getLinkedCaseBugList, {
|
||||
columns: testPlanColumns,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 354,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
@ -412,13 +374,13 @@
|
|||
status: statusFilterValue.value,
|
||||
handleUser: handleUserFilterValue.value,
|
||||
};
|
||||
// TODO 不知道干啥的 要和后台同学确认一下
|
||||
filterParams[severityColumnId.value] = severityFilterValue.value;
|
||||
return {
|
||||
keyword: keyword.value,
|
||||
caseId: showType.value === 'link' ? props.caseId : null,
|
||||
testPlanCaseId: showType.value === 'link' ? null : props.caseId,
|
||||
projectId: appStore.currentProjectId,
|
||||
filter: { ...filterParams },
|
||||
condition: {
|
||||
keyword: keyword.value,
|
||||
filter: showType.value === 'link' ? linkPropsRes : 'testPlanPropsRes',
|
||||
|
@ -436,11 +398,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
const bugTableRef = ref();
|
||||
const planTableRef = ref();
|
||||
|
||||
function makeColumns(columnData: MsTableColumn) {
|
||||
const optionsMap: Record<string, any> = {
|
||||
status: statusFilterOptions.value,
|
||||
handleUser: handleUserFilterOptions.value,
|
||||
};
|
||||
return columnData.map((e) => {
|
||||
if (Object.prototype.hasOwnProperty.call(optionsMap, e.dataIndex as string)) {
|
||||
return {
|
||||
...e,
|
||||
filterConfig: {
|
||||
...e.filterConfig,
|
||||
options: optionsMap[e.dataIndex as string],
|
||||
},
|
||||
};
|
||||
}
|
||||
return { ...e };
|
||||
});
|
||||
}
|
||||
async function initFilterOptions() {
|
||||
if (hasAnyPermission(['PROJECT_BUG:READ'])) {
|
||||
const res = await getCustomOptionHeader(appStore.currentProjectId);
|
||||
handleUserFilterOptions.value = res.handleUserOption;
|
||||
statusFilterOptions.value = res.statusOption;
|
||||
|
||||
if (showType.value === 'link') {
|
||||
const columnList = makeColumns(columns);
|
||||
bugTableRef.value.initColumn(columnList);
|
||||
} else {
|
||||
const planColumnList = makeColumns(testPlanColumns);
|
||||
planTableRef.value.initColumn(planColumnList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -522,19 +513,12 @@
|
|||
() => showType.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
initFilterOptions();
|
||||
getFetch();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// watch(
|
||||
// () => activeTab.value,
|
||||
// (val) => {
|
||||
// if (val === 'bug') {
|
||||
// getFetch();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
const total = ref<number>(0);
|
||||
async function initBugList() {
|
||||
if (!hasAnyPermission(['PROJECT_BUG:READ'])) {
|
||||
|
@ -584,9 +568,12 @@
|
|||
|
||||
onMounted(() => {
|
||||
getFetch();
|
||||
initFilterOptions();
|
||||
initBugList();
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
initFilterOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
<template #reviewStatus="{ record }">
|
||||
<MsStatusTag :status="record.reviewStatus || 'PREPARED'" />
|
||||
</template>
|
||||
<!-- TODO 后台需要加 -->
|
||||
<!-- <template #[FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_STATUS]="{ filterContent }">
|
||||
<a-tag
|
||||
:color="reviewStatusMap[filterContent.value as ReviewStatus].color"
|
||||
:class="[reviewStatusMap[filterContent.value as ReviewStatus].class, 'px-[4px]']"
|
||||
size="small"
|
||||
>
|
||||
{{ t(reviewStatusMap[filterContent.value as ReviewStatus].label) }}
|
||||
</a-tag>
|
||||
</template> -->
|
||||
<template #status="{ record }">
|
||||
<MsIcon
|
||||
:type="statusIconMap[record.status]?.icon || ''"
|
||||
|
@ -42,19 +52,21 @@
|
|||
import MsStatusTag from '@/components/business/ms-status-tag/index.vue';
|
||||
|
||||
import { getDetailCaseReviewPage } from '@/api/modules/case-management/featureCase';
|
||||
import { reviewStatusMap } from '@/config/caseManagement';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
|
||||
import { ReviewCaseItem } from '@/models/caseManagement/caseReview';
|
||||
import { ReviewCaseItem, ReviewStatus } from '@/models/caseManagement/caseReview';
|
||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import { statusIconMap } from '../utils';
|
||||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
// const activeTab = computed(() => featureCaseStore.activeTab);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -62,6 +74,15 @@
|
|||
}>();
|
||||
|
||||
const keyword = ref<string>('');
|
||||
// TODO 后台需要加
|
||||
const reviewStatusOptions = computed(() => {
|
||||
return Object.keys(reviewStatusMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: reviewStatusMap[key as ReviewStatus].label,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
|
@ -84,8 +105,13 @@
|
|||
},
|
||||
{
|
||||
title: 'caseManagement.caseReview.status',
|
||||
dataIndex: 'reviewStatus',
|
||||
dataIndex: 'status',
|
||||
slotName: 'reviewStatus',
|
||||
// TODO 后台需要加
|
||||
// filterConfig: {
|
||||
// options: reviewStatusOptions.value,
|
||||
// filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_STATUS,
|
||||
// },
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -19,89 +19,14 @@
|
|||
<template #planStatus="{ record }">
|
||||
<MsStatusTag :status="record.planStatus" />
|
||||
</template>
|
||||
<template #statusFilter="{ columnConfig }">
|
||||
<a-trigger
|
||||
v-model:popup-visible="statusFilterVisible"
|
||||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px]"
|
||||
@click="statusFilterVisible = true"
|
||||
>
|
||||
{{ t(columnConfig.title as string) }}
|
||||
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="flex items-center justify-center px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of Object.keys(planStatusMap)" :key="key" :value="key">
|
||||
<a-tag
|
||||
:color="planStatusMap[key as planStatusType].color"
|
||||
:class="[planStatusMap[key as planStatusType].class, 'px-[4px]']"
|
||||
size="small"
|
||||
>
|
||||
{{ t(planStatusMap[key as planStatusType].label) }}
|
||||
</a-tag>
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="filter-button">
|
||||
<a-button size="mini" class="mr-[8px]" @click="resetStatusFilter">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
<a-button type="primary" size="mini" @click="handleFilterHidden(false)">
|
||||
{{ t('system.orgTemplate.confirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</template>
|
||||
<template #lastExecResult="{ record }">
|
||||
<execute-result :execute-result="record.lastExecResult" />
|
||||
</template>
|
||||
<template #lastExecResultFilter="{ columnConfig }">
|
||||
<a-trigger
|
||||
v-model:popup-visible="lastExecResultVisible"
|
||||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px]"
|
||||
@click="lastExecResultVisible = true"
|
||||
>
|
||||
{{ t(columnConfig.title as string) }}
|
||||
<icon-down :class="lastExecResultVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="flex items-center justify-center px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="lastExecResultFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of Object.keys(executionResultMap)" :key="key" :value="key">
|
||||
<MsIcon
|
||||
:type="executionResultMap[key]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[executionResultMap[key].color]"
|
||||
></MsIcon>
|
||||
<span>{{ executionResultMap[key]?.statusText || '' }} </span>
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="filter-button">
|
||||
<a-button size="mini" class="mr-[8px]" @click="resetLastExecuteResultFilter">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
<a-button type="primary" size="mini" @click="handleFilterHidden(false)">
|
||||
{{ t('system.orgTemplate.confirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
|
||||
<execute-result :execute-result="filterContent.value" />
|
||||
</template>
|
||||
</a-trigger>
|
||||
<template #[FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER]="{ filterContent }">
|
||||
<MsStatusTag :status="filterContent.value" />
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</div>
|
||||
|
@ -126,6 +51,7 @@
|
|||
import { AssociateFunctionalCaseItem, planStatusType } from '@/models/testPlan/testPlan';
|
||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||
import { planStatusMap } from '@/views/test-plan/testPlan/config';
|
||||
|
@ -139,6 +65,23 @@
|
|||
caseId: string; // 用例id
|
||||
}>();
|
||||
|
||||
const executeResultOptions = computed(() => {
|
||||
return Object.keys(executionResultMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: executionResultMap[key].statusText,
|
||||
};
|
||||
});
|
||||
});
|
||||
const planStatusOptions = computed(() => {
|
||||
return Object.keys(planStatusMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: planStatusMap[key as planStatusType].label,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'ID',
|
||||
|
@ -163,14 +106,20 @@
|
|||
title: 'caseManagement.featureCase.planStatus',
|
||||
slotName: 'planStatus',
|
||||
dataIndex: 'planStatus',
|
||||
titleSlotName: 'statusFilter',
|
||||
filterConfig: {
|
||||
options: planStatusOptions.value,
|
||||
filterSlotName: FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER,
|
||||
},
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnExecutionResult',
|
||||
slotName: 'lastExecResult',
|
||||
dataIndex: 'lastExecResult',
|
||||
titleSlotName: 'lastExecResultFilter',
|
||||
filterConfig: {
|
||||
options: executeResultOptions.value,
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT,
|
||||
},
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
|
@ -187,12 +136,6 @@
|
|||
},
|
||||
];
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref<string[]>([]);
|
||||
|
||||
const lastExecResultVisible = ref(false);
|
||||
const lastExecResultFilters = ref<string[]>([]);
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
|
||||
getLinkedCaseTestPlanList,
|
||||
{
|
||||
|
@ -214,10 +157,6 @@
|
|||
setLoadListParams({
|
||||
keyword: keyword.value,
|
||||
caseId: props.caseId,
|
||||
filter: {
|
||||
planStatus: statusFilters.value,
|
||||
lastExecResult: lastExecResultFilters.value,
|
||||
},
|
||||
});
|
||||
await loadList();
|
||||
}
|
||||
|
@ -226,26 +165,6 @@
|
|||
initData();
|
||||
}, 100);
|
||||
|
||||
function handleFilterHidden(val: boolean) {
|
||||
if (!val) {
|
||||
statusFilterVisible.value = false;
|
||||
lastExecResultVisible.value = false;
|
||||
searchList();
|
||||
}
|
||||
}
|
||||
|
||||
function resetStatusFilter() {
|
||||
statusFilterVisible.value = false;
|
||||
statusFilters.value = [];
|
||||
searchList();
|
||||
}
|
||||
|
||||
function resetLastExecuteResultFilter() {
|
||||
lastExecResultVisible.value = false;
|
||||
lastExecResultFilters.value = [];
|
||||
searchList();
|
||||
}
|
||||
|
||||
// 去测试计划页面
|
||||
function goToPlan(record: AssociateFunctionalCaseItem) {
|
||||
router.push({
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
<template>
|
||||
<!-- TODO 目前有一部分表格筛选在用 后边这个也要替换 暂时先修改了icon统一 -->
|
||||
<a-trigger v-model:popup-visible="innerVisible" trigger="click" @popup-visible-change="handleFilterHidden">
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px]"
|
||||
:class="`${
|
||||
innerVisible ? '!bg-[rgb(var(--primary-1))]' : ''
|
||||
} arco-btn-text--secondary no-hover p-[8px_4px] text-[14px]
|
||||
`"
|
||||
size="mini"
|
||||
@click.stop="innerVisible = true"
|
||||
>
|
||||
<div class="font-medium">
|
||||
<div :class="`${innerVisible ? 'filter-title' : ''} flex items-center pr-[2px] font-medium`">
|
||||
{{ t(props.title) }}
|
||||
</div>
|
||||
<icon-down :class="innerVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
<svg-icon
|
||||
width="16px"
|
||||
height="16px"
|
||||
:name="innerVisible ? 'filter-icon-color' : 'filter-icon'"
|
||||
class="text-[12px] font-medium"
|
||||
/>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
|
@ -116,4 +125,13 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.filter-title {
|
||||
border-radius: 4px;
|
||||
color: rgb(var(--primary-5));
|
||||
background: rgb(var(--primary-1)) content-box;
|
||||
.filter-icon {
|
||||
color: rgb(var(--primary-5)) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -32,49 +32,14 @@
|
|||
@batch-action="handleTableBatch"
|
||||
@module-change="searchReview"
|
||||
>
|
||||
<template #statusFilter="{ columnConfig }">
|
||||
<a-trigger
|
||||
v-model:popup-visible="statusFilterVisible"
|
||||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px]"
|
||||
size="mini"
|
||||
@click="statusFilterVisible = true"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
</div>
|
||||
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of Object.keys(reviewStatusMap)" :key="key" :value="key">
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_STATUS]="{ filterContent }">
|
||||
<a-tag
|
||||
:color="reviewStatusMap[key as ReviewStatus].color"
|
||||
:class="[reviewStatusMap[key as ReviewStatus].class, 'px-[4px]']"
|
||||
:color="reviewStatusMap[filterContent.value as ReviewStatus].color"
|
||||
:class="[reviewStatusMap[filterContent.value as ReviewStatus].class, 'px-[4px]']"
|
||||
size="small"
|
||||
>
|
||||
{{ t(reviewStatusMap[key as ReviewStatus].label) }}
|
||||
{{ t(reviewStatusMap[filterContent.value as ReviewStatus].label) }}
|
||||
</a-tag>
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div class="filter-button">
|
||||
<a-button size="mini" class="mr-[8px]" @click="resetStatusFilter">
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
<a-button type="primary" size="mini" @click="handleFilterHidden(false)">
|
||||
{{ t('system.orgTemplate.confirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</template>
|
||||
<template #reviewersFilter="{ columnConfig }">
|
||||
<TableFilter
|
||||
|
@ -245,6 +210,7 @@
|
|||
import { ModuleTreeNode } from '@/models/common';
|
||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
activeFolder: string;
|
||||
|
@ -280,6 +246,15 @@
|
|||
|
||||
const innerShowType = useVModel(props, 'showType', emit);
|
||||
|
||||
const reviewStatusOptions = computed(() => {
|
||||
return Object.keys(reviewStatusMap).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: reviewStatusMap[key as ReviewStatus].label,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeMount(async () => {
|
||||
try {
|
||||
const [userRes, memberRes] = await Promise.all([
|
||||
|
@ -447,7 +422,10 @@
|
|||
title: 'caseManagement.caseReview.status',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
titleSlotName: 'statusFilter',
|
||||
filterConfig: {
|
||||
options: reviewStatusOptions.value,
|
||||
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_STATUS,
|
||||
},
|
||||
showDrag: true,
|
||||
width: 150,
|
||||
},
|
||||
|
@ -469,7 +447,14 @@
|
|||
title: 'caseManagement.caseReview.reviewer',
|
||||
slotName: 'reviewers',
|
||||
dataIndex: 'reviewers',
|
||||
titleSlotName: 'reviewersFilter',
|
||||
filterConfig: {
|
||||
mode: 'remote',
|
||||
loadOptionParams: {
|
||||
projectId: appStore.currentProjectId,
|
||||
},
|
||||
remoteMethod: FilterRemoteMethodsEnum.PROJECT_PERMISSION_MEMBER,
|
||||
placeholderText: t('caseManagement.caseReview.reviewerPlaceholder'),
|
||||
},
|
||||
showDrag: true,
|
||||
width: 150,
|
||||
},
|
||||
|
@ -573,7 +558,6 @@
|
|||
moduleIds,
|
||||
createByMe: innerShowType.value === 'createByMe' ? userStore.id : undefined,
|
||||
reviewByMe: innerShowType.value === 'reviewByMe' ? userStore.id : undefined,
|
||||
filter: { status: statusFilters.value, reviewers: reviewersFilters.value },
|
||||
combine: filter
|
||||
? {
|
||||
...filter.combine,
|
||||
|
@ -712,7 +696,7 @@
|
|||
currentSelectCount: batchParams.value?.currentSelectCount || 0,
|
||||
condition: {
|
||||
keyword: keyword.value,
|
||||
filter: { status: statusFilters.value, reviewers: reviewersFilters.value },
|
||||
filter: propsRes.value.filter,
|
||||
combine: batchParams.value.condition,
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue