zjk
2026-02-04 b4dc99a92c8fbdfefa48870fde72042e0d1e168a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<script setup lang="ts">
import Quill from 'quill'
import { useTemplateRef } from 'vue'
import 'quill/dist/quill.snow.css'
 
defineOptions({
  name: 'RichTextEditor',
})
 
const { disabled } = defineProps<Props>()
interface Props {
  disabled?: boolean
}
const model = defineModel<string>()
 
let editorInst = null
 
const editorModel = ref<string>()
 
onMounted(() => {
  initEditor()
})
 
const editorRef = useTemplateRef<HTMLElement>('editorRef')
function initEditor() {
  const options = {
    modules: {
      toolbar: [
        { header: [1, 2, 3, 4, 5, 6, false] }, // 标题
        'bold', // 加粗
        'italic', // 斜体
        'strike', // 删除线
        { size: ['small', false, 'large', 'huge'] }, // 字体大小
        { font: [] }, // 字体种类
        { color: [] }, // 字体颜色、
        { background: [] }, // 字体背景颜色
        'link', // 插入链接
        'image', // 插入图片
        'blockquote', // 引用
        'link', // 超链接
        'image', // 插入图片
        'video', // 插入视频
        { list: 'bullet' }, // 无序列表
        { list: 'ordered' }, // 有序列表
        { script: 'sub' }, // 下标
        { script: 'super' }, // 上标
        { align: [] }, // 对齐方式
        'formula', // 公式
        'clean', // remove formatting button
      ],
    },
 
    placeholder: 'Insert text here ...',
    theme: 'snow',
  }
  const quill = new Quill(editorRef.value!, options)
 
  quill.on('text-change', (_delta, _oldDelta, _source) => {
    editorModel.value = quill.getSemanticHTML()
  })
 
  if (disabled)
    quill.enable(false)
 
  editorInst = quill
 
  if (model.value)
    setContents(model.value)
}
 
function setContents(html: string) {
  editorInst!.setContents(editorInst!.clipboard.convert({ html }))
}
 
watch(
  () => model.value,
  (newValue, _oldValue) => {
    if (newValue && newValue !== editorModel.value) {
      setContents(newValue)
    }
    else if (!newValue) {
      setContents('')
    }
  },
)
 
watch(editorModel, (newValue, oldValue) => {
  if (newValue && newValue !== oldValue)
    model.value = newValue
 
  else if (!newValue)
    editorInst!.setContents([])
})
 
watch(
  () => disabled,
  (newValue, _oldValue) => {
    editorInst!.enable(!newValue)
  },
)
 
onBeforeUnmount(() => editorInst = null)
</script>
 
<template>
  <div ref="editorRef" />
</template>