zoukankan      html  css  js  c++  java
  • 基于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 {
         600px;
        height: 600px;
        border: 1px solid #ddd;
        position: relative;
    }
    .content-viewer {
         100%;
        height: 552px;
        overflow-y: scroll;
        overflow-x: hidden;
    }
    .editor-btn-viewer {
         100%;
        position: absolute;
        bottom: 0;
        left: 0;
    }
    .el-button {
        background-color: #eee;
    }
    .editor-btn{
         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{
         100%;
        height: auto;
        border: 1px solid #ccc;
        min-height: 200px;
        text-align: left;
    }
    .editor-item>img{
         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;
         100%;
        line-height: 100px;
    }
    </style>
    

      

  • 相关阅读:
    关于HTML5画布canvas的功能
    HTML5新标签介绍
    为HTML5添加新样式标签
    iOS开发相关图书推荐
    Android相关图书推荐
    C语言相关图书推荐
    C#相关图书推荐
    C++相关图书推荐
    JavaScript相关图书推荐
    Java相关书籍推荐
  • 原文地址:https://www.cnblogs.com/yiyitong/p/9812502.html
Copyright © 2011-2022 走看看