style(功能用例): 导入优化

--story=1015918 --user=吕梦园
https://www.tapd.cn/55049933/prong/stories/view/1155049933001015918
This commit is contained in:
teukkk 2024-08-26 17:02:41 +08:00 committed by 刘瑞斌
parent 6294eb6f47
commit bbe09529d9
8 changed files with 293 additions and 257 deletions

View File

@ -370,31 +370,24 @@
const existRead = record.perChecked.some(
(item: string) => item.split(':')[0] === preStr && item.split(':')[1] === 'READ'
);
const existCreate = record.perChecked.some(
(item: string) => item.split(':')[0] === preStr && item.split(':')[1] === 'ADD'
);
if (!existRead && postStr !== 'READ') {
record.perChecked.push(`${preStr}:READ`);
}
if (lastEditStr === 'IMPORT') {
if (!existCreate && lastEditStr === 'IMPORT') {
//
if (!record.perChecked.includes(`${preStr}:READ+ADD`)) {
record.perChecked.push(`${preStr}:READ+ADD`);
}
if (!record.perChecked.includes(`${preStr}:READ+UPDATE`)) {
record.perChecked.push(`${preStr}:READ+UPDATE`);
}
record.perChecked.push(`${preStr}:ADD`);
record.perChecked.push(`${preStr}:READ+UPDATE`);
}
} else {
//
const preStr = currentValue.split(':')[0];
const postStr = currentValue.split(':')[1];
const lastEditStr = currentValue.split('+')[1]; // +
if (postStr === 'READ') {
//
//
record.perChecked = record.perChecked.filter((item: string) => !item.includes(preStr));
} else if (['ADD', 'UPDATE'].includes(lastEditStr)) {
//
record.perChecked = record.perChecked.filter(
(item: string) => !item.includes('IMPORT') && item !== currentValue
);
} else {
record.perChecked.splice(record.perChecked.indexOf(currentValue), 1);
}

View File

@ -0,0 +1,46 @@
<template>
<a-radio-group v-model="value">
<template v-for="item in props.options" :key="item">
<a-radio :value="item.value">
<template #radio="{ checked }">
<div :class="` radio-item ${checked ? 'radio-item--active' : ''}`">
{{ item.label }}
</div>
</template>
</a-radio>
</template>
</a-radio-group>
</template>
<script setup lang="ts">
const props = defineProps<{
options: { value: string; label: string }[];
}>();
const value = defineModel<string>('value', { required: true });
</script>
<style lang="less" scoped>
.arco-radio-group .arco-radio {
margin-right: 8px;
padding-left: 0;
}
.radio-item {
@apply flex cursor-pointer items-center bg-white;
padding-left: 8px;
width: 186px;
height: 38px;
border: 1px solid;
border-color: var(--color-text-n8);
border-radius: var(--border-radius-small);
&:hover {
border-color: rgb(var(--primary-5));
}
}
.radio-item--active {
border-color: rgb(var(--primary-5));
color: rgb(var(--primary-5));
background-color: rgb(var(--primary-1));
}
</style>

View File

@ -15,6 +15,14 @@
@adv-search="handleAdvSearch"
@refresh="fetchData()"
>
<template #left>
<div>
<a-button v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="mr-[12px]" type="primary" @click="caseDetail">
{{ t('common.newCreate') }}
</a-button>
<ImportCase ref="importCaseRef" @init-modules="emit('initModules')" @confirm-import="confirmImport" />
</div>
</template>
<template #right>
<a-radio-group
v-model:model-value="showType"
@ -172,11 +180,8 @@
{{ t('caseManagement.featureCase.creatingCase') }}
</MsButton>
<span v-permission="['FUNCTIONAL_CASE:READ+IMPORT']"> {{ t('caseManagement.featureCase.or') }} </span>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="!mx-[8px]" @click="emit('import', 'Excel')">
{{ t('caseManagement.featureCase.importExcel') }}
</MsButton>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" @click="emit('import', 'Xmind')">
{{ t('caseManagement.featureCase.importXmind') }}
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="!mx-[8px]" @click="importCase()">
{{ t('common.import') }}
</MsButton>
</div>
</template>
@ -380,6 +385,7 @@
import BatchEditModal from './batchEditModal.vue';
import CaseDetailDrawer from './caseDetailDrawer.vue';
import FeatureCaseTree from './caseTree.vue';
import ImportCase from './import/index.vue';
import AddDemandModal from './tabContent/tabDemand/addDemandModal.vue';
import ThirdDemandDrawer from './tabContent/tabDemand/thirdDemandDrawer.vue';
@ -456,7 +462,7 @@
const emit = defineEmits<{
(e: 'init', params: CaseModuleQueryParams, refreshModule?: boolean): void;
(e: 'import', type: 'Excel' | 'Xmind'): void;
(e: 'initModules'): void;
}>();
const minderStore = useMinderStore();
@ -1015,6 +1021,22 @@
emitTableParams();
}
//
function caseDetail() {
router.push({
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
});
}
//
const importCaseRef = ref<InstanceType<typeof ImportCase>>();
function importCase() {
importCaseRef.value?.importCase();
}
function confirmImport() {
emit('initModules');
initData();
}
const showDetailDrawer = ref(false);
const activeDetailId = ref<string>('');
const activeCaseIndex = ref<number>(0);

View File

@ -4,38 +4,33 @@
title-align="start"
class="ms-modal-form ms-modal-medium"
:ok-text="t('common.confirm')"
:title="t('common.import')"
:cancel-text="t('common.cancel')"
@close="handleCancel"
>
<template #title>
{{
props.validateType === 'Excel'
? t('caseManagement.featureCase.formExcelExport')
: t('caseManagement.featureCase.formXMindExport')
}}
</template>
<div>
<a-alert class="mb-4">
<MsSeparateRadioButton v-model:value="validateType" :options="validateTypeOptions" />
<a-alert class="my-4">
<div class="flex items-center">
{{ t('caseManagement.featureCase.beforeUploadTip', { type: props.validateType }) }}
{{ t('caseManagement.featureCase.beforeUploadTip', { type: validateType }) }}
<MsIcon
:type="props.validateType === 'Excel' ? 'icon-icon_file-excel_colorful1' : 'icon-icon_file-xmind_colorful1'"
:type="validateType === 'Excel' ? 'icon-icon_file-excel_colorful1' : 'icon-icon_file-xmind_colorful1'"
class="mx-1 cursor-pointer text-[rgb(var(--primary-6))]"
></MsIcon>
<MsButton @click="downloadExcelTemplate">
{{ t('caseManagement.featureCase.downloadTemplate', { type: props.validateType }) }}
{{ t('caseManagement.featureCase.downloadTemplate', { type: validateType }) }}
</MsButton>
</div>
</a-alert>
<MsUpload
v-model:file-list="fileList"
class="mb-6 w-full"
:accept="props.validateType === 'Excel' ? 'excel' : 'xmind'"
:accept="validateType === 'Excel' ? 'excel' : 'xmind'"
:max-size="100"
size-unit="MB"
main-text="caseManagement.featureCase.dragOrClick"
:sub-text="
props.validateType === 'Excel'
validateType === 'Excel'
? t('caseManagement.featureCase.onlyEXcelTip')
: t('caseManagement.featureCase.onlyXmindTip')
"
@ -80,7 +75,7 @@
@click="saveConfirm"
>
{{
props.validateType === 'Excel'
validateType === 'Excel'
? t('caseManagement.featureCase.checkImportFile')
: t('caseManagement.featureCase.checkTemplate')
}}
@ -93,9 +88,10 @@
<script setup lang="ts">
import { ref } from 'vue';
import { FileItem, Message } from '@arco-design/web-vue';
import { FileItem } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsSeparateRadioButton from '@/components/pure/ms-separate-radio-button/index.vue';
import MsUpload from '@/components/pure/ms-upload/index.vue';
import { downloadTemplate } from '@/api/modules/case-management/featureCase';
@ -107,7 +103,6 @@
const props = defineProps<{
visible: boolean;
validateType: 'Excel' | 'Xmind';
confirmLoading: boolean;
}>();
@ -116,7 +111,19 @@
(e: 'save', files: FileItem[], isRecover: boolean): void;
(e: 'close'): void;
}>();
const validateType = defineModel<'Excel' | 'Xmind'>('validateType', { required: true });
const validateTypeOptions = [
{ value: 'Excel', label: t('caseManagement.featureCase.importExcel') },
{ value: 'Xmind', label: t('caseManagement.featureCase.importXmind') },
];
const fileList = ref<FileItem[]>([]);
watch(
() => validateType.value,
() => {
fileList.value = [];
}
);
const appStore = useAppStore();
const currentProjectId = computed(() => appStore.currentProjectId);
@ -127,12 +134,13 @@
});
const handleCancel = () => {
validateType.value = 'Excel';
fileList.value = [];
emit('close');
};
const fileTypeTip = computed(() => {
return props.validateType === 'Excel'
return validateType.value === 'Excel'
? t('caseManagement.featureCase.excelImportTip')
: t('caseManagement.featureCase.xmindImportTip');
});
@ -142,8 +150,8 @@
// excel|| xmind
async function downloadExcelTemplate() {
try {
const res = await downloadTemplate(currentProjectId.value, props.validateType);
downloadByteFile(res, props.validateType === 'Excel' ? 'excel_case.xlsx' : 'xmind_case.xmind');
const res = await downloadTemplate(currentProjectId.value, validateType.value);
downloadByteFile(res, validateType.value === 'Excel' ? 'excel_case.xlsx' : 'xmind_case.xmind');
} catch (error) {
console.log(error);
}
@ -153,5 +161,3 @@
emit('save', fileList.value, isRecover.value);
}
</script>
<style scoped></style>

View File

@ -0,0 +1,173 @@
<template>
<a-button v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" type="outline" @click="importCase">
{{ t('common.import') }}
</a-button>
<!-- 导入 -->
<ExportExcelModal
v-model:visible="showExcelModal"
v-model:validate-type="validateType"
:confirm-loading="validateLoading"
@save="validateTemplate"
@close="showExcelModal = false"
/>
<ValidateModal
v-model:visible="validateModal"
:validate-type="validateType"
:percent="progress"
@cancel="cancelValidate"
@check-finished="checkFinished"
/>
<ValidateResult
v-model:visible="validateResultModal"
:validate-type="validateType"
:validate-info="validateInfo"
:import-loading="importLoading"
@close="closeHandler"
@save="confirmImport"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import ExportExcelModal from './exportCaseModal.vue';
import ValidateModal from './validateModal.vue';
import ValidateResult from './validateResult.vue';
import { importExcelOrXMindCase, importExcelOrXMindChecked } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import type { ValidateInfo } from '@/models/caseManagement/featureCase';
import type { FileItem } from '@arco-design/web-vue';
import Message from '@arco-design/web-vue/es/message';
const emit = defineEmits<{
(e: 'confirmImport'): void;
(e: 'initModules'): void;
}>();
const appStore = useAppStore();
const { t } = useI18n();
const showExcelModal = ref<boolean>(false);
function importCase() {
showExcelModal.value = true;
}
const validateType = ref<'Excel' | 'Xmind'>('Excel');
const fileList = ref<FileItem[]>([]);
const isCover = ref<boolean>(false);
const validateLoading = ref<boolean>(false);
const validateModal = ref<boolean>(false);
//
const validateResultModal = ref<boolean>(false);
const intervalId = ref<any>(null);
const progress = ref<number>(0);
const increment = ref<number>(0.1);
function updateProgress() {
progress.value = Math.floor(progress.value + increment.value);
if (progress.value >= 1) {
progress.value = 1;
}
}
function finish() {
clearInterval(intervalId.value);
progress.value = 1;
updateProgress();
}
function start() {
progress.value = 0;
increment.value = 0.1;
intervalId.value = setInterval(() => {
if (progress.value >= 1) {
finish();
} else {
updateProgress();
}
}, 100);
}
const validateInfo = ref<ValidateInfo>({
failCount: 0,
successCount: 0,
errorMessages: [],
});
//
async function validateTemplate(files: FileItem[], cover: boolean) {
fileList.value = files;
isCover.value = cover;
validateLoading.value = true;
try {
validateModal.value = true;
start();
const params = {
projectId: appStore.currentProjectId,
versionId: '',
cover,
};
const result = await importExcelOrXMindChecked(
{ request: params, fileList: files.map((item: any) => item.file) },
validateType.value
);
finish();
validateInfo.value = result.data;
} catch (error) {
validateModal.value = false;
// eslint-disable-next-line no-console
console.log(error);
} finally {
validateLoading.value = false;
}
}
function checkFinished() {
validateResultModal.value = true;
}
function cancelValidate() {
validateModal.value = false;
}
function closeHandler() {
showExcelModal.value = false;
validateResultModal.value = false;
emit('initModules');
}
const importLoading = ref<boolean>(false);
//
async function confirmImport() {
importLoading.value = true;
try {
const params = {
projectId: appStore.currentProjectId,
versionId: '',
cover: isCover.value,
count: validateInfo.value.successCount,
};
await importExcelOrXMindCase(
{ request: params, fileList: fileList.value.map((item: any) => item.file) },
validateType.value
);
Message.success(t('caseManagement.featureCase.importSuccess'));
validateResultModal.value = false;
showExcelModal.value = false;
emit('confirmImport');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
importLoading.value = false;
}
}
defineExpose({
importCase,
});
</script>

View File

@ -4,51 +4,13 @@
<template #first>
<div class="p-[16px] pb-0">
<div class="feature-case h-[100%]">
<div class="mb-[16px] flex justify-between">
<a-input
v-model:model-value="groupKeyword"
:placeholder="t('caseManagement.caseReview.folderSearchPlaceholder')"
allow-clear
:max-length="255"
/>
<a-dropdown-button
v-if="hasAllPermission(['FUNCTIONAL_CASE:READ+IMPORT', 'FUNCTIONAL_CASE:READ+ADD'])"
class="ml-2"
type="primary"
@click="handleSelect('newCase')"
>
{{ t('common.newCreate') }}
<template #icon>
<icon-down />
</template>
<template #content>
<a-doption
v-permission="['FUNCTIONAL_CASE:READ+IMPORT']"
value="Excel"
@click="handleSelect('import', 'Excel')"
>
{{ t('caseManagement.featureCase.importExcel') }}
</a-doption>
<a-doption
v-permission="['FUNCTIONAL_CASE:READ+IMPORT']"
value="Xmind"
@click="handleSelect('import', 'Xmind')"
>
{{ t('caseManagement.featureCase.importXmind') }}
</a-doption>
</template>
</a-dropdown-button>
<a-button
v-else
v-permission="['FUNCTIONAL_CASE:READ+ADD']"
class="ml-2"
type="primary"
@click="handleSelect('newCase')"
>
{{ t('common.newCreate') }}
</a-button>
</div>
<a-input
v-model:model-value="groupKeyword"
:placeholder="t('caseManagement.caseReview.folderSearchPlaceholder')"
allow-clear
class="mb-[16px]"
:max-length="255"
/>
<div class="case h-[38px]">
<div class="flex items-center" :class="getActiveClass('all')" @click="setActiveFolder('all')">
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
@ -123,35 +85,13 @@
:modules-count="modulesCount"
:module-name="activeFolderName"
@init="initModulesCount"
@import="importCase"
@init-modules="initModules"
/>
</div>
</template>
</MsSplitBox>
</MsCard>
<!-- </div> -->
<ExportExcelModal
v-model:visible="showExcelModal"
:validate-type="validateType"
:confirm-loading="validateLoading"
@save="validateTemplate"
@close="showExcelModal = false"
/>
<ValidateModal
v-model:visible="validateModal"
:validate-type="validateType"
:percent="progress"
@cancel="cancelValidate"
@check-finished="checkFinished"
/>
<ValidateResult
v-model:visible="validateResultModal"
:validate-type="validateType"
:validate-info="validateInfo"
:import-loading="importLoading"
@close="closeHandler"
@save="conFirmImport"
/>
</template>
<script setup lang="ts">
@ -169,25 +109,17 @@
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
import CaseTable from './components/caseTable.vue';
import FeatureCaseTree from './components/caseTree.vue';
import ExportExcelModal from './components/export/exportCaseModal.vue';
import ValidateModal from './components/export/validateModal.vue';
import ValidateResult from './components/export/validateResult.vue';
import {
createCaseModuleTree,
importExcelOrXMindCase,
importExcelOrXMindChecked,
} from '@/api/modules/case-management/featureCase';
import { createCaseModuleTree } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
import { hasAnyPermission } from '@/utils/permission';
import type { CreateOrUpdateModule, ValidateInfo } from '@/models/caseManagement/featureCase';
import type { CreateOrUpdateModule } from '@/models/caseManagement/featureCase';
import { TableQueryParams } from '@/models/common';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import type { FileItem } from '@arco-design/web-vue';
import Message from '@arco-design/web-vue/es/message';
const route = useRoute();
@ -306,145 +238,9 @@
tableFilterParams.value = { ...params };
}
//
function caseDetail() {
router.push({
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
});
}
const showExcelModal = ref<boolean>(false);
const validateType = ref<'Excel' | 'Xmind'>('Excel');
// excel
function importCase(type: 'Excel' | 'Xmind') {
validateType.value = type;
showExcelModal.value = true;
}
const validateModal = ref<boolean>(false);
//
const validateResultModal = ref<boolean>(false);
const validateInfo = ref<ValidateInfo>({
failCount: 0,
successCount: 0,
errorMessages: [],
});
const intervalId = ref<any>(null);
const progress = ref<number>(0);
const increment = ref<number>(0.1);
function updateProgress() {
progress.value = Math.floor(progress.value + increment.value);
if (progress.value >= 1) {
progress.value = 1;
}
}
function finish() {
clearInterval(intervalId.value);
progress.value = 1;
updateProgress();
}
function start() {
progress.value = 0;
increment.value = 0.1;
intervalId.value = setInterval(() => {
if (progress.value >= 1) {
finish();
} else {
updateProgress();
}
}, 100);
}
const fileList = ref<FileItem[]>([]);
const isCover = ref<boolean>(false);
const validateLoading = ref<boolean>(false);
//
async function validateTemplate(files: FileItem[], cover: boolean) {
fileList.value = files;
isCover.value = cover;
validateLoading.value = true;
try {
validateModal.value = true;
start();
const params = {
projectId: appStore.currentProjectId,
versionId: '',
cover,
};
const result = await importExcelOrXMindChecked(
{ request: params, fileList: files.map((item: any) => item.file) },
validateType.value
);
finish();
validateInfo.value = result.data;
} catch (error) {
validateModal.value = false;
console.log(error);
} finally {
validateLoading.value = false;
}
}
function checkFinished() {
validateResultModal.value = true;
}
function cancelValidate() {
validateModal.value = false;
}
function closeHandler() {
showExcelModal.value = false;
validateResultModal.value = false;
function initModules() {
caseTreeRef.value.initModules();
}
const importLoading = ref<boolean>(false);
//
async function conFirmImport() {
importLoading.value = true;
try {
const params = {
projectId: appStore.currentProjectId,
versionId: '',
cover: isCover.value,
count: validateInfo.value.successCount,
};
await importExcelOrXMindCase(
{ request: params, fileList: fileList.value.map((item: any) => item.file) },
validateType.value
);
Message.success(t('caseManagement.featureCase.importSuccess'));
validateResultModal.value = false;
showExcelModal.value = false;
caseTableRef.value.initData();
caseTreeRef.value.initModules();
} catch (error) {
console.log(error);
} finally {
importLoading.value = false;
}
}
function handleSelect(value: string | number | Record<string, any> | undefined, type?: 'Excel' | 'Xmind') {
switch (value) {
case 'newCase':
caseDetail();
break;
case 'import':
importCase(type as 'Excel' | 'Xmind');
break;
default:
break;
}
}
function deleteNode() {
nextTick(() => {