mirror of
https://github.com/tencentmusic/supersonic.git
synced 2026-04-21 06:04:19 +08:00
[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:
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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}`);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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}>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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?.();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Outlet } from 'umi';
|
||||
import { Outlet } from '@umijs/max';
|
||||
|
||||
const market: React.FC = () => {
|
||||
return (
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user