mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
添加图表元素
This commit is contained in:
parent
c538cb0fc1
commit
c4c87b1b9c
1124
package-lock.json
generated
1124
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,131 +1,130 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="chart"
|
class="chart"
|
||||||
:style="{
|
:style="{
|
||||||
width: width + 'px',
|
width: width * scale + 'px',
|
||||||
height: height + 'px',
|
height: height * scale + 'px',
|
||||||
}"
|
transform: `scale(${1 / scale})`,
|
||||||
>
|
}"
|
||||||
<canvas ref="canvasRef"></canvas>
|
>
|
||||||
</div>
|
<canvas ref="canvasRef"></canvas>
|
||||||
</template>
|
</div>
|
||||||
|
</template>
|
||||||
<script lang="ts">
|
|
||||||
import { computed, defineComponent, onMounted, onUnmounted, PropType, ref } from 'vue'
|
<script lang="ts">
|
||||||
import Chart from 'chart.js'
|
import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, watch } from 'vue'
|
||||||
|
import Chart from 'chart.js'
|
||||||
interface ChartData {
|
import { ChartData, ChartType } from '@/types/slides'
|
||||||
labels: string[];
|
|
||||||
values: number[][];
|
const commonConfigs = {
|
||||||
}
|
backgroundColor: 'rgba(209, 68, 36, 0.3)',
|
||||||
|
borderColor: 'rgba(209, 68, 36)',
|
||||||
// const data = {
|
borderWidth: 2,
|
||||||
// labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
}
|
||||||
// values: [
|
|
||||||
// [12, 19, 3, 5, 2, 3],
|
const defaultOptions: Chart.ChartOptions = {
|
||||||
// [22, 9, 13, 25, 12, 5],
|
maintainAspectRatio: false,
|
||||||
// ]
|
animation: {
|
||||||
// }
|
duration: 0,
|
||||||
|
},
|
||||||
// bar
|
hover: {
|
||||||
// horizontalBar
|
animationDuration: 0,
|
||||||
// line
|
},
|
||||||
// radar
|
responsiveAnimationDuration: 0,
|
||||||
// pie
|
layout: {
|
||||||
// doughnut
|
padding: {
|
||||||
// polarArea
|
left: 5,
|
||||||
|
right: 5,
|
||||||
export default defineComponent({
|
top: 5,
|
||||||
name: 'chart',
|
bottom: 5,
|
||||||
props: {
|
},
|
||||||
type: {
|
},
|
||||||
type: String,
|
legend: {
|
||||||
required: true,
|
display: false,
|
||||||
},
|
},
|
||||||
width: {
|
elements: {
|
||||||
type: Number,
|
line: {
|
||||||
required: true,
|
tension: 0,
|
||||||
},
|
fill: false,
|
||||||
height: {
|
...commonConfigs,
|
||||||
type: Number,
|
},
|
||||||
required: true,
|
rectangle: {
|
||||||
},
|
...commonConfigs,
|
||||||
data: {
|
},
|
||||||
type: Object as PropType<ChartData>,
|
arc: {
|
||||||
required: true,
|
...commonConfigs,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
}
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
|
||||||
let chart: Chart
|
export default defineComponent({
|
||||||
|
name: 'chart',
|
||||||
const data = computed(() => ({
|
props: {
|
||||||
labels: props.data.labels,
|
type: {
|
||||||
datasets: props.data.values.map(item => {
|
type: String as PropType<ChartType>,
|
||||||
return {
|
required: true,
|
||||||
data: item,
|
},
|
||||||
backgroundColor: [
|
width: {
|
||||||
'rgba(255, 99, 132, 0.2)',
|
type: Number,
|
||||||
'rgba(54, 162, 235, 0.2)',
|
required: true,
|
||||||
'rgba(255, 206, 86, 0.2)',
|
},
|
||||||
'rgba(75, 192, 192, 0.2)',
|
height: {
|
||||||
'rgba(153, 102, 255, 0.2)',
|
type: Number,
|
||||||
'rgba(255, 159, 64, 0.2)',
|
required: true,
|
||||||
],
|
},
|
||||||
borderColor: [
|
data: {
|
||||||
'rgba(255, 99, 132, 0.5)',
|
type: Object as PropType<ChartData>,
|
||||||
'rgba(54, 162, 235, 0.5)',
|
required: true,
|
||||||
'rgba(255, 206, 86, 0.5)',
|
},
|
||||||
'rgba(75, 192, 192, 0.5)',
|
options: {
|
||||||
'rgba(153, 102, 255, 0.5)',
|
type: Object as PropType<Chart.ChartOptions>,
|
||||||
'rgba(255, 159, 64, 0.5)',
|
},
|
||||||
],
|
scale: {
|
||||||
borderWidth: 1,
|
type: Number,
|
||||||
}
|
default: 1,
|
||||||
}),
|
},
|
||||||
}))
|
},
|
||||||
|
setup(props) {
|
||||||
onMounted(() => {
|
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||||
if(!canvasRef.value) return
|
let chart: Chart
|
||||||
const ctx = canvasRef.value.getContext('2d') as CanvasRenderingContext2D
|
|
||||||
chart = new Chart(ctx, {
|
const data = computed(() => ({
|
||||||
type: props.type,
|
labels: props.data.labels,
|
||||||
data: data.value,
|
datasets: props.data.series.map(item => ({ data: item })),
|
||||||
options: {
|
}))
|
||||||
maintainAspectRatio: false,
|
|
||||||
animation: {
|
const options = computed(() => {
|
||||||
duration: 0,
|
const options = props.options || {}
|
||||||
},
|
return { ...defaultOptions, ...options }
|
||||||
hover: {
|
})
|
||||||
animationDuration: 0,
|
|
||||||
},
|
watch(data, () => {
|
||||||
responsiveAnimationDuration: 0,
|
if(!chart) return
|
||||||
layout: {
|
chart.data = data.value
|
||||||
padding: {
|
chart.update()
|
||||||
left: 8,
|
})
|
||||||
right: 8,
|
|
||||||
top: 8,
|
onMounted(() => {
|
||||||
bottom: 8
|
if(!canvasRef.value) return
|
||||||
},
|
const ctx = canvasRef.value.getContext('2d') as CanvasRenderingContext2D
|
||||||
},
|
chart = new Chart(ctx, {
|
||||||
legend: {
|
type: props.type,
|
||||||
display: false,
|
data: data.value,
|
||||||
},
|
options: options.value,
|
||||||
elements: {
|
})
|
||||||
line: {
|
})
|
||||||
tension: 0,
|
|
||||||
fill: false,
|
onUnmounted(() => chart.destroy())
|
||||||
},
|
|
||||||
},
|
return {
|
||||||
},
|
canvasRef,
|
||||||
})
|
}
|
||||||
})
|
},
|
||||||
|
})
|
||||||
onUnmounted(() => chart.destroy())
|
</script>
|
||||||
|
|
||||||
return {
|
<style lang="scss" scoped>
|
||||||
canvasRef,
|
.chart {
|
||||||
}
|
transform-origin: 0 0;
|
||||||
},
|
}
|
||||||
})
|
</style>
|
||||||
</script>
|
|
@ -23,5 +23,6 @@ export default {
|
|||||||
vertical-align: -0.15em;
|
vertical-align: -0.15em;
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
outline: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -35,6 +35,12 @@ export const DEFAULT_CHART = {
|
|||||||
top: 0,
|
top: 0,
|
||||||
width: 500,
|
width: 500,
|
||||||
height: 500,
|
height: 500,
|
||||||
|
data: {
|
||||||
|
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
||||||
|
series: [
|
||||||
|
[12, 19, 3, 5, 2, 18],
|
||||||
|
]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_TABLE = {
|
export const DEFAULT_TABLE = {
|
||||||
|
@ -3,7 +3,7 @@ import { MutationTypes } from '@/store'
|
|||||||
import { createRandomCode } from '@/utils/common'
|
import { createRandomCode } from '@/utils/common'
|
||||||
import { getImageSize } from '@/utils/image'
|
import { getImageSize } from '@/utils/image'
|
||||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||||
import { PPTElement, TableElementCell } from '@/types/slides'
|
import { ChartType, PPTElement, TableElementCell } from '@/types/slides'
|
||||||
import { ShapePoolItem } from '@/configs/shapes'
|
import { ShapePoolItem } from '@/configs/shapes'
|
||||||
import { LinePoolItem } from '@/configs/lines'
|
import { LinePoolItem } from '@/configs/lines'
|
||||||
import {
|
import {
|
||||||
@ -67,13 +67,12 @@ export default () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createChartElement = (chartType: string, data: string) => {
|
const createChartElement = (chartType: ChartType) => {
|
||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_CHART,
|
...DEFAULT_CHART,
|
||||||
type: 'chart',
|
type: 'chart',
|
||||||
id: createRandomCode(),
|
id: createRandomCode(),
|
||||||
chartType,
|
chartType,
|
||||||
data,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,26 @@ export const slides: Slide[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'sahduyi',
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
id: 'sdasdax',
|
||||||
|
type: 'chart',
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 400,
|
||||||
|
chartType: 'line',
|
||||||
|
data: {
|
||||||
|
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
||||||
|
series: [
|
||||||
|
[12, 19, 3, 5, 4, 18],
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'sajd172',
|
id: 'sajd172',
|
||||||
elements: [
|
elements: [
|
||||||
|
@ -105,6 +105,11 @@ export interface PPTLineElement {
|
|||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ChartType = 'bar' | 'horizontalBar' | 'line' | 'pie' | 'doughnut' | 'polarArea' | 'radar'
|
||||||
|
export interface ChartData {
|
||||||
|
labels: string[];
|
||||||
|
series: number[][];
|
||||||
|
}
|
||||||
export interface PPTChartElement {
|
export interface PPTChartElement {
|
||||||
type: 'chart';
|
type: 'chart';
|
||||||
id: string;
|
id: string;
|
||||||
@ -114,8 +119,8 @@ export interface PPTChartElement {
|
|||||||
groupId?: string;
|
groupId?: string;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
chartType: string;
|
chartType: ChartType;
|
||||||
data: string;
|
data: ChartData;
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, PropType } from 'vue'
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
import { PPTElement } from '@/types/slides'
|
import { ElementTypes, PPTElement } from '@/types/slides'
|
||||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||||
|
|
||||||
import useLockElement from '@/hooks/useLockElement'
|
import useLockElement from '@/hooks/useLockElement'
|
||||||
@ -33,6 +33,7 @@ import ImageElement from '@/views/components/element/ImageElement/index.vue'
|
|||||||
import TextElement from '@/views/components/element/TextElement/index.vue'
|
import TextElement from '@/views/components/element/TextElement/index.vue'
|
||||||
import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
|
import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
|
||||||
import LineElement from '@/views/components/element/LineElement/index.vue'
|
import LineElement from '@/views/components/element/LineElement/index.vue'
|
||||||
|
import ChartElement from '@/views/components/element/ChartElement/index.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element',
|
name: 'editable-element',
|
||||||
@ -57,10 +58,11 @@ export default defineComponent({
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
const currentElementComponent = computed(() => {
|
const currentElementComponent = computed(() => {
|
||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': ImageElement,
|
[ElementTypes.IMAGE]: ImageElement,
|
||||||
'text': TextElement,
|
[ElementTypes.TEXT]: TextElement,
|
||||||
'shape': ShapeElement,
|
[ElementTypes.SHAPE]: ShapeElement,
|
||||||
'line': LineElement,
|
[ElementTypes.LINE]: LineElement,
|
||||||
|
[ElementTypes.CHART]: ChartElement,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
|
75
src/views/Editor/Canvas/Operate/ChartElementOperate.vue
Normal file
75
src/views/Editor/Canvas/Operate/ChartElementOperate.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chart-element-operate">
|
||||||
|
<BorderLine
|
||||||
|
class="operate-border-line"
|
||||||
|
v-for="line in borderLines"
|
||||||
|
:key="line.type"
|
||||||
|
:type="line.type"
|
||||||
|
:style="line.style"
|
||||||
|
/>
|
||||||
|
<template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)">
|
||||||
|
<ResizeHandler
|
||||||
|
class="operate-resize-handler"
|
||||||
|
v-for="point in resizeHandlers"
|
||||||
|
:key="point.direction"
|
||||||
|
:type="point.direction"
|
||||||
|
:style="point.style"
|
||||||
|
@mousedown.stop="$event => scaleElement($event, elementInfo, point.direction)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import { State } from '@/store'
|
||||||
|
|
||||||
|
import { PPTShapeElement } from '@/types/slides'
|
||||||
|
import { OperateResizeHandler } from '@/types/edit'
|
||||||
|
import useCommonOperate from '../hooks/useCommonOperate'
|
||||||
|
|
||||||
|
import ResizeHandler from './ResizeHandler.vue'
|
||||||
|
import BorderLine from './BorderLine.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'chart-element-operate',
|
||||||
|
inheritAttrs: false,
|
||||||
|
components: {
|
||||||
|
ResizeHandler,
|
||||||
|
BorderLine,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elementInfo: {
|
||||||
|
type: Object as PropType<PPTShapeElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isActiveGroupElement: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isMultiSelect: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
scaleElement: {
|
||||||
|
type: Function as PropType<(e: MouseEvent, element: PPTShapeElement, command: OperateResizeHandler) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const store = useStore<State>()
|
||||||
|
const canvasScale = computed(() => store.state.canvasScale)
|
||||||
|
|
||||||
|
const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
|
||||||
|
const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
|
||||||
|
const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
||||||
|
|
||||||
|
return {
|
||||||
|
scaleWidth,
|
||||||
|
resizeHandlers,
|
||||||
|
borderLines,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="text-element-operate">
|
<div class="shape-element-operate">
|
||||||
<BorderLine
|
<BorderLine
|
||||||
class="operate-border-line"
|
class="operate-border-line"
|
||||||
v-for="line in borderLines"
|
v-for="line in borderLines"
|
||||||
@ -39,7 +39,7 @@ import ResizeHandler from './ResizeHandler.vue'
|
|||||||
import BorderLine from './BorderLine.vue'
|
import BorderLine from './BorderLine.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'text-element-operate',
|
name: 'shape-element-operate',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
components: {
|
components: {
|
||||||
RotateHandler,
|
RotateHandler,
|
||||||
|
@ -33,13 +33,14 @@
|
|||||||
import { defineComponent, PropType, computed, Ref } from 'vue'
|
import { defineComponent, PropType, computed, Ref } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { State } from '@/store'
|
import { State } from '@/store'
|
||||||
import { PPTElement, Slide } from '@/types/slides'
|
import { ElementTypes, PPTElement, Slide } from '@/types/slides'
|
||||||
import { OperateLineHandler, OperateResizeHandler } from '@/types/edit'
|
import { OperateLineHandler, OperateResizeHandler } from '@/types/edit'
|
||||||
|
|
||||||
import ImageElementOperate from './ImageElementOperate.vue'
|
import ImageElementOperate from './ImageElementOperate.vue'
|
||||||
import TextElementOperate from './TextElementOperate.vue'
|
import TextElementOperate from './TextElementOperate.vue'
|
||||||
import ShapeElementOperate from './ShapeElementOperate.vue'
|
import ShapeElementOperate from './ShapeElementOperate.vue'
|
||||||
import LineElementOperate from './LineElementOperate.vue'
|
import LineElementOperate from './LineElementOperate.vue'
|
||||||
|
import ChartElementOperate from './ChartElementOperate.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'operate',
|
name: 'operate',
|
||||||
@ -85,10 +86,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
const currentOperateComponent = computed(() => {
|
const currentOperateComponent = computed(() => {
|
||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': ImageElementOperate,
|
[ElementTypes.IMAGE]: ImageElementOperate,
|
||||||
'text': TextElementOperate,
|
[ElementTypes.TEXT]: TextElementOperate,
|
||||||
'shape': ShapeElementOperate,
|
[ElementTypes.SHAPE]: ShapeElementOperate,
|
||||||
'line': LineElementOperate,
|
[ElementTypes.LINE]: LineElementOperate,
|
||||||
|
[ElementTypes.CHART]: ChartElementOperate,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chart-style-panel">
|
||||||
|
chart-style-panel
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'chart-style-panel',
|
||||||
|
})
|
||||||
|
</script>
|
@ -17,6 +17,7 @@ import TextStylePanel from './TextStylePanel.vue'
|
|||||||
import ImageStylePanel from './ImageStylePanel.vue'
|
import ImageStylePanel from './ImageStylePanel.vue'
|
||||||
import ShapeStylePanel from './ShapeStylePanel.vue'
|
import ShapeStylePanel from './ShapeStylePanel.vue'
|
||||||
import LineStylePanel from './LineStylePanel.vue'
|
import LineStylePanel from './LineStylePanel.vue'
|
||||||
|
import ChartStylePanel from './ChartStylePanel.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'element-style-panel',
|
name: 'element-style-panel',
|
||||||
@ -32,6 +33,7 @@ export default defineComponent({
|
|||||||
[ElementTypes.IMAGE]: ImageStylePanel,
|
[ElementTypes.IMAGE]: ImageStylePanel,
|
||||||
[ElementTypes.SHAPE]: ShapeStylePanel,
|
[ElementTypes.SHAPE]: ShapeStylePanel,
|
||||||
[ElementTypes.LINE]: LineStylePanel,
|
[ElementTypes.LINE]: LineStylePanel,
|
||||||
|
[ElementTypes.CHART]: ChartStylePanel,
|
||||||
}
|
}
|
||||||
return panelMap[handleElement.value.type] || null
|
return panelMap[handleElement.value.type] || null
|
||||||
})
|
})
|
||||||
|
@ -29,7 +29,7 @@ export default {
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.color-btn-icon {
|
svg.color-btn-icon {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<component
|
<component
|
||||||
:is="currentElementComponent"
|
:is="currentElementComponent"
|
||||||
:elementInfo="elementInfo"
|
:elementInfo="elementInfo"
|
||||||
|
target="screen"
|
||||||
></component>
|
></component>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -17,12 +18,13 @@
|
|||||||
import { computed, defineComponent, PropType, Ref } from 'vue'
|
import { computed, defineComponent, PropType, Ref } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { State } from '@/store'
|
import { State } from '@/store'
|
||||||
import { PPTElement, Slide } from '@/types/slides'
|
import { ElementTypes, PPTElement, Slide } from '@/types/slides'
|
||||||
|
|
||||||
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
|
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
|
||||||
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
||||||
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
|
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
|
||||||
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
|
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
|
||||||
|
import BaseChartElement from '@/views/components/element/ChartElement/BaseChartElement.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'screen-element',
|
name: 'screen-element',
|
||||||
@ -43,10 +45,11 @@ export default defineComponent({
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
const currentElementComponent = computed(() => {
|
const currentElementComponent = computed(() => {
|
||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': BaseImageElement,
|
[ElementTypes.IMAGE]: BaseImageElement,
|
||||||
'text': BaseTextElement,
|
[ElementTypes.TEXT]: BaseTextElement,
|
||||||
'shape': BaseShapeElement,
|
[ElementTypes.SHAPE]: BaseShapeElement,
|
||||||
'line': BaseLineElement,
|
[ElementTypes.LINE]: BaseLineElement,
|
||||||
|
[ElementTypes.CHART]: BaseChartElement,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, PropType, defineComponent } from 'vue'
|
import { computed, PropType, defineComponent, inject } from 'vue'
|
||||||
import { Slide } from '@/types/slides'
|
import { Slide } from '@/types/slides'
|
||||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||||
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
|
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
|
||||||
@ -37,10 +37,6 @@ export default defineComponent({
|
|||||||
type: Object as PropType<Slide>,
|
type: Object as PropType<Slide>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
scale: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
animationIndex: {
|
animationIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: -1,
|
default: -1,
|
||||||
@ -50,7 +46,10 @@ export default defineComponent({
|
|||||||
const background = computed(() => props.slide.background)
|
const background = computed(() => props.slide.background)
|
||||||
const { backgroundStyle } = useSlideBackgroundStyle(background)
|
const { backgroundStyle } = useSlideBackgroundStyle(background)
|
||||||
|
|
||||||
|
const scale = inject('scale')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
scale,
|
||||||
backgroundStyle,
|
backgroundStyle,
|
||||||
VIEWPORT_SIZE,
|
VIEWPORT_SIZE,
|
||||||
VIEWPORT_ASPECT_RATIO,
|
VIEWPORT_ASPECT_RATIO,
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ScreenSlide
|
<ScreenSlide
|
||||||
:scale="scale"
|
|
||||||
:slide="slide"
|
:slide="slide"
|
||||||
:animationIndex="animationIndex"
|
:animationIndex="animationIndex"
|
||||||
/>
|
/>
|
||||||
@ -59,7 +58,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 { useStore } from 'vuex'
|
||||||
import throttle from 'lodash/throttle'
|
import throttle from 'lodash/throttle'
|
||||||
import { MutationTypes, State } from '@/store'
|
import { MutationTypes, State } from '@/store'
|
||||||
@ -88,7 +87,9 @@ export default defineComponent({
|
|||||||
|
|
||||||
const slideWidth = ref(0)
|
const slideWidth = ref(0)
|
||||||
const slideHeight = ref(0)
|
const slideHeight = ref(0)
|
||||||
|
|
||||||
const scale = computed(() => slideWidth.value / VIEWPORT_SIZE)
|
const scale = computed(() => slideWidth.value / VIEWPORT_SIZE)
|
||||||
|
provide('scale', scale)
|
||||||
|
|
||||||
const slideThumbnailModelVisible = ref(false)
|
const slideThumbnailModelVisible = ref(false)
|
||||||
|
|
||||||
|
@ -6,18 +6,20 @@
|
|||||||
<component
|
<component
|
||||||
:is="currentElementComponent"
|
:is="currentElementComponent"
|
||||||
:elementInfo="elementInfo"
|
:elementInfo="elementInfo"
|
||||||
|
target="thumbnail"
|
||||||
></component>
|
></component>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, PropType } from 'vue'
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
import { PPTElement } from '@/types/slides'
|
import { ElementTypes, PPTElement } from '@/types/slides'
|
||||||
|
|
||||||
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
|
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
|
||||||
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
||||||
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
|
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
|
||||||
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
|
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
|
||||||
|
import BaseChartElement from '@/views/components/element/ChartElement/BaseChartElement.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'base-element',
|
name: 'base-element',
|
||||||
@ -34,10 +36,11 @@ export default defineComponent({
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
const currentElementComponent = computed(() => {
|
const currentElementComponent = computed(() => {
|
||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': BaseImageElement,
|
[ElementTypes.IMAGE]: BaseImageElement,
|
||||||
'text': BaseTextElement,
|
[ElementTypes.TEXT]: BaseTextElement,
|
||||||
'shape': BaseShapeElement,
|
[ElementTypes.SHAPE]: BaseShapeElement,
|
||||||
'line': BaseLineElement,
|
[ElementTypes.LINE]: BaseLineElement,
|
||||||
|
[ElementTypes.CHART]: BaseChartElement,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
:style="{
|
:style="{
|
||||||
width: VIEWPORT_SIZE + 'px',
|
width: VIEWPORT_SIZE + 'px',
|
||||||
height: VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO + 'px',
|
height: VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO + 'px',
|
||||||
transform: `scale(${size / VIEWPORT_SIZE})`,
|
transform: `scale(${scale})`,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="background" :style="{ ...backgroundStyle }"></div>
|
<div class="background" :style="{ ...backgroundStyle }"></div>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, PropType, defineComponent } from 'vue'
|
import { computed, PropType, defineComponent, provide } from 'vue'
|
||||||
import { Slide } from '@/types/slides'
|
import { Slide } from '@/types/slides'
|
||||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||||
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
|
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
|
||||||
@ -51,7 +51,11 @@ export default defineComponent({
|
|||||||
const background = computed(() => props.slide.background)
|
const background = computed(() => props.slide.background)
|
||||||
const { backgroundStyle } = useSlideBackgroundStyle(background)
|
const { backgroundStyle } = useSlideBackgroundStyle(background)
|
||||||
|
|
||||||
|
const scale = computed(() => props.size / VIEWPORT_SIZE)
|
||||||
|
provide('scale', 1)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
scale,
|
||||||
backgroundStyle,
|
backgroundStyle,
|
||||||
VIEWPORT_SIZE,
|
VIEWPORT_SIZE,
|
||||||
VIEWPORT_ASPECT_RATIO,
|
VIEWPORT_ASPECT_RATIO,
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div class="base-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
|
||||||
|
:type="elementInfo.chartType"
|
||||||
|
:width="elementInfo.width"
|
||||||
|
:height="elementInfo.height"
|
||||||
|
:data="elementInfo.data"
|
||||||
|
:scale="scale"
|
||||||
|
:options="target === 'thumbnail' ? { tooltips: { enabled: false } } : {}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, inject, 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: 'base-element-chart',
|
||||||
|
components: {
|
||||||
|
ElementOutline,
|
||||||
|
Chart,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elementInfo: {
|
||||||
|
type: Object as PropType<PPTChartElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String as PropType<'thumbnail' | 'screen'>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const scale = inject('scale') || 1
|
||||||
|
|
||||||
|
return {
|
||||||
|
scale,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.base-element-chart {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
94
src/views/components/element/ChartElement/index.vue
Normal file
94
src/views/components/element/ChartElement/index.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editable-element-chart"
|
||||||
|
:class="{ 'lock': elementInfo.lock }"
|
||||||
|
:style="{
|
||||||
|
top: elementInfo.top + 'px',
|
||||||
|
left: elementInfo.left + 'px',
|
||||||
|
width: elementInfo.width + 'px',
|
||||||
|
height: elementInfo.height + 'px',
|
||||||
|
}"
|
||||||
|
@mousedown="$event => handleSelectElement($event)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="element-content"
|
||||||
|
v-contextmenu="contextmenus"
|
||||||
|
>
|
||||||
|
<ElementOutline
|
||||||
|
:width="elementInfo.width"
|
||||||
|
:height="elementInfo.height"
|
||||||
|
:outline="elementInfo.outline"
|
||||||
|
/>
|
||||||
|
<Chart
|
||||||
|
:type="elementInfo.chartType"
|
||||||
|
:width="elementInfo.width"
|
||||||
|
:height="elementInfo.height"
|
||||||
|
:data="elementInfo.data"
|
||||||
|
:scale="canvasScale"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import { State } from '@/store'
|
||||||
|
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: {
|
||||||
|
type: Object as PropType<PPTChartElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selectElement: {
|
||||||
|
type: Function as PropType<(e: MouseEvent, element: PPTChartElement, canMove?: boolean) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
contextmenus: {
|
||||||
|
type: Function as PropType<() => ContextmenuItem[]>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const handleSelectElement = (e: MouseEvent) => {
|
||||||
|
if(props.elementInfo.lock) return
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
props.selectElement(e, props.elementInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = useStore<State>()
|
||||||
|
const canvasScale = computed(() => store.state.canvasScale)
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleSelectElement,
|
||||||
|
canvasScale,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.editable-element-chart {
|
||||||
|
position: absolute;
|
||||||
|
cursor: move;
|
||||||
|
|
||||||
|
&.lock .element-content {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,6 +4,7 @@
|
|||||||
top: elementInfo.top + 'px',
|
top: elementInfo.top + 'px',
|
||||||
left: elementInfo.left + 'px',
|
left: elementInfo.left + 'px',
|
||||||
width: elementInfo.width + 'px',
|
width: elementInfo.width + 'px',
|
||||||
|
height: elementInfo.height + 'px',
|
||||||
transform: `rotate(${elementInfo.rotate}deg)`,
|
transform: `rotate(${elementInfo.rotate}deg)`,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
top: elementInfo.top + 'px',
|
top: elementInfo.top + 'px',
|
||||||
left: elementInfo.left + 'px',
|
left: elementInfo.left + 'px',
|
||||||
width: elementInfo.width + 'px',
|
width: elementInfo.width + 'px',
|
||||||
|
height: elementInfo.height + 'px',
|
||||||
transform: `rotate(${elementInfo.rotate}deg)`,
|
transform: `rotate(${elementInfo.rotate}deg)`,
|
||||||
}"
|
}"
|
||||||
@mousedown="$event => handleSelectElement($event)"
|
@mousedown="$event => handleSelectElement($event)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user