/*
|
* @Company: hugeInfo
|
* @Author: lwh
|
* @Date: 2025-04-15 20:04:56
|
* @LastEditTime: 2025-04-18 11:11:09
|
* @LastEditors: lwh
|
* @Version: 1.0.0
|
* @Description:
|
*/
|
const app = getApp();
|
|
const $$ = require('../../utils/util');
|
|
// 详情接口
|
function getByIdApi(param) {
|
return $$.request({
|
url: 'caseInfo/getCaseInfo',
|
type: 'get',
|
submitData: param || {},
|
service: 'mediate',
|
});
|
}
|
|
// 获取用户信息
|
function getUserInfoApi() {
|
return $$.request({
|
url: 'paUser/personal',
|
type: 'get',
|
service: 'cust',
|
});
|
}
|
|
function getawApi(submitData) {
|
return $$.request({
|
url: 'case-law/get-law',
|
type: 'post',
|
ai: true,
|
submitData,
|
service: 'mediate',
|
});
|
}
|
// 获取案例
|
function getCaseApi(submitData) {
|
return $$.request({
|
url: 'case-law/get-case',
|
type: 'post',
|
ai: true,
|
submitData,
|
service: 'mediate',
|
});
|
}
|
|
// 获取调解策略
|
function getMediateStrategypi(submitData) {
|
return $$.request({
|
url: 'case-law/getMediateStrategy',
|
type: 'post',
|
ai: true,
|
submitData,
|
service: 'mediate',
|
});
|
}
|
|
// AI对话接口
|
function getStreamChatApi(submitData) {
|
return $$.request({
|
url: 'case-law/streamChat',
|
type: 'post',
|
ai: true,
|
submitData,
|
service: 'mediate',
|
});
|
}
|
|
Page({
|
recordMannager: wx.getRecorderManager(),
|
data: {
|
chatList: [],
|
inputValue: '',
|
scrollToView: '',
|
userInfo: {},
|
imageUrl: $$.url.img,
|
imgUrl: app.globalData.imgUrl,
|
demoImgUrl: $$.url.img + 'Aimge.png',
|
demoAIImgUrl: $$.url.img + '1.gif',
|
typingMessage: null,
|
typingIndex: 0,
|
typingTimer: null,
|
isTyping: false,
|
isRecording: false,
|
showModal: false, //按住说话显示
|
recordManager: null,
|
caseId: '', // 案件ID
|
caseDes: '', // 案件描述
|
isCardExpanded: false, // 控制卡片展开/收起状态
|
},
|
|
onLoad(options) {
|
// 接收传入的参数
|
this.getById(options);
|
|
// 初始化录音管理器
|
this.recordManager = wx.getRecorderManager();
|
|
// 监听录音结束事件
|
this.recordManager.onStop((res) => {
|
this.setData({
|
isRecording: false,
|
});
|
if (res.duration < 1000) {
|
wx.showToast({
|
title: '录音时间太短',
|
icon: 'none',
|
});
|
return;
|
}
|
|
// 调用语音识别接口
|
wx.showLoading({
|
title: '识别中...',
|
});
|
|
// 这里需要替换为实际的语音识别接口
|
// 模拟语音识别结果
|
setTimeout(() => {
|
wx.hideLoading();
|
this.setData({
|
inputValue: '这是一段模拟的语音识别结果',
|
});
|
}, 1500);
|
});
|
},
|
|
// 获取纠纷案件详情
|
async getById(props) {
|
$$.showLoading();
|
const res = await getByIdApi({
|
id: props.caseId,
|
});
|
$$.hideLoading();
|
if (res.type) {
|
let data = res.data || {};
|
this.setData({
|
submitData: data,
|
oneList: [...data.personList, ...data.agentList],
|
caseId: props.caseId,
|
});
|
this.getFilesId(data.id);
|
}
|
},
|
|
// 开始录音
|
startRecord() {
|
this.setData({
|
isRecording: true,
|
});
|
this.recordManager.start({
|
duration: 60000, // 最长录音时间
|
sampleRate: 16000,
|
numberOfChannels: 1,
|
encodeBitRate: 48000,
|
format: 'mp3',
|
});
|
},
|
|
// 停止录音
|
stopRecord() {
|
this.recordManager.stop();
|
},
|
|
// 获取用户信息
|
async getUserInfo() {
|
$$.showLoading();
|
const res = await getUserInfoApi();
|
$$.hideLoading();
|
if (res.type) {
|
this.setData({
|
userInfo: res.data,
|
});
|
}
|
},
|
|
// 输入框内容变化
|
onInputChange(e) {
|
this.setData({
|
inputValue: e.detail.value,
|
});
|
},
|
|
// 发送消息
|
async sendMessage() {
|
const { inputValue, chatList } = this.data;
|
if (!inputValue.trim()) return;
|
|
// 清空相关数据
|
this.setData({
|
caseData: [],
|
AIData: [],
|
chatList: [],
|
});
|
|
// 添加用户消息
|
this.addMessage('user', inputValue);
|
|
// 清空输入框
|
this.setData({
|
inputValue: '',
|
});
|
|
// 调用AI对话接口
|
const res = await getStreamChatApi({
|
// caseText: this.data?.submitData?.caseDes,
|
caseText:
|
'在广州市白云区松洲街松南菜市场,张平云因隔壁档口苏振昌把泡沫箱摆在其档口旁边,双方因泡沫箱摆放位置发生纠纷,苏振昌故意损坏了张平云档口的灯泡,张平云报警求助。民警将双方带回派出所进行调解。',
|
query: inputValue,
|
// conversationId: this.data.caseId,
|
});
|
|
if (res.type) {
|
// this.setData({
|
// chatList: [...chatList, { type: 'ai', content: res.data, time: new Date().getTime() }],
|
// });
|
}
|
|
// 模拟AI回复
|
setTimeout(() => {
|
this.startTyping('感谢您的咨询,我会尽快为您解答。');
|
}, 1000);
|
},
|
|
// 开始逐字显示
|
startTyping(content) {
|
// 清除之前的定时器
|
if (this.data.typingTimer) {
|
clearInterval(this.data.typingTimer);
|
}
|
|
// 设置正在打字状态
|
this.setData({
|
isTyping: true,
|
});
|
|
// 添加一条空消息
|
const newMessage = {
|
type: 'ai',
|
content: '',
|
time: new Date().getTime(),
|
};
|
|
// 只保留最新的两条消息
|
const chatList = [...this.data.chatList, newMessage].slice(-2);
|
this.setData({
|
chatList,
|
typingMessage: content,
|
typingIndex: 0,
|
});
|
|
// 开始逐字显示
|
const timer = setInterval(() => {
|
const { typingIndex, typingMessage, chatList } = this.data;
|
if (typingIndex < typingMessage.length) {
|
const newContent = typingMessage.substring(0, typingIndex + 1);
|
// 更新最后一条消息的内容
|
const updatedChatList = [...chatList];
|
updatedChatList[updatedChatList.length - 1].content = newContent;
|
this.setData({
|
chatList: updatedChatList,
|
typingIndex: typingIndex + 1,
|
});
|
|
// 减少滚动频率,只在段落结束或每30个字符时滚动一次
|
if (typingIndex % 30 === 0 || typingMessage.charAt(typingIndex) === '\n' || typingIndex === typingMessage.length - 1) {
|
this.scrollToBottom(false);
|
}
|
} else {
|
clearInterval(timer);
|
this.setData({
|
typingMessage: null,
|
typingIndex: 0,
|
typingTimer: null,
|
isTyping: false,
|
});
|
|
// 完成打字后滚动到底部(使用平滑滚动)
|
this.scrollToBottom(true);
|
}
|
}, 50);
|
|
this.setData({
|
typingTimer: timer,
|
});
|
},
|
|
// 添加消息
|
addMessage(type, content) {
|
const { chatList } = this.data;
|
const newMessage = {
|
type,
|
content,
|
time: new Date().getTime(),
|
};
|
|
// 只保留最新的两条消息
|
const newChatList = [...chatList, newMessage].slice(-2);
|
|
this.setData({
|
chatList: newChatList,
|
scrollToView: `msg-${newChatList.length - 1}`,
|
});
|
|
// 自动滚动到底部
|
this.scrollToBottom();
|
},
|
|
// 滚动到底部
|
scrollToBottom(smooth = true) {
|
setTimeout(() => {
|
// 设置滚动位置
|
this.setData({
|
scrollToView: 'card-bottom',
|
});
|
|
// 阻止频繁滚动引起的抖动
|
if (!this._scrollDebounce) {
|
this._scrollDebounce = true;
|
setTimeout(() => {
|
this._scrollDebounce = false;
|
}, 300);
|
}
|
}, 50);
|
},
|
|
// 录音结束触发
|
_endRecord(e) {
|
this._transferText(e);
|
},
|
|
touchStart(e) {
|
let that = this;
|
wx.getSetting({
|
success(res) {
|
if (res.authSetting['scope.record'] === false) {
|
$$.hideLoading();
|
$$.showModal({
|
content: '抱歉!此功能需授权麦克风录音功能',
|
confirmText: '跳转授权',
|
success: (res) => {
|
if (res.confirm) {
|
wx.openSetting({
|
success(res) {
|
if (res.authSetting['scope.record']) {
|
$$.showToast({
|
title: '授权成功',
|
});
|
} else {
|
$$.showToast({
|
title: '授权失败',
|
});
|
}
|
},
|
});
|
}
|
},
|
});
|
return false;
|
}
|
that.setData({
|
showModal: true,
|
});
|
// 开始说话
|
const options = {
|
duration: 60000,
|
sampleRate: 16000,
|
numberOfChannels: 1,
|
encodeBitRate: 96000,
|
format: 'pcm',
|
};
|
that.recordMannager.start(options);
|
that.recordMannager.onStart(() => console.log('开始录音'));
|
that.recordMannager.onError((e) => {
|
console.log('onError', e);
|
$$.showToast({
|
title: '抱歉!录音时间过短,请重新录入',
|
});
|
that.setData({
|
second: 60,
|
showModal: false,
|
});
|
});
|
},
|
});
|
},
|
|
touchEnd() {
|
let that = this;
|
that.recordMannager.onStop((e) => that._endRecord(e));
|
that.recordMannager.stop();
|
that.setData({
|
showModal: false,
|
});
|
console.log('结束录音');
|
},
|
|
// 语音转文字
|
_transferText(e) {
|
console.log('开始识别', e);
|
$$.showLoading();
|
let speakUrl = e.tempFilePath;
|
let that = this;
|
wx.uploadFile({
|
url: `${$$.baseUrl}${$$.url.sys}/api/wechat/xfyun/speech`,
|
filePath: speakUrl,
|
name: 'fileNames',
|
header: {
|
Authorization: app.globalData.token,
|
},
|
complete(res) {
|
$$.hideLoading();
|
if (res.errMsg === 'uploadFile:ok') {
|
const { code, data, msg } = JSON.parse(res.data);
|
if (code === '0' || code === 0) {
|
that.setData({
|
inputValue: that.data.inputValue + data || '',
|
// number: (that.data.value + data || '').length,
|
});
|
} else {
|
$$.showToast({
|
icon: 'error',
|
title: msg,
|
});
|
}
|
} else {
|
$$.showToast({
|
icon: 'error',
|
title: '录音转写失败',
|
});
|
}
|
},
|
});
|
},
|
|
// 返回上一页
|
handleBack() {
|
wx.navigateBack();
|
},
|
|
onShow() {
|
this.getUserInfo();
|
// 添加欢迎消息,使用逐字显示效果
|
setTimeout(() => {
|
this.startTyping('您好,我是解纷数智人,有什么可以帮您?');
|
}, 500);
|
},
|
|
onUnload() {
|
// 清除定时器
|
if (this.data.typingTimer) {
|
clearInterval(this.data.typingTimer);
|
}
|
},
|
|
// 处理快捷按钮点击
|
async handleQuickButton(e) {
|
const { type } = e.currentTarget.dataset;
|
let message = '';
|
|
// 清空相关数据
|
this.setData({
|
caseData: [],
|
AIData: [],
|
chatList: [],
|
});
|
|
switch (type) {
|
case 'case':
|
message = '请帮我推荐一些类似的案例';
|
break;
|
case 'law':
|
message = '请提供相关的法律条文';
|
break;
|
case 'strategy':
|
message = '请给出调解策略建议';
|
break;
|
}
|
|
// 添加用户消息
|
this.addMessage('user', message);
|
|
try {
|
// 模拟AI回复
|
if (type === 'case') {
|
const res = await getCaseApi({
|
caseClaim: this.data?.submitData?.caseClaim,
|
caseDes: this.data?.submitData?.caseDes,
|
caseId: this.data?.caseId,
|
});
|
if (res.type) {
|
if (!res.data || res.data.length === 0) {
|
this.startTyping('抱歉,未找到相似的案例。');
|
return;
|
}
|
// 先显示正在分析的提示
|
await this.startTypingPromise('正在为您分析相似案例...\n\n');
|
// 设置数据并逐字显示
|
this.setData({
|
caseData: res.data,
|
});
|
// 构建要显示的文本
|
const caseText = `为您找到${res.data.length}个相似案例,请点击查看详情。`;
|
await this.startTypingPromise(caseText);
|
} else {
|
this.startTyping('抱歉,获取案例信息失败,请稍后重试。');
|
}
|
}
|
|
if (type === 'law') {
|
const res = await getawApi({
|
caseId: this.data.caseId,
|
});
|
if (res.type) {
|
if (!res.data || res.data.length === 0) {
|
this.startTyping('抱歉,未找到相关的法律条文。');
|
return;
|
}
|
// 先显示正在分析的提示
|
await this.startTypingPromise('正在为您查找相关法条...\n\n');
|
// 设置数据并逐字显示
|
this.setData({
|
AIData: res.data,
|
});
|
// 构建要显示的文本
|
const lawText = `为您找到${res.data.length}条相关法律条文,请展开查看详情。`;
|
await this.startTypingPromise(lawText);
|
} else {
|
this.startTyping('抱歉,获取法律条文失败,请稍后重试。');
|
}
|
}
|
|
if (type === 'strategy') {
|
// 先展示提示信息
|
await this.startTypingPromise('正在为您分析调解策略...\n\n');
|
|
// 调用接口获取数据
|
const res = await getMediateStrategypi({
|
caseText: this.data?.submitData?.caseDes,
|
// caseId: this.data.caseId,
|
});
|
|
// 展示接口返回的数据
|
if (res.type) {
|
// 确保上一条消息已完全展示后再显示新消息
|
setTimeout(() => {
|
this.startTyping(res.data || '');
|
}, 500);
|
} else {
|
setTimeout(() => {
|
this.startTyping('抱歉,获取调解策略失败,请稍后重试。');
|
}, 500);
|
}
|
}
|
} catch (error) {
|
console.error('请求失败:', error);
|
this.startTyping('抱歉,服务器出现异常,请稍后重试。');
|
}
|
},
|
|
// 返回Promise的逐字显示方法
|
startTypingPromise(content) {
|
return new Promise((resolve) => {
|
// 清除之前的定时器
|
if (this.data.typingTimer) {
|
clearInterval(this.data.typingTimer);
|
}
|
|
// 设置正在打字状态
|
this.setData({
|
isTyping: true,
|
});
|
|
// 添加一条空消息
|
const newMessage = {
|
type: 'ai',
|
content: '',
|
time: new Date().getTime(),
|
};
|
|
// 只保留最新的两条消息
|
const chatList = [...this.data.chatList, newMessage].slice(-2);
|
this.setData({
|
chatList,
|
typingMessage: content,
|
typingIndex: 0,
|
});
|
|
// 开始逐字显示
|
const timer = setInterval(() => {
|
const { typingIndex, typingMessage, chatList } = this.data;
|
if (typingIndex < typingMessage.length) {
|
const newContent = typingMessage.substring(0, typingIndex + 1);
|
// 更新最后一条消息的内容
|
const updatedChatList = [...chatList];
|
updatedChatList[updatedChatList.length - 1].content = newContent;
|
this.setData({
|
chatList: updatedChatList,
|
typingIndex: typingIndex + 1,
|
});
|
|
// 减少滚动频率,只在段落结束或每30个字符时滚动一次
|
if (typingIndex % 30 === 0 || typingMessage.charAt(typingIndex) === '\n' || typingIndex === typingMessage.length - 1) {
|
this.scrollToBottom(false);
|
}
|
} else {
|
clearInterval(timer);
|
this.setData({
|
typingMessage: null,
|
typingIndex: 0,
|
typingTimer: null,
|
isTyping: false,
|
});
|
|
// 完成打字后滚动到底部(使用平滑滚动)
|
this.scrollToBottom(true);
|
resolve(); // 完成打字效果后解析Promise
|
}
|
}, 50);
|
|
this.setData({
|
typingTimer: timer,
|
});
|
});
|
},
|
|
// 打开折叠法条
|
lawClick(e) {
|
let item = e.currentTarget.dataset.item;
|
let index = e.currentTarget.dataset.index;
|
this.setData({
|
AIData: this.data.AIData.map((i, idx) => ({
|
...i,
|
show: idx === index ? (i.show ? false : true) : false,
|
})),
|
});
|
},
|
|
// 跳转案例详情
|
caseClick(e) {
|
let url = e.currentTarget.dataset.url;
|
let caseId = e.currentTarget.dataset.caseid;
|
let caseType = e.currentTarget.dataset.casetype;
|
let caseName = e.currentTarget.dataset.casename;
|
|
wx.navigateTo({
|
url: url + '?caseId=' + caseId + '&type=' + caseType + '&caseName=' + caseName,
|
});
|
},
|
|
// 切换卡片展开/收起状态
|
toggleCard() {
|
this.setData({
|
isCardExpanded: !this.data.isCardExpanded,
|
});
|
},
|
});
|