mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-11 20:25:12 +00:00
* [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.
548 lines
19 KiB
TypeScript
548 lines
19 KiB
TypeScript
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;
|