feat(缺陷管理): 详情编辑
This commit is contained in:
parent
d6c1b9f55a
commit
01d8747468
|
@ -4,7 +4,7 @@ import MSR from '@/api/http/index';
|
||||||
import * as bugURL from '@/api/requrls/bug-management';
|
import * as bugURL from '@/api/requrls/bug-management';
|
||||||
|
|
||||||
import { BugEditFormObject, BugExportParams, BugListItem } from '@/models/bug-management';
|
import { BugEditFormObject, BugExportParams, BugListItem } from '@/models/bug-management';
|
||||||
import { AssociatedList, OperationFile } from '@/models/caseManagement/featureCase';
|
import { AssociatedList, CreateOrUpdateDemand, DemandItem, OperationFile } from '@/models/caseManagement/featureCase';
|
||||||
import { CommonList, TableQueryParams, TemplateOption } from '@/models/common';
|
import { CommonList, TableQueryParams, TemplateOption } from '@/models/common';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,3 +181,30 @@ export function deleteSingleByRecycle(id: string) {
|
||||||
export function deleteBatchByRecycle(data: TableQueryParams) {
|
export function deleteBatchByRecycle(data: TableQueryParams) {
|
||||||
return MSR.post({ url: bugURL.getBatchDeleteUrl, data });
|
return MSR.post({ url: bugURL.getBatchDeleteUrl, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------关联需求
|
||||||
|
|
||||||
|
// 已关联需求列表
|
||||||
|
export function getAssociatedList(data: TableQueryParams) {
|
||||||
|
return MSR.post<CommonList<DemandItem[]>>({ url: bugURL.getDemandListUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缺陷管理-关联用例-未关联用例-列表分页
|
||||||
|
export function getUnAssociatedList(data: TableQueryParams) {
|
||||||
|
return MSR.post({ url: bugURL.getUnrelatedDemandListUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未关联用例-模块树
|
||||||
|
export function getModuleTree(data: TableQueryParams) {
|
||||||
|
return MSR.post({ url: `${bugURL.getUnrelatedModuleTreeUrl}`, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量关联需求
|
||||||
|
export function batchAssociation(data: TableQueryParams) {
|
||||||
|
return MSR.post({ url: bugURL.postAddDemandUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消关联
|
||||||
|
export function cancelAssociation(id: string) {
|
||||||
|
return MSR.get({ url: `${bugURL.getCancelDemandUrl}/${id}` });
|
||||||
|
}
|
||||||
|
|
|
@ -51,3 +51,16 @@ export const getBatchRecoverUrl = '/bug/trash/batch-recover';
|
||||||
export const getDeleteSingleUrl = '/bug/trash/delete/';
|
export const getDeleteSingleUrl = '/bug/trash/delete/';
|
||||||
// 批量删除
|
// 批量删除
|
||||||
export const getBatchDeleteUrl = '/bug/trash/batch-delete';
|
export const getBatchDeleteUrl = '/bug/trash/batch-delete';
|
||||||
|
|
||||||
|
// 获取关联的需求列表
|
||||||
|
export const getDemandListUrl = '/bug/case/page';
|
||||||
|
// 批量添加关联
|
||||||
|
export const postAddDemandUrl = '/bug/case/relate';
|
||||||
|
// 单个取消关联
|
||||||
|
export const getCancelDemandUrl = '/bug/case/un-relate/';
|
||||||
|
// 未关联的用例列表
|
||||||
|
export const getUnrelatedDemandListUrl = '/bug/case/un-relate/page';
|
||||||
|
// 未关联的模块树
|
||||||
|
export const getUnrelatedModuleTreeUrl = '/bug/case/un-relate/module/tree';
|
||||||
|
// 未关联的模块树 数量
|
||||||
|
export const getUnrelatedModuleTreeCountUrl = '/bug/case/un-relate/module/count';
|
||||||
|
|
|
@ -88,20 +88,19 @@
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t('bugManagement.detail.detail') }}
|
{{ t('bugManagement.detail.detail') }}
|
||||||
</template>
|
</template>
|
||||||
<BugDetailTab />
|
<BugDetailTab :allow-edit="true" :detail-info="detailInfo" @update-success="updateSuccess" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="case">
|
<a-tab-pane key="case">
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t('bugManagement.detail.case') }}
|
{{ t('bugManagement.detail.case') }}
|
||||||
<a-badge class="relative top-1 ml-1" :count="1000" :max-count="99" />
|
|
||||||
</template>
|
</template>
|
||||||
<BugCaseTab />
|
<BugCaseTab :bug-id="detailInfo.id" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="comment">
|
<a-tab-pane key="comment">
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t('bugManagement.detail.comment') }}
|
{{ t('bugManagement.detail.comment') }}
|
||||||
</template>
|
</template>
|
||||||
<CommentTab ref="commentRef" bug-id="detailInfo.id" />
|
<CommentTab ref="commentRef" :bug-id="detailInfo.id" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,6 +118,7 @@
|
||||||
:form-rule="formRules"
|
:form-rule="formRules"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:option="options"
|
:option="options"
|
||||||
|
@change="handleOK"
|
||||||
/>
|
/>
|
||||||
<!-- 自定义字段结束 -->
|
<!-- 自定义字段结束 -->
|
||||||
<div class="baseItem">
|
<div class="baseItem">
|
||||||
|
@ -175,6 +175,7 @@
|
||||||
followBug,
|
followBug,
|
||||||
getBugDetail,
|
getBugDetail,
|
||||||
getTemplateById,
|
getTemplateById,
|
||||||
|
updateBug,
|
||||||
} from '@/api/modules/bug-management/index';
|
} from '@/api/modules/bug-management/index';
|
||||||
import useFullScreen from '@/hooks/useFullScreen';
|
import useFullScreen from '@/hooks/useFullScreen';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -214,7 +215,7 @@
|
||||||
|
|
||||||
const activeTab = ref<string | number>('detail');
|
const activeTab = ref<string | number>('detail');
|
||||||
|
|
||||||
const detailInfo = ref<Record<string, any>>({}); // 存储当前详情信息,通过loadBug 获取
|
const detailInfo = ref<Record<string, any>>({ match: [] }); // 存储当前详情信息,通过loadBug 获取
|
||||||
const tags = ref([]);
|
const tags = ref([]);
|
||||||
|
|
||||||
// 处理表单格式
|
// 处理表单格式
|
||||||
|
@ -332,6 +333,25 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOK = async () => {
|
||||||
|
const values = await fApi.value.validate();
|
||||||
|
if (values) {
|
||||||
|
const params = {
|
||||||
|
id: detailInfo.value.id,
|
||||||
|
projectId: currentProjectId.value,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
await updateBug(params);
|
||||||
|
Message.success(t('common.editSuccess'));
|
||||||
|
detailDrawerRef.value?.initDetail();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 表单配置项
|
// 表单配置项
|
||||||
const options = {
|
const options = {
|
||||||
resetBtn: false, // 不展示默认配置的重置和提交
|
resetBtn: false, // 不展示默认配置的重置和提交
|
||||||
|
|
|
@ -1,148 +1,286 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="p-[16px]">
|
<div>
|
||||||
<div class="flex flex-row justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<a-dropdown trigger="hover">
|
<a-dropdown @select="handleSelect">
|
||||||
|
<a-button type="primary"> {{ t('caseManagement.featureCase.linkCase') }} </a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="showRelatedDrawer('api')">{{ t('bugManagement.detail.apiCase') }}</a-doption>
|
<a-doption v-for="item of caseTypeOptions" :key="item.value" :value="item.value">{{
|
||||||
<a-doption @click="showRelatedDrawer('scenario')">{{ t('bugManagement.detail.scenarioCase') }}</a-doption>
|
t(item.label)
|
||||||
<a-doption @click="showRelatedDrawer('ui')">{{ t('bugManagement.detail.uiCase') }}</a-doption>
|
|
||||||
<a-doption @click="showRelatedDrawer('performance')">{{
|
|
||||||
t('bugManagement.detail.performanceCase')
|
|
||||||
}}</a-doption>
|
}}</a-doption>
|
||||||
</template>
|
</template>
|
||||||
<a-button type="primary">{{ t('bugManagement.edit.linkCase') }}</a-button>
|
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
v-model:model-value="keyword"
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('bugManagement.detail.searchCase')"
|
class="mx-[8px] w-[240px]"
|
||||||
class="w-[230px]"
|
@search="searchCase"
|
||||||
@search="searchUser"
|
@press-enter="searchCase"
|
||||||
@press-enter="searchUser"
|
|
||||||
></a-input-search>
|
></a-input-search>
|
||||||
</div>
|
</div>
|
||||||
<ms-base-table class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
|
<ms-base-table v-bind="propsRes" v-on="propsEvent">
|
||||||
<template #name="{ record }">
|
<template #defectName="{ record }">
|
||||||
<span>{{ record.name }}</span>
|
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||||
<span v-if="record.adminFlag" class="ml-[4px] text-[var(--color-text-4)]">{{ `(${t('common.admin')})` }}</span>
|
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsRemoveButton
|
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||||
:title="t('system.organization.removeName', { name: record.name })"
|
</template>
|
||||||
:sub-title-tip="t('system.organization.removeTip')"
|
<template v-if="(keyword || '').trim() === ''" #empty>
|
||||||
@ok="handleRemove()"
|
<div class="flex w-full items-center justify-center">
|
||||||
/>
|
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||||
|
<a-dropdown @select="handleSelect">
|
||||||
|
<MsButton class="ml-[8px]">
|
||||||
|
{{ t('caseManagement.featureCase.linkCase') }}
|
||||||
|
</MsButton>
|
||||||
|
<template #content>
|
||||||
|
<a-doption v-for="item of caseTypeOptions" :key="item.value" :value="item.value">{{
|
||||||
|
t(item.label)
|
||||||
|
}}</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
</div>
|
<MsCaseAssociate
|
||||||
<MsDrawer
|
v-model:visible="innerVisible"
|
||||||
:width="680"
|
v-model:project-id="innerProject"
|
||||||
:visible="relatedVisible"
|
v-model:currentSelectCase="currentSelectCase"
|
||||||
unmount-on-close
|
:ok-button-disabled="associateForm.reviewers.length === 0"
|
||||||
:footer="false"
|
:get-modules-func="getModuleTree"
|
||||||
:mask="false"
|
:modules-params="modulesTreeParams"
|
||||||
@cancel="relatedVisible = false"
|
:get-table-func="getUnAssociatedList"
|
||||||
|
:table-params="getTableParams"
|
||||||
|
:modules-count="modulesCount"
|
||||||
|
:module-options="caseTypeOptions"
|
||||||
|
:confirm-loading="confirmLoading"
|
||||||
|
:case-id="props.bugId"
|
||||||
|
:associated-ids="associatedIds"
|
||||||
|
:type="RequestModuleEnum.API_CASE"
|
||||||
|
@close="emit('close')"
|
||||||
|
@save="saveHandler"
|
||||||
>
|
>
|
||||||
<template #title>
|
</MsCaseAssociate>
|
||||||
<div class="flex flex-row items-center gap-[4px]">
|
|
||||||
<div>{{ t('bugManagement.detail.relatedCase') }}</div>
|
|
||||||
<a-select>
|
|
||||||
<a-option>1</a-option>
|
|
||||||
<a-option>2</a-option>
|
|
||||||
<a-option>3</a-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
</MsDrawer>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import MsRemoveButton from '@/components/business/ms-remove-button/MsRemoveButton.vue';
|
import MsCaseAssociate from '@/components/business/ms-case-associate/index.vue';
|
||||||
|
import { RequestModuleEnum } from '@/components/business/ms-case-associate/utils';
|
||||||
|
|
||||||
import { postProjectMemberByProjectId } from '@/api/modules/setting/organizationAndProject';
|
import {
|
||||||
|
batchAssociation,
|
||||||
|
cancelAssociation,
|
||||||
|
getAssociatedList,
|
||||||
|
getModuleTree,
|
||||||
|
getUnAssociatedList,
|
||||||
|
} from '@/api/modules/bug-management';
|
||||||
|
import { postTabletList } from '@/api/modules/project-management/menuManagement';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { formatPhoneNumber } from '@/utils';
|
import { useAppStore } from '@/store';
|
||||||
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
|
|
||||||
|
import type { TableQueryParams } from '@/models/common';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
import Message from '@arco-design/web-vue/es/message';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const relatedVisible = ref(false);
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
const relatedType = ref('api');
|
|
||||||
|
|
||||||
const showRelatedDrawer = (type: string) => {
|
const props = defineProps<{
|
||||||
relatedType.value = type;
|
bugId: string; // 缺陷id
|
||||||
};
|
}>();
|
||||||
|
|
||||||
const keyword = ref('');
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', val: boolean): void;
|
||||||
|
(e: 'update:project', val: string): void;
|
||||||
|
(e: 'success', val: string[]): void;
|
||||||
|
(e: 'close'): void;
|
||||||
|
}>();
|
||||||
|
const keyword = ref<string>('');
|
||||||
|
|
||||||
const projectColumn: MsTableColumn = [
|
const columns: MsTableColumn = [
|
||||||
{
|
{
|
||||||
title: 'system.organization.userName',
|
title: 'caseManagement.featureCase.tableColumnID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: 200,
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.tableColumnName',
|
||||||
slotName: 'name',
|
slotName: 'name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
|
showInTable: true,
|
||||||
showTooltip: true,
|
showTooltip: true,
|
||||||
width: 200,
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'system.organization.email',
|
title: 'caseManagement.featureCase.projectName',
|
||||||
dataIndex: 'email',
|
slotName: 'projectName',
|
||||||
|
dataIndex: 'projectName',
|
||||||
|
showInTable: true,
|
||||||
showTooltip: true,
|
showTooltip: true,
|
||||||
width: 200,
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'system.organization.phone',
|
title: 'caseManagement.featureCase.tableColumnVersion',
|
||||||
dataIndex: 'phone',
|
slotName: 'version',
|
||||||
|
dataIndex: 'version',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.changeType',
|
||||||
|
slotName: 'type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.tableColumnActions',
|
||||||
|
slotName: 'operation',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 140,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
},
|
},
|
||||||
{ title: 'system.organization.operation', slotName: 'operation' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const { propsRes, propsEvent, loadList, setKeyword } = useTable(
|
const { propsRes, propsEvent, loadList, setLoadListParams, setKeyword } = useTable(getAssociatedList, {
|
||||||
postProjectMemberByProjectId,
|
columns,
|
||||||
{
|
|
||||||
heightUsed: 240,
|
|
||||||
columns: projectColumn,
|
|
||||||
scroll: { x: '100%' },
|
scroll: { x: '100%' },
|
||||||
selectable: false,
|
heightUsed: 340,
|
||||||
noDisable: false,
|
enableDrag: true,
|
||||||
pageSimple: true,
|
});
|
||||||
},
|
|
||||||
(record) => {
|
|
||||||
return {
|
|
||||||
...record,
|
|
||||||
phone: formatPhoneNumber(record.phone || ''),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function searchUser() {
|
const innerVisible = ref(false);
|
||||||
|
const innerProject = ref(currentProjectId.value);
|
||||||
|
|
||||||
|
const associateForm = ref({
|
||||||
|
reviewers: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const associatedIds = ref<string[]>([]);
|
||||||
|
|
||||||
|
const currentSelectCase = ref<string>('');
|
||||||
|
|
||||||
|
const modulesTreeParams = ref<TableQueryParams>({});
|
||||||
|
|
||||||
|
const getTableParams = ref<TableQueryParams>({});
|
||||||
|
|
||||||
|
function handleSelect(value: string | number | Record<string, any> | undefined) {
|
||||||
|
currentSelectCase.value = value as string;
|
||||||
|
innerVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFetch() {
|
||||||
|
setLoadListParams({
|
||||||
|
keyword: keyword.value,
|
||||||
|
bugId: props.bugId,
|
||||||
|
});
|
||||||
|
await loadList();
|
||||||
|
const { msPagination } = propsRes.value;
|
||||||
|
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cancelLink(record: any) {
|
||||||
|
try {
|
||||||
|
const { id } = record;
|
||||||
|
await cancelAssociation(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const caseTypeOptions = ref<{ label: string; value: string }[]>([]);
|
||||||
|
|
||||||
|
const modulesCount = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
async function saveHandler(params: TableQueryParams) {
|
||||||
|
try {
|
||||||
|
confirmLoading.value = true;
|
||||||
|
await batchAssociation(params);
|
||||||
|
Message.success(t('caseManagement.featureCase.AssociatedSuccess'));
|
||||||
|
innerVisible.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
confirmLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleMaps: Record<string, { label: string; value: string }[]> = {
|
||||||
|
apiTest: [
|
||||||
|
{
|
||||||
|
value: 'API',
|
||||||
|
label: t('caseManagement.featureCase.apiCase'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'SCENARIO',
|
||||||
|
label: t('caseManagement.featureCase.sceneCase'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
uiTest: [
|
||||||
|
{
|
||||||
|
value: 'UI',
|
||||||
|
label: t('caseManagement.featureCase.uiCase'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loadTest: [
|
||||||
|
{
|
||||||
|
value: 'PERFORMANCE',
|
||||||
|
label: t('caseManagement.featureCase.propertyCase'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getEnabledModules() {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
currentSelectCase.value = caseTypeOptions.value[0].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchCase() {
|
||||||
setKeyword(keyword.value);
|
setKeyword(keyword.value);
|
||||||
await loadList();
|
await loadList();
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchData = async () => {
|
onMounted(async () => {
|
||||||
await loadList();
|
getEnabledModules();
|
||||||
};
|
getFetch();
|
||||||
|
});
|
||||||
const handleRemove = async () => {
|
|
||||||
try {
|
|
||||||
Message.success(t('common.removeSuccess'));
|
|
||||||
fetchData();
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style scoped></style>
|
||||||
:deep(.custom-height) {
|
|
||||||
height: 100vh !important;
|
|
||||||
border: 1px solid red;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -2,111 +2,479 @@
|
||||||
<div class="p-[16px]">
|
<div class="p-[16px]">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="header-title">{{ t('bugManagement.edit.content') }}</div>
|
<div class="header-title">{{ t('bugManagement.edit.content') }}</div>
|
||||||
<div class="header-action">
|
<div v-if="!contentEditAble" v-permission="['PROJECT_BUG:READ+UPDATE']" class="header-action">
|
||||||
<a-button>
|
<a-button type="text" @click="contentEditAble = true">
|
||||||
<template #icon> <MsIconfont type="icon-icon_edit_outlined" /> </template>
|
<template #icon> <MsIconfont type="icon-icon_edit_outlined" /> </template>
|
||||||
{{ t('bugManagement.edit.contentEdit') }}
|
{{ t('bugManagement.edit.contentEdit') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header">
|
|
||||||
<div class="header-title">{{ t('bugManagement.edit.content') }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mt-[8]" :class="{ 'max-h-[260px]': contentEditAble }">
|
<div class="mt-[8]" :class="{ 'max-h-[260px]': contentEditAble }">
|
||||||
<MsRichText
|
<MsRichText
|
||||||
v-if="form.content"
|
v-if="contentEditAble"
|
||||||
v-model:raw="form.content"
|
v-model:raw="form.content"
|
||||||
|
v-model:filed-ids="fileIds"
|
||||||
:disabled="!contentEditAble"
|
:disabled="!contentEditAble"
|
||||||
:placeholder="t('bugManagement.edit.contentPlaceholder')"
|
:placeholder="t('bugManagement.edit.contentPlaceholder')"
|
||||||
|
:upload-image="handleUploadImage"
|
||||||
/>
|
/>
|
||||||
<div v-else>-</div>
|
<div v-else v-dompurify-html="form?.content || '-'" class="text-[var(--color-text-3)]"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="contentEditAble" class="flex justify-end">
|
||||||
|
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button>
|
||||||
|
<a-button class="ml-[12px]" type="primary" :loading="confirmLoading">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
</a-button></div
|
||||||
|
>
|
||||||
|
<div v-if="props.allowEdit">
|
||||||
|
<div class="font-medium text-[var(--color-text-1)]">
|
||||||
|
{{ t('bugManagement.edit.file') }}
|
||||||
</div>
|
</div>
|
||||||
<a-dropdown trigger="hover">
|
<div class="mb-1">
|
||||||
|
<a-dropdown position="tr" trigger="hover">
|
||||||
|
<a-button v-permission="['PROJECT_BUG:READ+UPDATE']" type="outline">
|
||||||
|
<template #icon> <icon-plus class="text-[14px]" /> </template
|
||||||
|
>{{ t('system.orgTemplate.addAttachment') }}</a-button
|
||||||
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<MsUpload
|
<a-upload
|
||||||
|
ref="uploadRef"
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
multiple
|
:show-file-list="false"
|
||||||
draggable
|
:before-upload="beforeUpload"
|
||||||
accept="unknown"
|
|
||||||
is-limit
|
|
||||||
size-unit="MB"
|
|
||||||
:max-size="500"
|
|
||||||
>
|
>
|
||||||
<a-doption>{{ t('bugManagement.edit.localUpload') }}</a-doption>
|
<template #upload-button>
|
||||||
</MsUpload>
|
<a-button type="text" class="!text-[var(--color-text-1)]">
|
||||||
<a-doption @click="handleLineFile">{{ t('bugManagement.edit.linkFile') }}</a-doption>
|
<icon-upload />{{ t('caseManagement.featureCase.uploadFile') }}
|
||||||
</template>
|
|
||||||
<a-button type="outline">
|
|
||||||
<template #icon>
|
|
||||||
<icon-plus />
|
|
||||||
</template>
|
|
||||||
{{ t('bugManagement.edit.uploadFile') }}
|
|
||||||
</a-button>
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-upload>
|
||||||
|
<a-button type="text" class="!text-[var(--color-text-1)]" @click="associatedFile">
|
||||||
|
<MsIcon type="icon-icon_link-copy_outlined" size="16" />
|
||||||
|
{{ t('caseManagement.featureCase.associatedFile') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mb-[8px] mt-[2px] text-[var(--color-text-4)]">{{ t('bugManagement.edit.fileExtra') }}</div>
|
<div class="mb-[8px] mt-[2px] text-[var(--color-text-4)]">{{ t('bugManagement.edit.fileExtra') }}</div>
|
||||||
<FileList
|
<MsFileList
|
||||||
|
ref="fileListRef"
|
||||||
|
v-model:file-list="fileList"
|
||||||
:show-tab="false"
|
:show-tab="false"
|
||||||
:file-list="fileList"
|
:request-params="{
|
||||||
:upload-func="uploadFile"
|
bugId: bugId,
|
||||||
@delete-file="deleteFile"
|
projectId: currentProjectId,
|
||||||
@reupload="reupload"
|
}"
|
||||||
@handle-preview="handlePreview"
|
:upload-func="uploadOrAssociationFile"
|
||||||
|
:handle-delete="deleteFileHandler"
|
||||||
|
:show-delete="props.allowEdit"
|
||||||
>
|
>
|
||||||
</FileList>
|
<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,
|
||||||
|
bugId: bugId,
|
||||||
|
fileId: item.uid,
|
||||||
|
local: true,
|
||||||
|
}"
|
||||||
|
@success="emit('updateSuccess')"
|
||||||
|
/>
|
||||||
|
<MsButton
|
||||||
|
v-if="item.status === 'done'"
|
||||||
|
type="button"
|
||||||
|
status="primary"
|
||||||
|
class="!mr-[4px]"
|
||||||
|
@click="downloadFile(item)"
|
||||||
|
>
|
||||||
|
{{ t('caseManagement.featureCase.download') }}
|
||||||
|
</MsButton>
|
||||||
|
</div>
|
||||||
|
<!-- 关联文件 -->
|
||||||
|
<div v-else class="flex flex-nowrap">
|
||||||
|
<MsButton
|
||||||
|
v-if="item.status !== 'init'"
|
||||||
|
type="button"
|
||||||
|
status="primary"
|
||||||
|
class="!mr-[4px]"
|
||||||
|
@click="handlePreview(item)"
|
||||||
|
>
|
||||||
|
{{ t('ms.upload.preview') }}
|
||||||
|
</MsButton>
|
||||||
|
<MsButton
|
||||||
|
v-if="item.status === 'done'"
|
||||||
|
type="button"
|
||||||
|
status="primary"
|
||||||
|
class="!mr-[4px]"
|
||||||
|
@click="downloadFile(item)"
|
||||||
|
>
|
||||||
|
{{ 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>
|
||||||
|
<RelateFileDrawer
|
||||||
|
v-model:visible="associatedDrawer"
|
||||||
|
:get-tree-request="getModules"
|
||||||
|
:get-count-request="getModulesCount"
|
||||||
|
:get-list-request="getAssociatedFileList"
|
||||||
|
:get-list-fun-params="getListFunParams"
|
||||||
|
@save="saveSelectAssociatedFile"
|
||||||
|
/>
|
||||||
|
<a-image-preview v-model:visible="previewVisible" :src="imageUrl" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FileItem } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsIconfont from '@/components/pure/ms-icon-font/index.vue';
|
import MsIconfont from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||||
import FileList from '@/components/pure/ms-upload/fileList.vue';
|
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
||||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
|
import RelateFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
|
||||||
|
import TransferModal from '@/views/case-management/caseManagementFeature/components/tabContent/transferModal.vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
checkFileIsUpdateRequest,
|
||||||
|
deleteFileOrCancelAssociation,
|
||||||
|
downloadFileRequest,
|
||||||
|
editorUploadFile,
|
||||||
|
getAssociatedFileList,
|
||||||
|
previewFile,
|
||||||
|
transferFileRequest,
|
||||||
|
updateFile,
|
||||||
|
uploadOrAssociationFile,
|
||||||
|
} from '@/api/modules/bug-management';
|
||||||
|
import { updateCaseRequest } from '@/api/modules/case-management/featureCase';
|
||||||
|
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import { downloadByteFile, sleep } from '@/utils';
|
||||||
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
|
import { BugEditFormObject } from '@/models/bug-management';
|
||||||
|
import { AssociatedList, AttachFileInfo } from '@/models/caseManagement/featureCase';
|
||||||
|
import { TableQueryParams } from '@/models/common';
|
||||||
|
|
||||||
|
import { convertToFileByBug } from '@/views/bug-management/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
detailInfo: BugEditFormObject;
|
||||||
|
allowEdit?: boolean; // 是否允许编辑
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'updateSuccess'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const transferVisible = ref<boolean>(false);
|
||||||
|
const previewVisible = ref<boolean>(false);
|
||||||
|
// 富文本附件id
|
||||||
|
const fileIds = ref<string[]>([]);
|
||||||
|
const imageUrl = ref<string>('');
|
||||||
|
const associatedDrawer = ref(false);
|
||||||
|
const fileListRef = ref<InstanceType<typeof MsFileList>>();
|
||||||
|
// 富文本编辑器是否可编辑
|
||||||
const contentEditAble = ref(false);
|
const contentEditAble = ref(false);
|
||||||
const fileList = ref<FileItem[]>([]);
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
|
const fileList = ref<MsFileItem[]>([]);
|
||||||
|
const bugId = computed(() => props.detailInfo.id);
|
||||||
|
const attachmentsList = ref<AttachFileInfo[]>([]);
|
||||||
|
const getListFunParams = ref<TableQueryParams>({
|
||||||
|
combine: {
|
||||||
|
hiddenIds: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
const form = ref({
|
const form = ref({
|
||||||
content: '',
|
content: '',
|
||||||
fileList: [],
|
deleteLocalFileIds: [] as string[],
|
||||||
|
unLinkRefIds: [] as string[],
|
||||||
|
linkFileIds: [] as string[],
|
||||||
});
|
});
|
||||||
const uploadFile = (file: File) => {
|
|
||||||
const fileItem: FileItem = {
|
const handleFileFunc = async (attachments: AttachFileInfo[]) => {
|
||||||
uid: `${Date.now()}`,
|
if (attachments && attachments.length) {
|
||||||
name: file.name,
|
attachmentsList.value = attachments;
|
||||||
status: 'init',
|
// 检查文件是否有更新
|
||||||
file,
|
const checkUpdateFileIds = await checkFileIsUpdateRequest(attachments.map((item: any) => item.fileId));
|
||||||
|
// 处理文件列表
|
||||||
|
fileList.value =
|
||||||
|
attachments
|
||||||
|
.map((fileInfo: any) => {
|
||||||
|
return {
|
||||||
|
...fileInfo,
|
||||||
|
name: fileInfo.fileName,
|
||||||
|
isUpdateFlag: checkUpdateFileIds.includes(fileInfo.id),
|
||||||
};
|
};
|
||||||
fileList.value.push(fileItem);
|
})
|
||||||
return Promise.resolve(fileItem);
|
.map((fileInfo: any) => {
|
||||||
};
|
return convertToFileByBug(fileInfo);
|
||||||
const handlePreview = (item: FileItem) => {
|
}) || [];
|
||||||
const { url } = item;
|
}
|
||||||
window.open(url);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteFile = (item: FileItem) => {
|
function handleCancel() {
|
||||||
fileList.value = fileList.value.filter((e) => e.uid !== item.uid);
|
contentEditAble.value = false;
|
||||||
|
}
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
// function handleOK() {
|
||||||
|
// caseFormRef.value?.validate().then(async (res: any) => {
|
||||||
|
// if (!res) {
|
||||||
|
// try {
|
||||||
|
// confirmLoading.value = true;
|
||||||
|
// await updateCaseRequest();
|
||||||
|
// Message.success(t('caseManagement.featureCase.editSuccess'));
|
||||||
|
// handleCancel();
|
||||||
|
// emit('updateSuccess');
|
||||||
|
// } catch (error) {
|
||||||
|
// // eslint-disable-next-line no-console
|
||||||
|
// console.log(error);
|
||||||
|
// } finally {
|
||||||
|
// confirmLoading.value = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
const initCurrentDetail = async (detail: BugEditFormObject) => {
|
||||||
|
const { attachments, content } = detail;
|
||||||
|
form.value.content = content;
|
||||||
|
handleFileFunc(attachments);
|
||||||
};
|
};
|
||||||
|
|
||||||
const reupload = (item: FileItem) => {
|
// 删除本地文件
|
||||||
fileList.value = fileList.value.map((e) => {
|
async function deleteFileHandler(item: MsFileItem) {
|
||||||
if (e.uid === item.uid) {
|
try {
|
||||||
|
const params = {
|
||||||
|
id: item.uid,
|
||||||
|
local: item.local,
|
||||||
|
bugId,
|
||||||
|
projectId: currentProjectId.value,
|
||||||
|
};
|
||||||
|
await deleteFileOrCancelAssociation(params);
|
||||||
|
Message.success(
|
||||||
|
item.local ? t('caseManagement.featureCase.deleteSuccess') : t('caseManagement.featureCase.cancelLinkSuccess')
|
||||||
|
);
|
||||||
|
emit('updateSuccess');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeUpload(file: File) {
|
||||||
|
const _maxSize = 50 * 1024 * 1024;
|
||||||
|
if (file.size > _maxSize) {
|
||||||
|
Message.warning(t('ms.upload.overSize'));
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(_fileList: MsFileItem[]) {
|
||||||
|
fileList.value = _fileList.map((e) => {
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
status: 'init',
|
enable: true, // 是否启用
|
||||||
|
local: true, // 是否本地文件
|
||||||
};
|
};
|
||||||
}
|
|
||||||
return e;
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLineFile = () => {};
|
// 预览图片
|
||||||
|
async function handlePreview(item: MsFileItem) {
|
||||||
|
try {
|
||||||
|
previewVisible.value = true;
|
||||||
|
if (item.status !== 'init') {
|
||||||
|
const res = await previewFile({
|
||||||
|
projectId: currentProjectId.value,
|
||||||
|
bugId: bugId.value as string,
|
||||||
|
fileId: item.uid,
|
||||||
|
associated: !item.local,
|
||||||
|
});
|
||||||
|
const blob = new Blob([res], { type: 'image/jpeg' });
|
||||||
|
imageUrl.value = URL.createObjectURL(blob);
|
||||||
|
} else {
|
||||||
|
imageUrl.value = item.url || '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载
|
||||||
|
async function downloadFile(item: MsFileItem) {
|
||||||
|
try {
|
||||||
|
const res = await downloadFileRequest({
|
||||||
|
projectId: currentProjectId.value,
|
||||||
|
bugId: bugId.value as string,
|
||||||
|
fileId: item.uid,
|
||||||
|
associated: !item.local,
|
||||||
|
});
|
||||||
|
downloadByteFile(res, `${item.name}`);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function associatedFile() {
|
||||||
|
associatedDrawer.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理关联文件
|
||||||
|
function saveSelectAssociatedFile(fileData: AssociatedList[]) {
|
||||||
|
const fileResultList = fileData.map((fileInfo) => convertToFileByBug(fileInfo));
|
||||||
|
fileList.value.push(...fileResultList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后台传过来的local文件的item列表
|
||||||
|
const oldLocalFileList = computed(() => {
|
||||||
|
return attachmentsList.value.filter((item) => item.local).map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 已经关联过的id列表
|
||||||
|
const associateFileIds = computed(() => {
|
||||||
|
return attachmentsList.value.filter((item: any) => !item.local).map((item: any) => item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前新增传过来的关联list
|
||||||
|
const currentAlreadyAssociateFileList = computed(() => {
|
||||||
|
return fileList.value
|
||||||
|
.filter((item) => !item.local && !associateFileIds.value.includes(item.uid))
|
||||||
|
.map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 后台已保存本地文件的item列表
|
||||||
|
const currentOldLocalFileList = computed(() => {
|
||||||
|
return fileList.value.filter((item) => item.local && item.status !== 'init').map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增关联文件ID列表
|
||||||
|
const newAssociateFileListIds = computed(() => {
|
||||||
|
return fileList.value
|
||||||
|
.filter((item: any) => !item.local && !associateFileIds.value.includes(item.uid))
|
||||||
|
.map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取消关联文件id TODO
|
||||||
|
const unLinkFilesIds = computed(() => {
|
||||||
|
const deleteAssociateFileIds = fileList.value
|
||||||
|
.filter(
|
||||||
|
(item: any) =>
|
||||||
|
!currentAlreadyAssociateFileList.value.includes(item.uid) && associateFileIds.value.includes(item.uid)
|
||||||
|
)
|
||||||
|
.map((item) => item.uid);
|
||||||
|
return associateFileIds.value.filter(
|
||||||
|
(id: string) => !currentAlreadyAssociateFileList.value.includes(id) && !deleteAssociateFileIds.includes(id)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除本地上传的文件id
|
||||||
|
const deleteFileMetaIds = computed(() => {
|
||||||
|
return oldLocalFileList.value
|
||||||
|
.filter((item: any) => !currentOldLocalFileList.value.includes(item.id))
|
||||||
|
.map((item: any) => item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理关联文件和已关联文件本地文件和已上传文本文件
|
||||||
|
function getFilesParams() {
|
||||||
|
form.value.deleteLocalFileIds = deleteFileMetaIds.value;
|
||||||
|
form.value.unLinkRefIds = unLinkFilesIds.value;
|
||||||
|
form.value.linkFileIds = newAssociateFileListIds.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startUpload() {
|
||||||
|
await sleep(300);
|
||||||
|
fileListRef.value?.startUpload();
|
||||||
|
emit('updateSuccess');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件列表单个上传
|
||||||
|
watch(
|
||||||
|
() => fileList.value,
|
||||||
|
async (val) => {
|
||||||
|
const isNewFiles = val.filter((item) => item.status === 'init').length;
|
||||||
|
if (val && isNewFiles) {
|
||||||
|
startUpload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新文件
|
||||||
|
async function handleUpdateFile(item: MsFileItem) {
|
||||||
|
try {
|
||||||
|
await updateFile(currentProjectId.value, item.associationId);
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUploadImage(file: File) {
|
||||||
|
const { data } = await editorUploadFile({
|
||||||
|
fileList: [file],
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监视文件列表处理关联和本地文件
|
||||||
|
watch(
|
||||||
|
() => fileList.value,
|
||||||
|
(val) => {
|
||||||
|
if (val) {
|
||||||
|
getListFunParams.value.combine.hiddenIds = fileList.value.filter((item) => !item.local).map((item) => item.uid);
|
||||||
|
getFilesParams();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
initCurrentDetail(props.detailInfo);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -115,6 +483,7 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
&-title {
|
&-title {
|
||||||
|
font-weight: 500;
|
||||||
color: var(--color-text-1);
|
color: var(--color-text-1);
|
||||||
}
|
}
|
||||||
&-action {
|
&-action {
|
||||||
|
|
|
@ -80,20 +80,20 @@
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isNextTip = ref<boolean>(false);
|
const isNextTip = ref<boolean>(false);
|
||||||
const countDown = ref<number>(5);
|
const countDown = ref<number>(15);
|
||||||
const timer = ref<any>(null);
|
const timer = ref<any>(null);
|
||||||
function setCountdown() {
|
function setCountdown() {
|
||||||
// timer.value = setInterval(() => {
|
timer.value = setInterval(() => {
|
||||||
// if (countDown.value > 1) {
|
if (countDown.value > 1) {
|
||||||
// --countDown.value;
|
--countDown.value;
|
||||||
// } else {
|
} else {
|
||||||
// clearInterval(timer.value);
|
clearInterval(timer.value);
|
||||||
// router.push({
|
router.push({
|
||||||
// name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
// }, 1000);
|
}, 1000);
|
||||||
timer.value = 5;
|
timer.value = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDoNotShowAgainChecked() {
|
function isDoNotShowAgainChecked() {
|
||||||
|
|
|
@ -218,6 +218,7 @@
|
||||||
} from '@/api/modules/bug-management';
|
} from '@/api/modules/bug-management';
|
||||||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useVisit from '@/hooks/useVisit';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { downloadByteFile } from '@/utils';
|
import { downloadByteFile } from '@/utils';
|
||||||
|
@ -274,9 +275,12 @@
|
||||||
const isEdit = computed(() => !!route.query.id && route.params.mode === 'edit');
|
const isEdit = computed(() => !!route.query.id && route.params.mode === 'edit');
|
||||||
const bugId = computed(() => route.query.id || '');
|
const bugId = computed(() => route.query.id || '');
|
||||||
const isEditOrCopy = computed(() => !!bugId.value);
|
const isEditOrCopy = computed(() => !!bugId.value);
|
||||||
|
const isCopy = computed(() => route.params.mode === 'copy');
|
||||||
const imageUrl = ref('');
|
const imageUrl = ref('');
|
||||||
const previewVisible = ref<boolean>(false);
|
const previewVisible = ref<boolean>(false);
|
||||||
const richTextFileIds = ref<string[]>([]);
|
const richTextFileIds = ref<string[]>([]);
|
||||||
|
const visitedKey = 'doNotNextTipCreateBug';
|
||||||
|
const { getIsVisited } = useVisit(visitedKey);
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
return isEdit.value ? t('bugManagement.editBug') : t('bugManagement.createBug');
|
return isEdit.value ? t('bugManagement.editBug') : t('bugManagement.createBug');
|
||||||
|
@ -507,10 +511,13 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const tmpObj = {
|
const tmpObj: BugEditFormObject = {
|
||||||
...form.value,
|
...form.value,
|
||||||
customFields,
|
customFields,
|
||||||
};
|
};
|
||||||
|
if (isCopy.value) {
|
||||||
|
delete tmpObj.id;
|
||||||
|
}
|
||||||
// 执行保存操作
|
// 执行保存操作
|
||||||
const res = await createOrUpdateBug({ request: tmpObj, fileList: fileList.value as unknown as File[] });
|
const res = await createOrUpdateBug({ request: tmpObj, fileList: fileList.value as unknown as File[] });
|
||||||
if (isEdit.value) {
|
if (isEdit.value) {
|
||||||
|
@ -520,6 +527,7 @@
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Message.success(t('common.createSuccess'));
|
Message.success(t('common.createSuccess'));
|
||||||
|
|
||||||
if (isContinue) {
|
if (isContinue) {
|
||||||
// 如果是保存并继续创建
|
// 如果是保存并继续创建
|
||||||
const { templateId } = form.value;
|
const { templateId } = form.value;
|
||||||
|
@ -536,6 +544,12 @@
|
||||||
fileList.value = [];
|
fileList.value = [];
|
||||||
} else {
|
} else {
|
||||||
// 否则跳转到成功页
|
// 否则跳转到成功页
|
||||||
|
if (getIsVisited()) {
|
||||||
|
router.push({
|
||||||
|
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
router.push({
|
router.push({
|
||||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
|
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
|
||||||
query: {
|
query: {
|
||||||
|
|
Loading…
Reference in New Issue