feat(缺陷管理): 缺陷列表导出

This commit is contained in:
RubyLiu 2023-12-18 18:39:22 +08:00 committed by 刘瑞斌
parent 9c28dd2fa4
commit 13a5c7ebbc
9 changed files with 162 additions and 13 deletions

View File

@ -44,6 +44,7 @@
"@types/color": "^3.0.4",
"@vueuse/core": "^10.4.1",
"ace-builds": "^1.24.2",
"ahooks-vue": "^0.15.1",
"axios": "^0.24.0",
"dayjs": "^1.11.9",
"echarts": "^5.4.3",

View File

@ -1,7 +1,7 @@
import MSR from '@/api/http/index';
import * as bugURL from '@/api/requrls/bug-management';
import { BugListItem } from '@/models/bug-management';
import { BugExportParams, BugListItem } from '@/models/bug-management';
import { CommonList, TableQueryParams, TemplateOption } from '@/models/common';
/**
@ -44,3 +44,13 @@ export function getTemplateById(data: TableQueryParams) {
export function getExportConfig(projectId: string) {
return MSR.get({ url: `${bugURL.getExportConfigUrl}${projectId}` });
}
// 同步缺陷
export function syncBugOpenSource(params: { projectId: string }) {
return MSR.get({ url: bugURL.getSyncBugOpenSourceUrl, params });
}
// 导出缺陷
export function exportBug(data: BugExportParams) {
return MSR.post({ url: bugURL.postExportBugUrl, data });
}

View File

@ -7,3 +7,5 @@ export const postBatchDeleteBugUrl = '/bug/batch-delete';
export const getTemplateUrl = '/bug/template';
export const getTemplageOption = '/bug/template/option';
export const getExportConfigUrl = '/bug/export/columns/';
export const getSyncBugOpenSourceUrl = '/bug/sync/';
export const postExportBugUrl = '/bug/export';

View File

@ -1,4 +1,5 @@
import { PropType } from 'vue';
import { onClickOutside } from '@vueuse/core';
import { useI18n } from '@/hooks/useI18n';
@ -46,6 +47,8 @@ export default defineComponent({
const { mode, options, modelValue, defaultValue } = toRefs(props);
const oldModelValue = ref(modelValue.value);
const editStatus = ref<EditStatus>('null');
const selectRef = ref<HTMLElement | null>(null);
const chengeStatus = ref(false);
const { t } = useI18n();
@ -54,6 +57,7 @@ export default defineComponent({
if (result) {
defaultValue.value = v;
Message.success(t('common.updateSuccess'));
chengeStatus.value = true;
} else {
Message.error(t('common.updateFail'));
modelValue.value = oldModelValue.value;
@ -69,11 +73,15 @@ export default defineComponent({
const handleClick = () => {
editStatus.value = 'active';
if (mode.value === 'select') {
selectRef.value?.focus();
}
};
const handleReset = () => {
editStatus.value = 'null';
modelValue.value = oldModelValue.value;
chengeStatus.value = false;
};
const handleBlur = () => {
@ -109,12 +117,22 @@ export default defineComponent({
);
};
onClickOutside(selectRef, (event) => {
if (editStatus.value === 'active' && !chengeStatus.value) {
handleReset();
}
});
const renderChild = () => {
if (mode.value === 'input') {
return <a-input modelValue={modelValue} onKeyDown={handleKeyDown} onBlur={handleBlur} />;
}
if (mode.value === 'select') {
return <a-select modelValue={modelValue} options={options.value} onSelect={handleChange} />;
return (
<div ref={selectRef}>
<a-select modelValue={modelValue} options={options.value} onSelect={handleChange} />
</div>
);
}
if (mode.value === 'tagInput') {
return <a-input-tag modelValue={modelValue} onKeyDown={handleKeyDown} onBlur={handleBlur} />;

View File

@ -109,7 +109,7 @@
selectable: false,
noDisable: true,
showSetting: false,
heightUsed: 280,
heightUsed: 288,
});
const fetchData = async () => {

View File

@ -1,3 +1,5 @@
import { BatchApiParams } from './common';
export interface BugListItem {
id: string; // 缺陷id
num: string; // 缺陷编号
@ -14,4 +16,14 @@ export interface BugListItem {
updateTime: string; // 更新时间
deleted: boolean; // 删除标志
}
export interface BugExportColumn {
key: string; // 字段key
text?: string; // 字段名称
columnType?: string; // 字段类型
}
export interface BugExportParams extends BatchApiParams {
bugExportColumns: BugExportColumn[]; // 导出字段
}
export default {};

View File

@ -4,12 +4,20 @@
<template #left>
<div class="flex gap-[12px]">
<a-button type="primary" @click="handleCreate">{{ t('bugManagement.createBug') }} </a-button>
<a-button type="outline" @click="handleSync">{{ t('bugManagement.syncBug') }} </a-button>
<a-button :disabled="syncBugLoading" type="outline" @click="handleSync"
>{{ t('bugManagement.syncBug') }}
</a-button>
<a-button type="outline" @click="handleExport">{{ t('common.export') }} </a-button>
</div>
</template>
</MsAdvanceFilter>
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<MsBaseTable
class="mt-[16px]"
v-bind="propsRes"
:action-config="tableBatchActions"
v-on="propsEvent"
@batch-action="handleTableBatch"
>
<template #name="{ record, rowIndex }">
<a-button type="text" class="px-0" @click="handleShowDetail(record.id, rowIndex)">{{ record.name }}</a-button>
</template>
@ -77,7 +85,14 @@
<a-date-picker v-model="syncObject.time" show-time class="w-[304px]" />
</div>
</a-modal>
<MsExportDrawer v-model:visible="exportVisible" :all-data="exportOptionData" />
<MsExportDrawer v-model:visible="exportVisible" :all-data="exportOptionData" @confirm="exportConfirm">
<template #title>
<span class="text-[var(--color-text-1)]">{{ t('bugManagement.exportBug') }}</span>
<span v-if="currentSelectParams.currentSelectCount" class="text-[var(--color-text-4)]"
>({{ t('bugManagement.exportBugCount', { count: currentSelectParams.currentSelectCount }) }})</span
>
</template>
</MsExportDrawer>
<BugDetailDrawer
v-model:visible="detailVisible"
:detail-id="activeDetailId"
@ -103,9 +118,9 @@
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCard from '@/components/pure/ms-card/index.vue';
import MsExportDrawer from '@/components/pure/ms-export-drawer/index.vue';
import { MsExportDrawerMap } from '@/components/pure/ms-export-drawer/types';
import { MsExportDrawerMap, MsExportDrawerOption } from '@/components/pure/ms-export-drawer/types';
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 MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
@ -113,15 +128,24 @@
import BugDetailDrawer from './components/bug-detail-drawer.vue';
import DeleteModal from './components/deleteModal.vue';
import { deleteSingleBug, getBugList, getExportConfig } from '@/api/modules/bug-management';
import {
deleteSingleBug,
exportBug,
getBugList,
getExportConfig,
syncBugOpenSource,
} from '@/api/modules/bug-management';
import { updateOrAddProjectUserGroup } from '@/api/modules/project-management/usergroup';
import { useI18n } from '@/hooks/useI18n';
import router from '@/router';
import { useAppStore, useTableStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import { BugListItem } from '@/models/bug-management';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { useRequest } from 'ahooks-vue';
const { t } = useI18n();
const tableStore = useTableStore();
@ -137,6 +161,20 @@
const activeCaseIndex = ref<number>(0);
const currentDeleteObj = reactive<{ id: string; name: string }>({ id: '', name: '' });
const deleteVisible = ref(false);
const keyword = ref('');
const licenseStore = useLicenseStore();
const isXpack = computed(() => licenseStore.hasLicense());
//
const currentSelectParams = ref<BatchActionQueryParams>({ selectAll: false, currentSelectCount: 0 });
const { loading: syncBugLoading, run: syncBugRun } = useRequest(
() => syncBugOpenSource({ projectId: projectId.value }),
{
pollingInterval: 1 * 1000,
pollingWhenHidden: true,
manual: true,
}
);
const syncObject = reactive({
time: '',
@ -187,6 +225,7 @@
title: 'bugManagement.bugName',
editType: ColumnEditTypeEnum.INPUT,
dataIndex: 'name',
slotName: 'name',
showTooltip: true,
},
{
@ -267,7 +306,7 @@
}
};
const { propsRes, propsEvent, setKeyword, setLoadListParams, setProps } = useTable(
const { propsRes, propsEvent, setKeyword, setLoadListParams, setProps, resetSelector } = useTable(
getBugList,
{
tableKey: TableKeyEnum.BUG_MANAGEMENT,
@ -281,6 +320,24 @@
(record) => handleNameChange(record)
);
const tableBatchActions = {
baseAction: [
{
label: 'common.export',
eventTag: 'export',
},
{
label: 'common.edit',
eventTag: 'edit',
},
{
label: 'common.delete',
eventTag: 'delete',
danger: true,
},
],
};
watchEffect(() => {
setProps({ heightUsed: heightUsed.value });
});
@ -339,9 +396,28 @@
const fetchData = async (v = '') => {
setKeyword(v);
keyword.value = v;
setProps({ data });
};
const exportConfirm = async (option: MsExportDrawerOption[]) => {
try {
const { selectedIds, selectAll, excludeIds } = currentSelectParams.value;
await exportBug({
selectIds: selectedIds || [],
selectAll,
excludeIds,
condition: { keyword: keyword.value },
bugExportColumns: option.map((item) => item),
});
Message.success(t('common.exportSuccess'));
exportVisible.value = false;
resetSelector();
} catch (error) {
Message.error(t('common.exportFail'));
}
};
const handleSingleDelete = (record?: TableData) => {
if (record) {
currentDeleteObj.id = record.id;
@ -360,7 +436,12 @@
});
};
const handleSync = () => {
syncVisible.value = true;
if (isXpack.value) {
syncVisible.value = true;
} else {
//
syncBugRun();
}
};
const handleShowDetail = (id: string, rowIndex: number) => {
@ -379,9 +460,14 @@
console.log('create', record);
};
const handleDelete = (record: BugListItem) => {
const handleBatchDelete = (params: BatchActionQueryParams) => {
// eslint-disable-next-line no-console
console.log('create', record);
console.log('create', params);
};
const handleBatchEdit = (params: BatchActionQueryParams) => {
// eslint-disable-next-line no-console
console.log('create', params);
};
const handleExport = () => {
@ -456,6 +542,23 @@
{ label: t('bugManagement.statusO.refused'), value: 'Refused' },
];
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
currentSelectParams.value = params;
switch (event.eventTag) {
case 'export':
handleExport();
break;
case 'delete':
handleBatchDelete(params);
break;
case 'edit':
handleBatchEdit(params);
break;
default:
break;
}
}
onMounted(() => {
setLoadListParams({ projectId: projectId.value });
fetchData();

View File

@ -28,6 +28,8 @@ export default {
batchEdit: '批量编辑',
selectProps: '选择属性',
batchUpdate: '批量更新为',
exportBug: '导出缺陷',
exportBugCount: '已选 {count} 条缺陷',
edit: {
defaultSystemTemplate: '默认为系统模板',
content: '缺陷内容',

View File

@ -275,6 +275,7 @@
selectable: true,
showSetting: true,
showJumpMethod: true,
heightUsed: 288,
},
(record) => ({
...record,