feat(功能用例): 缺陷需求调整&附件联调&关联用例补充
This commit is contained in:
parent
81b9f083e5
commit
74026933e4
|
@ -1,7 +1,7 @@
|
|||
import MSR from '@/api/http/index';
|
||||
import * as bugURL from '@/api/requrls/bug-management';
|
||||
|
||||
import { BugExportParams, BugListItem } from '@/models/bug-management';
|
||||
import { BugExportParams, BugListItem, DefaultTemplate } from '@/models/bug-management';
|
||||
import { CommonList, TableQueryParams, TemplateOption } from '@/models/common';
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@ export function updateBatchBug(data: TableQueryParams) {
|
|||
}
|
||||
|
||||
export function createBug(data: TableQueryParams) {
|
||||
return MSR.post({ url: bugURL.postCreateBugUrl, data });
|
||||
return MSR.uploadFile({ url: bugURL.postCreateBugUrl }, { request: data.request, fileList: data.fileList }, '');
|
||||
}
|
||||
|
||||
export function deleteSingleBug(data: TableQueryParams) {
|
||||
|
@ -44,6 +44,10 @@ export function getTemplateById(data: TableQueryParams) {
|
|||
export function getExportConfig(projectId: string) {
|
||||
return MSR.get({ url: `${bugURL.getExportConfigUrl}${projectId}` });
|
||||
}
|
||||
// 获取模版详情
|
||||
export function getTemplateDetailInfo(data: DefaultTemplate) {
|
||||
return MSR.post({ url: `${bugURL.getTemplateDetailUrl}`, data });
|
||||
}
|
||||
|
||||
// 同步缺陷
|
||||
export function syncBugOpenSource(params: { projectId: string }) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
DeleteReviewModuleUrl,
|
||||
EditReviewUrl,
|
||||
FollowReviewUrl,
|
||||
GetAssociatedIdsUrl,
|
||||
GetReviewDetailUrl,
|
||||
GetReviewListUrl,
|
||||
GetReviewModulesUrl,
|
||||
|
@ -107,3 +108,8 @@ export const getReviewDetail = (id: string) => {
|
|||
export const getReviewUsers = (projectId: string, keyword: string) => {
|
||||
return MSR.get<ReviewUserItem[]>({ url: `${GetReviewUsersUrl}/${projectId}`, params: { keyword } });
|
||||
};
|
||||
|
||||
// 获取评审人员列表
|
||||
export const getAssociatedIds = (reviewId: string) => {
|
||||
return MSR.get<string[]>({ url: `${GetAssociatedIdsUrl}/${reviewId}` });
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
BatchEditCaseUrl,
|
||||
BatchMoveCaseUrl,
|
||||
CancelAssociationDemandUrl,
|
||||
checkFileIsUpdateUrl,
|
||||
CreateCaseModuleTreeUrl,
|
||||
CreateCaseUrl,
|
||||
CreateCommentItemUrl,
|
||||
|
@ -26,6 +27,8 @@ import {
|
|||
GetCommentListUrl,
|
||||
GetDefaultTemplateFieldsUrl,
|
||||
GetDemandListUrl,
|
||||
GetDetailCaseReviewUrl,
|
||||
GetFileIsUpdateUrl,
|
||||
GetRecycleCaseListUrl,
|
||||
GetRecycleCaseModulesCountUrl,
|
||||
GetSearchCustomFieldsUrl,
|
||||
|
@ -215,13 +218,22 @@ export function getTransferFileTree(projectId: string) {
|
|||
|
||||
// 预览文件
|
||||
export function previewFile(data: OperationFile) {
|
||||
return MSR.post({ url: PreviewFileUrl, data });
|
||||
return MSR.post({ url: PreviewFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFileRequest(data: OperationFile) {
|
||||
return MSR.post({ url: DownloadFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
|
||||
}
|
||||
// 检查文件是否更新
|
||||
export function checkFileIsUpdateRequest(data: string[]) {
|
||||
return MSR.post({ url: checkFileIsUpdateUrl, data });
|
||||
}
|
||||
|
||||
// 更新文件
|
||||
export function updateFile(projectId: string, id: string) {
|
||||
return MSR.get({ url: `${GetFileIsUpdateUrl}/${projectId}/${id}` });
|
||||
}
|
||||
|
||||
// 删除文件或取消关联用例文件
|
||||
export function deleteFileOrCancelAssociation(data: OperationFile) {
|
||||
|
@ -252,4 +264,9 @@ export function DeleteCommentList(commentId: string) {
|
|||
return MSR.post({ url: `${DeleteCommentItemUrl}/${commentId}` });
|
||||
}
|
||||
|
||||
// 评审
|
||||
export function getDetailCaseReviewPage(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<CaseManagementTable>>({ url: GetDetailCaseReviewUrl, data });
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -7,5 +7,6 @@ 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 getTemplateDetailUrl = '/bug/template/detail';
|
||||
export const getSyncBugOpenSourceUrl = '/bug/sync/';
|
||||
export const postExportBugUrl = '/bug/export';
|
||||
|
|
|
@ -13,3 +13,4 @@ export const MoveReviewModuleUrl = '/case/review/module/move'; // 移动评审
|
|||
export const AddReviewModuleUrl = '/case/review/module/add'; // 新增评审模块
|
||||
export const GetReviewModulesUrl = '/case/review/module/tree'; // 获取评审模块树
|
||||
export const DeleteReviewModuleUrl = '/case/review/module/delete'; // 删除评审模块
|
||||
export const GetAssociatedIdsUrl = '/case/review/detail/get-ids'; // 获取已关联用例id集合
|
||||
|
|
|
@ -85,6 +85,10 @@ export const DownloadFileUrl = '/attachment/download';
|
|||
export const deleteFileOrCancelAssociationUrl = '/attachment/delete/file';
|
||||
// 获取转存目录
|
||||
export const getTransferTreeUrl = '/attachment/options';
|
||||
// 附件是否更新
|
||||
export const GetFileIsUpdateUrl = '/attachment/update';
|
||||
// 检查文件是否更新
|
||||
export const checkFileIsUpdateUrl = '/attachment/check-update';
|
||||
|
||||
// 评论列表
|
||||
export const GetCommentListUrl = '/functional/case/comment/get/list';
|
||||
|
@ -94,5 +98,7 @@ export const CreateCommentItemUrl = '/functional/case/comment/save';
|
|||
export const UpdateCommentItemUrl = '/functional/case/comment/update';
|
||||
// 删除评论
|
||||
export const DeleteCommentItemUrl = '/functional/case/comment/delete';
|
||||
// 获取详情用例评审
|
||||
export const GetDetailCaseReviewUrl = '/functional/case/review/page';
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
<template #headerLeft>
|
||||
<div class="float-left">
|
||||
<a-select
|
||||
v-if="props?.moduleOptions"
|
||||
v-model="caseType"
|
||||
class="ml-2 max-w-[100px]"
|
||||
:placeholder="t('caseManagement.featureCase.PleaseSelect')"
|
||||
>
|
||||
<a-option v-for="item of actionType" :key="item.value" :value="item.value">{{ item.name }}</a-option>
|
||||
<a-option v-for="item of props?.moduleOptions" :key="item.value" :value="item.value">{{
|
||||
t(item.label)
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -21,7 +24,7 @@
|
|||
<div class="w-[292px] border-r border-[var(--color-text-n8)] p-[16px]">
|
||||
<div class="flex items-center justify-between">
|
||||
<MsProjectSelect v-model:project="innerProject" class="mb-[16px]" />
|
||||
<a-select v-if="caseType === 'API'" v-model="protocolType" class="mb-[16px] ml-2 max-w-[90px]">
|
||||
<a-select v-if="caseType === 'API_CASE'" v-model="protocolType" class="mb-[16px] ml-2 max-w-[90px]">
|
||||
<a-option v-for="item of protocolOptions" :key="item" :value="item">{{ item }}</a-option>
|
||||
</a-select>
|
||||
</div>
|
||||
|
@ -34,8 +37,8 @@
|
|||
<div class="folder">
|
||||
<div :class="getFolderClass('all')" @click="setActiveFolder('all')">
|
||||
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||
<div class="folder-name">{{ t('caseManagement.caseReview.allReviews') }}</div>
|
||||
<div class="folder-count">({{ allCaseCount }})</div>
|
||||
<div class="folder-name">{{ t('caseManagement.featureCase.allCase') }}</div>
|
||||
<div class="folder-count">({{ props.modulesCount['all'] }})</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-divider class="my-[8px]" />
|
||||
|
@ -69,6 +72,7 @@
|
|||
<MsAdvanceFilter
|
||||
v-model:keyword="keyword"
|
||||
:filter-config-list="filterConfigList"
|
||||
:custom-fields-config-list="searchCustomFields"
|
||||
:row-count="filterRowCount"
|
||||
:search-placeholder="t('caseManagement.caseReview.searchPlaceholder')"
|
||||
@keyword-search="searchCase"
|
||||
|
@ -94,7 +98,7 @@
|
|||
</MsAdvanceFilter>
|
||||
<ms-base-table v-bind="propsRes" no-disable class="mt-[16px]" v-on="propsEvent">
|
||||
<template #caseLevel="{ record }">
|
||||
<caseLevel :case-level="record.caseLevel" />
|
||||
<caseLevel :case-level="(getCaseLevel(record) as CaseLevel)" />
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<div class="footer">
|
||||
|
@ -103,13 +107,13 @@
|
|||
</div>
|
||||
<div class="flex items-center">
|
||||
<slot name="footerRight">
|
||||
<a-button type="secondary" :disabled="loading" class="mr-[12px]" @click="cancel">
|
||||
<a-button type="secondary" :disabled="props.confirmLoading" class="mr-[12px]" @click="cancel">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="propsRes.selectedKeys.size === 0 || props.okButtonDisabled"
|
||||
:loading="props.confirmLoading"
|
||||
:disabled="propsRes.selectedKeys.size === 0"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
{{ t('ms.case.associate.associate') }}
|
||||
|
@ -123,11 +127,10 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import { FilterFormItem } from '@/components/pure/ms-advance-filter/type';
|
||||
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import { FilterFormItem, FilterType } from '@/components/pure/ms-advance-filter/type';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
|
@ -138,100 +141,52 @@
|
|||
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||
import caseLevel from './caseLevel.vue';
|
||||
|
||||
import { getCustomFieldsTable } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { mapTree } from '@/utils';
|
||||
|
||||
import type { CaseManagementTable, CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
|
||||
import type { CommonList, TableQueryParams } from '@/models/common';
|
||||
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
||||
|
||||
import type { CaseLevel } from './types';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
project: string;
|
||||
getModulesFunc: (params: any) => Promise<ModuleTreeNode[]>;
|
||||
modulesCount?: Record<string, number>; // 模块数量统计对象
|
||||
getModulesFunc: (projectId: string) => Promise<ModuleTreeNode[]>; // 获取模块树请求
|
||||
getTableFunc: (params: TableQueryParams) => Promise<CommonList<CaseManagementTable>>; // 获取表
|
||||
tableParams?: TableQueryParams; // 查询表格的额外的参数
|
||||
modulesCount: Record<string, number>; // 模块数量统计对象
|
||||
okButtonDisabled?: boolean; // 确认按钮是否禁用
|
||||
selectedKeys?: string[]; // 已选中的用例id
|
||||
currentSelectCase: string | number | Record<string, any> | undefined; // 当前选中的用例类型
|
||||
moduleOptions?: { label: string; value: string }[]; // 功能模块对应用例下拉
|
||||
confirmLoading: boolean;
|
||||
associatedIds: string[]; // 已关联用例id集合用于去重已关联
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'update:project', val: string): void;
|
||||
(e: 'init', val: string[]): void;
|
||||
(e: 'folderNodeSelect', ids: (string | number)[], springIds: string[]): void;
|
||||
(e: 'success', val: string[]): void;
|
||||
(e: 'update:currentSelectCase', val: string | number | Record<string, any> | undefined): void;
|
||||
(e: 'init', val: CaseModuleQueryParams): void; // 初始化模块数量
|
||||
(e: 'close'): void;
|
||||
(e: 'save', params: TableQueryParams): void; // 保存对外传递关联table 相关参数
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const virtualListProps = computed(() => {
|
||||
return {
|
||||
height: 'calc(100vh - 251px)',
|
||||
};
|
||||
});
|
||||
|
||||
const innerVisible = ref(props.visible);
|
||||
const innerProject = ref(props.project);
|
||||
|
||||
// 协议类型
|
||||
const protocolType = ref('HTTP');
|
||||
const caseType = ref('API');
|
||||
|
||||
const protocolOptions = ref(['DUBBO', 'HTTP', 'TCP', 'SQL']);
|
||||
const actionType = ref([
|
||||
{
|
||||
value: 'API',
|
||||
name: t('caseManagement.featureCase.apiCase'),
|
||||
},
|
||||
{
|
||||
value: 'SCENE',
|
||||
name: t('caseManagement.featureCase.sceneCase'),
|
||||
},
|
||||
{
|
||||
value: 'UI',
|
||||
name: t('caseManagement.featureCase.uiCase'),
|
||||
},
|
||||
{
|
||||
value: 'PERFORMANCE',
|
||||
name: t('caseManagement.featureCase.propertyCase'),
|
||||
},
|
||||
]);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
innerVisible.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerVisible.value,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
emit('update:visible', false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.project,
|
||||
(val) => {
|
||||
innerProject.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerProject.value,
|
||||
(val) => {
|
||||
emit('update:project', val);
|
||||
}
|
||||
);
|
||||
|
||||
const activeFolder = ref('all');
|
||||
const activeFolderName = ref(t('ms.case.associate.allCase'));
|
||||
const allCaseCount = ref(0);
|
||||
const filterRowCount = ref(0);
|
||||
const filterConfigList = ref<FilterFormItem[]>([]);
|
||||
|
||||
function getFolderClass(id: string) {
|
||||
return activeFolder.value === id ? 'folder-text folder-text--active' : 'folder-text';
|
||||
|
@ -247,9 +202,14 @@
|
|||
activeFolder.value = id;
|
||||
activeFolderName.value = t('ms.case.associate.allCase');
|
||||
selectedModuleKeys.value = [];
|
||||
emit('folderNodeSelect', [id], []);
|
||||
}
|
||||
|
||||
const innerVisible = ref(props.visible);
|
||||
const innerProject = ref(props.project);
|
||||
|
||||
const protocolType = ref('HTTP'); // 协议类型
|
||||
const protocolOptions = ref(['HTTP']);
|
||||
|
||||
/**
|
||||
* 初始化模块树
|
||||
* @param isSetDefaultKey 是否设置第一个节点为选中节点
|
||||
|
@ -257,8 +217,16 @@
|
|||
async function initModules(isSetDefaultKey = false) {
|
||||
try {
|
||||
moduleLoading.value = true;
|
||||
const res = await props.getModulesFunc(appStore.currentProjectId);
|
||||
folderTree.value = res;
|
||||
const res = await props.getModulesFunc(innerProject.value);
|
||||
folderTree.value = mapTree<ModuleTreeNode>(res, (e) => {
|
||||
return {
|
||||
...e,
|
||||
hideMoreAction: e.id === 'root',
|
||||
draggable: false,
|
||||
disabled: false,
|
||||
count: props.modulesCount?.[e.id] || 0,
|
||||
};
|
||||
});
|
||||
if (isSetDefaultKey) {
|
||||
selectedModuleKeys.value = [folderTree.value[0].id];
|
||||
activeFolderName.value = folderTree.value[0].name;
|
||||
|
@ -267,13 +235,7 @@
|
|||
offspringIds.push(e.id);
|
||||
return e;
|
||||
});
|
||||
|
||||
emit('folderNodeSelect', selectedModuleKeys.value, offspringIds);
|
||||
}
|
||||
emit(
|
||||
'init',
|
||||
folderTree.value.map((e) => e.name)
|
||||
);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
@ -283,40 +245,31 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* 处理文件夹树节点选中事件
|
||||
* 处理模块树节点选中事件
|
||||
*/
|
||||
const offspringIds = ref<string[]>([]);
|
||||
|
||||
function folderNodeSelect(_selectedKeys: (string | number)[], node: MsTreeNodeData) {
|
||||
selectedModuleKeys.value = _selectedKeys as string[];
|
||||
activeFolder.value = node.id;
|
||||
activeFolderName.value = node.name;
|
||||
const offspringIds: string[] = [];
|
||||
offspringIds.value = [];
|
||||
mapTree(node.children || [], (e) => {
|
||||
offspringIds.push(e.id);
|
||||
offspringIds.value.push(e.id);
|
||||
return e;
|
||||
});
|
||||
|
||||
emit('folderNodeSelect', _selectedKeys, offspringIds);
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initModules();
|
||||
// 选中用例类型
|
||||
const caseType = computed({
|
||||
get() {
|
||||
return props.currentSelectCase;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:currentSelectCase', val);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化模块资源数量
|
||||
*/
|
||||
watch(
|
||||
() => props.modulesCount,
|
||||
(obj) => {
|
||||
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
||||
return {
|
||||
...node,
|
||||
count: obj?.[node.id] || 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const keyword = ref('');
|
||||
const version = ref('');
|
||||
const versionOptions = ref([
|
||||
|
@ -343,7 +296,7 @@
|
|||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
width: 90,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'ms.case.associate.caseName',
|
||||
|
@ -352,7 +305,7 @@
|
|||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
title: 'ms.case.associate.caseLevel',
|
||||
|
@ -363,137 +316,207 @@
|
|||
{
|
||||
title: 'ms.case.associate.version',
|
||||
slotName: 'version',
|
||||
width: 80,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'ms.case.associate.tags',
|
||||
dataIndex: 'tags',
|
||||
isTag: true,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateUser',
|
||||
slotName: 'createUser',
|
||||
dataIndex: 'createUser',
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateTime',
|
||||
slotName: 'createTime',
|
||||
dataIndex: 'createTime',
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
list: [
|
||||
{
|
||||
id: 'ded3d43',
|
||||
name: '测试评审1',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 0, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 100,
|
||||
passCount: 0,
|
||||
failCount: 10,
|
||||
reviewCount: 20,
|
||||
reviewingCount: 25,
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
{
|
||||
id: 'g545hj4',
|
||||
name: '测试评审2',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 1, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 105,
|
||||
passCount: 50,
|
||||
failCount: 10,
|
||||
reviewCount: 20,
|
||||
reviewingCount: 25,
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
{
|
||||
id: 'hj65b54',
|
||||
name: '测试评审3',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 2, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 125,
|
||||
passCount: 70,
|
||||
failCount: 10,
|
||||
reviewCount: 20,
|
||||
reviewingCount: 25,
|
||||
passRate: '80%',
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
{
|
||||
id: 'wefwefw',
|
||||
name: '测试评审4',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 3, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 130,
|
||||
passCount: 70,
|
||||
failCount: 10,
|
||||
reviewCount: 0,
|
||||
reviewingCount: 50,
|
||||
passRate: '80%',
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
],
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 2,
|
||||
}),
|
||||
props.getTableFunc,
|
||||
{
|
||||
columns,
|
||||
scroll: {
|
||||
x: '100%',
|
||||
},
|
||||
showSetting: false,
|
||||
selectable: true,
|
||||
showSelectAll: true,
|
||||
},
|
||||
(item) => {
|
||||
(record) => {
|
||||
return {
|
||||
...item,
|
||||
tags: item.tags?.map((e: string) => ({ id: e, name: e })) || [],
|
||||
...record,
|
||||
tags: (JSON.parse(record.tags) || []).map((item: string, i: number) => {
|
||||
return {
|
||||
id: `${record.id}-${i}`,
|
||||
name: item,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function searchCase() {
|
||||
setLoadListParams({
|
||||
version: version.value,
|
||||
keyword: keyword.value,
|
||||
});
|
||||
loadList();
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
searchCase();
|
||||
const searchParams = ref<TableQueryParams>({
|
||||
moduleIds: [],
|
||||
version: version.value,
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
loading.value = true;
|
||||
Message.success(t('ms.case.associate.associateSuccess'));
|
||||
innerVisible.value = false;
|
||||
emit('success', Array.from(propsRes.value.selectedKeys));
|
||||
resetSelector();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
function getLoadListParams() {
|
||||
if (activeFolder.value === 'all') {
|
||||
searchParams.value.moduleIds = [];
|
||||
} else {
|
||||
searchParams.value.moduleIds = [activeFolder.value, ...offspringIds.value];
|
||||
}
|
||||
setLoadListParams({
|
||||
...searchParams.value,
|
||||
...props.tableParams,
|
||||
keyword: keyword.value,
|
||||
projectId: innerProject.value,
|
||||
excludeIds: [...props.associatedIds],
|
||||
});
|
||||
}
|
||||
|
||||
const combine = ref<Record<string, any>>({});
|
||||
const searchCustomFields = ref<FilterFormItem[]>([]);
|
||||
const filterConfigList = ref<FilterFormItem[]>([]);
|
||||
|
||||
async function initFilter() {
|
||||
const result = await getCustomFieldsTable(appStore.currentProjectId);
|
||||
filterConfigList.value = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'id',
|
||||
type: FilterType.INPUT,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnName',
|
||||
dataIndex: 'name',
|
||||
type: FilterType.INPUT,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnModule',
|
||||
dataIndex: 'moduleId',
|
||||
type: FilterType.TREE_SELECT,
|
||||
treeSelectData: folderTree.value,
|
||||
treeSelectProps: {
|
||||
fieldNames: {
|
||||
title: 'name',
|
||||
key: 'id',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnVersion',
|
||||
dataIndex: 'versionId',
|
||||
type: FilterType.INPUT,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateUser',
|
||||
dataIndex: 'createUser',
|
||||
type: FilterType.SELECT,
|
||||
selectProps: {
|
||||
mode: 'static',
|
||||
options: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateTime',
|
||||
dataIndex: 'createTime',
|
||||
type: FilterType.DATE_PICKER,
|
||||
},
|
||||
{
|
||||
title: 'bugManagement.createTime',
|
||||
dataIndex: 'createTime',
|
||||
type: FilterType.DATE_PICKER,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnUpdateUser',
|
||||
dataIndex: 'updateUser',
|
||||
type: FilterType.SELECT,
|
||||
selectProps: {
|
||||
mode: 'static',
|
||||
options: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnUpdateTime',
|
||||
dataIndex: 'updateTime',
|
||||
type: FilterType.DATE_PICKER,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnTag',
|
||||
dataIndex: 'tags',
|
||||
type: FilterType.TAGS_INPUT,
|
||||
},
|
||||
];
|
||||
// 处理系统自定义字段
|
||||
searchCustomFields.value = result.map((item: any) => {
|
||||
const FilterTypeKey: keyof typeof FilterType = CustomTypeMaps[item.type].type;
|
||||
const formType = FilterType[FilterTypeKey];
|
||||
const formObject = CustomTypeMaps[item.type];
|
||||
const { props: formProps } = formObject;
|
||||
const currentItem: any = {
|
||||
title: item.name,
|
||||
dataIndex: item.id,
|
||||
type: formType,
|
||||
};
|
||||
|
||||
if (formObject.propsKey && formProps.options) {
|
||||
formProps.options = item.options;
|
||||
currentItem[formObject.propsKey] = {
|
||||
...formProps,
|
||||
};
|
||||
}
|
||||
return currentItem;
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化模块数量
|
||||
function initModuleCount() {
|
||||
emit('init', {
|
||||
keyword: keyword.value,
|
||||
moduleIds: [],
|
||||
projectId: innerProject.value,
|
||||
current: propsRes.value.msPagination?.current,
|
||||
pageSize: propsRes.value.msPagination?.pageSize,
|
||||
combine: combine.value,
|
||||
});
|
||||
}
|
||||
|
||||
function searchCase() {
|
||||
getLoadListParams();
|
||||
loadList();
|
||||
initModuleCount();
|
||||
}
|
||||
|
||||
// 保存参数
|
||||
function handleConfirm() {
|
||||
const { excludeKeys, selectedKeys, selectorStatus } = propsRes.value;
|
||||
const { versionId, moduleIds } = searchParams.value;
|
||||
const params = {
|
||||
excludeIds: [...excludeKeys],
|
||||
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
|
||||
selectAll: selectorStatus === 'all',
|
||||
moduleIds,
|
||||
versionId,
|
||||
refId: '',
|
||||
projectId: innerProject.value,
|
||||
};
|
||||
emit('save', params);
|
||||
}
|
||||
|
||||
// 用例等级
|
||||
function getCaseLevel(record: CaseManagementTable) {
|
||||
const caseLevelRes = record.customFields.find((item: any) => item.name === '用例等级');
|
||||
if (caseLevelRes) {
|
||||
return JSON.parse(caseLevelRes.value).replaceAll('P', '') * 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
|
@ -502,6 +525,82 @@
|
|||
emit('close');
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
innerVisible.value = val;
|
||||
if (val) {
|
||||
searchCase();
|
||||
initFilter();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerVisible.value,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
emit('update:visible', false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 用例类型改变
|
||||
watch(
|
||||
() => caseType.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
initModules(true);
|
||||
searchCase();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.project,
|
||||
(val) => {
|
||||
if (val) {
|
||||
innerProject.value = val;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerProject.value,
|
||||
(val) => {
|
||||
emit('update:project', val);
|
||||
resetSelector();
|
||||
initModules(true);
|
||||
searchCase();
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => activeFolder.value,
|
||||
() => {
|
||||
searchCase();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 初始化模块数量
|
||||
*/
|
||||
watch(
|
||||
() => props.modulesCount,
|
||||
(obj) => {
|
||||
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
||||
return {
|
||||
...node,
|
||||
count: obj?.[node.id] || 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
innerProject.value = appStore.currentProjectId;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
initModules,
|
||||
});
|
||||
|
|
|
@ -29,9 +29,12 @@
|
|||
</a-avatar>
|
||||
</template>
|
||||
<template #title>
|
||||
<a-tooltip :content="item.file.name">
|
||||
<div class="one-line-text max-w-[80%] font-normal">{{ item.file.name }}</div>
|
||||
</a-tooltip>
|
||||
<div class="flex items-center">
|
||||
<a-tooltip :content="item.file.name">
|
||||
<div class="one-line-text max-w-[80%] font-normal">{{ item.file.name }}</div>
|
||||
</a-tooltip>
|
||||
<slot name="title" :item="item"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<div v-if="item.status === UploadStatus.init" class="text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
||||
|
@ -83,7 +86,13 @@
|
|||
>
|
||||
{{ t('ms.upload.reUpload') }}
|
||||
</MsButton>
|
||||
<MsButton v-if="props.showDelete" type="button" status="danger" class="!mr-[4px]" @click="deleteFile(item)">
|
||||
<MsButton
|
||||
v-if="props.showDelete"
|
||||
type="button"
|
||||
:status="item.deleteContent ? 'primary' : 'danger'"
|
||||
class="!mr-[4px]"
|
||||
@click="deleteFile(item)"
|
||||
>
|
||||
{{ t(item.deleteContent) || t('ms.upload.delete') }}
|
||||
</MsButton>
|
||||
<slot name="actions" :item="item"></slot>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
:disabled="props.disabled"
|
||||
:class="getAllScreenClass"
|
||||
:style="{
|
||||
width: props.isAllScreen ? `calc(100% - ${menuWidth}px - 16px)` : '100%',
|
||||
width: props.isAllScreen ? `calc(100% - ${menuWidth}px - 16px)` : '',
|
||||
}"
|
||||
@change="handleChange"
|
||||
@before-upload="beforeUpload"
|
||||
|
|
|
@ -26,4 +26,11 @@ export interface BugExportParams extends BatchApiParams {
|
|||
bugExportColumns: BugExportColumn[]; // 导出字段
|
||||
}
|
||||
|
||||
// 获取默认模版缺陷
|
||||
export interface DefaultTemplate {
|
||||
id: string;
|
||||
projectId: string;
|
||||
fromStatusId?: string;
|
||||
platformBugKey?: string;
|
||||
}
|
||||
export default {};
|
||||
|
|
|
@ -71,7 +71,7 @@ export interface CaseManagementTable {
|
|||
updateTime: string;
|
||||
deleteTime: string;
|
||||
steps: string;
|
||||
customFields: CustomAttributes[]; // 自定义字段集合
|
||||
customFields: customFieldsItem[]; // 自定义字段集合
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
@loaded="loadedCase"
|
||||
>
|
||||
<template #titleLeft>
|
||||
<div class="flex items-center"><caseLevel :case-level="(caseLevels as CaseLevel)" /></div>
|
||||
<div class="flex items-center"><caseLevel :case-level="caseLevels" /></div>
|
||||
</template>
|
||||
<template #titleRight="{ loading }">
|
||||
<div class="rightButtons flex items-center">
|
||||
|
@ -118,7 +118,7 @@
|
|||
<TabCaseTable v-else-if="activeTab === 'case'" />
|
||||
<TabDefect v-else-if="activeTab === 'bug'" />
|
||||
<TabDependency v-else-if="activeTab === 'dependency'" />
|
||||
<TabCaseReview v-else-if="activeTab === 'caseReview'" />
|
||||
<TabCaseReview v-else-if="activeTab === 'caseReview'" :case-id="props.detailId" />
|
||||
<TabTestPlan v-else-if="activeTab === 'testPlan'" />
|
||||
<TabComment v-else-if="activeTab === 'comments'" :case-id="props.detailId" />
|
||||
<TabChangeHistory v-else-if="activeTab === 'changeHistory'" />
|
||||
|
@ -296,13 +296,13 @@
|
|||
|
||||
const detailInfo = ref<DetailCase>({ ...initDetail });
|
||||
const customFields = ref<CustomAttributes[]>([]);
|
||||
const caseLevels = ref(0);
|
||||
const caseLevels = ref<CaseLevel>(0);
|
||||
function loadedCase(detail: DetailCase) {
|
||||
detailInfo.value = { ...detail };
|
||||
customFields.value = detailInfo.value.customFields;
|
||||
const caseLevelsValue = customFields.value.find((item) => item.fieldName === '用例等级')?.defaultValue;
|
||||
if (caseLevelsValue) {
|
||||
caseLevels.value = JSON.parse(caseLevelsValue).replaceAll('P', '') * 1;
|
||||
caseLevels.value = (JSON.parse(caseLevelsValue).replaceAll('P', '') * 1) as CaseLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,9 +76,9 @@
|
|||
<!-- 渲染自定义字段开始 -->
|
||||
<template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
|
||||
<div v-if="isCaseLevel(item.slotName as string).name === '用例等级'" class="flex items-center">
|
||||
<span v-if="!record.visible" class="flex items-center" @click="record.visible = true"
|
||||
><caseLevel :case-level="getCaseLevel(record, item)"
|
||||
/></span>
|
||||
<span v-if="!record.visible" class="flex items-center" @click="record.visible = true">
|
||||
<caseLevel :case-level="getCaseLevel(record, item)" />
|
||||
</span>
|
||||
<TableFormChange
|
||||
v-model:visible="record.visible"
|
||||
:default-value="record[item.slotName]"
|
||||
|
@ -583,7 +583,7 @@
|
|||
searchCustomFields.value = result.map((item: any) => {
|
||||
const FilterTypeKey: keyof typeof FilterType = CustomTypeMaps[item.type].type;
|
||||
const formType = FilterType[FilterTypeKey];
|
||||
const formObject = item.type;
|
||||
const formObject = CustomTypeMaps[item.type];
|
||||
const { props: formProps } = formObject;
|
||||
const currentItem: any = {
|
||||
title: item.name,
|
||||
|
@ -960,13 +960,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// const searchList = debounce(() => {
|
||||
// getLoadListParams();
|
||||
// loadList();
|
||||
// }, 100);
|
||||
|
||||
const fetchData = (keywordStr = '') => {
|
||||
console.log(keywordStr);
|
||||
setKeyword(keywordStr);
|
||||
keyword.value = keywordStr;
|
||||
getLoadListParams();
|
||||
|
@ -1090,7 +1084,7 @@
|
|||
}
|
||||
|
||||
function getCaseLevel(record: CaseManagementTable, item: MsTableColumnData): CaseLevel {
|
||||
return ((record[item.slotName as string] || '').replaceAll('P', '') * 1) as CaseLevel;
|
||||
return (record[item.slotName as string].replaceAll('P', '') * 1) as CaseLevel;
|
||||
}
|
||||
|
||||
// 模块树改变回调
|
||||
|
|
|
@ -103,6 +103,15 @@
|
|||
<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"
|
||||
|
@ -114,6 +123,7 @@
|
|||
</MsButton>
|
||||
<TransferModal
|
||||
v-model:visible="transferVisible"
|
||||
:request-fun="transferFileRequest"
|
||||
:params="{
|
||||
projectId: currentProjectId,
|
||||
caseId:route.query.id as string,
|
||||
|
@ -134,6 +144,15 @@
|
|||
</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="route.query.id"
|
||||
type="button"
|
||||
|
@ -143,8 +162,21 @@
|
|||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="route.query.id && 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>
|
||||
</MsFileList>
|
||||
</div>
|
||||
<!-- 文件列表结束 -->
|
||||
|
@ -217,6 +249,7 @@
|
|||
:get-list-request="getAssociatedFileListUrl"
|
||||
@save="saveSelectAssociatedFile"
|
||||
/>
|
||||
<a-image-preview v-model:visible="previewVisible" :src="imageUrl" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -236,11 +269,14 @@
|
|||
import TransferModal from './tabContent/transferModal.vue';
|
||||
|
||||
import {
|
||||
deleteFileOrCancelAssociation,
|
||||
checkFileIsUpdateRequest,
|
||||
downloadFileRequest,
|
||||
getAssociatedFileListUrl,
|
||||
getCaseDefaultFields,
|
||||
getCaseDetail,
|
||||
previewFile,
|
||||
transferFileRequest,
|
||||
updateFile,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||
import { getProjectFieldList } from '@/api/modules/setting/template';
|
||||
|
@ -452,6 +488,32 @@
|
|||
);
|
||||
});
|
||||
|
||||
const imageUrl = ref('');
|
||||
const previewVisible = ref<boolean>(false);
|
||||
|
||||
// 预览图片
|
||||
async function handlePreview(item: MsFileItem) {
|
||||
try {
|
||||
previewVisible.value = true;
|
||||
if (item.status !== 'init') {
|
||||
const res = await previewFile({
|
||||
projectId: currentProjectId.value,
|
||||
caseId: route.query.id as string,
|
||||
fileId: item.uid,
|
||||
local: item.local,
|
||||
});
|
||||
const blob = new Blob([res], { type: 'image/jpeg' });
|
||||
imageUrl.value = URL.createObjectURL(blob);
|
||||
} else {
|
||||
imageUrl.value = item.url as string;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const checkUpdateFileIds = ref<string[]>([]);
|
||||
|
||||
// 处理详情字段
|
||||
function getDetailData(detailResult: DetailCase) {
|
||||
const { customFields, attachments, steps, tags } = detailResult;
|
||||
|
@ -476,13 +538,13 @@
|
|||
}
|
||||
if (attachments) {
|
||||
attachmentsList.value = attachments;
|
||||
|
||||
// 处理文件列表
|
||||
fileList.value = attachments
|
||||
.map((fileInfo: any) => {
|
||||
return {
|
||||
...fileInfo,
|
||||
name: fileInfo.fileName,
|
||||
isUpdateFlag: checkUpdateFileIds.value.includes(fileInfo.id),
|
||||
};
|
||||
})
|
||||
.map((fileInfo: any) => {
|
||||
|
@ -497,6 +559,11 @@
|
|||
isLoading.value = true;
|
||||
await getAllCaseFields();
|
||||
const detailResult: DetailCase = await getCaseDetail(route.query.id as string);
|
||||
const fileIds = (detailResult.attachments || []).map((item: any) => item.id);
|
||||
if (fileIds.length) {
|
||||
checkUpdateFileIds.value = await checkFileIsUpdateRequest(fileIds);
|
||||
}
|
||||
|
||||
getDetailData(detailResult);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
@ -667,6 +734,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 更新文件
|
||||
async function handleUpdateFile(item: MsFileItem) {
|
||||
try {
|
||||
await updateFile(currentProjectId.value, item.associationId);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
caseFormRef,
|
||||
formRef,
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</a-alert>
|
||||
<MsUpload
|
||||
v-model:file-list="fileList"
|
||||
class="mb-6"
|
||||
class="mb-6 w-full"
|
||||
:accept="props.validateType === 'Excel' ? 'excel' : 'xmind'"
|
||||
:max-size="100"
|
||||
size-unit="MB"
|
||||
|
|
|
@ -8,73 +8,39 @@
|
|||
:width="800"
|
||||
unmount-on-close
|
||||
:show-continue="true"
|
||||
@continue="handleDrawerConfirm(true)"
|
||||
@confirm="handleDrawerConfirm"
|
||||
@cancel="handleDrawerCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" layout="vertical">
|
||||
<a-form-item
|
||||
field="name"
|
||||
field="title"
|
||||
:label="t('bugManagement.bugName')"
|
||||
:rules="[{ required: true, message: t('bugManagement.edit.nameIsRequired') }]"
|
||||
:placeholder="t('bugManagement.edit.pleaseInputBugName')"
|
||||
>
|
||||
<a-input v-model="form.name" :max-length="255" show-word-limit />
|
||||
<a-input v-model="form.title" :max-length="255" show-word-limit />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('bugManagement.edit.content')">
|
||||
<MsRichText v-model="form.content" />
|
||||
<MsRichText v-model="form.description" />
|
||||
</a-form-item>
|
||||
<div class="mb-[8px] text-[var(--color-text-1)]">{{ t('bugManagement.edit.file') }}</div>
|
||||
<div>
|
||||
<a-dropdown trigger="hover">
|
||||
<template #content>
|
||||
<MsUpload
|
||||
v-model:file-list="fileList"
|
||||
:auto-upload="false"
|
||||
multiple
|
||||
draggable
|
||||
accept="unknown"
|
||||
is-limit
|
||||
size-unit="MB"
|
||||
:max-size="500"
|
||||
>
|
||||
<a-doption>{{ t('bugManagement.edit.localUpload') }}</a-doption>
|
||||
</MsUpload>
|
||||
<a-doption>{{ t('bugManagement.edit.linkFile') }}</a-doption>
|
||||
</template>
|
||||
<a-button type="outline">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
{{ t('bugManagement.edit.uploadFile') }}
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
<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>
|
||||
</a-form>
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { FileItem } from '@arco-design/web-vue';
|
||||
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
|
||||
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import FileList from '@/components/pure/ms-upload/fileList.vue';
|
||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||
|
||||
import { createBug, getTemplageOption, getTemplateDetailInfo } from '@/api/modules/bug-management/index';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import { TemplateOption } from '@/models/common';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -83,54 +49,20 @@
|
|||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
const fileList = ref<FileItem[]>([]);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const form = ref({
|
||||
name: '',
|
||||
content: '',
|
||||
const templateOptions = ref<TemplateOption[]>([]);
|
||||
|
||||
// TODO缺陷类型
|
||||
const initForm: any = {
|
||||
title: '',
|
||||
templateId: '',
|
||||
handleMan: [],
|
||||
status: '',
|
||||
severity: '',
|
||||
tag: [],
|
||||
});
|
||||
|
||||
// 上传文件
|
||||
const uploadFile = (file: File) => {
|
||||
const fileItem: FileItem = {
|
||||
uid: `${Date.now()}`,
|
||||
name: file.name,
|
||||
status: 'init',
|
||||
file,
|
||||
};
|
||||
fileList.value.push(fileItem);
|
||||
return Promise.resolve(fileItem);
|
||||
projectId: appStore.currentProjectId,
|
||||
description: '',
|
||||
customFields: [],
|
||||
};
|
||||
|
||||
// 删除文件
|
||||
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',
|
||||
};
|
||||
}
|
||||
return e;
|
||||
});
|
||||
};
|
||||
|
||||
// 预览文件
|
||||
const handlePreview = (item: FileItem) => {
|
||||
const { url } = item;
|
||||
window.open(url);
|
||||
};
|
||||
const form = ref({ ...initForm });
|
||||
|
||||
const showDrawer = computed({
|
||||
get() {
|
||||
|
@ -141,10 +73,49 @@
|
|||
},
|
||||
});
|
||||
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const templateCustomFields = ref([]);
|
||||
function handleDrawerCancel() {
|
||||
formRef.value?.resetFields();
|
||||
form.value = { ...initForm };
|
||||
showDrawer.value = false;
|
||||
}
|
||||
|
||||
const drawerLoading = ref<boolean>(false);
|
||||
|
||||
function handleDrawerConfirm() {}
|
||||
function handleDrawerCancel() {}
|
||||
function handleDrawerConfirm(isContinue: boolean) {
|
||||
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||
if (!errors) {
|
||||
drawerLoading.value = true;
|
||||
try {
|
||||
await createBug({ request: { ...form.value, customFields: templateCustomFields.value }, fileList: [] });
|
||||
Message.success(t('caseManagement.featureCase.quicklyCreateDefectSuccess'));
|
||||
if (!isContinue) {
|
||||
handleDrawerCancel();
|
||||
}
|
||||
form.value = { ...initForm };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
drawerLoading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
templateOptions.value = await getTemplageOption({ projectId: appStore.currentProjectId });
|
||||
form.value.templateId = templateOptions.value.find((item) => item.enableDefault)?.id as string;
|
||||
const result = await getTemplateDetailInfo({ id: form.value.templateId, projectId: appStore.currentProjectId });
|
||||
templateCustomFields.value = result.customFields.map((item: any) => {
|
||||
return {
|
||||
id: item.fieldId,
|
||||
name: item.fieldName,
|
||||
type: item.type,
|
||||
value: item.defaultValue || '',
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
getFetch();
|
||||
// getFetch();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -34,9 +34,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<ms-base-table v-if="showType === 'link'" ref="tableRef" v-bind="linkPropsRes" v-on="linkTableEvent">
|
||||
<template #defectName="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
<template #title="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.title }}</span>
|
||||
<a-popover title="" position="right">
|
||||
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
<template #content>
|
||||
<div class="min-w-[300px] text-[14px] text-[var(--color-text-1)]">
|
||||
{{ record.title }}
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||
|
@ -56,7 +63,7 @@
|
|||
</ms-base-table>
|
||||
<ms-base-table v-else v-bind="testPlanPropsRes" v-on="testPlanTableEvent">
|
||||
<template #defectName="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||
<span class="one-line-text max-w[300px]"> {{ record.title }}</span
|
||||
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
|
@ -90,11 +97,13 @@
|
|||
import AddDefectDrawer from './addDefectDrawer.vue';
|
||||
import LinkDefectDrawer from './linkDefectDrawer.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { getBugList } from '@/api/modules/bug-management/index';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const showType = ref('link');
|
||||
|
@ -113,10 +122,10 @@
|
|||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectName',
|
||||
slotName: 'defectName',
|
||||
dataIndex: 'defectName',
|
||||
slotName: 'title',
|
||||
dataIndex: 'title',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
showTooltip: false,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
|
@ -141,15 +150,6 @@
|
|||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnLevel',
|
||||
dataIndex: 'level',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
|
@ -165,7 +165,7 @@
|
|||
propsEvent: linkTableEvent,
|
||||
loadList: loadLinkList,
|
||||
setLoadListParams: setLinkListParams,
|
||||
} = useTable(getRecycleListRequest, {
|
||||
} = useTable(getBugList, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEFECT,
|
||||
scroll: { x: '100%' },
|
||||
|
@ -194,7 +194,7 @@
|
|||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.testPlan',
|
||||
title: 'caseManagement.featureCase.planName',
|
||||
slotName: 'testPlan',
|
||||
dataIndex: 'testPlan',
|
||||
showInTable: true,
|
||||
|
@ -229,7 +229,7 @@
|
|||
propsEvent: testPlanTableEvent,
|
||||
loadList: testPlanLinkList,
|
||||
setLoadListParams: setTestPlanListParams,
|
||||
} = useTable(getRecycleListRequest, {
|
||||
} = useTable(getBugList, {
|
||||
columns: testPlanColumns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEFECT_TEST_PLAN,
|
||||
scroll: { x: '100%' },
|
||||
|
@ -239,10 +239,10 @@
|
|||
|
||||
function getFetch() {
|
||||
if (showType.value === 'link') {
|
||||
setLinkListParams({ keyword: keyword.value });
|
||||
setLinkListParams({ keyword: keyword.value, projectId: appStore.currentProjectId });
|
||||
loadLinkList();
|
||||
} else {
|
||||
setTestPlanListParams({ keyword: keyword.value });
|
||||
setTestPlanListParams({ keyword: keyword.value, projectId: appStore.currentProjectId });
|
||||
testPlanLinkList();
|
||||
}
|
||||
}
|
||||
|
@ -259,6 +259,7 @@
|
|||
function linkDefect() {
|
||||
showLinkDrawer.value = true;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => showType.value,
|
||||
(val) => {
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
<a-dropdown @select="handleSelect">
|
||||
<a-button type="primary"> {{ t('caseManagement.featureCase.linkCase') }} </a-button>
|
||||
<template #content>
|
||||
<a-doption v-for="item of caseType" :key="item.value" :value="item.value">{{ item.name }}</a-doption>
|
||||
<a-doption v-for="item of caseTypeOptions" :key="item.value" :value="item.value">{{
|
||||
t(item.label)
|
||||
}}</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-input-search
|
||||
|
@ -26,10 +28,17 @@
|
|||
<MsCaseAssociate
|
||||
v-model:visible="innerVisible"
|
||||
v-model:project="innerProject"
|
||||
v-model:currentSelectCase="currentSelectCase"
|
||||
:ok-button-disabled="associateForm.reviewers.length === 0"
|
||||
:get-modules-func="getCaseModuleTree"
|
||||
@success="writeAssociateCases"
|
||||
:get-table-func="getCaseList"
|
||||
:modules-count="modulesCount"
|
||||
:module-options="caseTypeOptions"
|
||||
:confirm-loading="confirmLoading"
|
||||
:associated-ids="associatedIds"
|
||||
@close="emit('close')"
|
||||
@init="getModuleCount"
|
||||
@save="saveHandler"
|
||||
>
|
||||
</MsCaseAssociate>
|
||||
</div>
|
||||
|
@ -44,13 +53,27 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsCaseAssociate from '@/components/business/ms-case-associate/index.vue';
|
||||
|
||||
import { getCaseModuleTree, getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { getAssociatedIds } from '@/api/modules/case-management/caseReview';
|
||||
import {
|
||||
getCaseList,
|
||||
getCaseModulesCounts,
|
||||
getCaseModuleTree,
|
||||
getRecycleListRequest,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
import { postTabletList } from '@/api/modules/project-management/menuManagement';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import type { CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'update:project', val: string): void;
|
||||
|
@ -135,38 +158,74 @@
|
|||
reviewers: [],
|
||||
});
|
||||
|
||||
const associatedIds = ref<string[]>([]);
|
||||
|
||||
async function getLinkedIds() {
|
||||
// try {
|
||||
// associatedIds.value = await getAssociatedIds('1111');
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
}
|
||||
|
||||
const currentSelectCase = ref<string | number | Record<string, any> | undefined>('');
|
||||
function handleSelect(value: string | number | Record<string, any> | undefined) {
|
||||
currentSelectCase.value = value;
|
||||
innerVisible.value = true;
|
||||
getLinkedIds();
|
||||
}
|
||||
|
||||
function cancelLink(record: any) {}
|
||||
|
||||
const caseType = ref([
|
||||
{
|
||||
value: 'API',
|
||||
name: '接口用例',
|
||||
},
|
||||
{
|
||||
value: 'SCENE',
|
||||
name: '接口用例',
|
||||
},
|
||||
{
|
||||
value: 'UI',
|
||||
name: 'UI用例',
|
||||
},
|
||||
{
|
||||
value: 'PERFORMANCE',
|
||||
name: '性能用例',
|
||||
},
|
||||
]);
|
||||
const caseTypeOptions = ref<{ label: string; value: string }[]>([]);
|
||||
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const modulesCount = ref<Record<string, any>>({});
|
||||
|
||||
function writeAssociateCases(ids: string[]) {
|
||||
emit('success', ids);
|
||||
async function getModuleCount(params: CaseModuleQueryParams) {
|
||||
try {
|
||||
modulesCount.value = await getCaseModulesCounts(params);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
|
||||
function saveHandler(params: TableQueryParams) {}
|
||||
|
||||
const moduleMaps: Record<string, { label: string; value: string }[]> = {
|
||||
apiTest: [
|
||||
{
|
||||
value: 'API_CASE',
|
||||
label: t('caseManagement.featureCase.apiCase'),
|
||||
},
|
||||
{
|
||||
value: 'SCENE_CASE',
|
||||
label: t('caseManagement.featureCase.sceneCase'),
|
||||
},
|
||||
],
|
||||
uiTest: [
|
||||
{
|
||||
value: 'UI_CASE',
|
||||
label: t('caseManagement.featureCase.uiCase'),
|
||||
},
|
||||
],
|
||||
loadTest: [
|
||||
{
|
||||
value: 'LOAD_CASE',
|
||||
label: t('caseManagement.featureCase.propertyCase'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const result = await postTabletList({ projectId: currentProjectId.value });
|
||||
const caseArr = result.filter((item) => Object.keys(moduleMaps).includes(item.module));
|
||||
caseArr.forEach((item: any) => {
|
||||
const currentModule = moduleMaps[item.module];
|
||||
caseTypeOptions.value.push(...currentModule);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
@search="searchList"
|
||||
@press-enter="searchList"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" v-on="propsEvent">
|
||||
|
@ -28,13 +30,19 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { getDetailCaseReviewPage } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
import debounce from 'lodash-es/debounce';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
caseId: string; // 用例id
|
||||
}>();
|
||||
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
|
@ -74,13 +82,26 @@
|
|||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getRecycleListRequest, {
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getDetailCaseReviewPage, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_REVIEW,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
function initData() {
|
||||
setLoadListParams({ keyword: keyword.value, caseId: props.caseId });
|
||||
loadList();
|
||||
}
|
||||
|
||||
const searchList = debounce(() => {
|
||||
initData();
|
||||
}, 100);
|
||||
|
||||
onBeforeMount(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -129,17 +129,26 @@
|
|||
:upload-func="uploadOrAssociationFile"
|
||||
:handle-delete="deleteFileHandler"
|
||||
:show-delete="props.allowEdit"
|
||||
:handle-view="handlePreview"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
<div v-if="props.allowEdit">
|
||||
<!-- 本地文件 -->
|
||||
<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 type="button" status="primary" class="!mr-[4px]" @click="transferVisible = true">
|
||||
{{ t('caseManagement.featureCase.storage') }}
|
||||
</MsButton>
|
||||
<TransferModal
|
||||
v-model:visible="transferVisible"
|
||||
:request-fun="transferFileRequest"
|
||||
:params="{
|
||||
projectId: currentProjectId,
|
||||
caseId: detailForm.id,
|
||||
|
@ -160,6 +169,15 @@
|
|||
</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="item.status === 'done'"
|
||||
type="button"
|
||||
|
@ -169,9 +187,23 @@
|
|||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="item.isUpdateFlag"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="handleUpdateFile(item)"
|
||||
>
|
||||
{{ t('common.update') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</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>
|
||||
</MsFileList>
|
||||
</div>
|
||||
<LinkFileDrawer
|
||||
|
@ -182,6 +214,7 @@
|
|||
@save="saveSelectAssociatedFile"
|
||||
/>
|
||||
</div>
|
||||
<a-image-preview v-model:visible="previewVisible" :src="imageUrl" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -197,11 +230,14 @@
|
|||
import TransferModal from './transferModal.vue';
|
||||
|
||||
import {
|
||||
checkFileIsUpdateRequest,
|
||||
deleteFileOrCancelAssociation,
|
||||
downloadFileRequest,
|
||||
getAssociatedFileListUrl,
|
||||
previewFile,
|
||||
transferFileRequest,
|
||||
updateCaseRequest,
|
||||
updateFile,
|
||||
uploadOrAssociationFile,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||
|
@ -455,9 +491,19 @@
|
|||
console.log(error);
|
||||
}
|
||||
}
|
||||
const checkUpdateFileIds = ref<string[]>([]);
|
||||
|
||||
// 检测更新文件
|
||||
async function getCheckFileIds(fileIds: string[]) {
|
||||
try {
|
||||
checkUpdateFileIds.value = await checkFileIsUpdateRequest(fileIds);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取详情
|
||||
function getDetails() {
|
||||
async function getDetails() {
|
||||
const { steps, attachments } = detailForm.value;
|
||||
if (steps) {
|
||||
stepData.value = JSON.parse(steps).map((item: any) => {
|
||||
|
@ -467,6 +513,11 @@
|
|||
};
|
||||
});
|
||||
}
|
||||
const fileIds = (attachments || []).map((item: any) => item.id);
|
||||
if (fileIds.length) {
|
||||
await getCheckFileIds(fileIds);
|
||||
}
|
||||
|
||||
attachmentsList.value = attachments || [];
|
||||
// 处理文件列表
|
||||
fileList.value = (attachments || [])
|
||||
|
@ -474,6 +525,7 @@
|
|||
return {
|
||||
...fileInfo,
|
||||
name: fileInfo.fileName,
|
||||
isUpdateFlag: checkUpdateFileIds.value.includes(fileInfo.id),
|
||||
};
|
||||
})
|
||||
.map((fileInfo: any) => {
|
||||
|
@ -481,14 +533,23 @@
|
|||
});
|
||||
}
|
||||
|
||||
// TOTO接口需要调整
|
||||
const imageUrl = ref('');
|
||||
const previewVisible = ref<boolean>(false);
|
||||
// 预览
|
||||
async function handlePreview(item: MsFileItem) {
|
||||
const res = await previewFile({
|
||||
projectId: currentProjectId.value,
|
||||
caseId: detailForm.value.id,
|
||||
fileId: item.uid,
|
||||
local: item.local,
|
||||
});
|
||||
try {
|
||||
previewVisible.value = true;
|
||||
const res = await previewFile({
|
||||
projectId: currentProjectId.value,
|
||||
caseId: detailForm.value.id,
|
||||
fileId: item.uid,
|
||||
local: item.local,
|
||||
});
|
||||
const blob = new Blob([res], { type: 'image/jpeg' });
|
||||
imageUrl.value = URL.createObjectURL(blob);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
|
@ -546,6 +607,16 @@
|
|||
fileList.value.push(...fileResultList);
|
||||
}
|
||||
|
||||
// 更新文件
|
||||
async function handleUpdateFile(item: MsFileItem) {
|
||||
try {
|
||||
await updateFile(currentProjectId.value, item.associationId);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
detailForm.value = { ...props.form };
|
||||
getDetails();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-modal v-model:visible="transferVisible" title-align="start" class="ms-modal-upload ms-modal-small">
|
||||
<template #title> 请选择转存目录 </template>
|
||||
<template #title> {{ t('caseManagement.featureCase.selectTransferDirectory') }} </template>
|
||||
<a-tree-select
|
||||
v-model="transferId"
|
||||
:data="transCategory"
|
||||
|
@ -31,7 +31,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { getTransferFileTree, transferFileRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { getTransferFileTree } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
|||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
params: OperationFile; // 转存文件参数
|
||||
requestFun: (params: OperationFile) => Promise<any>;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -77,7 +78,7 @@
|
|||
async function handleBeforeOk() {
|
||||
loading.value = true;
|
||||
try {
|
||||
await transferFileRequest({ ...requestParams.value, moduleId: transferId.value });
|
||||
await props.requestFun({ ...requestParams.value, moduleId: transferId.value });
|
||||
Message.success(t('caseManagement.featureCase.transferFileSuccess'));
|
||||
handleCancel();
|
||||
emit('success');
|
||||
|
|
|
@ -102,21 +102,23 @@ export function convertToFile(fileInfo: AssociatedList): MsFileItem {
|
|||
const gatewayAddress = `${window.location.protocol}//${window.location.hostname}:${window.location.port}`;
|
||||
const fileName = fileInfo.fileType ? `${fileInfo.name}.${fileInfo.fileType || ''}` : `${fileInfo.name}`;
|
||||
const type = fileName.split('.')[1];
|
||||
const isImage = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg'].some((ext) => ext === type.toLowerCase());
|
||||
const file = new File([new Blob()], `${fileName}`, {
|
||||
type: isImage ? `image/${type}` : `application/${type}`,
|
||||
type: `application/${type}`,
|
||||
});
|
||||
Object.defineProperty(file, 'size', { value: fileInfo.size });
|
||||
const { id, local, isUpdateFlag, associateId } = fileInfo;
|
||||
return {
|
||||
enable: fileInfo.enable || false,
|
||||
file,
|
||||
name: fileName,
|
||||
percent: 0,
|
||||
status: 'done',
|
||||
uid: fileInfo.id,
|
||||
uid: id,
|
||||
url: `${gatewayAddress}/${fileInfo.filePath || ''}`,
|
||||
local: fileInfo.local,
|
||||
deleteContent: fileInfo.local ? '' : 'caseManagement.featureCase.cancelLink',
|
||||
local,
|
||||
deleteContent: local ? '' : 'caseManagement.featureCase.cancelLink',
|
||||
isUpdateFlag,
|
||||
associateId,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@ export default {
|
|||
'caseManagement.featureCase.dependency': 'dependencies',
|
||||
'caseManagement.featureCase.caseReview': 'case review',
|
||||
'caseManagement.featureCase.testPlan': 'Test plan',
|
||||
'caseManagement.featureCase.planName': 'Plan name',
|
||||
'caseManagement.featureCase.comments': 'comments',
|
||||
'caseManagement.featureCase.changeHistory': 'Change history',
|
||||
'caseManagement.featureCase.demandPlatform': 'Platform',
|
||||
|
@ -158,7 +159,7 @@ export default {
|
|||
'caseManagement.featureCase.transferFileSuccess': 'Successful transfer',
|
||||
'caseManagement.featureCase.defectName': 'Defect name',
|
||||
'caseManagement.featureCase.defectState': 'Defect state',
|
||||
'caseManagement.featureCase.createDefect': 'Create defect',
|
||||
'caseManagement.featureCase.createDefect': 'Quickly Create defect',
|
||||
'caseManagement.featureCase.testPlanLinkList': 'Test plan association list',
|
||||
'caseManagement.featureCase.directLink': 'Direct correlation',
|
||||
'caseManagement.featureCase.linkDefect': 'Associated defect',
|
||||
|
@ -240,4 +241,7 @@ export default {
|
|||
'caseManagement.featureCase.CheckSuccess': 'Check success',
|
||||
'caseManagement.featureCase.tableNoData': 'No data available',
|
||||
'caseManagement.featureCase.noAssociatedDefect': 'No associated defects, please',
|
||||
'caseManagement.featureCase.fileIsUpdated': 'File is updated',
|
||||
'caseManagement.featureCase.selectTransferDirectory': 'Please select the transfer directory',
|
||||
'caseManagement.featureCase.quicklyCreateDefectSuccess': 'Quick bug creation success',
|
||||
};
|
||||
|
|
|
@ -134,6 +134,7 @@ export default {
|
|||
'caseManagement.featureCase.dependency': '依赖关系',
|
||||
'caseManagement.featureCase.caseReview': '用例评审',
|
||||
'caseManagement.featureCase.testPlan': '测试计划',
|
||||
'caseManagement.featureCase.planName': '计划名称',
|
||||
'caseManagement.featureCase.comments': '评论',
|
||||
'caseManagement.featureCase.changeHistory': '变更历史',
|
||||
'caseManagement.featureCase.demandPlatform': '平台',
|
||||
|
@ -156,7 +157,7 @@ export default {
|
|||
'caseManagement.featureCase.transferFileSuccess': '转存成功',
|
||||
'caseManagement.featureCase.defectName': '缺陷名称',
|
||||
'caseManagement.featureCase.defectState': '缺陷状态',
|
||||
'caseManagement.featureCase.createDefect': '创建缺陷',
|
||||
'caseManagement.featureCase.createDefect': '快速创建缺陷',
|
||||
'caseManagement.featureCase.testPlanLinkList': '测试计划关联列表',
|
||||
'caseManagement.featureCase.directLink': '直接关联',
|
||||
'caseManagement.featureCase.linkDefect': '关联缺陷',
|
||||
|
@ -234,5 +235,8 @@ export default {
|
|||
'caseManagement.featureCase.CheckFailure': '校验失败',
|
||||
'caseManagement.featureCase.CheckSuccess': '校验成功',
|
||||
'caseManagement.featureCase.tableNoData': '暂无数据',
|
||||
'caseManagement.featureCase.noAssociatedDefect': '暂无可关联缺陷,请',
|
||||
'caseManagement.featureCase.noAssociated': '暂无可关联缺陷,请',
|
||||
'caseManagement.featureCase.fileIsUpdated': '当前文件已更新',
|
||||
'caseManagement.featureCase.selectTransferDirectory': '请选择转存目录',
|
||||
'caseManagement.featureCase.quicklyCreateDefectSuccess': '快速创建缺陷成功',
|
||||
};
|
||||
|
|
|
@ -2,9 +2,15 @@
|
|||
<MsCaseAssociate
|
||||
v-model:visible="innerVisible"
|
||||
v-model:project="innerProject"
|
||||
v-model:currentSelectCase="currentSelectCase"
|
||||
:ok-button-disabled="associateForm.reviewers.length === 0"
|
||||
:get-modules-func="getCaseModuleTree"
|
||||
@success="writeAssociateCases"
|
||||
:modules-count="modulesCount"
|
||||
:get-table-func="getCaseList"
|
||||
:associated-ids="associatedIds"
|
||||
:confirm-loading="confirmLoading"
|
||||
@init="getModuleCount"
|
||||
@save="saveHandler"
|
||||
>
|
||||
<template #footerLeft>
|
||||
<a-form ref="associateFormRef" :model="associateForm">
|
||||
|
@ -71,11 +77,13 @@
|
|||
import MsSelect from '@/components/business/ms-select';
|
||||
|
||||
import { getReviewUsers } from '@/api/modules/case-management/caseReview';
|
||||
import { getCaseModuleTree } from '@/api/modules/case-management/featureCase';
|
||||
import { getCaseList, getCaseModulesCounts, getCaseModuleTree } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import type { CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -88,7 +96,6 @@
|
|||
(e: 'success', val: string[]): void;
|
||||
(e: 'close'): void;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const { currentLocale } = useLocale();
|
||||
|
@ -157,10 +164,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
function writeAssociateCases(ids: string[]) {
|
||||
emit('success', ids);
|
||||
const currentSelectCase = ref<string | number | Record<string, any> | undefined>('');
|
||||
const modulesCount = ref<Record<string, any>>({});
|
||||
|
||||
async function getModuleCount(params: CaseModuleQueryParams) {
|
||||
try {
|
||||
modulesCount.value = await getCaseModulesCounts(params);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const associatedIds = ref<string[]>([]);
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
|
||||
function saveHandler(params: TableQueryParams) {}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initReviewers();
|
||||
});
|
||||
|
|
|
@ -133,7 +133,7 @@ export default {
|
|||
'system.orgTemplate.startState': '开始状态',
|
||||
'system.orgTemplate.endState': '结束状态',
|
||||
'system.orgTemplate.iconTip': '图标可调整状态顺序',
|
||||
'system.orgTemplate.anyStateToAll': '任何状态可转换到改状态',
|
||||
'system.orgTemplate.anyStateToAll': '任何状态可转换到该状态',
|
||||
'system.orgTemplate.enableAnyStateToAll': '开启',
|
||||
'system.orgTemplate.enableNotAnyStateToAll': '未开启',
|
||||
'system.orgTemplate.createFlowStep': '创建流转步骤',
|
||||
|
|
Loading…
Reference in New Issue