| | |
| | | <template> |
| | | <div> |
| | | <el-upload |
| | | v-if="type === 'url'" |
| | | ref="uploadRef" |
| | | :action="upload.url" |
| | | :before-upload="handleBeforeUpload" |
| | | :on-success="handleUploadSuccess" |
| | |
| | | name="file" |
| | | :show-file-list="false" |
| | | :headers="upload.headers" |
| | | ref="uploadRef" |
| | | v-if="type === 'url'" |
| | | > |
| | | </el-upload> |
| | | <div class="editor"> |
| | | <quill-editor |
| | | ref="quillEditorRef" |
| | | v-model:content="content" |
| | | contentType="html" |
| | | @textChange="(e: any) => $emit('update:modelValue', content)" |
| | | content-type="html" |
| | | :options="options" |
| | | :style="styles" |
| | | @text-change="(e: any) => $emit('update:modelValue', content)" |
| | | /> |
| | | </div> |
| | | </div> |
| | |
| | | import { QuillEditor, Quill } from '@vueup/vue-quill'; |
| | | import '@vueup/vue-quill/dist/vue-quill.snow.css'; |
| | | import { propTypes } from '@/utils/propTypes'; |
| | | import { globalHeaders } from "@/utils/request"; |
| | | import { globalHeaders } from '@/utils/request'; |
| | | |
| | | const props = defineProps({ |
| | | /* 编辑器的内容 */ |
| | |
| | | const upload = reactive<UploadOption>({ |
| | | headers: globalHeaders(), |
| | | url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload' |
| | | }) |
| | | }); |
| | | const quillEditorRef = ref(); |
| | | |
| | | const options = ref({ |
| | | theme: "snow", |
| | | theme: 'snow', |
| | | bounds: document.body, |
| | | debug: "warn", |
| | | debug: 'warn', |
| | | modules: { |
| | | // 工具栏配置 |
| | | toolbar: { |
| | | container: [ |
| | | ["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'] // 链接、图片、视频 |
| | | ], |
| | | handlers: { |
| | | image: function (value: any) { |
| | | if (value) { |
| | | // 调用element图片上传 |
| | | (document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click(); |
| | | (document.querySelector('.editor-img-uploader>.el-upload') as HTMLDivElement)?.click(); |
| | | } else { |
| | | Quill.format("image", true); |
| | | Quill.format('image', true); |
| | | } |
| | | }, |
| | | }, |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | placeholder: "请输入内容", |
| | | readOnly: props.readOnly, |
| | | placeholder: '请输入内容', |
| | | readOnly: props.readOnly |
| | | }); |
| | | |
| | | const styles = computed(() => { |
| | |
| | | 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 content = ref(''); |
| | | watch( |
| | | () => props.modelValue, |
| | | (v) => { |
| | | if (v !== content.value) { |
| | | content.value = v === undefined ? '<p></p>' : v; |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | // 图片上传成功返回图片地址 |
| | | const handleUploadSuccess = (res: any) => { |
| | |
| | | // 获取光标位置 |
| | | let length = quill.selection.savedRange.index; |
| | | // 插入图片,res为服务器返回的图片链接地址 |
| | | quill.insertEmbed(length, "image", res.data.url); |
| | | quill.insertEmbed(length, 'image', res.data.url); |
| | | // 调整光标到最后 |
| | | quill.setSelection(length + 1); |
| | | proxy?.$modal.closeLoading(); |
| | |
| | | proxy?.$modal.loading(res.msg); |
| | | proxy?.$modal.closeLoading(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 图片上传前拦截 |
| | | const handleBeforeUpload = (file: any) => { |
| | | const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"]; |
| | | const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg']; |
| | | const isJPG = type.includes(file.type); |
| | | //检验文件格式 |
| | | if (!isJPG) { |
| | |
| | | } |
| | | proxy?.$modal.loading('正在上传文件,请稍候...'); |
| | | return true; |
| | | } |
| | | }; |
| | | |
| | | // 图片失败拦截 |
| | | const handleUploadError = (err: any) => { |
| | | console.error(err); |
| | | proxy?.$modal.msgError('上传文件失败'); |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | |
| | | .quill-img { |
| | | display: none; |
| | | } |
| | | .ql-snow .ql-tooltip[data-mode="link"]::before { |
| | | content: "请输入链接地址:"; |
| | | .ql-snow .ql-tooltip[data-mode='link']::before { |
| | | content: '请输入链接地址:'; |
| | | } |
| | | .ql-snow .ql-tooltip.ql-editing a.ql-action::after { |
| | | border-right: 0; |
| | | content: "保存"; |
| | | content: '保存'; |
| | | padding-right: 0; |
| | | } |
| | | .ql-snow .ql-tooltip[data-mode="video"]::before { |
| | | content: "请输入视频地址:"; |
| | | .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"; |
| | | 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='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='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-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: "文本"; |
| | | 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='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='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='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='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='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-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: "标准字体"; |
| | | 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='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: "等宽字体"; |
| | | .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> |