소스 검색

修改礼品卡列表页面的样式

llp 1 주 전
부모
커밋
e779ecde2d

+ 13 - 11
packages/Longyi/Gift/src/DataGrids/GiftCards/GiftCardsDataGrid.php

@@ -18,7 +18,7 @@ class GiftCardsDataGrid extends DataGrid
         $queryBuilder = DB::table('gift_cards')
         $queryBuilder = DB::table('gift_cards')
             ->leftJoin('customers', 'gift_cards.customer_id', '=', 'customers.id')
             ->leftJoin('customers', 'gift_cards.customer_id', '=', 'customers.id')
             ->leftJoin('gift_card_usage_logs as gift_log', function ($leftJoin) {
             ->leftJoin('gift_card_usage_logs as gift_log', function ($leftJoin) {
-                $leftJoin->on('gift_log.customer_id', '=', 'gift_cards.customer_id')
+                $leftJoin->on('gift_log.giftcard_number', '=', 'gift_cards.giftcard_number')
                     ->where('gift_log.action_type', GiftCardUsageLog::ACTION_ADD);
                     ->where('gift_log.action_type', GiftCardUsageLog::ACTION_ADD);
             })
             })
             ->select(
             ->select(
@@ -176,19 +176,21 @@ class GiftCardsDataGrid extends DataGrid
      */
      */
     public function prepareActions()
     public function prepareActions()
     {
     {
-//        if (bouncer()->hasPermission('gift.edit')) {
-//            $this->addAction([
-//                'icon'   => 'icon-edit',
-//                'title'  => trans('gift::app.admin.datagrid.edit'),
-//                'method' => 'GET',
-//                'url'    => function ($row) {
-//                    return route('admin.gift.edit', $row->id);
-//                },
-//            ]);
-//        }
+        if (bouncer()->hasPermission('gift.edit')) {
+            $this->addAction([
+                'index'  => 'edit',
+                'icon'   => 'icon-edit',
+                'title'  => trans('gift::app.admin.datagrid.edit'),
+                'method' => 'GET',
+                'url'    => function ($row) {
+                    return route('admin.gift.edit', $row->id);
+                },
+            ]);
+        }
 
 
         if (bouncer()->hasPermission('gift.delete')) {
         if (bouncer()->hasPermission('gift.delete')) {
             $this->addAction([
             $this->addAction([
+                'index'  => 'delete',
                 'icon'   => 'icon-delete',
                 'icon'   => 'icon-delete',
                 'title'  => trans('gift::app.admin.datagrid.delete'),
                 'title'  => trans('gift::app.admin.datagrid.delete'),
                 'method' => 'DELETE',
                 'method' => 'DELETE',

+ 83 - 34
packages/Longyi/Gift/src/Http/Controllers/Admin/GiftController.php

@@ -5,7 +5,6 @@ namespace Longyi\Gift\Http\Controllers\Admin;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\JsonResponse;
 use Illuminate\View\View;
 use Illuminate\View\View;
 use Longyi\Gift\Models\GiftCards;
 use Longyi\Gift\Models\GiftCards;
-use Longyi\Gift\Models\GiftCardUsageLog;
 use Webkul\Admin\Http\Controllers\Controller;
 use Webkul\Admin\Http\Controllers\Controller;
 use Longyi\Gift\DataGrids\GiftCards\GiftCardsDataGrid;
 use Longyi\Gift\DataGrids\GiftCards\GiftCardsDataGrid;
 use Longyi\Gift\Repositories\GiftCardsRepository;
 use Longyi\Gift\Repositories\GiftCardsRepository;
@@ -31,55 +30,105 @@ class GiftController extends Controller
     public function index(): View|JsonResponse
     public function index(): View|JsonResponse
     {
     {
         if (request()->ajax()) {
         if (request()->ajax()) {
-             return datagrid(GiftCardsDataGrid::class)->process();
+            return datagrid(GiftCardsDataGrid::class)->process();
         }
         }
 
 
         return view('gift::admin.index');
         return view('gift::admin.index');
     }
     }
 
 
-    /**
-     * Show the form for creating a new resource.
-     */
-    public function create(): View
-    {
-        return view('gift::admin.create');
-    }
-
     /**
     /**
      * Store a newly created resource in storage.
      * Store a newly created resource in storage.
      */
      */
-    public function store(): JsonResponse|\Illuminate\Http\RedirectResponse
+    public function store(): JsonResponse
     {
     {
         $data = request()->validate([
         $data = request()->validate([
-            'giftcard_amount' => 'required|numeric|min:0',
-            'customer_id' => 'required|integer',
-            'expirationdate' => 'required|date_format:Y-m-d',
-            'giftcard_status' => 'required|in:1,2',
+            'giftcard_amount'  => 'required|numeric|min:0',
+            'customer_email'   => 'nullable|email',
+            'customer_id'      => 'nullable|integer|exists:customers,id',
+            'expirationdate'   => 'required|date_format:Y-m-d H:i:s',
+            'giftcard_status'  => 'required|in:1,2',
         ]);
         ]);
 
 
+        // 如果填写了邮箱,必须验证通过(customer_id 不能为空)
+        if (!empty($data['customer_email']) && empty($data['customer_id'])) {
+            return new JsonResponse([
+                'message' => '邮箱验证失败,该邮箱未注册',
+            ], 422);
+        }
+
         $data['giftcard_number'] = $this->generateUniqueGiftCardNumber();
         $data['giftcard_number'] = $this->generateUniqueGiftCardNumber();
         $data['used_giftcard_amount'] = 0;
         $data['used_giftcard_amount'] = 0;
         $data['type'] = 1;
         $data['type'] = 1;
         $data['remaining_giftcard_amount'] = $data['giftcard_amount'] ?? 0;
         $data['remaining_giftcard_amount'] = $data['giftcard_amount'] ?? 0;
+        $data['channel'] = 'Administrator addition';
 
 
         $this->giftCardsRepository->create($data);
         $this->giftCardsRepository->create($data);
-        // 记录使用日志
-        GiftCardUsageLog::create([
-            'giftcard_number' => $data['giftcard_number'],
-            'customer_id' => $data['customer_id'],
-            'used_amount' => 0,
-            'balance_before' => $data['giftcard_amount'],
-            'balance_after' => $data['giftcard_amount'],
-            'action_type' => GiftCardUsageLog::ACTION_ADD,
-            'status' => 'completed',
-            'notes' => "Administrator addition",
+
+        return new JsonResponse([
+            'message' => trans('gift::app.admin.create.create-success'),
         ]);
         ]);
-        if (request()->expectsJson()) {
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     */
+    public function edit(int $id): JsonResponse
+    {
+        $giftCard = $this->giftCardsRepository->find($id);
+
+        if (! $giftCard) {
             return new JsonResponse([
             return new JsonResponse([
-                'message' => '礼品卡创建成功',
-            ]);
+                'message' => trans('gift::app.admin.edit.not-found'),
+            ], 404);
         }
         }
-        return redirect()->route('admin.gift.index');
+
+        $data = $giftCard->toArray();
+
+        // 格式化过期时间,避免 toArray() 的时区序列化问题
+        if ($giftCard->expirationdate) {
+            $data['expirationdate'] = $giftCard->expirationdate->format('Y-m-d H:i:s');
+        }
+
+        // 附带 customer email 方便编辑
+        if ($giftCard->customer_id) {
+            $customer = \Webkul\Customer\Models\Customer::find($giftCard->customer_id);
+            if ($customer) {
+                $data['customer_email'] = $customer->email;
+            }
+        }
+
+        return new JsonResponse([
+            'data' => $data,
+        ]);
+    }
+
+    /**
+     * Update the specified resource in storage.
+     */
+    public function update(): JsonResponse
+    {
+        $data = request()->validate([
+            'id'                => 'required|integer',
+            'expirationdate'    => 'required|date_format:Y-m-d H:i:s',
+            'giftcard_status'   => 'required|in:1,2',
+        ]);
+
+        $giftCard = $this->giftCardsRepository->find($data['id']);
+
+        if (! $giftCard) {
+            return new JsonResponse([
+                'message' => trans('gift::app.admin.edit.not-found'),
+            ], 404);
+        }
+
+        $giftCard->update([
+            'expirationdate'  => $data['expirationdate'],
+            'giftcard_status' => $data['giftcard_status'],
+        ]);
+
+        return new JsonResponse([
+            'message' => trans('gift::app.admin.edit.update-success'),
+        ]);
     }
     }
 
 
     /**
     /**
@@ -143,11 +192,11 @@ class GiftController extends Controller
             $this->giftCardsRepository->delete($id);
             $this->giftCardsRepository->delete($id);
 
 
             return new JsonResponse([
             return new JsonResponse([
-                'message' => '礼品卡删除成功',
+                'message' => trans('gift::app.admin.datagrid.delete-success'),
             ]);
             ]);
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             return new JsonResponse([
             return new JsonResponse([
-                'message' => '礼品卡删除失败',
+                'message' => trans('gift::app.admin.datagrid.delete-error'),
             ], 400);
             ], 400);
         }
         }
     }
     }
@@ -161,7 +210,7 @@ class GiftController extends Controller
 
 
         if (empty($indices)) {
         if (empty($indices)) {
             return new JsonResponse([
             return new JsonResponse([
-                'message' => '请选择要删除的礼品卡',
+                'message' => trans('gift::app.admin.datagrid.mass-delete-empty'),
             ], 422);
             ], 422);
         }
         }
 
 
@@ -171,11 +220,11 @@ class GiftController extends Controller
             }
             }
 
 
             return new JsonResponse([
             return new JsonResponse([
-                'message' => '选中的礼品卡删除成功',
+                'message' => trans('gift::app.admin.datagrid.delete-success'),
             ]);
             ]);
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             return new JsonResponse([
             return new JsonResponse([
-                'message' => '批量删除失败',
+                'message' => trans('gift::app.admin.datagrid.mass-delete-error'),
             ], 400);
             ], 400);
         }
         }
     }
     }

+ 4 - 2
packages/Longyi/Gift/src/Models/GiftCards.php

@@ -35,7 +35,7 @@ class GiftCards extends Model implements GiftCardsContract
         'used_giftcard_amount' => 'decimal:2',
         'used_giftcard_amount' => 'decimal:2',
         'remaining_giftcard_amount' => 'decimal:2',
         'remaining_giftcard_amount' => 'decimal:2',
         'customer_id' => 'integer',
         'customer_id' => 'integer',
-        'expirationdate' => 'date',
+        'expirationdate' => 'datetime',
         'channel' => 'string',
         'channel' => 'string',
     ];
     ];
     protected static function boot()
     protected static function boot()
@@ -48,8 +48,10 @@ class GiftCards extends Model implements GiftCardsContract
                 'giftcard_number' => $giftCard->giftcard_number,
                 'giftcard_number' => $giftCard->giftcard_number,
                 'customer_id' => $giftCard->customer_id,
                 'customer_id' => $giftCard->customer_id,
                 'balance_before' => $giftCard->giftcard_amount,
                 'balance_before' => $giftCard->giftcard_amount,
+                'balance_after' => $giftCard->giftcard_amount,
                 'action_type' => GiftCardUsageLog::ACTION_ADD,
                 'action_type' => GiftCardUsageLog::ACTION_ADD,
-                'notes' => $giftCard->notes
+                'status' => 'completed',
+                'notes' => $giftCard->channel ?: '',
             ]);
             ]);
         });
         });
     }
     }

+ 51 - 36
packages/Longyi/Gift/src/Resources/lang/en/app.php

@@ -2,51 +2,66 @@
 
 
 return [
 return [
     'admin' => [
     'admin' => [
-        'title' => '礼品卡管理',
+        'title' => 'Gift Cards',
+
+        'index' => [
+            'create-btn' => 'Add Gift Card',
+        ],
 
 
         'datagrid' => [
         'datagrid' => [
-            'id' => 'ID',
-            'giftcard-number' => '礼品卡号',
-            'giftcard-amount' => '卡金额',
-            'used-amount' => '已用金额',
-            'remaining-amount' => '剩余金额',
-            'customer-id' => '客户 ID',
-            'expiration-date' => '过期日期',
-            'status' => '状态',
-            'created-at' => '创建时间',
-            'edit' => '编辑',
-            'delete' => '删除',
-            'delete-success' => '选中的礼品卡删除成功',
+            'id'                => 'ID',
+            'giftcard-number'   => 'Card Number',
+            'giftcard-amount'   => 'Amount',
+            'used-amount'       => 'Used Amount',
+            'remaining-amount'  => 'Remaining Amount',
+            'customer-id'       => 'Customer ID',
+            'expiration-date'   => 'Expiration Date',
+            'status'            => 'Status',
+            'notes'             => 'Notes',
+            'created-at'        => 'Created At',
+            'edit'              => 'Edit',
+            'delete'            => 'Delete',
+            'delete-success'    => 'Deleted successfully',
+            'delete-error'      => 'Delete failed',
+            'mass-delete-empty' => 'Please select gift cards to delete',
+            'mass-delete-error' => 'Mass delete failed',
         ],
         ],
 
 
         'create' => [
         'create' => [
-            'title' => '创建礼品卡',
-            'save-btn' => '保存',
-            'back-btn' => '返回',
-            'general' => '常规信息',
-            'giftcard-number' => '礼品卡号',
-            'giftcard-amount' => '卡金额',
-            'customer-id' => '客户 ID',
-            'expirationdate' => '过期日期',
-            'giftcard-status' => '状态',
-            'unused' => '未使用',
-            'used' => '已使用',
+            'title'                      => 'Create Gift Card',
+            'save-btn'                   => 'Save',
+            'back-btn'                   => 'Cancel',
+            'general'                    => 'General',
+            'giftcard-number'            => 'Card Number',
+            'giftcard-amount'            => 'Amount',
+            'customer-email'             => 'Customer Email',
+            'customer-email-placeholder' => 'Enter customer email',
+            'customer-id'                => 'Customer ID',
+            'expirationdate'             => 'Expiration Date',
+            'giftcard-status'            => 'Status',
+            'unused'                     => 'Unused',
+            'used'                       => 'Used',
+            'validating'                 => 'Validating...',
+            'validation-failed'          => 'Validation failed, please try again',
+            'create-success'             => 'Gift card created successfully',
         ],
         ],
 
 
         'edit' => [
         'edit' => [
-            'title' => '编辑礼品卡',
-            'save-btn' => '保存',
-            'back-btn' => '返回',
+            'title'          => 'Edit Gift Card',
+            'save-btn'       => 'Save',
+            'back-btn'       => 'Cancel',
+            'not-found'      => 'Gift card not found',
+            'update-success' => 'Gift card updated successfully',
         ],
         ],
     ],
     ],
     'giftcard' => [
     'giftcard' => [
-        'giftcard_amount' => 'Giftcard Amount',
-        'applied'   =>  'Giftcard Applied',
-        'discount'   =>  'Giftcard Discount',
-        'apply'   =>  'Apply Giftcard',
-        'enter-your-code' => 'Enter your code',
-        'remove'          => 'Remove Giftcard',
-        'remaining_giftcard_amount' => 'Remaining Amount',
-        'giftcard_number'      => 'Giftcard Number',
-    ]
+        'giftcard_amount'             => 'Gift Card Amount',
+        'applied'                     => 'Gift Card Applied',
+        'discount'                    => 'Gift Card Discount',
+        'apply'                       => 'Apply Gift Card',
+        'enter-your-code'             => 'Enter your code',
+        'remove'                      => 'Remove Gift Card',
+        'remaining_giftcard_amount'   => 'Remaining Amount',
+        'giftcard_number'             => 'Gift Card Number',
+    ],
 ];
 ];

+ 49 - 34
packages/Longyi/Gift/src/Resources/lang/zh_CN/app.php

@@ -4,49 +4,64 @@ return [
     'admin' => [
     'admin' => [
         'title' => '礼品卡管理',
         'title' => '礼品卡管理',
 
 
+        'index' => [
+            'create-btn' => '添加礼品卡',
+        ],
+
         'datagrid' => [
         'datagrid' => [
-            'id' => 'ID',
-            'giftcard-number' => '礼品卡号',
-            'giftcard-amount' => '卡金额',
-            'used-amount' => '已用金额',
-            'remaining-amount' => '剩余金额',
-            'customer-id' => '客户 ID',
-            'expiration-date' => '过期日期',
-            'status' => '状态',
-            'created-at' => '创建时间',
-            'edit' => '编辑',
-            'delete' => '删除',
-            'delete-success' => '选中的礼品卡删除成功',
+            'id'                => 'ID',
+            'giftcard-number'   => '礼品卡号',
+            'giftcard-amount'   => '卡金额',
+            'used-amount'       => '已用金额',
+            'remaining-amount'  => '剩余金额',
+            'customer-id'       => '客户 ID',
+            'expiration-date'   => '过期日期',
+            'status'            => '状态',
+            'notes'             => '备注',
+            'created-at'        => '创建时间',
+            'edit'              => '编辑',
+            'delete'            => '删除',
+            'delete-success'    => '删除成功',
+            'delete-error'      => '删除失败',
+            'mass-delete-empty' => '请选择要删除的礼品卡',
+            'mass-delete-error' => '批量删除失败',
         ],
         ],
 
 
         'create' => [
         'create' => [
-            'title' => '创建礼品卡',
-            'save-btn' => '保存',
-            'back-btn' => '返回',
-            'general' => '常规信息',
-            'giftcard-number' => '礼品卡号',
-            'giftcard-amount' => '卡金额',
-            'customer-id' => '客户 ID',
-            'expirationdate' => '过期日期',
-            'giftcard-status' => '状态',
-            'unused' => '未使用',
-            'used' => '已使用',
+            'title'                      => '创建礼品卡',
+            'save-btn'                   => '保存',
+            'back-btn'                   => '返回',
+            'general'                    => '常规信息',
+            'giftcard-number'            => '礼品卡号',
+            'giftcard-amount'            => '卡金额',
+            'customer-email'             => '用户邮箱',
+            'customer-email-placeholder' => '请输入用户邮箱',
+            'customer-id'                => '客户 ID',
+            'expirationdate'             => '过期日期',
+            'giftcard-status'            => '状态',
+            'unused'                     => '未使用',
+            'used'                       => '已使用',
+            'validating'                 => '正在验证...',
+            'validation-failed'          => '验证失败,请稍后重试',
+            'create-success'             => '礼品卡创建成功',
         ],
         ],
 
 
         'edit' => [
         'edit' => [
-            'title' => '编辑礼品卡',
-            'save-btn' => '保存',
-            'back-btn' => '返回',
+            'title'          => '编辑礼品卡',
+            'save-btn'       => '保存',
+            'back-btn'       => '返回',
+            'not-found'      => '礼品卡未找到',
+            'update-success' => '礼品卡更新成功',
         ],
         ],
     ],
     ],
     'giftcard' => [
     'giftcard' => [
-        'giftcard_amount' => 'Giftcard Amount',
-        'applied'   =>  'Giftcard Applied',
-        'discount'   =>  'Giftcard Discount',
-        'apply'   =>  'Apply Giftcard',
-        'enter-your-code' => 'Enter your code',
-        'remove'          => 'Remove Giftcard',
-        'remaining_giftcard_amount' => 'Remaining Amount',
-        'giftcard_number'      => 'Giftcard Number',
+        'giftcard_amount'             => '礼品卡金额',
+        'applied'                     => '礼品卡已应用',
+        'discount'                    => '礼品卡折扣',
+        'apply'                       => '应用礼品卡',
+        'enter-your-code'             => '请输入卡号',
+        'remove'                      => '移除礼品卡',
+        'remaining_giftcard_amount'   => '剩余金额',
+        'giftcard_number'             => '礼品卡号',
     ],
     ],
 ];
 ];

+ 0 - 230
packages/Longyi/Gift/src/Resources/views/admin/create.blade.php

@@ -1,230 +0,0 @@
-
-<x-admin::layouts>
-    <x-slot:title>
-        创建礼品卡
-    </x-slot>
-
-    <div class="flex justify-between items-center">
-        <p class="text-xl text-gray-800 font-bold">
-            创建礼品卡
-        </p>
-
-        <div class="flex gap-x-2.5">
-            <a
-                href="{{ route('admin.gift.index') }}"
-                class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-100"
-            >
-                返回
-            </a>
-
-            <button
-                type="submit"
-                form="gift-form"
-                class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
-            >
-                保存
-            </button>
-        </div>
-    </div>
-
-    <x-admin::form
-        :action="route('admin.gift.store')"
-        id="gift-form"
-    >
-        {{-- CSRF Token --}}
-        @csrf
-
-        <div class="flex flex-col gap-1.5 mt-2.5">
-            {{-- General Section --}}
-            <x-admin::accordion>
-                <x-slot:header>
-                    <p class="p-2.5 text-gray-800 text-base font-semibold">
-                        常规信息
-                    </p>
-                </x-slot>
-
-                <x-slot:content>
-                    <x-admin::form.control-group>
-                        <x-admin::form.control-group.label>
-                            卡金额
-                        </x-admin::form.control-group.label>
-
-                        <x-admin::form.control-group.control
-                            type="text"
-                            name="giftcard_amount"
-                            :value="old('giftcard_amount')"
-                            rules="numeric|min:0"
-                            label="卡金额"
-                            placeholder="0.00"
-                        />
-
-                        <x-admin::form.control-group.error
-                            control-name="giftcard_amount"
-                        />
-                    </x-admin::form.control-group>
-
-                    <x-admin::form.control-group>
-                        <x-admin::form.control-group.label>
-                            用户邮箱
-                        </x-admin::form.control-group.label>
-
-                        <x-admin::form.control-group.control
-                            type="text"
-                            name="customer_email_display"
-                            :value="old('customer_email_display')"
-                            rules="email"
-                            label="用户邮箱"
-                            placeholder="请输入用户邮箱"
-                            id="customer_email_create"
-                            class="customer-email-input"
-                        />
-
-                        <input type="hidden" name="customer_id" id="customer_id_create" value="{{ old('customer_id') }}" />
-
-                        <div id="email-validation-message-create" class="mt-1 text-sm"></div>
-
-                        <x-admin::form.control-group.error
-                            control-name="customer_email_display"
-                        />
-                    </x-admin::form.control-group>
-
-                    <x-admin::form.control-group>
-                        <x-admin::form.control-group.label class="required">
-                            过期日期
-                        </x-admin::form.control-group.label>
-
-                        <x-admin::form.control-group.control
-                            type="date"
-                            name="expirationdate"
-                            :value="old('expirationdate')"
-                            rules="required"
-                            label="过期日期"
-                        />
-
-                        <x-admin::form.control-group.error
-                            control-name="expirationdate"
-                        />
-                    </x-admin::form.control-group>
-
-                    <x-admin::form.control-group>
-                        <x-admin::form.control-group.label>
-                            状态
-                        </x-admin::form.control-group.label>
-
-                        <x-admin::form.control-group.control
-                            type="select"
-                            name="giftcard_status"
-                            :value="old('giftcard_status', 1)"
-                            label="状态"
-                        >
-                            <option value="1">未使用</option>
-                            <option value="2">已使用</option>
-                        </x-admin::form.control-group.control>
-
-                        <x-admin::form.control-group.error
-                            control-name="giftcard_status"
-                        />
-                    </x-admin::form.control-group>
-                </x-slot>
-            </x-admin::accordion>
-        </div>
-    </x-admin::form>
-
-    <script>
-        document.addEventListener('DOMContentLoaded', function() {
-            // 等待一小段时间确保所有组件都渲染完成
-            setTimeout(() => {
-                // 尝试多种方式获取邮箱输入框
-                const emailInput = document.getElementById('customer_email_create') ||
-                    document.querySelector('input[name="customer_email_display"]') ||
-                    document.querySelector('.customer-email-input') ||
-                    document.querySelector('input[placeholder="请输入用户邮箱"]');
-                const customerIdInput = document.getElementById('customer_id_create');
-                const validationMessage = document.getElementById('email-validation-message-create');
-                let debounceTimer;
-
-                console.log('=== Debug Info ===');
-                console.log('Email input:', emailInput);
-                console.log('Customer ID input:', customerIdInput);
-                console.log('Validation message:', validationMessage);
-
-                // 打印所有 input 元素,帮助调试
-                const allInputs = document.querySelectorAll('input');
-                console.log('All inputs:', Array.from(allInputs).map(input => ({
-                    name: input.name,
-                    id: input.id,
-                    class: input.className,
-                    placeholder: input.placeholder
-                })));
-
-                if (emailInput) {
-                    console.log('✓ Email input found:', emailInput);
-
-                    emailInput.addEventListener('blur', function() {
-                        console.log('Blur event triggered, value:', this.value);
-                        validateEmail(this.value.trim());
-                    });
-                } else {
-                    console.error('✗ Email input element not found!');
-                }
-
-                function validateEmail(email) {
-                    clearTimeout(debounceTimer);
-
-                    if (!email) {
-                        if (validationMessage) validationMessage.innerHTML = '';
-                        if (customerIdInput) customerIdInput.value = '';
-                        return;
-                    }
-
-                    debounceTimer = setTimeout(() => {
-                        console.log('Validating email:', email);
-                        if (validationMessage) {
-                            validationMessage.innerHTML = '<span class="text-gray-500">正在验证...</span>';
-                        }
-
-                        fetch("{{ route('admin.gift.validate_email') }}", {
-                            method: 'POST',
-                            headers: {
-                                'Content-Type': 'application/json',
-                                'X-CSRF-TOKEN': '{{ csrf_token() }}'
-                            },
-                            body: JSON.stringify({ email: email })
-                        })
-                            .then(response => {
-                                console.log('Response status:', response.status);
-                                return response.json();
-                            })
-                            .then(data => {
-                                console.log('Response data:', data);
-                                if (data.valid) {
-                                    if (validationMessage) {
-                                        validationMessage.innerHTML = `<span class="text-green-600">✓ ${data.message} (${data.customer_name})</span>`;
-                                    }
-                                    if (customerIdInput) {
-                                        customerIdInput.value = data.customer_id || '';
-                                    }
-                                } else {
-                                    if (validationMessage) {
-                                        validationMessage.innerHTML = `<span class="text-red-600">✗ ${data.message}</span>`;
-                                    }
-                                    if (customerIdInput) {
-                                        customerIdInput.value = '';
-                                    }
-                                }
-                            })
-                            .catch(error => {
-                                console.error('Validation error:', error);
-                                if (validationMessage) {
-                                    validationMessage.innerHTML = '<span class="text-red-600">✗ 验证失败,请稍后重试</span>';
-                                }
-                                if (customerIdInput) {
-                                    customerIdInput.value = '';
-                                }
-                            });
-                    }, 500);
-                }
-            }, 1000); // 延迟1秒执行,确保组件渲染完成
-        });
-    </script>
-</x-admin::layouts>

+ 414 - 20
packages/Longyi/Gift/src/Resources/views/admin/index.blade.php

@@ -1,25 +1,419 @@
 <x-admin::layouts>
 <x-admin::layouts>
     <x-slot:title>
     <x-slot:title>
-        礼品卡管理
+        @lang('gift::app.admin.title')
     </x-slot>
     </x-slot>
 
 
-    @if(session('success'))
-        <div class="mb-4 px-4 py-3 bg-green-500 text-white rounded-lg">
-            {{ session('success') }}
-        </div>
-    @endif
-
-    <div class="flex justify-between items-center">
-        <p class="text-xl text-gray-800 font-bold">
-            礼品卡列表
-        </p>
-
-        <a href="{{ route('admin.gift.create') }}">
-            <button class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
-                添加礼品卡
-            </button>
-        </a>
-    </div>
-
-    <x-admin::datagrid src="{{ route('admin.gift.index') }}"></x-admin::datagrid>
+    <v-gift-cards>
+        <!-- DataGrid Shimmer -->
+        <x-admin::shimmer.datagrid />
+    </v-gift-cards>
+
+    @pushOnce('scripts')
+        <script
+            type="text/x-template"
+            id="v-gift-cards-template"
+        >
+            <div>
+                <div class="flex items-center justify-between">
+                    <p class="text-xl font-bold text-gray-800 dark:text-white">
+                        @lang('gift::app.admin.title')
+                    </p>
+
+                    <div class="flex items-center gap-x-2.5">
+                        @if (bouncer()->hasPermission('gift.create'))
+                            <button
+                                type="button"
+                                class="primary-button"
+                                @click="selectedGift=0; resetForm(); $refs.giftUpdateOrCreateModal.toggle()"
+                            >
+                                @lang('gift::app.admin.index.create-btn')
+                            </button>
+                        @endif
+                    </div>
+                </div>
+
+                <x-admin::datagrid
+                    :src="route('admin.gift.index')"
+                    ref="datagrid"
+                >
+                    <template #body="{
+                        isLoading,
+                        available,
+                        applied,
+                        selectAll,
+                        sort,
+                        performAction
+                    }">
+                        <template v-if="isLoading">
+                            <x-admin::shimmer.datagrid.table.body />
+                        </template>
+
+                        <template v-else>
+                            <div
+                                v-for="record in available.records"
+                                class="row grid items-center gap-2.5 border-b px-4 py-4 text-gray-600 transition-all hover:bg-gray-50 dark:border-gray-800 dark:text-gray-300 dark:hover:bg-gray-950"
+                                :style="`grid-template-columns: repeat(${gridsCount}, minmax(0, 1fr))`"
+                            >
+                                <!-- Mass Actions Checkbox -->
+                                <p v-if="available.massActions.length">
+                                    <label :for="`mass_action_select_record_${record[available.meta.primary_column]}`">
+                                        <input
+                                            type="checkbox"
+                                            :name="`mass_action_select_record_${record[available.meta.primary_column]}`"
+                                            :value="record[available.meta.primary_column]"
+                                            :id="`mass_action_select_record_${record[available.meta.primary_column]}`"
+                                            class="peer hidden"
+                                            v-model="applied.massActions.indices"
+                                        >
+                                        <span class="icon-uncheckbox peer-checked:icon-checked cursor-pointer rounded-md text-2xl peer-checked:text-blue-600"></span>
+                                    </label>
+                                </p>
+
+                                <!-- ID -->
+                                <p>@{{ record.id }}</p>
+
+                                <!-- Card Amount -->
+                                <p>@{{ record.giftcard_amount }}</p>
+
+                                <!-- Used Amount -->
+                                <p>@{{ record.used_giftcard_amount }}</p>
+
+                                <!-- Remaining Amount -->
+                                <p>@{{ record.remaining_giftcard_amount }}</p>
+
+                                <!-- Customer Email -->
+                                <p v-html="record.customer_email"></p>
+
+                                <!-- Expiration Date -->
+                                <p>@{{ record.expirationdate }}</p>
+
+                                <!-- Status -->
+                                <p v-html="record.giftcard_status"></p>
+
+                                <!-- Notes -->
+                                <p>@{{ record.notes }}</p>
+
+                                <!-- Created At -->
+                                <p>@{{ record.created_at }}</p>
+
+                                <!-- Actions -->
+                                <div class="flex justify-end">
+                                    @if (bouncer()->hasPermission('gift.edit'))
+                                        <a @click="selectedGift=1; editModal(record.actions.find(action => action.index === 'edit')?.url)">
+                                            <span
+                                                :class="record.actions.find(action => action.index === 'edit')?.icon"
+                                                class="cursor-pointer rounded-md p-1.5 text-2xl transition-all hover:bg-gray-200 dark:hover:bg-gray-800 max-sm:place-self-center"
+                                            >
+                                            </span>
+                                        </a>
+                                    @endif
+
+                                    @if (bouncer()->hasPermission('gift.delete'))
+                                        <a @click="performAction(record.actions.find(action => action.index === 'delete'))">
+                                            <span
+                                                :class="record.actions.find(action => action.index === 'delete')?.icon"
+                                                class="cursor-pointer rounded-md p-1.5 text-2xl transition-all hover:bg-gray-200 dark:hover:bg-gray-800 max-sm:place-self-center"
+                                            >
+                                            </span>
+                                        </a>
+                                    @endif
+                                </div>
+                            </div>
+                        </template>
+                    </template>
+                </x-admin::datagrid>
+
+                <!-- Create / Edit Modal -->
+                <x-admin::form
+                    v-slot="{ meta, errors, handleSubmit }"
+                    as="div"
+                    ref="modalForm"
+                >
+                    <form
+                        @submit="handleSubmit($event, updateOrCreate)"
+                        ref="giftCreateForm"
+                    >
+                        <x-admin::modal ref="giftUpdateOrCreateModal">
+                            <x-slot:header>
+                                <p class="text-lg font-bold text-gray-800 dark:text-white">
+                                    <span v-if="selectedGift">
+                                        @lang('gift::app.admin.edit.title')
+                                    </span>
+                                    <span v-else>
+                                        @lang('gift::app.admin.create.title')
+                                    </span>
+                                </p>
+                            </x-slot>
+
+                            <x-slot:content>
+                                <x-admin::form.control-group.control
+                                    type="hidden"
+                                    name="id"
+                                    v-model="selectedGiftData.id"
+                                />
+
+                                <!-- 编辑模式:显示只读信息 + 可修改的过期时间和状态 -->
+                                <template v-if="selectedGift">
+                                    <!-- Gift Card Number (只读) -->
+                                    <div class="mb-4 rounded-md bg-gray-50 p-4 dark:bg-gray-800">
+                                        <div class="grid grid-cols-2 gap-4 text-sm">
+                                            <div>
+                                                <span class="text-gray-500">@lang('gift::app.admin.datagrid.giftcard-number')</span>
+                                                <p class="font-medium text-gray-800 dark:text-white">@{{ selectedGiftData.giftcard_number }}</p>
+                                            </div>
+                                            <div>
+                                                <span class="text-gray-500">@lang('gift::app.admin.create.giftcard-amount')</span>
+                                                <p class="font-medium text-gray-800 dark:text-white">@{{ selectedGiftData.giftcard_amount }}</p>
+                                            </div>
+                                            <div>
+                                                <span class="text-gray-500">@lang('gift::app.admin.datagrid.used-amount')</span>
+                                                <p class="font-medium text-gray-800 dark:text-white">@{{ selectedGiftData.used_giftcard_amount }}</p>
+                                            </div>
+                                            <div>
+                                                <span class="text-gray-500">@lang('gift::app.admin.datagrid.remaining-amount')</span>
+                                                <p class="font-medium text-gray-800 dark:text-white">@{{ selectedGiftData.remaining_giftcard_amount }}</p>
+                                            </div>
+                                            <div>
+                                                <span class="text-gray-500">用户邮箱</span>
+                                                <p class="font-medium text-gray-800 dark:text-white">@{{ selectedGiftData.customer_email || '未绑定' }}</p>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </template>
+
+                                <!-- 创建模式:显示金额和邮箱 -->
+                                <template v-else>
+                                    <!-- Card Amount -->
+                                    <x-admin::form.control-group>
+                                        <x-admin::form.control-group.label class="required">
+                                            @lang('gift::app.admin.create.giftcard-amount')
+                                        </x-admin::form.control-group.label>
+                                        <x-admin::form.control-group.control
+                                            type="text"
+                                            name="giftcard_amount"
+                                            rules="required|numeric|min:0"
+                                            v-model="selectedGiftData.giftcard_amount"
+                                            :label="trans('gift::app.admin.create.giftcard-amount')"
+                                            placeholder="0.00"
+                                        />
+                                        <x-admin::form.control-group.error control-name="giftcard_amount" />
+                                    </x-admin::form.control-group>
+
+                                    <!-- Customer Email -->
+                                    <x-admin::form.control-group>
+                                        <x-admin::form.control-group.label>
+                                            @lang('gift::app.admin.create.customer-email')
+                                        </x-admin::form.control-group.label>
+                                        <x-admin::form.control-group.control
+                                            type="text"
+                                            name="customer_email"
+                                            v-model="selectedGiftData.customer_email"
+                                            :label="trans('gift::app.admin.create.customer-email')"
+                                            placeholder="@lang('gift::app.admin.create.customer-email-placeholder')"
+                                        />
+                                        <input type="hidden" name="customer_id" v-model="selectedGiftData.customer_id" />
+                                        <div v-if="emailValidationMessage" v-html="emailValidationMessage" class="mt-1 text-sm"></div>
+                                        <x-admin::form.control-group.error control-name="customer_email" />
+                                        <x-admin::form.control-group.error control-name="customer_id" />
+                                    </x-admin::form.control-group>
+                                </template>
+
+                                <!-- Expiration Date (创建和编辑都显示) -->
+                                <x-admin::form.control-group>
+                                    <x-admin::form.control-group.label class="required">
+                                        @lang('gift::app.admin.create.expirationdate')
+                                    </x-admin::form.control-group.label>
+                                    <x-admin::form.control-group.control
+                                        type="datetime"
+                                        name="expirationdate"
+                                        rules="required"
+                                        v-model="selectedGiftData.expirationdate"
+                                        :label="trans('gift::app.admin.create.expirationdate')"
+                                    />
+                                    <x-admin::form.control-group.error control-name="expirationdate" />
+                                </x-admin::form.control-group>
+
+                                <!-- Status (创建和编辑都显示) -->
+                                <x-admin::form.control-group>
+                                    <x-admin::form.control-group.label>
+                                        @lang('gift::app.admin.create.giftcard-status')
+                                    </x-admin::form.control-group.label>
+                                    <x-admin::form.control-group.control
+                                        type="select"
+                                        name="giftcard_status"
+                                        v-model="selectedGiftData.giftcard_status"
+                                    >
+                                        <option value="1">@lang('gift::app.admin.create.unused')</option>
+                                        <option value="2">@lang('gift::app.admin.create.used')</option>
+                                    </x-admin::form.control-group.control>
+                                    <x-admin::form.control-group.error control-name="giftcard_status" />
+                                </x-admin::form.control-group>
+                            </x-slot>
+
+                            <x-slot:footer>
+                                <x-admin::button
+                                    button-type="button"
+                                    class="secondary-button mr-2"
+                                    :title="trans('gift::app.admin.create.back-btn')"
+                                    @click="$refs.giftUpdateOrCreateModal.close()"
+                                />
+                                <x-admin::button
+                                    button-type="submit"
+                                    class="primary-button"
+                                    :title="trans('gift::app.admin.create.save-btn')"
+                                    ::loading="isLoading"
+                                    ::disabled="isLoading"
+                                />
+                            </x-slot>
+                        </x-admin::modal>
+                    </form>
+                </x-admin::form>
+            </div>
+        </script>
+
+        <script type="module">
+            app.component('v-gift-cards', {
+                template: '#v-gift-cards-template',
+
+                data() {
+                    return {
+                        selectedGift: 0,
+                        selectedGiftData: {},
+                        isLoading: false,
+                        emailValidationMessage: '',
+                        emailDebounceTimer: null,
+                    }
+                },
+
+                watch: {
+                    'selectedGiftData.customer_email': {
+                        handler(newEmail) {
+                            // 清除上一次的定时器
+                            if (this.emailDebounceTimer) {
+                                clearTimeout(this.emailDebounceTimer);
+                            }
+                            // 编辑模式不触发验证
+                            if (this.selectedGift) {
+                                return;
+                            }
+                            // 500ms 防抖
+                            this.emailDebounceTimer = setTimeout(() => {
+                                this.validateEmail(newEmail);
+                            }, 500);
+                        },
+                        immediate: false
+                    }
+                },
+
+                computed: {
+                    gridsCount() {
+                        let count = this.$refs.datagrid.available.columns.length;
+                        if (this.$refs.datagrid.available.actions.length) {
+                            ++count;
+                        }
+                        if (this.$refs.datagrid.available.massActions.length) {
+                            ++count;
+                        }
+                        return count;
+                    },
+                },
+
+                methods: {
+                    updateOrCreate(params, { resetForm, setErrors }) {
+                        // 创建模式:检查邮箱是否已填写且验证通过
+                        if (!params.id && this.selectedGiftData.customer_email && !this.selectedGiftData.customer_id) {
+                            this.$emitter.emit('add-flash', { type: 'error', message: '请等待邮箱验证通过后再提交' });
+                            return;
+                        }
+
+                        this.isLoading = true;
+
+                        let formData = new FormData(this.$refs.giftCreateForm);
+
+                        if (params.id) {
+                            formData.append('_method', 'put');
+                        }
+
+                        this.$axios.post(
+                            params.id
+                                ? "{{ route('admin.gift.update') }}"
+                                : "{{ route('admin.gift.store') }}",
+                            formData
+                        )
+                            .then((response) => {
+                                this.isLoading = false;
+                                this.$emitter.emit('add-flash', { type: 'success', message: response.data.message });
+                                this.$refs.giftUpdateOrCreateModal.close();
+                                this.$refs.datagrid.get();
+                                resetForm();
+                                this.emailValidationMessage = '';
+                            })
+                            .catch(error => {
+                                this.isLoading = false;
+                                if (error.response.status == 422) {
+                                    setErrors(error.response.data.errors);
+                                }
+                            });
+                    },
+
+                    editModal(url) {
+                        this.$axios.get(url)
+                            .then((response) => {
+                                this.selectedGiftData = response.data.data;
+
+                                // 类型转换:确保 select 能正确匹配
+                                if (this.selectedGiftData.giftcard_status !== undefined) {
+                                    this.selectedGiftData.giftcard_status = String(this.selectedGiftData.giftcard_status);
+                                }
+
+                                // 格式化过期时间为 flat-picker 需要的格式 Y-m-d H:i:s
+                                if (this.selectedGiftData.expirationdate) {
+                                    this.selectedGiftData.expirationdate = this.selectedGiftData.expirationdate.replace('T', ' ').replace(/\.\d+Z$/, '');
+                                }
+
+                                this.emailValidationMessage = '';
+                                this.$refs.giftUpdateOrCreateModal.toggle();
+                            })
+                            .catch(error => this.$emitter.emit('add-flash', {
+                                type: 'error', message: error.response.data.message
+                            }));
+                    },
+
+                    validateEmail(email) {
+                        this.emailValidationMessage = '';
+
+                        if (!email) {
+                            this.selectedGiftData.customer_id = null;
+                            return;
+                        }
+
+                        this.emailValidationMessage = '<span class="text-gray-500">@lang("gift::app.admin.create.validating")</span>';
+
+                        this.$axios.post(
+                            "{{ route('admin.gift.validate_email') }}",
+                            { email: email }
+                        )
+                            .then((response) => {
+                                if (response.data.valid) {
+                                    this.emailValidationMessage = '<span class="text-green-600">' + response.data.message + ' (' + response.data.customer_name + ')</span>';
+                                    this.selectedGiftData.customer_id = response.data.customer_id;
+                                } else {
+                                    this.emailValidationMessage = '<span class="text-red-600">' + response.data.message + '</span>';
+                                    this.selectedGiftData.customer_id = null;
+                                }
+                            })
+                            .catch(error => {
+                                this.emailValidationMessage = '<span class="text-red-600">@lang("gift::app.admin.create.validation-failed")</span>';
+                                this.selectedGiftData.customer_id = null;
+                            });
+                    },
+
+                    resetForm() {
+                        this.selectedGiftData = {};
+                        this.emailValidationMessage = '';
+                    }
+                }
+            })
+        </script>
+    @endPushOnce
 </x-admin::layouts>
 </x-admin::layouts>

+ 5 - 4
packages/Longyi/Gift/src/Routes/admin-routes.php

@@ -6,13 +6,14 @@ use Longyi\Gift\Http\Controllers\Admin\GiftController;
 Route::group(['middleware' => ['web', 'admin'], 'prefix' => 'admin/gift'], function () {
 Route::group(['middleware' => ['web', 'admin'], 'prefix' => 'admin/gift'], function () {
     Route::controller(GiftController::class)->group(function () {
     Route::controller(GiftController::class)->group(function () {
         Route::get('', 'index')->name('admin.gift.index');
         Route::get('', 'index')->name('admin.gift.index');
-        // Create routes
-        Route::get('/create', 'create')->name('admin.gift.create');
+        // Create & Update
         Route::post('/store', 'store')->name('admin.gift.store');
         Route::post('/store', 'store')->name('admin.gift.store');
-        // Delete routes
+        Route::get('/edit/{id}', 'edit')->name('admin.gift.edit');
+        Route::put('/update', 'update')->name('admin.gift.update');
+        // Delete
         Route::delete('/{id}', 'destroy')->name('admin.gift.destroy');
         Route::delete('/{id}', 'destroy')->name('admin.gift.destroy');
         Route::post('/mass-delete', 'massDelete')->name('admin.gift.mass_delete');
         Route::post('/mass-delete', 'massDelete')->name('admin.gift.mass_delete');
-        //validate
+        // Validate
         Route::post('/validate-email', 'validateEmail')->name('admin.gift.validate_email');
         Route::post('/validate-email', 'validateEmail')->name('admin.gift.validate_email');
     });
     });
 });
 });