Files
supersonic/webapp/packages/supersonic-fe/src/components/MDatePicker/DynamicDate.tsx
tristanliu 3f08d95aaa [improvement][semantic-fe] Updating the datePicker component to use dayjs instead. (#432)
* [improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab.
[improvement][semantic-fe] Add permission control to the action buttons for the main domain; apply high sensitivity filtering to the authorization of metrics/dimensions.
[improvement][semantic-fe] Optimize the editing mode in the dimension/metric/datasource components to use the modelId stored in the database for data, instead of relying on the data from the state manager.

* [improvement][semantic-fe] Add time granularity setting in the data source configuration.

* [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility

* [improvement][semantic-fe] Modification of data source creation prompt wording"

* [improvement][semantic-fe] metric market experience optimization

* [improvement][semantic-fe] enhance the analysis of metric trends

* [improvement][semantic-fe] optimize the presentation of metric trend permissions

* [improvement][semantic-fe] add metric trend download functionality

* [improvement][semantic-fe] fix the dimension initialization issue in metric correlation

* [improvement][semantic-fe] Fix the issue of database changes not taking effect when creating based on an SQL data source.

* [improvement][semantic-fe] Optimizing pagination logic and some CSS styles

* [improvement][semantic-fe] Fixing the API for the indicator list by changing "current" to "pageNum"

* [improvement][semantic-fe] Fixing the default value setting for the indicator list

* [improvement][semantic-fe] Adding batch operations for indicators/dimensions/models

* [improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API

* [improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators

* [improvement][semantic-fe] Optimizing the logic for setting dimension values and editing data sources, and adding system settings functionality

* [improvement][semantic-fe] Upgrading antd version to 5.x, extracting the batch operation button component, optimizing the interaction for system settings, and expanding the configuration generation types for list-to-select component.

* [improvement][semantic-fe] Adding the ability to filter dimensions based on whether they are tags or not.

* [improvement][semantic-fe] Adding the ability to edit relationships between models in the canvas.

* [improvement][semantic-fe] Updating the datePicker component to use dayjs instead.
2023-11-27 21:31:51 +08:00

548 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import type { RadioChangeEvent } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { objToList } from '@/utils/utils';
// import { LatestDateMap } from '@/services/global/type';
import { DateRangeType, DateRangeTypeToPickerMap, DateRangePicker } from './type';
import {
SHORT_CUT_ITEM_LIST,
datePeriodTypeWordingMap,
getDynamicDateRangeStringByParams,
getDateStrings,
datePeriodTypeMap,
dateRangeTypeExchangeDatePeriodTypeMap,
} from './utils';
import {
DynamicAdvancedConfigType,
DatePeriodType,
PerDatePeriodType,
DateSettingType,
} from './type';
import {
Typography,
Space,
Collapse,
Tag,
Row,
Col,
Radio,
InputNumber,
Select,
Checkbox,
Tooltip,
DatePicker,
} from 'antd';
import dayjs from 'dayjs';
import styles from './style.less';
const { CheckableTag } = Tag;
const { Panel } = Collapse;
const { Link } = Typography;
const { Option } = Select;
type Props = {
initialValues?: any;
submitFormDataState?: boolean;
dateRangeTypeProps?: DateRangeType;
onDateRangeChange: (value: string[], config: any) => void;
onAdvanceSettingCollapsedChange?: (collapse: boolean) => void;
onShortCutClick?: (shortCutId: string) => void;
onDateRangeStringAndDescChange?: ({
dateRangeString,
dateRangeStringDesc,
}: {
dateRangeString: string[];
dateRangeStringDesc: string;
}) => void;
disabledAdvanceSetting?: boolean;
};
const DynamicDate: React.FC<Props> = ({
initialValues,
dateRangeTypeProps,
submitFormDataState,
onDateRangeChange,
onAdvanceSettingCollapsedChange,
onShortCutClick,
onDateRangeStringAndDescChange,
disabledAdvanceSetting = false,
}: any) => {
const initAdvacedConfigPanelCollapsed = () => {
return !initialValues?.dateSettingType || initialValues?.shortCutId ? [] : ['1'];
};
const [advancedPanelFormResetState, setAdvancedPanelFormResetState] =
useState(dateRangeTypeProps);
useEffect(() => {
// 当时间粒度发生变化时重置高级设置面板
resetAdvancedPanelForm(dateRangeTypeProps);
}, [dateRangeTypeProps]);
useEffect(() => {
if (initialValues?.dateSettingType !== DateSettingType.DYNAMIC) {
return;
}
initShortCutByDateRangeChange(dateRangeTypeProps);
}, [advancedPanelFormResetState]);
const resetAdvancedPanelForm = (dateRangeType: DateRangeType) => {
const lastConfigTypeFormData = advancedPanelFormData[DynamicAdvancedConfigType.LAST];
const historyConfigTypeFormData = advancedPanelFormData[DynamicAdvancedConfigType.HISTORY];
switch (dateRangeType) {
case DateRangeType.DAY:
setAdvancedPanelFormData({
...advancedPanelFormData,
[DynamicAdvancedConfigType.LAST]: {
...lastConfigTypeFormData,
periodType: DatePeriodType.DAY,
},
[DynamicAdvancedConfigType.HISTORY]: {
...historyConfigTypeFormData,
periodType: DatePeriodType.DAY,
},
});
break;
case DateRangeType.WEEK:
setAdvancedPanelFormData({
...advancedPanelFormData,
[DynamicAdvancedConfigType.LAST]: {
...lastConfigTypeFormData,
periodType: DatePeriodType.WEEK,
},
[DynamicAdvancedConfigType.HISTORY]: {
...historyConfigTypeFormData,
periodType: DatePeriodType.WEEK,
},
});
break;
case DateRangeType.MONTH:
setAdvancedPanelFormData({
...advancedPanelFormData,
[DynamicAdvancedConfigType.LAST]: {
...lastConfigTypeFormData,
periodType: DatePeriodType.MONTH,
},
[DynamicAdvancedConfigType.HISTORY]: {
...historyConfigTypeFormData,
periodType: DatePeriodType.MONTH,
},
});
break;
default:
break;
}
setAdvancedPanelFormResetState(dateRangeType);
setAdvacedConfigPanelCollapsed([]);
};
const initAdvancedConfigType = () => {
return initialValues?.dynamicAdvancedConfigType;
};
const initAdvancedPanelFormData = () => {
let defaultConfig = {
[DynamicAdvancedConfigType.LAST]: {
number: 1,
periodType: dateRangeTypeExchangeDatePeriodTypeMap[dateRangeTypeProps],
includesCurrentPeriod: false,
},
[DynamicAdvancedConfigType.HISTORY]: {
number: 1,
periodType: dateRangeTypeExchangeDatePeriodTypeMap[dateRangeTypeProps],
},
[DynamicAdvancedConfigType.FROM_DATE_PERIOD]: {
perPeriodType: PerDatePeriodType.PERDAY,
},
[DynamicAdvancedConfigType.FROM_DATE]: { date: dayjs() },
};
if (advancedPanelFormData) {
defaultConfig = { ...advancedPanelFormData };
}
if (initialValues?.dynamicAdvancedConfigType) {
const { dynamicAdvancedConfigType } = initialValues;
const targetConfig = defaultConfig[dynamicAdvancedConfigType];
if (!targetConfig) {
return defaultConfig;
}
const mergeConfig = Object.keys(targetConfig).reduce((result, key) => {
return {
...result,
[key]: initialValues[key],
};
}, {});
defaultConfig[dynamicAdvancedConfigType] = mergeConfig;
}
return defaultConfig;
};
const initShortCutSettingChecked = () => {
return initialValues?.shortCutId || '';
};
const [advacedConfigPanelCollapsed, setAdvacedConfigPanelCollapsed] = useState<string | string[]>(
initAdvacedConfigPanelCollapsed(),
);
const [advancedConfigType, setAdvancedConfigType] = useState<
DynamicAdvancedConfigType | undefined
>(initAdvancedConfigType());
const [advancedPanelFormData, setAdvancedPanelFormData] = useState<any>(
initAdvancedPanelFormData(),
);
const [shortCutSettingChecked, setShortCutSettingChecked] = useState<string>(
initShortCutSettingChecked(),
);
useEffect(() => {
// 外部状态触发表单数据提交
if (submitFormDataState && advancedConfigType) {
updateAdvancedPanelFormData(
advancedPanelFormData[advancedConfigType],
advancedConfigType,
true,
);
}
}, [submitFormDataState]);
const init = () => {
setAdvacedConfigPanelCollapsed(initAdvacedConfigPanelCollapsed());
setAdvancedConfigType(initAdvancedConfigType());
setAdvancedPanelFormData(initAdvancedPanelFormData());
setShortCutSettingChecked(initShortCutSettingChecked());
};
useEffect(() => {
if (initialValues?.dateSettingType === DateSettingType.DYNAMIC) {
init();
}
}, [initialValues]);
const handleDateRangeChange = (dateRange: string[], config: any) => {
onDateRangeChange(dateRange, {
...config,
dateSettingType: DateSettingType.DYNAMIC,
});
};
const updateAdvancedPanelFormData = (
formData: any,
configType: DynamicAdvancedConfigType,
emitImmediately = false,
) => {
const mergeConfigTypeData = {
...advancedPanelFormData[configType],
...formData,
// shortCutId: shortCutSettingChecked,
dateRangeType: dateRangeTypeProps,
dynamicAdvancedConfigType: configType,
};
const { dateRangeString, dateRangeStringDesc } = getDynamicDateRangeStringByParams(
mergeConfigTypeData,
configType,
{ maxPartition: dayjs().format('YYYY-MM-DD') },
);
mergeConfigTypeData.dateRangeStringDesc = dateRangeStringDesc;
onDateRangeStringAndDescChange?.({ dateRangeString, dateRangeStringDesc });
if (emitImmediately) {
handleDateRangeChange(dateRangeString, mergeConfigTypeData);
}
setAdvancedPanelFormData({
...advancedPanelFormData,
[configType]: mergeConfigTypeData,
});
};
// 根据当前时间粒度判断高级设置中时间区间选项哪些可用
const isDisabledDatePeriodTypeOption = (
datePeriodType: DatePeriodType,
dateRangeType: DateRangeType,
) => {
switch (datePeriodType) {
case DatePeriodType.DAY:
return ![DateRangeType.DAY].includes(dateRangeType);
case DatePeriodType.WEEK:
return ![DateRangeType.DAY, DateRangeType.WEEK].includes(dateRangeType);
case DatePeriodType.MONTH:
return false;
case DatePeriodType.YEAR:
return false;
default:
return false;
}
};
const getDatePeriodTypeOptions = (dateRangeType: DateRangeType) => {
const list = objToList(datePeriodTypeMap);
const optionList = list.reduce((result: any[], { value, label }: any) => {
const isDisabled = isDisabledDatePeriodTypeOption(value, dateRangeType);
if (isDisabled) {
return result;
}
result.push(
<Option value={value} key={value}>
{label}
</Option>,
);
return result;
}, []);
return optionList;
};
const isAdvancedConfigTypeRadioDisabled = (type: DynamicAdvancedConfigType) => {
return type !== advancedConfigType;
};
const initShortCutByDateRangeChange = (dateRangeType: DateRangeType, emitImmediately = false) => {
const shortCutList = SHORT_CUT_ITEM_LIST.filter((item) => {
return item.dateRangeType === dateRangeType;
});
const firstItem = shortCutList[0];
if (firstItem) {
handleShortCutChange(firstItem, emitImmediately);
}
};
const handleShortCutChange = (item: any, emitImmediately = true) => {
const { id, advancedConfigType, initData } = item;
// 设置选中状态
setShortCutSettingChecked(id);
// 设置快捷选项AdvancedConfigType类型
setAdvancedConfigType(advancedConfigType);
// 更新数据至表单数据并立即向上层组件传递
updateAdvancedPanelFormData(initData, advancedConfigType, emitImmediately);
if (emitImmediately) {
// 触发快捷选项点击时间,上层组件关闭配置浮窗
onShortCutClick?.(id);
}
};
const handleAdvancedPanelFormChange = () => {
// 当高级面板表单发生变化时,重置快捷选项为空
setShortCutSettingChecked('');
};
return (
<>
<div className={styles.dateShortCutSettingContent}>
<Row>
{SHORT_CUT_ITEM_LIST.map((item) => {
const { id, text, dateRangeType } = item;
if (dateRangeType === dateRangeTypeProps) {
return (
<Col key={`row-col-${id}`}>
<CheckableTag
className={styles['ant-tag-checkable']}
checked={shortCutSettingChecked === id}
key={`row-col-tag-${id}`}
onChange={() => {
handleShortCutChange(item);
}}
>
<div className={styles['tag-value-box']}>{text}</div>
</CheckableTag>
</Col>
);
}
return undefined;
})}
</Row>
</div>
{!disabledAdvanceSetting && (
<div className={styles.dateAdvancedSettingContent}>
<Collapse
// defaultActiveKey={['1']}
activeKey={advacedConfigPanelCollapsed}
onChange={(key: string | string[]) => {
setAdvacedConfigPanelCollapsed(key);
if (key.length === 0) {
onAdvanceSettingCollapsedChange?.(false);
return;
}
onAdvanceSettingCollapsedChange?.(true);
}}
bordered={false}
ghost={true}
expandIconPosition="right"
>
<Panel
header=""
// collapsible={'disabled'}
key="1"
extra={
<Space>
<Link></Link>
</Space>
}
>
<div>
<Space>
<div style={{ color: 'rgba(0, 0, 0, 0.85)' }}></div>
<Tooltip
title={`日期随着时间推移而更新。 若在1月1日设置查询日期为“今天” 则第二天的查询日期为1月2日。`}
>
<QuestionCircleOutlined />
</Tooltip>
</Space>
</div>
<Radio.Group
onChange={(e: RadioChangeEvent) => {
const configType = e.target.value;
setAdvancedConfigType(configType);
updateAdvancedPanelFormData(advancedPanelFormData[configType], configType);
handleAdvancedPanelFormChange();
}}
value={advancedConfigType}
>
<Space direction="vertical">
<Radio value={DynamicAdvancedConfigType.LAST}>
<Space size={10}>
<span className={styles.advancedSettingItemText}></span>
<InputNumber
style={{ width: 120 }}
placeholder="请输入数字"
min={1}
disabled={isAdvancedConfigTypeRadioDisabled(DynamicAdvancedConfigType.LAST)}
value={advancedPanelFormData[DynamicAdvancedConfigType.LAST].number}
onChange={(value: number | null) => {
updateAdvancedPanelFormData(
{ number: value },
DynamicAdvancedConfigType.LAST,
);
handleAdvancedPanelFormChange();
}}
/>
<Select
// defaultValue={DatePeriodType.DAY}
style={{ width: 120 }}
disabled={isAdvancedConfigTypeRadioDisabled(DynamicAdvancedConfigType.LAST)}
value={advancedPanelFormData[DynamicAdvancedConfigType.LAST].periodType}
onClick={(e) => {
// 禁止冒泡触发Radio点击后续逻辑
e.preventDefault();
e.stopPropagation();
}}
onChange={(value: string) => {
updateAdvancedPanelFormData(
{ periodType: value },
DynamicAdvancedConfigType.LAST,
);
handleAdvancedPanelFormChange();
}}
>
{getDatePeriodTypeOptions(dateRangeTypeProps)}
</Select>
<Checkbox
disabled={isAdvancedConfigTypeRadioDisabled(DynamicAdvancedConfigType.LAST)}
checked={
advancedPanelFormData[DynamicAdvancedConfigType.LAST]
.includesCurrentPeriod
}
onChange={(e) => {
const isChecked = e.target.checked;
updateAdvancedPanelFormData(
{ includesCurrentPeriod: isChecked },
DynamicAdvancedConfigType.LAST,
);
handleAdvancedPanelFormChange();
}}
>
{
datePeriodTypeWordingMap[
advancedPanelFormData[DynamicAdvancedConfigType.LAST].periodType
]
}
</Checkbox>
</Space>
</Radio>
<Radio value={DynamicAdvancedConfigType.HISTORY}>
<Space size={10}>
<span className={styles.advancedSettingItemText}></span>
<InputNumber
style={{ width: 120 }}
placeholder="请输入数字"
min={1}
disabled={isAdvancedConfigTypeRadioDisabled(
DynamicAdvancedConfigType.HISTORY,
)}
value={advancedPanelFormData[DynamicAdvancedConfigType.HISTORY].number}
// defaultValue={3}
onChange={(value: number | null) => {
updateAdvancedPanelFormData(
{ number: value },
DynamicAdvancedConfigType.HISTORY,
);
handleAdvancedPanelFormChange();
}}
/>
<Select
// defaultValue={DatePeriodType.DAY}
style={{ width: 120 }}
disabled={isAdvancedConfigTypeRadioDisabled(
DynamicAdvancedConfigType.HISTORY,
)}
value={advancedPanelFormData[DynamicAdvancedConfigType.HISTORY].periodType}
onClick={(e) => {
// 禁止冒泡触发Radio点击后续逻辑
e.preventDefault();
e.stopPropagation();
}}
onChange={(value: string) => {
updateAdvancedPanelFormData(
{ periodType: value },
DynamicAdvancedConfigType.HISTORY,
);
handleAdvancedPanelFormChange();
}}
>
{getDatePeriodTypeOptions(dateRangeTypeProps)}
</Select>
</Space>
</Radio>
<Radio value={DynamicAdvancedConfigType.FROM_DATE}>
<Space size={10}>
<span className={styles.advancedSettingItemText}></span>
<DatePicker
disabled={isAdvancedConfigTypeRadioDisabled(
DynamicAdvancedConfigType.FROM_DATE,
)}
value={dayjs(
advancedPanelFormData[DynamicAdvancedConfigType.FROM_DATE].date,
)}
disabledDate={(current) => {
return current && current > dayjs().endOf('day');
}}
picker={DateRangeTypeToPickerMap[dateRangeTypeProps]}
onChange={(date, dateString) => {
if (!date) {
return;
}
const picker = DateRangeTypeToPickerMap[dateRangeTypeProps];
if (picker === DateRangePicker.WEEK) {
date.startOf('week').format('YYYY-MM-DD');
}
if (picker === DateRangePicker.MONTH) {
date.startOf('month').format('YYYY-MM-DD');
}
updateAdvancedPanelFormData(
{ date },
DynamicAdvancedConfigType.FROM_DATE,
);
handleAdvancedPanelFormChange();
}}
/>
</Space>
</Radio>
</Space>
</Radio.Group>
</Panel>
</Collapse>
</div>
)}
</>
);
};
export default DynamicDate;