feat(功能用例): 表头筛选功能&修改功能用例bug

This commit is contained in:
xinxin.wu 2024-02-22 19:24:30 +08:00 committed by Craftsman
parent c5d4efe096
commit 150a5bf1fe
13 changed files with 323 additions and 75 deletions

View File

@ -1,12 +1,14 @@
<template>
<div
class="mr-[4px] h-[8px] w-[8px] rounded-full"
:style="{
backgroundColor: caseLevel.bgColor,
border: `1px solid ${caseLevel.borderColor}`,
}"
></div>
{{ caseLevel.label }}
<div class="flex items-center">
<div
class="mr-[4px] h-[8px] w-[8px] rounded-full"
:style="{
backgroundColor: caseLevel.bgColor,
border: `1px solid ${caseLevel.borderColor}`,
}"
></div>
{{ caseLevel.label }}
</div>
</template>
<script setup lang="ts">

View File

@ -536,8 +536,8 @@
() => props.visible,
(val) => {
if (val) {
activeTab.value = 'detail';
showDrawerVisible.value = val;
activeTab.value = 'detail';
}
}
);
@ -601,11 +601,8 @@
);
onMounted(() => {
if (activeTab.value) {
settingDrawerRef.value.getTabModule();
}
settingDrawerRef.value.getTabModule();
});
provide('activeTab', activeTab.value);
</script>
<style scoped lang="less">

View File

@ -42,12 +42,67 @@
<template #name="{ record, rowIndex }">
<span type="text" class="px-0" @click="showCaseDetail(record.id, rowIndex)">{{ record.name }}</span>
</template>
<template #updateUser="{ record }">
<span type="text" class="px-0" >{{ record.updateUserName }}</span>
<template #updateUserName="{ record }">
<span type="text" class="px-0">{{ record.updateUserName || '-' }}</span>
</template>
<template #caseLevel="{ record }">
<caseLevel :case-level="getCaseLevels(record.customFields)" />
</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>
</TableFilter>
</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="initData()"
>
<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="initData()"
>
<template #item="{ item }">
{{ item.label }}
</template>
</TableFilter>
</template>
<template #createUserFilter="{ columnConfig }">
<TableFilter
v-model:visible="createUserFilterVisible"
v-model:status-filters="updateUserFilters"
:title="(columnConfig.title as string)"
:list="memberOptions"
@search="initData()"
>
<template #item="{ item }">
{{ item.label }}
</template>
</TableFilter>
</template>
<template #reviewStatus="{ record }">
<MsIcon
:type="statusIconMap[record.reviewStatus]?.icon || ''"
@ -266,6 +321,7 @@
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 TableFormChange from './tableFormChange.vue';
import {
@ -435,6 +491,7 @@
{
title: 'caseManagement.featureCase.tableColumnLevel',
slotName: 'caseLevel',
titleSlotName: 'caseLevelFilter',
showInTable: true,
width: 200,
showDrag: true,
@ -452,6 +509,7 @@
title: 'caseManagement.featureCase.tableColumnExecutionResult',
dataIndex: 'lastExecuteResult',
slotName: 'lastExecuteResult',
titleSlotName: 'executeResultFilter',
showInTable: true,
width: 200,
showDrag: true,
@ -483,8 +541,9 @@
},
{
title: 'caseManagement.featureCase.tableColumnUpdateUser',
slotName: 'updateUser',
dataIndex: 'updateUser',
slotName: 'updateUserName',
dataIndex: 'updateUserName',
titleSlotName: 'updateUserFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
@ -509,6 +568,7 @@
title: 'caseManagement.featureCase.tableColumnCreateUser',
slotName: 'createUserName',
dataIndex: 'createUserName',
titleSlotName: 'createUserFilter',
showInTable: true,
width: 200,
showDrag: true,
@ -596,10 +656,11 @@
const filterConfigList = ref<FilterFormItem[]>([]);
const searchCustomFields = ref<FilterFormItem[]>([]);
const scrollWidth = ref<number>(3400);
const memberOptions = ref<{ label: string; value: string }[]>([]);
async function initFilter() {
const result = await getCustomFieldsTable(currentProjectId.value);
let memberOptions = await getProjectMemberOptions(appStore.currentProjectId, keyword.value);
memberOptions = memberOptions.map((e) => ({ label: e.name, value: e.id }));
memberOptions.value = await getProjectMemberOptions(appStore.currentProjectId, keyword.value);
memberOptions.value = memberOptions.value.map((e: any) => ({ label: e.name, value: e.id }));
filterConfigList.value = [
{
title: 'caseManagement.featureCase.tableColumnID',
@ -635,7 +696,7 @@
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions,
options: memberOptions.value,
},
},
{
@ -649,7 +710,7 @@
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions,
options: memberOptions.value,
},
},
{
@ -777,6 +838,29 @@
moduleIds: [],
});
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
const caseLevelFields = ref<Record<string, any>>({});
//
const caseFilterVisible = ref(false);
const caseLevelList = computed(() => {
return caseLevelFields.value?.options || [];
});
const executeResultFilters = ref(Object.keys(executionResultMap));
const updateUserFilters = ref(memberOptions.value.map((item) => item.value));
function getExecuteResultList() {
const list: any = [];
Object.keys(executionResultMap).forEach((key) => {
list.push({
...executionResultMap[key],
});
});
return list;
}
const executeResultFilterList = ref(getExecuteResultList());
const caseFilters = ref<string[]>([]);
function getLoadListParams() {
if (props.activeFolder === 'all') {
searchParams.value.moduleIds = [];
@ -786,10 +870,20 @@
setLoadListParams({
...searchParams.value,
keyword: keyword.value,
filter: { reviewStatus: statusFilters.value },
filter: {
reviewStatus: statusFilters.value,
[caseLevelFields.value.fieldId]: caseFilters.value,
lastExecuteResult: executeResultFilters.value,
updateUserName: updateUserFilters.value,
},
});
}
//
const executeResultFilterVisible = ref(false);
const updateUserFilterVisible = ref(false);
const createUserFilterVisible = ref(false);
//
async function initData() {
getLoadListParams();
@ -1084,6 +1178,8 @@
};
});
caseLevelFields.value = result.customFields.find((item: any) => item.internal);
caseFilters.value = caseLevelFields.value.options.map((item: any) => item.value);
fullColumns = [
...columns.slice(0, columns.length - 1),
...customFieldsColumns,
@ -1316,11 +1412,11 @@
}
}
onMounted(() => {
onMounted(async () => {
if (route.query.id) {
showCaseDetail(route.query.id as string, 0);
}
getDefaultFields();
await getDefaultFields();
initFilter();
initData();
});

View File

@ -90,6 +90,61 @@
<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>
</TableFilter>
</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="updateUserFilters"
:title="(columnConfig.title as string)"
:list="memberOptions"
@search="initRecycleList()"
>
<template #item="{ item }">
{{ item.label }}
</template>
</TableFilter>
</template>
<template #reviewStatus="{ record }">
<MsIcon
:type="statusIconMap[record.reviewStatus]?.icon || ''"
@ -141,6 +196,9 @@
<span class="one-line-text inline-block">{{ getModules(record.moduleId) }}</span>
</a-tooltip>
</template>
<template #updateUserName="{ record }">
<span type="text" class="px-0">{{ record.updateUserName || '-' }}</span>
</template>
<!-- 回收站自定义字段 -->
<template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
<a-tooltip
@ -190,6 +248,7 @@
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 {
batchDeleteRecycleCase,
@ -286,6 +345,7 @@
{
title: 'caseManagement.featureCase.tableColumnLevel',
slotName: 'caseLevel',
titleSlotName: 'caseLevelFilter',
showInTable: true,
width: 200,
showDrag: true,
@ -303,6 +363,7 @@
title: 'caseManagement.featureCase.tableColumnExecutionResult',
dataIndex: 'lastExecuteResult',
slotName: 'lastExecuteResult',
titleSlotName: 'executeResultFilter',
showInTable: true,
width: 200,
showDrag: true,
@ -336,6 +397,7 @@
title: 'caseManagement.featureCase.tableColumnUpdateUser',
slotName: 'updateUserName',
dataIndex: 'updateUserName',
titleSlotName: 'updateUserFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
@ -499,12 +561,35 @@
? t('caseManagement.featureCase.allCase')
: findNodeByKey<Record<string, any>>(caseTree.value, featureCaseStore.moduleId[0], 'id')?.name;
});
const memberOptions = ref<{ label: string; value: string }[]>([]);
const searchParams = ref<TableQueryParams>({
projectId: currentProjectId.value,
moduleIds: [],
});
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
const caseLevelFields = ref<Record<string, any>>({});
//
const caseFilterVisible = ref(false);
const caseLevelList = computed(() => {
return caseLevelFields.value?.options || [];
});
const caseFilters = ref<string[]>([]);
const executeResultFilters = ref(Object.keys(executionResultMap));
const updateUserFilters = ref(memberOptions.value.map((item) => item.value));
function getExecuteResultList() {
const list: any = [];
Object.keys(executionResultMap).forEach((key) => {
list.push({
...executionResultMap[key],
});
});
return list;
}
const executeResultFilterList = ref(getExecuteResultList());
// count
const emitTableParams: CaseModuleQueryParams = {
keyword: keyword.value,
@ -540,12 +625,42 @@
};
}
//
function getLoadListParams() {
if (activeFolder.value === 'all') {
searchParams.value.moduleIds = [];
} else {
searchParams.value.moduleIds = [activeFolder.value, ...offspringIds.value];
}
setLoadListParams({
...searchParams.value,
keyword: keyword.value,
filter: {
reviewStatus: statusFilters.value,
[caseLevelFields.value.fieldId]: caseFilters.value,
lastExecuteResult: executeResultFilters.value,
updateUserName: updateUserFilters.value,
},
});
}
//
const executeResultFilterVisible = ref(false);
const updateUserFilterVisible = ref(false);
const createUserFilterVisible = ref(false);
//
function initRecycleList() {
getLoadListParams();
loadList();
}
//
async function handleBatchRecover() {
try {
await restoreCaseList(getBatchParams());
Message.success(t('caseManagement.featureCase.recoveredSuccessfully'));
loadList();
initRecycleList();
resetSelector();
initRecycleModulesCount();
} catch (error) {
@ -600,26 +715,6 @@
return `/${moduleName.join('/')}`;
}
}
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
//
function getLoadListParams() {
if (activeFolder.value === 'all') {
searchParams.value.moduleIds = [];
} else {
searchParams.value.moduleIds = [activeFolder.value, ...offspringIds.value];
}
setLoadListParams({
...searchParams.value,
keyword: keyword.value,
filter: { reviewStatus: statusFilters.value },
});
}
//
function initRecycleList() {
getLoadListParams();
loadList();
}
//
async function recoverCase(id: string) {
@ -706,19 +801,20 @@
width: 300,
};
});
caseLevelFields.value = result.customFields.find((item: any) => item.internal);
caseFilters.value = caseLevelFields.value.options.map((item: any) => item.value);
fullColumns = [
...columns.slice(0, columns.length - 1),
...customFieldsColumns,
...columns.slice(columns.length - 1, columns.length),
];
tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_RECYCLE_TABLE, fullColumns, 'drawer');
await tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_RECYCLE_TABLE, fullColumns, 'drawer');
}
async function initFilter() {
const result = await getCustomFieldsTable(currentProjectId.value);
let memberOptions = await getProjectMemberOptions(appStore.currentProjectId, keyword.value);
memberOptions = memberOptions.map((e) => ({ label: e.name, value: e.id }));
memberOptions.value = await getProjectMemberOptions(appStore.currentProjectId, keyword.value);
memberOptions.value = memberOptions.value.map((e: any) => ({ label: e.name, value: e.id }));
filterConfigList.value = [
{
title: 'caseManagement.featureCase.tableColumnID',
@ -754,7 +850,7 @@
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions,
options: memberOptions.value,
},
},
{
@ -768,7 +864,7 @@
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions,
options: memberOptions.value,
},
},
{
@ -854,14 +950,13 @@
}
}
onMounted(() => {
getDefaultFields();
onMounted(async () => {
await getDefaultFields();
initFilter();
initRecycleList();
getRecycleModules();
initRecycleModulesCount();
});
tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_RECYCLE_TABLE, columns, 'drawer');
</script>
<style scoped lang="less">

View File

@ -114,10 +114,8 @@
initData();
}, 100);
onBeforeMount(() => {
if (props.caseId) {
initData();
}
onMounted(() => {
initData();
});
</script>

View File

@ -229,11 +229,9 @@
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
}
onBeforeMount(() => {
onMounted(() => {
doCheckIsTip();
if (props.caseId) {
initData();
}
initData();
});
</script>

View File

@ -151,7 +151,7 @@
}
);
onBeforeMount(() => {
onMounted(() => {
getAllCommentList();
});

View File

@ -348,7 +348,7 @@
}
}
onBeforeMount(async () => {
onMounted(async () => {
try {
const result = await getCaseRelatedInfo(currentProjectId.value);
if (result && result.platform_key) {

View File

@ -201,9 +201,7 @@
);
onMounted(() => {
if (props.caseId) {
initData();
}
initData();
});
</script>

View File

@ -28,7 +28,7 @@
:upload-image="handleUploadImage"
class="mt-2"
/>
<div v-else v-dompurify-html="detailForm?.prerequisite || '-'" class="text-[var(--color-text-3)]"></div>
<div v-else v-dompurify-html="detailForm?.prerequisite || '-'"></div>
</a-form-item>
<a-form-item
field="step"
@ -69,7 +69,6 @@
<div
v-if="detailForm.caseEditType === 'TEXT' && !isEditPreposition"
v-dompurify-html="detailForm.textDescription || '-'"
class="text-[var(--color-text-3)]"
></div>
</a-form-item>
<a-form-item
@ -83,7 +82,7 @@
v-model:filed-ids="expectedResultFileIds"
:upload-image="handleUploadImage"
/>
<div v-else v-dompurify-html="detailForm.expectedResult || '-'" class="text-[var(--color-text-3)]"></div>
<div v-else v-dompurify-html="detailForm.expectedResult || '-'"></div>
</a-form-item>
<a-form-item field="description" :label="t('caseManagement.featureCase.remark')">
<MsRichText
@ -92,7 +91,7 @@
v-model:raw="detailForm.description"
:upload-image="handleUploadImage"
/>
<div v-else v-dompurify-html="detailForm.description || '-'" class="text-[var(--color-text-3)]"></div>
<div v-else v-dompurify-html="detailForm.description || '-'"></div>
</a-form-item>
<div v-if="isEditPreposition" class="flex justify-end">
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button>
@ -144,6 +143,7 @@
ref="fileListRef"
v-model:file-list="fileList"
:show-tab="false"
mode="static"
:request-params="{
caseId: detailForm.id,
projectId: currentProjectId,

View File

@ -0,0 +1,59 @@
<template>
<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]" @click="innerVisible = true">
<div class="font-medium">
{{ t(props.title) }}
</div>
<icon-down :class="innerVisible ? '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="innerStatusFilters" direction="vertical" size="small">
<a-checkbox
v-for="(item, index) of props.list"
:key="item[props.valueKey || 'value']"
:value="item[props.valueKey || 'value']"
>
<slot name="item" :item="item" :index="index"></slot>
</a-checkbox>
</a-checkbox-group>
</div>
</div>
</template>
</a-trigger>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useVModel } from '@vueuse/core';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
title: string;
statusFilters: string[];
list: any[];
valueKey?: string;
}>();
const emit = defineEmits<{
(e: 'update:visible', visible: boolean): void;
(e: 'update:statusFilters', visible: boolean): void;
(e: 'search'): void;
}>();
const innerVisible = useVModel(props, 'visible', emit);
const innerStatusFilters = useVModel(props, 'statusFilters', emit);
function handleFilterHidden(val: boolean) {
innerVisible.value = val;
if (!val) {
emit('search');
}
}
</script>
<style scoped></style>

View File

@ -126,7 +126,7 @@ export function getTableFields(customFields: CustomAttributes[], itemDataIndex:
const multipleExcludes = ['MULTIPLE_SELECT', 'CHECKBOX', 'MULTIPLE_MEMBER'];
const selectExcludes = ['MEMBER', 'RADIO', 'SELECT'];
const currentColumnData: CustomAttributes | undefined = customFields.find(
const currentColumnData: CustomAttributes | undefined = (customFields || []).find(
(item: any) => itemDataIndex.dataIndex === item.fieldId
);

View File

@ -50,6 +50,9 @@
<template #triggerMode="{ record }">
<span>{{ t(ExecutionMethodsLabel[record.triggerMode]) }}</span>
</template>
<template #operationTime="{ record }">
<span>{{ dayjs(record.operationTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
<template #operation="{ record }">
<MsButton
v-if="['PENDING', 'RUNNING', 'RERUNNING'].includes(record.status)"
@ -59,6 +62,7 @@
>
<a-divider v-if="['PENDING', 'RUNNING', 'RERUNNING'].includes(record.status)" direction="vertical" />
<MsButton class="!mr-0" @click="execution(record)">{{ t('project.taskCenter.execution') }}</MsButton>
<a-divider direction="vertical" />
<MsButton class="!mr-0">{{ t('project.taskCenter.viewReport') }}</MsButton>
</template>
</ms-base-table>
@ -68,6 +72,7 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Message } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
@ -193,7 +198,7 @@
title: 'common.operation',
slotName: 'operation',
dataIndex: 'operation',
width: 200,
width: 220,
fixed: 'right',
},
];
@ -207,7 +212,7 @@
},
showSetting: false,
selectable: true,
heightUsed: 300,
heightUsed: 330,
showSelectAll: true,
}
);