123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745 |
- <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>
|