基于现有企微身份认证功能,增加员工问卷填写和问卷查看功能:
注意:企业微信身份激活功能已存在,本方案复用现有认证流程,仅增加问卷引导和查看功能。
员工打开企微应用
↓
[企微自动认证] ← 现有功能
├─ WxworkAuth.authenticate()
├─ 获取企微用户信息
└─ 自动登录/注册
↓
[检查问卷状态] ← 新增功能
↓ 未填写
[引导填写问卷弹窗] ← 新增功能
├─ 提示:完善能力画像
├─ [稍后填写] [立即填写]
└─ 点击"立即填写"
↓
[员工问卷页面] ← 已完成
├─ 填写17道调研题目
├─ 逐题自动保存
└─ 提交完成
↓
[问卷完成提示] ← 新增功能
├─ 显示能力画像
└─ 进入工作台
组长工作台
↓
点击设计师卡片/列表
↓
[设计师详情弹窗]
├─ Tab1: 基本信息
├─ Tab2: 负载概况
├─ Tab3: 负载详细日历
└─ Tab4: 能力问卷 ← 新增
├─ 显示问卷填写状态
├─ 查看完整问卷答案
└─ 查看能力画像摘要
需要在现有 Profile 表中增加以下字段:
| 字段名 | 类型 | 说明 | 示例值 |
|---|---|---|---|
| isActivated | Boolean | 是否已激活 | true |
| activatedAt | Date | 激活时间 | 2025-10-30T10:00:00Z |
| surveyCompleted | Boolean | 是否完成问卷 | true |
| surveyCompletedAt | Date | 问卷完成时间 | 2025-10-30T10:30:00Z |
| surveyLogId | String | 关联的SurveyLog ID | "survey_xxx" |
这些字段用于快速判断员工状态,避免每次都查询 SurveyLog 表
已实现,用于存储问卷答案:
| 字段名 | 类型 | 说明 |
|---|---|---|
| profile | Pointer → Profile | 关联员工 |
| type | String | 'survey-profile' |
| data | Object | 问卷答案 |
| isCompleted | Boolean | 是否完成 |
| completedAt | Date | 完成时间 |
注意:企微身份认证已完成,本方案仅需增加问卷引导和查看功能
组件路径:根据项目架构,可能在以下位置之一
src/app/pages/designer/dashboard/dashboard.ts (设计师工作台)src/modules/wxwork/pages/home/home.component.ts (企微端首页)在工作台组件中增加:
export class Dashboard implements OnInit {
private wxAuth: WxworkAuth | null = null;
private currentProfile: FmodeObject | null = null;
// 新增:问卷相关属性
showSurveyGuide: boolean = false;
surveyCompleted: boolean = false;
async ngOnInit() {
// 1. 企微认证(现有功能)
await this.authenticateAndLoadData();
// 2. 检查问卷状态(新增功能)
await this.checkSurveyStatus();
// 3. 显示问卷引导(新增功能)
if (!this.surveyCompleted) {
this.showSurveyGuide = true;
}
}
/**
* 认证并加载数据(现有方法)
*/
private async authenticateAndLoadData(): Promise<void> {
if (!this.wxAuth) return;
try {
// 执行企微认证
await this.wxAuth.authenticate();
// 获取当前员工
this.currentProfile = await this.wxAuth.currentProfile();
console.log('当前员工:', this.currentProfile);
// 加载工作台数据
await this.loadDashboardData();
} catch (error) {
console.error('认证或加载数据失败:', error);
// 使用模拟数据降级
this.loadMockData();
}
}
/**
* 检查问卷状态(新增方法)
*/
async checkSurveyStatus() {
if (!this.currentProfile?.id) return;
try {
const Parse = (window as any).Parse;
const query = new Parse.Query('SurveyLog');
query.equalTo('profile', this.currentProfile.toPointer());
query.equalTo('type', 'survey-profile');
query.equalTo('isCompleted', true);
const surveyLog = await query.first();
this.surveyCompleted = !!surveyLog;
console.log('问卷状态:', this.surveyCompleted ? '已填写' : '未填写');
} catch (err) {
console.error('检查问卷状态失败:', err);
}
}
/**
* 跳转到问卷页面(新增方法)
*/
goToSurvey() {
const cid = this.wxAuth?.config.cid || '';
window.location.href = `/wxwork/${cid}/survey/profile`;
}
/**
* 关闭问卷引导(新增方法)
*/
closeSurveyGuide() {
this.showSurveyGuide = false;
// 可以记录到localStorage,避免每次都弹出
localStorage.setItem('survey_guide_closed', 'true');
}
}
在工作台HTML中增加:
<!-- 问卷引导弹窗 -->
@if (showSurveyGuide) {
<div class="survey-guide-overlay" (click)="closeSurveyGuide()">
<div class="survey-guide-modal" (click)="$event.stopPropagation()">
<button class="close-btn" (click)="closeSurveyGuide()">
<ion-icon name="close"></ion-icon>
</button>
<div class="guide-content">
<div class="guide-icon">
<ion-icon name="clipboard-outline"></ion-icon>
</div>
<h2>完善您的能力画像</h2>
<p class="guide-description">
为了更精准地为您匹配合适的项目,<br/>
请花8-10分钟完成能力调研问卷。
</p>
<ul class="guide-benefits">
<li>
<ion-icon name="checkmark-circle"></ion-icon>
<span>智能匹配项目,发挥您的专长</span>
</li>
<li>
<ion-icon name="checkmark-circle"></ion-icon>
<span>避免不匹配项目导致返工</span>
</li>
<li>
<ion-icon name="checkmark-circle"></ion-icon>
<span>合理分配工作量,提升效率</span>
</li>
</ul>
<div class="guide-actions">
<button class="btn-secondary" (click)="closeSurveyGuide()">
稍后填写
</button>
<button class="btn-primary" (click)="goToSurvey()">
<span>立即填写</span>
<ion-icon name="arrow-forward"></ion-icon>
</button>
</div>
<p class="guide-hint">
<ion-icon name="time-outline"></ion-icon>
预计用时:8-10分钟
</p>
</div>
</div>
</div>
}
// 问卷引导弹窗样式
.survey-guide-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
animation: fadeIn 0.3s ease;
}
.survey-guide-modal {
background: white;
border-radius: 16px;
padding: 32px;
max-width: 480px;
width: 90%;
position: relative;
animation: slideUp 0.3s ease;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
.close-btn {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border: none;
background: #f3f4f6;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: #e5e7eb;
}
ion-icon {
width: 20px;
height: 20px;
}
}
.guide-content {
text-align: center;
.guide-icon {
width: 80px;
height: 80px;
margin: 0 auto 24px;
background: linear-gradient(135deg, #3B82F6, #60A5FA);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
ion-icon {
width: 40px;
height: 40px;
color: white;
}
}
h2 {
margin: 0 0 16px;
font-size: 24px;
font-weight: 700;
color: #1F2937;
}
.guide-description {
margin: 0 0 24px;
font-size: 15px;
line-height: 1.6;
color: #6B7280;
}
.guide-benefits {
list-style: none;
padding: 0;
margin: 0 0 32px;
text-align: left;
li {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
margin-bottom: 8px;
background: #EFF6FF;
border-radius: 8px;
ion-icon {
width: 20px;
height: 20px;
color: #3B82F6;
flex-shrink: 0;
}
span {
font-size: 14px;
color: #1F2937;
}
}
}
.guide-actions {
display: flex;
gap: 12px;
margin-bottom: 16px;
button {
flex: 1;
padding: 14px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
ion-icon {
width: 18px;
height: 18px;
}
}
.btn-secondary {
background: #F3F4F6;
color: #1F2937;
&:hover {
background: #E5E7EB;
}
}
.btn-primary {
background: linear-gradient(135deg, #3B82F6, #60A5FA);
color: white;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(59, 130, 246, 0.5);
}
}
}
.guide-hint {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-size: 13px;
color: #6B7280;
margin: 0;
ion-icon {
width: 16px;
height: 16px;
}
}
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
组件路径:src/app/pages/team-leader/components/designer-detail-modal.component.ts
HTML 模板修改:
<div class="modal-tabs">
<button [class.active]="currentTab === 'info'" (click)="currentTab = 'info'">
基本信息
</button>
<button [class.active]="currentTab === 'workload'" (click)="currentTab = 'workload'">
负载概况
</button>
<button [class.active]="currentTab === 'calendar'" (click)="currentTab = 'calendar'">
负载详细日历
</button>
<!-- 新增:能力问卷Tab -->
<button [class.active]="currentTab === 'survey'" (click)="currentTab = 'survey'; loadSurvey()">
能力问卷
@if (!designer.surveyCompleted) {
<span class="badge-warning">未填写</span>
} @else {
<span class="badge-success">✓</span>
}
</button>
</div>
<div class="modal-body">
<!-- 现有Tab内容... -->
<!-- 新增:能力问卷Tab -->
@if (currentTab === 'survey') {
<div class="survey-tab">
@if (!surveyLoading && !designer.surveyCompleted) {
<!-- 未填写状态 -->
<div class="empty-state">
<ion-icon name="document-text-outline" class="empty-icon"></ion-icon>
<h3>该员工尚未填写能力问卷</h3>
<p>问卷数据用于智能订单分配</p>
<button class="btn-primary" (click)="sendSurveyReminder()">
<ion-icon name="mail-outline"></ion-icon>
发送填写提醒
</button>
</div>
} @else if (surveyLoading) {
<!-- 加载中 -->
<div class="loading-state">
<ion-spinner></ion-spinner>
<p>加载问卷数据...</p>
</div>
} @else {
<!-- 已填写:显示问卷内容 -->
<div class="survey-content">
<!-- 能力画像摘要卡片 -->
<div class="capability-card">
<h3>
<ion-icon name="analytics-outline"></ion-icon>
能力画像摘要
</h3>
<div class="capability-grid">
<div class="capability-item">
<label>擅长风格</label>
<div class="tags">
@for (style of surveyData.q1_expertise_styles; track style) {
<span class="tag tag-blue">{{ style }}</span>
}
</div>
</div>
<div class="capability-item">
<label>擅长空间</label>
<div class="tags">
@for (space of surveyData.q2_expertise_spaces; track space) {
<span class="tag tag-green">{{ space }}</span>
}
</div>
</div>
<div class="capability-item">
<label>技术优势</label>
<div class="tags">
@for (adv of surveyData.q3_technical_advantages; track adv) {
<span class="tag tag-purple">{{ adv }}</span>
}
</div>
</div>
<div class="capability-item">
<label>项目难度</label>
<span class="badge badge-primary">{{ surveyData.q5_project_difficulty }}</span>
</div>
<div class="capability-item">
<label>周承接量</label>
<span>{{ surveyData.q7_weekly_capacity }}</span>
</div>
<div class="capability-item">
<label>紧急订单</label>
<span>
{{ surveyData.q8_urgent_willingness }}
@if (surveyData.q8_urgent_limit) {
<span class="hint">(每月不超过{{ surveyData.q8_urgent_limit }}次)</span>
}
</span>
</div>
</div>
</div>
<!-- 详细问卷答案(可折叠) -->
<div class="survey-details">
<button class="accordion-header" (click)="toggleDetails()">
<h3>详细问卷答案</h3>
<ion-icon [name]="showDetails ? 'chevron-up' : 'chevron-down'"></ion-icon>
</button>
@if (showDetails) {
<div class="accordion-content">
<!-- 一、核心技术能力 -->
<div class="survey-section">
<h4>一、核心技术能力</h4>
<div class="answer-item">
<label>擅长风格:</label>
<span>{{ formatArrayAnswer(surveyData.q1_expertise_styles) }}</span>
</div>
<div class="answer-item">
<label>擅长空间:</label>
<span>{{ formatArrayAnswer(surveyData.q2_expertise_spaces) }}</span>
</div>
<div class="answer-item">
<label>技术优势:</label>
<span>{{ formatArrayAnswer(surveyData.q3_technical_advantages) }}</span>
</div>
</div>
<!-- 二、项目经验与案例 -->
<div class="survey-section">
<h4>二、项目经验与案例</h4>
<div class="case-card">
<h5>📋 代表性案例1</h5>
<p><strong>项目类型:</strong>{{ surveyData.q4_case_1_type }}</p>
<p><strong>核心亮点:</strong>{{ surveyData.q4_case_1_highlight }}</p>
</div>
@if (surveyData.q4_case_2_type) {
<div class="case-card">
<h5>📋 代表性案例2</h5>
<p><strong>项目类型:</strong>{{ surveyData.q4_case_2_type }}</p>
<p><strong>核心亮点:</strong>{{ surveyData.q4_case_2_highlight }}</p>
</div>
}
<div class="answer-item">
<label>项目难度:</label>
<span>{{ surveyData.q5_project_difficulty }}</span>
</div>
</div>
<!-- 三、项目承接偏好 -->
<div class="survey-section">
<h4>三、项目承接偏好</h4>
<div class="answer-item">
<label>偏好项目类型:</label>
<span>{{ formatArrayAnswer(surveyData.q6_prefer_project_types) }}</span>
</div>
<div class="answer-item">
<label>周承接量:</label>
<span>{{ surveyData.q7_weekly_capacity }}</span>
</div>
<div class="answer-item">
<label>紧急订单意愿:</label>
<span>{{ surveyData.q8_urgent_willingness }}</span>
</div>
</div>
<!-- 四、协作与交付习惯 -->
<div class="survey-section">
<h4>四、协作与交付习惯</h4>
<div class="answer-item">
<label>进度反馈:</label>
<span>{{ surveyData.q9_progress_feedback }}</span>
</div>
<div class="answer-item">
<label>交付确认:</label>
<span>{{ formatArrayAnswer(surveyData.q10_delivery_confirmation) }}</span>
</div>
<div class="answer-item">
<label>需求清晰度:</label>
<span>{{ surveyData.q11_requirement_clarity }}</span>
</div>
<div class="answer-item">
<label>沟通方式:</label>
<span>{{ formatArrayAnswer(surveyData.q12_communication_methods) }}</span>
</div>
</div>
<!-- 五、问题应对与风险预警 -->
<div class="survey-section">
<h4>五、问题应对与风险预警</h4>
<div class="answer-item">
<label>敏感词了解:</label>
<span>{{ surveyData.q13_sensitive_words_awareness }}</span>
</div>
<div class="answer-item">
<label>问题处理:</label>
<span>{{ surveyData.q14_problem_handling }}</span>
</div>
<div class="answer-item">
<label>任务通知:</label>
<span>{{ formatArrayAnswer(surveyData.q15_task_notification) }}</span>
</div>
</div>
<!-- 六、补充说明 -->
@if (surveyData.q16_cannot_accept || surveyData.q17_additional_notes) {
<div class="survey-section">
<h4>六、补充说明</h4>
@if (surveyData.q16_cannot_accept) {
<div class="answer-item">
<label>无法承接类型:</label>
<span>{{ surveyData.q16_cannot_accept }}</span>
</div>
}
@if (surveyData.q17_additional_notes) {
<div class="answer-item">
<label>其他补充:</label>
<span>{{ surveyData.q17_additional_notes }}</span>
</div>
}
</div>
}
</div>
}
</div>
<!-- 问卷填写时间 -->
<div class="survey-footer">
<ion-icon name="time-outline"></ion-icon>
<span>填写时间:{{ surveyCompletedAt | date:'yyyy-MM-dd HH:mm' }}</span>
<button class="btn-text" (click)="sendSurveyUpdateReminder()">
通知更新问卷
</button>
</div>
</div>
}
</div>
}
</div>
export class DesignerDetailModalComponent implements OnInit {
// 现有属性...
// 新增:问卷相关属性
currentTab: string = 'info';
surveyLoading: boolean = false;
surveyData: any = null;
surveyCompletedAt: Date | null = null;
showDetails: boolean = false;
async ngOnInit() {
// 加载基本信息
await this.loadDesignerInfo();
// 检查问卷状态
await this.checkSurveyStatus();
}
/**
* 检查员工问卷状态
*/
async checkSurveyStatus() {
if (!this.designer?.id) return;
try {
const query = new Parse.Query('SurveyLog');
query.equalTo('profile', this.designer.toPointer());
query.equalTo('type', 'survey-profile');
query.equalTo('isCompleted', true);
query.descending('updatedAt');
const surveyLog = await query.first();
if (surveyLog) {
this.designer.surveyCompleted = true;
this.designer.surveyCompletedAt = surveyLog.get('completedAt');
} else {
this.designer.surveyCompleted = false;
}
} catch (err) {
console.error('检查问卷状态失败:', err);
}
}
/**
* 加载问卷数据
*/
async loadSurvey() {
if (!this.designer?.surveyCompleted || this.surveyData) return;
this.surveyLoading = true;
try {
const query = new Parse.Query('SurveyLog');
query.equalTo('profile', this.designer.toPointer());
query.equalTo('type', 'survey-profile');
query.equalTo('isCompleted', true);
query.descending('updatedAt');
const surveyLog = await query.first();
if (surveyLog) {
this.surveyData = surveyLog.get('data') || {};
this.surveyCompletedAt = surveyLog.get('completedAt');
}
} catch (err) {
console.error('加载问卷数据失败:', err);
window?.fmode?.alert('加载问卷数据失败,请重试');
} finally {
this.surveyLoading = false;
}
}
/**
* 切换详细答案显示
*/
toggleDetails() {
this.showDetails = !this.showDetails;
}
/**
* 格式化数组答案
*/
formatArrayAnswer(arr: string[] | string): string {
if (Array.isArray(arr)) {
return arr.join('、');
}
return arr || '未填写';
}
/**
* 发送填写提醒
*/
async sendSurveyReminder() {
if (!this.designer?.id) return;
try {
// TODO: 通过企微发送消息提醒填写问卷
const surveyUrl = `${window.location.origin}/wxwork/${this.cid}/profile/activation`;
// 调用企微API发送消息
// await this.wxwork.sendMessage({
// touser: this.designer.get('userid'),
// msgtype: 'text',
// text: {
// content: `请完成员工能力问卷填写:${surveyUrl}`
// }
// });
window?.fmode?.alert('已发送填写提醒');
} catch (err) {
console.error('发送提醒失败:', err);
window?.fmode?.alert('发送提醒失败,请重试');
}
}
/**
* 发送更新提醒
*/
async sendSurveyUpdateReminder() {
if (!this.designer?.id) return;
try {
const surveyUrl = `${window.location.origin}/wxwork/${this.cid}/survey/profile`;
window?.fmode?.alert('已通知员工更新问卷');
} catch (err) {
console.error('发送更新提醒失败:', err);
window?.fmode?.alert('发送更新提醒失败,请重试');
}
}
}
简化说明:由于企微身份认证已完成,本方案只需增加问卷引导和查看功能
src/app/pages/designer/dashboard/dashboard.tscheckSurveyStatus() 方法goToSurvey() 方法closeSurveyGuide() 方法ngOnInit() 中调用问卷检查src/app/pages/designer/dashboard/dashboard.html@if (showSurveyGuide)src/app/pages/designer/dashboard/dashboard.scss.survey-guide-overlay 样式.survey-guide-modal 样式checkSurveyStatus() 方法loadSurvey() 方法formatArrayAnswer() 方法toggleDetails() 方法sendSurveyReminder() 方法(未填写时)sendSurveyUpdateReminder() 方法(已填写时)员工问卷路由已配置:
// src/app/app.routes.ts
{
path: 'survey/profile',
loadComponent: () => import('../modules/profile/pages/profile-survey/profile-survey.component').then(m => m.ProfileSurveyComponent),
title: '员工技能调研'
}
无需额外配置路由。
主题色:
布局:
布局结构:
能力画像摘要卡片(固定显示)
↓
详细问卷答案(可折叠)
↓
问卷填写时间 + 操作按钮
样式规范:
sequenceDiagram
participant E as 员工端
participant A as 激活组件
participant P as Profile表
participant S as SurveyLog表
E->>A: 打开企微应用
A->>A: 检查 isActivated
alt 未激活
A->>E: 显示身份填写页面
E->>A: 填写姓名、手机号等
A->>P: 保存基本信息
A->>E: 显示问卷页面
E->>A: 填写17道题目
A->>S: 保存问卷答案
A->>P: 更新 isActivated=true, surveyCompleted=true
A->>E: 显示激活成功页面
else 已激活
A->>E: 进入工作台
end
sequenceDiagram
participant L as 组长端
participant D as 详情弹窗
participant S as SurveyLog表
L->>D: 点击设计师卡片
D->>D: 加载基本信息
L->>D: 点击"能力问卷"Tab
D->>S: 查询 SurveyLog (type='survey-profile')
alt 已填写
S->>D: 返回问卷数据
D->>L: 显示能力画像 + 详细答案
else 未填写
D->>L: 显示"未填写"状态
L->>D: 点击"发送填写提醒"
D-->>员工: 发送企微消息
end
| 用例ID | 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|---|
| ACT-01 | 首次登录激活 | 1. 新员工首次登录 2. 自动跳转激活页面 |
显示身份填写表单 |
| ACT-02 | 身份信息验证 | 1. 不填姓名点击下一步 2. 填写错误手机号 |
显示验证错误提示 |
| ACT-03 | 头像上传 | 1. 点击头像上传 2. 选择图片 |
预览上传的头像 |
| ACT-04 | 问卷填写 | 1. 完成身份填写 2. 进入问卷页面 3. 填写17道题 |
逐题保存,最后提交成功 |
| ACT-05 | 激活成功 | 1. 完成所有步骤 2. 查看成功页面 |
显示欢迎信息和能力画像 |
| ACT-06 | 重复访问 | 1. 已激活员工再次登录 | 直接进入工作台,不显示激活页面 |
| 用例ID | 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|---|
| SUR-01 | 查看已填写问卷 | 1. 组长打开设计师详情 2. 点击"能力问卷"Tab |
显示完整问卷数据和能力画像 |
| SUR-02 | 查看未填写状态 | 1. 查看未填写问卷的员工 | 显示"未填写"提示和发送提醒按钮 |
| SUR-03 | 发送填写提醒 | 1. 点击"发送填写提醒"按钮 | 成功发送企微消息给员工 |
| SUR-04 | 折叠详细答案 | 1. 点击"详细问卷答案" 2. 再次点击 |
答案区域展开/折叠 |
| SUR-05 | 问卷数据格式 | 1. 查看各类题型答案 | 多选题用顿号分隔,文本题完整显示 |
简化后的时间表:基于现有功能,只需增加问卷引导和查看
| 阶段 | 任务 | 预计工时 | 负责人 |
|---|---|---|---|
| Phase 1 | 工作台增加问卷引导弹窗 | 4h | 前端开发 |
| Phase 2 | 员工详情弹窗增加问卷Tab | 6h | 前端开发 |
| Phase 3 | 测试和优化 | 2h | 前端开发 |
| 总计 | 12小时 |
对比原方案:
// 检查员工激活状态
async checkActivationStatus(): Promise<ActivationStatus> {
const profile = await this.wxAuth.currentProfile();
return {
isActivated: profile.get('isActivated') || false,
surveyCompleted: profile.get('surveyCompleted') || false,
needsActivation: !profile.get('isActivated'),
needsSurvey: profile.get('isActivated') && !profile.get('surveyCompleted')
};
}
// 批量查询多个员工的问卷状态(用于列表展示)
async batchLoadSurveyStatus(profiles: FmodeObject[]): Promise<Map<string, boolean>> {
const profileIds = profiles.map(p => p.id);
const query = new Parse.Query('SurveyLog');
query.containedIn('profile', profileIds.map(id => ({
__type: 'Pointer',
className: 'Profile',
objectId: id
})));
query.equalTo('type', 'survey-profile');
query.equalTo('isCompleted', true);
query.select('profile');
const results = await query.find();
const statusMap = new Map<string, boolean>();
results.forEach(log => {
const profileId = log.get('profile')?.id;
if (profileId) {
statusMap.set(profileId, true);
}
});
return statusMap;
}
// 激活完成后更新Profile
async completeActivation(profile: FmodeObject, surveyLogId: string) {
profile.set('isActivated', true);
profile.set('activatedAt', new Date());
profile.set('surveyCompleted', true);
profile.set('surveyCompletedAt', new Date());
profile.set('surveyLogId', surveyLogId);
await profile.save();
}
基于问卷数据自动匹配最适合的设计师:
在管理后台增加团队能力分析页面:
支持问卷题目迭代:
支持导出员工能力数据:
| 接口路径 | 方法 | 说明 |
|---|---|---|
/api/profile/activate |
POST | 激活员工账号 |
/api/profile/check-status |
GET | 检查激活状态 |
/api/survey/save |
POST | 保存问卷答案 |
/api/survey/get |
GET | 获取问卷数据 |
/api/survey/send-reminder |
POST | 发送填写提醒 |
ALTER TABLE Profile ADD COLUMN isActivated BOOLEAN DEFAULT FALSE;
ALTER TABLE Profile ADD COLUMN activatedAt TIMESTAMP NULL;
ALTER TABLE Profile ADD COLUMN surveyCompleted BOOLEAN DEFAULT FALSE;
ALTER TABLE Profile ADD COLUMN surveyCompletedAt TIMESTAMP NULL;
ALTER TABLE Profile ADD COLUMN surveyLogId VARCHAR(255) NULL;
{
"msgtype": "text",
"text": {
"content": "【员工能力调研】\n\n您好,请完成员工能力问卷填写,这将帮助我们更好地为您匹配合适的项目。\n\n点击链接填写:[链接]\n\n预计用时:8-10分钟"
}
}
{
"msgtype": "text",
"text": {
"content": "【问卷更新提醒】\n\n您好,为了更准确地匹配项目,建议您更新一下能力问卷信息。\n\n点击链接更新:[链接]"
}
}
文档版本:v1.0
创建时间:2025-10-31
最后更新:2025-10-31
状态:待评审