diff --git a/packages/base/src/data/EmitterKey.ts b/packages/base/src/data/EmitterKey.ts
index 492004229c..f5a08fb494 100644
--- a/packages/base/src/data/EmitterKey.ts
+++ b/packages/base/src/data/EmitterKey.ts
@@ -13,7 +13,8 @@ enum EmitterKey {
DMS_Refresh_Export_Data_Workflow = 'DMS_Refresh_Export_Data_Workflow',
DMS_Refresh_Sync_Data_Source = 'DMS_Refresh_Sync_Data_Source',
DMS_Refresh_Global_Data_Source = 'DMS_Refresh_Global_Data_Source',
- DMS_Batch_Test_Data_Source_Connection = 'DMS_Batch_Test_Data_Source_Connection'
+ DMS_Batch_Test_Data_Source_Connection = 'DMS_Batch_Test_Data_Source_Connection',
+ Refresh_Resource_Overview_Page = 'Refresh_Resource_Overview_Page'
}
export default EmitterKey;
diff --git a/packages/base/src/hooks/useBusinessTag/index.tsx b/packages/base/src/hooks/useBusinessTag/index.tsx
new file mode 100644
index 0000000000..3707690161
--- /dev/null
+++ b/packages/base/src/hooks/useBusinessTag/index.tsx
@@ -0,0 +1,48 @@
+import { useRequest } from 'ahooks';
+import { DmsApi } from '@actiontech/shared/lib/api';
+import { ResponseCode } from '@actiontech/shared/lib/enum';
+import { useMemo } from 'react';
+
+const useBusinessTag = () => {
+ const {
+ data,
+ loading,
+ run: updateBusinessTagList
+ } = useRequest(
+ () => {
+ return DmsApi.ProjectService.ListBusinessTags({
+ page_size: 1000
+ })
+ .then((res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ return res.data.data;
+ }
+ return [];
+ })
+ .catch(() => {
+ return [];
+ });
+ },
+ {
+ manual: true
+ }
+ );
+
+ const businessTagOptions = useMemo(() => {
+ return (
+ data?.map((businessTag) => ({
+ label: businessTag.name,
+ value: businessTag.uid
+ })) ?? []
+ );
+ }, [data]);
+
+ return {
+ businessTagList: data ?? [],
+ businessTagOptions,
+ loading,
+ updateBusinessTagList
+ };
+};
+
+export default useBusinessTag;
diff --git a/packages/base/src/hooks/useProjectTips/index.ts b/packages/base/src/hooks/useProjectTips/index.ts
index 5c2705f28b..c4369ca224 100644
--- a/packages/base/src/hooks/useProjectTips/index.ts
+++ b/packages/base/src/hooks/useProjectTips/index.ts
@@ -3,26 +3,33 @@ import { DmsApi } from '@actiontech/shared/lib/api';
import { ResponseCode } from '@actiontech/shared/lib/enum';
import { useBoolean } from 'ahooks';
import { IListProjectV2 } from '@actiontech/shared/lib/api/base/service/common';
+import { IListProjectsV2Params } from '@actiontech/shared/lib/api/base/service/Project/index.d';
const useProjectTips = () => {
const [projectList, setProjectList] = useState([]);
const [loading, { setTrue, setFalse }] = useBoolean();
- const updateProjects = useCallback(() => {
- setTrue();
- DmsApi.ProjectService.ListProjectsV2({ page_size: 9999 })
- .then((res) => {
- if (res.data.code === ResponseCode.SUCCESS) {
- setProjectList(res.data.data ?? []);
- }
+ const updateProjects = useCallback(
+ (params?: Omit) => {
+ setTrue();
+ DmsApi.ProjectService.ListProjectsV2({
+ ...(params ?? {}),
+ page_size: 9999
})
- .catch(() => {
- setProjectList([]);
- })
- .finally(() => {
- setFalse();
- });
- }, [setFalse, setTrue]);
+ .then((res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ setProjectList(res.data.data ?? []);
+ }
+ })
+ .catch(() => {
+ setProjectList([]);
+ })
+ .finally(() => {
+ setFalse();
+ });
+ },
+ [setFalse, setTrue]
+ );
const projectIDOptions = useMemo(() => {
return projectList.map((project) => {
diff --git a/packages/base/src/hooks/useThemeStyleData/__snapshots__/index.test.tsx.snap b/packages/base/src/hooks/useThemeStyleData/__snapshots__/index.test.tsx.snap
index 54d97c15dc..a2783cf0ce 100644
--- a/packages/base/src/hooks/useThemeStyleData/__snapshots__/index.test.tsx.snap
+++ b/packages/base/src/hooks/useThemeStyleData/__snapshots__/index.test.tsx.snap
@@ -84,6 +84,18 @@ exports[`test base/hooks/useThemeStyleData should match snapshot 1`] = `
"modify": "#292C33",
},
},
+ "resourceOverview": {
+ "chart": {
+ "activeStroke": "#ffffff",
+ "markShowdowColor": "rgba(26, 206, 219, 0.2)",
+ "markStroke": "rgba(26, 206, 219, 0.2)",
+ "pieStroke": "#ffffff",
+ },
+ "icon": {
+ "databaseFilled": "#4583FF",
+ "schemaFilled": "#15C7D4",
+ },
+ },
"sideMenu": {
"projectSelector": {
"dropdown": {
diff --git a/packages/base/src/locale/zh-CN/dmsProject.ts b/packages/base/src/locale/zh-CN/dmsProject.ts
index 889bbaa970..ad37ed5c1d 100644
--- a/packages/base/src/locale/zh-CN/dmsProject.ts
+++ b/packages/base/src/locale/zh-CN/dmsProject.ts
@@ -89,5 +89,6 @@ export default {
'业务:在项目的基础上,业务作为子项目或特定业务领域的标签,帮助您进一步组织和细化数据资源。业务层使您能够针对特定需求进行数据的集中管理和操作。',
dataSource:
'数据源:数据源是构建在业务基础上的实际操作对象。您可以在数据源层面执行具体的数据操作,如查询、更新和管理数据。'
- }
+ },
+ resourceOverview: '资源全景视图'
};
diff --git a/packages/base/src/locale/zh-CN/index.ts b/packages/base/src/locale/zh-CN/index.ts
index f715c66cd9..be15b1e9f9 100644
--- a/packages/base/src/locale/zh-CN/index.ts
+++ b/packages/base/src/locale/zh-CN/index.ts
@@ -13,7 +13,7 @@ import dmsMenu from './dmsMenu';
import dmsAccount from './dmsAccount';
import dmsDataExport from './dmsDataExport';
import dmsGlobalDataSource from './dmsGlobalDataSource';
-
+import resourceOverview from './resourceOverview';
// eslint-disable-next-line import/no-anonymous-default-export
export default {
translation: {
@@ -30,6 +30,7 @@ export default {
dmsMenu,
dmsAccount,
dmsDataExport,
- dmsGlobalDataSource
+ dmsGlobalDataSource,
+ resourceOverview
}
};
diff --git a/packages/base/src/locale/zh-CN/resourceOverview.ts b/packages/base/src/locale/zh-CN/resourceOverview.ts
new file mode 100644
index 0000000000..6543b9a673
--- /dev/null
+++ b/packages/base/src/locale/zh-CN/resourceOverview.ts
@@ -0,0 +1,37 @@
+// eslint-disable-next-line import/no-anonymous-default-export
+export default {
+ title: '资源全景视图',
+ baseInfo: {
+ businessCount: '业务总数',
+ projectCount: '项目总数',
+ sourceCount: '数据源总数'
+ },
+
+ expandAll: '展开全部',
+ collapseAll: '折叠全部',
+
+ resourceTopology: '资源拓扑',
+ resourceDetail: '资源详情',
+ selectProjectFirst: '请先选择所属项目',
+ unboundProject: '暂未关联项目',
+ resourceList: {
+ resourceName: '资源名称',
+ type: '资源类型',
+ business: '所属业务',
+ project: '所属项目',
+ environment: '所属环境',
+ auditScore: '审核质量评分',
+ auditScoreTips: '分数区间是(0-100)分',
+ highPrioritySqlCount: '高优先级SQL数',
+ pendingWorkflowCount: '待处理工单数量',
+ viewDetail: '查看详情'
+ },
+ distributionChart: {
+ title: '资源类型分布',
+ emptyText: '暂无数据,购买数据源后回到本页查看',
+ sourceTotal: '数据源总计',
+ sourceNumItem: '数据源数量',
+ sourceProportionItem: '数据源占比'
+ },
+ export: '导出'
+};
diff --git a/packages/base/src/page/DataSource/components/AddDataSource/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataSource/components/AddDataSource/__snapshots__/index.test.tsx.snap
index f6aa26893d..a654126ea4 100644
--- a/packages/base/src/page/DataSource/components/AddDataSource/__snapshots__/index.test.tsx.snap
+++ b/packages/base/src/page/DataSource/components/AddDataSource/__snapshots__/index.test.tsx.snap
@@ -2612,63 +2612,65 @@ exports[`page/DataSource/AddDataSource render submit when add database api succe
+
+
diff --git a/packages/base/src/router/test/__snapshots__/router.sqle.test.tsx.snap b/packages/base/src/router/test/__snapshots__/router.sqle.test.tsx.snap
index ce879c4c75..4183a208e6 100644
--- a/packages/base/src/router/test/__snapshots__/router.sqle.test.tsx.snap
+++ b/packages/base/src/router/test/__snapshots__/router.sqle.test.tsx.snap
@@ -695,6 +695,13 @@ exports[`base/router-sqle-ee render sqle route data snap 1`] = `
"key": "project",
"path": "/project",
},
+ {
+ "element":
+ resourceOverview
+
,
+ "key": "resourceOverview",
+ "path": "/resource-overview",
+ },
{
"children": [
{
diff --git a/packages/base/src/router/test/router.base.test.tsx b/packages/base/src/router/test/router.base.test.tsx
index 77a7a7bab4..792168cf47 100644
--- a/packages/base/src/router/test/router.base.test.tsx
+++ b/packages/base/src/router/test/router.base.test.tsx
@@ -183,6 +183,15 @@ describe('base/router-base-ee', () => {
});
});
+ describe('render route resourceOverview', () => {
+ it('render router resourceOverview', async () => {
+ const { baseElement } = customRender(['/resource-overview']);
+
+ expect(screen.getByText('resourceOverview')).toBeInTheDocument();
+ expect(baseElement).toMatchSnapshot();
+ });
+ });
+
describe('render route syncDataSource', () => {
it('render route syncDataSourceList', async () => {
const { baseElement } = customRender([
diff --git a/packages/base/src/testUtils/mockApi/project/data.ts b/packages/base/src/testUtils/mockApi/project/data.ts
index 517ccc784e..f6399f0f04 100644
--- a/packages/base/src/testUtils/mockApi/project/data.ts
+++ b/packages/base/src/testUtils/mockApi/project/data.ts
@@ -98,7 +98,7 @@ export const mockBatchImportDBCheckData: IImportDBServiceV2[] = [
port: '3306',
user: 'root',
password: '123456',
- environment_tag_uid: '1',
+ environment_tag_name: 'environment-1',
maintenance_times: [
{
maintenance_start_time: {
@@ -130,7 +130,7 @@ export const mockBatchImportDBCheckData: IImportDBServiceV2[] = [
port: '1521',
user: 'system',
password: '123456',
- environment_tag_uid: '2',
+ environment_tag_name: 'environment-2',
maintenance_times: [],
desc: 'oracle_1',
sqle_config: {
diff --git a/packages/base/src/theme/dark/index.ts b/packages/base/src/theme/dark/index.ts
index 33984233d5..7376f47f00 100644
--- a/packages/base/src/theme/dark/index.ts
+++ b/packages/base/src/theme/dark/index.ts
@@ -5,6 +5,7 @@ import { systemTheme } from './system';
import { dataExportTheme } from './dataExport';
import { sqleDarkTheme } from 'sqle/src/theme/dark';
import { iconTheme } from './icon';
+import { resourceOverviewTheme } from './resourceOverview';
const darkTheme = createTheme({
baseTheme: {
@@ -12,7 +13,8 @@ const darkTheme = createTheme({
guidance: guidanceTheme,
system: systemTheme,
dataExport: dataExportTheme,
- icon: iconTheme
+ icon: iconTheme,
+ resourceOverview: resourceOverviewTheme
},
sqleTheme: sqleDarkTheme
});
diff --git a/packages/base/src/theme/dark/resourceOverview.ts b/packages/base/src/theme/dark/resourceOverview.ts
new file mode 100644
index 0000000000..d2c628c11a
--- /dev/null
+++ b/packages/base/src/theme/dark/resourceOverview.ts
@@ -0,0 +1,14 @@
+import { ResourceOverviewTheme } from '../type';
+
+export const resourceOverviewTheme: ResourceOverviewTheme = {
+ icon: {
+ databaseFilled: '#4583FF',
+ schemaFilled: '#15C7D4'
+ },
+ chart: {
+ pieStroke: '#fff',
+ markStroke: 'rgba(26, 206, 219, 0.2)',
+ markShowdowColor: 'rgba(26, 206, 219, 0.2)',
+ activeStroke: '#fff'
+ }
+};
diff --git a/packages/base/src/theme/light/index.ts b/packages/base/src/theme/light/index.ts
index 66959630ed..8d94bad216 100644
--- a/packages/base/src/theme/light/index.ts
+++ b/packages/base/src/theme/light/index.ts
@@ -5,6 +5,7 @@ import { sqleLightTheme } from 'sqle/src/theme/light';
import { systemTheme } from './system';
import { dataExportTheme } from './dataExport';
import { iconTheme } from './icon';
+import { resourceOverviewTheme } from './resourceOverview';
const lightTheme = createTheme({
baseTheme: {
@@ -12,7 +13,8 @@ const lightTheme = createTheme({
guidance: guidanceTheme,
system: systemTheme,
dataExport: dataExportTheme,
- icon: iconTheme
+ icon: iconTheme,
+ resourceOverview: resourceOverviewTheme
},
sqleTheme: sqleLightTheme
});
diff --git a/packages/base/src/theme/light/resourceOverview.ts b/packages/base/src/theme/light/resourceOverview.ts
new file mode 100644
index 0000000000..3ca5f534e7
--- /dev/null
+++ b/packages/base/src/theme/light/resourceOverview.ts
@@ -0,0 +1,14 @@
+import { ResourceOverviewTheme } from '../type';
+
+export const resourceOverviewTheme: ResourceOverviewTheme = {
+ icon: {
+ databaseFilled: '#4583FF',
+ schemaFilled: '#15C7D4'
+ },
+ chart: {
+ pieStroke: '#ffffff',
+ markStroke: 'rgba(26, 206, 219, 0.2)',
+ markShowdowColor: 'rgba(26, 206, 219, 0.2)',
+ activeStroke: '#ffffff'
+ }
+};
diff --git a/packages/base/src/theme/type.ts b/packages/base/src/theme/type.ts
index cf427abcbc..eb485f1157 100644
--- a/packages/base/src/theme/type.ts
+++ b/packages/base/src/theme/type.ts
@@ -100,3 +100,16 @@ export type IconTheme = {
modify: string;
};
};
+
+export type ResourceOverviewTheme = {
+ icon: {
+ databaseFilled: string;
+ schemaFilled: string;
+ };
+ chart: {
+ pieStroke: string;
+ markStroke: string;
+ markShowdowColor: string;
+ activeStroke: string;
+ };
+};
diff --git a/packages/base/src/types/theme.type.ts b/packages/base/src/types/theme.type.ts
index 9cf0c5617b..70a7198502 100644
--- a/packages/base/src/types/theme.type.ts
+++ b/packages/base/src/types/theme.type.ts
@@ -3,7 +3,8 @@ import {
GuidanceTheme,
SideMenuTheme,
SystemTheme,
- IconTheme
+ IconTheme,
+ ResourceOverviewTheme
} from '../theme/type';
export interface BaseTheme {
@@ -12,6 +13,7 @@ export interface BaseTheme {
system: SystemTheme;
dataExport: DataExportTheme;
icon: IconTheme;
+ resourceOverview: ResourceOverviewTheme;
}
interface ThemeCustom {
diff --git a/packages/shared/lib/api/base/index.ts b/packages/shared/lib/api/base/index.ts
index 3f29c48ddc..e81b5bf974 100644
--- a/packages/shared/lib/api/base/index.ts
+++ b/packages/shared/lib/api/base/index.ts
@@ -22,3 +22,4 @@ export { default as SMSService } from './service/SMS';
export { default as UserService } from './service/User';
export { default as UserGroupService } from './service/UserGroup';
export { default as WebhookService } from './service/Webhook';
+export { default as ResourceOverviewService } from './service/ResourceOverview';
diff --git a/packages/shared/lib/api/base/service/ResourceOverview/index.d.ts b/packages/shared/lib/api/base/service/ResourceOverview/index.d.ts
new file mode 100644
index 0000000000..b3d379b6b6
--- /dev/null
+++ b/packages/shared/lib/api/base/service/ResourceOverview/index.d.ts
@@ -0,0 +1,50 @@
+import {
+ IResourceOverviewResourceListResV1,
+ IResourceOverviewResourceTypeDistributionResV1,
+ IResourceOverviewStatisticsResV1,
+ IResourceOverviewTopologyResV1
+} from '../common.d';
+
+export interface IGetResourceOverviewResourceListV1Params {
+ filter_by_db_type?: string;
+
+ filter_by_business_tag_uid?: string;
+
+ filter_by_environment_tag_uid?: string;
+
+ filter_by_project_uid?: string;
+
+ fuzzy_search_resource_name?: string;
+
+ sort_by_field?: string;
+
+ sort_asc?: boolean;
+
+ page_index?: number;
+
+ page_size?: number;
+}
+
+export interface IGetResourceOverviewResourceListV1Return
+ extends IResourceOverviewResourceListResV1 {}
+
+export interface IGetResourceOverviewResourceTypeDistributionV1Return
+ extends IResourceOverviewResourceTypeDistributionResV1 {}
+
+export interface IGetResourceOverviewStatisticsV1Return
+ extends IResourceOverviewStatisticsResV1 {}
+
+export interface IGetResourceOverviewTopologyV1Params {
+ filter_by_db_type?: string;
+
+ filter_by_business_tag_uid?: string;
+
+ filter_by_environment_tag_uid?: string;
+
+ filter_by_project_uid?: string;
+
+ fuzzy_search_resource_name?: string;
+}
+
+export interface IGetResourceOverviewTopologyV1Return
+ extends IResourceOverviewTopologyResV1 {}
diff --git a/packages/shared/lib/api/base/service/ResourceOverview/index.ts b/packages/shared/lib/api/base/service/ResourceOverview/index.ts
new file mode 100644
index 0000000000..2f59f6218e
--- /dev/null
+++ b/packages/shared/lib/api/base/service/ResourceOverview/index.ts
@@ -0,0 +1,62 @@
+/* tslint:disable no-identical-functions */
+/* tslint:disable no-useless-cast */
+/* tslint:disable no-unnecessary-type-assertion */
+/* tslint:disable no-big-function */
+/* tslint:disable no-duplicate-string */
+import ServiceBase from '../Service.base';
+import { AxiosRequestConfig } from 'axios';
+
+import {
+ IGetResourceOverviewResourceListV1Params,
+ IGetResourceOverviewResourceListV1Return,
+ IGetResourceOverviewResourceTypeDistributionV1Return,
+ IGetResourceOverviewStatisticsV1Return,
+ IGetResourceOverviewTopologyV1Params,
+ IGetResourceOverviewTopologyV1Return
+} from './index.d';
+
+class ResourceOverviewService extends ServiceBase {
+ public GetResourceOverviewResourceListV1(
+ params: IGetResourceOverviewResourceListV1Params,
+ options?: AxiosRequestConfig
+ ) {
+ const paramsData = this.cloneDeep(params);
+ return this.get
(
+ '/v1/dms/resource_overview/resource_list',
+ paramsData,
+ options
+ );
+ }
+
+ public GetResourceOverviewResourceTypeDistributionV1(
+ options?: AxiosRequestConfig
+ ) {
+ return this.get(
+ '/v1/dms/resource_overview/resource_type_distribution',
+ undefined,
+ options
+ );
+ }
+
+ public GetResourceOverviewStatisticsV1(options?: AxiosRequestConfig) {
+ return this.get(
+ '/v1/dms/resource_overview/statistics',
+ undefined,
+ options
+ );
+ }
+
+ public GetResourceOverviewTopologyV1(
+ params: IGetResourceOverviewTopologyV1Params,
+ options?: AxiosRequestConfig
+ ) {
+ const paramsData = this.cloneDeep(params);
+ return this.get(
+ '/v1/dms/resource_overview/topology',
+ paramsData,
+ options
+ );
+ }
+}
+
+export default new ResourceOverviewService();
diff --git a/packages/shared/lib/api/base/service/common.d.ts b/packages/shared/lib/api/base/service/common.d.ts
index 72131b5a65..4b5832456d 100644
--- a/packages/shared/lib/api/base/service/common.d.ts
+++ b/packages/shared/lib/api/base/service/common.d.ts
@@ -1073,34 +1073,6 @@ export interface IImportDBService {
user?: string;
}
-export interface IImportDBServiceCommon {
- additional_params?: IAdditionalParam[];
-
- db_type?: string;
-
- desc?: string;
-
- host?: string;
-
- is_enable_masking?: boolean;
-
- maintenance_times?: IMaintenanceTime[];
-
- name?: string;
-
- password?: string;
-
- port?: string;
-
- project_uid?: string;
-
- source?: string;
-
- sqle_config?: ISQLEConfig;
-
- user?: string;
-}
-
export interface IImportDBServiceV2 {
additional_params?: IAdditionalParam[];
@@ -1108,7 +1080,7 @@ export interface IImportDBServiceV2 {
desc?: string;
- environment_tag_uid: string;
+ environment_tag_name: string;
host?: string;
@@ -2131,6 +2103,96 @@ export interface IRejectDataExportWorkflowReq {
payload?: IRejectDataExportWorkflowPayload;
}
+export interface IResourceBusiness {
+ business_tag?: IBusinessTag;
+
+ project?: IResourceProject[];
+}
+
+export interface IResourceDBService {
+ db_service_name?: string;
+
+ db_service_uid?: string;
+}
+
+export interface IResourceListData {
+ audit_score?: number;
+
+ business_tag?: IBusinessTag;
+
+ environment_tag?: IEnvironmentTag;
+
+ high_priority_sql_count?: number;
+
+ pending_workflow_count?: number;
+
+ project?: IResourceProject;
+
+ resource_name?: string;
+
+ resource_type?: string;
+
+ resource_uid?: string;
+}
+
+export interface IResourceOverviewResourceListResV1 {
+ code?: number;
+
+ data?: IResourceListData[];
+
+ message?: string;
+
+ total_nums?: number;
+}
+
+export interface IResourceOverviewResourceTypeDistributionResV1 {
+ code?: number;
+
+ data?: IResourceTypeDistributionData[];
+
+ message?: string;
+
+ total_nums?: number;
+}
+
+export interface IResourceOverviewStatisticsResV1 {
+ code?: number;
+
+ data?: {
+ business_total_number?: number;
+
+ db_service_total_number?: number;
+
+ project_total_number?: number;
+ };
+
+ message?: string;
+}
+
+export interface IResourceOverviewTopologyResV1 {
+ code?: number;
+
+ data?: IResourceBusiness[];
+
+ message?: string;
+
+ total_nums?: number;
+}
+
+export interface IResourceProject {
+ db_service?: IResourceDBService[];
+
+ project_name?: string;
+
+ project_uid?: string;
+}
+
+export interface IResourceTypeDistributionData {
+ count?: number;
+
+ resource_type?: string;
+}
+
export interface IRole {
desc?: string;
diff --git a/packages/shared/lib/components/EditableSelect/EditableSelect.tsx b/packages/shared/lib/components/EditableSelect/EditableSelect.tsx
index 724b4c9d3e..1fe1275b08 100644
--- a/packages/shared/lib/components/EditableSelect/EditableSelect.tsx
+++ b/packages/shared/lib/components/EditableSelect/EditableSelect.tsx
@@ -18,6 +18,7 @@ import {
DownOutlined
} from '@actiontech/icons';
import type { MenuProps } from 'antd';
+import { ReminderInformation } from '../ReminderInformation';
const EditableSelect: React.FC = ({
value,
@@ -34,7 +35,7 @@ const EditableSelect: React.FC = ({
addable = true,
updatable = true,
deletable = true,
- onConfirmOpenChange
+ errorMessage
}) => {
const { t } = useTranslation();
const [newItemName, setNewItemName] = useState('');
@@ -44,6 +45,9 @@ const EditableSelect: React.FC = ({
);
const [editName, setEditName] = useState('');
const [dropdownOpen, setDropdownOpen] = useState(false);
+ const [deletingItem, setDeletingItem] = useState(
+ null
+ );
const handleAdd = () => {
onAdd?.(newItemName);
@@ -63,6 +67,7 @@ const EditableSelect: React.FC = ({
const startEditing = (item: EditableSelectOption) => {
setEditingItem(item);
setEditName(item.label);
+ setDeletingItem(null);
};
const cancelEditing = () => {
@@ -72,6 +77,7 @@ const EditableSelect: React.FC = ({
const startAdding = () => {
setIsAdding(true);
+ setDeletingItem(null);
};
const resetState = () => {
@@ -79,6 +85,7 @@ const EditableSelect: React.FC = ({
setIsAdding(false);
setEditingItem(null);
setEditName('');
+ setDeletingItem(null);
};
const onOpenChange = (open: boolean) => {
@@ -100,73 +107,78 @@ const EditableSelect: React.FC = ({
resetState();
},
label: (
-
- {editingItem?.value === option.value ? (
-
e.stopPropagation()}>
-
setEditName(e.target.value)}
- />
-
-
- {t('common.cancel')}
-
-
- {t('common.save')}
-
-
-
- ) : (
- <>
-
{option.label}
-
-
+
+
+ {editingItem?.value === option.value ? (
+
e.stopPropagation()}>
+
setEditName(e.target.value)}
+ />
+
+
+ {t('common.cancel')}
+
}
- onClick={(e) => {
- e.stopPropagation();
- startEditing(option);
- }}
- className="edit-button"
- />
-
-
- {
- e?.stopPropagation();
- return onDelete?.(option);
- }}
- onCancel={(e) => {
- e?.stopPropagation();
- }}
- onOpenChange={onConfirmOpenChange}
+ onClick={handleUpdate}
+ disabled={!editName.trim()}
>
+ {t('common.save')}
+
+
+
+ ) : (
+ <>
+
{option.label}
+
+
}
+ icon={}
onClick={(e) => {
e.stopPropagation();
+ startEditing(option);
}}
- className="delete-button"
+ className="edit-button"
/>
-
-
-
- >
- )}
+
+
+ {
+ e?.stopPropagation();
+ setDeletingItem(option);
+ onDelete?.(option);
+ }}
+ onCancel={(e) => {
+ e?.stopPropagation();
+ }}
+ >
+ }
+ onClick={(e) => {
+ e.stopPropagation();
+ }}
+ className="delete-button"
+ />
+
+
+
+ >
+ )}
+
+
+
+
)
}));
diff --git a/packages/shared/lib/components/EditableSelect/__tests__/EditableSelect.test.tsx b/packages/shared/lib/components/EditableSelect/__tests__/EditableSelect.test.tsx
index e28d319888..3a62acb156 100644
--- a/packages/shared/lib/components/EditableSelect/__tests__/EditableSelect.test.tsx
+++ b/packages/shared/lib/components/EditableSelect/__tests__/EditableSelect.test.tsx
@@ -220,6 +220,28 @@ describe('EditableSelect', () => {
expect(mockOnDelete).toHaveBeenCalledWith(mockOptions[0]);
});
+ it('render error message when confirm delete failed', async () => {
+ const errorMessage = '当前数据不可删除';
+ customRender({
+ errorMessage
+ });
+ fireEvent.click(getBySelector('.editable-select-trigger'));
+ await act(async () => jest.advanceTimersByTime(0));
+
+ const deleteButton = getAllBySelector('.delete-button')[0];
+ fireEvent.click(deleteButton);
+ await act(async () => jest.advanceTimersByTime(0));
+ expect(screen.getByText('确认要删除么?')).toBeInTheDocument();
+
+ const confirmButton = getBySelector('.ant-btn-primary.ant-btn-sm');
+ fireEvent.click(confirmButton);
+ await act(async () => jest.advanceTimersByTime(0));
+
+ expect(mockOnDelete).toHaveBeenCalledWith(mockOptions[0]);
+
+ expect(screen.getByText(errorMessage)).toBeInTheDocument();
+ });
+
it('should not show actions when disabled', async () => {
customRender({
disabled: true
diff --git a/packages/shared/lib/components/EditableSelect/__tests__/__snapshots__/EditableSelect.test.tsx.snap b/packages/shared/lib/components/EditableSelect/__tests__/__snapshots__/EditableSelect.test.tsx.snap
index 308f9f297a..48d6bc1667 100644
--- a/packages/shared/lib/components/EditableSelect/__tests__/__snapshots__/EditableSelect.test.tsx.snap
+++ b/packages/shared/lib/components/EditableSelect/__tests__/__snapshots__/EditableSelect.test.tsx.snap
@@ -90,63 +90,65 @@ exports[`EditableSelect should render select with options 2`] = `