From 0a78197a1db290ebd49858628026c3e2ecb64c3c Mon Sep 17 00:00:00 2001 From: zxc <1171051090@qq.com> Date: Fri, 13 Sep 2024 21:28:09 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8EChart=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E5=9B=BE=E8=A1=A8=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 45 +++ package.json | 1 + src/configs/chart.ts | 54 ++++ src/configs/chartTypes.ts | 15 - src/hooks/useCreateElement.ts | 37 +-- src/hooks/useExport.ts | 77 +++-- src/hooks/useImport.ts | 31 +- src/types/slides.ts | 18 +- src/views/Editor/CanvasTool/ChartPool.vue | 10 +- .../ChartStylePanel/index.vue | 155 ++-------- .../element/ChartElement/BaseChartElement.vue | 18 +- .../components/element/ChartElement/Chart.vue | 216 +++----------- .../element/ChartElement/chartOption.ts | 276 ++++++++++++++++++ .../components/element/ChartElement/index.vue | 6 +- 14 files changed, 519 insertions(+), 440 deletions(-) create mode 100644 src/configs/chart.ts delete mode 100644 src/configs/chartTypes.ts create mode 100644 src/views/components/element/ChartElement/chartOption.ts diff --git a/package-lock.json b/package-lock.json index 829c48d7..45b86824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "clipboard": "^2.0.11", "crypto-js": "^4.2.0", "dexie": "3.0.3", + "echarts": "^5.5.1", "file-saver": "^2.0.5", "hfmath": "^0.0.2", "html-to-image": "^1.11.11", @@ -2317,6 +2318,15 @@ "node": ">=8" } }, + "node_modules/echarts": { + "version": "5.5.1", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4933,6 +4943,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "node_modules/txml": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/txml/-/txml-5.1.1.tgz", @@ -5250,6 +5265,14 @@ "engines": { "node": ">=10" } + }, + "node_modules/zrender": { + "version": "5.6.0", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "dependencies": { + "tslib": "2.3.0" + } } }, "dependencies": { @@ -6850,6 +6873,15 @@ "is-obj": "^2.0.0" } }, + "echarts": { + "version": "5.5.1", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "requires": { + "tslib": "2.3.0", + "zrender": "5.6.0" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -8929,6 +8961,11 @@ "dev": true, "requires": {} }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "txml": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/txml/-/txml-5.1.1.tgz", @@ -9137,6 +9174,14 @@ "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zrender": { + "version": "5.6.0", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "requires": { + "tslib": "2.3.0" + } } } } diff --git a/package.json b/package.json index a2484bf9..fa964b5a 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "clipboard": "^2.0.11", "crypto-js": "^4.2.0", "dexie": "3.0.3", + "echarts": "^5.5.1", "file-saver": "^2.0.5", "hfmath": "^0.0.2", "html-to-image": "^1.11.11", diff --git a/src/configs/chart.ts b/src/configs/chart.ts new file mode 100644 index 00000000..d289d9f2 --- /dev/null +++ b/src/configs/chart.ts @@ -0,0 +1,54 @@ +import type { ChartData } from '@/types/slides' + +export const CHART_DEFAULT_DATA: { [key: string]: ChartData } = { + 'bar': { + labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], + legends: ['系列1', '系列2'], + series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]], + }, + 'column': { + labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], + legends: ['系列1', '系列2'], + series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]], + }, + 'line': { + labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], + legends: ['系列1', '系列2'], + series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]], + }, + 'pie': { + labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], + legends: ['值'], + series: [[12, 19, 5, 2, 18]], + }, + 'ring': { + labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], + legends: ['值'], + series: [[12, 19, 5, 2, 18]], + }, + 'area': { + labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], + legends: ['系列1', '系列2'], + series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]], + }, + 'scatter': { + labels: ['坐标1', '坐标2', '坐标3', '坐标4', '坐标5'], + legends: ['X', 'Y'], + series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]], + }, +} + +export const CHART_PRESET_THEMES = [ + ['#d87c7c', '#919e8b', '#d7ab82', '#6e7074', '#61a0a8', '#efa18d'], + ['#dd6b66', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53', '#eedd78'], + ['#516b91', '#59c4e6', '#edafda', '#93b7e3', '#a5e7f0', '#cbb0e3'], + ['#893448', '#d95850', '#eb8146', '#ffb248', '#f2d643', '#ebdba4'], + ['#4ea397', '#22c3aa', '#7bd9a5', '#d0648a', '#f58db2', '#f2b3c9'], + ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'], + ['#fc97af', '#87f7cf', '#f7f494', '#72ccff', '#f7c5a0', '#d4a4eb'], + ['#c1232b', '#27727b', '#fcce10', '#e87c25', '#b5c334', '#fe8463'], + ['#2ec7c9', '#b6a2de', '#5ab1ef', '#ffb980', '#d87a80', '#8d98b3'], + ['#e01f54', '#001852', '#f5e8c8', '#b8d2c7', '#c6b38e', '#a4d8c2'], + ['#c12e34', '#e6b600', '#0098d9', '#2b821d', '#005eaa', '#339ca8'], + ['#8a7ca8', '#e098c7', '#8fd3e8', '#71669e', '#cc70af', '#7cb4cc'], +] \ No newline at end of file diff --git a/src/configs/chartTypes.ts b/src/configs/chartTypes.ts deleted file mode 100644 index 400821f7..00000000 --- a/src/configs/chartTypes.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ChartType } from '@/types/slides' - -interface ChartTypes { - [propName: string]: ChartType -} - -export const CHART_TYPES: ChartTypes = { - bar: 'bar', - horizontalBar: 'bar', - line: 'line', - area: 'line', - scatter: 'line', - pie: 'pie', - ring: 'pie', -} \ No newline at end of file diff --git a/src/hooks/useCreateElement.ts b/src/hooks/useCreateElement.ts index a9fef573..570534d5 100644 --- a/src/hooks/useCreateElement.ts +++ b/src/hooks/useCreateElement.ts @@ -2,10 +2,10 @@ import { storeToRefs } from 'pinia' import { nanoid } from 'nanoid' import { useMainStore, useSlidesStore } from '@/store' import { getImageSize } from '@/utils/image' -import type { PPTLineElement, PPTElement, TableCell, TableCellStyle, PPTShapeElement, PPTChartElement, ChartOptions, PresetChartType } from '@/types/slides' +import type { PPTLineElement, PPTElement, TableCell, TableCellStyle, PPTShapeElement, ChartType } from '@/types/slides' import { type ShapePoolItem, SHAPE_PATH_FORMULAS } from '@/configs/shapes' import type { LinePoolItem } from '@/configs/lines' -import { CHART_TYPES } from '@/configs/chartTypes' +import { CHART_DEFAULT_DATA } from '@/configs/chart' import useHistorySnapshot from '@/hooks/useHistorySnapshot' interface CommonElementPosition { @@ -86,40 +86,19 @@ export default () => { * 创建图表元素 * @param chartType 图表类型 */ - const createChartElement = (type: PresetChartType) => { - const newElement: PPTChartElement = { + const createChartElement = (type: ChartType) => { + createElement({ type: 'chart', id: nanoid(10), - chartType: CHART_TYPES[type], + chartType: type, left: 300, top: 81.25, width: 400, height: 400, rotate: 0, - themeColor: [theme.value.themeColor], - gridColor: theme.value.fontColor, - data: { - labels: ['类别1', '类别2', '类别3', '类别4', '类别5'], - legends: ['系列1'], - series: [ - [12, 19, 5, 2, 18], - ], - }, - } - - const options: ChartOptions = { - ...(type === 'bar' ? { horizontalBars: false, stackBars: false } : {}), - ...(type === 'horizontalBar' ? { horizontalBars: true, stackBars: false } : {}), - ...(type === 'line' ? { showLine: true, lineSmooth: true, showArea: false } : {}), - ...(type === 'area' ? { showLine: true, lineSmooth: true, showArea: true } : {}), - ...(type === 'scatter' ? { showLine: false, lineSmooth: true, showArea: false } : {}), - ...(type === 'pie' ? { donut: false } : {}), - ...(type === 'ring' ? { donut: true } : {}), - } - - createElement({ - ...newElement, - options, + themeColors: [theme.value.themeColor], + textColor: theme.value.fontColor, + data: CHART_DEFAULT_DATA[type], }) } diff --git a/src/hooks/useExport.ts b/src/hooks/useExport.ts index d8e5fdfc..9e997385 100644 --- a/src/hooks/useExport.ts +++ b/src/hooks/useExport.ts @@ -611,12 +611,12 @@ export default () => { } let chartColors: string[] = [] - if (el.themeColor.length === 10) chartColors = el.themeColor.map(color => formatColor(color).color) - else if (el.themeColor.length === 1) chartColors = tinycolor(el.themeColor[0]).analogous(10).map(color => formatColor(color.toHexString()).color) + if (el.themeColors.length === 10) chartColors = el.themeColors.map(color => formatColor(color).color) + else if (el.themeColors.length === 1) chartColors = tinycolor(el.themeColors[0]).analogous(10).map(color => formatColor(color.toHexString()).color) else { - const len = el.themeColor.length - const supplement = tinycolor(el.themeColor[len - 1]).analogous(10 + 1 - len).map(color => color.toHexString()) - chartColors = [...el.themeColor.slice(0, len - 1), ...supplement].map(color => formatColor(color).color) + const len = el.themeColors.length + const supplement = tinycolor(el.themeColors[len - 1]).analogous(10 + 1 - len).map(color => color.toHexString()) + chartColors = [...el.themeColors.slice(0, len - 1), ...supplement].map(color => formatColor(color).color) } const options: pptxgen.IChartOpts = { @@ -624,40 +624,63 @@ export default () => { y: el.top / ratioPx2Inch.value, w: el.width / ratioPx2Inch.value, h: el.height / ratioPx2Inch.value, - chartColors: el.chartType === 'pie' ? chartColors : chartColors.slice(0, el.data.series.length), + chartColors: (el.chartType === 'pie' || el.chartType === 'ring') ? chartColors : chartColors.slice(0, el.data.series.length), } - if (el.fill) options.plotArea = { fill: { color: formatColor(el.fill).color } } - if (el.legend) { + const textColor = formatColor(el.textColor || '#000000').color + options.catAxisLabelColor = textColor + options.valAxisLabelColor = textColor + + const fontSize = 14 / ratioPx2Pt.value + options.catAxisLabelFontSize = fontSize + options.valAxisLabelFontSize = fontSize + + if (el.fill || el.outline) { + const plotArea: pptxgen.IChartPropsFillLine = {} + if (el.fill) { + plotArea.fill = { color: formatColor(el.fill).color } + } + if (el.outline) { + plotArea.border = { + pt: el.outline.width! / ratioPx2Pt.value, + color: formatColor(el.outline.color!).color, + } + } + options.plotArea = plotArea + } + + if ((el.data.series.length > 1 && el.chartType !== 'scatter') || el.chartType === 'pie' || el.chartType === 'ring') { options.showLegend = true - options.legendPos = el.legend === 'top' ? 't' : 'b' - options.legendColor = formatColor(el.gridColor || '#000000').color - options.legendFontSize = 14 / ratioPx2Pt.value + options.legendPos = 'b' + options.legendColor = textColor + options.legendFontSize = fontSize } let type = pptx.ChartType.bar if (el.chartType === 'bar') { type = pptx.ChartType.bar - options.barDir = el.options?.horizontalBars ? 'bar' : 'col' + options.barDir = 'col' + } + else if (el.chartType === 'column') { + type = pptx.ChartType.bar + options.barDir = 'bar' } else if (el.chartType === 'line') { - if (el.options?.showArea) type = pptx.ChartType.area - else if (el.options?.showLine === false) { - type = pptx.ChartType.scatter - - chartData.unshift({ name: 'X-Axis', values: Array(el.data.series[0].length).fill(0).map((v, i) => i) }) - options.lineSize = 0 - } - else type = pptx.ChartType.line - - if (el.options?.lineSmooth) options.lineSmooth = true + type = pptx.ChartType.line + } + else if (el.chartType === 'area') { + type = pptx.ChartType.area + } + else if (el.chartType === 'scatter') { + type = pptx.ChartType.scatter + options.lineSize = 0 } else if (el.chartType === 'pie') { - if (el.options?.donut) { - type = pptx.ChartType.doughnut - options.holeSize = 75 - } - else type = pptx.ChartType.pie + type = pptx.ChartType.pie + } + else if (el.chartType === 'ring') { + type = pptx.ChartType.doughnut + options.holeSize = 60 } pptxSlide.addChart(type, chartData, options) diff --git a/src/hooks/useImport.ts b/src/hooks/useImport.ts index 2365f610..ee303b2c 100644 --- a/src/hooks/useImport.ts +++ b/src/hooks/useImport.ts @@ -14,7 +14,6 @@ import type { TableCellStyle, TableCell, ChartType, - ChartOptions, SlideBackground, PPTShapeElement, PPTLineElement, @@ -389,10 +388,9 @@ export default () => { let series: number[][] if (el.chartType === 'scatterChart' || el.chartType === 'bubbleChart') { - const data = el.data - labels = data[0].map(item => item + '') - legends = ['系列1'] - series = [data[1]] + labels = el.data[0].map((item, index) => `坐标${index + 1}`) + legends = ['X', 'Y'] + series = el.data } else { const data = el.data as ChartItem[] @@ -401,32 +399,32 @@ export default () => { series = data.map(item => item.values.map(v => v.y)) } - const options: ChartOptions = {} - let chartType: ChartType = 'bar' switch (el.chartType) { case 'barChart': case 'bar3DChart': chartType = 'bar' - if (el.barDir === 'bar') options.horizontalBars = true - if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stackBars = true + if (el.barDir === 'bar') chartType = 'column' break case 'lineChart': case 'line3DChart': + chartType = 'line' + break case 'areaChart': case 'area3DChart': + chartType = 'area' + break case 'scatterChart': case 'bubbleChart': - chartType = 'line' - if (el.chartType === 'areaChart' || el.chartType === 'area3DChart') options.showArea = true - if (el.chartType === 'scatterChart' || el.chartType === 'bubbleChart') options.showLine = false + chartType = 'scatter' break case 'pieChart': case 'pie3DChart': - case 'doughnutChart': chartType = 'pie' - if (el.chartType === 'doughnutChart') options.donut = true + break + case 'doughnutChart': + chartType = 'ring' break default: } @@ -440,14 +438,13 @@ export default () => { left: el.left, top: el.top, rotate: 0, - themeColor: [theme.value.themeColor], - gridColor: theme.value.fontColor, + themeColors: [theme.value.themeColor], + textColor: theme.value.fontColor, data: { labels, legends, series, }, - options, }) } else if (el.type === 'group' || el.type === 'diagram') { diff --git a/src/types/slides.ts b/src/types/slides.ts index ce5a9fd3..f71db1dd 100644 --- a/src/types/slides.ts +++ b/src/types/slides.ts @@ -395,9 +395,7 @@ export interface PPTLineElement extends Omit - + @@ -15,15 +15,15 @@ diff --git a/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/index.vue b/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/index.vue index 4501b746..43f139c9 100644 --- a/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/index.vue +++ b/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/index.vue @@ -6,63 +6,6 @@ - -
- 条形图样式 - 堆叠样式 -
-
- 环形图样式 -
- - - -
-
图例:
-