mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-16 06:56:57 +00:00
[improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators (#347)
* [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 * [improvement][semantic-fe] fix the dimension initialization issue in metric correlation * [improvement][semantic-fe] Fix the issue of database changes not taking effect when creating based on an SQL data source. * [improvement][semantic-fe] Optimizing pagination logic and some CSS styles * [improvement][semantic-fe] Fixing the API for the indicator list by changing "current" to "pageNum" * [improvement][semantic-fe] Fixing the default value setting for the indicator list * [improvement][semantic-fe] Adding batch operations for indicators/dimensions/models * [improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API * [improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators
This commit is contained in:
@@ -1,27 +1,44 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Button } from 'antd';
|
||||
import { Modal, Button, message } from 'antd';
|
||||
import DimensionMetricRelationTableTransfer from './DimensionMetricRelationTableTransfer';
|
||||
import { ISemantic } from '../data';
|
||||
|
||||
import { updateExprMetric } from '../service';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
|
||||
type Props = {
|
||||
onCancel: () => void;
|
||||
open: boolean;
|
||||
metricItem: ISemantic.IMetricItem;
|
||||
metricItem?: ISemantic.IMetricItem;
|
||||
relationsInitialValue?: ISemantic.IDrillDownDimensionItem[];
|
||||
onSubmit: (relations: ISemantic.IDrillDownDimensionItem[]) => void;
|
||||
};
|
||||
|
||||
const DimensionAndMetricRelationModal: React.FC<Props> = ({
|
||||
open,
|
||||
metricItem,
|
||||
metricItem = {},
|
||||
relationsInitialValue,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [relationList, setRelationList] = useState<ISemantic.IDrillDownDimensionItem[]>([]);
|
||||
|
||||
const saveMetric = async (relationList: any) => {
|
||||
const queryParams = {
|
||||
...metricItem,
|
||||
relateDimension: {
|
||||
...(metricItem?.relateDimension || {}),
|
||||
drillDownDimensions: relationList,
|
||||
},
|
||||
};
|
||||
const { code, msg } = await updateExprMetric(queryParams);
|
||||
if (code === 200) {
|
||||
// message.success('编辑指标成功');
|
||||
// onSubmit?.(queryParams);
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -30,6 +47,7 @@ const DimensionAndMetricRelationModal: React.FC<Props> = ({
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
onSubmit(relationList);
|
||||
saveMetric(relationList);
|
||||
}}
|
||||
>
|
||||
完成
|
||||
|
||||
@@ -33,7 +33,7 @@ const DimensionMetricRelationTableTransfer: React.FC<Props> = ({
|
||||
onChange,
|
||||
}) => {
|
||||
const [targetKeys, setTargetKeys] = useState<string[]>([]);
|
||||
const { selectModelId: modelId, selectDomainId } = domainManger;
|
||||
const { selectModelId: modelId } = domainManger;
|
||||
const [checkedMap, setCheckedMap] = useState<Record<string, ISemantic.IDrillDownDimensionItem>>(
|
||||
{},
|
||||
);
|
||||
@@ -42,7 +42,7 @@ const DimensionMetricRelationTableTransfer: React.FC<Props> = ({
|
||||
|
||||
useEffect(() => {
|
||||
queryDimensionList();
|
||||
}, []);
|
||||
}, [metricItem, relationsInitialValue]);
|
||||
|
||||
const queryDimensionList = async () => {
|
||||
const { code, data, msg } = await getDimensionList({ modelId: metricItem?.modelId || modelId });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DownOutlined, PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
import { Input, message, Tree, Popconfirm, Space, Tooltip, Row, Col } from 'antd';
|
||||
import { Input, message, Tree, Popconfirm, Space, Tooltip, Row, Col, Button } from 'antd';
|
||||
import type { DataNode } from 'antd/lib/tree';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { FC, Key } from 'react';
|
||||
@@ -119,7 +119,7 @@ const DomainListTree: FC<DomainListProps> = ({
|
||||
return (
|
||||
<div className={styles.projectItem}>
|
||||
<span
|
||||
className={styles.title}
|
||||
className={styles.projectItemTitle}
|
||||
onClick={() => {
|
||||
handleSelect(id, name);
|
||||
}}
|
||||
@@ -180,17 +180,31 @@ const DomainListTree: FC<DomainListProps> = ({
|
||||
return (
|
||||
<div className={styles.domainList}>
|
||||
<Row>
|
||||
<Col flex="1 1 200px">
|
||||
<Col flex="1 1 auto">
|
||||
{/* <Space> */}
|
||||
<Search
|
||||
allowClear
|
||||
className={styles.search}
|
||||
placeholder="请输入主题域名称进行查询"
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
{/* </Space> */}
|
||||
</Col>
|
||||
{createDomainBtnVisible && (
|
||||
<Col flex="0 1 50px">
|
||||
<Col flex="0 0 40px" style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Tooltip title="新增顶级域">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setProjectInfoParams({ type: 'top', modelType: 'add' });
|
||||
setProjectInfoModalVisible(true);
|
||||
onCreateDomainBtnClick?.();
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/* <Tooltip title="新增顶级域">
|
||||
<PlusCircleOutlined
|
||||
onClick={() => {
|
||||
setProjectInfoParams({ type: 'top', modelType: 'add' });
|
||||
@@ -199,7 +213,7 @@ const DomainListTree: FC<DomainListProps> = ({
|
||||
}}
|
||||
className={styles.addBtn}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Tooltip> */}
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
@@ -6,7 +6,6 @@ import ClassDataSourceTable from './ClassDataSourceTable';
|
||||
import ClassDimensionTable from './ClassDimensionTable';
|
||||
import ClassMetricTable from './ClassMetricTable';
|
||||
import PermissionSection from './Permission/PermissionSection';
|
||||
// import DatabaseSection from './Database/DatabaseSection';
|
||||
import EntitySettingSection from './Entity/EntitySettingSection';
|
||||
import ChatSettingSection from '../ChatSetting/ChatSettingSection';
|
||||
import OverView from './OverView';
|
||||
@@ -16,6 +15,7 @@ import { LeftOutlined } from '@ant-design/icons';
|
||||
import { ISemantic } from '../data';
|
||||
import SemanticGraphCanvas from '../SemanticGraphCanvas';
|
||||
import RecommendedQuestionsSection from '../components/Entity/RecommendedQuestionsSection';
|
||||
import DatabaseTable from '../components/Database/DatabaseTable';
|
||||
|
||||
import type { Dispatch } from 'umi';
|
||||
|
||||
@@ -39,10 +39,10 @@ const DomainManagerTab: React.FC<Props> = ({
|
||||
onMenuChange,
|
||||
}) => {
|
||||
const defaultTabKey = 'xflow';
|
||||
const { selectDomainId, domainList } = domainManger;
|
||||
const { selectDomainId, domainList, selectModelId } = domainManger;
|
||||
const tabItem = [
|
||||
{
|
||||
label: '模型',
|
||||
label: '模型管理',
|
||||
key: 'overview',
|
||||
children: (
|
||||
<OverView
|
||||
@@ -53,16 +53,16 @@ const DomainManagerTab: React.FC<Props> = ({
|
||||
/>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// label: '数据库',
|
||||
// key: 'dataBase',
|
||||
// children: <DatabaseSection />,
|
||||
// },
|
||||
{
|
||||
label: '权限管理',
|
||||
key: 'permissonSetting',
|
||||
children: <PermissionSection permissionTarget={'domain'} />,
|
||||
},
|
||||
{
|
||||
label: '数据库管理',
|
||||
key: 'database',
|
||||
children: <DatabaseTable />,
|
||||
},
|
||||
].filter((item) => {
|
||||
const target = domainList.find((domain) => domain.id === selectDomainId);
|
||||
if (target?.hasEditPermission) {
|
||||
@@ -76,7 +76,7 @@ const DomainManagerTab: React.FC<Props> = ({
|
||||
label: '画布',
|
||||
key: 'xflow',
|
||||
children: (
|
||||
<div style={{ width: '100%', marginTop: -20 }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<SemanticGraphCanvas />
|
||||
</div>
|
||||
),
|
||||
@@ -131,20 +131,35 @@ const DomainManagerTab: React.FC<Props> = ({
|
||||
items={!isModel ? tabItem : isModelItem}
|
||||
activeKey={activeKey || defaultTabKey}
|
||||
destroyInactiveTabPane
|
||||
tabBarExtraContent={
|
||||
isModel ? (
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<LeftOutlined />}
|
||||
onClick={() => {
|
||||
onBackDomainBtnClick?.();
|
||||
}}
|
||||
style={{ marginRight: 10 }}
|
||||
>
|
||||
返回主题域
|
||||
</Button>
|
||||
) : undefined
|
||||
}
|
||||
size="large"
|
||||
tabBarExtraContent={{
|
||||
left: (
|
||||
<>
|
||||
{!!selectModelId && (
|
||||
<div
|
||||
className={styles.backBtn}
|
||||
onClick={() => {
|
||||
onBackDomainBtnClick?.();
|
||||
}}
|
||||
>
|
||||
<LeftOutlined />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
// right: isModel ? (
|
||||
// <Button
|
||||
// type="primary"
|
||||
// icon={<LeftOutlined />}
|
||||
// onClick={() => {
|
||||
// onBackDomainBtnClick?.();
|
||||
// }}
|
||||
// style={{ marginRight: 10, marginBottom: 5 }}
|
||||
// >
|
||||
// 返回主题域
|
||||
// </Button>
|
||||
// ) : undefined,
|
||||
}}
|
||||
onChange={(menuKey: string) => {
|
||||
onMenuChange?.(menuKey);
|
||||
}}
|
||||
|
||||
@@ -43,7 +43,7 @@ const EntitySettingSection: React.FC<Props> = ({ domainManger }) => {
|
||||
}, [modelId]);
|
||||
|
||||
return (
|
||||
<div style={{ width: 800, margin: '0 auto' }}>
|
||||
<div style={{ width: 800, margin: '20px auto' }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size={20}>
|
||||
{
|
||||
<ProCard title="实体" bordered>
|
||||
|
||||
@@ -72,7 +72,7 @@ const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => {
|
||||
}, [modelId]);
|
||||
|
||||
return (
|
||||
<div style={{ width: 800, margin: '0 auto' }}>
|
||||
<div style={{ width: 800, margin: '20px auto' }}>
|
||||
<ProCard bordered title="问题推荐列表">
|
||||
<TextAreaCommonEditList
|
||||
value={questionData}
|
||||
|
||||
@@ -144,6 +144,7 @@ const ModelCreateFormModal: React.FC<ModelCreateFormModalProps> = (props) => {
|
||||
subTitle={'配置之后,可在指标主页和问答指标卡处选择用来对指标进行下钻和过滤'}
|
||||
/>
|
||||
}
|
||||
hidden={!basicInfo?.id}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
|
||||
@@ -10,9 +10,79 @@
|
||||
min-height: calc(100vh - 48px);
|
||||
// background: #f8f9fb;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
position: relative;
|
||||
.sider {
|
||||
flex: 0 0 350px;
|
||||
border-right: 5px solid #eee;
|
||||
|
||||
.domainTitle {
|
||||
margin-bottom: 0;
|
||||
font-size: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
padding: 12px 0 12px 35px;
|
||||
}
|
||||
|
||||
.addBtn {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
margin-top: 18px;
|
||||
&:hover {
|
||||
color: #296DF3;
|
||||
}
|
||||
}
|
||||
.treeTitle {
|
||||
margin-bottom: 0;
|
||||
padding: 20px;
|
||||
line-height: 34px;
|
||||
text-align: right;
|
||||
background: #fff;
|
||||
// border-bottom: 1px solid #d9d9d9;
|
||||
|
||||
.title {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.search {
|
||||
width: calc(100% - 60px);
|
||||
margin: 10px 0 10px 35px;
|
||||
}
|
||||
|
||||
.tree {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
|
||||
.projectItem {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
cursor: auto;
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
.projectItemTitle {
|
||||
font-size: 16px;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.operation {
|
||||
.icon {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.collapseLeftBtn {
|
||||
position: absolute;
|
||||
top: calc(50% + 45px);
|
||||
@@ -41,20 +111,20 @@
|
||||
padding: 20px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
.backBtn {
|
||||
background-color: #f8f8f8;
|
||||
padding: 5px;
|
||||
color: #b0b4bc;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
&:hover {
|
||||
background-color: #7599e5;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
// .backBtn {
|
||||
// background-color: #f8f8f8;
|
||||
// padding: 5px;
|
||||
// color: #b0b4bc;
|
||||
// cursor: pointer;
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
// &:hover {
|
||||
// background-color: #7599e5;
|
||||
// color: #fff;
|
||||
// }
|
||||
// }
|
||||
.tab {
|
||||
:global {
|
||||
.ant-tabs-tab-btn {
|
||||
@@ -66,6 +136,24 @@
|
||||
.ant-tabs-nav {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.ant-tabs-tab {
|
||||
padding-bottom:15px;
|
||||
}
|
||||
}
|
||||
.backBtn {
|
||||
height: 56px;
|
||||
background-color: #f8f8f8;
|
||||
padding: 5px;
|
||||
color: #b0b4bc;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
&:hover {
|
||||
background-color: #7599e5;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chatSettingSectionTab {
|
||||
@@ -87,164 +175,42 @@
|
||||
.ant-card-body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.ant-tabs-content-holder {
|
||||
padding-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.resource {
|
||||
display: flex;
|
||||
|
||||
.tree {
|
||||
flex: 1;
|
||||
|
||||
.headOperation {
|
||||
.btn {
|
||||
margin-right: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.resourceSearch {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.view {
|
||||
width: 480px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.selectTypesBtn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.domainTreeSelect {
|
||||
// width: 300px;
|
||||
}
|
||||
|
||||
|
||||
.domainList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 400px;
|
||||
// width: 400px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
// min-height: calc(100vh - 48px);
|
||||
.addBtn {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
margin-top: 18px;
|
||||
&:hover {
|
||||
color: #296DF3;
|
||||
}
|
||||
}
|
||||
.treeTitle {
|
||||
margin-bottom: 0;
|
||||
padding: 20px;
|
||||
line-height: 34px;
|
||||
text-align: right;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
|
||||
.title {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.search {
|
||||
width: calc(100% - 20px);
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.tree {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
|
||||
.projectItem {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
cursor: auto;
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.operation {
|
||||
.icon {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user {
|
||||
display: grid;
|
||||
}
|
||||
// .user {
|
||||
// display: grid;
|
||||
// }
|
||||
|
||||
.search{
|
||||
width: 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.paramsName{
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.deleteBtn{
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.authBtn{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selectedResource{
|
||||
font-size: 12px;
|
||||
color: darkgrey;
|
||||
margin-top: 4px;
|
||||
}
|
||||
// .search{
|
||||
// width: 50%;
|
||||
// margin-bottom: 20px;
|
||||
// }
|
||||
|
||||
|
||||
.switch{
|
||||
// min-width: 900px;
|
||||
// max-width: 1200px;
|
||||
// width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
.switchUser {
|
||||
width: 200px;
|
||||
margin-left: 33px;
|
||||
:global {
|
||||
.ant-select {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dimensionIntentionForm {
|
||||
margin-bottom: -24px !important;
|
||||
margin-top: 10px;
|
||||
margin-left: 26px;
|
||||
}
|
||||
// .authBtn{
|
||||
// cursor: pointer;
|
||||
// }
|
||||
|
||||
.classTable {
|
||||
:global {
|
||||
.ant-pro-table-search-query-filter {
|
||||
padding-left: 0 !important;
|
||||
// padding-bottom: 24px !important;
|
||||
.ant-pro-form-query-filter {
|
||||
.ant-form-item {
|
||||
// margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-table-tbody > tr.ant-table-row-selected > td {
|
||||
background: none;
|
||||
@@ -275,8 +241,6 @@
|
||||
padding: 0 11px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
// background-color: #fff;
|
||||
// border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
color: #000a24d9;
|
||||
|
||||
Reference in New Issue
Block a user