[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:
tristanliu
2023-10-16 06:10:37 -05:00
committed by GitHub
parent 37bb9ff767
commit c5536aa25d
23 changed files with 2997 additions and 173 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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