chenjj il y a 13 heures
Parent
commit
00285a88ed

+ 28 - 16
App.vue

@@ -1,29 +1,41 @@
 <script>
+import WebSocketManager from '@/utils/WebSocketManager.js';
+import { watch } from 'vue';
+import store from '@/store'
 export default {
+	data:{
+		userId: uni.getStorageSync('userId') //读取本地存储
+	},
 	onLaunch: function () {
 		console.log('App Launch')
-		/*
-		this.$socket.connect({ url: "ws://127.0.0.1:8080/sc" }).then(res => {
-			console.log("success");
-			this.$socket.onMessage(res => {
-				console.log("onmessage",res);
-			})
-			this.$socket.on("on").then(res=>{
-				console.log("on",res);
-			})
-			this.$socket.send({msg:"xxx"},true).then(res=>{
-				console.log("callback-uuid",res);
-			})
-			this.$socket.send({event:"on"})
-		})
-		*/
+		
 	},
 	onShow: function () {
 		console.log('App Show')
 	},
 	onHide: function () {
 		console.log('App Hide')
-	}
+	},
+	watch: {
+        userId: {
+            handler() {
+                console.log('yong---------------',this.userId);
+				const wsManager = new WebSocketManager(this.userId);
+				// 设置消息回调
+				wsManager.onMessage(data => {
+					console.log('app-接收到的消息:', data);
+					// 处理消息逻辑
+					if(data.type === 'msgUnreadCount'){
+						store.dispatch('handleMessageCount',data.data)
+					}
+				});
+				// 建立连接
+				wsManager.connect();
+            },
+            immediate: true,
+            deep: true
+        }
+    },
 }
 </script>
 

+ 53 - 0
api/conversation.js

@@ -0,0 +1,53 @@
+import request from '../utils/request'
+
+
+
+//查询会话记录列表
+export function getList(data) {
+    return request({
+        url: `/core/conversation/list`,
+        method: 'post',
+        data
+    })
+}
+
+
+//发送
+export function sendMsg(data) {
+    return request({
+        url: `/core/conversation/sendMsg`,
+        method: 'post',
+        data
+    })
+}
+
+
+//订单页面 点击沟通 获取会话记录详细信息
+export function getOrderConversationInfo(data) {
+    return request({
+        url: `/core/conversation/getOrderConversationInfo`,
+        method: 'post',
+        data
+    })
+}
+
+//消息列表页面 点击会话 获取会话记录详细信息
+export function getListConversationInfo(params) {
+    return request({
+        url: `/core/conversation/getListConversationInfo`,
+        method: 'get',
+        params
+    })
+}
+
+
+
+
+//打开会话框时,设置已读
+export function setRead(data) {
+    return request({
+        url: `/core/conversation/setRead`,
+        method: 'post',
+        data
+    })
+}

+ 19 - 11
components/Client/new_file.vue

@@ -239,20 +239,28 @@
 			}
 			
 			if (service.key !== 13) {
-				const res = await getVolunteerInfo({
-					serviceCategory: service.key
-				});
-				if (res.code === 200 && res.data) {
-					//已有注册,跳转详情页面
+				if ([1,2,3,4,5].includes(service.key)) {
+					const res = await getVolunteerInfo({
+						serviceCategory: service.key
+					});
+					if (res.code === 200 && res.data) {
+						//已有注册,跳转详情页面
+						uni.navigateTo({
+							url: `/pages_home/pages/details/index?data=${encodeURIComponent(JSON.stringify(service))}`
+						})
+						return
+					}
+
 					uni.navigateTo({
-						url: `/pages_home/pages/details/index?data=${encodeURIComponent(JSON.stringify(service))}`
+						url: `/pages_home/pages/register/index?data=${encodeURIComponent(JSON.stringify(service))}`
 					})
-					return
+				} else {
+					// 其他条目提示“敬请期待”
+					uni.showToast({
+						title: '敬请期待',
+						icon: 'none'
+					});
 				}
-
-				uni.navigateTo({
-					url: `/pages_home/pages/register/index?data=${encodeURIComponent(JSON.stringify(service))}`
-				})
 				return;
 			}
 			init();

+ 74 - 48
components/CustomTabBar/index.vue

@@ -1,9 +1,11 @@
 <template>
     <view class="tab_bar">
         <view class="tabbarBox">
-            <view class="handleBox" v-for="(item) in tabBarList" :key="item.key +'tab_bar'">
+            <view class="handleBox" v-for="(item) in tabBarList" :key="item.key + 'tab_bar'">
                 <view class="menuBox" @click="goPages(item)">
                     <view class="menuIcon">
+                        <view class="message-style" v-if="item.key === 'chat' && messageCount > 0">{{ messageCount }}
+                        </view>
                         <image v-if="item.key != selectIndex" class="img" :src="item.iconPath"></image>
                         <image v-else class="img" :src="item.selectedIconPath"></image>
                     </view>
@@ -18,6 +20,8 @@
 </template>
 
 <script>
+import { onMounted } from 'vue';
+import store from '@/store'
 export default {
     props: {
         page: {
@@ -28,8 +32,8 @@ export default {
     watch: {
         page: {
             handler(value) {
-                console.log('value',value);
-                
+                console.log('value', value);
+
                 this.selectIndex = value;
             },
             immediate: true,
@@ -39,43 +43,44 @@ export default {
     data() {
         return {
             selectIndex: null,
-            userType: uni.getStorageSync('userType') //读取本地存储
+            userType: uni.getStorageSync('userType'), //读取本地存储
+            messageCount: store.state.user.messageCount
         }
     },
     computed: {
         //获取当前页面
         tabBarList() {
-            if(this.userType === 1){
+            if (this.userType === 1) {
                 return [
-                {
-                    "pagePath": "/pages/index",
-                    "iconPath": "/static/images/tabbar/home.png",
-                    "selectedIconPath": "/static/images/tabbar/home_.png",
-                    "text": "首页",
-                    key:'home'
-                },
-                {
-                    "pagePath": "/pages/mallMenu",
-                    "iconPath": "/static/images/tabbar/class.png",
-                    "selectedIconPath": "/static/images/tabbar/class_.png",
-                    "text": "分类",
-                    key:'class'
-                },
-                {
-                    "pagePath": "/pages/classify",
-                    "iconPath": "/static/images/tabbar/work.png",
-                    "selectedIconPath": "/static/images/tabbar/work_.png",
-                    "text": "订单",
-                    key:'order'
-                },
-                {
-                    "pagePath": "/pages/mine",
-                    "iconPath": "/static/images/tabbar/mine.png",
-                    "selectedIconPath": "/static/images/tabbar/mine_.png",
-                    "text": "我的",
-                    key:'mine'
-                }
-            ]
+                    {
+                        "pagePath": "/pages/index",
+                        "iconPath": "/static/images/tabbar/home.png",
+                        "selectedIconPath": "/static/images/tabbar/home_.png",
+                        "text": "首页",
+                        key: 'home'
+                    },
+                    {
+                        "pagePath": "/pages/mallMenu",
+                        "iconPath": "/static/images/tabbar/class.png",
+                        "selectedIconPath": "/static/images/tabbar/class_.png",
+                        "text": "分类",
+                        key: 'class'
+                    },
+                    {
+                        "pagePath": "/pages/classify",
+                        "iconPath": "/static/images/tabbar/work.png",
+                        "selectedIconPath": "/static/images/tabbar/work_.png",
+                        "text": "订单",
+                        key: 'order'
+                    },
+                    {
+                        "pagePath": "/pages/mine",
+                        "iconPath": "/static/images/tabbar/mine.png",
+                        "selectedIconPath": "/static/images/tabbar/mine_.png",
+                        "text": "我的",
+                        key: 'mine'
+                    }
+                ]
             }
             return [
                 {
@@ -83,14 +88,14 @@ export default {
                     "iconPath": "/static/images/tabbar/home.png",
                     "selectedIconPath": "/static/images/tabbar/home_.png",
                     "text": "首页",
-                     key:'home'
+                    key: 'home'
                 },
                 {
                     "pagePath": "/pages/mallMenu",
                     "iconPath": "/static/images/tabbar/class.png",
                     "selectedIconPath": "/static/images/tabbar/class_.png",
                     "text": "分类",
-                     key:'class'
+                    key: 'class'
                 },
                 // {
                 //     "pagePath": "/pages/release",
@@ -99,26 +104,26 @@ export default {
                 //     "text": "发布",
                 //     key:'release'
                 // },
-                // {
-                //     "pagePath": "/pages/chat",
-                //     "iconPath": "/static/images/tabbar/class.png",
-                //     "selectedIconPath": "/static/images/tabbar/class_.png",
-                //     "text": "消息",
-                //     key:'chat'
-                // },
+                {
+                    "pagePath": "/pages/chat",
+                    "iconPath": "/static/images/tabbar/class.png",
+                    "selectedIconPath": "/static/images/tabbar/class_.png",
+                    "text": "消息",
+                    key: 'chat'
+                },
                 {
                     "pagePath": "/pages/classify",
                     "iconPath": "/static/images/tabbar/work.png",
                     "selectedIconPath": "/static/images/tabbar/work_.png",
                     "text": "订单",
-                     key:'order'
+                    key: 'order'
                 },
                 {
                     "pagePath": "/pages/mine",
                     "iconPath": "/static/images/tabbar/mine.png",
                     "selectedIconPath": "/static/images/tabbar/mine_.png",
                     "text": "我的",
-                    key:'mine'
+                    key: 'mine'
                 }
             ]
         }
@@ -127,12 +132,13 @@ export default {
     created() {
 
 
-
-    }, 
+    },
+    onMounted() {
+    },
     methods: {
         //进入tabble页
         goPages(page) {
-            console.log('page,index',page);
+            console.log('page,index', page);
             uni.switchTab({
                 url: page.pagePath
             })
@@ -187,4 +193,24 @@ export default {
     @extend .Text;
     color: #d81e06;
 }
+
+.menuIcon {
+    position: relative;
+}
+
+.message-style {
+    background: rgba(239, 68, 68, 1);
+    color: #fff;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    width: 20px;
+    height: 20px;
+    border-radius: 20px;
+
+    position: absolute;
+    right: 0;
+    z-index: 3;
+}
 </style>

+ 2 - 2
config.js

@@ -2,8 +2,8 @@
 const config = {
 	// baseUrl: 'https://vue.ruoyi.vip/prod-api',
 	//cloud后台网关地址
-	baseUrl: 'http://192.168.100.95:9527',//嵘哥
-	// baseUrl: 'http://192.168.100.101:9527',//龙哥
+	// baseUrl: 'http://192.168.100.95:9527',//嵘哥
+	baseUrl: 'http://192.168.100.127:9527',//龙哥
 	// baseUrl: 'https://zybooks.tech/prod-api',
 	mapKey:'KFEBZ-P2GKZ-A5PX4-7Q6Y7-KXOBF-XCB4C',
 	appName: '金邻助家',

+ 20 - 12
pages/chat.vue

@@ -5,19 +5,20 @@
             <view class="chat-main">
                 <view v-if="list && list.length > 0">
                     <view class="chat-item" v-for="item in list" :key="item.code" @click="onClick(item)">
-                        <img src="/static/serverImg/mine/user.png" alt="" class="chat-img" />
+                        <img v-if="item.conversationAvatar" :src="item.conversationAvatar" alt="" class="chat-img" />
+                        <img src="/static/serverImg/mine/user.png" alt="" class="chat-img" v-else/>
                         <view class="chat-box">
                             <view class="chat-top">
-                                <text class="chat-name">toutou旗舰店</text>
-                                <text class="chat-time">昨天</text>
+                                <text class="chat-name">{{ type?item.userName:item.volunteerName }}</text>
+                                <text class="chat-time">{{ item.createTime }}</text>
                             </view>
                             <view class="chat-bottom">
                                 <text class="chat-text">
-                                    尊敬的会员,您行行行行行行行行行行想尊敬的会员,您行行行行行行行行行行想尊敬的会员,您行行行行行行行行行行想尊敬的会员,您行行行行行行行行行行想
+                                   {{ item.msgContent }}
                                 </text>
-                                <view class="chat-num">
+                                <!-- <view class="chat-num">
                                     2
-                                </view>
+                                </view> -->
                             </view>
                         </view>
                     </view>
@@ -44,10 +45,17 @@ import { getAccountChangeList, getVolunteerChangeList } from "@/api/mine";
 import { onShow } from '@dcloudio/uni-app';
 import { useDict } from '@/utils/dict.js';
 import NoneView from '@/components/NoneView/index.vue'
+
+import { getList } from '@/api/conversation.js';
+
 const { } = useDict();
 const userType = uni.getStorageSync('userType') //读取本地存储
 const list = ref([])
 
+
+const type = computed(() =>{
+    return userType === 1 
+})
 const isRefreshing = ref(false)
 
 
@@ -65,7 +73,7 @@ const pages = ref({
 const onClick = (record) => {
 	console.log("TCL: onClick -> record", record)
     uni.navigateTo({
-        url: `/pages_orderuser/pages/talk/pages/index/index?orderId=${123}`
+        url: `/pages_orderuser/pages/talk/pages/index/index?conversationRecordId=${record.conversationRecordId}`
     });
 }
 const scrolltolower = () => {
@@ -96,10 +104,10 @@ const init = async (type) => {
             })
         }
 
-        const listApi = userType === 1 ? getAccountChangeList : getVolunteerChangeList;
-        const res = await listApi({
-            pageNum: pages.value.current,
-            pageSize: pages.value.pageSize,
+        const res = await getList({
+            // pageNum: pages.value.current,
+            // pageSize: pages.value.pageSize,
+            system: userType===1?'1':'2'
         });
         list.value = type === 'top' ? res.rows : [...list.value, ...res.rows];
         pages.value.total = res.total;
@@ -137,7 +145,7 @@ const totalInit = async () => {
 }
 onShow(() => {
     init('top');
-    totalInit();
+    // totalInit();
 })
 </script>
 

+ 3 - 4
pages/classify.vue

@@ -187,10 +187,9 @@ function btnClick(row, type) {
   }
 
   //前往沟通
-  // wxMakePhoneCall(phone)
-  // uni.navigateTo({
-  // 	url: `/pages_orderuser/pages/talk/pages/index/index?orderId=${row.secondOrderId}`
-  // });
+  uni.navigateTo({
+  	url: `/pages_orderuser/pages/talk/pages/index/index?orderId=${row.mainOrderId}`
+  });
 }
 provide('onClick', btnClick)
 

+ 7 - 4
pages/common/orderList/listItem.vue

@@ -18,10 +18,13 @@
                     </text>
                 </view>
                 <view class="item-de">
-                    <view style="flex: 1;">
+                    <view >
                         <img src="/static/serverImg/list/ad2.png" alt="" style="width: 28rpx;height: 28rpx;">
                     </view>
-                    {{ data.address }}
+                    <view style="flex: 1;">
+                        {{ data.address }}
+                    </view>
+                   
                 </view>
                 <view class="item-time">
                     服务时间:{{ data.workDate }} {{ data.workStartTime }}(<text class="item-time-text">{{ data.serviceDuration }}分钟</text>)
@@ -31,9 +34,9 @@
             <view class="item-box">
                 <view class="item-btns">
                     <view class="btn-m" @click="onClick(1)" v-if="['2','4'].includes(data.orderStatus)">查看</view>
-                    <!-- <view class="btn-m" @click="onClick(2)">
+                    <view class="btn-m" @click="onClick(2)">
                         <text style="margin-left: 8rpx;">沟通</text>
-                    </view> -->
+                    </view>
                     <view class="btn-m" @click="onClick(3)"  v-if="['3', '1'].includes(data.orderStatus)">
                         <!-- <up-icon name="chat" color="#ffffff" size="20"></up-icon> -->
                        {{ data.orderStatus === '1' ? '开始' : '完成' }}服务

+ 7 - 2
pages/index.vue

@@ -94,6 +94,7 @@ import { chooseLocationInit, locateTheCurrentAddress } from '@/utils/adress'
 import CustomTabBar from '@/components/CustomTabBar/index.vue'
 import { regionAddresstree } from '@/api/home.js'
 
+
 const rightList = ref([])
 const leftList = ref([])
 const list3 = ref([])
@@ -132,7 +133,8 @@ const pages = ref({
 
 const cityClick = async () => {
   const { latitude, longitude } = data.address
-  const address = await chooseLocationInit({ latitude, longitude })
+  const address = await chooseLocationInit({ latitude, longitude },addresstree.value)
+	console.log("TCL: cityClick -> address", address)
   data.address = { ...data.address, ...address }
   settingAddress()
   console.log('address', address)
@@ -239,7 +241,7 @@ const settingAddress = async () => {
   try {
     console.log('data', data)
 
-    const { address } = data
+    const { address } = data;
     const { cityCode, latitude, longitude } = address
     const parmas = {
       provinceName: cityCode.data[0], // 省
@@ -279,8 +281,11 @@ onShow(() => {
   })
 })
 
+const addresstree = ref([]);
+
 onMounted(async () => {
   const res_dara = await regionAddresstree()
+  addresstree.value = res_dara.data;
   locateTheCurrentAddress(res_dara.data)
     .then((res) => {
       console.log('locateTheCurrentAddress', res)

+ 3 - 0
pages/login.vue

@@ -57,6 +57,7 @@ import { useRouter } from 'vue-router' // 根据实际情况选择路由库
 import store from '@/store'
 import { onShow } from '@dcloudio/uni-app'
 
+
 const imagePath = '/static/9efd1.png' // Path to the logo image (red tree)
 
 // 获取全局配置
@@ -184,6 +185,7 @@ const pwdLogin = async () => {
 // 登录成功后,处理函数
 const loginSuccess = () => {
   store.dispatch('GetInfo').then((res) => {
+		console.log("TCL: loginSuccess -> res", res)
     console.log(store.state.user.userOrWorker, '>>>>99')
     if (store.state.user.userOrWorker == 0) {
       uni.reLaunch({
@@ -192,6 +194,7 @@ const loginSuccess = () => {
       return
     }
     uni.setStorageSync('userType', store.state.user.userOrWorker)
+    uni.setStorageSync('userId', res.user.userId)
     uni.switchTab({
       url: '/pages/index',
     })

+ 98 - 36
pages_orderuser/pages/talk/pages/index/index.vue

@@ -6,15 +6,19 @@
 					@refresherrefresh="refresherrefresh" :refresher-enabled="true" :scroll-with-animation="false"
 					:refresher-triggered="scrollView.refresherTriggered" :scroll-into-view="scrollView.intoView">
 					<view class="talk-list">
-						<view v-for="(item, index) in talkList" :key="item.id" :id="'msg-' + item.id">
-							<view class="item flex-col" :class="item.type == 1 ? 'push' : 'pull'">
-								<image :src="item.pic" mode="aspectFill" class="pic"></image>
+						<view v-for="(item, index) in talkList" :key="item.msgSendTime" :id="'msg-' + item.msgSendTime">
+							<view class="item flex-col" :class="item.senderId == this.userId ? 'push' : 'pull'">
+								<!-- <image :src="item.pic" mode="aspectFill" class="pic"></image> -->
+								<img v-if="item.senderId !== this.userId" :src="orderInfo.conversationAvatar" alt=""
+									class="pic" mode="aspectFill" />
+								<img src="/static/serverImg/mine/user.png" alt="" class="pic" mode="aspectFill"
+									v-else />
 								<view class="content">
 									<template v-if="item.contentType === 'image'">
-										<image :src="item.content" mode="widthFix" style="width: 400rpx;"></image>
+										<image :src="item.msgContent" mode="widthFix" style="width: 400rpx;"></image>
 									</template>
 									<template v-else>
-										{{ item.content }}
+										{{ item.msgContent }}
 									</template>
 								</view>
 							</view>
@@ -42,6 +46,9 @@
 <script>
 import { getHistoryMsg } from "@/pages_orderuser/pages/talk/request/template-talk/history-msg.js";
 import { wxUploadFile, wxMakePhoneCall } from '@/utils/wxRequest.js'
+import { sendMsg, getOrderConversationInfo, getListConversationInfo, setRead } from '@/api/conversation.js';
+import { onMounted } from "vue";
+
 export default {
 	data() {
 		return {
@@ -61,6 +68,12 @@ export default {
 			},
 			// 发送内容
 			content: '',
+			orderId: '',
+			orderInfo: {},//订单信息
+			userType: uni.getStorageSync('userType'), //读取本地存储
+			system: '2',
+			userId: uni.getStorageSync('userId'), //读取本地存储
+			conversationRecordId: null,//会话id
 		}
 	},
 	computed: {
@@ -75,12 +88,38 @@ export default {
 			return "";
 		}
 	},
-	onLoad() {
+	onLoad(options) {
+		console.log("TCL: onLoad -> options", options)
+		if (options.orderId) {
+			this.orderId = options.orderId;
+
+		}
+		if (options.conversationRecordId) {
+			this.conversationRecordId = options.conversationRecordId;
+		}
+		//1用户2志愿者3后台
+		this.system = this.userType === 1 ? '1' : '2';
 		// #ifdef H5
 		this.scrollView.safeAreaHeight = uni.getSystemInfoSync().safeArea.height;
 		// #endif
 
+
 		this.getMessage();
+
+	},
+	onMounted() {
+		uni.onSocketMessage(function (parmas) {
+			const res = JSON.parse(parmas.data);
+			console.log("TCL: get -> res", res)
+			if (res.type === 'msgNew') {
+				this.talkList.unshift({ ...res.data, code: 200 });
+			}
+
+			console.log(' onMounted-收到服务器消息:', res, this.talkList);
+
+
+		});
+
 	},
 	methods: {
 		// 下拉刷新
@@ -97,32 +136,48 @@ export default {
 			// 此处用到 ES7 的 async/await 知识,为使代码更加优美。不懂的请自行学习。
 			let get = async () => {
 				this.ajax.flag = false;
-				let data = await getHistoryMsg({
-					page: this.ajax.page,
-					rows: this.ajax.rows
-				});
-				this.scrollView.refresherTriggered = false;
 
-				// 获取待滚动元素选择器,解决插入数据后,滚动条定位时使用。取当前消息数据的第一条信息元素
-				const selector = `msg-${data?.[0]?.id}`;;
+				const parmas = {
+					system: this.system
+				}
+				const apiFun = this.orderId ? getOrderConversationInfo : getListConversationInfo
+				if (this.orderId) {
+					parmas.mainOrderId = this.orderId
+				}
+				if (this.conversationRecordId) {
+					parmas.conversationRecordId = this.conversationRecordId
+				}
 
-				// 将获取到的消息数据合并到消息数组中
-				this.talkList = this.talkList.concat(data);
+				await setRead(parmas); //设置已读
 
-				// 数据挂载后执行,不懂的请自行阅读 Vue.js 文档对 Vue.nextTick 函数说明。
-				this.$nextTick(() => {
-					// 设置当前滚动的位置
-					this.scrollView.intoView = selector;
+				const res = await apiFun(parmas);
+				console.log("TCL: getOrder -> res", res)
+				this.scrollView.refresherTriggered = false;
+				if (res.code === 200) {
+					const data = res.data.vos;
+					// 获取待滚动元素选择器,解决插入数据后,滚动条定位时使用。取当前消息数据的第一条信息元素
+					const selector = `msg-${data?.[0]?.msgSendTime}`;
+
+					// 将获取到的消息数据合并到消息数组中
+					this.talkList = [...this.talkList, ...data];
+					console.log("TCL: getOrder -> talkList", this.talkList)
+					this.orderInfo = res.data;
+
+					// 数据挂载后执行,不懂的请自行阅读 Vue.js 文档对 Vue.nextTick 函数说明。
+					this.$nextTick(() => {
+						// 设置当前滚动的位置
+						this.scrollView.intoView = selector;
+						if (data.length < this.ajax.rows) {
+							// 当前消息数据条数小于请求要求条数时,则无更多消息,不再允许请求。
+							// 可在此处编写无更多消息数据时的逻辑
+						} else {
+							this.ajax.flag = true;
+							this.ajax.page++;
+						}
+					})
+				}
 
-					if (data.length < this.ajax.rows) {
-						// 当前消息数据条数小于请求要求条数时,则无更多消息,不再允许请求。
-						// 可在此处编写无更多消息数据时的逻辑
-					} else {
-						this.ajax.flag = true;
-						this.ajax.page++;
-					}
 
-				})
 			}
 			get();
 		},
@@ -146,18 +201,25 @@ export default {
 			this.sendMessage(res[0].url, 'image');
 			console.log("TCL: handleImageClick -> res", res)
 		},
-		sendMessage(content, contentType = 'text') {
+		async sendMessage(content, contentType = 'text') {
 			// 将当前发送信息 添加到消息列表。
 			let data = {
-				"id": new Date().getTime(),
-				content,
-				contentType,
-				"type": 1,
-				"pic": "/static/logo.png"
+				conversationRecordId: "3",
+				msgContent: content,
+				msgSendTime: new Date().getTime(),
+				senderId: this.userId,
 			}
-			this.talkList.unshift(data);
-		}
-	}
+			const res = await sendMsg({
+				...this.orderInfo,
+				msgContent: content,
+				system: this.system
+			});
+			this.talkList.unshift({ ...data, code: res.code });
+			this.scrollView.intoView = `msg-${data.msgSendTime}`;
+		},
+
+	},
+
 }
 </script>
 

+ 3 - 1
pages_orderuser/pages/talk/request/template-talk/history-msg.js

@@ -1,3 +1,5 @@
+
+
 export const getHistoryMsg = (params)=>{
 	const { page = 1,rows = 10 } = params ?? {};
 	let join = ()=>{
@@ -14,7 +16,7 @@ export const getHistoryMsg = (params)=>{
 				"pic":"/static/logo.png"	// 头像
 			})
 		}
-		
+			
 		/*
 			颠倒数组中元素的顺序。将最新的数据排在本次接口返回数据的最后面。
 			后端接口按 消息的时间降序查找出当前页的数据后,再将本页数据按消息时间降序排序返回。

+ 15 - 3
store/modules/user.ts

@@ -16,7 +16,9 @@ const user: Module<UserState, UserState> = {
     roles: storage.get(constant.roles),
     permissions: storage.get(constant.permissions),
 	  userOrWorker: storage.get(constant.userOrWorker),
-    nickName:  storage.get(constant.nickName)
+    nickName:  storage.get(constant.nickName),
+    userId: null,
+    messageCount:0
   },
   mutations: {
     SET_TOKEN: (state, token: string) => {
@@ -45,7 +47,13 @@ const user: Module<UserState, UserState> = {
     SET_NICKNAME: (state, nickName: String) => {
       state.nickName = nickName
       storage.set(constant.nickName, nickName)
-    }
+    },
+    SET_USERID: (state, id: String) => {
+      state.userId = id
+    },
+    SET_MESSAGECOUNT: (state, num: Number) => {
+      state.messageCount = num
+    },
   },
 
   actions: {
@@ -89,6 +97,7 @@ const user: Module<UserState, UserState> = {
           commit('SET_NAME', username)
           commit('SET_AVATAR', avatar)
           commit('SET_NICKNAME', res.user.nickName)
+          commit('SET_USERID', res.user.userId)
           resolve(res)
         }).catch(error => {
           reject(error)
@@ -110,7 +119,10 @@ const user: Module<UserState, UserState> = {
           reject(error)
         })
       })
-    }
+    },
+    handleMessageCount({ commit, state },count) { 
+      commit('SET_MESSAGECOUNT', count);
+    },
   }
 }
 

+ 142 - 0
utils/WebSocketManager.js

@@ -0,0 +1,142 @@
+
+class WebSocketManager {
+    constructor(userId) {
+        // 1用户 2志愿者
+        this.system = uni.getStorageSync('userType') === 1 ? '1' : '2';
+        this.userId = userId;
+        this.url = `ws://192.168.100.127:9527/websocket/${this.system}/${this.userId}`;
+        this.socketTask = null;
+        this.heartbeatInterval = null;
+        this.reconnectTimer = null;
+        this.isConnecting = false;
+        this.onMessageCallback = null;
+    }
+
+    /**
+     * 建立连接
+     */
+    connect() {
+        if (this.isConnecting || !this.userId) {
+            console.warn('正在连接或缺少用户ID');
+            return;
+        }
+        this.isConnecting = true;
+
+        console.log('建立 WebSocket 连接:', this.url);
+        if(!this.socketTask){
+            this.socketTask = uni.connectSocket({
+                url: this.url,
+                success: () => console.log('WebSocket 连接创建成功'),
+            });
+        }
+       
+
+        this._setupEventListeners();
+    }
+
+    /**
+     * 设置事件监听器
+     */
+    _setupEventListeners() {
+        this.socketTask.onOpen(() => {
+            console.log('WebSocket 已打开');
+            this.isConnecting = false;
+            this.startHeartbeat();
+        });
+
+        this.socketTask.onMessage(res => {
+            console.log('收到 WebSocket 消息:', res.data);
+            try {
+                const data = JSON.parse(res.data);
+                if (typeof this.onMessageCallback === 'function') {
+                    this.onMessageCallback(data); // 回调通知外部
+                }
+            } catch (e) {
+                console.error('消息解析失败:', res.data);
+            }
+        });
+
+        this.socketTask.onError(err => {
+            console.error('WebSocket 发生错误:', err);
+        });
+
+        this.socketTask.onClose(() => {
+            console.log('WebSocket 已关闭');
+            this.stopHeartbeat();
+            this.reconnect(); // 自动重连
+        });
+    }
+
+    /**
+     * 启动心跳机制
+     */
+    startHeartbeat() {
+        // this.stopHeartbeat();
+        // this.heartbeatInterval = setInterval(() => {
+        //     uni.sendSocketMessage({
+        //         data: 'heartbeat',
+        //         success: () => console.log('心跳包已发送'),
+        //         fail: err => {
+        //             console.error('心跳包发送失败:', err);
+        //             this.stopHeartbeat();
+        //         }
+        //     });
+        // }, 5000);
+    }
+
+    /**
+     * 停止心跳机制
+     */
+    stopHeartbeat() {
+        if (this.heartbeatInterval) {
+            clearInterval(this.heartbeatInterval);
+            this.heartbeatInterval = null;
+        }
+    }
+
+    /**
+     * 重新连接
+     */
+    reconnect() {
+        clearTimeout(this.reconnectTimer);
+        this.reconnectTimer = setTimeout(() => {
+            console.log('尝试重新连接...');
+            this.connect();
+        }, 3000);
+    }
+
+    /**
+     * 主动发送消息
+     */
+    sendMessage(message) {
+        if (this.socketTask && typeof message !== 'undefined') {
+            uni.sendSocketMessage({
+                data: typeof message === 'object' ? JSON.stringify(message) : message,
+                success: () => console.log('消息发送成功:', message),
+                fail: err => console.error('消息发送失败:', err)
+            });
+        } else {
+            console.warn('尚未建立连接,消息发送失败');
+        }
+    }
+
+    /**
+     * 主动关闭连接
+     */
+    closeConnection() {
+        if (this.socketTask) {
+            this.socketTask.close();
+            this.socketTask = null;
+        }
+        this.stopHeartbeat();
+    }
+
+    /**
+     * 设置消息回调
+     */
+    onMessage(callback) {
+        this.onMessageCallback = callback;
+    }
+}
+
+export default WebSocketManager;

+ 2 - 3
utils/adress.js

@@ -4,13 +4,12 @@ const appName = config.appName;
 // const QQMapWX = require('./qqmap-wx-jssdk.js')
 import QQMapWX from './qqmap-wx-jssdk.js'
 
-// import addressData from './address.js';
 
 
 /**
  * 腾讯位置服务地图选点
  */
-const chooseLocationInit = (data) => {
+const chooseLocationInit = (data,addressData) => {
   // const key = mapKey; //使用在腾讯位置服务申请的key
   // const referer = appName; //调用插件的app的名称
   // wx.navigateTo({
@@ -24,7 +23,7 @@ const chooseLocationInit = (data) => {
         console.log('111',res);
         if(res.address.name){
           const result = splitAddress(res.address);
-          const handlecityData = getCityCode([result.province,result.city,result.district].join(' '))
+          const handlecityData = getCityCode([result.province,result.city,result.district].join(' '),addressData)
           resolve({...res,cityCode:handlecityData})
         }else{
           resolve(data)