添加基础的图表元素
This commit is contained in:
parent
ae4d82e701
commit
f79712eef3
|
@ -0,0 +1,108 @@
|
|||
<template>
|
||||
<div class="chart">
|
||||
<div
|
||||
class="chart-content"
|
||||
ref="chartRef"
|
||||
:style="{
|
||||
width: width + 'px',
|
||||
height: height + 'px',
|
||||
transform: `scale(${1 / slideScale})`,
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, onMounted, PropType, ref, Ref, watch } from 'vue'
|
||||
import upperFirst from 'lodash/upperFirst'
|
||||
import Chartist, {
|
||||
IChartistLineChart,
|
||||
IChartistBarChart,
|
||||
IChartistPieChart,
|
||||
ILineChartOptions,
|
||||
IBarChartOptions,
|
||||
IPieChartOptions,
|
||||
} from 'chartist'
|
||||
import { ChartData, ChartType } from '@/types/slides'
|
||||
|
||||
import 'chartist/dist/scss/chartist.scss'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'chart',
|
||||
props: {
|
||||
width: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<ChartType>,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<ChartData>,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Object as PropType<ILineChartOptions & IBarChartOptions & IPieChartOptions>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const chartRef = ref<HTMLElement | null>(null)
|
||||
const slideScale: Ref<number> = inject('slideScale') || ref(1)
|
||||
|
||||
let chart: IChartistLineChart | IChartistBarChart | IChartistPieChart | undefined
|
||||
|
||||
const getDataAndOptions = () => {
|
||||
const propsOptopns = props.options || {}
|
||||
const options = {
|
||||
...propsOptopns,
|
||||
width: props.width * slideScale.value,
|
||||
height: props.height * slideScale.value,
|
||||
}
|
||||
const data = props.type === 'pie' ? { ...props.data, series: props.data.series[0] } : props.data
|
||||
return { data, options }
|
||||
}
|
||||
|
||||
const renderChart = () => {
|
||||
if(!chartRef.value) return
|
||||
|
||||
const type = upperFirst(props.type)
|
||||
const { data, options } = getDataAndOptions()
|
||||
chart = new Chartist[type](chartRef.value, data, options)
|
||||
}
|
||||
|
||||
const updateChart = () => {
|
||||
if(!chart) {
|
||||
renderChart()
|
||||
return
|
||||
}
|
||||
const { data, options } = getDataAndOptions()
|
||||
chart.update(data, options)
|
||||
}
|
||||
|
||||
watch([
|
||||
() => props.width,
|
||||
() => props.height,
|
||||
() => props.data,
|
||||
slideScale,
|
||||
], updateChart)
|
||||
|
||||
onMounted(renderChart)
|
||||
|
||||
return {
|
||||
slideScale,
|
||||
chartRef,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chart-content {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
</style>
|
|
@ -49,6 +49,20 @@ export const DEFAULT_TABLE = {
|
|||
outline: {
|
||||
width: 2,
|
||||
style: 'solid',
|
||||
color: DEFAULT_COLOR
|
||||
color: DEFAULT_COLOR,
|
||||
},
|
||||
}
|
||||
|
||||
export const DEFAULT_FORMULA = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
}
|
||||
|
||||
export const MIN_SIZE = {
|
||||
text: 20,
|
||||
image: 20,
|
||||
shape: 15,
|
||||
chart: 200,
|
||||
table: 20,
|
||||
formula: 20,
|
||||
}
|
|
@ -1,6 +1,44 @@
|
|||
import { Slide } from '@/types/slides'
|
||||
|
||||
export const slides: Slide[] = [
|
||||
{
|
||||
id: 'xsxa123',
|
||||
elements: [
|
||||
{
|
||||
id: 'sdasaxsxs',
|
||||
type: 'chart',
|
||||
left: 100,
|
||||
top: 100,
|
||||
width: 400,
|
||||
height: 400,
|
||||
chartType: 'pie',
|
||||
data: {
|
||||
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
|
||||
series: [
|
||||
[5, 2, 4, 2, 10],
|
||||
],
|
||||
},
|
||||
options: {
|
||||
donut: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sdasaxs',
|
||||
type: 'chart',
|
||||
left: 600,
|
||||
top: 100,
|
||||
width: 300,
|
||||
height: 300,
|
||||
chartType: 'line',
|
||||
data: {
|
||||
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
|
||||
series: [
|
||||
[5, 2, 4, 2, 10],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'xxx1',
|
||||
background: {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { IBarChartOptions, ILineChartOptions, IPieChartOptions } from 'chartist'
|
||||
|
||||
export interface PPTElementShadow {
|
||||
h: number;
|
||||
v: number;
|
||||
|
@ -105,7 +107,7 @@ export interface PPTLineElement {
|
|||
shadow?: PPTElementShadow;
|
||||
}
|
||||
|
||||
export type ChartType = 'bar' | 'horizontalBar' | 'line' | 'pie' | 'doughnut' | 'polarArea' | 'radar'
|
||||
export type ChartType = 'bar' | 'line' | 'pie'
|
||||
export interface ChartData {
|
||||
labels: string[];
|
||||
series: number[][];
|
||||
|
@ -121,6 +123,7 @@ export interface PPTChartElement {
|
|||
height: number;
|
||||
chartType: ChartType;
|
||||
data: ChartData;
|
||||
options?: ILineChartOptions & IBarChartOptions & IPieChartOptions;
|
||||
outline?: PPTElementOutline;
|
||||
}
|
||||
|
||||
|
@ -145,8 +148,20 @@ export interface PPTTableElement {
|
|||
colSizes: number[];
|
||||
data: TableElementCell[][];
|
||||
}
|
||||
export interface PPTFormulaElement {
|
||||
type: 'formula';
|
||||
id: string;
|
||||
left: number;
|
||||
top: number;
|
||||
lock?: boolean;
|
||||
groupId?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
latex: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement
|
||||
export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement | PPTFormulaElement
|
||||
|
||||
export interface PPTAnimation {
|
||||
elId: string;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { State, MutationTypes } from '@/store'
|
|||
import { ElementTypes, PPTElement, PPTImageElement, PPTLineElement, PPTShapeElement } from '@/types/slides'
|
||||
import { OperateResizeHandlers, AlignmentLineProps, MultiSelectRange } from '@/types/edit'
|
||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||
import { MIN_SIZE } from '@/configs/element'
|
||||
import { AlignLine, uniqAlignLines } from '@/utils/element'
|
||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||
|
||||
|
@ -111,7 +112,7 @@ export default (
|
|||
const startPageX = e.pageX
|
||||
const startPageY = e.pageY
|
||||
|
||||
const minSize = 15
|
||||
const minSize = MIN_SIZE[element.type] || 20
|
||||
const getSizeWithinRange = (size: number) => size < minSize ? minSize : size
|
||||
|
||||
let points: ReturnType<typeof getRotateElementPoints>
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, Ref, ref, watch, watchEffect } from 'vue'
|
||||
import { computed, defineComponent, provide, Ref, ref, watch, watchEffect } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import throttle from 'lodash/throttle'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
|
@ -215,6 +215,8 @@ export default defineComponent({
|
|||
]
|
||||
}
|
||||
|
||||
provide('slideScale', canvasScale)
|
||||
|
||||
return {
|
||||
elementList,
|
||||
activeElementIdList,
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
<div class="row">
|
||||
<div style="flex: 3;">大小:</div>
|
||||
<InputNumber
|
||||
:min="15"
|
||||
:min="minSize"
|
||||
:max="1500"
|
||||
:step="5"
|
||||
:value="width"
|
||||
|
@ -83,7 +83,7 @@
|
|||
</template>
|
||||
<div style="flex: 1;" v-else></div>
|
||||
<InputNumber
|
||||
:min="15"
|
||||
:min="minSize"
|
||||
:max="800"
|
||||
:step="5"
|
||||
:disabled="handleElement.type === 'text'"
|
||||
|
@ -138,6 +138,7 @@ import { useStore } from 'vuex'
|
|||
import round from 'lodash/round'
|
||||
import { MutationTypes, State } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { MIN_SIZE } from '@/configs/element'
|
||||
import useOrderElement from '@/hooks/useOrderElement'
|
||||
import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas'
|
||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||
|
@ -155,6 +156,11 @@ export default defineComponent({
|
|||
const rotate = ref(0)
|
||||
const fixedRatio = ref(false)
|
||||
|
||||
const minSize = computed(() => {
|
||||
if(!handleElement.value) return 20
|
||||
return MIN_SIZE[handleElement.value.type] || 20
|
||||
})
|
||||
|
||||
watch(handleElement, () => {
|
||||
if(!handleElement.value) return
|
||||
|
||||
|
@ -228,6 +234,7 @@ export default defineComponent({
|
|||
height,
|
||||
rotate,
|
||||
fixedRatio,
|
||||
minSize,
|
||||
updateLeft,
|
||||
updateTop,
|
||||
updateWidth,
|
||||
|
|
|
@ -23,7 +23,7 @@ import BaseImageElement from '@/views/components/element/ImageElement/BaseImageE
|
|||
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
||||
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
|
||||
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
|
||||
import BaseChartElement from '@/views/components/element/ChartElement/BaseChartElement.vue'
|
||||
import ScreenChartElement from '@/views/components/element/ChartElement/ScreenChartElement.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'screen-element',
|
||||
|
@ -48,7 +48,7 @@ export default defineComponent({
|
|||
[ElementTypes.TEXT]: BaseTextElement,
|
||||
[ElementTypes.SHAPE]: BaseShapeElement,
|
||||
[ElementTypes.LINE]: BaseLineElement,
|
||||
[ElementTypes.CHART]: BaseChartElement,
|
||||
[ElementTypes.CHART]: ScreenChartElement,
|
||||
}
|
||||
return elementTypeMap[props.elementInfo.type] || null
|
||||
})
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, onUnmounted, Ref, ref } from 'vue'
|
||||
import { computed, defineComponent, onMounted, onUnmounted, provide, Ref, ref } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import throttle from 'lodash/throttle'
|
||||
import { MutationTypes, State } from '@/store'
|
||||
|
@ -216,6 +216,8 @@ export default defineComponent({
|
|||
},
|
||||
]
|
||||
}
|
||||
|
||||
provide('slideScale', scale)
|
||||
|
||||
return {
|
||||
slides,
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
:height="elementInfo.height"
|
||||
:outline="elementInfo.outline"
|
||||
/>
|
||||
Chart
|
||||
<IconChartLine fill="#d70206" strokeWidth="2" :size="size" v-if="elementInfo.chartType === 'line'" />
|
||||
<IconChartHistogram fill="#d70206" strokeWidth="2" :size="size" v-else-if="elementInfo.chartType === 'bar'" />
|
||||
<IconChartProportion fill="#d70206" strokeWidth="2" :size="size" v-else-if="elementInfo.chartType === 'pie'" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import { PPTChartElement } from '@/types/slides'
|
||||
|
||||
import ElementOutline from '@/views/components/element/ElementOutline.vue'
|
||||
|
@ -35,6 +37,13 @@ export default defineComponent({
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const size = computed(() => Math.min(props.elementInfo.width, props.elementInfo.height))
|
||||
|
||||
return {
|
||||
size,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -46,5 +55,10 @@ export default defineComponent({
|
|||
.element-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: .5;
|
||||
background-color: rgba($color: #000, $alpha: .05);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div class="screen-element-chart"
|
||||
:style="{
|
||||
top: elementInfo.top + 'px',
|
||||
left: elementInfo.left + 'px',
|
||||
width: elementInfo.width + 'px',
|
||||
height: elementInfo.height + 'px',
|
||||
}"
|
||||
>
|
||||
<div class="element-content">
|
||||
<ElementOutline
|
||||
:width="elementInfo.width"
|
||||
:height="elementInfo.height"
|
||||
:outline="elementInfo.outline"
|
||||
/>
|
||||
<Chart
|
||||
:width="elementInfo.width"
|
||||
:height="elementInfo.height"
|
||||
:type="elementInfo.chartType"
|
||||
:data="elementInfo.data"
|
||||
:options="elementInfo.options"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { PPTChartElement } from '@/types/slides'
|
||||
|
||||
import ElementOutline from '@/views/components/element/ElementOutline.vue'
|
||||
import Chart from '@/components/Chart.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'screen-element-chart',
|
||||
components: {
|
||||
ElementOutline,
|
||||
Chart,
|
||||
},
|
||||
props: {
|
||||
elementInfo: {
|
||||
type: Object as PropType<PPTChartElement>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.screen-element-chart {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.element-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -18,7 +18,13 @@
|
|||
:height="elementInfo.height"
|
||||
:outline="elementInfo.outline"
|
||||
/>
|
||||
Chart
|
||||
<Chart
|
||||
:width="elementInfo.width"
|
||||
:height="elementInfo.height"
|
||||
:type="elementInfo.chartType"
|
||||
:data="elementInfo.data"
|
||||
:options="elementInfo.options"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -29,11 +35,13 @@ import { PPTChartElement } from '@/types/slides'
|
|||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
|
||||
import ElementOutline from '@/views/components/element/ElementOutline.vue'
|
||||
import Chart from '@/components/Chart.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editable-element-chart',
|
||||
components: {
|
||||
ElementOutline,
|
||||
Chart,
|
||||
},
|
||||
props: {
|
||||
elementInfo: {
|
||||
|
|
Loading…
Reference in New Issue