From 7cf640b2f4ae8608b04d21709e215fca80f30261 Mon Sep 17 00:00:00 2001
From: sky121113
Date: Mon, 9 Feb 2026 17:16:00 +0800
Subject: [PATCH] feat(sales): replace import page with dialog UI and support
template download
---
.../Controllers/SalesImportController.php | 4 +-
.../Exports/SalesImportTemplateExport.php | 37 +++++
app/Modules/Sales/Routes/web.php | 2 +-
.../js/Components/Sales/SalesImportDialog.tsx | 139 ++++++++++++++++++
resources/js/Pages/Sales/Import/Create.tsx | 90 ------------
resources/js/Pages/Sales/Import/Index.tsx | 20 ++-
6 files changed, 193 insertions(+), 99 deletions(-)
create mode 100644 app/Modules/Sales/Exports/SalesImportTemplateExport.php
create mode 100644 resources/js/Components/Sales/SalesImportDialog.tsx
delete mode 100644 resources/js/Pages/Sales/Import/Create.tsx
diff --git a/app/Modules/Sales/Controllers/SalesImportController.php b/app/Modules/Sales/Controllers/SalesImportController.php
index 7c61aab..181a050 100644
--- a/app/Modules/Sales/Controllers/SalesImportController.php
+++ b/app/Modules/Sales/Controllers/SalesImportController.php
@@ -30,9 +30,9 @@ class SalesImportController extends Controller
]);
}
- public function create()
+ public function template()
{
- return Inertia::render('Sales/Import/Create');
+ return Excel::download(new \App\Modules\Sales\Exports\SalesImportTemplateExport, 'sales_import_template.xlsx');
}
public function store(Request $request)
diff --git a/app/Modules/Sales/Exports/SalesImportTemplateExport.php b/app/Modules/Sales/Exports/SalesImportTemplateExport.php
new file mode 100644
index 0000000..b5f3606
--- /dev/null
+++ b/app/Modules/Sales/Exports/SalesImportTemplateExport.php
@@ -0,0 +1,37 @@
+ ['font' => ['bold' => true]],
+ ];
+ }
+}
diff --git a/app/Modules/Sales/Routes/web.php b/app/Modules/Sales/Routes/web.php
index 02e08c1..17f4ce4 100644
--- a/app/Modules/Sales/Routes/web.php
+++ b/app/Modules/Sales/Routes/web.php
@@ -10,7 +10,7 @@ Route::middleware(['auth', 'verified'])->prefix('sales')->name('sales-imports.')
});
Route::post('/imports', [SalesImportController::class, 'store'])->middleware('permission:sales_imports.create')->name('store');
- Route::get('/imports/create', [SalesImportController::class, 'create'])->middleware('permission:sales_imports.create')->name('create');
+ Route::get('/imports/template', [SalesImportController::class, 'template'])->middleware('permission:sales_imports.create')->name('template');
Route::post('/imports/{import}/confirm', [SalesImportController::class, 'confirm'])->middleware('permission:sales_imports.confirm')->name('confirm');
Route::delete('/imports/{import}', [SalesImportController::class, 'destroy'])->middleware('permission:sales_imports.delete')->name('destroy');
diff --git a/resources/js/Components/Sales/SalesImportDialog.tsx b/resources/js/Components/Sales/SalesImportDialog.tsx
new file mode 100644
index 0000000..8def14d
--- /dev/null
+++ b/resources/js/Components/Sales/SalesImportDialog.tsx
@@ -0,0 +1,139 @@
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/Components/ui/dialog";
+import { Button } from "@/Components/ui/button";
+import { Input } from "@/Components/ui/input";
+import { Label } from "@/Components/ui/label";
+import { Upload, AlertCircle, Info, FileSpreadsheet } from "lucide-react";
+import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/Components/ui/accordion";
+import { useForm } from "@inertiajs/react";
+import { Alert, AlertDescription } from "@/Components/ui/alert";
+import React from "react";
+
+interface SalesImportDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+
+export default function SalesImportDialog({ open, onOpenChange }: SalesImportDialogProps) {
+ const { data, setData, post, processing, errors, reset, clearErrors } = useForm<{
+ file: File | null;
+ }>({
+ file: null,
+ });
+
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ setData("file", e.target.files[0]);
+ clearErrors("file");
+ }
+ };
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ post(route("sales-imports.store"), {
+ onSuccess: () => {
+ reset();
+ onOpenChange(false);
+ },
+ });
+ };
+
+ return (
+
+ );
+}
diff --git a/resources/js/Pages/Sales/Import/Create.tsx b/resources/js/Pages/Sales/Import/Create.tsx
deleted file mode 100644
index 4127f81..0000000
--- a/resources/js/Pages/Sales/Import/Create.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
-import { Head, useForm, Link } from '@inertiajs/react';
-import { Button } from '@/Components/ui/button';
-import { Input } from '@/Components/ui/input';
-import { Label } from '@/Components/ui/label';
-import { Upload, ArrowLeft, FileSpreadsheet } from 'lucide-react';
-import React from 'react';
-
-export default function SalesImportCreate() {
- const { data, setData, post, processing, errors } = useForm({
- file: null as File | null,
- });
-
- const submit = (e: React.FormEvent) => {
- e.preventDefault();
- post(route('sales-imports.store'));
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
- 新增銷售匯入
-
-
-
-
-
-
-
匯入說明
-
- - 請使用統一的 Excel 格式(商品銷貨單)。
- - 系統將自動解析機台編號、商品代號與交易時間。
- - 匯入後請至「待確認」清單檢查內容,確認無誤後再執行扣庫。
-
-
-
-
- );
-}
diff --git a/resources/js/Pages/Sales/Import/Index.tsx b/resources/js/Pages/Sales/Import/Index.tsx
index fe2ffaa..2b3861e 100644
--- a/resources/js/Pages/Sales/Import/Index.tsx
+++ b/resources/js/Pages/Sales/Import/Index.tsx
@@ -28,6 +28,7 @@ import Pagination from "@/Components/shared/Pagination";
import { SearchableSelect } from "@/Components/ui/searchable-select";
import { router } from "@inertiajs/react";
import { usePermission } from "@/hooks/usePermission";
+import SalesImportDialog from "@/Components/Sales/SalesImportDialog";
interface ImportBatch {
id: number;
@@ -54,6 +55,7 @@ interface Props {
export default function SalesImportIndex({ batches, filters = {} }: Props) {
const { can } = usePermission();
const [perPage, setPerPage] = useState(filters?.per_page?.toString() || "10");
+ const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);
useEffect(() => {
if (filters?.per_page) {
@@ -91,15 +93,21 @@ export default function SalesImportIndex({ batches, filters = {} }: Props) {
{can('sales_imports.create') && (
-
-
-
+
)}
+
+