员工扫码进入企微应用
↓
WxworkAuth.authenticateAndLogin()
↓
身份激活/Profile同步
↓
跳转到设计师工作台
↓
检查 Profile.surveyCompleted
↓
未完成 → 显示问卷引导弹窗 → 跳转问卷页面
↓
已完成 → 正常使用工作台
身份激活
src/app/pages/designer/dashboard/dashboard.ts - 工作台认证逻辑fmode-ng/core 的 WxworkAuth - 企微SDK问卷功能
src/modules/profile/pages/profile-survey/profile-survey.component.ts - 问卷组件src/app/pages/designer/dashboard/dashboard.ts - 问卷引导逻辑组长查看
src/app/pages/team-leader/dashboard/dashboard.ts - 组长端查看问卷| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 测试工具页面 | 本地开发调试 | 可视化、直观、易操作 | 需要编译运行 |
| Console调试 | 快速验证 | 最快速、无需额外代码 | 需要手动输入命令 |
| 真实企微环境 | 最终验证 | 最真实 | 需要配置外网域名 |
步骤1:启动开发服务器
ng serve
步骤2:访问测试页面
浏览器打开:
http://localhost:4200/test-wxwork-activation/test
其中 test 是公司ID(cid),可以替换为:
test - 测试公司demo - 演示公司测试页面提供以下功能:
✓ 1. 初始化企微认证
✓ 2. 执行身份激活
✓ 3. 检查问卷状态
✓ 4. 跳转到相应页面
10:30:15 获取公司ID: test
10:30:16 初始化企微认证...
10:30:17 ✅ 企微认证初始化成功
10:30:18 执行身份激活...
10:30:19 ✅ 身份激活成功: 王刚
10:30:20 角色: 组员
10:30:21 检查问卷状态...
10:30:22 ⚠️ 问卷未完成
10:30:23 测试完成!
员工ID: woAs2qCQAAPjkaSBZg3GVdXjIG3vxAOg
姓名: 王刚
角色: 组员
问卷状态: ❌ 未完成
1. 访问测试页面
2. 点击"开始测试"
3. 等待测试完成
4. 查看员工信息,确认问卷状态为"未完成"
5. 点击"前往工作台"
6. 验证是否显示问卷引导弹窗
1. 在测试页面点击"前往问卷页面"
2. 填写21个问题
3. 提交问卷
4. 返回测试页面
5. 点击"开始测试"刷新状态
6. 确认问卷状态变为"已完成"
1. 完成问卷后,点击"重置问卷状态"
2. 点击"前往工作台"
3. 验证是否再次显示问卷引导弹窗
打开任意页面,按 F12 打开Console,粘贴以下代码:
// 重置问卷状态函数
async function resetSurvey() {
// 动态加载Parse SDK
const Parse = (await import('https://api.fmode.cn/parse/sdk.js')).default;
Parse.initialize('nova');
Parse.serverURL = 'https://api.fmode.cn/parse';
// 获取当前Profile ID
const profileId = localStorage.getItem('Parse/ProfileId');
if (!profileId) {
console.error('❌ 未找到Profile ID,请先登录');
return;
}
// 查询并更新Profile
const Profile = Parse.Object.extend('Profile');
const query = new Parse.Query(Profile);
try {
const profile = await query.get(profileId);
console.log('当前问卷状态:', profile.get('surveyCompleted'));
// 重置为未完成
profile.set('surveyCompleted', false);
profile.unset('surveyCompletedAt');
profile.unset('surveyLogId');
await profile.save();
console.log('✅ 问卷状态已重置为未完成');
console.log('🔄 刷新页面以查看效果');
// 自动刷新
setTimeout(() => location.reload(), 1000);
} catch (error) {
console.error('❌ 重置失败:', error);
}
}
// 执行重置
resetSurvey();
async function checkSurveyStatus() {
const Parse = (await import('https://api.fmode.cn/parse/sdk.js')).default;
Parse.initialize('nova');
Parse.serverURL = 'https://api.fmode.cn/parse';
const profileId = localStorage.getItem('Parse/ProfileId');
if (!profileId) {
console.error('❌ 未找到Profile ID');
return;
}
const Profile = Parse.Object.extend('Profile');
const query = new Parse.Query(Profile);
try {
const profile = await query.get(profileId);
console.log('==== 员工信息 ====');
console.log('员工ID:', profile.id);
console.log('姓名:', profile.get('realname') || profile.get('name'));
console.log('角色:', profile.get('roleName'));
console.log('问卷状态:', profile.get('surveyCompleted') ? '✅ 已完成' : '❌ 未完成');
if (profile.get('surveyCompletedAt')) {
console.log('完成时间:', profile.get('surveyCompletedAt'));
}
// 如果已完成,查询问卷答案
if (profile.get('surveyCompleted')) {
const SurveyLog = Parse.Object.extend('SurveyLog');
const surveyQuery = new Parse.Query(SurveyLog);
surveyQuery.equalTo('profile', profile.toPointer());
surveyQuery.equalTo('type', 'survey-profile');
surveyQuery.descending('createdAt');
surveyQuery.limit(1);
const survey = await surveyQuery.first();
if (survey) {
console.log('==== 问卷答案 ====');
const answers = survey.get('answers') || [];
answers.forEach((answer, index) => {
console.log(`Q${index + 1}: ${answer.question}`);
console.log(`A${index + 1}:`, answer.answer);
console.log('---');
});
}
}
} catch (error) {
console.error('❌ 查询失败:', error);
}
}
checkSurveyStatus();
如果您在工作台页面,可以手动触发问卷引导弹窗:
// 在设计师工作台页面的Console执行
// 注意:这个方法需要页面已经加载完成
// 方法1:直接修改组件状态(仅演示用)
// 实际需要通过Angular的机制访问组件实例
// 方法2:直接跳转到问卷页面
const cid = location.pathname.split('/')[2] || 'test';
location.href = `/wxwork/${cid}/survey/profile`;
1. 使用内网穿透工具
由于企微需要访问公网域名,需要使用内网穿透:
# 安装 ngrok
brew install ngrok # macOS
# 或从 https://ngrok.com 下载
# 启动穿透
ngrok http 4200
会得到一个公网URL,例如:
https://abc123.ngrok.io
2. 配置企业微信应用
登录企业微信管理后台 (https://work.weixin.qq.com):
设置"应用主页":
https://abc123.ngrok.io/wxwork/你的公司ID
设置"可信域名":
abc123.ngrok.io
保存配置
步骤1:在手机企微中打开应用
打开企业微信 → 工作台 → 找到CRM应用 → 点击进入
步骤2:观察激活流程
在浏览器开发者工具 Network 标签中观察:
1. GET /wxwork/:cid/designer/dashboard
→ 加载工作台页面
2. POST /parse/functions/wxworkAuth
→ 执行企微认证
3. GET /parse/classes/Profile?where=...
→ 查询Profile
4. GET /parse/classes/Profile/:id
→ 检查问卷状态
步骤3:验证问卷引导
Profile.surveyCompleted 为 false,应该显示问卷引导弹窗/wxwork/:cid/survey/profile在企微内使用vConsole
在页面HTML的 <head> 中临时添加:
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
if (location.href.includes('wxwork')) {
new VConsole();
}
</script>
这样在手机上可以查看Console日志。
原因: WxworkAuth 初始化失败
解决方法:
cid 参数是否正确fmode-ng/core 已正确安装查看Console是否有详细错误信息
# 重新安装依赖
npm install fmode-ng@latest
原因:
解决方法:
test或demo)确认当前用户有查询权限
// Console中手动创建测试Profile
async function createTestProfile() {
const Parse = (await import('https://api.fmode.cn/parse/sdk.js')).default;
Parse.initialize('nova');
Parse.serverURL = 'https://api.fmode.cn/parse';
const Profile = Parse.Object.extend('Profile');
const profile = new Profile();
profile.set('name', '测试员工');
profile.set('realname', '王刚');
profile.set('roleName', '组员');
profile.set('company', {
__type: 'Pointer',
className: 'Company',
objectId: 'test_company_001'
});
profile.set('surveyCompleted', false);
const saved = await profile.save();
console.log('✅ 测试Profile已创建:', saved.id);
localStorage.setItem('Parse/ProfileId', saved.id);
return saved;
}
createTestProfile();
原因:
surveyCompleted 字段为 true解决方法:
// 1. 检查状态
checkSurveyStatus();
// 2. 如果状态错误,重置
resetSurvey();
// 3. 检查DOM
document.querySelector('.survey-guide-overlay');
// 应该返回弹窗元素,如果为null说明未渲染
原因:
解决方法:
// 在组长工作台Console执行
async function debugEmployeeSurvey(employeeName) {
const Parse = (await import('https://api.fmode.cn/parse/sdk.js')).default;
Parse.initialize('nova');
Parse.serverURL = 'https://api.fmode.cn/parse';
console.log('==== 调试员工问卷 ====');
console.log('查找员工:', employeeName);
// 1. 查找Profile
const Profile = Parse.Object.extend('Profile');
const profileQuery = new Parse.Query(Profile);
profileQuery.equalTo('realname', employeeName);
profileQuery.limit(1);
const profile = await profileQuery.first();
if (!profile) {
console.error('❌ 未找到员工Profile,请检查名字是否正确');
return;
}
console.log('✅ 找到Profile:', profile.id);
console.log(' 问卷状态:', profile.get('surveyCompleted'));
// 2. 查找SurveyLog
if (profile.get('surveyCompleted')) {
const SurveyLog = Parse.Object.extend('SurveyLog');
const surveyQuery = new Parse.Query(SurveyLog);
surveyQuery.equalTo('profile', profile.toPointer());
surveyQuery.equalTo('type', 'survey-profile');
surveyQuery.descending('createdAt');
surveyQuery.limit(1);
const survey = await surveyQuery.first();
if (survey) {
console.log('✅ 找到问卷记录:', survey.id);
console.log(' 答案数量:', survey.get('answers')?.length || 0);
console.log(' 提交时间:', survey.get('createdAt'));
} else {
console.error('❌ 未找到问卷记录');
}
}
}
// 使用:替换为实际员工名字
debugEmployeeSurvey('王刚');
原因:
解决方法:
确认Parse权限配置
// 手动测试提交问卷
async function testSubmitSurvey() {
const Parse = (await import('https://api.fmode.cn/parse/sdk.js')).default;
Parse.initialize('nova');
Parse.serverURL = 'https://api.fmode.cn/parse';
const profileId = localStorage.getItem('Parse/ProfileId');
const SurveyLog = Parse.Object.extend('SurveyLog');
const surveyLog = new SurveyLog();
const Profile = Parse.Object.extend('Profile');
const profile = new Profile();
profile.id = profileId;
surveyLog.set('type', 'survey-profile');
surveyLog.set('profile', profile.toPointer());
surveyLog.set('answers', [
{
question: '测试问题',
type: 'single',
answer: '测试答案'
}
]);
try {
await surveyLog.save();
console.log('✅ 问卷提交成功');
} catch (error) {
console.error('❌ 提交失败:', error);
}
}
testSubmitSurvey();
{
surveyCompleted: Boolean, // 是否完成问卷
surveyCompletedAt: Date, // 问卷完成时间
surveyLogId: String // 关联的SurveyLog ID
}
添加字段SQL(如果使用PostgreSQL):
-- 添加问卷相关字段到Profile表
ALTER TABLE "Profile"
ADD COLUMN IF NOT EXISTS "surveyCompleted" BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS "surveyCompletedAt" TIMESTAMP,
ADD COLUMN IF NOT EXISTS "surveyLogId" VARCHAR(255);
-- 添加索引
CREATE INDEX IF NOT EXISTS "idx_profile_survey_completed"
ON "Profile" ("surveyCompleted");
{
type: 'survey-profile', // 问卷类型
profile: Pointer<Profile>, // 关联员工
company: Pointer<Company>, // 所属公司
answers: Array<{
question: string, // 问题文本
type: 'single' | 'multiple' | 'scale', // 题型
answer: string | string[] | number, // 答案
options?: string[] // 选项列表(可选)
}>,
createdAt: Date, // 提交时间
updatedAt: Date // 更新时间
}
创建表SQL:
-- SurveyLog表已存在,只需确认type字段支持'survey-profile'
-- 可以添加检查约束
ALTER TABLE "SurveyLog"
ADD CONSTRAINT "check_survey_type"
CHECK ("type" IN ('survey-project', 'survey-contact', 'survey-profile'));
在提交测试报告前,请确认以下各项:
推荐的调试流程:
1. 使用测试工具页面 (http://localhost:4200/test-wxwork-activation/test)
→ 快速验证核心流程
2. 使用Console命令
→ 精细控制和调试
3. 真机企微测试
→ 最终验收
关键调试点:
遇到问题时:
祝调试顺利!🎉