|
@@ -1,108 +1,649 @@
|
|
|
|
|
+# 项目问卷组件产品需求文档
|
|
|
|
|
|
|
|
-# 项目问卷组件及功能描述
|
|
|
|
|
-# 数据范式
|
|
|
|
|
-SurveyLog 问卷结果
|
|
|
|
|
-- contact Pointer<ContactInfo> 提交联系人
|
|
|
|
|
-- project Pointer<Project> 关联项目
|
|
|
|
|
-- profile Pointer<Profile> 提交员工
|
|
|
|
|
-- company Pointer<Company> 所属帐套 localStorage.get("company")
|
|
|
|
|
-- data Object 问卷的结果存储
|
|
|
|
|
-- type String
|
|
|
|
|
- - survey-project 项目问卷
|
|
|
|
|
- - survey-contact 联系人问卷
|
|
|
|
|
- - survey-profile 员工问卷
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# 核心组件
|
|
|
|
|
-- 请您根据Project.id及currentContact.id持久化该项目该联系人本次合作问卷,存储在SurveyLog表
|
|
|
|
|
-- 请您设计project-survey组件页面,专用于采集本项目用户的需求信息。
|
|
|
|
|
-- 页面需要有三种状态:欢迎阶段、答题阶段、结果报告
|
|
|
|
|
- - 初始状态,欢迎阶段,通过/survey/project/:pid进入,携带WxworkAuthGuard守卫
|
|
|
|
|
- - 页面初始化中采用 rules/wxwork/auth.md 外部联系人直接通过.currentContact()获取外部用户信息
|
|
|
|
|
- - 此处contact填写的不同,加载和保存的SurveyLog也是不同的,有一个项目多个客户联系人填写的情况
|
|
|
|
|
- - 欢迎页主要介绍问卷目标,亲切引导用户填写,帮助项目更好开展
|
|
|
|
|
- - 显示当前外部联系人的currentContact加载的头像与名称
|
|
|
|
|
- - 有一个开始按钮,点击后进入答题阶段
|
|
|
|
|
- - 答题阶段
|
|
|
|
|
- - 单屏显示一道题目,单选点击若非其他可以切换下一题,若是其他需要用户补充内容再进入下一题
|
|
|
|
|
- - 若是问答题则需要用户填写完成,手动点击下一题
|
|
|
|
|
- - 其中涉及到姓名、手机等选项,若ContactInfo表中已存在则隐藏,方便客户快速完成
|
|
|
|
|
- - 结果展示
|
|
|
|
|
- - 当查询到SurveyLog的内容完整,则默认展示结果,方便客服、组员查看。
|
|
|
|
|
-
|
|
|
|
|
-# 配合使用
|
|
|
|
|
-- 项目详情页的问卷发送与查看
|
|
|
|
|
- - src/modules/project/pages/project-detail/project-detail.component.ts 项目详情页中,客户联系人卡片的位置,需要显示问卷的填写状态
|
|
|
|
|
- - 如果已填写,打开后,弹出加载项目问卷project-survey组件,展示结果
|
|
|
|
|
- - 如果未填写,提示用户点击,发送问卷,调用WxworkSDK中的ww属性进行发送
|
|
|
|
|
- ``` ts
|
|
|
|
|
- import { WxworkSDK, WxworkCorp, WxworkCurrentChat } from 'fmode-ng/core';
|
|
|
|
|
-
|
|
|
|
|
- // 属性声明
|
|
|
|
|
- wework: WxworkSDK;
|
|
|
|
|
- // 初始化
|
|
|
|
|
- let cid = localStorage.getItem("company") || "cDL6R1hgSi"
|
|
|
|
|
- this.wework = new WxworkSDK({ cid: this.cid, appId: 'crm' });
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // 具体方法
|
|
|
|
|
- thi.wework.ww.openExistedChatWithMsg({
|
|
|
|
|
- chatId: 'chatId123', // 当前项目详情页的groupChat.get("chat_id")
|
|
|
|
|
- msg: { // 构造一个美观漂亮的链接分享,邀请客户填写
|
|
|
|
|
- msgtype: 'link',
|
|
|
|
|
- link: {
|
|
|
|
|
- title: 'title1',
|
|
|
|
|
- desc: 'desc1',
|
|
|
|
|
- url: 'link1', // 参考组件路由规则
|
|
|
|
|
- imgUrl: 'imgurl1' // 可用 assets/logo.jpg
|
|
|
|
|
- }
|
|
|
|
|
|
|
+## 一、概述
|
|
|
|
|
+
|
|
|
|
|
+### 1.1 功能定位
|
|
|
|
|
+项目问卷是家装效果图服务的**初次合作需求调研工具**,通过精简的选择式问卷快速了解客户需求、服务偏好和协作习惯,帮助团队更精准地提供服务。
|
|
|
|
|
+
|
|
|
|
|
+### 1.2 业务价值
|
|
|
|
|
+- **客户视角**: 5分钟快速完成,明确表达需求偏好,减少后期沟通成本
|
|
|
|
|
+- **服务视角**: 提前了解客户侧重点,制定针对性服务方案,提升满意度
|
|
|
|
|
+- **数据视角**: 积累客户需求数据,优化服务流程和质量管控点
|
|
|
|
|
+
|
|
|
|
|
+### 1.3 应用场景
|
|
|
|
|
+1. **项目启动前**: 客服在项目订单分配阶段,发送问卷给客户填写
|
|
|
|
|
+2. **群聊分享**: 通过企微群聊直接发送问卷链接,客户点击即可填写
|
|
|
|
|
+3. **多客户项目**: 支持一个项目多个客户联系人分别填写(如公司项目的多个负责人)
|
|
|
|
|
+4. **结果查看**: 客服/组员/组长可随时查看客户已填写的问卷结果
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 二、数据范式
|
|
|
|
|
+
|
|
|
|
|
+### 2.1 SurveyLog 问卷结果表
|
|
|
|
|
+
|
|
|
|
|
+| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|
|
|
|
|
+|--------|------|------|------|--------|
|
|
|
|
|
+| objectId | String | 是 | 主键ID | "survey001" |
|
|
|
|
|
+| **contact** | **Pointer** | **是** | **提交联系人** | **→ ContactInfo** |
|
|
|
|
|
+| **project** | **Pointer** | **是** | **关联项目** | **→ Project** |
|
|
|
|
|
+| profile | Pointer | 否 | 提交员工(内部员工填写时使用) | → Profile |
|
|
|
|
|
+| **company** | **Pointer** | **是** | **所属帐套** | **→ Company** |
|
|
|
|
|
+| **type** | **String** | **是** | **问卷类型** | **"survey-project"** |
|
|
|
|
|
+| **data** | **Object** | **是** | **问卷结果** | **{q1: "答案1", ...}** |
|
|
|
|
|
+| isCompleted | Boolean | 否 | 是否完整填写 | true |
|
|
|
|
|
+| completedAt | Date | 否 | 完成时间 | 2024-12-01T10:00:00.000Z |
|
|
|
|
|
+| isDeleted | Boolean | 否 | 软删除标记 | false |
|
|
|
|
|
+| createdAt | Date | 自动 | 创建时间 | 2024-12-01T09:00:00.000Z |
|
|
|
|
|
+| updatedAt | Date | 自动 | 更新时间 | 2024-12-01T10:00:00.000Z |
|
|
|
|
|
+
|
|
|
|
|
+**type 枚举值**:
|
|
|
|
|
+- `survey-project`: 项目问卷
|
|
|
|
|
+- `survey-contact`: 联系人问卷(暂未实现)
|
|
|
|
|
+- `survey-profile`: 员工问卷(暂未实现)
|
|
|
|
|
+
|
|
|
|
|
+**data 字段结构示例**:
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "q1_service_type": "效果图+技术配合",
|
|
|
|
|
+ "q2_space_count": "3",
|
|
|
|
|
+ "q2_space_types": "客厅/主卧/儿童房",
|
|
|
|
|
+ "q3_value_focus": ["细节写实度", "视觉吸引力"],
|
|
|
|
|
+ "q4_tech_support": "需要",
|
|
|
|
|
+ "q4_tech_focus": ["材质搭配", "灯光布局"],
|
|
|
|
|
+ "q5_cooperation_mode": "前期多沟通",
|
|
|
|
|
+ "q6_attention_points": ["软装色调易偏差"],
|
|
|
|
|
+ "q7_special_requirements": "业主喜欢暖色调,注意避免冷色",
|
|
|
|
|
+ "q8_has_reference": "有",
|
|
|
|
|
+ "contact_name": "李总",
|
|
|
|
|
+ "contact_phone": "13800138000"
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 三、核心组件设计
|
|
|
|
|
+
|
|
|
|
|
+### 3.1 ProjectSurveyComponent 项目问卷组件
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.1 路由配置
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 路由: /wxwork/:cid/survey/project/:projectId
|
|
|
|
|
+{
|
|
|
|
|
+ path: 'wxwork/:cid',
|
|
|
|
|
+ children: [
|
|
|
|
|
+ {
|
|
|
|
|
+ path: 'survey/project/:projectId',
|
|
|
|
|
+ loadComponent: () => import('../modules/project/pages/project-survey/project-survey.component'),
|
|
|
|
|
+ title: '项目需求调查'
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.2 组件状态机
|
|
|
|
|
+组件包含三种状态,通过 `currentState` 控制:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+type SurveyState = 'welcome' | 'questionnaire' | 'result';
|
|
|
|
|
+
|
|
|
|
|
+currentState: SurveyState = 'welcome';
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**状态转换流程**:
|
|
|
|
|
+```
|
|
|
|
|
+[欢迎页] --点击开始--> [答题页] --提交完成--> [结果页]
|
|
|
|
|
+ ↑ ↓
|
|
|
|
|
+ └──────────────── 查看结果 ──────────────────┘
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 欢迎页 (welcome)
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.1 页面布局
|
|
|
|
|
+```
|
|
|
|
|
+┌─────────────────────────────────┐
|
|
|
|
|
+│ 问卷欢迎页 │
|
|
|
|
|
+├─────────────────────────────────┤
|
|
|
|
|
+│ [用户头像] │
|
|
|
|
|
+│ 您好,李总 │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ 《家装效果图服务初次合作需求调查表》 │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ 尊敬的伙伴: │
|
|
|
|
|
+│ 为让本次效果图服务更贴合您的工作节 │
|
|
|
|
|
+│ 奏与核心需求,我们准备了简短选择式 │
|
|
|
|
|
+│ 问卷,您的偏好将直接帮我们校准服务 │
|
|
|
|
|
+│ 方向,感谢支持! │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ • 预计用时: 3-5分钟 │
|
|
|
|
|
+│ • 题目数量: 8题 │
|
|
|
|
|
+│ • 题型: 选择题为主 │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ [开始填写] │
|
|
|
|
|
+└─────────────────────────────────┘
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.2 功能实现
|
|
|
|
|
+1. **用户识别**:
|
|
|
|
|
+ - 通过 `WxworkAuth.currentContact()` 获取当前外部联系人
|
|
|
|
|
+ - 显示联系人头像和名称
|
|
|
|
|
+ - 记录 `contact.id` 用于后续保存
|
|
|
|
|
+
|
|
|
|
|
+2. **数据检查**:
|
|
|
|
|
+ - 组件初始化时查询 SurveyLog 表
|
|
|
|
|
+ - 条件: `project == projectId AND contact == contactId`
|
|
|
|
|
+ - 如果已存在且 `isCompleted == true`,直接跳转到结果页
|
|
|
|
|
+
|
|
|
|
|
+3. **开始按钮**:
|
|
|
|
|
+ - 点击后执行 `startSurvey()`
|
|
|
|
|
+ - 切换状态: `currentState = 'questionnaire'`
|
|
|
|
|
+ - 初始化题目索引: `currentQuestionIndex = 0`
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### 3.3 答题页 (questionnaire)
|
|
|
|
|
+
|
|
|
|
|
+#### 3.3.1 页面布局
|
|
|
|
|
+```
|
|
|
|
|
+┌─────────────────────────────────┐
|
|
|
|
|
+│ 进度: 1/8 ●●○○○○○○ │
|
|
|
|
|
+├─────────────────────────────────┤
|
|
|
|
|
+│ 一、基础需求 │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ 1. 本次您需要的核心服务是? │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ ○ 纯效果图渲染 │
|
|
|
|
|
+│ ● 效果图+技术配合 │
|
|
|
|
|
+│ ○ 其他补充: [____________] │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ [← 上一题] [下一题 →] │
|
|
|
|
|
+└─────────────────────────────────┘
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.3.2 题目数据结构
|
|
|
|
|
+```typescript
|
|
|
|
|
+interface Question {
|
|
|
|
|
+ id: string; // 题目ID,如 "q1", "q2"
|
|
|
|
|
+ section: string; // 章节,如 "基础需求", "核心侧重"
|
|
|
|
|
+ title: string; // 题目文本
|
|
|
|
|
+ type: 'single' | 'multiple' | 'text' | 'number'; // 题型
|
|
|
|
|
+ options?: string[]; // 选项列表
|
|
|
|
|
+ hasOther?: boolean; // 是否有"其他"选项
|
|
|
|
|
+ required?: boolean; // 是否必填
|
|
|
|
|
+ skipCondition?: (contact: any) => boolean; // 跳过条件
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.3.3 题目列表
|
|
|
|
|
+```typescript
|
|
|
|
|
+const questions: Question[] = [
|
|
|
|
|
+ // 一、基础需求
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q1',
|
|
|
|
|
+ section: '基础需求',
|
|
|
|
|
+ title: '本次您需要的核心服务是?',
|
|
|
|
|
+ type: 'single',
|
|
|
|
|
+ options: ['纯效果图渲染', '效果图+技术配合'],
|
|
|
|
|
+ hasOther: true,
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q2',
|
|
|
|
|
+ section: '基础需求',
|
|
|
|
|
+ title: '需覆盖的关键空间数量及类型?',
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ placeholder: '例: 3个,客厅/主卧/儿童房',
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 二、核心侧重
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q3',
|
|
|
|
|
+ section: '核心侧重',
|
|
|
|
|
+ title: '您更希望本次效果图突出哪些价值?(可多选2-3项)',
|
|
|
|
|
+ type: 'multiple',
|
|
|
|
|
+ options: ['细节写实度', '视觉吸引力', '风格适配性'],
|
|
|
|
|
+ hasOther: true,
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q4',
|
|
|
|
|
+ section: '核心侧重',
|
|
|
|
|
+ title: '关于方案建议,是否需要我们技术团队配合?',
|
|
|
|
|
+ type: 'single',
|
|
|
|
|
+ options: ['需要', '暂不需要'],
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 三、协作节奏
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q5',
|
|
|
|
|
+ section: '协作节奏',
|
|
|
|
|
+ title: '您偏好的服务协作方式是?',
|
|
|
|
|
+ type: 'single',
|
|
|
|
|
+ options: ['前期多沟通', '先出初版再修改', '灵活协调'],
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 四、特殊提醒
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q6',
|
|
|
|
|
+ section: '特殊提醒',
|
|
|
|
|
+ title: '过往合作中,是否有需要特别注意的点?(可多选)',
|
|
|
|
|
+ type: 'multiple',
|
|
|
|
|
+ options: ['软装色调易偏差', '建模细节需盯控'],
|
|
|
|
|
+ hasOther: true
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q7',
|
|
|
|
|
+ section: '特殊提醒',
|
|
|
|
|
+ title: '本次项目是否有特殊要求?(如业主禁忌、重点展示点)',
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ placeholder: '请输入特殊要求...'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'q8',
|
|
|
|
|
+ section: '特殊提醒',
|
|
|
|
|
+ title: '是否有参考素材?(如风格图、实景图)',
|
|
|
|
|
+ type: 'single',
|
|
|
|
|
+ options: ['有(后续群内发送)', '无(需求已清晰)']
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 联系信息(自动跳过)
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'contact_name',
|
|
|
|
|
+ section: '联系信息',
|
|
|
|
|
+ title: '对接人姓名',
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ skipCondition: (contact) => !!contact?.get('realname')
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'contact_phone',
|
|
|
|
|
+ section: '联系信息',
|
|
|
|
|
+ title: '对接人电话',
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ skipCondition: (contact) => !!contact?.get('mobile')
|
|
|
|
|
+ }
|
|
|
|
|
+];
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.3.4 答题交互逻辑
|
|
|
|
|
+
|
|
|
|
|
+1. **单选题**:
|
|
|
|
|
+ - 点击选项后自动保存答案到 `answers[questionId]`
|
|
|
|
|
+ - 如果不是"其他"选项,自动跳转下一题
|
|
|
|
|
+ - 如果是"其他"选项,显示输入框,输入完成后需手动点击"下一题"
|
|
|
|
|
+
|
|
|
|
|
+2. **多选题**:
|
|
|
|
|
+ - 可选择多个选项
|
|
|
|
|
+ - 点击"下一题"后保存并跳转
|
|
|
|
|
+
|
|
|
|
|
+3. **文本题/数字题**:
|
|
|
|
|
+ - 输入完成后点击"下一题"
|
|
|
|
|
+
|
|
|
|
|
+4. **题目跳过**:
|
|
|
|
|
+ - 如果 `skipCondition` 返回 `true`,自动跳过该题
|
|
|
|
|
+ - 例如: ContactInfo 已有手机号,跳过手机号填写
|
|
|
|
|
+
|
|
|
|
|
+5. **进度指示**:
|
|
|
|
|
+ - 顶部显示进度条: `currentQuestionIndex / totalQuestions`
|
|
|
|
|
+ - 显示当前章节名称
|
|
|
|
|
+
|
|
|
|
|
+6. **导航按钮**:
|
|
|
|
|
+ - "上一题": 返回上一题,可修改答案
|
|
|
|
|
+ - "下一题": 保存当前答案并跳转(最后一题显示"提交")
|
|
|
|
|
+
|
|
|
|
|
+#### 3.3.5 数据保存策略
|
|
|
|
|
+
|
|
|
|
|
+**自动保存**:
|
|
|
|
|
+- 每答完一题后自动保存到 Parse (防止中途退出丢失数据)
|
|
|
|
|
+- 保存方式:
|
|
|
|
|
+ ```typescript
|
|
|
|
|
+ surveyLog.set('data', {
|
|
|
|
|
+ ...surveyLog.get('data'),
|
|
|
|
|
+ [questionId]: answer
|
|
|
|
|
+ });
|
|
|
|
|
+ await surveyLog.save();
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+**完成标记**:
|
|
|
|
|
+- 最后一题提交后设置 `isCompleted = true`
|
|
|
|
|
+- 设置 `completedAt = new Date()`
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### 3.4 结果页 (result)
|
|
|
|
|
+
|
|
|
|
|
+#### 3.4.1 页面布局
|
|
|
|
|
+```
|
|
|
|
|
+┌─────────────────────────────────┐
|
|
|
|
|
+│ ✓ 问卷提交成功 │
|
|
|
|
|
+├─────────────────────────────────┤
|
|
|
|
|
+│ 感谢您的反馈! │
|
|
|
|
|
+│ 我们将根据您的选择制定服务方案 │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ 【您的答卷】 │
|
|
|
|
|
+│ ━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
|
|
|
+│ 核心服务: 效果图+技术配合 │
|
|
|
|
|
+│ 空间数量: 3个(客厅/主卧/儿童房) │
|
|
|
|
|
+│ 价值侧重: 细节写实度、视觉吸引力 │
|
|
|
|
|
+│ 技术配合: 需要(材质搭配、灯光布局) │
|
|
|
|
|
+│ 协作方式: 前期多沟通 │
|
|
|
|
|
+│ 注意事项: 软装色调易偏差 │
|
|
|
|
|
+│ 特殊要求: 业主喜欢暖色调 │
|
|
|
|
|
+│ 参考素材: 有(后续群内发送) │
|
|
|
|
|
+│ ━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
|
|
|
+│ 对接人: 李总 │
|
|
|
|
|
+│ 电话: 138****8000 │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ [返回项目] │
|
|
|
|
|
+└─────────────────────────────────┘
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.4.2 功能实现
|
|
|
|
|
+1. **结果展示**:
|
|
|
|
|
+ - 从 SurveyLog.data 读取答案
|
|
|
|
|
+ - 格式化显示(选择题显示选项文本,文本题直接显示)
|
|
|
|
|
+ - 手机号脱敏显示(中间4位显示为 ****)
|
|
|
|
|
+
|
|
|
|
|
+2. **权限控制**:
|
|
|
|
|
+ - 客户本人: 可查看完整结果(包括完整手机号)
|
|
|
|
|
+ - 客服/组员/组长: 可查看完整结果
|
|
|
|
|
+ - 其他外部联系人: 无权查看
|
|
|
|
|
+
|
|
|
|
|
+3. **返回按钮**:
|
|
|
|
|
+ - 返回项目详情页
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 四、项目详情页集成
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 客户卡片问卷状态显示
|
|
|
|
|
+
|
|
|
|
|
+在 `project-detail.component.html` 的客户联系人卡片区域添加问卷入口:
|
|
|
|
|
+
|
|
|
|
|
+```html
|
|
|
|
|
+<!-- 客户信息卡片 -->
|
|
|
|
|
+<div class="contact-card">
|
|
|
|
|
+ <div class="contact-info" (click)="openContactPanel()">
|
|
|
|
|
+ <img [src]="contact?.get('data')?.avatar || 'assets/default-avatar.png'" />
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h3>{{ contact?.get('realname') || contact?.get('name') }}</h3>
|
|
|
|
|
+ <p>{{ canViewCustomerPhone ? contact?.get('mobile') : '***' }}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 问卷状态 -->
|
|
|
|
|
+ <div class="survey-status" (click)="handleSurveyClick($event)">
|
|
|
|
|
+ <ion-icon [name]="surveyStatus.icon"></ion-icon>
|
|
|
|
|
+ <span>{{ surveyStatus.text }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 问卷状态查询
|
|
|
|
|
+
|
|
|
|
|
+在 `project-detail.component.ts` 中添加:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 问卷状态
|
|
|
|
|
+surveyStatus: {
|
|
|
|
|
+ filled: boolean;
|
|
|
|
|
+ text: string;
|
|
|
|
|
+ icon: string;
|
|
|
|
|
+ surveyLog?: FmodeObject;
|
|
|
|
|
+} = {
|
|
|
|
|
+ filled: false,
|
|
|
|
|
+ text: '发送问卷',
|
|
|
|
|
+ icon: 'document-text-outline'
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+async loadSurveyStatus() {
|
|
|
|
|
+ if (!this.project?.id || !this.contact?.id) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const query = new Parse.Query('SurveyLog');
|
|
|
|
|
+ query.equalTo('project', this.project.toPointer());
|
|
|
|
|
+ query.equalTo('contact', this.contact.toPointer());
|
|
|
|
|
+ query.equalTo('type', 'survey-project');
|
|
|
|
|
+ query.equalTo('isCompleted', true);
|
|
|
|
|
+ const surveyLog = await query.first();
|
|
|
|
|
+
|
|
|
|
|
+ if (surveyLog) {
|
|
|
|
|
+ this.surveyStatus = {
|
|
|
|
|
+ filled: true,
|
|
|
|
|
+ text: '查看问卷',
|
|
|
|
|
+ icon: 'checkmark-circle',
|
|
|
|
|
+ surveyLog
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('查询问卷状态失败:', err);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 问卷发送功能
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+async sendSurvey() {
|
|
|
|
|
+ if (!this.groupChat || !this.wxwork) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const chatId = this.groupChat.get('chat_id');
|
|
|
|
|
+ const surveyUrl = `${window.location.origin}/wxwork/${this.cid}/survey/project/${this.project?.id}`;
|
|
|
|
|
+
|
|
|
|
|
+ await this.wxwork.ww.openExistedChatWithMsg({
|
|
|
|
|
+ chatId: chatId,
|
|
|
|
|
+ msg: {
|
|
|
|
|
+ msgtype: 'link',
|
|
|
|
|
+ link: {
|
|
|
|
|
+ title: '《家装效果图服务初次合作需求调查表》',
|
|
|
|
|
+ desc: '为让本次服务更贴合您的需求,请花3-5分钟填写简短问卷,感谢支持!',
|
|
|
|
|
+ url: surveyUrl,
|
|
|
|
|
+ imgUrl: `${window.location.origin}/assets/logo.jpg`
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
- ```
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ alert('问卷已发送到群聊!');
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('发送问卷失败:', err);
|
|
|
|
|
+ alert('发送失败,请重试');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.4 问卷查看功能
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 新增模态框状态
|
|
|
|
|
+showSurveyModal: boolean = false;
|
|
|
|
|
+selectedSurveyLog: FmodeObject | null = null;
|
|
|
|
|
+
|
|
|
|
|
+async viewSurvey() {
|
|
|
|
|
+ if (!this.surveyStatus.surveyLog) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.selectedSurveyLog = this.surveyStatus.surveyLog;
|
|
|
|
|
+ this.showSurveyModal = true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async handleSurveyClick(event: Event) {
|
|
|
|
|
+ event.stopPropagation();
|
|
|
|
|
+
|
|
|
|
|
+ if (this.surveyStatus.filled) {
|
|
|
|
|
+ // 已填写,查看结果
|
|
|
|
|
+ await this.viewSurvey();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 未填写,发送问卷
|
|
|
|
|
+ await this.sendSurvey();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 五、技术实现要点
|
|
|
|
|
+
|
|
|
|
|
+### 5.1 企微授权集成
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+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.currentContact = await this.wxAuth.currentContact();
|
|
|
|
|
+ console.log('当前联系人:', this.currentContact);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取联系人失败:', error);
|
|
|
|
|
+ alert('无法识别您的身份,请通过企微群聊进入');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 检查是否已填写问卷
|
|
|
|
|
+ await this.checkExistingSurvey();
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5.2 数据查询与保存
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 查询现有问卷
|
|
|
|
|
+async checkExistingSurvey() {
|
|
|
|
|
+ const query = new Parse.Query('SurveyLog');
|
|
|
|
|
+ query.equalTo('project', this.projectId);
|
|
|
|
|
+ query.equalTo('contact', this.currentContact.toPointer());
|
|
|
|
|
+ query.equalTo('type', 'survey-project');
|
|
|
|
|
+
|
|
|
|
|
+ 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') || '';
|
|
|
|
|
+
|
|
|
|
|
+ const project = new Parse.Object('Project');
|
|
|
|
|
+ project.id = this.projectId;
|
|
|
|
|
+
|
|
|
|
|
+ this.surveyLog.set('company', company.toPointer());
|
|
|
|
|
+ this.surveyLog.set('project', project.toPointer());
|
|
|
|
|
+ this.surveyLog.set('contact', this.currentContact.toPointer());
|
|
|
|
|
+ this.surveyLog.set('type', 'survey-project');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新答案
|
|
|
|
|
+ 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';
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5.3 联系人信息补全
|
|
|
|
|
+
|
|
|
|
|
+如果问卷中填写了姓名/手机号,需要同步更新 ContactInfo 表:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+async updateContactInfo() {
|
|
|
|
|
+ const data = this.surveyLog.get('data');
|
|
|
|
|
+
|
|
|
|
|
+ if (data.contact_name || data.contact_phone) {
|
|
|
|
|
+ if (data.contact_name && !this.currentContact.get('realname')) {
|
|
|
|
|
+ this.currentContact.set('realname', data.contact_name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (data.contact_phone && !this.currentContact.get('mobile')) {
|
|
|
|
|
+ this.currentContact.set('mobile', data.contact_phone);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await this.currentContact.save();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
|
|
|
-# 《家装效果图服务初次合作需求调查表》
|
|
|
|
|
-## 尊敬的伙伴:
|
|
|
|
|
-为让本次效果图服务更贴合您的工作节奏与核心需求,我们准备了简短选择式问卷,您的偏好将直接帮我们校准服务方向,感谢支持!
|
|
|
|
|
|
|
+## 六、《家装效果图服务初次合作需求调查表》
|
|
|
|
|
+### 尊敬的伙伴:
|
|
|
|
|
+为让本次效果图服务更贴合您的工作节奏与核心需求,我们准备了简短选择式问卷,您的偏好将直接帮我们校准服务方向,感谢支持!
|
|
|
|
|
|
|
|
|
|
|
|
|
-## 一、基础需求:快速明确服务范围
|
|
|
|
|
-1. 本次您需要的核心服务是?
|
|
|
|
|
- □ 纯效果图渲染(仅输出可视化图像)
|
|
|
|
|
- □ 效果图+技术配合(含方案相关建议)
|
|
|
|
|
- □ 其他补充:______
|
|
|
|
|
|
|
+### 一、基础需求:快速明确服务范围
|
|
|
|
|
+1. 本次您需要的核心服务是?
|
|
|
|
|
+ □ 纯效果图渲染(仅输出可视化图像)
|
|
|
|
|
+ □ 效果图+技术配合(含方案相关建议)
|
|
|
|
|
+ □ 其他补充:______
|
|
|
|
|
|
|
|
-2. 需覆盖的关键空间数量及类型?
|
|
|
|
|
- 数量:______个(例:3个,空间类型:客厅/主卧/儿童房)
|
|
|
|
|
|
|
+2. 需覆盖的关键空间数量及类型?
|
|
|
|
|
+ 数量:______个(例:3个,空间类型:客厅/主卧/儿童房)
|
|
|
|
|
|
|
|
|
|
|
|
|
-## 二、核心侧重:帮我们锁定服务重点
|
|
|
|
|
-3. 您更希望本次效果图突出哪些价值?(可多选,选2-3项)
|
|
|
|
|
- □ 细节写实度(如空间尺寸匹配、材质还原,贴合落地需求)
|
|
|
|
|
- □ 视觉吸引力(如氛围营造、风格亮点,方便对接业主)
|
|
|
|
|
- □ 风格适配性(精准匹配预设调性,减少后期调整)
|
|
|
|
|
- □ 其他重点:______
|
|
|
|
|
|
|
+### 二、核心侧重:帮我们锁定服务重点
|
|
|
|
|
+3. 您更希望本次效果图突出哪些价值?(可多选,选2-3项)
|
|
|
|
|
+ □ 细节写实度(如空间尺寸匹配、材质还原,贴合落地需求)
|
|
|
|
|
+ □ 视觉吸引力(如氛围营造、风格亮点,方便对接业主)
|
|
|
|
|
+ □ 风格适配性(精准匹配预设调性,减少后期调整)
|
|
|
|
|
+ □ 其他重点:______
|
|
|
|
|
|
|
|
-4. 关于方案建议,是否需要我们技术团队配合?
|
|
|
|
|
- □ 需要(侧重方向:□ 材质搭配 □ 灯光布局 □ 空间优化)
|
|
|
|
|
- □ 暂不需要(已有明确方案,仅需渲染)
|
|
|
|
|
|
|
+4. 关于方案建议,是否需要我们技术团队配合?
|
|
|
|
|
+ □ 需要(侧重方向:□ 材质搭配 □ 灯光布局 □ 空间优化)
|
|
|
|
|
+ □ 暂不需要(已有明确方案,仅需渲染)
|
|
|
|
|
|
|
|
|
|
|
|
|
-## 三、协作节奏:匹配您的沟通习惯
|
|
|
|
|
-5. 您偏好的服务协作方式是?
|
|
|
|
|
- □ 前期多沟通(确认方向、细节后再推进,减少返工)
|
|
|
|
|
- □ 先出初版再修改(快速看到成果,针对性调整)
|
|
|
|
|
- □ 灵活协调(根据进度随时沟通)
|
|
|
|
|
|
|
+### 三、协作节奏:匹配您的沟通习惯
|
|
|
|
|
+5. 您偏好的服务协作方式是?
|
|
|
|
|
+ □ 前期多沟通(确认方向、细节后再推进,减少返工)
|
|
|
|
|
+ □ 先出初版再修改(快速看到成果,针对性调整)
|
|
|
|
|
+ □ 灵活协调(根据进度随时沟通)
|
|
|
|
|
|
|
|
|
|
|
|
|
-## 四、特殊提醒:提前规避潜在偏差
|
|
|
|
|
-6. 过往合作中,是否有需要特别注意的点?(可多选)
|
|
|
|
|
- □ 软装色调易偏差 □ 建模细节需盯控 □ 其他:______
|
|
|
|
|
|
|
+### 四、特殊提醒:提前规避潜在偏差
|
|
|
|
|
+6. 过往合作中,是否有需要特别注意的点?(可多选)
|
|
|
|
|
+ □ 软装色调易偏差 □ 建模细节需盯控 □ 其他:______
|
|
|
|
|
|
|
|
-7. 本次项目是否有特殊要求?(如业主禁忌、重点展示点)
|
|
|
|
|
- ______
|
|
|
|
|
|
|
+7. 本次项目是否有特殊要求?(如业主禁忌、重点展示点)
|
|
|
|
|
+ ______
|
|
|
|
|
|
|
|
-8. 是否有参考素材(如风格图、实景图)需同步?
|
|
|
|
|
- □ 有(后续群内发送) □ 无(需求已清晰)
|
|
|
|
|
|
|
+8. 是否有参考素材(如风格图、实景图)需同步?
|
|
|
|
|
+ □ 有(后续群内发送) □ 无(需求已清晰)
|
|
|
|
|
|
|
|
|
|
|
|
|
-感谢您的反馈!我们将根据您的选择制定服务方案,对接人:______(姓名),电话:______,有问题可随时联系~
|
|
|
|
|
|
|
+感谢您的反馈!我们将根据您的选择制定服务方案,对接人:______(姓名),电话:______,有问题可随时联系~
|