Files
star-erp/.agent/skills/ui-consistency/SKILL.md
sky121113 f7238c2860
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 51s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped
fix: 統一 UI 按鈕樣式並新增 button-outlined-error hover 效果
- 修正 5 處硬編碼顏色樣式改用預定義按鈕類別
- 新增 button-outlined-error 的 hover 狀態(bg-red-50)
- 修正倉庫模組刪除按鈕樣式統一性
- 角色管理權限 Badge 改用標準組件
- 新增 UI 統一性規範 skill
- 修復 1 處 lint 警告(移除未使用參數)

變更檔案:
- resources/css/app.css: 新增 button-outlined-error hover 樣式
- resources/js/Components/Warehouse/WarehouseDialog.tsx
- resources/js/Pages/Admin/Role/Index.tsx
- resources/js/Pages/Warehouse/EditInventory.tsx
- resources/js/Pages/Warehouse/Inventory.tsx
- resources/js/Pages/Warehouse/SafetyStockSettings.tsx
- .agent/skills/ui-consistency/SKILL.md (新增)
2026-01-14 11:31:36 +08:00

743 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: UI 統一性規範
description: 確保 koori-erp ERP 系統後台所有頁面的 UI 元件保持統一的樣式與行為
---
# UI 統一性規範
## 概述
本 skill 提供 koori-erp ERP 系統的 UI 統一性規範,確保所有頁面使用一致的元件、樣式類別、圖標和佈局模式。
## 核心原則
1. **使用統一的 UI 組件庫**:優先使用 `@/Components/ui/` 中的元件
2. **遵循既定的樣式類別**:使用 `app.css` 中定義的自定義按鈕類別
3. **統一的圖標系統**:全面使用 `lucide-react` 圖標
4. **一致的佈局模式**:表格、分頁、操作按鈕等保持相同結構
---
## 1. 按鈕規範
### 1.1 按鈕樣式類別
專案在 `resources/css/app.css` 中定義了統一的按鈕樣式類別,**必須**使用這些類別而非自定義樣式:
#### Filled 按鈕(實心按鈕)
```tsx
// 主要操作按鈕(綠色主題色)
<Button className="button-filled-primary">
<Plus className="h-4 w-4 mr-2" />
</Button>
// 成功操作
<Button className="button-filled-success"></Button>
// 資訊操作
<Button className="button-filled-info"></Button>
// 警告操作
<Button className="button-filled-warning"></Button>
// 錯誤/刪除操作
<Button className="button-filled-error"></Button>
```
#### Outlined 按鈕(邊框按鈕)
```tsx
// 次要操作(主題色邊框)
<Button variant="outline" size="sm" className="button-outlined-primary">
<Pencil className="h-4 w-4" />
</Button>
// 成功樣式邊框
<Button className="button-outlined-success"></Button>
// 資訊樣式邊框
<Button className="button-outlined-info"></Button>
// 警告樣式邊框
<Button className="button-outlined-warning"></Button>
// 錯誤/刪除樣式邊框
<Button variant="outline" size="sm" className="button-outlined-error">
<Trash2 className="h-4 w-4" />
</Button>
```
#### Text 按鈕(文字按鈕)
```tsx
// 文字按鈕
<Button className="button-text-primary"></Button>
```
### 1.2 按鈕大小
使用 shadcn/ui Button 組件的標準尺寸:
- `size="sm"`小型按鈕h-8用於表格操作列
- `size="default"`預設按鈕h-9用於一般操作
- `size="lg"`大型按鈕h-10用於主要 CTA
- `size="icon"`圖標按鈕size-9僅含圖標的正方形按鈕
### 1.3 常見操作按鈕模式
#### 新增按鈕(頁面頂部)
```tsx
<Can permission="resource.create">
<Link href={route('resource.create')}>
<Button className="button-filled-primary">
<Plus className="h-4 w-4 mr-2" />
XXX
</Button>
</Link>
</Can>
```
#### 編輯按鈕(表格操作列)
```tsx
<Can permission="resource.edit">
<Link href={route('resource.edit', item.id)}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title="編輯"
>
<Pencil className="h-4 w-4" />
</Button>
</Link>
</Can>
```
#### 刪除按鈕(表格操作列,帶確認對話框)
```tsx
<Can permission="resource.delete">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="button-outlined-error"
title="刪除"
>
<Trash2 className="h-4 w-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
{item.name}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction
onClick={() => handleDelete(item.id)}
className="bg-red-600 hover:bg-red-700"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</Can>
```
---
## 2. 圖標規範
### 2.1 圖標庫
**統一使用 `lucide-react`**,不使用其他圖標庫(如 FontAwesome、Material Icons 等)。
### 2.2 圖標尺寸標準
- **小型圖標**`h-3 w-3`(用於 Badge、小文字旁
- **標準圖標**`h-4 w-4`(用於按鈕、表格操作)
- **標題圖標**`h-6 w-6`(用於頁面標題)
### 2.3 常用操作圖標映射
| 操作 | 圖標組件 | 使用情境 |
|------|----------|----------|
| 新增 | `<Plus />` | 新增按鈕 |
| 編輯 | `<Pencil />` | 編輯按鈕 |
| 刪除 | `<Trash2 />` | 刪除按鈕 |
| 查看 | `<Eye />` | 查看詳情 |
| 搜尋 | `<Search />` | 搜尋欄位 |
| 篩選 | `<Filter />` | 篩選功能 |
| 下載 | `<Download />` | 下載/匯出 |
| 上傳 | `<Upload />` | 上傳/匯入 |
| 設定 | `<Settings />` | 設定功能 |
| 複製 | `<Copy />` | 複製內容 |
| 郵件 | `<Mail />` | Email 顯示 |
| 使用者 | `<Users />`, `<User />` | 使用者管理 |
| 權限 | `<Shield />` | 角色/權限 |
| 排序 | `<ArrowUpDown />`, `<ArrowUp />`, `<ArrowDown />` | 表格排序 |
### 2.4 圖標使用範例
```tsx
import { Plus, Pencil, Trash2, Eye, Search } from 'lucide-react';
// 頁面標題
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
<Users className="h-6 w-6 text-[#01ab83]" />
使
</h1>
// 按鈕內圖標
<Button className="button-filled-primary">
<Plus className="h-4 w-4 mr-2" />
使
</Button>
// 純圖標按鈕(表格操作列)
<Button variant="outline" size="sm" className="button-outlined-primary">
<Pencil className="h-4 w-4" />
</Button>
```
---
## 3. 表格規範
### 3.1 表格容器
使用統一的表格包裝樣式:
```tsx
<div className="bg-white rounded-lg shadow-sm border">
<Table>
{/* 表格內容 */}
</Table>
</div>
```
或使用更精緻的樣式(用於管理頁面):
```tsx
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
<Table>
{/* 表格內容 */}
</Table>
</div>
```
### 3.2 表格標題列
```tsx
<TableHeader className="bg-gray-50">
<TableRow>
<TableHead className="w-[50px] text-center">#</TableHead>
<TableHead>
<button
onClick={() => onSort("name")}
className="flex items-center hover:text-gray-900 font-semibold"
>
<SortIcon field="name" />
</button>
</TableHead>
<TableHead className="text-center"></TableHead>
</TableRow>
</TableHeader>
```
**關鍵要點**
- 使用 `bg-gray-50` 背景色
- 序號欄位固定寬度 `w-[50px]` 並置中
- 可排序欄位使用 `<button>` 包裹,加上 hover 效果
- 操作欄位置中顯示
### 3.3 排序圖標元件
```tsx
const SortIcon = ({ field }: { field: string }) => {
if (sortField !== field) {
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
}
if (sortDirection === "asc") {
return <ArrowUp className="h-4 w-4 text-primary ml-1" />;
}
if (sortDirection === "desc") {
return <ArrowDown className="h-4 w-4 text-primary ml-1" />;
}
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
};
```
### 3.4 表格主體
```tsx
<TableBody>
{items.length === 0 ? (
<TableRow>
<TableCell colSpan={7} className="text-center py-8 text-gray-500">
</TableCell>
</TableRow>
) : (
items.map((item, index) => (
<TableRow key={item.id}>
<TableCell className="text-gray-500 font-medium text-center">
{startIndex + index}
</TableCell>
{/* 其他欄位 */}
<TableCell className="text-center">
<div className="flex justify-center gap-2">
{/* 操作按鈕 */}
</div>
</TableCell>
</TableRow>
))
)}
</TableBody>
```
**關鍵要點**
- 空狀態訊息使用置中、灰色文字
- 序號欄使用 `text-gray-500 font-medium text-center`
- 操作欄使用 `flex justify-center gap-2` 排列按鈕
### 3.5 操作欄按鈕組
```tsx
<TableCell className="text-center">
<div className="flex justify-center gap-2">
<Can permission="resource.edit">
<Link href={route('resource.edit', item.id)}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title="編輯"
>
<Pencil className="h-4 w-4" />
</Button>
</Link>
</Can>
<Can permission="resource.delete">
<AlertDialog>
{/* 刪除確認對話框 */}
</AlertDialog>
</Can>
</div>
</TableCell>
```
---
## 4. 分頁規範
### 4.1 統一分頁元件
使用 `@/Components/shared/Pagination` 元件:
```tsx
import Pagination from "@/Components/shared/Pagination";
// 在表格下方
<div className="mt-4 flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2 text-sm text-gray-500">
<span></span>
<SearchableSelect
value={perPage}
onValueChange={handlePerPageChange}
options={[
{ label: "10", value: "10" },
{ label: "20", value: "20" },
{ label: "50", value: "50" },
{ label: "100", value: "100" }
]}
className="w-[80px] h-8"
showSearch={false}
/>
<span></span>
</div>
<Pagination links={data.links} />
</div>
```
### 4.2 每頁筆數狀態管理
```tsx
const [perPage, setPerPage] = useState<string>(filters.per_page || "10");
const handlePerPageChange = (value: string) => {
setPerPage(value);
router.get(
route('resource.index'),
{ per_page: value },
{ preserveState: false, replace: true, preserveScroll: true }
);
};
```
---
## 5. Badge 與狀態顯示
### 5.1 基本 Badge
使用 `@/Components/ui/badge`
```tsx
import { Badge } from "@/Components/ui/badge";
// Outline 樣式(最常用)
<Badge variant="outline">
{item.category?.name || '-'}
</Badge>
// 預設樣式(主題色背景)
<Badge variant="default"></Badge>
// 錯誤樣式
<Badge variant="destructive"></Badge>
```
### 5.2 角色顯示(特殊樣式)
參考使用者管理的角色顯示模式:
```tsx
<div className="flex flex-wrap gap-2">
{user.roles.map(role => (
<div
key={role.id}
className={cn(
"inline-flex flex-col px-3 py-1.5 rounded-md border",
role.name === 'super-admin'
? "bg-purple-50 border-purple-200"
: "bg-gray-50 border-gray-200"
)}
>
<div className="flex items-center gap-1">
{role.name === 'super-admin' && <Shield className="h-3 w-3 text-purple-600" />}
<span className={cn(
"text-sm font-medium",
role.name === 'super-admin' ? "text-purple-700" : "text-gray-900"
)}>
{role.display_name}
</span>
</div>
<span className="text-[10px] text-gray-500 font-mono">
{role.name}
</span>
</div>
))}
</div>
```
---
## 6. 頁面佈局規範
### 6.1 標準頁面頭部
```tsx
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
<IconComponent className="h-6 w-6 text-[#01ab83]" />
</h1>
<p className="text-gray-500 mt-1">
</p>
</div>
<Can permission="resource.create">
<Link href={route('resource.create')}>
<Button className="button-filled-primary">
<Plus className="h-4 w-4 mr-2" />
</Button>
</Link>
</Can>
</div>
```
### 6.2 容器寬度
```tsx
<div className="container mx-auto p-6 max-w-7xl">
{/* 頁面內容 */}
</div>
```
---
## 7. 權限控制規範
### 7.1 使用 Can 元件
**所有**涉及權限的 UI 元素都必須使用 `<Can>` 元件包裹:
```tsx
import { Can } from "@/Components/Permission/Can";
<Can permission="resource.create">
{/* 新增按鈕 */}
</Can>
<Can permission="resource.edit">
{/* 編輯按鈕 */}
</Can>
<Can permission="resource.delete">
{/* 刪除按鈕 */}
</Can>
```
### 7.2 權限命名規範
遵循 `resource.action` 格式:
- `resource.index`:查看列表
- `resource.show`:查看詳情
- `resource.create`:新增
- `resource.edit`:編輯
- `resource.delete`:刪除
---
## 8. 通知訊息規範
### 8.1 使用 Toast 通知
使用 `sonner``toast` 進行通知:
```tsx
import { toast } from 'sonner';
// 成功訊息
toast.success('操作成功');
// 錯誤訊息
toast.error('操作失敗');
// 資訊訊息
toast.info('提示訊息');
// 警告訊息
toast.warning('警告訊息');
```
### 8.2 常見操作的 Toast 訊息
```tsx
// 新增成功
router.post(route('resource.store'), data, {
onSuccess: () => toast.success('新增成功'),
onError: () => toast.error('新增失敗,請檢查輸入內容'),
});
// 更新成功
router.put(route('resource.update', id), data, {
onSuccess: () => toast.success('更新成功'),
onError: () => toast.error('更新失敗'),
});
// 刪除成功
router.delete(route('resource.destroy', id), {
onSuccess: () => toast.success('已刪除'),
onError: () => toast.error('刪除失敗,請檢查權限'),
});
```
---
## 9. 顏色系統
### 9.1 主題色
參考 `resources/css/app.css` 中的色彩定義:
```css
--primary-main: #01ab83; /* 主題綠色 */
--primary-dark: #018a6a; /* 深綠色 */
--primary-light: #33bc9a; /* 淺綠色 */
--primary-lightest: #e6f7f3; /* 最淺綠色背景 */
--grey-0: #1a1a1a; /* 深黑色文字 */
--grey-1: #4a4a4a; /* 深灰色文字 */
--grey-2: #6b6b6b; /* 中灰色文字 */
--grey-3: #9e9e9e; /* 淺灰色文字 */
--grey-4: #e0e0e0; /* 邊框灰色 */
--grey-5: #fff; /* 白色 */
```
### 9.2 狀態色
```css
--other-success: #01ab83; /* 成功(同主題色)*/
--other-error: #dc2626; /* 錯誤紅色 */
--other-warning: #f59e0b; /* 警告橙色 */
--other-info: #3b82f6; /* 資訊藍色 */
```
---
## 10. 檢查清單
在開發或審查頁面時,請確認以下項目:
### ✅ 按鈕
- [ ] 使用 `button-filled-*``button-outlined-*` 類別
- [ ] 主要操作使用 `button-filled-primary`
- [ ] 編輯操作使用 `button-outlined-primary`
- [ ] 刪除操作使用 `button-outlined-error`
- [ ] 按鈕尺寸正確sm/default/lg
- [ ] 包含適當的圖標(左側或單一圖標)
### ✅ 圖標
- [ ] 全部使用 `lucide-react`
- [ ] 尺寸正確h-3/h-4/h-6 w-3/w-4/w-6
- [ ] 顏色與上下文一致
- [ ] 有明確的語義(編輯=Pencil、刪除=Trash2 等)
### ✅ 表格
- [ ] 使用 `@/Components/ui/table` 元件
- [ ]`bg-white rounded-lg shadow-sm border` 容器
- [ ] 標題列有 `bg-gray-50` 背景
- [ ] 序號欄固定寬度並置中
- [ ] 操作欄使用 `flex justify-center gap-2`
- [ ] 空狀態訊息置中顯示
- [ ] 可排序欄位有排序圖標
### ✅ 分頁
- [ ] 使用 `@/Components/shared/Pagination`
- [ ] 有每頁筆數選擇器10/20/50/100
- [ ] 佈局為 `flex justify-between`
### ✅ 權限
- [ ] 所有操作按鈕都用 `<Can>` 包裹
- [ ] 權限命名符合 `resource.action` 格式
### ✅ 通知
- [ ] 使用 `toast` 提供操作反饋
- [ ] 成功/錯誤訊息明確
### ✅ 整體
- [ ] 頁面有標準頭部(標題 + 圖標 + 說明 + 新增按鈕)
- [ ] 容器寬度使用 `max-w-7xl`
- [ ] 色彩使用符合主題
---
## 11. 常見錯誤與修正
### ❌ 錯誤:自定義按鈕樣式
```tsx
// 錯誤
<Button className="bg-green-500 text-white hover:bg-green-600">
</Button>
```
```tsx
// 正確
<Button className="button-filled-primary">
<Plus className="h-4 w-4 mr-2" />
</Button>
```
### ❌ 錯誤:混用圖標庫
```tsx
// 錯誤
import { FaEdit } from 'react-icons/fa'; // ❌ 不使用 react-icons
<FaEdit />
```
```tsx
// 正確
import { Pencil } from 'lucide-react'; // ✅ 使用 lucide-react
<Pencil className="h-4 w-4" />
```
### ❌ 錯誤:操作欄未置中
```tsx
// 錯誤
<TableCell>
<Button></Button>
<Button></Button>
</TableCell>
```
```tsx
// 正確
<TableCell className="text-center">
<div className="flex justify-center gap-2">
<Button variant="outline" size="sm" className="button-outlined-primary">
<Pencil className="h-4 w-4" />
</Button>
<Button variant="outline" size="sm" className="button-outlined-error">
<Trash2 className="h-4 w-4" />
</Button>
</div>
</TableCell>
```
### ❌ 錯誤:缺少權限控制
```tsx
// 錯誤
<Button onClick={handleDelete}></Button>
```
```tsx
// 正確
<Can permission="resource.delete">
<Button
variant="outline"
size="sm"
className="button-outlined-error"
onClick={handleDelete}
>
<Trash2 className="h-4 w-4" />
</Button>
</Can>
```
---
## 12. 實際範例
參考以下頁面作為標準實作:
- **使用者管理**`resources/js/Pages/Admin/User/Index.tsx`
- **產品管理**`resources/js/Pages/Product/Index.tsx`
- **產品表格**`resources/js/Components/Product/ProductTable.tsx`
這些頁面展示了完整的 UI 統一性實踐,包括按鈕、圖標、表格、分頁、權限控制等所有元素。
---
## 總結
遵循本規範可確保:
1.**視覺一致性**:所有頁面看起來像同一個系統
2.**維護效率**:使用統一組件,修改一處即可影響全局
3.**開發速度**:有明確的模式可循,減少決策時間
4.**使用者體驗**:一致的互動模式降低學習成本
當你在開發或審查 koori-erp 的 UI 時,請務必參考此規範,確保每個元件都符合既定的標準。