最近花时间弄了一个动态配置小程序页面的功能css
主要分配置端和渲染端(参考萤火商城后台配置小程序)vue
1.先设计出页面有哪些展现类组件git
2.设计组件样式小程序
3.抽取可以动态变化的配置参数bash
4.开发页面组件把每一个动能组件分类编写样式独立文件 方便后期维护(目前我这里是写在一块儿的)async
5.小程序端 相应的写出对应的组件参数配置模式防止组件不能渲染ide
icon 使用的是阿里矢量图工具
https://www.iconfont.cn/
复制代码
1.整理组件列表post
2.配置点击组件默认数据flex
<template>
<div class="my-menu">
<div class="menu-title">
<span>组件库</span>
</div>
<div class="navs">
<div class="navs-group">
<template v-for="g in navs">
<div class="title" :key="g.type">
{{g.title}}
</div>
<div class="navs-components am-cf" :key="g.title">
<div class="special" @click="addcomponent(item)" v-for="item in g.item" :key="item.type">
<p class="item-icon">
<i class="iconfont" :class="[item.icon]" style="font-size: 1.8rem;"></i>
</p>
<p>
{{item.name}}
</p>
</div>
</div>
</template>
</div>
</div>
<div class="action">
<el-button type="primary" @click="save">保存页面</el-button>
</div>
</div>
</template>
<script>
export default {
name: '',
data() {
return {
navs: [
{
title: '媒体组件',
type: 'mt',
item: [
{
type: 'banner',
icon: 'icontupianlunbo',
name: '图片轮播',
},
{
type: 'imageSingle',
icon: 'icontupian',
name: '单组图片',
},
{
type: 'video',
icon: 'iconshipin',
name: '视频组',
},
{
type: 'article',
icon: 'iconwenzhang',
name: '文章组',
},
{
type: 'special',
icon: 'icontoutiao',
name: '头条快报',
},
],
},
{
title: '业务组件',
type: 'yw',
item: [
{
type: 'search',
icon: 'iconsearch',
name: '搜索框',
},
{
type: 'notice',
icon: 'icondvt-notice',
name: '公告组',
},
{
type: 'navBar',
icon: 'icondaohangzu',
name: '导航组',
},
],
},
{
title: '工具组件',
type: 'gj',
item: [
{
type: 'service',
icon: 'iconzaixiankefu',
name: '在线客服',
},
{
type: 'officialAccount',
icon: 'iconguanzhugongzhonghao',
name: '关注公众号',
},
{
type: 'richText',
icon: 'iconfuwenben',
name: '富文本',
},
{
type: 'blank',
icon: 'iconkongbai',
name: '辅助空白',
},
{
type: 'guide',
icon: 'iconfengexian',
name: '辅助线',
},
],
},
],
defaultimgurl: '',
defaultimgurl1: '',
defaultserviceurl: '',
defaultnoticeurl: '',
}
},
components: {},
created() {},
methods: {
async save() {
this.$emit('save')
},
addcomponent(val) {
let item = {}
if (val.type == 'banner') {
item = {
name: val.name,
type: 'banner',
style: {
height: 200,
backgroundTpye: 'all',
background: '',
},
params: {
interval: 2800,
},
data: [
{
linkUrl: '',
imgUrl: this.defaultimgurl,
},
{
linkUrl: '',
imgUrl: this.defaultimgurl,
},
],
}
}
if (val.type == 'imageSingle') {
item = {
name: val.name,
type: 'imageSingle',
style: {
paddingTop: 0,
paddingLeft: 0,
background: '#ffffff',
height: 200,
},
data: [
{
linkUrl: '',
imgUrl: this.defaultimgurl,
},
{
linkUrl: '',
imgUrl: this.defaultimgurl,
},
],
}
}
if (val.type == 'video') {
item = {
name: val.name,
type: 'video',
style: {
paddingTop: 0,
height: 200,
},
params: {
videoUrl: '',
poster: this.defaultimgurl,
autoplay: '0',
},
}
}
if (val.type == 'navBar') {
item = {
name: val.name,
type: val.type,
style: {
background: '',
rowsNum: 4,
marginLeft: 0,
paddingTop: 0,
},
params: {
title: '标题内容',
visible: 1,
color: '#666666',
textAlign: 'center',
},
data: [
{
imgUrl: this.defaultimgurl,
linkUrl: '',
text: '按钮文字1',
color: '#666666',
},
{
imgUrl: this.defaultimgurl,
linkUrl: '',
text: '按钮文字2',
color: '#666666',
},
{
imgUrl: this.defaultimgurl,
linkUrl: '',
text: '按钮文字3',
color: '#666666',
},
{
imgUrl: this.defaultimgurl,
linkUrl: '',
text: '按钮文字4',
color: '#666666',
},
],
}
}
if (val.type == 'article') {
item = {
name: '文章组',
type: 'article',
params: {
source: 'auto',
auto: {
category: 0,
showNum: 6,
},
},
style: [],
defaultData: [
{
article_title: '此处显示文章标题',
article_cont: '这是文章的内容',
show_type: 10,
image: this.defaultimgurl,
date: '2020-05-10 12:00',
},
{
article_title: '此处显示文章标题',
show_type: 10,
article_cont: '这是文章的内容',
image: this.defaultimgurl,
date: '2020-05-10 12:00',
},
],
data: [],
}
}
if (val.type == 'special') {
item = {
name: '头条快报',
type: 'special',
params: {
source: 'auto',
auto: {
category: 0,
showNum: 6,
},
},
style: {
display: '1',
image: this.defaultimgurl1,
},
defaultData: [
{
article_title: '头条内容。。。',
},
{
article_title: '头条内容。。。',
},
],
data: [],
}
}
if (val.type == 'blank') {
item = {
name: '辅助空白',
type: 'blank',
style: {
height: 20,
background: '#ffffff',
},
}
}
if (val.type == 'guide') {
item = {
name: '辅助线',
type: 'guide',
style: {
background: '#ffffff',
lineStyle: 'solid',
lineHeight: 1,
lineColor: '#000000',
paddingTop: 10,
},
}
}
if (val.type == 'service') {
item = {
name: '在线客服',
type: 'service',
params: {
type: 'chat',
image: this.defaultserviceurl,
phone_num: '',
},
style: {
right: 1,
bottom: 10,
opacity: 100,
},
}
}
if (val.type == 'notice') {
item = {
name: '公告组',
type: 'notice',
params: {
text: '这里是第一条自定义公告的标题',
icon: this.defaultnoticeurl,
},
style: {
paddingTop: '4',
background: '#ffffff',
textColor: '#000000',
},
}
}
if (val.type == 'search') {
item = {
name: '搜索框',
type: 'search',
params: {
placeholder: '请输入关键字进行搜索',
},
style: {
textAlign: 'left',
searchStyle: 'square',
},
}
}
if (val.type == 'richText') {
item = {
name: '富文本',
type: 'richText',
params: {
content: '<p>这里是文本的内容</p>',
},
style: {
paddingTop: '0',
paddingLeft: '0',
background: '#ffffff',
},
}
}
this.$emit('add', item)
},
},
}
</script>
<style lang="scss" scoped>
.my-menu {
width: 285px;
height: auto;
background: #fdfdfd;
border: 1px solid #ddd;
padding: 15px 10px;
transition: all 0.3s;
user-select: none;
.menu-title {
position: relative;
padding: 0 22px;
height: 30px;
border-bottom: 1px solid #eef1f5;
line-height: 30px;
&:before {
content: '';
position: absolute;
width: 4px;
height: 13px;
background: #00aeff;
top: 8px;
left: 9px;
}
}
.navs {
padding: 10px 5px;
border-bottom: 1px dotted #ddd;
position: relative;
display: flex;
.navs-group {
.title {
// font-size: 1.24rem;
color: #999;
margin: 10px 0;
}
.navs-components {
.special {
width: 74px;
float: left;
padding: 3px 0;
margin: 5px;
border: 1px solid #dddddd;
text-align: center;
font-size: 12px;
cursor: pointer;
transition: All 0.3s ease-in-out;
background: #f4f4f4;
p {
color: #424242;
}
}
}
}
}
.action {
margin-top: 15px;
text-align: right;
position: relative;
}
}
.am-cf:after {
clear: both;
}
.am-cf:after,
.am-cf:before {
content: ' ';
display: table;
}
</style>
复制代码
1.根据默认参数渲染组件
<template>
<div class="my-phone">
<div class="phone-top optional" :style="{backgroundColor: pages.page.style.titleBackgroundColor}" @click="changitem(pages.page,-1)" :class="{'selected':selectitem.type=='page'}">
<h4 :style="{color: pages.page.style.titleTextColor}">{{pages.page.params.title}}</h4>
</div>
<div class="phone-main">
<div class="dragArea" :style="{background:pages.page.style.pageBackgroundColor}">
<draggable v-model="pages.items" @start="dragstart" @end="dragend">
<template v-for="(item,index) in pages.items">
<div v-if="item.type!='service'" :key="index" @click="changitem(item,index)" class="drag optional" :class="{selected:selectindex==index}">
<div v-if="item.type=='banner'" class="my-banner" :style="{paddingBottom:item.style.paddingTop+'px',backgroundColor:item.style.background}">
<img v-for="(img,imgindex) in item.data" :key="'banner'+imgindex" :src="img.imgUrl" :style="{padding:item.style.paddingTop+'px '+item.style.paddingLeft+'px 0px',}">
</div>
<div v-if="item.type=='imageSingle'" class="my-imageSingle" :style="{paddingBottom:item.style.paddingTop+'px',background:item.style.background}">
<div class="item-image" v-for="(img,imgindex) in item.data" :key="'imageSingle'+imgindex" :style="{padding:item.style.paddingTop+'px '+item.style.paddingLeft+'px 0px',}">
<img :src="img.imgUrl">
</div>
</div>
<div v-if="item.type=='video'" class="my-video" :style="{padding:item.style.paddingTop+'px 0px',}">
<video :src="item.params.videoUrl" :poster="item.params.poster" controls="controls" :style="{height:item.style.height+'px'}"></video>
</div>
<div v-if="item.type=='navBar'" class="my-navBar" :style="{background:item.style.background,margin:'0px '+item.style.marginLeft+'px'}" style="border-radius: 3px;">
<p class="item-text am-text-truncate" v-if="item.params.visible" :style="{color:item.params.color,textAlign:item.params.textAlign}">
{{item.params.title}}
</p>
<ul :class="['am-avg-sm-'+item.style.rowsNum]">
<li v-for="(item,index) in item.data" :key="'navBar'+index" class="my-navBar-li">
<div class="item-image">
<img :src="item.imgUrl">
</div>
<p class="item-text am-text-truncate" :style="{color:item.color}">
{{item.text}}
</p>
</li>
</ul>
</div>
<div v-if="item.type=='article'" class="my-article">
<div class="article-item" v-for="(item,index) in item.defaultData" :key="index">
<div class="article-item__image">
<img :src="item.image">
</div>
<div class="article-item__right">
<div class="article-item__title twolist-hidden">
{{item.article_title}}
</div>
<div class="article-item__coont">
{{item.article_cont}}
</div>
<div class="article-item__footer">
{{item.date}}
</div>
</div>
</div>
</div>
<div v-if="item.type=='special'" class="my-special">
</div>
<div v-if="item.type=='search'" class="my-search">
</div>
<div v-if="item.type=='notice'" class="my-notice" :style="{padding:item.style.paddingTop+'px 10px',background:item.style.background}">
<div class="notice__icon">
<img :src="item.params.icon">
</div>
<div class="notice__text">
<span :style="{color:item.style.textColor}">{{item.params.text}}</span>
</div>
</div>
<div v-if="item.type=='guide'" class="my-guide" :style="{padding:item.style.paddingTop+'px 0px',background:item.style.background}">
<p class="line" :style="{borderTop:item.style.lineHeight+'px '+item.style.lineStyle+' '+item.style.lineColor}"></p>
</div>
<div v-if="item.type=='blank'" class="my-blank" :style="{height:item.style.height+'px',background:item.style.background}">
</div>
<div class="btn-edit-del" v-if="item.type!='service'">
<div class="btn-edit-del" @click.stop="delitem(item,index)">
删除
</div>
</div>
</div>
<div v-if="item.type=='service'" @click="changitem(item,index)" class="my-service drag optional " :class="{selected:selectindex==index}" :style="{right:item.style.right+'%',bottom:item.style.bottom+'%',opacity:item.style.opacity/100}" :key="index">
<div class="service-icon">
<img :src="item.params.image">
</div>
<div class="btn-edit-del">
<div class="btn-edit-del" @click.stop="delitem(item,index)">
删除
</div>
</div>
</div>
</template>
</draggable>
</div>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: '',
model: {
prop: 'pages',
},
props: {
pages: {
type: [Array, Object],
default() {
return {
page: {
type: 'page',
name: '页面设置',
params: {
title: '',
share_title: '',
},
style: {
titleTextColor: '',
titleBackgroundColor: '',
pageBackgroundColor: '',
},
},
items: [],
}
},
},
},
data() {
return {
selected: '',
selectindex: -1,
selectitem: {
type: '',
},
}
},
components: {
draggable,
},
created() {},
methods: {
//选中模块
changitem(val, index) {
this.selectitem = val
this.selectindex = index
this.chang()
},
dragstart(evt) {
this.selectindex = evt.oldIndex
this.chang()
},
dragend(evt) {
this.selectindex = evt.newIndex
this.chang()
},
delitem(item, index) {
this.$confirm('确认删除吗?', '提示', {
confirmButtonText: '肯定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
this.pages.items.splice(index, 1)
this.selected = ''
this.$emit('modulechang', { type: '' })
})
.catch(() => {})
},
chang() {
this.$emit('modulechang', this.selectitem)
},
},
}
</script>
<style lang="scss" scoped>
.my-phone {
width: 377px;
border-radius: 3px;
box-shadow: 0 3px 10px #dcdcdc;
border: 1px solid #ddd;
.phone-top {
width: 100%;
height: 66px;
text-align: center;
background: url('../../assets/phone-top-black.png') center center / contain
no-repeat;
h4 {
margin: 0;
font-size: 1.4rem;
font-weight: normal;
white-space: nowrap;
line-height: 88px;
}
}
.optional {
position: relative;
&:before {
content: ' ';
}
}
.optional:hover:before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 2px dashed #00a0e9;
cursor: move;
}
.optional.selected:before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 2px dashed #00a0e9;
cursor: move;
}
.phone-main {
position: relative;
border-top: 0;
user-select: none;
line-height: normal;
.my-service {
position: absolute;
z-index: 999;
.service-icon {
padding: 5px;
img {
display: block;
width: 45px;
height: 45px;
}
}
}
.dragArea {
overflow-y: auto;
height: 580px;
.drag {
&:hover {
.btn-edit-del {
display: block;
}
}
.btn-edit-del {
height: 16px;
position: absolute;
right: 2px;
bottom: 2px;
display: none;
z-index: 90;
> div {
width: 32px;
height: 16px;
line-height: 16px;
display: inline-block;
text-align: center;
font-size: 10px;
color: #fff;
background: rgba(0, 0, 0, 0.4);
margin-left: 2px;
cursor: pointer;
position: relative;
z-index: 11;
}
}
.my-banner {
display: block;
margin: 0;
padding: 0;
height: auto;
overflow: hidden;
img {
width: 100%;
display: none;
}
img:first-child {
display: block;
}
}
.my-imageSingle {
display: block;
margin: 0;
padding: 0;
height: auto;
overflow: hidden;
img {
width: 100%;
display: block;
}
}
.my-video {
video {
display: block;
width: 100%;
height: 100%;
}
}
.my-article {
background: #f7f7f7;
.article-item {
display: flex;
margin-bottom: 10px;
padding: 15px;
background: #fff;
.article-item__right {
padding-left: 10px;
.article-item__title {
min-height: 30px;
text-overflow: ellipsis;
overflow: hidden;
}
.article-item__coont {
min-height: 30px;
text-overflow: ellipsis;
overflow: hidden;
}
.item__footer {
margin-top: 5px;
}
}
.article-item__image img {
display: block;
width: 120px;
}
}
}
.my-notice {
padding: 4px 10px;
line-height: 26px;
display: flex;
.notice__icon {
font-size: 0;
img {
width: 16px;
height: 16px;
vertical-align: middle;
border: 0;
}
}
.notice__text {
padding-left: 5px;
flex: 1;
word-wrap: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
.my-guide {
.line {
height: 0;
width: 100%;
margin: 0;
}
}
.my-blank {
}
}
.selected {
.btn-edit-del {
display: block;
}
}
.my-navBar {
.my-navBar-li {
margin: 10px 0;
.item-text {
text-align: center;
}
.item-image {
text-align: center;
margin-bottom: 4px;
img {
height: 44px;
width: 44px;
}
}
}
ul,
li {
list-style: none;
padding: 0;
margin: 0;
}
}
}
}
}
.am-ellipsis,
.am-text-truncate {
word-wrap: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.am-avg-sm-3 > li:nth-of-type(3n + 1) {
clear: both;
}
.am-avg-sm-3 > li:nth-of-type(n) {
clear: none;
}
.am-avg-sm-3 > li {
width: 33.33333333%;
}
.am-avg-sm-4 > li:nth-of-type(4n + 1) {
clear: both;
}
.am-avg-sm-4 > li:nth-of-type(n) {
clear: none;
}
.am-avg-sm-4 > li {
width: 25%;
}
.am-avg-sm-5 > li:nth-of-type(5n + 1) {
clear: both;
}
.am-avg-sm-5 > li:nth-of-type(n) {
clear: none;
}
.am-avg-sm-5 > li {
width: 20%;
}
[class*='am-avg-'] > li {
display: block;
height: auto;
float: left;
}
[class*='am-avg-']:after,
[class*='am-avg-']:before {
content: ' ';
display: table;
}
[class*='am-avg-']:after {
clear: both;
}
[class*='am-avg-']:after,
[class*='am-avg-']:before {
content: ' ';
display: table;
}
[class*='am-avg-'] {
display: block;
padding: 0;
margin: 0;
list-style: none;
}
</style>
复制代码
1.配置组件参数
<template>
<div class="my-editor form-horizontal" v-show="data.type">
<!-- 页面设置 -->
<template v-if="data.type=='page'">
<div class="editor-title">
页面设置
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="页面标题">
<el-input v-model="data.params.title"></el-input>
<span class="help-block">小程序端顶部显示的标题</span>
</el-form-item>
<el-form-item label="分享标题">
<el-input v-model="data.params.share_title"></el-input>
<span class="help-block">小程序端转发时显示的标题</span>
</el-form-item>
<el-form-item label="标题栏文字">
<el-radio-group v-model="data.style.titleTextColor">
<el-radio label="black">黑色</el-radio>
<el-radio label="white">白色</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="标题栏背景">
<el-color-picker v-model="data.style.titleBackgroundColor"></el-color-picker>
</el-form-item>
<el-form-item label="页面背景">
<el-color-picker v-model="data.style.pageBackgroundColor"></el-color-picker>
</el-form-item>
</el-form>
</template>
<!-- 图片轮播 -->
<template v-if="data.type=='banner'">
<div class="editor-title">
图片轮播
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="切换时间">
<el-input-number v-model="data.params.interval" controls-position="right" :min="1000" style="width:100%"></el-input-number>
<span class="help-block">轮播图自动切换的间隔时间,单位:毫秒</span>
</el-form-item>
<el-form-item label="上下边距">
<el-slider v-model="data.style.paddingTop" :min="0" :max="50"></el-slider>{{data.style.paddingTop}}px(像素)
</el-form-item>
<el-form-item label="左右边距">
<el-slider v-model="data.style.paddingLeft" :min="0" :max="50"></el-slider>{{data.style.paddingLeft}}px(像素)
</el-form-item>
<!-- <el-form-item label="背景颜色模式">
<el-radio-group v-model="data.style.backgroundTpye">
<el-radio label="all">所有填充</el-radio>
<el-radio label="top">上下</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item label="背景颜色">
<el-color-picker v-model="data.style.background"></el-color-picker>
</el-form-item>
<div class="form-items">
<div class="form-item" v-for="(img,imgindex) in data.data" :key="'banner'+imgindex">
<i class="el-icon-close item-delete" @click.stop="delimgitem(data.data,imgindex)"></i>
<div class="item-inner">
<el-form-item label="图片">
<div class="data-image" @click.stop="showfile(imgindex,data.type)">
<img :src="img.imgUrl">
</div>
<span class="help-block">建议尺寸750x360</span>
</el-form-item>
<el-form-item label="连接地址">
<el-input v-model="img.linkUrl"></el-input>
</el-form-item>
</div>
</div>
</div>
<div class="form-item-add" @click.stop="addimgitem(data.data)">
添加一个
</div>
</el-form>
</template>
<!-- 单组图片 -->
<template v-if="data.type=='imageSingle'">
<div class="editor-title">
单组图片
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="上下边距">
<el-slider v-model="data.style.paddingTop" :min="0" :max="50"></el-slider>{{data.style.paddingTop}}px(像素)
</el-form-item>
<el-form-item label="左右边距">
<el-slider v-model="data.style.paddingLeft" :min="0" :max="50"></el-slider>{{data.style.paddingLeft}}px(像素)
</el-form-item>
<el-form-item label="背景颜色">
<el-color-picker v-model="data.style.background"></el-color-picker>
</el-form-item>
<div class="form-items">
<div class="form-item" v-for="(img,imgindex) in data.data" :key="'banner'+imgindex">
<i class="el-icon-close item-delete" @click.stop="delimgitem(data.data,imgindex)"></i>
<div class="item-inner">
<el-form-item label="图片">
<div class="data-image" @click.stop="showfile(imgindex,data.type)">
<img :src="img.imgUrl">
</div>
</el-form-item>
<el-form-item label="连接地址">
<el-input v-model="img.linkUrl"></el-input>
</el-form-item>
</div>
</div>
</div>
<div class="form-item-add" @click.stop="addimgitem(data.data)">
添加一个
</div>
</el-form>
</template>
<!-- 视频组件 -->
<template v-if="data.type=='video'">
<div class="editor-title">
视频组
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="上下边距">
<el-slider v-model="data.style.paddingTop" :min="0" :max="50"></el-slider>{{data.style.paddingTop}}px(像素)
</el-form-item>
<el-form-item label="视频高度">
<el-slider v-model="data.style.height" :min="0" :max="500"></el-slider>{{data.style.height}}px(像素)
</el-form-item>
<el-form-item label="视频封面">
<div class="data-image" @click.stop="showfile(-1,data.type)">
<img :src="data.params.poster">
</div>
</el-form-item>
<el-form-item label="视频地址">
<el-input v-model="data.params.videoUrl"></el-input>
</el-form-item>
<el-form-item label="自动播放">
<el-radio-group v-model="data.params.autoplay">
<el-radio label="0">否</el-radio>
<el-radio label="1">是</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</template>
<!-- 导航组 -->
<template v-if="data.type=='navBar'">
<div class="editor-title">
导航组
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="是否显示标题">
<el-radio-group v-model="data.params.visible">
<el-radio :label="1">显示</el-radio>
<el-radio :label="0">隐藏</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="data.params.visible">
<el-form-item label="标题文字">
<el-input v-model="data.params.title"></el-input>
</el-form-item>
<el-form-item label="文字对齐">
<el-radio-group v-model="data.params.textAlign">
<el-radio label="left">显居左</el-radio>
<el-radio label="center">居中</el-radio>
<el-radio label="right">居右</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="标题文字验证">
<el-color-picker v-model="data.params.color"></el-color-picker>
</el-form-item>
</template>
<el-form-item label="左右边距">
<el-slider v-model="data.style.marginLeft" :min="0" :max="50"></el-slider>{{data.style.paddingLeft}}px(像素)
</el-form-item>
<el-form-item label="背景颜色">
<el-color-picker v-model="data.style.background"></el-color-picker>
</el-form-item>
<el-form-item label="每行数量">
<el-radio-group v-model="data.style.rowsNum">
<el-radio :label="3">3</el-radio>
<el-radio :label="4">4</el-radio>
<el-radio :label="5">5</el-radio>
</el-radio-group>
</el-form-item>
<div class="form-items">
<draggable v-model="data.data">
<div class="form-item" v-for="(img,imgindex) in data.data" :key="'banner'+imgindex">
<i class="el-icon-close item-delete" @click.stop="delimgitem(data.data,imgindex)"></i>
<div class="item-inner">
<el-form-item label="图片">
<div class="data-image" @click.stop="showfile(imgindex,data.type)">
<img :src="img.imgUrl">
</div>
<span class="help-block">建议尺寸100x100</span>
</el-form-item>
<el-form-item label="文字内容">
<el-input v-model="img.text"></el-input>
</el-form-item>
<el-form-item label="文字颜色">
<el-color-picker v-model="img.color"></el-color-picker>
</el-form-item>
<el-form-item label="业务分类">
<SelectTree :props="bprops" :options="business" :data.sync="img.classify" :clearable="true" :accordion="true" height="200" style="width:100%"></SelectTree>
</el-form-item>
<el-form-item label="连接地址">
<el-input v-model="img.linkUrl"></el-input>
</el-form-item>
</div>
</div>
</draggable>
</div>
<div class="form-item-add" @click.stop="addimgitem(data.data)">
添加一个
</div>
</el-form>
</template>
<!-- 文章组 -->
<template v-if="data.type=='article'">
<div class="editor-title">
文章组
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="文章分类">
<SelectTree :props="props" :options="trredata" :data.sync="data.params.auto.category" :clearable="true" :accordion="true" height="200" style="width:100%"></SelectTree>
</el-form-item>
<el-form-item label="显示数量">
<el-input-number v-model="data.params.auto.showNum" placeholder="请输入显示数量" :min="1" controls-position="right" style="width:100%"></el-input-number>
</el-form-item>
</el-form>
</template>
<!-- 公告组 -->
<template v-if="data.type=='notice'">
<div class="editor-title">
公告组
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="上下边距">
<el-slider v-model="data.style.paddingTop" :min="0" :max="50"></el-slider>{{data.style.paddingTop}}px(像素)
</el-form-item>
<el-form-item label="背景颜色">
<el-color-picker v-model="data.style.background"></el-color-picker>
</el-form-item>
<el-form-item label="文字颜色">
<el-color-picker v-model="data.style.textColor"></el-color-picker>
</el-form-item>
<el-form-item label="公告图标">
<div class="data-image" @click.stop="showfile(-1,data.type)">
<img :src="data.params.icon" style="height: 30px;">
<span class="help-block">建议尺寸32x32</span>
</div>
</el-form-item>
<el-form-item label="公告内容">
<el-input v-model="data.params.text"></el-input>
</el-form-item>
</el-form>
</template>
<!-- 辅助空白 -->
<template v-if="data.type=='blank'">
<div class="editor-title">
辅助空白
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="组件高度">
<el-slider v-model="data.style.height" :min="0" :max="50"></el-slider>{{data.style.height}}px(像素)
</el-form-item>
<el-form-item label="背景颜色">
<el-color-picker v-model="data.style.background"></el-color-picker>
</el-form-item>
</el-form>
</template>
<!-- 辅助线 -->
<template v-if="data.type=='guide'">
<div class="editor-title">
辅助线
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="背景颜色">
<el-color-picker v-model="data.style.background"></el-color-picker>
</el-form-item>
<el-form-item label="线条样式">
<el-radio-group v-model="data.style.lineStyle">
<el-radio label="solid">实线</el-radio>
<el-radio label="dashed">虚线</el-radio>
<el-radio label="dotted">点状</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="线条颜色">
<el-color-picker v-model="data.style.lineColor"></el-color-picker>
</el-form-item>
<el-form-item label="线条高度">
<el-slider v-model="data.style.lineHeight" :min="0" :max="20"></el-slider>{{data.style.lineHeight}}px(像素)
</el-form-item>
<el-form-item label="上下边距">
<el-slider v-model="data.style.paddingTop" :min="0" :max="50"></el-slider>{{data.style.paddingTop}}px(像素)
</el-form-item>
</el-form>
</template>
<!-- 在线客服 -->
<template v-if="data.type=='service'">
<div class="editor-title">
在线客服
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="底边距">
<el-slider v-model="data.style.bottom" :min="0" :max="100"></el-slider>{{data.style.height}}%
</el-form-item>
<el-form-item label="右边距">
<el-slider v-model="data.style.right" :min="0" :max="100"></el-slider>{{data.style.height}}%
</el-form-item>
<el-form-item label="不透明度">
<el-slider v-model="data.style.opacity" :min="0" :max="100"></el-slider>{{data.style.opacity}}%
</el-form-item>
<el-form-item label="线条样式">
<el-radio-group v-model="data.params.type">
<el-radio label="chat">在线聊天</el-radio>
<el-radio label="phone">拨打电话</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="电话号码" v-if="data.params.type=='phone'">
<el-input v-model="data.params.phone_num" placeholder="请输入电话号码" :maxlength='11' type="tel"></el-input>
</el-form-item>
<el-form-item label="客服图标">
<div class="data-image" @click.stop="showfile(-1,data.type)">
<img :src="data.params.image" style="height: 30px;">
<span class="help-block">建议尺寸90x90</span>
</div>
</el-form-item>
</el-form>
</template>
<FileLibrary :visible.sync="FileLibraryvisible" @ok="Fileok"></FileLibrary>
</div>
</template>
<script>
import FileLibrary from '../FileLibrary'
import SelectTree from '../treeSelect'
import draggable from 'vuedraggable'
export default {
name: '',
model: {
prop: 'data',
},
props: {
data: {
type: [Object, Array],
default() {
return {
type: '',
}
},
},
},
data() {
return {
dataindex: -1,
FileLibraryvisible: false,
ntype: '',
trredata: [
{
id: 0,
label: '所有分类',
children: [],
},
],
props: {
// 配置项(必选)
value: 'id',
label: 'label',
children: 'children',
// disabled:true
},
bprops: {
// 配置项(必选)
value: 'id',
label: 'name',
children: 'children',
},
business: [],
}
},
components: {
FileLibrary,
SelectTree,
draggable,
},
created() {
},
methods: {
addimgitem(data) {
data.push({
linkUrl: '',
imgUrl: '',
})
},
delimgitem(data, index) {
data.splice(index, 1)
},
showfile(index, type) {
this.FileLibraryvisible = true
this.dataindex = index
this.ntype = type
},
Fileok(val) {
if (val.length != 0) {
switch (this.ntype) {
case 'banner':
case 'imageSingle':
case 'navBar':
this.data.data[this.dataindex].imgUrl = val[0]
break
case 'video':
this.data.params.poster = val[0]
break
case 'notice':
this.data.params.icon = val[0]
break
case 'service':
this.data.params.image = val[0]
break
}
}
},
},
}
</script>
<style lang="scss" scoped>
.my-editor {
width: 400px;
height: auto;
min-height: 100px;
padding: 15px 10px;
border: 1px solid #ddd;
.data-image {
display: inline-block;
width: auto;
min-width: 40px;
max-width: 220px;
text-align: center;
cursor: pointer;
}
.editor-title {
position: relative;
padding: 0 22px;
height: 34px;
border-bottom: 1px solid #eef1f5;
margin-bottom: 20px;
line-height: 34px;
&:before {
content: '';
position: absolute;
width: 4px;
height: 13px;
background: #00aeff;
top: 10px;
left: 9px;
}
}
.tpl-form-line-form {
font-size: 1.3rem !important;
color: #656565;
}
.help-block {
color: #838fa1;
font-size: 12px;
}
.form-items {
height: auto;
padding: 5px 6px;
.form-item {
background: #f7fafc;
margin-bottom: 0.6rem;
position: relative;
border-radius: 3px;
cursor: move;
.item-delete {
position: absolute;
top: -6px;
right: -6px;
height: 16px;
width: 16px;
line-height: 16px;
background: rgba(153, 153, 153, 0.7);
color: #fff;
border-radius: 50%;
text-align: center;
cursor: pointer;
font-size: 12px;
transition: background-color 0.3s ease-out, border-color 0.3s ease-out;
}
.item-inner {
padding: 10px;
background: #f7fafc;
span {
display: block;
width: 100%;
}
}
}
}
.form-item-add {
width: 100%;
background: #fdfdfd;
color: #6b6b6b;
border: 1px solid #efefef;
outline: none;
padding: 10px 16px;
border-radius: 2px;
font-size: 12px;
line-height: 1;
text-align: center;
vertical-align: middle;
cursor: pointer;
user-select: none;
transition: All 0.2s ease-in-out;
}
.data-image {
display: inline-block;
width: auto;
min-width: 40px;
max-width: 220px;
text-align: center;
cursor: pointer;
img {
display: block;
max-width: 100%;
height: 50px;
}
}
}
</style>
复制代码
<template>
<el-row :gutter="20">
<el-col :span="8">
<LeftPage @save="save" @add="addcomponent"></LeftPage>
</el-col>
<el-col :span="8">
<MainPage :pages="pages" @modulechang="modulechang"></MainPage>
</el-col>
<el-col :span="8">
<RightPage :data="selectitem"></RightPage>
</el-col>
</el-row>
</template>
<script>
import LeftPage from './left'
import MainPage from './main'
import RightPage from './right'
export default {
name: '',
props: {
data: {
type: [Object],
default() {
return {}
},
},
},
data() {
return {
pages: this.data,
selected: '',
selectindex: -1,
selectitem: {
type: '',
},
}
},
components: {
LeftPage,
MainPage,
RightPage,
},
created() {},
watch: {
'data.page.type'() {
this.pages = this.data
},
},
methods: {
modulechang(val) {
this.selectitem = val
},
addcomponent(item) {
this.pages.items.push(item)
},
save() {
this.$emit('save', this.pages)
},
},
}
</script>
<style lang="scss" scoped>
</style>
复制代码
下期会分享动态配置表单配置