<template>
<view class="liu-editor">
<view class="scroll">
<view class="toolsBar-view">
<view class="toolsBar-item" :class="{active:formats[item.name]}" @click="toolsBarItemClick(item)" v-for="(item,index) in toolbarList" :key="index">
<image style="width: 30rpx;height: 30rpx;" :src="item.icon" mode=""></image>
</view>
</view>
</view>
<editor
id="editor"
:show-img-resize="true"
:show-img-size="true"
:show-img-toolbar="true"
:placeholder="placeholder"
@ready="onEditorReady"
@statuschange="onStatusChange"
></editor>
<t-color-picker ref="colorPicker" :color="color" @confirm="colorConfirm" @cancel="colorCancel"></t-color-picker>
</view>
</template>
<script>
export default {
name: "liu-editor",
props: {
placeholder: {
type: String,
default: '开始输入...'
},
loadingText: {
type: String,
default: '上传中...'
},
isLoading: {
type: Boolean,
default: true
},
uploadImgUrl: {
type: String,
default: ''
},
uploadConfig:{
type:Object,
default:()=>({})
},
html:{
type:String,
default:''
}
},
data() {
return {
editorCtx: null,
formats:{},
toolbarList:[
{
name:'image',
icon:'/static/editor-img/image.png',
type:'upload',
},
{
name:'previewFun',
icon:'/static/editor-img/preview.png',
type:'click',
},
{
name:'header',
icon:'/static/editor-img/header.png',
type:'select',
value:[
{
name:'一级标题',
value:'H1'
},
{
name:'二级标题',
value:'H2'
},
{
name:'三级标题',
value:'H3'
},
{
name:'四级标题',
value:'H4'
},
{
name:'五级标题',
value:'H5'
},
{
name:'六级标题',
value:'H6'
}
]
},
{
name:'bold',
icon:'/static/editor-img/bold.png',
type:'click',
},
{
name:'italic',
icon:'/static/editor-img/italic.png',
type:'click',
},
{
name:'underline',
icon:'/static/editor-img/underline.png',
type:'click',
},
{
name:'strike',
icon:'/static/editor-img/strike.png',
type:'click',
},
{
name:'indent',
icon:'/static/editor-img/indent.png',
type:'select',
value:[
{
name:'向右缩进',
value:'+1',
},
{
name:'向左缩进',
value:'-1'
}
]
},
{
name:'list',
icon:'/static/editor-img/list.png',
type:'select',
value:[
{
name:'有序列表',
value:'ordered',
},
{
name:'无序列表',
value:'bullet'
},
{
name:'方框列表',
value:'check'
}
],
},
{
name:'lineHeight',
icon:'/static/editor-img/lineHeight.png',
type:'select',
value:[
{
name:'1',
value:'1'
},
{
name:'1.5',
value:'1.5'
},
{
name:'2',
value:'2'
},
{
name:'2.5',
value:'2.5'
},
{
name:'3',
value:'3'
}
],
},
{
name:'fontSize',
icon:'/static/editor-img/fontSize.png',
type:'select',
value:[
{
name:'10px',
value:'10px'
},
{
name:'13px',
value:'13px'
},
{
name:'16px',
value:'16px'
},
{
name:'18px',
value:'18px'
},
{
name:'24px',
value:'24px'
},
{
name:'32px',
value:'32px'
},
{
name:'48px',
value:'48px'
}
],
},
{
name:'align',
icon:'/static/editor-img/align.png',
type:'select',
value:[
{
name:'左对齐',
value:'left'
},
{
name:'居中对齐',
value:'center'
},
{
name:'右对齐',
value:'right'
},
{
name:'整行对齐',
value:'justify'
}
],
},
{
name:'color',
icon:'/static/editor-img/color.png',
type:'popup',
},
{
name:'undoFun',
icon:'/static/editor-img/undo.png',
type:'click',
},
{
name:'redoFun',
icon:'/static/editor-img/redo.png',
type:'click',
},
{
name:'margin',
icon:'/static/editor-img/margin.png',
type:'select',
value:[
{
name:'四周外边距',
value:'margin',
},
{
name:'上外边距',
value:'marginTop',
},
{
name:'下外边距',
value:'marginBottom',
},
{
name:'左外边距',
value:'marginLeft',
},
{
name:'右外边距',
value:'marginRight',
}
],
},
{
name:'padding',
icon:'/static/editor-img/padding.png',
type:'select',
value:[
{
name:'四周内边距',
value:'padding',
},
{
name:'上内边距',
value:'paddingTop',
},
{
name:'下内边距',
value:'paddingBottom',
},
{
name:'左内边距',
value:'paddingLeft',
},
{
name:'右内边距',
value:'paddingRight',
}
],
},
{
name:'clearFun',
icon:'/static/editor-img/clear.png',
type:'click',
}
],
color: {
r: 255,
g: 0,
b: 0,
a: 0.6
},
};
},
watch:{
html(n,o){
n && this.onEditorReady();
}
},
methods: {
colorConfirm(e) {
this.format('color',e.hex);
},
colorCancel() {
},
onEditorReady() {
if( this.editorCtx ){
this.html && this.initEditor()
return;
}
uni.createSelectorQuery().select('#editor').context((res) => {
if( res.context ){
this.editorCtx = res.context;
this.html && this.initEditor()
}
}).exec()
},
onStatusChange(e){
this.formats = e.detail;
},
format(name,value){
this.editorCtx.format(name,value);
},
insertImage() {
uni.chooseImage({
success: async (res) => {
if (res.tempFilePaths.length) {
this.isLoading && uni.showLoading({title: this.loadingText,mask:true});
for (let i = 0,ilen=res.tempFilePaths.length; i < ilen; i++) {
try{
let [err,img_res] = await this.uploadImageSync(res.tempFilePaths[i]);
let resData = JSON.parse(img_res.data);
if( !resData.error_code ){
uni.hideLoading();
this.editorCtx.insertImage({
src:resData.data.uri,
alt:'图片',
extClass:'editor--editor-img'
})
this.editorCtx.insertText({text:' '});
}else{
uni.hideLoading();
this.toast('图片上传失败');
}
}catch(e){
uni.hideLoading();
this.toast('上传图片发生错误');
}
}
}
},
fail: (err) => {
console.log(err);
}
})
},
initEditor(){
this.editorCtx.setContents({html:this.html});
},
uploadImageSync(filePath) {
if( !this.uploadImgUrl ){return filePath};
let header = this.uploadConfig.header || {};
let formData = this.uploadConfig.formData || {};
return uni.uploadFile({
url: this.uploadImgUrl,
filePath,
header:{
'content-type': 'multipart/form-data',
...header
},
name: 'file',
formData,
});
},
toast(title,duration=1000,icon='none'){
uni.showToast({title,duration,icon});
},
toolsBarItemClick(item){
let switchFun = {
'click':()=>{
let keys = {previewFun:1,undoFun:1,redoFun:1,clearFun:1};
if( keys[item.name] ){
return this[item.name] && this[item.name]();
}
this.format(item.name,item.value);
},
'popup':()=>{
if( this.formats[item.name] ){
return this.format(item.name,'');
}
this.$refs.colorPicker.open();
},
'select':()=>{
if( this.formats[item.name] ){
return this.format(item.name,'');
}
let keys = item.value.map(item=>item.name);
if( item.name === 'margin' || item.name === 'padding' ){
uni.showActionSheet({
itemList: keys,
success: resOne=>{
let name = item.value[resOne.tapIndex].value;
setTimeout(()=>{
let list = ['0px','10px','15px','20px','25px','30px'];
uni.showActionSheet({
itemList: list,
success: resTwo=>{
this.format(name,list[resTwo.tapIndex]);
}
});
},100);
},
fail: function (res) {
console.log(res.errMsg);
}
});
return;
}
uni.showActionSheet({
itemList: keys,
success: res=>{
this.format(item.name,item.value[res.tapIndex].value);
},
fail: function (res) {
console.log(res.errMsg);
}
});
},
'upload':()=>{
this.insertImage();
}
}
switchFun[item.type] && switchFun[item.type]();
},
async previewFun(){
let res = await this.editorGetContents();
if( res.html ){
uni.setStorageSync('editorPreviewHtml',res.html);
}
uni.navigateTo({url:'/pages/editorPreview/editorPreview'});
},
undoFun(){
this.toast('撤销');
this.editorCtx.undo();
},
clearFun(){
uni.showModal({
content:'确定清空编辑器内容?',
success: res => {
if( res.confirm ){
this.editorCtx.clear();
}
}
})
},
editorGetContents(cb){
return new Promise((resolve,reject)=>{
this.editorCtx.getContents({
success:res=>{
resolve(res);
},
fail:err=>{
reject(err);
}
})
});
},
}
}
</script>
<style>
.liu-editor {
width: 100%;
background-color: #fff;
}
.toolsBar-view{
display: flex;
align-items:center;
background-color: #F2F2F2;
padding-left: 20rpx;
border-bottom: 4rpx solid #e1e1e1;
}
.toolsBar-item{
margin-right:25rpx;
width: 50rpx;
height: 50rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.toolsBar-item.active{
background-color: rgba(255,85,0,0.5);
}
.scroll{
width: 100%;
height: 80rpx;
overflow: hidden;
overflow-x: auto;
display: flex;
}
#editor {
padding: 20rpx 20rpx 20rpx;
background-color: #F2F2F2;
box-sizing: border-box;
width: 100%;
height: 100%;
font-size: 16px;
line-height: 1.5;
overflow: auto;
}
</style>