# 身份激活页面 - 可选编辑表单(最终版) ## 功能概述 身份激活页面提供**智能表单**功能: - ✅ 所有字段自动从企微获取并预填充 - ✅ 所有字段均可由用户修改 - ✅ 只有**真实姓名**为必填项 - ✅ 部门、角色、手机号为可选项 ## 实现日期 2025年11月3日 ## 字段说明 | 字段名 | 类型 | 必填 | 自动获取 | 说明 | |--------|------|------|----------|------| | **真实姓名** | 文本输入 | ✅ 是 | ✅ 是 | 从企微自动填充,用户可修改 | | **所属部门** | 下拉选择 | ❌ 否 | ✅ 是 | 使用 `getDepartment()` 获取初始值 | | **职位角色** | 下拉选择 | ❌ 否 | ✅ 是 | 使用 `getUserRole()` 获取初始值 | | **手机号** | 电话输入 | ❌ 否 | ✅ 是 | 从企微自动填充,用户可修改 | | **员工ID** | 只读显示 | - | ✅ 是 | 企微 userid,不可编辑 | ## 界面效果 ``` ┌─────────────────────────────────────┐ │ 用户身份确认 │ │ 请确认您的身份信息 │ ├─────────────────────────────────────┤ │ [头像] │ │ 张设计师 │ │ 组员 │ ├─────────────────────────────────────┤ │ 真实姓名 * │ │ [张设计师 ] ← 自动填充 │ │ │ │ 所属部门 │ │ [▼ 设计部 ] ← 自动选中 │ │ • 设计部 │ │ • 建模部 │ │ • 渲染部... │ │ │ │ 职位角色 │ │ [▼ 组员 ] ← 自动选中 │ │ • 组员 │ │ • 组长 │ │ • 主管... │ │ │ │ 手机号 │ │ [13800138000 ] ← 自动填充 │ │ │ │ 员工ID │ │ [test_user_001 ] ← 只读 │ ├─────────────────────────────────────┤ │ [✓ 确认身份] │ └─────────────────────────────────────┘ ``` ## 核心实现 ### 1. 数据模型(TypeScript) **表单数据**(第52-58行): ```typescript formData = { realname: '', // 真实姓名(必填) department: '', // 所属部门(可选) roleName: '', // 职位角色(可选) mobile: '' // 手机号(可选) }; ``` **部门列表**(第60-69行): ```typescript departmentList = [ '设计部', '建模部', '渲染部', '软装部', '后期部', '综合部', '管理部' ]; ``` **角色列表**(第71-77行): ```typescript roleList = [ '组员', '组长', '主管', '经理' ]; ``` ### 2. 自动填充逻辑 **使用原有方法获取初始值**(第163-177行): ```typescript private populateFormData(): void { // 姓名:从Profile或企微获取 this.formData.realname = this.profile?.get('realname') || this.profile?.get('name') || this.userInfo?.name || ''; // 手机号:从Profile或企微获取 this.formData.mobile = this.profile?.get('mobile') || this.userInfo?.mobile || ''; // 部门:使用原有方法获取(智能处理多种格式) this.formData.department = this.getDepartment(); // 角色:使用原有方法获取 this.formData.roleName = this.getUserRole(); console.log('📝 自动填充表单数据:', this.formData); } ``` ### 3. 表单验证 **只验证必填字段:姓名**(第222-228行): ```typescript // 表单验证 if (!this.formData.realname?.trim()) { alert('请填写您的真实姓名'); return; } // 部门和角色为可选,不做必填验证 ``` ### 4. 数据保存 **保存用户选择的所有信息**(第248-278行): ```typescript // 设置激活标记并保存表单数据 if (this.profile) { this.profile.set('isActivated', true); this.profile.set('activatedAt', new Date()); // 保存用户编辑的信息 this.profile.set('realname', this.formData.realname); this.profile.set('name', this.formData.realname); // 同时更新name字段 // 保存部门和角色(可选) if (this.formData.department) { this.profile.set('department', this.formData.department); this.profile.set('departmentName', this.formData.department); } if (this.formData.roleName) { this.profile.set('roleName', this.formData.roleName); } // 保存手机号(可选) if (this.formData.mobile) { this.profile.set('mobile', this.formData.mobile); } await this.profile.save(); } ``` ### 5. HTML 表单结构 **真实姓名**(第47-58行): ```html
``` **所属部门**(第60-72行): ```html
``` **职位角色**(第74-86行): ```html
``` ## 工作流程 ### 初始化流程 ``` 用户访问激活页面 ↓ initAuth() - 初始化企微认证 ↓ 获取企微用户信息 ├─ userid ├─ name ├─ mobile ├─ department (多种格式) └─ avatar ↓ checkActivationStatus() - 查询Profile ↓ populateFormData() - 自动填充表单 ├─ formData.realname ← name ├─ formData.mobile ← mobile ├─ formData.department ← getDepartment() 🔑 └─ formData.roleName ← getUserRole() 🔑 ↓ 显示表单(所有字段已填充) ``` ### 用户操作流程 ``` 表单已自动填充 ↓ 用户查看信息 ↓ 可选操作: ├─ 修改姓名 ✏️ ├─ 重新选择部门 ✏️ ├─ 重新选择角色 ✏️ ├─ 修改手机号 ✏️ └─ 或保持默认值 ✅ ↓ 点击"确认身份" ↓ 验证姓名不为空 ↓ 保存所有字段到Profile ↓ 激活成功 ``` ## 关键方法复用 ### getDepartment() 方法 **位置**:第368-393行 **功能**:智能处理企微部门信息的多种格式 ```typescript getDepartment(): string { const dept = this.userInfo?.department; // 处理数组格式 if (Array.isArray(dept) && dept.length > 0) { return `部门${dept[0]}`; } // 处理对象格式 if (dept && typeof dept === 'object' && !Array.isArray(dept)) { return dept.name || dept.departmentName || '未知部门'; } // 处理字符串格式 if (typeof dept === 'string') { return dept; } // 从 Profile 获取部门信息 const profileDept = this.profile?.get('department') || this.profile?.get('departmentName'); if (profileDept) { return typeof profileDept === 'string' ? profileDept : (profileDept.name || '未知部门'); } return '未分配部门'; } ``` **优势**: - ✅ 兼容企微的多种部门数据格式 - ✅ 多级降级逻辑,确保总能获取到值 - ✅ 优先使用Profile中的部门信息(用户之前保存的) ### getUserRole() 方法 **位置**:第361-363行 **功能**:获取用户角色 ```typescript getUserRole(): string { return this.profile?.get('roleName') || '员工'; } ``` **优势**: - ✅ 优先从Profile获取(用户之前保存的角色) - ✅ 默认值为"员工" - ✅ 简洁高效 ## 使用场景 ### 场景1:企微信息完整准确 ``` 企微返回: name: "王刚" department: "设计部" roleName: "组员" mobile: "13800138000" ↓ 表单自动填充: 真实姓名: [王刚] 所属部门: [设计部] (已选中) 职位角色: [组员] (已选中) 手机号: [13800138000] ↓ 用户点击"确认身份" ↓ ✅ 激活成功(无需修改) ``` ### 场景2:部门信息为数组格式 ``` 企微返回: department: [1, 2] ↓ getDepartment() 处理: return "部门1" ↓ 表单显示: 所属部门: [部门1] (已选中) ↓ 用户可以: • 保持"部门1" • 或选择"设计部"等其他部门 ↓ ✅ 保存用户选择 ``` ### 场景3:用户修改信息 ``` 企微自动填充: 真实姓名: [测试员工] 所属部门: [部门1] 职位角色: [员工] ↓ 用户修改: 真实姓名: [李设计师] ✏️ 所属部门: [设计部] ✏️ 职位角色: [组员] ✏️ ↓ 点击"确认身份" ↓ ✅ 保存修改后的信息 ``` ### 场景4:部门角色留空 ``` 用户操作: 真实姓名: [王刚] ✅ (必填) 所属部门: [请选择部门(可选)] (未选择) 职位角色: [请选择角色(可选)] (未选择) 手机号: [] (未填写) ↓ 点击"确认身份" ↓ ✅ 激活成功 ↓ Profile保存: realname: "王刚" department: null (未设置) roleName: null (未设置) mobile: null (未设置) ``` ## 数据库保存结果 ### 示例1:所有字段填写完整 ```javascript { // 必填字段 realname: "王刚", name: "王刚", // 可选字段(用户填写) department: "设计部", departmentName: "设计部", roleName: "组员", mobile: "13800138000", // 企微字段(自动同步) userid: "WangGang001", avatar: "https://...", // 系统字段 isActivated: true, activatedAt: "2025-11-03T12:00:00.000Z" } ``` ### 示例2:仅填写必填字段 ```javascript { // 必填字段 realname: "李设计师", name: "李设计师", // 可选字段(未填写,不保存) // department: undefined // departmentName: undefined // roleName: undefined // mobile: undefined // 企微字段 userid: "test_user_001", // 系统字段 isActivated: true, activatedAt: "2025-11-03T12:00:00.000Z" } ``` ## 核心优势 ### ✅ 智能自动化 - **自动获取**:所有字段从企微自动获取 - **智能处理**:使用原有的 `getDepartment()` 和 `getUserRole()` 方法 - **多格式兼容**:支持企微的多种数据格式 ### ✅ 灵活可选 - **必填最小化**:只有姓名为必填 - **可选字段**:部门、角色、手机号均可选 - **自由修改**:所有字段均可由用户修改 ### ✅ 用户体验 - **零输入启动**:字段已预填充,大部分情况一键确认 - **清晰提示**:"请选择部门(可选)"明确告知可选 - **即时反馈**:头像下方的角色实时更新 ### ✅ 数据质量 - **智能降级**:多级数据源降级,确保有值 - **条件保存**:只保存用户填写的可选字段 - **字段同步**:同时更新 `name/realname`、`department/departmentName` ## 样式说明 表单样式已在 `profile-activation.component.scss` 中定义: **输入框样式**: ```scss .form-input { width: 100%; padding: 12px 16px; border: 1px solid #e0e0e0; border-radius: 8px; &:focus { border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } } ``` **下拉框样式**: ```scss .form-select { appearance: none; background-image: url("data:image/svg+xml,..."); // 自定义箭头 padding-right: 36px; cursor: pointer; } ``` **只读字段样式**: ```scss .form-group.readonly { .readonly-value { padding: 12px 16px; background: #f5f5f5; border-radius: 8px; color: #666; } } ``` ## 测试建议 ### 功能测试 - [ ] 访问激活页面,所有字段已自动填充 - [ ] 修改姓名后保存成功 - [ ] 选择不同部门后保存成功 - [ ] 选择不同角色后保存成功 - [ ] 修改手机号后保存成功 - [ ] 清空姓名后无法提交(必填验证) - [ ] 部门和角色留空仍可提交(可选) - [ ] 头像下方角色实时更新 ### 数据测试 - [ ] 企微返回数组格式部门,正确处理 - [ ] 企微返回对象格式部门,正确处理 - [ ] 企微返回字符串格式部门,正确处理 - [ ] Profile有部门数据,优先使用Profile数据 - [ ] 可选字段未填写时,不保存到数据库 ### UI测试 - [ ] 下拉框显示自定义箭头 - [ ] 输入框聚焦时紫色高亮 - [ ] 必填字段有红色 `*` 标记 - [ ] 可选字段有"(可选)"提示 - [ ] 只读字段(员工ID)灰色背景 - [ ] 移动端布局正常(16px字体) ## 后续优化建议 ### 1. 从数据库加载部门列表 ```typescript async loadDepartments() { const Parse = FmodeParse.with('nova'); const query = new Parse.Query('Department'); const departments = await query.find(); this.departmentList = departments.map(d => d.get('name')); } ``` ### 2. 添加部门搜索功能 ```html ``` ### 3. 角色权限关联 ```typescript // 根据选择的角色显示不同的权限说明 getRoleDescription(role: string): string { const descriptions = { '组员': '普通设计师,负责具体项目执行', '组长': '小组负责人,管理团队成员', '主管': '部门主管,负责部门整体运营', '经理': '部门经理,负责战略决策' }; return descriptions[role] || ''; } ``` ### 4. 手机号格式验证 ```typescript validateMobile(): boolean { if (!this.formData.mobile) return true; // 可选字段 return /^1[3-9]\d{9}$/.test(this.formData.mobile); } ``` ### 5. 表单防抖保存 ```typescript // 用户修改后自动保存草稿 private saveDraft = debounce(() => { localStorage.setItem('activation_draft', JSON.stringify(this.formData)); }, 500); ``` ## 总结 最终方案实现了**"智能自动 + 灵活可选"**的完美平衡: - 🚀 **高效**:所有字段自动获取,大部分情况一键确认 - 🔄 **复用**:使用原有的 `getDepartment()` 和 `getUserRole()` 方法 - ✏️ **灵活**:所有字段均可修改,满足个性化需求 - 📋 **可选**:只有姓名必填,其他字段可选 - 🎯 **准确**:智能处理多种数据格式,确保数据质量 - 🎨 **友好**:清晰的视觉提示和即时反馈 这种设计既保证了激活流程的便捷性,又给用户提供了充分的自主权,是企业内部系统的最佳实践。