diff --git a/database/migrations/2025_01_01_000001_create_sys_user_table.php b/database/migrations/2025_01_01_000001_create_sys_user_table.php index 361e6eb..228d474 100644 --- a/database/migrations/2025_01_01_000001_create_sys_user_table.php +++ b/database/migrations/2025_01_01_000001_create_sys_user_table.php @@ -23,7 +23,7 @@ return new class extends Migration $table->integer('sex')->default(0)->comment('性别(男、女)'); $table->string('bio', 255)->default('')->nullable()->comment('个人简介'); $table->string('mobile', 20)->default('')->comment('手机号'); - $table->string('email', 50)->unique()->comment('邮箱'); + $table->string('email', 50)->default('')->comment('邮箱'); $table->timestamp('email_verified_at')->nullable(); $table->integer('dept_id')->default(0)->comment('部门ID'); $table->string('login_ip', 60)->default('')->comment('最后登录IP'); @@ -78,7 +78,7 @@ return new class extends Migration $table->comment('系统用户部门表'); }); } - + // 系统用户权限表 if (! Schema::hasTable('sys_rule')) { Schema::create('sys_rule', function (Blueprint $table) { diff --git a/database/migrations/2026_05_30_000005_create_merchant_tables.php b/database/migrations/2026_05_30_000005_create_merchant_tables.php index 785fbde..2fbe9ad 100644 --- a/database/migrations/2026_05_30_000005_create_merchant_tables.php +++ b/database/migrations/2026_05_30_000005_create_merchant_tables.php @@ -8,21 +8,6 @@ return new class extends Migration { public function up(): void { - // 商户分类 - if (! Schema::hasTable('merchant_categories')) { - Schema::create('merchant_categories', function (Blueprint $table) { - $table->id(); - $table->string('name', 50)->comment('分类名称'); - $table->string('slug', 50)->comment('标识'); - $table->string('description', 255)->default('')->comment('描述'); - $table->string('icon', 255)->default('')->comment('图标'); - $table->integer('sort')->default(0)->comment('排序'); - $table->tinyInteger('status')->default(1)->comment('状态:0=禁用,1=正常'); - $table->timestamps(); - $table->unique('slug'); - $table->comment('商户分类'); - }); - } // 商户/店铺 if (! Schema::hasTable('merchants')) { @@ -74,6 +59,22 @@ return new class extends Migration }); } + // 商户商品分类 + if (! Schema::hasTable('platform_categories')) { + Schema::create('platform_categories', function (Blueprint $table) { + $table->id(); + $table->string('name', 50)->comment('分类名称'); + $table->string('slug', 50)->comment('标识'); + $table->string('description', 255)->default('')->comment('描述'); + $table->string('icon', 255)->default('')->comment('图标'); + $table->integer('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态:0=禁用,1=正常'); + $table->timestamps(); + $table->unique('slug'); + $table->comment('商户分类'); + }); + } + // 商品 if (! Schema::hasTable('merchant_products')) { Schema::create('merchant_products', function (Blueprint $table) { diff --git a/database/seeders/SysRuleSeeder.php b/database/seeders/SysRuleSeeder.php index 80bc8a6..1e9fad6 100644 --- a/database/seeders/SysRuleSeeder.php +++ b/database/seeders/SysRuleSeeder.php @@ -675,6 +675,85 @@ class SysRuleSeeder extends Seeder ], ], ], + [ + 'type' => 'menu', + 'name' => '商户管理', + 'local' => 'menu.merchant_portal', + 'icon' => 'ShopOutlined', + 'key' => 'merchant.portal', + 'children' => [ + [ + 'type' => 'route', + 'name' => '商品列表', + 'key' => 'merchant.portal.product', + 'path' => '/merchant-portal/product', + 'local' => 'menu.merchant_portal.product', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.portal.product.query'], + ['type' => 'rule', 'name' => '新增商品', 'key' => 'merchant.portal.product.create'], + ['type' => 'rule', 'name' => '修改商品', 'key' => 'merchant.portal.product.update'], + ['type' => 'rule', 'name' => '删除商品', 'key' => 'merchant.portal.product.delete'], + ], + ], + [ + 'type' => 'route', + 'name' => '商品分类', + 'key' => 'merchant.portal.product-category', + 'path' => '/merchant-portal/product-category', + 'local' => 'menu.merchant_portal.product-category', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.portal.product-category.query'], + ['type' => 'rule', 'name' => '新增分类', 'key' => 'merchant.portal.product-category.create'], + ['type' => 'rule', 'name' => '修改分类', 'key' => 'merchant.portal.product-category.update'], + ['type' => 'rule', 'name' => '删除分类', 'key' => 'merchant.portal.product-category.delete'], + ], + ], + [ + 'type' => 'route', + 'name' => '订单列表', + 'key' => 'merchant.portal.order', + 'path' => '/merchant-portal/order', + 'local' => 'menu.merchant_portal.order', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.portal.order.query'], + ], + ], + [ + 'type' => 'route', + 'name' => '交易流水', + 'key' => 'merchant.portal.payment', + 'path' => '/merchant-portal/payment', + 'local' => 'menu.merchant_portal.payment', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.portal.payment.query'], + ], + ], + [ + 'type' => 'route', + 'name' => '打印机管理', + 'key' => 'merchant.portal.printer', + 'path' => '/merchant-portal/printer', + 'local' => 'menu.merchant_portal.printer', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.portal.printer.query'], + ['type' => 'rule', 'name' => '新增打印机', 'key' => 'merchant.portal.printer.create'], + ['type' => 'rule', 'name' => '修改打印机', 'key' => 'merchant.portal.printer.update'], + ['type' => 'rule', 'name' => '删除打印机', 'key' => 'merchant.portal.printer.delete'], + ], + ], + [ + 'type' => 'route', + 'name' => '商户信息', + 'key' => 'merchant.portal.store', + 'path' => '/merchant-portal/store', + 'local' => 'menu.merchant_portal.store', + 'children' => [ + ['type' => 'rule', 'name' => '查看信息', 'key' => 'merchant.portal.store.query'], + ['type' => 'rule', 'name' => '修改信息', 'key' => 'merchant.portal.store.update'], + ], + ], + ], + ], [ 'type' => 'route', 'name' => 'XinAdmin', @@ -695,6 +774,34 @@ class SysRuleSeeder extends Seeder ->where('status', 1) ->select(DB::raw('1 as role_id'), 'id') ); + + // 创建"商户"角色并分配商户门户权限 + $merchantRoleExists = DB::table('sys_role')->where('name', '商户')->exists(); + if (! $merchantRoleExists) { + $merchantRoleId = DB::table('sys_role')->insertGetId([ + 'name' => '商户', + 'status' => 1, + 'created_at' => now(), + 'updated_at' => now(), + ]); + + // 为商户角色分配 merchant.portal.* 及 dashboard 权限 + $portalRuleIds = DB::table('sys_rule') + ->where('status', 1) + ->where(function ($query) { + $query->where('key', 'LIKE', 'merchant.portal.%') + ->orWhere('key', 'dashboard') + ->orWhere('key', 'dashboard.analysis'); + }) + ->pluck('id'); + + $insertData = $portalRuleIds->map(fn ($ruleId) => [ + 'role_id' => $merchantRoleId, + 'rule_id' => $ruleId, + ])->toArray(); + + DB::table('sys_role_rule')->insert($insertData); + } } /** diff --git a/modules/AnnoRoute/RouteRegisterService.php b/modules/AnnoRoute/RouteRegisterService.php index d4acbdc..d8ea8b0 100644 --- a/modules/AnnoRoute/RouteRegisterService.php +++ b/modules/AnnoRoute/RouteRegisterService.php @@ -108,9 +108,10 @@ class RouteRegisterService } else { $authMiddleware[] = 'authGuard'; } + if (is_string($authorize) && !empty($abilitiesPrefix)) { $authMiddleware[] = 'abilities:' . $abilitiesPrefix . '.' . $authorize; - } else { + } else if ($authorize !== true) { $authMiddleware[] = 'abilities:' . $authorize; } } diff --git a/modules/Common/Console/Commands/GenerateRouteHelperCommand.php b/modules/Common/Console/Commands/GenerateRouteHelperCommand.php index 6be08ce..cc496ce 100644 --- a/modules/Common/Console/Commands/GenerateRouteHelperCommand.php +++ b/modules/Common/Console/Commands/GenerateRouteHelperCommand.php @@ -167,8 +167,8 @@ class GenerateRouteHelperCommand extends Command if (is_string($authorize) && !empty($abilitiesPrefix)) { $authMiddleware[] = 'abilities:' . $abilitiesPrefix . '.' . $authorize; - } else { - $authMiddleware[] = 'abilities:' . (is_string($authorize) ? $authorize : ''); + } else if($authorize !== true) { + $authMiddleware[] = 'abilities:' . $authorize; } return $authMiddleware; diff --git a/modules/Merchant/Http/Controllers/MerchantStoreController.php b/modules/Merchant/Http/Controllers/MerchantStoreController.php index da71953..d2ff367 100644 --- a/modules/Merchant/Http/Controllers/MerchantStoreController.php +++ b/modules/Merchant/Http/Controllers/MerchantStoreController.php @@ -4,6 +4,8 @@ namespace Modules\Merchant\Http\Controllers; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Hash; use Modules\AnnoRoute\Attribute\DeleteRoute; use Modules\AnnoRoute\Attribute\GetRoute; use Modules\AnnoRoute\Attribute\PostRoute; @@ -12,6 +14,7 @@ use Modules\AnnoRoute\Attribute\RequestAttribute; use Modules\Common\Http\Controllers\BaseController; use Modules\Merchant\Http\Requests\MerchantStoreFormRequest; use Modules\Merchant\Models\MerchantStoreModel; +use Modules\SystemUser\Models\SysUserModel; #[RequestAttribute('/merchant/store', 'merchant.store')] class MerchantStoreController extends BaseController @@ -38,8 +41,44 @@ class MerchantStoreController extends BaseController #[PostRoute(authorize: 'create')] public function create(MerchantStoreFormRequest $request): JsonResponse { - MerchantStoreModel::create($request->validated()); - return $this->success(); + $data = $request->validated(); + + // 检查用户名是否已存在 + $username = $data['username']; + if (SysUserModel::where('username', $username)->exists()) { + return $this->error('用户名已存在'); + } + + DB::beginTransaction(); + try { + // 创建管理员用户 + $user = SysUserModel::create([ + 'username' => $username, + 'password' => Hash::make($data['password']), + 'nickname' => $data['name'] ?? $username, + 'status' => 1, + ]); + + // 分配"商户"角色 + $merchantRole = DB::table('sys_role')->where('name', '商户')->first(); + if ($merchantRole) { + DB::table('sys_user_role')->insert([ + 'user_id' => $user->id, + 'role_id' => $merchantRole->id, + ]); + } + + // 创建商户,关联用户 + $data['user_id'] = $user->id; + unset($data['username'], $data['password']); + MerchantStoreModel::create($data); + + DB::commit(); + return $this->success(); + } catch (\Exception $e) { + DB::rollBack(); + return $this->error('创建商户失败:' . $e->getMessage()); + } } #[PutRoute( @@ -53,7 +92,11 @@ class MerchantStoreController extends BaseController if (empty($model)) { return $this->error('商户不存在'); } - $model->update($request->validated()); + + $data = $request->validated(); + // 更新时不允许通过此接口修改 username/password/user_id + unset($data['username'], $data['password'], $data['user_id']); + $model->update($data); return $this->success(); } diff --git a/modules/Merchant/Http/Controllers/Portal/MerchantAuthTrait.php b/modules/Merchant/Http/Controllers/Portal/MerchantAuthTrait.php new file mode 100644 index 0000000..1174fe0 --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/MerchantAuthTrait.php @@ -0,0 +1,46 @@ + merchants.user_id 关联获取 + */ + public function getMerchantId(): int + { + if ($this->_merchantId === null) { + $userId = Auth::id(); + $merchant = MerchantStoreModel::where('user_id', $userId)->first(['id']); + + if (!$merchant) { + $this->throwError('商户信息不存在,请联系管理员'); + } + + $this->_merchantId = $merchant->id; + } + + return $this->_merchantId; + } + + /** + * 获取当前登录商户的完整模型 + */ + public function getMerchant(): MerchantStoreModel + { + $userId = Auth::id(); + $merchant = MerchantStoreModel::where('user_id', $userId)->first(); + + if (!$merchant) { + $this->throwError('商户信息不存在,请联系管理员'); + } + + return $merchant; + } +} diff --git a/modules/Merchant/Http/Controllers/Portal/OrderController.php b/modules/Merchant/Http/Controllers/Portal/OrderController.php new file mode 100644 index 0000000..007fe6e --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/OrderController.php @@ -0,0 +1,36 @@ + '=', + 'pay_status' => '=', + 'pay_type' => '=', + ]; + + protected array $quickSearchField = ['order_no']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = OrderModel::where('merchant_id', $this->getMerchantId()); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } +} diff --git a/modules/Merchant/Http/Controllers/Portal/PaymentController.php b/modules/Merchant/Http/Controllers/Portal/PaymentController.php new file mode 100644 index 0000000..2704714 --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/PaymentController.php @@ -0,0 +1,40 @@ + '=', + 'status' => '=', + ]; + + protected array $quickSearchField = ['payment_no']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $merchantId = $this->getMerchantId(); + + $query = OrderPaymentModel::whereHas('order', function ($q) use ($merchantId) { + $q->where('merchant_id', $merchantId); + }); + + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } +} diff --git a/modules/Merchant/Http/Controllers/Portal/PrinterController.php b/modules/Merchant/Http/Controllers/Portal/PrinterController.php new file mode 100644 index 0000000..5771de0 --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/PrinterController.php @@ -0,0 +1,82 @@ + '=', + 'brand' => '=', + ]; + + protected array $quickSearchField = ['name']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = PrinterModel::where('merchant_id', $this->getMerchantId()); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[PostRoute(authorize: 'create')] + public function create(PrinterFormRequest $request): JsonResponse + { + $data = $request->validated(); + $data['merchant_id'] = $this->getMerchantId(); + PrinterModel::create($data); + return $this->success(); + } + + #[PutRoute( + route: '/{id}', + authorize: 'update', + where: ['id' => '[0-9]+'] + )] + public function update(int $id, PrinterFormRequest $request): JsonResponse + { + $model = PrinterModel::where('id', $id) + ->where('merchant_id', $this->getMerchantId()) + ->first(); + if (empty($model)) { + return $this->error('打印机不存在'); + } + $model->update($request->validated()); + return $this->success(); + } + + #[DeleteRoute( + route: '/{id}', + authorize: 'delete', + where: ['id' => '[0-9]+'] + )] + public function delete(int $id): JsonResponse + { + $model = PrinterModel::where('id', $id) + ->where('merchant_id', $this->getMerchantId()) + ->first(); + if (empty($model)) { + return $this->error('打印机不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Controllers/Portal/ProductCategoryController.php b/modules/Merchant/Http/Controllers/Portal/ProductCategoryController.php new file mode 100644 index 0000000..8279a95 --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/ProductCategoryController.php @@ -0,0 +1,82 @@ + '=', + 'parent_id' => '=', + ]; + + protected array $quickSearchField = ['name']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = ProductCategoryModel::where('merchant_id', $this->getMerchantId()); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[PostRoute(authorize: 'create')] + public function create(ProductCategoryFormRequest $request): JsonResponse + { + $data = $request->validated(); + $data['merchant_id'] = $this->getMerchantId(); + ProductCategoryModel::create($data); + return $this->success(); + } + + #[PutRoute( + route: '/{id}', + authorize: 'update', + where: ['id' => '[0-9]+'] + )] + public function update(int $id, ProductCategoryFormRequest $request): JsonResponse + { + $model = ProductCategoryModel::where('id', $id) + ->where('merchant_id', $this->getMerchantId()) + ->first(); + if (empty($model)) { + return $this->error('分类不存在'); + } + $model->update($request->validated()); + return $this->success(); + } + + #[DeleteRoute( + route: '/{id}', + authorize: 'delete', + where: ['id' => '[0-9]+'] + )] + public function delete(int $id): JsonResponse + { + $model = ProductCategoryModel::where('id', $id) + ->where('merchant_id', $this->getMerchantId()) + ->first(); + if (empty($model)) { + return $this->error('分类不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Controllers/Portal/ProductController.php b/modules/Merchant/Http/Controllers/Portal/ProductController.php new file mode 100644 index 0000000..f6d1ceb --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/ProductController.php @@ -0,0 +1,83 @@ + '=', + 'is_recommend' => '=', + 'category_id' => '=', + ]; + + protected array $quickSearchField = ['name']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = MerchantProductModel::where('merchant_id', $this->getMerchantId()); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[PostRoute(authorize: 'create')] + public function create(ProductFormRequest $request): JsonResponse + { + $data = $request->validated(); + $data['merchant_id'] = $this->getMerchantId(); + MerchantProductModel::create($data); + return $this->success(); + } + + #[PutRoute( + route: '/{id}', + authorize: 'update', + where: ['id' => '[0-9]+'] + )] + public function update(int $id, ProductFormRequest $request): JsonResponse + { + $model = MerchantProductModel::where('id', $id) + ->where('merchant_id', $this->getMerchantId()) + ->first(); + if (empty($model)) { + return $this->error('商品不存在'); + } + $model->update($request->validated()); + return $this->success(); + } + + #[DeleteRoute( + route: '/{id}', + authorize: 'delete', + where: ['id' => '[0-9]+'] + )] + public function delete(int $id): JsonResponse + { + $model = MerchantProductModel::where('id', $id) + ->where('merchant_id', $this->getMerchantId()) + ->first(); + if (empty($model)) { + return $this->error('商品不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Controllers/Portal/StoreController.php b/modules/Merchant/Http/Controllers/Portal/StoreController.php new file mode 100644 index 0000000..7b0cf0c --- /dev/null +++ b/modules/Merchant/Http/Controllers/Portal/StoreController.php @@ -0,0 +1,31 @@ +getMerchant(); + return $this->success($merchant->toArray()); + } + + #[PutRoute(authorize: 'update')] + public function update(StoreFormRequest $request): JsonResponse + { + $merchant = $this->getMerchant(); + $merchant->update($request->validated()); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Requests/MerchantStoreFormRequest.php b/modules/Merchant/Http/Requests/MerchantStoreFormRequest.php index 68efd4c..1d6cb99 100644 --- a/modules/Merchant/Http/Requests/MerchantStoreFormRequest.php +++ b/modules/Merchant/Http/Requests/MerchantStoreFormRequest.php @@ -10,8 +10,10 @@ class MerchantStoreFormRequest extends BaseFormRequest public function rules(): array { - return [ - 'user_id' => 'required|integer', + $rules = [ + 'user_id' => 'nullable|integer', + 'username' => 'nullable|max:50', + 'password' => 'nullable|min:6|max:50', 'category_id' => 'nullable|integer', 'name' => 'required|max:50', 'logo' => 'nullable|max:255', @@ -30,13 +32,23 @@ class MerchantStoreFormRequest extends BaseFormRequest 'status' => 'nullable|integer|in:0,1,2,3', 'reject_reason' => 'nullable|max:255', ]; + + // 新建时用户名和密码必填 + if (! $this->isUpdate()) { + $rules['username'] = 'required|max:50'; + $rules['password'] = 'required|min:6|max:50'; + } + + return $rules; } public function messages(): array { return [ 'name.required' => '店铺名称不能为空', - 'user_id.required' => '所属用户不能为空', + 'username.required' => '登录用户名不能为空', + 'password.required' => '登录密码不能为空', + 'password.min' => '登录密码不能少于6位', ]; } } diff --git a/modules/Merchant/Http/Requests/Portal/PrinterFormRequest.php b/modules/Merchant/Http/Requests/Portal/PrinterFormRequest.php new file mode 100644 index 0000000..67bb59d --- /dev/null +++ b/modules/Merchant/Http/Requests/Portal/PrinterFormRequest.php @@ -0,0 +1,30 @@ + 'required|max:50', + 'brand' => 'nullable|max:30', + 'model' => 'nullable|max:50', + 'device_no' => 'nullable|max:100', + 'secret_key' => 'nullable|max:100', + 'status' => 'nullable|integer|in:0,1', + 'config' => 'nullable', + ]; + } + + public function messages(): array + { + return [ + 'name.required' => '打印机名称不能为空', + ]; + } +} diff --git a/modules/Merchant/Http/Requests/Portal/ProductCategoryFormRequest.php b/modules/Merchant/Http/Requests/Portal/ProductCategoryFormRequest.php new file mode 100644 index 0000000..589d592 --- /dev/null +++ b/modules/Merchant/Http/Requests/Portal/ProductCategoryFormRequest.php @@ -0,0 +1,30 @@ + 'nullable|integer', + 'name' => 'required|max:100', + 'slug' => 'nullable|max:100', + 'description' => 'nullable', + 'icon' => 'nullable|max:255', + 'sort' => 'nullable|integer', + 'status' => 'nullable|integer|in:0,1', + ]; + } + + public function messages(): array + { + return [ + 'name.required' => '分类名称不能为空', + ]; + } +} diff --git a/modules/Merchant/Http/Requests/Portal/ProductFormRequest.php b/modules/Merchant/Http/Requests/Portal/ProductFormRequest.php new file mode 100644 index 0000000..9163e45 --- /dev/null +++ b/modules/Merchant/Http/Requests/Portal/ProductFormRequest.php @@ -0,0 +1,35 @@ + 'nullable|integer', + 'name' => 'required|max:100', + 'description' => 'nullable', + 'images' => 'nullable|array', + 'price' => 'required|numeric', + 'original_price' => 'nullable|numeric', + 'stock' => 'nullable|integer', + 'unit' => 'nullable|max:20', + 'status' => 'nullable|integer|in:0,1', + 'is_recommend' => 'nullable|integer|in:0,1', + 'sort' => 'nullable|integer', + ]; + } + + public function messages(): array + { + return [ + 'name.required' => '商品名称不能为空', + 'price.required' => '售价不能为空', + ]; + } +} diff --git a/modules/Merchant/Http/Requests/Portal/StoreFormRequest.php b/modules/Merchant/Http/Requests/Portal/StoreFormRequest.php new file mode 100644 index 0000000..c7394d8 --- /dev/null +++ b/modules/Merchant/Http/Requests/Portal/StoreFormRequest.php @@ -0,0 +1,37 @@ + 'required|max:50', + 'logo' => 'nullable|max:255', + 'banner' => 'nullable|max:255', + 'description' => 'nullable', + 'contact_name' => 'nullable|max:30', + 'contact_mobile' => 'nullable|max:20', + 'province' => 'nullable|max:30', + 'city' => 'nullable|max:30', + 'district' => 'nullable|max:30', + 'address' => 'nullable|max:255', + 'latitude' => 'nullable|max:20', + 'longitude' => 'nullable|max:20', + 'business_hours' => 'nullable|max:100', + 'min_price' => 'nullable|numeric', + ]; + } + + public function messages(): array + { + return [ + 'name.required' => '店铺名称不能为空', + ]; + } +} diff --git a/modules/Merchant/Models/MerchantCategoryModel.php b/modules/Merchant/Models/MerchantCategoryModel.php index 88b1d9c..104c755 100644 --- a/modules/Merchant/Models/MerchantCategoryModel.php +++ b/modules/Merchant/Models/MerchantCategoryModel.php @@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model; class MerchantCategoryModel extends Model { - protected $table = 'merchant_categories'; + protected $table = 'platform_categories'; protected $fillable = [ 'name', diff --git a/modules/Merchant/Models/OrderPaymentModel.php b/modules/Merchant/Models/OrderPaymentModel.php index 29b14c4..457dd53 100644 --- a/modules/Merchant/Models/OrderPaymentModel.php +++ b/modules/Merchant/Models/OrderPaymentModel.php @@ -3,6 +3,7 @@ namespace Modules\Merchant\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class OrderPaymentModel extends Model { @@ -30,4 +31,9 @@ class OrderPaymentModel extends Model 'paid_at' => 'datetime', 'refund_at' => 'datetime', ]; + + public function order(): BelongsTo + { + return $this->belongsTo(OrderModel::class, 'order_id', 'id'); + } } diff --git a/routes/api.php b/routes/api.php index 799f827..9379d3d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -8,17 +8,253 @@ Route::get('/', function () { // IndexController Route::controller(App\Http\Controllers\IndexController::class)->prefix('api')->group(function () { - Route::get('/index', 'index')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); - Route::post('/login', 'login')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); - Route::post('/register', 'register')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); + Route::get('/index', 'index')->middleware(['auth:sanctum', 'authGuard:users']); + Route::post('/login', 'login')->middleware(['auth:sanctum', 'authGuard:users']); + Route::post('/register', 'register')->middleware(['auth:sanctum', 'authGuard:users']); +}); + +// PaymentTransactionController +Route::controller(App\Http\Controllers\PaymentTransactionController::class)->prefix('finance/payment-transaction')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:finance.transaction.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:finance.transaction.delete']); }); // UserController Route::controller(App\Http\Controllers\UserController::class)->prefix('api/user')->group(function () { - Route::get('/', 'getUserInfo')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); - Route::post('/logout', 'logout')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); - Route::put('/', 'setUserInfo')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); - Route::post('/setPwd', 'setPassword')->middleware(['auth:sanctum', 'authGuard:users', 'abilities:']); + Route::get('/', 'getUserInfo')->middleware(['auth:sanctum', 'authGuard:users']); + Route::post('/logout', 'logout')->middleware(['auth:sanctum', 'authGuard:users']); + Route::put('/', 'setUserInfo')->middleware(['auth:sanctum', 'authGuard:users']); + Route::post('/setPwd', 'setPassword')->middleware(['auth:sanctum', 'authGuard:users']); +}); + +// ForumCategoryController +Route::controller(Modules\Forum\Http\Controllers\ForumCategoryController::class)->prefix('forum/category')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.category.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.category.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.category.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.category.delete']); +}); + +// ForumCommentController +Route::controller(Modules\Forum\Http\Controllers\ForumCommentController::class)->prefix('forum/comment')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.comment.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.comment.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.comment.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.comment.delete']); +}); + +// ForumPostController +Route::controller(Modules\Forum\Http\Controllers\ForumPostController::class)->prefix('forum/post')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post.delete']); +}); + +// ForumPostFavoriteController +Route::controller(Modules\Forum\Http\Controllers\ForumPostFavoriteController::class)->prefix('forum/post-favorite')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post-favorite.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post-favorite.delete']); +}); + +// ForumPostLikeController +Route::controller(Modules\Forum\Http\Controllers\ForumPostLikeController::class)->prefix('forum/post-like')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post-like.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.post-like.delete']); +}); + +// ForumSensitiveWordController +Route::controller(Modules\Forum\Http\Controllers\ForumSensitiveWordController::class)->prefix('forum/sensitive-word')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.sensitive-word.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.sensitive-word.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.sensitive-word.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:forum.sensitive-word.delete']); +}); + +// MemberAddressController +Route::controller(Modules\Member\Http\Controllers\MemberAddressController::class)->prefix('member/address')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.address.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.address.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.address.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.address.delete']); +}); + +// MemberUserController +Route::controller(Modules\Member\Http\Controllers\MemberUserController::class)->prefix('member/user')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.user.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.user.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.user.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:member.user.delete']); +}); + +// MerchantCategoryController +Route::controller(Modules\Merchant\Http\Controllers\MerchantCategoryController::class)->prefix('merchant/category')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.category.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.category.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.category.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.category.delete']); +}); + +// MerchantProductController +Route::controller(Modules\Merchant\Http\Controllers\MerchantProductController::class)->prefix('merchant/product')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product.delete']); +}); + +// MerchantStoreController +Route::controller(Modules\Merchant\Http\Controllers\MerchantStoreController::class)->prefix('merchant/store')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.store.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.store.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.store.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.store.delete']); +}); + +// MerchantWithdrawalController +Route::controller(Modules\Merchant\Http\Controllers\MerchantWithdrawalController::class)->prefix('merchant/withdrawal')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.withdrawal.query']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.withdrawal.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.withdrawal.delete']); +}); + +// OrderController +Route::controller(Modules\Merchant\Http\Controllers\OrderController::class)->prefix('merchant/order')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.order.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.order.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.order.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.order.delete']); +}); + +// OrderPaymentController +Route::controller(Modules\Merchant\Http\Controllers\OrderPaymentController::class)->prefix('merchant/order-payment')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.order_payment.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.order_payment.delete']); +}); + +// OrderRefundController +Route::controller(Modules\Merchant\Http\Controllers\OrderRefundController::class)->prefix('merchant/refund')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.refund.query']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.refund.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.refund.delete']); +}); + +// OrderController +Route::controller(Modules\Merchant\Http\Controllers\Portal\OrderController::class)->prefix('merchant-portal/order')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.order.query']); +}); + +// PaymentController +Route::controller(Modules\Merchant\Http\Controllers\Portal\PaymentController::class)->prefix('merchant-portal/payment')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.payment.query']); +}); + +// PrinterController +Route::controller(Modules\Merchant\Http\Controllers\Portal\PrinterController::class)->prefix('merchant-portal/printer')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.printer.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.printer.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.printer.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.printer.delete']); +}); + +// ProductCategoryController +Route::controller(Modules\Merchant\Http\Controllers\Portal\ProductCategoryController::class)->prefix('merchant-portal/product-category')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product-category.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product-category.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product-category.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product-category.delete']); +}); + +// ProductController +Route::controller(Modules\Merchant\Http\Controllers\Portal\ProductController::class)->prefix('merchant-portal/product')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.product.delete']); +}); + +// StoreController +Route::controller(Modules\Merchant\Http\Controllers\Portal\StoreController::class)->prefix('merchant-portal/store')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.store.query']); + Route::put('/', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.portal.store.update']); +}); + +// PrinterController +Route::controller(Modules\Merchant\Http\Controllers\PrinterController::class)->prefix('merchant/printer')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.printer.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.printer.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.printer.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.printer.delete']); +}); + +// PrintTaskController +Route::controller(Modules\Merchant\Http\Controllers\PrintTaskController::class)->prefix('merchant/print-task')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.print-task.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.print-task.delete']); +}); + +// ProductCategoryController +Route::controller(Modules\Merchant\Http\Controllers\ProductCategoryController::class)->prefix('merchant/product-category')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product-category.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product-category.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product-category.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:merchant.product-category.delete']); +}); + +// DepositPaymentController +Route::controller(Modules\Runner\Http\Controllers\DepositPaymentController::class)->prefix('runner/deposit-payment')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.deposit_payment.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.deposit_payment.delete']); +}); + +// RunnerApplicationController +Route::controller(Modules\Runner\Http\Controllers\RunnerApplicationController::class)->prefix('runner/application')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.application.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.application.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.application.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.application.delete']); +}); + +// RunnerComplaintAppealController +Route::controller(Modules\Runner\Http\Controllers\RunnerComplaintAppealController::class)->prefix('runner/complaint-appeal')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint_appeal.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint_appeal.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint_appeal.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint_appeal.delete']); +}); + +// RunnerComplaintController +Route::controller(Modules\Runner\Http\Controllers\RunnerComplaintController::class)->prefix('runner/complaint')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.complaint.delete']); +}); + +// RunnerCreditLogController +Route::controller(Modules\Runner\Http\Controllers\RunnerCreditLogController::class)->prefix('runner/credit-log')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.credit_log.query']); +}); + +// RunnerDepositController +Route::controller(Modules\Runner\Http\Controllers\RunnerDepositController::class)->prefix('runner/deposit')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.deposit.query']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.deposit.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.deposit.delete']); +}); + +// RunnerWithdrawalController +Route::controller(Modules\Runner\Http\Controllers\RunnerWithdrawalController::class)->prefix('runner/withdrawal')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.withdrawal.query']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.withdrawal.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.withdrawal.delete']); +}); + +// RunnerWorkerController +Route::controller(Modules\Runner\Http\Controllers\RunnerWorkerController::class)->prefix('runner/worker')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.worker.query']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.worker.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:runner.worker.delete']); }); // AgentController @@ -135,13 +371,13 @@ Route::controller(Modules\SystemTool\Http\Controllers\SysStorageController::clas // IndexController Route::controller(Modules\SystemUser\Http\Controllers\IndexController::class)->prefix('system')->group(function () { Route::post('/login', 'login')->middleware(['login_log']); - Route::post('/logout', 'logout')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); - Route::get('/info', 'info')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); - Route::get('/menu', 'menu')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); - Route::put('/updateInfo', 'updateInfo')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); - Route::put('/updatePassword', 'updatePassword')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); - Route::post('/uploadAvatar', 'uploadAvatar')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); - Route::get('/loginRecord', 'loginRecord')->middleware(['auth:sanctum', 'authGuard', 'abilities:']); + Route::post('/logout', 'logout')->middleware(['auth:sanctum', 'authGuard']); + Route::get('/info', 'info')->middleware(['auth:sanctum', 'authGuard']); + Route::get('/menu', 'menu')->middleware(['auth:sanctum', 'authGuard']); + Route::put('/updateInfo', 'updateInfo')->middleware(['auth:sanctum', 'authGuard']); + Route::put('/updatePassword', 'updatePassword')->middleware(['auth:sanctum', 'authGuard']); + Route::post('/uploadAvatar', 'uploadAvatar')->middleware(['auth:sanctum', 'authGuard']); + Route::get('/loginRecord', 'loginRecord')->middleware(['auth:sanctum', 'authGuard']); }); // SysDeptController @@ -186,3 +422,34 @@ Route::controller(Modules\SystemUser\Http\Controllers\SysUserController::class)- Route::get('/role', 'role')->middleware(['auth:sanctum', 'authGuard', 'abilities:system.user.role']); Route::get('/dept', 'dept')->middleware(['auth:sanctum', 'authGuard', 'abilities:system.user.dept']); }); + +// TaskBidController +Route::controller(Modules\Task\Http\Controllers\TaskBidController::class)->prefix('task/bid')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.bid.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.bid.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.bid.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.bid.delete']); +}); + +// TaskCategoryController +Route::controller(Modules\Task\Http\Controllers\TaskCategoryController::class)->prefix('task/category')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.category.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.category.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.category.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.category.delete']); +}); + +// TaskOrderController +Route::controller(Modules\Task\Http\Controllers\TaskOrderController::class)->prefix('task/order')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.order.query']); + Route::post('/', 'create')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.order.create']); + Route::put('/{id}', 'update')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.order.update']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.order.delete']); +}); + +// TaskPaymentController +Route::controller(Modules\Task\Http\Controllers\TaskPaymentController::class)->prefix('task/payment')->group(function () { + Route::get('/', 'query')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.payment.query']); + Route::get('/order/{orderId}', 'getByOrderId')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.payment.query']); + Route::delete('/{id}', 'delete')->middleware(['auth:sanctum', 'authGuard', 'abilities:task.payment.delete']); +}); diff --git a/web/locales/en_US/index.ts b/web/locales/en_US/index.ts index 9aa9b43..79b20f3 100644 --- a/web/locales/en_US/index.ts +++ b/web/locales/en_US/index.ts @@ -40,6 +40,13 @@ import merchantPrintTask from "./merchant/print-task"; import merchantOrder from "./merchant/order"; import merchantRefund from "./merchant/refund"; +import merchantPortalProduct from "./merchant-portal/product"; +import merchantPortalProductCategory from "./merchant-portal/product-category"; +import merchantPortalOrder from "./merchant-portal/order"; +import merchantPortalPayment from "./merchant-portal/payment"; +import merchantPortalPrinter from "./merchant-portal/printer"; +import merchantPortalStore from "./merchant-portal/store"; + import taskCategory from "./task/category"; import taskOrderErrand from "./task/order-errand"; import taskOrderPickup from "./task/order-pickup"; @@ -104,6 +111,12 @@ export default { ...merchantPrintTask, ...merchantOrder, ...merchantRefund, + ...merchantPortalProduct, + ...merchantPortalProductCategory, + ...merchantPortalOrder, + ...merchantPortalPayment, + ...merchantPortalPrinter, + ...merchantPortalStore, ...taskCategory, ...taskOrderErrand, ...taskOrderPickup, diff --git a/web/locales/en_US/menu.ts b/web/locales/en_US/menu.ts index 45df323..6cfbea5 100644 --- a/web/locales/en_US/menu.ts +++ b/web/locales/en_US/menu.ts @@ -85,4 +85,11 @@ export default { "menu.finance.order_payment": "Order Payments", "menu.finance.merchant_withdrawal": "Merchant Withdrawals", "menu.xin-admin": "XinAdmin", + "menu.merchant_portal": "Merchant Portal", + "menu.merchant_portal.product": "Products", + "menu.merchant_portal.product-category": "Categories", + "menu.merchant_portal.order": "Orders", + "menu.merchant_portal.payment": "Transactions", + "menu.merchant_portal.printer": "Printers", + "menu.merchant_portal.store": "Store Info", } diff --git a/web/locales/en_US/merchant-portal/order.ts b/web/locales/en_US/merchant-portal/order.ts new file mode 100644 index 0000000..b1f14ca --- /dev/null +++ b/web/locales/en_US/merchant-portal/order.ts @@ -0,0 +1,29 @@ +export default { + "merchant.portal.order.page.title": "Orders", + "merchant.portal.order.page.description": "View your order records, status and payment details", + "merchant.portal.order.id": "ID", + "merchant.portal.order.order_no": "Order No.", + "merchant.portal.order.user_id": "User ID", + "merchant.portal.order.total_amount": "Total Amount", + "merchant.portal.order.discount_amount": "Discount", + "merchant.portal.order.delivery_fee": "Delivery Fee", + "merchant.portal.order.payment_amount": "Paid Amount", + "merchant.portal.order.pay_type": "Payment Method", + "merchant.portal.order.pay_type.0": "Balance", + "merchant.portal.order.pay_type.1": "WeChat Pay", + "merchant.portal.order.pay_status": "Payment Status", + "merchant.portal.order.pay_status.0": "Pending", + "merchant.portal.order.pay_status.1": "Paid", + "merchant.portal.order.pay_status.2": "Refunding", + "merchant.portal.order.pay_status.3": "Refunded", + "merchant.portal.order.status": "Order Status", + "merchant.portal.order.status.0": "Pending", + "merchant.portal.order.status.1": "Delivering", + "merchant.portal.order.status.2": "Delivered", + "merchant.portal.order.status.3": "Completed", + "merchant.portal.order.status.4": "Cancelled", + "merchant.portal.order.paid_at": "Paid At", + "merchant.portal.order.completed_at": "Completed At", + "merchant.portal.order.remark": "Remark", + "merchant.portal.order.created_at": "Created At", +}; diff --git a/web/locales/en_US/merchant-portal/payment.ts b/web/locales/en_US/merchant-portal/payment.ts new file mode 100644 index 0000000..6b18ba6 --- /dev/null +++ b/web/locales/en_US/merchant-portal/payment.ts @@ -0,0 +1,17 @@ +export default { + "merchant.portal.payment.page.title": "Transactions", + "merchant.portal.payment.page.description": "View payment records and transaction history for your orders", + "merchant.portal.payment.id": "ID", + "merchant.portal.payment.payment_no": "Payment No.", + "merchant.portal.payment.order_id": "Order ID", + "merchant.portal.payment.amount": "Amount", + "merchant.portal.payment.pay_type": "Payment Method", + "merchant.portal.payment.pay_type.0": "Balance", + "merchant.portal.payment.pay_type.1": "WeChat Pay", + "merchant.portal.payment.status": "Status", + "merchant.portal.payment.status.0": "Pending", + "merchant.portal.payment.status.1": "Success", + "merchant.portal.payment.status.2": "Refunded", + "merchant.portal.payment.paid_at": "Paid At", + "merchant.portal.payment.created_at": "Created At", +}; diff --git a/web/locales/en_US/merchant-portal/printer.ts b/web/locales/en_US/merchant-portal/printer.ts new file mode 100644 index 0000000..333a630 --- /dev/null +++ b/web/locales/en_US/merchant-portal/printer.ts @@ -0,0 +1,16 @@ +export default { + "merchant.portal.printer.page.title": "Printers", + "merchant.portal.printer.page.description": "Manage your printer devices for printing order receipts", + "merchant.portal.printer.id": "ID", + "merchant.portal.printer.name": "Printer Name", + "merchant.portal.printer.name.required": "Printer name is required", + "merchant.portal.printer.brand": "Brand", + "merchant.portal.printer.model": "Model", + "merchant.portal.printer.device_no": "Device No.", + "merchant.portal.printer.secret_key": "Secret Key", + "merchant.portal.printer.status": "Status", + "merchant.portal.printer.status.0": "Disabled", + "merchant.portal.printer.status.1": "Enabled", + "merchant.portal.printer.created_at": "Created At", + "merchant.portal.printer.updated_at": "Updated At", +}; diff --git a/web/locales/en_US/merchant-portal/product-category.ts b/web/locales/en_US/merchant-portal/product-category.ts new file mode 100644 index 0000000..a953442 --- /dev/null +++ b/web/locales/en_US/merchant-portal/product-category.ts @@ -0,0 +1,17 @@ +export default { + "merchant.portal.product-category.page.title": "Categories", + "merchant.portal.product-category.page.description": "Manage your product categories for grouping products", + "merchant.portal.product-category.id": "ID", + "merchant.portal.product-category.parent_id": "Parent Category", + "merchant.portal.product-category.name": "Category Name", + "merchant.portal.product-category.name.required": "Category name is required", + "merchant.portal.product-category.slug": "Slug", + "merchant.portal.product-category.icon": "Icon", + "merchant.portal.product-category.description": "Description", + "merchant.portal.product-category.sort": "Sort", + "merchant.portal.product-category.status": "Status", + "merchant.portal.product-category.status.0": "Disabled", + "merchant.portal.product-category.status.1": "Enabled", + "merchant.portal.product-category.created_at": "Created At", + "merchant.portal.product-category.updated_at": "Updated At", +}; diff --git a/web/locales/en_US/merchant-portal/product.ts b/web/locales/en_US/merchant-portal/product.ts new file mode 100644 index 0000000..793d625 --- /dev/null +++ b/web/locales/en_US/merchant-portal/product.ts @@ -0,0 +1,25 @@ +export default { + "merchant.portal.product.page.title": "Products", + "merchant.portal.product.page.description": "Manage your products, including listing, delisting, and recommendations", + "merchant.portal.product.id": "ID", + "merchant.portal.product.category_id": "Category", + "merchant.portal.product.name": "Product Name", + "merchant.portal.product.name.required": "Product name is required", + "merchant.portal.product.description": "Description", + "merchant.portal.product.images": "Images", + "merchant.portal.product.price": "Price", + "merchant.portal.product.price.required": "Price is required", + "merchant.portal.product.original_price": "Original Price", + "merchant.portal.product.stock": "Stock", + "merchant.portal.product.unit": "Unit", + "merchant.portal.product.sales": "Sales", + "merchant.portal.product.status": "Status", + "merchant.portal.product.status.0": "Offline", + "merchant.portal.product.status.1": "Online", + "merchant.portal.product.is_recommend": "Recommended", + "merchant.portal.product.is_recommend.0": "No", + "merchant.portal.product.is_recommend.1": "Yes", + "merchant.portal.product.sort": "Sort", + "merchant.portal.product.created_at": "Created At", + "merchant.portal.product.updated_at": "Updated At", +}; diff --git a/web/locales/en_US/merchant-portal/store.ts b/web/locales/en_US/merchant-portal/store.ts new file mode 100644 index 0000000..eea320e --- /dev/null +++ b/web/locales/en_US/merchant-portal/store.ts @@ -0,0 +1,17 @@ +export default { + "merchant.portal.store.page.title": "Store Info", + "merchant.portal.store.page.description": "View and edit your store information", + "merchant.portal.store.name": "Store Name", + "merchant.portal.store.name.required": "Store name is required", + "merchant.portal.store.logo": "Logo", + "merchant.portal.store.banner": "Banner", + "merchant.portal.store.description": "Description", + "merchant.portal.store.contact_name": "Contact Name", + "merchant.portal.store.contact_mobile": "Contact Phone", + "merchant.portal.store.province": "Province", + "merchant.portal.store.city": "City", + "merchant.portal.store.district": "District", + "merchant.portal.store.address": "Address", + "merchant.portal.store.business_hours": "Business Hours", + "merchant.portal.store.min_price": "Min. Order Price", +}; diff --git a/web/locales/en_US/merchant/store.ts b/web/locales/en_US/merchant/store.ts index 97fb170..fc827a3 100644 --- a/web/locales/en_US/merchant/store.ts +++ b/web/locales/en_US/merchant/store.ts @@ -4,6 +4,10 @@ export default { "merchant.store.id": "ID", "merchant.store.user_id": "Owner", "merchant.store.user_id.required": "Owner is required", + "merchant.store.username": "Username", + "merchant.store.username.required": "Username is required", + "merchant.store.password": "Password", + "merchant.store.password.required": "Password must be at least 6 characters", "merchant.store.category_id": "Category", "merchant.store.name": "Store Name", "merchant.store.name.required": "Store name is required", diff --git a/web/locales/zh_CN/index.ts b/web/locales/zh_CN/index.ts index 9aa9b43..79b20f3 100644 --- a/web/locales/zh_CN/index.ts +++ b/web/locales/zh_CN/index.ts @@ -40,6 +40,13 @@ import merchantPrintTask from "./merchant/print-task"; import merchantOrder from "./merchant/order"; import merchantRefund from "./merchant/refund"; +import merchantPortalProduct from "./merchant-portal/product"; +import merchantPortalProductCategory from "./merchant-portal/product-category"; +import merchantPortalOrder from "./merchant-portal/order"; +import merchantPortalPayment from "./merchant-portal/payment"; +import merchantPortalPrinter from "./merchant-portal/printer"; +import merchantPortalStore from "./merchant-portal/store"; + import taskCategory from "./task/category"; import taskOrderErrand from "./task/order-errand"; import taskOrderPickup from "./task/order-pickup"; @@ -104,6 +111,12 @@ export default { ...merchantPrintTask, ...merchantOrder, ...merchantRefund, + ...merchantPortalProduct, + ...merchantPortalProductCategory, + ...merchantPortalOrder, + ...merchantPortalPayment, + ...merchantPortalPrinter, + ...merchantPortalStore, ...taskCategory, ...taskOrderErrand, ...taskOrderPickup, diff --git a/web/locales/zh_CN/menu.ts b/web/locales/zh_CN/menu.ts index 9ad3b3b..0522be2 100644 --- a/web/locales/zh_CN/menu.ts +++ b/web/locales/zh_CN/menu.ts @@ -85,4 +85,11 @@ export default { "menu.finance.order_payment": "订单支付日志", "menu.finance.merchant_withdrawal": "商户提现", "menu.xin-admin": "XinAdmin", + "menu.merchant_portal": "商户管理", + "menu.merchant_portal.product": "商品列表", + "menu.merchant_portal.product-category": "商品分类", + "menu.merchant_portal.order": "订单列表", + "menu.merchant_portal.payment": "交易流水", + "menu.merchant_portal.printer": "打印机管理", + "menu.merchant_portal.store": "商户信息", }; diff --git a/web/locales/zh_CN/merchant-portal/order.ts b/web/locales/zh_CN/merchant-portal/order.ts new file mode 100644 index 0000000..2f5ee36 --- /dev/null +++ b/web/locales/zh_CN/merchant-portal/order.ts @@ -0,0 +1,29 @@ +export default { + "merchant.portal.order.page.title": "订单列表", + "merchant.portal.order.page.description": "查看您的订单记录,了解订单状态和支付情况", + "merchant.portal.order.id": "ID", + "merchant.portal.order.order_no": "订单号", + "merchant.portal.order.user_id": "用户ID", + "merchant.portal.order.total_amount": "订单总额", + "merchant.portal.order.discount_amount": "优惠金额", + "merchant.portal.order.delivery_fee": "配送费", + "merchant.portal.order.payment_amount": "实付金额", + "merchant.portal.order.pay_type": "支付方式", + "merchant.portal.order.pay_type.0": "余额支付", + "merchant.portal.order.pay_type.1": "微信支付", + "merchant.portal.order.pay_status": "支付状态", + "merchant.portal.order.pay_status.0": "待支付", + "merchant.portal.order.pay_status.1": "已支付", + "merchant.portal.order.pay_status.2": "退款中", + "merchant.portal.order.pay_status.3": "已退款", + "merchant.portal.order.status": "订单状态", + "merchant.portal.order.status.0": "待接单", + "merchant.portal.order.status.1": "配送中", + "merchant.portal.order.status.2": "已送达", + "merchant.portal.order.status.3": "已完成", + "merchant.portal.order.status.4": "已取消", + "merchant.portal.order.paid_at": "支付时间", + "merchant.portal.order.completed_at": "完成时间", + "merchant.portal.order.remark": "备注", + "merchant.portal.order.created_at": "创建时间", +}; diff --git a/web/locales/zh_CN/merchant-portal/payment.ts b/web/locales/zh_CN/merchant-portal/payment.ts new file mode 100644 index 0000000..55e5453 --- /dev/null +++ b/web/locales/zh_CN/merchant-portal/payment.ts @@ -0,0 +1,17 @@ +export default { + "merchant.portal.payment.page.title": "交易流水", + "merchant.portal.payment.page.description": "查看您订单的支付记录和交易流水", + "merchant.portal.payment.id": "ID", + "merchant.portal.payment.payment_no": "支付单号", + "merchant.portal.payment.order_id": "订单ID", + "merchant.portal.payment.amount": "支付金额", + "merchant.portal.payment.pay_type": "支付方式", + "merchant.portal.payment.pay_type.0": "余额支付", + "merchant.portal.payment.pay_type.1": "微信支付", + "merchant.portal.payment.status": "支付状态", + "merchant.portal.payment.status.0": "待支付", + "merchant.portal.payment.status.1": "支付成功", + "merchant.portal.payment.status.2": "已退款", + "merchant.portal.payment.paid_at": "支付时间", + "merchant.portal.payment.created_at": "创建时间", +}; diff --git a/web/locales/zh_CN/merchant-portal/printer.ts b/web/locales/zh_CN/merchant-portal/printer.ts new file mode 100644 index 0000000..ba3129f --- /dev/null +++ b/web/locales/zh_CN/merchant-portal/printer.ts @@ -0,0 +1,16 @@ +export default { + "merchant.portal.printer.page.title": "打印机管理", + "merchant.portal.printer.page.description": "管理您的打印机设备,用于打印订单小票", + "merchant.portal.printer.id": "ID", + "merchant.portal.printer.name": "打印机名称", + "merchant.portal.printer.name.required": "打印机名称不能为空", + "merchant.portal.printer.brand": "品牌", + "merchant.portal.printer.model": "型号", + "merchant.portal.printer.device_no": "设备编号", + "merchant.portal.printer.secret_key": "密钥", + "merchant.portal.printer.status": "状态", + "merchant.portal.printer.status.0": "禁用", + "merchant.portal.printer.status.1": "启用", + "merchant.portal.printer.created_at": "创建时间", + "merchant.portal.printer.updated_at": "更新时间", +}; diff --git a/web/locales/zh_CN/merchant-portal/product-category.ts b/web/locales/zh_CN/merchant-portal/product-category.ts new file mode 100644 index 0000000..6ebdcdc --- /dev/null +++ b/web/locales/zh_CN/merchant-portal/product-category.ts @@ -0,0 +1,17 @@ +export default { + "merchant.portal.product-category.page.title": "商品分类", + "merchant.portal.product-category.page.description": "管理您的商品分类,用于对商品进行分组", + "merchant.portal.product-category.id": "ID", + "merchant.portal.product-category.parent_id": "上级分类", + "merchant.portal.product-category.name": "分类名称", + "merchant.portal.product-category.name.required": "分类名称不能为空", + "merchant.portal.product-category.slug": "别名", + "merchant.portal.product-category.icon": "图标", + "merchant.portal.product-category.description": "描述", + "merchant.portal.product-category.sort": "排序", + "merchant.portal.product-category.status": "状态", + "merchant.portal.product-category.status.0": "禁用", + "merchant.portal.product-category.status.1": "启用", + "merchant.portal.product-category.created_at": "创建时间", + "merchant.portal.product-category.updated_at": "更新时间", +}; diff --git a/web/locales/zh_CN/merchant-portal/product.ts b/web/locales/zh_CN/merchant-portal/product.ts new file mode 100644 index 0000000..11d7675 --- /dev/null +++ b/web/locales/zh_CN/merchant-portal/product.ts @@ -0,0 +1,25 @@ +export default { + "merchant.portal.product.page.title": "商品列表", + "merchant.portal.product.page.description": "管理您的商品,支持上架、下架、推荐等操作", + "merchant.portal.product.id": "ID", + "merchant.portal.product.category_id": "商品分类", + "merchant.portal.product.name": "商品名称", + "merchant.portal.product.name.required": "商品名称不能为空", + "merchant.portal.product.description": "商品描述", + "merchant.portal.product.images": "商品图片", + "merchant.portal.product.price": "售价", + "merchant.portal.product.price.required": "售价不能为空", + "merchant.portal.product.original_price": "原价", + "merchant.portal.product.stock": "库存", + "merchant.portal.product.unit": "单位", + "merchant.portal.product.sales": "销量", + "merchant.portal.product.status": "状态", + "merchant.portal.product.status.0": "下架", + "merchant.portal.product.status.1": "上架", + "merchant.portal.product.is_recommend": "推荐", + "merchant.portal.product.is_recommend.0": "否", + "merchant.portal.product.is_recommend.1": "是", + "merchant.portal.product.sort": "排序", + "merchant.portal.product.created_at": "创建时间", + "merchant.portal.product.updated_at": "更新时间", +}; diff --git a/web/locales/zh_CN/merchant-portal/store.ts b/web/locales/zh_CN/merchant-portal/store.ts new file mode 100644 index 0000000..6674432 --- /dev/null +++ b/web/locales/zh_CN/merchant-portal/store.ts @@ -0,0 +1,17 @@ +export default { + "merchant.portal.store.page.title": "商户信息", + "merchant.portal.store.page.description": "查看和编辑您的商户基本信息", + "merchant.portal.store.name": "店铺名称", + "merchant.portal.store.name.required": "店铺名称不能为空", + "merchant.portal.store.logo": "店铺Logo", + "merchant.portal.store.banner": "店铺Banner", + "merchant.portal.store.description": "店铺描述", + "merchant.portal.store.contact_name": "联系人", + "merchant.portal.store.contact_mobile": "联系电话", + "merchant.portal.store.province": "省", + "merchant.portal.store.city": "市", + "merchant.portal.store.district": "区", + "merchant.portal.store.address": "详细地址", + "merchant.portal.store.business_hours": "营业时间", + "merchant.portal.store.min_price": "起送价", +}; diff --git a/web/locales/zh_CN/merchant/store.ts b/web/locales/zh_CN/merchant/store.ts index e1ec552..0339fd8 100644 --- a/web/locales/zh_CN/merchant/store.ts +++ b/web/locales/zh_CN/merchant/store.ts @@ -4,6 +4,10 @@ export default { "merchant.store.id": "ID", "merchant.store.user_id": "所属用户", "merchant.store.user_id.required": "所属用户不能为空", + "merchant.store.username": "登录用户名", + "merchant.store.username.required": "登录用户名不能为空", + "merchant.store.password": "登录密码", + "merchant.store.password.required": "登录密码不能少于6位", "merchant.store.category_id": "商户分类", "merchant.store.name": "店铺名称", "merchant.store.name.required": "店铺名称不能为空", diff --git a/web/pages/merchant-portal/order/index.tsx b/web/pages/merchant-portal/order/index.tsx new file mode 100644 index 0000000..c4ebb86 --- /dev/null +++ b/web/pages/merchant-portal/order/index.tsx @@ -0,0 +1,221 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IOrder from '@/domain/iOrder'; +import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +const { Title, Text } = Typography; +dayjs.extend(relativeTime); + +const Table: React.FC = () => { + const { t } = useTranslation(); + + const columns: XinTableColumn[] = [ + { + title: t('merchant.portal.order.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.portal.order.order_no'), + dataIndex: 'order_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.order.user_id'), + dataIndex: 'user_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.order.total_amount'), + dataIndex: 'total_amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.portal.order.discount_amount'), + dataIndex: 'discount_amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.portal.order.delivery_fee'), + dataIndex: 'delivery_fee', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.portal.order.payment_amount'), + dataIndex: 'payment_amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.portal.order.pay_type'), + dataIndex: 'pay_type', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.order.pay_type.0') }, + { value: 1, label: t('merchant.portal.order.pay_type.1') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'default', 1: 'blue' }; + const labelMap: Record = { + 0: t('merchant.portal.order.pay_type.0'), + 1: t('merchant.portal.order.pay_type.1'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.portal.order.pay_type.0'), value: 0 }, + { text: t('merchant.portal.order.pay_type.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.order.pay_status'), + dataIndex: 'pay_status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.order.pay_status.0') }, + { value: 1, label: t('merchant.portal.order.pay_status.1') }, + { value: 2, label: t('merchant.portal.order.pay_status.2') }, + { value: 3, label: t('merchant.portal.order.pay_status.3') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'success', 2: 'processing', 3: 'default' }; + const labelMap: Record = { + 0: t('merchant.portal.order.pay_status.0'), + 1: t('merchant.portal.order.pay_status.1'), + 2: t('merchant.portal.order.pay_status.2'), + 3: t('merchant.portal.order.pay_status.3'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.portal.order.pay_status.0'), value: 0 }, + { text: t('merchant.portal.order.pay_status.1'), value: 1 }, + { text: t('merchant.portal.order.pay_status.2'), value: 2 }, + { text: t('merchant.portal.order.pay_status.3'), value: 3 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.order.status'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.order.status.0') }, + { value: 1, label: t('merchant.portal.order.status.1') }, + { value: 2, label: t('merchant.portal.order.status.2') }, + { value: 3, label: t('merchant.portal.order.status.3') }, + { value: 4, label: t('merchant.portal.order.status.4') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'processing', 2: 'processing', 3: 'success', 4: 'default' }; + const labelMap: Record = { + 0: t('merchant.portal.order.status.0'), + 1: t('merchant.portal.order.status.1'), + 2: t('merchant.portal.order.status.2'), + 3: t('merchant.portal.order.status.3'), + 4: t('merchant.portal.order.status.4'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.portal.order.status.0'), value: 0 }, + { text: t('merchant.portal.order.status.1'), value: 1 }, + { text: t('merchant.portal.order.status.2'), value: 2 }, + { text: t('merchant.portal.order.status.3'), value: 3 }, + { text: t('merchant.portal.order.status.4'), value: 4 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.order.paid_at'), + dataIndex: 'paid_at', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'), + }, + { + title: t('merchant.portal.order.completed_at'), + dataIndex: 'completed_at', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'), + }, + { + title: t('merchant.portal.order.remark'), + dataIndex: 'remark', + valueType: 'text', + hideInSearch: true, + align: 'center', + }, + { + title: t('merchant.portal.order.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant-portal/order', + columns, + rowKey: 'id', + accessName: 'merchant.portal.order', + addShow: false, + editShow: false, + scroll: { x: 1400 }, + formProps: { + grid: true, + colProps: { span: 12 }, + rowProps: { gutter: [30, 0] }, + layout: 'vertical', + }, + modalProps: { width: 800 }, + cardProps: { variant: 'borderless' }, + pagination: { size: 'small', style: { marginBottom: 0 } }, + }; + + return ( + <> +
+ {t('merchant.portal.order.page.title')} + {t('merchant.portal.order.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant-portal/payment/index.tsx b/web/pages/merchant-portal/payment/index.tsx new file mode 100644 index 0000000..c5fae49 --- /dev/null +++ b/web/pages/merchant-portal/payment/index.tsx @@ -0,0 +1,138 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IOrderPayment from '@/domain/iOrderPayment'; +import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +const { Title, Text } = Typography; +dayjs.extend(relativeTime); + +const Table: React.FC = () => { + const { t } = useTranslation(); + + const columns: XinTableColumn[] = [ + { + title: t('merchant.portal.payment.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.portal.payment.payment_no'), + dataIndex: 'payment_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.payment.order_id'), + dataIndex: 'order_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.payment.amount'), + dataIndex: 'amount', + valueType: 'text', + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.portal.payment.pay_type'), + dataIndex: 'pay_type', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.payment.pay_type.0') }, + { value: 1, label: t('merchant.portal.payment.pay_type.1') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'default', 1: 'blue' }; + const labelMap: Record = { + 0: t('merchant.portal.payment.pay_type.0'), + 1: t('merchant.portal.payment.pay_type.1'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.portal.payment.pay_type.0'), value: 0 }, + { text: t('merchant.portal.payment.pay_type.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.payment.status'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.payment.status.0') }, + { value: 1, label: t('merchant.portal.payment.status.1') }, + { value: 2, label: t('merchant.portal.payment.status.2') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'success', 2: 'error' }; + const labelMap: Record = { + 0: t('merchant.portal.payment.status.0'), + 1: t('merchant.portal.payment.status.1'), + 2: t('merchant.portal.payment.status.2'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.portal.payment.status.0'), value: 0 }, + { text: t('merchant.portal.payment.status.1'), value: 1 }, + { text: t('merchant.portal.payment.status.2'), value: 2 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.payment.paid_at'), + dataIndex: 'paid_at', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'), + }, + { + title: t('merchant.portal.payment.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant-portal/payment', + columns, + rowKey: 'id', + accessName: 'merchant.portal.payment', + addShow: false, + editShow: false, + scroll: { x: 1000 }, + cardProps: { variant: 'borderless' }, + pagination: { size: 'small', style: { marginBottom: 0 } }, + }; + + return ( + <> +
+ {t('merchant.portal.payment.page.title')} + {t('merchant.portal.payment.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant-portal/printer/index.tsx b/web/pages/merchant-portal/printer/index.tsx new file mode 100644 index 0000000..3ee9ca9 --- /dev/null +++ b/web/pages/merchant-portal/printer/index.tsx @@ -0,0 +1,128 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IPrinter from '@/domain/iPrinter'; +import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +const { Title, Text } = Typography; +dayjs.extend(relativeTime); + +const Table: React.FC = () => { + const { t } = useTranslation(); + + const columns: XinTableColumn[] = [ + { + title: t('merchant.portal.printer.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.portal.printer.name'), + dataIndex: 'name', + valueType: 'text', + align: 'center', + required: true, + rules: [{ required: true, message: t('merchant.portal.printer.name.required') }], + }, + { + title: t('merchant.portal.printer.brand'), + dataIndex: 'brand', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.printer.model'), + dataIndex: 'model', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.printer.device_no'), + dataIndex: 'device_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.printer.secret_key'), + dataIndex: 'secret_key', + valueType: 'password', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.printer.status'), + dataIndex: 'status', + valueType: 'radioButton', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.printer.status.0') }, + { value: 1, label: t('merchant.portal.printer.status.1') }, + ], + }, + render: (value: number) => { + return value === 1 + ? {t('merchant.portal.printer.status.1')} + : {t('merchant.portal.printer.status.0')}; + }, + filters: [ + { text: t('merchant.portal.printer.status.0'), value: 0 }, + { text: t('merchant.portal.printer.status.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.printer.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + { + title: t('merchant.portal.printer.updated_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'updated_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant-portal/printer', + columns, + rowKey: 'id', + accessName: 'merchant.portal.printer', + scroll: { x: 1200 }, + formProps: { + grid: true, + colProps: { span: 12 }, + rowProps: { gutter: [30, 0] }, + layout: 'vertical', + }, + modalProps: { width: 600 }, + cardProps: { variant: 'borderless' }, + pagination: { size: 'small', style: { marginBottom: 0 } }, + }; + + return ( + <> +
+ {t('merchant.portal.printer.page.title')} + {t('merchant.portal.printer.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant-portal/product-category/index.tsx b/web/pages/merchant-portal/product-category/index.tsx new file mode 100644 index 0000000..b55d44a --- /dev/null +++ b/web/pages/merchant-portal/product-category/index.tsx @@ -0,0 +1,134 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IProductCategory from '@/domain/iProductCategory'; +import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +const { Title, Text } = Typography; +dayjs.extend(relativeTime); + +const Table: React.FC = () => { + const { t } = useTranslation(); + + const columns: XinTableColumn[] = [ + { + title: t('merchant.portal.product-category.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.portal.product-category.name'), + dataIndex: 'name', + valueType: 'text', + align: 'center', + required: true, + rules: [{ required: true, message: t('merchant.portal.product-category.name.required') }], + }, + { + title: t('merchant.portal.product-category.parent_id'), + dataIndex: 'parent_id', + valueType: 'text', + align: 'center', + }, + { + title: t('merchant.portal.product-category.slug'), + dataIndex: 'slug', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.product-category.icon'), + dataIndex: 'icon', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.product-category.sort'), + dataIndex: 'sort', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.product-category.description'), + dataIndex: 'description', + valueType: 'text', + hideInTable: true, + hideInSearch: true, + }, + { + title: t('merchant.portal.product-category.status'), + dataIndex: 'status', + valueType: 'radioButton', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.product-category.status.0') }, + { value: 1, label: t('merchant.portal.product-category.status.1') }, + ], + }, + render: (value: number) => { + return value === 1 + ? {t('merchant.portal.product-category.status.1')} + : {t('merchant.portal.product-category.status.0')}; + }, + filters: [ + { text: t('merchant.portal.product-category.status.0'), value: 0 }, + { text: t('merchant.portal.product-category.status.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.product-category.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + { + title: t('merchant.portal.product-category.updated_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'updated_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant-portal/product-category', + columns, + rowKey: 'id', + accessName: 'merchant.portal.product-category', + scroll: { x: 1200 }, + formProps: { + grid: true, + colProps: { span: 12 }, + rowProps: { gutter: [30, 0] }, + layout: 'vertical', + }, + modalProps: { width: 600 }, + cardProps: { variant: 'borderless' }, + pagination: { size: 'small', style: { marginBottom: 0 } }, + }; + + return ( + <> +
+ {t('merchant.portal.product-category.page.title')} + {t('merchant.portal.product-category.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant-portal/product/index.tsx b/web/pages/merchant-portal/product/index.tsx new file mode 100644 index 0000000..74dc6f6 --- /dev/null +++ b/web/pages/merchant-portal/product/index.tsx @@ -0,0 +1,173 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IMerchantProduct from '@/domain/iMerchantProduct'; +import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +const { Title, Text } = Typography; +dayjs.extend(relativeTime); + +const Table: React.FC = () => { + const { t } = useTranslation(); + + const columns: XinTableColumn[] = [ + { + title: t('merchant.portal.product.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.portal.product.name'), + dataIndex: 'name', + valueType: 'text', + align: 'center', + required: true, + rules: [{ required: true, message: t('merchant.portal.product.name.required') }], + }, + { + title: t('merchant.portal.product.category_id'), + dataIndex: 'category_id', + valueType: 'text', + align: 'center', + }, + { + title: t('merchant.portal.product.price'), + dataIndex: 'price', + valueType: 'text', + align: 'center', + render: (value: number) => `¥${value || '0.00'}`, + }, + { + title: t('merchant.portal.product.original_price'), + dataIndex: 'original_price', + valueType: 'text', + align: 'center', + hideInSearch: true, + render: (value: number) => value ? `¥${value}` : '-', + }, + { + title: t('merchant.portal.product.stock'), + dataIndex: 'stock', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.product.sales'), + dataIndex: 'sales', + hideInForm: true, + hideInSearch: true, + align: 'center', + }, + { + title: t('merchant.portal.product.unit'), + dataIndex: 'unit', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.product.description'), + dataIndex: 'description', + valueType: 'text', + hideInTable: true, + hideInSearch: true, + }, + { + title: t('merchant.portal.product.status'), + dataIndex: 'status', + valueType: 'radioButton', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.product.status.0') }, + { value: 1, label: t('merchant.portal.product.status.1') }, + ], + }, + render: (value: number) => { + return value === 1 + ? {t('merchant.portal.product.status.1')} + : {t('merchant.portal.product.status.0')}; + }, + filters: [ + { text: t('merchant.portal.product.status.0'), value: 0 }, + { text: t('merchant.portal.product.status.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.portal.product.is_recommend'), + dataIndex: 'is_recommend', + valueType: 'radioButton', + fieldProps: { + options: [ + { value: 0, label: t('merchant.portal.product.is_recommend.0') }, + { value: 1, label: t('merchant.portal.product.is_recommend.1') }, + ], + }, + render: (value: number) => { + return value === 1 + ? {t('merchant.portal.product.is_recommend.1')} + : {t('merchant.portal.product.is_recommend.0')}; + }, + align: 'center', + }, + { + title: t('merchant.portal.product.sort'), + dataIndex: 'sort', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.portal.product.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + { + title: t('merchant.portal.product.updated_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'updated_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant-portal/product', + columns, + rowKey: 'id', + accessName: 'merchant.portal.product', + scroll: { x: 1400 }, + formProps: { + grid: true, + colProps: { span: 12 }, + rowProps: { gutter: [30, 0] }, + layout: 'vertical', + }, + modalProps: { width: 800 }, + cardProps: { variant: 'borderless' }, + pagination: { size: 'small', style: { marginBottom: 0 } }, + }; + + return ( + <> +
+ {t('merchant.portal.product.page.title')} + {t('merchant.portal.product.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant-portal/store/index.tsx b/web/pages/merchant-portal/store/index.tsx new file mode 100644 index 0000000..01e5758 --- /dev/null +++ b/web/pages/merchant-portal/store/index.tsx @@ -0,0 +1,107 @@ +import { Typography } from 'antd'; +import React from 'react'; +import XinForm from '@/components/XinForm'; +import type { FormColumn } from '@/components/XinForm/typings'; +import { useTranslation } from 'react-i18next'; +import createAxios from '@/utils/request'; + +const { Title, Text } = Typography; + +const Store: React.FC = () => { + const { t } = useTranslation(); + + const columns: FormColumn[] = [ + { + title: t('merchant.portal.store.name'), + dataIndex: 'name', + valueType: 'text', + rules: [{ required: true, message: t('merchant.portal.store.name.required') }], + }, + { + title: t('merchant.portal.store.logo'), + dataIndex: 'logo', + valueType: 'text', + }, + { + title: t('merchant.portal.store.banner'), + dataIndex: 'banner', + valueType: 'text', + }, + { + title: t('merchant.portal.store.description'), + dataIndex: 'description', + valueType: 'textarea', + }, + { + title: t('merchant.portal.store.contact_name'), + dataIndex: 'contact_name', + valueType: 'text', + }, + { + title: t('merchant.portal.store.contact_mobile'), + dataIndex: 'contact_mobile', + valueType: 'text', + }, + { + title: t('merchant.portal.store.province'), + dataIndex: 'province', + valueType: 'text', + }, + { + title: t('merchant.portal.store.city'), + dataIndex: 'city', + valueType: 'text', + }, + { + title: t('merchant.portal.store.district'), + dataIndex: 'district', + valueType: 'text', + }, + { + title: t('merchant.portal.store.address'), + dataIndex: 'address', + valueType: 'text', + }, + { + title: t('merchant.portal.store.business_hours'), + dataIndex: 'business_hours', + valueType: 'text', + }, + { + title: t('merchant.portal.store.min_price'), + dataIndex: 'min_price', + valueType: 'text', + }, + ]; + + return ( + <> +
+ {t('merchant.portal.store.page.title')} + {t('merchant.portal.store.page.description')} +
+ { + const { data } = await createAxios({ + url: '/merchant-portal/store', + method: 'get', + }); + return data.data; + }} + onFinish={async (values) => { + await createAxios({ + url: '/merchant-portal/store', + method: 'put', + data: values, + }); + return true; + }} + /> + + ); +}; + +export default Store; diff --git a/web/pages/merchant/store/index.tsx b/web/pages/merchant/store/index.tsx index 79a75a2..54a9ea8 100644 --- a/web/pages/merchant/store/index.tsx +++ b/web/pages/merchant/store/index.tsx @@ -31,12 +31,24 @@ const Table: React.FC = () => { rules: [{ required: true, message: t('merchant.store.name.required') }], }, { - title: t('merchant.store.user_id'), - dataIndex: 'user_id', + title: t('merchant.store.username'), + dataIndex: 'username', valueType: 'text', align: 'center', required: true, hideInSearch: true, + hideInTable: true, + rules: [{ required: true, message: t('merchant.store.username.required') }], + }, + { + title: t('merchant.store.password'), + dataIndex: 'password', + valueType: 'password', + align: 'center', + required: true, + hideInSearch: true, + hideInTable: true, + rules: [{ required: true, min: 6, message: t('merchant.store.password.required') }], }, { title: t('merchant.store.category_id'),