[improvement][semantic-fe] add metric trend download functionality (#246)

* [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

* [improvement][semantic-fe] optimize the presentation of metric trend permissions

* [improvement][semantic-fe] add metric trend download functionality
This commit is contained in:
tristanliu
2023-10-17 05:35:24 -05:00
committed by GitHub
parent 968d50e071
commit 883cdbefbe
8 changed files with 142 additions and 104 deletions

View File

@@ -100,7 +100,7 @@
"sql-formatter": "^2.3.3",
"supersonic-chat-sdk": "0.0.0",
"umi": "3.5",
"umi-request": "^1.0.8"
"umi-request": "^1.4.0"
},
"devDependencies": {
"@ant-design/pro-cli": "^2.0.2",

View File

@@ -75,14 +75,6 @@ const MDatePicker: React.FC<Props> = ({
initialValues?.staticParams?.dateRangeType ||
DateRangeType.DAY,
);
// const [pickerType, setPickerType] = useState<PickerType>(() => {
// // if (staticFormData.dateRangeType) {
// // return DateRangeTypeToPickerMap[staticFormData.dateRangeType];
// // }
// return DateRangePicker.DATE;
// });
// const [dateRangeValue, setDateRangeValue] = useState<any>([]);
const staticDefaultConfig = {
dateSettingType: DateSettingType.STATIC,
@@ -92,8 +84,7 @@ const MDatePicker: React.FC<Props> = ({
dateMultiple: [],
dateRangeStringDesc: '',
};
// const { getMaxPartitionData } = useModel('useMaxPartitionData');
// const { globalViewId } = useModel('useViewsData');
const [latestDateMap, setLatestDateMap] = useState<LatestDateMap>({
maxPartition: moment().format('YYYY-MM-DD'),
});

View File

@@ -83,12 +83,17 @@ const MetricFilter: React.FC<Props> = ({ initFilterValues = {}, onFiltersChange
</Row>
</div>
</StandardFormRow>
<Space size={80}>
<Space size={40}>
<StandardFormRow key="showType" title="切换为卡片" block>
<FormItem name="showType" valuePropName="checked">
<Switch size="small" />
</FormItem>
</StandardFormRow>
<StandardFormRow key="onlyShowMe" title="仅显示我的" block>
<FormItem name="onlyShowMe" valuePropName="checked">
<Switch size="small" />
</FormItem>
</StandardFormRow>
<StandardFormRow key="domainIds" title="所属主题域" block>
<FormItem name="domainIds">
<DomainTreeSelect />

View File

@@ -1,10 +1,11 @@
import React, { useState, useEffect, useRef } from 'react';
import { SemanticNodeType } from '../../enum';
import moment from 'moment';
import { message } from 'antd';
import { queryStruct } from '@/pages/SemanticModel/service';
import { message, Row, Col, Button } from 'antd';
import { queryStruct, downloadCosFile } from '@/pages/SemanticModel/service';
import TrendChart from '@/pages/SemanticModel/Metric/components/MetricTrend';
import MDatePicker from '@/components/MDatePicker';
import { useModel } from 'umi';
import { DateRangeType, DateSettingType } from '@/components/MDatePicker/type';
import { ISemantic } from '../../data';
@@ -24,6 +25,7 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
const [metricTrendLoading, setMetricTrendLoading] = useState<boolean>(false);
const [metricColumnConfig, setMetricColumnConfig] = useState<ISemantic.IMetricTrendColumn>();
const [authMessage, setAuthMessage] = useState<string>('');
const [downloadLoding, setDownloadLoding] = useState<boolean>(false);
const [periodDate, setPeriodDate] = useState<{
startDate: string;
endDate: string;
@@ -34,17 +36,28 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
dateField: dateFieldMap[DateRangeType.DAY],
});
const getMetricTrendData = async () => {
const getMetricTrendData = async (download = false) => {
if (download) {
setDownloadLoding(true);
} else {
setMetricTrendLoading(true);
}
const { modelId, bizName, name } = nodeData;
indicatorFields.current = [{ name, column: bizName }];
const { code, data, msg } = await queryStruct({
const res = await queryStruct({
modelId,
bizName,
dateField: periodDate.dateField,
startDate: periodDate.startDate,
endDate: periodDate.endDate,
download,
});
if (download) {
setDownloadLoding(false);
return;
}
const { code, data, msg } = res;
setMetricTrendLoading(false);
if (code === 200) {
const { resultList, columns, queryAuthorization } = data;
@@ -78,6 +91,8 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
return (
<>
<div style={{ marginBottom: 5 }}>
<Row>
<Col flex="1 1 200px">
<MDatePicker
initialValues={{
dateSettingType: 'DYNAMIC',
@@ -107,13 +122,21 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
}}
disabledAdvanceSetting={true}
/>
</Col>
<Col flex="0 1">
<Button
type="primary"
loading={downloadLoding}
onClick={() => {
getMetricTrendData(true);
}}
>
</Button>
</Col>
</Row>
</div>
{authMessage && (
<div style={{ color: '#d46b08', marginBottom: 15 }}>
: {authMessage}
</div>
)}
<div style={{ color: '#d46b08', marginBottom: 15 }}>: {authMessage}</div>
{authMessage && <div style={{ color: '#d46b08', marginBottom: 15 }}>{authMessage}</div>}
<TrendChart
data={metricTrendData}
isPer={

View File

@@ -3,7 +3,7 @@ import ProTable from '@ant-design/pro-table';
import { message, Space, Popconfirm, Tag, Spin } from 'antd';
import React, { useRef, useState, useEffect } from 'react';
import type { Dispatch } from 'umi';
import { connect, history } from 'umi';
import { connect, history, useModel } from 'umi';
import type { StateType } from '../model';
import { SENSITIVE_LEVEL_ENUM } from '../constant';
import { queryMetric, deleteMetric } from '../service';
@@ -31,6 +31,10 @@ type QueryMetricListParams = {
};
const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { initialState = {} } = useModel('@@initialState');
const { currentUser = {} } = initialState as any;
const { selectDomainId, selectModelId: modelId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const defaultPagination = {
@@ -59,6 +63,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { code, data, msg } = await queryMetric({
...pagination,
...params,
createdBy: params.onlyShowMe ? currentUser.name : null,
pageSize: params.showType ? 100 : defaultPagination.pageSize,
});
setLoading(false);
@@ -184,15 +189,6 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
title: '描述',
search: false,
},
// {
// dataIndex: 'type',
// title: '指标类型',
// valueEnum: {
// ATOMIC: '原子指标',
// DERIVED: '衍生指标',
// },
// },
{
dataIndex: 'updatedAt',
title: '更新时间',

View File

@@ -62,13 +62,11 @@ const NodeInfoDrawer: React.FC<Props> = ({
}
const {
alias,
fullPath,
bizName,
createdBy,
createdAt,
updatedAt,
description,
// domainName,
sensitiveLevel,
modelName,
nodeType,
@@ -111,13 +109,11 @@ const NodeInfoDrawer: React.FC<Props> = ({
{
title: '指标趋势',
render: () => (
<div key="指标趋势" style={{ display: 'block' }}>
<Row key={`metricTrendSection`} style={{ marginBottom: 10, display: 'flex' }}>
<Col span={24}>
<MetricTrendSection nodeData={nodeData} />
</Col>
</Row>
</div>
),
},
{

View File

@@ -1,4 +1,7 @@
import request from 'umi-request';
import axios from 'axios';
import { AUTH_TOKEN_KEY } from '@/common/constants';
import moment from 'moment';
const getRunningEnv = () => {
return window.location.pathname.includes('/chatSetting/') ? 'chat' : 'semantic';
@@ -366,21 +369,37 @@ export function searchDictLatestTaskList(data: any): Promise<any> {
});
}
export function queryStruct({
const downloadStruct = (blob: Blob) => {
const fieldName = `supersonic_${moment().format('YYYYMMDDhhmmss')}.xlsx`;
const link = document.createElement('a');
link.href = URL.createObjectURL(new Blob([blob]));
link.download = fieldName;
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href);
document.body.removeChild(link);
};
export async function queryStruct({
modelId,
bizName,
dateField = 'sys_imp_date',
startDate,
endDate,
download = false,
}: {
modelId: number;
bizName: string;
dateField: string;
startDate: string;
endDate: string;
download?: boolean;
}): Promise<any> {
return request(`${process.env.API_BASE_URL}query/struct`, {
const response = await request(
`${process.env.API_BASE_URL}query/${download ? 'download/' : ''}struct`,
{
method: 'POST',
...(download ? { responseType: 'blob', getResponse: true } : {}),
data: {
modelId,
groups: [dateField],
@@ -408,5 +427,11 @@ export function queryStruct({
limit: 365,
nativeQuery: false,
},
});
},
);
if (download) {
downloadStruct(response.data);
} else {
return response;
}
}

View File

@@ -39,11 +39,13 @@ const responseInterceptor = async (response: Response) => {
const contextpath = response.headers.get('contextpath');
win.location.href = contextpath;
} else {
try {
const data: Result<any> = await response?.clone()?.json?.();
if (Number(data.code) === 403) {
history.push('/login');
return response;
}
} catch (e) {}
}
return response;