# 房源列表 UI 设计文档 > **版本**:v1.0 · **日期**:2026-04-26 > **依赖规范**:UI_SYSTEM.md v1.2 · 组件规范设计.md v1.0 > **PRD 来源**:`Project/fonrey/PRD/房源管理/房源管理模块PRD.md` §5.1 房源列表 > **优先级**:P0 功能在本文档中用 🔴 标注,P1 用 🟡,P2 用 ⚫ --- ## 目录 1. [模块概述](#1-模块概述) - 1.1 功能范围 - 1.2 页面清单 - 1.3 用户角色与权限差异 2. [页面设计规范](#2-页面设计规范) - 2.1 房源列表主页 3. [Data Table 规范](#3-data-table-规范) - 3.1 列定义 - 3.2 列状态变体 - 3.3 操作列 - 3.4 表格交互状态 4. [弹窗设计规范(列表页)](#4-弹窗设计规范列表页) - 4.1 自定义列弹窗 5. [交互状态规范](#5-交互状态规范) - 5.1 房源状态机 - 5.2 权限控制矩阵 - 5.3 HTMX 请求规范 6. [关键数据字段说明](#6-关键数据字段说明) 7. [竞品截图对应关系](#7-竞品截图对应关系) 8. [实现优先级与工期估算](#8-实现优先级与工期估算) 9. [开放问题(待决策)](#9-开放问题待决策) --- ## 1. 模块概述 ### 1.1 功能范围 **P0 功能(MVP 必须实现)🔴** - 房源列表展示(出售 / 出租 / 未挂牌 / 成交房源 / 全部房源 五个一级 Tab) - 关键词搜索(房源编号、小区/学校名称、地址、业主主姓名、电话、钥匙编号等) - 楼栋 / 单元 / 房号独立精确输入 - 多维度组合筛选(范围、区域、价格、面积、户型、楼层、标签、筛选、维护) - 列表数据展示(含交易类型标签、状态 Badge、价格趋势箭头) - 批量操作(批量收藏、取消收藏、设置保护房、修改相关方、删除) - 分页与每页条数控制(每页 20 条,可跳页) - 新增房源主 CTA 按钮 **P1 功能(第一迭代)🟡** - 已存搜索条件保存与快速调用 - 导出当前筛选结果(Excel) - 自定义列表显示字段 - 智能排序(系统推荐) - 海报视图切换 - 关注小区配置提示 - 重复房源检测 - 疑似问题号码房源查询 **P2 功能(路线图)⚫** - 地图找房视图 - 全部商铺列表 Tab - 全部写字楼列表 Tab ### 1.2 页面清单 | 页面名称 | URL 模式建议 | 优先级 | 对应 PRD 章节 | |---|---|---|---| | 房源列表(出售) | `/properties/?tab=for_sale` | P0 🔴 | §5.1, Story 2 | | 房源列表(出租) | `/properties/?tab=for_rent` | P0 🔴 | §5.1, Story 2 | | 房源列表(未挂牌) | `/properties/?tab=unlisted` | P0 🔴 | §5.1, Story 2 | | 房源列表(成交房源) | `/properties/?tab=sold` | P0 🔴 | §5.1, Story 2 | | 房源列表(全部房源) | `/properties/` | P0 🔴 | §5.1, Story 2 | ### 1.3 用户角色与权限差异 | 差异点 | 经纪人 | 店长 | 管理员 | |---|---|---|---| | 默认数据范围 | 仅自己名下(`seller_agent_id = me`) | 本门店全部(`org_unit_id IN my_stores`) | 全司所有 | | 「与我相关」快捷筛选 | 可用(默认可能已激活) | 可用 | 可用 | | 「我部门相关」快捷筛选 | 仅限自己所属部门 | 本门店 | 全司 | | 批量删除 | 仅限自己名下房源 | 本门店房源 | 所有房源 | | 修改相关方 | 不可操作他人房源 | 本门店范围 | 所有 | | 重复房源检测链接 | 可见 | 可见 | 可见 | | 「+ 新增房源」按钮 | 可见 | 可见 | 可见 | | 导出按钮 | 仅自己数据 | 本门店数据 | 全量 | | 业主电话 | 打码显示 | 打码显示(可申请查看) | 明文 | --- ## 2. 页面设计规范 ### 2.1 房源列表主页(P0 🔴) #### 2.1.1 页面概述 - **URL**:`/properties/`(query params:`tab`, `status`, `grade`, `page`, `q`, `sort`, `order` 等) - **访问入口**:顶部全局导航栏「房源」菜单 → 默认进入全部房源列表 - **页面职责**:展示经纪人名下(或门店/全司)的房源列表,支持多维度搜索筛选、批量操作、状态快览 - **竞品参考截图**: - `Project/fonrey/screenshots/房源/房源列表.png`(出售 Tab 视图,主参考) - `Project/fonrey/screenshots/房源/全部房源.png`(全部房源 Tab 视图) #### 2.1.2 布局结构 ``` ┌──────────────────────────────────────────────────────────────────────┐ │ 一级 Tab 导航(出售 / 出租 / 未挂牌 / 成交房源 / 全部房源) │ │ 右侧:更多 ▾ | 重复房源 | 疑似问题号码房源 | + 新增房源 按钮 │ ├──────────────────────────────────────────────────────────────────────┤ │ 搜索区域(关键词搜索 + 楼栋/单元/房号精确输入 + 地图找房入口) │ │ 关注小区配置提示条(可关闭) │ ├──────────────────────────────────────────────────────────────────────┤ │ 快捷筛选行(范围:最新挂牌 / 最新降价 / 与我相关 / 我部门相关 / 收藏房源 / 超时未跟进)│ │ 区域筛选行(区域按钮组 + 地铁选项) │ │ 价格筛选行(售价/单价 + 预设区间 + 自定义区间) │ │ 面积筛选行(预设区间 + 自定义区间) │ │ 房型筛选行(室数 + 卫生间数量) │ │ 楼层筛选行(低/中/高/顶/底 + 自定义区间) │ │ 标签筛选行(速销/独家/有钥匙/电梯等) │ │ 筛选行(相关方/维护人/房屋现状/状态属性/装修朝向等下拉) │ │ 维护行(发布/实勘/核验/跟进带看/钥匙委托/维护完成度) │ │ 底部操作行(查询 / 重置 / 已存搜索条件 ▾ / 收起更多 ∧) │ ├──────────────────────────────────────────────────────────────────────┤ │ 工具栏(房源海报 | 批量操作按钮 | 更多 ▾ | 共N条 | 导出 | 自定义列表 | 智能排序)│ ├──────────────────────────────────────────────────────────────────────┤ │ 数据表格主体 │ ├──────────────────────────────────────────────────────────────────────┤ │ 分页栏(共N条 | 上一页 / 页码 / 下一页 / 每页20条 / 跳页) │ └──────────────────────────────────────────────────────────────────────┘ ``` 整体页面背景:`bg-neutral-50` 主内容区外层容器:`max-w-[1600px] mx-auto px-6 py-4` 各区块背景:`bg-white rounded-lg border border-neutral-200` #### 2.1.3 区域详细规范 --- **[一级 Tab 导航区]** | 属性 | 说明 | |---|---| | 组件 | Tab Navigation(§10 Tab Navigation),underline 变体 | | 位置 | 页面最顶部,紧贴全局顶导下方,无额外卡片容器 | | Tab 项 | 出售(含数量 Badge)/ 出租(含数量 Badge)/ 未挂牌 / 成交房源 / 全部房源 | | 激活样式 | `border-b-2 border-primary-600 text-primary-600 font-medium` | | 非激活样式 | `text-neutral-500 hover:text-neutral-700` | | 右侧内容 | 「更多 ▾」下拉菜单 + 「重复房源」链接 + 「疑似问题号码房源」链接 + 「+ 新增房源」主按钮 | **右侧操作区(Tab 栏右侧,绝对定位)**: ```html
重复房源 疑似问题号码房源 新增房源
``` > **截图差异说明**:竞品截图中「+ 新增房源」为橙色按钮,Fonrey 使用主色 Teal(`bg-primary-600`),品牌差异,无需对齐竞品色。 --- **[搜索区域]** | 属性 | 说明 | |---|---| | 组件 | 搜索输入框组合 | | 容器 | `bg-white rounded-lg border border-neutral-200 px-4 py-3 mt-3` | | 关键词搜索框 | 宽约 `flex-1 max-w-lg`,占位符「房源编号/小区/学校名称/地址/业主姓名/电话/钥匙编号等」 | | 楼栋输入框 | 标签「楼栋」+ 独立 Input,`w-24`,精确匹配 `block_no` | | 单元输入框 | 标签「单元」+ 独立 Input,`w-24`,精确匹配 `unit_no` | | 房号输入框 | 标签「房号」+ 独立 Input + Heroicon `information-circle` 提示,`w-24`,精确匹配 `room_no` | | 搜索按钮 | `bg-primary-600 text-white` Heroicon `magnifying-glass`,`w-9 h-9 rounded-lg` | | 地图找房(P2 ⚫)| 右侧浅色按钮「🗺 地图找房 →」,P2 阶段置灰不可点击 | ```html
楼栋
单元
房号

搜索历史在后台记录,请勿违规!

配置关注小区 (关注小区后,当该小区产生对应交易类型下的新上房源、降价房源时,系统将第一时间通知您,提升您的作业效率哦!)
``` --- **[筛选区 - 详细规范]** 筛选区按行组织,每行格式:`[标签(w-8)] [选项组]`,标签使用 `text-xs text-neutral-400 whitespace-nowrap w-8`。 **范围筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 最新挂牌 | Checkbox | 筛选最近挂牌房源(`listed_at` 降序默认) | | 最新降价 | Checkbox | 最近有调价记录且降价 | | 与我相关 | Checkbox + ⓘ | `seller_agent_id = me OR first_recorder_id = me` | | 我部门相关 | Checkbox + ⓘ | 本门店员工相关房源 | | 收藏房源 | 下拉 ▾ | 展开选择收藏集合(来自 `property_favorites`) | | 超时未跟进房源 | Checkbox | `last_followed_at < NOW() - 7d`(具体阈值待产品确认) | ```html
范围
``` **区域筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 地区(行政区) | Tag 按钮多选 | 宝山/嘉定/静安/闵行/普陀/松江/长宁等,多选,来自 `complexes.district` | | 地铁 | 下拉 ▾ | 选择地铁线 + 站点(二级联动),来自楼盘数据 | ```html
区域
{% for district in districts %} {% endfor %}
``` **价格筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 售价/单价 切换 | 单选 Tab | 出售 Tab 默认显示;出租 Tab 替换为「租价」 | | 价格预设区间 | Tag 单选 | 200万以下 / 250-300万 / 300-400万 / 400-500万 / 500-700万 / 700-1000万 / 1000-1500万 / 1500-2000万 / 2000万以上 | | 自定义区间 | 数字输入框 × 2 + 单位 | `最小值 ~ 最大值 万元` | | 收起/展开 | 文字链接 | Alpine.js 控制显示部分预设区间 | ```html
价格
|
~ 万元
``` **面积筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 面积预设区间 | Tag 单选 | 50m²以下 / 50-70m² / 70-90m² / 90-110m² / 110-130m² / 130-150m² / 150m²以上 | | 自定义区间 | 数字输入框 × 2 + 单位「m²」 | 精确区间输入 | **房型筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 室数 | Tag 多选 | 1室 / 2室 / 3室 / 4室 / 5室及以上,对应 `bedroom_count` | | 卫生间 | Tag 多选 | 1卫 / 2卫 / 3卫 / 4卫,对应 `bathroom_count` | | 收起/展开 | 文字链接 | 控制卫生间行显示 | **楼层筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 楼层段 | Tag 多选 | 低层(1-3层) / 中层(4-9层) / 高层(10层+) / 顶楼 / 底楼 | | 自定义区间 | 数字输入框 × 2 + 单位「层」 | 精确楼层范围 | **标签筛选行**: | 筛选项 | 类型 | 说明 | |---|---|---| | 标签 | Checkbox 多选 | 速销、独家、有钥匙、电梯、唯一、有照片、贷款、视频、AI视频、有VR、3D | | 一键装换 | Checkbox | 快捷筛选 | | 一般委托 | Checkbox | 快捷筛选 | **筛选行(展开更多)**: 下拉筛选项,使用 ``,Alpine.js `selected[]` 数组 | | 2 | 房源名称 | `complex_name` + `block_no` + `unit_no` + `room_no` + 交易类型标签 + 速卖/电梯等标签 | `min-w-[200px] max-w-[280px]` | 左对齐 | 否 | 蓝色链接跳详情;名称行下方渲染标签组(见 §3.2);「切换展示」链接(P1 🟡) | | 3 | 楼栋 | `block_no` | `w-16`(64px) | 左对齐 | 否 | 纯文字;无数据显示 `-` | | 4 | 单元 | `unit_no` | `w-16`(64px) | 左对齐 | 否 | 纯文字;无数据显示 `-` | | 5 | 房号 | `room_no` | `w-16`(64px) | 左对齐 | 否 | 纯文字;无数据显示 `-` | | 6 | 区域板块 | `district` + `business_area` | `min-w-[100px]` | 左对齐 | 否 | 格式:`嘉定 丰庄`,两行或空格分隔 | | 7 | 状态 | `status` | `w-16`(64px) | 左对齐 | 否 | Status Badge(见 §3.2) | | 8 | 售价(万) | `sale_price` | `w-24`(96px) | 右对齐 | **是** | 价格数字 + 价格变动趋势箭头(见 §3.2);出租 Tab 此列隐藏 | | 9 | 单价(元/m²) | `sale_unit_price` | `w-28`(112px) | 右对齐 | **是** | 数字,格式化千分位;出租 Tab 此列隐藏 | | 10 | 租价(元/月) | `rent_price` | `w-28`(112px) | 右对齐 | **是** | 数字,格式化千分位;出售 Tab 此列隐藏 | | 11 | 面积(m²) | `area` | `w-20`(80px) | 右对齐 | **是** | 保留1位小数,如 `81.3` | | 12 | 户型 | `bedroom_count` + `living_room_count` | `w-16`(64px) | 左对齐 | 否 | 格式:`3/1/1`(室/厅/卫);简显为 `X室X厅` | | 13 | 楼层 | `floor` + `total_floors` | `w-16`(64px) | 左对齐 | 否 | 格式:`4/6`;无数据显示 `-` | | 14 | 朝向 | `orientation` | `w-12`(48px) | 左对齐 | 否 | 中文显示:南北/东南等;无数据显示 `-` | | 15 | 挂牌日期 | `listed_at` | `w-28`(112px) | 左对齐 | **是** | 格式:`YYYY-MM-DD` | | 16 | 房源最后跟进日 | `last_followed_at` | `w-28`(112px) | 左对齐 | **是**(默认降序) | 格式:`YYYY-MM-DD`;超过 30 天字色 `text-danger-600` | > **全部房源 Tab** 与**出售/出租 Tab** 的差异:全部房源 Tab 同时显示「售价」和「租价」列,部分行会有一列为 `-`,属正常显示。 > **`whitespace-nowrap` 要求**:以下列的数据单元格(``)必须加 `whitespace-nowrap`,防止内容折行:状态、售价、单价、租价、户型、楼层、朝向、挂牌日期、最后跟进日期。 > **自定义列(P1 🟡)**:用户通过「自定义列表」弹窗(§4.1)选择显示字段,可选字段包括:单价、等级、属性、装修、建成年代、来源、相关方、维护完成度等。 **表格 HTML 结构(关键片段)**: ```html
{% for prop in properties %} {% endfor %}
房源名称 楼栋 单元 房号 区域板块 状态 售价(万) 房源最后跟进日
{{ prop.transaction_type_display }} {{ prop.complex_name }}{{ prop.room_display }}
{% for tag in prop.display_tags %} {{ tag.name }} {% endfor %} {% if prop.ownership_label %} {{ prop.ownership_label }} {% endif %}
{{ prop.block_no|default:"-" }} {{ prop.unit_no|default:"-" }} {{ prop.room_no|default:"-" }}
{{ prop.district }} {{ prop.business_area }}
{{ prop.status_display }} {% if prop.sale_price %}
{{ prop.sale_price }} {% if prop.price_change_direction == 'down' %} ↓ {{ prop.price_change_display }} {% elif prop.price_change_direction == 'up' %} ↑ {{ prop.price_change_display }} {% endif %}
{% else %}-{% endif %}
{{ prop.sale_unit_price|default:"-" }} {% if prop.rent_price %}{{ prop.rent_price }}{% else %}-{% endif %} {{ prop.area }} {{ prop.layout_display }} {% if prop.floor %}{{ prop.floor }}/{{ prop.total_floors }}{% else %}-{% endif %} {{ prop.orientation_display|default:"-" }} {{ prop.listed_at|date:"Y-m-d"|default:"-" }} {{ prop.last_followed_at|date:"Y-m-d"|default:"-" }}
``` ### 3.2 列状态变体 **交易类型标签(房源名称列内,蓝/橙色 Badge)**: | `property_type` + `status` 场景 | 显示文字 | 样式 | |---|---|---| | 出售中(`for_sale`) | 买卖 | `bg-danger-600 text-white text-[10px] px-1.5 py-0.5 rounded` | | 出租中(`for_rent`) | 租赁 | `bg-info-600 text-white text-[10px] px-1.5 py-0.5 rounded` | | 出售+出租(`for_sale_rent`) | 租售 | 同时展示「买卖」和「租赁」两个 Badge | **房源附加标签(名称行下方标签组)**: | 标签类型 | 来源 | 样式 | |---|---|---| | 满五 | `ownership_years` 含「满五」 | `bg-success-50 text-success-600 text-[10px] px-1.5 py-0.5 rounded-sm font-medium` | | 电梯 | `has_elevator = true` 且有电梯 | `bg-neutral-100 text-neutral-600 text-[10px] px-1.5 py-0.5 rounded-sm` | | 视频 | `property_tags` 含视频标签 | `bg-neutral-100 text-neutral-600 text-[10px] px-1.5 py-0.5 rounded-sm` | | 私(私盘) | `attribute = 'private'` | `bg-warning-50 text-warning-700 text-[10px] px-1.5 py-0.5 rounded-sm font-medium` | | 速销 | `property_tags` 含速销标签 | `bg-danger-50 text-danger-600 text-[10px] px-1.5 py-0.5 rounded-sm` | | 独家 | `property_tags` 含独家标签 | `bg-primary-50 text-primary-600 text-[10px] px-1.5 py-0.5 rounded-sm` | **状态 Badge(状态列)**: | `status` 值 | 显示文字 | 样式 | |---|---|---| | `for_sale` | 出售 | `bg-success-50 text-success-700 text-xs px-2 py-0.5 rounded-full` | | `for_rent` | 出租 | `bg-info-50 text-info-600 text-xs px-2 py-0.5 rounded-full` | | `for_sale_rent` | 租售 | `bg-primary-50 text-primary-700 text-xs px-2 py-0.5 rounded-full` | | `suspended` | 暂缓 | `bg-neutral-100 text-neutral-500 text-xs px-2 py-0.5 rounded-full` | | `sold_elsewhere` | 他售 | `bg-warning-50 text-warning-600 text-xs px-2 py-0.5 rounded-full` | | `rented_elsewhere` | 他租 | `bg-warning-50 text-warning-600 text-xs px-2 py-0.5 rounded-full` | | `sold` | 成交 | `bg-neutral-200 text-neutral-600 text-xs px-2 py-0.5 rounded-full` | | `unlisted` | 未挂牌 | `bg-neutral-100 text-neutral-400 text-xs px-2 py-0.5 rounded-full` | **价格趋势箭头(售价列)**: ```html
275
650
``` > **PRD 补充说明**:竞品截图中「全部房源」Tab 显示的价格趋势为小型下箭头(`↓`)贴在价格数字下方。设计为:降价→ `text-danger-600` 下箭头;涨价 → `text-success-600` 上箭头;未变化 → 不显示箭头。 **排序列头(售价/单价/面积/挂牌日期/最后跟进日)**: ```html 售价(万) ``` **行选中态**: ```html ``` ### 3.3 操作列 MVP 阶段房源列表无独立操作列(行点击跳转到详情页即可);点击「房源名称」链接跳转。 > **注**:竞品截图中房源列表最右侧无固定操作列按钮,操作通过 checkbox 批量选中后在工具栏操作,或直接点击行跳详情。本设计遵循此交互模式,**不添加操作列**,降低信息密度。 ### 3.4 表格交互状态 | 状态 | 触发场景 | 视觉表现 | |---|---|---| | 默认(无选中) | 页面加载完毕 | 所有行 `bg-white`,hover 时 `bg-neutral-50` | | 行选中 | 勾选复选框 | `bg-primary-50 hover:bg-primary-100`;工具栏批量操作按钮激活 | | 全选 | 点击表头复选框 | 当前页所有行选中;表头 checkbox `indeterminate` 或 `checked` | | Loading(HTMX 请求中) | 筛选/分页/排序触发 | 骨架屏覆盖 `#property-list-container`(见 §2.1.6) | | 空状态(无数据) | 筛选无结果 / 首次进入 | 见 §2.1.5 空状态设计 | --- ## 4. 弹窗设计规范(列表页) > **范围说明**:本章仅包含从**房源列表页直接触发**的弹窗。调价、改状态、改等级等操作弹窗从**房源详情页**触发,记录于详情页 UI 设计文档。 ### 4.1 自定义列弹窗(P1 🟡) #### 4.1.1 触发方式 - **触发位置**:工具栏右侧「自定义列表」按钮(Heroicon `adjustments-horizontal` + 文字) - **组件类型**:Modal Dialog(`组件规范设计.md` §7) - **尺寸**:`max-w-2xl`(640px) #### 4.1.2 弹窗布局 ``` ┌─────────────────────────────────────────────────────┐ │ 标题:自定义列表信息 [×] │ ├────────────────────────┬────────────────────────────┤ │ 未选信息 │ 已选信息 │ │ (可勾选字段列表) │ (已选字段,拖拽排序) │ │ │ ┌─────────────────────┐ │ │ □ 等级 │ │ ⋮⋮ 房源名称 [🔒] │ │ │ □ 属性 │ │ ⋮⋮ 楼栋 [删] │ │ │ □ 装修 │ │ ⋮⋮ 单元 [删] │ │ │ □ 建成年代 │ │ ⋮⋮ 房号 [删] │ │ │ □ 来源 │ │ ⋮⋮ 区域板块 [删] │ │ │ □ 完成度 │ │ ... │ │ │ □ 相关方 │ └─────────────────────┘ │ ├────────────────────────┴────────────────────────────┤ │ [恢复默认] [取消] [确定] │ └─────────────────────────────────────────────────────┘ ``` #### 4.1.3 字段说明 **固定不可隐藏列**:房源名称(锁定,无删除按钮) **可选字段(完整清单)**:楼栋、单元、房号、区域板块、状态、售价(万)、单价(元/m²)、租价(元/月)、面积(m²)、户型、楼层、朝向、挂牌日期、最后跟进日、等级、属性、装修、建成年代、来源、维护完成度、出售方、首录方 #### 4.1.4 提交行为 - **提交方式**:`hx-post="/properties/column-preferences/"` - **成功响应**:关闭弹窗 + HTMX 刷新 `#property-list-container` 以重新渲染列 - **HTMX 属性**: ```html
``` #### 4.1.5 使用的特殊组件 | 组件名 | 来源 | 用途 | |---|---|---| | Modal Dialog | §7 Modal Dialog | 弹窗容器,`max-w-2xl` | Alpine.js 管理:`selectedColumns`(有序数组)、`availableColumns`(剩余可选)、`dragging` 状态;拖拽通过 Alpine.js + SortableJS 实现。 --- ## 5. 交互状态规范 ### 5.1 全局状态机 **房源状态流转**: ``` for_sale(出售) ──→ suspended(暂缓) ──→ for_sale(重新挂牌) ──→ sold_elsewhere(他售) ──→ sold(成交) for_rent(出租) ──→ suspended(暂缓) ──→ for_rent(重新挂牌) ──→ rented_elsewhere(他租) ──→ sold(成交) unlisted(未挂牌)──→ for_sale / for_rent(挂牌) suspended(暂缓) ──→ for_sale / for_rent(恢复挂牌) sold_elsewhere / rented_elsewhere ──→ for_sale / for_rent(重新挂牌) ``` **状态在列表页的视觉标记**: - `for_sale` / `for_rent`:正常行背景 - `suspended` / `sold_elsewhere` / `rented_elsewhere`:正常行背景,状态 Badge 有区分 - `sold`:行文字颜色轻微降低(`text-neutral-500`),提示该房源已成交 ### 5.2 权限控制矩阵 | 操作 | 经纪人 | 店长 | 管理员 | |---|---|---|---| | 查看房源列表 | ✅(自己名下 + 公盘) | ✅(本门店 + 公盘) | ✅(全部) | | 新增房源 | ✅ | ✅ | ✅ | | 批量收藏/取消收藏 | ✅ | ✅ | ✅ | | 设置保护房 | ❌(需权限) | ✅ | ✅ | | 批量修改相关方 | ❌(需权限) | ✅(本门店) | ✅ | | 批量删除 | ✅(自己名下) | ✅(本门店) | ✅ | | 导出 | ✅(自己数据) | ✅(本门店) | ✅(全量) | | 查看业主电话 | ❌(打码,需点击查看) | 同经纪人 | ✅ | | 重复房源检测 | ✅ | ✅ | ✅ | ### 5.3 HTMX 请求规范 | 操作 | hx-trigger | hx-get/post | hx-target | hx-swap | Loading | |---|---|---|---|---|---| | Tab 切换(出售/出租等) | `click` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 关键词搜索输入 | `keyup changed delay:300ms, search` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 筛选 Checkbox 变更 | `change` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 筛选 Tag 按钮点击 | `click`(Alpine.js 更新 hidden input)→ `change` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 查询按钮点击 | `click` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 重置按钮 | `click` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 排序列头点击 | `click` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 分页页码点击 | `click` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 每页条数变更 | `change` | `hx-get="/properties/"` | `#property-list-container` | `innerHTML` | 骨架屏 | | 导出按钮 | `click` | `hx-post="/properties/export/"` | `#export-status-area` | `innerHTML` | Toast 提示「导出任务已提交」 | | 自定义列保存 | `submit` | `hx-post="/properties/column-preferences/"` | `#property-list-container` | `innerHTML` | Modal 内 Loading Spinner | **Alpine.js 状态管理分工**: | 状态/行为 | 管理方式 | 说明 | |---|---|---| | 一级 Tab 激活状态(`activeTab`) | Alpine.js | 用于控制售价/租价列的显示/隐藏 | | 筛选区展开/收起(`showFilters`) | Alpine.js | 控制筛选区域整体折叠 | | 展开更多筛选(`showMoreFilters`) | Alpine.js | 控制下方展开筛选行的显示 | | 价格模式切换(`priceMode`)| Alpine.js | 售价/单价 Tab 切换 | | 区域多选(`selectedDistricts[]`) | Alpine.js | 维护已选行政区数组 | | 表格行选中(`selected[]`) | Alpine.js | 维护已勾选行的 ID 数组,驱动批量操作按钮激活 | | 已选条数(`selectedCount`) | Alpine.js 计算属性 | `selected.length` | | 自定义列弹窗开关(`columnSettingsOpen`) | Alpine.js | 弹窗的 open/close 状态 | | 数据加载/筛选/分页/排序 | HTMX | 所有后端数据请求 | | 表单提交(批量操作/导出/保存列设置) | HTMX | 后端交互 | --- ## 6. 关键数据字段说明 | 字段名(英文) | 显示名 | 数据类型 | 说明 | |---|---|---|---| | `id` | 房源 ID | UUID | 主键,用于行跳转链接 | | `property_type` | 房源类型 | VARCHAR(20) | residential / villa / shop 等 | | `status` | 状态 | VARCHAR(20) | for_sale / for_rent / suspended 等 | | `attribute` | 属性 | VARCHAR(20) | public / private / special / sealed | | `complex_name` | 小区名称 | 关联查询 | 来自 `complexes.name` | | `block_no` | 楼栋号 | VARCHAR(30) | 栋/幢/弄号 | | `unit_no` | 单元号 | VARCHAR(30) | — | | `room_no` | 房号 | VARCHAR(30) | 门牌号 | | `floor` | 所在楼层 | SMALLINT | — | | `total_floors` | 总楼层 | SMALLINT | — | | `bedroom_count` | 室数 | SMALLINT | 户型组合 | | `living_room_count` | 厅数 | SMALLINT | 户型组合 | | `bathroom_count` | 卫数 | SMALLINT | 户型组合 | | `area` | 建筑面积 | NUMERIC(8,2) | m²,保留1位小数显示 | | `sale_price` | 挂牌售价 | NUMERIC(12,2) | 万元 | | `sale_unit_price` | 单价 | 计算字段 | `sale_price * 10000 / area`,元/m² | | `rent_price` | 挂牌租价 | NUMERIC(10,2) | 元/月 | | `orientation` | 朝向 | VARCHAR(10) | east / south 等,需翻译 | | `decoration` | 装修 | VARCHAR(10) | rough / fine 等,需翻译 | | `has_elevator` | 是否有电梯 | BOOLEAN | 用于标签显示 | | `ownership_years` | 房本年限 | VARCHAR(30) | 满2年/满5年等,用于「满五」标签 | | `grade` | 等级 | VARCHAR(10) | A_urgent / A / B / C / D | | `listed_at` | 最近挂牌时间 | TIMESTAMPTZ | 显示为 `YYYY-MM-DD` | | `last_followed_at` | 最后跟进时间 | TIMESTAMPTZ | 超30天标红,显示为 `YYYY-MM-DD` | | `completeness_score` | 维护完成度 | SMALLINT | 0-100,Celery 异步计算 | | `seller_agent_name` | 出售方经纪人 | 关联查询 | 来自 `staff.name` | | `first_recorder_name` | 首录方 | 关联查询 | 来自 `staff.name` | | `district` | 行政区 | 关联查询 | 来自 `complexes → districts` | | `business_area` | 商圈 | 关联查询 | 来自 `complexes → business_areas` | | `tags` | 标签列表 | 关联查询 | 来自 `property_tag_relations → property_tags` | | `is_favorited` | 是否已收藏 | BOOLEAN | 当前用户收藏状态,来自 `property_favorites` | | `latest_price_direction` | 最近调价方向 | 计算字段 | `up` / `down` / `null`,来自最近一条 `price_changes` 记录 | --- ## 7. 竞品截图对应关系 | 截图路径 | 对应功能 | 对应文档章节 | 采纳的设计要点 | |---|---|---|---| | `Project/fonrey/screenshots/房源/房源列表.png` | 出售 Tab 列表视图(主参考) | §2.1.2 布局、§3.1 列定义、§3.2 状态变体 | 列布局(楼栋/单元/房号/区域板块/状态/售价/单价/面积/户型/楼层/朝向/挂牌/跟进日);交易类型 Badge(橙色买卖/蓝色租赁);多维度筛选区展开方式;工具栏布局(批量操作+导出+自定义);分页样式(每页20条+跳页) | | `Project/fonrey/screenshots/房源/全部房源.png` | 全部房源 Tab 视图 | §2.1.3 区域详细规范、§3.1 列定义 | 全部房源同时含售价/租价/面积列;出售+出租混合显示方式;房源名称行显示多个附加标签(满五/电梯/视频/私盘等);价格列下方显示趋势小箭头 | **与竞品截图差异说明(PRD 优先)**: 1. **主色差异**:竞品使用橙色(`#F97316`)作为主按钮色和 Tab 激活色;Fonrey 使用 Teal(`#0F766E` = `primary-600`)。所有激活状态、主按钮均使用 Teal。 2. **按钮颜色**:竞品「新增房源」为橙色大按钮;Fonrey 改为 `bg-primary-600` Teal 色。 3. **区域筛选位置**:竞品截图中区域筛选(宝山/嘉定等)放在「区域」行独立显示,与 PRD 一致,本文档采纳。 4. **右侧悬浮栏**:竞品截图中页面右侧有悬浮操作栏(增客/增房/发审批等);该功能属于全局导航组件,不在本列表页 UI 文档范围内,由全局布局文档单独定义。 --- ## 8. 实现优先级与工期估算 | 页面/功能 | 优先级 | 特殊组件复杂度 | 工期估算(前端) | |---|---|---|---| | 一级 Tab 导航 + 页面框架 | P0 🔴 | 低 | 0.5 天 | | 搜索区域(关键词 + 楼栋/单元/房号) | P0 🔴 | 低 | 0.5 天 | | 范围/区域/价格/面积/房型筛选行 | P0 🔴 | 中(多 Tag 按钮联动) | 1.5 天 | | 楼层/标签/筛选/维护筛选行 | P0 🔴 | 中(多下拉联动) | 1 天 | | 工具栏(批量操作 + 总条数) | P0 🔴 | 低 | 0.5 天 | | 数据表格(核心列定义 + 状态 Badge + 价格箭头) | P0 🔴 | 中(多列状态变体) | 1.5 天 | | 分页栏 | P0 🔴 | 低 | 0.25 天 | | 空状态 + Loading 骨架屏 | P0 🔴 | 低 | 0.25 天 | | HTMX 请求整合(所有筛选联动) | P0 🔴 | 中 | 1 天 | | Alpine.js 状态管理整合 | P0 🔴 | 中 | 0.5 天 | | 重复房源检测 + 疑似号码入口 | P1 🟡 | 低 | 0.25 天 | | 已存搜索条件保存/调用 | P1 🟡 | 中 | 1 天 | | 导出功能(Celery 异步) | P1 🟡 | 低(复用客源导出) | 0.5 天 | | 自定义列表弹窗(含拖拽排序) | P1 🟡 | 高(SortableJS 集成) | 1.5 天 | | 智能排序(P1) | P1 🟡 | 低 | 0.25 天 | | 海报视图切换(P1) | P1 🟡 | 高(卡片布局全新设计) | 2 天 | | 地图找房(P2) | P2 ⚫ | 高(地图组件集成) | — | **P0 总估算**:约 7.5 天前端工时 **P1 总估算**:约 5.5 天前端工时 --- ## 9. 开放问题(待决策) | # | 问题 | 影响范围 | 待确认方 | |---|---|---|---| | 1 | 「超时未跟进房源」的超时阈值是多少天?PRD 未明确指定具体天数 | 范围筛选行、超时标红逻辑 | 产品经理 | | 2 | 「与我相关」的精确范围定义:是仅 `seller_agent_id = me`,还是包含 `first_recorder_id / number_holder_id`? | 筛选逻辑、后端查询 | 产品经理 + 后端 | | 3 | 价格趋势箭头:是显示具体降幅数字(如「↓5万」),还是仅显示方向箭头?竞品截图中仅有箭头图标 | §3.2 价格趋势箭头渲染 | 产品经理 | | 4 | 房源名称列下方标签组的完整枚举:「满五/电梯/视频/私盘/速销/独家」之外还有哪些?是否全部来自 `property_tags` 还是有些来自字段推导? | §3.2 房源附加标签 | 产品经理 + 后端 | | 5 | 「海报视图」的具体布局设计尚未定义,仅知道是列表/海报切换;海报单卡片展示哪些字段? | 工具栏切换按钮、海报卡片组件 | 产品经理 | | 6 | 地铁筛选是否需要在 MVP(P0)阶段实现?PRD 中列出但 MVP 优先级矩阵中未单独标注 | 区域筛选行地铁入口 | 产品经理 | | 7 | 「关注小区配置提示条」是否在列表页默认显示?关闭后是否持久化(即刷新后不再显示)? | 搜索区关注小区提示条 | 产品经理 | | 8 | 全部房源 Tab 下,出售价和租价同时展示时,对于只有租价的房源,售价列显示 `-` 还是直接隐藏该列? | §3.1 全部房源 Tab 列定义 | 产品经理 | | 9 | 「一键装换」和「一般委托」标签筛选的具体业务含义?在数据模型中对应哪个字段或条件? | 标签筛选行 | 产品经理 + 后端 | | 10 | 列表默认展示的 Tab 是「出售」还是「全部房源」?用户未操作时哪个 Tab 默认激活? | 一级 Tab 初始状态 | 产品经理 |