build(deps): 新增 CKEditor 与 TinyMCE 依赖
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
|
||||
import { onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import type { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
|
||||
import type { CampusRecord, DepartmentRecord } from '@/api/management/types'
|
||||
|
||||
import EditorCkPane from './editors/EditorCkPane.vue'
|
||||
import EditorTinyPane from './editors/EditorTinyPane.vue'
|
||||
import EditorWangPane from './editors/EditorWangPane.vue'
|
||||
import type { DocumentTemplate, TemplateEditorForm } from '../types'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -24,21 +23,45 @@ const emit = defineEmits<{
|
||||
|
||||
// Word 自维护方案:科室把文书从 Word/WPS 直接粘贴进来(保留表格与基本格式),
|
||||
// 或上传 .docx 由 mammoth 转换;签署时由交互管线识别"勾选"表并转为签署域。
|
||||
const editorRef = shallowRef<IDomEditor>()
|
||||
// 富文本引擎做成可切换的三套(对比 Word 粘贴保真度),存储统一为编辑器产出的 HTML。
|
||||
type EditorEngine = 'wangeditor' | 'ckeditor5' | 'tinymce'
|
||||
|
||||
interface EditorPaneHandle {
|
||||
getText: () => string
|
||||
insertText: (text: string) => void
|
||||
}
|
||||
|
||||
const engineComponents = {
|
||||
wangeditor: EditorWangPane,
|
||||
ckeditor5: EditorCkPane,
|
||||
tinymce: EditorTinyPane,
|
||||
}
|
||||
|
||||
const engineOptions: Array<{ key: EditorEngine; label: string; note: string }> = [
|
||||
{
|
||||
key: 'wangeditor',
|
||||
label: 'WangEditor',
|
||||
note: '已接管粘贴,尽量保留内联样式(MIT)',
|
||||
},
|
||||
{
|
||||
key: 'ckeditor5',
|
||||
label: 'CKEditor 5',
|
||||
note: 'PasteFromOffice 插件,Word 粘贴保真最强(GPL2)',
|
||||
},
|
||||
{
|
||||
key: 'tinymce',
|
||||
label: 'TinyMCE',
|
||||
note: '均衡之选(MIT)',
|
||||
},
|
||||
]
|
||||
|
||||
const engine = ref<EditorEngine>('wangeditor')
|
||||
const engineNote = ref(engineOptions[0]?.note ?? '')
|
||||
const valueHtml = ref('<p><br></p>')
|
||||
const editorMode = 'default'
|
||||
const docxInput = ref<HTMLInputElement | null>(null)
|
||||
const importing = ref(false)
|
||||
const importMessage = ref('')
|
||||
|
||||
const toolbarConfig: Partial<IToolbarConfig> = {
|
||||
excludeKeys: ['group-video', 'fullScreen'],
|
||||
}
|
||||
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
placeholder:
|
||||
'将 Word / WPS 中的文书内容直接粘贴到此处(保留表格与基本格式),或点击上方「导入 .docx」自动转换。',
|
||||
}
|
||||
const paneRef = ref<EditorPaneHandle | null>(null)
|
||||
|
||||
// 占位符在签署时由系统按患者与就诊信息自动填充;签署交互管线同时会识别"勾选"表格。
|
||||
const placeholders = [
|
||||
@@ -102,13 +125,13 @@ function handleCampusChange() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleCreated(editor: IDomEditor) {
|
||||
editorRef.value = editor
|
||||
function selectEngine(next: EditorEngine) {
|
||||
engine.value = next
|
||||
engineNote.value = engineOptions.find((option) => option.key === next)?.note ?? ''
|
||||
}
|
||||
|
||||
function insertPlaceholder(text: string) {
|
||||
// slate 文本节点:{ text };由编辑器在当前光标处插入。
|
||||
editorRef.value?.insertNode({ text })
|
||||
paneRef.value?.insertText(text)
|
||||
}
|
||||
|
||||
async function handleDocxChange(event: Event) {
|
||||
@@ -139,7 +162,7 @@ async function handleDocxChange(event: Event) {
|
||||
}
|
||||
|
||||
function save() {
|
||||
const text = editorRef.value?.getText().trim()
|
||||
const text = paneRef.value?.getText() ?? ''
|
||||
|
||||
if (!form.name.trim()) {
|
||||
importMessage.value = '请填写文书名称'
|
||||
@@ -154,10 +177,6 @@ function save() {
|
||||
form.contentHtml = valueHtml.value
|
||||
emit('save', { ...form, contentHtml: valueHtml.value })
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
editorRef.value?.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -242,6 +261,20 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
|
||||
<div class="editor-shell">
|
||||
<div class="engine-bar">
|
||||
<span class="engine-label">编辑器引擎</span>
|
||||
<button
|
||||
v-for="option in engineOptions"
|
||||
:key="option.key"
|
||||
type="button"
|
||||
class="engine-button"
|
||||
:class="{ active: engine === option.key }"
|
||||
@click="selectEngine(option.key)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</button>
|
||||
<span class="engine-note">{{ engineNote }}</span>
|
||||
</div>
|
||||
<div class="docx-bar">
|
||||
<button
|
||||
type="button"
|
||||
@@ -265,18 +298,12 @@ onBeforeUnmount(() => {
|
||||
</span>
|
||||
</div>
|
||||
<div class="editor-area">
|
||||
<Toolbar
|
||||
class="editor-toolbar"
|
||||
:editor="editorRef"
|
||||
:default-config="toolbarConfig"
|
||||
:mode="editorMode"
|
||||
/>
|
||||
<Editor
|
||||
v-model="valueHtml"
|
||||
class="editor-content"
|
||||
:default-config="editorConfig"
|
||||
:mode="editorMode"
|
||||
@on-created="handleCreated"
|
||||
<component
|
||||
:is="engineComponents[engine]"
|
||||
:key="engine"
|
||||
ref="paneRef"
|
||||
:model-value="valueHtml"
|
||||
@update:model-value="valueHtml = $event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -478,24 +505,52 @@ onBeforeUnmount(() => {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.engine-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.engine-label {
|
||||
color: var(--ink);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.engine-button {
|
||||
padding: 5px 12px;
|
||||
color: var(--mut);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
background: #fff;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.engine-button:hover {
|
||||
border-color: var(--brand);
|
||||
color: var(--brand);
|
||||
}
|
||||
|
||||
.engine-button.active {
|
||||
color: #fff;
|
||||
background: var(--brand);
|
||||
border-color: var(--brand);
|
||||
}
|
||||
|
||||
.engine-note {
|
||||
color: var(--mut);
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
.editor-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 380px;
|
||||
min-height: 420px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.editor-toolbar {
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.editor-content {
|
||||
flex: 1;
|
||||
min-height: 340px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
|
||||
Reference in New Issue
Block a user