feat: 图表添加配置(平滑曲线、堆叠模式)

This commit is contained in:
zxc 2024-09-13 22:58:20 +08:00
parent 495799aafa
commit 2e4a3df988
8 changed files with 110 additions and 30 deletions

View File

@ -660,13 +660,16 @@ export default () => {
if (el.chartType === 'bar') { if (el.chartType === 'bar') {
type = pptx.ChartType.bar type = pptx.ChartType.bar
options.barDir = 'col' options.barDir = 'col'
if (el.options?.stack) options.barGrouping = 'stacked'
} }
else if (el.chartType === 'column') { else if (el.chartType === 'column') {
type = pptx.ChartType.bar type = pptx.ChartType.bar
options.barDir = 'bar' options.barDir = 'bar'
if (el.options?.stack) options.barGrouping = 'stacked'
} }
else if (el.chartType === 'line') { else if (el.chartType === 'line') {
type = pptx.ChartType.line type = pptx.ChartType.line
if (el.options?.lineSmooth) options.lineSmooth = true
} }
else if (el.chartType === 'area') { else if (el.chartType === 'area') {
type = pptx.ChartType.area type = pptx.ChartType.area

View File

@ -19,6 +19,7 @@ import type {
PPTLineElement, PPTLineElement,
ShapeTextAlign, ShapeTextAlign,
PPTTextElement, PPTTextElement,
ChartOptions,
} from '@/types/slides' } from '@/types/slides'
const convertFontSizePtToPx = (html: string, ratio: number) => { const convertFontSizePtToPx = (html: string, ratio: number) => {
@ -398,6 +399,8 @@ export default () => {
legends = data.map(item => item.key) legends = data.map(item => item.key)
series = data.map(item => item.values.map(v => v.y)) series = data.map(item => item.values.map(v => v.y))
} }
const options: ChartOptions = {}
let chartType: ChartType = 'bar' let chartType: ChartType = 'bar'
@ -406,6 +409,7 @@ export default () => {
case 'bar3DChart': case 'bar3DChart':
chartType = 'bar' chartType = 'bar'
if (el.barDir === 'bar') chartType = 'column' if (el.barDir === 'bar') chartType = 'column'
if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
break break
case 'lineChart': case 'lineChart':
case 'line3DChart': case 'line3DChart':
@ -413,6 +417,7 @@ export default () => {
break break
case 'areaChart': case 'areaChart':
case 'area3DChart': case 'area3DChart':
if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
chartType = 'area' chartType = 'area'
break break
case 'scatterChart': case 'scatterChart':
@ -445,6 +450,7 @@ export default () => {
legends, legends,
series, series,
}, },
options,
}) })
} }
else if (el.type === 'group' || el.type === 'diagram') { else if (el.type === 'group' || el.type === 'diagram') {

View File

@ -394,6 +394,12 @@ export interface PPTLineElement extends Omit<PPTBaseElement, 'height' | 'rotate'
export type ChartType = 'bar' | 'column' | 'line' | 'pie' | 'ring' | 'area' | 'scatter' export type ChartType = 'bar' | 'column' | 'line' | 'pie' | 'ring' | 'area' | 'scatter'
export interface ChartOptions {
lineSmooth?: boolean
stack?: boolean
}
export interface ChartData { export interface ChartData {
labels: string[] labels: string[]
legends: string[] legends: string[]
@ -411,6 +417,8 @@ export interface ChartData {
* *
* data: 图表数据 * data: 图表数据
* *
* options: 扩展选项
*
* outline?: 边框 * outline?: 边框
* *
* themeColors: 主题色 * themeColors: 主题色
@ -422,6 +430,7 @@ export interface PPTChartElement extends PPTBaseElement {
fill?: string fill?: string
chartType: ChartType chartType: ChartType
data: ChartData data: ChartData
options?: ChartOptions
outline?: PPTElementOutline outline?: PPTElementOutline
themeColors: string[] themeColors: string[]
textColor?: string textColor?: string

View File

@ -6,6 +6,24 @@
<Divider /> <Divider />
<template v-if="['bar', 'column', 'area', 'line'].includes(handleChartElement.chartType)">
<div class="row">
<Checkbox
v-if="handleChartElement.chartType === 'line'"
@update:value="value => updateOptions({ lineSmooth: value })"
:value="lineSmooth"
>使用平滑曲线</Checkbox>
<Checkbox
v-if="['bar', 'column', 'area'].includes(handleChartElement.chartType)"
@update:value="value => updateOptions({ stack: value })"
:value="stack"
style="flex: 1;"
>堆叠样式</Checkbox>
</div>
<Divider />
</template>
<div class="row"> <div class="row">
<div style="width: 40%;">背景填充</div> <div style="width: 40%;">背景填充</div>
<Popover trigger="click" style="width: 60%;"> <Popover trigger="click" style="width: 60%;">
@ -99,7 +117,7 @@
import { onUnmounted, ref, watch, type Ref } from 'vue' import { onUnmounted, ref, watch, type Ref } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store' import { useMainStore, useSlidesStore } from '@/store'
import type { ChartData, PPTChartElement } from '@/types/slides' import type { ChartData, ChartOptions, PPTChartElement } from '@/types/slides'
import emitter, { EmitterEvents } from '@/utils/emitter' import emitter, { EmitterEvents } from '@/utils/emitter'
import useHistorySnapshot from '@/hooks/useHistorySnapshot' import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import { CHART_PRESET_THEMES } from '@/configs/chart' import { CHART_PRESET_THEMES } from '@/configs/chart'
@ -110,6 +128,7 @@ import ColorButton from '@/components/ColorButton.vue'
import ColorPicker from '@/components/ColorPicker/index.vue' import ColorPicker from '@/components/ColorPicker/index.vue'
import Modal from '@/components/Modal.vue' import Modal from '@/components/Modal.vue'
import Divider from '@/components/Divider.vue' import Divider from '@/components/Divider.vue'
import Checkbox from '@/components/Checkbox.vue'
import Button from '@/components/Button.vue' import Button from '@/components/Button.vue'
import ButtonGroup from '@/components/ButtonGroup.vue' import ButtonGroup from '@/components/ButtonGroup.vue'
import Popover from '@/components/Popover.vue' import Popover from '@/components/Popover.vue'
@ -131,11 +150,26 @@ const fill = ref<string>('#000')
const themeColors = ref<string[]>([]) const themeColors = ref<string[]>([])
const textColor = ref('') const textColor = ref('')
const lineSmooth = ref(false)
const stack = ref(false)
watch(handleElement, () => { watch(handleElement, () => {
if (!handleElement.value || handleElement.value.type !== 'chart') return if (!handleElement.value || handleElement.value.type !== 'chart') return
fill.value = handleElement.value.fill || '#fff' fill.value = handleElement.value.fill || '#fff'
lineSmooth.value = false
stack.value = false
if (handleElement.value.options) {
const {
lineSmooth: _lineSmooth,
stack: _stack,
} = handleElement.value.options
if (_lineSmooth !== undefined) lineSmooth.value = _lineSmooth
if (_stack !== undefined) stack.value = _stack
}
themeColors.value = handleElement.value.themeColors themeColors.value = handleElement.value.themeColors
textColor.value = handleElement.value.textColor || '#333' textColor.value = handleElement.value.textColor || '#333'
}, { deep: true, immediate: true }) }, { deep: true, immediate: true })
@ -156,6 +190,15 @@ const updateFill = (value: string) => {
updateElement({ fill: value }) updateElement({ fill: value })
} }
//
const updateOptions = (optionProps: ChartOptions) => {
console.log(optionProps)
const _handleElement = handleElement.value as PPTChartElement
const newOptions = { ..._handleElement.options, ...optionProps }
updateElement({ options: newOptions })
}
// //
const updateTheme = (color: string, index: number) => { const updateTheme = (color: string, index: number) => {
const props = { const props = {

View File

@ -29,7 +29,7 @@
:data="elementInfo.data" :data="elementInfo.data"
:themeColors="elementInfo.themeColors" :themeColors="elementInfo.themeColors"
:textColor="elementInfo.textColor" :textColor="elementInfo.textColor"
:legends="elementInfo.data.legends" :options="elementInfo.options"
/> />
</div> </div>
</div> </div>

View File

@ -6,7 +6,7 @@
import { onMounted, ref, computed, watch } from 'vue' import { onMounted, ref, computed, watch } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import tinycolor from 'tinycolor2' import tinycolor from 'tinycolor2'
import type { ChartData, ChartType } from '@/types/slides' import type { ChartData, ChartOptions, ChartType } from '@/types/slides'
import { getChartOption } from './chartOption' import { getChartOption } from './chartOption'
const props = defineProps<{ const props = defineProps<{
@ -15,8 +15,8 @@ const props = defineProps<{
type: ChartType type: ChartType
data: ChartData data: ChartData
themeColors: string[] themeColors: string[]
legends: string[]
textColor?: string textColor?: string
options?: ChartOptions
}>() }>()
let chart: echarts.ECharts | null = null let chart: echarts.ECharts | null = null
@ -40,6 +40,8 @@ const updateOption = () => {
data: props.data, data: props.data,
themeColors: themeColors.value, themeColors: themeColors.value,
textColor: props.textColor, textColor: props.textColor,
lineSmooth: props.options?.lineSmooth || false,
stack: props.options?.stack || false,
}) })
if (option) chart!.setOption(option, true) if (option) chart!.setOption(option, true)
} }

View File

@ -6,6 +6,8 @@ export interface ChartOptionPayload {
data: ChartData data: ChartData
themeColors: string[] themeColors: string[]
textColor?: string textColor?: string
lineSmooth?: boolean
stack?: boolean
} }
export const getChartOption = ({ export const getChartOption = ({
@ -13,6 +15,8 @@ export const getChartOption = ({
data, data,
themeColors, themeColors,
textColor, textColor,
lineSmooth,
stack,
}: ChartOptionPayload): echarts.EChartsOption | null => { }: ChartOptionPayload): echarts.EChartsOption | null => {
if(type === 'bar') { if(type === 'bar') {
return { return {
@ -39,14 +43,18 @@ export const getChartOption = ({
yAxis: { yAxis: {
type: 'value', type: 'value',
}, },
series: data.series.map((item, index) => ({ series: data.series.map((item, index) => {
data: item, const seriesItem: echarts.SeriesOption = {
name: data.legends[index], data: item,
type: 'bar', name: data.legends[index],
label: { type: 'bar',
show: true, label: {
}, show: true,
})), },
}
if (stack) seriesItem.stack = 'A'
return seriesItem
}),
} }
} }
if(type === 'column') { if(type === 'column') {
@ -74,14 +82,18 @@ export const getChartOption = ({
xAxis: { xAxis: {
type: 'value', type: 'value',
}, },
series: data.series.map((item, index) => ({ series: data.series.map((item, index) => {
data: item, const seriesItem: echarts.SeriesOption = {
name: data.legends[index], data: item,
type: 'bar', name: data.legends[index],
label: { type: 'bar',
show: true, label: {
}, show: true,
})), },
}
if (stack) seriesItem.stack = 'A'
return seriesItem
}),
} }
} }
if(type === 'line') { if(type === 'line') {
@ -113,6 +125,7 @@ export const getChartOption = ({
data: item, data: item,
name: data.legends[index], name: data.legends[index],
type: 'line', type: 'line',
smooth: lineSmooth,
label: { label: {
show: true, show: true,
}, },
@ -230,15 +243,19 @@ export const getChartOption = ({
yAxis: { yAxis: {
type: 'value', type: 'value',
}, },
series: data.series.map((item, index) => ({ series: data.series.map((item, index) => {
data: item, const seriesItem: echarts.SeriesOption = {
name: data.legends[index], data: item,
type: 'line', name: data.legends[index],
areaStyle: {}, type: 'line',
label: { areaStyle: {},
show: true, label: {
}, show: true,
})), },
}
if (stack) seriesItem.stack = 'A'
return seriesItem
}),
} }
} }
if(type === 'scatter') { if(type === 'scatter') {

View File

@ -34,7 +34,7 @@
:data="elementInfo.data" :data="elementInfo.data"
:themeColors="elementInfo.themeColors" :themeColors="elementInfo.themeColors"
:textColor="elementInfo.textColor" :textColor="elementInfo.textColor"
:legends="elementInfo.data.legends" :options="elementInfo.options"
/> />
</div> </div>
</div> </div>