refactor(功能用例&缺陷管理): 功能用例基本信息和缺陷管理基本信息调整

This commit is contained in:
xinxin.wu 2024-06-12 19:28:22 +08:00 committed by Craftsman
parent bfe7789e78
commit 28b2e9197f
20 changed files with 830 additions and 590 deletions

View File

@ -265,6 +265,7 @@
excludeIds: [...excludeKeys].concat(...(props.associatedIds || [])),
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
selectAll: selectorStatus === 'all',
associateApiType: 'API_CASE',
};
}

View File

@ -240,6 +240,7 @@
excludeIds: [...excludeKeys].concat(...(props.associatedIds || [])),
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
selectAll: selectorStatus === 'all',
associateApiType: 'API',
};
}

View File

@ -1,22 +1,14 @@
<template>
<TreeFolderAll
v-if="props.activeTab === CaseLinkEnum.API"
v-model:selectedProtocols="selectedProtocols"
:active-folder="activeFolder"
:folder-name="props.folderName"
:all-count="allCount"
:show-expand-api="false"
:not-show-operation="props.activeTab !== 'API'"
@set-active-folder="setActiveFolder"
@selected-protocols-change="selectedProtocolsChange"
/>
<MsFolderAll
v-else
:active-folder="activeFolder"
:folder-name="props.folderName"
:all-count="allCount"
@set-active-folder="setActiveFolder"
>
</MsFolderAll>
<a-divider class="my-[8px] mt-0" />
<div class="mb-[8px] flex items-center gap-[8px]">
<a-input

View File

@ -368,7 +368,6 @@
activeFolderName.value = name ?? '';
}
const isAddAssociatedCase = ref<boolean>(false);
const functionalTableRef = ref<InstanceType<typeof CaseTable>>();
const apiTableRef = ref<InstanceType<typeof ApiTable>>();
const caseTableRef = ref<InstanceType<typeof ApiCaseTable>>();

View File

@ -1,4 +1,5 @@
import { ReviewItem, ReviewResult, ReviewStatus } from '@/models/caseManagement/caseReview';
import type { DetailCase } from '@/models/caseManagement/featureCase';
// 评审结果
export type ReviewResultMap = Record<
@ -106,3 +107,24 @@ export const minderTagMap = {
EXPECTED_RESULT: 'minder.tag.expect',
DESCRIPTION: 'minder.tag.remark',
};
export const defaultCaseDetail: DetailCase = {
id: '',
projectId: '',
templateId: '',
name: '',
prerequisite: '', // prerequisite
caseEditType: '', // 编辑模式:步骤模式/文本模式
steps: '',
textDescription: '',
expectedResult: '', // 预期结果
description: '',
publicCase: false, // 是否公共用例
moduleId: '',
versionId: '',
tags: [],
customFields: [], // 自定义字段集合
relateFileMetaIds: [], // 关联文件ID集合
functionalPriority: '',
reviewStatus: 'UN_REVIEWED',
};

View File

@ -0,0 +1,290 @@
<template>
<a-spin :loading="loading" class="w-full">
<div class="form-item-container">
<!-- 所属平台一致, 详情展示 -->
<div v-if="props.currentPlatform === detailInfo.platform" class="h-full w-full">
<!-- 自定义字段开始 -->
<div class="inline-block w-full break-words">
<MsFormCreate
v-if="props.formRule.length"
ref="formCreateRef"
v-model:form-item="innerFormItem"
v-model:api="fApi"
v-model:form-rule="innerFormRules"
class="w-full"
:option="options"
@change="handelFormCreateChange"
/>
<!-- 自定义字段结束 -->
<div
v-if="!props.isPlatformDefaultTemplate && hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
class="baseItem"
>
<a-form
:model="{}"
:label-col-props="{
span: 9,
}"
:wrapper-col-props="{
span: 15,
}"
label-align="left"
content-class="tags-class"
>
<a-form-item field="tags" :label="t('system.orgTemplate.tags')">
<MsTagsInput
v-model:model-value="innerTags"
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
@blur="changeTag"
/>
</a-form-item>
</a-form>
</div>
</div>
<!-- 内置基础信息结束 -->
</div>
<!-- 所属平台不一致, 详情不展示, 展示空面板 -->
<div v-else>
<a-empty> {{ $t('messageBox.noContent') }} </a-empty>
</div>
</div>
</a-spin>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Message } from '@arco-design/web-vue';
import { cloneDeep, debounce } from 'lodash-es';
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import { createOrUpdateBug } from '@/api/modules/bug-management';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import { hasAnyPermission } from '@/utils/permission';
import { BugEditCustomField, BugEditFormObject } from '@/models/bug-management';
import { makeCustomFieldsParams } from '../utils';
const appStore = useAppStore();
const { t } = useI18n();
const props = defineProps<{
detail: Record<string, any>;
currentPlatform: string;
formRule: FormItem[];
isPlatformDefaultTemplate: boolean;
platformSystemFields: BugEditCustomField[]; //
}>();
const emit = defineEmits<{
(e: 'update:tags', val: string[]): void;
(e: 'updateSuccess'): void;
}>();
const loading = ref<boolean>(false);
const detailInfo = ref<Record<string, any>>({ match: [] }); // loadBug
const fApi = ref<any>(null);
const innerFormRules = defineModel<FormItem[]>('formRule', { default: [] });
const innerFormItem = defineModel<FormRuleItem[]>('formItem', { default: [] });
const innerTags = defineModel<string[]>('tags', { default: [] });
//
const options = {
resetBtn: false, //
submitBtn: false,
on: false, // on
form: {
layout: 'horizontal',
labelAlign: 'left',
labelColProps: {
span: 9,
},
wrapperColProps: {
span: 15,
},
},
//
row: {
gutter: 0,
},
wrap: {
'asterisk-position': 'end',
'validate-trigger': ['change'],
},
};
async function makeParams() {
const customFields = await makeCustomFieldsParams(innerFormItem.value);
if (props.isPlatformDefaultTemplate) {
//
props.platformSystemFields.forEach((item) => {
customFields.push({
id: item.fieldId,
name: item.fieldName,
type: item.type,
value: item.defaultValue,
});
});
}
const params: BugEditFormObject = {
projectId: appStore.currentProjectId,
id: detailInfo.value.id,
templateId: detailInfo.value.templateId,
tags: innerTags.value,
deleteLocalFileIds: [],
unLinkRefIds: [],
linkFileIds: [],
customFields,
richTextTmpFileIds: [],
};
if (!props.isPlatformDefaultTemplate) {
params.description = detailInfo.value.description;
params.title = detailInfo.value.title;
}
return params;
}
function saveHandler() {
try {
fApi.value?.validate().then(async (valid: any) => {
if (valid === true) {
loading.value = true;
const requestParams = await makeParams();
await createOrUpdateBug({ request: requestParams, fileList: [] as unknown as File[] });
Message.success(t('common.editSuccess'));
emit('updateSuccess');
}
});
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
}
const handelFormCreateChange = debounce(() => {
saveHandler();
}, 300);
const changeTag = debounce(() => {
emit('update:tags', innerTags.value);
saveHandler();
}, 300);
watchEffect(() => {
detailInfo.value = cloneDeep(props.detail);
});
</script>
<style scoped lang="less">
.form-item-container {
max-width: 50%;
.baseItem {
margin-bottom: 16px;
height: 32px;
line-height: 32px;
@apply flex;
.label {
width: 84px;
color: var(--color-text-3);
}
}
:deep(.arco-form-item-layout-horizontal) {
margin-bottom: 16px !important;
}
:deep(.arco-form-item-label-col) {
padding-right: 0;
}
:deep(.arco-col-9) {
flex: 0 0 84px;
width: 84px;
}
:deep(.arco-col-15) {
flex: 0 0 calc(100% - 84px);
width: calc(100% - 84px);
}
:deep(.arco-form-item-label::after) {
color: red !important;
}
:deep(.arco-form-item-label-col > .arco-form-item-label) {
color: var(--color-text-3) !important;
}
:deep(.arco-select-view-single) {
border-color: transparent !important;
.arco-select-view-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
.arco-select-view-suffix {
visibility: visible !important;
}
}
&:hover > .arco-input {
font-weight: normal;
text-decoration: none;
color: var(--color-text-1);
}
& > .arco-input {
font-weight: 500;
text-decoration: underline;
color: var(--color-text-1);
}
}
:deep(.arco-input-tag) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-input-wrapper) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-select-view-multiple) {
border-color: transparent !important;
.arco-select-view-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
.arco-select-view-suffix {
visibility: visible !important;
}
}
}
:deep(.arco-textarea-wrapper) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-input-number) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-picker) {
border-color: transparent !important;
.arco-picker-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
arco-picker-suffix {
visibility: visible !important;
}
}
}
}
</style>

View File

@ -2,7 +2,7 @@
<MsDetailDrawer
ref="detailDrawerRef"
v-model:visible="showDrawerVisible"
:width="1200"
:width="900"
:footer="false"
:title="t('bugManagement.detail.title', { id: detailInfo?.num, name: detailInfo?.title })"
:tooltip-text="(detailInfo && detailInfo.title) || null"
@ -91,7 +91,7 @@
</MsButton>
</div>
</template>
<template #default="{ loading }">
<template #default="{ detail }">
<div
ref="wrapperRef"
:class="[
@ -113,23 +113,12 @@
class="no-content relative border-b"
/>
</div>
<MsSplitBox
expand-direction="right"
:size="0.8"
:max="0.7"
:min="0.6"
direction="horizontal"
class="!h-[calc(100%-48px)]"
:class="{ 'left-bug-detail': activeTab === 'comment' }"
>
<template #first>
<div class="leftWrapper h-full">
<a-spin :loading="detailLoading" class="w-full">
<div class="tab-pane-container p-4">
<div class="tab-pane-container">
<BugDetailTab
v-if="activeTab === 'detail'"
ref="bugDetailTabRef"
:form-item="formItem"
:allow-edit="hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
:detail-info="detailInfo"
:is-platform-default-template="isPlatformDefaultTemplate"
@ -137,6 +126,17 @@
:current-platform="props.currentPlatform"
@update-success="detailDrawerRef?.initDetail()"
/>
<BasicInfo
v-if="activeTab === 'basicInfo'"
v-model:tags="tags"
:form-rule="formRules"
:detail="detail"
:current-platform="props.currentPlatform"
:is-platform-default-template="isPlatformDefaultTemplate"
:loading="rightLoading"
:platform-system-fields="platformSystemFields"
@update-success="detailDrawerRef?.initDetail()"
/>
<BugCaseTab
v-else-if="activeTab === 'case'"
@ -150,69 +150,6 @@
</div>
</a-spin>
</div>
</template>
<template #second>
<a-spin :loading="rightLoading" class="w-full">
<!-- 所属平台一致, 详情展示 -->
<div v-if="props.currentPlatform === detailInfo.platform" class="rightWrapper h-full p-4">
<!-- 自定义字段开始 -->
<div class="inline-block w-full break-words">
<a-skeleton v-if="rightLoading" class="w-full" :loading="rightLoading" :animation="true">
<a-space direction="vertical" class="w-[100%]" size="large">
<a-skeleton-line :rows="14" :line-height="30" :line-spacing="30" />
</a-space>
</a-skeleton>
<div v-if="!rightLoading" class="mb-4 font-medium">
<strong>
{{ t('bugManagement.detail.basicInfo') }}
</strong>
</div>
<MsFormCreate
v-if="!rightLoading"
ref="formCreateRef"
v-model:form-item="formItem"
v-model:api="fApi"
:form-rule="formRules"
class="w-full"
:option="options"
@change="handelFormCreateChange"
/>
<!-- 自定义字段结束 -->
<div
v-if="!isPlatformDefaultTemplate && hasAnyPermission(['PROJECT_BUG:READ+UPDATE']) && !loading"
class="baseItem"
>
<a-form
:model="{}"
:label-col-props="{
span: 9,
}"
:wrapper-col-props="{
span: 15,
}"
label-align="left"
content-class="tags-class"
>
<a-form-item field="tags" :label="t('system.orgTemplate.tags')">
<MsTagsInput
v-model:model-value="tags"
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
@blur="changeTag"
/>
</a-form-item>
</a-form>
</div>
</div>
<!-- 内置基础信息结束 -->
</div>
<!-- 所属平台不一致, 详情不展示, 展示空面板 -->
<div v-else>
<a-empty> {{ $t('messageBox.noContent') }} </a-empty>
</div>
</a-spin>
</template>
</MsSplitBox>
</div>
<CommentInput
v-if="activeTab === 'comment' && hasAnyPermission(['PROJECT_BUG:READ+COMMENT'])"
@ -235,20 +172,17 @@
import { useRoute, useRouter } from 'vue-router';
import { useClipboard } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
import { debounce } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
import type { FormItem } from '@/components/pure/ms-form-create/types';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import type { MsPaginationI } from '@/components/pure/ms-table/type';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import CommentInput from '@/components/business/ms-comment/input.vue';
import { CommentParams } from '@/components/business/ms-comment/types';
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
import BasicInfo from './basicInfo.vue';
import BugCaseTab from './bugCaseTab.vue';
import BugDetailTab from './bugDetailTab.vue';
import BugHistoryTab from './bugHistoryTab.vue';
@ -302,9 +236,8 @@
const commentContent = ref('');
const commentRef = ref();
const noticeUserIds = ref<string[]>([]); // ids
const fApi = ref();
const formRules = ref<FormItem[]>([]); //
const formItem = ref<FormRuleItem[]>([]); //
const currentProjectId = computed(() => appStore.currentProjectId);
const showDrawerVisible = defineModel<boolean>('visible', { default: false });
const bugDetailTabRef = ref();
@ -463,6 +396,10 @@
}
const tabList = [
{
value: 'basicInfo',
label: t('bugManagement.detail.basicInfo'),
},
{
value: 'detail',
label: t('bugManagement.detail.detail'),
@ -577,54 +514,6 @@
});
}
/**
* 单独更新字段
*/
async function updateFieldHandler() {
try {
rightLoading.value = true;
await bugDetailTabRef.value?.handleSave();
rightLoading.value = false;
} catch (error) {
console.log(error);
} finally {
rightLoading.value = false;
}
}
const handelFormCreateChange = debounce(() => {
updateFieldHandler();
}, 300);
const changeTag = debounce(() => {
detailInfo.value.tags = tags.value;
updateFieldHandler();
}, 300);
//
const options = {
resetBtn: false, //
submitBtn: false,
on: false, // on
form: {
layout: 'horizontal',
labelAlign: 'left',
labelColProps: {
span: 9,
},
wrapperColProps: {
span: 15,
},
},
//
row: {
gutter: 0,
},
wrap: {
'asterisk-position': 'end',
'validate-trigger': ['change'],
},
};
const uploadFileIds = ref<string[]>([]);
async function publishHandler(currentContent: string) {
try {
@ -794,14 +683,9 @@
justify-content: flex-start !important;
}
.tab-pane-container {
@apply flex-1 overflow-y-auto px-4;
@apply flex-1 overflow-y-auto p-4;
.ms-scroll-bar();
}
//:deep(.w-full .arco-form-item-label) {
// display: inline-block;
// width: 100%;
// word-wrap: break-word;
//}
:deep(.arco-form-item-content) {
overflow-wrap: anywhere;
}

View File

@ -202,7 +202,6 @@
import { Message } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import { FormRuleItem } from '@/components/pure/ms-form-create/types';
import MsIconfont from '@/components/pure/ms-icon-font/index.vue';
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
@ -232,7 +231,6 @@
import { useAppStore } from '@/store';
import { downloadByteFile, sleep } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { findParents, Option } from '@/utils/recursion';
import { BugEditCustomField, BugEditCustomFieldItem, BugEditFormObject } from '@/models/bug-management';
import { AssociatedList, AttachFileInfo } from '@/models/caseManagement/featureCase';
@ -248,7 +246,7 @@
const props = defineProps<{
detailInfo: BugEditFormObject;
formItem: FormRuleItem[];
// formItem: FormRuleItem[];
allowEdit?: boolean; //
isPlatformDefaultTemplate: boolean; //
platformSystemFields: BugEditCustomField[]; //
@ -453,26 +451,22 @@
return fileIds;
}
function getDetailCustomFields() {
return props.detailInfo.customFields.map((item: any) => {
return {
id: item.id,
name: item.name,
type: item.type,
value: item.value,
};
});
}
//
async function handleSave() {
try {
confirmLoading.value = true;
const { formItem } = props;
const customFields: BugEditCustomFieldItem[] = [];
if (formItem && formItem.length) {
formItem.forEach((item: FormRuleItem) => {
let itemVal = item.value;
if (item.sourceType === 'CASCADER') {
itemVal = findParents(item.options as Option[], item.value as string, []) || '';
}
customFields.push({
id: item.field as string,
name: item.title as string,
type: item.sourceType as string,
value: Array.isArray(itemVal) ? JSON.stringify(itemVal) : (itemVal as string),
});
});
}
const customFields: BugEditCustomFieldItem[] = getDetailCustomFields();
if (props.isPlatformDefaultTemplate) {
//
props.platformSystemFields.forEach((item) => {

View File

@ -4,9 +4,13 @@
* @param {stafileInfotus} file
*/
import { FormRuleItem } from '@/components/pure/ms-form-create/types';
import { getFileEnum } from '@/components/pure/ms-upload/iconMap';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import { findParents, Option } from '@/utils/recursion';
import { BugEditCustomFieldItem } from '@/models/bug-management';
import { AssociatedList } from '@/models/caseManagement/featureCase';
export function convertToFileByBug(fileInfo: AssociatedList): MsFileItem {
@ -68,3 +72,22 @@ export function convertToFileByDetail(fileInfo: AssociatedList): MsFileItem {
associateId,
};
}
export function makeCustomFieldsParams(formItem: FormRuleItem[]) {
const customFields: BugEditCustomFieldItem[] = [];
if (formItem && formItem.length) {
formItem.forEach((item: FormRuleItem) => {
let itemVal = item.value;
if (item.sourceType === 'CASCADER') {
itemVal = findParents(item.options as Option[], item.value as string, []) || '';
}
customFields.push({
id: item.field as string,
name: item.title as string,
type: item.sourceType as string,
value: Array.isArray(itemVal) ? JSON.stringify(itemVal) : (itemVal as string),
});
});
}
return customFields;
}

View File

@ -2,7 +2,7 @@
<MsDetailDrawer
ref="detailDrawerRef"
v-model:visible="showDrawerVisible"
:width="1200"
:width="960"
:footer="false"
:mask="false"
:title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.num, name: detailInfo?.name })"
@ -100,30 +100,17 @@
class="no-content relative border-b"
@change="clickMenu"
/>
<span class="absolute bottom-0 right-4 top-4 my-auto text-[var(--color-text-2)]" @click="showMenuSetting">{{
<span class="display-setting h-full text-[var(--color-text-2)]" @click="showMenuSetting">{{
t('caseManagement.featureCase.detailDisplaySetting')
}}</span>
</div>
<MsSplitBox
:size="0.8"
:max="0.7"
:min="0.6"
direction="horizontal"
expand-direction="right"
class="!h-[calc(100%-48px)]"
>
<template #first>
<div class="leftWrapper h-full">
<div class="leftContent w-full pl-[16px] pr-[24px] pt-4">
<div class="w-full p-[16px] pt-4">
<template v-if="activeTab === 'detail'">
<TabDetail
ref="tabDetailRef"
:form="detailInfo"
:allow-edit="true"
:form-rules="formItem"
:form-api="fApi"
@update-success="updateSuccess"
/>
<TabDetail :form="detailInfo" :allow-edit="true" :form-rules="formItem" @update-success="updateSuccess" />
</template>
<template v-if="activeTab === 'basicInfo'">
<BasicInfo :loading="loading" :detail="detail" @update-success="updateSuccess" />
</template>
<template v-if="activeTab === 'requirement'">
<TabDemand :case-id="detail.id" />
@ -151,81 +138,6 @@
</template>
</div>
</div>
</template>
<template #second>
<div class="rightWrapper h-full p-4">
<a-skeleton v-if="loading" class="w-full" :loading="loading" :animation="true">
<a-space direction="vertical" class="w-[100%]" size="large">
<a-skeleton-line :rows="14" :line-height="30" :line-spacing="30" />
</a-space>
</a-skeleton>
<div v-else>
<div class="mb-4 font-medium">{{ t('caseManagement.featureCase.basicInfo') }}</div>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnModule') }}</span>
<span class="w-[calc(100%-36%)]">
<a-tree-select
v-model="detailInfo.moduleId"
:data="caseTree"
class="w-full"
:allow-search="true"
:filter-tree-node="filterTreeNode"
:field-names="{
title: 'name',
key: 'id',
children: 'children',
}"
:tree-props="{
virtualListProps: {
height: 200,
},
}"
@change="handleChangeModule"
>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text w-[300px] text-[var(--color-text-1)]">{{ node.name }}</div>
</a-tooltip>
</template>
</a-tree-select>
</span>
</div>
<!-- 自定义字段开始 -->
<MsFormCreate
v-if="formRules.length"
ref="formCreateRef"
v-model:api="fApi"
v-model:form-item="formItem"
:form-rule="formRules"
class="w-full"
:option="options"
@change="changeHandler"
/>
<!-- 自定义字段结束 -->
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateUser') }}</span>
<a-tooltip
v-if="translateTextToPX(detailInfo?.createUserName) > 200"
:content="detailInfo?.createUserName"
>
<span class="one-line-text" style="max-width: 200px">{{ detailInfo?.createUserName }}</span>
</a-tooltip>
<span v-else class="value">{{ detailInfo?.createUserName }}</span>
</div>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateTime') }}</span>
<span class="value">{{ dayjs(detailInfo?.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</div>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnTag') }}</span>
<span class="value">
<MsTag v-for="item of detailInfo.tags" :key="item"> {{ item }} </MsTag>
</span>
</div>
</div>
</div>
</template>
</MsSplitBox>
<inputComment
ref="commentInputRef"
v-model:content="content"
@ -250,21 +162,19 @@
import { computed, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useClipboard } from '@vueuse/core';
import { Message, TreeNodeData } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import type { MsPaginationI } from '@/components/pure/ms-table/type';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
import inputComment from '@/components/business/ms-comment/input.vue';
import { CommentParams } from '@/components/business/ms-comment/types';
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
import BasicInfo from './tabContent/basicInfo.vue';
import SettingDrawer from './tabContent/settingDrawer.vue';
import {
@ -276,21 +186,19 @@
getCaseModuleTree,
} from '@/api/modules/case-management/featureCase';
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
import { defaultCaseDetail } from '@/config/caseManagement';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useUserStore from '@/store/modules/user';
import { characterLimit } from '@/utils';
import { translateTextToPX } from '@/utils/css';
import type { CustomAttributes, DetailCase, TabItemType } from '@/models/caseManagement/featureCase';
import { ModuleTreeNode } from '@/models/common';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { getCaseLevels, initFormCreate } from './utils';
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
import debounce from 'lodash-es/debounce';
//
const TabDefect = defineAsyncComponent(() => import('./tabContent/tabBug/tabDefect.vue'));
const TabCaseTable = defineAsyncComponent(() => import('./tabContent/tabCase/tabCaseTable.vue'));
@ -351,26 +259,6 @@
break;
}
}
const initDetail: DetailCase = {
id: '',
projectId: '',
templateId: '',
name: '',
prerequisite: '', // prerequisite
caseEditType: '', // /
steps: '',
textDescription: '',
expectedResult: '', //
description: '',
publicCase: false, //
moduleId: '',
versionId: '',
tags: [],
customFields: [], //
relateFileMetaIds: [], // ID
functionalPriority: '',
reviewStatus: 'UN_REVIEWED',
};
const caseTree = ref<ModuleTreeNode[]>([]);
@ -382,7 +270,7 @@
}
}
const route = useRoute();
const detailInfo = ref<DetailCase>({ ...initDetail });
const detailInfo = ref<DetailCase>(cloneDeep(defaultCaseDetail));
const customFields = ref<CustomAttributes[]>([]);
const caseLevels = ref<CaseLevel>('P0');
@ -451,8 +339,8 @@
}
}
const followLoading = ref<boolean>(false);
//
const followLoading = ref<boolean>(false);
async function followHandler() {
followLoading.value = true;
try {
@ -506,50 +394,12 @@
const formRules = ref<FormItem[]>([]);
const formItem = ref<FormRuleItem[]>([]);
//
const options = {
resetBtn: false, //
submitBtn: false,
on: false, // on
form: {
layout: 'horizontal',
labelAlign: 'left',
labelColProps: {
span: 9,
},
wrapperColProps: {
span: 15,
},
contentClass: 'contentClass',
autoLabelWidth: true,
},
//
row: {
gutter: 0,
},
wrap: {
'asterisk-position': 'end',
'validate-trigger': ['change'],
'hide-asterisk': true,
},
};
const fApi = ref(null);
//
function initForm() {
formRules.value = initFormCreate(customFields.value, ['FUNCTIONAL_CASE:READ+UPDATE']);
}
const tabDetailRef = ref();
function handleChangeModule(value: string | number | LabelValue | Array<string | number> | LabelValue[] | undefined) {
detailInfo.value.moduleId = value as string;
tabDetailRef.value.handleOK();
}
function filterTreeNode(searchValue: string, nodeValue: TreeNodeData) {
return (nodeValue as ModuleTreeNode).name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
}
function getTotal(key: string) {
switch (key) {
case 'detail':
@ -568,7 +418,9 @@
},
{ deep: true }
);
const settingDrawerRef = ref();
watch(
() => props.visible,
(val) => {
@ -621,17 +473,20 @@
isActive.value = !isActive.value;
}
const changeHandler = debounce(() => {
tabDetailRef.value.handleOK();
}, 300);
const tabDefaultSettingList: TabItemType[] = [
{
value: 'basicInfo',
label: t('caseManagement.featureCase.basicInfo'),
canHide: false,
isShow: true,
},
{
value: 'detail',
label: t('caseManagement.featureCase.detail'),
canHide: false,
isShow: true,
},
{
value: 'case',
label: t('caseManagement.featureCase.case'),
@ -724,6 +579,13 @@
</script>
<style scoped lang="less">
.display-setting {
position: absolute;
top: 0;
right: 16px;
height: 48px;
line-height: 48px;
}
:deep(.arco-menu-light) {
height: 50px;
background: none !important;
@ -739,116 +601,6 @@
border-bottom: 1px solid var(--color-text-n8);
}
}
.rightWrapper {
.baseItem {
margin-bottom: 16px;
height: 32px;
line-height: 32px;
@apply flex;
.label {
flex-shrink: 0;
width: 84px;
color: var(--color-text-3);
}
.value {
padding-left: 10px;
}
}
:deep(.arco-form-item-layout-horizontal) {
margin-bottom: 16px !important;
}
:deep(.arco-form-item-label-col) {
padding-right: 0;
}
:deep(.arco-col-9) {
flex: 0 0 84px;
width: 84px;
}
:deep(.arco-col-15) {
flex: 0 0 calc(100% - 84px);
width: calc(100% - 84px);
}
:deep(.arco-form-item-label::after) {
color: red !important;
}
:deep(.arco-form-item-label-col > .arco-form-item-label) {
overflow: hidden;
width: 84px;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--color-text-3) !important;
}
:deep(.arco-select-view-single) {
border-color: transparent !important;
.arco-select-view-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
.arco-select-view-suffix {
visibility: visible !important;
}
}
&:hover > .arco-input {
font-weight: normal;
text-decoration: none;
color: var(--color-text-1);
}
& > .arco-input {
font-weight: 500;
text-decoration: underline;
color: var(--color-text-1);
}
}
:deep(.arco-input-tag) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-input-wrapper) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-select-view-multiple) {
border-color: transparent !important;
.arco-select-view-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
.arco-select-view-suffix {
visibility: visible !important;
}
}
}
:deep(.arco-textarea-wrapper) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-input-number) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-picker) {
border-color: transparent !important;
.arco-picker-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
arco-picker-suffix {
visibility: visible !important;
}
}
}
}
.rightButtons {
:deep(.ms-button--secondary):hover,
:hover > .arco-icon {

View File

@ -0,0 +1,318 @@
<template>
<a-spin class="w-full" :loading="loading">
<div class="h-full w-full">
<div class="basic-container">
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnModule') }}</span>
<span class="w-full">
<a-tree-select
v-model="detailInfo.moduleId"
:data="caseTree"
class="w-full"
:allow-search="true"
:filter-tree-node="filterTreeNode"
:field-names="{
title: 'name',
key: 'id',
children: 'children',
}"
:tree-props="{
virtualListProps: {
height: 200,
},
}"
@change="handleChangeModule"
>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text w-[300px] text-[var(--color-text-1)]">{{ node.name }}</div>
</a-tooltip>
</template>
</a-tree-select>
</span>
</div>
<MsFormCreate
v-if="formRules.length"
ref="formCreateRef"
v-model:api="fApi"
v-model:form-item="formItem"
:form-rule="formRules"
class="w-full"
:option="options"
@change="changeHandler"
/>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateUser') }}</span>
<a-tooltip v-if="translateTextToPX(detailInfo?.createUserName) > 200" :content="detailInfo?.createUserName">
<span class="one-line-text" style="max-width: 200px">{{ detailInfo?.createUserName }}</span>
</a-tooltip>
<span v-else class="value">{{ detailInfo?.createUserName }}</span>
</div>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateTime') }}</span>
<span class="value">{{ dayjs(detailInfo?.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</div>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnTag') }}</span>
<span class="value">
<MsTag v-for="item of detailInfo.tags" :key="item"> {{ item }} </MsTag>
</span>
</div>
</div>
</div>
</a-spin>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Message, TreeNodeData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import { getCaseModuleTree, updateCaseRequest } from '@/api/modules/case-management/featureCase';
import { defaultCaseDetail } from '@/config/caseManagement';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import { translateTextToPX } from '@/utils/css';
import type { CustomAttributes, DetailCase } from '@/models/caseManagement/featureCase';
import { ModuleTreeNode } from '@/models/common';
import { initFormCreate } from '../utils';
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
import debounce from 'lodash-es/debounce';
const { t } = useI18n();
const props = defineProps<{
detail: DetailCase;
}>();
const emit = defineEmits<{
(e: 'updateSuccess'): void;
}>();
const appStore = useAppStore();
const detailInfo = ref<DetailCase>(cloneDeep(defaultCaseDetail));
const options = {
resetBtn: false, //
submitBtn: false,
on: false, // on
form: {
layout: 'horizontal',
labelAlign: 'left',
labelColProps: {
span: 9,
},
wrapperColProps: {
span: 15,
},
},
row: {
gutter: 0,
},
wrap: {
'asterisk-position': 'end',
'validate-trigger': ['change'],
},
};
const formRules = ref<FormItem[]>([]);
const formItem = ref<FormRuleItem[]>([]);
function getParams() {
const customFieldsArr = (formItem.value || []).map((item: any) => {
return {
fieldId: item.field,
value: Array.isArray(item.value) ? JSON.stringify(item.value) : item.value,
};
});
return {
request: {
...detailInfo.value,
deleteFileMetaIds: [], // id
unLinkFilesIds: [], // id
newAssociateFileListIds: [], // id
customFields: customFieldsArr,
caseDetailFileIds: [],
},
fileList: [], //
};
}
const fApi = ref<any>(null);
const loading = ref<boolean>(false);
function updateHandler() {
try {
fApi.value?.validate().then(async (valid: any) => {
if (valid === true) {
loading.value = true;
await updateCaseRequest(getParams());
loading.value = false;
Message.success(t('caseManagement.featureCase.editSuccess'));
emit('updateSuccess');
}
});
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
}
function handleChangeModule(value: string | number | LabelValue | Array<string | number> | LabelValue[] | undefined) {
detailInfo.value.moduleId = value as string;
updateHandler();
}
function filterTreeNode(searchValue: string, nodeValue: TreeNodeData) {
return (nodeValue as ModuleTreeNode).name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
}
const changeHandler = debounce(() => {
updateHandler();
}, 300);
const caseTree = ref<ModuleTreeNode[]>([]);
async function getCaseTree() {
try {
caseTree.value = await getCaseModuleTree({ projectId: appStore.currentProjectId });
} catch (error) {
console.log(error);
}
}
//
function initForm() {
const { customFields } = detailInfo.value;
formRules.value = initFormCreate(customFields as CustomAttributes[], ['FUNCTIONAL_CASE:READ+UPDATE']);
}
watchEffect(() => {
detailInfo.value = props.detail;
initForm();
});
onBeforeMount(() => {
getCaseTree();
});
</script>
<style scoped lang="less">
.basic-container {
max-width: 50%;
.baseItem {
margin-bottom: 16px;
height: 32px;
line-height: 32px;
@apply flex;
.label {
flex-shrink: 0;
width: 84px;
color: var(--color-text-3);
}
.value {
padding-left: 10px;
}
}
:deep(.arco-form-item-layout-horizontal) {
margin-bottom: 16px !important;
}
:deep(.arco-form-item-label-col) {
padding-right: 0;
}
:deep(.arco-col-9) {
flex: 0 0 84px;
width: 84px;
}
:deep(.arco-col-15) {
flex: 0 0 calc(100% - 84px);
width: calc(100% - 84px);
}
:deep(.arco-form-item-label::after) {
color: red !important;
}
:deep(.arco-form-item-label-col > .arco-form-item-label) {
overflow: hidden;
width: 84px;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--color-text-3) !important;
}
:deep(.arco-select-view-single) {
border-color: transparent !important;
.arco-select-view-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
.arco-select-view-suffix {
visibility: visible !important;
}
}
&:hover > .arco-input {
font-weight: normal;
text-decoration: none;
color: var(--color-text-1);
}
& > .arco-input {
font-weight: 500;
text-decoration: underline;
color: var(--color-text-1);
}
}
:deep(.arco-input-tag) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-input-wrapper) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-select-view-multiple) {
border-color: transparent !important;
.arco-select-view-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
.arco-select-view-suffix {
visibility: visible !important;
}
}
}
:deep(.arco-textarea-wrapper) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-input-number) {
border-color: transparent !important;
&:hover {
border-color: rgb(var(--primary-5)) !important;
}
}
:deep(.arco-picker) {
border-color: transparent !important;
.arco-picker-suffix {
visibility: hidden;
}
&:hover {
border-color: rgb(var(--primary-5)) !important;
arco-picker-suffix {
visibility: visible !important;
}
}
}
}
</style>

View File

@ -4,15 +4,7 @@
<span type="text" class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">{{ record.num }}</span>
</template>
<template #name="{ record }">
<a-tooltip :content="record.name">
<div class="one-line-text max-w-[calc(100%-32px)]"> {{ record.name }}</div>
</a-tooltip>
<a-popover class="bug-content-popover" title="" position="right" style="width: 480px">
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
<template #content>
<div v-dompurify-html="record.content" class="markdown-body bug-content"> </div>
</template>
</a-popover>
<BugNamePopover :name="record.name" :content="record.content" />
</template>
<template #statusName="{ record }">
<div class="one-line-text">{{ record.statusName || '-' }}</div>
@ -59,6 +51,7 @@
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import BugNamePopover from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/bugNamePopover.vue';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
@ -157,7 +150,7 @@
});
</script>
<style lang="less">
<!-- <style lang="less">
.bug-content-popover {
.arco-popover-content {
overflow: auto;
@ -165,4 +158,4 @@
.ms-scroll-bar();
}
}
</style>
</style> -->

View File

@ -0,0 +1,32 @@
<template>
<div class="one-line-text max-w-[calc(100%-32px)]"> {{ props.name }}</div>
<a-popover class="bug-content-popover" title="" position="right" style="width: 480px" tri>
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
<template #content>
<div v-dompurify-html="props.content" class="markdown-body bug-content"> </div>
</template>
</a-popover>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const props = defineProps<{
name: string;
content: string;
}>();
</script>
<style lang="less">
.bug-content-popover {
.arco-popover-content {
overflow: auto;
max-height: 400px;
.ms-scroll-bar();
}
}
</style>

View File

@ -38,17 +38,7 @@
v-on="propsEvent"
>
<template #name="{ record }">
<div class="flex flex-nowrap items-center">
<div class="one-line-text max-w-[200px] flex-auto items-center"> {{ characterLimit(record.name) }}</div>
<a-popover class="bug-content-popover" title="" position="right" style="width: 480px">
<div class="ml-1 flex-auto text-[rgb(var(--primary-5))]">{{
t('caseManagement.featureCase.preview')
}}</div>
<template #content>
<div v-dompurify-html="record.content" class="markdown-body"> </div>
</template>
</a-popover>
</div>
<BugNamePopover :name="record.name" :content="record.content" />
</template>
</ms-base-table>
</div>
@ -62,11 +52,11 @@
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import BugNamePopover from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/bugNamePopover.vue';
import { getDrawerDebugPage } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import { characterLimit } from '@/utils';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -104,10 +94,7 @@
title: 'caseManagement.featureCase.defectName',
slotName: 'name',
dataIndex: 'name',
showInTable: true,
showTooltip: true,
width: 300,
ellipsis: true,
showDrag: false,
},

View File

@ -67,21 +67,14 @@
:load-params="{
caseId: props.caseId,
}"
:can-edit="true"
@link="linkDefect"
@new="createDefect"
@cancel-link="cancelLink"
/>
<ms-base-table v-else v-bind="testPlanPropsRes" ref="planTableRef" v-on="testPlanTableEvent">
<template #name="{ record }">
<div class="flex flex-nowrap items-center">
<div class="one-line-text">{{ characterLimit(record.name) }}</div>
<a-popover title="" position="right" style="width: 480px">
<div class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</div>
<template #content>
<div v-dompurify-html="record.content" class="markdown-body" style="margin-left: 48px"> </div>
</template>
</a-popover>
</div>
<BugNamePopover :name="record.name" :content="record.content" />
</template>
<template #handleUserName="{ record }">
<a-tooltip :content="record.handleUserName">
@ -145,6 +138,7 @@
import AddDefectDrawer from './addDefectDrawer.vue';
import BugList from './bugList.vue';
import LinkDefectDrawer from './linkDefectDrawer.vue';
import BugNamePopover from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/bugNamePopover.vue';
import TableFilter from '@/views/case-management/caseManagementFeature/components/tableFilter.vue';
import { getBugList, getCustomOptionHeader } from '@/api/modules/bug-management';
@ -194,20 +188,14 @@
title: 'caseManagement.featureCase.tableColumnID',
dataIndex: 'num',
width: 100,
showInTable: true,
showTooltip: true,
showDrag: false,
fixed: 'left',
},
{
title: 'caseManagement.featureCase.defectName',
slotName: 'name',
dataIndex: 'name',
showInTable: true,
showTooltip: false,
width: 300,
ellipsis: true,
showDrag: false,
},
{
title: 'caseManagement.featureCase.defectState',
@ -217,19 +205,13 @@
options: [],
labelKey: 'text',
},
showInTable: true,
width: 150,
ellipsis: true,
showDrag: false,
},
{
title: 'common.creator',
slotName: 'createUserName',
dataIndex: 'createUserName',
showInTable: true,
showTooltip: true,
width: 200,
ellipsis: true,
},
{
title: 'caseManagement.featureCase.updateUser',
@ -239,19 +221,14 @@
options: [],
labelKey: 'text',
},
showInTable: true,
width: 200,
ellipsis: true,
},
{
title: 'caseManagement.featureCase.defectSource',
slotName: 'source',
dataIndex: 'source',
showInTable: true,
showTooltip: true,
width: 100,
ellipsis: true,
showDrag: false,
},
{
title: 'caseManagement.featureCase.tableColumnActions',
@ -259,8 +236,6 @@
dataIndex: 'operation',
fixed: 'right',
width: 100,
showInTable: true,
showDrag: false,
},
]);
@ -278,31 +253,21 @@
title: 'caseManagement.featureCase.defectName',
slotName: 'name',
dataIndex: 'name',
showInTable: true,
showTooltip: true,
width: 250,
ellipsis: true,
showDrag: false,
},
{
title: 'caseManagement.featureCase.planName',
slotName: 'testPlanName',
dataIndex: 'testPlanName',
showInTable: true,
showTooltip: true,
width: 200,
ellipsis: true,
showDrag: false,
},
{
title: 'caseManagement.featureCase.defectState',
slotName: 'defectState',
dataIndex: 'defectState',
showInTable: true,
showTooltip: true,
width: 150,
ellipsis: true,
showDrag: false,
},
{
title: 'caseManagement.featureCase.updateUser',
@ -312,10 +277,8 @@
options: [],
labelKey: 'text',
},
showInTable: true,
showTooltip: true,
width: 200,
ellipsis: true,
},
];

View File

@ -6,13 +6,13 @@
v-if="caseEnable"
v-permission="['FUNCTIONAL_CASE:READ+ADD', 'FUNCTIONAL_CASE:READ+UPDATE', 'FUNCTIONAL_CASE:READ+DELETE']"
type="primary"
class="mr-2"
@click="associatedDemand"
>
{{ t('caseManagement.featureCase.associatedDemand') }}</a-button
>
<a-button
v-permission="['FUNCTIONAL_CASE:READ+ADD', 'FUNCTIONAL_CASE:READ+UPDATE', 'FUNCTIONAL_CASE:READ+DELETE']"
class="mx-3"
type="outline"
@click="addDemand"
>

View File

@ -303,7 +303,6 @@
form: DetailCase;
allowEdit?: boolean; //
formRules?: FormRuleItem[]; //
formApi?: any;
isTestPlan?: boolean; //
isDisabledTestPlan?: boolean; // -
}>(),
@ -485,16 +484,12 @@
caseFormRef.value?.validate().then(async (res: any) => {
if (!res) {
try {
props.formApi?.validate().then(async (valid: any) => {
if (valid === true) {
confirmLoading.value = true;
await updateCaseRequest(getParams());
confirmLoading.value = false;
Message.success(t('caseManagement.featureCase.editSuccess'));
isEditPreposition.value = false;
emit('updateSuccess');
}
});
} catch (error) {
console.log(error);
} finally {

View File

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

View File

@ -115,6 +115,13 @@
}
);
function reset(val: boolean) {
if (!val) {
form.value.field = '';
formRef.value?.resetFields();
}
}
function beforeConfirm(done?: (closed: boolean) => void) {
if (loading.value) return;
formRef.value?.validate(async (errors) => {
@ -129,6 +136,7 @@
name: form.value.field,
});
Message.success(t('project.fileManagement.addSubModuleSuccess'));
reset(false);
emit('addFinish', form.value.field);
} else if (props.mode === 'rename') {
//
@ -137,6 +145,7 @@
name: form.value.field,
});
Message.success(t('project.fileManagement.renameSuccess'));
reset(false);
emit('renameFinish', form.value.field);
}
if (done) {
@ -164,13 +173,6 @@
callback(t('project.fileManagement.nameExist'));
}
}
function reset(val: boolean) {
if (!val) {
form.value.field = '';
formRef.value?.resetFields();
}
}
</script>
<style lang="less" scoped></style>

View File

@ -20,15 +20,7 @@
<MsButton type="text" @click="handleShowDetail(record.id)">{{ record.num }}</MsButton>
</template>
<template #name="{ record }">
<a-tooltip :content="record.title">
<div class="one-line-text max-w-[calc(100%-32px)]"> {{ record.title }}</div>
</a-tooltip>
<a-popover class="bug-content-popover" title="" position="right" style="width: 480px">
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
<template #content>
<div v-dompurify-html="record.content" class="markdown-body bug-content"> </div>
</template>
</a-popover>
<BugNamePopover :name="record.title" :content="record.content" />
</template>
<template #linkCase="{ record }">
<CaseCountPopover :bug-item="record" />
@ -54,6 +46,7 @@
import useTable from '@/components/pure/ms-table/useTable';
import CaseCountPopover from './caseCountPopover.vue';
import BugDetailDrawer from '@/views/bug-management/components/bug-detail-drawer.vue';
import BugNamePopover from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/bugNamePopover.vue';
import { getCustomOptionHeader, getPlatform } from '@/api/modules/bug-management';
import { planDetailBugPage } from '@/api/modules/test-plan/testPlan';