name: prototype-design
description: |
通用頁面原型設計技能。基於 58種設計風格(預設Figma 風格) UI 的複雜業務系統 HTML 原型開發,
包括標準頁面結構、統計卡片、篩選條件、資料表格、彈窗設計。
適用場景:(1) 建立新的管理頁面原型 (2) 對照業務文件實現功能模組
(3) 設計彈窗和詳情頁 (4) 構建帶看板和列表檢視的頁面。
樣式選擇:內建58+設計系統,直接引用 references/design-systems/ 目錄。
使用時指定 DESIGN_SYSTEM=<名稱> 即可應用對應設計風格。
origin: openclaw
last_updated: 2026-04-14
changelog:
- 2026-04-14: 初始版本,從 WMS 專案經驗中沉澱
- 2026-04-14: 新增長對話上下文管理章節
- 2026-04-14: 新增 WMS 專案實戰經驗(10條核心教訓)
7w4.net有更好的技能外掛。
mkdir -p project/{pages,styles,scripts}
cd project
內建設計系統(references/design-systems/):
- 預設:figma-DESIGN.md
- 管理後臺:linear-DESIGN.md
- 簡約專業:vercel-DESIGN.md
通用頁面原型開發指南,支援58+設計系統風格。
project/
├── index.html # 單頁應用入口
├── pages/ # 頁面模組
│ ├── dashboard.html
│ ├── staff.html
│ └── ...
├── styles/
│ └── main.css # 全域性樣式
└── scripts/
└── main.js # 全域性指令碼
重要:設計系統檔案位於 references/design-systems/ 目錄。
企業級:IBM、Salesforce
讀取對應 DESIGN.md
bash
cat references/design-systems/figma-DESIGN.md
應用設計規範
| 風格 | 設計系統 | 特點 |
|---|---|---|
| 預設 | figma-DESIGN.md |
多彩色、現代 |
| 管理後臺 | linear-DESIGN.md |
紫色主題、精緻 |
| 簡約專業 | vercel-DESIGN.md |
黑白精準、極簡 |
| 溫暖風格 | notion-DESIGN.md |
暖色極簡 |
| 企業級 | stripe-DESIGN.md |
紫色漸變、高階感 |
<div id="page-xxx" class="page">
<!-- 1. Header -->
<header class="header">
<div class="header-left">
<h2>頁面標題</h2>
<div class="breadcrumb"><span>首頁</span><span>/</span><span>當前路徑</span></div>
</div>
<div class="header-right">
<button class="btn btn-outline btn-sm">匯出</button>
<button class="btn btn-primary btn-sm" onclick="openAddModal()">新增</button>
</div>
</header>
<!-- 2. 統計卡片 -->
<div class="stats-grid" style="grid-template-columns:repeat(4,1fr);">
<div class="stat-card">
<div class="stat-card-title">標題</div>
<div class="stat-card-value">數值</div>
<div class="stat-card-change">描述</div>
</div>
</div>
<!-- 3. 篩選條件 -->
<div class="table-filters">
<div class="filter-group">
<span class="filter-label">欄位名</span>
<input type="text" class="filter-input" placeholder="提示...">
</div>
</div>
<!-- 4. 資料表格 -->
<table>
<thead><tr><th>欄位1</th><th>欄位2</th><th>操作</th></tr></thead>
<tbody>
<tr>
<td>資料</td>
<td><span class="tag tag-success">狀態</span></td>
<td><button class="btn btn-sm btn-outline">詳情</button></td>
</tr>
</tbody>
</table>
</div>
<!-- 遮罩 + 居中彈窗 -->
<div id="modal-xxx" style="
display:none;
position:fixed;
top:0;left:0;right:0;bottom:0;
background:rgba(0,0,0,0.6);
z-index:1000;
align-items:center;
justify-content:center;
">
<!-- 內容框 -->
<div style="
background:white;
border-radius:16px;
width:560px;
max-width:90vw;
max-height:85vh;
overflow:hidden;
box-shadow:0 25px 80px rgba(0,0,0,0.35);
">
<!-- 標題欄 -->
<div style="
padding:20px 24px;
border-bottom:1px solid #E5E7EB;
display:flex;
align-items:center;
justify-content:space-between;
background:#F9FAFB;
">
<h3 style="margin:0;font-size:18px;font-weight:600;color:#111827;">彈窗標題</h3>
<button onclick="closeModal()" style="
border:none;
background:none;
font-size:24px;
color:#6B7280;
cursor:pointer;
padding:4px;
line-height:1;
">×</button>
</div>
<!-- 內容區 -->
<div style="
padding:24px;
overflow-y:auto;
max-height:calc(85vh - 140px);
">
<!-- 表單項示例 -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div>
<label style="display:block;font-size:14px;font-weight:500;margin-bottom:8px;color:#374151;">
欄位名 <span style="color:#EF4444;">*</span>
</label>
<select style="
width:100%;
padding:10px 12px;
border:1px solid #E5E7EB;
border-radius:8px;
font-size:14px;
background:white;
">
<option value="">請選擇</option>
<option>選項1</option>
<option>選項2</option>
</select>
</div>
<div>
<label style="display:block;font-size:14px;font-weight:500;margin-bottom:8px;color:#374151;">欄位名</label>
<input type="text" placeholder="請輸入" style="
width:100%;
padding:10px 12px;
border:1px solid #E5E7EB;
border-radius:8px;
font-size:14px;
box-sizing:border-box;
">
</div>
</div>
<div style="margin-top:16px;">
<label style="display:block;font-size:14px;font-weight:500;margin-bottom:8px;color:#374151;">備註</label>
<textarea rows="3" placeholder="請輸入備註" style="
width:100%;
padding:10px 12px;
border:1px solid #E5E7EB;
border-radius:8px;
font-size:14px;
resize:none;
box-sizing:border-box;
"></textarea>
</div>
</div>
<!-- 底部按鈕 -->
<div style="
padding:16px 24px;
border-top:1px solid #E5E7EB;
display:flex;
justify-content:flex-end;
gap:12px;
background:#F9FAFB;
">
<button onclick="closeModal()" style="
padding:10px 20px;
border:1px solid #E5E7EB;
background:white;
border-radius:8px;
font-size:14px;
font-weight:500;
color:#374151;
cursor:pointer;
">取消</button>
<button onclick="saveData()" style="
padding:10px 20px;
border:none;
background:#4F46E5;
color:white;
border-radius:8px;
font-size:14px;
font-weight:500;
cursor:pointer;
">儲存</button>
</div>
</div>
</div>
| 元素 | 樣式屬性 | 正確值 | 常見錯誤 |
|---|---|---|---|
| 外層遮罩 | position |
fixed |
用 absolute 會滾動 |
| 遮罩背景 | background |
rgba(0,0,0,0.6) |
0.5 太淡,0.7 太濃 |
| 遮罩定位 | top/left/right/bottom |
0(全屏覆蓋) |
忘記設定任一邊 |
| 彈窗容器 | display |
flex |
父級用 flex 居中 |
| 居中方式 | align-items + justify-content |
center + center |
缺少任一屬性 |
| 內容框圓角 | border-radius |
16px |
12px 不夠現代 |
| 內容框寬度 | width |
560px 或 90vw |
固定 px 在小螢幕不友好 |
| 內容框高度 | max-height |
85vh |
80vh 可能顯示不全 |
| 陰影 | box-shadow |
0 25px 80px rgba(0,0,0,0.35) |
太淡看不出層次 |
| 元素 | 樣式屬性 | 正確值 |
|---|---|---|
| 輸入框/下拉框 | padding |
10px 12px |
| 輸入框/下拉框 | border-radius |
8px |
| 輸入框/下拉框 | border |
1px solid #E5E7EB |
| 輸入框/下拉框 | font-size |
14px |
| textarea | resize |
none |
| textarea | box-sizing |
border-box |
| 必填標記 | color | #EF4444 (紅色) |
class="modal"、class="btn btn-primary" - 這些類通常沒有定義樣式或樣式被覆蓋z-index:1000:確保在最上層overflow:hidden:內容超出需要滾動,必須用 overflow-y:auto<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:16px;">
<!-- 列 -->
<div style="background:#F9FAFB;border-radius:12px;padding:16px;">
<div style="display:flex;justify-content:space-between;padding-bottom:12px;border-bottom:1px solid #E5E7EB;">
<span>待開始</span>
<span style="background:#FEE2E2;color:#DC2626;padding:2px 8px;border-radius:10px;font-size:12px;">5</span>
</div>
<div style="background:white;border-radius:8px;padding:12px;margin-top:12px;cursor:pointer;" onclick="showDetail()">
<div style="font-weight:500;">單號</div>
<div style="font-size:12px;color:#6B7280;">描述</div>
</div>
</div>
</div>
<div class="tabs" style="margin-bottom:24px;">
<button class="tab active" onclick="switchTab('tab1')" style="background:#EEF2FF;color:#4F46E5;">Tab1</button>
<button class="tab" onclick="switchTab('tab2')" style="background:#F3F4F6;color:#6B7280;">Tab2</button>
</div>
問題現象:更新 pages/xxx.html 後,直接開啟 index.html 檢視不會看到變化。
原因:index.html 是單頁應用入口,包含所有頁面的內嵌副本。pages/ 目錄的修改不會自動同步。
常見錯誤:彈窗 HTML 放在 </div> (頁面關閉標籤) 之後
<!-- ❌ 錯誤:彈窗在頁面 div 外部 -->
<div id="page-xxx" class="page">
...頁面內容...
</div>
<!-- 彈窗在這是錯誤的 -->
<div id="modal-xxx" style="display:none...">彈窗內容</div>
<!-- ✅ 正確:彈窗必須在頁面 div 內部 -->
<div id="page-xxx" class="page">
...頁面內容...
<!-- 彈窗放在這裡 -->
<div id="modal-xxx" style="display:none...">彈窗內容</div>
</div>
常見錯誤:使用全域性選擇器 .tabs .tab 會影響所有頁面
// ❌ 錯誤:會選擇頁面上所有的 tab
const tabs = document.querySelectorAll('.tabs .tab');
// ✅ 正確:使用頁面級作用域
const tabs = document.querySelectorAll('#page-xxx .tabs > .tab');
Tab 切換函式模板:
function switchXxxTab(tabName) {
// 1. 隱藏所有 tab 內容
document.querySelectorAll('.xxx-tab').forEach(t => t.style.display = 'none');
// 2. 顯示選中的 tab 內容
document.getElementById('xxx-' + tabName).style.display = 'block';
// 3. 更新 tab 按鈕狀態(使用頁面級作用域)
const tabs = document.querySelectorAll('#page-xxx .tabs > .tab');
tabs.forEach((t, i) => {
if (i === /* 當前tab索引 */) {
t.style.background = '#EEF2FF';
t.style.color = '#4F46E5';
} else {
t.style.background = '#F3F4F6';
t.style.color = '#6B7280';
}
});
}
解決方案:每次建立/更新 pages/ 目錄的頁面後,必須執行以下同步指令碼:
cd project
python3 << 'PYEOF'
import os
import re
# 1. 讀取當前index.html
with open('index.html', 'r', encoding='utf-8') as f:
content = f.read()
# 2. 獲取pages/目錄下所有html檔案(按檔名排序)
pages_dir = 'pages'
page_files = sorted([f for f in os.listdir(pages_dir) if f.endswith('.html')])
# 3. 對每個頁面檔案,提取 <div id="page-xxx" 內容
for page_file in page_files:
with open(os.path.join(pages_dir, page_file), 'r', encoding='utf-8') as f:
page_content = f.read()
# 提取 page-xxx 塊的完整內容
match = re.search(r'(<div id="(page-\w+)"[^>]*class="page"[^>]*>.*?<script>\s*function \w+Open\w+Modal)', page_content, re.DOTALL)
if not match:
match = re.search(r'(<div id="(page-\w+)"[^>]*class="page"[^>]*>.*?)(<!--\s+<div id="page-)', page_content, re.DOTALL)
if match:
page_id = match.group(2)
page_block = match.group(1).strip()
# 在index.html中查詢並替換對應的page塊
# 匹配模式:<div id="page-xxx" class="page">...</div> 或 <div id="page-xxx" class="page">...<div id="page-yyy"
pattern = rf'(<div id="{re.escape(page_id)}"[^>]*class="page"[^>]*>)(.*?)((?=<div id="page-)|(?=<script>\s*$)|(?=</main>)|(?=</body>))'
existing = re.search(pattern, content, re.DOTALL)
if existing:
content = content[:existing.start()] + page_block + content[existing.end():]
print(f'✓ Updated: {page_id}')
else:
print(f'⚠ Not found in index: {page_id}')
else:
print(f'⚠ No page block found in: {page_file}')
# 4. 寫回index.html
with open('index.html', 'w', encoding='utf-8') as f:
f.write(content)
print('\n✅ Sync complete!')
PYEOF
重要提示:
- ❌ 不要跳過同步步驟
- ❌ 不要只更新 pages/ 目錄就以為完成了
- ✅ 每次修改後都要運行同步指令碼,再驗證 index.html
| 元件 | 類名 |
|---|---|
| 頁面容器 | .page |
| 頁頭 | .header |
| 內容區 | .content |
| 統計卡片 | .stat-card |
| 表格卡片 | .table-card |
| 按鈕-主要 | .btn .btn-primary |
| 按鈕-次要 | .btn .btn-outline |
| 標籤 | .tag |
| 狀態 | .status |
| 分頁 | .pagination |
cd project
python3 << 'PYEOF'
page_order = ['dashboard', 'page1', 'page2', ...]
pages = [open(f'pages/{p}.html').read() for p in page_order]
html = f'''<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700" rel="stylesheet">
<link rel="stylesheet" href="styles/main.css">
</head>
<body>
<div class="layout">
<aside class="sidebar">...</aside>
<main class="main">
{chr(10).join(pages)}
</main>
</div>
<script src="scripts/main.js"></script>
</body>
</html>'''
open('index.html','w').write(html)
PYEOF
| 問題 | 症狀 | 解決方案 |
|---|---|---|
| 彈窗打不開 | xxx is not defined |
檢查 main.js 引用位置和函式定義 |
| Tab 選中態錯亂 | 所有 Tab 同時選中 | 用頁面級作用域選擇器 |
| Hash URL 不工作 | 頁面 display:none |
新增 hashchange 監聽 |
| 表格侵入側邊欄 | 橫向滾動時佈局亂 | 用 Ant Design 固定表頭表格 |
| 核取方塊丟失 | 資料行沒有 checkbox | 手動新增每個資料行的核取方塊 |
| 同步後功能失效 | 彈窗/按鈕不工作 | 檢查 script 標籤格式和 div 平衡 |
當對話持續較長時,上下文視窗會逐漸積累歷史訊息,可能導致: - 模型響應變慢 - token 超出限制 - 早期上下文被遺忘
OpenClaw 會自動對長對話進行 compaction(壓縮):
- 將對話歷史壓縮成一個 summary 摘要
- 保留關鍵的專案進展、決策、待辦事項
- 釋放大量 token 空間
觸發時機:通常在上下文累積到一定量時自動進行,無需手動干預
當收到 compaction 後的新對話時,立即執行以下步驟:
1️⃣ 讀取 summary 摘要,理解當前專案狀態
↓
2️⃣ 檢查 memory/YYYY-MM-DD.md 檔案
↓
3️⃣ 將新進展追加到 memory 檔案
↓
4️⃣ 繼續工作,保持檔案平衡
原則:每個功能完成後立即提交,避免大量變更堆積
# ✅ 好的做法:功能完成即提交
git add -A && git commit -m "feat: 新增使用者管理彈窗"
# ❌ 不好的做法:多個功能一起提交
git add -A && git commit -m "feat: 多項最佳化"
好處: - 如果出問題,容易回溯 - 減少每次提交的檔案變更量 - Compaction 後的 summary 更簡潔
每次 HTML 修改後檢查 div 平衡:
# 檢查 div 平衡
python3 -c "
with open('index.html', 'r') as f:
c = f.read()
print(f'opens={c.count(\"<div\")}, closes={c.count(\"</div>\")}, diff={c.count(\"<div\")-c.count(\"</div>\")}')
"
# ✅ 正確輸出:opens=xxx, closes=xxx, diff=0
# ❌ 錯誤輸出:opens=xxx, closes=xxx, diff=≠0
如果 diff 不為 0,立即修復:
- 缺少 </div>:在合適位置新增
- 多餘 </div>:刪除多餘的部分
每次 compaction 後或重要里程碑時,將進展寫入 memory/YYYY-MM-DD.md:
## 14:30 狀態更新
### 功能名稱 - 完善/修復
**新增內容**:
- 具體修改1
- 具體修改2
**提交記錄**:`abc1234` feat: 描述
**檔案**:`/path/to/file.html`
使用者提出需求
↓
理解現有程式碼結構(檢視 summary + 檔案)
↓
用 Python 指令碼做精確文本替換
↓
修改完檢查 div 平衡
↓
提交程式碼(每個功能單獨提交)
↓
截圖驗證(如需要)
↓
回覆使用者
↓
(Compaction 自動觸發時)
↓
讀取 summary,追加進展到 memory 檔案
WMS 原型是單頁 HTML(index.html),3700+ divs:
- 不是日誌檔案:不會無限增長
- 每次修改同一個檔案:不是追加模式
- Compaction 只壓縮對話歷史:不影響 HTML 檔案本身
"我是 AI,每次醒來都是新的開始。但檔案裡的內容是我的記憶。"
references/design-systems/ 目錄包含58+設計系統完整規範每次對任何頁面進行修改,必須遵循以下步驟:
1️⃣ 修改 pages/xxx.html
↓
2️⃣ 運行同步指令碼 → 更新 index.html
↓
3️⃣ 本地測試 → 驗證功能正常
↓
4️⃣ 提交程式碼 → git add + commit
在 pages/ 目錄的對應頁面檔案中進行修改:
- 彈窗 HTML 必須放在 <div id="page-xxx" class="page"> 內部
- 指令碼函式放在頁面的 <script> 塊中
必須運行同步指令碼:
cd your-project-directory
python3 << 'PYEOF'
import os
import re
with open('index.html', 'r', encoding='utf-8') as f:
content = f.read()
pages_dir = 'pages'
page_files = sorted([f for f in os.listdir(pages_dir) if f.endswith('.html')])
for page_file in page_files:
with open(os.path.join(pages_dir, page_file), 'r', encoding='utf-8') as f:
page_content = f.read()
match = re.search(r'(<div id="(page-\w+)"[^>]*class="page"[^>]*>.*?)(?=<div id="page-|\s*<script>|\s*</main>)', page_content, re.DOTALL)
if not match:
print(f'⚠ Skip: {page_file}')
continue
page_id = match.group(2)
page_block = match.group(1).strip()
# 驗證 div 平衡
opens = page_block.count('<div')
closes = page_block.count('</div>')
if opens != closes:
print(f'❌ Div imbalance in {page_id}: opens={opens}, closes={closes}')
continue
pattern = rf'(<div id="{re.escape(page_id)}"[^>]*class="page"[^>]*>)(.*?)(?=<div id="page-|\s*</main>)'
existing = re.search(pattern, content, re.DOTALL)
if existing:
content = content[:existing.start()] + page_block + content[existing.end():]
print(f'✓ Synced: {page_id}')
else:
print(f'⚠ Not found in index: {page_id}')
opens_all = content.count('<div')
closes_all = content.count('</div>')
print(f'\nDiv balance: opens={opens_all}, closes={closes_all}, diff={opens_all-closes_all}')
with open('index.html', 'w', encoding='utf-8') as f:
f.write(content)
print('✅ Sync complete!')
PYEOF
重要檢查:
- 同步後必須驗證 index.html 的 div 平衡(opens == closes)
- 如果不平衡,查詢問題所在
啟動本地伺服器(如果未執行):
cd your-project-directory
npx http-server -p 8080 &
測試步驟:
# 1. 開啟瀏覽器到首頁
agent-browser goto http://localhost:8080
# 2. 導航到目標頁面(使用 URL hash)
agent-browser goto http://localhost:8080/#page-name
# 3. 測試彈窗
agent-browser eval "openAddModal()" # 開啟彈窗
agent-browser screenshot test-modal.png # 截圖驗證
agent-browser eval "closeModal()" # 關閉彈窗
# 4. 關閉瀏覽器
agent-browser close
測試檢查清單: - [ ] 頁面正常載入 - [ ] 彈窗能開啟 - [ ] 彈窗內容正確 - [ ] 彈窗能關閉 - [ ] Tab 切換正常(如有) - [ ] 按鈕點選有效
cd your-project-directory
git add -A
git commit -m "fix: 描述修改內容"
症狀:openAddModal is not defined 等錯誤
根本原因:
1. main.js 引用被同步指令碼刪除
2. 同步指令碼移動了 <script src="main.js"></script> 位置
解決方案:
// 確保 main.js 在 </main> 之後引入
</main>
<script src="scripts/main.js"></script>
</body>
症狀:切換 Tab 時按鈕背景色不變化,全部選中態顯示錯誤
根本原因:Tab 切換函式使用全域性選擇器 .tabs .tab 會選中所有頁面的 tab
解決方案:使用頁面級作用域
// ❌ 錯誤:會選擇所有頁面的 tab
document.querySelectorAll('.tabs .tab')
// ✅ 正確:只選當前頁面的 tab
document.querySelectorAll('#page-xxx .tabs > .tab')
症狀:http://localhost:8080/#delivery_return 直接訪問時頁面 display:none
根本原因:沒有 hashchange 監聽,導航只用 click 事件
解決方案:在 main.js 新增 hash 路由監聽
// Hash 路由監聽 - 支援直接訪問 #page
window.addEventListener('hashchange', () => {
const hash = window.location.hash.replace('#', '');
if (hash) {
const navItem = document.querySelector(`[data-page="${hash}"]`);
if (navItem) {
navItem.click();
}
}
});
// 頁面載入時檢查 hash
if (window.location.hash) {
setTimeout(() => {
const hash = window.location.hash.replace('#', '');
const navItem = document.querySelector(`[data-page="${hash}"]`);
if (navItem) {
navItem.click();
}
}, 100);
}
症狀:表格內容多時,整個頁面寬度被撐開,侵入左側選單欄
根本原因:
1. 表格使用 width:100% + min-width:1400px 矛盾
2. 容器沒有正確設定 overflow:hidden
解決方案:使用 Ant Design 風格的固定表頭表格
核心原理:
1. 表頭和表體分離 - 用兩個獨立的 <table>
2. 表頭固定 - overflow:hidden,表頭不滾動
3. 表體滾動 - overflow:auto;max-height:440px
4. 固定列用 position:sticky - left:0 / right:0
5. 固定表格佈局 - table-layout:fixed + colgroup 定義列寬
程式碼模板:
<!-- 外層容器 -->
<div class="ant-table-wrapper" style="display:flex;flex-direction:column;overflow:hidden;height:500px;">
<!-- 表頭 - 固定不滾動 -->
<div class="ant-table-header" style="overflow:hidden;border-bottom:2px solid #E5E7EB;flex-shrink:0;">
<table style="table-layout:fixed;width:1800px;border-collapse:collapse;font-size:14px;">
<colgroup>
<col style="width:48px;"><!-- 核取方塊 -->
<col style="width:180px;"><!-- 單號 -->
<col style="width:100px;"><!-- 其他列... -->
<!-- ... -->
</colgroup>
<thead><tr>
<th style="position:sticky;left:0;background:#F9FAFB;z-index:4;"><input type="checkbox"></th>
<th style="position:sticky;left:48px;background:#F9FAFB;z-index:3;">單號</th>
<th>欄位1</th>
<th>欄位2</th>
<!-- ... -->
<th style="position:sticky;right:0;background:#F9FAFB;z-index:3;">操作</th>
</tr></thead>
</table>
</div>
<!-- 表體 - 獨立滾動 -->
<div class="ant-table-body" style="overflow:auto;max-height:440px;">
<table style="table-layout:fixed;width:1800px;border-collapse:collapse;font-size:14px;">
<colgroup>
<!-- 與表頭相同的列寬定義 -->
</colgroup>
<tbody>
<tr>
<td style="position:sticky;left:0;background:white;z-index:2;"><input type="checkbox"></td>
<td style="position:sticky;left:48px;background:white;z-index:2;">單號值</td>
<td>欄位1值</td>
<!-- ... -->
<td style="position:sticky;right:0;background:white;z-index:2;"><button>操作</button></td>
</tr>
</tbody>
</table>
</div>
</div>
關鍵樣式:
| 元素 | 樣式 | 說明 |
|------|------|------|
| 表頭 wrapper | overflow:hidden;flex-shrink:0 | 不滾動 |
| 表體 wrapper | overflow:auto;max-height:440px | 垂直滾動 |
| 表頭表格 | table-layout:fixed;width:固定值 | 固定列寬 |
| 固定列 th/td | position:sticky;left:0;z-index:2 | 左側固定 |
| 右側固定列 | position:sticky;right:0 | 右側固定 |
| 固定列背景 | background:#F9FAFB (表頭) / background:white (表體) | 避免透明 |
解決方案:
<div class="table-filters">
<!-- 其他篩選欄位... -->
<div class="filter-group" style="display:flex;gap:8px;">
<button class="btn btn-outline btn-sm" onclick="resetXxxFilters()">重置</button>
<button class="btn btn-primary btn-sm" onclick="searchXxx()">查詢</button>
</div>
</div>
對應 JS 函式:
function resetXxxFilters() {
document.querySelectorAll('#page-xxx .filter-select').forEach(s => s.selectedIndex = 0);
document.querySelectorAll('#page-xxx .filter-input').forEach(i => i.value = '');
}
function searchXxx() {
alert('查詢功能 - 實際專案中會呼叫API過濾資料');
}
解決方案:每行資料第一個 td 新增核取方塊
<tr>
<td style="position:sticky;left:0;z-index:1;background:white;">
<input type="checkbox">
</td>
<td style="position:sticky;left:48px;z-index:1;background:white;">
<span style="font-family:monospace;font-weight:500;">FC-2026-0410-001</span>
</td>
<!-- 其他欄位... -->
</tr>
症狀:同步後彈窗函式丟失
原因:</script> 後面沒有正確換行,導致被當作 HTML 標籤解析
解決方案:
<!-- ❌ 錯誤 -->
<script>function foo(){}</script><div>
<!-- ✅ 正確 -->
<script>
function foo(){}
</script>
<div>
| 問題 | 症狀 | 解決方案 |
|---|---|---|
| 彈窗打不開 | xxx is not defined |
檢查 main.js 引用位置和函式定義 |
| Tab 選中態錯亂 | 所有 Tab 同時選中 | 用頁面級作用域選擇器 |
| Hash URL 不工作 | 頁面 display:none |
新增 hashchange 監聽 |
| 表格侵入側邊欄 | 橫向滾動時佈局亂 | 用 Ant Design 固定表頭表格 |
| 核取方塊丟失 | 資料行沒有 checkbox | 手動新增每個資料行的核取方塊 |
| 同步後功能失效 | 彈窗/按鈕不工作 | 檢查 script 標籤格式和 div 平衡 |
問題現象:"新增組織"按鈕點選無反應,彈窗無法開啟
排查過程: 1. 檢查按鈕 onclick 處理函式 → 存在 2. 檢查 JavaScript 函式 → 存在 3. 檢查彈窗 HTML 位置 → 發現彈窗放在了頁面 div 外部
根本原因:
<!-- ❌ 錯誤:彈窗在頁面 div 外部 -->
<div id="page-system_org" class="page">
...頁面內容...
</div>
<!-- 彈窗在這裡,頁面關閉後的位置 -->
<div id="modal-add-org" style="display:none...">...</div>
<!-- ✅ 正確:彈窗必須在頁面 div 內部 -->
<div id="page-system_org" class="page">
...頁面內容...
<!-- 彈窗必須放在這裡 -->
<div id="modal-add-org" style="display:none...">...</div>
</div>
教訓:彈窗 HTML 放在 </div> (頁面關閉標籤) 之後導致彈窗不顯示。必須在頁面 div 內部。
問題現象:修復彈窗位置後,按鈕仍然無法點選
排查過程:
1. 使用 prototype-design 技能的除錯方法
2. 檢查 div 平衡 → 發現頁面 div 缺少閉合標籤
3. 修復 div 後檢查指令碼塊 → 發現多餘的 }
根本原因:submitNewException() 函式後有一個多餘的閉合括號 },導致 JavaScript 語法錯誤,整個指令碼塊無法執行
// ❌ 錯誤:函式後有多餘的 }
function submitNewException() {
// ...表單提交邏輯
}
//} <-- 多餘的括號導致語法錯誤
// ✅ 正確:括號匹配
function submitNewException() {
// ...表單提交邏輯
}
教訓:修改 HTML 時要確保 div 平衡;修改 JS 時要確保括號匹配。使用 Python 指令碼做精確替換比手動編輯更安全。
問題現象:新增彈窗後,彈窗中的按鈕點選無反應
根本原因:新增的彈窗按鈕沒有 onclick 屬性
解決方案:每個按鈕都要顯式繫結 onclick
<!-- ❌ 錯誤:按鈕沒有 onclick -->
<button class="btn btn-primary">確定</button>
<!-- ✅ 正確:顯式繫結 onclick -->
<button onclick="submitAddOrg()" class="btn btn-primary">確定</button>
教訓:複製彈窗模板時,別忘了修改按鈕的 onclick 屬性。
問題現象:各頁面按鈕樣式不統一,有內聯樣式如 style="padding:10px 24px;"
解決方案:統一使用 CSS 類
<!-- ✅ 主按鈕:黑色背景,膠囊形狀 -->
<button class="btn btn-primary btn-sm">新增</button>
<!-- ✅ 次按鈕:白色背景,灰色邊框 -->
<button class="btn btn-outline btn-sm">取消</button>
按鈕樣式規範:
| 元素 | 類名 | 樣式 |
|------|------|------|
| 主按鈕 | .btn .btn-primary | 黑色背景 #111827,膠囊形 |
| 次按鈕 | .btn .btn-outline | 白色背景,#E5E7EB 邊框 |
| 按鈕尺寸 | .btn-sm | padding: 6px 12px; font-size: 12px |
| 膠囊形狀 | border-radius: 50px | 用於主、次按鈕 |
問題現象:手動編輯 HTML 時容易出現 div 不平衡、括號遺漏等問題
解決方案:使用 Python 指令碼做精確文本替換
with open('index.html', 'r') as f:
content = f.read()
# 替換前先備份
# ...
# 精確替換一段 HTML
old_text = '''<button class="btn btn-primary btn-sm">
新增模組
</button>'''
new_text = '''<button class="btn btn-primary btn-sm" onclick="openAddModuleModal()">
新增模組
</button>'''
content = content.replace(old_text, new_text)
with open('index.html', 'w') as f:
f.write(content)
# 驗證 div 平衡
print(f'opens={content.count("<div")}, closes={content.count("</div>")}, diff={content.count("<div")-content.count("</div>")}')
教訓:用 Python 指令碼做精確替換,比手動編輯更可靠,尤其是涉及多行 HTML 時。
新增模組彈窗欄位: | 欄位 | 型別 | 說明 | |------|------|------| | 模組編碼 | text | 必填,如 SYS_010 | | 模組名稱 | text | 必填 | | 上級模組 | select | 無(頂級模組)/系統管理/基礎檔案... | | 模組分類 | select | 系統管理/基礎檔案/作業配置... | | 選單層級 | select | 一級/二級/三級選單 | | 選單圖示 | text | emoji 格式 | | 路由路徑 | text | 如 /system/module | | 排序 | number | 數字越小越靠前 | | 狀態 | select | 啟用/停用 | | 備註 | textarea | 可選 |
新增組織彈窗欄位: | 欄位 | 型別 | 說明 | |------|------|------| | 組織編碼 | text | 必填,如 ORG_010 | | 組織名稱 | text | 必填 | | 上級組織 | select | 母公司/北京倉/上海倉... | | 組織型別 | select | 公司/倉庫/部門/作業組 | | 負責人 | text | 可選 | | 聯絡電話 | tel | 可選 | | 所在地區 | text | 如 北京市/朝陽區 | | 狀態 | select | 啟用/停用 | | 組織地址 | textarea | 可選 |
新增使用者彈窗欄位: | 欄位 | 型別 | 說明 | |------|------|------| | 使用者名稱 | text | 必填 | | 登入密碼 | password | 必填,6位以上 | | 姓名 | text | 必填 | | 手機號 | tel | 必填 | | 郵箱 | email | 可選 | | 所屬組織 | select | 必填,北京倉-收貨組/上海倉... | | 使用者角色 | select | 超級管理員/倉庫管理員/作業員... | | 崗位 | text | 可選 | | 入職日期 | date | 可選 | | 狀態 | select | 正常/停用/待稽核 |
質量與異常報表增強: - 異常趨勢圖(近7天柱狀圖) - 異常型別分佈(貨損42%/配送延誤28%等) - 閉環率分析(96.8%閉環率、2.5h平均處理時長)
對賬準確率監察增強: - KPI 從 4 個擴充套件到 6 個 - 新增已核銷/核對中指標
作業報表增強: - 新增時效達標率分析(揀貨98.5%/上架97.8%等)
庫存報表增強: - 新增滯銷庫存預警(30天以上) - 新增安全庫存預警
問題現象:頁面有多個相似按鈕(如"新增"),用 document.querySelector 定位困難
解決方案:使用複合條件或更精確的選擇器
// ❌ 困難:頁面有多個 btn-primary
document.querySelector('.btn.btn-primary')
// ✅ 更好:使用 onclick 屬性定位
document.querySelector('button[onclick="openAddModuleModal()"]')
// ✅ 更好:在頁面內部查詢
document.querySelector('#page-system_module button[onclick="openAddModuleModal()"]')
原則:每次修改後立即驗證,不要積累多個問題
驗證清單:
# 1. 檢查 div 平衡
python3 -c "with open('index.html') as f: c=f.read(); print(f'opens={c.count(\"<div\")}, closes={c.count(\"</div>\")}, diff={c.count(\"<div\")-c.count(\"</div>\")}')"
# 2. 截圖驗證
agent-browser goto http://localhost:8080/#page-name
agent-browser eval "openAddModal()"
agent-browser screenshot test-modal.png
agent-browser close
# 3. 提交程式碼
git add -A && git commit -m "feat: 描述"
為什麼 WMS 原型用單頁 HTML 沒有問題: 1. Compaction 只壓縮對話歷史:不影響 HTML 檔案本身 2. 不是日誌檔案:不會無限增長 3. 每次修改同一個檔案:不是追加模式 4. 3700+ divs 仍然高效:檔案體積適中,渲染快
什麼時候該拆分成多檔案: - 單個檔案超過 2MB - 需要多人協作(git merge 衝突) - 模組之間完全獨立
這個技能質量不錯,它提供了58種設計風格可選,直接參考就能用,不用從零設計。頁面模板、彈窗、表格這些常用元件都有現成規範,複製改改就能用。還總結了真實專案踩過的坑,比如彈窗位置放錯、表格樣式衝突等問題都有解決方案。不過文件有些地方寫重複了,看起來不夠精簡。總體來說是一款實用且經驗豐富的原型設計技能,適合快速開發管理後臺頁面。