feat(缺陷管理): 导出窗口公共组件
This commit is contained in:
parent
1721410efa
commit
1a8b1cc1f5
|
@ -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}` });
|
||||
}
|
||||
|
|
|
@ -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/';
|
||||
|
|
|
@ -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>
|
|
@ -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',
|
||||
};
|
|
@ -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': '其他字段为系统生成字段, 导出后不支持导入',
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue