2025-09-29

聊天记录上拉获取记录
This commit is contained in:
2025-10-09 15:13:48 +08:00
parent 22c4b5d4d5
commit 039111ed27
14 changed files with 445 additions and 122 deletions

10
.hbuilderx/launch.json Normal file
View File

@@ -0,0 +1,10 @@
{
"version" : "1.0",
"configurations" : [
{
"customPlaygroundType" : "device",
"playground" : "standard",
"type" : "uni-app:app-android"
}
]
}

View File

@@ -8,4 +8,5 @@ export const groupAPI = {
getList: (params) => http.get(baseURL + '/group/list', params),
add: (data) => http.post(baseURL + '/group/', data),
getOne: (params) => http.get(baseURL + '/group/one', params),
getById: (groupId) => http.get(baseURL + '/group/'+ groupId),
}

17
api/message.js Normal file
View File

@@ -0,0 +1,17 @@
import {
http
} from "../util/api"
const baseURL = "http://192.168.0.15:3007"
// 项目相关API
export const messageAPI = {
// 列表
read: (params) => http.post(baseURL + '/message/read', params),
unread: (params) => http.get(baseURL + '/message/unread', params),
list: (params) => http.get(baseURL + "/message/list", params)
}
export default {
messageAPI
}

View File

@@ -45,7 +45,9 @@
]
},
/* ios */
"ios" : {},
"ios" : {
"dSYMs" : false
},
/* SDK */
"sdkConfigs" : {
"payment" : {

View File

@@ -69,7 +69,8 @@
"style" :
{
"navigationBarTitleText" : "聊天页面",
"navigationStyle": "custom"
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
}
],

View File

@@ -1,9 +1,9 @@
<template>
<view class="chat-container">
<u-navbar title="聊天" id="navBarId" :background="{background: 'transparent' }" :border-bottom="false"
<u-navbar :title="chatTitle" 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>
<image class="collection" src="/static/icon/More horizontal.png" mode=""></image>
</template>
</u-navbar>
@@ -37,11 +37,16 @@
<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>
{{handleFormatDate(item.createTime)}}
</view>
</view>
<!-- 录音内容 -->
<view class="mp3-content" v-if="item.type=='mp3'">
<view class="mp3" @click="handlePlayMp3(item.content)">
<view class="mp3-icon">
@@ -51,9 +56,13 @@
点击播放
</view>
</view>
<!-- 图片内容 -->
<view class="img-content" v-if="item.type=='img'">
<u-image @click="handlePreView(item.content)" height="100%" :src="item.content"
:lazy-load="true" mode="heightFix"></u-image>
<view>
{{handleFormatDate(item.createTime)}}
</view>
</view>
<!-- 头像 -->
<view class="msg-avatar">
@@ -65,15 +74,38 @@
</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="msg-content" v-if="item.type=='text'">
<view class="text">
{{item.content}}
</view>
<view>
{{handleFormatDate(item.createTime)}}
</view>
</view>
<!-- 对方录音-->
<view class="mp3-content" v-if="item.type=='mp3'">
<view class="mp3" @click="handlePlayMp3(item.content)">
<view class="mp3-icon">
<image style="width: 100%;height: 100%;" src="/static/icon/listen.png" mode="">
</image>
</view>
点击播放
</view>
</view>
<!-- 图片内容 -->
<view class="img-content" v-if="item.type=='img'">
<u-image @click="handlePreView(item.content)" height="100%" :src="item.content"
:lazy-load="true" mode="heightFix"></u-image>
<view>
{{handleFormatDate(item.createTime)}}
</view>
</view>
</view>
</view>
@@ -119,11 +151,13 @@
<script setup lang="ts">
import { onMounted, ref, getCurrentInstance, onBeforeMount, nextTick } from 'vue';
import {
onLoad, onLaunch
onLoad, onLaunch, onReady, onUnload, onPullDownRefresh
} from "@dcloudio/uni-app";
import io from '@hyoga/uni-socket.io';
import { getImageUrl } from '../../util/common';
import { permissionUtil } from '@/uni_modules/colorful-uni-perm';
import { messageAPI } from '../../api/message';
import { groupAPI } from '../../api/group';
const navBarRef = ref()
const instance = getCurrentInstance();
@@ -141,16 +175,55 @@
}
})
}
const handleFormatDate = (date) => {
const inputDate = new Date(date.replace(/-/g, '/'));
const now = new Date();
// 计算日期差(天)
const diffDays = Math.floor((now - inputDate) / (1000 * 60 * 60 * 24));
// 格式化时间
const hours = inputDate.getHours().toString().padStart(2, '0');
const minutes = inputDate.getMinutes().toString().padStart(2, '0');
const year = inputDate.getFullYear();
const month = (inputDate.getMonth() + 1).toString().padStart(2, '0');
const day = inputDate.getDate().toString().padStart(2, '0');
if (diffDays === 0) {
return `今天 ${hours}:${minutes}`;
} else if (diffDays === 1) {
return '昨天';
} else {
return `${year}-${month}-${day}`;
}
}
const dataList = ref([])
// 模拟聊天数据
const mockData = () => {
for (var i = 0; i < 5; i++) {
if (i % 2 == 0) {
dataList.value.push({
"createId": 3641,
"groupId": "1",
"content": "即使在没有空格的地方也 ",
"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"
}
})
dataList.value.push({
"createId": 3641,
"groupId": "1",
"content": "即使在没有空格的地方也即使在没",
"type": "text",
"messageId": 44,
"createTime": "2025-09-27T18:32:42.000Z",
@@ -179,41 +252,42 @@
}
})
}
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"
}
})
dataList.value.push(
{
"createId": 3641,
"groupId": "5",
"content": "http://114.55.111.44:9000/jurongquan/app_records/1759110192420_Screenshot_20250929_084636_com.bilibili.star.bili.jpg",
"type": "img",
"messageId": 62,
"createTime": "2025-09-28T17:43:13.000Z",
"userInfo": {
"id": 3641,
"username": "15867461617",
"userType": "agent",
"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"
}
})
dataList.value.push(
{
"createId": 3641,
"groupId": "5",
"content": "http://114.55.111.44:9000/jurongquan/app_records/1759110192420_Screenshot_20250929_084636_com.bilibili.star.bili.jpg",
"type": "img",
"messageId": 62,
"createTime": "2025-09-28T17:43:13.000Z",
"userInfo": {
"id": 3641,
"username": "15867461617",
"userType": "agent",
"isSystemAccount": 0,
"avatar": "/uploads/documents/1753833656669_524106424.jpg"
}
})
}
// 发送消息
@@ -433,38 +507,47 @@
});
}
// 初始化
const groupId = ref()
const userId = ref()
const chatTitle = ref()
onLoad((val) => {
groupId.value = val.groupId
});
const scrollTop = ref(0)
const socket = ref()
// 通信连接
const connection = () => {
socket.value = io('http://192.168.0.15:3007', {
const socket = ref()
const connection = async () => {
console.log("连接服务器");
socket.value = io(`http://192.168.0.15:3007?userId=${userId.value}`, {
query: {},
transports: ['websocket', 'polling'],
timeout: 5000,
});
socket.value.on('connect', async () => {
socket.value.on('connect', () => {
console.log("连接成功");
})
socket.value.emit('addGroup', {
groupId: groupId.value,
})
socket.value.on('serverMsg', async (message:any) => {
dataList.value.push(message)
// console.log(dataList.value);
setTimeout(()=>{
scrollToBottom()
}, 500)
// socket.value.emit('cleanup')
socket.value.emit('addGroup', {
groupId: groupId.value
})
socket.value.on('serverMsg', async (message : any) => {
dataList.value.push(message)
// console.log(dataList.value);
setTimeout(() => {
scrollToBottom()
}, 500)
})
})
}
const scrollTop = ref(0)
const disConnection = () => {
console.log("disConnection");
socket.value.emit('leaveGroup', {
groupId: groupId.value
})
}
const scrollToBottom = () => {
uni.createSelectorQuery().in(this).select("#scroll-main").boundingClientRect((res : any) => {
@@ -475,17 +558,69 @@
}).exec()
}
const loadData = () => {
const loadUnReadData = () => {
// 获取用户ID
userId.value = uni.getStorageSync("user").id
// 获取标题
groupAPI.getById(groupId.value).then(res => {
chatTitle.value = res.data.program.linkman.username + "(" + res.data.program.name + ")"
})
// 未读变已读
let data = { groupId: groupId.value, userId: userId.value }
messageAPI.unread(data).then(res => {
if (res.code == 200) {
dataList.value = dataList.value.concat(res.data)
}
})
messageAPI.read({ groupId: groupId.value, userId: userId.value })
}
onMounted(() => {
// mockData()
loadData()
const params = ref({
page: 1,
size: 10,
groupId: null
})
const loadFix = ref(true)
// 历史记录
const loadHistoryData = () => {
params.value.groupId = groupId.value
messageAPI.list(params.value).then(res => {
console.log(res);
if (res.data.list.length != 0) {
res.data.list.forEach(item => {
dataList.value.unshift(item)
})
params.value.page++
}
})
}
onPullDownRefresh(() => {
loadHistoryData()
uni.stopPullDownRefresh()
})
onLoad((val) => {
groupId.value = val.groupId
});
onReady(() => {
})
onMounted(() => {
loadHeight()
// mockData()
loadUnReadData()
connection()
})
onUnload(() => {
disConnection()
})
</script>
<style scoped lang="scss">
@@ -553,6 +688,14 @@
.text {
overflow-wrap: break-word;
text-align: right;
background-color: #4873FF;
display: block;
padding: 10rpx;
color: #fff;
box-shadow: 0rpx 4rpx 8rpx 0rpx #00000040;
}
}
@@ -568,11 +711,11 @@
// border: 1rpx solid #000;
padding: 15rpx 10rpx;
background-color: #55ff7f;
justify-content: flex-end;
align-items: center;
justify-content: flex-start;
// align-items: center;
.mp3-icon {
margin: 0 10rpx;
// margin: 0 10rpx;
width: 25rpx;
height: 25rpx;
}
@@ -582,6 +725,7 @@
.img-content {
margin-right: 10rpx;
height: 200rpx;
margin-bottom: 30rpx;
}
.msg-avatar {
@@ -599,24 +743,58 @@
justify-content: flex-start;
padding: 0 10rpx;
.msg-avatar {
width: 80rpx;
height: 80rpx;
}
.msg-content {
// background-color: #00ffff;
padding: 2rpx;
width: 70%;
margin-left: 10rpx;
// border: 1rpx solid #c7c7c7;
box-sizing: content-box;
.text {
overflow-wrap: break-word;
text-align: left;
background-color: #F0F3FF;
display: block;
padding: 10rpx;
box-shadow: 0rpx 4rpx 8rpx 0rpx #00000040;
}
}
.msg-avatar {
width: 80rpx;
height: 80rpx;
margin-right: 10rpx;
.mp3-content {
padding: 2rpx;
// width: 70%;
margin-left: 10rpx;
box-sizing: content-box;
.mp3 {
display: flex;
// border: 1rpx solid #000;
padding: 15rpx 10rpx;
background-color: #55ff7f;
justify-content: flex-start;
align-items: center;
.mp3-icon {
margin: 0 10rpx;
width: 25rpx;
height: 25rpx;
}
}
}
.img-content {
margin-left: 10rpx;
height: 200rpx;
}
}
}
}

View File

@@ -3,22 +3,52 @@
<!-- 可滚动的内容区域 -->
<view class="content-container">
<view style="padding: 32rpx;">
<view id="fixedHeadId">
<view class="message-title">
容讯聊天室
<view>
<view id="fixedHeadId" style="padding: 32rpx 32rpx 0;">
<view class="head-box1">
<view class="title">
容讯聊天室
</view>
<view class="icon">
<image class="icon-img" src="/static/icon/search.png" mode=""></image>
<image class="icon-img2" src="/static/icon/Bookmark.png" mode=""></image>
</view>
</view>
<u-row class="message-search">
<u-col span="9">
<u-search placeholder="点击查询项目" v-model="keyword" :show-action="false"></u-search>
</u-col>
<u-col class="collection" span="3">
<image class="collection-icon" src="/static/icon/Bookmark.png" mode=""></image>
<view class="collection-text">
我的收藏
<view class="head-box2">
<view class="item">
<view class="box-img">
<image class="img" src="/static/message/system.png" mode=""></image>
</view>
</u-col>
</u-row>
<view class="title">
系统消息
</view>
</view>
<view class="item">
<view class="box-img">
<image class="img" src="/static/message/customer.png" mode=""></image>
</view>
<view class="title">
客户消息
</view>
</view>
<view class="item">
<view class="box-img">
<image class="img" src="/static/message/order.png" mode=""></image>
</view>
<view class="title">
订单通知
</view>
</view>
<view class="item">
<view class="box-img">
<image class="img" src="/static/message/process.png" mode=""></image>
</view>
<view class="title">
项目进度
</view>
</view>
</view>
<view class="menu-list">
<view class="menu-item" v-for="(item,index) in menuList" @click="handleMenuChange(index)">
<view class="menu-title">{{item.name + '(' + item.count +')'}}
@@ -29,9 +59,35 @@
</view>
</view>
</view>
<scroll-view scroll-y="true" class="message-list" :style="'height:'+height+'px'">
<view class="message-item" v-for="item in messageList" @click="handleChat(item)">
{{item.program.name}}
<view class="message-item" v-for="item in messageList">
<u-swipe-action bgColor="#F8FBFF" :index="item.groupId" class="other-program" @content-click="handleChat(item)" v-if="item.program.linkmanId !== params.userId"
:options="options">
<view class="item-name title-wrap">
{{ item.program.name}}
</view>
<view class="item-company">
{{'负责公司:' + item.program.company }}
</view>
<!-- <view class="item-date">
{{ item.program.startDate + "至" + item.program.endDate }}
</view> -->
<view class="item-unread" v-if="item.userUnread.length!=0">
未读 {{item.userUnread.length}}
</view>
</u-swipe-action>
<u-swipe-action bgColor="#F8FBFF" class="my-program" @content-click="handleChat(item)" v-else :options="options">
<view class="item-name title-wrap">
{{ item.program.name}}
</view>
<view class="item-company">
{{'咨询人:' + maskPhoneNumber(item.user.username) }}
</view>
<view class="item-unread" v-if="item.chargeUnread.length!=0">
未读 {{item.chargeUnread.length}}
</view>
</u-swipe-action>
</view>
</scroll-view>
</view>
@@ -48,7 +104,7 @@
} from '@dcloudio/uni-app';
import { onReady as onUniReady } from '@dcloudio/uni-app';
import { groupAPI } from '../../api/group';
import { getUserInfo } from '../../util/common';
import { getUserInfo, maskPhoneNumber } from '../../util/common';
const instance = getCurrentInstance();
const height = ref(0)
@@ -91,7 +147,7 @@
currentMenu.value = val
}
// 项目
// 消息
const messageList = ref([])
const user = ref()
const size = 10
@@ -101,6 +157,20 @@
userId: '',
customerId: '',
})
const options = [
{
text: '收藏',
style: {
backgroundColor: '#007aff'
}
},
{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}
]
const handleChat = (item) => {
uni.navigateTo({
@@ -111,6 +181,7 @@
const loadData = () => {
groupAPI.getList(params.value).then((res) => {
messageList.value = res.data.list
console.log(messageList.value);
})
}
@@ -138,50 +209,76 @@
/* 内容滚动区域 */
.content-container {
background: linear-gradient(180deg, #2F75F9 0%, #F0F3FF 34.13%);
// background: linear-gradient(180deg, #2F75F9 0%, #F0F3FF 34.13%);
background: linear-gradient(270deg, #659CFF 0%, #2D75FB 50%, #659CFF 100%);
width: 100%;
height: 100%;
.message-title {
margin-top: 40rpx;
text-align: center;
font-family: SF Pro;
font-weight: 700;
font-style: Bold;
font-size: 40rpx;
leading-trim: NONE;
line-height: 48rpx;
letter-spacing: 0%;
color: #FFFFFF;
.head-box1 {
padding-top: 60rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
font-family: SF Pro;
font-weight: 700;
font-style: Bold;
font-size: 40rpx;
leading-trim: NONE;
line-height: 48rpx;
letter-spacing: 0%;
color: #FFFFFF;
}
.message-search {
margin-top: 20rpx;
.collection {
width: 100%;
.icon {
display: flex;
justify-content: flex-end;
align-items: center;
.collection-icon {
.icon-img {
width: 34rpx;
height: 34rpx;
margin-right: 20rpx;
}
.icon-img2 {
width: 48rpx;
height: 48rpx;
}
}
}
.head-box2 {
display: flex;
justify-content: space-around;
margin-top: 20rpx;
.collection-text {
font-family: SF Pro;
font-weight: 30;
font-style: Expanded Ultralight;
font-size: 24rpx;
.item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.box-img {
.img {
width: 68rpx;
height: 68rpx;
}
}
.title {
margin-top: 10rpx;
font-family: Work Sans;
font-weight: 400;
font-style: Regular;
font-size: 26rpx;
leading-trim: NONE;
line-height: 26px;
letter-spacing: 0%;
color: #FFFFFF;
line-height: 100%;
letter-spacing: -2%;
text-align: center;
color: #fff;
}
}
}
.menu-list {
@@ -207,7 +304,6 @@
.menu-icon {
width: 22rpx;
height: 22rpx;
}
}
}
@@ -216,11 +312,20 @@
margin-top: 10rpx;
.message-item {
// border: 1px solid #000;
padding: 20rpx 10px;
background-color: #FFFFFF;
border-radius: 10rpx;
margin-bottom: 10rpx;
.other-message {
padding: 20rpx 0rpx 20rpx 20rpx;
background-color: #F8FBFF;
;
}
.my-program {
padding: 20rpx 0rpx 20rpx 20rpx;
background-color: #F8FBFF;
border-bottom: 2rpx solid #f1f4ff;
}
}
}
}

View File

@@ -164,6 +164,7 @@
</view>
</u-modal>
<u-toast ref="pdMsgRef" />
</view>
</template>
@@ -173,6 +174,8 @@
import { programAPI } from '../../api/program';
import { onLoad } from '@dcloudio/uni-app'
import { groupAPI } from '../../api/group';
const pdMsgRef = ref()
const userId = ref()
const programId = ref()
@@ -229,11 +232,17 @@
} else {
// 开启群组聊天
groupAPI.add({ programId: programId.value, userId: userId.value }).then(res => {
console.log(res);
if(res.code==200){
// 创建成功,进入聊天
uni.redirectTo({
url: '/pages/message/chat?groupId=' + res.data.groupId
})
}else{
pdMsgRef.value.show({
title: res.msg,
type: 'error'
})
}
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

BIN
static/icon/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

BIN
static/message/customer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
static/message/order.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
static/message/process.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
static/message/system.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB