优化仪表盘内容

This commit is contained in:
2026-01-23 09:33:28 +08:00
parent 1963ea7244
commit 8283555457
6 changed files with 817 additions and 350 deletions

View File

@@ -0,0 +1,389 @@
<template>
<div :style="{ '--editor-height': editorHeight }">
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig" :disabled="disabled" @blur="onBlur"
@focus="onFocus"></ckeditor>
</div>
</template>
<script setup>
import {
ClassicEditor,
Alignment,
AutoImage,
Autoformat,
BlockQuote,
Bold,
CodeBlock,
DataFilter,
DataSchema,
Essentials,
FindAndReplace,
FontBackgroundColor,
FontColor,
FontFamily,
FontSize,
GeneralHtmlSupport,
Heading,
Highlight,
HorizontalLine,
Image,
ImageCaption,
ImageInsert,
ImageResize,
ImageStyle,
ImageToolbar,
ImageUpload,
Indent,
IndentBlock,
Italic,
Link,
LinkImage,
List,
MediaEmbed,
MediaEmbedToolbar,
Mention,
Paragraph,
PasteFromOffice,
RemoveFormat,
SelectAll,
ShowBlocks,
SourceEditing,
SpecialCharacters,
SpecialCharactersArrows,
SpecialCharactersCurrency,
SpecialCharactersEssentials,
SpecialCharactersLatin,
SpecialCharactersMathematical,
SpecialCharactersText,
Style,
Subscript,
Superscript,
Table,
TableCaption,
TableCellProperties,
TableColumnResize,
TableProperties,
TableToolbar,
TextTransformation,
TodoList,
Underline,
Undo,
WordCount,
} 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 coreTranslations from "ckeditor5/translations/zh-cn.js";
import "ckeditor5/ckeditor5.css";
const { proxy } = useCurrentInstance();
// 组件名称
defineOptions({
name: "scCkeditor"
});
// Props 定义
const props = defineProps({
modelValue: {
type: String,
default: "",
},
placeholder: {
type: String,
default: "请输入内容……",
},
toolbar: {
type: String,
default: "basic",
},
height: {
type: String,
default: "400px",
},
disabled: {
type: Boolean,
default: false,
},
});
// Emits 定义
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",
],
basic: [
"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",
],
};
// 插件配置常量
const PLUGINS = [
Alignment,
AutoImage,
Autoformat,
BlockQuote,
Bold,
CodeBlock,
DataFilter,
DataSchema,
Essentials,
FindAndReplace,
FontBackgroundColor,
FontColor,
FontFamily,
FontSize,
GeneralHtmlSupport,
Heading,
Highlight,
HorizontalLine,
Image,
ImageCaption,
ImageInsert,
ImageResize,
ImageStyle,
ImageToolbar,
ImageUpload,
Indent,
IndentBlock,
Italic,
Link,
LinkImage,
List,
MediaEmbed,
MediaEmbedToolbar,
Mention,
Paragraph,
PasteFromOffice,
RemoveFormat,
SelectAll,
ShowBlocks,
SourceEditing,
SpecialCharacters,
SpecialCharactersArrows,
SpecialCharactersCurrency,
SpecialCharactersEssentials,
SpecialCharactersLatin,
SpecialCharactersMathematical,
SpecialCharactersText,
Style,
Subscript,
Superscript,
Table,
TableCaption,
TableCellProperties,
TableColumnResize,
TableProperties,
TableToolbar,
TextTransformation,
TodoList,
Underline,
Undo,
WordCount,
UploadAdapterPlugin,
];
// 响应式数据
const editorData = ref("");
const editorHeight = ref(props.height);
const editor = ClassicEditor;
// 编辑器配置
const editorConfig = computed(() => ({
language: { ui: "zh-cn", content: "zh-cn" },
translations: [coreTranslations],
plugins: PLUGINS,
toolbar: {
shouldNotGroupWhenFull: true,
items: TOOLBARS[props.toolbar] || TOOLBARS.basic,
},
placeholder: props.placeholder,
image: {
styles: ["alignLeft", "alignCenter", "alignRight"],
toolbar: [
"imageTextAlternative",
"toggleImageCaption",
"|",
"imageStyle:alignLeft",
"imageStyle:alignCenter",
"imageStyle:alignRight",
"|",
"linkImage",
],
},
mediaEmbed: {
previewsInData: true,
providers: [
{
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>')
},
},
],
},
fontSize: {
options: [10, 12, 14, 16, 18, 20, 22, 24, 26, 30, 32, 36],
},
style: {
definitions: [
{
name: "Article category",
element: "h3",
classes: ["category"],
},
{
name: "Info box",
element: "p",
classes: ["info-box"],
},
],
},
upload: {
uploadUrl: proxy?.$API?.common?.upload?.url || "",
withCredentials: false,
extendData: { type: "images" },
headers: {
Authorization: "Bearer " + proxy?.$TOOL?.data?.get("TOKEN"),
},
},
}));
// 监听 modelValue 变化
watch(
() => props.modelValue,
(newVal) => {
editorData.value = newVal ?? "";
},
{ immediate: true }
);
// 监听 height 变化
watch(
() => props.height,
(newVal) => {
editorHeight.value = newVal;
}
);
// 移除图片宽高的正则替换函数
const stripImageDimensions = (html) => {
return html.replace(/<img[^>]*>/gi, (match) => {
return match
.replace(/width="[^"]*"/gi, "")
.replace(/height="[^"]*"/gi, "");
});
};
// 失去焦点事件 - 移除图片的固定宽高,避免响应式布局问题
const onBlur = () => {
const cleanedData = stripImageDimensions(editorData.value);
editorData.value = cleanedData;
emit("update:modelValue", cleanedData);
};
</script>
<style>
:root {
--ck-z-panel: 9999;
}
.ck-content {
height: var(--editor-height);
}
.ck-source-editing-area,
.ck-source-editing-area textarea {
height: var(--editor-height);
}
.ck-source-editing-area textarea {
overflow-y: scroll !important;
}
</style>