附加说明:
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>