mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-10 11:07:06 +00:00
[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:
@@ -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",
|
||||
|
||||
@@ -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'),
|
||||
});
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -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: '更新时间',
|
||||
|
||||
@@ -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>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user