mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-11 12:07:42 +00:00
[improvement][headless-fe] The view management functionality has been added. This feature allows users to create, edit, and manage different views within the system. (#717)
* [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. * [improvement][semantic-fe] Fixing the issue with passing the model ID for dimensions in the indicator market. * [improvement][semantic-fe] Fixing the abnormal state of the popup when creating a model. * [improvement][semantic-fe] Adding permission logic for bulk operations in the indicator market. * [improvement][semantic-fe] Adding the ability to download and transpose data. * [improvement][semantic-fe] Fixing the initialization issue with the date selection component in the indicator details page when switching time granularity. * [improvement][semantic-fe] Fixing the logic error in the dimension value setting. * [improvement][semantic-fe] Fixing the synchronization issue with the question and answer settings information. * [improvement][semantic-fe] Optimizing the canvas functionality for better performance and user experience. * [improvement][semantic-fe] Optimizing the update process for drawing model relationship edges in the canvas. * [improvement][semantic-fe] Changing the line type for canvas connections. * [improvement][semantic-fe] Replacing the initialization variable from "semantic" to "headless". * [improvement][semantic-fe] Fixing the missing migration issue for default drill-down dimension configuration in model editing. Additionally, optimizing the data retrieval method for initializing fields in the model. * [improvement][semantic-fe] Updating the logic for the fieldName. * [improvement][semantic-fe] Adjusting the position of the metrics tab. * [improvement][semantic-fe] Changing the 字段名称 to 英文名称. * [improvement][semantic-fe] Fix metric measurement deletion. * [improvement][semantic-fe] UI optimization for metric details page. * [improvement][semantic-fe] UI optimization for metric details page. * [improvement][semantic-fe] UI adjustment for metric details page. * [improvement][semantic-fe] The granularity field in the time type of model editing now supports setting it as empty. * [improvement][semantic-fe] Added field type and metric type to the metric creation options. * [improvement][semantic-fe] The organization structure selection feature has been added to the permission management. * [improvement][semantic-fe] Improved user experience for the metric list. * [improvement][semantic-fe] fix update the metric list. * [improvement][headless-fe] Added view management functionality. * [improvement][headless-fe] The view management functionality has been added. This feature allows users to create, edit, and manage different views within the system.
This commit is contained in:
@@ -1,15 +1,13 @@
|
|||||||
import React, { useRef, forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
||||||
import type { Ref, ReactNode } from 'react';
|
import type { Ref } from 'react';
|
||||||
import type { RadioChangeEvent } from 'antd';
|
import type { RadioChangeEvent } from 'antd';
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
import { objToList } from '@/utils/utils';
|
import { objToList } from '@/utils/utils';
|
||||||
// import { LatestDateMap } from '@/services/global/type';
|
|
||||||
import { DateRangeType, DateRangeTypeToPickerMap, DateRangePicker } from './type';
|
import { DateRangeType, DateRangeTypeToPickerMap, DateRangePicker } from './type';
|
||||||
import {
|
import {
|
||||||
SHORT_CUT_ITEM_LIST,
|
SHORT_CUT_ITEM_LIST,
|
||||||
datePeriodTypeWordingMap,
|
datePeriodTypeWordingMap,
|
||||||
getDynamicDateRangeStringByParams,
|
getDynamicDateRangeStringByParams,
|
||||||
getDateStrings,
|
|
||||||
datePeriodTypeMap,
|
datePeriodTypeMap,
|
||||||
dateRangeTypeExchangeDatePeriodTypeMap,
|
dateRangeTypeExchangeDatePeriodTypeMap,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
@@ -74,7 +72,6 @@ const DynamicDate: React.FC<Props> = forwardRef(
|
|||||||
) => {
|
) => {
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
dynamicDateUpdateAdvancedPanelFormData: () => {
|
dynamicDateUpdateAdvancedPanelFormData: () => {
|
||||||
// return [...dataSource];
|
|
||||||
if (advancedConfigType) {
|
if (advancedConfigType) {
|
||||||
updateAdvancedPanelFormData(
|
updateAdvancedPanelFormData(
|
||||||
advancedPanelFormData[advancedConfigType],
|
advancedPanelFormData[advancedConfigType],
|
||||||
@@ -420,7 +417,6 @@ const DynamicDate: React.FC<Props> = forwardRef(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
// defaultValue={DatePeriodType.DAY}
|
|
||||||
style={{ width: 120 }}
|
style={{ width: 120 }}
|
||||||
disabled={isAdvancedConfigTypeRadioDisabled(
|
disabled={isAdvancedConfigTypeRadioDisabled(
|
||||||
DynamicAdvancedConfigType.LAST,
|
DynamicAdvancedConfigType.LAST,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.standardFormRow {
|
.standardFormRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 16px;
|
||||||
// padding-bottom: 16px;
|
// padding-bottom: 16px;
|
||||||
// border-bottom: 1px dashed @border-color-split;
|
// border-bottom: 1px dashed @border-color-split;
|
||||||
:global {
|
:global {
|
||||||
|
|||||||
@@ -170,18 +170,6 @@ ol {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// .ant-menu-submenu-selected {
|
|
||||||
// background-color: #296DF3 !important;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// .ant-menu-item:active {
|
|
||||||
// background: inherit
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.ant-pro-top-nav-header-main-left {
|
.ant-pro-top-nav-header-main-left {
|
||||||
min-width: 100px !important;
|
min-width: 100px !important;
|
||||||
}
|
}
|
||||||
@@ -191,20 +179,6 @@ ol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// .link {
|
|
||||||
// color: #296df3;
|
|
||||||
// cursor: pointer;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// .dot {
|
|
||||||
// float: right;
|
|
||||||
// width: 8px;
|
|
||||||
// height: 8px;
|
|
||||||
// background: #bfbfbf;
|
|
||||||
// border-radius: 100%;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -213,10 +187,6 @@ ol {
|
|||||||
padding-right: 50px;
|
padding-right: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .ant-notification-topRight {
|
|
||||||
// right: 240px !important;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
.g6ContextMenuContainer {
|
.g6ContextMenuContainer {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -257,3 +227,16 @@ ol {
|
|||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inherit-from-model-row{
|
||||||
|
background-color: #e6edfc;
|
||||||
|
|
||||||
|
.ant-table-cell-row-hover {
|
||||||
|
background-color:#e6edfc!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form-item .ant-form-item-label >label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #667085;
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ const ChatSettingSection: React.FC<Props> = () => {
|
|||||||
<ProCard bordered title="指标模式" style={{ marginBottom: 20 }}>
|
<ProCard bordered title="指标模式" style={{ marginBottom: 20 }}>
|
||||||
<EntitySection
|
<EntitySection
|
||||||
ref={metricRef}
|
ref={metricRef}
|
||||||
chatConfigType={ChatConfigType.AGG}
|
chatConfigType={ChatConfigType.METRIC}
|
||||||
onConfigSave={() => {
|
onConfigSave={() => {
|
||||||
tagRef.current.refreshConfigData();
|
tagRef.current.refreshConfigData();
|
||||||
}}
|
}}
|
||||||
@@ -28,7 +28,7 @@ const ChatSettingSection: React.FC<Props> = () => {
|
|||||||
<ProCard bordered title="标签模式" style={{ marginBottom: 20 }}>
|
<ProCard bordered title="标签模式" style={{ marginBottom: 20 }}>
|
||||||
<EntitySection
|
<EntitySection
|
||||||
ref={tagRef}
|
ref={tagRef}
|
||||||
chatConfigType={ChatConfigType.DETAIL}
|
chatConfigType={ChatConfigType.TAG}
|
||||||
onConfigSave={() => {
|
onConfigSave={() => {
|
||||||
metricRef.current.refreshConfigData();
|
metricRef.current.refreshConfigData();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ const ChatSetting: React.FC<Props> = ({
|
|||||||
{
|
{
|
||||||
label: '指标模式',
|
label: '指标模式',
|
||||||
key: 'metric',
|
key: 'metric',
|
||||||
children: <EntitySection chatConfigType={ChatConfigType.AGG} />,
|
children: <EntitySection chatConfigType={ChatConfigType.METRIC} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '标签模式',
|
label: '标签模式',
|
||||||
key: 'dimenstion',
|
key: 'dimenstion',
|
||||||
children: <EntitySection chatConfigType={ChatConfigType.DETAIL} />,
|
children: <EntitySection chatConfigType={ChatConfigType.TAG} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '推荐问题',
|
label: '推荐问题',
|
||||||
|
|||||||
@@ -298,15 +298,6 @@ const DataSourceFieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSq
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <Alert
|
|
||||||
style={{ marginBottom: '10px' }}
|
|
||||||
banner
|
|
||||||
message={
|
|
||||||
<div>
|
|
||||||
为了保障同一个模型下维度/指标列表唯一,消除歧义,若本模型下的多个数据源存在相同的字段名并且都勾选了快速创建,系统默认这些相同字段的指标维度是同一个,同时列表中将只显示第一次创建的指标/维度。
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/> */}
|
|
||||||
<Table<FieldItem>
|
<Table<FieldItem>
|
||||||
dataSource={fields}
|
dataSource={fields}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|||||||
@@ -468,16 +468,6 @@ const SqlDetail: React.FC<IProps> = ({
|
|||||||
<div className={`${styles.sqlBottmWrap} ${screenSize}`}>
|
<div className={`${styles.sqlBottmWrap} ${screenSize}`}>
|
||||||
<div className={styles.sqlResultWrap}>
|
<div className={styles.sqlResultWrap}>
|
||||||
<div className={styles.sqlToolBar}>
|
<div className={styles.sqlToolBar}>
|
||||||
{/* {
|
|
||||||
<Button
|
|
||||||
className={styles.sqlToolBtn}
|
|
||||||
type="primary"
|
|
||||||
onClick={startCreatDataSource}
|
|
||||||
disabled={!runState}
|
|
||||||
>
|
|
||||||
生成数据源
|
|
||||||
</Button>
|
|
||||||
} */}
|
|
||||||
<Button
|
<Button
|
||||||
className={styles.sqlToolBtn}
|
className={styles.sqlToolBtn}
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ const LIST_KEY = 'list';
|
|||||||
const SqlSide: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
|
const SqlSide: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
|
||||||
const defaultPanes: Panes[] = [
|
const defaultPanes: Panes[] = [
|
||||||
{
|
{
|
||||||
key: '数据源查询',
|
key: '模型查询',
|
||||||
title: initialValues?.name || '数据源查询',
|
title: initialValues?.name || '模型查询',
|
||||||
type: 'add',
|
type: 'add',
|
||||||
isSave: true,
|
isSave: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const [activeKey, setActiveKey] = useState('数据源查询');
|
const [activeKey, setActiveKey] = useState('模型查询');
|
||||||
const [panes, setPanes] = useState<Panes[]>(defaultPanes);
|
const [panes, setPanes] = useState<Panes[]>(defaultPanes);
|
||||||
const tableRef: TableRef = useRef();
|
const tableRef: TableRef = useRef();
|
||||||
const panesRef = useRef<Panes[]>(defaultPanes);
|
const panesRef = useRef<Panes[]>(defaultPanes);
|
||||||
@@ -63,7 +63,7 @@ const SqlSide: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialValues) {
|
if (initialValues) {
|
||||||
updateTabSql(initialValues?.modelDetail?.sqlQuery || '', '数据源查询');
|
updateTabSql(initialValues?.modelDetail?.sqlQuery || '', '模型查询');
|
||||||
}
|
}
|
||||||
}, [initialValues]);
|
}, [initialValues]);
|
||||||
|
|
||||||
|
|||||||
@@ -175,26 +175,26 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
render: ColumnsConfig.metricInfo.render,
|
render: ColumnsConfig.metricInfo.render,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
dataIndex: 'modelName',
|
// dataIndex: 'modelName',
|
||||||
title: '所属模型',
|
// title: '所属模型',
|
||||||
render: (_, record: any) => {
|
// render: (_, record: any) => {
|
||||||
if (record.hasAdminRes) {
|
// if (record.hasAdminRes) {
|
||||||
return (
|
// return (
|
||||||
<a
|
// <a
|
||||||
target="blank"
|
// target="blank"
|
||||||
href={`/webapp/model/${record.domainId}/${record.modelId}/metric`}
|
// href={`/webapp/model/${record.domainId}/${record.modelId}/metric`}
|
||||||
// onClick={() => {
|
// // onClick={() => {
|
||||||
// history.push(`/model/${record.domainId}/${record.modelId}/metric`);
|
// // history.push(`/model/${record.domainId}/${record.modelId}/metric`);
|
||||||
// }}
|
// // }}
|
||||||
>
|
// >
|
||||||
{record.modelName}
|
// {record.modelName}
|
||||||
</a>
|
// </a>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
return <> {record.modelName}</>;
|
// return <> {record.modelName}</>;
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
dataIndex: 'sensitiveLevel',
|
dataIndex: 'sensitiveLevel',
|
||||||
title: '敏感度',
|
title: '敏感度',
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ const DataSourceRelationFormDrawer: React.FC<DataSourceRelationFormDrawerProps>
|
|||||||
<FormItem hidden={true} name="id" label="ID">
|
<FormItem hidden={true} name="id" label="ID">
|
||||||
<Input placeholder="id" />
|
<Input placeholder="id" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="主数据源:">{nodeDataSource?.sourceData?.name}</FormItem>
|
<FormItem label="主模型:">{nodeDataSource?.sourceData?.name}</FormItem>
|
||||||
<FormItem label="关联数据源:">{nodeDataSource?.targetData?.name}</FormItem>
|
<FormItem label="关联模型:">{nodeDataSource?.targetData?.name}</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
name="joinKey"
|
name="joinKey"
|
||||||
label="可关联Key:"
|
label="可关联Key:"
|
||||||
tooltip="主从数据源中必须具有相同的主键或外键才可建立关联关系"
|
tooltip="主从模型中必须具有相同的主键或外键才可建立关联关系"
|
||||||
rules={[{ required: true, message: '请选择关联Key' }]}
|
rules={[{ required: true, message: '请选择关联Key' }]}
|
||||||
>
|
>
|
||||||
<Select placeholder="请选择关联Key">
|
<Select placeholder="请选择关联Key">
|
||||||
@@ -148,7 +148,7 @@ const DataSourceRelationFormDrawer: React.FC<DataSourceRelationFormDrawerProps>
|
|||||||
forceRender
|
forceRender
|
||||||
width={400}
|
width={400}
|
||||||
getContainer={false}
|
getContainer={false}
|
||||||
title={'数据源关联信息'}
|
title={'模型关联信息'}
|
||||||
mask={false}
|
mask={false}
|
||||||
open={open}
|
open={open}
|
||||||
footer={renderFooter()}
|
footer={renderFooter()}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const DeleteConfirmModal: React.FC<Props> = ({
|
|||||||
deleteQuery = deleteDatasource;
|
deleteQuery = deleteDatasource;
|
||||||
}
|
}
|
||||||
if (!deleteQuery) {
|
if (!deleteQuery) {
|
||||||
message.error('当前节点类型不是维度,指标,数据源中的一种,请确认节点数据');
|
message.error('当前节点类型不是维度,指标,模型中的一种,请确认节点数据');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setConfirmLoading(true);
|
setConfirmLoading(true);
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const GraphLegend: React.FC<Props> = ({
|
|||||||
<div className={styles.graphLegend}>
|
<div className={styles.graphLegend}>
|
||||||
<Checkbox.Group style={{ width: '100%' }} onChange={handleChange} value={groupValue}>
|
<Checkbox.Group style={{ width: '100%' }} onChange={handleChange} value={groupValue}>
|
||||||
<div style={{ width: '100%', maxWidth: '450px' }}>
|
<div style={{ width: '100%', maxWidth: '450px' }}>
|
||||||
<div className={styles.title}>可见数据源</div>
|
<div className={styles.title}>可见模型</div>
|
||||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||||
<Space wrap size={[8, 16]}>
|
<Space wrap size={[8, 16]}>
|
||||||
{legendOptions.map((item) => {
|
{legendOptions.map((item) => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const initLegend = ({ nodeData, filterFunctions }) => {
|
|||||||
fill: '#a6ccff',
|
fill: '#a6ccff',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
},
|
},
|
||||||
title: '可见数据源',
|
title: '可见模型',
|
||||||
titleConfig: {
|
titleConfig: {
|
||||||
position: 'center',
|
position: 'center',
|
||||||
offsetX: 0,
|
offsetX: 0,
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ const ModelRelationFormDrawer: React.FC<ModelRelationFormDrawerProps> = ({
|
|||||||
<FormItem hidden={true} name="id" label="ID">
|
<FormItem hidden={true} name="id" label="ID">
|
||||||
<Input placeholder="id" />
|
<Input placeholder="id" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="起始数据源:">
|
<FormItem label="起始模型:">
|
||||||
<span style={{ color: '#296df3', fontWeight: 500 }}>{nodeModel?.sourceData?.name}</span>
|
<span style={{ color: '#296df3', fontWeight: 500 }}>{nodeModel?.sourceData?.name}</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="目标数据源:">
|
<FormItem label="目标模型:">
|
||||||
<span style={{ color: '#296df3', fontWeight: 500 }}>{nodeModel?.targetData?.name}</span>
|
<span style={{ color: '#296df3', fontWeight: 500 }}>{nodeModel?.targetData?.name}</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
@@ -231,7 +231,7 @@ const ModelRelationFormDrawer: React.FC<ModelRelationFormDrawerProps> = ({
|
|||||||
width={400}
|
width={400}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
getContainer={false}
|
getContainer={false}
|
||||||
title={'数据源关联信息'}
|
title={'模型关联信息'}
|
||||||
mask={false}
|
mask={false}
|
||||||
open={open}
|
open={open}
|
||||||
footer={renderFooter()}
|
footer={renderFooter()}
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
|
||||||
|
import type { ForwardRefRenderFunction } from 'react';
|
||||||
|
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||||
|
|
||||||
|
import { Form, Input, Select, InputNumber } from 'antd';
|
||||||
|
|
||||||
|
import { wrapperTransTypeAndId } from '../../utils';
|
||||||
|
|
||||||
|
import { ISemantic } from '../../data';
|
||||||
|
import { ChatConfigType, TransType, SemanticNodeType } from '../../enum';
|
||||||
|
import TransTypeTag from '../../components/TransTypeTag';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
// entityData: any;
|
||||||
|
// chatConfigKey: string;
|
||||||
|
chatConfigType: ChatConfigType.TAG | ChatConfigType.METRIC;
|
||||||
|
metricList?: ISemantic.IMetricItem[];
|
||||||
|
dimensionList?: ISemantic.IDimensionItem[];
|
||||||
|
form: any;
|
||||||
|
// domainId: number;
|
||||||
|
// onSubmit: (params?: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const Option = Select.Option;
|
||||||
|
|
||||||
|
const formDefaultValue = {
|
||||||
|
unit: 7,
|
||||||
|
period: 'DAY',
|
||||||
|
timeMode: 'LAST',
|
||||||
|
};
|
||||||
|
|
||||||
|
const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
||||||
|
{ metricList, dimensionList, chatConfigType, form },
|
||||||
|
ref,
|
||||||
|
) => {
|
||||||
|
const [dataItemListOptions, setDataItemListOptions] = useState<any>([]);
|
||||||
|
|
||||||
|
const initData = () => {
|
||||||
|
form.setFieldsValue({
|
||||||
|
queryConfig: {
|
||||||
|
[defaultConfigKeyMap[chatConfigType]]: {
|
||||||
|
timeDefaultConfig: {
|
||||||
|
...formDefaultValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (form && !form.getFieldValue('id')) {
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const defaultConfigKeyMap = {
|
||||||
|
[ChatConfigType.TAG]: 'tagTypeDefaultConfig',
|
||||||
|
[ChatConfigType.METRIC]: 'metricTypeDefaultConfig',
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Array.isArray(dimensionList) && Array.isArray(metricList)) {
|
||||||
|
const dimensionEnum = dimensionList.map((item: ISemantic.IDimensionItem) => {
|
||||||
|
const { name, id, bizName } = item;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<TransTypeTag type={SemanticNodeType.DIMENSION} />
|
||||||
|
{name}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: wrapperTransTypeAndId(TransType.DIMENSION, id),
|
||||||
|
bizName,
|
||||||
|
id,
|
||||||
|
transType: TransType.DIMENSION,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const metricEnum = metricList.map((item: ISemantic.IMetricItem) => {
|
||||||
|
const { name, id, bizName } = item;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<TransTypeTag type={SemanticNodeType.METRIC} />
|
||||||
|
{name}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: wrapperTransTypeAndId(TransType.METRIC, id),
|
||||||
|
bizName,
|
||||||
|
id,
|
||||||
|
transType: TransType.METRIC,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setDataItemListOptions([...dimensionEnum, ...metricEnum]);
|
||||||
|
}
|
||||||
|
}, [dimensionList, metricList]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{chatConfigType === ChatConfigType.TAG && (
|
||||||
|
<FormItem
|
||||||
|
name={['queryConfig', defaultConfigKeyMap[ChatConfigType.TAG], 'defaultDisplayInfo']}
|
||||||
|
label="圈选结果展示字段"
|
||||||
|
getValueFromEvent={(value, items) => {
|
||||||
|
const result: { dimensionIds: number[]; metricIds: number[] } = {
|
||||||
|
dimensionIds: [],
|
||||||
|
metricIds: [],
|
||||||
|
};
|
||||||
|
items.forEach((item: any) => {
|
||||||
|
if (item.transType === TransType.DIMENSION) {
|
||||||
|
result.dimensionIds.push(item.id);
|
||||||
|
}
|
||||||
|
if (item.transType === TransType.METRIC) {
|
||||||
|
result.metricIds.push(item.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}}
|
||||||
|
getValueProps={(value) => {
|
||||||
|
const { dimensionIds, metricIds } = value || {};
|
||||||
|
const dimensionValues = Array.isArray(dimensionIds)
|
||||||
|
? dimensionIds.map((id: number) => {
|
||||||
|
return wrapperTransTypeAndId(TransType.DIMENSION, id);
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
const metricValues = Array.isArray(metricIds)
|
||||||
|
? metricIds.map((id: number) => {
|
||||||
|
return wrapperTransTypeAndId(TransType.METRIC, id);
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
return {
|
||||||
|
value: [...dimensionValues, ...metricValues],
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
allowClear
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
optionLabelProp="name"
|
||||||
|
filterOption={(inputValue: string, item: any) => {
|
||||||
|
const { name } = item;
|
||||||
|
if (name.includes(inputValue)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
placeholder="请选择圈选结果展示字段"
|
||||||
|
options={dataItemListOptions}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
<FormItem
|
||||||
|
label={
|
||||||
|
<FormItemTitle
|
||||||
|
title={'时间范围'}
|
||||||
|
subTitle={'问答搜索结果选择中,如果没有指定时间范围,将会采用默认时间范围'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input.Group compact>
|
||||||
|
{chatConfigType === ChatConfigType.TAG ? (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
lineHeight: '32px',
|
||||||
|
marginRight: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
前
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FormItem
|
||||||
|
name={[
|
||||||
|
'queryConfig',
|
||||||
|
defaultConfigKeyMap[chatConfigType],
|
||||||
|
'timeDefaultConfig',
|
||||||
|
'timeMode',
|
||||||
|
]}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Select style={{ width: '90px' }}>
|
||||||
|
<Option value="LAST">前</Option>
|
||||||
|
<Option value="RECENT">最近</Option>
|
||||||
|
</Select>
|
||||||
|
</FormItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<FormItem
|
||||||
|
name={['queryConfig', defaultConfigKeyMap[chatConfigType], 'timeDefaultConfig', 'unit']}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<InputNumber style={{ width: '120px' }} />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
name={[
|
||||||
|
'queryConfig',
|
||||||
|
defaultConfigKeyMap[chatConfigType],
|
||||||
|
'timeDefaultConfig',
|
||||||
|
'period',
|
||||||
|
]}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Select style={{ width: '90px' }}>
|
||||||
|
<Option value="DAY">天</Option>
|
||||||
|
<Option value="WEEK">周</Option>
|
||||||
|
<Option value="MONTH">月</Option>
|
||||||
|
<Option value="YEAR">年</Option>
|
||||||
|
</Select>
|
||||||
|
</FormItem>
|
||||||
|
</Input.Group>
|
||||||
|
</FormItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default forwardRef(DefaultSettingForm);
|
||||||
@@ -4,19 +4,21 @@ import { ISemantic } from '../../data';
|
|||||||
|
|
||||||
import { TransType } from '../../enum';
|
import { TransType } from '../../enum';
|
||||||
import DimensionMetricVisibleTransfer from '../../components/Entity/DimensionMetricVisibleTransfer';
|
import DimensionMetricVisibleTransfer from '../../components/Entity/DimensionMetricVisibleTransfer';
|
||||||
import { wrapperTransTypeAndId } from '../../components/Entity/utils';
|
import { wrapperTransTypeAndId } from '../../utils';
|
||||||
|
|
||||||
export type ModelCreateFormModalProps = {
|
export type ModelCreateFormModalProps = {
|
||||||
dimensionList: ISemantic.IDimensionItem[];
|
dimensionList?: ISemantic.IDimensionItem[];
|
||||||
metricList: ISemantic.IMetricItem[];
|
metricList?: ISemantic.IMetricItem[];
|
||||||
modelId?: number;
|
modelId?: number;
|
||||||
selectedTransferKeys: React.Key[];
|
selectedTransferKeys: React.Key[];
|
||||||
|
toolbarSolt?: ReactNode;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSubmit: (values: any, selectedKeys: React.Key[]) => void;
|
onSubmit: (values: any, selectedKeys: React.Key[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DimensionMetricTransferModal: React.FC<ModelCreateFormModalProps> = ({
|
const DimensionMetricTransferModal: React.FC<ModelCreateFormModalProps> = ({
|
||||||
modelId,
|
modelId,
|
||||||
|
toolbarSolt,
|
||||||
selectedTransferKeys,
|
selectedTransferKeys,
|
||||||
metricList,
|
metricList,
|
||||||
dimensionList,
|
dimensionList,
|
||||||
@@ -36,6 +38,9 @@ const DimensionMetricTransferModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!dimensionList || !metricList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const sourceDimensionList = dimensionList.reduce((mergeList: any[], item) => {
|
const sourceDimensionList = dimensionList.reduce((mergeList: any[], item) => {
|
||||||
mergeList.push(addItemKey(item, TransType.DIMENSION));
|
mergeList.push(addItemKey(item, TransType.DIMENSION));
|
||||||
return mergeList;
|
return mergeList;
|
||||||
@@ -84,7 +89,7 @@ const DimensionMetricTransferModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DimensionMetricVisibleTransfer
|
<DimensionMetricVisibleTransfer
|
||||||
titles={['未关联维度/指标', '已关联维度/指标']}
|
titles={[<>{toolbarSolt}</>, '已加入维度/指标']}
|
||||||
listStyle={{
|
listStyle={{
|
||||||
width: 520,
|
width: 520,
|
||||||
height: 600,
|
height: 600,
|
||||||
@@ -94,26 +99,27 @@ const DimensionMetricTransferModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
onChange={(newTargetKeys: string[]) => {
|
onChange={(newTargetKeys: string[]) => {
|
||||||
const removeDimensionList: ISemantic.IDimensionItem[] = [];
|
const removeDimensionList: ISemantic.IDimensionItem[] = [];
|
||||||
const removeMetricList: ISemantic.IMetricItem[] = [];
|
const removeMetricList: ISemantic.IMetricItem[] = [];
|
||||||
const dimensionItemChangeList = dimensionList.reduce(
|
const dimensionItemChangeList = Array.isArray(dimensionList)
|
||||||
(dimensionChangeList: any[], item: any) => {
|
? dimensionList.reduce((dimensionChangeList: any[], item: any) => {
|
||||||
if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.DIMENSION, item.id))) {
|
if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.DIMENSION, item.id))) {
|
||||||
dimensionChangeList.push(item);
|
dimensionChangeList.push(item);
|
||||||
} else {
|
} else {
|
||||||
removeDimensionList.push(item.id);
|
removeDimensionList.push(item.id);
|
||||||
}
|
}
|
||||||
return dimensionChangeList;
|
return dimensionChangeList;
|
||||||
},
|
}, [])
|
||||||
[],
|
: [];
|
||||||
);
|
|
||||||
|
|
||||||
const metricItemChangeList = metricList.reduce((metricChangeList: any[], item: any) => {
|
const metricItemChangeList = Array.isArray(metricList)
|
||||||
if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.METRIC, item.id))) {
|
? metricList.reduce((metricChangeList: any[], item: any) => {
|
||||||
metricChangeList.push(item);
|
if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.METRIC, item.id))) {
|
||||||
} else {
|
metricChangeList.push(item);
|
||||||
removeMetricList.push(item.id);
|
} else {
|
||||||
}
|
removeMetricList.push(item.id);
|
||||||
return metricChangeList;
|
}
|
||||||
}, []);
|
return metricChangeList;
|
||||||
|
}, [])
|
||||||
|
: [];
|
||||||
|
|
||||||
setSelectedItemList([...dimensionItemChangeList, ...metricItemChangeList]);
|
setSelectedItemList([...dimensionItemChangeList, ...metricItemChangeList]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,773 +0,0 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
Button,
|
|
||||||
Modal,
|
|
||||||
Steps,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
Radio,
|
|
||||||
Switch,
|
|
||||||
InputNumber,
|
|
||||||
message,
|
|
||||||
Result,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
Space,
|
|
||||||
Tooltip,
|
|
||||||
Tag,
|
|
||||||
} from 'antd';
|
|
||||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
|
||||||
import MetricMeasuresFormTable from './MetricMeasuresFormTable';
|
|
||||||
import { SENSITIVE_LEVEL_OPTIONS, METRIC_DEFINE_TYPE } from '../constant';
|
|
||||||
import { formLayout } from '@/components/FormHelper/utils';
|
|
||||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
|
||||||
import styles from './style.less';
|
|
||||||
import { getMetricsToCreateNewMetric, getModelDetail, getDrillDownDimension } from '../service';
|
|
||||||
import MetricMetricFormTable from './MetricMetricFormTable';
|
|
||||||
import MetricFieldFormTable from './MetricFieldFormTable';
|
|
||||||
import DimensionAndMetricRelationModal from './DimensionAndMetricRelationModal';
|
|
||||||
import TableTitleTooltips from '../components/TableTitleTooltips';
|
|
||||||
import { createMetric, updateMetric, mockMetricAlias, getMetricTags } from '../service';
|
|
||||||
import { ISemantic } from '../data';
|
|
||||||
import { history } from 'umi';
|
|
||||||
|
|
||||||
export type CreateFormProps = {
|
|
||||||
datasourceId?: number;
|
|
||||||
domainId: number;
|
|
||||||
modelId: number;
|
|
||||||
createModalVisible: boolean;
|
|
||||||
metricItem: any;
|
|
||||||
onCancel?: () => void;
|
|
||||||
onSubmit?: (values: any) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const { Step } = Steps;
|
|
||||||
const FormItem = Form.Item;
|
|
||||||
const { TextArea } = Input;
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
const queryParamsTypeParamsKey = {
|
|
||||||
[METRIC_DEFINE_TYPE.MEASURE]: 'metricDefineByMeasureParams',
|
|
||||||
[METRIC_DEFINE_TYPE.METRIC]: 'metricDefineByMetricParams',
|
|
||||||
[METRIC_DEFINE_TYPE.FIELD]: 'metricDefineByFieldParams',
|
|
||||||
};
|
|
||||||
|
|
||||||
const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|
||||||
datasourceId,
|
|
||||||
domainId,
|
|
||||||
modelId,
|
|
||||||
onCancel,
|
|
||||||
createModalVisible,
|
|
||||||
metricItem,
|
|
||||||
onSubmit,
|
|
||||||
}) => {
|
|
||||||
const isEdit = !!metricItem?.id;
|
|
||||||
const [currentStep, setCurrentStep] = useState(0);
|
|
||||||
const formValRef = useRef({} as any);
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const updateFormVal = (val: any) => {
|
|
||||||
const formVal = {
|
|
||||||
...formValRef.current,
|
|
||||||
...val,
|
|
||||||
};
|
|
||||||
formValRef.current = formVal;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [classMeasureList, setClassMeasureList] = useState<ISemantic.IMeasure[]>([]);
|
|
||||||
|
|
||||||
const [exprTypeParamsState, setExprTypeParamsState] = useState<{
|
|
||||||
[METRIC_DEFINE_TYPE.MEASURE]: ISemantic.IMeasureTypeParams;
|
|
||||||
[METRIC_DEFINE_TYPE.METRIC]: ISemantic.IMetricTypeParams;
|
|
||||||
[METRIC_DEFINE_TYPE.FIELD]: ISemantic.IFieldTypeParams;
|
|
||||||
}>({
|
|
||||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
|
||||||
measures: [],
|
|
||||||
expr: '',
|
|
||||||
},
|
|
||||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
|
||||||
metrics: [],
|
|
||||||
expr: '',
|
|
||||||
},
|
|
||||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
|
||||||
fields: [],
|
|
||||||
expr: '',
|
|
||||||
},
|
|
||||||
} as any);
|
|
||||||
|
|
||||||
// const [exprTypeParamsState, setExprTypeParamsState] = useState<ISemantic.IMeasure[]>([]);
|
|
||||||
|
|
||||||
const [defineType, setDefineType] = useState(METRIC_DEFINE_TYPE.MEASURE);
|
|
||||||
|
|
||||||
const [createNewMetricList, setCreateNewMetricList] = useState<ISemantic.IMetricItem[]>([]);
|
|
||||||
const [fieldList, setFieldList] = useState<string[]>([]);
|
|
||||||
const [isPercentState, setIsPercentState] = useState<boolean>(false);
|
|
||||||
const [isDecimalState, setIsDecimalState] = useState<boolean>(false);
|
|
||||||
const [hasMeasuresState, setHasMeasuresState] = useState<boolean>(true);
|
|
||||||
const [llmLoading, setLlmLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const [tagOptions, setTagOptions] = useState<{ label: string; value: string }[]>([]);
|
|
||||||
|
|
||||||
const [metricRelationModalOpenState, setMetricRelationModalOpenState] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const [drillDownDimensions, setDrillDownDimensions] = useState<
|
|
||||||
ISemantic.IDrillDownDimensionItem[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const [drillDownDimensionsConfig, setDrillDownDimensionsConfig] = useState<
|
|
||||||
ISemantic.IDrillDownDimensionItem[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const forward = () => setCurrentStep(currentStep + 1);
|
|
||||||
const backward = () => setCurrentStep(currentStep - 1);
|
|
||||||
|
|
||||||
const queryModelDetail = async () => {
|
|
||||||
// const { code, data } = await getMeasureListByModelId(modelId);
|
|
||||||
const { code, data } = await getModelDetail({ modelId: modelId || metricItem?.modelId });
|
|
||||||
if (code === 200) {
|
|
||||||
if (Array.isArray(data?.modelDetail?.fields)) {
|
|
||||||
setFieldList(data.modelDetail.fields);
|
|
||||||
}
|
|
||||||
if (Array.isArray(data?.modelDetail?.measures)) {
|
|
||||||
setClassMeasureList(data.modelDetail.measures);
|
|
||||||
if (datasourceId) {
|
|
||||||
const hasMeasures = data.some(
|
|
||||||
(item: ISemantic.IMeasure) => item.datasourceId === datasourceId,
|
|
||||||
);
|
|
||||||
setHasMeasuresState(hasMeasures);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setClassMeasureList([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryDrillDownDimension = async (metricId: number) => {
|
|
||||||
const { code, data, msg } = await getDrillDownDimension(metricId);
|
|
||||||
if (code === 200 && Array.isArray(data)) {
|
|
||||||
setDrillDownDimensionsConfig(data);
|
|
||||||
}
|
|
||||||
if (code !== 200) {
|
|
||||||
message.error(msg);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
queryModelDetail();
|
|
||||||
queryMetricsToCreateNewMetric();
|
|
||||||
queryMetricTags();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleNext = async () => {
|
|
||||||
const fieldsValue = await form.validateFields();
|
|
||||||
const submitForm = {
|
|
||||||
...formValRef.current,
|
|
||||||
...fieldsValue,
|
|
||||||
metricDefineType: defineType,
|
|
||||||
[queryParamsTypeParamsKey[defineType]]: exprTypeParamsState[defineType],
|
|
||||||
};
|
|
||||||
updateFormVal(submitForm);
|
|
||||||
if (currentStep < 1) {
|
|
||||||
forward();
|
|
||||||
} else {
|
|
||||||
await saveMetric(submitForm);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
bizName,
|
|
||||||
description,
|
|
||||||
sensitiveLevel,
|
|
||||||
typeParams,
|
|
||||||
dataFormat,
|
|
||||||
dataFormatType,
|
|
||||||
alias,
|
|
||||||
tags,
|
|
||||||
metricDefineType,
|
|
||||||
metricDefineByMeasureParams,
|
|
||||||
metricDefineByMetricParams,
|
|
||||||
metricDefineByFieldParams,
|
|
||||||
} = metricItem;
|
|
||||||
const isPercent = dataFormatType === 'percent';
|
|
||||||
const isDecimal = dataFormatType === 'decimal';
|
|
||||||
const initValue = {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
bizName,
|
|
||||||
sensitiveLevel,
|
|
||||||
description,
|
|
||||||
tags,
|
|
||||||
// isPercent,
|
|
||||||
dataFormatType: dataFormatType || '',
|
|
||||||
alias: alias && alias.trim() ? alias.split(',') : [],
|
|
||||||
dataFormat: dataFormat || {
|
|
||||||
decimalPlaces: 2,
|
|
||||||
needMultiply100: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const editInitFormVal = {
|
|
||||||
...formValRef.current,
|
|
||||||
...initValue,
|
|
||||||
};
|
|
||||||
if (metricDefineType === METRIC_DEFINE_TYPE.MEASURE) {
|
|
||||||
const { measures, expr } = metricDefineByMeasureParams || {};
|
|
||||||
setExprTypeParamsState({
|
|
||||||
...exprTypeParamsState,
|
|
||||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
|
||||||
measures: measures || [],
|
|
||||||
expr: expr || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (metricDefineType === METRIC_DEFINE_TYPE.METRIC) {
|
|
||||||
const { metrics, expr } = metricDefineByMetricParams || {};
|
|
||||||
setExprTypeParamsState({
|
|
||||||
...exprTypeParamsState,
|
|
||||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
|
||||||
metrics: metrics || [],
|
|
||||||
expr: expr || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (metricDefineType === METRIC_DEFINE_TYPE.FIELD) {
|
|
||||||
const { fields, expr } = metricDefineByFieldParams || {};
|
|
||||||
setExprTypeParamsState({
|
|
||||||
...exprTypeParamsState,
|
|
||||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
|
||||||
fields: fields || [],
|
|
||||||
expr: expr || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
updateFormVal(editInitFormVal);
|
|
||||||
form.setFieldsValue(initValue);
|
|
||||||
setDefineType(metricDefineType);
|
|
||||||
setIsPercentState(isPercent);
|
|
||||||
setIsDecimalState(isDecimal);
|
|
||||||
queryDrillDownDimension(metricItem?.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isEdit) {
|
|
||||||
initData();
|
|
||||||
}
|
|
||||||
}, [metricItem]);
|
|
||||||
|
|
||||||
const isEmptyConditions = (
|
|
||||||
metricDefineType: METRIC_DEFINE_TYPE,
|
|
||||||
metricDefineParams:
|
|
||||||
| ISemantic.IMeasureTypeParams
|
|
||||||
| ISemantic.IMetricTypeParams
|
|
||||||
| ISemantic.IFieldTypeParams,
|
|
||||||
) => {
|
|
||||||
if (metricDefineType === METRIC_DEFINE_TYPE.MEASURE) {
|
|
||||||
const { measures } = (metricDefineParams as ISemantic.IMeasureTypeParams) || {};
|
|
||||||
if (!(Array.isArray(measures) && measures.length > 0)) {
|
|
||||||
message.error('请添加一个度量');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (metricDefineType === METRIC_DEFINE_TYPE.METRIC) {
|
|
||||||
const { metrics } = (metricDefineParams as ISemantic.IMetricTypeParams) || {};
|
|
||||||
if (!(Array.isArray(metrics) && metrics.length > 0)) {
|
|
||||||
message.error('请添加一个指标');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (metricDefineType === METRIC_DEFINE_TYPE.FIELD) {
|
|
||||||
const { fields } = (metricDefineParams as ISemantic.IFieldTypeParams) || {};
|
|
||||||
if (!(Array.isArray(fields) && fields.length > 0)) {
|
|
||||||
message.error('请添加一个字段');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveMetric = async (fieldsValue: any) => {
|
|
||||||
const queryParams = {
|
|
||||||
modelId: isEdit ? metricItem.modelId : modelId,
|
|
||||||
relateDimension: {
|
|
||||||
...(metricItem?.relateDimension || {}),
|
|
||||||
drillDownDimensions,
|
|
||||||
},
|
|
||||||
...fieldsValue,
|
|
||||||
};
|
|
||||||
const { alias, dataFormatType } = queryParams;
|
|
||||||
queryParams.alias = Array.isArray(alias) ? alias.join(',') : '';
|
|
||||||
if (!queryParams[queryParamsTypeParamsKey[defineType]]?.expr) {
|
|
||||||
message.error('请输入度量表达式');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!dataFormatType) {
|
|
||||||
delete queryParams.dataFormat;
|
|
||||||
}
|
|
||||||
if (isEmptyConditions(defineType, queryParams[queryParamsTypeParamsKey[defineType]])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let saveMetricQuery = createMetric;
|
|
||||||
if (queryParams.id) {
|
|
||||||
saveMetricQuery = updateMetric;
|
|
||||||
}
|
|
||||||
const { code, msg } = await saveMetricQuery(queryParams);
|
|
||||||
if (code === 200) {
|
|
||||||
message.success('编辑指标成功');
|
|
||||||
onSubmit?.(queryParams);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.error(msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
const generatorMetricAlias = async () => {
|
|
||||||
setLlmLoading(true);
|
|
||||||
const { code, data } = await mockMetricAlias({ ...metricItem });
|
|
||||||
const formAlias = form.getFieldValue('alias');
|
|
||||||
setLlmLoading(false);
|
|
||||||
if (code === 200) {
|
|
||||||
form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data])));
|
|
||||||
} else {
|
|
||||||
message.error('大语言模型解析异常');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryMetricTags = async () => {
|
|
||||||
const { code, data } = await getMetricTags();
|
|
||||||
if (code === 200) {
|
|
||||||
// form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data])));
|
|
||||||
setTagOptions(
|
|
||||||
Array.isArray(data)
|
|
||||||
? data.map((tag: string) => {
|
|
||||||
return { label: tag, value: tag };
|
|
||||||
})
|
|
||||||
: [],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
message.error('获取指标标签失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const queryMetricsToCreateNewMetric = async () => {
|
|
||||||
const { code, data } = await getMetricsToCreateNewMetric({
|
|
||||||
modelId: modelId || metricItem?.modelId,
|
|
||||||
});
|
|
||||||
if (code === 200) {
|
|
||||||
setCreateNewMetricList(data);
|
|
||||||
} else {
|
|
||||||
message.error('获取指标标签失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = () => {
|
|
||||||
if (currentStep === 1) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: '0 0 20px 24px',
|
|
||||||
// borderBottom: '1px solid #eee',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Radio.Group
|
|
||||||
buttonStyle="solid"
|
|
||||||
value={defineType}
|
|
||||||
onChange={(e) => {
|
|
||||||
setDefineType(e.target.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Radio.Button value={METRIC_DEFINE_TYPE.MEASURE}>按度量</Radio.Button>
|
|
||||||
<Radio.Button value={METRIC_DEFINE_TYPE.METRIC}>按指标</Radio.Button>
|
|
||||||
<Radio.Button value={METRIC_DEFINE_TYPE.FIELD}>按字段</Radio.Button>
|
|
||||||
</Radio.Group>
|
|
||||||
</div>
|
|
||||||
{defineType === METRIC_DEFINE_TYPE.MEASURE && (
|
|
||||||
<>
|
|
||||||
<MetricMeasuresFormTable
|
|
||||||
datasourceId={datasourceId}
|
|
||||||
typeParams={exprTypeParamsState[METRIC_DEFINE_TYPE.MEASURE]}
|
|
||||||
measuresList={classMeasureList}
|
|
||||||
onFieldChange={(measures: ISemantic.IMeasure[]) => {
|
|
||||||
setExprTypeParamsState((prevState) => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
|
||||||
...prevState[METRIC_DEFINE_TYPE.MEASURE],
|
|
||||||
measures,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onSqlChange={(expr: string) => {
|
|
||||||
setExprTypeParamsState((prevState) => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
|
||||||
...prevState[METRIC_DEFINE_TYPE.MEASURE],
|
|
||||||
expr,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{defineType === METRIC_DEFINE_TYPE.METRIC && (
|
|
||||||
<>
|
|
||||||
<p className={styles.desc}>
|
|
||||||
通过
|
|
||||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
|
||||||
字段
|
|
||||||
</Tag>
|
|
||||||
和
|
|
||||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
|
||||||
度量
|
|
||||||
</Tag>
|
|
||||||
创建的指标可用来创建新的指标
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<MetricMetricFormTable
|
|
||||||
typeParams={exprTypeParamsState[METRIC_DEFINE_TYPE.METRIC]}
|
|
||||||
metricList={createNewMetricList}
|
|
||||||
onFieldChange={(metrics: ISemantic.IMetricTypeParamsItem[]) => {
|
|
||||||
setExprTypeParamsState((prevState) => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
|
||||||
...prevState[METRIC_DEFINE_TYPE.METRIC],
|
|
||||||
metrics,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onSqlChange={(expr: string) => {
|
|
||||||
setExprTypeParamsState((prevState) => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
|
||||||
...prevState[METRIC_DEFINE_TYPE.METRIC],
|
|
||||||
expr,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{defineType === METRIC_DEFINE_TYPE.FIELD && (
|
|
||||||
<>
|
|
||||||
<MetricFieldFormTable
|
|
||||||
typeParams={exprTypeParamsState[METRIC_DEFINE_TYPE.FIELD]}
|
|
||||||
fieldList={fieldList}
|
|
||||||
onFieldChange={(fields: ISemantic.IFieldTypeParamsItem[]) => {
|
|
||||||
setExprTypeParamsState((prevState) => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
|
||||||
...prevState[METRIC_DEFINE_TYPE.FIELD],
|
|
||||||
fields,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onSqlChange={(expr: string) => {
|
|
||||||
setExprTypeParamsState((prevState) => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
|
||||||
...prevState[METRIC_DEFINE_TYPE.FIELD],
|
|
||||||
expr,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<FormItem hidden={true} name="id" label="ID">
|
|
||||||
<Input placeholder="id" />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
name="name"
|
|
||||||
label="指标名称"
|
|
||||||
rules={[{ required: true, message: '请输入指标名称' }]}
|
|
||||||
>
|
|
||||||
<Input placeholder="名称不可重复" />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
name="bizName"
|
|
||||||
label="英文名称"
|
|
||||||
rules={[{ required: true, message: '请输入英文名称' }]}
|
|
||||||
>
|
|
||||||
<Input placeholder="名称不可重复" disabled={isEdit} />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="别名">
|
|
||||||
<Row>
|
|
||||||
<Col flex="1 1 200px">
|
|
||||||
<FormItem name="alias" noStyle>
|
|
||||||
<Select
|
|
||||||
mode="tags"
|
|
||||||
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
|
|
||||||
tokenSeparators={[',']}
|
|
||||||
maxTagCount={9}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</Col>
|
|
||||||
{isEdit && (
|
|
||||||
<Col flex="0 1 75px">
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
loading={llmLoading}
|
|
||||||
size="small"
|
|
||||||
style={{ top: '2px' }}
|
|
||||||
onClick={() => {
|
|
||||||
generatorMetricAlias();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Space>
|
|
||||||
智能填充
|
|
||||||
<Tooltip title="智能填充将根据指标相关信息,使用大语言模型获取指标别名">
|
|
||||||
<InfoCircleOutlined />
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem name="tags" label="标签">
|
|
||||||
<Select
|
|
||||||
mode="tags"
|
|
||||||
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
|
|
||||||
tokenSeparators={[',']}
|
|
||||||
maxTagCount={9}
|
|
||||||
options={tagOptions}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
name="sensitiveLevel"
|
|
||||||
label="敏感度"
|
|
||||||
rules={[{ required: true, message: '请选择敏感度' }]}
|
|
||||||
>
|
|
||||||
<Select placeholder="请选择敏感度">
|
|
||||||
{SENSITIVE_LEVEL_OPTIONS.map((item) => (
|
|
||||||
<Option key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
name="description"
|
|
||||||
label={
|
|
||||||
<TableTitleTooltips
|
|
||||||
title="业务口径"
|
|
||||||
overlayInnerStyle={{ width: 600 }}
|
|
||||||
tooltips={
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
在录入指标时,请务必详细填写指标口径。口径描述对于理解指标的含义、计算方法和使用场景至关重要。一个清晰、准确的口径描述可以帮助其他用户更好地理解和使用该指标,避免因为误解而导致错误的数据分析和决策。在填写口径时,建议包括以下信息:
|
|
||||||
</p>
|
|
||||||
<p>1. 指标的计算方法:详细说明指标是如何计算的,包括涉及的公式、计算步骤等。</p>
|
|
||||||
<p>2. 数据来源:描述指标所依赖的数据来源,包括数据表、字段等信息。</p>
|
|
||||||
<p>3. 使用场景:说明该指标适用于哪些业务场景,以及如何在这些场景中使用该指标。</p>
|
|
||||||
<p>4. 任何其他相关信息:例如数据更新频率、数据质量要求等。</p>
|
|
||||||
<p>
|
|
||||||
请确保口径描述清晰、简洁且易于理解,以便其他用户能够快速掌握指标的核心要点。
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
rules={[{ required: true, message: '请输入业务口径' }]}
|
|
||||||
>
|
|
||||||
<TextArea placeholder="请输入业务口径" />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={
|
|
||||||
<FormItemTitle
|
|
||||||
title={'下钻维度配置'}
|
|
||||||
subTitle={'配置下钻维度后,将可以在指标卡中进行下钻'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
setMetricRelationModalOpenState(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
设 置
|
|
||||||
</Button>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={
|
|
||||||
<FormItemTitle
|
|
||||||
title={'数据格式化'}
|
|
||||||
// subTitle={'开启后,指标数据展示时会根据配置进行格式化,如0.02 -> 2%'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
name="dataFormatType"
|
|
||||||
>
|
|
||||||
<Radio.Group buttonStyle="solid" size="middle">
|
|
||||||
<Radio.Button value="">默认</Radio.Button>
|
|
||||||
<Radio.Button value="decimal">小数</Radio.Button>
|
|
||||||
<Radio.Button value="percent">百分比</Radio.Button>
|
|
||||||
</Radio.Group>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
{(isPercentState || isDecimalState) && (
|
|
||||||
<FormItem
|
|
||||||
label={
|
|
||||||
<FormItemTitle
|
|
||||||
title={'小数位数'}
|
|
||||||
subTitle={`对小数位数进行设置,如保留两位,0.021252 -> 0.02${
|
|
||||||
isPercentState ? '%' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
name={['dataFormat', 'decimalPlaces']}
|
|
||||||
>
|
|
||||||
<InputNumber placeholder="请输入需要保留小数位数" style={{ width: '300px' }} />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
{isPercentState && (
|
|
||||||
<>
|
|
||||||
<FormItem
|
|
||||||
label={
|
|
||||||
<FormItemTitle
|
|
||||||
title={'原始值是否乘以100'}
|
|
||||||
subTitle={'如 原始值0.001 ->展示值0.1% '}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
name={['dataFormat', 'needMultiply100']}
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</FormItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const renderFooter = () => {
|
|
||||||
if (!hasMeasuresState) {
|
|
||||||
return <Button onClick={onCancel}>取消</Button>;
|
|
||||||
}
|
|
||||||
if (currentStep === 1) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button style={{ float: 'left' }} onClick={backward}>
|
|
||||||
上一步
|
|
||||||
</Button>
|
|
||||||
<Button onClick={onCancel}>取消</Button>
|
|
||||||
<Button type="primary" onClick={handleNext}>
|
|
||||||
完成
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button onClick={onCancel}>取消</Button>
|
|
||||||
<Button type="primary" onClick={handleNext}>
|
|
||||||
下一步
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
forceRender
|
|
||||||
width={800}
|
|
||||||
style={{ top: 48 }}
|
|
||||||
// styles={{ padding: '32px 40px 48px' }}
|
|
||||||
destroyOnClose
|
|
||||||
title={`${isEdit ? '编辑' : '新建'}指标`}
|
|
||||||
maskClosable={false}
|
|
||||||
open={createModalVisible}
|
|
||||||
footer={renderFooter()}
|
|
||||||
onCancel={onCancel}
|
|
||||||
>
|
|
||||||
{hasMeasuresState ? (
|
|
||||||
<>
|
|
||||||
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
|
|
||||||
<Step title="基本信息" />
|
|
||||||
<Step title="度量信息" />
|
|
||||||
</Steps>
|
|
||||||
<Form
|
|
||||||
{...formLayout}
|
|
||||||
form={form}
|
|
||||||
initialValues={{
|
|
||||||
...formValRef.current,
|
|
||||||
dataFormatType: '',
|
|
||||||
}}
|
|
||||||
onValuesChange={(value, values: any) => {
|
|
||||||
const { isPercent, dataFormatType } = values;
|
|
||||||
// if (isPercent !== undefined) {
|
|
||||||
// setIsPercentState(isPercent);
|
|
||||||
// }
|
|
||||||
if (dataFormatType === 'percent') {
|
|
||||||
setIsPercentState(true);
|
|
||||||
setIsDecimalState(false);
|
|
||||||
}
|
|
||||||
if (dataFormatType === 'decimal') {
|
|
||||||
setIsPercentState(false);
|
|
||||||
setIsDecimalState(true);
|
|
||||||
}
|
|
||||||
if (!dataFormatType) {
|
|
||||||
setIsPercentState(false);
|
|
||||||
setIsDecimalState(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={styles.form}
|
|
||||||
>
|
|
||||||
{renderContent()}
|
|
||||||
</Form>
|
|
||||||
<DimensionAndMetricRelationModal
|
|
||||||
metricItem={metricItem}
|
|
||||||
relationsInitialValue={drillDownDimensionsConfig}
|
|
||||||
open={metricRelationModalOpenState}
|
|
||||||
onCancel={() => {
|
|
||||||
setMetricRelationModalOpenState(false);
|
|
||||||
}}
|
|
||||||
onSubmit={(relations) => {
|
|
||||||
setDrillDownDimensions(relations);
|
|
||||||
setMetricRelationModalOpenState(false);
|
|
||||||
}}
|
|
||||||
onRefreshRelationData={() => {
|
|
||||||
queryDrillDownDimension(metricItem?.id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Result
|
|
||||||
status="warning"
|
|
||||||
subTitle="当前数据源缺少度量,无法创建指标。请前往数据源配置中,将字段设置为度量"
|
|
||||||
extra={
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
key="console"
|
|
||||||
onClick={() => {
|
|
||||||
history.replace(`/model/${domainId}/${modelId || metricItem?.modelId}/dataSource`);
|
|
||||||
onCancel?.();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
去创建
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MetricInfoCreateForm;
|
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Form, Button, Modal, Input, Select, Steps } from 'antd';
|
import { Form, Button, Modal, Input, Select, Steps, Tabs, Space } from 'antd';
|
||||||
import styles from '../../components/style.less';
|
import styles from '../../components/style.less';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { formLayout } from '@/components/FormHelper/utils';
|
import { formLayout } from '@/components/FormHelper/utils';
|
||||||
import { createView, updateView } from '../../service';
|
import { createView, updateView, getDimensionList, queryMetric } from '../../service';
|
||||||
import { ISemantic } from '../../data';
|
import { ISemantic } from '../../data';
|
||||||
import { isString } from 'lodash';
|
import { isString } from 'lodash';
|
||||||
import ViewModelConfigTable from './ViewModelConfigTable';
|
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||||
|
import SelectPartner from '@/components/SelectPartner';
|
||||||
|
import SelectTMEPerson from '@/components/SelectTMEPerson';
|
||||||
import ViewModelConfigTransfer from './ViewModelConfigTransfer';
|
import ViewModelConfigTransfer from './ViewModelConfigTransfer';
|
||||||
|
import DefaultSettingForm from './DefaultSettingForm';
|
||||||
|
import SqlEditor from '@/components/SqlEditor';
|
||||||
|
import ProCard from '@ant-design/pro-card';
|
||||||
|
import { ChatConfigType } from '../../enum';
|
||||||
|
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
@@ -33,7 +39,6 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
currentModel: modelList[0]?.id,
|
currentModel: modelList[0]?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [submitData, setSubmitData] = useState({});
|
|
||||||
const [saveLoading, setSaveLoading] = useState<boolean>(false);
|
const [saveLoading, setSaveLoading] = useState<boolean>(false);
|
||||||
const [modalWidth, setModalWidth] = useState<number>(800);
|
const [modalWidth, setModalWidth] = useState<number>(800);
|
||||||
const [selectedModelItem, setSelectedModelItem] = useState<ISemantic.IModelItem | undefined>(
|
const [selectedModelItem, setSelectedModelItem] = useState<ISemantic.IModelItem | undefined>(
|
||||||
@@ -48,13 +53,41 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
});
|
});
|
||||||
}, [viewItem]);
|
}, [viewItem]);
|
||||||
|
|
||||||
|
const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>();
|
||||||
|
const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedModelItem?.id) {
|
||||||
|
queryDimensionList(selectedModelItem.id);
|
||||||
|
queryMetricList(selectedModelItem.id);
|
||||||
|
}
|
||||||
|
}, [selectedModelItem]);
|
||||||
|
|
||||||
|
const queryDimensionList = async (modelId) => {
|
||||||
|
const { code, data, msg } = await getDimensionList({ modelId });
|
||||||
|
if (code === 200 && Array.isArray(data?.list)) {
|
||||||
|
setDimensionList(data.list);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryMetricList = async (modelId) => {
|
||||||
|
const { code, data, msg } = await queryMetric({ modelId });
|
||||||
|
if (code === 200 && Array.isArray(data?.list)) {
|
||||||
|
setMetricList(data.list);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
const fieldsValue = await form.validateFields();
|
const fieldsValue = await form.validateFields();
|
||||||
const viewModelConfigsMap = configTableRef?.current.getViewModelConfigs() || {};
|
const viewModelConfigsMap = configTableRef?.current.getViewModelConfigs() || {};
|
||||||
|
|
||||||
const queryData: ISemantic.IModelItem = {
|
const queryData: ISemantic.IModelItem = {
|
||||||
...formVals,
|
...formVals,
|
||||||
...fieldsValue,
|
...fieldsValue,
|
||||||
...submitData,
|
|
||||||
viewDetail: {
|
viewDetail: {
|
||||||
viewModelConfigs: Object.values(viewModelConfigsMap),
|
viewModelConfigs: Object.values(viewModelConfigsMap),
|
||||||
},
|
},
|
||||||
@@ -71,36 +104,24 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const footer = (
|
const stepWidth = {
|
||||||
<>
|
'0': 800,
|
||||||
<Button onClick={onCancel}>取消</Button>
|
'1': 1200,
|
||||||
<Button type="primary" loading={saveLoading} onClick={handleConfirm}>
|
'2': 800,
|
||||||
确定
|
};
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const forward = () => {
|
const forward = () => {
|
||||||
setModalWidth(1200);
|
setModalWidth(stepWidth[`${currentStep + 1}`]);
|
||||||
setCurrentStep(currentStep + 1);
|
setCurrentStep(currentStep + 1);
|
||||||
};
|
};
|
||||||
const backward = () => {
|
const backward = () => {
|
||||||
setModalWidth(800);
|
setModalWidth(stepWidth[`${currentStep - 1}`]);
|
||||||
setCurrentStep(currentStep - 1);
|
setCurrentStep(currentStep - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNext = async () => {
|
const handleNext = async () => {
|
||||||
const fieldsValue = await form.validateFields();
|
await form.validateFields();
|
||||||
const submitForm = {
|
forward();
|
||||||
...submitData,
|
|
||||||
...fieldsValue,
|
|
||||||
};
|
|
||||||
setSubmitData(submitForm);
|
|
||||||
if (currentStep < 1) {
|
|
||||||
forward();
|
|
||||||
} else {
|
|
||||||
// await saveMetric(submitForm);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderFooter = () => {
|
const renderFooter = () => {
|
||||||
@@ -110,34 +131,60 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
<Button style={{ float: 'left' }} onClick={backward}>
|
<Button style={{ float: 'left' }} onClick={backward}>
|
||||||
上一步
|
上一步
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={onCancel}>取消</Button>
|
<Button type="primary" onClick={handleNext}>
|
||||||
{/* <Button type="primary" onClick={handleNext}> */}
|
下一步
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleConfirm();
|
handleConfirm();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
完成
|
保 存
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// if (currentStep === 2) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <Button style={{ float: 'left' }} onClick={backward}>
|
||||||
|
// 上一步
|
||||||
|
// </Button>
|
||||||
|
// <Button
|
||||||
|
// type="primary"
|
||||||
|
// onClick={() => {
|
||||||
|
// handleConfirm();
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// 保 存
|
||||||
|
// </Button>
|
||||||
|
// </>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={onCancel}>取消</Button>
|
<Button onClick={onCancel}>取消</Button>
|
||||||
<Button type="primary" onClick={handleNext}>
|
<Button type="primary" onClick={handleNext}>
|
||||||
下一步
|
下一步
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
handleConfirm();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
保 存
|
||||||
|
</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
if (currentStep === 1) {
|
return (
|
||||||
return (
|
<>
|
||||||
<div>
|
<div style={{ display: currentStep === 1 ? 'block' : 'none' }}>
|
||||||
<FormItem
|
{/* <FormItem
|
||||||
name="currentModel"
|
name="currentModel"
|
||||||
label="选择模型"
|
label="选择模型"
|
||||||
rules={[{ required: true, message: '请选择模型!' }]}
|
rules={[{ required: true, message: '请选择模型!' }]}
|
||||||
@@ -152,54 +199,156 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
return { label: item.name, value: item.id };
|
return { label: item.name, value: item.id };
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem> */}
|
||||||
<ViewModelConfigTransfer
|
<ViewModelConfigTransfer
|
||||||
|
toolbarSolt={
|
||||||
|
<Space>
|
||||||
|
<span>切换模型: </span>
|
||||||
|
<Select
|
||||||
|
value={selectedModelItem?.id}
|
||||||
|
placeholder="请选择模型,获取当前模型下指标维度信息"
|
||||||
|
onChange={(val) => {
|
||||||
|
setDimensionList(undefined);
|
||||||
|
setMetricList(undefined);
|
||||||
|
const modelItem = modelList.find((item) => item.id === val);
|
||||||
|
setSelectedModelItem(modelItem);
|
||||||
|
}}
|
||||||
|
options={modelList.map((item) => {
|
||||||
|
return { label: item.name, value: item.id };
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
dimensionList={dimensionList}
|
||||||
|
metricList={metricList}
|
||||||
modelItem={selectedModelItem}
|
modelItem={selectedModelItem}
|
||||||
viewItem={viewItem}
|
viewItem={viewItem}
|
||||||
ref={configTableRef}
|
ref={configTableRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
<div style={{ display: currentStep === 2 ? 'block' : 'none' }}>
|
||||||
<>
|
<Tabs
|
||||||
<FormItem
|
items={[
|
||||||
name="name"
|
// {
|
||||||
label="视图名称"
|
// label: 'SQL过滤',
|
||||||
rules={[{ required: true, message: '请输入视图名称!' }]}
|
// key: 'sqlFilter',
|
||||||
>
|
// children: (
|
||||||
<Input placeholder="视图名称不可重复" />
|
// <>
|
||||||
</FormItem>
|
// <FormItem
|
||||||
<FormItem
|
// name="filterSql"
|
||||||
name="bizName"
|
// label={<span style={{ fontSize: 14 }}>SQL</span>}
|
||||||
label="视图英文名称"
|
// tooltip="主要用于词典导入场景, 对维度值进行过滤 格式: field1 = 'xxx' and field2 = 'yyy'"
|
||||||
rules={[{ required: true, message: '请输入视图英文名称!' }]}
|
// >
|
||||||
>
|
// <SqlEditor height={'150px'} />
|
||||||
<Input placeholder="请输入视图英文名称" />
|
// </FormItem>
|
||||||
</FormItem>
|
// </>
|
||||||
<FormItem
|
// ),
|
||||||
name="alias"
|
// },
|
||||||
label="别名"
|
{
|
||||||
getValueFromEvent={(value) => {
|
label: '权限设置',
|
||||||
return Array.isArray(value) ? value.join(',') : '';
|
key: 'permissionSetting',
|
||||||
}}
|
children: (
|
||||||
getValueProps={(value) => {
|
<>
|
||||||
return {
|
<FormItem
|
||||||
value: isString(value) ? value.split(',') : [],
|
name="admins"
|
||||||
};
|
label={
|
||||||
}}
|
<FormItemTitle
|
||||||
>
|
title={'管理员'}
|
||||||
<Select
|
subTitle={'管理员将拥有主题域下所有编辑及访问权限'}
|
||||||
mode="tags"
|
/>
|
||||||
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
|
}
|
||||||
tokenSeparators={[',']}
|
>
|
||||||
maxTagCount={9}
|
<SelectTMEPerson placeholder="请邀请团队成员" />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="adminOrgs" label="按组织">
|
||||||
|
<SelectPartner
|
||||||
|
type="selectedDepartment"
|
||||||
|
treeSelectProps={{
|
||||||
|
placeholder: '请选择需要授权的部门',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
{/* <FormItem
|
||||||
|
style={{ marginTop: 40, marginBottom: 60 }}
|
||||||
|
name="filterSql"
|
||||||
|
label={<span style={{ fontSize: 14 }}>过滤SQL</span>}
|
||||||
|
tooltip="主要用于词典导入场景, 对维度值进行过滤 格式: field1 = 'xxx' and field2 = 'yyy'"
|
||||||
|
>
|
||||||
|
<SqlEditor height={'150px'} />
|
||||||
|
</FormItem> */}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '问答设置',
|
||||||
|
key: 'chatSetting',
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<ProCard bordered title="指标模式" style={{ marginBottom: 20 }}>
|
||||||
|
<DefaultSettingForm
|
||||||
|
form={form}
|
||||||
|
dimensionList={dimensionList}
|
||||||
|
metricList={metricList}
|
||||||
|
chatConfigType={ChatConfigType.METRIC}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
<ProCard bordered title="标签模式" style={{ marginBottom: 20 }}>
|
||||||
|
<DefaultSettingForm
|
||||||
|
form={form}
|
||||||
|
dimensionList={dimensionList}
|
||||||
|
metricList={metricList}
|
||||||
|
chatConfigType={ChatConfigType.TAG}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</div>
|
||||||
<FormItem name="description" label="视图描述">
|
|
||||||
<Input.TextArea placeholder="视图描述" />
|
<div style={{ display: currentStep === 0 ? 'block' : 'none' }}>
|
||||||
</FormItem>
|
<FormItem
|
||||||
|
name="name"
|
||||||
|
label="视图名称"
|
||||||
|
rules={[{ required: true, message: '请输入视图名称!' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="视图名称不可重复" />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
name="bizName"
|
||||||
|
label="视图英文名称"
|
||||||
|
rules={[{ required: true, message: '请输入视图英文名称!' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="请输入视图英文名称" />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
name="alias"
|
||||||
|
label="别名"
|
||||||
|
getValueFromEvent={(value) => {
|
||||||
|
return Array.isArray(value) ? value.join(',') : '';
|
||||||
|
}}
|
||||||
|
getValueProps={(value) => {
|
||||||
|
return {
|
||||||
|
value: isString(value) ? value.split(',') : [],
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
mode="tags"
|
||||||
|
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
|
||||||
|
tokenSeparators={[',']}
|
||||||
|
maxTagCount={9}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="admins" label={<FormItemTitle title={'责任人'} />}>
|
||||||
|
<SelectTMEPerson placeholder="请邀请团队成员" />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="description" label="视图描述">
|
||||||
|
<Input.TextArea placeholder="视图描述" />
|
||||||
|
</FormItem>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -210,13 +359,14 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
|
|||||||
destroyOnClose
|
destroyOnClose
|
||||||
title={'视图信息'}
|
title={'视图信息'}
|
||||||
open={true}
|
open={true}
|
||||||
// footer={footer}
|
maskClosable={false}
|
||||||
footer={renderFooter()}
|
footer={renderFooter()}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
>
|
>
|
||||||
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
|
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
|
||||||
<Step title="基本信息" />
|
<Step title="基本信息" />
|
||||||
<Step title="关联信息" />
|
<Step title="关联信息" />
|
||||||
|
{/* <Step title="进阶设置" /> */}
|
||||||
</Steps>
|
</Steps>
|
||||||
<Form
|
<Form
|
||||||
{...formLayout}
|
{...formLayout}
|
||||||
|
|||||||
@@ -1,30 +1,28 @@
|
|||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
// import { InfoCircleOutlined } from '@ant-design/icons';
|
|
||||||
import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
||||||
import type { Ref } from 'react';
|
import type { ReactNode, Ref } from 'react';
|
||||||
import DimensionMetricTransferModal from './DimensionMetricTransferModal';
|
import DimensionMetricTransferModal from './DimensionMetricTransferModal';
|
||||||
// import styles from '../../components/style.less';
|
|
||||||
import { TransType } from '../../enum';
|
import { TransType } from '../../enum';
|
||||||
import { getDimensionList, queryMetric } from '../../service';
|
import { getDimensionList, queryMetric } from '../../service';
|
||||||
import { wrapperTransTypeAndId } from '../../components/Entity/utils';
|
import { wrapperTransTypeAndId } from '../../utils';
|
||||||
import { ISemantic } from '../../data';
|
import { ISemantic } from '../../data';
|
||||||
import { isArrayOfValues } from '@/utils/utils';
|
import { isArrayOfValues } from '@/utils/utils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
// modelList: ISemantic.IModelItem[];
|
|
||||||
viewItem: ISemantic.IViewItem;
|
viewItem: ISemantic.IViewItem;
|
||||||
modelItem?: ISemantic.IModelItem;
|
modelItem?: ISemantic.IModelItem;
|
||||||
[key: string]: any;
|
dimensionList?: ISemantic.IDimensionItem[];
|
||||||
|
metricList?: ISemantic.IMetricItem[];
|
||||||
|
toolbarSolt?: ReactNode;
|
||||||
};
|
};
|
||||||
const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
|
const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
|
||||||
({ viewItem, modelItem }: Props, ref: Ref<any>) => {
|
({ viewItem, modelItem, dimensionList, metricList, toolbarSolt }: Props, ref: Ref<any>) => {
|
||||||
// const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
|
||||||
const [selectedTransferKeys, setSelectedTransferKeys] = useState<React.Key[]>([]);
|
const [selectedTransferKeys, setSelectedTransferKeys] = useState<React.Key[]>([]);
|
||||||
|
|
||||||
const [viewModelConfigsMap, setViewModelConfigsMap] = useState({});
|
const [viewModelConfigsMap, setViewModelConfigsMap] = useState({});
|
||||||
|
|
||||||
const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>([]);
|
const [mergeDimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>();
|
||||||
const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>([]);
|
const [mergeMetricList, setMetricList] = useState<ISemantic.IMetricItem[]>();
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
getViewModelConfigs: () => {
|
getViewModelConfigs: () => {
|
||||||
@@ -34,44 +32,24 @@ const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
|
|||||||
|
|
||||||
const queryDimensionListByIds = async (ids: number[]) => {
|
const queryDimensionListByIds = async (ids: number[]) => {
|
||||||
if (!isArrayOfValues(ids)) {
|
if (!isArrayOfValues(ids)) {
|
||||||
queryDimensionList([]);
|
setDimensionList(dimensionList);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { code, data, msg } = await getDimensionList({ ids });
|
const { code, data, msg } = await getDimensionList({ ids });
|
||||||
if (code === 200 && Array.isArray(data?.list)) {
|
if (code === 200 && Array.isArray(data?.list)) {
|
||||||
queryDimensionList(data.list);
|
const mergeList = data?.list.reduce(
|
||||||
} else {
|
|
||||||
message.error(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryMetricListByIds = async (ids: number[]) => {
|
|
||||||
if (!isArrayOfValues(ids)) {
|
|
||||||
queryMetricList([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { code, data, msg } = await queryMetric({ ids });
|
|
||||||
if (code === 200 && Array.isArray(data?.list)) {
|
|
||||||
queryMetricList(data.list);
|
|
||||||
} else {
|
|
||||||
message.error(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryDimensionList = async (selectedDimensionList: ISemantic.IDimensionItem[]) => {
|
|
||||||
const { code, data, msg } = await getDimensionList({ modelId: modelItem?.id });
|
|
||||||
if (code === 200 && Array.isArray(data?.list)) {
|
|
||||||
const mergeList = selectedDimensionList.reduce(
|
|
||||||
(modelDimensionList: ISemantic.IDimensionItem[], item) => {
|
(modelDimensionList: ISemantic.IDimensionItem[], item) => {
|
||||||
const hasItem = data.list.find((dataListItem: ISemantic.IDimensionItem) => {
|
const hasItem = Array.isArray(dimensionList)
|
||||||
return dataListItem.id === item.id;
|
? dimensionList.find((dataListItem: ISemantic.IDimensionItem) => {
|
||||||
});
|
return dataListItem.id === item.id;
|
||||||
|
})
|
||||||
|
: [];
|
||||||
if (!hasItem) {
|
if (!hasItem) {
|
||||||
return [item, ...modelDimensionList];
|
return [item, ...modelDimensionList];
|
||||||
}
|
}
|
||||||
return modelDimensionList;
|
return modelDimensionList;
|
||||||
},
|
},
|
||||||
data.list,
|
dimensionList,
|
||||||
);
|
);
|
||||||
setDimensionList(mergeList);
|
setDimensionList(mergeList);
|
||||||
} else {
|
} else {
|
||||||
@@ -79,21 +57,24 @@ const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryMetricList = async (selectedMetricList: ISemantic.IMetricItem[]) => {
|
const queryMetricListByIds = async (ids: number[]) => {
|
||||||
const { code, data, msg } = await queryMetric({ modelId: modelItem?.id });
|
if (!isArrayOfValues(ids)) {
|
||||||
|
setMetricList(metricList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { code, data, msg } = await queryMetric({ ids });
|
||||||
if (code === 200 && Array.isArray(data?.list)) {
|
if (code === 200 && Array.isArray(data?.list)) {
|
||||||
const mergeList = selectedMetricList.reduce(
|
const mergeList = data.list.reduce((modelMetricList: ISemantic.IMetricItem[], item) => {
|
||||||
(modelMetricList: ISemantic.IMetricItem[], item) => {
|
const hasItem = Array.isArray(metricList)
|
||||||
const hasItem = data.list.find((dataListItem: ISemantic.IMetricItem) => {
|
? metricList.find((dataListItem: ISemantic.IMetricItem) => {
|
||||||
return dataListItem.id === item.id;
|
return dataListItem.id === item.id;
|
||||||
});
|
})
|
||||||
if (!hasItem) {
|
: [];
|
||||||
return [item, ...modelMetricList];
|
if (!hasItem) {
|
||||||
}
|
return [item, ...modelMetricList];
|
||||||
return modelMetricList;
|
}
|
||||||
},
|
return modelMetricList;
|
||||||
data.list,
|
}, metricList);
|
||||||
);
|
|
||||||
setMetricList(mergeList);
|
setMetricList(mergeList);
|
||||||
} else {
|
} else {
|
||||||
message.error(msg);
|
message.error(msg);
|
||||||
@@ -126,12 +107,14 @@ const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
setSelectedTransferKeys(transferKeys);
|
setSelectedTransferKeys(transferKeys);
|
||||||
// setSelectedRowKeys(idList);
|
|
||||||
setViewModelConfigsMap(viewConfigMap);
|
setViewModelConfigsMap(viewConfigMap);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!dimensionList || !metricList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const viewModelConfigs = isArrayOfValues(Object.values(viewModelConfigsMap))
|
const viewModelConfigs = isArrayOfValues(Object.values(viewModelConfigsMap))
|
||||||
? (Object.values(viewModelConfigsMap) as ISemantic.IViewModelConfigItem[])
|
? (Object.values(viewModelConfigsMap) as ISemantic.IViewModelConfigItem[])
|
||||||
: viewItem?.viewDetail?.viewModelConfigs;
|
: viewItem?.viewDetail?.viewModelConfigs;
|
||||||
@@ -146,17 +129,18 @@ const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
|
|||||||
queryDimensionListByIds(allDimensions);
|
queryDimensionListByIds(allDimensions);
|
||||||
queryMetricListByIds(allMetrics);
|
queryMetricListByIds(allMetrics);
|
||||||
} else {
|
} else {
|
||||||
queryDimensionList([]);
|
setDimensionList(dimensionList);
|
||||||
queryMetricList([]);
|
setMetricList(metricList);
|
||||||
}
|
}
|
||||||
}, [modelItem]);
|
}, [modelItem, dimensionList, metricList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DimensionMetricTransferModal
|
<DimensionMetricTransferModal
|
||||||
|
toolbarSolt={toolbarSolt}
|
||||||
modelId={modelItem?.id}
|
modelId={modelItem?.id}
|
||||||
dimensionList={dimensionList}
|
dimensionList={mergeDimensionList}
|
||||||
metricList={metricList}
|
metricList={mergeMetricList}
|
||||||
selectedTransferKeys={selectedTransferKeys}
|
selectedTransferKeys={selectedTransferKeys}
|
||||||
onSubmit={(
|
onSubmit={(
|
||||||
submitData: Record<string, ISemantic.IViewModelConfigItem>,
|
submitData: Record<string, ISemantic.IViewModelConfigItem>,
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import { Form, Button, Modal, Input } from 'antd';
|
||||||
|
import styles from '../style.less';
|
||||||
|
import { message } from 'antd';
|
||||||
|
import { formLayout } from '@/components/FormHelper/utils';
|
||||||
|
import { createView, updateView, getDimensionList, queryMetric } from '../../service';
|
||||||
|
import { ISemantic } from '../../data';
|
||||||
|
import DefaultSettingForm from './DefaultSettingForm';
|
||||||
|
import { isArrayOfValues } from '@/utils/utils';
|
||||||
|
import ProCard from '@ant-design/pro-card';
|
||||||
|
import { ChatConfigType } from '../../enum';
|
||||||
|
|
||||||
|
export type ModelCreateFormModalProps = {
|
||||||
|
domainId: number;
|
||||||
|
viewItem: any;
|
||||||
|
modelList: ISemantic.IModelItem[];
|
||||||
|
onCancel: () => void;
|
||||||
|
onSubmit: (values: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ViewSearchFormModal: React.FC<ModelCreateFormModalProps> = ({
|
||||||
|
viewItem,
|
||||||
|
domainId,
|
||||||
|
onCancel,
|
||||||
|
onSubmit,
|
||||||
|
}) => {
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const [saveLoading, setSaveLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>();
|
||||||
|
const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const viewModelConfigs = viewItem?.viewDetail?.viewModelConfigs;
|
||||||
|
if (Array.isArray(viewModelConfigs)) {
|
||||||
|
const allMetrics: number[] = [];
|
||||||
|
const allDimensions: number[] = [];
|
||||||
|
viewModelConfigs.forEach((item: ISemantic.IViewModelConfigItem) => {
|
||||||
|
const { metrics, dimensions } = item;
|
||||||
|
allMetrics.push(...metrics);
|
||||||
|
allDimensions.push(...dimensions);
|
||||||
|
});
|
||||||
|
queryDimensionListByIds(allDimensions);
|
||||||
|
queryMetricListByIds(allMetrics);
|
||||||
|
}
|
||||||
|
}, [viewItem]);
|
||||||
|
|
||||||
|
const queryDimensionListByIds = async (ids: number[]) => {
|
||||||
|
if (!isArrayOfValues(ids)) {
|
||||||
|
setDimensionList([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { code, data, msg } = await getDimensionList({ ids });
|
||||||
|
if (code === 200 && Array.isArray(data?.list)) {
|
||||||
|
setDimensionList(data.list);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryMetricListByIds = async (ids: number[]) => {
|
||||||
|
if (!isArrayOfValues(ids)) {
|
||||||
|
setMetricList([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { code, data, msg } = await queryMetric({ ids });
|
||||||
|
if (code === 200 && Array.isArray(data?.list)) {
|
||||||
|
setMetricList(data.list);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirm = async () => {
|
||||||
|
const fieldsValue = await form.validateFields();
|
||||||
|
|
||||||
|
const queryData: ISemantic.IModelItem = {
|
||||||
|
...viewItem,
|
||||||
|
...fieldsValue,
|
||||||
|
domainId,
|
||||||
|
};
|
||||||
|
setSaveLoading(true);
|
||||||
|
const { code, msg } = await (!queryData.id ? createView : updateView)(queryData);
|
||||||
|
setSaveLoading(false);
|
||||||
|
if (code === 200) {
|
||||||
|
onSubmit?.(queryData);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderFooter = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button onClick={onCancel}>取消</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
handleConfirm();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
保 存
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderContent = () => {
|
||||||
|
return (
|
||||||
|
<div className={styles.viewSearchFormContainer}>
|
||||||
|
<ProCard title="指标模式" style={{ marginBottom: 10, borderBottom: '1px solid #eee' }}>
|
||||||
|
<DefaultSettingForm
|
||||||
|
form={form}
|
||||||
|
dimensionList={dimensionList}
|
||||||
|
metricList={metricList}
|
||||||
|
chatConfigType={ChatConfigType.METRIC}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
|
||||||
|
<ProCard title="标签模式">
|
||||||
|
<DefaultSettingForm
|
||||||
|
form={form}
|
||||||
|
dimensionList={dimensionList}
|
||||||
|
metricList={metricList}
|
||||||
|
chatConfigType={ChatConfigType.TAG}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
width={800}
|
||||||
|
destroyOnClose
|
||||||
|
title={'视图信息'}
|
||||||
|
open={true}
|
||||||
|
maskClosable={false}
|
||||||
|
footer={renderFooter()}
|
||||||
|
onCancel={onCancel}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
{...formLayout}
|
||||||
|
form={form}
|
||||||
|
initialValues={{
|
||||||
|
...viewItem,
|
||||||
|
}}
|
||||||
|
onValuesChange={(value, values) => {}}
|
||||||
|
>
|
||||||
|
<FormItem hidden={true} name="id" label="ID">
|
||||||
|
<Input placeholder="id" />
|
||||||
|
</FormItem>
|
||||||
|
{renderContent()}
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ViewSearchFormModal;
|
||||||
@@ -12,6 +12,7 @@ import moment from 'moment';
|
|||||||
import styles from '../../components/style.less';
|
import styles from '../../components/style.less';
|
||||||
import { ISemantic } from '../../data';
|
import { ISemantic } from '../../data';
|
||||||
import { ColumnsConfig } from '../../components/MetricTableColumnRender';
|
import { ColumnsConfig } from '../../components/MetricTableColumnRender';
|
||||||
|
import ViewSearchFormModal from './ViewSearchFormModal';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
disabledEdit?: boolean;
|
disabledEdit?: boolean;
|
||||||
@@ -26,6 +27,7 @@ const ViewTable: React.FC<Props> = ({ disabledEdit = false, modelList, domainMan
|
|||||||
const [saveLoading, setSaveLoading] = useState<boolean>(false);
|
const [saveLoading, setSaveLoading] = useState<boolean>(false);
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
|
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
|
||||||
|
const [searchModalOpen, setSearchModalOpen] = useState(false);
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
||||||
const updateViewStatus = async (modelData: ISemantic.IViewItem) => {
|
const updateViewStatus = async (modelData: ISemantic.IViewItem) => {
|
||||||
@@ -69,9 +71,6 @@ const ViewTable: React.FC<Props> = ({ disabledEdit = false, modelList, domainMan
|
|||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
title: '视图名称',
|
title: '视图名称',
|
||||||
search: false,
|
search: false,
|
||||||
// render: (_, record) => {
|
|
||||||
// return <a>{_}</a>;
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dataIndex: 'alias',
|
dataIndex: 'alias',
|
||||||
@@ -116,7 +115,7 @@ const ViewTable: React.FC<Props> = ({ disabledEdit = false, modelList, domainMan
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'x',
|
dataIndex: 'x',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
width: 150,
|
width: 250,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return (
|
return (
|
||||||
<Space className={styles.ctrlBtnContainer}>
|
<Space className={styles.ctrlBtnContainer}>
|
||||||
@@ -129,6 +128,15 @@ const ViewTable: React.FC<Props> = ({ disabledEdit = false, modelList, domainMan
|
|||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
key="searchEditBtn"
|
||||||
|
onClick={() => {
|
||||||
|
setViewItem(record);
|
||||||
|
setSearchModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
查询设置
|
||||||
|
</a>
|
||||||
{record.status === StatusEnum.ONLINE ? (
|
{record.status === StatusEnum.ONLINE ? (
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
@@ -223,6 +231,21 @@ const ViewTable: React.FC<Props> = ({ disabledEdit = false, modelList, domainMan
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{searchModalOpen && (
|
||||||
|
<ViewSearchFormModal
|
||||||
|
domainId={selectDomainId}
|
||||||
|
viewItem={viewItem}
|
||||||
|
modelList={modelList}
|
||||||
|
onSubmit={() => {
|
||||||
|
queryViewList();
|
||||||
|
setSearchModalOpen(false);
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
setSearchModalOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.viewSearchFormContainer {
|
||||||
|
:global {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
|
||||||
import { Button, Modal } from 'antd';
|
|
||||||
import type { IDataSource } from '../data';
|
|
||||||
import ProTable from '@ant-design/pro-table';
|
|
||||||
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
|
||||||
import { connect } from 'umi';
|
|
||||||
import type { Dispatch } from 'umi';
|
|
||||||
import type { StateType } from '../model';
|
|
||||||
|
|
||||||
export type CreateFormProps = {
|
|
||||||
measuresList: any[];
|
|
||||||
selectedMeasuresList: any[];
|
|
||||||
onCancel: () => void;
|
|
||||||
onSubmit: (selectMeasuresList: any[]) => void;
|
|
||||||
createModalVisible: boolean;
|
|
||||||
projectManger: StateType;
|
|
||||||
dispatch: Dispatch;
|
|
||||||
};
|
|
||||||
|
|
||||||
const BindMeasuresTable: React.FC<CreateFormProps> = ({
|
|
||||||
measuresList = [],
|
|
||||||
selectedMeasuresList = [],
|
|
||||||
onSubmit,
|
|
||||||
onCancel,
|
|
||||||
createModalVisible,
|
|
||||||
projectManger,
|
|
||||||
}) => {
|
|
||||||
const { searchParams = {} } = projectManger || {};
|
|
||||||
const actionRef = useRef<ActionType>();
|
|
||||||
|
|
||||||
const [selectedMeasuresKeys, setSelectedMeasuresKeys] = useState<string[]>(() => {
|
|
||||||
return selectedMeasuresList.map((item: any) => {
|
|
||||||
return item.bizName;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const [selectMeasuresList, setSelectMeasuresList] = useState<IDataSource.IMeasuresItem[]>([]);
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
onSubmit?.(selectMeasuresList);
|
|
||||||
};
|
|
||||||
|
|
||||||
const findMeasureItemByName = (bizName: string) => {
|
|
||||||
return measuresList.find((item) => {
|
|
||||||
return item.bizName === bizName;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const selectedMeasures: IDataSource.IMeasuresItem[] = selectedMeasuresKeys.map((bizName) => {
|
|
||||||
const item = findMeasureItemByName(bizName);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
setSelectMeasuresList([...selectedMeasures]);
|
|
||||||
}, [selectedMeasuresKeys]);
|
|
||||||
|
|
||||||
useEffect(() => {}, []);
|
|
||||||
|
|
||||||
const columns: ProColumns[] = [
|
|
||||||
{
|
|
||||||
dataIndex: 'bizName',
|
|
||||||
title: '度量名称',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'alias',
|
|
||||||
title: '别名',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'agg',
|
|
||||||
title: '算子类型',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const renderFooter = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button onClick={onCancel}>取消</Button>
|
|
||||||
<Button type="primary" onClick={handleSubmit}>
|
|
||||||
添加度量
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const rowSelection = {
|
|
||||||
selectedRowKeys: selectedMeasuresKeys,
|
|
||||||
onChange: (_selectedRowKeys: any[]) => {
|
|
||||||
setSelectedMeasuresKeys([..._selectedRowKeys]);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
width={800}
|
|
||||||
destroyOnClose
|
|
||||||
title="度量添加"
|
|
||||||
open={createModalVisible}
|
|
||||||
footer={renderFooter()}
|
|
||||||
onCancel={() => {
|
|
||||||
onCancel();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProTable
|
|
||||||
actionRef={actionRef}
|
|
||||||
rowKey="bizName"
|
|
||||||
rowSelection={rowSelection}
|
|
||||||
columns={columns}
|
|
||||||
params={{ ...searchParams }}
|
|
||||||
pagination={false}
|
|
||||||
dataSource={measuresList || []}
|
|
||||||
size="small"
|
|
||||||
search={false}
|
|
||||||
options={false}
|
|
||||||
scroll={{ y: 800 }}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(({ projectManger }: { projectManger: StateType }) => ({
|
|
||||||
projectManger,
|
|
||||||
}))(BindMeasuresTable);
|
|
||||||
@@ -279,7 +279,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
|||||||
<FormItem
|
<FormItem
|
||||||
name="expr"
|
name="expr"
|
||||||
label="表达式"
|
label="表达式"
|
||||||
tooltip="表达式中的字段必须在创建数据源的时候被标记为日期或者维度"
|
tooltip="表达式中的字段必须在创建模型的时候被标记为日期或者维度"
|
||||||
rules={[{ required: true, message: '请输入表达式' }]}
|
rules={[{ required: true, message: '请输入表达式' }]}
|
||||||
>
|
>
|
||||||
<SqlEditor height={'150px'} />
|
<SqlEditor height={'150px'} />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import TransTypeTag from '../TransTypeTag';
|
|||||||
type Props = {
|
type Props = {
|
||||||
entityData: any;
|
entityData: any;
|
||||||
chatConfigKey: string;
|
chatConfigKey: string;
|
||||||
chatConfigType: ChatConfigType.DETAIL | ChatConfigType.AGG;
|
chatConfigType: ChatConfigType.TAG | ChatConfigType.METRIC;
|
||||||
metricList: ISemantic.IMetricItem[];
|
metricList: ISemantic.IMetricItem[];
|
||||||
dimensionList: ISemantic.IDimensionItem[];
|
dimensionList: ISemantic.IDimensionItem[];
|
||||||
domainId: number;
|
domainId: number;
|
||||||
@@ -59,7 +59,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
|||||||
...chatDefaultConfig,
|
...chatDefaultConfig,
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
if (chatConfigType === ChatConfigType.DETAIL) {
|
if (chatConfigType === ChatConfigType.TAG) {
|
||||||
initDataItemValue(chatDefaultConfig);
|
initDataItemValue(chatDefaultConfig);
|
||||||
}
|
}
|
||||||
}, [entityData, dataItemListOptions]);
|
}, [entityData, dataItemListOptions]);
|
||||||
@@ -165,7 +165,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
|||||||
<Input placeholder="id" />
|
<Input placeholder="id" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
{chatConfigType === ChatConfigType.DETAIL && (
|
{chatConfigType === ChatConfigType.TAG && (
|
||||||
<FormItem name="dataItemIds" label="圈选结果展示字段">
|
<FormItem name="dataItemIds" label="圈选结果展示字段">
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
@@ -193,7 +193,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Input.Group compact>
|
<Input.Group compact>
|
||||||
{chatConfigType === ChatConfigType.DETAIL ? (
|
{chatConfigType === ChatConfigType.TAG ? (
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ import type { IChatConfig } from '../../data';
|
|||||||
import { ChatConfigType } from '../../enum';
|
import { ChatConfigType } from '../../enum';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
chatConfigType: ChatConfigType.DETAIL | ChatConfigType.AGG;
|
chatConfigType: ChatConfigType.TAG | ChatConfigType.METRIC;
|
||||||
onConfigSave?: () => void;
|
onConfigSave?: () => void;
|
||||||
dispatch: Dispatch;
|
dispatch: Dispatch;
|
||||||
domainManger: StateType;
|
domainManger: StateType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EntitySection: React.FC<Props> = forwardRef(
|
const EntitySection: React.FC<Props> = forwardRef(
|
||||||
({ domainManger, chatConfigType = ChatConfigType.DETAIL, onConfigSave }, ref: Ref<any>) => {
|
({ domainManger, chatConfigType = ChatConfigType.TAG, onConfigSave }, ref: Ref<any>) => {
|
||||||
const { selectDomainId, selectModelId: modelId, dimensionList, metricList } = domainManger;
|
const { selectDomainId, selectModelId: modelId, dimensionList, metricList } = domainManger;
|
||||||
|
|
||||||
const [entityData, setEntityData] = useState<IChatConfig.IChatRichConfig>();
|
const [entityData, setEntityData] = useState<IChatConfig.IChatRichConfig>();
|
||||||
@@ -34,10 +34,10 @@ const EntitySection: React.FC<Props> = forwardRef(
|
|||||||
|
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
const { chatAggRichConfig, chatDetailRichConfig, id, domainId, modelId } = data;
|
const { chatAggRichConfig, chatDetailRichConfig, id, domainId, modelId } = data;
|
||||||
if (chatConfigType === ChatConfigType.DETAIL) {
|
if (chatConfigType === ChatConfigType.TAG) {
|
||||||
setEntityData({ ...chatDetailRichConfig, id, domainId, modelId });
|
setEntityData({ ...chatDetailRichConfig, id, domainId, modelId });
|
||||||
}
|
}
|
||||||
if (chatConfigType === ChatConfigType.AGG) {
|
if (chatConfigType === ChatConfigType.METRIC) {
|
||||||
setEntityData({ ...chatAggRichConfig, id, domainId, modelId });
|
setEntityData({ ...chatAggRichConfig, id, domainId, modelId });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -66,7 +66,7 @@ const EntitySection: React.FC<Props> = forwardRef(
|
|||||||
entityData={entityData || {}}
|
entityData={entityData || {}}
|
||||||
chatConfigType={chatConfigType}
|
chatConfigType={chatConfigType}
|
||||||
chatConfigKey={
|
chatConfigKey={
|
||||||
chatConfigType === ChatConfigType.DETAIL ? 'chatDetailConfig' : 'chatAggConfig'
|
chatConfigType === ChatConfigType.TAG ? 'chatDetailConfig' : 'chatAggConfig'
|
||||||
}
|
}
|
||||||
dimensionList={dimensionList.filter((item) => {
|
dimensionList={dimensionList.filter((item) => {
|
||||||
const blackDimensionList = entityData?.visibility?.blackDimIdList;
|
const blackDimensionList = entityData?.visibility?.blackDimIdList;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type Props = {
|
|||||||
onSqlChange: (sql: string) => void;
|
onSqlChange: (sql: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MetricMeasuresFormTable: React.FC<Props> = ({
|
const MetricFieldFormTable: React.FC<Props> = ({
|
||||||
typeParams,
|
typeParams,
|
||||||
fieldList,
|
fieldList,
|
||||||
onFieldChange,
|
onFieldChange,
|
||||||
@@ -80,7 +80,6 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
rowKey="fieldName"
|
rowKey="fieldName"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={tableData}
|
dataSource={tableData}
|
||||||
pagination={false}
|
|
||||||
search={false}
|
search={false}
|
||||||
toolbar={{
|
toolbar={{
|
||||||
search: {
|
search: {
|
||||||
@@ -97,6 +96,7 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
pagination={{ defaultPageSize: 10 }}
|
||||||
size="small"
|
size="small"
|
||||||
options={false}
|
options={false}
|
||||||
tableAlertRender={false}
|
tableAlertRender={false}
|
||||||
@@ -134,4 +134,4 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MetricMeasuresFormTable;
|
export default MetricFieldFormTable;
|
||||||
|
|||||||
@@ -122,14 +122,70 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
const backward = () => setCurrentStep(currentStep - 1);
|
const backward = () => setCurrentStep(currentStep - 1);
|
||||||
|
|
||||||
const queryModelDetail = async () => {
|
const queryModelDetail = async () => {
|
||||||
// const { code, data } = await getMeasureListByModelId(modelId);
|
|
||||||
const { code, data } = await getModelDetail({ modelId: modelId || metricItem?.modelId });
|
const { code, data } = await getModelDetail({ modelId: modelId || metricItem?.modelId });
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
if (Array.isArray(data?.modelDetail?.fields)) {
|
if (Array.isArray(data?.modelDetail?.fields)) {
|
||||||
setFieldList(data.modelDetail.fields);
|
if (Array.isArray(metricItem?.metricDefineByFieldParams?.fields)) {
|
||||||
|
const fieldList = data.modelDetail.fields.map((item: ISemantic.IFieldTypeParamsItem) => {
|
||||||
|
const { fieldName } = item;
|
||||||
|
if (
|
||||||
|
metricItem?.metricDefineByFieldParams?.fields.find(
|
||||||
|
(measureParamsItem: ISemantic.IFieldTypeParamsItem) =>
|
||||||
|
measureParamsItem.fieldName === fieldName,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
orderNumber: 9999,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
orderNumber: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortList = fieldList.sort(
|
||||||
|
(
|
||||||
|
a: ISemantic.IFieldTypeParamsItem & { orderNumber: number },
|
||||||
|
b: ISemantic.IFieldTypeParamsItem & { orderNumber: number },
|
||||||
|
) => b.orderNumber - a.orderNumber,
|
||||||
|
);
|
||||||
|
setFieldList(sortList);
|
||||||
|
} else {
|
||||||
|
setFieldList(data.modelDetail.fields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(data?.modelDetail?.measures)) {
|
if (Array.isArray(data?.modelDetail?.measures)) {
|
||||||
setClassMeasureList(data.modelDetail.measures);
|
if (Array.isArray(metricItem?.metricDefineByMeasureParams?.measures)) {
|
||||||
|
const measureList = data.modelDetail.measures.map((item: ISemantic.IMeasure) => {
|
||||||
|
const { bizName } = item;
|
||||||
|
if (
|
||||||
|
metricItem?.metricDefineByMeasureParams?.measures.find(
|
||||||
|
(measureParamsItem: ISemantic.IMeasure) => measureParamsItem.bizName === bizName,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
orderNumber: 9999,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
orderNumber: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const sortMeasureList = measureList.sort(
|
||||||
|
(
|
||||||
|
a: ISemantic.IMeasure & { orderNumber: number },
|
||||||
|
b: ISemantic.IMeasure & { orderNumber: number },
|
||||||
|
) => b.orderNumber - a.orderNumber,
|
||||||
|
);
|
||||||
|
setClassMeasureList(sortMeasureList);
|
||||||
|
} else {
|
||||||
|
setClassMeasureList(data.modelDetail.measures);
|
||||||
|
}
|
||||||
|
|
||||||
if (datasourceId) {
|
if (datasourceId) {
|
||||||
const hasMeasures = data.some(
|
const hasMeasures = data.some(
|
||||||
(item: ISemantic.IMeasure) => item.datasourceId === datasourceId,
|
(item: ISemantic.IMeasure) => item.datasourceId === datasourceId,
|
||||||
@@ -338,7 +394,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
const queryMetricTags = async () => {
|
const queryMetricTags = async () => {
|
||||||
const { code, data } = await getMetricTags();
|
const { code, data } = await getMetricTags();
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
// form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data])));
|
|
||||||
setTagOptions(
|
setTagOptions(
|
||||||
Array.isArray(data)
|
Array.isArray(data)
|
||||||
? data.map((tag: string) => {
|
? data.map((tag: string) => {
|
||||||
@@ -355,7 +410,37 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
modelId: modelId || metricItem?.modelId,
|
modelId: modelId || metricItem?.modelId,
|
||||||
});
|
});
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
setCreateNewMetricList(data);
|
if (Array.isArray(metricItem?.metricDefineByMetricParams?.metrics)) {
|
||||||
|
const fieldList = data.map((item: ISemantic.IMetricTypeParamsItem) => {
|
||||||
|
const { bizName } = item;
|
||||||
|
if (
|
||||||
|
metricItem?.metricDefineByMetricParams?.metrics.find(
|
||||||
|
(measureParamsItem: ISemantic.IMetricTypeParamsItem) =>
|
||||||
|
measureParamsItem.bizName === bizName,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
orderNumber: 9999,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
orderNumber: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortList = fieldList.sort(
|
||||||
|
(
|
||||||
|
a: ISemantic.IMetricTypeParamsItem & { orderNumber: number },
|
||||||
|
b: ISemantic.IMetricTypeParamsItem & { orderNumber: number },
|
||||||
|
) => b.orderNumber - a.orderNumber,
|
||||||
|
);
|
||||||
|
setCreateNewMetricList(sortList);
|
||||||
|
} else {
|
||||||
|
setCreateNewMetricList(data);
|
||||||
|
}
|
||||||
|
// setCreateNewMetricList(data);
|
||||||
} else {
|
} else {
|
||||||
message.error('获取指标标签失败');
|
message.error('获取指标标签失败');
|
||||||
}
|
}
|
||||||
@@ -417,15 +502,11 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
{defineType === METRIC_DEFINE_TYPE.METRIC && (
|
{defineType === METRIC_DEFINE_TYPE.METRIC && (
|
||||||
<>
|
<>
|
||||||
<p className={styles.desc}>
|
<p className={styles.desc}>
|
||||||
通过
|
基于
|
||||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
<Tag color="#2499ef14" className={styles.markerTag}>
|
||||||
字段
|
已有
|
||||||
</Tag>
|
</Tag>
|
||||||
和
|
指标来衍生新的指标
|
||||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
|
||||||
度量
|
|
||||||
</Tag>
|
|
||||||
创建的指标可用来创建新的指标
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<MetricMetricFormTable
|
<MetricMetricFormTable
|
||||||
@@ -701,7 +782,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
<>
|
<>
|
||||||
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
|
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
|
||||||
<Step title="基本信息" />
|
<Step title="基本信息" />
|
||||||
<Step title="度量信息" />
|
<Step title="表达式" />
|
||||||
</Steps>
|
</Steps>
|
||||||
<Form
|
<Form
|
||||||
{...formLayout}
|
{...formLayout}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Button, Input, Space, Tag } from 'antd';
|
import { Input, Space, Tag } from 'antd';
|
||||||
import ProTable from '@ant-design/pro-table';
|
import ProTable from '@ant-design/pro-table';
|
||||||
import ProCard from '@ant-design/pro-card';
|
import ProCard from '@ant-design/pro-card';
|
||||||
import SqlEditor from '@/components/SqlEditor';
|
import SqlEditor from '@/components/SqlEditor';
|
||||||
import BindMeasuresTable from './BindMeasuresTable';
|
|
||||||
import FormLabelRequire from './FormLabelRequire';
|
import FormLabelRequire from './FormLabelRequire';
|
||||||
import styles from './style.less';
|
import styles from './style.less';
|
||||||
import { ISemantic } from '../data';
|
import { ISemantic } from '../data';
|
||||||
@@ -26,7 +26,9 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
onSqlChange,
|
onSqlChange,
|
||||||
}) => {
|
}) => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const [measuresModalVisible, setMeasuresModalVisible] = useState<boolean>(false);
|
|
||||||
|
const [tableData, setTableData] = useState<any[]>([]);
|
||||||
|
|
||||||
const [measuresParams, setMeasuresParams] = useState(
|
const [measuresParams, setMeasuresParams] = useState(
|
||||||
typeParams || {
|
typeParams || {
|
||||||
expr: '',
|
expr: '',
|
||||||
@@ -34,6 +36,20 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [selectedKeys, setSelectedKeys] = useState<string[]>(() => {
|
||||||
|
return measuresParams?.measures.map((item: any) => {
|
||||||
|
return item.bizName;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const datasource =
|
||||||
|
datasourceId && Array.isArray(measuresList)
|
||||||
|
? measuresList.filter((item) => item.datasourceId === datasourceId)
|
||||||
|
: measuresList;
|
||||||
|
setTableData(datasource);
|
||||||
|
}, [measuresList]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMeasuresParams({ ...typeParams });
|
setMeasuresParams({ ...typeParams });
|
||||||
}, [typeParams]);
|
}, [typeParams]);
|
||||||
@@ -49,8 +65,9 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
{
|
{
|
||||||
dataIndex: 'constraint',
|
dataIndex: 'constraint',
|
||||||
title: '限定条件',
|
title: '限定条件',
|
||||||
|
width: 250,
|
||||||
tooltip:
|
tooltip:
|
||||||
'该限定条件用于在计算指标时限定口径,作用于度量,所用于过滤的维度必须在创建数据源的时候被标记为日期或者维度,不需要加where关键字。比如:维度A="值1" and 维度B="值2"',
|
'该限定条件用于在计算指标时限定口径,作用于度量,所用于过滤的维度必须在创建模型的时候被标记为日期或者维度,不需要加where关键字。比如:维度A="值1" and 维度B="值2"',
|
||||||
render: (_: any, record: any) => {
|
render: (_: any, record: any) => {
|
||||||
const { constraint, name } = record;
|
const { constraint, name } = record;
|
||||||
const { measures } = measuresParams;
|
const { measures } = measuresParams;
|
||||||
@@ -78,57 +95,59 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
{
|
{
|
||||||
dataIndex: 'agg',
|
dataIndex: 'agg',
|
||||||
title: '聚合函数',
|
title: '聚合函数',
|
||||||
},
|
width: 80,
|
||||||
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
dataIndex: 'x',
|
|
||||||
valueType: 'option',
|
|
||||||
render: (_: any, record: any) => {
|
|
||||||
const { bizName } = record;
|
|
||||||
return (
|
|
||||||
<Space>
|
|
||||||
<a
|
|
||||||
key="deleteBtn"
|
|
||||||
onClick={() => {
|
|
||||||
const { measures } = measuresParams;
|
|
||||||
const list = measures.filter((item: any) => {
|
|
||||||
return item.bizName !== bizName;
|
|
||||||
});
|
|
||||||
onFieldChange?.(list);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</a>
|
|
||||||
</Space>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys: selectedKeys,
|
||||||
|
onChange: (_selectedRowKeys: any[], items: ISemantic.IMeasure[]) => {
|
||||||
|
setSelectedKeys([..._selectedRowKeys]);
|
||||||
|
|
||||||
|
const measures = items.map(({ bizName, name, expr, datasourceId, agg }) => {
|
||||||
|
return {
|
||||||
|
bizName,
|
||||||
|
name,
|
||||||
|
expr,
|
||||||
|
agg,
|
||||||
|
datasourceId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
onFieldChange?.(measures);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
<ProTable
|
<ProTable
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
headerTitle={<FormLabelRequire title="度量列表" />}
|
headerTitle={<FormLabelRequire title="度量列表" />}
|
||||||
rowKey="name"
|
rowKey="bizName"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={measuresParams?.measures || []}
|
dataSource={tableData}
|
||||||
pagination={false}
|
|
||||||
search={false}
|
search={false}
|
||||||
|
toolbar={{
|
||||||
|
search: {
|
||||||
|
placeholder: '请输入度量名称',
|
||||||
|
onSearch: (value: string) => {
|
||||||
|
setTableData(
|
||||||
|
[...tableData].reduce((data: ISemantic.IMeasure[], item: ISemantic.IMeasure) => {
|
||||||
|
if (item.bizName.includes(value)) {
|
||||||
|
data.push(item);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}, []),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
pagination={{ defaultPageSize: 10 }}
|
||||||
size="small"
|
size="small"
|
||||||
options={false}
|
options={false}
|
||||||
toolBarRender={() => [
|
tableAlertRender={false}
|
||||||
<Button
|
scroll={{ y: 500 }}
|
||||||
key="create"
|
rowSelection={rowSelection}
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
setMeasuresModalVisible(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
增加度量
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
<ProCard
|
<ProCard
|
||||||
title={<FormLabelRequire title="度量表达式" />}
|
title={<FormLabelRequire title="度量表达式" />}
|
||||||
@@ -161,33 +180,6 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
</ProCard>
|
</ProCard>
|
||||||
</Space>
|
</Space>
|
||||||
{measuresModalVisible && (
|
|
||||||
<BindMeasuresTable
|
|
||||||
measuresList={
|
|
||||||
datasourceId && Array.isArray(measuresList)
|
|
||||||
? measuresList.filter((item) => item.datasourceId === datasourceId)
|
|
||||||
: measuresList
|
|
||||||
}
|
|
||||||
selectedMeasuresList={measuresParams?.measures || []}
|
|
||||||
onSubmit={async (values: any[]) => {
|
|
||||||
const measures = values.map(({ bizName, name, expr, datasourceId, agg }) => {
|
|
||||||
return {
|
|
||||||
bizName,
|
|
||||||
name,
|
|
||||||
expr,
|
|
||||||
agg,
|
|
||||||
datasourceId,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
onFieldChange?.(measures);
|
|
||||||
setMeasuresModalVisible(false);
|
|
||||||
}}
|
|
||||||
onCancel={() => {
|
|
||||||
setMeasuresModalVisible(false);
|
|
||||||
}}
|
|
||||||
createModalVisible={measuresModalVisible}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const MetricMetricFormTable: React.FC<Props> = ({
|
|||||||
|
|
||||||
// const [selectMeasuresList, setSelectMeasuresList] = useState<IDataSource.IMeasuresItem[]>([]);
|
// const [selectMeasuresList, setSelectMeasuresList] = useState<IDataSource.IMeasuresItem[]>([]);
|
||||||
|
|
||||||
const [selectedMeasuresKeys, setSelectedMeasuresKeys] = useState<string[]>(() => {
|
const [selectedKeys, setSelectedKeys] = useState<string[]>(() => {
|
||||||
// return [];
|
// return [];
|
||||||
return defineTypeParams.metrics.map((item: any) => {
|
return defineTypeParams.metrics.map((item: any) => {
|
||||||
return item.bizName;
|
return item.bizName;
|
||||||
@@ -68,9 +68,9 @@ const MetricMetricFormTable: React.FC<Props> = ({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const rowSelection = {
|
const rowSelection = {
|
||||||
selectedRowKeys: selectedMeasuresKeys,
|
selectedRowKeys: selectedKeys,
|
||||||
onChange: (_selectedRowKeys: any[]) => {
|
onChange: (_selectedRowKeys: any[]) => {
|
||||||
setSelectedMeasuresKeys([..._selectedRowKeys]);
|
setSelectedKeys([..._selectedRowKeys]);
|
||||||
onFieldChange(
|
onFieldChange(
|
||||||
metricList.reduce(
|
metricList.reduce(
|
||||||
(metrics: ISemantic.IMetricTypeParamsItem[], item: ISemantic.IMetricItem) => {
|
(metrics: ISemantic.IMetricTypeParamsItem[], item: ISemantic.IMetricItem) => {
|
||||||
@@ -97,7 +97,7 @@ const MetricMetricFormTable: React.FC<Props> = ({
|
|||||||
rowKey="bizName"
|
rowKey="bizName"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={tableData}
|
dataSource={tableData}
|
||||||
pagination={false}
|
// pagination={false}
|
||||||
search={false}
|
search={false}
|
||||||
toolbar={{
|
toolbar={{
|
||||||
search: {
|
search: {
|
||||||
@@ -117,6 +117,7 @@ const MetricMetricFormTable: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
pagination={{ defaultPageSize: 10 }}
|
||||||
size="small"
|
size="small"
|
||||||
options={false}
|
options={false}
|
||||||
tableAlertRender={false}
|
tableAlertRender={false}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { StateType } from '../../model';
|
|||||||
import SqlEditor from '@/components/SqlEditor';
|
import SqlEditor from '@/components/SqlEditor';
|
||||||
import { TransType } from '../../enum';
|
import { TransType } from '../../enum';
|
||||||
import DimensionMetricVisibleTransfer from '../Entity/DimensionMetricVisibleTransfer';
|
import DimensionMetricVisibleTransfer from '../Entity/DimensionMetricVisibleTransfer';
|
||||||
import { wrapperTransTypeAndId } from '../Entity/utils';
|
import { wrapperTransTypeAndId } from '../../utils';
|
||||||
import styles from '../style.less';
|
import styles from '../style.less';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const TransTypeTag: React.FC<Props> = ({ type }) => {
|
|||||||
) : type === SemanticNodeType.METRIC ? (
|
) : type === SemanticNodeType.METRIC ? (
|
||||||
<Tag color="orange">{'指标'}</Tag>
|
<Tag color="orange">{'指标'}</Tag>
|
||||||
) : type === SemanticNodeType.DATASOURCE ? (
|
) : type === SemanticNodeType.DATASOURCE ? (
|
||||||
<Tag color="green">{'数据源'}</Tag>
|
<Tag color="green">{'模型'}</Tag>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ export const IS_TAG_ENUM = {
|
|||||||
|
|
||||||
export const SENSITIVE_LEVEL_COLOR = {
|
export const SENSITIVE_LEVEL_COLOR = {
|
||||||
[SENSITIVE_LEVEL.LOW]: 'default',
|
[SENSITIVE_LEVEL.LOW]: 'default',
|
||||||
// [SENSITIVE_LEVEL.MID]: 'orange',
|
[SENSITIVE_LEVEL.MID]: 'orange',
|
||||||
[SENSITIVE_LEVEL.MID]: 'geekblue',
|
// [SENSITIVE_LEVEL.MID]: 'geekblue',
|
||||||
[SENSITIVE_LEVEL.HIGH]: 'volcano',
|
[SENSITIVE_LEVEL.HIGH]: 'volcano',
|
||||||
// [SENSITIVE_LEVEL.HIGH]: '#1677ff',
|
// [SENSITIVE_LEVEL.HIGH]: '#1677ff',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SEMANTIC_NODE_TYPE_CONFIG = {
|
export const SEMANTIC_NODE_TYPE_CONFIG = {
|
||||||
[SemanticNodeType.DATASOURCE]: {
|
[SemanticNodeType.DATASOURCE]: {
|
||||||
label: '数据源',
|
label: '模型',
|
||||||
value: SemanticNodeType.DATASOURCE,
|
value: SemanticNodeType.DATASOURCE,
|
||||||
color: 'cyan',
|
color: 'cyan',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export enum ChatConfigType {
|
export enum ChatConfigType {
|
||||||
DETAIL = 'detail',
|
TAG = 'TAG',
|
||||||
AGG = 'agg',
|
METRIC = 'METRIC',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TransType {
|
export enum TransType {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { Form, Input, InputNumber, Switch, Select } from 'antd';
|
|||||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||||
import DisabledWheelNumberInput from '@/components/DisabledWheelNumberInput';
|
import DisabledWheelNumberInput from '@/components/DisabledWheelNumberInput';
|
||||||
import { ConfigParametersItem } from '../System/types';
|
import { ConfigParametersItem } from '../System/types';
|
||||||
|
import { TransType } from './enum';
|
||||||
|
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
@@ -200,3 +202,7 @@ export const genneratorFormItemList = (itemList: ConfigParametersItem[]) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const wrapperTransTypeAndId = (exTransType: TransType, id: number) => {
|
||||||
|
return `${exTransType}-${id}`;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user