bill 4 éve
commit
79de797731
100 módosított fájl, 7941 hozzáadás és 0 törlés
  1. 14 0
      .gitignore
  2. 264 0
      app.js
  3. 113 0
      app.json
  4. 60 0
      app.wxss
  5. BIN
      common/font/iconfont.eot
  6. 1 0
      common/font/iconfont.js
  7. BIN
      common/font/iconfont.ttf
  8. BIN
      common/font/iconfont.woff
  9. BIN
      common/font/iconfont.woff2
  10. 225 0
      common/font/iconfont.wxss
  11. 0 0
      common/styles/index.wxss
  12. 65 0
      component/auth/api.js
  13. 85 0
      component/auth/auth.js
  14. 3 0
      component/auth/auth.json
  15. 20 0
      component/auth/auth.wxml
  16. 81 0
      component/auth/auth.wxss
  17. 30 0
      component/common/icon/index.js
  18. 5 0
      component/common/icon/index.json
  19. 1 0
      component/common/icon/index.wxml
  20. 4 0
      component/common/icon/index.wxss
  21. 23 0
      component/head/head.js
  22. 4 0
      component/head/head.json
  23. 4 0
      component/head/head.wxml
  24. 1 0
      component/head/head.wxss
  25. 1120 0
      component/image-cropper/image-cropper.js
  26. 3 0
      component/image-cropper/image-cropper.json
  27. 24 0
      component/image-cropper/image-cropper.wxml
  28. 123 0
      component/image-cropper/image-cropper.wxss
  29. 38 0
      component/show-empty-data/show-empty-data.js
  30. 4 0
      component/show-empty-data/show-empty-data.json
  31. 6 0
      component/show-empty-data/show-empty-data.wxml
  32. 37 0
      component/show-empty-data/show-empty-data.wxss
  33. 286 0
      component/sticky/index.js
  34. 4 0
      component/sticky/index.json
  35. 7 0
      component/sticky/index.wxml
  36. 20 0
      component/sticky/index.wxs
  37. 1 0
      component/sticky/index.wxss
  38. 25 0
      component/tarbar/tarbar.js
  39. 4 0
      component/tarbar/tarbar.json
  40. 8 0
      component/tarbar/tarbar.wxml
  41. 8 0
      component/tarbar/tarbar.wxss
  42. 85 0
      component/trtc-room/common/constants.js
  43. 346 0
      component/trtc-room/controller/user-controller.js
  44. 14 0
      component/trtc-room/libs/mta_analysis.js
  45. 1 0
      component/trtc-room/libs/tim-wx.js
  46. 33 0
      component/trtc-room/model/pusher.js
  47. 38 0
      component/trtc-room/model/stream.js
  48. 17 0
      component/trtc-room/model/user.js
  49. 76 0
      component/trtc-room/template/custom/custom.wxml
  50. 8 0
      component/trtc-room/template/custom/custom.wxss
  51. 2267 0
      component/trtc-room/trtc-room.js
  52. 4 0
      component/trtc-room/trtc-room.json
  53. 5 0
      component/trtc-room/trtc-room.wxml
  54. 13 0
      component/trtc-room/trtc-room.wxss
  55. 21 0
      component/trtc-room/utils/compare-version.js
  56. 50 0
      component/trtc-room/utils/environment.js
  57. 62 0
      component/trtc-room/utils/event.js
  58. 163 0
      component/voice/voice.js
  59. 6 0
      component/voice/voice.json
  60. 9 0
      component/voice/voice.wxml
  61. 20 0
      component/voice/voice.wxss
  62. 22 0
      config.js
  63. 121 0
      config/api.js
  64. 86 0
      custom-tab-bar/index.js
  65. 3 0
      custom-tab-bar/index.json
  66. 22 0
      custom-tab-bar/index.wxml
  67. 83 0
      custom-tab-bar/index.wxss
  68. 224 0
      dataReport/components/customer-report/index.js
  69. 6 0
      dataReport/components/customer-report/index.json
  70. 31 0
      dataReport/components/customer-report/index.wxml
  71. 41 0
      dataReport/components/customer-report/index.wxss
  72. 98 0
      dataReport/components/ec-canvas/defaultOption.js
  73. 250 0
      dataReport/components/ec-canvas/ec-canvas.js
  74. 4 0
      dataReport/components/ec-canvas/ec-canvas.json
  75. 4 0
      dataReport/components/ec-canvas/ec-canvas.wxml
  76. 4 0
      dataReport/components/ec-canvas/ec-canvas.wxss
  77. 22 0
      dataReport/components/ec-canvas/echarts.js
  78. 121 0
      dataReport/components/ec-canvas/wx-canvas.js
  79. 471 0
      dataReport/components/scene-report/scene-report.js
  80. 7 0
      dataReport/components/scene-report/scene-report.json
  81. 38 0
      dataReport/components/scene-report/scene-report.wxml
  82. 95 0
      dataReport/components/scene-report/scene-report.wxss
  83. 112 0
      dataReport/components/summary/summary.js
  84. 6 0
      dataReport/components/summary/summary.json
  85. 24 0
      dataReport/components/summary/summary.wxml
  86. 41 0
      dataReport/components/summary/summary.wxss
  87. 15 0
      dataReport/pages/data-report/data-report.js
  88. 8 0
      dataReport/pages/data-report/data-report.json
  89. 4 0
      dataReport/pages/data-report/data-report.wxml
  90. 19 0
      dataReport/pages/data-report/data-report.wxss
  91. BIN
      lib/wxParse/emojis/00.gif
  92. BIN
      lib/wxParse/emojis/01.gif
  93. BIN
      lib/wxParse/emojis/02.gif
  94. BIN
      lib/wxParse/emojis/03.gif
  95. BIN
      lib/wxParse/emojis/04.gif
  96. BIN
      lib/wxParse/emojis/05.gif
  97. BIN
      lib/wxParse/emojis/06.gif
  98. BIN
      lib/wxParse/emojis/07.gif
  99. BIN
      lib/wxParse/emojis/08.gif
  100. 0 0
      lib/wxParse/emojis/09.gif

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+# Windows
+[Dd]esktop.ini
+Thumbs.db
+$RECYCLE.BIN/
+
+# macOS
+.DS_Store
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+
+# Node.js
+node_modules/

+ 264 - 0
app.js

@@ -0,0 +1,264 @@
+const util = require('./utils/util.js');
+const api = require('./config/api');
+
+
+! function () {
+    var PageTmp = Page;
+    Page = function (pageConfig) {
+        // 设置全局默认分享
+        let tempShare = pageConfig.onShareAppMessage
+        pageConfig.onShareAppMessage = function(...args) {
+            let pages = getCurrentPages()
+            let path = pages[pages.length - 1].route;
+            let defconfig = {
+                title: '【好友推荐】一起来云逛吧~',
+                desc: '【好友推荐】一起来云逛吧~',
+                path: path,
+                imageUrl: 'https://houseoss.4dkankan.com/4dHouse/miniprogram/aaa.jpg',
+            };
+
+            let currConfig = tempShare && tempShare.call(this, ...args)
+            let config = defconfig
+            if (currConfig) {
+                config = {
+                    ...defconfig,
+                    ...currConfig,
+                }
+            }
+
+            return config
+        }
+
+        let onShow = pageConfig.onShow
+        pageConfig.onShow = function(...args) {
+            getApp().setLoginProps(true)
+
+            return onShow.call(this, ...args)
+        }
+
+        PageTmp(pageConfig);
+    };
+}();
+
+const voiceCbs = []
+const voiceProps = {
+    force: true,
+    noMute: false,
+    pullUrls: [],
+    pushUrl: false,
+}
+const loginCbs = []
+const loginProps = {
+    isLogin: true
+};
+
+App({
+    onShareAppMessage() {
+        console.log('--------------------')
+    },
+    onLaunch: function () {
+        //获取小程序更新机制兼容
+        if (wx.canIUse('getUpdateManager')) {
+            const updateManager = wx.getUpdateManager()
+            updateManager.onCheckForUpdate(function (res) {
+                // 请求完新版本信息的回调
+                if (res.hasUpdate) {
+                    updateManager.onUpdateReady(function () {
+                        wx.showModal({
+                            title: '更新提示',
+                            content: '新版本已经准备好,是否重启应用?',
+                            success: function (res) {
+                                if (res.confirm) {
+                                    // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+                                    updateManager.applyUpdate()
+                                }
+                            }
+                        })
+                    })
+                    updateManager.onUpdateFailed(function () {
+                        // 新的版本下载失败
+                        wx.showModal({
+                            title: '已经有新版本了哟~',
+                            content: '新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~',
+                        })
+                    })
+                }
+            })
+        } else {
+            // 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
+            wx.showModal({
+                title: '提示',
+                content: '当前微信版本过低,无法更好体验程序,请升级到最新微信版本后重试。'
+            })
+        }
+        this.onGetSysInfo()
+    },
+    globalData: {
+        voiceProps,
+        loginProps,
+        userInfo: {
+            nickName: 'Hi,游客',
+            userName: '点击去登录',
+            avatarUrl: 'https://platform-wxmall.oss-cn-beijing.aliyuncs.com/upload/20180727/150547696d798c.png'
+        },
+        token: '',
+        userCoupon: 'NO_USE_COUPON', //默认不适用优惠券
+        courseCouponCode: {}, //购买课程的时候优惠券信息
+        isiPhoneX: false,
+        isLogin: false
+    },
+
+    addVoicePropsListener(cb) {
+        voiceCbs.push(cb)
+    },
+
+    removeVoicePropsListener(cb) {
+        voiceCbs.splice(voiceCbs.indexOf(cb), 1)
+    },
+    setVoiceProps(props) {
+        console.log(props, 'props')
+        Object.keys(props).forEach(k => {
+            voiceProps[k] = props[k]
+        })
+        voiceCbs.forEach(cb => cb(props))
+    },
+
+    addLoginListener(cb) {
+        loginCbs.push(cb)
+    },
+
+    removeLoginListener(cb) {
+        loginCbs.splice(loginCbs.indexOf(cb), 1)
+    },
+
+    setLoginProps(props) {
+        loginProps.isLogin = props
+        loginCbs.forEach(cb => cb())
+    },
+
+    onGetSysInfo() {
+        // 先缓存获取
+        let isiPhoneX = wx.getStorageSync('isiPhoneX') || false
+        // 缓存没有 再获取
+        if (!isiPhoneX) {
+            wx.getSystemInfo({
+                success: res => {
+                    // 手机品牌
+                    let modelmes = res.model;
+                    console.log('mode---------------lmes', modelmes)
+                    // 如果是 X,XS,XR,XS MAX 均可适配
+                    if (modelmes.indexOf('iPhone X') != -1) {
+                        // 存储型号
+                        this.globalData.isiPhoneX = true
+                        wx.setStorageSync('isiPhoneX', true)
+                        // 加入回调
+                        this.sysCallback && this.sysCallback()
+                    }
+                },
+            })
+        } else {
+            this.globalData.isiPhoneX = isiPhoneX
+        }
+    },
+    // 下拉刷新
+    onPullDownRefresh: function () {
+        // 显示顶部刷新图标
+        wx.showNavigationBarLoading();
+        var that = this;
+        // 隐藏导航栏加载框
+        wx.hideNavigationBarLoading();
+        // 停止下拉动作
+        wx.stopPullDownRefresh();
+
+        this.checkNetStatu()
+    },
+    // 更新小程序
+    updateManager: function () {
+        //获取系统信息 客户端基础库
+        wx.getSystemInfo({
+            success: function (res) {
+                //基础库版本比较,版本更新必须是1.9.90以上
+                const v = util.compareVersion(res.SDKVersion, '1.9.90');
+                if (v > 0) {
+                    const manager = wx.getUpdateManager();
+                    manager.onCheckForUpdate(function (res) {
+                        // 请求完新版本信息的回调
+                        //console.log(res.hasUpdate);
+                    });
+                    manager.onUpdateReady(function () {
+                        wx.showModal({
+                            title: '更新提示',
+                            content: '新版本已经准备好,是否重启应用?',
+                            success: function (res) {
+                                if (res.confirm) {
+                                    // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+                                    manager.applyUpdate();
+                                }
+                            }
+                        })
+                    });
+                    manager.onUpdateFailed(function () {
+                        // 新的版本下载失败
+                    });
+                } else {
+                    // wx.showModal({
+                    //   title: '温馨提示',
+                    //   content: '当前微信版本过低,无法更好体验程序,请升级到最新微信版本后重试。'
+                    // })
+                }
+            },
+        })
+    },
+
+    getCurrentPages: function () {
+        var pages = getCurrentPages(); //获取加载的页面
+        var currentPage = pages[pages.length - 1]; //获取当前页面的对象
+        var url = currentPage.route; //当前页面url
+        var options = currentPage.options; //获取url中所带的参数
+        //拼接url的参数
+        var currentPage = url + '?';
+        for (var key in options) {
+            var value = options[key]
+            currentPage += key + '=' + value + '&';
+        }
+        currentPage = currentPage.substring(0, currentPage.length - 1);
+        return currentPage;
+    },
+
+
+
+    async checkNetStatu() {
+        if (!(await util.checkNetStatu())) {
+            return wx.redirectTo({
+                url: '/pages/offline/offline?url=' + decodeURI(this.getCurrentPages()),
+            })
+        }
+    },
+
+    updateCardCount() {
+        util.request(api.CartList).then((res) => {
+            //显示红字,badge
+            if (!res.data || !res.data.cartTotal) {
+                return
+            }
+            // global.setTabBarBadge({
+            //     index: 2,
+            //     text: res.data.cartTotal.goodsCount ,
+            // });
+            if (res.data.cartTotal.goodsCount) {
+                wx.setTabBarBadge({
+                    index: 1,
+                    text: res.data.cartTotal.goodsCount + '',
+                });
+            } else {
+                wx.removeTabBarBadge({
+                    index: 1,
+                });
+            }
+        })
+    },
+    onLaunch() {
+        console.error('第一次进来')
+        wx.setStorageSync('lastSceneNum', null)
+    }
+})

+ 113 - 0
app.json

@@ -0,0 +1,113 @@
+{
+  "pages": [
+    "pages/index/index",
+    "pages/user/user",
+    "pages/webview/index",
+    "pages/discover/discover",
+    "pages/ncategory/category",
+    "pages/ucenter/index/index",
+    "pages/catalog/catalog",
+    "pages/coupon/coupon",
+    "pages/offline/offline",
+    "pages/category/category",
+    "pages/brandDetail/brandDetail",
+    "pages/coupon/info/info",
+    "pages/newGoods/newGoods",
+    "pages/hotGoods/hotGoods",
+    "pages/ucenter/address/address",
+    "pages/ucenter/addressAdd/addressAdd",
+    "pages/ucenter/footprint/footprint",
+    "pages/ucenter/order/order",
+    "pages/ucenter/orderDetail/orderDetail",
+    "pages/ucenter/feedback/feedback",
+    "pages/ucenter/coupon/coupon",
+    "pages/ucenter/collect/collect",
+    "pages/auth/login/login",
+    "pages/auth/register/register",
+    "pages/auth/reset/reset",
+    "pages/pay/pay",
+    "pages/payResult/payResult",
+    "pages/topic/topic",
+    "pages/comment/comment",
+    "pages/commentPost/commentPost",
+    "pages/topicComment/topicComment",
+    "pages/brand/brand",
+    "pages/search/search",
+    "pages/cart/cart",
+    "pages/shopping/checkout/checkout",
+    "pages/shopping/address/address",
+    "pages/shopping/addressAdd/addressAdd",
+    "pages/goods/goods",
+    "pages/topicDetail/topicDetail",
+    "pages/auth/mobile/mobile",
+    "pages/shopping/selCoupon/selCoupon",
+    "pages/ucenter/help/help",
+    "pages/ucenter/helpInfo/helpInfo",
+    "pages/auth/btnAuth/btnAuth",
+    "pages/logistics/index",
+    "pages/shared/shared",
+    "pages/share",
+    "pages/godis/godis",
+    "component/auth/auth",
+    "pages/user/imageCropper"
+  ],
+  "window": {
+    "backgroundTextStyle": "dark",
+    "navigationBarBackgroundColor": "#fff",
+    "navigationBarTitleText": "杭州天门科技有限公司",
+    "navigationBarTextStyle": "black",
+    "enablePullDownRefresh": true
+  },
+  "subpackages": [
+    {
+      "root": "dataReport",
+      "pages": [
+        "pages/data-report/data-report"
+      ],
+      "plugins": {
+        "ykfchat": {
+          "version": "2.0.7",
+          "provider": "wx1fdc8c9f3a3a05e4"
+        }
+      },
+      "independent": false
+    }
+  ],
+  "tabBar": {
+    "backgroundColor": "#fff",
+    "borderStyle": "black",
+    "selectedColor": "#ED5D18",
+    "color": "#666C7D",
+    "list": [
+      {
+        "pagePath": "pages/index/index",
+        "iconPath": "static/images/hbtab_home@2x.png",
+        "selectedIconPath": "static/images/btab_home@2x.png",
+        "text": "首页"
+      },
+      {
+        "pagePath": "pages/cart/cart",
+        "iconPath": "static/images/hbtab_cart@2x.png",
+        "selectedIconPath": "static/images/btab_cart@2x.png",
+        "text": "购物车"
+      },
+      {
+        "pagePath": "pages/ucenter/index/index",
+        "iconPath": "static/images/hbtab_my@2x.png",
+        "selectedIconPath": "static/images/btab_my@2x.png",
+        "text": "我的"
+      }
+    ]
+  },
+  "networkTimeout": {
+    "request": 10000,
+    "downloadFile": 10000
+  },
+  "debug": true,
+  "sitemapLocation": "sitemap.json",
+  "usingComponents": {
+    "lip-sync": "/component/voice/voice",
+    "mp-sticky": "/component/sticky/index",
+    "auth": "/component/auth/auth"
+  }
+}

+ 60 - 0
app.wxss

@@ -0,0 +1,60 @@
+/**app.wxss**/
+page{
+  /* padding-bottom: 80rpx; */
+}
+
+.container {
+  box-sizing: border-box;
+  background-color: #f4f4f4;
+  font-family: PingFangSC-Light,helvetica,'Heiti SC';
+} 
+
+
+.isIPx{
+  bottom:64rpx!important;
+}
+
+.isIPxBottom{
+  padding-bottom:64rpx!important;
+}
+
+
+.buttomPart{
+  bottom: 80rpx!important;
+}
+
+view,text,navigator{
+  box-sizing: border-box;
+  padding:0;
+  margin:0;
+}
+
+view,text{
+  font-family: PingFangSC-Light,helvetica,'Heiti SC';
+  /* font-size: 29rpx; */
+  color: #131D34;
+}
+
+button::after {
+  display: none;
+}
+
+.display-none {
+  display: none !important;
+}
+::-webkit-scrollbar {
+  width: 5px;
+  background-color: #f5f5f5;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: #999;
+}
+
+.theme-font{
+  color: #ED5D18;
+}
+
+.theme-background{
+  background-color: #ED5D18;
+}

BIN
common/font/iconfont.eot


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
common/font/iconfont.js


BIN
common/font/iconfont.ttf


BIN
common/font/iconfont.woff


BIN
common/font/iconfont.woff2


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 225 - 0
common/font/iconfont.wxss


+ 0 - 0
common/styles/index.wxss


+ 65 - 0
component/auth/api.js

@@ -0,0 +1,65 @@
+const tempId = 'klhtcMH_MixbhY2AqKjZ6sl7y51UkpWJ4Xj4P4Cn9U0'
+
+export const getSubOpen = () => {
+  let userInfo = wx.getStorageSync('userInfo');
+  if (!userInfo.isAdmin) {
+    return;
+  }
+  
+  wx.getSetting({
+    withSubscriptions: true,
+    success: res => {
+      let itemSettings = res.subscriptionsSetting
+      if (!itemSettings || !itemSettings[tempId] || itemSettings[tempId] === 'reject') {
+        wx.showModal({
+          title: '温馨提示',
+          content: '您是工作人员,请允许消息推送',
+          showCancel: true, //是否显示取消按钮
+          cancelText: "取消", //默认是“取消”
+          confirmText: "确定", //默认是“确定”
+          success: (res) => {
+            authMsg() 
+          }
+        })
+      }
+    }
+  })
+}
+
+export const authMsg = () => {
+  wx.requestSubscribeMessage({
+    tmplIds: [tempId],
+    success(res) {
+      console.log('成功订阅消息', res)
+      if (res[tempId] === 'reject') {
+        rejectAuth()
+      }
+    },
+    fail: (e) => {
+      console.error(e)
+      if (e.errCode === 20004) {
+        rejectAuth()
+      }
+    }
+  })
+}
+
+export const rejectAuth = () => {
+  wx.showModal({
+    title: '温馨提示',
+    content: '您关闭了授权,将跳转到设置页面,请打开VR带看提醒开关',
+    showCancel: true, //是否显示取消按钮
+    cancelText: "取消", //默认是“取消”
+    confirmText: "确定", //默认是“确定”
+    success: (res) => {
+      wx.openSetting({
+        withSubscriptions: true,
+        success: (res) => {
+          if (!res.subscriptionsSetting.mainSwitch) {
+            this.getSubOpen()
+          }
+        }
+      })
+    }
+  })
+}

+ 85 - 0
component/auth/auth.js

@@ -0,0 +1,85 @@
+// component/auth/auth.js
+const api = require('../../config/api')
+const util = require('../../utils/util.js');
+import {getSubOpen} from './api'
+
+Component({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    show: false,
+    canIUse: wx.canIUse('button.open-type.getUserInfo')
+  },
+  
+  attached() {
+    this.callback = () => {
+      this.setData({show: !getApp().globalData.loginProps.isLogin})
+
+      let pages = getCurrentPages()
+      let currPage = pages[pages.length - 1].route
+      if (currPage === 'pages/discover/discover') {
+        this.setData({bottom: '50px'})
+      } else {
+        this.setData({bottom: 0})
+      }
+    }
+    this.callback()
+    getApp().addLoginListener(this.callback)
+
+    getSubOpen()
+  },
+  detached() {
+    getApp().removeLoginListener(this.callback)
+  },
+  methods: {
+    quitHandle: function() {
+      getApp().setLoginProps(true)
+    },
+    getCode: function() {
+      return new Promise(r => {
+        wx.login({
+          success: function(res) {
+            if (res.code) {
+              r(res.code)
+            }
+          }
+        });
+      })
+    },
+    authLogin() {
+      this.triggerEvent('login')
+    },
+    bindGetUserInfo: async function(e) {
+      let code = await this.getCode()
+      //登录远程服务器
+      util.request(api.AuthLoginByWeixin, {
+        code: code,
+        userInfo: e.detail
+      }, 'POST', 'application/json').then(res => {
+        if (res.errno === 0) {
+          //存储用户信息
+          res.data.userInfo.userId = res.data.userId
+          res.data.userInfo.sessionKey = res.data.sessionKey
+          console.log('-------', res.data.userInfo)
+          wx.setStorageSync('userInfo', res.data.userInfo);
+          wx.setStorageSync('token', res.data.token);
+          wx.setStorageSync('userId', res.data.userId);
+          wx.setStorageSync('isLogin', true);
+          getApp().setLoginProps(true)
+          this.authLogin()
+          getSubOpen()
+          
+        } else {
+          // util.showErrorToast(res.errmsg)
+          // wx.showModal({
+          //   title: '提示',
+          //   content: res.errmsg,
+          //   showCancel: false
+          // });
+        }
+      });
+    },
+  }
+})

+ 3 - 0
component/auth/auth.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 20 - 0
component/auth/auth.wxml

@@ -0,0 +1,20 @@
+<!--component/auth/auth.wxml-->
+<view wx:if="{{show}}">
+  <cover-view class="auth-bg" bindtap="quitHandle">
+  </cover-view>
+    <cover-view class="auth-layer" style="bottom: {{bottom}}">
+      <cover-view class="title">授权登录</cover-view>
+      <cover-view class="content">
+        <cover-image src="/static/images/img_empower_logo@2x.png"></cover-image>
+        <cover-view class="text">杭州天门科技有限公司申请获取以下权限</cover-view>
+        <cover-view class="xian"></cover-view>
+        <cover-view class="authul">
+          <cover-view class="authli">
+            ·您的公开信息(昵称、头像等)
+          </cover-view>
+        </cover-view>
+      </cover-view>
+
+      <button class="button" wx:if="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">允许使用</button>
+    </cover-view>
+</view>

+ 81 - 0
component/auth/auth.wxss

@@ -0,0 +1,81 @@
+/* component/auth/auth.wxss */
+
+.auth-layer {
+  position: fixed;
+  height: calc(612rpx + env(safe-area-inset-bottom));
+  background: #FFFFFF;
+  border-radius: 8px 8px 0px 0px;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  z-index: 99999999999999;
+  padding: 52rpx 36rpx 32rpx;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  padding-bottom: calc(env(safe-area-inset-bottom) + 36rpx);
+}
+
+
+.auth-bg {
+  width: 100vw;
+  height: 100vh;
+  background-color: rgba(0, 0, 0, 0.3);
+  z-index: 9999999999;
+  position: fixed;
+  left: 0;
+  top: 0;
+}
+
+.auth-layer .title {
+  font-size: 30rpx;
+  color: #111111;
+  line-height: 42rpx;
+}
+
+.auth-layer .content {
+  height: 406rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.auth-layer .content cover-image {
+  width: 165rpx;
+  height: 165rpx;
+}
+
+.auth-layer .content .text {
+  font-size: 26rpx;
+  color: #111111;
+  padding: 28rpx 0 36rpx;
+}
+
+.xian {
+  align-self: stretch;
+  height: 1px;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+
+.auth-layer .authul {
+  align-self: stretch;
+  flex: 1;
+}
+
+.auth-layer .authul .authli {
+  text-align: center;
+  font-size: 26rpx;
+  color: #79868F;
+  margin-top: 30rpx;
+}
+
+.auth-layer .button {
+  line-height: 80rpx;
+  font-size: 30rpx;
+  color: #fff;
+  background: #07C563;
+  opacity: 1;
+  border-radius: 4px;
+  width: 100%;
+  text-align: center;
+}

+ 30 - 0
component/common/icon/index.js

@@ -0,0 +1,30 @@
+// component/shared/icon/icon.js
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+    icon: {
+      type: String,
+      value: ''
+    },
+    size: {
+      type: [String, Array],
+      value: '16'
+    }
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+  }
+})

+ 5 - 0
component/common/icon/index.json

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

+ 1 - 0
component/common/icon/index.wxml

@@ -0,0 +1 @@
+<text class="iconfont icon{{icon}}" style="font-size: {{size || 16}}px"></text>

+ 4 - 0
component/common/icon/index.wxss

@@ -0,0 +1,4 @@
+@import './../../../common/font/iconfont.wxss';
+.iconfont {
+  color: inherit;
+}

+ 23 - 0
component/head/head.js

@@ -0,0 +1,23 @@
+// component/head/head.js
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+  }
+})

+ 4 - 0
component/head/head.json

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

+ 4 - 0
component/head/head.wxml

@@ -0,0 +1,4 @@
+<!--component/head/head.wxml-->
+<view class="">
+  <image></image>
+</view>

+ 1 - 0
component/head/head.wxss

@@ -0,0 +1 @@
+/* component/head/head.wxss */

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1120 - 0
component/image-cropper/image-cropper.js


+ 3 - 0
component/image-cropper/image-cropper.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 24 - 0
component/image-cropper/image-cropper.wxml

@@ -0,0 +1,24 @@
+  <view class='image-cropper' catchtouchmove='_preventTouchMove'>
+    <view class='main' bindtouchend="_cutTouchEnd" bindtouchstart="_cutTouchStart" bindtouchmove="_cutTouchMove" bindtap="_click">
+      <view class='content'>
+        <view class='content_top bg_gray {{_flag_bright?"":"bg_black"}}' style="height:{{cut_top}}px;transition-property:{{_cut_animation?'':'background'}}"></view>
+        <view class='content_middle' style="height:{{height}}px;">
+          <view class='content_middle_left bg_gray {{_flag_bright?"":"bg_black"}}' style="width:{{cut_left}}px;transition-property:{{_cut_animation?'':'background'}}"></view>
+          <view class='content_middle_middle' style="width:{{width}}px;height:{{height}}px;transition-duration: .3s;transition-property:{{_cut_animation?'':'background'}};">
+            <view class="border border-top-left"></view>
+            <view class="border border-top-right"></view>
+            <view class="border border-right-top"></view>
+            <view class="border border-right-bottom"></view>
+            <view class="border border-bottom-right"></view>
+            <view class="border border-bottom-left"></view>
+            <view class="border border-left-bottom"></view>
+            <view class="border border-left-top"></view>
+          </view>
+          <view class='content_middle_right bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view>
+        </view>
+        <view class='content_bottom bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view>
+      </view>
+      <image bindload="imageLoad" bindtouchstart="_start" bindtouchmove="_move" bindtouchend="_end" style="width:{{img_width ? img_width + 'px' : 'auto'}};height:{{img_height ? img_height + 'px' : 'auto'}};transform:translate3d({{_img_left-img_width/2}}px,{{_img_top-img_height/2}}px,0) scale({{scale}}) rotate({{angle}}deg);transition-duration:{{_cut_animation?.4:0}}s;" class='img' src='{{imgSrc}}'></image>
+    </view>
+    <canvas canvas-id='image-cropper' disable-scroll="true" style="width:{{_canvas_width * export_scale}}px;height:{{_canvas_height * export_scale}}px;left:{{canvas_left}}px;top:{{canvas_top}}px" class='image-cropper-canvas'></canvas>
+  </view>

+ 123 - 0
component/image-cropper/image-cropper.wxss

@@ -0,0 +1,123 @@
+.image-cropper{
+  background:rgba(14, 13, 13,.8);
+  position: fixed;
+  top:0;
+  left:0;
+  width:100vw;
+  height:100vh;
+  z-index: 1;
+}
+.main{
+  position: absolute;
+  width:100vw;
+  height:100vh;
+  overflow: hidden;
+}
+.content{
+  z-index: 9;
+  position: absolute;
+  width:100vw;
+  height:100vh;
+  display: flex;
+  flex-direction:column;
+  pointer-events:none;
+}
+.bg_black{
+  background: rgba(0, 0, 0, 0.8)!important;
+}
+.bg_gray{
+  background: rgba(0, 0, 0, 0.45);
+  transition-duration: .35s;
+}
+.content>.content_top{
+  pointer-events:none;
+}
+.content>.content_middle{
+  display: flex;
+  height: 200px;
+  width:100%;
+}
+.content_middle_middle{
+  width:200px;
+  box-sizing:border-box;
+  position: relative;
+  transition-duration: .3s;
+}
+.content_middle_right{
+  flex: auto;
+}
+.content>.content_bottom{
+  flex: auto;
+}
+.image-cropper .img{
+  z-index: 2;
+  top:0;
+  left:0;
+  position: absolute;
+  border:none;
+  width:100%;
+  backface-visibility: hidden;
+  transform-origin:center;
+}
+.image-cropper-canvas{
+  position: fixed;
+  background: white;
+  width:150px;
+  height:150px;
+  z-index: 10;
+  top:-200%;
+  pointer-events:none;
+}
+.border{
+  background: white;
+  pointer-events:auto;
+  position:absolute;
+}
+.border-top-left{
+  left:-2.5px;
+  top:-2.5px;
+  height:2.5px;
+  width:33rpx;
+}
+.border-top-right{
+  right:-2.5px;
+  top:-2.5px;
+  height:2.5px;
+  width:33rpx;
+}
+.border-right-top{
+  top:-1px;
+  width:2.5px;
+  height:30rpx;
+  right:-2.5px;
+}
+.border-right-bottom{
+  width:2.5px;
+  height:30rpx;
+  right:-2.5px;
+  bottom:-1px;
+}
+.border-bottom-left{
+  height:2.5px;
+  width:33rpx;
+  bottom:-2.5px;
+  left:-2.5px;
+}
+.border-bottom-right{
+  height:2.5px;
+  width:33rpx;
+  bottom:-2.5px;
+  right:-2.5px;
+}
+.border-left-top{
+  top:-1px;
+  width:2.5px;
+  height:30rpx;
+  left:-2.5px;
+}
+.border-left-bottom{
+  width:2.5px;
+  height:30rpx;
+  left:-2.5px;
+  bottom:-1px;
+}

+ 38 - 0
component/show-empty-data/show-empty-data.js

@@ -0,0 +1,38 @@
+// component/show-empty-data/show-empty-data.js
+var util = require('../../utils/util.js');
+
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+    showType: {
+      type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)
+      value: '', // 属性初始值(可选),如果未指定则会根据类型选择一个
+      observer: '_courseChange'
+    },
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    imgServer: util.imgServer
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+    /**
+      * 团转到首页
+      */
+    gotoHome: function (event) {
+      wx.reLaunch({
+        url: '../index/index'
+      })
+    }
+
+  }
+})

+ 4 - 0
component/show-empty-data/show-empty-data.json

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

+ 6 - 0
component/show-empty-data/show-empty-data.wxml

@@ -0,0 +1,6 @@
+<!--component/show-empty-data/show-empty-data.wxml-->
+ <view class='no-course'>
+    <image class='img' src="{{imgServer + '/my_course_empty.png'}}"></image>
+    <text class='desc' wx:if="{{showType == 'NO_COUPON'}}">您还没有优惠券~</text>
+    <!-- <button class='btn' bindtap='gotoHome' wx:if="{{showType == 'NO_COUPON'}}">去获取</button> -->
+  </view>

+ 37 - 0
component/show-empty-data/show-empty-data.wxss

@@ -0,0 +1,37 @@
+/* component/show-empty-data/show-empty-data.wxss */
+
+.no-course {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+}
+
+.no-course .img {
+  margin-top: 116rpx;
+  width: 597rpx;
+  height: 467rpx;
+  margin-bottom: 60rpx;
+}
+
+.no-course .desc{
+  font-size: 30rpx;
+  line-height: 40rpx;
+  color: #333;
+   font-weight: bold;
+}
+
+.no-course .btn {
+  display: flex;
+  background-color: #fff;
+  border: 1rpx solid #d2d2d2;
+  font-size: 28rpx;
+  border-radius: 30rpx;
+  width: 200rpx;
+  height: 60rpx;
+  justify-content: center;
+  align-items: center;
+  color: #333;
+  margin-top: 20rpx;
+}

+ 286 - 0
component/sticky/index.js

@@ -0,0 +1,286 @@
+module.exports =
+/******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// define __esModule on exports
+/******/ 	__webpack_require__.r = function(exports) {
+/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ 		}
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
+/******/ 	};
+/******/
+/******/ 	// create a fake namespace object
+/******/ 	// mode & 1: value is a module id, require it
+/******/ 	// mode & 2: merge all properties of value into the ns
+/******/ 	// mode & 4: return value when already ns object
+/******/ 	// mode & 8|1: behave like require
+/******/ 	__webpack_require__.t = function(value, mode) {
+/******/ 		if(mode & 1) value = __webpack_require__(value);
+/******/ 		if(mode & 8) return value;
+/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ 		var ns = Object.create(null);
+/******/ 		__webpack_require__.r(ns);
+/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ 		return ns;
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 8);
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ 8:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var selectQuery = __webpack_require__(9);
+var target = '.weui-sticky';
+Component({
+    options: {
+        addGlobalClass: true,
+        pureDataPattern: /^_/,
+        multipleSlots: true
+    },
+    behaviors: [selectQuery],
+    properties: {
+        offsetTop: {
+            type: Number,
+            value: 0
+        },
+        zIndex: {
+            type: Number,
+            value: 99
+        },
+        disabled: {
+            type: Boolean,
+            value: false
+        },
+        container: {
+            type: null
+        }
+    },
+    data: {
+        fixed: false,
+        height: 0,
+        _attached: false,
+        _containerHeight: 0
+    },
+    observers: {
+        disabled: function disabled(newVal) {
+            if (!this._attached) return;
+            newVal ? this.disconnectObserver() : this.initObserver();
+        },
+        container: function container(newVal) {
+            if (typeof newVal !== 'function' || !this.data.height) return;
+            this.observerContainer();
+        }
+    },
+    lifetimes: {
+        attached: function attached() {
+            this.data._attached = true;
+            if (!this.data.disabled) this.initObserver();
+        },
+        detached: function detached() {
+            this.data._attached = false;
+            this.disconnectObserver();
+        }
+    },
+    methods: {
+        getContainerRect: function getContainerRect() {
+            var nodesRef = this.data.container();
+            return new Promise(function (resolve) {
+                return nodesRef.boundingClientRect(resolve).exec();
+            });
+        },
+        initObserver: function initObserver() {
+            var _this = this;
+
+            this.disconnectObserver();
+            this.getRect(target).then(function (rect) {
+                _this.setData({
+                    height: rect.height
+                });
+                _this.observerContent();
+                _this.observerContainer();
+            });
+        },
+        disconnectObserver: function disconnectObserver(observerName) {
+            if (observerName) {
+                var observer = this[observerName];
+                observer && observer.disconnect();
+            } else {
+                this.contentObserver && this.contentObserver.disconnect();
+                this.containerObserver && this.containerObserver.disconnect();
+            }
+        },
+        observerContent: function observerContent() {
+            var _this2 = this;
+
+            var offsetTop = this.data.offsetTop;
+
+            this.disconnectObserver('contentObserver');
+            var contentObserver = this.createIntersectionObserver({
+                thresholds: [1],
+                initialRatio: 1
+            });
+            contentObserver.relativeToViewport({
+                top: -offsetTop
+            });
+            contentObserver.observe(target, function (res) {
+                if (_this2.data.disabled) return;
+                _this2.setFixed(res.boundingClientRect.top);
+            });
+            this.contentObserver = contentObserver;
+        },
+        observerContainer: function observerContainer() {
+            var _this3 = this;
+
+            var _data = this.data,
+                container = _data.container,
+                height = _data.height,
+                offsetTop = _data.offsetTop;
+
+            if (typeof container !== 'function') return;
+            this.disconnectObserver('containerObserver');
+            this.getContainerRect().then(function (rect) {
+                _this3.getRect(target).then(function (contentRect) {
+                    var _contentTop = contentRect.top;
+                    var _containerTop = rect.top;
+                    var _containerHeight = rect.height;
+                    var _relativeTop = _contentTop - _containerTop;
+                    var containerObserver = _this3.createIntersectionObserver({
+                        thresholds: [1],
+                        initialRatio: 1
+                    });
+                    containerObserver.relativeToViewport({
+                        top: _containerHeight - height - offsetTop - _relativeTop
+                    });
+                    containerObserver.observe(target, function (res) {
+                        if (_this3.data.disabled) return;
+                        _this3.setFixed(res.boundingClientRect.top);
+                    });
+                    _this3.data._relativeTop = _relativeTop;
+                    _this3.data._containerHeight = _containerHeight;
+                    _this3.containerObserver = containerObserver;
+                });
+            });
+        },
+        setFixed: function setFixed(top) {
+            var _data2 = this.data,
+                height = _data2.height,
+                _containerHeight = _data2._containerHeight,
+                _relativeTop = _data2._relativeTop,
+                offsetTop = _data2.offsetTop;
+
+            var fixed = _containerHeight && height ? top >= height + offsetTop + _relativeTop - _containerHeight && top < offsetTop : top < offsetTop;
+            this.triggerEvent('scroll', {
+                scrollTop: top,
+                isFixed: fixed
+            });
+            this.setData({ fixed: fixed });
+        }
+    }
+});
+
+/***/ }),
+
+/***/ 9:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+module.exports = Behavior({
+  methods: {
+    getRect: function getRect(selector) {
+      var _this = this;
+
+      return new Promise(function (resolve, reject) {
+        _this.createSelectorQuery().select(selector).boundingClientRect(function (rect) {
+          if (rect) {
+            resolve(rect);
+          } else {
+            reject(new Error("can not find selector: " + selector));
+          }
+        }).exec();
+      });
+    },
+    getAllRects: function getAllRects(selector) {
+      var _this2 = this;
+
+      return new Promise(function (resolve, reject) {
+        _this2.createSelectorQuery().selectAll(selector).boundingClientRect(function (rects) {
+          if (rects && rects.lenght > 0) {
+            resolve(rects);
+          } else {
+            reject(new Error("can not find selector: " + selector));
+          }
+        }).exec();
+      });
+    }
+  }
+});
+
+/***/ })
+
+/******/ });

+ 4 - 0
component/sticky/index.json

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

+ 7 - 0
component/sticky/index.wxml

@@ -0,0 +1,7 @@
+<wxs src="./index.wxs" module="computed" />
+
+<view class="weui-sticky" style="{{computed.containerStyle({fixed, height, zIndex})}}">
+  <view class="{{fixed ? 'weui-sticky__fixed' : ''}}" style="{{computed.wrapStyle({fixed, offsetTop})}}">
+    <slot ></slot>
+  </view>
+</view>

+ 20 - 0
component/sticky/index.wxs

@@ -0,0 +1,20 @@
+
+/* eslint-disable */
+function wrapStyle(data) {
+  if (data.fixed) {
+    return 'top: ' + data.offsetTop + 'px;'
+  }
+  return ''
+}
+
+function containerStyle(data) {
+  if (data.fixed) {
+    return 'height: ' + data.height + 'px; z-index: ' + data.zIndex + ';'
+  }
+  return ''
+}
+
+module.exports = {
+  wrapStyle: wrapStyle,
+  containerStyle: containerStyle
+}

+ 1 - 0
component/sticky/index.wxss

@@ -0,0 +1 @@
+.weui-sticky{position:relative}.weui-sticky__fixed{position:fixed;left:0;top:0}

+ 25 - 0
component/tarbar/tarbar.js

@@ -0,0 +1,25 @@
+// component/tarbar/tarbar.js
+// import config from '../../app.json'
+
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    // tabbar: config.tabBar
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+  }
+})

+ 4 - 0
component/tarbar/tarbar.json

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

+ 8 - 0
component/tarbar/tarbar.wxml

@@ -0,0 +1,8 @@
+<cover-view class="layer">
+  <cover-view class="content">
+    <slot></slot>
+  </cover-view>
+  <cover-view>
+    
+  </cover-view>
+</cover-view>

+ 8 - 0
component/tarbar/tarbar.wxss

@@ -0,0 +1,8 @@
+/* component/tarbar/tarbar.wxss */
+.layer {
+  display: flex;
+}
+
+.content {
+
+}

+ 85 - 0
component/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: false, // 是否开启 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
component/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

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 14 - 0
component/trtc-room/libs/mta_analysis.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
component/trtc-room/libs/tim-wx.js


+ 33 - 0
component/trtc-room/model/pusher.js

@@ -0,0 +1,33 @@
+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
component/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
component/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

+ 76 - 0
component/trtc-room/template/custom/custom.wxml

@@ -0,0 +1,76 @@
+<!-- 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" 
+        wx:if="{{pusher.url}}"
+        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>

+ 8 - 0
component/trtc-room/template/custom/custom.wxss

@@ -0,0 +1,8 @@
+/* 通过方法自定义模式 */
+.template-custom{
+  display: none;
+}
+.template-custom .pusher-container,
+.template-custom .player-container{
+  position: absolute;
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2267 - 0
component/trtc-room/trtc-room.js


+ 4 - 0
component/trtc-room/trtc-room.json

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

+ 5 - 0
component/trtc-room/trtc-room.wxml

@@ -0,0 +1,5 @@
+<import src='./template/custom/custom.wxml'/>
+
+<view class="trtc-room-container {{isFullscreenDevice?'fullscreen-device-fix':''}}">
+  <template is='custom' data="{{pusher, streamList, debug}}"></template>
+</view>

+ 13 - 0
component/trtc-room/trtc-room.wxss

@@ -0,0 +1,13 @@
+@import "./template/custom/custom.wxss";
+
+.pusher {
+  width: 100%;
+  height: 100%;
+  visibility: hidden;
+}
+
+.player {
+  width: 100%;
+  height: 100%;
+  visibility: hidden;
+}

+ 21 - 0
component/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
component/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 (safeArea && 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
component/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

+ 163 - 0
component/voice/voice.js

@@ -0,0 +1,163 @@
+// component/voice/voice.js
+
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    show: true,
+    pushUrl: false,
+    pullUrls: [],
+    trtcConfig: {},
+    room: 0
+  },
+  
+
+
+  attached() {
+    this.callback = (user={}) => {
+      console.warn('---user---', user,'---user---')
+      if (user.action === 'stopCall' && this.hasEnter) {
+        this.unpublishLocalAudio()
+        this.trtcRoomContext.exitRoom()
+        this.hasEnter = false
+        return
+      }
+      if (user.userId) {
+        this.enterAudioRoom(user)
+        return
+      }
+      console.warn(user.noMute, '---noMute')
+      if (user.noMute) {
+        this.unpublishLocalAudio()
+      } else {
+        this.publishLocalAudio()
+      }
+    }
+    // console.log(getApp().globalData.pusher, 'getApp().globalData.pusher')
+    // if (getApp().globalData.pusher) {
+    //   this.callback(getApp().globalData.pusher)
+    // }
+    console.log('attch')
+    getApp().addVoicePropsListener(this.callback)
+  },
+  detached() {
+    console.log('detached')
+    getApp().removeVoicePropsListener(this.callback)
+    // this.trtcRoomContext.exitRoom()
+    this.hasEnter = false
+  },
+  pageLifetimes: {
+    show() {
+      this.setData({show: true})
+      // this.trtcRoomContext = this.selectComponent('#trtcroom')
+      // if (getApp().globalData.audioUser) {
+      //   this.enterAudioRoom(getApp().globalData.audioUser, true)
+      // }
+      // if (this.data.show) {
+      //   this.enterAudioRoom()
+      // }
+    },
+    hide() {
+      clearTimeout(this.time)
+      this.setData({show: false})
+    },
+  },
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    enterAudioRoom(user, noEnter) {
+      // if (this.hasEnter) {
+      //   return
+      // }
+      console.error('hasEnter', this.hasEnter)
+      this.hasEnter = true
+      getApp().globalData.audioUser = user
+      console.log(user, 'user')
+      this.setData({
+        trtcConfig: {
+          scene: 'rtc',
+          sdkAppID: '1400450635', // 开通实时音视频服务创建应用后分配的 SDKAppID
+          userID: user.userId, // 用户 ID,可以由您的帐号系统指定
+          userSig: user.sig, // 身份签名,相当于登录密码的作用
+          template: 'custom', // 画面排版模式
+        }
+      }, () => {
+        this.trtcRoomContext = this.selectComponent('#trtcroom')
+        if (noEnter) {
+          return
+        }
+        console.log(this.trtcRoomContext, )
+        let EVENT =this.trtcRoomContext.EVENT
+  
+        if (this.trtcRoomContext) {
+          this.trtcRoomContext.on(EVENT.LOCAL_JOIN, (event) => {
+            // 进房成功后发布本地音频流和视频流 
+            console.error('进入房间')
+            this.successEnter = true
+            if (user.noMute) {
+              this.unpublishLocalAudio()
+            } else {
+              this.publishLocalAudio()
+            }
+          })
+  
+          this.trtcRoomContext.on(EVENT.LOCAL_LEAVE, (event) => {
+            console.error('离开房间')
+          })
+
+          // 监听远端用户的音频流的变更事件
+         this.trtcRoomContext.on(EVENT.REMOTE_AUDIO_ADD, (event) => {
+            // 订阅(即播放)远端用户的音频流
+            let userID = event.data.userID
+           this.trtcRoomContext.subscribeRemoteAudio({
+              userID: userID
+            })
+          })
+          // this.trtcRoomContext.on(EVENT.LOCAL_AUDIO_VOLUME_UPDATE, event => {
+          //   console.log(event)
+          // })
+         this.trtcRoomContext.enterRoom({
+            roomID: getApp().globalData.roomId
+          }).then(() => {
+            
+            console.log('成功进入房间')
+          }).catch((res) => {
+            console.error('room joinRoom 进房失败:', res)
+            this.hasEnter = false
+          })
+          let timer = null
+          timer = setInterval(() => {
+            if (!this.successEnter) {
+              this.trtcRoomContext.enterRoom({
+                roomID: getApp().globalData.roomId
+              }).then(() => {
+                console.log('成功进入房间')
+              }).catch((res) => {
+                console.error('room joinRoom 进房失败:', res)
+                this.hasEnter = false
+              })
+            } else {
+              clearInterval(timer)
+              timer = null
+            }
+          }, 3000);
+        }
+      })
+    },
+    unpublishLocalAudio () {
+      this.trtcRoomContext && this.trtcRoomContext.unpublishLocalAudio()
+    },
+    publishLocalAudio () {
+      this.trtcRoomContext && this.trtcRoomContext.publishLocalAudio()
+    }
+  }
+})

+ 6 - 0
component/voice/voice.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "trtc-room": "../trtc-room/trtc-room"
+  }
+}

+ 9 - 0
component/voice/voice.wxml

@@ -0,0 +1,9 @@
+<!--component/voice/voice.wxml-->
+<view>
+  <view class="layer">
+    <trtc-room id="trtcroom" config="{{trtcConfig}}"></trtc-room>
+  </view>
+</view>
+  <!-- <cover-view style="position: fixed; background-color: rgba(0,0,0,0.5); color: #fff;white-space:pre-wrap;z-index:999999">
+    --{{JSON.stringify(trtcConfig)}}--
+  </cover-view> -->

+ 20 - 0
component/voice/voice.wxss

@@ -0,0 +1,20 @@
+/* component/voice/voice.wxss */
+.layer {
+  position: absolute;
+  z-index: -999999999;
+  left: -99999999999px;
+  top: -99999999999px;
+  opacity: 0;
+}
+
+.pp {
+  position: fixed;
+  left: -99999999999px;
+  top: -99999999999px;
+  background-color: rgba(0,0,0,0.3);
+  z-index: 99999999999999;
+}
+
+.pp {
+  color: #fff;
+}

+ 22 - 0
config.js

@@ -0,0 +1,22 @@
+const dev = {
+  socketHost: 'wss://testshop.4dkankan.com',
+  requestHost: 'https://testzhifangbao.4dage.com',
+  // 容器地址
+  // viewHost: 'http://192.168.0.112:8081',
+  // 发现地址
+  env: 'dev',
+  viewChildHost: 'https://test.4dkankan.com',
+  viewHost: 'https://test.4dkankan.com'
+}
+
+const mal = {
+  env: 'pro',
+  socketHost: 'wss://shop.4dkankan.com',
+  requestHost: 'https://zhifangbao.4dage.com',
+  viewHost: 'https://www.4dkankan.com',
+  viewChildHost: 'https://www.4dkankan.com'
+}
+
+
+export default dev
+// export default mal

+ 121 - 0
config/api.js

@@ -0,0 +1,121 @@
+import remote from '../config.js'
+
+const API_BASE_URL = remote.requestHost + '/api/';
+// const API_BASE_URL = 'http://192.168.0.98:8089/platform-framework/api/'
+
+// const API_BASE_URL = 'https://testzhifangbao.4dage.com/api/'
+// const API_BASE_URL = 'http://192.168.0.207:8080/platform-framework/api/';
+
+
+module.exports = {
+    API_BASE_URL,
+    RoomList: API_BASE_URL + 'liveShow/getRoomList', //获取直播间列表
+
+    IndexTypeList: API_BASE_URL + 'brand/brandTypeList', //店铺类型列表
+    IndexList: API_BASE_URL + 'brand/list', //首页商铺列表
+    IndexUrlNewGoods: API_BASE_URL + 'index/newGoods', //新品首发
+    IndexUrlHotGoods: API_BASE_URL + 'index/hotGoods', //热卖商品
+    IndexUrlTopic: API_BASE_URL + 'index/topic', //专题精选
+    IndexUrlBrand: API_BASE_URL + 'index/brand', //品牌制造商
+    IndexUrlCategory: API_BASE_URL + 'index/category', //首页底部的分类及商品列表
+    IndexUrlBanner: API_BASE_URL + 'index/banner', //首页banner
+    IndexUrlChannel: API_BASE_URL + 'index/channel', //banner下的分类
+    CatalogList: API_BASE_URL + 'catalog/index',  //分类目录全部分类数据接口
+    CatalogCurrent: API_BASE_URL + 'catalog/current',  //分类目录当前分类数据接口
+
+    AuthLoginByWeixin: API_BASE_URL + 'auth/login_by_weixin', //微信登录
+    decryptedPhoneNum: API_BASE_URL + 'auth/decryptedPhoneNum', //获取微信手机号
+
+    SueneCategory: API_BASE_URL + 'goods/getCategoryByScene',  //获得分类数据
+    
+    GoodsNumCount: API_BASE_URL + 'goods/goodsNumOfBrand',  //统计商品总数
+    GoodsCount: API_BASE_URL + 'goods/count',  //统计商品总数
+    GoodsList: API_BASE_URL + 'goods/list',  //获得商品列表
+    GoodsCategory: API_BASE_URL + 'goods/category',  //获得分类数据
+    GoodsDetail: API_BASE_URL + 'goods/detail',  //获得商品的详情
+    GoodsHot: API_BASE_URL + 'goods/hot',  //人气推荐
+    GoodsRelated: API_BASE_URL + 'goods/related',  //商品详情页的关联商品(大家都在看)
+
+    CoupuonInfo: API_BASE_URL + 'coupon/getCouponById', //获取优惠券详情
+    BrandCouponList: API_BASE_URL + 'coupon/getListByBrand', //获取商家的优惠券
+    UNBrandCouponList: API_BASE_URL + 'coupon/getListUnAuth', //未登录获取商家的优惠券
+    CouponUsedList: API_BASE_URL + 'coupon/list',  //优惠券可使用列表
+    CouponNoUsedList: API_BASE_URL + 'coupon/getInvalidCoupons',  //优惠券不可使用列表
+    CouponExchange: API_BASE_URL +  'coupon/exchange', //领取优惠券
+    // GoodsList: API_BASE_URL + 'goods/list',  //获得商品列表
+    // GoodsCategory: API_BASE_URL + 'goods/category',  //获得分类数据
+    // GoodsDetail: API_BASE_URL + 'goods/detail',  //获得商品的详情
+    // GoodsHot: API_BASE_URL + 'goods/hot',  //人气推荐
+    // GoodsRelated: API_BASE_URL + 'goods/related',  //商品详情页的关联商品(大家都在看)
+
+    BrandList: API_BASE_URL + 'brand/list',  //品牌列表
+    BrandDetail: API_BASE_URL + 'brand/detail',  //品牌详情
+    BrandDetailByScene: API_BASE_URL + 'brand/getByScene',  //品牌详情
+
+    CartList: API_BASE_URL + 'cart/index', //获取购物车的数据
+    CartAdd: API_BASE_URL + 'cart/add', // 添加商品到购物车
+    BuyAdd: API_BASE_URL + 'buy/add', // 直接购买    
+    CartUpdate: API_BASE_URL + 'cart/update', // 更新购物车的商品
+    CartDelete: API_BASE_URL + 'cart/delete', // 删除购物车的商品
+    CartChecked: API_BASE_URL + 'cart/checked', // 选择或取消选择商品
+    CartGoodsCount: API_BASE_URL + 'cart/goodscount', // 获取购物车商品件数
+    CartCheckout: API_BASE_URL + 'cart/checkout', // 下单前信息确认
+
+    OrderCount: API_BASE_URL + 'order/index', // 获取订单数量
+    OrderSubmit: API_BASE_URL + 'order/submit', // 提交订单
+    PayPrepayId: API_BASE_URL + 'pay/prepay', //获取微信统一下单prepay_id
+
+    CollectList: API_BASE_URL + 'collect/list',  //收藏列表
+    CollectAddOrDelete: API_BASE_URL + 'collect/addordelete',  //添加或取消收藏
+
+    CommentList: API_BASE_URL + 'comment/list',  //评论列表
+    CommentCount: API_BASE_URL + 'comment/count',  //评论总数
+    CommentPost: API_BASE_URL + 'comment/post',   //发表评论
+
+    TopicList: API_BASE_URL + 'topic/list',  //专题列表
+    TopicDetail: API_BASE_URL + 'topic/detail',  //专题详情
+    TopicRelated: API_BASE_URL + 'topic/related',  //相关专题
+
+    SearchIndex: API_BASE_URL + 'search/index',  //搜索页面数据
+    SearchHelper: API_BASE_URL + 'search/helper',  //搜索帮助
+    SearchClearHistory: API_BASE_URL + 'search/clearhistory',  //搜索帮助
+
+    AddressList: API_BASE_URL + 'address/list',  //收货地址列表
+    AddressDetail: API_BASE_URL + 'address/detail',  //收货地址详情
+    AddressSave: API_BASE_URL + 'address/save',  //保存收货地址
+    AddressDelete: API_BASE_URL + 'address/delete',  //删除收货地址
+
+    RegionList: API_BASE_URL + 'region/list',  //获取区域列表
+
+    OrderList: API_BASE_URL + 'order/list',  //订单列表
+    OrderDetail: API_BASE_URL + 'order/detail',  //订单详情
+    OrderCancel: API_BASE_URL + 'order/cancelOrder',  //取消订单
+    OrderConfirm: API_BASE_URL + 'order/confirmOrder',  //确认收货
+
+    FootprintList: API_BASE_URL + 'footprint/list',  //足迹列表
+    FootprintDelete: API_BASE_URL + 'footprint/delete',  //删除足迹
+
+    HomeBanner: API_BASE_URL + 'slideShow/getSlideList', //首页轮播
+    UploadFile: API_BASE_URL + 'upload/upload', //上传文件
+    UserInfo: API_BASE_URL + 'user/getUerInfo', //获取用户信息
+    UpdateUserInfo: API_BASE_URL + 'user/updateUerInfo', //修改用户信息
+    FeedbackAdd: API_BASE_URL + 'feedback/save', //添加反馈
+    SmsCode: API_BASE_URL + 'user/smscode', //发送短信
+    BindMobile: API_BASE_URL + 'user/bindMobile', //绑定手机
+    Login: API_BASE_URL + 'auth/login', //账号登录
+    Register: API_BASE_URL + 'auth/register', //注册
+    CouponList: API_BASE_URL + 'coupon/list', // 优惠券列表
+    GoodsCouponList: API_BASE_URL + 'coupon/listByGoods', // 商品优惠券列表   
+    OrderQuery: API_BASE_URL + 'pay/query',//微信查询订单状态
+
+    HelpTypeList: API_BASE_URL + 'helpissue/typeList', //查看帮助类型列表
+    HelpIssueList: API_BASE_URL + 'helpissue/issueList', //查看问题列表
+
+    LogisticsDetail: API_BASE_URL + 'logistics/detail', //查看物流详情
+
+    // 统计相关
+    SceneReport: API_BASE_URL + '../app/census/getSceneCensus',
+    GetAdminBrands: API_BASE_URL + 'user/getAdminBrands',
+    AddTalkCount: API_BASE_URL + '../app/census/addGoodsQueryNum',
+    AddGoodsViewCount: API_BASE_URL + 'goods/logDetail'
+};

+ 86 - 0
custom-tab-bar/index.js

@@ -0,0 +1,86 @@
+let tabBar = {
+  "custom": true,
+  "backgroundColor": "#fff",
+  "borderStyle": "black",
+  "selectedColor": "#ED5D18",
+  "color": "#666C7D",
+  "list": [
+    {
+      "pagePath": "/pages/index/index",
+      "iconPath": "/static/images/hbtab_home@2x.png",
+      "selectedIconPath": "/static/images/btab_home@2x.png",
+      "text": "店铺"
+    },
+    {
+      "pagePath": "/pages/catalog/catalog",
+      "iconPath": "/static/images/hbtab_classify@2x.png",
+      "selectedIconPath": "/static/images/btab_classify@2x.png",
+      "text": "分类"
+    },
+    // {
+    //   "pagePath": "/pages/discover/discover",
+    //   "iconPath": "/static/images/btab_view@2x.png",
+    //   "selectedIconPath": "/static/images/btab_view_active@2x.png",
+    //   "keyiconPath": "/static/images/key_btab_view@2x.png",
+    //   "text": "发现",
+    //   "keynote": true
+    // },
+    {
+      "pagePath": "/pages/cart/cart",
+      "iconPath": "/static/images/hbtab_cart@2x.png",
+      "selectedIconPath": "/static/images/btab_cart@2x.png",
+      "text": "购物车"
+    },
+    {
+      "pagePath": "/pages/ucenter/index/index",
+      "iconPath": "/static/images/hbtab_my@2x.png",
+      "selectedIconPath": "/static/images/btab_my@2x.png",
+      "text": "我的"
+    }
+  ]
+}
+
+let selectIndex = 0
+let fns = []
+let setCountFns = []
+
+global.setTabBarBadge = function(args) {
+  setCountFns.forEach(fn => fn(args))
+}
+
+Component({
+  data: {
+    tips: {},
+    selectIndex,
+    color: tabBar.color,
+    selectedColor: tabBar.selectedColor,
+    backgroundColor: tabBar.backgroundColor,
+    borderStyle: tabBar.borderStyle,
+    list: tabBar.list
+  },
+  attached() {
+    setCountFns.push((args) => {
+      this.setData({
+        tips: {
+          index: args.index, 
+          count: args.text
+        }
+      })
+    })
+    fns.push(() => this.setData({ selectIndex }))
+    fns.forEach(fn => fn())
+  },
+  methods: {
+    switchTab(e) {
+      const data = e.currentTarget.dataset
+      const url = data.path
+      wx.switchTab({url})
+      this.setData({
+        selectIndex: data.index
+      })
+      selectIndex = data.index
+
+      fns.forEach(fn => fn())
+    }
+  }
+})

+ 3 - 0
custom-tab-bar/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 22 - 0
custom-tab-bar/index.wxml

@@ -0,0 +1,22 @@
+<!--miniprogram/custom-tab-bar/index.wxml-->
+<cover-view class="tab-layout">
+<cover-view class="tab-bar">
+  <cover-view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-sj="{{item}}" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
+    <cover-image src="{{index === selectIndex ? item.selectedIconPath : item.iconPath}}"></cover-image>
+    <cover-view style="color: {{index === selectIndex ? selectedColor : color}}">{{item.text}} </cover-view>
+    <cover-view class="icon" wx:if="{{index === tips.index}}">{{tips.count}}</cover-view>
+  </cover-view>
+</cover-view>
+<cover-view 
+  wx:for="{{list}}"
+  wx:key="index"
+  class="keynote"
+  data-path="{{item.pagePath}}"
+  data-index="{{index}}"
+  bindtap="switchTab"
+  style="left: {{index * (1 / list.length) * 100}}%; width: {{(1 / list.length * 100)}}%"
+  wx:if="{{item.keynote}}">
+  <cover-image src="{{item.keyiconPath}}"></cover-image>
+</cover-view>
+
+</cover-view>

+ 83 - 0
custom-tab-bar/index.wxss

@@ -0,0 +1,83 @@
+.tab-layout {
+  position: fixed;
+  height: calc(70px + env(safe-area-inset-bottom));
+  bottom: 0;
+  left: 0;
+  width: 100%;
+}
+
+
+.keynote {
+  position: absolute;
+  top: 0;
+  z-index: 100;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 2;
+}
+
+.keynote cover-image {
+  width: 56px;
+  height: 56px;
+  position: relative;
+}
+
+.tab-bar {
+  margin-top: 20px;
+  height: 50px;
+  background: white;
+  display: flex;
+  z-index: 1;
+  box-sizing: content-box;
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.tab-bar-border {
+  background-color: rgba(0, 0, 0, 0.33);
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 1px;
+  transform: scaleY(0.5);
+}
+
+.tab-bar-item {
+  flex: 1;
+  padding: 10rpx 0;
+  text-align: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+  position: relative;
+}
+
+.tab-bar-item cover-image {
+  width: 48rpx;
+  height: 48rpx;
+  margin-bottom: 6rpx;
+  position: relative;
+  z-index: 99999999;
+}
+
+.tab-bar-item cover-view {
+  font-size: 20rpx;
+  position: relative;
+  z-index: 99999999;
+}
+
+.tab-bar-item .icon {
+  position: absolute;
+  right: 40rpx;
+  top: 5px;
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  background-color: #ED5D18;
+  color: #fff;
+  font-size: 10px;
+  text-align: center;
+  line-height: 12px;
+}

+ 224 - 0
dataReport/components/customer-report/index.js

@@ -0,0 +1,224 @@
+import * as echarts from '../ec-canvas/echarts'
+import { mergeOptions } from '../ec-canvas/defaultOption'
+
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    ec: {
+      onInit: initChart
+    },
+    barEc: {
+      onInit: initBarChart
+    },
+    pieEc : {
+      onInit: initSexPieChart
+    },
+    areaPieEc: {
+      onInit: initAreaPieChart
+    }
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+  }
+})
+
+function initChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    title: {
+      text: '新老客户统计'
+    },
+    series: [
+      {
+        data: [[0, 1000], [3, 500], [6, 750], [9, 1400], [12, 1250], [15, 1600], [18, 1800]],
+        type: 'line',
+        smooth: true,
+        name: '新客户',
+        areaStyle: {
+          opacity: 0.1
+        },
+        symbolSize: 0
+      },
+      {
+        data: [[0, 1250], [3, 600], [6, 800], [9, 1500], [12, 1400], [15, 1750], [18, 2000]],
+        type: 'line',
+        smooth: true,
+        name: '老客户',
+        areaStyle: {
+          opacity: 0.1
+        },
+        symbolSize: 0
+      },
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function initBarChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    title: {
+      text: '客户来源统计'
+    },
+    xAxis: {
+      type: 'value',
+      axisLabel: {
+        align: 'center'
+      }
+    },
+    yAxis: {
+      type: 'category',
+      data: ['网页', 'APP', '小程序', '其它'],
+      axisLabel: {
+        margin:  0
+      }
+    },
+    series: [
+      {
+        type: 'bar',
+        data: [{
+          value: 2,
+          itemStyle: {
+            
+          }
+        }, 4.9, 7.0, 11],
+        barGap: 0,
+        barWidth: 10,
+        itemStyle: {
+          barBorderRadius:[0, 10, 10, 0],
+        }
+      }
+    ]
+  };
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function initSexPieChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    title: {
+      text: '客户性别统计'
+    },
+    xAxis: {
+      show: false
+    },
+    yAxis: {
+      show: false
+    },
+    color: ['#17D2D2','#FAD43B','#4DAEFF','#F69758','#738EFE','#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83',  '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
+    legend: {
+      left: 'center',
+      bottom: 0,
+      top: null,
+      data: ['男性:50%', '女性:50%', '1性:50%']
+    },
+    series: [
+      {
+        data: [[0, 1000], [3, 500], [6, 750], [9, 1400], [12, 1250], [15, 1600], [18, 1800]],
+        type: 'pie',
+        radius: ['30%', '50%'],
+        data: [
+          { value: 100, name: '男性:50%' },
+          { value: 100, name: '女性:50%' },
+          { value: 100, name: '1性:50%' },
+        ],
+        
+        label: {
+          show: false,
+          position: 'center'
+        },
+        labelLine: {
+          show: false
+        }
+      }
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function initAreaPieChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    title: {
+      text: '客户区域统计'
+    },
+    xAxis: {
+      show: false
+    },
+    yAxis: {
+      show: false
+    },
+    color: ['#17D2D2','#FAD43B','#4DAEFF','#F69758','#738EFE','#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83',  '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
+    legend: {
+      left: 'center',
+      bottom: 0,
+      top: null,
+      data: ['男性:50%', '女性:50%', '1性:50%']
+    },
+    series: [
+      {
+        data: [[0, 1000], [3, 500], [6, 750], [9, 1400], [12, 1250], [15, 1600], [18, 1800]],
+        type: 'pie',
+        radius: ['30%', '50%'],
+        data: [
+          { value: 100, name: '男性:50%' },
+          { value: 100, name: '女性:50%' },
+          { value: 100, name: '1性:50%' },
+        ],
+        
+        label: {
+          show: false,
+          position: 'center'
+        },
+        labelLine: {
+          show: false
+        }
+      }
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}

+ 6 - 0
dataReport/components/customer-report/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "ec-canvas": "../ec-canvas/ec-canvas"
+  }
+}

+ 31 - 0
dataReport/components/customer-report/index.wxml

@@ -0,0 +1,31 @@
+<view class="data-num-w">
+  <view class="data-num">
+    <view class="value">9268</view>
+    <view class="label">访客数量</view>
+  </view>
+  <view class="data-num">
+    <view class="value scene">9268</view>
+    <view class="label">询盘客户量</view>
+  </view>
+  <view class="data-num">
+    <view class="value customer">9268</view>
+    <view class="label">客户转化率</view>
+  </view>
+</view>
+
+<view class="canvas-container">
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-line" ec="{{ ec }}" />
+  
+</view>
+
+<!-- <view class="canvas-container">
+  <ec-canvas id="mychart-dom-multi-bar"  canvas-id="mychart-multi-bar" ec="{{ barEc }}" />
+</view> -->
+
+<view class="canvas-container">
+  <ec-canvas id="mychart-dom-multi-pie"  canvas-id="mychart-multi-pie" ec="{{ pieEc }}" />
+</view>
+
+<view class="canvas-container">
+  <ec-canvas id="mychart-dom-multi-pie"  canvas-id="mychart-multi-pie" ec="{{ areaPieEc }}" />
+</view>

+ 41 - 0
dataReport/components/customer-report/index.wxss

@@ -0,0 +1,41 @@
+.data-num-w {
+  display: flex;
+  width: 100%;
+  justify-content: space-between;
+  margin: 42rpx 0 46rpx;
+}
+.data-num {
+  flex: 1;
+  text-align: center;
+}
+
+.data-num .value {
+  width: 120rpx;
+  height: 120rpx;
+  line-height: 120rpx;
+  text-align: center;
+  border-radius: 50%;
+  color: #17D2D2;
+  background: rgba(23,210,210,0.1);
+  margin: 0 auto 14rpx;
+  font-weight: bold;
+}
+.value.scene {
+  color: #738EFE;
+  background:rgba(115,142,254,0.1);
+}
+
+.value.customer {
+  color: #4DAEFF;
+  background:rgba(77,174,255,0.1);
+}
+
+.data-num .label {
+  font-size: 22rpx;
+}
+
+.canvas-container {
+  width: 100%;
+  height: 476rpx;
+  margin-bottom: 40rpx;
+}

+ 98 - 0
dataReport/components/ec-canvas/defaultOption.js

@@ -0,0 +1,98 @@
+const defaultOptions = {
+  title: {
+    text: '',
+    left: 'left',
+    top: 0,
+    textStyle: {
+      fontSize: 17
+    }
+  },
+  textStyle: {
+    color: '#909090'
+  },
+  grid: {
+    top: 37,
+    left: 40,
+    right: 20,
+    bottom: 20
+  },
+  color: ["#17D2D2", "#738EFE"],
+  legend: {
+    left: 'right',
+    itemWidth: 14,
+    icon: 'roundRect',
+    textStyle: {
+      fontSize: 11
+    }
+  },
+
+  tooltip: {
+    show: true,
+    trigger: 'axis'
+    // formatter: "{c} {d}"
+  },
+  xAxis: {
+    type: 'category',
+    // boundaryGap: false,
+    data: [],
+    boundaryGap: false,
+    axisTick: {
+      show: false
+    },
+    axisLine: {
+      show: true,
+      lineStyle: {
+        color: '#e5e5e5'
+      }
+    },
+    splitLine: {
+      lineStyle: {
+        color: '#e5e5e5'
+      }
+    },
+    axisLabel: {
+      showMaxLabel: true
+    }
+  },
+  yAxis: {
+    type: 'value',
+    minInterval: 1,
+    axisTick: {
+      show: false
+    },
+    axisLine: {
+      show: true,
+      lineStyle: {
+        color: '#e5e5e5'
+      }
+    },
+    axisLabel: {
+      margin: 7,
+      formatter (value) {
+        return value.toString()
+      }
+    },
+    splitLine: {
+      lineStyle: {
+        color: '#e5e5e5'
+      }
+    }
+  }
+}
+
+function isObject (value) {
+  return Object.prototype.toString.call(value) === '[object Object]'
+}
+export function mergeOptions (options, defaultOption=defaultOptions) {
+  Object.keys(defaultOption).forEach(item => {
+    if (!options[item]) {
+      options[item] = defaultOption[item]
+    } else {
+      if (isObject(options[item])) {
+        mergeOptions(options[item], defaultOption[item])
+      }
+    }
+  })
+  return options
+}
+

+ 250 - 0
dataReport/components/ec-canvas/ec-canvas.js

@@ -0,0 +1,250 @@
+import WxCanvas from './wx-canvas';
+import * as echarts from './echarts';
+
+let ctx;
+
+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
+    } else if (num1 < num2) {
+      return -1
+    }
+  }
+  return 0
+}
+
+Component({
+  properties: {
+    canvasId: {
+      type: String,
+      value: 'ec-canvas'
+    },
+
+    ec: {
+      type: Object
+    },
+
+    forceUseOldCanvas: {
+      type: Boolean,
+      value: false
+    }
+  },
+
+  data: {
+    isUseNewCanvas: false
+  },
+
+  ready: function () {
+    // Disable prograssive because drawImage doesn't support DOM as parameter
+    // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html
+    echarts.registerPreprocessor(option => {
+      if (option && option.series) {
+        if (option.series.length > 0) {
+          option.series.forEach(series => {
+            series.progressive = 0;
+          });
+        }
+        else if (typeof option.series === 'object') {
+          option.series.progressive = 0;
+        }
+      }
+    });
+
+    if (!this.data.ec) {
+      console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" '
+        + 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>');
+      return;
+    }
+
+    if (!this.data.ec.lazyLoad) {
+      this.init();
+    }
+  },
+
+  methods: {
+    init: function (callback) {
+      const version = wx.getSystemInfoSync().SDKVersion
+
+      const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0;
+      const forceUseOldCanvas = this.data.forceUseOldCanvas;
+      const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas;
+      this.setData({ isUseNewCanvas });
+
+      if (forceUseOldCanvas && canUseNewCanvas) {
+        console.warn('开发者强制使用旧canvas,建议关闭');
+      }
+
+      if (isUseNewCanvas) {
+        // console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
+        // 2.9.0 可以使用 <canvas type="2d"></canvas>
+        this.initByNewWay(callback);
+      } else {
+        const isValid = compareVersion(version, '1.9.91') >= 0
+        if (!isValid) {
+          console.error('微信基础库版本过低,需大于等于 1.9.91。'
+            + '参见:https://github.com/ecomfe/echarts-for-weixin'
+            + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82');
+          return;
+        } else {
+          console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能');
+          this.initByOldWay(callback);
+        }
+      }
+    },
+
+    initByOldWay(callback) {
+      // 1.9.91 <= version < 2.9.0:原来的方式初始化
+      ctx = wx.createCanvasContext(this.data.canvasId, this);
+      const canvas = new WxCanvas(ctx, this.data.canvasId, false);
+
+      echarts.setCanvasCreator(() => {
+        return canvas;
+      });
+      // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
+      const canvasDpr = 1
+      var query = wx.createSelectorQuery().in(this);
+      query.select('.ec-canvas').boundingClientRect(res => {
+        if (typeof callback === 'function') {
+          this.chart = callback(canvas, res.width, res.height, canvasDpr);
+        }
+        else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
+          this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr);
+        }
+        else {
+          this.triggerEvent('init', {
+            canvas: canvas,
+            width: res.width,
+            height: res.height,
+            canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init
+          });
+        }
+      }).exec();
+    },
+
+    initByNewWay(callback) {
+      // version >= 2.9.0:使用新的方式初始化
+      const query = wx.createSelectorQuery().in(this)
+      query
+        .select('.ec-canvas')
+        .fields({ node: true, size: true })
+        .exec(res => {
+          const canvasNode = res[0].node
+          this.canvasNode = canvasNode
+
+          const canvasDpr = wx.getSystemInfoSync().pixelRatio
+          const canvasWidth = res[0].width
+          const canvasHeight = res[0].height
+
+          const ctx = canvasNode.getContext('2d')
+
+          const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode)
+          echarts.setCanvasCreator(() => {
+            return canvas
+          })
+
+          if (typeof callback === 'function') {
+            this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
+          } else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
+            this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr)
+          } else {
+            this.triggerEvent('init', {
+              canvas: canvas,
+              width: canvasWidth,
+              height: canvasHeight,
+              dpr: canvasDpr
+            })
+          }
+        })
+    },
+    canvasToTempFilePath(opt) {
+      if (this.data.isUseNewCanvas) {
+        // 新版
+        const query = wx.createSelectorQuery().in(this)
+        query
+          .select('.ec-canvas')
+          .fields({ node: true, size: true })
+          .exec(res => {
+            const canvasNode = res[0].node
+            opt.canvas = canvasNode
+            wx.canvasToTempFilePath(opt)
+          })
+      } else {
+        // 旧的
+        if (!opt.canvasId) {
+          opt.canvasId = this.data.canvasId;
+        }
+        ctx.draw(true, () => {
+          wx.canvasToTempFilePath(opt, this);
+        });
+      }
+    },
+
+    touchStart(e) {
+      if (this.chart && e.touches.length > 0) {
+        var touch = e.touches[0];
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mousedown', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.dispatch('mousemove', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.processGesture(wrapTouch(e), 'start');
+      }
+    },
+
+    touchMove(e) {
+      if (this.chart && e.touches.length > 0) {
+        var touch = e.touches[0];
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mousemove', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.processGesture(wrapTouch(e), 'change');
+      }
+    },
+
+    touchEnd(e) {
+      if (this.chart) {
+        const touch = e.changedTouches ? e.changedTouches[0] : {};
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mouseup', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.dispatch('click', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.processGesture(wrapTouch(e), 'end');
+      }
+    }
+  }
+});
+
+function wrapTouch(event) {
+  for (let i = 0; i < event.touches.length; ++i) {
+    const touch = event.touches[i];
+    touch.offsetX = touch.x;
+    touch.offsetY = touch.y;
+  }
+  return event;
+}

+ 4 - 0
dataReport/components/ec-canvas/ec-canvas.json

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

+ 4 - 0
dataReport/components/ec-canvas/ec-canvas.wxml

@@ -0,0 +1,4 @@
+<!-- 新的:接口对其了H5 -->
+<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>
+<!-- 旧的 -->
+<canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>

+ 4 - 0
dataReport/components/ec-canvas/ec-canvas.wxss

@@ -0,0 +1,4 @@
+.ec-canvas {
+  width: 100%;
+  height: 100%;
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 22 - 0
dataReport/components/ec-canvas/echarts.js


+ 121 - 0
dataReport/components/ec-canvas/wx-canvas.js

@@ -0,0 +1,121 @@
+export default class WxCanvas {
+  constructor(ctx, canvasId, isNew, canvasNode) {
+    this.ctx = ctx;
+    this.canvasId = canvasId;
+    this.chart = null;
+    this.isNew = isNew
+    if (isNew) {
+      this.canvasNode = canvasNode;
+    }
+    else {
+      this._initStyle(ctx);
+    }
+
+    // this._initCanvas(zrender, ctx);
+
+    this._initEvent();
+  }
+
+  getContext(contextType) {
+    if (contextType === '2d') {
+      return this.ctx;
+    }
+  }
+
+  // canvasToTempFilePath(opt) {
+  //   if (!opt.canvasId) {
+  //     opt.canvasId = this.canvasId;
+  //   }
+  //   return wx.canvasToTempFilePath(opt, this);
+  // }
+
+  setChart(chart) {
+    this.chart = chart;
+  }
+
+  attachEvent() {
+    // noop
+  }
+
+  detachEvent() {
+    // noop
+  }
+
+  _initCanvas(zrender, ctx) {
+    zrender.util.getContext = function () {
+      return ctx;
+    };
+
+    zrender.util.$override('measureText', function (text, font) {
+      ctx.font = font || '12px sans-serif';
+      return ctx.measureText(text);
+    });
+  }
+
+  _initStyle(ctx) {
+    var styles = ['fillStyle', 'strokeStyle', 'globalAlpha',
+      'textAlign', 'textBaseAlign', 'shadow', 'lineWidth',
+      'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize'];
+
+    styles.forEach(style => {
+      Object.defineProperty(ctx, style, {
+        set: value => {
+          if (style !== 'fillStyle' && style !== 'strokeStyle'
+            || value !== 'none' && value !== null
+          ) {
+            ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+          }
+        }
+      });
+    });
+
+    ctx.createRadialGradient = () => {
+      return ctx.createCircularGradient(arguments);
+    };
+  }
+
+  _initEvent() {
+    this.event = {};
+    const eventNames = [{
+      wxName: 'touchStart',
+      ecName: 'mousedown'
+    }, {
+      wxName: 'touchMove',
+      ecName: 'mousemove'
+    }, {
+      wxName: 'touchEnd',
+      ecName: 'mouseup'
+    }, {
+      wxName: 'touchEnd',
+      ecName: 'click'
+    }];
+
+    eventNames.forEach(name => {
+      this.event[name.wxName] = e => {
+        const touch = e.touches[0];
+        this.chart.getZr().handler.dispatch(name.ecName, {
+          zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
+          zrY: name.wxName === 'tap' ? touch.clientY : touch.y
+        });
+      };
+    });
+  }
+
+  set width(w) {
+    if (this.canvasNode) this.canvasNode.width = w
+  }
+  set height(h) {
+    if (this.canvasNode) this.canvasNode.height = h
+  }
+
+  get width() {
+    if (this.canvasNode)
+      return this.canvasNode.width
+    return 0
+  }
+  get height() {
+    if (this.canvasNode)
+      return this.canvasNode.height
+    return 0
+  }
+}

+ 471 - 0
dataReport/components/scene-report/scene-report.js

@@ -0,0 +1,471 @@
+import * as echarts from '../ec-canvas/echarts'
+import { mergeOptions } from '../ec-canvas/defaultOption'
+var util = require('../../../utils/util');
+var api = require('../../../config/api.js');
+
+let ecXdata, ecYdata, rateXdata, rateYdata, ec2Xdata, ec2Ydata, ec3Xdata, ec3Ydata
+const dayLength = 7
+Component({
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    userInfo: {},
+    detail: {},
+    activeType: 0,
+    brandNameList: [],
+    index: 0,
+    // ec2: {
+    //   onInit: init2Chart
+    // },
+    // barEc: {
+    //   onInit: initBarChart
+    // },
+    dataNumList: [
+      {
+        value: 0,
+        label: '店铺浏览量',
+        key: 'pvTotalNum',
+        bgColor: 'rgba(255, 205, 60, 0.1)',
+        color: '#FFCD3C'
+      },
+      {
+        value: 0,
+        label: '用户访问量',
+        key: 'uvTotalNum',
+        bgColor: 'rgba(77,174,255,0.1)',
+        color: '#4DAEFF'
+      },
+      {
+        value: 0,
+        label: '用户咨询量',
+        bgColor: 'rgba(23,210,210,0.1)',
+        color: '#17D2D2',
+        key: 'totalTalkNum'
+      },
+      {
+        value: 0,
+        label: '店铺商品数',
+        bgColor: 'rgba(246,151,88,0.1)',
+        color: '#F69758',
+        key: 'goodsNum'
+      }
+    ],
+    chartTypes: [
+      {
+        icon: 'data_booth',
+        name: '店铺数据',
+        value: 0
+      },
+      {
+        icon: 'data_visitor',
+        name: '访客数据',
+        value: 1
+      },
+      {
+        icon: 'data_wares',
+        name: '商品数据',
+        value: 2
+      },
+    ]
+  },
+
+  lifetimes: {
+    
+  },
+  async ready () {
+    this.userInfo = wx.getStorageSync('userinfoDetail')
+    this.brandList = this.userInfo.brandList
+    this.setData({
+      userInfo: this.userInfo,
+      brandNameList: [this.userInfo.brandName]
+    })
+    await this.getBrandList()
+    this.getData()
+  },
+  methods: {
+    changeActiveType (e) {
+      const { value } = e.currentTarget.dataset
+      this.setData({
+        activeType: value
+      })
+    },
+    async getData () {
+      let userInfo = this.userInfo
+      if (!userInfo || !userInfo.userId) {
+        return
+      }
+      
+      let res = await util.request(api.SceneReport ,{
+        guideId: userInfo.userId,
+        brandId: this.brandList[this.data.index].brandId,
+        startDate: fotmatDate(new Date(new Date().getTime() - 86000000), 'yyyy-MM-dd'),
+        size: dayLength
+      }, 'get')
+      if (res.code !== 0) {
+        wx.showModal({
+          title: '未查询到店铺统计信息',
+        })
+        return
+      }
+      let data = res.data
+      let hotVisitHistory = this.formatData(data.hotVisitHistory)
+      ec3Xdata = hotVisitHistory.xaxis
+      ec3Ydata = hotVisitHistory.result
+      let sceneVisit = this.formatData(data.pvHistory)
+      ecXdata = sceneVisit.xaxis
+      ecYdata = sceneVisit.result
+      let uvVisit = this.formatData(data.uvHistory)
+      ec2Xdata = uvVisit.xaxis
+      ec2Ydata = [uvVisit.result]
+      let dealHistory = this.formatData(data.dealHistory)
+      let goodsDetailVisitHistory = this.formatData(data.goodsDetailVisitHistory)
+      rateXdata = goodsDetailVisitHistory.xaxis
+      rateYdata = goodsDetailVisitHistory.result.map((item, index) => ((dealHistory.result[index] || 0) * 100 / (item || 1)).toFixed(1))
+      let talkHistory = this.formatData(data.talkHistory)
+      ec2Ydata[1] = talkHistory.result
+      let dataNumList = this.data.dataNumList.map(item => {
+        if (data[item.key]) {
+          item.value = formatNum(data[item.key])
+        }
+        return item
+      })
+      this.setData({
+        // detail: data,
+        dataNumList,
+        ec: {
+          onInit: initChart
+        },
+        ec2: {
+          onInit: init2Chart
+        },
+        ec3: {
+          onInit: init3Chart
+        },
+        rateEc: {
+          onInit: initRateChart
+        }
+      })
+    },
+    formatData (data) {
+      if (typeof data === 'string') {
+        data = JSON.parse(data)
+      }
+      if (!(data instanceof Array)) {
+        data = []
+      }
+      let originData = []
+      if (data.length < dayLength) {
+        let today = new Date(fotmatDate(new Date(), 'yyyy-MM-dd')).getTime()
+        
+        for (let i =0; i < dayLength; i++) {
+          let theDay = today - (dayLength - i) * 86400000
+          let inData = {}
+          let date = fotmatDate(new Date(theDay), 'yyyy-MM-dd')
+          inData[date] = data.find(item => item[`${date} 00:00:00`]) ? data.find(item => item[`${date} 00:00:00`])[`${date} 00:00:00`] : 0
+          originData.push(inData)
+        }
+      } else {
+        originData = data
+      }
+      let xaxis = originData.map(item => Object.keys(item)[0].slice(5,10))
+      let result = originData.map(item => item[Object.keys(item)[0]])
+      return {
+        result,
+        xaxis,
+      }
+    },
+    async getBrandList () {
+      let res = await util.request(api.GetAdminBrands ,{
+        userId: this.userInfo.userId,
+      })
+      let list = res.data
+      let brandNameList = list.map(item => item.brandName.slice(0,15))
+      this.brandList = list
+      this.setData({
+        brandNameList
+      }) 
+    },
+    bindPickerChange (e) {
+      let index = e.detail.value
+      let brand = this.brandList[index]
+      console.log(e)
+      this.setData({
+        index
+      })
+      this.reset()
+      wx.nextTick(() => {
+        this.getData()
+      })
+    },
+    reset() {
+      this.setData({
+        ec: '',
+        ec2: '',
+        ec3: '',
+        rateEc: '',
+        dataNumList: [
+          {
+            value: 0,
+            label: '店铺浏览量',
+            key: 'pvTotalNum',
+            bgColor: 'rgba(255, 205, 60, 0.1)',
+            color: '#FFCD3C'
+          },
+          {
+            value: 0,
+            label: '用户访问量',
+            key: 'uvTotalNum',
+            bgColor: 'rgba(77,174,255,0.1)',
+            color: '#4DAEFF'
+          },
+          {
+            value: 0,
+            label: '用户咨询量',
+            bgColor: 'rgba(23,210,210,0.1)',
+            color: '#17D2D2',
+            key: 'totalTalkNum'
+          },
+          {
+            value: 0,
+            label: '店铺商品数',
+            bgColor: 'rgba(246,151,88,0.1)',
+            color: '#F69758',
+            key: 'goodsNum'
+          }
+        ],
+      })
+    }
+  }
+})
+
+function initChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+  
+  var option = {
+    title: {
+      text: ''
+    },
+    tooltip: {
+      formatter: `{a}: {c} \n${new Date().getFullYear()}-{b}`,
+      axisPointer: {
+        lineStyle: {
+          color: '#FFCD3C'
+        }
+      }
+    },
+    color: ['#FFCD3C'],
+    xAxis: {
+      boundaryGap: false,
+      data: ecXdata
+    },
+    series: [
+      {
+        data: ecYdata,
+        type: 'line',
+        name: '浏览量',
+        symbolSize: 2,
+        showSymbol: false,
+        symbol: 'circle',
+        smooth: 0.5,
+        areaStyle: {
+          opacity: 0.1
+        },
+      },
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function init3Chart(canvas, width, height, dpr, xdata,) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+  
+  var option = {
+    title: {
+      text: ''
+    },
+    color: ['#ED5D18'],
+    tooltip: {
+      formatter: `{a}: {c} \n${new Date().getFullYear()}-{b}`,
+      axisPointer: {
+        type: 'shadow',
+        shadowStyle: {
+          color: 'rgba(255, 205, 60, 0.1)'
+        },
+        lineStyle: {
+          color: 'rgba(255, 205, 60, 0.1)'
+        }
+      }
+    },
+    xAxis: {
+      data: ec3Xdata,
+      boundaryGap: true
+    },
+    series: [
+      {
+        data: ec3Ydata,
+        type: 'bar',
+        name: '浏览量',
+        itemStyle: {
+          barBorderRadius: [5,5,0,0],
+        },
+        barWidth: 10,
+        emphasis: {
+          itemStyle: {
+            opacity: 1
+          }
+        }
+      },
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function init2Chart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    xAxis: {
+      data: ec2Xdata
+    },
+    tooltip: {
+      formatter: `访问量: {c0} \n咨询量: {c1} \n${new Date().getFullYear()}-{b}`,
+      axisPointer: {
+        lineStyle: {
+          color: '#FFCD3C'
+        }
+      }
+    },
+    series: [
+      {
+        data: ec2Ydata[0] || [],
+        type: 'line',
+        name: '客户访问量',
+        symbolSize: 2,
+        showSymbol: false,
+        symbol: 'circle',
+        smooth: 0.5,
+        color: '#ED5D18',
+        areaStyle: {
+          opacity: 0.1
+        },
+      },
+      {
+        data:  ec2Ydata[1] || [],
+        type: 'line',
+        name: '客户咨询量',
+        symbolSize: 2,
+        showSymbol: false,
+        symbol: 'circle',
+        color: '#FFCD3C',
+        smooth: 0.5,
+        areaStyle: {
+          opacity: 0.1
+        },
+      },
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function initRateChart(canvas, width, height, dpr,) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+  
+  var option = {
+    title: {
+      text: ''
+    },
+    color: ['#FFCD3C'],
+    tooltip: {
+      formatter: `{a}: {c}% \n ${new Date().getFullYear()}-{b}`
+    },
+    xAxis: {
+      data: rateXdata,
+      axisLine: {
+        show: true
+      }
+    },
+    // yAxis: {
+    //   max: 100
+    // },
+    series: [
+      {
+        data: rateYdata,
+        type: 'line',
+        name: '转化率',
+        symbolSize: 2,
+        showSymbol: false,
+        symbol: 'circle'
+      },
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function fotmatDate (date, fmt='yyyy.MM.dd') {
+  if (!(date instanceof Date)) {
+    date = changeDateFormat(date)
+  }
+  var o = {   
+    "M+" : date.getMonth()+1,                 //月份   
+    "d+" : date.getDate(),                    //日   
+    "h+" : date.getHours(),                   //小时   
+    "m+" : date.getMinutes(),                 //分   
+    "s+" : date.getSeconds(),                 //秒   
+    "q+" : Math.floor((date.getMonth()+3)/3), //季度   
+    "S"  : date.getMilliseconds()             //毫秒   
+  };   
+  if(/(y+)/.test(fmt))   
+    fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length));   
+  for(var k in o)   
+    if(new RegExp("("+ k +")").test(fmt))   
+  fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));   
+  return fmt;   
+}
+
+// 转格式 否则IOS下会有问题
+function changeDateFormat(cellval) {
+	var dateVal = cellval + "";
+	if (cellval != null) {
+    dateVal = dateVal.slice(0, 19)
+		dateVal = dateVal.replace("T"," ").replace("Z","").replace(/-/g, "/");
+		var date = new Date(dateVal);
+		
+		date = new Date(date.setHours(date.getHours()));
+		
+		return date
+	}
+}
+
+// 数值太大转为带万的文字
+function formatNum (num) {
+  return num >= 10000 ? num >= 1000000 ? `99W+` : `${(num/10000).toFixed(2).replace(/[.]?0+$/g,"")}W` : num
+}

+ 7 - 0
dataReport/components/scene-report/scene-report.json

@@ -0,0 +1,7 @@
+{
+  "component": true,
+  "usingComponents": {
+    "ec-canvas": "../ec-canvas/ec-canvas",
+    "icon": "/component/common/icon/index"
+  }
+}

+ 38 - 0
dataReport/components/scene-report/scene-report.wxml

@@ -0,0 +1,38 @@
+<view class="title"></view>
+<picker bindchange="bindPickerChange" value="{{index}}" range="{{brandNameList}}">
+  <view class="title">
+    {{brandNameList[index]}}<icon class="arrow-icon" icon="scene_tit_arrow" />
+  </view>
+</picker>
+<view class="sub-title">综合数据</view>
+<view class="data-num-w">
+  <view class="data-num" wx:for="{{ dataNumList }}" wx:key="index" >
+    <view class="value" style="background-color: {{item.bgColor}}; color: {{ item.color }}">{{ item.value }}</view>
+    <view class="label">{{ item.label }}</view>
+  </view>
+</view>
+
+<view class="sub-title">可视化视图</view>
+<view class="chart-type-w">
+  <view class="chart-type {{ activeType === item.value ? 'is-active' : '' }}" wx:for="{{chartTypes}}" wx:key="index" data-value="{{item.value}}" bindtap="changeActiveType" >
+    <icon class="icon" icon="{{ item.icon }}" />
+    <text >{{ item.name }}</text>
+  </view>
+</view>
+<view class="canvas-container" wx:if="{{ activeType === chartTypes[0].value && ec }}">
+  <view class="sub-title">店铺浏览数据</view>
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-line" ec="{{ ec }}" />
+</view>
+<view class="canvas-container" wx:if="{{ activeType === chartTypes[1].value && ec2 }}">
+  <view class="sub-title">客户访问数据</view>
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-line" ec="{{ ec2 }}" />
+</view>
+<view class="canvas-container" wx:if="{{ activeType === chartTypes[2].value && ec3 }}">
+  <view class="sub-title">热点(商品)浏览数据</view>
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-bar" ec="{{ ec3 }}" />
+</view>
+
+<view class="canvas-container"  wx:if="{{ rateEc }}">
+  <view class="sub-title">商机转化率</view>
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-bar" ec="{{ rateEc }}" />
+</view>

+ 95 - 0
dataReport/components/scene-report/scene-report.wxss

@@ -0,0 +1,95 @@
+view, text, page {
+  background: #fff;
+}
+.title {
+  font-size: 40rpx;
+  text-align: center;
+  padding: 16rpx 0 22rpx;
+  font-weight: bold;
+  line-height: 56rpx;
+}
+.sub-title {
+  font-size: 34rpx;
+  color: #131d34;
+  margin-bottom: 32rpx;
+  font-weight: bold;
+}
+.data-num-w {
+  display: flex;
+  width: 100%;
+  justify-content: space-between;
+  margin: 42rpx 0 32rpx;
+  flex-wrap: wrap;
+  border-bottom: 2rpx solid #F7F7F7;
+  padding-bottom: 34rpx;
+}
+.arrow-icon {
+  margin-left: 15rpx;
+}
+.data-num {
+  flex: 0 0 25%;
+  text-align: center;
+}
+
+.data-num .value {
+  width: 120rpx;
+  height: 120rpx;
+  line-height: 120rpx;
+  text-align: center;
+  border-radius: 50%;
+  color: #17D2D2;
+  background: rgba(23,210,210,0.1);
+  margin: 0 auto 14rpx;
+  font-weight: bold;
+}
+
+
+.data-num .label {
+  font-size: 22rpx;
+}
+
+.canvas-container {
+  width: 100%;
+  height: 476rpx;
+  margin-bottom: 40rpx;
+  position: relative;
+}
+
+.canvas-container .sub-title {
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+.chart-type-w {
+  display: flex;
+  margin-bottom: 34rpx;
+}
+
+.chart-type {
+  flex: 1;
+  margin-right: 20rpx;
+  color: #909090;
+  border: 1px solid #909090;
+  border-radius: 8rpx;
+  height: 80rpx;
+  line-height: 76rpx;
+  padding-left: 32rpx;
+  text-align: right;
+  font-size: 22rpx;
+  display: flex;
+  align-items: center;
+}
+.chart-type:last-child {
+  margin-right: 0;
+}
+.icon {
+  height: 100%;
+  margin-right: 24rpx;
+}
+.chart-type.is-active {
+  border-color: #ED5D18;
+  color: #202020;
+}
+.chart-type.is-active .icon {
+  color: #ED5D18;
+}

+ 112 - 0
dataReport/components/summary/summary.js

@@ -0,0 +1,112 @@
+import * as echarts from '../ec-canvas/echarts'
+import { mergeOptions } from '../ec-canvas/defaultOption'
+function initChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    series: [
+      {
+        data: [[0, 1000], [3, 500], [6, 750], [9, 1400], [12, 1250], [15, 1600], [18, 1800]],
+        type: 'line',
+        smooth: true,
+        name: '主页访问量',
+        areaStyle: {
+          opacity: 0.1
+        },
+        symbolSize: 0
+      },
+      {
+        data: [[0, 1250], [3, 600], [6, 800], [9, 1500], [12, 1400], [15, 1750], [18, 2000]],
+        type: 'line',
+        smooth: true,
+        name: '场景浏览量',
+        areaStyle: {
+          opacity: 0.1
+        },
+        symbolSize: 0
+      },
+    ]
+  };
+
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+function initBarChart(canvas, width, height, dpr) {
+  const chart = echarts.init(canvas, null, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+
+  var option = {
+    title: {
+      text: '商品统计数据'
+    },
+    xAxis: {
+      type: 'category',
+      data: ['aaa', 'aaa', 'aaa', 'asdsad'],
+      axisLabel: {
+        align: 'center'
+      }
+    },
+    series: [
+      {
+        name: '浏览量',
+        type: 'bar',
+        data: [2.0, 4.9, 7.0, 11],
+        barGap: 0,
+        barWidth: 10,
+        itemStyle: {
+          barBorderRadius:[10, 10, 0, 0],
+        }
+      },
+      {
+        name: '询盘数',
+        type: 'bar',
+        data: [2.0, 4.9, 7.0, 11],
+        barGap: 0,
+        barWidth: 10,
+        itemStyle: {
+          barBorderRadius:[10, 10, 0, 0],
+        },
+      },
+    ]
+  };
+  chart.setOption(mergeOptions(option));
+  return chart;
+}
+
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    ec: {
+      onInit: initChart
+    },
+    barEc: {
+      onInit: initBarChart
+    }
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+  }
+})

+ 6 - 0
dataReport/components/summary/summary.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "ec-canvas": "../ec-canvas/ec-canvas"
+  }
+}

+ 24 - 0
dataReport/components/summary/summary.wxml

@@ -0,0 +1,24 @@
+<view class="data-num-w">
+  <view class="data-num">
+    <view class="value">9268</view>
+    <view class="label">展位访问量</view>
+  </view>
+  <view class="data-num">
+    <view class="value scene">9268</view>
+    <view class="label">场景浏览量</view>
+  </view>
+  <view class="data-num">
+    <view class="value customer">9268</view>
+    <view class="label">客户询盘量</view>
+  </view>
+</view>
+
+<view class="canvas-container">
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-line" ec="{{ ec }}" />
+  
+</view>
+
+<view class="canvas-container">
+  <ec-canvas id="mychart-dom-multi-line"  canvas-id="mychart-multi-line" ec="{{ barEc }}" />
+  
+</view>

+ 41 - 0
dataReport/components/summary/summary.wxss

@@ -0,0 +1,41 @@
+.data-num-w {
+  display: flex;
+  width: 100%;
+  justify-content: space-between;
+  margin: 42rpx 0 46rpx;
+}
+.data-num {
+  flex: 1;
+  text-align: center;
+}
+
+.data-num .value {
+  width: 120rpx;
+  height: 120rpx;
+  line-height: 120rpx;
+  text-align: center;
+  border-radius: 50%;
+  color: #17D2D2;
+  background: rgba(23,210,210,0.1);
+  margin: 0 auto 14rpx;
+  font-weight: bold;
+}
+.value.scene {
+  color: #738EFE;
+  background:rgba(115,142,254,0.1);
+}
+
+.value.customer {
+  color: #4DAEFF;
+  background:rgba(77,174,255,0.1);
+}
+
+.data-num .label {
+  font-size: 22rpx;
+}
+
+.canvas-container {
+  width: 100%;
+  height: 476rpx;
+  margin-bottom: 40rpx;
+}

+ 15 - 0
dataReport/pages/data-report/data-report.js

@@ -0,0 +1,15 @@
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  }
+})

+ 8 - 0
dataReport/pages/data-report/data-report.json

@@ -0,0 +1,8 @@
+{
+  "usingComponents": {
+    "summary": "./../../components/summary/summary",
+    "customer-report": "./../../components/customer-report/index",
+    "scene-report": "./../../components/scene-report/scene-report"
+  },
+  "navigationBarTitleText": "数据统计"
+}

+ 4 - 0
dataReport/pages/data-report/data-report.wxml

@@ -0,0 +1,4 @@
+<view class="container">
+  <!-- <view class="title">这里是展位名称</view> -->
+  <scene-report  />
+</view>

+ 19 - 0
dataReport/pages/data-report/data-report.wxss

@@ -0,0 +1,19 @@
+page {
+  font-size: 30rpx;
+  color: #131D34;
+}
+view, text, page {
+  background: #fff;
+}
+.container {
+  padding: 0 46rpx 100rpx;
+  margin-top: 16rpx;
+  background: #fff;
+}
+.title {
+  font-size: 40rpx;
+  text-align: center;
+  padding: 16rpx 0 22rpx;
+  font-weight: bold;
+  line-height: 56rpx;
+}

BIN
lib/wxParse/emojis/00.gif


BIN
lib/wxParse/emojis/01.gif


BIN
lib/wxParse/emojis/02.gif


BIN
lib/wxParse/emojis/03.gif


BIN
lib/wxParse/emojis/04.gif


BIN
lib/wxParse/emojis/05.gif


BIN
lib/wxParse/emojis/06.gif


BIN
lib/wxParse/emojis/07.gif


BIN
lib/wxParse/emojis/08.gif


+ 0 - 0
lib/wxParse/emojis/09.gif


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott