feat(缺陷管理): 导出窗口公共组件

This commit is contained in:
RubyLiu 2023-12-01 16:38:01 +08:00 committed by Craftsman
parent 1721410efa
commit 1a8b1cc1f5
7 changed files with 331 additions and 1 deletions

View File

@ -40,3 +40,7 @@ export function getTemplageOption(params: { projectId: string }) {
export function getTemplateById(data: TableQueryParams) {
return MSR.get({ url: bugURL.getTemplateUrl, data });
}
// 获取导出字段配置
export function getExportConfig(projectId: string) {
return MSR.get({ url: `${bugURL.getExportConfigUrl}${projectId}` });
}

View File

@ -6,3 +6,4 @@ export const getDeleteBugUrl = '/bug/delete/';
export const postBatchDeleteBugUrl = '/bug/batch-delete';
export const getTemplateUrl = '/bug/template';
export const getTemplageOption = '/bug/template/option';
export const getExportConfigUrl = '/bug/export/columns/';

View File

@ -0,0 +1,278 @@
<template>
<MsDrawer
v-model:visible="visible"
:ok-text="t('common.export')"
:ok-loading="drawerLoading"
:width="800"
min-width="800px"
unmount-on-close
:show-continue="false"
:ok-disabled="!selectedList.length"
@confirm="handleDrawerConfirm"
@cancel="handleDrawerCancel"
>
<div class="panel-wrapper">
<div class="inner-wrapper">
<div class="optional-field">
<div class="optional-header">
<div class="font-medium">{{ t('system.orgTemplate.optionalField') }}</div>
<a-checkbox :model-value="isCheckedAll" :indeterminate="indeterminate" @change="handleChangeAll">
<span class="font-medium text-[var(--color-text-3)]">{{ t('system.orgTemplate.selectAll') }}</span>
</a-checkbox>
</div>
<div class="p-[16px]">
<a-checkbox-group :model-value="selectedIds" @change="handleGroupChange">
<div v-if="systemList.length" class="mb-[32px]">
<div class="text-[var(--color-text-4)]">{{ t('ms-export-drawer.systemFiled') }}</div>
<div class="flex flex-row flex-wrap">
<a-checkbox
v-for="item in systemList"
:key="item.key"
:value="item.key"
class="mt-[8px] w-[95px] pl-[0px]"
>
<a-tooltip :content="item.text">
<span class="one-line-text">{{ item.text }}</span>
</a-tooltip>
</a-checkbox>
</div>
</div>
<div v-if="customList.length" class="mb-[32px]">
<div class="text-[var(--color-text-4)]">{{ t('ms-export-drawer.customFiled') }}</div>
<div class="flex flex-row flex-wrap">
<a-checkbox
v-for="item in customList"
:key="item.key"
:value="item.key"
class="mt-[8px] w-[95px] pl-[0px]"
>
<a-tooltip :content="item.text">
<span class="one-line-text">{{ item.text }}</span>
</a-tooltip>
</a-checkbox>
</div>
</div>
<div v-if="otherList.length" class="mb-[32px]">
<div class="flex flex-row items-center gap-[4px]">
<div class="text-[var(--color-text-4)]"> {{ t('ms-export-drawer.otherFiled') }}</div>
<a-tooltip :content="t('ms-export-drawer.otherTip')" position="right">
<icon-question-circle class="text-[rgb(var(--primary-5))]" />
</a-tooltip>
</div>
<div class="flex flex-row flex-wrap">
<a-checkbox
v-for="item in otherList"
:key="item.key"
:value="item.key"
class="mt-[8px] w-[95px] pl-[0px]"
>
<a-tooltip :content="item.text">
<div class="one-line-text">
{{ item.text }}
</div>
</a-tooltip>
</a-checkbox>
</div>
</div>
</a-checkbox-group>
</div>
</div>
<div class="w-[270px]">
<div class="optional-header">
<div class="font-medium">{{ t('system.orgTemplate.selectedField') }}</div>
<MsButton @click="clearHandler">{{ t('system.orgTemplate.clear') }}</MsButton>
</div>
<div class="p-[16px]">
<VueDraggable v-model="selectedList" ghost-class="ghost">
<div
v-for="element in selectedList"
:key="element.key"
class="mb-[8px] flex flex-row items-center gap-[4px] bg-[var(--color-text-n9)] p-[8px]"
>
<a-tooltip :content="element.text">
<div> <MsIcon type="icon-icon_drag" class="mt-[3px] text-[16px] text-[var(--color-text-4)]" /></div>
<div class="one-line-text ml-2 w-[178px]">{{ element.text }}</div>
</a-tooltip>
<icon-close
:style="{ 'font-size': '14px' }"
class="cursor-pointer text-[var(--color-text-3)]"
@click="removeSelectedField(element.key)"
/>
</div>
</VueDraggable>
</div>
</div>
</div>
</div>
<template #title>
<slot name="title"></slot>
</template>
</MsDrawer>
</template>
<script setup lang="ts">
/**
* @description 系统管理-模版-模版管理-创建模板-添加字段到模板抽屉
*/
import { computed, ref } from 'vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { useI18n } from '@/hooks/useI18n';
import { MsExportDrawerMap, MsExportDrawerOption } from './types';
import { VueDraggable } from 'vue-draggable-plus';
const { t } = useI18n();
const drawerLoading = ref<boolean>(false);
interface MsExportDrawerProps {
visible: boolean;
allData: MsExportDrawerMap;
}
const props = defineProps<MsExportDrawerProps>();
const selectedList = ref<MsExportDrawerOption[]>([]); //
const selectedIds = ref<string[]>([]); // id
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'confirm', value: MsExportDrawerOption[]): void;
}>();
const visible = computed({
get() {
return props.visible;
},
set(value) {
emit('update:visible', value);
},
});
const systemList = computed(() => {
const { systemColumns } = props.allData;
if (systemColumns) {
return Object.keys(systemColumns).map((key) => {
return {
key,
text: systemColumns[key],
columnType: 'system',
};
});
}
return [];
});
const customList = computed(() => {
const { customColumns } = props.allData;
if (customColumns) {
return Object.keys(customColumns).map((key) => {
return {
key,
text: customColumns[key],
columnType: 'custom',
};
});
}
return [];
});
const otherList = computed(() => {
const { otherColumns } = props.allData;
if (otherColumns) {
return Object.keys(otherColumns).map((key) => {
return {
key,
text: otherColumns[key],
columnType: 'other',
};
});
}
return [];
});
const allList = computed(() => {
return [...systemList.value, ...customList.value, ...otherList.value];
});
const handleDrawerConfirm = () => {
emit('confirm', selectedList.value);
};
const handleDrawerCancel = () => {
visible.value = false;
selectedList.value = [];
};
const isCheckedAll = computed(() => {
return selectedList.value.length === allList.value.length;
});
const indeterminate = computed(() => {
return selectedList.value.length > 0 && selectedList.value.length < allList.value.length;
});
const clearHandler = () => {
selectedList.value = [];
};
const handleChangeAll = (value: boolean | (string | number | boolean)[]) => {
if (value) {
selectedList.value = allList.value;
} else {
selectedList.value = [];
}
};
const removeSelectedField = (id: string) => {
selectedList.value = selectedList.value.filter((item) => item.key !== id);
};
const handleGroupChange = (value: (string | number | boolean)[]) => {
selectedList.value = allList.value.filter((item) => value.includes(item.key));
};
watchEffect(() => {
selectedIds.value = selectedList.value.map((item) => item.key);
});
</script>
<style scoped lang="less">
:deep(.arco-drawer) {
width: 65% !important;
}
.panel-wrapper {
display: flex;
width: 100%;
height: 100%;
flex-flow: row nowrap;
.inner-wrapper {
display: flex;
overflow: hidden;
width: 100%;
height: 100%;
border: 1px solid var(--color-text-n8);
//
.optional-field {
flex-grow: 1;
height: 100%;
border-right: 1px solid var(--color-text-n8);
}
.optional-header {
padding: 0 16px;
height: 54px;
color: var(--color-text-3);
background: var(--color-text-n9);
@apply flex flex-row items-center justify-between;
}
}
}
.ghost {
border: 1px dashed rgba(var(--primary-5));
background-color: rgba(var(--primary-1));
}
</style>

View File

@ -0,0 +1,9 @@
export default {
'ms-export-drawer.couldSelect': 'Could Select',
'ms-export-drawer.selected': 'Selected',
'ms-export-drawer.allSelected': 'All Selected',
'ms-export-drawer.systemFiled': 'System Fields',
'ms-export-drawer.customFiled': 'Custom Fields',
'ms-export-drawer.otherFiled': 'Other Fields',
'ms-export-drawer.otherTip': 'Other fields are system-generated fields and cannot be imported after export',
};

View File

@ -0,0 +1,9 @@
export default {
'ms-export-drawer.couldSelect': '可选字段',
'ms-export-drawer.selected': '已选字段',
'ms-export-drawer.allSelected': '全选',
'ms-export-drawer.systemFiled': '系统字段',
'ms-export-drawer.customFiled': '自定义字段',
'ms-export-drawer.otherFiled': '其他字段',
'ms-export-drawer.otherTip': '其他字段为系统生成字段, 导出后不支持导入',
};

View File

@ -0,0 +1,13 @@
export interface MsExportDrawerColumns {
[key: string]: string;
}
export interface MsExportDrawerMap {
systemColumns?: MsExportDrawerColumns;
customColumns?: MsExportDrawerColumns;
otherColumns?: MsExportDrawerColumns;
}
export interface MsExportDrawerOption {
text: string;
key: string;
columnType: string;
}

View File

@ -5,6 +5,7 @@
<div class="flex gap-[12px]">
<a-button type="primary" @click="handleCreate">{{ t('bugManagement.createBug') }} </a-button>
<a-button type="outline" @click="handleSync">{{ t('bugManagement.syncBug') }} </a-button>
<a-button type="outline" @click="handleExport">{{ t('common.export') }} </a-button>
</div>
</template>
</MsAdvanceFilter>
@ -65,6 +66,7 @@
<a-date-picker v-model="syncObject.time" show-time class="w-[304px]" />
</div>
</a-modal>
<MsExportDrawer v-model:visible="exportVisible" :all-data="exportOptionData" />
</template>
<script lang="ts" setup>
@ -74,11 +76,13 @@
import { FilterFormItem, FilterType } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCard from '@/components/pure/ms-card/index.vue';
import MsExportDrawer from '@/components/pure/ms-export-drawer/index.vue';
import { MsExportDrawerMap } from '@/components/pure/ms-export-drawer/types';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import { getBugList } from '@/api/modules/bug-management';
import { getBugList, getExportConfig } from '@/api/modules/bug-management';
import { updateOrAddProjectUserGroup } from '@/api/modules/project-management/usergroup';
import { useI18n } from '@/hooks/useI18n';
import router from '@/router';
@ -95,6 +99,8 @@
const filterVisible = ref(false);
const filterRowCount = ref(0);
const syncVisible = ref(false);
const exportVisible = ref(false);
const exportOptionData = ref<MsExportDrawerMap>({});
const syncObject = reactive({
time: '',
operator: '',
@ -268,6 +274,10 @@
console.log('create', record);
};
const handleExport = () => {
exportVisible.value = true;
};
const jumpToTestPlan = (record: BugListItem) => {
router.push({
name: 'testPlan',
@ -278,8 +288,14 @@
});
};
const setExportOptionData = async () => {
const res = await getExportConfig(projectId.value);
exportOptionData.value = res;
};
onMounted(() => {
setLoadListParams({ projectId: projectId.value });
fetchData();
setExportOptionData();
});
</script>