diff --git a/CommunicationRecords/.cloudbase/container/debug.json b/CommunicationRecords/.cloudbase/container/debug.json new file mode 100644 index 0000000..0d44458 --- /dev/null +++ b/CommunicationRecords/.cloudbase/container/debug.json @@ -0,0 +1 @@ +{"containers":[],"config":{}} \ No newline at end of file diff --git a/CommunicationRecords/.gitignore b/CommunicationRecords/.gitignore new file mode 100644 index 0000000..14ea590 --- /dev/null +++ b/CommunicationRecords/.gitignore @@ -0,0 +1,14 @@ +# Windows +[Dd]esktop.ini +Thumbs.db +$RECYCLE.BIN/ + +# macOS +.DS_Store +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes + +# Node.js +node_modules/ diff --git a/CommunicationRecords/app.js b/CommunicationRecords/app.js index 2db4645..a6df4b4 100644 --- a/CommunicationRecords/app.js +++ b/CommunicationRecords/app.js @@ -15,7 +15,11 @@ App({ }, globalData: { userInfo: null, - baseUrl: 'https://你的域名' // 后端 API 根地址 + baseUrl: 'https://你的域名', // 后端 API 根地址 + userKey:"", + avatarUrl:"", + weChatName:"", + } }) diff --git a/CommunicationRecords/app.wxss b/CommunicationRecords/app.wxss index 06c6fc9..d494213 100644 --- a/CommunicationRecords/app.wxss +++ b/CommunicationRecords/app.wxss @@ -1,4 +1,4 @@ -/**app.wxss**/ + .container { height: 100%; display: flex; diff --git a/CommunicationRecords/assets/default.png b/CommunicationRecords/assets/default.png new file mode 100644 index 0000000..8c2af15 Binary files /dev/null and b/CommunicationRecords/assets/default.png differ diff --git a/CommunicationRecords/images/Blvlogo.png b/CommunicationRecords/images/Blvlogo.png new file mode 100644 index 0000000..8c2af15 Binary files /dev/null and b/CommunicationRecords/images/Blvlogo.png differ diff --git a/CommunicationRecords/images/Icon_阅读模式_7.487_7.504.png b/CommunicationRecords/images/Icon_阅读模式_7.487_7.504.png new file mode 100644 index 0000000..502e851 Binary files /dev/null and b/CommunicationRecords/images/Icon_阅读模式_7.487_7.504.png differ diff --git a/CommunicationRecords/images/Keyboard.png b/CommunicationRecords/images/Keyboard.png new file mode 100644 index 0000000..5424e0e Binary files /dev/null and b/CommunicationRecords/images/Keyboard.png differ diff --git a/CommunicationRecords/images/Voice.png b/CommunicationRecords/images/Voice.png new file mode 100644 index 0000000..8f4f99f Binary files /dev/null and b/CommunicationRecords/images/Voice.png differ diff --git a/CommunicationRecords/images/lock.png b/CommunicationRecords/images/lock.png new file mode 100644 index 0000000..0e029b3 Binary files /dev/null and b/CommunicationRecords/images/lock.png differ diff --git a/CommunicationRecords/images/lock2.png b/CommunicationRecords/images/lock2.png new file mode 100644 index 0000000..fecdf92 Binary files /dev/null and b/CommunicationRecords/images/lock2.png differ diff --git a/CommunicationRecords/images/lock_3.png b/CommunicationRecords/images/lock_3.png new file mode 100644 index 0000000..6af681e Binary files /dev/null and b/CommunicationRecords/images/lock_3.png differ diff --git a/CommunicationRecords/images/lock_r.png b/CommunicationRecords/images/lock_r.png new file mode 100644 index 0000000..54e8876 Binary files /dev/null and b/CommunicationRecords/images/lock_r.png differ diff --git a/CommunicationRecords/images/logo.png b/CommunicationRecords/images/logo.png new file mode 100644 index 0000000..5238212 Binary files /dev/null and b/CommunicationRecords/images/logo.png differ diff --git a/CommunicationRecords/images/touxiang.png b/CommunicationRecords/images/touxiang.png new file mode 100644 index 0000000..2f6ca82 Binary files /dev/null and b/CommunicationRecords/images/touxiang.png differ diff --git a/CommunicationRecords/images/unlock_b.png b/CommunicationRecords/images/unlock_b.png new file mode 100644 index 0000000..6b9dfa8 Binary files /dev/null and b/CommunicationRecords/images/unlock_b.png differ diff --git a/CommunicationRecords/images/unlock_blue.png b/CommunicationRecords/images/unlock_blue.png new file mode 100644 index 0000000..d81d2e4 Binary files /dev/null and b/CommunicationRecords/images/unlock_blue.png differ diff --git a/CommunicationRecords/pages/chat/chat.js b/CommunicationRecords/pages/chat/chat.js new file mode 100644 index 0000000..e4e70d0 --- /dev/null +++ b/CommunicationRecords/pages/chat/chat.js @@ -0,0 +1,1126 @@ +// 在文件顶部引入配置 +const config = require('../../utils/config.js'); +const mock = require('./mock.js') +const app = getApp() +// 1. 引入插件 +const plugin = requirePlugin('WechatSI'); +// 2. 获取全局唯一的语音识别管理器 +const manager = plugin.getRecordRecognitionManager(); +function formatNumber(n) { + n = n.toString() + return n[1] ? n : '0' + n +} +function formatTime(date) { + const y = date.getFullYear() + const m = formatNumber(date.getMonth() + 1) + const d = formatNumber(date.getDate()) + const h = formatNumber(date.getHours()) + const mi = formatNumber(date.getMinutes()) + return { + date: `${y}-${m}-${d}`, + time: `${y}-${m}-${d} ${h}:${mi}` + } +} + +Page({ + data: { + chatTitle: '宝来威智能AI', + msgList: [], + OriginalmsgList: [], + toView: '', + qinputTxt: '', + ginputTxt: '', + voiceMode: true, + recording: false, + recording1: false, + cancelSend: false, + cancelSend1: false, + TabCur:1, + UserKey:"", + resultText: '' , // 识别出的文本结果 + triggered: false, // 控制下拉状态 + pulling: false, // 是否正在下拉 + pullText: '下拉刷新', + PageNumber:1, + RecordingStatus:0,//0 丢弃 /1 全局/2私有 + TrhandleTouchEnd:0, + scrollHeight:500, + Keyboard:"", + Voice:"", + timer: null, // 用于存储定时器ID[5,6](@ref) + modalName:"", + inputValue:"", + RevisemsgId:-1, + isFocused: false, + avatarUrl:"/images/touxiang.png", + weChatName:"", + }, + + onLoad() { + + + this.setData({avatarUrl:app.globalData.avatarUrl,weChatName:app.globalData.weChatName}) + this.loadHistory() + this.initRecordManager() + this.GetmyScrollhight() + // this.initSound() + this.startTimer(); + }, + // 初始化提示音 + // initSound() { + // // this.dingCtx = wx.createInnerAudioContext(); + // // this.dingCtx.src = '/assets/sounds/ding.wav'; // 指向生成的音频文件 + // }, +// 页面卸载时清理资源 +onUnload: function () { + this.clearTimer(); + if (this.data.recording) { + manager.stop(); + this.setData({ + recording: false, + recording1: false, + cancelSend: false, + cancelSend1: false, + RecordingStatus: 0 + }); + } +}, +onHide() { + this.clearTimer(); + // 停止录音 + if (this.data.recording || this.data.recording1) { + manager.stop(); + this.setData({ + recording: false, + recording1: false, + cancelSend: false, + cancelSend1: false, + RecordingStatus: 0 + }); + } +}, +onShow: function() { + // 如果页面从后台返回,可以重新启动定时器[6](@ref) + this.startTimer(); +}, + + // 启动定时器 + startTimer: function() { + // 先清除可能存在的旧定时器,防止重复创建[6](@ref) + this.clearTimer(); + + const that = this; + // 设置一个每秒执行一次的定时器,并保存其ID[1](@ref) + const timerId = setInterval(function() { + // 在这里编写你需要每秒执行的逻辑 + that.executePerSecond(); + }, 1000); + + // 将定时器ID存入data中,便于管理[5,6](@ref) + this.setData({ + timer: timerId + }); + }, + executePerSecond: function() { + const list = this.data.OriginalmsgList + const TabCur=this.data.TabCur + if (!list ||list.length==0) { + return + } + const t =Date.now() + let cuncvl =0 + //debugger + + let dateObj //= new Date(dateString); + for (let index = 0; index < list.length; index++) { + const element = list[list.length-index-1]; + if (element.lock==='Unlock') { + let dateObj=new Date(element.time); + cuncvl =t - dateObj + if (cuncvl>300000) { + element.lock='lock' + list[list.length-index-1]=element + this.setData({OriginalmsgList:list}) + this.tabSelect1(TabCur) + + } + } + + + + } + + }, + // 清除定时器 + clearTimer: function() { + if (this.data.timer !== null) { + clearInterval(this.data.timer); // 清除定时器[4](@ref) + this.setData({ + timer: null + }); + console.log('定时器已清除'); + } +}, +async onAvatar(e){ + let tempFilePath =e.detail.avatarUrl + let UserKey = wx.getStorageSync('openid'); + this.uploadAvatarToServer(tempFilePath,UserKey) +}, +uploadAvatarToServer(tempFilePath,userKey) { + // 显示加载提示,提升用户体验 + wx.showLoading({ title: '上传中...' }); + + wx.uploadFile({ + url: 'https://wx-xcx-check.blv-oa.com:4433/api/Check/UploadFile', // 你的服务器上传接口 + filePath: tempFilePath, + name: 'file', // 与后端约定的文件参数名 + formData: { + 'rootPathType': 'Avatar', // 指定保存到 wwwroot/Avatar 目录 + 'userKey': userKey // 指定要更新头像的用户 + }, + success: (res) => { + wx.hideLoading(); + const data = JSON.parse(res.data); // 注意: uploadFile返回的data是字符串,需解析 + if (data.success) { + // 上传成功,拿到服务器返回的永久链接 + const permanentUrl = data.url; + this.setData({ + avatarUrl: permanentUrl // 更新为永久链接 + }); + // 接下来可以将 permanentUrl 保存到本地缓存或发送给后端更新用户信息 + wx.showToast({ title: '上传成功' }); + } else { + wx.showToast({ title: '上传失败: ' + data.message, icon: 'none' }); + } + }, + fail: (err) => { + wx.hideLoading(); + console.error('上传接口调用失败', err); + wx.showToast({ title: '网络错误', icon: 'none' }); + } + }); +}, + + /* ---------- 历史消息 ---------- */ + loadHistory() { + // 本地 Mock,上线改为请求接口 + + //const list = mock.map(item => ({ ...item, showDate: true })) + let UserKey=wx.getStorageSync('openid') + try { + this.GetConversations(UserKey,0) + } catch (error) { + // console.error('获取历史信息失败!:', error); + wx.showToast({ + title: "获取历史信息失败!", + icon: 'none' + }); + } + + //this.setData({ msgList: list }) + this.scrollBottom() + }, + async GetConversations(UserKey ,MessageType) { + + const apiRes = await new Promise((resolve, reject) => { + wx.request({ + url: `${config.baseUrl}/api/Check/GetConversationsByPage`, + method: 'POST', + data: {UserKey:UserKey ,Page:1,PageSize:20 ,MessageType:0}, + success: res =>{ + console.log(res) + const list = this.data.msgList + for (let index = 0; index < res.data.data.conversations.length; index++) { + const element = res.data.data.conversations[index]; + const createTime = element.createTime.toString().replace("T"," ") + let location =`${element.latitude},${element.longitude}` + const newMsg = { + id: element.id, + location:location, + guid :element.guid, + content:element.conversationContent, + date:createTime, + time:createTime, + isSelf:element.messageType, + showDate:createTime, + sendMethod:element.sendMethod, + voicetime:element.speakingTime, + status: 'success', + lock:'lock' + } + list.push(newMsg) + + } + + this.setData({ msgList: list,OriginalmsgList: list}) + }, + fail: reject + }); + }); + + }, + /* ---------- 文字输入 ---------- */ + qonInput(e) { + this.setData({ qinputTxt: e.detail.value }) + }, + gonInput(e) { + this.setData({ ginputTxt: e.detail.value }) + }, + qsendText() { + + let txt = this.data.qinputTxt.trim() + if (!txt) return + if (txt.length>100000) { + txt=txt.substring(0,100000).trim() + } + this.pushMsg(txt, 1,0) + this.upload(txt, 'text',1) + this.setData({ qinputTxt: '' }) + }, + gsendText() { + + let txt = this.data.qinputTxt.trim() + if (!txt) return + if (txt.length>1000) { + txt=txt.substring(0,1000).trim() + } + this.pushMsg(txt, 2,0) + this.upload(txt, 'text',2) + this.setData({ qinputTxt: '' }) + }, + /* ---------- 模式切换 ---------- */ + switchInputMode() { + this.setData({ voiceMode: !this.data.voiceMode }) + }, + + /* ---------- 语音录制 ---------- */ + initRecordManager: function () { + const that = this; + + // 识别开始事件 + manager.onStart = function (res) { + console.log("开始录音识别"); + // 可以在这里触发手机振动反馈 + wx.vibrateShort({ type: 'medium' }); + console.log("触发手机振动反馈"); + + + }; + + // 有新的识别内容返回(实时返回) + manager.onRecognize = function (res) { + console.log('实时识别结果:', res.result); + // 如果需要实时显示,可以在这里 setData + that.setData({ resultText: res.result }); + }; + + // 识别结束事件(返回最终结果) + manager.onStop = function (res) { + console.log('识别结束', res); + let finalText = res.result; + if (!finalText||finalText.length==0) { + wx.showToast({ title: '未识别到内容', icon: 'none' }); + finalText = ''; + return + } + //发送 + //debugger + let RecordingStatus =that.data.RecordingStatus + let TrhandleTouchEnd = that.data.TrhandleTouchEnd + let cost = Date.now() - that.recordStartTime + console.log(cost) + console.log(RecordingStatus) + if (RecordingStatus!==0 ||cost>=60000) { + // 播放提示音 + if (this.dingCtx) { + this.dingCtx.stop(); + this.dingCtx.play(); + } + if (finalText.length>100000) { + finalText=finalText.substring(0,100000).trim() + } + + if (cost>=60000) { + cost=60 + }else{ + cost = Math.ceil(cost / 1000) + } + + that.pushMsg(finalText, TrhandleTouchEnd,cost) + that.upload(finalText, 'voice',TrhandleTouchEnd) + } + + + + // 更新结果到页面 + that.setData({ + resultText: '', // 识别结束清空实时显示 + recording1: false, + recording: false, + RecordingStatus:0 + }); + }; + + // 识别错误事件 + manager.onError = function (res) { + // console.log('识别错误:'+ res); + // let TrhandleTouchEnd =that.data.TrhandleTouchEnd + // if (TrhandleTouchEnd==1) { + // manager.stop() + // } + // if (TrhandleTouchEnd==2) { + // manager.stop() + // } + + + + that.setData({ recording: false,recording1: false }); + // 可以根据错误码给出具体提示 + const errorMap = { + '-30003': '说话时间太短', + '-30004': '没有听清,请重试', + '-30011': '上个录音正在识别中' + }; + const msg = errorMap[res.retcode] || '识别失败'; + wx.showToast({ title: msg, icon: 'none' }); + + }; + }, + // 开始录音 + startRecord: function () { + const that = this; + // 先检查授权 + wx.getSetting({ + success(res) { + if (res.authSetting['scope.record']) { + // 已有授权,开始录音 + that.doStartRecord(); + } else { + // 请求授权 + wx.authorize({ + scope: 'scope.record', + success() { that.doStartRecord(); }, + fail() { + wx.showModal({ + title: '权限申请', + content: '需要您授权使用麦克风', + success(modalRes) { + if (modalRes.confirm) { + wx.openSetting(); // 引导用户去设置页开启 + } + } + }); + } + }); + } + } + }); + }, + // 执行开始录音操作 + doStartRecord: function () { + console.log("doStartRecord"); + + this.setData({ + // recording: true, + resultText: '' // 开始新的录音时清空上次结果 + }); + // 调用插件的start方法,传入参数 + setTimeout(() => { + console.log("调用插件的start方法,传入参数"); + + let TrhandleTouchEnd =this.data.TrhandleTouchEnd + console.log(TrhandleTouchEnd); + if (TrhandleTouchEnd==1) { + let recording =this.data.recording + console.log(recording); + if (recording==false) { + + // this.stopRecord1() + // this.stopRecord() + // console.log("打断录音"); + return + } + } + if (TrhandleTouchEnd==2) { + let recording1 =this.data.recording1 + console.log(recording1); + if (recording1==false) { + + // this.stopRecord1() + // this.stopRecord() + // console.log("打断录音"); + return + } + } + manager.start({ + lang: 'zh_CN', // 语言,支持 zh_CN(中文)、en_US(英文)等 + duration: 60000 // 录音时长,单位毫秒(这里是60秒) + }); + + }, 300); +}, + +// 停止录音 +stopRecord: function () { + if (this.data.recording) { + manager.stop(); // 停止录音并触发 onStop 回调 + } +}, +stopRecord1: function () { + if (this.data.recording1) { + manager.stop(); // 停止录音并触发 onStop 回调 + } +}, + + handleTouchStart(e) { + + console.log("handleTouchStart") + this.startY = e.touches[0].pageY + this.recordStartTime = Date.now() + this.setData({ recording: true, cancelSend: false ,RecordingStatus:0,TrhandleTouchEnd:1}) + //this.recorderManager.start({ duration: 60000, format: 'mp3' }) + const recording =this.data.recording + //wx.vibrateShort('heavy'); + if (recording) { + this.startRecord() + console.log("startRecord") + } + console.log("handleTouchStart_en") + }, + handleTouchStart1(e) { + //wx.vibrateLong(); +// wx.vibrateShort('heavy'); + console.log("handleTouchStart1") + this.startY1 = e.touches[0].pageY + this.recordStartTime = Date.now() + this.setData({ recording1: true, cancelSend1: false ,RecordingStatus:0,TrhandleTouchEnd:2}) + //this.recorderManager.start({ duration: 60000, format: 'mp3' }) + this.startRecord() + }, + handleTouchMove(e) { + + //console.log("handleTouchMove") + const diff = this.startY - e.touches[0].pageY + //console.log(diff) + if (diff > 100) { + this.setData({ cancelSend:true,RecordingStatus:0}) + manager.stop(); + } + + }, + handleTouchMove1(e) { + //console.log("handleTouchMove1") + const diff = this.startY1 - e.touches[0].pageY + //console.log(diff) + if (diff > 100) { + this.setData({ cancelSend1:true,RecordingStatus:0}) + manager.stop(); + } + + }, + handleTouchEnd() { + console.log("handleTouchEnd") + const cost = Date.now() - this.recordStartTime + if (cost < 350) { + //this.recorderManager.stop() + manager.stop(); + this.setData({ recording: false,RecordingStatus:0 }) + console.log( this.data.recording) + return + } + //this.recorderManager.stop() + manager.stop(); + this.setData({ recording: false ,RecordingStatus:1}) + console.log( this.data.recording) + }, + handleTouchEnd1() { + console.log("handleTouchEnd1") + const cost = Date.now() - this.recordStartTime + if (cost < 350) { + //this.recorderManager.stop() + manager.stop(); + this.setData({ recording1: false }) + console.log( this.data.recording) + return + } + //this.recorderManager.stop() + manager.stop(); + this.setData({ recording1: false,RecordingStatus:2 }) + console.log( this.data.recording) + }, + handleTouchCancel() { + console.log("handleTouchCancel") + //this.recorderManager.stop() + manager.stop(); + this.setData({ recording: false, cancelSend: false ,RecordingStatus:0}) + }, + handleTouchCancel1() { + + console.log("handleTouchCancel1") + //this.recorderManager.stop() + this.stopRecord() + this.setData({ recording1: false, cancelSend1: false,RecordingStatus:0 }) + }, + + + + /* ---------- 消息渲染 ---------- */ + pushMsg(content, MessageType,voicetime) { + const { date, time } = formatTime(new Date()) + const list = this.data.OriginalmsgList + const last = list[list.length - 1] + const showDate = !last || last.date !== date + const newMsg = { + id: Date.now(), + voicetime,voicetime, + content, + date, + time, + isSelf:MessageType, + showDate, + status: 'sending', // 添加状态字段: sending, success, failed + lock:'Unlock' + } + list.push(newMsg) + this.setData({ OriginalmsgList: list }) + let TabCur=this.data.TabCur + this.tabSelect1(TabCur) + this.scrollBottom() + return newMsg.id; + }, + scrollBottom() { + setTimeout(() => { + this.setData({ toView: `msg-${this.data.msgList.length - 1}` }) + }, 100) + }, + + /* ---------- 上传服务器 ---------- */ +async upload(txt, type, MessageType) { + console.log('upload') + const msgId = this.data.OriginalmsgList[this.data.OriginalmsgList.length - 1].id; + + + try { + const location = await new Promise((resolve, reject) => { + wx.getLocation({ + type: 'gcj02', + success(res) { + console.log('getLocation') + resolve(`${res.latitude},${res.longitude}`) + }, + fail(err) { + console.error('获取位置失败:', err) + resolve('') // 位置获取失败时使用空字符串 + } + }) + }) + const list = [...this.data.OriginalmsgList] + const msgIndex = list.findIndex(msg => msg.id === msgId) + list[msgIndex].location=location + list[msgIndex].sendMethod=type + list[msgIndex].isSelf=MessageType + list[msgIndex].time=this.GetTimeDate() + this.setData({ OriginalmsgList: list }) + + const apiRes = await this.post(txt, type, location, MessageType,list[msgIndex].voicetime) + + + console.log(apiRes) + // 更新消息状态为成功 + + this.updateMessageStatus(msgId, 'success') + list[msgIndex].time=apiRes.data.receivedTime + list[msgIndex].guid =apiRes.data.conversationGuid + this.setData({ OriginalmsgList: list }) + console.log(this.data.OriginalmsgList) + } catch (error) { + console.error('上传失败:') + console.error(error) + + // 更新消息状态为失败 + this.updateMessageStatus(msgId, 'failed') + wx.showToast({ + title: '发送失败,请重试', + icon: 'none' + }) + } + }, + + +// 添加更新消息状态的方法 +updateMessageStatus(msgId, status) { + + console.log('updateMessageStatus') + console.log(status) + const list = [...this.data.OriginalmsgList] + const msgIndex = list.findIndex(msg => msg.id === msgId) + if (msgIndex !== -1) { + list[msgIndex].status = status + this.setData({ OriginalmsgList: list }) + let TabCur = this.data.TabCur + this.tabSelect1(TabCur) + } +}, +// 添加重发消息方法 +retryMessage(e) { + const { id } = e.currentTarget.dataset + const list = this.data.OriginalmsgList + const msgIndex = list.findIndex(msg => msg.id === id) + if (msgIndex !== -1) { + const msg = list[msgIndex] + this.updateMessageStatus(id, 'sending') + this.upload(msg.content, msg.sendMethod, msg.isSelf) + } +}, + + async post(content, sendMethod, location,MessagType,voicetime) { + // console.log('post') + + let UserKey=wx.getStorageSync('openid') + const apiRes = await new Promise((resolve, reject) => { + wx.request({ + url: `${config.baseUrl}/api/Check/AddConversation`, + method: 'POST', + data: { + UserKey:UserKey , + ConversationContent: content, + SendMethod: sendMethod, + UserLocation: location, + MessageType:MessagType, + SpeakingTime:voicetime, + Guid:"" + }, + success: res =>{ + // console.log(res); + resolve(res); // 增加这行,解析Promise并返回结果 + }, + fail: (err) => { + //console.error('请求失败:', err); + reject(err); + } + }); + }); + //console.log(apiRes); + return apiRes + + }, + + + + tabSelect(e){ + this.tabSelect1(e.currentTarget.dataset.id) + }, + tabSelect1(type) { + let list = [] + let Oldlist = [] + switch (type) { + case 1: + list =this.data.OriginalmsgList + break; + case 2: + Oldlist =this.data.OriginalmsgList + for (let index = 0; index < Oldlist.length; index++) { + const element = Oldlist[index]; + if (element.isSelf===1) { + list.push(element) + } + } + break; + case 3: + Oldlist =this.data.OriginalmsgList + for (let index = 0; index < Oldlist.length; index++) { + const element = Oldlist[index]; + if (element.isSelf===2) { + list.push(element) + } + } + break; + default: + list =this.data.OriginalmsgList + break; + } + this.setData({ + msgList: list, + TabCur: type, + scrollLeft: (type-1)*60 + }) + }, + + /* 1. 下拉中(实时)*/ + onPulling(e) { + + if (!this.data.pulling) this.setData({ pulling: true }) + const dy = e.detail.dy // 下拉垂直距离 + this.setData({ pullText: dy > 80 ? '释放立即刷新' : '下拉刷新' }) + }, + /* 2. 达到阈值松手 → 真正刷新 */ + async onRefresh() { + + // this.setData({ pullText: '加载中…' }) + // await this.loadData(true) // true=刷新模式 + // this.setData({ triggered: false }) // 关闭下拉状态 + + // 记录刷新开始时间,用于计算最少展示时间 + const startTime = Date.now(); + const minAnimationTime = 1000; // 最少展示1秒动画 + + this.setData({ + pullText: '加载中…', + isLoading: true // 添加加载状态标识 + }); + + try { + // 执行数据加载 + await this.loadData(true); + + // 计算已用时间,如果不足1秒则延迟补足 + const elapsedTime = Date.now() - startTime; + const remainingTime = Math.max(minAnimationTime - elapsedTime, 0); + + if (remainingTime > 0) { + await new Promise(resolve => setTimeout(resolve, remainingTime)); + } + + // 显示"已加载最新记录"提示 + this.setData({ + pullText: '已加载最新记录' + }); + + // 短暂显示提示文字后再关闭下拉状态 + await new Promise(resolve => setTimeout(resolve, 500)); + + } catch (error) { + console.error('下拉刷新失败:', error); + this.setData({ + pullText: '加载失败,请重试' + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + } finally { + // 无论成功失败,最终都关闭下拉状态 + this.setData({ + triggered: false, + isLoading: false + }); + } + + + }, + /* 3. 回弹复位 */ + onRestore() { + this.setData({ pulling: false, pullText: '下拉刷新' }) + }, + /* 4. 请求数据 */ + async loadData(reload = false) { + let oldli =this.data.OriginalmsgList + let pagenumber =Math.floor(oldli.length /20) + let yushu =oldli.length % 20 + if (yushu>0) { + let nli =[] + for (let nindex = yushu; nindex < oldli.length; nindex++) { + const element = oldli[nindex]; + nli.push(element) + } + oldli=nli + } + let UserKey=wx.getStorageSync('openid') + pagenumber=pagenumber+1 + const apiRes = await new Promise((resolve, reject) => { + + wx.request({ + url: `${config.baseUrl}/api/Check/GetConversationsByPage`, + method: 'POST', + data: {UserKey:UserKey ,Page:pagenumber,PageSize:20 ,MessageType:0}, + success: res =>{ + console.log(res) + let list = [] + for (let index = 0; index < res.data.data.conversations.length; index++) { + const element = res.data.data.conversations[index]; + const createTime = element.createTime.toString().replace("T"," ") + let location =`${element.latitude},${element.longitude}` + const newMsg = { + id: element.id, + guid :element.guid, + location:location, + content:element.conversationContent, + date:createTime, + time:createTime, + isSelf:element.messageType, + showDate:createTime, + sendMethod:element.sendMethod, + voicetime:element.speakingTime, + status: 'success', + lock:'lock' + } + list.push(newMsg) + } + + const newArray = list.concat(oldli) + this.setData({ PageNumber: pagenumber,OriginalmsgList: newArray,triggered: false}) + let TabCur=this.data.TabCur + this.tabSelect1(TabCur) + + + }, + fail: reject + }); + }); + }, + handleLongPressmsg(e){ + console.log(e) + + const id = e.currentTarget.id + const list = this.data.OriginalmsgList + if (!id||!list ||list.length==0) { + return + } + + for (let index = 0; index < list.length; index++) { + const element = list[list.length-index-1]; + if (id==element.id) { + if (element.status=='success' && element.lock=='Unlock') { + + this.setData({ + inputValue:element.content, + RevisemsgId:id, + modalName: "DialogModal1" // px→rpx + }); + + } + return + } + + } + + + }, + inputSearchForHotels(e){ + + this.setData({ + inputValue: e.detail.value + }) + }, + async hideModal(e) { + + this.setData({ + modalName: "" + }) + if (e.currentTarget.id=='ok') { + const id = this.data.RevisemsgId + const inputValue =this.data.inputValue + let TabCur=this.data.TabCur + const list = this.data.OriginalmsgList + if (inputValue.length==0) { + wx.showToast({ + title: '修改内容不能为空', + icon: 'none', + duration:2000 + }) + + return + } + + for (let index = 0; index < list.length; index++) { + const element = list[list.length-index-1]; + if (id==element.id) { + try { + const result = await this.updateConversation( + element.guid, // 会话ID + inputValue, // 新内容 + element.sendMethod, // 发送方式 + element.location, // 定位信息(可选) + element.isSelf // 消息类型(可选) + ); + list[list.length-index-1].content=inputValue + list[list.length-index-1].time=result.data.receivedTime + this.setData({ OriginalmsgList: list }) + this.tabSelect1(TabCur) + return + } catch (error) { + wx.showToast({ + title: '网络异常修改内容失败!', + icon: 'none', + duration:2000 + }) + list[list.length-index-1].time=this.GetTimeDate() + list[list.length-index-1].lock='Unlock' + this.setData({ OriginalmsgList: list }) + this.tabSelect1(TabCur) + return + } + + + } + + } + }else if (e.currentTarget.id=='Withdrawal') { + const id = this.data.RevisemsgId + + let TabCur=this.data.TabCur + const list = this.data.OriginalmsgList + let Nlist =this.data.OriginalmsgList + + for (let index = 0; index < list.length; index++) { + const element = list[list.length-index-1]; + if (id==element.id) { + try { + const result = await this.deleteConversation(element.guid); + Nlist.splice(list.length-index-1,1); + this.setData({ OriginalmsgList: Nlist,qinputTxt:element.content ,voiceMode:false}) + this.tabSelect1(TabCur) + return + } catch (error) { + wx.showToast({ + title: '网络异常修改内容失败!', + icon: 'none', + duration:2000 + }) + list[list.length-index-1].time=this.GetTimeDate() + list[list.length-index-1].lock='Unlock' + this.setData({ OriginalmsgList: list }) + this.tabSelect1(TabCur) + return + } + + + } + + } + } + + }, + GetTimeDate(){ +// 1. 获取当前时间戳 +let timestamp = Date.now(); // 例如:1733225035000 + +// 2. 将时间戳转换为 Date 对象 +let dateObj = new Date(timestamp); + +// 3. 获取各个时间部分并进行格式化 +let year = dateObj.getFullYear(); // 年份(四位) +let month = String(dateObj.getMonth() + 1).padStart(2, '0'); // 月份(补零) +let day = String(dateObj.getDate()).padStart(2, '0'); // 日期(补零) +let hours = String(dateObj.getHours()).padStart(2, '0'); // 小时(补零) +let minutes = String(dateObj.getMinutes()).padStart(2, '0'); // 分钟(补零) +let seconds = String(dateObj.getSeconds()).padStart(2, '0'); // 秒(补零) + +// 4. 拼接成所需的字符串格式 +// 格式:YYYY-MM-DD HH:mm:ss("yyyy-MM-dd HH:mm:ss") +let formattedString1 = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +return formattedString1 +}, + +/** + * 软删除会话记录 + * @param {number|string} id - 会话记录ID + * @returns {Promise} - API响应结果 + */ +async deleteConversation(id) { + let UserKey = wx.getStorageSync('openid'); + const apiRes = await new Promise((resolve, reject) => { + wx.request({ + url: `${config.baseUrl}/api/Check/DeleteConversation`, + method: 'POST', + data: { + Guid: id, + UserKey: UserKey + }, + success: res => { + resolve(res); + }, + fail: (err) => { + console.error('删除会话记录失败:', err); + reject(err); + } + }); + }); + return apiRes; +}, +/** + * 更新会话记录 + * @param {number|string} id - 会话记录ID + * @param {string} content - 新的会话内容 + * @param {string} sendMethod - 新的发送方式 + * @param {string} [location] - 新的用户定位信息(可选) + * @param {number} [messageType] - 消息类型:1-公有消息,2-私有消息(可选) + * @returns {Promise} - API响应结果 + */ +async updateConversation(id, content, sendMethod, location = '', messageType = null) { + let UserKey = wx.getStorageSync('openid'); + const apiRes = await new Promise((resolve, reject) => { + wx.request({ + url: `${config.baseUrl}/api/Check/UpdateConversation`, + method: 'POST', + data: { + Guid: id, + UserKey: UserKey, + ConversationContent: content, + SendMethod: sendMethod, + UserLocation: location, + MessageType: messageType + }, + success: res => { + resolve(res); + }, + fail: (err) => { + console.error('更新会话记录失败:', err); + reject(err); + } + }); + }); + return apiRes; +}, +// 获得焦点时的处理函数 +onTextareaFocus: function(e) { + console.log('textarea 获得焦点', e.detail); + // 示例:记录焦点状态,用于UI变化 + this.setData({ + isFocused: true + }); +}, + +// 失去焦点时的处理函数 +onTextareaBlur: function(e) { + console.log('textarea 失去焦点', e.detail.value); + // 示例:进行表单验证或提交数据 + this.setData({ + isFocused: false + }); + +}, + + + GetmyScrollhight(){ + + wx.createSelectorQuery() + .in(this) + .select('#myScroll') + .boundingClientRect(rect => { + if (rect) { + const topPx = rect.top; // px + const bottomPx = 10 / 2; // 10rpx → 5px(2倍屏) + //const heightPx = screenHeight - topPx - bottomPx; + + wx.createSelectorQuery() + .in(this) + .select('#BottomFrame') //底部输入栏 + .boundingClientRect(nrect => { + if (nrect) { + const BottomFrametopPx = nrect.top; // px + const BottomFramebottomPx = 40 / 2; // 10rpx → 5px(2倍屏) + const BottomFrameheightPx = BottomFrametopPx-topPx- bottomPx; + + + // 3. 转 rpx 并写入 + this.setData({ + scrollHeight: BottomFrameheightPx //* 2 // px→rpx + }); + } + }) + .exec(); + + + } + }) + .exec(); + } +}) \ No newline at end of file diff --git a/CommunicationRecords/pages/chat/chat.json b/CommunicationRecords/pages/chat/chat.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/CommunicationRecords/pages/chat/chat.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/CommunicationRecords/pages/chat/chat.wxml b/CommunicationRecords/pages/chat/chat.wxml new file mode 100644 index 0000000..8c692bf --- /dev/null +++ b/CommunicationRecords/pages/chat/chat.wxml @@ -0,0 +1,167 @@ + + + + + + + + + {{weChatName}} + + + + + + 全部信息 + + + 公开信息 + + + 个人信息 + + + + + + + + + + + + + {{ pullText }} + + + + + + + + + + + + + + + ({{!item.voicetime? 0:item.voicetime}} s) + {{item.time}} + + + {{item.content}} + 发送中... + 发送失败,点击重发 + + + + + + + + + + + + + + {{resultText}} + 正在转换中... + + + + + + + + + + + + +