首页 > 编程 > JavaScript > 正文

基于Vue实现微信小程序的图文编辑器

2019-11-19 13:24:44
字体:
来源:转载
供稿:网友

由于微信小程序不能使用常规的图文编辑器(比如百度的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实现微信小程序的图文编辑器,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表