- 修正 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 (新增)
17 KiB
17 KiB
name, description
| name | description |
|---|---|
| UI 統一性規範 | 確保 koori-erp ERP 系統後台所有頁面的 UI 元件保持統一的樣式與行為 |
UI 統一性規範
概述
本 skill 提供 koori-erp ERP 系統的 UI 統一性規範,確保所有頁面使用一致的元件、樣式類別、圖標和佈局模式。
核心原則
- 使用統一的 UI 組件庫:優先使用
@/Components/ui/中的元件 - 遵循既定的樣式類別:使用
app.css中定義的自定義按鈕類別 - 統一的圖標系統:全面使用
lucide-react圖標 - 一致的佈局模式:表格、分頁、操作按鈕等保持相同結構
1. 按鈕規範
1.1 按鈕樣式類別
專案在 resources/css/app.css 中定義了統一的按鈕樣式類別,必須使用這些類別而非自定義樣式:
Filled 按鈕(實心按鈕)
// 主要操作按鈕(綠色主題色)
<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 按鈕(邊框按鈕)
// 次要操作(主題色邊框)
<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 按鈕(文字按鈕)
// 文字按鈕
<Button className="button-text-primary">查看更多</Button>
1.2 按鈕大小
使用 shadcn/ui Button 組件的標準尺寸:
size="sm":小型按鈕(h-8),用於表格操作列size="default":預設按鈕(h-9),用於一般操作size="lg":大型按鈕(h-10),用於主要 CTAsize="icon":圖標按鈕(size-9),僅含圖標的正方形按鈕
1.3 常見操作按鈕模式
新增按鈕(頁面頂部)
<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>
編輯按鈕(表格操作列)
<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>
<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 圖標使用範例
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 表格容器
使用統一的表格包裝樣式:
<div className="bg-white rounded-lg shadow-sm border">
<Table>
{/* 表格內容 */}
</Table>
</div>
或使用更精緻的樣式(用於管理頁面):
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
<Table>
{/* 表格內容 */}
</Table>
</div>
3.2 表格標題列
<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 排序圖標元件
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 表格主體
<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 操作欄按鈕組
<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 元件:
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 每頁筆數狀態管理
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:
import { Badge } from "@/Components/ui/badge";
// Outline 樣式(最常用)
<Badge variant="outline">
{item.category?.name || '-'}
</Badge>
// 預設樣式(主題色背景)
<Badge variant="default">啟用中</Badge>
// 錯誤樣式
<Badge variant="destructive">停用</Badge>
5.2 角色顯示(特殊樣式)
參考使用者管理的角色顯示模式:
<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 標準頁面頭部
<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 容器寬度
<div className="container mx-auto p-6 max-w-7xl">
{/* 頁面內容 */}
</div>
7. 權限控制規範
7.1 使用 Can 元件
所有涉及權限的 UI 元素都必須使用 <Can> 元件包裹:
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 進行通知:
import { toast } from 'sonner';
// 成功訊息
toast.success('操作成功');
// 錯誤訊息
toast.error('操作失敗');
// 資訊訊息
toast.info('提示訊息');
// 警告訊息
toast.warning('警告訊息');
8.2 常見操作的 Toast 訊息
// 新增成功
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 中的色彩定義:
--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 狀態色
--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. 常見錯誤與修正
❌ 錯誤:自定義按鈕樣式
// 錯誤
<Button className="bg-green-500 text-white hover:bg-green-600">
新增
</Button>
// 正確
<Button className="button-filled-primary">
<Plus className="h-4 w-4 mr-2" />
新增
</Button>
❌ 錯誤:混用圖標庫
// 錯誤
import { FaEdit } from 'react-icons/fa'; // ❌ 不使用 react-icons
<FaEdit />
// 正確
import { Pencil } from 'lucide-react'; // ✅ 使用 lucide-react
<Pencil className="h-4 w-4" />
❌ 錯誤:操作欄未置中
// 錯誤
<TableCell>
<Button>編輯</Button>
<Button>刪除</Button>
</TableCell>
// 正確
<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>
❌ 錯誤:缺少權限控制
// 錯誤
<Button onClick={handleDelete}>刪除</Button>
// 正確
<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 統一性實踐,包括按鈕、圖標、表格、分頁、權限控制等所有元素。
總結
遵循本規範可確保:
- ✅ 視覺一致性:所有頁面看起來像同一個系統
- ✅ 維護效率:使用統一組件,修改一處即可影響全局
- ✅ 開發速度:有明確的模式可循,減少決策時間
- ✅ 使用者體驗:一致的互動模式降低學習成本
當你在開發或審查 koori-erp 的 UI 時,請務必參考此規範,確保每個元件都符合既定的標準。