feat(用例评审): 用例评审Done
This commit is contained in:
parent
f83f94bac2
commit
067e7dc689
|
@ -145,25 +145,21 @@
|
||||||
|
|
||||||
const cardOverHeight = computed(() => {
|
const cardOverHeight = computed(() => {
|
||||||
if (isFullScreen.value) {
|
if (isFullScreen.value) {
|
||||||
if (props.simple) {
|
|
||||||
// 简单模式没有标题、没有底部
|
|
||||||
return props.noContentPadding ? 0 : 48;
|
|
||||||
}
|
|
||||||
if (props.hideFooter) {
|
if (props.hideFooter) {
|
||||||
// 隐藏底部
|
// 隐藏底部
|
||||||
return props.noContentPadding ? 88 : 118;
|
return 62;
|
||||||
}
|
}
|
||||||
return 246;
|
return 142;
|
||||||
}
|
}
|
||||||
if (props.simple) {
|
if (props.simple) {
|
||||||
// 简单模式没有标题、没有底部
|
// 简单模式没有标题、没有底部
|
||||||
return props.noContentPadding ? 88 + _specialHeight : 136 + _specialHeight;
|
return props.noContentPadding ? 76 + _specialHeight : 124 + _specialHeight;
|
||||||
}
|
}
|
||||||
if (props.hideFooter) {
|
if (props.hideFooter) {
|
||||||
// 隐藏底部
|
// 隐藏底部
|
||||||
return props.noContentPadding ? 152 + _specialHeight : 192 + _specialHeight;
|
return props.noContentPadding ? 140 + _specialHeight : 180 + _specialHeight;
|
||||||
}
|
}
|
||||||
return 246 + _specialHeight;
|
return 234 + _specialHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
const getComputedContentStyle = computed(() => {
|
const getComputedContentStyle = computed(() => {
|
||||||
|
|
|
@ -517,6 +517,8 @@
|
||||||
:deep(.halo-rich-text-editor) {
|
:deep(.halo-rich-text-editor) {
|
||||||
padding: 16px 24px !important;
|
padding: 16px 24px !important;
|
||||||
.editor-header {
|
.editor-header {
|
||||||
|
.ms-scroll-bar();
|
||||||
|
|
||||||
justify-content: start !important;
|
justify-content: start !important;
|
||||||
}
|
}
|
||||||
p:first-child {
|
p:first-child {
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="ms-upload-main-text w-full">
|
<div class="ms-upload-main-text w-full">
|
||||||
<a-tooltip :content="fileList[0]?.name">
|
<a-tooltip :content="fileList[0]?.name">
|
||||||
<span class="one-line-text w-[80%]"> {{ fileList[0]?.name }}</span>
|
<span class="one-line-text w-[80%] text-center"> {{ fileList[0]?.name }}</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="ms-upload-sub-text">{{ formatFileSize(fileList[0]?.file?.size || 0) }}</div>
|
<div class="ms-upload-sub-text">{{ formatFileSize(fileList[0]?.file?.size || 0) }}</div>
|
||||||
|
|
|
@ -166,14 +166,14 @@
|
||||||
</div>
|
</div>
|
||||||
<caseTabDemand
|
<caseTabDemand
|
||||||
ref="caseDemandRef"
|
ref="caseDemandRef"
|
||||||
:fun-params="{ projectId: appStore.currentProjectId, caseId: route.query.caseId as string, keyword: demandKeyword }"
|
:fun-params="{ projectId: appStore.currentProjectId, caseId: activeCaseId, keyword: demandKeyword }"
|
||||||
:show-empty="false"
|
:show-empty="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex h-full flex-col overflow-hidden">
|
<div v-else class="flex flex-1 flex-col overflow-hidden">
|
||||||
<div class="review-history-list">
|
<div class="review-history-list">
|
||||||
<a-spin :loading="reviewHistoryListLoading" class="h-full w-full">
|
<a-spin :loading="reviewHistoryListLoading" class="h-full w-full">
|
||||||
<div v-for="item of reviewHistoryList" :key="item.id" class="mb-[16px]">
|
<div v-for="item of reviewHistoryList" :key="item.id" class="review-history-list-item">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<MSAvatar :avatar="item.userLogo" />
|
<MSAvatar :avatar="item.userLogo" />
|
||||||
<div class="ml-[8px] flex items-center">
|
<div class="ml-[8px] flex items-center">
|
||||||
|
@ -208,7 +208,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-footer">
|
<div class="content-footer">
|
||||||
<div class="mb-[16px] flex items-center">
|
<div class="mb-[12px] flex items-center">
|
||||||
<div class="font-medium text-[var(--color-text-1)]">
|
<div class="font-medium text-[var(--color-text-1)]">
|
||||||
{{ t('caseManagement.caseReview.startReview') }}
|
{{ t('caseManagement.caseReview.startReview') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -225,10 +225,12 @@
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<reviewForm ref="dialogFormRef" />
|
<reviewForm
|
||||||
<a-button type="primary" class="mt-[16px]" :loading="submitReviewLoading" @click="submitReview">
|
:review-id="reviewId"
|
||||||
{{ t('caseManagement.caseReview.submitReview') }}
|
:case-id="activeCaseId"
|
||||||
</a-button>
|
:review-pass-rule="reviewDetail.reviewPassRule"
|
||||||
|
@done="reviewDone"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
|
@ -250,7 +252,6 @@
|
||||||
* @description 功能测试-用例评审-用例详情
|
* @description 功能测试-用例评审-用例详情
|
||||||
*/
|
*/
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import MSAvatar from '@/components/pure/ms-avatar/index.vue';
|
import MSAvatar from '@/components/pure/ms-avatar/index.vue';
|
||||||
|
@ -260,7 +261,6 @@
|
||||||
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsPagination from '@/components/pure/ms-pagination/index';
|
import MsPagination from '@/components/pure/ms-pagination/index';
|
||||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
|
||||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||||
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||||
import caseTemplateDetail from '../caseManagementFeature/components/caseTemplateDetail.vue';
|
import caseTemplateDetail from '../caseManagementFeature/components/caseTemplateDetail.vue';
|
||||||
|
@ -272,7 +272,6 @@
|
||||||
getCaseReviewHistoryList,
|
getCaseReviewHistoryList,
|
||||||
getReviewDetail,
|
getReviewDetail,
|
||||||
getReviewDetailCasePage,
|
getReviewDetailCasePage,
|
||||||
saveCaseReviewResult,
|
|
||||||
} from '@/api/modules/case-management/caseReview';
|
} from '@/api/modules/case-management/caseReview';
|
||||||
import { getCaseDetail } from '@/api/modules/case-management/featureCase';
|
import { getCaseDetail } from '@/api/modules/case-management/featureCase';
|
||||||
import { reviewDefaultDetail, reviewResultMap } from '@/config/caseManagement';
|
import { reviewDefaultDetail, reviewResultMap } from '@/config/caseManagement';
|
||||||
|
@ -283,21 +282,20 @@
|
||||||
import type { DetailCase } from '@/models/caseManagement/featureCase';
|
import type { DetailCase } from '@/models/caseManagement/featureCase';
|
||||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import { Instance } from 'tippy.js';
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const reviewDetail = ref<ReviewItem>({ ...reviewDefaultDetail });
|
const reviewDetail = ref<ReviewItem>({ ...reviewDefaultDetail });
|
||||||
|
const reviewId = ref(route.query.id as string);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
// 初始化评审详情
|
// 初始化评审详情
|
||||||
async function initDetail() {
|
async function initDetail() {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await getReviewDetail(route.query.id as string);
|
const res = await getReviewDetail(reviewId.value);
|
||||||
reviewDetail.value = res;
|
reviewDetail.value = res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
@ -333,7 +331,7 @@
|
||||||
caseListLoading.value = true;
|
caseListLoading.value = true;
|
||||||
const res = await getReviewDetailCasePage({
|
const res = await getReviewDetailCasePage({
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
reviewId: route.query.id as string,
|
reviewId: reviewId.value,
|
||||||
viewFlag: onlyMine.value,
|
viewFlag: onlyMine.value,
|
||||||
keyword: keyword.value,
|
keyword: keyword.value,
|
||||||
current: pageNation.value.current,
|
current: pageNation.value.current,
|
||||||
|
@ -470,7 +468,7 @@
|
||||||
async function initReviewHistoryList() {
|
async function initReviewHistoryList() {
|
||||||
try {
|
try {
|
||||||
reviewHistoryListLoading.value = true;
|
reviewHistoryListLoading.value = true;
|
||||||
const res = await getCaseReviewHistoryList(route.query.id as string, activeCaseId.value);
|
const res = await getCaseReviewHistoryList(reviewId.value, activeCaseId.value);
|
||||||
reviewHistoryList.value = res;
|
reviewHistoryList.value = res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
@ -489,7 +487,6 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
const autoNext = ref(false);
|
const autoNext = ref(false);
|
||||||
const dialogFormRef = ref<InstanceType<typeof reviewForm>>();
|
|
||||||
const demandKeyword = ref('');
|
const demandKeyword = ref('');
|
||||||
const caseDemandRef = ref<InstanceType<typeof caseTabDemand>>();
|
const caseDemandRef = ref<InstanceType<typeof caseTabDemand>>();
|
||||||
|
|
||||||
|
@ -505,51 +502,23 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitReviewLoading = ref(false);
|
function reviewDone() {
|
||||||
// 提交评审
|
if (autoNext.value) {
|
||||||
function submitReview() {
|
// 自动下一个,更改激活的 id会刷新详情
|
||||||
dialogFormRef.value?.validateForm(async (caseResultForm: Record<string, any>) => {
|
const index = caseList.value.findIndex((e) => e.caseId === activeCaseId.value);
|
||||||
try {
|
if (index < caseList.value.length - 1) {
|
||||||
submitReviewLoading.value = true;
|
activeCaseId.value = caseList.value[index + 1].caseId;
|
||||||
const params = {
|
} else {
|
||||||
projectId: appStore.currentProjectId,
|
// 当前是最后一个,刷新数据
|
||||||
caseId: activeCaseId.value,
|
loadCaseDetail();
|
||||||
reviewId: route.query.id as string,
|
initReviewHistoryList();
|
||||||
status: caseResultForm.value.result,
|
|
||||||
reviewPassRule: reviewDetail.value.reviewPassRule,
|
|
||||||
content: caseResultForm.value.reason,
|
|
||||||
notifier: '', // TODO: 通知人
|
|
||||||
};
|
|
||||||
await saveCaseReviewResult(params);
|
|
||||||
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
|
||||||
caseResultForm.value = {
|
|
||||||
result: 'PASS' as ReviewResult,
|
|
||||||
reason: '',
|
|
||||||
fileList: [] as MsFileItem[],
|
|
||||||
};
|
|
||||||
if (autoNext.value) {
|
|
||||||
// 自动下一个,更改激活的 id会刷新详情
|
|
||||||
const index = caseList.value.findIndex((e) => e.caseId === activeCaseId.value);
|
|
||||||
if (index < caseList.value.length - 1) {
|
|
||||||
activeCaseId.value = caseList.value[index + 1].caseId;
|
|
||||||
} else {
|
|
||||||
// 当前是最后一个,刷新数据
|
|
||||||
loadCaseDetail();
|
|
||||||
initReviewHistoryList();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 不自动下一个才请求详情
|
|
||||||
loadCaseDetail();
|
|
||||||
initReviewHistoryList();
|
|
||||||
}
|
|
||||||
loadCaseList();
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
submitReviewLoading.value = false;
|
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
// 不自动下一个才请求详情
|
||||||
|
loadCaseDetail();
|
||||||
|
initReviewHistoryList();
|
||||||
|
}
|
||||||
|
loadCaseList();
|
||||||
}
|
}
|
||||||
|
|
||||||
const editCaseVisible = ref(false);
|
const editCaseVisible = ref(false);
|
||||||
|
@ -596,7 +565,7 @@
|
||||||
moduleIds,
|
moduleIds,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
keyword.value = route.query.caseId as string;
|
keyword.value = route.query.reviewId as string;
|
||||||
}
|
}
|
||||||
initDetail();
|
initDetail();
|
||||||
loadCaseList();
|
loadCaseList();
|
||||||
|
@ -654,7 +623,12 @@
|
||||||
.review-history-list {
|
.review-history-list {
|
||||||
@apply h-full;
|
@apply h-full;
|
||||||
|
|
||||||
padding: 16px 0 16px 16px;
|
padding: 16px 0 0 16px;
|
||||||
|
.review-history-list-item {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.content-footer {
|
.content-footer {
|
||||||
|
|
|
@ -158,34 +158,13 @@
|
||||||
class="mb-0"
|
class="mb-0"
|
||||||
>
|
>
|
||||||
<div class="flex w-full items-center">
|
<div class="flex w-full items-center">
|
||||||
<a-mention
|
<MsRichText
|
||||||
v-model:model-value="dialogForm.reason"
|
v-model:raw="dialogForm.reason"
|
||||||
type="textarea"
|
v-model:commentIds="dialogForm.commentIds"
|
||||||
:auto-size="{ minRows: 1 }"
|
:upload-image="handleUploadImage"
|
||||||
:max-length="1000"
|
class="w-full"
|
||||||
allow-clear
|
|
||||||
class="flex flex-1 items-center"
|
|
||||||
/>
|
/>
|
||||||
<MsUpload
|
|
||||||
v-model:file-list="dialogForm.fileList"
|
|
||||||
accept="image"
|
|
||||||
size-unit="MB"
|
|
||||||
:auto-upload="false"
|
|
||||||
multiple
|
|
||||||
:limit="10"
|
|
||||||
:disabled="dialogForm.fileList.length >= 10"
|
|
||||||
>
|
|
||||||
<a-button type="outline" class="ml-[8px] p-[8px_6px]" :disabled="dialogForm.fileList.length >= 10">
|
|
||||||
<icon-file-image :size="18" />
|
|
||||||
</a-button>
|
|
||||||
</MsUpload>
|
|
||||||
</div>
|
</div>
|
||||||
<MsFileList
|
|
||||||
v-model:file-list="dialogForm.fileList"
|
|
||||||
show-mode="imageList"
|
|
||||||
:show-tab="false"
|
|
||||||
class="mt-[8px]"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
v-if="dialogShowType === 'changeReviewer'"
|
v-if="dialogShowType === 'changeReviewer'"
|
||||||
|
@ -231,9 +210,10 @@
|
||||||
type="primary"
|
type="primary"
|
||||||
class="ml-[12px]"
|
class="ml-[12px]"
|
||||||
:loading="dialogLoading"
|
:loading="dialogLoading"
|
||||||
|
:disabled="submitReviewDisabled"
|
||||||
@click="commitResult"
|
@click="commitResult"
|
||||||
>
|
>
|
||||||
{{ t('caseManagement.caseReview.commitResult') }}
|
{{ t('caseManagement.caseReview.submitReview') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<a-button
|
||||||
v-if="dialogShowType === 'changeReviewer'"
|
v-if="dialogShowType === 'changeReviewer'"
|
||||||
|
@ -268,11 +248,10 @@
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsPopconfirm from '@/components/pure/ms-popconfirm/index.vue';
|
import MsPopconfirm from '@/components/pure/ms-popconfirm/index.vue';
|
||||||
|
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
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 { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
import MsSelect from '@/components/business/ms-select';
|
import MsSelect from '@/components/business/ms-select';
|
||||||
|
|
||||||
|
@ -284,6 +263,7 @@
|
||||||
getReviewDetailCasePage,
|
getReviewDetailCasePage,
|
||||||
getReviewUsers,
|
getReviewUsers,
|
||||||
} from '@/api/modules/case-management/caseReview';
|
} from '@/api/modules/case-management/caseReview';
|
||||||
|
import { editorUploadFile } from '@/api/modules/case-management/featureCase';
|
||||||
import { getProjectMemberCommentOptions } from '@/api/modules/project-management/projectMember';
|
import { getProjectMemberCommentOptions } from '@/api/modules/project-management/projectMember';
|
||||||
import { reviewResultMap } from '@/config/caseManagement';
|
import { reviewResultMap } from '@/config/caseManagement';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -420,14 +400,14 @@
|
||||||
...filter.combine,
|
...filter.combine,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
current: propsRes.value.msPagination?.current,
|
|
||||||
pageSize: propsRes.value.msPagination?.pageSize,
|
|
||||||
total: propsRes.value.msPagination?.total,
|
|
||||||
};
|
};
|
||||||
setLoadListParams(tableParams.value);
|
setLoadListParams(tableParams.value);
|
||||||
loadList();
|
loadList();
|
||||||
emit('init', {
|
emit('init', {
|
||||||
...tableParams.value,
|
...tableParams.value,
|
||||||
|
current: propsRes.value.msPagination?.current,
|
||||||
|
pageSize: propsRes.value.msPagination?.pageSize,
|
||||||
|
total: propsRes.value.msPagination?.total,
|
||||||
moduleIds: [],
|
moduleIds: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -472,6 +452,7 @@
|
||||||
reviewer: [] as string[],
|
reviewer: [] as string[],
|
||||||
isAppend: false,
|
isAppend: false,
|
||||||
fileList: [] as MsFileItem[],
|
fileList: [] as MsFileItem[],
|
||||||
|
commentIds: [] as string[],
|
||||||
};
|
};
|
||||||
const dialogForm = ref({ ...defaultDialogForm });
|
const dialogForm = ref({ ...defaultDialogForm });
|
||||||
const dialogFormRef = ref<FormInstance>();
|
const dialogFormRef = ref<FormInstance>();
|
||||||
|
@ -649,13 +630,13 @@
|
||||||
reviewPassRule: props.reviewPassRule,
|
reviewPassRule: props.reviewPassRule,
|
||||||
status: dialogForm.value.result as ReviewResult,
|
status: dialogForm.value.result as ReviewResult,
|
||||||
content: dialogForm.value.reason,
|
content: dialogForm.value.reason,
|
||||||
notifier: '', // TODO: 通知人
|
notifier: dialogForm.value.commentIds.join(';'),
|
||||||
selectIds: batchParams.value.selectIds,
|
selectIds: batchParams.value.selectIds,
|
||||||
selectAll: batchParams.value.selectAll,
|
selectAll: batchParams.value.selectAll,
|
||||||
excludeIds: batchParams.value.excludeIds,
|
excludeIds: batchParams.value.excludeIds,
|
||||||
condition: batchParams.value.condition,
|
condition: batchParams.value.condition,
|
||||||
});
|
});
|
||||||
Message.success(t('common.updateSuccess'));
|
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
resetSelector();
|
resetSelector();
|
||||||
emit('refresh');
|
emit('refresh');
|
||||||
|
@ -670,6 +651,19 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleUploadImage(file: File) {
|
||||||
|
const { data } = await editorUploadFile({
|
||||||
|
fileList: [file],
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitReviewDisabled = computed(
|
||||||
|
() =>
|
||||||
|
dialogForm.value.result !== 'PASS' &&
|
||||||
|
(dialogForm.value.reason === '' || dialogForm.value.reason.trim() === '<p style=""></p>')
|
||||||
|
);
|
||||||
|
|
||||||
const reviewersOptions = ref<SelectOptionData[]>([]);
|
const reviewersOptions = ref<SelectOptionData[]>([]);
|
||||||
const reviewerLoading = ref(false);
|
const reviewerLoading = ref(false);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-form ref="dialogFormRef" :model="caseResultForm" layout="vertical">
|
<a-form ref="dialogFormRef" :model="caseResultForm" layout="vertical">
|
||||||
<a-form-item field="reason" :label="t('caseManagement.caseReview.reviewResult')" class="mb-[8px]">
|
<a-form-item field="reason" class="mb-[4px]">
|
||||||
<a-radio-group v-model:model-value="caseResultForm.result" @change="() => dialogFormRef?.resetFields()">
|
<a-radio-group v-model:model-value="caseResultForm.result" @change="() => dialogFormRef?.resetFields()">
|
||||||
<a-radio value="PASS">
|
<a-radio value="PASS">
|
||||||
<div class="inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
|
@ -28,61 +28,66 @@
|
||||||
</a-radio>
|
</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<div class="flex w-full items-center">
|
||||||
field="reason"
|
<a-button type="secondary" class="p-[8px_6px]" size="small" @click="modalVisible = true">
|
||||||
:label="t('caseManagement.caseReview.reason')"
|
<icon-plus class="mr-[4px]" />
|
||||||
:rules="
|
{{ t('caseManagement.caseReview.reason') }}
|
||||||
caseResultForm.result !== 'PASS'
|
</a-button>
|
||||||
? [{ required: true, message: t('caseManagement.caseReview.reasonRequired') }]
|
</div>
|
||||||
: []
|
|
||||||
"
|
|
||||||
asterisk-position="end"
|
|
||||||
class="mb-0"
|
|
||||||
>
|
|
||||||
<div class="flex w-full items-center">
|
|
||||||
<a-mention
|
|
||||||
v-model:model-value="caseResultForm.reason"
|
|
||||||
type="textarea"
|
|
||||||
:auto-size="{ minRows: 1 }"
|
|
||||||
:max-length="1000"
|
|
||||||
allow-clear
|
|
||||||
class="flex flex-1 items-center"
|
|
||||||
/>
|
|
||||||
<MsUpload
|
|
||||||
v-model:file-list="caseResultForm.fileList"
|
|
||||||
accept="image"
|
|
||||||
size-unit="MB"
|
|
||||||
:auto-upload="false"
|
|
||||||
:limit="10"
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<a-button type="outline" class="ml-[8px] p-[8px_6px]">
|
|
||||||
<icon-file-image :size="18" />
|
|
||||||
</a-button>
|
|
||||||
</MsUpload>
|
|
||||||
</div>
|
|
||||||
<MsFileList
|
|
||||||
v-model:file-list="caseResultForm.fileList"
|
|
||||||
show-mode="imageList"
|
|
||||||
:show-tab="false"
|
|
||||||
class="mt-[8px]"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
class="mt-[12px]"
|
||||||
|
:disabled="submitDisabled"
|
||||||
|
:submit-review-loading="submitReviewLoading"
|
||||||
|
@click="submitReview"
|
||||||
|
>
|
||||||
|
{{ t('caseManagement.caseReview.submitReview') }}
|
||||||
|
</a-button>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="t('caseManagement.caseReview.reason')"
|
||||||
|
class="p-[4px]"
|
||||||
|
title-align="start"
|
||||||
|
body-class="p-0"
|
||||||
|
:width="680"
|
||||||
|
:cancel-button-props="{ disabled: submitReviewLoading }"
|
||||||
|
:ok-button-props="{ disabled: submitDisabled }"
|
||||||
|
:ok-loading="submitReviewLoading"
|
||||||
|
:ok-text="t('caseManagement.caseReview.submitReview')"
|
||||||
|
@before-ok="submitReview"
|
||||||
|
>
|
||||||
|
<MsRichText
|
||||||
|
v-model:raw="caseResultForm.reason"
|
||||||
|
v-model:commentIds="caseResultForm.commentIds"
|
||||||
|
:upload-image="handleUploadImage"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FormInstance } from '@arco-design/web-vue';
|
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
|
||||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
|
|
||||||
|
import { saveCaseReviewResult } from '@/api/modules/case-management/caseReview';
|
||||||
|
import { editorUploadFile } from '@/api/modules/case-management/featureCase';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { ReviewResult } from '@/models/caseManagement/caseReview';
|
import { ReviewPassRule, ReviewResult } from '@/models/caseManagement/caseReview';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
reviewId: string;
|
||||||
|
caseId: string;
|
||||||
|
reviewPassRule: ReviewPassRule;
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits(['done']);
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const dialogFormRef = ref<FormInstance>();
|
const dialogFormRef = ref<FormInstance>();
|
||||||
|
@ -90,23 +95,64 @@
|
||||||
result: 'PASS' as ReviewResult,
|
result: 'PASS' as ReviewResult,
|
||||||
reason: '',
|
reason: '',
|
||||||
fileList: [] as MsFileItem[],
|
fileList: [] as MsFileItem[],
|
||||||
|
commentIds: [] as string[],
|
||||||
});
|
});
|
||||||
|
const submitReviewLoading = ref(false);
|
||||||
|
const submitDisabled = computed(
|
||||||
|
() =>
|
||||||
|
caseResultForm.value.result !== 'PASS' &&
|
||||||
|
(caseResultForm.value.reason === '' || caseResultForm.value.reason.trim() === '<p style=""></p>')
|
||||||
|
);
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
|
||||||
function validateForm(cb: (form: Record<string, any>) => void) {
|
async function handleUploadImage(file: File) {
|
||||||
dialogFormRef.value?.validate((errors) => {
|
const { data } = await editorUploadFile({
|
||||||
if (!errors && typeof cb === 'function') {
|
fileList: [file],
|
||||||
cb(caseResultForm.value);
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交评审
|
||||||
|
function submitReview() {
|
||||||
|
dialogFormRef.value?.validate(async (errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
submitReviewLoading.value = true;
|
||||||
|
const params = {
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
caseId: props.caseId,
|
||||||
|
reviewId: props.reviewId,
|
||||||
|
status: caseResultForm.value.result,
|
||||||
|
reviewPassRule: props.reviewPassRule,
|
||||||
|
content: caseResultForm.value.reason,
|
||||||
|
notifier: caseResultForm.value.commentIds.join(';'),
|
||||||
|
};
|
||||||
|
await saveCaseReviewResult(params);
|
||||||
|
modalVisible.value = false;
|
||||||
|
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
||||||
|
caseResultForm.value = {
|
||||||
|
result: 'PASS' as ReviewResult,
|
||||||
|
reason: '',
|
||||||
|
fileList: [] as MsFileItem[],
|
||||||
|
commentIds: [] as string[],
|
||||||
|
};
|
||||||
|
emit('done');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
submitReviewLoading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
validateForm,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.image-preview-container {
|
:deep(.arco-form-item-label-col) {
|
||||||
margin-top: 8px;
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.arco-radio {
|
||||||
|
@apply pl-0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue