mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 支持形状格式刷
This commit is contained in:
parent
697e9a2300
commit
60e4fbd4d0
@ -115,7 +115,8 @@ npm run serve
|
||||
- 阴影
|
||||
- 透明度
|
||||
- 翻转
|
||||
- 编辑文字
|
||||
- 形状格式刷
|
||||
- 编辑文字(支持富文本,与文字元素的富文本编辑功能近似)
|
||||
#### 线条
|
||||
- 颜色
|
||||
- 宽度
|
||||
|
27
src/hooks/useShapeFormatPainter.ts
Normal file
27
src/hooks/useShapeFormatPainter.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useMainStore } from '@/store'
|
||||
import type { PPTShapeElement } from '@/types/slides'
|
||||
|
||||
export default () => {
|
||||
const mainStore = useMainStore()
|
||||
const { shapeFormatPainter, handleElement } = storeToRefs(mainStore)
|
||||
|
||||
const toggleShapeFormatPainter = () => {
|
||||
const _handleElement = handleElement.value as PPTShapeElement
|
||||
|
||||
if (shapeFormatPainter.value) mainStore.setShapeFormatPainter(null)
|
||||
else {
|
||||
mainStore.setShapeFormatPainter({
|
||||
fill: _handleElement.fill,
|
||||
gradient: _handleElement.gradient,
|
||||
outline: _handleElement.outline,
|
||||
opacity: _handleElement.opacity,
|
||||
shadow: _handleElement.shadow,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
toggleShapeFormatPainter,
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ export default () => {
|
||||
const mainStore = useMainStore()
|
||||
const { richTextAttrs, textFormatPainter } = storeToRefs(mainStore)
|
||||
|
||||
const toggleFormatPainter = () => {
|
||||
const toggleTextFormatPainter = () => {
|
||||
if (textFormatPainter.value) mainStore.setTextFormatPainter(null)
|
||||
else {
|
||||
mainStore.setTextFormatPainter({
|
||||
@ -23,6 +23,6 @@ export default () => {
|
||||
}
|
||||
|
||||
return {
|
||||
toggleFormatPainter,
|
||||
toggleTextFormatPainter,
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { customAlphabet } from 'nanoid'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ToolbarStates } from '@/types/toolbar'
|
||||
import type { CreatingElement, TextFormatPainter } from '@/types/edit'
|
||||
import type { CreatingElement, ShapeFormatPainter, TextFormatPainter } from '@/types/edit'
|
||||
import type { DialogForExportTypes } from '@/types/export'
|
||||
import { type TextAttrs, defaultRichTextAttrs } from '@/utils/prosemirror/utils'
|
||||
import { SYS_FONTS } from '@/configs/font'
|
||||
@ -34,6 +34,7 @@ export interface MainState {
|
||||
dialogForExport: DialogForExportTypes
|
||||
databaseId: string
|
||||
textFormatPainter: TextFormatPainter | null
|
||||
shapeFormatPainter: ShapeFormatPainter | null
|
||||
showSelectPanel: boolean
|
||||
showSearchPanel: boolean
|
||||
}
|
||||
@ -67,6 +68,7 @@ export const useMainStore = defineStore('main', {
|
||||
dialogForExport: '', // 导出面板
|
||||
databaseId, // 标识当前应用的indexedDB数据库ID
|
||||
textFormatPainter: null, // 文字格式刷
|
||||
shapeFormatPainter: null, // 形状格式刷
|
||||
showSelectPanel: false, // 打开选择面板
|
||||
showSearchPanel: false, // 打开查找替换面板
|
||||
}),
|
||||
@ -183,6 +185,10 @@ export const useMainStore = defineStore('main', {
|
||||
this.textFormatPainter = textFormatPainter
|
||||
},
|
||||
|
||||
setShapeFormatPainter(shapeFormatPainter: ShapeFormatPainter | null) {
|
||||
this.shapeFormatPainter = shapeFormatPainter
|
||||
},
|
||||
|
||||
setSelectPanelState(show: boolean) {
|
||||
this.showSelectPanel = show
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { ShapePoolItem } from '@/configs/shapes'
|
||||
import type { LinePoolItem } from '@/configs/lines'
|
||||
import type { ImageClipDataRange } from './slides'
|
||||
import type { ImageClipDataRange, PPTElementOutline, PPTElementShadow, ShapeGradient } from './slides'
|
||||
|
||||
export enum ElementOrderCommands {
|
||||
UP = 'up',
|
||||
@ -111,4 +111,12 @@ export interface TextFormatPainter {
|
||||
fontsize?: string
|
||||
fontname?: string
|
||||
align?: 'left' | 'right' | 'center'
|
||||
}
|
||||
|
||||
export interface ShapeFormatPainter {
|
||||
fill?: string
|
||||
gradient?: ShapeGradient
|
||||
outline?: PPTElementOutline
|
||||
opacity?: number
|
||||
shadow?: PPTElementShadow
|
||||
}
|
@ -198,7 +198,7 @@
|
||||
style="flex: 1;"
|
||||
:checked="!!textFormatPainter"
|
||||
v-tooltip="'格式刷'"
|
||||
@click="toggleFormatPainter()"
|
||||
@click="toggleTextFormatPainter()"
|
||||
><IconFormatBrush /></CheckboxButton>
|
||||
</ButtonGroup>
|
||||
|
||||
@ -234,6 +234,15 @@
|
||||
<ElementShadow />
|
||||
<Divider />
|
||||
<ElementOpacity />
|
||||
<Divider />
|
||||
|
||||
<div class="row">
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="!!shapeFormatPainter"
|
||||
@click="toggleShapeFormatPainter()"
|
||||
><IconFormatBrush /> 形状格式刷</CheckboxButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -247,6 +256,7 @@ import { type ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/s
|
||||
import emitter, { EmitterEvents } from '@/utils/emitter'
|
||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||
import useTextFormatPainter from '@/hooks/useTextFormatPainter'
|
||||
import useShapeFormatPainter from '@/hooks/useShapeFormatPainter'
|
||||
|
||||
import ElementOpacity from '../common/ElementOpacity.vue'
|
||||
import ElementOutline from '../common/ElementOutline.vue'
|
||||
@ -269,7 +279,7 @@ import Popover from '@/components/Popover.vue'
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const slidesStore = useSlidesStore()
|
||||
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter } = storeToRefs(mainStore)
|
||||
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter, shapeFormatPainter } = storeToRefs(mainStore)
|
||||
|
||||
const handleShapeElement = handleElement as Ref<PPTShapeElement>
|
||||
|
||||
@ -292,7 +302,8 @@ watch(handleElement, () => {
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
const { addHistorySnapshot } = useHistorySnapshot()
|
||||
const { toggleFormatPainter } = useTextFormatPainter()
|
||||
const { toggleTextFormatPainter } = useTextFormatPainter()
|
||||
const { toggleShapeFormatPainter } = useShapeFormatPainter()
|
||||
|
||||
const updateElement = (props: Partial<PPTShapeElement>) => {
|
||||
slidesStore.updateElement({ id: handleElementId.value, props })
|
||||
|
@ -142,7 +142,7 @@
|
||||
style="flex: 1;"
|
||||
:checked="!!textFormatPainter"
|
||||
v-tooltip="'格式刷'"
|
||||
@click="toggleFormatPainter()"
|
||||
@click="toggleTextFormatPainter()"
|
||||
><IconFormatBrush /></CheckboxButton>
|
||||
<Popover placement="bottom-end" trigger="click" v-model:value="linkPopoverVisible" style="width: 33.33%;">
|
||||
<template #content>
|
||||
@ -424,7 +424,7 @@ const slidesStore = useSlidesStore()
|
||||
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter } = storeToRefs(mainStore)
|
||||
|
||||
const { addHistorySnapshot } = useHistorySnapshot()
|
||||
const { toggleFormatPainter } = useTextFormatPainter()
|
||||
const { toggleTextFormatPainter } = useTextFormatPainter()
|
||||
|
||||
const updateElement = (props: Partial<PPTTextElement>) => {
|
||||
slidesStore.updateElement({ id: handleElementId.value, props })
|
||||
|
@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div
|
||||
class="editable-element-shape"
|
||||
:class="{ 'lock': elementInfo.lock }"
|
||||
:class="{
|
||||
'lock': elementInfo.lock,
|
||||
'format-painter': shapeFormatPainter,
|
||||
}"
|
||||
:style="{
|
||||
top: elementInfo.top + 'px',
|
||||
left: elementInfo.left + 'px',
|
||||
@ -24,6 +27,7 @@
|
||||
}"
|
||||
v-contextmenu="contextmenus"
|
||||
@mousedown="$event => handleSelectElement($event)"
|
||||
@mouseup="execFormatPainter()"
|
||||
@touchstart="$event => handleSelectElement($event)"
|
||||
@dblclick="startEdit()"
|
||||
>
|
||||
@ -99,7 +103,7 @@ const props = defineProps<{
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const slidesStore = useSlidesStore()
|
||||
const { handleElementId } = storeToRefs(mainStore)
|
||||
const { handleElementId, shapeFormatPainter } = storeToRefs(mainStore)
|
||||
|
||||
const { addHistorySnapshot } = useHistorySnapshot()
|
||||
|
||||
@ -110,6 +114,17 @@ const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
|
||||
props.selectElement(e, props.elementInfo, canMove)
|
||||
}
|
||||
|
||||
const execFormatPainter = () => {
|
||||
if (!shapeFormatPainter.value) return
|
||||
slidesStore.updateElement({
|
||||
id: props.elementInfo.id,
|
||||
props: shapeFormatPainter.value,
|
||||
})
|
||||
|
||||
addHistorySnapshot()
|
||||
mainStore.setShapeFormatPainter(null)
|
||||
}
|
||||
|
||||
const outline = computed(() => props.elementInfo.outline)
|
||||
const { outlineWidth, outlineColor, strokeDashArray } = useElementOutline(outline)
|
||||
|
||||
@ -175,6 +190,9 @@ const startEdit = () => {
|
||||
&.lock .element-content {
|
||||
cursor: default;
|
||||
}
|
||||
&.format-painter .element-content {
|
||||
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAVCAYAAACzK0UYAAAABHNCSVQICAgIfAhkiAAAAVRJREFUSInt1DFuwjAYBeCXUrFavgBN9yB6AKR6Bi7AVLrlBpFYgAUpp2i37AysVDIXcCIuwJRMEEYk9LrQDlVQ7EiVOvSt/v1/tmUbeZ7TGMPL5WLgEJLzNE2ptabWmsfjkTeLjTGUUvJ8Pjsjo9GIUkpKKam1voncuTRumn/EKfd1BSQnAF4qhvyK2k1VD88YQ6UUiqJI2+12r2LiPI7j2Xa7rV9yRZbLpRWiAKhGwjW1x3XN828jD9PpVK3X60bAarWy20lZltjv940QwO4KPzbu7oCgLMu/g3Q6ncZI73Q6WSFhGDZGnrIss0LG4zGEEG4ISZUkiW8DDAYDCCEQBIEbAmAWx7GNgSiKAOB1OBzaIyQnSZIom/cRRRG63e7C87z3MAw/fu7Gy/OcRVEgCIK01Wp9/10k37Ism9TdLCHEFzC/zvMPh8Nmt9v5ANDv9/EJD8ykxYswZDkAAAAASUVORK5CYII=) 1 10, default !important;
|
||||
}
|
||||
}
|
||||
.rotate-wrapper {
|
||||
width: 100%;
|
||||
|
Loading…
x
Reference in New Issue
Block a user