Skip to content

Commit 5ba5833

Browse files
XinyueDuduxinyue.dxy
andauthored
Dev/add sandkey (#221)
* feat: add liquid chart * feat: liquid markdown and knowledge fixed * feat: fixed * feat: add sandkey * feat: fixed knowledge and md and fixed fontSize * feat: deleted console * feat: fixed tooltip and animate * feat: add sankey knowledge * feat: fixed md --------- Co-authored-by: duxinyue.dxy <[email protected]>
1 parent 8e86136 commit 5ba5833

File tree

11 files changed

+538
-4
lines changed

11 files changed

+538
-4
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
## 图表属性
2+
3+
- 名称:桑基图
4+
- 别名:桑基流图,英文名:Sankey Chart
5+
- 形状:流向带状图,节点与流动线条
6+
- 图表类别:流向关系图表
7+
- 图表功能:展示流量、能量、资金等在各节点间的流动和分布
8+
9+
## 基础概念
10+
11+
桑基图是一种用于可视化流量、能量、资金等在不同节点间流动关系的图表。通过带宽表示流量大小,节点和流向线条直观展示各部分的流向和分布,常用于能量流、资金流、用户路径等分析场景。
12+
13+
## 适用场景
14+
15+
适合展示各类流量分布和流向关系,如能源流动、资金流转、用户行为路径、供应链流动等。突出流量的分布结构和流向路径。
16+
17+
## 不适用场景
18+
19+
1. 不适合仅有单一节点或无流向关系的数据。
20+
2. 不适合展示时间序列趋势,建议使用折线图或面积图。
21+
3. 不适合强调类别占比或组成结构,建议使用饼图、条形图等。
22+
4. 当关注的是具体数值而非流向关系时,桑基图并非最佳选择。
23+
5. 节点过多或流向过于复杂时,桑基图可能难以阅读。
24+
25+
## 图表用法
26+
27+
### 图表属性
28+
29+
```typescript
30+
type SankeyChart = {
31+
type: 'sankey';
32+
data: {
33+
source: string;
34+
target: string;
35+
value: number;
36+
}[];
37+
nodeAlign?: 'left' | 'center' | 'right' | 'justify';
38+
title?: string;
39+
theme?: 'default' | 'dark' | 'academy';
40+
style?: {
41+
backgroundColor?: string;
42+
palette?: string[];
43+
};
44+
};
45+
```
46+
47+
### 数据要求
48+
49+
- type:图表类型,必填,文本类型,值为 "sankey"。
50+
- data:桑基图数据,必填,数组类型。
51+
- source:源节点名称,必填,文本类型。
52+
- target:目标节点名称,必填,文本类型。
53+
- value:流量值,必填,数值类型。
54+
- nodeAlign:节点对齐方式,选填,文本类型,可选值为 "left" | "center" | "right" | "justify",默认值为 "center"。
55+
- title:图表标题,选填,文本类型。
56+
- theme:图表主题,选填,文本类型,可选值为 "default" | "dark" | "academy",默认值为 "default"。
57+
- style:图表样式,选填,对象类型;
58+
- palette:颜色映射,选填,数组类型,合法颜色值数组。
59+
- backgroundColor:背景颜色,选填,文本类型,合法颜色值。
60+
61+
## 使用示例
62+
63+
1. 展示能源流动关系。
64+
65+
```json
66+
{
67+
"type": "sankey",
68+
"data": [
69+
{ "source": "煤炭", "target": "发电厂", "value": 120 },
70+
{ "source": "天然气", "target": "发电厂", "value": 80 },
71+
{ "source": "发电厂", "target": "工业", "value": 100 },
72+
{ "source": "发电厂", "target": "居民", "value": 60 },
73+
{ "source": "发电厂", "target": "商业", "value": 40 }
74+
],
75+
"nodeAlign": "justify",
76+
"title": "能源流动关系"
77+
}
78+
```
79+
80+
2. 展示资金流转路径, 主题为 dark。
81+
82+
```json
83+
{
84+
"type": "sankey",
85+
"data": [
86+
{ "source": "投资人", "target": "创业公司", "value": 200 },
87+
{ "source": "创业公司", "target": "市场营销", "value": 80 },
88+
{ "source": "创业公司", "target": "研发", "value": 120 },
89+
{ "source": "市场营销", "target": "客户", "value": 70 },
90+
{ "source": "研发", "target": "客户", "value": 50 }
91+
],
92+
"nodeAlign": "center",
93+
"title": "资金流转路径",
94+
"theme": "dark"
95+
}
96+
```
97+
98+
3. 展示用户行为路径, 自定义配色。
99+
100+
```json
101+
{
102+
"type": "sankey",
103+
"data": [
104+
{ "source": "首页", "target": "产品页", "value": 300 },
105+
{ "source": "产品页", "target": "购物车", "value": 150 },
106+
{ "source": "购物车", "target": "结算页", "value": 100 },
107+
{ "source": "结算页", "target": "支付成功", "value": 80 },
108+
{ "source": "结算页", "target": "支付失败", "value": 20 }
109+
],
110+
"nodeAlign": "left",
111+
"title": "用户行为路径",
112+
"style": {
113+
"palette": ["#5B8FF9", "#61DDAA", "#65789B", "#F6BD16", "#7262FD"],
114+
"backgroundColor": "#f0f2f5"
115+
}
116+
}
117+
```

src/Liquid/index.en.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
order: 12
33
group:
44
order: 1
5-
title: Statistical Chart
5+
title: Statistical Charts
66
demo: { cols: 2 }
77
toc: content
88
nav: { title: 'Component', order: 1 }

src/Liquid/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export type LiquidProps = Theme &
1414
height?: number;
1515
};
1616

17-
const defaultConfig = (props: LiquidProps): LiquidConfig => {
17+
const defaultConfig = (props: LiquidProps) => {
1818
const { percent, shape = 'circle', style = {}, width = 600, height = 400 } = props;
1919
const { backgroundColor, palette } = style;
2020
const inferFontSize = Math.min(width, height) / 10;
@@ -35,6 +35,8 @@ const defaultConfig = (props: LiquidProps): LiquidConfig => {
3535
...(palette?.[0] ? { outlineStroke: palette[0] } : {}),
3636
},
3737
...(backgroundColor ? { viewStyle: { viewFill: backgroundColor } } : {}),
38+
interaction: { tooltip: false },
39+
animate: false,
3840
};
3941
};
4042

src/Sankey/demos/common.tsx

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { Sankey } from '@antv/gpt-vis';
2+
import { Form, Input, Select } from 'antd';
3+
import React, { useState } from 'react';
4+
5+
const data = [
6+
{ source: "Agricultural 'waste'", target: 'Bio-conversion', value: 124.729 },
7+
{ source: 'Bio-conversion', target: 'Liquid', value: 0.597 },
8+
{ source: 'Bio-conversion', target: 'Losses', value: 26.862 },
9+
{ source: 'Bio-conversion', target: 'Solid', value: 280.322 },
10+
{ source: 'Bio-conversion', target: 'Gas', value: 81.144 },
11+
{ source: 'Biofuel imports', target: 'Liquid', value: 35 },
12+
{ source: 'Biomass imports', target: 'Solid', value: 35 },
13+
{ source: 'Coal imports', target: 'Coal', value: 11.606 },
14+
{ source: 'Coal reserves', target: 'Coal', value: 63.965 },
15+
{ source: 'Coal', target: 'Solid', value: 75.571 },
16+
{ source: 'District heating', target: 'Industry', value: 10.639 },
17+
{ source: 'District heating', target: 'Heating and cooling - commercial', value: 22.505 },
18+
{ source: 'District heating', target: 'Heating and cooling - homes', value: 46.184 },
19+
{ source: 'Electricity grid', target: 'Over generation / exports', value: 104.453 },
20+
{ source: 'Electricity grid', target: 'Heating and cooling - homes', value: 113.726 },
21+
{ source: 'Electricity grid', target: 'H2 conversion', value: 27.14 },
22+
{ source: 'Electricity grid', target: 'Industry', value: 342.165 },
23+
{ source: 'Electricity grid', target: 'Road transport', value: 37.797 },
24+
{ source: 'Electricity grid', target: 'Agriculture', value: 4.412 },
25+
{ source: 'Electricity grid', target: 'Heating and cooling - commercial', value: 40.858 },
26+
];
27+
28+
const themes = ['default', 'academy', 'dark'] as const;
29+
export const PALETTE = [
30+
'#8459fc',
31+
'#ff89bd',
32+
'#1677ff',
33+
'#00c2ff',
34+
'#ff9a00',
35+
'#f2cc2e',
36+
'#7587dc',
37+
'#bd80fa',
38+
];
39+
40+
export const DEFAULT_COLOR_PALETTE = [
41+
'#1783FF',
42+
'#F08F56',
43+
'#D580FF',
44+
'#00C9C9',
45+
'#7863FF',
46+
'#DB9D0D',
47+
'#60C42D',
48+
'#FF80CA',
49+
'#2491B3',
50+
'#17C76F',
51+
];
52+
53+
export const ACADEMY_COLOR_PALETTE = [
54+
'#4e79a7',
55+
'#f28e2c',
56+
'#e15759',
57+
'#76b7b2',
58+
'#59a14f',
59+
'#edc949',
60+
'#af7aa1',
61+
'#ff9da7',
62+
'#9c755f',
63+
'#bab0ab',
64+
];
65+
66+
export default function SankeyDemo() {
67+
const [theme, setTheme] = useState<'default' | 'academy' | 'dark'>('default');
68+
const [backgroundColor, setBackgroundColor] = useState<string>('');
69+
const [palette, setPalette] = useState<string[]>([]);
70+
71+
const onValuesChange = (changedValues: {
72+
theme: 'default' | 'academy' | 'dark';
73+
lineWidth: number;
74+
backgroundColor: string;
75+
palette: string[];
76+
}) => {
77+
if (changedValues.theme) setTheme(changedValues.theme);
78+
if (changedValues.backgroundColor !== undefined)
79+
setBackgroundColor(changedValues.backgroundColor);
80+
if (changedValues.palette !== undefined) {
81+
let newPalette = changedValues.palette;
82+
if (typeof newPalette === 'string') {
83+
try {
84+
newPalette = JSON.parse(newPalette);
85+
} catch {
86+
newPalette = [];
87+
}
88+
}
89+
setPalette(Array.isArray(newPalette) ? newPalette : []);
90+
} else {
91+
setPalette([]);
92+
}
93+
};
94+
95+
return (
96+
<div>
97+
<Form
98+
layout="inline"
99+
style={{ marginBottom: 12 }}
100+
initialValues={{ theme, backgroundColor, palette }}
101+
onValuesChange={onValuesChange}
102+
>
103+
<Form.Item label="Theme" name="theme" style={{ marginBottom: 6 }}>
104+
<Select style={{ width: 120 }} options={themes.map((t) => ({ label: t, value: t }))} />
105+
</Form.Item>
106+
<Form.Item
107+
label="Background"
108+
name="backgroundColor"
109+
rules={[
110+
{
111+
pattern: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,
112+
message: '请输入有效的色值编码,例如 #fff 或 #ffffff',
113+
},
114+
]}
115+
style={{ marginBottom: 6 }}
116+
>
117+
<Input placeholder="#ffffff" style={{ width: 120 }} />
118+
</Form.Item>
119+
<Form.Item label="Palette" name="palette" style={{ marginBottom: 6 }}>
120+
<Select
121+
placeholder="选择调色板"
122+
style={{ width: 200 }}
123+
options={[
124+
{
125+
label: `默认调色板: ${DEFAULT_COLOR_PALETTE.join(', ')}`,
126+
value: JSON.stringify(DEFAULT_COLOR_PALETTE),
127+
},
128+
{
129+
label: `学术风格调色板: ${ACADEMY_COLOR_PALETTE.join(', ')}`,
130+
value: JSON.stringify(ACADEMY_COLOR_PALETTE),
131+
},
132+
{ label: `内置调色板: ${PALETTE.join(', ')}`, value: JSON.stringify(PALETTE) },
133+
]}
134+
allowClear
135+
/>
136+
</Form.Item>
137+
</Form>
138+
<Sankey
139+
data={data}
140+
title="Sankey Chart Example"
141+
theme={theme}
142+
style={{ backgroundColor, palette }}
143+
/>
144+
</div>
145+
);
146+
}

src/Sankey/demos/markdown.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { BubbleProps } from '@ant-design/x';
2+
import { Bubble } from '@ant-design/x';
3+
import { ChartType, GPTVisLite, Sankey, withChartCode } from '@antv/gpt-vis';
4+
import React from 'react';
5+
6+
const markdownContent = `
7+
当然了,以下是为你绘制的一个桑基图,展示了不同能源流向的分布情况:
8+
9+
\`\`\`vis-chart
10+
11+
{
12+
"type": "sankey",
13+
"theme": "academy",
14+
"data": [{"source":"Agricultural 'waste'","target":"Bio-conversion","value":124.729},{"source":"Bio-conversion","target":"Liquid","value":0.597},{"source":"Bio-conversion","target":"Losses","value":26.862},{"source":"Bio-conversion","target":"Solid","value":280.322},{"source":"Bio-conversion","target":"Gas","value":81.144},{"source":"Biofuel imports","target":"Liquid","value":35},{"source":"Biomass imports","target":"Solid","value":35},{"source":"Coal imports","target":"Coal","value":11.606},{"source":"Coal reserves","target":"Coal","value":63.965},{"source":"Coal","target":"Solid","value":75.571},{"source":"District heating","target":"Industry","value":10.639},{"source":"District heating","target":"Heating and cooling - commercial","value":22.505},{"source":"District heating","target":"Heating and cooling - homes","value":46.184},{"source":"Electricity grid","target":"Over generation / exports","value":104.453},{"source":"Electricity grid","target":"Heating and cooling - homes","value":113.726},{"source":"Electricity grid","target":"H2 conversion","value":27.14},{"source":"Electricity grid","target":"Industry","value":342.165},{"source":"Electricity grid","target":"Road transport","value":37.797},{"source":"Electricity grid","target":"Agriculture","value":4.412},{"source":"Electricity grid","target":"Heating and cooling - commercial","value":40.858}]
15+
}
16+
\`\`\`
17+
`;
18+
19+
const bgStyle = {
20+
display: 'grid',
21+
gridGap: '20px 0',
22+
padding: 20,
23+
background: '#f7f7f7',
24+
borderRadius: 8,
25+
overflow: 'auto',
26+
};
27+
28+
const CodeComponent = withChartCode({
29+
components: { [ChartType.Sankey]: Sankey },
30+
style: { width: 600 },
31+
});
32+
const RenderMarkdown: BubbleProps['messageRender'] = (content) => (
33+
<GPTVisLite components={{ code: CodeComponent }}>{content}</GPTVisLite>
34+
);
35+
36+
export default () => (
37+
<div style={bgStyle}>
38+
<Bubble
39+
placement="end"
40+
content="请生成区域能源系统流向图"
41+
avatar={{
42+
src: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*2Q5LRJ3LFPUAAAAAAAAAAAAADmJ7AQ/fmt.webp',
43+
}}
44+
styles={{ content: { background: '#ebebeb' } }}
45+
/>
46+
<Bubble
47+
content={markdownContent}
48+
messageRender={RenderMarkdown}
49+
avatar={{
50+
src: 'https://mdn.alipayobjects.com/huamei_je4oko/afts/img/A*6LRBT7rjOkQAAAAAAAAAAAAADsZ-AQ/original',
51+
}}
52+
variant="shadow"
53+
styles={{ content: { background: '#fff' } }}
54+
/>
55+
</div>
56+
);

0 commit comments

Comments
 (0)