diff --git a/src/hooks/useExport.ts b/src/hooks/useExport.ts index dc01a531..2a3eb7ea 100644 --- a/src/hooks/useExport.ts +++ b/src/hooks/useExport.ts @@ -244,6 +244,7 @@ export default () => { pptxSlide.addText(textProps, options) } + else if (el.type === 'image') { const options: pptxgen.ImageProps = { path: el.src, @@ -260,6 +261,7 @@ export default () => { pptxSlide.addImage(options) } + else if (el.type === 'shape') { if (el.special) { const svgRef = document.querySelector(`.thumbnail-list .base-element-${el.id} svg`) as HTMLElement @@ -319,7 +321,27 @@ export default () => { pptxSlide.addShape('custGeom' as pptxgen.ShapeType, options) } + if (el.text) { + const textProps = formatHTML(el.text.content) + + const options: pptxgen.TextPropsOptions = { + x: el.left / 100, + y: el.top / 100, + w: el.width / 100, + h: el.height / 100, + fontSize: 20 * 0.75, + fontFace: '微软雅黑', + color: '#000000', + valign: el.text.align, + } + if (el.rotate) options.rotate = el.rotate + if (el.text.defaultColor) options.color = formatColor(el.text.defaultColor).color + if (el.text.defaultFontName) options.fontFace = el.text.defaultFontName + + pptxSlide.addText(textProps, options) + } } + else if (el.type === 'line') { const path = getLineElementPath(el) const points = formatPoints(toPoints(path)) @@ -341,6 +363,7 @@ export default () => { } pptxSlide.addShape('custGeom' as pptxgen.ShapeType, options) } + else if (el.type === 'chart') { const chartData = [] for (let i = 0; i < el.data.series.length; i++) { @@ -388,6 +411,7 @@ export default () => { pptxSlide.addChart(type, chartData, options) } + else if (el.type === 'table') { const hiddenCells = [] for (let i = 0; i < el.data.length; i++) { diff --git a/src/plugins/iconPark.ts b/src/plugins/iconPark.ts index b8b78886..77fc75fa 100644 --- a/src/plugins/iconPark.ts +++ b/src/plugins/iconPark.ts @@ -81,6 +81,9 @@ import { Erase, Clear, FolderClose, + AlignTextTopOne, + AlignTextBottomOne, + AlignTextMiddleOne, } from '@icon-park/vue-next' export default { @@ -152,6 +155,9 @@ export default { app.component('IconUpOne', UpOne) app.component('IconDownOne', DownOne) app.component('IconFormat', Format) + app.component('IconAlignTextTopOne', AlignTextTopOne) + app.component('IconAlignTextBottomOne', AlignTextBottomOne) + app.component('IconAlignTextMiddleOne', AlignTextMiddleOne) // 箭头与符号 app.component('IconDown', Down) diff --git a/src/store/constants.ts b/src/store/constants.ts index e3f5c102..10cc3f93 100644 --- a/src/store/constants.ts +++ b/src/store/constants.ts @@ -17,6 +17,7 @@ export const enum MutationTypes { SET_RICHTEXT_ATTRS = 'setRichTextAttrs', SET_SELECTED_TABLE_CELLS = 'setSelectedTableCells', SET_SCALING_STATE = 'setScalingState', + SET_EDITING_SHAPE_ELEMENT_ID = 'setEditingShapeElementId', // slides SET_THEME = 'setTheme', diff --git a/src/store/mutations.ts b/src/store/mutations.ts index 604e203b..de6ca451 100644 --- a/src/store/mutations.ts +++ b/src/store/mutations.ts @@ -90,6 +90,10 @@ export const mutations: MutationTree = { state.isScaling = isScaling }, + [MutationTypes.SET_EDITING_SHAPE_ELEMENT_ID](state, ellId: string) { + state.editingShapeElementId = ellId + }, + // slides [MutationTypes.SET_THEME](state, themeProps: Partial) { diff --git a/src/store/state.ts b/src/store/state.ts index bbd0d5c2..6568ab91 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -33,6 +33,7 @@ export interface State { richTextAttrs: TextAttrs; selectedTableCells: string[]; isScaling: boolean; + editingShapeElementId: string; } export const state: State = { @@ -62,4 +63,5 @@ export const state: State = { richTextAttrs: defaultRichTextAttrs, // 富文本状态 selectedTableCells: [], // 选中的表格单元格 isScaling: false, // 正在进行元素缩放 + editingShapeElementId: '', // 当前正处在编辑文字状态的形状ID } \ No newline at end of file diff --git a/src/types/slides.ts b/src/types/slides.ts index e27eb04b..c1fdcbc6 100644 --- a/src/types/slides.ts +++ b/src/types/slides.ts @@ -82,6 +82,12 @@ export interface ShapeGradient { color: [string, string]; rotate: number; } +export interface ShapeText { + content: string; + defaultFontName: string; + defaultColor: string; + align: 'top' | 'middle' | 'bottom'; +} export interface PPTShapeElement extends PPTBaseElement { type: 'shape'; viewBox: number; @@ -96,6 +102,7 @@ export interface PPTShapeElement extends PPTBaseElement { flipV?: boolean; shadow?: PPTElementShadow; special?: boolean; + text?: ShapeText; } export interface PPTLineElement extends Omit { diff --git a/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue b/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue index 5e818ac5..16b6be4a 100644 --- a/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue +++ b/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue @@ -70,6 +70,122 @@ + + + @@ -81,7 +197,9 @@ \ No newline at end of file diff --git a/src/views/components/element/ProsemirrorEditor.vue b/src/views/components/element/ProsemirrorEditor.vue new file mode 100644 index 00000000..b606ebd7 --- /dev/null +++ b/src/views/components/element/ProsemirrorEditor.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/src/views/components/element/ShapeElement/BaseShapeElement.vue b/src/views/components/element/ShapeElement/BaseShapeElement.vue index 4ab4a456..be00350c 100644 --- a/src/views/components/element/ShapeElement/BaseShapeElement.vue +++ b/src/views/components/element/ShapeElement/BaseShapeElement.vue @@ -18,6 +18,8 @@ opacity: elementInfo.opacity, filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '', transform: flipStyle, + color: text.defaultColor, + fontFamily: text.defaultFontName, }" > + +
+
+
@@ -57,7 +63,7 @@