[improvement][headless-fe] Revamped the interaction for semantic modeling routing and successfully implemented the switching between dimension and dataset management. (#1934)

Co-authored-by: tristanliu <tristanliu@tencent.com>
This commit is contained in:
Jun Zhang
2024-11-30 20:03:41 +08:00
committed by GitHub
parent 593597fe26
commit 82c63a7f22
42 changed files with 1889 additions and 997 deletions

View File

@@ -1,7 +1,8 @@
import { message, Tabs, Button, Space } from 'antd';
import React, { useState, useEffect } from 'react';
import { getMetricData, getDimensionList, getDrillDownDimension } from '../service';
import { useParams, history } from '@umijs/max';
import { useParams, history, Helmet } from '@umijs/max';
import { BASE_TITLE } from '@/common/constants';
import styles from './style.less';
import { ArrowLeftOutlined } from '@ant-design/icons';
import MetricTrendSection from '@/pages/SemanticModel/Metric/components/MetricTrendSection';
@@ -32,6 +33,9 @@ const MetricDetail: React.FC<Props> = () => {
}, [metricId]);
const queryMetricData = async (metricId: string) => {
if (!metricId) {
return;
}
const { code, data, msg } = await getMetricData(metricId);
if (code === 200) {
setMetircData({ ...data });
@@ -80,7 +84,7 @@ const MetricDetail: React.FC<Props> = () => {
{
key: 'metricCaliberInput',
label: '基础信息',
children: <MetricBasicInfo metircData={metircData} onQueryMetricData={queryMetricData} />,
children: <MetricBasicInfo metircData={metircData} />,
},
{
key: 'metricTrend',
@@ -103,8 +107,20 @@ const MetricDetail: React.FC<Props> = () => {
return (
<>
<Helmet
title={`${metircData?.id ? `[指标]${metircData?.name}-${BASE_TITLE}` : '新建指标'}`}
/>
<div className={styles.metricDetailWrapper}>
<div className={styles.metricDetail}>
<div className={styles.siderContainer}>
<MetricInfoSider
relationDimensionOptions={relationDimensionOptions}
metircData={metircData}
onDimensionRelationBtnClick={() => {
setMetricRelationModalOpenState(true);
}}
/>
</div>
<div className={styles.tabContainer}>
<Tabs
defaultActiveKey="metricCaliberInput"
@@ -130,15 +146,6 @@ const MetricDetail: React.FC<Props> = () => {
className={styles.metricDetailTab}
/>
</div>
<div className={styles.siderContainer}>
<MetricInfoSider
relationDimensionOptions={relationDimensionOptions}
metircData={metircData}
onDimensionRelationBtnClick={() => {
setMetricRelationModalOpenState(true);
}}
/>
</div>
</div>
<DimensionAndMetricRelationModal
metricItem={metircData}

View File

@@ -1,21 +1,25 @@
import { message } from 'antd';
import React, { useState, useEffect } from 'react';
import { getMetricData } from '../service';
import { useParams, useModel } from '@umijs/max';
import styles from './style.less';
import { useParams, useModel, Helmet } from '@umijs/max';
import { BASE_TITLE } from '@/common/constants';
import { ISemantic } from '../data';
import MetricInfoEditSider from './MetricInfoEditSider';
import MetricInfoCreateForm from './components/MetricInfoCreateForm';
import { MetricSettingKey } from './constants';
import DetailContainer from '../components/DetailContainer';
import DetailSider from '../components/DetailContainer/DetailSider';
import { ProjectOutlined, ConsoleSqlOutlined } from '@ant-design/icons';
import { MetricSettingKey, MetricSettingWording } from './constants';
type Props = Record<string, any>;
const MetricDetail: React.FC<Props> = () => {
const params: any = useParams();
const metricId = params.metricId;
const metricId = +params.metricId;
const modelId = +params.modelId;
const domainId = +params.domainId;
const [metircData, setMetircData] = useState<ISemantic.IMetricItem>();
const metricModel = useModel('SemanticModel.metricData');
const { selectMetric, setSelectMetric } = metricModel;
const { setSelectMetric } = metricModel;
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
useEffect(() => {
@@ -31,7 +35,10 @@ const MetricDetail: React.FC<Props> = () => {
};
}, []);
const queryMetricData = async (metricId: string) => {
const queryMetricData = async (metricId: number) => {
if (!metricId) {
return;
}
const { code, data, msg } = await getMetricData(metricId);
if (code === 200) {
setMetircData({ ...data });
@@ -41,23 +48,46 @@ const MetricDetail: React.FC<Props> = () => {
message.error(msg);
};
const settingList = [
{
icon: <ProjectOutlined />,
key: MetricSettingKey.BASIC,
text: MetricSettingWording[MetricSettingKey.BASIC],
},
{
icon: <ConsoleSqlOutlined />,
key: MetricSettingKey.SQL_CONFIG,
text: MetricSettingWording[MetricSettingKey.SQL_CONFIG],
},
];
return (
<>
<div className={styles.metricEditWrapper}>
<div className={styles.metricDetail}>
<div className={styles.siderContainer}>
<MetricInfoEditSider
onSettingKeyChange={(key: string) => {
setSettingKey(key);
}}
metircData={metircData}
/>
</div>
<div className={styles.tabContainer}>
<MetricInfoCreateForm settingKey={settingKey} metricItem={metircData} />
</div>
</div>
</div>
<Helmet
title={`${
metircData?.id ? `[指标]${metircData?.name}-${BASE_TITLE}` : `新建指标-${BASE_TITLE}`
}`}
/>
<DetailContainer
siderNode={
<DetailSider
menuKey={MetricSettingKey.BASIC}
menuList={settingList}
detailData={metircData}
onMenuKeyChange={(key: string) => {
setSettingKey(key);
}}
/>
}
containerNode={
<MetricInfoCreateForm
settingKey={settingKey}
metricItem={metircData}
modelId={metircData?.modelId || modelId}
domainId={metircData?.domainId || domainId}
/>
}
/>
</>
);
};

View File

@@ -1,57 +0,0 @@
import { message } from 'antd';
import React, { useState, useEffect } from 'react';
import { getMetricData } from '../service';
import { useParams } from '@umijs/max';
import styles from './style.less';
import { ISemantic } from '../data';
import MetricInfoEditSider from './MetricInfoEditSider';
import MetricInfoCreateForm from './components/MetricInfoCreateForm';
import { MetricSettingKey } from './constants';
type Props = Record<string, any>;
const MetricDetail: React.FC<Props> = () => {
const params: any = useParams();
const metricId = params.metricId;
const [metircData, setMetircData] = useState<ISemantic.IMetricItem>();
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
useEffect(() => {
if (!metricId) {
return;
}
queryMetricData(metricId);
}, [metricId]);
const queryMetricData = async (metricId: string) => {
const { code, data, msg } = await getMetricData(metricId);
if (code === 200) {
setMetircData({ ...data });
return;
}
message.error(msg);
};
return (
<>
<div className={styles.metricWrapper}>
<div className={styles.metricDetail}>
{/* <div className={styles.siderContainer}>
<MetricInfoEditSider
onSettingKeyChange={(key: string) => {
setSettingKey(key);
}}
metircData={metircData}
/>
</div> */}
<div className={styles.tabContainer}>
<MetricInfoCreateForm settingKey={settingKey} metricItem={metircData} />
</div>
</div>
</div>
</>
);
};
export default MetricDetail;

View File

@@ -152,8 +152,6 @@ const ClassMetricTable: React.FC<Props> = ({}) => {
};
const handleMetricEdit = (metricItem: ISemantic.IMetricItem) => {
// setMetricItem(metricItem);
// setCreateModalVisible(true);
history.push(`/model/metric/edit/${metricItem.id}`);
};

View File

@@ -1,162 +0,0 @@
import { Tag, Space, Tooltip } from 'antd';
import React, { useState } from 'react';
import dayjs from 'dayjs';
import { MetricSettingKey, MetricSettingWording } from './constants';
import { basePath } from '../../../../config/defaultSettings';
import {
ExportOutlined,
SolutionOutlined,
PartitionOutlined,
ProjectOutlined,
ConsoleSqlOutlined,
SettingOutlined,
} from '@ant-design/icons';
import styles from './style.less';
import { ISemantic } from '../data';
import IndicatorStar from '../components/IndicatorStar';
type Props = {
metircData: ISemantic.IMetricItem;
onSettingKeyChange?: (key: MetricSettingKey) => void;
};
const MetricInfoEditSider: React.FC<Props> = ({ metircData, onSettingKeyChange }) => {
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
const settingList = [
{
icon: <ProjectOutlined />,
key: MetricSettingKey.BASIC,
text: MetricSettingWording[MetricSettingKey.BASIC],
},
{
icon: <ConsoleSqlOutlined />,
key: MetricSettingKey.SQL_CONFIG,
text: MetricSettingWording[MetricSettingKey.SQL_CONFIG],
},
// {
// icon: <DashboardOutlined />,
// key: MetricSettingKey.DIMENSION_CONFIG,
// text: MetricSettingWording[MetricSettingKey.DIMENSION_CONFIG],
// },
];
return (
<div className={styles.metricInfoSider}>
<div className={styles.sectionContainer}>
{metircData?.id ? (
<div className={styles.title}>
<div className={styles.name}>
<Space>
<IndicatorStar indicatorId={metircData?.id} initState={metircData?.isCollect} />
{metircData?.name}
{metircData?.hasAdminRes && (
<span
className={styles.gotoMetricListIcon}
onClick={() => {
window.open(`${basePath}model/${metircData.domainId}/${metircData.modelId}/`);
}}
>
<Tooltip title="前往所属模型指标列表">
<ExportOutlined />
</Tooltip>
</span>
)}
</Space>
</div>
{metircData?.bizName && <div className={styles.bizName}>{metircData.bizName}</div>}
</div>
) : (
<div className={styles.createTitle}>
<Space>
<SettingOutlined />
</Space>
</div>
)}
<hr className={styles.hr} />
<div className={styles.section} style={{ padding: '16px 0' }}>
<ul className={styles.settingList}>
{settingList.map((item) => {
return (
<li
className={item.key === settingKey ? styles.active : ''}
key={item.key}
onClick={() => {
onSettingKeyChange?.(item.key);
setSettingKey(item.key);
}}
>
<div className={styles.icon}>{item.icon}</div>
<div className={styles.content}>
<span className={styles.text}> {item.text}</span>
</div>
</li>
);
})}
</ul>
</div>
{/* <hr className={styles.hr} /> */}
{metircData?.id && (
<div className={styles.section} style={{ marginTop: 'auto' }}>
<div className={styles.sectionTitleBox}>
<span className={styles.sectionTitle}>
<Space>
<SolutionOutlined />
</Space>
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
<Space>
<Tag icon={<PartitionOutlined />} color="#3b5999">
{metircData?.modelName || '模型名为空'}
</Tag>
{metircData?.hasAdminRes && (
<span
className={styles.gotoMetricListIcon}
onClick={() => {
window.open(`${basePath}model/${metircData.domainId}/0/overview`);
}}
>
<Tooltip title="前往模型设置页">
<ExportOutlined />
</Tooltip>
</span>
)}
</Space>
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>{metircData?.createdBy}</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
{metircData?.createdAt
? dayjs(metircData?.createdAt).format('YYYY-MM-DD HH:mm:ss')
: ''}
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
{metircData?.createdAt
? dayjs(metircData?.updatedAt).format('YYYY-MM-DD HH:mm:ss')
: ''}
</span>
</div>
</div>
)}
{/* <hr className={styles.hr} /> */}
</div>
</div>
);
};
export default MetricInfoEditSider;

View File

@@ -59,15 +59,6 @@ const MetricInfoSider: React.FC<Props> = ({
<div className={styles.sectionContainer}>
<hr className={styles.hr} />
<div className={styles.section}>
{/* <div className={styles.sectionTitleBox}>
<span className={styles.sectionTitle}>
<Space>
<ContainerOutlined />
基本信息
</Space>
</span>
</div> */}
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>

View File

@@ -1,4 +1,4 @@
import { Form, Input, Space, Row, Col, Switch, Flex, Tag } from 'antd';
import { Form, Input, Space, Row, Col } from 'antd';
import StandardFormRow from '@/components/StandardFormRow';
import TagSelect from '@/components/TagSelect';
import React, { ReactNode, useEffect } from 'react';

View File

@@ -29,7 +29,6 @@ import {
batchCreateTag,
batchDeleteTag,
} from '../../service';
import { ArrowLeftOutlined } from '@ant-design/icons';
import MetricMetricFormTable from '../../components/MetricMetricFormTable';
import MetricFieldFormTable from '../../components/MetricFieldFormTable';
import DimensionAndMetricRelationModal from '../../components/DimensionAndMetricRelationModal';
@@ -38,9 +37,12 @@ import { createMetric, updateMetric, mockMetricAlias, getMetricTags } from '../.
import { MetricSettingKey, MetricSettingWording } from '../constants';
import { ISemantic } from '../../data';
import { history } from '@umijs/max';
import { toDomainList, toModelList } from '@/pages/SemanticModel/utils';
import globalStyles from '@/global.less';
export type CreateFormProps = {
modelId: number;
domainId: number;
datasourceId?: number;
metricItem: any;
settingKey: MetricSettingKey;
@@ -59,6 +61,8 @@ const queryParamsTypeParamsKey = {
};
const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
modelId,
domainId,
datasourceId,
onCancel,
settingKey,
@@ -66,9 +70,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
onSubmit,
}) => {
const isEdit = !!metricItem?.id;
const domainId = metricItem?.domainId;
const modelId = metricItem?.modelId;
const [currentStep, setCurrentStep] = useState(0);
const formValRef = useRef({} as any);
const [form] = Form.useForm();
const updateFormVal = (val: any) => {
@@ -383,6 +384,9 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
}
message.success('编辑指标成功');
onSubmit?.(queryParams);
if (!isEdit) {
toModelList(domainId, modelId!, 'metric');
}
return;
}
message.error(msg);
@@ -482,10 +486,11 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
};
const renderContent = () => {
if (settingKey === MetricSettingKey.SQL_CONFIG) {
return (
return (
<>
<div
style={{
display: settingKey === MetricSettingKey.SQL_CONFIG ? 'block' : 'none',
marginLeft: '-24px',
}}
>
@@ -609,211 +614,211 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
</>
)}
</div>
);
}
return (
<>
<FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" />
</FormItem>
<Row gutter={20}>
<Col span={12}>
<FormItem
name="name"
label="指标名称"
rules={[{ required: true, message: '请输入指标名称' }]}
>
<Input placeholder="名称不可重复" />
</FormItem>
</Col>
<Col span={12}>
<FormItem
name="bizName"
label="英文名称"
rules={[{ required: true, message: '请输入英文名称' }]}
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<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>
</Col>
<Col span={12}>
<FormItem name="classifications" label="分类">
<Select
mode="tags"
placeholder="支持手动输入及选择"
tokenSeparators={[',']}
maxTagCount={9}
options={tagOptions}
/>
</FormItem>
</Col>
</Row>
<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="请输入业务口径" style={{ minHeight: 173 }} />
</FormItem>
<FormItem label="别名">
<div style={{ display: settingKey === MetricSettingKey.BASIC ? 'block' : 'none' }}>
<FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" />
</FormItem>
<Row gutter={20}>
<Col flex="1 1 200px">
<FormItem name="alias" noStyle>
<Col span={12}>
<FormItem
name="name"
label="指标名称"
rules={[{ required: true, message: '请输入指标名称' }]}
>
<Input placeholder="名称不可重复" />
</FormItem>
</Col>
<Col span={12}>
<FormItem
name="bizName"
label="英文名称"
rules={[{ required: true, message: '请输入英文名称' }]}
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<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>
</Col>
<Col span={12}>
<FormItem name="classifications" label="分类">
<Select
style={{ maxWidth: 500 }}
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
placeholder="支持手动输入及选择"
tokenSeparators={[',']}
maxTagCount={9}
options={tagOptions}
/>
</FormItem>
</Col>
{isEdit && (
<Col flex="0 1 75px">
<Tooltip title="智能填充将根据指标相关信息,使用大语言模型获取指标别名">
<Button
type="primary"
loading={llmLoading}
style={{ top: '5px' }}
onClick={() => {
generatorMetricAlias();
}}
>
</Button>
</Tooltip>
</Col>
)}
</Row>
</FormItem>
<Divider />
<FormItem
name="isTag"
valuePropName="checked"
hidden={!!!process.env.SHOW_TAG}
getValueFromEvent={(value) => {
return value === true ? 1 : 0;
}}
getValueProps={(value) => {
return {
checked: value === 1,
};
}}
>
<Row gutter={20}>
<Col flex="1 1 200px">
<FormItemTitle
title={`设为标签`}
subTitle={`如果勾选,代表取值都是一种'标签',可用作对实体的圈选`}
/>
</Col>
<Col flex="0 1 75px">
<Switch />
</Col>
</Row>
<Divider />
</FormItem>
<FormItem>
<Row gutter={20}>
<Col flex="1 1 200px">
<FormItemTitle
title={'下钻维度配置'}
subTitle={'配置下钻维度后,将可以在指标卡中进行下钻'}
/>
</Col>
<Col flex="0 1 75px">
<Button
type="primary"
onClick={() => {
setMetricRelationModalOpenState(true);
}}
>
</Button>
</Col>
</Row>
</FormItem>
<Divider />
<FormItem label={<FormItemTitle title={'数据格式化'} />} 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
name="description"
label={
<FormItemTitle
title={'小数位数'}
subTitle={`对小数位数进行设置如保留两位0.021252 -> 0.02${
isPercentState ? '%' : ''
}`}
<TableTitleTooltips
title="业务口径"
overlayInnerStyle={{ width: 600 }}
tooltips={
<>
<p>
使使
</p>
<p>1. </p>
<p>2. </p>
<p>
3. 使使
</p>
<p>4. </p>
<p>
便
</p>
</>
}
/>
}
name={['dataFormat', 'decimalPlaces']}
rules={[{ required: true, message: '请输入业务口径' }]}
>
<InputNumber placeholder="请输入需要保留小数位数" style={{ width: '300px' }} />
<TextArea placeholder="请输入业务口径" style={{ minHeight: 173 }} />
</FormItem>
)}
{isPercentState && (
<>
<FormItem label="别名">
<Row gutter={20}>
<Col flex="1 1 200px">
<FormItem name="alias" noStyle>
<Select
style={{ maxWidth: 500 }}
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem>
</Col>
{isEdit && (
<Col flex="0 1 75px">
<Tooltip title="智能填充将根据指标相关信息,使用大语言模型获取指标别名">
<Button
type="primary"
loading={llmLoading}
style={{ top: '5px' }}
onClick={() => {
generatorMetricAlias();
}}
>
</Button>
</Tooltip>
</Col>
)}
</Row>
</FormItem>
<Divider />
<FormItem
name="isTag"
valuePropName="checked"
hidden={!!!process.env.SHOW_TAG}
getValueFromEvent={(value) => {
return value === true ? 1 : 0;
}}
getValueProps={(value) => {
return {
checked: value === 1,
};
}}
>
<Row gutter={20}>
<Col flex="1 1 200px">
<FormItemTitle
title={`设为标签`}
subTitle={`如果勾选,代表取值都是一种'标签',可用作对实体的圈选`}
/>
</Col>
<Col flex="0 1 75px">
<Switch />
</Col>
</Row>
<Divider />
</FormItem>
<FormItem>
<Row gutter={20}>
<Col flex="1 1 200px">
<FormItemTitle
title={'下钻维度配置'}
subTitle={'配置下钻维度后,将可以在指标卡中进行下钻'}
/>
</Col>
<Col flex="0 1 75px">
<Button
type="primary"
onClick={() => {
setMetricRelationModalOpenState(true);
}}
>
</Button>
</Col>
</Row>
</FormItem>
<Divider />
<FormItem label={<FormItemTitle title={'数据格式化'} />} 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={'原始值是否乘以100'}
subTitle={'如 原始值0.001 ->展示值0.1% '}
title={'小数位数'}
subTitle={`对小数位数进行设置如保留两位0.021252 -> 0.02${
isPercentState ? '%' : ''
}`}
/>
}
name={['dataFormat', 'needMultiply100']}
valuePropName="checked"
name={['dataFormat', 'decimalPlaces']}
>
<Switch />
<InputNumber placeholder="请输入需要保留小数位数" style={{ width: '300px' }} />
</FormItem>
</>
)}
)}
{isPercentState && (
<>
<FormItem
label={
<FormItemTitle
title={'原始值是否乘以100'}
subTitle={'如 原始值0.001 ->展示值0.1% '}
/>
}
name={['dataFormat', 'needMultiply100']}
valuePropName="checked"
>
<Switch />
</FormItem>
</>
)}
</div>
</>
);
};
@@ -827,7 +832,10 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
<span style={{ flex: 'auto' }}>{MetricSettingWording[settingKey]}</span>
<span style={{ flex: 'none' }}>
<Button
<Button type="primary" onClick={handleSave}>
</Button>
{/* <Button
size="middle"
type="link"
key="backListBtn"
@@ -839,7 +847,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
<ArrowLeftOutlined />
返回列表页
</Space>
</Button>
</Button> */}
</span>
</div>
<div className={styles.infoCardContainer}>
@@ -870,13 +878,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
{renderContent()}
</Form>
</div>
<div className={styles.infoCardFooter}>
<div className={styles.infoCardFooterContainer}>
<Button type="primary" onClick={handleSave}>
</Button>
</div>
</div>
</div>
<DimensionAndMetricRelationModal
metricItem={metricItem}
@@ -898,15 +899,13 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
<Result
style={{ background: '#fff' }}
status="warning"
subTitle="当前数据缺少度量,无法创建指标。请前往数据源配置中,将字段设置为度量"
subTitle="当前数据模型缺少度量,无法创建指标。请前往模型配置中,将字段设置为度量"
extra={
<Button
type="primary"
key="console"
onClick={() => {
history.replace(
`/model/domain/manager/${domainId}/${modelId || metricItem?.modelId}/dataSource`,
);
toDomainList(domainId, 'menuKey');
onCancel?.();
}}
>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Outlet } from 'umi';
import { Outlet } from '@umijs/max';
const market: React.FC = () => {
return (

View File

@@ -183,68 +183,7 @@
}
}
.metricEditWrapper {
.metricDetailTab {
:global {
.ant-tabs-nav {
margin: 10px 20px 0 20px;
padding: 0 20px;
background-color: rgb(255, 255, 255);
border-radius: 8px;
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}
.ant-tabs-tab {
padding: 12px 0;
color: #344767;
font-weight: 500;
}
}
}
.metricDetail {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
padding: 0px;
background-color: transparent;
height: 100%;
.tabContainer {
padding: 24px;
min-height: calc(100vh - 78px);
width: calc(100vw - 350px);
background-color: #fafafb;
}
.metricInfoContent {
padding: 25px;
.title {
position: relative;
margin-bottom: 12px;
color: #0e73ff;
font-weight: bold;
font-size: 16px;
&::before {
position: absolute;
top: 10px;
left: -10px;
display: block;
width: 3px;
height: 14px;
font-size: 0;
background: #0e73ff;
border: 1px solid #0e73ff;
border-radius: 2px;
content: '';
}
}
}
.siderContainer {
width: 350px;
min-height: calc(100vh - 78px);
border-radius: 6px;
padding: 24px 0 24px 24px;
}
}
}
.metricDetailWrapper {
height: calc(100vh - 56px);
@@ -276,7 +215,8 @@
.tabContainer {
height: 100%;
min-height: calc(100vh - 78px);
width: calc(100vw - 450px);
// width: calc(100vw - 450px);
width: 100%;
background-color: #fafafb;
}
.metricInfoContent {
@@ -305,9 +245,8 @@
.siderContainer {
width: 450px;
min-height: calc(100vh - 78px);
margin: 10px 20px 20px 0;
background-color: rgb(255, 255, 255);
border-radius: 6px;
padding: 10px 0 24px 24px;
box-shadow: rgba(0, 0, 0, 0.08) 6px 0px 16px 0px, rgba(0, 0, 0, 0.12) 3px 0px 6px -4px,
rgba(0, 0, 0, 0.05) 9px 0px 28px 8px;
}
@@ -326,262 +265,5 @@
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}
.metricInfoSider {
padding: 20px;
color: #344767;
background-color: #fff;
height: 100%;
border: 1px solid #e6ebf1;
border-radius: 6px;
.createTitle {
margin-bottom: 10px;
color:#344767;
font-weight: 500;
font-size: 16px;
font-family: var(--tencent-font-family);
}
.gotoMetricListIcon {
color: #3182ce;
cursor: pointer;
&:hover {
color: #5493ff;
}
}
.title {
margin-bottom: 20px;
.name {
font-weight: 600;
font-size: 18px;
}
.bizName {
margin: 5px 0 0 25px;
color: #7b809a;
font-weight: 400;
}
}
.desc {
display: block;
margin-top: 8px;
color: #7b809a;
font-weight: 500;
font-size: 14px;
line-height: 1.9;
}
.subTitle {
margin: 0px;
color: rgb(123, 128, 154);
font-weight: 700;
font-size: 14px;
line-height: 1.25;
letter-spacing: 0.03333em;
text-transform: uppercase;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.sectionContainer {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
overflow: scroll;
overflow: hidden;
background-image: none;
border-radius: 6px;
.section {
padding: 16px;
color: rgb(52, 71, 103);
line-height: 1.25;
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitleBox {
padding: 8px 0;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitle {
margin: 0px;
color: rgb(52, 71, 103);
font-weight: 600;
font-size: 16px;
line-height: 1.625;
letter-spacing: 0.0075em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
.item {
display: flex;
padding-top: 8px;
padding-right: 16px;
padding-bottom: 8px;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.itemLable {
min-width: fit-content;
margin: 0px;
margin-right: 10px;
color: #344767;
font-weight: 700;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.itemValue {
margin: 0px;
color: #7b809a;
font-weight: 400;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: none;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
}
.hr {
flex-shrink: 0;
margin: 0px;
border-color: rgb(242, 244, 247);
// border-width: 0px 0px thin;
border-style: solid;
}
.ctrlBox {
.ctrlList {
position: relative;
margin: 0px;
padding: 8px 0px;
list-style: none;
background-color: rgb(249, 250, 251);
li {
position: relative;
display: flex;
flex-grow: 1;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
min-width: 0px;
margin: 4px;
padding: 4px 16px;
color: inherit;
text-align: left;
text-decoration: none;
vertical-align: middle;
background-color: transparent;
border: 0px;
border-radius: 0px;
outline: 0px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
appearance: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-box-flex: 1;
-webkit-box-pack: start;
-webkit-box-align: center;
&:hover {
color: #3182ce;
text-decoration: none;
background-color: rgba(16, 24, 40, 0.04);
}
}
.ctrlItemIcon {
flex-shrink: 0;
min-width: unset;
margin-right: 5px;
font-size: 14px;
}
.styles.ctrlItemLable {
display: block;
margin: 0px;
font-weight: 400;
font-size: 14px;
line-height: 1.6;
}
}
}
}
}
.settingList {
list-style: none;
margin: 0px;
position: relative;
padding: 0px;
li {
-webkit-tap-highlight-color: transparent;
background-color: transparent;
outline: 0px;
border: 0px;
margin: 0px;
border-radius: 0px;
cursor: pointer;
user-select: none;
vertical-align: middle;
appearance: none;
display: flex;
flex-grow: 1;
justify-content: flex-start;
align-items: center;
position: relative;
text-decoration: none;
min-width: 0px;
box-sizing: border-box;
text-align: left;
padding: 8px 16px;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
&.active {
background-color: rgba(22, 119, 255, 0.08);
.icon {
color: rgb(22, 119, 255);
}
.content {
.text {
color: rgb(22, 119, 255);
}
}
}
.icon {
min-width: 32px;
color: #344767;
flex-shrink: 0;
display: inline-flex;
}
.content {
flex: 1 1 auto;
min-width: 0px;
margin-top: 4px;
margin-bottom: 4px;
.text {
margin: 0px;
color: #344767;
font-size: 16px;
// line-height: 1.57;
// font-family: var(--tencent-font-family);
font-weight: 600;
display: block;
}
}
&:hover {
text-decoration: none;
background-color: rgba(0, 0, 0, 0.04);
}
}
}