Skip to content

常用模板

注意:所有代码示例仅供参考,开发者可根据实际业务需求,自行编写、调整代码逻辑来满足自身的业务需求。

1.模板示例

基础组装样式举例,真实使用中可自定义组装信息流样式。

信息流大图信息流上文下图信息流上图下文信息流组图信息流左图右文信息流左文右图

2.实战示例

该示例主旨是为了方便开发者在此基础上二次开发所以开放了源代码,开发者可以根据自己的需要对该示例进行改动或二次开发。 具体源代码和示例可以以DEMO参考:

DEMO点这

2.1 判断逻辑示例(供参考)

2.2 配套示例代码


2.2.1 引入组件示例

html
<import name="feed-composite" src="../template/composite/FeedComposite.ux"></import>
<template>
  <div class="wrapper">
    <!-- 参数说明:https://quickapp-sdk.vivo.com.cn/site/template.html#_2-3-%E5%B1%9E%E6%80%A7%E8%AF%B4%E6%98%8E -->
    <!-- 
      big-pic、
      group-pic、
      left-pic-right-text、
      left-text-right-pic、
      top-pic-bottom-text、
      top-text-bottom-pic
     -->
    <feed-composite
      position-id="27886a42516544039a5bfc75655ae26d"
      media-id="635f3699b4f243aeaf34d7d660df5e85"
      is-render="{{isRender}}"
      template-big-horizontal="top-pic-bottom-text"
      template-big-vertical="big-pic"
      template-small="left-pic-right-text"
      template-group="group-pic"
      @ad-load="onFeedAdLoad"
      @ad-error="onAdError"
      @ad-click="onAdClick"
    ></feed-composite>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isRender: false // 控制广告是否渲染
    }
  },
  onInit() { },
  onFeedAdLoad(evt) {
    // 控制广告展示
    this.isRender = true;
    // 竞价通知根据业务需要发送成功与失败通知

    /**
     * 发送竞价通知 - 相关文档链接:
     * https://quickapp-sdk.vivo.com.cn/site/api.html#_4-1-2%E3%80%81%E5%8F%91%E9%80%81%E7%AB%9E%E4%BB%B7%E9%80%9A%E7%9F%A5
     */
    evt.detail.sendNotice(true, evt.detail.adData.price);
    // # 竞价失败:发送竞价通知
    // evt.detail.sendNotice(false, 7.9, 1);
  },
  onAdClick(evt) {
    console.log("点击事件回调", evt);
  },
  onAdError(data) {
    console.log("报错事件回调", data);
  },
  // 信息流广告滚动重复曝光需要在当前页面的 onPageScroll(){} 中添加 this.$broadcast("vad-page-scroll", evt); 不添加会影响收入
  onPageScroll(evt) {
    // 调用$broadcast()事件完成向下传递
    this.$broadcast('vad-page-scroll', evt);
  },
}
</script>

<style>
.wrapper {
  flex-direction: column;
  align-items: center;
}
</style>

2.2.1 组件的源代码

UX文件:FeedComposite.ux
html
<import name="vad-button" src="vad-sdk/templates/vadButton.ux"></import>
<import name="vad-ad" src="vad-sdk/templates/vadAd.ux"></import>
<import name="vad-compliance" src="vad-sdk/templates/vadCompliance.ux"></import>
<import name="vad-drawer-compliance" src="vad-sdk/templates/vadDrawerCompliance.ux"></import>
<import name="vad-video" src="vad-sdk/templates/vadVideo.ux"></import>
<import name="vad-frame-feed" src="vad-sdk/templates/vadFrameFeed.ux"></import>
<import name="vad-score" src="vad-sdk/templates/vadScore.ux"></import>

<template>
  <div class="{{containerClassName}}">
    <vad-frame-feed
      position-id="{{positionId}}"
      media-id="{{mediaId}}"
      custom-style="{{getCustomStyle}}"
      is-render="{{isRender}}"
      close-position="{{option.closePosition}}"
      @ad-load="onFeedAdLoad"
    >
      <block if="{{renderMode === 'big-pic'}}">
        <div class="show-content">
          <vad-video
            if="{{ adData.materialMode === 'MODE_VIDEO'}}"
            id="vadVideo"
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.videoStyle}}"
          ></vad-video>
          <image
            class="big-pic-style"
            else
            src="{{adData.imageUrls[0]}}"
          ></image>
        </div>
        <div style="height: 125px; align-items: center">
          <vad-ad
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.adStyle}}"
          ></vad-ad>
          <div style="align-items: center">
            <vad-button
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
              custom-style="{{getCustomStyle.buttonStyle}}"
            ></vad-button>
            <div id="close-icon-target"></div>
          </div>
        </div>
        <div style="flex-direction: column">
          <vad-compliance
            position-id="{{adData.positionId}}"
            grid="{{getComplianceGrid}}"
            custom-style="{{getCustomStyle.complianceStyle}}"
            @command-click="onCommandClick"
          ></vad-compliance>
          <vad-score
            if="{{option.isApp}}"
            position-id="{{adData.positionId}}"
          ></vad-score>
        </div>
      </block>
      <block elif="{{renderMode === 'group-pic'}}">
        <div class="show-content">
          <div
            if="{{adData.materialMode === 'MODE_GROUP' && adData.imageUrls}}"
          >
            <div style="border-radius: 40px">
              <image
                class="group-img"
                for="(index, item) in adData.imageUrls"
                src="{{item}}"
                tid="{{index}}"
                style="{{getImgMargin(index)}}"
              ></image>
            </div>
          </div>
          <div style="width: 1000px; height: 245px" else>
            <text>素材资源不符合组图要求,请检查</text>
          </div>
        </div>
        <div style="height: 125px; align-items: center">
          <vad-ad
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.adStyle}}"
          ></vad-ad>
          <div style="align-items: center">
            <vad-button
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
              custom-style="{{getCustomStyle.buttonStyle}}"
            ></vad-button>
          </div>
        </div>
        <div style="flex-direction: column">
          <vad-compliance
            position-id="{{adData.positionId}}"
            grid="{{getComplianceGrid}}"
            custom-style="{{getCustomStyle.complianceStyle}}"
            @command-click="onCommandClick"
          ></vad-compliance>
          <vad-score
            if="{{option.isApp}}"
            position-id="{{adData.positionId}}"
          ></vad-score>
        </div>
      </block>
      <block elif="{{renderMode === 'left-pic-right-text'}}">
        <div>
          <div class="show-content">
            <image class="show-image" src="{{adData.imageUrls[0]}}"></image>
          </div>
          <div style="flex-direction: column; width: 628px; flex: 1">
            <text style="{{getCustomStyle.titleStyle}}">{{
              adData.title
            }}</text>
            <vad-compliance
              position-id="{{adData.positionId}}"
              grid="{{getComplianceGrid}}"
              custom-style="{{getCustomStyle.complianceStyle}}"
              @command-click="onCommandClick"
            ></vad-compliance>
            <vad-score
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
            ></vad-score>
          </div>
        </div>
        <div style="height: 125px; align-items: center">
          <vad-ad
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.adStyle}}"
          ></vad-ad>
          <div style="align-items: center">
            <vad-button
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
              custom-style="{{getCustomStyle.buttonStyle}}"
            ></vad-button>
          </div>
        </div>
      </block>
      <block elif="{{renderMode === 'left-text-right-pic'}}">
        <div style="justify-content: space-between">
          <div style="flex-direction: column; width: 628px; flex: 1">
            <text style="{{getCustomStyle.titleStyle}}">{{
              adData.title
            }}</text>
            <vad-compliance
              position-id="{{adData.positionId}}"
              grid="{{getComplianceGrid}}"
              custom-style="{{getCustomStyle.complianceStyle}}"
              @command-click="onCommandClick"
            ></vad-compliance>
            <vad-score
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
            ></vad-score>
          </div>
          <div class="show-content">
            <image class="show-image" src="{{adData.imageUrls[0]}}"></image>
          </div>
        </div>
        <div style="height: 125px; align-items: center">
          <vad-ad
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.adStyle}}"
          ></vad-ad>
          <div style="align-items: center">
            <vad-button
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
              custom-style="{{getCustomStyle.buttonStyle}}"
            ></vad-button>
          </div>
        </div>
      </block>
      <block elif="{{renderMode === 'top-pic-bottom-text'}}">
        <!-- 上图下文 -->
        <div class="show-content">
          <vad-video
            if="{{ adData.materialMode === 'MODE_VIDEO'}}"
            id="vadVideo"
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.videoStyle}}"
          ></vad-video>
          <image class="show-image" else src="{{adData.imageUrls[0]}}"></image>
        </div>
        <div style="height: 125px; align-items: center">
          <vad-ad
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.adStyle}}"
          ></vad-ad>
          <div style="align-items: center">
            <vad-button
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
              custom-style="{{getCustomStyle.buttonStyle}}"
            ></vad-button>
          </div>
        </div>
        <div style="flex-direction: column">
          <text style="{{getCustomStyle.titleStyle}}">{{ adData.title }}</text>
          <vad-compliance
            position-id="{{adData.positionId}}"
            grid="{{getComplianceGrid}}"
            custom-style="{{getCustomStyle.complianceStyle}}"
            @command-click="onCommandClick"
          ></vad-compliance>
          <vad-score
            if="{{option.isApp}}"
            position-id="{{adData.positionId}}"
          ></vad-score>
        </div>
      </block>
      <block elif="{{renderMode === 'top-text-bottom-pic'}}">
        <div style="flex-direction: column">
          <text style="{{getCustomStyle.titleStyle}}">{{ adData.title }}</text>
          <vad-compliance
            position-id="{{adData.positionId}}"
            grid="{{getComplianceGrid}}"
            custom-style="{{getCustomStyle.complianceStyle}}"
            @command-click="onCommandClick"
          ></vad-compliance>
          <vad-score
            if="{{option.isApp}}"
            position-id="{{adData.positionId}}"
          ></vad-score>
        </div>
        <div style="height: 125px; align-items: center">
          <vad-ad
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.adStyle}}"
          ></vad-ad>
          <div style="align-items: center">
            <vad-button
              if="{{option.isApp}}"
              position-id="{{adData.positionId}}"
              custom-style="{{getCustomStyle.buttonStyle}}"
            ></vad-button>
          </div>
        </div>
        <div class="show-content">
          <vad-video
            if="{{ adData.materialMode === 'MODE_VIDEO'}}"
            id="vadVideo"
            position-id="{{adData.positionId}}"
            custom-style="{{getCustomStyle.videoStyle}}"
          ></vad-video>
          <image else class="show-image" src="{{adData.imageUrls[0]}}"></image>
        </div>
      </block>
      <!--vad-drawer-compliance 放到最低-->
      <vad-drawer-compliance
        visible="{{option.drawerVisible}}"
        position-id="{{adData.positionId}}"
        content-type="{{option.contentType}}"
        @visible-change="onDrawerVisibleChange"
        @tab-active="onTabActive"
      ></vad-drawer-compliance>
    </vad-frame-feed>
  </div>
</template>

<script>
import { styleObj } from "./FeedCompositeConfig.js"
export default {
  props: {
    positionId: {
      type: String,
      required: true
    },
    mediaId: {
      type: String,
      required: true
    },
    isRender: {
      type: Boolean,
      required: true,
      default: false
    },
    templateBigHorizontal: {
      type: String,
      default: "top-pic-bottom-text"
    },
    templateBigVertical: {
      type: String,
      default: "big-pic"
    },
    templateSmall: {
      type: String,
      default: "left-pic-right-text"
    },
    templateGroup: {
      type: String,
      default: "group-pic"
    }
  },
  computed: {
    renderMode() {
      const adDataLength = Object.keys(this.adData).length;
      if (adDataLength > 0) {
        return this.customMode();
      } else {
        return "";
      }
    },
    containerClassName() {
      const className = this.getContainerClassName(this.renderMode);
      return className;
    },
    getComplianceGrid() {
      let grid = styleObj[this.renderMode].grid;
      return grid;
    },
    getCustomStyle() {
      const videoStyle = styleObj[this.renderMode];
      const customStyle = {
        buttonStyle: {},
        complianceStyle: {
          containerStyle: {},
          itemStyle: {
            paddingLeft: 0,
            color: "#999999"
          },
          separatorStyle: {
            display: "none"
          },
          groupStyle: {}
        },
        appNameStyle: {
          color: "#f6642c"
        },
        titleStyle: {
          color: "#000",
          fontSize: "48px",
          fontStyle: "normal",
          fontWeight: 400,
          lineHeight: "60px",
          lines: 2,
          textOverflow: "ellipsis",
        },
        adStyle: {
          containerStyle: {
            borderRadius: "14px",
            backgroundColor: "#dddddd"
          }
        }
      };
      Object.assign(customStyle, videoStyle);
      return customStyle;
    }
  },
  // 页面级组件的数据模型,影响传入数据的覆盖机制:private内定义的属性不允许被覆盖
  data() {
    return {
      option: {
        contentType: '',
        drawerVisible: false,
        closePosition: {
          top: 20,
          right: 20
        },
        isDonwloadApp: false,
        isApp: false,
        materialMode: ''
      },
      adData: {},
      adDataStr: '',
      testShow: false
    }
  },
  getContainerClassName(mode) {
    let className;
    switch (mode) {
      case "big-pic":
        className = "feed-big-pic-container";
        break;
      case "group-pic":
        className = "feed-group-pic-container";
        break;
      case "left-pic-right-text":
        className = "feed-left-pic-right-text-container";
        break;
      case "left-text-right-pic":
        className = "feed-left-text-right-pic-container";
        break;
      case "top-pic-bottom-text":
        className = "feed-top-pic-bottom-text-container";
        break;
      case "top-text-bottom-pic":
        className = "feed-top-text-bottom-pic-container";
        break;
      default:
        className = "";
        break;
    }
    return className;
  },
  /**
   * 此部分为自定义逻辑,将根据返回的素材自动决定展示模板
   * 因为模板素材较多,所以该示例仅展示部分模板
   */
  customMode() {
    /**
     * "MODE_VIDEO" 视频模式
     * "MODE_GROUP" 组图模式
     * "MODE_LARGE" 大图模式
     * "MODE_SMALL" 小图模式
     */
    const materialMode = this.adData.materialMode;
    const width = this.adData.dimensions[0];
    const height = this.adData.dimensions[1];
    switch (materialMode) {
      case "MODE_VIDEO":
        return this.templateBigHorizontal;
        break;
      case "MODE_GROUP":
        return this.templateGroup;
        break;
      case "MODE_LARGE":
        return width > height ? this.templateBigHorizontal : this.templateBigVertical;
        break;
      case "MODE_SMALL":
        return this.templateSmall;
        break;
      default:
        break;
    }
    return mode;
  },
  onCommandClick(evt) {
    this.option.drawerVisible = true;
    this.option.contentType = evt.detail
  },
  onTabActive(evt) {
    this.option.contentType = evt.detail;
  },
  onFeedAdLoad(evt) {
    this.adData = evt.detail.adData;
    this.option.isApp = this.isApp(this.adData.app);
  },
  isApp(app) {
    return !!app;
  },
  // 关联vad-video组件,视频播放与暂停
  onDrawerVisibleChange(evt) {
    this.option.drawerVisible = evt.detail;
  },
  getImgMargin(index) {
    if (index === 0) {
      return {
        "marginRight": "4.5px"
      }
    } else if (index === 1) {
      return {
        "marginLeft": "4.5px",
        "marginRight": "4.5px"
      }
    } else if (index === 2) {
      return {
        "marginLeft": "4.5px"
      }
    }
  }
}
</script>

<style>
@import './FeedComposite.css';
</style>
CSS文件:FeedComposite.css
css
/* 大图模板样式 --- 开始 */
.feed-big-pic-container {
    flex-direction: column;
    width: 1000px;
    margin-top: 20px;
    margin-left: auto;
    margin-right: auto;
}

.feed-big-pic-container .show-content {
    width: 100%;
}

.feed-big-pic-container .show-content .big-pic-style {
    width: 1000px;
    height: 1778px;
    object-fit: fill;
    background-color: #dddddd;
    border-radius: 40px;
}

/* 大图模板样式 --- 结束 */

/* 组图模板样式 --- 开始 */
.feed-group-pic-container {
    flex-direction: column;
    width: 1000px;
    margin-top: 20px;
    margin-left: auto;
    margin-right: auto;
}

.feed-group-pic-container .show-content {
    width: 100%;
}

.feed-group-pic-container .show-content .group-img {
    width: 327px;
    height: 245px;
    object-fit: contain;
    border-radius: 40px;
}

/* 组图模板样式 --- 结束 */

/* 左图右文模板样式 --- 开始 */
.feed-left-pic-right-text-container {
    flex-direction: column;
    margin-left: auto;
    margin-right: auto;
    width: 1000px;
}

.feed-left-pic-right-text-container .show-content {
    border-radius: 40px;
    height: 246px;
    background-color: #dddddd;
    margin-right: 40px;
    flex-grow: 0;
}

.feed-left-pic-right-text-container .show-content .show-image {
    object-fit: contain;
}

/* 左图右文模板样式 --- 结束 */

/* 左文右图模板样式 --- 开始 */
.feed-left-text-right-pic-container {
    flex-direction: column;
    margin-left: auto;
    margin-right: auto;
    width: 1000px;
}

.feed-left-text-right-pic-container .show-content {
    border-radius: 40px;
    height: 246px;
    background-color: #dddddd;
    margin-left: 73px;
    flex-grow: 0;
}

.feed-left-text-right-pic-container .show-content .show-image {
    object-fit: contain;
}

/* 左文右图模板样式 --- 结束 */

/* 上图下文模板样式 --- 开始 */
.feed-top-pic-bottom-text-container {
    flex-direction: column;
    margin-left: auto;
    margin-right: auto;
    width: 1000px;
}

.feed-top-pic-bottom-text-container .show-content {
    border-radius: 40px;
    height: 562.5px;
    background-color: #dddddd;
}

.feed-top-pic-bottom-text-container .show-content .show-image {
    object-fit: contain;
}

/* 上图下文模板样式 --- 结束 */

/* 上文下图模板样式 --- 开始 */
.feed-top-text-bottom-pic-container {
    flex-direction: column;
    width: 1000px;
    margin-left: auto;
    margin-right: auto;
}

.feed-top-text-bottom-pic-container .show-content {
    border-radius: 40px;
    height: 562.5px;
    background-color: #dddddd;
}

.feed-top-text-bottom-pic-container .show-content .show-image {
    object-fit: contain;
}

/* 上文下图模板样式 --- 结束 */
JS文件:FeedCompositeConfig.js
js
export const styleObj = {
  "big-pic": {
    grid: [[1, 2], [3, 4, 5, 6]],
    videoStyle: {
      containerStyle: {
        width: '1000px',
        height: '1778px',
        borderRadius: '40px',
        overflow: 'hidden',
      },
      videoStyle: {
        width: '1000px',
        height: '1778px',
        objectFit: 'contain',
      },
      maskStyle: {
        width: '1000px',
        height: '1778px',
      },
    },
  },
  "group-pic": {
    grid: [[1,2], [3], [4,5,6]]
  },
  "left-pic-right-text": {
    grid: [[1,2], [3], [4,5,6]]
  },
  "left-text-right-pic": {
    grid: [[1,2], [3], [4,5,6]]
  },
  "top-pic-bottom-text": {
    grid: [[1,2,4,5,6],[3]],
    videoStyle: {
      containerStyle: {
        width: '1000px',
        height: '563px',
        borderRadius: '40px',
        overflow: 'hidden',
      },
      videoStyle: {
        width: '1000px',
        height: '563px',
        objectFit: 'contain',
      },
      maskStyle: {
        width: '1000px',
        height: '563px',
      },
    },
  },
  "top-text-bottom-pic": {
    grid: [[1,2,4,5,6],[3]],
    videoStyle: {
      containerStyle: {
        width: '1000px',
        height: '563px',
        borderRadius: '40px',
        overflow: 'hidden',
      },
      videoStyle: {
        width: '1000px',
        height: '563px',
        objectFit: 'contain',
      },
      maskStyle: {
        width: '1000px',
        height: '563px',
      },
    },
  },
}

2.3 属性说明

属性名类型默认值必填说明
position-idstringnone广告位id
media-idstringnone媒体id
is-renderbooleanfalse是否展示广告
template-big-horizontalstringtop-pic-bottom-text
大图横屏模板:
①.top-pic-bottom-text
②.top-text-bottom-pic
template-big-verticalstringbig-pic
大图竖屏模板:
①.big-pic
template-smallstringleft-pic-right-text
小图模板:
①.left-pic-right-text
②.left-text-right-pic
template-groupstringgroup-pic
组图模板:
①.group-pic
@ad-loadeventnone 广告加载触发事件
onAdLoad(evt: MouseEvent):void{}
  • evt.detail.adData
  • evt.detail.sendNotice
详情请通用事件:
@ad-erroreventnone广告异常触发事件