Files
nexus/Project/fonrey/UI_DESIGN/客源详情_UI.md
2026-04-25 20:24:32 +08:00

468 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 客源详情页 UI 设计文档
> **版本**v1.2 · **日期**2026-04-25
> **依赖规范**`Project/fonrey/UI_SYSTEM/UI_SYSTEM.md` v1.2、`Project/fonrey/UI_SYSTEM/组件规范设计.md`
> **视觉参考**`Project/fonrey/UI_SYSTEM/preview.html`(页面骨架、卡片层级、工具栏密度)
> **PRD 来源**`Project/fonrey/PRD/客源管理/客源管理模块PRD.md` §5.7 私客详情页
> **静态原型**`Project/fonrey/客源详情_静态原型.html`(以此为视觉真相来源)
---
## 1. 模块概述
### 1.1 页面目标
- 在一个页面内完成私客详情查看、跟进、带看、状态流转、联系人管理、相关员工查看。
- 桌面优先(`>=1280px`),不做移动端适配。
- 技术栈固定Tailwind CSS + HTMX + Alpine.js + Django Template。
### 1.2 本版关键改动(相对 v1.1
- 修正 Topbar 配色:`bg-white border-b``bg-primary-800`,与 UI_SYSTEM v1.2 对齐。
- 修正主内容区 padding`pt-14 py-4``pt-[72px] py-5`
- 修正 Section 锚点导航 sticky 位置:`top-20``top-16`
- 修正右侧面板 sticky`top-20``top-16`,并增加 `max-h-[calc(100vh-80px)] overflow-y-auto`
- 修正客源概览卡片顶部标识区:`bg-primary-600`(非 `bg-primary-800`)。
- 补充客源解读 Section 实际字段和数据展示规范。
- 补充 Alpine.js `clientDetailPage()` 状态机完整定义。
### 1.3 URL 与入口
- 详情页主路由:`/clients/<client_id>/`
- 入口:客源列表点击姓名或详情操作。
- 所有左侧 Section 默认随页面 SSR 输出,首屏即可看到首个 Section向下滚动查看其余 Section。
---
## 2. 设计基线(必须遵守)
### 2.1 视觉与 Token
- 主色:`primary-600`,禁用硬编码 Hex。
- Topbar 背景:`bg-primary-800`(深青绿,区别于内容区)。
- 页面底色:`bg-neutral-50`,内容卡片:`bg-white border border-neutral-200 rounded-lg`
- 正文字号:`text-sm`;辅助:`text-xs text-neutral-500`;关键数字加 `tabular-nums`
- 圆角:卡片 `rounded-lg`,输入/按钮 `rounded-md`,标签 `rounded``rounded-full`
- 焦点环统一:`focus-visible:ring-2 focus-visible:ring-primary-600/40`
### 2.2 组件与图标
- 图标库Heroicons v2。
- 默认Outline 24px`w-5 h-5`
- 高密Mini 16px`w-4 h-4`
- 禁止独立 CSS 文件;样式全部使用 Tailwind utility class。
### 2.3 页面骨架
- Topbar`fixed top-0 left-0 right-0 h-14 z-20 bg-primary-800 flex items-center justify-between`
- Sidebar`fixed left-0 top-14 h-[calc(100vh-56px)] w-60 z-20 border-r border-neutral-200 bg-white`
- 主内容区:`ml-60 pt-[72px] min-h-screen bg-neutral-50 px-6 py-5`
- 区块垂直节奏:`space-y-4`(外层)、`space-y-6`(左侧 Section 间距)
---
## 3. 页面信息架构
### 3.1 整体结构
```
Topbar (h-14, bg-primary-800)
-> Sidebar (w-60, fixed left)
-> Content Area (ml-60, pt-[72px], bg-neutral-50, px-6 py-5)
-> Breadcrumb + Page Title
-> Main Grid (12 cols, gap-6)
- Left (col-span-8)
- Sticky Section Anchor Nav (top-16, z-30)
- Section 1: 需求信息
- Section 2: 跟进记录
- Section 3: 带看记录
- Section 4: 客源解读
- Section 5: 二手配房
- Right Sidebar (col-span-4, sticky top-16)
- 客源信息概览(含快捷操作)
- 联系人
- 相关员工
```
### 3.2 主体布局骨架
```html
<main class="ml-60 pt-[72px] min-h-screen bg-neutral-50 px-6 py-5">
<div class="mx-auto max-w-[1600px] space-y-4">
<!-- 面包屑 + 页头 -->
<div class="flex items-start justify-between">
<div>
<nav class="flex items-center gap-1 text-xs text-neutral-500 mb-1" aria-label="面包屑">...</nav>
<h1 class="text-xl font-semibold text-neutral-800">客源详情</h1>
<p class="text-xs text-neutral-500 mt-0.5">按 Section 连续展示,点击导航锚点快速定位</p>
</div>
</div>
<div class="grid grid-cols-12 gap-6 items-start">
<section class="col-span-8 min-w-0 space-y-6">
<!-- Anchor Nav + All Sections -->
</section>
<aside class="col-span-4 min-w-0 space-y-3 sticky top-16 max-h-[calc(100vh-80px)] overflow-y-auto">
<!-- Right panels -->
</aside>
</div>
</div>
</main>
```
---
## 4. Topbar
结构同 UI_SYSTEM §5.2,客源详情页激活「客源」导航项。
```html
<header class="fixed top-0 left-0 right-0 h-14 z-20 bg-primary-800 flex items-center justify-between">
<!-- 左区Logo -->
<div class="flex items-center gap-2 px-4 w-60 shrink-0">
<div class="w-7 h-7 rounded-md bg-primary-500 flex items-center justify-center text-white text-sm font-semibold">F</div>
<span class="text-base font-semibold text-white">Fonrey</span>
</div>
<!-- 中区主导航nav flex-1无搜索框 -->
<nav class="flex items-center gap-1 flex-1 px-2" aria-label="主导航">
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">主页</a>
<a class="px-3 py-1.5 text-sm rounded-md bg-primary-600 text-white font-medium">客源</a><!-- 激活态 -->
<!-- …其余项 -->
</nav>
<!-- 右区 -->
<div class="flex items-center gap-1 px-4 shrink-0">
<button class="p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md" aria-label="消息"><!-- BellIcon --></button>
<div class="flex items-center gap-2 pl-3 ml-1 border-l border-primary-700">
<div class="w-8 h-8 rounded-full bg-primary-600 text-white flex items-center justify-center text-sm font-semibold"></div>
<span class="text-sm font-medium text-primary-100">魏深</span>
</div>
</div>
</header>
```
> 注:详情页 Topbar 右区只有消息通知 + 头像姓名,省略设置按钮(与 preview.html 保持一致)。
---
## 5. 左侧主内容区(全量 Section + 锚点导航)
### 5.1 Section 导航(替代 Tab
#### 5.1.1 交互定义
- 导航仅用于锚点跳转,不切换内容,不销毁 DOM。
- 点击导航项:`scrollToSection(id)` 平滑滚动到目标 Section。
- 当前高亮:`IntersectionObserver`rootMargin `-140px 0px -55% 0px`threshold `0.01`)驱动 `activeSection`
- 导航条 sticky`sticky top-16 z-30`,滚动时固定在左栏顶部。
#### 5.1.2 导航样式
- 容器:`bg-white border border-neutral-200 rounded-lg px-3 py-2 sticky top-16 z-30 shadow-xs`
- 项默认:`px-3 py-1.5 text-sm rounded-md text-neutral-600 hover:bg-neutral-100 hover:text-neutral-800`
- 项激活:`bg-primary-50 text-primary-700 font-medium`
- 焦点:`focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40`
- `aria-current="true"` 标注当前激活项
#### 5.1.3 Alpine.js 数据结构
```js
navItems: [
{ id: 'section-requirements', label: '需求信息' },
{ id: 'section-follow', label: '跟进记录' },
{ id: 'section-viewings', label: '带看记录' },
{ id: 'section-insights', label: '客源解读' },
{ id: 'section-matches', label: '二手配房' }
],
activeSection: 'section-requirements'
```
#### 5.1.4 代码骨架
```html
<div class="bg-white border border-neutral-200 rounded-lg px-3 py-2 sticky top-16 z-30 shadow-xs">
<nav class="flex items-center gap-1 overflow-x-auto whitespace-nowrap" aria-label="详情分区导航">
<template x-for="item in navItems" :key="item.id">
<a :href="'#' + item.id"
@click.prevent="scrollToSection(item.id)"
:aria-current="activeSection === item.id ? 'true' : 'false'"
:class="activeSection === item.id
? 'bg-primary-50 text-primary-700 font-medium'
: 'text-neutral-600 hover:bg-neutral-100 hover:text-neutral-800'"
class="px-3 py-1.5 text-sm rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40"
x-text="item.label"></a>
</template>
</nav>
</div>
```
---
### 5.2 Section 1需求信息P0
- ID`section-requirements`class 含 `section-anchor scroll-mt-24`
- 容器:`bg-white rounded-lg border border-neutral-200 p-4 space-y-4`
- Header标题 `text-base font-semibold text-neutral-800` + 右侧文字链「编辑」`text-sm text-primary-600 hover:text-primary-700 hover:underline underline-offset-2`
- 字段网格:`grid grid-cols-3 gap-x-6 gap-y-4`
- 字段项:`<dt class="text-xs text-neutral-500">` + `<dd class="text-sm text-neutral-900">`,数字型加 `tabular-nums`
- 备注字段:`col-span-3`(跨满三列)
- 空值:统一显示 `-`
字段列表总价、面积、居室、装修、朝向、楼层、楼龄、意向商圈、意向小区、交通情况、备注col-span-3
---
### 5.3 Section 2跟进记录P0
- ID`section-follow`class 含 `section-anchor scroll-mt-24`
- 容器:`bg-white rounded-lg border border-neutral-200 p-4 space-y-4`
- Header 右侧「写跟进」Primary 按钮,含铅笔图标 `w-4 h-4`,点击触发写跟进 Drawer`drawerOpen=true`
**类型筛选条**`flex items-center gap-2 flex-wrap`
- 激活项:`px-3 py-1 text-xs rounded-full bg-primary-600 text-white`
- 默认项:`px-3 py-1 text-xs rounded-full bg-neutral-100 text-neutral-600 hover:bg-neutral-200`
- 选项:全部 / 写入跟进 / 敏感信息跟进 / 修改跟进 / 其他跟进
**日期筛选栏**`border border-neutral-200 rounded-md p-3 bg-neutral-50 flex items-center gap-3 flex-wrap text-xs`,含开始/结束日期输入框、有录音/有图片 checkbox。
**时间线**`relative border-l-2 border-neutral-200 ml-3 space-y-4 pl-5`
- 普通记录:圆点 `bg-primary-600`,卡片 `rounded-md border border-neutral-200 p-3 bg-white space-y-1`
- 敏感记录:圆点 `bg-warning-600`,卡片 `rounded-md border border-warning-600/20 bg-warning-50 p-3 space-y-1`
- 类型标签:`inline-flex items-center px-2 py-0.5 rounded-full bg-info-50 text-info-600 font-medium`(电话),`bg-warning-50 text-warning-600`(敏感查看)
**底部**:「查看全部跟进」文字链,`text-sm text-primary-600 hover:underline underline-offset-2`
---
### 5.4 Section 3带看记录P0
- ID`section-viewings`class 含 `section-anchor scroll-mt-24`
- Header 右侧:两个 Secondary 按钮(`新增预约` / `新增带看`
- 筛选栏同跟进记录日期筛选样式含「归属人带看」「其他人带看」checkbox
- 时间线结构同跟进记录,房源名称用 `text-primary-600 hover:underline` 链接
- 带看次数标签:`inline-flex items-center px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 font-medium`(如「一看」)
---
### 5.5 Section 4客源解读P1
- ID`section-insights`class 含 `section-anchor scroll-mt-24`
- Header 右侧:更新时间文字 `text-xs text-neutral-500`
**行为指标行**`grid grid-cols-3 gap-3`
- 卡片:`border border-neutral-200 rounded-md p-3 bg-white`
- 标签 `text-xs text-neutral-500`,值 `text-xl font-semibold text-neutral-900 tabular-nums`
- 字段:活跃行为 / 工作日活跃 / 周末活跃
**偏好占比行**`grid grid-cols-3 gap-3`
- 卡片:`border border-neutral-200 rounded-md p-3 text-center`
- 标签 `text-xs text-neutral-500`,占比值 `text-2xl font-semibold text-primary-600 tabular-nums`
- 字段:价格偏好 / 户型偏好 / 面积偏好
---
### 5.6 Section 5二手配房P1
- ID`section-matches`class 含 `section-anchor scroll-mt-24`
- Header 右侧「批量分享」Secondary 按钮
**分组标题**`text-sm font-semibold text-neutral-700`(如「优质户型」)
**房源卡片**`border border-neutral-200 rounded-md p-3`
- 缩略图:`w-20 h-14 rounded bg-neutral-100`(占位)
- 房源名:`text-sm font-medium text-primary-600 hover:underline`
- 描述:`text-xs text-neutral-500`(户型·面积·区域)
- 标签行:`inline-flex items-center px-1.5 py-0.5 text-[11px] rounded`,朝向 `bg-warning-50 text-warning-600`,私盘 `bg-info-50 text-info-600`
- 价格:`text-sm font-medium text-neutral-900 tabular-nums`,降价说明 `text-xs font-normal text-neutral-500`
---
## 6. 右侧信息面板
右侧 `col-span-4``sticky top-16 max-h-[calc(100vh-80px)] overflow-y-auto space-y-3`
### 6.1 客源信息概览P0
容器:`bg-white rounded-lg border border-neutral-200 overflow-hidden`
**顶部标识区**`bg-primary-600 px-4 py-3`
- 求购 Badge`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-white/20 text-white`
- 客户姓名:`text-sm font-semibold text-white truncate`
- 带看进度副文字:`text-xs text-white/80 mt-0.5`
**标签行**`flex items-center gap-1.5 flex-wrap`
- 私客:`bg-neutral-100 text-neutral-600`
- 带看进度(如「一看」):`bg-primary-50 text-primary-700`
- 等级如「C(一般)」):`bg-warning-50 text-warning-600`
- 统一尺寸:`inline-flex items-center px-1.5 py-0.5 text-xs rounded`
**字段列表**`dl space-y-1.5`
- 每行:`grid grid-cols-[72px_1fr] gap-2`
- `dt``text-xs text-neutral-500``dd``text-xs text-right text-neutral-800`
- 数字/日期加 `tabular-nums`,编号加 `font-mono`
- 字段:最近跟进 / 客户编号 / 委托日期 / 需求类型 / 房源用途 / 客户来源
### 6.2 快捷操作区P0
**三主按钮**`grid grid-cols-3 gap-2`
- 样式:`flex flex-col items-center gap-1 py-2 text-xs font-medium bg-primary-600 text-white rounded-md hover:bg-primary-700`
- 按钮:打电话 / 写跟进(触发 `drawerOpen=true`/ 报备/常看
**操作网格**`grid grid-cols-2 gap-1`
- 默认:`px-2 py-2 text-xs text-left rounded-md text-neutral-600 hover:bg-neutral-100`
- 危险项(转无效):`text-danger-600 hover:bg-danger-50`
- 按钮:收藏 / 不置顶 / 改等级(`modal='grade'`/ 改状态(`modal='status'`/ 转公客 / 转成交(`modal='deal'`/ 转无效 / 编辑客源(`modal='edit'`
### 6.3 联系人面板P0
容器:`bg-white rounded-lg border border-neutral-200 p-3`
- Header 操作:`查看号码` / `新增联系人``text-xs text-primary-600 hover:underline space-x-2`
- 姓名:`text-sm font-medium text-neutral-900`
- 号码(脱敏):`text-xs text-neutral-600 tabular-nums`
- 拨打统计:`text-xs text-neutral-500`
### 6.4 相关员工面板P0
容器:`bg-white rounded-lg border border-neutral-200 p-3`
- Header 操作:`编辑``text-xs text-primary-600 hover:underline`
- 每个员工块(`space-y-3`
- 角色标签:`text-xs font-medium text-neutral-700`(如「【首录人】」)
- 姓名:`text-sm text-neutral-900`
- 参与时间:`text-xs text-neutral-500 tabular-nums`
---
## 7. 弹窗与抽屉
### 7.1 统一规范
- 遮罩:`fixed inset-0 z-50 bg-neutral-900/40`,点击关闭
- 弹窗层:`z-60 fixed inset-0 flex items-center justify-center p-4 pointer-events-none`
- 弹窗体:`pointer-events-auto bg-white rounded-xl shadow-lg border border-neutral-200 flex flex-col`
- Header`flex items-center justify-between px-5 py-4 border-b border-neutral-200`,关闭按钮 `p-1 text-neutral-500 hover:bg-neutral-100 rounded-md`
- Footer`flex items-center justify-end gap-2 px-5 py-3 border-t border-neutral-200 bg-neutral-50`,取消 Secondary + 确认 Primary
### 7.2 弹窗清单
| 弹窗 | 触发 | 宽度 | 内容要点 |
|---|---|---|---|
| 改等级 | `modal='grade'` | `max-w-sm` | 原等级展示 + 下拉选择新等级 |
| 改状态 | `modal='status'` | `max-w-md` | 原状态展示 + 下拉新状态 + 理由 textarea |
| 转成交 | `modal='deal'` | `max-w-lg` | 状态/房源类型下拉 + 选择成交房源按钮 |
| 编辑基础信息 | `modal='edit'` | `max-w-2xl max-h-[85vh] overflow-y-auto` | 需求类型/来源/用途/付款方式/证件号码 |
### 7.3 写跟进 Drawer
- 触发:`drawerOpen=true`
- 宽度:`w-[480px]`,从右侧滑入(`translate-x-full``translate-x-0`
- 字段跟进方式select/ 跟进内容textarea至少 6 字)/ 跟进时间input/ 附件file/ 是否开放给同事查看checkbox默认勾选
---
## 8. Alpine.js 状态机
```js
function clientDetailPage() {
return {
modal: null, // 'grade' | 'status' | 'deal' | 'edit' | null
drawerOpen: false,
navItems: [
{ id: 'section-requirements', label: '需求信息' },
{ id: 'section-follow', label: '跟进记录' },
{ id: 'section-viewings', label: '带看记录' },
{ id: 'section-insights', label: '客源解读' },
{ id: 'section-matches', label: '二手配房' }
],
activeSection: 'section-requirements',
observer: null,
scrollToSection(id) {
const el = document.getElementById(id)
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
},
init() {
const sections = Array.from(document.querySelectorAll('.section-anchor'))
this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) this.activeSection = entry.target.id
})
}, { root: null, rootMargin: '-140px 0px -55% 0px', threshold: 0.01 })
sections.forEach((s) => this.observer.observe(s))
}
}
}
```
---
## 9. HTMX 交互规范
### 9.1 原则
- 页面首次渲染直接 SSR 输出完整 5 个 Section。
- 每个 Section 内部筛选/分页独立请求,仅刷新本 Section 容器。
- 弹窗/Drawer 提交后定向刷新对应 Section 或右侧面板。
### 9.2 请求映射
| 操作 | URL | Target | Swap |
|---|---|---|---|
| 跟进类型筛选 | `/clients/{id}/follow-logs/partial` | `#follow-section-body` | `innerHTML` |
| 跟进加载更多 | `/clients/{id}/follow-logs/partial?page=N` | `#follow-timeline-list` | `beforeend` |
| 带看筛选 | `/clients/{id}/viewings/partial` | `#viewings-section-body` | `innerHTML` |
| 客源解读刷新 | `/clients/{id}/insights/partial` | `#insights-section-body` | `innerHTML` |
| 配房筛选/分页 | `/clients/{id}/matches/partial` | `#matches-section-body` | `innerHTML` |
| 查看号码 | `/clients/{id}/contacts/{cid}/reveal-phone/` | `#phone-{cid}` | `innerHTML` |
---
## 10. 状态与可用性规范
### 10.1 Loading
- 每个 Section 内独立 `htmx-indicator` 骨架。
- 按钮提交中显示 Spinner + 进行时文案(如 `保存中...`)。
### 10.2 Empty
- 跟进为空:`暂无跟进`
- 带看为空:`暂无带看记录` + `新增带看` 按钮
- 配房为空:`暂无匹配房源`
### 10.3 Error
- `htmx:responseError` 保留旧内容 + 右下角 Error Toast。
### 10.4 A11y
- 可点击项支持键盘 Tab 聚焦。
- 所有交互控件保留 `focus-visible` 样式。
- 锚点导航当前项 `aria-current="true"`
- Modal 打开时 `role="dialog"` + `aria-modal="true"`
---
## 11. 工程落地清单
1. `body` 挂载 `x-data="clientDetailPage()"`,包含完整状态机(见第 8 节)。
2. Topbar 使用 `bg-primary-800`,激活项 `bg-primary-600 text-white`
3. 主内容区:`ml-60 pt-[72px] min-h-screen bg-neutral-50 px-6 py-5`
4. Section 锚点导航 sticky `top-16 z-30`;右侧面板 sticky `top-16 max-h-[calc(100vh-80px)] overflow-y-auto`
5. 所有 Section 添加 class `section-anchor scroll-mt-24`,供 IntersectionObserver 监听。
6. 右侧客源概览顶部标识区用 `bg-primary-600`(非 `bg-primary-800`)。
7. 每个 Section 设置独立 HTMX target避免全页刷新。
8. Modal 遮罩 `z-50`,弹窗体 `z-60`Drawer 遮罩 `z-50`Drawer 面板 `z-60`
9. 全量检查 class 是否符合 token颜色、圆角、焦点环、表格密度
---
## 12. 验收标准
- Topbar 为深青绿色 `bg-primary-800`,与内容区有明显层次区分。
- 左侧主区无 Tab 切换行为,所有内容可连续滚动查看。
- 点击 Section 导航仅发生锚点滚动,不触发内容隐藏/显示。
- 锚点导航随滚动自动高亮当前 Section。
- 页面视觉与 `客源详情_静态原型.html` 一致:层级、卡片密度、按钮和输入风格一致。
- 颜色、字号、圆角、焦点环全部使用系统 token 与规范类名。
- 关键路径写跟进、改状态、查看号码可在单页完成并有明确反馈loading/toast/error