78 KiB
客源详情页 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.2 页面清单
- 1.3 用户角色与权限差异
- 页面设计规范
- 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.1 编辑基础信息(Modal)
- 3.2 改等级(Modal)
- 3.3 改状态(Modal)
- 3.4 转成交(Modal)
- 3.5 选择成交房源(Modal 宽型)
- 3.6 写入跟进(Drawer)
- 3.7 新增带看(全页表单)
- 3.8 新增预约带看(全页表单)
- 交互状态规范
- 4.1 客源状态机
- 4.2 权限控制矩阵
- 4.3 HTMX 请求规范
- 关键数据字段说明
- 竞品截图对应关系
- 实现优先级与工期估算
- 开放问题(待决策)
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(不可点击) |
[双栏主布局]
<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" 首次点击时加载 |
<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 区域显示骨架屏:
<!-- 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 结构:
<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 空状态设计
若需求信息尚未录入(所有字段均为空),在内容区中部显示:
<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 | 同上,与跟进目的共用展开/收起逻辑 |
<!-- 筛选区 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,不可删除 |
[加载更多]
<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 空状态设计
<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 状态
<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 空状态设计
<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]
<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 空状态设计
<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,选项:满意/不满意/已跟进 |
卡片网格布局:
<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 空状态设计
<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]
<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;本系统采用 Tealprimary-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 |
- |
字段行渲染:
<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>
[展开全部链接]
<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)]
<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)]
<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)]
<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 |
[联系人条目]
<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>
查看号码交互:
<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 区域详细规范
<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 属性:
<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 属性:
<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 属性:
<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 | 是 | 正数,单位万元 | 无 |
| 成交方 | 员工标签(可删除,可搜索添加) | 是 | 至少一人 | 当前归属人 |
成交房源选择:
<!-- 点击触发 "选择成交房源" 宽型 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二次确认
<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状态,选中后激活"确定"按钮
<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 属性:
<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 操作日志 | 产品 |