feat(缺陷管理): 替换缺陷管理&回收站表头过滤筛选

This commit is contained in:
xinxin.wu 2024-05-15 12:33:48 +08:00 committed by 刘瑞斌
parent b9d37f77e1
commit d02ac3c8cc
10 changed files with 115 additions and 294 deletions

View File

@ -883,6 +883,7 @@
.arco-table-cell-with-filter {
float: left;
.arco-table-th-title {
border-radius: 2px;
background: rgb(var(--primary-1)) content-box;
.filter-icon {
color: rgb(var(--primary-5)) !important;

View File

@ -161,8 +161,8 @@ export default function useTableProps<T>(
};
// 设置请求参数,如果出了分页参数还有搜索参数,在模板页面调用此方法,可以加入参数
const loadListParams = ref<object>({});
const setLoadListParams = (params?: object) => {
const loadListParams = ref<TableQueryParams>({});
const setLoadListParams = (params?: TableQueryParams) => {
loadListParams.value = params || {};
};
// 设置keyword
@ -201,11 +201,15 @@ export default function useTableProps<T>(
current,
pageSize: currentPageSize,
sort: sortItem.value,
filter: filterItem.value,
keyword: keyword.value,
combine: advanceFilter.combine,
searchMode: advanceFilter.accordBelow,
...loadListParams.value,
filter: {
...filterItem.value,
...loadListParams.value.filter,
},
};
const data = await loadListFunc(tableQueryParams.value);
const tmpArr = data.list || data.data.list;

View File

@ -877,7 +877,7 @@ export function customFieldDataToTableData(customFieldData: Record<string, any>[
// 后端返回来的数据这个字段没值
field.type = customField.type;
if (selectExcludes.includes(field.type) && Array.isArray(field.options)) {
tableData[field.id] = field.options.find((option) => option.value === field.value)?.text;
tableData[`custom_single_${field.id}`] = field.options.find((option) => option.value === field.value)?.text;
} else if (field.type === 'MULTIPLE_INPUT' && field.value) {
// 处理标签形式
tableData[field.id] = JSON.parse(field.value).join('') || '-';
@ -885,7 +885,7 @@ export function customFieldDataToTableData(customFieldData: Record<string, any>[
// 多值的类型后端返回的是json字符串
try {
field.value = JSON.parse(field.value);
tableData[field.id] = field.value
tableData[`custom_multiple_${field.id}`] = field.value
.map((val: string) => field.options.find((option: { value: string }) => option.value === val)?.text)
.join(',');
} catch (e) {

View File

@ -67,80 +67,8 @@
{{ record.relationCaseCount }}
</a-button>
</template>
<template #createUserFilter="{ columnConfig }">
<TableFilter
v-model:visible="createUserFilterVisible"
v-model:status-filters="createUserFilterValue"
:title="(columnConfig.title as string)"
:list="createUserFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</template>
<template #updateUserFilter="{ columnConfig }">
<TableFilter
v-model:visible="updateUserFilterVisible"
v-model:status-filters="updateUserFilterValue"
:title="(columnConfig.title as string)"
:list="updateUserFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</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"
v-model:status-filters="severityFilterValue"
:title="(columnConfig.title as string)"
:list="severityFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
<template #statusName="{ record }">
{{ record.statusName || '-' }}
</template>
</MsBaseTable>
</div>
@ -228,6 +156,7 @@
import { useRoute } from 'vue-router';
import { useIntervalFn } from '@vueuse/core';
import { Message, TableData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import { MsAdvanceFilter, timeSelectOptions } from '@/components/pure/ms-advance-filter';
import { BackEndEnum, FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
@ -239,7 +168,6 @@
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import BugDetailDrawer from './components/bug-detail-drawer.vue';
import TableFilter from '@/views/case-management/caseManagementFeature/components/tableFilter.vue';
import {
checkBugExist,
@ -267,6 +195,8 @@
import { RouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { makeColumns } from '@/views/case-management/caseManagementFeature/components/utils';
const { t } = useI18n();
const MsExportDrawer = defineAsyncComponent(() => import('@/components/pure/ms-export-drawer/index.vue'));
const DeleteModal = defineAsyncComponent(() => import('./components/deleteModal.vue'));
@ -296,22 +226,7 @@
const isXpack = computed(() => licenseStore.hasLicense());
const { openDeleteModal } = useModal();
const route = useRoute();
const createUserFilterOptions = ref<BugOptionItem[]>([]);
const createUserFilterVisible = ref(false);
const createUserFilterValue = ref<string[]>([]);
const updateUserFilterOptions = ref<BugOptionItem[]>([]);
const updateUserFilterVisible = ref(false);
const updateUserFilterValue = ref<string[]>([]);
const handleUserFilterOptions = ref<BugOptionItem[]>([]);
const handleUserFilterVisible = ref(false);
const handleUserFilterValue = ref<string[]>([]);
const statusFilterOptions = ref<BugOptionItem[]>([]);
const statusFilterVisible = ref(false);
const statusFilterValue = ref<string[]>([]);
const severityFilterOptions = ref<BugOptionItem[]>([]);
const severityFilterVisible = ref(false);
const severityFilterValue = ref<string[]>([]);
const severityColumnId = ref('');
//
const isComplete = ref(false);
@ -360,8 +275,6 @@
// filters
customFields.value.forEach((item) => {
if ((item.fieldName === '严重程度' || item.fieldName === 'Bug Degree') && item.options) {
severityFilterOptions.value = [];
severityColumnId.value = `custom_single_${item.fieldId}`;
item.options.forEach((option) => {
severityFilterOptions.value.push({
value: option.value,
@ -374,7 +287,7 @@
return customFieldToColumns(res);
};
const columns: MsTableColumn = [
let columns: MsTableColumn = [
{
title: 'bugManagement.ID',
dataIndex: 'num',
@ -401,11 +314,14 @@
},
{
title: 'bugManagement.status',
dataIndex: 'statusName',
dataIndex: 'status',
width: 100,
showTooltip: true,
slotName: 'status',
titleSlotName: 'statusFilter',
slotName: 'statusName',
filterConfig: {
options: [],
labelKey: 'text',
},
showDrag: true,
showInTable: true,
},
@ -414,8 +330,11 @@
dataIndex: 'handleUser',
slotName: 'handleUser',
showTooltip: true,
titleSlotName: 'handleUserFilter',
width: 125,
filterConfig: {
options: [],
labelKey: 'text',
},
showDrag: true,
showInTable: true,
},
@ -448,11 +367,14 @@
width: 125,
showTooltip: true,
showDrag: true,
titleSlotName: 'createUserFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
filterConfig: {
options: [],
labelKey: 'text',
},
showInTable: true,
},
{
@ -472,11 +394,14 @@
width: 125,
showTooltip: true,
showDrag: true,
titleSlotName: 'updateUserFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
filterConfig: {
options: [],
labelKey: 'text',
},
showInTable: true,
},
{
@ -541,17 +466,9 @@
};
function initTableParams() {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: keyword.value,
projectId: projectId.value,
filter: { ...filterParams },
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
@ -809,22 +726,15 @@
}
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
if (params.condition) {
params.condition.filter = { ...filterParams };
params.condition.filter = propsRes.value.filter;
} else {
params.condition = { filter: { ...filterParams } };
params.condition = { filter: propsRes.value.filter };
}
const condition = {
keyword: keyword.value,
searchMode: filterResult.value.accordBelow,
filter: params.condition.filter,
filter: propsRes.value.filter,
combine: filterResult.value.combine,
};
currentSelectParams.value = {
@ -852,10 +762,14 @@
async function initFilterOptions() {
const res = await getCustomOptionHeader(appStore.currentProjectId);
createUserFilterOptions.value = res.userOption;
updateUserFilterOptions.value = res.userOption;
handleUserFilterOptions.value = res.handleUserOption;
statusFilterOptions.value = res.statusOption;
const filterOptionsMaps: Record<string, any> = {
status: res.statusOption,
handleUser: res.handleUserOption,
createUser: res.userOption,
updateUser: res.userOption,
};
columns = makeColumns(filterOptionsMaps, columns);
}
function saveSort(sortObj: { [key: string]: string }) {
@ -878,8 +792,12 @@
customColumns.forEach((item) => {
if (item.title === '严重程度' || item.title === 'Bug Degree') {
item.showInTable = true;
item.titleSlotName = 'severityFilter';
item.slotName = 'severity';
item.dataIndex = `custom_single_${item.dataIndex}`;
item.filterConfig = {
options: cloneDeep(unref(severityFilterOptions.value)) || [],
labelKey: 'text',
};
} else {
item.showInTable = false;
}
@ -889,15 +807,15 @@
console.log(error);
}
}
await getColumnHeaders();
await getColumnHeaders();
await initFilterOptions();
await tableStore.initColumn(TableKeyEnum.BUG_MANAGEMENT, columns.concat(customColumns), 'drawer');
onMounted(() => {
setLoadListParams({ projectId: projectId.value });
setCurrentPlatform();
setExportOptionData();
initFilterOptions();
fetchData();
if (route.query.id) {
//

View File

@ -21,6 +21,16 @@
v-on="propsEvent"
@batch-action="handleTableBatch"
>
<template #deleteUserName="{ record }">
<a-tooltip :content="`${record.deleteUserName}`" position="tl">
<div class="one-line-text">{{ characterLimit(record.deleteUserName) }}</div>
</a-tooltip>
</template>
<template #statusName="{ record }">
<a-tooltip :content="`${record.statusName}`" position="tl">
<div class="one-line-text">{{ characterLimit(record.statusName) }}</div>
</a-tooltip>
</template>
<template #operation="{ record }">
<div class="flex flex-row flex-nowrap">
<MsButton class="!mr-0" @click="handleRecover(record)">{{ t('bugManagement.recycle.recover') }}</MsButton>
@ -34,97 +44,6 @@
<template #deleteTimeColumn="{ record }">
{{ dayjs(record.deleteTime).format('YYYY-MM-DD HH:mm:ss') || '-' }}
</template>
<template #createUserFilter="{ columnConfig }">
<TableFilter
v-model:visible="createUserFilterVisible"
v-model:status-filters="createUserFilterValue"
:title="(columnConfig.title as string)"
:list="createUserFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</template>
<template #updateUserFilter="{ columnConfig }">
<TableFilter
v-model:visible="updateUserFilterVisible"
v-model:status-filters="updateUserFilterValue"
:title="(columnConfig.title as string)"
:list="updateUserFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</template>
<template #deleteUserFilter="{ columnConfig }">
<TableFilter
v-model:visible="deleteUserFilterVisible"
v-model:status-filters="deleteUserFilterValue"
:title="(columnConfig.title as string)"
:list="deleteUserFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</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"
v-model:status-filters="severityFilterValue"
:title="(columnConfig.title as string)"
:list="severityFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</template>
<template #empty> </template>
</MsBaseTable>
</div>
@ -134,6 +53,7 @@
<script lang="ts" async setup>
import { ref } from 'vue';
import { Message, TableData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
@ -143,7 +63,6 @@
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import TableFilter from '@/views/case-management/caseManagementFeature/components/tableFilter.vue';
import {
deleteBatchByRecycle,
@ -162,6 +81,8 @@
import { BugEditCustomField, BugListItem, BugOptionItem } from '@/models/bug-management';
import { TableKeyEnum } from '@/enums/tableEnum';
import { makeColumns } from '@/views/case-management/caseManagementFeature/components/utils';
const { t } = useI18n();
const { openDeleteModal } = useModal();
const tableStore = useTableStore();
@ -191,32 +112,13 @@
},
]);
//
const createUserFilterOptions = ref<BugOptionItem[]>([]);
const createUserFilterVisible = ref(false);
const createUserFilterValue = ref<string[]>([]);
const updateUserFilterOptions = ref<BugOptionItem[]>([]);
const updateUserFilterVisible = ref(false);
const updateUserFilterValue = ref<string[]>([]);
const handleUserFilterOptions = ref<BugOptionItem[]>([]);
const handleUserFilterVisible = ref(false);
const handleUserFilterValue = ref<string[]>([]);
const deleteUserFilterOptions = ref<BugOptionItem[]>([]);
const deleteUserFilterVisible = ref(false);
const deleteUserFilterValue = ref<string[]>([]);
const statusFilterOptions = ref<BugOptionItem[]>([]);
const statusFilterVisible = ref(false);
const statusFilterValue = ref<string[]>([]);
const severityFilterOptions = ref<BugOptionItem[]>([]);
const severityFilterVisible = ref(false);
const severityFilterValue = ref<string[]>([]);
const severityColumnId = ref('');
const filterResult = ref<FilterResult>({ accordBelow: 'AND', combine: {} });
const currentSelectParams = ref<BatchActionQueryParams>({ selectAll: false, currentSelectCount: 0 });
const heightUsed = computed(() => 286 + (filterVisible.value ? 160 + (filterRowCount.value - 1) * 60 : 0));
const columns: MsTableColumn = [
let columns: MsTableColumn = [
{
title: 'bugManagement.recycle.deleteTime',
dataIndex: 'deleteTime',
@ -231,12 +133,14 @@
},
{
title: 'bugManagement.recycle.deleteMan',
dataIndex: 'deleteUserName',
dataIndex: 'deleteUser',
width: 125,
showDrag: true,
slotName: 'deleteUser',
showTooltip: true,
titleSlotName: 'deleteUserFilter',
slotName: 'deleteUserName',
filterConfig: {
options: [],
labelKey: 'text',
},
showInTable: true,
},
{
@ -268,17 +172,23 @@
dataIndex: 'handleUser',
slotName: 'handleUser',
showTooltip: true,
titleSlotName: 'handleUserFilter',
filterConfig: {
options: [],
labelKey: 'text',
},
width: 125,
showDrag: true,
showInTable: true,
},
{
title: 'bugManagement.status',
dataIndex: 'statusName',
dataIndex: 'status',
width: 100,
slotName: 'status',
titleSlotName: 'statusFilter',
slotName: 'statusName',
filterConfig: {
options: [],
labelKey: 'text',
},
showDrag: true,
showInTable: true,
},
@ -296,11 +206,14 @@
width: 125,
showTooltip: true,
showDrag: true,
titleSlotName: 'createUserFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
filterConfig: {
options: [],
labelKey: 'text',
},
showInTable: true,
},
{
@ -320,11 +233,14 @@
width: 125,
showTooltip: true,
showDrag: true,
titleSlotName: 'updateUserFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
filterConfig: {
options: [],
labelKey: 'text',
},
showInTable: true,
},
{
@ -355,8 +271,6 @@
// filters
customFields.value.forEach((item) => {
if ((item.fieldName === '严重程度' || item.fieldName === 'Bug Degree') && item.options) {
severityFilterOptions.value = [];
severityColumnId.value = `custom_single_${item.fieldId}`;
item.options.forEach((option) => {
severityFilterOptions.value.push({
value: option.value,
@ -408,18 +322,9 @@
});
function initTableParams() {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
deleteUser: deleteUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: keyword.value,
projectId: projectId.value,
filter: { ...filterParams },
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
@ -509,18 +414,10 @@
//
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
deleteUser: deleteUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
if (params.condition) {
params.condition.filter = { ...filterParams };
params.condition.filter = propsRes.value.filter;
} else {
params.condition = { filter: { ...filterParams } };
params.condition = { filter: { ...propsRes.value.filter } };
}
const condition = {
keyword: keyword.value,
@ -550,11 +447,14 @@
async function initFilterOptions() {
const res = await getCustomOptionHeader(appStore.currentProjectId);
createUserFilterOptions.value = res.userOption;
updateUserFilterOptions.value = res.userOption;
deleteUserFilterOptions.value = res.userOption;
handleUserFilterOptions.value = res.handleUserOption;
statusFilterOptions.value = res.statusOption;
const filterOptionsMaps: Record<string, any> = {
status: res.statusOption,
handleUser: res.handleUserOption,
createUser: res.userOption,
updateUser: res.userOption,
deleteUser: res.userOption,
};
columns = makeColumns(filterOptionsMaps, columns);
}
let customColumns: MsTableColumn = [];
@ -565,8 +465,12 @@
customColumns.forEach((item) => {
if (item.title === '严重程度' || item.title === 'Bug Degree') {
item.showInTable = true;
item.titleSlotName = 'severityFilter';
item.slotName = 'severity';
item.dataIndex = `custom_single_${item.dataIndex}`;
item.filterConfig = {
options: cloneDeep(unref(severityFilterOptions.value)) || [],
labelKey: 'text',
};
} else {
item.showInTable = false;
}
@ -575,13 +479,14 @@
console.log(error);
}
}
await getColumnHeaders();
await initFilterOptions();
await tableStore.initColumn(TableKeyEnum.BUG_MANAGEMENT_RECYCLE, columns.concat(customColumns), 'drawer');
onMounted(() => {
setLoadListParams({ projectId: projectId.value });
initFilterOptions();
fetchData();
});
</script>

View File

@ -4,9 +4,9 @@
<span type="text" class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">{{ record.num }}</span>
</template>
<template #name="{ record }">
<span class="one-line-text max-w-[150px]"> {{ characterLimit(record.name) }}</span>
<div class="one-line-text flex max-w-[150px] flex-nowrap items-center"> {{ characterLimit(record.name) }}</div>
<a-popover title="" position="right" style="width: 480px">
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
<div class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</div>
<template #content>
<div v-dompurify-html="record.content" class="markdown-body" style="margin-left: 48px"> </div>
</template>
@ -112,19 +112,13 @@
const severityColumnId = ref('');
const severityFilterValue = ref<string[]>([]);
function initTableParams() {
const filterParams: Record<string, any> = {
// status: statusFilterValue.value,
// handleUser: handleUserFilterValue.value,
};
// TODO
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: props.keyword,
caseId: props.caseId,
projectId: appStore.currentProjectId,
condition: {
keyword: props.keyword,
filter: linkPropsRes.value.filter,
filter: { ...linkPropsRes.value.filter, [severityColumnId.value]: severityFilterValue.value },
},
};
}

View File

@ -196,7 +196,7 @@
dataIndex: 'name',
showInTable: true,
showTooltip: false,
width: 200,
width: 250,
ellipsis: true,
showDrag: false,
},

View File

@ -20,10 +20,11 @@
<MsStatusTag :status="record.planStatus" />
</template>
<template #lastExecResult="{ record }">
<execute-result :execute-result="record.lastExecResult" />
<ExecuteResult v-if="record.lastExecResult" :execute-result="record.lastExecResult" />
<span v-else>-</span>
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<execute-result :execute-result="filterContent.value" />
<ExecuteResult :execute-result="filterContent.value" />
</template>
<template #[FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER]="{ filterContent }">
<MsStatusTag :status="filterContent.value" />

View File

@ -475,8 +475,6 @@
}
const keyword = ref<string>('');
const statusFilterVisible = ref(false);
const statusFilters = ref<string[]>([]);
const showType = ref<keyof typeof testPlanTypeEnum>(testPlanTypeEnum.TEST_PLAN);
const testPlanBatchActions = {

View File

@ -116,7 +116,7 @@
dataIndex: 'name',
showInTable: true,
showTooltip: false,
width: 200,
width: 300,
ellipsis: true,
showDrag: false,
},