feat(缺陷管理): 回收站接口对接

This commit is contained in:
RubyLiu 2024-02-05 15:08:20 +08:00 committed by Craftsman
parent 9a140a4df2
commit 9975bc12db
9 changed files with 226 additions and 131 deletions

View File

@ -159,3 +159,25 @@ export function getAttachmentList(bugId: string) {
export function editorUploadFile(data: { fileList: File[] }) {
return MSR.uploadFile({ url: bugURL.editorUploadFileUrl }, { fileList: data.fileList }, '', false);
}
// --------------------回收站
// 获取回收站列表
export function getRecycleList(data: TableQueryParams) {
return MSR.post<CommonList<BugListItem>>({ url: bugURL.getRecycleListUrl, data });
}
// 单个恢复
export function recoverSingleByRecycle(id: string) {
return MSR.get({ url: `${bugURL.getRecoverSingleUrl}${id}` });
}
// 批量恢复
export function recoverBatchByRecycle(data: TableQueryParams) {
return MSR.post({ url: bugURL.getBatchRecoverUrl, data });
}
// 删除
export function deleteSingleByRecycle(id: string) {
return MSR.get({ url: `${bugURL.getDeleteSingleUrl}${id}` });
}
// 批量删除
export function deleteBatchByRecycle(data: TableQueryParams) {
return MSR.post({ url: bugURL.getBatchDeleteUrl, data });
}

View File

@ -40,3 +40,14 @@ export const deleteFileOrCancelAssociationUrl = '/bug/attachment/delete';
export const getAttachmentListUrl = '/bug/attachment/list/';
// 富文本编辑器上传图片
export const editorUploadFileUrl = '/bug/attachment/upload/editor';
// 获取回收站列表
export const getRecycleListUrl = '/bug/trash/page';
// 单个恢复
export const getRecoverSingleUrl = '/bug/trash/recover/';
// 批量恢复
export const getBatchRecoverUrl = '/bug/trash/batch-recover';
// 删除
export const getDeleteSingleUrl = '/bug/trash/delete/';
// 批量删除
export const getBatchDeleteUrl = '/bug/trash/batch-delete';

View File

@ -37,6 +37,7 @@ export enum TableKeyEnum {
CASE_MANAGEMENT_DETAIL_TABLE = 'caseManagementDetailTable',
CASE_MANAGEMENT_ASSOCIATED_TABLE = 'caseManagementAssociatedFileTable',
BUG_MANAGEMENT = 'bugManagement',
BUG_MANAGEMENT_RECYCLE = 'bugManagementRecycle',
CASE_MANAGEMENT_REVIEW = 'caseManagementReview',
CASE_MANAGEMENT_REVIEW_CASE = 'caseManagementReviewCase',
CASE_MANAGEMENT_TAB_DEFECT = 'caseManagementTabDefect',

View File

@ -5,7 +5,7 @@ import { BatchApiParams } from './common';
export interface BugListItem {
id: string; // 缺陷id
num: string; // 缺陷编号
name: string; // 缺陷名称
title: string; // 缺陷名称
severity: string; // 缺陷严重程度
status: string; // 缺陷状态
handleUser: string; // 缺陷处理人

View File

@ -65,7 +65,7 @@ const BugManagement: AppRouteRecordRaw = {
name: BugManagementRouteEnum.BUG_MANAGEMENT_RECYCLE,
component: () => import('@/views/bug-management/recycle.vue'),
meta: {
locale: 'bugManagement.recycle',
locale: 'bugManagement.recycle.recycleBin',
roles: ['PROJECT_BUG:READ'],
isTopMenu: true,
},

View File

@ -1,7 +1,7 @@
import { findKey } from 'lodash-es';
import JSEncrypt from 'jsencrypt';
import { MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type';
import { BatchActionQueryParams, MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type';
import { CustomFieldItem } from '@/models/bug-management';
@ -492,7 +492,7 @@ export function formatPhoneNumber(phoneNumber = '') {
}
return phoneNumber;
}
// 表格自定义字段转column
export function customFieldToColumns(customFields: CustomFieldItem[]) {
return customFields.map((field) => {
const { fieldName, fieldKey, fieldId } = field;
@ -507,3 +507,13 @@ export function customFieldToColumns(customFields: CustomFieldItem[]) {
return column;
});
}
// 表格查询参数转请求参数
export function tableParamsToRequestParams(params: BatchActionQueryParams) {
const { selectedIds, selectAll, excludeIds, condition } = params;
return {
selectIds: selectedIds,
excludeIds,
selectAll,
condition,
};
}

View File

@ -110,10 +110,6 @@
<template #second>
<div class="rightWrapper p-[24px]">
<div class="mb-4 font-medium">{{ t('bugManagement.detail.basicInfo') }}</div>
<div class="baseItem">
<span class="label"> {{ t('bugManagement.detail.handleUser') }}</span>
<MsUserSelector v-model:model-value="detailInfo.handleUser" />
</div>
<!-- 自定义字段开始 -->
<MsFormCreate
v-if="formRules.length"
@ -127,7 +123,7 @@
<!-- 自定义字段结束 -->
<div class="baseItem">
<span class="label"> {{ t('bugManagement.detail.tag') }}</span>
<MsTagsInput v-model:modelValue="detailInfo.tag"></MsTagsInput>
<!-- <MsTagsInput></MsTagsInput> -->
</div>
<div class="baseItem">
<span class="label"> {{ t('bugManagement.detail.creator') }}</span>
@ -169,7 +165,6 @@
import { CommentInput } from '@/components/business/ms-comment';
import { CommentParams } from '@/components/business/ms-comment/types';
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
import { MsUserSelector } from '@/components/business/ms-user-selector';
import BugCaseTab from './bugCaseTab.vue';
import BugDetailTab from './bugDetailTab.vue';
import CommentTab from './commentTab.vue';
@ -220,13 +215,21 @@
const tabSetting = ref<TabItemType[]>([...tabSettingList.value]);
const activeTab = ref<string | number>('detail');
const detailInfo = ref<Record<string, any>>({});
const detailInfo = ref<Record<string, any>>({
tags: [],
id: '',
createUser: '',
createTime: '',
description: '',
followFlag: false,
templateId: '',
title: '',
});
const customFields = ref<CustomAttributes[]>([]);
function loadedBug(detail: CaseManagementTable) {
detailInfo.value = { ...detail };
customFields.value = detailInfo.value.customFields;
detailInfo.value.id = '1070838426116099';
}
const editLoading = ref<boolean>(false);
@ -363,7 +366,7 @@
try {
const params = {
// TODO
bugId: detailInfo.value.id || '1070838426116099',
bugId: detailInfo.value.id,
notifier: notifiers,
replyUser: '',
parentId: '',
@ -372,7 +375,7 @@
};
await createOrUpdateComment(params as CommentParams);
Message.success(t('common.publishSuccessfully'));
commentRef.value?.initData(detailInfo.value.id || '1070838426116099');
commentRef.value?.initData(detailInfo.value.id);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -3,7 +3,6 @@ export default {
index: '缺陷管理',
addBug: '创建缺陷',
editBug: '编辑缺陷',
recycle: '回收站',
createBug: '创建缺陷',
syncBug: '同步缺陷',
ID: 'ID',
@ -90,6 +89,19 @@ export default {
mightWantTo: '你可能还想',
caseRelated: '关联用例',
},
recycle: {
recycleBin: '回收站',
recover: '恢复',
recoverSuccess: '恢复成功',
recoverError: '恢复失败',
permanentlyDelete: '彻底删除',
permanentlyDeleteTip: '是否彻底删除 {name} 缺陷?',
deleteContent: '删除后,缺陷无法恢复,请谨慎操作!',
batchDelete: '是否彻底删除{count}条缺陷?',
searchPlaceholder: '通过 ID 或名称搜索',
deleteTime: '删除时间',
deleteMan: '删除人',
},
severityO: {
fatal: '致命',
serious: '严重',

View File

@ -1,21 +1,29 @@
<template>
<MsCard simple>
<MsAdvanceFilter :filter-config-list="filterConfigList" :row-count="filterRowCount">
<MsAdvanceFilter
:search-placeholder="t('bugManagement.recycle.searchPlaceholder')"
:filter-config-list="filterConfigList"
:row-count="filterRowCount"
@keyword-search="fetchData"
>
<template #left>
<div></div>
</template>
</MsAdvanceFilter>
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #numberOfCase="{ record }">
<span class="cursor-pointer text-[rgb(var(--primary-5))]" @click="jumpToTestPlan(record)">{{
record.memberCount
}}</span>
</template>
<MsBaseTable
class="mt-[16px]"
v-bind="propsRes"
:action-config="tableAction"
v-on="propsEvent"
@batch-action="handleTableBatch"
>
<template #operation="{ record }">
<div class="flex flex-row flex-nowrap">
<MsButton class="!mr-0" @click="handleCopy(record)">{{ t('common.copy') }}</MsButton>
<MsButton class="!mr-0" @click="handleRecover(record)">{{ t('bugManagement.recycle.recover') }}</MsButton>
<a-divider direction="vertical" />
<MsButton class="!mr-0" @click="handleEdit(record)">{{ t('common.edit') }}</MsButton>
<MsButton class="!mr-0" @click="handleDelete(record)">{{
t('bugManagement.recycle.permanentlyDelete')
}}</MsButton>
</div>
</template>
<template #empty> </template>
@ -23,29 +31,35 @@
</MsCard>
</template>
<script lang="ts" setup>
<script lang="ts" async setup>
import { Message } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem, FilterType } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCard from '@/components/pure/ms-card/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import { updateOrAddProjectUserGroup } from '@/api/modules/project-management/usergroup';
import { postProjectTableByOrg } from '@/api/modules/setting/organizationAndProject';
import {
deleteBatchByRecycle,
deleteSingleByRecycle,
getRecycleList,
recoverBatchByRecycle,
recoverSingleByRecycle,
} from '@/api/modules/bug-management';
import { useI18n } from '@/hooks/useI18n';
import router from '@/router';
import useModal from '@/hooks/useModal';
import { useAppStore, useTableStore } from '@/store';
import { characterLimit, tableParamsToRequestParams } from '@/utils';
import { BugListItem } from '@/models/bug-management';
import { OrgProjectTableItem } from '@/models/setting/system/orgAndProject';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
const { t } = useI18n();
const { openDeleteModal } = useModal();
const tableStore = useTableStore();
const appStore = useAppStore();
const projectId = computed(() => appStore.currentProjectId);
@ -65,15 +79,6 @@
mode: 'static',
},
},
{
title: 'bugManagement.severity',
dataIndex: 'severity',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
multiple: true,
},
},
{
title: 'bugManagement.createTime',
dataIndex: 'createTime',
@ -84,106 +89,96 @@
const heightUsed = computed(() => 286 + (filterVisible.value ? 160 + (filterRowCount.value - 1) * 60 : 0));
const columns: MsTableColumn = [
{
title: 'bugManagement.recycle.deleteTime',
dataIndex: 'deleteTime',
showDrag: true,
width: 180,
},
{
title: 'bugManagement.recycle.deleteMan',
dataIndex: 'deleteUserName',
showDrag: true,
},
{
title: 'bugManagement.ID',
dataIndex: 'num',
showTooltip: true,
showDrag: true,
},
{
title: 'bugManagement.bugName',
editType: ColumnEditTypeEnum.INPUT,
dataIndex: 'name',
dataIndex: 'title',
showTooltip: true,
},
{
title: 'bugManagement.severity',
slotName: 'memberCount',
showDrag: true,
dataIndex: 'severity',
},
{
title: 'bugManagement.status',
dataIndex: 'status',
showDrag: true,
},
{
title: 'bugManagement.handleMan',
dataIndex: 'handleUser',
showTooltip: true,
showDrag: true,
},
{
title: 'bugManagement.numberOfCase',
dataIndex: 'relationCaseCount',
slotName: 'numberOfCase',
showDrag: true,
},
{
title: 'bugManagement.belongPlatform',
width: 180,
showDrag: true,
dataIndex: 'platform',
},
{
title: 'bugManagement.tag',
showDrag: true,
isStringTag: true,
dataIndex: 'tag',
},
{
title: 'bugManagement.creator',
dataIndex: 'createUser',
showDrag: true,
},
{
title: 'bugManagement.updateUser',
dataIndex: 'updateUser',
showDrag: true,
showTooltip: true,
},
{
title: 'bugManagement.createTime',
dataIndex: 'createTime',
showDrag: true,
width: 180,
},
{
title: 'bugManagement.updateTime',
dataIndex: 'updateTime',
title: 'bugManagement.status',
dataIndex: 'statusName',
showDrag: true,
},
{
title: 'bugManagement.handleMan',
dataIndex: 'handleUserName',
showTooltip: true,
showDrag: true,
},
{
title: 'bugManagement.tag',
showDrag: true,
isStringTag: true,
dataIndex: 'tags',
},
{
title: 'common.operation',
slotName: 'operation',
dataIndex: 'operation',
fixed: 'right',
width: 230,
width: 150,
},
];
await tableStore.initColumn(TableKeyEnum.BUG_MANAGEMENT, columns, 'drawer');
const handleNameChange = async (record: OrgProjectTableItem) => {
try {
await updateOrAddProjectUserGroup(record);
Message.success(t('common.updateSuccess'));
return true;
} catch (error) {
return false;
}
};
await tableStore.initColumn(TableKeyEnum.BUG_MANAGEMENT_RECYCLE, columns, 'drawer');
const { propsRes, propsEvent, loadList, setKeyword, setLoadListParams, setProps } = useTable(
postProjectTableByOrg,
getRecycleList,
{
tableKey: TableKeyEnum.BUG_MANAGEMENT,
selectable: false,
noDisable: false,
showJumpMethod: true,
tableKey: TableKeyEnum.BUG_MANAGEMENT_RECYCLE,
selectable: true,
noDisable: true,
showSetting: true,
scroll: { x: '1769px' },
scroll: { x: '1900px' },
},
undefined,
(record) => handleNameChange(record)
(record) => {
record.deleteTime = dayjs(record.deleteTime).format('YYYY-MM-DD HH:mm:ss');
return record;
}
);
const tableAction = {
baseAction: [
{
eventTag: 'recover',
label: t('bugManagement.recycle.recover'),
permission: ['PROJECT_BUG:READ+UPDATE'],
},
{
eventTag: 'delete',
label: t('bugManagement.recycle.permanentlyDelete'),
permission: ['PROJECT_BUG:READ+DELETE'],
},
],
};
watchEffect(() => {
setProps({ heightUsed: heightUsed.value });
});
@ -193,39 +188,80 @@
await loadList();
};
const handleCreate = () => {
// eslint-disable-next-line no-console
console.log('create');
};
const handleSync = () => {
// eslint-disable-next-line no-console
console.log('sync');
//
const handleRecover = async (record: BugListItem) => {
try {
await recoverSingleByRecycle(record.id);
Message.success(t('bugManagement.recycle.recoverSuccess'));
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
};
const handleCopy = (record: BugListItem) => {
// eslint-disable-next-line no-console
console.log('create', record);
};
const handleEdit = (record: BugListItem) => {
// eslint-disable-next-line no-console
console.log('create', record);
//
const handleBatchRecover = async (params: BatchActionQueryParams) => {
try {
const tmpObj = { ...tableParamsToRequestParams(params), projectId: projectId.value };
await recoverBatchByRecycle(tmpObj);
Message.success(t('bugManagement.recycle.recoverSuccess'));
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
};
//
const handleDelete = (record: BugListItem) => {
// eslint-disable-next-line no-console
console.log('create', record);
};
const jumpToTestPlan = (record: BugListItem) => {
router.push({
name: 'testPlan',
query: {
bugId: record.id,
projectId: projectId.value,
openDeleteModal({
title: t('bugManagement.recycle.permanentlyDeleteTip', { name: characterLimit(record.title) }),
content: t('bugManagement.recycle.deleteContent'),
onBeforeOk: async () => {
try {
await deleteSingleByRecycle(record.id);
Message.success(t('common.deleteSuccess'));
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
});
};
//
const handleBatchDelete = (params: BatchActionQueryParams) => {
openDeleteModal({
title: t('bugManagement.recycle.batchDelete', { count: params.currentSelectCount }),
content: t('bugManagement.recycle.deleteContent'),
onBeforeOk: async () => {
try {
const tmpObj = { ...tableParamsToRequestParams(params), projectId: projectId.value };
await deleteBatchByRecycle(tmpObj);
Message.success(t('common.deleteSuccess'));
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
});
};
//
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
switch (event.eventTag) {
case 'recover':
handleBatchRecover(params);
break;
case 'delete':
handleBatchDelete(params);
break;
default:
break;
}
}
onMounted(() => {
setLoadListParams({ projectId: projectId.value });