diff --git a/frontend/package.json b/frontend/package.json index ff1d42b19e..bd510095f4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,7 @@ "@types/color": "^3.0.4", "@vueuse/core": "^10.4.1", "ace-builds": "^1.24.2", + "ahooks-vue": "^0.15.1", "axios": "^0.24.0", "dayjs": "^1.11.9", "echarts": "^5.4.3", diff --git a/frontend/src/api/modules/bug-management/index.ts b/frontend/src/api/modules/bug-management/index.ts index 69e259a6ed..156e95368c 100644 --- a/frontend/src/api/modules/bug-management/index.ts +++ b/frontend/src/api/modules/bug-management/index.ts @@ -1,7 +1,7 @@ import MSR from '@/api/http/index'; import * as bugURL from '@/api/requrls/bug-management'; -import { BugListItem } from '@/models/bug-management'; +import { BugExportParams, BugListItem } from '@/models/bug-management'; import { CommonList, TableQueryParams, TemplateOption } from '@/models/common'; /** @@ -44,3 +44,13 @@ export function getTemplateById(data: TableQueryParams) { export function getExportConfig(projectId: string) { return MSR.get({ url: `${bugURL.getExportConfigUrl}${projectId}` }); } + +// 同步缺陷 +export function syncBugOpenSource(params: { projectId: string }) { + return MSR.get({ url: bugURL.getSyncBugOpenSourceUrl, params }); +} + +// 导出缺陷 +export function exportBug(data: BugExportParams) { + return MSR.post({ url: bugURL.postExportBugUrl, data }); +} diff --git a/frontend/src/api/requrls/bug-management.ts b/frontend/src/api/requrls/bug-management.ts index 1e65e37623..6f740660c1 100644 --- a/frontend/src/api/requrls/bug-management.ts +++ b/frontend/src/api/requrls/bug-management.ts @@ -7,3 +7,5 @@ export const postBatchDeleteBugUrl = '/bug/batch-delete'; export const getTemplateUrl = '/bug/template'; export const getTemplageOption = '/bug/template/option'; export const getExportConfigUrl = '/bug/export/columns/'; +export const getSyncBugOpenSourceUrl = '/bug/sync/'; +export const postExportBugUrl = '/bug/export'; diff --git a/frontend/src/components/business/ms-edit-comp/edit-comp.tsx b/frontend/src/components/business/ms-edit-comp/edit-comp.tsx index 4968a8f952..9aa050a93d 100644 --- a/frontend/src/components/business/ms-edit-comp/edit-comp.tsx +++ b/frontend/src/components/business/ms-edit-comp/edit-comp.tsx @@ -1,4 +1,5 @@ import { PropType } from 'vue'; +import { onClickOutside } from '@vueuse/core'; import { useI18n } from '@/hooks/useI18n'; @@ -46,6 +47,8 @@ export default defineComponent({ const { mode, options, modelValue, defaultValue } = toRefs(props); const oldModelValue = ref(modelValue.value); const editStatus = ref('null'); + const selectRef = ref(null); + const chengeStatus = ref(false); const { t } = useI18n(); @@ -54,6 +57,7 @@ export default defineComponent({ if (result) { defaultValue.value = v; Message.success(t('common.updateSuccess')); + chengeStatus.value = true; } else { Message.error(t('common.updateFail')); modelValue.value = oldModelValue.value; @@ -69,11 +73,15 @@ export default defineComponent({ const handleClick = () => { editStatus.value = 'active'; + if (mode.value === 'select') { + selectRef.value?.focus(); + } }; const handleReset = () => { editStatus.value = 'null'; modelValue.value = oldModelValue.value; + chengeStatus.value = false; }; const handleBlur = () => { @@ -109,12 +117,22 @@ export default defineComponent({ ); }; + onClickOutside(selectRef, (event) => { + if (editStatus.value === 'active' && !chengeStatus.value) { + handleReset(); + } + }); + const renderChild = () => { if (mode.value === 'input') { return ; } if (mode.value === 'select') { - return ; + return ( +
+ +
+ ); } if (mode.value === 'tagInput') { return ; diff --git a/frontend/src/components/business/ms-user-group-comp/userTable.vue b/frontend/src/components/business/ms-user-group-comp/userTable.vue index 8884533e14..35e4564846 100644 --- a/frontend/src/components/business/ms-user-group-comp/userTable.vue +++ b/frontend/src/components/business/ms-user-group-comp/userTable.vue @@ -109,7 +109,7 @@ selectable: false, noDisable: true, showSetting: false, - heightUsed: 280, + heightUsed: 288, }); const fetchData = async () => { diff --git a/frontend/src/models/bug-management.ts b/frontend/src/models/bug-management.ts index f3e117e336..19c42cf734 100644 --- a/frontend/src/models/bug-management.ts +++ b/frontend/src/models/bug-management.ts @@ -1,3 +1,5 @@ +import { BatchApiParams } from './common'; + export interface BugListItem { id: string; // 缺陷id num: string; // 缺陷编号 @@ -14,4 +16,14 @@ export interface BugListItem { updateTime: string; // 更新时间 deleted: boolean; // 删除标志 } + +export interface BugExportColumn { + key: string; // 字段key + text?: string; // 字段名称 + columnType?: string; // 字段类型 +} +export interface BugExportParams extends BatchApiParams { + bugExportColumns: BugExportColumn[]; // 导出字段 +} + export default {}; diff --git a/frontend/src/views/bug-management/index.vue b/frontend/src/views/bug-management/index.vue index cc1afc4856..2b04010dfd 100644 --- a/frontend/src/views/bug-management/index.vue +++ b/frontend/src/views/bug-management/index.vue @@ -4,12 +4,20 @@ - + @@ -77,7 +85,14 @@ - + + + (0); const currentDeleteObj = reactive<{ id: string; name: string }>({ id: '', name: '' }); const deleteVisible = ref(false); + const keyword = ref(''); + const licenseStore = useLicenseStore(); + const isXpack = computed(() => licenseStore.hasLicense()); + // 当前选择的条数 + const currentSelectParams = ref({ selectAll: false, currentSelectCount: 0 }); + + const { loading: syncBugLoading, run: syncBugRun } = useRequest( + () => syncBugOpenSource({ projectId: projectId.value }), + { + pollingInterval: 1 * 1000, + pollingWhenHidden: true, + manual: true, + } + ); const syncObject = reactive({ time: '', @@ -187,6 +225,7 @@ title: 'bugManagement.bugName', editType: ColumnEditTypeEnum.INPUT, dataIndex: 'name', + slotName: 'name', showTooltip: true, }, { @@ -267,7 +306,7 @@ } }; - const { propsRes, propsEvent, setKeyword, setLoadListParams, setProps } = useTable( + const { propsRes, propsEvent, setKeyword, setLoadListParams, setProps, resetSelector } = useTable( getBugList, { tableKey: TableKeyEnum.BUG_MANAGEMENT, @@ -281,6 +320,24 @@ (record) => handleNameChange(record) ); + const tableBatchActions = { + baseAction: [ + { + label: 'common.export', + eventTag: 'export', + }, + { + label: 'common.edit', + eventTag: 'edit', + }, + { + label: 'common.delete', + eventTag: 'delete', + danger: true, + }, + ], + }; + watchEffect(() => { setProps({ heightUsed: heightUsed.value }); }); @@ -339,9 +396,28 @@ const fetchData = async (v = '') => { setKeyword(v); + keyword.value = v; setProps({ data }); }; + const exportConfirm = async (option: MsExportDrawerOption[]) => { + try { + const { selectedIds, selectAll, excludeIds } = currentSelectParams.value; + await exportBug({ + selectIds: selectedIds || [], + selectAll, + excludeIds, + condition: { keyword: keyword.value }, + bugExportColumns: option.map((item) => item), + }); + Message.success(t('common.exportSuccess')); + exportVisible.value = false; + resetSelector(); + } catch (error) { + Message.error(t('common.exportFail')); + } + }; + const handleSingleDelete = (record?: TableData) => { if (record) { currentDeleteObj.id = record.id; @@ -360,7 +436,12 @@ }); }; const handleSync = () => { - syncVisible.value = true; + if (isXpack.value) { + syncVisible.value = true; + } else { + // 直接开始轮询 + syncBugRun(); + } }; const handleShowDetail = (id: string, rowIndex: number) => { @@ -379,9 +460,14 @@ console.log('create', record); }; - const handleDelete = (record: BugListItem) => { + const handleBatchDelete = (params: BatchActionQueryParams) => { // eslint-disable-next-line no-console - console.log('create', record); + console.log('create', params); + }; + + const handleBatchEdit = (params: BatchActionQueryParams) => { + // eslint-disable-next-line no-console + console.log('create', params); }; const handleExport = () => { @@ -456,6 +542,23 @@ { label: t('bugManagement.statusO.refused'), value: 'Refused' }, ]; + function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) { + currentSelectParams.value = params; + switch (event.eventTag) { + case 'export': + handleExport(); + break; + case 'delete': + handleBatchDelete(params); + break; + case 'edit': + handleBatchEdit(params); + break; + default: + break; + } + } + onMounted(() => { setLoadListParams({ projectId: projectId.value }); fetchData(); diff --git a/frontend/src/views/bug-management/locale/zh-CN.ts b/frontend/src/views/bug-management/locale/zh-CN.ts index d7a52769ab..78d7339b4e 100644 --- a/frontend/src/views/bug-management/locale/zh-CN.ts +++ b/frontend/src/views/bug-management/locale/zh-CN.ts @@ -28,6 +28,8 @@ export default { batchEdit: '批量编辑', selectProps: '选择属性', batchUpdate: '批量更新为', + exportBug: '导出缺陷', + exportBugCount: '已选 {count} 条缺陷', edit: { defaultSystemTemplate: '默认为系统模板', content: '缺陷内容', diff --git a/frontend/src/views/setting/system/user/index.vue b/frontend/src/views/setting/system/user/index.vue index 509a3fd577..c3a85e318c 100644 --- a/frontend/src/views/setting/system/user/index.vue +++ b/frontend/src/views/setting/system/user/index.vue @@ -275,6 +275,7 @@ selectable: true, showSetting: true, showJumpMethod: true, + heightUsed: 288, }, (record) => ({ ...record,