ssfg 5 gadi atpakaļ
vecāks
revīzija
674b5891ad
100 mainītis faili ar 4701 papildinājumiem un 384 dzēšanām
  1. 5 19
      app.json
  2. 85 0
      components/trtc-room/common/constants.js
  3. 346 0
      components/trtc-room/controller/user-controller.js
  4. 14 0
      components/trtc-room/libs/mta_analysis.js
  5. 1 0
      components/trtc-room/libs/tim-wx.js
  6. 32 0
      components/trtc-room/model/pusher.js
  7. 38 0
      components/trtc-room/model/stream.js
  8. 17 0
      components/trtc-room/model/user.js
  9. BIN
      components/trtc-room/static/audio-active.png
  10. BIN
      components/trtc-room/static/audio-false.png
  11. BIN
      components/trtc-room/static/audio-true.png
  12. BIN
      components/trtc-room/static/back.png
  13. BIN
      components/trtc-room/static/beauty-false.png
  14. BIN
      components/trtc-room/static/beauty-true.png
  15. BIN
      components/trtc-room/static/beauty.png
  16. BIN
      components/trtc-room/static/camera-false.png
  17. BIN
      components/trtc-room/static/camera-true.png
  18. BIN
      components/trtc-room/static/camera.png
  19. BIN
      components/trtc-room/static/close-white-big.png
  20. BIN
      components/trtc-room/static/close-white.png
  21. BIN
      components/trtc-room/static/display-pause-false.png
  22. BIN
      components/trtc-room/static/display-pause-true.png
  23. BIN
      components/trtc-room/static/display-pause-white.png
  24. BIN
      components/trtc-room/static/display-play-false.png
  25. BIN
      components/trtc-room/static/display-play-true.png
  26. BIN
      components/trtc-room/static/display-play-white.png
  27. BIN
      components/trtc-room/static/fullscreen-white.png
  28. BIN
      components/trtc-room/static/fullscreen.png
  29. BIN
      components/trtc-room/static/hangup-red.png
  30. BIN
      components/trtc-room/static/hangup.png
  31. BIN
      components/trtc-room/static/im-disable.png
  32. BIN
      components/trtc-room/static/im-white.png
  33. BIN
      components/trtc-room/static/im.png
  34. BIN
      components/trtc-room/static/list-white.png
  35. BIN
      components/trtc-room/static/list.png
  36. BIN
      components/trtc-room/static/loading.png
  37. BIN
      components/trtc-room/static/micro-open.png
  38. BIN
      components/trtc-room/static/more-disable.png
  39. BIN
      components/trtc-room/static/more-enable.png
  40. BIN
      components/trtc-room/static/more-white.png
  41. BIN
      components/trtc-room/static/music-white.png
  42. BIN
      components/trtc-room/static/mute-camera-gray.png
  43. BIN
      components/trtc-room/static/mute-camera-white.png
  44. BIN
      components/trtc-room/static/mute-mic-gray.png
  45. BIN
      components/trtc-room/static/mute-mic-white.png
  46. BIN
      components/trtc-room/static/phone.png
  47. BIN
      components/trtc-room/static/setting-white.png
  48. BIN
      components/trtc-room/static/setting.png
  49. BIN
      components/trtc-room/static/slide-up.png
  50. BIN
      components/trtc-room/static/speaker-false.png
  51. BIN
      components/trtc-room/static/speaker-true.png
  52. BIN
      components/trtc-room/static/speaker-white.png
  53. BIN
      components/trtc-room/static/switch.png
  54. 101 0
      components/trtc-room/template/1v1/1v1.wxml
  55. 111 0
      components/trtc-room/template/1v1/1v1.wxss
  56. 75 0
      components/trtc-room/template/custom/custom.wxml
  57. 11 0
      components/trtc-room/template/custom/custom.wxss
  58. 261 0
      components/trtc-room/template/grid/grid.wxml
  59. 563 0
      components/trtc-room/template/grid/grid.wxss
  60. 2611 0
      components/trtc-room/trtc-room.js
  61. 1 1
      miniprogram_npm/vant-weapp/skeleton/index.json
  62. 70 0
      components/trtc-room/trtc-room.wxml
  63. 226 0
      components/trtc-room/trtc-room.wxss
  64. 21 0
      components/trtc-room/utils/compare-version.js
  65. 50 0
      components/trtc-room/utils/environment.js
  66. 62 0
      components/trtc-room/utils/event.js
  67. BIN
      images/banjihz.png
  68. BIN
      images/ewmbj.png
  69. BIN
      images/finallytest.png
  70. BIN
      images/gift1.jpg
  71. BIN
      images/gift2.jpg
  72. BIN
      images/gift3.jpg
  73. BIN
      images/gift4.jpg
  74. BIN
      images/gift5.jpg
  75. BIN
      images/icon1.png
  76. BIN
      images/icon2.png
  77. BIN
      images/icon3.png
  78. BIN
      images/icon4.png
  79. BIN
      images/khzy_icon1.jpg
  80. BIN
      images/mb.png
  81. BIN
      images/nodata.png
  82. BIN
      images/redcloud.png
  83. BIN
      images/sc_img.png
  84. BIN
      images/scdj1.jpg
  85. BIN
      images/scdj2.jpg
  86. BIN
      images/scdj3.jpg
  87. BIN
      images/scdj4.jpg
  88. BIN
      images/spfm.jpg
  89. BIN
      images/xydj_bj.png
  90. BIN
      images/zbfm.png
  91. BIN
      images/zhfk_bj.png
  92. BIN
      images/zsbj.jpg
  93. 0 1
      miniprogram_npm/vant-weapp/action-sheet/index.d.ts
  94. 0 62
      miniprogram_npm/vant-weapp/action-sheet/index.js
  95. 0 8
      miniprogram_npm/vant-weapp/action-sheet/index.json
  96. 0 67
      miniprogram_npm/vant-weapp/action-sheet/index.wxml
  97. 0 1
      miniprogram_npm/vant-weapp/action-sheet/index.wxss
  98. 0 1
      miniprogram_npm/vant-weapp/area/index.d.ts
  99. 0 224
      miniprogram_npm/vant-weapp/area/index.js
  100. 0 0
      miniprogram_npm/vant-weapp/area/index.json

+ 5 - 19
app.json

@@ -1,7 +1,8 @@
 {
   "pages": [
     "pages/welcome/welcome",
-    "pages/testAnswer/testAnswer",
+    "pages/zbDetails/zbDetails",
+     "pages/testAnswer/testAnswer",
     "pages/myClass/myClass",
     "pages/last/last",
     "pages/index/index",
@@ -19,7 +20,7 @@
     "pages/onlineClass/onlineClass",
     "pages/dbDetails/dbDetails",
     "pages/dbVideo/dbVideo",
-    "pages/zbDetails/zbDetails",
+    
     "pages/myNotice/myNotice",
     "pages/myInteractions/myInteractions",
     "pages/myMission/myMission",
@@ -41,6 +42,7 @@
   },
   "usingComponents": {
     "van-button": "/miniprogram_npm/vant-weapp/button/index",
+    "van-divider": "/miniprogram_npm/vant-weapp/divider/index",
     "van-row": "/miniprogram_npm/vant-weapp/row/index",
     "van-col": "/miniprogram_npm/vant-weapp/col/index",
     "van-datetime-picker": "/miniprogram_npm/vant-weapp/datetime-picker/index",
@@ -48,34 +50,18 @@
     "van-popup": "/miniprogram_npm/vant-weapp/popup/index",
     "van-radio": "/miniprogram_npm/vant-weapp/radio/index",
     "van-radio-group": "/miniprogram_npm/vant-weapp/radio-group/index",
-    "van-action-sheet": "/miniprogram_npm/vant-weapp/action-sheet/index",
-    "van-search": "/miniprogram_npm/vant-weapp/search/index",
-    "van-tree-select": "/miniprogram_npm/vant-weapp/tree-select/index",
-    "van-overlay": "/miniprogram_npm/vant-weapp/overlay/index",
-    "van-dialog": "/miniprogram_npm/vant-weapp/dialog/index",
     "van-tab": "/miniprogram_npm/vant-weapp/tab/index",
     "van-tabs": "/miniprogram_npm/vant-weapp/tabs/index",
-    "van-stepper": "/miniprogram_npm/vant-weapp/stepper/index",
-    "van-collapse": "/miniprogram_npm/vant-weapp/collapse/index",
-    "van-collapse-item": "/miniprogram_npm/vant-weapp/collapse-item/index",
-    "van-cell": "/miniprogram_npm/vant-weapp/cell/index",
-    "van-cell-group": "/miniprogram_npm/vant-weapp/cell-group/index",
-    "van-toast": "/miniprogram_npm/vant-weapp/toast/index",
-    "van-field": "/miniprogram_npm/vant-weapp/field/index",
     "van-checkbox": "/miniprogram_npm/vant-weapp/checkbox/index",
     "van-checkbox-group": "/miniprogram_npm/vant-weapp/checkbox-group/index",
-    "van-loading": "/miniprogram_npm/vant-weapp/loading/index",
     "van-icon": "/miniprogram_npm/vant-weapp/icon/index",
     "van-nav-bar": "/miniprogram_npm/vant-weapp/nav-bar/index",
     "van-tabbar": "/miniprogram_npm/vant-weapp/tabbar/index",
     "van-tabbar-item": "/miniprogram_npm/vant-weapp/tabbar-item/index",
-    "van-rate": "/miniprogram_npm/vant-weapp/rate/index",
     "van-image": "/miniprogram_npm/vant-weapp/image/index",
-    "van-divider": "/miniprogram_npm/vant-weapp/divider/index",
     "van-grid": "/miniprogram_npm/vant-weapp/grid/index",
     "van-grid-item": "/miniprogram_npm/vant-weapp/grid-item/index",
-    "van-notice-bar": "/miniprogram_npm/vant-weapp/notice-bar/index",
-    "van-uploader": "/miniprogram_npm/vant-weapp/uploader/index"
+    "van-notice-bar": "/miniprogram_npm/vant-weapp/notice-bar/index"
   },
   "tabBar": {
     "color": "#a9b7b7",

+ 85 - 0
components/trtc-room/common/constants.js

@@ -0,0 +1,85 @@
+export const EVENT = {
+  LOCAL_JOIN: 'LOCAL_JOIN', // 本地进房成功
+  LOCAL_LEAVE: 'LOCAL_LEAVE', // 本地退房
+  REMOTE_USER_JOIN: 'REMOTE_USER_JOIN', // 远端用户进房
+  REMOTE_USER_LEAVE: 'REMOTE_USER_LEAVE', // 远端用户退房
+  REMOTE_VIDEO_ADD: 'REMOTE_VIDEO_ADD', // 远端视频流添加事件,当远端用户取消发布音频流后会收到该通知
+  REMOTE_VIDEO_REMOVE: 'REMOTE_VIDEO_REMOVE', // 远端视频流移出事件,当远端用户取消发布音频流后会收到该通知
+  REMOTE_AUDIO_ADD: 'REMOTE_AUDIO_ADD', // 远端音频流添加事件,当远端用户取消发布音频流后会收到该通知
+  REMOTE_AUDIO_REMOVE: 'REMOTE_AUDIO_REMOVE', // 远端音频流移除事件,当远端用户取消发布音频流后会收到该通知
+  REMOTE_STATE_UPDATE: 'REMOTE_STATE_UPDATE', // 远端用户播放状态变更
+  LOCAL_NET_STATE_UPDATE: 'LOCAL_NET_STATE_UPDATE', // 本地推流网络状态变更
+  REMOTE_NET_STATE_UPDATE: 'REMOTE_NET_STATE_UPDATE', // 远端用户网络状态变更
+  LOCAL_AUDIO_VOLUME_UPDATE: 'LOCAL_AUDIO_VOLUME_UPDATE', // 本地音量变更
+  REMOTE_AUDIO_VOLUME_UPDATE: 'REMOTE_AUDIO_VOLUME_UPDATE', // 远端用户音量变更
+  VIDEO_FULLSCREEN_UPDATE: 'VIDEO_FULLSCREEN_UPDATE', // 调用 player requestFullScreen 或者 exitFullScreen 后触发
+  BGM_PLAY_START: 'BGM_PLAY_START', // 调用 LivePusherContext.playBGM(Object object)
+  BGM_PLAY_FAIL: 'BGM_PLAY_FAIL', //
+  BGM_PLAY_PROGRESS: 'BGM_PLAY_PROGRESS', // bgm 播放时间戳变更
+  BGM_PLAY_COMPLETE: 'BGM_PLAY_COMPLETE', // bgm 播放结束 或者 调用 LivePusherContext.stopBGM() ?
+  ERROR: 'ERROR', // pusher 出现错误
+  IM_READY: 'IM_READY', // IM SDK 可用
+  IM_MESSAGE_RECEIVED: 'IM_MESSAGE_RECEIVED', // 收到IM 消息
+  IM_NOT_READY: 'IM_NOT_READY', // IM SDK 不可用
+  IM_KICKED_OUT: 'IM_KICKED_OUT', // IM SDK 下线
+  IM_ERROR: 'IM_ERROR', // IM SDK 下线
+}
+
+export const DEFAULT_COMPONENT_CONFIG = {
+  sdkAppID: '',
+  userID: '',
+  userSig: '',
+  template: '',
+  debugMode: false, // 是否开启调试模式
+  enableIM: true, // 是否开启 IM
+}
+
+export const DEFAULT_PUSHER_CONFIG = {
+  url: '',
+  mode: 'RTC', // RTC:实时通话(trtc sdk) live:直播模式(liteav sdk)
+  autopush: false, // 自动推送
+  enableCamera: false, // 是否开启摄像头
+  enableMic: false, // 是否开启麦克风
+  enableAgc: false, // 是否开启音频自动增益
+  enableAns: false, // 是否开启音频噪声抑制
+  enableEarMonitor: false, // 是否开启耳返(目前只在iOS平台有效)
+  enableAutoFocus: true, // 是否自动对焦
+  enableZoom: false, // 是否支持调整焦距
+  minBitrate: 600, // 最小码率
+  maxBitrate: 900, // 最大码率
+  videoWidth: 360, // 视频宽(若设置了视频宽高就会忽略aspect)
+  videoHeight: 640, // 视频高(若设置了视频宽高就会忽略aspect)
+  beautyLevel: 0, // 美颜,取值范围 0-9 ,0 表示关闭
+  whitenessLevel: 0, // 美白,取值范围 0-9 ,0 表示关闭
+  videoOrientation: 'vertical', // vertical horizontal
+  videoAspect: '9:16', // 宽高比,可选值有 3:4,9:16
+  frontCamera: 'front', // 前置或后置摄像头,可选值:front,back
+  enableRemoteMirror: false, // 设置推流画面是否镜像,产生的效果会表现在 live-player
+  localMirror: 'auto', // auto:前置摄像头镜像,后置摄像头不镜像(系统相机的表现)enable:前置摄像头和后置摄像头都镜像 disable: 前置摄像头和后置摄像头都不镜像
+  enableBackgroundMute: false, // 进入后台时是否静音
+  audioQuality: 'high', // 高音质(48KHz)或低音质(16KHz),可选值:high,low
+  audioVolumeType: 'voicecall', // 声音类型 可选值: media: 媒体音量,voicecall: 通话音量
+  audioReverbType: 0, // 音频混响类型 0: 关闭 1: KTV 2: 小房间 3:大会堂 4:低沉 5:洪亮 6:金属声 7:磁性
+  // waitingImage: 'https://web-player-1252463788.cos.ap-shanghai.myqcloud.com/demo/1px.png', // 当微信切到后台时的垫片图片 trtc暂不支持
+  waitingImage: 'https://mc.qcloudimg.com/static/img/daeed8616ac5df256c0591c22a65c4d3/pause_publish.jpg', // 当微信切到后台时的垫片图片 trtc暂不支持
+  waitingImageHash: '',
+  beautyStyle: 'smooth', // 美颜类型,取值有:smooth: 光滑 、nature: 自然
+  filter: '', // standard: 标准 pink: 粉嫩 nostalgia: 怀旧 blues: 蓝调 romantic: 浪漫  cool: 清凉 fresher: 清新 solor: 日系 aestheticism: 唯美 whitening:美白 cerisered: 樱红
+}
+
+export const DEFAULT_PLAYER_CONFIG = {
+  src: '',
+  mode: 'RTC',
+  autoplay: true, // 7.0.9 必须设置为true,否则 Android 有概率调用play()失败
+  muteAudio: true, // 默认不拉取音频,需要手动订阅,如果要快速播放,需要设置false
+  muteVideo: true, // 默认不拉取视频,需要手动订阅,如果要快速播放,需要设置false
+  orientation: 'vertical', // 画面方向 vertical horizontal
+  objectFit: 'fillCrop', // 填充模式,可选值有 contain,fillCrop
+  enableBackgroundMute: false, // 进入后台时是否静音(已废弃,默认退台静音)
+  minCache: 1, // 最小缓冲区,单位s(RTC 模式推荐 0.2s)
+  maxCache: 2, // 最大缓冲区,单位s(RTC 模式推荐 0.8s)
+  soundMode: 'speaker', // 声音输出方式 ear speaker
+  enableRecvMessage: 'false', // 是否接收SEI消息
+  autoPauseIfNavigate: true, // 当跳转到其它小程序页面时,是否自动暂停本页面的实时音视频播放
+  autoPauseIfOpenNative: true, // 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
+}

+ 346 - 0
components/trtc-room/controller/user-controller.js

@@ -0,0 +1,346 @@
+import Event from '../utils/event.js'
+import User from '../model/user.js'
+import Stream from '../model/stream.js'
+import { EVENT } from '../common/constants.js'
+
+const TAG_NAME = 'UserController'
+/**
+ * 通讯成员管理
+ */
+class UserController {
+  constructor(componentContext) {
+    // userMap 用于存储完整的数据结构
+    this.userMap = new Map()
+    // userList 用于存储简化的用户数据 Object,包括 {userID hasMainAudio hasMainVideo hasAuxAudio hasAuxVideo}
+    this.userList = []
+    // streamList 存储steam 对象列表,用于 trtc-room 渲染 player
+    this.streamList = []
+    this._emitter = new Event()
+    this.componentContext = componentContext
+    this.isNewVersion = componentContext.isNewVersion
+  }
+  userEventHandler(event) {
+    const code = event.detail.code
+    let data
+    if (event.detail.message && typeof event.detail.message === 'string') {
+      try {
+        data = JSON.parse(event.detail.message)
+      } catch (exception) {
+        console.warn(TAG_NAME, 'userEventHandler 数据格式错误', exception)
+        return false
+      }
+    } else {
+      console.warn(TAG_NAME, 'userEventHandler 数据格式错误')
+      return false
+    }
+    switch (code) {
+      case 1020:
+        // console.log(TAG_NAME, '远端用户全量列表更新:', code)
+        if (!this.isNewVersion) {
+          // TODO 旧版SDK处理逻辑,返回全量的用户列表,需要对userList 进行前后对比,筛选出新增用户,暂不实现
+        }
+        break
+      case 1031:
+        // console.log(TAG_NAME, '远端用户进房通知:', code)
+        // 1031 有新用户
+        // {
+        //   "userlist":[
+        //          {
+        //              "userid":"webrtc11"
+        //          }
+        //      ]
+        // }
+        this.addUser(data)
+        break
+      case 1032:
+        // console.log(TAG_NAME, '远端用户退房通知:', code)
+        // 1032 有用户退出
+        this.removeUser(data)
+        break
+      case 1033:
+        // console.log(TAG_NAME, '远端用户视频状态位变化通知:', code)
+        // 1033 用户视频状态变化,新增stream或者更新stream 状态
+        // {
+        //   "userlist":[
+        //          {
+        //              "userid":"webrtc11",
+        //              "playurl":" room://rtc.tencent.com?userid=xxx&streamtype=main",
+        //              "streamtype":"main",
+        //              "hasvideo":true
+        //          }
+        //      ]
+        // }
+        this.updateUserVideo(data)
+        break
+      case 1034:
+        // console.log(TAG_NAME, '远端用户音频状态位变化通知:', code)
+        // 1034 用户音频状态变化
+        // {
+        //   "userlist":[
+        //          {
+        //              "userid":"webrtc11",
+        //              "playurl":" room://rtc.tencent.com?userid=xxx&streamtype=main",
+        //              "hasaudio":false
+        //          }
+        //      ]
+        // }
+        this.updateUserAudio(data)
+        break
+    }
+  }
+  /**
+   * 处理用户进房事件
+   * @param {Object} data pusher 下发的数据
+   */
+  addUser(data) {
+    // console.log(TAG_NAME, 'addUser', data)
+    const incomingUserList = data.userlist
+    const userMap = this.userMap
+    if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
+      incomingUserList.forEach((item) => {
+        const userID = item.userid
+        // 已经在 map 中的用户
+        let user = this.getUser(userID)
+        if (!user) {
+          // 新增用户
+          user = new User({ userID: userID })
+          this.userList.push({
+            userID: userID,
+          })
+        }
+        userMap.set(userID, user)
+        this._emitter.emit(EVENT.REMOTE_USER_JOIN, { userID: userID, userList: this.userList })
+        // console.log(TAG_NAME, 'addUser', item, userMap.get(userID), this.userMap)
+      })
+    }
+  }
+  /**
+   * 处理用户退房事件
+   * @param {Object} data pusher 下发的数据 {userlist}
+   */
+  removeUser(data) {
+    // console.log(TAG_NAME, 'removeUser', data)
+    const incomingUserList = data.userlist
+    if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
+      incomingUserList.forEach((item) => {
+        const userID = item.userid
+        let user = this.getUser(userID)
+        // 偶现SDK触发退房事件前没有触发进房事件
+        if (!user || !user.streams) {
+          return
+        }
+        // 从userList 里删除指定的用户和 stream
+        this._removeUserAndStream(userID)
+        // 重置
+        user.streams['main'] && user.streams['main'].reset()
+        user.streams['aux'] && user.streams['aux'].reset()
+        // 用户退出,释放引用,外部调用该 user 所有stream 的 playerContext.stop() 方法停止播放
+        // TODO 触发时机提前了,方便外部用户做出处理,时机仍需进一步验证
+        this._emitter.emit(EVENT.REMOTE_USER_LEAVE, { userID: userID, userList: this.userList, streamList: this.streamList })
+        user = undefined
+        this.userMap.delete(userID)
+        // console.log(TAG_NAME, 'removeUser', this.userMap)
+      })
+    }
+  }
+  /**
+   * 处理用户视频通知事件
+   * @param {Object} data pusher 下发的数据 {userlist}
+   */
+  updateUserVideo(data) {
+    console.log(TAG_NAME, 'updateUserVideo', data)
+    const incomingUserList = data.userlist
+    if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
+      incomingUserList.forEach((item) => {
+        const userID = item.userid
+        const streamType = item.streamtype
+        const streamID = userID + '_' + streamType
+        const hasVideo = item.hasvideo
+        const src = item.playurl
+        const user = this.getUser(userID)
+        // 更新指定用户的属性
+        if (user) {
+          // 查找对应的 stream
+          let stream = user.streams[streamType]
+          console.log(TAG_NAME, 'updateUserVideo start', user, streamType, stream)
+          // 常规逻辑
+          // 新来的stream,新增到 user.steams 和 streamList,streamList 包含所有用户(有音频或视频)的 stream
+          if (!stream) {
+            // 不在 user streams 里,需要新建
+            user.streams[streamType] = stream = new Stream({ userID, streamID, hasVideo, src, streamType })
+            this._addStream(stream)
+          } else {
+            // 更新 stream 属性
+            stream.setProperty({ hasVideo })
+            if (!hasVideo && !stream.hasAudio) {
+              this._removeStream(stream)
+            }
+            // or
+            // if (hasVideo) {
+            //   stream.setProperty({ hasVideo })
+            // } else if (!stream.hasAudio) {
+            //   // hasVideo == false && hasAudio == false
+            //   this._removeStream(stream)
+            // }
+          }
+          // 特殊逻辑
+          if (streamType === 'aux') {
+            if (hasVideo) {
+              // 辅流需要修改填充模式
+              stream.objectFit = 'contain'
+              this._addStream(stream)
+            } else {
+              // 如果是辅流要移除该 stream,否则需要移除 player
+              this._removeStream(stream)
+            }
+          }
+          // 更新所属user 的 hasXxx 值
+          this.userList.find((item)=>{
+            if (item.userID === userID) {
+              item[`has${streamType.replace(/^\S/, (s) => s.toUpperCase())}Video`] = hasVideo
+              return true
+            }
+          })
+          console.log(TAG_NAME, 'updateUserVideo end', user, streamType, stream)
+          const eventName = hasVideo ? EVENT.REMOTE_VIDEO_ADD : EVENT.REMOTE_VIDEO_REMOVE
+          this._emitter.emit(eventName, { stream: stream, streamList: this.streamList, userList: this.userList })
+          // console.log(TAG_NAME, 'updateUserVideo', user, stream, this.userMap)
+        }
+      })
+    }
+  }
+  /**
+   * 处理用户音频通知事件
+   * @param {Object} data pusher 下发的数据 {userlist}
+   */
+  updateUserAudio(data) {
+    // console.log(TAG_NAME, 'updateUserAudio', data)
+    const incomingUserList = data.userlist
+    if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
+      incomingUserList.forEach((item) => {
+        const userID = item.userid
+        // 音频只跟着 stream main ,这里只修改 main
+        const streamType = 'main'
+        const streamID = userID + '_' + streamType
+        const hasAudio = item.hasaudio
+        const src = item.playurl
+        const user = this.getUser(userID)
+        if (user) {
+          let stream = user.streams[streamType]
+          // if (!stream) {
+          //   user.streams[streamType] = stream = new Stream({ streamType: streamType })
+          //   this._addStream(stream)
+          // }
+
+          // 常规逻辑
+          // 新来的stream,新增到 user.steams 和 streamList,streamList 包含所有用户的 stream
+          if (!stream) {
+            // 不在 user streams 里,需要新建
+            user.streams[streamType] = stream = new Stream({ userID, streamID, hasAudio, src, streamType })
+            this._addStream(stream)
+          } else {
+            // 更新 stream 属性
+            stream.setProperty({ hasAudio })
+            if (!hasAudio && !stream.hasVideo) {
+              this._removeStream(stream)
+            }
+            // or
+            // if (hasAudio) {
+            //   stream.setProperty({ hasAudio })
+            // } else if (!stream.hasVideo) {
+            // // hasVideo == false && hasAudio == false
+            //   this._removeStream(stream)
+            // }
+          }
+
+          // stream.userID = userID
+          // stream.streamID = userID + '_' + streamType
+          // stream.hasAudio = hasAudio
+          // stream.src = src
+          // 更新所属 user 的 hasXxx 值
+          this.userList.find((item)=>{
+            if (item.userID === userID) {
+              item[`has${streamType.replace(/^\S/, (s) => s.toUpperCase())}Audio`] = hasAudio
+              return true
+            }
+          })
+          const eventName = hasAudio ? EVENT.REMOTE_AUDIO_ADD : EVENT.REMOTE_AUDIO_REMOVE
+          this._emitter.emit(eventName, { stream: stream, streamList: this.streamList, userList: this.userList })
+          // console.log(TAG_NAME, 'updateUserAudio', user, stream, this.userMap)
+        }
+      })
+    }
+  }
+  /**
+   *
+   * @param {String} userID 用户ID
+   * @returns {Object}
+   */
+  getUser(userID) {
+    return this.userMap.get(userID)
+  }
+  getStream({ userID, streamType }) {
+    const user = this.userMap.get(userID)
+    if (user) {
+      return user.streams[streamType]
+    }
+    return undefined
+  }
+  getUserList() {
+    return this.userList
+  }
+  getStreamList() {
+    return this.streamList
+  }
+  /**
+   * 重置所有user 和 steam
+   * @returns {Object}
+   */
+  reset() {
+    this.streamList.forEach((item)=>{
+      item.reset()
+    })
+    this.streamList = []
+    this.userList = []
+    this.userMap.clear()
+    return {
+      userList: this.userList,
+      streamList: this.streamList,
+    }
+  }
+  on(eventCode, handler, context) {
+    this._emitter.on(eventCode, handler, context)
+  }
+  off(eventCode, handler) {
+    this._emitter.off(eventCode, handler)
+  }
+  /**
+   * 删除用户和所有的 stream
+   * @param {String} userID 用户ID
+   */
+  _removeUserAndStream(userID) {
+    this.streamList = this.streamList.filter((item)=>{
+      return item.userID !== userID && item.userID !== ''
+    })
+    this.userList = this.userList.filter((item)=>{
+      return item.userID !== userID
+    })
+  }
+  _addStream(stream) {
+    if (!this.streamList.includes(stream)) {
+      this.streamList.push(stream)
+    }
+  }
+  _removeStream(stream) {
+    this.streamList = this.streamList.filter((item)=>{
+      if (item.userID === stream.userID && item.streamType === stream.streamType) {
+        return false
+      }
+      return true
+    })
+    const user = this.getUser(stream.userID)
+    user.streams[stream.streamType] = undefined
+  }
+}
+
+export default UserController

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 14 - 0
components/trtc-room/libs/mta_analysis.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
components/trtc-room/libs/tim-wx.js


+ 32 - 0
components/trtc-room/model/pusher.js

@@ -0,0 +1,32 @@
+import { DEFAULT_PUSHER_CONFIG } from '../common/constants.js'
+
+class Pusher {
+  constructor(options) {
+    Object.assign(this, DEFAULT_PUSHER_CONFIG, {
+      isVisible: true, // 手Q初始化时不能隐藏 puser和player 否则黑屏
+    }, options)
+  }
+  /**
+   * 通过wx.createLivePusherContext 获取<live-pusher> context
+   * @param {Object} context 组件上下文
+   * @returns {Object} livepusher context
+   */
+  getPusherContext(context) {
+    if (!this.pusherContext) {
+      this.pusherContext = wx.createLivePusherContext(context)
+    }
+    return this.pusherContext
+  }
+  reset() {
+    console.log('Pusher reset', this.pusherContext)
+    if (this.pusherContext) {
+      console.log('Pusher pusherContext.stop()')
+      this.pusherContext.stop()
+      this.pusherContext = null
+    }
+    Object.assign(this, DEFAULT_PUSHER_CONFIG, {
+      isVisible: true,
+    })
+  }
+}
+export default Pusher

+ 38 - 0
components/trtc-room/model/stream.js

@@ -0,0 +1,38 @@
+// 一个stream 对应一个 player
+import { DEFAULT_PLAYER_CONFIG } from '../common/constants.js'
+
+class Stream {
+  constructor(options) {
+    Object.assign(this, DEFAULT_PLAYER_CONFIG, {
+      userID: '', // 该stream 关联的userID
+      streamType: '', // stream 类型 [main small] aux
+      streamID: '', // userID + '_' + streamType
+      isVisible: true, // 手Q初始化时不能隐藏 puser和player 否则黑屏。iOS 微信初始化时不能隐藏,否则同层渲染失败,player会置顶
+      hasVideo: false,
+      hasAudio: false,
+      volume: 0, // 音量大小 0~100
+      playerContext: undefined, // playerContext 依赖component context来获取,目前只能在渲染后获取
+    }, options)
+  }
+  setProperty(options) {
+    Object.assign(this, options)
+  }
+  reset() {
+    if (this.playerContext) {
+      this.playerContext.stop()
+      this.playerContext = undefined
+    }
+    Object.assign(this, DEFAULT_PLAYER_CONFIG, {
+      userID: '', // 该stream 关联的userID
+      streamType: '', // stream 类型 [main small] aux
+      streamID: '',
+      isVisible: true,
+      hasVideo: false,
+      hasAudio: false,
+      volume: 0, // 音量大小 0~100
+      playerContext: undefined,
+    })
+  }
+}
+
+export default Stream

+ 17 - 0
components/trtc-room/model/user.js

@@ -0,0 +1,17 @@
+class User {
+  constructor(options) {
+    Object.assign(this, {
+      userID: '',
+      // hasMainStream: false, // 触发 1034 且stream type 为 main 即为true
+      // hasAuxStream: false, // 触发 1034 且stream type 为 aux 即为true
+      // hasSmallStream: false, // 触发 1034 且stream type 为 small 即为true
+      streams: {
+        // main: mainStream
+        // aux: auxStream
+      }, // 有0~2个Stream, 进房没有推流,main aux, small 特殊处理,small 和 main 同时只播放一路
+      // stream 是用于渲染 live-player 的数据源
+    }, options)
+  }
+}
+
+export default User

BIN
components/trtc-room/static/audio-active.png


BIN
components/trtc-room/static/audio-false.png


BIN
components/trtc-room/static/audio-true.png


BIN
components/trtc-room/static/back.png


BIN
components/trtc-room/static/beauty-false.png


BIN
components/trtc-room/static/beauty-true.png


BIN
components/trtc-room/static/beauty.png


BIN
components/trtc-room/static/camera-false.png


BIN
components/trtc-room/static/camera-true.png


BIN
components/trtc-room/static/camera.png


BIN
components/trtc-room/static/close-white-big.png


BIN
components/trtc-room/static/close-white.png


BIN
components/trtc-room/static/display-pause-false.png


BIN
components/trtc-room/static/display-pause-true.png


BIN
components/trtc-room/static/display-pause-white.png


BIN
components/trtc-room/static/display-play-false.png


BIN
components/trtc-room/static/display-play-true.png


BIN
components/trtc-room/static/display-play-white.png


BIN
components/trtc-room/static/fullscreen-white.png


BIN
components/trtc-room/static/fullscreen.png


BIN
components/trtc-room/static/hangup-red.png


BIN
components/trtc-room/static/hangup.png


BIN
components/trtc-room/static/im-disable.png


BIN
components/trtc-room/static/im-white.png


BIN
components/trtc-room/static/im.png


BIN
components/trtc-room/static/list-white.png


BIN
components/trtc-room/static/list.png


BIN
components/trtc-room/static/loading.png


BIN
components/trtc-room/static/micro-open.png


BIN
components/trtc-room/static/more-disable.png


BIN
components/trtc-room/static/more-enable.png


BIN
components/trtc-room/static/more-white.png


BIN
components/trtc-room/static/music-white.png


BIN
components/trtc-room/static/mute-camera-gray.png


BIN
components/trtc-room/static/mute-camera-white.png


BIN
components/trtc-room/static/mute-mic-gray.png


BIN
components/trtc-room/static/mute-mic-white.png


BIN
components/trtc-room/static/phone.png


BIN
components/trtc-room/static/setting-white.png


BIN
components/trtc-room/static/setting.png


BIN
components/trtc-room/static/slide-up.png


BIN
components/trtc-room/static/speaker-false.png


BIN
components/trtc-room/static/speaker-true.png


BIN
components/trtc-room/static/speaker-white.png


BIN
components/trtc-room/static/switch.png


+ 101 - 0
components/trtc-room/template/1v1/1v1.wxml

@@ -0,0 +1,101 @@
+<!-- template 1v1 -->
+<template name='1v1'>
+  <view class="template-1v1">
+    <view wx:for="{{streamList}}" wx:key="streamID" class="view-container player-container">
+      <live-player
+        class="player"
+        id="{{item.streamID}}"
+        data-userid="{{item.userID}}"
+        data-streamid="{{item.streamID}}"
+        data-streamtype="{{item.streamType}}"
+        src= "{{item.src}}"
+        mode= "RTC"
+        autoplay= "{{item.autoplay}}"
+        mute-audio= "{{item.muteAudio}}"
+        mute-video= "{{item.muteVideo}}"
+        orientation= "{{item.orientation}}"
+        object-fit= "{{item.objectFit}}"
+        background-mute= "{{item.enableBackgroundMute}}"
+        min-cache= "{{item.minCache}}"
+        max-cache= "{{item.maxCache}}"
+        sound-mode= "{{item.soundMode}}"
+        enable-recv-message= "{{item.enableRecvMessage}}"
+        auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
+        auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}"
+        debug="{{debug}}"
+        bindstatechange="_playerStateChange"
+        bindfullscreenchange="_playerFullscreenChange"
+        bindnetstatus="_playerNetStatus"
+        bindaudiovolumenotify  ="_playerAudioVolumeNotify"
+      />
+    </view>
+    <view class="view-container pusher-container {{pusher.enableCamera?'':'none'}} {{streamList.length===0? 'fullscreen':''}}">
+      <live-pusher
+        class="pusher"
+        url="{{pusher.url}}"
+        mode="{{pusher.mode}}"
+        autopush="{{pusher.autopush}}"
+        enable-camera="{{pusher.enableCamera}}"
+        enable-mic="{{pusher.enableMic}}"
+        muted="{{!pusher.enableMic}}"
+        enable-agc="{{pusher.enableAgc}}"
+        enable-ans="{{pusher.enableAns}}"
+        enable-ear-monitor="{{pusher.enableEarMonitor}}"
+        auto-focus="{{pusher.enableAutoFocus}}"
+        zoom="{{pusher.enableZoom}}"
+        min-bitrate="{{pusher.minBitrate}}"
+        max-bitrate="{{pusher.maxBitrate}}"
+        video-width="{{pusher.videoWidth}}"
+        video-height="{{pusher.videoHeight}}"
+        beauty="{{pusher.beautyLevel}}"
+        whiteness="{{pusher.whitenessLevel}}"
+        orientation="{{pusher.videoOrientation}}"
+        aspect="{{pusher.videoAspect}}"
+        device-position="{{pusher.frontCamera}}"
+        remote-mirror="{{pusher.enableRemoteMirror}}"
+        local-mirror="{{pusher.localMirror}}"
+        background-mute="{{pusher.enableBackgroundMute}}"
+        audio-quality="{{pusher.audioQuality}}"
+        audio-volume-type="{{pusher.audioVolumeType}}"
+        audio-reverb-type="{{pusher.audioReverbType}}"
+        waiting-image="{{pusher.waitingImage}}"
+        debug="{{debug}}"
+        bindstatechange="_pusherStateChangeHandler"
+        bindnetstatus="_pusherNetStatusHandler"
+        binderror="_pusherErrorHandler"
+        bindbgmstart="_pusherBGMStartHandler"
+        bindbgmprogress="_pusherBGMProgressHandler"
+        bindbgmcomplete="_pusherBGMCompleteHandler"
+        bindaudiovolumenotify="_pusherAudioVolumeNotify"
+      />
+      <!-- <view class="loading" wx:if="{{streamList.length === 0}}">
+        <view class="loading-img">
+          <image src="./static/loading.png" class="rotate-img"></image>
+        </view>
+        <view class="loading-text">等待接听中...</view>
+      </view> -->
+    </view>
+    <!-- <view class="handle-btns">
+      <view class="btn-normal" bindtap="_toggleAudio">
+        <image class="btn-image" src="{{pusher.enableMic? './static/audio-true.png': './static/audio-false.png'}} "></image>
+      </view>
+      <view class="btn-normal" bindtap="switchCamera" >
+        <image class="btn-image" src="./static/switch.png"></image>
+      </view>
+      <view class="btn-normal" bindtap="_toggleSoundMode">
+        <image class="btn-image" src="{{streamList[0].soundMode === 'ear' ? './static/phone.png': './static/speaker-true.png'}} "></image>
+      </view>
+    </view> -->
+    <!-- <view class="bottom-btns"> -->
+      <!-- <view class="btn-normal" data-key="beautyLevel" data-value="9|0" data-value-type="number" bindtap="_setPuserProperty">
+        <image class="btn-image" src="{{pusher.beautyLevel == 9 ? './static/beauty-true.png': './static/beauty-false.png'}} "></image>
+      </view>
+      <view class="btn-hangup" bindtap="_hangUp">
+        <image class="btn-image" src="./static/hangup.png"></image>
+      </view> -->
+      <!-- <view class="btn-normal" bindtap="_toggleIMPanel">
+        <image class="btn-image" src="{{enableIM? './static/im.png': './static/im-disable.png'}}"></image>
+      </view> -->
+    <!-- </view> -->
+  </view>
+</template>

+ 111 - 0
components/trtc-room/template/1v1/1v1.wxss

@@ -0,0 +1,111 @@
+/* 1v1 视频电话模式 */
+.template-1v1{
+  width: 100vw;
+  height: 100vh;
+  position: relative;
+  /* border: 1px solid red; */
+}
+.template-1v1 .pusher-container{
+  width: 240rpx;
+  height: 320rpx;
+  position: absolute;
+  right: 0rpx;
+  top: 0rpx;
+  z-index: 20;
+  border: 1px solid red;
+}
+/* .template-1v1 .pusher-container.fullscreen{
+  width: 100vw;
+  height: 100vh;
+  top: 0;
+  right: 0;
+} */
+/* .template-1v1 .loading {
+  position: absolute;
+  top: 40vh;
+  left: 50vw;
+  transform: translate(-50%, 0);
+  width: 300rpx;
+  height: 250rpx;
+  border-radius: 12rpx;
+  background: rgba(0,0,0,0.6);
+  color: white;
+  padding: 40rpx;
+  display: flex;
+  flex-direction: column;
+} */
+.template-1v1 .loading-img {
+  height: 200rpx;
+  display:flex;
+  justify-content: center;
+  align-items: center;
+  animation: rotate 2s linear infinite;
+}
+.template-1v1 .rotate-img {
+  width:160rpx;
+  height: 160rpx;
+}
+.template-1v1 .loading-text {
+  width: 100%;
+  padding-top: 40rpx;
+  text-align: center;
+}
+@keyframes rotate {
+  0%{ transform: rotate(0deg);}
+  50%{ transform: rotate(180deg);}
+  100%{ transform: rotate(360deg);}
+}
+.template-1v1 .player-container:nth-child(1){
+  width: 100vw;
+  height: 100vh;
+}
+.template-1v1 .handle-btns {
+  position: absolute;
+  z-index: 3;
+  bottom: 15vh;
+  width: 100vw;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-around;
+}
+
+.template-1v1 .bottom-btns {
+  position: absolute;
+  z-index: 3;
+  bottom: 3vh;
+  width: 100vw;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-around;
+}
+
+/* .template-1v1 image {
+  width: 4vh;
+  height: 4vh;
+} */
+
+.template-1v1 .btn-normal {
+  width: 8vh;
+  height: 8vh;
+  box-sizing: border-box;
+  display: flex;
+  background: white;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+}
+.template-1v1 .btn-hangup .btn-image,
+.template-1v1 .btn-normal .btn-image{
+  width: 4vh;
+  height: 4vh;
+}
+.template-1v1 .btn-hangup  {
+  width: 8vh;
+  height: 8vh;
+  background: #f75c45;
+  box-sizing: border-box;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+}

+ 75 - 0
components/trtc-room/template/custom/custom.wxml

@@ -0,0 +1,75 @@
+<!-- template custom -->
+<template name='custom'>
+  <view class="template-custom">
+    <view class="players-container">
+      <view wx:for="{{streamList}}" wx:key="streamID" wx:if="{{item.src && (item.hasVideo || item.hasAudio)}}" class="view-container player-container {{item.isVisible?'':'none'}}" style="left:{{item.xAxis}};top:{{item.yAxis}};width:{{item.width}};height:{{item.height}};z-index:{{item.zIndex}};">
+        <live-player
+          class="player" 
+          id="{{item.streamID}}" 
+          data-userid="{{item.userID}}"
+          data-streamid="{{item.streamID}}"
+          data-streamtype="{{item.streamType}}"
+          src= "{{item.src}}"
+          mode= "{{item.mode}}"
+          autoplay= "{{item.autoplay}}"
+          mute-audio= "{{item.muteAudio}}"
+          mute-video= "{{item.muteVideo}}"
+          orientation= "{{item.orientation}}"
+          object-fit= "{{item.objectFit}}"
+          background-mute= "{{item.enableBackgroundMute}}"
+          min-cache= "{{item.minCache}}"
+          max-cache= "{{item.maxCache}}"
+          sound-mode= "{{item.soundMode}}"
+          enable-recv-message= "{{item.enableRecvMessage}}"
+          auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
+          auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}"
+          debug="{{debug}}"
+          bindstatechange="_playerStateChange"
+          bindfullscreenchange="_playerFullscreenChange"
+          bindnetstatus="_playerNetStatus"
+          bindaudiovolumenotify  ="_playerAudioVolumeNotify"
+        />
+      </view>
+    </view>
+    <view class="view-container pusher-container {{pusher.isVisible?'':'none'}}" style="left:{{pusher.xAxis}};top:{{pusher.yAxis}};width:{{pusher.width}};height:{{pusher.height}};z-index:{{pusher.zIndex}};">
+      <live-pusher 
+        class="pusher" 
+        url="{{pusher.url}}" 
+        mode="{{pusher.mode}}"
+        autopush="{{pusher.autopush}}"
+        enable-camera="{{pusher.enableCamera}}"
+        enable-mic="{{pusher.enableMic}}"
+        muted="{{!pusher.enableMic}}"
+        enable-agc="{{pusher.enableAgc}}"
+        enable-ans="{{pusher.enableAns}}"
+        enable-ear-monitor="{{pusher.enableEarMonitor}}"
+        auto-focus="{{pusher.enableAutoFocus}}"
+        zoom="{{pusher.enableZoom}}"
+        min-bitrate="{{pusher.minBitrate}}"
+        max-bitrate="{{pusher.maxBitrate}}"
+        video-width="{{pusher.videoWidth}}"
+        video-height="{{pusher.videoHeight}}"
+        beauty="{{pusher.beautyLevel}}"
+        whiteness="{{pusher.whitenessLevel}}"
+        orientation="{{pusher.videoOrientation}}"
+        aspect="{{pusher.videoAspect}}"
+        device-position="{{pusher.frontCamera}}"
+        remote-mirror="{{pusher.enableRemoteMirror}}"
+        local-mirror="{{pusher.localMirror}}"
+        background-mute="{{pusher.enableBackgroundMute}}"
+        audio-quality="{{pusher.audioQuality}}"
+        audio-volume-type="{{pusher.audioVolumeType}}"
+        audio-reverb-type="{{pusher.audioReverbType}}"
+        waiting-image="{{pusher.waitingImage}}"
+        debug="{{debug}}"
+        bindstatechange="_pusherStateChangeHandler"
+        bindnetstatus="_pusherNetStatusHandler"
+        binderror="_pusherErrorHandler"
+        bindbgmstart="_pusherBGMStartHandler"
+        bindbgmprogress="_pusherBGMProgressHandler"
+        bindbgmcomplete="_pusherBGMCompleteHandler"
+        bindaudiovolumenotify="_pusherAudioVolumeNotify"
+      />
+    </view>
+  </view>
+</template>

+ 11 - 0
components/trtc-room/template/custom/custom.wxss

@@ -0,0 +1,11 @@
+/* 通过方法自定义模式 */
+.template-custom{
+  /* 绝对定位模式 pusher 和 player 都用绝对定位*/
+  width: 100vw;
+  height: 100vh;
+  position: relative;
+}
+.template-custom .pusher-container,
+.template-custom .player-container{
+  position: absolute;
+}

+ 261 - 0
components/trtc-room/template/grid/grid.wxml

@@ -0,0 +1,261 @@
+<!-- template grid -->
+<template name='grid'>
+  <view class="template-grid">
+    <view class="column-layout">
+      <view class="column-1">
+        <view class="grid-scroll-container" bindtouchstart="_handleGridTouchStart" bindtouchend="_handleGridTouchEnd">
+          <!-- <view id="grid-container-id" class="grid-container {{visibleStreamList.length < 4 ? 'stream-' + visibleStreamList.length : visibleStreamList.length%2 == 0? 'stream-odd':'stream-even'}}"> -->
+          <view id="grid-container-id" class="grid-container {{visibleStreamList.length < 4 ? 'stream-' + visibleStreamList.length : 'stream-3'}}">
+            <view class="view-container pusher-container {{pusher.isVisible && ((gridCurrentPage === 1 && gridPlayerPerPage > 3) || gridPlayerPerPage < 4)?'':'none'}}">
+              <live-pusher
+                class="pusher"
+                url="{{pusher.url}}"
+                mode="{{pusher.mode}}"
+                autopush="{{pusher.autopush}}"
+                enable-camera="{{pusher.enableCamera}}"
+                enable-mic="{{pusher.enableMic}}"
+                muted="{{!pusher.enableMic}}"
+                enable-agc="{{pusher.enableAgc}}"
+                enable-ans="{{pusher.enableAns}}"
+                enable-ear-monitor="{{pusher.enableEarMonitor}}"
+                auto-focus="{{pusher.enableAutoFocus}}"
+                zoom="{{pusher.enableZoom}}"
+                min-bitrate="{{pusher.minBitrate}}"
+                max-bitrate="{{pusher.maxBitrate}}"
+                video-width="{{pusher.videoWidth}}"
+                video-height="{{pusher.videoHeight}}"
+                beauty="{{pusher.beautyLevel}}"
+                whiteness="{{pusher.whitenessLevel}}"
+                orientation="{{pusher.videoOrientation}}"
+                aspect="{{pusher.videoAspect}}"
+                device-position="{{pusher.frontCamera}}"
+                remote-mirror="{{pusher.enableRemoteMirror}}"
+                local-mirror="{{pusher.localMirror}}"
+                background-mute="{{pusher.enableBackgroundMute}}"
+                audio-quality="{{pusher.audioQuality}}"
+                audio-volume-type="{{pusher.audioVolumeType}}"
+                audio-reverb-type="{{pusher.audioReverbType}}"
+                waiting-image="{{pusher.waitingImage}}"
+                debug="{{debug}}"
+                beauty-style="{{pusher.beautyStyle}}"
+                filter="{{pusher.filter}}"
+                bindstatechange="_pusherStateChangeHandler"
+                bindnetstatus="_pusherNetStatusHandler"
+                binderror="_pusherErrorHandler"
+                bindbgmstart="_pusherBGMStartHandler"
+                bindbgmprogress="_pusherBGMProgressHandler"
+                bindbgmcomplete="_pusherBGMCompleteHandler"
+                bindaudiovolumenotify="_pusherAudioVolumeNotify"
+              />
+              <view class="no-video" wx:if="{{!pusher.enableCamera}}">
+                <image class="image" src="./static/mute-camera-white.png"></image>
+              </view>
+              <!-- <view class="no-audio" wx:if="{{!pusher.enableMic}}">
+                <image class="image" src="./static/mute-mic-white.png"></image>
+              </view>
+              <view class="audio-volume" wx:if="{{pusher.enableMic}}">
+                <image class="image" src="./static/micro-open.png"></image>
+                <view class="audio-active" style="height:{{pusher.volume}}%">
+                  <image class="image" src="./static/audio-active.png"></image>
+                </view>
+              </view> -->
+            </view>
+            <view wx:for="{{visibleStreamList}}" 
+              wx:key="streamID"
+              class="view-container player-container {{item.isVisible?'':'none'}}" 
+              id="{{'player-'+item.streamID}}"
+              data-userid="{{item.userID}}" 
+              data-streamtype="{{item.streamType}}" 
+              bindtap="_doubleTabToggleFullscreen">
+              <live-player
+                class="player"
+                id="{{item.streamID}}"
+                data-userid="{{item.userID}}"
+                data-streamid="{{item.streamID}}"
+                data-streamtype="{{item.streamType}}"
+                src= "{{item.src}}"
+                mode= "RTC"
+                autoplay= "{{item.autoplay}}"
+                mute-audio= "{{item.muteAudio}}"
+                mute-video= "{{item.muteVideo}}"
+                orientation= "{{item.orientation}}"
+                object-fit= "{{item.objectFit}}"
+                background-mute= "{{item.enableBackgroundMute}}"
+                min-cache= "{{item.minCache}}"
+                max-cache= "{{item.maxCache}}"
+                sound-mode= "{{item.soundMode}}"
+                enable-recv-message= "{{item.enableRecvMessage}}"
+                auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
+                auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}"
+                debug="{{debug}}"
+                bindstatechange="_playerStateChange"
+                bindfullscreenchange="_playerFullscreenChange"
+                bindnetstatus="_playerNetStatus"
+                bindaudiovolumenotify="_playerAudioVolumeNotify"
+              />
+              <view class="no-video" wx:if="{{item.muteVideo}}">
+                <image class="image" src="./static/display-pause-white.png"></image>
+                <view class="text">
+                  <p>{{item.userID}}</p>
+                </view>
+              </view>
+              <view class="no-video" wx:if="{{!item.hasVideo && !item.muteVideo}}">
+                <image class="image" src="./static/mute-camera-white.png"></image>
+                <view class="text">
+                  <p>{{item.userID}}</p>
+                </view>
+                <view class="text">
+                  <p>对方摄像头未打开</p>
+                </view>
+              </view>
+              <view class="no-audio" wx:if="{{!item.hasAudio}}">
+                <image class="image" src="./static/mute-mic-white.png"></image>
+              </view>
+              <view class="audio-volume" wx:if="{{item.hasAudio}}">
+                <image class="image" src="./static/micro-open.png"></image>
+                <view class="audio-active" style="height:{{item.volume}}%">
+                  <image class="image" src="./static/audio-active.png"></image>
+                </view>
+              </view>
+              <view class="operation-bar">
+                <view class="operation-item-container">
+                  <view class="operation-item" catchtap="_handleSubscribeRemoteAudio" data-user-i-d="{{item.userID}}" data-stream-type="{{item.streamType}}">
+                    <image class="item-image" src="{{item.muteAudio? './static/speaker-false.png': './static/speaker-white.png'}}"></image>
+                  </view>
+                  <view class="operation-item" catchtap="_handleSubscribeRemoteVideo" data-user-i-d="{{item.userID}}" data-stream-type="{{item.streamType}}">
+                    <image class="item-image" src="{{item.muteVideo? './static/display-pause-false.png': './static/display-play-white.png'}}"></image>
+                  </view>
+                  <view class="operation-item" bindtap="_toggleFullscreen" data-user-i-d="{{item.userID}}" data-stream-type="{{item.streamType}}">
+                    <image class="item-image" src="./static/fullscreen-white.png"></image>
+                  </view>
+                </view>
+              </view>
+            </view>
+            <view wx:for="{{gridPagePlaceholderStreamList}}" wx:key="id" class="view-container player-container player-placeholder">
+              <image class="image" src="./static/mute-camera-white.png"></image>
+            </view>
+          </view>
+        </view>
+      </view>
+      <!-- <view class="column-2">
+        <view class="menu" wx:if="{{!isShowMoreMenu}}">
+          <view class="menu-item" bindtap="_switchSettingPanel">
+            <image class="image" src="./static/setting-white.png"></image>
+          </view>
+          <view class="menu-item" bindtap="_switchMemberListPanel">
+            <image class="image" src="./static/list-white.png"></image>
+          </view>
+          <view class="menu-item" bindtap="_hangUp">
+            <image class="image" src="./static/hangup-red.png"></image>
+          </view>
+          <view class="menu-item" bindtap="_toggleIMPanel">
+            <image class="image" src="{{enableIM? './static/im-white.png': './static/im-disable.png'}}"></image>
+          </view>
+        </view>
+      </view> -->
+    </view>
+
+    <view class="pages-container" wx:if="{{gridPageCount > 1}}">
+      <view wx:for="{{gridPageCount}}" wx:key="this" class="page-item {{index+1 === gridCurrentPage? 'current':''}}" ></view>
+    </view>
+    <view class="panel memberlist-panel {{panelName === 'memberlist-panel' ? '' : 'none'}}" >
+      <view bindtap="_handleMaskerClick" class='close-btn'>X</view>
+      <view class="panel-header">成员列表</view>
+      <view class="panel-body">
+        <view class="panel-tips" wx:if="{{streamList.length === 0}}">暂无成员</view>
+        <scroll-view class="scroll-container" scroll-y="true">
+          <view class="member-item" wx:for="{{streamList}}" wx:key="streamID" >
+            <view class="member-id">{{item.userID}}</view>
+            <view class="member-btns">
+              <button class="btn" hover-class="btn-hover" data-userid="{{item.userID}}" data-streamtype="{{item.streamType}}" data-key="objectFit" data-value="fillCrop|contain" bindtap="_setPlayerProperty">{{item.objectFit === 'fillCrop'? '填充':'适应'}}</button>
+              <button class="btn" hover-class="btn-hover" data-userid="{{item.userID}}" data-streamtype="{{item.streamType}}" data-key="orientation" data-value="vertical|horizontal" bindtap="_setPlayerProperty">{{item.orientation === 'vertical'? '竖屏':'横屏'}}</button>
+              <button class="btn" hover-class="btn-hover" data-userid="{{item.userID}}" data-streamtype="{{item.streamType}}" bindtap="_switchStreamType" wx:if="{{item.streamType === 'main'}}">{{item._definitionType === 'small'? '小画面':'主画面'}}</button>
+              <button class="btn" hover-class="btn-hover" data-userid="{{item.userID}}" data-streamtype="{{item.streamType}}" bindtap="_handleSnapshotClick">截屏</button>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+    </view>
+    <view class="panel setting-panel {{panelName === 'setting-panel' ? '' : 'none'}}" >
+      <view bindtap="_handleMaskerClick" class='close-btn'>X</view>
+      <view class="panel-header">推流设置</view>
+      <view class="panel-body">
+        <scroll-view class="scroll-container" scroll-y="true">
+          <view class="setting-option">
+            <view class="label">启用摄像头</view>
+            <view class="btn-normal" bindtap="_toggleVideo">
+              <image class="btn-image" src="{{pusher.enableCamera? './static/camera-true.png': './static/camera-false.png'}}"></image>
+            </view>
+          </view>
+          <view class="setting-option">
+            <view class="label">启用麦克风</view>
+            <view class="btn-normal" bindtap="_toggleAudio">
+              <image class="btn-image" src="{{pusher.enableMic? './static/audio-true.png': './static/audio-false.png'}}"></image>
+            </view>
+          </view>
+          <view class="setting-option">
+            <view class="label">切换摄像头</view>
+            <view class="btn-normal" bindtap="switchCamera" >
+              <image class="btn-image" src="./static/switch.png"></image>
+            </view>
+          </view>
+          <view class='setting-option'>
+            <view class="label">开启美颜</view>
+            <switch class="setting-switch" color="#006eff" checked="{{pusher.beautyLevel == 9 ? true: false}}" data-key="beautyLevel" data-value="9|0" data-value-type="number" bindchange="_setPuserProperty"/>
+          </view>
+          <view class='setting-option'>
+            <view class="label">开启AGC</view>
+            <switch class="setting-switch" color="#006eff" checked="{{pusher.enableAgc}}" data-key="enableAgc" data-value="true" data-value-type="boolean" bindchange="_setPuserProperty"/>
+          </view>
+          <view class='setting-option'>
+            <view class="label">开启ANS</view>
+            <switch class="setting-switch" color="#006eff" checked="{{pusher.enableAns}}" data-key="enableAns" data-value="true" data-value-type="boolean" bindchange="_setPuserProperty"/>
+          </view>
+          <view class='setting-option'>
+            <view class="label">开启横屏推流</view>
+            <switch class="setting-switch" color="#006eff" checked="{{pusher.videoOrientation === 'vertical' ? false: true}}" data-key="videoOrientation"  data-value="horizontal|vertical" data-value-type="string" bindchange="_setPuserProperty"/>
+          </view>
+        </scroll-view>
+      </view>
+    </view>
+    <view class="panel bgm-panel {{panelName === 'bgm-panel' ? '' : 'none'}}" >
+      <view bindtap="_handleMaskerClick" class='close-btn'>X</view>
+      <view class="panel-header">背景音乐</view>
+      <view class="panel-body">
+        <view class="setting-option">
+          <view class="label">MIC音量</view>
+          <view class="slider-content">
+            <slider value="{{MICVolume}}" min="0" max="100" show-value="true" activeColor="#006eff" bindchange="_changeProperty" data-property-name="MICVolume" />
+          </view>
+        </view>
+        <view class="setting-option">
+          <view class="label">BGM音量</view>
+          <view class="slider-content">
+            <slider value="{{BGMVolume}}" min="0" max="100" show-value="true" activeColor="#006eff" bindchange="_changeProperty" data-property-name="BGMVolume" />
+          </view>
+        </view>
+        <view class="setting-option">
+          <view class="label">播放进度</view>
+          <view class="slider-content">
+            <progress activeColor="#006eff" percent="{{BGMProgress}}"></progress>
+          </view>
+        </view>
+        <view class="menu">
+          <view class="menu-item" bindtap="_handleBGMOperation" data-operation-name="playBGM">
+            <view class="label">播放</view>
+          </view>
+          <view class="menu-item" bindtap="_handleBGMOperation" data-operation-name="pauseBGM">
+            <view class="label">暂停</view>
+          </view>
+          <view class="menu-item" bindtap="_handleBGMOperation" data-operation-name="resumeBGM">
+            <view class="label">继续</view>
+          </view>
+          <view class="menu-item" bindtap="_handleBGMOperation" data-operation-name="stopBGM">
+            <view class="label">停止</view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="masker {{panelName =='' ? 'none' : ''}}" bindtap="_handleMaskerClick"></view>
+  </view>
+</template>

+ 563 - 0
components/trtc-room/template/grid/grid.wxss

@@ -0,0 +1,563 @@
+/* 9人 会议模版 */
+.template-grid {
+  width: 750rpx;
+  height: 500rpx;
+}
+
+.column-layout {
+  width: 750rpx;
+  height: 500rpx;
+  /* display: flex;
+  flex-direction: column; */
+  position: relative;
+}
+
+.column-layout .column-1 {
+  flex: 1;
+}
+
+/* .column-layout .column-2 {
+  position: relative;
+  height: 100rpx;
+  background-color: rgb(36, 36, 36);
+} */
+
+.fullscreen-device-fix .column-layout .column-2 {
+  height: 120rpx;
+}
+
+.menu {
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
+}
+
+.menu .menu-item {
+  text-align: center;
+  height: 100rpx;
+  flex-grow: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+
+.menu .menu-item .image {
+  width: 46rpx;
+  height: 46rpx;
+}
+
+.more-menu {
+  position: absolute;
+  top: 0;
+}
+
+.more-menu .scroll-container {
+  width: 100%;
+  height: 100rpx;
+  white-space: nowrap;
+}
+
+.more-menu .menu-item-container {
+  width: 20%;
+  display: inline-block;
+}
+
+.template-grid .grid-container {
+  width: 750rpx;
+  height: 500rpx;
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  position: relative;
+  /* position: absolute;
+  right: 0;
+  bottom: 0; */
+}
+
+.pusher-container {
+  width: 300rpx;
+  height: 300rpx;
+  position: absolute;
+  bottom: 0;
+  right: 0;
+}
+
+.player-container {
+  width: 700rpx;
+  height: 500rpx;
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+
+.template-grid .grid-scroll-container {
+  width: 100%;
+  height: 100%;
+  /* box-sizing: border-box; */
+  /* overflow-y: scroll; */
+  background-color: #000;
+}
+
+.grid-containe.overflow {
+  height: auto;
+}
+
+.template-grid .view-container {
+  position: relative;
+}
+
+.stream-0 .view-container {
+  width: 100%;
+  height: 100%;
+}
+
+.stream-1 .view-container {
+  width: 100%;
+  height: 50%;
+}
+
+.stream-2 .view-container {
+  width: 50%;
+  height: 50%;
+}
+
+.stream-2 .view-container:nth-child(1) {
+  width: 100%;
+  height: 50%;
+}
+
+.stream-3 .view-container {
+  width: 50%;
+  height: 50%;
+}
+
+.stream-4 .view-container {
+  width: 50%;
+  height: 33.3%;
+}
+
+.stream-4 .view-container:nth-child(1) {
+  width: 100%;
+  height: 33.3%;
+}
+
+.stream-5 .view-container {
+  width: 50%;
+  height: 33.3%;
+}
+
+.stream-6 .view-container {
+  width: 33.3%;
+  height: 33.3%;
+}
+
+.stream-6 .view-container:nth-child(1) {
+  width: 100%;
+  height: 33.3%;
+}
+
+.stream-7 .view-container {
+  width: 33.3%;
+  height: 33.3%;
+}
+
+.stream-7 .view-container:nth-child(1) {
+  width: 50%;
+  height: 33.3%;
+}
+
+.stream-7 .view-container:nth-child(2) {
+  width: 50%;
+  height: 33.3%;
+}
+
+.stream-8 .view-container {
+  width: 33.3%;
+  height: 33.3%;
+}
+
+.stream-even .view-container {
+  width: 50%;
+  height: 50%;
+}
+
+.stream-odd .view-container {
+  width: 50%;
+  height: 50%;
+}
+
+.stream-odd .view-container:last-child {
+  width: 100%;
+  height: 50%;
+}
+
+.template-grid .operation-bar {
+  position: absolute;
+  bottom: 6rpx;
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+}
+
+.operation-bar .operation-item-container {
+  width: auto;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  background: rgb(0, 0, 0, .3);
+  border-radius: 10rpx;
+}
+
+.template-grid .operation-bar .operation-item {
+  width: 64rpx;
+  height: 64rpx;
+  /* flex-grow: 1; */
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+}
+
+.operation-item .item-image {
+  width: 36rpx;
+  height: 36rpx;
+}
+
+.template-grid .volume-progress {
+  width: 100%;
+  position: absolute;
+  bottom: 0;
+}
+
+.template-grid .btn-normal {
+  width: 64rpx;
+  height: 64rpx;
+  margin: 0 6rpx;
+  box-sizing: border-box;
+  display: flex;
+  background: rgba(255, 255, 255, 1);
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+}
+
+.template-grid .btn-normal .btn-image {
+  width: 36rpx;
+  height: 36rpx;
+}
+
+.template-grid .btn-hangup {
+  background: #f75c45;
+}
+
+.template-grid .panel {
+  position: absolute;
+  background: rgba(0, 0, 0, 0.8);
+  width: 90vw;
+  height: auto;
+  z-index: 999;
+  top: 50vh;
+  left: 50vw;
+  transform: translate(-50%, -50%);
+  color: white;
+  display: flex;
+  flex-direction: column;
+  padding: 20rpx 0;
+  border-radius: 10rpx;
+  box-sizing: border-box;
+  font-size: 14px;
+}
+
+.panel .close-btn {
+  position: absolute;
+  top: 0;
+  right: 0;
+  padding: 5px 10px;
+}
+
+.panel .panel-header {
+  text-align: center;
+  padding-bottom: 20rpx;
+}
+
+.panel .panel-tips {
+  color: #999;
+  text-align: center;
+}
+
+.panel .panel-body {
+  flex: 1;
+  max-height: 50vh;
+}
+
+.panel .panel-body .scroll-container {
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+}
+
+.memberlist-panel .panel-body {
+  height: 50vh;
+}
+
+.memberlist-panel .member-item {
+  display: flex;
+  /* border-bottom: 1px solid #999; */
+  margin: 16rpx 16rpx 16rpx 32rpx;
+}
+
+.memberlist-panel .member-id {
+  width: 30%;
+  font-size: 12px;
+  line-height: 64rpx;
+}
+
+.memberlist-panel .member-btns {
+  width: 70%;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.memberlist-panel .member-btns .btn-normal {
+  margin-left: 0;
+}
+
+.memberlist-panel .member-btns .btn {
+  margin-right: 0;
+}
+
+.setting-panel .panel-body {
+  height: 50vh;
+}
+
+.setting-panel .setting-option {
+  display: flex;
+  justify-content: space-between;
+  margin: 16rpx 16rpx 16rpx 32rpx;
+  /* box-sizing: border-box;
+  padding: 12rpx 16rpx 12rpx 32rpx; */
+}
+
+.setting-panel .setting-option .label {
+  /* line-height: 64rpx; */
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+
+.setting-panel .setting-switch {
+  transform: scale(0.8);
+  margin-right: -12rpx;
+}
+
+.bgm-panel .panel-body {
+  height: auto;
+}
+
+.bgm-panel .setting-option {
+  height: 60rpx;
+  display: flex;
+  flex-direction: row;
+  margin: 16rpx 16rpx 16rpx 32rpx;
+}
+
+.bgm-panel .setting-option .label {
+  width: 140rpx;
+  line-height: 60rpx;
+}
+
+.bgm-panel .setting-option .slider-content {
+  flex: 1;
+  line-height: 60rpx;
+}
+
+.bgm-panel .setting-option .slider-content slider {
+  transform: scale(0.9);
+  margin: 0;
+}
+
+.bgm-panel .setting-option .slider-content progress {
+  transform: scale(0.9);
+  margin-top: 28rpx;
+}
+
+.bgm-panel .menu {
+  padding: 16rpx 32rpx 16rpx 32rpx;
+  box-sizing: border-box;
+}
+
+.bgm-panel .menu .menu-item {
+  height: 80rpx;
+  background-color: #333;
+}
+
+.template-grid .masker {
+  position: absolute;
+  top: 0;
+  width: 100vw;
+  height: 100vh;
+  background: rgba(0, 0, 0, 0.4);
+}
+
+.template-grid .no-stream,
+.template-grid .no-video {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+  color: #fff;
+  background-color: rgba(0, 0, 0, 0.4);
+  font-size: 12px;
+}
+
+.template-grid .audio-volume,
+.template-grid .no-audio {
+  position: absolute;
+  bottom: 20rpx;
+  left: 20rpx;
+  width: 36rpx;
+  height: 36rpx;
+}
+
+.no-stream .image,
+.no-video .image {
+  width: 60rpx;
+  height: 60rpx;
+}
+
+.audio-volume .image,
+.no-audio .image {
+  width: 36rpx;
+  height: 36rpx;
+  position: absolute;
+  /*android 的bug ,image absolute后会向上漂移几个像素,如果要对其必须都设置absolute*/
+}
+
+.audio-active {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+}
+
+.audio-active .image {
+  bottom: 0;
+}
+
+.slide-up-tips {
+  position: absolute;
+  bottom: -100rpx;
+  left: 50%;
+  transform: translate(-50%, 0);
+  width: 200rpx;
+  height: auto;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+  font-size: 12px;
+  color: #fff;
+  background-color: rgba(0, 0, 0, 0.4);
+  box-sizing: border-box;
+  padding: 20rpx;
+  border-radius: 10rpx;
+  opacity: 0;
+}
+
+.slide-up-tips .image {
+  width: 100rpx;
+  height: 100rpx;
+}
+
+.player-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.player-placeholder .image {
+  width: 100rpx;
+  height: 100rpx;
+}
+
+.pages-container {
+  width: auto;
+  left: 50%;
+  transform: translate(-50%, 0);
+  height: 20rpx;
+  position: absolute;
+  bottom: 12%;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+}
+
+.pages-container .page-item {
+  width: 20rpx;
+  height: 20rpx;
+  border-radius: 50%;
+  margin: 0 8rpx;
+  background-color: rgb(99, 99, 99, .5);
+}
+
+.pages-container .page-item.current {
+  background-color: #fff;
+}
+
+.radio-group-no-box {
+  display: inline-block;
+  color: #006eff;
+  background-color: #ffffff;
+  border: 1px solid #006eff;
+  border-radius: 4px;
+  margin-left: 180rpx;
+  font-size: 12px;
+}
+
+.radio-group-no-box .radio-item {
+  padding: 5px 8px;
+  text-align: center;
+  border-right: 1px solid #006eff;
+  display: inline-block;
+}
+
+.radio-group-no-box .radio-item:last-child {
+  border-right: none;
+}
+
+.radio-group-no-box .radio-item.selected {
+  color: #ffffff;
+  background-color: #006eff;
+}
+
+.radio-group-no-box radio {
+  display: none;
+}
+
+.picker-label {
+  display: inline-block;
+  color: #006eff;
+  background-color: #ffffff;
+  border: 1px solid #006eff;
+  border-radius: 4px;
+  padding: 5px 8px;
+  text-align: center;
+  font-size: 12px;
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2611 - 0
components/trtc-room/trtc-room.js


+ 1 - 1
miniprogram_npm/vant-weapp/skeleton/index.json

@@ -1,4 +1,4 @@
 {
   "component": true,
   "usingComponents": {}
-}
+}

+ 70 - 0
components/trtc-room/trtc-room.wxml

@@ -0,0 +1,70 @@
+<import src='./template/1v1/1v1.wxml'/>
+<import src='./template/grid/grid.wxml'/>
+<import src='./template/custom/custom.wxml'/>
+
+<view class="trtc-room-container {{isFullscreenDevice?'fullscreen-device-fix':''}}">
+  <block wx:if="{{template === '1v1'}}">
+    <template is='1v1' data="{{pusher, streamList, debug, enableIM}}"></template>
+  </block>
+  <block wx:if="{{template === 'grid'}}">
+    <template is='grid' data="{{pusher, streamList, visibleStreamList, debug, enableIM, panelName, gridPagePlaceholderStreamList, gridPageCount, gridCurrentPage, gridPlayerPerPage, isShowMoreMenu, MICVolume, BGMVolume, BGMProgress, beautyStyle, beautyStyleArray, filterIndex, filterArray, audioReverbTypeArray}}"></template>
+  </block>
+  <block wx:if="{{template === 'custom'}}">
+    <template is='custom' data="{{pusher, streamList, debug}}"></template>
+  </block>
+
+  <view class="im-panel" wx:if="{{enableIM && showIMPanel}}">
+    <view class="message-panel-body">
+      <scroll-view scroll-y="true" class="message-scroll-container" scroll-into-view="message{{messageList.length-1}}" scroll-with-animation="{{true}}">
+        <view class="message-list">
+          <view class="message-item" wx:for="{{messageList}}" wx:key="index" id="message{{index}}">
+            <span class="user-name {{item.name == config.userID?'mine':''}}">{{item.name}}</span>
+            <span class="message-content">{{item.message}}</span>
+          </view>
+          <view id="message-bottom"></view>
+        </view>
+      </scroll-view>
+        
+    </view>
+    <view class="message-panel-bottom">
+      <view class="message-input-container">
+        <input class="message-input" type="text" value="{{messageContent}}" bindinput='_inputIMMessage' bindconfirm='_sendIMMessage' confirm-type="send" placeholder="请输入消息" maxlength="200" placeholder-style="color:#ffffff;opacity: 0.55;"/>
+      </view>
+      <view class="message-send-btn">
+        <button class="btn" bindtap="_sendIMMessage" hover-class="btn-hover">发送</button>
+      </view>
+    </view>
+    <view bindtap="_toggleIMPanel" class='close-btn'>X</view>
+  </view>
+
+  <view class="debug-info-btn {{debugMode && !debugPanel?'':'none'}}">
+    <button class="debug-btn" bindtap="_debugTogglePanel" hover-class="button-hover">Debug</button>
+  </view>
+  <view class="debug-info {{debugMode && debugPanel?'':'none'}}">
+    <view bindtap="_debugTogglePanel" class='close-btn'>X</view>
+    <view>appVersion: {{appVersion}}</view>
+    <view>libVersion: {{libVersion}}</view>
+    <view>template: {{template}}</view>
+    <view>debug: <button class="{{debug?'':'false'}} debug-btn" bindtap="_debugToggleVideoDebug" hover-class="button-hover">{{debug}}</button></view>
+    <view>userID: {{pusher.userID}}</view>
+    <view>roomID: {{pusher.roomID}}</view>
+    <view>camera: <button class="{{pusher.enableCamera?'':'false'}} debug-btn" bindtap="_toggleVideo" hover-class="button-hover">{{pusher.enableCamera}}</button></view>
+    <view>mic: <button class="{{pusher.enableMic?'':'false'}} debug-btn" bindtap="_toggleAudio" hover-class="button-hover">{{pusher.enableMic}}</button></view>
+    <view>switch camera: <button class="debug-btn" bindtap="switchCamera" hover-class="button-hover">{{cameraPosition||pusher.frontCamera}}</button></view>
+    <view>Room:
+      <button class="debug-btn" bindtap="_debugEnterRoom" hover-class="button-hover">Enter</button>
+      <button class="debug-btn" bindtap="_debugExitRoom" hover-class="button-hover">Exit</button>
+      <button class="debug-btn" bindtap="_debugGoBack" hover-class="button-hover">Go back</button>
+    </view>
+    <view>IM: <button class="debug-btn" bindtap="_debugSendRandomMessage" hover-class="button-hover">send</button></view>
+    <view>user count: {{userList.length}}</view>
+    <view wx:for="{{userList}}" wx:key="userID">{{item.userID}}|
+      mainV:<span class="text {{item.hasMainVideo? 'true' : 'false' }}">{{item.hasMainVideo||false}}</span>|
+      mainA:<span class="text {{item.hasMainAudio? 'true' : 'false' }}">{{item.hasMainAudio||false}}</span>|
+      auxV:<span class="text {{item.hasAuxVideo? 'true' : 'false' }}">{{item.hasAuxVideo||false}}</span></view>
+    <view>stream count: {{streamList.length}}</view>
+    <view wx:for="{{streamList}}" wx:key="streamID">{{item.userID}}|{{item.streamType}}|
+      SubV:<button class="{{!item.muteVideo?'':'false'}} debug-btn" bindtap="_debugToggleRemoteVideo" hover-class="button-hover" data-user-i-d="{{item.userID}}" data-stream-type="{{item.streamType}}">{{!item.muteVideo}}</button>|
+      SubA:<button class="{{!item.muteAudio?'':'false'}} debug-btn" bindtap="_debugToggleRemoteAudio" hover-class="button-hover" data-user-i-d="{{item.userID}}" data-stream-type="{{item.streamType}}">{{!item.muteAudio}}</button></view>
+  </view>
+</view>

+ 226 - 0
components/trtc-room/trtc-room.wxss

@@ -0,0 +1,226 @@
+
+@import "./template/1v1/1v1.wxss";
+@import "./template/grid/grid.wxss";
+@import "./template/custom/custom.wxss";
+
+.pusher {
+  width: 240rpx;
+  height: 320rpx;
+}
+.player {
+  width: 100%;
+  height: 100%;
+}
+.debug-info{
+  max-width: 100vw;
+  max-height: 90vh;
+  box-sizing: border-box;
+  overflow-y: scroll;
+  position: absolute;
+  z-index: 9999;
+  background-color: rgba(0, 0, 0, .5);
+  color: #fff;
+  bottom: 20rpx;
+  left: 0;
+  padding: 10rpx;
+  font-size: 12px;
+}
+.debug-info-btn .debug-btn,
+.debug-info .debug-btn{
+  padding: 0 8px;
+  min-height: 18px;
+  width: auto;
+  font-size: 12px;
+  line-height: 18px;
+  display: inline-block;
+  color: #06ae56;
+  background-color: #f2f2f2;
+}
+.debug-info .debug-btn.false{
+  color: rgb(114, 114, 114);
+}
+.debug-info-btn .debug-btn,
+.debug-info .button-hover {
+  background-color: rgb(219, 219, 219);
+}
+.debug-info .close-btn{
+  position: absolute;
+  top: 0;
+  right: 0;
+  padding: 5px 10px;
+}
+.debug-info .text.true{
+  color: #1fff8b;
+}
+.debug-info .text.false{
+  color: #ff2e2e;
+}
+.debug-info-btn{
+  position: absolute;
+  z-index: 9998;
+  bottom: 160rpx;
+  left: 0;
+}
+
+.trtc-room-container .btn {
+  display: inline-block;
+  width: auto;
+  height: 60rpx;
+  min-height: 60rpx;
+  line-height: 60rpx;
+  font-size: 12px;
+  font-weight: normal;
+  padding: 0 10rpx;
+  color: #006eff;
+  background-color: #f2f2f2;
+  margin: 0 16rpx;
+}
+.trtc-room-container .btn.active{
+  color: #f2f2f2;
+  background-color: #006eff;
+}
+.trtc-room-container .btn-hover{
+  background-color: #d1d1d1;
+}
+
+.im-panel{
+  position: absolute;
+  z-index: 9;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: 90vw;
+  height: 320rpx;
+  top: 50vh;
+  left: 50vw;
+  transform: translate(-50%, -50%);
+  padding: 20rpx 0;
+  border-radius: 10rpx;
+  font-size: 12px;
+  /* bottom: 25vh; */
+  color: #fff;
+  background-color: rgba(0, 0, 0, 0.8);
+}
+.im-panel .close-btn {
+  position: absolute;
+  top: 0;
+  right: -3px;
+  padding: 5px 10px;
+  z-index: 99;
+}
+.message-panel-body{
+  width: 100%;
+  height: 80%;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.message-scroll-container{
+  height: 100%;
+  /* box-sizing: border-box;
+  padding: 0 20rpx; */
+}
+.message-list{
+  width: 100%;
+  box-sizing: border-box;
+  padding: 0 20rpx;
+  /* display: flex;
+  flex-direction: column; */
+}
+.message-item{
+  width: 100%;
+  /* height: 36rpx; */
+  /* padding: 0 20rpx; */
+  padding-bottom: 10rpx;
+  display: flex;
+  flex-direction: row;
+}
+.message-item .user-name{
+  width: 20%;
+  color: #2483ff;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.user-name.mine{
+  color: #ff7424;
+}
+.message-item .separate{
+  padding:0 5px;
+  color: #fff;
+}
+.message-item .message-content{
+  word-wrap:break-word; 
+  word-break:break-all;
+  padding-left: 20rpx;
+  position: relative;
+  max-width: 80%; 
+  box-sizing: border-box;
+}
+.message-content::after{
+  content: ':';
+  position: absolute;
+  left: 0;
+  top: 0;
+}
+.message-panel-bottom{
+  width: 100%;
+  height: 50rpx;
+  box-sizing: border-box;
+  padding: 0 20rpx 0;
+  margin-top: 20rpx;
+  display: flex;
+  flex-direction: row;
+}
+.message-input-container {
+  flex-grow: 1;
+}
+.message-input-container .message-input {
+  font-size: 12px;
+  padding-left: 20rpx;
+  border-radius: 10rpx;
+  height: 100%;
+  background-color: rgba(0,0,0,0.1);
+}
+.message-send-btn .btn{
+  margin-right: 0;
+  height: 50rpx;
+  min-height: 50rpx;
+  line-height: 50rpx;
+}
+
+.volume-animation{
+  position: absolute;
+  width: 80rpx;
+  height: 80rpx;
+  left: 0;
+  top: initial;
+  bottom: 20rpx;
+  z-index: 9;
+  /* transform: translate(-50%, 0); */
+}
+.volume-animation .image{
+  position: absolute;
+  width: 80rpx;
+  height: 80rpx;
+}
+.volume-animation .audio-active{
+  animation: viewlinear 1.5s linear infinite;
+} 
+@keyframes viewlinear {
+  
+  /** 第一种写法**/
+  0% {
+    height: 0;
+  }
+  100% {
+    height: 100%;
+  }
+}
+
+.none,
+.view-container.none,
+.template-grid .view-container.none,
+.template-1v1 .view-container.none{
+  display: none !important;
+}

+ 21 - 0
components/trtc-room/utils/compare-version.js

@@ -0,0 +1,21 @@
+export default function compareVersion(v1, v2) {
+  v1 = v1.split('.')
+  v2 = v2.split('.')
+  const len = Math.max(v1.length, v2.length)
+  while (v1.length < len) {
+    v1.push('0')
+  }
+  while (v2.length < len) {
+    v2.push('0')
+  }
+  for (let i = 0; i < len; i++) {
+    const num1 = parseInt(v1[i])
+    const num2 = parseInt(v2[i])
+    if (num1 > num2) {
+      return 1
+    } if (num1 < num2) {
+      return -1
+    }
+  }
+  return 0
+}

+ 50 - 0
components/trtc-room/utils/environment.js

@@ -0,0 +1,50 @@
+import compareVersion from './compare-version.js'
+const TAG_NAME = 'TRTC-ROOM'
+
+const env = wx ? wx : qq
+if (!env) {
+  console.error(TAG_NAME, '不支持当前小程序环境')
+}
+const systemInfo = env.getSystemInfoSync()
+const safeArea = systemInfo.safeArea
+if (systemInfo.system === 'iOS 13.3' || (systemInfo.model === 'iPhoneX' && systemInfo.system === 'iOS 13.3.1') ) {
+  // audio-volume-type = media
+  console.log('use media audio volume type')
+}
+console.log(TAG_NAME, 'SystemInfo', systemInfo)
+let isNewVersion
+if (typeof qq !== 'undefined') {
+  isNewVersion = true
+} else if (typeof wx !== 'undefined') {
+  if (compareVersion(systemInfo.version, '7.0.8') >= 0 || // mobile pc
+  (compareVersion(systemInfo.version, '2.4.0') >= 0 && compareVersion(systemInfo.version, '6.0.0') < 0) && // mac os
+  compareVersion(systemInfo.SDKVersion, '2.10.0') >= 0) {
+    isNewVersion = true
+  } else {
+    isNewVersion = false
+  }
+}
+
+export const IS_TRTC = isNewVersion
+export const IS_QQ = typeof qq !== 'undefined'
+export const IS_WX = typeof wx !== 'undefined'
+export const IS_IOS = /iOS/i.test(systemInfo.system)
+export const IS_ANDROID = /Android/i.test(systemInfo.system)
+export const IS_MAC = /mac/i.test(systemInfo.system)
+export const APP_VERSION = systemInfo.version
+export const LIB_VERSION = (function() {
+  if (systemInfo.SDKBuild) {
+    return systemInfo.SDKVersion + '-' + systemInfo.SDKBuild
+  }
+  return systemInfo.SDKVersion
+})()
+
+let isFullscreenDevie = false
+if (systemInfo.screenHeight > safeArea.bottom) {
+// if (/iphone\s{0,}x/i.test(systemInfo.model)) {
+  isFullscreenDevie = true
+}
+
+export const IS_FULLSCREEN_DEVICE = isFullscreenDevie
+
+console.log(TAG_NAME, 'APP_VERSION:', APP_VERSION, ' LIB_VERSION:', LIB_VERSION, ' is new version:', IS_TRTC)

+ 62 - 0
components/trtc-room/utils/event.js

@@ -0,0 +1,62 @@
+class Event {
+  on(event, fn, ctx) {
+    if (typeof fn !== 'function') {
+      console.error('listener must be a function')
+      return
+    }
+
+    this._stores = this._stores || {};
+    (this._stores[event] = this._stores[event] || []).push({ cb: fn, ctx: ctx })
+  }
+
+  emit(event) {
+    this._stores = this._stores || {}
+    let store = this._stores[event]
+    let args
+
+    if (store) {
+      store = store.slice(0)
+      args = [].slice.call(arguments, 1),
+      args[0] = {
+        eventCode: event,
+        data: args[0],
+      }
+      for (let i = 0, len = store.length; i < len; i++) {
+        store[i].cb.apply(store[i].ctx, args)
+      }
+    }
+  }
+
+  off(event, fn) {
+    this._stores = this._stores || {}
+
+    // all
+    if (!arguments.length) {
+      this._stores = {}
+      return
+    }
+
+    // specific event
+    const store = this._stores[event]
+    if (!store) return
+
+    // remove all handlers
+    if (arguments.length === 1) {
+      delete this._stores[event]
+      return
+    }
+
+    // remove specific handler
+    let cb
+    for (let i = 0, len = store.length; i < len; i++) {
+      cb = store[i].cb
+      if (cb === fn) {
+        store.splice(i, 1)
+        break
+      }
+    }
+    return
+  }
+}
+
+module.exports = Event

BIN
images/banjihz.png


BIN
images/ewmbj.png


BIN
images/finallytest.png


BIN
images/gift1.jpg


BIN
images/gift2.jpg


BIN
images/gift3.jpg


BIN
images/gift4.jpg


BIN
images/gift5.jpg


BIN
images/icon1.png


BIN
images/icon2.png


BIN
images/icon3.png


BIN
images/icon4.png


BIN
images/khzy_icon1.jpg


BIN
images/mb.png


BIN
images/nodata.png


BIN
images/redcloud.png


BIN
images/sc_img.png


BIN
images/scdj1.jpg


BIN
images/scdj2.jpg


BIN
images/scdj3.jpg


BIN
images/scdj4.jpg


BIN
images/spfm.jpg


BIN
images/xydj_bj.png


BIN
images/zbfm.png


BIN
images/zhfk_bj.png


BIN
images/zsbj.jpg


+ 0 - 1
miniprogram_npm/vant-weapp/action-sheet/index.d.ts

@@ -1 +0,0 @@
-export {};

+ 0 - 62
miniprogram_npm/vant-weapp/action-sheet/index.js

@@ -1,62 +0,0 @@
-import { VantComponent } from '../common/component';
-import { button } from '../mixins/button';
-import { openType } from '../mixins/open-type';
-VantComponent({
-  mixins: [button, openType],
-  props: {
-    show: Boolean,
-    title: String,
-    cancelText: String,
-    description: String,
-    round: {
-      type: Boolean,
-      value: true,
-    },
-    zIndex: {
-      type: Number,
-      value: 100,
-    },
-    actions: {
-      type: Array,
-      value: [],
-    },
-    overlay: {
-      type: Boolean,
-      value: true,
-    },
-    closeOnClickOverlay: {
-      type: Boolean,
-      value: true,
-    },
-    closeOnClickAction: {
-      type: Boolean,
-      value: true,
-    },
-    safeAreaInsetBottom: {
-      type: Boolean,
-      value: true,
-    },
-  },
-  methods: {
-    onSelect(event) {
-      const { index } = event.currentTarget.dataset;
-      const item = this.data.actions[index];
-      if (item && !item.disabled && !item.loading) {
-        this.$emit('select', item);
-        if (this.data.closeOnClickAction) {
-          this.onClose();
-        }
-      }
-    },
-    onCancel() {
-      this.$emit('cancel');
-    },
-    onClose() {
-      this.$emit('close');
-    },
-    onClickOverlay() {
-      this.$emit('click-overlay');
-      this.onClose();
-    },
-  },
-});

+ 0 - 8
miniprogram_npm/vant-weapp/action-sheet/index.json

@@ -1,8 +0,0 @@
-{
-  "component": true,
-  "usingComponents": {
-    "van-icon": "../icon/index",
-    "van-popup": "../popup/index",
-    "van-loading": "../loading/index"
-  }
-}

+ 0 - 67
miniprogram_npm/vant-weapp/action-sheet/index.wxml

@@ -1,67 +0,0 @@
-<wxs src="../wxs/utils.wxs" module="utils" />
-
-<van-popup
-  show="{{ show }}"
-  position="bottom"
-  round="{{ round }}"
-  z-index="{{ zIndex }}"
-  overlay="{{ overlay }}"
-  custom-class="van-action-sheet"
-  safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
-  close-on-click-overlay="{{ closeOnClickOverlay }}"
-  bind:close="onClickOverlay"
->
-  <view wx:if="{{ title }}" class="van-hairline--bottom van-action-sheet__header">
-    {{ title }}
-    <van-icon
-      name="close"
-      custom-class="van-action-sheet__close"
-      bind:click="onClose"
-    />
-  </view>
-  <view wx:if="{{ description }}" class="van-action-sheet__description">
-    {{ description }}
-  </view>
-  <view wx:if="{{ actions && actions.length }}">
-    <!-- button外包一层view,防止actions动态变化,导致渲染时button被打散 -->
-    <button
-      wx:for="{{ actions }}"
-      wx:key="index"
-      open-type="{{ item.openType }}"
-      style="{{ item.color ? 'color: ' + item.color : '' }}"
-      class="{{ utils.bem('action-sheet__item', { disabled: item.disabled || item.loading }) }} van-hairline--top {{ item.className || '' }}"
-      hover-class="van-action-sheet__item--hover"
-      data-index="{{ index }}"
-      bind:tap="onSelect"
-      bindgetuserinfo="bindGetUserInfo"
-      bindcontact="bindContact"
-      bindgetphonenumber="bindGetPhoneNumber"
-      binderror="bindError"
-      bindlaunchapp="bindLaunchApp"
-      bindopensetting="bindOpenSetting"
-      lang="{{ lang }}"
-      session-from="{{ sessionFrom }}"
-      send-message-title="{{ sendMessageTitle }}"
-      send-message-path="{{ sendMessagePath }}"
-      send-message-img="{{ sendMessageImg }}"
-      show-message-card="{{ showMessageCard }}"
-      app-parameter="{{ appParameter }}"
-    >
-      <block wx:if="{{ !item.loading }}">
-        {{ item.name }}
-        <text wx:if="{{ item.subname }}" class="van-action-sheet__subname" >{{ item.subname }}</text>
-      </block>
-      <van-loading wx:else custom-class="van-action-sheet__loading" size="20px" />
-    </button>
-  </view>
-  <slot />
-  <view
-    wx:if="{{ cancelText }}"
-    class="van-action-sheet__cancel"
-    hover-class="van-action-sheet__cancel--hover"
-    hover-stay-time="70"
-    bind:tap="onCancel"
-  >
-    {{ cancelText }}
-  </view>
-</van-popup>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
miniprogram_npm/vant-weapp/action-sheet/index.wxss


+ 0 - 1
miniprogram_npm/vant-weapp/area/index.d.ts

@@ -1 +0,0 @@
-export {};

+ 0 - 224
miniprogram_npm/vant-weapp/area/index.js

@@ -1,224 +0,0 @@
-import { VantComponent } from '../common/component';
-import { pickerProps } from '../picker/shared';
-const COLUMNSPLACEHOLDERCODE = '000000';
-VantComponent({
-  classes: ['active-class', 'toolbar-class', 'column-class'],
-  props: Object.assign(Object.assign({}, pickerProps), {
-    value: {
-      type: String,
-      observer(value) {
-        this.code = value;
-        this.setValues();
-      },
-    },
-    areaList: {
-      type: Object,
-      value: {},
-      observer: 'setValues',
-    },
-    columnsNum: {
-      type: null,
-      value: 3,
-      observer(value) {
-        this.setData({
-          displayColumns: this.data.columns.slice(0, +value),
-        });
-      },
-    },
-    columnsPlaceholder: {
-      type: Array,
-      observer(val) {
-        this.setData({
-          typeToColumnsPlaceholder: {
-            province: val[0] || '',
-            city: val[1] || '',
-            county: val[2] || '',
-          },
-        });
-      },
-    },
-  }),
-  data: {
-    columns: [{ values: [] }, { values: [] }, { values: [] }],
-    displayColumns: [{ values: [] }, { values: [] }, { values: [] }],
-    typeToColumnsPlaceholder: {},
-  },
-  mounted() {
-    setTimeout(() => {
-      this.setValues();
-    }, 0);
-  },
-  methods: {
-    getPicker() {
-      if (this.picker == null) {
-        this.picker = this.selectComponent('.van-area__picker');
-      }
-      return this.picker;
-    },
-    onCancel(event) {
-      this.emit('cancel', event.detail);
-    },
-    onConfirm(event) {
-      const { index } = event.detail;
-      let { value } = event.detail;
-      value = this.parseOutputValues(value);
-      this.emit('confirm', { value, index });
-    },
-    emit(type, detail) {
-      detail.values = detail.value;
-      delete detail.value;
-      this.$emit(type, detail);
-    },
-    // parse output columns data
-    parseOutputValues(values) {
-      const { columnsPlaceholder } = this.data;
-      return values.map((value, index) => {
-        // save undefined value
-        if (!value) return value;
-        value = JSON.parse(JSON.stringify(value));
-        if (!value.code || value.name === columnsPlaceholder[index]) {
-          value.code = '';
-          value.name = '';
-        }
-        return value;
-      });
-    },
-    onChange(event) {
-      const { index, picker, value } = event.detail;
-      this.code = value[index].code;
-      this.setValues().then(() => {
-        this.$emit('change', {
-          picker,
-          values: this.parseOutputValues(picker.getValues()),
-          index,
-        });
-      });
-    },
-    getConfig(type) {
-      const { areaList } = this.data;
-      return (areaList && areaList[`${type}_list`]) || {};
-    },
-    getList(type, code) {
-      const { typeToColumnsPlaceholder } = this.data;
-      let result = [];
-      if (type !== 'province' && !code) {
-        return result;
-      }
-      const list = this.getConfig(type);
-      result = Object.keys(list).map((code) => ({
-        code,
-        name: list[code],
-      }));
-      if (code) {
-        // oversea code
-        if (code[0] === '9' && type === 'city') {
-          code = '9';
-        }
-        result = result.filter((item) => item.code.indexOf(code) === 0);
-      }
-      if (typeToColumnsPlaceholder[type] && result.length) {
-        // set columns placeholder
-        const codeFill =
-          type === 'province'
-            ? ''
-            : type === 'city'
-            ? COLUMNSPLACEHOLDERCODE.slice(2, 4)
-            : COLUMNSPLACEHOLDERCODE.slice(4, 6);
-        result.unshift({
-          code: `${code}${codeFill}`,
-          name: typeToColumnsPlaceholder[type],
-        });
-      }
-      return result;
-    },
-    getIndex(type, code) {
-      let compareNum = type === 'province' ? 2 : type === 'city' ? 4 : 6;
-      const list = this.getList(type, code.slice(0, compareNum - 2));
-      // oversea code
-      if (code[0] === '9' && type === 'province') {
-        compareNum = 1;
-      }
-      code = code.slice(0, compareNum);
-      for (let i = 0; i < list.length; i++) {
-        if (list[i].code.slice(0, compareNum) === code) {
-          return i;
-        }
-      }
-      return 0;
-    },
-    setValues() {
-      const county = this.getConfig('county');
-      let { code } = this;
-      if (!code) {
-        if (this.data.columnsPlaceholder.length) {
-          code = COLUMNSPLACEHOLDERCODE;
-        } else if (Object.keys(county)[0]) {
-          code = Object.keys(county)[0];
-        } else {
-          code = '';
-        }
-      }
-      const province = this.getList('province');
-      const city = this.getList('city', code.slice(0, 2));
-      const picker = this.getPicker();
-      if (!picker) {
-        return;
-      }
-      const stack = [];
-      stack.push(picker.setColumnValues(0, province, false));
-      stack.push(picker.setColumnValues(1, city, false));
-      if (city.length && code.slice(2, 4) === '00') {
-        [{ code }] = city;
-      }
-      stack.push(
-        picker.setColumnValues(
-          2,
-          this.getList('county', code.slice(0, 4)),
-          false
-        )
-      );
-      return Promise.all(stack)
-        .catch(() => {})
-        .then(() =>
-          picker.setIndexes([
-            this.getIndex('province', code),
-            this.getIndex('city', code),
-            this.getIndex('county', code),
-          ])
-        )
-        .catch(() => {});
-    },
-    getValues() {
-      const picker = this.getPicker();
-      return picker ? picker.getValues().filter((value) => !!value) : [];
-    },
-    getDetail() {
-      const values = this.getValues();
-      const area = {
-        code: '',
-        country: '',
-        province: '',
-        city: '',
-        county: '',
-      };
-      if (!values.length) {
-        return area;
-      }
-      const names = values.map((item) => item.name);
-      area.code = values[values.length - 1].code;
-      if (area.code[0] === '9') {
-        area.country = names[1] || '';
-        area.province = names[2] || '';
-      } else {
-        area.province = names[0] || '';
-        area.city = names[1] || '';
-        area.county = names[2] || '';
-      }
-      return area;
-    },
-    reset(code) {
-      this.code = code || '';
-      return this.setValues();
-    },
-  },
-});

+ 0 - 0
miniprogram_npm/vant-weapp/area/index.json


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels