做项目时候活动详情页面遇到的思考总结1。

首先这个整体在活动详情这个页面显示。这个页面是这个项目得亮点也是难点,

先是pageleft拖拽组件,获得组件数据component改变拖拽状态dragactive,然后在预览页面pagereview中添加到预览位置(先获得拖动鼠标距离预览区top距离,进行鼠标pageY+滚动轴的距离-固定头部位置,会得到this.$refs.pageView.scrollTop + event.pageY - viewWrapTop。接着鼠标在该组件上部就会预览位置添加到上部,下部就是同理,所以要获取(当前组件高度)/2,当遍历state中h5组件高度,前组件高度就用后一个-前一个即可,如果大于一半,那么就把当前索引+1,赋值给addinddex,向H5页面发送预添加组件,如果拖拽组件离开h5,那么就add index=null,然后删除预览位置,拖拽结束,判断addindex是否存在,存在就传递参数调用方法,生成组件,向h5发送更新数据(pagedata)。结束后会更新pagedata

所有要在h5改变的都用到了跨源通信,都要在crs中改变才行。

这是拖拽结束

页面渲染的时候会发送请求获取页面数据,而且还要给crs发送当前这个数据,让crs做更改,然后更新页面数据pagedata和给h5把当前页面数据发送。

  created() {
    this.init()
  },
  methods: {
    init() {
      this.getData()
    },
    getData() {
      let pageId = this.$route.query.id
      if (pageId) {
        getCmsPageById(pageId).then(({data}) => {
          this.postDataToH5(data)
        })
      }
    },
    postDataToH5(data) {
      if (data && data.componentList) {
        data.componentList.forEach((item) => {
          if (item.data.validTime && typeof item.data.validTime === 'string') {
            item.data.validTime = JSON.parse(item.data.validTime)
          }
          // 获取的组件数据无id则手动设置随机id
          if (!item.id) {
            const id = createRandomId()
            item.id = item.data.component + '-' + id
          }
        })
        this.$store.commit('UPDATE_COMPONENT', {data})
        this.$store.commit('VIEW_UPDATE')
      }
    },

难点

1.拖拽得实现和拖拽预览位置得确定。

2.拖拽之后在crs中展示 这里使用了跨源通信。

3.后端传递数据,利用vuex管理组件得数据和页面数据。

4.crs内嵌cms,用了iframe

总体就是通过左侧对组件得拖拽实现在crs中得页面组件得渲染和预览展示,中间使用了Window.postMessage()方法进行跨源通信。(组件是沟通两个系统的桥梁

先说这个页面整体布局吧 

左侧:折叠面板-组件列表  

中间 :crs-h5预览 通过iframe内嵌的(知道端口号。

右侧:编辑区域

<div class="home">
    <!-- 搭建框架 -->
    <PageHead />
    <PageLeft />
    <PageView />
    <PageRight />

    <!-- 组件的公共配置内容 -->
    <!-- 上传图片组件 -->
    <UpLoadImg :dialog-image-visible.sync="dialogImageVisible" @upLoadImgSuccess="upLoadImgSuccess" />
  </div>

首先看后端返回得数据。对组件的修改就是对组件jason对象得修改!

返回的数据主要就是有

图片广告、图文导航、公告、魔方、文本

1.组件的名字、这个组件下得组件名字

2.组件有效时间 因为有秒杀时间

3.图片列表

4.组件得样式,字体大小,背景色,组件里文本内容、行高、行距

const componentlist = [

  {

    title: '基础组件',

    components: [....]

}

{
        name: '图片广告',
        iconClass: 'cms-icon-ad',
        maxNumForAdd: DEF_MAX_NUM,
        data: {
          component: 'Carousel',
          //秒杀组件 有效时间
          validTime: [],
          layout: 'swiper',
          imageList: [
            {
              link: null,
              imageUrl: '',
              text: '导航1'
            }
          ],
          //图片得详情样式
          imageMargin: 0,
          isDefaultMargin: 0,
          marginSize: [0, 0],
          isBorderRadius: 0,
          radius: 0,
          backgroundColor: '',
          piclist: []
          // imageRadius: 'square',
          // imageStyle: 'normal',
          // imageMargin: 0,
          // pageMargin: 0
        }
      /* 
      * component 组件类型标识
      * validTime 组件生效时间 
      * layout 组件布局方式
      * text 浮层标题名称
      * backgroundColor 背景颜色
      * textColor 字体颜色
      * columnPadding 上下边距
      * rowPadding 左右边距
      * lineNumber 单行个数
      * borderRadius 圆角大小
      * imageList 图片列表
      */ 
 {
        name: '图文导航',
        maxNumForAdd: DEF_MAX_NUM,
        iconClass: 'cms-icon-nav',
        data: {
          component: 'ImageNav',
          validTime: [],
          layout: 'pic',
          text: '',
          backgroundColor: '#FFFFFF',
          textColor: '#323233',
          columnPadding: 20,
          rowPadding: 20,
          lineNumber: 4,
          borderRadius: 0,
          imageList: [

以上是组件得数据。

接下来就先写左侧pageLeft,整个是一个折叠面板,遍历数据嘛,联合vuex了,看store里得数据。

state: {
    // 当前设置内容 1-页面;2-组件
    setType: 1,
    // 上传图片弹出框组件是否显示
    dialogImageVisible: false,
    // 上传图片成功后的回调事件
    upLoadImgSuccess: null,
    // 当前搭建的页面数据,保存时也是保存该对象
    pageData: JSON.parse(JSON.stringify(emptyPageData)),
    // 当前选中的组件id,根据该id显示对应的编辑组件及预览时添加选中状态
    activeComponentId: null,
    // 当前是否正在拖动组件到页面
    dragActive: false,
    // 当前正在拖动的组件对象
    dragComponent: {},
    // 拖动左侧组件时当前要添加到的索引位置
    addComponentIndex: null,
    // 搭建模块跨源通信 - H5页面高度
    previewHeight: '',
    // 搭建模块跨源通信 - H5页面各组件top值, 计算拖拽时组件要拖动到的位置
    componentsTopList: '',
    wxParams: JSON.stringify({isLogin: true}) // h5页面参数
  },

主要得也就是:

1.当前页面的数据pageData

2.是否正在拖动组件 dragActive

3.正在拖动得组件 dragComponent

4.拖动组件要添加到中间得索引位置 addComponentIndex 

5.h5页面的两个属性:h5页面高度+h5各组件得top值。

这里我们采用 HTML5 拖拽特性,为 DOM 节点添加 draggable 属性后即可自由拖动:
 

<ul class="component-list">
          <li
            v-for="(component,size) in item.components"
            :key="size"
//判断组件是否被选中
            :class="draggableEnable(component) ? 'drag-enabled' : 'drag-disabled'"
//判断组件是否可拖拽
            :draggable="draggableEnable(component)"
	//拖拽时候 逻辑通信

            @dragstart="onDragstart(component, $event)"
	//拖拽之后
            @dragend="onDragend($event)"
          >

遍历组件,动态判断组件是否可以拖拽。

    draggableEnable(component) {
      let curNum = this.componentMap[component.data.component] || 0
      return curNum < component.maxNumForAdd
    }

能否被点击就是看页面上使用的组件超没超过组件的最大值,怎么获取这个使用组件得值呢?在getter中写了一个方法,记录组件使用的值,map,通过查询键就可以获得。

  pageComponentTotalMap: (state) => {
    let map = {}
    let cList = state.pageData.componentList || []
    let cName
    cList.forEach((c) => {
      cName = c.data.component
      if (map[cName]) {
        map[cName] += 1
      } else {
        map[cName] = 1
      }
    })
    return map
  }

整个页面得数据在state得pagedata中。首先初始化pagedata,后续每次操作都会在mutation中改变pagedata中的数据。!!

初始化pageData。

const emptyPageData = {

  id: '',

  name: '页面标题',

  shareDesc: '', // 微信分享文案

  shareImage: '', // 微信分享图片

  backgroundColor: '', // 页面背景颜色

  backgroundImage: '', // 页面背景图片

  backgroundPosition: 'top', // 页面背景位置

  cover: '',

  componentList: []

}

左侧组件列表拖拽实现,

拖拽现在左侧开始,然后拖拽到预览页面,离开预览页面,拖拽结束,拖拽结束根据是否有添加组件的索引值判断是否添加,是这个顺序。

拖拽开始得时候、拖拽之后的方法:

拖拽开始,拖拽的状态设置成不可拖拽state.dragActive = value,把这个组件传递过去state.dragComponent = value。

拖拽时:

    onDragstart(component, event) {
      console.log('开始拖动组件', component, event)
      this.SET_DRAG_STATE(true)
      this.SET_DRAG_COMPONENT(JSON.parse(JSON.stringify(component)))
    },

拖拽结束: 

是否有添加索引(因为在组件离开预览位置置index=null,

    onDragend(event) {
      this.SET_DRAG_STATE(false)
      let addIndex = this.addComponentIndex
      if (addIndex != null) {
        console.log('生成组件')
        this.pageChange({
          type: 'add',
          index: addIndex,
          data: this.dragComponent
        })
        this.SET_DRAG_INDEX(null)
        console.log(addIndex, 'addIndex')
        this.VIEW_SET_ACTIVE(addIndex)
      }
    },

pagechange 根据传递参数,选择调用方法,向h5发送数据。

    pageChange({ commit }, changeValue) {
      console.log(changeValue, 'changeValue')
      const commitObj = {
        add: 'ADD_COMPONENT', // 新增组件
        delete: 'DELETE_COMPONENT', // 删除组件
        edit: 'EDIT_COMPONENT', // 编辑组件
        update: 'UPDATE_COMPONENT' // 更新页面
      }
      commitObj[changeValue.type] && commit(commitObj[changeValue.type], changeValue)
      // 向H5页面发送更改后的数据
      commit('VIEW_UPDATE')
    },

 

中间crs-h5拖拽开始、结束的方法:

左侧组件拖动到页面预览区域事件,

记录组件要添加的位置,预览位置。下面方法是添加在预添加位置上的

      <div v-if="dragActive" class="preview-drag-mask" @dragover="onDragover($event)" />

      <div v-if="dragActive" class="preview-drag-out" @dragover="onDragout($event)" />

获取拖拽的预览位置这里可以说是难点。首先获得鼠标当前位置,event.pageY,再减去头部固定的位置,这时候可能h5长度过大有了滚动轴,所以再加上滚动轴:

this.$refs.pageView.scrollTop + event.pageY - viewWrapTop

然后再获取拖拽组件要添加的位置的索引,遍历h5组件,

    // 左侧组件拖动到页面预览区域事件
    onDragover(event) {
      event.preventDefault() // 设置当前区域可以被进入
      const viewWrapTop = 191 // 页面预览区域距离顶部top
      // 拖动距离预览区top距离
      let dropTop = this.$refs.pageView.scrollTop + event.pageY - viewWrapTop
      console.log(dropTop, 'dropTop')
      // 获取当前拖动组件要添加位置的索引
      let addIndex = 0
      for (let i = this.componentsTopList.length - 1; i >= 0; i--) {
        const value = this.componentsTopList[i]
        const prev = this.componentsTopList[i - 1] || 0
        const _half = (value - prev) / 2 // 当前组件高度的一半
        if (i === 0 && dropTop <= _half) break
        if (dropTop > (value - _half)) {
          addIndex = i + 1
          break
        }
      }
      // 预览区域生成预添加组件
      if (this.addComponentIndex === addIndex) return
      // console.log('预添加组件,索引值为', this.componentsTopList, addIndex, dropTop)
      this.SET_DRAG_INDEX(addIndex)
      this.VIEW_ADD_PREVIEW(addIndex)
    },

拖拽组件离开h5

设置添加位置索引为null,向H5页面发送删除预添加组件。

内嵌crs

<iframe id="previewIframe"
                class="preview-iframe"
                :src="previewSrc"
                title="频道名称"
需不需要边框0不需要1需要
                frameborder="0"
                allowfullscreen
                width="100%"
                :height="previewHeight"
                @load="onloadH5"
        />

 previewSrc() {
      return settings.decorateViewSrc + `?pageId=${this.$route.query.id || ''}&noLogin=true`
    }

 跨源通信

Window.postMessage()方法 跨源通信
otherWindow.postMessage(message, targetOrigin, [transfer]);

/**
 * @name: 父级窗口搭建页面与H5预览页面跨源通信函数
 * @param {Window} win  要接收消息的目标窗口
 * @param {String} targetOrigin  指定win中可以接收到消息的origin
 */
export class Messager {
  // 构造实例函数
  constructor(targetOrigin) {
    this.targetOrigin = targetOrigin
    this.actions = {}
    // 监听传送数据的实例函数
    this.messageListener = (event) => {
      // 判断接收的消息是否是指定的域发送、是否有监听指定的事件,符合则执行监听事件
      const type = event.data && event.data.type
      if (event.origin === this.targetOrigin && type && this.actions[type]) {
        this.actions[type](event.data.value)
      }
    }
    window.addEventListener('message', this.messageListener)
  }
//监听函数
  on(type, cb) {
    this.actions[type] = cb
    return this
  }
  /**
   * @name: 发送指定名称的消息
   * @param {String} type   发送的消息名称
   * @param {Object} value  发送的数据
   */
//发送函数
  emit(type, value) {
    var win = document.getElementById('previewIframe').contentWindow
	//发送消息
    win.postMessage({
      type, value
    }, this.targetOrigin)
    return this
  }
  // 移除消息的监听
  destroy() {
    window.removeEventListener('message', this.messageListener)
  }
}

挂载pageview页面的时候,初始化数据

    // 跨源通信对象H5数据的监听
    initMessage({ commit }) {
      // 监听H5预览页面高度变化
      messager.on('pageHeightChange', data => {
        console.log('从H5更新组件高度为', data)
        let height = data.height ? data.height + 72 : 768
        let list = data.componentsTopList || []
        commit('UPDATE_PAGE_HEIGHT', { height, list })
      })
      // 监听H5预览页面数据变化
      messager.on('pageChange', data => {
        console.log('从H5更新组件数据为', data)
        commit('UPDATE_COMPONENT', { data })
      })
      // 监听H5预览页面选中项id变化
      messager.on('setActive', id => {
        commit('SET_ACTIVE_ID', id)
        commit('SET_SETTYPE', 2)
      })
    }
  },

})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值