视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
基于Vue实现微信小程序的图文编辑器
2020-11-27 22:11:11 责编:小采
文档


由于微信小程序不能使用常规的图文编辑器(比如百度的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/87450236515428.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" " /></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" " /></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" " /></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" " /></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" " /></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" " /></div>
 <div title="放大字体" v-on:click.stop="fontda(i-1)" v-bind:class="{ 'head-btn': true}"><img src="~/res/img/icon-font-da.png" " /></div>
 <div title="缩小字体" v-on:click.stop="fontxiao(i-1)" v-bind:class="{ 'head-btn': true}"><img src="~/res/img/icon-font-xiao.png" " /></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" " /></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" " /></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" " /></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" " />
 <img class="loading" v-if="editorData[i-1].loading==0" src="~/res/img/img_loading.gif" " />
 </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",
 "#9353",
 "#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实现微信小程序的图文编辑器,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

下载本文
显示全文
专题