From ef1fc47cff8b10e7f7e5e76c3cd2e2256162f76d Mon Sep 17 00:00:00 2001 From: sky121113 Date: Wed, 7 Jan 2026 14:44:01 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=85=A5=E9=A9=97=E8=AD=89=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E4=BD=BF=E7=94=A8=E8=80=85=E6=8C=89=E9=88=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Auth/LoginController.php | 60 ++++++++ app/Http/Middleware/HandleInertiaRequests.php | 4 +- app/Models/User.php | 1 + ..._07_132554_add_username_to_users_table.php | 30 ++++ public/logo.png | Bin 0 -> 4965 bytes resources/js/Components/ApplicationLogo.tsx | 11 ++ resources/js/Components/InputError.tsx | 10 ++ resources/js/Layouts/AuthenticatedLayout.tsx | 68 +++++++-- resources/js/Pages/Auth/Login.tsx | 140 ++++++++++++++++++ routes/web.php | 125 +++++++++------- 10 files changed, 380 insertions(+), 69 deletions(-) create mode 100644 app/Http/Controllers/Auth/LoginController.php create mode 100644 database/migrations/2026_01_07_132554_add_username_to_users_table.php create mode 100644 public/logo.png create mode 100644 resources/js/Components/ApplicationLogo.tsx create mode 100644 resources/js/Components/InputError.tsx create mode 100644 resources/js/Pages/Auth/Login.tsx diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php new file mode 100644 index 0000000..47359ce --- /dev/null +++ b/app/Http/Controllers/Auth/LoginController.php @@ -0,0 +1,60 @@ +validate([ + 'username' => ['required', 'string'], + 'password' => ['required', 'string'], + ], [ + 'username.required' => '請輸入帳號', + 'password.required' => '請輸入密碼', + ]); + + $credentials = $request->only('username', 'password'); + + if (Auth::attempt($credentials, $request->boolean('remember'))) { + $request->session()->regenerate(); + + return redirect()->intended(route('dashboard')); + } + + throw ValidationException::withMessages([ + 'username' => '帳號或密碼錯誤。', + ]); + } + + /** + * Destroy an authenticated session. + */ + public function destroy(Request $request) + { + Auth::guard('web')->logout(); + + $request->session()->invalidate(); + + $request->session()->regenerateToken(); + + return redirect('/'); + } +} diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index c19ce18..546321f 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -37,7 +37,9 @@ class HandleInertiaRequests extends Middleware { return [ ...parent::share($request), - // + 'auth' => [ + 'user' => $request->user(), + ], ]; } } diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b7..b48779b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -20,6 +20,7 @@ class User extends Authenticatable protected $fillable = [ 'name', 'email', + 'username', 'password', ]; diff --git a/database/migrations/2026_01_07_132554_add_username_to_users_table.php b/database/migrations/2026_01_07_132554_add_username_to_users_table.php new file mode 100644 index 0000000..2b9a7ab --- /dev/null +++ b/database/migrations/2026_01_07_132554_add_username_to_users_table.php @@ -0,0 +1,30 @@ +string('username')->unique()->after('name'); + $table->string('email')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('username'); + $table->string('email')->nullable(false)->change(); + }); + } +}; diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8d08d2d8c5935b2e591418458ec239b207aa4b7d GIT binary patch literal 4965 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 { + return ( + 小小冰室 Logo + ); +} diff --git a/resources/js/Components/InputError.tsx b/resources/js/Components/InputError.tsx new file mode 100644 index 0000000..593d1ba --- /dev/null +++ b/resources/js/Components/InputError.tsx @@ -0,0 +1,10 @@ +import { HTMLAttributes } from 'react'; +import { cn } from '@/lib/utils'; + +export default function InputError({ message, className = '', ...props }: HTMLAttributes & { message?: string }) { + return message ? ( +

+ {message} +

+ ) : null; +} diff --git a/resources/js/Layouts/AuthenticatedLayout.tsx b/resources/js/Layouts/AuthenticatedLayout.tsx index f9e2c66..a273263 100644 --- a/resources/js/Layouts/AuthenticatedLayout.tsx +++ b/resources/js/Layouts/AuthenticatedLayout.tsx @@ -1,5 +1,4 @@ import { - ChevronDown, ChevronRight, Package, ShoppingCart, @@ -11,13 +10,24 @@ import { Warehouse, Truck, Contact2, - FileText + FileText, + LogOut, + User, + ChevronDown } from "lucide-react"; import { Toaster } from "sonner"; import { useState, useEffect } from "react"; import { Link, usePage } from "@inertiajs/react"; import { cn } from "@/lib/utils"; import BreadcrumbNav, { BreadcrumbItemType } from "@/Components/shared/BreadcrumbNav"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/Components/ui/dropdown-menu"; interface MenuItem { id: string; @@ -34,7 +44,9 @@ export default function AuthenticatedLayout({ children: React.ReactNode, breadcrumbs?: BreadcrumbItemType[] }) { - const { url } = usePage(); + const { url, props } = usePage(); + // @ts-ignore + const user = props.auth?.user || { name: 'Guest', username: 'guest' }; const [isCollapsed, setIsCollapsed] = useState(() => { if (typeof window !== "undefined") { return localStorage.getItem("sidebar-collapsed") === "true"; @@ -243,6 +255,38 @@ export default function AuthenticatedLayout({ 小小冰室 ERP + + {/* User Menu */} + + +
+ + {user.name} + + + {user.username || 'Administrator'} + +
+
+ +
+
+ + 我的帳號 + + + + + 登出系統 + + + +
{/* Sidebar Desktop */} @@ -281,15 +325,17 @@ export default function AuthenticatedLayout({ {isCollapsed ? : } - + {/* Mobile Sidebar Overlay */} - {isMobileOpen && ( -
setIsMobileOpen(false)} - /> - )} + { + isMobileOpen && ( +
setIsMobileOpen(false)} + /> + ) + } {/* Mobile Sidebar Drawer */}
+
); } diff --git a/resources/js/Pages/Auth/Login.tsx b/resources/js/Pages/Auth/Login.tsx new file mode 100644 index 0000000..d92b91c --- /dev/null +++ b/resources/js/Pages/Auth/Login.tsx @@ -0,0 +1,140 @@ +import { Head, useForm } from "@inertiajs/react"; +import { FormEventHandler, useEffect } from "react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/Components/ui/button"; +import { Input } from "@/Components/ui/input"; +import { Label } from "@/Components/ui/label"; +import InputError from "../../Components/InputError"; +import ApplicationLogo from "../../Components/ApplicationLogo"; + +export default function Login() { + const { data, setData, post, processing, errors, reset } = useForm({ + username: localStorage.getItem("saved_username") || "", + password: "", + remember: false, + rememberUsername: localStorage.getItem("remember_username") === "true", + }); + + useEffect(() => { + return () => { + reset("password"); + }; + }, []); + + const submit: FormEventHandler = (e) => { + e.preventDefault(); + + // 處理記住帳號邏輯 + if (data.rememberUsername) { + localStorage.setItem("saved_username", data.username); + localStorage.setItem("remember_username", "true"); + } else { + localStorage.removeItem("saved_username"); + localStorage.setItem("remember_username", "false"); + } + + post(route("login"), { + onFinish: () => reset("password"), + }); + }; + + return ( +
+ + + {/* 動態背景裝飾 */} +
+
+
+ +
+
+ +
+
+
+
+ + setData("username", e.target.value)} + required + autoFocus + /> + +
+ +
+ + setData("password", e.target.value)} + required + /> + +
+ +
+ + + +
+ + +
+
+ +

+ © 2026 小小冰室. All rights reserved. +

+
+
+ ); +} diff --git a/routes/web.php b/routes/web.php index e826813..c9c2b69 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,66 +5,77 @@ use Inertia\Inertia; use App\Http\Controllers\CategoryController; use App\Http\Controllers\VendorController; use App\Http\Controllers\VendorProductController; - use App\Http\Controllers\DashboardController; - -Route::get('/', [DashboardController::class, 'index'])->name('dashboard'); - use App\Http\Controllers\ProductController; - -Route::get('/products', [ProductController::class, 'index'])->name('products.index'); -Route::post('/products', [ProductController::class, 'store'])->name('products.store'); -Route::put('/products/{product}', [ProductController::class, 'update'])->name('products.update'); -Route::delete('/products/{product}', [ProductController::class, 'destroy'])->name('products.destroy'); - -Route::post('/categories', [CategoryController::class, 'store'])->name('categories.store'); -Route::put('/categories/{category}', [CategoryController::class, 'update'])->name('categories.update'); -Route::delete('/categories/{category}', [CategoryController::class, 'destroy'])->name('categories.destroy'); - - -// 倉庫管理 -Route::resource('warehouses', \App\Http\Controllers\WarehouseController::class); - -// 庫存管理 -Route::get('warehouses/{warehouse}/inventory', [\App\Http\Controllers\InventoryController::class, 'index'])->name('warehouses.inventory.index'); - -// 安全庫存管理 -Route::prefix('warehouses/{warehouse}/safety-stock-settings')->name('warehouses.safety-stock.')->group(function () { - Route::get('/', [\App\Http\Controllers\SafetyStockController::class, 'index'])->name('index'); - Route::post('/', [\App\Http\Controllers\SafetyStockController::class, 'store'])->name('store'); - Route::put('/{inventory}', [\App\Http\Controllers\SafetyStockController::class, 'update'])->name('update'); - Route::delete('/{inventory}', [\App\Http\Controllers\SafetyStockController::class, 'destroy'])->name('destroy'); -}); - -Route::get('/warehouses/{warehouse}/add-inventory', [\App\Http\Controllers\InventoryController::class, 'create'])->name('warehouses.add-inventory'); -Route::post('/warehouses/{warehouse}/inventory', [\App\Http\Controllers\InventoryController::class, 'store'])->name('warehouses.inventory.store'); -Route::get('/warehouses/{warehouse}/inventory/{inventory}/edit', [\App\Http\Controllers\InventoryController::class, 'edit'])->name('warehouses.inventory.edit'); -Route::put('/warehouses/{warehouse}/inventory/{inventory}', [\App\Http\Controllers\InventoryController::class, 'update'])->name('warehouses.inventory.update'); -Route::delete('/warehouses/{warehouse}/inventory/{inventory}', [\App\Http\Controllers\InventoryController::class, 'destroy'])->name('warehouses.inventory.destroy'); -Route::get('/warehouses/{warehouse}/inventory/{inventory}/history', [\App\Http\Controllers\InventoryController::class, 'history'])->name('warehouses.inventory.history'); - -// 撥補單 (Transfer Order) -Route::post('/transfer-orders', [\App\Http\Controllers\TransferOrderController::class, 'store'])->name('transfer-orders.store'); -Route::get('/api/warehouses/{warehouse}/inventories', [\App\Http\Controllers\TransferOrderController::class, 'getWarehouseInventories'])->name('api.warehouses.inventories'); - +use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\PurchaseOrderController; +use App\Http\Controllers\WarehouseController; +use App\Http\Controllers\InventoryController; +use App\Http\Controllers\SafetyStockController; +use App\Http\Controllers\TransferOrderController; -Route::get('/purchase-orders', [PurchaseOrderController::class, 'index'])->name('purchase-orders.index'); -Route::get('/purchase-orders/create', [PurchaseOrderController::class, 'create'])->name('purchase-orders.create'); -Route::post('/purchase-orders', [PurchaseOrderController::class, 'store'])->name('purchase-orders.store'); -Route::get('/purchase-orders/{id}', [PurchaseOrderController::class, 'show'])->name('purchase-orders.show'); -Route::get('/purchase-orders/{id}/edit', [PurchaseOrderController::class, 'edit'])->name('purchase-orders.edit'); -Route::put('/purchase-orders/{id}', [PurchaseOrderController::class, 'update'])->name('purchase-orders.update'); -Route::delete('/purchase-orders/{id}', [PurchaseOrderController::class, 'destroy'])->name('purchase-orders.destroy'); +Route::get('/login', [LoginController::class, 'show'])->name('login'); +Route::post('/login', [LoginController::class, 'store']); +Route::post('/logout', [LoginController::class, 'destroy'])->name('logout'); -// 廠商管理 -Route::get('/vendors', [VendorController::class, 'index'])->name('vendors.index'); -Route::get('/vendors/{vendor}', [VendorController::class, 'show'])->name('vendors.show'); -Route::post('/vendors', [VendorController::class, 'store'])->name('vendors.store'); -Route::put('/vendors/{vendor}', [VendorController::class, 'update'])->name('vendors.update'); -Route::delete('/vendors/{vendor}', [VendorController::class, 'destroy'])->name('vendors.destroy'); +Route::middleware('auth')->group(function () { + Route::get('/', [DashboardController::class, 'index'])->name('dashboard'); -// 供貨商品相關路由 -Route::post('/vendors/{vendor}/products', [VendorProductController::class, 'store'])->name('vendors.products.store'); -Route::put('/vendors/{vendor}/products/{product}', [VendorProductController::class, 'update'])->name('vendors.products.update'); -Route::delete('/vendors/{vendor}/products/{product}', [VendorProductController::class, 'destroy'])->name('vendors.products.destroy'); + // 類別管理 (用於商品對話框) + Route::get('/categories', [CategoryController::class, 'index'])->name('categories.index'); + Route::post('/categories', [CategoryController::class, 'store'])->name('categories.store'); + Route::put('/categories/{category}', [CategoryController::class, 'update'])->name('categories.update'); + Route::delete('/categories/{category}', [CategoryController::class, 'destroy'])->name('categories.destroy'); + + // 商品管理 + Route::get('/products', [ProductController::class, 'index'])->name('products.index'); + Route::post('/products', [ProductController::class, 'store'])->name('products.store'); + Route::put('/products/{product}', [ProductController::class, 'update'])->name('products.update'); + + // 廠商管理 + Route::get('/vendors', [VendorController::class, 'index'])->name('vendors.index'); + Route::get('/vendors/{vendor}', [VendorController::class, 'show'])->name('vendors.show'); + Route::post('/vendors', [VendorController::class, 'store'])->name('vendors.store'); + Route::put('/vendors/{vendor}', [VendorController::class, 'update'])->name('vendors.update'); + Route::delete('/vendors/{vendor}', [VendorController::class, 'destroy'])->name('vendors.destroy'); + + // 供貨商品相關路由 + Route::post('/vendors/{vendor}/products', [VendorProductController::class, 'store'])->name('vendors.products.store'); + Route::put('/vendors/{vendor}/products/{product}', [VendorProductController::class, 'update'])->name('vendors.products.update'); + Route::delete('/vendors/{vendor}/products/{product}', [VendorProductController::class, 'destroy'])->name('vendors.products.destroy'); + + // 倉庫管理 + Route::get('/warehouses', [WarehouseController::class, 'index'])->name('warehouses.index'); + Route::post('/warehouses', [WarehouseController::class, 'store'])->name('warehouses.store'); + Route::put('/warehouses/{warehouse}', [WarehouseController::class, 'update'])->name('warehouses.update'); + Route::delete('/warehouses/{warehouse}', [WarehouseController::class, 'destroy'])->name('warehouses.destroy'); + + // 倉庫庫存管理 + Route::get('/warehouses/{warehouse}/inventory', [InventoryController::class, 'index'])->name('warehouses.inventory.index'); + Route::get('/warehouses/{warehouse}/inventory/create', [InventoryController::class, 'create'])->name('warehouses.inventory.create'); + Route::post('/warehouses/{warehouse}/inventory', [InventoryController::class, 'store'])->name('warehouses.inventory.store'); + Route::get('/warehouses/{warehouse}/inventory/{inventoryId}/edit', [InventoryController::class, 'edit'])->name('warehouses.inventory.edit'); + Route::put('/warehouses/{warehouse}/inventory/{inventoryId}', [InventoryController::class, 'update'])->name('warehouses.inventory.update'); + Route::delete('/warehouses/{warehouse}/inventory/{inventoryId}', [InventoryController::class, 'destroy'])->name('warehouses.inventory.destroy'); + Route::get('/warehouses/{warehouse}/inventory/{inventoryId}/history', [InventoryController::class, 'history'])->name('warehouses.inventory.history'); + + // 安全庫存設定 + Route::get('/warehouses/{warehouse}/safety-stock', [SafetyStockController::class, 'index'])->name('warehouses.safety-stock.index'); + Route::post('/warehouses/{warehouse}/safety-stock', [SafetyStockController::class, 'store'])->name('warehouses.safety-stock.store'); + Route::put('/warehouses/{warehouse}/safety-stock/{inventory}', [SafetyStockController::class, 'update'])->name('warehouses.safety-stock.update'); + Route::delete('/warehouses/{warehouse}/safety-stock/{inventory}', [SafetyStockController::class, 'destroy'])->name('warehouses.safety-stock.destroy'); + + // 採購單管理 + Route::get('/purchase-orders', [PurchaseOrderController::class, 'index'])->name('purchase-orders.index'); + Route::get('/purchase-orders/create', [PurchaseOrderController::class, 'create'])->name('purchase-orders.create'); + Route::post('/purchase-orders', [PurchaseOrderController::class, 'store'])->name('purchase-orders.store'); + Route::get('/purchase-orders/{id}', [PurchaseOrderController::class, 'show'])->name('purchase-orders.show'); + Route::get('/purchase-orders/{id}/edit', [PurchaseOrderController::class, 'edit'])->name('purchase-orders.edit'); + Route::put('/purchase-orders/{id}', [PurchaseOrderController::class, 'update'])->name('purchase-orders.update'); + Route::delete('/purchase-orders/{id}', [PurchaseOrderController::class, 'destroy'])->name('purchase-orders.destroy'); + + // 撥補單 (在庫存調撥時使用) + Route::post('/transfer-orders', [TransferOrderController::class, 'store'])->name('transfer-orders.store'); + Route::get('/api/warehouses/{warehouse}/inventories', [TransferOrderController::class, 'getWarehouseInventories'])->name('api.warehouses.inventories'); + +}); // End of auth middleware group