dwh 2 years ago
commit
a9872430d7
100 changed files with 18509 additions and 0 deletions
  1. 23 0
      .gitignore
  2. 16 0
      .hbuilderx/launch.json
  3. 129 0
      App.vue
  4. 480 0
      components/DragImage/DragImage.vue
  5. 333 0
      components/Popup/PayPopup.vue
  6. 107 0
      components/Popup/Popup.vue
  7. 156 0
      components/Popup/VipPopup.vue
  8. 84 0
      components/Status/Status.vue
  9. 109 0
      components/TabBar/TabBar.vue
  10. 77 0
      components/TopBar/TopBar.vue
  11. 68 0
      components/base/common.js
  12. 146 0
      components/base/emojiMap.js
  13. 232 0
      components/base/message-facade.js
  14. 763 0
      components/invinbg-image-cropper/invinbg-image-cropper.vue
  15. 33 0
      components/tui-chat/message-elements/audio-message/index.css
  16. 69 0
      components/tui-chat/message-elements/audio-message/index.vue
  17. 88 0
      components/tui-chat/message-elements/custom-message/index.css
  18. 178 0
      components/tui-chat/message-elements/custom-message/index.vue
  19. 18 0
      components/tui-chat/message-elements/emoji/index.css
  20. 48 0
      components/tui-chat/message-elements/emoji/index.vue
  21. 13 0
      components/tui-chat/message-elements/face-message/index.css
  22. 58 0
      components/tui-chat/message-elements/face-message/index.vue
  23. 65 0
      components/tui-chat/message-elements/file-message/index.css
  24. 86 0
      components/tui-chat/message-elements/file-message/index.vue
  25. 13 0
      components/tui-chat/message-elements/image-message/index.css
  26. 52 0
      components/tui-chat/message-elements/image-message/index.vue
  27. 29 0
      components/tui-chat/message-elements/system-message/index.css
  28. 88 0
      components/tui-chat/message-elements/system-message/index.vue
  29. 46 0
      components/tui-chat/message-elements/text-message/index.css
  30. 56 0
      components/tui-chat/message-elements/text-message/index.vue
  31. 9 0
      components/tui-chat/message-elements/tip-message/index.css
  32. 47 0
      components/tui-chat/message-elements/tip-message/index.vue
  33. 38 0
      components/tui-chat/message-elements/video-message/index.css
  34. 58 0
      components/tui-chat/message-elements/video-message/index.vue
  35. 68 0
      components/tui-chat/message-list/index.css
  36. 252 0
      components/tui-chat/message-list/index.vue
  37. 86 0
      components/tui-chat/message-private/common-words/index.css
  38. 92 0
      components/tui-chat/message-private/common-words/index.vue
  39. 178 0
      components/tui-chat/message-private/order-list/index.css
  40. 141 0
      components/tui-chat/message-private/order-list/index.vue
  41. 93 0
      components/tui-chat/message-private/service-evaluation/index.css
  42. 120 0
      components/tui-chat/message-private/service-evaluation/index.vue
  43. 92 0
      components/tui-conversation/conversation-item/index.css
  44. 152 0
      components/tui-conversation/conversation-item/index.vue
  45. 363 0
      components/tui-group/group-profile/index.css
  46. 277 0
      components/tui-group/group-profile/index.vue
  47. 21 0
      index.html
  48. 31 0
      main.js
  49. 105 0
      manifest.json
  50. 201 0
      package-lock.json
  51. 16 0
      package.json
  52. 294 0
      pages.json
  53. 1346 0
      pages/friends/friends.vue
  54. 947 0
      pages/friends/user.vue
  55. 274 0
      pages/info/city.vue
  56. 248 0
      pages/info/datum.vue
  57. 767 0
      pages/info/editCenter.vue
  58. 396 0
      pages/info/figure.vue
  59. 241 0
      pages/info/labels.vue
  60. 138 0
      pages/info/sex.vue
  61. 215 0
      pages/info/wechat.vue
  62. 228 0
      pages/login/login.vue
  63. 211 0
      pages/login/loginByCode.vue
  64. 152 0
      pages/login/loginByPassword.vue
  65. 137 0
      pages/login/loginByPhone.vue
  66. 477 0
      pages/messages/messages.vue
  67. 586 0
      pages/mine/album.vue
  68. 382 0
      pages/mine/guest.vue
  69. 1004 0
      pages/mine/mine.vue
  70. 414 0
      pages/search/search.vue
  71. 494 0
      pages/vip/vip.vue
  72. 36 0
      pages/wallet/wallet.vue
  73. 22 0
      pages/webview/webview.vue
  74. 533 0
      pagesSub/chatting/chatting.vue
  75. 217 0
      pagesSub/faceVideo/faceVideo.vue
  76. 144 0
      pagesSub/setting/setting.vue
  77. 763 0
      pagesSub/talk/talk.vue
  78. BIN
      static/back.png
  79. BIN
      static/input-clear.png
  80. 1122 0
      static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.js
  81. 0 0
      static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js
  82. BIN
      static/tabbar/friends-off.png
  83. BIN
      static/tabbar/friends-on.png
  84. BIN
      static/tabbar/home-off.png
  85. BIN
      static/tabbar/home-on.png
  86. BIN
      static/tabbar/message-off.png
  87. BIN
      static/tabbar/message-on.png
  88. BIN
      static/tabbar/mine-off.png
  89. BIN
      static/tabbar/mine-on.png
  90. BIN
      static/tabbar/tabbar-add.png
  91. BIN
      static/video-play.png
  92. 69 0
      store/index.js
  93. 50 0
      uni.scss
  94. 18 0
      uni_modules/buuug7-img-cropper/changelog.md
  95. 9 0
      uni_modules/buuug7-img-cropper/components/buuug7-img-cropper/buuug7-img-cropper.vue
  96. 8 0
      uni_modules/buuug7-img-cropper/hybrid/html/cropper/cropper.min.css
  97. 9 0
      uni_modules/buuug7-img-cropper/hybrid/html/cropper/cropper.min.js
  98. 39 0
      uni_modules/buuug7-img-cropper/hybrid/html/cropper/index.html
  99. 267 0
      uni_modules/buuug7-img-cropper/hybrid/html/cropper/index.js
  100. 149 0
      uni_modules/buuug7-img-cropper/hybrid/html/cropper/style.css

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"mp-weixin" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 129 - 0
App.vue

@@ -0,0 +1,129 @@
+<script>
+	
+	export default {
+		onLaunch: function() {
+			const that=this;
+			// #ifdef MP
+			let sysInfo=uni.getSystemInfoSync(),menuInfo=uni.getMenuButtonBoundingClientRect();
+			let offsetBottom=menuInfo.top-sysInfo.statusBarHeight;
+			
+			if(sysInfo.system.indexOf('iOS')!==-1){
+				this.$store.commit('setPlatform','ios');
+			}
+			else if(sysInfo.system.indexOf('Android')!==-1){
+				this.$store.commit('setPlatform','android');
+			}
+			else{
+				this.$store.commit('setPlatform','other');
+			}
+			this.$store.commit('setStatusBarHeight',sysInfo.statusBarHeight);
+			this.$store.commit('setTopbarOffsetHeight',sysInfo.statusBarHeight + offsetBottom*2 + menuInfo.height);
+			this.$store.commit('setTabBarHeight',(this.$util.hasSafeArea()?68:20)+106);
+			
+			return;//单元测试开关
+			if(uni.getStorageSync('autoLogin')==='false'){
+				return;
+			}
+			uni.login({//免密登录
+				success:(res)=>{
+					that.$api.login.wxLogin({
+						scene: "WxMPLogin",
+						miniProgramEncryUserInfoParam:{
+							jsCode:res.code
+						}
+					}).then(result=>{
+						if(result.status==='Succ'){
+							if(!result.data.succ){
+								if(result.data.wxLoginInfo){//新用户保存用户openid,unionId
+									uni.setStorageSync('openId',result.data.wxLoginInfo.openId);
+									uni.setStorageSync('unionId',result.data.wxLoginInfo.unionId);
+								}
+							}
+							else{
+								uni.setStorageSync('LL_Ukn',result.data.userToken.ukn);
+								uni.setStorageSync('token',result.data.userToken.token);
+								uni.setStorageSync('userInfo',JSON.stringify(result.data));
+								uni.setStorageSync('user',JSON.stringify(result.data.userToken.user));
+								this.$api.public.aliossCdn({}).then(cdnRes=>{
+									this.$store.commit('setImageCdn',cdnRes.data.pictureCdn);
+									this.$store.commit('setVideoCdn',cdnRes.data.videoCdn);
+								})
+								this.$api.public.mineDetail({
+									getAlbum:true,
+									completeUser:result.data.userToken.user
+								}).then(res=>{
+									this.$store.commit('setUserInfo',res.data);
+									uni.setStorageSync('userInfo',JSON.stringify(res.data));
+									
+									if(result.data.isNew){
+										uni.reLaunch({
+											url:'/pages/info/sex'
+										})
+										return;
+									}
+									let step=result.data.regStepNew||result.data.regStep;
+									switch(step){
+										case 'Sex':
+											uni.reLaunch({
+												url:'/pages/info/sex'
+											})
+										break;
+										case 'SocialImage':
+											uni.reLaunch({
+												url:'/pages/info/figure'
+											})
+										break;
+										case 'SocialData':
+											uni.reLaunch({
+												url:'/pages/info/datum'
+											})
+										break;
+										case 'CityStay':
+											uni.reLaunch({
+												url:'/pages/info/city'
+											})
+										break;
+										case 'WxInfo':
+											uni.reLaunch({
+												url:'/pages/info/wechat'
+											})
+										break;
+										case 'Basic':
+										case 'Index':
+											uni.reLaunch({
+												url:'/pages/friends/friends'
+											})
+										break;
+									}
+									uni.reLaunch({
+										url:'/pages/friends/friends'
+									})
+								})
+								
+							}
+						}
+						
+						
+					})
+				},
+				fail: (err) => {
+					console.log(err)
+				}
+			});
+			// #endif
+		},
+		onShow: function() {
+			console.log('App Show')
+			
+		},
+		onHide: function() {
+			console.log('App Hide')
+		},
+		onUnload() {
+			console.log('im logout')
+			uni.$TUIKit.logout();
+			uni.$TUIKit.destroy();
+		},
+		
+	}
+</script>

+ 480 - 0
components/DragImage/DragImage.vue

@@ -0,0 +1,480 @@
+<template>
+  <view class="con">
+    <movable-area class="area" :style="{ height: areaHeight }" @mouseenter="mouseenter" @mouseleave="mouseleave">
+      <block v-for="(item, index) in imageList" :key="item.id">
+        <movable-view
+          class="view"
+          :x="item.x"
+          :y="item.y"
+          direction="all"
+          :damping="40"
+          :disabled="item.disable"
+		  @click="previewImage(index)"
+          @change="onChange($event, item)"
+          @longpress="touchstart(item)"
+          @touchend="touchend(item)"
+          :style="{ width: viewWidth + 'px', height: viewWidth + 'px', 'z-index': item.zIndex, opacity: item.opacity }"
+        >
+          <view class="area-con" :style="{ width: childWidth, height: childWidth, transform: 'scale(' + item.scale + ')' }">
+            <image class="pre-image" :src="item.src" mode="aspectFill"></image>
+            <view class="del-con" @click="delImage(item, index)" @touchstart.stop="delImageMp(item, index)" @touchend.stop="nothing()" @mousedown.stop="nothing()" @mouseup.stop="nothing()" v-if="!chooseImage">
+              <view class="del-wrap">
+                <image
+                  class="del-image"
+                  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAhdEVYdENyZWF0aW9uIFRpbWUAMjAyMDowNzoyNSAyMTo1NDoyOU4TkJAAAADcSURBVFhH7ZfRCoMwDEXLvkjwwVf/bH/emmAyN6glTW9WBjsgwm28OeCLpj81Sil7zvlJ90UiONS/yY5VogsO6XrBg3IEQ5a/s8vRSWUAKmLqp2w5jz5BiNQEGMo3GbloDLtFXJ1IkaEuhAiiY6gEIqB4yqACSk9piIBiKQ8VUFpLviKg3C2rESKgWERCBZSWiEfgIfffYvrrsAgoISJ3Apy3zuTxcSxLQkV6ykNEPKVQkZEyiAiiZKgDIaC4upACSlcn5fM/+WuDCAHF1E/Z/N9AhkMZnPNDPI+UDjPIXgAQIGjNAAAAAElFTkSuQmCC"
+                ></image>
+              </view>
+            </view>
+			<view class="choose" v-if="item.choosed">
+				{{item.chooseIndex}}
+			</view>
+          </view>
+        </movable-view>
+      </block>
+      <view
+        class="add"
+        v-if="imageList.length < number"
+        :style="{ top: add.y, left: add.x, width: viewWidth + 'px', height: viewWidth + 'px' }"
+        @click="addImages"
+      >
+        <view class="add-wrap" :style="{ width: childWidth, height: childWidth }">
+          <image
+            style="width: 216rpx;height: 216rpx;"
+           :src="`${assetsUrl}album-add.png`" mode="aspectFit"></image>
+        </view>
+      </view>
+    </movable-area>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+	  assetsUrl:this.$util.assetsUrl,
+      imageList: [],
+      width: 0,
+      add: {
+        x: 0,
+        y: 0
+      },
+      colsValue: 0,
+      viewWidth: 0,
+      tempItem: null,
+      timer: null,
+      changeStatus: true,
+      preStatus: true,
+    }
+  },
+  props: {
+    // 返回排序后图片
+    list: {
+      type: Array,
+      default: function() {
+        return []
+      }
+    },
+    // 选择图片数量限制
+    number: {
+      type: Number,
+      default: 6
+    },
+    // 图片父容器宽度(实际显示的图片宽度为 imageWidth / 1.1 ),单位 rpx
+    imageWidth: {
+      type: Number,
+      default: 230
+    },
+    // 图片列数(cols > 0 则 imageWidth 无效)
+    cols: {
+      type: Number,
+      default: 0
+    },
+    // 图片周围空白填充,单位 rpx
+    padding: {
+      type: Number,
+      default: 10
+    },
+    // 拖动图片时放大倍数 [0, ∞)
+    scale: {
+      type: Number,
+      default: 1.1
+    },
+    // 拖动图片时不透明度
+    opacity: {
+      type: Number,
+      default: 0.7
+    },
+    // 自定义添加(需配合 @aaddImage 事件使用)
+    custom: {
+      type: Boolean,
+      default: false
+    },
+	chooseImage:{
+		type:Boolean,
+		default:false
+	}
+  },
+  watch:{
+	  list(val,oldval){
+		  this.init();
+	  }
+  },
+  computed: {
+    areaHeight() {
+      if (this.imageList.length < this.number) {
+        return Math.ceil((this.imageList.length + 1) / this.colsValue) * this.viewWidth + 'px'
+      } else {
+        return Math.ceil(this.imageList.length / this.colsValue) * this.viewWidth + 'px'
+      }
+    },
+    childWidth() {
+      return this.viewWidth - this.rpx2px(this.padding) * 2 + 'px'
+    },
+  },
+  created() {
+    this.width = uni.getSystemInfoSync().windowWidth
+    this.viewWidth = this.rpx2px(this.imageWidth)
+  },
+  mounted() {
+    this.init();
+  },
+  methods: {
+	init(){
+		const query = uni.createSelectorQuery().in(this)
+		query.select('.area').boundingClientRect(data => {
+		  this.colsValue = Math.floor(data.width / this.viewWidth)
+		  if(this.cols > 0){
+		    this.colsValue = this.cols
+		    this.viewWidth = data.width / this.cols
+		  }
+		  this.imageList=[];
+		  for (let item of this.list) {
+		    this.addProperties(item)
+		  }
+		})
+		query.exec()
+	},
+    onChange(e, item) {
+      if(!item) return
+      item.oldX = e.detail.x
+      item.oldY = e.detail.y
+      if (e.detail.source === 'touch') {
+        if(item.moveEnd){
+          item.offset = Math.sqrt(Math.pow(item.oldX - item.absX * this.viewWidth, 2) + Math.pow(item.oldY - item.absY * this.viewWidth, 2))
+        }
+        let x = Math.floor((e.detail.x + this.viewWidth / 2) / this.viewWidth)
+        if(x >= this.colsValue) return
+        let y = Math.floor((e.detail.y + this.viewWidth / 2) / this.viewWidth)
+        let index = this.colsValue * y + x
+        if (item.index != index && index < this.imageList.length) {
+          this.changeStatus = false
+          for (let obj of this.imageList) {
+            if (item.index > index && obj.index >= index && obj.index < item.index) {
+              this.change(obj, 1)
+            } else if (item.index < index && obj.index <= index && obj.index > item.index) {
+              this.change(obj, -1)
+            } else if(obj.id != item.id) {
+              obj.offset = 0
+              obj.x = obj.oldX
+              obj.y = obj.oldY
+              setTimeout(() => {
+                this.$nextTick(() => {
+                  obj.x = obj.absX * this.viewWidth
+                  obj.y = obj.absY * this.viewWidth
+                })
+              }, 0)
+            }
+          }
+          item.index = index
+          item.absX = x
+          item.absY = y
+          
+        }
+      }
+    },
+    change(obj, i){
+      obj.index += i
+      obj.offset = 0
+      obj.x = obj.oldX
+      obj.y = obj.oldY
+      obj.absX = obj.index % this.colsValue
+      obj.absY = Math.floor(obj.index / this.colsValue)
+      setTimeout(() => {
+        this.$nextTick(() => {
+          obj.x = obj.absX * this.viewWidth
+          obj.y = obj.absY * this.viewWidth
+        })
+      }, 0)
+    },
+    touchstart(item) {
+	  uni.vibrateShort();
+	  item.disable=false;
+      this.imageList.forEach(v => {
+        v.zIndex = v.index + 9
+      })
+      item.zIndex = 99;
+      item.moveEnd = true;
+      this.tempItem = item
+      this.timer = setTimeout(() => {
+        item.scale = this.scale
+        item.opacity = this.opacity
+        clearTimeout(this.timer)
+        this.timer = null
+      }, 200)
+    },
+    touchend(item) {
+      this.previewImage(item)
+      item.scale = 1
+      item.opacity = 1
+      item.x = item.oldX
+      item.y = item.oldY
+      item.offset = 0
+      item.moveEnd = false;
+	  item.disable=true;
+      setTimeout(() => {
+        this.$nextTick(() => {
+          item.x = item.absX * this.viewWidth
+          item.y = item.absY * this.viewWidth
+          this.tempItem = null
+          this.changeStatus = true
+        })
+      }, 0)
+	  this.sortList()
+    },
+    previewImage(index){
+	    if(typeof index==='object'){return;}
+		if(this.chooseImage){
+			this.imageList[index].choosed=this.imageList[index].choosed?false:true
+			let num=1,arr=[];
+			for(let i=0;i<this.imageList.length;i++){
+				if(this.imageList[i].choosed){
+					this.imageList[i].chooseIndex=num;
+					num++;
+					arr.push(this.imageList[i].mediaId)
+				}
+			}
+			this.$emit('chooseEvent',arr)
+			return;
+		}
+        let arr=[],obj={};
+        for(let i=0;i<this.imageList.length;i++){
+			obj={url:'',type:''};
+			if(this.imageList[i].cate==='Img'){
+				obj.type='image';
+			}
+			if(this.imageList[i].cate==='Vdo'){
+				obj.type='video';
+			}
+			obj.url=this.imageList[i].src;
+			arr.push(obj)
+		}
+      uni.previewMedia({
+      	sources:arr,
+      	current:index,
+      	showmenu:true,
+      	success: (res) => {
+      	},
+      	fail: (err) => {
+      		console.log(err);
+      	}
+      })
+    },
+    mouseenter(){
+      //#ifdef H5
+      this.imageList.forEach(v => {
+        v.disable = false
+      })
+      //#endif
+      
+    },
+    mouseleave(){
+      //#ifdef H5
+      if(this.tempItem){
+        this.imageList.forEach(v => {
+          v.disable = true
+          v.zIndex = v.index + 9
+          v.offset = 0
+          v.moveEnd = false
+          if(v.id == this.tempItem.id){
+            if (this.timer){
+              clearTimeout(this.timer)
+              this.timer = null
+            }
+            v.scale = 1
+            v.opacity = 1
+            v.x = v.oldX
+            v.y = v.oldY
+            this.$nextTick(() => {
+              v.x = v.absX * this.viewWidth
+              v.y = v.absY * this.viewWidth
+              this.tempItem = null
+            })
+          }
+        })
+        this.changeStatus = true
+      }
+      //#endif
+    },
+    addImages() {
+      if(this.custom){
+        this.$emit('addImage')
+      } else {
+        let checkNumber = this.number - this.imageList.length
+        uni.chooseImage({
+          count: checkNumber,
+          sourceType: ['album', 'camera'],
+          success: res => {
+            let count = checkNumber <= res.tempFilePaths.length ? checkNumber : res.tempFilePaths.length
+            for (let i = 0; i < count; i++) {
+              this.addProperties(res.tempFilePaths[i])
+            }
+          }
+        })
+      }
+    },
+    addImage(image){
+		this.addProperties(image);
+
+    },
+    delImage(item, index){
+      this.imageList.splice(index, 1)
+      for (let obj of this.imageList) {
+        if (obj.index > item.index) {
+          obj.index -= 1
+          obj.x = obj.oldX
+          obj.y = obj.oldY
+          obj.absX = obj.index % this.colsValue
+          obj.absY = Math.floor(obj.index / this.colsValue)
+          this.$nextTick(() => {
+            obj.x = obj.absX * this.viewWidth
+            obj.y = obj.absY * this.viewWidth
+          })
+        }
+      }
+      this.add.x = (this.imageList.length % this.colsValue) * this.viewWidth + 'px'
+      this.add.y = Math.floor(this.imageList.length / this.colsValue) * this.viewWidth + 'px'
+      this.sortList();
+	  this.$emit('delImage',index)
+    },
+    delImageMp(item, index){
+      //#ifdef MP
+      this.delImage(item, index)
+      //#endif
+    },
+    sortList() {
+      let list=this.imageList.slice();
+      list.sort((a, b) => {
+        return a.index - b.index
+      })
+      for (let i = 0; i < list.length; i++) {
+        list[i] = list[i].mediaId;
+      }
+      this.$emit('sortImage', list)
+    },
+    addProperties(item){
+      let absX = this.imageList.length % this.colsValue
+      let absY = Math.floor(this.imageList.length / this.colsValue)
+      let x = absX * this.viewWidth
+      let y = absY * this.viewWidth
+      this.imageList.push({
+        src: item.urlThumbnail,
+		mediaId: item.mediaId,
+		cate:item.cate,
+        x,
+        y,
+        oldX: x,
+        oldY: y,
+        absX,
+        absY,
+        scale: 1,
+        zIndex: 9,
+        opacity: 1,
+        index: this.imageList.length,
+        id: this.guid(),
+        disable: true,
+        offset: 0,
+        moveEnd: false,
+		choosed:false
+      })
+      this.add.x = (this.imageList.length % this.colsValue) * this.viewWidth + 'px'
+      this.add.y = Math.floor(this.imageList.length / this.colsValue) * this.viewWidth + 'px'
+    },
+    nothing(){},
+    rpx2px(v){
+      return this.width * v / 750
+    },
+    guid() {
+    	function S4() {
+    		return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+    	}
+    	return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .area {
+    width: 100%;
+    .view {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      .area-con {
+        position: relative;
+        .pre-image {
+          width: 100%;
+          height: 100%;
+		  border-radius: 8rpx;
+        }
+        .del-con {
+          position: absolute;
+          top: 0rpx;
+          right: 0rpx;
+          padding: 0 0 20rpx 20rpx;
+          .del-wrap {
+            width: 36rpx;
+            height: 36rpx;
+            background-color: rgba(0, 0, 0, 0.4);
+            border-radius: 0 0 0 10rpx;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            .del-image {
+              width: 20rpx;
+              height: 20rpx;
+            }
+          }
+        }
+		.choose{
+			width: 40rpx;
+			height: 40rpx;
+			position: absolute;
+			right: 8rpx;
+			top: 8rpx;
+			border-radius: 40rpx;
+			border: 2rpx solid #FFFFFF;
+			background: #FE3B49;
+			color: #FFFFFF;
+			text-align: center;
+			font-size: 24rpx;
+			line-height: 40rpx;
+		}
+      }
+    }
+    .add {
+      position: absolute;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      .add-wrap{
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        background-color: #151126;
+      }
+    }
+  }
+}
+</style>

+ 333 - 0
components/Popup/PayPopup.vue

@@ -0,0 +1,333 @@
+<template>
+	<view class="container flex-center">
+		<view class="popup flex-center">
+			<image :src="`${assetsUrl}popup-close-white.png`"  mode="aspectFill" class="close" @click="close"></image>
+			<view class="top flex-start">
+				<image :src="`${assetsUrl}popup-vip.png`" mode="aspectFill" class="top-icon"></image>
+				<view class="top-text font40 fw600">
+					4项特权
+				</view>
+			</view>
+			<swiper :indicator-dots="true" indicator-color="rgba(255,255,255,0.3)" indicator-active-color="#ffffff" :autoplay="true" :interval="5000" :duration="1000" class="swiper" :current="privilegeIndex">
+				<swiper-item v-for="(item,index) in privileges" :key="index">
+					<view class="swiper-item flex-center">
+						<view class="si-content">
+							<image :src="`${assetsUrl}${item.img}.png`" mode="aspectFill" class="si-img"></image>
+							<view class="si-title font36 fw600">
+								{{item.title}}
+							</view>
+							<view class="si-text font28 fw400">
+								{{item.text}}
+							</view>
+						</view>
+					</view>
+				</swiper-item>
+			</swiper>
+			<scroll-view scroll-x="true" class="tabs flex-start">
+				<view class="tab" :class="tabIndex===index?'active-tab':'no-active'" v-for="(item,index) in priceDatas.prices2" :key="index" @click="tabClick(index)">
+					<view class="tab-time font28 fw600" :style="{'color':`${tabIndex===index?'#FFDDBD':''}`}">
+						{{item.name}}
+					</view>
+					<view class="tab-price" :style="{'color':`${tabIndex===index?'#FFDDBD':''}`}">
+						<span style="font-size: 72rpx;" >{{item.priceFenPerMonth/100}}</span><span class="font22 fw500">元/月</span>
+					</view>
+					<view class="tab-under font24 fw400" :style="{'color':`${tabIndex===index?'#FFDDBD':''}`}">
+						{{item.originalPriceFenPerMonth/100}}元/月
+					</view>
+					<view class="tab-tag font20 fw600" v-if="tabIndex===index">
+						{{item.tags}}
+					</view>
+				</view>
+			</scroll-view>
+			<view class="btn font32 fw600" @click="toPay">
+				{{platForm==='ios'?'联系客服':'立即解锁'}}
+			</view>
+			<view class="tip font22 fw400" v-if="platForm==='ios'">
+				成为会员即代表同意<text style="color:#ffffff">《增值服务协议》</text></br>
+				iOS暂不支持微信支付,请点击<text style="color:#ffffff">联系客服</text>并回复“充值”。
+			</view>
+			<view class="tip font22 fw400" v-else>
+				成为会员即代表同意<text style="color:#ffffff">《增值服务协议》</text>
+				,如果支付成功,但是没有开通VIP,请<text style="color:#ffffff">联系客服</text>。
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name:"VipPopup",
+		props:{
+			swiperIndex:{
+				type:Number,
+				default:''
+			}
+		},
+		data() {
+			return {
+				assetsUrl: this.$util.assetsUrl,
+				privileges:[
+					{img:'popup-talk',title:'随心畅聊',text:'10次/天主动私聊女生'},
+					{img:'popup-recommend',title:'特别推荐',text:'尊贵标识,优先推荐'},
+					{img:'popup-limit',title:'颜遇无限',text:'无限次数查看女士主页和相册'},
+					{img:'popup-visitor',title:'查看访客',text:'解锁女生来访痕迹'},
+				],
+				privilegeIndex:0,
+				tabIndex:0,
+				priceDatas:{},
+				totalPrice:0,
+				priceConfig:{
+					feeFen: null,
+					sceneId: null,
+					scene: null,
+					body : null,
+					userId: null,
+					pkgCate:'JyPark'
+				},
+				
+			};
+		},
+		watch:{
+			'swiperIndex':function(val,oldval){
+				console.log(val)
+				this.privilegeIndex=val;
+			}
+		},
+		computed:{
+			platForm(){
+				return this.$store.state.platform
+			}
+		},
+		mounted() {
+			if(!uni.getStorageSync('token')){
+				return;
+			}
+			this.getConfigData();
+		},
+		methods:{
+			close(){
+				this.$emit('closePopup');
+			},
+			getConfigData(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.priceBySceneConfigs({
+					completeUser:user,
+					scene:'Member'
+				}).then(res=>{
+					this.priceDatas=res.data;
+					this.totalPrice=this.priceDatas.prices2[this.tabIndex].priceFen;
+					this.priceConfig.feeFen=this.priceDatas.prices2[this.tabIndex].priceFen;
+					this.priceConfig.scene=this.priceDatas.prices2[this.tabIndex].sceneCate;
+					this.priceConfig.sceneId=this.priceDatas.prices2[this.tabIndex].sceneId;
+					this.priceConfig.body=this.priceDatas.prices2[this.tabIndex].name;
+					this.priceConfig.userId=(this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))).id;
+				})
+			},
+			tabClick(index){
+				this.tabIndex=index;
+				this.totalPrice=this.priceDatas.prices2[index].priceFen;
+				this.priceConfig.feeFen=this.priceDatas.prices2[index].priceFen;
+				this.priceConfig.scene=this.priceDatas.prices2[index].sceneCate;
+				this.priceConfig.sceneId=this.priceDatas.prices2[index].sceneId;
+				this.priceConfig.body=this.priceDatas.prices2[index].name;
+				this.priceConfig.userId=(this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))).id;
+			},
+			toService(){
+				uni.openCustomerServiceChat({
+				     extInfo:{
+				         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+				     },
+				     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+				     fail(res){
+						 console.log(res)
+				         uni.showToast({
+				           title: '客服联系失败',
+				           icon:'none'
+				         })
+				     }
+				 })
+				this.$emit('closePopup');
+				
+			},
+			toPay(){
+				if(this.platForm==='ios'){
+					this.$emit('closePopup');
+					uni.openCustomerServiceChat({
+					     extInfo:{
+					         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+					     },
+					     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+					     fail(res){
+							 console.log(res)
+					         wx.showToast({
+					           title: '客服联系失败',
+					           icon:'none'
+					         })
+					     }
+					 })
+					 return;
+				}
+				uni.getProvider({
+					service:'payment',
+					success:(provider)=>{
+						console.log(provider.provider)
+						this.$api.pay.creatWxOrder(this.priceConfig).then(res=>{
+							if(res.data.succ){
+								uni.requestPayment({
+									provider:provider.provider[0],
+									timeStamp: String(res.data.timeStamp),
+								    nonceStr: res.data.nonceStr,
+								    package: `prepay_id=${res.data.wxUniPrePayId}`,
+								    signType: 'MD5',
+								    paySign: res.data.paySign,
+								    success:(result)=>{
+										console.log(result)
+										if(result.errMsg==='requestPayment:ok'){
+											uni.showToast({
+												icon:'success',
+												title:'支付成功'
+											})
+											this.$emit('closePopup');
+										}
+									},
+								    fail:(err)=>{console.log(err)}
+								})
+							}
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	flex-direction: column;
+	position: relative;
+	z-index: 999;
+	.popup{
+		width: 100vw;
+		background-color: #6C52F4;
+		position: relative;
+		flex-direction: column;
+		border-radius: 40rpx 40rpx 0rpx 0rpx;
+		
+		.close{
+			position: absolute;
+			right: 20rpx;
+			top: 20rpx;
+			width: 32rpx;
+			height: 32rpx;
+		}
+		.top{
+			height: 132rpx;
+			width: 630rpx;
+			padding: 40rpx 40rpx 0rpx 0rpx;
+			box-sizing: border-box;
+			.top-icon{
+				width: 160rpx;
+				height: 60rpx;
+			}
+			.top-text{
+				color: #ffffff;
+				margin-left: 8rpx;
+			}
+		}
+		.swiper{
+			width: 100%;
+			height: 430rpx;
+			.swiper-item{
+				width: 100%;
+				flex-direction: column;
+				.si-img{
+					width: 630rpx;
+					height: 256rpx;
+				}
+				.si-title{
+					color: #ffffff;
+					text-align: center;
+				}
+				.si-text{
+					color: #ffffff;
+					text-align: center;
+					margin-top: 16rpx;
+				}
+				
+			}
+		}
+		.tabs{
+			height: 340rpx;
+			white-space:nowrap;
+			.tab{
+				position: relative;
+				margin-right: 12rpx;
+				
+				border-radius: 40rpx;
+				display: inline-block;
+				transition: all .1s;
+				overflow: hidden;
+				&:nth-of-type(1){
+					margin-left: 32rpx;
+				}
+				&:nth-last-of-type(1){
+					margin-right: 32rpx;
+				}
+				.tab-time{
+					color: #79726B;
+				}
+				.tab-price{
+					color: #79726B;
+					margin-top: 24rpx;
+				}
+				.tab-under{
+					color: #79726B;
+					text-decoration: line-through;
+					margin-top: 24rpx;
+				}
+				.tab-tag{
+					color: #79726B;
+					background: #FFDDBD;
+					border-radius: 0rpx 40rpx 0px 16rpx;
+					width: 88rpx;
+					height: 36rpx;
+					line-height: 36rpx;
+					text-align: center;
+					position: absolute;
+					right: -1rpx;
+					top: -1rpx;
+				}
+			}
+			.no-active{
+				border: 1rpx solid #79726B;
+				padding: 40rpx 24rpx;
+				width: 188rpx;
+				background-color: #0D0B0A;
+			}
+			.active-tab{
+				padding: 40rpx 24rpx;
+				width: 182rpx;
+				border: 4rpx solid #FFDDBD !important;
+				background-color: #221F1C !important;
+			}
+		}
+		.btn{
+			width: 630rpx;
+			height: 104rpx;
+			background: #FFFFFF;
+			border-radius: 56rpx;
+			color: #6C52F4;
+			line-height: 104rpx;
+			text-align: center;
+		}
+		.tip{
+			margin: 0 32rpx;
+			color: #D4D4F1;
+			line-height: 32rpx;
+			padding-bottom: 68rpx;
+			margin-top: 16rpx;
+			text-align: center;
+		}
+	}
+}
+</style>

+ 107 - 0
components/Popup/Popup.vue

@@ -0,0 +1,107 @@
+<template>
+	<view class="container flex-center">
+		<view class="popup flex-center">
+			<image :src="`${assetsUrl}popup-close.png`"  mode="aspectFill" class="close" @click="close"></image>
+			<image :src="`${assetsUrl}popup-auth.png`" mode="aspectFit" class="popop-img"></image>
+			<view class="content font28 fw400" v-if="content1">
+				{{content1}}
+			</view>
+			<view class="content font28 fw400" v-if="content2">
+				{{content2}}
+			</view>
+			<view class="tip" v-if="tip1">
+				{{tip1}}
+			</view>
+			<view class="tip" v-if="tip2">
+				{{tip2}}
+			</view>
+			<view class="btn  font28 fw600" @click="emitEvent">
+				{{btntext}}
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name:"Popup",
+		props:{
+			content1:{
+				type:String,
+				default:''
+			},
+			content2:{
+				type:String,
+				default:''
+			},
+			tip1:{
+				type:String,
+				default:''
+			},
+			tip2:{
+				type:String,
+				default:''
+			},
+			btntext:{
+				type:String,
+				default:''
+			},
+			btnEvent:{
+				type:String,
+				default:''
+			}
+		},
+		data() {
+			return {
+				assetsUrl: this.$util.assetsUrl,
+			};
+		},
+		methods:{
+			close(){
+				this.$emit('closePopup');
+			},
+			emitEvent(){
+				this.$emit(this.btnEvent);
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	flex-direction: column;
+	.popup{
+		width: 590rpx;
+		height: 668rpx;
+		background-color: #171325;
+		position: relative;
+		flex-direction: column;
+		border-radius: 40rpx;
+		.close{
+			position: absolute;
+			right: 20rpx;
+			top: 20rpx;
+			width: 32rpx;
+			height: 32rpx;
+		}
+		.popop-img{
+			width: 436rpx;
+			height: 312rpx;
+		}
+		.content{
+			margin-bottom: 16rpx;
+			color: #ffffff;
+		}
+		.btn{
+			width: 470rpx;
+			height: 84rpx;
+			background: #6C52F4;
+			border-radius: 42rpx;
+			line-height: 84rpx;
+			text-align: center;
+			color: #ffffff;
+			margin-top: 68rpx;
+		}
+	}
+}
+</style>

+ 156 - 0
components/Popup/VipPopup.vue

@@ -0,0 +1,156 @@
+<template>
+	<view class="container flex-center">
+		<view class="popup flex-center">
+			<image :src="`${assetsUrl}popup-close-white.png`"  mode="aspectFill" class="close" @click="close"></image>
+			<view class="top flex-start">
+				<image :src="`${assetsUrl}popup-vip.png`" mode="aspectFill" class="top-icon"></image>
+				<view class="top-text font40 fw600">
+					4项特权
+				</view>
+			</view>
+			<swiper :indicator-dots="true" indicator-color="rgba(255,255,255,0.3)" indicator-active-color="#ffffff" :autoplay="true" :interval="5000" :duration="1000" class="swiper" :current="privilegeIndex">
+				<swiper-item v-for="(item,index) in privileges" :key="index">
+					<view class="swiper-item flex-center">
+						<view class="si-content">
+							<image :src="`${assetsUrl}${item.img}.png`" mode="aspectFill" class="si-img"></image>
+							<view class="si-title font36 fw600">
+								{{item.title}}
+							</view>
+							<view class="si-text font28 fw400">
+								{{item.text}}
+							</view>
+						</view>
+					</view>
+				</swiper-item>
+			</swiper>
+			<view class="bottom flex-center">
+				<view class="b-text1 font28 fw400">
+					开通会员请
+				</view>
+				<view class="b-text1" style="text-decoration: underline;margin-left: 8rpx;" @click="toService">
+					联系客服
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name:"VipPopup",
+		props:{
+			swiperIndex:{
+				type:Number,
+				default:''
+			}
+		},
+		data() {
+			return {
+				assetsUrl: this.$util.assetsUrl,
+				privileges:[
+					{img:'popup-talk',title:'随心畅聊',text:'10次/天主动私聊女生'},
+					{img:'popup-recommend',title:'特别推荐',text:'尊贵标识,优先推荐'},
+					{img:'popup-limit',title:'颜遇无限',text:'无限次数查看女士主页和相册'},
+					{img:'popup-visitor',title:'查看访客',text:'解锁女生来访痕迹'},
+				],
+				privilegeIndex:-1
+			};
+		},
+		watch:{
+			'swiperIndex':function(val,oldval){
+				console.log(val)
+				this.privilegeIndex=val;
+			}
+		},
+		methods:{
+			close(){
+				this.$emit('closePopup');
+			},
+			toService(){
+				uni.openCustomerServiceChat({
+				     extInfo:{
+				         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+				     },
+				     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+				     fail(res){
+						 console.log(res)
+				         uni.showToast({
+				           title: '客服联系失败',
+				           icon:'none'
+				         })
+				     }
+				 })
+				this.$emit('closePopup');
+				
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	flex-direction: column;
+	.popup{
+		width: 630rpx;
+		height: 716rpx;
+		background-color: #6C52F4;
+		position: relative;
+		flex-direction: column;
+		border-radius: 40rpx;
+		
+		.close{
+			position: absolute;
+			right: 20rpx;
+			top: 20rpx;
+			width: 32rpx;
+			height: 32rpx;
+		}
+		.top{
+			height: 132rpx;
+			width: 630rpx;
+			padding: 40rpx 40rpx 0rpx 40rpx;
+			box-sizing: border-box;
+			.top-icon{
+				width: 160rpx;
+				height: 60rpx;
+			}
+			.top-text{
+				color: #ffffff;
+				margin-left: 8rpx;
+			}
+		}
+		.swiper{
+			flex: 1;
+			width: 630rpx;
+			.swiper-item{
+				width: 630rpx;
+				flex-direction: column;
+				.si-img{
+					width: 630rpx;
+					height: 256rpx;
+				}
+				.si-title{
+					color: #ffffff;
+					text-align: center;
+				}
+				.si-text{
+					color: #ffffff;
+					text-align: center;
+					margin-top: 16rpx;
+				}
+				
+			}
+		}
+		.bottom{
+			width: 100%;
+			height: 120rpx;
+			padding: 0rpx 40rpx 0rpx 40rpx;
+			box-sizing: border-box;
+			background-color: rgba(255, 255, 255, 0.08);
+			.b-text1{
+				color: #ffffff;
+			}
+		}
+	}
+}
+</style>

+ 84 - 0
components/Status/Status.vue

@@ -0,0 +1,84 @@
+<template>
+	<view class="container flex-center">
+		<view class="no-pos flex-center"  >
+			<image :src="`${assetsUrl}status-no-data.png`" mode="aspectFit" class="no-pos-img" v-if="type==='noData'"></image>
+			<image :src="`${assetsUrl}status-authing.png`" mode="aspectFit" class="no-pos-img" v-else-if="type==='authing'"></image>
+			<image :src="`${assetsUrl}status-deleted.png`" mode="aspectFit" class="no-pos-img" v-else-if="type==='deleted'"></image>
+			<image :src="`${assetsUrl}status-frozen.png`" mode="aspectFit" class="no-pos-img" v-else-if="type==='frozen'"></image>
+			<image :src="`${assetsUrl}status-no-msg.png`" mode="aspectFit" class="no-pos-img" v-else-if="type==='noMsg'"></image>
+			<image :src="`${assetsUrl}status-no-pos.png`" mode="aspectFit" class="no-pos-img" v-else-if="type==='noPos'"></image>
+			<image :src="`${assetsUrl}status-no-net.png`" mode="aspectFit" class="no-pos-img" v-else-if="type==='noNet'"></image>
+			<text class="no-pos-text font24 fw400" v-if="text">{{text}}</text>
+			<view class="no-pos-btn font24 fw400" @click="btnClick" v-if="btnText">
+				{{btnText}}
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * "noData":无数据,
+	 * "noNet":无网络
+	 * "authing":审核中
+	 * "deleted":已删除
+	 * "frozen":已冻结
+	 * "noMsg":无消息
+	 * "noPos":无定位
+	 */
+	export default {
+		name:"Status",
+		props:{
+			type:{
+				type:String,
+				default:"noData"
+			},
+			text:{
+				type:String,
+				default:""
+			},
+			btnText:{
+				type:String,
+				default:""
+			}
+		},
+		data() {
+			return {
+				assetsUrl: this.$util.assetsUrl,
+			};
+		},
+		methods:{
+			btnClick(){
+				this.$emit('btnEvent');
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100%;
+	height: 500rpx;
+	.no-pos{
+		flex-direction: column;
+		.no-pos-img{
+			width: 400rpx;
+			height: 400rpx;
+		}
+		.no-pos-text{
+			color: $fontcolor3;
+			margin-top: -30rpx;
+		}
+		.no-pos-btn{
+			width: 136rpx;
+			height: 48rpx;
+			background: $primary;
+			border-radius: 24rpx;
+			line-height: 48rpx;
+			text-align: center;
+			color: $fontcolor5;
+			margin-top: 50rpx;
+		}
+	}
+}
+</style>

+ 109 - 0
components/TabBar/TabBar.vue

@@ -0,0 +1,109 @@
+<template>
+	<cover-view id="tabbar" class="tabbar" >
+		<cover-view class="tabs flex-between" :style="{'padding-bottom':`${safeArea?'68rpx':'20rpx'}`}">
+			<cover-view class="item flex-center" @click="switchTab(0)">
+				<cover-image :src="`${assetsUrl}tabbar-friends-${tabIndex===0?'on':'off'}.png`" mode="aspectFill" class="tab-img"></cover-image>
+				<cover-view class="tab-text font22 fw400" :class="tabIndex===0?'tab-text-active':''">
+					交友
+				</cover-view>
+			</cover-view>
+			<cover-view class="item flex-center" @click="switchTab(1)">
+				<cover-image :src="`${assetsUrl}tabbar-message-${tabIndex===1?'on':'off'}.png`" mode="aspectFill" class="tab-img"></cover-image>
+				<cover-view class="tab-text font22 fw400" :class="tabIndex===1?'tab-text-active':''">
+					消息
+				</cover-view>
+			</cover-view>
+			<cover-view class="item flex-center" @click="switchTab(2)">
+				<cover-image :src="`${assetsUrl}tabbar-mine-${tabIndex===2?'on':'off'}.png`" mode="aspectFill" class="tab-img"></cover-image>
+				<cover-view class="tab-text font22 fw400" :class="tabIndex===2?'tab-text-active':''">
+					我的
+				</cover-view>
+			</cover-view>
+		</cover-view>
+	</cover-view>
+</template>
+
+<script>
+	import {hasSafeArea} from '../../util/index.js'
+	export default {
+		name:"TabBar",
+		props:{
+			tabIndex:{
+				type:Number,
+				default:0
+			}
+		},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				safeArea:this.$util.hasSafeArea(),
+				list:[ 
+					{
+						pagePath: "/pages/friends/friends",
+						text: "交友"
+					},
+					{
+						pagePath: "/pages/messages/messages",
+						text: "消息"
+					},
+					{
+						pagePath: "/pages/mine/mine",
+						text: "我的"
+					}
+				],
+			};
+		},
+		mounted() {
+			
+		},
+		methods:{
+			switchTab(index){
+				console.log(index)
+				uni.switchTab({
+					url:this.list[index].pagePath
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.tabbar{
+		width: 100vw;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		padding: 0 44rpx;
+		box-sizing: border-box;
+		background-color: $bgcolor1;
+		z-index: 1000;
+		.tabs{
+			.item{
+				flex: 1;
+				flex-direction: column;
+				.tab-img{
+					width: 72rpx;
+					height: 72rpx;
+				}
+				.tab-text{
+					color: $fontcolor2;
+					text-align: center;
+				}
+				.tab-text-active{
+					color: $fontcolor5 !important;
+				}
+			}
+			.add{
+				width: 96rpx;
+				height: 72rpx;
+				background: linear-gradient(133deg, #7F5CFA 0%, #654AFE 100%);
+				border-radius: 32rpx;
+				.add-img{
+					width: 96rpx;
+					height: 72rpx;
+				}
+			}
+		}
+	}
+
+</style>

+ 77 - 0
components/TopBar/TopBar.vue

@@ -0,0 +1,77 @@
+<template>
+	<view class="topbar flex-between" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+		<view class="icon flex-center" @click="action">
+			<image src="../../static/back.png" mode="aspectFill" class="icon-img" v-if="icon==='back'"></image>
+		</view>
+		<view class="text font32 fw600" :style="{'line-height':`${topbarOffsetHeight-statusBarHeight}px`}">
+			{{title}}
+		</view>
+		<view class="null">
+			
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name:"TopBar",
+		props:{
+			icon:{
+				type:String,
+				default:''
+			},
+			title:{
+				type:String,
+				default:''
+			}
+		},
+		data() {
+			return {
+				
+			};
+		},
+		computed:{
+			statusBarHeight(){
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight(){
+				return this.$store.state.topbarOffsetHeight;
+			}
+		},
+		methods:{
+			action(){
+				switch(this.icon){
+					case 'back':
+						uni.navigateBack({
+							delta:1
+						})
+					break;
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.topbar{
+		background-color: $bgcolor1;
+	}
+	.icon{
+		width: 128rpx;
+		height: 100%;
+		.icon-img{
+			width: 40rpx;
+			height: 40rpx;
+		}
+	}
+	.text{
+		height: 100%;
+		flex: 1;
+		color: $fontcolor5;
+		text-align: center;
+	}
+	.null{
+		width: 128rpx;
+		height: 100%;
+	}
+</style>

+ 68 - 0
components/base/common.js

@@ -0,0 +1,68 @@
+export function caculateTimeago(dateTimeStamp) {
+  const minute = 1000 * 60; // 把分,时,天,周,半个月,一个月用毫秒表示
+
+  const hour = minute * 60;
+  const day = hour * 24;
+  const week = day * 7;
+  const now = new Date().getTime(); // 获取当前时间毫秒
+
+  const diffValue = now - dateTimeStamp; // 时间差
+
+  let result = '';
+
+  if (diffValue < 0) {
+    return;
+  }
+
+  const minC = diffValue / minute; // 计算时间差的分,时,天,周,月
+
+  const hourC = diffValue / hour;
+  const dayC = diffValue / day;
+  const weekC = diffValue / week;
+
+  if (weekC >= 1 && weekC <= 4) {
+    result = ` ${parseInt(weekC, 10)}周前`;
+  } else if (dayC >= 1 && dayC <= 6) {
+    result = ` ${parseInt(dayC, 10)}天前`;
+  } else if (hourC >= 1 && hourC <= 23) {
+    result = ` ${parseInt(hourC, 10)}小时前`;
+  } else if (minC >= 1 && minC <= 59) {
+    result = ` ${parseInt(minC, 10)}分钟前`;
+  } else if (diffValue >= 0 && diffValue <= minute) {
+    result = '刚刚';
+  } else {
+    const datetime = new Date();
+    datetime.setTime(dateTimeStamp);
+    const Nyear = datetime.getFullYear();
+    const Nmonth = datetime.getMonth() + 1 < 10 ? `0${datetime.getMonth() + 1}` : datetime.getMonth() + 1;
+    const Ndate = datetime.getDate() < 10 ? `0${datetime.getDate()}` : datetime.getDate();
+    result = `${Nyear}-${Nmonth}-${Ndate}`;
+  }
+
+  return result;
+}
+export function formateTime(secondTime) {
+  const time = secondTime;
+  let newTime;
+  let hour;
+  let minite;
+  let seconds;
+  if (time >= 3600) {
+    hour = parseInt(time / 3600) < 10 ? `0${parseInt(time / 3600)}` : parseInt(time / 3600);
+    minite = parseInt(time % 60 / 60) < 10 ? `0${parseInt(time % 60 / 60)}` : parseInt(time % 60 / 60);
+    seconds = time % 3600 < 10 ? `0${time % 3600}` : time % 3600;
+    if (seconds > 60) {
+      minite = parseInt(seconds / 60) < 10 ? `0${parseInt(seconds / 60)}` : parseInt(seconds / 60);
+      seconds = seconds % 60 < 10 ? `0${seconds % 60}` : seconds % 60;
+    }
+    newTime = `${hour}:${minite}:${seconds}`;
+  } else if (time >= 60 && time < 3600) {
+    minite = parseInt(time / 60) < 10 ? `0${parseInt(time / 60)}` : parseInt(time / 60);
+    seconds = time % 60 < 10 ? `0${time % 60}` : time % 60;
+    newTime = `00:${minite}:${seconds}`;
+  } else if (time < 60) {
+    seconds = time < 10 ? `0${time}` : time;
+    newTime = `00:00:${seconds}`;
+  }
+  return newTime;
+}

+ 146 - 0
components/base/emojiMap.js

@@ -0,0 +1,146 @@
+export const emojiUrl = 'https://web.sdk.qcloud.com/im/assets/emoji/';
+export const emojiMap = {
+  '[NO]': 'emoji_0@2x.png',
+  '[OK]': 'emoji_1@2x.png',
+  '[下雨]': 'emoji_2@2x.png',
+  '[么么哒]': 'emoji_3@2x.png',
+  '[乒乓]': 'emoji_4@2x.png',
+  '[便便]': 'emoji_5@2x.png',
+  '[信封]': 'emoji_6@2x.png',
+  '[偷笑]': 'emoji_7@2x.png',
+  '[傲慢]': 'emoji_8@2x.png',
+  '[再见]': 'emoji_9@2x.png',
+  '[冷汗]': 'emoji_10@2x.png',
+  '[凋谢]': 'emoji_11@2x.png',
+  '[刀]': 'emoji_12@2x.png',
+  '[删除]': 'emoji_13@2x.png',
+  '[勾引]': 'emoji_14@2x.png',
+  '[发呆]': 'emoji_15@2x.png',
+  '[发抖]': 'emoji_16@2x.png',
+  '[可怜]': 'emoji_17@2x.png',
+  '[可爱]': 'emoji_18@2x.png',
+  '[右哼哼]': 'emoji_19@2x.png',
+  '[右太极]': 'emoji_20@2x.png',
+  '[右车头]': 'emoji_21@2x.png',
+  '[吐]': 'emoji_22@2x.png',
+  '[吓]': 'emoji_23@2x.png',
+  '[咒骂]': 'emoji_24@2x.png',
+  '[咖啡]': 'emoji_25@2x.png',
+  '[啤酒]': 'emoji_26@2x.png',
+  '[嘘]': 'emoji_27@2x.png',
+  '[回头]': 'emoji_28@2x.png',
+  '[困]': 'emoji_29@2x.png',
+  '[坏笑]': 'emoji_30@2x.png',
+  '[多云]': 'emoji_31@2x.png',
+  '[大兵]': 'emoji_32@2x.png',
+  '[大哭]': 'emoji_33@2x.png',
+  '[太阳]': 'emoji_34@2x.png',
+  '[奋斗]': 'emoji_35@2x.png',
+  '[奶瓶]': 'emoji_36@2x.png',
+  '[委屈]': 'emoji_37@2x.png',
+  '[害羞]': 'emoji_38@2x.png',
+  '[尴尬]': 'emoji_39@2x.png',
+  '[左哼哼]': 'emoji_40@2x.png',
+  '[左太极]': 'emoji_41@2x.png',
+  '[左车头]': 'emoji_42@2x.png',
+  '[差劲]': 'emoji_43@2x.png',
+  '[弱]': 'emoji_44@2x.png',
+  '[强]': 'emoji_45@2x.png',
+  '[彩带]': 'emoji_46@2x.png',
+  '[彩球]': 'emoji_47@2x.png',
+  '[得意]': 'emoji_48@2x.png',
+  '[微笑]': 'emoji_49@2x.png',
+  '[心碎了]': 'emoji_50@2x.png',
+  '[快哭了]': 'emoji_51@2x.png',
+  '[怄火]': 'emoji_52@2x.png',
+  '[怒]': 'emoji_53@2x.png',
+  '[惊恐]': 'emoji_54@2x.png',
+  '[惊讶]': 'emoji_55@2x.png',
+  '[憨笑]': 'emoji_56@2x.png',
+  '[手枪]': 'emoji_57@2x.png',
+  '[打哈欠]': 'emoji_58@2x.png',
+  '[抓狂]': 'emoji_59@2x.png',
+  '[折磨]': 'emoji_60@2x.png',
+  '[抠鼻]': 'emoji_61@2x.png',
+  '[抱抱]': 'emoji_62@2x.png',
+  '[抱拳]': 'emoji_63@2x.png',
+  '[拳头]': 'emoji_64@2x.png',
+  '[挥手]': 'emoji_65@2x.png',
+  '[握手]': 'emoji_66@2x.png',
+  '[撇嘴]': 'emoji_67@2x.png',
+  '[擦汗]': 'emoji_68@2x.png',
+  '[敲打]': 'emoji_69@2x.png',
+  '[晕]': 'emoji_70@2x.png',
+  '[月亮]': 'emoji_71@2x.png',
+  '[棒棒糖]': 'emoji_72@2x.png',
+  '[汽车]': 'emoji_73@2x.png',
+  '[沙发]': 'emoji_74@2x.png',
+  '[流汗]': 'emoji_75@2x.png',
+  '[流泪]': 'emoji_76@2x.png',
+  '[激动]': 'emoji_77@2x.png',
+  '[灯泡]': 'emoji_78@2x.png',
+  '[炸弹]': 'emoji_79@2x.png',
+  '[熊猫]': 'emoji_80@2x.png',
+  '[爆筋]': 'emoji_81@2x.png',
+  '[爱你]': 'emoji_82@2x.png',
+  '[爱心]': 'emoji_83@2x.png',
+  '[爱情]': 'emoji_84@2x.png',
+  '[猪头]': 'emoji_85@2x.png',
+  '[猫咪]': 'emoji_86@2x.png',
+  '[献吻]': 'emoji_87@2x.png',
+  '[玫瑰]': 'emoji_88@2x.png',
+  '[瓢虫]': 'emoji_89@2x.png',
+  '[疑问]': 'emoji_90@2x.png',
+  '[白眼]': 'emoji_91@2x.png',
+  '[皮球]': 'emoji_92@2x.png',
+  '[睡觉]': 'emoji_93@2x.png',
+  '[磕头]': 'emoji_94@2x.png',
+  '[示爱]': 'emoji_95@2x.png',
+  '[礼品袋]': 'emoji_96@2x.png',
+  '[礼物]': 'emoji_97@2x.png',
+  '[篮球]': 'emoji_98@2x.png',
+  '[米饭]': 'emoji_99@2x.png',
+  '[糗大了]': 'emoji_100@2x.png',
+  '[红双喜]': 'emoji_101@2x.png',
+  '[红灯笼]': 'emoji_102@2x.png',
+  '[纸巾]': 'emoji_103@2x.png',
+  '[胜利]': 'emoji_104@2x.png',
+  '[色]': 'emoji_105@2x.png',
+  '[药]': 'emoji_106@2x.png',
+  '[菜刀]': 'emoji_107@2x.png',
+  '[蛋糕]': 'emoji_108@2x.png',
+  '[蜡烛]': 'emoji_109@2x.png',
+  '[街舞]': 'emoji_110@2x.png',
+  '[衰]': 'emoji_111@2x.png',
+  '[西瓜]': 'emoji_112@2x.png',
+  '[调皮]': 'emoji_113@2x.png',
+  '[象棋]': 'emoji_114@2x.png',
+  '[跳绳]': 'emoji_115@2x.png',
+  '[跳跳]': 'emoji_116@2x.png',
+  '[车厢]': 'emoji_117@2x.png',
+  '[转圈]': 'emoji_118@2x.png',
+  '[鄙视]': 'emoji_119@2x.png',
+  '[酷]': 'emoji_120@2x.png',
+  '[钞票]': 'emoji_121@2x.png',
+  '[钻戒]': 'emoji_122@2x.png',
+  '[闪电]': 'emoji_123@2x.png',
+  '[闭嘴]': 'emoji_124@2x.png',
+  '[闹钟]': 'emoji_125@2x.png',
+  '[阴险]': 'emoji_126@2x.png',
+  '[难过]': 'emoji_127@2x.png',
+  '[雨伞]': 'emoji_128@2x.png',
+  '[青蛙]': 'emoji_129@2x.png',
+  '[面条]': 'emoji_130@2x.png',
+  '[鞭炮]': 'emoji_131@2x.png',
+  '[风车]': 'emoji_132@2x.png',
+  '[飞吻]': 'emoji_133@2x.png',
+  '[飞机]': 'emoji_134@2x.png',
+  '[饥饿]': 'emoji_135@2x.png',
+  '[香蕉]': 'emoji_136@2x.png',
+  '[骷髅]': 'emoji_137@2x.png',
+  '[麦克风]': 'emoji_138@2x.png',
+  '[麻将]': 'emoji_139@2x.png',
+  '[鼓掌]': 'emoji_140@2x.png',
+  '[龇牙]': 'emoji_141@2x.png'
+};
+export const emojiName = ['[龇牙]', '[调皮]', '[流汗]', '[偷笑]', '[再见]', '[敲打]', '[擦汗]', '[猪头]', '[玫瑰]', '[流泪]', '[大哭]', '[嘘]', '[酷]', '[抓狂]', '[委屈]', '[便便]', '[炸弹]', '[菜刀]', '[可爱]', '[色]', '[害羞]', '[得意]', '[吐]', '[微笑]', '[怒]', '[尴尬]', '[惊恐]', '[冷汗]', '[爱心]', '[示爱]', '[白眼]', '[傲慢]', '[难过]', '[惊讶]', '[疑问]', '[困]', '[么么哒]', '[憨笑]', '[爱情]', '[衰]', '[撇嘴]', '[阴险]', '[奋斗]', '[发呆]', '[右哼哼]', '[抱抱]', '[坏笑]', '[飞吻]', '[鄙视]', '[晕]', '[大兵]', '[可怜]', '[强]', '[弱]', '[握手]', '[胜利]', '[抱拳]', '[凋谢]', '[米饭]', '[蛋糕]', '[西瓜]', '[啤酒]', '[瓢虫]', '[勾引]', '[OK]', '[爱你]', '[咖啡]', '[月亮]', '[刀]', '[发抖]', '[差劲]', '[拳头]', '[心碎了]', '[太阳]', '[礼物]', '[皮球]', '[骷髅]', '[挥手]', '[闪电]', '[饥饿]', '[困]', '[咒骂]', '[折磨]', '[抠鼻]', '[鼓掌]', '[糗大了]', '[左哼哼]', '[打哈欠]', '[快哭了]', '[吓]', '[篮球]', '[乒乓]', '[NO]', '[跳跳]', '[怄火]', '[转圈]', '[磕头]', '[回头]', '[跳绳]', '[激动]', '[街舞]', '[献吻]', '[左太极]', '[右太极]', '[闭嘴]', '[猫咪]', '[红双喜]', '[鞭炮]', '[红灯笼]', '[麻将]', '[麦克风]', '[礼品袋]', '[信封]', '[象棋]', '[彩带]', '[蜡烛]', '[爆筋]', '[棒棒糖]', '[奶瓶]', '[面条]', '[香蕉]', '[飞机]', '[左车头]', '[车厢]', '[右车头]', '[多云]', '[下雨]', '[钞票]', '[熊猫]', '[灯泡]', '[风车]', '[闹钟]', '[雨伞]', '[彩球]', '[钻戒]', '[沙发]', '[纸巾]', '[手枪]', '[青蛙]'];

+ 232 - 0
components/base/message-facade.js

@@ -0,0 +1,232 @@
+import {
+  emojiMap,
+  emojiUrl
+} from './emojiMap';
+/** 传入message.element(群系统消息SystemMessage,群提示消息GroupTip除外)
+ * content = {
+ *  type: 'TIMTextElem',
+ *  content: {
+ *    text: 'AAA[龇牙]AAA[龇牙]AAA[龇牙AAA]'
+ *  }
+ *}
+ **/
+// 群提示消息的含义 (opType)
+
+const GROUP_TIP_TYPE = {
+  MEMBER_JOIN: 1,
+  MEMBER_QUIT: 2,
+  MEMBER_KICKED_OUT: 3,
+  MEMBER_SET_ADMIN: 4,
+  // 被设置为管理员
+  MEMBER_CANCELED_ADMIN: 5,
+  // 被取消管理员
+  GROUP_INFO_MODIFIED: 6,
+  // 修改群资料,转让群组为该类型,msgBody.msgGroupNewInfo.ownerAccount表示新群主的ID
+  MEMBER_INFO_MODIFIED: 7 // 修改群成员信息
+
+}; // 解析小程序text, 表情信息也是[嘻嘻]文本
+
+export function parseText(message) {
+  const renderDom = [];
+  let temp = message.payload.text;
+  let left = -1;
+  let right = -1;
+
+  while (temp !== '') {
+    left = temp.indexOf('[');
+    right = temp.indexOf(']');
+
+    switch (left) {
+      case 0:
+        if (right === -1) {
+          renderDom.push({
+            name: 'span',
+            text: temp
+          });
+          temp = '';
+        } else {
+          const _emoji = temp.slice(0, right + 1);
+
+          if (emojiMap[_emoji]) {
+            renderDom.push({
+              name: 'img',
+              src: emojiUrl + emojiMap[_emoji]
+            });
+            temp = temp.substring(right + 1);
+          } else {
+            renderDom.push({
+              name: 'span',
+              text: '['
+            });
+            temp = temp.slice(1);
+          }
+        }
+
+        break;
+
+      case -1:
+        renderDom.push({
+          name: 'span',
+          text: temp
+        });
+        temp = '';
+        break;
+
+      default:
+        renderDom.push({
+          name: 'span',
+          text: temp.slice(0, left)
+        });
+        temp = temp.substring(left);
+        break;
+    }
+  }
+
+  return renderDom;
+} // 解析群系统消息
+
+export function parseGroupSystemNotice(message) {
+  const {
+    payload
+  } = message;
+  const groupName = payload.groupProfile.name || payload.groupProfile.groupID;
+  const {
+    groupID
+  } = payload.groupProfile;
+  let text;
+
+  switch (payload.operationType) {
+    case 1:
+      text = `${payload.operatorID} 申请加入群组:${groupName}(群ID:${groupID})`;
+      break;
+
+    case 2:
+      text = `成功加入群组:${groupName} (群ID:${groupID})`;
+      break;
+
+    case 3:
+      text = `申请加入群组:${groupName} (群ID:${groupID})被拒绝`;
+      break;
+
+    case 4:
+      text = `被管理员${payload.operatorID}踢出群组:${groupName}(群ID:${groupID})`;
+      break;
+
+    case 5:
+      text = `群:${groupName} (群ID:${groupID})已被${payload.operatorID}解散`;
+      break;
+
+    case 6:
+      text = `我(用户ID:${payload.operatorID})成功创建群聊:${groupName}(群ID:${groupID})`;
+      break;
+
+    case 7:
+      text = `用户ID:${payload.operatorID}邀请你加群:${groupName}(群ID:${groupID})`;
+      break;
+
+    case 8:
+      text = `你退出群组:${groupName}(群ID:${groupID})`;
+      break;
+
+    case 9:
+      text = `你被${payload.operatorID}设置为群:${groupName}(群ID:${groupID})的管理员`;
+      break;
+
+    case 10:
+      text = `你被${payload.operatorID}撤销群:${groupName} (群ID:${groupID})的管理员身份`;
+      break;
+
+    case 255:
+      text = `自定义群系统通知: ${payload.userDefinedField}`;
+      break;
+  }
+
+  return text;
+} // 解析群提示消息
+
+export function parseGroupTip(message) {
+  const {
+    payload
+  } = message;
+  const userName = message.nick || payload.userIDList.join(',');
+  let tip;
+  let user;
+
+  switch (payload.operationType) {
+    case GROUP_TIP_TYPE.MEMBER_JOIN:
+      tip = `${userName} 加入群聊`;
+      break;
+
+    case GROUP_TIP_TYPE.MEMBER_QUIT:
+      tip = `群成员退群:${userName}`;
+      break;
+
+    case GROUP_TIP_TYPE.MEMBER_KICKED_OUT:
+      tip = `群成员被踢:${userName}`;
+      break;
+
+    case GROUP_TIP_TYPE.MEMBER_SET_ADMIN:
+      tip = `${payload.operatorID}将 ${userName}设置为管理员`;
+      break;
+
+    case GROUP_TIP_TYPE.MEMBER_CANCELED_ADMIN:
+      tip = `${payload.operatorID}将 ${userName}取消作为管理员`;
+      break;
+
+    case GROUP_TIP_TYPE.GROUP_INFO_MODIFIED:
+      tip = '群资料修改';
+      break;
+
+    case GROUP_TIP_TYPE.MEMBER_INFO_MODIFIED:
+      for (const member of payload.memberList) {
+        if (member.muteTime > 0) {
+          tip = `群成员:${member.userID}被禁言${member.muteTime}秒`;
+        } else {
+          tip = `群成员:${member.userID}被取消禁言`;
+        }
+      }
+
+      break;
+
+    case 256:
+      user = message.nick || message.from;
+
+      if (payload.text === '无应答') {
+        user = payload.userIDList.join(',');
+      }
+
+      tip = payload.text === '结束群聊' ? '结束群聊' : `"${user}" ${payload.text}`;
+      break;
+  }
+
+  return [{
+    name: 'groupTip',
+    text: tip
+  }];
+} // 解析图片消息
+
+export function parseImage(message) {
+  const renderDom = [{
+    name: 'image',
+    // 这里默认渲染的是 1080P 的图片
+    src: message.payload.imageInfoArray[0].url
+  }];
+  return renderDom;
+} // 解析视频消息
+
+export function parseVideo(message) {
+  const renderDom = {
+    name: 'video',
+    src: message.payload.videoUrl
+  };
+  return renderDom;
+} // 解析语音消息
+
+export function parseAudio(message) {
+  const renderDom = {
+    name: 'audio',
+    src: message.payload.url,
+    second: message.payload.second === 0 ? 1 : message.payload.second
+  };
+  return renderDom;
+}

+ 763 - 0
components/invinbg-image-cropper/invinbg-image-cropper.vue

@@ -0,0 +1,763 @@
+<template>
+    <view class="vue-cropper" ref="cropper" :style="{ top : `${containerTop}px` }" v-show="show">
+        <view class="cropper-box">
+            <view class="cropper-box-canvas" @touchstart.stop.prevent="imgTouchStart" @touchmove.stop.prevent="imgMoveing" @touchend.stop.prevent="imgMoveEnd" :style="{
+					'width': imageWidth + 'px',
+					'height': imageHeight + 'px',
+					'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ x / scale + 'px,' + y / scale + 'px,' + '0)'
+					+ 'rotateZ('+ rotate * 90 +'deg)'
+					}">
+                <image :src="src" alt="cropper-img" ref="cropperImg" mode="scaleToFill" class="uni-image"></image>
+            </view>
+        </view>
+        <view class="cropper-drag-box cropper-modal cropper-move pointer-events"></view>
+        <view class="cropper-crop-box" :class="{'pointer-events': cropFixed}" :style="{'width': cropW + 'px','height': cropH + 'px','transform': 'translate3d('+ cropOffsertX + 'px,' + cropOffsertY + 'px,' + '0)'}">
+            <view class="cropper-view-box">
+                <image :style="{'width': imageWidth + 'px','height': imageHeight + 'px','transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ (x - cropOffsertX) / scale  + 'px,' + (y - cropOffsertY) / scale + 'px,' + '0)' + 'rotateZ('+ rotate * 90 +'deg)'}" mode="scaleToFill" :src="src" alt="cropper-img"></image>
+            </view>
+
+            <view v-if="!cropFixed" class="cropper-face cropper-move" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="cropMoveing"></view>
+
+            <view class="crop-line line-w"></view>
+            <view class="crop-line line-a"></view>
+            <view class="crop-line line-s"></view>
+            <view class="crop-line line-d"></view>
+            <block v-if="!cropFixed">
+                <view class="crop-point point-lt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'left-top')"></view>
+                <view class="crop-point point-mt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-top')"></view>
+                <view class="crop-point point-rt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'right-top')"></view>
+                <view class="crop-point point-ml" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-left')"></view>
+                <view class="crop-point point-mr" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-right')"></view>
+                <view class="crop-point point-lb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'left-bottom')"></view>
+                <view class="crop-point point-mb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-bottom')"></view>
+                <view class="crop-point point-rb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'right-bottom')"></view>
+            </block>
+        </view>
+
+        <canvas canvas-id="myCanvas" class="cropper-canvas" :style="{ 'width': cropW + 'px','height': cropH + 'px' }"></canvas>
+
+        <view class="btn-group">
+            <view class="btn-item rotate-btn" v-show="showRotateBtn" @tap="rotateHandler"></view>
+        </view>
+
+        <view class="uni-info__ft">
+            <view class="uni-modal__btn uni-modal__btn_default" style="color: #9A9ABF;" @tap="cancel">取消</view>
+			<view class="uni-modal__btn uni-modal__btn_default" style="color: #9A9ABF;" @tap="init">还原</view>
+            <view class="uni-modal__btn uni-modal__btn_primary" style="color: #FFFFFF;" @tap="confirm">下一步</view>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        name: 'image-cropper',
+        props: {
+            cropWidth: {
+                type: Number,
+                default: 200,
+            },
+            cropHeight: {
+                type: Number,
+                default: 200
+            },
+            cropFixed: {
+                type: Boolean,
+                default: false,
+            },
+            src: {
+                type: String,
+            },
+            showResetBtn: {
+                type: Boolean,
+                default: true,
+            },
+            showRotateBtn: {
+                type: Boolean,
+                default: true,
+            }
+        },
+        data() {
+            const sysInfo = uni.getSystemInfoSync();
+            const pixelRatio = sysInfo.pixelRatio
+            return {
+                show: false,
+                scale: 1,
+                rotate: 0,
+                cropW: 0,
+                cropH: 0,
+                cropOldW: 0,
+                cropOldH: 0,
+                sysInfo: sysInfo,
+                pixelRatio: pixelRatio,
+                imageRealWidth: 0,
+                imageRealHeight: 0,
+                cropOffsertX: 0,
+                cropOffsertY: 0,
+                startX: 0,
+                startY: 0,
+                // 裁剪框与边界间距
+                border: 5,
+                x: 0,
+                y: 0,
+                startL: 0,
+                oldScale: 1,
+            }
+        },
+        watch: {
+            src(val) {
+                if(val.length > 0) {
+                    this.init()
+                }
+            },
+            show(val) {
+                if(!val) {
+                    this.src = ''
+                }
+            }
+        },
+        computed: {
+            containerTop() {
+                let top = 0
+                // #ifdef H5
+                    top = 44
+                // #endif
+                return top;
+            },
+            // 容器高度
+            containerHeight() {
+                return this.windowHeight - 48;
+            },
+            // 屏幕宽度
+            windowWidth() {
+                return this.sysInfo.windowWidth;
+            },
+            windowHeight() {
+                return this.sysInfo.windowHeight;
+            },
+            // 图片宽高比
+            imageRatio() {
+                if (this.imageRealHeight > 0) {
+                    return this.imageRealWidth / this.imageRealHeight
+                }
+                return 0
+            },
+            // 等比缩放后的宽度
+            imageWidth() {
+                if (this.imageRatio >= 1) {
+                    return this.windowWidth
+                }
+                return this.windowWidth * this.imageRatio
+            },
+            // 等比缩放后的高度
+            imageHeight() {
+                if (this.imageRatio >= 1) {
+                    return this.windowWidth / this.imageRatio
+                }
+                return this.windowWidth
+            },
+        },
+        methods: {
+            rotateHandler() {
+                if(this.rotate == 3) {
+                    this.rotate = 0;
+                } else {
+                    ++this.rotate
+                }
+            },
+            init() {
+                this.rotate = 0;
+                this.scale = 1;
+                this.cropW = this.cropWidth
+                this.cropH = this.cropHeight
+                uni.showLoading({
+                    title: '图片加载中...',
+                })
+                this.loadImage(this.src).then((e) => {
+                    uni.hideLoading()
+                }).catch((e) => {
+                    uni.hideLoading()
+                    uni.showModal({
+                        title: '标题',
+                        content: '图片加载失败'
+                    })
+                })
+            },
+            loadImage(src) {
+                const _this = this
+                return new Promise((resolve, reject) => {
+                    uni.getImageInfo({
+                        src: src,
+                        success: (res) => {
+                            _this.imageRealWidth = res.width
+                            _this.imageRealHeight = res.height
+
+                            _this.cropOffsertX = _this.windowWidth / 2 - _this.cropW / 2
+                            _this.cropOffsertY = _this.windowHeight / 2 - _this.cropH / 2
+                            _this.show = true
+
+                            _this.$nextTick(() => {
+                                _this.x = _this.windowWidth / 2 - _this.imageWidth / 2
+                                _this.y = _this.containerHeight / 2 - _this.imageHeight / 2
+                            });
+                            resolve(res)
+                        },
+                        fail: (e) => {
+                            _this.show = false
+                            reject(e)
+                        }
+                    })
+                });
+
+            },
+            cancel() {
+                this.show = false
+                this.$emit('cancel')
+            },
+            confirm(event) {
+                uni.showLoading({
+                    title: '裁剪中...',
+                })
+                const _this = this
+                const ctx = uni.createCanvasContext('myCanvas', _this);
+
+                const pixelRatio = _this.pixelRatio
+                const imgage = _this.src
+                const imgW = _this.imageWidth * _this.scale;
+                const imgH = _this.imageHeight * _this.scale
+                const rotate = _this.rotate
+                let dx = _this.cropOffsertX - _this.x - (_this.imageWidth - imgW) / 2;
+                let dy = _this.cropOffsertY - _this.y - (_this.imageHeight - imgH) / 2;
+
+                ctx.setFillStyle('white')
+                ctx.fillRect(0, 0, imgW, imgH)
+                ctx.save()
+
+                ctx.rotate((rotate * 90 * Math.PI) / 180);
+                switch (rotate) {
+                    case 1:
+                        dx += (imgH-imgW) / 2
+                        dy -= (imgH-imgW) / 2
+                        ctx.drawImage(imgage, -dy, dx, imgW, -imgH);
+                        break;
+                    case 2:
+                        ctx.drawImage(imgage, dx, dy, -imgW, -imgH);
+                        break;
+                    case 3:
+                        dx += (imgH-imgW) / 2
+                        dy -= (imgH-imgW) / 2
+                        ctx.drawImage(imgage, dy, -dx, -imgW, imgH);
+                        break;
+                    default:
+                        ctx.drawImage(imgage, -dx, -dy, imgW, imgH);
+                        break;
+                }
+                ctx.restore()
+                ctx.draw(false, () => {
+                    uni.canvasToTempFilePath({
+                        canvasId: 'myCanvas',
+                        destWidth: _this.cropW * pixelRatio,
+                        destHeight: _this.cropH * pixelRatio,
+                        success: (res) => {
+                            uni.hideLoading()
+                            event.detail.tempFilePath = res.tempFilePath
+                            _this.show = false
+                            _this.$emit('confirm', event)
+                        },
+                        fail: (e) => {
+                            uni.hideLoading()
+                            uni.showModal({
+                                title: '提示',
+                                content: '裁剪失败'
+                            })
+                        }
+                    }, _this);
+                })
+
+            },
+            imgTouchStart(e) {
+                if(e.touches.length == 2) {
+                    this.oldScale = this.scale
+                    this.scaling = true
+                    const x = e.touches[0].pageX - e.touches[1].pageX
+                    const y = e.touches[0].pageY - e.touches[1].pageY
+                    const hypotenuse = Math.sqrt(
+                        Math.pow(x, 2) +
+                        Math.pow(y, 2)
+                    )
+
+                    this.startL = Math.max(x, y, hypotenuse)
+                    uni.showModal({
+                        content: this.startL
+                    })
+                } else {
+                    this.startX = e.touches[0].pageX - this.x
+                    this.startY = e.touches[0].pageY - this.y
+                }
+            },
+            imgMoveing(e) {
+                if(this.scaling) {
+                    let scale = this.oldScale
+
+                    const x = e.touches[0].pageX - e.touches[1].pageX
+                    const y = e.touches[0].pageY - e.touches[1].pageY
+                    const hypotenuse = Math.sqrt(
+                        Math.pow(x, 2) +
+                        Math.pow(y, 2)
+                    )
+
+                    const newL = Math.max(x, y, hypotenuse)
+
+                    const cha = newL - this.startL;
+
+                    // 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
+                    // 1px - 0.2
+                    let coe = 1;
+                    coe =
+                        coe / this.imageWidth > coe / this.imageHeight
+                            ? coe / this.imageHeight
+                            : coe / this.imageWidth;
+                    coe = coe > 0.1 ? 0.1 : coe;
+                    const num = coe * cha;
+
+                    if (cha > 0) {
+                        scale += Math.abs(num);
+                    } else if (cha < 0) {
+                        scale > Math.abs(num) ? (scale -= Math.abs(num)) : scale;
+                    }
+
+                    this.scale = scale;
+                } else {
+                    const moveX = e.touches[0].pageX - this.startX
+                    const moveY = e.touches[0].pageY - this.startY
+
+                    this.x = moveX
+                    this.y = moveY
+                }
+            },
+            imgMoveEnd() {
+                setTimeout(() => {
+                    this.scaling = false
+                }, 100)
+            },
+            touchStart(e) {
+                this.startX = e.touches[0].pageX - this.cropOffsertX;
+                this.startY = e.touches[0].pageY - this.cropOffsertY;
+
+                this.cropOldW = this.cropW
+                this.cropOldH = this.cropH
+            },
+            cropMoveing(e) {
+                const moveX = this._cropX(e.touches[0].pageX - this.startX)
+                const moveY = this._cropY(e.touches[0].pageY - this.startY)
+
+                this.cropOffsertX = moveX
+                this.cropOffsertY = moveY
+            },
+            dragMove(e, type) {
+                if(this.cropFixed) {
+                    return false
+                }
+                const moveX = e.touches[0].pageX - this.startX
+                const moveY = e.touches[0].pageY - this.startY
+                switch (type) {
+                    case 'left-top':
+                        this._cropMoveLeft(moveX)
+                        this._cropMoveTop(moveY)
+                        break;
+                    case 'middle-top':
+                        this._cropMoveTop(moveY)
+                        break;
+                    case 'right-top':
+                        this._cropMoveTop(moveY)
+                        this._cropMoveRight(moveX)
+                        break;
+                    case 'middle-right':
+                        this._cropMoveRight(moveX)
+                        break;
+                    case 'right-bottom':
+                        this._cropMoveRight(moveX)
+                        this._cropMoveBottom(moveY)
+                        break;
+                    case 'middle-bottom':
+                        this._cropMoveBottom(moveY)
+                        break;
+                    case 'left-bottom':
+                        this._cropMoveBottom(moveY)
+                        this._cropMoveLeft(moveX)
+                        break;
+                    case 'middle-left':
+                        this._cropMoveLeft(moveX)
+                        break;
+                    default:
+                        break;
+                }
+            },
+            _cropMoveTop(y) {
+                const topY = this._cropY(y)
+                this.cropH += this.cropOffsertY - topY
+                this.cropOffsertY = topY
+            },
+            _cropMoveRight(x) {
+                if(this.cropOldW + x >= this.windowWidth - this.border) {
+                    return false;
+                }
+                this.cropW = this.cropOldW + (x  - this.cropOffsertX)
+            },
+            _cropMoveBottom(y) {
+                if(this.cropOldH + y >= this.windowHeight - this.containerTop - this.border) {
+                    return false;
+                }
+                this.cropH = this.cropOldH + (y  - this.cropOffsertY)
+            },
+            _cropMoveLeft(x) {
+                const leftX = this._cropY(x)
+                this.cropW += this.cropOffsertX - leftX
+                this.cropOffsertX = leftX
+            },
+            _cropX(x) {
+                if(x <= this.border) {
+                    return this.border
+                }
+                if(x + this.cropW >= this.windowWidth - this.border) {
+                    return this.windowWidth - this.cropW - this.border
+                }
+                return x
+            },
+            _cropY(y) {
+                if(y <= this.border) {
+                    return this.border
+                }
+                if(y + this.cropH >= this.windowHeight - this.containerTop - this.border) {
+                    return this.windowHeight - this.cropH - this.containerTop - this.border
+                }
+                return y
+            }
+        }
+    }
+</script>
+
+<style scoped lang="css">
+
+    @font-face {
+        font-family: "iconfont";
+        src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAR4AAsAAAAACKgAAAQsAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqEfIRGATYCJAMMCwgABCAFhG0HShugB8gOJUHBwAAAAAFEBNmwzd4dtatSmmpFoVAEhUThEAYkCozFKDCqCVO6RfH/89v869awDnTR1qrSANFt4GG4SNxreBn91fmV9f3+53J613ieHba+N1zmGM8PA7oXTaCAxpjei8IoLWFsGLu4jPME6vWJJdovqmgAO4U2LRBnep0K7GJmpYQWanXVOWuLuAFrtenK4haAa/f38QnKsCOpyrRFh6eFWsh5KXnfYcn958BGQNKfE8wmMmaAQpzkuo9Z+ukZluoltVV5abUipL5i/ysArlhWVut/eCRBVNPUjYg6oUo7JTHFoaYDSvdacnKTq9GAB4AY5y2dtL3qpFh1DENdnJC6Hq+xYb7pyRMDMzc/fYoJjY8flwO3m98rMucF+IZHj6Cagw5UeKpxyFbt2rHGY/8jpa7CYMvLfcIesLjY3bdqhaf+nqgQs2qT/+rjCH/VfA0VFGuAC3iE8NEr/Vau8vZsXiUy7+V3c3tQQXMAuNjDCC89KDIHH0OFhnUi81GEPwyc7wZUaN7DnUf4g+ZLQsMKYV/94NjK7R7TEM4niTY1oJ5zEU62aNVaasUub08YLUEam5EnT6a61/I17dNk+vTu9jpJjXhsTFwjqTtpCBxBIIgS6iQnc/Zod1YGKp0rAwsD8kkyP6AwcK0hcAwkiQmBhWvxPZWKDu86aUH2nLEdi9rGX1eXq5P6A1SrnAucMVMdZH/GKi/jyfCqJyucfK3mXpVujXOPfFf5LC4Dvx0X/943JyOq4HuCTZ8KiIPPAb6ro8akpT6ufiq39BQrNlk5mp8pO0JlJLk8f5QalRjoP60IMx0N8n7wGhSD3n6/F1zlcTVz/cR+Ev0lkLSTd7UiPbD/wCxGRMA2Krwro2O0bTQtImbwhjAJc0S3N4ROx15/PH60IzaIOjCbEelqkDOfETNxb/FMixnWNzeJp2KPQw9A5d76jGUOQOUvH7RE/o2RfkNatd3OGf9q0QKbnq8WB7qy+hVqJRjJn1BQgP/iErks0yy5iGJTrOayW7C/z0IoZH0qNH+7N+31XXc7G2p1hZDU6IWs1ghaqDNQpcEKVKu1BfWmFW9u0IFhKUodpswCEFodgqTZHWStbqOF+hqqdPsG1VrDEuodhfueDcZCj+QzuIrFtZh6BNNraIowbCzi1dbhOlOfionKXHoTzgzoY5hCKk/minEKZ/pYMDCoU7IsgREM3Y8Vgcvwvj4aMzK0AdewUpJljWkyGZH3IKmG7gfEHgZOhYXTwqiNwOhp0CiE3ZiFpL5fB6dj0keFKcGV+JvgGAP0vWMUpOQ10GI1VQt3LoMHDNJRYrEIPInAoPXDFEEnrk9P0zDG/FEGOA2WFNkiaZRGhuoRddXS8bX917cL6mn9c6TIUXSekybKHKQfJXFq2KSiRklLYU8dNKWDIX0cAA==') format('woff2');
+    }
+
+    .vue-cropper {
+        position: fixed;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        z-index: 1001;
+        box-sizing: border-box;
+        user-select: none;
+        -webkit-user-select: none;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        direction: ltr;
+        touch-action: none;
+        text-align: left;
+        /* background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
+    */
+		background-color: #000000;
+	}
+
+    .cropper-canvas {
+        position: absolute;
+        top: -9999px;
+        left:-9999px;
+        z-index: -998;
+    }
+
+    .vue-cropper .uni-info__ft {
+        position: absolute;
+        line-height: 48px;
+        font-size: 18px;
+        display: -webkit-box;
+        display: -webkit-flex;
+		background-color: #000000;
+		padding-bottom: 34rpx;
+        display: flex;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        z-index: 998;
+    }
+
+    .btn-group {
+        position: absolute;
+        right: 30px;
+        bottom: 78px;
+        z-index: 998;
+    }
+
+    .btn-item {
+        position: relative;
+        width: 40px;
+        height: 40px;
+        background: #fff;
+        border-radius: 20px;
+        padding: 10px;
+        display: inline-block;
+        margin-left: 10px;
+    }
+
+    .btn-item:active {
+        background: #ccc;
+    }
+
+    .rotate-btn {
+        font-family: "iconfont" !important;
+        font-size: 24px;
+        font-style: normal;
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
+        line-height: 20px;
+    }
+
+    .rotate-btn:before {
+        content: "\e65c";
+        margin-left: -2px;
+    }
+
+    .reset-btn {
+        font-family: "iconfont" !important;
+        font-size: 24px;
+        font-style: normal;
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
+        line-height: 20px;
+    }
+
+    .reset-btn:before {
+        content: "\e648";
+        margin-left: -2px;
+    }
+
+    .vue-cropper .uni-info__ft:after {
+        content: " ";
+        position: absolute;
+        left: 0;
+        top: 0;
+        right: 0;
+        height: 1px;
+        border-top: 1px solid #d5d5d6;
+        color: #d5d5d6;
+        -webkit-transform-origin: 0 0;
+        transform-origin: 0 0;
+        -webkit-transform: scaleY(.5);
+        transform: scaleY(.5);
+        z-index: 998;
+    }
+
+    .vue-cropper .uni-modal__btn {
+        display: block;
+        -webkit-box-flex: 1;
+        -webkit-flex: 1;
+        flex: 1;
+        font-size:28rpx;
+        text-decoration: none;
+        -webkit-tap-highlight-color: rgba(0,0,0,0);
+        position: relative;
+        text-align: center;
+        background-color: #000000;
+        z-index: 998;
+    }
+/* 
+    .vue-cropper .uni-modal__btn:first-child:after { display:  none }
+    .vue-cropper .uni-modal__btn:after {
+        content: " ";
+        position: absolute;
+        left: 0;
+        top: 0;
+        width: 1px;
+        bottom: 0;
+        border-left: 1px solid #d5d5d6;
+        color: #d5d5d6;
+        -webkit-transform-origin: 0 0;
+        transform-origin: 0 0;
+        -webkit-transform: scaleX(.5);
+        transform: scaleX(.5);
+        z-index: 998;
+    } */
+
+    .vue-cropper .uni-modal__btn:active {
+        background-color: #eee;
+    }
+
+    .cropper-box,
+    .cropper-box-canvas,
+    .cropper-drag-box,
+    .cropper-crop-box,
+    .cropper-face {
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        user-select: none;
+        z-index: 998;
+    }
+
+    .uni-image {
+        width: 100%;
+        height: 100%;
+    }
+
+    .cropper-box-canvas image {
+        position: relative;
+        text-align: left;
+        user-select: none;
+        transform: none;
+        max-width: none;
+        max-height: none;
+        z-index: 998;
+    }
+
+    .cropper-box {
+        overflow: hidden;
+    }
+
+    .cropper-move {
+        cursor: move;
+    }
+
+    .cropper-crop {
+        cursor: crosshair;
+    }
+
+    .cropper-modal {
+        background: rgba(0, 0, 0, 0.5);
+    }
+
+    .pointer-events {
+        pointer-events:none;
+    }
+
+    .cropper-crop-box {
+        /*border: 2px solid #39f;*/
+    }
+
+    .cropper-view-box {
+        display: block;
+        overflow: hidden;
+        width: 100%;
+        height: 100%;
+        outline: 1px solid #39f;
+        outline-color: rgba(51, 153, 255, 0.75);
+        user-select: none;
+    }
+
+    .cropper-view-box image {
+        user-select: none;
+        text-align: left;
+        max-width: none;
+        max-height: none;
+    }
+
+    .cropper-face {
+        top: 0;
+        left: 0;
+        background-color: #fff;
+        opacity: 0.1;
+    }
+
+    .crop-line {
+        position: absolute;
+        display: block;
+        width: 100%;
+        height: 100%;
+        opacity: 0.1;
+        z-index: 998;
+    }
+
+    .line-w {
+        top: -3px;
+        left: 0;
+        height: 5px;
+        cursor: n-resize;
+    }
+
+    .line-a {
+        top: 0;
+        left: -3px;
+        width: 5px;
+        cursor: w-resize;
+    }
+
+    .line-s {
+        bottom: -3px;
+        left: 0;
+        height: 5px;
+        cursor: s-resize;
+    }
+
+    .line-d {
+        top: 0;
+        right: -3px;
+        width: 5px;
+        cursor: e-resize;
+    }
+
+    .crop-point {
+        position: absolute;
+        width: 8px;
+        height: 8px;
+        opacity: 0.75;
+        background-color: #39f;
+        border-radius: 100%;
+        z-index: 998;
+    }
+
+    .point-lt {
+        top: -4px;
+        left: -4px;
+        cursor: nw-resize;
+    }
+
+    .point-mt {
+        top: -5px;
+        left: 50%;
+        margin-left: -3px;
+        cursor: n-resize;
+    }
+
+    .point-rt {
+        top: -4px;
+        right: -4px;
+        cursor: ne-resize;
+    }
+
+    .point-ml {
+        top: 50%;
+        left: -4px;
+        margin-top: -3px;
+        cursor: w-resize;
+    }
+
+    .point-mr {
+        top: 50%;
+        right: -4px;
+        margin-top: -3px;
+        cursor: e-resize;
+    }
+
+    .point-lb {
+        bottom: -5px;
+        left: -4px;
+        cursor: sw-resize;
+    }
+
+    .point-mb {
+        bottom: -5px;
+        left: 50%;
+        margin-left: -3px;
+        cursor: s-resize;
+    }
+
+    .point-rb {
+        bottom: -5px;
+        right: -4px;
+        cursor: se-resize;
+    }
+</style>

+ 33 - 0
components/tui-chat/message-elements/audio-message/index.css

@@ -0,0 +1,33 @@
+.audio-message {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	max-width: 60vw;
+	line-height: 52rpx;
+	padding: 12rpx 24rpx;
+	background: #1F1A30;
+	color: #ffffff;
+	border-radius: 16rpx 4rpx 16rpx 16rpx;
+}
+
+.audio-icon {
+	width: 28rpx;
+	height: 28rpx;
+}
+
+.my-text {
+	border-radius: 16rpx 4rpx 16rpx 16rpx;
+	background: #6C52F4;
+}
+
+.audio {
+	/*border-radius: 2px 10px 10px 10px;*/
+	height: 60rpx;
+	font-family: PingFangSC-Medium;
+	font-size: 28rpx;
+	color: #ffffff;
+	line-height: 28rpx;
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+}

+ 69 - 0
components/tui-chat/message-elements/audio-message/index.vue

@@ -0,0 +1,69 @@
+<template>
+	<view :class="'audio-message ' + (isMine ? 'my-audio' : '')">
+		<image :src="`${assetsUrl}talk-voice-msg${isPlaying?'-playing':''}.png`" mode="aspectFit" class="audio-icon"></image>
+		<view class="audio " @click="handlePlayAudioMessage" :style="'width: ' + 120 + 'rpx'">{{ '" ' + message.payload.second }}</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			audio: {},
+			assetsUrl:this.$util.assetsUrl,
+			isPlaying:false
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object,
+			default: () => {}
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.message=newVal;
+				// this.setData({
+				// 	message: newVal
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+
+	beforeMount() {
+		this.audio = uni.createInnerAudioContext();
+		this.audio.onPlay(() => {});
+		this.audio.onEnded(() => {
+			this.isPlaying=false;
+		});
+		this.audio.onError(e => {
+			console.error(e, 'onError');
+			// ios 音频播放无声,可能是因为系统开启了静音模式
+			uni.showToast({
+				icon: 'none',
+				title: '该音频暂不支持播放'
+			});
+		});
+	},
+
+	methods: {
+		handlePlayAudioMessage() {
+			this.audio.src = this.message.payload.url;
+			this.isPlaying=true;
+			this.audio.play();
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 88 - 0
components/tui-chat/message-elements/custom-message/index.css

@@ -0,0 +1,88 @@
+
+.image-message{
+	width: 300rpx;
+}
+.msg-img{
+	width: 300rpx;
+	border-radius: 2px 10px 10px 10px;
+}
+.video-message{
+	width: 160px;
+	height: 120px;
+	background-color: rgba(0, 0, 0, 0.2);
+	border-radius: 2px 10px 10px 10px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	position: relative;
+	overflow: hidden;
+}
+.poster{
+	width: 100%;
+	height: 100%;
+	position: absolute;
+	left: 0;
+	right: 0;
+	z-index: 1;
+}
+.video-icon {
+	position: relative;
+	z-index: 10;
+	display: block;
+	width: 32px;
+	height: 32px;
+}
+
+.message-body-span {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	/*justify-content: flex-start;*/
+	flex-wrap: wrap;
+	outline: none;
+	font-size: 28rpx;
+	color: #ffffff;
+	position: relative;
+	max-width: 60vw;
+}
+
+.message-body-span-text {
+	width: 100%;
+	display: inline;
+	word-wrap: break-word;
+	word-break: break-all;
+	color: #ffffff;
+}
+
+.custom-content-text {
+	font-family: PingFangSC-Regular;
+	height: 25px;
+	line-height: 25px;
+	font-size: 28rpx;
+	letter-spacing: 0;
+}
+.system-message{
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	border-radius: 16rpx 4rpx 16rpx 16rpx;
+	width:100vw;
+}
+.err-title{
+	color: #ffffff;
+	font-size: 28rpx;
+	padding-top: 30rpx;
+}
+.err-line{
+	width: 70%;
+	padding: 20rpx 24rpx;
+	margin: 0 auto;
+	text-align: center;
+}
+
+.err-text{
+	font-size: 20rpx;
+	color: #ffffff;
+	text-align: center;
+}

+ 178 - 0
components/tui-chat/message-elements/custom-message/index.vue

@@ -0,0 +1,178 @@
+<template>
+	<view>
+		<view v-if="renderDom.type === 'image'" class="image-message">
+			<image :src="renderDom.imageUrl" mode="widthFix" class="msg-img" @click="previewImage"></image>
+		</view>
+		<view v-if="renderDom.type === 'video'" class="video-message" @click="playerHander">
+			<image :src="renderDom.videoCover" mode="aspectFill" class="poster"></image>
+			<image :src="`${assetsUrl}talk-playing.png`" mode="aspectFit" class="video-icon"></image>
+		</view>
+		<view v-if="renderDom.type === 'system'" class="system-message" @click="playerHander">
+			<view class="err-title" v-if="renderDom.text!==''">
+				{{renderDom.text}}
+			</view>
+			<view class="err-line">
+				<text class="err-text">{{renderDom.context}}</text>
+				<text class="err-text" @click="toPath(renderDom.highlightLink)" :style="{'color':`${renderDom.highlightColor}`}">
+					{{renderDom.highlightContext}}
+				</text>
+			</view>
+			
+		</view>
+		<view v-if="renderDom.type === 'notSupport'" class="message-body-span text-message">
+			<view class="message-body-span-text">{{ renderDom[0].text }}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { formateTime } from '../../../base/common.js';
+export default {
+	data() {
+		return {
+			renderDom:null,
+			assetsUrl:this.$util.assetsUrl
+		};
+	},
+	components: {},
+	props: {
+		message: {
+			type: Object,
+			default: () => {}
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.message=newVal;
+				this.renderDom=this.parseCustom(newVal);
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	methods: {
+		// 解析音视频通话消息
+		extractCallingInfoFromMessage(message) {
+			const callingmessage = JSON.parse(message.payload.data);
+			if (callingmessage.businessID !== 1) {
+				return '';
+			}
+			const objectData = JSON.parse(callingmessage.data);
+			switch (callingmessage.actionType) {
+				case 1: {
+					if (objectData.call_end >= 0 && !callingmessage.groupID) {
+						return `通话时长:${formateTime(objectData.call_end)}`;
+					}
+					if (callingmessage.groupID) {
+						return '结束群聊';
+					}
+					if (objectData.data && objectData.data.cmd === 'switchToAudio') {
+						return '切换语音通话';
+					}
+					if (objectData.data && objectData.data.cmd === 'switchToVideo') {
+						return '切换视频通话';
+					}
+					return '发起通话';
+				}
+				case 2:
+					return '取消通话';
+				case 3:
+					if (objectData.data && objectData.data.cmd === 'switchToAudio') {
+						return '切换语音通话';
+					}
+					if (objectData.data && objectData.data.cmd === 'switchToVideo') {
+						return '切换视频通话';
+					}
+					return '已接听';
+				case 4:
+					return '拒绝通话';
+				case 5:
+					if (objectData.data && objectData.data.cmd === 'switchToAudio') {
+						return '切换语音通话';
+					}
+					if (objectData.data && objectData.data.cmd === 'switchToVideo') {
+						return '切换视频通话';
+					}
+					return '无应答';
+				default:
+					return '';
+			}
+		},
+		parseCustom(message) {
+			// 约定自定义消息的 data 字段作为区分,不解析的不进行展示
+			let data=JSON.parse(message.payload.data);
+			console.log(data)
+			if(data.type===5){
+				const renderDom={
+					type:'image',
+					name:'custom',
+					imageUrl:data.path,
+				}
+				return renderDom;
+			}
+			if(data.type===6){
+				const renderDom={
+					type:'video',
+					name:'custom',
+					videoUrl:data.path,
+					videoCover:`${data.path}?x-oss-process=video/snapshot,w_0,h_0,t_10,ar_auto,f_jpg`,
+					width:data.width,
+					height:data.height
+				}
+				return renderDom;
+			}
+			if(data.type===99&&data.systemMsgType===10){
+				const renderDom={
+					type:'system',
+					name:'custom',
+					text:'该消息已被屏蔽',
+					flow:'out',
+					context:data.context.substring(0,data.context.indexOf(data.highlightContext)),
+					link:`/pages/webview/webview?url=${this.$util.protocal.behaviorStandar}`,
+					highlightColor:`#${data.highlightColor.substring(2)}`,
+					highlightContext:data.highlightContext
+				}
+				return renderDom;
+			}
+			if(data.type===99&&data.systemMsgType===4){
+				const renderDom={
+					type:'system',
+					name:'custom',
+					text:'',
+					flow:'out',
+					context:data.context.substring(data.context.indexOf(data.highlightContext),data.context.length),
+					link:`/pages/mine/album`,
+					highlightColor:`#${data.highlightColor.substring(2)}`,
+					highlightContext:data.highlightContext
+				}
+				return renderDom;
+			}
+			return {type: 'notSupport',text: '[自定义消息]'};
+		},
+		previewImage() {
+			uni.previewImage({
+				current: this.renderDom.imageUrl,
+				// 当前显示图片的http链接
+				urls: [this.renderDom.imageUrl]
+			});
+		},
+		playerHander() {
+			uni.$emit('videoPlayerHandler', {
+				isPlay: true,
+				message: this.renderDom
+			});
+		},
+		stopHander() {
+			this.isPlay = false;
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 18 - 0
components/tui-chat/message-elements/emoji/index.css

@@ -0,0 +1,18 @@
+.emoji-box{
+	padding: 0 11rpx;
+	box-sizing: border-box;
+	height: 400rpx;
+	flex-wrap: wrap;
+	transition: height .3s;
+	overflow-y: scroll;
+}
+.emoji-item{
+	width: 64rpx;
+	height: 64rpx;
+	display: inline;
+	
+}
+.emoji-img{
+	width: 64rpx;
+	height: 64rpx;
+}

+ 48 - 0
components/tui-chat/message-elements/emoji/index.vue

@@ -0,0 +1,48 @@
+<template>
+	<view class="emoji-box flex-start">
+		<view v-for="(item, index) in emojiList" :key="index" class="emoji-item">
+			<image :data-name="item.emojiName" class="emoji-img" :src="item.url" @tap="handleEnterEmoji"></image>
+		</view>
+	</view>
+</template>
+
+<script>
+import { emojiName, emojiUrl, emojiMap } from '../../../base/emojiMap';
+
+export default {
+	data() {
+		return {
+			emojiList: []
+		};
+	},
+
+	components: {},
+	props: {},
+
+	beforeMount() {
+		for (let i = 0; i < emojiName.length; i++) {
+			this.emojiList.push({
+				emojiName: emojiName[i],
+				url: emojiUrl + emojiMap[emojiName[i]]
+			});
+		}
+		
+		// this.setData({
+		// 	emojiList: this.emojiList
+		// });
+	},
+
+	methods: {
+		handleEnterEmoji(event) {
+			this.$emit('enterEmoji', {
+				detail: {
+					message: event.currentTarget.dataset.name
+				}
+			});
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 13 - 0
components/tui-chat/message-elements/face-message/index.css

@@ -0,0 +1,13 @@
+.TUI-faceMessage {
+  width: 150px;
+  height: 110px;
+  max-width: 60vw;
+}
+.face-message {
+  width: 100%;
+  height: 100%;
+  border-radius: 10px 10px 10px 10px;
+}
+.my-image {
+  border-radius: 10px 2px 10px 10px;
+}

+ 58 - 0
components/tui-chat/message-elements/face-message/index.vue

@@ -0,0 +1,58 @@
+<template>
+	<view class="TUI-faceMessage" @tap="previewImage"><image class="face-message" :src="renderDom.src"></image></view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			renderDom: [],
+			percent: 0,
+			faceUrl: 'https://web.sdk.qcloud.com/im/assets/face-elem/'
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.renderDom=this.parseFace(newVal);
+				// this.setData({
+				// 	renderDom: this.parseFace(newVal)
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	methods: {
+		// 解析face 消息
+		parseFace(message) {
+			const renderDom = {
+				src: `${this.faceUrl + message.payload.data}@2x.png`
+			};
+			return renderDom;
+		},
+
+		previewImage() {
+			uni.previewImage({
+				current: this.renderDom[0].src,
+				// 当前显示图片的http链接
+				urls: [this.renderDom[0].src]
+			});
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 65 - 0
components/tui-chat/message-elements/file-message/index.css

@@ -0,0 +1,65 @@
+.TUI-fileMessage {
+	display: flex;
+	padding: 10rpx 24rpx;
+	background-color: #fff;
+	border-radius: 2px 10px 10px 10px;
+	border: 1px solid #D9D9D9;
+}
+
+.fileMessage {
+	display: flex;
+}
+
+.fileMessage-box {
+	display: flex;
+	background: white;
+	align-items: center;
+	height: 150rpx;
+}
+
+.file-icon {
+	width: 80rpx;
+	height: 80rpx;
+}
+
+.pop {
+	position: fixed;
+	width: 50%;
+	bottom: 400rpx;
+	margin-left: 90rpx;
+	background: rgba(0, 0, 0, 0.3);
+	z-index: 99999;
+}
+
+.text-box {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 112rpx;
+}
+
+.download-confirm {
+	font-family: PingFangSC-Regular;
+	font-size: 16px;
+	color: #E85454;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 22px;
+}
+
+.abandon {
+	opacity: 0.8;
+	font-family: PingFangSC-Regular;
+	font-size: 16px;
+	color: #FFFFFF;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 22px;
+}
+
+.file-title {
+	max-width: 53vw;
+	display: inline;
+	word-wrap: break-word;
+	word-break: break-all;
+}

+ 86 - 0
components/tui-chat/message-elements/file-message/index.vue

@@ -0,0 +1,86 @@
+<template>
+	<view>
+		<view class="TUI-fileMessage">
+			<view class="fileMessage">
+				<view class="fileMessage-box">
+					<image class="file-icon" src="/static/static/images/file.png"></image>
+					<label @tap="download" class="file-title">{{ filePayload.fileName }}</label>
+				</view>
+			</view>
+		</view>
+		<view class="pop" v-if="Show">
+			<view class="text-box"><text class="download-confirm" @tap.stop="downloadConfirm">下载</text></view>
+			<view class="text-box"><text class="abandon" @tap="cancel">取消</text></view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			Show: false,
+			filePayload: {}
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object,
+			default: () => {}
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.filePayload= newVal.payload;
+				// this.setData({
+				// 	filePayload: newVal.payload
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	methods: {
+		download() {
+			this.Show=true;
+			// this.setData({
+			// 	Show: true
+			// });
+		},
+
+		downloadConfirm() {
+			uni.downloadFile({
+				url: this.filePayload.fileUrl,
+
+				success(res) {
+					const filePath = res.tempFilePath;
+					uni.openDocument({
+						filePath,
+
+						success() {
+							console.log('打开文档成功');
+						}
+					});
+				}
+			});
+		},
+
+		cancel() {
+			this.Show=false;
+			// this.setData({
+			// 	Show: false
+			// });
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 13 - 0
components/tui-chat/message-elements/image-message/index.css

@@ -0,0 +1,13 @@
+.TUI-ImageMessage {
+	width: 150px;
+}
+
+.image-message {
+	width: 100%;
+	height: 100%;
+	border-radius: 10px 10px 10px 10px;
+}
+
+.my-image {
+	border-radius: 10px 2px 10px 10px;
+}

+ 52 - 0
components/tui-chat/message-elements/image-message/index.vue

@@ -0,0 +1,52 @@
+<template>
+	<view class="TUI-ImageMessage" @tap="previewImage">
+		<image :class="'image-message ' + (isMine ? 'my-image' : '')" mode="widthFix" :src="renderDom[0].src"></image>
+	</view>
+</template>
+
+<script>
+import { parseImage } from '../../../base/message-facade';
+
+export default {
+	data() {
+		return {
+			renderDom: [],
+			percent: 0
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object,
+			default: ''
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.renderDom=parseImage(newVal);
+				this.percent=newVal.percent;
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	methods: {
+		previewImage() {
+			uni.previewImage({
+				current: this.renderDom[0].src,
+				// 当前显示图片的http链接
+				urls: [this.renderDom[0].src]
+			});
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 29 - 0
components/tui-chat/message-elements/system-message/index.css

@@ -0,0 +1,29 @@
+.container {
+	width: 100%;
+	height: 200rpx;
+}
+
+.handle {
+	display: flex;
+	justify-content: space-between;
+}
+
+.card {
+	font-size: 14px;
+	margin: 20px;
+	padding: 20px;
+	box-sizing: border-box;
+	border: 1px solid #abdcff;
+	background-color: #f0faff;
+	border-radius: 12px;
+}
+
+.time {}
+
+.button {
+	color: blue;
+	border-radius: 8px;
+	line-height: 30px;
+	font-size: 16px;
+	width: 70px;
+}

+ 88 - 0
components/tui-chat/message-elements/system-message/index.vue

@@ -0,0 +1,88 @@
+<template>
+	<view>
+		<view v-if="message.payload.operationType === 1" class="card handle">
+			<view>
+				<view class="time">{{ messageTime }}</view>
+				{{ renderDom }}
+			</view>
+			<view class="choose"><view class="button" @tap="handleClick">处理</view></view>
+		</view>
+		<view class="card" v-else>
+			<view class="time">{{ messageTime }}</view>
+			{{ renderDom }}
+		</view>
+	</view>
+</template>
+
+<script>
+import { parseGroupSystemNotice } from '../../../base/message-facade';
+import { caculateTimeago } from '../../../base/common';
+
+export default {
+	data() {
+		return {
+			// message: {},
+			messageTime: '',
+			renderDom: ''
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.messageTime=caculateTimeago(newVal.time * 1000);
+				this.renderDom=parseGroupSystemNotice(newVal);
+				// this.setData({
+				// 	messageTime: caculateTimeago(newVal.time * 1000),
+				// 	renderDom: parseGroupSystemNotice(newVal)
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+
+	methods: {
+		handleClick() {
+			uni.showActionSheet({
+				itemList: ['同意', '拒绝'],
+				success: res => {
+					const option = {
+						handleAction: 'Agree',
+						handleMessage: '欢迎进群',
+						message: this.message
+					};
+
+					if (res.tapIndex === 1) {
+						option.handleAction = 'Reject';
+						option.handleMessage = '拒绝申请';
+					}
+
+					uni.$TUIKit
+						.handleGroupApplication(option)
+						.then(() => {
+							uni.showToast({
+								title: option.handleAction === 'Agree' ? '已同意申请' : '已拒绝申请'
+							});
+						})
+						.catch(error => {
+							uni.showToast({
+								title: error.message || '处理失败',
+								icon: 'none'
+							});
+						});
+				}
+			});
+		}
+	}
+};
+</script>
+<style scoped>
+@import './index.css';
+</style>

+ 46 - 0
components/tui-chat/message-elements/text-message/index.css

@@ -0,0 +1,46 @@
+.text-message {
+	display: inline-flex;
+	max-width: 60vw;
+	line-height: 52rpx;
+	padding: 12rpx 24rpx;
+	background: #1F1A30;
+	color: #ffffff;
+	border-radius: 16rpx 4rpx 16rpx 16rpx;
+}
+
+.my-text {
+	border-radius: 16rpx 4rpx 16rpx 16rpx;
+	background: #6C52F4;
+}
+
+.message-body-span {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	/*justify-content: flex-start;*/
+	flex-wrap: wrap;
+	outline: none;
+	font-size: 28rpx;
+	color: #ffffff;
+	position: relative;
+	max-width: 60vw;
+}
+
+.message-body-span-text {
+	width: 100%;
+	display: inline;
+	word-wrap: break-word;
+	word-break: break-all;
+}
+
+.message-body-span-image {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	margin: 0 4rpx;
+}
+
+.emoji-icon {
+	width: 20px;
+	height: 20px;
+}

+ 56 - 0
components/tui-chat/message-elements/text-message/index.vue

@@ -0,0 +1,56 @@
+<template>
+	<view :class="'text-message ' + (isMine ? 'my-text' : '')">
+		<view v-for="(item, index) in renderDom" :key="index" class="message-body-span">
+			<span class="message-body-span-text" v-if="item.name === 'span'">{{ item.text }}</span>
+			<image v-if="item.name === 'img'" class="emoji-icon" :src="item.src"></image>
+		</view>
+	</view>
+</template>
+
+<script>
+import { parseText } from '../../../base/message-facade';
+
+export default {
+	data() {
+		return {
+			renderDom: []
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.renderDom=parseText(newVal)
+				// this.setData({
+				// 	renderDom: parseText(newVal)
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+
+	beforeMount() {
+		// 在组件实例进入页面节点树时执行
+	},
+
+	destroyed() {
+		// 在组件实例被从页面节点树移除时执行
+	},
+
+	methods: {}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 9 - 0
components/tui-chat/message-elements/tip-message/index.css

@@ -0,0 +1,9 @@
+.tip-message {
+	width: 100%;
+}
+
+.text-message {
+	text-align: center;
+	font-size: 12px;
+	color: #999999;
+}

+ 47 - 0
components/tui-chat/message-elements/tip-message/index.vue

@@ -0,0 +1,47 @@
+<template>
+	<view class="tip-message">
+		<view class="text-message">{{ renderDom[0].text }}</view>
+	</view>
+</template>
+
+<script>
+import { parseGroupTip } from '../../../base/message-facade';
+
+export default {
+	data() {
+		return {};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.renderDom=parseGroupTip(newVal);
+				// this.setData({
+				// 	renderDom: parseGroupTip(newVal)
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+
+	beforeMount() {
+		// 在组件实例进入页面节点树时执行
+	},
+
+	destroyed() {
+		// 在组件实例被从页面节点树移除时执行
+	},
+
+	methods: {}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 38 - 0
components/tui-chat/message-elements/video-message/index.css

@@ -0,0 +1,38 @@
+.video-message {}
+
+.video-box {
+	width: 160px;
+	height: 120px;
+	background-color: rgba(0, 0, 0, 0.2);
+	border-radius: 10px 2px 10px 10px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	position: relative;
+	overflow: hidden;
+}
+
+.my-video {
+	border-radius: 10px 2px 10px 10px;
+}
+
+.icon {
+	position: absolute;
+	bottom: 30px;
+	right: 30px;
+}
+.poster{
+	width: 100%;
+	height: 100%;
+	position: absolute;
+	left: 0;
+	right: 0;
+	z-index: 1;
+}
+.video-icon {
+	position: relative;
+	z-index: 10;
+	display: block;
+	width: 32px;
+	height: 32px;
+}

+ 58 - 0
components/tui-chat/message-elements/video-message/index.vue

@@ -0,0 +1,58 @@
+<template>
+	<view :class="'video-box ' + (isMine ? 'my-video' : '')" @click="playerHander">
+		<image :src="message.payload.thumbUrl" mode="aspectFill" class="poster"></image>
+		<image :src="`${assetsUrl}talk-playing.png`" mode="aspectFit" class="video-icon"></image>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			isPlay: false,
+			assetsUrl:this.$util.assetsUrl,
+		};
+	},
+
+	components: {},
+	props: {
+		message: {
+			type: Object,
+			default: () => {}
+		},
+		isMine: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		message: {
+			handler: function(newVal) {
+				this.message=newVal;
+				// this.setData({
+				// 	message: newVal
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	onLoad() {
+		console.log(this.message)
+	},
+	methods: {
+		playerHander() {
+			uni.$emit('videoPlayerHandler', {
+				isPlay: true,
+				message: this.message
+			});
+		},
+		stopHander() {
+			this.isPlay = false;
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 68 - 0
components/tui-chat/message-list/index.css

@@ -0,0 +1,68 @@
+.message-list-container {
+	width: 100%;
+	height: 100%;
+}
+
+.t-message-item {
+	/*max-width: 60vw;*/
+	padding: 16rpx 0;
+}
+
+.t-recieve-message {
+	display: flex;
+	flex-direction: row;
+	justify-items: flex-end;
+	align-items:flex-start;
+	width: 100vw;
+}
+
+.t-message-avatar {
+	margin-left: 20rpx;
+	margin-right: 12rpx;
+	border-radius: 10rpx;
+	width: 80rpx;
+	height: 80rpx;
+}
+
+.t-self-message {
+	display: flex;
+	flex-direction: row;
+	justify-content: flex-end;
+	/*align-items: center;*/
+	width: 100vw;
+}
+
+.t-self-message-body {
+	display: flex;
+	justify-content: flex-start;
+	flex-wrap: wrap;
+	outline: none;
+}
+
+.t-recieve-message-body {
+	display: flex;
+	justify-content: flex-start;
+	flex-wrap: wrap;
+	outline: none;
+	/*background: #F8F8F8;*/
+	border-radius: 2px 10px 10px 10px;
+	margin-left: 8rpx;
+
+}
+
+.read-receipts {
+	line-height: 42px;
+	height: 42px;
+	font-size: 12px;
+	color: #6e7981;
+	margin-right: 10px
+}
+
+.no-message {
+	text-align: center;
+	width: 100%;
+	font-size: 24rpx;
+	color: #a5b5c1;
+	height: 60rpx;
+	line-height: 60rpx;
+}

+ 252 - 0
components/tui-chat/message-list/index.vue

@@ -0,0 +1,252 @@
+<template>
+	<scroll-view
+		class="scroll-view"
+		scroll-y="true" 
+		:style="{'height': `${scrollHeight}px`,'margin-top':`${topbarOffsetHeight}px`}"
+		v-if="scrollHeight>0"
+		lower-threshold="200"
+		:scroll-top="scrollTop"
+		:scroll-into-view="scrollTo"
+		scroll-with-animation="true"
+		refresher-enabled="true"
+		:refresher-triggered="scrollTriggered"
+		:refresher-threshold="45" 
+		refresher-default-style="white"
+		refresher-background="#151126" 
+		@refresherrefresh="scrollRefresh" 
+		@refresherpulling="scrollPulling"
+		@refresherrestore="scrollRestore" 
+		@refresherabort="scrollAbort"
+		@scrolltolower="scrollToBottom"
+	>
+		<view id="message-scroll" style="width:100%">
+			<view class="no-message" v-if="isCompleted">没有更多啦</view>
+			<view v-for="(item,index) in messageList" :key="index" class="t-message">
+				<view v-if="conversation.type !== '@TIM#SYSTEM'" :id="item.ID">
+					<view class="t-message-item">
+						<TUI-TipMessage v-if="item.type === 'TIMGroupTipElem'" :message="item"></TUI-TipMessage>
+						<view v-if="item.type !== 'TIMGroupTipElem'" :class="item.flow === 'out'||(item.flow === 'in'&&item.payload.data&&JSON.parse(item.payload.data).type===99) ? 't-self-message' : 't-recieve-message'">
+							<image
+								class="t-message-avatar"
+								v-if="(item.flow === 'in'&&item.type !== 'TIMCustomElem')||(item.flow === 'in'&&(item.payload.data&&JSON.parse(item.payload.data).type!==99))"
+								:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+							></image>
+							<view class="read-receipts" v-if="conversation.type === 'C2C' && item.flow === 'out'">
+								<view v-if="item.isPeerRead">已读</view>
+								<view v-else>未读</view>
+							</view>
+							<view>
+								<TUI-TextMessage v-if="item.type === 'TIMTextElem'" :message="item" :isMine="item.flow === 'out'"></TUI-TextMessage>
+								<TUI-ImageMessage v-if="item.type === 'TIMImageElem'" :message="item" :isMine="item.flow === 'out'"></TUI-ImageMessage>
+								<TUI-VideoMessage v-if="item.type === 'TIMVideoFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-VideoMessage>
+								<TUI-AudioMessage v-if="item.type === 'TIMSoundElem'" :message="item" :isMine="item.flow === 'out'"></TUI-AudioMessage>
+								<TUI-CustomMessage v-if="item.type === 'TIMCustomElem'" :message="item" :isMine="item.flow === 'out'"></TUI-CustomMessage>
+								<TUI-FaceMessage v-if="item.type === 'TIMFaceElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FaceMessage>
+								<TUI-FileMessage v-if="item.type === 'TIMFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FileMessage>
+							</view>
+							<image
+								class="t-message-avatar"
+								v-if="item.flow === 'out'"
+								:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+							></image>
+							<!-- <image
+								class="t-message-avatar"
+								v-if="item.flow === 'in'&&(item.payload.data&&JSON.parse(item.payload.data).type!==99)"
+								:src="mineUserInfo.icon || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+							></image> -->
+						</view>
+					</view>
+				</view>
+				<view v-else :id="item.ID" :data-value="item.ID"><TUI-SystemMessage :message="item"></TUI-SystemMessage></view>
+			</view>
+		</view>
+		<view :id="endItem"></view>
+	</scroll-view>
+</template>
+
+<script>
+import TUITextMessage from '../message-elements/text-message/index';
+import TUIImageMessage from '../message-elements/image-message/index';
+import TUIVideoMessage from '../message-elements/video-message/index';
+import TUIAudioMessage from '../message-elements/audio-message/index';
+import TUICustomMessage from '../message-elements/custom-message/index';
+import TUITipMessage from '../message-elements/tip-message/index';
+import TUISystemMessage from '../message-elements/system-message/index';
+import TUIFaceMessage from '../message-elements/face-message/index';
+import TUIFileMessage from '../message-elements/file-message/index';
+
+export default {
+	data() {
+		return {
+			// 当前会话
+			messageList: [],
+			// 自己的 ID 用于区分历史消息中,哪部分是自己发出的
+			scrollView: '',
+			scrollTo: '',
+			endItem:'',
+			scrollTop:null,
+			scrollRefreshing:false,
+			scrollTriggered:true,
+			nextReqMessageID: '',
+			isSend:false,
+			// 下一条消息标志
+			isCompleted: false ,// 当前会话消息是否已经请求完毕
+		};
+	},
+
+	components: {
+		TUITextMessage,
+		TUIImageMessage,
+		TUIVideoMessage,
+		TUIAudioMessage,
+		TUICustomMessage,
+		TUITipMessage,
+		TUISystemMessage,
+		TUIFaceMessage,
+		TUIFileMessage
+	},
+	props: {
+		conversation: {
+			type: Object,
+			default: () => {}
+		},
+		topbarOffsetHeight:{
+			type:Number,
+			default:0
+		},
+		scrollHeight:{
+			type:Number,
+			default:0
+		},
+	},
+	watch: {
+		conversation: {
+			handler: function(newVal) {//发送消息不进行全部拉取
+				if (newVal&&newVal.conversationID&&!this.isSend) {
+					console.log(newVal.conversationID)
+					this.conversation=newVal;
+					this.getMessageList(newVal);
+				};
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	computed:{
+		mineUserInfo(){
+			return JSON.parse(uni.getStorageSync('userInfo'));
+		}
+	},
+	mounted() {
+		uni.$TUIKit.on(uni.$TUIKitEvent.MESSAGE_RECEIVED, this.$onMessageReceived, this);
+		uni.$TUIKit.on(uni.$TUIKitEvent.MESSAGE_READ_BY_PEER, this.$onMessageReadByPeer, this);
+		this.$EventBus.$on('sendMessage', (message)=>{
+			this.isSend=true;
+			this.updateMessageList(message);
+		})
+	},
+	destroyed() {
+		// 一定要解除相关的事件绑定
+		uni.$TUIKit.off(uni.$TUIKitEvent.MESSAGE_RECEIVED, this.$onMessageReceived);
+	},
+	methods: {
+		/**
+		 * 推荐下拉刷新、加载更多
+		 */
+		scrollPulling(e) {},
+		scrollRestore() {this.scrollTriggered = true;},
+		scrollAbort() {},
+		scrollToBottom(){},
+		scrollRefresh() {
+			if (this.scrollRefreshing||this.isCompleted)
+			{
+				this.isSend=false;
+				this.isCompleted=true;
+				this.scrollTriggered=false;
+				this.scrollRefreshing = false;
+				return;
+			}
+			this.scrollRefreshing = true;
+			this.getMessageList(this.conversation);
+			setTimeout(() => {
+				this.scrollTriggered = false;
+				this.scrollRefreshing = false;
+			}, 1000);
+		},
+
+		getMessageList(conversation) {
+			if (!this.isCompleted) {
+				uni.$TUIKit
+					.getMessageList({
+						conversationID: conversation.conversationID,
+						nextReqMessageID: this.nextReqMessageID,
+						count: 15
+					})
+					.then(res => {
+						const { messageList } = res.data; // 消息列表。
+						console.log(res)
+						this.nextReqMessageID = res.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
+						this.isCompleted = res.data.isCompleted; // 表示是否已经拉完所有消息。
+						this.messageList = [...messageList, ...this.messageList];
+						this.$handleMessageRender(this.messageList, messageList);
+					});
+			}
+		},
+
+		// 自己的消息上屏
+		updateMessageList(msg) {
+			this.messageList.push(msg);
+			this.scrollToButtom();
+		},
+
+		// 消息已读更新
+		$onMessageReadByPeer() {
+			this.messageList=this.messageList
+		},
+		scrollToButtom() {
+			const query = uni.createSelectorQuery().in(this);
+			let nodesRef = query.select('#message-scroll');
+			nodesRef
+				.boundingClientRect(res => {
+					if(res){
+						this.$nextTick(() => {
+							//进入页面滚动到底部
+							this.scrollTop = res.height;
+							// this.endItem='view_id_' + parseInt(Math.random() * 1000000)
+							// this.scrollTo=this.endItem;
+							console.log('滑动到底部了')
+						});
+					}
+					
+				})
+				.exec();
+		},
+		// 收到的消息
+		$onMessageReceived(value) {
+			this.isSend=true;
+			this.messageList.push(value.data[0]);
+			this.scrollToButtom();
+		},
+
+		// 历史消息渲染
+		$handleMessageRender(messageList) {
+			if (messageList.length > 0) {
+				this.messageList=messageList;
+				setTimeout(()=>{
+					this.$nextTick(() => {
+						//进入页面滚动到底部
+						this.scrollTop = 99999;
+						// this.endItem='view_id_' + parseInt(Math.random() * 1000000)
+						// this.scrollTo=this.endItem;
+						console.log('滑动到底部了')
+					});
+				},500)
+				
+			}
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 86 - 0
components/tui-chat/message-private/common-words/index.css

@@ -0,0 +1,86 @@
+.tui-common-words-container {
+	position: fixed;
+	width: 100vw;
+	height: 100vh;
+	z-index: 100;
+	top: 0;
+	/*  #ifdef  H5  */
+	top: calc(88rpx + constant(safe-area-inset-top));
+	top: calc(88rpx + env(safe-area-inset-top));
+	/*  #endif  */
+	background: rgba(0, 0, 0, 0.5);
+}
+
+.tui-common-words-box {
+	position: absolute;
+	width: 100%;
+	height: 60%;
+	bottom: 0;
+	background: rgba(255, 255, 255, 1);
+	padding-bottom: 68rpx;
+	z-index: 200;
+}
+
+.tui-common-words-title {
+	display: flex;
+	flex-wrap: nowrap;
+	justify-content: space-between;
+	padding-left: 40rpx;
+	padding-right: 40rpx;
+	padding-top: 48rpx;
+	font-family: PingFangSC-Medium;
+	font-size: 36rpx;
+	color: #000000;
+	letter-spacing: 0;
+	line-height: 50rpx;
+}
+
+.tui-search-bar {
+	display: flex;
+	flex-wrap: nowrap;
+	align-items: center;
+	margin: 32rpx 40rpx;
+	width: 670rpx;
+	height: 80rpx;
+	background: #FFFFFF;
+	border-radius: 40rpx;
+	border-radius: 40rpx;
+	background-color: #F8F8F8;
+}
+
+.tui-searchcion {
+	display: inline-block;
+	margin-left: 24rpx;
+	width: 48rpx;
+	height: 48rpx;
+}
+
+.tui-search-bar-input {
+	margin-left: 16rpx;
+	line-height: 40rpx;
+	font-size: 28rpx;
+	width: 100%;
+	display: inline-block;
+}
+
+.tui-common-words-list {
+	position: absolute;
+	top: 242rpx;
+	bottom: 68rpx;
+	width: 750rpx;
+}
+
+.tui-common-words-item {
+	width: 750rpx;
+	height: 112rpx;
+	border-bottom: 2rpx solid #EEF0F3;
+	background-color: #FFFFFF;
+	font-family: PingFangSC-Regular;
+	font-size: 32rpx;
+	color: #333333;
+	letter-spacing: 0;
+	line-height: 44rpx;
+	padding: 0 40rpx;
+	display: flex;
+	align-items: center;
+}

+ 92 - 0
components/tui-chat/message-private/common-words/index.vue

@@ -0,0 +1,92 @@
+<template>
+	<view v-show="display" class="tui-common-words-container">
+		<view class="tui-common-words-box">
+			<view class="tui-common-words-title">
+				<view>请选择你要发送的常用语</view>
+				<view style="color: #006EFF; font-family: PingFangSC-Regular;" class="tui-common-words-close" @tap="handleClose">关闭</view>
+			</view>
+			<view class="tui-search-bar">
+				<image class="tui-searchcion" src="/static/static/assets/serach-icon.svg"></image>
+				<input class="tui-search-bar-input" :value="words" placeholder="请输入您想要提出的问题" @input="wordsInput" />
+			</view>
+			<scroll-view class="tui-common-words-list" scroll-y="true" enable-flex="true">
+				<view v-for="(item, index) in commonWordsMatch" :key="index" class="tui-common-words-item" @tap="sendMessage" :data-words="item">{{ item }}</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+const commonWordsList = [
+	'什么时候发货',
+	'发什么物流',
+	'为什么物流一直没更新',
+	'最新优惠',
+	'包邮吗',
+	'修改地址信息',
+	'修改收件人信息',
+	'物流一直显示正在揽收',
+	'问题A',
+	'问题B'
+];
+
+export default {
+	data() {
+		return {
+			words: '',
+			commonWordsMatch: commonWordsList
+		};
+	},
+
+	components: {},
+	props: {
+		display: {
+			type: Boolean,
+			default: false
+		}
+	},
+	watch: {
+		display: {
+			handler: function(newVal) {
+				// this.setData({
+				//   display: newVal
+				// });
+			},
+			immediate: true
+		}
+	},
+	methods: {
+		handleClose() {
+			this.$emit('close', {
+				detail: {
+					key: '0'
+				}
+			});
+		},
+
+		wordsInput(e) {
+			(this.commonWordsMatch = []),
+				commonWordsList.forEach(item => {
+					if (item.indexOf(e.detail.value) > -1) {
+						this.commonWordsMatch.push(item);
+					}
+				});
+			this.setData({
+				words: e.detail.value,
+				commonWordsMatch: this.commonWordsMatch
+			});
+		},
+
+		sendMessage(e) {
+			this.$emit('sendMessage', {
+				detail: {
+					message: e.currentTarget.dataset.words
+				}
+			});
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 178 - 0
components/tui-chat/message-private/order-list/index.css

@@ -0,0 +1,178 @@
+.tui-cards-container {
+	position: fixed;
+	width: 100vw;
+	height: 100vh;
+	z-index: 100;
+	top: 0;
+	/*  #ifdef  H5  */
+	top: calc(88rpx + constant(safe-area-inset-top));
+	top: calc(88rpx + env(safe-area-inset-top));
+	/*  #endif  */
+	background: rgba(0, 0, 0, 0.5);
+}
+
+.tui-cards-box {
+	position: absolute;
+	width: 100%;
+	height: 60%;
+	bottom: 0;
+	background: #F4F5F9;
+	padding-bottom: 68rpx;
+	z-index: 200;
+}
+
+.tui-cards-title {
+	display: flex;
+	flex-wrap: nowrap;
+	justify-content: space-between;
+	padding-left: 40rpx;
+	padding-right: 40rpx;
+	padding-top: 48rpx;
+	font-family: PingFangSC-Medium;
+	font-size: 36rpx;
+	color: #000000;
+	letter-spacing: 0;
+	line-height: 50rpx;
+}
+
+.tui-search-bar {
+	display: flex;
+	flex-wrap: nowrap;
+	align-items: center;
+	margin: 32rpx 40rpx;
+	width: 670rpx;
+	height: 80rpx;
+	background: #FFFFFF;
+	border-radius: 40rpx;
+	border-radius: 40rpx;
+	background-color: #F8F8F8;
+}
+
+.tui-searchcion {
+	display: inline-block;
+	margin-left: 24rpx;
+	width: 48rpx;
+	height: 48rpx;
+}
+
+.tui-search-bar-input {
+	margin-left: 16rpx;
+	line-height: 40rpx;
+	font-size: 28rpx;
+	width: 100%;
+	display: inline-block;
+}
+
+.tui-order-list {
+	position: absolute;
+	top: 242rpx;
+	bottom: 68rpx;
+	width: 750rpx;
+}
+
+.tui-order-item {
+	width: 670rpx;
+	margin: 32rpx 40rpx;
+	height: 388rpx;
+	background-color: #FFFFFF;
+	border-radius: 4px;
+	display: flex;
+	flex-wrap: wrap;
+	align-items: center;
+}
+
+.order-title {
+	width: 670rpx;
+	height: 102rpx;
+	padding: 32rpx 40rpx;
+	padding-bottom: 0;
+	border-bottom: 2rpx solid #EEF0F3;
+}
+
+.order-title>.order-number {
+	font-family: PingFangSC-Medium;
+	font-size: 32rpx;
+	line-height: 44rpx;
+	color: #000000;
+	letter-spacing: 0;
+	margin-bottom: 8rpx;
+}
+
+.order-title>.order-time {
+	font-family: PingFangSC-Regular;
+	font-size: 24rpx;
+	line-height: 34rpx;
+	color: #999999;
+	letter-spacing: 0;
+}
+
+.order-info {
+	display: flex;
+	flex-wrap: nowrap;
+	width: 670rpx;
+	height: 236rpx;
+	padding: 32rpx 40rpx;
+}
+
+.order-content {
+	width: 450rpx;
+	margin-left: 32rpx;
+}
+
+.order-content-title {
+	font-family: PingFangSC-Medium;
+	width: 378rpx;
+	line-height: 34rpx;
+	font-size: 24rpx;
+	color: #000000;
+	letter-spacing: 0;
+	margin-bottom: 12rpx;
+}
+
+.order-content-description {
+	display: flex;
+	flex-wrap: nowrap;
+	font-family: PingFangSC-Regular;
+	max-width: 410rpx;
+	line-height: 34rpx;
+	font-size: 24rpx;
+	color: #999999;
+	letter-spacing: 0;
+	margin-bottom: 12rpx;
+}
+
+.order-content-price {
+	font-family: PingFangSC-Medium;
+	font-size: 36rpx;
+	line-height: 50rpx;
+	color: #FF7201;
+	letter-spacing: 0;
+}
+
+.order-image {
+	width: 156rpx;
+	height: 156rpx;
+}
+
+.btn-send-order {
+	width: 176rpx;
+	height: 58rpx;
+	background-color: #006EFF;
+	border-radius: 14.5px;
+	font-family: PingFangSC-Regular;
+	font-size: 24rpx;
+	color: #FFFFFF;
+	line-height: 28px;
+	text-align: center;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	margin-right: 40rpx;
+}
+
+.btn-send-text {
+	font-family: PingFangSC-Regular;
+	font-size: 12px;
+	color: #FFFFFF;
+	line-height: 14px;
+}

+ 141 - 0
components/tui-chat/message-private/order-list/index.vue

@@ -0,0 +1,141 @@
+<template>
+	<view v-if="display" class="tui-cards-container">
+		<view class="tui-cards-box">
+			<view class="tui-cards-title">
+				<view>请选择你要发送的订单</view>
+				<view style="color: #006EFF; font-family: PingFangSC-Regular;" class="tui-cards-close" @tap="handleClose">关闭</view>
+			</view>
+			<view class="tui-search-bar">
+				<image class="tui-searchcion" src="/static/static/assets/serach-icon.svg"></image>
+				<input class="tui-search-bar-input" :value="words" placeholder="搜索" @input="wordsInput" />
+			</view>
+			<scroll-view class="tui-order-list" scroll-y="true" enable-flex="true">
+				<view v-for="(item, index) in orderMatch" :key="index" class="tui-order-item">
+					<view class="order-title">
+						<view class="order-number">订单编号: {{ item.orderNum }}</view>
+						<view class="order-time">{{ item.time }}</view>
+					</view>
+					<view class="order-info">
+						<image class="order-image" :src="item.imageUrl"></image>
+						<view class="order-content">
+							<view class="order-content-title">{{ item.title }}</view>
+							<view class="order-content-description">{{ item.description }}</view>
+							<view style="display: flex; flex-wrap: no-wrap; justify-content: space-between;">
+								<view class="order-content-price">{{ item.price }}</view>
+								<view class="btn-send-order" :data-order="item" @tap.stop="sendMessage"><text class="btn-send-text">发送此订单</text></view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+const orderList = [
+	{
+		orderNum: 1,
+		time: '2021-7-20 20:45',
+		title: '[天博检验]新冠核酸检测/预约',
+		description: '专业医学检测,电子报告',
+		imageUrl: 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/miles.jpeg',
+		price: '80元'
+	},
+	{
+		orderNum: 2,
+		time: '2021-7-20 22:45',
+		title: '[路边]新冠核酸检测/预约',
+		description: '专业医学检测,电子报告',
+		imageUrl: 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/miles.jpeg',
+		price: '7000元'
+	}
+];
+
+export default {
+	data() {
+		return {
+			words: '',
+			orderMatch: orderList
+		};
+	},
+
+	components: {},
+	props: {
+		display: {
+			type: Boolean,
+			default: false
+		},
+		conversation: {
+			type: Object,
+			default: () => {}
+		}
+	},
+	watch: {
+		display: {
+			handler: function(newVal) {
+				// this.setData({
+				//   display: newVal
+				// });
+			},
+			immediate: true
+		},
+		conversation: {
+			handler: function(newVal) {
+				this.conversation=newVal;
+				// this.setData({
+				// 	conversation: newVal
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+	methods: {
+		handleClose() {
+			this.$emit('close', {
+				detail: {
+					key: '1'
+				}
+			});
+		},
+
+		wordsInput(e) {
+			(this.orderMatch = []),
+				orderList.forEach(item => {
+					if (item.title.indexOf(e.detail.value) > -1 || item.orderNum === ~~e.detail.value) {
+						this.orderMatch.push(item);
+					}
+				});
+			this.newVal=e.detail.value;
+			this.orderMatch=orderMatch;
+			// this.setData({
+			// 	words: e.detail.value,
+			// 	orderMatch: this.orderMatch
+			// });
+		},
+
+		sendMessage(e) {
+			const { order } = e.currentTarget.dataset;
+			this.$emit('sendCustomMessage', {
+				detail: {
+					payload: {
+						// data 字段作为表示,可以自定义
+						data: 'order',
+						description: order.description,
+						// 获取骰子点数
+						extension: JSON.stringify({
+							title: order.title,
+							imageUrl: order.imageUrl,
+							price: order.price
+						})
+					}
+				}
+			});
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 93 - 0
components/tui-chat/message-private/service-evaluation/index.css

@@ -0,0 +1,93 @@
+.tui-cards-container {
+	position: fixed;
+	width: 100vw;
+	height: 100vh;
+	z-index: 100;
+	top: 0;
+	/*  #ifdef  H5  */
+	top: calc(88rpx + constant(safe-area-inset-top));
+	top: calc(88rpx + env(safe-area-inset-top));
+	/*  #endif  */
+	background: rgba(0, 0, 0, 0.5);
+}
+
+.service-evaluation {
+	position: absolute;
+	bottom: 0;
+	right: 0;
+	left: 0;
+	background: #FFFFFF;
+	padding: 48rpx 40rpx;
+}
+
+.header {
+	display: flex;
+	justify-content: space-between;
+	font-family: PingFangSC-Regular;
+}
+
+.btn {
+	width: 100%;
+	padding: 0;
+	margin: 0 auto;
+	text-align: center;
+	background: none;
+}
+
+.btn-close {
+	color: #006EFF;
+}
+
+.header-label {
+	font-size: 18px;
+	color: #000000;
+	letter-spacing: 0;
+	line-height: 25px;
+}
+
+.header .btn {
+	font-size: 16px;
+	color: #006EFF;
+	letter-spacing: 0;
+	line-height: 24px;
+}
+
+.main {
+	display: flex;
+	flex-direction: column;
+	padding: 48rpx 0;
+}
+
+.main-evaluation-score {
+	padding: 0 60rpx 48rpx;
+	display: flex;
+	justify-content: space-between;
+	align-items: flex-end;
+}
+
+.main-evaluation-score .score-star {
+	width: 72rpx;
+	height: 72rpx;
+}
+
+.main-textarea {
+	background: #F8F8F8;
+	border: 0 solid #D9D9D9;
+	border-radius: 4px;
+	font-size: 14px;
+	padding: 16rpx 32rpx;
+}
+
+.textarea-placeholder {
+	color: #B0B0B0;
+}
+
+.footer .btn {
+	width: 100%;
+	padding: 26rpx 0;
+	background: #006EFF;
+	border-radius: 24px;
+	border-radius: 24px;
+	font-size: 16px;
+	color: #FFFFFF;
+}

+ 120 - 0
components/tui-chat/message-private/service-evaluation/index.vue

@@ -0,0 +1,120 @@
+<template>
+	<view class="tui-cards-container" v-if="display">
+		<view class="service-evaluation">
+			<view class="header">
+				<label class="header-label">请对本次服务进行评价</label>
+				<view class="btn-close" @tap="handleClose">关闭</view>
+			</view>
+			<view class="main">
+				<view class="main-evaluation-score">
+					<image
+						v-for="(item, index) in scoreList"
+						:key="index"
+						class="score-star"
+						:data-score="item"
+						:src="'/static/static/images/star' + (item > score ? '-grey' : '') + '.png'"
+						@tap="handleScore"
+					></image>
+				</view>
+				<textarea
+					class="main-textarea"
+					cols="30"
+					rows="10"
+					@input="bindTextAreaInput"
+					placeholder="请输入评语"
+					placeholder-style="textarea-placeholder"
+				></textarea>
+			</view>
+			<view class="footer"><view class="btn" @tap="sendMessage" :disabled="score === 0 && !comment">提交评价</view></view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			scoreList: [1, 2, 3, 4, 5],
+			score: 5,
+			comment: ''
+		};
+	},
+
+	components: {},
+	props: {
+		display: {
+			type: Boolean,
+			default: ''
+		}
+	},
+	watch: {
+		display: {
+			handler: function(newVal) {},
+			immediate: true
+		}
+	},
+
+	onPageShow() {
+		this.score=0;
+		this.score='';
+		// this.setData({
+		// 	score: 0,
+		// 	comment: ''
+		// });
+	},
+
+	methods: {
+		handleClose() {
+			this.$emit('close', {
+				detail: {
+					key: '2'
+				}
+			});
+		},
+
+		handleScore(e) {
+			let { score } = e.currentTarget.dataset;
+
+			if (score === this.score) {
+				score = 0;
+			}
+			this.score=score;
+			// this.setData({
+			// 	score
+			// });
+		},
+
+		bindTextAreaInput(e) {
+			this.comment= e.detail.value;
+			// this.setData({
+			// 	comment: e.detail.value
+			// });
+		},
+
+		sendMessage() {
+			this.$emit('sendCustomMessage', {
+				detail: {
+					payload: {
+						// data 字段作为表示,可以自定义
+						data: 'evaluation',
+						description: '对本次服务的评价',
+						// 获取骰子点数
+						extension: JSON.stringify({
+							score: this.score,
+							comment: this.comment
+						})
+					}
+				}
+			});
+			this.setData({
+				score: 0,
+				comment: ''
+			});
+			this.handleClose();
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 92 - 0
components/tui-conversation/conversation-item/index.css

@@ -0,0 +1,92 @@
+.t-conversation-item-container {
+  width: 100vw;
+  height: 150rpx;
+  background-color: #FFFFFF;
+}
+
+.t-conversation-item {
+  width: 100vw;
+  height: 150rpx;
+  display: flex;
+  left: 0;
+  align-items: center;
+  justify-content: flex-start;
+  box-sizing: border-box;
+  border-bottom: 2rpx solid #EEF0F3;
+}
+.avatar-box{
+  position: relative;
+  display: inline-flex;
+}
+.t-conversation-item-avatar{
+  position: relative;
+  width: 96rpx;
+  height: 96rpx;
+  border-radius: 14rpx;
+  /*padding-left: 40rpx;*/
+  /*padding-right: 24rpx;*/
+  /*padding-bottom: 28rpx;*/
+  /*padding-top: 28rpx;*/
+  margin: 0 16rpx;
+  overflow: auto;
+}
+.t-conversation-item-content {
+  max-width: 60%;
+  flex: 1;
+  padding-left: 20rpx;
+}
+.t-conversation-item-info {
+  line-height: 34rpx;
+  font-size: 24rpx;
+  color: #999999;
+  margin-right: 30rpx;
+}
+
+.t-error {
+  background-color: #fb5250;
+  color: #fff;
+}
+
+.t-conversation-delete {
+  width: 144rpx;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #E85454;
+  color: #FFFFFF;
+  line-height: 44rpx;
+  font-size: 32rpx;
+}
+
+.tui-conversation-item-name {
+  line-height: 53rpx;
+  font-size: 36rpx;
+  font-family: 'PingFangSC-Regular';
+  color: #333333;
+}
+
+.tui-conversation-lastMessage {
+  line-height: 40rpx;
+  font-size: 28rpx;
+  font-family: 'PingFangSC-Regular';
+  color: #999999;
+  max-width: 90%;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+}
+.unread {
+  top: -10rpx;
+  right: 0rpx;
+  position: absolute;
+  padding: 0 10rpx;
+  height: 15px;
+  border-radius: 16rpx;
+  color: #ffffff;
+  background-color: red;
+}
+.read-text {
+  line-height: 15px;
+  font-size: 10px;
+}

+ 152 - 0
components/tui-conversation/conversation-item/index.vue

@@ -0,0 +1,152 @@
+<template>
+	<!--TODO: 默认图片在 cos 上添加 -->
+	<movable-area v-if="conversation.conversationID" class="t-conversation-item-container">
+		<movable-view class="t-conversation-item" direction="horizontal" @change="handleTouchMove" damping="100" :x="xScale">
+			<view class="avatar-box">
+				<image class="t-conversation-item-avatar" :src="setConversationAvatar" @error="handleImageError"></image>
+				<view class="unread" v-if="conversation.unreadCount !== 0">
+					<view class="read-text" v-if="conversation.unreadCount > 99">99+</view>
+					<view class="read-text" v-else>{{ conversation.unreadCount }}</view>
+				</view>
+			</view>
+			<view class="t-conversation-item-content">
+				<label class="tui-conversation-item-name">{{ conversationName }}</label>
+				<view class="tui-conversation-lastMessage">
+					<text>{{ conversation.lastMessage.messageForShow }}</text>
+				</view>
+			</view>
+			<view class="t-conversation-item-info">{{ timeago }}</view>
+			<!--    <view class="t-conversation-delete" @tap.stop="deleteConversation">删除</view>-->
+		</movable-view>
+	</movable-area>
+</template>
+
+<script>
+import { caculateTimeago } from '../../base/common';
+
+export default {
+	data() {
+		return {
+			xScale: 0,
+			conversationName: '',
+			conversationAvatar: '',
+			setConversationAvatar: '',
+			timeago: ''
+		};
+	},
+
+	components: {},
+	props: {
+		conversation: {
+			type: Object,
+			default: () => {}
+		}
+	},
+	watch: {
+		conversation: {
+			handler: function(conversation) {
+				// 计算时间戳
+				this.conversationName=this.getConversationName(conversation);
+				this.setConversationAvatar=this.setConversationAvatarHandler(conversation);
+				this.timeago=caculateTimeago(conversation.lastMessage.lastTime * 1000);
+				// this.setData({
+				// 	conversationName: this.getConversationName(conversation),
+				// 	setConversationAvatar: this.setConversationAvatarHandler(conversation),
+				// 	timeago: caculateTimeago(conversation.lastMessage.lastTime * 1000)
+				// });
+				this.$updateTimeago(conversation);
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+
+	methods: {
+		// 先查 remark;无 remark 查 (c2c)nick/(group)name;最后查 (c2c)userID/(group)groupID
+		getConversationName(conversation) {
+			if (conversation.type === '@TIM#SYSTEM') {
+				return '系统通知';
+			}
+
+			if (conversation.type === 'C2C') {
+				return conversation.remark || conversation.userProfile.nick || conversation.userProfile.userID;
+			}
+
+			if (conversation.type === 'GROUP') {
+				return conversation.groupProfile.name || conversation.groupProfile.groupID;
+			}
+		},
+
+		setConversationAvatarHandler(conversation) {
+			if (conversation.type === '@TIM#SYSTEM') {
+				return 'https://web.sdk.qcloud.com/component/TUIKit/assets/system.png';
+			}
+
+			if (conversation.type === 'C2C') {
+				return conversation.userProfile.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png';
+			}
+
+			if (conversation.type === 'GROUP') {
+				return conversation.groupProfile.avatar || '/static/static/assets/gruopavatar.svg';
+			}
+		},
+
+		deleteConversation() {
+			uni.showModal({
+				content: '确认删除会话?',
+				success: res => {
+					if (res.confirm) {
+						uni.$TUIKit.deleteConversation(this.conversation.conversationID);
+						this.setData({
+							conversation: {},
+							xScale: 0
+						});
+					}
+				}
+			});
+		},
+
+		handleTouchMove(e) {
+			if (!this.lock) {
+				this.last = e.detail.x;
+				this.lock = true;
+			}
+
+			if (this.lock && e.detail.x - this.last < -5) {
+				this.setData({
+					xScale: -75
+				});
+				setTimeout(() => {
+					this.lock = false;
+				}, 2000);
+			} else if (this.lock && e.detail.x - this.last > 5) {
+				this.setData({
+					xScale: 0
+				});
+				setTimeout(() => {
+					this.lock = false;
+				}, 2000);
+			}
+		},
+
+		$updateTimeago(conversation) {
+			if (conversation.conversationID) {
+				// conversation.lastMessage.timeago = caculateTimeago(conversation.lastMessage.lastTime * 1000);
+				conversation.lastMessage.messageForShow = conversation.lastMessage.messageForShow.slice(0, 15);
+			}
+			this.setData({
+				conversation
+			});
+		},
+
+		handleImageError() {
+			this.setData({
+				setConversationAvatar: '/static/static/assets/gruopavatar.svg'
+			});
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 363 - 0
components/tui-group/group-profile/index.css

@@ -0,0 +1,363 @@
+.group-information-box {
+	width: 100vw;
+	background: #006EFF;
+}
+
+.group-box {
+	display: inline-flex;
+	width: 100%;
+	box-sizing: border-box;
+	padding: 34rpx 40rpx;
+}
+
+.group-ID {
+	flex: 1;
+	max-width: 50%;
+	color: white;
+	display: inline-block;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+}
+
+.group-member {
+	padding-left: 110rpx;
+	color: white;
+}
+
+.icon-right {
+	float: right;
+	width: 32rpx;
+	height: 32rpx;
+	padding-top: 10rpx;
+	padding-left: 4rpx;
+}
+
+.showdetail {
+	display: flex;
+	width: 100vw;
+	height: 200rpx;
+	background: #006EFF;
+}
+
+.box {
+	width: 100rpx;
+	height: 130rpx;
+	padding-left: 35rpx;
+	padding-top: 10rpx;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+}
+
+.left-box {
+	display: flex;
+	padding-left: 200rpx;
+	padding-top: 10rpx;
+	width: 250rpx;
+}
+
+.box-group {
+	position: absolute;
+	width: 80rpx;
+	height: 80rpx;
+	right: 160rpx;
+}
+
+.box-group-quit {
+	position: absolute;
+	right: 80rpx;
+	width: 80rpx;
+	height: 80rpx;
+}
+
+.quitgroup {
+	display: block;
+	width: 80rpx;
+	height: 80rpx;
+	padding-left: 28rpx;
+}
+
+.addmember {
+	display: block;
+	width: 80rpx;
+	height: 80rpx;
+}
+
+.profile-box {
+	width: 80rpx;
+	height: 80rpx;
+	flex-shrink: 0;
+	z-index: 999
+}
+
+.addmember-text {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-family: PingFangSC-Regular;
+	font-size: 10px;
+	color: #FFFFFF;
+	letter-spacing: 0;
+}
+
+.quitgroup-text {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-family: PingFangSC-Regular;
+	font-size: 10px;
+	color: #FFFFFF;
+	letter-spacing: 0;
+	padding-left: 20rpx;
+	width: 100rpx;
+}
+
+.nick-box {
+	margin-top: 10px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-family: PingFangSC-Regular;
+	font-size: 10px;
+	color: #FFFFFF;
+	letter-spacing: 0;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	width: 100%;
+}
+
+.popup-mask {
+	width: 100vw;
+	height: 100vh;
+	position: fixed;
+	z-index: 10;
+	top: 0;
+	/*  #ifdef  H5  */
+	top: calc(88rpx + constant(safe-area-inset-top));
+	top: calc(88rpx + env(safe-area-inset-top));
+	/*  #endif  */
+	right: 0;
+	background: rgba(0, 0, 0, 0.60);
+	display: flex;
+	align-items: flex-end;
+}
+
+.pop-main {
+	max-height: 60%;
+	width: 100%;
+	padding-bottom: 120rpx;
+	background: #FFFFFF;
+	overflow: auto
+}
+
+.group-member-text {
+	display: inline-block;
+	padding-top: 40rpx;
+	padding-left: 40rpx;
+	opacity: 0.8;
+	font-family: PingFangSC-Medium;
+	font-size: 18px;
+	letter-spacing: 0;
+	line-height: 25px;
+}
+
+.close {
+	padding-left: 460rpx;
+	font-family: PingFangSC-Regular;
+	font-size: 18px;
+	color: #2B8BD5;
+	letter-spacing: 0;
+	line-height: 24px;
+}
+
+.quitpop-mask {
+	width: 100vw;
+	height: 100vh;
+	position: fixed;
+	z-index: 10;
+	top: 0;
+	/*  #ifdef  H5  */
+	top: calc(88rpx + constant(safe-area-inset-top));
+	top: calc(88rpx + env(safe-area-inset-top));
+	/*  #endif  */
+	right: 0;
+	background: rgba(0, 0, 0, 0.60);
+	display: flex;
+	align-items: flex-end;
+}
+
+.quitpop {
+	position: fixed;
+	bottom: 0;
+	width: 100%;
+	height: 25%;
+	background: #FFFFFF;
+	z-index: 99999;
+}
+
+.text-box {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 112rpx;
+}
+
+.confirmQuitgroup-text {
+	display: inline-block;
+	opacity: 0.4;
+	font-family: PingFangSC-Regular;
+	font-size: 14px;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 18px;
+}
+
+.quitgroup-confirm {
+	font-family: PingFangSC-Regular;
+	font-size: 16px;
+	color: #E85454;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 22px;
+}
+
+.quitgroup-abandon {
+	opacity: 0.8;
+	font-family: PingFangSC-Regular;
+	font-size: 16px;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 22px;
+}
+
+.mask {
+	position: absolute;
+	top: 450rpx;
+	left: 100rpx;
+	width: 70%;
+	height: 20%;
+	background: #999999;
+	z-index: 999;
+}
+
+.popup {
+	background: #ffffff;
+	border: 1px solid #eeeeee;
+}
+
+.popup-main {
+	height: 56px;
+	padding: 60rpx 0;
+	text-align: center;
+	font-family: PingFangSC-Regular;
+	font-size: 14px;
+	color: #999999;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 18px;
+}
+
+.input-box {
+	padding-top: 30rpx;
+}
+
+.popup-footer {
+	display: flex;
+}
+
+.popup-footer-button {
+	flex: 1;
+}
+
+.popup-footer .cancel {
+	font-family: PingFangSC-Regular;
+	font-size: 14px;
+	color: #000000;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 22px;
+	height: 44px;
+}
+
+.popup-footer .submit {
+	font-family: PingFangSC-Regular;
+	font-size: 14px;
+	color: #E85454;
+	letter-spacing: 0;
+	text-align: center;
+	line-height: 22px;
+	height: 44px;
+}
+
+.cancellation {
+	margin-top: 68px;
+	margin-left: 20px;
+	margin-right: 20px;
+	background-color: white;
+	width: 280px;
+	height: 46px;
+	border: 1px solid #E85454;
+	border-radius: 24px;
+	border-radius: 24px;
+}
+
+.confirm-cancellation {
+	margin-left: 110px;
+	margin-top: 13px;
+	font-family: PingFangSC-Regular;
+	font-size: 16px;
+	color: #E85454;
+	letter-spacing: 0;
+}
+
+.image-list {
+	padding-top: 40rpx;
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.image-nick-box {
+	width: 108rpx;
+	height: 140rpx;
+	padding-left: 32rpx;
+	padding-top: 32rpx;
+}
+
+.image-box {
+	width: 108rpx;
+	height: 108rpx;
+	padding-left: 32rpx;
+	border-radius: 4px;
+	border-radius: 4px;
+}
+
+.groupmembername {
+	display: inline-block;
+	font-family: PingFangSC-Regular;
+	font-size: 12px;
+	color: #999999;
+	letter-spacing: 0;
+	padding-left: 32rpx;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	width: 80rpx;
+}
+
+.text-box-qiut {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 112rpx;
+}
+
+.text-box-cancle {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 112rpx;
+	border-top: 16rpx solid #eeeeee;
+}

+ 277 - 0
components/tui-group/group-profile/index.vue

@@ -0,0 +1,277 @@
+<template>
+	<view>
+		<view class="group-information-box" @show="getgroupProfile">
+			<view class="group-box">
+				<text class="group-ID">群ID:{{ conversation.groupProfile.groupID }}</text>
+				<view @tap="showMoreHandler">
+					<text class="group-member">聊天成员:{{ conversation.groupProfile.memberCount }}人</text>
+					<image v-if="notShow" class=" icon-right" src="/static/static/assets/down.svg"></image>
+					<image v-if="isShow" class=" icon-right" src="/static/static/assets/up.svg"></image>
+				</view>
+			</view>
+			<view v-show="!hidden" class="showdetail">
+				<view v-for="(item, index) in groupmemberprofile" :key="index" class="box" v-if="index < 3" :data-value="item">
+					<image
+						class="profile-box"
+						:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+					></image>
+					<text class="nick-box">{{ item.nick || item.userID }}</text>
+				</view>
+				<view class="box" v-if="showMore">
+					<image class="profile-box" src="/static/static/assets/show.svg" @tap="showMoreMember"></image>
+					<text class="nick-box">更多</text>
+				</view>
+				<view class="left-box">
+					<view class="box-group" v-if="addShow">
+						<image class="addmember" src="/static/static/assets/addgroup.svg" @tap="addMember"></image>
+						<text class="addmember-text">添加成员</text>
+					</view>
+					<view class="box-group-quit">
+						<image class="quitgroup" src="/static/static/assets/quitgroup.svg" @tap.stop="quitGroup"></image>
+						<text class="quitgroup-text">退出群聊</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="pop-container">
+			<view class="popup-mask" v-if="popupToggle" @tap.stop="handleEditToggle">
+				<view class="pop-main">
+					<view class="pop-main-header">
+						<text class="group-member-text">群成员</text>
+						<text class="close" @tap.stop="close">关闭</text>
+					</view>
+					<view class="image-list">
+						<view v-for="(item, index) in groupmemberprofile" :key="index" class="image-nick-box" :data-value="item">
+							<image
+								class="image-box"
+								:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+							></image>
+							<text class="groupmembername">{{ item.nick || item.userID }}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="pop-container">
+			<view class="quitpop-mask" v-if="quitpopupToggle">
+				<view class="quitpop">
+					<view class=" quit-box">
+						<view class="text-box"><text class="confirmQuitgroup-text">退出群聊后会同步删除历史聊天记录,是否要退出群聊?</text></view>
+						<view class="text-box-qiut" @tap.stop="quitgroupConfirm"><text class="quitgroup-confirm">退出</text></view>
+						<view class="text-box-cancle" @tap="quitgroupAbandon"><text class="quitgroup-abandon">取消</text></view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="mask" v-if="addpopupToggle">
+			<view class="popup">
+				<view class="popup-main">
+					<text>添加群成员</text>
+					<input class="input-box" type="number" placeholder="请输入userID" @input="binduserIDInput" placeholder-style="color:#BBBBBB;" />
+				</view>
+				<view class="popup-footer">
+					<button class=" popup-footer-button submit" @tap.stop="submit">确认</button>
+					<button class="popup-footer-button cancel" @tap.stop="close">取消</button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import logger from '../../../utils/logger';
+
+export default {
+	data() {
+		return {
+			userID: '',
+			// conversation: {},
+			newgroup: {},
+			groupmemberprofile: {},
+			groupmemberavatar: [],
+			groupmembernick: [],
+			hidden: true,
+			notShow: true,
+			isShow: false,
+			showMore: false,
+			addShow: false,
+			popupToggle: false,
+			quitpopupToggle: false,
+			addpopupToggle: false
+		};
+	},
+
+	components: {},
+	props: {
+		conversation: {
+			type: Object
+		}
+	},
+	watch: {
+		conversation: {
+			handler: function(newVal) {
+				if (newVal.type === 'GROUP');
+				this.conversation=newVal;
+				// this.setData({
+				// 	conversation: newVal
+				// });
+			},
+			immediate: true,
+			deep: true
+		}
+	},
+
+	beforeMount() {},
+
+	methods: {
+		showMoreHandler() {
+			uni.$TUIKit
+				.getGroupMemberList({
+					groupID: this.conversation.groupProfile.groupID,
+					count: 50,
+					offset: 0
+				}) // 从0开始拉取30个群成员
+				.then(imResponse => {
+					logger.log(`| TUI-group-profile | getGroupMemberList  | getGroupMemberList-length: ${imResponse.data.memberList.length}`);
+
+					if (this.conversation.groupProfile.type === 'Private') {
+						this.setData({
+							addShow: true
+						});
+					}
+
+					if (imResponse.data.memberList.length > 3) {
+						this.setData({
+							showMore: true
+						});
+					}
+
+					this.setData({
+						groupmemberprofile: imResponse.data.memberList,
+						hidden: !this.hidden,
+						notShow: !this.notShow,
+						isShow: !this.isShow
+					});
+				});
+		},
+
+		showless() {
+			this.setData({
+				isShow: false,
+				notShow: true,
+				hidden: true
+			});
+		},
+
+		showMoreMember() {
+			this.setData({
+				popupToggle: true //  quitpopupToggle: false
+			});
+		},
+
+		close() {
+			this.setData({
+				popupToggle: false,
+				addpopupToggle: false,
+				quitpopupToggle: false
+			});
+		},
+
+		quitGroup() {
+			this.setData({
+				quitpopupToggle: true,
+				popupToggle: false
+			});
+		},
+
+		quitgroupConfirm() {
+			uni.$TUIKit
+				.quitGroup(this.conversation.groupProfile.groupID)
+				.then(imResponse => {
+					console.log(imResponse.data.groupID); // 退出成功的群 ID
+
+					uni.navigateBack({
+						delta: 1
+					});
+				})
+				.catch(imError => {
+					uni.showToast({
+						title: '该群不允许群主主动退出',
+						icon: 'none'
+					});
+					console.warn('quitGroup error:', imError); // 退出群组失败的相关信息
+				});
+		},
+
+		quitgroupAbandon() {
+			console.log(22222);
+			this.setData({
+				quitpopupToggle: false
+			});
+		},
+
+		addMember() {
+			this.setData({
+				addpopupToggle: true
+			});
+		},
+
+		binduserIDInput(e) {
+			const id = e.detail.value;
+			this.setData({
+				userID: id
+			});
+		},
+
+		submit() {
+			console.log(this.userID);
+			uni.$TUIKit
+				.addGroupMember({
+					groupID: this.conversation.groupProfile.groupID,
+					userIDList: [this.userID]
+				})
+				.then(imResponse => {
+					if (imResponse.data.successUserIDList.length > 0) {
+						uni.showToast({
+							title: '添加成功',
+							duration: 800
+						});
+						this.userID = '';
+						this.addMemberModalVisible = false;
+						this.setData({
+							addpopupToggle: false
+						});
+					}
+
+					if (imResponse.data.existedUserIDList.length > 0) {
+						uni.showToast({
+							title: '该用户已在群中',
+							duration: 800,
+							icon: 'none'
+						});
+					}
+				})
+				.catch(imError => {
+					console.warn('addGroupMember error:', imError); // 错误信息
+
+					uni.showToast({
+						title: '添加失败,请确保该用户存在',
+						duration: 800,
+						icon: 'none'
+					});
+				});
+		},
+
+		handleEditToggle() {
+			console.log('占位:函数 handleEditToggle 未声明');
+		},
+
+		getgroupProfile() {
+			console.log('占位:函数 getgroupProfile 未声明');
+		}
+	}
+};
+</script>
+<style>
+@import './index.css';
+</style>

+ 21 - 0
index.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+	<meta name="referrer" content="never" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 31 - 0
main.js

@@ -0,0 +1,31 @@
+import App from './App'
+import Vue from 'vue';
+import Store from './store';
+import util from './util';
+import api from '@/util/api.js'
+import moment from 'moment';
+import {VueJsonp} from 'vue-jsonp'
+Vue.use(VueJsonp)
+Vue.prototype.$store=Store;
+Vue.prototype.$util=util;
+Vue.prototype.$api=api;
+Vue.prototype.$moment=moment;
+Vue.prototype.$EventBus = new Vue();
+// #ifndef VUE3
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+    ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+  const app = createSSRApp(App)
+  return {
+    app
+  }
+}
+// #endif

+ 105 - 0
manifest.json

@@ -0,0 +1,105 @@
+{
+    "name" : "sugarpark-mini-program",
+    "appid" : "__UNI__1ED9DE5",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx1b43cad351b5f0d1",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "postcss" : true,
+            "minified" : true,
+            "checkSiteMap" : false,
+            "ignoreDevUnusedFiles" : false,
+            "ignoreUploadUnusedFiles" : false
+        },
+        "usingComponents" : true,
+        "requiredPrivateInfos" : [ "getFuzzyLocation" ],
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "获取您的位置获得更多优质推荐"
+            },
+            "scope.userFuzzyLocation" : {
+                "desc" : "获取您的位置获得更多优质推荐"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2",
+    "h5" : {
+        "sdkConfigs" : {
+            "maps" : {
+                "qqmap" : {
+                    "key" : "E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII"
+                }
+            }
+        },
+        "router" : {
+            "mode" : "hash"
+        },
+        "title" : "糖果公园",
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : true
+            }
+        }
+    }
+}

+ 201 - 0
package-lock.json

@@ -0,0 +1,201 @@
+{
+  "name": "sugarpark-mini-program",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "@dcloudio/uni-ui": "^1.4.20",
+        "tim-upload-plugin": "^1.0.5",
+        "tim-wx-sdk": "^2.22.1"
+      },
+      "devDependencies": {
+        "cos-wx-sdk-v5": "^1.0.10",
+        "crypto-js": "^4.1.1",
+        "js-base64": "^3.7.2",
+        "moment": "^2.29.4",
+        "tim-js-sdk": "^2.17.0",
+        "vue-jsonp": "^2.0.0",
+        "vuedraggable": "^2.24.3"
+      }
+    },
+    "node_modules/@dcloudio/uni-ui": {
+      "version": "1.4.20",
+      "resolved": "https://registry.npmjs.org/@dcloudio/uni-ui/-/uni-ui-1.4.20.tgz",
+      "integrity": "sha512-vWrT64u83H1YYrTF1SpWI0ZTplP/411a9tSPqd6ePRXDDODueWTfngguP/iEzAyeXMYLoS1N3rUWoTSxYrr9zQ=="
+    },
+    "node_modules/@xmldom/xmldom": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
+      "integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/cos-wx-sdk-v5": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/cos-wx-sdk-v5/-/cos-wx-sdk-v5-1.4.0.tgz",
+      "integrity": "sha512-hb04qPMXcCePt7nB7QkFbjaMfnQEbCRH8YNw6NeXRPLFuGUY2o3/1jrQHeghYM467zluyi5CUOsE2Ln5EwjJQw==",
+      "dev": true,
+      "dependencies": {
+        "@xmldom/xmldom": "^0.8.2",
+        "mime": "^2.4.6"
+      }
+    },
+    "node_modules/crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
+      "dev": true
+    },
+    "node_modules/js-base64": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
+      "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==",
+      "dev": true
+    },
+    "node_modules/mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/sortablejs": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
+      "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==",
+      "dev": true
+    },
+    "node_modules/tim-js-sdk": {
+      "version": "2.22.1",
+      "resolved": "https://registry.npmjs.org/tim-js-sdk/-/tim-js-sdk-2.22.1.tgz",
+      "integrity": "sha512-U/BnrwzB1/hYuqQc3yPtO7udNoy8GNE5r0ahEUp/NQueW4P6QDu2RUCI2j4sF+yopC8tNG4etFgmAgGXBT+2Ug==",
+      "dev": true
+    },
+    "node_modules/tim-upload-plugin": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tim-upload-plugin/-/tim-upload-plugin-1.0.5.tgz",
+      "integrity": "sha512-GFxo3l60Os/p8rlg1THMcdjIiee7/IyeOTUJ4cwMBgRMxFoxFUmXmKoIS2207+r5F8ai3HqyRZOfiNfcHYrbZw=="
+    },
+    "node_modules/tim-wx-sdk": {
+      "version": "2.22.1",
+      "resolved": "https://registry.npmjs.org/tim-wx-sdk/-/tim-wx-sdk-2.22.1.tgz",
+      "integrity": "sha512-KES+gLvAENLzeuzAxfb36g+Ftgvu5EdOHdAElcSv062SyADwd+mYP6K2Ewm0eKXS/cLDwKitXg/wMIdB1tHtlg=="
+    },
+    "node_modules/vue-jsonp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/vue-jsonp/-/vue-jsonp-2.0.0.tgz",
+      "integrity": "sha512-Mzd9GNeuKP5hHFDWZNMWOsCuMILSkA6jo2l4A02wheFz3qqBzH7aSEFTey1BRCZCLizlaf1EqJ5YUtF392KspA==",
+      "dev": true
+    },
+    "node_modules/vuedraggable": {
+      "version": "2.24.3",
+      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
+      "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
+      "dev": true,
+      "dependencies": {
+        "sortablejs": "1.10.2"
+      }
+    }
+  },
+  "dependencies": {
+    "@dcloudio/uni-ui": {
+      "version": "1.4.20",
+      "resolved": "https://registry.npmjs.org/@dcloudio/uni-ui/-/uni-ui-1.4.20.tgz",
+      "integrity": "sha512-vWrT64u83H1YYrTF1SpWI0ZTplP/411a9tSPqd6ePRXDDODueWTfngguP/iEzAyeXMYLoS1N3rUWoTSxYrr9zQ=="
+    },
+    "@xmldom/xmldom": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
+      "integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
+      "dev": true
+    },
+    "cos-wx-sdk-v5": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/cos-wx-sdk-v5/-/cos-wx-sdk-v5-1.4.0.tgz",
+      "integrity": "sha512-hb04qPMXcCePt7nB7QkFbjaMfnQEbCRH8YNw6NeXRPLFuGUY2o3/1jrQHeghYM467zluyi5CUOsE2Ln5EwjJQw==",
+      "dev": true,
+      "requires": {
+        "@xmldom/xmldom": "^0.8.2",
+        "mime": "^2.4.6"
+      }
+    },
+    "crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
+      "dev": true
+    },
+    "js-base64": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
+      "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==",
+      "dev": true
+    },
+    "mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true
+    },
+    "moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+      "dev": true
+    },
+    "sortablejs": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
+      "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==",
+      "dev": true
+    },
+    "tim-js-sdk": {
+      "version": "2.22.1",
+      "resolved": "https://registry.npmjs.org/tim-js-sdk/-/tim-js-sdk-2.22.1.tgz",
+      "integrity": "sha512-U/BnrwzB1/hYuqQc3yPtO7udNoy8GNE5r0ahEUp/NQueW4P6QDu2RUCI2j4sF+yopC8tNG4etFgmAgGXBT+2Ug==",
+      "dev": true
+    },
+    "tim-upload-plugin": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tim-upload-plugin/-/tim-upload-plugin-1.0.5.tgz",
+      "integrity": "sha512-GFxo3l60Os/p8rlg1THMcdjIiee7/IyeOTUJ4cwMBgRMxFoxFUmXmKoIS2207+r5F8ai3HqyRZOfiNfcHYrbZw=="
+    },
+    "tim-wx-sdk": {
+      "version": "2.22.1",
+      "resolved": "https://registry.npmjs.org/tim-wx-sdk/-/tim-wx-sdk-2.22.1.tgz",
+      "integrity": "sha512-KES+gLvAENLzeuzAxfb36g+Ftgvu5EdOHdAElcSv062SyADwd+mYP6K2Ewm0eKXS/cLDwKitXg/wMIdB1tHtlg=="
+    },
+    "vue-jsonp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/vue-jsonp/-/vue-jsonp-2.0.0.tgz",
+      "integrity": "sha512-Mzd9GNeuKP5hHFDWZNMWOsCuMILSkA6jo2l4A02wheFz3qqBzH7aSEFTey1BRCZCLizlaf1EqJ5YUtF392KspA==",
+      "dev": true
+    },
+    "vuedraggable": {
+      "version": "2.24.3",
+      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
+      "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
+      "dev": true,
+      "requires": {
+        "sortablejs": "1.10.2"
+      }
+    }
+  }
+}

+ 16 - 0
package.json

@@ -0,0 +1,16 @@
+{
+  "devDependencies": {
+    "cos-wx-sdk-v5": "^1.0.10",
+    "crypto-js": "^4.1.1",
+    "js-base64": "^3.7.2",
+    "moment": "^2.29.4",
+    "tim-js-sdk": "^2.17.0",
+    "vue-jsonp": "^2.0.0",
+    "vuedraggable": "^2.24.3"
+  },
+  "dependencies": {
+    "@dcloudio/uni-ui": "^1.4.20",
+    "tim-upload-plugin": "^1.0.5",
+    "tim-wx-sdk": "^2.22.1"
+  }
+}

+ 294 - 0
pages.json

@@ -0,0 +1,294 @@
+{
+	"easycom": {
+			"autoscan": true,
+			"custom": {
+				// uni-ui 规则如下配置
+				"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
+			}
+	},
+	"usingComponents": {
+	  "van-index-bar": "@vant/weapp/index-bar/index",
+	  "van-index-anchor": "@vant/weapp/index-anchor/index"
+	},
+	"tabBar":{
+		"custom":true,
+		"color": "#7D7DA4",
+		"selectedColor": "#FFFFFF",
+		"borderStyle": "black",
+		"backgroundColor": "#151126",
+		"list": [
+			{
+				"pagePath": "pages/friends/friends",
+				"iconPath": "static/tabbar/friends-off.png",
+				"selectedIconPath": "static/tabbar/friends-on.png",
+				"text": "交友"
+			},
+			{
+				"pagePath": "pages/messages/messages",
+				"iconPath": "static/tabbar/message-off.png",
+				"selectedIconPath": "static/tabbar/message-on.png",
+				"text": "消息"
+			},
+			{
+				"pagePath": "pages/mine/mine",
+				"iconPath": "static/tabbar/mine-off.png",
+				"selectedIconPath": "static/tabbar/mine-on.png",
+				"text": "我的"
+			}
+		]
+		
+	},
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path" : "pages/friends/friends",
+			"style" :                                                                                    
+			{
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+			}
+			
+		},
+	    {
+            "path" : "pages/login/login",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        },{
+            "path" : "pages/login/loginByPhone",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/login/loginByCode",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/login/loginByPassword",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/info/sex",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/info/figure",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/info/datum",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/info/city",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/info/wechat",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        
+        ,{
+            "path" : "pages/mine/mine",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/friends/user",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/mine/guest",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/mine/album",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/info/editCenter",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        },
+		{
+		    "path" : "pages/info/labels",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "",
+		        "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+		    }
+		    
+		}
+        ,{
+            "path" : "pages/vip/vip",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/wallet/wallet",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/webview/webview",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        
+        ,{
+            "path" : "pages/messages/messages",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+        ,{
+            "path" : "pages/search/search",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle":"custom"
+            }
+            
+        }
+    ],
+	"subPackages": [{
+		"root": "pagesSub/",
+		"pages": [
+			{
+			    "path" : "chatting/chatting",
+			    "style" :                                                                                    
+			    {
+			        "navigationBarTitleText": "",
+			        "enablePullDownRefresh": false,
+					"navigationStyle":"custom"
+			    }
+			    
+			}
+			,{
+				"path" : "setting/setting",
+				"style" :                                                                                    
+				{
+					"navigationBarTitleText": "",
+					"enablePullDownRefresh": false,
+					"navigationStyle":"custom"
+				}
+            
+			}
+			,{
+			    "path" : "faceVideo/faceVideo",
+			    "style" :                                                                                    
+			    {
+			        "navigationBarTitleText": "",
+			        "enablePullDownRefresh": false,
+					"navigationStyle":"custom"
+			    }
+			    
+			}
+		]
+	}],
+	"globalStyle": {
+		"navigationBarTextStyle": "white",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#151126",
+		"backgroundColor": "#151126"
+	}
+}

+ 1346 - 0
pages/friends/friends.vue

@@ -0,0 +1,1346 @@
+<template>
+	<view class="container">
+		<TabBar :tabIndex="tabIndex" ></TabBar>
+		<uni-popup ref="popup" type="center">
+			<Popup :content1="popup.content1" :content2="popup.content2" :tip1="popup.tip1" :tip2="popup.tip2" :btntext="popup.btntext" @closePopup="closePopup"  @toLogin="toLogin" :btnEvent="'toLogin'"></Popup>
+		</uni-popup>
+		<view id="topnav" class="topnav flex-start"
+			:style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item" v-for="(item,index) in navs" :key="index" :style="{
+				'font-size':`${navIndex===index?'40rpx':'32rpx'}`,
+				'color':`${navIndex===index?'#ffffff':''}`,
+				'font-weight':`${navIndex===index?'#ffffff':''}`}"
+				@click="navClick(index)">
+				
+				<view class="nav-item-text fw700">{{item}}</view>
+			</view>
+			<image :src="`${assetsUrl}friends-searchicon.png`" mode="aspectFit" class="nav-search" @click="toSearch"></image>
+			<image :src="`${assetsUrl}friends-circle.png`" mode="aspectFit" class="nav-item-bg" :style="{'left':`${navIndex*158}rpx`}" ></image>
+		</view>
+		
+		<swiper class="outer-swiper" :current="navIndex" :style="{'height':`${scrollHeight}px`}" @change="outerSwiperChange">
+			<swiper-item class="outer-swiper-item">
+				<scroll-view class="scroll-view" 
+					v-if="scrollHeight>0"
+					scroll-y="true" 
+					lower-threshold="200"
+					:style="{'height':`${scrollHeight}px`}" 
+					refresher-enabled="true" 
+					:refresher-triggered="recommendTriggered"
+					:refresher-threshold="45" 
+					refresher-default-style="white"
+					refresher-background="#151126" 
+					@refresherrefresh="recommendRefresh" 
+					@refresherpulling="recommendPulling"
+					@refresherrestore="recommendRestore" 
+					@refresherabort="recommendAbort"
+					@scrolltolower="recommendToBottom"
+					show-scrollbar="false"
+					>
+				<view slot="refresher" class="refresh-container" style="display: block; width: 100%; height: 80px; background: blue; display: flex; align-items: center;">
+				    <view class="view1" style="position: absolute; text-align: center; width: 100%;">
+				      下拉刷新
+				    </view>
+				</view>
+					<image :src="`${assetsUrl}friends-bg.png`" mode="widthFix" class="tops-bg" v-if="recommendTopsM.length>0"></image>
+					<view class="tops-nav flex-center" v-if="recommendTopsM.length>0">
+						<view class="tops-nav-item" :class="topsIndex===index?'font32 fw600 tops-nav-item-active':''" v-for="(item,index) in tops" :key="index" @click="topsNavClick(index)">
+							{{item}}
+						</view>
+						<view class="tops-line" :style="{'left':`${topsIndex*160+250}rpx`}"></view>
+					</view>
+					<swiper :current="topsIndex" class="swiper" @change="swiperChange" v-if="recommendTopsM.length>0">
+						<swiper-item class="swiper-item">
+							<view class="tops flex-between">
+								<view class="tops-third flex-center">
+									<view class="tt-item flex-center" @click="toDetail(recommendTopsM[1].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-2.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="recommendTopsM[1].icon" class="tt-item-head-box" style="border: 2rpx solid #E2E2E2;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{recommendTopsM[1].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-heart.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{recommendTopsM[1].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" style="margin: 0rpx 16rpx;" @click="toDetail(recommendTopsM[0].userId)">
+										<view class="tt-item-bg-center" style="width: 228rpx;"></view>
+										<image :src="`${assetsUrl}friends-1.png`" mode="aspectFill" class="tt-item-img" style="transform: translateY(0rpx);"></image>
+										<image :src="`${assetsUrl}friends-1-bling.png`" mode="aspectFill" class="tt-item-img-border" style="transform: translateY(-60rpx);"></image>
+										<image :src="recommendTopsM[0].icon" class="tt-item-head-box" style="border: 2rpx solid #FFEC36;transform: translateY(-18rpx);"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{recommendTopsM[0].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-heart.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{recommendTopsM[0].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" @click="toDetail(recommendTopsM[2].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-3.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="recommendTopsM[2].icon" class="tt-item-head-box" style="border: 2rpx solid #E19865;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{recommendTopsM[2].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-heart.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{recommendTopsM[2].val}}
+											</view>
+										</view>
+									</view>
+								</view>
+							</view>
+						</swiper-item>
+						<swiper-item  class="swiper-item">
+							<view class="tops flex-between">
+								<view class="tops-third flex-center">
+									<view class="tt-item flex-center" @click="toDetail(recommendTopsH[1].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-2.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="recommendTopsH[1].icon" class="tt-item-head-box" style="border: 2rpx solid #E2E2E2;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{recommendTopsH[1].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-sugar.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{recommendTopsH[1].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" style="margin: 0rpx 16rpx;" @click="toDetail(recommendTopsH[0].userId)">
+										<view class="tt-item-bg-center" style="width: 228rpx;"></view>
+										<image :src="`${assetsUrl}friends-1.png`" mode="aspectFill" class="tt-item-img" style="transform: translateY(-5rpx);"></image>
+										<image :src="`${assetsUrl}friends-1-bling.png`" mode="aspectFill" class="tt-item-img-border" style="transform: translateY(-60rpx);"></image>
+										<image :src="recommendTopsH[0].icon" class="tt-item-head-box" style="border: 2rpx solid #FFEC36;transform: translateY(-18rpx);"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{recommendTopsH[0].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-sugar.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{recommendTopsH[0].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center"  @click="toDetail(recommendTopsH[2].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-3.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="recommendTopsH[2].icon" class="tt-item-head-box" style="border: 2rpx solid #E19865;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{recommendTopsH[2].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-sugar.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{recommendTopsH[2].val}}
+											</view>
+										</view>
+									</view>
+								</view>
+							</view>
+						</swiper-item>
+					</swiper>
+					<view class="location flex-between" @click="computedLocation" v-if="showGetLocation">
+						<image :src="`${assetsUrl}friends-pos.png`" mode="aspectFill" class="l-pos"></image>
+						<text class="l-text font24 fw400">点此开启位置权限,获得更精准推荐!</text>
+						<view class="l-open font24 fw400">开启</view>
+					</view>
+					<view class="list-item flex-start" v-for="(item,index) in recommendList" :key="index" @click="toDetail(item.id)">
+						<view class="list-head-box">
+							<image :src="item.iconThumbnail" mode="aspectFill" class="list-head-img"></image>
+							<view class="list-head-dot" style="background-color: #38E825;" v-if="item.lastActiveTime<=30"></view>
+							<view class="list-head-dot" style="background-color: #0ABDEF;" v-else-if="item.lastActiveTime>30&&item.lastActiveTime<=1440"></view>
+						</view>
+						<view class="list-info-box">
+							<view class="name-box flex-between">
+								<view class="name flex-start">
+									<view class="name-text font28 fw600">
+										{{item.nick}}
+									</view>
+									<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFill" class="name-img" v-if="item.vip"></image>
+									<image :src="`${assetsUrl}friends-godness.png`" mode="aspectFill" class="name-img-godness" v-if="item.goddess"></image>
+									<image :src="`${assetsUrl}friends-real.png`" mode="aspectFill" class="name-img" v-else-if="item.realMan"></image>
+								</view>
+								<view class="distance font22 fw400" v-if="item.distance&&item.distance!=='NaNm'">
+									{{item.distance}}
+								</view>
+							</view>
+							<view class="sex-box flex-center" :style="{'background-color':`${item.sex==='Male'?'rgba(108,82,244,0.21)':''}`}">
+								<image :src="`${assetsUrl}friends-female.png`" mode="aspectFill" class="sex-img" v-if="item.sex==='Famale'"></image>
+								<image :src="`${assetsUrl}friends-male.png`" mode="aspectFill" class="sex-img" v-if="item.sex==='Male'"></image>
+								<view class="el font20 fw500 sex-text1" v-if="item.sex==='Famale'">
+									{{item.ageInfo.age}}
+								</view>
+								<view class="el font20 fw500 sex-text2" v-if="item.sex==='Male'">
+									{{item.ageInfo.age}}
+								</view>
+							</view>
+							<view class="tip-box font28 fw400 el" v-if="item.desc">
+								签名:{{item.desc}}
+							</view>
+							<view class="tip-box font28 fw400 el" v-else>
+								签名:暂无介绍
+							</view>
+							
+						</view>
+						<view class="img-box flex-between" v-if="item.lastNews">
+							<image :src="item.lastNews.mediaUrls[0]" mode="aspectFill" class="ib1"></image>
+							<image :src="item.lastNews.mediaUrls[1]" mode="aspectFill" class="ib2"></image>
+							<image :src="item.lastNews.mediaUrls[2]" mode="aspectFill" class="ib3"></image>
+						</view>
+					</view>
+					<view class="no-more font24 fw400" v-if="recommendList.length!==0&&recommendList.length>=recommendTotal">没有更多了</view>
+				</scroll-view>
+			</swiper-item>
+			<swiper-item class="outer-swiper-item">
+				<scroll-view class="scroll-view" 
+					v-if="scrollHeight>0"
+					scroll-y="true" 
+					lower-threshold="200"
+					:style="{'height':`${scrollHeight}px`}" 
+					refresher-enabled="true" 
+					:refresher-triggered="nearTriggered"
+					:refresher-threshold="45" 
+					refresher-default-style="white"
+					refresher-background="#151126" 
+					@refresherrefresh="nearRefresh" 
+					@refresherpulling="nearPulling"
+					@refresherrestore="nearRestore" 
+					@refresherabort="nearAbort"
+					@scrolltolower="nearToBottom"
+					:show-scrollbar="false"
+				>
+					<image :src="`${assetsUrl}friends-bg.png`" mode="widthFix" class="tops-bg" v-if="!showGetLocation"></image>
+					<view class="tops-nav flex-center" v-if="!showGetLocation">
+						<view class="tops-nav-item" :class="topsIndex===index?'font32 fw600 tops-nav-item-active':''" v-for="(item,index) in tops" :key="index" @click="topsNavClick(index)">
+							{{item}}
+						</view>
+						<view class="tops-line" :style="{'left':`${topsIndex*160+250}rpx`}"></view>
+					</view>
+					<swiper :current="topsIndex" class="swiper" @change="swiperChange"  v-if="!showGetLocation">
+						<swiper-item class="swiper-item">
+							<view class="tops flex-between">
+								<view class="tops-third flex-center">
+									<view class="tt-item flex-center" @click="toDetail(nearTopsM[1].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-2.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="nearTopsM[1].icon" class="tt-item-head-box" style="border: 2rpx solid #E2E2E2;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{nearTopsM[1].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-heart.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{nearTopsM[1].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" style="margin: 0rpx 16rpx;" @click="toDetail(nearTopsM[0].userId)">
+										<view class="tt-item-bg-center" style="width: 228rpx;"></view>
+										<image :src="`${assetsUrl}friends-1.png`" mode="aspectFill" class="tt-item-img" style="transform: translateY(-5rpx);"></image>
+										<image :src="`${assetsUrl}friends-1-bling.png`" mode="aspectFill" class="tt-item-img-border" style="transform: translateY(-60rpx);"></image>
+										<image :src="nearTopsM[0].icon" class="tt-item-head-box" style="border: 2rpx solid #FFEC36;transform: translateY(-18rpx);"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{nearTopsM[0].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-heart.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{nearTopsM[0].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" @click="toDetail(nearTopsM[2].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-3.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="nearTopsM[2].icon" class="tt-item-head-box" style="border: 2rpx solid #E19865;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{nearTopsM[2].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-heart.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{nearTopsM[2].val}}
+											</view>
+										</view>
+									</view>
+								</view>
+							</view>
+						</swiper-item>
+						<swiper-item  class="swiper-item">
+							<view class="tops flex-between">
+								<view class="tops-third flex-center">
+									<view class="tt-item flex-center" @click="toDetail(nearTopsH[1].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-2.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="nearTopsH[1].icon"  class="tt-item-head-box" style="border: 2rpx solid #E2E2E2;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{nearTopsH[1].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-sugar.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{nearTopsH[1].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" style="margin: 0rpx 16rpx;" @click="toDetail(nearTopsH[0].userId)">
+										<view class="tt-item-bg-center" style="width: 228rpx;"></view>
+										<image :src="`${assetsUrl}friends-1.png`" mode="aspectFill" class="tt-item-img" style="transform: translateY(-5rpx);"></image>
+										<image :src="`${assetsUrl}friends-1-bling.png`" mode="aspectFill" class="tt-item-img-border" style="transform: translateY(-60rpx);"></image>
+										<image :src="nearTopsH[0].icon"  class="tt-item-head-box" style="border: 2rpx solid #FFEC36;transform: translateY(-18rpx);"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{nearTopsH[0].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-sugar.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{nearTopsH[0].val}}
+											</view>
+										</view>
+									</view>
+									<view class="tt-item  flex-center" @click="toDetail(nearTopsH[2].userId)">
+										<view class="tt-item-bg"></view>
+										<image :src="`${assetsUrl}friends-3.png`" mode="aspectFill" class="tt-item-img"></image>
+										<image :src="nearTopsH[2].icon"  class="tt-item-head-box" style="border: 2rpx solid #E19865;"></image>
+										<view class="tt-item-name font28 fw600 el">
+											{{nearTopsH[2].nick}}
+										</view>
+										<view class="tt-item-num-box flex-center">
+											<image :src="`${assetsUrl}friends-sugar.png`" mode="aspectFill" class="tt-item-num-img"></image>
+											<view class="tt-item-num-text font22 fw600">
+												{{nearTopsH[2].val}}
+											</view>
+										</view>
+									</view>
+								</view>
+							</view>
+						</swiper-item>
+					</swiper>
+					<view class="list-item flex-start" v-for="(item,index) in nearList" :key="index" @click="toDetail(item.id)">
+						<view class="list-head-box">
+							<image :src="item.iconThumbnail" mode="aspectFill" class="list-head-img"></image>
+							<view class="list-head-dot" style="background-color: #38E825;" v-if="item.lastActiveTime<=30"></view>
+							<view class="list-head-dot" style="background-color: #0ABDEF;" v-else-if="item.lastActiveTime>30&&item.lastActiveTime<=1440"></view>
+						</view>
+						<view class="list-info-box">
+							<view class="name-box flex-between">
+								<view class="name flex-start">
+									<view class="name-text font28 fw600">
+										{{item.nick}}
+									</view>
+									<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFill" class="name-img" v-if="item.vip"></image>
+									<image :src="`${assetsUrl}friends-godness.png`" mode="aspectFill" class="name-img-godness" v-if="item.goddess"></image>
+									<image :src="`${assetsUrl}friends-real.png`" mode="aspectFill" class="name-img" v-else-if="item.realMan"></image>
+								</view>
+								<view class="distance font22 fw400" v-if="item.distance&&item.distance!=='NaNm'">
+									{{item.distance}}
+								</view>
+							</view>
+							<view class="sex-box flex-center" :style="{'background-color':`${item.sex==='Male'?'rgba(108,82,244,0.21)':''}`}">
+								<image :src="`${assetsUrl}friends-female.png`" mode="aspectFill" class="sex-img" v-if="item.sex==='Famale'"></image>
+								<image :src="`${assetsUrl}friends-male.png`" mode="aspectFill" class="sex-img" v-if="item.sex==='Male'"></image>
+								<view class="el font20 fw500 sex-text1" v-if="item.sex==='Famale'">
+									{{item.ageInfo.age}}
+								</view>
+								<view class="el font20 fw500 sex-text2" v-if="item.sex==='Male'">
+									{{item.ageInfo.age}}
+								</view>
+							</view>
+							<view class="tip-box font28 fw400 el" v-if="item.desc">
+								签名:{{item.desc}}
+							</view>
+							<view class="tip-box font28 fw400 el" v-else>
+								签名:暂无介绍
+							</view>
+						</view>
+						<view class="img-box flex-between" v-if="item.lastNews">
+							<image :src="item.lastNews.mediaUrls[0]" mode="aspectFill" class="ib1"></image>
+							<image :src="item.lastNews.mediaUrls[1]" mode="aspectFill" class="ib2"></image>
+							<image :src="item.lastNews.mediaUrls[2]" mode="aspectFill" class="ib3"></image>
+						</view>
+					</view>
+					<view class="no-more font24 fw400" v-if="nearList.length!==0&&nearList.length>=nearTotal">没有更多了</view>
+					<Status :type="statusType" :btnText="statusBtnText" :text="statusText" @btnEvent="computedLocation" v-if="showGetLocation"></Status>
+					<Status type="noData" text="暂无数据" v-if="showNoData"></Status>
+				</scroll-view>
+			</swiper-item>
+		</swiper>
+		
+	</view>
+</template>
+
+<script>
+	import TabBar from '@/components/TabBar/TabBar.vue';
+	import Status from '@/components/Status/Status.vue';
+	import wxMap from '@/static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js';
+	import TIM from 'tim-wx-sdk';
+	import Popup from '@/components/Popup/Popup.vue';
+	// import COS from 'cos-wx-sdk-v5';
+	import TIMUploadPlugin from 'tim-upload-plugin';
+
+	 // 腾讯位置服务,手机账号:18996226740
+
+	const wxMapSdk=new wxMap({key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII'});
+	import {get} from '@/util/index.js'
+	export default {
+		components: {
+			TabBar,Status,Popup
+		},
+		data() {
+			return {
+				statusType:'noPos',
+				statusBtnText:'开启定位',
+				statusText:'开启定位后才能帮你找到身边的TA哦',
+				assetsUrl: this.$util.assetsUrl,
+				tabIndex: 0,
+				navs: ['推荐', '附近'],
+				navIndex:0,
+				tops:['魅力榜','壕气榜'],
+				topsIndex:0,
+				scrollHeight:0,
+				rankingOptions:{
+					city:'',
+					cityCode:'',
+					number:3,
+					type:'M'
+				},
+				popup:{
+					content1:'',
+					content2:'',
+					tip1:'',
+					tip2:'',
+					btntext:''
+				},
+				recommendListOptions:{
+					filterIds:[],
+					page:{
+						index:1,
+						size:20,
+						sortValues:[]
+					},
+					queryPre:{
+						city:'',
+						cityCode:'',
+						femaleGoddess:false,
+						femaleNew:false,
+						geo:{
+							lat:0,
+							lon:0
+						},
+						maleNew:false,
+						maleVip:false
+						
+					},
+					showList: true,
+					type: {}
+				},
+				nearListOptions:{
+					filterIds:[],
+					page:{
+						index:1,
+						size:20,
+						sortValues:[]
+					},
+					queryPre:{
+						city:'',
+						cityCode:'',
+						femaleGoddess:false,
+						femaleNew:false,
+						geo:{
+							lat:0,
+							lon:0
+						},
+						maleNew:false,
+						maleVip:false
+						
+					},
+					showList: true,
+					type: {}
+				},
+				showGetLocation:false,
+				showNoData:false,
+				latitude:0,
+				longitude:0,
+				locationCity:'',
+				locationCityCode:'',
+				recommendTopsM:[],
+				recommendTopsH:[],
+				recommendList:[],
+				recommendTotal:0,
+				recommendRefreshing:false,
+				recommendTriggered:true,
+				
+				nearTopsM:[],
+				nearTopsH:[],
+				nearList:[],
+				nearTotal:0,
+				nearRefreshing:false,
+				nearTriggered:true,
+				otherInfo:null
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight||20;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight||40;
+			},
+			userInfo(){
+				return this.$store.state.userInfo||{};
+			}
+		},
+		mounted() {
+			uni.showLoading({
+				mask:true,
+				title:'加载中'
+			})
+			this.computedScollviewHeight();
+			if(!uni.getStorageSync('token')){
+				this.computedLocation();
+			}
+			else{
+				this.computedLocation();
+				this.getRecommendTopsMData();
+				this.getRecommendTopsHData();
+				setTimeout(()=>{
+					this.imInit();
+				},3000)
+				
+			}
+			
+			
+			
+			
+		},
+		// #ifdef MP
+		onShareAppMessage(){
+			return {
+				title: '糖果公园',
+				path: `/pages/login/login?share=${this.userInfo.inviteCode}`,
+			}
+		},
+		// #endif
+		methods: {
+			imInit(){
+				/**
+				 * IM初始化
+				 */
+				if(uni.$TUIKit){
+					uni.$TUIKit.logout();
+				}
+				const SDKAppID = this.$util.IMSDKCode,that=this;
+				uni.$TUIKit = TIM.create({
+					SDKAppID: SDKAppID
+				});
+				uni.$TUIKitTIM = TIM;
+				uni.$TUIKitEvent = TIM.EVENT;
+				uni.$TUIKitVersion = TIM.VERSION;
+				uni.$TUIKitTypes = TIM.TYPES; // 监听系统级事件
+				uni.$TUIKit.on(uni.$TUIKitEvent.SDK_READY, this.onSDKReady);
+				uni.$TUIKit.on(uni.$TUIKitEvent.SDK_NOT_READY, this.onSdkNotReady);
+				uni.$TUIKit.on(uni.$TUIKitEvent.KICKED_OUT, this.onKickedOut);
+				uni.$TUIKit.on(uni.$TUIKitEvent.ERROR, this.onTIMError);
+				uni.$TUIKit.on(uni.$TUIKitEvent.NET_STATE_CHANGE, this.onNetStateChange);
+				uni.$TUIKit.on(uni.$TUIKitEvent.SDK_RELOAD, this.onSDKReload);
+				uni.$resetLoginData = this.resetLoginData();
+				uni.$TUIKit.registerPlugin({ 'tim-upload-plugin':TIMUploadPlugin });
+			},
+			resetLoginData() {
+				let user=JSON.parse(uni.getStorageSync('userInfo'));	
+				this.$api.IM.loadSig({}).then(res=>{
+					this.$store.commit('setImLoadSig',res.data.sig);
+					uni.$TUIKit.login({
+						userID:String(user.id),
+						userSig:this.$store.state.IMloadSig
+					})
+				})
+				
+				uni.hideLoading();
+			},
+			onTIMError() {},
+			onSDKReady({name}) {
+				const isSDKReady = name === uni.$TUIKitEvent.SDK_READY ? true : false;
+				if(isSDKReady){
+					uni.$emit('isSDKReady', {
+						isSDKReady: true
+					});
+					
+				}
+				
+			},
+			
+			onNetStateChange() {},
+			onSDKReload() {},
+			onSdkNotReady() {},
+			onKickedOut() {
+				uni.showToast({
+					title: '您被踢下线',
+					icon: 'error'
+				});
+				uni.reLaunch({
+					url: '/pages/login/login'
+				})
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			recommendRefresh(){
+				if(!uni.getStorageSync('token')){
+					setTimeout(() => {
+						this.recommendTriggered = false;
+						this.recommendRefreshing = false;
+					}, 1000)
+					return;
+				}
+				if (this.recommendRefreshing) 
+				{
+					return;
+				}
+				this.recommendRefreshing = true;
+				setTimeout(() => {
+					this.recommendTriggered = false;
+					this.recommendRefreshing = false;
+				}, 1000)
+				this.recommendListOptions.page={
+					index:1,
+					size:20,
+					sortValues:[]
+				}
+				this.getRecommendTopsMData();
+				this.getRecommendTopsHData();
+				this.getRecommendList();
+			},
+			recommendPulling(e) {},
+			recommendRestore() {this.recommendTriggered = true;},
+			recommendAbort() {},
+			recommendToBottom(){
+				if(this.recommendList.length>=this.recommendTotal){return;}
+				this.recommendListOptions.page.index++;
+				this.getRecommendList();
+			},
+			/**
+			 * 附近下拉刷新、加载更多
+			 */
+			nearRefresh(){
+				if (this.nearRefreshing) 
+				{
+					return;
+				}
+				this.nearRefreshing = true;
+				setTimeout(() => {
+					this.nearTriggered = false;
+					this.nearRefreshing = false;
+				}, 1000)
+				this.nearListOptions.page={
+					index:1,
+					size:20,
+					sortValues:[]
+				}
+				this.getNearTopsMData();
+				this.getNearTopsHData();
+				this.getNearList();
+			},
+			nearPulling(e) {},
+			nearRestore() {this.nearTriggered = true;},
+			nearAbort() {},
+			nearToBottom(){
+				if(this.nearList.length>=this.nearTotal){return;}
+				this.nearListOptions.page.index++;
+				this.getNearList();
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf = this.$store.state.tabbarHeight/2;
+				query.select('#topnav').boundingClientRect(data => {
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+
+			},
+			/**
+			 * 计算列表定位距离
+			 */
+			computedLocation(){
+				// #ifdef MP
+					uni.getFuzzyLocation({
+						type:'gcj02',
+						success:res=>{
+							this.latitude=res.latitude;
+							this.longitude=res.longitude;
+							this.$store.commit('setLatitude',this.latitude);
+							this.$store.commit('setLongitude',this.longitude);
+							wxMapSdk.reverseGeocoder({
+								location:{
+									latitude:res.latitude,
+									longitude:res.longitude
+								},
+								success:result=>{
+									this.locationCity=result.result.ad_info.city;
+									this.locationCityCode=result.result.ad_info.city_code.split(result.result.ad_info.nation_code)[1];
+									this.getTempRecommendList();
+									if(!uni.getStorageSync('token')){
+										this.getTempRecommendList();
+									}
+									else{
+										this.$api.public.heartBeat({
+											city:this.locationCity,
+											cityCode:this.locationCityCode,
+											geo:{
+												lat:this.latitude,
+												lon:this.longitude,
+											}
+											
+										}).then(()=>{
+											this.getNearTopsMData();
+											this.getNearTopsHData();
+											this.getNearList();
+											this.getRecommendList();
+											this.showGetLocation=false;
+										}).catch(err=>{
+											this.getTempRecommendList();
+										})
+									}
+									
+									
+								}
+							})
+							
+						},
+						fail:err=>{
+							this.showGetLocation=true;
+							uni.showModal({
+								content: '检测到您没打开地址信息权限,是否去设置打开?',
+								confirmText: "确认",
+								cancelText: '取消',
+								success:res=>{
+									if(res.confirm){
+										uni.openSetting({
+											success:ress=>{
+												if(ress.authSetting){
+													this.computedLocation();
+												}
+											}
+										})
+									}
+								}
+							})
+						}
+					})
+				// #endif
+				// #ifdef H5
+				console.log('h5')
+					uni.getLocation({
+						type: 'wgs84',
+						success: (res) => {
+							console.log(res)
+							this.latitude=res.latitude;
+							this.longitude=res.longitude;
+							this.$store.commit('setLatitude',this.latitude);
+							this.$store.commit('setLongitude',this.longitude);
+							this.$jsonp('https://apis.map.qq.com/ws/geocoder/v1',{
+								key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII',
+								output:'jsonp',
+								callbackName: 'QQmap',
+								location:`${this.latitude},${this.longitude}`
+							}).then(result=>{
+								console.log(result)
+								this.locationCity=result.result.ad_info.city;
+								this.locationCityCode=result.result.ad_info.city_code.split(result.result.ad_info.nation_code)[1];
+								this.getTempRecommendList();
+								if(!uni.getStorageSync('token')){
+									this.getTempRecommendList();
+								}
+								else{
+									this.$api.public.heartBeat({
+										city:this.locationCity,
+										cityCode:this.locationCityCode,
+										geo:{
+											lat:this.latitude,
+											lon:this.longitude,
+										}
+										
+									}).then(()=>{
+										this.getNearTopsMData();
+										this.getNearTopsHData();
+										this.getNearList();
+										this.getRecommendList();
+										this.showGetLocation=false;
+									}).catch(err=>{
+										this.getTempRecommendList();
+									})
+								}
+							}).catch(err=>{
+								console.log(err)
+							})
+						},
+						fail:err=>{
+							console.log(err);
+						}
+					})
+				// #endif
+					
+			},
+			toLogin(){
+				uni.reLaunch({
+					url:'/pages/login/login'
+				})
+			},
+			navClick(index){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.popup.open();
+					return;
+				}
+				this.rankingOptions.city=(index===0?'':this.locationCity);
+				this.rankingOptions.cityCode=(index===0?'':this.locationCityCode);
+				this.navIndex=index;
+			},
+			toSearch(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.popup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:'/pages/search/search'
+				})
+			},
+			topsNavClick(index){
+				this.topsIndex=index;
+			},
+			outerSwiperChange(e){
+				this.navIndex=e.detail.current;
+			},
+			swiperChange(e){
+				this.topsIndex=e.detail.current;
+			},
+			getRecommendTopsMData(){
+				this.rankingOptions.type='M';
+				this.$api.public.ranking(this.rankingOptions).then(res=>{
+					this.recommendTopsM=res.data.list;
+				})
+			},
+			getRecommendTopsHData(){
+				this.rankingOptions.type='H';
+				this.$api.public.ranking(this.rankingOptions).then(res=>{
+					this.recommendTopsH=res.data.list;
+				})
+			},
+			getNearTopsMData(){
+				this.rankingOptions.type='M';
+				this.rankingOptions.city=this.locationCity;
+				this.rankingOptions.cityCode=this.locationCityCode;
+				this.$api.public.ranking(this.rankingOptions).then(res=>{
+					this.nearTopsM=res.data.list;
+				})
+			},
+			getNearTopsHData(){
+				this.rankingOptions.type='H';
+				this.rankingOptions.city=this.locationCity;
+				this.rankingOptions.cityCode=this.locationCityCode;
+				this.$api.public.ranking(this.rankingOptions).then(res=>{
+					this.nearTopsH=res.data.list;
+				})
+			},
+			getTempRecommendList(){//获取免登录体验数据
+				this.recommendListOptions.type='FamaleReco';
+				this.$api.public.friendsNoAuth(this.recommendListOptions).then(res=>{
+					if(res.status==='Succ'){
+						
+						this.recommendTotal=res.data.page.recordCount;
+						this.recommendListOptions.page.sortValues=res.data.sortValues;
+						
+						let arr=[],obj={latitude:0,longitude:0};
+						for(let i=0;i<res.data.users.length;i++){
+							res.data.users[i].lastActiveTime=this.$moment(new Date()).diff(res.data.users[i].lastActive,'minutes');
+							obj={latitude:0,longitude:0};
+							obj.latitude=res.data.users[i].geo.lat;
+							obj.longitude=res.data.users[i].geo.lon;
+							arr.push(obj)
+						}
+						
+						wxMapSdk.calculateDistance({
+							mode:'straight',
+							from:{
+								latitude: this.latitude,
+								longitude: this.longitude
+							},
+							to:arr,
+							success:dists=>{
+								if(dists.message==="query ok"){
+									for(let j=0;j<dists.result.elements.length;j++){
+											res.data.users[j].distance=(dists.result.elements[j].distance>1000?`${Math.floor(dists.result.elements[j].distance/100)/10}km`:`${dists.result.elements[j].distance}m`)
+										
+									}
+									if(this.recommendListOptions.page.index>1){
+										this.recommendList=[...this.recommendList,...res.data.users];
+									}
+									else{
+										this.recommendList=res.data.users;
+									}
+									uni.hideLoading();
+									this.$forceUpdate();
+								}
+								
+							}
+						})
+						this.recommendList=res.data.users;
+					}
+				})
+			},
+			getRecommendList(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.recommendListOptions.type=user.sex==='male'?'MaleReco':'FamaleReco';
+				this.recommendListOptions.queryPre.city=this.locationCity;
+				this.recommendListOptions.queryPre.cityCode=this.locationCityCode;
+				this.recommendListOptions.queryPre.geo.lat=this.latitude;
+				this.recommendListOptions.queryPre.geo.lon=this.longitude;
+				this.$api.public.friends(this.recommendListOptions).then(res=>{
+					if(res.status==='Succ'){
+						
+						this.recommendTotal=res.data.page.recordCount;
+						this.recommendListOptions.page.sortValues=res.data.sortValues;
+						
+						let arr=[],obj={latitude:0,longitude:0};
+						for(let i=0;i<res.data.users.length;i++){
+							res.data.users[i].lastActiveTime=this.$moment(new Date()).diff(res.data.users[i].lastActive,'minutes');
+							obj={latitude:0,longitude:0};
+							obj.latitude=res.data.users[i].geo.lat;
+							obj.longitude=res.data.users[i].geo.lon;
+							arr.push(obj)
+						}
+						
+						wxMapSdk.calculateDistance({
+							mode:'straight',
+							from:{
+								latitude: this.latitude,
+								longitude: this.longitude
+							},
+							to:arr,
+							success:dists=>{
+								if(dists.message==="query ok"){
+									for(let j=0;j<dists.result.elements.length;j++){
+											res.data.users[j].distance=(dists.result.elements[j].distance>1000?`${Math.floor(dists.result.elements[j].distance/100)/10}km`:`${dists.result.elements[j].distance}m`)
+										
+									}
+									if(this.recommendListOptions.page.index>1){
+										this.recommendList=[...this.recommendList,...res.data.users];
+									}
+									else{
+										this.recommendList=res.data.users;
+									}
+									this.$forceUpdate();
+								}
+								
+							}
+						})
+					}
+				})
+			},
+			getNearList(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.nearListOptions.type=user.sex==='male'?'MaleNearby':'FamaleNearby';
+				this.nearListOptions.queryPre.city=this.locationCity;
+				this.nearListOptions.queryPre.cityCode=this.locationCityCode;
+				this.nearListOptions.queryPre.geo.lat=this.latitude;
+				this.nearListOptions.queryPre.geo.lon=this.longitude;
+				this.$api.public.friends(this.nearListOptions).then(res=>{
+					if(res.status==='Succ'&&res.data.users.length>0){
+						
+						this.nearTotal=res.data.page.recordCount;
+						this.nearListOptions.page.sortValues=res.data.sortValues;
+						
+						this.showNoData=res.data.users.length===0?true:false;
+						let arr=[],obj={latitude:0,longitude:0};
+						for(let i=0;i<res.data.users.length;i++){
+							res.data.users[i].lastActiveTime=this.$moment(new Date()).diff(res.data.users[i].lastActive,'minutes');
+							obj={latitude:0,longitude:0};
+							obj.latitude=res.data.users[i].geo.lat;
+							obj.longitude=res.data.users[i].geo.lon;
+							arr.push(obj)
+						}
+						wxMapSdk.calculateDistance({
+							mode:'straight',
+							from:{
+								latitude: this.latitude,
+								longitude: this.longitude
+							},
+							to:arr,
+							success:dists=>{
+								if(dists.message==="query ok"){
+									for(let j=0;j<dists.result.elements.length;j++){
+											res.data.users[j].distance=(dists.result.elements[j].distance>1000?`${Math.floor(dists.result.elements[j].distance/100)/10}km`:`${dists.result.elements[j].distance}m`)
+									}
+									if(this.nearListOptions.page.index>1){
+										this.nearList=[...this.nearList,...res.data.users];
+									}
+									else{
+										this.nearList=res.data.users;
+									}
+									this.$forceUpdate();
+								}
+								
+							}
+						})
+						
+					}
+					else{
+						this.showNoData=this.nearList.length===0?true:false;
+					}
+				})
+			},
+			toDetail(id){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.popup.open();
+					return;
+				}
+				uni.showLoading({})
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.userDetail({getAlbum:true,completeUser:user,uponUserId:id}).then(res=>{
+					if(res.data.frozen){
+						uni.showToast({
+							title:'该用户已被冻结',
+							icon:'none'
+						});
+						return;
+					}
+					if(res.data.sex===user.sex){
+						uni.showToast({
+							title:'同性用户不能查看主页',
+							icon:'none'
+						})
+					}
+					else{
+						this.otherInfo=res.data;
+						uni.hideLoading();
+						uni.navigateTo({
+							url:`/pages/friends/user?id=${id}`
+						})
+					}
+				})
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		width: 100vw;
+		min-height: 100vh;
+		background-color: $bgcolor1;
+		overflow: hidden;
+		.topnav {
+			margin: 0 60rpx;
+			align-items: center;
+			position: relative;
+			.nav-item {
+				color: $fontcolor3;
+				margin-right: 72rpx;
+				transition: all 0.3s;
+				padding: 0 10rpx;
+				.nav-item-text{
+					position: relative;
+					z-index: 1;
+				}
+			}
+			.nav-search{
+				width:56rpx;
+				height: 56rpx;
+				margin-left: 110rpx;
+				
+			}
+			.nav-item-bg{
+				position: absolute;
+				left: 0;
+				bottom: 10rpx;
+				width: 92rpx;
+				height: 68rpx;
+				z-index: 0;
+				transition: all .3s;
+			}
+		}
+		.location{
+			margin-top: 40rpx;
+			padding: 12rpx;
+			background-color: $bgcolor3;
+			border-radius: 36rpx;
+			.l-pos{
+				width: 32rpx;
+				height: 32rpx;
+			}
+			.l-text{
+				color: $fontcolor2;
+				flex: 1;
+				text-align: left;
+				margin-left: 8rpx;
+			}
+			.l-open{
+				width: 88rpx;
+				height: 48rpx;
+				border-radius: 24rpx;
+				line-height: 48rpx;
+				text-align: center;
+				background-color: $primary;
+				color: $fontcolor5;
+			}
+		}
+		.scroll-view{
+			padding: 24rpx 32rpx 0rpx 32rpx;
+			box-sizing: border-box;
+			.tops-bg{
+				width: 100%;
+				height: 100%;
+				position: absolute;
+				left: 0;
+				top: 0;
+				z-index: 0;
+			}
+			.tops-nav{
+				position: relative;
+				height: 86rpx;
+				z-index: 1;
+				.tops-nav-item{
+					width: 160rpx;
+					text-align: center;
+					color: $fontcolor3;
+				}
+				.tops-nav-item-active{
+					color: $fontcolor5 !important;
+				}
+				.tops-line{
+					width: 24rpx;
+					height: 4rpx;
+					background: $fontcolor5;
+					border-radius: 2rpx;
+					position: absolute;
+					bottom: 0;
+					transition: all 0.3s;
+				}
+			}
+			.swiper{
+				height: 396rpx;
+				.swiper-item{
+					height: 100%;
+					.tops{
+						position: relative;
+						height: 100%;
+						flex-direction: column;
+						
+						.tops-third{
+							height: 100%;
+							.tt-item{
+								position: relative;
+								flex-direction: column;
+								height: 100%;
+								width: 208rpx;
+								.tt-item-bg{
+									width: 196rpx;
+									height: 208rpx;
+									background: linear-gradient(180deg, rgba(226,226,226,0.1000) 0%, rgba(226,226,226,0) 100%);
+									border-radius: 16rpx 16rpx 0rpx 0rpx;
+									position: absolute;
+									bottom: 0;
+								}
+								.tt-item-bg-center{
+									width: 196rpx;
+									height: 248rpx;
+									background: linear-gradient(180deg, rgba(255,236,54,0.1000) 0%, rgba(255,236,54,0) 100%);
+									border-radius: 16rpx 16rpx 0rpx 0rpx;
+									position: absolute;
+									bottom: 0;
+								}
+								.tt-item-img{
+									width: 92rpx;
+									height: 40rpx;
+									transform: translateY(20rpx);
+									position: relative;
+									z-index:20;
+								}
+								.tt-item-img-border{
+									width: 196rpx;
+									height: 192rpx;
+									position: absolute;
+									left: 0;
+									right: 0;
+									margin: auto;
+									transform: translateY(-30rpx);
+								}
+								.tt-item-head-box{
+									width: 136rpx;
+									height: 136rpx;
+									border-radius: 68rpx;
+									position: relative;
+									z-index: 10;
+									
+								}
+								.tt-item-name{
+									width: 136rpx;
+									height: 40rpx;
+									text-align: center;
+									line-height: 40rpx;
+									color: $fontcolor5;
+									margin-bottom: 16rpx;
+									margin-top: 14rpx;
+								}
+								.tt-item-num-box{
+									.tt-item-num-img{
+										width: 32rpx;
+										height: 32rpx;
+									}
+									.tt-item-num-text{
+										color: $fontcolor5;
+										margin-left: 8rpx;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			.list-item{
+				flex-wrap: wrap;
+				padding: 40rpx 0rpx;
+				.list-head-box{
+					width: 136rpx;
+					height: 136rpx;
+					position: relative;
+					.list-head-img{
+						width: 136rpx;
+						height: 136rpx;
+						background-color: #ffffff;
+						border-radius: 136rpx;
+					}
+					.list-head-dot{
+						width: 24rpx;
+						height: 24rpx;
+						border-radius: 24rpx;
+						position: absolute;
+						background-color: aqua;
+						bottom: 2rpx;
+						right: 2rpx;
+						border: 2rpx solid $bgcolor2;
+					}
+				}
+				.list-info-box{
+					box-sizing: border-box;
+					width: 544rpx;
+					padding-left: 32rpx;
+					height: 136rpx;
+					flex-direction: column;
+					display: flex;
+					justify-content: flex-start;
+					align-items: flex-start;
+					.name-box{
+						width: 100%;
+						.name{
+							.name-text{
+								color: $fontcolor5;
+							}
+							.name-img{
+								width: 68rpx;
+								height: 32rpx;
+								margin-left: 8rpx;
+								
+							}
+							.name-img-godness{
+								width: 76rpx;
+								height: 40rpx;
+								margin-left: 8rpx;
+								transform: translateY(-5rpx);
+							}
+						}
+						.distance{
+							color: $fontcolor3;
+						}
+					}
+					.sex-box{
+						padding: 0 15rpx;
+						height: 32rpx;
+						background: rgba(226, 53, 104, 0.2);
+						border-radius: 25rpx;
+						margin-top: 12rpx;
+						.sex-img{
+							width: 24rpx;
+							height: 24rpx;
+						}
+						.sex-text1{
+							color:#E23568;
+							margin-left: 4rpx;
+						}
+						.sex-text2{
+							color:#6C52F4;
+							margin-left: 4rpx;
+						}
+					}
+					.tip-box{
+						color: $fontcolor3;
+						width: 100%;
+						margin-top: 10rpx;
+					}
+					
+				}
+				.img-box{
+					width: 544rpx;
+					height: 168rpx;
+					margin-left: 168rpx;
+					margin-top: 24rpx;
+					.ib1{
+						width: 168rpx;
+						height: 168rpx;
+						border-radius: 16rpx 0rpx 0rpx 16rpx;
+						background: $fontcolor5;
+					}
+					.ib2{
+						width: 168rpx;
+						height: 168rpx;
+						background: $fontcolor5;
+					}
+					.ib3{
+						width: 168rpx;
+						height: 168rpx;
+						border-radius: 0rpx 16rpx 16rpx 0rpx;
+						background: $fontcolor5;
+					}
+				}
+			}
+			
+			
+		}
+	}
+</style>

+ 947 - 0
pages/friends/user.vue

@@ -0,0 +1,947 @@
+<template>
+	<view class="container" ref="container">
+		<!-- @touchmove.stop.prevent="moveStop" -->
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`,'background-color':`rgba(21, 17, 38,${topNavAlpha})`}" v-if="!cover.isFullScreen">
+			<view class="nav-item" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+		</view>
+		<uni-popup ref="popup" type="center">
+			<Popup :content1="popup.content1" :content2="popup.content2" :tip1="popup.tip1" :tip2="popup.tip2" :btntext="popup.btntext" @closePopup="closePopup"  @toService="toService" :btnEvent="'toService'"></Popup>
+		</uni-popup>
+		<uni-popup ref="vippopup" type="center">
+			<VipPopup :swiperIndex="vipPopupIndex" @closePopup="closeVipPopup"></VipPopup>
+		</uni-popup>
+		<uni-popup ref="paypopup" type="bottom" :safe-area="false">
+			<PayPopup :swiperIndex="payPopupIndex" @closePopup="closePayPopup"></PayPopup>
+		</uni-popup>
+		<view class="video-box"  :style="{'height':`${cover.isFullScreen?'100vh':'680rpx'}`,'position':`${cover.isFullScreen?'fixed':'relative'}`}" @click.prevent="videoAction">
+			<video 
+			:src="cover.url" 
+			class="m-video" autoplay loop :controls="false" 
+			muted 
+			object-fit="cover" 
+			v-if="cover.cate==='video'" 
+			:enable-progress-gesture="false"
+			@touchstart="touchStart" @touchend="touchEnd"
+			:style="{'height':`${cover.isFullScreen?'100vh':'680rpx'}`,'position':`${cover.isFullScreen?'fixed':'relative'}`}">
+				<view class="video-inner flex-center" v-if="!cover.isFullScreen">
+					<view class="head-box">
+						<image :src="userInfo.iconThumbnail" mode="aspectFill" class="head-img"></image>
+						<view class="head-active" style="background-color: #38E825;" v-if="userInfo.lastActiveTime<=30"></view>
+						<view class="head-active" style="background-color: #0ABDEF;" v-else-if="userInfo.lastActiveTime>30&&userInfo.lastActiveTime<=1440"></view>
+					</view>
+					
+					<view class="name-box flex-center">
+						<view class="name fw600 font44">
+							{{userInfo.nick}}
+						</view>
+					</view>
+					<view class="age-box fw600 font28">
+						{{userInfo.ageInfo.age}}岁&nbsp;丨&nbsp;{{userInfo.career}}
+					</view>
+					<view class="city-box flex-center" >
+						<image :src="`${assetsUrl}user-pos.png`" mode="aspectFit" class="city-pos"></image> 
+						<view class="city font24 fw500">
+							{{userInfo.currCity}}<text v-if="userInfo.distance&&userInfo.distance!=='NaNm'">&nbsp;·&nbsp;{{userInfo.distance}}</text>
+						</view>
+					</view>
+					<view class="video-edit flex-start" v-if="likeNum>0" @click="likeAction">
+						<image :src="`${assetsUrl}user-heart${isLiked?'3':'2'}.png`" mode="aspectFill" class="video-edit-img"></image>
+						<view class="video-edit-text font22 fw400">
+							{{likeNum}}
+						</view>
+					</view>
+				</view>
+			</video>
+			<view class="m-video" v-else>
+				<view class="video-inner flex-center" v-if="!cover.isFullScreen">
+					<view class="head-box">
+						<image :src="userInfo.iconThumbnail" mode="aspectFill" class="head-img"></image>
+						<view class="head-active" style="background-color: #38E825;" v-if="userInfo.lastActiveTime<=30"></view>
+						<view class="head-active" style="background-color: #0ABDEF;" v-else-if="userInfo.lastActiveTime>30&&userInfo.lastActiveTime<=1440"></view>
+					</view>
+					<view class="name-box flex-center">
+						<view class="name fw600 font44" >
+							{{userInfo.nick}}
+						</view>
+					</view>
+					<view class="age-box fw600 font28">
+						{{userInfo.ageInfo.age}}岁&nbsp;丨&nbsp;{{userInfo.career}}
+					</view>
+					<view class="city-box flex-center">
+						<image :src="`${assetsUrl}user-pos.png`" mode="aspectFit" class="city-pos"></image> 
+						<view class="city font24 fw500">
+							{{userInfo.currCity}}<text v-if="userInfo.distance&&userInfo.distance!=='NaNm'">&nbsp;·&nbsp;{{userInfo.distance}}</text>
+						</view>
+					</view>
+					<view class="video-edit flex-start" v-if="likeNum>0"  @click="likeAction">
+						<image :src="`${assetsUrl}user-heart${isLiked?'3':'2'}.png`" mode="aspectFill" class="video-edit-img"></image>
+						<view class="video-edit-text font22 fw400">
+							{{likeNum}}
+						</view>
+					</view>
+				</view>
+				<image :src="cover.url" mode="widthFix" class="cover-inner"></image>
+				<view class="cover-modal"></view>
+			</view>
+			
+		</view>
+		<view class="info-box" :style="{'top':`${cover.isFullScreen?'100vh':'648rpx'}`}" v-if="!cover.isFullScreen">
+			<view class="tag-box flex-start">
+				<view class="tag flex-center">
+					<image :src="`${assetsUrl}user-height.png`" mode="aspectFill" class="tag-img"></image>
+					<view class="tag-text font28 fw400">
+						{{userInfo.height}}cm
+					</view>
+				</view>
+				<view class="tag flex-center">
+					<image :src="`${assetsUrl}user-weight.png`" mode="aspectFill" class="tag-img"></image>
+					<view class="tag-text font28 fw400">
+						{{userInfo.weight}}kg
+					</view>
+				</view>
+				<view class="tag flex-center">
+					<image :src="`${assetsUrl}user-constellation.png`" mode="aspectFill" class="tag-img"></image>
+					<view class="tag-text font28 fw400">
+						{{userInfo.ageInfo.astro}}
+					</view>
+				</view>
+				<!-- <view class="tag flex-center">
+					<image :src="`${assetsUrl}user-education.png`" mode="aspectFill" class="tag-img"></image>
+					<view class="tag-text font28 fw400">
+						本科
+					</view>
+				</view> -->
+			</view>
+			<view class="live-box flex-start">
+				<view class="live-inner flex-start">
+					<image :src="`${assetsUrl}user-city.png`" mode="aspectFill" class="live-img"></image>
+					<span class="live font28 fw400">常驻:</span><span class="live font28 fw400" v-for="(item,index) in userInfo.cities" :key="index">{{item}}</span>
+				</view>
+				
+			</view>
+			<view class="desc-box font28 fw400" v-if="userInfo.desc">
+				<image :src="`${assetsUrl}user-desc.png`" mode="aspectFill" class="desc-img"></image>
+				{{userInfo.desc}}
+			</view>
+			<view class="tab-box flex-start">
+				<view class="tab-item" @click="tabClick(0)" >
+					<view class="tab-item-text fw700" :style="{
+					'font-size':`${tabIndex===0?'44rpx':'36rpx'}`,
+					'color':`${tabIndex===0?'#ffffff':''}`,
+					'font-weight':`${tabIndex===0?'#ffffff':''}`}">关于{{userInfo.sex==='Famale'?'她':'他'}}</view>
+				</view>
+				<!-- <view class="tab-item" @click="tabClick(1)" 
+					style="margin-left: 72rpx;">
+					<view class="tab-item-text fw700" :style="{
+					'font-size':`${tabIndex===1?'44rpx':'36rpx'}`,
+					'color':`${tabIndex===1?'#ffffff':''}`,
+					'font-weight':`${tabIndex===1?'#ffffff':''}`}">动态</view>
+				</view> -->
+				<image :src="`${assetsUrl}friends-circle.png`" mode="aspectFill" class="tab-item-bg" :style="{'left':`${tabIndex*195+15}rpx`}" ></image>
+			</view>
+			<view class="cup-box flex-start">
+				<view class="cup-item flex-center"  v-if="userInfo.realMan">
+					<image :src="`${assetsUrl}friends-real.png`" mode="aspectFit" class="cup-img" style="width: 76rpx;height: 40rpx;"></image>
+					<view class="cup-text font24" >
+						{{userInfo.sex==='Famale'?'她':'他'}}已完成真人认证
+					</view>
+				</view>
+				<view class="cup-item flex-center"  v-if="userInfo.vip">
+					<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFit" class="cup-img" style="width: 76rpx;height: 40rpx;"></image>
+					<view class="cup-text font24" >
+						{{userInfo.sex==='Famale'?'她':'他'}}是尊贵的vip用户
+					</view>
+				</view>
+<!-- 				<view class="cup-item flex-center">
+					<image :src="`${assetsUrl}user-cup.png`" mode="aspectFit" class="cup-img" style="width: 40rpx;height: 40rpx;"></image>
+					<view class="cup-text font24">
+						重庆市魅力榜第3名
+					</view>
+				</view> -->
+				<view class="cup-item flex-center" v-if="userInfo.goddess&&userInfo.realMan">
+					<image :src="`${assetsUrl}user-charm.png`" mode="aspectFit" class="cup-img" style="width: 76rpx;height: 42rpx;"></image>
+					<view class="cup-text font24">
+						{{userInfo.sex==='Famale'?'她':'他'}}已完成真人认证且颜值较高
+					</view>
+				</view>
+				<view class="cup-item flex-center" v-if="!userInfo.realMan">
+					<image :src="`${assetsUrl}user-no-real.png`" mode="aspectFit" class="cup-img" style="width: 76rpx;height: 42rpx;"></image>
+					<view class="cup-text font24">
+						{{userInfo.sex==='Famale'?'她':'他'}}未完成真人认证、交友谨慎
+					</view>
+				</view>
+			</view>
+			<view class="photo-album">
+				<view class="pics-box flex-center">
+					<image :src="currentPic.url" mode="widthFix" class="big-pic" v-if="currentPic.cate==='Img'" @click="preview"></image>
+					<video :src="currentPic.url" 
+					class="big-pic" 
+						autoplay 
+						loop 
+						:controls="false" 
+						muted 
+						object-fit="fill" 
+						v-if="currentPic.cate==='Vdo'" 
+						:style="{'height':`${currentPic.height}px`}"
+						@click="preview">
+					</video>
+					<scroll-view scroll-x="true" class="pics-list flex-start" v-if="pics.length>1">
+						<view class="pic-item flex-center"  v-for="(item,index) in pics" :key="index" @click="picItemClick(index)">
+							<image :src="item.urlThumbnail" mode="aspectFill" class="pic-img" :class="currentIndex===index?'active-item':''"></image>
+						</view>
+					</scroll-view>
+					<image :src="`${assetsUrl}user-self.png`" mode="aspectFill" class="self" v-if="currentPic.authStatus==='Succ'"></image>
+				</view>
+			</view>
+			<view class="hopes-box"  v-if="userInfo.hobbysStr.length>0">
+				<view class="title">
+					{{userInfo.sex==='Famale'?'她':'他'}}的标签
+				</view>
+				<view class="hopes flex-start">
+					<view class="hopes-item font28"  v-for="(item,index) in userInfo.hobbysStr" :key="index">
+						{{item}}
+					</view>
+				</view>
+			</view>
+			<view class="hopes-box" v-if="userInfo.hopesStr.length>0">
+				<view class="title">
+					{{userInfo.sex==='Famale'?'她':'他'}}喜欢的
+				</view>
+				<view class="hopes flex-start">
+					<view class="hopes-item font28"  v-for="(item,index) in userInfo.hopesStr" :key="index">
+						{{item}}
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="btn-box flex-between">
+			<view class="talk-btn flex-center" :style="{'width':`${isLiked?'100%':'320rpx'}`}" @click="toTalk">
+				<image :src="`${assetsUrl}user-talk.png`" mode="aspectFit" class="btn-icon"></image>
+				<text class="btn-text font32 fw600">聊一聊</text>
+			</view>
+			<view class="like-btn flex-center" style="width: 320rpx;" @click="likeAction" v-if="!isLiked">
+				<image :src="`${assetsUrl}user-heart1.png`" mode="aspectFit" class="btn-icon"></image>
+				<text class="btn-text font32 fw600">喜欢TA</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import TabBar from '@/components/TabBar/TabBar.vue';
+	import wxMap from '@/static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js';
+	import Popup from '@/components/Popup/Popup.vue';
+	import PayPopup from '@/components/Popup/PayPopup.vue';
+	import VipPopup from '@/components/Popup/VipPopup.vue';
+	/**
+	 * 腾讯位置服务,手机账号:18996226740
+	 */
+	const wxMapSdk=new wxMap({key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII'})
+	export default {
+		components:{TabBar,Popup,PayPopup,VipPopup},
+		data() {
+			return {
+				scrollHeight:0,
+				topNavAlpha:0,
+				assetsUrl:this.$util.assetsUrl,
+				startTime:0,
+				startPosition:0,
+				endPosition:0,
+				currentPic:null,
+				currentIndex:0,
+				pics:[],
+				userInfo:null,//用户信息
+				cover:{
+					url:'',
+					cate:'video',
+					isFullScreen:false
+				},
+				labels:[],
+				hopes:[],
+				liveCitys:['1','2'],
+				tabIndex:0,
+				id:null,
+				latitude: 0,
+				longitude: 0,
+				hobbysEnums:[],
+				hopesEnums:[],
+				likeNum:0,
+				isLiked:false,
+				popup:{
+					content1:'',
+					content2:'',
+					tip1:'',
+					tip2:'',
+					btntext:''
+				},
+				payPopupIndex:-1,
+				vipPopupIndex:-1,
+				scrollTop:0
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			platForm(){
+				return this.$store.state.platform
+			},
+			mineInfo(){
+				return this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))
+			}
+		},
+		onLoad(options) {
+		
+			this.getConfig();
+			this.id=options.id;
+			this.getUserDetail();
+			this.likeInit();
+			this.getLikeData();
+			this.getMineDetail()
+		},
+		mounted() {
+			
+			
+		},
+		onPageScroll(e) {
+			this.topNavAlpha=e.scrollTop/250;
+			this.scrollTop=e.scrollTop;
+		},
+		onShareAppMessage(){
+			return {
+				title: '糖果公园',
+				path: `/pages/login/login?share=${this.userInfo.inviteCode}`,
+			}
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			moveStop(){
+				return false
+			},
+			closePopup(){
+				this.$refs.popup.close();
+				
+			},
+			closePayPopup(){
+				this.payPopupIndex=-1;
+				this.$refs.paypopup.close();
+			},
+			closeVipPopup(){
+				this.vipPopupIndex=-1;
+				this.$refs.vippopup.close();
+			},
+			likeInit(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.isLike({
+					completeUser:user,
+					opte:'Fav',
+					rdtn:'Do',
+					uurd:this.id
+				}).then(res=>{
+					if(res.data.msg==='该用户已被收藏'){
+						this.isLiked=true;
+					}else{
+						this.$api.public.isLike({
+							completeUser:user,
+							opte:'Fav',
+							rdtn:'Cancel',
+							uurd:this.id
+						}).then(result=>{})
+					}
+				})
+			},
+			toService(){
+				this.$refs.popup.close();
+				uni.openCustomerServiceChat({
+				     extInfo:{
+				         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+				     },
+				     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+				     fail(res){
+						 console.log(res)
+				         wx.showToast({
+				           title: '客服联系失败',
+				           icon:'none'
+				         })
+				     }
+				 })
+			},
+			likeAction(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.isLike({
+					completeUser:user,
+					opte:'Fav',
+					rdtn:this.isLiked?'Cancel':'Do',
+					uurd:this.id
+				}).then(res=>{
+					if(res.data.succ){
+						this.isLiked=!this.isLiked;
+						if(this.isLiked){
+							this.likeNum++;
+						}else{
+							this.likeNum--;
+						}
+					}
+				})
+			},
+			preview(){
+				let arr=[],obj={};
+				for(let i=0;i<this.pics.length;i++){
+					console.log(this.pics[i])
+					obj={url:'',type:'',width:'',height:''};
+					if(this.pics[i].cate==='Img'){
+						obj.type='image';
+					}
+					if(this.pics[i].cate==='Vdo'){
+						obj.type='video';
+						obj.width=686;
+						obj.height=this.pics[i].h/(this.pics[i].w/343)*2
+					}
+					obj.url=this.pics[i].url;
+					arr.push(obj)
+				}
+				console.log(arr)
+				uni.previewMedia({
+					sources:arr,
+					current:this.currentIndex,
+					showmenu:true
+				})
+			},
+			tabClick(index){
+				this.tabIndex=index;
+			},
+			touchStart(e){
+				this.startTime = Date.now()
+				this.startPosition = e.changedTouches[0].clientY
+			},
+			touchEnd(e){
+				const endTime = Date.now()
+				if (endTime - this.startTime <100){
+					return;
+				}
+				if (Math.abs(this.endPosition - this.startPosition) > 10){
+					this.endPosition = e.changedTouches[0].clientY;
+					let elePosition = this.endPosition - this.startPosition > 0 ? "toBottom": "toTop"
+					if(elePosition==='toBottom'){
+						this.cover.isFullScreen=true;
+					}
+					else{
+						this.cover.isFullScreen=false;
+					}
+				} else {
+					return;
+				}
+			},
+			videoAction(){
+				if(this.cover.isFullScreen){
+					this.cover.isFullScreen=false;
+				}
+			},
+			getUserDetail(){
+				let pages = getCurrentPages();
+				let prevPage = pages[pages.length - 2];
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.userInfo=prevPage.$vm.otherInfo;
+				this.pics=this.userInfo.medias;
+				if(user.sex==='Male'){
+					this.$api.public.seeMaleDetail({completeUser:user,jsjf:this.id}).then(()=>{})
+				}else if(user.sex==='Famale'){
+					this.$api.public.seeFamaleDetail({completeUser:user,pusd:this.id}).then(()=>{})
+				}
+				
+				this.userInfo.lastActiveTime=this.$moment(new Date()).diff(this.userInfo.lastActive,'minutes');
+				this.currentPic=this.userInfo.medias[0];
+				if(this.userInfo.bkg.indexOf('.mp4')!==-1){
+					this.cover.cate='video';
+					this.cover.url=this.userInfo.bkg;
+				}
+				else{
+					this.cover.cate='img';
+					this.cover.url=this.userInfo.bkg;
+				}
+				wxMapSdk.calculateDistance({
+					mode:'straight',
+					from:{
+						latitude: this.$store.state.latitude,
+						longitude: this.$store.state.longitude
+					},
+					to:[{
+						latitude: this.userInfo.geo.lat,
+						longitude: this.userInfo.geo.lon
+					}],
+					success:dists=>{
+							this.userInfo.distance=(dists.result.elements[0].distance>1000?`${Math.floor(dists.result.elements[0].distance/100)/10}km`:`${dists.result.elements[0].distance}m`)
+						
+					}
+				});
+				if(this.userInfo.hobbies){
+					let arr=[];
+					this.userInfo.hobbies.forEach((item,index)=>{
+						this.hobbysEnums.forEach((sitem,sindex)=>{
+							if(item===sitem.key&&!arr.includes(sitem.desc)){
+								arr.push(sitem.desc)
+							}
+						})
+					})
+					this.userInfo.hobbysStr=arr;
+				}
+				if(this.userInfo.wanteds){
+					let arr=[];
+					this.userInfo.wanteds.forEach((item,index)=>{
+						this.hopesEnums.forEach((sitem,sindex)=>{
+							if(item===sitem.key&&!arr.includes(sitem.desc)){
+								arr.push(sitem.desc)
+							}
+						})
+					})
+					this.userInfo.hopesStr=arr;
+				}
+				// this.$api.public.userDetail({getAlbum:true,completeUser:user,uponUserId:this.id}).then(res=>{
+				// 	if(res.status==='Succ'){
+						
+						
+						
+				// 	}
+				// })
+			},
+			picItemClick(index){
+				this.currentIndex=index;
+				this.currentPic=this.pics[index];
+				this.currentPic.height=this.currentPic.h/(this.currentPic.w/343);
+				this.$nextTick(()=>{
+					setTimeout(() => { uni.pageScrollTo({scrollTop: this.scrollTop, duration: 0});}, 50);
+				});
+			},
+			getConfig(){
+				this.$api.public.config({tscsj:['Hobbys','Hopes']}).then(res=>{
+					this.hobbysEnums=res.data.hobbys;
+					this.hopesEnums=res.data.hopes;
+				}).catch(err=>{
+					console.log(err)
+				})
+			},
+			getLikeData(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.userData({
+					completeUser:user,
+					listType:'FavsForMe',
+					page:{
+						index:1,
+						size:20,
+						sortValues:[]
+					},
+					uponUserId:this.id
+				}).then(res=>{
+					this.likeNum=res.data;
+				})
+			},
+			toTalk(){
+				console.log(this.userInfo,this.mineInfo);
+				if(this.mineInfo.sex==='Male'&&!this.mineInfo.vip){
+					if(this.platForm==='ios'){
+						this.vipPopupIndex=0;
+						this.$refs.vippopup.open();
+					}else{
+						this.payPopupIndex=0;
+						this.$refs.paypopup.open();
+					}
+					
+					return;
+				}
+				if(this.mineInfo.sex==='Famale'&&!this.mineInfo.realMan){
+					this.popup={
+						content1:'认证后才能开启私聊哦',
+						content2:'给客服回复关键词「真人认证」',
+						tip1:'',
+						tip2:'',
+						btntext:'联系客服去认证'
+					}
+					this.$refs.popup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:`/pagesSub/chatting/chatting?conversationid=C2C${this.userInfo.id}`
+				})
+			},
+			getMineDetail(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.mineDetail({
+					getAlbum:true,
+					completeUser:user
+				}).then(res=>{
+					this.$store.commit('setUserInfo',res.data);
+					uni.setStorageSync('userInfo',JSON.stringify(res.data));//正式环境删除
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		backdrop-filter: blur(10px);
+		.nav-item {
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+			
+		}
+		.nav-edit{
+			width:120rpx;
+			height: 56rpx;
+			border-radius: 56rpx;
+			color: $fontcolor5;
+			line-height: 56rpx;
+			text-align: center;
+			background-color: rgba(0, 0, 0, 0.5);
+		}
+	}
+	.video-box{
+		width: 100vw;
+		transition: all 0.3s;
+		.m-video{
+			width: 100vw;
+			position: relative;
+			.video-inner{
+				flex-direction: column;
+				height: 680rpx;
+				transform: translateY(30rpx);
+				position: relative;
+				z-index: 10;
+				.head-box{
+					position: relative;
+					width: 168rpx;
+					height: 168rpx;
+					border-radius: 72rpx;
+					.head-img{
+						width: 168rpx;
+						height: 168rpx;
+						border-radius: 72rpx;
+						border: 4rpx solid #FFFFFF;
+					}
+					.head-active{
+						width: 24rpx;
+						height: 24rpx;
+						border-radius: 24rpx;
+						position: absolute;
+						bottom: 0;
+						right: 0;
+					}
+				}
+
+				.name-box{
+					height: 60rpx;
+					margin-top: 24rpx;
+					.name{
+						color:  $fontcolor5;
+						height: 60rpx;
+						line-height: 60rpx;
+					}
+				}
+				.age-box{
+					color: $fontcolor5;
+					height: 40rpx;
+					line-height: 40rpx;
+				}
+				.city-box{
+					height: 34rpx;
+					.city-pos{
+						width: 24rpx;
+						height: 24rpx;
+						margin-right: 4rpx;
+					}
+					.city{
+						height: 34rpx;
+						line-height: 34rpx;
+						color: $fontcolor5;
+					}
+				}
+				.video-edit{
+					position: absolute;
+					bottom: 80rpx;
+					right: 32rpx;
+					height: 48rpx;
+					background-color:rgba(0, 0, 0, 0.5);
+					padding: 0 20rpx;
+					border-radius: 48rpx;
+					z-index: 6;
+					.video-edit-img{
+						width: 24rpx;
+						height: 24rpx;
+						margin-right: 4rpx;
+					}
+					.video-edit-text{
+						margin-left: 16rpx;
+						line-height: 48rpx;
+						color: $fontcolor5;
+					}
+				}
+			}
+			.cover-inner{
+				width: 100vw;
+				height: 680rpx;
+				position: absolute;
+				top: 0;
+				z-index: 4;
+			}
+			.cover-modal{
+				position: absolute;
+				top: 0;
+				width: 100vw;
+				height: 680rpx;
+				z-index: 5;
+				background: rgba(0,0,0,0.4);
+				backdrop-filter: blur(20rpx);
+			}
+		}
+	}
+	.info-box{
+		position: absolute;
+		z-index: 20;
+		left: 0;
+		width: 100vw;
+		padding-bottom: 300rpx;
+		background-color: $bgcolor1;
+		transition: top .3s;
+		border-radius: 40rpx 40rpx 0rpx 0rpx;
+		.tag-box{
+			margin: 48rpx 32rpx;
+			.tag{
+				width: 164rpx;
+				height: 96rpx;
+				background-color: $bgcolor3;
+				border-radius: 24rpx;
+				margin-right: 10rpx;
+				.tag-img{
+					width: 32rpx;
+					height: 32rpx;
+				}
+				.tag-text{
+					color: $fontcolor5;
+					margin-left: 4rpx;
+				}
+			}
+		}
+		.live-box{
+			margin: 24rpx 32rpx 0rpx 32rpx;
+			
+			
+			.live-inner{
+				padding: 28rpx;
+				background-color: $bgcolor3;
+				max-width: 100%;
+				border-radius: 24rpx;
+				flex-wrap: wrap;
+				.live-img{
+					width: 32rpx;
+					height: 32rpx;
+					display: inline;
+				}
+				.live{
+					padding: 2rpx 16rpx;
+					color: $fontcolor5;
+				}
+			}
+			
+		}
+		.desc-box{
+			padding: 56rpx 48rpx 56rpx 72rpx;
+			margin: 56rpx 32rpx 0rpx 32rpx;
+			position: relative;
+			background: linear-gradient(180deg, #1F1A30 0%, #151126 100%);
+			color: $fontcolor5;
+			.desc-img{
+				position: absolute;
+				left: 0;
+				top: -40rpx;
+				width: 88rpx;
+				height: 88rpx;
+			}
+		}
+		.tab-box{
+			margin: 66rpx 32rpx 0rpx 32rpx;
+			position: relative;
+			align-items: baseline;
+			min-height: 76rpx;
+			.tab-item{
+				width: 144rpx;
+				.tab-item-text{
+					height: 100%;
+					line-height: 70rpx;
+					position: relative;
+					z-index: 1;
+					color: $fontcolor3;
+					transition: all 0.3s;
+				}
+			}
+			.tab-item-bg{
+				position: absolute;
+				left: 0;
+				bottom: 10rpx;
+				width: 96rpx;
+				height: 68rpx;
+				padding: 0rpx 5rpx;
+				z-index: 0;
+				transition: all .3s;
+			}
+		}
+		.cup-box{
+			flex-wrap: wrap;
+			margin: 24rpx 32rpx 0rpx 32rpx;
+			.cup-item{
+				height: 56rpx;
+				background-color: $bgcolor4;
+				border-radius: 28rpx;
+				margin-right: 20rpx;
+				margin-top: 20rpx;
+				padding: 0rpx 20rpx;
+				.cup-img{
+				}
+				.cup-text{
+					display: inline;
+					color: $fontcolor5;
+					margin-left: 8rpx;
+				}
+			}
+		}
+		.photo-album{
+			margin: 56rpx 32rpx 0rpx 32rpx;
+			.pa-box{
+				.pa-title{
+					color: $fontcolor5;
+				}
+				.pa-tip{
+					color: $fontcolor3;
+				}
+			}
+			.pics-box{
+				width: 100%;
+				border-radius: 16rpx;
+				margin-top: 26rpx;
+				position: relative;
+				margin-bottom: 30rpx;
+				overflow: hidden;
+				
+				.self{
+					position: absolute;
+					width: 72rpx;
+					height: 40rpx;
+					right: 24rpx;
+					top: 24rpx;
+					z-index: 100;
+				}
+				.big-pic{
+					width: 100%;
+					border-radius: 40rpx;
+				}
+				.pics-list{
+					width: 100%;
+					height: 186rpx;
+					position: absolute;
+					bottom: -35rpx;
+					white-space:nowrap;
+					transform: translateY(-20rpx);
+					.pic-item{
+						display: inline-block;
+						width: 96rpx;
+						height: 128rpx;
+						border-radius: 16rpx;
+						margin-left: 16rpx;
+						box-shadow:0rpx 0rpx 10rpx 1rpx #151126;
+						transform: translateY(10rpx);
+						&:nth-last-of-type(1){
+							margin-right: 16rpx;
+						}
+						.pic-img{
+							width: 96rpx;
+							height: 128rpx;
+							border-radius: 16rpx;
+							box-sizing: border-box;
+							border: 4rpx solid transparent;
+							transition: all 0.3s;
+						}
+					}
+					.active-item{
+						border: 4rpx solid #ffffff !important;
+					}
+				}
+			}
+			
+		}
+		.hopes-box{
+			margin: 56rpx 32rpx;
+			.title{
+				color: $fontcolor5;
+				margin-bottom: 12rpx;
+			}
+			.hopes{
+				flex-wrap: wrap;
+				.hopes-item{
+					padding: 12rpx 20rpx;
+					border-radius: 16rpx;
+					color: $fontcolor3;
+					background-color: $bgcolor3;
+					margin-left: 20rpx;
+					margin-top: 20rpx;
+					&:nth-of-type(1){
+						margin-left: 0rpx;
+					}
+				}
+			}
+		}
+	}
+	.btn-box{
+		padding: 0rpx 32rpx;
+		width: 100vw;
+		box-sizing: border-box;
+		position: fixed;
+		z-index:20;
+		bottom: 100rpx;
+		.talk-btn{
+			
+			margin-right: 12rpx;
+			height: 104rpx;
+			background: $primary;
+			border-radius: 52rpx;
+		}
+		.like-btn{
+			margin-left: 12rpx;
+			height: 104rpx;
+			background: linear-gradient(133deg, #FA5C74 0%, #BC55BF 53%, #8A4FFF 100%);
+			border-radius: 52rpx;
+		}
+		.btn-icon{
+			width: 40rpx;
+			height: 40rpx;
+		}
+		.btn-text{
+			color: $fontcolor5;
+			margin-left: 8rpx;
+		}
+	}
+}
+</style>

+ 274 - 0
pages/info/city.vue

@@ -0,0 +1,274 @@
+<template>
+	<view class="container">
+		<TopBar id="topbar"></TopBar>
+		<view class="line-bar flex-between" id="linebar" v-if="type==='add'">
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span"></view>
+		</view>
+		<view class="title font44 fw600" id='title'>
+			选择你常驻的城市
+		</view>
+		<view class="tip font28 fw400" id="tip">
+			选择你居住的、经常去的城市,以便推荐异性朋友
+		</view>
+		<view class="choose-list flex-start" id="chooselist">
+			<view class="choose-tag flex-center" v-for="(item,index) in chooseCitys" :key="index">
+				<text class="tag-text font28 fw600">{{item.name}}</text>
+				<image :src="`${assetsUrl}info-figure-close.png`" mode="aspectFill" class="tag-img" @click="deleteTag(index)"></image>
+			</view>
+		</view>
+		<scroll-view scroll-y="true" class="citys" :style="{'height':`${scollHeight}px`}">
+			<view class="city-tags" v-for="(item,index) in cityList" :key="index">
+				<view class="c-label font28 fw700">
+					{{item.name}}
+				</view>
+				<view class="c-tags flex-start">
+					<view class="c-tag font28 fw400" :class="sitem.choose?'tag-choose':''" v-for="(sitem,sindex) in item.data" :key="sindex" @click="chooseTag(index,sindex)">
+						{{sitem.name}}
+					</view>
+				</view>
+				
+			</view>
+			
+		</scroll-view>
+		<cover-view class="btn font32 fw600" @click="sure">
+			{{type==='add'?'下一步':'完成'}}
+		</cover-view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				type:'add',
+				assetsUrl:this.$util.assetsUrl,
+				chooseCitys:[],
+				cityList:[],
+				scollHeight:0,
+				saveOption:{
+					cities:[],
+					completeUser:null,
+				}
+			};
+		},
+		onLoad(option) {
+			console.log(option)
+			if(option.type==='edit'){
+				this.type="edit";
+				let pages = getCurrentPages();
+				let prevPage = pages[pages.length - 2];
+				this.chooseCitys=prevPage.$vm.myCitys;
+			}
+			this.computedScollviewHeight();
+			this.getHotCitys()
+		},
+		methods:{
+			computedScollviewHeight(){
+				let query = uni.createSelectorQuery();
+				let heightLeaf=0;
+				let str=this.type==='add'?'#topbar,#linebar,#title,#tip,#chooselist':'#topbar,#title,#tip,#chooselist'
+				query.selectAll(str).boundingClientRect(data => {
+					data.forEach(item=>{
+						heightLeaf+=item.height;
+					})
+				}).exec(()=>{
+					let sysInfo=uni.getSystemInfoSync();
+					this.scollHeight=sysInfo.windowHeight-heightLeaf-(this.type==='add'?160:145);
+					console.log(this.scollHeight)
+				});
+				
+			},
+			sure(){
+				if(this.type==='add'){
+					if(this.chooseCitys.length===0){
+						uni.showToast({
+							title:'请选择常驻城市',
+							icon:'none'
+						})
+						return;
+					}
+					for(let i=0;i<this.chooseCitys.length;i++){
+						this.saveOption.cities.push(`${this.chooseCitys[i].name}`);
+					}
+					this.saveOption.completeUser=JSON.parse(uni.getStorageSync('user'));
+					uni.showLoading({
+						title:'保存中',
+						mask:true
+					})
+					this.$api.login.saveCity(this.saveOption).then(res=>{
+						if(res.data.succ){
+							uni.setStorageSync('regStep','WxInfo');
+							uni.hideLoading();
+							uni.reLaunch({
+								url:`/pages/info/wechat`
+							})
+						}
+					})
+				}else{
+					let pages = getCurrentPages();
+					let prevPage = pages[pages.length - 2];
+					let arr=[];
+					for(let i=0;i<this.chooseCitys.length;i++){
+						arr.push(`${this.chooseCitys[i].name}`);
+					}
+					console.log(arr)
+					prevPage.$vm.myCitys=arr;
+					uni.navigateBack({
+						delta:1
+					})
+				}
+					
+				
+				
+			},
+			deleteTag(index){
+				this.chooseCitys.splice(index,1);
+				setTimeout(()=>{
+					this.computedScollviewHeight();
+				},10)
+				
+			},
+			chooseTag(index,sindex){
+				if(this.chooseCitys.length>5){return;}
+				this.cityList[index].data[sindex].choose=!this.cityList[index].data[sindex].choose;
+				if(this.cityList[index].data[sindex].choose&&!this.chooseCitys.includes({name:this.cityList[index].data[sindex].name,code:this.cityList[index].data[sindex].code})){
+					this.chooseCitys.push({name:this.cityList[index].data[sindex].name,code:this.cityList[index].data[sindex].code})
+				}
+				setTimeout(()=>{
+					this.computedScollviewHeight();
+				},10)
+				setTimeout(()=>{
+					this.cityList[index].data[sindex].choose=false;
+				},500)
+			},
+			getHotCitys(){
+				let arr=[],obj={name:'',code:'',data:[]},obj1={name:'',code:'',choose:false}
+				this.$api.login.popularCities({}).then(res=>{
+					for(let i=0;i<res.data.cities.length;i++){
+						obj={name:'',code:'',data:[]};
+						obj.name=res.data.cities[i].n;
+						obj.code=res.data.cities[i].c;
+						for(let j=0;j<res.data.cities[i].cities.length;j++){
+							obj1={name:'',code:'',choose:false}
+							obj1.name=res.data.cities[i].cities[j].split('#')[1];
+							obj1.code=res.data.cities[i].cities[j].split('#')[0];
+							obj.data.push(obj1);
+						}
+						arr.push(obj)
+					}
+					this.cityList=arr;
+				})
+			},
+			bindClick(e){
+				console.log(e)
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	min-height: 100vh;
+	background-color: $bgcolor1;
+	.line-bar{
+		margin: 0 60rpx;
+		margin-top: 30rpx;
+		.span{
+			width: 120rpx;
+			height: 8rpx;
+			border-radius: 4rpx 0rpx 0rpx 4rpx;
+			background-color: $bgcolor4;
+		}
+		.active{
+			background-color: $primary;
+		}
+	}
+	.title{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+		margin-top: 108rpx;
+	}
+	.tip{
+		color: $fontcolor3;
+		padding-left: 60rpx;
+		margin-top: 8rpx;
+	}
+	.choose-list{
+		padding: 72rpx 60rpx 8rpx 60rpx;
+		flex-wrap: wrap;
+		.choose-tag{
+			background-color: $bgcolor4;
+			border-radius: 38rpx;
+			padding: 0 20rpx;
+			height: 72rpx;
+			margin-right: 32rpx;
+			margin-bottom: 32rpx;
+			.tag-text{
+				color: $fontcolor4;
+				text-align: center;
+				line-height: 72rpx;
+			}
+			.tag-img{
+				width: 24rpx;
+				height: 24rpx;
+				padding-left: 16rpx;
+			}
+		}
+	}
+	.citys{
+		height: 700rpx;
+		padding: 0 60rpx;
+		box-sizing: border-box;
+		.city-tags{
+			.c-label{
+				color: $fontcolor3;
+				margin-bottom: 24rpx;
+				margin-top: 16rpx;
+			}
+			.c-tags{
+				flex-wrap: wrap;
+				.c-tag{
+					background-color: $bgcolor3;
+					color: $fontcolor3;
+					padding: 0 20rpx;
+					height: 72rpx;
+					line-height: 72rpx;
+					text-align: center;
+					border-radius: 38rpx;
+					margin-right: 24rpx;
+					margin-bottom: 24rpx;
+					transition: all .3s;
+				}
+			}
+			
+			.tag-choose{
+				background: $primary !important;
+				color: $fontcolor5 !important;
+			}
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+</style>

+ 248 - 0
pages/info/datum.vue

@@ -0,0 +1,248 @@
+<template>
+	<view class="container">
+		<TopBar></TopBar>
+		<view class="line-bar flex-between">
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span"></view>
+			<view class="span"></view>
+		</view>
+		<view class="title font44 fw600">
+			设置你的社交资料
+		</view>
+		<view class="tip font28 fw400">
+			10秒填写基本信息,邂逅你喜欢的新朋友
+		</view>
+		<view class="select-title font28 fw400">
+			你的身高
+		</view>
+		<picker mode="selector" :range="heights" :value="userInfo.sex==='Male'?'20':'15'" @change="heightsChange">
+			<view class="select flex-between">
+				<view class="select-val font28 fw400" :style="{'color':`${height==='选择身高'?'':'#ffffff'}`}">
+					{{height}}
+				</view>
+				<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+			</view>
+		</picker>
+		<view class="select-title font28 fw400">
+			你的体重
+		</view>
+		<picker mode="selector" :range="weights" :value="userInfo.sex==='Male'?'30':'15'" @change="weightsChange">
+			<view class="select flex-between">
+				<view class="select-val font28 fw400" :style="{'color':`${weight==='选择体重'?'':'#ffffff'}`}">
+					{{weight}}
+				</view>
+				<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+			</view>
+		</picker>
+		<view class="select-title font28 fw400">
+			你的年龄
+		</view>
+		<picker mode="date" fields="day" start="1963-01-01" end="2004-01-01" value="1993-09-12" @change="agesChange">
+			<view class="select flex-between">
+				<view class="select-val font28 fw400" :style="{'color':`${age==='选择年龄'?'':'#ffffff'}`}">
+					{{age}}
+				</view>
+				<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+			</view>
+		</picker>
+		<view class="select-title font28 fw400">
+			你的职业
+		</view>
+		<picker mode="multiSelector" :range="jobs" @change="jobsChange" @columnchange="jobsColumnChange">
+			<view class="select flex-between">
+				<view class="select-val font28 fw400" :style="{'color':`${job==='选择职业'?'':'#ffffff'}`}">
+					{{job}}
+				</view>
+				<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+			</view>
+		</picker>
+		<view style="height: 200rpx;"></view>
+		<cover-view class="btn font32 fw600" @click="sure">
+			下一步
+		</cover-view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				heights:[],
+				height:'选择身高',
+				weights:[],
+				weight:'选择体重',
+				age:'选择年龄',
+				jobs:[],
+				careersData:[],
+				job:'选择职业',
+				saveOption:{
+					birthday:'',
+					completeUser:null,
+				},
+				userInfo:null
+			};
+		},
+		mounted(){
+			this.userInfo=JSON.parse(uni.getStorageSync('userInfo'))
+			let arr=[];
+			for(let i=150;i<210;i++){
+				arr.push(`${i}cm`);
+			}
+			this.heights=arr;
+			arr=[];
+			for(let i=30;i<200;i++){
+				arr.push(`${i}kg`);
+			}
+			this.weights=arr;
+			this.getCareersConfig();
+		},
+		methods:{
+			getCareersConfig(){
+				this.$api.public.config({tscsj:['Careers']}).then(res=>{
+					this.careersData=res.data.careers;
+					let arr=[],arr1=[];
+					for(let item in res.data.careers){
+						arr1.push(item)
+					}
+					arr.push(arr1);
+					arr.push(res.data.careers[arr1[0]]);
+					this.jobs=arr;
+				}).catch(err=>{
+					console.log(err)
+				})
+			},
+			sure(){
+				if(this.height==='选择身高'){uni.showToast({title:'请选择身高',icon:'none'});return;}
+				if(this.weight==='选择体重'){uni.showToast({title:'请选择体重',icon:'none'});return;}
+				if(this.age==='选择年龄'){uni.showToast({title:'请选择出生年月',icon:'none'});return;}
+				if(this.job==='选择职业'){uni.showToast({title:'请选择职业',icon:'none'});return;}
+				let user=JSON.parse(uni.getStorageSync('user'));
+				user.career=this.job;
+				user.height=Number(this.height.substring(0,2));
+				user.weight=Number(this.weight.split('kg')[0]);
+				this.saveOption.completeUser=user;
+				this.saveOption.birthday=this.age;
+				uni.showLoading({
+					title:'保存中',
+					mask:true
+				})
+				this.$api.login.saveDatum(this.saveOption).then(res=>{
+					if(res.data.succ){
+						uni.setStorageSync('regStep','CityStay');
+						uni.setStorageSync('user',JSON.stringify(user));
+						uni.hideLoading();
+						uni.reLaunch({
+							url:`/pages/info/city`
+						})
+					}
+				}).catch(()=>{})
+			},
+			heightsChange(e){
+				this.height=this.heights[e.detail.value];
+			},
+			weightsChange(e){
+				this.weight=this.weights[e.detail.value];
+			},
+			agesChange(e){
+				this.age=e.detail.value;
+			},
+			jobsChange(e){
+				let str=this.jobs[1][e.detail.value[1]];
+				this.job=str;
+			},
+			jobsColumnChange(e){
+				let leftValue=this.jobs[e.detail.column][e.detail.value];
+				if(e.detail.column===0){
+					this.jobs[1]=this.careersData[leftValue];
+					this.$forceUpdate()
+				}
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	min-height: 100vh;
+	background-color: $bgcolor1;
+	.line-bar{
+		margin: 0 60rpx;
+		margin-top: 30rpx;
+		.span{
+			width: 120rpx;
+			height: 8rpx;
+			border-radius: 4rpx 0rpx 0rpx 4rpx;
+			background-color: $bgcolor4;
+		}
+		.active{
+			background-color: $primary;
+		}
+	}
+	.title{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+		margin-top: 108rpx;
+	}
+	.tip{
+		color: $fontcolor3;
+		padding-left: 60rpx;
+		margin-top: 8rpx;
+	}
+	.select-title{
+		color: $fontcolor3;
+		margin: 0 60rpx;
+		margin-top: 56rpx;
+	}
+	.select{
+		background-color: $bgcolor3;
+		margin: 0 60rpx;
+		margin-top: 16rpx;
+		height: 112rpx;
+		border-radius: 16px;
+		padding: 0 36rpx;
+		.select-val{
+			color: $fontcolor1;
+		}
+		.select-icon{
+			width: 24rpx;
+			height: 24rpx;
+		}
+	}
+	
+	.img-box{
+		width: 320rpx;
+		margin-left: 60rpx;
+		
+		.img{
+			width: 320rpx;
+			height: 320rpx;
+		}
+		.img-text{
+			color: $fontcolor3;
+			text-align: center;
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+</style>

+ 767 - 0
pages/info/editCenter.vue

@@ -0,0 +1,767 @@
+<template>
+	<view class="container">
+		<image-cropper :src="tempFilePath" @confirm="confirm" @cancel="cancel"></image-cropper>
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`,'background-color':`rgba(21, 17, 38,${topNavAlpha})`}" v-if="!cover.isFullScreen">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-text font32 fw600">
+				{{pageName}}
+			</view>
+			<view class="nav-item"></view>
+		</view>
+		<uni-popup ref="popup" type="bottom" :is-mask-click="false">
+			<view class="popup" @touchmove.prevent>
+				<view class="p-title-box flex-between">
+					<view class="p-title font36 fw600">
+						上传头像
+					</view>
+					<image :src="`${assetsUrl}info-figure-close.png`" mode="aspectFill" class="p-close" @click="hideHeadPopup"></image>
+				</view>
+				<view class="p-title-tip font28 fw400">
+					上传清晰本人照片更容易交到好友哦
+				</view>
+				<view class="p-img-box flex-between">
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img1.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							五官清晰
+						</view>
+					</view>
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img2.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							半身照
+						</view>
+					</view>
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img3.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							风景和人
+						</view>
+					</view>
+				</view>
+				<view style="height: 60rpx;">
+					
+				</view>
+			</view>
+			
+		</uni-popup>
+		<scroll-view
+		scroll-y="true" 
+		:style="{'height': `${scrollHeight}px`,'padding-top':`${topNavHeight}px`}"
+		v-if="scrollHeight>0"
+		lower-threshold="200"
+		refresher-enabled="true"
+		:refresher-triggered="scrollTriggered"
+		:refresher-threshold="45" 
+		refresher-default-style="white"
+		refresher-background="#151126" 
+		@refresherrefresh="scrollRefresh" 
+		@refresherpulling="scrollPulling"
+		@refresherrestore="scrollRestore" 
+		@refresherabort="scrollAbort"
+		@scrolltolower="scrollToBottom"
+		class="scroll-view flex-start"
+		>
+		
+			<view class="head-info" @click="showHeadPopup">
+				<view class="head-box flex-center">
+					<view class="head-inner flex-center" v-if="!cropFilePath">
+						<image :src="`${assetsUrl}info-figure-add.png`" mode="aspectFill" class="add"></image>
+					</view>
+					<image :src="cropFilePath" mode="aspectFit" v-else></image>
+				</view>
+				<image :src="`${assetsUrl}info-figure-camera.png`" mode="aspectFill" class="camera"></image>
+			</view>
+			<view class="label-box flex-between">
+				<view class="left font28 fw400">
+					昵称
+				</view>
+				<view class="right flex-center" @click="randNick">
+					<view class="rand-btn flex-center">
+						<image :src="`${assetsUrl}info-figure-tou.png`" mode="aspectFill" class="rand"></image>
+						<view class="rand-text font20 fw400">
+							随机昵称
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="input-box flex-between">
+				<input type="text" maxlength="10" placeholder="请输入你的昵称" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="nickname">
+				<view class="input-num font22 fw400" v-if="nickname!==''">
+					{{nickname.length}}/10
+				</view>
+			</view>
+			<view class="label-box flex-between">
+				<view class="left font28 fw400">
+					个人简介
+				</view>
+				<view class="right flex-center" v-if="introduce!==''">
+					<view class="input-num font22 fw400">
+						{{introduce.length}}/200
+					</view>
+				</view>
+			</view>
+			<view class="textarea-box flex-between">
+				<textarea  maxlength="200" placeholder="可以填写个人介绍、人生经历、交友期望等" placeholder-style="color:#494667;font-size:28rpx;" class="textarea fw500 font32" v-model="introduce"></textarea>
+			</view>
+			<view class="select-title font28 fw400">
+				你的身高
+			</view>
+			<picker mode="selector" :range="heights" value="30" @change="heightsChange">
+				<view class="select flex-between">
+					<view class="select-val font28 fw400" :style="{'color':`${height==='选择身高'?'':'#ffffff'}`}">
+						{{height}}
+					</view>
+					<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+				</view>
+			</picker>
+			<view class="select-title font28 fw400">
+				你的体重
+			</view>
+			<picker mode="selector" :range="weights" value="50" @change="weightsChange">
+				<view class="select flex-between">
+					<view class="select-val font28 fw400" :style="{'color':`${weight==='选择体重'?'':'#ffffff'}`}">
+						{{weight}}
+					</view>
+					<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+				</view>
+			</picker>
+			<view class="select-title font28 fw400">
+				你的年龄
+			</view>
+			<picker mode="date" fields="day" value="1993-09-12" @change="agesChange">
+				<view class="select flex-between">
+					<view class="select-val font28 fw400" :style="{'color':`${age==='选择年龄'?'':'#ffffff'}`}">
+						{{age}}
+					</view>
+					<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+				</view>
+			</picker>
+			<view class="select-title font28 fw400">
+				你的职业
+			</view>
+			<picker mode="multiSelector" :range="jobs" @change="jobsChange" @columnchange="jobsColumnChange">
+				<view class="select flex-between">
+					<view class="select-val font28 fw400" :style="{'color':`${job==='选择职业'?'':'#ffffff'}`}">
+						{{job}}
+					</view>
+					<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+				</view>
+			</picker>
+			<view class="label-box flex-between">
+				<view class="left font28 fw400">
+					微信号
+				</view>
+			</view>
+			<view class="input-box flex-between">
+				<input type="text" maxlength="10" placeholder="请填写真实正确的微信号" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="wechat">
+			</view>
+			<view class="select-title font28 fw400">
+				设置查看权限
+			</view>
+			<picker mode="selector" :range="auths" range-key="value" @change="authsChange">
+				<view class="select flex-between">
+					<view class="select-val font28 fw400" :style="{'color':`${height===''?'':'#ffffff'}`}">
+						{{auth}}
+					</view>
+					<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+				</view>
+			</picker>
+			<view class="label-box flex-between">
+				<view class="left font28 fw400">
+					常驻城市
+				</view>
+				<view class="right flex-center" @click="editCity">
+					<image :src="`${assetsUrl}mine-edit.png`" mode="aspectFill" class="edit"></image>
+				</view>
+			</view>
+			<view class="tag-box flex-start">
+				<view class="tag font28" v-for="(item,index) in myCitys" :key="index">
+					{{item}}
+				</view>
+				<view class="tag font28" v-if="myCitys.length===0">
+					未设置
+				</view>
+			</view>
+			<view class="label-box flex-between">
+				<view class="left font28 fw400">
+					个性标签
+				</view>
+				<view class="right flex-center" @click="editLabels">
+					<image :src="`${assetsUrl}mine-edit.png`" mode="aspectFill" class="edit"></image>
+				</view>
+			</view>
+			<view class="tag-box flex-start">
+				<view class="tag font28" v-for="(item,index) in myLabels" :key="index">
+					{{item.name}}
+				</view>
+				<view class="tag font28" v-if="myLabels.length===0">
+					未设置
+				</view>
+			</view>
+			<view class="label-box flex-between">
+				<view class="left font28 fw400">
+					我喜欢的
+				</view>
+				<view class="right flex-center" @click="editWants">
+					<image :src="`${assetsUrl}mine-edit.png`" mode="aspectFill" class="edit"></image>
+				</view>
+			</view>
+			<view class="tag-box flex-start">
+				<view class="tag font28" v-for="(item,index) in myWants" :key="index">
+					{{item.name}}
+				</view>
+				<view class="tag font28" v-if="myWants.length===0">
+					未设置
+				</view>
+			</view>
+		</scroll-view>
+		<view class="btn font32 fw600" @click="sure">
+			{{btnText}}
+		</view>
+	</view>
+</template>
+
+<script>
+	import TabBar from '@/components/TabBar/TabBar.vue';
+	import ImageCropper from "@/components/invinbg-image-cropper/invinbg-image-cropper.vue";
+	import wxMap from '@/static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js';
+	import {getPolicy,computeSignature,getKey} from '@/util/oss.js';
+	import {encode} from '@/util/base64.js'
+	/**
+	 * 腾讯位置服务,手机账号:18996226740
+	 */
+	const wxMapSdk=new wxMap({key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII'})
+	export default {
+		components:{TabBar,ImageCropper},
+		data() {
+			return {
+				btnText:'完成',
+				pageName:'个人资料',
+				assetsUrl:this.$util.assetsUrl,
+				scrollHeight:0,
+				topNavHeight:0,
+				scrollRefreshing:false,
+				scrollTriggered:true,
+				tempFilePath: '',
+				cropFilePath: '',
+				nickname:'',
+				introduce:'',
+				heights:[],
+				height:'选择身高',
+				weights:[],
+				weight:'选择体重',
+				age:'选择年龄',
+				jobs:[],
+				careersData:[],
+				job:'选择职业',
+				auths:[
+					{key:'WxM2F5',value:'对VIP公开或使用颜豆、权益卡查看'},
+					{key:'WaitAuth',value:'经过我授权才能查看微信'},
+				],
+				auth:'经过我授权才能查看微信',
+				weiXinStatusEnum:'WaitAuth',
+				wechat:'',
+				myCitys:[],
+				myLabels:[],
+				myWants:[],
+				saveOptions:{
+						birthday: "",
+						career: "",
+						cities: [],
+						completeUser: {},
+						desc: "",
+						education: {},
+						height: 0,
+						icon: "",
+						myLabelIds: [],
+						nick: "",
+						wantedLabelIds: [],
+						weiXinStatus: {},
+						weight: 0,
+						wxId: ""
+				}
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			}
+		},
+		onLoad(options) {
+			this.computedScollviewHeight();
+			this.getMineData();
+		},
+		mounted() {
+			let arr=[];
+			for(let i=150;i<210;i++){
+				arr.push(`${i}cm`);
+			}
+			this.heights=arr;
+			arr=[];
+			for(let i=30;i<150;i++){
+				arr.push(`${i}kg`);
+			}
+			this.weights=arr;
+			this.getCareersConfig();
+			
+		},
+		onPageScroll(e) {
+			
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			showHeadPopup(){
+				this.btnText="从相册上传";
+				this.$refs.popup.open();
+			},
+			hideHeadPopup(){
+				this.btnText="完成";
+				this.$refs.popup.close();
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.select('#topnav').boundingClientRect(data => {
+					this.topNavHeight=data.height;
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			scrollRefresh(){
+				if (this.scrollRefreshing) 
+				{
+					return;
+				}
+				this.scrollRefreshing = true;
+				setTimeout(() => {
+					this.scrollTriggered = false;
+					this.scrollRefreshing = false;
+				}, 1000)
+				this.getMineDetail();
+			},
+			scrollPulling(e) {},
+			scrollRestore() {this.scrollTriggered = true;},
+			scrollAbort() {},
+			scrollToBottom(){
+
+			},
+			randNick(){
+				this.$api.login.randNick({}).then(res=>{
+					this.nickname=res.data.nick;
+				}).catch(err=>{})
+			},
+			confirm(e) {
+				this.tempFilePath = ''
+				this.cropFilePath = e.detail.tempFilePath;
+				const that=this;
+				const policyText=getPolicy();
+				const policy=encode(JSON.stringify(policyText));
+				const key=getKey(0,this.cropFilePath.split('.')[1]);
+				that.$api.public.aliossToken({}).then(resuslt=>{
+					let formData={
+						key:key,
+						policy:policy,
+						OSSAccessKeyId:resuslt.data.accessKeyId,
+						signature:computeSignature(resuslt.data.accessKeySecret,policy),
+						'x-oss-security-token':resuslt.data.securityToken,
+						success_action_status:'200'
+					}
+					uni.uploadFile({
+						url: 'https://zhenyanapp-gen.oss-cn-qingdao.aliyuncs.com',
+						filePath:this.cropFilePath,
+						name: 'file',
+						header:{
+							"Content-Type": "multipart/form-data"
+						},
+						formData: formData,
+						success: (data) => {
+						    if (data.statusCode === 200) {
+							  that.icon=`${that.$store.state.imageCdn}/${key}`;
+							  that.saveOptions.icon=`${that.$store.state.imageCdn}/${key}`;
+						    }
+						},
+						fail: err => {
+						    console.log(err);
+						  }
+					})
+				})
+				
+			},
+			cancel() {
+				console.log('canceled')
+			},
+			getCareersConfig(){
+				this.$api.public.config({tscsj:['Careers']}).then(res=>{
+					this.careersData=res.data.careers;
+					let arr=[],arr1=[];
+					for(let item in res.data.careers){
+						arr1.push(item)
+					}
+					arr.push(arr1);
+					arr.push(res.data.careers[arr1[0]]);
+					this.jobs=arr;
+				}).catch(err=>{
+					console.log(err)
+				})
+			},
+			getMineDetail(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.mineDetail({
+					getAlbum:true,
+					completeUser:user
+				}).then(res=>{
+					this.$store.commit('setUserInfo',res.data);
+					uni.setStorageSync('userInfo',JSON.stringify(res.data));//正式环境删除
+				})
+			},
+			heightsChange(e){
+				this.height=this.heights[e.detail.value];
+			},
+			weightsChange(e){
+				this.weight=this.weights[e.detail.value];
+			},
+			agesChange(e){
+				this.age=e.detail.value;
+			},
+			jobsChange(e){
+				let str=this.jobs[1][e.detail.value[1]];
+				this.job=str;
+			},
+			jobsColumnChange(e){
+				let leftValue=this.jobs[e.detail.column][e.detail.value];
+				if(e.detail.column===0){
+					this.jobs[1]=this.careersData[leftValue];
+					this.$forceUpdate()
+				}
+			},
+			authsChange(e){
+				this.auth=this.auths[e.detail.value].value;
+				this.weiXinStatusEnum=this.auths[e.detail.value].key;
+			},
+			editCity(){
+				uni.navigateTo({
+					url:'/pages/info/city?type=edit'
+				})
+			},
+			editLabels(){
+				uni.navigateTo({
+					url:'/pages/info/labels?type=labels'
+				})
+			},
+			editWants(){
+				uni.navigateTo({
+					url:'/pages/info/labels?type=wants'
+				})
+			},
+			getMineData(){
+				let userInfo=this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'));
+				console.log(userInfo)
+				this.saveOptions.icon=userInfo.icon
+				this.cropFilePath=userInfo.iconThumbnail;
+				this.nickname=userInfo.nick;
+				this.introduce=userInfo.desc;
+				this.height=userInfo.height;
+				this.weight=userInfo.weight;
+				this.age=userInfo.ageInfo.birthday;
+				this.job=userInfo.career;
+				this.wechat=userInfo.wxId
+				this.weiXinStatusEnum=userInfo.weixinStatus;
+				this.auths.forEach((item,index)=>{
+					if(item.key===this.weiXinStatusEnum){
+						this.auth=item.value;
+					}
+				});
+				this.myCitys=userInfo.cities||[];
+				this.myLabels=userInfo.myLabels||[];
+				this.myWants=userInfo.userWantedLabels||[];
+				
+			},
+			sure(){
+				if(this.btnText==='完成'){
+					let userInfo=this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'));
+					this.saveOptions.birthday=this.age;
+					this.saveOptions.education=userInfo.education;
+					this.saveOptions.career=this.job;
+					this.saveOptions.cities=this.myCitys;
+					this.saveOptions.completeUser=JSON.parse(uni.getStorageSync('user'));
+					this.saveOptions.desc=this.introduce;
+					this.saveOptions.weight=this.weight;
+					this.saveOptions.height=this.height;
+					this.saveOptions.nick=this.nickname;
+					this.saveOptions.weiXinStatus=this.weiXinStatusEnum;
+					this.saveOptions.wxId=this.wechat;
+					if(!this.saveOptions.icon){uni.showToast({mask:true,title:'请上传头像',icon:'none'});return;}
+					if(!this.saveOptions.nick){uni.showToast({mask:true,title:'请填写昵称',icon:'none'});return;}
+					if(!this.saveOptions.desc){uni.showToast({mask:true,title:'请填写个人简介',icon:'none'});return;}
+					uni.showLoading({
+						mask:true,
+						title:'更新中···'
+					})
+					this.$api.login.editPersonal(this.saveOptions).then(res=>{
+						
+						if(res.status==='Succ'){
+							let user=JSON.parse(uni.getStorageSync('user'));
+							
+							this.$api.login.editHead({completeUser:user,icon:this.saveOptions.icon}).then(res=>{
+								uni.hideLoading();
+								uni.showToast({
+									mask:true,
+									title:'更新成功',
+									icon:'success'
+								});
+							})
+							this.getMineDetail();
+						}
+					})
+					
+				}
+				else{
+					this.$refs.popup.close();
+					this.btnText="完成";
+					uni.chooseImage({
+						count: 1, //默认9
+						sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+						sourceType: ['album','camera'], //从相册选择
+						success: res=>{
+							this.tempFilePath = res.tempFiles[0].path;
+						}
+					});
+				}
+				
+				
+			}
+		}
+			
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		backdrop-filter: blur(10px);
+		.nav-item {
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+			
+		}
+		.nav-text{
+			flex: 1;
+			color: $fontcolor5;
+			height: 40rpx;
+			text-align: center;
+		}
+	}
+	.popup{
+		width: 100vw;
+		box-sizing: border-box;
+		padding: 56rpx 60rpx;
+		background-color: $bgcolor3;
+		.p-title-box{
+			.p-title{
+				color: $fontcolor5;
+			}
+			.p-close{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.p-title-tip{
+			color: $fontcolor3;
+			margin-top: 16rpx;
+		}
+		.p-img-box{
+			margin-top: 80rpx;
+			.p-img{
+				.img{
+					width: 200rpx;
+					height: 200rpx;
+				}
+				.p-text{
+					color: $fontcolor2;
+					text-align: center;
+				}
+			}
+		}
+	}
+	.scroll-view{
+		flex-wrap: wrap;
+		align-items: flex-start !important;
+		box-sizing: border-box;
+		.head-info{
+			position: relative;
+			width: 184rpx;
+			height: 184rpx;
+			margin: 0 auto;
+			.head-box{
+				width: 184rpx;
+				height: 184rpx;
+				border-radius: 92rpx;
+				margin: 0 auto;
+				margin-top: 88rpx;
+				overflow: hidden;
+				
+				.head-inner{
+					width: 184rpx;
+					height: 184rpx;
+					background: linear-gradient(133deg, rgba(250, 92, 116, 1), rgba(188, 85, 191, 1), rgba(138, 79, 255, 1));
+					.add{
+						width: 40rpx;
+						height: 40rpx;
+						background-color: $bgcolor1;
+						border-radius: 92rpx;
+						padding: 68rpx;
+					}
+				}
+			}
+			.camera{
+				position: absolute;
+				right: 0;
+				bottom: 0;
+				width: 56rpx;
+				height: 56rpx;
+			}
+		}
+		.label-box{
+			margin: 0 60rpx;
+			margin-top: 56rpx;
+			.left{
+				color: $fontcolor3;
+			}
+			.right{
+				.rand-btn{
+					border-radius: 24rpx;
+					border: 1rpx solid #7D7DA4;
+					width: 136rpx;
+					height: 40rpx;
+					.rand{
+						width: 24rpx;
+						height: 24rpx;
+					}
+					.rand-text{
+						color: $fontcolor2;
+						margin-left: 10rpx;
+					}
+				}
+				.edit{
+					width: 32rpx;
+					height: 32rpx;
+				}
+			}
+		}
+		.tag-box{
+			margin: 0 60rpx;
+			margin-top: 56rpx;
+			flex-wrap: wrap;
+			.tag{
+				height: 40rpx;
+				line-height: 40rpx;
+				color: $fontcolor3;
+				padding: 0 20rpx;
+				margin-right: 20rpx;
+				border-radius: 16rpx;
+				background-color: $bgcolor3;
+				margin-bottom: 20rpx;
+			}
+		}
+		.input-box{
+			
+			margin: 0rpx 60rpx;
+			margin-top: 16rpx;
+			background-color: $bgcolor3;
+			border-radius: 16rpx;
+			height: 112rpx;
+			padding: 0rpx 32rpx;
+			.input{
+				color: $fontcolor5;
+			}
+			
+		}
+		.textarea-box{
+			
+			border-radius: 16rpx;
+			margin: 0rpx 60rpx;
+			margin-top: 16rpx;
+			background-color: $bgcolor3;
+			padding: 0rpx 32rpx;
+			min-height: 200rpx;
+			.textarea{
+				color: $fontcolor5;
+				height: 136rpx;
+			}
+		}
+		.select-title{
+			color: $fontcolor3;
+			margin: 0 60rpx;
+			margin-top: 56rpx;
+		}
+		.select{
+			background-color: $bgcolor3;
+			margin: 0 60rpx;
+			margin-top: 16rpx;
+			height: 112rpx;
+			border-radius: 16px;
+			padding: 0 36rpx;
+			.select-val{
+				color: $fontcolor1;
+			}
+			.select-icon{
+				width: 24rpx;
+				height: 24rpx;
+			}
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+	.input-num{
+		color: $fontcolor2;
+	}
+}
+
+</style>

+ 396 - 0
pages/info/figure.vue

@@ -0,0 +1,396 @@
+<template>
+	<view class="container">
+		<image-cropper :src="tempFilePath" @confirm="confirm" @cancel="cancel"></image-cropper>
+		<TopBar></TopBar>
+		<uni-popup ref="popup" type="bottom" :is-mask-click="false">
+			<view class="popup" @touchmove.prevent>
+				<view class="p-title-box flex-between">
+					<view class="p-title font36 fw600">
+						上传头像
+					</view>
+					<image :src="`${assetsUrl}info-figure-close.png`" mode="aspectFill" class="p-close" @click="hideHeadPopup"></image>
+				</view>
+				<view class="p-title-tip font28 fw400">
+					上传清晰本人照片更容易交到好友哦
+				</view>
+				<view class="p-img-box flex-between">
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img1.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							五官清晰
+						</view>
+					</view>
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img2.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							半身照
+						</view>
+					</view>
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img3.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							风景和人
+						</view>
+					</view>
+				</view>
+				<view style="height: 60rpx;">
+					
+				</view>
+			</view>
+			
+		</uni-popup>
+		<view class="line-bar flex-between">
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span"></view>
+			<view class="span"></view>
+			<view class="span"></view>
+		</view>
+		<view class="title font44 fw600">
+			设置你的社交形象
+		</view>
+		<view class="tip font28 fw400">
+			展示你的真实一面,给大家留一个好印象
+		</view>
+		<view class="head-info" @click="showHeadPopup">
+			<view class="head-box flex-center">
+				<view class="head-inner flex-center" v-if="cropFilePath===''">
+					<image :src="`${assetsUrl}info-figure-add.png`" mode="aspectFill" class="add"></image>
+				</view>
+				<image :src="cropFilePath" mode="aspectFit" v-else></image>
+			</view>
+			<image :src="`${assetsUrl}info-figure-camera.png`" mode="aspectFill" class="camera"></image>
+		</view>
+		<view class="label-box flex-between">
+			<view class="left font28 fw400">
+				昵称
+			</view>
+			<view class="right flex-center" @click="randNick">
+				<view class="rand-btn flex-center">
+					<image :src="`${assetsUrl}info-figure-tou.png`" mode="aspectFill" class="rand"></image>
+					<view class="rand-text font20 fw400">
+						随机昵称
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="input-box flex-between">
+			<input type="text" maxlength="10" placeholder="请输入你的昵称" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="nickname">
+			<view class="input-num font22 fw400">
+				{{nickname.length}}/10
+			</view>
+		</view>
+		<view class="label-box flex-between">
+			<view class="left font28 fw400">
+				个人简介
+			</view>
+			<view class="right flex-center">
+				<view class="input-num font22 fw400">
+					{{introduce.length}}/200
+				</view>
+			</view>
+		</view>
+		
+		<view class="textarea-box flex-between">
+			<textarea  maxlength="200" placeholder="可以填写个人介绍、人生经历、交友期望等" placeholder-style="color:#494667;font-size:28rpx;" class="textarea fw500 font32" v-model="introduce"></textarea>
+		</view>
+		<view class="label-box flex-between">
+			<view class="left font28 fw400">
+				邀请码<span class="font22">(可不填)</span>
+			</view>
+		</view>
+		<view class="input-box flex-between">
+			<input type="text" maxlength="6" placeholder="请输入你的邀请码" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="code" >
+		</view>
+		<view style="height: 200rpx;"></view>
+		<view class="btn font32 fw600" @click="sure">
+			{{btnText}}
+		</view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	import ImageCropper from "@/components/invinbg-image-cropper/invinbg-image-cropper.vue";
+	import {getPolicy,computeSignature,getKey} from '@/util/oss.js';
+	import {encode} from '@/util/base64.js'
+	export default {
+		components:{TopBar,ImageCropper},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				nickname:'',
+				introduce:'',
+				code:'',
+				icon:'',
+				btnText:'下一步',
+				tempFilePath: '',
+				cropFilePath: '',
+				
+			};
+		},
+		methods:{
+			sure(){
+				if(this.btnText==='从相册上传'){
+					
+					uni.chooseImage({
+						count: 1, //默认9
+						sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+						sourceType: ['album','camera'], //从相册选择
+						success: res=>{
+							this.tempFilePath = res.tempFiles[0].path;
+						}
+					});
+				}
+				else{
+					if(this.icon===''){uni.showToast({title:'请上传头像',icon:'none'});return;}
+					if(this.nickname===''){uni.showToast({title:'请填写昵称',icon:'none'});return;}
+					if(this.introduce===''){uni.showToast({title:'请填写个人简介',icon:'none'});return;}
+					uni.showLoading({mask:true});
+					let user=JSON.parse(uni.getStorageSync('user'));
+					user.nick=this.nickname;
+					user.desc=this.introduce;
+					user.shareCode=this.code;
+					user.icon=this.icon;
+					let saveOption={
+						"completeUser": user,
+						"weiXinStatusEnum": 'NoWeiXin',
+					}
+					uni.showLoading({
+						title:'保存中',
+						mask:true
+					})
+					this.$api.login.saveFigure(saveOption).then(res=>{
+						uni.setStorageSync('regStep','SocialData');
+						uni.setStorageSync('LL_Ukn',res.data.ukn);
+						uni.setStorageSync('user',JSON.stringify(user));
+						this.$api.login.editHead({completeUser:user,icon:this.icon}).then(res=>{
+							uni.hideLoading();
+						})
+						uni.reLaunch({
+							url:`/pages/info/datum`
+						})
+					}).catch(err=>{})
+				}
+			},
+			randNick(){
+				this.$api.login.randNick({}).then(res=>{
+					this.nickname=res.data.nick;
+				}).catch(err=>{})
+			},
+			confirm(e) {
+				this.tempFilePath = ''
+				this.cropFilePath = e.detail.tempFilePath;
+				this.btnText="下一步";
+				this.$refs.popup.close();
+				const that=this;
+				const policyText=getPolicy();
+				const policy=encode(JSON.stringify(policyText));
+				const key=getKey(0,this.cropFilePath.split('.')[1]);
+				that.$api.public.aliossToken({}).then(resuslt=>{
+					let formData={
+						key:key,
+						policy:policy,
+						OSSAccessKeyId:resuslt.data.accessKeyId,
+						signature:computeSignature(resuslt.data.accessKeySecret,policy),
+						'x-oss-security-token':resuslt.data.securityToken,
+						success_action_status:'200'
+					}
+					uni.uploadFile({
+						url: 'https://zhenyanapp-gen.oss-cn-qingdao.aliyuncs.com',
+						filePath:this.cropFilePath,
+						name: 'file',
+						header:{
+							"Content-Type": "multipart/form-data"
+						},
+						formData: formData,
+						success: (data) => {
+						    if (data.statusCode === 200) {
+							  that.icon=`${that.$store.state.imageCdn}/${key}`;
+						    }
+						},
+						fail: err => {
+						    console.log(err);
+						  }
+					})
+				})
+				
+			},
+			cancel() {
+				console.log('canceled')
+			},
+			showHeadPopup(){
+				this.btnText="从相册上传";
+				this.$refs.popup.open();
+			},
+			hideHeadPopup(){
+				this.btnText="下一步";
+				this.$refs.popup.close();
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	min-height: 100vh;
+	background-color: $bgcolor1;
+	.popup{
+		width: 100vw;
+		box-sizing: border-box;
+		padding: 56rpx 60rpx;
+		background-color: $bgcolor3;
+		.p-title-box{
+			.p-title{
+				color: $fontcolor5;
+			}
+			.p-close{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.p-title-tip{
+			color: $fontcolor3;
+			margin-top: 16rpx;
+		}
+		.p-img-box{
+			margin-top: 80rpx;
+			.p-img{
+				.img{
+					width: 200rpx;
+					height: 200rpx;
+				}
+				.p-text{
+					color: $fontcolor2;
+					text-align: center;
+				}
+			}
+		}
+	}
+	.line-bar{
+		margin: 0 60rpx;
+		margin-top: 30rpx;
+		.span{
+			width: 120rpx;
+			height: 8rpx;
+			border-radius: 4rpx 0rpx 0rpx 4rpx;
+			background-color: $bgcolor4;
+		}
+		.active{
+			background-color: $primary;
+		}
+	}
+	.title{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+		margin-top: 108rpx;
+	}
+	.tip{
+		color: $fontcolor3;
+		padding-left: 60rpx;
+		margin-top: 8rpx;
+	}
+	.head-info{
+		position: relative;
+		width: 184rpx;
+		height: 184rpx;
+		margin: 0 auto;
+		.head-box{
+			width: 184rpx;
+			height: 184rpx;
+			border-radius: 92rpx;
+			margin: 0 auto;
+			margin-top: 88rpx;
+			overflow: hidden;
+			
+			.head-inner{
+				width: 184rpx;
+				height: 184rpx;
+				background: linear-gradient(133deg, rgba(250, 92, 116, 1), rgba(188, 85, 191, 1), rgba(138, 79, 255, 1));
+				.add{
+					width: 40rpx;
+					height: 40rpx;
+					background-color: $bgcolor1;
+					border-radius: 92rpx;
+					padding: 68rpx;
+				}
+			}
+		}
+		.camera{
+			position: absolute;
+			right: 0;
+			bottom: 0;
+			width: 56rpx;
+			height: 56rpx;
+		}
+	}
+	.label-box{
+		margin: 0 60rpx;
+		margin-top: 56rpx;
+		.left{
+			color: $fontcolor3;
+		}
+		.right{
+			.rand-btn{
+				border-radius: 24rpx;
+				border: 1rpx solid #7D7DA4;
+				width: 136rpx;
+				height: 40rpx;
+				.rand{
+					width: 24rpx;
+					height: 24rpx;
+				}
+				.rand-text{
+					color: $fontcolor2;
+					margin-left: 10rpx;
+				}
+			}
+		}
+	}
+	.input-box{
+		
+		margin: 0rpx 60rpx;
+		margin-top: 16rpx;
+		background-color: $bgcolor3;
+		border-radius: 16rpx;
+		height: 112rpx;
+		padding: 0rpx 32rpx;
+		.input{
+			color: $fontcolor5;
+		}
+		
+	}
+	.textarea-box{
+		
+		border-radius: 16rpx;
+		margin: 0rpx 60rpx;
+		margin-top: 16rpx;
+		background-color: $bgcolor3;
+		padding: 0rpx 32rpx;
+		min-height: 200rpx;
+		.textarea{
+			color: $fontcolor5;
+			height: 136rpx;
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+.input-num{
+	color: $fontcolor2;
+}
+</style>

+ 241 - 0
pages/info/labels.vue

@@ -0,0 +1,241 @@
+<template>
+	<view class="container">
+		<TopBar id="topbar" :icon="'back'"></TopBar>
+		<view class="title font44 fw600" id='title'>
+			选择你{{type==='wants'?'喜欢':'个性'}}标签
+		</view>
+		<view class="tip font28 fw400" id="tip">
+			{{type==='wants'?'添加你喜欢的特质,让推荐更加精准':'增加自己的的个性标签,让大家更了解你'}}
+		</view>
+		<view class="choose-list flex-start" id="chooselist">
+			<view class="choose-tag flex-center" v-for="(item,index) in chooseLabels" :key="index">
+				<text class="tag-text font28 fw600">{{item.name}}</text>
+				<image :src="`${assetsUrl}info-figure-close.png`" mode="aspectFill" class="tag-img" @click="deleteTag(index)"></image>
+			</view>
+		</view>
+		<scroll-view scroll-y="true" class="citys" :style="{'height':`${scollHeight}px`}">
+			<view class="city-tags" v-for="(item,index) in labelsList" :key="index">
+				<view class="c-label font28 fw700">
+					{{item.name}}
+				</view>
+				<view class="c-tags flex-start">
+					<view class="c-tag font28 fw400" :class="sitem.choose?'tag-choose':''" v-for="(sitem,sindex) in item.list" :key="sindex" @click="chooseTag(index,sindex)">
+						{{sitem.name}}
+					</view>
+				</view>
+				
+			</view>
+			
+		</scroll-view>
+		<cover-view class="btn font32 fw600" @click="sure">
+			完成
+		</cover-view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				chooseLabels:[],
+				labelsList:[],
+				scollHeight:0,
+				type:''
+			};
+		},
+		onLoad(option) {
+			let pages = getCurrentPages();
+			let prevPage = pages[pages.length - 2];
+			console.log(option)
+			if(option.type&&option.type==='wants'){
+				this.type=option.type;
+				this.chooseLabels=prevPage.$vm.myWants;
+			}
+			else if(option.type&&option.type==='labels'){
+				this.type=option.type;
+				this.chooseLabels=prevPage.$vm.myLabels;
+			}
+			this.computedScollviewHeight();
+			
+			
+			this.getLabels();
+		},
+		methods:{
+			computedScollviewHeight(){
+				let query = uni.createSelectorQuery();
+				let heightLeaf=0;
+				let str='#topbar,#title,#tip,#chooselist'
+				query.selectAll(str).boundingClientRect(data => {
+					data.forEach(item=>{
+						heightLeaf+=item.height;
+					})
+				}).exec(()=>{
+					let sysInfo=uni.getSystemInfoSync();
+					this.scollHeight=sysInfo.windowHeight-heightLeaf-145;
+					console.log(this.scollHeight)
+				});
+				
+			},
+			sure(){
+				let pages = getCurrentPages();
+				let prevPage = pages[pages.length - 2];
+				let arr=[],ids=[];
+				for(let i=0;i<this.chooseLabels.length;i++){
+					arr.push(this.chooseLabels[i]);
+					ids.push(`${this.chooseLabels[i].id}`);
+				}
+				console.log(ids)
+				if(this.type==="wants"){
+					prevPage.$vm.myWants=arr;
+					prevPage.$vm.saveOptions.wantedLabelIds=ids;
+				}
+				else{
+					prevPage.$vm.myLabels=arr;
+					prevPage.$vm.saveOptions.myLabelIds=ids;
+				}
+				
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			deleteTag(index){
+				this.chooseLabels.splice(index,1);
+				setTimeout(()=>{
+					this.computedScollviewHeight();
+				},10)
+				
+			},
+			chooseTag(index,sindex){
+				this.labelsList[index].list[sindex].choose=!this.labelsList[index].list[sindex].choose;
+				if(this.labelsList[index].list[sindex].choose&&!this.chooseLabels.includes({name:this.labelsList[index].list[sindex].name,id:this.labelsList[index].list[sindex].id})){
+					this.chooseLabels.push({name:this.labelsList[index].list[sindex].name,id:this.labelsList[index].list[sindex].id})
+				}
+				setTimeout(()=>{
+					this.computedScollviewHeight();
+				},10)
+				setTimeout(()=>{
+					this.labelsList[index].list[sindex].choose=false;
+				},500)
+			},
+			getLabels(){
+				this.$api.login.labelsConfig({
+					isSpLabel:this.type==='labels'?true:false,
+					pageNum:1,
+					pageSize:20,
+					//type:['Looks','Character','Trait','Hobby']
+				}).then(res=>{
+					this.labelsList=res.data;
+				}).catch(err=>{
+					console.log(err)
+				})
+			},
+			bindClick(e){
+				console.log(e)
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	min-height: 100vh;
+	background-color: $bgcolor1;
+	.line-bar{
+		margin: 0 60rpx;
+		margin-top: 30rpx;
+		.span{
+			width: 120rpx;
+			height: 8rpx;
+			border-radius: 4rpx 0rpx 0rpx 4rpx;
+			background-color: $bgcolor4;
+		}
+		.active{
+			background-color: $primary;
+		}
+	}
+	.title{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+		margin-top: 108rpx;
+	}
+	.tip{
+		color: $fontcolor3;
+		padding-left: 60rpx;
+		margin-top: 8rpx;
+	}
+	.choose-list{
+		padding: 72rpx 60rpx 8rpx 60rpx;
+		flex-wrap: wrap;
+		.choose-tag{
+			background-color: $bgcolor4;
+			border-radius: 38rpx;
+			padding: 0 20rpx;
+			height: 72rpx;
+			margin-right: 32rpx;
+			margin-bottom: 32rpx;
+			.tag-text{
+				color: $fontcolor4;
+				text-align: center;
+				line-height: 72rpx;
+			}
+			.tag-img{
+				width: 24rpx;
+				height: 24rpx;
+				padding-left: 16rpx;
+			}
+		}
+	}
+	.citys{
+		height: 700rpx;
+		padding: 0 60rpx;
+		box-sizing: border-box;
+		.city-tags{
+			.c-label{
+				color: $fontcolor3;
+				margin-bottom: 24rpx;
+				margin-top: 16rpx;
+			}
+			.c-tags{
+				flex-wrap: wrap;
+				.c-tag{
+					background-color: $bgcolor3;
+					color: $fontcolor3;
+					padding: 0 20rpx;
+					height: 72rpx;
+					line-height: 72rpx;
+					text-align: center;
+					border-radius: 38rpx;
+					margin-right: 24rpx;
+					margin-bottom: 24rpx;
+					transition: all .3s;
+				}
+			}
+			
+			.tag-choose{
+				background: $primary !important;
+				color: $fontcolor5 !important;
+			}
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+</style>

+ 138 - 0
pages/info/sex.vue

@@ -0,0 +1,138 @@
+<template>
+	<view class="container">
+		<TopBar></TopBar>
+		<view class="line-bar flex-between">
+			<view class="span active"></view>
+			<view class="span"></view>
+			<view class="span"></view>
+			<view class="span"></view>
+			<view class="span"></view>
+		</view>
+		<view class="title font44 fw600">
+			请选择性别
+		</view>
+		<view class="tip font28 fw400">
+			选择好性别,才好遇见你的那个TA
+		</view>
+		<view class="img-box" style="margin-top: 104rpx;" @click="sex='Famale'">
+			<image :src="`${assetsUrl}info-female-${sex==='Famale'?'on':'off'}.png`" mode="aspectFill" class="img"></image>
+			<view class="img-text font28 fw400" :style="{'color':`${sex==='Famale'?'#ffffff':''}`}">
+				我是女生
+			</view>
+		</view>
+		<view class="img-box" style="margin-left: 368rpx;" @click="sex='Male'">
+			<image :src="`${assetsUrl}info-male-${sex==='Male'?'on':'off'}.png`" mode="aspectFill" class="img"></image>
+			<view class="img-text font28 fw400" :style="{'color':`${sex==='Male'?'#ffffff':''}`}">
+				我是男生
+			</view>
+		</view>
+		<view style="height: 200rpx;"></view>
+		<cover-view class="btn font32 fw600" @click="sure">
+			确认选择
+		</cover-view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				sex:'Famale',
+			};
+		},
+		methods:{
+			sure(){
+				uni.showModal({
+					title:'确认性别',
+					content:`性别选择后无法修改,当前选择为${this.sex==='Famale'?'女性':'男性'},请确认!`,
+					confirmText:'确认',
+					confirmColor:'#6C52F4',
+					success:(res)=>{
+						if(res.confirm){
+							uni.showLoading({
+								title:'保存中',
+								mask:true
+							})
+							let user=JSON.parse(uni.getStorageSync('user'));
+							user.sex=this.sex;
+							this.$api.login.saveSex({
+								"completeUser": user,
+								"weiXinStatusEnum": 'NoWeiXin',
+							}).then(res=>{
+								uni.setStorageSync('regStep','SocialImage');
+								uni.setStorageSync('LL_Ukn',res.data.ukn);
+								uni.setStorageSync('user',JSON.stringify(user));
+								uni.hideLoading();
+								uni.reLaunch({
+									url:`/pages/info/figure`
+								})
+							}).catch(err=>{})
+						}
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	min-height: 100vh;
+	background-color: $bgcolor1;
+	.line-bar{
+		margin: 0 60rpx;
+		margin-top: 30rpx;
+		.span{
+			width: 120rpx;
+			height: 8rpx;
+			border-radius: 4rpx 0rpx 0rpx 4rpx;
+			background-color: $bgcolor4;
+		}
+		.active{
+			background-color: $primary;
+		}
+	}
+	.title{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+		margin-top: 108rpx;
+	}
+	.tip{
+		color: $fontcolor3;
+		padding-left: 60rpx;
+		margin-top: 8rpx;
+	}
+	.img-box{
+		width: 320rpx;
+		margin-left: 60rpx;
+		
+		.img{
+			width: 320rpx;
+			height: 320rpx;
+		}
+		.img-text{
+			color: $fontcolor3;
+			text-align: center;
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+</style>

+ 215 - 0
pages/info/wechat.vue

@@ -0,0 +1,215 @@
+<template>
+	<view class="container">
+		<TopBar></TopBar>
+		<view class="line-bar flex-between">
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span active"></view>
+			<view class="span active"></view>
+		</view>
+		<view class="title font44 fw600">
+			设置你的微信号
+		</view>
+		<view class="tip font28 fw400">
+			如果你不在线,他可以添加你的微信
+		</view>
+		<view class="label-box flex-between">
+			<view class="left font28 fw400">
+				微信号
+			</view>
+		</view>
+		<view class="input-box flex-between">
+			<input type="text" maxlength="10" placeholder="请填写真实正确的微信号" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="wechat">
+		</view>
+		<view class="select-title font28 fw400">
+			设置查看权限
+		</view>
+		<picker mode="selector" :range="auths" range-key="value" @change="authsChange">
+			<view class="select flex-between">
+				<view class="select-val font28 fw400" :style="{'color':`${height===''?'':'#ffffff'}`}">
+					{{auth}}
+				</view>
+				<image :src="`${assetsUrl}info-figure-select.png`" mode="aspectFill" class="select-icon"></image>
+			</view>
+		</picker>
+		<cover-view class="btn font32 fw600" @click="sure">
+			下一步
+		</cover-view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	import ImageCropper from "@/components/invinbg-image-cropper/invinbg-image-cropper.vue";
+
+	export default {
+		components:{TopBar,ImageCropper},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				auths:[],
+				auth:'',
+				wechat:'',
+				saveOption:{
+					completeUser: {},
+					weiXinStatusEnum: 'WxM2F5'
+				}
+			};
+		},
+		mounted() {
+			let user=JSON.parse(uni.getStorageSync('user'));
+			if(user.sex==='Famale'){
+				this.auths=[
+					{key:'WxM2F5',value:'对VIP公开或使用颜豆、权益卡查看'},
+					{key:'WaitAuth',value:'经过我授权才能查看微信'},
+				]
+				this.auth='对VIP公开或使用颜豆、权益卡查看';
+			}
+			else if(user.sex==='Male'){
+				this.auths=[
+					{key:'WxF2M7',value:'对完成真人认证的女士公开'},
+					{key:'WaitAuth',value:'经过我授权才能查看微信'},
+				]
+				this.auth='对完成真人认证的女士公开';
+			}
+		},
+		methods:{
+			sure(){
+				if(this.wechat===''){uni.showToast({title:'请填写微信号',icon:'none'});return;}
+				let user=JSON.parse(uni.getStorageSync('user'));
+				user.wxId=this.wechat;
+				this.saveOption.completeUser=user;
+				uni.showLoading({
+					title:'保存中',
+					mask:true
+				})
+				this.$api.login.saveWechat(this.saveOption).then(res=>{
+					uni.hideLoading();
+					if(res.data.succ){
+						uni.setStorageSync('regStep','Index');
+						uni.setStorageSync('user',JSON.stringify(user));
+						uni.showToast({
+							icon:'success',
+							title:'保存成功'
+						});
+						this.getMineDetail();
+						uni.switchTab({
+							url:'/pages/friends/friends'
+						})
+					}
+				})
+			},
+			authsChange(e){
+				this.auth=this.auths[e.detail.value].value;
+				this.saveOption.weiXinStatusEnum=this.auths[e.detail.value].key;
+			},
+			confirm(e) {
+				this.tempFilePath = ''
+				this.cropFilePath = e.detail.tempFilePath;
+				this.btnText="下一步";
+				this.$refs.popup.close();
+			},
+			getMineDetail(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.mineDetail({
+					getAlbum:true,
+					completeUser:user
+				}).then(res=>{
+					this.$store.commit('setUserInfo',res.data);
+					uni.setStorageSync('userInfo',JSON.stringify(res.data));//正式环境删除
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	min-height: 100vh;
+	background-color: $bgcolor1;
+	.line-bar{
+		margin: 0 60rpx;
+		margin-top: 30rpx;
+		.span{
+			width: 120rpx;
+			height: 8rpx;
+			border-radius: 4rpx 0rpx 0rpx 4rpx;
+			background-color: $bgcolor4;
+		}
+		.active{
+			background-color: $primary;
+		}
+	}
+	.title{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+		margin-top: 108rpx;
+	}
+	.tip{
+		color: $fontcolor3;
+		padding-left: 60rpx;
+		margin-top: 8rpx;
+	}
+	
+	.label-box{
+		margin: 0 60rpx;
+		margin-top: 56rpx;
+		.left{
+			color: $fontcolor3;
+		}
+	}
+	.input-box{
+		
+		margin: 0rpx 60rpx;
+		margin-top: 16rpx;
+		background-color: $bgcolor3;
+		border-radius: 16rpx;
+		height: 112rpx;
+		padding: 0rpx 32rpx;
+		.input{
+			color: $fontcolor5;
+		}
+		
+	}
+	.select-title{
+		color: $fontcolor3;
+		margin: 0 60rpx;
+		margin-top: 56rpx;
+	}
+	.select{
+		background-color: $bgcolor3;
+		margin: 0 60rpx;
+		margin-top: 16rpx;
+		height: 112rpx;
+		border-radius: 16px;
+		padding: 0 36rpx;
+		.select-val{
+			color: $fontcolor1;
+		}
+		.select-icon{
+			width: 24rpx;
+			height: 24rpx;
+		}
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+.input-num{
+	color: $fontcolor2;
+}
+</style>

+ 228 - 0
pages/login/login.vue

@@ -0,0 +1,228 @@
+<template>
+	<view class="container">
+		<video :src="`${assetsUrl}login_bg${parseInt(Math.random()*6)+1}.mp4`" class="l-video" autoplay loop :controls="false" muted object-fit="cover" :enable-progress-gesture="false">
+			<image class="lv-logo" :src="`${assetsUrl}login-sugar.png`"></image>
+			<view class="lv-box flex-center">
+				<image class="lv-wechat" :src="`${assetsUrl}login-ph.png`" @click="toPhoneLogin"></image>
+				<button class="btn" open-type="getPhoneNumber" @getphonenumber="bindGetPhoneNumber">
+					<image class="lv-phone" :src="`${assetsUrl}login-we.png`" ></image>
+				</button>
+				
+			</view>
+			<view class="lv-pro font22">
+				登录即代表您已阅读并同意<text @click="toUserService">《用户服务协议》</text> 和 <text @click="toPrivate">《隐私政策》</text>
+			</view>
+		</video>
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl
+			}
+		},
+		mounted() {
+		},
+		onShareAppMessage(){
+			return {
+				title: '糖果公园',
+				path: `/pages/login/login?share=${this.userInfo.inviteCode}`,
+			}
+		},
+		methods: {
+			toPhoneLogin(){
+				uni.navigateTo({
+					url:'/pages/login/loginByPhone'
+				})
+			},
+			
+			bindGetPhoneNumber(e){
+				console.log(e.detail);
+				uni.showLoading({
+					mask:true,
+					title:'正在登录'
+				})
+				if(e.detail.errMsg==='getPhoneNumber:ok'){
+					let appConfig=uni.getAccountInfoSync();
+					let deviceConfig=uni.getSystemInfoSync();
+					this.$api.login.wxLogin({
+						scene: "WxMPLogin",
+						miniProgramEncryUserInfoParam:{
+						    encryptedData:e.detail.encryptedData,
+							iv:e.detail.iv,
+							code:e.detail.code,
+							deviceCode:deviceConfig.deviceId,
+							pkgCate:"JyPark",
+							platform:"WechatMP",
+							appVersion:appConfig.miniProgram.version||'1.0.0',
+						},
+						wxLoginInfo:{
+							openId:uni.getStorageSync('openId')||null,
+							unionId:uni.getStorageSync('unionId')||null
+						}
+					}).then(res=>{
+						if(res.data.frozen){
+							uni.showToast({
+								icon:"none",
+								title:res.data.msg
+							});
+						}
+						else if(res.data.succ){
+							uni.setStorageSync('LL_Ukn',res.data.userToken.ukn);
+							uni.setStorageSync('token',res.data.userToken.token);
+							uni.setStorageSync('userInfo',JSON.stringify(res.data));
+							uni.setStorageSync('user',JSON.stringify(res.data.userToken.user));
+							this.$api.public.aliossCdn({}).then(cdnRes=>{
+								this.$store.commit('setImageCdn',cdnRes.data.pictureCdn);
+								this.$store.commit('setVideoCdn',cdnRes.data.videoCdn);
+							})
+							if(res.data.isNew){
+								uni.navigateTo({
+									url:'/pages/info/sex'
+								})
+								return;
+							}
+							this.getMineDetail(res.data.regStepNew);
+							
+							
+						}
+						else{
+							uni.showToast({
+								title:res.data.msg,icon:'none'
+							})
+						}
+						uni.hideLoading();
+					}).catch(err=>{
+						uni.hideLoading();
+						console.log(err);
+					})
+				}else{
+					uni.hideLoading();
+					uni.showToast({
+						icon:'none',
+						mask:true,
+						title:'授权拒绝,请重试'
+					})
+				}
+			},
+			getMineDetail(regStepNew){
+				let step=uni.getStorageSync('regStep')||regStepNew;
+				switch(step){
+					case 'Sex':
+						uni.navigateTo({
+							url:'/pages/info/sex'
+						})
+					break;
+					case 'SocialImage':
+						uni.navigateTo({
+							url:'/pages/info/figure'
+						})
+					break;
+					case 'SocialData':
+						uni.navigateTo({
+							url:'/pages/info/datum'
+						})
+					break;
+					case 'CityStay':
+						uni.navigateTo({
+							url:'/pages/info/city'
+						})
+					break;
+					case 'WxInfo':
+						uni.navigateTo({
+							url:'/pages/info/wechat'
+						})
+					break;
+					case 'Basic':
+					case 'Index':
+						let user=JSON.parse(uni.getStorageSync('user'));
+						this.$api.public.mineDetail({
+							getAlbum:true,
+							completeUser:user
+						}).then(res=>{
+							this.$store.commit('setUserInfo',res.data);
+							uni.setStorageSync('userInfo',JSON.stringify(res.data));//正式环境删除
+							uni.setStorageSync('autoLogin','true');//正式环境删除
+							
+						})
+						uni.switchTab({
+							url:'/pages/friends/friends'
+						})
+					break;
+				}
+				
+			},
+			toUserService(){
+				uni.navigateTo({
+					url:`/pages/webview/webview?url=${this.$util.protocal.userAgreement}`
+				})
+			},
+			toPrivate(){
+				uni.navigateTo({
+					url:`/pages/webview/webview?url=${this.$util.protocal.privacy}`
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	
+	.l-video{
+		width: 100vw;
+		height: 100vh;
+		position: relative;
+		.lv-logo{
+			position: absolute;
+			z-index: 10;
+			top: 256rpx;
+			left: 58rpx;
+			width: 372rpx;
+			height: 120rpx;
+		}
+		
+		.lv-box{
+			position: absolute;
+			bottom: 232rpx;
+			width: 100%;
+			.lv-wechat{
+				width: 96rpx;
+				height: 96rpx;
+			}
+			.btn{
+				width: 486rpx;
+				height: 96rpx;
+				position: relative;
+				margin-left: 50rpx;
+				margin: 0;
+				padding: 0;
+				margin-left: 30rpx;
+					.lv-phone{
+						width: 486rpx;
+						height: 96rpx;
+					}
+				
+			}
+			
+		}
+		.lv-pro{
+			color: #ffffff;
+			position: absolute;
+			z-index: 10;
+			bottom: 160rpx;
+			height: 25rpx;
+			line-height: 25rpx;
+			width: 100%;
+			text-align: center;
+		}
+		
+	}
+	
+}
+</style>

+ 211 - 0
pages/login/loginByCode.vue

@@ -0,0 +1,211 @@
+<template>
+	<view class="container">
+		<TopBar :icon="topbarIcon" :title="topbarTitle"></TopBar>
+		<view class="tip font44 fw600" style="margin-top: 100rpx;">
+			验证码登录
+		</view>
+		<view class="phone-tip font28 fw400" style="margin-top: 24rpx;">
+			短信验证码已发送至:
+		</view>
+		<view class="phone-tip font28 fw400">
+			{{phone}}
+		</view>
+		<view class="input-box flex-between">
+			<input type="number" maxlength="6" placeholder="请输入验证码" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="code" @input="inputChange" focus="true">
+			<image src="../../static/input-clear.png" mode="aspectFill" class="clear" v-show="phone!==''" @click="clear"></image>
+		</view>
+		<view class="btn font32 fw400" :class="timer===null?'primarybg':''" @click="reGetCode">
+			{{btnStr}}
+		</view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				topbarIcon:'back',
+				topbarTitle:'',
+				phone:'',
+				code:'',
+				btnStr:'',
+				time:0,
+				timer:'',
+				verCodeOption:{
+					scene: "VerCodeLogin",
+					verCodeLogin:{
+						phone: "",
+						verCode: ""
+					}
+				},
+			};
+		},
+		onLoad(option) {
+			console.log(option)
+			this.phone=option.phone;
+			this.reGetCode()
+		},
+		methods:{
+			clear(){
+				this.code='';
+			},
+			inputChange(e){
+				if(e.detail.value.length===6){
+					this.verCodeOption.verCodeLogin.phone=this.phone;
+					this.verCodeOption.verCodeLogin.verCode=this.code;
+					this.$api.login.login(this.verCodeOption).then(res=>{
+						if(res.data.frozen){
+							uni.showToast({
+								icon:"none",
+								title:res.data.msg
+							});
+						}
+						else if(res.data.succ){
+							uni.setStorageSync('LL_Ukn',res.data.userToken.ukn);
+							uni.setStorageSync('token',res.data.userToken.token);
+							uni.setStorageSync('userInfo',JSON.stringify(res.data));
+							uni.setStorageSync('user',JSON.stringify(res.data.userToken.user));
+							this.$api.public.aliossCdn({}).then(cdnRes=>{
+								this.$store.commit('setImageCdn',cdnRes.data.pictureCdn);
+								this.$store.commit('setVideoCdn',cdnRes.data.videoCdn);
+							})
+							if(res.data.isNew){
+								uni.navigateTo({
+									url:'/pages/info/sex'
+								})
+								return;
+							}
+							this.getMineDetail(res.data.regStepNew);
+							
+							
+						}
+						else{
+							uni.showToast({
+								title:res.data.msg,icon:'none'
+							})
+						}
+					})
+				}
+			},
+			reGetCode(){
+					this.time=60;
+					this.btnStr=`重新获取(${--this.time}s)`
+					this.timer=setInterval(()=>{
+						if(this.time<=1){
+							clearInterval(this.timer);
+							this.timer=null;
+							this.btnStr="重新获取"
+						}
+						else{
+							this.btnStr=`重新获取(${--this.time}s)`
+						}
+					},1000)
+			},
+			getMineDetail(regStepNew){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.mineDetail({
+					getAlbum:true,
+					completeUser:user
+				}).then(res=>{
+					this.$store.commit('setUserInfo',res.data);
+					uni.setStorageSync('userInfo',JSON.stringify(res.data));//正式环境删除
+					let step=uni.getStorageSync('regStep')||regStepNew;
+					switch(step){
+						case 'Sex':
+							uni.navigateTo({
+								url:'/pages/info/sex'
+							})
+						break;
+						case 'SocialImage':
+							uni.navigateTo({
+								url:'/pages/info/figure'
+							})
+						break;
+						case 'SocialData':
+							uni.navigateTo({
+								url:'/pages/info/datum'
+							})
+						break;
+						case 'CityStay':
+							uni.navigateTo({
+								url:'/pages/info/city'
+							})
+						break;
+						case 'WxInfo':
+							uni.navigateTo({
+								url:'/pages/info/wechat'
+							})
+						break;
+						case 'Basic':
+						case 'Index':
+							uni.switchTab({
+								url:'/pages/friends/friends'
+							})
+						break;
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+.container{
+	background-color: $bgcolor1;
+	width: 100vw;
+	height: 100vh;
+	.tip{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+	}
+	.phone-tip{
+		color: $fontcolor2;
+		padding-left: 60rpx;
+		
+	}
+	.input-box{
+		
+		margin: 0rpx 60rpx;
+		margin-top: 160rpx;
+		background-color: $bgcolor3;
+		border-radius: 16rpx;
+		height: 112rpx;
+		padding: 0rpx 32rpx;
+		.input{
+			color: $fontcolor5;
+		}
+		.clear{
+			width: 32rpx;
+			height: 32rpx;
+		}
+	}
+	.btn{
+		margin: 0rpx 60rpx;
+		margin-top: 120rpx;
+		background-color: $fontcolor1;
+		height: 104rpx;
+		border-radius: 52rpx;
+		line-height: 104rpx;
+		text-align: center;
+		color: $fontcolor3;
+	}
+	.link{
+		box-sizing: border-box;
+		margin: 0rpx 60rpx;
+		margin-top: 30rpx;
+		background-color: transparent;
+		height: 104rpx;
+		border-radius: 52rpx;
+		line-height: 104rpx;
+		text-align: center;
+		color: $fontcolor3;
+		border: 1rpx solid $fontcolor1;
+	}
+	.primarybg{
+		background-color: $primary !important;
+		color: $fontcolor5 !important;
+	}
+}
+</style>

+ 152 - 0
pages/login/loginByPassword.vue

@@ -0,0 +1,152 @@
+<template>
+	<view class="container">
+		<TopBar :icon="topbarIcon" :title="topbarTitle"></TopBar>
+		<view class="tip font44 fw600" style="margin-top: 100rpx;">
+			嗨,请输入
+		</view>
+		<view class="tip font44 fw600">
+			你的手机号码和密码
+		</view>
+		<view class="phone-tip font28 fw400">
+			密码需要6-20位,可使用字母、数字、字符
+		</view>
+		<view class="input-box flex-between" style="margin-top: 160rpx;">
+			<input type="number" maxlength="11" placeholder="请输入手机号" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="phone" @input="phoneChange">
+			<image src="../../static/input-clear.png" mode="aspectFill" class="clear" v-if="phone.length>0" @click="clearPhone"></image>
+		</view>
+		<view class="input-box flex-between" style="margin-top: 56rpx;">
+			<input :type="!showPassword?'password':'text'" maxlength="20" placeholder="请输入密码" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="password" @input="passwordChange">
+			<image :src="`${assetsUrl}${showPassword?'eye-open':'eye-close'}.png`" mode="aspectFill" class="clear" @click="showPassword=!showPassword" v-if="password.length>0"></image>
+		</view>
+		<view class="btn font32 fw400" :class="isCheck?'primarybg':''" @click="login">
+			立即登录
+		</view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				topbarIcon:'back',
+				topbarTitle:'',
+				phone:'',
+				password:'',
+				isCheck:false,
+				showPassword:false,
+				
+			};
+		},
+		methods:{
+			clearPhone(){
+				this.phone='';
+			},
+			phoneChange(e){
+				if(e.detail.value.length===11&&this.$util.checkPhone(e.detail.value)&&this.password.length>6){
+					this.isCheck=true;
+				}
+				else{
+					this.isCheck=false;
+				}
+			},
+			passwordChange(e){
+				if(this.$util.checkPhone(this.phone)&&e.detail.value.length>6){
+					this.isCheck=true;
+				}
+				else{
+					this.isCheck=false;
+				}
+			},
+			toCode(){
+				uni.navigateTo({
+					url:'/pages/login/loginByCode'
+				})
+			},
+			login(){
+				if(!this.phone){
+					uni.showToast({
+						icon:'none',
+						title:'请输入手机号码!'
+					})
+					return;
+				}
+				if(!this.$util.checkPhone(this.phone)){
+					uni.showToast({
+						icon:'none',
+						title:'手机号码格式不正确!'
+					})
+					return;
+				}
+				if(!this.password){
+					uni.showToast({
+						icon:'none',
+						title:'请输入密码!'
+					})
+					return;
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+.container{
+	background-color: $bgcolor1;
+	width: 100vw;
+	height: 100vh;
+	.tip{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+	}
+	.phone-tip{
+		color: $fontcolor2;
+		padding-left: 60rpx;
+		
+	}
+	.input-box{
+		
+		margin: 0rpx 60rpx;
+		
+		background-color: $bgcolor3;
+		border-radius: 16rpx;
+		height: 112rpx;
+		padding: 0rpx 32rpx;
+		.input{
+			color: $fontcolor5;
+		}
+		.clear{
+			width: 32rpx;
+			height: 32rpx;
+		}
+	}
+	.btn{
+		margin: 0rpx 60rpx;
+		margin-top: 120rpx;
+		background-color: $fontcolor1;
+		height: 104rpx;
+		border-radius: 52rpx;
+		line-height: 104rpx;
+		text-align: center;
+		color: $fontcolor3;
+	}
+	.link{
+		box-sizing: border-box;
+		margin: 0rpx 60rpx;
+		margin-top: 30rpx;
+		background-color: transparent;
+		height: 104rpx;
+		border-radius: 52rpx;
+		line-height: 104rpx;
+		text-align: center;
+		color: $fontcolor3;
+		border: 1rpx solid $fontcolor1;
+	}
+	.primarybg{
+		background-color: $primary !important;
+		color: $fontcolor5 !important;
+	}
+}
+</style>

+ 137 - 0
pages/login/loginByPhone.vue

@@ -0,0 +1,137 @@
+<template>
+	<view class="container">
+		<TopBar :icon="topbarIcon" :title="topbarTitle"></TopBar>
+		<view class="tip font44 fw600" style="margin-top: 100rpx;">
+			嗨,请验证
+		</view>
+		<view class="tip font44 fw600">
+			你的手机号码
+		</view>
+		<view class="input-box flex-between">
+			<input type="number" maxlength="11" placeholder="请输入手机号" placeholder-style="color:#494667;font-size:28rpx;" class="input fw500 font32" v-model="getCodeOptions.phone" @input="inputChange">
+			<image src="../../static/input-clear.png" mode="aspectFill" class="clear" v-show="phone!==''" @click="clear"></image>
+		</view>
+		<view class="btn font32 fw400" :class="isCheck?'primarybg':''" @click="toCode">
+			获取验证码
+		</view>
+		<!-- <view class="link" @click="toPassword">
+			密码登录
+		</view> -->
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				topbarIcon:'back',
+				topbarTitle:'',
+				isCheck:true,
+				getCodeOptions:{
+					authenticate: "authenticate",
+					phone: '',
+					scene: "VerCodeLogin",
+					token: "token",
+					wxLoginInfo:{
+						openId:uni.getStorageSync('openId')||null,
+						unionId:uni.getStorageSync('unionId')||null
+					}
+				}
+			};
+		},
+		methods:{
+			inputChange(e){
+				if(e.detail.value.length===11&&this.$util.checkPhone(e.detail.value)){
+					this.isCheck=true;
+				}
+				else{
+					this.isCheck=false;
+				}
+			},
+			clear(){
+				this.getCodeOptions.phone='';
+			},
+			toCode(){
+				if(!this.isCheck){
+					uni.showToast({
+						icon:'none',
+						title:'请输入手机号'
+					})
+					return;
+				}
+				uni.showLoading({
+					mask:true,
+					title:'发送中'
+				})
+				this.$api.login.call4VeriCode(this.getCodeOptions).then(res=>{	
+					uni.hideLoading();
+					uni.navigateTo({
+						url:`/pages/login/loginByCode?phone=${this.getCodeOptions.phone}`
+					})
+				}).catch(err=>{})
+				
+			},
+			toPassword(){
+				uni.navigateTo({
+					url:'/pages/login/loginByPassword'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+.container{
+	background-color: $bgcolor1;
+	width: 100vw;
+	height: 100vh;
+	.tip{
+		color: $fontcolor5;
+		padding-left: 60rpx;
+	}
+	.input-box{
+		
+		margin: 0rpx 60rpx;
+		margin-top: 160rpx;
+		background-color: $bgcolor3;
+		border-radius: 16rpx;
+		height: 112rpx;
+		padding: 0rpx 32rpx;
+		.input{
+			color: $fontcolor5;
+		}
+		.clear{
+			width: 32rpx;
+			height: 32rpx;
+		}
+	}
+	.btn{
+		margin: 0rpx 60rpx;
+		margin-top: 120rpx;
+		background-color: $fontcolor1;
+		height: 104rpx;
+		border-radius: 52rpx;
+		line-height: 104rpx;
+		text-align: center;
+		color: $fontcolor3;
+	}
+	.link{
+		box-sizing: border-box;
+		margin: 0rpx 60rpx;
+		margin-top: 30rpx;
+		background-color: transparent;
+		height: 104rpx;
+		border-radius: 52rpx;
+		line-height: 104rpx;
+		text-align: center;
+		color: $fontcolor3;
+		border: 1rpx solid $fontcolor1;
+	}
+	.primarybg{
+		background-color: $primary !important;
+		color: $fontcolor5 !important;
+	}
+}
+</style>

+ 477 - 0
pages/messages/messages.vue

@@ -0,0 +1,477 @@
+<template>
+	<view class="container">
+		<TabBar :tabIndex="tabIndex"></TabBar>
+		<uni-popup ref="popup" type="center">
+			<Popup :content1="popup.content1" :content2="popup.content2" :tip1="popup.tip1" :tip2="popup.tip2" :btntext="popup.btntext" @closePopup="closePopup"  @toService="toService" :btnEvent="'toService'"></Popup>
+		</uni-popup>
+		<uni-popup ref="paypopup" type="bottom" :safe-area="false">
+			<PayPopup :swiperIndex="payPopupIndex" @closePopup="closePayPopup"></PayPopup>
+		</uni-popup>
+		<uni-popup ref="vippopup" type="center">
+			<VipPopup :swiperIndex="vipPopupIndex" @closePopup="closeVipPopup"></VipPopup>
+		</uni-popup>
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}" >
+			<view class="nav-item font44 fw600">
+				消息
+			</view>
+			<image :src="`${assetsUrl}message-setting.png`" mode="aspectFill" class="nav-img" @click="openSetting"></image>
+		</view>
+		<scroll-view class="scroll-view"
+			v-if="scrollHeight>0"
+			scroll-y="true" 
+			lower-threshold="200"
+			:style="{'height':`${scrollHeight}px`,'padding-top':`${topbarOffsetHeight}px`}" 
+			>
+				<!-- <view class="message-item flex-between">
+					<image :src="`${assetsUrl}message-system.png`" mode="aspectFill" class="left-img"></image>
+					<view class="right-info">
+						<view class="ri-top flex-between">
+							<view class="rit-title fw600 font32">
+								系统通知
+							</view>
+							<view class="rit-time font22 fw400">
+								2022-8-31 09:57:13
+							</view>
+						</view>
+						<view class="ri-bottom flex-between">
+							<view class="rib-text font28 fw400">
+								heihei
+							</view>
+							<view class="rib-num font20 fw400">
+								99
+							</view>
+						</view>
+					</view>
+				</view> -->
+				<view class="message-item flex-between" v-for="(item,index) in messagesList" :key="index"  :data-index="index" @touchstart="touchStart" @touchend="touchEnd">
+					<image :src="item.userProfile.avatar" mode="aspectFill" class="left-img" :style="{'width':`${showDelIndex===index?'0rpx':'120rpx'}`}" @click="toTalk(item.userProfile.userID)"></image>
+					<view class="right-info" @click="toTalk(item.userProfile.userID)">
+						<view class="ri-top flex-between">
+							<view class="rit-title fw600 font32">
+								{{item.userProfile.nick}}
+							</view>
+							<view class="rit-time font22 fw400">
+								{{item.lastMessage.lastTimeStr}}
+							</view>
+						</view>
+						<view class="ri-bottom flex-between">
+							<view class="rib-text font28 fw400">
+								{{item.lastMessage.payload.text}}
+							</view>
+							<view class="rib-num font20 fw400" v-if="item.unreadCount!==0">
+								{{item.unreadCount}}
+							</view>
+						</view>
+					</view>
+					<view class="del-box font28 fw400" :style="{'width':`${showDelIndex===index?'120rpx':'0rpx'}`}" @click="delConversation">
+						删除
+					</view>
+				</view>
+			<!-- <view class="no-more font24 fw400" v-if="messagesList.length!==0&&messagesList.length>=recommendTotal">没有更多了</view> -->
+			<Status type="noMsg" text="暂无消息" v-if="showNoMsg"></Status>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import TabBar from '@/components/TabBar/TabBar.vue';
+	import Popup from '@/components/Popup/Popup.vue';
+	import VipPopup from '@/components/Popup/VipPopup.vue';
+	import PayPopup from '@/components/Popup/PayPopup.vue';
+	import Status from '@/components/Status/Status.vue';
+	/**
+	 * 腾讯位置服务,手机账号:18996226740
+	 */
+	import wxMap from '@/static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js';
+	const wxMapSdk=new wxMap({key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII'})
+	export default {
+		components:{TabBar,Popup,VipPopup,PayPopup,Status},
+		data() {
+			return {
+				scrollHeight:0,
+				topNavAlpha:0,
+				assetsUrl:this.$util.assetsUrl,
+				startTime:0,
+				startPosition:0,
+				endPosition:0,
+				showDelIndex:-1,
+				tabIndex:1,
+				messagesList:[],
+				popup:{
+					content1:'',
+					content2:'',
+					tip1:'',
+					tip2:'',
+					btntext:''
+				},
+				payPopupIndex:-1,
+				vipPopupIndex:-1,
+				userInfo:null,//会话消息用户信息
+				showNoMsg:false,
+				systemMsg:{},
+				
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			platform(){
+				return this.$store.state.platform;
+			},
+			mineInfo(){
+				return this.$store.state.userInfo
+			}
+		},
+		onLoad() {
+			this.computedScollviewHeight();
+			if(!uni.getStorageSync('token')){
+				this.showNoMsg=true;
+				return;
+			}
+			uni.$TUIKit.on(uni.$TUIKitEvent.CONVERSATION_LIST_UPDATED, this.onConversationListUpdated);
+			
+		},
+		onShow() {
+			if(!uni.getStorageSync('token')){
+				return;
+			}
+			this.getSystemMessages();
+			this.getUserMessages();
+		},
+		onShareAppMessage(){
+			return {
+				title: '糖果公园',
+				path: `/pages/login/login?share=${this.userInfo.inviteCode}`,
+			}
+		},
+		methods:{
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf = this.$store.state.tabbarHeight/2;
+				query.select('#topnav').boundingClientRect(data => {
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			},
+			toLogin(){
+				uni.reLaunch({
+					url:'/pages/login/login'
+				})
+			},
+			closePopup(){
+				this.$refs.popup.close();
+				
+			},
+			closePayPopup(){
+				this.payPopupIndex=-1;
+				this.$refs.paypopup.close();
+			},
+			closeVipPopup(){
+				this.vipPopupIndex=-1;
+				this.$refs.vippopup.close();
+			},
+			touchStart(e){
+				this.startTime = Date.now()
+				this.startPosition = e.changedTouches[0].clientX;
+			},
+			touchEnd(e){
+				const endTime = Date.now()
+				if (endTime - this.startTime <100){
+					return;
+				}
+				if (Math.abs(this.endPosition - this.startPosition) > 100){
+					this.endPosition = e.changedTouches[0].clientX;
+					let elePosition = this.endPosition - this.startPosition < 0 ? "toLeft": "toRight";
+					console.log(elePosition)
+					if(elePosition==='toLeft'){
+						this.showDelIndex=e.currentTarget.dataset.index;
+					}
+					else{
+						this.showDelIndex=-1;
+					}
+				} else {
+					return;
+				}
+			},
+			onConversationListUpdated(event){
+				if(event.data.length===0){
+					this.messagesList=[];
+					this.showNoMsg=true;
+					return;
+				}
+				else{
+					this.getUserMessages();
+				}
+				
+			},
+			delConversation(){
+				uni.$TUIKit.deleteConversation(this.messagesList[this.showDelIndex].conversationID);
+				this.showDelIndex=-1;
+			},
+			openSetting(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.popup.open();
+					return;
+				}
+				uni.showActionSheet({
+					itemList: ['全部已读', '清空消息'],
+					success: (res)=>{
+						if(res.tapIndex===0){
+							uni.$TUIKit.setAllMessageRead({scope:uni.$TUIKitTypes.READ_ALL_MSG});
+						}
+						if(res.tapIndex===1){
+							uni.showModal({
+								title:'清空消息列表',
+								content:'清空消息列表后,暂无恢复,请确认是否清空消息列表',
+								success: (ress) => {
+									if(ress.confirm){
+										this.messagesList.forEach(item=>{
+											uni.$TUIKit.deleteConversation(item.conversationID);
+										})
+									}
+								}
+							})
+						}
+					},
+					fail: res=>{
+						console.log(res.errMsg);
+					}
+				});
+			},
+			getSystemMessages(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.notifyActive({}).then(res=>{
+					this.$api.public.loadSystemMsgs({
+						completeUser:user,
+						onlineRecent:false,
+						page:{
+							index:1,
+							size:20,
+							sortValue:null
+						}
+					}).then(result=>{
+						
+					})
+				})
+			},
+			getUserMessages(){
+				uni.$TUIKit.getConversationList().then(res=>{
+					console.log(res.data.conversationList)
+					for(let i=0;i<res.data.conversationList.length;i++){
+						res.data.conversationList[i].lastMessage.lastTimeStr=this.$moment(res.data.conversationList[i].lastMessage.lastTime*1000).format('YYYY-MM-DD hh:mm:ss');
+						if(res.data.conversationList[i].lastMessage.type==='TIMImageElem'){
+							res.data.conversationList[i].lastMessage.payload.text="[图片消息]"
+						}
+						if(res.data.conversationList[i].lastMessage.type==='TIMVideoFileElem'){
+							res.data.conversationList[i].lastMessage.payload.text="[视频消息]"
+						}
+						if(res.data.conversationList[i].lastMessage.type==='TIMSoundElem'){
+							res.data.conversationList[i].lastMessage.payload.text="[音频消息]"
+						}
+						if(res.data.conversationList[i].lastMessage.type==='TIMCustomElem'){
+							let msg=JSON.parse(res.data.conversationList[i].lastMessage.payload.data);
+							if(msg.type===99)
+							{
+								res.data.conversationList[i].lastMessage.payload.text="[系统消息]"
+							}
+							if(msg.type===5)
+							{
+								res.data.conversationList[i].lastMessage.payload.text="[图片消息]"
+							}
+							if(msg.type===6){
+								res.data.conversationList[i].lastMessage.payload.text="[视频消息]"
+							}
+							if(msg.type===100){
+								res.data.conversationList.splice(i,1);
+								i--;
+								// res.data.conversationList[i].userProfile.nick='社区动态';
+								// res.data.conversationList[i].userProfile.avatar=`${this.assetsUrl}message-active.png`
+								// res.data.conversationList[i].lastMessage.payload.text="[有人喜欢你]"
+							}
+						}
+					}
+					this.messagesList=res.data.conversationList;
+					if(this.messagesList.length===0){
+						this.showNoMsg=true;
+					}
+					else{
+						this.showNoMsg=false;
+					}
+				})
+			},
+			toTalk(id){
+				uni.showLoading({
+					mask:true,
+					title:'加载中'
+				})
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.userDetail({getAlbum:true,completeUser:user,uponUserId:id}).then(res=>{
+					console.log(res);
+					this.userInfo=res.data;
+					let arr=[],obj={latitude:0,longitude:0};
+					obj.latitude=this.userInfo.geo.lat;
+					obj.longitude=this.userInfo.geo.lon;
+					arr.push(obj);
+					wxMapSdk.calculateDistance({
+						mode:'straight',
+						from:{
+							latitude: this.$store.state.latitude,
+							longitude: this.$store.state.longitude
+						},
+						to:arr,
+						success:dists=>{
+							uni.hideLoading();
+							if(dists.message==="query ok"){
+								console.log(dists,this.mineInfo)
+								for(let j=0;j<dists.result.elements.length;j++){
+									this.userInfo.distance=(dists.result.elements[j].distance>1000?`${Math.floor(dists.result.elements[j].distance/100)/10}km`:`${dists.result.elements[j].distance}m`)
+								}
+								if(this.mineInfo.sex==='Male'&&!this.mineInfo.vip){
+									if(this.platform==='ios'){
+										this.vipPopupIndex=0;
+										this.$refs.vippopup.open();
+										return;
+									}
+									else{
+										this.payPopupIndex=0;
+										this.$refs.paypopup.open();
+										return;
+									}
+									
+								}
+								if(this.mineInfo.sex==='Famale'&&!this.mineInfo.realMan){
+									this.popup={
+										content1:'认证后才能开启私聊哦',
+										content2:'给客服回复关键词「真人认证」',
+										tip1:'',
+										tip2:'',
+										btntext:'联系客服去认证'
+									}
+									this.$refs.popup.open();
+									return;
+								}
+								uni.navigateTo({
+									url:`/pagesSub/chatting/chatting?conversationid=C2C${id}`
+								})
+							}
+							else{
+								uni.showToast({
+									title:'计算距离失败',
+									icon:'none'
+								});
+								uni.navigateTo({
+									url:`/pagesSub/chatting/chatting?conversationid=C2C${id}`
+								})
+							}
+							
+						},
+						fail:err=>{
+							console.log(err)
+						}
+					})
+					
+					
+				})
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100%;
+		z-index: 100;
+		backdrop-filter: blur(10px);
+		.nav-item {
+			height: 56rpx;
+			margin-left: 16rpx;
+			color: #ffffff;
+			
+		}
+		.nav-img{
+			width: 44rpx;
+			height: 44rpx;
+			margin: 4rpx 0rpx 0rpx 13rpx;
+		}
+	}
+	.scroll-view{
+		.message-item{
+			width: 100vw;
+			box-sizing: border-box;
+			padding: 24rpx 28rpx;
+			.left-img{
+				width: 120rpx;
+				height: 120rpx;
+				margin-right: 24rpx;
+				border-radius: 44rpx;
+				transition: all 0.3s;
+			}
+			.right-info{
+				flex: 1;
+				height: 120rpx;
+				transition: all 0.3s;
+				.ri-top{
+					margin-top: 8rpx;
+					.rit-title{
+						color: $fontcolor5;
+					}
+					.rit-time{
+						color: $fontcolor2;
+					}
+				}
+				.ri-bottom{
+					margin-top: 20rpx;
+					.rib-text{
+						color: $fontcolor3;
+					}
+					.rib-num{
+						width:36rpx;
+						height: 36rpx;
+						border-radius: 36rpx;
+						background-color: #FE3B49;
+						color: #ffffff;
+						line-height: 36rpx;
+						text-align: center;
+					}
+				}
+			}
+			.del-box{
+				background-color: #FE3B49;
+				color: #ffffff;
+				height: 120rpx;
+				transition: all 0.3s;
+				overflow: hidden;
+				line-height: 120rpx;
+				text-align: center;
+				margin-left: 16rpx;
+			}
+		}
+	}
+}
+</style>

+ 586 - 0
pages/mine/album.vue

@@ -0,0 +1,586 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`,'background-color':`rgba(21, 17, 38,${topNavAlpha})`}" v-if="!cover.isFullScreen">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-text font32 fw600">
+				{{pageName}}
+			</view>
+			<view class="nav-item"></view>
+		</view>
+		<uni-popup ref="modalPopup" type="center">
+			<Popup :content1="popup.content1" :content2="popup.content2" :tip1="popup.tip1" :tip2="popup.tip2" :btntext="popup.btntext" @closePopup="closePopup" @toService="toService" :btnEvent="'toService'"></Popup>
+		</uni-popup>
+		<uni-popup ref="popup" type="bottom" @maskClick="popupMaskClick">
+			<view class="popup" @touchmove.prevent>
+				<view class="p-title-box flex-between">
+					<view class="p-title font36 fw600">
+						上传相册
+					</view>
+					<image :src="`${assetsUrl}info-figure-close.png`" mode="aspectFill" class="p-close" @click="closePopup"></image>
+				</view>
+				<view class="p-title-tip font28 fw400">
+					上传清晰本人照片更容易交到好友哦
+				</view>
+				<view class="p-img-box flex-between">
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img1.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							五官清晰
+						</view>
+					</view>
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img2.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							半身照
+						</view>
+					</view>
+					<view class="p-img">
+						<image :src="`${assetsUrl}info-figure-img3.png`" mode="aspectFill" class="img"></image>
+						<view class="p-text font22 fw400">
+							风景和人
+						</view>
+					</view>
+				</view>
+				<view style="height: 60rpx;">
+					
+				</view>
+			</view>
+			
+		</uni-popup>
+		<uni-popup ref="authPopup" type="bottom" @maskClick="authPopupMaskClick">
+			<view class="auth-popup" @touchmove.prevent>
+				<view class="ap-title">
+					请上传本人正面清晰照片
+				</view>
+				<view class="ap-imgs flex-start">
+					<view class="ap-img-box">
+						<image :src="`${assetsUrl}auth-ap1.png`" mode="aspectFill" class="ap-img"></image>
+						<view class="ap-alpha flex-center">
+							<image :src="`${assetsUrl}auth-right.png`" mode="aspectFill" class="ap-right"></image>
+							<view class="ap-text font30 fw400">
+								清晰正脸
+							</view>
+						</view>
+					</view>
+					<view class="ap-img-box">
+						<image :src="`${assetsUrl}auth-ap2.png`" mode="aspectFill" class="ap-img"></image>
+						<view class="ap-alpha flex-center">
+							<image :src="`${assetsUrl}auth-right.png`" mode="aspectFill" class="ap-right"></image>
+							<view class="ap-text font30 fw400">
+								全身/半身
+							</view>
+						</view>
+					</view>
+					<view class="ap-img-box">
+						<image :src="`${assetsUrl}auth-ap3.png`" mode="aspectFill" class="ap-img"></image>
+						<view class="ap-alpha flex-center">
+							<image :src="`${assetsUrl}auth-right.png`" mode="aspectFill" class="ap-right"></image>
+							<view class="ap-text font30 fw400">
+								记得微笑
+							</view>
+						</view>
+					</view>
+					<view class="ap-img-box">
+						<image :src="`${assetsUrl}auth-ap4.png`" mode="aspectFill" class="ap-img"></image>
+						<view class="ap-alpha flex-center">
+							<image :src="`${assetsUrl}auth-right.png`" mode="aspectFill" class="ap-right"></image>
+							<view class="ap-text font30 fw400">
+								光线合适
+							</view>
+						</view>
+					</view>
+				</view>
+				<view style="height: 60rpx;">
+					
+				</view>
+			</view>
+			
+		</uni-popup>
+		<scroll-view
+		scroll-y="true" 
+		:style="{'height': `${scrollHeight}px`,'padding-top':`${topNavHeight}px`}"
+		v-if="scrollHeight>0"
+		lower-threshold="200"
+		refresher-enabled="true"
+		:refresher-triggered="scrollTriggered"
+		:refresher-threshold="45" 
+		refresher-default-style="white"
+		refresher-background="#151126" 
+		@refresherrefresh="scrollRefresh" 
+		@refresherpulling="scrollPulling"
+		@refresherrestore="scrollRestore" 
+		@refresherabort="scrollAbort"
+		@scrolltolower="scrollToBottom"
+		class="scroll-view"
+		>
+			<drag-image :list="albumData" :number="1000" :custom="true" @addImage="toAddImg" @sortImage="sortImg" @delImage="deleteImg" :chooseImage="isChooseImage" @chooseEvent="chooseImageHandle"></drag-image>
+		</scroll-view>
+		<view class="btn-tip font24 fw400" v-if="!isChooseImage">
+			长按可拖动调整图片顺序
+		</view>
+		<view class="btn font32 fw600" @click="sure">
+			{{btnText}}
+		</view>
+	</view>
+</template>
+
+<script>
+	import TabBar from '@/components/TabBar/TabBar.vue';
+	import Popup from '@/components/Popup/Popup.vue';
+	import wxMap from '@/static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js';
+	import DragImage from '@/components/DragImage/DragImage.vue';
+	import {getPolicy,computeSignature,getKey} from '@/util/oss.js';
+	import {encode} from '@/util/base64.js'
+	/**
+	 * 腾讯位置服务,手机账号:18996226740
+	 */
+	const wxMapSdk=new wxMap({key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII'})
+	export default {
+		components:{TabBar,Popup,DragImage},
+		data() {
+			return {
+				btnText:'去认证',
+				pageName:'编辑相册',
+				isChooseImage:false,//是否为选择图片模式
+				assetsUrl:this.$util.assetsUrl,
+				scrollHeight:0,
+				topNavHeight:0,
+				scrollRefreshing:false,
+				scrollTriggered:true,
+				showNoData:false,
+				albumData:[],
+				authChooseImages:[],
+				moveItemAlbum:{},
+				moveItemIndex:null,
+				currentIndex:0,
+				isSort:false,
+				popup:{
+					content1:'',
+					content2:'',
+					tip1:'',
+					tip2:'',
+					btntext:''
+				},
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			imageCdn(){
+				return this.$store.state.imageCdn;
+			},
+			videoCdn(){
+				return this.$store.state.videoCdn;
+			}
+		},
+		onLoad(options) {
+			if(options.type==='auth'){
+				this.isChooseImage=true;
+				this.btnText='前往认证';
+				this.pageName='选择照片';
+			}else{
+				this.isChooseImage=false;
+			}
+			this.computedScollviewHeight()
+			this.getAlbumData();
+		},
+		mounted() {
+			
+			
+		},
+		onPageScroll(e) {
+			
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			chooseImageHandle(data){
+				this.authChooseImages=data;
+			},
+			toAddImg(){
+				this.btnText='从相册上传';
+				if(this.isChooseImage){
+					this.$refs.authPopup.open();
+				}
+				else{
+					this.$refs.popup.open();
+				}
+				
+			},
+			popupMaskClick(){
+				this.btnText=this.isChooseImage?'前往认证':'去认证';
+			},
+			authPopupMaskClick(){
+				this.btnText=this.isChooseImage?'前往认证':'去认证';
+			},
+			closePopup(){
+				this.$refs.popup.close();
+				this.$refs.modalPopup.close();
+				this.$refs.authPopup.close();
+				this.btnText=this.isChooseImage?'前往认证':'去认证';
+			},
+			sortImg(ids){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.albumSort({
+					completeUser:user,
+					mediaIdsInSeq:ids
+				}).then(res=>{})
+			},
+			toService(){
+				this.$refs.popup.close();
+				uni.openCustomerServiceChat({
+				     extInfo:{
+				         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+				     },
+				     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+				     fail(res){
+						 console.log(res)
+				         wx.showToast({
+				           title: '客服联系失败',
+				           icon:'none'
+				         })
+				     }
+				 })
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.select('#topnav').boundingClientRect(data => {
+					this.topNavHeight=data.height;
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			scrollRefresh(){
+				if (this.scrollRefreshing) 
+				{
+					return;
+				}
+				this.scrollRefreshing = true;
+				setTimeout(() => {
+					this.scrollTriggered = false;
+					this.scrollRefreshing = false;
+				}, 1000)
+				this.getAlbumData();
+			},
+			scrollPulling(e) {},
+			scrollRestore() {this.scrollTriggered = true;},
+			scrollAbort() {},
+			scrollToBottom(){
+
+			},
+			getAlbumData(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.album({uurd:user.id}).then(res=>{
+					for(let i=0;i<res.data.dms.length;i++){
+						res.data.dms[i].isMove=false;
+					}
+					let arr=[];
+					if(this.isChooseImage){
+						for(let i=0;i<res.data.dms.length;i++){
+							if(res.data.dms[i].cate==='Img'){
+								arr.push(res.data.dms[i])
+							}
+						}
+						this.albumData=arr;
+					
+					}
+					else{
+						this.albumData=res.data.dms;
+					}
+					
+				})
+			},
+			deleteImg(index){
+				this.$api.public.albumDelete({mediaId:this.albumData[index].mediaId}).then(res=>{})
+			},
+			sure(){
+				if(this.btnText==='从相册上传'){
+					
+					this.addImg();
+				}
+				else{
+					this.popup={
+						content1:'进行认证流程需给客服回复关键词',
+						content2:'「真人认证」',
+						tip1:'',
+						tip2:'',
+						btntext:'联系客服去认证'
+					}
+					this.$refs.modalPopup.open();
+				}
+			},
+			
+			async addImg(){
+				const that=this;
+				this.closePopup();
+				
+				uni.chooseMedia({
+				  count: 9,
+				  mediaType: this.isChooseImage?['image']:['image','video'],
+				  sourceType: ['album', 'camera'],
+				  maxDuration: 30,
+				  camera: 'back',
+				  success(res) {
+					uni.showLoading({
+						mask:true,
+						title:"正在上传···"
+					})
+						let arr=[],obj={},imageCdn='',videoCdn='';
+						
+						
+						for(let i=0;i<res.tempFiles.length;i++){
+							obj={
+								cate:res.tempFiles[i].fileType==='image'?'Img':'Vdo',
+								cdt:'General',
+								env:'Album',
+								url:null
+							}
+							const policyText=getPolicy();
+							const policy=encode(JSON.stringify(policyText));
+							const key=getKey(i,res.tempFiles[i].tempFilePath.split('.')[1]);
+							that.$api.public.aliossToken({}).then(resuslt=>{
+								let formData={
+									key:key,
+									policy:policy,
+									OSSAccessKeyId:resuslt.data.accessKeyId,
+									signature:computeSignature(resuslt.data.accessKeySecret,policy),
+									'x-oss-security-token':resuslt.data.securityToken,
+									success_action_status:'200'
+								}
+								uni.uploadFile({
+									url: 'https://zhenyanapp-gen.oss-cn-qingdao.aliyuncs.com',
+									filePath: res.tempFiles[i].tempFilePath,
+									name: 'file',
+									header:{
+										"Content-Type": "multipart/form-data"
+									},
+									formData: formData,
+									success: (data) => {
+									    if (data.statusCode === 200) {
+										  if(obj.cate==='Img'){
+											  let link=`${that.imageCdn}/${key}`;
+											  obj.url=link;
+										  }
+										  else if(obj.cate==='Vdo'){
+											  let link=`${that.videoCdn}/${key}`;
+											  obj.url=link;
+										  }
+										  arr.push(obj);
+										  if(arr.length===res.tempFiles.length){
+											    that.$api.public.albumAdd({addMediaParamList:arr}).then(mapResult=>{
+													if(mapResult.status==='Succ'){
+														that.getAlbumData();
+														uni.hideLoading();
+													}
+											    })
+										  }
+									    }
+									},
+									fail: err => {
+									    console.log(err);
+									  }
+								})
+							})
+							
+						}
+				  }
+				})
+			},
+		}
+			
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		backdrop-filter: blur(10px);
+		.nav-item {
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+			
+		}
+		.nav-text{
+			flex: 1;
+			color: $fontcolor5;
+			height: 40rpx;
+			text-align: center;
+		}
+	}
+	.popup{
+		width: 100vw;
+		box-sizing: border-box;
+		padding: 56rpx 60rpx;
+		background-color: $bgcolor3;
+		.p-title-box{
+			.p-title{
+				color: $fontcolor5;
+			}
+			.p-close{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.p-title-tip{
+			color: $fontcolor3;
+			margin-top: 16rpx;
+		}
+		.p-img-box{
+			margin-top: 80rpx;
+			.p-img{
+				.img{
+					width: 200rpx;
+					height: 200rpx;
+				}
+				.p-text{
+					color: $fontcolor2;
+					text-align: center;
+				}
+			}
+		}
+	}
+	.auth-popup{
+		background-color: bgcolor1;
+		border-radius: 40rpx 40rpx 0rpx 0rpx;
+		.ap-title{
+			color: #ffffff;
+			text-align: center;
+		}
+		.ap-imgs{
+			flex-wrap: wrap;
+			margin: 80rpx 64rpx 50rpx 64rpx;
+			.ap-img-box{
+				width: 306rpx;
+				height: 306rpx;
+				border-radius: 8rpx;
+				overflow: hidden;
+				margin-right: 10rpx;
+				margin-bottom: 10rpx;
+				position: relative;
+				&:nth-of-type(2n){
+					margin-right: 0rpx;
+				}
+				.ap-img{
+					width: 306rpx;
+					height: 306rpx;
+				}
+				.ap-alpha{
+					position: absolute;
+					width: 306rpx;
+					height: 80rpx;
+					bottom: 0;
+					left: 0;
+					background: rgba(0,0,0,0.3);
+					.ap-right{
+						width: 48rpx;
+						height: 48rpx;
+					}
+					.ap-text{
+						color: #ffffff;
+					}
+				}
+			}
+		}
+	}
+	.scroll-view{
+		position: relative;
+		padding: 0rpx 30rpx;
+		box-sizing: border-box;
+		.move-area{
+			position: absolute;
+			left: 0;
+			top: 0;
+			width: 100%;
+			flex-wrap: wrap;
+			align-items: flex-start !important;
+			.img-box{
+				width: 216rpx;
+				height: 216rpx;
+				border-radius: 8rpx;
+				position: relative;
+				z-index: 1;
+				margin-right: 19rpx;
+				margin-top: 20rpx;
+				&:nth-of-type(3n){
+					margin-right: 0rpx;
+				}
+				.img-add{
+					width: 216rpx;
+					height: 216rpx;
+					border-radius: 8rpx;
+				}
+				.img-delete{
+					position: absolute;
+					right: 16rpx;
+					top: 16rpx;
+					width: 40rpx;
+					height: 40rpx;
+				}
+			}
+			
+		}
+	}
+	.btn-tip{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 160rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 50rpx;
+		color: #9A9ABF;
+		text-align: center;
+		line-height: 50rpx;
+	}
+	.btn{
+		position: fixed;
+		z-index: 999;
+		left: 0;
+		right: 0;
+		bottom: 44rpx;
+		margin: auto;
+		width: 630rpx;
+		height: 104rpx;
+		border-radius: 52rpx;
+		background-color: $primary;
+		color: $fontcolor5;
+		text-align: center;
+		line-height: 104rpx;
+	}
+}
+</style>

+ 382 - 0
pages/mine/guest.vue

@@ -0,0 +1,382 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-between"  :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-text font32 fw600">
+				{{pageName}}
+			</view>
+			<view class="nav-item"></view>
+		</view>
+		<scroll-view 
+		scroll-y="true" 
+		:style="{'height': `${scrollHeight}px`,'padding-top':`${topNavHeight}px`}"
+		v-if="scrollHeight>0"
+		lower-threshold="200"
+		refresher-enabled="true"
+		:refresher-triggered="scrollTriggered"
+		:refresher-threshold="45" 
+		refresher-default-style="white"
+		refresher-background="#151126" 
+		@refresherrefresh="scrollRefresh" 
+		@refresherpulling="scrollPulling"
+		@refresherrestore="scrollRestore" 
+		@refresherabort="scrollAbort"
+		@scrolltolower="scrollToBottom"
+		>
+			<view class="list-item flex-start" v-for="(item,index) in listData" :key="index" @click="toDetail(item.id)" >
+				<view class="list-head-box">
+					<image :src="item.iconThumbnail" mode="aspectFill" class="list-head-img"></image>
+					<view class="list-head-dot" style="background-color: #38E825;" v-if="item.lastActiveTime<=30"></view>
+					<view class="list-head-dot" style="background-color: #0ABDEF;" v-else-if="item.lastActiveTime>30&&item.lastActiveTime<=1440"></view>
+				</view>
+				<view class="list-info-box">
+					<view class="name-box flex-between">
+						<view class="name flex-start">
+							<view class="name-text font28 fw600">
+								{{item.nick}}
+							</view>
+							<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFill" class="name-img" v-if="item.vip"></image>
+							<image :src="`${assetsUrl}friends-godness.png`" mode="aspectFill" class="name-img-godness" v-if="item.goddess"></image>
+							<image :src="`${assetsUrl}friends-real.png`" mode="aspectFill" class="name-img" v-else-if="item.realMan"></image>
+						</view>
+						<view class="distance font22 fw400" v-if="item.distance">
+							{{item.distance}}
+						</view>
+					</view>
+					<view class="sex-box flex-center">
+						<image :src="`${assetsUrl}friends-female.png`" mode="aspectFill" class="sex-img"></image>
+						<view class="el font20 fw500 sex-text">
+							{{item.ageInfo.age}}
+						</view>
+					</view>
+					<view class="tip-box font28 fw400 el" v-if="item.desc">
+						签名:{{item.desc}}
+					</view>
+					<view class="tip-box font28 fw400 el" v-else>
+						签名:暂无介绍
+					</view>
+				</view>
+				<view class="img-box flex-between" v-if="item.lastNews">
+					<image :src="item.lastNews.mediaUrls[0]" mode="aspectFill" class="ib1"></image>
+					<image :src="item.lastNews.mediaUrls[1]" mode="aspectFill" class="ib2"></image>
+					<image :src="item.lastNews.mediaUrls[2]" mode="aspectFill" class="ib3"></image>
+				</view>
+			</view>
+			<Status type="noData" text="暂无数据" v-if="showNoData"></Status>
+			<view class="no-more font24 fw400" v-if="listData.length!==0&&listData.length>=scrollTotal">没有更多了</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import wxMap from '@/static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js';
+	/**
+	 * 腾讯位置服务,手机账号:18996226740
+	 */
+	const wxMapSdk=new wxMap({key:'E5SBZ-T2YC3-CBL3F-YGFQQ-26PP2-ERFII'});
+	import Status from '@/components/Status/Status.vue';
+	export default {
+		components: {
+			Status
+		},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				pageName:'',
+				scrollHeight:0,
+				topNavHeight:0,
+				pageType:'',
+				getOptions:{
+					index:1,
+					size:20,
+					sortValues:[]
+				},
+				listData:[],
+				scrollTotal:0,
+				scrollRefreshing:false,
+				scrollTriggered:true,
+				showNoData:false,
+				user:null,
+				otherInfo:null
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			latitude(){
+				return this.$store.state.latitude;
+			},
+			longitude(){
+				return this.$store.state.longitude;
+			},
+		},
+		onLoad(option) {
+			console.log(option)
+			this.pageType=option.pagetype;
+			this.pageName=option.name;
+			this.computedScollviewHeight();
+			this.getGuestData();
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.select('#topnav').boundingClientRect(data => {
+					this.topNavHeight=data.height;
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			scrollRefresh(){
+				if (this.scrollRefreshing) 
+				{
+					return;
+				}
+				this.scrollRefreshing = true;
+				setTimeout(() => {
+					this.scrollTriggered = false;
+					this.scrollRefreshing = false;
+				}, 1000)
+				this.getOptions={
+					index:1,
+					size:20,
+					sortValues:[]
+				}
+				this.getGuestData();
+			},
+			scrollPulling(e) {},
+			scrollRestore() {this.scrollTriggered = true;},
+			scrollAbort() {},
+			scrollToBottom(){
+				if(this.listData.length>=this.scrollTotal){return;}
+				this.getOptions.index++;
+				this.getGuestData();
+			},
+			getGuestData(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.guestor({
+					completeUser:user,
+					listType:this.pageType,
+					page:this.getOptions,
+					prefer:{
+						nick:null,
+						onlineIn24Hour:false
+					},
+					uponUserId:user.id
+				}).then(res=>{
+					if(res.data.users&&res.data.users.length===0){this.showNoData=true;}
+					if(this.getOptions.index>1){
+						this.listData=[...this.listData,...res.data.users];
+					}
+					else{
+						this.listData=res.data.users;
+					}
+					this.scrollTotal=res.data.page.recordCount;
+					let arr=[],obj={latitude:0,longitude:0};
+					for(let i=0;i<this.listData.length;i++){
+						this.listData[i].lastActiveTime=this.$moment(new Date()).diff(this.listData[i].lastActive,'minutes');
+						obj={latitude:0,longitude:0};
+						obj.latitude=this.listData[i].geo.lat;
+						obj.longitude=this.listData[i].geo.lon;
+						arr.push(obj)
+					}
+					console.log(arr)
+					wxMapSdk.calculateDistance({
+						mode:'straight',
+						from:{
+							latitude: this.$store.state.latitude,
+							longitude: this.$store.state.longitude
+						},
+						to:arr,
+						success:dists=>{
+							if(dists.message==="query ok"){
+								for(let j=0;j<dists.result.elements.length;j++){
+									this.listData[j].distance=(dists.result.elements[j].distance>1000?`${Math.floor(dists.result.elements[j].distance/100)/10}km`:`${dists.result.elements[j].distance}m`)
+								}
+								console.log(this.listData)
+							}
+						},fail:err=>{
+							console.log(err)
+						}
+					});
+				})
+			},
+			toDetail(id){
+				uni.showLoading({})
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.userDetail({getAlbum:true,completeUser:user,uponUserId:id}).then(res=>{
+					if(res.data.sex===user.sex){
+						uni.showToast({
+							title:'同性用户不能查看主页',
+							icon:'none'
+						})
+					}
+					else{
+						this.otherInfo=res.data;
+						uni.hideLoading();
+						uni.navigateTo({
+							url:`/pages/friends/user?id=${id}`
+						})
+					}
+				})
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	overflow: hidden;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		background-color: $bgcolor1;
+		.nav-item{
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.nav-text{
+			flex: 1;
+			color: $fontcolor5;
+			height: 40rpx;
+			text-align: center;
+		}
+	}
+	.list-item{
+		flex-wrap: wrap;
+		padding: 40rpx 32rpx;
+		.list-head-box{
+			width: 136rpx;
+			height: 136rpx;
+			position: relative;
+			.list-head-img{
+				width: 136rpx;
+				height: 136rpx;
+				background-color: #ffffff;
+				border-radius: 136rpx;
+			}
+			.list-head-dot{
+				width: 24rpx;
+				height: 24rpx;
+				border-radius: 24rpx;
+				position: absolute;
+				background-color: aqua;
+				bottom: 2rpx;
+				right: 2rpx;
+				border: 2rpx solid $bgcolor2;
+			}
+		}
+		.list-info-box{
+			box-sizing: border-box;
+			width: 544rpx;
+			padding-left: 32rpx;
+			height: 136rpx;
+			flex-direction: column;
+			display: flex;
+			justify-content: flex-start;
+			align-items: flex-start;
+			.name-box{
+				width: 100%;
+				.name{
+					.name-text{
+						color: $fontcolor5;
+					}
+					.name-img{
+						width: 68rpx;
+						height: 32rpx;
+						margin-left: 8rpx;
+						
+					}
+					.name-img-godness{
+						width: 76rpx;
+						height: 40rpx;
+						margin-left: 8rpx;
+						transform: translateY(-5rpx);
+					}
+				}
+				.distance{
+					color: $fontcolor3;
+				}
+			}
+			.sex-box{
+				width: 74rpx;
+				height: 32rpx;
+				background: rgba(226, 53, 104, 0.2);
+				border-radius: 25rpx;
+				margin-top: 12rpx;
+				.sex-img{
+					width: 24rpx;
+					height: 24rpx;
+				}
+				.sex-text{
+					color:#E23568 ;
+				}
+			}
+			.tip-box{
+				color: $fontcolor3;
+				width: 100%;
+				margin-top: 10rpx;
+			}
+			
+		}
+		.img-box{
+			width: 544rpx;
+			height: 168rpx;
+			margin-left: 168rpx;
+			margin-top: 24rpx;
+			.ib1{
+				width: 168rpx;
+				height: 168rpx;
+				border-radius: 16rpx 0rpx 0rpx 16rpx;
+				background: $fontcolor5;
+			}
+			.ib2{
+				width: 168rpx;
+				height: 168rpx;
+				background: $fontcolor5;
+			}
+			.ib3{
+				width: 168rpx;
+				height: 168rpx;
+				border-radius: 0rpx 16rpx 16rpx 0rpx;
+				background: $fontcolor5;
+			}
+		}
+	}
+}
+</style>

+ 1004 - 0
pages/mine/mine.vue

@@ -0,0 +1,1004 @@
+<template>
+	<view class="container">
+		<TabBar :tabIndex="tabIndex" v-if="!cover.isFullScreen"></TabBar>
+		<uni-popup ref="popup" type="center">
+			<Popup :content1="popup.content1" :content2="popup.content2" :tip1="popup.tip1" :tip2="popup.tip2" :btntext="popup.btntext" @closePopup="closePopup"  @toService="toService" :btnEvent="'toService'"></Popup>
+		</uni-popup>
+		<uni-popup ref="loginpopup" type="center">
+			<Popup :content1="popup.content1" :content2="popup.content2" :tip1="popup.tip1" :tip2="popup.tip2" :btntext="popup.btntext" @closePopup="closeLoginPopup"  @toLogin="toLogin" :btnEvent="'toLogin'"></Popup>
+		</uni-popup>
+		<uni-popup ref="vippopup" type="center">
+			<VipPopup :swiperIndex="vipPopupIndex" @closePopup="closeVipPopup"></VipPopup>
+		</uni-popup>
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`,'background-color':`rgba(21, 17, 38,${topNavAlpha})`}" v-if="!cover.isFullScreen">
+			<!-- <view class="nav-item">
+				<image :src="`${assetsUrl}mine-home.png`" mode="widthFix" class="nav-img"></image>
+			</view> -->
+			<view class="nav-item" @click="toService">
+				<image :src="`${assetsUrl}mine-service.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-item" @click="toSetting">
+				<image :src="`${assetsUrl}mine-setting.png`" mode="widthFix"  class="nav-img"></image>
+			</view>
+			<view class="nav-item nav-edit font20 fw400" @click="toEdit">
+				编辑资料
+			</view>
+		</view>
+		<scroll-view scroll-y="true" v-if="scrollHeight>0" :style="{'height':`${scrollHeight}px`}" @scroll="handleScroll">
+		<!-- 'z-index':`${cover.isFullScreen?1011:10}` -->
+			<view class="video-box"  :style="{'height':`${cover.isFullScreen?'100vh':'680rpx'}`,'position':`${cover.isFullScreen?'fixed':'relative'}`}" @click.prevent="videoAction">
+				<video 
+				:src="cover.url" 
+				class="m-video" autoplay loop :controls="false" 
+				muted 
+				object-fit="cover" 
+				v-if="cover.cate==='video'" 
+				@touchstart="touchStart" @touchend="touchEnd"
+				:enable-progress-gesture="false"
+				:style="{'height':`${cover.isFullScreen?'100vh':'680rpx'}`,'position':`${cover.isFullScreen?'fixed':'relative'}`}">
+					<view class="video-inner flex-center" v-if="!cover.isFullScreen">
+						<image :src="userInfo.iconThumbnail||defaulHead" mode="aspectFill" class="head-img" ></image>
+						<view class="name-box flex-center">
+							<view class="name fw600 font44">
+								{{userInfo.nick||'未登录'}}
+							</view>
+							<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFit" class="name-img" v-if="userInfo.vip"></image>
+							<image :src="`${assetsUrl}friends-godness.png`" mode="aspectFit" class="name-img" v-else-if="userInfo.goddess"></image>
+							<image :src="`${assetsUrl}friends-realMan.png`" mode="aspectFit" class="name-img" v-else-if="userInfo.real"></image>
+						</view>
+						<view class="video-edit flex-start" @click="toEditVideo">
+							<image :src="`${assetsUrl}mine-edit.png`" mode="aspectFill" class="video-edit-img"></image>
+							<view class="video-edit-text font22 fw400">
+								修改封面视频
+							</view>
+						</view>
+					</view>
+				</video>
+				<view class="m-video" v-else>
+					<view class="video-inner flex-center" v-if="!cover.isFullScreen">
+						<image :src="userInfo.iconThumbnail||defaulHead" mode="aspectFill" class="head-img" @click="toLogin"></image>
+						<view class="name-box flex-center">
+							<view class="name fw600 font44">
+								{{userInfo.nick||'未登录'}}
+							</view>
+							<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFit" class="name-img" v-if="userInfo.vip"></image>
+							<image :src="`${assetsUrl}friends-godness.png`" mode="aspectFit" class="name-img" v-else-if="userInfo.goddess"></image>
+							<image :src="`${assetsUrl}friends-realMan.png`" mode="aspectFit" class="name-img" v-else-if="userInfo.real"></image>
+						</view>
+						<view class="video-edit flex-start" @click="toEditVideo">
+							<image :src="`${assetsUrl}mine-edit.png`" mode="aspectFill" class="video-edit-img"></image>
+							<view class="video-edit-text font22 fw400">
+								修改封面视频
+							</view>
+						</view>
+					</view>
+					<image :src="cover.url||defaulHead" mode="widthFix" class="cover-inner"></image>
+					<view class="cover-modal"></view>
+				</view>
+				
+			</view>
+			<view class="info-box" :style="{'top':`${cover.isFullScreen?'100vh':'648rpx'}`}" v-if="!cover.isFullScreen">
+				<view class="tabs flex-between">
+					<view class="tab-item flex-center" v-for="(item,index) in tabs" :key="index" @click="toPath(item.path,item.name)">
+						<view class="tab-num font36 fw700">
+							{{item.num}}
+							<span class="tab-dot fw400 font22" v-if="item.dotnum>0">
+								{{item.dotnum}}
+							</span>
+						</view>
+						<view class="tab-text font20 fw400">
+							{{item.name}}
+						</view>
+						
+					</view>
+				</view>
+				<view class="vip-box flex-between" @click="toVip" v-if="userInfo.sex==='Male'">
+					<view class="vip-left flex-start">
+						<image :src="`${assetsUrl}mine-vip.png`" mode="aspectFill" class="vip-img"></image>
+						<view class="vl-box flex-center">
+							<image :src="`${assetsUrl}mine-vip-text.png`" mode="aspectFill" class="vl-img"></image>
+							<view class="vl-text font24 fw400" v-if="userInfo.vip">
+								有效期至{{userInfo.memExpire.split(' ')[0]}}
+							</view>
+							<view class="vl-text font24 fw400" v-else-if="userInfo.hisVip">
+								您的VIP已经过期啦
+							</view>
+							<view class="vl-text font24 fw400" v-else>
+								会员中心
+							</view>
+						</view>
+					</view>
+					<view class="vip-right font22 fw600">
+							立即{{userInfo.vip?'续费':'开通'}}
+					</view>
+				</view>
+				<view class="cards flex-center">
+					<view class="card-item flex-center" :class="item.class" v-for="(item,index) in cards" :key="index" @click="toCardsPath(item.path)">
+						<view class="ci-title font32 fw600">
+							{{item.title}}
+						</view>
+						<view class="ci-tip font22 fw400">
+							{{item.tip}}
+						</view>
+						<image :src="`${assetsUrl}${item.bgImg}`" mode="aspectFit" class="ci-bg"></image>
+					</view>
+				</view>
+				<view class="photo-album">
+					<view class="pa-box flex-between">
+						<view class="pa-title font32 fw600">
+							我的相册
+						</view>
+						<view class="pa-tip font22 fw600" @click="toAlbum">
+							编辑相册
+						</view>
+					</view>
+					<view class="pics-box flex-center" v-if="pics.length>0">
+						<image :src="currentPic.url" mode="widthFix" class="big-pic" v-if="currentPic.cate==='Img'"  @click="preview"></image>
+						<video :src="currentPic.url" class="big-pic" 
+							autoplay 
+							loop 
+							:controls="false" 
+							muted 
+							object-fit="fill" 
+							v-if="currentPic.cate==='Vdo'"  
+							:style="{'height':`${currentPic.height}px`}"
+							@click="preview">
+						</video>
+						<scroll-view scroll-x="true" class="pics-list flex-start" v-if="pics.length>1">
+							<view class="pic-item flex-center"  v-for="(item,index) in pics" :key="index" @click="picItemClick(index)">
+								<image :src="item.urlThumbnail" mode="aspectFill" class="pic-img" :class="currentIndex===index?'active-item':''"></image>
+							</view>
+						</scroll-view>
+						<image :src="`${assetsUrl}user-self.png`" mode="aspectFill" class="self" v-if="currentPic.authStatus==='Succ'"></image>
+					</view>
+					<view class="no-pics-box" v-else>
+						<view class="np-title font36 fw600">
+							添加你的第一张照片吧!
+						</view>
+						<view class="np-tip font28 fw400">
+							分享给懂你的人
+						</view>
+						<view class="np-btn" @click="toAlbum">
+							添加照片
+						</view>
+						<image  :src="`${assetsUrl}mine-no-images.png`" mode="aspectFill" class="np-bg"></image>
+					</view>
+				</view>
+				<view class="wechat-oa">
+					<official-account></official-account>
+				</view>
+				
+				<!-- <view class="null-box"></view> -->
+			</view>
+		
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import TabBar from '@/components/TabBar/TabBar.vue';
+	import Popup from '@/components/Popup/Popup.vue';
+	import VipPopup from '@/components/Popup/VipPopup.vue';
+	
+	export default {
+		components:{TabBar,Popup,VipPopup},
+		data() {
+			return {
+				scrollHeight:0,
+				topNavAlpha:0,
+				assetsUrl:this.$util.assetsUrl,
+				defaulHead:`${this.$util.assetsUrl}default-head.png`,
+				startTime:0,
+				startPosition:0,
+				endPosition:0,
+				tabIndex:2,
+				tabs:[
+					{name:'异性访客',num:0,dotnum:0,path:'/pages/mine/guest?pagetype=VisitForMe&name=异性访客'},
+					{name:'喜欢我的',num:0,dotnum:0,path:'/pages/mine/guest?pagetype=FavsForMe&name=喜欢我的'},
+					{name:'我喜欢的',num:0,dotnum:0,path:'/pages/mine/guest?pagetype=MyFavs&name=我喜欢的'},
+					{name:'我看过的',num:0,dotnum:0,path:'/pages/mine/guest?pagetype=MyVisit&name=我看过的'},
+				],
+				cards:[
+					// {title:'邀请好友',tip:'领取vip时长',bgImg:'mine-gift.png',class:"card1",path:"/pages/webView/webView?url=https://promote.sugarpark.cn"},
+					{title:'认证中心',tip:'提升人气秘密',bgImg:'mine-star.png',class:"card2",path:'/pages/auth/auth'},
+					{title:'我的钱包',tip:'充糖果、看收益',bgImg:'mine-money.png',class:"card3",path:'/pages/wallet/wallet'}
+				],
+				currentPic:null,
+				currentIndex:0,
+				pics:[],
+				cover:{
+					url:'',
+					cate:'image',
+					isFullScreen:false
+				},
+				popup:{
+					content1:'',
+					content2:'',
+					tip1:'',
+					tip2:'',
+					btntext:''
+				},
+				vipPopupIndex:-1,
+				onshowIsNot:false
+				
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			platform(){
+				return this.$store.state.platform;
+			},
+			userInfo(){
+				return this.$store.state.userInfo||{}
+			}
+		},
+		onLoad() {
+			
+			this.cards[1].path=`/pages/wallet/wallet`
+		},
+		onShow() {
+			this.computedScollviewHeight();
+			if(!uni.getStorageSync('token')){
+				return;
+			}
+			if(!this.onshowIsNot){
+				this.getMineData();
+				this.getMineDetail();
+				this.getPkgVideo();
+			}
+			
+			
+		},
+		onShareAppMessage(){
+			return {
+				title: '糖果公园',
+				path: `/pages/login/login?share=${this.userInfo.inviteCode}`,
+			}
+		},
+		methods:{
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let heightLeaf = this.$store.state.tabbarHeight/2;
+				let sysInfo = uni.getSystemInfoSync();
+				this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+			
+			},
+			toCardsPath(path){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				switch(path){
+					case '/pages/auth/auth':
+						this.popup={
+							content1:'进行认证流程需给客服回复关键词',
+							content2:'「真人认证」',
+							tip1:'',
+							tip2:'',
+							btntext:'联系客服去认证'
+						}
+						this.$refs.popup.open();
+						return;
+					break;
+					case '/pages/wallet/wallet':
+						this.popup={
+							content1:'查看钱包和提现需给客服回复关键词',
+							content2:'「钱包」',
+							tip1:'',
+							tip2:'',
+							btntext:'联系客服查看'
+						}
+						this.$refs.popup.open();
+						return;
+						// uni.navigateTo({
+						// 	url:path
+						// })
+					break;
+				}
+				
+			},
+			closePopup(){
+				this.$refs.popup.close();
+				
+			},
+			closeLoginPopup(){
+				this.$refs.loginpopup.close();
+			},
+			closeVipPopup(){
+				this.vipPopupIndex=-1;
+				this.$refs.vippopup.close();
+			},
+			toEditVideo(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:'/pagesSub/faceVideo/faceVideo'
+				})
+			},
+			toLogin(){
+				if(!uni.getStorageSync('token')){
+					uni.reLaunch({
+						url:'/pages/login/login'
+					})
+					return;
+				}
+			},
+			toService(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				this.$refs.popup.close();
+				uni.openCustomerServiceChat({
+				     extInfo:{
+				         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+				     },
+				     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+				     fail(res){
+						 console.log(res)
+				         wx.showToast({
+				           title: '客服联系失败',
+				           icon:'none'
+				         })
+				     }
+				 })
+			},
+			toSetting(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:'/pagesSub/setting/setting'
+				})
+			},
+			handleScroll(e){
+				this.topNavAlpha=e.detail.scrollTop/250;
+			},
+			toAlbum(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:'/pages/mine/album'
+				})
+			},
+			preview(){
+				let arr=[],obj={};
+				console.log(this.pics)
+				this.onshowIsNot=true;
+				for(let i=0;i<this.pics.length;i++){
+					obj={url:'',type:''};
+					if(this.pics[i].cate==='Img'){
+						obj.type='image';
+					}
+					if(this.pics[i].cate==='Vdo'){
+						obj.type='video';
+					}
+					obj.url=this.pics[i].url;
+					arr.push(obj)
+				}
+				uni.previewMedia({
+					sources:arr,
+					current:this.currentIndex,
+					showmenu:true,
+					success: (res) => {
+						console.log(res);
+						this.onshowIsNot=true;
+					},
+					fail: (err) => {
+						console.log(err);
+					}
+				})
+			},
+			toPath(path,name){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				switch(name)
+				{
+					
+					case '异性访客':
+						
+						if(this.userInfo.sex==='Famale'){
+							if(!this.userInfo.realMan){
+								this.popup={
+									content1:'进行认证流程需给客服回复关键词',
+									content2:'「真人认证」',
+									tip1:'',
+									tip2:'',
+									btntext:'联系客服去认证'
+								}
+								this.$refs.popup.open();
+								return;
+								
+							}
+						}
+						else if(this.userInfo.sex==='Male'){
+							if(!this.userInfo.vip){
+								this.vipPopupIndex=3;
+								this.$refs.vippopup.open();
+								return;
+								
+							}
+						}
+						this.tabs[0].dotnum=0;
+					break;
+					case '喜欢我的':
+						
+						// if(this.userInfo.sex==='Famale'){
+						// 	if(!this.userInfo.realMan){
+						// 		this.popup={
+						// 			content1:'进行认证流程需给客服回复关键词',
+						// 			content2:'「真人认证」',
+						// 			tip1:'',
+						// 			tip2:'',
+						// 			btntext:'联系客服去认证'
+						// 		}
+						// 		this.$refs.popup.open();
+						// 		return;
+								
+						// 	}
+						// }
+						// else if(this.userInfo.sex==='Male'){
+						// 	if(!this.userInfo.vip){
+						// 		this.vipPopupIndex=3;
+						// 		this.$refs.vippopup.open();
+						// 		return;
+								
+						// 	}
+						// }
+						this.tabs[1].dotnum=0;
+					break;
+					case '我喜欢的':
+						this.tabs[2].dotnum=0;
+					break;
+					case '我看过的':
+						this.tabs[3].dotnum=0;
+					break;
+				}
+				uni.navigateTo({
+					url:path
+				})
+				
+			},
+			toEdit(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:'/pages/info/editCenter'
+				})
+			},
+			toVip(){
+				if(!uni.getStorageSync('token')){
+					this.popup={
+						content1:'您还未登录',
+						content2:'该功能登录后才能使用',
+						tip1:'',
+						tip2:'',
+						btntext:'去登录'
+					}
+					this.$refs.loginpopup.open();
+					return;
+				}
+				if(this.platform==='ios'){
+					this.vipPopupIndex=0;
+					this.$refs.vippopup.open();
+					return;
+				}
+				uni.navigateTo({
+					url:'/pages/vip/vip'
+				})
+			},
+			touchStart(e){
+				this.startTime = Date.now()
+				this.startPosition = e.changedTouches[0].clientY
+			},
+			touchEnd(e){
+				const endTime = Date.now()
+				if (endTime - this.startTime <100){
+					return;
+				}
+				if (Math.abs(this.endPosition - this.startPosition) > 10){
+					this.endPosition = e.changedTouches[0].clientY;
+					let elePosition = this.endPosition - this.startPosition > 0 ? "toBottom": "toTop"
+					if(elePosition==='toBottom'){
+						this.cover.isFullScreen=true;
+					}
+					else{
+						this.cover.isFullScreen=false;
+					}
+				} else {
+					return;
+				}
+			},
+			videoAction(){
+				if(this.cover.isFullScreen){
+					this.cover.isFullScreen=false;
+				}
+			},
+			getMineData(){
+				this.$api.public.mine({}).then(res=>{
+					this.tabs[0].dotnum=res.data.vmb;
+					this.tabs[0].num=res.data.vme;
+					this.tabs[1].dotnum=res.data.fmb;
+					this.tabs[1].num=res.data.fm;
+					this.tabs[2].num=res.data.fv;
+					this.tabs[3].num=res.data.vbm;
+				})
+			},
+			getMineDetail(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.mineDetail({
+					getAlbum:true,
+					completeUser:user
+				}).then(res=>{
+					this.$store.commit('setUserInfo',res.data);
+					uni.setStorageSync('userInfo',JSON.stringify(res.data));//正式环境删除
+					this.pics=this.userInfo.medias;
+					this.currentPic=this.userInfo.medias[0];
+					
+				})
+			},
+			getPkgVideo(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.videoProcess({
+					completeUser:user,
+					pageHomeUserId:user.id,
+					personalPageHomeVideoOperateEnum:'NONE'
+				}).then(res=>{
+				  if(res.status==='Succ'&&res.data.personalPageHomeVideoUrl){
+					  this.cover.url=res.data.personalPageHomeVideoUrl;
+					  this.cover.cate='video';
+					  return;
+				  }
+				  
+				  if(this.userInfo.bkg&&this.userInfo.bkg.indexOf('.mp4')!==-1){
+				  	this.cover.cate='video';
+				  	this.cover.url=this.userInfo.bkg;
+				  }
+				  else{
+				  	this.cover.cate='img';
+				  	this.cover.url=this.userInfo.bkg;
+				  }
+				})
+			},
+			picItemClick(index){
+				this.currentIndex=index;
+				this.currentPic=this.pics[index];
+				this.currentPic.height=this.currentPic.h/(this.currentPic.w/343);
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	.popup{
+		width: 590rpx;
+		height: 668rpx;
+		background-color: #171325;
+		position: relative;
+		flex-direction: column;
+		border-radius: 40rpx;
+		.close{
+			position: absolute;
+			right: 20rpx;
+			top: 20rpx;
+			width: 32rpx;
+			height: 32rpx;
+		}
+		.popop-img{
+			width: 436rpx;
+			height: 312rpx;
+		}
+		.content{
+			margin-bottom: 16rpx;
+			color: #ffffff;
+		}
+		.btn{
+			width: 470rpx;
+			height: 84rpx;
+			background: #6C52F4;
+			border-radius: 42rpx;
+			line-height: 84rpx;
+			text-align: center;
+			color: #ffffff;
+			margin-top: 68rpx;
+		}
+	}
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100%;
+		z-index: 100;
+		backdrop-filter: blur(10px);
+		.nav-item {
+			width: 56rpx;
+			height: 56rpx;
+			margin-left: 16rpx;
+			.nav-img{
+				width: 56rpx;
+				height: 56rpx;
+			}
+			
+		}
+		.nav-edit{
+			width:120rpx;
+			height: 56rpx;
+			border-radius: 56rpx;
+			color: $fontcolor5;
+			line-height: 56rpx;
+			text-align: center;
+			background-color: rgba(0, 0, 0, 0.5);
+		}
+	}
+	.video-box{
+		width: 100vw;
+		transition: all 0.3s;
+		.m-video{
+			width: 100vw;
+			height: 680rpx;
+			position: relative;
+			.video-inner{
+				flex-direction: column;
+				height: 100%;
+				transform: translateY(30rpx);
+				position: relative;
+				z-index: 6;
+				.head-img{
+					width: 168rpx;
+					height: 168rpx;
+					border-radius: 72rpx;
+					border: 4rpx solid #FFFFFF;
+				}
+				.name-box{
+					height: 60rpx;
+					margin-top: 24rpx;
+					.name{
+						color:  $fontcolor5;
+						height: 60rpx;
+						line-height: 60rpx;
+					}
+					.name-img{
+						width: 76rpx;
+						height: 42rpx;
+						margin-left: 8rpx;
+					}
+				}
+				.video-edit{
+					position: absolute;
+					bottom: 80rpx;
+					right: 32rpx;
+					height: 48rpx;
+					background-color:rgba(0, 0, 0, 0.5);
+					padding: 0 20rpx;
+					border-radius: 48rpx;
+					z-index: 6;
+					.video-edit-img{
+						width: 24rpx;
+						height: 24rpx;
+						margin-right: 4rpx;
+					}
+					.video-edit-text{
+						
+						line-height: 48rpx;
+						color: $fontcolor5;
+					}
+				}
+			}
+			.cover-inner{
+				width: 100vw;
+				height: 680rpx;
+				position: absolute;
+				top: 0;
+				z-index: 4;
+			}
+			.cover-modal{
+				position: absolute;
+				top: 0;
+				width: 100vw;
+				height: 680rpx;
+				z-index: 5;
+				background: rgba(0,0,0,0.4);
+				backdrop-filter: blur(20rpx);
+			}
+		}
+	}
+	.info-box{
+		position: absolute;
+		z-index: 20;
+		left: 0;
+		width: 100vw;
+		transition: top .3s;
+		.tabs{
+			padding: 24rpx 32rpx 0rpx 32rpx;
+			border-radius: 40rpx 40rpx 0rpx 0rpx;
+			
+			background-color: $bgcolor2;
+			.tab-item{
+				width: 172rpx;
+				height: 144rpx;
+				flex-direction: column;
+				
+				.tab-num{
+					color: $fontcolor5;
+					position: relative;
+					.tab-dot{
+						position: absolute;
+						top: -25rpx;
+						left: 40rpx;
+						height: 32rpx;
+						padding: 0rpx 12rpx;
+						line-height: 32rpx;
+						text-align: center;
+						border-radius: 32rpx;
+						color: $fontcolor5;
+						background-color: $assist2;
+					}
+				}
+				.tab-text{
+					color: $fontcolor2;
+					margin-top: 8rpx;
+				}
+				
+			}
+		}
+		.vip-box{
+			box-sizing: border-box;
+			margin: 0rpx 32rpx;
+			background: linear-gradient(133deg, #1B1A1D 0%, #000000 100%);
+			border-radius: 16rpx;
+			.vip-left{
+				.vip-img{
+					width: 164rpx;
+					height: 140rpx;
+					margin-left: 16rpx;
+				}
+				.vl-box{
+					flex-direction: column;
+					margin-left: 16rpx;
+					align-items: flex-start !important;
+					.vl-text{
+						margin-top: 8rpx;
+						color: $fontcolor5;
+						text-align: left;
+						width: 100%;
+					}
+					.vl-img{
+						width: 124rpx;
+						height: 36rpx;
+					}
+				}
+			}
+			.vip-right{
+				width: 136rpx;
+				height: 48rpx;
+				border-radius: 32rpx;
+				border: 1rpx solid #F2CEB9;
+				margin-right: 32rpx;
+				text-align: center;
+				line-height: 48rpx;
+				color: #F2CEB9;
+			}
+		}
+		.cards{
+			margin: 0rpx 32rpx;
+			margin-top: 14rpx;
+			.card-item{
+				flex: 1;
+				height: 136rpx;
+				padding: 0 16rpx;
+				border-radius: 16rpx;
+				flex-direction: column;
+				position: relative;
+				margin-left: 12rpx;
+				&:nth-of-type(1){
+					margin-left: 0rpx;
+				}
+				.ci-title{
+					color: $fontcolor5;
+					width: 100%;
+					text-align: left;
+					position: relative;
+					z-index: 10;
+				}
+				.ci-tip{
+					color: $fontcolor5;
+					margin-top: 10rpx;
+					width: 100%;
+					text-align: left;
+					position: relative;
+					z-index: 10;
+				}
+				.ci-bg{
+					position: absolute;
+					right: 0;
+					bottom: 0;
+					width: 88rpx;
+					height: 118rpx;
+					z-index: 1;
+				}
+			}
+			.card1{
+				background:linear-gradient(133deg, #FA5C74 0%, #F51D44 100%);
+			}
+			.card2{
+				background:linear-gradient(133deg, #8C67FE 0%, #4A5BFE 100%);
+			}
+			.card3{
+				background:linear-gradient(133deg, #FAA85C 0%, #FE884A 100%);
+			}
+		}
+		.photo-album{
+			margin: 56rpx 32rpx 0rpx 32rpx;
+			.pa-box{
+				.pa-title{
+					color: $fontcolor5;
+				}
+				.pa-tip{
+					color: $fontcolor3;
+				}
+			}
+			.pics-box{
+				width: 100%;
+				margin-top: 26rpx;
+				position: relative;
+				margin-bottom: 30rpx;
+				.self{
+					position: absolute;
+					width: 72rpx;
+					height: 40rpx;
+					right: 24rpx;
+					top: 24rpx;
+					z-index: 100;
+				}
+				.big-pic{
+					width: 100%;
+					height: 500rpx;
+					border-radius: 40rpx;
+				}
+				.pics-list{
+					width: 100%;
+					height: 186rpx;
+					position: absolute;
+					bottom: -35rpx;
+					white-space:nowrap;
+					transform: translateY(-20rpx);
+					.pic-item{
+						display: inline-block;
+						width: 96rpx;
+						height: 128rpx;
+						border-radius: 16rpx;
+						margin-left: 16rpx;
+						box-shadow:0rpx 0rpx 10rpx 1rpx #151126;
+						transform: translateY(6rpx);
+						&:nth-last-of-type(1){
+							margin-right: 16rpx;
+						}
+						.pic-img{
+							width: 96rpx;
+							height: 128rpx;
+							border-radius: 16rpx;
+							box-sizing: border-box;
+							border: 4rpx solid transparent;
+							transition: all 0.3s;
+						}
+					}
+					.active-item{
+						border: 4rpx solid #ffffff !important;
+					}
+				}
+			}
+			.no-pics-box{
+				margin: 16rpx 0rpx;
+				background-color: $bgcolor3;
+				border-radius: 20rpx;
+				position: relative;
+				padding: 0rpx 48rpx;
+				padding-top: 64rpx;
+				padding-bottom: 48rpx;
+				.np-title{
+					color: $fontcolor4;
+				}
+				.np-tip{
+					color: $fontcolor4;
+					margin-top: 24rpx;
+				}
+				.np-btn{
+					margin-top: 86rpx;
+					width: 240rpx;
+					height: 72rpx;
+					background: #6C52F4;
+					border-radius: 36rpx;
+					line-height: 72rpx;
+					text-align: center;
+					color: $fontcolor5;
+				}
+				.np-bg{
+					position: absolute;
+					width: 344rpx;
+					height: 400rpx;
+					right: 0;
+					top: 0;
+				}
+			}
+		}
+		.wechat-oa{
+			margin: 32rpx;
+			border-radius: 16rpx;
+			overflow: hidden;
+			margin-top: 60rpx;
+		}
+	}
+	
+}
+</style>

+ 414 - 0
pages/search/search.vue

@@ -0,0 +1,414 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<image :src="`${assetsUrl}back.png`" mode="widthFix" class="back" @click="back" ></image>
+		</view>
+		<view class="search-box flex-start" :style="{'padding-top':`${topbarOffsetHeight+5}px`}">
+			<view class="input-box flex-start">
+				<image :src="`${assetsUrl}search-input-serch.png`" mode="aspectFill" class="search-img"></image>
+				<input :focus="inputFocus" type="text" placeholder="输入昵称进行搜索" placeholder-style="font-size:24rpx;color:#7D7DA4;" class="search-input font24 fw400" v-model="inputText" confirm-type="search" @confirm="search" @input="search">
+			</view>
+			<!-- <view class="cancel font28 fw400" @click="search" v-if="users.length===0||user">
+				搜索
+			</view>
+			<view class="cancel font28 fw400" @click="cancel" v-else>
+				取消
+			</view> -->
+		</view>
+		<view class="his-input" v-if="inputText===null" :style="{'padding-top':`${topbarOffsetHeight+56}px`}">
+			
+			<view class="his-item flex-between" v-for="(item,index) in searchs" :key="index">
+				<view class="his-left flex-start" @click="chooseHisItem(index)">
+					<image :src="`${assetsUrl}search-time.png`" mode="aspectFill" class="his-icon"></image>
+					<text class="his-text font28 fw400">{{item}}</text>
+				</view>
+				<image :src="`${assetsUrl}search-close.png`" mode="aspectFill" class="his-close" @click="closeHisItem(index)"></image>
+			</view>
+			<view class="tip font24 fw400" v-if="searchs.length>0" @click="clearAll">
+				清空搜索记录
+			</view>
+		</view>
+		<view :style="{'padding-top':`${topbarOffsetHeight+56}px`}" v-if="users&&users.length>0">
+			<view class="list-item flex-start" v-for="(item,index) in users" :key="index" @click="toDetail(item.id)">
+				<view class="list-head-box">
+					<image :src="item.iconThumbnail" mode="aspectFill" class="list-head-img"></image>
+					<view class="list-head-dot" style="background-color: #38E825;" v-if="item.lastActiveTime<=30"></view>
+					<view class="list-head-dot" style="background-color: #0ABDEF;" v-else-if="item.lastActiveTime>30&&item.lastActiveTime<=1440"></view>
+				</view>
+				<view class="list-info-box">
+					<view class="name-box flex-between">
+						<view class="name flex-start">
+							<view class="name-text font28 fw600">
+								{{item.nick}}
+							</view>
+							<image :src="`${assetsUrl}friends-vip.png`" mode="aspectFill" class="name-img" v-if="item.vip"></image>
+							<image :src="`${assetsUrl}friends-godness.png`" mode="aspectFill" class="name-img-godness" v-if="item.goddess"></image>
+							<image :src="`${assetsUrl}friends-real.png`" mode="aspectFill" class="name-img" v-else-if="item.realMan"></image>
+						</view>
+						<view class="distance font22 fw400" v-if="item.distance&&item.distance!=='NaNm'">
+							{{item.distance}}
+						</view>
+					</view>
+					<view class="sex-box flex-center" :style="{'background-color':`${item.sex==='Male'?'rgba(108,82,244,0.21)':''}`}">
+						<image :src="`${assetsUrl}friends-female.png`" mode="aspectFill" class="sex-img" v-if="item.sex==='Famale'"></image>
+						<image :src="`${assetsUrl}friends-male.png`" mode="aspectFill" class="sex-img" v-if="item.sex==='Male'"></image>
+						<view class="el font20 fw500 sex-text1" v-if="item.sex==='Famale'">
+							{{item.ageInfo.age}}
+						</view>
+						<view class="el font20 fw500 sex-text2" v-if="item.sex==='Male'">
+							{{item.ageInfo.age}}
+						</view>
+					</view>
+					<view class="tip-box font28 fw400 el" v-if="item.desc">
+						签名:{{item.desc}}
+					</view>
+					<view class="tip-box font28 fw400 el" v-else>
+						签名:暂无介绍
+					</view>
+				</view>
+				<view class="img-box flex-between" v-if="item.lastNews">
+					<image :src="item.lastNews.mediaUrls[0]" mode="aspectFill" class="ib1"></image>
+					<image :src="item.lastNews.mediaUrls[1]" mode="aspectFill" class="ib2"></image>
+					<image :src="item.lastNews.mediaUrls[2]" mode="aspectFill" class="ib3"></image>
+				</view>
+			</view>
+		</view>
+		<view class="no-more font24 fw400" v-if="users!==null">没有更多了</view>
+		<Status type="noData" text="暂无数据" v-if="showNoData||!users||users===[]"></Status>
+	</view>
+</template>
+
+<script>
+	import Status from '@/components/Status/Status.vue';
+	export default {
+		components: {
+			Status
+		},
+		data() {
+			return {
+				assetsUrl: this.$util.assetsUrl,
+				inputText:null,
+				searchOptions:{
+					completeUser:{},
+					geo:{
+						lat:0,
+						lon:0
+					},
+					listType:'',
+					page:{
+						index:1,
+						size:20,
+						sortValues:[]
+					},
+					prefer:{
+						cityCode:null,
+						femaleGoddess:false,
+						femaleNew:false,
+						maleNew:false,
+						maleVip:false,
+						maxAge:null,
+						minAge:null,
+						nick:'',
+						onlineIn24Hour:null,
+						onlineIn30Min:null,
+						sortType:'Recommend'
+					}
+				},
+				users:null,
+				searchs:[],
+				inputFocus:true,
+				showNoData:true
+			};
+		},
+		onLoad() {
+			this.searchs=uni.getStorageSync('searchHistory')||[];
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			userInfo(){
+				return this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))
+			}
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			search(){
+				if(this.inputText===null){
+					this.users=null;
+					return;
+				}
+				if(this.inputText===''){
+					this.users=null;
+					this.inputText=null;
+					return;
+				}
+				this.searchs=uni.getStorageSync('searchHistory')||[];
+				if(this.searchs.includes(this.inputText)){
+					this.searchs.splice(this.searchs.indexOf(this.inputText),1);
+				}
+				if(this.searchs.length>=10){
+					this.searchs.shift();
+				}
+				this.searchs.unshift(this.inputText);
+				uni.setStorageSync('searchHistory',this.searchs);
+				let user=JSON.parse(uni.getStorageSync('user'))
+				this.searchOptions.listType=user.sex==='male'?'MaleReco':'FaMaleReco';
+				this.searchOptions.geo.lat=this.$store.state.latitude;
+				this.searchOptions.geo.lon=this.$store.state.longitude;
+				this.searchOptions.prefer.nick=this.inputText;
+				this.searchOptions.completeUser=user;
+				this.$api.public.search(this.searchOptions).then(res=>{
+					if(res.status==='Succ'){
+						this.users=res.data.users;
+						this.showNoData=this.users.length===0?true:false;
+					}
+				})
+			},
+			cancel(){
+				this.users=null;
+				this.inputText=null;
+			},
+			chooseHisItem(index){
+				this.inputText=this.searchs[index];
+				this.$nextTick(()=>{
+					this.inputFocus=true;
+				})
+				this.search();
+				
+			},
+			closeHisItem(index){
+				this.searchs.splice(index,1);
+				uni.setStorageSync('searchHistory',this.searchs);
+			},
+			clearAll(){
+				uni.setStorageSync('searchHistory',[]);
+				this.searchs=[]
+			},
+			toDetail(id){
+				uni.showLoading({})
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.userDetail({getAlbum:true,completeUser:user,uponUserId:id}).then(res=>{
+					if(res.data.frozen){
+						uni.showToast({
+							title:'该用户已被冻结',
+							icon:'none'
+						});
+						return;
+					}
+					if(res.data.sex===user.sex){
+						uni.showToast({
+							title:'同性用户不能查看主页',
+							icon:'none'
+						})
+					}
+					else{
+						this.otherInfo=res.data;
+						uni.hideLoading();
+						uni.navigateTo({
+							url:`/pages/friends/user?id=${id}`
+						})
+					}
+				})
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container {
+		width: 100vw;
+		min-height: 100vh;
+		background-color: $bgcolor1;
+		overflow: hidden;
+		.topnav {
+			margin: 0 32rpx;
+			width: 100vw;
+			align-items: center;
+			position: fixed;
+			background-color: $bgcolor1;
+			z-index: 11;
+			padding-bottom: 10rpx;
+			.back{
+				width: 40rpx;
+				height: 40rpx;
+				margin-right: 16rpx;
+			}
+		}
+		.search-box{
+			margin: 0 32rpx;
+			height: 96rpx;
+			width: 686rpx;
+			align-items: center;
+			position: fixed;
+			background-color: $bgcolor1;
+			z-index: 10;
+			padding-bottom: 10rpx;
+			
+			.input-box{
+				flex: 1;
+				margin: 20rpx 0rpx;
+				height: 80rpx;
+				background-color: #332D4C;
+				padding: 0rpx 24rpx;
+				border-radius: 40rpx;
+				.search-img{
+					width: 40rpx;
+					height: 40rpx;
+				}
+				.search-input{
+					flex: 1;
+					color: $fontcolor5;
+					margin-left: 5rpx;
+				}
+			}
+			.cancel{
+				color: #D4D4F1;
+				margin-left: 16rpx;
+			}
+		}
+		.his-input{
+			margin: 0 32rpx;
+			margin-top: 24rpx;
+			.his-item{
+				padding: 28rpx 0rpx;
+				.his-left{
+					flex: 1;
+					.his-icon{
+						width: 40rpx;
+						height: 40rpx;
+					}
+					.his-text{
+						color: #D4D4F1;
+						margin-left: 16rpx;
+					}
+				}
+				.his-close{
+					width: 32rpx;
+					height: 32rpx;
+					padding: 8rpx;
+				}
+			}
+			.tip{
+				color: #D4D4F1;
+				text-align: center;
+				text-decoration: underline;
+			}
+		}
+		.list-item{
+			margin: 0 32rpx;
+			flex-wrap: wrap;
+			padding: 40rpx 0rpx;
+			.list-head-box{
+				width: 136rpx;
+				height: 136rpx;
+				position: relative;
+				.list-head-img{
+					width: 136rpx;
+					height: 136rpx;
+					background-color: #ffffff;
+					border-radius: 136rpx;
+				}
+				.list-head-dot{
+					width: 24rpx;
+					height: 24rpx;
+					border-radius: 24rpx;
+					position: absolute;
+					background-color: aqua;
+					bottom: 2rpx;
+					right: 2rpx;
+					border: 2rpx solid $bgcolor2;
+				}
+			}
+			.list-info-box{
+				box-sizing: border-box;
+				width: 544rpx;
+				padding-left: 32rpx;
+				height: 136rpx;
+				flex-direction: column;
+				display: flex;
+				justify-content: flex-start;
+				align-items: flex-start;
+				.name-box{
+					width: 100%;
+					.name{
+						.name-text{
+							color: $fontcolor5;
+						}
+						.name-img{
+							width: 68rpx;
+							height: 32rpx;
+							margin-left: 8rpx;
+							
+						}
+						.name-img-godness{
+							width: 76rpx;
+							height: 40rpx;
+							margin-left: 8rpx;
+							transform: translateY(-5rpx);
+						}
+					}
+					.distance{
+						color: $fontcolor3;
+					}
+				}
+				.sex-box{
+					padding: 0 15rpx;
+					height: 32rpx;
+					background: rgba(226, 53, 104, 0.2);
+					border-radius: 25rpx;
+					margin-top: 12rpx;
+					.sex-img{
+						width: 24rpx;
+						height: 24rpx;
+					}
+					.sex-text1{
+						color:#E23568;
+						margin-left: 4rpx;
+					}
+					.sex-text2{
+						color:#6C52F4;
+						margin-left: 4rpx;
+					}
+				}
+				.tip-box{
+					color: $fontcolor3;
+					width: 100%;
+					margin-top: 10rpx;
+				}
+				
+			}
+			.img-box{
+				width: 544rpx;
+				height: 168rpx;
+				margin-left: 168rpx;
+				margin-top: 24rpx;
+				.ib1{
+					width: 168rpx;
+					height: 168rpx;
+					border-radius: 16rpx 0rpx 0rpx 16rpx;
+					background: $fontcolor5;
+				}
+				.ib2{
+					width: 168rpx;
+					height: 168rpx;
+					background: $fontcolor5;
+				}
+				.ib3{
+					width: 168rpx;
+					height: 168rpx;
+					border-radius: 0rpx 16rpx 16rpx 0rpx;
+					background: $fontcolor5;
+				}
+			}
+		}
+}
+</style>

+ 494 - 0
pages/vip/vip.vue

@@ -0,0 +1,494 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-between"  :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-text font32 fw600">
+				{{topbarTitle}}
+			</view>
+			<view class="nav-item"></view>
+		</view>
+		<scroll-view
+		scroll-y="true" 
+		:style="{'height': `${scrollHeight}px`,'padding-top':`${topNavHeight}px`}"
+		v-if="scrollHeight>0"
+		lower-threshold="200"
+		refresher-enabled="true"
+		:refresher-triggered="scrollTriggered"
+		:refresher-threshold="45" 
+		refresher-default-style="white"
+		refresher-background="#151126" 
+		@refresherrefresh="scrollRefresh" 
+		@refresherpulling="scrollPulling"
+		@refresherrestore="scrollRestore" 
+		@refresherabort="scrollAbort"
+		@scrolltolower="scrollToBottom"
+		class="scroll-view"
+		>
+			<view class="top flex-between">
+				<view class="top-left">
+					<view class="user-box flex-start">
+						<image :src="userInfo.iconThumbnail" mode="aspectFit" class="head-img"></image>
+						<view class="user-info flex-center">
+							<view class="name font32 fw600">
+								{{userInfo.nick}}
+							</view>
+							<view class="phone font22 fw400">
+								{{userInfo.phone}}
+							</view>
+						</view>
+					</view>
+					<view class="tip font28 fw400">
+						开通会员享更多惊喜特权
+					</view>
+				</view>
+				<image :src="`${assetsUrl}vip-bg.png`" mode="aspectFit" class="top-right"></image>
+			</view>
+			<scroll-view scroll-x="true" class="tabs flex-start">
+				<view class="tab" :class="tabIndex===index?'active-tab':'no-active'" v-for="(item,index) in priceDatas.prices2" :key="index" @click="tabClick(index)">
+					<view class="tab-time font28 fw600">
+						{{item.name}}
+					</view>
+					<view class="tab-price">
+						<span style="font-size: 72rpx;">{{item.priceFenPerMonth/100}}</span><span class="font22 fw500">元/月</span>
+					</view>
+					<view class="tab-under font24 fw400">
+						{{item.originalPriceFenPerMonth/100}}元/月
+					</view>
+					<view class="tab-tag font20 fw600" v-if="tabIndex===index">
+						{{item.tags}}
+					</view>
+				</view>
+			</scroll-view>
+			<swiper :indicator-dots="false" :disable-touch="true" :vertical="true" :circular="true" :autoplay="true" :interval="2000" :duration="500" class="swiper">
+				<view class="swiper-model" @touchmove.stop="catchTouchMove">
+					<swiper-item class="swiper-item flex-start" v-for="(item,index) in promotions" :key="index" >
+						<view class="item-box flex-start">
+							<image :src="item.headImg" mode="aspectFit" class="item-img"></image>
+							<view class="item-text font24 fw400">
+								{{item.nick}}刚刚开通了会员
+							</view>
+						</view>
+					</swiper-item>
+				</view>
+				
+			</swiper>
+			<view class="prot font22 fw400">
+				成为会员及代表同意<span style="color: #FFDDBD;" @click="toAddedService">《增值服务协议》</span>
+			</view>
+			<image :src="`${assetsUrl}vip-action.png`" mode="widthFix" class="action"></image>
+			<view class="qustion-title font30 fw500">
+				支付遇到问题
+			</view>
+			<view class="qustions font24 fw400">
+				1.如果支付成功,但是没有开通VIP,请<span style="color: #FFDDBD; margin-left: 4rpx;" @click="toService">联系客服</span>
+			</view>
+			
+		</scroll-view>
+		<view class="btn flex-between">
+			<view class="btn-left">
+				<view class="bl-total font24 fw500">
+					合计:<span style="font-size:24rpx;font-weight: bold;color: #FFDDBD;margin-left: 4rpx;">¥</span><span style="font-size:40rpx;font-weight: bold;color: #FFDDBD;">{{totalPrice/100}}</span>
+				</view>
+<!-- 				<view class="bl-tip font20 fw400">
+					每月自动续费
+				</view> -->
+			</view>
+			<view class="btn-right" @click="pay">
+				<image :src="`${assetsUrl}vip-btn.png`" mode="aspectFit" class="br-img"></image>
+				<view class="br-text font32 fw600">
+					立即支付
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import TopBar from '@/components/TopBar/TopBar.vue';
+	export default {
+		components:{TopBar},
+		data() {
+			return {
+				topbarIcon:'back',
+				topbarTitle:'会员中心',
+				assetsUrl:this.$util.assetsUrl,
+				scrollRefreshing:false,
+				scrollTriggered:true,
+				scrollHeight:0,
+				topNavHeight:0,
+				tabIndex:0,
+				priceDatas:{},
+				promotions:[],
+				totalPrice:0,
+				priceConfig:{
+					feeFen: null,
+					sceneId: null,
+					scene: null,
+					body : null,
+					userId: null,
+					pkgCate:'JyPark'
+				}
+			};
+		},
+		mounted() {
+			this.computedScollviewHeight();
+			this.getConfigData();
+			this.getPromotionsData();
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			userInfo(){
+				return this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))
+			}
+		},
+		methods:{
+			catchTouchMove(){
+				return false;
+			},
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			toAddedService(){
+				uni.navigateTo({
+					url:`/pages/webview/webview?url=${this.$util.protocal.addedService}`
+				})
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.select('#topnav').boundingClientRect(data => {
+					this.topNavHeight=data.height;
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			scrollRefresh(){
+				if (this.scrollRefreshing) 
+				{
+					return;
+				}
+				this.scrollRefreshing = true;
+				setTimeout(() => {
+					this.scrollTriggered = false;
+					this.scrollRefreshing = false;
+				}, 1000)
+				this.getConfigData();
+			},
+			scrollPulling(e) {},
+			scrollRestore() {this.scrollTriggered = true;},
+			scrollAbort() {},
+			scrollToBottom(){},
+			getConfigData(){
+				let user=JSON.parse(uni.getStorageSync('user'));
+				this.$api.public.priceBySceneConfigs({
+					completeUser:user,
+					scene:'Member'
+				}).then(res=>{
+					this.priceDatas=res.data;
+					this.totalPrice=this.priceDatas.prices2[this.tabIndex].priceFen;
+					this.priceConfig.feeFen=this.priceDatas.prices2[this.tabIndex].priceFen;
+					this.priceConfig.scene=this.priceDatas.prices2[this.tabIndex].sceneCate;
+					this.priceConfig.sceneId=this.priceDatas.prices2[this.tabIndex].sceneId;
+					this.priceConfig.body=this.priceDatas.prices2[this.tabIndex].name;
+					this.priceConfig.userId=(this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))).id;
+				})
+			},
+			getPromotionsData(){
+				this.$api.public.vipPromotions({}).then(res=>{
+					this.promotions=res.data;
+				})
+			},
+			tabClick(index){
+				this.tabIndex=index;
+				this.totalPrice=this.priceDatas.prices2[index].priceFen;
+				this.priceConfig.feeFen=this.priceDatas.prices2[index].priceFen;
+				this.priceConfig.scene=this.priceDatas.prices2[index].sceneCate;
+				this.priceConfig.sceneId=this.priceDatas.prices2[index].sceneId;
+				this.priceConfig.body=this.priceDatas.prices2[index].name;
+				this.priceConfig.userId=(this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))).id;
+			},
+			pay(){
+				uni.getProvider({
+					service:'payment',
+					success:(provider)=>{
+						console.log(provider.provider)
+						this.$api.pay.creatWxOrder(this.priceConfig).then(res=>{
+							if(res.data.succ){
+								uni.requestPayment({
+									provider:provider.provider[0],
+									timeStamp: String(res.data.timeStamp),
+								    nonceStr: res.data.nonceStr,
+								    package: `prepay_id=${res.data.wxUniPrePayId}`,
+								    signType: 'MD5',
+								    paySign: res.data.paySign,
+								    success:(result)=>{
+										console.log(result)
+										if(result.errMsg==='requestPayment:ok'){
+											uni.showToast({
+												icon:'success',
+												title:'支付成功'
+											})
+										}
+									},
+								    fail:(err)=>{console.log(err)}
+								})
+							}
+						})
+					}
+				})
+				
+			},
+			toService(){
+				uni.openCustomerServiceChat({
+				     extInfo:{
+				         url:'https://work.weixin.qq.com/kfid/kfca1b21d2f7e8a18e9',//客服链接
+				     },
+				     corpId:'wwa8f2a0d8a6dc0950',//企业ID
+				     fail(res){
+						 console.log(res)
+				         wx.showToast({
+				           title: '客服联系失败',
+				           icon:'none'
+				         })
+				     }
+				 })
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	overflow: hidden;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		background-color: $bgcolor1;
+		.nav-item{
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.nav-text{
+			flex: 1;
+			color: $fontcolor5;
+			height: 40rpx;
+			text-align: center;
+		}
+	}
+	.scroll-view{
+		
+		.top{
+			.top-left{
+				padding-left: 44rpx;
+				.user-box{
+					.head-img{
+						width: 88rpx;
+						height: 88rpx;
+						border-radius: 88rpx;
+						background-color: #ffffff;
+					}
+					.user-info{
+						flex-direction: column;
+						margin-left: 16rpx;
+						align-items: flex-start;
+						.name{
+							color: #ffffff;
+						}
+						.phone{
+							color: #FFDDBD;
+							margin-top: 10rpx;
+						}
+					}
+				}
+				.tip{
+					color: #79726B;
+					margin-top: 40rpx;
+				}
+			}
+			.top-right{
+				width: 300rpx;
+				height: 260rpx;
+			}
+		}
+		.tabs{
+			white-space:nowrap;
+			.tab{
+				position: relative;
+				margin-right: 12rpx;
+				
+				border-radius: 40rpx;
+				display: inline-block;
+				transition: all .1s;
+				overflow: hidden;
+				&:nth-of-type(1){
+					margin-left: 32rpx;
+				}
+				&:nth-last-of-type(1){
+					margin-right: 32rpx;
+				}
+				.tab-time{
+					color: #FFDDBD;
+				}
+				.tab-price{
+					color: #FFDDBD;
+					margin-top: 24rpx;
+				}
+				.tab-under{
+					color: #FFDDBD;
+					text-decoration: line-through;
+					margin-top: 24rpx;
+				}
+				.tab-tag{
+					color: #201F1E;
+					background: #FFDDBD;
+					border-radius: 0rpx 40rpx 0px 16rpx;
+					width: 88rpx;
+					height: 36rpx;
+					line-height: 36rpx;
+					text-align: center;
+					position: absolute;
+					right: -1rpx;
+					top: -1rpx;
+				}
+			}
+			.no-active{
+				border: 1rpx solid #79726B;
+				padding: 40rpx 24rpx;
+				width: 188rpx;
+			}
+			.active-tab{
+				padding: 40rpx 24rpx;
+				width: 182rpx;
+				border: 4rpx solid #FFDDBD !important;
+				background-color: #221F1C !important;
+			}
+		}
+		.swiper{
+			margin: 48rpx 32rpx 16rpx 32rpx;
+			height: 80rpx;
+			position: relative;
+			.swiper-model{
+				position: absolute;
+				z-index: 100;
+				width: 100%;
+				height: 80rpx;
+			}
+			.swiper-item{
+				height: 80rpx;
+				.item-box{
+					padding: 20rpx 24rpx;
+					height: 80rpx;
+					.item-img{
+						width: 40rpx;
+						height: 40rpx;
+						border-radius: 40rpx;
+						background-color: #ffffff;
+					}
+					.item-text{
+						color: #ffffff;
+						margin-left: 16rpx;
+					}
+				}
+			}
+		}
+		.prot{
+			margin: 0 32rpx;
+			color: #ffffff;
+		}
+		.action{
+			margin: 64rpx 32rpx;
+			width: 686rpx;
+		}
+		.qustion-title{
+			color: #ffffff;
+			margin: 0 32rpx;
+		}
+		.qustions{
+			color: #79726B;
+			margin: 20rpx 32rpx 80rpx 32rpx;
+			padding-bottom: 284rpx;
+		}
+	}
+	
+	.btn{
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 100;
+		width: 686rpx;
+		height: 128rpx;
+		margin: 0rpx 32rpx;
+		margin-bottom: 76rpx;
+		background: linear-gradient(133deg, #1B1A1D 0%, #000000 100%);
+		border-radius: 64rpx;
+		border: 2rpx solid #979797;
+		.btn-left{
+			padding-left:48rpx;
+			.bl-total{
+				color: #ffffff;
+			}
+			.bl-tip{
+				color: #79726B;
+			}
+		}
+		.btn-right{
+			position: relative;
+			width: 208rpx;
+			height: 96rpx;
+			padding-right: 20rpx;
+			
+			.br-img{
+				position: absolute;
+				left: 0;
+				right: 0;
+				top: 0;
+				bottom: 0;
+				margin: 0 auto;
+				width: 208rpx;
+				height: 96rpx;
+				z-index: 0;
+			}
+			.br-text{
+				color: #000000;
+				position: relative;
+				z-index: 1;
+				width: 208rpx;
+				height: 96rpx;
+				text-align: center;
+				line-height: 96rpx;
+			}
+		}
+	}
+}
+</style>

+ 36 - 0
pages/wallet/wallet.vue

@@ -0,0 +1,36 @@
+<template>
+	<view>
+		<web-view @message="getMessage" :src="url"></web-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				url:null,
+				title:null
+			};
+		},
+		onLoad() {
+			let h5Login={
+				platform:'wxMiniprogram',
+				ukn:encodeURIComponent(uni.getStorageSync('LL_Ukn')).replace(/\+/g,'%2B') ,
+				token:uni.getStorageSync('token'),
+				userinfo:JSON.parse(uni.getStorageSync('userInfo')),
+				user:JSON.parse(uni.getStorageSync('user'))
+			}
+			let str=`https://promote.sugarpark.cn/walletHome?h5loginstr=${JSON.stringify(h5Login)}`
+			this.url=str;
+		},
+		methods:{
+			getMessage(e){
+				console.log(e);
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 22 - 0
pages/webview/webview.vue

@@ -0,0 +1,22 @@
+<template>
+	<view>
+		<web-view :src="link"></web-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				link:''
+			};
+		},
+		onLoad(options) {
+			this.link=options.url;
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 533 - 0
pagesSub/chatting/chatting.vue

@@ -0,0 +1,533 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-center flex-center">
+				<view class="nav-text font32 fw600">
+					{{userInfo.nick}}
+				</view>
+				<view class="nav-tip font20 fw400" >
+					<text></text>{{onlineState}} · {{userInfo.distance}}
+				</view>
+			</view>
+			
+			<view class="nav-item"></view>
+		</view>
+		<view class="message-list" :style="{'height': `${scrollHeight}px`,'margin-top':`${topbarOffsetHeight}px`}">
+			<TUI-message-list id="message-list" ref="messageList" :conversation="conversation" :scrollHeight="scrollHeight" :topbarOffsetHeight="topbarOffsetHeight" />
+		</view>
+		<view class="talk-box" id="talk-box" @touchmove="prevent()">
+			<view class="input-box flex-between">
+				<input type="text" class="input" confirm-type="send" placeholder="请输入消息…" v-model="inputText" placeholder-style="color:#7D7DA4 ;font-size:24rpx" @confirm="sendTextMessage">
+				<view class="input-btn font22 fw400" @click="sendTextMessage">
+					发送
+				</view>
+			</view>
+			<view class="action-box flex-between">
+				<image :src="`${assetsUrl}talk-voice.png`" mode="aspectFit" class="act-img" @click="showActionPanel(0)"></image>
+				<image :src="`${assetsUrl}talk-pic.png`" mode="aspectFit" class="act-img" @click="showActionPanel(1)"></image>
+				<image :src="`${assetsUrl}talk-video.png`" mode="aspectFit" class="act-img" @click="showActionPanel(2)"></image>
+				<image :src="`${assetsUrl}talk-emo.png`" mode="aspectFit" class="act-img" @click="showActionPanel(3)"></image>
+			</view>
+			<view class="action-panel" :style="{'padding-bottom':`${showActionIndex===-1?'68rpx':'20rpx'}`}">
+				<view class="voice-panel flex-center" v-if="showActionIndex===0">
+					<view class="voice-text font22 fw400">
+						{{voiceText}}
+					</view>
+					<image :src="`${assetsUrl}talk-voice-${isRecording?'delete':'open'}.png`" mode="aspectFill" class="voice-img" @longpress="handleLongPress" @touchmove="handleTouchMove" @touchend="handleTouchEnd"></image>
+				</view>
+				<view class="emoji-box" v-if="showActionIndex===3">
+					<TUI-Emoji @enterEmoji="appendMessage"></TUI-Emoji>
+				</view>
+			</view>
+		</view>
+		<view v-if="videoPlay" class="container-box" @tap.stop="stopVideoHander">
+			<video
+				v-if="videoPlay"
+				class="video-message"
+				:src="videoMessage.payload.videoUrl||videoMessage.videoUrl"
+				:poster="videoMessage.payload.thumbUrl||videoMessage.videoCover"
+				object-fit="cover"
+				error="videoError"
+				autoplay="true"
+				direction="0"
+				show-fullscreen-btn="false"
+				:style="{'width':`${videoMessage.payload.thumbWidth||videoMessage.width}px`,'height':`${videoMessage.payload.thumbHeight||videoMessage.height}px`}"
+			/>
+		</view>
+	</view>
+</template>
+
+<script>
+	import TUIEmoji from '../../components/tui-chat/message-elements/emoji/index';
+	import TUIMessageList from '../../components/tui-chat/message-list/index';
+	
+	export default {
+		components: {
+			TUIMessageList,
+			TUIEmoji
+		},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				scrollHeight:0,
+				topNavHeight:0,
+				userInfo:null,
+				messageList:[],
+				message:null,//消息实例
+				onlineState:'',
+				scrollRefreshing:false,
+				scrollTriggered:true,
+				conversation:'',
+				conversationID: '',
+				inputText:'',
+				showSendBtn:false,
+				showActionIndex:-1,
+				recorderManager:null,
+				voiceText:'按住说话,松手发送',
+				recordTimeTotal:60,//最大长度,30秒
+				isRecording:false,
+				canSend:false,
+				startPoint:0,
+				videoPlay: false,
+				videoMessage: {},
+				
+				
+			}
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			}
+		},
+		created() {
+			uni.$on('videoPlayerHandler', value => {
+				this.videoPlay = value.isPlay;
+				this.videoMessage = value.message;
+			});
+			uni.authorize({
+				scope:'scope.record',
+				success:()=>{
+					this.startRecordManage();
+				},
+				fail:err=>{
+					uni.showModal({
+						content: '检测到您没打开录音功能权限,是否去设置打开?',
+						confirmText: "确认",
+						cancelText: '取消',
+						success:res=>{
+							if(res.confirm){
+								uni.openSetting({
+									success:ress=>{
+										if(ress.authSetting){
+											this.startRecordManage();
+										}
+									}
+								})
+							}
+						}
+					})
+				}
+			})
+		},
+		onLoad(options) {
+			this.conversationID=options.conversationid;
+			uni.$TUIKit.setMessageRead({
+				conversationID:this.conversationID
+			})
+			uni.$TUIKit.getConversationProfile(this.conversationID).then(res => {
+				const { conversation } = res.data;
+				this.conversation = conversation;
+			});
+			let pages=getCurrentPages();
+			let prePage=pages[pages.length-2];
+			this.userInfo=prePage.$vm.userInfo;
+			
+		},
+		mounted() {
+			
+			if(this.userInfo.lastActiveTime===0||this.userInfo.online){
+				this.onlineState='在线';
+			}
+			else if(this.userInfo.lastActiveTime<30&&!this.userInfo.online){
+				this.onlineState='刚刚';
+			}
+			else{
+				this.onlineState='离线';
+			}
+			setTimeout(()=>{
+				this.computedScollviewHeight();
+			},500)
+			
+			
+		},
+		methods: {
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			prevent(){
+				return false;
+			},
+			stopVideoHander() {
+				this.videoPlay = false;
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.selectAll('#topnav,#talk-box').boundingClientRect(data => {
+					data.forEach(item=>{
+						heightLeaf+=item.height;
+					})
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+					this.$refs.messageList.scrollToButtom();
+				});
+			
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			scrollRefresh(){
+				if (this.scrollRefreshing) 
+				{
+					return;
+				}
+				this.scrollRefreshing = true;
+				setTimeout(() => {
+					this.scrollTriggered = false;
+					this.scrollRefreshing = false;
+				}, 1000);
+				this.isLoadPreData=true;
+			
+			},
+			scrollPulling(e) {},
+			scrollRestore() {this.scrollTriggered = true;},
+			scrollAbort() {},
+			scrollToBottom(){},
+			showActionPanel(index){
+				if(this.showActionIndex===index){
+					this.showActionIndex=-1;
+					setTimeout(()=>{
+						this.computedScollviewHeight();
+					},50)
+					return;
+				}
+				
+				switch(index){
+					case 0:
+						this.showActionIndex=index;
+						setTimeout(()=>{
+							this.computedScollviewHeight();
+						},50)
+					break;
+					case 1:
+						this.showActionIndex=-1;
+						uni.chooseImage({
+							count: 1,
+							sizeType: ['original', 'compressed'],
+							sourceType: ['album', 'camera'],
+							success:res=>{
+							    console.log(res)
+								const message = uni.$TUIKit.createImageMessage({
+									to: String(this.userInfo.id),
+									conversationType: this.conversation.type,
+									payload: {
+										file: res
+									},
+									onProgress:event=>{}
+								});
+								this.$sendTIMMessage(message);
+							}
+						})
+					break;
+					case 2:
+						uni.chooseVideo({
+							sourceType: ['album', 'camera'],
+							maxDuration: 60,
+							camera: 'back',
+							success:res=>{
+								const message = uni.$TUIKit.createVideoMessage({
+									to: String(this.userInfo.id),
+									conversationType: this.conversation.type,
+									payload: {
+										file: res
+									},
+									onProgress:event=>{}
+								});
+								this.$sendTIMMessage(message);
+							}
+						})
+					break;
+					case 3:
+						this.showActionIndex=index;
+						setTimeout(()=>{
+							this.computedScollviewHeight();
+						},50)
+					break;
+				}
+				
+			},
+			appendMessage(e) {
+				this.inputText+=e.detail.message;
+			},
+			startRecordManage(){
+				// 加载声音录制管理器
+				this.recorderManager = uni.getRecorderManager();
+				console.log(this.recorderManager)
+				this.recorderManager.onStop(res => {
+					console.log(res)
+					clearInterval(this.recordTimer);
+					// 兼容 uniapp 打包app,duration 和 fileSize 需要用户自己补充
+					// 文件大小 = (音频码率) x 时间长度(单位:秒) / 8
+					let msg = {
+						duration: res.duration ? res.duration : this.recordTime * 1000,
+						tempFilePath: res.tempFilePath,
+						fileSize: res.fileSize ? res.fileSize : ((48 * this.recordTime) / 8) * 1024
+					};
+					uni.hideLoading();
+					// 兼容 uniapp 语音消息没有duration
+					if (this.canSend) {
+						if (msg.duration < 1000) {
+							uni.showToast({
+								title: '录音时间太短',
+								icon: 'none'
+							});
+						} else {
+							// res.tempFilePath 存储录音文件的临时路径
+							const message = uni.$TUIKit.createAudioMessage({
+								to: String(this.userInfo.id),
+								conversationType: this.conversation.type,
+								payload: {
+									file: msg
+								}
+							});
+							this.$sendTIMMessage(message);
+						}
+					}
+					this.startPoint=0;
+					this.isRecording=false;
+					this.canSend=true;
+					this.voiceText='按住说话,松手发送';
+				});
+				this.recorderManager.onError(err=>{
+					console.log(err)
+				})
+			},
+			handleLongPress(e) {
+				uni.vibrateShort();
+				this.recorderManager.start({
+					duration: 60000,
+					// 录音的时长,单位 ms,最大值 600000(10 分钟)
+					sampleRate: 44100,
+					// 采样率
+					numberOfChannels: 1,
+					// 录音通道数
+					encodeBitRate: 192000,
+					// 编码码率
+					format: 'aac' // 音频格式,选择此格式创建的音频消息,可以在即时通信 IM 全平台(Android、iOS、微信小程序和Web)互通
+				});
+				this.startPoint=e.touches[0];
+				this.voiceText='正在录音,上划可取消';
+				this.isRecording=true;
+				this.recordTime=0;
+				this.recordTimer = setInterval(() => {
+					this.recordTime++;
+					if(this.recorTime>=this.recordTimeTotal){
+						this.recorderManager.stop();
+						clearInterval(this.recordTimer);
+						this.recordTimer=null;
+					}
+				}, 1000);
+			},
+			
+			// 录音时的手势上划移动距离对应文案变化
+			handleTouchMove(e) {
+				if (this.isRecording) {
+					if (this.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 100) {
+						this.voiceText='松开手指,取消发送';
+						this.canSend=false;
+					} else if (this.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 20) {
+						this.voiceText='上划可取消';
+						this.canSend=true;
+					} else {
+						this.voiceText='抬起停止';
+						this.canSend=true;
+					}
+				}
+			},
+			
+			// 手指离开页面滑动
+			handleTouchEnd() {
+				this.isRecording=false;
+				this.voiceText='按住说话,松手发送';
+				uni.hideLoading();
+				this.recorderManager.stop();
+			},
+			sendTextMessage() {
+				const to = String(this.userInfo.id);
+				const text =this.inputText;
+				const message = uni.$TUIKit.createTextMessage({
+					to,
+					conversationType: this.conversation.type,
+					payload: {
+						text
+					}
+				});
+				this.inputText='';
+				this.$sendTIMMessage(message);
+			},
+			$sendTIMMessage(message) {
+				this.$EventBus.$emit('sendMessage', message)
+				uni.$TUIKit.sendMessage(message).then((res) => {
+					this.$refs.messageList.scrollToButtom();
+				}).catch((error) => {
+					console.log(error)
+				})
+			},
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	overflow: hidden;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		background-color: $bgcolor1;
+		.nav-item{
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.nav-center{
+			flex: 1;
+			flex-direction: column;
+			.nav-text{
+				
+				color: $fontcolor5;
+				height: 40rpx;
+				text-align: center;
+			}
+			.nav-tip{
+				color:$fontcolor2;
+			}
+		}
+		
+	}
+	.talk-box{
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100vw;
+		padding-top: 16rpx;
+		background-color: $bgcolor1;
+		.input-box{
+			margin: 0rpx 32rpx;
+			height: 80rpx;
+			border-radius: 80rpx;
+			background-color: $bgcolor4;
+			padding: 0 24rpx;
+			.input{
+				width: 100%;
+				height: 100%;
+				color: #ffffff;
+				font-size: 22rpx;
+			}
+			.input-btn{
+				background-color: $primary;
+				color: #ffffff;
+				border-radius: 16rpx;
+				height: 56rpx;
+				line-height: 56rpx;
+				width: 120rpx;
+				text-align: center;
+				
+			}
+		}
+		.action-box{
+			padding: 0rpx 0rpx 20rpx 0rpx;
+			margin: 0 90rpx;
+			margin-top: 36rpx;
+			.act-img{
+				width: 56rpx;
+				height: 56rpx;
+			}
+		}
+		.action-panel{
+			width: 100vw;
+			.voice-panel{
+				height: 400rpx;
+				flex-direction: column;
+				.voice-text{
+					color: #7D7DA4;
+					text-align: center;
+					
+				}
+				.voice-img{
+					width: 200rpx;
+					height: 200rpx;
+					margin-top: 40rpx;
+				}
+			}
+		}
+		.emoji-box{
+			padding: 0 11rpx;
+			box-sizing: border-box;
+			height: 400rpx;
+			flex-wrap: wrap;
+			transition: height .3s;
+			.emoji-item{
+				width: 64rpx;
+				height: 64rpx;
+				padding: 20rpx;
+				.emoji-img{
+					width: 64rpx;
+					height: 64rpx;
+				}
+			}
+		}
+	}
+	.container-box {
+		position: fixed;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		top: 0;
+		background-color: rgba(0, 0, 0, 0.5);
+		.video-message {
+			width: 90vw;
+			height: auto;
+		}
+	}
+}
+</style>
+

+ 217 - 0
pagesSub/faceVideo/faceVideo.vue

@@ -0,0 +1,217 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-between"  :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-text font32 fw600">
+				{{topbarTitle}}
+			</view>
+			<view class="nav-item"></view>
+		</view>
+		<view class="upload-box flex-center" @click="chooseMedia">
+			<image :src="`${assetsUrl}mine-camera.png`" mode="widthFix" class="upload-img"></image>
+			<view class="upload-text font28 fw400">选择视频</view>
+		</view>
+		<view class="tip font22 fw400">
+			点击添加你的封面视频
+		</view>
+	</view>
+</template>
+
+<script>
+	import {getPolicy,computeSignature,getKey} from '@/util/oss.js';
+	import {encode} from '@/util/base64.js'
+	export default {
+		data() {
+			return {
+				topbarIcon:'back',
+				topbarTitle:'编辑封面视频',
+				assetsUrl:this.$util.assetsUrl,
+				topNavHeight:0,
+				options:{
+					completeUser:null,
+					pageHomeUserId:null,
+					personalPageHomeVideoOperateEnum:'UPLOAD',
+					personalPageHomeVideoUrl: "",
+					sizeMB: 0,
+					vdoTime: 0,
+					width: 0,
+					height:0
+				}
+			};
+		},
+		mounted() {
+			this.computedScollviewHeight();
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			userInfo(){
+				return this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))
+			}
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.select('#topnav').boundingClientRect(data => {
+					this.topNavHeight=data.height;
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			
+			},
+			chooseMedia(){
+				const that=this;
+				uni.chooseMedia({
+					count: 1,
+				    mediaType: ['video'],
+				    sourceType: ['album', 'camera'],
+				    maxDuration: 30,
+				    camera: 'back',
+				    success:(res)=>{
+						console.log(res.tempFiles[0]);
+						uni.showLoading({
+							title:'正在上传',
+							mask:true
+						})
+						const policyText=getPolicy();
+						const policy=encode(JSON.stringify(policyText));
+						const key=getKey(0,res.tempFiles[0].tempFilePath.split('.')[1]);
+						that.$api.public.aliossToken({}).then(resuslt=>{
+							let formData={
+								key:key,
+								policy:policy,
+								OSSAccessKeyId:resuslt.data.accessKeyId,
+								signature:computeSignature(resuslt.data.accessKeySecret,policy),
+								'x-oss-security-token':resuslt.data.securityToken,
+								success_action_status:'200'
+							}
+							uni.uploadFile({
+								url: 'https://zhenyanapp-gen.oss-cn-qingdao.aliyuncs.com',
+								filePath:res.tempFiles[0].tempFilePath,
+								name: 'file',
+								header:{
+									"Content-Type": "multipart/form-data"
+								},
+								formData: formData,
+								success: (data) => {
+								    if (data.statusCode === 200) {
+									  let videoUrl=`${that.$store.state.videoCdn}/${key}`;
+									  let user=JSON.parse(uni.getStorageSync('user'));
+									  that.options.height=res.tempFiles[0].height;
+									  that.options.width=res.tempFiles[0].width;
+									  that.options.completeUser=user;
+									  that.options.pageHomeUserId=user.id;
+									  that.options.sizeMB=res.tempFiles[0].size/1024/1024;
+									  that.options.personalPageHomeVideoUrl=videoUrl;
+									  that.options.vdoTime=res.tempFiles[0].duration;
+									  that.$api.public.videoProcess(that.options).then(res=>{
+										  uni.hideLoading()
+										  if(res.status==='Succ'){
+											  
+											  uni.showToast({
+											  	icon:'success',
+												title:'上传成功'
+											  });
+											  setTimeout(()=>{
+												  uni.navigateBack({
+												  	delta:1
+												  })
+											  },1500)
+										  }
+										  else{
+											 uni.showToast({
+											 	icon:'none',
+											 	title:'上传失败,请重试!'
+											 }); 
+										  }
+									  })
+								    }
+								},
+								fail: err => {
+								    console.log(err);
+								  }
+							})
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	overflow: hidden;
+	position: relative;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		background-color: $bgcolor1;
+		.nav-item{
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.nav-text{
+			flex: 1;
+			color: $fontcolor5;
+			height: 40rpx;
+			text-align: center;
+		}
+	}
+	.upload-box{
+		flex-direction: column;
+		width: 360rpx;
+		height: 360rpx;
+		background: #1F1A30;
+		border-radius: 16rpx;
+		position: absolute;
+		left: 0;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		margin: auto;
+		.upload-img{
+			width: 80rpx;
+			height: 80rpx;
+		}
+		.upload-text{
+			color: #ffffff;
+			margin-top: 8rpx;
+		}
+	}
+	.tip{
+		color: #7D7DA4;
+		margin-top: 40rpxs;
+	}
+}
+</style>

+ 144 - 0
pagesSub/setting/setting.vue

@@ -0,0 +1,144 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-between"  :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-text font32 fw600">
+				{{topbarTitle}}
+			</view>
+			<view class="nav-item"></view>
+		</view>
+		<view class="setting-box flex-between" :style="{'margin-top':`${topNavHeight+15}px`}">
+			<view class="setting-item flex-between" @click="loginOut">
+				<view class="setting-text font28 fw600">
+					退出登录
+				</view>
+				<image :src="`${assetsUrl}setting-more.png`" mode="widthFix" class="setting-icon"></image>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				topbarIcon:'back',
+				topbarTitle:'设置',
+				assetsUrl:this.$util.assetsUrl,
+				topNavHeight:0,
+			};
+		},
+		mounted() {
+			this.computedScollviewHeight();
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			},
+			userInfo(){
+				return this.$store.state.userInfo||JSON.parse(uni.getStorageSync('userInfo'))
+			}
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.select('#topnav').boundingClientRect(data => {
+					this.topNavHeight=data.height;
+					heightLeaf += data.height;
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+				});
+			
+			},
+			loginOut(){
+				uni.showModal({
+					title:'退出登录',
+					content:'退出登录后将清除所有本机数据,下次登录自动登录,是否继续?',
+					confirmColor:'#6C52F4',
+					confirmText:'继续',
+					success:(res)=>{
+						if(res.confirm){
+							uni.$TUIKit.logout();
+							uni.$TUIKit.destroy();
+							uni.setStorageSync('token','');
+							uni.setStorageSync('LL_Ukn','');
+							uni.setStorageSync('autoLogin','false');
+							uni.reLaunch({
+								url:'/pages/login/login'
+							})
+						}
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	overflow: hidden;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		background-color: $bgcolor1;
+		.nav-item{
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.nav-text{
+			flex: 1;
+			color: $fontcolor5;
+			height: 40rpx;
+			text-align: center;
+		}
+	}
+	.setting-box{
+		margin: 16rpx;
+		border-radius: 8rpx;
+		background-color: $bgcolor3;
+		
+		flex-direction: column;
+		.setting-item{
+			width: 100%;
+			height: 112rpx;
+			padding: 0rpx 32rpx;
+			box-sizing: border-box;
+			.setting-text{
+				color: $fontcolor5;
+			}
+			.setting-icon{
+				width: 24rpx;
+				height: 24rpx;
+			}
+		}
+	}
+}
+</style>

+ 763 - 0
pagesSub/talk/talk.vue

@@ -0,0 +1,763 @@
+<template>
+	<view class="container">
+		<view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
+			<view class="nav-item flex-center" @click="back">
+				<image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
+			</view>
+			<view class="nav-center flex-center">
+				<view class="nav-text font32 fw600">
+					{{userInfo.nick}}
+				</view>
+				<view class="nav-tip font20 fw400" >
+					{{onlineState}} · {{userInfo.distance}}
+				</view>
+			</view>
+			
+			<view class="nav-item"></view>
+		</view>
+		<scroll-view
+		class="scroll-view"
+		scroll-y="true" 
+		:style="{'height': `${scrollHeight}px`,'margin-top':`${topbarOffsetHeight}px`}"
+		v-if="scrollHeight>0"
+		lower-threshold="200"
+		:scroll-into-view="scrollTo"
+		refresher-enabled="true"
+		:refresher-triggered="scrollTriggered"
+		:refresher-threshold="45" 
+		refresher-default-style="white"
+		refresher-background="#151126" 
+		@refresherrefresh="scrollRefresh" 
+		@refresherpulling="scrollPulling"
+		@refresherrestore="scrollRestore" 
+		@refresherabort="scrollAbort"
+		@scrolltolower="scrollToBottom"
+		>
+			<!-- <view :id="startItem"></view>
+			<view class="msg-line" v-for="(item,index) in messageList" :key="index" v-if="item.hide!==true">
+				
+
+				<view class="line" v-if="item.flow==='in'">
+					<view class="time font20 fw400" v-if="item.showTime">
+						{{item.timeStr}}
+					</view>
+					<view class="msg-item flex-start" >
+						<view class="msg-head">
+							<image :src="item.avatar" mode="aspectFill" class="head-img"></image>
+						</view>
+						<view class="msg-text  font28 fw400">
+							<rich-text :nodes="item.payload.text"></rich-text>
+						</view>
+						
+						<view class="msg-tag  font22 fw400">
+							
+						</view>
+					</view>
+				</view>
+				<view class="line" v-if="(item.flow==='out'||item.payload.data.systemMsgType===10)&&item.payload.data.systemMsgType!==4">
+					<view class="time font20 fw400" v-if="item.showTime">
+						{{item.timeStr}}
+					</view>
+					<view class="msg-item flex-end" >
+						<view class="msg-tag font22 fw400" v-if="!item.payload.data">
+							{{item.isRead?'已读':'未读'}}
+						</view>
+						<image :src="`${assetsUrl}talk-payatention.png`" mode="aspectFill" class="err-icon" v-else></image>
+						<view class="msg-text  font28 fw400"  style="background-color: #6C52F4;border-radius: 16rpx 4rpx 16rpx 16rpx;" v-if="item.payload.text!==undefined">
+							<rich-text :nodes="item.payload.text"></rich-text>
+						</view>
+						<view class="msg-head">
+							<image :src="item.avatar" mode="aspectFill" class="head-img"></image>
+						</view>
+					</view>
+					
+				</view>
+				<view class="err-line flex-center" v-if="item.payload.data">
+					<text class="err-text font20 fw400">{{item.payload.data.context}}</text><text class="err-text font20 fw400" @click="toPath(item.payload.data.highlightLink)" :style="{'color':`${item.payload.data.highlightColor}`}">{{item.payload.data.highlightContext}}</text>
+				</view>
+			</view>
+			
+			<view :id="endItem"></view> -->
+			
+			<view id="message-scroll" style="width:100%">
+				<view class="no-message" v-if="isCompleted">没有更多啦</view>
+				<view v-for="item in messageList" :key="item.ID" class="t-message">
+					<view v-if="conversation.type !== '@TIM#SYSTEM'" :id="item.ID">
+						<view class="t-message-item">
+							<TUI-TipMessage v-if="item.type === 'TIMGroupTipElem'" :message="item"></TUI-TipMessage>
+							<view v-if="item.type !== 'TIMGroupTipElem'" :class="item.flow === 'out' ? 't-self-message' : 't-recieve-message'">
+								<image
+									class="t-message-avatar"
+									v-if="item.flow === 'in'"
+									:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+								></image>
+								<view class="read-receipts" v-if="conversation.type === 'C2C' && item.flow === 'out'">
+									<view v-if="item.isPeerRead">已读</view>
+									<view v-else>未读</view>
+								</view>
+								<view>
+									<TUI-TextMessage v-if="item.type === 'TIMTextElem'" :message="item" :isMine="item.flow === 'out'"></TUI-TextMessage>
+									<TUI-ImageMessage v-if="item.type === 'TIMImageElem'" :message="item" :isMine="item.flow === 'out'"></TUI-ImageMessage>
+									<TUI-VideoMessage v-if="item.type === 'TIMVideoFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-VideoMessage>
+									<TUI-AudioMessage v-if="item.type === 'TIMSoundElem'" :message="item" :isMine="item.flow === 'out'"></TUI-AudioMessage>
+									<TUI-CustomMessage v-if="item.type === 'TIMCustomElem'" :message="item" :isMine="item.flow === 'out'"></TUI-CustomMessage>
+									<TUI-FaceMessage v-if="item.type === 'TIMFaceElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FaceMessage>
+									<TUI-FileMessage v-if="item.type === 'TIMFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FileMessage>
+								</view>
+								<image
+									class="t-message-avatar"
+									v-if="item.flow === 'out'"
+									:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
+								></image>
+							</view>
+						</view>
+					</view>
+					<view v-else :id="item.ID" :data-value="item.ID"><TUI-SystemMessage :message="item"></TUI-SystemMessage></view>
+				</view>
+			</view>
+		</scroll-view>
+		<view class="message-input">
+			<TUI-message-input id="message-input" ref="messageInput" :conversation="conversation" @sendMessage="sendMessage" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import TIM from 'tim-wx-sdk';
+	import COS from 'cos-wx-sdk-v5';
+	import {emojiUrl,emojiMap,emojiName} from '@/util/emojiMap.js';
+	import TUITextMessage from '../message-elements/text-message/index';
+	import TUIImageMessage from '../message-elements/image-message/index';
+	import TUIVideoMessage from '../message-elements/video-message/index';
+	import TUIAudioMessage from '../message-elements/audio-message/index';
+	import TUICustomMessage from '../message-elements/custom-message/index';
+	import TUITipMessage from '../message-elements/tip-message/index';
+	import TUISystemMessage from '../message-elements/system-message/index';
+	import TUIFaceMessage from '../message-elements/face-message/index';
+	import TUIFileMessage from '../message-elements/file-message/index';
+	import TUIMessageInput from '../message-elements/message-input/index';
+	export default {
+		components: {
+			TUITextMessage,
+			TUIImageMessage,
+			TUIVideoMessage,
+			TUIAudioMessage,
+			TUICustomMessage,
+			TUITipMessage,
+			TUISystemMessage,
+			TUIFaceMessage,
+			TUIFileMessage,
+			TUIMessageInput
+		},
+		data() {
+			return {
+				assetsUrl:this.$util.assetsUrl,
+				scrollHeight:0,
+				topNavHeight:0,
+				inputText:'',
+				userInfo:null,
+				messageList:[],
+				message:null,//消息实例
+				onlineState:'',
+				scrollRefreshing:false,
+				scrollTriggered:true,
+				scrollTo:'',
+				startItem:'',
+				endItem:'',
+				nextReqMessageID:'',
+				isLoadPreData:false,//是否加载上一页历史记录
+				isCompleted:false,//历史记录是否已全部加载完成,
+				emojiUrl,
+				emojiMap,
+				emojiName,
+				showEmojis:false
+			};
+		},
+		computed: {
+			statusBarHeight() {
+				return this.$store.state.statusBarHeight;
+			},
+			topbarOffsetHeight() {
+				return this.$store.state.topbarOffsetHeight;
+			}
+		},
+		created() {
+			let pages=getCurrentPages();
+			let prePage=pages[pages.length-2];
+			this.userInfo=prePage.$vm.userInfo;
+			this.IMinit();
+		},
+		destroyed() {
+			console.log('destory')
+			uni.$TUIKit.logout();
+			uni.$TUIKit.destroy();
+		},
+		mounted() {
+			
+			if(this.userInfo.lastActiveTime===0||this.userInfo.online){
+				this.onlineState='在线';
+			}
+			else if(this.userInfo.lastActiveTime<30&&!this.userInfo.online){
+				this.onlineState='刚刚';
+			}
+			else{
+				this.onlineState='离线';
+			}
+			this.computedScollviewHeight();
+			
+			
+			
+		},
+		methods:{
+			back(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			/**
+			 * 计算scroll高度
+			 */
+			computedScollviewHeight() {
+				let query = uni.createSelectorQuery().in(this);
+				let heightLeaf =0;
+				query.selectAll('#topnav,#talk').boundingClientRect(data => {
+					data.forEach(item=>{
+						heightLeaf+=item.height;
+					})
+				}).exec(() => {
+					let sysInfo = uni.getSystemInfoSync();
+					this.scrollHeight = sysInfo.windowHeight - heightLeaf;
+					setTimeout(()=>{
+						this.endItem='view_id_' + parseInt(Math.random() * 1000000)
+						this.scrollTo=this.endItem;
+						console.log('滑动到底部了')
+					},1000)
+					
+				});
+			
+			},
+			/**
+			 * 推荐下拉刷新、加载更多
+			 */
+			scrollRefresh(){
+				if (this.scrollRefreshing) 
+				{
+					return;
+				}
+				this.scrollRefreshing = true;
+				setTimeout(() => {
+					this.scrollTriggered = false;
+					this.scrollRefreshing = false;
+				}, 1000);
+				this.isLoadPreData=true;
+				this.loadHistoryMessage();
+
+			},
+			scrollPulling(e) {},
+			scrollRestore() {this.scrollTriggered = true;},
+			scrollAbort() {},
+			scrollToBottom(){
+
+			},
+			toPath(path){
+				uni.navigateTo({
+					url:path
+				})
+			},
+			action(str){
+				switch(str){
+					case 'emo':
+						this.showEmojis=!this.showEmojis;
+						setTimeout(()=>{
+							this.computedScollviewHeight();
+							
+						},300)
+					break;
+				}
+				
+			},
+			emojiInput(index){
+				console.log(this.emojiName[index]);
+				this.inputText+=this.emojiName[index];
+			},
+			IMinit(){
+				/**
+				 * IM初始化
+				 */
+				const SDKAppID = 1400456480,that=this;
+				uni.$TUIKit = TIM.create({
+					SDKAppID: SDKAppID
+				});
+				uni.$TUIKitTIM = TIM;
+				uni.$TUIKitEvent = TIM.EVENT;
+				uni.$TUIKitVersion = TIM.VERSION;
+				uni.$TUIKitTypes = TIM.TYPES; // 监听系统级事件
+				uni.$resetLoginData = this.resetLoginData();
+				uni.$TUIKit.on(uni.$TUIKitEvent.SDK_READY, this.onSDKReady);
+				uni.$TUIKit.on(uni.$TUIKitEvent.SDK_NOT_READY, this.onSdkNotReady);
+				uni.$TUIKit.on(uni.$TUIKitEvent.KICKED_OUT, this.onKickedOut);
+				uni.$TUIKit.on(uni.$TUIKitEvent.ERROR, this.onTIMError);
+				uni.$TUIKit.on(uni.$TUIKitEvent.NET_STATE_CHANGE, this.onNetStateChange);
+				uni.$TUIKit.on(uni.$TUIKitEvent.SDK_RELOAD, this.onSDKReload);
+				
+			},
+			createMessageConversation(){
+				/**
+				 * 创建消息会话
+				 */
+				this.message=uni.$TUIKit.createTextMessage({
+					//to: that.userInfo.id,
+					to:'7421',
+					conversationType: 'C2C',
+					needReadReceipt: true,
+					payload:{
+						text:'tim'
+					}
+				})
+				this.loadHistoryMessage();
+			},
+			sendMessage(){
+				/**
+				 * 发送消息
+				 */
+				const that=this;
+				if(this.inputText===''){return;}
+				this.message.payload.text=this.inputText;
+				uni.$TUIKit.sendMessage(this.message).then(res=>{
+					console.log(res);
+					res.data.message.timeStr=this.$moment(res.data.message.time*1000).calendar(null,{
+						sameDay: '[今天] hh:mm',
+						lastDay: '[昨天] hh:mm',
+						sameElse: 'YYYY-MM-DD hh:mm:ss'
+					});
+					if(res.code===0){
+						// let allStr=res.data.message.payload.text;
+						// let allObj={
+						// 	name:'div',
+						// 	attrs:{
+						// 		class:'input',
+						// 		style:''
+						// 	},
+						// 	children:[]
+						// }
+						// let j=0;
+						// for(j=0;j<this.emojiName.length;j++){
+						// 	if(allStr.indexOf(this.emojiName[j])>-1){
+						// 		let imgurl=this.emojiUrl+this.emojiMap[this.emojiName[j]];
+						// 		let obj={
+						// 			name:'img',
+						// 			attrs:{
+						// 				src:imgurl,
+						// 				class:'',
+						// 				style:'width:20px;height:20px;display:inline;'
+						// 			},
+						// 			children:[]
+						// 		}
+						// 		allObj.children.push(obj);
+						// 	}
+							
+						// }
+						// if(j>=this.emojiName.length){//不包含表情
+						// 	let obj={
+						// 		name:'div',
+						// 		attrs:{
+						// 			class:'input',
+						// 			style:''
+						// 		},
+						// 		children:[
+						// 			{
+						// 				type:'text',
+						// 				text:allStr
+						// 			}
+						// 		]
+						// 	}
+						// 	allObj.children.push(obj);
+						// }
+						// res.data.message.payload.text=[allObj];
+						that.messageList.push(res.data.message);
+						that.inputText='';
+					}
+					that.endItem='view_id_' + parseInt(Math.random() * 1000000)
+					that.scrollTo=that.endItem;
+				}).catch(err=>{
+					uni.showToast({
+						title:'发送失败',
+						icon:"none"
+					});
+					let user=JSON.parse(uni.getStorageSync('userInfo'))
+					let obj={
+						avatar:user.icon,
+						flow:'out',
+						payload:{
+							data:{
+								context:'聊天信息涉嫌违规内容,多次违规可能会被封号,请遵守',
+								highlightColor: "#44CBFB",
+								highlightContext: "《平台行为规范》",
+								highlightLink: "/pages/webview/webview?url=https://h5.sugarpark.cn/agreement/behavior-standard.html",
+								systemMsgType: 10,
+								type: 99,
+							},
+							text:'****'
+							
+							
+						}
+					}
+					
+				})
+			},
+			loadHistoryMessage(){
+				/**
+				 * 获取历史消息
+				 */
+				uni.$TUIKit.getMessageList({
+					conversationID:this.message.conversationID,
+					nextReqMessageID:this.nextReqMessageID,
+					count:15
+				}).then(res=>{
+					
+					let result=JSON.parse(JSON.stringify(res));
+					console.log(result,result.data.nextReqMessageID,this.nextReqMessageID)
+					
+					if(this.isCompleted){
+						return;
+					}
+					this.nextReqMessageID=result.data.nextReqMessageID;
+					
+					if(result.code===0&&result.data.messageList.length>0){
+						for(let i=0;i<result.data.messageList.length;i++){
+							/**
+							 * 处理时间
+							 */
+							result.data.messageList[i].timeStr=this.$moment(result.data.messageList[i].time*1000).calendar(null,{
+								sameDay: '[今天] hh:mm',
+								lastDay: '[昨天] hh:mm',
+								sameElse: 'YYYY-MM-DD hh:mm:ss'
+							});
+							if(i>=1){
+								result.data.messageList[i].showTime=(result.data.messageList[i].time-result.data.messageList[i-1].time<300?false:true);
+							}
+							/**
+							 * 处理表情
+							 */
+							// if(result.data.messageList[i].payload.text){
+							// 	let allStr=result.data.messageList[i].payload.text;
+							// 	let arr=[];
+							// 	let allObj={
+							// 		name:'div',
+							// 		attrs:{
+							// 			class:'input',
+							// 			style:''
+							// 		},
+							// 		children:[]
+							// 	}
+							// 	for(let j=0;j<this.emojiName.length;j++){
+							// 		if(allStr.indexOf(this.emojiName[j])>-1){
+							// 			arr.push(this.emojiName[j]);
+							// 			let imgurl=this.emojiUrl+this.emojiMap[this.emojiName[j]];
+							// 			let obj={
+							// 				name:'img',
+							// 				attrs:{
+							// 					src:imgurl,
+							// 					class:'',
+							// 					style:'width:20px;height:20px;display:inline;'
+							// 				},
+							// 				children:[]
+							// 			}
+							// 			allObj.children.push(obj);
+							// 		}
+							// 	}
+							// 	result.data.messageList[i].payload.text=[allObj];
+							// }
+							
+							/**
+							 * 处理非常规消息
+							 */
+							// if(result.data.messageList[i].payload.data){
+							// 	let data=JSON.parse(result.data.messageList[i].payload.data);
+							// 		result.data.messageList[i].flow='out';
+							// 		result.data.messageList[i].avatar=JSON.parse(uni.getStorageSync('userInfo')).icon;
+							// 		if(data.systemMsgType===10){
+							// 			result.data.messageList[i].payload.text='****';
+							// 			data.highlightLink=`/pages/webview/webview?url=${this.$util.protocal.behaviorStandar}`;
+							// 			if(data.context&&data.context.indexOf(data.highlightContext)!==-1){
+							// 				data.context=data.context.substring(0,data.context.indexOf(data.highlightContext));
+							// 				data.highlightColor=`#${data.highlightColor.substring(2)}`
+							// 			}
+							// 		}
+							// 		if(data.systemMsgType===4){
+							// 			data.context='上传真人认证照片,会有更多人喜欢哦~';
+							// 			data.highlightLink='/pages/mine/album';
+							// 			if(data.context&&data.context.indexOf(data.highlightContext)!==-1){
+							// 				data.context=data.context.substring(data.context.indexOf(data.highlightContext)+data.highlightContext.length,data.context.length);
+							// 				data.highlightColor=`#${data.highlightColor.substring(2)}`
+							// 			}
+							// 		}
+							// 		result.data.messageList[i].payload.data=data;
+							// }
+							
+						}
+						this.messageList=[...result.data.messageList,...this.messageList];
+						console.log(this.messageList);
+						this.isCompleted=result.data.isCompleted;
+						if(this.isLoadPreData){
+							this.startItem='view_id_' + parseInt(Math.random() * 1000000)
+							this.scrollTo=this.startItem;
+							console.log('滑动到顶部了')
+							this.isLoadPreData=false;
+						}
+						else{
+							this.endItem='view_id_' + parseInt(Math.random() * 1000000)
+							this.scrollTo=this.endItem;
+							console.log('滑动到底部了')
+						}
+						
+						
+					}
+				})
+			},
+			
+			resetLoginData() {
+				let user=JSON.parse(uni.getStorageSync('userInfo'));
+				this.$api.IM.loadSig({}).then(res=>{
+					this.$store.commit('setImLoadSig',res.data.sig);
+					uni.$TUIKit.login({
+						userID:String(user.id),
+						userSig:res.data.sig
+					})
+				})
+				
+			},
+			onTIMError() {},
+			onSDKReady({name}) {
+				  const isSDKReady = name === uni.$TUIKitEvent.SDK_READY ? true : false
+				if(isSDKReady){
+					this.createMessageConversation();
+				}
+				
+			},
+			onNetStateChange() {},
+			onSDKReload() {},
+			onSdkNotReady() {},
+			onKickedOut() {
+				uni.showToast({
+					title: '您被踢下线',
+					icon: 'error'
+				});
+				uni.reLaunch({
+					url: '/pages/login/login'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.container{
+	width: 100vw;
+	height: 100vh;
+	background-color: $bgcolor1;
+	position: relative;
+	overflow: hidden;
+	.topnav {
+		padding: 0 10rpx;
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100vw;
+		z-index: 100;
+		background-color: $bgcolor1;
+		.nav-item{
+			width: 40rpx;
+			height: 40rpx;
+			margin-left: 16rpx;
+			
+			.nav-img{
+				width: 40rpx;
+				height: 40rpx;
+			}
+		}
+		.nav-center{
+			flex: 1;
+			flex-direction: column;
+			.nav-text{
+				
+				color: $fontcolor5;
+				height: 40rpx;
+				text-align: center;
+			}
+			.nav-tip{
+				color:$fontcolor2;
+			}
+		}
+		
+	}
+	.scroll-view{
+		transition: height .3s;
+	}
+	
+	.t-message-item {
+		/*max-width: 60vw;*/
+		padding: 16rpx 0;
+	}
+
+	.t-recieve-message {
+		display: flex;
+		flex-direction: row;
+		justify-items: flex-start;
+		align-items: center;
+		width: 100vw;
+	}
+
+	.t-message-avatar {
+		margin-left: 20rpx;
+		margin-right: 12rpx;
+		border-radius: 10rpx;
+		width: 80rpx;
+		height: 80rpx;
+	}
+
+	.t-self-message {
+		display: flex;
+		flex-direction: row;
+		justify-content: flex-end;
+		/*align-items: center;*/
+		width: 100vw;
+	}
+
+	.t-self-message-body {
+		display: flex;
+		justify-content: flex-start;
+		flex-wrap: wrap;
+		outline: none;
+	}
+
+	.t-recieve-message-body {
+		display: flex;
+		justify-content: flex-start;
+		flex-wrap: wrap;
+		outline: none;
+		/*background: #F8F8F8;*/
+		border-radius: 2px 10px 10px 10px;
+		margin-left: 8rpx;
+
+	}
+
+	.read-receipts {
+		line-height: 42px;
+		height: 42px;
+		font-size: 12px;
+		color: #6e7981;
+		margin-right: 10px
+	}
+
+	.no-message {
+		text-align: center;
+		position: fixed;
+		width: 100%;
+		font-size: 12px;
+		color: #a5b5c1;
+		height: 40px;
+		top: -40px;
+		right: 0;
+	}
+
+	// .msg-line{
+	// 	margin:16rpx 32rpx;
+	// 	.time{
+	// 		color: #ffffff;
+	// 		text-align: center;
+	// 		margin: 40rpx 0rpx;
+	// 	}
+	// 	.line{
+	// 		.msg-item{
+	// 			.msg-head{
+	// 				width:80rpx;
+	// 				height:80rpx;
+	// 				border-radius:34rpx;
+	// 				.head-img{
+	// 					width:80rpx;
+	// 					height:80rpx;
+	// 					border-radius:34rpx;
+	// 				}
+					
+	// 			}
+	// 			.msg-text{
+	// 				padding: 20rpx 24rpx;
+	// 				border-radius: 4rpx 16rpx 16rpx 16rpx;
+	// 				background: #1F1A30;
+	// 				color:#ffffff;
+	// 				margin-left:24rpx;
+	// 				margin-right:16rpx;
+	// 			}
+	// 			.err-icon{
+	// 				width: 40rpx;
+	// 				height: 40rpx;
+	// 			}
+	// 			.msg-tag{
+	// 				width:44rpx;
+	// 				height: 80rpx;
+	// 				color:#6C52F4;
+	// 				line-height:80rpx;
+	// 				text-align:center;
+	// 			}
+	// 		}
+	// 	}
+	// 	.err-line{
+	// 		flex-wrap: wrap;
+	// 		width: 73%;
+	// 		padding: 20rpx 24rpx;
+	// 		margin: 0 auto;
+	// 		.err-text{
+	// 			color:#ffffff ;
+	// 			text-align: center;
+	// 		}
+	// 	}
+		
+	// }
+	.talk-box{
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100vw;
+		padding-top: 16rpx;
+		background-color: $bgcolor1;
+		.input-box{
+			margin: 0rpx 32rpx;
+			height: 80rpx;
+			border-radius: 80rpx;
+			background-color: $bgcolor4;
+			padding: 0 24rpx;
+			.input{
+				width: 100%;
+				height: 100%;
+				color: #ffffff;
+				font-size: 22rpx;
+			}
+		}
+		.action-box{
+			margin-top: 36rpx;
+			padding-bottom: 16rpx;
+			.act-img{
+				width: 56rpx;
+				height: 56rpx;
+			}
+		}
+		.emoji-box{
+			padding: 0 11rpx;
+			box-sizing: border-box;
+			height: 400rpx;
+			flex-wrap: wrap;
+			transition: height .3s;
+			.emoji-item{
+				width: 64rpx;
+				height: 64rpx;
+				padding: 20rpx;
+				.emoji-img{
+					width: 64rpx;
+					height: 64rpx;
+				}
+			}
+		}
+	}
+}
+</style>

BIN
static/back.png


BIN
static/input-clear.png


+ 1122 - 0
static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.js

@@ -0,0 +1,1122 @@
+/**
+ * 微信小程序JavaScriptSDK
+ * 
+ * @version 1.2
+ * @date 2019-03-06
+ */
+
+var ERROR_CONF = {
+    KEY_ERR: 311,
+    KEY_ERR_MSG: 'key格式错误',
+    PARAM_ERR: 310,
+    PARAM_ERR_MSG: '请求参数信息有误',
+    SYSTEM_ERR: 600,
+    SYSTEM_ERR_MSG: '系统错误',
+    WX_ERR_CODE: 1000,
+    WX_OK_CODE: 200
+};
+var BASE_URL = 'https://apis.map.qq.com/ws/';
+var URL_SEARCH = BASE_URL + 'place/v1/search';
+var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
+var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
+var URL_CITY_LIST = BASE_URL + 'district/v1/list';
+var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
+var URL_DISTANCE = BASE_URL + 'distance/v1/';
+var URL_DIRECTION = BASE_URL + 'direction/v1/';
+var MODE = {
+  driving: 'driving',
+  transit: 'transit'
+};
+var EARTH_RADIUS = 6378136.49;
+var Utils = {
+  /**
+  * md5加密方法
+  * 版权所有©2011 Sebastian Tschan,https://blueimp.net
+  */
+  safeAdd(x, y) {
+    var lsw = (x & 0xffff) + (y & 0xffff);
+    var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+    return (msw << 16) | (lsw & 0xffff);
+  },
+  bitRotateLeft(num, cnt) {
+    return (num << cnt) | (num >>> (32 - cnt));
+  },
+  md5cmn(q, a, b, x, s, t) {
+    return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b);
+  },
+  md5ff(a, b, c, d, x, s, t) {
+    return this.md5cmn((b & c) | (~b & d), a, b, x, s, t);
+  },
+  md5gg(a, b, c, d, x, s, t) {
+    return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t);
+  },
+  md5hh(a, b, c, d, x, s, t) {
+    return this.md5cmn(b ^ c ^ d, a, b, x, s, t);
+  },
+  md5ii(a, b, c, d, x, s, t) {
+    return this.md5cmn(c ^ (b | ~d), a, b, x, s, t);
+  },
+  binlMD5(x, len) {
+    /* append padding */
+    x[len >> 5] |= 0x80 << (len % 32);
+    x[((len + 64) >>> 9 << 4) + 14] = len;
+
+    var i;
+    var olda;
+    var oldb;
+    var oldc;
+    var oldd;
+    var a = 1732584193;
+    var b = -271733879;
+    var c = -1732584194;
+    var d = 271733878;
+
+    for (i = 0; i < x.length; i += 16) {
+      olda = a;
+      oldb = b;
+      oldc = c;
+      oldd = d;
+
+      a = this.md5ff(a, b, c, d, x[i], 7, -680876936);
+      d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+      c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+      b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+      a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+      d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+      c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+      b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+      a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+      d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+      c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063);
+      b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+      a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+      d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+      c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+      b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+      a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+      d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+      c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+      b = this.md5gg(b, c, d, a, x[i], 20, -373897302);
+      a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+      d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+      c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+      b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+      a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+      d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+      c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+      b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+      a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+      d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+      c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+      b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+      a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558);
+      d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+      c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+      b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+      a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+      d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+      c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+      b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+      a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+      d = this.md5hh(d, a, b, c, x[i], 11, -358537222);
+      c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+      b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+      a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+      d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+      c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+      b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+      a = this.md5ii(a, b, c, d, x[i], 6, -198630844);
+      d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+      c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+      b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+      a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+      d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+      c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+      b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+      a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+      d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+      c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+      b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+      a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+      d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+      c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+      b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+      a = this.safeAdd(a, olda);
+      b = this.safeAdd(b, oldb);
+      c = this.safeAdd(c, oldc);
+      d = this.safeAdd(d, oldd);
+    }
+    return [a, b, c, d];
+  },
+  binl2rstr(input) {
+    var i;
+    var output = '';
+    var length32 = input.length * 32;
+    for (i = 0; i < length32; i += 8) {
+      output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff);
+    }
+    return output;
+  },
+  rstr2binl(input) {
+    var i;
+    var output = [];
+    output[(input.length >> 2) - 1] = undefined;
+    for (i = 0; i < output.length; i += 1) {
+      output[i] = 0;
+    }
+    var length8 = input.length * 8;
+    for (i = 0; i < length8; i += 8) {
+      output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32);
+    }
+    return output;
+  },
+  rstrMD5(s) {
+    return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8));
+  },
+  rstrHMACMD5(key, data) {
+    var i;
+    var bkey = this.rstr2binl(key);
+    var ipad = [];
+    var opad = [];
+    var hash;
+    ipad[15] = opad[15] = undefined;
+    if (bkey.length > 16) {
+      bkey = this.binlMD5(bkey, key.length * 8);
+    }
+    for (i = 0; i < 16; i += 1) {
+      ipad[i] = bkey[i] ^ 0x36363636;
+      opad[i] = bkey[i] ^ 0x5c5c5c5c;
+    }
+    hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8);
+    return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128));
+  },
+  rstr2hex(input) {
+    var hexTab = '0123456789abcdef';
+    var output = '';
+    var x;
+    var i;
+    for (i = 0; i < input.length; i += 1) {
+      x = input.charCodeAt(i);
+      output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
+    }
+    return output;
+  },
+  str2rstrUTF8(input) {
+    return unescape(encodeURIComponent(input));
+  },
+  rawMD5(s) {
+    return this.rstrMD5(this.str2rstrUTF8(s));
+  },
+  hexMD5(s) {
+    return this.rstr2hex(this.rawMD5(s));
+  },
+  rawHMACMD5(k, d) {
+    return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d));
+  },
+  hexHMACMD5(k, d) {
+    return this.rstr2hex(this.rawHMACMD5(k, d));
+  },
+
+  md5(string, key, raw) {
+    if (!key) {
+      if (!raw) {
+        return this.hexMD5(string);
+      }
+      return this.rawMD5(string);
+    }
+    if (!raw) {
+      return this.hexHMACMD5(key, string);
+    }
+    return this.rawHMACMD5(key, string);
+  },
+  /**
+   * 得到md5加密后的sig参数
+   * @param {Object} requestParam 接口参数
+   * @param {String} sk签名字符串
+   * @param {String} featrue 方法名
+   * @return 返回加密后的sig参数
+   */
+  getSig(requestParam, sk, feature, mode) {
+    var sig = null;
+    var requestArr = [];
+    Object.keys(requestParam).sort().forEach(function(key){
+      requestArr.push(key + '=' + requestParam[key]);
+    });
+    if (feature == 'search') {
+      sig = '/ws/place/v1/search?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'suggest') {
+      sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'reverseGeocoder') {
+      sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'geocoder') {
+      sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'getCityList') {
+      sig = '/ws/district/v1/list?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'getDistrictByCityId') {
+      sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'calculateDistance') {
+      sig = '/ws/distance/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'direction') {
+      sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk;
+    }
+    sig = this.md5(sig);
+    return sig;
+  },
+    /**
+     * 得到终点query字符串
+     * @param {Array|String} 检索数据
+     */
+    location2query(data) {
+        if (typeof data == 'string') {
+            return data;
+        }
+        var query = '';
+        for (var i = 0; i < data.length; i++) {
+            var d = data[i];
+            if (!!query) {
+                query += ';';
+            }
+            if (d.location) {
+                query = query + d.location.lat + ',' + d.location.lng;
+            }
+            if (d.latitude && d.longitude) {
+                query = query + d.latitude + ',' + d.longitude;
+            }
+        }
+        return query;
+    },
+
+    /**
+     * 计算角度
+     */
+    rad(d) {
+      return d * Math.PI / 180.0;
+    },  
+    /**
+     * 处理终点location数组
+     * @return 返回终点数组
+     */
+    getEndLocation(location){
+      var to = location.split(';');
+      var endLocation = [];
+      for (var i = 0; i < to.length; i++) {
+        endLocation.push({
+          lat: parseFloat(to[i].split(',')[0]),
+          lng: parseFloat(to[i].split(',')[1])
+        })
+      }
+      return endLocation;
+    },
+
+    /**
+     * 计算两点间直线距离
+     * @param a 表示纬度差
+     * @param b 表示经度差
+     * @return 返回的是距离,单位m
+     */
+    getDistance(latFrom, lngFrom, latTo, lngTo) {
+      var radLatFrom = this.rad(latFrom);
+      var radLatTo = this.rad(latTo);
+      var a = radLatFrom - radLatTo;
+      var b = this.rad(lngFrom) - this.rad(lngTo);
+      var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));
+      distance = distance * EARTH_RADIUS;
+      distance = Math.round(distance * 10000) / 10000;
+      return parseFloat(distance.toFixed(0));
+    },
+    /**
+     * 使用微信接口进行定位
+     */
+    getWXLocation(success, fail, complete) {
+        wx.getFuzzyLocation({
+            type: 'gcj02',
+            success: success,
+            fail: fail,
+            complete: complete
+        });
+    },
+
+    /**
+     * 获取location参数
+     */
+    getLocationParam(location) {
+        if (typeof location == 'string') {
+            var locationArr = location.split(',');
+            if (locationArr.length === 2) {
+                location = {
+                    latitude: location.split(',')[0],
+                    longitude: location.split(',')[1]
+                };
+            } else {
+                location = {};
+            }
+        }
+        return location;
+    },
+
+    /**
+     * 回调函数默认处理
+     */
+    polyfillParam(param) {
+        param.success = param.success || function () { };
+        param.fail = param.fail || function () { };
+        param.complete = param.complete || function () { };
+    },
+
+    /**
+     * 验证param对应的key值是否为空
+     * 
+     * @param {Object} param 接口参数
+     * @param {String} key 对应参数的key
+     */
+    checkParamKeyEmpty(param, key) {
+        if (!param[key]) {
+            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误');
+            param.fail(errconf);
+            param.complete(errconf);
+            return true;
+        }
+        return false;
+    },
+
+    /**
+     * 验证参数中是否存在检索词keyword
+     * 
+     * @param {Object} param 接口参数
+     */
+    checkKeyword(param){
+        return !this.checkParamKeyEmpty(param, 'keyword');
+    },
+
+    /**
+     * 验证location值
+     * 
+     * @param {Object} param 接口参数
+     */
+    checkLocation(param) {
+        var location = this.getLocationParam(param.location);
+        if (!location || !location.latitude || !location.longitude) {
+            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
+            param.fail(errconf);
+            param.complete(errconf);
+            return false;
+        }
+        return true;
+    },
+
+    /**
+     * 构造错误数据结构
+     * @param {Number} errCode 错误码
+     * @param {Number} errMsg 错误描述
+     */
+    buildErrorConfig(errCode, errMsg) {
+        return {
+            status: errCode,
+            message: errMsg
+        };
+    },
+
+    /**
+     * 
+     * 数据处理函数
+     * 根据传入参数不同处理不同数据
+     * @param {String} feature 功能名称
+     * search 地点搜索
+     * suggest关键词提示
+     * reverseGeocoder逆地址解析
+     * geocoder地址解析
+     * getCityList获取城市列表:父集
+     * getDistrictByCityId获取区县列表:子集
+     * calculateDistance距离计算
+     * @param {Object} param 接口参数
+     * @param {Object} data 数据
+     */
+    handleData(param,data,feature){
+      if (feature == 'search') {
+        var searchResult = data.data;
+        var searchSimplify = [];
+        for (var i = 0; i < searchResult.length; i++) {
+          searchSimplify.push({
+            id: searchResult[i].id || null,
+            title: searchResult[i].title || null,
+            latitude: searchResult[i].location && searchResult[i].location.lat || null,
+            longitude: searchResult[i].location && searchResult[i].location.lng || null,
+            address: searchResult[i].address || null,
+            category: searchResult[i].category || null,
+            tel: searchResult[i].tel || null,
+            adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
+            city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
+            district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
+            province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
+          })
+        }
+        param.success(data, {
+          searchResult: searchResult,
+          searchSimplify: searchSimplify
+        })
+      } else if (feature == 'suggest') {
+        var suggestResult = data.data;
+        var suggestSimplify = [];
+        for (var i = 0; i < suggestResult.length; i++) {
+          suggestSimplify.push({
+            adcode: suggestResult[i].adcode || null,
+            address: suggestResult[i].address || null,
+            category: suggestResult[i].category || null,
+            city: suggestResult[i].city || null,
+            district: suggestResult[i].district || null,
+            id: suggestResult[i].id || null,
+            latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
+            longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
+            province: suggestResult[i].province || null,
+            title: suggestResult[i].title || null,
+            type: suggestResult[i].type || null
+          })
+        }
+        param.success(data, {
+          suggestResult: suggestResult,
+          suggestSimplify: suggestSimplify
+          })
+      } else if (feature == 'reverseGeocoder') {
+        var reverseGeocoderResult = data.result;
+        var reverseGeocoderSimplify = {
+          address: reverseGeocoderResult.address || null,
+          latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
+          longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
+          adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
+          city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
+          district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
+          nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
+          province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
+          street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
+          street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
+          recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
+          rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
+        };
+        if (reverseGeocoderResult.pois) {//判断是否返回周边poi
+          var pois = reverseGeocoderResult.pois;
+          var poisSimplify = [];
+          for (var i = 0;i < pois.length;i++) {
+            poisSimplify.push({
+              id: pois[i].id || null,
+              title: pois[i].title || null,
+              latitude: pois[i].location && pois[i].location.lat || null,
+              longitude: pois[i].location && pois[i].location.lng || null,
+              address: pois[i].address || null,
+              category: pois[i].category || null,
+              adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
+              city: pois[i].ad_info && pois[i].ad_info.city || null,
+              district: pois[i].ad_info && pois[i].ad_info.district || null,
+              province: pois[i].ad_info && pois[i].ad_info.province || null
+            })
+          }
+          param.success(data,{
+            reverseGeocoderResult: reverseGeocoderResult,
+            reverseGeocoderSimplify: reverseGeocoderSimplify,
+            pois: pois,
+            poisSimplify: poisSimplify
+          })
+        } else {
+          param.success(data, {
+            reverseGeocoderResult: reverseGeocoderResult,
+            reverseGeocoderSimplify: reverseGeocoderSimplify
+          })
+        }
+      } else if (feature == 'geocoder') {
+        var geocoderResult = data.result;
+        var geocoderSimplify = {
+          title: geocoderResult.title || null,
+          latitude: geocoderResult.location && geocoderResult.location.lat || null,
+          longitude: geocoderResult.location && geocoderResult.location.lng || null,
+          adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
+          province: geocoderResult.address_components && geocoderResult.address_components.province || null,
+          city: geocoderResult.address_components && geocoderResult.address_components.city || null,
+          district: geocoderResult.address_components && geocoderResult.address_components.district || null,
+          street: geocoderResult.address_components && geocoderResult.address_components.street || null,
+          street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null,
+          level: geocoderResult.level || null
+        };
+        param.success(data,{
+          geocoderResult: geocoderResult,
+          geocoderSimplify: geocoderSimplify
+        });
+      } else if (feature == 'getCityList') {
+        var provinceResult = data.result[0];
+        var cityResult = data.result[1];
+        var districtResult = data.result[2];
+        param.success(data,{
+          provinceResult: provinceResult,
+          cityResult: cityResult,
+          districtResult: districtResult
+        });
+      } else if (feature == 'getDistrictByCityId') {
+        var districtByCity = data.result[0];
+        param.success(data, districtByCity);
+      } else if (feature == 'calculateDistance') {
+        var calculateDistanceResult = data.result.elements;  
+        var distance = [];
+        for (var i = 0; i < calculateDistanceResult.length; i++){
+          distance.push(calculateDistanceResult[i].distance);
+        }   
+        param.success(data, {
+          calculateDistanceResult: calculateDistanceResult,
+          distance: distance
+          });
+      } else if (feature == 'direction') {
+        var direction = data.result.routes;
+        param.success(data,direction);
+      } else {
+        param.success(data);
+      }
+    },
+
+    /**
+     * 构造微信请求参数,公共属性处理
+     * 
+     * @param {Object} param 接口参数
+     * @param {Object} param 配置项
+     * @param {String} feature 方法名
+     */
+    buildWxRequestConfig(param, options, feature) {
+        var that = this;
+        options.header = { "content-type": "application/json" };
+        options.method = 'GET';
+        options.success = function (res) {
+            var data = res.data;
+            if (data.status === 0) {
+              that.handleData(param, data, feature);
+            } else {
+                param.fail(data);
+            }
+        };
+        options.fail = function (res) {
+            res.statusCode = ERROR_CONF.WX_ERR_CODE;
+            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+        };
+        options.complete = function (res) {
+            var statusCode = +res.statusCode;
+            switch(statusCode) {
+                case ERROR_CONF.WX_ERR_CODE: {
+                    param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+                    break;
+                }
+                case ERROR_CONF.WX_OK_CODE: {
+                    var data = res.data;
+                    if (data.status === 0) {
+                        param.complete(data);
+                    } else {
+                        param.complete(that.buildErrorConfig(data.status, data.message));
+                    }
+                    break;
+                }
+                default:{
+                    param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
+                }
+
+            }
+        };
+        return options;
+    },
+
+    /**
+     * 处理用户参数是否传入坐标进行不同的处理
+     */
+    locationProcess(param, locationsuccess, locationfail, locationcomplete) {
+        var that = this;
+        locationfail = locationfail || function (res) {
+            res.statusCode = ERROR_CONF.WX_ERR_CODE;
+            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+        };
+        locationcomplete = locationcomplete || function (res) {
+            if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
+                param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+            }
+        };
+        if (!param.location) {
+            that.getWXLocation(locationsuccess, locationfail, locationcomplete);
+        } else if (that.checkLocation(param)) {
+            var location = Utils.getLocationParam(param.location);
+            locationsuccess(location);
+        }
+    }
+};
+
+
+class QQMapWX {
+
+    /**
+     * 构造函数
+     * 
+     * @param {Object} options 接口参数,key 为必选参数
+     */
+    constructor(options) {
+        if (!options.key) {
+            throw Error('key值不能为空');
+        }
+        this.key = options.key;
+    };
+
+    /**
+     * POI周边检索
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 参数对象结构可以参考
+     * @see http://lbs.qq.com/webservice_v1/guide-search.html
+     */
+    search(options) {
+        var that = this;
+        options = options || {};
+
+        Utils.polyfillParam(options);
+
+        if (!Utils.checkKeyword(options)) {
+            return;
+        }
+
+        var requestParam = {
+            keyword: options.keyword,
+            orderby: options.orderby || '_distance',
+            page_size: options.page_size || 10,
+            page_index: options.page_index || 1,
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.address_format) {
+            requestParam.address_format = options.address_format;
+        }
+
+        if (options.filter) {
+            requestParam.filter = options.filter;
+        }
+
+        var distance = options.distance || "1000";
+        var auto_extend = options.auto_extend || 1;
+        var region = null;
+        var rectangle = null;
+
+        //判断城市限定参数
+        if (options.region) {
+          region = options.region;
+        }
+
+        //矩形限定坐标(暂时只支持字符串格式)
+        if (options.rectangle) {
+          rectangle = options.rectangle;
+        }
+
+        var locationsuccess = function (result) {        
+          if (region && !rectangle) {
+            //城市限定参数拼接
+            requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+          } else if (rectangle && !region) {
+            //矩形搜索
+            requestParam.boundary = "rectangle(" + rectangle + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+            } else {
+              requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+            }            
+            wx.request(Utils.buildWxRequestConfig(options, {
+                url: URL_SEARCH,
+                data: requestParam
+            }, 'search'));
+        };
+        Utils.locationProcess(options, locationsuccess);
+    };
+
+    /**
+     * sug模糊检索
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 参数对象结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-suggestion.html
+     */
+    getSuggestion(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (!Utils.checkKeyword(options)) {
+            return;
+        }
+
+        var requestParam = {
+            keyword: options.keyword,
+            region: options.region || '全国',
+            region_fix: options.region_fix || 0,
+            policy: options.policy || 0,
+            page_size: options.page_size || 10,//控制显示条数
+            page_index: options.page_index || 1,//控制页数
+            get_subpois : options.get_subpois || 0,//返回子地点
+            output: 'json',
+            key: that.key
+        };
+        //长地址
+        if (options.address_format) {
+          requestParam.address_format = options.address_format;
+        }
+        //过滤
+        if (options.filter) {
+          requestParam.filter = options.filter;
+        }
+        //排序
+        if (options.location) {
+          var locationsuccess = function (result) {
+            requestParam.location = result.latitude + ',' + result.longitude;
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+            }
+            wx.request(Utils.buildWxRequestConfig(options, {
+              url: URL_SUGGESTION,
+              data: requestParam
+            }, "suggest"));      
+          };
+          Utils.locationProcess(options, locationsuccess);
+        } else {
+          if (options.sig) {
+            requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+          }
+          wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_SUGGESTION,
+            data: requestParam
+          }, "suggest"));      
+        }        
+    };
+
+    /**
+     * 逆地址解析
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-gcoder.html
+     */
+    reverseGeocoder(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+        var requestParam = {
+            coord_type: options.coord_type || 5,
+            get_poi: options.get_poi || 0,
+            output: 'json',
+            key: that.key
+        };
+        if (options.poi_options) {
+            requestParam.poi_options = options.poi_options
+        }
+
+        var locationsuccess = function (result) {
+            requestParam.location = result.latitude + ',' + result.longitude;
+          if (options.sig) {
+            requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder');
+          }
+            wx.request(Utils.buildWxRequestConfig(options, {
+                url: URL_GET_GEOCODER,
+                data: requestParam
+            }, 'reverseGeocoder'));
+        };
+        Utils.locationProcess(options, locationsuccess);
+    };
+
+    /**
+     * 地址解析
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-geocoder.html
+     */
+    geocoder(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'address')) {
+            return;
+        }
+
+        var requestParam = {
+            address: options.address,
+            output: 'json',
+            key: that.key
+        };
+
+        //城市限定
+        if (options.region) {
+          requestParam.region = options.region;
+        }
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_GET_GEOCODER,
+            data: requestParam
+        },'geocoder'));
+    };
+
+
+    /**
+     * 获取城市列表
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-region.html
+     */
+    getCityList(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+        var requestParam = {
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_CITY_LIST,
+            data: requestParam
+        },'getCityList'));
+    };
+
+    /**
+     * 获取对应城市ID的区县列表
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-region.html
+     */
+    getDistrictByCityId(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'id')) {
+            return;
+        }
+
+        var requestParam = {
+            id: options.id || '',
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_AREA_LIST,
+            data: requestParam
+        },'getDistrictByCityId'));
+    };
+
+    /**
+     * 用于单起点到多终点的路线距离(非直线距离)计算:
+     * 支持两种距离计算方式:步行和驾车。
+     * 起点到终点最大限制直线距离10公里。
+     *
+     * 新增直线距离计算。
+     * 
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-distance.html
+     */
+    calculateDistance(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'to')) {
+            return;
+        }
+
+        var requestParam = {
+            mode: options.mode || 'walking',
+            to: Utils.location2query(options.to),
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.from) {
+          options.location = options.from;
+        }
+
+        //计算直线距离
+        if(requestParam.mode == 'straight'){        
+          var locationsuccess = function (result) {
+            var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标
+            var data = {
+              message:"query ok",
+              result:{
+                elements:[]
+              },
+              status:0
+            };
+            for (var i = 0; i < locationTo.length; i++) {
+              data.result.elements.push({//将坐标存入
+                distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
+                duration:0,
+                from:{
+                  lat: result.latitude,
+                  lng:result.longitude
+                },
+                to:{
+                  lat: locationTo[i].lat,
+                  lng: locationTo[i].lng
+                }
+              });            
+            }
+            var calculateResult = data.result.elements;
+            var distanceResult = [];
+            for (var i = 0; i < calculateResult.length; i++) {
+              distanceResult.push(calculateResult[i].distance);
+            }  
+            return options.success(data,{
+              calculateResult: calculateResult,
+              distanceResult: distanceResult
+            });
+          };
+          
+          Utils.locationProcess(options, locationsuccess);
+        } else {
+          var locationsuccess = function (result) {
+            requestParam.from = result.latitude + ',' + result.longitude;
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance');
+            }
+            wx.request(Utils.buildWxRequestConfig(options, {
+              url: URL_DISTANCE,
+              data: requestParam
+            },'calculateDistance'));
+          };
+
+          Utils.locationProcess(options, locationsuccess);
+        }      
+    };
+
+  /**
+   * 路线规划:
+   * 
+   * @param {Object} options 接口参数对象
+   * 
+   * 请求参数结构可以参考
+   * https://lbs.qq.com/webservice_v1/guide-road.html
+   */
+  direction(options) {
+    var that = this;
+    options = options || {};
+    Utils.polyfillParam(options);
+
+    if (Utils.checkParamKeyEmpty(options, 'to')) {
+      return;
+    }
+
+    var requestParam = {
+      output: 'json',
+      key: that.key
+    };
+
+    //to格式处理
+    if (typeof options.to == 'string') {
+      requestParam.to = options.to;
+    } else {
+      requestParam.to = options.to.latitude + ',' + options.to.longitude;
+    }
+    //初始化局部请求域名
+    var SET_URL_DIRECTION = null;
+    //设置默认mode属性
+    options.mode = options.mode || MODE.driving;
+
+    //设置请求域名
+    SET_URL_DIRECTION = URL_DIRECTION + options.mode;
+
+    if (options.from) {
+      options.location = options.from;
+    }
+
+    if (options.mode == MODE.driving) {
+      if (options.from_poi) {
+        requestParam.from_poi = options.from_poi;
+      }
+      if (options.heading) {
+        requestParam.heading = options.heading;
+      }
+      if (options.speed) {
+        requestParam.speed = options.speed;
+      }
+      if (options.accuracy) {
+        requestParam.accuracy = options.accuracy;
+      }
+      if (options.road_type) {
+        requestParam.road_type = options.road_type;
+      }
+      if (options.to_poi) {
+        requestParam.to_poi = options.to_poi;
+      }
+      if (options.from_track) {
+        requestParam.from_track = options.from_track;
+      }
+      if (options.waypoints) {
+        requestParam.waypoints = options.waypoints;
+      }
+      if (options.policy) {
+        requestParam.policy = options.policy;
+      }
+      if (options.plate_number) {
+        requestParam.plate_number = options.plate_number;
+      }
+    }
+
+    if (options.mode == MODE.transit) {
+      if (options.departure_time) {
+        requestParam.departure_time = options.departure_time;
+      }
+      if (options.policy) {
+        requestParam.policy = options.policy;
+      }
+    } 
+
+    var locationsuccess = function (result) {
+      requestParam.from = result.latitude + ',' + result.longitude;
+      if (options.sig) {
+        requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction',options.mode);
+      }
+      wx.request(Utils.buildWxRequestConfig(options, {
+        url: SET_URL_DIRECTION,
+        data: requestParam
+      }, 'direction'));
+    };
+
+    Utils.locationProcess(options, locationsuccess);
+  }
+};
+
+module.exports = QQMapWX;

File diff suppressed because it is too large
+ 0 - 0
static/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js


BIN
static/tabbar/friends-off.png


BIN
static/tabbar/friends-on.png


BIN
static/tabbar/home-off.png


BIN
static/tabbar/home-on.png


BIN
static/tabbar/message-off.png


BIN
static/tabbar/message-on.png


BIN
static/tabbar/mine-off.png


BIN
static/tabbar/mine-on.png


BIN
static/tabbar/tabbar-add.png


BIN
static/video-play.png


+ 69 - 0
store/index.js

@@ -0,0 +1,69 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+export default new Vuex.Store({
+  state: {
+	statusBarHeight:null,
+	topbarOffsetHeight:null,
+	tabbarHeight:null,
+	hasSafeArea:null,
+	platform:null,
+	isUpdateUserInfo:false,
+	appId:'wxc351e5dcc35e5b30',
+	version:null,
+	service:null,
+	latitude:null,
+	longitude:null,
+	userInfo:null,
+	imageCdn:null,
+	videoCdn:null,
+	IMloadSig:null,
+  },
+  mutations: {
+	setStatusBarHeight(state,preload){
+		state.statusBarHeight=preload;
+	},
+	setTopbarOffsetHeight(state,preload){
+		state.topbarOffsetHeight=preload;
+	},
+	setTabBarHeight(state,preload){
+		state.tabbarHeight=preload;
+	},
+	setHasSafeArea(state,preload){
+		state.hasSafeArea=preload;
+	},
+	setPlatform(state,preload){
+		state.platform=preload;
+	},
+	setIsUpdateUserInfo(state,preload){
+		state.isUpdateUserInfo=preload;
+	},
+	setVersion(state,preload){
+		state.version=preload;
+	},
+	setService(state,preload){
+		state.service=preload;
+	},
+	setLatitude(state,preload){
+		state.latitude=preload;
+	},
+	setLongitude(state,preload){
+		state.longitude=preload;
+	},
+	setUserInfo(state,preload){
+		state.userInfo=preload;
+	},
+	setImageCdn(state,preload){
+		state.imageCdn=preload;
+	},
+	setVideoCdn(state,preload){
+		state.videoCdn=preload;
+	},
+	setImLoadSig(state,preload){
+		state.IMloadSig=preload;
+	}
+  },
+  modules: {
+  }
+})

+ 50 - 0
uni.scss

@@ -0,0 +1,50 @@
+/*每个页面公共css */
+$primary:#6C52F4;
+$assist1:#0ABDEF;
+$assist2:#FE3B49;
+$assist3:#FFCD32;
+$bgcolor1:#151126;
+$bgcolor2:#151126;
+$bgcolor3:#1F1A30;
+$bgcolor4:#332D4C;
+$fontcolor1:#494667;
+$fontcolor2:#7D7DA4;
+$fontcolor3:#9A9ABF;
+$fontcolor4:#D4D4F1;
+$fontcolor5:#FFFFFF;
+.font72{font-size: 72rpx;}
+.font44{font-size: 44rpx;}
+.font36{font-size: 36rpx;}
+.font32{font-size: 32rpx;}
+.font28{font-size: 28rpx;}
+.font24{font-size: 24rpx;}
+.font22{font-size: 22rpx;}
+.font20{font-size: 20rpx;}
+.fw400{font-weight: 400;}
+.fw500{font-weight: 500;}
+.fw600{font-weight: 600;}
+.fw700{font-weight: 700;}
+.el{
+	 white-space: nowrap;
+	 overflow: hidden;
+	 text-overflow: ellipsis;
+}
+button{background-color: transparent;}
+.button-hover{ background: transparent; }
+button::after {border: none;box-sizing: content-box; }
+::-webkit-scrollbar { width: 0; height: 0; color: transparent; }
+.placeholder-input{
+	font-size: 28rpx;
+	font-family: PingFangSC-Regular, PingFang SC;
+	font-weight: 400;
+	color: #B7B7C5;
+	line-height: 40rpx;
+}
+/*每个页面公共css */
+.flex-center{display: flex;justify-content: center;align-items: center;}
+.flex-around{display: flex;justify-content: space-around;align-items: center;}
+.flex-between{display: flex;justify-content: space-between;align-items: center;}
+.flex-start{display: flex;justify-content: flex-start;align-items: center;}
+.flex-end{display: flex;justify-content: flex-end;align-items: flex-end;}
+.null-box{width: 100%;height: 176rpx;background-color: $bgcolor1;}
+.no-more{color: $fontcolor3;text-align: center;padding: 10rpx 0rpx 30rpx 0rpx;}

+ 18 - 0
uni_modules/buuug7-img-cropper/changelog.md

@@ -0,0 +1,18 @@
+## 0.0.9(2021-08-17)
+- 更新readme.md
+## 0.0.8(2021-08-17)
+- fix issue
+## 0.0.7(2021-08-17)
+- update readme.md
+## 0.0.6(2021-08-17)
+- update readme.md
+## 0.0.5(2021-08-17)
+- 更新readme.md
+## 0.0.4(2021-08-17)
+- 增加圆形裁剪
+## 0.0.3(2021-08-16)
+- 更新文档
+## 0.0.2(2021-08-16)
+- 更新文档
+## 0.0.1(2021-08-16)
+- Init

+ 9 - 0
uni_modules/buuug7-img-cropper/components/buuug7-img-cropper/buuug7-img-cropper.vue

@@ -0,0 +1,9 @@
+<template>
+</template>
+
+<script>
+	// this file is omit
+</script>
+
+<style>
+</style>

File diff suppressed because it is too large
+ 8 - 0
uni_modules/buuug7-img-cropper/hybrid/html/cropper/cropper.min.css


File diff suppressed because it is too large
+ 9 - 0
uni_modules/buuug7-img-cropper/hybrid/html/cropper/cropper.min.js


+ 39 - 0
uni_modules/buuug7-img-cropper/hybrid/html/cropper/index.html

@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="zh">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
+    <title>图片裁剪</title>
+    <link rel="stylesheet" href="cropper.min.css" />
+    <link rel="stylesheet" href="style.css" />
+    <script src="cropper.min.js"></script>
+    <script src="uni.webview.1.5.2.js"></script>
+  </head>
+  <body>
+    <div class="file-upload-box">
+      <input
+        id="my-input"
+        type="file"
+        class="hidden-input"
+        accept="image/png, image/jpeg, image/webp"
+      />
+      <label for="my-input" class="input-label">+</label>
+    </div>
+
+    <div class="img-crop-area"></div>
+
+    <div class="previewAll">
+      <div class="preview"></div>
+      <div class="preview"></div>
+      <div class="preview"></div>
+      <div class="preview"></div>
+    </div>
+
+    <p>
+      <button class="btn disabled" id="save">确定</button>
+    </p>
+
+    <script src="index.js"></script>
+  </body>
+</html>

+ 267 - 0
uni_modules/buuug7-img-cropper/hybrid/html/cropper/index.js

@@ -0,0 +1,267 @@
+// 宽高比
+const aspectRatio = 1 / 1;
+// 自动裁剪区域, 默认为 50%
+const autoCropAre = 0.5;
+// 裁剪宽度
+const croppedWidth = 200;
+// 裁剪高度
+const croppedHeight = croppedWidth * aspectRatio;
+// 是否裁剪为圆形
+const roundedCrop = true;
+
+
+const fileUploadBox = document.querySelector(".file-upload-box");
+const saveBtn = document.querySelector("#save");
+const previews = document.querySelectorAll(".preview");
+let previewReady = false;
+let croppable = false;
+
+document.addEventListener("UniAppJSBridgeReady", Init);
+
+async function Init(params) {
+  console.log(`uniAppSDK loaded`);
+
+  const env = await getEnv();
+  console.log("当前环境:" + JSON.stringify(env));
+
+  const imgDataUrl = await selectFile(env);
+
+  // hidden input box
+  fileUploadBox.style.display = "none";
+
+  // create image
+  const image = new Image();
+  image.src = imgDataUrl;
+  image.crossorigin = true;
+  document.querySelector(".img-crop-area").appendChild(image);
+
+  image.onload = function () {
+    const options = {
+      aspectRatio: aspectRatio,
+      autoCropAre: autoCropAre,
+      viewMode: 1,
+      ready: function () {
+        let clone = this.cloneNode();
+
+        clone.className = "";
+        clone.style.cssText =
+          "display: block;" +
+          "width: 100%;" +
+          "min-width: 0;" +
+          "min-height: 0;" +
+          "max-width: none;" +
+          "max-height: none;";
+
+        each(previews, function (elem) {
+          elem.appendChild(clone.cloneNode());
+        });
+
+        croppable = true;
+        previewReady = true;
+        saveBtn.classList.remove("disabled");
+
+        if (roundedCrop) {
+          const elements = document.querySelectorAll(
+            ".cropper-view-box, .cropper-face"
+          );
+          for (let item of elements) {
+            item.style.borderRadius = "50%";
+          }
+        }
+      },
+      crop: function (event) {
+        if (!previewReady) {
+          return;
+        }
+
+        let data = event.detail;
+        let cropper = this.cropper;
+        let imageData = cropper.getImageData();
+        let previewAspectRatio = data.width / data.height;
+
+        each(previews, function (elem) {
+          let previewImage = elem.getElementsByTagName("img").item(0);
+
+          let previewWidth = elem.offsetWidth;
+          let previewHeight = previewWidth / previewAspectRatio;
+          let imageScaledRatio = data.width / previewWidth;
+
+          if (roundedCrop) {
+            elem.style.borderRadius = "50%";
+          }
+
+          elem.style.height = previewHeight + "px";
+          previewImage.style.width =
+            imageData.naturalWidth / imageScaledRatio + "px";
+          previewImage.style.height =
+            imageData.naturalHeight / imageScaledRatio + "px";
+          previewImage.style.marginLeft = -data.x / imageScaledRatio + "px";
+          previewImage.style.marginTop = -data.y / imageScaledRatio + "px";
+        });
+      },
+    };
+
+    const cropper = new Cropper(image, options);
+
+    save.addEventListener("click", () => {
+      if (!croppable) {
+        return;
+      }
+
+      let croppedCanvas = cropper.getCroppedCanvas({
+        width: croppedWidth,
+        height: croppedHeight,
+      });
+
+      if (roundedCrop) {
+        croppedCanvas = getRoundedCanvas(croppedCanvas);
+      }
+
+      const postData = {
+        data: {
+          type: "croppedData",
+          dataUrl: croppedCanvas.toDataURL(),
+        },
+      };
+
+      // console.log(`postData`, postData);
+
+      if (env.plus) {
+        uni.postMessage(postData);
+      } else if (env.h5) {
+        top.postMessage(postData);
+      } else if (env.miniprogram) {
+        // 小程序
+        top.postMessage(postData);
+      }
+
+      // back to previous page
+      uni.navigateBack({
+        delta: 1,
+      });
+    });
+  };
+}
+
+function getRoundedCanvas(sourceCanvas) {
+  let canvas = document.createElement("canvas");
+  let context = canvas.getContext("2d");
+  let width = sourceCanvas.width;
+  let height = sourceCanvas.height;
+
+  canvas.width = width;
+  canvas.height = height;
+  context.imageSmoothingEnabled = true;
+  context.drawImage(sourceCanvas, 0, 0, width, height);
+  context.globalCompositeOperation = "destination-in";
+  context.beginPath();
+  context.arc(
+    width / 2,
+    height / 2,
+    Math.min(width, height) / 2,
+    0,
+    2 * Math.PI,
+    true
+  );
+  context.fill();
+  return canvas;
+}
+
+function each(arr, callback) {
+  let length = arr.length;
+  let i;
+
+  for (i = 0; i < length; i++) {
+    callback.call(arr, arr[i], i, arr);
+  }
+
+  return arr;
+}
+
+async function selectFile(env) {
+  const fileInput = document.querySelector("#my-input");
+  return new Promise((resolve, reject) => {
+    fileInput.addEventListener("change", async (event) => {
+      let result;
+      result = await getDataUrlFromReader(event);
+      resolve(result);
+    });
+  });
+}
+
+async function getDataUrlFromReader(event) {
+  const files = event.target.files;
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.addEventListener("loadend", () => {
+      resolve(reader.result);
+    });
+    reader.readAsDataURL(files[0]);
+  });
+}
+
+async function getEnv() {
+  return new Promise((resolve, reject) => {
+    uni.getEnv((res) => {
+      resolve(res);
+    });
+  });
+}
+
+// TODO:
+async function chooseWithPlusApi() {
+  const btnArray = [
+    {
+      title: "拍照",
+    },
+    {
+      title: "从手机相册选择",
+    },
+  ];
+
+  return new Promise((resolve, reject) => {
+    plus.nativeUI.actionSheet(
+      {
+        cancel: "取消",
+        buttons: btnArray,
+      },
+      function (e) {
+        let index = e.index;
+        switch (index) {
+          case 0:
+            break;
+          case 1:
+            let camera = plus.camera.getCamera();
+            camera.captureImage(
+              function (file) {
+                resolve(file);
+              },
+              function () {
+                console.log("从相机获取照片失败");
+                reject("从相机获取照片失败");
+              },
+              {
+                filename: "_doc/photo/",
+                index: 1,
+              }
+            );
+            break;
+          case 2:
+            plus.gallery.pick(
+              function (file) {
+                resolve(file);
+              },
+              function () {
+                console.log("取消图片选择");
+                reject("取消图片选择");
+              },
+              {
+                multiple: false,
+              }
+            );
+            break;
+        }
+      }
+    );
+  });
+}

+ 149 - 0
uni_modules/buuug7-img-cropper/hybrid/html/cropper/style.css

@@ -0,0 +1,149 @@
+/* button styles */
+.btn {
+  box-sizing: border-box;
+  position: relative;
+  display: inline-block;
+  font-weight: 400;
+  line-height: 1.5;
+  color: #000;
+  text-align: center;
+  text-decoration: none;
+  vertical-align: middle;
+  cursor: pointer;
+  user-select: none;
+  background-color: #e9ecef;
+  border: 1px solid #e9ecef;
+  padding: 0.375rem 0.75rem;
+  font-size: 1rem;
+  border-radius: 4px;
+  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
+    border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+.btn *,
+.btn *::before,
+.btn *::after {
+  box-sizing: inherit;
+}
+.btn img,
+.btn svg {
+  display: inline-flex;
+  vertical-align: -0.125em;
+  width: 1em;
+  height: 1em;
+}
+.btn:hover {
+  text-decoration: none;
+  background-color: #cbd3da;
+}
+.btn:focus {
+  outline: none;
+}
+.btn.disabled,
+.btn:disabled {
+  opacity: 0.65;
+  pointer-events: none;
+}
+
+.btn.primary {
+  background-color: #007bff;
+  border-color: #007bff;
+  color: #fff;
+}
+.btn.primary:hover {
+  text-decoration: none;
+  background-color: #0062cc;
+}
+
+.btn.outline {
+  background-color: transparent;
+  border-color: #e9ecef;
+}
+.btn.outline:hover {
+  text-decoration: none;
+  background-color: #e9ecef;
+}
+
+.btn.link {
+  background-color: transparent;
+  color: #007bff;
+  border-color: transparent;
+}
+.btn.link:hover {
+  background-color: #e9ecef;
+}
+
+.btn.block {
+  width: 100%;
+  display: block;
+}
+
+.btn.small {
+  padding: 0.1rem 0.4rem;
+}
+
+/* index.html */
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+body,
+html {
+  padding: 0;
+  margin: 0;
+}
+
+body {
+  padding: 0.5rem;
+}
+
+img {
+  display: block;
+  /* This rule is very important, please don't ignore this */
+  max-width: 100%;
+  max-height: 400px;
+  width: 100%;
+}
+
+.previewAll {
+  display: grid;
+  grid-template-columns: 4fr 3fr 2fr 1fr;
+  gap: 0.5rem;
+  margin-top: 0.5rem;
+}
+
+.previewAll .preview {
+  overflow: hidden;
+}
+
+/* file input */
+
+.file-upload-box {
+  position: relative;
+}
+.file-upload-box .hidden-input {
+  position: absolute !important;
+  width: 1px;
+  height: 1px;
+  overflow: hidden;
+  clip: rect(1px 1px 1px 1px);
+}
+.file-upload-box input.hidden-input:focus + label {
+  outline: thin dotted;
+}
+.file-upload-box input.hidden-input:focus-within + label {
+  outline: thin dotted;
+}
+.file-upload-box .input-label {
+  border: 1px solid #eee;
+  width: 80px;
+  height: 80px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 2rem;
+  font-weight: lighter;
+  color: #555;
+}

Some files were not shown because too many files changed in this diff