From f23e935d90ac5af8c9f7340534490106352e1f68 Mon Sep 17 00:00:00 2001 From: wohainilaodou Date: Tue, 19 Nov 2024 18:46:17 +0800 Subject: [PATCH] [INLONG-11510][Dashboard] Add Page to dirty data query --- inlong-dashboard/src/ui/locales/cn.json | 32 +- inlong-dashboard/src/ui/locales/en.json | 33 +- .../pages/GroupDetail/DataStorage/index.tsx | 24 +- .../SynchronizeDetail/SyncSink/index.tsx | 24 +- .../src/ui/pages/common/DirtyModal/conf.tsx | 47 ++ .../src/ui/pages/common/DirtyModal/index.tsx | 591 ++++++++++++++++++ 6 files changed, 747 insertions(+), 4 deletions(-) create mode 100644 inlong-dashboard/src/ui/pages/common/DirtyModal/conf.tsx create mode 100644 inlong-dashboard/src/ui/pages/common/DirtyModal/index.tsx diff --git a/inlong-dashboard/src/ui/locales/cn.json b/inlong-dashboard/src/ui/locales/cn.json index 535063ee005..8bf79e6c3e7 100644 --- a/inlong-dashboard/src/ui/locales/cn.json +++ b/inlong-dashboard/src/ui/locales/cn.json @@ -1,5 +1,6 @@ { "basic.Edit": "编辑", + "basic.Search": "搜索", "basic.Detail": "详情", "basic.Operating": "操作", "basic.OperatingSuccess": "操作成功", @@ -1020,5 +1021,34 @@ "pages.GroupDataTemplate.VisibleRange.InCharges":"责任人", "pages.GroupDataTemplate.VisibleRange.Tenant":"租户", "miscellaneous.total": "... 共", - "miscellaneous.tenants": "个租户" + "miscellaneous.tenants": "个租户", + "meta.Sinks.DirtyData.TdbankImpDate": "脏数据分区", + "meta.Sinks.DirtyData.DataFlowId": "DataFlowId", + "meta.Sinks.DirtyData.GroupId": "GroupId", + "meta.Sinks.DirtyData.StreamId": "StreamId", + "meta.Sinks.DirtyData.ReportTime": "上报时间", + "meta.Sinks.DirtyData.DataTime": "Data Time", + "meta.Sinks.DirtyData.ServerType": "服务类型", + "meta.Sinks.DirtyData.DirtyType": "脏数据类型", + "meta.Sinks.DirtyData.DirtyMessage": "脏消息", + "meta.Sinks.DirtyData.ExtInfo": "额外信息", + "meta.Sinks.DirtyData.DirtyData": "脏数据", + "meta.Sinks.DirtyData.DirtyDetailWarning": "脏数据任务正在运行,请稍后再试", + "meta.Sinks.DirtyData.DirtyTrendWarning": "脏数据趋势任务正在运行,请稍后再试", + "meta.Sinks.DirtyData.DataCount": "脏数据个数", + "meta.Sinks.DirtyData.Search.DirtyType": "脏数据类型", + "meta.Sinks.DirtyData.Search.ServerType": "服务类型", + "meta.Sinks.DirtyData.StartTimeError": "开始时间不能大于当前时间", + "meta.Sinks.DirtyData.endTimeNotGreaterThanStartTime": "结束时间不能大于当前时间", + "meta.Sinks.DirtyData.TimeIntervalError": "时间间隔能超过七天", + "meta.Sinks.DirtyTrend.DataTimeUnit":"时间单位", + "meta.Sinks.DirtyTrend.Day":"天", + "meta.Sinks.DirtyTrend.Hour":"小时", + "meta.Sinks.DirtyData.Detail":"详情", + "meta.Sinks.DirtyData.Trend":"趋势", + "meta.Sinks.DirtyData":"脏数据查询", + "meta.Sinks.DirtyData.DirtyType.DeserializeError":"反序列化错误", + "meta.Sinks.DirtyData.DirtyType.FieldMappingError":"字段映射错误", + "meta.Sinks.DirtyData.DirtyType.LoadError":"加载错误", + "meta.Sinks.DirtyData.Search.KeyWord":"请输入关键字" } diff --git a/inlong-dashboard/src/ui/locales/en.json b/inlong-dashboard/src/ui/locales/en.json index b74f54e58e1..426a2f75979 100644 --- a/inlong-dashboard/src/ui/locales/en.json +++ b/inlong-dashboard/src/ui/locales/en.json @@ -1,5 +1,6 @@ { "basic.Edit": "Edit", + "basic.Search": "Search", "basic.Detail": "Detail", "basic.Operating": "Operation", "basic.OperatingSuccess": "Operating success", @@ -374,6 +375,7 @@ "meta.Sinks.Cls.Tag": "Tag", "meta.Sinks.Cls.Tokenizer": "Tokenizer rule", "meta.Sinks.Cls.IsMetaField": "Is meta field", + "meta.Group.InlongGroupId": "Inlong group id", "meta.Group.InlongGroupIdRules": "Only English letters, numbers, dots(.), minus(-), and underscores(_)", "meta.Group.InlongGroupName": "Inlong group name", @@ -1020,5 +1022,34 @@ "pages.GroupDataTemplate.VisibleRange.InCharges":"Owner", "pages.GroupDataTemplate.VisibleRange.Tenant":"Tenant", "miscellaneous.total": "... total ", - "miscellaneous.tenants": " tenants" + "miscellaneous.tenants": " tenants", + "meta.Sinks.DirtyData.TdbankImpDate": "TdbankImp Date", + "meta.Sinks.DirtyData.DataFlowId": "DataFlowId", + "meta.Sinks.DirtyData.GroupId": "GroupId", + "meta.Sinks.DirtyData.StreamId": "StreamId", + "meta.Sinks.DirtyData.ReportTime": "Report Time", + "meta.Sinks.DirtyData.DataTime": "Data Time", + "meta.Sinks.DirtyData.ServerType": "Server Type", + "meta.Sinks.DirtyData.DirtyType": "Dirty Type", + "meta.Sinks.DirtyData.DirtyMessage": "Dirty Message", + "meta.Sinks.DirtyData.ExtInfo": "Ext Info", + "meta.Sinks.DirtyData.DirtyData": "DirtyData", + "meta.Sinks.DirtyData.DirtyDetailWarning": "The dirty data task is running, please try again later", + "meta.Sinks.DirtyData.DirtyTrendWarning": "The dirty data trending task is running, try again later", + "meta.Sinks.DirtyData.DataCount": "Data Count", + "meta.Sinks.DirtyData.Search.DirtyType": "Dirty Type", + "meta.Sinks.DirtyData.Search.ServerType": "Server Type", + "meta.Sinks.DirtyData.StartTimeError": "The start time cannot be greater than the current time", + "meta.Sinks.DirtyData.endTimeNotGreaterThanStartTime": "The end time cannot be greater than the current time", + "meta.Sinks.DirtyData.TimeIntervalError": "The time interval can be more than seven days", + "meta.Sinks.DirtyTrend.DataTimeUnit":"DataTime Unit", + "meta.Sinks.DirtyTrend.Day":"Day", + "meta.Sinks.DirtyTrend.Hour":"Hour", + "meta.Sinks.DirtyData.Detail":"Detail", + "meta.Sinks.DirtyData.Trend":"Trend", + "meta.Sinks.DirtyData":"Dirty Data Search", + "meta.Sinks.DirtyData.DirtyType.DeserializeError":"DeserializeError", + "meta.Sinks.DirtyData.DirtyType.FieldMappingError":"FieldMappingError", + "meta.Sinks.DirtyData.DirtyType.LoadError":"LoadError", + "meta.Sinks.DirtyData.Search.KeyWord":"Please enter a keyword" } diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx index b91cac850d2..5f1086d91e6 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/DataStorage/index.tsx @@ -25,6 +25,7 @@ import { TableOutlined, EditOutlined, DeleteOutlined, + AreaChartOutlined, } from '@ant-design/icons'; import HighTable from '@/ui/components/HighTable'; import { defaultSize } from '@/configs/pagination'; @@ -36,6 +37,7 @@ import request from '@/core/utils/request'; import { pickObjectArray } from '@/core/utils'; import { CommonInterface } from '../common'; import { sinks } from '@/plugins/sinks'; +import DirtyModal from '@/ui/pages/common/DirtyModal'; interface Props extends CommonInterface { inlongStreamId?: string; @@ -58,7 +60,12 @@ const Comp = ({ inlongGroupId, inlongStreamId, readonly }: Props, ref) => { const [createModal, setCreateModal] = useState>({ open: false, }); - + const [dirtyModal, setDirtyModal] = useState>({ + open: false, + }); + const onOpenDirtyModal = useCallback(({ id }) => { + setDirtyModal({ open: true, id }); + }, []); const { data, loading, @@ -173,6 +180,9 @@ const Comp = ({ inlongGroupId, inlongStreamId, readonly }: Props, ref) => { + ), } as any, @@ -238,6 +248,9 @@ const Comp = ({ inlongGroupId, inlongStreamId, readonly }: Props, ref) => { , + , ]} > @@ -277,6 +290,15 @@ const Comp = ({ inlongGroupId, inlongStreamId, readonly }: Props, ref) => { }} onCancel={() => setCreateModal({ open: false })} /> + { + await getList(); + setDirtyModal({ open: false }); + }} + onCancel={() => setDirtyModal({ open: false })} + /> ); }; diff --git a/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncSink/index.tsx b/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncSink/index.tsx index 9e7c243630f..2d5f2a761ae 100644 --- a/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncSink/index.tsx +++ b/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncSink/index.tsx @@ -25,6 +25,7 @@ import { TableOutlined, EditOutlined, DeleteOutlined, + AreaChartOutlined, } from '@ant-design/icons'; import HighTable from '@/ui/components/HighTable'; import { defaultSize } from '@/configs/pagination'; @@ -36,6 +37,7 @@ import request from '@/core/utils/request'; import { pickObjectArray } from '@/core/utils'; import { sinks } from '@/plugins/sinks'; import { CommonInterface } from '../common'; +import DirtyModal from '@/ui/pages/common/DirtyModal'; interface Props extends CommonInterface { inlongStreamId: string; @@ -58,7 +60,12 @@ const Comp = ({ inlongGroupId, inlongStreamId, sinkMultipleEnable, readonly }: P const [createModal, setCreateModal] = useState>({ open: false, }); - + const [dirtyModal, setDirtyModal] = useState>({ + open: false, + }); + const onOpenDirtyModal = useCallback(({ id }) => { + setDirtyModal({ open: true, id }); + }, []); const { data, loading, @@ -179,6 +186,9 @@ const Comp = ({ inlongGroupId, inlongStreamId, sinkMultipleEnable, readonly }: P + ), } as any, @@ -249,6 +259,9 @@ const Comp = ({ inlongGroupId, inlongStreamId, sinkMultipleEnable, readonly }: P , + , ]} > @@ -289,6 +302,15 @@ const Comp = ({ inlongGroupId, inlongStreamId, sinkMultipleEnable, readonly }: P }} onCancel={() => setCreateModal({ open: false })} /> + { + await getList(); + setDirtyModal({ open: false }); + }} + onCancel={() => setDirtyModal({ open: false })} + /> ); }; diff --git a/inlong-dashboard/src/ui/pages/common/DirtyModal/conf.tsx b/inlong-dashboard/src/ui/pages/common/DirtyModal/conf.tsx new file mode 100644 index 00000000000..ce18eceb8a7 --- /dev/null +++ b/inlong-dashboard/src/ui/pages/common/DirtyModal/conf.tsx @@ -0,0 +1,47 @@ +/* + * 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 i18n from '@/i18n'; + +export const statusList = [ + { + label: i18n.t('meta.Sinks.DirtyData.DirtyType.DeserializeError'), + value: 'DeserializeError', + }, + { + label: i18n.t('meta.Sinks.DirtyData.DirtyType.FieldMappingError'), + value: 'FieldMappingError', + }, + { + label: i18n.t('meta.Sinks.DirtyData.DirtyType.LoadError'), + value: 'LoadError', + }, +]; + +export const statusMap = statusList.reduce( + (acc, cur) => ({ + ...acc, + [cur.value]: cur, + }), + {}, +); + +export const genStatusTag = value => { + return statusMap[value]; +}; diff --git a/inlong-dashboard/src/ui/pages/common/DirtyModal/index.tsx b/inlong-dashboard/src/ui/pages/common/DirtyModal/index.tsx new file mode 100644 index 00000000000..c364d8b585e --- /dev/null +++ b/inlong-dashboard/src/ui/pages/common/DirtyModal/index.tsx @@ -0,0 +1,591 @@ +/* + * 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 React, { useEffect, useState } from 'react'; +import { Button, message, Modal, Tabs, TabsProps } from 'antd'; +import { ModalProps } from 'antd/es/modal'; +import i18n from '@/i18n'; +import HighTable from '@/ui/components/HighTable'; + +import dayjs from 'dayjs'; +import request from '@/core/utils/request'; +import { useForm } from 'antd/es/form/Form'; +import FormGenerator from '@/ui/components/FormGenerator'; +import Charts from '@/ui/components/Charts'; +import { genStatusTag } from '@/ui/pages/common/DirtyModal/conf'; + +export interface Props extends ModalProps { + id?: number; +} +const Comp: React.FC = ({ ...modalProps }) => { + const [form1] = useForm(); + const [form2] = useForm(); + const [loading, setLoading] = useState(false); + const getColumns = [ + { + title: i18n.t('meta.Sinks.DirtyData.TdbankImpDate'), + dataIndex: 'tdbankImpDate', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.DataFlowId'), + dataIndex: 'dataFlowId', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.GroupId'), + dataIndex: 'groupId', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.StreamId'), + dataIndex: 'streamId', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.ReportTime'), + dataIndex: 'reportTime', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.DataTime'), + dataIndex: 'dataTime', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.ServerType'), + dataIndex: 'serverType', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.DirtyType'), + dataIndex: 'dirtyType', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.DirtyMessage'), + dataIndex: 'dirtyMessage', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.ExtInfo'), + dataIndex: 'extInfo', + width: 90, + }, + { + title: i18n.t('meta.Sinks.DirtyData.DirtyData'), + dataIndex: 'dirtyData', + width: 90, + }, + ]; + + const defaultDetailOptions = { + keyword: '', + dataCount: '10', + dirtyType: '', + serverType: '', + startDate: dayjs().format('YYYYMMDD'), + endDate: dayjs().format('YYYYMMDD'), + }; + const defaultTrendOptions = { + dataTimeUnit: '', + dirtyType: '', + serverType: 'D', + startTime: dayjs().format('YYYYMMDD'), + endTime: dayjs().format('YYYYMMDD'), + }; + const [options, setOptions] = useState(defaultDetailOptions); + const [trendOptions, setTrendOption] = useState(defaultTrendOptions); + const [data, setData] = useState([]); + const [trendData, setTrendData] = useState([]); + const [tabValue, setTabValue] = useState('detail'); + useEffect(() => { + if (modalProps.open) { + if (tabValue === 'detail') { + setOptions(defaultDetailOptions); + form1.resetFields(); + getTaskResult().then(item => { + setData(item); + }); + } + if (tabValue === 'trend') { + setOptions(defaultDetailOptions); + form2.resetFields(); + form2.setFieldsValue({ + dataTimeUnit: 'D', + }); + getTrendData().then(item => { + setTrendData(item); + }); + } + } + }, [modalProps.open, tabValue]); + const [messageApi, contextHolder] = message.useMessage(); + const warning = () => { + messageApi.open({ + type: 'warning', + content: + tabValue === 'detail' + ? i18n.t('meta.Sinks.DirtyData.DirtyDetailWarning') + : i18n.t('meta.Sinks.DirtyData.DirtyTrendWarning'), + }); + }; + const getTaskResult = async () => { + setLoading(true); + const taskId = await getTaskId(); + const status = await request({ + url: '/sink/SqlTaskStatus/' + taskId, + method: 'GET', + }); + if (status === 'success') { + const data = await request({ + url: '/sink/getDirtyData/' + taskId, + method: 'GET', + }); + setLoading(false); + return data; + } else { + setLoading(false); + warning(); + } + return []; + }; + const getTaskId = async () => { + const data = await request({ + url: '/sink/listDirtyData', + method: 'POST', + data: { + ...options, + startDate: options.startDate ? dayjs(options.startDate).format('YYYYMMDD') : '', + endDate: options.endDate ? dayjs(options.endDate).format('YYYYMMDD') : '', + sinkIdList: [modalProps.id], + }, + }); + return data.taskId; + }; + + const getTrendData = async () => { + const taskId = await getTrendTaskId(); + const status = await request({ + url: '/sink/SqlTaskStatus/' + taskId, + method: 'GET', + }); + + if (status === 'success') { + const data = await request({ + url: '/sink/getDirtyDataTrend/' + taskId, + method: 'GET', + }); + return data; + } else { + warning(); + } + return []; + }; + + const getTrendTaskId = async () => { + const data = await request({ + url: '/sink/listDirtyDataTrend', + method: 'POST', + data: { + ...trendOptions, + sinkIdList: [modalProps.id], + }, + }); + return data.taskId; + }; + const onSearch = async () => { + await form1.validateFields(); + await getTaskResult().then(item => { + setData(item); + }); + }; + const onTrendSearch = async () => { + await form2.validateFields(); + await getTrendData().then(item => { + setTrendData(item); + }); + }; + + const getDetailFilterFormContent = defaultValues => [ + { + type: 'inputsearch', + name: 'keyword', + initialValue: defaultValues.keyword, + props: { + placeholder: i18n.t('meta.Sinks.DirtyData.Search.KeyWord'), + }, + }, + { + label: i18n.t('meta.Sinks.DirtyData.DataCount'), + type: 'input', + name: 'dataCount', + }, + { + label: i18n.t('meta.Sinks.DirtyData.Search.DirtyType'), + type: 'select', + name: 'dirtyType', + props: { + options: [ + { + label: i18n.t('meta.Sinks.DirtyData.DirtyType.DeserializeError'), + value: 'DeserializeError', + }, + { + label: i18n.t('meta.Sinks.DirtyData.DirtyType.FieldMappingError'), + value: 'FieldMappingError', + }, + { + label: i18n.t('meta.Sinks.DirtyData.DirtyType.LoadError'), + value: 'LoadError', + }, + ], + }, + }, + { + label: i18n.t('meta.Sinks.DirtyData.Search.ServerType'), + type: 'select', + name: 'serverType', + props: { + options: [ + { + label: 'Undefined', + value: 'Undefined', + }, + { + label: 'TubeMQ', + value: 'TubeMQ', + }, + { + label: 'Iceberg', + value: 'Iceberg', + }, + ], + }, + }, + { + type: 'datepicker', + label: i18n.t('pages.GroupDetail.Audit.StartDate'), + name: 'startDate', + initialValue: dayjs(options.startDate), + props: { + allowClear: true, + format: 'YYYYMMDD', + }, + rules: [ + { required: true }, + ({ getFieldValue }) => ({ + validator(_, value) { + if (Boolean(value)) { + if (value.isAfter(dayjs())) { + return Promise.reject(new Error(i18n.t('meta.Sinks.DirtyData.StartTimeError'))); + } + } + return Promise.resolve(); + }, + }), + ], + }, + { + type: 'datepicker', + label: i18n.t('pages.GroupDetail.Audit.EndDate'), + name: 'endDate', + initialValue: dayjs(options.endDate), + props: values => { + return { + allowClear: true, + format: 'YYYYMMDD', + }; + }, + rules: [ + { required: true }, + ({ getFieldValue }) => ({ + validator(_, value) { + if (Boolean(value)) { + if (value.isAfter(dayjs())) { + return Promise.reject(new Error(i18n.t('endTimeNotGreaterThanStartTime'))); + } + const timeDiff = value.diff(getFieldValue('startDate'), 'day'); + if (timeDiff <= 7) { + return Promise.resolve(); + } + return Promise.reject(new Error(i18n.t('meta.Sinks.DirtyData.TimeIntervalError'))); + } + return Promise.resolve(); + }, + }), + ], + }, + { + type: ( + + ), + }, + ]; + const getTendFilterFormContent = defaultValues => [ + { + label: i18n.t('meta.Sinks.DirtyData.Search.DirtyType'), + type: 'select', + name: 'dirtyType', + props: { + options: [ + { + label: 'DeserializeError', + value: 'DeserializeError', + }, + { + label: 'FieldMappingError', + value: 'FieldMappingError', + }, + { + label: 'LoadError', + value: 'LoadError', + }, + ], + }, + }, + { + label: i18n.t('meta.Sinks.DirtyData.Search.ServerType'), + type: 'select', + name: 'serverType', + props: { + options: [ + { + label: 'Undefined', + value: 'Undefined', + }, + { + label: 'TubeMQ', + value: 'TubeMQ', + }, + { + label: 'Iceberg', + value: 'Iceberg', + }, + ], + }, + }, + { + label: i18n.t('meta.Sinks.DirtyTrend.DataTimeUnit'), + type: 'select', + name: 'dataTimeUnit', + initialValue: 'D', + props: { + options: [ + { + label: i18n.t('meta.Sinks.DirtyTrend.Day'), + value: 'D', + }, + { + label: i18n.t('meta.Sinks.DirtyTrend.Hour'), + value: 'H', + }, + ], + }, + }, + + { + type: 'datepicker', + label: i18n.t('pages.GroupDetail.Audit.StartDate'), + name: 'startTime', + props: values => { + return { + allowClear: true, + showTime: values.dataTimeUnit === 'H', + format: values.dataTimeUnit === 'D' ? 'YYYYMMDD' : 'YYYYMMDDHH', + }; + }, + initialValue: dayjs(trendOptions.startTime), + rules: [ + { required: true }, + ({ getFieldValue }) => ({ + validator(_, value) { + if (Boolean(value)) { + if (value.isAfter(dayjs())) { + return Promise.reject(new Error(i18n.t('meta.Sinks.DirtyData.StartTimeError'))); + } + } + return Promise.resolve(); + }, + }), + ], + }, + { + type: 'datepicker', + label: i18n.t('pages.GroupDetail.Audit.EndDate'), + name: 'endTime', + initialValue: dayjs(trendOptions.endTime), + props: values => { + return { + allowClear: true, + showTime: values.dataTimeUnit === 'H', + format: values.dataTimeUnit === 'D' ? 'YYYYMMDD' : 'YYYYMMDDHH', + }; + }, + rules: [ + { required: true }, + ({ getFieldValue }) => ({ + validator(_, value) { + if (Boolean(value)) { + if (value.isAfter(dayjs())) { + return Promise.reject( + new Error(i18n.t('meta.Sinks.DirtyData.endTimeNotGreaterThanStartTime')), + ); + } + const timeDiff = value.diff(getFieldValue('startTime'), 'day'); + if (timeDiff <= 7) { + return Promise.resolve(); + } + return Promise.reject(new Error(i18n.t('meta.Sinks.DirtyData.TimeIntervalError'))); + } + return Promise.resolve(); + }, + }), + ], + }, + { + type: ( + + ), + }, + ]; + + const onFilter = allValues => { + setOptions(prev => ({ + ...prev, + ...allValues, + startDate: allValues.startDate ? +allValues.startDate.$d : '', + endDate: allValues.startDate ? +allValues.startDate.$d : '', + })); + }; + const onTrendFilter = allValues => { + setTrendOption(prev => ({ + ...prev, + ...allValues, + startTime: allValues.startTime + ? allValues.dataTimeUnit === 'H' + ? dayjs(allValues.startTime.$d).format('YYYYMMDDHH') + : dayjs(allValues.startTime.$d).format('YYYYMMDD') + : '', + endTime: allValues.endTime + ? allValues.dataTimeUnit === 'H' + ? dayjs(allValues.endTime.$d).format('YYYYMMDDHH') + : dayjs(allValues.endTime.$d).format('YYYYMMDD') + : '', + })); + }; + const scroll = { x: 2000 }; + const toChartData = trendData => { + return { + legend: { + data: trendData.map(item => item.reportTime), + }, + tooltip: { + trigger: 'axis', + }, + xAxis: { + type: 'category', + data: trendData.map(item => item.reportTime), + }, + yAxis: { + type: 'value', + }, + series: trendData.map(item => ({ + name: item.reportTime, + type: 'line', + data: trendData.map(item => item.count), + })), + }; + }; + const items: TabsProps['items'] = [ + { + key: 'detail', + label: i18n.t('meta.Sinks.DirtyData.Detail'), + children: ( + + ), + }, + { + key: 'trend', + label: i18n.t('meta.Sinks.DirtyData.Trend'), + children: ( + <> + + + + ), + }, + ]; + const onTabChange = (key: string) => { + setTabValue(key); + }; + useEffect(() => { + onTabChange('detail'); + }, [modalProps.open]); + return ( + <> + {contextHolder} + { + onTabChange('detail'); + }} + > +
+ +
+
+ + ); +}; + +export default Comp;