2025-09-28 聊天修改

This commit is contained in:
2025-09-28 17:30:20 +08:00
parent 5110537d73
commit 08c54797b6
15 changed files with 1380 additions and 188 deletions

535
pages/message/chat.vue Normal file
View File

@@ -0,0 +1,535 @@
<template>
<view class="chat-container">
<u-navbar title="聊天" id="navBarId" :background="{background: 'transparent' }" :border-bottom="false"
back-icon-color="#000" title-color="#000">
<template v-slot:right>
<image class="collection" src="/static/icon/Bookmark.png" mode=""></image>
</template>
</u-navbar>
<view class="bottom-view" id="bottomId">
<!-- 麦克风/文本切换 -->
<view class="icon u-m-r-10" @click="handleChangeMic">
<image src="/static/icon/Mic.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
<textarea v-if="!showMic" name="" v-model="text" class="text" maxlength="3000"></textarea>
<u-button v-else class="btn-mic" @click="handleSay">点击说话</u-button>
<!-- 表情包 -->
<view v-show="text==null || text==''" class="icon u-m-r-10 u-m-l-10">
<image src="/static/icon/expression.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
<!-- 文本域展开 -->
<view v-show="text!=null && text!=''" class="icon u-m-r-10 u-m-l-10" @click="handleMagnify">
<image src="/static/icon/big.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
<!-- 相机 -->
<view v-show="text==null || text==''" class="icon" @click="handleCamera">
<image src="/static/icon/Camera.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
<!-- 文本发送 -->
<view v-show="text!=null && text!=''" class="icon u-m-r-10 u-m-l-10" @click="handleSend">
<image src="/static/icon/send.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
</view>
<scroll-view :style="'height:'+scrollHeight+'px'" scroll-y="true" class="scroll-main">
<view v-for="(item, index) in dataList">
<view v-if="item.createId==userId" class="my-message">
<view class="msg-main">
<view class="msg-content" v-if="item.type=='text'">
<view class="text">
{{item.content}}
</view>
</view>
<view class="mp3-content" v-if="item.type=='mp3'">
<view class="mp3">
<view class="mp3-icon">
<image style="width: 100%;height: 100%;" src="/static/icon/listen.png" mode="">
</image>
</view>
点击播放
</view>
</view>
<view class="msg-avatar">
<image style="width: 100%;height: 100%;" :src="getImageUrl(item.userInfo.avatar)" mode="">
</image>
</view>
</view>
</view>
<view v-else class="other-message">
<view class="msg-main">
<view class="msg-avatar">
<image style="width: 100%;height: 100%;" :src="getImageUrl(item.userInfo.avatar)" mode="">
</image>
</view>
<view class="msg-content">
<view class="text">
{{item.content}}
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<u-popup v-model="showLongText" mode="bottom" height="80%">
<view class="long-text">
<u-input v-model="text" type="textarea" maxlength="3000" />
</view>
</u-popup>
<u-mask :show="showMask" @click="handleStopMic" blur="10">
<view class="mask-warp">
<view class="mask-text">
{{!showMaskBtn?'正在说话...点击结束': '点击播放录音'}}
</view>
<view v-if="showMaskBtn" class="mask-btn">
<u-button class="btn" @click.stop="handleCancel">取消</u-button>
<u-button class="btn" @click.stop="handleSendMp3">发送</u-button>
</view>
</view>
</u-mask>
<u-toast ref="msgToast" duration="6000" />
</view>
</template>
<script setup lang="ts">
import { onMounted, ref, getCurrentInstance, onBeforeMount } from 'vue';
import {
onLoad, onLaunch
} from "@dcloudio/uni-app";
import io from '@hyoga/uni-socket.io';
import { getImageUrl } from '../../util/common';
import { permissionUtil } from '@/uni_modules/colorful-uni-perm';
const text = ref('')
const socket = ref()
const navBarRef = ref()
const dataList = ref([])
const mockData = () => {
for (var i = 0; i < 3; i++) {
if (i % 2 == 0) {
dataList.value.push({
"createId": 3641,
"groupId": "1",
"content": "即使在没有空格的地方也 ",
"type": "text",
"messageId": 44,
"createTime": "2025-09-27T18:32:42.000Z",
"userInfo": {
"id": 9958,
"username": "15867461647",
"userType": "user",
"isSystemAccount": 0,
"avatar": "/uploads/documents/1753833656669_524106424.jpg"
}
})
} else {
dataList.value.push({
"createId": 9955,
"groupId": "1",
"content": "2222",
"type": "text",
"messageId": 44,
"createTime": "2025-09-27T18:32:42.000Z",
"userInfo": {
"id": 9958,
"username": "15867461647",
"userType": "user",
"isSystemAccount": 0,
"avatar": "/uploads/documents/1753833656669_524106424.jpg"
}
})
}
dataList.value.push(
{
"createId": 3641,
"groupId": "5",
"content": "http://114.55.111.44:9000/jurongquan/app_records/1759049412254.mp3",
"type": "mp3",
"messageId": 56,
"createTime": "2025-09-28T00:50:16.000Z",
"userInfo": {
"id": 3641,
"username": "15867461617",
"userType": "agent",
"isSystemAccount": 0,
"avatar": "/uploads/documents/1753833656669_524106424.jpg"
}
})
}
}
const handleSend = () => {
socket.value.emit('clientMsg', {
createId: userId.value,
groupId: groupId.value,
content: text.value,
type: "text"
})
}
const showLongText = ref(false)
const showMic = ref(false)
const showMask = ref(false)
const showMaskBtn = ref(false)
const msgToast = ref(null)
const voicePath = ref('')
const recorderManager = uni.getRecorderManager();
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;
// 选择麦克风
const handleChangeMic = () => {
showMic.value = !showMic.value
}
// 开始录音
const handleSay = () => {
permissionUtil.requestAndroidPermission('android.permission.READ_MEDIA_AUDIO').then((status) => {
// status 为 1 表示用户已授权0 表示用户已拒绝, -1 表示用户永久拒绝
console.log('权限申请结果:', status);
if (status == -1) {
// 弹窗提示开启权限
msgToast.value.show({
title: '您已关闭录音权限,请在设置开启应用录音权限',
type: 'warning'
})
} else if (status == 1) {
showMask.value = true
recorderManager.start();
recorderManager.onStart(() => {
console.log('录音开始');
});
recorderManager.onError((err) => {
console.error('录音错误', err);
});
}
});
}
// 停止录音
const handleStopMic = () => {
showMaskBtn.value = true
if (voicePath.value) {
console.log("播放录音");
innerAudioContext.src = voicePath.value
innerAudioContext.play();
} else {
recorderManager.stop()
// showMask.value = false
recorderManager.onStop(function (res) {
console.log('recorder stop' + JSON.stringify(res));
voicePath.value = res.tempFilePath;
});
}
}
// 取消发送录音
const handleCancel = () => {
showMask.value = false
showMaskBtn.value = false
voicePath.value = ''
}
// 发送录音文件
const handleSendMp3 = () => {
const token = uni.getStorageSync("token")
uni.uploadFile({
// url: 'http://192.168.0.4:3005/upload',
url: 'http://192.168.0.15:3007/upload/file',
filePath: voicePath.value,
header: {
"Authorization": "Bearer " + token
},
name: "file",
success: (uploadFileRes) => {
let result = JSON.parse(uploadFileRes.data)
if (result.data.fileUrl) {
// 上传文件成功,添加消息记录
socket.value.emit('clientMsg', {
createId: userId.value,
groupId: groupId.value,
content: result.data.fileUrl,
type: "mp3"
})
showMask.value = false
showMaskBtn.value = false
voicePath.value = ''
}
},
fail: (res) => {
console.log(JSON.stringify(res));
}
})
}
// 文本放大
const handleMagnify = () => {
showLongText.value = true
}
const handleCamera = () => {
permissionUtil.requestAndroidPermission('android.permission.READ_EXTERNAL_STORAGE').then((status) => {
console.log('相册权限申请结果:', status);
if (status == -1) {
// 弹窗提示开启权限
msgToast.value.show({
title: '您已关闭读取相册权限,请在设置开启应用相册权限',
type: 'warning'
})
} else if (status == 1) {
uni.chooseImage({
count: 10, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: function (res) {
uploadImages(res.tempFilePaths)
}
});
}
})
}
const uploadImages = (value) => {
console.log(value);
const token = uni.getStorageSync("token")
uni.uploadFile({
// url: 'http://192.168.0.4:3005/upload/image',
url: 'http://192.168.0.15:3007/upload/files',
filePath: value,
header: {
"Authorization": "Bearer " + token
},
name: 'file',
success: (uploadFileRes) => {
console.log(uploadFileRes);
// console.log(JSON.stringify(uploadFileRes.data));
},
fail: (res) => {
console.log(JSON.stringify(res));
}
})
}
const groupId = ref()
const userId = ref()
onLoad((val) => {
groupId.value = val.groupId
});
const instance = getCurrentInstance();
const scrollHeight = ref(0)
const loadHeight = () => {
uni.getSystemInfo({
success(res) {
let screenHeight = res.screenHeight
uni.createSelectorQuery().in(instance.proxy).select("#navBarId").boundingClientRect((data : any) => {
scrollHeight.value = screenHeight - data.height
}).exec()
uni.createSelectorQuery().in(instance.proxy).select("#bottomId").boundingClientRect((data : any) => {
scrollHeight.value = scrollHeight.value - data.height
}).exec()
}
})
}
const connection = () => {
socket.value = io('http://192.168.0.15:3007', {
query: {},
transports: ['websocket', 'polling'],
timeout: 5000,
});
socket.value.on('connect', async () => {
console.log("连接成功");
})
socket.value.emit('addGroup', {
groupId: groupId.value,
})
socket.value.on('serverMsg', async (message) => {
dataList.value.push(message)
console.log(dataList.value);
})
}
const loadData = () => {
userId.value = uni.getStorageSync("user").id
}
onMounted(() => {
mockData()
loadData()
loadHeight()
connection()
})
</script>
<style scoped lang="scss">
.chat-container {
width: 100%;
height: 100vh;
background: linear-gradient(180deg, #E3E8FF 0%, #FFFFFF 100%);
background-blend-mode: lighten;
.collection {
width: 48rpx;
height: 48rpx;
margin-right: 24rpx;
}
.bottom-view {
width: 100%;
height: 100rpx;
// background-color: #aaffff;
position: absolute;
bottom: 0;
display: flex;
align-items: center;
padding: 10rpx;
.icon {
width: 60rpx;
height: 60rpx;
}
.text {
flex: 1;
height: 96%;
border: 2rpx solid #DEEFFF;
border-radius: 10rpx;
padding: 0 10rpx;
}
.btn-mic {
width: 100%;
height: 100%;
}
}
.scroll-main {
.my-message {
text-align: right;
margin-bottom: 20rpx;
.msg-main {
display: flex;
justify-content: flex-end;
padding: 0 10rpx;
.msg-content {
// background-color: #00ffff;
padding: 2rpx;
width: 70%;
margin-right: 10rpx;
// border: 1rpx solid #c7c7c7;
box-sizing: content-box;
.text {
overflow-wrap: break-word;
text-align: right;
}
}
.mp3-content {
padding: 2rpx;
// width: 70%;
margin-right: 10rpx;
box-sizing: content-box;
.mp3 {
display: flex;
// border: 1rpx solid #000;
padding: 15rpx 10rpx;
background-color: #55ff7f;
justify-content: flex-end;
align-items: center;
.mp3-icon {
margin-left: 10rpx;
width: 25rpx;
height: 25rpx;
}
}
}
.msg-avatar {
width: 80rpx;
height: 80rpx;
}
}
}
.other-message {
margin-bottom: 20rpx;
.msg-main {
display: flex;
justify-content: flex-start;
padding: 0 10rpx;
.msg-content {
// background-color: #00ffff;
padding: 2rpx;
width: 70%;
// border: 1rpx solid #c7c7c7;
box-sizing: content-box;
.text {
overflow-wrap: break-word;
text-align: left;
}
}
.msg-avatar {
width: 80rpx;
height: 80rpx;
margin-right: 10rpx;
}
}
}
}
.long-text {
padding: 40rpx 20rpx;
}
.mask-warp {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
.mask-text {
font-size: 36rpx;
color: #fff;
}
.mask-btn {
position: absolute;
bottom: 0;
width: 100%;
height: 80rpx;
display: flex;
.btn {
width: 50%;
}
}
}
}
</style>

View File

@@ -48,6 +48,7 @@
} from '@dcloudio/uni-app';
import { onReady as onUniReady } from '@dcloudio/uni-app';
import { groupAPI } from '../../api/group';
import { getUserInfo } from '../../util/common';
const instance = getCurrentInstance();
const height = ref(0)
@@ -92,23 +93,23 @@
// 项目
const messageList = ref([])
const mockData = () => {
for (var i = 0; i < 20; i++) {
messageList.value.push({
messageName: "test" + i
})
}
}
const user = ref()
const size = 10
const params = ref({
page: 1,
size: size,
userId: '',
customerId: '',
})
const handleChat = (item) => {
uni.navigateTo({
url: '/pages/program/chat?groupId=' + item.groupId
url: '/pages/message/chat?groupId=' + item.groupId
})
}
const loadData = () => {
groupAPI.getList().then((res) => {
groupAPI.getList(params.value).then((res) => {
messageList.value = res.data.list
})
}
@@ -120,7 +121,8 @@
onUniReady(() => {
loadHeight()
user.value = getUserInfo()
params.value.userId = user.value.id
loadData()
})
</script>