1、Vue中实现富文本功能
1.1 拓展功能,上传图片从系统图片素材库中拉取图片,黏贴图片时,图片也存入图片素材库中
2、将自带的class样式换为style内联样式
3、适配小程序端(仅展示,不需要编辑)
4、小程序端新增编辑富文本
1、Vue中实现富文本功能
- 使用插件 vue-quill-editor ,先下载插件
npm insatll --save vue-quill-editor
- 在main.js 中引入
import VueQuillEditor from 'vue-quill-editor' //富文本import * as Quill from 'quill'import 'quill/dist/quill.core.css'import 'quill/dist/quill.snow.css'import 'quill/dist/quill.bubble.css'Vue.use(VueQuillEditor);
- 封装富文本组件 textEditor
uploadPictures 为图片素材库 ,引入自己的素材库,
vxe-model 是vxe 插件的弹框组件
diyConfig 为组件传参,默认file,调用本地电脑上的图片,libImgStore为使用素材库中的图片
imgData 为调用图片素材库时必要的传参
getContent 为编辑时富文本Html传参
函数
editorFn 回调函数 返回值进行下一步处理
// 组件 textEditor.vue<template> <div> <quill-editor class="editor" ref="myTextEditor" v-model="content" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)" @change="(e)=>onEditorChange(e)" > </quill-editor> <vxe-modal v-model="modalPic" width="1200" height="720" min-width="860" id="imgUpdata" min-height="420" resize remember storage transfer :esc-closable="true"> <template v-slot:title> <span >素材库</span> </template> <uploadPictures :isChoice="imageData.isChoice" @getPic="getPic" @getPicD="getPicD" v-if="modalPic" :storeId.sync="imageData.storeId"></uploadPictures> </vxe-modal> </div></template><script>// 图片素材库引入 uploadPictures import uploadPictures from '@/components/uploadPictures';import axios from "axios"; export default { props: { getContent:{ type: String, default:"" }, diyConfig:{//自定义配置 type: Object, default:()=>{ return { image:'file',//file 上传图片从电脑文件中选择,libImgStore 微商城素材库 } } }, imageData:{ type: Object, default:()=>{ return {}// storeId 店号,isChoice:多选单选 } } }, components: { uploadPictures, }, mounted() { this.$nextTick(() => { const quill = this.$refs.myTextEditor.quill quill.root.addEventListener('paste', this.handlePaste) //获取图片库中的分类 if(this.diyConfig.image == 'libImgStore'){ this.getClassList() } }) }, data() { return { content: null, editorOption: { modules: { toolbar:{ container:[ ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线 ["blockquote", "code-block"], // 引用 代码块 [{ header: 1 }, { header: 2 }], // 1、2 级标题 [{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表 [{ script: "sub" }, { script: "super" }], // 上标/下标 [{ indent: "-1" }, { indent: "+1" }], // 缩进 // [{'direction': 'rtl'}], // 文本方向 [{ // size: ["small", false, "large", "huge"] size: ['14px', '16px', '18px', '20px', '24px', '28px', '32px'] }], // 字体大小 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题 [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色 [{ font: ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong'] }], // 字体种类 [{ align: [] }], // 对齐方式 ["clean"], // 清除文本格式 ["link", "image", "video"] // 链接、图片、视频 ],//工具菜单栏配置 handlers: { 'image': (e)=>this.imageHandle(e,this.quill) } } }, placeholder: '添加内容...', //提示 readyOnly: false, //是否只读 theme: 'snow', //主题 snow/bubble syntax: true, //语法检测 }, modalPic: false, isHandlingImage: false, } }, directives: { quillImageAdded: { bind(el, binding) { el.addEventListener('paste', (event) => { console.log(event,"paste") if (event.target.tagName === 'IMG') { binding.value(event.target.src); } }); } } }, methods: { // 失去焦点 onEditorBlur() { // console.log(editor) }, // 获得焦点 onEditorFocus() { // console.log(editor) }, // 开始 onEditorReady() { // console.log(editor) }, // 值发生变化 onEditorChange(editor) { this.content = editor.html; this.$emit('update:editor', this.content) this.$emit('editorFn', this.content) }, clearContent() { this.content = '' }, getClassList(){ this.$http.GET('/MA/mallPsdClassify/psdClassList').then(data => { this.classList = data.data this.formDatas[1].options = data.data.map(item => { item.value = item.classifyId; item.name = item.classifyId + '--' + item.classifyName; return item; }); }) }, imageHandle(e,quill){ if(this.diyConfig && this.diyConfig.image == 'libImgStore'){ if (e) { alert('点击了上传图片') } else { quill.format('image', false); } }else{ quill.format('image', ''); } }, // 获取图片信息 getPic(pc) { let quill = this.$refs.myTextEditor.quill; const range = quill.getSelection(true); console.log(pc,"pc",range) if (!range) return; quill.insertEmbed(range.index, "image", pc.picUrl); this.modalPic = false; }, getPicD(pc) { let quill = this.$refs.myTextEditor.quill; const range = quill.getSelection(true); if (!range) return; // 将图片插入到富文本编辑器中 pc.reverse() pc.forEach(element => { quill.insertEmbed(range.index, "image", element.picUrl); }); this.modalPic = false; }, handlePaste(event) { const clipboardData = event.clipboardData || window.clipboardData if (!clipboardData) return const items = clipboardData.items if (!items) return for (let i = 0; i < items.length; i++) { const item = items[i] if (item.type.indexOf('image') !== -1) { const file = item.getAsFile() // 处理粘贴的图片文件 this.handleImageUpload(file) } } }, async handleImageUpload(file) { this.isHandlingImage = false; if(!this.isHandlingImage && this.diyConfig.image && this.diyConfig.image == 'libImgStore'){ this.isHandlingImage = true; console.log(this.modalPic,"image.png",this.isHandlingImage) if(this.modalPic){ this.isHandlingImage = false; return } const formData = new FormData() formData.append('file', file); // 有的后台需要传文件名,不然会报错 formData.append("type", 'image'); formData.append("storeId", this.imageData.storeId); // 发送图片到服务器上传 axios ({ method: "post", url: "/manager/crm-wx/wechat/media/uploadMedia", data: formData, headers: { "Content-Type": "multipart/form-data" } }).then(res=>{ if (res.data.code==200 && res.data.data.url!=null) { // 上传成功将照片传回父组件 const imageUrl = res.data.data.url; let obj = { classifyId:this.classList[0].classifyId, classifyName: this.classList[0].classifyName, media_id: res.data.data.media_id, picUrl: imageUrl, picName: file.name, } //图片上传成功后添加到图片素材库分类中 this.$http.POSTJSON("/MA/mallPsdClassifyRef/merge", [obj]) .then(data => { if (data.code == 200) { this.delDiyImg() // // 上传成功后,在富文本框中插入图片 const quill = this.$refs.myTextEditor.quill const range = quill.getSelection(true) quill.insertEmbed(range.index, 'image', imageUrl) quill.setSelection(range.index + 1) // this.onEditorChange({html:this.content}) // 在富文本框中展示图片 // 如果你不想展示上传失败的图片,则可以省略这一步 // quill.format('image', imageUrl) }else{ this.$message.warning("图片上传失败1!") this.delDiyImg() } }); }else{ this.$message.warning("图片上传失败2!") this.delDiyImg() } this.isHandlingImage = false; }).catch(err=>{ console.log(err,"err") this.$message.warning("图片上传失败3!") this.delDiyImg() this.isHandlingImage = false; }) } }, delDiyImg(){ const quill = this.$refs.myTextEditor.quill const index = quill.getSelection().index quill.deleteText(index-1, 1) } }, computed: { editor() { return this.$refs.myTextEditor.quillEditor; } }, watch: { getContent: { handler(newVal) { console.log(newVal,"newVal") this.content = newVal; }, immediate: true }, diyConfig: { handler(newVal) { if(newVal && newVal.image == 'libImgStore'){ let that = this this.editorOption.modules.toolbar.handlers.image = function(val){ if (val) { that.modalPic = true; } else { this.quill.format('image', false); } } }else{ delete this.editorOption.modules.toolbar.handlers.image } }, immediate: true } } }</script><style lang="scss">.ql-editor u ,.ql-snow u{ text-decoration: underline;}.ql-editor s,del , .ql-snow s,del{ text-decoration: line-through;}.ql-editor i,em , .ql-snow i,em{ font-style: italic;}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before { content: "黑体"; font-family: "SimHei";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before { content: "微软雅黑"; font-family: "Microsoft YaHei";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before { content: "楷体"; font-family: "KaiTi";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before { content: "仿宋"; font-family: "FangSong";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before { content: "Arial"; font-family: "Arial";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before { content: "Times New Roman"; font-family: "Times New Roman";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before { content: "sans-serif"; font-family: "sans-serif";}.ql-font-SimSun { font-family: "SimSun";}.ql-font-SimHei { font-family: "SimHei";}.ql-font-Microsoft-YaHei { font-family: "Microsoft YaHei";}.ql-font-KaiTi { font-family: "KaiTi";}.ql-font-FangSong { font-family: "FangSong";}.ql-font-Arial { font-family: "Arial";}.ql-font-Times-New-Roman { font-family: "Times New Roman";}.ql-font-sans-serif { font-family: "sans-serif";}/* 字号设置 *//* 默认字号 */.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="14px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before { content: "14px"; font-size: 14px;}.ql-size-14px { font-size: 14px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before { content: "16px"; font-size: 16px;}.ql-size-16px { font-size: 16px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before { content: "18px"; font-size: 18px;}.ql-size-18px { font-size: 18px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before { content: "20px"; font-size: 20px;}.ql-size-20px { font-size: 20px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before { content: "24px"; font-size: 24px;}.ql-size-24px { font-size: 24px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="26px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="26px"]::before { content: "26px"; font-size: 26px;}.ql-size-26px { font-size: 26px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="28px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="28px"]::before { content: "28px"; font-size: 28px;}.ql-size-28px { font-size: 28px;}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="32px"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="32px"]::before { content: "32px"; font-size: 32px;}.ql-size-32px { font-size: 32px;} .editor { line-height: normal !important; } .ql-snow .ql-tooltip[data-mode=link]::before { content: "请输入链接地址:" !important; } .ql-snow .ql-tooltip.ql-editing a.ql-action::after { border-right: 0px; content: '保存' !important; padding-right: 0px; } .ql-snow .ql-tooltip[data-mode=video]::before { content: "请输入视频地址:" !important; } .ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: '文本' !important; } .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' !important; } .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' !important; } .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' !important; } .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' !important; } .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' !important; } .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' !important; } .quill-editor .ql-editor .ql-container img{ max-width: 400px; }</style>
在页面中使用
<textEditor ref="editor" :editor.sync="content" :getContent="getContent" :diyConfig="{image:'libImgStore'}" :imageData="{storeId: form.storeId,isChoice:'多选'}" style="width:340px;" @editorFn="editorFn"></textEditor>
此时富文本已经可以使用了
2、 将自带的class样式换为style内联样式
可以参考文档 https://www.kancloud.cn/liuwave/quill/1409946#_61
将main.js 中引入的文件换为 下面文件
import VueQuillEditor from 'vue-quill-editor' //富文本import * as Quill from 'quill'import 'quill/dist/quill.core.css'import 'quill/dist/quill.snow.css'import 'quill/dist/quill.bubble.css'// 设置字体大小const fontSizeStyle = Quill.import('attributors/style/size') // 引入这个后会把样式写在style上fontSizeStyle.whitelist = [ '14px', '16px', '18px', '20px', '24px', '28px', '32px']Quill.register(fontSizeStyle, true)const Font = Quill.import('attributors/style/font') // 引入这个后会把样式写在style上const fonts = [ 'SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong']Font.whitelist = fonts // 将字体加入到白名单Quill.register(Font, true)var Align = Quill.import('attributors/style/align')Quill.register(Align, true)var Direction = Quill.import('attributors/style/direction')Quill.register(Direction, true)Vue.use(VueQuillEditor);
中间这个部分 会在写入样式时,写入style样式,达到目的
其中的 whitelist 、fonts、这两个数组赋值,是将富文本自带的数组换为自定义的数组了
查看原本数据 可以 console.log(Quill.imports) 单独打印出来看
3、 小程序中展示(仅展示,不编辑)
使用uview 中自带的富文本功能,Parse只需把富文本传值传入即可展示,对应的样式查看官方文档
https://v1.uviewui.com/components/parse.html
基本使用,长按复制,样式设置 等功能都能满足 (注意有1.0版本和2.0 版本)根据下载的插件看不同版本的文档
4、小程序中新增、编辑管理端中存入的富文本
1、下载插件
https://ext.dcloud.net.cn/plugin?id=12013 富文本插件
下载到自己项目中
2、把不需要的功能屏蔽,或者如果有满足不了的可以自己开发。
源码中 上传图片代码有两处,根据平台不同,找到对应代码,图片我默认给宽度100%了,所以我控制了不让调整大小,更剧自己需求具体适配
在微信小程序中适配是 // #ifdef MP 从这以下的代码
添加一个参数用来控制富文本的样式 ,我主要是用来更改他的高度
下载插件后的代码 piaoyi-editor 更改完后的代码,可直接复制替换
<template> <view class="container-editor"> <view class="textarea"> <view class="page-body"> <view class='wrapper ql-editor'> <PickerColor ref="colorPicker" :color="{r: 255,g: 0,b: 0,a: 0.6}" @confirm="confirm"></PickerColor> <view class='toolbar' @tap="format"> <view :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-font-size" data-name="fontSize" data-value="24px"></view> <view :class="formats.color? 'ql-active' : ''" class="iconfont icon-zitiyanse" data-name="color" :data-value="formats.color"> </view> <view :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-formatheader1" data-name="header" :data-value="1"></view> <view :class="formats.header === 2 ? 'ql-active' : ''" class="iconfont icon-formatheader2" data-name="header" :data-value="2"></view> <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-shanchuxian" data-name="strike"></view> <view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi" data-name="align" data-value="left"></view> <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> <!-- <view :class="formats.lineHeight ? 'ql-active' : ''" class="iconfont icon-LineHeight" 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-duanqianju" data-name="marginTop" data-value="10px"></view> <view :class="formats.previewarginBottom ? 'ql-active' : ''" class="iconfont icon-duanhouju" data-name="marginBottom" data-value="10px"></view> --> <!-- <view class="iconfont icon-rili4" @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-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="iconfont icon-undo" @tap="undo"></view> <view class="iconfont icon-redo" @tap="redo"></view> <view class="iconfont icon-format" @tap="clear"></view> </view> <!-- showImgResize --> <editor id="editor" class="editor" placeholder="开始输入..." showImgSize showImgToolbar @statuschange="onStatusChange" :read-only="readOnly" @ready="onEditorReady" :style="[editStyle]" @input="saveContens"> </editor> </view> </view> </view> </view></template><script> import PickerColor from "./color-picker.vue" export default { components: { PickerColor }, props: { api: { type: String, default: '' }, photoUrl: { type: String, default: '' }, values: { type: String, default: '' }, readOnly: { type: Boolean, default: false }, maxlength: { type: Number, default: 300 }, name: { type: String, default: 'file' }, editStyle: { type: Object, default: ()=>{} }, }, data() { return { currentTab: 0, curColor: '#000000', show: true, hdid: "", myHtml: "", formats: {} } }, methods: { showPicker() { this.$refs.colorPicker.open() }, confirm(e) { this.editorCtx.format('color', e.hex) }, saveContens() { let that = this; let maxlength = parseInt(that.maxlength); that.editorCtx.getContents({ success: function(res) { let html_text = res.html; let html_length = html_text.length; if (html_length > maxlength) { uni.showModal({ title: '最多只能输入' + maxlength + '字', confirmText: '确定', showCancel: false, success(res) { that.$emit("changes", { html: res.html, length: html_length }); } }); } else { that.$emit("changes", { html: res.html=='<p><br></p>'?'':res.html, length: html_length }); } }, }) }, update() { //获取一下是否有数据 let that = this; setTimeout(() => { that.editorCtx.setContents({ "html": that.values }); }, 1000) }, onEditorReady() { let that = this; uni.createSelectorQuery().in(this).select('#editor').context((res) => { that.editorCtx = res.context; that.update(); }).exec((result) => {}); }, undo() { this.editorCtx.undo() }, redo() { this.editorCtx.redo() }, format(e) { let { name, value } = e.target.dataset if (!name) return if (name == 'color') { this.showPicker() } else { this.editorCtx.format(name, value) } }, onStatusChange(e) { const formats = e.detail this.formats = formats }, insertDivider() { this.editorCtx.insertDivider(); }, clear() { let that = this this.editorCtx.clear({ success: res=>{ this.$emit("changes",{html:""}) } }); }, insertDate() { const date = new Date() const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}` this.editorCtx.insertText({ text: formatDate }) }, insertImage() { let that = this; // #ifdef APP-PLUS || H5 uni.chooseImage({ count: 1, //默认9 sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有 sourceType: ['album'], //从相册或者相机选择 success: (res) => { const tempFilePaths = res.tempFilePaths[0] if (!this.api || !this.photoUrl) { that.editorCtx.insertImage({ src: tempFilePaths, alt: '图像', success: function() {} }) uni.showToast({ title: '未传入api字段或者photoUrl字段,此为临时图片路径', duration: 3000, icon: 'none' }) } else { uni.uploadFile({ url: this.photoUrl + this.api, filePath: tempFilePaths, name: this.name, formData: {}, success: (uploadFileRes) => { var obj = JSON.parse(uploadFileRes.data) if (obj.code == 200) { this.img = this.photoUrl + '/' + obj.data wx.showToast({ title: obj.msg, icon: 'none' }) this.editorCtx.insertImage({ src: this.img, alt: '图像', width: "100%", success: function() { const imageElement = document.querySelector('.custom-image-class'); console.log(imageElement,"imageElement") if (imageElement) { imageElement.style.width = '100%'; // 设置宽度为20px } } }) } else { wx.showToast({ title: obj.msg, icon: 'none' }) } } }); } }, fail() { uni.showToast({ title: '未授权访问相册权限,请授权后使用', icon: 'none' }) } }); // #endif // #ifdef MP uni.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['album'], sizeType: 'compressed', success: (chooseImageRes) => { const tempFilePaths = chooseImageRes.tempFiles[0].tempFilePath; console.log(tempFilePaths) if (!this.api || !this.photoUrl) { that.editorCtx.insertImage({ src: tempFilePaths, alt: '图像', success: function() {} }) uni.showToast({ title: '未传入api字段或者photoUrl字段,此为临时图片路径', duration: 3000, icon: 'none' }) } else { uni.uploadFile({ url: this.photoUrl + this.api, filePath: tempFilePaths, name: this.name, formData: {}, success: (uploadFileRes) => { var obj = JSON.parse(uploadFileRes.data) console.log(obj,"uploadFileRes") if (obj.code == 200) { // this.img = path + '/' + obj.data this.img = obj.data.url wx.showToast({ title: obj.msg, icon: 'none' }) this.editorCtx.insertImage({ src: this.img, alt: '图像', width: "100%", success: function() {} }) } else { wx.showToast({ title: obj.msg, icon: 'none' }) } }, fail(err) { console.log(err) uni.showToast({ title: err.errMsg, icon: 'none' }) } }); } } }) // #endif } } }</script><style> @import url('iconfont.css'); .tabs { display: flex; justify-content: space-around; background-color: #FFFFFF; } .tabs .current { border-bottom: 2px solid #0369D6; } .tabs .tab { font-size: 32upx; } .main { padding: 20upx; background-color: #FFFFFF; } .main .item { display: flex; justify-content: space-between; line-height: 80upx; border-bottom: 1px solid #F2F2F2; } .main .item .left { min-width: 200upx; } .main .title { padding-bottom: 20upx; font-weight: bold; border-bottom: 1px solid #F2F2F2; } .main .textarea { border: 1px solid #F2F2F2; } .wrapper { padding: 0 10upx; } .iconfont { display: inline-block; width: 9%; cursor: pointer; font-size: 40upx !important; text-align: center; padding: 10upx 0; } .icon-rili4 { font-size: 48upx !important; } .icon-duanqianju, .icon-duanhouju, .icon-zitijiacu, .icon-zitixieti, .icon-zitixiahuaxian, .icon-shanchuxian { font-size: 36upx !important; } .toolbar { box-sizing: border-box; border-bottom: 0; margin-bottom: 10upx; } .ql-container { box-sizing: border-box; width: 100%; min-height: 600upx; height: auto; background: #fff; font-size: 32upx; line-height: 1; padding-bottom: 60upx; } /deep/ .ql-editor.ql-blank:before { font-size: 28upx; font-style: inherit; } .ql-active { color: #ff0000; } button { width: 150upx; font-size: 30upx; } .editor { color: #333; }</style>
插件已经修改为成后,就可以在页面中使用了
3、在页面中使用
注意下面部分需要换成自己的接口
完整代码 如下
<template> <view class="richtext"> <piaoyiEditor :values="desc" :maxlength="3000" @changes="saveContens" :readOnly="readOnly" :photoUrl="photoUrl" :api="api" :name="name" :editStyle="editStyle"/> <view class="q-flex-center q-position-fixed-Bottom q-borderBox u-p-b-20 u-p-t-20 q-shadow bottomBar"> <view class="buttonSTY" @click="submit"> 确认 </view> </view> </view></template><script> import piaoyiEditor from '@/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue'; export default { data() { return { readOnly: false, //是否只读 photoUrl: '', //服务器图片域名或者ip api: '/wechat/media/uploadMedia', //上传图片接口地址 txt: '', name: 'file', desc: '<div>11111222</div>', editStyle: { height: "calc(100vh - 130px)", } }; }, components: { piaoyiEditor }, onLoad(option){ this.desc = decodeURIComponent(option.desc); this.txt = this.desc; }, onShow() { this.photoUrl = this.$common.api_url+this.$common.fileserver; this.api = `/wechat/media/uploadMedia?type=image&storeId=${uni.getStorageSync('shopNo')}`; }, methods: { saveContens(e) { console.log(e,"e") this.txt = e.html }, submit(){ let that = this; let pages = getCurrentPages() let prevPage = pages[pages.length - 2]; uni.navigateBack({ delta: 1, success() { prevPage.$vm.richTextSubmit(encodeURIComponent(that.txt)); }, }) }, }, }</script><style scoped> .bottomBar{ background: #fff; } .buttonSTY{ width: 600rpx; text-align: center; background: #FC6B3A; height: 80rpx; font-size: 30rpx; line-height: 80rpx; color: #f3f3f3; border-radius: 40rpx; }</style>