更新
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div :style="{ '--editor-height': editorHeight }">
|
||||
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig" :disabled="disabled" @blur="onBlur"
|
||||
@focus="onFocus"></ckeditor>
|
||||
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig" :disabled="disabled" @blur="onBlur" @focus="onFocus"></ckeditor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -69,137 +68,119 @@ import {
|
||||
Underline,
|
||||
Undo,
|
||||
WordCount,
|
||||
} from "ckeditor5";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import { UploadAdapterPlugin } from "./UploadAdapter.js";
|
||||
} from 'ckeditor5'
|
||||
import { Ckeditor } from '@ckeditor/ckeditor5-vue'
|
||||
import { UploadAdapterPlugin } from './UploadAdapter.js'
|
||||
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { useCurrentInstance } from "@/utils/tool";
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useCurrentInstance } from '@/utils/tool'
|
||||
|
||||
import coreTranslations from "ckeditor5/translations/zh-cn.js";
|
||||
import "ckeditor5/ckeditor5.css";
|
||||
import coreTranslations from 'ckeditor5/translations/zh-cn.js'
|
||||
import 'ckeditor5/ckeditor5.css'
|
||||
|
||||
const { proxy } = useCurrentInstance();
|
||||
const { proxy } = useCurrentInstance()
|
||||
|
||||
// 组件名称
|
||||
defineOptions({
|
||||
name: "scCkeditor"
|
||||
});
|
||||
name: 'scCkeditor',
|
||||
})
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入内容……",
|
||||
default: '请输入内容……',
|
||||
},
|
||||
toolbar: {
|
||||
type: String,
|
||||
default: "basic",
|
||||
default: 'basic',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: "400px",
|
||||
default: '400px',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
// Emits 定义
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
// 工具栏配置常量
|
||||
const TOOLBARS = {
|
||||
full: [
|
||||
"sourceEditing",
|
||||
"undo",
|
||||
"redo",
|
||||
"heading",
|
||||
"style",
|
||||
"|",
|
||||
"superscript",
|
||||
"subscript",
|
||||
"removeFormat",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"link",
|
||||
"fontBackgroundColor",
|
||||
"fontFamily",
|
||||
"fontSize",
|
||||
"fontColor",
|
||||
"|",
|
||||
"outdent",
|
||||
"indent",
|
||||
"alignment",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"todoList",
|
||||
"|",
|
||||
"blockQuote",
|
||||
"insertTable",
|
||||
"imageInsert",
|
||||
"mediaEmbed",
|
||||
"highlight",
|
||||
"horizontalLine",
|
||||
"selectAll",
|
||||
"showBlocks",
|
||||
"specialCharacters",
|
||||
"codeBlock",
|
||||
"findAndReplace",
|
||||
'sourceEditing',
|
||||
'undo',
|
||||
'redo',
|
||||
'heading',
|
||||
'style',
|
||||
'|',
|
||||
'superscript',
|
||||
'subscript',
|
||||
'removeFormat',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'link',
|
||||
'fontBackgroundColor',
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontColor',
|
||||
'|',
|
||||
'outdent',
|
||||
'indent',
|
||||
'alignment',
|
||||
'bulletedList',
|
||||
'numberedList',
|
||||
'todoList',
|
||||
'|',
|
||||
'blockQuote',
|
||||
'insertTable',
|
||||
'imageInsert',
|
||||
'mediaEmbed',
|
||||
'highlight',
|
||||
'horizontalLine',
|
||||
'selectAll',
|
||||
'showBlocks',
|
||||
'specialCharacters',
|
||||
'codeBlock',
|
||||
'findAndReplace',
|
||||
],
|
||||
basic: [
|
||||
"sourceEditing",
|
||||
"undo",
|
||||
"redo",
|
||||
"heading",
|
||||
"|",
|
||||
"removeFormat",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"link",
|
||||
"fontBackgroundColor",
|
||||
"fontFamily",
|
||||
"fontSize",
|
||||
"fontColor",
|
||||
"|",
|
||||
"outdent",
|
||||
"indent",
|
||||
"alignment",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"todoList",
|
||||
"|",
|
||||
"insertTable",
|
||||
"imageInsert",
|
||||
"mediaEmbed",
|
||||
'sourceEditing',
|
||||
'undo',
|
||||
'redo',
|
||||
'heading',
|
||||
'|',
|
||||
'removeFormat',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'link',
|
||||
'fontBackgroundColor',
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontColor',
|
||||
'|',
|
||||
'outdent',
|
||||
'indent',
|
||||
'alignment',
|
||||
'bulletedList',
|
||||
'numberedList',
|
||||
'todoList',
|
||||
'|',
|
||||
'insertTable',
|
||||
'imageInsert',
|
||||
'mediaEmbed',
|
||||
],
|
||||
simple: [
|
||||
"undo",
|
||||
"redo",
|
||||
"heading",
|
||||
"|",
|
||||
"removeFormat",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"link",
|
||||
"fontBackgroundColor",
|
||||
"fontFamily",
|
||||
"fontSize",
|
||||
"fontColor",
|
||||
"|",
|
||||
"insertTable",
|
||||
"imageInsert",
|
||||
"mediaEmbed",
|
||||
],
|
||||
};
|
||||
simple: ['undo', 'redo', 'heading', '|', 'removeFormat', 'bold', 'italic', 'underline', 'link', 'fontBackgroundColor', 'fontFamily', 'fontSize', 'fontColor', '|', 'insertTable', 'imageInsert', 'mediaEmbed'],
|
||||
}
|
||||
|
||||
// 插件配置常量
|
||||
const PLUGINS = [
|
||||
@@ -265,16 +246,16 @@ const PLUGINS = [
|
||||
Undo,
|
||||
WordCount,
|
||||
UploadAdapterPlugin,
|
||||
];
|
||||
]
|
||||
|
||||
// 响应式数据
|
||||
const editorData = ref("");
|
||||
const editorHeight = ref(props.height);
|
||||
const editor = ClassicEditor;
|
||||
const editorData = ref('')
|
||||
const editorHeight = ref(props.height)
|
||||
const editor = ClassicEditor
|
||||
|
||||
// 编辑器配置
|
||||
const editorConfig = computed(() => ({
|
||||
language: { ui: "zh-cn", content: "zh-cn" },
|
||||
language: { ui: 'zh-cn', content: 'zh-cn' },
|
||||
translations: [coreTranslations],
|
||||
plugins: PLUGINS,
|
||||
toolbar: {
|
||||
@@ -283,27 +264,18 @@ const editorConfig = computed(() => ({
|
||||
},
|
||||
placeholder: props.placeholder,
|
||||
image: {
|
||||
styles: ["alignLeft", "alignCenter", "alignRight"],
|
||||
toolbar: [
|
||||
"imageTextAlternative",
|
||||
"toggleImageCaption",
|
||||
"|",
|
||||
"imageStyle:alignLeft",
|
||||
"imageStyle:alignCenter",
|
||||
"imageStyle:alignRight",
|
||||
"|",
|
||||
"linkImage",
|
||||
],
|
||||
styles: ['alignLeft', 'alignCenter', 'alignRight'],
|
||||
toolbar: ['imageTextAlternative', 'toggleImageCaption', '|', 'imageStyle:alignLeft', 'imageStyle:alignCenter', 'imageStyle:alignRight', '|', 'linkImage'],
|
||||
},
|
||||
mediaEmbed: {
|
||||
previewsInData: true,
|
||||
providers: [
|
||||
{
|
||||
name: "mp4",
|
||||
name: 'mp4',
|
||||
url: /\.(mp4|avi|mov|flv|wmv|mkv)$/i,
|
||||
html: match => {
|
||||
const url = match["input"];
|
||||
return ('<video controls width="100%" height="100%" src="' + url + '"></video>')
|
||||
html: (match) => {
|
||||
const url = match['input']
|
||||
return '<video controls width="100%" height="100%" src="' + url + '"></video>'
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -314,59 +286,57 @@ const editorConfig = computed(() => ({
|
||||
style: {
|
||||
definitions: [
|
||||
{
|
||||
name: "Article category",
|
||||
element: "h3",
|
||||
classes: ["category"],
|
||||
name: 'Article category',
|
||||
element: 'h3',
|
||||
classes: ['category'],
|
||||
},
|
||||
{
|
||||
name: "Info box",
|
||||
element: "p",
|
||||
classes: ["info-box"],
|
||||
name: 'Info box',
|
||||
element: 'p',
|
||||
classes: ['info-box'],
|
||||
},
|
||||
],
|
||||
},
|
||||
upload: {
|
||||
uploadUrl: proxy?.$API?.common?.upload?.url || "",
|
||||
uploadUrl: proxy?.$API?.common?.upload?.url || '',
|
||||
withCredentials: false,
|
||||
extendData: { type: "images" },
|
||||
extendData: { type: 'images' },
|
||||
headers: {
|
||||
Authorization: "Bearer " + proxy?.$TOOL?.data?.get("TOKEN"),
|
||||
Authorization: 'Bearer ' + proxy?.$TOOL?.data?.get('TOKEN'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
}))
|
||||
|
||||
// 监听 modelValue 变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
editorData.value = newVal ?? "";
|
||||
editorData.value = newVal ?? ''
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 监听 height 变化
|
||||
watch(
|
||||
() => props.height,
|
||||
(newVal) => {
|
||||
editorHeight.value = newVal;
|
||||
}
|
||||
);
|
||||
editorHeight.value = newVal
|
||||
},
|
||||
)
|
||||
|
||||
// 移除图片宽高的正则替换函数
|
||||
const stripImageDimensions = (html) => {
|
||||
return html.replace(/<img[^>]*>/gi, (match) => {
|
||||
return match
|
||||
.replace(/width="[^"]*"/gi, "")
|
||||
.replace(/height="[^"]*"/gi, "");
|
||||
});
|
||||
};
|
||||
return match.replace(/width="[^"]*"/gi, '').replace(/height="[^"]*"/gi, '')
|
||||
})
|
||||
}
|
||||
|
||||
// 失去焦点事件 - 移除图片的固定宽高,避免响应式布局问题
|
||||
const onBlur = () => {
|
||||
const cleanedData = stripImageDimensions(editorData.value);
|
||||
editorData.value = cleanedData;
|
||||
emit("update:modelValue", cleanedData);
|
||||
};
|
||||
const cleanedData = stripImageDimensions(editorData.value)
|
||||
editorData.value = cleanedData
|
||||
emit('update:modelValue', cleanedData)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user