| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- <script>
- import { mapState } from 'vuex'
- import moment from 'moment'
- import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js'
- import api from '@/api'
- export default {
- mixins: [MescrollMixin], // 使用mixin
- data() {
- return {
- downOption: {
- autoShowLoading: true, // 显示下拉刷新的进度条
- textColor: '#adadad' // 下拉刷新的文本颜色
- },
- upOption: {
- use: false, // 禁止上拉
- toTop: {
- src: '' // 不显示回到顶部按钮
- }
- },
- pageNum: 1, // 页码
- pageSize: 10, // 页长
- isEnd: false, // 是否无消息
- msgList: [], // 消息列表
- send_msg: '', // 发送的消息
- send_img: '', // 发送的图片
- timer: '', // 定时器 timer
- isScroll: false, // 是否滚动到底部
- deny_ids: '' // 测回的消息id
- }
- },
- computed: {
- ...mapState({
- user: 'user',
- token: 'token'
- }),
- // 最新的消息id
- last_id() {
- return this.msgList.length && this.msgList[this.msgList.length - 1].id
- },
- // 最'旧'的消息id
- first_id() {
- return this.msgList.length && this.msgList[0].id
- },
- denyList() {
- return this.deny_ids.split('|')
- },
- msgListFilter() {
- let list = JSON.parse(JSON.stringify(this.msgList))
- for (let i = 0; i < this.denyList.length; i++) {
- for (let j = 0; j < list.length; j++) {
- if (this.denyList[i] === list[j].id) {
- list.splice(j, 1)
- break
- }
- }
- }
- return list
- },
- actionList() {
- return this.user.user_type === 99 ? ['复制', '+1', '撤回', '封禁'] : ['复制', '+1']
- }
- },
- methods: {
- /*上拉刷新的回调 */
- async downCallback() {
- try {
- let res = await api.chatGetOldMessage(this.first_id)
- res.reverse()
- // 需自行维护页码
- this.pageNum++
- // 先隐藏下拉刷新的状态
- this.mescroll.endSuccess()
- // 不满一页,说明已经无更多消息 (建议根据您实际接口返回的总页码数,总消息量,是否有消息的字段来判断)
- if (res.length < this.pageSize) {
- this.isEnd = true // 标记已无更多消息
- this.mescroll.lockDownScroll(true) // 锁定下拉
- }
- // 生成VIEW_ID,大写,避免污染源数据
- res.forEach(val => {
- val.VIEW_ID = 'msg' + val.id // 不以数字开头
- })
- // 获取当前最顶部的VIEW_ID (注意是写在res.concat前面)
- let topMsg = this.msgList[0]
- //设置列表数据
- this.msgList = res.concat(this.msgList) // 注意不是this.msgList.concat
- // 开启定时器
- if (!this.timer) this.msgLoop()
- this.$nextTick(() => {
- if (this.pageNum <= 2) {
- // 第一页直接滚动到底部 ( this.pageNum已在前面加1 )
- this.toBottom()
- } else if (topMsg) {
- // 保持顶部消息的位置
- let view = uni.createSelectorQuery().select('#' + topMsg.VIEW_ID)
- view
- .boundingClientRect(v => {
- // console.log('节点离页面顶部的距离=' + v.top)
- this.mescroll.myScrollTo(v.top - 92, 0) // 减去上偏移量100
- })
- .exec()
- }
- })
- } catch (e) {
- this.pageNum-- // 联网失败,必须回减页码
- this.mescroll.endErr() // 隐藏下拉刷新的状态
- }
- },
- async sendMsg(type = '1') {
- if ((type === '1' && !this.send_msg) || (type === '2' && !this.send_img)) return
- let content = type === '1' ? this.send_msg : this.send_img
- // 置空防止二次点击
- this.send_msg = ''
- this.send_img = ''
- this.isScroll = true // 新消息请求后滚动到底部
- await api.chatSendMessage({ type, content })
- // 停止定时器
- clearInterval(this.timer)
- this.timer = ''
- // 手动刷新
- await this.getNewMsg()
- },
- async getNewMsg() {
- if (!this.token) return
- try {
- let { deny_ids, msg_list } = await api.chatGetNewMessage(this.last_id)
- msg_list.reverse()
- this.msgList = [...this.msgList, ...msg_list]
- this.deny_ids = deny_ids
- } catch (e) {}
- if (this.isScroll) {
- this.toBottom()
- this.isScroll = false
- }
- // 如果定时器被清除则重新开启轮询
- if (!this.timer) {
- this.msgLoop()
- }
- },
- // 消息轮询
- msgLoop() {
- this.timer = setInterval(() => {
- this.getNewMsg()
- }, 3000)
- },
- chooseImg() {
- uni.chooseImage({
- count: 1,
- sizeType: ['compressed'], // 只传压缩图
- success: async ({ tempFilePaths }) => {
- let res = await api.uploadImg({
- filePath: tempFilePaths[0],
- formData: {
- type: 'message'
- }
- })
- let result = JSON.parse(res).data
- this.send_img = result.base_url
- await this.sendMsg('2')
- }
- })
- },
- previewImg(src) {
- uni.previewImage({
- urls: [src]
- })
- },
- isShowTime(pre, cur) {
- if (pre < 0) return
- let preTimestamp = moment(this.msgList[pre].create_time).valueOf()
- let curTimestamp = moment(this.msgList[cur].create_time).valueOf()
- return curTimestamp - preTimestamp > 300000 // 如果大于五分钟就显示时间
- },
- actionSheet(msg) {
- uni.showActionSheet({
- itemList: this.actionList,
- success: ({ tapIndex }) => {
- switch (tapIndex) {
- case 0:
- this.$common.copy(msg.content)
- break
- case 1:
- this.send_msg = msg.content
- this.sendMsg()
- break
- case 2:
- this.backMessage(msg)
- break
- case 3:
- this.deny(msg)
- break
- default:
- break
- }
- }
- })
- },
- async backMessage(msg) {
- await api.chatBackMessage(msg.id)
- this.$common.toast('已撤回')
- },
- async deny(msg) {
- await api.chatDenyUser(msg.uid)
- this.$common.toast('封禁成功')
- },
- toBottom() {
- setTimeout(() => {
- this.mescroll.myScrollTo(99999, 0)
- uni.pageScrollTo({ scrollTop: 99999, duration: 50 })
- })
- }
- },
- beforeDestroy() {
- clearInterval(this.timer)
- }
- }
- </script>
- <template>
- <view class="bg">
- <!-- 需配置bottom的偏移量, 用于底部留白 -->
- <mescroll-uni
- ref="mescrollRef"
- @init="mescrollInit"
- :down="downOption"
- @down="downCallback"
- :up="upOption"
- :bottom="160"
- >
- <!-- 无更多消息 -->
- <view v-if="isEnd" class="msg-end">没有更多消息了</view>
- <!-- 消息列表 (必须配置id,以便定位) -->
- <view v-for="(msg, i) in msgListFilter" :key="msg.id" :id="msg.VIEW_ID">
- <view class="time" v-if="isShowTime(i - 1, i)">
- {{ msg.create_time }}
- </view>
- <view class="msg flex" :class="{ self: msg.uid === user.id }">
- <view class="avatar">
- <u-avatar :src="msg.avatar" size="80rpx"></u-avatar>
- </view>
- <view
- class="flex1"
- :class="{
- flex: msg.uid === user.id,
- 'flex-end': msg.uid === user.id
- }"
- @click="
- () => {
- if (user.user_type === 99) actionSheet(msg)
- }
- "
- >
- <view class="nickname" v-if="msg.uid !== user.id">{{ msg.nick_name }}</view>
- <view class="msg-warp flex">
- <view v-if="msg.type === '1'" class="msg-content">
- <text>{{ msg.content }}</text>
- </view>
- <u-image
- v-else
- mode="widthFix"
- width="250rpx"
- :src="msg.content"
- @click.native.stop="previewImg(msg.content)"
- ></u-image>
- </view>
- </view>
- </view>
- </view>
- </mescroll-uni>
- <!-- 底部 fixed定位 -->
- <view class="bottom flex flex-center">
- <view class="flex1 input-box flex align-center">
- <input
- type="text"
- v-model="send_msg"
- class="send-input"
- :hold-keyboard="false"
- auto-blur
- @confirm="
- ({ detail }) => {
- send_msg = detail.value
- sendMsg()
- }
- "
- confirm-type="send"
- :class="{ 'text-center': user.user_type !== 99 }"
- :disabled="user.user_type !== 99"
- :placeholder="user.user_type !== 99 ? '全体禁言中' : ''"
- />
- <u-button
- text="发送"
- shape="circle"
- color="var(--theme)"
- class="send_btn"
- @click="sendMsg()"
- :disabled="user.user_type !== 99"
- ></u-button>
- </view>
- <view v-if="user.user_type === 99" class="send-img-box flex flex-center" @click="chooseImg">
- <image src="/static/images/chat/list-msg-img.png" class="send-img"></image>
- </view>
- </view>
- </view>
- </template>
- <style lang="scss">
- .bg {
- background-color: #f7f7f7;
- }
- /* 无更多消息 */
- .msg-end {
- padding: 40rpx 0;
- font-size: 24rpx;
- text-align: center;
- color: #adadad;
- }
- .time {
- margin: 16rpx 0 0;
- font-size: 22rpx;
- font-weight: 500;
- color: #adadad;
- text-align: center;
- }
- /*消息列表*/
- .msg {
- margin: 30rpx 0;
- &.self {
- justify-content: end;
- flex-direction: row-reverse;
- }
- .avatar {
- position: relative;
- margin: 0 20rpx 0 25rpx;
- }
- .nickname {
- font-size: 20rpx;
- font-weight: 500;
- color: #adadad;
- margin-bottom: 16rpx;
- }
- .msg-warp {
- .msg-content {
- max-width: 450rpx;
- min-width: 20rpx;
- min-height: 45rpx;
- padding: 16rpx 24rpx;
- background: #ffffff;
- border-radius: 10px;
- word-break: break-all;
- font-size: 30rpx;
- font-weight: 500;
- color: #232323;
- line-height: 44rpx;
- }
- .msg-img {
- max-width: 250rpx;
- }
- }
- &.self {
- .avatar {
- margin: 0 25rpx 0 20rpx;
- }
- .msg-content {
- background-color: #d4ddfc;
- }
- }
- }
- .bottom {
- position: fixed;
- bottom: 0;
- width: 100%;
- padding: 0 30rpx 0 30rpx;
- box-sizing: border-box;
- height: 149rpx;
- background: #ffffff;
- .input-box {
- background: #f6f6f6;
- border-radius: 41rpx;
- .send-input {
- flex: 1;
- height: 82rpx;
- padding: 0 44rpx;
- font-size: 28rpx;
- font-weight: 500;
- color: #959595;
- }
- .send_btn {
- width: 130rpx;
- height: 80rpx;
- }
- }
- .send-img-box {
- width: 80rpx;
- height: 80rpx;
- .send-img {
- width: 42rpx;
- height: 40rpx;
- }
- }
- }
- </style>
|