组件-员工问卷.md 40 KB

员工问卷组件产品需求文档

一、概述

1.1 功能定位

员工问卷是内部技术组员的专业特长及偏好调研工具,通过系统化问卷了解员工的技术能力、项目经验、承接偏好和协作习惯,为客服智能分配订单提供数据支撑。

1.2 业务价值

  • 员工视角: 清晰表达技术优势和承接意愿,避免不匹配项目导致返工
  • 管理视角: 精准掌握团队能力分布,合理分配订单,提升交付质量
  • 客服视角: 快速匹配设计师特长与客户需求,减少沟通成本
  • 数据视角: 积累员工能力画像,优化团队培训和项目排期

1.3 应用场景

  1. 员工入职时: 新员工首次登录企微端,完成身份认证后填写问卷
  2. 定期更新: 员工可更新问卷内容(如新增擅长风格、调整承接意愿)
  3. 订单分配前: 客服查看员工问卷,匹配合适的设计师
  4. 组长管理: 组长在员工详情弹窗中查看完整问卷答案和能力画像

二、数据范式

2.1 SurveyLog 问卷结果表(复用现有表)

字段名 类型 必填 说明 示例值
objectId String 主键ID "survey_profile_001"
profile Pointer 提交员工 → Profile
project Pointer 关联项目(员工问卷为null) null
contact Pointer 关联联系人(员工问卷为null) null
company Pointer 所属帐套 → Company
type String 问卷类型 "survey-profile"
data Object 问卷结果 {q1: ["奶油风"], ...}
isCompleted Boolean 是否完整填写 true
completedAt Date 完成时间 2025-10-30T10:00:00.000Z
version Number 问卷版本号 1
isDeleted Boolean 软删除标记 false
createdAt Date 自动 创建时间 2025-10-30T09:00:00.000Z
updatedAt Date 自动 更新时间 2025-10-30T10:00:00.000Z

type 枚举值:

  • survey-project: 项目问卷(客户填写)
  • survey-contact: 联系人问卷(暂未实现)
  • survey-profile: 员工问卷(本次实现)

data 字段结构示例:

{
  "q1_expertise_styles": ["奶油风", "极简风", "新中式"],
  "q2_expertise_spaces": ["常规住宅空间", "大平层/别墅复杂空间", "特殊功能空间"],
  "q3_technical_advantages": ["渲染精度高", "方案深化能力"],
  "q4_case_1_type": "140㎡极简风四居室",
  "q4_case_1_highlight": "高还原度材质渲染,精准呈现原木与水泥灰的质感碰撞",
  "q4_case_2_type": "90㎡奶油风三居室",
  "q4_case_2_highlight": "方案深化能力突出,优化客餐厅动线并设计嵌入式收纳系统",
  "q5_project_difficulty": "中等难度",
  "q6_prefer_project_types": ["擅长风格的常规项目", "新风格探索项目", "需方案深化的项目"],
  "q7_weekly_capacity": "1-2个(中等难度)",
  "q8_urgent_willingness": "愿意承接",
  "q8_urgent_limit": "2",
  "q9_progress_feedback": "关键节点同步",
  "q10_delivery_confirmation": ["交付前必同步客服/组长", "参考图逐张确认"],
  "q11_requirement_clarity": "基础需求+核心偏好",
  "q12_communication_methods": ["群内文字同步", "短语音快速沟通", "复杂需求开线上会议"],
  "q13_sensitive_words_awareness": "可识别部分敏感词",
  "q14_problem_handling": "先尝试自行调整",
  "q15_task_notification": "工作群@+私信提醒",
  "q16_cannot_accept": "暂无法承接大型商业空间(如超过500㎡的商场展厅、连锁餐饮门店)项目,以及无任何参考图且客户需求极度模糊的项目。",
  "q17_additional_notes": "擅长使用SU进行建模,同时熟练运用Lumion、V-Ray进行渲染;希望多承接新中式风格项目,进一步提升该风格的方案深化与软装搭配能力;针对老人房、儿童房等特殊功能空间,可提供符合使用者生理与心理需求的细节优化建议。"
}

三、核心组件设计

3.1 ProfileSurveyComponent 员工问卷组件

3.1.1 路由配置

// 路由: /wxwork/:cid/survey/profile
{
  path: 'wxwork/:cid',
  children: [
    {
      path: 'survey/profile',
      loadComponent: () => import('../modules/profile/pages/profile-survey/profile-survey.component'),
      title: '员工技能调研'
    }
  ]
}

3.1.2 组件状态机

组件包含三种状态,通过 currentState 控制:

type SurveyState = 'welcome' | 'questionnaire' | 'result';

currentState: SurveyState = 'welcome';

状态转换流程:

[欢迎页] --点击开始--> [答题页] --提交完成--> [结果页]
    ↑                                              ↓
    └──────────────── 重新填写 ──────────────────┘

3.2 欢迎页 (welcome)

3.2.1 页面布局

┌─────────────────────────────────┐
│         员工问卷欢迎页            │
├─────────────────────────────────┤
│  [员工头像]                       │
│  您好,张三                       │
│                                  │
│  《技术组员偏好及状况调研表》      │
│                                  │
│  尊敬的伙伴:                     │
│  为了更精准地匹配项目与您的专业能力│
│  请完成本次能力调研。您的选择将帮助│
│  我们合理分配订单,避免不匹配项目导│
│  致返工,让您的优势得到充分发挥!  │
│                                  │
│  • 预计用时: 8-10分钟             │
│  • 题目数量: 17题                 │
│  • 题型: 选择题为主,少量填空题    │
│                                  │
│         [开始填写]                 │
│                                  │
│  [已填写过? 查看/更新答案]         │
└─────────────────────────────────┘

3.2.2 功能实现

  1. 用户识别:

    • 通过 WxworkAuth.currentProfile() 获取当前登录员工
    • 显示员工头像和姓名
    • 记录 profile.id 用于后续保存
  2. 数据检查:

    • 组件初始化时查询 SurveyLog 表
    • 条件: type == 'survey-profile' AND profile == profileId
    • 如果已存在且 isCompleted == true,显示"查看/更新答案"入口
  3. 开始按钮:

    • 点击后执行 startSurvey()
    • 切换状态: currentState = 'questionnaire'
    • 初始化题目索引: currentQuestionIndex = 0

3.3 答题页 (questionnaire)

3.3.1 页面布局

┌─────────────────────────────────┐
│  进度: 1/17 ●○○○○○○○○○○○○○○○○ │
├─────────────────────────────────┤
│  一、核心技术能力                 │
│                                  │
│  1. 您最擅长的家装风格?          │
│     (可多选,优先选3项)           │
│                                  │
│  ☑ 奶油风                        │
│  ☑ 极简风(含侘寂风)             │
│  ☑ 新中式                        │
│  □ 轻奢风                        │
│  □ 美式/欧式                     │
│  □ 复古风(如法式、美式复古)     │
│  □ 其他: [____________]          │
│                                  │
│         [← 上一题]  [下一题 →]   │
└─────────────────────────────────┘

3.3.2 题目数据结构

interface Question {
  id: string;              // 题目ID,如 "q1", "q2"
  section: string;         // 章节,如 "核心技术能力", "项目经验与案例"
  title: string;           // 题目文本
  subtitle?: string;       // 副标题提示
  type: 'single' | 'multiple' | 'text' | 'number' | 'textarea'; // 题型
  options?: string[];      // 选项列表
  hasOther?: boolean;      // 是否有"其他"选项
  required?: boolean;      // 是否必填
  maxSelections?: number;  // 多选题最多选择数量
  placeholder?: string;    // 输入框占位符
}

3.3.3 题目列表

const questions: Question[] = [
  // ==================== 一、核心技术能力 ====================
  {
    id: 'q1_expertise_styles',
    section: '核心技术能力',
    title: '您最擅长的家装风格?',
    subtitle: '可多选,优先选3项',
    type: 'multiple',
    maxSelections: 3,
    options: [
      '奶油风',
      '极简风(含侘寂风)',
      '新中式',
      '轻奢风',
      '美式/欧式',
      '复古风(如法式、美式复古)'
    ],
    hasOther: true,
    required: true
  },
  {
    id: 'q2_expertise_spaces',
    section: '核心技术能力',
    title: '您擅长的空间类型?',
    subtitle: '可多选',
    type: 'multiple',
    options: [
      '常规住宅空间(客厅/卧室/厨房)',
      '大平层/别墅复杂空间(如挑空客厅、独立书房)',
      '商业空间(民宿/小型展厅/餐饮)',
      '特殊功能空间(如儿童房、老人房、电竞房)'
    ],
    required: true
  },
  {
    id: 'q3_technical_advantages',
    section: '核心技术能力',
    title: '技术能力优势?',
    subtitle: '可多选,优先选2项',
    type: 'multiple',
    maxSelections: 2,
    options: [
      '渲染精度高(光影/材质还原度优)',
      '建模速度快(高效完成基础建模)',
      '软装搭配落地性强(贴合实际采购与风格统一)',
      '方案深化能力(可提供空间优化/灯光布局建议)',
      '复杂场景处理(如异形吊顶、定制柜体建模)'
    ],
    required: true
  },

  // ==================== 二、项目经验与案例 ====================
  {
    id: 'q4_case_1_type',
    section: '项目经验与案例',
    title: '代表性案例1:项目类型',
    subtitle: '例如:120㎡极简风三居室',
    type: 'text',
    placeholder: '请输入项目类型...',
    required: true
  },
  {
    id: 'q4_case_1_highlight',
    section: '项目经验与案例',
    title: '代表性案例1:核心亮点',
    subtitle: '例如:高还原度材质渲染/复杂吊顶建模',
    type: 'textarea',
    placeholder: '请输入核心亮点...',
    required: true
  },
  {
    id: 'q4_case_2_type',
    section: '项目经验与案例',
    title: '代表性案例2:项目类型',
    subtitle: '选填,便于客服对接同类需求',
    type: 'text',
    placeholder: '请输入项目类型...',
    required: false
  },
  {
    id: 'q4_case_2_highlight',
    section: '项目经验与案例',
    title: '代表性案例2:核心亮点',
    subtitle: '选填',
    type: 'textarea',
    placeholder: '请输入核心亮点...',
    required: false
  },
  {
    id: 'q5_project_difficulty',
    section: '项目经验与案例',
    title: '可独立处理的项目难度?',
    type: 'single',
    options: [
      '基础难度(常规户型、成熟风格、无复杂结构)',
      '中等难度(复杂户型、小众风格、需基础深化)',
      '高等难度(别墅/异形空间、高还原度需求、深度方案配合)'
    ],
    required: true
  },

  // ==================== 三、项目承接偏好 ====================
  {
    id: 'q6_prefer_project_types',
    section: '项目承接偏好',
    title: '您优先想承接的项目类型?',
    subtitle: '可多选',
    type: 'multiple',
    options: [
      '擅长风格的常规项目(稳定发挥优势)',
      '新风格探索项目(愿意尝试未接触过的风格)',
      '需方案深化的项目(可输出技术建议)',
      '批量小型项目(如多套同户型基础渲染)'
    ],
    hasOther: true,
    required: true
  },
  {
    id: 'q7_weekly_capacity',
    section: '项目承接偏好',
    title: '您可承接的单周期项目数量上限?',
    subtitle: '以周为单位',
    type: 'single',
    options: [
      '2-3个(基础难度)',
      '1-2个(中等难度)',
      '1个(高等难度)',
      '可灵活调整(需提前沟通)'
    ],
    required: true
  },
  {
    id: 'q8_urgent_willingness',
    section: '项目承接偏好',
    title: '对紧急订单的承接意愿?',
    subtitle: '紧急订单:需24-48小时内交付初版',
    type: 'single',
    options: [
      '愿意承接',
      '暂不承接(优先保证常规订单质量)',
      '视情况而定(需提前确认时间是否充裕)'
    ],
    required: true
  },
  {
    id: 'q8_urgent_limit',
    section: '项目承接偏好',
    title: '如愿意承接紧急订单,每月上限?',
    subtitle: '如不承接可跳过',
    type: 'number',
    placeholder: '请输入数字(次)',
    required: false
  },

  // ==================== 四、协作与交付习惯 ====================
  {
    id: 'q9_progress_feedback',
    section: '协作与交付习惯',
    title: '项目进度反馈与时效把控?',
    type: 'single',
    options: [
      '每日同步进度(含"建模完成50%"等节点),超时前6小时主动预警',
      '关键节点同步(建模完成/渲染初版/最终交付前),超时前12小时主动预警',
      '有问题时即时反馈,无问题则按约定时间交付'
    ],
    required: true
  },
  {
    id: 'q10_delivery_confirmation',
    section: '协作与交付习惯',
    title: '交付前确认流程?',
    subtitle: '可多选',
    type: 'multiple',
    options: [
      '交付前必同步客服/组长,确认"是否需先给客户预览"后再发送',
      '若客户明确要参考图,会逐张确认具体参考要求并同步记录至工作群',
      '简单需求可直接交付,但复杂需求必提前同步确认'
    ],
    required: true
  },
  {
    id: 'q11_requirement_clarity',
    section: '协作与交付习惯',
    title: '对接需求时,您希望获取的客户信息清晰度?',
    type: 'single',
    options: [
      '需详细需求文档(含每张图的参考图、尺寸、风格说明)',
      '基础需求+核心偏好(可自主补充细节,但需确认"是否符合客户预期")',
      '灵活(简单沟通后推进,若遇参考要求不明确,会主动向客服/组长确认)'
    ],
    required: true
  },
  {
    id: 'q12_communication_methods',
    section: '协作与交付习惯',
    title: '若项目需协作,您倾向的对接方式?',
    subtitle: '可多选',
    type: 'multiple',
    options: [
      '群内文字同步(含参考要求、进度节点,便于追溯)',
      '短语音快速沟通(紧急时)',
      '复杂需求开线上会议(明确参考细节/深化方向)'
    ],
    required: true
  },

  // ==================== 五、问题应对与风险预警 ====================
  {
    id: 'q13_sensitive_words_awareness',
    section: '问题应对与风险预警',
    title: '您了解项目中的"敏感词"吗?',
    subtitle: '如"效果图不满意"、"出图时间拖了"、"参考不对"',
    type: 'single',
    options: [
      '了解,遇到会即时截图反馈客服/组长,触发Issue跟进',
      '不了解,需提供敏感词清单',
      '可识别部分敏感词,不确定时会先咨询组长再处理'
    ],
    required: true
  },
  {
    id: 'q14_problem_handling',
    section: '问题应对与风险预警',
    title: '若某个环节出问题,您的处理流程?',
    subtitle: '如"建模尺寸偏差"、"客户不认可色调"',
    type: 'single',
    options: [
      '先暂停当前工作,即时在群内说明问题+附截图,触发代办任务后等待协调',
      '先尝试自行调整,调整无效后再反馈(最长不超过2小时)',
      '优先联系客服了解"客户真实诉求",再同步组长制定解决方案'
    ],
    required: true
  },
  {
    id: 'q15_task_notification',
    section: '问题应对与风险预警',
    title: '您希望的代办任务通知方式?',
    subtitle: '可多选',
    type: 'multiple',
    options: [
      '工作群@+私信提醒',
      '电话通知(仅紧急任务)',
      '企业微信工单提醒'
    ],
    required: true
  },

  // ==================== 六、补充说明 ====================
  {
    id: 'q16_cannot_accept',
    section: '补充说明',
    title: '暂时无法承接的项目类型?',
    subtitle: '例如:暂不接商业空间/暂不接无参考图的项目',
    type: 'textarea',
    placeholder: '请输入暂时无法承接的项目类型...',
    required: false
  },
  {
    id: 'q17_additional_notes',
    section: '补充说明',
    title: '其他项目相关补充?',
    subtitle: '例如:擅长用SU建模/希望多接新中式项目提升能力',
    type: 'textarea',
    placeholder: '请输入其他补充说明...',
    required: false
  }
];

3.3.4 答题交互逻辑

  1. 单选题:

    • 点击选项后自动保存答案到 answers[questionId]
    • 自动跳转下一题
  2. 多选题:

    • 可选择多个选项
    • 如有 maxSelections 限制,达到上限后禁用其他选项
    • 显示选择数量提示:已选 2/3 项
    • 点击"下一题"后保存并跳转
  3. 文本题/数字题:

    • 输入完成后点击"下一题"
    • 文本输入框支持单行输入
  4. 多行文本题:

    • 使用 textarea,支持多行输入
    • 输入完成后点击"下一题"
  5. 进度指示:

    • 顶部显示进度条: currentQuestionIndex / totalQuestions
    • 显示当前章节名称
  6. 导航按钮:

    • "上一题": 返回上一题,可修改答案
    • "下一题": 保存当前答案并跳转(最后一题显示"提交")

3.3.5 数据保存策略

自动保存:

  • 每答完一题后自动保存到 Parse(防止中途退出丢失数据)
  • 保存方式:

    surveyLog.set('data', {
    ...surveyLog.get('data'),
    [questionId]: answer
    });
    await surveyLog.save();
    

完成标记:

  • 最后一题提交后设置 isCompleted = true
  • 设置 completedAt = new Date()
  • 设置 version = 1(用于后续问卷更新迭代)

3.4 结果页 (result)

3.4.1 页面布局

┌─────────────────────────────────┐
│  ✓ 问卷提交成功                   │
├─────────────────────────────────┤
│  感谢您的反馈!                   │
│  系统已记录您的能力画像,客服将根据│
│  您的专长合理分配订单。           │
│                                  │
│  【您的能力画像】                 │
│  ━━━━━━━━━━━━━━━━━━━━━━━       │
│  擅长风格: 奶油风、极简风、新中式  │
│  擅长空间: 常规住宅、特殊功能空间  │
│  技术优势: 渲染精度高、方案深化能力│
│  项目难度: 中等难度               │
│  周承接量: 1-2个(中等难度)       │
│  紧急订单: 愿意承接(每月不超过2次)│
│  进度同步: 关键节点同步           │
│  沟通方式: 群内文字、短语音、会议  │
│  ━━━━━━━━━━━━━━━━━━━━━━━       │
│                                  │
│  提示:您可以随时重新填写问卷更新信息│
│                                  │
│         [返回首页]  [重新填写]    │
└─────────────────────────────────┘

3.4.2 功能实现

  1. 结果展示:

    • 从 SurveyLog.data 读取答案
    • 格式化显示(选择题显示选项文本,文本题直接显示)
    • 提取关键信息形成"能力画像摘要"
  2. 权限控制:

    • 员工本人: 可查看完整结果
    • 组长: 可查看所有组员的完整结果
    • 管理员: 可查看所有员工的完整结果
    • 客服: 可查看受限字段(不含敏感信息)
  3. 操作按钮:

    • "返回首页": 返回企微端工作台
    • "重新填写": 重新进入答题页,更新问卷答案

四、员工详情弹窗集成

4.1 员工列表问卷状态显示

employees.component.html 的员工列表中增加问卷状态标识:

<!-- 员工列表表格 -->
<table class="employee-table">
  <thead>
    <tr>
      <th>头像</th>
      <th>姓名</th>
      <th>职位</th>
      <th>部门</th>
      <th>问卷状态</th>
      <th>操作</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let employee of employees" (click)="openEmployeeDetail(employee)">
      <td><img [src]="employee.avatar || 'assets/default-avatar.svg'" /></td>
      <td>{{ employee.name }}</td>
      <td>{{ employee.roleName }}</td>
      <td>{{ employee.department }}</td>
      <td>
        <span class="survey-badge" [class.completed]="employee.surveyCompleted">
          {{ employee.surveyCompleted ? '✓ 已填写' : '未填写' }}
        </span>
      </td>
      <td>
        <button (click)="openEmployeeDetail(employee); $event.stopPropagation()">
          查看详情
        </button>
      </td>
    </tr>
  </tbody>
</table>

4.2 员工详情弹窗增加问卷答案展示

employee-detail-modal.component.html 中增加问卷答案Tab:

<!-- 员工详情弹窗 -->
<div class="employee-detail-modal">
  <div class="modal-header">
    <h2>{{ employee?.name }} 的详细信息</h2>
    <button (click)="close()">×</button>
  </div>

  <div class="modal-tabs">
    <button [class.active]="currentTab === 'info'" (click)="currentTab = 'info'">
      基本信息
    </button>
    <button [class.active]="currentTab === 'survey'" (click)="currentTab = 'survey'">
      能力问卷
      @if (!surveyCompleted) {
        <span class="badge-warning">未填写</span>
      }
    </button>
    <button [class.active]="currentTab === 'projects'" (click)="currentTab = 'projects'">
      项目列表
    </button>
  </div>

  <div class="modal-body">
    <!-- 基本信息Tab -->
    @if (currentTab === 'info') {
      <div class="info-section">
        <div class="info-item">
          <label>手机号:</label>
          <span>{{ employee?.mobile }}</span>
        </div>
        <div class="info-item">
          <label>邮箱:</label>
          <span>{{ employee?.email }}</span>
        </div>
        <!-- 其他基本信息... -->
      </div>
    }

    <!-- 能力问卷Tab -->
    @if (currentTab === 'survey') {
      <div class="survey-section">
        @if (!surveyCompleted) {
          <div class="empty-state">
            <ion-icon name="document-text-outline"></ion-icon>
            <p>该员工尚未填写能力问卷</p>
            <button (click)="sendSurveyLink()">发送问卷链接</button>
          </div>
        } @else {
          <!-- 能力画像摘要 -->
          <div class="capability-summary">
            <h3>能力画像摘要</h3>
            <div class="summary-grid">
              <div class="summary-item">
                <label>擅长风格:</label>
                <div class="tags">
                  @for (style of surveyData.q1_expertise_styles; track style) {
                    <span class="tag">{{ style }}</span>
                  }
                </div>
              </div>
              <div class="summary-item">
                <label>技术优势:</label>
                <div class="tags">
                  @for (adv of surveyData.q3_technical_advantages; track adv) {
                    <span class="tag tag-primary">{{ adv }}</span>
                  }
                </div>
              </div>
              <div class="summary-item">
                <label>项目难度:</label>
                <span class="badge">{{ surveyData.q5_project_difficulty }}</span>
              </div>
              <div class="summary-item">
                <label>周承接量:</label>
                <span>{{ surveyData.q7_weekly_capacity }}</span>
              </div>
            </div>
          </div>

          <!-- 详细问卷答案 -->
          <div class="survey-details">
            <h3>详细问卷答案</h3>
            
            <div class="survey-section-group">
              <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-group">
              <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>

          <div class="survey-footer">
            <p class="survey-time">填写时间: {{ surveyCompletedAt | date:'yyyy-MM-dd HH:mm' }}</p>
            <button (click)="sendSurveyUpdateLink()">通知员工更新问卷</button>
          </div>
        }
      </div>
    }

    <!-- 项目列表Tab -->
    @if (currentTab === 'projects') {
      <!-- 项目列表内容... -->
    }
  </div>
</div>

4.3 查询员工问卷数据

employee-detail-modal.component.ts 中添加:

// 问卷状态
surveyCompleted: boolean = false;
surveyData: any = null;
surveyCompletedAt: Date | null = null;

async loadEmployeeSurvey() {
  if (!this.employee?.id) return;

  try {
    const query = new Parse.Query('SurveyLog');
    const profilePointer = {
      __type: 'Pointer',
      className: 'Profile',
      objectId: this.employee.id
    };
    query.equalTo('profile', profilePointer);
    query.equalTo('type', 'survey-profile');
    query.equalTo('isCompleted', true);
    query.descending('updatedAt'); // 获取最新版本
    
    const surveyLog = await query.first();

    if (surveyLog) {
      this.surveyCompleted = true;
      this.surveyData = surveyLog.get('data') || {};
      this.surveyCompletedAt = surveyLog.get('completedAt');
    }
  } catch (err) {
    console.error('查询员工问卷失败:', err);
  }
}

formatArrayAnswer(arr: string[] | string): string {
  if (Array.isArray(arr)) {
    return arr.join('、');
  }
  return arr || '未填写';
}

sendSurveyLink() {
  const surveyUrl = `${window.location.origin}/wxwork/${this.cid}/survey/profile`;
  // TODO: 通过企微发送问卷链接给员工
  window?.fmode?.alert('问卷链接已发送给员工');
}

sendSurveyUpdateLink() {
  const surveyUrl = `${window.location.origin}/wxwork/${this.cid}/survey/profile`;
  // TODO: 通过企微通知员工更新问卷
  window?.fmode?.alert('已通知员工更新问卷');
}

五、企业微信端员工认证集成

5.1 员工首次登录引导流程

[企微端首次登录] 
    ↓
[检测是否填写问卷]
    ↓ 否
[引导页:请完善您的能力画像]
    ↓
[跳转到问卷填写页]
    ↓
[填写完成]
    ↓
[进入工作台]

5.2 Wxwork端首页增加问卷入口

src/modules/wxwork/pages/home/home.component.ts 中添加:

async ngOnInit() {
  // 1. 企微身份认证
  await this.wxAuth.authenticate();
  
  // 2. 获取当前员工Profile
  this.currentProfile = await this.wxAuth.currentProfile();
  
  // 3. 检查是否填写问卷
  await this.checkSurveyStatus();
  
  // 4. 如果未填写,显示引导弹窗
  if (!this.surveyCompleted) {
    this.showSurveyGuide = true;
  }
}

async checkSurveyStatus() {
  if (!this.currentProfile?.id) return;

  try {
    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;
  } catch (err) {
    console.error('检查问卷状态失败:', err);
  }
}

goToSurvey() {
  this.router.navigate(['/wxwork', this.cid, 'survey', 'profile']);
}

5.3 问卷引导弹窗

<!-- 问卷引导弹窗 -->
@if (showSurveyGuide) {
  <div class="survey-guide-modal">
    <div class="modal-content">
      <div class="guide-icon">
        <ion-icon name="clipboard-outline"></ion-icon>
      </div>
      <h2>完善您的能力画像</h2>
      <p>
        为了更精准地为您匹配合适的项目,<br/>
        请花8-10分钟完成能力调研问卷。
      </p>
      <p class="guide-tip">
        您的选择将帮助我们合理分配订单,<br/>
        避免不匹配项目导致返工。
      </p>
      <div class="guide-actions">
        <button class="btn-secondary" (click)="showSurveyGuide = false">
          稍后填写
        </button>
        <button class="btn-primary" (click)="goToSurvey()">
          立即填写
        </button>
      </div>
    </div>
  </div>
}

六、技术实现要点

6.1 企微授权集成

import { WxworkAuth } from 'fmode-ng/core';

async ngOnInit() {
  // 1. 初始化企微授权
  const cid = this.route.snapshot.paramMap.get('cid') || '';
  this.wxAuth = new WxworkAuth({ cid, appId: 'crm' });

  // 2. 获取当前登录员工
  try {
    this.currentProfile = await this.wxAuth.currentProfile();
    console.log('当前员工:', this.currentProfile);
  } catch (error) {
    console.error('获取员工信息失败:', error);
    window?.fmode?.alert('无法识别您的身份,请通过企微重新进入');
    return;
  }

  // 3. 检查是否已填写问卷
  await this.checkExistingSurvey();
}

6.2 数据查询与保存

// 查询现有问卷
async checkExistingSurvey() {
  const query = new Parse.Query('SurveyLog');
  query.equalTo('profile', this.currentProfile.toPointer());
  query.equalTo('type', 'survey-profile');
  query.descending('updatedAt'); // 获取最新版本

  this.surveyLog = await query.first();

  if (this.surveyLog?.get('isCompleted')) {
    // 已完成,直接显示结果
    this.currentState = 'result';
  } else if (this.surveyLog) {
    // 未完成,恢复进度
    this.answers = this.surveyLog.get('data') || {};
    this.currentState = 'questionnaire';
  }
}

// 保存答案
async saveAnswer(questionId: string, answer: any) {
  if (!this.surveyLog) {
    // 首次保存,创建记录
    const SurveyLog = Parse.Object.extend('SurveyLog');
    this.surveyLog = new SurveyLog();

    const company = new Parse.Object('Company');
    company.id = localStorage.getItem('company') || '';

    this.surveyLog.set('company', company.toPointer());
    this.surveyLog.set('profile', this.currentProfile.toPointer());
    this.surveyLog.set('type', 'survey-profile');
    this.surveyLog.set('version', 1);
  }

  // 更新答案
  const data = this.surveyLog.get('data') || {};
  data[questionId] = answer;
  this.surveyLog.set('data', data);

  await this.surveyLog.save();
}

// 完成问卷
async completeSurvey() {
  if (!this.surveyLog) return;

  this.surveyLog.set('isCompleted', true);
  this.surveyLog.set('completedAt', new Date());
  await this.surveyLog.save();

  // 切换到结果页
  this.currentState = 'result';
}

6.3 多选题选择数量限制

// 多选题选择逻辑
toggleMultipleOption(option: string) {
  const question = this.getCurrentQuestion();
  if (!question) return;

  if (!this.answers[question.id]) {
    this.answers[question.id] = [];
  }

  const index = this.answers[question.id].indexOf(option);
  
  if (index > -1) {
    // 取消选择
    this.answers[question.id].splice(index, 1);
  } else {
    // 检查是否达到最大选择数
    if (question.maxSelections && 
        this.answers[question.id].length >= question.maxSelections) {
      window?.fmode?.alert(`最多只能选择 ${question.maxSelections} 项`);
      return;
    }
    // 添加选择
    this.answers[question.id].push(option);
  }
}

// 获取选择进度文本
getSelectionProgress(question: Question): string {
  if (!question.maxSelections) return '';
  const count = this.answers[question.id]?.length || 0;
  return `已选 ${count}/${question.maxSelections} 项`;
}

七、UI样式设计参考

7.1 样式继承客户问卷

  • 保持与客户问卷一致的视觉风格
  • 使用相同的配色方案(紫色主题渐变)
  • 保持相同的卡片布局和进度条样式
  • 统一的按钮风格和交互动效

7.2 差异化设计

  • 员工问卷使用蓝色或橙色作为强调色(区别于客户问卷的紫色)
  • 多选题显示选择进度(如"已选 2/3 项")
  • 能力画像摘要卡片使用徽章和标签展示
  • 案例展示使用独立的卡片样式

八、数据分析与应用

8.1 能力画像统计

在管理后台增加"团队能力分析"页面:

  • 风格分布: 统计团队擅长的风格分布(饼图)
  • 技术能力矩阵: 展示不同技术能力的人数分布(雷达图)
  • 承接能力: 统计团队总承接量和紧急订单承接意愿
  • 协作偏好: 分析团队沟通方式偏好,优化协作流程

8.2 智能订单分配

基于问卷数据优化订单分配逻辑:

// 订单智能匹配算法
function matchDesigner(project: Project): Profile[] {
  const requirements = project.requirements; // 项目需求
  const candidates: Profile[] = [];

  // 1. 查询所有已填写问卷的设计师
  const query = new Parse.Query('SurveyLog');
  query.equalTo('type', 'survey-profile');
  query.equalTo('isCompleted', true);
  query.include('profile');
  
  const surveys = await query.find();

  // 2. 匹配算法
  for (const survey of surveys) {
    const data = survey.get('data');
    let score = 0;

    // 风格匹配 (+30分)
    if (data.q1_expertise_styles?.includes(requirements.style)) {
      score += 30;
    }

    // 空间类型匹配 (+20分)
    if (data.q2_expertise_spaces?.includes(requirements.spaceType)) {
      score += 20;
    }

    // 项目难度匹配 (+20分)
    if (matchDifficulty(data.q5_project_difficulty, requirements.difficulty)) {
      score += 20;
    }

    // 当前负载 (+15分,负载越低分数越高)
    const currentLoad = await getDesignerLoad(survey.get('profile').id);
    const maxLoad = parseWeeklyCapacity(data.q7_weekly_capacity);
    score += (1 - currentLoad / maxLoad) * 15;

    // 紧急订单匹配 (+15分)
    if (requirements.isUrgent && data.q8_urgent_willingness === '愿意承接') {
      score += 15;
    }

    candidates.push({
      profile: survey.get('profile'),
      score: score,
      surveyData: data
    });
  }

  // 3. 按分数排序
  candidates.sort((a, b) => b.score - a.score);

  return candidates.slice(0, 5); // 返回前5名候选人
}

九、实施步骤

9.1 Phase 1: 核心组件开发

  1. 创建 ProfileSurveyComponent 组件
  2. 实现问卷欢迎页、答题页、结果页
  3. 集成企微身份认证
  4. 实现数据保存和查询逻辑

9.2 Phase 2: 后台管理集成

  1. 员工列表增加问卷状态标识
  2. 员工详情弹窗增加问卷答案Tab
  3. 实现问卷数据格式化展示
  4. 增加"发送问卷链接"功能

9.3 Phase 3: 企微端引导流程

  1. 首页增加问卷状态检测
  2. 实现问卷引导弹窗
  3. 在工作台增加"更新问卷"入口
  4. 问卷填写完成后的引导提示

9.4 Phase 4: 数据分析与应用

  1. 开发团队能力分析页面
  2. 优化订单智能分配算法
  3. 增加问卷数据导出功能
  4. 实现问卷版本迭代机制

十、测试用例

10.1 基本流程测试

  • 员工首次登录,显示问卷引导弹窗
  • 填写问卷,每题自动保存
  • 中途退出后恢复进度
  • 提交完成,显示结果页
  • 再次进入,直接显示结果页

10.2 题目交互测试

  • 单选题自动跳转下一题
  • 多选题达到上限后禁用其他选项
  • "其他"选项显示输入框
  • 必填题未填写时提示
  • 上一题按钮返回上一题

10.3 后台管理测试

  • 员工列表正确显示问卷状态
  • 员工详情弹窗正确显示问卷答案
  • 格式化展示多选答案
  • 案例卡片正确显示
  • "发送问卷链接"功能正常

10.4 权限测试

  • 员工本人可查看完整问卷
  • 组长可查看所有组员问卷
  • 管理员可查看所有员工问卷
  • 非授权人员无法访问他人问卷

十一、后续优化方向

11.1 问卷版本管理

  • 支持问卷题目迭代更新
  • 记录员工每次填写的历史版本
  • 对比不同版本的答案变化

11.2 智能推荐

  • 基于问卷数据推荐培训课程
  • 推荐适合的项目类型
  • 推荐协作伙伴(能力互补)

11.3 数据可视化

  • 个人能力雷达图
  • 团队能力分布热力图
  • 订单匹配成功率趋势

11.4 自动化流程

  • 新员工入职自动发送问卷
  • 定期(如每季度)提醒员工更新问卷
  • 问卷未填写自动限制接单

附录:问卷完整题目清单

一、核心技术能力(3题)

  1. 您最擅长的家装风格?(多选,优先选3项)
  2. 您擅长的空间类型?(多选)
  3. 技术能力优势?(多选,优先选2项)

二、项目经验与案例(5题)

  1. 代表性案例1:项目类型(文本)
  2. 代表性案例1:核心亮点(多行文本)
  3. 代表性案例2:项目类型(文本,选填)
  4. 代表性案例2:核心亮点(多行文本,选填)
  5. 可独立处理的项目难度?(单选)

三、项目承接偏好(4题)

  1. 您优先想承接的项目类型?(多选)
  2. 您可承接的单周期项目数量上限?(单选)
  3. 对紧急订单的承接意愿?(单选)
  4. 如愿意承接紧急订单,每月上限?(数字,选填)

四、协作与交付习惯(4题)

  1. 项目进度反馈与时效把控?(单选)
  2. 交付前确认流程?(多选)
  3. 对接需求时,您希望获取的客户信息清晰度?(单选)
  4. 若项目需协作,您倾向的对接方式?(多选)

五、问题应对与风险预警(3题)

  1. 您了解项目中的"敏感词"吗?(单选)
  2. 若某个环节出问题,您的处理流程?(单选)
  3. 您希望的代办任务通知方式?(多选)

六、补充说明(2题)

  1. 暂时无法承接的项目类型?(多行文本,选填)
  2. 其他项目相关补充?(多行文本,选填)

文档版本: v1.0
创建时间: 2025-10-30
作者: AI Assistant
审核状态: 待审核