在电脑端使用 localStorage 模拟企微环境,测试会话激活功能的完整流程。
cd yss-project
npm install  # 如果还没安装依赖
ng serve
服务器启动后,访问:http://localhost:4200
按 F12 或右键 → 检查,打开开发者工具,切换到 Console 标签页。
在控制台中依次执行以下代码:
// ========== 1. 设置公司ID ==========
localStorage.setItem('company', 'test-company-001');
// ========== 2. 设置当前用户 ==========
const mockUser = {
  objectId: 'user-001',
  id: 'user-001',
  userid: 'wxwork-user-001',
  name: '测试技术员',
  realName: '张三',
  roleName: '技术',
  department: {
    __type: 'Pointer',
    className: 'Department',
    objectId: 'dept-001'
  },
  company: {
    __type: 'Pointer',
    className: 'Company',
    objectId: 'test-company-001'
  }
};
localStorage.setItem('currentUser', JSON.stringify(mockUser));
// ========== 3. 设置项目数据 ==========
const mockProject = {
  objectId: 'project-001',
  id: 'project-001',
  title: '测试项目 - 现代简约风格装修',
  description: '客厅、卧室、厨房三室一厅装修,预算15-20万',
  status: '进行中',
  contact: {
    __type: 'Pointer',
    className: 'ContactInfo',
    objectId: 'contact-001'
  },
  assignee: {
    __type: 'Pointer',
    className: 'Profile',
    objectId: 'user-001'
  },
  department: {
    __type: 'Pointer',
    className: 'Department',
    objectId: 'dept-001'
  }
};
localStorage.setItem('mockProject', JSON.stringify(mockProject));
// ========== 4. 设置客户数据 ==========
const mockContact = {
  objectId: 'contact-001',
  id: 'contact-001',
  name: '李女士',
  external_userid: 'external-user-001',
  mobile: '138****8888',
  company: 'test-company-001',
  data: {
    avatar: 'https://via.placeholder.com/100',
    wechat: 'lixiaojie123',
    tags: {
      preference: '现代简约',
      budget: { min: 150000, max: 200000 },
      colorAtmosphere: '暖色调'
    }
  }
};
localStorage.setItem('mockContact', JSON.stringify(mockContact));
// ========== 5. 设置群聊数据 ==========
const mockGroupChat = {
  objectId: 'groupchat-001',
  id: 'groupchat-001',
  chat_id: 'wrkSFfCgAAXXXXXXXXXXXXXXXXXXXX',
  name: '【李女士】现代简约装修项目群',
  company: 'test-company-001',
  project: {
    __type: 'Pointer',
    className: 'Project',
    objectId: 'project-001'
  },
  introSent: false,
  introSentAt: null,
  joinQrcode: {
    qr_code: 'https://via.placeholder.com/300?text=QR+Code'
  },
  joinUrl: {
    join_url: 'https://work.weixin.qq.com/ca/cawcde123456'
  },
  member_list: [
    {
      userid: 'wxwork-user-001',
      type: 1,
      name: '张三',
      invitor: {
        userid: 'admin-001'
      }
    },
    {
      userid: 'external-user-001',
      type: 2,
      name: '李女士',
      invitor: {
        userid: 'wxwork-user-001'
      }
    },
    {
      userid: 'wxwork-user-002',
      type: 1,
      name: '王组长',
      invitor: {
        userid: 'admin-001'
      }
    }
  ],
  messages: [
    {
      msgid: 'msg-001',
      from: 'external-user-001',
      msgtime: Math.floor(Date.now() / 1000) - 3600,
      msgtype: 'text',
      text: {
        content: '你好,我想了解一下项目的进度'
      }
    },
    {
      msgid: 'msg-002',
      from: 'wxwork-user-001',
      msgtime: Math.floor(Date.now() / 1000) - 3500,
      msgtype: 'text',
      text: {
        content: '您好李女士,目前我们正在进行方案设计,预计明天可以给您看初稿'
      }
    },
    {
      msgid: 'msg-003',
      from: 'external-user-001',
      msgtime: Math.floor(Date.now() / 1000) - 3400,
      msgtype: 'text',
      text: {
        content: '好的,那我等你们的消息'
      }
    },
    {
      msgid: 'msg-004',
      from: 'external-user-001',
      msgtime: Math.floor(Date.now() / 1000) - 700,
      msgtype: 'text',
      text: {
        content: '对了,我想把客厅的颜色改成浅灰色,可以吗?'
      }
    },
    {
      msgid: 'msg-005',
      from: 'external-user-001',
      msgtime: Math.floor(Date.now() / 1000) - 650,
      msgtype: 'text',
      text: {
        content: '还有厨房的橱柜我想换个品牌'
      }
    }
  ]
};
localStorage.setItem('mockGroupChat', JSON.stringify(mockGroupChat));
// ========== 6. 设置部门数据 ==========
const mockDepartment = {
  objectId: 'dept-001',
  id: 'dept-001',
  name: '设计部',
  leader: {
    __type: 'Pointer',
    className: 'Profile',
    objectId: 'leader-001'
  }
};
localStorage.setItem('mockDepartment', JSON.stringify(mockDepartment));
// ========== 7. 设置组长数据 ==========
const mockLeader = {
  objectId: 'leader-001',
  id: 'leader-001',
  name: '王组长',
  userid: 'wxwork-user-002',
  roleName: '组长'
};
localStorage.setItem('mockLeader', JSON.stringify(mockLeader));
console.log('✅ 所有模拟数据已设置完成!');
console.log('📝 数据清单:');
console.log('- 公司ID:', localStorage.getItem('company'));
console.log('- 当前用户:', JSON.parse(localStorage.getItem('currentUser')).name);
console.log('- 项目:', JSON.parse(localStorage.getItem('mockProject')).title);
console.log('- 客户:', JSON.parse(localStorage.getItem('mockContact')).name);
console.log('- 群聊:', JSON.parse(localStorage.getItem('mockGroupChat')).name);
console.log('- 消息数量:', JSON.parse(localStorage.getItem('mockGroupChat')).messages.length);
chat-activation.component.ts在组件的 loadData() 方法中添加 localStorage 支持:
async loadData() {
  try {
    this.loading = true;
    
    // ========== 开发环境:使用 localStorage ==========
    if (!this.wxwork && typeof window !== 'undefined') {
      console.log('🔧 开发模式:使用 localStorage 模拟数据');
      
      // 1. 加载当前用户
      const userStr = localStorage.getItem('currentUser');
      if (userStr) {
        this.currentUser = JSON.parse(userStr) as any;
        console.log('✅ 用户加载成功:', this.currentUser.name);
      }
      
      // 2. 加载项目
      const projectStr = localStorage.getItem('mockProject');
      if (projectStr) {
        this.project = JSON.parse(projectStr) as any;
        console.log('✅ 项目加载成功:', this.project.title);
      }
      
      // 3. 加载客户
      const contactStr = localStorage.getItem('mockContact');
      if (contactStr) {
        this.contact = JSON.parse(contactStr) as any;
        console.log('✅ 客户加载成功:', this.contact.name);
      }
      
      // 4. 加载群聊
      const groupChatStr = localStorage.getItem('mockGroupChat');
      if (groupChatStr) {
        this.groupChat = JSON.parse(groupChatStr) as any;
        this.chatId = this.groupChat.chat_id;
        this.introSent = this.groupChat.introSent || false;
        console.log('✅ 群聊加载成功:', this.groupChat.name);
        
        // 加载入群方式
        this.joinMethods.qrCode = this.groupChat.joinQrcode?.qr_code || '';
        this.joinMethods.link = this.groupChat.joinUrl?.join_url || '';
      }
      
      // 5. 加载部门和组长
      const deptStr = localStorage.getItem('mockDepartment');
      const leaderStr = localStorage.getItem('mockLeader');
      if (deptStr && leaderStr && this.project) {
        const dept = JSON.parse(deptStr);
        const leader = JSON.parse(leaderStr);
        this.project.department = dept;
        this.project.department.leader = leader;
        console.log('✅ 部门和组长加载成功');
      }
      
      // 6. 生成介绍文案
      this.generateIntroTemplate();
      
      // 7. 加载消息(使用 mock 数据)
      await this.loadChatMessagesFromLocalStorage();
      
      this.loading = false;
      this.cdr.markForCheck();
      return;
    }
    
    // ========== 生产环境:正常流程 ==========
    // ... 原有代码保持不变
    
  } catch (error) {
    console.error('❌ 加载数据失败:', error);
    this.error = error.message || '加载失败';
  } finally {
    this.loading = false;
  }
}
// 新增:从 localStorage 加载消息
async loadChatMessagesFromLocalStorage() {
  try {
    this.loadingMessages = true;
    
    if (!this.groupChat) {
      this.messages = [];
      this.updateStatistics();
      return;
    }
    
    const messagesData = this.groupChat.messages || [];
    const memberList = this.groupChat.member_list || [];
    const customerUserId = this.contact?.external_userid || '';
    
    // 转换为 ChatMessage 格式
    this.messages = messagesData.map((msg: any, index: number) => {
      const isCustomer = msg.from === customerUserId || 
                        memberList.some((m: any) => 
                          m.type === 2 && m.userid === msg.from
                        );
      
      const msgTime = new Date(msg.msgtime * 1000);
      const needsReply = isCustomer && this.checkNeedsReply(msg, messagesData, index);
      
      return {
        id: msg.msgid || `msg-${index}`,
        senderName: this.getSenderName(msg.from, memberList),
        senderUserId: msg.from,
        content: this.getMessageContent(msg),
        time: msgTime,
        isCustomer,
        needsReply,
        msgType: msg.msgtype
      };
    }).sort((a: ChatMessage, b: ChatMessage) => b.time.getTime() - a.time.getTime());
    
    this.updateStatistics();
    this.applyFilters();
    
    console.log('✅ 消息加载完成:', {
      总消息数: this.totalMessages,
      客户消息: this.customerMessageCount,
      未回复: this.unreadCount
    });
    
  } catch (error) {
    console.error('❌ 加载消息失败:', error);
  } finally {
    this.loadingMessages = false;
    this.cdr.markForCheck();
  }
}
async sendGroupIntro() {
  try {
    if (!this.chatId) {
      window?.fmode?.alert('群聊信息不完整');
      return;
    }
    
    this.sendingIntro = true;
    
    // ========== 开发环境:模拟发送 ==========
    if (!this.wecorp) {
      console.log('🔧 开发模式:模拟发送群介绍');
      console.log('📝 文案内容:', this.introTemplate);
      
      // 模拟延迟
      await new Promise(resolve => setTimeout(resolve, 1000));
      
      // 更新 localStorage
      if (this.groupChat) {
        this.groupChat.introSent = true;
        this.groupChat.introSentAt = new Date();
        localStorage.setItem('mockGroupChat', JSON.stringify(this.groupChat));
        this.introSent = true;
      }
      
      alert('✅ 群介绍已发送(模拟)!');
      this.sendingIntro = false;
      this.cdr.markForCheck();
      return;
    }
    
    // ========== 生产环境:实际发送 ==========
    // @ts-ignore - 企微API类型定义问题
    await this.wecorp.message.send({
      chatid: this.chatId,
      msgtype: 'text',
      text: {
        content: this.introTemplate
      }
    });
    
    // 更新数据库标记
    if (this.groupChat) {
      this.groupChat.set('introSent', true);
      this.groupChat.set('introSentAt', new Date());
      await this.groupChat.save();
      this.introSent = true;
    }
    
    window?.fmode?.alert('群介绍已发送!');
    
  } catch (error) {
    console.error('发送群介绍失败:', error);
    window?.fmode?.alert('发送失败,请重试');
  } finally {
    this.sendingIntro = false;
    this.cdr.markForCheck();
  }
}
async sendSuggestedReply(reply: SuggestedReply) {
  try {
    if (!this.chatId) {
      window?.fmode?.alert('无法发送消息');
      return;
    }
    
    // ========== 开发环境:模拟发送 ==========
    if (!this.wecorp) {
      console.log('🔧 开发模式:模拟发送回复');
      console.log('📝 回复内容:', reply.text);
      
      // 模拟延迟
      await new Promise(resolve => setTimeout(resolve, 500));
      
      // 添加新消息到 localStorage
      const groupChatStr = localStorage.getItem('mockGroupChat');
      if (groupChatStr) {
        const groupChat = JSON.parse(groupChatStr);
        const newMessage = {
          msgid: `msg-${Date.now()}`,
          from: this.currentUser?.userid || 'wxwork-user-001',
          msgtime: Math.floor(Date.now() / 1000),
          msgtype: 'text',
          text: {
            content: reply.text
          }
        };
        groupChat.messages.push(newMessage);
        localStorage.setItem('mockGroupChat', JSON.stringify(groupChat));
        this.groupChat = groupChat;
      }
      
      alert('✅ 消息已发送(模拟)!');
      
      // 关闭建议面板
      this.showSuggestions = false;
      this.selectedMessage = null;
      
      // 刷新消息列表
      await this.loadChatMessagesFromLocalStorage();
      
      return;
    }
    
    // ========== 生产环境:实际发送 ==========
    // @ts-ignore - 企微API类型定义问题
    await this.wecorp.message.send({
      chatid: this.chatId,
      msgtype: 'text',
      text: {
        content: reply.text
      }
    });
    
    window?.fmode?.alert('消息已发送!');
    
    // 关闭建议面板
    this.showSuggestions = false;
    this.selectedMessage = null;
    
    // 刷新消息列表
    await this.loadChatMessages();
    
  } catch (error) {
    console.error('发送消息失败:', error);
    window?.fmode?.alert('发送失败,请重试');
  }
}
在浏览器中访问:
http://localhost:4200/wxwork/test-company-001/project/project-001/chat-activation
或者如果有查询参数:
http://localhost:4200/wxwork/test-company-001/project/project-001/chat-activation?chatId=wrkSFfCgAAXXXXXXXXXXXXXXXXXXXX
页面加载
入群方式卡片
群介绍文案
消息列表
筛选功能
未回复提醒
F12 打开开发者工具测试过程中,控制台会输出详细日志:
🔧 开发模式:使用 localStorage 模拟数据
✅ 用户加载成功: 张三
✅ 项目加载成功: 测试项目 - 现代简约风格装修
✅ 客户加载成功: 李女士
✅ 群聊加载成功: 【李女士】现代简约装修项目群
✅ 部门和组长加载成功
✅ 消息加载完成: {总消息数: 5, 客户消息: 3, 未回复: 2}
修改消息时间为10分钟前:
const groupChat = JSON.parse(localStorage.getItem('mockGroupChat'));
// 将最后一条客户消息改为15分钟前
groupChat.messages[3].msgtime = Math.floor(Date.now() / 1000) - 900; // 15分钟前
groupChat.messages[4].msgtime = Math.floor(Date.now() / 1000) - 850; // 14分钟前
localStorage.setItem('mockGroupChat', JSON.stringify(groupChat));
// 刷新页面
location.reload();
预期效果:
const groupChat = JSON.parse(localStorage.getItem('mockGroupChat'));
groupChat.introSent = true;
groupChat.introSentAt = new Date().toISOString();
localStorage.setItem('mockGroupChat', JSON.stringify(groupChat));
location.reload();
预期效果:
const groupChat = JSON.parse(localStorage.getItem('mockGroupChat'));
// 添加更多消息
for (let i = 0; i < 10; i++) {
  groupChat.messages.push({
    msgid: `msg-extra-${i}`,
    from: i % 2 === 0 ? 'external-user-001' : 'wxwork-user-001',
    msgtime: Math.floor(Date.now() / 1000) - (600 - i * 50),
    msgtype: 'text',
    text: {
      content: i % 2 === 0 ? `客户消息 ${i}` : `技术回复 ${i}`
    }
  });
}
localStorage.setItem('mockGroupChat', JSON.stringify(groupChat));
location.reload();
预期效果:
解决方法:
// 检查 localStorage 数据
console.log('Company:', localStorage.getItem('company'));
console.log('User:', localStorage.getItem('currentUser'));
console.log('Project:', localStorage.getItem('mockProject'));
console.log('GroupChat:', localStorage.getItem('mockGroupChat'));
// 如果数据不存在,重新执行步骤1.2的代码
解决方法:
// 检查群聊消息
const groupChat = JSON.parse(localStorage.getItem('mockGroupChat'));
console.log('Messages:', groupChat.messages);
console.log('Messages count:', groupChat.messages.length);
// 如果为空,重新设置
groupChat.messages = [/* 复制步骤1.2中的消息数据 */];
localStorage.setItem('mockGroupChat', JSON.stringify(groupChat));
location.reload();
解决方法:
解决方法:
// 检查是否进入开发模式
console.log('wxwork:', this.wxwork);
console.log('wecorp:', this.wecorp);
// 如果为 null,说明进入了开发模式
// 查看控制台是否有 "🔧 开发模式:..." 的日志
测试完成后,清除所有模拟数据:
// 清除所有测试数据
localStorage.removeItem('company');
localStorage.removeItem('currentUser');
localStorage.removeItem('mockProject');
localStorage.removeItem('mockContact');
localStorage.removeItem('mockGroupChat');
localStorage.removeItem('mockDepartment');
localStorage.removeItem('mockLeader');
console.log('✅ 所有测试数据已清除');
或者清除所有 localStorage:
localStorage.clear();
console.log('✅ localStorage 已完全清空');
测试时建议截图保存以下内容:
所有以下项目都通过即为测试完成:
# 会话激活功能测试报告
**测试时间**: 2025-11-01
**测试人员**: [你的名字]
**测试环境**: Chrome 浏览器 + localhost:4200
## 测试结果
### 1. 基础功能
- [ ] 页面加载: ✅ 通过 / ❌ 失败
- [ ] 入群方式: ✅ 通过 / ❌ 失败
- [ ] 群介绍: ✅ 通过 / ❌ 失败
### 2. 消息功能
- [ ] 消息列表: ✅ 通过 / ❌ 失败
- [ ] 筛选功能: ✅ 通过 / ❌ 失败
- [ ] 未回复提醒: ✅ 通过 / ❌ 失败
### 3. 辅助回复
- [ ] 快速回复: ✅ 通过 / ❌ 失败
- [ ] 发送消息: ✅ 通过 / ❌ 失败
### 4. 响应式
- [ ] 移动端布局: ✅ 通过 / ❌ 失败
## 发现的问题
1. [问题描述]
2. [问题描述]
## 改进建议
1. [建议内容]
2. [建议内容]
测试通过后,可以:
祝测试顺利! 🎉
如有问题,请查看控制台日志或联系开发团队。