From 35e056e4640fbe96418b5ffed2e9c2f696d92101 Mon Sep 17 00:00:00 2001 From: wohainilaodou Date: Mon, 30 Dec 2024 17:56:09 +0800 Subject: [PATCH] [INLONG-11626][Dashboard] TheData access adds COS and SQL data sources --- .../src/plugins/nodes/defaults/COS.ts | 58 ++++ .../src/plugins/nodes/defaults/index.ts | 5 + .../src/plugins/sources/defaults/COS.ts | 271 ++++++++++++++++++ .../src/plugins/sources/defaults/SQL.ts | 259 +++++++++++++++++ .../src/plugins/sources/defaults/index.ts | 10 + .../streams/common/StreamDefaultInfo.ts | 6 - inlong-dashboard/src/ui/locales/cn.json | 11 + inlong-dashboard/src/ui/locales/en.json | 12 +- .../pages/GroupDetail/DataSources/index.tsx | 86 ++++-- .../pages/GroupDetail/DataStorage/index.tsx | 72 +++-- .../ui/pages/GroupDetail/DataStream/index.tsx | 87 ++++-- .../pages/GroupDetail/OperationLog/config.tsx | 21 +- .../pages/GroupDetail/OperationLog/index.tsx | 58 +++- 13 files changed, 858 insertions(+), 98 deletions(-) create mode 100644 inlong-dashboard/src/plugins/nodes/defaults/COS.ts create mode 100644 inlong-dashboard/src/plugins/sources/defaults/COS.ts create mode 100644 inlong-dashboard/src/plugins/sources/defaults/SQL.ts diff --git a/inlong-dashboard/src/plugins/nodes/defaults/COS.ts b/inlong-dashboard/src/plugins/nodes/defaults/COS.ts new file mode 100644 index 00000000000..e82a3cc5df0 --- /dev/null +++ b/inlong-dashboard/src/plugins/nodes/defaults/COS.ts @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { DataWithBackend } from '@/plugins/DataWithBackend'; +import { RenderRow } from '@/plugins/RenderRow'; +import { RenderList } from '@/plugins/RenderList'; +import { NodeInfo } from '../common/NodeInfo'; + +const { I18n } = DataWithBackend; +const { FieldDecorator } = RenderRow; + +export default class COSNodeInfo + extends NodeInfo + implements DataWithBackend, RenderRow, RenderList +{ + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + }) + @I18n('meta.Nodes.COS.BucketName') + bucketName: string; + + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + }) + @I18n('meta.Nodes.COS.CredentialsId') + credentialsId: string; + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + }) + @I18n('meta.Nodes.COS.CredentialsKey') + credentialsKey: string; + + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + }) + @I18n('meta.Nodes.COS.Region') + region: string; +} diff --git a/inlong-dashboard/src/plugins/nodes/defaults/index.ts b/inlong-dashboard/src/plugins/nodes/defaults/index.ts index 858ac347ebf..b25fd69cf9d 100644 --- a/inlong-dashboard/src/plugins/nodes/defaults/index.ts +++ b/inlong-dashboard/src/plugins/nodes/defaults/index.ts @@ -101,4 +101,9 @@ export const allDefaultNodes: MetaExportWithBackendList = [ value: 'HTTP', LoadEntity: () => import('./Http'), }, + { + label: 'COS', + value: 'COS', + LoadEntity: () => import('./COS'), + }, ]; diff --git a/inlong-dashboard/src/plugins/sources/defaults/COS.ts b/inlong-dashboard/src/plugins/sources/defaults/COS.ts new file mode 100644 index 00000000000..0927319d291 --- /dev/null +++ b/inlong-dashboard/src/plugins/sources/defaults/COS.ts @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { DataWithBackend } from '@/plugins/DataWithBackend'; +import { RenderRow } from '@/plugins/RenderRow'; +import { RenderList } from '@/plugins/RenderList'; +import i18n from '@/i18n'; +import rulesPattern from '@/core/utils/pattern'; +import { SourceInfo } from '../common/SourceInfo'; +import MultiSelectWithALL, { ALL_OPTION_VALUE } from '@/ui/components/MultiSelectWithAll'; + +const { I18n } = DataWithBackend; +const { FieldDecorator, IngestionField } = RenderRow; +const { ColumnDecorator } = RenderList; + +export default class COSSource + extends SourceInfo + implements DataWithBackend, RenderRow, RenderList +{ + @FieldDecorator({ + type: 'select', + rules: [{ required: true }], + props: values => ({ + disabled: Boolean(values.id), + showSearch: true, + allowClear: true, + filterOption: false, + options: { + requestTrigger: ['onOpen', 'onSearch'], + requestService: keyword => ({ + url: '/cluster/list', + method: 'POST', + data: { + keyword, + type: 'AGENT', + clusterTag: values.clusterTag, + pageNum: 1, + pageSize: 10, + }, + }), + requestParams: { + formatResult: result => + result?.list?.map(item => ({ + ...item, + label: item.displayName, + value: item.name, + })), + }, + }, + onChange: (value, option) => { + return { + clusterId: option.id, + }; + }, + }), + }) + @ColumnDecorator() + @IngestionField() + @I18n('meta.Sources.File.ClusterName') + inlongClusterName: string; + + @FieldDecorator({ + type: 'text', + hidden: true, + }) + @I18n('clusterId') + @IngestionField() + clusterId: number; + + @FieldDecorator({ + type: MultiSelectWithALL, + tooltip: i18n.t('meta.Sources.File.FileIpHelp'), + rules: [ + { + pattern: rulesPattern.fileIp, + message: i18n.t('meta.Sources.File.IpRule'), + required: true, + }, + ], + props: values => ({ + mode: 'multiple', + disabled: Boolean(values.id), + showSearch: true, + allowClear: true, + filterOption: false, + options: { + requestTrigger: ['onOpen', 'onSearch'], + requestService: keyword => ({ + url: '/cluster/node/list', + method: 'POST', + data: { + keyword, + parentId: values.clusterId, + pageNum: 1, + pageSize: 10, + }, + }), + requestParams: { + formatResult: result => { + const allOption = { + label: ALL_OPTION_VALUE, + value: ALL_OPTION_VALUE, + }; + return result?.list + ? [ + allOption, + ...result.list.map(item => ({ + label: item.ip, + value: item.ip, + })), + ] + : [allOption]; + }, + }, + }, + }), + }) + @ColumnDecorator() + @IngestionField() + @I18n('meta.Sources.File.DataSourceIP') + agentIp: string[] | string; + + @FieldDecorator({ + type: 'select', + rules: [{ required: true }], + props: values => ({ + showSearch: true, + allowClear: true, + filterOption: false, + disabled: [200, 201, 202, 204, 205, 300, 301, 302, 304, 305].includes(values?.status), + options: { + requestAuto: true, + requestTrigger: ['onOpen', 'onSearch'], + requestService: keyword => ({ + url: '/node/list', + method: 'POST', + data: { + keyword, + pageNum: 1, + pageSize: 20, + type: 'COS', + }, + }), + requestParams: { + formatResult: result => + result?.list?.map(item => ({ + ...item, + label: item.displayName, + value: item.name, + })), + }, + }, + }), + }) + @IngestionField() + @ColumnDecorator() + @I18n('meta.Sources.COS.DataNode') + dataNodeName: string; + + @FieldDecorator({ + type: 'input', + tooltip: i18n.t('meta.Sources.File.FilePathHelp'), + rules: [ + { required: true }, + { + pattern: /^\S*$/, + message: i18n.t('meta.Sources.File.FilePathPatternHelp'), + }, + ], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @ColumnDecorator() + @IngestionField() + @I18n('meta.Sources.File.FilePath') + pattern: string; + + @FieldDecorator({ + type: 'inputnumber', + rules: [{ required: true }], + initialValue: 20, + props: values => ({ + min: 1, + max: 100, + precision: 0, + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sources.File.MaxFileCount') + maxFileCount: number; + + @FieldDecorator({ + type: 'radio', + initialValue: 'H', + props: values => ({ + disabled: Boolean(values.id), + options: [ + { + label: i18n.t('meta.Sources.File.Cycle.Day'), + value: 'D', + }, + { + label: i18n.t('meta.Sources.File.Cycle.Hour'), + value: 'H', + }, + { + label: i18n.t('meta.Sources.File.Cycle.Minute'), + value: 'm', + }, + ], + }), + }) + @IngestionField() + @I18n('meta.Sources.File.Cycle') + cycleUnit: string; + + @FieldDecorator({ + type: 'input', + tooltip: i18n.t('meta.Sources.File.TimeOffsetHelp'), + initialValue: '0h', + rules: [ + { + pattern: /-?\d+[mhd]$/, + required: true, + message: i18n.t('meta.Sources.File.TimeOffsetRules'), + }, + ], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sources.File.TimeOffset') + timeOffset: string; + + @FieldDecorator({ + type: 'select', + initialValue: 'GMT+8:00', + props: values => ({ + disabled: Boolean(values.id), + options: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, + -12, + ].map(item => ({ + label: Math.sign(item) === 1 || Math.sign(item) === 0 ? `GMT+${item}:00` : `GMT${item}:00`, + value: Math.sign(item) === 1 || Math.sign(item) === 0 ? `GMT+${item}:00` : `GMT${item}:00`, + })), + }), + }) + @IngestionField() + @I18n('meta.Sources.File.TimeZone') + dataTimeZone: string; +} diff --git a/inlong-dashboard/src/plugins/sources/defaults/SQL.ts b/inlong-dashboard/src/plugins/sources/defaults/SQL.ts new file mode 100644 index 00000000000..e231fdd542f --- /dev/null +++ b/inlong-dashboard/src/plugins/sources/defaults/SQL.ts @@ -0,0 +1,259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { DataWithBackend } from '@/plugins/DataWithBackend'; +import { RenderRow } from '@/plugins/RenderRow'; +import { RenderList } from '@/plugins/RenderList'; +import i18n from '@/i18n'; +import rulesPattern from '@/core/utils/pattern'; +import { SourceInfo } from '../common/SourceInfo'; +import MultiSelectWithALL, { ALL_OPTION_VALUE } from '@/ui/components/MultiSelectWithAll'; + +const { I18n } = DataWithBackend; +const { FieldDecorator, IngestionField } = RenderRow; +const { ColumnDecorator } = RenderList; + +export default class SQLSource + extends SourceInfo + implements DataWithBackend, RenderRow, RenderList +{ + @FieldDecorator({ + type: 'select', + rules: [{ required: true }], + props: values => ({ + disabled: Boolean(values.id), + showSearch: true, + allowClear: true, + filterOption: false, + options: { + requestTrigger: ['onOpen', 'onSearch'], + requestService: keyword => ({ + url: '/cluster/list', + method: 'POST', + data: { + keyword, + type: 'AGENT', + clusterTag: values.clusterTag, + pageNum: 1, + pageSize: 10, + }, + }), + requestParams: { + formatResult: result => + result?.list?.map(item => ({ + ...item, + label: item.displayName, + value: item.name, + })), + }, + }, + onChange: (value, option) => { + return { + clusterId: option.id, + }; + }, + }), + }) + @ColumnDecorator() + @IngestionField() + @I18n('meta.Sources.File.ClusterName') + inlongClusterName: string; + + @FieldDecorator({ + type: 'text', + hidden: true, + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @I18n('clusterId') + @IngestionField() + clusterId: number; + + @FieldDecorator({ + type: 'select', + rules: [ + { + pattern: rulesPattern.ip, + message: i18n.t('meta.Sources.File.IpRule'), + required: true, + }, + ], + props: values => ({ + disabled: Boolean(values.id), + showSearch: true, + allowClear: true, + filterOption: false, + options: { + requestTrigger: ['onOpen', 'onSearch'], + requestService: keyword => ({ + url: '/cluster/node/list', + method: 'POST', + data: { + keyword, + parentId: values.clusterId, + pageNum: 1, + pageSize: 10, + }, + }), + requestParams: { + formatResult: result => + result?.list?.map(item => ({ + ...item, + label: item.ip, + value: item.ip, + })), + }, + }, + }), + }) + @ColumnDecorator() + @IngestionField() + @I18n('meta.Sources.File.DataSourceIP') + agentIp: string; + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @ColumnDecorator() + @I18n('meta.Sinks.SQL.Sql') + sql: string; + + @FieldDecorator({ + type: 'radio', + initialValue: 'H', + props: values => ({ + disabled: Boolean(values.id), + options: [ + { + label: i18n.t('meta.Sources.File.Cycle.Day'), + value: 'D', + }, + { + label: i18n.t('meta.Sources.File.Cycle.Hour'), + value: 'H', + }, + ], + }), + }) + @IngestionField() + @I18n('meta.Sources.File.Cycle') + cycleUnit: string; + + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sinks.SQL.JdbcUrl') + jdbcUrl: string; + + @FieldDecorator({ + rules: [{ required: true }], + type: 'input', + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sinks.SQL.Username') + username: string; + + @FieldDecorator({ + type: 'input', + rules: [{ required: true }], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sinks.SQL.Password') + jdbcPassword: string; + + @FieldDecorator({ + type: 'input', + tooltip: i18n.t('meta.Sources.File.TimeOffsetHelp'), + initialValue: '0h', + rules: [ + { + pattern: /-?\d+[mhd]$/, + required: true, + message: i18n.t('meta.Sources.File.TimeOffsetRules'), + }, + ], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sources.File.TimeOffset') + timeOffset: string; + + @FieldDecorator({ + type: 'inputnumber', + rules: [{ required: true }], + initialValue: 20, + props: values => ({ + min: 1, + max: 100, + precision: 0, + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sources.File.MaxFileCount') + maxInstanceCount: string; + + @FieldDecorator({ + type: 'inputnumber', + initialValue: 1000, + rules: [{ required: true }], + props: values => ({ + disabled: Boolean(values.id), + }), + }) + @IngestionField() + @I18n('meta.Sinks.SQL.FetchSize') + fetchSize: string; + + @FieldDecorator({ + type: 'select', + initialValue: 'GMT+8:00', + props: values => ({ + disabled: Boolean(values.id), + options: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, + -12, + ].map(item => ({ + label: Math.sign(item) === 1 || Math.sign(item) === 0 ? `GMT+${item}:00` : `GMT${item}:00`, + value: Math.sign(item) === 1 || Math.sign(item) === 0 ? `GMT+${item}:00` : `GMT${item}:00`, + })), + }), + }) + @IngestionField() + @I18n('meta.Sources.File.TimeZone') + dataTimeZone: string; +} diff --git a/inlong-dashboard/src/plugins/sources/defaults/index.ts b/inlong-dashboard/src/plugins/sources/defaults/index.ts index 416e7c310f5..2c55bd8ee3c 100644 --- a/inlong-dashboard/src/plugins/sources/defaults/index.ts +++ b/inlong-dashboard/src/plugins/sources/defaults/index.ts @@ -95,4 +95,14 @@ export const allDefaultSources: MetaExportWithBackendList = [ value: 'ICEBERG', LoadEntity: () => import('./Iceberg'), }, + { + label: 'COS', + value: 'COS', + LoadEntity: () => import('./COS'), + }, + { + label: 'SQL', + value: 'SQL', + LoadEntity: () => import('./SQL'), + }, ]; diff --git a/inlong-dashboard/src/plugins/streams/common/StreamDefaultInfo.ts b/inlong-dashboard/src/plugins/streams/common/StreamDefaultInfo.ts index b62ac7d4b34..e9a1e944271 100644 --- a/inlong-dashboard/src/plugins/streams/common/StreamDefaultInfo.ts +++ b/inlong-dashboard/src/plugins/streams/common/StreamDefaultInfo.ts @@ -81,12 +81,6 @@ export class StreamDefaultInfo implements DataWithBackend, RenderRow, RenderList @I18n('basic.Modifier') readonly modifier: string; - @ColumnDecorator({ - render: text => timestampFormat(text), - }) - @I18n('basic.CreateTime') - readonly createTime: string; - @FieldDecorator({ type: 'select', props: { diff --git a/inlong-dashboard/src/ui/locales/cn.json b/inlong-dashboard/src/ui/locales/cn.json index 4f631bb1518..c49db0d276c 100644 --- a/inlong-dashboard/src/ui/locales/cn.json +++ b/inlong-dashboard/src/ui/locales/cn.json @@ -375,6 +375,12 @@ "meta.Sinks.Cls.Tag": "标签", "meta.Sinks.Cls.Tokenizer": "分词规则", "meta.Sinks.Cls.IsMetaField": "是否为元字段", + "meta.Sources.COS.DataNode": "数据节点", + "meta.Sinks.SQL.Sql": "Sql", + "meta.Sinks.SQL.JdbcUrl": "JDBC 地址", + "meta.Sinks.SQL.Username": "用户名", + "meta.Sinks.SQL.Password": "密码", + "meta.Sinks.SQL.FetchSize": "捕获大小", "meta.Group.InlongGroupId": "数据流组 ID", "meta.Group.InlongGroupIdRules": "只能包含小写字母、数字、点号(.)、中划线(-)、下划线(_)", "meta.Group.InlongGroupName": "数据流组名称", @@ -582,6 +588,10 @@ "meta.Nodes.Doris.Username": "用户名", "meta.Nodes.Doris.Password": "密码", "meta.Nodes.Doris.Url": "地址", + "meta.Nodes.COS.BucketName": "存储桶名称", + "meta.Nodes.COS.CredentialsId": "凭据 ID", + "meta.Nodes.COS.CredentialsKey": "凭据密钥", + "meta.Nodes.COS.Region": "地区", "components.EditableTable.NewLine": "新增一行", "components.EditableTable.BatchParseField": "批量解析字段", "components.EditableTable.DeleteAll": "删除全部", @@ -700,6 +710,7 @@ "pages.GroupDetail.Resource": "资源详情", "pages.GroupDetail.Delay": "传输时延", "pages.GroupDetail.OperationLog": "操作日志", + "pages.GroupDetail.OperationLogDetail": "操作日志详情", "pages.GroupDetail.Resource.Info": " 信息", "pages.GroupDetail.Audit.DataStream": "数据流", "pages.GroupDetail.Audit.StartDate": "开始日期", diff --git a/inlong-dashboard/src/ui/locales/en.json b/inlong-dashboard/src/ui/locales/en.json index b7e60d316bd..a75613ce68e 100644 --- a/inlong-dashboard/src/ui/locales/en.json +++ b/inlong-dashboard/src/ui/locales/en.json @@ -375,7 +375,12 @@ "meta.Sinks.Cls.Tag": "Tag", "meta.Sinks.Cls.Tokenizer": "Tokenizer rule", "meta.Sinks.Cls.IsMetaField": "Is meta field", - + "meta.Sources.COS.DataNode": "Data Node", + "meta.Sinks.SQL.Sql": "Sql", + "meta.Sinks.SQL.JdbcUrl": "JDBC Url", + "meta.Sinks.SQL.Username": "Username", + "meta.Sinks.SQL.Password": "Password", + "meta.Sinks.SQL.FetchSize": "Fetch Size", "meta.Group.InlongGroupId": "Inlong group id", "meta.Group.InlongGroupIdRules": "Only English letters, numbers, dots(.), minus(-), and underscores(_)", "meta.Group.InlongGroupName": "Inlong group name", @@ -583,6 +588,10 @@ "meta.Nodes.Doris.Username": "Username", "meta.Nodes.Doris.Password": "Password", "meta.Nodes.Doris.Url": "URL", + "meta.Nodes.COS.BucketName": "Bucket Name", + "meta.Nodes.COS.CredentialsId": "Credentials ID", + "meta.Nodes.COS.CredentialsKey": "Credentials Key", + "meta.Nodes.COS.Region": "Region", "components.EditableTable.NewLine": "New line", "components.EditableTable.BatchParseField": "Batch add", "components.EditableTable.DeleteAll": "Delete all", @@ -700,6 +709,7 @@ "pages.GroupDetail.Audit": "Audit", "pages.GroupDetail.Delay": "Transmission delay", "pages.GroupDetail.OperationLog": "Operation log", + "pages.GroupDetail.OperationLogDetail": "Operation Log Detail", "pages.GroupDetail.Resource": "Resource detail", "pages.GroupDetail.Resource.Info": " Info", "pages.GroupDetail.Audit.DataStream": "Data stream", diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/DataSources/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/DataSources/index.tsx index 6af108b3c17..72792710afa 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/DataSources/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/DataSources/index.tsx @@ -35,7 +35,7 @@ import { useDefaultMeta, useLoadMeta, SourceMetaType } from '@/plugins'; import DetailModal from './DetailModal'; import i18n from '@/i18n'; import request from '@/core/utils/request'; -import { pickObjectArray } from '@/core/utils'; +import { pickObjectArray, timestampFormat } from '@/core/utils'; import { CommonInterface } from '../common'; import { sources } from '@/plugins/sources'; @@ -205,37 +205,63 @@ const Comp = ({ inlongGroupId, inlongStreamId, readonly }: Props, ref) => { ); const columns = useMemo(() => { - return entityColumns?.concat([ - { - title: i18n.t('basic.Operating'), - dataIndex: 'action', - fixed: 'right', - width: 200, - render: (text, record) => - readonly ? ( - '-' - ) : ( - <> - - - {record?.status === 101 && ( - - )} - {(record?.status === 101 || record?.status === 104) && ( - - )} - - ), - }, - ]); + {record?.status === 101 && ( + + )} + {(record?.status === 101 || record?.status === 104) && ( + + )} + + ), + }, + ]); }, [entityColumns, onDelete, onEdit, readonly]); const scroll = { x: 850 }; return ( diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx index 5f1086d91e6..bd76b3c06b3 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx @@ -34,7 +34,7 @@ import i18n from '@/i18n'; import DetailModal from './DetailModal'; import { useDefaultMeta, useLoadMeta, SinkMetaType } from '@/plugins'; import request from '@/core/utils/request'; -import { pickObjectArray } from '@/core/utils'; +import { pickObjectArray, timestampFormat } from '@/core/utils'; import { CommonInterface } from '../common'; import { sinks } from '@/plugins/sinks'; import DirtyModal from '@/ui/pages/common/DirtyModal'; @@ -165,28 +165,54 @@ const Comp = ({ inlongGroupId, inlongStreamId, readonly }: Props, ref) => { ); const columns = useMemo(() => { - return entityColumns?.concat([ - { - title: i18n.t('basic.Operating'), - dataIndex: 'action', - render: (text, record) => - readonly ? ( - '-' - ) : ( - <> - - - - - ), - } as any, - ]); + return entityColumns + ?.map(item => { + if (item.dataIndex === 'creator') { + return { + ...item, + render: (text, record) => ( + <> +
{text}
+
{record.createTime && timestampFormat(record.createTime)}
+ + ), + }; + } + if (item.dataIndex === 'modifier') { + return { + ...item, + render: (text, record) => ( + <> +
{text}
+ {text ?
{record.modifyTime && timestampFormat(record.modifyTime)}
: ''} + + ), + }; + } + return item; + }) + .concat([ + { + title: i18n.t('basic.Operating'), + dataIndex: 'action', + render: (text, record) => + readonly ? ( + '-' + ) : ( + <> + + + + + ), + } as any, + ]); }, [entityColumns, onDelete, onEdit, readonly]); return ( diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/DataStream/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/DataStream/index.tsx index 878f6fdfd67..978fc30ce12 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/DataStream/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/DataStream/index.tsx @@ -32,6 +32,7 @@ import StreamItemModal from './StreamItemModal'; import SourceSinkCard from './SourceSinkCard'; import { getFilterFormContent } from './config'; import PreviewModal from './PreviewModal'; +import { timestampFormat } from '@/core/utils'; type Props = CommonInterface; @@ -190,38 +191,64 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => { return Entity ? new Entity().renderList() : []; }, [Entity]); - const columns = entityColumns?.concat([ - { - title: t('basic.Operating'), - dataIndex: 'action', - render: (text, record) => - readonly ? ( - '-' - ) : ( -
e.stopPropagation()}> - - - {record?.status && (record?.status === 120 || record?.status === 130) && ( - - )} - {record?.status && (groupStatus === 120 || groupStatus === 130) && ( - - )} - -
- ), - }, - ]); + {record?.status && (record?.status === 120 || record?.status === 130) && ( + + )} + {record?.status && (groupStatus === 120 || groupStatus === 130) && ( + + )} + + + ), + }, + ]); return ( <> diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/config.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/config.tsx index 992f242a95b..71a38ec1113 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/config.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/config.tsx @@ -18,7 +18,7 @@ */ import i18n from '@/i18n'; -import { Tooltip } from 'antd'; +import { Button } from 'antd'; import React from 'react'; import { dateFormat } from '@/core/utils'; @@ -124,7 +124,7 @@ export const getFormContent = inlongGroupId => [ }, ]; -export const getTableColumns = [ +export const getTableColumns = setOperationLogInfoModal => [ { title: i18n.t('pages.GroupDetail.OperationLog.Table.GroupId'), dataIndex: 'inlongGroupId', @@ -151,11 +151,18 @@ export const getTableColumns = [ title: i18n.t('pages.GroupDetail.OperationLog.Table.Log'), dataIndex: 'body', ellipsis: true, - render: body => ( - - {body} - - ), + render: body => + body && ( + + ), }, { title: i18n.t('pages.GroupDetail.OperationLog.Table.OperationTime'), diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/index.tsx index d00138ceb38..a8bb0d96d76 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/OperationLog/index.tsx @@ -23,6 +23,8 @@ import { useRequest } from '@/ui/hooks'; import { CommonInterface } from '../common'; import { getFormContent, getTableColumns } from './config'; import { defaultSize } from '@/configs/pagination'; +import { Button, Modal } from 'antd'; +import i18n from '@/i18n'; type Props = CommonInterface; @@ -32,6 +34,10 @@ const Comp: React.FC = ({ inlongGroupId }) => { pageNum: 1, }); + const [operationLogInfoModal, setOperationLogInfoModal] = useState>({ + open: false, + }); + const { data: sourceData, run } = useRequest( { url: '/operationLog/list', @@ -71,6 +77,17 @@ const Comp: React.FC = ({ inlongGroupId }) => { run(); }, []); + const onOk = () => { + setOperationLogInfoModal({ open: false }); + navigator.clipboard.writeText( + JSON.stringify( + operationLogInfoModal.data && JSON.parse(operationLogInfoModal.data?.toString()), + null, + 4, + ), + ); + }; + return ( <> = ({ inlongGroupId }) => { onFilter, }} table={{ - columns: getTableColumns, + columns: getTableColumns(setOperationLogInfoModal), dataSource: sourceData?.list, pagination: pagination, rowKey: 'inlongGroupId', onChange, }} /> + setOperationLogInfoModal({ open: false })} + footer={[ + , + ]} + > +
+
+
+              
+                {JSON.stringify(
+                  operationLogInfoModal.data && JSON.parse(operationLogInfoModal.data?.toString()),
+                  null,
+                  4,
+                )}
+              
+            
+
+
+
); };