| | |
| | | :before-upload="handleBeforeUpload" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | name="file" |
| | | name="files" |
| | | :show-file-list="false" |
| | | :headers="headers" |
| | | class="editor-img-uploader" |
| | | v-if="type == 'url'" |
| | | v-if="type === 'url'" |
| | | > |
| | | <i ref="uploadRef" class="editor-img-uploader"></i> |
| | | </el-upload> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import axios from 'axios'; |
| | | 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/upload"); // 上传的图片服务器地址 |
| | | const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/public/upload"); // 上传的图片服务器地址 |
| | | const headers = ref({ |
| | | Authorization: "Bearer " + getToken() |
| | | Authorization: "Bearer " + getToken(), |
| | | }); |
| | | |
| | | const props = defineProps({ |
| | |
| | | type: { |
| | | type: String, |
| | | default: "url", |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | const options = ref({ |
| | |
| | | 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"] // 链接、图片、视频 |
| | | ["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 |
| | | readOnly: props.readOnly, |
| | | }); |
| | | |
| | | const styles = computed(() => { |
| | |
| | | }); |
| | | |
| | | const content = ref(""); |
| | | watch(() => props.modelValue, (v) => { |
| | | if (v !== content.value) { |
| | | content.value = v == undefined ? "<p></p>" : v; |
| | | } |
| | | }, { immediate: true }); |
| | | watch( |
| | | () => props.modelValue, |
| | | (v) => { |
| | | if (v !== content.value) { |
| | | content.value = v == undefined ? "<p></p>" : v; |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | // 如果设置了上传地址则自定义图片上传事件 |
| | | onMounted(() => { |
| | | if (props.type == 'url') { |
| | | if (props.type == "url") { |
| | | let quill = quillEditorRef.value.getQuill(); |
| | | let toolbar = quill.getModule("toolbar"); |
| | | toolbar.addHandler("image", (value) => { |
| | |
| | | quill.format("image", false); |
| | | } |
| | | }); |
| | | quill.root.addEventListener('paste', handlePasteCapture, true); |
| | | quill.root.addEventListener("paste", handlePasteCapture, 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(); |
| | | // 获取光标位置 |
| | | let length = quill.selection.savedRange.index; |
| | | const selection = quill.getSelection(true); |
| | | const length = selection ? selection.index : quill.getLength(); |
| | | // 插入图片,res.url为服务器返回的图片链接地址 |
| | | quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName); |
| | | 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 ""; |
| | | } |
| | | |
| | | // 上传失败处理 |
| | |
| | | if (clipboard && clipboard.items) { |
| | | for (let i = 0; i < clipboard.items.length; i++) { |
| | | const item = clipboard.items[i]; |
| | | if (item.type.indexOf('image') !== -1) { |
| | | if (item.type.indexOf("image") !== -1) { |
| | | e.preventDefault(); |
| | | const file = item.getAsFile(); |
| | | insertImage(file); |
| | |
| | | |
| | | function insertImage(file) { |
| | | const formData = new FormData(); |
| | | formData.append("file", file); |
| | | axios.post(uploadUrl.value, formData, { headers: { "Content-Type": "multipart/form-data", Authorization: headers.value.Authorization } }).then(res => { |
| | | handleUploadSuccess(res.data); |
| | | formData.append("files", file); |
| | | uploadPublicFile(formData).then((res) => { |
| | | handleUploadSuccess(res) |
| | | }) |
| | | } |
| | | </script> |
| | |
| | | .editor-img-uploader { |
| | | display: none; |
| | | } |
| | | .editor, .ql-toolbar { |
| | | .editor, |
| | | .ql-toolbar { |
| | | white-space: pre-wrap !important; |
| | | line-height: normal !important; |
| | | } |