vue動態配置小程序頁面(一)

最近花時間弄了一個動態配置小程序頁面的功能css

主要分配置端和渲染端(參考螢火商城後臺配置小程序)vue

設計思路

1.先設計出頁面有哪些展現類組件git

2.設計組件樣式小程序

3.抽取可以動態變化的配置參數bash

4.開發頁面組件把每一個動能組件分類編寫樣式獨立文件 方便後期維護(目前我這裏是寫在一塊兒的)async

5.小程序端 相應的寫出對應的組件參數配置模式防止組件不能渲染ide

icon 使用的是阿里矢量圖工具

https://www.iconfont.cn/
複製代碼

1、左邊組件

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>
複製代碼

2、中間組件

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>

複製代碼

3、右邊組件

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>

複製代碼

4、主組件

<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>

複製代碼

下期會分享動態配置表單配置

相關文章
相關標籤/搜索