fix: 聊天(已实现发送文本消息,接收未实现)

This commit is contained in:
2025-07-19 16:04:43 +08:00
parent 1d0c89ac16
commit 86d7f5b21c
16 changed files with 2349 additions and 348 deletions

10
App.vue
View File

@@ -2,6 +2,7 @@
import {
getVersion
} from './request/api/api'
import { webSocketManager } from '@/utils/ws/websocket.js';
export default {
// //初始化状态-如果登录过,保持登录状态
onLaunch: async function() {
@@ -81,6 +82,8 @@
content: '当前微信版本过低,可能无法使用该功能,请升级到最新版本后重试。'
})
}
this.initWebSocket();
},
onShow: function(e) {
@@ -110,7 +113,12 @@
},
onUnload:function() {
// uni.removeStorageSync('token')
}
},
methods: {
initWebSocket() {
webSocketManager.connect();
}
}
}
</script>

View File

@@ -77,7 +77,17 @@
"optimization" : {
"subPackages" : true
},
"permission" : {}
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于获取附近服务"
},
"scope.record": {
"desc": "需要录音功能发送语音消息"
},
"scope.camera": {
"desc": "需要摄像头拍照和视频通话"
}
}
},
"mp-alipay" : {
"usingComponents" : true
@@ -91,5 +101,6 @@
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
"vueVersion" : "2",
"sassImplementationName": "node-sass"
}

288
package-lock.json generated
View File

@@ -5,19 +5,305 @@
"packages": {
"": {
"dependencies": {
"@vue/composition-api": "^1.4.1",
"js-base64": "^3.7.7",
"uview-ui": "^1.8.8"
"pinia": "^2.0.14",
"uview-ui": "^1.8.8",
"vue-demi": "^0.14.5",
"vuex": "^3.6.2"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
"integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
"integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
"peer": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"postcss": "^8.4.14",
"source-map": "^0.6.1"
},
"optionalDependencies": {
"prettier": "^1.18.2 || ^2.0.0"
}
},
"node_modules/@vue/composition-api": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.4.1.tgz",
"integrity": "sha512-ZTat9ru/rwecveRnFzlO2mduOBpGfnBdXn+WtBcFLV9UsL/D+6nX47RWuLiVdNxNDX0qphGZRC+JDjwt+YTnRA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.1"
},
"peerDependencies": {
"vue": ">= 2.5 < 3"
}
},
"node_modules/@vue/devtools-api": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT",
"peer": true
},
"node_modules/js-base64": {
"version": "3.7.7",
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.7.tgz",
"integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"peer": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC",
"peer": true
},
"node_modules/pinia": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz",
"integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"license": "MIT",
"optional": true,
"peer": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/uview-ui": {
"version": "1.8.8",
"resolved": "https://registry.npmmirror.com/uview-ui/-/uview-ui-1.8.8.tgz",
"integrity": "sha512-Osal3yzXiHor0In9OPTZuXTaqTbDglMZ9RGK/MPYDoQQs+y0hrBCUD0Xp5T70C8i2lLu2X6Z11zJhmsQWMR7Jg=="
},
"node_modules/vue": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
}
},
"node_modules/vue-demi": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.5.tgz",
"integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
"license": "MIT",
"peerDependencies": {
"vue": "^2.0.0"
}
}
}
}

View File

@@ -1,6 +1,10 @@
{
"dependencies": {
"@vue/composition-api": "^1.4.1",
"js-base64": "^3.7.7",
"uview-ui": "^1.8.8"
"pinia": "^2.0.14",
"uview-ui": "^1.8.8",
"vue-demi": "^0.14.5",
"vuex": "^3.6.2"
}
}

View File

@@ -5,7 +5,8 @@
// npm安装方式
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
"pages": [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
"pages": [
//pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/home/index",
"style": {
@@ -110,9 +111,11 @@
// "needLogin": true
}
],
"subPackages": [{
"subPackages": [
{
"root": "subPackages",
"pages": [{
"pages": [
{
"path": "doctor/doctor-detail",
"style": {
"navigationBarTitleText": "医生详情",
@@ -162,9 +165,11 @@
}
}
]
}, {
},
{
"root": "subPackages",
"pages": [{
"pages": [
{
"path": "my/myinfo-add",
"style": {
"navigationBarTitleText": "添加就诊人",
@@ -191,7 +196,8 @@
"navigationBarTitleText": "订单详情",
"enablePullDownRefresh": false
}
}, {
},
{
"path": "my/myreport",
"style": {
"navigationBarTitleText": "检查报告",
@@ -211,7 +217,8 @@
"navigationBarTitleText": "线上复诊",
"enablePullDownRefresh": false
}
}, {
},
{
"path": "my/mystore",
"style": {
"navigationStyle": "default",
@@ -219,7 +226,8 @@
"navigationBarBackgroundColor": "#1777FF",
"navigationBarTextStyle": "white"
}
}, {
},
{
"path": "my/mycollection",
"style": {
"navigationBarTitleText": "个人资料",
@@ -246,7 +254,8 @@
"navigationBarTitleText": "处方记录",
"enablePullDownRefresh": true
}
}, {
},
{
"path": "my/myappiont",
"style": {
"navigationBarTitleText": "挂号记录",
@@ -283,34 +292,42 @@
}
}
]
}, {
},
{
"root": "subPackages",
"pages": [{
"path": "register/register",
"style": {
"navigationBarTitleText": "确认挂号信息"
"pages": [
{
"path": "register/register",
"style": {
"navigationBarTitleText": "确认挂号信息"
}
},
{
"path": "register/register-success",
"style": {
"navigationBarTitleText": "确认挂号信息"
}
},
{
"path": "register/register-info",
"style": {
"navigationBarTitleText": "挂号详情",
"navigationStyle": "custom"
}
}
}, {
"path": "register/register-success",
"style": {
"navigationBarTitleText": "确认挂号信息"
}
}, {
"path": "register/register-info",
"style": {
"navigationBarTitleText": "挂号详情",
"navigationStyle": "custom"
}
}]
}, {
]
},
{
"root": "subPackages",
"pages": [{
"pages": [
{
"path": "shop/orders",
"style": {
"navigationBarTitleText": "商城订单",
"enablePullDownRefresh": true
}
}, {
},
{
"path": "shop/search-home",
"style": {
"navigationBarTitleText": "萧康诊所门店"
@@ -374,7 +391,8 @@
},
{
"root": "subPackages",
"pages": [{
"pages": [
{
"path": "setup/filed",
"style": {
"navigationBarTitleText": "设置"
@@ -415,6 +433,13 @@
"style": {
"navigationBarTitleText": "客服中心"
}
},
{
"path": "chat/chat",
"style": {
"navigationBarTitleText": "在线问诊",
"enablePullDownRefresh": false
}
}
]
}
@@ -425,7 +450,8 @@
"spacing": "4px",
"backgroundColor": "#fff",
"selectedColor": "#1575FC",
"list": [{
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "static/home/02.png",
@@ -449,7 +475,8 @@
"iconPath": "static/home/07.png",
"selectedIconPath": "static/home/08.png"
}
]
],
"color": ""
},
"globalStyle": {
"navigationBarTextStyle": "black",

View File

@@ -1,310 +1,405 @@
<template>
<view class="content safe-area-inset-bottom">
<!-- 标题 -->
<view class="title">
<text>全部消息</text>
<view class="clear" @click="getClearInfo">
<text class="iconfont icon-qingchu"></text>
清除未读
</view>
</view>
<view class="content safe-area-inset-bottom">
<!-- 标题 -->
<view class="title">
<text>全部消息</text>
<view class="clear" @click="getClearInfo">
<text class="iconfont icon-qingchu"></text>
清除未读
</view>
</view>
<!-- 消息列表 -->
<view class="list" @click="goSystem">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/xt.png" mode=""></image>
</view>
<u-badge size="default" v-if="system.num>=0" :count="system.num" :is-dot="system.num==0?true:false"
type="error" :offset="system.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">系统消息</text>
<text class="time">{{system.time}}</text>
</view>
<view class="preview" v-if="system.num >0">
您有{{system.num}}条未读信息
</view>
<view class="preview" v-if="system.num==undefined">
暂无新消息
</view>
</view>
</view>
<!-- 消息列表 -->
<view class="list" @click="goSystem">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/xt.png" mode=""></image>
</view>
<u-badge size="default" v-if="system.num>=0" :count="system.num" :is-dot="system.num==0?true:false"
type="error" :offset="system.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">系统消息</text>
<text class="time">{{system.time}}</text>
</view>
<view class="preview" v-if="system.num >0">
您有{{system.num}}条未读信息
</view>
<view class="preview" v-if="system.num==undefined">
暂无新消息
</view>
</view>
</view>
<view class="list" @click="goOrder">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/cp.png" mode=""></image>
</view>
<u-badge size="default" v-if="order.num>=0" :count="order.num" :is-dot="order.num==0?true:false"
type="error" :offset="order.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">产品订单</text>
<text class="time">{{order.time}}</text>
</view>
<view class="preview" v-if="order.num>0">
您有{{order.num}}条未读信息
</view>
<view class="preview" v-if="order.num==undefined">
暂无新消息
</view>
</view>
</view>
<view class="list" @click="goOrder">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/cp.png" mode=""></image>
</view>
<u-badge size="default" v-if="order.num>=0" :count="order.num" :is-dot="order.num==0?true:false"
type="error" :offset="order.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">产品订单</text>
<text class="time">{{order.time}}</text>
</view>
<view class="preview" v-if="order.num>0">
您有{{order.num}}条未读信息
</view>
<view class="preview" v-if="order.num==undefined">
暂无新消息
</view>
</view>
</view>
<view class="list" @click="goDoctor">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/ysxx.png" mode=""></image>
</view>
<u-badge size="default" v-if="doctor.num>=0" :count="doctor.num" :is-dot="doctor.num==0?true:false"
type="error" :offset="doctor.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">医生消息</text>
<text class="time">{{doctor.time}}</text>
</view>
<view class="preview" v-if="doctor.num>0">
您有{{doctor.num}}条新的消息
</view>
<view class="preview" v-if="doctor.num==undefined">
暂无新消息
</view>
</view>
</view>
</view>
<view class="list" @click="goDoctor">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/ysxx.png" mode=""></image>
</view>
<u-badge size="default" v-if="doctor.num>=0" :count="doctor.num" :is-dot="doctor.num==0?true:false"
type="error" :offset="doctor.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">医生消息</text>
<text class="time">{{doctor.time}}</text>
</view>
<view class="preview" v-if="doctor.num>0">
您有{{doctor.num}}条新的消息
</view>
<view class="preview" v-if="doctor.num==undefined">
暂无新消息
</view>
</view>
</view>
<!-- 在线问诊-->
<view class="list" @click="goChat">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="/static/xx/ysxx.png" mode=""></image>
</view>
<u-badge size="default" v-if="doctor.num>=0" :count="doctor.num" :is-dot="doctor.num==0?true:false"
type="error" :offset="doctor.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">在线问诊</text>
<text class="time">{{doctor.time}}</text>
</view>
<view class="preview" v-if="doctor.num>0">
您有{{doctor.num}}条新的消息
</view>
<view class="preview" v-if="doctor.num==undefined">
暂无新消息
</view>
</view>
</view>
<!-- 模拟<u-divider>在线问诊</u-divider>效果-->
<!-- 标题 -->
<view class="title">
<text>在线问诊</text>
<!-- <view class="clear" @click="getClearInfo">-->
<!-- <text class="iconfont icon-qingchu"></text>-->
<!-- 清除未读-->
<!-- </view>-->
</view>
<!-- 在线问诊 - 用户-->
<view v-for="item in imChat" :key="item.id" class="list" @click="goChat(item)">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image :src="item.avatar" mode=""></image>
</view>
<u-badge size="default" v-if="item.un_read_num>=0" :count="item.un_read_num" :is-dot="item.un_read_num==0?true:false"
type="error" :offset="item.un_read_num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">{{ item.nick_name }}</text>
<text class="time">{{item.last_message_time}}</text>
</view>
<view class="preview" v-if="item.un_read_num>0">
<p>{{ item.last_message }}</p>
<p>您有{{item.un_read_num}}条新的消息</p>
</view>
<view class="preview" v-if="item.un_read_num==undefined">
暂无新消息
</view>
</view>
</view>
</view>
</template>
<script>
import {
getseesionInfo,
getseesionRead
} from '../../request/api/api'
export default {
data() {
return {
order: [],
system: [],
doctor:[],
type: ""
}
},
onLoad() {
import {
getseesionInfo,
getseesionRead
} from '../../request/api/api'
export default {
data() {
return {
order: [],
system: [],
doctor:[],
imChat:[
{
id: 'doctor-1',
room_id: 'xk_chat_room_68786b4fd071233eb15dfa3e62c1',
nick_name: '李二狗',
avatar: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250108/29bdb9560340373bb3096394a845afa5.jpg',
un_read_num: 0,
last_message: '[图片]',
last_message_time: '2021-07-05 09:05:05'
},
{
id: 'doctor-2',
room_id: 'xk_chat_room_68786b4fd071233eb15dfa3e62c1',
nick_name: '王三三',
avatar: '/static/xx/ysxx.png',
un_read_num: 0,
last_message: '[图片]',
last_message_time: '2021-07-05 09:05:05'
}
],
type: ""
}
},
onLoad() {
},
methods: {
// 清除未读信息
getClearInfo() {
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "all"
}
}).then((res) => {
// console.log(res, 'res');
uni.showToast({
title: '清除成功',
duration: 2000,
icon: "success"
});
},
methods: {
// 清除未读信息
getClearInfo() {
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "all"
}
}).then((res) => {
// console.log(res, 'res');
uni.showToast({
title: '清除成功',
duration: 2000,
icon: "success"
});
this.getInfo()
})
},
// 系统信息
goSystem() {
uni.navigateTo({
url: '/pages/index/system'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "system"
}
}).then((res) => {
// console.log(res, 'res');
})
},
// 详情
goOrder() {
uni.navigateTo({
url: '/pages/index/order-info'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "order"
}
}).then((res) => {
// console.log(res, 'res');
})
},
goDoctor() {
uni.navigateTo({
url: '/pages/index/doctor-info'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "doctor_news"
}
}).then((res) => {
// console.log(res, 'res');
})
},
//
getInfo() {
getseesionInfo({
method: "get",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
}
}).then((res) => {
// console.log(res, 'info');
this.order = res.data.data.order
this.system = res.data.data.system
this.doctor=res.data.data.doctor_news
})
}
},
mounted() {
this.getInfo()
},
onShow() {
this.order = []
this.system = []
this.doctor=[]
this.getInfo()
}
}
this.getInfo()
})
},
// 系统信息
goSystem() {
uni.navigateTo({
url: '/pages/index/system'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "system"
}
}).then((res) => {
// console.log(res, 'res');
})
},
// 详情
goOrder() {
uni.navigateTo({
url: '/pages/index/order-info'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "order"
}
}).then((res) => {
// console.log(res, 'res');
})
},
goDoctor() {
uni.navigateTo({
url: '/pages/index/doctor-info'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "doctor_news"
}
}).then((res) => {
// console.log(res, 'res');
})
},
goChat(item) {
uni.navigateTo({
url: `/subPackages/chat/chat?room_id=${item.room_id}&nick_name=${item.nick_name}&user_id=${item.id}&avatar=${item.avatar}`
})
// getseesionRead({
// method: "post",
// data: {
// store_id: uni.getStorageSync('store_id') || 11001,
// type: "doctor_news"
// }
// }).then((res) => {
// // console.log(res, 'res');
// })
},
//
getInfo() {
getseesionInfo({
method: "get",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
}
}).then((res) => {
// console.log(res, 'info');
this.order = res.data.data.order
this.system = res.data.data.system
this.doctor=res.data.data.doctor_news
})
}
},
mounted() {
this.getInfo()
},
onShow() {
this.order = []
this.system = []
this.doctor=[]
this.getInfo()
}
}
</script>
<style lang="scss" scoped>
.content {
width: 750rpx;
max-height: 100%;
min-height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
background: #F5F5F5;
.content {
width: 750rpx;
max-height: 100%;
min-height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
background: #F5F5F5;
.title {
padding: 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
.title {
padding: 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
text {
height: 50rpx;
font-size: 36rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #0F172A;
}
text {
height: 50rpx;
font-size: 36rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #0F172A;
}
.clear {
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #536DFE;
line-height: 33rpx;
.clear {
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #536DFE;
line-height: 33rpx;
.iconfont {
color: #536DFE;
margin-right: 8rpx;
vertical-align: text-top;
}
}
}
.iconfont {
color: #536DFE;
margin-right: 8rpx;
vertical-align: text-top;
}
}
}
.list {
width: 750rpx;
display: flex;
padding: 32rpx 38rpx;
justify-content: space-between;
background: #fff;
.list {
width: 750rpx;
display: flex;
padding: 32rpx 38rpx;
justify-content: space-between;
background: #fff;
.avator {
position: relative;
.avator {
position: relative;
.img {
width: 84rpx;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 50%;
background: #E6EAFF;
.img {
width: 84rpx;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 50%;
background: #E6EAFF;
image {
width: 100%;
height: 100%;
}
}
}
image {
width: 100%;
height: 100%;
}
}
}
.info {
width: 570rpx;
height: 94rpx;
font-family: PingFang SC-Medium, PingFang SC;
.info {
width: 570rpx;
height: 94rpx;
font-family: PingFang SC-Medium, PingFang SC;
.infos_title {
width: 570rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8rpx;
.infos_title {
width: 570rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8rpx;
.doctor {
.name {
height: 42rpx;
font-size: 32rpx;
font-weight: 500;
color: #1E293B;
.doctor {
.name {
height: 42rpx;
font-size: 32rpx;
font-weight: 500;
color: #1E293B;
}
}
.infos {
height: 42rpx;
font-size: 28rpx;
font-weight: 500;
color: #475569;
}
}
.infos {
height: 42rpx;
font-size: 28rpx;
font-weight: 500;
color: #475569;
}
}
.time {
font-size: 24rpx;
font-weight: 400;
color: #94A3B8;
}
}
.time {
font-size: 24rpx;
font-weight: 400;
color: #94A3B8;
}
}
.preview {
width: 570rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #475569;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
.preview {
width: 570rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #475569;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
}
}
</style>

View File

@@ -3,4 +3,15 @@ import http from './request'
// export async function sendMsg(params) {
// let data = await http('/v1/im/message-send', params)
// return data
// }
// }
// 登录 手机号授权
export async function sendToUserApi(params) {
return await http('/imApi/send-to-user', {
method: 'POST',
data: params,
// 传输方式是json
headers: {
'Content-Type': 'application/json'
}
}, 1)
}

View File

@@ -18,14 +18,18 @@ const sensitiveData = [
'idcard'
]
function request(url, params) {
let baseURL = "https://app.xiaokang88.com/member"
// let baseURL = "http://www.xk888.com/member"
function request(url, params, method = 0) {
// let baseURL = "https://app.xiaokang88.com/member"
let baseURL = "http://127.0.0.1:18000/member"
if (url.split('/').indexOf('imApi') !== -1) {
baseURL = "http://127.0.0.1:12080/api"
}
if (url.split('/').indexOf('newApi') !== -1) {
baseURL = "https://shop.xiaokang88.com/member"
}
url = url.replace('/oldApi', '');
url = url.replace('/newApi', '');
url = url.replace('/imApi', '');
// 加密
if (params != null) {
@@ -44,7 +48,7 @@ function request(url, params) {
// 请求头
header: {
"authorization": 'Bearer ' + uni.getStorageSync("token"),
"Content-Type": "application/x-www-form-urlencoded"
"Content-Type": method === 0? "application/x-www-form-urlencoded": 'application/json'
},
// 成功钩子

81
store/chat/chat.js Normal file
View File

@@ -0,0 +1,81 @@
// chat.js
// 聊天消息管理器
const ChatManager = {
// 当前房间ID
currentRoomId: null,
// 存储未读消息数 { roomId: count }
unreadCounts: {},
// 进入聊天室
enterRoom(roomId) {
this.currentRoomId = roomId;
uni.setStorageSync('currentRoomId', roomId);
console.log(`进入房间: ${roomId}`);
},
// 离开聊天室
leaveRoom() {
this.currentRoomId = null;
uni.removeStorageSync('currentRoomId');
console.log('离开当前房间');
},
// 获取当前房间ID
getCurrentRoomId() {
return this.currentRoomId || uni.getStorageSync('currentRoomId');
},
// 增加未读消息数
increaseUnreadCount(roomId) {
// 如果当前不在该房间,才增加未读计数
if (this.getCurrentRoomId() !== roomId) {
this.unreadCounts[roomId] = (this.unreadCounts[roomId] || 0) + 1;
this.notifyUnreadChange();
console.log(`房间 ${roomId} 未读消息 +1, 当前: ${this.unreadCounts[roomId]}`);
}
},
// 重置未读消息数
resetUnreadCount(roomId) {
if (this.unreadCounts[roomId]) {
this.unreadCounts[roomId] = 0;
this.notifyUnreadChange();
console.log(`重置房间 ${roomId} 未读消息数`);
}
},
// 获取未读消息数
getUnreadCount(roomId) {
return this.unreadCounts[roomId] || 0;
},
// 获取所有未读消息数
getAllUnreadCount() {
return Object.values(this.unreadCounts).reduce((sum, count) => sum + count, 0);
},
// 通知未读消息变化
notifyUnreadChange() {
uni.$emit('unread-message-update', {
total: this.getAllUnreadCount(),
byRoom: {...this.unreadCounts}
});
},
// 处理收到的消息
handleIncomingMessage(message) {
const currentRoomId = this.getCurrentRoomId();
// 如果当前在消息所属的房间,直接显示
if (currentRoomId === message.room_id) {
// 通知聊天页面显示消息
uni.$emit('new-message', message);
} else {
// 不在当前房间,增加未读消息数
this.increaseUnreadCount(message.room_id);
}
}
};
export { ChatManager };

View File

@@ -37,7 +37,7 @@
// state,
// dispatch
// }, res) {
// console.log('user-123');
// console.log('user-252');
// // 存到状态中
// state.user = res
// state.user_token = uni.getStorageSync('token');

1293
subPackages/chat/chat.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -102,7 +102,7 @@
registerList,
storeChange,
userList
} from '../../request/api/api'
} from '@/request/api/api'
export default {
data() {
return {

View File

@@ -519,7 +519,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 380rpx;
}
}
@@ -573,7 +573,7 @@
display: flex;
flex-wrap: wrap;
/deep/ .u-tag {
::v-deep .u-tag {
margin: 10rpx 20rpx 10rpx 0;
}
@@ -615,11 +615,11 @@
margin-top: 2rpx;
}
/deep/ .u-input {
::v-deep .u-input {
margin-left: 130rpx;
}
/deep/ .u-input__right-icon ::before {
::v-deep .u-input__right-icon ::before {
content: ""
}
@@ -671,7 +671,7 @@
.u_inp {
margin: 40rpx 0;
/deep/ .u-input {
::v-deep .u-input {
background: #F1F5F9;
}
}

View File

@@ -634,7 +634,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 400rpx;
}
}
@@ -657,7 +657,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 380rpx;
}
}
@@ -740,11 +740,11 @@
margin-top: 2rpx;
}
/deep/ .u-input {
::v-deep .u-input {
margin-left: 130rpx;
}
/deep/ .u-input__right-icon ::before {
::v-deep .u-input__right-icon ::before {
content: ""
}
@@ -830,7 +830,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 380rpx;
}
}
@@ -884,7 +884,7 @@
display: flex;
flex-wrap: wrap;
/deep/ .u-tag {
::v-deep .u-tag {
margin: 10rpx 20rpx 10rpx 0;
}
@@ -926,11 +926,11 @@
margin-top: 2rpx;
}
/deep/ .u-input {
::v-deep .u-input {
margin-left: 130rpx;
}
/deep/ .u-input__right-icon ::before {
::v-deep .u-input__right-icon ::before {
content: ""
}
@@ -982,7 +982,7 @@
.u_inp {
margin: 40rpx 0;
/deep/ .u-input {
::v-deep .u-input {
background: #F1F5F9;
}
}

View File

@@ -723,7 +723,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 380rpx;
}
}
@@ -746,7 +746,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 380rpx;
}
}
@@ -828,11 +828,11 @@
margin-top: 2rpx;
}
/deep/ .u-input {
::v-deep .u-input {
margin-left: 130rpx;
}
/deep/ .u-input__right-icon ::before {
::v-deep .u-input__right-icon ::before {
content: ""
}
@@ -918,7 +918,7 @@
color: #1E293B;
}
/deep/ .u-input {
::v-deep .u-input {
width: 380rpx;
}
}
@@ -972,7 +972,7 @@
display: flex;
flex-wrap: wrap;
/deep/ .u-tag {
::v-deep .u-tag {
margin: 10rpx 20rpx 10rpx 0;
}
@@ -1014,11 +1014,11 @@
margin-top: 2rpx;
}
/deep/ .u-input {
::v-deep .u-input {
margin-left: 130rpx;
}
/deep/ .u-input__right-icon ::before {
::v-deep .u-input__right-icon ::before {
content: ""
}
@@ -1070,7 +1070,7 @@
.u_inp {
margin: 40rpx 0;
/deep/ .u-input {
::v-deep .u-input {
background: #F1F5F9;
}
}

181
utils/ws/websocket.js Normal file
View File

@@ -0,0 +1,181 @@
export class WebSocketManager {
constructor(url) {
this.url = url;
this.socket = null;
this.isConnected = false;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
this.messageHandlers = [];
this.pingInterval = null;
}
connect() {
if (this.isConnected) return;
console.log('正在连接WebSocket...');
this.socket = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket连接成功');
},
fail: (err) => {
console.error('WebSocket连接失败:', err);
this.handleReconnect();
}
});
this.socket.onOpen(() => {
console.log('WebSocket连接已打开');
this.isConnected = true;
this.reconnectAttempts = 0;
// 开始心跳检测
this.startHeartbeat();
// 绑定当前用户
const userId = uni.getStorageSync('userId');
if (userId) {
this.bindUser(userId);
}
});
this.socket.onMessage((res) => {
try {
const data = JSON.parse(res.data);
console.log('收到消息:', data);
this.handleMessage(data);
} catch (e) {
console.error('解析消息失败:', e);
}
});
this.socket.onClose((res) => {
console.log('WebSocket连接关闭:', res);
this.isConnected = false;
this.stopHeartbeat();
this.handleReconnect();
});
this.socket.onError((err) => {
console.error('WebSocket错误:', err);
});
}
bindUser(userId) {
this.send({
type: 'bind',
userId: userId
});
}
send(message) {
if (!this.isConnected) {
console.error('WebSocket未连接');
return false;
}
try {
const messageStr = JSON.stringify(message);
this.socket.send({
data: messageStr,
success: () => {
console.log('消息发送成功:', message);
},
fail: (err) => {
console.error('消息发送失败:', err);
}
});
return true;
} catch (err) {
console.error('发送消息出错:', err);
return false;
}
}
addMessageHandler(handler) {
this.messageHandlers.push(handler);
}
removeMessageHandler(handler) {
const index = this.messageHandlers.indexOf(handler);
if (index !== -1) {
this.messageHandlers.splice(index, 1);
}
}
handleMessage(data) {
// 如果对象中有clientId则给websocket发送
if (data.clientId) {
const userId = '252'; // 实际项目中从存储获取
const bindMessage = {
request_type: 'bind',
user_type: 'user',
sender_user_id: `user-${userId}`,
};
this.send(bindMessage);
}
// 处理聊天消息
if (data.message_type !== undefined) {
// 将消息转发给聊天管理器
this.messageHandlers.forEach(handler => {
try {
handler(data);
} catch (e) {
console.error('消息处理出错:', e);
}
});
} else {
// 其他消息处理
this.messageHandlers.forEach(handler => {
try {
handler(data);
} catch (e) {
console.error('消息处理出错:', e);
}
});
}
}
startHeartbeat() {
this.pingInterval = setInterval(() => {
if (this.isConnected) {
this.send({type: 'ping'});
}
}, 300000);
}
stopHeartbeat() {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
}
handleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log('已达到最大重连次数');
return;
}
setTimeout(() => {
this.reconnectAttempts++;
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000);
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect();
}, this.reconnectDelay);
}
disconnect() {
if (this.socket) {
this.socket.close();
this.socket = null;
}
this.isConnected = false;
this.stopHeartbeat();
}
}
// 全局WebSocket实例
export const webSocketManager = new WebSocketManager('ws://127.0.0.1:12080/ws');