1744 lines
78 KiB
Markdown
1744 lines
78 KiB
Markdown
# 客源详情页 UI 设计文档
|
||
|
||
> **版本**:v1.0 · **日期**:2026-04-25
|
||
> **依赖规范**:UI_SYSTEM.md v1.2 · 组件规范设计.md v1.0
|
||
> **PRD 来源**:`Project/fonrey/PRD/客源管理/客源管理模块PRD.md` §5.7 私客详情页
|
||
> **优先级**:P0 功能用 🔴 标注,P1 用 🟡,P2 用 ⚫
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [模块概述](#1-模块概述)
|
||
- 1.1 功能范围
|
||
- 1.2 页面清单
|
||
- 1.3 用户角色与权限差异
|
||
2. [页面设计规范](#2-页面设计规范)
|
||
- 2.1 私客详情页 — 整体框架
|
||
- 2.2 Section 左侧:需求信息 Tab
|
||
- 2.3 Section 左侧:跟进记录 Tab
|
||
- 2.4 Section 左侧:带看 Tab
|
||
- 2.5 Section 左侧:客源解读 Tab
|
||
- 2.6 Section 左侧:二手配房 Tab
|
||
- 2.7 Section 右侧:客源信息概览面板
|
||
- 2.8 Section 右侧:联系人面板
|
||
- 2.9 Section 右侧:相关员工面板
|
||
3. [弹窗/抽屉设计规范](#3-弹窗抽屉设计规范)
|
||
- 3.1 编辑基础信息(Modal)
|
||
- 3.2 改等级(Modal)
|
||
- 3.3 改状态(Modal)
|
||
- 3.4 转成交(Modal)
|
||
- 3.5 选择成交房源(Modal 宽型)
|
||
- 3.6 写入跟进(Drawer)
|
||
- 3.7 新增带看(全页表单)
|
||
- 3.8 新增预约带看(全页表单)
|
||
4. [交互状态规范](#4-交互状态规范)
|
||
- 4.1 客源状态机
|
||
- 4.2 权限控制矩阵
|
||
- 4.3 HTMX 请求规范
|
||
5. [关键数据字段说明](#5-关键数据字段说明)
|
||
6. [竞品截图对应关系](#6-竞品截图对应关系)
|
||
7. [实现优先级与工期估算](#7-实现优先级与工期估算)
|
||
8. [开放问题(待决策)](#8-开放问题待决策)
|
||
|
||
---
|
||
|
||
## 1. 模块概述
|
||
|
||
### 1.1 功能范围
|
||
|
||
**P0 功能 🔴**
|
||
- 私客详情页整体框架(左右双栏布局 + Tab 导航)
|
||
- 需求信息查看与编辑
|
||
- 跟进记录(全部/写入跟进/敏感信息跟进/修改跟进/其他跟进)
|
||
- 带看记录(预约带看 + 实际带看)
|
||
- 联系人查看与新增
|
||
- 客源状态变更:改等级、改状态
|
||
- 转公客 / 转成交 / 转无效
|
||
- 右侧客源信息概览面板(基础字段展示 + 快捷操作)
|
||
- 相关员工查看
|
||
|
||
**P1 功能 🟡**
|
||
- 客源解读(AI 行为分析)
|
||
- 二手配房(录客配房 + 系统配房)
|
||
- 收藏功能
|
||
- 待办提醒模块
|
||
- 编辑基础信息(完整表单)
|
||
|
||
**P2 功能 ⚫**
|
||
- 编辑信息来源
|
||
- 查看操作日志
|
||
- 名下房产修改
|
||
|
||
---
|
||
|
||
### 1.2 页面清单
|
||
|
||
| 页面名称 | URL 模式建议 | 优先级 | 对应 PRD 章节 |
|
||
|---|---|---|---|
|
||
| 私客详情页(主框架) | `/clients/<client_id>/` | P0 🔴 | §5.7.1 |
|
||
| 需求信息 Tab | `/clients/<client_id>/requirements/` (HTMX partial) | P0 🔴 | §5.7.2 |
|
||
| 跟进记录 Tab | `/clients/<client_id>/follow-logs/` (HTMX partial) | P0 🔴 | §5.7.2 |
|
||
| 带看 Tab | `/clients/<client_id>/viewings/` (HTMX partial) | P0 🔴 | §5.7.2 |
|
||
| 客源解读 Tab | `/clients/<client_id>/insights/` (HTMX partial) | P1 🟡 | §5.7.2 |
|
||
| 二手配房 Tab | `/clients/<client_id>/property-matches/` (HTMX partial) | P1 🟡 | §5.7.2 |
|
||
| 右侧信息面板 | `/clients/<client_id>/sidebar/` (HTMX partial) | P0 🔴 | §5.7.3 |
|
||
| 新增带看(全页) | `/clients/<client_id>/viewings/create/` | P0 🔴 | §5.7.2 |
|
||
| 新增预约带看(全页) | `/clients/<client_id>/viewings/appointment/create/` | P0 🔴 | §5.7.2 |
|
||
|
||
---
|
||
|
||
### 1.3 用户角色与权限差异
|
||
|
||
| 功能/视图元素 | 经纪人(本人) | 经纪人(他人客源) | 店长 | 管理员 |
|
||
|---|---|---|---|---|
|
||
| 查看详情页 | ✅ | 受限(需权限) | ✅ | ✅ |
|
||
| 查看联系人号码明文 | ✅(有操作留痕) | ❌ | ✅ | ✅ |
|
||
| 写入跟进记录 | ✅ | ❌ | ✅ | ✅ |
|
||
| 编辑需求信息 | ✅ | ❌ | ✅ | ✅ |
|
||
| 改等级/改状态 | ✅ | ❌ | ✅ | ✅ |
|
||
| 转公客/转成交/转无效 | ✅ | ❌ | ✅ | ✅ |
|
||
| 编辑相关员工 | ❌ | ❌ | ✅ | ✅ |
|
||
| 查看操作日志 | 部分 | ❌ | ✅ | ✅ |
|
||
| 敏感信息跟进查看 | ✅(本人) | ❌ | ✅ | ✅ |
|
||
| 转公客后操作 | ❌ | ❌ | ✅ | ✅ |
|
||
|
||
> 注:联系人号码查看必须留痕(自动写入 `log_type='sensitive_view'` 的 `client_follow_logs` 记录)。
|
||
|
||
---
|
||
|
||
## 2. 页面设计规范
|
||
|
||
### 2.1 私客详情页 — 整体框架(P0 🔴)
|
||
|
||
#### 2.1.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/`
|
||
- **访问入口**:客源列表点击任意行 → 跳转详情页;面包屑返回列表
|
||
- **页面职责**:集中展示私客的全量信息,提供跟进、带看、状态变更等核心操作入口
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/私客详情.png`
|
||
|
||
#### 2.1.2 布局结构
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────────┐
|
||
│ 顶部导航栏(全局,来自 base.html,高度 48px) │
|
||
├────────────────────────────────────────────────────────────────────────┤
|
||
│ 面包屑导航(客源 / 客源管理 / {客源标题}) │
|
||
├──────────────────────────────────────┬─────────────────────────────────┤
|
||
│ │ │
|
||
│ 左侧主内容区(约 68% 宽度) │ 右侧信息面板(约 32% 宽度) │
|
||
│ ───────────────────────────── │ ───────────────────────── │
|
||
│ [Tab 导航栏] │ [客源标题 Banner(橙色)] │
|
||
│ 需求信息 | 跟进记录 | 带看 | │ [状态标签行] │
|
||
│ 客源解读 | 智能配房 │ [基础字段列表] │
|
||
│ │ [展开全部 链接] │
|
||
│ [Tab 内容区(HTMX 动态加载)] │ [快捷操作按钮组] │
|
||
│ │ [状态变更操作网格] │
|
||
│ │ [待办提醒区块] │
|
||
│ │ [联系人区块] │
|
||
│ │ [相关员工区块] │
|
||
│ │ │
|
||
└──────────────────────────────────────┴─────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.1.3 区域详细规范
|
||
|
||
**[面包屑导航区]**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| 组件 | 原生 HTML `<nav>` + Heroicons `chevron-right` 16px |
|
||
| 内容 | 客源 / 客源管理 / {client.title} |
|
||
| 样式 | `flex items-center gap-1 text-sm text-neutral-500 px-6 py-3` |
|
||
| 最后一项 | `text-neutral-700 font-medium`(不可点击) |
|
||
|
||
**[双栏主布局]**
|
||
|
||
```html
|
||
<div class="flex gap-6 px-6 pb-6 items-start">
|
||
<!-- 左侧主内容区 -->
|
||
<div class="flex-1 min-w-0 space-y-0">
|
||
<!-- Tab 导航 + Tab 内容 -->
|
||
</div>
|
||
<!-- 右侧固定面板 -->
|
||
<div class="w-80 flex-shrink-0 space-y-3 sticky top-4">
|
||
<!-- 客源信息概览 + 联系人 + 相关员工 -->
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
> 右侧面板使用 `sticky top-4` 实现滚动时固定,最大高度 `max-h-[calc(100vh-5rem)] overflow-y-auto`
|
||
|
||
**[Tab 导航栏]**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| 组件 | §10 Tab Navigation |
|
||
| Tab 列表 | 需求信息 / 跟进记录 / 带看 / 客源解读 / 智能配房 |
|
||
| 默认激活 | 需求信息 |
|
||
| 激活样式 | `text-primary-600 font-medium border-b-2 border-primary-600 -mb-px` |
|
||
| 未激活样式 | `text-neutral-500 hover:text-neutral-700` |
|
||
| Alpine 管理 | `x-data="{ activeTab: 'requirements' }"` |
|
||
| HTMX 行为 | 点击 Tab 时 `hx-get` 对应 partial URL,`hx-target="#tab-content"`, `hx-swap="innerHTML"` |
|
||
| 懒加载 | 非默认 Tab 内容通过 `hx-trigger="click"` 首次点击时加载 |
|
||
|
||
```html
|
||
<div x-data="{ activeTab: 'requirements' }" class="bg-white rounded-lg border border-neutral-200">
|
||
<!-- Tab 导航 -->
|
||
<div class="border-b border-neutral-200 px-4" role="tablist">
|
||
<nav class="flex gap-1 -mb-px">
|
||
<button
|
||
role="tab"
|
||
:aria-selected="activeTab === 'requirements'"
|
||
@click="activeTab = 'requirements'"
|
||
:class="activeTab === 'requirements'
|
||
? 'h-10 px-4 text-sm text-primary-600 font-medium border-b-2 border-primary-600 -mb-px'
|
||
: 'h-10 px-4 text-sm text-neutral-500 hover:text-neutral-700'"
|
||
hx-get="/clients/{{ client.id }}/requirements/"
|
||
hx-target="#tab-content"
|
||
hx-swap="innerHTML"
|
||
hx-trigger="click[activeTab !== 'requirements']"
|
||
>需求信息</button>
|
||
<!-- 其余 Tab 类似 -->
|
||
</nav>
|
||
</div>
|
||
<!-- Tab 内容区 -->
|
||
<div id="tab-content" class="p-4" role="tabpanel">
|
||
<!-- 初始渲染需求信息内容 -->
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### 2.1.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 | 自定义说明 |
|
||
|---|---|---|---|
|
||
| Tab Navigation | §10 Tab Navigation | 左侧五个功能 Tab 切换 | 不做懒加载 `intersect`,改为 `click` 首次触发 |
|
||
|
||
#### 2.1.5 空状态设计
|
||
|
||
不适用(详情页框架本身无空状态,各 Tab 内容各自定义)
|
||
|
||
#### 2.1.6 Loading 状态
|
||
|
||
Tab 切换时,`#tab-content` 区域显示骨架屏:
|
||
|
||
```html
|
||
<!-- HTMX indicator(放在 Tab 内容容器内) -->
|
||
<div id="tab-content-skeleton" class="animate-pulse space-y-3 p-4 htmx-indicator">
|
||
<div class="h-4 bg-neutral-200 rounded w-3/4"></div>
|
||
<div class="h-4 bg-neutral-200 rounded w-1/2"></div>
|
||
<div class="h-4 bg-neutral-200 rounded w-2/3"></div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
### 2.2 Section 左侧:需求信息 Tab(P0 🔴)
|
||
|
||
#### 2.2.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/requirements/`(HTMX partial)
|
||
- **访问入口**:详情页默认激活 Tab
|
||
- **页面职责**:展示客户购房/租房需求的全量字段,支持内联编辑
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/需求信息.png`
|
||
|
||
#### 2.2.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ [标题区] 需求信息 [编辑] 链接 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [字段网格] 3列布局,字段名(灰色)/ 字段值(深色) │
|
||
│ 总价 | 面积 | 居室 │
|
||
│ 装修 | 朝向 | 楼层 │
|
||
│ 楼龄 | 意向商圈 | 意向小区 │
|
||
│ 交通情况 | 备注(跨列) │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.2.3 区域详细规范
|
||
|
||
**[需求信息字段网格]**
|
||
|
||
| 字段名 | 数据字段 | 展示类型 | 特殊渲染 |
|
||
|---|---|---|---|
|
||
| 总价 | `requirement.price_min` / `price_max` | 文本范围 | `{min}-{max}万元`,空时显示 `-` |
|
||
| 面积 | `requirement.area_min` / `area_max` | 文本范围 | `{min}㎡-{max}㎡` |
|
||
| 居室 | `requirement.rooms` | 文本 | 如 `2居` |
|
||
| 装修 | `requirement.decoration` | 文本 | 空时 `-` |
|
||
| 朝向 | `requirement.orientation` | 文本 | 多选逗号分隔 |
|
||
| 楼层 | `requirement.floor_preference` | 文本 | 多选,如 `中楼层、低楼层` |
|
||
| 楼龄 | `requirement.building_age` | 文本 | 空时 `-` |
|
||
| 意向商圈 | `requirement.preferred_districts` | 多标签 | 逗号分隔文本,空时 `-` |
|
||
| 意向小区 | `requirement.preferred_communities` | 多标签 | 逗号分隔,空时 `-` |
|
||
| 交通情况 | `requirement.transport_preference` | 文本 | 空时 `-` |
|
||
| 备注 | `requirement.notes` | 多行文本 | 跨越全行 `col-span-3`,空时 `-` |
|
||
|
||
**字段渲染 HTML 结构:**
|
||
|
||
```html
|
||
<div class="grid grid-cols-3 gap-x-6 gap-y-4">
|
||
<div class="space-y-1">
|
||
<dt class="text-xs text-neutral-500">总价</dt>
|
||
<dd class="text-sm text-neutral-800">{{ req.price_min }}-{{ req.price_max }}万元</dd>
|
||
</div>
|
||
<!-- ... -->
|
||
</div>
|
||
```
|
||
|
||
**[编辑按钮]**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| 样式 | `text-sm text-info-600 hover:text-blue-700 font-medium` |
|
||
| 触发 | 点击打开编辑需求信息 Drawer(§3.1) |
|
||
| 权限 | 仅归属人/管理员/店长可见 |
|
||
|
||
#### 2.2.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 | 自定义说明 |
|
||
|---|---|---|---|
|
||
| Inline Edit Mode | §15 | 字段网格切换编辑态 | 本模块改为 Drawer 编辑,不用内联编辑模式 |
|
||
|
||
#### 2.2.5 空状态设计
|
||
|
||
若需求信息尚未录入(所有字段均为空),在内容区中部显示:
|
||
|
||
```html
|
||
<div class="flex flex-col items-center justify-center py-12 text-center">
|
||
<svg class="w-10 h-10 text-neutral-300 mb-3"><!-- Heroicons document-text --></svg>
|
||
<p class="text-sm text-neutral-500">暂未填写需求信息</p>
|
||
<button class="mt-3 text-sm text-primary-600 hover:underline">立即编辑</button>
|
||
</div>
|
||
```
|
||
|
||
#### 2.2.6 Loading 状态
|
||
|
||
由父页面 Tab 切换骨架屏覆盖(见 §2.1.6),内容区自身不额外处理。
|
||
|
||
---
|
||
|
||
### 2.3 Section 左侧:跟进记录 Tab(P0 🔴)
|
||
|
||
#### 2.3.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/follow-logs/`(HTMX partial)
|
||
- **访问入口**:点击"跟进记录" Tab
|
||
- **页面职责**:按时间线展示所有跟进记录,支持筛选、写入新跟进
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/跟进记录-全部.png`、`跟进记录-写入跟进.png` 等
|
||
|
||
#### 2.3.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ [子 Tab 行] 全部 | 写入跟进 | 敏感信息跟进 | 修改跟进 | 其他跟进 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [筛选区(写入跟进 Tab 展开更多筛选)] │
|
||
│ 日期范围选择器 [有录音] [有图片] 复选框 │
|
||
│ 跟进目的:多选复选框(展开/收起) │
|
||
│ 跟进标签:多选复选框(展开/收起) │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [时间线内容区] │
|
||
│ ● 2026-04-19 ────────────────────────────────── │
|
||
│ [跟进记录条目 1] │
|
||
│ [跟进记录条目 2] │
|
||
│ ● 2026-04-18 ────────────────────────────────── │
|
||
│ [跟进记录条目 ...] │
|
||
│ [加载更多 按钮] │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.3.3 区域详细规范
|
||
|
||
**[子 Tab 导航栏]**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| 组件 | §10 Tab Navigation(嵌套,风格改为 pill 式) |
|
||
| Tab 列表 | 全部 / 写入跟进 / 敏感信息跟进 / 修改跟进 / 其他跟进 |
|
||
| 映射 log_type | `all` / `written` / `sensitive_view` / `modified` / `other` |
|
||
| 样式 | `bg-neutral-100 rounded-full px-3 py-1 text-xs`;激活 `bg-primary-600 text-white` |
|
||
| Alpine | `x-data="{ logTab: 'all' }"` |
|
||
| HTMX | 切换时 `hx-get="/clients/{id}/follow-logs/?type={logTab}"` `hx-target="#log-list"` `hx-swap="innerHTML"` |
|
||
|
||
**[筛选区]**
|
||
|
||
| 字段 | 组件类型 | 说明 |
|
||
|---|---|---|
|
||
| 日期范围 | §9 Date Range Picker | `开始时间 → 结束时间`;`hx-trigger="change"` |
|
||
| 有录音 / 有图片 | Checkbox | `hx-trigger="change"` 触发重筛 |
|
||
| 跟进目的 | 多选 Checkbox 网格 | 仅在"写入跟进"Tab 下展开显示;默认折叠,"展开 ∨" 链接切换;竞品中约 25 个选项 |
|
||
| 跟进标签 | 多选 Checkbox | 同上,与跟进目的共用展开/收起逻辑 |
|
||
|
||
```html
|
||
<!-- 筛选区 Alpine 管理展开/收起 -->
|
||
<div x-data="{ filterOpen: false }">
|
||
<div class="flex items-center gap-4 py-2 border-b border-neutral-100">
|
||
<span class="text-xs text-neutral-500">筛选:</span>
|
||
<label class="flex items-center gap-1 text-xs text-neutral-600 cursor-pointer">
|
||
<input type="checkbox" class="w-3 h-3 rounded accent-primary-600"
|
||
hx-get="/clients/{{ id }}/follow-logs/" hx-trigger="change"
|
||
hx-include="[name='filter']" hx-target="#log-list" name="filter" value="has_audio">
|
||
有录音
|
||
</label>
|
||
<label class="flex items-center gap-1 text-xs text-neutral-600 cursor-pointer">
|
||
<input type="checkbox" class="w-3 h-3 rounded accent-primary-600"
|
||
hx-get="/clients/{{ id }}/follow-logs/" hx-trigger="change"
|
||
hx-include="[name='filter']" hx-target="#log-list" name="filter" value="has_image">
|
||
有图片
|
||
</label>
|
||
</div>
|
||
<!-- 跟进目的(仅写入跟进 Tab 显示) -->
|
||
<template x-if="logTab === 'written'">
|
||
<div class="py-2 border-b border-neutral-100">
|
||
<div class="flex items-center gap-2 flex-wrap">
|
||
<span class="text-xs text-neutral-500 flex-shrink-0">跟进目的:</span>
|
||
<!-- 复选框网格 -->
|
||
<div x-show="filterOpen" class="flex flex-wrap gap-2">
|
||
<!-- 25 个目的选项 -->
|
||
</div>
|
||
<button @click="filterOpen = !filterOpen"
|
||
class="text-xs text-info-600 hover:underline ml-auto">
|
||
<span x-text="filterOpen ? '收起 ∧' : '展开 ∨'"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
```
|
||
|
||
**[时间线内容区]**
|
||
|
||
采用 §10.3 内嵌 Timeline 子组件规范:
|
||
|
||
| 元素 | 规格 |
|
||
|---|---|
|
||
| 外容器 | `relative pl-6 space-y-1` |
|
||
| 竖线 | `absolute left-2.5 top-0 bottom-0 w-px bg-neutral-200` |
|
||
| 日期分组标题 | `flex items-center gap-2 text-xs font-semibold text-neutral-500 uppercase py-2`;圆点 `w-3 h-3 rounded-full border-2 border-primary-600 bg-white absolute -left-[14px]` |
|
||
| 记录条目卡片 | `ml-2 bg-white border border-neutral-100 rounded-md p-3 space-y-1` |
|
||
|
||
**跟进记录条目卡片规范:**
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────┐
|
||
│ [类型标签 Badge] 【电话】 [时间戳] 11:25 [⋯ 更多] │
|
||
│ [内容文本] 433弄5楼65.85平想置换... │
|
||
│ [底部元信息] 都市港湾店一组 雷威 · 2026-04-19 │
|
||
│ [已开放] [隐藏] 操作链接(灰色小字) │
|
||
└────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| 元素 | 规格 |
|
||
|---|---|
|
||
| 类型标签 | `text-xs font-medium px-2 py-0.5 rounded-full bg-blue-100 text-blue-700` |
|
||
| 时间戳 | `text-xs text-neutral-400 tabular-nums` |
|
||
| 内容文本 | `text-sm text-neutral-700 leading-relaxed` |
|
||
| 元信息 | `text-xs text-neutral-400` |
|
||
| 操作链接 | `text-xs text-info-600 hover:underline`("ℹ️ 已开放" / "隐藏") |
|
||
| 更多菜单 | Heroicons `ellipsis-horizontal` 16px,点击展开 Dropdown(修改/删除选项) |
|
||
| 敏感信息标记 | `log_type === 'sensitive_view'`:条目背景 `bg-amber-50 border-amber-200`,不可删除 |
|
||
|
||
**[加载更多]**
|
||
|
||
```html
|
||
<div id="log-load-more" class="text-center py-3">
|
||
<button class="text-sm text-primary-600 hover:underline"
|
||
hx-get="/clients/{{ id }}/follow-logs/?page=2"
|
||
hx-target="#log-list"
|
||
hx-swap="beforeend"
|
||
hx-indicator="#log-load-spinner">
|
||
查看全部跟进
|
||
</button>
|
||
<span id="log-load-spinner" class="htmx-indicator ml-2 inline-block w-4 h-4 border-2 border-primary-600 border-t-transparent rounded-full animate-spin"></span>
|
||
</div>
|
||
```
|
||
|
||
#### 2.3.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 | 自定义说明 |
|
||
|---|---|---|---|
|
||
| Tab Navigation | §10 | 跟进类型子 Tab | 改为 pill 风格,而非下划线风格 |
|
||
| Timeline 子组件 | §10.3 | 时间线记录展示 | 增加日期分组标题,圆点用 `border-primary-600` |
|
||
| Date Range Picker | §9 | 跟进记录日期筛选 | 标准实现,`hx-trigger="change"` 自动触发筛选 |
|
||
|
||
#### 2.3.5 空状态设计
|
||
|
||
```html
|
||
<div class="flex flex-col items-center justify-center py-16 text-center">
|
||
<svg class="w-10 h-10 text-neutral-300 mb-3"><!-- Heroicons chat-bubble-left-ellipsis --></svg>
|
||
<p class="text-sm text-neutral-500">暂无跟进</p>
|
||
</div>
|
||
```
|
||
|
||
#### 2.3.6 Loading 状态
|
||
|
||
```html
|
||
<div class="animate-pulse space-y-3 p-4 htmx-indicator">
|
||
<div class="h-3 bg-neutral-200 rounded w-24"></div><!-- 日期标题 -->
|
||
<div class="ml-2 space-y-2 border border-neutral-100 rounded-md p-3">
|
||
<div class="h-3 bg-neutral-200 rounded w-16"></div>
|
||
<div class="h-4 bg-neutral-200 rounded w-full"></div>
|
||
<div class="h-3 bg-neutral-200 rounded w-1/2"></div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
### 2.4 Section 左侧:带看 Tab(P0 🔴)
|
||
|
||
#### 2.4.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/viewings/`(HTMX partial)
|
||
- **访问入口**:点击"带看" Tab
|
||
- **页面职责**:展示该客源的预约带看和实际带看记录,提供新增入口
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/带看.png`
|
||
|
||
#### 2.4.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ [子 Tab] 预约 | 带看(active) [日期范围选择器] │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [筛选复选框] □ 归属人带看 □ 其他人带看 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [时间线带看记录列表] │
|
||
│ ● 2026-04-17 20:30 │
|
||
│ 带看情况: 客户继续维护 │
|
||
│ 房源: 金沙丽晶苑一期-001-1201 [一看] 带看: 雷威 │
|
||
│ [详情 ›] │
|
||
│ [加载更多] │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.4.3 区域详细规范
|
||
|
||
**[子 Tab 导航]**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| Tab 列表 | 预约 / 带看 |
|
||
| Alpine | `x-data="{ viewingTab: 'viewing' }"` |
|
||
| HTMX | `hx-get="/clients/{id}/viewings/?type={viewingTab}"` `hx-target="#viewing-list"` |
|
||
|
||
**[筛选行]**
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| 归属人带看 | `hx-trigger="change"` 筛选重载 |
|
||
| 其他人带看 | 同上 |
|
||
| 日期范围 | §9 Date Range Picker,`hx-trigger="change"` |
|
||
|
||
**[带看记录条目]**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ ● 2026-04-17 20:30 │
|
||
│ 带看情况: 客户继续维护 │
|
||
│ [房源链接] 金沙丽晶苑一期-001-1201 │
|
||
│ [带看次数 Badge: 一看 橙色] 带看: 雷威 │
|
||
│ [详情 › 链接] │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| 元素 | 规格 |
|
||
|---|---|
|
||
| 带看次数 Badge | `text-xs font-medium px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 border border-primary-200` |
|
||
| 房源链接 | `text-sm text-info-600 hover:underline` |
|
||
| 带看情况 | `text-sm text-neutral-700` |
|
||
| 带看人 | `text-xs text-neutral-500` |
|
||
| 详情链接 | `text-xs text-info-600 hover:underline ml-auto` |
|
||
|
||
#### 2.4.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 | 自定义说明 |
|
||
|---|---|---|---|
|
||
| Tab Navigation | §10 | 预约/带看子 Tab | pill 风格 |
|
||
| Timeline 子组件 | §10.3 | 带看记录时间线 | 圆点时间戳展示带看时刻 |
|
||
| Date Range Picker | §9 | 带看日期筛选 | 标准实现 |
|
||
|
||
#### 2.4.5 空状态设计
|
||
|
||
```html
|
||
<div class="flex flex-col items-center justify-center py-16 text-center">
|
||
<svg class="w-10 h-10 text-neutral-300 mb-3"><!-- Heroicons home --></svg>
|
||
<p class="text-sm text-neutral-500">暂无带看记录</p>
|
||
<a href="/clients/{{ id }}/viewings/create/"
|
||
class="mt-3 text-sm text-primary-600 hover:underline">新增带看</a>
|
||
</div>
|
||
```
|
||
|
||
#### 2.4.6 Loading 状态
|
||
|
||
与 §2.3.6 相同的骨架屏方案。
|
||
|
||
---
|
||
|
||
### 2.5 Section 左侧:客源解读 Tab(P1 🟡)
|
||
|
||
#### 2.5.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/insights/`(HTMX partial)
|
||
- **访问入口**:点击"客源解读" Tab
|
||
- **页面职责**:展示 AI 分析的客户找房行为数据:活跃时间、购房偏好、区域偏好
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/客源解读.png`
|
||
|
||
#### 2.5.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ [说明文字] + 使用指南链接 [数据更新时间戳] │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [活跃行为卡片] [活跃时间卡片: 工作日 | 周末] │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ 购房偏好 [时段筛选: 近7日 | 近30日 | 近90日] │
|
||
│ [偏好摘要网格: 价格/户型/面积/商圈/小区] │
|
||
│ [类型占比行] │
|
||
│ [偏好子 Tab: 二手偏好 | 新房偏好] │
|
||
│ [三个甜甜圈图: 价格偏好 / 户型偏好 / 面积偏好] │
|
||
│ [区域-商圈偏好] [小区偏好] │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.5.3 区域详细规范
|
||
|
||
**[活跃行为卡片]**
|
||
|
||
| 属性 | 说明 |
|
||
|---|---|
|
||
| 样式 | `bg-white border border-neutral-200 rounded-lg p-4` |
|
||
| 内容 | 标题"活跃行为"+ 数值(如"7 天内")用 `text-2xl font-bold text-neutral-800` |
|
||
| 副标题 | `text-sm text-neutral-500` 描述说明 |
|
||
|
||
**[购房偏好摘要网格]**
|
||
|
||
| 字段 | 规格 |
|
||
|---|---|
|
||
| 价格 | `text-sm font-semibold text-neutral-800` 展示范围 |
|
||
| 户型 | 同上 |
|
||
| 面积 | 同上 |
|
||
| 商圈 / 小区 | 空时显示 `-` |
|
||
|
||
**[甜甜圈图]**
|
||
|
||
> MVP 阶段可用简单的百分比 `text-4xl font-bold text-primary-600` 替代 Chart.js 图表,配合 `text-sm text-neutral-500` 标签文字。P1 阶段再引入图表库。
|
||
|
||
**[时段筛选 Pill]**
|
||
|
||
```html
|
||
<div x-data="{ period: '7d' }" class="flex gap-1">
|
||
<template x-for="p in [{val:'7d', label:'近7日'},{val:'30d', label:'近30日'},{val:'90d', label:'近90日'}]">
|
||
<button @click="period = p.val"
|
||
:class="period === p.val ? 'bg-primary-600 text-white' : 'bg-neutral-100 text-neutral-600'"
|
||
class="text-xs px-3 py-1 rounded-full"
|
||
hx-get="/clients/{{ id }}/insights/" :hx-vals="JSON.stringify({period: p.val})"
|
||
hx-target="#insight-content" hx-swap="innerHTML">
|
||
<span x-text="p.label"></span>
|
||
</button>
|
||
</template>
|
||
</div>
|
||
```
|
||
|
||
#### 2.5.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 | 自定义说明 |
|
||
|---|---|---|---|
|
||
| Tab Navigation | §10 | 二手偏好/新房偏好子 Tab | pill 风格 |
|
||
|
||
#### 2.5.5 空状态设计
|
||
|
||
```html
|
||
<div class="text-center py-12">
|
||
<svg class="w-10 h-10 text-neutral-300 mx-auto mb-3"><!-- Heroicons chart-bar --></svg>
|
||
<p class="text-sm text-neutral-500">暂无找房行为数据</p>
|
||
<p class="text-xs text-neutral-400 mt-1">数据由系统每日凌晨更新</p>
|
||
</div>
|
||
```
|
||
|
||
#### 2.5.6 Loading 状态
|
||
|
||
骨架屏三个卡片占位块,`animate-pulse` 处理。
|
||
|
||
---
|
||
|
||
### 2.6 Section 左侧:二手配房 Tab(P1 🟡)
|
||
|
||
#### 2.6.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/property-matches/`(HTMX partial)
|
||
- **访问入口**:点击"智能配房" Tab
|
||
- **页面职责**:展示系统根据客户需求匹配的二手房源,按分组展示推荐卡片
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/二手配房.png`
|
||
|
||
#### 2.6.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ 二手配房 [更新时间] ℹ️ [批量分享 按钮] │
|
||
│ [子 Tab] 录客配房(active) | 系统配房 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [分组标题: 优质户型] │
|
||
│ [房源卡片] [房源卡片] [房源卡片] │
|
||
│ [分组标题: 降价] │
|
||
│ [房源卡片] [房源卡片] │
|
||
│ [分组标题: 热门] │
|
||
│ [房源卡片] ... │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.6.3 区域详细规范
|
||
|
||
**[房源卡片]**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ [缩略图 80×60] [房源名称(蓝色链接)] │
|
||
│ [规格] 2/2/1 · 101.17㎡ │
|
||
│ [区域] 嘉定 丰庄 │
|
||
│ [特征标签组](橙/蓝 pills) │
|
||
│ [价格] 620万 [跌价信息] 已跌20万 │
|
||
│ [单价] 61283元/㎡ │
|
||
│ [分享房源] [反馈 ∨](action 链接) │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
| 元素 | 规格 |
|
||
|---|---|
|
||
| 缩略图 | `w-20 h-15 rounded-md object-cover flex-shrink-0` |
|
||
| 房源名称 | `text-sm font-medium text-info-600 hover:underline` |
|
||
| 规格文字 | `text-xs text-neutral-500` |
|
||
| 特征标签 | `text-xs px-1.5 py-0.5 rounded bg-orange-50 text-orange-700` 或 `bg-blue-50 text-blue-700` |
|
||
| 价格 | `text-base font-bold text-neutral-900` |
|
||
| 跌价标注 | `text-xs text-neutral-400` |
|
||
| 分组标题 | `text-sm font-semibold text-neutral-700 py-2 border-b border-neutral-200 mb-3` |
|
||
| 批量分享 | `btn-secondary text-xs` 灰色描边按钮 |
|
||
| 反馈 ∨ | Dropdown,选项:满意/不满意/已跟进 |
|
||
|
||
**卡片网格布局:**
|
||
|
||
```html
|
||
<div class="grid grid-cols-1 gap-3" id="match-list">
|
||
<!-- 每个分组 -->
|
||
<div>
|
||
<h3 class="text-sm font-semibold text-neutral-700 py-2 border-b border-neutral-200 mb-3">优质户型</h3>
|
||
<div class="space-y-3">
|
||
<!-- 房源卡片 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### 2.6.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Tab Navigation | §10 | 录客配房/系统配房子 Tab |
|
||
|
||
#### 2.6.5 空状态设计
|
||
|
||
```html
|
||
<div class="text-center py-12">
|
||
<svg class="w-10 h-10 text-neutral-300 mx-auto mb-3"><!-- Heroicons building-office-2 --></svg>
|
||
<p class="text-sm text-neutral-500">暂无匹配房源</p>
|
||
</div>
|
||
```
|
||
|
||
#### 2.6.6 Loading 状态
|
||
|
||
卡片骨架屏,每张卡片用 `animate-pulse` 占位块。
|
||
|
||
---
|
||
|
||
### 2.7 Section 右侧:客源信息概览面板(P0/P1)
|
||
|
||
#### 2.7.1 页面概述
|
||
|
||
- **URL**:`/clients/<client_id>/sidebar/`(HTMX partial,初始 SSR)
|
||
- **访问入口**:详情页右侧固定面板(持续可见)
|
||
- **页面职责**:集中展示客源核心标识信息 + 快捷操作入口
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/客源信息概览.png`
|
||
|
||
#### 2.7.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────┐
|
||
│ [橙色 Banner] │
|
||
│ 求购 叶阿姨买一房(上门) 女士 │
|
||
├──────────────────────────────────────────────┤
|
||
│ [状态标签行] 私客 · 一看 · C(一般) │
|
||
├──────────────────────────────────────────────┤
|
||
│ [基础字段列表(只读)] │
|
||
│ 最近跟进 / 客户编号 / 委托日期 ... │
|
||
│ [展开全部 ∨ 链接] │
|
||
├──────────────────────────────────────────────┤
|
||
│ [三个主操作按钮](P0) │
|
||
│ [打电话] [写跟进] [报备/常看] │
|
||
├──────────────────────────────────────────────┤
|
||
│ [状态变更操作网格](P0) │
|
||
│ ☆ 收藏 · 不置顶 │
|
||
│ 改等级 · 改状态 │
|
||
│ 转公客 · 转成交 │
|
||
│ 转无效 · 编辑客源 │
|
||
└──────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.7.3 区域详细规范
|
||
|
||
**[橙色 Banner]**
|
||
|
||
```html
|
||
<div class="bg-primary-600 rounded-t-lg px-4 py-3">
|
||
<div class="flex items-start gap-2">
|
||
<span class="text-xs font-medium text-white bg-white/20 px-2 py-0.5 rounded-full">求购</span>
|
||
<div class="flex-1 min-w-0">
|
||
<h2 class="text-sm font-semibold text-white leading-snug truncate">
|
||
{{ client.title }}
|
||
</h2>
|
||
<p class="text-xs text-white/80 mt-0.5">{{ client.primary_contact.name }} {{ client.primary_contact.gender_display }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
> 注:竞品用橙色 `#f5774a`;本系统采用 Teal `primary-600 (#0F766E)` 替代,保持品牌一致性。
|
||
|
||
**[状态标签行]**
|
||
|
||
| 标签 | 样式 |
|
||
|---|---|
|
||
| 私客(client_type) | `text-xs px-2 py-0.5 rounded-full bg-neutral-100 text-neutral-600` |
|
||
| 带看次数(如"一看") | `text-xs px-2 py-0.5 rounded-full bg-blue-100 text-blue-700` |
|
||
| 等级(C/一般) | `text-xs px-2 py-0.5 rounded-full`;A 级 `bg-red-100 text-red-700`;B 级 `bg-orange-100 text-orange-700`;C 级 `bg-yellow-100 text-yellow-700`;D/E `bg-neutral-100 text-neutral-500` |
|
||
|
||
**[基础字段列表]**
|
||
|
||
| 字段名 | 数据来源 | 空值展示 |
|
||
|---|---|---|
|
||
| 最近跟进 | `client.last_follow_at` | `-` |
|
||
| 客户编号 | `client.client_no` | — |
|
||
| 委托日期 | `client.created_at` date | — |
|
||
| 需求类型 | `client.requirement_type_display` | — |
|
||
| 房源用途 | `client.property_usage_display` | — |
|
||
| 客户来源 | `client.source_display` | — |
|
||
| 购房目的 | `client.purchase_purpose` | `-` |
|
||
| 付款方式 | `client.payment_method` | `-` |
|
||
| 名下房产 | `client.owned_properties` | `未填写` |
|
||
| 贷款记录 | `client.loan_record` | `-` |
|
||
| 证件类型 | `client.id_type` | `-` |
|
||
| 证件号码 | `client.id_number`(脱敏) | `-` |
|
||
| 意向学校 | `client.preferred_school` | `-` |
|
||
| 入学时间 | `client.school_start_year` | `-` |
|
||
|
||
**字段行渲染:**
|
||
|
||
```html
|
||
<dl class="space-y-1.5 px-4 py-3">
|
||
<div class="flex items-start justify-between gap-2">
|
||
<dt class="text-xs text-neutral-400 flex-shrink-0 w-16">最近跟进</dt>
|
||
<dd class="text-xs text-neutral-700 text-right">{{ client.last_follow_at|date:"Y-m-d"|default:"-" }}</dd>
|
||
</div>
|
||
<!-- ... -->
|
||
</dl>
|
||
```
|
||
|
||
**[展开全部链接]**
|
||
|
||
```html
|
||
<button x-data="{ expanded: false }" @click="expanded = !expanded"
|
||
class="w-full text-center text-xs text-info-600 hover:underline py-2 border-t border-neutral-100">
|
||
<span x-text="expanded ? '收起 ∧' : '展开全部 ∨'"></span>
|
||
</button>
|
||
<!-- 隐藏字段区,x-show="expanded" -->
|
||
```
|
||
|
||
**[三个主操作按钮(P0)]**
|
||
|
||
```html
|
||
<div class="grid grid-cols-3 gap-2 px-4 py-3 border-t border-neutral-100">
|
||
<button class="flex flex-col items-center gap-1 py-2 bg-primary-600 hover:bg-primary-700
|
||
text-white text-xs font-medium rounded-md transition-colors">
|
||
<svg class="w-4 h-4"><!-- Heroicons phone --></svg>
|
||
打电话
|
||
</button>
|
||
<button hx-get="/clients/{{ id }}/follow-logs/create/"
|
||
hx-target="#drawer-container" hx-swap="innerHTML"
|
||
@click="drawerOpen = true"
|
||
class="flex flex-col items-center gap-1 py-2 bg-primary-600 hover:bg-primary-700
|
||
text-white text-xs font-medium rounded-md transition-colors">
|
||
<svg class="w-4 h-4"><!-- Heroicons pencil-square --></svg>
|
||
写跟进
|
||
</button>
|
||
<button class="flex flex-col items-center gap-1 py-2 bg-primary-600 hover:bg-primary-700
|
||
text-white text-xs font-medium rounded-md transition-colors">
|
||
<svg class="w-4 h-4"><!-- Heroicons flag --></svg>
|
||
报备/常看
|
||
</button>
|
||
</div>
|
||
```
|
||
|
||
**[状态变更操作网格(P0)]**
|
||
|
||
```html
|
||
<div class="grid grid-cols-2 gap-1 px-4 py-3 border-t border-neutral-100">
|
||
<!-- 收藏 -->
|
||
<button class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons star --></svg>
|
||
收藏
|
||
</button>
|
||
<!-- 不置顶 -->
|
||
<button class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons arrow-down --></svg>
|
||
不置顶
|
||
</button>
|
||
<!-- 改等级(触发 Modal) -->
|
||
<button hx-get="/clients/{{ id }}/change-grade/"
|
||
hx-target="#modal-container" hx-swap="innerHTML"
|
||
@click="modalOpen = true"
|
||
class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons adjustments-horizontal --></svg>
|
||
改等级
|
||
</button>
|
||
<!-- 改状态 -->
|
||
<button hx-get="/clients/{{ id }}/change-status/"
|
||
hx-target="#modal-container" hx-swap="innerHTML"
|
||
@click="modalOpen = true"
|
||
class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons arrow-path --></svg>
|
||
改状态
|
||
</button>
|
||
<!-- 转公客 -->
|
||
<button class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons users --></svg>
|
||
转公客
|
||
</button>
|
||
<!-- 转成交 -->
|
||
<button hx-get="/clients/{{ id }}/convert-deal/"
|
||
hx-target="#modal-container" hx-swap="innerHTML"
|
||
@click="modalOpen = true"
|
||
class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons check-badge --></svg>
|
||
转成交
|
||
</button>
|
||
<!-- 转无效 -->
|
||
<button class="flex items-center gap-2 px-3 py-2 text-xs text-danger-600
|
||
hover:bg-red-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-danger-500"><!-- Heroicons x-circle --></svg>
|
||
转无效
|
||
</button>
|
||
<!-- 编辑客源 -->
|
||
<button hx-get="/clients/{{ id }}/edit-basic/"
|
||
hx-target="#modal-container" hx-swap="innerHTML"
|
||
@click="modalOpen = true"
|
||
class="flex items-center gap-2 px-3 py-2 text-xs text-neutral-600
|
||
hover:bg-neutral-50 rounded-md transition-colors">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- Heroicons pencil --></svg>
|
||
编辑客源
|
||
</button>
|
||
</div>
|
||
```
|
||
|
||
**[待办提醒区块(P1)]**
|
||
|
||
```html
|
||
<div class="px-4 py-3 border-t border-neutral-100">
|
||
<div class="flex items-center justify-between mb-1">
|
||
<span class="text-xs font-medium text-neutral-700">待办提醒 ({{ todo_count }})</span>
|
||
<button class="text-xs text-info-600 hover:underline">写办</button>
|
||
</div>
|
||
<p class="text-xs text-neutral-400">系统会在设置的时间点发送跟进通知</p>
|
||
</div>
|
||
```
|
||
|
||
#### 2.7.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 改等级/改状态/转成交/编辑客源 弹窗容器 |
|
||
|
||
#### 2.7.5 空状态设计
|
||
|
||
不适用(面板始终显示)
|
||
|
||
#### 2.7.6 Loading 状态
|
||
|
||
右侧面板初始 SSR 渲染,HTMX 更新时对应区域显示 `opacity-50 pointer-events-none` 禁用态。
|
||
|
||
---
|
||
|
||
### 2.8 Section 右侧:联系人面板(P0 🔴)
|
||
|
||
#### 2.8.1 页面概述
|
||
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/联系人.png`
|
||
|
||
#### 2.8.2 布局结构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ 联系人 [查看号码] [新增联系人] 操作链接 │
|
||
├─────────────────────────────────────────────┤
|
||
│ [联系人条目] │
|
||
│ 姓名 · 称呼 │
|
||
│ 电话1: +86 137****8888 [默认拨打] [接通N次] │
|
||
│ 微信: - QQ: - 备注: - │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.8.3 区域详细规范
|
||
|
||
**[面板 Header]**
|
||
|
||
| 元素 | 规格 |
|
||
|---|---|
|
||
| 标题 | `text-sm font-semibold text-neutral-800` |
|
||
| 查看号码 | `text-xs text-info-600 hover:underline`;点击触发 HTMX POST 到 `/clients/{id}/reveal-phone/`,响应返回明文号码,同时后端写入 sensitive_view 日志 |
|
||
| 新增联系人 | `text-xs text-info-600 hover:underline`;触发 Drawer |
|
||
|
||
**[联系人条目]**
|
||
|
||
```html
|
||
<div class="space-y-2">
|
||
<div class="flex items-start gap-2">
|
||
<div class="flex-1 min-w-0">
|
||
<p class="text-sm font-medium text-neutral-800">
|
||
{{ contact.name }} <span class="text-neutral-400 text-xs">{{ contact.gender_display }}</span>
|
||
</p>
|
||
<!-- 手机号(脱敏) -->
|
||
<p class="text-xs text-neutral-600 mt-1">
|
||
电话1:
|
||
<span id="phone-display-{{ contact.id }}" class="tabular-nums">
|
||
{{ contact.phone_masked }}
|
||
</span>
|
||
<span class="ml-2 text-neutral-400">默认拨打</span>
|
||
<span class="ml-1 text-neutral-400">接通{{ contact.call_success_count }}次</span>
|
||
<span class="ml-1 text-neutral-400">拨打{{ contact.call_total_count }}次</span>
|
||
</p>
|
||
<p class="text-xs text-neutral-400 mt-0.5">微信: {{ contact.wechat|default:"-" }}</p>
|
||
</div>
|
||
<button class="text-primary-600 hover:text-primary-700 flex-shrink-0 p-1">
|
||
<svg class="w-4 h-4"><!-- Heroicons phone --></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
**查看号码交互:**
|
||
|
||
```html
|
||
<button hx-post="/clients/{{ id }}/contacts/{{ contact.id }}/reveal-phone/"
|
||
hx-target="#phone-display-{{ contact.id }}"
|
||
hx-swap="innerHTML"
|
||
hx-confirm="查看号码将会留下操作记录,确认继续?"
|
||
class="text-xs text-info-600 hover:underline">
|
||
查看号码
|
||
</button>
|
||
```
|
||
|
||
#### 2.8.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 新增联系人表单 |
|
||
|
||
#### 2.8.5 空状态设计
|
||
|
||
联系人列表为空时显示 `text-xs text-neutral-400` 提示 "暂无联系人"。
|
||
|
||
---
|
||
|
||
### 2.9 Section 右侧:相关员工面板(P0 🔴)
|
||
|
||
#### 2.9.1 页面概述
|
||
|
||
- **竞品参考截图**:`Project/fonrey/screenshots/客源/相关员工.png`
|
||
|
||
#### 2.9.2 布局结构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ 相关员工 [编辑] │
|
||
├─────────────────────────────────────────────┤
|
||
│ 【首录人】 ☎ 电话图标 │
|
||
│ 都市港湾店一组 雷威 │
|
||
│ 参与时间: 2026-04-17 19:21 │
|
||
│ 【归属人】 ☎ 电话图标 │
|
||
│ 都市港湾店一组 雷威 │
|
||
│ 参与时间: 2026-04-17 19:21 │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.9.3 区域详细规范
|
||
|
||
```html
|
||
<div class="bg-white rounded-lg border border-neutral-200 p-4">
|
||
<div class="flex items-center justify-between mb-3">
|
||
<h3 class="text-sm font-semibold text-neutral-800">相关员工</h3>
|
||
{% if can_edit_staff %}
|
||
<button class="text-xs text-info-600 hover:underline">编辑</button>
|
||
{% endif %}
|
||
</div>
|
||
<div class="space-y-3">
|
||
{% for staff in related_staff %}
|
||
<div class="flex items-start justify-between">
|
||
<div>
|
||
<p class="text-xs font-medium text-neutral-700">【{{ staff.role_display }}】</p>
|
||
<p class="text-sm text-neutral-800">{{ staff.department }} {{ staff.name }}</p>
|
||
<p class="text-xs text-neutral-400">参与时间: {{ staff.joined_at|date:"Y-m-d H:i" }}</p>
|
||
</div>
|
||
<button class="text-primary-600 hover:text-primary-700 p-1 flex-shrink-0">
|
||
<svg class="w-4 h-4"><!-- Heroicons phone --></svg>
|
||
</button>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
| 元素 | 权限控制 |
|
||
|---|---|
|
||
| 编辑按钮 | 仅管理员/店长可见(`{% if can_edit_staff %}`) |
|
||
| 电话图标 | 拨打员工内部电话,无需权限控制 |
|
||
|
||
---
|
||
|
||
## 3. 弹窗/抽屉设计规范
|
||
|
||
### 3.1 编辑基础信息(P1 🟡)
|
||
|
||
#### 3.1.1 触发方式
|
||
|
||
- **触发位置**:右侧客源信息概览面板 → "编辑客源" 按钮
|
||
- **组件类型**:Modal Dialog(内容字段多,但不足以需要 Drawer 宽度)
|
||
- **尺寸**:`max-w-2xl`(宽型)+ 最大高度 `max-h-[80vh]` 内部滚动
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/编辑基础信息.png`
|
||
|
||
#### 3.1.2 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值/预填值 |
|
||
|---|---|---|---|---|
|
||
| 需求类型 | Checkbox(二手/新房) | 是 | 至少选一 | 预填当前值 |
|
||
| 用途 | Select | 是 | 必选 | 预填当前值 |
|
||
| 来源 | Select | 是 | 必选 | 预填当前值 |
|
||
| 购房目的 | 多选 Checkbox | 否 | — | 预填当前值 |
|
||
| 付款方式 | Select | 否 | — | 预填 |
|
||
| 名下房产 | Select | 否 | — | 预填 |
|
||
| 贷款记录 | Radio(有/无) | 否 | — | 预填 |
|
||
| 证件类型 | Select | 否 | — | 预填 |
|
||
| 证件号码 | Text Input | 否 | 身份证 18 位格式校验 | 预填 |
|
||
| 意向学校 | 动态 Text Input(可多行) | 否 | — | 预填 |
|
||
| 入学时间 | Select(年份) | 否 | — | 预填 |
|
||
|
||
#### 3.1.3 提交行为
|
||
|
||
- **提交方式**:`hx-patch="/clients/{{ id }}/edit-basic/"`
|
||
- **成功响应**:关闭 Modal + Toast "保存成功" + `hx-get` 刷新右侧面板
|
||
- **失败响应(422)**:字段级 `<p class="text-xs text-danger-600 mt-1">` 错误提示
|
||
- **HTMX 属性**:
|
||
|
||
```html
|
||
<form hx-patch="/clients/{{ id }}/edit-basic/"
|
||
hx-target="#modal-container"
|
||
hx-swap="innerHTML"
|
||
hx-on::after-request="if(event.detail.successful){ modalOpen = false; htmx.trigger('#sidebar', 'refresh') }">
|
||
```
|
||
|
||
#### 3.1.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 弹窗容器 |
|
||
| Multi-select Tag Input | §17 | 购房目的多选 |
|
||
|
||
---
|
||
|
||
### 3.2 改等级(P0 🔴)
|
||
|
||
#### 3.2.1 触发方式
|
||
|
||
- **触发位置**:右侧面板 → "改等级" 按钮
|
||
- **组件类型**:Modal Dialog(内容极简)
|
||
- **尺寸**:`max-w-sm`
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/改等级.png`
|
||
|
||
#### 3.2.2 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| 原等级 | 只读文本 | — | — | `client.grade_display` |
|
||
| 新等级 | Select | 是 | 必选;不能与原等级相同 | 无 |
|
||
|
||
**新等级选项**:A_urgent(A+紧急)/ A / B / C(默认)/ D / E
|
||
|
||
#### 3.2.3 提交行为
|
||
|
||
- **提交方式**:`hx-post="/clients/{{ id }}/change-grade/"`
|
||
- **成功**:关闭 Modal + Toast "等级已更新" + 刷新右侧状态标签行
|
||
- **失败(422)**:Select 下方显示错误文字
|
||
- **HTMX 属性**:
|
||
|
||
```html
|
||
<form hx-post="/clients/{{ id }}/change-grade/"
|
||
hx-target="#status-badge-row"
|
||
hx-swap="outerHTML"
|
||
hx-on::after-request="if(event.detail.successful){ modalOpen = false }">
|
||
```
|
||
|
||
#### 3.2.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 弹窗容器 |
|
||
|
||
---
|
||
|
||
### 3.3 改状态(P0 🔴)
|
||
|
||
#### 3.3.1 触发方式
|
||
|
||
- **触发位置**:右侧面板 → "改状态" 按钮
|
||
- **组件类型**:Modal Dialog
|
||
- **尺寸**:`max-w-md`
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/改状态.png`
|
||
|
||
#### 3.3.2 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| 原状态 | 只读文本 | — | — | `client.status_display`(如"求购") |
|
||
| 新状态 | Select | 是 | 必选;受状态机约束(见 §4.1) | 无 |
|
||
| 等级 | Select | 否 | — | 当前等级预填 |
|
||
| 更改理由 | Textarea | 是 | 非空 | 无 |
|
||
|
||
**新状态可选项(根据当前状态动态过滤,后端控制)**:
|
||
- 当前 `buying`/`renting` → 可选:`suspended`(暂缓)、`public`(转公)、`bought`/`rented_done`(成交)、`invalid`(无效)
|
||
|
||
#### 3.3.3 提交行为
|
||
|
||
- **提交方式**:`hx-post="/clients/{{ id }}/change-status/"`
|
||
- **成功**:关闭 Modal + Toast "状态已更新" + 刷新整个右侧面板
|
||
- **失败(422)**:字段级错误
|
||
- **HTMX 属性**:
|
||
|
||
```html
|
||
<form hx-post="/clients/{{ id }}/change-status/"
|
||
hx-target="#sidebar-panel"
|
||
hx-swap="outerHTML"
|
||
hx-on::after-request="if(event.detail.successful){ modalOpen = false }">
|
||
```
|
||
|
||
#### 3.3.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 弹窗容器 |
|
||
|
||
---
|
||
|
||
### 3.4 转成交(P0 🔴)
|
||
|
||
#### 3.4.1 触发方式
|
||
|
||
- **触发位置**:右侧面板 → "转成交" 按钮
|
||
- **组件类型**:Modal Dialog
|
||
- **尺寸**:`max-w-lg`
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/转成交.png`
|
||
|
||
#### 3.4.2 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| 状态 | Radio(我购/我租) | 是 | 必选 | 我购 |
|
||
| 房源类型 | Radio(二手/新房) | 是 | 必选 | 二手 |
|
||
| 成交房源 | 触发选择房源 Modal 的链接 | 是 | 必选 | 无 |
|
||
| 成交日期 | Date Picker | 是 | 不能早于委托日期 | 今日 |
|
||
| 成交价格 | Number Input | 是 | 正数,单位万元 | 无 |
|
||
| 成交方 | 员工标签(可删除,可搜索添加) | 是 | 至少一人 | 当前归属人 |
|
||
|
||
**成交房源选择:**
|
||
|
||
```html
|
||
<!-- 点击触发 "选择成交房源" 宽型 Modal(§3.5) -->
|
||
<button type="button"
|
||
hx-get="/properties/select-modal/"
|
||
hx-target="#property-select-modal-container"
|
||
hx-swap="innerHTML"
|
||
@click="propertyModalOpen = true"
|
||
class="text-sm text-info-600 hover:underline">
|
||
+ 选择成交房源
|
||
</button>
|
||
<!-- 已选后展示 -->
|
||
<div id="selected-property-display" class="hidden text-sm text-neutral-800">
|
||
<!-- 由 §3.5 Modal 选中后注入 -->
|
||
</div>
|
||
<input type="hidden" name="property_id" id="selected-property-id">
|
||
```
|
||
|
||
#### 3.4.3 提交行为
|
||
|
||
- **提交方式**:`hx-post="/clients/{{ id }}/convert-deal/"`
|
||
- **成功**:关闭 Modal + Toast "已成功转为成交客" + 页面跳转至成交客详情
|
||
- **失败(422)**:字段级错误
|
||
- **特殊**:成功后客源状态不可逆(`bought`/`rented_done`),需在提交前显示 `hx-confirm` 二次确认
|
||
|
||
```html
|
||
<form hx-post="/clients/{{ id }}/convert-deal/"
|
||
hx-confirm="确认将此客户标记为成交?此操作不可撤销。"
|
||
hx-on::after-request="if(event.detail.successful){ window.location.href='/clients/bought/{{ id }}/' }">
|
||
```
|
||
|
||
#### 3.4.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 弹窗容器 |
|
||
| Date Range Picker | §9 | 成交日期选择(单日模式) |
|
||
|
||
---
|
||
|
||
### 3.5 选择成交房源(P0 🔴)
|
||
|
||
#### 3.5.1 触发方式
|
||
|
||
- **触发位置**:转成交 Modal → "选择成交房源" 链接
|
||
- **组件类型**:Modal Dialog(宽型,含搜索表格)
|
||
- **尺寸**:`max-w-5xl`(全宽搜索表)
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/选择成交房源.png`
|
||
|
||
#### 3.5.2 表单字段规范
|
||
|
||
**[搜索筛选区]**
|
||
|
||
| 字段 | 组件类型 | 说明 |
|
||
|---|---|---|
|
||
| 关键词 | Text Input | 房源编号/楼盘地址/业主姓名/电话 |
|
||
| 楼栋 / 单元 / 房号 | 三个联动 Text Input | 精确定位 |
|
||
| 区域 | Select | 城区筛选 |
|
||
| 状态 | Select | 房源状态 |
|
||
| 相关方 | Select + 员工搜索 | 归属人筛选 |
|
||
|
||
**[结果表格]**
|
||
|
||
| 列名 | 宽度 | 说明 |
|
||
|---|---|---|
|
||
| 选择(Radio) | 48px | 单选 |
|
||
| 房源名称 | auto | 含楼栋单元房号 |
|
||
| 交易类型 | 80px | 买卖/租赁 |
|
||
| 状态 | 80px | Badge 展示 |
|
||
| 用途 | 60px | 住宅/商业 |
|
||
| 城区商圈 | 100px | 文本 |
|
||
| 房型 | 80px | X/X/X |
|
||
| 楼层 | 80px | X/X |
|
||
| 面积(㎡) | 80px | `tabular-nums` |
|
||
|
||
**分页**:`共N条 | ‹ 1 2 3 … ›`(§2 Pagination 组件)
|
||
|
||
#### 3.5.3 提交行为
|
||
|
||
- **提交方式**:点击"确定"将选中房源 ID 写入父 Modal 的隐藏字段
|
||
- **HTMX 属性**:搜索按钮 `hx-get` 刷新结果表
|
||
- **Alpine 管理**:`selectedPropertyId` 状态,选中后激活"确定"按钮
|
||
|
||
```html
|
||
<div x-data="{ selectedPropertyId: null, selectedPropertyName: '' }">
|
||
<!-- 表格内 Radio 按钮绑定 @change="selectedPropertyId = $el.value; selectedPropertyName = ..." -->
|
||
<!-- 底部 Footer -->
|
||
<div class="flex items-center justify-between">
|
||
<span class="text-sm text-neutral-500">
|
||
已选 (<span x-text="selectedPropertyId ? 1 : 0"></span>/1)
|
||
</span>
|
||
<button :disabled="!selectedPropertyId"
|
||
@click="
|
||
document.getElementById('selected-property-id').value = selectedPropertyId;
|
||
document.getElementById('selected-property-display').textContent = selectedPropertyName;
|
||
propertyModalOpen = false;
|
||
"
|
||
class="btn-primary disabled:opacity-40 disabled:cursor-not-allowed">
|
||
确定
|
||
</button>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### 3.5.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Modal Dialog | §7 | 宽型容器 `max-w-5xl` |
|
||
| Data Table | §1 | 房源选择结果表 |
|
||
| Pagination | §2 | 表格分页 |
|
||
|
||
---
|
||
|
||
### 3.6 写入跟进(P0 🔴)
|
||
|
||
#### 3.6.1 触发方式
|
||
|
||
- **触发位置**:右侧面板 → "写跟进" 按钮;或跟进记录 Tab 内"写入跟进"按钮
|
||
- **组件类型**:Drawer(右侧抽屉,字段较多但无需全页)
|
||
- **尺寸**:`w-[480px]`
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/跟进记录-写入跟进.png`
|
||
|
||
#### 3.6.2 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| 跟进方式 | Select / Radio Pills | 是 | 必选 | 电话 |
|
||
| 跟进目的 | 多选 Checkbox | 否 | — | 无 |
|
||
| 跟进内容 | Textarea | 是 | ≥ 6 字 | 无 |
|
||
| 跟进标签 | §17 Multi-select Tag Input | 否 | — | 无 |
|
||
| 跟进时间 | DateTime Picker | 是 | 不能超过当前时间 | 当前时间 |
|
||
| 附件 | 文件上传 | 否 | 最多 20MB,格式 bmp/jpg/png/gif | 无 |
|
||
| 是否开放 | Toggle | 否 | — | 开放(`true`) |
|
||
|
||
> **注**:跟进方式选项(含图标):电话 / 上门 / 短信 / 微信 / 其他
|
||
|
||
#### 3.6.3 提交行为
|
||
|
||
- **提交方式**:`hx-post="/clients/{{ id }}/follow-logs/create/"`
|
||
- **成功**:关闭 Drawer + Toast "跟进记录已保存" + `hx-trigger` 刷新跟进记录 Tab 列表
|
||
- **失败(422)**:字段级错误
|
||
- **HTMX 属性**:
|
||
|
||
```html
|
||
<form hx-post="/clients/{{ id }}/follow-logs/create/"
|
||
hx-encoding="multipart/form-data"
|
||
hx-target="#log-list"
|
||
hx-swap="afterbegin"
|
||
hx-on::after-request="if(event.detail.successful){ drawerOpen = false; showToast('跟进记录已保存') }">
|
||
```
|
||
|
||
#### 3.6.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Drawer | §16 | 抽屉容器 |
|
||
| Multi-select Tag Input | §17 | 跟进标签选择 |
|
||
| Date Range Picker | §9 | 跟进时间(单时间模式) |
|
||
|
||
---
|
||
|
||
### 3.7 新增带看(P0 🔴)
|
||
|
||
#### 3.7.1 触发方式
|
||
|
||
- **触发位置**:右侧面板 → "报备/常看" 按钮;或带看 Tab → 空状态"新增带看"
|
||
- **组件类型**:全页表单(字段多、含文件上传、含房源关联)
|
||
- **URL**:`/clients/<client_id>/viewings/create/`
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/新增带看.png`
|
||
|
||
#### 3.7.2 布局结构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ 面包屑: 客源 / 客源管理 / 新增带看 │
|
||
│ 页面标题: 新增带看 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [带看信息卡片] │
|
||
│ 业务类型(只读) | 带看客户(只读+查看详情链接) │
|
||
│ 经纪人(只读) │
|
||
│ 带看时间(日期+时间段)* │
|
||
│ 陪看人(最多5位)+ 合作带看人(最多5位) │
|
||
│ 带看情况(Textarea)* │
|
||
│ 附件上传 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [带看房源卡片] │
|
||
│ 添加房源(至少1套,最多10套)* │
|
||
│ 每套房源选择客户意向程度 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [操作按钮] 提交(primary) 保存(secondary) 取消 │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 3.7.3 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| 业务类型 | 只读文本 | — | — | 二手 |
|
||
| 带看客户 | 只读文本 + 链接 | — | — | 当前客源 |
|
||
| 经纪人 | 只读文本 | — | — | 当前登录人 |
|
||
| 带看时间 | Date Picker + Time Range | 是 | 开始时间 < 结束时间 | 无 |
|
||
| 陪看人 | 员工多选搜索 | 否 | ≤ 5 人 | 无 |
|
||
| 合作带看人 | 员工多选搜索 | 否 | ≤ 5 人 | 无 |
|
||
| 带看情况 | Textarea | 是 | ≥ 6 字 | 无 |
|
||
| 附件 | 文件上传 | 否 | 每张 ≤ 20MB,bmp/jpg/png/gif | 无 |
|
||
| 带看房源 | 动态房源选择列表 | 是 | 1-10 套 | 无 |
|
||
| 每套意向程度 | Select | 是(每套) | 必选 | 无 |
|
||
|
||
#### 3.7.4 提交行为
|
||
|
||
- **提交方式**:`hx-post="/clients/{{ id }}/viewings/create/"` `hx-encoding="multipart/form-data"`
|
||
- **成功**:跳转至带看详情页 + Toast "带看记录已提交"
|
||
- **失败(422)**:字段级错误,保留已填内容
|
||
- **"保存"按钮**:`hx-post="...?draft=true"`,保存草稿,不跳转
|
||
|
||
#### 3.7.5 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Date Range Picker | §9 | 带看时间段选择 |
|
||
| Dynamic Form Table | §18 | 带看房源动态增删列表 |
|
||
| Photo Gallery Manager | §12 | 附件上传管理 |
|
||
|
||
---
|
||
|
||
### 3.8 新增预约带看(P0 🔴)
|
||
|
||
#### 3.8.1 触发方式
|
||
|
||
- **触发位置**:带看 Tab → 预约子 Tab → 空状态 or "新增预约" 按钮
|
||
- **组件类型**:全页表单
|
||
- **URL**:`/clients/<client_id>/viewings/appointment/create/`
|
||
- **竞品截图**:`Project/fonrey/screenshots/客源/新增预约带看.png`
|
||
|
||
#### 3.8.2 表单字段规范
|
||
|
||
| 字段名 | 组件类型 | 必填 | 校验规则 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| 业务类型 | 只读文本 | — | — | 二手 |
|
||
| 预约客户 | 只读文本 + 链接 | — | — | 当前客源 |
|
||
| 预约人 | 只读文本 | — | — | 当前登录人 |
|
||
| 预约时间(日期) | Date Picker | 是 | 不早于今日 | 无 |
|
||
| 预约时间(时刻) | Time Picker | 是 | — | 无 |
|
||
| 陪看人 | 员工多选 | 否 | ≤ 5 人 | 无 |
|
||
| 合作带看人 | 员工多选 | 否 | ≤ 5 人 | 无 |
|
||
| 预约房源 | 动态房源选择列表 | 是 | 1-10 套 | 无 |
|
||
|
||
#### 3.8.3 提交行为
|
||
|
||
- **提交方式**:`hx-post="/clients/{{ id }}/viewings/appointment/create/"`
|
||
- **成功**:跳转带看 Tab(预约子 Tab)+ Toast "预约带看已创建"
|
||
- **失败(422)**:字段级错误
|
||
|
||
#### 3.8.4 使用的特殊组件
|
||
|
||
| 组件名 | 来源 | 用途 |
|
||
|---|---|---|
|
||
| Date Range Picker | §9 | 预约日期+时刻 |
|
||
| Dynamic Form Table | §18 | 预约房源动态列表 |
|
||
|
||
---
|
||
|
||
## 4. 交互状态规范
|
||
|
||
### 4.1 全局状态机
|
||
|
||
客源状态流转(`clients.status` 字段):
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ buying │
|
||
│ (求购,可逆态) │
|
||
└──────────────────────────────┘
|
||
│ │
|
||
┌──────┘ └──────┐
|
||
▼ ▼
|
||
suspended(暂缓) public(转公)
|
||
(可逆 → 回到 buying) (不可逆)
|
||
│
|
||
└──────────────────────────────────┐
|
||
▼
|
||
bought(成交)(不可逆)
|
||
invalid(无效)(不可逆)
|
||
```
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ renting │
|
||
│ (求租,可逆态) │
|
||
└──────────────────────────────┘
|
||
│ │
|
||
┌──────┘ └──────┐
|
||
▼ ▼
|
||
suspended(暂缓) public(转公)
|
||
│
|
||
└──────────────────────────────────┐
|
||
▼
|
||
rented_done(成交)(不可逆)
|
||
invalid(无效)(不可逆)
|
||
```
|
||
|
||
**UI 控制规则:**
|
||
- 右侧面板"转公客"/"转成交"/"转无效"按钮在状态已为不可逆态时 `disabled` + `opacity-40 cursor-not-allowed`
|
||
- "改状态" Modal 中新状态 Select 选项由后端根据当前状态动态过滤
|
||
|
||
---
|
||
|
||
### 4.2 权限控制矩阵
|
||
|
||
| 操作 | 经纪人(本人) | 经纪人(他人) | 店长 | 管理员 |
|
||
|---|---|---|---|---|
|
||
| 查看需求信息 | ✅ | 受限 | ✅ | ✅ |
|
||
| 编辑需求信息 | ✅ | ❌ | ✅ | ✅ |
|
||
| 写入跟进 | ✅ | ❌ | ✅ | ✅ |
|
||
| 查看跟进记录 | ✅ | ❌(敏感不可见) | ✅ | ✅ |
|
||
| 查看联系人明文号码 | ✅(留痕) | ❌ | ✅(留痕) | ✅(留痕) |
|
||
| 新增联系人 | ✅ | ❌ | ✅ | ✅ |
|
||
| 改等级 | ✅ | ❌ | ✅ | ✅ |
|
||
| 改状态 | ✅ | ❌ | ✅ | ✅ |
|
||
| 转公客 | ✅ | ❌ | ✅ | ✅ |
|
||
| 转成交 | ✅ | ❌ | ✅ | ✅ |
|
||
| 转无效 | ✅ | ❌ | ✅ | ✅ |
|
||
| 新增带看 | ✅ | ❌ | ✅ | ✅ |
|
||
| 编辑相关员工 | ❌ | ❌ | ✅ | ✅ |
|
||
| 查看操作日志 | 部分 | ❌ | ✅ | ✅ |
|
||
| 删除跟进记录 | ✅(非敏感) | ❌ | ✅(非敏感) | ✅(非敏感) |
|
||
|
||
---
|
||
|
||
### 4.3 HTMX 请求规范
|
||
|
||
| 操作 | hx-trigger | hx-method + URL | hx-target | hx-swap | Loading 行为 |
|
||
|---|---|---|---|---|---|
|
||
| Tab 切换(需求信息) | click | GET `/clients/{id}/requirements/` | `#tab-content` | innerHTML | `htmx-indicator` 骨架屏 |
|
||
| Tab 切换(跟进记录) | click | GET `/clients/{id}/follow-logs/` | `#tab-content` | innerHTML | 骨架屏 |
|
||
| Tab 切换(带看) | click | GET `/clients/{id}/viewings/` | `#tab-content` | innerHTML | 骨架屏 |
|
||
| Tab 切换(客源解读) | click | GET `/clients/{id}/insights/` | `#tab-content` | innerHTML | 骨架屏 |
|
||
| Tab 切换(智能配房) | click | GET `/clients/{id}/property-matches/` | `#tab-content` | innerHTML | 骨架屏 |
|
||
| 跟进子 Tab 切换 | click | GET `/clients/{id}/follow-logs/?type=written` | `#log-list` | innerHTML | 列表 spinner |
|
||
| 跟进记录日期筛选 | change | GET `/clients/{id}/follow-logs/` | `#log-list` | innerHTML | 列表 spinner |
|
||
| 跟进记录加载更多 | click | GET `/clients/{id}/follow-logs/?page=N` | `#log-list` | beforeend | 按钮 spinner |
|
||
| 带看子 Tab 切换 | click | GET `/clients/{id}/viewings/?type=appointment` | `#viewing-list` | innerHTML | 列表 spinner |
|
||
| 查看联系人号码 | click | POST `/clients/{id}/contacts/{cid}/reveal-phone/` | `#phone-display-{cid}` | innerHTML | 行内 spinner |
|
||
| 打开改等级 Modal | click | GET `/clients/{id}/change-grade/` | `#modal-container` | innerHTML | 无(预加载) |
|
||
| 提交改等级 | submit | POST `/clients/{id}/change-grade/` | `#status-badge-row` | outerHTML | 按钮 loading |
|
||
| 打开改状态 Modal | click | GET `/clients/{id}/change-status/` | `#modal-container` | innerHTML | 无 |
|
||
| 提交改状态 | submit | POST `/clients/{id}/change-status/` | `#sidebar-panel` | outerHTML | 按钮 loading |
|
||
| 打开转成交 Modal | click | GET `/clients/{id}/convert-deal/` | `#modal-container` | innerHTML | 无 |
|
||
| 提交转成交 | submit | POST `/clients/{id}/convert-deal/` | body | — | 按钮 loading |
|
||
| 选择成交房源搜索 | click | GET `/properties/select-modal/?q=...` | `#property-table-body` | innerHTML | 表格 spinner |
|
||
| 打开写跟进 Drawer | click | GET `/clients/{id}/follow-logs/create/` | `#drawer-container` | innerHTML | 无 |
|
||
| 提交写跟进 | submit | POST `/clients/{id}/follow-logs/create/` | `#log-list` | afterbegin | 按钮 loading |
|
||
| 编辑基础信息提交 | submit | PATCH `/clients/{id}/edit-basic/` | `#sidebar-panel` | outerHTML | 按钮 loading |
|
||
| 客源解读时段切换 | click | GET `/clients/{id}/insights/?period=7d` | `#insight-content` | innerHTML | 内容区 spinner |
|
||
|
||
---
|
||
|
||
## 5. 关键数据字段说明
|
||
|
||
| 字段名(英文) | 显示名 | 数据类型 | 说明 |
|
||
|---|---|---|---|
|
||
| `client.id` | 客源 ID | UUID | 路由参数 |
|
||
| `client.client_no` | 客户编号 | string | 格式 `{日期}{类型}{随机}` |
|
||
| `client.title` | 需求标题 | string | 如"姚叔叔置换电梯两房(上门)" |
|
||
| `client.status` | 状态 | enum | buying/renting/suspended/public/bought/rented_done/invalid |
|
||
| `client.grade` | 等级 | enum | A_urgent/A/B/C/D/E |
|
||
| `client.client_type` | 客源类型 | enum | private(私客)/public(公客)/deal(成交客) |
|
||
| `client.source` | 客户来源 | string | 如"线下·门店接待" |
|
||
| `client.requirement_type` | 需求类型 | enum | second_hand/new/rent |
|
||
| `client.property_usage` | 房源用途 | enum | residential/commercial/... |
|
||
| `client.purchase_purpose` | 购房目的 | multi-enum | 刚需/投资/学区/改善 |
|
||
| `client.payment_method` | 付款方式 | enum | full/loan/... |
|
||
| `client.owned_properties` | 名下房产 | string | 自由文本 |
|
||
| `client.loan_record` | 贷款记录 | boolean | 有/无 |
|
||
| `client.id_type` | 证件类型 | enum | id_card/passport/... |
|
||
| `client.id_number` | 证件号码 | string(加密) | AES 加密,展示时脱敏 |
|
||
| `client.preferred_school` | 意向学校 | string list | 可多条 |
|
||
| `client.school_start_year` | 入学时间 | year | — |
|
||
| `client.last_follow_at` | 最近跟进时间 | datetime | — |
|
||
| `client.created_at` | 委托日期 | datetime | — |
|
||
| `client.activity_level` | 活跃度 | int | Celery 每日计算 |
|
||
| `client_contacts.name` | 联系人姓名 | string | — |
|
||
| `client_contacts.phone_hash` | 手机号 Hash | string | SHA-256,用于重复检测 |
|
||
| `client_contacts.phone_encrypted` | 手机号密文 | string | AES 加密,展示时解密脱敏 |
|
||
| `client_contacts.gender` | 性别 | enum | male/female |
|
||
| `client_contacts.wechat` | 微信号 | string | — |
|
||
| `client_requirements.price_min/max` | 总价范围 | decimal | 万元 |
|
||
| `client_requirements.area_min/max` | 面积范围 | decimal | ㎡ |
|
||
| `client_requirements.rooms` | 居室 | string | — |
|
||
| `client_requirements.floor_preference` | 楼层偏好 | multi-enum | — |
|
||
| `client_requirements.preferred_districts` | 意向商圈 | string list | — |
|
||
| `client_follow_logs.log_type` | 跟进类型 | enum | written/modified/sensitive_view/other/system |
|
||
| `client_follow_logs.follow_method` | 跟进方式 | enum | phone/door/sms/wechat/other |
|
||
| `client_follow_logs.content` | 跟进内容 | text | — |
|
||
| `client_follow_logs.is_open` | 是否开放 | boolean | — |
|
||
| `client_viewings.viewing_time` | 带看时间 | datetime | — |
|
||
| `client_viewings.situation` | 带看情况 | text | — |
|
||
| `client_viewings.viewing_count` | 带看次数 label | string | 一看/二看/... |
|
||
| `client_property_matches.match_type` | 配房类型 | enum | recorded/system |
|
||
| `client_property_matches.group_label` | 分组标签 | string | 优质户型/降价/热门/新上 |
|
||
| `related_staff.role` | 员工角色 | enum | first_recorder(首录人)/owner(归属人) |
|
||
| `related_staff.joined_at` | 参与时间 | datetime | — |
|
||
|
||
---
|
||
|
||
## 6. 竞品截图对应关系
|
||
|
||
| 截图路径 | 对应功能 | 对应文档章节 | 采纳的设计要点 |
|
||
|---|---|---|---|
|
||
| `私客详情.png` | 详情页整体框架 | §2.1 | 左右双栏布局;右侧 Banner 使用品牌主色(竞品橙色改为 Teal);Tab 导航在左侧内容区顶部 |
|
||
| `需求信息.png` | 需求信息 Tab 内容 | §2.2 | 3 列字段网格;右上角蓝色"编辑"链接;空字段显示 `-` |
|
||
| `跟进记录-全部.png` | 跟进记录时间线 | §2.3 | 日期分组橙色圆点;条目卡片带类型 Badge;"已开放"/"隐藏"小字操作链接 |
|
||
| `跟进记录-写入跟进.png` | 跟进子 Tab + 扩展筛选 | §2.3 | 跟进目的多选展开/收起;pill 式子 Tab 导航 |
|
||
| `跟进记录-敏感信息跟进.png` | 敏感信息跟进空状态 | §2.3.5 | 空状态文案"暂无跟进" |
|
||
| `跟进记录-修改跟进.png` | 修改跟进空状态 | §2.3.5 | 同上 |
|
||
| `跟进记录-其他跟进.png` | 其他跟进(系统日志) | §2.3 | 【新增私客】等系统类型条目,无操作按钮 |
|
||
| `带看.png` | 带看 Tab 内容 | §2.4 | 预约/带看子 Tab;带看次数橙色 Badge;"详情 ›" 链接 |
|
||
| `新增带看.png` | 新增带看全页表单 | §3.7 | 分两个卡片区域(带看信息 + 带看房源);三个操作按钮(提交/保存/取消) |
|
||
| `新增预约带看.png` | 新增预约带看全页表单 | §3.8 | 日期+时刻分两个选择器;不含"保存"按钮(预约只有提交/取消) |
|
||
| `客源解读.png` | 客源解读 Tab | §2.5 | 时段筛选 Pill;活跃行为卡片;三个甜甜圈图(MVP 用百分比文字代替) |
|
||
| `二手配房.png` | 二手配房 Tab | §2.6 | 分组标题 + 房源卡片列表;录客/系统配房子 Tab;"批量分享"次要按钮 |
|
||
| `客源信息概览.png` | 右侧客源信息面板 | §2.7 | 字段列表 + 三主按钮 + 2列操作网格;"展开全部"收起切换 |
|
||
| `编辑基础信息.png` | 编辑基础信息 Modal | §3.1 | 宽型 Modal;多字段含联动(证件类型 → 证件号);意向学校可动态添加 |
|
||
| `改等级.png` | 改等级 Modal | §3.2 | 极简 2 字段;`max-w-sm` |
|
||
| `改状态.png` | 改状态 Modal | §3.3 | 4 字段含 Textarea;状态选项后端动态控制 |
|
||
| `转成交.png` | 转成交 Modal | §3.4 | Radio 选购/租、二手/新房;成交方员工标签组件 |
|
||
| `选择成交房源.png` | 选择成交房源宽型 Modal | §3.5 | 搜索 + 表格 + 分页;Radio 单选;"已选(0/1)"状态控制确定按钮 |
|
||
| `联系人.png` | 联系人面板 | §2.8 | 号码脱敏;"查看号码"留痕确认;接通/拨打次数展示 |
|
||
| `相关员工.png` | 相关员工面板 | §2.9 | 首录人/归属人标签;右侧电话图标;"编辑"仅管理员可见 |
|
||
|
||
---
|
||
|
||
## 7. 实现优先级与工期估算
|
||
|
||
| 页面/功能 | 优先级 | 特殊组件复杂度 | 工期估算(前端) |
|
||
|---|---|---|---|
|
||
| 详情页整体框架(双栏 + Tab) | P0 🔴 | ⭐ Tab Navigation | 1 天 |
|
||
| 需求信息 Tab(只读 + 编辑链接) | P0 🔴 | ⭐ | 0.5 天 |
|
||
| 右侧信息概览面板(字段 + 操作网格) | P0 🔴 | ⭐ | 1 天 |
|
||
| 跟进记录 Tab(时间线 + 子 Tab + 筛选) | P0 🔴 | ⭐⭐ Timeline + Date Range Picker | 2 天 |
|
||
| 写入跟进 Drawer | P0 🔴 | ⭐⭐ Drawer + Multi-select Tag | 1.5 天 |
|
||
| 带看 Tab(时间线) | P0 🔴 | ⭐ | 1 天 |
|
||
| 新增带看(全页表单) | P0 🔴 | ⭐⭐ Dynamic Form Table + 文件上传 | 2 天 |
|
||
| 新增预约带看(全页表单) | P0 🔴 | ⭐ Date Picker | 1 天 |
|
||
| 改等级 Modal | P0 🔴 | ⭐ | 0.5 天 |
|
||
| 改状态 Modal | P0 🔴 | ⭐ | 0.5 天 |
|
||
| 转成交 Modal | P0 🔴 | ⭐⭐ | 1 天 |
|
||
| 选择成交房源 Modal(搜索表格) | P0 🔴 | ⭐⭐ Data Table + Pagination | 1.5 天 |
|
||
| 联系人面板(含查看号码留痕) | P0 🔴 | ⭐ | 0.5 天 |
|
||
| 相关员工面板 | P0 🔴 | ⭐ | 0.5 天 |
|
||
| 编辑基础信息 Modal(完整表单) | P1 🟡 | ⭐⭐ Multi-select | 1.5 天 |
|
||
| 客源解读 Tab(数据图表) | P1 🟡 | ⭐⭐ 图表组件 | 2 天 |
|
||
| 二手配房 Tab(房源卡片网格) | P1 🟡 | ⭐⭐ | 1.5 天 |
|
||
| 待办提醒区块 | P1 🟡 | ⭐ | 0.5 天 |
|
||
| 收藏功能 | P1 🟡 | ⭐ | 0.5 天 |
|
||
| 查看操作日志 | P2 ⚫ | ⭐ | 0.5 天 |
|
||
| **合计 P0** | — | — | **~14.5 天** |
|
||
| **合计 P1** | — | — | **~6 天** |
|
||
| **合计 P2** | — | — | **~0.5 天** |
|
||
|
||
---
|
||
|
||
## 8. 开放问题(待决策)
|
||
|
||
| # | 问题 | 影响范围 | 待确认方 |
|
||
|---|---|---|---|
|
||
| 1 | 竞品 Banner 颜色为橙色 `#f5774a`,本系统使用 Teal `primary-600`。确认是否在客源详情页 Banner 引入橙色辅助色,还是统一用 Teal? | §2.7 右侧面板 Banner | 产品/设计 |
|
||
| 2 | 跟进目的选项约 25 个,竞品截图可见,是否与 PRD 中枚举完全一致?需后端确认字段枚举值。 | §2.3 跟进筛选 | 后端 |
|
||
| 3 | "报备/常看" 按钮的业务逻辑:是打开带看表单还是单独的"报备"功能?竞品未见明确说明。 | §2.7 快捷操作 | 产品 |
|
||
| 4 | 联系人"查看号码"是否需要每次查看都弹出确认对话框(`hx-confirm`),还是首次查看确认即可(本 session 内缓存权限)? | §2.8 | 产品/法务 |
|
||
| 5 | 带看房源"意向程度"的选项枚举值是什么?竞品未完整显示。 | §3.7 新增带看 | 后端 |
|
||
| 6 | "转公客"操作是否需要理由 Textarea(竞品未显示此 Modal)?直接 POST 还是需要二次确认弹窗? | §2.7 状态变更 | 产品 |
|
||
| 7 | 客源解读 Tab 数据是否 MVP 就需要真实 AI 接口,还是 P1 阶段可先用静态假数据 + 占位图? | §2.5 | 产品/后端 |
|
||
| 8 | 右侧面板"展开全部"默认折叠后显示几个字段?竞品显示约 10 个,全部展开约 14 个,需确认默认展示数量。 | §2.7 基础字段列表 | 产品 |
|
||
| 9 | 二手配房"批量分享"的分享目标是什么渠道(微信/短信/链接复制)?需确认 P1 实现范围。 | §2.6 | 产品 |
|
||
| 10 | `client_status_logs` 是否需要在详情页"查看操作日志"入口展示?竞品有此链接但 PRD 未详细说明展示方式(独立页 or Drawer)。 | P2 操作日志 | 产品 |
|