- 新增 PRD/系统配置/系统配置模块PRD.md(v0.1 Draft) - MVP 范围:US-SETTING-001-A(Lookup Items)、B(房源字段必填规则)、C(客源录入规则) - 新增 PRD/系统配置/系统配置数据模型设计说明_for_Atlas.md - 新增 PRD/系统配置/系统配置参数数据.md(竞品参数数据) - 删除旧版 PRD/系统配置/系统配置.md(已被新PRD替代) - 新增 DATA_MODEL/DATA_MODEL_SETTING.md(系统配置数据模型) - 新增 DATA_MODEL/ENUMS.md(枚举定义与约定) - 新增 AGENTS.md(AI Agent 开发规范) - 更新 PRD/TASK.md:US-SETTING-001 拆分为 A/B/C 三个子任务,修正参考文档路径与验收标准 - 新增 VIBE_CODING_开工前缺失清单.md - 新增 TECH_STACK/房源管理技术方案.md - 更新 DATA_MODEL/DATA_MODEL.md、DATA_MODEL_CLIENT.md、DATA_MODEL_LOGIN.md - 更新 PRD/PRD_MVP.md、PRD/权限管理/权限管理模块PRD.md - 更新 TECH_STACK/TECH_STACK.md、权限管理系统技术方案.md - 更新 UI_DESIGN/preview.html、UI_SYSTEM/UI_SYSTEM.md - 新增 prompt/PRD - 为系统设置生成PRD设计文档.md、更新 prompt 模板
1743 lines
78 KiB
Markdown
1743 lines
78 KiB
Markdown
# Fonrey UI System 设计规范
|
||
|
||
**版本**:v1.2
|
||
**最后更新**:2026-04-25
|
||
**维护者**:UI/UX 架构组
|
||
**适用技术栈**:Tailwind CSS + HTMX + Alpine.js + Django 模板
|
||
**目标分辨率**:桌面 Web,≥ 1280px(v1 不做移动端适配)
|
||
|
||
> **For developers**: 本文是开发还原 UI 的唯一权威。所有组件以 HTML + Tailwind class 描述,不输出 JSX。新增页面须先到此处查找可复用组件与模板;如需新模式,先补本文再落地。
|
||
> **关联文档**:
|
||
> - `Project/fonrey/TECH_STACK/TECH_STACK.md`(技术总纲)
|
||
> - `Project/fonrey/UI_SYSTEM/组件清单.md`(组件可行性分析)
|
||
> - `Project/fonrey/PRD/*`(业务需求)
|
||
|
||
---
|
||
|
||
## 0. 设计语言定调(Design Language Overview)
|
||
|
||
参考 Linear 的克制、Notion 的信息密度、Salesforce Lightning 的企业严谨,结合竞品(链家/贝壳式房产工具)的操作效率要求,Fonrey 的设计语言定位为:
|
||
|
||
- **专业、克制、高密度**:表格为王,单屏尽可能多展示数据;色彩只为信息服务,不用于装饰。
|
||
- **主色靛青(Teal)**:低饱和、冷静,与状态色(绿/黄/红)形成强区分;与房产行业"稳健/可靠"意象吻合,同时避免与 success 绿产生语义歧义。
|
||
- **中等圆角(8px)**:既不像消费端(12px+)过于柔软,也不像传统企业端(0-2px)过于呆板。
|
||
- **紧凑密度**:表格默认行高 56px(含 40×40 封面缩略图),表单字段间距 12px,信息密度优先于呼吸感。
|
||
|
||
---
|
||
|
||
## 1. 设计原则(Design Principles)
|
||
|
||
1. **效率优先(Efficiency First)**
|
||
减少视觉噪音,让用户聚焦在数据和操作上。表格为核心场景,一屏可见行数 ≥ 15。
|
||
2. **状态可见(Visible State)**
|
||
任何异步请求必须有反馈(骨架屏 / Spinner / Toast);任何数据变更必须给出成功或失败的即时提示。HTMX 的"静默成功"是 Bug。
|
||
3. **复用先于新建(Reuse Over Reinvent)**
|
||
每个组件在本文中有唯一标准实现;新需求须先尝试组合既有组件,再提案新组件。
|
||
4. **键盘友好(Keyboard First)**
|
||
高频操作(搜索、翻页、新增、保存)须支持键盘;表格、表单、弹窗均支持 Tab / Enter / ESC。
|
||
5. **一致性高于美观(Consistency Over Cleverness)**
|
||
相同的动作在全产品使用相同的图标、色彩、位置。经纪人的肌肉记忆比单页的视觉惊喜更重要。
|
||
|
||
---
|
||
|
||
## 2. 设计 Token(Design Tokens)
|
||
|
||
所有 Token 均映射到 `tailwind.config.js` 的 `theme.extend`,禁止在模板中使用任意十六进制色值。
|
||
|
||
### 2.1 颜色系统
|
||
|
||
#### 2.1.1 品牌色(Primary — Teal)
|
||
|
||
| Token | Hex | Tailwind 类 | 使用场景 |
|
||
| ------------- | --------- | ---------------- | ------------------------- |
|
||
| `primary-50` | `#F0FDFA` | `bg-primary-50` | 页面强调区微底色、Tag 极淡底 |
|
||
| `primary-100` | `#CCFBF1` | `bg-primary-100` | 选中背景、标签底色 |
|
||
| `primary-200` | `#99F6E4` | `bg-primary-200` | Hover 标签底色 |
|
||
| `primary-500` | `#14B8A6` | `bg-primary-500` | 辅助主色(图标、强调文字) |
|
||
| `primary-600` | `#0F766E` | `bg-primary-600` | **主按钮、激活态、Tab 下划线(基准主色)** |
|
||
| `primary-700` | `#115E59` | `bg-primary-700` | 主按钮 Hover |
|
||
| `primary-800` | `#134E4A` | `bg-primary-800` | 主按钮 Active / 深色文字 |
|
||
|
||
#### 2.1.2 中性色(Neutral — Slate 系,偏冷灰)
|
||
|
||
| Token | Hex | 使用场景 |
|
||
|---|---|---|
|
||
| `neutral-50` | `#F8FAFC` | 页面背景 |
|
||
| `neutral-100` | `#F1F5F9` | Hover 底色、表头底色、禁用输入框底色 |
|
||
| `neutral-200` | `#E2E8F0` | 分隔线、默认边框 |
|
||
| `neutral-300` | `#CBD5E1` | 输入框边框、次级按钮边框 |
|
||
| `neutral-400` | `#94A3B8` | 占位符、禁用文字、辅助图标 |
|
||
| `neutral-500` | `#64748B` | 辅助文字、副标题 |
|
||
| `neutral-600` | `#475569` | 次级正文 |
|
||
| `neutral-700` | `#334155` | 正文 |
|
||
| `neutral-800` | `#1E293B` | 标题 |
|
||
| `neutral-900` | `#0F172A` | 强调标题 |
|
||
|
||
#### 2.1.3 语义色(Semantic)
|
||
|
||
| Token | Hex | 使用场景 |
|
||
|---|---|---|
|
||
| `success-600` | `#16A34A` | 操作成功 Toast、在售/激活状态 |
|
||
| `success-50` | `#F0FDF4` | Success Tag 底色 |
|
||
| `warning-600` | `#D97706` | 待确认/临期提醒 |
|
||
| `warning-50` | `#FFFBEB` | Warning Tag 底色 |
|
||
| `danger-600` | `#DC2626` | 删除/错误/逾期 |
|
||
| `danger-50` | `#FEF2F2` | Danger Tag 底色 |
|
||
| `info-600` | `#2563EB` | 信息提示、Link、已成交状态 |
|
||
| `info-50` | `#EFF6FF` | Info Tag 底色 |
|
||
|
||
> **语义色与主色分离**:主色是 Teal,success 是独立绿,避免"主操作按钮看起来像成功提示"。
|
||
|
||
#### 2.1.4 背景层级
|
||
|
||
| 层级 | Tailwind 类 | 使用场景 |
|
||
|---|---|---|
|
||
| L0 页面背景 | `bg-neutral-50` | 整体页面底色 |
|
||
| L1 卡片/面板 | `bg-white` | 内容区块 |
|
||
| L2 表头/次级区块 | `bg-neutral-100` | 表头、工具栏底色、代码块 |
|
||
| L3 悬浮层 | `bg-white shadow-lg border border-neutral-200` | 弹窗、下拉、抽屉 |
|
||
| L4 遮罩 | `bg-neutral-900/40` | Modal / Drawer 遮罩 |
|
||
|
||
### 2.2 字体系统
|
||
|
||
基础字体栈(Tailwind 默认即可):
|
||
```
|
||
font-sans: "Inter", "PingFang SC", "Microsoft YaHei", -apple-system, sans-serif;
|
||
font-mono: "JetBrains Mono", "SFMono-Regular", Menlo, monospace;
|
||
```
|
||
|
||
| 层级 | 字号 | 字重 | 行高 | Tailwind 类 | 使用场景 |
|
||
|---|---|---|---|---|---|
|
||
| H1 页面标题 | 20px | 600 | 28px | `text-xl font-semibold text-neutral-800` | 页面 H1(紧凑型,避免占太多垂直空间) |
|
||
| H2 区块标题 | 16px | 600 | 24px | `text-base font-semibold text-neutral-800` | 卡片/面板标题 |
|
||
| H3 次级标题 | 14px | 600 | 20px | `text-sm font-semibold text-neutral-700` | 表单分组、Section 内标题 |
|
||
| Body 正文 | 14px | 400 | 20px | `text-sm text-neutral-700` | 表单标签、描述、表格数据(默认) |
|
||
| Data 数据强调 | 14px | 500 | 20px | `text-sm font-medium text-neutral-900` | 表格关键列(房源标题、价格) |
|
||
| Number 数值 | 20px | 600 | 28px | `text-xl font-semibold tabular-nums` | Stat Card 数值、价格展示 |
|
||
| Caption 辅助 | 12px | 400 | 16px | `text-xs text-neutral-500` | 提示、占位符、时间戳 |
|
||
| Mono 代码/ID | 12px | 400 | 16px | `text-xs font-mono text-neutral-600` | 房源编号、系统 ID |
|
||
|
||
> **关键约定**:表格与表单中**所有数字列**必须加 `tabular-nums`(等宽数字),保证纵向对齐。
|
||
|
||
### 2.3 间距系统
|
||
|
||
4px 基础栅格,映射到 Tailwind 默认间距 Token。
|
||
|
||
| 场景 | Token | 值 |
|
||
|---|---|---|
|
||
| 原子间距(图标与文字) | `gap-1` / `gap-1.5` | 4 / 6 px |
|
||
| 组件内边距(密集) | `p-2` | 8 px(如 Tag 内) |
|
||
| 组件内边距(标准) | `px-3 py-2` | 12/8 px(输入框、按钮 md) |
|
||
| 卡片内边距 | `p-4` 或 `p-6` | 16 / 24 px |
|
||
| 表单字段纵向间距 | `space-y-3` | 12 px |
|
||
| 区块纵向间距 | `space-y-6` | 24 px |
|
||
| 页面两侧边距 | `px-6` | 24 px |
|
||
| 列表页内容区边距 | `px-6 py-4` | — |
|
||
|
||
### 2.4 阴影与圆角
|
||
|
||
| Token | 值 | 使用场景 |
|
||
|---|---|---|
|
||
| `rounded` | 4px | 紧凑 Tag、小 Badge |
|
||
| `rounded-md` | 6px | 按钮、输入框、下拉选项 |
|
||
| `rounded-lg` | 8px | **卡片、面板、表格容器(基准)** |
|
||
| `rounded-xl` | 12px | Modal / Drawer |
|
||
| `rounded-full` | 圆 | 头像、开关滑块、圆点 |
|
||
|
||
| 阴影 | 使用场景 |
|
||
|---|---|
|
||
| `shadow-xs`(自定义 `0 1px 2px rgba(15,23,42,0.04)`) | 卡片静态 |
|
||
| `shadow-sm` | 卡片 Hover、次级浮层(如 Tooltip) |
|
||
| `shadow-md` | Dropdown、Popover |
|
||
| `shadow-lg` | Modal、Drawer、Toast |
|
||
|
||
### 2.5 圆角圆点与边框
|
||
|
||
- 默认边框:`border border-neutral-200`
|
||
- 强调边框(Focus/激活):`ring-2 ring-primary-600/30 border-primary-600`
|
||
- 错误边框:`border-danger-600 ring-2 ring-danger-600/20`
|
||
|
||
### 2.6 Z-index 层级
|
||
|
||
| 层级 | 值 | 场景 |
|
||
|---|---|---|
|
||
| `z-20` | 20 | 侧边栏、顶部导航 |
|
||
| `z-30` | 30 | 页面内 Sticky 工具栏 |
|
||
| `z-40` | 40 | Dropdown / Popover |
|
||
| `z-50` | 50 | Modal / Drawer 遮罩 |
|
||
| `z-60` | 60 | Modal / Drawer 面板 |
|
||
| `z-70` | 70 | Toast 容器(始终最顶层) |
|
||
|
||
### 2.7 Tailwind 配置示例(节选)
|
||
|
||
```js
|
||
// tailwind.config.js
|
||
module.exports = {
|
||
content: ['./apps/**/templates/**/*.html', './templates/**/*.html'],
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
primary: {
|
||
50: '#F0FDFA', 100: '#CCFBF1', 200: '#99F6E4',
|
||
500: '#14B8A6', 600: '#0F766E', 700: '#115E59', 800: '#134E4A',
|
||
},
|
||
success: { 50: '#F0FDF4', 600: '#16A34A' },
|
||
warning: { 50: '#FFFBEB', 600: '#D97706' },
|
||
danger: { 50: '#FEF2F2', 600: '#DC2626' },
|
||
info: { 50: '#EFF6FF', 600: '#2563EB' },
|
||
},
|
||
boxShadow: {
|
||
xs: '0 1px 2px rgba(15,23,42,0.04)',
|
||
},
|
||
fontFamily: {
|
||
sans: ['Inter', 'PingFang SC', 'Microsoft YaHei', 'sans-serif'],
|
||
mono: ['JetBrains Mono', 'SFMono-Regular', 'Menlo', 'monospace'],
|
||
},
|
||
},
|
||
},
|
||
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 基础组件规范(Base Components)
|
||
|
||
### 3.1 按钮(Button)
|
||
|
||
#### 3.1.1 变体
|
||
|
||
| 变体 | 用途 | Tailwind 类 |
|
||
|---|---|---|
|
||
| Primary | 主操作(每个区域唯一) | `bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800` |
|
||
| Secondary | 次级操作 | `bg-white border border-neutral-300 text-neutral-700 hover:bg-neutral-50 hover:border-neutral-400` |
|
||
| Danger | 删除、不可逆操作 | `bg-danger-600 text-white hover:bg-danger-600/90` |
|
||
| Ghost | 工具栏、表格行操作 | `text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900` |
|
||
| Link | 内联跳转 | `text-primary-600 hover:text-primary-700 hover:underline underline-offset-2` |
|
||
| Icon | 仅图标的工具按钮 | `text-neutral-500 hover:bg-neutral-100 hover:text-neutral-700 rounded-md p-1.5` |
|
||
|
||
#### 3.1.2 尺寸
|
||
|
||
| 尺寸 | 场景 | Tailwind 类 |
|
||
|---|---|---|
|
||
| sm | 表格操作、Tag 内 | `px-2.5 py-1 text-xs rounded` |
|
||
| md(默认) | 表单提交、工具栏 | `px-3 py-1.5 text-sm rounded-md` |
|
||
| lg | 页面主操作(新增按钮) | `px-4 py-2 text-sm rounded-md` |
|
||
|
||
#### 3.1.3 状态
|
||
|
||
- **Focus**:`focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40`
|
||
- **Loading**:禁用 + 内嵌 Spinner + 文案改为进行时("保存中…")
|
||
- **Disabled**:`disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-current`(即 hover 无效果)
|
||
|
||
#### 3.1.4 标准 HTML 片段
|
||
|
||
```html
|
||
<!-- Primary 按钮(含图标) -->
|
||
<button type="submit"
|
||
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium
|
||
bg-primary-600 text-white rounded-md
|
||
hover:bg-primary-700 active:bg-primary-800
|
||
focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40
|
||
disabled:opacity-50 disabled:cursor-not-allowed">
|
||
<svg class="w-4 h-4" aria-hidden="true"><!-- PlusIcon --></svg>
|
||
新增房源
|
||
</button>
|
||
|
||
<!-- Loading 态 -->
|
||
<button disabled
|
||
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium
|
||
bg-primary-600 text-white rounded-md opacity-70 cursor-wait">
|
||
<svg class="w-4 h-4 animate-spin" aria-hidden="true"><!-- Spinner --></svg>
|
||
保存中…
|
||
</button>
|
||
```
|
||
|
||
#### 3.1.5 禁忌
|
||
|
||
- ❌ 同一视觉区域不得出现两个 Primary 按钮
|
||
- ❌ Danger 按钮必须二次确认(Modal),不得直接触发删除
|
||
- ❌ 不得用颜色以外的方式(如加粗)表达"危险"
|
||
- ❌ Ghost 按钮不允许填充背景色做变体(容易与 Secondary 混淆)
|
||
|
||
---
|
||
|
||
### 3.2 输入框(Input)
|
||
|
||
#### 3.2.1 状态
|
||
|
||
| 状态 | Tailwind 类 |
|
||
|---|---|
|
||
| 默认 | `border-neutral-300` |
|
||
| Hover | `hover:border-neutral-400` |
|
||
| Focus | `focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20` |
|
||
| 错误 | `border-danger-600 focus:border-danger-600 focus:ring-danger-600/20` |
|
||
| 禁用 | `bg-neutral-100 text-neutral-400 cursor-not-allowed` |
|
||
| 只读 | `bg-neutral-50 text-neutral-700` |
|
||
|
||
#### 3.2.2 标准结构
|
||
|
||
```html
|
||
<div class="space-y-1">
|
||
<label for="title" class="block text-sm font-medium text-neutral-700">
|
||
房源标题 <span class="text-danger-600">*</span>
|
||
</label>
|
||
<input id="title" type="text" name="title"
|
||
placeholder="请输入房源标题"
|
||
class="block w-full px-3 py-2 text-sm rounded-md
|
||
border border-neutral-300 placeholder:text-neutral-400
|
||
focus:outline-none focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20
|
||
disabled:bg-neutral-100 disabled:text-neutral-400">
|
||
<!-- 错误提示(优先) -->
|
||
<p class="text-xs text-danger-600">标题不能为空</p>
|
||
<!-- 辅助说明(仅无错误时显示) -->
|
||
<p class="text-xs text-neutral-500">不超过 50 字,将用于客户端展示</p>
|
||
</div>
|
||
```
|
||
|
||
#### 3.2.3 变体
|
||
|
||
- **Textarea**:同上,`rows="3"` 起步,右下角 `resize-y`;需字数统计时右下角显示 `x-text="val.length + '/200'"`
|
||
- **带前缀/后缀**:用 `relative` 包裹,前缀/后缀绝对定位 `absolute left-3 / right-3`
|
||
- **带单位**:右侧灰色文字(如"万元"),用 `pr-14` 留白
|
||
- **数字输入**:`type="number"` + `tabular-nums` + 取消浏览器 spinner(用 `appearance-none`)
|
||
- **密码输入**:默认 `type="password"`,右侧眼睛图标按钮(Alpine.js 切换 type)
|
||
|
||
---
|
||
|
||
### 3.3 下拉选择(Select / Dropdown)
|
||
|
||
#### 3.3.1 三种实现路径
|
||
|
||
| 场景 | 实现 |
|
||
|---|---|
|
||
| 单选、选项 ≤ 10、不需搜索 | **原生 `<select>`**(配合 `@tailwindcss/forms` 统一样式) |
|
||
| 单选带搜索、多选、分组 | **Alpine.js 自定义下拉** |
|
||
| 选项依赖其他字段(如选了"区"后加载"商圈") | **HTMX 动态拉取** `hx-get="/api/districts/{{id}}/areas/" hx-trigger="change from:#district" hx-target="#area"` |
|
||
| 树形选择(员工、门店、组织) | **Tree Select**(详见 3.3.4) |
|
||
| 多选标签式 | **Multi-select Tag Input**(详见 3.3.5) |
|
||
|
||
#### 3.3.2 原生 Select 标准
|
||
|
||
```html
|
||
<select class="block w-full px-3 py-2 text-sm rounded-md border border-neutral-300
|
||
focus:outline-none focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20
|
||
bg-white">
|
||
<option value="">请选择</option>
|
||
<option value="sale">出售</option>
|
||
<option value="rent">出租</option>
|
||
</select>
|
||
```
|
||
|
||
#### 3.3.3 Alpine.js 自定义下拉(带搜索)
|
||
|
||
```html
|
||
<div x-data="{ open: false, query: '', selected: null,
|
||
options: [{id:1,name:'浦东新区'},{id:2,name:'徐汇区'}],
|
||
get filtered() { return this.options.filter(o => o.name.includes(this.query)) } }"
|
||
@click.away="open=false" class="relative">
|
||
<button type="button" @click="open=!open"
|
||
class="w-full flex items-center justify-between px-3 py-2 text-sm rounded-md
|
||
border border-neutral-300 bg-white text-left
|
||
hover:border-neutral-400 focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20">
|
||
<span x-text="selected?.name || '请选择区域'" :class="{'text-neutral-400': !selected}"></span>
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- ChevronDownIcon --></svg>
|
||
</button>
|
||
<div x-show="open" x-transition.opacity
|
||
class="absolute z-40 mt-1 w-full bg-white rounded-md shadow-md border border-neutral-200">
|
||
<div class="p-2 border-b border-neutral-100">
|
||
<input x-model="query" placeholder="搜索…"
|
||
class="w-full px-2 py-1 text-sm rounded border border-neutral-200">
|
||
</div>
|
||
<ul class="max-h-60 overflow-y-auto py-1">
|
||
<template x-for="o in filtered" :key="o.id">
|
||
<li @click="selected=o; open=false"
|
||
class="px-3 py-2 text-sm text-neutral-700 hover:bg-primary-50 cursor-pointer flex items-center justify-between">
|
||
<span x-text="o.name"></span>
|
||
<svg x-show="selected?.id===o.id" class="w-4 h-4 text-primary-600"><!-- CheckIcon --></svg>
|
||
</li>
|
||
</template>
|
||
<li x-show="filtered.length===0" class="px-3 py-6 text-xs text-neutral-400 text-center">暂无匹配</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### 3.3.4 Tree Select(树形选择器)
|
||
|
||
适用组织架构、员工选择、部门选择等。
|
||
|
||
- 数据:后端一次性返回完整 JSON 树(组织树通常 < 500 节点,无压力)
|
||
- 交互:节点左侧 `▶` 图标控制子树展开/折叠,叶节点可点击选中
|
||
- 搜索:输入时过滤,**命中子节点的父节点强制展开**
|
||
- 底部固定操作行:`隐藏离职员工` 开关
|
||
|
||
实现要点见 `组件清单.md` Tree Select 章节。
|
||
|
||
#### 3.3.5 Multi-select Tag Input(多选标签输入)
|
||
|
||
适用于多个平级标签(房源状态、客源需求类型)。见 3.7 状态标签组件的 Tag 样式。
|
||
|
||
```html
|
||
<!-- 容器(Focus 时 ring) -->
|
||
<div class="flex flex-wrap gap-1 min-h-[38px] px-2 py-1 rounded-md border border-neutral-300
|
||
focus-within:border-primary-600 focus-within:ring-2 focus-within:ring-primary-600/20">
|
||
<!-- 每个已选 Tag -->
|
||
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded
|
||
bg-primary-50 text-primary-700">
|
||
出售
|
||
<button type="button" class="hover:text-primary-900">
|
||
<svg class="w-3 h-3"><!-- XMarkIcon --></svg>
|
||
</button>
|
||
</span>
|
||
<input class="flex-1 min-w-[80px] text-sm outline-none" placeholder="选择或输入…">
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.4 表格(Table)
|
||
|
||
Fonrey 的核心场景,规范必须严格执行。
|
||
|
||
#### 3.4.1 结构规范
|
||
|
||
| 区域 | 说明 | Tailwind 类 |
|
||
|---|---|---|
|
||
| 外壳 | 圆角卡片容器 | `bg-white rounded-lg border border-neutral-200 overflow-hidden` |
|
||
| 表头 `<thead>` | 粘性、小字、中性色底 | `bg-neutral-50 text-xs font-medium text-neutral-500 uppercase tracking-wider sticky top-0 z-10` |
|
||
| 表头 `<th>` | 高 36px;排序箭头右对齐 | `px-4 py-2 text-left` |
|
||
| 数据行 `<tr>` | 密度可切换;Hover 高亮 | `hover:bg-neutral-50` |
|
||
| 数据单元 `<td>` | 正文字号;垂直居中 | `px-3 py-3 text-sm text-neutral-700 align-middle whitespace-nowrap` |
|
||
| 选中行 | 浅主色高亮 | `bg-primary-50/40` |
|
||
| 操作列 | 固定右侧;Ghost 图标按钮,Hover 显形 | `sticky right-0 bg-white opacity-0 group-hover:opacity-100` |
|
||
| 表格底部(分页栏) | | `px-4 py-3 border-t border-neutral-200 bg-white flex items-center justify-between` |
|
||
|
||
- **斑马纹**:B2B 高密度场景**不启用**(视觉噪音)。Hover 行高亮已足够区分。
|
||
- **封面缩略图**:房源/楼盘等含图业务表格,第一数据列(通常为"标题"列)内嵌缩略图(`<img>` 或占位 `<div>`),尺寸随密度档联动(见下表)。缩略图统一 `rounded` (4px),对象填充 `object-cover`,加载失败占位为 `bg-neutral-100` + 图片占位图标。
|
||
- **三档密度**:工具栏右侧"密度"图标按钮切换,Alpine.js + `localStorage`(key: `fonrey:table:{module}:density`)持久化,默认 `standard`。
|
||
|
||
| 档位 | Key | 行高 | 缩略图尺寸 | 单元内边距 | 使用场景 |
|
||
|---|---|---|---|---|---|
|
||
| 紧凑 Compact | `compact` | 40px | 无(图片列隐藏) | `px-3 py-2` | 数据核对、大批量浏览、导出前预览 |
|
||
| 标准 Standard(默认) | `standard` | 56px | 40×40 | `px-3 py-3` | 日常工作,"一眼认房" |
|
||
| 舒适 Comfortable | `comfortable` | 72px | 56×56 | `px-3 py-4` | 含更多副信息(楼层/朝向/装修标签换行展示) |
|
||
|
||
> **实现提示**:密度切换仅改 `<tbody>` 上的 class(如 `table-density-standard`),通过父级 class + 子选择器统一控制行高、内边距、图片显隐,避免逐行改 class。
|
||
> **无图业务**(客源、跟进记录、权限等)表格固定使用 `compact` 40px 行高,不提供密度切换。
|
||
|
||
#### 3.4.2 标准片段
|
||
|
||
```html
|
||
<div class="bg-white rounded-lg border border-neutral-200 overflow-hidden">
|
||
<!-- 工具栏:选中条数 + 批量操作 + 视图切换 + 导出 + 自定义列 -->
|
||
<div id="property-toolbar"
|
||
class="hidden px-4 py-2 bg-primary-50 border-b border-primary-100 items-center justify-between text-sm"
|
||
:class="selected.length > 0 ? 'flex' : 'hidden'">
|
||
<span class="text-primary-700">已选中 <span x-text="selected.length"></span> 条</span>
|
||
<div class="flex items-center gap-2">
|
||
<button class="text-sm text-neutral-700 hover:text-primary-700">批量分享</button>
|
||
<button class="text-sm text-neutral-700 hover:text-danger-600">批量删除</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="overflow-x-auto">
|
||
<table class="min-w-full divide-y divide-neutral-200">
|
||
<thead class="bg-neutral-50">
|
||
<tr>
|
||
<th class="px-4 py-2 w-10"><input type="checkbox" class="rounded border-neutral-300"></th>
|
||
<th class="px-4 py-2 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider">房源编号</th>
|
||
<th class="px-4 py-2 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider">
|
||
<button class="inline-flex items-center gap-1 hover:text-neutral-700">
|
||
挂牌价 <svg class="w-3 h-3"><!-- ChevronUpDownIcon --></svg>
|
||
</button>
|
||
</th>
|
||
<th class="px-4 py-2 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider">状态</th>
|
||
<th class="px-4 py-2 sticky right-0 bg-neutral-50">操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-neutral-100 bg-white"
|
||
hx-get="/property/list/" hx-trigger="load" hx-swap="innerHTML">
|
||
<!-- 行由 HTMX 填充 -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 分页栏 -->
|
||
<div class="px-4 py-3 border-t border-neutral-200 bg-white flex items-center justify-between">
|
||
<div class="text-xs text-neutral-500">共 3,629 条</div>
|
||
<div class="flex items-center gap-1"><!-- 分页按钮组 --></div>
|
||
<div class="flex items-center gap-2 text-xs text-neutral-500">
|
||
<select class="text-xs rounded border-neutral-300">
|
||
<option>20 条/页</option><option>50 条/页</option><option>100 条/页</option>
|
||
</select>
|
||
<span>跳至</span>
|
||
<input type="number" class="w-12 text-xs rounded border-neutral-300 text-center">
|
||
<span>页</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### 3.4.3 HTMX 局部刷新约定
|
||
|
||
- 所有筛选、排序、翻页触发 `hx-get`,目标容器为 `<tbody>` 或 `#table-wrapper`
|
||
- **加载态**:`htmx:beforeRequest` 给 `<tbody>` 叠加骨架屏(`animate-pulse` 的 5 行占位)
|
||
- **错误态**:`htmx:responseError` 保留原内容 + 触发 Error Toast
|
||
|
||
#### 3.4.4 自定义列(Column Visibility)
|
||
|
||
右上角"自定义列表"按钮,弹出 Checkbox 面板,选择状态持久化到 `localStorage`(Key: `fonrey:table:{module}:cols`)。隐藏的列通过 Alpine.js `:class="{'hidden': !col.visible}"` 控制。
|
||
|
||
#### 3.4.5 空状态
|
||
|
||
见 6.3 空状态设计。表格空状态占整个 `<tbody>`,不使用 `colspan` 占所有列的骚操作,改用覆盖层(`absolute inset-0 flex items-center justify-center`)。
|
||
|
||
---
|
||
|
||
### 3.5 分页(Pagination)
|
||
|
||
```html
|
||
<nav class="flex items-center gap-1">
|
||
<button class="p-1.5 text-neutral-500 hover:bg-neutral-100 rounded disabled:opacity-30" disabled>
|
||
<svg class="w-4 h-4"><!-- ChevronLeftIcon --></svg>
|
||
</button>
|
||
<button class="min-w-[32px] h-8 px-2 text-sm bg-primary-600 text-white rounded">1</button>
|
||
<button class="min-w-[32px] h-8 px-2 text-sm text-neutral-700 hover:bg-neutral-100 rounded">2</button>
|
||
<button class="min-w-[32px] h-8 px-2 text-sm text-neutral-700 hover:bg-neutral-100 rounded">3</button>
|
||
<span class="px-1 text-neutral-400">…</span>
|
||
<button class="min-w-[32px] h-8 px-2 text-sm text-neutral-700 hover:bg-neutral-100 rounded">182</button>
|
||
<button class="p-1.5 text-neutral-500 hover:bg-neutral-100 rounded">
|
||
<svg class="w-4 h-4"><!-- ChevronRightIcon --></svg>
|
||
</button>
|
||
</nav>
|
||
```
|
||
|
||
- **当前页**:`bg-primary-600 text-white`
|
||
- **省略号**:Django 后端生成(前 3 后 3 + 当前 ±1)
|
||
- **同页多表格独立分页**:每个分页器 `hx-target` 指向各自的表格容器 id(见 `组件清单.md`)
|
||
|
||
---
|
||
|
||
### 3.6 弹窗(Modal)与抽屉(Drawer)
|
||
|
||
#### 3.6.1 选择原则
|
||
|
||
| 类型 | 场景 | 宽度 | 关闭行为 |
|
||
|---|---|---|---|
|
||
| Confirm Modal | 删除确认、不可逆操作 | `max-w-sm`(400px) | 点击遮罩**不关闭**(防误触),ESC 关闭 |
|
||
| Form Modal | 新增/编辑(字段 ≤ 6) | `max-w-lg`(560px) | 点击遮罩关闭,ESC 关闭 |
|
||
| Right Drawer | 查看详情、新增/编辑(字段多)、参考主页面 | `w-[640px]` 或 `w-[480px]` | 点击遮罩关闭,ESC 关闭 |
|
||
| Full Modal | 复杂配置(权限矩阵) | `w-[80vw] max-w-6xl` | 点击遮罩**不关闭**,顶部关闭按钮 |
|
||
|
||
#### 3.6.2 Modal 标准结构
|
||
|
||
```html
|
||
<div x-data="{ open: false }" @keydown.escape.window="open=false">
|
||
<!-- 触发 -->
|
||
<button @click="open=true">新增房源</button>
|
||
|
||
<!-- 遮罩 -->
|
||
<div x-show="open" x-transition.opacity
|
||
class="fixed inset-0 z-50 bg-neutral-900/40"
|
||
@click="open=false"></div>
|
||
|
||
<!-- 面板 -->
|
||
<div x-show="open" x-transition
|
||
class="fixed inset-0 z-60 flex items-center justify-center p-4 pointer-events-none">
|
||
<div class="w-full max-w-lg bg-white rounded-xl shadow-lg pointer-events-auto flex flex-col max-h-[85vh]">
|
||
<!-- Header -->
|
||
<div class="flex items-center justify-between px-5 py-4 border-b border-neutral-200">
|
||
<h2 class="text-base font-semibold text-neutral-800">新增房源</h2>
|
||
<button @click="open=false" class="p-1 text-neutral-500 hover:bg-neutral-100 rounded-md">
|
||
<svg class="w-5 h-5"><!-- XMarkIcon --></svg>
|
||
</button>
|
||
</div>
|
||
<!-- Body -->
|
||
<div class="flex-1 overflow-y-auto px-5 py-4 space-y-4">
|
||
<!-- 表单内容 -->
|
||
</div>
|
||
<!-- Footer -->
|
||
<div class="flex items-center justify-end gap-2 px-5 py-3 border-t border-neutral-200 bg-neutral-50">
|
||
<button @click="open=false" class="px-3 py-1.5 text-sm border border-neutral-300 rounded-md hover:bg-white">取消</button>
|
||
<button class="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-md hover:bg-primary-700">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### 3.6.3 Drawer 标准结构
|
||
|
||
```html
|
||
<div x-show="open" x-transition:enter="ease-out duration-200"
|
||
x-transition:enter-start="translate-x-full" x-transition:enter-end="translate-x-0"
|
||
x-transition:leave="ease-in duration-150"
|
||
x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full"
|
||
class="fixed right-0 top-0 h-full w-[640px] z-60 bg-white shadow-lg flex flex-col border-l border-neutral-200">
|
||
<!-- 同 Modal 的 Header / Body(overflow-y-auto) / Footer 结构 -->
|
||
</div>
|
||
```
|
||
|
||
#### 3.6.4 Confirm Modal
|
||
|
||
用于删除、下架、离职等不可逆操作。固定使用 Danger 风格:
|
||
|
||
```html
|
||
<!-- 面板内结构 -->
|
||
<div class="p-5 text-center space-y-3">
|
||
<div class="mx-auto w-12 h-12 rounded-full bg-danger-50 flex items-center justify-center">
|
||
<svg class="w-6 h-6 text-danger-600"><!-- ExclamationTriangleIcon --></svg>
|
||
</div>
|
||
<h2 class="text-base font-semibold text-neutral-800">确认删除该房源?</h2>
|
||
<p class="text-sm text-neutral-500">删除后将进入回收站,30 天内可恢复。</p>
|
||
</div>
|
||
<div class="flex items-center justify-center gap-2 px-5 py-3 border-t bg-neutral-50">
|
||
<button class="px-4 py-1.5 text-sm border border-neutral-300 rounded-md">取消</button>
|
||
<button class="px-4 py-1.5 text-sm bg-danger-600 text-white rounded-md hover:bg-danger-600/90">确认删除</button>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.7 状态标签(Badge / Tag)
|
||
|
||
用于状态展示,不可点击,不可编辑(编辑用 Multi-select Tag Input)。
|
||
|
||
| 样式类型 | 场景 | Tailwind 类 |
|
||
|---|---|---|
|
||
| Subtle(默认,底色+文字) | 高频状态展示 | `bg-{color}-50 text-{color}-700` |
|
||
| Solid | 极少数强调(如"紧急") | `bg-{color}-600 text-white` |
|
||
| Outline | 低密度场景 | `border border-{color}-600 text-{color}-700` |
|
||
|
||
#### 3.7.1 业务状态色板
|
||
|
||
| 状态 | 色系 | 示例 |
|
||
|---|---|---|
|
||
| 在售 / 激活 / 在职 | success | `bg-success-50 text-success-600` |
|
||
| 出租 | info | `bg-info-50 text-info-600` |
|
||
| 跟进中 / 待确认 | warning | `bg-warning-50 text-warning-600` |
|
||
| 已成交 / 完成 | primary | `bg-primary-50 text-primary-700` |
|
||
| 已下架 / 停用 / 离职 | neutral | `bg-neutral-100 text-neutral-500` |
|
||
| 逾期 / 紧急 / 冻结 | danger | `bg-danger-50 text-danger-600` |
|
||
| 暂缓 | 浅灰 + 斜体 | `bg-neutral-100 text-neutral-600 italic` |
|
||
|
||
#### 3.7.2 交易类型标签色板
|
||
|
||
交易类型标签(买卖 / 租赁 / 租售)属于**分类标签**,不表达状态语义,禁止使用语义色(danger / info solid)。
|
||
|
||
**视觉层级要求**:交易类型标签须比同行副标签(满五、独家等)**更大、更醒目**:
|
||
- 交易类型标签:`text-xs`(12px)+ `px-2 py-0.5` + `font-semibold` + 较深底色
|
||
- 副标签(房源属性 Tag):`text-[10px]` + `px-1.5 py-0.5` + `font-medium` + 极淡底色
|
||
|
||
| 交易类型 | 色系 | Tailwind 类 | 设计理由 |
|
||
|---|---|---|---|
|
||
| 买卖 | primary(Teal) | `bg-primary-200 text-primary-800` | 核心出售业务,用品牌主色强调 |
|
||
| 租赁 | warning(Amber) | `bg-warning-200 text-warning-800` | 与买卖形成色相区分,暖色低调 |
|
||
| 租售 | neutral | `bg-neutral-300 text-neutral-800` | 兼含两种类型,中性色避免歧义 |
|
||
|
||
> **禁止**对交易类型标签使用 Solid 样式(`bg-{color}-600 text-white`),该样式仅用于极少数强调性状态(如"紧急")。
|
||
|
||
#### 3.7.2 标准片段
|
||
|
||
```html
|
||
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded
|
||
bg-success-50 text-success-600">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-success-600"></span>
|
||
在售
|
||
</span>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.8 Toast 通知
|
||
|
||
- 统一出现在**右下角**(`fixed bottom-6 right-6 z-70`)
|
||
- 多条堆叠,新消息**追加在底部**,超出视窗则顶部旧消息自动移除
|
||
- 固定宽度 `w-80`
|
||
|
||
| 类型 | 图标色 | 停留 | 手动关闭 |
|
||
|---|---|---|---|
|
||
| Success | `text-success-600` | 3s | 否 |
|
||
| Error | `text-danger-600` | 5s | 是 |
|
||
| Warning | `text-warning-600` | 5s | 是 |
|
||
| Info | `text-info-600` | 3s | 否 |
|
||
|
||
```html
|
||
<div class="w-80 bg-white rounded-lg shadow-lg border border-neutral-200
|
||
flex items-start gap-3 p-3"
|
||
x-data="{ show: true }" x-show="show" x-transition>
|
||
<svg class="w-5 h-5 text-success-600 shrink-0 mt-0.5"><!-- CheckCircleIcon --></svg>
|
||
<div class="flex-1 text-sm">
|
||
<p class="font-medium text-neutral-800">保存成功</p>
|
||
<p class="text-xs text-neutral-500">房源已更新</p>
|
||
</div>
|
||
<button @click="show=false" class="text-neutral-400 hover:text-neutral-600">
|
||
<svg class="w-4 h-4"><!-- XMarkIcon --></svg>
|
||
</button>
|
||
</div>
|
||
```
|
||
|
||
**HTMX 触发规范**:后端响应 `HX-Trigger` header:
|
||
```
|
||
HX-Trigger: {"fonrey:toast": {"type": "success", "message": "保存成功", "detail": "房源已更新"}}
|
||
```
|
||
前端全局监听 `document.addEventListener('fonrey:toast', ...)` 插入 Toast 节点。
|
||
|
||
---
|
||
|
||
### 3.9 加载状态(Loading States)
|
||
|
||
| 场景 | 实现方式 |
|
||
|---|---|
|
||
| HTMX 局部请求 | 目标区域叠加 Skeleton(`animate-pulse` 灰条占位),`htmx:beforeRequest` 添加,`htmx:afterSettle` 移除 |
|
||
| 按钮提交中 | 禁用按钮 + 内嵌 Spinner + 文案改为进行时("保存中…") |
|
||
| 页面首次加载 | 内容区骨架屏(与最终结构同骨架) |
|
||
| 长耗时任务(导出) | Celery 异步,Info Toast 提示"任务已提交,完成后通知";结果通过站内消息推送 |
|
||
|
||
#### 3.9.1 Skeleton 标准
|
||
|
||
```html
|
||
<div class="animate-pulse space-y-2">
|
||
<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>
|
||
```
|
||
|
||
#### 3.9.2 Spinner
|
||
|
||
```html
|
||
<svg class="w-4 h-4 animate-spin text-current" viewBox="0 0 24 24">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" fill="none" opacity="0.25"/>
|
||
<path d="M12 2a10 10 0 0 1 10 10" stroke="currentColor" stroke-width="3" fill="none"/>
|
||
</svg>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.10 Tab 导航(Tabs)
|
||
|
||
用于详情页内容区切换、筛选维度切换。
|
||
|
||
```html
|
||
<div x-data="{ active: 'basic' }">
|
||
<div class="border-b border-neutral-200 flex items-center gap-4">
|
||
<button @click="active='basic'"
|
||
:class="active==='basic' ? 'border-primary-600 text-primary-700' : 'border-transparent text-neutral-500 hover:text-neutral-700'"
|
||
class="px-1 py-3 text-sm font-medium border-b-2 transition-colors">
|
||
基本信息
|
||
</button>
|
||
<button @click="active='follow'"
|
||
:class="active==='follow' ? 'border-primary-600 text-primary-700' : 'border-transparent text-neutral-500 hover:text-neutral-700'"
|
||
class="px-1 py-3 text-sm font-medium border-b-2 transition-colors">
|
||
跟进记录
|
||
<span class="ml-1 px-1.5 py-0.5 text-xs bg-neutral-100 text-neutral-600 rounded">12</span>
|
||
</button>
|
||
</div>
|
||
<div x-show="active==='basic'" class="pt-4"
|
||
hx-get="/property/123/basic/" hx-trigger="intersect once">
|
||
<!-- 懒加载 -->
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
- **激活指示**:底部 2px 主色下划线 + 主色文字
|
||
- **计数 Badge**:中性色小圆角标
|
||
- **懒加载**:切换到 Tab 时用 HTMX `hx-get` 拉取,首次加载后缓存
|
||
|
||
---
|
||
|
||
### 3.11 折叠面板(Accordion / Collapsible)
|
||
|
||
引入 Alpine 官方插件 `@alpinejs/collapse`(1KB)处理高度过渡。
|
||
|
||
```html
|
||
<div x-data="{ open: true }" class="bg-white rounded-lg border border-neutral-200">
|
||
<button @click="open=!open"
|
||
class="w-full flex items-center justify-between px-4 py-3 text-left">
|
||
<span class="text-sm font-semibold text-neutral-800">重点信息</span>
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-sm text-neutral-500">8% / 8%</span>
|
||
<svg :class="open ? 'rotate-180' : ''" class="w-4 h-4 text-neutral-400 transition-transform">
|
||
<!-- ChevronDownIcon -->
|
||
</svg>
|
||
</div>
|
||
</button>
|
||
<div x-show="open" x-collapse class="px-4 pb-3 space-y-2 border-t border-neutral-100">
|
||
<!-- 子内容 -->
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.12 Toggle 开关(Switch)
|
||
|
||
```html
|
||
<button type="button" role="switch"
|
||
x-data="{ on: false }" @click="on=!on"
|
||
:aria-checked="on"
|
||
:class="on ? 'bg-primary-600' : 'bg-neutral-300'"
|
||
class="relative inline-flex items-center w-9 h-5 rounded-full transition-colors
|
||
focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40
|
||
disabled:opacity-50 disabled:cursor-not-allowed">
|
||
<span :class="on ? 'translate-x-4' : 'translate-x-0.5'"
|
||
class="inline-block w-4 h-4 bg-white rounded-full shadow transition-transform"></span>
|
||
</button>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.13 日期与日期范围
|
||
|
||
- **单日期**:原生 `<input type="date">` + `@tailwindcss/forms` 样式
|
||
- **日期范围(核心)**:引入 **Flatpickr**(16KB,无框架依赖)。参考 `组件清单.md` 推荐。
|
||
|
||
```html
|
||
<input type="text" id="date-range"
|
||
class="block w-full px-3 py-2 text-sm rounded-md border border-neutral-300
|
||
focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20"
|
||
placeholder="开始日期 - 结束日期">
|
||
<script>
|
||
flatpickr('#date-range', { mode: 'range', showMonths: 2, locale: 'zh', dateFormat: 'Y-m-d' });
|
||
</script>
|
||
```
|
||
|
||
Flatpickr 自定义样式覆盖见附录 10.3。
|
||
|
||
---
|
||
|
||
### 3.14 文件上传
|
||
|
||
- **单/少量图片(头像、实勘主图)**:原生 `<input type="file">` + Alpine 预览
|
||
- **多图批量上传(相册管理)**:引入 **Filepond**,支持拖拽、预览、进度、队列
|
||
- **拖拽排序**:引入 **SortableJS**(3KB)
|
||
|
||
统一上传目标 Cloudflare R2,后端接收后转存并返回 URL。上传进度条使用 Filepond 内置样式,颜色 override 为 `primary-600`。
|
||
|
||
---
|
||
|
||
### 3.15 图片预览(Lightbox)
|
||
|
||
引入 **Viewer.js**(5KB,无依赖)。覆盖缩放、旋转、全屏、翻页、缩略图条全部需求。样式用 Tailwind 覆盖。
|
||
|
||
---
|
||
|
||
## 4. 业务组件规范(Business Components)
|
||
|
||
### 4.1 房源卡片(Property Card)
|
||
|
||
用于"卡片视图"模式(列表页允许用户切换表格/卡片两种视图)。
|
||
|
||
```
|
||
┌──────────────────────────────────────┐
|
||
│ ┌─────┐ 房源标题(2行截断) ⋯ │
|
||
│ │封面 │ 浦东 · 张江高科 │
|
||
│ │ 图 │ 89㎡ · 2室1厅 · 高层 │
|
||
│ └─────┘ ¥580 万 [在售] │
|
||
│ ──────────────────────────────── │
|
||
│ 👤 张三 · 2 小时前更新 │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
- 宽度:响应式栅格 `grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4`
|
||
- 封面图:`w-32 h-24 rounded object-cover`,固定比例 4:3
|
||
- 点击整卡:进入详情页
|
||
- 右上角 `⋯`:触发 Action Menu(见 4.4)
|
||
|
||
### 4.2 筛选栏(Filter Bar)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 区域: [浦东新区▾] 价格: [¥300万-¥800万▾] 户型:[▾] 状态:[在售▾] │
|
||
│ [+ 高级筛选 (3)] [清空] [搜索] │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ 已选: [浦东新区 ×] [¥300-800万 ×] [2室1厅 ×] [清空所有] │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
- 常驻筛选横向排列,用 Select / Multi-select / Date Range
|
||
- 高级筛选按钮(带数字 Badge 表示已填的高级条件数量),点击展开折叠区
|
||
- **筛选变化触发 `hx-get`**,`hx-trigger="change delay:200ms"`,结果刷新列表 `hx-target`
|
||
- 已选条件 Tag 可单独删除(`×`)或一键清空
|
||
|
||
### 4.3 数据统计卡片(Stat Card)
|
||
|
||
```html
|
||
<div class="bg-white rounded-lg border border-neutral-200 p-4 flex items-start gap-3">
|
||
<div class="w-10 h-10 rounded-lg bg-primary-50 flex items-center justify-center text-primary-600 shrink-0">
|
||
<svg class="w-5 h-5"><!-- HomeIcon --></svg>
|
||
</div>
|
||
<div class="flex-1 min-w-0">
|
||
<div class="text-xs text-neutral-500">在售房源</div>
|
||
<div class="mt-1 flex items-baseline gap-2">
|
||
<span class="text-xl font-semibold tabular-nums text-neutral-900">1,289</span>
|
||
<span class="inline-flex items-center text-xs text-success-600">
|
||
<svg class="w-3 h-3"><!-- ArrowTrendingUpIcon --></svg> 12.5%
|
||
</span>
|
||
</div>
|
||
<div class="text-xs text-neutral-400 mt-0.5">较上月</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
### 4.4 操作菜单(Action Menu)
|
||
|
||
三点按钮 `⋯` 触发下拉,危险操作(删除)永远在底部且有分隔线。
|
||
|
||
```html
|
||
<div x-data="{ open: false }" @click.away="open=false" class="relative">
|
||
<button @click="open=!open" class="p-1 rounded hover:bg-neutral-100 text-neutral-500">
|
||
<svg class="w-5 h-5"><!-- EllipsisHorizontalIcon --></svg>
|
||
</button>
|
||
<div x-show="open" x-transition class="absolute right-0 mt-1 w-44 z-40
|
||
bg-white rounded-md shadow-md border border-neutral-200 py-1">
|
||
<button class="w-full text-left px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-50 flex items-center gap-2">
|
||
<svg class="w-4 h-4 text-neutral-500"><!-- PencilIcon --></svg> 编辑
|
||
</button>
|
||
<button class="w-full text-left px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-50 flex items-center gap-2">
|
||
<svg class="w-4 h-4 text-neutral-500"><!-- ShareIcon --></svg> 分享
|
||
</button>
|
||
<button class="w-full text-left px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-50 flex items-center gap-2">
|
||
<svg class="w-4 h-4 text-neutral-500"><!-- ArrowUpOnSquareIcon --></svg> 下架
|
||
</button>
|
||
<div class="my-1 border-t border-neutral-100"></div>
|
||
<button class="w-full text-left px-3 py-2 text-sm text-danger-600 hover:bg-danger-50 flex items-center gap-2">
|
||
<svg class="w-4 h-4"><!-- TrashIcon --></svg> 删除
|
||
</button>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
### 4.5 跟进时间线(Follow-up Timeline)
|
||
|
||
用于房源/客源详情页的跟进记录展示。
|
||
|
||
```html
|
||
<ol class="relative border-l-2 border-neutral-200 ml-3 space-y-5 pl-5">
|
||
<li class="relative">
|
||
<span class="absolute -left-[27px] top-1 w-3 h-3 rounded-full bg-primary-600 ring-4 ring-white"></span>
|
||
<div class="flex items-center gap-2 text-xs text-neutral-500">
|
||
<span class="font-medium text-neutral-700">张三</span>
|
||
<span>修改跟进</span>
|
||
<span>·</span>
|
||
<time>2026-04-25 14:30</time>
|
||
</div>
|
||
<div class="mt-1 text-sm text-neutral-700">
|
||
业主同意降价 20 万,挂牌价调整为 580 万,已通知重点客户。
|
||
</div>
|
||
</li>
|
||
<!-- 更多条目 -->
|
||
</ol>
|
||
|
||
<div class="mt-4 text-center">
|
||
<button hx-get="/property/123/follow/?page=2" hx-target="this" hx-swap="beforeend"
|
||
class="text-sm text-primary-600 hover:text-primary-700">查看全部跟进</button>
|
||
</div>
|
||
```
|
||
|
||
### 4.6 相册管理(Photo Gallery Manager)
|
||
|
||
参考 `组件清单.md`。核心组件:
|
||
- 可横向滚动分类 Tab + 溢出 `⋯`
|
||
- 多选图片网格(`grid-cols-6 gap-2`,每格 `aspect-[4/3]`)
|
||
- 批量工具栏(依赖选中状态启用)
|
||
- 上传 Modal(Filepond)
|
||
- 排序模式(SortableJS)
|
||
|
||
### 4.7 字段填写要求配置表(Dynamic Form Table)
|
||
|
||
用于 `系统设置 → 房源设置 → 字段标签设置`。一张可增删行 + 拖拽排序 + Toggle 切换的表格,结合 SortableJS + Alpine.js。区分系统预置行(不可删)与用户自定义行。详见 `组件清单.md` §Sortable Table with Drag Handle。
|
||
|
||
### 4.8 权限矩阵(Permission Matrix)
|
||
|
||
纵向:功能模块(房源、客源、楼盘 …);横向:权限动作(查看、新增、编辑、删除、审核)。交叉单元格为 Toggle 或三态(继承 / 开 / 关)。宽度超屏时 **左列固定** + 横向滚动:
|
||
|
||
```html
|
||
<div class="overflow-x-auto">
|
||
<table class="min-w-full">
|
||
<thead>
|
||
<tr>
|
||
<th class="sticky left-0 bg-white z-10 px-4 py-2 text-left text-xs font-medium text-neutral-500">
|
||
功能模块
|
||
</th>
|
||
<th class="px-4 py-2 text-center text-xs">查看</th>
|
||
<th class="px-4 py-2 text-center text-xs">新增</th>
|
||
<!-- ... -->
|
||
</tr>
|
||
</thead>
|
||
<!-- ... -->
|
||
</table>
|
||
</div>
|
||
```
|
||
|
||
### 4.9 业主/客源联系人卡片(Contact Card)
|
||
|
||
头像 + 姓名 + 电话(敏感脱敏 `138****1234`) + 角色标签 + 操作菜单。手机号悬浮显示"查看完整"按钮(触发权限校验 + 审计日志)。
|
||
|
||
---
|
||
|
||
## 5. 页面布局模板(Page Layout Templates)
|
||
|
||
### 5.1 整体框架
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────────┐
|
||
│ Topbar(56px,sticky top-0 z-20) │
|
||
│ [Logo 150px] [主导航: 主页 房源 客源 营销 交易 数据 人事 系统] │
|
||
│ [消息🔔] [帮助?] [头像▾] │
|
||
├─────────────────┬────────────────────────────────────────────────────┤
|
||
│ Sidebar │ 内容区(Content Area) │
|
||
│ 展开 240px │ 面包屑 > 房源管理 > 二手 & 租赁 [次操作] [主操作] │
|
||
│ 折叠 64px │ ───────────────────────────────────────────────── │
|
||
│ │ 页面标题 H1 │
|
||
│ [当前主分类 │ ───────────────────────────────────────────────── │
|
||
│ 的子菜单] │ 筛选 / 工具栏(Sticky,top-14) │
|
||
│ │ ───────────────────────────────────────────────── │
|
||
│ ← 折叠按钮 │ 内容主体(表格 / 卡片 / 表单) │
|
||
│ │ │
|
||
│ │ 分页栏 │
|
||
└─────────────────┴────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**尺寸约定**
|
||
|
||
| 区域 | 规格 |
|
||
|---|---|
|
||
| Topbar 高度 | 56px(`h-14`);`sticky top-0 z-20` |
|
||
| Sidebar 展开宽度 | 240px(`w-60`);`fixed left-0 top-14 h-[calc(100vh-56px)] z-20` |
|
||
| Sidebar 折叠宽度 | 64px(`w-16`) |
|
||
| 内容区偏移 | 展开态 `ml-60`,折叠态 `ml-16`;`transition-[margin] duration-200` |
|
||
| 内容区内边距 | `px-6 py-4` |
|
||
| 工具栏 Sticky 偏移 | `sticky top-14 z-30`(Topbar 下方紧贴) |
|
||
|
||
---
|
||
|
||
### 5.2 顶部导航栏(Topbar)
|
||
|
||
#### 5.2.1 结构规范
|
||
|
||
Topbar 分三区:左区(Logo)、中区(主导航 + 搜索)、右区(工具区)。
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ [🏢 Fonrey ▾] 主页 房源 客源 营销 [搜索框] [🔔] [⚙] [WS 王顺] │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| 区域 | 内容 | 样式 |
|
||
|---|---|---|
|
||
| 左区(`w-60 shrink-0`) | Logo 图标 + 产品名"Fonrey" | `flex items-center gap-2 px-4 text-base font-semibold text-white` |
|
||
| 中区(`flex-1`) | 主分类导航 + 全局搜索框 | `flex items-center gap-4 flex-1 px-2` |
|
||
| 右区(`shrink-0`) | 消息通知 / 设置 / 头像+姓名 | `flex items-center gap-1 px-4 shrink-0` |
|
||
|
||
> **配色**:Topbar 背景使用 `bg-primary-800`(`#134E4A`,深青绿),与下方白色 Sidebar 和 `bg-neutral-50` 内容区形成明确层次区分,同时保持品牌色系一致性。
|
||
|
||
#### 5.2.2 主导航 Tab
|
||
|
||
主分类(8项):**主页 / 房源 / 客源 / 营销 / 交易 / 数据 / 人事 / 系统**
|
||
|
||
- 每项:`px-3 py-1.5 text-sm rounded-md`
|
||
- 默认态:`text-primary-100 hover:bg-primary-700 hover:text-white`
|
||
- 激活态:`bg-primary-600 text-white font-medium`
|
||
- 点击切换主分类 → Sidebar 同步切换为该分类的子菜单
|
||
|
||
#### 5.2.3 全局搜索框
|
||
|
||
位于主导航右侧,宽度 `max-w-xs`。
|
||
|
||
- 默认态:`bg-primary-700/60 border-transparent text-white placeholder:text-primary-300`
|
||
- Hover:`hover:bg-primary-700`
|
||
- Focus:`focus:bg-white focus:text-neutral-700 focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20`
|
||
|
||
#### 5.2.4 右区工具
|
||
|
||
| 元素 | 图标 | 样式 |
|
||
|---|---|---|
|
||
| 消息通知 | `BellIcon` 20px + 红点 Badge | `p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md` |
|
||
| 设置 | `Cog6ToothIcon` 20px | 同上 |
|
||
| 头像菜单 | `w-7 h-7 rounded-full bg-primary-600` + 姓名 | 头像缩写 + `text-sm font-medium text-primary-100`,左侧 `border-l border-primary-700` 分隔 |
|
||
|
||
#### 5.2.5 Topbar HTML 片段
|
||
|
||
```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>
|
||
|
||
<!-- 中区:主导航 + 搜索 -->
|
||
<div class="flex items-center gap-4 flex-1 px-2">
|
||
<nav class="flex items-center gap-1 text-sm" aria-label="主导航">
|
||
{% for item in nav_items %}
|
||
<a href="{{ item.url }}"
|
||
class="px-3 py-1.5 rounded-md
|
||
{% if item.active %}bg-primary-600 text-white font-medium
|
||
{% else %}text-primary-100 hover:bg-primary-700 hover:text-white{% endif %}">
|
||
{{ item.label }}
|
||
</a>
|
||
{% endfor %}
|
||
</nav>
|
||
<!-- 全局搜索 -->
|
||
<div class="max-w-xs w-full relative">
|
||
<svg class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-primary-300" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-4.3-4.3M11 19a8 8 0 1 1 0-16 8 8 0 0 1 0 16Z"/>
|
||
</svg>
|
||
<input type="text" placeholder="搜索房源 / 客户 / 楼盘 ⌘K"
|
||
class="w-full pl-9 pr-3 py-1.5 text-sm rounded-md
|
||
bg-primary-700/60 border border-transparent text-white placeholder:text-primary-300
|
||
hover:bg-primary-700
|
||
focus:bg-white focus:text-neutral-700 focus:border-primary-600
|
||
focus:ring-2 focus:ring-primary-600/20 focus:outline-none
|
||
focus:placeholder:text-neutral-400">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右区:工具 -->
|
||
<div class="flex items-center gap-1 px-4 shrink-0">
|
||
<!-- 消息通知 -->
|
||
<button class="relative p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md" aria-label="消息通知">
|
||
<svg class="w-5 h-5"><!-- BellIcon --></svg>
|
||
<span class="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-danger-600"></span>
|
||
</button>
|
||
<!-- 设置 -->
|
||
<button class="p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md" aria-label="设置">
|
||
<svg class="w-5 h-5"><!-- Cog6ToothIcon --></svg>
|
||
</button>
|
||
<!-- 头像 + 姓名 -->
|
||
<div class="flex items-center gap-2 pl-3 ml-1 border-l border-primary-700">
|
||
<div class="w-7 h-7 rounded-full bg-primary-600 text-white flex items-center justify-center text-xs font-semibold">WS</div>
|
||
<span class="text-sm font-medium text-primary-100">王顺</span>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ [🏢 Fonrey ▾] 主页 房源 客源 营销 交易 数据 人事 系统 │
|
||
│ [🔍] [🔔2] [?] [WS 王顺▾] │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| 区域 | 内容 | 样式 |
|
||
|---|---|---|
|
||
| 左区(150px) | Logo 图 + 产品名"Fonrey" | `flex items-center gap-2 px-4 text-base font-semibold text-white` |
|
||
| 中区(flex-1) | 主分类导航 Tab | 见 5.2.2 |
|
||
| 右区(auto) | 全局搜索图标 / 消息 / 帮助 / 头像菜单 | `flex items-center gap-1 px-4` |
|
||
|
||
#### 5.2.2 主导航 Tab
|
||
|
||
主分类(8项):**主页 / 房源 / 客源 / 营销 / 交易 / 数据 / 人事 / 系统**
|
||
|
||
- 每项:`px-3 py-1.5 text-sm font-medium rounded-md`
|
||
- 默认态:`text-primary-100 hover:bg-primary-700 hover:text-white`
|
||
- 激活态:`bg-primary-600 text-white`
|
||
- 点击切换主分类 → Sidebar 同步切换为该分类的子菜单
|
||
|
||
#### 5.2.3 右区工具
|
||
|
||
| 元素 | 图标 | 说明 |
|
||
|---|---|---|
|
||
| 全局搜索 | `MagnifyingGlassIcon` 20px | 点击展开全局搜索 Popover(`max-w-lg`) |
|
||
| 消息通知 | `BellIcon` 20px + 红点 Badge | 未读数 ≤ 99,超出显示"99+";点击打开通知 Drawer |
|
||
| 帮助 | `QuestionMarkCircleIcon` 20px | 链接到帮助中心或触发 Tour |
|
||
| 头像菜单 | 头像(`w-8 h-8 rounded-full`)+ 姓名缩写 | 下拉菜单:个人设置 / 切换角色 / 退出 |
|
||
|
||
> **配色说明**:Topbar 背景使用 `bg-primary-800`(`#134E4A`,深青绿),与下方白色 Sidebar 和 `bg-neutral-50` 内容区形成明确层次区分,同时保持品牌色系一致性。
|
||
|
||
#### 5.2.4 Topbar HTML 片段
|
||
|
||
```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 class="flex items-center gap-0.5 flex-1 px-2" aria-label="主导航">
|
||
{% for item in nav_items %}
|
||
<a href="{{ item.url }}"
|
||
class="px-3 py-1.5 text-sm font-medium rounded-md
|
||
{% if item.active %}bg-primary-600 text-white
|
||
{% else %}text-primary-100 hover:bg-primary-700 hover:text-white{% endif %}">
|
||
{{ item.label }}
|
||
</a>
|
||
{% endfor %}
|
||
</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="搜索">
|
||
<svg class="w-5 h-5"><!-- MagnifyingGlassIcon --></svg>
|
||
</button>
|
||
|
||
<!-- 消息通知 -->
|
||
<button class="relative p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md"
|
||
aria-label="消息通知">
|
||
<svg class="w-5 h-5"><!-- BellIcon --></svg>
|
||
<span class="absolute top-1 right-1 min-w-[16px] h-4 px-1 text-[10px] font-bold
|
||
bg-danger-600 text-white rounded-full flex items-center justify-center
|
||
leading-none">2</span>
|
||
</button>
|
||
|
||
<!-- 帮助 -->
|
||
<button class="p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md" aria-label="帮助">
|
||
<svg class="w-5 h-5"><!-- QuestionMarkCircleIcon --></svg>
|
||
</button>
|
||
|
||
<!-- 头像菜单 -->
|
||
<div x-data="{ open: false }" @click.away="open=false" class="relative ml-1 pl-3 border-l border-primary-700">
|
||
<button @click="open=!open"
|
||
class="flex items-center gap-2 p-1 rounded-md hover:bg-primary-700">
|
||
<span class="w-8 h-8 rounded-full bg-primary-600 text-white
|
||
flex items-center justify-center text-sm font-semibold">
|
||
王
|
||
</span>
|
||
<span class="text-sm text-primary-100 font-medium">王顺</span>
|
||
<svg class="w-4 h-4 text-primary-300"><!-- ChevronDownIcon --></svg>
|
||
</button>
|
||
<div x-show="open" x-transition
|
||
class="absolute right-0 mt-1 w-44 bg-white rounded-md shadow-md
|
||
border border-neutral-200 py-1 z-40">
|
||
<a href="/profile/" class="flex items-center gap-2 px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-50">
|
||
<svg class="w-4 h-4 text-neutral-400"><!-- UserIcon --></svg> 个人设置
|
||
</a>
|
||
<div class="my-1 border-t border-neutral-100"></div>
|
||
<a href="/logout/" class="flex items-center gap-2 px-3 py-2 text-sm text-danger-600 hover:bg-danger-50">
|
||
<svg class="w-4 h-4"><!-- ArrowRightOnRectangleIcon --></svg> 退出登录
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
```
|
||
|
||
---
|
||
|
||
### 5.3 侧边栏(Sidebar)
|
||
|
||
#### 5.3.1 结构与折叠规范
|
||
|
||
```
|
||
展开态(240px) 折叠态(64px)
|
||
┌─────────────────────────┐ ┌──────┐
|
||
│ ▾ 房源管理 │ │ 🏘️ │ ← 一级图标,Hover Tooltip
|
||
│ 全部房源 ● │ │──────│
|
||
│ 二手 & 租赁 │ │ 👤 │
|
||
│ 商铺 / 写字楼 │ │ 🏢 │
|
||
│─────────────────────────│ │ ⚙️ │
|
||
│ 客源管理 │ └──────┘
|
||
│ 私客管理 ● │
|
||
│ 公客池 │
|
||
│─────────────────────────│
|
||
│ [← 折叠] │ ← 折叠按钮(固定在 Sidebar 底部)
|
||
└─────────────────────────┘
|
||
```
|
||
|
||
- **Sidebar 定位**:`fixed left-0 top-14 h-[calc(100vh-56px)] z-20 overflow-y-auto`
|
||
- **展开宽度**:`w-60`(240px);折叠宽度:`w-16`(64px)
|
||
- **Alpine.js 状态**:`x-data="{ collapsed: $persist(false).as('fonrey:sidebar:collapsed') }"`(使用 `@alpinejs/persist` 持久化)
|
||
- **内容区联动**:`<main :class="collapsed ? 'ml-16' : 'ml-60'" class="transition-[margin] duration-200">`
|
||
|
||
#### 5.3.2 菜单层级
|
||
|
||
Sidebar 展示**当前主分类的子菜单**,分为一级菜单和二级菜单(可选)。
|
||
|
||
| 层级 | 样式 | 激活态 |
|
||
|---|---|---|
|
||
| 一级菜单(有子项) | `flex items-center gap-3 px-3 py-2 text-sm font-medium text-neutral-700 rounded-md hover:bg-neutral-100 cursor-pointer` | `text-neutral-900 bg-neutral-100` |
|
||
| 一级菜单(无子项,直链) | 同上 | `bg-primary-50 text-primary-700 font-semibold border-l-2 border-primary-600 rounded-l-none` |
|
||
| 二级菜单 | `ml-8 flex items-center gap-2 px-3 py-1.5 text-sm text-neutral-600 rounded-md hover:bg-neutral-100` | `bg-primary-50 text-primary-700 font-medium border-l-2 border-primary-600 rounded-l-none` |
|
||
| 分组标题(可选) | `px-3 pt-4 pb-1 text-xs font-semibold text-neutral-400 uppercase tracking-wider` | — |
|
||
|
||
#### 5.3.3 折叠态行为
|
||
|
||
- 仅显示一级菜单图标(`w-5 h-5`),居中 `justify-center`
|
||
- 文字、箭头、二级菜单全部隐藏(`x-show="!collapsed"`)
|
||
- Hover 图标时,右侧弹出 Tooltip:菜单名称(`absolute left-16 ml-1 px-2 py-1 text-xs bg-neutral-900 text-white rounded whitespace-nowrap z-40`)
|
||
- 折叠按钮变为展开图标(`ChevronRightIcon`),固定在底部
|
||
|
||
#### 5.3.4 Sidebar HTML 片段
|
||
|
||
```html
|
||
<aside x-data="{ collapsed: $persist(false).as('fonrey:sidebar:collapsed'),
|
||
openGroup: $persist('property').as('fonrey:sidebar:openGroup') }"
|
||
:class="collapsed ? 'w-16' : 'w-60'"
|
||
class="fixed left-0 top-14 h-[calc(100vh-56px)] z-20
|
||
bg-white border-r border-neutral-200
|
||
flex flex-col overflow-y-auto overflow-x-hidden
|
||
transition-[width] duration-200">
|
||
|
||
<!-- 菜单主体 -->
|
||
<nav class="flex-1 py-3 space-y-0.5 px-2">
|
||
|
||
<!-- 一级菜单(有子菜单,可折叠) -->
|
||
<div>
|
||
<button @click="openGroup = (openGroup === 'property' ? '' : 'property')"
|
||
class="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium
|
||
text-neutral-700 rounded-md hover:bg-neutral-100
|
||
relative group">
|
||
<svg class="w-5 h-5 text-neutral-500 shrink-0"><!-- HomeModernIcon --></svg>
|
||
<span x-show="!collapsed" class="flex-1 text-left">房源管理</span>
|
||
<svg x-show="!collapsed"
|
||
:class="openGroup === 'property' ? 'rotate-90' : ''"
|
||
class="w-4 h-4 text-neutral-400 transition-transform shrink-0"><!-- ChevronRightIcon --></svg>
|
||
<!-- 折叠态 Tooltip -->
|
||
<span x-show="collapsed"
|
||
class="pointer-events-none absolute left-14 ml-1 px-2 py-1 text-xs
|
||
bg-neutral-900 text-white rounded whitespace-nowrap z-40
|
||
opacity-0 group-hover:opacity-100 transition-opacity">
|
||
房源管理
|
||
</span>
|
||
</button>
|
||
|
||
<!-- 二级菜单 -->
|
||
<div x-show="!collapsed && openGroup === 'property'" x-collapse class="mt-0.5 space-y-0.5">
|
||
<a href="/property/all/"
|
||
class="ml-8 flex items-center gap-2 px-3 py-1.5 text-sm text-neutral-600
|
||
rounded-r-md hover:bg-neutral-100
|
||
{% if active_sub == 'all' %}bg-primary-50 text-primary-700 font-medium
|
||
border-l-2 border-primary-600{% endif %}">
|
||
全部房源
|
||
</a>
|
||
<a href="/property/sale/"
|
||
class="ml-8 flex items-center gap-2 px-3 py-1.5 text-sm text-neutral-600
|
||
rounded-r-md hover:bg-neutral-100">
|
||
二手 & 租赁
|
||
</a>
|
||
<a href="/property/commercial/"
|
||
class="ml-8 flex items-center gap-2 px-3 py-1.5 text-sm text-neutral-600
|
||
rounded-r-md hover:bg-neutral-100">
|
||
商铺 / 写字楼
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 一级菜单(直链,无子菜单) -->
|
||
<a href="/complex/"
|
||
class="flex items-center gap-3 px-3 py-2 text-sm font-medium
|
||
text-neutral-700 rounded-md hover:bg-neutral-100
|
||
relative group
|
||
{% if active_nav == 'complex' %}bg-primary-50 text-primary-700 border-l-2 border-primary-600 rounded-l-none{% endif %}">
|
||
<svg class="w-5 h-5 text-neutral-500 shrink-0"><!-- BuildingOffice2Icon --></svg>
|
||
<span x-show="!collapsed">楼盘管理</span>
|
||
<span x-show="collapsed"
|
||
class="pointer-events-none absolute left-14 ml-1 px-2 py-1 text-xs
|
||
bg-neutral-900 text-white rounded whitespace-nowrap z-40
|
||
opacity-0 group-hover:opacity-100 transition-opacity">
|
||
楼盘管理
|
||
</span>
|
||
</a>
|
||
|
||
</nav>
|
||
|
||
<!-- 折叠按钮 -->
|
||
<div class="border-t border-neutral-200 p-2">
|
||
<button @click="collapsed = !collapsed"
|
||
class="w-full flex items-center justify-center gap-2 px-3 py-2 text-sm
|
||
text-neutral-500 hover:bg-neutral-100 rounded-md">
|
||
<svg :class="collapsed ? 'rotate-180' : ''"
|
||
class="w-4 h-4 transition-transform shrink-0"><!-- ChevronLeftIcon --></svg>
|
||
<span x-show="!collapsed" class="text-xs">收起</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 内容区偏移联动(写在 body 或 layout wrapper 层) -->
|
||
<main :class="collapsed ? 'ml-16' : 'ml-60'"
|
||
class="mt-14 transition-[margin] duration-200 min-h-[calc(100vh-56px)] bg-neutral-50">
|
||
{% block content %}{% endblock %}
|
||
</main>
|
||
```
|
||
|
||
---
|
||
|
||
### 5.4 列表页模板
|
||
|
||
适用:房源列表、客源列表、楼盘列表、权限人员列表、组织人员列表等。
|
||
|
||
```
|
||
面包屑 > 房源管理 > 二手 & 租赁 [导出▾] [+ 新增房源]
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
[Tab: 全部 (3629) | 在售 | 跟进中 | 已成交 | 已下架]
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
[筛选栏:区域 / 价格 / 户型 / 状态 / 经纪人 | 高级筛选(3) | 搜索]
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
[批量工具栏(选中时出现):已选 N 条 | 分享 | 收藏 | 下架 | 删除]
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
[视图切换: 表格 | 卡片] [密度: 紧凑] [自定义列▾]
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
表格主体(HTMX 局部刷新)
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
分页栏(共 3629 条 · 1 2 3 … · 20/页 · 跳至)
|
||
```
|
||
|
||
### 5.5 详情页模板
|
||
|
||
适用:房源详情、客源详情、楼盘详情、员工详情。
|
||
|
||
```
|
||
面包屑 > 房源 > 浦东张江花园 89㎡
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
[房源标题] [在售] [钥匙房] [收藏] [分享] [⋯ 更多]
|
||
浦东新区 · 张江高科路 · 89㎡ · 2室1厅 · ¥580 万
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
[Tab: 基本信息 | 跟进记录(12) | 相册(8) | 附件(3) | 操作日志]
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
主内容(Tab 内容,HTMX 懒加载)
|
||
|
||
┌───────────────────────┐ ┌────────────────────┐
|
||
│ 房源信息(含编辑链接)│ │ 维护完成度 69% │
|
||
│ │ │ [Accordion 进度] │
|
||
│ 产证信息(折叠) │ │ │
|
||
│ 房屋介绍 │ │ 相关员工(折叠) │
|
||
└───────────────────────┘ └────────────────────┘
|
||
```
|
||
|
||
- 详情页头部 Sticky(便于长页面滚动时保持操作按钮可见)
|
||
- Tab 计数 Badge 实时更新
|
||
- 右侧栏(`w-80`)用于维护度、相关员工等辅助信息;主区 `flex-1`
|
||
- **编辑入口 = 详情字段旁的"编辑"链接 → 右侧 Drawer 滑入**(遵循"保留上下文"原则)
|
||
|
||
### 5.6 设置页模板
|
||
|
||
适用:系统设置、权限管理、个人设置。
|
||
|
||
```
|
||
面包屑 > 系统设置 > 房源设置 > 字段标签设置
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
┌──────────────────┬────────────────────────────┐
|
||
│ 左侧分组导航 │ 右侧内容区 │
|
||
│ (240px) │ │
|
||
│ │ [搜索设置项…] [编辑] │
|
||
│ ▾ 房源设置 │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
||
│ 新增编辑查看 │ 员工信息设置 │
|
||
│ 字段标签设置 ● │ ┃ 个人信息 │
|
||
│ 相关方设置 │ ┃ 工龄计算方式: [...] │
|
||
│ ▾ 客源设置 │ ┃ 自动工号: [Toggle] │
|
||
│ ▾ 人事OA设置 │ │
|
||
│ │ [保存变更](Sticky 底部) │
|
||
└──────────────────┴────────────────────────────┘
|
||
```
|
||
|
||
- **Read/Edit 模式切换**:右上角"编辑"按钮 → 全页切换到编辑态(Alpine.js 全局 `editing` 状态 + 数据快照以便"取消"还原)
|
||
- 左侧导航支持二级折叠,激活项左侧 2px 主色竖条
|
||
- 底部保存按钮 Sticky(编辑态)
|
||
|
||
### 5.7 登录/认证页
|
||
|
||
独立布局,不使用 Sidebar。居中卡片 `max-w-md`,品牌色背景装饰(左侧 logo + slogan,右侧表单),参考 Salesforce Lightning 登录页密度。
|
||
|
||
### 5.8 空状态页(Empty Page)
|
||
|
||
见 6.3。
|
||
|
||
### 5.9 错误页(403/404/500)
|
||
|
||
三视图共享同一模板,仅图案与文案不同。`max-w-md mx-auto text-center` + SVG 插图 + 主按钮"返回首页"+ 次按钮"联系管理员"。
|
||
|
||
---
|
||
|
||
## 6. 交互状态规范(Interaction States)
|
||
|
||
### 6.1 HTMX 请求生命周期
|
||
|
||
| 阶段 | 事件 | 视觉反馈 |
|
||
|---|---|---|
|
||
| 发送前 | `htmx:beforeRequest` | 目标区域 Skeleton / 按钮进入 Loading 态 / 触发元素禁用 |
|
||
| 进行中 | `htmx:beforeOnLoad` | 保持 Loading |
|
||
| 成功 | `htmx:afterSettle` | 移除骨架;如响应头含 `HX-Trigger: fonrey:toast` 则显示 Toast |
|
||
| 422(校验失败) | 返回 Form Partial | 字段级红色提示 |
|
||
| 其他失败 | `htmx:responseError` | 保留原内容 + 全局 Error Toast |
|
||
| 网络超时 | `htmx:timeout` | 保留原内容 + "网络异常,请重试" Toast |
|
||
|
||
### 6.2 表单校验反馈
|
||
|
||
- **实时校验(Alpine.js)**:`blur` 时触发,不阻断提交
|
||
- **提交时校验(后端)**:后端返回 HTTP 422 + 包含错误 Partial;字段下方红色小字提示
|
||
- **错误提示位置**:字段下方,不使用顶部汇总(经纪人字段多,滚动回找浪费时间)
|
||
- **提交成功**:Toast + 可选页面跳转(如新增后跳详情)
|
||
|
||
### 6.3 空状态设计
|
||
|
||
| 场景 | 标题 | 描述 | 引导操作 |
|
||
|---|---|---|---|
|
||
| 列表无数据 | 暂无房源 | 可以录入首条房源开始管理 | `+ 新增房源`(Primary) |
|
||
| 搜索无结果 | 未找到匹配结果 | 请尝试调整筛选条件 | `清除筛选条件`(Link) |
|
||
| 权限不足 | 暂无访问权限 | 请联系管理员申请权限 | `联系管理员`(Secondary) |
|
||
| 功能关闭 | 功能未启用 | 请前往系统设置开启 | `前往设置`(Link,含权限判断) |
|
||
|
||
结构:
|
||
|
||
```html
|
||
<div class="py-16 px-6 text-center">
|
||
<img src="/static/img/empty-list.svg" class="w-40 mx-auto mb-4" alt="">
|
||
<h3 class="text-base font-semibold text-neutral-800">暂无房源</h3>
|
||
<p class="text-sm text-neutral-500 mt-1">可以录入首条房源开始管理</p>
|
||
<button class="mt-4 inline-flex items-center gap-1.5 px-4 py-2 text-sm
|
||
bg-primary-600 text-white rounded-md hover:bg-primary-700">
|
||
<svg class="w-4 h-4"><!-- PlusIcon --></svg> 新增房源
|
||
</button>
|
||
</div>
|
||
```
|
||
|
||
### 6.4 键盘快捷键基线
|
||
|
||
| 快捷键 | 动作 |
|
||
|---|---|
|
||
| `Ctrl/Cmd + K` | 打开全局搜索 |
|
||
| `Esc` | 关闭当前 Modal / Drawer |
|
||
| `Enter` | 提交当前表单 / 确认 Modal(非 Textarea 场景) |
|
||
| `Tab / Shift+Tab` | 表单字段间跳转 |
|
||
| `/` | 聚焦当前页面主搜索框 |
|
||
| `N`(列表页) | 打开"新增"入口 |
|
||
|
||
---
|
||
|
||
## 7. 图标规范(Icon Guidelines)
|
||
|
||
### 7.1 图标库
|
||
|
||
**Heroicons v2**(Tailwind 官方出品)
|
||
- **Outline 24px**:默认选择(工具栏、导航、行内)
|
||
- **Solid 20px**:用于状态指示、已选中态、强调
|
||
- **Mini 16px(solid)**:极密场景(表格行内、Tag 内)
|
||
- 所有图标**继承文字颜色**(`currentColor`),不单独设置 `fill`
|
||
|
||
引入方式:Django 模板内联 SVG(通过自定义 templatetag `{% heroicon 'plus' %}`),避免远程请求,可利用 CDN 缓存。
|
||
|
||
### 7.2 尺寸规范
|
||
|
||
| 尺寸 | 场景 | Tailwind |
|
||
|---|---|---|
|
||
| 24px | 侧边栏一级菜单 | `w-6 h-6` |
|
||
| 20px | 顶部栏、Stat Card、Toast | `w-5 h-5` |
|
||
| 16px | 按钮内、Tab、行内图标 | `w-4 h-4` |
|
||
| 12px | Tag 内、状态点 | `w-3 h-3` |
|
||
|
||
### 7.3 核心图标映射
|
||
|
||
| 业务动作 | Heroicon |
|
||
|---|---|
|
||
| 新增 | `plus` |
|
||
| 编辑 | `pencil-square` |
|
||
| 删除 | `trash` |
|
||
| 搜索 | `magnifying-glass` |
|
||
| 筛选 | `funnel` |
|
||
| 排序 | `chevron-up-down` |
|
||
| 导出 | `arrow-down-tray` |
|
||
| 导入 | `arrow-up-tray` |
|
||
| 更多 | `ellipsis-horizontal` |
|
||
| 关闭 | `x-mark` |
|
||
| 返回 | `arrow-left` |
|
||
| 刷新 | `arrow-path` |
|
||
| 收藏 | `star`(solid = 已收藏) |
|
||
| 分享 | `share` |
|
||
| 通知 | `bell` |
|
||
| 帮助 | `question-mark-circle` |
|
||
| 用户 | `user` |
|
||
| 组织 | `users` |
|
||
| 房源 | `home`(首页为 `squares-2x2`) |
|
||
| 客源 | `user-group` |
|
||
| 楼盘 | `building-office-2` |
|
||
| 权限 | `lock-closed` |
|
||
| 设置 | `cog-6-tooth` |
|
||
| 日历 | `calendar` |
|
||
| 电话 | `phone` |
|
||
| 钥匙 | `key` |
|
||
| 附件 | `paper-clip` |
|
||
| 图片 | `photo` |
|
||
| 视频 | `video-camera` |
|
||
| 下载 | `arrow-down-tray` |
|
||
| 成功 | `check-circle` |
|
||
| 警告 | `exclamation-triangle` |
|
||
| 错误 | `x-circle` |
|
||
| 信息 | `information-circle` |
|
||
|
||
**一致性铁律**:同一业务动作在全产品使用**完全相同**的图标。新增业务动作需登记本映射表。
|
||
|
||
---
|
||
|
||
## 8. 可访问性基线(Accessibility Baseline)
|
||
|
||
- 所有表单字段必须关联 `<label>`(`for` 属性或包裹)
|
||
- 颜色对比度达 WCAG AA 级(正文 4.5:1,大文字 3:1);已验证主色 `#0F766E` 与白色对比度 5.7:1 ✓
|
||
- 所有交互元素支持键盘:`Tab` 聚焦、`Enter/Space` 触发、`ESC` 关闭浮层
|
||
- 焦点可见:统一使用 `focus-visible:ring-2 focus-visible:ring-primary-600/40`
|
||
- 错误态不仅靠颜色:必须附带文字提示
|
||
- 纯图标按钮必须有 `aria-label` 或 `<span class="sr-only">`
|
||
- 图片必须有 `alt` 属性;装饰性图片用 `alt=""` + `aria-hidden="true"`
|
||
- 图标 SVG 用 `aria-hidden="true"`(除非是唯一语义载体)
|
||
- 表格使用语义化 `<th scope="col">`
|
||
|
||
---
|
||
|
||
## 9. 已决策事项(Resolved Decisions)
|
||
|
||
v1.0 遗留的 6 个问题,已于 v1.1 评审决策如下。
|
||
|
||
| # | 问题 | 决策 | 实施要点 |
|
||
|---|---|---|---|
|
||
| 1 | 是否支持暗色主题 | **v1 不做,开发预留接口** | 所有颜色走 Tailwind Token(不硬编码 Hex);根标签预留 `data-theme="light"` 属性;CSS 变量层接入待 v2 |
|
||
| 2 | 全局搜索(⌘K)覆盖范围 | **房源 + 客源 + 楼盘 + 同事 四类** | Command Palette 分组展示;↑↓ 切换、Enter 跳转、ESC 关闭;后端接口 `/api/search/?q=&types=property,customer,building,user` |
|
||
| 3 | 表格列是否支持拖拽排序 | **支持:拖拽 + 显隐 + localStorage 记忆** | SortableJS;"列设置"Drawer 右侧打开;状态 key `fonrey:table:{module}:cols`,结构 `[{key, visible, order}]` |
|
||
| 4 | 已成交状态色 | **info 蓝(`#2563EB`)** | 仅用于状态 Tag,不扩散到其他元素;与 success 绿(在售)、warning 橙(待核验)、neutral 灰(已下架)形成完整状态色阶 |
|
||
| 5 | 屏幕 <1280px 降级 | **显示引导提示页,锁定主内容** | 全屏 Splash:品牌 logo + "Fonrey 为桌面端设计,请放大浏览器窗口或使用 ≥1280px 屏幕";JS 监听 resize 自动显隐;不做响应式适配 |
|
||
| 6 | 国际化 | **v1 仅中文,文案硬编码** | 但 Token 层保持中性(不嵌入"万""㎡"等单位到 Token 名);字体栈保留 Inter + PingFang SC 双语准备;v2 接入 Django i18n 时无需重构组件 |
|
||
|
||
### 9.1 小屏降级提示页(规范)
|
||
|
||
```html
|
||
<!-- 监听 window.innerWidth < 1280 时插入,id="screen-gate" -->
|
||
<div id="screen-gate"
|
||
class="fixed inset-0 z-[100] bg-white flex flex-col items-center justify-center px-8 text-center">
|
||
<div class="w-16 h-16 rounded-xl bg-primary-600 text-white flex items-center justify-center text-2xl font-semibold mb-6">F</div>
|
||
<h1 class="text-xl font-semibold text-neutral-800 mb-2">请使用桌面端访问 Fonrey</h1>
|
||
<p class="text-sm text-neutral-600 max-w-md mb-6">
|
||
Fonrey 为桌面工作场景设计,建议屏幕宽度 ≥ 1280px。
|
||
请放大浏览器窗口,或切换到电脑端访问。
|
||
</p>
|
||
<p class="text-xs text-neutral-400">当前窗口:<span id="screen-gate-width" class="tabular-nums"></span> px</p>
|
||
</div>
|
||
```
|
||
|
||
- 触发阈值:`window.innerWidth < 1280`
|
||
- 不阻止登录、不阻止 Token 校验,仅遮罩 UI
|
||
- 移动端浏览器同样展示此页(不引导下载 App,v1 无 App)
|
||
|
||
### 9.2 全局搜索(⌘K)数据结构约定
|
||
|
||
```js
|
||
// 返回示例
|
||
{
|
||
"property": [{id, code, title, status, subtitle}], // 房源:F编号 + 标题 + 商圈
|
||
"customer": [{id, name, phone_mask, tag, subtitle}], // 客源:姓名 + 脱敏手机 + 意向
|
||
"building": [{id, name, district, unit_price}], // 楼盘:名称 + 商圈 + 均价
|
||
"user": [{id, name, dept, avatar_char}] // 同事:姓名 + 部门
|
||
}
|
||
```
|
||
|
||
- 分组固定顺序:房源 → 客源 → 楼盘 → 同事
|
||
- 每组最多 5 条,更多结果跳对应模块列表页
|
||
- 空查询时展示"最近访问"(前 8 条,不分组)
|
||
|
||
---
|
||
|
||
|
||
## 10. 附录(Appendix)
|
||
|
||
### 10.1 完整 Tailwind 配置文件
|
||
|
||
```js
|
||
// tailwind.config.js
|
||
/** @type {import('tailwindcss').Config} */
|
||
module.exports = {
|
||
content: [
|
||
'./apps/**/templates/**/*.html',
|
||
'./templates/**/*.html',
|
||
'./apps/**/*.py', // 若使用 django-components
|
||
],
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
primary: {
|
||
50: '#F0FDFA',
|
||
100: '#CCFBF1',
|
||
200: '#99F6E4',
|
||
300: '#5EEAD4',
|
||
400: '#2DD4BF',
|
||
500: '#14B8A6',
|
||
600: '#0F766E', // 主基准
|
||
700: '#115E59',
|
||
800: '#134E4A',
|
||
900: '#042F2E',
|
||
},
|
||
neutral: {
|
||
50: '#F8FAFC',
|
||
100: '#F1F5F9',
|
||
200: '#E2E8F0',
|
||
300: '#CBD5E1',
|
||
400: '#94A3B8',
|
||
500: '#64748B',
|
||
600: '#475569',
|
||
700: '#334155',
|
||
800: '#1E293B',
|
||
900: '#0F172A',
|
||
},
|
||
success: { 50: '#F0FDF4', 600: '#16A34A', 700: '#15803D' },
|
||
warning: { 50: '#FFFBEB', 600: '#D97706', 700: '#B45309' },
|
||
danger: { 50: '#FEF2F2', 600: '#DC2626', 700: '#B91C1C' },
|
||
info: { 50: '#EFF6FF', 600: '#2563EB', 700: '#1D4ED8' },
|
||
},
|
||
fontFamily: {
|
||
sans: ['Inter', '"PingFang SC"', '"Microsoft YaHei"', 'sans-serif'],
|
||
mono: ['"JetBrains Mono"', '"SFMono-Regular"', 'Menlo', 'monospace'],
|
||
},
|
||
boxShadow: {
|
||
xs: '0 1px 2px rgba(15, 23, 42, 0.04)',
|
||
},
|
||
zIndex: {
|
||
60: '60',
|
||
70: '70',
|
||
},
|
||
animation: {
|
||
'slide-in-right': 'slideInRight 0.2s ease-out',
|
||
},
|
||
keyframes: {
|
||
slideInRight: {
|
||
'0%': { transform: 'translateX(100%)' },
|
||
'100%': { transform: 'translateX(0)' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
plugins: [
|
||
require('@tailwindcss/forms'),
|
||
require('@tailwindcss/typography'),
|
||
],
|
||
}
|
||
```
|
||
|
||
### 10.2 第三方库清单(均为无框架依赖)
|
||
|
||
| 库 | 用途 | 体积 | 引入方式 |
|
||
|---|---|---|---|
|
||
| HTMX | 服务端驱动交互 | ~14KB | CDN |
|
||
| Alpine.js + `@alpinejs/collapse` + `@alpinejs/focus` | 前端状态 | ~18KB | CDN |
|
||
| Heroicons | 图标 | 按需内联 | npm/静态文件 |
|
||
| Flatpickr | 日期范围选择 | ~16KB | CDN |
|
||
| SortableJS | 拖拽排序(图片、表格行) | ~3KB | CDN |
|
||
| Filepond | 图片多文件上传 | ~50KB | CDN |
|
||
| Viewer.js | 图片灯箱预览 | ~5KB | CDN |
|
||
|
||
### 10.3 Flatpickr 样式覆盖(配合主色)
|
||
|
||
```css
|
||
/* static/css/flatpickr-overrides.css */
|
||
.flatpickr-day.selected,
|
||
.flatpickr-day.startRange,
|
||
.flatpickr-day.endRange { background: #0F766E; border-color: #0F766E; }
|
||
.flatpickr-day.inRange { background: #CCFBF1; border-color: #CCFBF1; color: #115E59; }
|
||
.flatpickr-day.today { border-color: #D97706; }
|
||
```
|
||
|
||
### 10.4 关联文档
|
||
|
||
- `Project/fonrey/TECH_STACK/TECH_STACK.md` —— 技术总纲
|
||
- `Project/fonrey/UI&UX/组件清单.md` —— 组件可行性与实现建议(本文的工程侧补充)
|
||
- `Project/fonrey/PRD/*` —— 各业务模块产品需求
|
||
- `Project/fonrey/screenshots/*` —— 竞品截图(设计参考)
|
||
|
||
### 10.5 变更记录
|
||
|
||
| 版本 | 日期 | 变更 | 作者 |
|
||
|---|---|---|---|
|
||
| v1.0 | 2026-04-25 | 首次建立 UI System:Token / 基础组件 15 项 / 业务组件 9 项 / 5 类页面模板 | UI/UX 架构组 |
|