|
@@ -0,0 +1,372 @@
|
|
|
|
|
+// pages/chat/chat.js
|
|
|
|
|
+const chatApi = require('../../api/chat');
|
|
|
|
|
+const userUtil = require('../../utils/user');
|
|
|
|
|
+
|
|
|
|
|
+Page({
|
|
|
|
|
+ data: {
|
|
|
|
|
+ userId: null,
|
|
|
|
|
+ sessionId: null,
|
|
|
|
|
+ messages: [],
|
|
|
|
|
+ inputMessage: '',
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ sending: false,
|
|
|
|
|
+ scrollIntoView: '' // 用于scroll-view滚动到底部
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ onLoad(options) {
|
|
|
|
|
+ console.log('聊天页面加载, options:', options);
|
|
|
|
|
+
|
|
|
|
|
+ const userInfo = userUtil.getUserInfo();
|
|
|
|
|
+ console.log('用户信息:', userInfo);
|
|
|
|
|
+
|
|
|
|
|
+ if (!userInfo || !userInfo.userId) {
|
|
|
|
|
+ console.error('用户未登录或userId为空');
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '请先登录',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ wx.navigateBack();
|
|
|
|
|
+ }, 1500);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log('设置userId:', userInfo.userId);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ userId: userInfo.userId
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用传入的sessionId(如果有)
|
|
|
|
|
+ if (options.sessionId) {
|
|
|
|
|
+ console.log('使用传入的sessionId:', options.sessionId);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ sessionId: options.sessionId
|
|
|
|
|
+ });
|
|
|
|
|
+ // 保存sessionId到本地存储
|
|
|
|
|
+ this.saveSessionId(options.sessionId);
|
|
|
|
|
+ this.loadMessageHistory();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 尝试从本地存储加载sessionId
|
|
|
|
|
+ const savedSessionId = this.getSavedSessionId(userInfo.userId);
|
|
|
|
|
+ if (savedSessionId) {
|
|
|
|
|
+ console.log('从本地存储加载sessionId:', savedSessionId);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ sessionId: savedSessionId
|
|
|
|
|
+ });
|
|
|
|
|
+ this.loadMessageHistory();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 创建新会话
|
|
|
|
|
+ console.log('创建新会话');
|
|
|
|
|
+ this.createSession();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ onShow() {
|
|
|
|
|
+ // 检查用户是否仍然登录
|
|
|
|
|
+ const userInfo = userUtil.getUserInfo();
|
|
|
|
|
+ if (!userInfo || !userInfo.userId) {
|
|
|
|
|
+ // 用户已退出登录,清除会话数据
|
|
|
|
|
+ if (this.data.sessionId) {
|
|
|
|
|
+ this.clearSavedSessionId(this.data.userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果userId发生变化(可能是切换了账号),重新加载
|
|
|
|
|
+ if (this.data.userId && userInfo.userId !== this.data.userId) {
|
|
|
|
|
+ console.log('检测到用户切换,重新加载会话');
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ userId: userInfo.userId,
|
|
|
|
|
+ sessionId: null,
|
|
|
|
|
+ messages: []
|
|
|
|
|
+ });
|
|
|
|
|
+ const savedSessionId = this.getSavedSessionId(userInfo.userId);
|
|
|
|
|
+ if (savedSessionId) {
|
|
|
|
|
+ this.setData({ sessionId: savedSessionId });
|
|
|
|
|
+ this.loadMessageHistory();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.createSession();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 创建新会话
|
|
|
|
|
+ async createSession() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ console.log('开始创建会话, userId:', this.data.userId);
|
|
|
|
|
+ if (!this.data.userId) {
|
|
|
|
|
+ throw new Error('userId为空,请先登录');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({ loading: true });
|
|
|
|
|
+ const session = await chatApi.createSession(this.data.userId);
|
|
|
|
|
+ console.log('创建会话成功:', session);
|
|
|
|
|
+
|
|
|
|
|
+ if (!session || !session.sessionId) {
|
|
|
|
|
+ throw new Error('创建会话失败,未返回sessionId');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 保存sessionId到本地存储
|
|
|
|
|
+ this.saveSessionId(session.sessionId);
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ sessionId: session.sessionId,
|
|
|
|
|
+ messages: [],
|
|
|
|
|
+ loading: false
|
|
|
|
|
+ });
|
|
|
|
|
+ console.log('会话创建完成, sessionId:', session.sessionId);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('创建会话失败:', error);
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '创建会话失败: ' + (error.message || error),
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ duration: 2000
|
|
|
|
|
+ });
|
|
|
|
|
+ this.setData({ loading: false });
|
|
|
|
|
+ throw error; // 重新抛出错误,让调用方知道失败
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 保存sessionId到本地存储
|
|
|
|
|
+ saveSessionId(sessionId) {
|
|
|
|
|
+ if (!this.data.userId || !sessionId) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const key = `chat_sessionId_${this.data.userId}`;
|
|
|
|
|
+ wx.setStorageSync(key, sessionId);
|
|
|
|
|
+ console.log('保存sessionId到本地存储:', key, sessionId);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 从本地存储获取sessionId
|
|
|
|
|
+ getSavedSessionId(userId) {
|
|
|
|
|
+ if (!userId) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ const key = `chat_sessionId_${userId}`;
|
|
|
|
|
+ const sessionId = wx.getStorageSync(key);
|
|
|
|
|
+ console.log('从本地存储获取sessionId:', key, sessionId);
|
|
|
|
|
+ return sessionId || null;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 清除本地存储的sessionId
|
|
|
|
|
+ clearSavedSessionId(userId) {
|
|
|
|
|
+ if (!userId) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const key = `chat_sessionId_${userId}`;
|
|
|
|
|
+ wx.removeStorageSync(key);
|
|
|
|
|
+ console.log('清除本地存储的sessionId:', key);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 加载消息历史
|
|
|
|
|
+ async loadMessageHistory() {
|
|
|
|
|
+ if (!this.data.sessionId || !this.data.userId) {
|
|
|
|
|
+ console.warn('无法加载消息历史:sessionId或userId为空');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.setData({ loading: true });
|
|
|
|
|
+ console.log('加载消息历史:', this.data.sessionId, this.data.userId);
|
|
|
|
|
+ const messages = await chatApi.getMessageHistory(this.data.sessionId, this.data.userId);
|
|
|
|
|
+ console.log('收到消息历史:', messages);
|
|
|
|
|
+
|
|
|
|
|
+ // 确保消息格式正确(处理时间戳)
|
|
|
|
|
+ const formattedMessages = (messages || []).map(msg => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ role: msg.role || 'user',
|
|
|
|
|
+ content: msg.content || '',
|
|
|
|
|
+ timestamp: msg.timestamp || new Date().toISOString()
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ messages: formattedMessages,
|
|
|
|
|
+ loading: false
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 延迟滚动到底部,确保DOM已更新
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.scrollToBottom();
|
|
|
|
|
+ }, 200);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载消息历史失败:', error);
|
|
|
|
|
+ // 如果是会话不存在或无权访问,清除本地保存的sessionId
|
|
|
|
|
+ if (error.message && (error.message.includes('不存在') || error.message.includes('无权访问'))) {
|
|
|
|
|
+ console.log('会话无效,清除本地保存的sessionId');
|
|
|
|
|
+ this.clearSavedSessionId(this.data.userId);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ sessionId: null,
|
|
|
|
|
+ messages: []
|
|
|
|
|
+ });
|
|
|
|
|
+ // 创建新会话
|
|
|
|
|
+ this.createSession();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '加载消息失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ this.setData({ loading: false });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 输入消息
|
|
|
|
|
+ onInput(e) {
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ inputMessage: e.detail.value
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 发送消息
|
|
|
|
|
+ async sendMessage() {
|
|
|
|
|
+ const message = this.data.inputMessage.trim();
|
|
|
|
|
+ if (!message) {
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '请输入消息',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查userId
|
|
|
|
|
+ if (!this.data.userId) {
|
|
|
|
|
+ console.error('userId为空,请先登录');
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '请先登录',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有sessionId,先创建会话
|
|
|
|
|
+ if (!this.data.sessionId) {
|
|
|
|
|
+ console.log('没有sessionId,创建新会话');
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createSession();
|
|
|
|
|
+ if (!this.data.sessionId) {
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '创建会话失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('创建会话失败:', error);
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '创建会话失败: ' + error,
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ duration: 2000
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ sending: true,
|
|
|
|
|
+ inputMessage: ''
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.log('开始发送消息:', {
|
|
|
|
|
+ sessionId: this.data.sessionId,
|
|
|
|
|
+ userId: this.data.userId,
|
|
|
|
|
+ message: message
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 添加用户消息到列表
|
|
|
|
|
+ const userMessage = {
|
|
|
|
|
+ role: 'user',
|
|
|
|
|
+ content: message,
|
|
|
|
|
+ timestamp: new Date().toISOString()
|
|
|
|
|
+ };
|
|
|
|
|
+ const currentMessages = [...this.data.messages, userMessage];
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ messages: currentMessages
|
|
|
|
|
+ });
|
|
|
|
|
+ this.scrollToBottom();
|
|
|
|
|
+
|
|
|
|
|
+ // 发送消息到服务器
|
|
|
|
|
+ console.log('调用API发送消息...');
|
|
|
|
|
+ const response = await chatApi.sendMessage(this.data.sessionId, message, this.data.userId);
|
|
|
|
|
+ console.log('收到API响应:', response);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新会话ID(如果是新创建的)
|
|
|
|
|
+ if (response.session && response.session.sessionId) {
|
|
|
|
|
+ const newSessionId = response.session.sessionId;
|
|
|
|
|
+ console.log('更新sessionId:', newSessionId);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ sessionId: newSessionId
|
|
|
|
|
+ });
|
|
|
|
|
+ // 保存新的sessionId到本地存储
|
|
|
|
|
+ this.saveSessionId(newSessionId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 添加AI回复到列表
|
|
|
|
|
+ if (response.assistantMessage) {
|
|
|
|
|
+ console.log('收到AI回复:', response.assistantMessage.content);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ messages: [...currentMessages, response.assistantMessage]
|
|
|
|
|
+ });
|
|
|
|
|
+ this.scrollToBottom();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('未收到AI回复');
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '未收到AI回复',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({ sending: false });
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('发送消息失败:', error);
|
|
|
|
|
+ console.error('错误详情:', JSON.stringify(error));
|
|
|
|
|
+ wx.showToast({
|
|
|
|
|
+ title: '发送失败: ' + (error.message || error || '未知错误'),
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ duration: 3000
|
|
|
|
|
+ });
|
|
|
|
|
+ // 移除用户消息(发送失败)
|
|
|
|
|
+ const messages = this.data.messages.filter(m => m.content !== message || m.role !== 'user');
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ messages: messages,
|
|
|
|
|
+ sending: false,
|
|
|
|
|
+ inputMessage: message
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 滚动到底部
|
|
|
|
|
+ scrollToBottom() {
|
|
|
|
|
+ // 使用scroll-view的scroll-into-view属性滚动到底部
|
|
|
|
|
+ if (this.data.messages && this.data.messages.length > 0) {
|
|
|
|
|
+ const lastIndex = this.data.messages.length - 1;
|
|
|
|
|
+ const scrollIntoView = `msg-${lastIndex}`;
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ scrollIntoView: scrollIntoView
|
|
|
|
|
+ });
|
|
|
|
|
+ // 清除scrollIntoView,以便下次可以再次触发
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ scrollIntoView: ''
|
|
|
|
|
+ });
|
|
|
|
|
+ }, 300);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 格式化时间
|
|
|
|
|
+ formatTime(date) {
|
|
|
|
|
+ if (!date) return '';
|
|
|
|
|
+ try {
|
|
|
|
|
+ const d = typeof date === 'string' ? new Date(date) : date;
|
|
|
|
|
+ if (isNaN(d.getTime())) return '';
|
|
|
|
|
+ const hours = d.getHours().toString().padStart(2, '0');
|
|
|
|
|
+ const minutes = d.getMinutes().toString().padStart(2, '0');
|
|
|
|
|
+ return `${hours}:${minutes}`;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|