bill-lai 3 år sedan
förälder
incheckning
706a062306
100 ändrade filer med 14254 tillägg och 0 borttagningar
  1. 14 0
      .gitignore
  2. 276 0
      app.js
  3. 39 0
      app.json
  4. 62 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. 54 0
      component/auth/auth.js
  13. 3 0
      component/auth/auth.json
  14. 20 0
      component/auth/auth.wxml
  15. 81 0
      component/auth/auth.wxss
  16. 56 0
      component/component/auth/auth.js
  17. 1120 0
      component/image-cropper/image-cropper.js
  18. 3 0
      component/image-cropper/image-cropper.json
  19. 24 0
      component/image-cropper/image-cropper.wxml
  20. 123 0
      component/image-cropper/image-cropper.wxss
  21. 85 0
      component/trtc-room/common/constants.js
  22. 346 0
      component/trtc-room/controller/user-controller.js
  23. 14 0
      component/trtc-room/libs/mta_analysis.js
  24. 1 0
      component/trtc-room/libs/tim-wx.js
  25. 33 0
      component/trtc-room/model/pusher.js
  26. 38 0
      component/trtc-room/model/stream.js
  27. 17 0
      component/trtc-room/model/user.js
  28. 76 0
      component/trtc-room/template/custom/custom.wxml
  29. 8 0
      component/trtc-room/template/custom/custom.wxss
  30. 2267 0
      component/trtc-room/trtc-room.js
  31. 4 0
      component/trtc-room/trtc-room.json
  32. 5 0
      component/trtc-room/trtc-room.wxml
  33. 13 0
      component/trtc-room/trtc-room.wxss
  34. 21 0
      component/trtc-room/utils/compare-version.js
  35. 50 0
      component/trtc-room/utils/environment.js
  36. 62 0
      component/trtc-room/utils/event.js
  37. 163 0
      component/voice/voice.js
  38. 6 0
      component/voice/voice.json
  39. 9 0
      component/voice/voice.wxml
  40. 20 0
      component/voice/voice.wxss
  41. 16 0
      config.js
  42. 119 0
      config/api.js
  43. 301 0
      lib/wxParse/html2json.js
  44. 192 0
      lib/wxParse/htmlparser.js
  45. 2532 0
      lib/wxParse/showdown.js
  46. 207 0
      lib/wxParse/wxDiscode.js
  47. 159 0
      lib/wxParse/wxParse.js
  48. 967 0
      lib/wxParse/wxParse.wxml
  49. 210 0
      lib/wxParse/wxParse.wxss
  50. 3 0
      package-lock.json
  51. 42 0
      pages/index/data.js
  52. 346 0
      pages/index/index.js
  53. 5 0
      pages/index/index.json
  54. 75 0
      pages/index/index.wxml
  55. 608 0
      pages/index/index.wxss
  56. 76 0
      pages/login/login.js
  57. 3 0
      pages/login/login.json
  58. 19 0
      pages/login/login.wxml
  59. 72 0
      pages/login/login.wxss
  60. 62 0
      pages/pay/pay.js
  61. 3 0
      pages/pay/pay.json
  62. 31 0
      pages/pay/pay.wxml
  63. 121 0
      pages/pay/pay.wxss
  64. 59 0
      pages/payResult/payResult.js
  65. 4 0
      pages/payResult/payResult.json
  66. 29 0
      pages/payResult/payResult.wxml
  67. 59 0
      pages/payResult/payResult.wxss
  68. 212 0
      pages/reserve/reserve.js
  69. 4 0
      pages/reserve/reserve.json
  70. 68 0
      pages/reserve/reserve.wxml
  71. 259 0
      pages/reserve/reserve.wxss
  72. 69 0
      pages/scene/scene.js
  73. 3 0
      pages/scene/scene.json
  74. 3 0
      pages/scene/scene.wxml
  75. 1 0
      pages/scene/scene.wxss
  76. 112 0
      pages/search/search.js
  77. 4 0
      pages/search/search.json
  78. 66 0
      pages/search/search.wxml
  79. 455 0
      pages/search/search.wxss
  80. 67 0
      pages/share.js
  81. 3 0
      pages/share.json
  82. 2 0
      pages/share.wxml
  83. 1 0
      pages/share.wxss
  84. 65 0
      pages/ucenter/address/address.js
  85. 4 0
      pages/ucenter/address/address.json
  86. 24 0
      pages/ucenter/address/address.wxml
  87. 129 0
      pages/ucenter/address/address.wxss
  88. 351 0
      pages/ucenter/addressAdd/addressAdd.js
  89. 4 0
      pages/ucenter/addressAdd/addressAdd.json
  90. 45 0
      pages/ucenter/addressAdd/addressAdd.wxml
  91. 180 0
      pages/ucenter/addressAdd/addressAdd.wxss
  92. 88 0
      pages/ucenter/collect/collect.js
  93. 4 0
      pages/ucenter/collect/collect.json
  94. 18 0
      pages/ucenter/collect/collect.wxml
  95. 85 0
      pages/ucenter/collect/collect.wxss
  96. 115 0
      pages/ucenter/feedback/feedback.js
  97. 4 0
      pages/ucenter/feedback/feedback.json
  98. 29 0
      pages/ucenter/feedback/feedback.wxml
  99. 121 0
      pages/ucenter/feedback/feedback.wxss
  100. 0 0
      pages/ucenter/footprint/footprint.js

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

+ 276 - 0
app.js

@@ -0,0 +1,276 @@
+const util = require('./utils/util.js');
+const api = require('./config/api');
+
+const bayCbs = []
+
+! 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: '/static/images/share.png',
+            };
+
+            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 && onShow.call(this, ...args)
+        }
+
+        PageTmp(pageConfig);
+    };
+}();
+
+const voiceCbs = []
+const voiceProps = {
+    noMute: false,
+    pullUrls: [],
+    pushUrl: false,
+}
+const loginCbs = []
+const loginProps = {
+    isLogin: true
+};
+
+App({
+    appHideCb: '',
+    onHide() {
+        this.appHideCb && this.appHideCb()
+    },
+    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;
+    },
+
+    orderBuyCbs(cb) {
+        bayCbs.push(cb)
+    },
+
+    callOrderBuyCbs(...args) {
+        while(bayCbs.length) {
+            bayCbs.shift()(...args)
+        }
+    },
+
+    async checkNetStatu() {
+        if (!(await util.checkNetStatu())) {
+            return wx.redirectTo({
+                url: '/pages/offline/offline?url=' + decodeURI(this.getCurrentPages()),
+            })
+        }
+    },
+
+    updateCardCount() {
+        return;
+        util.request(api.CartList).then((res) => {
+            //显示红字,badge
+            if (!res.data || !res.data.cartTotal) {
+                return
+            }
+            // global.setTabBarBadge({
+            //     index: 3,
+            //     text: res.data.cartTotal.goodsCount ,
+            // });
+            if (res.data.cartTotal.goodsCount) {
+                wx.setTabBarBadge({
+                    index: 3,
+                    text: res.data.cartTotal.goodsCount + '',
+                });
+            } else {
+                wx.removeTabBarBadge({
+                    index: 3,
+                });
+            }
+        })
+    },
+    onLaunch() {
+        wx.setStorageSync('lastSceneNum', null)
+    }
+})

+ 39 - 0
app.json

@@ -0,0 +1,39 @@
+{
+  "pages": [
+    
+    "pages/index/index",
+    "pages/search/search",
+    "pages/webview/index",
+    "pages/ucenter/index/index",
+    "pages/login/login",
+    "pages/ucenter/order/order",
+    "pages/scene/scene",
+    "pages/reserve/reserve",
+    "pages/user/user",
+    "pages/pay/pay",
+    "pages/payResult/payResult",
+    "pages/ucenter/help/help",
+    "pages/ucenter/helpInfo/helpInfo"
+  ],
+  "permission": {
+    "scope.userLocation": {
+      "desc": "你的位置信息将用于小程序为您推荐附近餐厅"
+    }
+  },
+  "window": {
+    "backgroundTextStyle": "dark",
+    "navigationBarBackgroundColor": "#fff",
+    "navigationBarTitleText": "广东火调VR",
+    "navigationBarTextStyle": "black",
+    "enablePullDownRefresh": true
+  },
+  "networkTimeout": {
+    "request": 10000,
+    "downloadFile": 10000
+  },
+  "debug": true,
+  "usingComponents": {
+    "lip-sync": "/component/voice/voice"
+  },
+  "sitemapLocation": "sitemap.json"
+}

+ 62 - 0
app.wxss

@@ -0,0 +1,62 @@
+/**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-Regular,helvetica,'Heiti SC';
+  /* font-family: PingFangSC-Regular, sans-serif; */
+  /* font-family: PingFangSC-Medium, PingFang 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


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
common/font/iconfont.js


BIN
common/font/iconfont.ttf


BIN
common/font/iconfont.woff


BIN
common/font/iconfont.woff2


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 225 - 0
common/font/iconfont.wxss


+ 0 - 0
common/styles/index.wxss


+ 54 - 0
component/auth/auth.js

@@ -0,0 +1,54 @@
+// component/auth/auth.js
+
+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)
+
+  },
+  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) {
+      console.log(e)
+    },
+  }
+})

+ 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;
+}

+ 56 - 0
component/component/auth/auth.js

@@ -0,0 +1,56 @@
+// component/auth/auth.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) {
+      console.log(e)
+    },
+  }
+})

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 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;
+}

+ 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

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 14 - 0
component/trtc-room/libs/mta_analysis.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 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;
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 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;
+}

+ 16 - 0
config.js

@@ -0,0 +1,16 @@
+const dev = {
+  // requestHost: 'http://192.168.0.29:8585/',
+  requestHost: 'https://testhuodiao.4dkankan.com/',
+  socketHost: 'wss://testshop.4dkankan.com',
+  viewHost: 'https://test.4dkankan.com'
+}
+
+const mal = {
+  socketHost: 'wss://shop.4dkankan.com',
+  requestHost: 'https://huodiao.4dage.com',
+  viewHost: 'https://www.4dkankan.com'
+}
+
+
+export default dev
+// export default mal

+ 119 - 0
config/api.js

@@ -0,0 +1,119 @@
+import remote from '../config.js'
+
+export const API_BASE_URL = remote.requestHost + '';
+
+// const API_BASE_URL = 'https://testzhifangbao.4dage.com/api/'
+// const API_BASE_URL = 'http://192.168.0.207:8080/platform-framework/api/';
+/**  ----------------角色接口----------------   */
+export const getRoleList = API_BASE_URL + '/web/user/getAllRoleList'
+
+/** ------------------------------------------ */
+
+
+
+
+/**  ----------------用户接口----------------   */
+// 登录
+export const userLogin = API_BASE_URL +'/web/user/wxlogin'
+// 注册
+export const userReg = API_BASE_URL +'/web/user/register'
+// 发送注册短信
+export const sendUserMsg =API_BASE_URL + '/web/user/getMsgAuthCode'
+// 修改密码
+export const updatePsw = API_BASE_URL +'/web/user/changePassword'
+
+// 登出
+export const userLogout = API_BASE_URL +'/web/user/logout'
+// 获取用户列表
+export const getUserList = API_BASE_URL +'/web/user/getUserList'
+// 修改用户信息
+export const updateUser = API_BASE_URL +'/web/user/changDeptAndRole'
+// 获取验证码
+export const getCode = API_BASE_URL +'/web/user/getRandCode'
+// 修改用户状态
+export const changeUserStatus =API_BASE_URL + '/web/user/changeStatus'
+export const updateUserPWD = ''
+/** ------------------------------------------ */
+
+
+
+
+/** ----------------部门接口---------------- */
+// 获取公司列表
+export const getCompanyList = API_BASE_URL +'/web/department/getAll'
+/** ------------------------------------------ */
+
+
+
+
+/** ----------------VR模型接口---------------- */
+// 获取场景列表
+export const getSceneList =API_BASE_URL + '/web/scene/getScenePage'
+// 通过相机获取场景列表
+export const getSceneListByCamera = API_BASE_URL +'/web/scene/getSceneList'
+// 同步场景
+export const asyncScene =API_BASE_URL + '/web/scene/synchronizeScene'
+// 删除场景
+export const deleteScene =API_BASE_URL + '/web/scene/delete'
+/** ------------------------------------------ */
+
+
+export const searchList = API_BASE_URL + '/web/fireProject/queryList'
+
+/** ----------------相机接口---------------- */
+// 获取相机列表
+export const getCameraList = API_BASE_URL +'/web/camera/getUserCameraList'
+// 获取相机选项
+export const getCameraOptions =API_BASE_URL + '/web/camera/getUserCameraList?pageNum=1&pageSize=100000'
+// 添加相机
+export const insertCamera = API_BASE_URL +'/web/camera/bindNew'
+
+// 相机解除绑定设备
+export const unbindCamera =API_BASE_URL + '/web/camera/unbind'
+/** ------------------------------------------ */
+
+
+
+/** ----------------火调项目---------------- */
+// 获取火调列表
+export const getFireList =API_BASE_URL + '/web/fireProject/queryProject'
+// 新增火调
+export const insertFire =API_BASE_URL + '/web/fireProject/addNewProject'
+// 修改火调
+export const updateFire = API_BASE_URL +'/web/fireProject/updateProject'
+// 火调设置为教学
+export const setTeach =API_BASE_URL + '/web/fireProject/setTeach'
+// 火调链接地址设置密码
+export const fireSetPsw = API_BASE_URL +'/web/fireProject/updateRandomCode'
+// 获取火调详情
+export const fireDetail = API_BASE_URL +'/web/fireProject/wxAnonGetDetail'
+/** ------------------------------------------ */
+
+export const axLogin = API_BASE_URL + '/web/user/wxRealLogin'
+
+/** ----------------火调附件---------------- */
+// 获取火调列表
+export const getAttachList = API_BASE_URL + '/web/attachment/wxAnonGetList'
+// 新增火调
+export const insertAttach = API_BASE_URL +'/web/attachment/addNew'
+// 火调设置为教学
+export const deleteAttach =API_BASE_URL + '/web/fireProject/setTeach'
+// 取消教学
+export const setUnTeach = API_BASE_URL +'/web/fireProject/setUnTeach'
+// 火调上传word,pdf文件
+export const uploadAttachFile =API_BASE_URL + '/web/fireProject/uploadFile'
+// 火调上传jpg,png文件
+export const uploadAttachImage = API_BASE_URL +'/web/fireProject/uploadImage'
+/** ------------------------------------------ */
+
+
+// 不需要登录就能请求的接口
+export const notLoginUrls = [userLogin, getCode, sendUserMsg, userReg, updatePsw]
+// 带文件的请求
+export const fileUrls = [uploadAttachFile, uploadAttachImage]
+// 需要限定卫GET请求方式的url
+export const GetUrls = [getRoleList, getCompanyList]
+// 需要限定请求方式的url
+export const PostUrls = [getFireList ]
+// 恒大接口
+export const HdUrls = []

+ 301 - 0
lib/wxParse/html2json.js

@@ -0,0 +1,301 @@
+/**
+ * html2Json 改造来自: https://github.com/Jxck/html2json
+ * 
+ * 
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ * 
+ * github地址: https://github.com/icindy/wxParse
+ * 
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+
+var __placeImgeUrlHttps = "https";
+var __emojisReg = '';
+var __emojisBaseSrc = '';
+var __emojis = {};
+var wxDiscode = require('./wxDiscode.js');
+var HTMLParser = require('./htmlparser.js');
+// Empty Elements - HTML 5
+var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr");
+// Block Elements - HTML 5
+var block = makeMap("br,a,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video");
+
+// Inline Elements - HTML 5
+var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
+
+// Elements that you can, intentionally, leave open
+// (and which close themselves)
+var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
+
+// Attributes that have their values filled in disabled="disabled"
+var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
+
+// Special Elements (can contain anything)
+var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
+function makeMap(str) {
+    var obj = {}, items = str.split(",");
+    for (var i = 0; i < items.length; i++)
+        obj[items[i]] = true;
+    return obj;
+}
+
+function q(v) {
+    return '"' + v + '"';
+}
+
+function removeDOCTYPE(html) {
+    return html
+        .replace(/<\?xml.*\?>\n/, '')
+        .replace(/<.*!doctype.*\>\n/, '')
+        .replace(/<.*!DOCTYPE.*\>\n/, '');
+}
+
+function trimHtml(html) {
+  return html
+        .replace(/\r?\n+/g, '')
+        .replace(/<!--.*?-->/ig, '')
+        .replace(/\/\*.*?\*\//ig, '')
+        .replace(/[ ]+</ig, '<')
+}
+
+
+function html2json(html, bindName) {
+    //处理字符串
+    html = removeDOCTYPE(html);
+    html = trimHtml(html);
+    html = wxDiscode.strDiscode(html);
+    //生成node节点
+    var bufArray = [];
+    var results = {
+        node: bindName,
+        nodes: [],
+        images:[],
+        imageUrls:[]
+    };
+    var index = 0;
+    HTMLParser(html, {
+        start: function (tag, attrs, unary) {
+            //debug(tag, attrs, unary);
+            // node for this element
+            var node = {
+                node: 'element',
+                tag: tag,
+            };
+
+            if (bufArray.length === 0) {
+                node.index = index.toString()
+                index += 1
+            } else {
+                var parent = bufArray[0];
+                if (parent.nodes === undefined) {
+                    parent.nodes = [];
+                }
+                node.index = parent.index + '.' + parent.nodes.length
+            }
+
+            if (block[tag]) {
+                node.tagType = "block";
+            } else if (inline[tag]) {
+                node.tagType = "inline";
+            } else if (closeSelf[tag]) {
+                node.tagType = "closeSelf";
+            }
+
+            if (attrs.length !== 0) {
+                node.attr = attrs.reduce(function (pre, attr) {
+                    var name = attr.name;
+                    var value = attr.value;
+                    if (name == 'class') {
+                        //  value = value.join("")
+                        node.classStr = value;
+                    }
+                    // has multi attibutes
+                    // make it array of attribute
+                    if (name == 'style') {
+                        //  value = value.join("")
+                        node.styleStr = value;
+                    }
+                    if (value.match(/ /)) {
+                        value = value.split(' ');
+                    }
+                    
+
+                    // if attr already exists
+                    // merge it
+                    if (pre[name]) {
+                        if (Array.isArray(pre[name])) {
+                            // already array, push to last
+                            pre[name].push(value);
+                        } else {
+                            // single value, make it array
+                            pre[name] = [pre[name], value];
+                        }
+                    } else {
+                        // not exist, put it
+                        pre[name] = value;
+                    }
+
+                    return pre;
+                }, {});
+            }
+
+            //对img添加额外数据
+            if (node.tag === 'img') {
+                node.imgIndex = results.images.length;
+                var imgUrl = node.attr.src;
+                if (imgUrl[0] == '') {
+                    imgUrl.splice(0, 1);
+                }
+                imgUrl = wxDiscode.urlToHttpUrl(imgUrl, __placeImgeUrlHttps);
+                node.attr.src = imgUrl;
+                node.from = bindName;
+                results.images.push(node);
+                results.imageUrls.push(imgUrl);
+            }
+            
+            // 处理font标签样式属性
+            if (node.tag === 'font') {
+                var fontSize = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large'];
+                var styleAttrs = {
+                    'color': 'color',
+                    'face': 'font-family',
+                    'size': 'font-size'
+                };
+                if (!node.attr.style) node.attr.style = [];
+                if (!node.styleStr) node.styleStr = '';
+                for (var key in styleAttrs) {
+                    if (node.attr[key]) {
+                        var value = key === 'size' ? fontSize[node.attr[key]-1] : node.attr[key];
+                        node.attr.style.push(styleAttrs[key]);
+                        node.attr.style.push(value);
+                        node.styleStr += styleAttrs[key] + ': ' + value + ';';
+                    }
+                }
+            }
+
+            //临时记录source资源
+            if(node.tag === 'source'){
+                results.source = node.attr.src;
+            }
+            
+            if (unary) {
+                // if this tag doesn't have end tag
+                // like <img src="hoge.png"/>
+                // add to parents
+                var parent = bufArray[0] || results;
+                if (parent.nodes === undefined) {
+                    parent.nodes = [];
+                }
+                parent.nodes.push(node);
+            } else {
+                bufArray.unshift(node);
+            }
+        },
+        end: function (tag) {
+            //debug(tag);
+            // merge into parent tag
+            var node = bufArray.shift();
+            if (node.tag !== tag) console.error('invalid state: mismatch end tag');
+
+            //当有缓存source资源时于于video补上src资源
+            if(node.tag === 'video' && results.source){
+                node.attr.src = results.source;
+                delete results.source;
+            }
+            
+            if (bufArray.length === 0) {
+                results.nodes.push(node);
+            } else {
+                var parent = bufArray[0];
+                if (parent.nodes === undefined) {
+                    parent.nodes = [];
+                }
+                parent.nodes.push(node);
+            }
+        },
+        chars: function (text) {
+            //debug(text);
+            var node = {
+                node: 'text',
+                text: text,
+                textArray:transEmojiStr(text)
+            };
+            
+            if (bufArray.length === 0) {
+                node.index = index.toString()
+                index += 1
+                results.nodes.push(node);
+            } else {
+                var parent = bufArray[0];
+                if (parent.nodes === undefined) {
+                    parent.nodes = [];
+                }
+                node.index = parent.index + '.' + parent.nodes.length
+                parent.nodes.push(node);
+            }
+        },
+        comment: function (text) {
+            //debug(text);
+            // var node = {
+            //     node: 'comment',
+            //     text: text,
+            // };
+            // var parent = bufArray[0];
+            // if (parent.nodes === undefined) {
+            //     parent.nodes = [];
+            // }
+            // parent.nodes.push(node);
+        },
+    });
+    return results;
+};
+
+function transEmojiStr(str){
+  // var eReg = new RegExp("["+__reg+' '+"]");
+//   str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
+  
+  var emojiObjs = [];
+  //如果正则表达式为空
+  if(__emojisReg.length == 0 || !__emojis){
+      var emojiObj = {}
+      emojiObj.node = "text";
+      emojiObj.text = str;
+      array = [emojiObj];
+      return array;
+  }
+  //这个地方需要调整
+  str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
+  var eReg = new RegExp("[:]");
+  var array = str.split(eReg);
+  for(var i = 0; i < array.length; i++){
+    var ele = array[i];
+    var emojiObj = {};
+    if(__emojis[ele]){
+      emojiObj.node = "element";
+      emojiObj.tag = "emoji";
+      emojiObj.text = __emojis[ele];
+      emojiObj.baseSrc= __emojisBaseSrc;
+    }else{
+      emojiObj.node = "text";
+      emojiObj.text = ele;
+    }
+    emojiObjs.push(emojiObj);
+  }
+  
+  return emojiObjs;
+}
+
+function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){
+    __emojisReg = reg;
+    __emojisBaseSrc=baseSrc;
+    __emojis=emojis;
+}
+
+module.exports = {
+    html2json: html2json,
+    emojisInit:emojisInit
+};
+

+ 192 - 0
lib/wxParse/htmlparser.js

@@ -0,0 +1,192 @@
+/**
+ * 
+ * htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
+ * 
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ * 
+ * github地址: https://github.com/icindy/wxParse
+ * 
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+// Regular Expressions for parsing tags and attributes
+var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
+	endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
+	attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
+
+// Empty Elements - HTML 5
+var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr");
+
+// Block Elements - HTML 5
+var block = makeMap("a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video");
+
+// Inline Elements - HTML 5
+var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
+
+// Elements that you can, intentionally, leave open
+// (and which close themselves)
+var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
+
+// Attributes that have their values filled in disabled="disabled"
+var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
+
+// Special Elements (can contain anything)
+var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
+
+function HTMLParser(html, handler) {
+	var index, chars, match, stack = [], last = html;
+	stack.last = function () {
+		return this[this.length - 1];
+	};
+
+	while (html) {
+		chars = true;
+
+		// Make sure we're not in a script or style element
+		if (!stack.last() || !special[stack.last()]) {
+
+			// Comment
+			if (html.indexOf("<!--") == 0) {
+				index = html.indexOf("-->");
+
+				if (index >= 0) {
+					if (handler.comment)
+						handler.comment(html.substring(4, index));
+					html = html.substring(index + 3);
+					chars = false;
+				}
+
+				// end tag
+			} else if (html.indexOf("</") == 0) {
+				match = html.match(endTag);
+
+				if (match) {
+					html = html.substring(match[0].length);
+					match[0].replace(endTag, parseEndTag);
+					chars = false;
+				}
+
+				// start tag
+			} else if (html.indexOf("<") == 0) {
+				match = html.match(startTag);
+
+				if (match) {
+					html = html.substring(match[0].length);
+					match[0].replace(startTag, parseStartTag);
+					chars = false;
+				}
+			}
+
+			if (chars) {
+				index = html.indexOf("<");
+				var text = ''
+				while (index === 0) {
+                                  text += "<";
+                                  html = html.substring(1);
+                                  index = html.indexOf("<");
+				}
+				text += index < 0 ? html : html.substring(0, index);
+				html = index < 0 ? "" : html.substring(index);
+
+				if (handler.chars)
+					handler.chars(text);
+			}
+
+		} else {
+
+			html = html.replace(new RegExp("([\\s\\S]*?)<\/" + stack.last() + "[^>]*>"), function (all, text) {
+				text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, "$1$2");
+				if (handler.chars)
+					handler.chars(text);
+
+				return "";
+			});
+
+
+			parseEndTag("", stack.last());
+		}
+
+		if (html == last)
+			throw "Parse Error: " + html;
+		last = html;
+	}
+
+	// Clean up any remaining tags
+	parseEndTag();
+
+	function parseStartTag(tag, tagName, rest, unary) {
+		tagName = tagName.toLowerCase();
+
+		if (block[tagName]) {
+			while (stack.last() && inline[stack.last()]) {
+				parseEndTag("", stack.last());
+			}
+		}
+
+		if (closeSelf[tagName] && stack.last() == tagName) {
+			parseEndTag("", tagName);
+		}
+
+		unary = empty[tagName] || !!unary;
+
+		if (!unary)
+			stack.push(tagName);
+
+		if (handler.start) {
+			var attrs = [];
+
+			rest.replace(attr, function (match, name) {
+				var value = arguments[2] ? arguments[2] :
+					arguments[3] ? arguments[3] :
+						arguments[4] ? arguments[4] :
+							fillAttrs[name] ? name : "";
+
+				attrs.push({
+					name: name,
+					value: value,
+					escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
+				});
+			});
+
+			if (handler.start) {
+				handler.start(tagName, attrs, unary);
+			}
+
+		}
+	}
+
+	function parseEndTag(tag, tagName) {
+		// If no tag name is provided, clean shop
+		if (!tagName)
+			var pos = 0;
+
+		// Find the closest opened tag of the same type
+		else {
+			tagName = tagName.toLowerCase();
+			for (var pos = stack.length - 1; pos >= 0; pos--)
+				if (stack[pos] == tagName)
+					break;
+		}
+		if (pos >= 0) {
+			// Close all the open elements, up the stack
+			for (var i = stack.length - 1; i >= pos; i--)
+				if (handler.end)
+					handler.end(stack[i]);
+
+			// Remove the open elements from the stack
+			stack.length = pos;
+		}
+	}
+};
+
+
+function makeMap(str) {
+	var obj = {}, items = str.split(",");
+	for (var i = 0; i < items.length; i++)
+		obj[items[i]] = true;
+	return obj;
+}
+
+module.exports = HTMLParser;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2532 - 0
lib/wxParse/showdown.js


+ 207 - 0
lib/wxParse/wxDiscode.js

@@ -0,0 +1,207 @@
+// HTML 支持的数学符号
+function strNumDiscode(str){
+    str = str.replace(/&forall;/g, '∀');
+    str = str.replace(/&part;/g, '∂');
+    str = str.replace(/&exists;/g, '∃');
+    str = str.replace(/&empty;/g, '∅');
+    str = str.replace(/&nabla;/g, '∇');
+    str = str.replace(/&isin;/g, '∈');
+    str = str.replace(/&notin;/g, '∉');
+    str = str.replace(/&ni;/g, '∋');
+    str = str.replace(/&prod;/g, '∏');
+    str = str.replace(/&sum;/g, '∑');
+    str = str.replace(/&minus;/g, '−');
+    str = str.replace(/&lowast;/g, '∗');
+    str = str.replace(/&radic;/g, '√');
+    str = str.replace(/&prop;/g, '∝');
+    str = str.replace(/&infin;/g, '∞');
+    str = str.replace(/&ang;/g, '∠');
+    str = str.replace(/&and;/g, '∧');
+    str = str.replace(/&or;/g, '∨');
+    str = str.replace(/&cap;/g, '∩');
+    str = str.replace(/&cap;/g, '∪');
+    str = str.replace(/&int;/g, '∫');
+    str = str.replace(/&there4;/g, '∴');
+    str = str.replace(/&sim;/g, '∼');
+    str = str.replace(/&cong;/g, '≅');
+    str = str.replace(/&asymp;/g, '≈');
+    str = str.replace(/&ne;/g, '≠');
+    str = str.replace(/&le;/g, '≤');
+    str = str.replace(/&ge;/g, '≥');
+    str = str.replace(/&sub;/g, '⊂');
+    str = str.replace(/&sup;/g, '⊃');
+    str = str.replace(/&nsub;/g, '⊄');
+    str = str.replace(/&sube;/g, '⊆');
+    str = str.replace(/&supe;/g, '⊇');
+    str = str.replace(/&oplus;/g, '⊕');
+    str = str.replace(/&otimes;/g, '⊗');
+    str = str.replace(/&perp;/g, '⊥');
+    str = str.replace(/&sdot;/g, '⋅');
+    return str;
+}
+
+//HTML 支持的希腊字母
+function strGreeceDiscode(str){
+    str = str.replace(/&Alpha;/g, 'Α');
+    str = str.replace(/&Beta;/g, 'Β');
+    str = str.replace(/&Gamma;/g, 'Γ');
+    str = str.replace(/&Delta;/g, 'Δ');
+    str = str.replace(/&Epsilon;/g, 'Ε');
+    str = str.replace(/&Zeta;/g, 'Ζ');
+    str = str.replace(/&Eta;/g, 'Η');
+    str = str.replace(/&Theta;/g, 'Θ');
+    str = str.replace(/&Iota;/g, 'Ι');
+    str = str.replace(/&Kappa;/g, 'Κ');
+    str = str.replace(/&Lambda;/g, 'Λ');
+    str = str.replace(/&Mu;/g, 'Μ');
+    str = str.replace(/&Nu;/g, 'Ν');
+    str = str.replace(/&Xi;/g, 'Ν');
+    str = str.replace(/&Omicron;/g, 'Ο');
+    str = str.replace(/&Pi;/g, 'Π');
+    str = str.replace(/&Rho;/g, 'Ρ');
+    str = str.replace(/&Sigma;/g, 'Σ');
+    str = str.replace(/&Tau;/g, 'Τ');
+    str = str.replace(/&Upsilon;/g, 'Υ');
+    str = str.replace(/&Phi;/g, 'Φ');
+    str = str.replace(/&Chi;/g, 'Χ');
+    str = str.replace(/&Psi;/g, 'Ψ');
+    str = str.replace(/&Omega;/g, 'Ω');
+
+    str = str.replace(/&alpha;/g, 'α');
+    str = str.replace(/&beta;/g, 'β');
+    str = str.replace(/&gamma;/g, 'γ');
+    str = str.replace(/&delta;/g, 'δ');
+    str = str.replace(/&epsilon;/g, 'ε');
+    str = str.replace(/&zeta;/g, 'ζ');
+    str = str.replace(/&eta;/g, 'η');
+    str = str.replace(/&theta;/g, 'θ');
+    str = str.replace(/&iota;/g, 'ι');
+    str = str.replace(/&kappa;/g, 'κ');
+    str = str.replace(/&lambda;/g, 'λ');
+    str = str.replace(/&mu;/g, 'μ');
+    str = str.replace(/&nu;/g, 'ν');
+    str = str.replace(/&xi;/g, 'ξ');
+    str = str.replace(/&omicron;/g, 'ο');
+    str = str.replace(/&pi;/g, 'π');
+    str = str.replace(/&rho;/g, 'ρ');
+    str = str.replace(/&sigmaf;/g, 'ς');
+    str = str.replace(/&sigma;/g, 'σ');
+    str = str.replace(/&tau;/g, 'τ');
+    str = str.replace(/&upsilon;/g, 'υ');
+    str = str.replace(/&phi;/g, 'φ');
+    str = str.replace(/&chi;/g, 'χ');
+    str = str.replace(/&psi;/g, 'ψ');
+    str = str.replace(/&omega;/g, 'ω');
+    str = str.replace(/&thetasym;/g, 'ϑ');
+    str = str.replace(/&upsih;/g, 'ϒ');
+    str = str.replace(/&piv;/g, 'ϖ');
+    str = str.replace(/&middot;/g, '·');
+    return str;
+}
+
+// 
+
+function strcharacterDiscode(str){
+    // 加入常用解析
+    str = str.replace(/&nbsp;/g, ' ');
+    str = str.replace(/&quot;/g, "'");
+    str = str.replace(/&amp;/g, '&');
+    // str = str.replace(/&lt;/g, '‹');
+    // str = str.replace(/&gt;/g, '›');
+
+    str = str.replace(/&lt;/g, '<');
+    str = str.replace(/&gt;/g, '>');
+    str = str.replace(/&#8226;/g, '•');
+
+    return str;
+}
+
+// HTML 支持的其他实体
+function strOtherDiscode(str){
+    str = str.replace(/&OElig;/g, 'Œ');
+    str = str.replace(/&oelig;/g, 'œ');
+    str = str.replace(/&Scaron;/g, 'Š');
+    str = str.replace(/&scaron;/g, 'š');
+    str = str.replace(/&Yuml;/g, 'Ÿ');
+    str = str.replace(/&fnof;/g, 'ƒ');
+    str = str.replace(/&circ;/g, 'ˆ');
+    str = str.replace(/&tilde;/g, '˜');
+    str = str.replace(/&ensp;/g, '');
+    str = str.replace(/&emsp;/g, '');
+    str = str.replace(/&thinsp;/g, '');
+    str = str.replace(/&zwnj;/g, '');
+    str = str.replace(/&zwj;/g, '');
+    str = str.replace(/&lrm;/g, '');
+    str = str.replace(/&rlm;/g, '');
+    str = str.replace(/&ndash;/g, '–');
+    str = str.replace(/&mdash;/g, '—');
+    str = str.replace(/&lsquo;/g, '‘');
+    str = str.replace(/&rsquo;/g, '’');
+    str = str.replace(/&sbquo;/g, '‚');
+    str = str.replace(/&ldquo;/g, '“');
+    str = str.replace(/&rdquo;/g, '”');
+    str = str.replace(/&bdquo;/g, '„');
+    str = str.replace(/&dagger;/g, '†');
+    str = str.replace(/&Dagger;/g, '‡');
+    str = str.replace(/&bull;/g, '•');
+    str = str.replace(/&hellip;/g, '…');
+    str = str.replace(/&permil;/g, '‰');
+    str = str.replace(/&prime;/g, '′');
+    str = str.replace(/&Prime;/g, '″');
+    str = str.replace(/&lsaquo;/g, '‹');
+    str = str.replace(/&rsaquo;/g, '›');
+    str = str.replace(/&oline;/g, '‾');
+    str = str.replace(/&euro;/g, '€');
+    str = str.replace(/&trade;/g, '™');
+
+    str = str.replace(/&larr;/g, '←');
+    str = str.replace(/&uarr;/g, '↑');
+    str = str.replace(/&rarr;/g, '→');
+    str = str.replace(/&darr;/g, '↓');
+    str = str.replace(/&harr;/g, '↔');
+    str = str.replace(/&crarr;/g, '↵');
+    str = str.replace(/&lceil;/g, '⌈');
+    str = str.replace(/&rceil;/g, '⌉');
+
+    str = str.replace(/&lfloor;/g, '⌊');
+    str = str.replace(/&rfloor;/g, '⌋');
+    str = str.replace(/&loz;/g, '◊');
+    str = str.replace(/&spades;/g, '♠');
+    str = str.replace(/&clubs;/g, '♣');
+    str = str.replace(/&hearts;/g, '♥');
+
+    str = str.replace(/&diams;/g, '♦');
+    str = str.replace(/&#39;/g, '\'');
+    return str;
+}
+
+function strMoreDiscode(str){
+    str = str.replace(/\r\n/g,"");  
+    str = str.replace(/\n/g,"");
+
+    str = str.replace(/code/g,"wxxxcode-style");
+    return str;
+}
+
+function strDiscode(str){
+    str = strNumDiscode(str);
+    str = strGreeceDiscode(str);
+    str = strcharacterDiscode(str);
+    str = strOtherDiscode(str);
+    str = strMoreDiscode(str);
+    return str;
+}
+function urlToHttpUrl(url,rep){
+    
+    var patt1 = new RegExp("^//");
+    var result = patt1.test(url);
+    if(result){
+        url = rep+":"+url;
+    }
+    return  url;
+}
+
+module.exports = {
+    strDiscode:strDiscode,
+    urlToHttpUrl:urlToHttpUrl
+}

+ 159 - 0
lib/wxParse/wxParse.js

@@ -0,0 +1,159 @@
+/**
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ * 
+ * github地址: https://github.com/icindy/wxParse
+ * 
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+
+/**
+ * utils函数引入
+ **/
+import showdown from './showdown.js';
+import HtmlToJson from './html2json.js';
+/**
+ * 配置及公有属性
+ **/
+var realWindowWidth = 0;
+var realWindowHeight = 0;
+wx.getSystemInfo({
+  success: function (res) {
+    realWindowWidth = res.windowWidth
+    realWindowHeight = res.windowHeight
+  }
+})
+/**
+ * 主函数入口区
+ **/
+function wxParse(bindName = 'wxParseData', type='html', data='<div class="color:red;">数据不能为空</div>', target,imagePadding) {
+  var that = target;
+  var transData = {};//存放转化后的数据
+  if (type == 'html') {
+    transData = HtmlToJson.html2json(data, bindName);
+    // console.log(JSON.stringify(transData, ' ', ' '));
+  } else if (type == 'md' || type == 'markdown') {
+    var converter = new showdown.Converter();
+    var html = converter.makeHtml(data);
+    transData = HtmlToJson.html2json(html, bindName);
+    // console.log(JSON.stringify(transData, ' ', ' '));
+  }
+  transData.view = {};
+  transData.view.imagePadding = 0;
+  if(typeof(imagePadding) != 'undefined'){
+    transData.view.imagePadding = imagePadding
+  }
+  var bindData = {};
+  bindData[bindName] = transData;
+  that.setData(bindData)
+  that.wxParseImgLoad = wxParseImgLoad;
+  that.wxParseImgTap = wxParseImgTap;
+}
+// 图片点击事件
+function wxParseImgTap(e) {
+  var that = this;
+  var nowImgUrl = e.target.dataset.src;
+  var tagFrom = e.target.dataset.from;
+  if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) {
+    wx.previewImage({
+      current: nowImgUrl, // 当前显示图片的http链接
+      urls: that.data[tagFrom].imageUrls // 需要预览的图片http链接列表
+    })
+  }
+}
+
+/**
+ * 图片视觉宽高计算函数区 
+ **/
+function wxParseImgLoad(e) {
+  var that = this;
+  var tagFrom = e.target.dataset.from;
+  var idx = e.target.dataset.idx;
+  if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) {
+    calMoreImageInfo(e, idx, that, tagFrom)
+  } 
+}
+// 假循环获取计算图片视觉最佳宽高
+function calMoreImageInfo(e, idx, that, bindName) {
+  var temData = that.data[bindName];
+  if (!temData || temData.images.length == 0) {
+    return;
+  }
+  var temImages = temData.images;
+  //因为无法获取view宽度 需要自定义padding进行计算,稍后处理
+  var recal = wxAutoImageCal(e.detail.width, e.detail.height,that,bindName); 
+  // temImages[idx].width = recal.imageWidth;
+  // temImages[idx].height = recal.imageheight; 
+  // temData.images = temImages;
+  // var bindData = {};
+  // bindData[bindName] = temData;
+  // that.setData(bindData);
+  var index = temImages[idx].index
+  var key = `${bindName}`
+  for (var i of index.split('.')) key+=`.nodes[${i}]`
+  var keyW = key + '.width'
+  var keyH = key + '.height'
+  that.setData({
+    [keyW]: recal.imageWidth,
+    [keyH]: recal.imageheight,
+  })
+}
+
+// 计算视觉优先的图片宽高
+function wxAutoImageCal(originalWidth, originalHeight,that,bindName) {
+  //获取图片的原始长宽
+  var windowWidth = 0, windowHeight = 0;
+  var autoWidth = 0, autoHeight = 0;
+  var results = {};
+  var padding = that.data[bindName].view.imagePadding;
+  windowWidth = realWindowWidth-2*padding;
+  windowHeight = realWindowHeight;
+  //判断按照那种方式进行缩放
+  // console.log("windowWidth" + windowWidth);
+  if (originalWidth > windowWidth) {//在图片width大于手机屏幕width时候
+    autoWidth = windowWidth;
+    // console.log("autoWidth" + autoWidth);
+    autoHeight = (autoWidth * originalHeight) / originalWidth;
+    // console.log("autoHeight" + autoHeight);
+    results.imageWidth = autoWidth;
+    results.imageheight = autoHeight;
+  } else {//否则展示原来的数据
+    results.imageWidth = originalWidth;
+    results.imageheight = originalHeight;
+  }
+  return results;
+}
+
+function wxParseTemArray(temArrayName,bindNameReg,total,that){
+  var array = [];
+  var temData = that.data;
+  var obj = null;
+  for(var i = 0; i < total; i++){
+    var simArr = temData[bindNameReg+i].nodes;
+    array.push(simArr);
+  }
+
+  temArrayName = temArrayName || 'wxParseTemArray';
+  obj = JSON.parse('{"'+ temArrayName +'":""}');
+  obj[temArrayName] = array;
+  that.setData(obj);
+}
+
+/**
+ * 配置emojis
+ * 
+ */
+
+function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){
+   HtmlToJson.emojisInit(reg,baseSrc,emojis);
+}
+
+module.exports = {
+  wxParse: wxParse,
+  wxParseTemArray:wxParseTemArray,
+  emojisInit:emojisInit
+}
+
+

+ 967 - 0
lib/wxParse/wxParse.wxml

@@ -0,0 +1,967 @@
+<!--**
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ * 
+ * github地址: https://github.com/icindy/wxParse
+ * 
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */-->
+
+<!--基础元素-->
+<template name="wxParseVideo">
+  <!--增加video标签支持,并循环添加-->
+  <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+    <video class="{{item.classStr}} wxParse-{{item.tag}}-video" src="{{item.attr.src}}"></video>
+  </view>
+</template>
+
+<template name="wxParseImg">
+  <image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" mode="aspectFit" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:{{item.width}}px;"
+  />
+</template>
+
+<template name="WxEmojiView">
+  <view class="WxEmojiView wxParse-inline" style="{{item.styleStr}}">
+    <block wx:for="{{item.textArray}}" wx:key="">
+      <block class="{{item.text == '\\n' ? 'wxParse-hide':''}}" wx:if="{{item.node == 'text'}}">{{item.text}}</block>
+      <block wx:elif="{{item.node == 'element'}}">
+        <image class="wxEmoji" src="{{item.baseSrc}}{{item.text}}" />
+      </block>
+    </block>
+  </view>
+</template>
+
+<template name="WxParseBr">
+  <text>\n</text>
+</template>
+<!--入口模版-->
+
+<template name="wxParse">
+  <block wx:for="{{wxParseData}}" wx:key="">
+    <template is="wxParse0" data="{{item}}" />
+  </block>
+</template>
+
+
+<!--循环模版-->
+<template name="wxParse0">
+  <!--<template is="wxParse1" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse1" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse1" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse1" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+    <block wx:elif="{{item.tag == 'table'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse1" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse1" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse1" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+
+
+<!--循环模版-->
+<template name="wxParse1">
+  <!--<template is="wxParse2" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse2" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse2" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse2" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse2" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse2" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+
+<!--循环模版-->
+<template name="wxParse2">
+  <!--<template is="wxParse3" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse3" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse3" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse3" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse3" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse3" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse3">
+  <!--<template is="wxParse4" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse4" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse4" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse4" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse4" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse4" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse4">
+  <!--<template is="wxParse5" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse5" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse5" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse5" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse5" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse5" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse5">
+  <!--<template is="wxParse6" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse6" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse6" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse6" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse6" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse6" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse6">
+  <!--<template is="wxParse7" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse7" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse7" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse7" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse7" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse7" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+<!--循环模版-->
+<template name="wxParse7">
+  <!--<template is="wxParse8" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse8" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse8" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse8" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse8" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse8" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse8">
+  <!--<template is="wxParse9" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse9" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse9" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse9" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse9" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse9" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse9">
+  <!--<template is="wxParse10" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse10" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse10" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse10" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse10" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse10" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse10">
+  <!--<template is="wxParse11" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse11" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse11" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse11" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse11" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse11" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>
+
+<!--循环模版-->
+<template name="wxParse11">
+  <!--<template is="wxParse12" data="{{item}}" />-->
+  <!--判断是否是标签节点-->
+  <block wx:if="{{item.node == 'element'}}">
+    <block wx:if="{{item.tag == 'button'}}">
+      <button type="default" size="mini">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse12" data="{{item}}" />
+        </block>
+      </button>
+    </block>
+    <!--li类型-->
+    <block wx:elif="{{item.tag == 'li'}}">
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
+        <view class="{{item.classStr}} wxParse-li-inner">
+          <view class="{{item.classStr}} wxParse-li-text">
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
+          </view>
+          <view class="{{item.classStr}} wxParse-li-text">
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+              <template is="wxParse12" data="{{item}}" />
+            </block>
+          </view>
+        </view>
+      </view>
+    </block>
+
+    <!--video类型-->
+    <block wx:elif="{{item.tag == 'video'}}">
+      <template is="wxParseVideo" data="{{item}}" />
+    </block>
+
+    <!--img类型-->
+    <block wx:elif="{{item.tag == 'img'}}">
+      <template is="wxParseImg" data="{{item}}" />
+    </block>
+
+    <!--a类型-->
+    <block wx:elif="{{item.tag == 'a'}}">
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse12" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <block wx:elif="{{item.tag == 'br'}}">
+      <template is="WxParseBr"></template>
+    </block>
+    <!--其他块级标签-->
+    <block wx:elif="{{item.tagType == 'block'}}">
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+          <template is="wxParse12" data="{{item}}" />
+        </block>
+      </view>
+    </block>
+
+    <!--内联标签-->
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
+        <template is="wxParse12" data="{{item}}" />
+      </block>
+    </view>
+
+  </block>
+
+  <!--判断是否是文本节点-->
+  <block wx:elif="{{item.node == 'text'}}">
+    <!--如果是,直接进行-->
+    <template is="WxEmojiView" data="{{item}}" />
+  </block>
+
+</template>

+ 210 - 0
lib/wxParse/wxParse.wxss

@@ -0,0 +1,210 @@
+
+/**
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ * 
+ * github地址: https://github.com/icindy/wxParse
+ * 
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+
+.wxParse{
+    margin: 0 5px;
+    font-family: Helvetica,sans-serif;
+    font-size: 28rpx;
+    color: #666;
+    line-height: 1.8;
+}
+view{
+     /* word-break:break-all; */
+      /* overflow:auto;  */
+}
+.wxParse-inline{
+    display: inline;
+    margin: 0;
+    padding: 0;
+}
+/*//标题 */
+.wxParse-div{margin: 0;padding: 0;}
+.wxParse-h1{ font-size:2em; margin: .67em 0 }
+.wxParse-h2{ font-size:1.5em; margin: .75em 0 }
+.wxParse-h3{ font-size:1.17em; margin: .83em 0 }
+.wxParse-h4{ margin: 1.12em 0}
+.wxParse-h5 { font-size:.83em; margin: 1.5em 0 }
+.wxParse-h6{ font-size:.75em; margin: 1.67em 0 }
+
+.wxParse-h1 {
+  font-size: 18px;
+  font-weight: 400;
+  margin-bottom: .9em;
+}
+.wxParse-h2 {
+  font-size: 16px;
+  font-weight: 400;
+  margin-bottom: .34em;
+}
+.wxParse-h3 {
+  font-weight: 400;
+  font-size: 15px;
+  margin-bottom: .34em;
+}
+.wxParse-h4 {
+  font-weight: 400;
+  font-size: 14px;
+  margin-bottom: .24em;
+}
+.wxParse-h5 {
+  font-weight: 400;
+  font-size: 13px;
+  margin-bottom: .14em;
+}
+.wxParse-h6 {
+  font-weight: 400;
+  font-size: 12px;
+  margin-bottom: .04em;
+}
+
+.wxParse-h1, .wxParse-h2, .wxParse-h3, .wxParse-h4, .wxParse-h5, .wxParse-h6, .wxParse-b, .wxParse-strong  { font-weight: bold }
+
+.wxParse-i,.wxParse-cite,.wxParse-em,.wxParse-var,.wxParse-address{font-style:italic}
+.wxParse-pre,.wxParse-tt,.wxParse-code,.wxParse-kbd,.wxParse-samp{font-family:monospace}
+.wxParse-pre{white-space:pre}
+.wxParse-big{font-size:1.17em}
+.wxParse-small,.wxParse-sub,.wxParse-sup{font-size:.83em}
+.wxParse-sub{vertical-align:sub}
+.wxParse-sup{vertical-align:super}
+.wxParse-s,.wxParse-strike,.wxParse-del{text-decoration:line-through}
+/*wxparse-自定义个性化的css样式*/
+/*增加video的css样式*/
+.wxParse-strong,.wxParse-s{display: inline}
+.wxParse-a{
+    color: deepskyblue;
+    word-break:break-all;
+    overflow:auto;
+}
+
+.wxParse-video{
+    text-align: center;
+    margin: 10px 0;
+}
+
+.wxParse-video-video{
+    width:100%;
+}
+
+.wxParse-img{
+    /*background-color: #efefef;*/
+    overflow: hidden;
+}
+
+.wxParse-blockquote {
+    margin: 0;
+    padding:10px 0 10px 5px;
+    font-family:Courier, Calibri,"宋体";
+    background:#f5f5f5;
+    border-left: 3px solid #dbdbdb;
+}
+
+.wxParse-code,.wxParse-wxxxcode-style{
+    display: inline;
+    background:#f5f5f5;
+}
+.wxParse-ul{
+    margin: 20rpx 10rpx;
+}
+
+.wxParse-li,.wxParse-li-inner{
+    display: flex;
+    align-items: baseline;
+    margin: 10rpx 0;
+}
+.wxParse-li-text{
+    
+    align-items: center;
+    line-height: 20px;
+}
+
+.wxParse-li-circle{
+    display: inline-flex;
+    width: 5px;
+    height: 5px;
+    background-color: #333;
+    margin-right: 5px;
+}
+
+.wxParse-li-square{
+    display: inline-flex;
+    width: 10rpx;
+    height: 10rpx;
+    background-color: #333;
+    margin-right: 5px;
+}
+.wxParse-li-ring{
+    display: inline-flex;
+    width: 10rpx;
+    height: 10rpx;
+    border: 2rpx solid #333;
+    border-radius: 50%;
+    background-color: #fff;
+    margin-right: 5px;
+}
+
+/*.wxParse-table{
+    width: 100%;
+    height: 400px;
+}
+.wxParse-thead,.wxParse-tfoot,.wxParse-tr{
+    display: flex;
+    flex-direction: row;
+}
+.wxParse-th,.wxParse-td{
+    display: flex;
+    width: 580px;
+    overflow: auto;
+}*/
+
+.wxParse-u {
+  text-decoration: underline;
+}
+.wxParse-hide{
+    display: none;
+}
+.WxEmojiView{
+    align-items: center;
+}
+.wxEmoji{
+    width: 16px;
+    height:16px;
+}
+.wxParse-tr{
+	display: flex;
+	border-right:1px solid #e0e0e0;
+	border-bottom:1px solid #e0e0e0;
+	border-top:1px solid #e0e0e0;
+}
+.wxParse-th,
+.wxParse-td{
+	flex:1;
+	padding:5px;
+	font-size:28rpx;
+	border-left:1px solid #e0e0e0;
+	word-break: break-all;
+}
+.wxParse-td:last{
+	border-top:1px solid #e0e0e0;
+}
+.wxParse-th{
+	background:#f0f0f0;
+	border-top:1px solid #e0e0e0;
+}
+.wxParse-del{
+    display: inline;
+}
+.wxParse-figure {
+  overflow: hidden;
+}
+.wxParse-p image{
+  width: 100%!important;
+} 

+ 3 - 0
package-lock.json

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

+ 42 - 0
pages/index/data.js

@@ -0,0 +1,42 @@
+export default [
+  {
+    name: '服饰',
+    id: 0,
+    type: 'fushi',
+    icon: '/static/images/img_classify01@2x.png'
+  },
+  {
+    name: '美食',
+    id: 2,
+    type: 'shuma',
+    icon: '/static/images/img_classify02@2x.png'
+  },
+  {
+    name: '家具',
+    id: 1,
+    type: 'jiaju',
+    icon: '/static/images/img_classify03@2x.png'
+  },
+  // {
+  //   name: '数码',
+  //   id: 2,
+  //   type: 'shuma'
+  // },
+  {
+    name: '娱乐',
+    id: 3,
+    type: 'yule',
+    icon: '/static/images/img_classify04@2x.png'
+  },
+  // {
+  //   name: '其他',
+  //   id: 4,
+  //   type: 'qita'
+  // },
+  {
+    name:'全部',
+    type:'all',
+    id: -1,
+    icon: '/static/images/img_classify05@2x.png'
+  }
+]

+ 346 - 0
pages/index/index.js

@@ -0,0 +1,346 @@
+const util = require('../../utils/util.js');
+const api = require('../../config/api.js');
+const getUserInfo = require('../../utils/getUserInfo').default;
+const bgImg = require('../../static/images/bgImg')
+import {
+  Router
+} from '../../utils/router.js'
+
+//获取应用实例
+Router({
+  data: {
+    currentBanner: 0,
+    banners: [],
+    showSearch: false,
+    activeType: -1,
+    header: [],
+    noRecord: '',
+    hideMask: false,
+    keyword: '',
+    url: 'https://www.4dkankan.com/eShopMobile.html?m=',
+    totalPages: 1,
+    currentPage: 1,
+    size: 10,
+    bgImg,
+    loading: false,
+    showClear: false,
+    brandList: []
+  },
+  clearKeyword() {
+    this.setData({
+      keyword: ''
+    })
+    
+    this.setData({showClear: false})
+    this.search()
+  },
+  swiperChange(e) {
+    this.setData({
+      currentBanner: e.detail.current
+    })
+  },
+  onShareAppMessage: function () {
+    return {
+      path: '/pages/index/index'
+    }
+  },
+  getImgHeight() {
+    const aw = 750
+    const ah = 480
+    this.setData({
+      imgHeight: ah * wx.getSystemInfoSync().windowWidth / aw
+    })
+
+    this.navTop = this.data.imgHeight * 0.87
+    this.bgTop = this.data.imgHeight * 0.6
+    this.setData({
+      navTop: this.bgTop
+    })
+  },
+  onPageScroll(e) {
+    if (e.scrollTop < 0) return;
+    this.setData({
+      bgTop: e.scrollTop > this.bgTop ? this.bgTop : e.scrollTop,
+      bgIsTop: e.scrollTop > this.navTop,
+      isFixed: e.scrollTop > this.data.navPosTop
+    })
+  },
+  onPullDownRefresh() {
+    this.setData({
+      brandList: this.data.brandList.slice(0, 10),
+      currentPage: 1
+    });
+    this.getBrandList(1, false, true);
+    getApp().onPullDownRefresh()
+  },
+
+  onReachBottom() {
+    if (!this.data.loading) {
+      this.loadMore();
+      console.log('reach down')
+    }
+  },
+
+  clickSwiper() {
+    wx.navigateTo({
+      url: this.data.banners[this.data.currentBanner].redirectUrl,
+    })
+  },
+  loadMore: function () {
+    if (this.data.currentPage < this.data.totalPages) {
+      console.log(this.data.currentPage + 1)
+      this.getBrandList(this.data.currentPage + 1);
+    } else {
+      return;
+    }
+  },
+
+  async getBrandList(page, isSearch = false, refresh = false) {
+    this.setData({
+      loading: true
+    })
+    let tempContent = this.data.brandList ?
+      this.data.brandList : [];
+    let {
+      size,
+      keyword,
+    } = this.data
+
+    console.log('gegeget')
+    util.request(api.getFireList, {
+        pageNum: page,
+        projectSn: keyword,
+        pageSize: size
+      }, 'POST')
+      .then(res => {
+        if (isSearch) {
+          wx.pageScrollTo({
+            scrollTop: 0
+          })
+          this.setData({
+            brandList: this.formBrandList(res.data.list),
+            currentPage: res.data.curPage,
+            totalPages: res.data.totalPageNum,
+            loading: false,
+            noRecord: false,
+            showSearch: false
+          });
+        } else if (refresh) {
+          this.setData({
+            brandList: this.formBrandList(res.data.list),
+            currentPage: res.data.curPage,
+            totalPages: res.data.totalPageNum,
+            loading: false
+          });
+        } else {
+          this.setData({
+            brandList: tempContent.concat(this.formBrandList(res.data.list)),
+            currentPage: res.data.curPage,
+            totalPages: res.data.totalPageNum,
+            loading: false
+          });
+        }
+
+
+
+      });
+  },
+
+  formBrandList(data) {
+    return data.map(item => {
+      let icon =  ~item.projectSite.indexOf('非建构筑物') ? '/static/images/fire_other.png' :
+        ~item.projectSite.indexOf('交通工具') ? '/static/images/fire_bus.png' :
+        ~item.projectSite.indexOf('垃圾及废弃物') ? '/static/images/fire_recycle.png' : '/static/images/fire_building.png'
+      return {
+        ...item, 
+        icon,
+        createTime: item.createTime.substr(0, 11)
+      }
+    })
+  },
+  inputChange: function (e) {
+    let val = e.detail.value
+    this.setData({
+      keyword: val,
+      noRecord: false
+    })
+
+    this.setData({showClear: !!val.length})
+    this.search()
+  },
+
+  handleScroll(ev) {
+    this.setData({
+      isFixed: ev.detail.isFixed
+    })
+  },
+  search: function () {
+    let {
+      keyword
+    } = this.data
+
+    this.getBrandList(1, true)
+    // if (this.brandList.length <= 0) {
+    //   this.setData({
+    //     noRecord: true
+    //   })
+    // }
+    // else {
+    //   this.setData({
+    //     activeType: -1,
+    //     noRecord: false,
+    //     showSearch: false
+    //   })
+    // }
+  },
+
+  inputFocus: function () {
+
+  },
+  gotoCateg() {
+    wx.navigateTo({
+      url: '/pages/ncategory/category?id=search',
+    })
+  },
+
+  tapHeaderBar(e) {
+    // let { id } = e.currentTarget.dataset
+    // wx.navigateTo({
+    //   url: '/pages/ncategory/category?id=' + id,
+    // })
+    let {
+      id
+    } = e.currentTarget.dataset
+    this.setData({
+      activeType: id,
+      showSearch: false,
+      keyword: '',
+      brandList: [],
+      currentPage: 1
+    })
+
+    this.getBrandList(1)
+  },
+
+  tabShow() {
+    this.setData({
+      showSearch: true
+    })
+  },
+
+
+  tabHide() {
+    this.setData({
+      showSearch: false,
+      noRecord: false,
+      keyword: ''
+    })
+  },
+
+
+  getUserInfo,
+
+  onLoad: async function (options) {
+    this.setData({
+      statusBarHeight: wx.getSystemInfoSync().statusBarHeight,
+      isiOS: wx.getSystemInfoSync().system.indexOf('iOS') > -1,
+    })
+    
+    let navHeight = 0
+
+    if (!this.data.isiOS) {
+      navHeight = 48;
+    } else {
+      navHeight = 44;
+    }
+
+    this.setData({
+      navHeight: navHeight
+    })
+
+
+    getApp().checkNetStatu();
+    // this.checkNetStatu()
+    this.setData({
+      imgServer: util.imgServer
+    })
+    this.onShow()
+
+    // wx.setBackgroundColor({
+    //   backgroundColor: '#ff7701'
+    // })
+
+
+    
+  },
+
+
+  async getType() {
+    let items = [{
+      name: '全部',
+      id: -1
+    }]
+    this.setData({
+      header: items
+    })
+  },
+
+  onReady: function () {
+    // 页面渲染完成
+  },
+
+  onShow: function (options) {
+    this.data.brandList = []
+    if (global.type) {
+      this.tapHeaderBar({
+        currentTarget: {
+          dataset: this.data.header[0]
+        }
+      })
+      global.type = null
+    }
+    if (typeof this.getTabBar === 'function' && this.getTabBar()) {
+      this.getTabBar().setData({
+        selected: 0
+      })
+    }
+
+    getApp().updateCardCount()
+
+    // this.options.keyword && this.inputChange({detail: {value: this.options.keyword}})
+    this.getBrandList(1)
+    this.getType()
+    this.getImgHeight()
+    this.getUserInfo()
+    // 页面显示
+  },
+  onHide: function () {
+    // 页面隐藏
+  },
+  onUnload: function () {
+    // 页面关闭
+  },
+  gotoWV: function (event) {
+    let {
+      id,
+      scene
+    } = event.currentTarget.dataset
+
+    if (!scene) {
+      return wx.showToast({
+        icon: 'none',
+        title: '暂无VR数据,无法查看',
+      })
+    }
+    wx.navigateTo({
+      url: `/pages/webview/index?id=${id}&scene=${scene}`,
+      // url: `/pages/reserve/reserve?id=${id}`
+    })
+  },
+
+  searchFocus() {
+    wx.navigateTo({
+      url: '/pages/search/search?keyword=' + this.data.keyword,
+    })
+  }
+})

+ 5 - 0
pages/index/index.json

@@ -0,0 +1,5 @@
+{
+  "enablePullDownRefresh": false,
+  "navigationBarTitleText": "火调VR",
+  "navigationStyle":"custom"
+}

+ 75 - 0
pages/index/index.wxml

@@ -0,0 +1,75 @@
+
+<view class="zxczxc" style="top: {{statusBarHeight + navHeight + 80}}px; height: calc(100% - {{statusBarHeight + navHeight + 80}}px);">
+
+	<view class="page-title" style="padding-top: {{statusBarHeight}}px; height: {{navHeight}}px; background-image: url({{bgImg}});">
+		<navigator url="/pages/ucenter/index/index">
+			<image src="{{userInfo.avatar || '/static/images/avatar_default.png'}}" ></image>
+			<image src="https://platform-wxmall.oss-cn-beijing.aliyuncs.com/upload/20180727/150547696d798c.png" wx:if="{{!userInfo && !userInfo.avatar}}" class="user-bg"></image>
+			<text bindtap="">Hi~ {{userInfo ? userInfo.nickName : '游客'}}</text>
+		</navigator>
+	</view>
+
+	<view class="search-layer" style="top: {{statusBarHeight + navHeight}}px; background-image: url({{bgImg}});background-position: 0 -{{statusBarHeight + navHeight}}px">
+		<view class="search" bindtap="searchFocus">
+			<input placeholder="请输入起火对象/单位名称" disabled placeholder-style="color:#C8C9CC;" value="{{keyword}}" bindinput="inputChange" bindconfirm="search" confirm-type="search"
+				type="text"></input>
+			<image wx:if="{{imgServer}}" src="{{imgServer + '/search.png'}}" class="icon"></image>
+			<view bindtap="clearKeyword" class="clear" wx:if="{{showClear}}"><image src="/static/images/list_close.png" /></view>
+		</view>
+	</view>
+
+	<image src="{{bgImg}}" class="bgimg"></image>
+
+	<scroll-view class="container" style="min-height: {{brandList.length < 3 ? '300px': 'auto'}}" >
+		<view class="body" id="tab-con">
+			<!-- <lip-sync inner-text="Some text"></lip-sync> -->
+			<view class="ul" wx:if="{{loading || brandList.length}}">
+				<view bindtap="gotoWV" data-scene="{{item.sceneNum}}" data-id="{{item.id}}" class="li" wx:for='{{brandList}}'
+					wx:key="id">
+					<view class="ctp">
+						<image mode="aspectFill" src="{{item.sceneCover|| '/static/images/fire_pic@2x.png'}}"></image>
+						<image class="icon" src="/static/images/reserv_vr@2x.png" wx:if="{{item.sceneCover}}"></image>
+					</view>
+					
+					<view class="item-content">
+						<view class="b-title">
+							<text>{{item.projectName}}</text>
+							<view><image src="{{item.icon}}"></image>
+							<text>{{item.projectSite}}</text></view>
+						</view>
+						<view class="b-info">
+							<view>
+								<text>{{item.organizerDeptName}}</text>								
+							</view>
+							<view>
+								<text>{{item.createTime}}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+
+				<view class="loadText" wx:if="{{!loading && currentPage < totalPages}}" bindtap="onReachBottom">加载更多</view>
+				<view class="loadText" wx:if="{{!loading && currentPage >= totalPages}}">- 已加载所有数据 -</view>
+				<view class="loadText loading" wx:if="{{loading}}" >
+					<image src="/static/images/fire_loading@2x.png"></image>
+				</view>
+			</view>
+			<view wx:else class="unbrand">
+				<image src="/static/images/fire_pic_data@2x.png"></image>
+				<view class="text">暂无数据或无查看权限~</view>
+			</view>
+		</view>
+
+		<view class="linear-mask {{hideMask? '': 'mask-hidden'}}"></view>
+		<view bindtap="tabHide" class="search-con" wx:if="{{showSearch}}" >
+			<view class="sc-con" catchtap>
+				<input focus="true" value="{{keyword}}" bindinput="inputChange" bindconfirm="search" confirm-type="search"
+					type="text"></input>
+				<image wx:if="{{imgServer}}" src="{{imgServer + '/fangdajing.png'}}"></image>
+			</view>
+			<view class="no-record" wx:if="{{noRecord}}">未搜索到相关内容</view>
+		</view>
+	</scroll-view>
+
+</view>
+

+ 608 - 0
pages/index/index.wxss

@@ -0,0 +1,608 @@
+page {
+  background: #F5F5F5;
+  background: #F5F5F5;
+}
+
+.container {
+  padding: 40rpx 30rpx;
+  background: #F5F5F5;
+  margin-top: -10px;
+  border-radius: 10px 10px 0px 0px;
+}
+
+.container::-webkit-scrollbar {
+  display: none;
+}
+
+.linear-mask {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  width: 100%;
+  height: 50%;
+  background: linear-gradient(to top, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
+  pointer-events: none;
+  opacity: 1;
+  transition: ease all 0.3s;
+  z-index: 2;
+}
+
+.mask-hidden {
+  opacity: 0;
+}
+
+.mall-logo {
+  position: fixed;
+  bottom: 15px;
+  right: 25px;
+  width: 80px;
+  height: auto;
+  pointer-events: none;
+  z-index: 999;
+}
+
+.search-con {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(255, 255, 255, 0.8);
+  z-index: 9999;
+}
+
+.no-record {
+  width: calc(100% - 80rpx);
+  margin: 40rpx 0 0 60rpx;
+}
+
+.sc-con {
+  width: calc(100% - 80rpx);
+  line-height: 60rpx;
+  height: 60rpx;
+  border: 1px solid #808080;
+  border-radius: 30rpx;
+  margin: 20rpx 0 0 40rpx;
+  background: #fff;
+  position: relative;
+}
+
+.sc-con input {
+  width: 90%;
+  background: none;
+  border: none;
+  padding-left: 30rpx;
+  font-size: 24rpx;
+  color: #000;
+  line-height: 40rpx;
+  height: 56rpx;
+}
+
+.sc-con image {
+  width: 40rpx;
+  height: 40rpx;
+  display: inline-block;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  right: 20rpx;
+}
+
+
+.header {
+  background-color: #fff;
+  /* padding-left: 46rpx;
+  padding-right: 46rpx; */
+  margin-top: 0;
+  padding: 12rpx 0 22rpx;
+  left: 30rpx;
+}
+
+.fixed {
+  position: fixed;
+  padding-left: 30rpx;
+  padding-right: 30rpx;
+  left: 0;
+  width: 100vw;
+  z-index: 999;
+}
+
+.header .ul {
+  display: flex;
+  align-items: baseline;
+  overflow-x: auto;
+  overflow-y: hidden
+}
+
+.header .ul .li {
+  flex: 0 0 auto;
+  text-align: center;
+  color: #131D34;
+  font-size: 30rpx;
+  font-weight: bold;
+  padding: 10rpx;
+}
+
+.header .ul .li:not(:last-child) {
+  margin-right: 42rpx;
+}
+
+.header .ul .li image {
+  width: 80rpx;
+  height: 80rpx;
+  margin-bottom: 14rpx;
+  display: block;
+}
+
+.header .ul .active {
+  font-size: 36rpx;
+  position: relative;
+}
+
+.header .ul .active text {
+  z-index: 2;
+  position: relative;
+}
+
+.header .ul .active::after {
+  content: '';
+  position: absolute;
+  bottom: 10rpx;
+  left: 0;
+  right: 0;
+  height: 20rpx;
+  background: linear-gradient(90deg, #ED5D18 0%, rgba(237, 93, 24, 0) 100%);
+  border-radius: 10px;
+  z-index: 1;
+}
+
+
+.search {
+  /* margin: 0 46rpx; */
+  position: relative;
+  /* box-shadow: 0 2px 2px #fff; */
+  z-index: 99;
+  height: 80rpx;
+  border: 1px solid rgba(0, 0, 0, 0.1);
+  border-radius: 4px;
+  background: #fff;
+}
+
+.search view:not(.clear) {
+  background: none;
+  width: 80%;
+  border: none;
+  padding-left: 20rpx;
+  font-size: 24rpx;
+  color: #fff;
+  line-height: 24rpx;
+  vertical-align: middle;
+}
+
+.search input {
+  box-sizing: border-box;
+  color: rgba(0, 0, 0, 0.3);
+  height: 100%;
+  width: 100%;
+  padding: 0 70rpx 0 70rpx;
+  position: relative;
+  z-index: 999;
+  border-radius: 4px;
+  font-size: 14px;
+  border: none;
+}
+
+.search .icon {
+  width: 28rpx;
+  height: 28rpx;
+  display: inline-block;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  left: 28rpx;
+}
+
+.clear {
+  width: 30px;
+  height: 30px;
+  display: inline-block;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  right: 4px;
+  padding: 10px;
+  z-index: 9999999;
+}
+
+.clear image {
+  display: block;
+  width: 100%;
+  height: 100%;
+}
+
+.body {
+  z-index: 11;
+  position: relative;
+  padding: 0 0 22rpx;
+  overflow: hidden;
+  width: 100%;
+  font-size: 0;
+}
+
+.body::-webkit-scrollbar {
+  display: none
+}
+
+.body .ul {
+  position: relative;
+  width: 100%;
+  font-size: 0;
+  z-index: 1;
+  overflow: hidden;
+}
+
+.body .li {
+  padding: 20rpx 30rpx;
+  margin-bottom: 20rpx;
+  background: #FFFFFF;
+  box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
+  border-radius: 4px;
+  display: flex;
+}
+.ctp {
+  flex: none;
+  position: relative;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.ctp image:not(.icon){
+  width: 160rpx;
+  height: 160rpx;
+}
+.ctp .icon {
+  right: 6px;
+  bottom: 6px;
+  width: 20px;
+  height: 20px;
+  position: absolute;
+}
+
+.body .li .count {
+  position: absolute;
+  width: 90rpx;
+  height: 28rpx;
+  background: rgba(0, 0, 0, 0.3);
+  opacity: 1;
+  border-radius: 14rpx;
+  right: 22rpx;
+  top: 288rpx;
+  font-size: 18rpx;
+  font-weight: 400;
+  /* line-height: 28rpx; */
+  color: #fff;
+  text-align: center;
+  padding: 7rpx;
+  padding-left: 31rpx;
+  line-height: 20rpx;
+  overflow: hidden;
+  box-sizing: border-box;
+}
+
+.body .li .count image {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 24rpx;
+  height: 14rpx;
+  left: 7rpx;
+  top: 8rpx;
+}
+
+.item-content {
+  padding-left: 20rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-around;
+  flex: none;
+  width: calc(100% - 180rpx);
+}
+
+.body .li .b-title {
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+.body .li .b-title text:nth-child(1) {
+  font-size: 16px;
+  font-weight: 500;
+  color: #333333;
+  line-height: 22px;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+  display: block;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+.body .li .b-title view:nth-child(2) {
+  line-height: 20px;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  display: block;
+  white-space:nowrap;
+  display: flex;
+  align-items: center;
+  overflow:hidden;
+}
+.body .li .b-title view:nth-child(2) text {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  font-size: 14px;
+  font-weight: 400;
+  color: #646566;
+  white-space: nowrap;
+  display: block;
+
+}
+
+.body .li .b-title view image {
+  height: 14px;
+  width: 14px;
+  flex: none;
+  margin-right: 5px;
+}
+
+.body .li .b-info {
+  margin-top: 16rpx;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.body .li .b-info text {
+  font-size: 11px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #969799;
+  line-height: 16px;
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.loading image {
+  content: '';
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  width: 15px;
+  height: 15px;
+  box-sizing: border-box;
+  animation: example 1s linear infinite;
+}
+/* 动画代码 */
+@keyframes example {
+  from {transform: translate(-50%, -50%) rotateZ(0);}
+  to {transform: translate(-50%, -50%) rotateZ(360deg);}
+}
+
+.item-content {
+  flex: 1;
+}
+.loadText {
+  position: relative;
+  height: 80rpx;
+  font-size: 12px;
+  color: rgb(160,160,160);
+  text-align: center;
+  margin-top: 20px;
+}
+
+.body .li .b-info view {
+  /* width: 50%; */
+}
+.body .li .b-info view:first-child text{
+  text-align: left;
+}
+
+.body .li .b-info view:last-child text{
+  text-align: right;
+}
+
+.body .li .b-info text.xxxx {
+  color: #FF7701;
+}
+.body .li .b-info text:nth-child(2) {
+  margin-left: 12rpx;
+}
+
+
+.unbrand {
+  margin-top: 180rpx;
+  text-align: center;
+}
+
+.unbrand image {
+  display: inline-block;
+  width: 250rpx;
+  height: 274rpx;
+}
+
+.unbrand .text {
+  color: #666C7D;
+  font-size: 30rpx;
+  margin-top: 40rpx;
+}
+
+.banner {
+  margin: 16rpx 0 38rpx;
+  position: relative;
+}
+
+.banner swiper {
+  height: 296rpx;
+}
+
+.banner .swiper-item image,
+.banner .swiper-item {
+  width: 100%;
+  height: 100%;
+}
+
+.banner .swiper-item {
+  border-radius: 4px;
+  overflow: hidden
+}
+
+.dots {
+  position: absolute;
+  bottom: 8rpx;
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 2;
+}
+
+.dots view {
+  display: inline-block;
+  width: 4px;
+  height: 4px;
+  background: #FFFFFF;
+  border-radius: 2px;
+  opacity: 0.5;
+  margin: 0 4rpx
+}
+
+.dots view.dotactive {
+  width: 8px;
+  opacity: 1;
+}
+
+.hotcateg {
+  margin-bottom: 40rpx;
+}
+
+.hotcateg .title {
+  font-size: 30rpx;
+  font-weight: bold;
+  line-height: 44rpx;
+  color: #131D34;
+  margin-bottom: 14rpx;
+  padding: 0 46rpx;
+}
+
+.hotcateg .swiper-item {
+  margin: 0 10rpx;
+  position: relative;
+  border-radius: 4px;
+  overflow: hidden;
+  width: calc(100% - 20rpx);
+  height: 100%;
+}
+
+.hotcateg .swiper-item image {
+  width: 100%;
+  height: 100%;
+}
+
+.hotcateg .swiper-item view {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 8rpx 24rpx;
+  font-size: 22rpx;
+  color: #fff;
+  background-color: rgba(0, 0, 0, 0.4);
+  text-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
+}
+
+
+.bgimg {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100vw;
+  height: 690rpx;
+  z-index: -9;
+}
+
+.bgimg image {
+  width: 100%;
+  height: 100%;
+}
+
+.page-title {
+  position: fixed;
+  box-sizing: content-box;
+  padding-right: 32rpx;
+  padding-left: 30rpx;
+  font-size: 32rpx;
+  left: 0;
+  top: 0;
+  width: 100vw;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+  z-index: 99;
+  background-size: 100vw 690rpx;
+  background-position: 0 0;
+}
+
+.page-title  navigator {
+  display: block;
+  max-width: 65%;
+  display: flex;
+  align-items: center;
+}
+
+.page-title navigator text {
+  display: block;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+.search-layer {
+  position: fixed;
+  left: 0;
+  right: 0;
+  height: 69px;
+  z-index: 99;
+  padding: 10px 15px 30px;
+  background-size: 100vw 690rpx;
+}
+
+.page-title image {
+  flex: 0 0 auto;
+  background: rgb(180, 180, 180);
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  margin-right: 10rpx;
+  vertical-align: top;
+}
+
+.page-title text {
+  position: relative;
+  color: #fff;
+  z-index: 1;
+  display: inline-block;
+  font-size: 18px;
+  font-family: PingFangSC-Semibold, PingFang SC;
+  font-weight: 600;
+  color: #FFFFFF;
+  line-height: 25px;
+}
+
+.zxczxc {
+  position: absolute;
+  left: 0;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+}

+ 76 - 0
pages/login/login.js

@@ -0,0 +1,76 @@
+const REG = /^((13[0-9]|14[01456879]|15[0-3,5-9]|16[2567]|17[0-8]|18[0-9]|19[0-3,5-9])\d{8})|(8){11}$/
+const api = require('../../config/api')
+const util = require('../../utils/util.js');
+const uutil = require('../../utils/index')
+
+// pages/login/login.js
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    phone: '',
+    psw: '',
+    showPsw: false,
+    tg: false
+  },
+  updatePhone(e) {
+    this.setData({phone: e.detail.value})
+    this.check(false)
+  },
+  updatePsw(e) {
+    this.setData({psw: e.detail.value})
+    this.check(false)
+  },
+  check(tip) {
+    if (!this.data.phone) {
+      tip && wx.showToast({title: '请输入手机号', icon: 'none'})
+    } else if (!REG.test(this.data.phone)) {
+      tip && wx.showToast({title: '手机号格式不正确!', icon: 'none'})
+    } else if (!this.data.psw) {
+      tip && wx.showToast({title: '请输入密码', icon: 'none'})
+    } else {
+      this.setData({ tg: true })
+      return true
+    }
+  },
+
+  showPsw() {
+    this.setData({showPsw: true})
+  },
+  hidePsw() {
+    this.setData({showPsw: false})
+  },
+  async login() {
+    if (!this.check(true)) return;
+    
+
+    //登录远程服务器
+    util.request(api.userLogin, {
+      userName: this.data.phone,
+      password: uutil.encryption(this.data.psw)
+    }, 'POST').then(res => {
+      if (res.code === 0) {
+        //存储用户信息
+        wx.setStorageSync('userInfo', res.data.user);
+        wx.setStorageSync('token', res.data.token);
+        wx.setStorageSync('userId', res.data.user.userId);
+        wx.setStorageSync('department', res.data.department);
+        wx.setStorageSync('isLogin', true);
+        getApp().setLoginProps(true)
+        wx.redirectTo({
+          url: '/pages/index/index',
+        })
+      } else {
+        wx.showToast({title: res.msg, icon: 'none'})
+      }
+    });
+  },
+
+  onLoad() {
+    if (wx.getStorageSync('token')) {
+      
+    }
+  }
+})

+ 3 - 0
pages/login/login.json

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

+ 19 - 0
pages/login/login.wxml

@@ -0,0 +1,19 @@
+<!--pages/login/login.wxml-->
+<view class="layout">
+  <image src="/static/images/logo_big@2x.png"></image>
+  <text class="title">欢迎登录</text>
+  <text class="desc">广东省消防救援总队火调VR数据平台</text>
+  <view>
+    <input type="text" placeholder="请输入账号" bindinput="updatePhone" value="{{phone}}" class="name" placeholder-style="color:#C8C9CC;" />
+  </view>
+  <view class="{{showPsw ? 'hide' : ''}}">
+    <input type="password" value="{{psw}}" placeholder="请输入密码" bindinput="updatePsw" class="{{psw}}" placeholder-style="color:#C8C9CC;" />
+    <image src="/static/images/log_eye_normal@2x.png" bindtap="showPsw"></image>
+  </view>
+  <view class="{{!showPsw ? 'hide' : ''}}">
+    <input type="text" value="{{psw}}"  placeholder="请输入密码" bindinput="updatePsw" class="{{psw}}" placeholder-style="color:#C8C9CC;"  />
+    <image src="/static/images/log_eye_normal@2xx.png" bindtap="hidePsw"></image>
+  </view>
+
+  <button class="btn {{tg ? 'success': ''}}" bindtap="login">登录</button>
+</view>

+ 72 - 0
pages/login/login.wxss

@@ -0,0 +1,72 @@
+/* pages/login/login.wxss */
+page {
+  background: #F5F5F5;
+}
+
+.layout {
+  margin: 100rpx 80rpx 0;
+}
+
+image {
+  width: 140rpx;
+  height: 140rpx;
+}
+
+.title {
+  display: block;
+  font-size: 52rpx;
+  margin-top: 14px;
+  color: #323233;
+  margin-bottom: 2px;
+
+}
+
+.hide {
+  position: absolute !important;
+  z-index: -111111111;
+  left: -9999999px;
+  top: -9999999px;
+}
+
+.desc {
+  color: #646566;
+  line-height: 40rpx;
+  font-size: 28rpx;
+  padding-bottom: 30rpx;
+}
+
+view:not(.layout) {
+  border-bottom: 1px solid #E8E8E8;
+  position: relative;
+}
+view:not(.layout) image {
+  position: absolute;
+  top: 50%;
+  right: 16rpx;
+  transform: translateY(-50%);
+  width: 32rpx;
+  height: 32rpx;
+}
+input {
+  padding: 28rpx 16rpx;
+  font-size: 28rpx;
+  margin-top: 20rpx;
+  color: #323233;
+  width: 80%;
+}
+
+.btn {
+  margin-top: 100rpx;
+  width: 100%;
+  height: 80rpx;
+  line-height: 80rpx;
+  text-align: center;
+  font-size: 14px;
+  color: #fff;
+  background: rgba(38, 85, 155, 0.5);
+  border-radius: 4px;
+}
+
+.success {
+  background-color: #26559B;
+}

+ 62 - 0
pages/pay/pay.js

@@ -0,0 +1,62 @@
+var app = getApp();
+var util = require('../../utils/util.js');
+var api = require('../../config/api.js');
+
+Page({
+  data: {
+    orderId: 0,
+    actualPrice: 0.00
+  },
+  onLoad: function (options) {
+    // 页面初始化 options为页面跳转所带来的参数
+    this.setData({
+      orderId: options.orderId,
+      actualPrice: options.actualPrice
+    })
+  },
+  onReady: function () {
+
+  },
+  onShow: function () {
+    // 页面显示
+
+  },
+  onHide: function () {
+    // 页面隐藏
+
+  },
+  onUnload: function () {
+    // 页面关闭
+
+  },
+  //向服务请求支付参数
+  requestPayParam() {
+    let that = this;
+    util.request(api.PayPrepayId, { orderId: that.data.orderId}).then(function (res) {
+      if (res.code === 0) {
+        let payParam = res.data;
+        
+        wx.requestPayment({
+          'timeStamp': payParam.timeStamp,
+          'nonceStr': payParam.nonceStr,
+          'package': payParam.package,
+          'signType': payParam.signType,
+          'paySign': payParam.paySign,
+          'success': function (res) {
+            wx.redirectTo({
+              url: '/pages/payResult/payResult?status=1',
+            })
+          },
+          'fail': function (res) {
+            // wx.redirectTo({
+            //   url: '/pages/payResult/payResult?status=0',
+            // })
+          }
+        })
+      }
+    });
+  },
+  startPay() {
+    this.requestPayParam();
+  }
+})

+ 3 - 0
pages/pay/pay.json

@@ -0,0 +1,3 @@
+{
+  "navigationBarTitleText": "支付订单"
+}

+ 31 - 0
pages/pay/pay.wxml

@@ -0,0 +1,31 @@
+<view class="container">
+  <auth bindlogin="onShow" />
+  <lip-sync />
+    <view class="total">
+      <view class="label">订单金额</view>
+      <view class="txt">{{actualPrice}}元</view>
+    </view>
+    <view class="pay-list">
+        <view class="h">请选择支付方式</view>
+        <view class="b">
+            <!--<view class="item">
+                <view class="checkbox checked"></view>
+                <view class="icon-alipay"></view>
+                <view class="name">支付宝</view>
+            </view>
+            <view class="item">
+                <view class="checkbox"></view>
+                <view class="icon-net"></view>
+                <view class="name">网易支付</view>
+            </view>-->
+            <view class="item">
+                <view class="checkbox checked"></view>
+                <image src="/static/images/wxpay.png" class="icon"></image>
+                <view class="name">微信支付</view>
+            </view>
+        </view>
+    </view>
+    <view class="tips">小程序只支持微信支付,如需其它支付方式,请在网页版支付</view>
+
+    <view class="pay-btn" bindtap="startPay">确定</view>
+</view>

+ 121 - 0
pages/pay/pay.wxss

@@ -0,0 +1,121 @@
+page{
+    min-height: 100%;
+    width: 100%;
+    background: #f4f4f4;
+}
+
+.container{
+  padding-top: 20rpx;
+}
+
+.total{
+  height: 104rpx;
+  background: #fff;
+  width: 100%;
+  line-height: 104rpx;
+  padding-left: 30rpx;
+  padding-right: 30rpx;
+}
+
+.total .label{
+  float: left;
+}
+
+.total .txt{
+  float: right;
+}
+
+.pay-list{
+    margin-top: 30rpx;
+    height: auto;
+    width: 100%;
+    overflow: hidden;
+}
+.pay-list .h{
+    width: 100%;
+    margin-left: 30rpx;
+    margin-bottom: 30rpx;
+}
+
+.pay-list .item{
+    height: 108rpx;
+    padding-left: 30rpx;
+    background: #fff;
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid #f4f4f4;
+}
+
+.pay-list .checkbox{
+    background: url(https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/wxmall/images/img_sprites_checkbox.png) 0 -448rpx no-repeat;
+    background-size: 38rpx 486rpx;
+    width: 40rpx;
+    height: 40rpx;
+    display: inline-block;
+    vertical-align: middle;
+    margin-right: 30rpx;
+}
+
+.pay-list .checkbox.checked{
+    background: url(https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/wxmall/images/img_sprites_checkbox.png) 0 -192rpx no-repeat;
+    background-size: 38rpx 486rpx;
+}
+
+.pay-list .icon-alipay{
+    display: inline-block;
+    vertical-align: middle;
+    background-image: url(http://yanxuan.nosdn.127.net/hxm/yanxuan-wap/p/20161201/style/img/sprites/payMethod-s3c1faebee4-d754da9c65.png);
+    background-repeat: no-repeat;
+    background-size: 56.25rpx 189.583rpx;
+    margin-right: 10.5rpx;
+    width: 56.25rpx;
+    height: 56.25rpx;
+}
+
+.pay-list .icon-net{
+    display: inline-block;
+    vertical-align: middle;
+    background: url(http://yanxuan.nosdn.127.net/hxm/yanxuan-wap/p/20161201/style/img/sprites/payMethod-s3c1faebee4-d754da9c65.png) 0 -66.7rpx no-repeat;
+    background-size: 56.25rpx 189.583rpx;
+    margin-right: 10.5rpx;
+    width: 56.25rpx;
+    height: 56.25rpx;
+}
+
+.pay-list .icon{
+    display: inline-block;
+    vertical-align: middle;
+    margin-right: 10.5rpx;
+    width: 56.25rpx;
+    height: 56.25rpx;
+}
+
+.pay-list .name{
+    display: inline-block;
+    vertical-align: middle;
+    height: 56.25rpx;
+    line-height: 56.25rpx;
+}
+
+.pay-btn{
+    position: fixed;
+    left: 0;
+    bottom: 0;
+    height: 100rpx;
+    width: 100%;
+    text-align: center;
+    line-height: 100rpx;
+    background: #ED5D18;
+    color: #fff;
+    font-size: 30rpx;
+}
+
+.tips{
+  height: 40rpx;
+  width: 100%;
+  font-size: 24rpx;
+  color: #999;
+  line-height: 40rpx;
+  padding-left: 30rpx;
+  padding-right: 30rpx;
+}

+ 59 - 0
pages/payResult/payResult.js

@@ -0,0 +1,59 @@
+var util = require('../../utils/util.js');
+var api = require('../../config/api.js');
+const pay = require('../../services/pay.js');
+
+var app = getApp();
+Page({
+  data: {
+    status: false,
+    orderId: 0
+  },
+  onLoad: function (options) {
+    // 页面初始化 options为页面跳转所带来的参数
+    this.setData({
+      orderId: options.orderId || 24,
+      status: options.status
+    })
+    this.updateSuccess()
+  },
+  naback(){
+    wx.switchTab({
+      url: '/pages/index/index',
+    })
+    // wx.navigateBack({
+    //   delta:15
+    // })
+  },
+  onReady: function () {
+
+  },
+  onShow: function () {
+    // 页面显示
+
+  },
+  onHide: function () {
+    // 页面隐藏
+
+  },
+  onUnload: function () {
+    // 页面关闭
+
+  },
+  
+  updateSuccess: function () {
+    let that = this
+    util.request(api.OrderQuery, { orderId: this.data.orderId}).then(function (res) {
+    })
+  },
+
+  payOrder() {
+    pay.payOrder(parseInt(this.data.orderId)).then(res => {
+      this.setData({
+        status: true
+      });
+      this.updateSuccess()
+    }).catch(res => {
+      util.showErrorToast('支付失败');
+    });
+  }
+})

+ 4 - 0
pages/payResult/payResult.json

@@ -0,0 +1,4 @@
+{
+    "navigationBarTitleText": "付款结果",
+    "navigationBarBackgroundColor": "#fafafa"
+}

+ 29 - 0
pages/payResult/payResult.wxml

@@ -0,0 +1,29 @@
+<view class="container">
+  <auth bindlogin="onShow" />
+  <lip-sync />
+  <view class="pay-result">
+    <view class="success" wx:if="{{status == true}}">
+      <view class="msg">付款成功</view>
+      <view class="btns">
+      <!-- url="/pages/ucenter/order/order" -->
+        <navigator url="/pages/ucenter/order/order?id=-1" hover-class="none"  open-type="redirect" class="btn">
+          查看订单
+        </navigator>
+        <view class="btn" bindtap="naback">继续逛</view>
+        <!-- <navigator class="btn" url="/pages/webview/index" open-type="switchTab">继续逛</navigator> -->
+      </view>
+    </view>
+    <view class="error" wx:if="{{status != true}}">
+      <view class="msg">付款失败</view>
+      <view class="tips">
+        <view class="p">请在 <text class="time">1小时</text> 内完成付款</view>
+        <view class="p">否则订单将会被系统取消</view>
+      </view>
+      <view class="btns">
+        <navigator class="btn" url="/pages/ucenter/order/order?id=-1" open-type="redirect">查看订单</navigator>
+        <view class="btn" bindtap='payOrder'>重新付款</view>
+      </view>
+    </view>
+  </view>
+
+</view>

+ 59 - 0
pages/payResult/payResult.wxss

@@ -0,0 +1,59 @@
+page {
+  min-height: 100%;
+  width: 100%;
+  background: #fff;
+}
+
+.container {
+  height: 100%;
+  background: #fff;
+}
+
+.pay-result {
+  background: #fff;
+}
+
+.pay-result .msg {
+  text-align: center;
+  margin: 100rpx auto;
+  color: #2bab25;
+  font-size: 36rpx;
+}
+
+.pay-result .btns {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.pay-result .btn {
+  text-align: center;
+  height: 80rpx;
+  margin: 0 20rpx;
+  width: 200rpx;
+  line-height: 78rpx;
+  border: 1px solid #868686;
+  color: #000000;
+  border-radius: 5rpx;
+}
+
+.pay-result .error .msg {
+  color: #ED5D18;
+  margin-bottom: 60rpx;
+}
+
+.pay-result .error .tips {
+  color: #7f7f7f;
+  margin-bottom: 70rpx;
+}
+
+.pay-result .error .tips .p {
+  font-size: 24rpx;
+  line-height: 42rpx;
+  text-align: center;
+}
+
+.pay-result .error .tips .p {
+  line-height: 42rpx;
+  text-align: center;
+}

+ 212 - 0
pages/reserve/reserve.js

@@ -0,0 +1,212 @@
+
+const api = require('../../config/api.js');
+const util = require('../../utils/util.js');
+import play from '../../utils/pay.js'
+
+// 手机号校验
+const PHONE = {
+  REG: /^((13[0-9]|14[01456879]|15[0-3,5-9]|16[2567]|17[0-8]|18[0-9]|19[0-3,5-9])\d{8})|(8){11}$/,
+  tip: '手机号格式不正确!'
+}
+
+// pages/reserve/reserve.js
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    date: '',
+    people: 0,
+    showDate: '',
+    name: '',
+    phone: '',
+    code: '',
+    sex:1,
+    showShare: false,
+    sendMis: 0
+  },
+
+  focusHandle() {
+    console.log(this.data.name, this.data.name.length)
+    this.setData({
+      name: this.data.name.substr(0, 4)
+    })
+  },
+
+  setSex1() {
+    this.setData({sex: 1})
+  },
+
+  setSex0() {
+    this.setData({sex: 0})
+  },
+
+  async sendCode() {
+    if (this.data.sendMis !== 0) return;
+
+    if (!PHONE.REG.test(this.data.phone)) {
+      return wx.showToast({
+        icon: 'none',
+        title: '请输入正确的手机号码',
+      })
+    }
+
+    this.data.sendMis = 60
+
+    try {
+      await util.request(api.OrderSendCode, {phoneNum: this.data.phone}, 'GET')
+      
+      this.setData({
+        sendMis: 60
+      })
+
+      let interval = setInterval(() => {
+        if (this.data.sendMis === 0) return clearInterval(interval)
+        this.setData({ sendMis: this.data.sendMis - 1 })
+      }, 1000)
+    } catch (e) {
+      this.data.sendMis = 0
+    }
+  },
+
+  updateName(ev) {
+    this.setData({name: ev.detail.value})
+  },
+  
+  updatePhone(ev) {
+    this.setData({phone: ev.detail.value})
+  },
+  
+  updateCode(ev) {
+    this.setData({code: ev.detail.value})
+  },
+
+  enter() {
+
+    this.quit()
+  },
+
+  quit() {
+    wx.navigateBack()
+  },
+  async getBrandDetail(id) {
+    let res = await util.request(api.BrandDetail, { canteenId: id}, 'GET')
+    this.setData({
+      brand: res.data
+    })
+    
+    wx.setNavigationBarTitle({
+      title: res.data.canteenName//页面标题为路由参数
+    })
+     
+  },
+
+  
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function (res) {
+    let userInfo = wx.getStorageSync('userInfo')
+    if (res.from === 'button') {
+      setTimeout(() => {
+        this.quit()
+      }, 50)
+      return {
+        title: '【好友分享】特设薄宴,静待贵客',
+        imageUrl: '/static/images/share.png',
+        path: `pages/scene/scene?id=${this.data.brand.id}&scene=${this.data.brand.sceneNum}&shareUserId=${userInfo.id}`
+      }
+    } else {
+      return {
+        title: '【好友推荐】场景还原,实现线上选座',
+        imageUrl: '/static/images/share.png',
+        path: `pages/scene/scene?id=${this.data.brand.id}&scene=${this.data.brand.sceneNum}&shareUserId=${userInfo.id}`
+      }
+    }
+  },
+
+
+  async getDeskDetail(canteenId, qid) {
+    let res = await util.request(api.deskList, {canteenId: canteenId, pageNum: 1, pageSize: 1000}, 'GET')
+    this.setData({
+      desk: res.data.list.find(item => item.tmDesk.id === qid).tmDesk
+    })
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: async function (options) {
+    this.getBrandDetail(options.canteenId)
+    this.getDeskDetail(options.canteenId, options.deskId)
+    this.setData({ people: options.people, date: options.date, showDate: options.date.substring(5, 16) })
+  },
+
+  async save() {
+    if (this.saveIng) return;
+
+    this.saveIng = true
+    if (!PHONE.REG.test(this.data.phone)) {
+      return wx.showToast({
+        icon: 'none',
+        title: '请输入正确的手机号码',
+      })
+    } else if (!Number(this.data.code) || this.data.code.length !== 6) {
+      return wx.showToast({
+        icon: 'none',
+        title: '验证码错误',
+      })
+    } else if (!this.data.name.trim().length) {
+      return wx.showToast({
+        icon: 'none',
+        title: '请输入预订人',
+      })
+    }
+
+    let res = await util.request(api.OrderSubmit, {
+      canteenId: this.data.brand.id,
+      deskId: this.data.desk.id,
+      deskName: this.data.desk.deskName,
+      deskType: this.data.desk.type,
+      msgCode: this.data.code,
+      orderPrice: 0.01,
+      reserveDate: this.options.date.substr(0, 10),
+      reserveUserGender: this.data.sex,
+      reservePersonNum: this.options.people,
+      reserveTime: this.options.date+":00",
+      // reserveUserId	预订人的ID		false	
+      reserveUserName: this.data.name,
+      reserveUserPhone: this.data.phone
+    }, 'POST', 'application/json')
+
+    if (res.code !== 0) {
+      return wx.showToast({
+        icon: 'none',
+        title: res.msg,
+      })
+    }
+
+    try {
+      await play(res.data.id)
+      let res1 = await util.request(api.OrderQuery, { orderId: res.data.id})
+      wx.showToast({
+        icon: 'success',
+        title: res1.errmsg
+      })
+      
+      getApp().callOrderBuyCbs({deskId: this.data.desk.id})
+      this.saveIng = false
+      return this.setData({showShare: true})
+    } catch (e) {
+      wx.showToast({
+        icon: 'none',
+        title: '付款失败',
+      })
+      wx.redirectTo({
+        url: '/pages/ucenter/order/order',
+      })
+      this.saveIng = false
+    }
+  },
+})

+ 4 - 0
pages/reserve/reserve.json

@@ -0,0 +1,4 @@
+{
+  "enablePullDownRefresh": false,
+  "usingComponents": {}
+}

+ 68 - 0
pages/reserve/reserve.wxml

@@ -0,0 +1,68 @@
+<!--pages/reserve/reserve.wxml-->
+<view class="order-layer">
+	<auth bindlogin="onShow" />
+  <view class="order-list">
+    <view class="order-item-info">
+      <image src="{{brand.coverPicLink}}"></image>
+      <view class="info-text">
+        <text class="title">{{desk.deskName}}</text>
+        <view class="aaa">
+          <image src="/static/images/reserv_people.png"></image>
+          <text>{{people}}人</text>
+        </view>
+        <view class="aaa">
+          <image src="/static/images/reserv_time.png"></image>
+          <text>{{showDate}}</text>
+        </view>
+      </view>
+    </view>
+  </view>
+
+  <view>
+    <view class="t-title">
+      预订信息
+    </view>
+    <view class="from-item">
+      <text class="label">预订人</text>
+      <input type="text" value="{{name}}" style="width: 50%" bindblur="focusHandle" bindinput="updateName" placeholder="请输入名称" placeholder-style="color:#C8C9CC;" />
+      <div class="xf">
+        <view class="bottom {{sex === 1 ? 'active': ''}}" bindtap="setSex1">先生</view>
+        <view class="bottom {{sex === 0 ? 'active': ''}}" bindtap="setSex0">女士</view>
+      </div>
+    </view>
+    <view class="from-item">
+      <text class="label">联系电话</text>
+      <input type="number" style="width: 70%" maxlength="11" placeholder="请输入手机号码" bindinput="updatePhone" value="{{phone}}" placeholder-style="color:#C8C9CC;" />
+      <div class="xf">
+        <text class="bottom" bindtap="sendCode">{{sendMis ? sendMis + '秒后可重新获取' : '获取验证码'}}</text>
+      </div>
+    </view>
+    <view class="from-item">
+      <text class="label">验证码</text>
+      <input type="number" maxlength="6" placeholder="请输入验证码" bindinput="updateCode" value="{{code}}" placeholder-style="color:#C8C9CC;"  />
+    </view>
+  </view>
+  
+  <view>
+    <view class="t-title"> 温馨提示 </view>
+    <view class="tip-content">可凭借订座短信,获取凉菜一份</view>
+  </view>
+
+</view>
+<view class="save">
+  <view class="jg">押金 <text>1</text></view>
+  <view bindtap="save" class="bc">立即预订</view>
+</view>
+
+
+<view class="share" wx:if="{{showShare}}" style="background-image: url({{brand.coverPicLink}})">
+    <view class="share-layer">
+      <view class="h5">预订成功</view>
+      <view class="p">是否要分享给朋友</view>
+
+      <view>
+  			<button class="span enter" class="btn" open-type="share">立即分享</button>
+        <view class="span quit" bindtap="quit">取消</view>
+      </view>
+    </view>
+  </view>

+ 259 - 0
pages/reserve/reserve.wxss

@@ -0,0 +1,259 @@
+/* pages/reserve/reserve.wxss */
+page {
+  background: #F7F7F7;
+}
+
+.share {
+  z-index: 9999;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  left: 0;
+  top: 0;
+  background: no-repeat center center;
+  background-size: cover;
+}
+
+.share-layer {
+  position: absolute;
+  z-index: 3;
+  width: 263px;
+  height: 234px;
+  background: rgba(0,0,0,0.8);
+  border-radius: 4px;
+  left: 50%;
+  top: 40%;
+  transform: translate(-50%, -50%);
+  line-height: 40px;
+  font-weight: 400;
+  color: rgba(255, 255, 255, 0.88);
+  padding: 25px 28px 21px;
+}
+
+.h5 {
+  font-size: 18px;
+  font-weight: 400;
+  line-height: 25px;
+  color: #fff;
+  margin: 0;
+  padding: 0;
+}
+
+.p {
+  color: #fff;
+  font-size: 13px;
+  padding: 15px 0 23px;
+  line-height: 18px;
+}
+.btn ,
+.span {
+  height: 40px;
+  text-align: center;
+  margin-top: 15px;
+  line-height: 40px;
+  border-radius: 4px;
+  font-size: 15px;
+  display: block;
+}
+
+.btn  {
+  background: #FFFFFF;
+  color: rgba(0, 0, 0, 0.8);
+}
+
+.quit {
+  color: #fff;
+  border: 1px solid currentColor;
+}
+
+.order-layer {
+  padding: 0 15px;
+}
+
+.order-layer > view {
+  position: relative;
+  background: #fff;
+  box-sizing: border-box;
+  border-radius: 12px;
+  margin-top: 20px;
+  box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
+}
+
+.order-list{
+  padding: 15px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+  font-size: 24rpx;
+  z-index: 9;
+}
+
+.order-item-info {
+  display: flex;
+  align-items: center;
+}
+
+
+.order-item-info image {
+  flex: 0 0 auto;
+  width: 80px;
+  height: 80px;
+  margin-right: 20rpx;
+}
+
+.success {
+  font-size: 12px;
+  color: #0ED171;
+  font-weight: 500;
+}
+.order-item-info {
+  width: 100%;
+}
+.info-text {
+  width: calc(100% - 90px);
+}
+
+.order-item-info .title {
+  font-size: 30rpx;
+  font-weight: 500;
+  color: #333333;
+  margin-bottom: 10px;
+  display: block;
+  overflow:hidden;
+  text-overflow:ellipsis; 
+  white-space:nowrap; 
+}
+
+.aaa image {
+  width: 12px;
+  height: 12px;
+  margin-right: 0;
+}
+
+.aaa text{
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #333333;
+  line-height: 20px;
+}
+
+.aaa text:nth-child(2) {
+  margin-left: 10rpx;
+}
+
+
+.t-title {
+  font-size: 16px;
+  color: #333;
+  font-weight: 500;
+  padding: 12px;
+  border-bottom: 1px solid #ECECEC;
+}
+
+.tip-content {
+  padding: 12px;
+  font-size: 14px;
+  color: #666666;
+}
+
+.from-item {
+  margin-left: 16px;
+  padding-top: 9px;
+  position: relative;
+  border-bottom: 1px solid #EBEDF0;
+}
+
+.label {
+  display: block;
+  color: #666666;
+  font-size: 14px;
+  font-weight: 400;
+  line-height: 20px;
+}
+
+.label::after {
+  content: '*';
+  color: #FF7701;
+  font-size: 1.2em;
+}
+
+.from-item input {
+  width: 100%;
+  height: 36px;
+  font-size: 14px;
+}
+
+.xf {
+  position: absolute;
+  right: 20px;
+  bottom: 9px;
+  z-index: 999;
+}
+
+.bottom {
+  display: inline-block;
+  padding: 0 16px;
+  height: 26px;
+  line-height: 26px;
+  border: 1px solid #999999;
+  color: #999999;
+  font-size: 12px;
+  margin-left: 10px;
+  border-radius: 4px;
+}
+
+.bottom.active {
+  color: #FF7701;
+  border-color: #FF7701;
+}
+
+
+.save {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 28rpx 46rpx 64rpx;
+  display: flex;
+  justify-content: space-between;
+  background-color: #fff;
+}
+
+.save button {
+  height: 96rpx;
+  background: #ED5D18;
+  color: #fff;
+  font-size: 34rpx;
+  line-height: 96rpx;
+}
+
+.jg {
+  font-size: 14px;
+  color: #666666;
+}
+
+.jg text {
+  font-size: 24px;
+  color: #FA5151;
+  letter-spacing: 3px;
+  font-weight: 500;
+  margin-left: 10px;
+}
+
+.jg text::before {
+  content: '¥';
+  font-size: 14px;
+}
+
+.bc {
+  line-height: 40px;
+  padding: 0 30px;
+  height: 40px;
+  font-size: 14px;
+  background: #FF7701;
+  border-radius: 20px;
+  font-weight: 400;
+  color: #FFFFFF;
+}

+ 69 - 0
pages/scene/scene.js

@@ -0,0 +1,69 @@
+// pages/scene/scene.js
+const api = require('../../config/api.js');
+import config from '../../config'
+var getUserInfo = require('../../utils/getUserInfo').default;
+
+Page({
+
+  onLoad() {
+    this.setData({ url: '' })
+  },
+  getUserInfo,
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onShow: async function () {
+    // let { data: { brand } } = await util.request(api.BrandDetail, options)
+    let options = this.options
+    let brand = {
+      sceneNum: options.scene,
+      // sceneNum: 't-sfytakg',
+      id: options.id,
+      shareUserId: options.shareUserId
+    }
+
+    let userInfo = this.getUserInfo()
+    let query = {
+      m: brand.sceneNum,
+      id: brand.id,
+      fireId: options.id,
+      shareUserId: brand.shareUserId,
+      token: wx.getStorageSync('token'),
+      detailApi: api.fireDetail,
+      userId: userInfo.id,
+      attachListApi: api.getAttachList,
+    }
+    let url = config.viewHost + '?' + Object.keys(query).map(key => `${key}=${query[key]}`).join('&')
+    wx.setNavigationBarTitle({ title: brand.name })
+
+    if (userInfo.id) {
+      this.setData({ url })
+    }
+    
+    getApp().orderBuyCbs((order) => {
+      setTimeout(() => {
+        this.addOrderNew = true
+        this.setData({
+          url: url + '#' + JSON.stringify(order)
+        })
+      }, 300)
+    })
+  },
+  
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function (res) {
+    let userInfo = this.getUserInfo()
+    let title = '【好友推荐】场景还原,实现线上选座'
+    if (this.addOrderNew) {
+      title = '【好友分享】特设薄宴,静待贵客'
+    }
+    return {
+      title,
+      imageUrl: '/static/images/share.png',
+      path: `pages/scene/scene?id=${this.options.id}&scene=${this.options.scene}&shareUserId=${userInfo.id}`
+    }
+  },
+
+})

+ 3 - 0
pages/scene/scene.json

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

+ 3 - 0
pages/scene/scene.wxml

@@ -0,0 +1,3 @@
+<view class="web-view">
+  <web-view wx:if="{{url}}" src="{{url}}"></web-view>
+</view>

+ 1 - 0
pages/scene/scene.wxss

@@ -0,0 +1 @@
+/* pages/scene/scene.wxss */

+ 112 - 0
pages/search/search.js

@@ -0,0 +1,112 @@
+// pages/search/search.js
+import {searchList, getFireList} from '../../config/api'
+import util from '../../utils/util'
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    showClear: false,
+    showDepSuccess: false,
+    keyword: '',
+    showRet: false,
+    departments: [],
+    projects: [],
+    depProjects: []
+  },
+
+  async depClick(ev) {
+    this.setData({showDepSuccess: true})
+    this.setData({keyword: ev.currentTarget.dataset.keyword})
+    let res = await util.request(getFireList, {
+      pageNum: 1,
+      organizerDeptId: ev.currentTarget.dataset.id,
+      pageSize: 10000000000
+    }, 'POST')
+    
+    this.setData({depProjects: this.formBrandList(res.data.list)})
+  },
+  formBrandList(data) {
+    return data.map(item => {
+      let icon =  ~item.projectSite.indexOf('非建构筑物') ? '/static/images/fire_other.png' :
+        ~item.projectSite.indexOf('交通工具') ? '/static/images/fire_bus.png' :
+        ~item.projectSite.indexOf('垃圾及废弃物') ? '/static/images/fire_recycle.png' : '/static/images/fire_building.png'
+      return {
+        ...item, 
+        icon,
+        createTime: item.createTime.substr(0, 11)
+      }
+    })
+  },
+  onShow() {
+    if (this.options.keyword) {
+      this.inputChange({detail: {value: this.options.keyword}})
+    }
+  },
+
+  clearKeyword() {
+    this.setData({
+      keyword: ''
+    })
+    
+    this.setData({showClear: false, showDepSuccess: false, projects: [], departments: []})
+    this.search()
+  },
+  
+  gotoWV: function (event) {
+    let {
+      id,
+      scene
+    } = event.currentTarget.dataset
+
+    if (!scene) {
+      return wx.showToast({
+        icon: 'none',
+        title: '暂无VR数据,无法查看',
+      })
+    }
+    
+    wx.navigateTo({
+      url: `/pages/webview/index?id=${id}&scene=${scene}`,
+      // url: `/pages/reserve/reserve?id=${id}`
+    })
+  },
+
+  inputChange: function (e) {
+    let val = e.detail.value
+    this.setData({
+      keyword: val
+    })
+
+    this.setData({showClear: !!val.length})
+    if (val) {
+      this.search()
+    } else {
+      this.clearKeyword()
+    }
+    
+  },
+
+  async search() {
+    this.setData({showRet: false})
+    let data = await util.request(searchList, {key: this.data.keyword}, 'GET') 
+    let projects = data.data.projects.map(item => {
+      let showTitle = '<div>' + item.projectName.split(this.data.keyword).join('<span style="color: #26559B">' + this.data.keyword + '</span>') + '</div>'
+      return { ...item, showTitle}
+    })
+    let departments = data.data.departments.map(item => {
+      let showTitle = '<div>' + item.name.split(this.data.keyword).join('<span style="color: #26559B">' + this.data.keyword + '</span>') + '</div>'
+      return { ...item, showTitle}
+    })
+    this.setData({projects: projects, showRet: true})
+    this.setData({departments: departments})
+  },
+
+  tapHandle(e) {
+    let prev = getCurrentPages()[getCurrentPages().length - 2]
+    prev.inputChange.call(prev, {detail: {value: e.currentTarget.dataset.keyword}})
+    wx.navigateBack()
+  }
+})

+ 4 - 0
pages/search/search.json

@@ -0,0 +1,4 @@
+{
+  "usingComponents": {},
+  "navigationBarTitleText": "搜索"
+}

+ 66 - 0
pages/search/search.wxml

@@ -0,0 +1,66 @@
+
+
+<view class="search-layer">
+  <view class="search">
+    <text class="placeholder" wx:if="{{!keyword}}">请输入起火对象/单位名称</text>
+    <input placeholder="" bindfocus="searchFocus" placeholder-style="color:#C8C9CC;" value="{{keyword}}" bindinput="inputChange" bindconfirm="search" confirm-type="search" focus="{{true}}"
+      type="text"></input>
+    <image src="/static/images/icon-normal_search2.png" class="icon"></image>
+    <view bindtap="clearKeyword" class="clear" wx:if="{{showClear}}">
+      <image src="/static/images/list_close.png" />
+    </view>
+  </view>
+</view>
+
+<view class="layout">
+  <view class="content" wx:if="{{!showDepSuccess}}">
+    <view class="item" wx:for="{{departments}}" bindtap="depClick" data-id="{{item.id}}" data-keyword="{{item.name}}">
+      <view class="title">
+        <image src="/static/images/fire_119@2x.png"></image>
+        <view><rich-text nodes="{{item.showTitle}}"></rich-text></view>
+      </view>
+      <image src="/static/images/fire_arrow@2x.png" class="jt"></image>
+    </view>
+    <view class="item" wx:for="{{projects}}" bindtap="gotoWV" data-keyword="广州市消防救援支队"   data-scene="{{item.sceneNum}}" data-id="{{item.id}}">
+      <view class="title">
+        <view><rich-text nodes="{{item.showTitle}}"></rich-text></view>
+      </view>
+      <image src="/static/images/fire_arrow@2x.png" class="jt"></image>
+    </view>
+
+    <view wx:if="{{!departments.length && !projects.length && showRet}}" class="unbrand">
+      <image src="/static/images/fire_pic_search@2x.png"></image>
+      <view class="text">抱歉,没有找到相关结果~~</view>
+    </view>
+  </view>
+  
+  <view class="body" wx:if="{{showDepSuccess}}">
+    <view class="ul" wx:if="{{depProjects.length}}">
+      <view bindtap="gotoWV"  data-scene="{{item.sceneNum}}" data-id="{{item.id}}" class="li" wx:for='{{depProjects}}'
+        wx:key="id">
+        <view class="ctp">
+						<image mode="aspectFill" src="{{item.sceneCover|| '/static/images/fire_pic@2x.png'}}"></image>
+        </view>
+        
+        <view class="item-content">
+          <view class="b-title">
+            <text>{{item.projectName}}</text>
+							<view><image src="{{item.icon}}"></image>{{item.projectSite}}</view>
+          </view>
+          <view class="b-info">
+            <view>
+              <text>{{item.organizerDeptName}}</text>								
+            </view>
+            <view>
+              <text>{{item.createTime}}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view wx:else class="unbrand">
+      <image src="/static/images/fire_pic_data@2x.png"></image>
+      <view class="text">暂无数据或无查看权限~</view>
+    </view>
+  </view>
+</view>

+ 455 - 0
pages/search/search.wxss

@@ -0,0 +1,455 @@
+
+.layout {
+  box-sizing: border-box;
+  width: 100%;
+  padding-top: 88rpx;
+  position: relative;
+  /* background: #000; */
+}
+
+.search-layer {
+  padding: 20rpx 30rpx 0;
+  position: fixed;
+  left: 0;
+  top: 0;
+  background: #fff;
+  right: 0;
+  z-index: 888;
+}
+.search {
+  position: relative;
+  box-sizing: border-box;
+  z-index: 99;
+  height: 80rpx;
+  border-radius: 4px;
+  background: #F5F5F5;
+  flex: none;
+}
+
+.search .placeholder {
+  position: absolute;
+  color:#C8C9CC;
+  left: 70rpx;
+  top: 50%;
+  transform: translate(0, -50%);
+  font-size: 14px;
+}
+
+.search view:not(.clear) {
+  background: none;
+  width: 80%;
+  border: none;
+  padding-left: 20rpx;
+  font-size: 24rpx;
+  color: #fff;
+  line-height: 24rpx;
+  vertical-align: middle;
+}
+
+.search input {
+  box-sizing: border-box;
+  color: rgba(0, 0, 0, 0.3);
+  height: 100%;
+  width: 100%;
+  padding: 0 70rpx 0 70rpx;
+  position: relative;
+  z-index: 999;
+  border-radius: 4px;
+  font-size: 14px;
+  border: none;
+}
+
+.search .icon {
+  width: 28rpx;
+  height: 28rpx;
+  display: inline-block;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  left: 28rpx;
+}
+
+.clear {
+  width: 30px;
+  height: 30px;
+  display: inline-block;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  right: 4px;
+  padding: 10px;
+  z-index: 9999999;
+}
+
+.clear image {
+  display: block;
+  width: 100%;
+  height: 100%;
+}
+
+
+.content {
+  padding: 30rpx;
+  /* width: calc(100% + 20px); */
+}
+
+.item {
+  padding: 22rpx 0;
+  border-bottom: 1px solid #EBEDF0;
+  display: flex;
+  justify-content: space-evenly;
+  align-items: center;
+}
+
+.title {
+  flex: 1;
+  display: flex;
+  align-items: center;
+}
+
+.title image {
+  width: 24rpx;
+  height: 24rpx;
+  margin-right: 3px;
+}
+
+.title view {
+  font-size: 14px;
+  font-weight: 400;
+  color: #323233;
+  line-height: 20px;
+}
+.title text {
+  color: #26559B;
+}
+
+.jt {
+  flex: none;
+  width: 20rpx;
+  height: 20rpx;
+}
+
+
+
+.body .ul {
+  position: relative;
+  width: 100%;
+  font-size: 0;
+  z-index: 1;
+  overflow: hidden;
+}
+
+.body .li {
+  padding: 20rpx 30rpx;
+  background: #FFFFFF;
+  border-radius: 4px;
+  border-bottom: 1px solid #EBEDF0;
+  display: flex;
+}
+.ctp {
+  flex: none;
+}
+
+
+.body .li .b-title view:nth-child(2) {
+  font-size: 14px;
+  font-weight: 400;
+  color: #646566;
+  line-height: 20px;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+
+.body .li .b-title view image {
+  height: 20px;
+  width: 20px;
+  margin-right: 5px;
+  vertical-align: baseline;
+}
+
+.ctp image{
+  width: 160rpx;
+  height: 160rpx;
+}
+
+.body .li .count {
+  position: absolute;
+  width: 90rpx;
+  height: 28rpx;
+  background: rgba(0, 0, 0, 0.3);
+  opacity: 1;
+  border-radius: 14rpx;
+  right: 22rpx;
+  top: 288rpx;
+  font-size: 18rpx;
+  font-weight: 400;
+  /* line-height: 28rpx; */
+  color: #fff;
+  text-align: center;
+  padding: 7rpx;
+  padding-left: 31rpx;
+  line-height: 20rpx;
+  overflow: hidden;
+  box-sizing: border-box;
+}
+
+.body .li .count image {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 24rpx;
+  height: 14rpx;
+  left: 7rpx;
+  top: 8rpx;
+}
+
+.item-content {
+  padding-left: 20rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-around;
+  flex: none;
+  width: calc(100% - 180rpx);
+}
+
+.body .li .b-title {
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+.body .li .b-title text:nth-child(1) {
+  font-size: 16px;
+  font-weight: 500;
+  color: #333333;
+  line-height: 22px;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+  display: block;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+.body .li .b-title text:nth-child(2) {
+  font-size: 14px;
+  font-weight: 400;
+  color: #646566;
+  line-height: 20px;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+.body .li .b-info {
+  margin-top: 16rpx;
+  display: flex;
+  align-items: center;
+}
+
+.body .li .b-info text {
+  font-size: 11px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #969799;
+  line-height: 16px;
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.body .li .b-info view {
+  width: 50%;
+}
+.body .li .b-info view:first-child text{
+  text-align: left;
+}
+
+.body .li .b-info view:last-child text{
+  text-align: right;
+}
+
+.body .li .b-info text.xxxx {
+  color: #FF7701;
+}
+.body .li .b-info text:nth-child(2) {
+  margin-left: 12rpx;
+}
+
+
+.unbrand {
+  margin-top: 180rpx;
+  text-align: center;
+}
+
+.unbrand image {
+  display: inline-block;
+  width: 250rpx;
+  height: 274rpx;
+}
+
+.unbrand .text {
+  color: #666C7D;
+  font-size: 30rpx;
+  margin-top: 40rpx;
+}
+
+.banner {
+  margin: 16rpx 0 38rpx;
+  position: relative;
+}
+
+.banner swiper {
+  height: 296rpx;
+}
+
+.banner .swiper-item image,
+.banner .swiper-item {
+  width: 100%;
+  height: 100%;
+}
+
+.banner .swiper-item {
+  border-radius: 4px;
+  overflow: hidden
+}
+
+.dots {
+  position: absolute;
+  bottom: 8rpx;
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 2;
+}
+
+.dots view {
+  display: inline-block;
+  width: 4px;
+  height: 4px;
+  background: #FFFFFF;
+  border-radius: 2px;
+  opacity: 0.5;
+  margin: 0 4rpx
+}
+
+.dots view.dotactive {
+  width: 8px;
+  opacity: 1;
+}
+
+.hotcateg {
+  margin-bottom: 40rpx;
+}
+
+.hotcateg .title {
+  font-size: 30rpx;
+  font-weight: bold;
+  line-height: 44rpx;
+  color: #131D34;
+  margin-bottom: 14rpx;
+  padding: 0 46rpx;
+}
+
+.hotcateg .swiper-item {
+  margin: 0 10rpx;
+  position: relative;
+  border-radius: 4px;
+  overflow: hidden;
+  width: calc(100% - 20rpx);
+  height: 100%;
+}
+
+.hotcateg .swiper-item image {
+  width: 100%;
+  height: 100%;
+}
+
+.hotcateg .swiper-item view {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 8rpx 24rpx;
+  font-size: 22rpx;
+  color: #fff;
+  background-color: rgba(0, 0, 0, 0.4);
+  text-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
+}
+
+
+.bgimg {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100vw;
+  height: 690rpx;
+  z-index: -9;
+}
+
+.bgimg image {
+  width: 100%;
+  height: 100%;
+}
+
+.page-title {
+  position: fixed;
+  box-sizing: content-box;
+  padding-right: 32rpx;
+  padding-left: 30rpx;
+  font-size: 32rpx;
+  left: 0;
+  top: 0;
+  width: 100vw;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+  z-index: 9999999999;
+}
+
+.page-title  navigator {
+  display: block;
+  max-width: 65%;
+  display: flex;
+  align-items: center;
+}
+
+.page-title navigator text {
+  display: block;
+  overflow:hidden;
+  text-overflow:ellipsis;
+  white-space:nowrap;
+}
+
+
+.page-title image {
+  flex: 0 0 auto;
+  background: rgb(180, 180, 180);
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  margin-right: 10rpx;
+  vertical-align: top;
+}
+
+.page-title text {
+  position: relative;
+  color: #fff;
+  z-index: 1;
+  display: inline-block;
+  font-size: 18px;
+  font-family: PingFangSC-Semibold, PingFang SC;
+  font-weight: 600;
+  color: #FFFFFF;
+  line-height: 25px;
+}
+
+.zxczxc {
+  position: absolute;
+  left: 0;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+}

+ 67 - 0
pages/share.js

@@ -0,0 +1,67 @@
+// pages/share.js
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+    getApp().onPullDownRefresh()
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 3 - 0
pages/share.json

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

+ 2 - 0
pages/share.wxml

@@ -0,0 +1,2 @@
+<!--pages/share.wxml-->
+<text>pages/share.wxml</text>

+ 1 - 0
pages/share.wxss

@@ -0,0 +1 @@
+/* pages/share.wxss */

+ 65 - 0
pages/ucenter/address/address.js

@@ -0,0 +1,65 @@
+var util = require('../../../utils/util.js');
+var api = require('../../../config/api.js');
+var app = getApp();
+
+Page({
+  data: {
+    addressList: [],
+  },
+  onLoad: function (options) {
+    // 页面初始化 options为页面跳转所带来的参数
+    
+  },
+  onReady: function () {
+    // 页面渲染完成
+  },
+  onShow: function () {
+    // 页面显示
+    this.getAddressList();
+  },
+  onPullDownRefresh(){
+    this.getAddressList();
+    getApp().onPullDownRefresh()
+  },
+
+  getAddressList (){
+    let that = this;
+    util.request(api.AddressList).then(function (res) {
+      if (res.code === 0) {
+        that.setData({
+          addressList: res.data
+        });
+      }
+    });
+  },
+  addressAddOrUpdate (event) {
+    wx.navigateTo({
+      url: '/pages/ucenter/addressAdd/addressAdd?id=' + event.currentTarget.dataset.addressId
+    })
+  },
+  deleteAddress(event){
+    let that = this;
+    wx.showModal({
+      title: '',
+      content: '确定要删除地址?',
+      success: function (res) {
+        if (res.confirm) {
+          let addressId = event.target.dataset.addressId;
+          util.request(api.AddressDelete, { id: addressId },'POST', 'application/json').then(function (res) {
+            if (res.code === 0) {
+              that.getAddressList();
+            }
+          });
+        }
+      }
+    })
+    return false;
+    
+  },
+  onHide: function () {
+    // 页面隐藏
+  },
+  onUnload: function () {
+    // 页面关闭
+  }
+})

+ 4 - 0
pages/ucenter/address/address.json

@@ -0,0 +1,4 @@
+{
+  "enablePullDownRefresh": true,
+  "navigationBarTitleText": "地址管理"
+}

+ 24 - 0
pages/ucenter/address/address.wxml

@@ -0,0 +1,24 @@
+<view class="container">
+  <auth bindlogin="onShow" />
+  <lip-sync />
+    <view class="address-list" wx:if="{{ addressList.length > 0 }}">
+        <view class="item" wx:for="{{addressList}}" wx:key="{{item.id}}" bindtap="addressAddOrUpdate" data-address-id="{{item.id}}">
+            <view class="l">
+                <view class="name">{{item.userName}}</view>
+                <view class="default" wx:if="{{item.is_default}}">默认</view>
+            </view>
+            <view class="c">
+                <view class="mobile">{{item.telNumber}}</view>
+                <view class="address">{{item.full_region+item.detailInfo}}</view>
+            </view>
+            <view class="r">
+                <image catchtap="deleteAddress" data-address-id="{{item.id}}" class="del" src="/static/images/del-address.png"></image>
+            </view>
+        </view>
+    </view>
+    <view class="empty-view" wx:if="{{ addressList.length <= 0 }}">
+      <image class="icon" src="http://yanxuan.nosdn.127.net/hxm/yanxuan-wap/p/20161201/style/img/icon-normal/noAddress-26d570cefa.png"></image>
+      <text class="text">收货地址在哪里</text>
+    </view>
+    <view class="add-address" bindtap="addressAddOrUpdate" data-address-id="0">新建</view>
+</view>

+ 129 - 0
pages/ucenter/address/address.wxss

@@ -0,0 +1,129 @@
+page{
+    width: 100%;
+    height: calc(100% - 100rpx);
+    padding-bottom: 100rpx;
+    background: #f4f4f4;
+}
+
+.container{
+  width: 100%;
+    height: 100%;
+}
+
+.address-list{
+    padding-left: 31.25rpx;
+    background: #fff url(https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/wxmall/images/address-bg-bd.png) 0 0 repeat-x;
+    background-size: auto 10.5rpx;
+    margin-bottom: 90rpx;
+    padding-bottom: 100rpx;
+}
+
+.address-list .item{
+    min-height: 156rpx;
+    align-items: center;
+    display: flex;
+    border-bottom: 1rpx solid #DCD9D9;
+    padding: 20rpx 0; 
+
+}
+
+.address-list .l{
+    width: 155rpx;
+    height: 80rpx;
+    overflow: hidden;
+}
+
+.address-list .name{
+    width: 155rpx;
+    height: 43rpx;
+    font-size: 29rpx;
+    color: #333;
+    margin-bottom: 5.2rpx;
+        text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+}
+
+.address-list .default{
+    width: 62.5rpx;
+    height: 33rpx;
+    line-height: 33rpx;
+    text-align: center;
+    font-size: 20rpx;
+    color: #ED5D18;
+    border: 1rpx solid #ED5D18;
+    visibility: visible;
+}
+
+
+.address-list .c{
+    flex: 1;
+    height: auto;
+    overflow: hidden;
+}
+
+.address-list .mobile{
+
+    height: 29rpx;
+    font-size: 29rpx;
+    line-height: 29rpx;
+    overflow: hidden;
+    color: #333;
+    margin-bottom: 6.25rpx;
+}
+
+.address-list .address{
+    font-size: 25rpx;
+    overflow: hidden;
+    color: #666;
+}
+
+.address-list .r{
+    width: 52rpx;
+    height: auto;
+    overflow: hidden;
+    margin-right: 16.5rpx;
+}
+
+.address-list .del{
+    display: block;
+    width: 52rpx;
+    height: 52rpx;
+}
+
+.add-address{
+    background: #ED5D18;
+    text-align: center;
+    width: 100%;
+    height: 99rpx;
+    line-height: 99rpx;
+    position: fixed;
+    border-radius: 0;
+    border: none;
+    color: #fff;
+    font-size: 29rpx;
+    bottom: 0;
+    left:0;
+}
+
+.empty-view{
+  height: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.empty-view .icon{
+  height: 248rpx;
+  width: 258rpx;
+  margin-bottom: 10rpx;
+}
+
+.empty-view .text{
+  width: auto;
+  font-size: 28rpx;
+  line-height: 35rpx;
+  color: #999;
+}

+ 351 - 0
pages/ucenter/addressAdd/addressAdd.js

@@ -0,0 +1,351 @@
+var util = require('../../../utils/util.js');
+var api = require('../../../config/api.js');
+var app = getApp();
+Page({
+  data: {
+    address: {
+      id:0,
+      province_id: 0,
+      city_id: 0,
+      district_id: 0,
+      address: '',
+      full_region: '',
+      userName: '',
+      telNumber: '',
+      is_default: 1,
+      scrollTop:0
+    },
+    addressId: 0,
+    openSelectRegion: false,
+    selectRegionList: [
+      { id: 0, name: '省份', parent_id: 1, type: 1 },
+      { id: 0, name: '城市', parent_id: 1, type: 2 },
+      { id: 0, name: '区县', parent_id: 1, type: 3 }
+    ],
+    regionType: 1,
+    regionList: [],
+    selectRegionDone: false,
+    currentRegion:0
+  },
+  bindinputMobile(event) {
+    let address = this.data.address;
+    address.telNumber = event.detail.value;
+    this.setData({
+      address: address
+    });
+  },
+  bindinputName(event) {
+    let address = this.data.address;
+    address.userName = event.detail.value;
+    this.setData({
+      address: address
+    });
+  },
+  bindinputAddress (event){
+    let address = this.data.address;
+    address.detailInfo = event.detail.value;
+    
+
+    this.setData({
+      address: address
+    });
+  },
+  bindIsDefault(){
+    let address = this.data.address;
+    address.is_default = !address.is_default;
+    this.setData({
+      address: address
+    });
+  },
+  getAddressDetail() {
+    let that = this;
+    util.request(api.AddressDetail, { id: that.data.addressId }).then(function (res) {
+      if (res.code === 0) {
+        if(res.data){
+            that.setData({
+                address: res.data
+            });
+        }
+      }
+    });
+  },
+  setRegionDoneStatus() {
+    let that = this;
+    let doneStatus = that.data.selectRegionList.every(item => {
+      return item.id != 0;
+    });
+
+    that.setData({
+      selectRegionDone: doneStatus
+    })
+
+  },
+  chooseRegion() {
+    let that = this;
+    this.setData({
+      openSelectRegion: !this.data.openSelectRegion
+    });
+
+    //设置区域选择数据
+    let address = this.data.address;
+    if (address.province_id > 0 && address.city_id > 0 && address.district_id > 0) {
+      let selectRegionList = this.data.selectRegionList;
+      selectRegionList[0].id = address.province_id;
+      selectRegionList[0].name = address.province_name;
+      selectRegionList[0].parent_id = 1;
+
+      selectRegionList[1].id = address.city_id;
+      selectRegionList[1].name = address.city_name;
+      selectRegionList[1].parent_id = address.province_id;
+
+      selectRegionList[2].id = address.district_id;
+      selectRegionList[2].name = address.district_name;
+      selectRegionList[2].parent_id = address.city_id;
+
+      this.setData({
+        selectRegionList: selectRegionList,
+        regionType: 3
+      });
+
+      this.getRegionList(address.city_id);
+    } else {
+      this.setData({
+        selectRegionList: [
+          { id: 0, name: '省份', parent_id: 1, type: 1 },
+          { id: 0, name: '城市', parent_id: 1, type: 2 },
+          { id: 0, name: '区县', parent_id: 1, type: 3 }
+        ],
+        regionType: 1
+      })
+      this.getRegionList(1);
+    }
+
+    this.setRegionDoneStatus();
+
+  },
+  onLoad: function (options) {
+    // 页面初始化 options为页面跳转所带来的参数
+    if (options.id) {
+      this.setData({
+        addressId: options.id
+      });
+      this.getAddressDetail();
+    }
+
+    this.getRegionList(1);
+
+  },
+  onReady: function () {
+
+  },
+  selectRegionType(event) {
+    let that = this;
+    let regionTypeIndex = event.target.dataset.regionTypeIndex;
+    let selectRegionList = that.data.selectRegionList;
+
+    //判断是否可点击
+    if (regionTypeIndex + 1 == this.data.regionType || (regionTypeIndex - 1 >= 0 && selectRegionList[regionTypeIndex-1].id <= 0)) {
+      return false;
+    }
+
+    this.setData({
+      regionType: regionTypeIndex + 1
+    })
+    
+    let selectRegionItem = selectRegionList[regionTypeIndex];
+
+    this.getRegionList(selectRegionItem.parent_id);
+
+    this.setRegionDoneStatus();
+
+  },
+  selectRegion(event) {
+    let that = this;
+    let regionIndex = event.target.dataset.regionIndex;
+    let regionItem = this.data.regionList[regionIndex];
+    let regionType = regionItem.type;
+    let selectRegionList = this.data.selectRegionList;
+    selectRegionList[regionType - 1] = regionItem;
+
+    this.setData({
+      currentRegion: this.data.regionList[regionIndex]
+    })
+
+    if (regionType != 3) {
+      this.setData({
+        selectRegionList: selectRegionList,
+        regionType: regionType + 1,
+        scrollTop: 0
+      })
+      this.getRegionList(regionItem.id);
+    } else {
+      this.setData({
+        selectRegionList: selectRegionList,
+        scrollTop: 0
+      })
+    }
+    
+    //重置下级区域为空
+    selectRegionList.map((item, index) => {
+      if (index > regionType - 1) {
+        item.id = 0;
+        item.name = index == 1 ? '城市' : '区县';
+        item.parent_id = 0;
+      }
+      return item;
+    });
+
+    this.setData({
+      selectRegionList: selectRegionList
+    })
+
+
+    that.setData({
+      regionList: that.data.regionList.map(item => {
+
+        //标记已选择的
+        if (that.data.regionType == item.type && that.data.selectRegionList[that.data.regionType - 1].id == item.id) {
+          item.selected = true;
+        } else {
+          item.selected = false;
+        }
+
+        return item;
+      })
+    });
+
+    this.setRegionDoneStatus();
+
+  },
+  doneSelectRegion() {
+    if (this.data.selectRegionDone === false) {
+      return false;
+    }
+
+    let address = this.data.address;
+    let selectRegionList = this.data.selectRegionList;
+    address.province_id = selectRegionList[0].id;
+    address.city_id = selectRegionList[1].id;
+    address.district_id = selectRegionList[2].id;
+    address.province_name = selectRegionList[0].name;
+    address.city_name = selectRegionList[1].name;
+    address.district_name = selectRegionList[2].name;
+    address.full_region = selectRegionList.map(item => {
+      return item.name;
+    }).join('');
+
+    this.setData({
+      address: address,
+      openSelectRegion: false
+    });
+
+  },
+  cancelSelectRegion() {
+    this.setData({
+      openSelectRegion: false,
+      regionType: this.data.regionDoneStatus ? 3 : 1
+    });
+
+  },
+  getRegionList(regionId) {
+    let that = this;
+    let regionType = that.data.regionType;
+    util.request(api.RegionList, { parentId: regionId || that.data.currentRegion.id }).then(function (res) {
+      if (res.code === 0) {
+        that.setData({
+          regionList: res.data.map(item => {
+
+            //标记已选择的
+            if (regionType == item.type && that.data.selectRegionList[regionType - 1].id == item.id) {
+              item.selected = true;
+            } else {
+              item.selected = false;
+            }
+
+            return item;
+          })
+        });
+      }
+    });
+  },
+  cancelAddress(){
+    wx.navigateBack({
+      url: '/pages/ucenter/address/address',
+    })
+  },
+  saveAddress(){
+    let address = this.data.address;
+
+    if (address.userName == '') {
+      util.showErrorToast('请输入姓名');
+
+      return false;
+    }
+
+    if (address.telNumber == '') {
+      util.showErrorToast('请输入手机号码');
+      return false;
+    }
+
+    if (address.district_id == 0) {
+      util.showErrorToast('请输入省市区');
+      return false;
+    }
+
+    if (!address.detailInfo){
+      util.showErrorToast('请输入详细地址');
+      return false;
+    }
+
+    if (!address.detailInfo.trim()) {
+      util.showErrorToast('请输入详细地址');
+      return false;
+    }
+
+    let that = this;
+    util.request(api.AddressSave, { 
+      id: address.id,
+      userName: address.userName,
+      telNumber: address.telNumber,
+      province_id: address.province_id,
+      city_id: address.city_id,
+      district_id: address.district_id,
+      is_default: address.is_default,
+      provinceName: address.province_name,
+      cityName: address.city_name,
+      countyName: address.district_name,
+      detailInfo: address.detailInfo,
+    }, 'POST', 'application/json').then(function (res) {
+      if (res.code === 0) {
+        wx.navigateBack({
+          url: '/pages/ucenter/address/address',
+        })
+      }else{
+
+        wx.showModal({
+          title: '提示',
+          content: res.errmsg,
+          showCancel: false,
+          confirmColor:'#ED5D18',
+          success: function (res) {
+
+          }
+        })
+     
+      }
+    });
+
+  },
+  onShow: function () {
+    // 页面显示
+
+  },
+  onHide: function () {
+    // 页面隐藏
+
+  },
+  onUnload: function () {
+    // 页面关闭
+
+  }
+})

+ 4 - 0
pages/ucenter/addressAdd/addressAdd.json

@@ -0,0 +1,4 @@
+{
+    "enablePullDownRefresh": false,
+    "navigationBarTitleText": "地址管理"
+}

+ 45 - 0
pages/ucenter/addressAdd/addressAdd.wxml

@@ -0,0 +1,45 @@
+<view class="add-address">
+  <auth bindlogin="onShow" />
+  <lip-sync />
+    <view class="add-form">
+        <view class="form-item">
+            <input class="input"  bindinput="bindinputName" placeholder="姓名" value="{{address.userName}}" auto-focus/>
+        </view>
+        <view class="form-item">
+            <input class="input" bindinput="bindinputMobile" value="{{address.telNumber}}" maxlength="15" type="number" placeholder="手机号码"/>
+        </view>
+        <view class="form-item">
+            <input class="input" value="{{address.full_region}}" disabled="true" bindtap="chooseRegion" placeholder="省份、城市、区县"/>
+        </view>
+        <view class="form-item">
+            <input class="input" bindinput="bindinputAddress" value="{{address.detailInfo}}" placeholder="详细地址, 如街道、楼盘号等"/>
+        </view>
+        <view class="form-default">
+            
+            <view bindtap="bindIsDefault" class="default-input {{address.is_default == 1 ? 'selected' : ''}}">
+              <image src="{{address.is_default ?  '../../../static/images/icon-normal_checkbox-checked.png' :'../../../static/images/icon-normal_checkbox.png' }}" class="checkbox"></image>
+              设为默认地址
+            </view>
+        </view>
+    </view>
+
+    <view class="btns">
+        <button class="cannel" bindtap="cancelAddress">取消</button>
+        <button class="save" bindtap="saveAddress">保存</button>
+    </view>
+
+    <view class="region-select" wx:if="{{openSelectRegion}}">
+      <view class="hd">
+        <view class="region-selected">
+          <view class="item {{item.id == 0 ? 'disabled' : ''}} {{(regionType -1) === index ? 'selected' : ''}}" bindtap="selectRegionType" data-region-type-index="{{index}}" wx:for="{{selectRegionList}}" wx:key="{{item.id}}">{{item.name}}</view>
+        </view>
+        <view class="done {{selectRegionDone ? '' : 'disabled'}}" bindtap="doneSelectRegion">确定</view>
+      </view>
+      <scroll-view scroll-y scroll-top='{{scrollTop}}' scroll-x='false' class="bd">
+        <view class="region-list">
+          <view class="item {{item.selected ? 'selected' : ''}}" bindtap="selectRegion" data-region-index="{{index}}" wx:for="{{regionList}}" wx:key="{{item.id}}">{{item.name}}</view>
+        </view>
+      </scroll-view>
+    </view>
+</view>
+<view class="bg-mask" bindtap="cancelSelectRegion" wx:if="{{openSelectRegion}}"></view>

+ 180 - 0
pages/ucenter/addressAdd/addressAdd.wxss

@@ -0,0 +1,180 @@
+page{
+    height: 100%;
+    background: #f4f4f4;
+}
+.add-address .add-form{
+    background: #fff;
+    width: 100%;
+    height: auto;
+    overflow: hidden;
+}
+
+.add-address .form-item{
+    height: 116rpx;
+    padding-left: 31.25rpx;
+    border-bottom: 1px solid #d9d9d9;
+    display: flex;
+    align-items: center;
+    padding-right: 31.25rpx;
+}
+
+.add-address .input{
+    flex: 1;
+    height: 44rpx;
+    line-height: 44rpx;
+    overflow: hidden;
+}
+
+.add-address .form-default{
+    border-bottom: 1px solid #d9d9d9;
+    height: 96rpx;
+    background: #fafafa;
+    padding-top: 28rpx;
+    font-size: 28rpx;
+}
+
+.default-input{
+    margin: 0 auto;
+    display: block;
+    width: 240rpx;
+    height: 40rpx;
+    line-height: 40rpx;
+    font-size: 28rpx;
+}
+
+.checkbox{
+  display: inline-block;
+  height: 34rpx;
+  width: 34rpx;
+  vertical-align: middle;
+  /* margin: 65rpx 18rpx 65rpx 26rpx; */
+}
+
+.add-address .btns{
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    overflow: hidden;
+    display: flex;
+    height: 100rpx;
+    width: 100%;
+}
+
+.add-address .cannel,.add-address .save{
+    flex: 1;
+    height: 100rpx;
+    text-align: center;
+    line-height: 100rpx;
+    font-size: 28rpx;
+    color: #fff;
+    border:none;
+    border-radius: 0;
+}
+
+.add-address .cannel{
+    background: #333;
+}
+
+.add-address .save{
+    background: #ED5D18;
+}
+
+
+.region-select{
+  width: 100%;
+  height: 600rpx;
+  background: #fff;
+  position: fixed;
+  z-index: 10;
+  left:0;
+  bottom: 0;
+}
+
+.region-select .hd{
+  height: 108rpx;
+  width: 100%;
+  border-bottom: 1px solid #f4f4f4;
+  padding: 46rpx 30rpx 0 30rpx;
+}
+
+.region-select .region-selected{
+  float: left;
+  height: 60rpx;
+  display: flex;
+}
+
+.region-select .region-selected .item{
+  max-width: 170rpx;
+  margin-right: 30rpx;
+  text-align: left;
+  line-height: 60rpx;
+  height: 100%;
+  color: #333;
+  font-size: 28rpx;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.region-select .region-selected .item.disabled{
+  color: #999;
+}
+
+.region-select .region-selected .item.selected{
+  color: #ED5D18;
+}
+
+.region-select .done{
+  float: right;
+  height: 60rpx;
+  width: 60rpx;
+  border: none;
+  background: #fff;
+  line-height: 60rpx;
+  text-align: center;
+  color: #333;
+  font-size: 28rpx;
+}
+
+.region-select .done.disabled{
+  color: #999;
+}
+
+
+
+.region-select .bd{
+  height: 492rpx;
+  width: 100%;
+  padding: 0 30rpx;
+}
+
+.region-select .region-list{
+  height: auto;
+  overflow-y: scroll;
+  overflow-x: hidden;
+
+}
+
+.region-select .region-list .item{
+  width: 100%;
+  height: 104rpx;
+  line-height: 104rpx;
+  text-align: left;
+  color: #333;
+  font-size: 28rpx;
+}
+
+.region-select .region-list .item.selected{
+  color: #ED5D18;
+}
+
+
+.bg-mask{
+  height: 100%;
+  width: 100%;
+  background: rgba(0, 0, 0, 0.4);
+  position: fixed;
+  top:0;
+  left:0;
+  z-index: 8;
+}

+ 88 - 0
pages/ucenter/collect/collect.js

@@ -0,0 +1,88 @@
+var util = require('../../../utils/util.js');
+var api = require('../../../config/api.js');
+
+var app = getApp();
+
+Page({
+  data: {
+    typeId: 0,
+    loadding: true,
+    collectList: []
+  },
+  getCollectList() {
+    let that = this;
+    that.setData({loadding: true})
+    util.request(api.CollectList, { typeId: that.data.typeId}).then(function (res) {
+      if (res.code === 0) {
+        that.setData({
+          collectList: res.data,
+          loadding: false
+        });
+      }
+    });
+  },
+  onLoad: function (options) {
+    getApp().checkNetStatu();
+  },
+  onReady: function () {
+
+  },
+  onShow: function () {
+    this.getCollectList();
+  },
+  onHide: function () {
+    // 页面隐藏
+
+  },
+  onUnload: function () {
+    // 页面关闭
+  },
+  openGoods(event) {
+    
+    let that = this;
+    let goodsId = this.data.collectList[event.currentTarget.dataset.index].value_id;
+
+    //触摸时间距离页面打开的毫秒数  
+    var touchTime = that.data.touch_end - that.data.touch_start;
+    //如果按下时间大于350为长按
+    if (touchTime > 350) {
+      wx.showModal({
+        title: '',
+        content: '确定删除收藏吗?',
+        success: function (res) {
+          if (res.confirm) {
+            util.request(api.CollectAddOrDelete, { typeId: that.data.typeId, valueId: goodsId}).then(function (res) {
+              if (res.code === 0) {
+                wx.showToast({
+                  title: '删除成功',
+                  icon: 'success',
+                  duration: 2000
+                });
+                that.getCollectList();
+              }
+            });
+          }
+        }
+      })
+    } else {
+      
+      wx.navigateTo({
+        url: '/pages/goods/goods?id=' + goodsId,
+      });
+    }  
+  },
+  //按下事件开始  
+  touchStart: function (e) {
+    let that = this;
+    that.setData({
+      touch_start: e.timeStamp
+    })
+  },
+  //按下事件结束  
+  touchEnd: function (e) {
+    let that = this;
+    that.setData({
+      touch_end: e.timeStamp
+    })
+  },
+})

+ 4 - 0
pages/ucenter/collect/collect.json

@@ -0,0 +1,4 @@
+{
+  "navigationBarTitleText": "我的收藏",
+  "enablePullDownRefresh": false
+}

+ 18 - 0
pages/ucenter/collect/collect.wxml

@@ -0,0 +1,18 @@
+<view class="container">
+  <auth bindlogin="onShow" />
+  <lip-sync />
+  <view class="collect-list" wx:if="{{loadding || collectList.length > 0}}">
+    <view class="item" bindtap="openGoods" wx:for="{{collectList}}" wx:key="{{item.id}}" data-index="{{index}}">
+      <image class="img" src="{{item.list_pic_url}}"></image>
+      <view class="info">
+        <view class="name">{{item.name}}</view>
+        <view class="subtitle">{{item.goods_simple_desc}}</view>
+        <view class="price">¥{{item.retail_price}}</view>
+      </view>
+    </view>
+  </view>
+  <view wx:else class="unbrand">
+    <image src="/static/images/img_noshop@3x.png"></image>
+    <view class="text">收藏夹空空如也</view>
+  </view>
+</view>

+ 85 - 0
pages/ucenter/collect/collect.wxss

@@ -0,0 +1,85 @@
+page{
+    background: #f4f4f4;
+    min-height: 100%;
+}
+
+.container{
+    background: #f4f4f4;
+    min-height: 100%;
+}
+
+.collect-list{
+  width: 100%;
+  height: auto;
+  overflow: hidden;
+  background: #fff;
+  padding-left: 30rpx;
+  border-top: 1px solid #e1e1e1;
+}
+
+.item{
+  height: 212rpx;
+  width: 720rpx;
+  background: #fff;
+  padding: 30rpx 30rpx 30rpx 0;
+  border-bottom: 1px solid #e1e1e1;
+}
+
+.item:last-child{
+  border-bottom: 1px solid #fff;
+}
+
+.item .img{
+  float: left;
+  width: 150rpx;
+  height: 150rpx;
+}
+
+.item .info{
+  float: right;
+  width: 540rpx;
+  height: 150rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  padding-left: 20rpx;
+}
+
+.item .info .name{
+  font-size: 28rpx;
+  color: #333;
+  line-height: 40rpx;
+}
+
+
+.item .info .subtitle{
+  margin-top: 8rpx;
+  font-size: 24rpx;
+  color: #888;
+  line-height: 40rpx;
+}
+
+.item .info .price{
+  margin-top: 8rpx;
+  font-size: 28rpx;
+  color: #333;
+  line-height: 40rpx;
+}
+
+
+.unbrand {
+  margin-top: 180rpx;
+  text-align: center;
+}
+
+.unbrand image {
+  display: inline-block;
+  width: 250rpx;
+  height: 274rpx;
+}
+
+.unbrand .text {
+  color: #666C7D;
+  font-size: 30rpx;
+  margin-top: 40rpx;
+}

+ 115 - 0
pages/ucenter/feedback/feedback.js

@@ -0,0 +1,115 @@
+var util = require('../../../utils/util.js');
+var api = require('../../../config/api.js');
+
+
+
+var app = getApp();
+
+Page({
+  data: {
+    array: ['请选择反馈类型', '商品相关', '物流状况', '客户服务', '优惠活动', '功能异常', '产品建议', '其他'],
+    index: 0,
+    content:'',
+    contentLength:0,
+    mobile:''
+  },
+  bindPickerChange: function (e) {
+    this.setData({
+      index: e.detail.value
+    });
+  },
+  mobileInput: function (e) {
+    let that = this;
+    
+    this.setData({
+      mobile: e.detail.value,
+    });
+  },
+  contentInput: function (e) {
+   
+    let that = this;
+    this.setData({
+      contentLength: e.detail.cursor,
+      content: e.detail.value,
+    });
+  },
+  cleanMobile:function(){
+    let that = this;
+
+  },
+  sbmitFeedback : function(e){
+    let that = this;
+    if (that.data.index == 0){
+      util.showErrorToast('请选择反馈类型');
+      return false;
+    }
+
+    if (that.data.content == '') {
+      util.showErrorToast('请输入反馈内容');
+      return false;
+    }
+
+    if (that.data.mobile == '') {
+      util.showErrorToast('请输入手机号码');
+      return false;
+    }
+    if (!that.data.mobile.match(/^1[3-9][0-9]\d{8}$/)) {
+      wx.showModal({
+        title: '温馨提示',
+        content: '手机号格式不正确,仅支持国内手机号码'
+      });
+      return false
+    }
+    wx.showLoading({
+      title: '提交中...',
+      mask:true,
+      success: function () {
+
+      }
+    });
+
+    util.request(api.FeedbackAdd, 
+      { phoneNum: that.data.mobile, type: that.data.index, typeDesc: that.data.array[that.data.index], content: that.data.content},
+      'POST',
+      'application/json'
+    ).then(function (res) {
+      if (res.code === 0) {
+
+        wx.hideLoading();
+
+        wx.showToast({
+          title: '反馈成功',
+          icon: 'success',
+          duration: 2000,
+          complete: function () {
+            that.setData({
+              index: 0,
+              content: '',
+              contentLength: 0,
+              mobile: ''
+            });
+          }
+        });
+      } else {
+        util.showErrorToast(res.data);
+      }
+      
+    });
+  },
+  onLoad: function (options) {
+    getApp().checkNetStatu();
+  },
+  onReady: function () {
+
+  },
+  onShow: function () {
+
+  },
+  onHide: function () {
+    // 页面隐藏
+
+  },
+  onUnload: function () {
+    // 页面关闭
+  }
+})

+ 4 - 0
pages/ucenter/feedback/feedback.json

@@ -0,0 +1,4 @@
+{
+  "enablePullDownRefresh": false,
+  "navigationBarTitleText": "意见反馈"
+}

+ 29 - 0
pages/ucenter/feedback/feedback.wxml

@@ -0,0 +1,29 @@
+<view class="container">
+  <auth bindlogin="onShow" />
+  <lip-sync />
+
+  <picker bindchange="bindPickerChange" value="{{index}}" range="{{array}}">
+    <view class="picker">
+      <view class="fb-type">
+        <view class="type-label">{{array[index]}}</view>
+        <image class="type-icon" src="http://yanxuan.nosdn.127.net/hxm/yanxuan-wap/p/20161201/style/img/icon-normal/pickerArrow-a8b918f05f.png"></image>
+
+      </view>
+    </view>
+  </picker>
+  <view class="fb-body">
+    <textarea class="content" placeholder="对我们的网站、商品、服务,您还有什么建议吗?请告诉我们。"  bindinput ="contentInput" maxlength="500" auto-focus="true" value="{{content}}"/>
+    <view class="text-count">{{contentLength}}/500</view>
+  </view>
+  <view class="fb-mobile">
+    <view class="label">手机号码</view>
+    <view class="mobile-box">
+      <input class="mobile" maxlength="11" type="number" placeholder="方便我们与您联系" bindinput ="mobileInput" value="{{mobile}}"/>
+      <!--
+      <image class="clear-icon" src="https://platform-wxmall.oss-cn-beijing.aliyuncs.com/upload/20180727/150647657fcdd0.png" bindtap="cleanMobile"></image>
+      -->
+    </view>
+  </view>
+
+  <view class="fb-btn" bindtap="sbmitFeedback">提交</view>
+</view>

+ 121 - 0
pages/ucenter/feedback/feedback.wxss

@@ -0,0 +1,121 @@
+page{
+    background: #f4f4f4;
+}
+
+.container{
+    background: #f4f4f4;
+    padding-top: 30rpx;
+    width: 100%;
+    overflow: hidden;
+
+}
+
+.fb-type{
+  height: 104rpx;
+  width: 100%;
+  background: #fff;
+  margin-bottom: 20rpx;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding-left: 30rpx;
+  padding-right: 30rpx;
+}
+
+.fb-type .type-label{
+  height: 36rpx;
+  flex: 1;
+  color: #333;
+  font-size: 28rpx;
+}
+
+.fb-type .type-icon{
+  height: 36rpx;
+  width: 36rpx;
+}
+
+.fb-body{
+  width: 100%;
+  background: #fff;
+  height: 374rpx;
+  padding: 18rpx 30rpx 64rpx 30rpx;
+}
+
+.fb-body .content{
+  width: 100%;
+  height: 100%;
+  color: #333;
+  line-height: 40rpx;
+  font-size: 28rpx;
+}
+
+.fb-body .text-count{
+  padding-top: 17rpx;
+  line-height: 30rpx;
+  float: right;
+  color: #666;
+  font-size: 24rpx;
+}
+
+.fb-mobile{
+  height: 162rpx;
+  width: 100%;
+}
+
+.fb-mobile .label{
+  height: 58rpx;
+  width: 100%;
+  padding-top: 14rpx;
+  padding-bottom: 11rpx;
+  color: #7f7f7f;
+  font-size: 24rpx;
+  padding-left: 30rpx;
+  padding-right: 30rpx;
+  line-height: 33rpx;
+}
+
+.fb-mobile .mobile-box{
+  height: 104rpx;
+  width: 100%;
+  color: #333;
+  padding-left: 30rpx;
+  padding-right: 30rpx;
+  font-size: 24rpx;
+  background: #fff;
+  position: relative;
+}
+
+.fb-mobile .mobile{
+  position: absolute;
+  top: 27rpx;
+  left: 30rpx;
+  height: 50rpx;
+  width: 100%;
+  color: #333;
+  line-height: 50rpx;
+  font-size: 24rpx;
+}
+
+.clear-icon{
+  position: absolute;
+  top: 43rpx;
+  right: 30rpx;
+  width: 28rpx;
+  height: 28rpx;
+}
+
+.fb-btn{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 98rpx;
+  line-height: 98rpx;
+  background: #ED5D18;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  border-radius: 0;
+  color: #fff;
+  font-size: 28rpx;
+}

+ 0 - 0
pages/ucenter/footprint/footprint.js


Vissa filer visades inte eftersom för många filer har ändrats