diff --git a/database/migrations/2026_05_30_000006_create_order_tables.php b/database/migrations/2026_05_30_000006_create_order_tables.php index 618c370..a53ac0f 100644 --- a/database/migrations/2026_05_30_000006_create_order_tables.php +++ b/database/migrations/2026_05_30_000006_create_order_tables.php @@ -79,6 +79,29 @@ return new class extends Migration }); } + // 订单支付日志(支付成功后写入,关联三方支付流水) + if (! Schema::hasTable('order_payments')) { + Schema::create('order_payments', function (Blueprint $table) { + $table->id(); + $table->string('payment_no', 32)->comment('支付日志编号'); + $table->unsignedBigInteger('order_id')->comment('订单ID'); + $table->unsignedInteger('user_id')->comment('支付用户ID'); + $table->unsignedBigInteger('payment_transaction_id')->nullable()->comment('三方支付流水ID'); + $table->decimal('amount', 10, 2)->comment('支付金额'); + $table->tinyInteger('pay_type')->comment('支付方式:0=余额,1=微信支付'); + $table->tinyInteger('status')->default(0)->comment('状态:0=待支付,1=已支付,2=已退款'); + $table->timestamp('paid_at')->nullable()->comment('支付时间'); + $table->timestamp('refund_at')->nullable()->comment('退款时间'); + $table->timestamps(); + $table->unique('payment_no'); + $table->index('order_id'); + $table->index('user_id'); + $table->index('payment_transaction_id'); + $table->index('status'); + $table->comment('订单支付日志'); + }); + } + // 商户提现 if (! Schema::hasTable('merchant_withdrawals')) { Schema::create('merchant_withdrawals', function (Blueprint $table) { @@ -103,6 +126,7 @@ return new class extends Migration public function down(): void { Schema::dropIfExists('merchant_withdrawals'); + Schema::dropIfExists('order_payments'); Schema::dropIfExists('order_refunds'); Schema::dropIfExists('order_items'); Schema::dropIfExists('orders'); diff --git a/database/seeders/SysRuleSeeder.php b/database/seeders/SysRuleSeeder.php index 7fb2625..80bc8a6 100644 --- a/database/seeders/SysRuleSeeder.php +++ b/database/seeders/SysRuleSeeder.php @@ -454,6 +454,31 @@ class SysRuleSeeder extends Seeder ['type' => 'rule', 'name' => '删除任务', 'key' => 'merchant.print-task.delete'], ], ], + [ + 'type' => 'route', + 'name' => '订单管理', + 'key' => 'merchant.order', + 'path' => '/merchant/order', + 'local' => 'menu.merchant.order', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.order.query'], + ['type' => 'rule', 'name' => '新增订单', 'key' => 'merchant.order.create'], + ['type' => 'rule', 'name' => '修改订单', 'key' => 'merchant.order.update'], + ['type' => 'rule', 'name' => '删除订单', 'key' => 'merchant.order.delete'], + ], + ], + [ + 'type' => 'route', + 'name' => '订单退款', + 'key' => 'merchant.refund', + 'path' => '/merchant/refund', + 'local' => 'menu.merchant.refund', + 'children' => [ + ['type' => 'rule', 'name' => '查询列表', 'key' => 'merchant.refund.query'], + ['type' => 'rule', 'name' => '处理退款', 'key' => 'merchant.refund.update'], + ['type' => 'rule', 'name' => '删除记录', 'key' => 'merchant.refund.delete'], + ], + ], ], ], // 校园任务 @@ -625,6 +650,29 @@ class SysRuleSeeder extends Seeder ['type' => 'rule', 'key' => 'finance.transaction.delete', 'name' => '删除'], ], ], + [ + 'type' => 'route', + 'name' => '订单支付日志', + 'key' => 'merchant.order_payment', + 'path' => '/finance/order-payment', + 'local' => 'menu.finance.order_payment', + 'children' => [ + ['type' => 'rule', 'name' => '查询', 'key' => 'merchant.order_payment.query'], + ['type' => 'rule', 'name' => '删除', 'key' => 'merchant.order_payment.delete'], + ], + ], + [ + 'type' => 'route', + 'name' => '商户提现', + 'key' => 'merchant.withdrawal', + 'path' => '/finance/merchant-withdrawal', + 'local' => 'menu.finance.merchant_withdrawal', + 'children' => [ + ['type' => 'rule', 'name' => '查询', 'key' => 'merchant.withdrawal.query'], + ['type' => 'rule', 'name' => '审核', 'key' => 'merchant.withdrawal.update'], + ['type' => 'rule', 'name' => '删除', 'key' => 'merchant.withdrawal.delete'], + ], + ], ], ], [ diff --git a/modules/Merchant/Http/Controllers/MerchantWithdrawalController.php b/modules/Merchant/Http/Controllers/MerchantWithdrawalController.php new file mode 100644 index 0000000..4fda967 --- /dev/null +++ b/modules/Merchant/Http/Controllers/MerchantWithdrawalController.php @@ -0,0 +1,67 @@ + '=', + 'channel' => '=', + 'merchant_id' => '=', + ]; + + protected array $quickSearchField = ['withdraw_no']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = MerchantWithdrawalModel::query(); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[PutRoute( + route: '/{id}', + authorize: 'update', + where: ['id' => '[0-9]+'] + )] + public function update(int $id, MerchantWithdrawalFormRequest $request): JsonResponse + { + $model = MerchantWithdrawalModel::find($id); + 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 = MerchantWithdrawalModel::find($id); + if (empty($model)) { + return $this->error('提现记录不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Controllers/OrderController.php b/modules/Merchant/Http/Controllers/OrderController.php new file mode 100644 index 0000000..8434d2b --- /dev/null +++ b/modules/Merchant/Http/Controllers/OrderController.php @@ -0,0 +1,76 @@ + '=', + 'pay_status' => '=', + 'user_id' => '=', + 'merchant_id' => '=', + ]; + + protected array $quickSearchField = ['order_no']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = OrderModel::query(); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[PostRoute(authorize: 'create')] + public function create(OrderFormRequest $request): JsonResponse + { + OrderModel::create($request->validated()); + return $this->success(); + } + + #[PutRoute( + route: '/{id}', + authorize: 'update', + where: ['id' => '[0-9]+'] + )] + public function update(int $id, OrderFormRequest $request): JsonResponse + { + $model = OrderModel::find($id); + 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 = OrderModel::find($id); + if (empty($model)) { + return $this->error('订单不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Controllers/OrderPaymentController.php b/modules/Merchant/Http/Controllers/OrderPaymentController.php new file mode 100644 index 0000000..d6e7e4f --- /dev/null +++ b/modules/Merchant/Http/Controllers/OrderPaymentController.php @@ -0,0 +1,51 @@ + '=', + 'pay_type' => '=', + 'order_id' => '=', + 'user_id' => '=', + ]; + + protected array $quickSearchField = ['payment_no']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = OrderPaymentModel::query(); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[DeleteRoute( + route: '/{id}', + authorize: 'delete', + where: ['id' => '[0-9]+'] + )] + public function delete(int $id): JsonResponse + { + $model = OrderPaymentModel::find($id); + if (empty($model)) { + return $this->error('支付记录不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Controllers/OrderRefundController.php b/modules/Merchant/Http/Controllers/OrderRefundController.php new file mode 100644 index 0000000..dfa0171 --- /dev/null +++ b/modules/Merchant/Http/Controllers/OrderRefundController.php @@ -0,0 +1,68 @@ + '=', + 'order_id' => '=', + 'user_id' => '=', + 'merchant_id' => '=', + ]; + + protected array $quickSearchField = ['refund_no']; + + #[GetRoute(authorize: 'query')] + public function query(Request $request): JsonResponse + { + $params = $request->all(); + $pageSize = $params['pageSize'] ?? 10; + $query = OrderRefundModel::query(); + $data = $this->buildSearch($params, $query) + ->paginate($pageSize) + ->toArray(); + return $this->success($data); + } + + #[PutRoute( + route: '/{id}', + authorize: 'update', + where: ['id' => '[0-9]+'] + )] + public function update(int $id, OrderRefundFormRequest $request): JsonResponse + { + $model = OrderRefundModel::find($id); + 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 = OrderRefundModel::find($id); + if (empty($model)) { + return $this->error('退款记录不存在'); + } + $model->delete(); + return $this->success(); + } +} diff --git a/modules/Merchant/Http/Requests/MerchantWithdrawalFormRequest.php b/modules/Merchant/Http/Requests/MerchantWithdrawalFormRequest.php new file mode 100644 index 0000000..c4a54a6 --- /dev/null +++ b/modules/Merchant/Http/Requests/MerchantWithdrawalFormRequest.php @@ -0,0 +1,52 @@ +isUpdate()) { + return [ + 'withdraw_no' => 'required|string|max:32|unique:merchant_withdrawals,withdraw_no', + 'merchant_id' => 'required|integer', + 'amount' => 'required|numeric', + 'channel' => 'required|string|max:50', + 'account_info' => 'required|array', + 'status' => 'required|integer|in:0,1,2', + 'remark' => 'nullable|string', + 'processed_at' => 'nullable|date', + ]; + } + + $id = $this->route('id'); + return [ + 'withdraw_no' => ['required', 'string', 'max:32', Rule::unique('merchant_withdrawals', 'withdraw_no')->ignore($id)], + 'merchant_id' => 'required|integer', + 'amount' => 'required|numeric', + 'channel' => 'required|string|max:50', + 'account_info' => 'required|array', + 'status' => 'required|integer|in:0,1,2', + 'remark' => 'nullable|string', + 'processed_at' => 'nullable|date', + ]; + } + + public function messages(): array + { + return [ + 'withdraw_no.required' => '提现单号不能为空', + 'withdraw_no.unique' => '提现单号已存在', + 'merchant_id.required' => '商户ID不能为空', + 'amount.required' => '提现金额不能为空', + 'channel.required' => '提现渠道不能为空', + 'account_info.required' => '账户信息不能为空', + 'status.required' => '提现状态不能为空', + ]; + } +} diff --git a/modules/Merchant/Http/Requests/OrderFormRequest.php b/modules/Merchant/Http/Requests/OrderFormRequest.php new file mode 100644 index 0000000..3f4cea8 --- /dev/null +++ b/modules/Merchant/Http/Requests/OrderFormRequest.php @@ -0,0 +1,69 @@ +isUpdate()) { + return [ + 'order_no' => 'required|string|max:32|unique:orders,order_no', + 'user_id' => 'required|integer', + 'merchant_id' => 'required|integer', + 'address_id' => 'required|integer', + 'total_amount' => 'required|numeric', + 'discount_amount' => 'nullable|numeric', + 'delivery_fee' => 'nullable|numeric', + 'payment_amount' => 'required|numeric', + 'pay_type' => 'required|integer|in:0,1,2', + 'pay_status' => 'required|integer|in:0,1', + 'paid_at' => 'nullable|date', + 'status' => 'required|integer|in:0,1,2,3,4,5', + 'cancel_reason' => 'nullable|string', + 'remark' => 'nullable|string', + 'completed_at' => 'nullable|date', + ]; + } + + $id = $this->route('id'); + return [ + 'order_no' => ['required', 'string', 'max:32', Rule::unique('orders', 'order_no')->ignore($id)], + 'user_id' => 'required|integer', + 'merchant_id' => 'required|integer', + 'address_id' => 'required|integer', + 'total_amount' => 'required|numeric', + 'discount_amount' => 'nullable|numeric', + 'delivery_fee' => 'nullable|numeric', + 'payment_amount' => 'required|numeric', + 'pay_type' => 'required|integer|in:0,1,2', + 'pay_status' => 'required|integer|in:0,1', + 'paid_at' => 'nullable|date', + 'status' => 'required|integer|in:0,1,2,3,4,5', + 'cancel_reason' => 'nullable|string', + 'remark' => 'nullable|string', + 'completed_at' => 'nullable|date', + ]; + } + + public function messages(): array + { + return [ + 'order_no.required' => '订单号不能为空', + 'order_no.unique' => '订单号已存在', + 'user_id.required' => '用户ID不能为空', + 'merchant_id.required' => '商户ID不能为空', + 'address_id.required' => '地址ID不能为空', + 'total_amount.required' => '订单总金额不能为空', + 'payment_amount.required' => '支付金额不能为空', + 'pay_type.required' => '支付类型不能为空', + 'pay_status.required' => '支付状态不能为空', + 'status.required' => '订单状态不能为空', + ]; + } +} diff --git a/modules/Merchant/Http/Requests/OrderPaymentFormRequest.php b/modules/Merchant/Http/Requests/OrderPaymentFormRequest.php new file mode 100644 index 0000000..a22f9e5 --- /dev/null +++ b/modules/Merchant/Http/Requests/OrderPaymentFormRequest.php @@ -0,0 +1,54 @@ +isUpdate()) { + return [ + 'payment_no' => 'required|string|max:32|unique:order_payments,payment_no', + 'order_id' => 'required|integer', + 'user_id' => 'required|integer', + 'payment_transaction_id' => 'nullable|integer', + 'amount' => 'required|numeric', + 'pay_type' => 'required|integer|in:0,1,2', + 'status' => 'required|integer|in:0,1,2', + 'paid_at' => 'nullable|date', + 'refund_at' => 'nullable|date', + ]; + } + + $id = $this->route('id'); + return [ + 'payment_no' => ['required', 'string', 'max:32', Rule::unique('order_payments', 'payment_no')->ignore($id)], + 'order_id' => 'required|integer', + 'user_id' => 'required|integer', + 'payment_transaction_id' => 'nullable|integer', + 'amount' => 'required|numeric', + 'pay_type' => 'required|integer|in:0,1,2', + 'status' => 'required|integer|in:0,1,2', + 'paid_at' => 'nullable|date', + 'refund_at' => 'nullable|date', + ]; + } + + public function messages(): array + { + return [ + 'payment_no.required' => '支付单号不能为空', + 'payment_no.unique' => '支付单号已存在', + 'order_id.required' => '订单ID不能为空', + 'user_id.required' => '用户ID不能为空', + 'amount.required' => '支付金额不能为空', + 'pay_type.required' => '支付类型不能为空', + 'status.required' => '支付状态不能为空', + ]; + } +} diff --git a/modules/Merchant/Http/Requests/OrderRefundFormRequest.php b/modules/Merchant/Http/Requests/OrderRefundFormRequest.php new file mode 100644 index 0000000..03c0a63 --- /dev/null +++ b/modules/Merchant/Http/Requests/OrderRefundFormRequest.php @@ -0,0 +1,59 @@ +isUpdate()) { + return [ + 'refund_no' => 'required|string|max:32|unique:order_refunds,refund_no', + 'order_id' => 'required|integer', + 'order_item_id' => 'nullable|integer', + 'user_id' => 'required|integer', + 'merchant_id' => 'required|integer', + 'amount' => 'required|numeric', + 'reason' => 'required|string', + 'images' => 'nullable|array', + 'status' => 'required|integer|in:0,1,2,3', + 'reply' => 'nullable|string', + 'processed_at' => 'nullable|date', + ]; + } + + $id = $this->route('id'); + return [ + 'refund_no' => ['required', 'string', 'max:32', Rule::unique('order_refunds', 'refund_no')->ignore($id)], + 'order_id' => 'required|integer', + 'order_item_id' => 'nullable|integer', + 'user_id' => 'required|integer', + 'merchant_id' => 'required|integer', + 'amount' => 'required|numeric', + 'reason' => 'required|string', + 'images' => 'nullable|array', + 'status' => 'required|integer|in:0,1,2,3', + 'reply' => 'nullable|string', + 'processed_at' => 'nullable|date', + ]; + } + + public function messages(): array + { + return [ + 'refund_no.required' => '退款单号不能为空', + 'refund_no.unique' => '退款单号已存在', + 'order_id.required' => '订单ID不能为空', + 'user_id.required' => '用户ID不能为空', + 'merchant_id.required' => '商户ID不能为空', + 'amount.required' => '退款金额不能为空', + 'reason.required' => '退款原因不能为空', + 'status.required' => '退款状态不能为空', + ]; + } +} diff --git a/modules/Merchant/Models/MerchantWithdrawalModel.php b/modules/Merchant/Models/MerchantWithdrawalModel.php new file mode 100644 index 0000000..b7359f4 --- /dev/null +++ b/modules/Merchant/Models/MerchantWithdrawalModel.php @@ -0,0 +1,29 @@ + 'integer', + 'amount' => 'decimal:2', + 'account_info' => 'array', + 'status' => 'integer', + 'processed_at' => 'datetime', + ]; +} diff --git a/modules/Merchant/Models/OrderItemModel.php b/modules/Merchant/Models/OrderItemModel.php new file mode 100644 index 0000000..4368178 --- /dev/null +++ b/modules/Merchant/Models/OrderItemModel.php @@ -0,0 +1,28 @@ + 'integer', + 'product_id' => 'integer', + 'price' => 'decimal:2', + 'quantity' => 'integer', + 'subtotal' => 'decimal:2', + ]; +} diff --git a/modules/Merchant/Models/OrderModel.php b/modules/Merchant/Models/OrderModel.php new file mode 100644 index 0000000..c68bc98 --- /dev/null +++ b/modules/Merchant/Models/OrderModel.php @@ -0,0 +1,43 @@ + 'decimal:2', + 'discount_amount' => 'decimal:2', + 'delivery_fee' => 'decimal:2', + 'payment_amount' => 'decimal:2', + 'pay_type' => 'integer', + 'pay_status' => 'integer', + 'status' => 'integer', + 'user_id' => 'integer', + 'merchant_id' => 'integer', + 'address_id' => 'integer', + 'paid_at' => 'datetime', + 'completed_at' => 'datetime', + ]; +} diff --git a/modules/Merchant/Models/OrderPaymentModel.php b/modules/Merchant/Models/OrderPaymentModel.php new file mode 100644 index 0000000..29b14c4 --- /dev/null +++ b/modules/Merchant/Models/OrderPaymentModel.php @@ -0,0 +1,33 @@ + 'integer', + 'user_id' => 'integer', + 'payment_transaction_id' => 'integer', + 'amount' => 'decimal:2', + 'pay_type' => 'integer', + 'status' => 'integer', + 'paid_at' => 'datetime', + 'refund_at' => 'datetime', + ]; +} diff --git a/modules/Merchant/Models/OrderRefundModel.php b/modules/Merchant/Models/OrderRefundModel.php new file mode 100644 index 0000000..95113a4 --- /dev/null +++ b/modules/Merchant/Models/OrderRefundModel.php @@ -0,0 +1,35 @@ + 'integer', + 'order_item_id' => 'integer', + 'user_id' => 'integer', + 'merchant_id' => 'integer', + 'amount' => 'decimal:2', + 'images' => 'array', + 'status' => 'integer', + 'processed_at' => 'datetime', + ]; +} diff --git a/web/domain/iMerchantWithdrawal.ts b/web/domain/iMerchantWithdrawal.ts new file mode 100644 index 0000000..ab5d424 --- /dev/null +++ b/web/domain/iMerchantWithdrawal.ts @@ -0,0 +1,13 @@ +export default interface IMerchantWithdrawal { + id?: number; + withdraw_no?: string; + merchant_id?: number; + amount?: number; + channel?: string; + account_info?: string; + status?: number; + remark?: string; + processed_at?: string; + created_at?: string; + updated_at?: string; +} diff --git a/web/domain/iOrder.ts b/web/domain/iOrder.ts new file mode 100644 index 0000000..ed9910e --- /dev/null +++ b/web/domain/iOrder.ts @@ -0,0 +1,20 @@ +export default interface IOrder { + id?: number; + order_no?: string; + user_id?: number; + merchant_id?: number; + address_id?: number; + total_amount?: number; + discount_amount?: number; + delivery_fee?: number; + payment_amount?: number; + pay_type?: number; + pay_status?: number; + paid_at?: string; + status?: number; + cancel_reason?: string; + remark?: string; + completed_at?: string; + created_at?: string; + updated_at?: string; +} diff --git a/web/domain/iOrderPayment.ts b/web/domain/iOrderPayment.ts new file mode 100644 index 0000000..a82e8f7 --- /dev/null +++ b/web/domain/iOrderPayment.ts @@ -0,0 +1,14 @@ +export default interface IOrderPayment { + id?: number; + payment_no?: string; + order_id?: number; + user_id?: number; + payment_transaction_id?: string; + amount?: number; + pay_type?: number; + status?: number; + paid_at?: string; + refund_at?: string; + created_at?: string; + updated_at?: string; +} diff --git a/web/domain/iOrderRefund.ts b/web/domain/iOrderRefund.ts new file mode 100644 index 0000000..22ad4f3 --- /dev/null +++ b/web/domain/iOrderRefund.ts @@ -0,0 +1,16 @@ +export default interface IOrderRefund { + id?: number; + refund_no?: string; + order_id?: number; + order_item_id?: number; + user_id?: number; + merchant_id?: number; + amount?: number; + reason?: string; + images?: string; + status?: number; + reply?: string; + processed_at?: string; + created_at?: string; + updated_at?: string; +} diff --git a/web/locales/en_US/finance/merchant-withdrawal.ts b/web/locales/en_US/finance/merchant-withdrawal.ts new file mode 100644 index 0000000..9fe7540 --- /dev/null +++ b/web/locales/en_US/finance/merchant-withdrawal.ts @@ -0,0 +1,22 @@ +export default { + "merchant.withdrawal.page.title": "Merchant Withdrawals", + "merchant.withdrawal.page.description": "Manage merchant withdrawal requests", + "merchant.withdrawal.id": "ID", + "merchant.withdrawal.withdraw_no": "Withdraw No.", + "merchant.withdrawal.merchant_id": "Merchant ID", + "merchant.withdrawal.amount": "Amount", + "merchant.withdrawal.channel": "Channel", + "merchant.withdrawal.channel.wechat": "WeChat", + "merchant.withdrawal.channel.alipay": "Alipay", + "merchant.withdrawal.channel.bank": "Bank Card", + "merchant.withdrawal.account_info": "Account Info", + "merchant.withdrawal.status": "Status", + "merchant.withdrawal.status.0": "Pending Review", + "merchant.withdrawal.status.1": "Approved", + "merchant.withdrawal.status.2": "Completed", + "merchant.withdrawal.status.3": "Rejected", + "merchant.withdrawal.remark": "Remark", + "merchant.withdrawal.processed_at": "Processed At", + "merchant.withdrawal.created_at": "Created At", + "merchant.withdrawal.updated_at": "Updated At", +}; diff --git a/web/locales/en_US/finance/order-payment.ts b/web/locales/en_US/finance/order-payment.ts new file mode 100644 index 0000000..43e25bd --- /dev/null +++ b/web/locales/en_US/finance/order-payment.ts @@ -0,0 +1,21 @@ +export default { + "merchant.order_payment.page.title": "Order Payment Logs", + "merchant.order_payment.page.description": "View order payment records", + "merchant.order_payment.id": "ID", + "merchant.order_payment.payment_no": "Payment No.", + "merchant.order_payment.order_id": "Order ID", + "merchant.order_payment.user_id": "User ID", + "merchant.order_payment.payment_transaction_id": "Transaction ID", + "merchant.order_payment.amount": "Amount", + "merchant.order_payment.pay_type": "Pay Type", + "merchant.order_payment.pay_type.0": "Balance", + "merchant.order_payment.pay_type.1": "WeChat", + "merchant.order_payment.status": "Status", + "merchant.order_payment.status.0": "Unpaid", + "merchant.order_payment.status.1": "Paid", + "merchant.order_payment.status.2": "Refunded", + "merchant.order_payment.paid_at": "Paid At", + "merchant.order_payment.refund_at": "Refund At", + "merchant.order_payment.created_at": "Created At", + "merchant.order_payment.updated_at": "Updated At", +}; diff --git a/web/locales/en_US/index.ts b/web/locales/en_US/index.ts index e1fa32a..9aa9b43 100644 --- a/web/locales/en_US/index.ts +++ b/web/locales/en_US/index.ts @@ -37,6 +37,8 @@ import merchantProductCategory from "./merchant/product-category"; import merchantProduct from "./merchant/product"; import merchantPrinter from "./merchant/printer"; import merchantPrintTask from "./merchant/print-task"; +import merchantOrder from "./merchant/order"; +import merchantRefund from "./merchant/refund"; import taskCategory from "./task/category"; import taskOrderErrand from "./task/order-errand"; @@ -55,6 +57,8 @@ import financeDeposit from "./finance/deposit"; import financeDepositPayment from "./finance/deposit-payment"; import financeWithdrawal from "./finance/withdrawal"; import financeTransaction from "./finance/transaction"; +import financeOrderPayment from "./finance/order-payment"; +import financeMerchantWithdrawal from "./finance/merchant-withdrawal"; import userProfile from "./user/profile"; @@ -98,6 +102,8 @@ export default { ...merchantProduct, ...merchantPrinter, ...merchantPrintTask, + ...merchantOrder, + ...merchantRefund, ...taskCategory, ...taskOrderErrand, ...taskOrderPickup, @@ -113,6 +119,8 @@ export default { ...financeDepositPayment, ...financeWithdrawal, ...financeTransaction, + ...financeOrderPayment, + ...financeMerchantWithdrawal, ...userProfile, ...xinForm, ...xinTable, diff --git a/web/locales/en_US/menu.ts b/web/locales/en_US/menu.ts index 1a21152..45df323 100644 --- a/web/locales/en_US/menu.ts +++ b/web/locales/en_US/menu.ts @@ -61,6 +61,8 @@ export default { "menu.merchant.product": "Products", "menu.merchant.printer": "Printers", "menu.merchant.print-task": "Print Tasks", + "menu.merchant.order": "Orders", + "menu.merchant.refund": "Refunds", "menu.task": "Campus Tasks", "menu.task.orders": "Task Orders", "menu.task.category": "Categories", @@ -80,5 +82,7 @@ export default { "menu.finance.deposit_payment": "Deposit Payments", "menu.finance.withdrawal": "Withdrawals", "menu.finance.transaction": "Transactions", + "menu.finance.order_payment": "Order Payments", + "menu.finance.merchant_withdrawal": "Merchant Withdrawals", "menu.xin-admin": "XinAdmin", } diff --git a/web/locales/en_US/merchant/order.ts b/web/locales/en_US/merchant/order.ts new file mode 100644 index 0000000..9ba45e9 --- /dev/null +++ b/web/locales/en_US/merchant/order.ts @@ -0,0 +1,33 @@ +export default { + "merchant.order.page.title": "Order Management", + "merchant.order.page.description": "Manage campus merchant orders", + "merchant.order.id": "ID", + "merchant.order.order_no": "Order No.", + "merchant.order.user_id": "User ID", + "merchant.order.merchant_id": "Merchant ID", + "merchant.order.address_id": "Address ID", + "merchant.order.total_amount": "Total Amount", + "merchant.order.discount_amount": "Discount", + "merchant.order.delivery_fee": "Delivery Fee", + "merchant.order.payment_amount": "Payment Amount", + "merchant.order.pay_type": "Pay Type", + "merchant.order.pay_type.0": "Balance", + "merchant.order.pay_type.1": "WeChat", + "merchant.order.pay_status": "Pay Status", + "merchant.order.pay_status.0": "Unpaid", + "merchant.order.pay_status.1": "Paid", + "merchant.order.pay_status.2": "Refunding", + "merchant.order.pay_status.3": "Refunded", + "merchant.order.status": "Order Status", + "merchant.order.status.0": "Unpaid", + "merchant.order.status.1": "Pending Delivery", + "merchant.order.status.2": "Delivering", + "merchant.order.status.3": "Completed", + "merchant.order.status.4": "Cancelled", + "merchant.order.cancel_reason": "Cancel Reason", + "merchant.order.remark": "Remark", + "merchant.order.paid_at": "Paid At", + "merchant.order.completed_at": "Completed At", + "merchant.order.created_at": "Created At", + "merchant.order.updated_at": "Updated At", +}; diff --git a/web/locales/en_US/merchant/refund.ts b/web/locales/en_US/merchant/refund.ts new file mode 100644 index 0000000..5cce500 --- /dev/null +++ b/web/locales/en_US/merchant/refund.ts @@ -0,0 +1,22 @@ +export default { + "merchant.refund.page.title": "Order Refunds", + "merchant.refund.page.description": "Handle user refund requests", + "merchant.refund.id": "ID", + "merchant.refund.refund_no": "Refund No.", + "merchant.refund.order_id": "Order ID", + "merchant.refund.order_item_id": "Order Item ID", + "merchant.refund.user_id": "User ID", + "merchant.refund.merchant_id": "Merchant ID", + "merchant.refund.amount": "Amount", + "merchant.refund.reason": "Reason", + "merchant.refund.images": "Images", + "merchant.refund.status": "Status", + "merchant.refund.status.0": "Pending", + "merchant.refund.status.1": "Approved", + "merchant.refund.status.2": "Completed", + "merchant.refund.status.3": "Rejected", + "merchant.refund.reply": "Reply", + "merchant.refund.processed_at": "Processed At", + "merchant.refund.created_at": "Created At", + "merchant.refund.updated_at": "Updated At", +}; diff --git a/web/locales/zh_CN/finance/merchant-withdrawal.ts b/web/locales/zh_CN/finance/merchant-withdrawal.ts new file mode 100644 index 0000000..ae421a6 --- /dev/null +++ b/web/locales/zh_CN/finance/merchant-withdrawal.ts @@ -0,0 +1,22 @@ +export default { + "merchant.withdrawal.page.title": "商户提现", + "merchant.withdrawal.page.description": "管理商户提现申请", + "merchant.withdrawal.id": "ID", + "merchant.withdrawal.withdraw_no": "提现编号", + "merchant.withdrawal.merchant_id": "商户ID", + "merchant.withdrawal.amount": "提现金额", + "merchant.withdrawal.channel": "提现渠道", + "merchant.withdrawal.channel.wechat": "微信", + "merchant.withdrawal.channel.alipay": "支付宝", + "merchant.withdrawal.channel.bank": "银行卡", + "merchant.withdrawal.account_info": "账户信息", + "merchant.withdrawal.status": "审核状态", + "merchant.withdrawal.status.0": "待审核", + "merchant.withdrawal.status.1": "已通过", + "merchant.withdrawal.status.2": "已完成", + "merchant.withdrawal.status.3": "已拒绝", + "merchant.withdrawal.remark": "备注", + "merchant.withdrawal.processed_at": "处理时间", + "merchant.withdrawal.created_at": "申请时间", + "merchant.withdrawal.updated_at": "更新时间", +}; diff --git a/web/locales/zh_CN/finance/order-payment.ts b/web/locales/zh_CN/finance/order-payment.ts new file mode 100644 index 0000000..dbba4df --- /dev/null +++ b/web/locales/zh_CN/finance/order-payment.ts @@ -0,0 +1,21 @@ +export default { + "merchant.order_payment.page.title": "订单支付日志", + "merchant.order_payment.page.description": "查看商城订单支付记录", + "merchant.order_payment.id": "ID", + "merchant.order_payment.payment_no": "支付编号", + "merchant.order_payment.order_id": "订单ID", + "merchant.order_payment.user_id": "用户ID", + "merchant.order_payment.payment_transaction_id": "三方交易ID", + "merchant.order_payment.amount": "支付金额", + "merchant.order_payment.pay_type": "支付方式", + "merchant.order_payment.pay_type.0": "余额", + "merchant.order_payment.pay_type.1": "微信", + "merchant.order_payment.status": "支付状态", + "merchant.order_payment.status.0": "待支付", + "merchant.order_payment.status.1": "已支付", + "merchant.order_payment.status.2": "已退款", + "merchant.order_payment.paid_at": "支付时间", + "merchant.order_payment.refund_at": "退款时间", + "merchant.order_payment.created_at": "创建时间", + "merchant.order_payment.updated_at": "更新时间", +}; diff --git a/web/locales/zh_CN/index.ts b/web/locales/zh_CN/index.ts index e1fa32a..9aa9b43 100644 --- a/web/locales/zh_CN/index.ts +++ b/web/locales/zh_CN/index.ts @@ -37,6 +37,8 @@ import merchantProductCategory from "./merchant/product-category"; import merchantProduct from "./merchant/product"; import merchantPrinter from "./merchant/printer"; import merchantPrintTask from "./merchant/print-task"; +import merchantOrder from "./merchant/order"; +import merchantRefund from "./merchant/refund"; import taskCategory from "./task/category"; import taskOrderErrand from "./task/order-errand"; @@ -55,6 +57,8 @@ import financeDeposit from "./finance/deposit"; import financeDepositPayment from "./finance/deposit-payment"; import financeWithdrawal from "./finance/withdrawal"; import financeTransaction from "./finance/transaction"; +import financeOrderPayment from "./finance/order-payment"; +import financeMerchantWithdrawal from "./finance/merchant-withdrawal"; import userProfile from "./user/profile"; @@ -98,6 +102,8 @@ export default { ...merchantProduct, ...merchantPrinter, ...merchantPrintTask, + ...merchantOrder, + ...merchantRefund, ...taskCategory, ...taskOrderErrand, ...taskOrderPickup, @@ -113,6 +119,8 @@ export default { ...financeDepositPayment, ...financeWithdrawal, ...financeTransaction, + ...financeOrderPayment, + ...financeMerchantWithdrawal, ...userProfile, ...xinForm, ...xinTable, diff --git a/web/locales/zh_CN/menu.ts b/web/locales/zh_CN/menu.ts index 5cebcac..9ad3b3b 100644 --- a/web/locales/zh_CN/menu.ts +++ b/web/locales/zh_CN/menu.ts @@ -61,6 +61,8 @@ export default { "menu.merchant.product": "商品管理", "menu.merchant.printer": "打印机管理", "menu.merchant.print-task": "打印任务", + "menu.merchant.order": "订单管理", + "menu.merchant.refund": "订单退款", "menu.task": "校园任务", "menu.task.category": "任务分类", "menu.task.order_errand": "跑腿订单", @@ -80,5 +82,7 @@ export default { "menu.finance.deposit_payment": "保证金支付日志", "menu.finance.withdrawal": "接单员提现", "menu.finance.transaction": "三方支付流水", + "menu.finance.order_payment": "订单支付日志", + "menu.finance.merchant_withdrawal": "商户提现", "menu.xin-admin": "XinAdmin", }; diff --git a/web/locales/zh_CN/merchant/order.ts b/web/locales/zh_CN/merchant/order.ts new file mode 100644 index 0000000..9f37e19 --- /dev/null +++ b/web/locales/zh_CN/merchant/order.ts @@ -0,0 +1,33 @@ +export default { + "merchant.order.page.title": "订单管理", + "merchant.order.page.description": "管理校园商户订单", + "merchant.order.id": "ID", + "merchant.order.order_no": "订单号", + "merchant.order.user_id": "用户ID", + "merchant.order.merchant_id": "商户ID", + "merchant.order.address_id": "地址ID", + "merchant.order.total_amount": "订单总额", + "merchant.order.discount_amount": "优惠金额", + "merchant.order.delivery_fee": "配送费", + "merchant.order.payment_amount": "实付金额", + "merchant.order.pay_type": "支付方式", + "merchant.order.pay_type.0": "余额", + "merchant.order.pay_type.1": "微信", + "merchant.order.pay_status": "支付状态", + "merchant.order.pay_status.0": "待支付", + "merchant.order.pay_status.1": "已支付", + "merchant.order.pay_status.2": "退款中", + "merchant.order.pay_status.3": "已退款", + "merchant.order.status": "订单状态", + "merchant.order.status.0": "待支付", + "merchant.order.status.1": "待配送", + "merchant.order.status.2": "配送中", + "merchant.order.status.3": "已完成", + "merchant.order.status.4": "已取消", + "merchant.order.cancel_reason": "取消原因", + "merchant.order.remark": "备注", + "merchant.order.paid_at": "支付时间", + "merchant.order.completed_at": "完成时间", + "merchant.order.created_at": "创建时间", + "merchant.order.updated_at": "更新时间", +}; diff --git a/web/locales/zh_CN/merchant/refund.ts b/web/locales/zh_CN/merchant/refund.ts new file mode 100644 index 0000000..6481507 --- /dev/null +++ b/web/locales/zh_CN/merchant/refund.ts @@ -0,0 +1,22 @@ +export default { + "merchant.refund.page.title": "订单退款", + "merchant.refund.page.description": "处理用户退款申请", + "merchant.refund.id": "ID", + "merchant.refund.refund_no": "退款编号", + "merchant.refund.order_id": "订单ID", + "merchant.refund.order_item_id": "订单项ID", + "merchant.refund.user_id": "用户ID", + "merchant.refund.merchant_id": "商户ID", + "merchant.refund.amount": "退款金额", + "merchant.refund.reason": "退款原因", + "merchant.refund.images": "凭证图片", + "merchant.refund.status": "退款状态", + "merchant.refund.status.0": "待处理", + "merchant.refund.status.1": "已同意", + "merchant.refund.status.2": "已完成", + "merchant.refund.status.3": "已拒绝", + "merchant.refund.reply": "处理回复", + "merchant.refund.processed_at": "处理时间", + "merchant.refund.created_at": "申请时间", + "merchant.refund.updated_at": "更新时间", +}; diff --git a/web/pages/finance/merchant-withdrawal/index.tsx b/web/pages/finance/merchant-withdrawal/index.tsx new file mode 100644 index 0000000..4d794c5 --- /dev/null +++ b/web/pages/finance/merchant-withdrawal/index.tsx @@ -0,0 +1,158 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IMerchantWithdrawal from '@/domain/iMerchantWithdrawal'; +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.withdrawal.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.withdrawal.withdraw_no'), + dataIndex: 'withdraw_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.withdrawal.merchant_id'), + dataIndex: 'merchant_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.withdrawal.amount'), + dataIndex: 'amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.withdrawal.channel'), + dataIndex: 'channel', + valueType: 'select', + fieldProps: { + options: [ + { value: 'wechat', label: t('merchant.withdrawal.channel.wechat') }, + { value: 'alipay', label: t('merchant.withdrawal.channel.alipay') }, + { value: 'bank', label: t('merchant.withdrawal.channel.bank') }, + ], + }, + render: (value: string) => { + const colorMap: Record = { wechat: 'green', alipay: 'blue', bank: 'orange' }; + const labelMap: Record = { + wechat: t('merchant.withdrawal.channel.wechat'), + alipay: t('merchant.withdrawal.channel.alipay'), + bank: t('merchant.withdrawal.channel.bank'), + }; + return {labelMap[value] ?? value}; + }, + filters: [ + { text: t('merchant.withdrawal.channel.wechat'), value: 'wechat' }, + { text: t('merchant.withdrawal.channel.alipay'), value: 'alipay' }, + { text: t('merchant.withdrawal.channel.bank'), value: 'bank' }, + ], + align: 'center', + }, + { + title: t('merchant.withdrawal.status'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.withdrawal.status.0') }, + { value: 1, label: t('merchant.withdrawal.status.1') }, + { value: 2, label: t('merchant.withdrawal.status.2') }, + { value: 3, label: t('merchant.withdrawal.status.3') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'processing', 2: 'success', 3: 'error' }; + const labelMap: Record = { + 0: t('merchant.withdrawal.status.0'), + 1: t('merchant.withdrawal.status.1'), + 2: t('merchant.withdrawal.status.2'), + 3: t('merchant.withdrawal.status.3'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.withdrawal.status.0'), value: 0 }, + { text: t('merchant.withdrawal.status.1'), value: 1 }, + { text: t('merchant.withdrawal.status.2'), value: 2 }, + { text: t('merchant.withdrawal.status.3'), value: 3 }, + ], + align: 'center', + }, + { + title: t('merchant.withdrawal.remark'), + dataIndex: 'remark', + valueType: 'text', + hideInSearch: true, + align: 'center', + }, + { + title: t('merchant.withdrawal.processed_at'), + dataIndex: 'processed_at', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'), + }, + { + title: t('merchant.withdrawal.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant/withdrawal', + columns, + rowKey: 'id', + accessName: 'merchant.withdrawal', + addShow: false, + scroll: { x: 1200 }, + 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.withdrawal.page.title')} + {t('merchant.withdrawal.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/finance/order-payment/index.tsx b/web/pages/finance/order-payment/index.tsx new file mode 100644 index 0000000..94b01b5 --- /dev/null +++ b/web/pages/finance/order-payment/index.tsx @@ -0,0 +1,161 @@ +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.order_payment.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.order_payment.payment_no'), + dataIndex: 'payment_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.order_payment.order_id'), + dataIndex: 'order_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.order_payment.user_id'), + dataIndex: 'user_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.order_payment.amount'), + dataIndex: 'amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.order_payment.pay_type'), + dataIndex: 'pay_type', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.order_payment.pay_type.0') }, + { value: 1, label: t('merchant.order_payment.pay_type.1') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'default', 1: 'blue' }; + const labelMap: Record = { + 0: t('merchant.order_payment.pay_type.0'), + 1: t('merchant.order_payment.pay_type.1'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.order_payment.pay_type.0'), value: 0 }, + { text: t('merchant.order_payment.pay_type.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.order_payment.status'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.order_payment.status.0') }, + { value: 1, label: t('merchant.order_payment.status.1') }, + { value: 2, label: t('merchant.order_payment.status.2') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'success', 2: 'default' }; + const labelMap: Record = { + 0: t('merchant.order_payment.status.0'), + 1: t('merchant.order_payment.status.1'), + 2: t('merchant.order_payment.status.2'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.order_payment.status.0'), value: 0 }, + { text: t('merchant.order_payment.status.1'), value: 1 }, + { text: t('merchant.order_payment.status.2'), value: 2 }, + ], + align: 'center', + }, + { + title: t('merchant.order_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.order_payment.refund_at'), + dataIndex: 'refund_at', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'), + }, + { + title: t('merchant.order_payment.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant/order-payment', + columns, + rowKey: 'id', + accessName: 'merchant.order_payment', + addShow: false, + editShow: false, + scroll: { x: 1200 }, + 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.order_payment.page.title')} + {t('merchant.order_payment.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant/order/index.tsx b/web/pages/merchant/order/index.tsx new file mode 100644 index 0000000..4aed27a --- /dev/null +++ b/web/pages/merchant/order/index.tsx @@ -0,0 +1,226 @@ +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.order.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.order.order_no'), + dataIndex: 'order_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.order.user_id'), + dataIndex: 'user_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.order.merchant_id'), + dataIndex: 'merchant_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.order.total_amount'), + dataIndex: 'total_amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.order.discount_amount'), + dataIndex: 'discount_amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.order.delivery_fee'), + dataIndex: 'delivery_fee', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.order.payment_amount'), + dataIndex: 'payment_amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.order.pay_type'), + dataIndex: 'pay_type', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.order.pay_type.0') }, + { value: 1, label: t('merchant.order.pay_type.1') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'default', 1: 'blue' }; + const labelMap: Record = { + 0: t('merchant.order.pay_type.0'), + 1: t('merchant.order.pay_type.1'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.order.pay_type.0'), value: 0 }, + { text: t('merchant.order.pay_type.1'), value: 1 }, + ], + align: 'center', + }, + { + title: t('merchant.order.pay_status'), + dataIndex: 'pay_status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.order.pay_status.0') }, + { value: 1, label: t('merchant.order.pay_status.1') }, + { value: 2, label: t('merchant.order.pay_status.2') }, + { value: 3, label: t('merchant.order.pay_status.3') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'success', 2: 'processing', 3: 'default' }; + const labelMap: Record = { + 0: t('merchant.order.pay_status.0'), + 1: t('merchant.order.pay_status.1'), + 2: t('merchant.order.pay_status.2'), + 3: t('merchant.order.pay_status.3'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.order.pay_status.0'), value: 0 }, + { text: t('merchant.order.pay_status.1'), value: 1 }, + { text: t('merchant.order.pay_status.2'), value: 2 }, + { text: t('merchant.order.pay_status.3'), value: 3 }, + ], + align: 'center', + }, + { + title: t('merchant.order.status'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.order.status.0') }, + { value: 1, label: t('merchant.order.status.1') }, + { value: 2, label: t('merchant.order.status.2') }, + { value: 3, label: t('merchant.order.status.3') }, + { value: 4, label: t('merchant.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.order.status.0'), + 1: t('merchant.order.status.1'), + 2: t('merchant.order.status.2'), + 3: t('merchant.order.status.3'), + 4: t('merchant.order.status.4'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.order.status.0'), value: 0 }, + { text: t('merchant.order.status.1'), value: 1 }, + { text: t('merchant.order.status.2'), value: 2 }, + { text: t('merchant.order.status.3'), value: 3 }, + { text: t('merchant.order.status.4'), value: 4 }, + ], + align: 'center', + }, + { + title: t('merchant.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.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.order.remark'), + dataIndex: 'remark', + valueType: 'text', + hideInSearch: true, + align: 'center', + }, + { + title: t('merchant.order.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant/order', + columns, + rowKey: 'id', + accessName: 'merchant.order', + 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.order.page.title')} + {t('merchant.order.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table; diff --git a/web/pages/merchant/refund/index.tsx b/web/pages/merchant/refund/index.tsx new file mode 100644 index 0000000..73b37ce --- /dev/null +++ b/web/pages/merchant/refund/index.tsx @@ -0,0 +1,152 @@ +import { Tag, Typography } from 'antd'; +import React from 'react'; +import XinTable from '@/components/XinTable'; +import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings'; +import type IOrderRefund from '@/domain/iOrderRefund'; +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.refund.id'), + dataIndex: 'id', + hideInForm: true, + sorter: true, + align: 'center', + width: 80, + }, + { + title: t('merchant.refund.refund_no'), + dataIndex: 'refund_no', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.refund.order_id'), + dataIndex: 'order_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.refund.user_id'), + dataIndex: 'user_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.refund.merchant_id'), + dataIndex: 'merchant_id', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: t('merchant.refund.amount'), + dataIndex: 'amount', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: number) => `¥${value ?? '0.00'}`, + }, + { + title: t('merchant.refund.reason'), + dataIndex: 'reason', + valueType: 'text', + hideInSearch: true, + align: 'center', + }, + { + title: t('merchant.refund.status'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { value: 0, label: t('merchant.refund.status.0') }, + { value: 1, label: t('merchant.refund.status.1') }, + { value: 2, label: t('merchant.refund.status.2') }, + { value: 3, label: t('merchant.refund.status.3') }, + ], + }, + render: (value: number) => { + const colorMap: Record = { 0: 'warning', 1: 'processing', 2: 'success', 3: 'error' }; + const labelMap: Record = { + 0: t('merchant.refund.status.0'), + 1: t('merchant.refund.status.1'), + 2: t('merchant.refund.status.2'), + 3: t('merchant.refund.status.3'), + }; + return {labelMap[value] ?? '-'}; + }, + filters: [ + { text: t('merchant.refund.status.0'), value: 0 }, + { text: t('merchant.refund.status.1'), value: 1 }, + { text: t('merchant.refund.status.2'), value: 2 }, + { text: t('merchant.refund.status.3'), value: 3 }, + ], + align: 'center', + }, + { + title: t('merchant.refund.reply'), + dataIndex: 'reply', + valueType: 'text', + hideInSearch: true, + align: 'center', + }, + { + title: t('merchant.refund.processed_at'), + dataIndex: 'processed_at', + hideInForm: true, + hideInSearch: true, + align: 'center', + render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'), + }, + { + title: t('merchant.refund.created_at'), + hideInForm: true, + hideInSearch: true, + dataIndex: 'created_at', + align: 'center', + render: (value: string) => (value ? dayjs(value).fromNow() : '-'), + }, + ]; + + const tableProps: XinTableProps = { + api: '/merchant/refund', + columns, + rowKey: 'id', + accessName: 'merchant.refund', + addShow: false, + scroll: { x: 1200 }, + 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.refund.page.title')} + {t('merchant.refund.page.description')} +
+ {...tableProps} /> + + ); +}; + +export default Table;