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


Posted in Javascript onJuly 25, 2018

由于微信小程序不能使用常规的图文编辑器(比如百度的UEditor )编辑新闻内容之类的,所以用vue写了个针对小程序用的图文编辑器。效果如下

基于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实现微信小程序的图文编辑器,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
可以文本显示的公告栏的js代码
Mar 11 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
Aug 09 Javascript
基于jquery的simpleValidate简易验证插件
Jan 31 Javascript
Angularjs 滚动加载更多数据
Mar 17 Javascript
JavaScript实现in-place思想的快速排序方法
Aug 07 Javascript
connection reset by peer问题总结及解决方案
Oct 21 Javascript
jquery实现输入框实时输入触发事件代码
Dec 21 Javascript
微信小程序 石头剪刀布实例代码
Jan 04 Javascript
Mongoose中document与object的区别示例详解
Sep 18 Javascript
Vue中实现权限控制的方法示例
Jun 07 Javascript
Jquery实现获取子元素的方法分析
Aug 24 jQuery
JavaScript用document.write()输出换行的示例代码
Nov 26 Javascript
详解ECMAScript typeof用法
Jul 25 #Javascript
微信小程序动态生成二维码的实现代码
Jul 25 #Javascript
JavaScript设计模式之装饰者模式定义与应用示例
Jul 25 #Javascript
JavaScript实现的文本框placeholder提示文字功能示例
Jul 25 #Javascript
Bootstrap Fileinput 4.4.7文件上传实例详解
Jul 25 #Javascript
JavaScript简单实现关键字文本搜索高亮显示功能示例
Jul 25 #Javascript
微信小程序实现指定显示行数多余文字去掉用省略号代替
Jul 25 #Javascript
You might like
基于ThinkPHP5框架使用QueryList爬取并存入mysql数据库操作示例
2019/05/25 PHP
js类 from qq
2006/11/13 Javascript
javascript(jquery)利用函数修改全局变量的代码
2009/11/02 Javascript
IE6下通过a标签点击切换图片的问题
2010/11/14 Javascript
javascript中String类的subString()方法和slice()方法
2011/05/24 Javascript
利用Javascript判断操作系统的类型实现不同操作系统下的兼容性
2013/01/29 Javascript
自动最大化窗口的Javascript代码
2013/05/22 Javascript
基于jQuery Bar Indicator 插件实现进度条展示效果
2015/09/30 Javascript
基于javascript制作微博发布栏效果
2016/04/04 Javascript
手机端js和html5刮刮卡效果
2020/09/29 Javascript
jQuery EasyUI 组件加上“清除”功能实例详解
2017/04/11 jQuery
JavaScript中splice与slice的区别
2017/05/09 Javascript
Kotlin学习第一步 kotlin语法特性
2017/05/25 Javascript
原生JS+CSS实现炫酷重力模拟弹跳系统的登录页面
2017/11/01 Javascript
js提取中文拼音首字母的封装工具类
2018/03/12 Javascript
JS数组扁平化(flat)方法总结详解
2019/06/24 Javascript
vue实现短信验证码登录功能(流程详解)
2019/12/10 Javascript
简单了解JavaScript arguement原理及作用
2020/05/28 Javascript
[03:39]2015国际邀请赛主赛事首日精彩回顾
2015/08/05 DOTA
pycharm远程调试openstack的图文教程
2017/11/21 Python
python实现AES和RSA加解密的方法
2019/03/28 Python
python celery分布式任务队列的使用详解
2019/07/08 Python
python计算波峰波谷值的方法(极值点)
2020/02/18 Python
python实现同一局域网下传输图片
2020/03/20 Python
基于CSS3实现的几个小loading效果
2018/09/27 HTML / CSS
CSS3教程:新增加的结构伪类
2009/04/02 HTML / CSS
Mankind美国/加拿大:英国领先的男士美容护发用品公司
2018/12/05 全球购物
德国消费电子产品购物网站:Guter Kauf
2020/09/15 全球购物
运动会通讯稿100字
2014/01/31 职场文书
奥巴马竞选演讲稿
2014/05/15 职场文书
应届大学生求职信
2014/07/20 职场文书
夫妻双方自愿离婚协议书
2014/10/24 职场文书
2014年销售助理工作总结
2014/12/01 职场文书
2016年3月份红领巾广播稿
2015/12/21 职场文书
详解PHP服务器如何在有限的资源里最大提升并发能力
2021/05/25 PHP
oracle delete误删除表数据后如何恢复
2022/06/28 Oracle