赞
踩
/* @font-face {font-family: "iconfont"; src:url('/static/font_1365296_2ijcbdrmsg.ttf') format('truetype') } */ @font-face {font-family: "iconfont"; src:url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2') } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* @font-face {font-family: "iconfont"; src:url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2') } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } */
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar title="我的收藏" showBack :showRight="false"> <input type="text" v-model="keyword" placeholder="请输入关键字" style="width: 650rpx;" class="font-md" @confirm="confirm"/> </free-nav-bar> <block v-if="searchType==''&&list.length===0"> <view class="py-3 flex align-center justify-center"> <text class="font text-light-muted">搜索指定内容</text> </view> <view class="px-4 flex flex-wrap"> <view class="flex align-center justify-center mb-3" style="width: 223rpx;" v-for="(item,index) in typeList" :key="index" @click="changeSearchType(item)"> <text class="font text-hover-primary">{{item.name}}</text> </view> </view> </block> <free-list-item v-for="(item,index) in list" :key="index" :title="item.nickname ? item.nickname : item.username" :cover="item.avatar ? item.avatar : '/static/images/userpic.png'" @click="open(item.id)"></free-list-item> </view> </template> <script> import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeListItem from '@/components/free-ui/free-list-item.vue'; import $H from '@/common/free-lib/request.js'; export default { components:{ freeNavBar, freeListItem }, data() { return { typeList:[{ name:'聊天记录', key:'history' }, { name:'用户', key:'user' }, { name:'群聊', key:'group' }], keyword:'', list:[], searchType:'' } }, methods: { confirm(){ $H.post('/search/user',{keyword:this.keyword}).then(res=>{ this.list=[]; if(res){ this.list.push(res); } }) }, // 打开用户资料 open(id){ uni.navigateTo({ url:'../../mail/user-base/user-base?user_id='+id }) }, changeSearchType(item){ console.log(item); this.searchType = item.key; } } } </script> <style> </style>
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar title="聊天记录" showBack :showRight="false"> </free-nav-bar> <!-- 搜索框 --> <view class="p-3 bg-light position-fixed left-0 right-0" :style="'top:'+top+'px;'"> <input type="text" value="" v-model="keyword" placeholder="搜索" class="bg-white rounded" placeholder-class="text-center" style="height: 80rpx;"/> </view> <view style="height:140rpx;"></view> <!-- 联系人列表 --> <view class="px-2 py-1 bg-light"> <text class="font-sm text-muted">{{keyword ? '搜索结果' : '最近联系人'}}</text> </view> <view v-for="(item,index) in allList" :key="index" :id="'chatItem_'+index"> <free-chat-item :item="item" :index="index" ref="chatItem" :pretime=" index > 0 ? list[index-1].create_time : 0" :shownickname="true"></free-chat-item> </view> <view style="height:100rpx;" class="flex align-center justify-center" v-if="keyword !== '' && searchList.length === 0"> <text class="font text-light-muted">暂无搜索结果</text> </view> </view> </template> <script> import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeMainButton from '@/components/free-ui/free-main-button.vue'; import freeChatItem from '@/components/free-ui/free-chat-item.vue'; import freeAvatar from '@/components/free-ui/free-avatar.vue'; import {mapState} from 'vuex'; export default { components:{ freeNavBar, freeMainButton, freeChatItem, freeAvatar }, data() { return { keyword:'', top:0, list:[] } }, computed:{ ...mapState({ user:state=>state.user.user, chat:state=>state.user.chat }), // 最终列表 allList(){ return this.keyword === '' ? this.list : this.searchList; }, // 搜索结果列表 searchList(){ if(this.keyword === ''){ return []; } return this.list.filter(item=>{ return item.data.indexOf(this.keyword) !== -1; }) }, // 选中列表 selectList(){ return this.list.filter(item=>item.checked) }, // 选中数量 selectCount(){ return this.selectList.length; } }, methods: { // 点击导航栏 handlenNav(){ if(!this.muliSelect){ return this.muliSelect = true; } // 发送 console.log('发送') }, // 选中、取消选中 selectItem(item){ // 选中、取消选中 if(this.muliSelect){ // 选中 if(!item.checked && (this.selectCount === 9)){ // 限制选中数量 return uni.showToast({ title:'最多选中9个', icon:'none' }) } // 取消选中 return item.checked = !item.checked; } // 发送 this.$refs.confirm.show((close)=>{ console.log('点击了确定'); close(); }); } }, onLoad() { let res = uni.getSystemInfoSync(); let statusBarHeight = 0; // #ifndef MP statusBarHeight = res.statusBarHeight; // #endif this.top = statusBarHeight + uni.upx2px(90); this.list = this.chat.getChatDetail(); } } </script> <style> </style>
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar title="群公告" showBack :showRight="true" bgColor="bg-white"> <free-main-button name="推送" slot="right" @click="submit"></free-main-button> </free-nav-bar> <textarea v-model="remark" placeholder="暂无公告..." class="bg-white p-2 font-md" style="width:750rpx;" /> </view> </template> <script> import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeMainButton from '@/components/free-ui/free-main-button.vue'; import $H from '@/common/free-lib/request.js'; import auth from '@/common/mixin/auth.js'; export default { mixins:[auth], components:{ freeNavBar, freeMainButton, }, data() { return { remark:'', id:0 } }, onLoad(e) { if(!e.params){ return this.backToast() } let params = JSON.parse(decodeURIComponent(e.params)); console.log(params) this.remark = params.remark; this.id = params.id; }, methods: { submit(){ $H.post('/group/remark',{ id:this.id, remark:this.remark }).then(res=>{ uni.showToast({ title:'推送成功', icon:'none' }); uni.navigateBack({ delta:res }); }) } } } </script> <style> </style>
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar title="好友申请列表" showBack :showRight="false"> </free-nav-bar> <free-list-item v-for="(item,index) in applyList" :key="index" :title="item.user.nickname ? item.user.nickname : item.user.username" :cover="item.user.avatar ? item.user.avatar : '/static/images/userpic.png'" :showRight="true" :showRightIcon="false"> <view slot="right"> <free-main-button v-if="item.status==='pending'" name="同意" @click='handle(item)'></free-main-button> <text v-else class="text-muted font-sm">{{item|formatTitle}}</text> </view> </free-list-item> <!-- 上拉加载 --> <view class="flex align-center justify-center py-4 bg-light" v-if="applyList.length >= 10"> <text class="text-muted font">{{loadmore}}</text> </view> </view> </template> <script> import freeMainButton from '@/components/free-ui/free-main-button.vue'; import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeListItem from '@/components/free-ui/free-list-item.vue'; import freeDivider from '@/components/free-ui/free-divider.vue'; import $H from '@/common/free-lib/request.js'; import auth from '@/common/mixin/auth.js'; import { mapState } from 'vuex'; export default { mixins:[auth], components: { freeNavBar, freeMainButton, freeListItem, freeDivider }, filters:{ formatTitle(value){ let obj = { agree:'已通过', refuse:'已拒绝', ignore:'已忽略' } return obj[value.status]; } }, computed:{ ...mapState({ applyList:state=>state.user.apply.rows }) }, data() { return { form:{ friend_id:0, nickname:"", lookme:1, lookhim:1 }, id:0, page:1, loadmore:'上拉加载更多',// 没有更多了,加载中... } }, // 监听下拉刷新 onPullDownRefresh() { this.page = 1; this.$store.dispatch('getApply',this.page).then(res=>{ uni.showToast({ title:'刷新成功', icon:'none' }) uni.stopPullDownRefresh() }) }, onLoad(e) { }, // 监听触底事件 onReachBottom() { if(this.loadmore !== '上拉加载更多'){ return; } this.loadmore = '加载中...'; this.page = this.page+1; this.$store.dispatch('getApply',this.page).then(res=>{ console.log(res) this.loadmore = this.applyList.length == this.page*10 ? '上拉加载更多' : '没有更多了'; }).catch(err=>{ this.page = this.page-1; this.loadmore = '上拉加载更多'; }) }, onShow() { if(this.loadmore !== '上拉加载更多'){ return; } this.loadmore = '加载中...'; this.page = this.page+1; this.$store.dispatch('getApply',this.page).then(res=>{ console.log(res) this.loadmore = this.applyList.length == this.page*10 ? '上拉加载更多' : '没有更多了'; }).catch(err=>{ this.page = this.page-1; this.loadmore = '上拉加载更多'; }) }, methods: { handle(item){ uni.navigateTo({ url:'../add-friend/add-friend?id='+item.id, }) } } } </script> <style> </style>
<template> <view> <view :class="getClass"> <!-- 状态栏 --> <view :style="'height:'+statusBarHeight+'px'"></view> <!-- 导航 --> <view class="w-100 flex align-center justify-between" style="height: 90rpx;"> <!-- 左边 --> <view class="flex align-center"> <!-- 返回按钮 --> <!-- #ifndef MP --> <free-icon-button v-if="showBack" @click="back"><text class="iconfont font-md"></text></free-icon-button> <!-- #endif --> <!-- 标题 --> <slot> <text v-if="title" class="font-md ml-3">{{getTitle}}</text> </slot> </view> <!-- 右边 --> <view class="flex align-center" v-if="showRight"> <slot name="right"> <free-icon-button @click="search"><text class="iconfont font-md"></text></free-icon-button> <free-icon-button @click="openExtend"><text class="iconfont font-md"></text></free-icon-button> </slot> </view> </view> </view> <!-- 占位 --> <view v-if="fixed" :style="fixedStyle"></view> <!-- 扩展菜单 --> <free-popup v-if="showRight" ref="extend" :bodyWidth="320" :bodyHeight="525" bodyBgColor="bg-dark" transformOrigin="right top"> <view class="flex flex-column" style="width: 320rpx;height: 525rpx;"> <view class="flex-1 flex align-center" hover-class="bg-hover-dark" v-for="(item,index) in menus" :key="index" @click="clickEvent(item)"> <text class="iconfont pl-3 pr-2 font-md text-white">{{item.icon}}</text> <text class="font-md text-white">{{item.name}}</text> </view> </view> </free-popup> </view> </template> <script> import freeIconButton from "./free-icon-button.vue" import freePopup from "./free-popup.vue" export default { props: { showBack:{ type:Boolean, default:false }, backEvent:{ type:Boolean, default:true }, title: { type: [String,Boolean], default:false }, fixed:{ type:Boolean, default:true }, noreadnum:{ type:[Number,String], default:0 }, bgColor:{ type:String, default:"bg-light" }, showRight:{ type:Boolean, default:true } }, components:{ freeIconButton, freePopup }, data() { return { statusBarHeight:0, navBarHeight:0, menus:[ { name:"发起群聊", event:"navigateTo", path:"/pages/mail/mail/mail?type=createGroup", icon:"\ue633" }, { name:"添加好友", event:"navigateTo", path:"/pages/common/search/search", icon:"\ue65d" }, // #ifndef H5 { name:"扫一扫", event:"", icon:"\ue614" }, // #endif { name:"收付款", event:"", icon:"\ue66c" }, { name:"帮助与反馈", event:"", icon:"\ue66c" } ], } }, mounted() { // #ifdef APP-PLUS-NVUE this.statusBarHeight = plus.navigator.getStatusbarHeight() // #endif this.navBarHeight = this.statusBarHeight + uni.upx2px(90) }, computed: { fixedStyle() { return `height:${this.navBarHeight}px` }, getTitle(){ let noreadnum = this.noreadnum > 0 ? '('+this.noreadnum+')' : '' return this.title + noreadnum }, getClass(){ let fixed = this.fixed?'fixed-top':'' return `${fixed} ${this.bgColor}` } }, methods: { openExtend() { this.$refs.extend.show(uni.upx2px(415),uni.upx2px(150)) }, // 返回 back(){ if(this.backEvent){ return uni.navigateBack({ delta: 1 }); } this.$emit('back') }, search(){ uni.navigateTo({ url: '/pages/common/search/search' }); }, clickEvent(item){ this.$refs.extend.hide() switch (item.event){ case 'navigateTo': uni.navigateTo({ url: item.path, }); break; default: uni.showToast({ title: '靓仔,自己发挥', icon: 'none' }); break; } } }, } </script> <style> </style>
<template> <view> <!-- 导航栏 --> <free-nav-bar :title="detail.name" :noreadnum="totalNoreadnum" showBack> <free-icon-button slot="right" @click="openChatSet"><text class="iconfont font-lg"></text> </free-icon-button> </free-nav-bar> <!-- 聊天内容区域 --> <scroll-view scroll-y class="bg-light position-fixed left-0 right-0 px-3" style="bottom: 105rpx;box-sizing: border-box;" :style="chatBodyBottom" :show-scrollbar="false" :scroll-into-view="scrollIntoView" :scroll-with-animation="true" @click="clickPage"> <!-- 聊天信息列表组件 --> <view v-for="(item,index) in list" :key="index" :id="'chatItem_'+index"> <free-chat-item :item="item" :index="index" ref="chatItem" :pretime=" index > 0 ? list[index-1].create_time : 0" @long="long" @preview="previewImage" :shownickname="currentChatItem.shownickname"></free-chat-item> </view> </scroll-view> <!-- #ifdef APP-PLUS-NVUE --> <div v-if="mode === 'action' || mode === 'emoticon'" class="position-fixed top-0 right-0 left-0" :style="'bottom:'+maskBottom+'px;'" @click="clickPage"></div> <!-- #endif --> <!-- 底部输入框 --> <view class="position-fixed left-0 right-0 border-top flex align-center" style="background-color: #F7F7F6;height: 105rpx;" :style="'bottom:'+KeyboardHeight+'px;'"> <free-icon-button v-if="mode === 'audio'" @click="changeVoiceOrText"><text class="iconfont font-lg"></text></free-icon-button> <free-icon-button v-else @click="changeVoiceOrText"><text class="iconfont font-lg"></text> </free-icon-button> <view class="flex-1"> <view v-if="mode === 'audio'" class="rounded flex align-center justify-center" style="height: 80rpx;" :class="isRecording?'bg-hover-light':'bg-white'" @touchstart="voiceTouchStart" @touchend="voiceTouchEnd" @touchcancel="voiceTouchCancel" @touchmove="voiceTouchMove"> <text class="font">{{isRecording ? '松开 结束':'按住 说话'}}</text> </view> <textarea v-else fixed class="bg-white rounded p-2 font-md" style="height: 50rpx;max-width: 450rpx;" :adjust-position="false" v-model="text" @focus="mode = 'text'" /> </view> <!-- 表情 --> <free-icon-button @click="openActionOrEmoticon('emoticon')"><text class="iconfont font-lg"></text> </free-icon-button> <template v-if="text.length === 0"> <!-- 扩展菜单 --> <free-icon-button @click="openActionOrEmoticon('action')"><text class="iconfont font-lg"></text> </free-icon-button> </template> <view v-else class="flex-shrink"> <!-- 发送按钮 --> <free-main-button name="发送" @click="send('text')"></free-main-button> </view> </view> <!-- 扩展菜单 --> <free-popup ref="action" bottom transformOrigin="center bottom" @hide="KeyboardHeight = 0" :mask="false"> <view style="height: 580rpx;" class="border-top border-light-secondary bg-light"> <swiper :indicator-dots="emoticonOrActionList.length > 1" style="height: 510rpx;"> <swiper-item class="row" v-for="(item,index) in emoticonOrActionList" :key="index"> <view class="col-3 flex flex-column align-center justify-center" style="height: 255rpx;" v-for="(item2,index2) in item" :key="index2" @click="actionEvent(item2)"> <image :src="item2.icon" mode="widthFix" style="width: 100rpx;height: 100rpx;"></image> <text class="font-sm text-muted mt-2">{{item2.name}}</text> </view> </swiper-item> </swiper> </view> </free-popup> <!-- 弹出层 --> <free-popup ref="extend" :bodyWidth="240" :bodyHeight="450" :tabbarHeight="105"> <view class="flex flex-column" style="width: 240rpx;" :style="getMenusStyle"> <view class="flex-1 flex align-center" hover-class="bg-light" v-for="(item,index) in menusList" :key="index" @click="clickEvent(item.event)"> <text class="font-md pl-3">{{item.name}}</text> </view> </view> </free-popup> <!-- 录音提示 --> <view v-if="isRecording" class="position-fixed top-0 left-0 right-0 flex align-center justify-center" style="bottom: 105rpx;"> <view style="width: 360rpx;height: 360rpx;background-color: rgba(0,0,0,0.5);" class="rounded flex flex-column align-center justify-center"> <image src="/static/images/audio/audio/recording.gif" style="width: 150rpx;height: 150rpx;"></image> <text class="font text-white mt-3">{{unRecord ? '松开手指,取消发送':'手指上滑,取消发送'}}</text> </view> </view> </view> </template> <script> // #ifdef APP-PLUS-NVUE const dom = weex.requireModule('dom') // #endif import freeNavBar from "@/components/free-ui/free-nav-bar.vue" import freeIconButton from "@/components/free-ui/free-icon-button.vue" import freeChatItem from '@/components/free-ui/free-chat-item.vue'; import freePopup from "@/components/free-ui/free-popup.vue" import freeMainButton from '@/components/free-ui/free-main-button.vue'; import { mapState, mapMutations } from 'vuex' import auth from '@/common/mixin/auth.js'; import $U from '@/common/free-lib/util.js'; import $H from '@/common/free-lib/request.js'; import $C from '@/common/free-lib/config.js'; export default { mixins: [auth], components: { freeNavBar, freeIconButton, freeChatItem, freePopup, freeMainButton }, data() { return { scrollIntoView: "", // 模式 text输入文字,emoticon表情,action操作,audio音频 mode: "text", // 扩展菜单列表 actionList: [ [{ name: "相册", icon: "/static/images/extends/pic.png", event: "uploadImage" }, { name: "拍摄", icon: "/static/images/extends/video.png", event: "uploadVideo" }, { name: "收藏", icon: "/static/images/extends/shoucan.png", event: "openFava" }, { name: "名片", icon: "/static/images/extends/man.png", event: "sendCard" }, { name: "语音通话", icon: "/static/images/extends/phone.png", event: "" }, { name: "位置", icon: "/static/images/extends/path.png", event: "" }] ], emoticonList: [], // 键盘高度 KeyboardHeight: 0, menusList: [], navBarHeight: 0, list: [], // 当前操作的气泡索引 propIndex: -1, // 输入文字 text: "", // 音频录制状态 isRecording: false, RecordingStartY: 0, // 取消录音 unRecord: false, detail: { id: 0, name: "", avatar: "", chat_type: "user" } } }, mounted() { var statusBarHeight = 0 // #ifdef APP-PLUS-NVUE statusBarHeight = plus.navigator.getStatusbarHeight() // #endif this.navBarHeight = statusBarHeight + uni.upx2px(90) // 监听键盘高度变化 uni.onKeyboardHeightChange(res => { if (this.mode !== 'action' && this.mode !== 'emoticon') { this.KeyboardHeight = res.height } if (this.KeyboardHeight > 0) { this.pageToBottom() } }) // 注册发送音频事件 this.regSendVoiceEvent((url) => { if (!this.unRecord) { this.send('audio', url, { time: this.RecordTime }) } }) this.pageToBottom() }, computed: { ...mapState({ chatList: state => state.user.chatList, RECORD: state => state.audio.RECORD, RecordTime: state => state.audio.RecordTime, chat: state => state.user.chat, totalNoreadnum: state => state.user.totalNoreadnum, user: state => state.user.user }), // 当前会话配置信息 currentChatItem() { let index = this.chatList.findIndex(item => item.id === this.detail.id && item.chat_type === this.detail .chat_type) if (index !== -1) { return this.chatList[index] } return {} }, // 获取蒙版的位置 maskBottom() { return this.KeyboardHeight + uni.upx2px(105) }, // 动态获取菜单高度 getMenusHeight() { let H = 100 return this.menusList.length * H }, // 获取菜单的样式 getMenusStyle() { return `height: ${this.getMenusHeight}rpx;` }, // 判断是否操作本人信息 isdoSelf() { // 获取本人id(假设拿到了) let id = 1 let user_id = this.propIndex > -1 ? this.list[this.propIndex].user_id : 0 return user_id === id }, // 聊天区域bottom chatBodyBottom() { return `bottom:${uni.upx2px(105) + this.KeyboardHeight}px;top:${this.navBarHeight}px;` }, // 获取操作或者表情列表 emoticonOrActionList() { return (this.mode === 'emoticon' || this.mode === 'action') ? this[this.mode + 'List'] : [] }, // 所有信息的图片地址 imageList() { let arr = [] this.list.forEach((item) => { if (item.type === 'emoticon' || item.type === 'image') { arr.push(item.data) } }) return arr } }, watch: { mode(newValue, oldValue) { if (newValue !== 'action' && newValue !== 'emoticon') { this.$refs.action.hide() } if (newValue !== 'text') { uni.hideKeyboard() } } }, onLoad(e) { if (!e.params) { return this.backToast() } this.detail = JSON.parse(decodeURIComponent(e.params)) console.log(this.detail); // 初始化 this.__init() // 创建聊天对象 this.chat.createChatObject(this.detail) // 获取历史记录 this.list = this.chat.getChatDetail() // 监听接收聊天信息 uni.$on('onMessage', this.onMessage) uni.$on('updateHistory', this.updateHistory) // 监听发送收藏和名片 uni.$on('sendItem', this.onSendItem) }, destroyed() { // 销毁聊天对象 this.chat.destoryChatObject() // 销毁监听接收聊天消息 uni.$off('onMessage', this.onMessage) uni.$off('updateHistory', this.updateHistory) uni.$off('sendItem', this.onSendItem) }, methods: { ...mapMutations(['regSendVoiceEvent']), onSendItem(e) { if (e.sendType === 'fava' || e.sendType === 'card') { this.send(e.type, e.data, e.options) } }, updateHistory(isclear = true) { if (isclear) { this.list = [] } else { this.list = this.chat.getChatDetail() } }, onMessage(message) { console.log('[聊天页] 监听接收聊天信息', message); if ((message.from_id === this.detail.id && message.chat_type === 'user') || (message.chat_type === 'group' && message.to_id === this.detail.id)) { if (message.isremove !== 1) { this.list.push(message) // 置于底部 return this.pageToBottom() } // 撤回消息 let index = this.list.findIndex(item => item.id === message.id) if (index !== -1) { this.list[index].isremove = 1 } } }, __init() { var total = 24; var page = Math.ceil(total / 8); var arr = []; for (var i = 0; i < page; i++) { var start = i * 8; arr[i] = []; for (var j = 0; j <= 8; j++) { arr[i].push({ name: '表情' + (start + j), icon: '/static/images/emoticon/5497/' + (start + j) + '.gif', event: 'sendEmoticon' }) } } this.emoticonList = arr; // var total = 20 // var page = Math.ceil(total/8) // var arr = [] // for (var i = 0; i < page; i++) { // var start = i*8 // arr[i] = [] // for (var j = 0; j < 8; j++) { // var no = start + j // if ((no+1) > total) { // continue; // } // arr[i].push({ // name:"表情"+no, // icon: $C.emoticonUrl + no +'.gif', // event:"sendEmoticon" // }) // } // } // this.emoticonList = arr // 初始化会话列表 this.chat.initChatListItem({ chat_type: this.detail.chat_type, to_id: this.detail.id, to_name: this.detail.name, to_avatar: this.detail.avatar, data: this.detail.chat_type === 'user' ? '你们已经是好友,可以开始聊天了' : '你已经加入群聊,可以开始聊天了' }) }, // 打开扩展菜单或者表情包 openActionOrEmoticon(mode = 'action') { this.mode = mode this.$refs.action.show() uni.hideKeyboard() this.KeyboardHeight = uni.upx2px(580) }, // 发送 send(type, data = '', options = {}) { // 组织数据格式 switch (type) { case 'text': data = data || this.text break; } let message = this.chat.formatSendData({ type, data, options }) // 渲染到页面 let index = this.list.length this.list.push(message) // 监听上传进度 let onProgress = false if (message.type !== 'text' && message.type !== 'emoticon' && message.type !== 'card' && !message.data .startsWith('http')) { onProgress = (progress) => { console.log('上传进度:', progress); } } // 发送到服务端 this.chat.send(message, onProgress).then(res => { console.log(res); // 发送成功 this.list[index].id = res.id this.list[index].data = res.data; this.list[index].sendStatus = 'success' }).catch(err => { // 发送失败 this.list[index].sendStatus = 'fail' console.log(err); }) // 发送文字成功,清空输入框 if (type === 'text') { this.text = '' } // 置于底部 this.pageToBottom() }, // 回到底部 pageToBottom() { // #ifdef APP-PLUS-NVUE let chatItem = this.$refs.chatItem let lastIndex = chatItem.length > 0 ? chatItem.length - 1 : 0 if (chatItem[lastIndex]) { dom.scrollToElement(chatItem[lastIndex], {}) } // #endif // #ifndef APP-NVUE setTimeout(() => { let lastIndex = this.list.length - 1 this.scrollIntoView = 'chatItem_' + lastIndex }, 300) // #endif }, // 长按消息气泡 long({ x, y, index }) { // 初始化 索引 this.propIndex = index // 组装菜单 let menus = [{ name: "发送给朋友", event: 'sendToChatItem' }, { name: "收藏", event: 'fava' }, { name: "删除", event: 'delete' }] let item = this.list[this.propIndex] let isSelf = this.user.id === item.from_id if (isSelf) { menus.push({ name: "撤回", event: 'removeChatItem' }) } // #ifndef H5 if (item.type === 'text') { menus.unshift({ name: "复制", event: 'copy', }) } // #endif this.menusList = menus // 显示扩展菜单 this.$refs.extend.show(x, y) }, // 操作菜单方法分发 clickEvent(event) { let item = this.list[this.propIndex] let isSelf = this.user.id === item.from_id switch (event) { case 'removeChatItem': // 撤回消息 // 拿到当前被操作的信息 this.chat.recall(item).then(res => { item.isremove = 1 }) break; case 'sendToChatItem': uni.navigateTo({ url: '../chat-list/chat-list?params=' + encodeURIComponent(JSON.stringify(item)), }); break; case 'copy': // 复制 uni.setClipboardData({ data: item.data, success: () => { uni.showToast({ title: '复制成功', icon: 'none' }); } }); break; case 'delete': uni.showModal({ content: '是否要删除该记录?', success: (res) => { if (!res.confirm) return; this.chat.deleteChatDetailItem(item, isSelf) this.list.splice(this.propIndex, 1) // 删除最后一条消息 if (this.list.length === this.propIndex) { this.chat.updateChatItem({ id: this.detail.id, chat_type: this.detail.chat_type }, (v) => { let o = this.list[this.propIndex - 1] let data = '' if (o) { data = this.chat.formatChatItemData(o, isSelf) } v.data = data return v }) } } }); break; case 'fava': // 加入收藏 uni.showModal({ content: '是否要加入收藏?', success: (res) => { if (res.confirm) { $H.post('/fava/create', { type: item.type, data: item.data, options: JSON.stringify(item.options) }).then(res => { uni.showToast({ title: '加入收藏成功', icon: 'none' }); }) } } }); break; } // 关闭菜单 this.$refs.extend.hide() }, // 扩展菜单 actionEvent(e) { switch (e.event) { case 'uploadImage': // 选择相册 uni.chooseImage({ count: 9, success: (res) => { // 发送到服务器 // 渲染到页面 res.tempFilePaths.forEach((item) => { this.send('image', item) }) } }) break; case 'uploadVideo': // 发送短视频 uni.chooseVideo({ maxDuration: 10, success: (res) => { this.send('video', res.tempFilePath) // 渲染页面 // 发送到服务端(获取视频封面,返回url) // 修改本地的发送状态 } }) break; case 'sendEmoticon': // 发送表情包 this.send('emoticon', e.icon) break; case 'openFava': // 发送收藏 uni.navigateTo({ url: '../../my/fava/fava?type=send', }); break; case 'sendCard': // 发送名片 uni.navigateTo({ url: '../../mail/mail/mail?type=sendCard&limit=1', }); break; } }, // 点击页面 clickPage() { this.mode = '' }, // 预览图片 previewImage(url) { uni.previewImage({ current: url, urls: this.imageList, indicator: "default" }) }, // 切换音频录制和文本输入 changeVoiceOrText() { this.mode = this.mode !== 'audio' ? 'audio' : 'text' }, // 录音相关 // 录音开始 voiceTouchStart(e) { // 初始化 this.isRecording = true this.RecordingStartY = e.changedTouches[0].screenY this.unRecord = false // 开始录音 this.RECORD.start({ format: "mp3" }) }, // 录音结束 voiceTouchEnd() { this.isRecording = false // 停止录音 this.RECORD.stop() }, // 录音被打断 voiceTouchCancel() { this.isRecording = false this.unRecord = true // 停止录音 this.RECORD.stop() }, voiceTouchMove(e) { let Y = Math.abs(e.changedTouches[0].screenY - this.RecordingStartY) this.unRecord = (Y >= 50) }, // 打开聊天信息设置 openChatSet() { uni.navigateTo({ url: '../chat-set/chat-set?params=' + JSON.stringify({ id: this.detail.id, chat_type: this.detail.chat_type }), }); } } } </script> <style> </style>
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar title="二维码名片" showBack :showRight="false"></free-nav-bar> <view class="p-5"> <view class="bg-white rounded p-4"> <view class="flex align-center mb-4"> <free-avatar :src="detail.avatar || '/static/images/demo/demo6.jpg'"></free-avatar> <view class="pl-4 flex flex-column"> <text class="font-md">{{detail.name}}</text> <text class="font text-light-muted">地区</text> </view> </view> <view class="flex flex-column align-center justify-center"> <image :src="src" mode="" style="width: 550rpx;height: 550rpx;" class="bg-secondary mb-4"></image> <text class="font text-light-muted">扫一扫上面的二维码,加我的微信</text> </view> </view> </view> </view> </template> <script> import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeAvatar from '@/components/free-ui/free-avatar.vue'; import {mapState} from 'vuex'; import $C from '@/common/free-lib/config.js'; export default { components:{ freeNavBar, freeAvatar }, computed:{ ...mapState({ user:state=>state.user.user }) }, data() { return { detail:{ id:0, name:"", avatar:'' } } }, onLoad(e) { if(e.params){ this.detail = JSON.parse(decodeURIComponent(e.params)); this.src = `${$C.codeUrl}/${e.type}_qrcode/${this.detail.id}?token=${this.user.token}`; console.log(this.src); } }, methods: { } } </script> <style> </style>
<script> export default { onLaunch: function() { // #ifdef APP-PLUS-NVUE // 加载公共图标库 const domModule = weex.requireModule('dom') domModule.addRule('fontFace', { 'fontFamily': "iconfont", 'src': "url('/static/font_1365296_2ijcbdrmsg.ttf')" }); // #endif // 初始化录音管理器 this.$store.commit('initRECORD'); // 初始化登录状态 this.$store.dispatch('initLogin'); console.log('App Launch') }, onShow: function() { this.$store.dispatch('reconnect'); console.log('App Show') }, onHide: function() { console.log('App Hide') } } </script> <style> /*每个页面公共css */ @import "./common/free.css"; @import "./common/common.css"; /* #ifndef APP-PLUS-NVUE */ @import "./common/free-icon.css"; /* #endif */ /* #ifdef MP */ ::-webkit-scrollbar{ display: none; } /* #endif */ </style>
/* 1.页面背景色 */ .page{ background-color: #EDEDED; /* #ifndef APP-PLUS-NVUE */ min-height: 100vh; height: auto; /* #endif */ /* #ifdef APP-PLUS-NVUE */ flex: 1; /* #endif */ } /* 2.主背景色(原谅绿) */ .main-bg-color{ background-color: #08C060; } .main-bg-hover-color{ background-color: #08D869; } /* 3.主文字色(原谅绿) */ .main-text-color{ color: #08C060; } .border-main{ border-color:#08C060 !important; } .bg-chat-item{ background-color: #08C060; } .text-chat-item{ color: #08C060; }
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar title="选择" showBack :showRight="false"> </free-nav-bar> <!-- 搜索框 --> <view class="p-3 bg-light position-fixed left-0 right-0" :style="'top:'+top+'px;'"> <input type="text" value="" v-model="keyword" placeholder="搜索" class="bg-white rounded" placeholder-class="text-center" style="height: 80rpx;"/> </view> <view style="height:140rpx;"></view> <!-- 联系人列表 --> <view class="px-2 py-1 bg-light"> <text class="font-sm text-muted">{{keyword ? '搜索结果' : '最近联系人'}}</text> </view> <free-list-item v-for="(item,index) in allList" :key="index" :title="item.name" :cover="item.avatar || '/static/images/userpic.png'" showRight :showRightIcon="false" @click="selectItem(item)"> <view v-if="muliSelect" slot="right" class="border rounded-circle flex align-center" style="width: 40rpx;height: 40rpx;" > <view v-if="item.checked" class="main-bg-color rounded-circle" style="width: 39rpx;height: 39rpx;"> </view> </view> </free-list-item> <view style="height:100rpx;" class="flex align-center justify-center" v-if="keyword !== '' && searchList.length === 0"> <text class="font text-light-muted">暂无搜索结果</text> </view> </view> </template> <script> import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeMainButton from '@/components/free-ui/free-main-button.vue'; import freeListItem from '@/components/free-ui/free-list-item.vue'; import freeAvatar from '@/components/free-ui/free-avatar.vue'; import $H from '@/common/free-lib/request.js'; export default { components:{ freeNavBar, freeMainButton, freeListItem, freeAvatar }, data() { return { keyword:'', muliSelect:false, top:0, list:[], group_id:0, } }, computed:{ // 最终列表 allList(){ return this.keyword === '' ? this.list : this.searchList; }, // 搜索结果列表 searchList(){ if(this.keyword === ''){ return []; } return this.list.filter(item=>{ return item.name.indexOf(this.keyword) !== -1; }) }, // 选中列表 selectList(){ return this.list.filter(item=>item.checked) }, // 选中数量 selectCount(){ return this.selectList.length; } }, methods: { update_data(e){ this.group_id = e.id; $H.get('/group_info/'+e.id).then(res=>{ this.list = res.group_users.map(item=>{ return { id:item.user_id, name:item.nickname || item.user.nickname || item.user.username, avatar:item.user.avatar } }) }) }, // 选中、取消选中 selectItem(item){ uni.showModal({ content:'是否要踢出该成员?', success:(res)=>{ if(res.confirm){ $H.post('/group/kickoff',{ id:this.group_id, user_id:item.id }).then(res=>{ console.log(res); uni.showToast({ title:'踢出成功', icon:'none' }); uni.navigateBack({ delta:1 }) }) } // 刷新页面 } }) } }, onLoad(e) { let res = uni.getSystemInfoSync(); let statusBarHeight = 0; // #ifndef MP statusBarHeight = res.statusBarHeight; // #endif this.top = statusBarHeight + uni.upx2px(90); if(e.id){ this.update_data(e); } } } </script> <style> </style>
<template> <view class="page"> <!-- 导航栏 --> <free-nav-bar showBack :showRight="detail.friend" bgColor="bg-white"> <view slot="right"> <free-icon-button v-if="detail.friend"><text class="iconfont font-md" @click="openAction"></text></free-icon-button> </view> </free-nav-bar> <view class="px-3 py-4 flex align-center bg-white border-bottom"> <free-avatar :src="detail.avatar" size="120"></free-avatar> <view class="flex flex-column ml-3 flex-1"> <view class="font-lg font-weight-bold flex justify-between"> <text class="font-lg font-weight-bold mb-1">{{detail.nickname}}</text> <image v-if="detail.star" src="/static/images/star.png" style="width: 40rpx;height: 40rpx;"></image> </view> <text class="font-md text-light-muted mb-1">账号:{{detail.username}}</text> <!-- <text class="font-md text-light-muted">地区:广东广州</text> --> </view> </view> <free-list-item v-if="detail.friend" showRight :showLeftIcon="false" @click="navigate(tagPath)"> <view class="flex align-center"> <text class="font-md text-dark mr-3">标签</text> <text class="font-md text-light-muted mr-2" v-for="(item,index) in detail.tags" :key="index">{{item}}</text> </view> </free-list-item> <free-divider></free-divider> <free-list-item v-if="detail.friend" showRight :showLeftIcon="false"> <view class="flex align-center"> <text class="font-md text-dark mr-3">朋友圈</text> <image src="/static/images/demo/cate_01.png" style="width: 90rpx; height: 90rpx;" class=" mr-2"></image> <image src="/static/images/demo/cate_01.png" style="width: 90rpx; height: 90rpx;" class=" mr-2"></image> <image src="/static/images/demo/cate_01.png" style="width: 90rpx; height: 90rpx;" class=" mr-2"></image> </view> </free-list-item> <free-list-item title="更多信息" showRight :showLeftIcon="false"></free-list-item> <free-divider></free-divider> <view v-if="detail.friend" class="py-3 flex align-center justify-center bg-white" hover-class="bg-light" @click="doEvent"> <text class="iconfont text-primary mr-1" v-if="!detail.isBlack"></text> <text class="font-md text-primary">{{detail.isblack ? '移除黑名单' : '发信息'}}</text> </view> <view v-else class="py-3 flex align-center justify-center bg-white" hover-class="bg-light" @click="navigate(addFriend())"> <text class="font-md text-primary">添加好友</text> </view> <!-- 扩展菜单 --> <free-popup ref="action" bottom transformOrigin="center bottom" maskColor> <scroll-view style="height: 580rpx;" scroll-y="true" class="bg-white" :show-scrollbar="false"> <free-list-item v-for="(item,index) in actions" :key="index" :title="item.title" :showRight="false" :border="false" @click="popupEvent(item)"> <text slot="icon" class="iconfont font-lg py-1">{{item.icon}}</text> </free-list-item> </scroll-view> </free-popup> </view> </template> <script> import freeNavBar from '@/components/free-ui/free-nav-bar.vue'; import freeIconButton from '@/components/free-ui/free-icon-button.vue'; import freeChatItem from '@/components/free-ui/free-chat-item.vue'; import freePopup from '@/components/free-ui/free-popup.vue'; import freeListItem from '@/components/free-ui/free-list-item.vue'; import freeDivider from '@/components/free-ui/free-divider.vue'; import freeAvatar from '@/components/free-ui/free-avatar.vue'; import auth from '@/common/mixin/auth.js'; import $H from '@/common/free-lib/request.js'; export default { mixins: [auth], components: { freeNavBar, freeIconButton, freeChatItem, freePopup, freeListItem, freeDivider, freeAvatar }, data() { return { detail: { id: 0, username: '', nickname: '', avatar: '', sex: '', sign: '', area: '', friend: false, lookhim: 1, lookme: 1, star: 0, isblack: 0, tags: [] }, } }, onShow() { this.getData(); }, onLoad(e) { uni.$on('saveRemarkTag', (e) => { this.detail.tagList = e.detail.tagList this.nickname = e.nickname; }) if (!e.user_id) { return this.backToast(); } this.detail.id = e.user_id; // 获取当前用户资料 this.getData(); }, beforeDestroy() { this.$refs.action.hide(); uni.$off('saveRemarkTag') }, computed: { tagPath() { return "mail/user-remark-tag/user-remark-tag?params="+JSON.stringify({ user_id:this.detail.id, nickname:this.detail.nickname, tags:this.detail.tags ? this.detail.tags.join(',') : '' }) }, actions() { return [{ icon: "\ue6b3", title: "设置备注和标签", type: "navigate", path: "mail/user-remark-tag/user-remark-tag?params="+JSON.stringify({ user_id:this.detail.id, nickname:this.detail.nickname, tags:this.detail.tags ? this.detail.tags.join(',') : '' }) }, { icon: "\ue613", title: "把他推荐给朋友", type: "navigate", path: "mail/send-card/send-card" }, { icon: "\ue6b0", title: this.detail.star ? '取消星标好友' : "设为星标朋友", type: "event", event: "setStar" }, { icon: "\ue667", title: "设置朋友圈和动态权限", type: "navigate", path: "mail/user-moments-auth/user-moments-auth?user_id="+this.detail.id+"¶ms="+JSON.stringify({ lookme:this.detail.lookme, lookhim:this.detail.lookhim, }) }, { icon: "\ue638", title: this.detail.isblack ? '移出黑名单' : "加入黑名单", type: "event", event: "setBlack" }, { icon: "\ue61c", title: "投诉", type: "navigate", path: "mail/user-report/user-report?params="+JSON.stringify({ user_id:this.detail.id, type:'user' }) }, { icon: "\ue638", title: "删除", type: "event", event: "deleteItem" }] } }, methods: { addFriend() { let obj = { friend_id: this.detail.id, nickname: this.detail.nickname, lookme: typeof this.detail.lookme === 'number' ? this.detail.lookme : 1, lookhim: typeof this.detail.lookhim === 'number' ? this.detail.lookhim : 1, }; return 'mail/add-friend/add-friend?params=' + JSON.stringify(obj); }, getData() { $H.get('/friend/read/' + this.detail.id).then(res => { if (!res) { return this.backToast('该用户不存在'); } this.detail = res; console.log(res); }); }, openAction() { this.$refs.action.show() }, navigate(url) { console.log(url) uni.navigateTo({ url: '/pages/' + url, }); }, // 操作菜单事件 popupEvent(e) { if (!e.type) { return; } setTimeout(() => { // 关闭弹出层 this.$refs.action.hide() }, 300) switch (e.type) { case 'navigate': this.navigate(e.path); break; case 'event': this[e.event](e); break; } }, // 删除好友 deleteItem(){ uni.showModal({ title: '是否要删除好友?', success: res => { if(res.confirm){ $H.post('/friend/destroy',{friend_id:this.detail.id}).then(res=>{ uni.showToast({ title:'删除好友成功', icon:'none' }); uni.reLaunch({ url:'/pages/tabbar/index/index' }) }) } }, fail: () => {}, complete: () => {} }); }, // 设为星标 setStar(e) { let star = this.detail.star == 0 ? 1 : 0; $H.post('/friend/setstar/' + this.detail.id, { star }).then(res => { this.detail.star = star; e.title = this.detail.star ? '取消标星好友' : '设为标星好友'; }); }, // 加入黑名单 setBlack(e) { let msg = this.detail.isblack ? '移出黑名单' : '加入黑名单'; uni.showModal({ content: '是否要' + msg, success: (res) => { if (res.confirm) { let isblack = this.detail.isblack == 0 ? 1:0 $H.post('/friend/setblack/' + this.detail.id, { isblack }).then(res => { this.detail.isblack = isblack; }); // this.detail.isBlack = !this.detail.isBlack; // e.title = this.isBlack ? '移出黑名单' : '加入黑名单'; uni.showToast({ title: msg + '成功', icon: 'none' }) } } }) }, // 发送消息 doEvent(e){ if(this.detail.isblack){ return this.setBlack(); } uni.navigateTo({ url:'../../chat/chat/chat?params='+encodeURIComponent(JSON.stringify({ id:this.detail.id, name:this.detail.nickname ? this.detail.nickname : this.detail.username, avatar:this.detail.avatar, chat_type:'user' })) }) } } } </script> <style> </style>
<template> <view> <!-- 导航栏 --> <free-nav-bar title="通讯录"></free-nav-bar> <!-- 通讯录列表 --> <scroll-view scroll-y="true" :style="'height:'+scrollHeight+'px;'" :scroll-into-view="scrollInto"> <free-list-item v-for="(item,index) in topList" :key="item.id" :title="item.title" :cover="item.cover" :showRight="item.id==='friend'" @click="navigate(item.path)" :showRightIcon='false'> <view slot="right"> <free-badge v-if="applyCount>0" :num="applyCount"></free-badge> </view> </free-list-item> <view v-for="(item,index) in list" :key="index" v-if="item.list.length>0" :id="'item-'+item.title"> <view class="py-2 px-3 border-bottom bg-light"> <text class="font-md text-dark">{{item.title}}</text> </view> <free-list-item v-for="(item2,index2) in item.list" :key="index2" :title="item2.name" :cover="item2.avatar ? item2.avatar : '/static/images/userpic.png'" @click="navigate('mail/user-base/user-base?user_id='+item2.user_id)"></free-list-item> </view> </scroll-view> <!-- 侧边导航条 --> <view class="position-fixed right-0 bottom-0 flex flex-column" :style="'top:'+top+'rpx;'" style="width: 50rpx;" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" > <view class="flex-1 flex align-center justify-center" v-for="(item,index) in list" :key="index"> <text class="font-sm text-muted">{{item.title}}</text> </view> </view> <!-- <block v-if="current"> --> <view class="position-fixed rounded-circle bg-light border flex align-center justify-center" style="width: 150rpx;height: 150rpx; left: 300rpx;" :style="'top:'+modalTop+'px;'" v-if="current!=''"> <text class="font-lg" >{{current}}</text> </view> <!-- </block> --> </view> </template> <script> import freeNavBar from "@/components/free-ui/free-nav-bar.vue"; import freeListItem from '@/components/free-ui/free-list-item.vue'; import freeBadge from '@/components/free-ui/free-badge.vue'; import { mapState } from 'vuex'; import auth from '@/common/mixin/auth.js'; export default { mixins:[auth], components:{ freeNavBar, freeListItem, freeBadge }, computed:{ ...mapState({ applyCount:state=>state.user.apply.count, list:state=>state.user.mailList }),//state.user.apply.count modalTop(){ return (this.scrollHeight-uni.upx2px(150))/2; }, itemHeight(){ let count = this.list.length; if(count<1){ return 0; } return this.scrollHeight/count; } }, onLoad() { console.log('mail/mail') let res = uni.getSystemInfoSync(); this.top = res.statusBarHeight + uni.upx2px(90); this.scrollHeight = res.windowHeight-this.top; this.$store.dispatch('getMailList'); }, data() { return { scrollInto:'', top:0, scrollHeight:0, current:'', topList:[ { id:'friend', title:"新的朋友", cover:"/static/images/mail/friend.png", path:"mail/apply-list/apply-list" }, { id:'group', title:"群聊", cover:"/static/images/mail/group.png", path:"mail/group-list/group-list" }, { id:'tag', title:"标签", cover:"/static/images/mail/tag.png", path:"mail/tag-list/tag-list" } ] } }, methods: { touchstart(e){ this.changeScrollInto(e); }, touchmove(e){ this.changeScrollInto(e); }, touchend(){ this.current = ''; }, // 联动 changeScrollInto(e){ // let Y = e.touches[0].pageY; // let index = Math.floor(Y / this.itemHeight); // let item = this.list[index]; // if(item){ // this.scrollInto = 'item-'+item.letter; // this.current = item.letter; // } let Y = e.touches[0].pageY // #ifdef MP Y = Y - this.top // #endif let index = Math.floor(Y / this.itemHeight) let item = this.list[index] if(item){ this.scrollInto = 'item-'+item.letter this.current = item.letter } } } } </script> <style> </style>
import $U from '@/common/free-lib/util.js'; import $H from '@/common/free-lib/request.js'; import Chat from '@/common/free-lib/chat.js'; import $C from '@/common/free-lib/config.js'; export default { state: { user: false, apply: { rows: [], count: 0, }, mailList: [], chat: null, // 会话列表 chatList: [], // 总未读数 totalNoreadnum: 0, notice: { avatar: '', user_id: 0, num: 0 } }, mutations: { updateUser(state, { k, v }) { if (state.user) { state.user[k] = v; $U.setStorage('user', JSON.stringify(state.user)); } } }, actions: { // 登录后处理 login({ state, dispatch }, user) { // 存到状态种 state.user = user; // 存储到本地存储中 $U.setStorage('token', user.token); $U.setStorage('user', JSON.stringify(user)); $U.setStorage('user_id', user.id); // 获取好友申请列表 dispatch('getApply'); // 更新角标提示 dispatch('updateMailBadge'); // 连接socket state.chat = new Chat({ url: $C.socketUrl }) // 获取会话列表 dispatch('getChatList'); // 初始化总未读数角标 dispatch('updateBadge'); // 获取朋友圈动态通知 dispatch('getNotice'); }, // 退出登录 logout({ state }) { // 清除登录状态 state.user = false; // 清除本地存储数据 $U.removeStorage('token'); $U.removeStorage('user'); $U.removeStorage('user_id'); // 关闭socket连接 if(state.chat){ state.chat.close(); state.chat = null; } // 跳转到登录页 uni.reLaunch({ url: '/pages/common/login/login' }) // 注销监听事件 uni.$off('onUpdateChatList') uni.$off('momentNotice') uni.$off('totalNoreadnum') }, // 初始化登录状态 initLogin({ state, dispatch }) { // 拿到存储的数据 let user = $U.getStorage('user'); if (user) { // 初始化登录状态 state.user = JSON.parse(user); // 连接socket state.chat = new Chat({ url: $C.socketUrl }) // 获取会话列表 dispatch('getChatList'); // 获取离线信息 // 获取好友申请列表 dispatch('getApply'); // 初始化总未读数角标 dispatch('updateBadge'); // 获取朋友圈动态通知 dispatch('getNotice'); } }, // 获取好友申请列表 getApply({ state, dispatch }, page = 1) { $H.get('/apply/' + page).then(res => { if (page === 1) { state.apply = res } else { // 下拉刷新 state.apply.rows = [...state.apply.rows, ...res.rows] state.apply.count = res.count } // 更新通讯录角标提示 dispatch('updateMailBadge'); }); }, // 更新通讯录角标提示 updateMailBadge({ state }) { let count = state.apply.count > 99 ? '99+' : state.apply.count.toString(); console.log(state.apply.count); if (state.apply.count > 0) { return uni.setTabBarBadge({ index: 1, text: count }) } uni.removeTabBarBadge({ index: 1 }) }, // 获取通讯录列表 getMailList({ state }) { $H.get('/friend/list').then(res => { state.mailList = res.rows.newList ? res.rows.newList : []; }) }, // 获取会话列表 getChatList({ state }) { state.chatList = state.chat.getChatList() // 监听会话列表变化 uni.$on('onUpdateChatList', (list) => { state.chatList = list }) }, // 获取朋友圈动态通知 getNotice({ state }) { state.notice = state.chat.getNotice(); if (state.notice.num > 0) { uni.setTabBarBadge({ index: 2, text: state.notice.num > 99 ? '99+' : state.notice.num.toString() }) } else { uni.removeTabBarBadge({ index: 2 }) } uni.$on('momentNotice', (notice) => { state.notice = notice }) }, // 初始化总未读数角标 // 更新未读数 async updateBadge(list = false) { // 开启监听总未读数变化 uni.$on('totalNoreadnum', (num) => { state.totalNoreadnum = num }) state.chat.updateBadge() }, // 初始化总未读数角标 updateBadge({ state }) { // 开启监听总未读数变化 uni.$on('totalNoreadnum', (num) => { console.log('totalNoreadnum:', num); state.totalNoreadnum = num }) state.chat.updateBadge() }, // 断线自动重连 reconnect({state}){ if(state.user && state.chat){ state.chat.reconnect() } } }, }
感谢大家观看,我们下次见
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。