mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
update
This commit is contained in:
parent
5bf32419bb
commit
942488f47c
@ -19,4 +19,13 @@
|
||||
}
|
||||
.ant-radio-button-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
.ant-slider-track {
|
||||
background-color: $themeColor;
|
||||
}
|
||||
.ant-slider-handle {
|
||||
border-color: $themeColor;
|
||||
}
|
||||
.ant-select {
|
||||
user-select: none;
|
||||
}
|
@ -44,7 +44,7 @@ export const setTextAlign = (tr: Transaction, schema: Schema, alignment: string)
|
||||
let { attrs } = node
|
||||
if(alignment) attrs = { ...attrs, align: alignment }
|
||||
else attrs = { ...attrs, align: null }
|
||||
tr = tr.setNodeMarkup(pos, nodeType, attrs, node.marks);
|
||||
tr = tr.setNodeMarkup(pos, nodeType, attrs, node.marks)
|
||||
})
|
||||
|
||||
return tr
|
||||
|
@ -2,6 +2,7 @@ import mitt, { Emitter } from 'mitt'
|
||||
|
||||
export enum EmitterEvents {
|
||||
UPDATE_TEXT_STATE = 'UPDATE_TEXT_STATE',
|
||||
EXEC_TEXT_COMMAND = 'EXEC_TEXT_COMMAND',
|
||||
}
|
||||
|
||||
const emitter: Emitter = mitt()
|
||||
|
@ -4,6 +4,7 @@
|
||||
<Select
|
||||
style="flex: 3;"
|
||||
:value="richTextAttrs.fontname"
|
||||
@change="value => emitRichTextCommand('fontname', value)"
|
||||
>
|
||||
<SelectOption v-for="font in availableFonts" :key="font.en" :value="font.en">
|
||||
<span :style="{ fontFamily: font.en }">{{font.zh}}</span>
|
||||
@ -12,6 +13,7 @@
|
||||
<Select
|
||||
style="flex: 2;"
|
||||
:value="richTextAttrs.fontsize"
|
||||
@change="value => emitRichTextCommand('fontsize', value)"
|
||||
>
|
||||
<SelectOption v-for="fontsize in fontSizeOptions" :key="fontsize" :value="fontsize">
|
||||
{{fontsize}}
|
||||
@ -22,7 +24,10 @@
|
||||
<ButtonGroup class="row">
|
||||
<Popover trigger="click">
|
||||
<template #content>
|
||||
<ColorPicker v-model="richTextAttrs.color" />
|
||||
<ColorPicker
|
||||
:modelValue="richTextAttrs.color"
|
||||
@update:modelValue="value => emitRichTextCommand('color', value)"
|
||||
/>
|
||||
</template>
|
||||
<Button class="text-color-btn" style="flex: 1;">
|
||||
<FontColorsOutlined />
|
||||
@ -31,7 +36,10 @@
|
||||
</Popover>
|
||||
<Popover trigger="click">
|
||||
<template #content>
|
||||
<ColorPicker v-model="richTextAttrs.backcolor" />
|
||||
<ColorPicker
|
||||
:modelValue="richTextAttrs.backcolor"
|
||||
@update:modelValue="value => emitRichTextCommand('backcolor', value)"
|
||||
/>
|
||||
</template>
|
||||
<Button class="text-color-btn" style="flex: 1;">
|
||||
<HighlightOutlined />
|
||||
@ -40,7 +48,10 @@
|
||||
</Popover>
|
||||
<Popover trigger="click">
|
||||
<template #content>
|
||||
<ColorPicker v-model="fill" />
|
||||
<ColorPicker
|
||||
:modelValue="fill"
|
||||
@update:modelValue="value => updateFill(value)"
|
||||
/>
|
||||
</template>
|
||||
<Button class="text-color-btn" style="flex: 1;">
|
||||
<BgColorsOutlined />
|
||||
@ -50,31 +61,79 @@
|
||||
</ButtonGroup>
|
||||
|
||||
<CheckboxButtonGroup class="row">
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.bold"><BoldOutlined /></CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.em"><ItalicOutlined /></CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.underline"><UnderlineOutlined /></CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.strikethrough"><StrikethroughOutlined /></CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.bold"
|
||||
@click="emitRichTextCommand('bold')"
|
||||
><BoldOutlined /></CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.em"
|
||||
@click="emitRichTextCommand('em')"
|
||||
><ItalicOutlined /></CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.underline"
|
||||
@click="emitRichTextCommand('underline')"
|
||||
><UnderlineOutlined /></CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.strikethrough"
|
||||
@click="emitRichTextCommand('strikethrough')"
|
||||
><StrikethroughOutlined /></CheckboxButton>
|
||||
</CheckboxButtonGroup>
|
||||
|
||||
<CheckboxButtonGroup class="row">
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.superscript">上</CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.subscript">下</CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.code">码</CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.blockquote">引</CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;">清</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.superscript"
|
||||
@click="emitRichTextCommand('superscript')"
|
||||
>上</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.subscript"
|
||||
@click="emitRichTextCommand('subscript')"
|
||||
>下</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.code"
|
||||
@click="emitRichTextCommand('code')"
|
||||
>码</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.blockquote"
|
||||
@click="emitRichTextCommand('blockquote')"
|
||||
>引</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
@click="emitRichTextCommand('clear')"
|
||||
>清</CheckboxButton>
|
||||
</CheckboxButtonGroup>
|
||||
|
||||
<Divider />
|
||||
|
||||
<RadioGroup class="row" button-style="solid" :value="richTextAttrs.align">
|
||||
<RadioGroup
|
||||
class="row"
|
||||
button-style="solid"
|
||||
:value="richTextAttrs.align"
|
||||
@change="e => emitRichTextCommand('align', e.target.value)"
|
||||
>
|
||||
<RadioButton value="left" style="flex: 1;"><AlignLeftOutlined /></RadioButton>
|
||||
<RadioButton value="center" style="flex: 1;"><AlignCenterOutlined /></RadioButton>
|
||||
<RadioButton value="right" style="flex: 1;"><AlignRightOutlined /></RadioButton>
|
||||
</RadioGroup>
|
||||
|
||||
<CheckboxButtonGroup class="row">
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.bulletList"><UnorderedListOutlined /></CheckboxButton>
|
||||
<CheckboxButton style="flex: 1;" :checked="richTextAttrs.orderedList"><OrderedListOutlined /></CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.bulletList"
|
||||
@click="emitRichTextCommand('bulletList')"
|
||||
><UnorderedListOutlined /></CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="richTextAttrs.orderedList"
|
||||
@click="emitRichTextCommand('orderedList')"
|
||||
><OrderedListOutlined /></CheckboxButton>
|
||||
</CheckboxButtonGroup>
|
||||
|
||||
<Divider />
|
||||
@ -95,15 +154,10 @@
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<ElementOutline />
|
||||
|
||||
<Divider />
|
||||
|
||||
<ElementShadow />
|
||||
|
||||
<Divider />
|
||||
|
||||
<ElementOpacity />
|
||||
</div>
|
||||
</template>
|
||||
@ -222,6 +276,10 @@ export default defineComponent({
|
||||
emitter.off(EmitterEvents.UPDATE_TEXT_STATE, attr => updateRichTextAttrs(attr))
|
||||
})
|
||||
|
||||
const emitRichTextCommand = (command: string, value?: string) => {
|
||||
emitter.emit(EmitterEvents.EXEC_TEXT_COMMAND, { command, value })
|
||||
}
|
||||
|
||||
const { addHistorySnapshot } = useHistorySnapshot()
|
||||
|
||||
const updateLineHeight = (value: number) => {
|
||||
@ -236,6 +294,12 @@ export default defineComponent({
|
||||
addHistorySnapshot()
|
||||
}
|
||||
|
||||
const updateFill = (value: string) => {
|
||||
const props = { fill: value }
|
||||
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||
addHistorySnapshot()
|
||||
}
|
||||
|
||||
return {
|
||||
fill,
|
||||
lineHeight,
|
||||
@ -247,12 +311,17 @@ export default defineComponent({
|
||||
wordSpaceOptions,
|
||||
updateLineHeight,
|
||||
updateWordSpace,
|
||||
updateFill,
|
||||
emitRichTextCommand,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.text-style-panel {
|
||||
user-select: none;
|
||||
}
|
||||
.row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -66,6 +66,8 @@ export default defineComponent({
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
font-family: '微软雅黑';
|
||||
|
||||
.text {
|
||||
position: relative;
|
||||
|
@ -51,6 +51,14 @@ import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||
|
||||
import ElementOutline from '@/views/components/element/ElementOutline.vue'
|
||||
import { toggleMark, wrapIn } from 'prosemirror-commands'
|
||||
import { alignmentCommand } from '@/prosemirror/commands/setTextAlign'
|
||||
import { toggleList } from '@/prosemirror/commands/toggleList'
|
||||
|
||||
interface CommandPayload {
|
||||
command: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editable-element-text',
|
||||
@ -81,7 +89,7 @@ export default defineComponent({
|
||||
id: props.elementInfo.id,
|
||||
props: { height: realHeight },
|
||||
})
|
||||
}, 500, { trailing: true })
|
||||
}, 300, { trailing: true })
|
||||
|
||||
const updateTextElementHeight = () => {
|
||||
if(!elementRef.value) return
|
||||
@ -115,12 +123,12 @@ export default defineComponent({
|
||||
props: { content: editorView.dom.innerHTML },
|
||||
})
|
||||
addHistorySnapshot()
|
||||
}, 500, { trailing: true })
|
||||
}, 300, { trailing: true })
|
||||
|
||||
const handleClick = debounce(function() {
|
||||
const attr = getTextAttrs(editorView)
|
||||
emitter.emit(EmitterEvents.UPDATE_TEXT_STATE, attr)
|
||||
}, 50, { trailing: true })
|
||||
}, 30, { trailing: true })
|
||||
|
||||
const handleKeydown = () => {
|
||||
handleInput()
|
||||
@ -164,6 +172,81 @@ export default defineComponent({
|
||||
const shadow = computed(() => props.elementInfo.shadow)
|
||||
const { shadowStyle } = useElementShadow(shadow)
|
||||
|
||||
const handleElementId = computed(() => store.state.handleElementId)
|
||||
|
||||
const execCommand = (payload: CommandPayload) => {
|
||||
if(handleElementId.value !== props.elementInfo.id) return
|
||||
|
||||
if(payload.command === 'fontname' && payload.value) {
|
||||
const mark = editorView.state.schema.marks.fontname.create({ fontname: payload.value })
|
||||
const { $from, $to } = editorView.state.selection
|
||||
editorView.dispatch(editorView.state.tr.addMark($from.pos, $to.pos, mark))
|
||||
}
|
||||
else if(payload.command === 'fontsize' && payload.value) {
|
||||
const mark = editorView.state.schema.marks.fontsize.create({ fontsize: payload.value })
|
||||
const { $from, $to } = editorView.state.selection
|
||||
editorView.dispatch(editorView.state.tr.addMark($from.pos, $to.pos, mark))
|
||||
}
|
||||
else if(payload.command === 'color' && payload.value) {
|
||||
const mark = editorView.state.schema.marks.forecolor.create({ color: payload.value })
|
||||
const { $from, $to } = editorView.state.selection
|
||||
editorView.dispatch(editorView.state.tr.addMark($from.pos, $to.pos, mark))
|
||||
}
|
||||
else if(payload.command === 'backcolor' && payload.value) {
|
||||
const mark = editorView.state.schema.marks.backcolor.create({ backcolor: payload.value })
|
||||
const { $from, $to } = editorView.state.selection
|
||||
editorView.dispatch(editorView.state.tr.addMark($from.pos, $to.pos, mark))
|
||||
}
|
||||
else if(payload.command === 'bold') {
|
||||
toggleMark(editorView.state.schema.marks.strong)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'em') {
|
||||
toggleMark(editorView.state.schema.marks.em)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'underline') {
|
||||
toggleMark(editorView.state.schema.marks.underline)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'strikethrough') {
|
||||
toggleMark(editorView.state.schema.marks.strikethrough)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'subscript') {
|
||||
toggleMark(editorView.state.schema.marks.subscript)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'superscript') {
|
||||
toggleMark(editorView.state.schema.marks.superscript)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'blockquote') {
|
||||
wrapIn(editorView.state.schema.nodes.blockquote)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'code') {
|
||||
toggleMark(editorView.state.schema.marks.code)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'align' && payload.value) {
|
||||
alignmentCommand(editorView, payload.value)
|
||||
}
|
||||
else if(payload.command === 'bulletList') {
|
||||
const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes
|
||||
toggleList(bulletList, listItem)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'orderedList') {
|
||||
const { ordered_list: orderedList, list_item: listItem } = editorView.state.schema.nodes
|
||||
toggleList(orderedList, listItem)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if(payload.command === 'clear') {
|
||||
if(editorView.state.selection.empty) return false
|
||||
const { $from, $to } = editorView.state.selection
|
||||
editorView.dispatch(editorView.state.tr.removeMark($from.pos, $to.pos))
|
||||
}
|
||||
editorView.focus()
|
||||
handleInput()
|
||||
handleClick()
|
||||
}
|
||||
|
||||
emitter.on(EmitterEvents.EXEC_TEXT_COMMAND, payload => execCommand(payload))
|
||||
onUnmounted(() => {
|
||||
emitter.off(EmitterEvents.EXEC_TEXT_COMMAND, payload => execCommand(payload))
|
||||
})
|
||||
|
||||
return {
|
||||
elementRef,
|
||||
editorViewRef,
|
||||
@ -188,6 +271,8 @@ export default defineComponent({
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
font-family: '微软雅黑';
|
||||
|
||||
.text {
|
||||
position: relative;
|
||||
|
Loading…
x
Reference in New Issue
Block a user