feat(缺陷管理): 缺陷管理创建&复制&编辑接口对接
This commit is contained in:
parent
2ad4ca9d0d
commit
f9a6582cb0
|
@ -3,8 +3,8 @@ import { CommentParams } from '@/components/business/ms-comment/types';
|
|||
import MSR from '@/api/http/index';
|
||||
import * as bugURL from '@/api/requrls/bug-management';
|
||||
|
||||
import { BugEditFormObject, BugExportParams, BugListItem, CreateOrUpdateComment } from '@/models/bug-management';
|
||||
import { AssociatedList } from '@/models/caseManagement/featureCase';
|
||||
import { BugEditFormObject, BugExportParams, BugListItem } from '@/models/bug-management';
|
||||
import { AssociatedList, OperationFile } from '@/models/caseManagement/featureCase';
|
||||
import { CommonList, TableQueryParams, TemplateOption } from '@/models/common';
|
||||
|
||||
/**
|
||||
|
@ -36,8 +36,8 @@ export function updateBatchBug(data: TableQueryParams) {
|
|||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function createBug(data: { request: BugEditFormObject; fileList: File[] }) {
|
||||
return MSR.uploadFile({ url: bugURL.postCreateBugUrl }, data, '', true);
|
||||
export function createOrUpdateBug(data: { request: BugEditFormObject; fileList: File[] }) {
|
||||
return MSR.uploadFile({ url: data.request.id ? bugURL.postUpdateBugUrl : bugURL.postCreateBugUrl }, data, '', true);
|
||||
}
|
||||
/**
|
||||
* 获取 bug 详情
|
||||
|
@ -107,3 +107,51 @@ export function getCommentList(bugId: string) {
|
|||
export function deleteComment(commentId: string) {
|
||||
return MSR.get({ url: `${bugURL.getDeleteCommentUrl}${commentId}` });
|
||||
}
|
||||
|
||||
export function getCustomFieldHeader(projectId: string) {
|
||||
return MSR.get({ url: `${bugURL.getCustomFieldHeaderUrl}${projectId}` });
|
||||
}
|
||||
|
||||
// 附件
|
||||
|
||||
// 上传文件并关联用例
|
||||
export function uploadOrAssociationFile(data: Record<string, any>) {
|
||||
return MSR.uploadFile({ url: bugURL.uploadOrAssociationFileUrl }, { request: data.request, fileList: [data.file] });
|
||||
}
|
||||
// 转存文件
|
||||
export function transferFileRequest(data: OperationFile) {
|
||||
return MSR.post({ url: bugURL.transferFileUrl, data });
|
||||
}
|
||||
// 获取文件转存目录
|
||||
export function getTransferFileTree(projectId: string) {
|
||||
return MSR.get({ url: `${bugURL.getTransferTreeUrl}/${projectId}` });
|
||||
}
|
||||
|
||||
// 预览文件
|
||||
export function previewFile(data: OperationFile) {
|
||||
return MSR.post({ url: bugURL.previewFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFileRequest(data: OperationFile) {
|
||||
return MSR.post({ url: bugURL.downloadFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
|
||||
}
|
||||
// 检查文件是否更新
|
||||
export function checkFileIsUpdateRequest(data: string[]) {
|
||||
return MSR.post({ url: bugURL.checkFileIsUpdateUrl, data });
|
||||
}
|
||||
|
||||
// 更新文件
|
||||
export function updateFile(projectId: string, id: string) {
|
||||
return MSR.get({ url: `${bugURL.getFileIsUpdateUrl}/${projectId}/${id}` });
|
||||
}
|
||||
|
||||
// 删除文件或取消关联用例文件
|
||||
export function deleteFileOrCancelAssociation(data: OperationFile) {
|
||||
return MSR.post({ url: bugURL.deleteFileOrCancelAssociationUrl, data });
|
||||
}
|
||||
|
||||
// 获取文件列表
|
||||
export function getAttachmentList(bugId: string) {
|
||||
return MSR.get({ url: `${bugURL.getAttachmentListUrl}${bugId}` });
|
||||
}
|
||||
|
|
|
@ -10,11 +10,31 @@ export const getExportConfigUrl = '/bug/export/columns/';
|
|||
export const getTemplateDetailUrl = '/bug/template/detail';
|
||||
export const getSyncBugOpenSourceUrl = '/bug/sync/';
|
||||
export const postExportBugUrl = '/bug/export';
|
||||
export const postAssociatedFileListUrl = '/bug/relate/case/page';
|
||||
export const getBugDetailUrl = '/bug/detail/';
|
||||
// 获取关联文件列表
|
||||
export const postAssociatedFileListUrl = '/bug/attachment/file/page';
|
||||
export const getBugDetailUrl = '/bug/get/';
|
||||
export const getFollowBugUrl = '/bug/follow/';
|
||||
export const getUnFollowBugUrl = '/bug/unfollow/';
|
||||
export const postUpdateCommentUrl = '/bug/comment/update';
|
||||
export const postCreateCommentUrl = '/bug/comment/add';
|
||||
export const getCommentListUrl = '/bug/comment/get/';
|
||||
export const getDeleteCommentUrl = '/bug/comment/delete/';
|
||||
export const getCustomFieldHeaderUrl = '/bug/header/custom-field/';
|
||||
// 上传or关联文件
|
||||
export const uploadOrAssociationFileUrl = '/bug/attachment/upload';
|
||||
// 转存文件
|
||||
export const transferFileUrl = '/bug/attachment/transfer';
|
||||
// 获取文件转存目录
|
||||
export const getTransferTreeUrl = '/bug/attachment/transfer/options/';
|
||||
// 预览文件
|
||||
export const previewFileUrl = '/bug/attachment/preview';
|
||||
// 下载文件
|
||||
export const downloadFileUrl = '/bug/attachment/download';
|
||||
// 检查文件是否更新
|
||||
export const checkFileIsUpdateUrl = '/bug/attachment/check-update';
|
||||
// 更新文件
|
||||
export const getFileIsUpdateUrl = '/bug/attachment/update';
|
||||
// 删除文件或取消关联用例文件
|
||||
export const deleteFileOrCancelAssociationUrl = '/bug/attachment/delete';
|
||||
// 获取附件列表
|
||||
export const getAttachmentListUrl = '/bug/attachment/list/';
|
||||
|
|
|
@ -9,6 +9,7 @@ export enum BugManagementRouteEnum {
|
|||
BUG_MANAGEMENT_INDEX = 'bugManagementIndex',
|
||||
BUG_MANAGEMENT_DETAIL = 'bbugManagementIndexDetail',
|
||||
BUG_MANAGEMENT_RECYCLE = 'bugManagementRecycle',
|
||||
BUG_MANAGEMENT_CREATE_SUCCESS = 'bugManagementCreateSuccess',
|
||||
}
|
||||
|
||||
export enum CaseManagementRouteEnum {
|
||||
|
|
|
@ -21,6 +21,7 @@ export default {
|
|||
'common.operation': 'Operation',
|
||||
'common.remove': 'Remove',
|
||||
'common.revoked': 'Revoked',
|
||||
'common.download': 'Download',
|
||||
'common.createSuccess': 'Create success',
|
||||
'common.createFailed': 'Create failed',
|
||||
'common.updateSuccess': 'Update success',
|
||||
|
|
|
@ -22,6 +22,7 @@ export default {
|
|||
'common.remove': '移除',
|
||||
'common.revoked': '已撤销',
|
||||
'common.filter': '筛选',
|
||||
'common.download': '下载',
|
||||
'common.createSuccess': '创建成功',
|
||||
'common.createFailed': '创建失败',
|
||||
'common.updateSuccess': '更新成功',
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface BugEditCustomField {
|
|||
platformOptionJson?: string; // 选项的 Json
|
||||
required: boolean;
|
||||
isMutiple?: boolean;
|
||||
options?: any[];
|
||||
}
|
||||
export interface BugEditFormObject {
|
||||
[key: string]: any;
|
||||
|
@ -62,4 +63,26 @@ export interface CreateOrUpdateComment {
|
|||
content: string;
|
||||
event: string; // 任务事件(仅评论: ’COMMENT‘; 评论并@: ’AT‘; 回复评论/回复并@: ’REPLAY‘;)
|
||||
}
|
||||
export default {};
|
||||
export interface CustomFieldItem {
|
||||
fieldId: string;
|
||||
fieldName: string;
|
||||
fieldKey: string;
|
||||
required: boolean;
|
||||
apiFieldId: string;
|
||||
defaultValue: string;
|
||||
type: string;
|
||||
options: string;
|
||||
platformOptionJson: string;
|
||||
supportSearch: boolean;
|
||||
optionMethod: string;
|
||||
internal: boolean;
|
||||
}
|
||||
|
||||
export interface OperationFile {
|
||||
id?: string;
|
||||
projectId: string;
|
||||
bugId: string;
|
||||
fileId?: string; // 文件id
|
||||
associated: boolean; // 是否是本地 还是关联
|
||||
moduleId?: string; // 文件转存模块id
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { key } from 'localforage';
|
||||
|
||||
import { TableQueryParams } from '@/models/common';
|
||||
import { StatusType } from '@/enums/caseEnum';
|
||||
|
||||
|
@ -261,10 +263,11 @@ export interface CreateOrUpdateDemand {
|
|||
export interface OperationFile {
|
||||
id?: string;
|
||||
projectId: string;
|
||||
caseId: string;
|
||||
caseId?: string;
|
||||
fileId?: string; // 文件id
|
||||
local: boolean; // 是否是本地
|
||||
local?: boolean; // 是否是本地
|
||||
moduleId?: string; // 文件转存模块id
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 评论列表项
|
||||
|
|
|
@ -29,7 +29,7 @@ const BugManagement: AppRouteRecordRaw = {
|
|||
},
|
||||
// 缺陷管理-编辑缺陷
|
||||
{
|
||||
path: 'edit',
|
||||
path: 'detail/:mode?',
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
|
||||
component: () => import('@/views/bug-management/edit.vue'),
|
||||
meta: {
|
||||
|
@ -42,12 +42,23 @@ const BugManagement: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
|
||||
locale: 'bugManagement.editBug',
|
||||
editLocale: 'menu.settings.organization.templateFieldSetting',
|
||||
locale: 'bugManagement.addBug',
|
||||
editTag: 'id',
|
||||
editLocale: 'bugManagement.editBug',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// 创建用例成功
|
||||
{
|
||||
path: 'create-success',
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
|
||||
component: () => import('@/views/bug-management/createSuccess.vue'),
|
||||
meta: {
|
||||
locale: 'bugManagement.createBugSuccess',
|
||||
roles: ['PROJECT_BUG:READ+ADD', 'PROJECT_BUG:READ+UPDATE'],
|
||||
},
|
||||
},
|
||||
// 回收站
|
||||
{
|
||||
path: 'recycle',
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { findKey } from 'lodash-es';
|
||||
import JSEncrypt from 'jsencrypt';
|
||||
|
||||
import { MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type';
|
||||
|
||||
import { CustomFieldItem } from '@/models/bug-management';
|
||||
|
||||
import { isObject } from './is';
|
||||
|
||||
type TargetContext = '_self' | '_parent' | '_blank' | '_top';
|
||||
|
@ -485,3 +490,18 @@ export function formatPhoneNumber(phoneNumber = '') {
|
|||
}
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
export function customFieldToColumns(customFields: CustomFieldItem[]) {
|
||||
return customFields.map((field) => {
|
||||
const { fieldName, fieldKey, fieldId } = field;
|
||||
const column: MsTableColumnData = {
|
||||
title: fieldName,
|
||||
dataIndex: ['handleUser', 'status'].includes(fieldId) ? fieldKey : fieldId,
|
||||
showTooltip: true,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
};
|
||||
return column;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
<template>
|
||||
<MsCard simple>
|
||||
<div class="h-full">
|
||||
<div class="mt-8 text-center">
|
||||
<div class="flex justify-center"><svg-icon :width="'60px'" :height="'60px'" :name="'success'" /></div>
|
||||
<div class="mb-2 mt-6 text-[20px] font-medium"> {{ t('common.addSuccess') }} </div>
|
||||
<div
|
||||
><span class="mr-1 text-[rgb(var(--primary-5))]">{{ countDown }}</span
|
||||
><span class="text-[var(--color-text-4)]">{{ t('bugManagement.success.countDownTip') }}</span></div
|
||||
>
|
||||
<div class="my-6">
|
||||
<a-button type="primary" @click="goDetail"> {{ t('bugManagement.success.bugDetail') }} </a-button>
|
||||
<a-button class="mx-3" type="outline" @click="continueCreate">
|
||||
{{ t('bugManagement.success.addContinueCreate') }}
|
||||
</a-button>
|
||||
<a-button type="secondary" @click="backCaseList">
|
||||
{{ t('bugManagement.success.backBugList') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<a-checkbox v-model="isNextTip" class="mb-6">{{ t('bugManagement.success.notNextTip') }}</a-checkbox>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-4 font-medium">{{ t('bugManagement.success.mightWantTo') }}</div>
|
||||
<MsCardList
|
||||
mode="static"
|
||||
:card-min-width="569"
|
||||
class="flex-1"
|
||||
:shadow-limit="50"
|
||||
:list="cardList"
|
||||
:is-proportional="false"
|
||||
:gap="16"
|
||||
padding-bottom-space="16px"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<div class="outerWrapper p-[3px]">
|
||||
<div class="innerWrapper flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="logo-img flex h-[48px] w-[48px] items-center justify-center">
|
||||
<svg-icon width="36px" height="36px" :name="item.key"></svg-icon>
|
||||
</div>
|
||||
<div class="ml-2"> {{ item.name }} </div>
|
||||
</div>
|
||||
|
||||
<a-button type="outline" @click="handleCaseRelated">
|
||||
{{ t('bugManagement.success.caseRelated') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MsCardList>
|
||||
</div>
|
||||
</div>
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsCardList from '@/components/business/ms-card-list/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
|
||||
import { BugManagementRouteEnum, RouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const visitedKey = 'doNotNextTipCreateBug';
|
||||
const { addVisited } = useVisit(visitedKey);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const cardList = ref([
|
||||
{
|
||||
key: 'caseReview',
|
||||
name: t('bugManagement.success.caseRelated'),
|
||||
},
|
||||
]);
|
||||
|
||||
const isNextTip = ref<boolean>(false);
|
||||
const countDown = ref<number>(5);
|
||||
const timer = ref<any>(null);
|
||||
function setCountdown() {
|
||||
// timer.value = setInterval(() => {
|
||||
// if (countDown.value > 1) {
|
||||
// --countDown.value;
|
||||
// } else {
|
||||
// clearInterval(timer.value);
|
||||
// router.push({
|
||||
// name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
// });
|
||||
// }
|
||||
// }, 1000);
|
||||
timer.value = 5;
|
||||
}
|
||||
|
||||
function isDoNotShowAgainChecked() {
|
||||
if (isNextTip.value) {
|
||||
addVisited();
|
||||
}
|
||||
}
|
||||
|
||||
// 返回用例列表
|
||||
function backCaseList() {
|
||||
clearInterval(timer.value);
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
});
|
||||
}
|
||||
|
||||
// 继续创建
|
||||
function continueCreate() {
|
||||
clearInterval(timer.value);
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
|
||||
});
|
||||
}
|
||||
|
||||
function goDetail() {
|
||||
clearInterval(timer.value);
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
query: route.query,
|
||||
});
|
||||
}
|
||||
|
||||
// 关联用例
|
||||
function handleCaseRelated() {
|
||||
router.push({
|
||||
name: RouteEnum.CASE_MANAGEMENT_CASE,
|
||||
query: route.query,
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => isNextTip.value,
|
||||
() => {
|
||||
isDoNotShowAgainChecked();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
setCountdown();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.outerWrapper {
|
||||
box-shadow: 0 6px 15px rgba(120 56 135/ 5%);
|
||||
@apply rounded bg-white;
|
||||
.innerWrapper {
|
||||
background: var(--color-bg-3);
|
||||
@apply rounded p-6;
|
||||
.logo-img {
|
||||
@apply mr-3 flex items-center bg-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -6,8 +6,9 @@
|
|||
has-breadcrumb
|
||||
:title="title"
|
||||
:loading="loading"
|
||||
@save="saveHandler"
|
||||
@save-and-continue="saveHandler"
|
||||
:is-edit="isEdit"
|
||||
@save="saveHandler(false)"
|
||||
@save-and-continue="saveHandler(true)"
|
||||
>
|
||||
<template #headerRight>
|
||||
<a-select
|
||||
|
@ -68,30 +69,82 @@
|
|||
</div>
|
||||
</a-form-item>
|
||||
<div class="mb-[8px] mt-[2px] text-[var(--color-text-4)]">{{ t('bugManagement.edit.fileExtra') }}</div>
|
||||
<FileList
|
||||
:show-tab="false"
|
||||
:file-list="fileList"
|
||||
:upload-func="uploadFile"
|
||||
@delete-file="deleteFile"
|
||||
@reupload="reupload"
|
||||
@handle-preview="handlePreview"
|
||||
>
|
||||
<FileList mode="static" :file-list="fileList">
|
||||
<template #actions="{ item }">
|
||||
<!-- 本地文件 -->
|
||||
<div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="handlePreview(item)"
|
||||
>
|
||||
{{ t('ms.upload.preview') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="transferFile"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.storage') }}
|
||||
</MsButton>
|
||||
<TransferModal
|
||||
v-model:visible="transferVisible"
|
||||
:request-fun="transferFileRequest"
|
||||
:params="{
|
||||
projectId: currentProjectId,
|
||||
bugId: bugId as string,
|
||||
fileId: item.uid,
|
||||
associated: !item.local,
|
||||
}"
|
||||
@success="getDetailInfo()"
|
||||
/>
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('common.download') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<!-- 关联文件 -->
|
||||
<div v-else class="flex flex-nowrap">
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="handlePreview(item)"
|
||||
>
|
||||
{{ t('ms.upload.preview') }}
|
||||
</MsButton>
|
||||
<MsButton v-if="bugId" type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
|
||||
{{ t('common.download') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="bugId && item.isUpdateFlag"
|
||||
type="button"
|
||||
status="primary"
|
||||
@click="handleUpdateFile(item)"
|
||||
>
|
||||
{{ t('common.update') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #title="{ item }">
|
||||
<span v-if="item.isUpdateFlag" class="ml-4 flex items-center font-normal text-[rgb(var(--warning-6))]"
|
||||
><icon-exclamation-circle-fill /> <span>{{ t('caseManagement.featureCase.fileIsUpdated') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</FileList>
|
||||
</div>
|
||||
<a-divider class="ml-[16px]" direction="vertical" />
|
||||
<div class="right mt-[16px] max-w-[433px] grow pr-[24px]">
|
||||
<!-- <a-form-item
|
||||
:label="t('bugManagement.handleMan')"
|
||||
field="handleMan"
|
||||
:rules="[{ required: true, message: t('bugManagement.edit.handleManIsRequired') }]"
|
||||
>
|
||||
<MsUserSelector
|
||||
v-model:model-value="form.handleMan"
|
||||
:type="UserRequestTypeEnum.PROJECT_PERMISSION_MEMBER"
|
||||
:load-option-params="{ projectId: appStore.currentProjectId }"
|
||||
placeholder="bugManagement.edit.handleManPlaceholder"
|
||||
/>
|
||||
</a-form-item> -->
|
||||
<MsFormCreate
|
||||
v-if="formRules.length"
|
||||
ref="formCreateRef"
|
||||
|
@ -101,7 +154,7 @@
|
|||
/>
|
||||
<a-form-item field="tag" :label="t('bugManagement.tag')">
|
||||
<MsTagsInput
|
||||
v-model:model-value="form.tag"
|
||||
v-model:model-value="form.tags"
|
||||
:placeholder="t('bugManagement.edit.tagPlaceholder')"
|
||||
allow-clear
|
||||
/>
|
||||
|
@ -133,12 +186,14 @@
|
|||
:get-list-fun-params="getListFunParams"
|
||||
@save="saveSelectAssociatedFile"
|
||||
/>
|
||||
<a-image-preview v-model:visible="previewVisible" :src="imageUrl" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import { FileItem, Message } from '@arco-design/web-vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
|
||||
import { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
|
||||
|
@ -148,25 +203,32 @@
|
|||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
import RelateFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
|
||||
import TransferModal from '@/views/case-management/caseManagementFeature/components/tabContent/transferModal.vue';
|
||||
|
||||
// import { MsUserSelector } from '@/components/business/ms-user-selector';
|
||||
// import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
|
||||
import {
|
||||
createBug,
|
||||
checkFileIsUpdateRequest,
|
||||
createOrUpdateBug,
|
||||
downloadFileRequest,
|
||||
getAssociatedFileList,
|
||||
getBugDetail,
|
||||
getTemplateById,
|
||||
getTemplateOption,
|
||||
previewFile,
|
||||
transferFileRequest,
|
||||
updateFile,
|
||||
} from '@/api/modules/bug-management';
|
||||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import router from '@/router';
|
||||
import { useAppStore } from '@/store';
|
||||
import { downloadByteFile } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
|
||||
import { BugEditCustomField, BugEditCustomFieldItem, BugEditFormObject } from '@/models/bug-management';
|
||||
import { AssociatedList, AttachFileInfo } from '@/models/caseManagement/featureCase';
|
||||
import { TableQueryParams } from '@/models/common';
|
||||
import { SelectValue } from '@/models/projectManagement/menuManagement';
|
||||
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { convertToFile } from '../case-management/caseManagementFeature/components/utils';
|
||||
|
||||
|
@ -187,7 +249,7 @@
|
|||
title: '',
|
||||
description: '',
|
||||
templateId: '',
|
||||
tag: [],
|
||||
tags: [],
|
||||
});
|
||||
|
||||
const getListFunParams = ref<TableQueryParams>({
|
||||
|
@ -201,12 +263,16 @@
|
|||
const fileList = ref<MsFileItem[]>([]);
|
||||
const formRules = ref<FormItem[]>([]);
|
||||
const formItem = ref<FormRuleItem[]>([]);
|
||||
const fApi = ref({});
|
||||
const fApi = ref<any>(null);
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
const associatedDrawer = ref(false);
|
||||
const loading = ref(false);
|
||||
const acceptType = ref('none'); // 模块-上传文件类型
|
||||
|
||||
const isEdit = computed(() => !!route.query.id);
|
||||
const isEdit = computed(() => !!route.query.id && route.params.mode === 'edit');
|
||||
const bugId = computed(() => route.query.id || '');
|
||||
const imageUrl = ref('');
|
||||
const previewVisible = ref<boolean>(false);
|
||||
|
||||
const title = computed(() => {
|
||||
return isEdit.value ? t('bugManagement.editBug') : t('bugManagement.createBug');
|
||||
|
@ -262,6 +328,11 @@
|
|||
(id: string) => !currentAlreadyAssociateFileList.value.includes(id) && !deleteAssociateFileIds.includes(id)
|
||||
);
|
||||
});
|
||||
const transferVisible = ref<boolean>(false);
|
||||
// 转存
|
||||
function transferFile() {
|
||||
transferVisible.value = true;
|
||||
}
|
||||
|
||||
// 处理表单格式
|
||||
const getFormRules = (arr: BugEditCustomField[]) => {
|
||||
|
@ -273,11 +344,11 @@
|
|||
name: item.fieldId,
|
||||
label: item.fieldName,
|
||||
value: item.value,
|
||||
options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : [],
|
||||
options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : item.options,
|
||||
required: item.required as boolean,
|
||||
props: {
|
||||
modelValue: item.value,
|
||||
options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : [],
|
||||
options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : item.options,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -316,37 +387,53 @@
|
|||
}
|
||||
};
|
||||
|
||||
const handlePreview = (item: FileItem) => {
|
||||
const { url } = item;
|
||||
window.open(url);
|
||||
};
|
||||
|
||||
const deleteFile = (item: FileItem) => {
|
||||
fileList.value = fileList.value.filter((e) => e.uid !== item.uid);
|
||||
};
|
||||
|
||||
const reupload = (item: FileItem) => {
|
||||
fileList.value = fileList.value.map((e) => {
|
||||
if (e.uid === item.uid) {
|
||||
return {
|
||||
...e,
|
||||
status: 'init',
|
||||
};
|
||||
// 预览图片
|
||||
async function handlePreview(item: MsFileItem) {
|
||||
try {
|
||||
previewVisible.value = true;
|
||||
if (item.status !== 'init') {
|
||||
const res = await previewFile({
|
||||
projectId: currentProjectId.value,
|
||||
bugId: bugId.value as string,
|
||||
fileId: item.uid,
|
||||
associated: !item.local,
|
||||
});
|
||||
const blob = new Blob([res], { type: 'image/jpeg' });
|
||||
imageUrl.value = URL.createObjectURL(blob);
|
||||
} else {
|
||||
imageUrl.value = item.url || '';
|
||||
}
|
||||
return e;
|
||||
});
|
||||
};
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const uploadFile = (file: File) => {
|
||||
const fileItem: FileItem = {
|
||||
uid: `${Date.now()}`,
|
||||
name: file.name,
|
||||
status: 'init',
|
||||
file,
|
||||
};
|
||||
fileList.value.push(fileItem);
|
||||
return Promise.resolve(fileItem);
|
||||
};
|
||||
// 下载
|
||||
async function downloadFile(item: MsFileItem) {
|
||||
try {
|
||||
const res = await downloadFileRequest({
|
||||
projectId: currentProjectId.value,
|
||||
bugId: bugId.value as string,
|
||||
fileId: item.uid,
|
||||
associated: !item.local,
|
||||
});
|
||||
downloadByteFile(res, `${item.name}`);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新文件
|
||||
async function handleUpdateFile(item: MsFileItem) {
|
||||
try {
|
||||
await updateFile(currentProjectId.value, item.associationId);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
function beforeUpload(file: File) {
|
||||
const _maxSize = 50 * 1024 * 1024;
|
||||
|
@ -378,10 +465,10 @@
|
|||
}
|
||||
|
||||
// 保存
|
||||
const saveHandler = async () => {
|
||||
const saveHandler = async (isContinue = false) => {
|
||||
formRef.value.validate((error: any) => {
|
||||
if (!error) {
|
||||
formCreateRef.value.formApi.validate(async (valid: any) => {
|
||||
fApi.value.validate(async (valid: any) => {
|
||||
if (valid === true) {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
@ -398,11 +485,41 @@
|
|||
}
|
||||
const tmpObj = {
|
||||
...form.value,
|
||||
tag: form.value.tag.join(',') || '',
|
||||
customFields,
|
||||
};
|
||||
await createBug({ request: tmpObj, fileList: fileList.value as unknown as File[] });
|
||||
Message.success(t('common.createSuccess'));
|
||||
// 执行保存操作
|
||||
const res = await createOrUpdateBug({ request: tmpObj, fileList: fileList.value as unknown as File[] });
|
||||
if (isEdit.value) {
|
||||
Message.success(t('common.updateSuccess'));
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
});
|
||||
} else {
|
||||
Message.success(t('common.createSuccess'));
|
||||
if (isContinue) {
|
||||
// 如果是保存并继续创建
|
||||
const { templateId } = form.value;
|
||||
// 用当前模板初始化自定义字段
|
||||
await templateChange(templateId);
|
||||
form.value = {
|
||||
projectId: appStore.currentProjectId, // 取当前项目id
|
||||
title: '',
|
||||
description: '',
|
||||
templateId,
|
||||
tags: [],
|
||||
};
|
||||
// 清空文件列表
|
||||
fileList.value = [];
|
||||
} else {
|
||||
// 否则跳转到成功页
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
|
||||
query: {
|
||||
id: res.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
|
@ -418,11 +535,45 @@
|
|||
|
||||
const getDetailInfo = async () => {
|
||||
const id = route.query.id as string;
|
||||
// TODO: 等后端接口
|
||||
if (!id) return;
|
||||
const res = await getBugDetail(id);
|
||||
const { customFields, file } = res;
|
||||
formRules.value = customFields;
|
||||
fileList.value = file;
|
||||
const { customFields, templateId, attachments } = res;
|
||||
if (attachments && attachments.length) {
|
||||
attachmentsList.value = attachments;
|
||||
// 检查文件是否有更新
|
||||
const checkUpdateFileIds = await checkFileIsUpdateRequest(attachments.value.map((item: any) => item.id));
|
||||
// 处理文件列表
|
||||
fileList.value = attachments
|
||||
.map((fileInfo: any) => {
|
||||
return {
|
||||
...fileInfo,
|
||||
name: fileInfo.fileName,
|
||||
isUpdateFlag: checkUpdateFileIds.value.includes(fileInfo.id),
|
||||
};
|
||||
})
|
||||
.map((fileInfo: any) => {
|
||||
return convertToFile(fileInfo);
|
||||
});
|
||||
}
|
||||
// 根据模板ID 初始化自定义字段
|
||||
await templateChange(templateId);
|
||||
const tmpObj = {};
|
||||
if (customFields && Array.isArray(customFields)) {
|
||||
customFields.forEach((item) => {
|
||||
tmpObj[item.id] = item.value;
|
||||
});
|
||||
}
|
||||
// 自定义字段赋值
|
||||
fApi.value.setValue(tmpObj);
|
||||
// 表单赋值
|
||||
form.value = {
|
||||
id: res.id,
|
||||
title: res.title,
|
||||
description: res.description,
|
||||
templateId: res.templateId,
|
||||
tags: res.tags,
|
||||
projectId: res.projectId,
|
||||
};
|
||||
};
|
||||
|
||||
const initDefaultFields = () => {
|
||||
|
@ -451,9 +602,13 @@
|
|||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (isEdit.value) {
|
||||
const { mode } = route.params;
|
||||
if (mode === 'edit') {
|
||||
// 详情
|
||||
getDetailInfo();
|
||||
} else if (mode === 'copy') {
|
||||
getDetailInfo();
|
||||
initDefaultFields();
|
||||
} else {
|
||||
initDefaultFields();
|
||||
}
|
||||
|
|
|
@ -28,8 +28,9 @@
|
|||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #name="{ record, rowIndex }">
|
||||
<a-button type="text" class="px-0" @click="handleShowDetail(record.id, rowIndex)">{{ record.title }}</a-button>
|
||||
<!-- ID -->
|
||||
<template #num="{ record, rowIndex }">
|
||||
<a-button type="text" class="px-0" @click="handleShowDetail(record.id, rowIndex)">{{ record.num }}</a-button>
|
||||
</template>
|
||||
<!-- 严重程度 -->
|
||||
<template #severity="{ record }">
|
||||
|
@ -134,7 +135,7 @@
|
|||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" async setup>
|
||||
import { Message, TableData } from '@arco-design/web-vue';
|
||||
|
||||
import { MsAdvanceFilter, timeSelectOptions } from '@/components/pure/ms-advance-filter';
|
||||
|
@ -158,6 +159,7 @@
|
|||
deleteSingleBug,
|
||||
exportBug,
|
||||
getBugList,
|
||||
getCustomFieldHeader,
|
||||
getExportConfig,
|
||||
syncBugOpenSource,
|
||||
} from '@/api/modules/bug-management';
|
||||
|
@ -167,6 +169,7 @@
|
|||
import router from '@/router';
|
||||
import { useAppStore, useTableStore } from '@/store';
|
||||
import useLicenseStore from '@/store/modules/setting/license';
|
||||
import { customFieldToColumns } from '@/utils';
|
||||
|
||||
import { BugEditCustomField, BugListItem } from '@/models/bug-management';
|
||||
import { RouteEnum } from '@/enums/routeEnum';
|
||||
|
@ -225,7 +228,7 @@
|
|||
},
|
||||
{
|
||||
title: 'bugManagement.bugName',
|
||||
dataIndex: 'name',
|
||||
dataIndex: 'title',
|
||||
type: FilterType.SELECT,
|
||||
selectProps: {
|
||||
mode: 'static',
|
||||
|
@ -259,17 +262,25 @@
|
|||
|
||||
const heightUsed = computed(() => 286 + (filterVisible.value ? 160 + (filterRowCount.value - 1) * 60 : 0));
|
||||
|
||||
// 获取自定义字段
|
||||
const getCustomFieldColumns = async () => {
|
||||
const res = await getCustomFieldHeader(projectId.value);
|
||||
customFields.value = res;
|
||||
return customFieldToColumns(res);
|
||||
};
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'bugManagement.ID',
|
||||
dataIndex: 'num',
|
||||
slotName: 'num',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: 'bugManagement.bugName',
|
||||
editType: ColumnEditTypeEnum.INPUT,
|
||||
dataIndex: 'title',
|
||||
slotName: 'name',
|
||||
width: 300,
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
|
@ -347,7 +358,8 @@
|
|||
width: 158,
|
||||
},
|
||||
];
|
||||
await tableStore.initColumn(TableKeyEnum.BUG_MANAGEMENT, columns, 'drawer');
|
||||
const customColumns = await getCustomFieldColumns();
|
||||
await tableStore.initColumn(TableKeyEnum.BUG_MANAGEMENT, columns.concat(customColumns), 'drawer');
|
||||
|
||||
const handleNameChange = async (record: BugListItem) => {
|
||||
try {
|
||||
|
@ -399,34 +411,6 @@
|
|||
setKeyword(v);
|
||||
keyword.value = v;
|
||||
await loadList();
|
||||
customFields.value = propsRes.value.customFields || [
|
||||
{
|
||||
fieldId: 'handleUser',
|
||||
fieldName: '处理人',
|
||||
required: true,
|
||||
apiFieldId: null,
|
||||
defaultValue: null,
|
||||
type: 'select',
|
||||
options: null,
|
||||
platformOptionJson:
|
||||
'[{"text":"副驾仙人","value":"728495172886530"},{"text":"社恐的程序员","value":"728495172886645"}]',
|
||||
supportSearch: null,
|
||||
optionMethod: null,
|
||||
isMutiple: true,
|
||||
},
|
||||
{
|
||||
fieldId: 'status',
|
||||
fieldName: '状态',
|
||||
required: true,
|
||||
apiFieldId: null,
|
||||
defaultValue: null,
|
||||
type: 'select',
|
||||
options: null,
|
||||
platformOptionJson: '[{"text":"新建","value":"100555929702892317"}]',
|
||||
supportSearch: null,
|
||||
optionMethod: null,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const handleAdvSearch = (filter: FilterResult) => {
|
||||
|
@ -500,6 +484,8 @@
|
|||
name: RouteEnum.BUG_MANAGEMENT_DETAIL,
|
||||
query: {
|
||||
id: record.id,
|
||||
},
|
||||
params: {
|
||||
mode: 'copy',
|
||||
},
|
||||
});
|
||||
|
@ -510,6 +496,8 @@
|
|||
name: RouteEnum.BUG_MANAGEMENT_DETAIL,
|
||||
query: {
|
||||
id: record.id,
|
||||
},
|
||||
params: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export default {
|
||||
bugManagement: {
|
||||
index: '缺陷管理',
|
||||
addBug: '创建缺陷',
|
||||
editBug: '编辑缺陷',
|
||||
recycle: '回收站',
|
||||
createBug: '创建缺陷',
|
||||
|
@ -80,6 +81,15 @@ export default {
|
|||
handleUser: '处理人',
|
||||
tag: '标签',
|
||||
},
|
||||
success: {
|
||||
countDownTip: '后回到缺陷列表,也可以手动回到缺陷列表',
|
||||
bugDetail: '缺陷详情',
|
||||
addContinueCreate: '继续创建',
|
||||
backBugList: '返回缺陷列表',
|
||||
notNextTip: '下次不再提醒',
|
||||
mightWantTo: '你可能还想',
|
||||
caseRelated: '关联用例',
|
||||
},
|
||||
severityO: {
|
||||
fatal: '致命',
|
||||
serious: '严重',
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
|
||||
import { createBug, getTemplateDetailInfo, getTemplateOption } from '@/api/modules/bug-management/index';
|
||||
import { createOrUpdateBug, getTemplateDetailInfo, getTemplateOption } from '@/api/modules/bug-management/index';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
|
@ -89,7 +89,10 @@
|
|||
if (!errors) {
|
||||
drawerLoading.value = true;
|
||||
try {
|
||||
await createBug({ request: { ...form.value, customFields: templateCustomFields.value }, fileList: [] });
|
||||
await createOrUpdateBug({
|
||||
request: { ...form.value, customFields: templateCustomFields.value },
|
||||
fileList: [],
|
||||
});
|
||||
Message.success(t('caseManagement.featureCase.quicklyCreateDefectSuccess'));
|
||||
if (!isContinue) {
|
||||
handleDrawerCancel();
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
const formRef = ref<FormInstance>();
|
||||
|
||||
const loading = ref(false);
|
||||
const isEdit = computed(() => props.currentProject && props.currentProject.id);
|
||||
const isEdit = computed(() => !!(props.currentProject && props.currentProject.id));
|
||||
const affiliatedOrgOption = ref<SystemOrgOption[]>([]);
|
||||
const appStore = useAppStore();
|
||||
const currentOrgId = computed(() => appStore.currentOrgId);
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
</template>
|
||||
</MsBaseTable>
|
||||
<AddProjectModal
|
||||
v-if="addProjectVisible"
|
||||
:visible="addProjectVisible"
|
||||
:current-project="currentUpdateProject"
|
||||
@cancel="handleAddProjectModalCancel"
|
||||
|
@ -252,8 +253,8 @@
|
|||
];
|
||||
|
||||
const showAddProject = () => {
|
||||
currentUpdateProject.value = undefined;
|
||||
addProjectVisible.value = true;
|
||||
currentUpdateProject.value = undefined;
|
||||
};
|
||||
|
||||
const handleEnableOrDisableProject = async (record: any, isEnable = true) => {
|
||||
|
@ -317,6 +318,7 @@
|
|||
};
|
||||
const handleAddProjectModalCancel = (shouldSearch: boolean) => {
|
||||
addProjectVisible.value = false;
|
||||
currentUpdateProject.value = undefined;
|
||||
if (shouldSearch) {
|
||||
fetchData();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue