diff --git a/src/hooks/useTextFormatPainter.ts b/src/hooks/useTextFormatPainter.ts
new file mode 100644
index 00000000..48b6d5d8
--- /dev/null
+++ b/src/hooks/useTextFormatPainter.ts
@@ -0,0 +1,28 @@
+import { storeToRefs } from 'pinia'
+import { useMainStore } from '@/store'
+
+export default () => {
+ const mainStore = useMainStore()
+ const { richTextAttrs, textFormatPainter } = storeToRefs(mainStore)
+
+ const toggleFormatPainter = () => {
+ if (textFormatPainter.value) mainStore.setTextFormatPainter(null)
+ else {
+ mainStore.setTextFormatPainter({
+ bold: richTextAttrs.value.bold,
+ em: richTextAttrs.value.em,
+ underline: richTextAttrs.value.underline,
+ strikethrough: richTextAttrs.value.strikethrough,
+ color: richTextAttrs.value.color,
+ backcolor: richTextAttrs.value.backcolor,
+ fontname: richTextAttrs.value.fontsize,
+ fontsize: richTextAttrs.value.fontsize,
+ align: richTextAttrs.value.align,
+ })
+ }
+ }
+
+ return {
+ toggleFormatPainter,
+ }
+}
\ No newline at end of file
diff --git a/src/plugins/icon.ts b/src/plugins/icon.ts
index 45f9b8a4..e2e87e68 100644
--- a/src/plugins/icon.ts
+++ b/src/plugins/icon.ts
@@ -109,6 +109,7 @@ import {
Needle,
TextRotationNone,
TextRotationDown,
+ FormatBrush,
} from '@icon-park/vue-next'
export const icons = {
@@ -219,6 +220,7 @@ export const icons = {
IconNeedle: Needle,
IconTextRotationNone: TextRotationNone,
IconTextRotationDown: TextRotationDown,
+ IconFormatBrush: FormatBrush,
}
export default {
diff --git a/src/store/main.ts b/src/store/main.ts
index 196cfd42..77c053af 100644
--- a/src/store/main.ts
+++ b/src/store/main.ts
@@ -1,6 +1,6 @@
import { customAlphabet } from 'nanoid'
import { defineStore } from 'pinia'
-import { CreatingElement } from '@/types/edit'
+import { CreatingElement, TextFormatPainter } from '@/types/edit'
import { ToolbarStates } from '@/types/toolbar'
import { DialogForExportTypes } from '@/types/export'
import { SYS_FONTS } from '@/configs/font'
@@ -31,6 +31,7 @@ export interface MainState {
selectedSlidesIndex: number[]
dialogForExport: DialogForExportTypes
databaseId: string
+ textFormatPainter: TextFormatPainter | null
}
const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
@@ -59,6 +60,7 @@ export const useMainStore = defineStore('main', {
selectedSlidesIndex: [], // 当前被选中的页面索引集合
dialogForExport: '', // 导出面板
databaseId, // 标识当前应用的indexedDB数据库ID
+ textFormatPainter: null, // 文字格式刷
}),
getters: {
@@ -160,5 +162,9 @@ export const useMainStore = defineStore('main', {
setDialogForExport(type: DialogForExportTypes) {
this.dialogForExport = type
},
+
+ setTextFormatPainter(textFormatPainter: TextFormatPainter | null) {
+ this.textFormatPainter = textFormatPainter
+ },
},
})
\ No newline at end of file
diff --git a/src/types/edit.ts b/src/types/edit.ts
index 91202b6c..464ad2f5 100644
--- a/src/types/edit.ts
+++ b/src/types/edit.ts
@@ -90,4 +90,16 @@ export interface CreatingLineElement {
type: 'line'
data: LinePoolItem
}
-export type CreatingElement = CreatingTextElement | CreatingShapeElement | CreatingLineElement
\ No newline at end of file
+export type CreatingElement = CreatingTextElement | CreatingShapeElement | CreatingLineElement
+
+export interface TextFormatPainter {
+ bold?: boolean
+ em?: boolean
+ underline?: boolean
+ strikethrough?: boolean
+ color?: string
+ backcolor?: string
+ fontsize?: string
+ fontname?: string
+ align?: 'left' | 'right' | 'center'
+}
\ No newline at end of file
diff --git a/src/utils/prosemirror/utils.ts b/src/utils/prosemirror/utils.ts
index 265c0a9c..f47735c7 100644
--- a/src/utils/prosemirror/utils.ts
+++ b/src/utils/prosemirror/utils.ts
@@ -159,16 +159,18 @@ export const getAttrValueInSelection = (view: EditorView, attr: string) => {
return value
}
+type Align = 'left' | 'right' | 'center'
+
interface DefaultAttrs {
color?: string
backcolor?: string
fontsize?: string
fontname?: string
- align?: string
+ align?: Align
}
const _defaultAttrs: DefaultAttrs = {
color: '#000',
- backcolor: '#000',
+ backcolor: '',
fontsize: '20px',
fontname: '微软雅黑',
align: 'left',
@@ -190,7 +192,7 @@ export const getTextAttrs = (view: EditorView, defaultAttrs: DefaultAttrs = {})
const fontsize = getAttrValue(marks, 'fontsize', 'fontsize') || defaultAttrs.fontsize
const fontname = getAttrValue(marks, 'fontname', 'fontname') || defaultAttrs.fontname
const link = getAttrValue(marks, 'link', 'href') || ''
- const align = getAttrValueInSelection(view, 'align') || defaultAttrs.align
+ const align = (getAttrValueInSelection(view, 'align') || defaultAttrs.align) as Align
const isBulletList = isActiveOfParentNodeType('bullet_list', view.state)
const isOrderedList = isActiveOfParentNodeType('ordered_list', view.state)
const isBlockquote = isActiveOfParentNodeType('blockquote', view.state)
@@ -232,7 +234,7 @@ export const defaultRichTextAttrs: TextAttrs = {
subscript: false,
code: false,
color: '#000',
- backcolor: '#000',
+ backcolor: '',
fontsize: '20px',
fontname: '微软雅黑',
link: '',
diff --git a/src/views/Editor/Canvas/index.vue b/src/views/Editor/Canvas/index.vue
index faf5c569..a0380347 100644
--- a/src/views/Editor/Canvas/index.vue
+++ b/src/views/Editor/Canvas/index.vue
@@ -93,7 +93,7 @@
+
+
\ No newline at end of file
diff --git a/src/views/components/element/ProsemirrorEditor.vue b/src/views/components/element/ProsemirrorEditor.vue
index 9bd1c505..057eb077 100644
--- a/src/views/components/element/ProsemirrorEditor.vue
+++ b/src/views/components/element/ProsemirrorEditor.vue
@@ -1,6 +1,7 @@
@@ -14,7 +15,7 @@ import { EditorView } from 'prosemirror-view'
import { toggleMark, wrapIn } from 'prosemirror-commands'
import { initProsemirrorEditor, createDocument } from '@/utils/prosemirror'
import { findNodesWithSameMark, getTextAttrs, autoSelectAll, addMark, markActive, getFontsize } from '@/utils/prosemirror/utils'
-import emitter, { EmitterEvents, RichTextCommand } from '@/utils/emitter'
+import emitter, { EmitterEvents, RichTextAction, RichTextCommand } from '@/utils/emitter'
import { alignmentCommand } from '@/utils/prosemirror/commands/setTextAlign'
import { indentCommand } from '@/utils/prosemirror/commands/setTextIndent'
import { toggleList } from '@/utils/prosemirror/commands/toggleList'
@@ -53,7 +54,7 @@ const emit = defineEmits<{
}>()
const mainStore = useMainStore()
-const { handleElementId } = storeToRefs(mainStore)
+const { handleElementId, textFormatPainter } = storeToRefs(mainStore)
const editorViewRef = ref()
let editorView: EditorView
@@ -104,23 +105,6 @@ watch(() => props.editable, () => {
editorView.setProps({ editable: () => props.editable })
})
-// Prosemirror编辑器的初始化和卸载
-onMounted(() => {
- editorView = initProsemirrorEditor((editorViewRef.value as Element), textContent.value, {
- handleDOMEvents: {
- focus: handleFocus,
- blur: handleBlur,
- keydown: handleKeydown,
- click: handleClick,
- },
- editable: () => props.editable,
- })
- if (props.autoFocus) editorView.focus()
-})
-onUnmounted(() => {
- editorView && editorView.destroy()
-})
-
// 暴露 focus 方法
const focus = () => editorView.focus()
defineExpose({ focus })
@@ -249,6 +233,38 @@ const execCommand = ({ target, action }: RichTextCommand) => {
handleClick()
}
+// 鼠标抬起时,执行格式刷命令
+const handleMouseup = () => {
+ if (!textFormatPainter.value) return
+
+ const actions: RichTextAction[] = [{ command: 'clear' }]
+ for (const key of Object.keys(textFormatPainter.value)) {
+ const command = key
+ const value = textFormatPainter.value[key]
+ if (value) actions.push({ command, value })
+ }
+ execCommand({ action: actions })
+ mainStore.setTextFormatPainter(null)
+}
+
+// Prosemirror编辑器的初始化和卸载
+onMounted(() => {
+ editorView = initProsemirrorEditor((editorViewRef.value as Element), textContent.value, {
+ handleDOMEvents: {
+ focus: handleFocus,
+ blur: handleBlur,
+ keydown: handleKeydown,
+ click: handleClick,
+ mouseup: handleMouseup,
+ },
+ editable: () => props.editable,
+ })
+ if (props.autoFocus) editorView.focus()
+})
+onUnmounted(() => {
+ editorView && editorView.destroy()
+})
+
emitter.on(EmitterEvents.RICH_TEXT_COMMAND, execCommand)
onUnmounted(() => {
emitter.off(EmitterEvents.RICH_TEXT_COMMAND, execCommand)
@@ -258,5 +274,9 @@ onUnmounted(() => {