mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-10 19:51:00 +00:00
[improvement][semantic-fe] metric market experience optimization (#109)
* [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
This commit is contained in:
@@ -81,7 +81,7 @@ const MetricFilter: React.FC<Props> = ({ filterValues = {}, onFiltersChange }) =
|
|||||||
<FormItem name="key" noStyle>
|
<FormItem name="key" noStyle>
|
||||||
<div className={styles.searchInput}>
|
<div className={styles.searchInput}>
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder="请输入需要查询指标的ID、指标名称、字段名称"
|
placeholder="请输入需要查询指标的ID、指标名称、字段名称、标签"
|
||||||
enterButton={<SearchOutlined style={{ marginTop: 5 }} />}
|
enterButton={<SearchOutlined style={{ marginTop: 5 }} />}
|
||||||
onSearch={(value) => {
|
onSearch={(value) => {
|
||||||
onSearch(value);
|
onSearch(value);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
||||||
import ProTable from '@ant-design/pro-table';
|
import ProTable from '@ant-design/pro-table';
|
||||||
import { message, Space, Popconfirm } from 'antd';
|
import { message, Space, Popconfirm, Tag } from 'antd';
|
||||||
import React, { useRef, useState, useEffect } from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import type { Dispatch } from 'umi';
|
import type { Dispatch } from 'umi';
|
||||||
import { connect } from 'umi';
|
import { connect, history } from 'umi';
|
||||||
import type { StateType } from '../model';
|
import type { StateType } from '../model';
|
||||||
import { SENSITIVE_LEVEL_ENUM } from '../constant';
|
import { SENSITIVE_LEVEL_ENUM } from '../constant';
|
||||||
import { queryMetric, deleteMetric } from '../service';
|
import { queryMetric, deleteMetric } from '../service';
|
||||||
@@ -89,6 +89,20 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
{
|
{
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
title: '指标名称',
|
title: '指标名称',
|
||||||
|
render: (_, record: any) => {
|
||||||
|
if (record.hasAdminRes) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
history.replace(`/model/${record.domainId}/${record.modelId}/metric`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{record.name}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <> {record.name}</>;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// dataIndex: 'alias',
|
// dataIndex: 'alias',
|
||||||
@@ -113,6 +127,25 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
title: '创建人',
|
title: '创建人',
|
||||||
search: false,
|
search: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'tags',
|
||||||
|
title: '标签',
|
||||||
|
search: false,
|
||||||
|
render: (tags) => {
|
||||||
|
if (Array.isArray(tags)) {
|
||||||
|
return (
|
||||||
|
<Space size={2}>
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<Tag color="blue" key={tag}>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <>--</>;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
title: '描述',
|
title: '描述',
|
||||||
@@ -140,43 +173,47 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
dataIndex: 'x',
|
dataIndex: 'x',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return (
|
if (record.hasAdminRes) {
|
||||||
<Space>
|
return (
|
||||||
<a
|
<Space>
|
||||||
key="metricEditBtn"
|
|
||||||
onClick={() => {
|
|
||||||
setMetricItem(record);
|
|
||||||
setCreateModalVisible(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<Popconfirm
|
|
||||||
title="确认删除?"
|
|
||||||
okText="是"
|
|
||||||
cancelText="否"
|
|
||||||
onConfirm={async () => {
|
|
||||||
const { code, msg } = await deleteMetric(record.id);
|
|
||||||
if (code === 200) {
|
|
||||||
setMetricItem(undefined);
|
|
||||||
actionRef.current?.reload();
|
|
||||||
} else {
|
|
||||||
message.error(msg);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<a
|
<a
|
||||||
key="metricDeleteBtn"
|
key="metricEditBtn"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMetricItem(record);
|
setMetricItem(record);
|
||||||
|
setCreateModalVisible(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除
|
编辑
|
||||||
</a>
|
</a>
|
||||||
</Popconfirm>
|
|
||||||
</Space>
|
<Popconfirm
|
||||||
);
|
title="确认删除?"
|
||||||
|
okText="是"
|
||||||
|
cancelText="否"
|
||||||
|
onConfirm={async () => {
|
||||||
|
const { code, msg } = await deleteMetric(record.id);
|
||||||
|
if (code === 200) {
|
||||||
|
setMetricItem(undefined);
|
||||||
|
queryMetricList();
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
key="metricDeleteBtn"
|
||||||
|
onClick={() => {
|
||||||
|
setMetricItem(record);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</a>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -239,7 +276,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
metricItem={metricItem}
|
metricItem={metricItem}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
setCreateModalVisible(false);
|
setCreateModalVisible(false);
|
||||||
actionRef?.current?.reload();
|
queryMetricList();
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'domainManger/queryMetricList',
|
type: 'domainManger/queryMetricList',
|
||||||
payload: {
|
payload: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// margin-bottom: 12px;
|
// margin-bottom: 12px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
width: 500px;
|
width: 540px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
.searchInput {
|
.searchInput {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
||||||
import ProTable from '@ant-design/pro-table';
|
import ProTable from '@ant-design/pro-table';
|
||||||
import { message, Button, Space, Popconfirm, Input } from 'antd';
|
import { message, Button, Space, Popconfirm, Input, Tag } from 'antd';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import type { Dispatch } from 'umi';
|
import type { Dispatch } from 'umi';
|
||||||
import { connect } from 'umi';
|
import { connect } from 'umi';
|
||||||
@@ -76,7 +76,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
dataIndex: 'key',
|
dataIndex: 'key',
|
||||||
title: '指标搜索',
|
title: '指标搜索',
|
||||||
hideInTable: true,
|
hideInTable: true,
|
||||||
renderFormItem: () => <Input placeholder="请输入ID/指标名称/字段名称" />,
|
renderFormItem: () => <Input placeholder="请输入ID/指标名称/字段名称/标签" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dataIndex: 'alias',
|
dataIndex: 'alias',
|
||||||
@@ -101,6 +101,25 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
|||||||
title: '创建人',
|
title: '创建人',
|
||||||
search: false,
|
search: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'tags',
|
||||||
|
title: '标签',
|
||||||
|
search: false,
|
||||||
|
render: (tags) => {
|
||||||
|
if (Array.isArray(tags)) {
|
||||||
|
return (
|
||||||
|
<Space size={2} wrap>
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<Tag color="blue" key={tag}>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <>--</>;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
title: '描述',
|
title: '描述',
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
|||||||
import styles from './style.less';
|
import styles from './style.less';
|
||||||
import { getMeasureListByModelId } from '../service';
|
import { getMeasureListByModelId } from '../service';
|
||||||
import TableTitleTooltips from '../components/TableTitleTooltips';
|
import TableTitleTooltips from '../components/TableTitleTooltips';
|
||||||
import { creatExprMetric, updateExprMetric, mockMetricAlias } from '../service';
|
import { creatExprMetric, updateExprMetric, mockMetricAlias, getMetricTags } from '../service';
|
||||||
import { ISemantic } from '../data';
|
import { ISemantic } from '../data';
|
||||||
import { history } from 'umi';
|
import { history } from 'umi';
|
||||||
|
|
||||||
@@ -75,6 +75,8 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
const [hasMeasuresState, setHasMeasuresState] = useState<boolean>(true);
|
const [hasMeasuresState, setHasMeasuresState] = useState<boolean>(true);
|
||||||
const [llmLoading, setLlmLoading] = useState<boolean>(false);
|
const [llmLoading, setLlmLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [tagOptions, setTagOptions] = useState<{ label: string; value: string }[]>([]);
|
||||||
|
|
||||||
const forward = () => setCurrentStep(currentStep + 1);
|
const forward = () => setCurrentStep(currentStep + 1);
|
||||||
const backward = () => setCurrentStep(currentStep - 1);
|
const backward = () => setCurrentStep(currentStep - 1);
|
||||||
|
|
||||||
@@ -95,6 +97,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
queryClassMeasureList();
|
queryClassMeasureList();
|
||||||
|
queryMetricTags();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleNext = async () => {
|
const handleNext = async () => {
|
||||||
@@ -126,6 +129,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
dataFormat,
|
dataFormat,
|
||||||
dataFormatType,
|
dataFormatType,
|
||||||
alias,
|
alias,
|
||||||
|
tags,
|
||||||
} = metricItem as any;
|
} = metricItem as any;
|
||||||
const isPercent = dataFormatType === 'percent';
|
const isPercent = dataFormatType === 'percent';
|
||||||
const isDecimal = dataFormatType === 'decimal';
|
const isDecimal = dataFormatType === 'decimal';
|
||||||
@@ -135,6 +139,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
bizName,
|
bizName,
|
||||||
sensitiveLevel,
|
sensitiveLevel,
|
||||||
description,
|
description,
|
||||||
|
tags,
|
||||||
// isPercent,
|
// isPercent,
|
||||||
dataFormatType: dataFormatType || '',
|
dataFormatType: dataFormatType || '',
|
||||||
alias: alias && alias.trim() ? alias.split(',') : [],
|
alias: alias && alias.trim() ? alias.split(',') : [],
|
||||||
@@ -204,6 +209,22 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 renderContent = () => {
|
const renderContent = () => {
|
||||||
if (currentStep === 1) {
|
if (currentStep === 1) {
|
||||||
return (
|
return (
|
||||||
@@ -277,6 +298,15 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
|||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem name="tags" label="标签">
|
||||||
|
<Select
|
||||||
|
mode="tags"
|
||||||
|
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
|
||||||
|
tokenSeparators={[',']}
|
||||||
|
maxTagCount={9}
|
||||||
|
options={tagOptions}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
name="sensitiveLevel"
|
name="sensitiveLevel"
|
||||||
label="敏感度"
|
label="敏感度"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const PermissionAdminForm: React.FC<Props> = ({
|
|||||||
...data,
|
...data,
|
||||||
};
|
};
|
||||||
fieldsValue.admins = fieldsValue.admins || [];
|
fieldsValue.admins = fieldsValue.admins || [];
|
||||||
|
fieldsValue.adminOrgs = fieldsValue.adminOrgs || [];
|
||||||
fieldsValue.viewers = fieldsValue.viewers || [];
|
fieldsValue.viewers = fieldsValue.viewers || [];
|
||||||
fieldsValue.viewOrgs = fieldsValue.viewOrgs || [];
|
fieldsValue.viewOrgs = fieldsValue.viewOrgs || [];
|
||||||
fieldsValue.isOpen = !!fieldsValue.isOpen;
|
fieldsValue.isOpen = !!fieldsValue.isOpen;
|
||||||
@@ -57,10 +58,11 @@ const PermissionAdminForm: React.FC<Props> = ({
|
|||||||
|
|
||||||
const saveAuth = async () => {
|
const saveAuth = async () => {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
const { admins, isOpen, viewOrgs = [], viewers = [] } = values;
|
const { admins, adminOrgs, isOpen, viewOrgs = [], viewers = [] } = values;
|
||||||
const queryClassData = {
|
const queryClassData = {
|
||||||
...classDetail,
|
...classDetail,
|
||||||
admins,
|
admins,
|
||||||
|
adminOrgs,
|
||||||
viewOrgs,
|
viewOrgs,
|
||||||
viewers,
|
viewers,
|
||||||
isOpen: isOpen ? 1 : 0,
|
isOpen: isOpen ? 1 : 0,
|
||||||
@@ -100,7 +102,16 @@ const PermissionAdminForm: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<SelectTMEPerson placeholder="请邀请团队成员" />
|
<SelectTMEPerson placeholder="请邀请团队成员" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
{APP_TARGET === 'inner' && (
|
||||||
|
<FormItem name="adminOrgs" label="按组织">
|
||||||
|
<SelectPartner
|
||||||
|
type="selectedDepartment"
|
||||||
|
treeSelectProps={{
|
||||||
|
placeholder: '请选择需要授权的部门',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={
|
label={
|
||||||
<FormItemTitle
|
<FormItemTitle
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ export function mockMetricAlias(data: any): Promise<any> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMetricTags(): Promise<any> {
|
||||||
|
return request.get(`${process.env.API_BASE_URL}metric/getMetricTags`);
|
||||||
|
}
|
||||||
|
|
||||||
export function getMeasureListByModelId(modelId: number): Promise<any> {
|
export function getMeasureListByModelId(modelId: number): Promise<any> {
|
||||||
return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfModel/${modelId}`);
|
return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfModel/${modelId}`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user