基于Vue实现微信小程序的图文编辑器
程序员文章站
2023-11-11 23:07:16
由于微信小程序不能使用常规的图文编辑器(比如百度的ueditor )编辑新闻内容之类的,所以用vue写了个针对小程序用的图文编辑器。效果如下
多图上传图片用到了&nb...
由于微信小程序不能使用常规的图文编辑器(比如百度的ueditor )编辑新闻内容之类的,所以用vue写了个针对小程序用的图文编辑器。效果如下
多图上传图片用到了 ajaxfileupload.js (不知道哪位仁兄写的,拿来用了,很好用)
最终形成一串json数据(转成字符串,传入后台存入数据库,小程序端用json.parse 转成json ,按照后台一样的方式渲染即可【小程序端代码还没写,后面再贴出来吧】)
json格式如
[{"mytype":1,"content":"测试数据\n\n11111\n\n","font":{"size":0,"weight":1,"del":1,"line":0,"center":1,"color":"#ed1c24","bgcolor":"#fff","showcolor":0}},{"mytype":3,"content":""},{"mytype":2,"content":"/upload/dyproductimgs/20180725/9841925131090216.jpg_e500_100.jpg","loading":1,"groupid":"627459ec-d372-e372-218e-b93b83cb2d02"},{"mytype":2,"content":"/upload/dyproductimgs/20180725/1574162212592205.jpg_e500_100.jpg","loading":1,"groupid":"627459ec-d372-e372-218e-b93b83cb2d02"},{"mytype":2,"content":"/upload/dyproductimgs/20180725/8745023656415428.jpg_e500_100.jpg","loading":1,"groupid":"627459ec-d372-e372-218e-b93b83cb2d02"},{"mytype":2,"content":"/upload/dyproductimgs/20180725/7027501123579481.jpg_e500_100.jpg","loading":1,"groupid":"627459ec-d372-e372-218e-b93b83cb2d02"}]
html代码
<div class="editor-box vue-container"> <div class="vuefor" v-for="i in editordata.length+1" v-on:click="hidecolorbox(i-1)"> <div class="tool-box"> <div class="tool-box-sub"> <div class="tool-list"> <div v-if="reload"> <input type="file" v-on:change.stop="uploadfile(i-1)" v-bind:id="buildfileid(i-1)" v-bind:name="buildfileid(i-1)" multiple="multiple"> </div> <label class="tool-item" v-on:click.stop="itemadd(i-1,1)"> <div class="icon"><img src="~/res/img/icon-font.png" alt="" /></div> <div class="text">文字</div> </label> <!--v-on:click.stop="itemadd(i-1,2)"--> <label class="tool-item" v-bind:for="buildfileid(i-1)"> <div class="icon"><img src="~/res/img/icon-img.png" alt="" /></div> <div class="text">图片</div> </label> <label class="tool-item" v-on:click.stop="itemadd(i-1,3)"> <div class="icon"><img src="~/res/img/icon-line.png" alt="" /></div> <div class="text">分割</div> </label> <label class="tool-item enabled" v-on:click.stop="itemadd(i-1,4)"> <div class="icon"><img src="~/res/img/icon-video.png" alt="" /></div> <div class="text">视频</div> </label> <label class="tool-item enabled" v-on:click.stop="itemadd(i-1,5)"> <div class="icon"><img src="~/res/img/icon-link.png" alt="" /></div> <div class="text">链接</div> </label> </div> </div> </div> <div class="editor-item" v-if="i <= editordata.length"> <div class="head"> <div class="h-btn fleft" v-on:click.stop="itemup(i-1)"> <img src="~/res/img/icon-up.png" /> </div> <div class="h-btn fleft" v-on:click.stop="itembottom(i-1)"> <img src="~/res/img/icon-bottom.png" /> </div> <div class="h-btn fright" v-on:click.stop="itemdel(i-1)"> <img src="~/res/img/icon-del.png" /> </div> </div> <div class="content" v-if="editordata[i-1].mytype==1"> <!--文字类型的输入框--> <div class="text-box"> <div class="head"> <div title="加粗" v-on:click.stop="fontweight(i-1)" v-bind:class="{ 'head-btn': true,'sel':editordata[i-1].font.weight==1 }"><img src="~/res/img/icon-font-weight.png" alt="" /></div> <div title="放大字体" v-on:click.stop="fontda(i-1)" v-bind:class="{ 'head-btn': true}"><img src="~/res/img/icon-font-da.png" alt="" /></div> <div title="缩小字体" v-on:click.stop="fontxiao(i-1)" v-bind:class="{ 'head-btn': true}"><img src="~/res/img/icon-font-xiao.png" alt="" /></div> <div title="删除线" v-on:click.stop="fontdel(i-1)" v-bind:class="{ 'head-btn': true,'sel':editordata[i-1].font.del==1 }"><img src="~/res/img/icon-font-del.png" alt="" /></div> <div title="下划线" v-on:click.stop="fontline(i-1)" v-bind:class="{ 'head-btn': true,'sel':editordata[i-1].font.line==1 }"><img src="~/res/img/icon-font-line.png" alt="" /></div> <div title="居中" v-on:click.stop="fontcenter(i-1)" v-bind:class="{ 'head-btn': true,'sel':editordata[i-1].font.center==1 }"><img src="~/res/img/icon-font-center.png" alt="" /></div> <div title="字体颜色" v-on:click.stop="fontshowcolor(i-1)" v-bind:class="{ 'head-btn': true }" v-bind:style="initfontcolor(editordata[i-1].font)"> a <div v-on:click.stop="stopclick" class="color-box" v-bind:class="{'hide':editordata[i-1].font.showcolor!=1}"> <div class="color-title"> 字体颜色 </div> <div class="color-list"> <div class="color-item" v-for="color in colors"> <span v-on:click.stop="fontsetcolor(i-1,color)" v-bind:style="initbgcolor(color)"></span> </div> </div> <div class="color-title"> 字体颜色代码 </div> <div class="color-input"> <input type="text" v-model="editordata[i-1].font.color" /> </div> <!--<div class="color-title"> 字体背景颜色 </div> <div class="color-list"> <div class="color-item" v-for="color in colors"> <span v-on:click.stop="fontsetcolor(i-1,color)" v-bind:style="initbgcolor(color)"></span> </div> </div> <div class="color-title"> 字体背景颜色代码 </div> <div class="color-input"> <input type="text" v-model="editordata[i-1].font.bgcolor" /> </div>--> </div> </div> </div> <div class="line"></div> <div class="input"> <textarea name="" rows="" cols="" v-bind:style="initstyle(editordata[i-1].font)" v-model="editordata[i-1].content"></textarea> </div> <div class="line"></div> </div> </div> <div class="content" v-if="editordata[i-1].mytype==2" style=""> <!--图片--> <div class="img-box"> <img v-if="editordata[i-1].loading==1" v-bind:src="editordata[i-1].content" alt="" /> <img class="loading" v-if="editordata[i-1].loading==0" src="~/res/img/img_loading.gif" alt="" /> </div> </div> <div class="content" v-if="editordata[i-1].mytype==3"> <!--分割线--> <div class="line-box"> </div> </div> <div class="clear" style=""></div> </div> </div> </div>
js 代码
需要引用 jquery、vue、ajaxfileupload
var pagedata = { editordata: [], colors: [ "#000", "#7f7f7f", "#880015", "#ed1c24", "#ff7f27", "#fff200", "#22b14c", "#3f48cc", "#e36c09", "#31859b", "#5f497a", "#76923c", "#953734", "#366092", "#938953", "#fff" ], reload:true }; //初始化vue var vmmenu = new vue({ el: '.vue-container', data: pagedata, methods: { //生成一个fileid buildfileid: function (index) { return "file" + index; }, initstyle: function (font) { var stylestr = ""; var fontsize = 18; fontsize += font.size * 3; stylestr += "font-size: " + fontsize + "px;" if (font.weight == 1) stylestr += "font-weight: bold;" if (font.del == 1) stylestr += "text-decoration:line-through;" if (font.line == 1) stylestr += "text-decoration:underline;" if (font.center == 1) stylestr += "text-align: center;" if (font.color) stylestr += ("color:" + font.color + ";"); if (font.bgcolor) stylestr += ("display: inline;background-color:" + font.bgcolor + ";"); return stylestr; }, //字体的颜色 initfontcolor: function (font) { var result = ""; result += "color:"; result += font.color; result += ";"; result += "background-color:"; result += font.bgcolor; result += ";"; return result; }, //字体背景的颜色 initbgcolor: function (color) { return "background-color:" + color; }, //加粗或者取消嘉措 fontweight: function (index) { pagedata.editordata[index].font.weight = (pagedata.editordata[index].font.weight == 1 ? 0 : 1); }, //字体加大 fontda: function (index) { pagedata.editordata[index].font.size++; }, //字体减小 fontxiao: function (index) { pagedata.editordata[index].font.size--; }, //删除线 fontdel: function (index) { pagedata.editordata[index].font.del = (pagedata.editordata[index].font.del == 1 ? 0 : 1); }, //下划线 fontline: function (index) { pagedata.editordata[index].font.line = (pagedata.editordata[index].font.line == 1 ? 0 : 1); }, //居中显示 fontcenter: function (index) { pagedata.editordata[index].font.center = (pagedata.editordata[index].font.center == 1 ? 0 : 1); }, fontshowcolor: function (index) { pagedata.editordata[index].font.showcolor = (pagedata.editordata[index].font.showcolor == 1 ? 0 : 1); }, //选择字体颜色 fontsetcolor: function (index, color) { pagedata.editordata[index].font.color = color; this.hidecolorbox(index); }, //隐藏颜色选择框 hidecolorbox: function (index) { if (pagedata.editordata && pagedata.editordata.length > index && pagedata.editordata[index].mytype == 1) pagedata.editordata[index].font.showcolor = 0; }, //上传图片 uploadfile: function (index) { //用于强制重新渲染 input.file 用于清空之前的文件 ^_^ pagedata.reload = false; //添加一个组id,方便后面上传完成后识别应该更新哪条数据 var groupid = guid(); var that = this; var fileid = "file" + index; var files = $("#" + fileid)[0].files; for (var i = 0; i < files.length; i++) { that.itemadd(index + i, 2, groupid); } jquery.ajaxfileupload({ url: '@url.content("~/img/uploadproductdpicarray?path=dyproductimgs")', //用于文件上传的服务器端请求地址 secureuri: false, //是否需要安全协议,一般设置为false fileelementid: fileid, //文件上传域的id datatype: 'json', //返回值类型 一般设置为json success: function (data) //服务器成功响应处理函数 { //var result = json.parse(data); pagedata.reload = true; var result = data; console.log(result); if (result.resultstate == "1") { var j = 0; for (var i = 0; i < pagedata.editordata.length; i++) { if (pagedata.editordata[i].groupid && pagedata.editordata[i].groupid == groupid) { pagedata.editordata[i].content = "@url.content("~")" + result.data[j].substring(1); pagedata.editordata[i].loading = 1; j++; } } console.log(result); } else alert("上传失败!"); }, error: function (data)//服务器响应失败处理函数 { alert("上传失败!"); } }); }, //上升模块 itemup: function (index) { if (index > 0) { var itemdata = pagedata.editordata[index]; pagedata.editordata.splice(index, 1); pagedata.editordata.splice(index - 1, 0, itemdata); } }, //下降模块 itembottom: function (index) { if (index + 1 < pagedata.editordata.length) { var itemdata = pagedata.editordata[index]; pagedata.editordata.splice(index, 1); pagedata.editordata.splice(index + 1, 0, itemdata); } }, //删除模块 itemdel: function (index) { pagedata.editordata.splice(index, 1); }, //添加一个新的模块 itemadd: function (index, type, groupid) { var itemdata = null; switch (type) { case 1: itemdata = { mytype: 1, content: "", font: { size: 0, //字体大小 每+1 字体+2px -1同减 weight: 0, //是否加粗 del: 0, //是否删除线 line: 0, //是否下划线 center: 0, //是否居中 color: "#000", //字体颜色 bgcolor: "#fff", //字体颜色 showcolor: 0 //是否显示颜色选择框 } }; break; case 2: itemdata = { mytype: 2, content: "res/img/1.jpg", loading: 0 //是否已经成功上传 }; break; case 3: itemdata = { mytype: 3, content: "" }; break; default: alert('暂不支持'); break; } if (itemdata) { if (groupid) itemdata.groupid = groupid; pagedata.editordata.splice(index, 0, itemdata); } }, //一个用于阻止冒泡的事件 stopclick: function () { }, }, //实例被调用后 created: function () { }, //el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 updated: function () { this.$nexttick(function () { ////console.log(pagedata); //var files = this.$refs.feedbakcimg; //for (var i = 0; i < files.length; i++) { // files[i].clearfiles(); //} }) } });
后台代码 .net (有些方法没有放出来,后面我有时间整理一个单独的demo出来放到云盘)
/// <summary> /// 批量上传商品详情图片 /// </summary> /// <returns></returns> [httppost] public contentresult uploadproductdpicarray(string path) { rdata<list<string>> result = new rdata<list<string>>(); result = uploadpicarray(path); if (result.resultstate == 1) for (int i = 0; i < result.data.count; i++) { if (st.tool.imagehelp.getimagesuffix(result.data[i]) != ".gif") { string imgpath = server.mappath($"~{result.data[i]}"); string imgpathnosuffix = imgpath.substring(0, imgpath.lastindexof(".")); string imgsuffix = st.tool.imagehelp.getimagesuffix(imgpath); image oldimg = image.fromfile(imgpath); //读取图片 //压缩宽度为500的图片,等比 清晰度 100 st.tool.imagehelp.picthumbnail(oldimg, imgpath + "_e500_100" + imgsuffix, 500, 0, 100); oldimg.dispose(); result.data[i] = result.data[i] + "_e500_100" + imgsuffix; } } var jsonresult = jsonconvert.serializeobject(result); return new contentresult() { content = jsonresult }; } /// <summary> /// 上传图片 /// </summary> /// <param name="_path">保存图片的文件夹名称</param> /// <returns>保存结果</returns> private rdata<string> uploadpic(string _path="public") { rdata<string> result = new rdata<string>(); httpfilecollectionbase _file = request.files; if (_file.count > 0) { long size = _file[0].contentlength; string type = _file[0].contenttype; string name = _file[0].filename; //文件格式 string _tp = path.getextension(name); if (_tp.tolower() == ".jpg" || _tp.tolower() == ".jpeg" || _tp.tolower() == ".gif" || _tp.tolower() == ".png" || _tp.tolower() == ".swf") { stream stream = _file[0].inputstream; image image = image.fromstream(stream); string datedir = datetime.now.tostring("yyyymmdd"); string savename = st.tool.expandstring.getnoncenumbert(16) + _tp; string filepath = $"{baseconfig.headpath}{_path}/{datedir}/"; string path = server.mappath(filepath); if (!directory.exists(path)) directory.createdirectory(path); //_file[0].saveas(server.mappath($"{filepath}{savename}")); //初始化图片对象 //image image = new bitmap(server.mappath($"{filepath}{savename}")); foreach (var p in image.propertyitems) { if (p.id == 0x112) { var rft = p.value[0] == 6 ? rotatefliptype.rotate90flipnone : p.value[0] == 3 ? rotatefliptype.rotate180flipnone : p.value[0] == 8 ? rotatefliptype.rotate270flipnone : p.value[0] == 1 ? rotatefliptype.rotatenoneflipnone : rotatefliptype.rotatenoneflipnone; p.value[0] = 0; //旋转属性值设置为不旋转 image.setpropertyitem(p); //回拷进图片流 image.rotateflip(rft); } } //重新保存为正常的图片 image.save(server.mappath($"{filepath}{savename}")); result.data = $"{filepath}{savename}"; } else result.errormsg = "只能上传图片。"; } else result.errormsg = "未选择文件"; return result; } /// <summary> /// 上传多张图片 /// </summary> /// <param name="_path"></param> /// <returns></returns> private rdata<list<string>> uploadpicarray(string _path = "public") { rdata<list<string>> result = new rdata<list<string>>(); result.data = new list<string>(); httpfilecollectionbase _file = request.files; if (_file.count > 0) for (int i = 0; i < _file.count; i++) { //thread.sleep(500); long size = _file[i].contentlength; string type = _file[i].contenttype; string name = _file[i].filename; //文件格式 string _tp = path.getextension(name); if (_tp.tolower() == ".jpg" || _tp.tolower() == ".jpeg" || _tp.tolower() == ".gif" || _tp.tolower() == ".png" || _tp.tolower() == ".swf") { stream stream = _file[i].inputstream; image image = image.fromstream(stream); string datedir = datetime.now.tostring("yyyymmdd"); string savename = st.tool.expandstring.getnoncenumbert(16) + _tp; string filepath = $"{baseconfig.headpath}{_path}/{datedir}/"; string path = server.mappath(filepath); if (!directory.exists(path)) directory.createdirectory(path); //_file[0].saveas(server.mappath($"{filepath}{savename}")); //初始化图片对象 //image image = new bitmap(server.mappath($"{filepath}{savename}")); foreach (var p in image.propertyitems) { if (p.id == 0x112) { var rft = p.value[0] == 6 ? rotatefliptype.rotate90flipnone : p.value[0] == 3 ? rotatefliptype.rotate180flipnone : p.value[0] == 8 ? rotatefliptype.rotate270flipnone : p.value[0] == 1 ? rotatefliptype.rotatenoneflipnone : rotatefliptype.rotatenoneflipnone; p.value[0] = 0; //旋转属性值设置为不旋转 image.setpropertyitem(p); //回拷进图片流 image.rotateflip(rft); } } //重新保存为正常的图片 image.save(server.mappath($"{filepath}{savename}")); result.data.add($"{filepath}{savename}"); //result.data = $"{filepath}{savename}"; } else result.errormsg = "只能上传图片。"; } else result.errormsg = "未选择文件"; return result; }
总结
以上所述是小编给大家介绍的基于vue实现微信小程序的图文编辑器,希望对大家有所帮助
上一篇: ai怎么绘制扁平化的快递包裹?
下一篇: 百度竞价代理商方面的潜规则有哪些?