From 2643443d1609c7da11fb5785af6ef71fcc5d5020 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期五, 23 一月 2026 14:52:20 +0800
Subject: [PATCH] 富文本编辑器引入
---
src/components/Editor/index.vue | 545 ++++++++++++++++++++++++++++++-----------------------
1 files changed, 306 insertions(+), 239 deletions(-)
diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue
index 952c1d8..499b11a 100644
--- a/src/components/Editor/index.vue
+++ b/src/components/Editor/index.vue
@@ -1,261 +1,328 @@
<template>
- <view class="editor-container">
- <div class="editor">
- <!-- <QuillEditor v-model:content="content"
- contentType="html"
- @textChange="(e) => emit('update:modelValue', content)"
- :options="options"
- :style="styles" /> -->
- </div>
+ <view class="container">
+ <view class="editor-wrapper"
+ :style="{ height: height + 'px' }">
+ <view class='toolbar'
+ @tap="format"
+ style="height: 120px;overflow-y: auto;">
+ <view :class="formats.bold ? 'ql-active' : ''"
+ class="iconfont icon-zitijiacu"
+ data-name="bold">
+ </view>
+ <view :class="formats.italic ? 'ql-active' : ''"
+ class="iconfont icon-zitixieti"
+ data-name="italic">
+ </view>
+ <view :class="formats.underline ? 'ql-active' : ''"
+ class="iconfont icon-zitixiahuaxian"
+ data-name="underline"></view>
+ <view :class="formats.strike ? 'ql-active' : ''"
+ class="iconfont icon-zitishanchuxian"
+ data-name="strike"></view>
+ <!-- #ifndef MP-BAIDU -->
+ <view :class="formats.align === 'left' ? 'ql-active' : ''"
+ class="iconfont icon-zuoduiqi"
+ data-name="align"
+ data-value="left"></view>
+ <!-- #endif -->
+ <view :class="formats.align === 'center' ? 'ql-active' : ''"
+ class="iconfont icon-juzhongduiqi"
+ data-name="align"
+ data-value="center"></view>
+ <view :class="formats.align === 'right' ? 'ql-active' : ''"
+ class="iconfont icon-youduiqi"
+ data-name="align"
+ data-value="right"></view>
+ <view :class="formats.align === 'justify' ? 'ql-active' : ''"
+ class="iconfont icon-zuoyouduiqi"
+ data-name="align"
+ data-value="justify"></view>
+ <!-- #ifndef MP-BAIDU -->
+ <view :class="formats.lineHeight ? 'ql-active' : ''"
+ class="iconfont icon-line-height"
+ data-name="lineHeight"
+ data-value="2"></view>
+ <view :class="formats.letterSpacing ? 'ql-active' : ''"
+ class="iconfont icon-Character-Spacing"
+ data-name="letterSpacing"
+ data-value="2em"></view>
+ <view :class="formats.marginTop ? 'ql-active' : ''"
+ class="iconfont icon-722bianjiqi_duanqianju"
+ data-name="marginTop"
+ data-value="20px"></view>
+ <view :class="formats.marginBottom ? 'ql-active' : ''"
+ class="iconfont icon-723bianjiqi_duanhouju"
+ data-name="marginBottom"
+ data-value="20px"></view>
+ <!-- #endif -->
+ <view class="iconfont icon-clearedformat"
+ @tap="removeFormat"></view>
+ <!-- #ifndef MP-BAIDU -->
+ <view :class="formats.fontFamily ? 'ql-active' : ''"
+ class="iconfont icon-font"
+ data-name="fontFamily"
+ data-value="Pacifico"></view>
+ <view :class="formats.fontSize === '24px' ? 'ql-active' : ''"
+ class="iconfont icon-fontsize"
+ data-name="fontSize"
+ data-value="24px"></view>
+ <!-- #endif -->
+ <view :class="formats.color === '#0000ff' ? 'ql-active' : ''"
+ class="iconfont icon-text_color"
+ data-name="color"
+ data-value="#0000ff"></view>
+ <view :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
+ class="iconfont icon-fontbgcolor"
+ data-name="backgroundColor"
+ data-value="#00ff00"></view>
+ <view class="iconfont icon-date"
+ @tap="insertDate"></view>
+ <view class="iconfont icon--checklist"
+ data-name="list"
+ data-value="check"></view>
+ <view :class="formats.list === 'ordered' ? 'ql-active' : ''"
+ class="iconfont icon-youxupailie"
+ data-name="list"
+ data-value="ordered"></view>
+ <view :class="formats.list === 'bullet' ? 'ql-active' : ''"
+ class="iconfont icon-wuxupailie"
+ data-name="list"
+ data-value="bullet"></view>
+ <view class="iconfont icon-undo"
+ @tap="undo"></view>
+ <view class="iconfont icon-redo"
+ @tap="redo"></view>
+ <view class="iconfont icon-outdent"
+ data-name="indent"
+ data-value="-1"></view>
+ <view class="iconfont icon-indent"
+ data-name="indent"
+ data-value="+1"></view>
+ <view class="iconfont icon-fengexian"
+ @tap="insertDivider"></view>
+ <view class="iconfont icon-charutupian"
+ @tap="insertImage"></view>
+ <view :class="formats.header === 1 ? 'ql-active' : ''"
+ class="iconfont icon-format-header-1"
+ data-name="header"
+ :data-value="1"></view>
+ <view :class="formats.script === 'sub' ? 'ql-active' : ''"
+ class="iconfont icon-zitixiabiao"
+ data-name="script"
+ data-value="sub"></view>
+ <view :class="formats.script === 'super' ? 'ql-active' : ''"
+ class="iconfont icon-zitishangbiao"
+ data-name="script"
+ data-value="super"></view>
+ <view class="iconfont icon-shanchu"
+ @tap="clear"></view>
+ <view :class="formats.direction === 'rtl' ? 'ql-active' : ''"
+ class="iconfont icon-direction-rtl"
+ data-name="direction"
+ data-value="rtl"></view>
+ </view>
+ <view class="content-wrapper"
+ :style="{ height: (height - 140) + 'px' }">
+ <editor id="editor"
+ class="ql-container"
+ placeholder="寮�濮嬭緭鍏�..."
+ show-img-size
+ show-img-toolbar
+ show-img-resize
+ @statuschange="onStatusChange"
+ @input="onInput"
+ :read-only="readOnly"
+ @ready="onEditorReady">
+ </editor>
+ </view>
+ </view>
</view>
</template>
-<script setup>
- import { ref, computed, watch } from "vue";
- // import { QuillEditor } from "@vueup/vue-quill";
- import "@vueup/vue-quill/dist/vue-quill.snow.css";
- import { getToken } from "@/utils/auth";
+<script>
+ export default {
+ props: {
+ modelValue: {
+ type: String,
+ default: "",
+ },
+ height: {
+ type: Number,
+ default: 300,
+ },
+ readOnly: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ formats: {},
+ editorCtx: null,
+ };
+ },
+ watch: {
+ modelValue: {
+ handler(newValue) {
+ if (this.editorCtx && newValue) {
+ this.editorCtx.setContents({ html: newValue });
+ }
+ },
+ immediate: true,
+ },
+ },
+ onLoad() {
+ // #ifndef MP-BAIDU
+ uni.loadFontFace({
+ family: "Pacifico",
+ source: 'url("https://sungd.github.io/Pacifico.ttf")',
+ });
+ // #endif
+ },
+ methods: {
+ onEditorReady() {
+ // #ifdef MP-BAIDU
+ this.editorCtx =
+ requireDynamicLib("editorLib").createEditorContext("editor");
+ // #endif
- const props = defineProps({
- /* 缂栬緫鍣ㄧ殑鍐呭 */
- modelValue: {
- type: String,
+ // #ifdef APP-PLUS || MP-WEIXIN || H5
+ uni
+ .createSelectorQuery()
+ .select("#editor")
+ .context(res => {
+ this.editorCtx = res.context;
+ // 鍒濆鍖栧唴瀹�
+ if (this.modelValue) {
+ this.editorCtx.setContents({ html: this.modelValue });
+ }
+ })
+ .exec();
+ // #endif
+ },
+ onInput() {
+ if (this.editorCtx) {
+ this.editorCtx.getContents({
+ success: res => {
+ this.$emit("update:modelValue", res.html);
+ },
+ });
+ }
+ },
+ undo() {
+ if (this.editorCtx) {
+ this.editorCtx.undo();
+ }
+ },
+ redo() {
+ if (this.editorCtx) {
+ this.editorCtx.redo();
+ }
+ },
+ format(e) {
+ if (!this.editorCtx) return;
+ let { name, value } = e.target.dataset;
+ if (!name) return;
+ this.editorCtx.format(name, value);
+ },
+ onStatusChange(e) {
+ const formats = e.detail;
+ this.formats = formats;
+ },
+ insertDivider() {
+ if (this.editorCtx) {
+ this.editorCtx.insertDivider({
+ success: function () {
+ console.log("insert divider success");
+ },
+ });
+ }
+ },
+ clear() {
+ uni.showModal({
+ title: "娓呯┖缂栬緫鍣�",
+ content: "纭畾娓呯┖缂栬緫鍣ㄥ叏閮ㄥ唴瀹癸紵",
+ success: res => {
+ if (res.confirm && this.editorCtx) {
+ this.editorCtx.clear({
+ success: function (res) {
+ console.log("clear success");
+ },
+ });
+ }
+ },
+ });
+ },
+ removeFormat() {
+ if (this.editorCtx) {
+ this.editorCtx.removeFormat();
+ }
+ },
+ insertDate() {
+ if (!this.editorCtx) return;
+ const date = new Date();
+ const formatDate = `${date.getFullYear()}/${
+ date.getMonth() + 1
+ }/${date.getDate()}`;
+ this.editorCtx.insertText({
+ text: formatDate,
+ });
+ },
+ insertImage() {
+ if (!this.editorCtx) return;
+ uni.chooseImage({
+ count: 1,
+ success: res => {
+ this.editorCtx.insertImage({
+ src: res.tempFilePaths[0],
+ alt: "鍥惧儚",
+ success: function () {
+ console.log("insert image success");
+ },
+ });
+ },
+ });
+ },
},
- /* 楂樺害 */
- height: {
- type: Number,
- default: null,
- },
- /* 鏈�灏忛珮搴� */
- minHeight: {
- type: Number,
- default: null,
- },
- /* 鍙 */
- readOnly: {
- type: Boolean,
- default: false,
- },
- /* 涓婁紶鏂囦欢澶у皬闄愬埗(MB) */
- fileSize: {
- type: Number,
- default: 5,
- },
- /* 绫诲瀷锛坆ase64鏍煎紡銆乽rl鏍煎紡锛� */
- type: {
- type: String,
- default: "url",
- },
- });
-
- const emit = defineEmits(["update:modelValue"]);
-
- const styles = computed(() => {
- let style = {};
- if (props.minHeight) {
- style.minHeight = `${props.minHeight}px`;
- }
- if (props.height) {
- style.height = `${props.height}px`;
- }
- return style;
- });
-
- const content = ref("");
-
- watch(
- () => props.modelValue,
- v => {
- if (v !== content.value) {
- content.value = v == undefined ? "<p></p>" : v;
- }
- },
- { immediate: true }
- );
-
- const options = {
- theme: "snow",
- bounds: document.body,
- debug: "warn",
- modules: {
- // 宸ュ叿鏍忛厤缃�
- toolbar: [
- [{ align: [] }], // 瀵归綈鏂瑰紡
- ["bold", "italic", "underline", "strike"], // 鍔犵矖 鏂滀綋 涓嬪垝绾� 鍒犻櫎绾�
- ["blockquote", "code-block"], // 寮曠敤 浠g爜鍧�
- [{ list: "ordered" }, { list: "bullet" }], // 鏈夊簭銆佹棤搴忓垪琛�
- [{ indent: "-1" }, { indent: "+1" }], // 缂╄繘
- [{ size: ["small", false, "large", "huge"] }], // 瀛椾綋澶у皬
- [{ header: [1, 2, 3, 4, 5, 6, false] }], // 鏍囬
- [{ color: [] }, { background: [] }], // 瀛椾綋棰滆壊銆佸瓧浣撹儗鏅鑹�
- ["clean"], // 娓呴櫎鏂囨湰鏍煎紡
- ["link", "image", "video"], // 閾炬帴銆佸浘鐗囥�佽棰�
- ],
- },
- placeholder: "璇疯緭鍏ュ唴瀹�",
- readOnly: props.readOnly,
};
</script>
-
<style>
- .editor-container {
- width: 100%;
+ @import "./editor-icon.css";
+
+ .editor-wrapper {
+ background: #fff;
}
- .editor-img-uploader {
- display: none;
- }
-
- .editor {
- width: 100%;
- }
-
- .quill-editor {
- border: 1px solid #e8e8e8;
- border-radius: 8px;
+ .content-wrapper {
+ background: #fff;
overflow: hidden;
}
- /* Quill缂栬緫鍣ㄦ牱寮� */
- :deep(.ql-toolbar.ql-snow) {
- border-bottom: 1px solid #e8e8e8;
- border-radius: 8px 8px 0 0;
- padding: 8px 12px;
+ .iconfont {
+ display: inline-block;
+ padding: 8px 8px;
+ width: 24px;
+ height: 24px;
+ cursor: pointer;
+ font-size: 20px;
}
- :deep(.ql-container.ql-snow) {
- min-height: 300px;
- border-radius: 0 0 8px 8px;
+ .toolbar {
+ box-sizing: border-box;
+ border-bottom: 0;
+ font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
}
- :deep(.ql-editor) {
- min-height: 300px;
- font-size: 14px;
+ .ql-container {
+ box-sizing: border-box;
+ padding: 12px 15px;
+ width: 100%;
+ height: 100%;
+ font-size: 16px;
line-height: 1.5;
- padding: 12px;
}
- /* 绉诲姩绔�傞厤 */
- @media (max-width: 768px) {
- :deep(.ql-toolbar.ql-snow) {
- padding: 6px 8px;
- }
-
- :deep(.ql-editor) {
- font-size: 13px;
- padding: 10px;
- }
+ .ql-active {
+ color: #06c;
}
-
- /* 鍥剧墖鏍峰紡 */
- :deep(.ql-editor img) {
- max-width: 100%;
- height: auto;
- border-radius: 4px;
- margin: 8px 0;
- }
-
- /* 宸ュ叿鏍忔寜閽牱寮� */
- :deep(.ql-toolbar.ql-snow .ql-button) {
- height: 28px;
- width: 28px;
- padding: 4px;
- }
-
- :deep(.ql-toolbar.ql-snow .ql-picker-label) {
- height: 28px;
- padding: 4px 8px;
- }
-
- /* 鎻愮ず妗嗘牱寮� */
- :deep(.ql-snow .ql-tooltip[data-mode="link"])::before {
- content: "璇疯緭鍏ラ摼鎺ュ湴鍧�:";
- }
-
- :deep(.ql-snow .ql-tooltip.ql-editing a.ql-action)::after {
- border-right: 0px;
- content: "淇濆瓨";
- padding-right: 0px;
- }
-
- :deep(.ql-snow .ql-tooltip[data-mode="video"])::before {
- content: "璇疯緭鍏ヨ棰戝湴鍧�:";
- }
-
- /* 瀛椾綋澶у皬閫夐」 */
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-label)::before,
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-item)::before {
- content: "14px";
- }
-
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"])::before,
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"])::before {
- content: "10px";
- }
-
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"])::before,
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"])::before {
- content: "18px";
- }
-
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"])::before,
- :deep(.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"])::before {
- content: "32px";
- }
-
- /* 鏍囬閫夐」 */
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label)::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item)::before {
- content: "鏂囨湰";
- }
-
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"])::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"])::before {
- content: "鏍囬1";
- }
-
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"])::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"])::before {
- content: "鏍囬2";
- }
-
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"])::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"])::before {
- content: "鏍囬3";
- }
-
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"])::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"])::before {
- content: "鏍囬4";
- }
-
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"])::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"])::before {
- content: "鏍囬5";
- }
-
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"])::before,
- :deep(.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"])::before {
- content: "鏍囬6";
- }
-
- /* 瀛椾綋閫夐」 */
- :deep(.ql-snow .ql-picker.ql-font .ql-picker-label)::before,
- :deep(.ql-snow .ql-picker.ql-font .ql-picker-item)::before {
- content: "鏍囧噯瀛椾綋";
- }
-
- :deep(.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"])::before,
- :deep(.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"])::before {
- content: "琛嚎瀛椾綋";
- }
-
- :deep(
- .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]
- )::before,
- :deep(
- .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]
- )::before {
- content: "绛夊瀛椾綋";
- }
-</style>
\ No newline at end of file
+</style>
--
Gitblit v1.9.3