基于vue实现的小程序管理后台图文编辑器

附加说明:

1)基于element-ui的button和upload组件

2)支持文字和图片混合编辑

实现思路:

  页面中主要的组件:多行文本编辑框(高度随文本变化)、图片上传

       功能点梳理:

              新增段落,在文末或者任一段落上可点击添加段落按钮,点击后在文末或段落后新增一个段落;

              新增的段落可选择是文本还是图片,选择文本则将该新增段落变为多行文本编辑框,选择图片则触发选择本地文件进行上传,上传完后替换原新增段落;

              任一段落可进行上下移动、删除、在其下增加新增端口的操作;

              完成编辑,点击完成编辑按钮,用户可指定后续操作,一般是将编辑的内容保存到后端。

代码如下:

<template>
    <div class="editor-viewer">
        <div class="content-viewer">
            <div class="content-list">
                <div v-for="(content, index) in contentList" class="editor-item"
                    :key="index" :tabindex="index" @mouseover="handleMouseIn(index, content)"
                    @mouseout="handleMouseOut(index, content)">
                    <div class="textarea" contenteditable="true" v-if="content.type === 'text'" >
                        {{content.value}}
                    </div>
                    <!-- <textarea v-if="content.type === 'text'" v-model="content.value"></textarea> -->
                    <img v-if="content.type === 'image'" :src="content.value">
                    <div class="add-module" v-if="content.type === 'empty'">
                        <el-button @click="change2Text(index)">A</el-button>
                        <el-upload :data="{index: index}"
                            class="el-button"
                            action="https://jsonplaceholder.typicode.com/posts/"
                            :show-file-list="false"
                            :on-success="handleUploadSuccess" 
                            :before-upload="beforeImageUpload" titlr="只能上传jpg/png文件,且不超过2M">
                            <i class="el-icon-picture" ></i>
                        </el-upload>
                        <el-button @click="delOne(index)" icon="el-icon-delete"></el-button>
                    </div>
                    <div class="editor-item-ops" v-if="content.type !== 'empty' " v-show="content.visible">
                        <el-button icon="el-icon-plus" circle @click="beginAdd(index)"></el-button>
                        <el-button icon="el-icon-arrow-down" v-if="contentList.length > 1 && index !== contentList.length - 1" 
                            circle @click="moveDown(index)"></el-button>
                        <el-button icon="el-icon-arrow-up" circle  v-if="contentList.length > 1 && index !== 0"
                            @click="moveUp(index)"></el-button>
                        <el-button icon="el-icon-delete" circle @click="delOne(index, true)"></el-button>
                    </div>
                </div>
            </div>
        </div>
        <div class="editor-btn-viewer">
            <el-button class="editor-btn" icon="el-icon-circle-check-outline"
                @click="finishEdit"> 完成编辑</el-button>
            <el-button class="editor-btn" icon="el-icon-circle-plus-outline" @click="addAtLast"> 添加模块</el-button>
        </div>
        
    </div>
</template>

<script>
import {Upload} from 'element-ui'
export default {
    data () {
        return {
            contentList: []
        }
    },
    props: ['contents'],
    components: {
        ElUpload: Upload
    },
    methods: {
        spliceContent (start, count, added) {
            if (typeof added !== 'undefined') {
                return this.contentList.splice(start, count, added)
            } else {
                return this.contentList.splice(start, count)
            }
        },
        addAtLast () {
            this.contentList.push({type: 'empty', value: '', visible: false})
        },
        beginAdd (index) {
            this.spliceContent(index+1, 0, {type: 'empty', value: '', visible: false}) 
        },
        moveDown (index) {
            let cur = this.spliceContent(index, 1)
            this.spliceContent(index + 1, 0, cur[0])
        },
        moveUp (index) {
            let cur = this.spliceContent(index, 1)
            this.spliceContent(index - 1, 0, cur[0])
        },
        delOne (index, delImage = false) {
            this.spliceContent(index, 1)
            if (delImage) {
                // todo: 需要删除内容服务器上的图片文件
            }
        },
        change2Text (index) {
            this.spliceContent(index, 1, {type: 'text', value: '', visible: false})
        },
        change2Image (index) {
            this.spliceContent(index, 1, {type: 'image', value: '', visible: false})
        },
        handleUploadSuccess(res, file) {
            // res 需要返回附加data:index
            let imageUrl = URL.createObjectURL(file.raw);
            this.spliceContent(res.index, 1, {type: 'image', value: imageUrl, visible: false})
        },
        beforeImageUpload(file) {
            const isJPG = file.type === 'image/jpeg';
            const isLt2M = file.size / 1024 / 1024 < 2;
            console.info(file)
            if (!isJPG) {
            this.$message.error('上传头像图片只能是 JPG 格式!');
            }
            if (!isLt2M) {
            this.$message.error('上传头像图片大小不能超过 2MB!');
            }
            return isJPG && isLt2M;
        },
        handleMouseIn (index) {
            let item = this.contentList[index];
            item.visible = true;
            this.spliceContent(index, 1, item);
        },
        handleMouseOut (index) {
            let item = this.contentList[index];
            item.visible = false;
            this.spliceContent(index, 1, item);
        },
        finishEdit () {

        }
    },
    mounted () {
        this.contentList = this.contents.map(el => {
            el.visible = false;
            return el;
        });
    }
}
</script>
<style scoped>
.editor-viewer {
    width: 600px;
    height: 600px;
    border: 1px solid #ddd;
    position: relative;
}
.content-viewer {
    width: 100%;
    height: 552px;
    overflow-y: scroll;
    overflow-x: hidden;
}
.editor-btn-viewer {
    width: 100%;
    position: absolute;
    bottom: 0;
    left: 0;
}
.el-button {
    background-color: #eee;
}
.editor-btn{
    width: 50%;
    padding: 15px;
    outline: none;
    border: 1px solid #ddd;
    border-radius: 0;
    margin: 0;
    font-size: 16px;
}
.el-button:hover {
    color: #409eff;
    border-color: #c6e2ff;
    background-color: #ecf5ff;
}
.content-list {
    padding: 5px;
}
.editor-item +.editor-item{
    margin-top: 5px;
}
.editor-item {  
    position: relative;
}
div.editor-item:hover{
    box-shadow: 0 0 10px #ccc;

}
.editor-item>.textarea, 
 .editor-item>img{
    width: 100%;
    height: auto;
    border: 1px solid #ccc;
    min-height: 200px;
    text-align: left;
}
.editor-item>img{
    width: 571px;
}
.editor-item>textarea{
    resize: none;
}
.editor-item-ops {
    height: 50px;
    position: absolute;
    right: 6px;
    bottom: 0;
    z-index:100;
}
.editor-item-ops > .editor-btn {
    background-color: #666;
}
.add-module {
    border: 1px dashed #ccc;
    min-height: 100px;
    width: 100%;
    line-height: 100px;
}
</style>

  

猜你喜欢

转载自www.cnblogs.com/yiyitong/p/9812502.html