From 6228e7c3b32775696020955c39a6d6050ccc86a3 Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Mon, 20 Jul 2026 13:53:52 +0800 Subject: [PATCH] feat: export SQL manage visible columns --- .../lib/api/sqle/service/SqlManage/index.d.ts | 2 + .../sqle/src/locale/en-US/sqlManagement.ts | 1 + .../sqle/src/locale/zh-CN/sqlManagement.ts | 1 + .../SQLEEIndex/exportColumnKeys.test.ts | 97 ++++++++++++++++++ .../component/SQLEEIndex/exportColumnKeys.ts | 92 +++++++++++++++++ .../component/SQLEEIndex/index.test.tsx | 98 ++++++++++++++++++- .../component/SQLEEIndex/index.tsx | 21 +++- 7 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.test.ts create mode 100644 packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.ts diff --git a/packages/shared/lib/api/sqle/service/SqlManage/index.d.ts b/packages/shared/lib/api/sqle/service/SqlManage/index.d.ts index 4da46a09d..43f944a59 100644 --- a/packages/shared/lib/api/sqle/service/SqlManage/index.d.ts +++ b/packages/shared/lib/api/sqle/service/SqlManage/index.d.ts @@ -106,6 +106,8 @@ export interface IExportSqlManageV1Params { sort_order?: exportSqlManageV1SortOrderEnum; extra_filters?: string; + + export_column_keys?: string; } export interface IExportSqlManageRemediationV1Params { diff --git a/packages/sqle/src/locale/en-US/sqlManagement.ts b/packages/sqle/src/locale/en-US/sqlManagement.ts index aa2dfb516..f4b89c0b1 100644 --- a/packages/sqle/src/locale/en-US/sqlManagement.ts +++ b/packages/sqle/src/locale/en-US/sqlManagement.ts @@ -8,6 +8,7 @@ export default { exporting: 'Exporting file', exportSuccessTips: 'Export file successfully', exportFailedTips: 'Export file failed', + noExportColumnTips: 'Please select at least one export column', remediationExportCurrentProject: 'Export Current Project SQL Remediation Report', remediationExportAllProjects: diff --git a/packages/sqle/src/locale/zh-CN/sqlManagement.ts b/packages/sqle/src/locale/zh-CN/sqlManagement.ts index 454417e07..0f256e7f2 100644 --- a/packages/sqle/src/locale/zh-CN/sqlManagement.ts +++ b/packages/sqle/src/locale/zh-CN/sqlManagement.ts @@ -8,6 +8,7 @@ export default { exporting: '正在导出文件', exportSuccessTips: '导出文件成功', exportFailedTips: '导出文件失败', + noExportColumnTips: '请至少选择一个导出列', remediationExportCurrentProject: '导出当前项目SQL整改报表', remediationExportAllProjects: '导出所有项目SQL整改报表', remediationExporting: '正在导出 SQL 管控整改报表', diff --git a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.test.ts b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.test.ts new file mode 100644 index 000000000..d85035930 --- /dev/null +++ b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.test.ts @@ -0,0 +1,97 @@ +import LocalStorageWrapper from '@actiontech/shared/lib/utils/LocalStorageWrapper'; +import SqlManagementColumn from './column'; +import { + getSqlManagementExportColumnKeys, + SQL_MANAGEMENT_TABLE_NAME +} from './exportColumnKeys'; + +const mockUpdateRemark = jest.fn(); +const mockOpenModal = jest.fn(); + +const columns = SqlManagementColumn( + '700300', + true, + mockUpdateRemark, + mockOpenModal +); + +describe('page/SqlManagement/SQLEEIndex/exportColumnKeys', () => { + afterEach(() => { + localStorage.clear(); + jest.clearAllMocks(); + }); + + it('return default export columns when current user has no settings', () => { + expect(getSqlManagementExportColumnKeys(columns, 'admin')).toEqual([ + 'sql_fingerprint', + 'sql', + 'source', + 'audit_result', + 'instance_name', + 'schema_name', + 'priority', + 'assignees', + 'endpoints', + 'status', + 'remark' + ]); + }); + + it('return visible business columns by local settings order', () => { + LocalStorageWrapper.set( + SQL_MANAGEMENT_TABLE_NAME, + JSON.stringify({ + admin: { + sql_fingerprint: { order: 2, show: true }, + sql: { order: 1, show: true }, + source: { order: 3, show: false }, + audit_result: { order: 4, show: true }, + selection: { order: 5, show: true }, + action: { order: 6, show: true }, + filter_business: { order: 7, show: true } + } + }) + ); + + expect(getSqlManagementExportColumnKeys(columns, 'admin')).toEqual([ + 'sql', + 'sql_fingerprint', + 'audit_result', + 'instance_name', + 'schema_name', + 'priority', + 'assignees', + 'endpoints', + 'status', + 'remark' + ]); + }); + + it('return empty array when all export columns are hidden', () => { + LocalStorageWrapper.set( + SQL_MANAGEMENT_TABLE_NAME, + JSON.stringify({ + admin: Object.fromEntries( + [ + 'sql_fingerprint', + 'sql', + 'source', + 'audit_result', + 'instance_name', + 'schema_name', + 'priority', + 'assignees', + 'endpoints', + 'status', + 'remark' + ].map((columnKey, index) => [ + columnKey, + { order: index + 1, show: false } + ]) + ) + }) + ); + + expect(getSqlManagementExportColumnKeys(columns, 'admin')).toEqual([]); + }); +}); diff --git a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.ts b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.ts new file mode 100644 index 000000000..4b473be0b --- /dev/null +++ b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/exportColumnKeys.ts @@ -0,0 +1,92 @@ +import LocalStorageWrapper from '@actiontech/shared/lib/utils/LocalStorageWrapper'; +import { ActiontechTableColumn } from '@actiontech/shared/lib/components/ActiontechTable'; +import { ISqlManage } from '@actiontech/shared/lib/api/sqle/service/common'; +import { SqlManagementTableFilterParamType } from './column'; + +export const SQL_MANAGEMENT_TABLE_NAME = 'sql_management_list'; + +const SQL_MANAGEMENT_EXPORT_COLUMN_KEYS = [ + 'sql_fingerprint', + 'sql', + 'source', + 'audit_result', + 'instance_name', + 'schema_name', + 'priority', + 'assignees', + 'endpoints', + 'status', + 'remark' +] as const; + +type SqlManagementExportColumnKey = + (typeof SQL_MANAGEMENT_EXPORT_COLUMN_KEYS)[number]; + +type SqlManagementColumnSettings = Partial< + Record< + string, + { + order?: number; + show?: boolean; + } + > +>; + +const exportColumnKeySet = new Set(SQL_MANAGEMENT_EXPORT_COLUMN_KEYS); + +const getColumnDataIndex = ( + dataIndex: ActiontechTableColumn< + ISqlManage, + SqlManagementTableFilterParamType + >[number]['dataIndex'] +) => { + return typeof dataIndex === 'string' ? dataIndex : undefined; +}; + +const readLocalColumnSettings = ( + tableName: string, + username: string +): SqlManagementColumnSettings => { + try { + const localColumns = JSON.parse( + LocalStorageWrapper.getOrDefault(tableName, '{}') + ); + return localColumns?.[username] ?? {}; + } catch { + return {}; + } +}; + +export const getSqlManagementExportColumnKeys = ( + columns: ActiontechTableColumn, + username: string, + tableName = SQL_MANAGEMENT_TABLE_NAME +): SqlManagementExportColumnKey[] => { + const localColumnSettings = readLocalColumnSettings(tableName, username); + + return columns + .map((column, index) => { + const columnKey = getColumnDataIndex(column.dataIndex); + if (!columnKey || !exportColumnKeySet.has(columnKey)) { + return undefined; + } + + const localSetting = localColumnSettings[columnKey]; + return { + key: columnKey as SqlManagementExportColumnKey, + show: localSetting?.show ?? column.show ?? true, + order: localSetting?.order ?? index + 1, + defaultOrder: index + 1 + }; + }) + .filter((column): column is NonNullable => { + return !!column && column.show; + }) + .sort((currentColumn, nextColumn) => { + return ( + currentColumn.order - nextColumn.order || + currentColumn.defaultOrder - nextColumn.defaultOrder + ); + }) + .map((column) => column.key); +}; diff --git a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.test.tsx b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.test.tsx index 617ec0e7f..7c60157dd 100644 --- a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.test.tsx +++ b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.test.tsx @@ -27,6 +27,8 @@ import { exportSqlManageV1FilterPriorityEnum, exportSqlManageRemediationV1ExportScopeEnum } from '@actiontech/shared/lib/api/sqle/service/SqlManage/index.enum'; +import LocalStorageWrapper from '@actiontech/shared/lib/utils/LocalStorageWrapper'; +import { SQL_MANAGEMENT_TABLE_NAME } from './exportColumnKeys'; jest.mock('react-redux', () => { return { @@ -87,6 +89,7 @@ describe('page/SqlManagement/SQLEEIndex', () => { jest.clearAllMocks(); jest.restoreAllMocks(); jest.clearAllTimers(); + localStorage.clear(); cleanup(); }); @@ -243,7 +246,9 @@ describe('page/SqlManagement/SQLEEIndex', () => { { ...exportParams, filter_assignee: mockCurrentUserReturn.uid, - filter_priority: exportSqlManageV1FilterPriorityEnum.high + filter_priority: exportSqlManageV1FilterPriorityEnum.high, + export_column_keys: + 'sql_fingerprint,sql,source,audit_result,instance_name,schema_name,priority,assignees,endpoints,status,remark' }, { responseType: 'blob' @@ -253,6 +258,97 @@ describe('page/SqlManagement/SQLEEIndex', () => { expect(screen.getByText('导出文件成功')).toBeInTheDocument(); }); + it('export data with current visible column settings order', async () => { + const request = sqlManage.getSqlManageList(); + const exportRequest = sqlManage.exportSqlManage(); + superRender(); + expect(request).toHaveBeenCalled(); + + LocalStorageWrapper.set( + SQL_MANAGEMENT_TABLE_NAME, + JSON.stringify({ + [mockCurrentUserReturn.username]: { + sql_fingerprint: { order: 2, show: true }, + sql: { order: 1, show: true }, + source: { order: 3, show: false }, + audit_result: { order: 4, show: true }, + instance_name: { order: 5, show: true }, + schema_name: { order: 6, show: true }, + priority: { order: 7, show: true }, + assignees: { order: 8, show: false }, + endpoints: { order: 9, show: true }, + status: { order: 10, show: true }, + remark: { order: 11, show: false }, + selection: { order: 12, show: true } + } + }) + ); + + const searchText = 'fingerprint keyword'; + const inputEle = getBySelector('#actiontech-table-search-input'); + fireEvent.change(inputEle, { + target: { value: searchText } + }); + + await act(async () => { + fireEvent.keyDown(inputEle, { + key: 'Enter', + code: 'Enter', + keyCode: 13 + }); + await jest.advanceTimersByTime(300); + }); + await act(async () => jest.advanceTimersByTime(3000)); + + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); + await user.click(screen.getByText('导出报表')); + await user.click(await screen.findByText('导出 SQL管控报表')); + + expect(exportRequest).toHaveBeenCalledWith( + { + ...exportParams, + fuzzy_search_sql_fingerprint: searchText, + export_column_keys: + 'sql,sql_fingerprint,audit_result,instance_name,schema_name,priority,endpoints,status' + }, + { + responseType: 'blob' + } + ); + }); + + it('show tips without export request when no visible export columns', async () => { + const request = sqlManage.getSqlManageList(); + const exportRequest = sqlManage.exportSqlManage(); + superRender(); + expect(request).toHaveBeenCalled(); + + LocalStorageWrapper.set( + SQL_MANAGEMENT_TABLE_NAME, + JSON.stringify({ + [mockCurrentUserReturn.username]: { + sql_fingerprint: { order: 1, show: false }, + sql: { order: 2, show: false }, + source: { order: 3, show: false }, + audit_result: { order: 4, show: false }, + instance_name: { order: 5, show: false }, + schema_name: { order: 6, show: false }, + priority: { order: 7, show: false }, + assignees: { order: 8, show: false }, + endpoints: { order: 9, show: false }, + status: { order: 10, show: false }, + remark: { order: 11, show: false } + } + }) + ); + + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); + await user.click(screen.getByText('导出报表')); + await user.click(await screen.findByText('导出 SQL管控报表')); + expect(exportRequest).not.toHaveBeenCalled(); + expect(screen.getByText('请至少选择一个导出列')).toBeInTheDocument(); + }); + it('export remediation report for current project', async () => { const request = sqlManage.getSqlManageList(); const exportRequest = sqlManage.exportSqlManageRemediation(); diff --git a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.tsx b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.tsx index 08cde8c76..c30aa91fa 100644 --- a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.tsx +++ b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/index.tsx @@ -57,6 +57,10 @@ import { BlacklistResV1TypeEnum } from '@actiontech/shared/lib/api/sqle/service/ import { SqlManagementListStyleWrapper } from './style'; import { pickStaticSqlManageFilters } from './sourceExtra.utils'; import useSqlManageSourceExtra from './hooks/useSqlManageSourceExtra'; +import { + getSqlManagementExportColumnKeys, + SQL_MANAGEMENT_TABLE_NAME +} from './exportColumnKeys'; const SQLEEIndex = () => { const { t } = useTranslation(); @@ -331,7 +335,7 @@ const SQLEEIndex = () => { const tableSetting = useMemo( () => ({ - tableName: 'sql_management_list', + tableName: SQL_MANAGEMENT_TABLE_NAME, username: username }), [username] @@ -370,6 +374,18 @@ const SQLEEIndex = () => { { setFalse: finishExport, setTrue: startExport } ] = useBoolean(false); const handleExport = () => { + const exportColumnKeys = getSqlManagementExportColumnKeys( + columns, + username + ); + + if (!exportColumnKeys.length) { + messageApi.warning( + t('sqlManagement.pageHeader.action.noExportColumnTips') + ); + return; + } + startExport(); const hideLoading = messageApi.loading( t('sqlManagement.pageHeader.action.exporting') @@ -385,7 +401,8 @@ const SQLEEIndex = () => { ? exportSqlManageV1FilterPriorityEnum.high : undefined, page_index: undefined, - page_size: undefined + page_size: undefined, + export_column_keys: exportColumnKeys.join(',') } as IExportSqlManageV1Params; SqlManage.exportSqlManageV1(params, { responseType: 'blob' }) .then((res) => {