mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-18 00:07:21 +00:00
[improvement][semantic-fe] enhance the analysis of metric trends (#234)
* [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
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Button } from 'antd';
|
||||
import DimensionMetricRelationTableTransfer from './DimensionMetricRelationTableTransfer';
|
||||
import { ISemantic } from '../data';
|
||||
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
|
||||
type Props = {
|
||||
onCancel: () => void;
|
||||
open: boolean;
|
||||
relationsInitialValue?: ISemantic.IDrillDownDimensionItem[];
|
||||
onSubmit: (relations: ISemantic.IDrillDownDimensionItem[]) => void;
|
||||
};
|
||||
|
||||
const DimensionAndMetricRelationModal: React.FC<Props> = ({
|
||||
open,
|
||||
relationsInitialValue,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [relationList, setRelationList] = useState<ISemantic.IDrillDownDimensionItem[]>([]);
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
onSubmit(relationList);
|
||||
}}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
width={1200}
|
||||
destroyOnClose
|
||||
title={
|
||||
<FormItemTitle
|
||||
title={'维度关联'}
|
||||
subTitle={'注意:完成指标信息更新后,维度关联配置信息才会被保存'}
|
||||
/>
|
||||
}
|
||||
maskClosable={false}
|
||||
open={open}
|
||||
footer={renderFooter()}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<DimensionMetricRelationTableTransfer
|
||||
relationsInitialValue={relationsInitialValue}
|
||||
onChange={(relations: ISemantic.IDrillDownDimensionItem[]) => {
|
||||
setRelationList(relations);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DimensionAndMetricRelationModal;
|
||||
@@ -0,0 +1,233 @@
|
||||
import { Table, Transfer, Checkbox } from 'antd';
|
||||
import type { ColumnsType, TableRowSelection } from 'antd/es/table/interface';
|
||||
import type { TransferItem } from 'antd/es/transfer';
|
||||
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
|
||||
import difference from 'lodash/difference';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { connect } from 'umi';
|
||||
import type { StateType } from '../model';
|
||||
import TransTypeTag from './TransTypeTag';
|
||||
import TableTitleTooltips from '../components/TableTitleTooltips';
|
||||
import { ISemantic } from '../data';
|
||||
import { SemanticNodeType, TransType } from '../enum';
|
||||
|
||||
interface RecordType {
|
||||
id: number;
|
||||
key: string;
|
||||
name: string;
|
||||
transType: TransType.DIMENSION | TransType.METRIC;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
domainManger: StateType;
|
||||
relationsInitialValue?: ISemantic.IDrillDownDimensionItem[];
|
||||
onChange: (relations: ISemantic.IDrillDownDimensionItem[]) => void;
|
||||
};
|
||||
|
||||
const DimensionMetricRelationTableTransfer: React.FC<Props> = ({
|
||||
domainManger,
|
||||
relationsInitialValue,
|
||||
onChange,
|
||||
}) => {
|
||||
const { dimensionList } = domainManger;
|
||||
|
||||
const [targetKeys, setTargetKeys] = useState<string[]>([]);
|
||||
|
||||
const [checkedMap, setCheckedMap] = useState<Record<string, ISemantic.IDrillDownDimensionItem>>(
|
||||
{},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!Array.isArray(relationsInitialValue)) {
|
||||
return;
|
||||
}
|
||||
const ids = relationsInitialValue.map((item) => `${item.dimensionId}`);
|
||||
const relationMap = relationsInitialValue.reduce((relationCheckedMap, item: any) => {
|
||||
const { dimensionId, necessary } = item;
|
||||
relationCheckedMap[dimensionId] = {
|
||||
dimensionId: Number(dimensionId),
|
||||
necessary: necessary,
|
||||
};
|
||||
return relationCheckedMap;
|
||||
}, {});
|
||||
setCheckedMap(relationMap);
|
||||
setTargetKeys(ids);
|
||||
}, [relationsInitialValue]);
|
||||
|
||||
const updateRelationCheckedMap = (
|
||||
record: RecordType,
|
||||
updateData: ISemantic.IDrillDownDimensionItem,
|
||||
) => {
|
||||
const { id } = record;
|
||||
const relationCheckedMap = {
|
||||
...checkedMap,
|
||||
};
|
||||
const target = relationCheckedMap[id];
|
||||
if (target) {
|
||||
relationCheckedMap[id] = {
|
||||
...target,
|
||||
...updateData,
|
||||
};
|
||||
} else {
|
||||
relationCheckedMap[id] = {
|
||||
...updateData,
|
||||
};
|
||||
}
|
||||
setCheckedMap(relationCheckedMap);
|
||||
handleRealtionChange(targetKeys, relationCheckedMap);
|
||||
};
|
||||
|
||||
const handleRealtionChange = (
|
||||
targetKeys: string[],
|
||||
relationCheckedMap: Record<string, ISemantic.IDrillDownDimensionItem>,
|
||||
) => {
|
||||
const relations = targetKeys.reduce(
|
||||
(relationList: ISemantic.IDrillDownDimensionItem[], dimensionId: string) => {
|
||||
const target = relationCheckedMap[dimensionId];
|
||||
if (target) {
|
||||
relationList.push(target);
|
||||
} else {
|
||||
relationList.push({
|
||||
dimensionId: Number(dimensionId),
|
||||
necessary: false,
|
||||
});
|
||||
}
|
||||
return relationList;
|
||||
},
|
||||
[],
|
||||
);
|
||||
onChange?.(relations);
|
||||
};
|
||||
|
||||
const rightColumns: ColumnsType<RecordType> = [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '名称',
|
||||
},
|
||||
{
|
||||
dataIndex: 'transType',
|
||||
width: 80,
|
||||
title: '类型',
|
||||
render: (transType: SemanticNodeType) => {
|
||||
return <TransTypeTag type={transType} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'y',
|
||||
title: (
|
||||
<TableTitleTooltips
|
||||
title="是否绑定"
|
||||
tooltips="若勾选绑定,则在查询该指标数据时必须结合该维度进行查询"
|
||||
/>
|
||||
),
|
||||
width: 120,
|
||||
render: (_: any, record: RecordType) => {
|
||||
const { transType, id } = record;
|
||||
return transType === TransType.DIMENSION ? (
|
||||
<Checkbox
|
||||
checked={checkedMap[id]?.necessary}
|
||||
onChange={(e: CheckboxChangeEvent) => {
|
||||
updateRelationCheckedMap(record, { dimensionId: id, necessary: e.target.checked });
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const leftColumns: ColumnsType<RecordType> = [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '名称',
|
||||
},
|
||||
{
|
||||
dataIndex: 'transType',
|
||||
title: '类型',
|
||||
render: (transType) => {
|
||||
return <TransTypeTag type={transType} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transfer
|
||||
showSearch
|
||||
titles={['未关联维度', '已关联维度']}
|
||||
dataSource={dimensionList.map((item) => {
|
||||
const transType = TransType.DIMENSION;
|
||||
const { id } = item;
|
||||
return {
|
||||
...item,
|
||||
transType,
|
||||
key: `${id}`,
|
||||
};
|
||||
})}
|
||||
listStyle={{
|
||||
width: 500,
|
||||
height: 600,
|
||||
}}
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { name } = item;
|
||||
if (name.includes(inputValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
targetKeys={targetKeys}
|
||||
onChange={(newTargetKeys: string[]) => {
|
||||
setTargetKeys(newTargetKeys);
|
||||
handleRealtionChange(newTargetKeys, checkedMap);
|
||||
}}
|
||||
>
|
||||
{({
|
||||
direction,
|
||||
filteredItems,
|
||||
onItemSelectAll,
|
||||
onItemSelect,
|
||||
selectedKeys: listSelectedKeys,
|
||||
}) => {
|
||||
const columns = direction === 'left' ? leftColumns : rightColumns;
|
||||
const rowSelection: TableRowSelection<TransferItem> = {
|
||||
onSelectAll(selected, selectedRows) {
|
||||
const treeSelectedKeys = selectedRows.map(({ key }) => key);
|
||||
const diffKeys = selected
|
||||
? difference(treeSelectedKeys, listSelectedKeys)
|
||||
: difference(listSelectedKeys, treeSelectedKeys);
|
||||
onItemSelectAll(diffKeys as string[], selected);
|
||||
},
|
||||
onSelect({ key }, selected) {
|
||||
onItemSelect(key as string, selected);
|
||||
},
|
||||
selectedRowKeys: listSelectedKeys,
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
rowSelection={rowSelection}
|
||||
columns={columns}
|
||||
dataSource={filteredItems as any}
|
||||
size="small"
|
||||
pagination={false}
|
||||
scroll={{ y: 450 }}
|
||||
onRow={({ key }) => ({
|
||||
onClick: () => {
|
||||
onItemSelect(key as string, !listSelectedKeys.includes(key as string));
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Transfer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(DimensionMetricRelationTableTransfer);
|
||||
@@ -46,8 +46,8 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
return;
|
||||
}
|
||||
const queryParams = {
|
||||
...dimensionItem,
|
||||
domainId: selectDomainId,
|
||||
id: dimensionItem.id,
|
||||
...fieldsValue,
|
||||
};
|
||||
const { code, msg } = await updateDimension(queryParams);
|
||||
|
||||
@@ -23,6 +23,7 @@ import { formLayout } from '@/components/FormHelper/utils';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
import styles from './style.less';
|
||||
import { getMeasureListByModelId } from '../service';
|
||||
import DimensionAndMetricRelationModal from './DimensionAndMetricRelationModal';
|
||||
import TableTitleTooltips from '../components/TableTitleTooltips';
|
||||
import { creatExprMetric, updateExprMetric, mockMetricAlias, getMetricTags } from '../service';
|
||||
import { ISemantic } from '../data';
|
||||
@@ -77,6 +78,12 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
|
||||
const [tagOptions, setTagOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
|
||||
const [metricRelationModalOpenState, setMetricRelationModalOpenState] = useState<boolean>(false);
|
||||
|
||||
const [drillDownDimensions, setDrillDownDimensions] = useState<
|
||||
ISemantic.IDrillDownDimensionItem[]
|
||||
>(metricItem?.relateDimension?.drillDownDimensions || []);
|
||||
|
||||
const forward = () => setCurrentStep(currentStep + 1);
|
||||
const backward = () => setCurrentStep(currentStep - 1);
|
||||
|
||||
@@ -169,6 +176,10 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
const saveMetric = async (fieldsValue: any) => {
|
||||
const queryParams = {
|
||||
modelId: isEdit ? metricItem.modelId : modelId,
|
||||
relateDimension: {
|
||||
...(metricItem?.relateDimension || {}),
|
||||
drillDownDimensions,
|
||||
},
|
||||
...fieldsValue,
|
||||
};
|
||||
const { typeParams, alias, dataFormatType } = queryParams;
|
||||
@@ -346,6 +357,23 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
>
|
||||
<TextArea placeholder="请输入业务口径" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'下钻维度配置'}
|
||||
subTitle={'配置下钻维度后,将可以在指标卡中进行下钻'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setMetricRelationModalOpenState(true);
|
||||
}}
|
||||
>
|
||||
设 置
|
||||
</Button>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={
|
||||
<FormItemTitle
|
||||
@@ -362,22 +390,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
</Radio.Group>
|
||||
</FormItem>
|
||||
|
||||
{/* <FormItem
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'是否展示为百分比'}
|
||||
subTitle={'开启后,指标数据展示时会根据配置进行格式化,如0.02 -> 2%'}
|
||||
/>
|
||||
}
|
||||
name="isPercent"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch
|
||||
onChange={(checked) => {
|
||||
form.setFieldValue(['dataFormat', 'needMultiply100'], checked);
|
||||
}}
|
||||
/>
|
||||
</FormItem> */}
|
||||
{(isPercentState || isDecimalState) && (
|
||||
<FormItem
|
||||
label={
|
||||
@@ -486,6 +498,17 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
>
|
||||
{renderContent()}
|
||||
</Form>
|
||||
<DimensionAndMetricRelationModal
|
||||
relationsInitialValue={drillDownDimensions}
|
||||
open={metricRelationModalOpenState}
|
||||
onCancel={() => {
|
||||
setMetricRelationModalOpenState(false);
|
||||
}}
|
||||
onSubmit={(relations) => {
|
||||
setDrillDownDimensions(relations);
|
||||
setMetricRelationModalOpenState(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Result
|
||||
|
||||
Reference in New Issue
Block a user