| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-upload |
| | | :action="uploadUrl" |
| | | :before-upload="handleBeforeUpload" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | name="files" |
| | | :show-file-list="false" |
| | | :headers="headers" |
| | | class="editor-img-uploader" |
| | | v-if="type === 'url'" |
| | | > |
| | | <i ref="uploadRef" class="editor-img-uploader"></i> |
| | | </el-upload> |
| | | </div> |
| | | <div class="editor"> |
| | | <quill-editor |
| | | ref="quillEditorRef" |
| | | v-model:content="content" |
| | | contentType="html" |
| | | @textChange="(e) => $emit('update:modelValue', content)" |
| | | :options="options" |
| | | :style="styles" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { QuillEditor } from "@vueup/vue-quill"; |
| | | import "@vueup/vue-quill/dist/vue-quill.snow.css"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import {uploadPublicFile} from "@/api/basicData/common.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const quillEditorRef = ref(); |
| | | const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/public/upload"); // ä¸ä¼ çå¾çæå¡å¨å°å |
| | | const headers = ref({ |
| | | Authorization: "Bearer " + getToken(), |
| | | }); |
| | | |
| | | const props = defineProps({ |
| | | /* ç¼è¾å¨çå
容 */ |
| | | modelValue: { |
| | | type: String, |
| | | }, |
| | | /* é«åº¦ */ |
| | | height: { |
| | | type: Number, |
| | | default: null, |
| | | }, |
| | | /* æå°é«åº¦ */ |
| | | minHeight: { |
| | | type: Number, |
| | | default: null, |
| | | }, |
| | | /* åªè¯» */ |
| | | readOnly: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | /* ä¸ä¼ æä»¶å¤§å°éå¶(MB) */ |
| | | fileSize: { |
| | | type: Number, |
| | | default: 5, |
| | | }, |
| | | /* ç±»åï¼base64æ ¼å¼ãurlæ ¼å¼ï¼ */ |
| | | type: { |
| | | type: String, |
| | | default: "url", |
| | | }, |
| | | }); |
| | | |
| | | const options = ref({ |
| | | theme: "snow", |
| | | bounds: document.body, |
| | | debug: "warn", |
| | | modules: { |
| | | // å·¥å
·æ é
ç½® |
| | | toolbar: [ |
| | | ["bold", "italic", "underline", "strike"], // å ç² æä½ ä¸å线 å é¤çº¿ |
| | | ["blockquote", "code-block"], // å¼ç¨ 代ç å |
| | | [{ list: "ordered" }, { list: "bullet" }], // æåºãæ åºå表 |
| | | [{ indent: "-1" }, { indent: "+1" }], // ç¼©è¿ |
| | | [{ size: ["small", false, "large", "huge"] }], // åä½å¤§å° |
| | | [{ header: [1, 2, 3, 4, 5, 6, false] }], // æ é¢ |
| | | [{ color: [] }, { background: [] }], // åä½é¢è²ãåä½èæ¯é¢è² |
| | | [{ align: [] }], // 坹齿¹å¼ |
| | | ["clean"], // æ¸
é¤ææ¬æ ¼å¼ |
| | | ["link", "image", "video"], // 龿¥ãå¾çãè§é¢ |
| | | ], |
| | | }, |
| | | placeholder: "请è¾å
¥å
容", |
| | | readOnly: props.readOnly, |
| | | }); |
| | | |
| | | 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 } |
| | | ); |
| | | |
| | | // å¦æè®¾ç½®äºä¸ä¼ å°ååèªå®ä¹å¾çä¸ä¼ äºä»¶ |
| | | onMounted(() => { |
| | | if (props.type == "url") { |
| | | let quill = quillEditorRef.value.getQuill(); |
| | | let toolbar = quill.getModule("toolbar"); |
| | | toolbar.addHandler("image", (value) => { |
| | | if (value) { |
| | | proxy.$refs.uploadRef.click(); |
| | | } else { |
| | | quill.format("image", false); |
| | | } |
| | | }); |
| | | quill.root.addEventListener("paste", handlePasteCapture, true); |
| | | } |
| | | }); |
| | | |
| | | // ä¸ä¼ åæ ¡æ£æ ¼å¼åå¤§å° |
| | | function handleBeforeUpload(file) { |
| | | const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"]; |
| | | const isJPG = type.includes(file.type); |
| | | //æ£éªæä»¶æ ¼å¼ |
| | | if (!isJPG) { |
| | | proxy.$modal.msgError(`å¾çæ ¼å¼é误!`); |
| | | return false; |
| | | } |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | if (props.fileSize) { |
| | | const isLt = file.size / 1024 / 1024 < props.fileSize; |
| | | if (!isLt) { |
| | | proxy.$modal.msgError(`ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿ ${props.fileSize} MB!`); |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // ä¸ä¼ æåå¤ç |
| | | function handleUploadSuccess(res, file) { |
| | | // 妿ä¸ä¼ æå |
| | | if (res.code == 200) { |
| | | const imageUrl = resolveImageUrl(res); |
| | | if (!imageUrl) { |
| | | proxy.$modal.msgError("æªè·åå°å¾çå°å"); |
| | | return; |
| | | } |
| | | // è·å坿æ¬å®ä¾ |
| | | let quill = toRaw(quillEditorRef.value).getQuill(); |
| | | // è·åå
æ ä½ç½® |
| | | const selection = quill.getSelection(true); |
| | | const length = selection ? selection.index : quill.getLength(); |
| | | // æå
¥å¾çï¼res.url为æå¡å¨è¿åçå¾ç龿¥å°å |
| | | quill.insertEmbed(length, "image", imageUrl); |
| | | // è°æ´å
æ å°æå |
| | | quill.setSelection(length + 1); |
| | | } else { |
| | | proxy.$modal.msgError("å¾çæå
¥å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | function resolveImageUrl(res) { |
| | | if (!res) return ""; |
| | | // å
¼å®¹æ°æ¥å£: data[0].previewURL |
| | | const previewURL = res?.data?.[0]?.previewURL; |
| | | if (previewURL) { |
| | | return previewURL; |
| | | } |
| | | // å
¼å®¹æ§æ¥å£ |
| | | if (res.url) { |
| | | return res.url; |
| | | } |
| | | if (res.fileName) { |
| | | return `${import.meta.env.VITE_APP_BASE_API}${res.fileName}`; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | // ä¸ä¼ 失败å¤ç |
| | | function handleUploadError() { |
| | | proxy.$modal.msgError("å¾çæå
¥å¤±è´¥"); |
| | | } |
| | | |
| | | // å¤å¶ç²è´´å¾çå¤ç |
| | | function handlePasteCapture(e) { |
| | | const clipboard = e.clipboardData || window.clipboardData; |
| | | if (clipboard && clipboard.items) { |
| | | for (let i = 0; i < clipboard.items.length; i++) { |
| | | const item = clipboard.items[i]; |
| | | if (item.type.indexOf("image") !== -1) { |
| | | e.preventDefault(); |
| | | const file = item.getAsFile(); |
| | | insertImage(file); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | function insertImage(file) { |
| | | const formData = new FormData(); |
| | | formData.append("files", file); |
| | | uploadPublicFile(formData).then((res) => { |
| | | handleUploadSuccess(res) |
| | | }) |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .editor-img-uploader { |
| | | display: none; |
| | | } |
| | | .editor, |
| | | .ql-toolbar { |
| | | white-space: pre-wrap !important; |
| | | line-height: normal !important; |
| | | } |
| | | .quill-img { |
| | | display: none; |
| | | } |
| | | .ql-snow .ql-tooltip[data-mode="link"]::before { |
| | | content: "请è¾å
¥é¾æ¥å°å:"; |
| | | } |
| | | .ql-snow .ql-tooltip.ql-editing a.ql-action::after { |
| | | border-right: 0px; |
| | | content: "ä¿å"; |
| | | padding-right: 0px; |
| | | } |
| | | .ql-snow .ql-tooltip[data-mode="video"]::before { |
| | | content: "请è¾å
¥è§é¢å°å:"; |
| | | } |
| | | .ql-snow .ql-picker.ql-size .ql-picker-label::before, |
| | | .ql-snow .ql-picker.ql-size .ql-picker-item::before { |
| | | content: "14px"; |
| | | } |
| | | .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, |
| | | .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { |
| | | content: "10px"; |
| | | } |
| | | .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, |
| | | .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { |
| | | content: "18px"; |
| | | } |
| | | .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, |
| | | .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { |
| | | content: "32px"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item::before { |
| | | content: "ææ¬"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { |
| | | content: "æ é¢1"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { |
| | | content: "æ é¢2"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { |
| | | content: "æ é¢3"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { |
| | | content: "æ é¢4"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { |
| | | content: "æ é¢5"; |
| | | } |
| | | .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, |
| | | .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { |
| | | content: "æ é¢6"; |
| | | } |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item::before { |
| | | content: "æ ååä½"; |
| | | } |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { |
| | | content: "衬线åä½"; |
| | | } |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { |
| | | content: "ç宽åä½"; |
| | | } |
| | | </style> |