Просмотр исходного кода

Merge branch 'master' of http://150.158.33.144:3000/lalalashen/MeetMateApp

# Conflicts:
#	pages/login/loginFirst.vue
ck110 1 месяц назад
Родитель
Сommit
6e5dcfa63f
100 измененных файлов с 4458 добавлено и 111 удалено
  1. 1 0
      App.vue
  2. 20 2
      androidPrivacy.json
  3. 87 0
      common/checkVersion.js
  4. 71 0
      common/emoji.js
  5. 120 0
      common/update_config.js
  6. 7 0
      components/uni-popup/i18n/en.json
  7. 8 0
      components/uni-popup/i18n/index.js
  8. 7 0
      components/uni-popup/i18n/zh-Hans.json
  9. 7 0
      components/uni-popup/i18n/zh-Hant.json
  10. 45 0
      components/uni-popup/keypress.js
  11. 26 0
      components/uni-popup/popup.js
  12. 90 0
      components/uni-popup/uni-popup.uvue
  13. 479 0
      components/uni-popup/uni-popup.vue
  14. 34 0
      components/uni-ui/uni-icons/uni-icons.vue
  15. 187 0
      components/uni-ui/uni-nav-bar/uni-nav-bar.vue
  16. 187 0
      components/uni-ui/uni-popup/uni-popup.vue
  17. 26 0
      components/uni-ui/uni-status-bar/uni-status-bar.vue
  18. 568 0
      components/wu-app-update/wu-app-update.vue
  19. 6 0
      main.js
  20. 87 84
      package.json
  21. 18 0
      pages.json
  22. 49 0
      pages/chat/components/delete-conversation.vue
  23. 75 0
      pages/chat/components/emoji.vue
  24. 53 0
      pages/chat/components/icon-voice.vue
  25. 112 0
      pages/chat/components/member.vue
  26. 551 0
      pages/chat/components/message.vue
  27. 170 0
      pages/chat/components/tools.vue
  28. 252 0
      pages/chat/components/user.vue
  29. 745 0
      pages/chat/detail.vue
  30. 256 0
      pages/chat/message.vue
  31. 4 2
      pages/index/index.vue
  32. 3 3
      pages/login/login.vue
  33. 4 4
      pages/my/editInfo.vue
  34. 3 3
      pages/my/editMobile.vue
  35. 4 4
      pages/my/forgetPass.vue
  36. 2 2
      pages/my/idcheck.vue
  37. 3 0
      pages/my/my.vue
  38. 4 4
      pages/my/step.vue
  39. 1 1
      pages/w3/levRule.vue
  40. 2 2
      pages/w3/tran.vue
  41. BIN
      static/appUploadAlertBoxBg.png
  42. 84 0
      static/css/iconfont.scss
  43. BIN
      static/icon/QQ.png
  44. BIN
      static/icon/add.png
  45. BIN
      static/icon/brief-icon-1.png
  46. BIN
      static/icon/brief-icon-2.png
  47. BIN
      static/icon/chat-icon.png
  48. BIN
      static/icon/chat-more.png
  49. BIN
      static/icon/comment.png
  50. BIN
      static/icon/delete.png
  51. BIN
      static/icon/dynamic-icon.png
  52. BIN
      static/icon/fabu.png
  53. BIN
      static/icon/fabu_1.png
  54. BIN
      static/icon/filter-icon.png
  55. BIN
      static/icon/floot-icon.png
  56. BIN
      static/icon/hi.png
  57. BIN
      static/icon/hot.png
  58. BIN
      static/icon/like.png
  59. BIN
      static/icon/location.png
  60. BIN
      static/icon/man.png
  61. BIN
      static/icon/may-3.png
  62. BIN
      static/icon/member.png
  63. BIN
      static/icon/message-more.png
  64. BIN
      static/icon/more.png
  65. BIN
      static/icon/my-icon-1.png
  66. BIN
      static/icon/my-icon-2.png
  67. BIN
      static/icon/my-icon-3.png
  68. BIN
      static/icon/my-icon-4.png
  69. BIN
      static/icon/my-icon-5.png
  70. BIN
      static/icon/my-icon-6.png
  71. BIN
      static/icon/no_like.png
  72. BIN
      static/icon/pengyouquan.png
  73. BIN
      static/icon/service.png
  74. BIN
      static/icon/tabbar/dynamic.png
  75. BIN
      static/icon/tabbar/home.png
  76. BIN
      static/icon/tabbar/meet.png
  77. BIN
      static/icon/tabbar/message.png
  78. BIN
      static/icon/tabbar/no-home.png
  79. BIN
      static/icon/tabbar/on-dynamic.png
  80. BIN
      static/icon/tabbar/on-meet.png
  81. BIN
      static/icon/tabbar/on-message.png
  82. BIN
      static/icon/tabbar/on-room.png
  83. BIN
      static/icon/tabbar/room.png
  84. BIN
      static/icon/tabs-icon.png
  85. BIN
      static/icon/to-my.png
  86. BIN
      static/icon/vip-icon.png
  87. BIN
      static/icon/weixin.png
  88. BIN
      static/icon/woman.png
  89. BIN
      static/images/ad.png
  90. BIN
      static/images/article/dazhaohu.png
  91. BIN
      static/images/article/dazhaohu_2.png
  92. BIN
      static/images/article/like.png
  93. BIN
      static/images/article/no_like.png
  94. BIN
      static/images/article/no_like_bak.png
  95. BIN
      static/images/article/pinglun.png
  96. BIN
      static/images/attention.png
  97. BIN
      static/images/avatar.png
  98. BIN
      static/images/birthday.png
  99. BIN
      static/images/brief-cover-1.png
  100. BIN
      static/images/brief-cover-2.png

+ 1 - 0
App.vue

@@ -35,6 +35,7 @@
 	@import 'colorui/main.css';
 	@import 'colorui/icon.css';
 	@import 'style/FontStyle.css';
+	@import "uni_modules/cl-uni/index.scss";
 
 	body {
 		-webkit-text-size-adjust: none;

+ 20 - 2
androidPrivacy.json

@@ -1,3 +1,21 @@
 {
-    "prompt" : "none"
-}
+	"version": "1",
+	"prompt": "template",
+	"title": "服务协议和隐私政策",
+	"message": "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
+	"buttonAccept": "同意并接受",
+	"buttonRefuse": "暂不同意",
+	"second": {
+		"title": "确认提示",
+		"message": "  进入应用前,你需先同意<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>,否则将退出应用。",
+		"buttonAccept": "同意并继续",
+		"buttonRefuse": "退出应用"
+	},
+	"styles": {
+		"backgroundColor": "#FFFFFF",
+		"borderRadius": "5px",
+		"buttonRefuse": {
+			"color": "#AAAAAA"
+		}
+	}
+}

+ 87 - 0
common/checkVersion.js

@@ -0,0 +1,87 @@
+import config from './update_config.js';
+/**
+ * 对比版本号,如需要,请自行修改判断规则
+ * 支持比对	("3.0.0.0.0.1.0.1", "3.0.0.0.0.1")	("3.0.0.1", "3.0")	("3.1.1", "3.1.1.1") 之类的
+ * @param {Object} v1
+ * @param {Object} v2
+ * v1 > v2 return 1
+ * v1 < v2 return -1
+ * v1 == v2 return 0
+ */
+function compare(v1 = '0', v2 = '0') {
+	v1 = String(v1).split('.')
+	v2 = String(v2).split('.')
+	const minVersionLens = Math.min(v1.length, v2.length);
+
+	let result = 0;
+	for (let i = 0; i < minVersionLens; i++) {
+		const curV1 = Number(v1[i])
+		const curV2 = Number(v2[i])
+
+		if (curV1 > curV2) {
+			result = 1
+			break;
+		} else if (curV1 < curV2) {
+			result = -1
+			break;
+		}
+	}
+
+	if (result === 0 && (v1.length !== v2.length)) {
+		const v1BiggerThenv2 = v1.length > v2.length;
+		const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
+		for (let i = minVersionLens; i < maxLensVersion.length; i++) {
+			const curVersion = Number(maxLensVersion[i])
+			if (curVersion > 0) {
+				v1BiggerThenv2 ? result = 1 : result = -1
+				break;
+			}
+		}
+	}
+
+	return result;
+}
+
+/**
+ * 检测升级 使用说明
+ * 1. 通过请求获取后台返回的更新信息
+ * 2. 获取后台更新信息后 和 应用本身的版本(manifest.json里的versionName)做对比。
+ * 3. 如果后台版本比应用本身的版本大 且 后台本次更新的平台里含有 用户的系统环境则触发更新,否则一律不触发更新。
+ */
+export default function checkVersion() {
+	return new Promise((resolve, reject) => {
+		// #ifdef APP-PLUS
+		uni.getSystemInfo({
+			success: (info) => {
+				// app系统环境
+				let appPlatform = info.platform;
+				console.log("appPlatform", appPlatform)
+				// 获取本机版本号
+				plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
+					// 请补充这个请求即可正常使用
+					uni.request({
+						url: 'http://e.yujianmate.com/Gapi/Index/updateWgt?skey=' +
+							getApp().globalData.skey + "&version=" + wgtinfo
+							.versionCode,
+						success: (res => {
+							res = res.data;
+							console.log("res22", res)
+							if (compare(res.version, wgtinfo.version) ===
+								1 && res.platform.indexOf(appPlatform) !== -
+								1) {
+								res.appPlatform = appPlatform;
+								resolve(res);
+							} else {
+								reject('当前版本已经是最新的,不需要更新')
+							}
+						}),
+						fail: (err) => {
+							reject(err);
+						}
+					})
+				});
+			}
+		});
+		// #endif
+	})
+}

+ 71 - 0
common/emoji.js

@@ -0,0 +1,71 @@
+export default[
+	"😀",
+	"😁",
+	"😂",
+	"😃",
+	"😄",
+	"😅",
+	"😆",
+	"😇",
+	"😈",
+	"😉",
+	"😊",
+	"😋",
+	"😌",
+	"😍",
+	"😎",
+	"😏",
+	"😐",
+	"😑",
+	"😒",
+	"😓",
+	"😔",
+	"😕",
+	"😖",
+	"😗",
+	"😘",
+	"😙",
+	"😚",
+	"😛",
+	"😜",
+	"😝",
+	"😞",
+	"😟",
+	"😠",
+	"😡",
+	"😢",
+	"😣",
+	"😤",
+	"😥",
+	"😦",
+	"😧",
+	"😨",
+	"😩",
+	"😪",
+	"😫",
+	"😬",
+	"😭",
+	"😮",
+	"😯",
+	"😰",
+	"😱",
+	"😲",
+	"😳",
+	"😴",
+	"😵",
+	"😶",
+	"😷",
+	"😸",
+	"😹",
+	"😺",
+	"😻",
+	"😼",
+	"😽",
+	"😾",
+	"😿",
+	"🙀",
+	"🙁",
+	"🙂",
+	"🙃",
+	"🙄",
+]

+ 120 - 0
common/update_config.js

@@ -0,0 +1,120 @@
+export default {
+	// props里设置的是默认样式 使用组件时可以被覆盖
+	props: {
+		// 主题颜色 
+		themeColor: {
+			default: '#61d287',
+			type: String
+		},
+		// 背景色
+		bgColor: {
+			default: '#fff',
+			type: String
+		},
+		// 版本号字体颜色
+		versionColor: {
+			default: '#fff',
+			type: String
+		},
+		// 关闭图标颜色
+		closeIconColor: {
+			default: "#fff",
+			type: String
+		},
+		// 关闭图标大小
+		closeIconSize: {
+			default: 26,
+			type: Number
+		},
+		// 更新标题文字颜色
+		titleColor: {
+			default: '#5e5e5e',
+			type: String
+		},
+		// 更新内容文字颜色
+		contentColor: {
+			default: '#878787',
+			type: String
+		},
+		// 短期内不更新图标大小
+		notRemindIconSize: {
+			default: 22,
+			type: Number
+		},
+		// 短期内不更新选中图标颜色
+		notRemindIconActColor: {
+			default: '',
+			type: String
+		},
+		// 短期内不更新未选中图标颜色
+		notRemindIconNotActColor: {
+			default: '#9d9d9d',
+			type: String
+		},
+		// 短期内不更新选中文字颜色
+		notRemindTextActColor: {
+			default: '#6b6b6b',
+			type: String
+		},
+		// 短期内不更新未选中文字颜色
+		notRemindTextNotActColor: {
+			default: '#9d9d9d',
+			type: String
+		},
+		// 下载按钮ios文字
+		downloadBtnTextIOS: {
+			default: '立即跳转更新',
+			type: String
+		},
+		// 下载按钮Android文字
+		downloadBtnTextAndroid: {
+			default: '立即升级',
+			type: String
+		},
+		// 下载中文字提示
+		downLoadingText: {
+			default: '安装包下载中,请稍后',
+			type: String
+		},
+		// 下载完成文字提示
+		downloadSuccessText: {
+			default: '下载完成,立即安装',
+			type: String
+		},
+		// wgt安装中显示文字
+		wgtInstallingText: {
+			default: '正在安装....',
+			type: String
+		},
+		// wgt安装完成重启显示文字
+		wgtInstalledText: {
+			default: '安装完毕,点击重启',
+			type: String
+		},
+		// 按钮背景色
+		btnBgColor: {
+			default: '',
+			type: String
+		},
+		// 按钮文字颜色
+		btnColor: {
+			default: '#fff',
+			type: String
+		},
+		// 进度条颜色
+		progressColor: {
+			default: '',
+			type: String
+		},
+		// 进度条文字样式
+		progressTextColor: {
+			default: '#4c4c4c',
+			type: String
+		},
+		// 提示用户更新的间隔时间 单位day
+		intervalAlertUserUpdateDay: {
+			default: 7,
+			type: Number
+		}
+	}
+}

+ 7 - 0
components/uni-popup/i18n/en.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "cancel",
+	"uni-popup.ok": "ok",
+	"uni-popup.placeholder": "pleace enter",
+	"uni-popup.title": "Hint",
+	"uni-popup.shareTitle": "Share to"
+}

+ 8 - 0
components/uni-popup/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 7 - 0
components/uni-popup/i18n/zh-Hans.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "取消",
+	"uni-popup.ok": "确定",
+	"uni-popup.placeholder": "请输入",
+		"uni-popup.title": "提示",
+		"uni-popup.shareTitle": "分享到"
+}

+ 7 - 0
components/uni-popup/i18n/zh-Hant.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "取消",
+	"uni-popup.ok": "確定",
+	"uni-popup.placeholder": "請輸入",
+	"uni-popup.title": "提示",
+	"uni-popup.shareTitle": "分享到"
+}

+ 45 - 0
components/uni-popup/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    // this.$once('hook:beforeDestroy', () => {
+    //   document.removeEventListener('keyup', listener)
+    // })
+  },
+	render: () => {}
+}
+// #endif

+ 26 - 0
components/uni-popup/popup.js

@@ -0,0 +1,26 @@
+
+export default {
+	data() {
+		return {
+			
+		}
+	},
+	created(){
+		this.popup = this.getParent()
+	},
+	methods:{
+		/**
+		 * 获取父元素实例
+		 */
+		getParent(name = 'uniPopup') {
+			let parent = this.$parent;
+			let parentName = parent.$options.name;
+			while (parentName !== name) {
+				parent = parent.$parent;
+				if (!parent) return false
+				parentName = parent.$options.name;
+			}
+			return parent;
+		},
+	}
+}

+ 90 - 0
components/uni-popup/uni-popup.uvue

@@ -0,0 +1,90 @@
+<template>
+  <view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
+    <view @click.stop>
+      <slot></slot>
+    </view>
+  </view>
+</template>
+
+<script>
+  type CloseCallBack = ()=> void;
+  let closeCallBack:CloseCallBack = () :void => {};
+  export default {
+    emits:["close","clickMask"],
+    data() {
+      return {
+        isShow:false,
+        isOpen:false
+      }
+    },
+    props: {
+      maskClick: {
+        type: Boolean,
+        default: true
+      },
+    },
+    watch: {
+      // 设置show = true 时,如果没有 open 需要设置为 open
+      isShow:{
+        handler(isShow) {
+          // console.log("isShow",isShow)
+          if(isShow && this.isOpen == false){
+            this.isOpen = true
+          }
+        },
+        immediate:true
+      },
+      // 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow
+      isOpen:{
+        handler(isOpen) {
+          // console.log("isOpen",isOpen)
+          if(isOpen && this.isShow == false){
+            this.isShow = true
+          }
+        },
+        immediate:true
+      }
+    },
+    methods:{
+      open(){
+        // ...funs : CloseCallBack[]
+        // if(funs.length > 0){
+        //   closeCallBack = funs[0]
+        // }
+        this.isOpen = true;
+      },
+      clickMask(){
+        if(this.maskClick == true){
+          this.$emit('clickMask')
+          this.close()
+        }
+      },
+      close(): void{
+        this.isOpen = false;
+        this.$emit('close')
+        closeCallBack()
+      },
+      hiden(){
+        this.isShow = false
+      },
+      show(){
+        this.isShow = true
+      }
+    }
+  }
+</script>
+
+<style>
+.popup-root {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 750rpx;
+  height: 100%;
+  flex: 1;
+  background-color: rgba(0, 0, 0, 0.3);
+  justify-content: center;
+  align-items: center;
+  z-index: 99;
+}
+</style>

+ 479 - 0
components/uni-popup/uni-popup.vue

@@ -0,0 +1,479 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
+		<view @touchstart="touchstart">
+			<uni-transition key="1" v-if="maskShow" name="mask" :styles="maskClass"
+				:duration="duration" :show="showTrans" @click="onTap" />
+			<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
+				:show="showTrans" @click="onTap">
+				<view class="uni-popup__wrapper" :style="{ backgroundColor: bg }" :class="[popupstyle]" @click="clear">
+					<slot />
+				</view>
+			</uni-transition>
+		</view>
+		<!-- #ifdef H5 -->
+		<keypress v-if="maskShow" @esc="onTap" />
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+	// #ifdef H5
+	import keypress from './keypress.js'
+	// #endif
+
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * 	@value left		左侧弹出
+	 * 	@value right  右侧弹出
+	 * 	@value message 消息提示
+	 * 	@value dialog 对话框
+	 * 	@value share 底部分享示例
+	 * @property {Boolean} animation = [true|false] 是否开启动画
+	 * @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
+	 * @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
+	 * @property {String}  backgroundColor 主窗口背景色
+	 * @property {String}  maskBackgroundColor 蒙版颜色
+	 * @property {Boolean} safeArea		   是否适配底部安全区
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 * @event {Function} maskClick 点击遮罩触发
+	 */
+
+	export default {
+		name: 'uniPopup',
+		components: {
+			// #ifdef H5
+			keypress
+			// #endif
+		},
+		emits: ['change', 'maskClick'],
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			// message: 消息提示 ; dialog : 对话框
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			isMaskClick: {
+				type: Boolean,
+				default: null
+			},
+			// TODO 2 个版本后废弃属性 ,使用 isMaskClick
+			maskClick: {
+				type: Boolean,
+				default: null
+			},
+			backgroundColor: {
+				type: String,
+				default: 'none'
+			},
+			safeArea: {
+				type: Boolean,
+				default: true
+			},
+			maskBackgroundColor: {
+				type: String,
+				default: 'rgba(0, 0, 0, 0.4)'
+			},
+		},
+
+		watch: {
+			/**
+			 * 监听type类型
+			 */
+			type: {
+				handler: function(type) {
+					if (!this.config[type]) return
+					this[this.config[type]](true)
+				},
+				immediate: true
+			},
+			isDesktop: {
+				handler: function(newVal) {
+					if (!this.config[newVal]) return
+					this[this.config[this.type]](true)
+				},
+				immediate: true
+			},
+			/**
+			 * 监听遮罩是否可点击
+			 * @param {Object} val
+			 */
+			maskClick: {
+				handler: function(val) {
+					this.mkclick = val
+				},
+				immediate: true
+			},
+			isMaskClick: {
+				handler: function(val) {
+					this.mkclick = val
+				},
+				immediate: true
+			},
+			// H5 下禁止底部滚动
+			showPopup(show) {
+				// #ifdef H5
+				// fix by mehaotian 处理 h5 滚动穿透的问题
+				document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
+				// #endif
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				popupWidth: 0,
+				popupHeight: 0,
+				config: {
+					top: 'top',
+					bottom: 'bottom',
+					center: 'center',
+					left: 'left',
+					right: 'right',
+					message: 'top',
+					dialog: 'center',
+					share: 'bottom'
+				},
+				maskClass: {
+					position: 'fixed',
+					bottom: 0,
+					top: 0,
+					left: 0,
+					right: 0,
+					backgroundColor: 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					position: 'fixed',
+					left: 0,
+					right: 0
+				},
+				maskShow: true,
+				mkclick: true,
+				popupstyle: 'top'
+			}
+		},
+		computed: {
+			isDesktop() {
+				return this.popupWidth >= 500 && this.popupHeight >= 500
+			},
+			bg() {
+				if (this.backgroundColor === '' || this.backgroundColor === 'none') {
+					return 'transparent'
+				}
+				return this.backgroundColor
+			}
+		},
+		mounted() {
+			const fixSize = () => {
+				const {
+					windowWidth,
+					windowHeight,
+					windowTop,
+					safeArea,
+					screenHeight,
+					safeAreaInsets
+				} = uni.getSystemInfoSync()
+				this.popupWidth = windowWidth
+				this.popupHeight = windowHeight + (windowTop || 0)
+				// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
+				if (safeArea && this.safeArea) {
+					// #ifdef MP-WEIXIN
+					this.safeAreaInsets = screenHeight - safeArea.bottom
+					// #endif
+					// #ifndef MP-WEIXIN
+					this.safeAreaInsets = safeAreaInsets.bottom
+					// #endif
+				} else {
+					this.safeAreaInsets = 0
+				}
+			}
+			fixSize()
+			// #ifdef H5
+			// window.addEventListener('resize', fixSize)
+			// this.$once('hook:beforeDestroy', () => {
+			// 	window.removeEventListener('resize', fixSize)
+			// })
+			// #endif
+		},
+		// #ifndef VUE3
+		// TODO vue2
+		destroyed() {
+			this.setH5Visible()
+		},
+		// #endif
+		// #ifdef VUE3
+		// TODO vue3
+		unmounted() {
+			this.setH5Visible()
+		},
+		// #endif
+		created() {
+			// this.mkclick =  this.isMaskClick || this.maskClick
+			if (this.isMaskClick === null && this.maskClick === null) {
+				this.mkclick = true
+			} else {
+				this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
+			}
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+			// TODO 处理 message 组件生命周期异常的问题
+			this.messageChild = null
+			// TODO 解决头条冒泡的问题
+			this.clearPropagation = false
+			this.maskClass.backgroundColor = this.maskBackgroundColor
+		},
+		methods: {
+			setH5Visible() {
+				// #ifdef H5
+				// fix by mehaotian 处理 h5 滚动穿透的问题
+				document.getElementsByTagName('body')[0].style.overflow = 'visible'
+				// #endif
+			},
+			/**
+			 * 公用方法,不显示遮罩层
+			 */
+			closeMask() {
+				this.maskShow = false
+			},
+			/**
+			 * 公用方法,遮罩层禁止点击
+			 */
+			disableMask() {
+				this.mkclick = false
+			},
+			// TODO nvue 取消冒泡
+			clear(e) {
+				// #ifndef APP-NVUE
+				e.stopPropagation()
+				// #endif
+				this.clearPropagation = true
+			},
+
+			open(direction) {
+				// fix by mehaotian 处理快速打开关闭的情况
+				if (this.showPopup) {
+					return
+				}
+				let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
+				if (!(direction && innerType.indexOf(direction) !== -1)) {
+					direction = this.type
+				}
+				if (!this.config[direction]) {
+					console.error('缺少类型:', direction)
+					return
+				}
+				this[this.config[direction]]()
+				this.$emit('change', {
+					show: true,
+					type: direction
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$emit('change', {
+					show: false,
+					type: this.type
+				})
+				clearTimeout(this.timer)
+				// // 自定义关闭事件
+				// this.customOpen && this.customClose()
+				this.timer = setTimeout(() => {
+					this.showPopup = false
+				}, 300)
+			},
+			// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
+			touchstart() {
+				this.clearPropagation = false
+			},
+
+			onTap() {
+				if (this.clearPropagation) {
+					// fix by mehaotian 兼容 nvue
+					this.clearPropagation = false
+					return
+				}
+				this.$emit('maskClick')
+				if (!this.mkclick) return
+				this.close()
+			},
+			/**
+			 * 顶部弹出样式处理
+			 */
+			top(type) {
+				this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
+				this.ani = ['slide-top']
+				this.transClass = {
+					position: 'fixed',
+					left: 0,
+					right: 0,
+					backgroundColor: this.bg
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+				this.$nextTick(() => {
+					if (this.messageChild && this.type === 'message') {
+						this.messageChild.timerClose()
+					}
+				})
+			},
+			/**
+			 * 底部弹出样式处理
+			 */
+			bottom(type) {
+				this.popupstyle = 'bottom'
+				this.ani = ['slide-bottom']
+				this.transClass = {
+					position: 'fixed',
+					left: 0,
+					right: 0,
+					bottom: 0,
+					paddingBottom: this.safeAreaInsets + 'px',
+					backgroundColor: this.bg
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			},
+			/**
+			 * 中间弹出样式处理
+			 */
+			center(type) {
+				this.popupstyle = 'center'
+				//微信小程序下,组合动画会出现文字向上闪动问题,再此做特殊处理
+				// #ifdef MP-WEIXIN
+					this.ani = ['fade']
+				// #endif
+				// #ifndef MP-WEIXIN
+					this.ani = ['zoom-out', 'fade']
+				// #endif
+				this.transClass = {
+					position: 'fixed',
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column',
+					/* #endif */
+					bottom: 0,
+					left: 0,
+					right: 0,
+					top: 0,
+					justifyContent: 'center',
+					alignItems: 'center'
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			},
+			left(type) {
+				this.popupstyle = 'left'
+				this.ani = ['slide-left']
+				this.transClass = {
+					position: 'fixed',
+					left: 0,
+					bottom: 0,
+					top: 0,
+					backgroundColor: this.bg,
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column'
+					/* #endif */
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			},
+			right(type) {
+				this.popupstyle = 'right'
+				this.ani = ['slide-right']
+				this.transClass = {
+					position: 'fixed',
+					bottom: 0,
+					right: 0,
+					top: 0,
+					backgroundColor: this.bg,
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column'
+					/* #endif */
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			}
+		}
+	}
+</script>
+<style lang="scss">
+	.uni-popup {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+
+		/* #endif */
+		&.top,
+		&.left,
+		&.right {
+			/* #ifdef H5 */
+			top: var(--window-top);
+			/* #endif */
+			/* #ifndef H5 */
+			top: 0;
+			/* #endif */
+		}
+
+		.uni-popup__wrapper {
+			/* #ifndef APP-NVUE */
+			display: block;
+			/* #endif */
+			position: relative;
+
+			/* iphonex 等安全区设置,底部安全区适配 */
+			/* #ifndef APP-NVUE */
+			// padding-bottom: constant(safe-area-inset-bottom);
+			// padding-bottom: env(safe-area-inset-bottom);
+			/* #endif */
+			&.left,
+			&.right {
+				/* #ifdef H5 */
+				padding-top: var(--window-top);
+				/* #endif */
+				/* #ifndef H5 */
+				padding-top: 0;
+				/* #endif */
+				flex: 1;
+			}
+		}
+	}
+
+	.fixforpc-z-index {
+		/* #ifndef APP-NVUE */
+		z-index: 999;
+		/* #endif */
+	}
+
+	.fixforpc-top {
+		top: 0;
+	}
+</style>

Разница между файлами не показана из-за своего большого размера
+ 34 - 0
components/uni-ui/uni-icons/uni-icons.vue


+ 187 - 0
components/uni-ui/uni-nav-bar/uni-nav-bar.vue

@@ -0,0 +1,187 @@
+<template>
+
+	<view class="uni-navbar">
+		<view :class="{'uni-navbar--fixed': fixed,'uni-navbar--shadow':border,'uni-navbar--border':border}" :style="{'background-color':backgroundColor}" class="uni-navbar__content">
+			<uni-status-bar v-if="statusBar" />
+			<view :style="{color:color}" class="uni-navbar__header uni-navbar__content_view">
+				<view class="uni-navbar__header-btns uni-navbar__content_view" @tap="onClickLeft">
+					<view v-if="leftIcon.length" class="uni-navbar__content_view">
+						<uni-icons :type="leftIcon" :color="color" size="24" />
+					</view>
+					<view v-if="leftText.length" :class="{'uni-navbar-btn-icon-left':!leftIcon.length}" class="uni-navbar-btn-text uni-navbar__content_view">{{ leftText }}</view>
+					<slot name="left" />
+				</view>
+				<view class="uni-navbar__header-container uni-navbar__content_view">
+					<view v-if="title.length" class="uni-navbar__header-container-inner uni-navbar__content_view">{{ title }}</view>
+					<!-- 标题插槽 -->
+					<slot />
+				</view>
+				<view :class="title.length?'uni-navbar__header-btns-right':''" class="uni-navbar__header-btns uni-navbar__content_view" @tap="onClickRight">
+					<view v-if="rightIcon.length" class="uni-navbar__content_view">
+						<uni-icons :type="rightIcon" :color="color" size="24" />
+					</view>
+					<!-- 优先显示图标 -->
+					<view v-if="rightText.length&&!rightIcon.length" class="uni-navbar-btn-text uni-navbar__content_view">{{ rightText }}</view>
+					<slot name="right" />
+				</view>
+			</view>
+		</view>
+		<view v-if="fixed" class="uni-navbar__placeholder">
+			<uni-status-bar v-if="statusBar" />
+			<view class="uni-navbar__placeholder-view" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import uniStatusBar from '../uni-status-bar/uni-status-bar.vue'
+	import uniIcons from '../uni-icons/uni-icons.vue'
+
+	export default {
+		name: 'UniNavBar',
+		components: {
+			uniStatusBar,
+			uniIcons
+		},
+		props: {
+			title: {
+				type: String,
+				default: ''
+			},
+			leftText: {
+				type: String,
+				default: ''
+			},
+			rightText: {
+				type: String,
+				default: ''
+			},
+			leftIcon: {
+				type: String,
+				default: ''
+			},
+			rightIcon: {
+				type: String,
+				default: ''
+			},
+			fixed: {
+				type: [Boolean, String],
+				default: false
+			},
+			color: {
+				type: String,
+				default: '#000000'
+			},
+			backgroundColor: {
+				type: String,
+				default: '#FFFFFF'
+			},
+			statusBar: {
+				type: [Boolean, String],
+				default: false
+			},
+			shadow: {
+				type: [String, Boolean],
+				default: true
+			},
+			border: {
+				type: [String, Boolean],
+				default: true
+			}
+		},
+		methods: {
+			onClickLeft() {
+				this.$emit('click-left')
+			},
+			onClickRight() {
+				this.$emit('click-right')
+			}
+		}
+	}
+</script>
+
+<style>
+	@charset "UTF-8";
+
+	.uni-navbar__content {
+		display: block;
+		position: relative;
+		width: 100%;
+		background-color: #fff;
+		overflow: hidden
+	}
+
+	.uni-navbar__content .uni-navbar__content_view {
+		display: flex;
+		align-items: center
+	}
+
+	.uni-navbar__header {
+		display: flex;
+		flex-direction: row;
+		width: 100%;
+		height: 44px;
+		line-height: 44px;
+		font-size: 16px
+	}
+
+	.uni-navbar__header-btns {
+		display: inline-flex;
+		flex-wrap: nowrap;
+		flex-shrink: 0;
+		width: 120upx;
+		padding: 0 12upx
+	}
+
+	.uni-navbar__header-btns:first-child {
+		padding-left: 0
+	}
+
+	.uni-navbar__header-btns:last-child {
+		width: 60upx
+	}
+
+	.uni-navbar__header-btns-right:last-child {
+		width: 120rpx;
+		text-align: right;
+		flex-direction: row-reverse
+	}
+
+	.uni-navbar__header-container {
+		width: 100%;
+		margin: 0 10upx
+	}
+
+	.uni-navbar__header-container-inner {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		font-size: 30upx
+	}
+
+	.uni-navbar__placeholder-view {
+		height: 44px
+	}
+
+	.uni-navbar--fixed {
+		position: fixed;
+		z-index: 998
+	}
+
+	.uni-navbar--shadow {
+		box-shadow: 0 1px 6px #ccc
+	}
+
+	.uni-navbar--border:after {
+		position: absolute;
+		z-index: 3;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		height: 1px;
+		content: '';
+		-webkit-transform: scaleY(.5);
+		transform: scaleY(.5);
+		background-color: #e5e5e5
+	}
+</style>

+ 187 - 0
components/uni-ui/uni-popup/uni-popup.vue

@@ -0,0 +1,187 @@
+<template>
+	<view v-if="showPopup" class="uni-popup">
+		<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask" @click="close(true)" />
+		<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper" @click="close(true)">
+			<view class="uni-popup__wrapper-box" @click.stop="clear">
+				<slot />
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'UniPopup',
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// 是否开启自定义
+			custom: {
+				type: Boolean,
+				default: false
+			},
+			// maskClick
+			maskClick: {
+				type: Boolean,
+				default: true
+			},
+			show: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				ani: '',
+				showPopup: false
+			}
+		},
+		watch: {
+			show(newValue) {
+				if (newValue) {
+					this.open()
+				} else {
+					this.close()
+				}
+			}
+		},
+		created() {},
+		methods: {
+			clear() {},
+			open() {
+				this.$emit('change', {
+					show: true
+				})
+				this.showPopup = true
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.ani = 'uni-' + this.type
+					}, 30)
+				})
+			},
+			close(type) {
+				if (!this.maskClick && type) return
+				this.$emit('change', {
+					show: false
+				})
+				this.ani = ''
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.showPopup = false
+					}, 300)
+				})
+			}
+		}
+	}
+</script>
+<style>
+	@charset "UTF-8";
+
+	.uni-popup {
+		position: fixed;
+		top: 0;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		z-index: 9998;
+		overflow: hidden
+	}
+
+	.uni-popup__mask {
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		z-index: 998;
+		background: rgba(0, 0, 0, .4);
+		opacity: 0
+	}
+
+	.uni-popup__mask.ani {
+		transition: all .3s
+	}
+
+	.uni-popup__mask.uni-bottom,
+	.uni-popup__mask.uni-center,
+	.uni-popup__mask.uni-top {
+		opacity: 1
+	}
+
+	.uni-popup__wrapper {
+		position: absolute;
+		z-index: 999;
+		box-sizing: border-box
+	}
+
+	.uni-popup__wrapper.ani {
+		transition: all .3s
+	}
+
+	.uni-popup__wrapper.top {
+		top: 0;
+		left: 0;
+		width: 100%;
+		transform: translateY(-100%)
+	}
+
+	.uni-popup__wrapper.bottom {
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		transform: translateY(100%)
+	}
+
+	.uni-popup__wrapper.center {
+		width: 100%;
+		height: 100%;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		transform: scale(1.2);
+		opacity: 0
+	}
+
+	.uni-popup__wrapper-box {
+		position: relative;
+		box-sizing: border-box
+	}
+
+	.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
+		/* padding: 30upx; */
+		background: #fff
+	}
+
+	.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
+		position: relative;
+		max-width: 80%;
+		max-height: 80%;
+		overflow-y: scroll
+	}
+
+	.uni-popup__wrapper.uni-custom.bottom .uni-popup__wrapper-box,
+	.uni-popup__wrapper.uni-custom.top .uni-popup__wrapper-box {
+		width: 100%;
+		max-height: 500px;
+		overflow-y: scroll
+	}
+
+	.uni-popup__wrapper.uni-bottom,
+	.uni-popup__wrapper.uni-top {
+		transform: translateY(0)
+	}
+
+	.uni-popup__wrapper.uni-center {
+		transform: scale(1);
+		opacity: 1
+	}
+</style>

+ 26 - 0
components/uni-ui/uni-status-bar/uni-status-bar.vue

@@ -0,0 +1,26 @@
+<template>
+	<view :style="{ height: statusBarHeight }" class="uni-status-bar">
+		<slot />
+	</view>
+</template>
+
+<script>
+	var statusBarHeight = uni.getSystemInfoSync().statusBarHeight + 'px'
+	export default {
+		name: 'UniStatusBar',
+		data() {
+			return {
+				statusBarHeight: statusBarHeight
+			}
+		}
+	}
+</script>
+
+<style>
+	.uni-status-bar {
+		display: block;
+		width: 100%;
+		height: 20px;
+		height: var(--status-bar-height);
+	}
+</style>

+ 568 - 0
components/wu-app-update/wu-app-update.vue

@@ -0,0 +1,568 @@
+<template>
+	<view class="wu-app-update-box">
+		<uni-popup ref="popup" type="center" :isMaskClick="false" @touchmove.stop.prevent>
+			<view class="content_popup" :style="{backgroundColor: bgColor}">
+				<!-- 关闭app -->
+				<wu-icon v-if="!isForceUpdata" class="close" name="close" :color="closeIconColor" :size="closeIconSize"
+					@click="closeUpdate"></wu-icon>
+				<!-- 版本提示 -->
+				<view class="version" :style="{color: versionColor}">v{{version}}</view>
+				<!-- 背景 -->
+				<image class="backgroundImg" width="100%" height="100%" src="../../static/appUploadAlertBoxBg.png">
+				</image>
+				<!-- 更新详细信息 -->
+				<view class="info center">
+					<text class="title" :style="{color: titleColor}">{{title}}</text>
+					<!-- 更新内容 -->
+					<scroll-view class="info_desc_scroll" :style="{color: contentColor}" scroll-y="true">
+						<rich-text :nodes="content"></rich-text>
+					</scroll-view>
+				</view>
+				<view class="footer" v-if="platform">
+					<button v-if="downloadSuccess && !wgtInstalled" class="btn" :style="btnStyle"
+						@click="installPackage" :loading="wgtInstalling" :disabled="wgtInstalling">
+						{{wgtInstalling ? wgtInstallingText : downloadSuccessText}}
+					</button>
+					<button v-else-if="wgtInstalled && isWGT" class="btn" :style="btnStyle" @click="restart">
+						{{wgtInstalledText}}
+					</button>
+					<!-- 更新进度 -->
+					<view class="progress-box flex f-c f-y-c" :style="{color: progressTextColor}"
+						v-else-if="downloading">
+						<progress class="progress" :percent="downLoadPercent" :activeColor="progressColor || themeColor"
+							show-info stroke-width="10" />
+						<view style="width:100%;font-size: 28rpx;display: flex;justify-content: space-around;">
+							<text>{{downLoadingText}}</text>
+							<text>({{downloadedSize}}M/{{packageFileSize || 0}}M)</text>
+						</view>
+					</view>
+					<!-- 选项 -->
+					<view v-else class="btns flex f-x-b">
+						<!-- IOS -->
+						<view v-if="platform == 'ios'" class="btn confirm" :style="btnStyle" @click="jumpToAppStore">
+							{{downloadBtnTextIOS}}
+						</view>
+						<!-- android -->
+						<view v-else class="btn confirm" :style="btnStyle" @click="updataApp">
+							{{downloadBtnTextAndroid}}
+						</view>
+					</view>
+					<!-- 短期内不在提醒 -->
+					<view v-if="!isForceUpdata" class="notRemind" @click="userNotRemind = !userNotRemind"
+						:class="{active: userNotRemind}">
+						<uni-icons :type="userNotRemind ? 'checkbox' : 'circle'" :size="notRemindIconSize"
+							:color="userNotRemind ? (notRemindIconActColor || themeColor) : notRemindIconNotActColor"></uni-icons>
+						<view class="remind-text"
+							:style="{color: userNotRemind ? notRemindTextActColor : notRemindTextNotActColor}">
+							{{intervalAlertUserUpdateDay}}日内不在提醒
+						</view>
+					</view>
+				</view>
+			</view>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	/**
+	 * wu-app-update app更新提示框
+	 * @description app更新提示框,支持热更新,强制更新,普通更新,暂不更新,后台下载,更新内容展示,进度条显示,ios跳转appstore等功能。
+	 * @property {String} titleColor 更新标题文字颜色(默认: #5e5e5e)。
+	 * @property {String} contentColor 更新内容文字颜色(默认: #878787)。
+	 * @property {String} themeColor 主题颜色。
+	 * @property {String} bgColor 背景色(默认: #fff)。
+	 * @property {String} versionColor 版本号字体颜色(默认: #fff)。
+	 * @property {String} closeIconColor 关闭图标颜色(默认: #fff)。
+	 * @property {String} closeIconSize 关闭图标大小(默认: 26)。
+	 * @property {String} notRemindIconSize 短期内不更新图标大小(默认: 22)。
+	 * @property {String} notRemindIconActColor 短期内不更新选中图标颜色(默认: '')。
+	 * @property {String} notRemindIconNotActColor 短期内不更新未选中图标颜色(默认: #9d9d9d)。
+	 * @property {String} notRemindTextActColor 短期内不更新选中文字颜色(默认: #6b6b6b)。
+	 * @property {String} notRemindTextNotActColor 短期内不更新未选中文字颜色(默认: #9d9d9d)。
+	 * @property {String} downloadBtnTextIOS 下载按钮ios文字(默认: 立即跳转更新)
+	 * @property {String} downloadBtnTextAndroid 下载按钮Android文字(默认: 立即升级)。
+	 * @property {String} downLoadingText 下载中文字提示(默认: 安装包下载中,请稍后)。
+	 * @property {String} downloadSuccessText 下载完成文字提示(默认: 下载完成,立即安装)。
+	 * @property {String} wgtInstallingText wgt安装中显示文字(默认: 正在安装....)。
+	 * @property {Number} wgtInstalledText wgt安装完成重启显示文字(默认: 安装完毕,点击重启)。
+	 * @property {Number} btnBgColor 按钮背景色(默认: '')。
+	 * @property {Number} btnColor 按钮文字颜色(默认: '#fff')。
+	 * @property {Number} progressColor 进度条颜色(默认: '')。
+	 * @property {Number} progressTextColor 进度条文字样式(默认: #4c4c4c)。
+	 * @property {Number} intervalAlertUserUpdateDay 提示用户更新的间隔时间 单位day(默认: 7)。
+	 * @example 
+	 */
+	import config from '../../common/update_config.js';
+	import checkVersion from '../../common/checkVersion.js';
+
+	export default {
+		name: 'wuAppUpdata',
+		props: config.props,
+		data() {
+			return {
+				// 更新的版本号
+				version: '',
+				// 系统环境
+				platform: '',
+				// 下载链接
+				downloadUrl: '',
+				// 跳转的应用市场列表
+				storeList: [],
+				// 是否wgt资源包
+				isWGT: false,
+				// 是否强制更新
+				isForceUpdata: false,
+				// 更新的标题
+				title: '',
+				// 更新的内容
+				content: ``,
+
+				// 下载下载状态
+				downloading: '',
+				// 是否下载完成
+				downloadSuccess: false,
+				// 下载进度
+				downLoadPercent: 0,
+				// 目前app已下载大小
+				downloadedSize: 0,
+				// app总大小
+				packageFileSize: 0,
+
+				// wgt是否安装中
+				wgtInstalling: false,
+				// wgt是否安装完成
+				wgtInstalled: false,
+
+				// 要安装的本地包地址
+				tempFilePath: false,
+				// 之前的安装的本地包地址
+				installForBeforeFilePath: null,
+				// 创建的下载任务
+				downloadTask: null,
+
+				// 用户上次拒绝的时间
+				userLastRefuseTime: uni.getStorageSync('userLastRefuseTime'),
+				// 用户是否短期内不更新
+				userNotRemind: false,
+			}
+		},
+		mounted() {
+			this.init();
+			let that = this;
+			uni.$on('check_update', function() {
+				that.init();
+			});
+		},
+		onShow() {},
+		methods: {
+
+			// 版本对比
+			compare(v1 = '0', v2 = '0') {
+				v1 = String(v1).split('.')
+				v2 = String(v2).split('.')
+				const minVersionLens = Math.min(v1.length, v2.length);
+
+				let result = 0;
+				for (let i = 0; i < minVersionLens; i++) {
+					const curV1 = Number(v1[i])
+					const curV2 = Number(v2[i])
+
+					if (curV1 > curV2) {
+						result = 1
+						break;
+					} else if (curV1 < curV2) {
+						result = -1
+						break;
+					}
+				}
+
+				if (result === 0 && (v1.length !== v2.length)) {
+					const v1BiggerThenv2 = v1.length > v2.length;
+					const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
+					for (let i = minVersionLens; i < maxLensVersion.length; i++) {
+						const curVersion = Number(maxLensVersion[i])
+						if (curVersion > 0) {
+							v1BiggerThenv2 ? result = 1 : result = -1
+							break;
+						}
+					}
+				}
+
+				return result;
+			},
+
+			// 获取更新内容片段
+			getContentHTML(content) {
+				let contentArr = content.split('\n');
+				return contentArr.map(item => `<p>${item}</p>`).join('\n')
+			},
+
+			// 跳转应用市场
+			checkStoreScheme() {
+				/**
+				 * 跳转应用市场逻辑
+				 * 如果本次更新设置了需要跳转的应用市场则从整个列表中筛选出来启用的应用市场
+				 * 按照设置的优先级(priority)从大到小排序
+				 * 并尝试跳转到所有应用市场
+				 * 如果都跳转失败的话则会显示失败
+				 */
+				// 可以跳转的应用市场
+				const canStoreList = (this.storeList || []).filter(item => item.enable)
+
+				let openSchemePromise;
+				if (canStoreList && canStoreList.length) {
+					canStoreList
+						.sort((cur, next) => next.priority - cur.priority)
+						.map(item => item.scheme)
+						.reduce((promise, cur, curIndex) => {
+							openSchemePromise = (promise || (promise = Promise.reject())).catch(() => {
+								return new Promise((resolve, reject) => {
+									plus.runtime.openURL(cur, (err) => {
+										reject(err)
+									})
+								})
+							})
+							return openSchemePromise
+						}, openSchemePromise)
+					return openSchemePromise
+				}
+
+				return Promise.reject()
+			},
+
+			// 初始化
+			init() {
+				console.log('xxxx');
+				// 如果在用户上次拒绝的时间存在
+				if (this.userLastRefuseTime) {
+					// 目标时间戳
+					let targetTime = this.userLastRefuseTime + this.intervalAlertUserUpdateDay * 24 * 60 * 60 * 1000;
+					// 现在时间戳
+					let nowTime = (new Date).getTime();
+					// 如果目标时间戳大于现在时间戳
+					if (targetTime > nowTime) {
+						// 并阻止执行
+						return;
+					} else {
+						// 清除拒绝时间
+						uni.removeStorageSync('userLastRefuseTime');
+					}
+				}
+
+				// 检查版本 需要更新时才会触发回调
+				checkVersion().then((res) => {
+					// 非静默更新时触发
+					if (!res.is_silently) {
+						// 读取下载好的包的缓存
+						const appDownLoadTempFilePath = uni.getStorageSync('appDownLoadTempFilePath');
+
+						// 更新的版本号
+						this.version = res.version;
+						// 系统环境
+						this.platform = res.appPlatform;
+						// 网络下载地址
+						this.downloadUrl = res.url;
+						// 跳转的应用市场列表
+						this.storeList = res.store_list || [];
+						// 更新内容 
+						this.content = this.getContentHTML(res.contents);
+						// 更新标题
+						this.title = res.title || '发现新版本';
+						// 是否强制更新
+						this.isForceUpdata = res.is_mandatory;
+						// 是否wgt资源包
+						this.isWGT = res.type == 'wgt';
+
+						// 如果已经有下载好的包
+						if (appDownLoadTempFilePath && this.compare(this.version, uni.getStorageSync(
+								'appDownLoadTempFilePathVersion')) == 0) {
+							this.tempFilePath = appDownLoadTempFilePath;
+							this.downloadSuccess = true;
+							this.installForBeforeFilePath = appDownLoadTempFilePath;
+						} else {
+							uni.removeStorageSync('appDownLoadTempFilePath');
+							uni.removeStorageSync('appDownLoadTempFilePathVersion');
+						}
+
+						// 打开更新提示
+						this.$refs.popup.open();
+					}
+				})
+			},
+
+			// 下载app
+			downloadPackage() {
+				if (!this.downloadTask) {
+					this.downloading = true;
+
+					//下载包
+					this.downloadTask = plus.downloader.createDownload(this.downloadUrl, {}, (download, status) => {
+						if (status == 200) {
+							this.downloadSuccess = true;
+							this.tempFilePath = download.filename;
+							uni.setStorageSync('appDownLoadTempFilePathVersion', this.version)
+							uni.setStorageSync('appDownLoadTempFilePath', this.tempFilePath);
+						}
+						// 清空下载进度
+						this.downLoadPercent = 0;
+						this.downloadedSize = 0;
+						this.packageFileSize = 0;
+						this.downloadTask = null;
+					});
+
+					this.downloadTask.start();
+
+					this.downloadTask.addEventListener("statechanged", (task, status) => {
+						switch (task.state) {
+							case 3:
+								// 更新下载进度
+								this.downLoadPercent = parseInt(task.downloadedSize / task.totalSize * 100);
+								this.downloadedSize = (task.downloadedSize / Math.pow(1024, 2)).toFixed(2);
+								this.packageFileSize = (task.totalSize / Math.pow(1024, 2)).toFixed(2);
+								break;
+						}
+					});
+				}
+			},
+
+			// 安装app
+			installPackage() {
+				// wgt资源包安装
+				if (this.isWGT) {
+					this.wgtInstalling = true;
+				}
+				plus.runtime.install(this.tempFilePath, {
+					force: true
+				}, async res => {
+					this.wgtInstalling = false;
+					this.wgtInstalled = true;
+				}, async err => {
+					this.downloadSuccess = false;
+					// 如果是安装之前的包,安装失败后删除之前的包
+					if (this.installForBeforeFilePath) {
+						await this.deleteSavedFile(this.installForBeforeFilePath)
+						this.installForBeforeFilePath = '';
+					}
+
+					uni.showLoading({
+						icon: 'none',
+						title: '更新失败,请重新下载',
+						mask: true
+					})
+				})
+			},
+
+			// 删除保存的文件
+			deleteSavedFile(tempFilePath) {
+				uni.removeStorageSync('appDownLoadTempFilePath')
+				uni.removeSavedFile({
+					tempFilePath
+				})
+			},
+
+			// 保存文件
+			saveFile(tempFilePath) {
+				return new Promise((resolve, reject) => {
+					uni.saveFile({
+						tempFilePath,
+						success({
+							savedFilePath
+						}) {
+							uni.setStorageSync('appDownLoadTempFilePath', tempFilePath)
+						},
+						complete() {
+							resolve()
+						}
+					})
+				})
+			},
+
+			// 重启应用
+			restart() {
+				this.wgtInstalled = false;
+				//更新完重启app
+				plus.runtime.restart();
+			},
+			// 跳转appstore
+			jumpToAppStore() {
+				// 请填入appid
+				plus.runtime.openURL(this.downloadUrl);
+			},
+			// 更新用户拒绝时间
+			updataUserRefuseTime() {
+				// 存储用户暂不升级的时间戳
+				this.userLastRefuseTime = (new Date).getTime();
+				uni.setStorageSync('userLastRefuseTime', this.userLastRefuseTime);
+			},
+			// 关闭更新框
+			closeUpdate() {
+				if (this.downloading) {
+					uni.showModal({
+						title: '是否取消下载?',
+						cancelText: '否',
+						confirmText: '是',
+						success: res => {
+							if (res.confirm) {
+								this.downloadTask && this.downloadTask.abort();
+								this.$refs.popup.close();
+							}
+						}
+					});
+				} else {
+					this.$refs.popup.close();
+					// 如果用户短期内不更新
+					if (this.userNotRemind) {
+						this.updataUserRefuseTime();
+					}
+				}
+
+				if (this.downloadSuccess) {
+					// 包已经下载完毕,稍后安装,将包保存在本地
+					this.saveFile(this.tempFilePath)
+				}
+			},
+			// 应用更新
+			updataApp() {
+				// 检查可跳转的应用市场 如果失败则走应用内更新
+				this.checkStoreScheme().catch(() => {
+					this.downloadPackage()
+				})
+			},
+		},
+		computed: {
+			btnStyle() {
+				return {
+					color: this.btnColor,
+					backgroundColor: this.btnBgColor || this.themeColor
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.wu-app-update-box {
+		:deep(.uni-popup) {
+			z-index: 1000;
+		}
+
+		.bg {
+			width: 100%;
+		}
+
+		.content_popup {
+			width: 590rpx;
+			border-radius: 10rpx;
+			background-size: cover;
+			background-repeat: no-repeat;
+			background-position: center;
+			box-sizing: border-box;
+			background-color: #FFF;
+			padding-bottom: 40rpx;
+			position: relative;
+
+			.close {
+				position: absolute;
+				right: 15rpx;
+				top: 15rpx;
+				z-index: 3;
+			}
+
+			.version {
+				position: absolute;
+				left: 40rpx;
+				top: 45rpx;
+				z-index: 3;
+				font-size: 75rpx;
+			}
+
+			.backgroundImg {
+				width: 100.1%;
+				height: 270rpx;
+				position: absolute;
+				top: -48rpx;
+			}
+
+			.info {
+				position: relative;
+				padding: 240rpx 40rpx 0;
+				z-index: 2;
+
+				.title {
+					font-size: 42rpx;
+				}
+
+				.info_desc_scroll {
+					margin-top: 20rpx;
+					font-size: 33rpx;
+					min-height: 200rpx;
+					max-height: 400rpx;
+					box-sizing: border-box;
+					line-height: 1.3;
+
+					p:not(:last-child) {
+						margin-bottom: 12rpx;
+					}
+				}
+			}
+
+			.footer {
+				padding: 0 30rpx;
+
+				.progress-box {
+					width: 100%;
+					margin-top: 25rpx;
+				}
+
+				:deep(.progress) {
+					width: 90%;
+					height: 40rpx;
+					margin-bottom: 5rpx;
+
+					.uni-progress-bar {
+						border-radius: 35rpx;
+
+						.uni-progress-inner-bar {
+							border-radius: 35rpx;
+						}
+					}
+				}
+
+				.btn {
+					margin-top: 35rpx;
+					height: 75rpx;
+					line-height: 75rpx;
+					border-radius: 14rpx;
+					font-size: 34rpx;
+					font-weight: 400;
+					text-align: center;
+					width: 100%;
+				}
+
+				.notRemind {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					margin-top: 15rpx;
+
+					.remind-text {
+						color: #9d9d9d;
+						font-size: 30rpx;
+						margin-left: 3rpx;
+						transition: color 80ms linear;
+					}
+				}
+
+			}
+		}
+
+		.close-img {
+			width: 70rpx;
+			height: 70rpx;
+			z-index: 1000;
+			position: absolute;
+			bottom: -120rpx;
+			left: calc(50% - 70rpx / 2);
+		}
+	}
+</style>

+ 6 - 0
main.js

@@ -5,7 +5,13 @@ import pubc from '@/common/public.js'
 // 多语言本地化
 import VueI18n from 'vue-i18n'
 import messages from './locale/index'
+import ClUni from "@/uni_modules/cl-uni";
 
+
+import emoji from "./common/emoji.js"
+Vue.prototype.emoji = emoji
+
+Vue.use(ClUni);
 let i18nConfig = {
 	locale: uni.getLocale(),
 	messages

+ 87 - 84
package.json

@@ -1,86 +1,89 @@
 {
-  "id": "uni-load-more",
-  "displayName": "uni-load-more 加载更多",
-  "version": "1.3.3",
-  "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
-  "keywords": [
-    "uni-ui",
-    "uniui",
-    "加载更多",
-    "load-more"
-],
-  "repository": "https://github.com/dcloudio/uni-ui",
-  "engines": {
-    "HBuilderX": ""
-  },
-  "directories": {
-    "example": "../../temps/example_temps"
-  },
-  "dcloudext": {
-    "category": [
-      "前端组件",
-      "通用组件"
-    ],
-    "sale": {
-      "regular": {
-        "price": "0.00"
-      },
-      "sourcecode": {
-        "price": "0.00"
-      }
-    },
-    "contact": {
-      "qq": ""
-    },
-    "declaration": {
-      "ads": "无",
-      "data": "无",
-      "permissions": "无"
-    },
-    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
-  },
-  "uni_modules": {
-    "dependencies": ["uni-scss"],
-    "encrypt": [],
-    "platforms": {
-      "cloud": {
-        "tcb": "y",
-        "aliyun": "y"
-      },
-      "client": {
-        "App": {
-          "app-vue": "y",
-          "app-nvue": "y"
-        },
-        "H5-mobile": {
-          "Safari": "y",
-          "Android Browser": "y",
-          "微信浏览器(Android)": "y",
-          "QQ浏览器(Android)": "y"
-        },
-        "H5-pc": {
-          "Chrome": "y",
-          "IE": "y",
-          "Edge": "y",
-          "Firefox": "y",
-          "Safari": "y"
-        },
-        "小程序": {
-          "微信": "y",
-          "阿里": "y",
-          "百度": "y",
-          "字节跳动": "y",
-          "QQ": "y"
-        },
-        "快应用": {
-          "华为": "u",
-          "联盟": "u"
-        },
-        "Vue": {
-            "vue2": "y",
-            "vue3": "y"
-        }
-      }
-    }
-  }
+	"id": "uni-load-more",
+	"displayName": "uni-load-more 加载更多",
+	"version": "1.3.3",
+	"description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
+	"keywords": [
+		"uni-ui",
+		"uniui",
+		"加载更多",
+		"load-more"
+	],
+	"repository": "https://github.com/dcloudio/uni-ui",
+	"engines": {
+		"HBuilderX": ""
+	},
+	"directories": {
+		"example": "../../temps/example_temps"
+	},
+	"dependencies": {
+		"cl-uni": "^1.8.20"
+	},
+	"dcloudext": {
+		"category": [
+			"前端组件",
+			"通用组件"
+		],
+		"sale": {
+			"regular": {
+				"price": "0.00"
+			},
+			"sourcecode": {
+				"price": "0.00"
+			}
+		},
+		"contact": {
+			"qq": ""
+		},
+		"declaration": {
+			"ads": "无",
+			"data": "无",
+			"permissions": "无"
+		},
+		"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+	},
+	"uni_modules": {
+		"dependencies": ["uni-scss", "cl-uni"],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				},
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				}
+			}
+		}
+	}
 }

+ 18 - 0
pages.json

@@ -159,6 +159,18 @@
 				"navigationBarTitleText": "我的X币",
 				"navigationBarBackgroundColor": "#161616"
 			}
+		}, {
+			"path": "pages/chat/detail",
+			"style": {
+				"navigationBarTitleText": "聊天",
+				"navigationBarBackgroundColor": "#161616"
+			}
+		}, {
+			"path": "pages/chat/message",
+			"style": {
+				"navigationBarTitleText": "会话",
+				"navigationBarBackgroundColor": "#161616"
+			}
 		},
 		{
 			"path": "pages/w3/invite",
@@ -254,6 +266,12 @@
 		"navigationBarBackgroundColor": "#161616",
 		"backgroundColor": "#161616"
 	},
+	"easycom": {
+		"autoscan": true,
+		"custom": {
+			"cl-(.*)": "uni_modules/cl-uni/components/cl-$1/cl-$1.vue"
+		}
+	},
 	"tabBar": {
 		// "custom":true,
 		"color": "#999999",

+ 49 - 0
pages/chat/components/delete-conversation.vue

@@ -0,0 +1,49 @@
+<template>
+	<uni-popup ref="popup" type="center">
+		<view class="main" @click="deletes()">
+			删除会话
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
+	export default {
+		components:{
+			uniPopup
+		},
+		data(){
+			return{
+				page: 1,
+				list:[],
+				groupID: "",
+				hasMore: true,
+				conversationID: ""
+			}
+		},
+		methods:{
+			async open(conversationID){
+				this.$refs.popup.open();
+				this.conversationID = conversationID
+				console.log(conversationID)
+			},
+			deletes(){
+				this.TIM.deleteConversations(this.conversationID)
+				this.$refs.popup.close(false)
+				uni.showToast({
+					title:"操作成功"
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.main{
+	width: 400rpx;
+	height: 100rpx;
+	line-height: 100rpx;
+	text-align: center;
+	color: #8e8e8e;
+}
+</style>

+ 75 - 0
pages/chat/components/emoji.vue

@@ -0,0 +1,75 @@
+<template>
+	<view
+		class="chat-emoji"
+		:class="[
+			{
+				'is-show': visible
+			}
+		]"
+	>
+		<scroll-view scroll-y class="scroller">
+			<cl-row>
+				<cl-col :span="4" v-for="(item, index) in emoji" :key="index">
+					<view class="block" @tap="select(item)">
+						{{item}}
+					</view>
+				</cl-col>
+			</cl-row>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+
+export default {
+	props: {
+		visible: Boolean
+	},
+
+	data() {
+		return {
+		};
+	},
+
+	methods: {
+		select(e) {
+			this.$emit("select", e);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.chat-emoji {
+	height: 0;
+	box-sizing: border-box;
+	transition: height 0.3s;
+
+	&.is-show {
+		height: 400rpx;
+		padding: 10rpx 0 0 0;
+	}
+
+	.scroller {
+		height: 100%;
+
+		.block {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			border-radius: 10rpx;
+			height: 100rpx;
+
+			&:active {
+				background-color: #f7f7f7;
+			}
+
+			image {
+				display: inline-block;
+				height: 60rpx;
+				width: 60rpx;
+			}
+		}
+	}
+}
+</style>

+ 53 - 0
pages/chat/components/icon-voice.vue

@@ -0,0 +1,53 @@
+<template>
+	<view class="icon-voice">
+		<text class="icon" :class="[`chat-${name}${index}`]"></text>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		play: Boolean,
+		name: String,
+	},
+
+	data() {
+		return {
+			timer: null,
+			index: "",
+		};
+	},
+
+	watch: {
+		play(val) {
+			clearInterval(this.timer);
+
+			if (val) {
+				this.index = 1;
+
+				this.timer = setInterval(() => {
+					if (this.index == 1) {
+						this.index = "";
+					} else {
+						this.index += 1;
+					}
+				}, 500);
+			} else {
+				this.index = "";
+			}
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.icon-voice {
+	display: inline-block;
+
+	.icon {
+		font-size: 50rpx;
+		position: relative;
+		top: 4rpx;
+	}
+}
+</style>

+ 112 - 0
pages/chat/components/member.vue

@@ -0,0 +1,112 @@
+<template>
+	<uni-popup ref="popup" type="bottom">
+		<view class="main">
+			<scroll-view scroll-y="true" class="list" @scrolltolower="loadMore">
+				<view class="text">群成员列表</view>
+				<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item.userID)">
+					<view class="left">
+						<image class="avatar" lazy-load="true" :src="item.avatar"></image>
+						<view class="nick">{{item.nick}}</view>
+						<image class="gender"  v-if="item.gender == 2" src="@/static/icon/woman.png" mode="aspectFill" />
+						<image class="gender" v-if="item.gender == 1" src="@/static/icon/man.png" mode="aspectFill" />
+					</view>
+					<image class="hi" src="@/static/images/article/dazhaohu_2.png" mode="aspectFill" @click="navigateToChatDetail(item.userID, item.nick)"/> 
+				</view>
+			</scroll-view>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
+	export default {
+		components:{
+			uniPopup
+		},
+		data(){
+			return{
+				page: 1,
+				list:[],
+				groupID: "",
+				hasMore: true,
+			}
+		},
+		methods:{
+			async open(id){
+				this.groupID = id;
+				this.page = 1;
+				this.hasMore = true
+				this.$refs.popup.open();
+				let res = await this.TIM.getGroupMemberList(id, this.page)
+				this.list = res.data.memberList;
+			},
+			async loadMore(){
+				if(!this.hasMore){
+					return
+				}
+				this.page = this.page + 1;
+				let res = await this.TIM.getGroupMemberList(this.groupID, this.page)
+				if(res.data.memberList.length == 0){
+					this.hasMore = false
+				}
+				this.list = this.list.concat(res.data.memberList)
+			},
+			toDetail(uid) {
+				uni.navigateTo({
+					url: "/pages/detail/index?uid=" + uid,
+				});
+			},
+			navigateToChatDetail(userID, nickName){
+				let jsonUserID = encodeURIComponent(JSON.stringify(userID));
+				let jsonNickName = encodeURIComponent(JSON.stringify(nickName));
+				let jsonConversationID = encodeURIComponent(JSON.stringify("C2C" + userID));
+				let jsonConversationType = encodeURIComponent(JSON.stringify("C2C"));
+				uni.navigateTo({
+					url:"/pages/chat/detail?userID=" + jsonUserID + "&nickName=" + jsonNickName + "&conversationID=" + jsonConversationID + "&conversationType=" + jsonConversationType
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.main{
+	height: 60vh;
+	padding: 50rpx 50rpx;
+}
+.text{
+	
+}
+.list{
+	height: calc(100% -100rpx);
+	.item{
+		display: flex;
+		justify-content:space-between;
+		align-items:center;
+		margin-bottom: 20rpx;
+		.left{
+			display: flex;
+			align-items:center;
+			.nick{
+				margin-left: 20rpx;
+			}
+			.avatar{
+				border-radius: 100%;
+				width: 75rpx;
+				height: 75rpx;
+			}
+			.gender{
+				margin-left: 20rpx;
+				width: 30rpx;
+				height: 30rpx;
+			}
+		}
+		.hi{
+			width: 45rpx;
+			height: 45rpx;
+		}
+		
+	}
+	
+}
+</style>

+ 551 - 0
pages/chat/components/message.vue

@@ -0,0 +1,551 @@
+<template>
+	<view class="chat-message">
+		<view class="chat-message-item" v-for="(item, index) in flist" :key="`${item.from}-${index}`" :class="[
+				`${item._isMy ? 'is-right' : 'is-left'}`,
+				`is-${item._mode}`,
+				{
+					'is-animation': item.animation,
+				},
+			]">
+			<!-- 发言时间 -->
+			<view class="date" v-if="item._date">
+				<text>{{ item._date }}</text>
+			</view>
+
+			<!-- 内容 -->
+			<view class="main">
+				<!-- 头像 -->
+				<view class="avatar" @tap="toDetail(item.from)">
+					<image :src="item.avatarUrl" mode="aspectFit" />
+				</view>
+
+				<!-- 详细 -->
+				<view class="det">
+					<template v-if="conversationType=='GROUP' && !item._isMy">
+						<view class="name-sex">
+							<view class="name">{{item.nick}}</view>
+							<view class="man" v-if="item.sex == 0">
+								<image src="@/static/icon/brief-icon-1.png" mode="aspectFill" />
+							</view>
+							<view class="woman" v-else-if="item.sex == 1">
+								<image src="@/static/icon/brief-icon-2.png" mode="aspectFill" />
+							</view>
+						</view>
+					</template>
+
+					<!-- 内容 -->
+					<view class="content" @tap="tapCont(item)" @longpress="longPressItem(item)">
+						<!-- 文本 -->
+						<template v-if="item._mode === 'text'">
+							{{ item.payload.text }}
+						</template>
+
+						<!-- 图片 -->
+						<template v-else-if="item._mode === 'image'">
+							<cl-loading-mask color="#000" :loading="item.loading" :text="`${item.progress}%`">
+								<image mode="widthFix" :src="item.payload.imageInfoArray[0].imageUrl"></image>
+							</cl-loading-mask>
+						</template>
+
+						<!-- 表情 -->
+						<template v-else-if="item._mode === 'emoji'">
+							<image :src="item.content.imageUrl"></image>
+						</template>
+
+						<!-- 语音 -->
+						<template v-else-if="item._mode === 'voice'">
+							<icon-voice v-if="item._isMy" name="icon-voice-right" :play="item.isPlay"></icon-voice>
+							<!-- <text class="duration">{{ item.payload.second }}</text> -->
+							<text style="color:#007AFF;">语音消息,点击收听</text>
+							<icon-voice v-if="!item._isMy" name="icon-voice-left" :play="item.isPlay"></icon-voice>
+						</template>
+
+						<!-- 视频 -->
+						<template v-else-if="item._mode === 'video'">
+							<cl-loading-mask color="#000" :loading="item.loading" :text="`${item.progress}%`">
+								<view class="item">
+									<image class="cover" :src="item.payload.thumbUrl" mode="aspectFill" />
+									<text v-if="!item.loading" class="chat-iconfont icon-play"></text>
+								</view>
+							</cl-loading-mask>
+						</template>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import dayjs from "../../../uni_modules/cl-uni/utils/dayjs";
+	import IconVoice from "./icon-voice";
+
+	// 音频
+	const innerAudioContext = uni.createInnerAudioContext();
+
+	// 模式,对应 contentType
+	const modes = ["text", "image", "emoji", "voice", "video"];
+
+	export default {
+		components: {
+			IconVoice,
+		},
+
+		props: {
+			/*
+			 * text: 文本
+			 * duration: 时长
+			 * videoUrl: 视频地址
+			 * voiceUrl: 语音地址
+			 * videoCoverUrl: 视频封面
+			 * imageUrl: 图片地址
+			 */
+			list: Array,
+			conversationType: String,
+		},
+
+		data() {
+			return {
+				voice: {
+					timer: null,
+				},
+				// 登录的用户信息
+				userInfo: {
+					userId: 1,
+					name: 'ai',
+					avatarUrl: ''
+				},
+			};
+		},
+
+		computed: {
+			flist() {
+				let date = "";
+
+				const {
+					avatarUrl,
+					name,
+					userId
+				} = this.userInfo;
+
+				let temp = this.list.map((e) => {
+					// 处理日期
+					e._date = date ?
+						dayjs(e.create_time).isBefore(dayjs(date).add(5, "minute")) ?
+						"" :
+						e.create_time :
+						e.create_time;
+
+					date = e.create_time;
+
+					// 是否是自己
+					e._isMy = e.from == this.userInfo.userId;
+
+					if (e._isMy) {
+						e.avatarUrl = e.avatar;
+						e.nick = name;
+						e.userId = userId;
+					} else {
+						e.avatarUrl = e.avatar;
+					}
+
+					// 消息模型
+					e._mode = e.type;
+					switch (e.type) {
+						case "TIMTextElem":
+							e._mode = "text";
+							break;
+						case "TIMImageElem":
+							e._mode = "image";
+							break;
+						case "TIMVideoFileElem":
+							e._mode = "video";
+							break;
+						case "TIMSoundElem":
+							e._mode = "voice";
+							break;
+						default:
+							e._mode = "error";
+							break;
+					}
+
+					if (e._mode == "error") {
+						return null;
+					}
+
+					if (e.isRevoked) {
+						return null;
+					}
+
+					return e;
+				});
+				let result = []
+				for (let i = 0; i < temp.length; i++) {
+					if (temp[i] != null) {
+						result.push(temp[i])
+					}
+				}
+
+				return result;
+			},
+		},
+
+		filters: {
+			duration(val) {
+				return Math.ceil((val || 1) / 1000);
+			},
+		},
+
+		destroyed() {
+			this.voicePause();
+		},
+
+		methods: {
+			// 点击内容
+			tapCont(item) {
+				console.log(item);
+				switch (item._mode) {
+					case "image":
+						this.previewImage(item);
+						break;
+					case "video":
+						this.videoPlay(item);
+						break;
+					case "voice":
+						this.voicePlay(item);
+						break;
+				}
+			},
+			// 前往详情
+			toDetail(uid) {
+				if (uid == this.userInfo.userId) {
+					return;
+				}
+				uni.navigateTo({
+					url: "/pages/detail/index?uid=" + uid,
+				});
+			},
+
+			// 长按
+			longPressItem(item) {
+				switch (item._mode) {
+					case "text":
+						uni.setClipboardData({
+							data: item.content.text,
+						});
+						break;
+				}
+			},
+
+			// 图片预览
+			previewImage(item) {
+				uni.previewImage({
+					current: item.payload.imageInfoArray[0].imageUrl,
+					urls: this.list.filter((e) => e._mode == "image").map((e) => e.payload.imageInfoArray[0]
+						.imageUrl),
+				});
+			},
+
+			// 视频播放
+			videoPlay(item) {
+				this.$root.video.url = item.payload.videoUrl;
+				this.$root.video.visible = true;
+			},
+
+			// 播放录音
+			voicePlay(item) {
+				// 设置播放状态
+				this.list.map((e) => {
+					this.$set(e, "isPlay", e.ID == item.ID ? e.isPlay : false);
+				});
+
+				item.isPlay = !item.isPlay;
+
+				if (item.isPlay) {
+					// 开始播放
+					innerAudioContext.src = item.payload.url;
+					innerAudioContext.play();
+				} else {
+					// 暂停播放
+					this.voicePause();
+				}
+
+				// 清除计时器
+				clearTimeout(this.voice.timer);
+
+				// x 秒后暂停
+				this.voice.timer = setTimeout(() => {
+					item.isPlay = false;
+				}, item.payload.second * 1000 || 1000);
+			},
+
+			// 暂停播放
+			voicePause() {
+				innerAudioContext.stop();
+				this.list.map((e) => {
+					e.isPlay = false;
+				});
+			},
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	@keyframes fadeInRight {
+		from {
+			opacity: 0;
+			transform: translate3d(100%, 0, 0);
+		}
+
+		to {
+			opacity: 1;
+			transform: translate3d(0, 0, 0);
+		}
+	}
+
+	@keyframes fadeInLeft {
+		from {
+			opacity: 0;
+			transform: translate3d(-100%, 0, 0);
+		}
+
+		to {
+			opacity: 1;
+			transform: translate3d(0, 0, 0);
+		}
+	}
+
+	.chat-message {
+		&-item {
+			font-size: 26rpx;
+			padding: 20rpx;
+
+			.date {
+				text-align: center;
+				margin: 10rpx 0 40rpx 0;
+
+				text {
+					background-color: #dadada;
+					font-size: 24rpx;
+					color: #fff;
+					border-radius: 6rpx;
+					padding: 4rpx 10rpx;
+					letter-spacing: 1rpx;
+				}
+			}
+
+			.main {
+				display: flex;
+
+				.avatar {
+					flex-shrink: 0;
+					height: 80rpx;
+
+					image {
+						height: 80rpx;
+						width: 80rpx;
+					}
+				}
+
+				.det {
+					display: flex;
+					flex-direction: column;
+					max-width: 60%;
+
+
+
+					.content {
+						display: inline-block;
+						padding: 24rpx 36rpx;
+						border-radius: 16rpx;
+						box-sizing: border-box;
+						position: relative;
+						font-size: 28rpx !important;
+					}
+				}
+			}
+
+			&.is-left {
+				.main {
+					.det {
+						margin-left: 20rpx;
+						align-items: flex-start;
+
+						.content {
+							background: #FEFDFE;
+							border-radius: 30rpx;
+							font-size: 35rpx;
+							font-weight: 400;
+							color: #1E1D1E;
+						}
+					}
+				}
+			}
+
+			&.is-right {
+				.main {
+					flex-direction: row-reverse;
+
+					.det {
+						margin-right: 20rpx;
+						align-items: flex-end;
+
+						.content {
+							background: #B6E98E;
+							border-radius: 30rpx;
+							font-size: 35rpx;
+							font-weight: 400;
+							color: #1E1D1E;
+						}
+					}
+				}
+
+				&.is-voice {
+					.content {
+						justify-content: flex-end;
+					}
+				}
+			}
+
+			&.is-animation {
+				&.is-left {
+					animation: fadeInLeft 0.5s ease both;
+				}
+
+				&.is-right {
+					animation: fadeInRight 0.5s ease both;
+				}
+			}
+
+			&.is-text {
+				.content {
+					max-width: 100%;
+					min-width: 80rpx;
+					word-wrap: break-word;
+				}
+			}
+
+			&.is-text,
+			&.is-voice {
+				.content {
+					padding: 20rpx;
+					line-height: 40rpx;
+					letter-spacing: 1rpx;
+				}
+			}
+
+			&.is-emoji {
+				.content {
+					padding: 20rpx;
+
+					image {
+						height: 40rpx;
+						width: 40rpx;
+					}
+				}
+			}
+
+			&.is-voice {
+				.det {
+					.content {
+						display: flex;
+						align-items: center;
+						// width: 130rpx;
+
+						.duration {
+							&::after {
+								content: '"';
+							}
+						}
+					}
+				}
+			}
+
+			&.is-video {
+				.item {
+					height: 300rpx;
+					width: 500rpx;
+					text-align: center;
+					line-height: 300rpx;
+					position: relative;
+					overflow: hidden;
+
+					.cover {
+						height: 100%;
+						width: 100%;
+						position: absolute;
+						left: 0;
+						top: 0;
+						border-radius: 10rpx;
+					}
+
+					.chat-iconfont {
+						color: gray;
+						font-size: 100rpx;
+						position: relative;
+					}
+				}
+			}
+
+			&.is-image {
+				.main {
+					.det {
+						.content {
+							background-color: #fff;
+
+							image {
+								display: block;
+								max-width: 300rpx;
+								border-radius: 16rpx;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	.name-sex {
+		display: flex;
+		align-items: center;
+	}
+
+	.man {
+		image {
+			width: 17rpx;
+			height: 17rpx;
+		}
+
+		background-color: #58bcff;
+		border-radius: 50%;
+		width: 25rpx;
+		height: 25rpx;
+		justify-content: center;
+		align-items: center;
+		display: flex;
+		margin-left: 10rpx;
+	}
+
+	.woman {
+		image {
+			width: 17rpx;
+			height: 17rpx;
+		}
+
+		background-color: #ff5a70;
+		border-radius: 50%;
+		width: 25rpx;
+		height: 25rpx;
+		justify-content: center;
+		align-items: center;
+		display: flex;
+		margin-left: 10rpx;
+	}
+
+	.name {
+		margin-bottom: 10rpx;
+		font-size: 24rpx;
+		font-weight: 400;
+		color: #bbccd8;
+	}
+</style>

+ 170 - 0
pages/chat/components/tools.vue

@@ -0,0 +1,170 @@
+<template>
+	<view class="chat-tools" :class="[
+			{
+				'is-show': visible
+			}
+		]">
+		<swiper>
+			<swiper-item>
+				<cl-row>
+					<cl-col :span="6">
+						<view class="block" @tap="onImage">
+							<view class="icon">
+								<text class="chat-iconfont icon-tool-image"></text>
+							</view>
+							<text class="label">照片</text>
+						</view>
+					</cl-col>
+
+					<cl-col :span="6">
+						<view class="block" @tap="onVideo">
+							<view class="icon">
+								<text class="chat-iconfont icon-tool-video"></text>
+							</view>
+							<text class="label">视频</text>
+						</view>
+					</cl-col>
+
+					<cl-col :span="6">
+						<view class="block" @tap="onShoot">
+							<view class="icon">
+								<text class="chat-iconfont icon-tool-shoot"></text>
+							</view>
+							<text class="label">拍摄</text>
+						</view>
+					</cl-col>
+				</cl-row>
+			</swiper-item>
+		</swiper>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			visible: Boolean,
+			userId: String,
+			conversationType: String,
+		},
+
+		methods: {
+			upload(file, {
+				contentType
+			}) {
+				let key = "";
+
+				switch (contentType) {
+					case 1:
+						key = "imageUrl";
+						break;
+					case 4:
+						key = "videoUrl";
+						break;
+				}
+
+				const data = {
+					fromId: 1, // 实际使用登录者id
+					contentType,
+					loading: true,
+					progress: 0,
+					content: {
+						[key]: file.path
+					}
+				};
+
+				this.$root.append(data);
+
+				setTimeout(() => {
+					data.progress = 100;
+					data.loading = false;
+				}, 300);
+			},
+
+			onImage() {
+				console.log("vvvvvvv");
+				uni.chooseImage({
+					count: 1,
+					sourceType: ["album"],
+					sizeType: ['original', 'compressed'],
+					success: res => {
+						console.log(res);
+						this.TIM.sendImageMessage(res, this.userId, this.conversationType);
+					}
+				});
+			},
+
+			onVideo() {
+				uni.chooseVideo({
+					count: 1,
+					sourceType: ["album"],
+					success: res => {
+						this.TIM.sendVideoMessage(res, this.userId, this.conversationType);
+					}
+				});
+			},
+
+			onShoot() {
+				uni.chooseImage({
+					sourceType: ["camera"],
+					success: res => {
+						this.upload(res.tempFiles[0], {
+							contentType: 1
+						});
+					}
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	@import "../../../static/css/iconfont.scss";
+
+	.chat-tools {
+		background-color: #f6f7f9;
+		height: 0;
+		transition: height 0.3s;
+
+		&.is-show {
+			height: 400rpx;
+		}
+
+		swiper {
+			height: 100%;
+		}
+
+		.block {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			height: 200rpx;
+
+			.icon {
+				height: 100rpx;
+				width: 100rpx;
+				border-radius: 10rpx;
+				background-color: #fff;
+				text-align: center;
+				line-height: 100rpx;
+
+				.chat-iconfont {
+					font-size: 60rpx;
+					color: #d9dbde;
+				}
+			}
+
+			.label {
+				margin-top: 15rpx;
+				font-size: 24rpx;
+				color: #333;
+			}
+
+			&:active {
+				.chat-iconfont {
+					color: #999;
+				}
+			}
+		}
+	}
+</style>

+ 252 - 0
pages/chat/components/user.vue

@@ -0,0 +1,252 @@
+<template>
+	<view class="chat-user">
+		<view class="top">
+			<view class="main">
+				<!-- 头像 -->
+				<view class="avatar-group">
+					<image src="@/static/images/chat-1.png" mode="aspectFill" class="avatar" />
+					<view class="sex">
+						<image src="@/static/icon/brief-icon-1.png" mode="aspectFill" />
+					</view>
+				</view>
+
+				<!-- 信息 -->
+				<view class="info">
+					<text class="name">孙漂亮</text>
+					<view class="num">
+						<image src="@/static/icon/chat-icon.png" mode="aspectFill" />
+						<text>56888人</text>
+					</view>
+				</view>
+
+				<!-- 按钮 -->
+				<cl-button round>+关注</cl-button>
+			</view>
+
+			<!-- 更多 -->
+			<view class="more" @tap="shareShow()"><image src="@/static/icon/chat-more.png" mode="aspectFill" /></view>
+		</view>
+
+		<!-- 加入房间列表 -->
+		<view class="list">
+			<cl-row :gutter="70">
+				<cl-col :span="6" v-for="(item, index) in list" :key="index">
+					<view class="item" @tap="join(item)">
+						<!-- 头像 -->
+						<view class="avatar-group">
+							<image
+								:src="item.avatar"
+								mode="aspectFill"
+								class="avatar"
+								:class="{ 'not-msg': !item.name }"
+							/>
+							<view class="homeowner" v-if="item.isHomeowner == true">房主</view>
+						</view>
+						<text class="name">{{ item.name }}</text>
+					</view>
+				</cl-col>
+			</cl-row>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			userInfo: {
+				avatar: require("@/static/images/chat-1.png"),
+				name: "孙漂亮",
+				isHomeowner: false,
+			},
+			list: [
+				{
+					avatar: require("@/static/images/chat-1.png"),
+					name: "孙漂亮",
+					isHomeowner: true,
+				},
+				{
+					avatar: require("@/static/images/chat-2.png"),
+					name: "每天都很棒",
+					isHomeowner: false,
+				},
+				{
+					avatar: require("@/static/images/chat-3.png"),
+					name: "吴得得儿~",
+					isHomeowner: false,
+				},
+				{
+					avatar: require("@/static/images/not-msg.png"),
+					name: "",
+					isHomeowner: false,
+				},
+				{
+					avatar: require("@/static/images/not-msg.png"),
+					name: "",
+					isHomeowner: false,
+				},
+				{
+					avatar: require("@/static/images/not-msg.png"),
+					name: "",
+					isHomeowner: false,
+				},
+				{
+					avatar: require("@/static/images/not-msg.png"),
+					name: "",
+					isHomeowner: false,
+				},
+				{
+					avatar: require("@/static/images/not-msg.png"),
+					name: "",
+					isHomeowner: false,
+				},
+			],
+		};
+	},
+	methods: {
+		join(item) {
+			if (!item.name) {
+				item.name = this.userInfo.name;
+				item.avatar = this.userInfo.avatar;
+			}
+		},
+		shareShow(){
+			console.log("abcccc");
+			uni.$emit('updateShareShow',{})
+		}
+	},
+};
+</script>
+
+<style lang="scss">
+.chat-user {
+	padding: 10rpx 30rpx 0 30rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		margin-bottom: 50rpx;
+		.main {
+			display: flex;
+			align-items: center;
+			height: 84rpx;
+			padding: 0 18rpx 0 12rpx;
+			background: rgba(0, 0, 0, 0.5);
+			border-radius: 46rpx;
+			.avatar-group {
+				position: relative;
+				margin-right: 18rpx;
+				.avatar {
+					position: relative;
+					top: 4rpx;
+					width: 72rpx;
+					height: 72rpx;
+					border-radius: 50%;
+				}
+				.sex {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					position: absolute;
+					right: 0;
+					bottom: 0;
+					width: 26rpx;
+					height: 26rpx;
+					background: #58bcff;
+					border-radius: 50%;
+					image {
+						width: 18rpx;
+						height: 18rpx;
+					}
+				}
+			}
+			.info {
+				margin-right: 20rpx;
+				.name {
+					font-size: 28rpx;
+					font-weight: 400;
+					color: #ffffff;
+				}
+				.num {
+					image {
+						width: 22rpx;
+						height: 18rpx;
+						margin-right: 6rpx;
+					}
+					text {
+						font-size: 22rpx;
+						font-weight: 400;
+						color: #a8a8a8;
+					}
+				}
+			}
+			/deep/.cl-button {
+				width: 108rpx;
+				height: 48rpx;
+				background: linear-gradient(92deg, #ff829c 0%, #ff5a70 100%);
+				&__text {
+					font-size: 24rpx;
+					font-weight: 400;
+					color: #ffffff;
+				}
+			}
+		}
+		.more {
+			display: flex;
+			align-items: center;
+			width: 48rpx;
+			height: 84rpx;
+			image {
+				width: 48rpx;
+				height: 12rpx;
+			}
+		}
+	}
+	.list {
+		.item {
+			height: 180rpx;
+			text-align: center;
+			.avatar-group {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				position: relative;
+				width: 120rpx;
+				height: 120rpx;
+				margin-bottom: 6rpx;
+				background: rgba(255, 255, 255, 0.3);
+				border-radius: 50%;
+				.avatar {
+					width: 108rpx;
+					height: 108rpx;
+					border-radius: 50%;
+					&.not-msg {
+						width: 36rpx;
+						height: 36rpx;
+					}
+				}
+				.homeowner {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					position: absolute;
+					left: calc(50% - 36rpx);
+					bottom: -12rpx;
+					width: 72rpx;
+					height: 36rpx;
+					background: #4d8b9d;
+					border-radius: 18rpx;
+					font-size: 24rpx;
+					font-weight: 400;
+					color: #ffffff;
+					z-index: 3;
+				}
+			}
+			.name {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #ffffff;
+			}
+		}
+	}
+}
+</style>

+ 745 - 0
pages/chat/detail.vue

@@ -0,0 +1,745 @@
+<template>
+	<view class="page-session" :style="{
+			height,
+		}" @touchmove="onTouchMove">
+		<view class="header">
+			<cl-topbar>
+				<view class="cl-topbar__text" @click="toDetail()">
+					<text class="cl-topbar__title">{{nickName}}</text>
+				</view>
+
+				<template slot="append">
+					<template v-if="conversationType=='GROUP'">
+						<image class="member-icon" src="../../static/icon/member.png" @tap="member"></image>
+					</template>
+					<template v-else>
+						<view class="cl-topbar__icon" @tap="report">
+							<!-- <cl-icon name="more"></cl-icon> -->
+							举报
+						</view>
+					</template>
+				</template>
+			</cl-topbar>
+		</view>
+
+		<progress :percent="percent" show-info stroke-width="3" v-if="progressShow" />
+
+		<!-- 消息列表 -->
+		<view class="message-list" @tap="onRetract">
+			<scroll-view class="scroller" scroll-y scroll-with-animation :scroll-top="scroller.top"
+				:upper-threshold="20" @scrolltoupper="refresh()">
+				<!-- 加载更多 -->
+				<view class="loadmore" v-if="loading">
+					<cl-loadmore :finish="isCompleted" loading :divider="false"></cl-loadmore>
+				</view>
+
+				<!-- 内容块 -->
+				<chat-message ref="message" :list="list" :conversationType="conversationType"></chat-message>
+			</scroll-view>
+
+			<!-- 录音弹窗 -->
+			<cl-popup :visible.sync="voice.visible" direction="center" padding="0" border-radius="10rpx">
+				<view class="popup-voice" :class="[
+						{
+							'is-cancel': voiceIsCancel,
+						},
+					]">
+					<text class="popup-voice__time">{{ voice.duration | voice_duration }}</text>
+					<text class="popup-voice__desc">松开发送,上滑取消</text>
+				</view>
+			</cl-popup>
+		</view>
+
+		<!-- 操作栏 -->
+		<view class="opbar">
+			<view class="main">
+				<!-- 麦克风 -->
+				<!-- #ifndef H5 -->
+				<view class="icon">
+					<text class="chat-iconfont icon-microphone" v-if="!op.isMicrophone" @tap="showMicrophone"></text>
+					<text class="chat-iconfont icon-keyboard" @tap="hideMicrophone" v-else></text>
+				</view>
+				<!-- #endif -->
+
+				<!-- 输入框 -->
+				<view class="input">
+					<button v-if="op.isMicrophone" class="press-btn" @longpress="onLongPress" @touchend="onRelease">
+						{{ voice.visible ? "松开结束" : "按住说话" }}
+					</button>
+					<cl-input v-else v-model="value" confirm-type="send" confirm-hold fill :placeholder="placeholder"
+						:cursor-spacing="10" :adjust-position="false" @focus="onFocus" @blur="onBlur"
+						@confirm="onTextSend" @keyboardheightchange="onKeyBoardHeightChange"></cl-input>
+				</view>
+
+				<!-- 表情图标 -->
+				<view class="icon">
+					<text class="chat-iconfont icon-emoji" v-if="!op.isEmoji" @tap="showEmoji"></text>
+					<text class="chat-iconfont icon-keyboard" v-else @tap="hideEmoji"></text>
+				</view>
+
+				<!-- 工具栏图标 -->
+				<template v-if="value == ''">
+					<view class="icon send">
+						<text class="chat-iconfont icon-add-circle" v-if="!op.isTools" @tap="showTools"></text>
+						<text class="chat-iconfont icon-subtract-circle" @tap="hideTools" v-else></text>
+					</view>
+				</template>
+				<template v-else>
+					<!-- 发送按钮 -->
+					<view class="icon send">
+						<text class="send-button" @tap="onTextSend">发送</text>
+					</view>
+				</template>
+			</view>
+
+			<view class="append">
+				<!-- 工具栏 -->
+				<chat-tools :visible="op.isTools" :userId="userID" :conversationType="conversationType"></chat-tools>
+
+				<!-- 表情 -->
+				<chat-emoji :visible="op.isEmoji" @select="onEmojiSelect"></chat-emoji>
+			</view>
+		</view>
+
+		<!-- 视频弹窗 -->
+		<cl-popup :visible.sync="video.visible" direction="center" force-update padding="0" border-radius="10rpx"
+			@close="onVideoClose">
+			<video id="video" autoplay :src="video.url" style="display: block"></video>
+		</cl-popup>
+
+		<member ref="member"></member>
+	</view>
+</template>
+
+<script>
+	import {
+		debounce
+	} from "../../uni_modules/cl-uni/utils";
+	import ChatTools from "./components/tools";
+	import ChatMessage from "./components/message";
+	import ChatEmoji from "./components/emoji";
+	import Member from "./components/member";
+
+	// 录音设备
+	const recorderManager = uni.getRecorderManager();
+
+	// 平台
+	const {
+		platform
+	} = uni.getSystemInfoSync();
+
+	export default {
+		components: {
+			ChatTools,
+			ChatMessage,
+			ChatEmoji,
+			Member,
+		},
+
+		data() {
+			return {
+				userInfos: [],
+				placeholder: "",
+				conversationID: "",
+				conversationType: "",
+				percent: 0,
+				progressShow: false,
+				firstLoad: true,
+				isCompleted: false,
+				nextReqMessageID: null,
+				userID: 0,
+				nickName: "",
+				// 平台
+				platform,
+				// 输入框文本
+				value: "",
+				// 键盘高度
+				keyBoardHeight: 0,
+				// 聊天记录数据
+				list: [{
+						contentType: 0,
+						type: 'TIMTextElem',
+						from: 1,
+						avatar: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/avatar/5.jpg",
+						payload: {
+							text: "Hello",
+						},
+						create_time: '2024-09-15 12:00:20'
+					},
+					{
+						contentType: 2,
+						type: 'TIMTextElem',
+						from: 2,
+						name: "神仙都没用",
+						avatar: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/avatar/5.jpg",
+						payload: {
+							text: "Hello",
+						},
+						content: {
+							text: "Hello",
+							imageUrl: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/face-with-party-horn-and-party-hat.png",
+						},
+						create_time: '2024-09-15 12:00:20'
+					},
+				],
+				// 底部操作栏配置
+				// list: [],
+				op: {
+					isMicrophone: false,
+					isEmoji: false,
+					isTools: false,
+				},
+				// 滚动条配置
+				scroller: {
+					top: 0,
+					intoView: "",
+				},
+				// 视频配置
+				video: {
+					visible: false,
+				},
+				// 音频配置
+				voice: {
+					visible: false,
+					duration: 0,
+					timer: null,
+					down: 0,
+					move: 0,
+				},
+				// 加载进度
+				loading: false,
+			};
+		},
+		onLoad(option) {
+			this.userID = "p1";
+			this.nickName = "老杨";
+			this.conversationID = "single";
+			this.conversationType = "C2C"; //GROUP
+
+			// this.userID = JSON.parse(decodeURIComponent(option.userID));
+			// this.nickName = JSON.parse(decodeURIComponent(option.nickName));
+			// this.conversationID = JSON.parse(decodeURIComponent(option.conversationID));
+			// this.conversationType = JSON.parse(decodeURIComponent(option.conversationType));
+			// if (this.conversationType == "GROUP") {
+			// 	this.placeholder = "发布违规言论会被禁言哦~";
+			// }
+
+			// this.TIM.setMessageRead(this.conversationID);
+
+			// this.setList();
+			this.getUserInfo();
+			uni.$on('messageUpdate', this.acceptMessage)
+			uni.$on('messageProgress', this.messageProgress)
+		},
+		computed: {
+			// 计算屏幕高度
+			height() {
+				return this.keyBoardHeight > 0 ?
+					`calc(100% - ${this.keyBoardHeight}px + env(safe-area-inset-bottom))` :
+					"100%";
+			},
+
+			// 录音滑动是否取消
+			voiceIsCancel() {
+				return this.voice.move ? this.voice.down - this.voice.move > 50 : false;
+			},
+		},
+
+		filters: {
+			voice_duration(t) {
+				return `00:${t < 10 ? `0${t}` : t}`;
+			},
+		},
+		methods: {
+			async getUserInfo() {
+				// let [err, res] = await this.$http.get('Group/members', {
+				// 	'id': this.userID,
+				// 	'type': this.conversationType
+				// });
+
+				// if (!this.$http.errorCheck(err, res)) {
+				// 	return;
+				// }
+				// this.userInfos = res.data.data;
+
+				this.userInfos = [{
+						id: 1,
+						user_icon: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/avatar/5.jpg",
+						username: 'ab',
+						sex: 1
+					},
+					{
+						id: 2,
+						user_icon: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/avatar/5.jpg",
+						username: 'cd',
+						sex: 1
+					}
+				];
+				this.setUserInfo();
+			},
+			setUserInfo() {
+				if (this.list.length == 0 || this.userInfos.length == 0) {
+					return
+				}
+
+				for (let i = 0; i < this.list.length; i++) {
+					let fromID = this.list[i].from
+
+					for (let j = 0; j < this.userInfos.length; j++) {
+						if (fromID == this.userInfos[j].id) {
+							this.list[i].avatar = this.userInfos[j].user_icon;
+							this.list[i].sex = this.userInfos[j].sex;
+							break;
+						}
+					}
+				}
+
+				this.list = this.list
+			},
+			toDetail() {
+				if (this.conversationType == "C2C") {
+					uni.navigateTo({
+						url: "/pages/detail/index?uid=" + this.userID,
+					});
+				}
+			},
+			report() {
+				setTimeout(() => {
+					uni.showToast({
+						title: '举报成功,我们会尽快进行处理!',
+						icon: "none"
+					});
+				}, 500);
+			},
+			member() {
+				this.$refs.member.open(this.userID);
+			},
+			refresh() {
+				this.loading = true;
+
+				if (this.isCompleted) {
+					return;
+				}
+
+				// this.setList();
+			},
+			messageProgress(e) {
+				this.percent = e * 100;
+
+				if (e == 1) {
+					this.progressShow = false;
+				} else {
+					this.progressShow = true;
+				}
+			},
+			timestampToTime(timestamp) {
+				var date = new Date(timestamp * 1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+				var Y = date.getFullYear() + '-';
+				var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+				//var D = date.getDate() + ' ';
+				var D = (date.getDate() + 1 < 10 ? '0' + (date.getDate()) : date.getDate()) + ' ';
+				var h = date.getHours() + ':';
+				var m = (date.getMinutes() + 1 < 10 ? '0' + (date.getMinutes()) : date.getMinutes()) + ' ';
+				var s = date.getSeconds();
+				return M + D + h + m;
+			},
+			acceptMessage(message) {
+				for (let i = 0; i < message.length; i++) {
+					if (message[i].conversationID == this.conversationID) {
+						this.list.push(message[i]);
+						this.scrollToBottom();
+					}
+				}
+			},
+			async setList() {
+				return;
+				let res = await this.TIM.getMessageList(this.conversationID, this.nextReqMessageID);
+				let messageList = res.data.messageList;
+				this.isCompleted = res.data.isCompleted;
+				this.nextReqMessageID = res.data.nextReqMessageID;
+
+				for (let i = 0; i < messageList.length; i++) {
+					messageList[i].create_time = this.timestampToTime(messageList[i].time);
+					messageList[i].sex = "3";
+				}
+
+				if (this.firstLoad) {
+					this.list = messageList;
+					this.scrollToBottom();
+				} else {
+					this.list = messageList.concat(this.list);
+				}
+
+				this.firstLoad = false;
+				uni.stopPullDownRefresh();
+
+				this.loading = false;
+				this.setUserInfo();
+			},
+
+			// 监听键盘高度
+			onKeyBoardHeightChange(e) {
+				this.keyBoardHeight = e.detail.height;
+			},
+
+			// 滑动监听
+			onTouchMove(e) {
+				if (this.voice.visible) {
+					// 记录移动的位置
+					this.voice.move = e.changedTouches[0].clientY;
+				}
+			},
+
+			// 长按说话
+			onLongPress(e) {
+				// uni.authorize({
+				// 	scope: 'scope.record',
+				// 	success() {
+				// 	    uni.getLocation()
+				// 	}
+				// });
+
+				// 关闭已存在播放声音
+				this.$refs["message"].voicePause();
+
+				console.log("按下按钮了");
+
+				this.$nextTick(() => {
+					// 记录按下位置
+					this.voice.down = e.touches[0].pageY;
+					// 清空移动位置
+					this.voice.move = 0;
+					// 显示弹窗
+					this.voice.visible = true;
+					// 开始录音
+					recorderManager.start({
+						// duration: 60000,
+						// sampleRate: 44100,
+						// numberOfChannels: 1,
+						// encodeBitRate: 192000,
+						// format: "aac",
+					});
+					// 计数器
+					this.voice.timer = setInterval(() => {
+						if (this.voice.duration >= 60) {
+							this.onRelease(e);
+						} else {
+							this.voice.duration += 1;
+						}
+					}, 1000);
+				});
+			},
+
+			// 松开
+			onRelease(e) {
+				// 记录移动位置
+				this.voice.move = e.changedTouches[0].clientY;
+				let duration = this.voice.duration * 1000;
+
+				console.log("松开按钮了");
+
+				// 暂停事件
+				recorderManager.onStop((res) => {
+					// 判断是否取消
+					if (!this.voiceIsCancel) {
+						res.duration = duration;
+						res.size = 34271;
+						this.TIM.sendAudioMessage(res, this.userID, this.conversationType);
+					}
+				});
+				// 清除计时器
+				clearInterval(this.voice.timer);
+				// 暂停录音
+				recorderManager.stop();
+				// 关闭弹窗
+				this.voice.visible = false;
+				// 清空时常
+				this.voice.duration = 0;
+			},
+
+			// 显示麦克风
+			showMicrophone() {
+				this.op.isMicrophone = true;
+				this.hideTools();
+				this.hideEmoji();
+				this.hideKeyBoard();
+			},
+
+			// 隐藏麦克风
+			hideMicrophone() {
+				this.op.isMicrophone = false;
+			},
+
+			// 显示表情
+			showEmoji() {
+				this.op.isEmoji = true;
+				this.hideTools();
+				this.hideMicrophone();
+				this.scrollToBottom();
+			},
+
+			// 隐藏表情
+			hideEmoji() {
+				this.op.isEmoji = false;
+			},
+
+			// 显示工具栏
+			showTools() {
+				this.op.isTools = true;
+				this.hideEmoji();
+				this.hideMicrophone();
+				this.scrollToBottom();
+			},
+
+			// 隐藏工具栏
+			hideTools() {
+				this.op.isTools = false;
+			},
+
+			// 隐藏键盘
+			hideKeyBoard() {
+				this.keyBoardHeight = 0;
+			},
+
+			// 滑动到底部
+			scrollToBottom: debounce(function() {
+				this.$nextTick(() => {
+					this.scroller.top = 2000000 + parseInt(Math.random() * 100);
+				});
+			}, 100),
+
+			// 收起
+			onRetract() {
+				this.hideTools();
+				this.hideEmoji();
+			},
+
+			// 聚焦
+			onFocus() {
+				this.hideEmoji();
+				this.hideTools();
+				this.scrollToBottom();
+			},
+
+			// 失焦
+			onBlur() {
+				this.hideKeyBoard();
+			},
+
+			// 发送消息
+			onTextSend() {
+				if (this.value) {
+					this.TIM.sendTextMessage(this.value, this.userID, this.conversationType);
+					this.value = "";
+				}
+
+				if (this.userID == "51") {
+					setTimeout(() => {
+						this.hackMessage()
+					}, 1000);
+				}
+			},
+
+			hackMessage() {
+				if (this.list.length > 0) {
+					let item = JSON.parse(JSON.stringify(this.list[0]))
+					item.avatar = "/static/logo.png";
+					item._mode = "text";
+					item.payload.text = "客服小姐姐正在赶来,请稍后~"
+					item.from = "51"
+					this.list.push(item)
+				} else {
+					this.list.push({
+						avatar: "/static/logo.png",
+						_mode: "text",
+						payload: {
+							text: "客服小姐姐正在赶来,请稍后~"
+						},
+						from: "51"
+					})
+				}
+			},
+
+			// 表情选择
+			onEmojiSelect(e) {
+				this.value = this.value + e;
+			},
+
+
+			// 追加数据到开头
+			prepend(...data) {
+				this.list.unshift(...data.filter(Boolean).reverse());
+			},
+
+			// 追加数据到结尾
+			append(...data) {
+				this.list.push(
+					...data
+					.map((e) => {
+						e.animation = true;
+						return e;
+					})
+					.filter(Boolean)
+				);
+				this.scrollToBottom();
+			},
+
+			// 关闭视频弹窗
+			onVideoClose() {
+				const video = uni.createVideoContext("video");
+				video.pause();
+			}
+		},
+	};
+</script>
+
+<style lang="scss">
+	@import "../../static/css/iconfont.scss";
+
+	page {
+		height: 100%;
+		overflow: hidden;
+		background-color: #fff;
+	}
+</style>
+
+<style lang="scss" scoped>
+	.page-session {
+		display: flex;
+		flex-direction: column;
+		padding-bottom: env(safe-area-inset-bottom);
+		box-sizing: border-box;
+		height: 100%;
+
+		.message-list {
+			flex: 1;
+			overflow: hidden;
+			background-color: #f7f7f7;
+			position: relative;
+
+			.loadmore {
+				margin: 10rpx 0;
+			}
+
+			.scroller {
+				height: 100%;
+			}
+
+			/deep/.cl-popup {
+				&__wrapper {
+					position: absolute;
+				}
+			}
+		}
+
+		.opbar {
+			flex-shrink: 0;
+			z-index: 9;
+			background-color: #fff;
+			border-top: 1rpx solid #f7f7f7;
+
+			.main {
+				display: flex;
+				align-items: center;
+				height: 100rpx;
+
+				.send {
+					margin-right: 30rpx;
+				}
+
+				.icon {
+					height: 80rpx;
+					width: 80rpx;
+					line-height: 80rpx;
+					text-align: center;
+					flex-shrink: 0;
+
+					.chat-iconfont {
+						font-size: 60rpx;
+					}
+
+					.send-button {
+						background-color: #66C67D;
+						color: #F7FFFB;
+						border-radius: 10rpx;
+						padding: 5rpx 10rpx;
+						font-size: 30rpx;
+					}
+				}
+
+				.input {
+					flex: 1;
+					margin: 0 10rpx;
+					height: 70rpx;
+					line-height: 70rpx;
+
+					.press-btn {
+						display: inline-block;
+						height: 70rpx;
+						width: 100%;
+						line-height: 70rpx;
+						color: #666;
+						border: 1rpx solid #dcdfe6;
+						font-size: 24rpx;
+						background-color: #fff;
+						margin: 0;
+						border-radius: 70rpx;
+						box-sizing: border-box;
+
+						&::after {
+							border: 0;
+						}
+
+						&:active {
+							background-color: #f7f7f7;
+						}
+					}
+				}
+			}
+		}
+
+		.popup-voice {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			padding: 20rpx;
+
+			&.is-cancel {
+				background-color: red;
+				color: #fff;
+			}
+
+			&__time {
+				font-size: 28rpx;
+				margin-bottom: 20rpx;
+				letter-spacing: 1rpx;
+			}
+
+			&__desc {
+				font-size: 24rpx;
+			}
+		}
+	}
+
+	.cl-topbar__icon {
+		font-size: 25rpx;
+		color: red;
+		padding: 0rpx 10rpx;
+	}
+
+	.member-icon {
+		width: 50rpx;
+		height: 50rpx;
+		margin-right: 30rpx;
+	}
+
+	.cl-topbar {
+		width: 100%;
+		height: 100rpx;
+		display: flex;
+		flex-direction: row;
+		justify-content: space-between;
+		align-items: center;
+		padding: 10rpx 20rpx;
+	}
+</style>

+ 256 - 0
pages/chat/message.vue

@@ -0,0 +1,256 @@
+<template>
+	<view class="page-message">
+		<!-- topbar -->
+		<cl-sticky is-topbar>
+			<cl-topbar :title="null" :show-back="false" :border="false">
+				<view class="content">
+					消息
+				</view>
+				<image src="../../static/icon/service.png" @click="navigateToService"></image>
+			</cl-topbar>
+		</cl-sticky>
+
+		<!-- list -->
+		<view class="list">
+			<view class="item" v-for="(item, index) in list" :key="index" @tap="navigateToChatDetail(item)"
+				@longtap="deletec(item.conversationID)">
+				<template v-if="item.type=='GROUP' || item.type=='C2C'">
+					<!-- 头像 -->
+					<view class="avatar">
+						<template v-if="item.type=='GROUP'">
+							<image lazy-load="true" src="/static/images/cp.png" mode="aspectFill" />
+						</template>
+						<template v-else>
+							<image lazy-load="true" :src="item.userProfile.avatar" mode="aspectFill" />
+						</template>
+						<view class="number" v-if="item.unreadCount!=0">{{ item.unreadCount }}</view>
+					</view>
+
+					<view class="content">
+						<!-- 昵称,消息 -->
+						<view class="info">
+							<template v-if="item.type=='GROUP'">
+								<text class="name">{{ item.groupProfile.name }}</text>
+							</template>
+							<template v-else>
+								<text class="name">{{ item.userProfile.nick }}</text>
+							</template>
+
+							<text class="last">{{ item.lastMessage.messageForShow }}</text>
+						</view>
+
+						<!-- 时间 -->
+						<text class="time">
+							{{ item.lastMessage.lastTimeFormat }}
+						</text>
+					</view>
+				</template>
+			</view>
+		</view>
+
+		<view class="kongbai" v-if="list.length == 0">
+			<image src="../../static/images/kongbai.png" mode="widthFix"></image>
+			<view>还没有消息哦</view>
+			<view>赶紧去找几个意中人聊聊天吧~</view>
+		</view>
+
+		<delete-conversation ref="deleteConversation"></delete-conversation>
+	</view>
+</template>
+
+<script>
+	import deleteConversation from "./components/delete-conversation.vue"
+
+	export default {
+		components: {
+			deleteConversation
+		},
+		data() {
+			return {
+				tabsActive: "match",
+				tabs: [{
+					label: "聊天",
+					name: "match",
+				}],
+				list: [],
+			};
+		},
+		onLoad() {
+			let __this = this;
+			this.setList();
+			uni.$on('conversationUpdate', function(data) {
+				__this.setList();
+			})
+		},
+		onShow() {
+			// this.TIM.getConversationList();
+			console.log("sdf")
+		},
+		methods: {
+			deletec(conversationID) {
+				this.$refs.deleteConversation.open(conversationID);
+			},
+			setList() {
+				// this.list = this.TIM.conversations;
+				this.list = [{
+					conversationID: 1,
+					type: 'C2C',
+					userProfile: {
+						nick: 'nk',
+						avatar: 'https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/avatar/5.jpg'
+					},
+					lastMessage: {
+						messageForShow: '最后一条消息记录',
+						lastTimeFormat: '2024-09-20 12:30:30'
+					}
+				}]
+				console.log(this.list)
+			},
+			activeTabs(item) {
+				this.tabsActive = item.name;
+			},
+			navigateToChatDetail(item) {
+				var jsonConversationID = encodeURIComponent(JSON.stringify(item.conversationID));
+				var jsonConversationType = encodeURIComponent(JSON.stringify(item.type));
+
+				if (item.type == "GROUP") {
+					var jsonUserID = encodeURIComponent(JSON.stringify(item.groupProfile.groupID));
+					var jsonNickName = encodeURIComponent(JSON.stringify(item.groupProfile.name));
+				} else {
+					var jsonUserID = encodeURIComponent(JSON.stringify(item.userProfile.userID));
+					var jsonNickName = encodeURIComponent(JSON.stringify(item.userProfile.nick));
+				}
+
+				uni.navigateTo({
+					url: "/pages/chat/detail?userID=" + jsonUserID + "&nickName=" + jsonNickName +
+						"&conversationID=" + jsonConversationID + "&conversationType=" + jsonConversationType
+				})
+			},
+			navigateToService() {
+				let jsonUserID = encodeURIComponent(JSON.stringify("51"));
+				let jsonNickName = encodeURIComponent(JSON.stringify("cp交友官方客服"));
+				let jsonConversationID = encodeURIComponent(JSON.stringify("C2C51"));
+				let jsonConversationType = encodeURIComponent(JSON.stringify("C2C"));
+				uni.navigateTo({
+					url: "/pages/chat/detail?userID=" + jsonUserID + "&nickName=" + jsonNickName +
+						"&conversationID=" + jsonConversationID + "&conversationType=" + jsonConversationType
+				})
+			},
+			async getUserInfoByIdNum(idNums) {
+				let [err, res] = await this.$http.get('user/getByIdNums', {
+					id_nums: idNums
+				});
+
+				if (!this.$http.errorCheck(err, res)) {
+					return;
+				}
+			},
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	.page-message {
+		.cl-topbar {
+			height: 100rpx;
+			padding-left: 30rpx;
+
+			.content {
+				font-size: 36rpx;
+				font-weight: bold;
+				display: inline-block;
+			}
+
+			image {
+				position: absolute;
+				right: 60rpx;
+				height: 40rpx;
+				width: 40rpx;
+			}
+		}
+
+		.list {
+			padding: 35rpx 30rpx;
+
+			.item {
+				display: flex;
+				margin-bottom: 32rpx;
+
+				.avatar {
+					position: relative;
+					width: 90rpx;
+					height: 90rpx;
+					margin-right: 30rpx;
+
+					image {
+						width: 90rpx;
+						height: 90rpx;
+						border-radius: 50%;
+					}
+
+					.number {
+						display: flex;
+						justify-content: center;
+						align-items: center;
+						position: absolute;
+						top: -36rpx;
+						right: -24rpx;
+						width: 36rpx;
+						height: 36rpx;
+						background-color: #fe6d73;
+						border-radius: 50% 50% 50% 0;
+						font-size: 20rpx;
+						font-weight: 400;
+						color: #ffffff;
+					}
+				}
+
+				.content {
+					display: flex;
+					padding-bottom: 25rpx;
+					border-bottom: 2rpx solid #ebebeb;
+					flex: 1;
+
+					.info {
+						flex: 1;
+
+						.name {
+							display: block;
+							margin-bottom: 14rpx;
+							font-size: 25rpx;
+							font-weight: 500;
+							color: #303030;
+						}
+
+						.last {
+							font-size: 28rpx;
+							font-weight: 400;
+							color: #bbbbbb;
+						}
+					}
+
+					.time {
+						font-size: 24rpx;
+						font-weight: 400;
+						color: #bbbbbb;
+					}
+				}
+			}
+		}
+
+		.kongbai {
+			position: fixed;
+			top: 0rpx;
+			bottom: 0rpx;
+			left: 0rpx;
+			right: 0rpx;
+			text-align: center;
+			padding-top: 200rpx;
+			font-size: 24rpx;
+
+			image {
+				width: 500rpx;
+			}
+		}
+	}
+</style>

+ 4 - 2
pages/index/index.vue

@@ -85,7 +85,7 @@
 		<view class="thread2"></view>
 		<view class="thread2"></view>
 
-
+		<wu-app-update></wu-app-update>
 	</view>
 </template>
 
@@ -102,7 +102,9 @@
 			let self = this;
 
 		},
-		onShow() {},
+		onShow() {
+			uni.$emit('check_update');
+		},
 		methods: {}
 	}
 </script>

+ 3 - 3
pages/login/login.vue

@@ -21,12 +21,12 @@
 			<block v-if="type == 'mobile' || type == 'pass'">
 				<view class="name">手机号码:</view>
 				<view class="item">
-					<input type="text" class="input" v-model="mobile" placeholder="请输入手机号码" />
+					<input type="text" class="input" v-model="mobile" placeholder="请输入手机号码" maxlength="11"/>
 				</view>
 				<block v-if="type == 'pass'">
 					<view class="name">登录密码:</view>
 					<view class="item">
-						<input type="password" class="input" v-model="password" placeholder="请输入登录密码" />
+						<input type="password" class="input" v-model="password" placeholder="请输入登录密码" maxlength="32"/>
 					</view>
 					<view class="other_list">
 						<text class="left" v-if="false">忘记密码</text>
@@ -38,7 +38,7 @@
 				<block v-if="type == 'mobile'">
 					<view class="name">验证码:</view>
 					<view class="item">
-						<input type="text" class="input" v-model="code" placeholder="请输入验证码" />
+						<input type="text" class="input" v-model="code" placeholder="请输入验证码" maxlength="6"/>
 						<view class="btn">获取验证码</view>
 					</view>
 					<view class="other_list">

+ 4 - 4
pages/my/editInfo.vue

@@ -9,11 +9,11 @@
 			</view>
 			<view class="name">昵称:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入昵称" />
+				<input type="text" class="input" placeholder="请输入昵称"  maxlength="10"/>
 			</view>
 			<view class="name">微信号:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入微信号" />
+				<input type="text" class="input" placeholder="请输入微信号"  maxlength="32"/>
 			</view>
 			<view class="name">性别:</view>
 			<view class="item2">
@@ -47,11 +47,11 @@
 			</view>
 			<view class="name">职业:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入职业" />
+				<input type="text" class="input" placeholder="请输入职业"  maxlength="32"/>
 			</view>
 			<view class="name">个性签名:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入个性签名" />
+				<input type="text" class="input" placeholder="请输入个性签名"  maxlength="64"/>
 			</view>
 			<view class="name">兴趣爱好:
 				<view class="right">添加+</view>

+ 3 - 3
pages/my/editMobile.vue

@@ -3,15 +3,15 @@
 		<view class="list_info">
 			<view class="name">旧手机号码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入旧手机号码" />
+				<input type="text" class="input" placeholder="请输入旧手机号码"  maxlength="11"/>
 			</view>
 			<view class="name">新手机号码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入新手机号码" />
+				<input type="text" class="input" placeholder="请输入新手机号码"  maxlength="11"/>
 			</view>
 			<view class="name">验证码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入验证码" />
+				<input type="text" class="input" placeholder="请输入验证码"  maxlength="6"/>
 				<view class="btn">获取验证码</view>
 			</view>
 

+ 4 - 4
pages/my/forgetPass.vue

@@ -3,20 +3,20 @@
 		<view class="list_info">
 			<view class="name">手机号码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入手机号码" />
+				<input type="text" class="input" placeholder="请输入手机号码"  maxlength="11"/>
 			</view>
 			<view class="name">验证码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入验证码" />
+				<input type="text" class="input" placeholder="请输入验证码"  maxlength="6"/>
 				<view class="btn">获取验证码</view>
 			</view>
 			<view class="name">设置新密码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请输入新登录密码" />
+				<input type="text" class="input" placeholder="请输入新登录密码"  maxlength="32"/>
 			</view>
 			<view class="name">确认新密码:</view>
 			<view class="item">
-				<input type="text" class="input" placeholder="请再次输入新登录密码" />
+				<input type="text" class="input" placeholder="请再次输入新登录密码"  maxlength="32"/>
 			</view>
 
 

+ 2 - 2
pages/my/idcheck.vue

@@ -4,11 +4,11 @@
 			<view class="list_info">
 				<view class="name">姓名:</view>
 				<view class="item">
-					<input type="text" class="input" v-model="realname" placeholder="请输入真实姓名" />
+					<input type="text" class="input" v-model="realname" placeholder="请输入真实姓名" maxlength="16"/>
 				</view>
 				<view class="name">身份证号:</view>
 				<view class="item">
-					<input type="text" class="input" v-model="idcard" placeholder="请输入身份证号" />
+					<input type="text" class="input" v-model="idcard" placeholder="请输入身份证号"  maxlength="18"/>
 				</view>
 
 				<view class="blankHeight"></view>

+ 3 - 0
pages/my/my.vue

@@ -179,6 +179,9 @@
 						},
 						success: (res) => {
 							console.log("----:", res.data);
+							getApp().globalData.skey = "";
+							getApp().globalData.uuid = "";
+							uni.removeStorageSync("wapptoken");
 							uni.redirectTo({
 								url: '/pages/login/login'
 							})

+ 4 - 4
pages/my/step.vue

@@ -15,11 +15,11 @@
 				</view>
 				<view class="name">给自己取个名字吧:</view>
 				<view class="item">
-					<input type="text" class="input" placeholder="请输入昵称" />
+					<input type="text" class="input" placeholder="请输入昵称" maxlength="10"/>
 				</view>
 				<view class="name">你的微信号是:</view>
 				<view class="item">
-					<input type="text" class="input" placeholder="请输入微信号" />
+					<input type="text" class="input" placeholder="请输入微信号" maxlength="32"/>
 				</view>
 				<view class="name">选一张你的真实照片作为封面:</view>
 				<view class="bcenter">
@@ -32,11 +32,11 @@
 			<block v-if="step == 2">
 				<view class="name">给自己写一个个性签名:</view>
 				<view class="item">
-					<input type="text" class="input" placeholder="请输入个性签名" />
+					<input type="text" class="input" placeholder="请输入个性签名" maxlength="64"/>
 				</view>
 				<view class="name">职业:</view>
 				<view class="item">
-					<input type="text" class="input" placeholder="请输入职业" />
+					<input type="text" class="input" placeholder="请输入职业" maxlength="16"/>
 				</view>
 				<view class="name">年龄:</view>
 				<view class="itemSingle">

+ 1 - 1
pages/w3/levRule.vue

@@ -41,7 +41,7 @@
 				</view>
 				<view class="line"></view>
 				<view class="item">
-					<text>战队通行证</text>
+					<text>持有通行证</text>
 					<text>{{myinfo.lev_card}}</text>
 					<text class="num">{{myinfo.num_tuanbox}}<br />({{myinfo.state2}})</text>
 				</view>

+ 2 - 2
pages/w3/tran.vue

@@ -7,7 +7,7 @@
 				</view>
 				<view class="num1">
 					<view class="num">
-						<input type="number" v-model="num" placeholder="请输入传送数量" />
+						<input type="number" v-model="num" placeholder="请输入传送数量" maxlength="16"/>
 					</view>
 					<view class="all" @click="num = myinfo.num_gmb">
 						全部
@@ -27,7 +27,7 @@
 				</view>
 				<view class="num1">
 					<view class="num">
-						<input type="number" v-model="mobile" placeholder="请输入传送手机号" />
+						<input type="number" v-model="mobile" placeholder="请输入传送手机号" maxlength="11"/>
 					</view>
 					<view class="all">
 					</view>

BIN
static/appUploadAlertBoxBg.png


+ 84 - 0
static/css/iconfont.scss

@@ -0,0 +1,84 @@
+@font-face {
+	font-family: "chat-iconfont"; /* project id 2341078 */
+	src: url("https://at.alicdn.com/t/font_2341078_y34sthtwq1.eot");
+	src: url("https://at.alicdn.com/t/font_2341078_y34sthtwq1.eot?#iefix")
+			format("embedded-opentype"),
+		url("https://at.alicdn.com/t/font_2341078_y34sthtwq1.woff2") format("woff2"),
+		url("https://at.alicdn.com/t/font_2341078_y34sthtwq1.woff") format("woff"),
+		url("https://at.alicdn.com/t/font_2341078_y34sthtwq1.ttf") format("truetype"),
+		url("https://at.alicdn.com/t/font_2341078_y34sthtwq1.svg#iconfont") format("svg");
+}
+
+.chat-iconfont {
+	font-family: "chat-iconfont";
+	font-size: 40rpx;
+	font-style: normal;
+}
+
+.icon-chat:before {
+	content: "\e648";
+}
+
+.icon-add-circle:before {
+	content: "\e635";
+}
+
+.icon-microphone:before {
+	content: "\e63e";
+}
+
+.icon-tool-image:before {
+	content: "\e643";
+}
+
+.icon-tool-call:before {
+	content: "\e644";
+}
+
+.icon-tool-folder:before {
+	content: "\e645";
+}
+
+.icon-tool-shoot:before {
+	content: "\e646";
+}
+
+.icon-voice-right:before {
+	content: "\e647";
+}
+
+.icon-voice-left:before {
+	content: "\e650";
+}
+
+.icon-keyboard:before {
+	content: "\e653";
+}
+
+.icon-subtract-circle:before {
+	content: "\e655";
+}
+
+.icon-more:before {
+	content: "\e657";
+}
+
+.icon-play:before {
+	content: "\e664";
+}
+
+.icon-emoji:before {
+	content: "\e6a9";
+}
+
+.icon-tool-video:before {
+	content: "\e75e";
+}
+
+.icon-voice-left1:before {
+	content: "\e75f";
+}
+
+.icon-voice-right1:before {
+	content: "\e760";
+}

BIN
static/icon/QQ.png


BIN
static/icon/add.png


BIN
static/icon/brief-icon-1.png


BIN
static/icon/brief-icon-2.png


BIN
static/icon/chat-icon.png


BIN
static/icon/chat-more.png


BIN
static/icon/comment.png


BIN
static/icon/delete.png


BIN
static/icon/dynamic-icon.png


BIN
static/icon/fabu.png


BIN
static/icon/fabu_1.png


BIN
static/icon/filter-icon.png


BIN
static/icon/floot-icon.png


BIN
static/icon/hi.png


BIN
static/icon/hot.png


BIN
static/icon/like.png


BIN
static/icon/location.png


BIN
static/icon/man.png


BIN
static/icon/may-3.png


BIN
static/icon/member.png


BIN
static/icon/message-more.png


BIN
static/icon/more.png


BIN
static/icon/my-icon-1.png


BIN
static/icon/my-icon-2.png


BIN
static/icon/my-icon-3.png


BIN
static/icon/my-icon-4.png


BIN
static/icon/my-icon-5.png


BIN
static/icon/my-icon-6.png


BIN
static/icon/no_like.png


BIN
static/icon/pengyouquan.png


BIN
static/icon/service.png


BIN
static/icon/tabbar/dynamic.png


BIN
static/icon/tabbar/home.png


BIN
static/icon/tabbar/meet.png


BIN
static/icon/tabbar/message.png


BIN
static/icon/tabbar/no-home.png


BIN
static/icon/tabbar/on-dynamic.png


BIN
static/icon/tabbar/on-meet.png


BIN
static/icon/tabbar/on-message.png


BIN
static/icon/tabbar/on-room.png


BIN
static/icon/tabbar/room.png


BIN
static/icon/tabs-icon.png


BIN
static/icon/to-my.png


BIN
static/icon/vip-icon.png


BIN
static/icon/weixin.png


BIN
static/icon/woman.png


BIN
static/images/ad.png


BIN
static/images/article/dazhaohu.png


BIN
static/images/article/dazhaohu_2.png


BIN
static/images/article/like.png


BIN
static/images/article/no_like.png


BIN
static/images/article/no_like_bak.png


BIN
static/images/article/pinglun.png


BIN
static/images/attention.png


BIN
static/images/avatar.png


BIN
static/images/birthday.png


BIN
static/images/brief-cover-1.png


BIN
static/images/brief-cover-2.png


Некоторые файлы не были показаны из-за большого количества измененных файлов