基于Vue實(shí)現(xiàn)微信小程序的圖文編輯器
由于微信小程序不能使用常規(guī)的圖文編輯器(比如百度的UEditor )編輯新聞內(nèi)容之類(lèi)的,所以用vue寫(xiě)了個(gè)針對(duì)小程序用的圖文編輯器。效果如下

多圖上傳圖片用到了 ajaxfileupload.js (不知道哪位仁兄寫(xiě)的,拿來(lái)用了,很好用)
最終形成一串Json數(shù)據(jù)(轉(zhuǎn)成字符串,傳入后臺(tái)存入數(shù)據(jù)庫(kù),小程序端用JSON.parse 轉(zhuǎn)成JSON ,按照后臺(tái)一樣的方式渲染即可【小程序端代碼還沒(méi)寫(xiě),后面再貼出來(lái)吧】)
json格式如
[{"mytype":1,"content":"測(cè)試數(shù)據(jù)\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">
<!--文字類(lèi)型的輸入框-->
<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: {
//生成一個(gè)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) {
//用于強(qiáng)制重新渲染 input.file 用于清空之前的文件 ^_^
pageData.reload = false;
//添加一個(gè)組ID,方便后面上傳完成后識(shí)別應(yīng)該更新哪條數(shù)據(jù)
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")', //用于文件上傳的服務(wù)器端請(qǐng)求地址
secureuri: false, //是否需要安全協(xié)議,一般設(shè)置為false
fileElementId: fileid, //文件上傳域的ID
dataType: 'json', //返回值類(lèi)型 一般設(shè)置為json
success: function (data) //服務(wù)器成功響應(yīng)處理函數(shù)
{
//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)//服務(wù)器響應(yīng)失敗處理函數(shù)
{
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);
},
//添加一個(gè)新的模塊
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 //是否已經(jīng)成功上傳
};
break;
case 3:
itemData = {
mytype: 3,
content: ""
};
break;
default:
alert('暫不支持');
break;
}
if (itemData) {
if (groupid) itemData.groupid = groupid;
pageData.editorData.splice(index, 0, itemData);
}
},
//一個(gè)用于阻止冒泡的事件
stopclick: function () { },
},
//實(shí)例被調(diào)用后
created: function () {
},
//el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用該鉤子。
updated: function () {
this.$nextTick(function () {
////console.log(pageData);
//var files = this.$refs.feedbakcImg;
//for (var i = 0; i < files.length; i++) {
// files[i].clearFiles();
//}
})
}
});
后臺(tái)代碼 .net (有些方法沒(méi)有放出來(lái),后面我有時(shí)間整理一個(gè)單獨(dú)的demo出來(lái)放到云盤(pán))
/// <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>保存結(jié)果</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}"));
//初始化圖片對(duì)象
//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; //旋轉(zhuǎn)屬性值設(shè)置為不旋轉(zhuǎn)
image.SetPropertyItem(p); //回拷進(jìn)圖片流
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}"));
//初始化圖片對(duì)象
//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; //旋轉(zhuǎn)屬性值設(shè)置為不旋轉(zhuǎn)
image.SetPropertyItem(p); //回拷進(jìn)圖片流
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;
}
總結(jié)
以上所述是小編給大家介紹的基于Vue實(shí)現(xiàn)微信小程序的圖文編輯器,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- activemq整合springboot使用方法(個(gè)人微信小程序用)
- 微信小程序登錄session的使用
- 微信小程序提取公用函數(shù)到util.js及使用方法示例
- 微信小程序 使用騰訊地圖SDK詳解及實(shí)現(xiàn)步驟
- 微信小程序使用第三方庫(kù)Underscore.js步驟詳解
- 微信小程序使用第三方庫(kù)Immutable.js實(shí)例詳解
- 微信小程序 富文本轉(zhuǎn)文本實(shí)例詳解
- 微信小程序富文本渲染引擎的詳解
- 如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過(guò)濾器
- 微信小程序?qū)崿F(xiàn)富文本圖片寬度自適應(yīng)的方法
- 微信小程序整合使用富文本編輯器的方法詳解
相關(guān)文章
解決vue項(xiàng)目運(yùn)行提示W(wǎng)arnings while compiling.警告的問(wèn)題
這篇文章主要介紹了解決vue項(xiàng)目運(yùn)行提示W(wǎng)arnings while compiling.警告的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
Vue結(jié)合leaflet實(shí)現(xiàn)克里金插值
本文主要介紹了Vue結(jié)合leaflet實(shí)現(xiàn)克里金插值,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
vue實(shí)現(xiàn)把接口單獨(dú)存放在一個(gè)文件方式
這篇文章主要介紹了vue實(shí)現(xiàn)把接口單獨(dú)存放在一個(gè)文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
修改vue源碼實(shí)現(xiàn)動(dòng)態(tài)路由緩存的方法
這篇文章主要介紹了修改vue源碼實(shí)現(xiàn)動(dòng)態(tài)路由緩存的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
vue.js計(jì)算屬性computed用法實(shí)例分析
這篇文章主要介紹了vue.js計(jì)算屬性computed用法,結(jié)合實(shí)例形式分析了vue.js使用computed方式進(jìn)行屬性計(jì)算的相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
vue 彈框產(chǎn)生的滾動(dòng)穿透問(wèn)題的解決
這篇文章主要介紹了vue 彈框產(chǎn)生的滾動(dòng)穿透問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09

