先来说一下,这是一个基于vue和JavaScript实现的详情叠楼样式,上移/下移/置顶/添加/删除功能【PS:虽然觉着有富文本编辑已经可以了,但是一个客户有需求,别人就一定有需求,所以还是想着分享一下】
富文本编辑框可以使用tinymec 指路文档:TinyMCE中文文档中文手册
- OK 先给大家贴个实现样式图
做了组件,页面中引用了elementUI插件(当然大家也可以自己写样式),下面直接贴代码
组件代码
<template>
<el-card style="width: 800px;margin-bottom: 5px;" v-for="(item,index) in detailContent" :key="index">
<template #header>
<div class="itemTit">
<div class="titlL">{{item.type}}</div>
<el-button-group>
<el-button type="primary" :disabled="index==0?true:false"
@click="moveUp(item, index)">上移</el-button>
<el-button type="primary" :disabled="index==detailContent.length-1?true:false"
@click="moveDown(item, index)">下移</el-button>
<el-button type="primary" :disabled="index==0?true:false" @click="topUp(item, index)">置顶</el-button>
<!-- <el-button type="primary" >添加</el-button> -->
<el-button type="primary" @click="deleteType(item, index)">删除</el-button>
</el-button-group>
</div>
</template>
<el-input v-if="item.type=='文字'" v-model="item.value" placeholder="请输入详情文字" style="width: 100%" :rows="5"
type="textarea" />
<el-upload class="avatar-uploader" :action="uploadUrl" accept="image/*" v-if="item.type=='小图'"
:file-list="item.url" :on-remove="handleSmallRemove" :on-success="handleSmallSuccess"
list-type="picture-card" @click="chooseIndex=index" multiple :on-change="handFileChange"
:class="'upload-file'+index">
<el-icon :size="16" style="cursor: pointer">
<component :is="plusIcon" />
</el-icon>
</el-upload>
<div v-if="item.type=='大图'" style="display: flex;align-items: end;">
<img v-if="item.value" :src="item.value" style="width: 500px; height: auto;margin-right: 10px;" />
<el-upload :action="uploadUrl" accept="image/*" :show-file-list="false" :file-list="item.url"
:on-success="handleBigSuccess" @click="chooseIndex=index">
<el-button type="primary">{{item.value?'替换图片':'上传图片'}}</el-button>
</el-upload>
</div>
<div v-if="item.type=='视频'" style="display: flex;align-items: end;">
<video v-if="item.value" :src="item.value" controls
style="width: 500px; height: 260px;margin-right: 10px;"></video>
<el-upload :action="uploadUrl" accept="video/*" :show-file-list="false" :file-list="item.url"
:on-success="handleBigSuccess" @click="chooseIndex=index">
<el-button type="primary">{{item.value?'替换视频':'上传视频'}}</el-button>
</el-upload>
</div>
</el-card>
<div>
<el-button-group style="margin-top: 20px;">
<el-button type="primary" @click="addType(0)">添加文字</el-button>
<el-button type="primary" @click="addType(1)">添加小图</el-button>
<el-button type="primary" @click="addType(2)">添加大图</el-button>
<el-button type="primary" @click="addType(3)">添加视频</el-button>
</el-button-group>
</div>
</template>
<script>
import config from "@/config";
export default {
props: {
modelValue: {
type: String,
default: ""
},
},
data() {
return {
range: [{
type: '文字',
value: '',
},
{
type: '小图',
value: [],
},
{
type: '大图',
value: '',
},
{
type: '视频',
value: '',
}
],
detailContent: [],
uploadUrl: config.API_URL + "/api/common/upload",
plusIcon: 'el-icon-plus',
uploadType: 1,
chooseIndex: -1,
uploadNum: 0,
uploadNumAll: 0,
uploadArr: []
}
},
watch: {
modelValue(val) {
console.log('modelValue', Array.isArray(val), val)
if (val.length > 0 && Array.isArray(val)) {
this.detailContent = val;
}
},
detailContent(val) {
console.log('detailContent', val)
this.$emit('updateContent', val);
}
},
mounted() {
},
methods: {
addType(type) {
var self = this;
var dataArr = JSON.parse(JSON.stringify(self.detailContent));
var data = JSON.parse(JSON.stringify(self.range[type]));
dataArr.push(data);
self.detailContent = dataArr;
this.$emit('updateContent', self.detailContent);
},
handFileChange() {
var upload_img = document.getElementsByClassName('upload-file'+this.chooseIndex)
let uploadNum = 0
if (upload_img && upload_img.length > 0) {
var upload = upload_img[0].getElementsByTagName('input')
if (upload && upload.length > 0 && upload[0].files && upload[0].files.length > 0) {
uploadNum = upload[0].files.length
}
}
if(uploadNum>0){
this.uploadNumAll = uploadNum;
this.uploadArr = JSON.parse(JSON.stringify(this.detailContent));
}
// console.log('上传数量', upload_img, uploadNum, this.uploadArr)
},
handleSmallSuccess(res) {
// console.log('上传handleSmallSuccess', res)
var self = this;
var dataArr = self.uploadArr;
dataArr[self.chooseIndex].value.push(res.data.url);
dataArr[self.chooseIndex].url.push({
name: dataArr[self.chooseIndex].url.length,
url: res.data.url
});
self.uploadArr = dataArr;
self.uploadNum++;
if (self.uploadNum == self.uploadNumAll) {
self.detailContent = self.uploadArr;
self.$emit('updateContent', self.detailContent);
self.uploadNumAll = 0;
self.uploadNum = 0;
}
// console.log('上传uploadNum', self.uploadNum, self.detailContent, self.uploadArr)
},
handleSmallRemove() {
var self = this;
var dataArr = JSON.parse(JSON.stringify(self.detailContent));
dataArr.forEach((item, index) => {
if (item.type == '小图') {
dataArr[index].value = [];
item.url.length > 0 && item.url.forEach((vv) => {
dataArr[index].value.push(vv.url);
})
}
})
self.detailContent = dataArr;
this.$emit('updateContent', self.detailContent);
},
handleBigSuccess(res) {
var self = this;
var dataArr = JSON.parse(JSON.stringify(self.detailContent));
dataArr[self.chooseIndex].value = res.data.url;
dataArr[self.chooseIndex].url[0].url = res.data.url;
self.detailContent = dataArr;
this.$emit('updateContent', self.detailContent);
},
imgDel(index, i) {
var self = this;
var value = self.detailContent[index].value;
value.splice(i, 1);
self.$set(self.detailContent[index], 'value', value);
},
moveUp(item, index) {
var self = this;
if (index == 0) return;
var dataArr = JSON.parse(JSON.stringify(self.detailContent));
self.swapArrayElements(dataArr, index, index - 1)
self.detailContent = dataArr;
},
topUp(item, index) {
var self = this;
var dataArr = JSON.parse(JSON.stringify(self.detailContent));
dataArr.splice(index, 1);
dataArr.unshift(item);
self.detailContent = dataArr;
},
moveDown(item, index) {
var self = this;
if (index == self.detailContent.length - 1) return;
var dataArr = JSON.parse(JSON.stringify(self.detailContent));
self.swapArrayElements(dataArr, index, index + 1)
self.detailContent = dataArr;
},
deleteType(item, index) {
var self = this;
self.detailContent.splice(index, 1);
},
swapArrayElements(arr, indexA, indexB) {
const temp = arr[indexA];
arr[indexA] = arr[indexB];
arr[indexB] = temp;
},
}
}
</script>
<style>
.itemBox {
/* padding: 0 20rpx; */
}
.itemTit {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0 30rpx;
}
.titlL {
flex: 1;
}
.titBtn {
border: 1px solid #e1e1e1;
width: 80rpx;
text-align: center;
border-radius: 5rpx;
margin-left: 10rpx;
font-size: 24rpx;
line-height: 40rpx;
color: #333;
}
.btnNo {
color: #777;
border: 1px solid #f5f5f5;
}
.txt {
width: 100%;
height: 200rpx;
}
.smallBox {
display: flex;
flex-wrap: wrap;
}
.smallImg {
width: 192rpx;
height: 192rpx;
border-radius: 10rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
overflow: hidden;
position: relative;
}
.smallImg image {
width: 100%;
height: 100%;
}
.smallImg:nth-child(3n) {
margin-right: 0;
}
.imgDel {
width: 100%;
background-color: rgba(0, 0, 0, 0.4);
color: #fff;
text-align: center;
line-height: 40rpx;
position: absolute;
bottom: 0;
font-size: 22rpx;
}
.smallBtn {
width: 192rpx;
height: 192rpx;
border-radius: 10rpx;
border: 1px dashed #e1e1e1;
color: #888;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.bigImg,
.video {
width: 100%;
height: 380rpx;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
</style>
引用页面代码
- vue的子组件引用都是一样的
<template>
<detail-edit :modelValue="content" @updateContent="updateContent"></detail-edit>
</template>
<script>
const detailEdit = defineAsyncComponent(() => import("@/components/detailEdit")); //import中写子组件放的文件路径
export default {
name: "test",
components: {
detailEdit
},
data() {
return {
content: []
}
},
mounted() {
},
methods: {
updateContent(val){
console.log('测试-modelValue', val)
}
}
}
</script>
同时,在uniapp的插件市场也有发布uniapp的叠楼插件
叠楼样式 上移 下移 置顶 添加 删除 - DCloud 插件市场
同时也贴个样式图吧!
果有任何的问题请指正!!都可以改的,一起进步!
大家有更好的方法也可以分享!