From 89291918fde48e5ed4f59f8abfe8f833463b6b6a Mon Sep 17 00:00:00 2001 From: sky121113 Date: Mon, 9 Mar 2026 13:48:06 +0800 Subject: [PATCH] =?UTF-8?q?[FEAT]=20=E5=AF=A6=E4=BD=9C=E9=85=8D=E6=96=B9?= =?UTF-8?q?=E8=88=87=E7=94=9F=E7=94=A2=E5=B7=A5=E5=96=AE=E8=87=AA=E5=8B=95?= =?UTF-8?q?=E6=90=9C=E5=B0=8B=EF=BC=8C=E5=84=AA=E5=8C=96=E5=88=86=E9=A0=81?= =?UTF-8?q?=20RWD=EF=BC=8C=E5=B0=87=E5=80=89=E5=BA=AB=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E6=94=B9=E7=82=BA=E9=81=B8=E5=A1=AB=E4=B8=A6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/rules/framework.md | 12 ++++- .../Components/Warehouse/WarehouseDialog.tsx | 3 +- resources/js/Components/shared/Pagination.tsx | 27 +++++++--- .../js/Pages/Admin/ActivityLog/Index.tsx | 12 ++--- resources/js/Pages/Production/Index.tsx | 49 +++++++++---------- .../js/Pages/Production/Recipe/Index.tsx | 48 +++++++++--------- resources/js/utils/validation.ts | 4 -- 7 files changed, 82 insertions(+), 73 deletions(-) diff --git a/.agents/rules/framework.md b/.agents/rules/framework.md index 3d14c50..4c74ce2 100644 --- a/.agents/rules/framework.md +++ b/.agents/rules/framework.md @@ -87,4 +87,14 @@ trigger: always_on ## 10. 部署與查修環境 (CI/CD & Troubleshooting) * **自動化部署**:本專案使用 CI/CD 自動化部署,開發者只需 push 程式碼至對應分支即可。 * **Demo 環境 (對應 `demo` 分支)**:若需查修測試站問題(例如查看 Error Log 或資料庫),請連線 `ssh gitea_work`。 -* **Production 環境 (對應 `main` 分支)**:若需查修正式站問題,請連線 `ssh erp`。 \ No newline at end of file +* **Production 環境 (對應 `main` 分支)**:若需查修正式站問題,請連線 `ssh erp`。 + +## 11. 瀏覽器測試規範 (Browser Testing) +當需要進行瀏覽器自動化測試或手動驗證時,請遵守以下連線資訊: + +* **本地測試網址**:`http://localhost:8081/` +* **預設管理員帳號**:`admin` +* **預設管理員密碼**:`password` + +> [!IMPORTANT] +> 在執行 browser subagent 或進行 E2E 測試時,請務必確認為 `8081` Port,以避免連線至錯誤的服務環境。 \ No newline at end of file diff --git a/resources/js/Components/Warehouse/WarehouseDialog.tsx b/resources/js/Components/Warehouse/WarehouseDialog.tsx index fab0c2a..9da26a2 100644 --- a/resources/js/Components/Warehouse/WarehouseDialog.tsx +++ b/resources/js/Components/Warehouse/WarehouseDialog.tsx @@ -266,14 +266,13 @@ export default function WarehouseDialog({ {/* 倉庫地址 */}
setFormData({ ...formData, address: e.target.value })} placeholder="例:台北市信義區信義路五段7號" - required className="h-9" />
diff --git a/resources/js/Components/shared/Pagination.tsx b/resources/js/Components/shared/Pagination.tsx index 7a9cbd6..35eed90 100644 --- a/resources/js/Components/shared/Pagination.tsx +++ b/resources/js/Components/shared/Pagination.tsx @@ -27,22 +27,33 @@ export default function Pagination({ links, className }: PaginationProps) { const isNext = label === "Next"; const activeIndex = links.findIndex(l => l.active); - // Tablet/Mobile visibility logic (< md): - // Show: Previous, Next, Active, and up to 2 neighbors (Total ~5 numeric pages) - // Hide others on small screens (hidden md:flex) - // User requested: "small than 800... display 5 pages" - const isVisibleOnTablet = + // Responsive visibility logic: + // Global: Previous, Next, Active are always visible + // Mobile (< sm): Active, +-1, First, Last, and Ellipses + // Tablet (sm < md): Active, +-2, First, Last, and Ellipses + // Desktop (>= md): All standard pages + const isFirst = key === 1; + const isLast = key === links.length - 2; + const isEllipsis = !isPrevious && !isNext && !link.url; + + const isMobileVisible = isPrevious || isNext || link.active || + isFirst || + isLast || + isEllipsis || key === activeIndex - 1 || - key === activeIndex + 1 || + key === activeIndex + 1; + + const isTabletVisible = + isMobileVisible || key === activeIndex - 2 || key === activeIndex + 2; const baseClasses = cn( - isVisibleOnTablet ? "flex" : "hidden md:flex", - "h-9 items-center justify-center rounded-md border px-3 text-sm" + "h-9 items-center justify-center rounded-md border px-3 text-sm", + isMobileVisible ? "flex" : (isTabletVisible ? "hidden sm:flex md:flex" : "hidden md:flex") ); // 如果是 Previous/Next 但沒有 URL,則不渲染(或者渲染為 disabled) diff --git a/resources/js/Pages/Admin/ActivityLog/Index.tsx b/resources/js/Pages/Admin/ActivityLog/Index.tsx index ef9962a..ca0011c 100644 --- a/resources/js/Pages/Admin/ActivityLog/Index.tsx +++ b/resources/js/Pages/Admin/ActivityLog/Index.tsx @@ -318,10 +318,10 @@ export default function ActivityLogIndex({ activities, filters, subject_types, u from={activities.from} /> -
-
+
+
- 每頁顯示 + 每頁顯示 - +
- 共 {activities.total} 筆資料 + 共 {activities.total} 筆資料
-
+
diff --git a/resources/js/Pages/Production/Index.tsx b/resources/js/Pages/Production/Index.tsx index 5a633c3..9ca8101 100644 --- a/resources/js/Pages/Production/Index.tsx +++ b/resources/js/Pages/Production/Index.tsx @@ -2,8 +2,9 @@ * 生產工單管理主頁面 */ -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import { Plus, Factory, Search, Eye, Pencil, Trash2 } from 'lucide-react'; +import { debounce } from "lodash"; import { formatQuantity } from "@/lib/utils"; import { Button } from "@/Components/ui/button"; import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; @@ -77,16 +78,25 @@ export default function ProductionIndex({ productionOrders, filters }: Props) { setPerPage(filters.per_page || productionOrders.per_page?.toString() || "10"); }, [filters]); - const handleFilter = () => { - router.get( - route('production-orders.index'), - { - search, - status: status === 'all' ? undefined : status, - per_page: perPage, - }, - { preserveState: true, replace: true, preserveScroll: true } - ); + const debouncedFilter = useCallback( + debounce((params: any) => { + router.get(route("production-orders.index"), params, { + preserveState: true, + replace: true, + preserveScroll: true, + }); + }, 300), + [] + ); + + const handleSearchChange = (term: string) => { + setSearch(term); + debouncedFilter({ + ...filters, + search: term, + status: status === "all" ? undefined : status, + per_page: perPage, + }); }; @@ -129,16 +139,12 @@ export default function ProductionIndex({ productionOrders, filters }: Props) { setSearch(e.target.value)} + onChange={(e) => handleSearchChange(e.target.value)} className="pl-10 pr-10 h-9" - onKeyDown={(e) => e.key === 'Enter' && handleFilter()} /> {search && ( - -