mirror of
https://github.com/VisActor/VChart
synced 2024-11-21 15:38:37 +00:00
fix: 动态条形图动画修复&badcase修复
This commit is contained in:
parent
5c887fb8d7
commit
5543defaba
2
.gitignore
vendored
2
.gitignore
vendored
@ -124,3 +124,5 @@ packages/wx-vchart/miniprogram/miniprogram_npm/
|
||||
docs/public/documents
|
||||
docs/public/vchart/preview/failedPreviewLists.json
|
||||
packages/vchart/__tests__/runtime/node/**.png
|
||||
# env files
|
||||
.env.local
|
||||
|
@ -3566,7 +3566,8 @@ export const mockUserInput9 = {
|
||||
Step1,100,1
|
||||
Step2,80,1
|
||||
Step3,60,1
|
||||
Step4,40,1`
|
||||
Step4,40,1`,
|
||||
input: '展示各个流程转化率'
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3580,7 +3581,8 @@ export const mockUserInput11 = {
|
||||
周四,10,12
|
||||
周五,13,15
|
||||
周六,10,15
|
||||
周日,12,14`
|
||||
周日,12,14`,
|
||||
input: '展示男女早餐饭量不同'
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3657,5 +3659,6 @@ South Asia,9.4,10.06,10.75,11.56,12.5
|
||||
Middle East & North Africa,9.54,10.6,11.05,11.5,11.92
|
||||
Latin America & Caribbean,8.74,9.46,10.35,10.94,12.21
|
||||
East Asia & Pacific,7.8,8.95,10.18,11.57,13.25
|
||||
Europe & Central Asia,9.52,10.39,10.93,11.69,12.63`
|
||||
Europe & Central Asia,9.52,10.39,10.93,11.69,12.63`,
|
||||
input: '看下各地区gdp情况'
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import './index.scss';
|
||||
import { Avatar, Input, Divider, Button, InputNumber, Upload, Message } from '@arco-design/web-react';
|
||||
import { Avatar, Input, Divider, Button, InputNumber, Upload, Message, Select } from '@arco-design/web-react';
|
||||
import {
|
||||
mockUserInput10,
|
||||
mockUserInput2,
|
||||
@ -9,12 +9,26 @@ import {
|
||||
mockUserInput6,
|
||||
mockUserInput6Eng,
|
||||
mockUserInput8,
|
||||
carSaleMockData
|
||||
carSaleMockData,
|
||||
mockUserInput15,
|
||||
acceptRatioData,
|
||||
mallSalesData,
|
||||
hotWordsData,
|
||||
mockUserInput4,
|
||||
mockUserInput5,
|
||||
mockUserInput9,
|
||||
mockUserInput11,
|
||||
mockUserInput12,
|
||||
mockUserInput13,
|
||||
mockUserInput14,
|
||||
mockUserInput16
|
||||
} from '../constants/mockData';
|
||||
import { excel2csv } from '../../../../src/excel';
|
||||
import VMind from '../../../../src/index';
|
||||
|
||||
const TextArea = Input.TextArea;
|
||||
const Option = Select.Option;
|
||||
|
||||
type IPropsType = {
|
||||
onSpecGenerate: (
|
||||
spec: any,
|
||||
@ -24,21 +38,45 @@ type IPropsType = {
|
||||
}
|
||||
) => void;
|
||||
};
|
||||
|
||||
const demoDataList: { [key: string]: any } = {
|
||||
pie: mockUserInput2,
|
||||
'dynamic bar zh_cn': mockUserInput6,
|
||||
line: mockUserInput8,
|
||||
column: mockUserInput3,
|
||||
column2: mockUserInput10,
|
||||
wordcloud: hotWordsData,
|
||||
wordcloud2: mockUserInput5,
|
||||
'scatter plot': mockUserInput4,
|
||||
funnel: mockUserInput9,
|
||||
'dual-axis': mockUserInput11,
|
||||
waterfall: mockUserInput12,
|
||||
rose: mockUserInput13,
|
||||
radar: mockUserInput14,
|
||||
sankey: mockUserInput15,
|
||||
'box-plot': mockUserInput16,
|
||||
'Electric vehicle sales': carSaleMockData,
|
||||
'College entrance examination': acceptRatioData,
|
||||
'Shopping Mall Sales Performance': mallSalesData,
|
||||
'Global GDP': mockUserInput6Eng,
|
||||
'Sales of different drinkings': mockUserInput3Eng
|
||||
};
|
||||
export function LeftInput(props: IPropsType) {
|
||||
const [describe, setDescribe] = useState<string>(mockUserInput2.input);
|
||||
const [csv, setCsv] = useState<string>(mockUserInput2.csv);
|
||||
const defaultDataKey = Object.keys(demoDataList)[0];
|
||||
const [describe, setDescribe] = useState<string>(demoDataList[defaultDataKey].input);
|
||||
const [csv, setCsv] = useState<string>(demoDataList[defaultDataKey].csv);
|
||||
const [spec, setSpec] = useState<string>('');
|
||||
const [time, setTime] = useState<number>(1000);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const vmind = new VMind(import.meta.OPENAI_KEY!);
|
||||
const vmind = new VMind(import.meta.env.OPENAI_KEY!, {
|
||||
url: import.meta.env.VITE_OPENAI_URL ?? undefined
|
||||
});
|
||||
|
||||
const askGPT = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const { spec, time } = await vmind.generateChart(csv, describe);
|
||||
props.onSpecGenerate(spec, time as any);
|
||||
setLoading(false);
|
||||
}, [describe, csv]);
|
||||
}, [vmind, csv, describe, props]);
|
||||
|
||||
const isAcceptFile = useCallback((file, accept) => {
|
||||
if (accept && file) {
|
||||
@ -68,6 +106,23 @@ export function LeftInput(props: IPropsType) {
|
||||
|
||||
return (
|
||||
<div className="left-sider">
|
||||
<Select
|
||||
style={{
|
||||
width: '100%'
|
||||
}}
|
||||
defaultValue={defaultDataKey}
|
||||
onChange={v => {
|
||||
const dataObj = demoDataList[v];
|
||||
setDescribe(dataObj.input);
|
||||
setCsv(dataObj.csv);
|
||||
}}
|
||||
>
|
||||
{Object.keys(demoDataList).map(name => (
|
||||
<Option key={name} value={name}>
|
||||
{name}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<div>
|
||||
<p>
|
||||
<Avatar size={18} style={{ backgroundColor: '#3370ff' }}>
|
||||
@ -78,7 +133,7 @@ export function LeftInput(props: IPropsType) {
|
||||
|
||||
<TextArea
|
||||
placeholder={describe}
|
||||
defaultValue={describe}
|
||||
value={describe}
|
||||
onChange={v => setDescribe(v)}
|
||||
style={{ minHeight: 160, marginTop: 20, background: 'transparent', border: '1px solid #eee' }}
|
||||
/>
|
||||
|
@ -139,7 +139,7 @@ export const getSchemaFromFieldInfo = (dataProcessResJson: GPTDataProcessResult)
|
||||
const usefulFields = dataProcessResJson.USEFUL_FIELDS;
|
||||
const schema = {
|
||||
fields: fieldInfo
|
||||
.filter(d => usefulFields.includes(d.fieldName))
|
||||
//.filter(d => usefulFields.includes(d.fieldName))
|
||||
.map(d => ({
|
||||
id: d.fieldName,
|
||||
alias: d.fieldName,
|
||||
|
@ -489,13 +489,13 @@ Respone in the following format:
|
||||
"FIELD_MAP": { // Visual channels and the fields mapped to them, available visual channels: ["x", "y", "color", "size", "angle", "time"]
|
||||
"x": the field mapped to the x-axis, can be empty. Can Only has one field.
|
||||
"y": the field mapped to the y-axis, can be empty. Can only has one field.
|
||||
"color": the field mapped to the color channel. Can't be empty in Word Cloud, Pie Chart and Rose Chart
|
||||
"size": the field mapped to the size channel, can be empty
|
||||
"angle": the field mapped to the angle channel of the pie chart, can be empty
|
||||
"color": the field mapped to the color channel. Must use a string field. Can't be empty in Word Cloud, Pie Chart and Rose Chart.
|
||||
"size": the field mapped to the size channel. Must use a number field. Can be empty
|
||||
"angle": the field mapped to the angle channel of the pie chart, can be empty.
|
||||
"time": This is usually a date field and can be used only in Dynamic Bar Chart. Can't be empty in Dynamic Bar Chart.
|
||||
"source": the field mapped to the source channel. Can't be empty in Sankey Chart
|
||||
"target": the field mapped to the target channel. Can't be empty in Sankey Chart
|
||||
"value": the field mapped to the value channel. Can't be empty in Sankey Chart
|
||||
"source": the field mapped to the source channel. Can't be empty in Sankey Chart.
|
||||
"target": the field mapped to the target channel. Can't be empty in Sankey Chart.
|
||||
"value": the field mapped to the value channel. Can't be empty in Sankey Chart.
|
||||
},
|
||||
"Reason": the reason for selecting the chart type and visual mapping.
|
||||
}
|
||||
@ -505,7 +505,7 @@ Constraints:
|
||||
1. No user assistance.
|
||||
2. The selected chart type in CHART_TYPE must be in the list of supported charts.
|
||||
3. Just ignore the user's request about duration and style in their input.
|
||||
4. All fields in the data must be mapped to at least one visual channel in FIELD_MAP. DO NOT change or translate the field names in FIELD_MAP.
|
||||
4. DO NOT change or translate the field names in FIELD_MAP.
|
||||
5. The keys in FIELD_MAP must be selected from the list of available visual channels.
|
||||
6. Wrap the reply content using \`\`\`, and the returned content must be directly parsed by JSON.parse() in JavaScript.
|
||||
|
||||
|
@ -880,6 +880,10 @@ export const rankingBarLabel = (spec: any, context: Context) => {
|
||||
style: {
|
||||
fill: '#FFFFFF',
|
||||
stroke: null
|
||||
},
|
||||
animation: {
|
||||
duration: spec.animationUpdate.axis.duration,
|
||||
easing: 'linear'
|
||||
}
|
||||
};
|
||||
return spec;
|
||||
|
@ -131,3 +131,14 @@ export const patchUserInput = (userInput: string) => {
|
||||
'严格按照prompt中的格式回复,不要有任何多余内容。 Use the original fieldName and DO NOT change or translate any word of the data fields in the response.';
|
||||
return finalStr;
|
||||
};
|
||||
|
||||
export const CARTESIAN_CHART_LIST = [
|
||||
'Dynamic Bar Chart',
|
||||
'Bar Chart',
|
||||
'Line Chart',
|
||||
'Scatter Plot',
|
||||
'Funnel Chart',
|
||||
'Dual Axis Chart',
|
||||
'Waterfall Chart',
|
||||
'Box Plot Chart'
|
||||
];
|
||||
|
@ -46,7 +46,7 @@ import {
|
||||
} from './pipes';
|
||||
import { Cell, ChartType, Context, Pipe } from '../typings';
|
||||
import { DataView } from '@visactor/vdataset';
|
||||
import { detectAxesType } from './utils';
|
||||
import { CARTESIAN_CHART_LIST, detectAxesType } from './utils';
|
||||
|
||||
export const vizDataToSpec = (
|
||||
dataView: DataView,
|
||||
@ -67,7 +67,7 @@ export const vizDataToSpec = (
|
||||
return { spec, chartTypeNew };
|
||||
};
|
||||
|
||||
const patchChartTypeAndCell = (chartType: string, cell: any, dataView: DataView) => {
|
||||
const patchChartTypeAndCell = (chartTypeOutter: string, cell: any, dataView: DataView) => {
|
||||
//对GPT返回结果进行修正
|
||||
//某些时候由于用户输入的意图不明确,GPT返回的cell中可能缺少字段。
|
||||
//此时需要根据规则补全
|
||||
@ -75,6 +75,7 @@ const patchChartTypeAndCell = (chartType: string, cell: any, dataView: DataView)
|
||||
|
||||
const { x, y } = cell;
|
||||
|
||||
let chartType = chartTypeOutter;
|
||||
// y轴字段有多个时,处理方式:
|
||||
// 1. 图表类型为: 箱型图, 图表类型不做矫正
|
||||
// 2. 图表类型为: 柱状图 或 折线图, 图表类型矫正为双轴图
|
||||
@ -87,12 +88,7 @@ const patchChartTypeAndCell = (chartType: string, cell: any, dataView: DataView)
|
||||
};
|
||||
}
|
||||
if (chartType === 'BAR CHART' || chartType === 'LINE CHART') {
|
||||
return {
|
||||
chartTypeNew: 'DUAL AXIS CHART',
|
||||
cellNew: {
|
||||
...cell
|
||||
}
|
||||
};
|
||||
chartType = 'DUAL AXIS CHART';
|
||||
} else {
|
||||
return {
|
||||
chartTypeNew: 'SCATTER PLOT',
|
||||
@ -105,6 +101,13 @@ const patchChartTypeAndCell = (chartType: string, cell: any, dataView: DataView)
|
||||
};
|
||||
}
|
||||
}
|
||||
//双轴图 订正yLeft和yRight
|
||||
if (chartType === 'DUAL AXIS CHART' && cell.yLeft && cell.yRight) {
|
||||
return {
|
||||
chartTypeNew: chartType,
|
||||
cellNew: { ...cell, y: [cell.yLeft, cell.yRight] }
|
||||
};
|
||||
}
|
||||
//饼图 必须有color字段和angle字段
|
||||
if (chartType === 'PIE CHART') {
|
||||
const cellNew = { ...cell };
|
||||
@ -215,6 +218,29 @@ const patchChartTypeAndCell = (chartType: string, cell: any, dataView: DataView)
|
||||
cellNew
|
||||
};
|
||||
}
|
||||
//直角坐标图表 必须有x字段
|
||||
if (CARTESIAN_CHART_LIST.map(chart => chart.toUpperCase()).includes(chartType)) {
|
||||
const cellNew = { ...cell };
|
||||
if (!cellNew.x) {
|
||||
const usedFields = Object.values(cell);
|
||||
const dataFields = Object.keys(dataView.latestData[0]);
|
||||
const remainedFields = dataFields.filter(f => !usedFields.includes(f));
|
||||
//没有分配x字段,从剩下的字段里选择一个离散字段分配到x上
|
||||
const xField = remainedFields.find(f => {
|
||||
const fieldType = detectAxesType(dataView.latestData, f);
|
||||
return fieldType === 'band';
|
||||
});
|
||||
if (xField) {
|
||||
cellNew.x = xField;
|
||||
} else {
|
||||
cellNew.x = remainedFields[0];
|
||||
}
|
||||
}
|
||||
return {
|
||||
chartTypeNew: chartType,
|
||||
cellNew
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
chartTypeNew: chartType,
|
||||
|
Loading…
Reference in New Issue
Block a user