From 4fbc81f0d3d7db00beb962971556fe85b03b3757 Mon Sep 17 00:00:00 2001
From: lotusnetwork <143520796+lotusnetwork@users.noreply.github.com>
Date: Wed, 30 Aug 2023 06:48:04 +0000
Subject: [PATCH] Release PaperDoll@build0
---
.env.example | 48 +
.gitattributes | 5 +
.gitignore | 40 +-
LICENSE | 2 +-
README.md | 31 +-
app/Console/Commands/CheckCommission.php | 127 +
app/Console/Commands/CheckOrder.php | 54 +
app/Console/Commands/CheckServer.php | 65 +
app/Console/Commands/CheckTicket.php | 52 +
app/Console/Commands/ClearUser.php | 51 +
app/Console/Commands/ResetLog.php | 52 +
app/Console/Commands/ResetPassword.php | 54 +
app/Console/Commands/ResetTraffic.php | 164 +
app/Console/Commands/ResetUser.php | 58 +
app/Console/Commands/SendRemindMail.php | 51 +
app/Console/Commands/Test.php | 41 +
app/Console/Commands/V2boardInstall.php | 155 +
app/Console/Commands/V2boardStatistics.php | 131 +
app/Console/Commands/V2boardUpdate.php | 63 +
app/Console/Kernel.php | 56 +
app/Exceptions/Handler.php | 77 +
.../Controllers/Admin/ConfigController.php | 202 +
.../Controllers/Admin/CouponController.php | 137 +
.../Controllers/Admin/KnowledgeController.php | 113 +
.../Controllers/Admin/NoticeController.php | 81 +
.../Controllers/Admin/OrderController.php | 190 +
.../Controllers/Admin/PaymentController.php | 133 +
app/Http/Controllers/Admin/PlanController.php | 125 +
.../Admin/Server/GroupController.php | 85 +
.../Admin/Server/HysteriaController.php | 113 +
.../Admin/Server/ManageController.php | 44 +
.../Admin/Server/RouteController.php | 72 +
.../Admin/Server/ShadowsocksController.php | 91 +
.../Admin/Server/TrojanController.php | 99 +
.../Admin/Server/VmessController.php | 92 +
app/Http/Controllers/Admin/StatController.php | 227 +
.../Controllers/Admin/SystemController.php | 105 +
.../Controllers/Admin/ThemeController.php | 91 +
.../Controllers/Admin/TicketController.php | 96 +
app/Http/Controllers/Admin/UserController.php | 294 ++
.../Controllers/Client/ClientController.php | 62 +
.../Controllers/Client/Protocols/Clash.php | 272 +
.../Controllers/Client/Protocols/GCLH.php | 272 +
.../Controllers/Client/Protocols/General.php | 182 +
.../Client/Protocols/QuantumultX.php | 116 +
.../Client/Protocols/Shadowsocks.php | 69 +
.../Controllers/Client/Protocols/Stash.php | 272 +
.../Client/Protocols/Surfboard.php | 161 +
.../Controllers/Client/Protocols/Surge.php | 162 +
app/Http/Controllers/Controller.php | 12 +
app/Http/Controllers/Guest/CommController.php | 38 +
.../Controllers/Guest/PaymentController.php | 49 +
app/Http/Controllers/Guest/PlanController.php | 18 +
.../Controllers/Guest/TelegramController.php | 123 +
.../Controllers/Passport/AuthController.php | 307 ++
.../Controllers/Passport/CommController.php | 82 +
.../Controllers/Server/UniProxyController.php | 139 +
.../Controllers/Staff/NoticeController.php | 59 +
app/Http/Controllers/Staff/PlanController.php | 41 +
.../Controllers/Staff/TicketController.php | 85 +
app/Http/Controllers/Staff/UserController.php | 107 +
app/Http/Controllers/User/CommController.php | 41 +
.../Controllers/User/CouponController.php | 25 +
.../Controllers/User/InviteController.php | 88 +
.../Controllers/User/KnowledgeController.php | 73 +
.../Controllers/User/NoticeController.php | 26 +
app/Http/Controllers/User/OrderController.php | 267 +
app/Http/Controllers/User/PlanController.php | 43 +
.../Controllers/User/ServerController.php | 38 +
app/Http/Controllers/User/StatController.php | 28 +
.../Controllers/User/TelegramController.php | 27 +
.../Controllers/User/TicketController.php | 198 +
app/Http/Controllers/User/UserController.php | 239 +
app/Http/Kernel.php | 92 +
app/Http/Middleware/Admin.php | 30 +
app/Http/Middleware/Authenticate.php | 21 +
app/Http/Middleware/CORS.php | 27 +
.../Middleware/CheckForMaintenanceMode.php | 17 +
app/Http/Middleware/Client.php | 34 +
app/Http/Middleware/EncryptCookies.php | 17 +
app/Http/Middleware/ForceJson.php | 22 +
app/Http/Middleware/Language.php | 17 +
.../Middleware/RedirectIfAuthenticated.php | 26 +
app/Http/Middleware/RequestLog.php | 24 +
app/Http/Middleware/Staff.php | 29 +
app/Http/Middleware/TrimStrings.php | 18 +
app/Http/Middleware/TrustProxies.php | 23 +
app/Http/Middleware/User.php | 30 +
app/Http/Middleware/VerifyCsrfToken.php | 24 +
app/Http/Requests/Admin/ConfigSave.php | 118 +
app/Http/Requests/Admin/CouponGenerate.php | 51 +
.../Requests/Admin/KnowledgeCategorySave.php | 29 +
.../Requests/Admin/KnowledgeCategorySort.php | 28 +
app/Http/Requests/Admin/KnowledgeSave.php | 33 +
app/Http/Requests/Admin/KnowledgeSort.php | 28 +
app/Http/Requests/Admin/MailSend.php | 34 +
app/Http/Requests/Admin/NoticeSave.php | 33 +
app/Http/Requests/Admin/OrderAssign.php | 34 +
app/Http/Requests/Admin/OrderFetch.php | 32 +
app/Http/Requests/Admin/OrderUpdate.php | 29 +
app/Http/Requests/Admin/PlanSave.php | 57 +
app/Http/Requests/Admin/PlanSort.php | 28 +
app/Http/Requests/Admin/PlanUpdate.php | 29 +
.../Requests/Admin/ServerShadowsocksSave.php | 52 +
.../Admin/ServerShadowsocksUpdate.php | 28 +
app/Http/Requests/Admin/ServerTrojanSave.php | 49 +
.../Requests/Admin/ServerTrojanUpdate.php | 28 +
app/Http/Requests/Admin/ServerVmessSave.php | 59 +
app/Http/Requests/Admin/ServerVmessUpdate.php | 28 +
app/Http/Requests/Admin/UserFetch.php | 33 +
app/Http/Requests/Admin/UserGenerate.php | 33 +
app/Http/Requests/Admin/UserSendMail.php | 29 +
app/Http/Requests/Admin/UserUpdate.php | 67 +
app/Http/Requests/Passport/AuthForget.php | 33 +
app/Http/Requests/Passport/AuthLogin.php | 31 +
app/Http/Requests/Passport/AuthRegister.php | 31 +
.../Requests/Passport/CommSendEmailVerify.php | 28 +
app/Http/Requests/Staff/UserUpdate.php | 56 +
app/Http/Requests/User/OrderSave.php | 30 +
app/Http/Requests/User/TicketSave.php | 32 +
app/Http/Requests/User/TicketWithdraw.php | 29 +
app/Http/Requests/User/UserChangePassword.php | 30 +
app/Http/Requests/User/UserTransfer.php | 29 +
app/Http/Requests/User/UserUpdate.php | 29 +
app/Http/Routes/AdminRoute.php | 144 +
app/Http/Routes/ClientRoute.php | 21 +
app/Http/Routes/GuestRoute.php | 23 +
app/Http/Routes/PassportRoute.php | 25 +
app/Http/Routes/ServerRoute.php | 19 +
app/Http/Routes/StaffRoute.php | 32 +
app/Http/Routes/UserRoute.php | 64 +
app/Jobs/OrderHandleJob.php | 54 +
app/Jobs/SendEmailJob.php | 75 +
app/Jobs/SendTelegramJob.php | 43 +
app/Jobs/TrafficFetchJob.php | 57 +
app/Logging/MysqlLogger.php | 11 +
app/Logging/MysqlLoggerHandler.php | 44 +
app/Models/CommissionLog.php | 16 +
app/Models/Coupon.php | 18 +
app/Models/InviteCode.php | 15 +
app/Models/Knowledge.php | 16 +
app/Models/Log.php | 16 +
app/Models/MailLog.php | 16 +
app/Models/Notice.php | 17 +
app/Models/Order.php | 17 +
app/Models/Payment.php | 17 +
app/Models/Plan.php | 16 +
app/Models/ServerGroup.php | 15 +
app/Models/ServerHysteria.php | 19 +
app/Models/ServerLog.php | 16 +
app/Models/ServerRoute.php | 16 +
app/Models/ServerShadowsocks.php | 20 +
app/Models/ServerStat.php | 16 +
app/Models/ServerTrojan.php | 19 +
app/Models/ServerVmess.php | 23 +
app/Models/Stat.php | 16 +
app/Models/StatServer.php | 16 +
app/Models/StatUser.php | 16 +
app/Models/Ticket.php | 16 +
app/Models/TicketMessage.php | 16 +
app/Models/User.php | 16 +
app/Payments/AlipayF2F.php | 93 +
app/Payments/BTCPay.php | 148 +
app/Payments/CoinPayments.php | 104 +
app/Payments/Coinbase.php | 129 +
app/Payments/EPay.php | 69 +
app/Payments/MGate.php | 102 +
app/Payments/StripeAlipay.php | 117 +
app/Payments/StripeCheckout.php | 139 +
app/Payments/StripeCredit.php | 124 +
app/Payments/StripeWepay.php | 117 +
app/Payments/WechatPayNative.php | 84 +
app/Plugins/Telegram/Commands/Bind.php | 38 +
.../Telegram/Commands/GetLatestUrl.php | 21 +
app/Plugins/Telegram/Commands/ReplyTicket.php | 37 +
app/Plugins/Telegram/Commands/Traffic.php | 28 +
app/Plugins/Telegram/Commands/UnBind.php | 26 +
app/Plugins/Telegram/Telegram.php | 15 +
app/Providers/AppServiceProvider.php | 28 +
app/Providers/AuthServiceProvider.php | 30 +
app/Providers/BroadcastServiceProvider.php | 21 +
app/Providers/EventServiceProvider.php | 29 +
app/Providers/HorizonServiceProvider.php | 43 +
app/Providers/RouteServiceProvider.php | 80 +
app/Services/AuthService.php | 104 +
app/Services/CouponService.php | 117 +
app/Services/MailService.php | 58 +
app/Services/OrderService.php | 321 ++
app/Services/PaymentService.php | 67 +
app/Services/PlanService.php | 41 +
app/Services/ServerService.php | 279 +
app/Services/StatisticalService.php | 283 +
app/Services/TelegramService.php | 85 +
app/Services/ThemeService.php | 49 +
app/Services/TicketService.php | 80 +
app/Services/UserService.php | 185 +
app/Utils/CacheKey.php | 38 +
app/Utils/Dict.php | 23 +
app/Utils/Helper.php | 123 +
artisan | 53 +
bootstrap/app.php | 55 +
bootstrap/cache/.gitignore | 2 +
composer.json | 80 +
config/app.php | 241 +
config/auth.php | 103 +
config/broadcasting.php | 59 +
config/cache.php | 103 +
config/cors.php | 34 +
config/database.php | 147 +
config/filesystems.php | 69 +
config/hashing.php | 52 +
config/horizon.php | 190 +
config/logging.php | 99 +
config/mail.php | 136 +
config/queue.php | 88 +
config/services.php | 33 +
config/session.php | 199 +
config/theme/.gitignore | 2 +
config/view.php | 36 +
database/.gitignore | 2 +
database/factories/UserFactory.php | 28 +
database/install.sql | 447 ++
..._08_19_000000_create_failed_jobs_table.php | 35 +
database/seeds/DatabaseSeeder.php | 16 +
database/update.sql | 686 +++
init.sh | 10 +
library/AlipayF2F.php | 164 +
phpunit.xml | 41 +
pm2.yaml | 5 +
public/assets/admin/components.async.js | 1 +
public/assets/admin/components.chunk.css | 29 +
public/assets/admin/env.example.js | 17 +
.../static/Simple-Line-Icons.0cb0b9c5.woff2 | Bin 0 -> 30064 bytes
.../static/Simple-Line-Icons.78f07e2c.woff | Bin 0 -> 81332 bytes
.../static/Simple-Line-Icons.d2285965.ttf | Bin 0 -> 54056 bytes
.../static/Simple-Line-Icons.ed67e5a3.svg | 391 ++
.../static/Simple-Line-Icons.f33df365.eot | Bin 0 -> 54266 bytes
.../admin/static/fa-brands-400.14c590d1.eot | Bin 0 -> 129590 bytes
.../admin/static/fa-brands-400.3e1b2a65.woff2 | Bin 0 -> 74524 bytes
.../admin/static/fa-brands-400.5e8aa9ea.ttf | Bin 0 -> 129284 bytes
.../admin/static/fa-brands-400.91fd86e5.svg | 3450 ++++++++++++
.../admin/static/fa-brands-400.df02c782.woff | Bin 0 -> 87520 bytes
.../admin/static/fa-regular-400.285a9d2a.ttf | Bin 0 -> 34096 bytes
.../admin/static/fa-regular-400.5623624d.woff | Bin 0 -> 16804 bytes
.../admin/static/fa-regular-400.6b5ed912.svg | 804 +++
.../admin/static/fa-regular-400.aa66d0e0.eot | Bin 0 -> 34394 bytes
.../static/fa-regular-400.ac21cac3.woff2 | Bin 0 -> 13584 bytes
.../admin/static/fa-solid-900.3ded831d.woff | Bin 0 -> 98016 bytes
.../admin/static/fa-solid-900.42e1fbd2.eot | Bin 0 -> 192122 bytes
.../admin/static/fa-solid-900.649208f1.svg | 4650 +++++++++++++++++
.../admin/static/fa-solid-900.896e20e2.ttf | Bin 0 -> 191836 bytes
.../admin/static/fa-solid-900.d6d8d5da.woff2 | Bin 0 -> 75408 bytes
public/assets/admin/theme/black.css | 4 +
public/assets/admin/theme/darkblue.css | 4 +
public/assets/admin/theme/default.css | 2 +
public/assets/admin/theme/green.css | 4 +
public/assets/admin/umi.css | 3 +
public/assets/admin/umi.js | 1 +
public/assets/admin/vendors.async.js | 1 +
public/index.php | 60 +
public/robots.txt | 2 +
public/theme/.gitignore | 3 +
.../theme/v2board/assets/components.async.js | 1 +
.../theme/v2board/assets/components.chunk.css | 25 +
public/theme/v2board/assets/env.example.js | 27 +
public/theme/v2board/assets/i18n/en-US.js | 277 +
public/theme/v2board/assets/i18n/fa-IR.js | 277 +
public/theme/v2board/assets/i18n/ja-JP.js | 277 +
public/theme/v2board/assets/i18n/ko-KR.js | 277 +
public/theme/v2board/assets/i18n/vi-VN.js | 277 +
public/theme/v2board/assets/i18n/zh-CN.js | 277 +
public/theme/v2board/assets/i18n/zh-TW.js | 277 +
.../assets/images/icon/Clash For Android.png | Bin 0 -> 1533 bytes
.../assets/images/icon/Clash For Windows.png | Bin 0 -> 1533 bytes
.../v2board/assets/images/icon/ClashX.png | Bin 0 -> 3417 bytes
.../assets/images/icon/QuantumultX.png | Bin 0 -> 3063 bytes
.../assets/images/icon/Shadowrocket.png | Bin 0 -> 2776 bytes
.../v2board/assets/images/icon/Stash.png | Bin 0 -> 5060 bytes
.../v2board/assets/images/icon/Surfboard.png | Bin 0 -> 992 bytes
.../v2board/assets/images/icon/Surge.png | Bin 0 -> 2424 bytes
.../static/Simple-Line-Icons.0cb0b9c5.woff2 | Bin 0 -> 30064 bytes
.../static/Simple-Line-Icons.78f07e2c.woff | Bin 0 -> 81332 bytes
.../static/Simple-Line-Icons.d2285965.ttf | Bin 0 -> 54056 bytes
.../static/Simple-Line-Icons.ed67e5a3.svg | 391 ++
.../static/Simple-Line-Icons.f33df365.eot | Bin 0 -> 54266 bytes
.../assets/static/fa-brands-400.14c590d1.eot | Bin 0 -> 129590 bytes
.../static/fa-brands-400.3e1b2a65.woff2 | Bin 0 -> 74524 bytes
.../assets/static/fa-brands-400.5e8aa9ea.ttf | Bin 0 -> 129284 bytes
.../assets/static/fa-brands-400.91fd86e5.svg | 3450 ++++++++++++
.../assets/static/fa-brands-400.df02c782.woff | Bin 0 -> 87520 bytes
.../assets/static/fa-regular-400.285a9d2a.ttf | Bin 0 -> 34096 bytes
.../static/fa-regular-400.5623624d.woff | Bin 0 -> 16804 bytes
.../assets/static/fa-regular-400.6b5ed912.svg | 804 +++
.../assets/static/fa-regular-400.aa66d0e0.eot | Bin 0 -> 34394 bytes
.../static/fa-regular-400.ac21cac3.woff2 | Bin 0 -> 13584 bytes
.../assets/static/fa-solid-900.3ded831d.woff | Bin 0 -> 98016 bytes
.../assets/static/fa-solid-900.42e1fbd2.eot | Bin 0 -> 192122 bytes
.../assets/static/fa-solid-900.649208f1.svg | 4650 +++++++++++++++++
.../assets/static/fa-solid-900.896e20e2.ttf | Bin 0 -> 191836 bytes
.../assets/static/fa-solid-900.d6d8d5da.woff2 | Bin 0 -> 75408 bytes
public/theme/v2board/assets/theme/black.css | 4 +
.../theme/v2board/assets/theme/darkblue.css | 4 +
public/theme/v2board/assets/theme/default.css | 2 +
public/theme/v2board/assets/theme/green.css | 4 +
public/theme/v2board/assets/umi.css | 6 +
public/theme/v2board/assets/umi.js | 1 +
public/theme/v2board/assets/vendors.async.js | 1 +
public/theme/v2board/config.json | 49 +
public/theme/v2board/dashboard.blade.php | 67 +
public/web.config | 28 +
resources/js/app.js | 1 +
resources/js/bootstrap.js | 28 +
resources/lang/en-US.json | 98 +
resources/lang/zh-CN.json | 98 +
resources/rules/.gitignore | 1 +
resources/rules/default.clash.yaml | 39 +
resources/rules/default.surfboard.conf | 576 ++
resources/rules/default.surge.conf | 595 +++
resources/rules/vpn.clash.yaml | 36 +
resources/sass/app.scss | 1 +
resources/views/admin.blade.php | 36 +
resources/views/errors/500.blade.php | 5 +
.../views/mail/classic/mailLogin.blade.php | 195 +
resources/views/mail/classic/notify.blade.php | 187 +
.../views/mail/classic/remindExpire.blade.php | 187 +
.../mail/classic/remindTraffic.blade.php | 187 +
resources/views/mail/classic/verify.blade.php | 195 +
.../views/mail/default/mailLogin.blade.php | 43 +
resources/views/mail/default/notify.blade.php | 42 +
.../views/mail/default/remindExpire.blade.php | 42 +
.../mail/default/remindTraffic.blade.php | 42 +
resources/views/mail/default/verify.blade.php | 42 +
routes/channels.php | 16 +
routes/console.php | 18 +
routes/web.php | 52 +
server.php | 21 +
storage/framework/cache/.gitignore | 2 +
storage/framework/sessions/.gitignore | 2 +
storage/framework/views/.gitignore | 2 +
storage/logs/.gitignore | 2 +
storage/views/.gitignore | 2 +
tests/Bootstrap.php | 42 +
tests/CreatesApplication.php | 22 +
tests/Feature/ExampleTest.php | 21 +
tests/TestCase.php | 10 +
tests/Unit/ExampleTest.php | 19 +
346 files changed, 40734 insertions(+), 24 deletions(-)
create mode 100755 .env.example
create mode 100755 .gitattributes
create mode 100644 app/Console/Commands/CheckCommission.php
create mode 100755 app/Console/Commands/CheckOrder.php
create mode 100644 app/Console/Commands/CheckServer.php
create mode 100644 app/Console/Commands/CheckTicket.php
create mode 100644 app/Console/Commands/ClearUser.php
create mode 100644 app/Console/Commands/ResetLog.php
create mode 100644 app/Console/Commands/ResetPassword.php
create mode 100644 app/Console/Commands/ResetTraffic.php
create mode 100644 app/Console/Commands/ResetUser.php
create mode 100644 app/Console/Commands/SendRemindMail.php
create mode 100644 app/Console/Commands/Test.php
create mode 100644 app/Console/Commands/V2boardInstall.php
create mode 100644 app/Console/Commands/V2boardStatistics.php
create mode 100644 app/Console/Commands/V2boardUpdate.php
create mode 100644 app/Console/Kernel.php
create mode 100755 app/Exceptions/Handler.php
create mode 100755 app/Http/Controllers/Admin/ConfigController.php
create mode 100644 app/Http/Controllers/Admin/CouponController.php
create mode 100644 app/Http/Controllers/Admin/KnowledgeController.php
create mode 100644 app/Http/Controllers/Admin/NoticeController.php
create mode 100644 app/Http/Controllers/Admin/OrderController.php
create mode 100644 app/Http/Controllers/Admin/PaymentController.php
create mode 100755 app/Http/Controllers/Admin/PlanController.php
create mode 100644 app/Http/Controllers/Admin/Server/GroupController.php
create mode 100644 app/Http/Controllers/Admin/Server/HysteriaController.php
create mode 100644 app/Http/Controllers/Admin/Server/ManageController.php
create mode 100644 app/Http/Controllers/Admin/Server/RouteController.php
create mode 100644 app/Http/Controllers/Admin/Server/ShadowsocksController.php
create mode 100644 app/Http/Controllers/Admin/Server/TrojanController.php
create mode 100644 app/Http/Controllers/Admin/Server/VmessController.php
create mode 100644 app/Http/Controllers/Admin/StatController.php
create mode 100644 app/Http/Controllers/Admin/SystemController.php
create mode 100644 app/Http/Controllers/Admin/ThemeController.php
create mode 100644 app/Http/Controllers/Admin/TicketController.php
create mode 100644 app/Http/Controllers/Admin/UserController.php
create mode 100644 app/Http/Controllers/Client/ClientController.php
create mode 100644 app/Http/Controllers/Client/Protocols/Clash.php
create mode 100644 app/Http/Controllers/Client/Protocols/GCLH.php
create mode 100644 app/Http/Controllers/Client/Protocols/General.php
create mode 100644 app/Http/Controllers/Client/Protocols/QuantumultX.php
create mode 100644 app/Http/Controllers/Client/Protocols/Shadowsocks.php
create mode 100644 app/Http/Controllers/Client/Protocols/Stash.php
create mode 100644 app/Http/Controllers/Client/Protocols/Surfboard.php
create mode 100644 app/Http/Controllers/Client/Protocols/Surge.php
create mode 100755 app/Http/Controllers/Controller.php
create mode 100644 app/Http/Controllers/Guest/CommController.php
create mode 100644 app/Http/Controllers/Guest/PaymentController.php
create mode 100755 app/Http/Controllers/Guest/PlanController.php
create mode 100644 app/Http/Controllers/Guest/TelegramController.php
create mode 100644 app/Http/Controllers/Passport/AuthController.php
create mode 100644 app/Http/Controllers/Passport/CommController.php
create mode 100644 app/Http/Controllers/Server/UniProxyController.php
create mode 100644 app/Http/Controllers/Staff/NoticeController.php
create mode 100755 app/Http/Controllers/Staff/PlanController.php
create mode 100644 app/Http/Controllers/Staff/TicketController.php
create mode 100644 app/Http/Controllers/Staff/UserController.php
create mode 100644 app/Http/Controllers/User/CommController.php
create mode 100644 app/Http/Controllers/User/CouponController.php
create mode 100644 app/Http/Controllers/User/InviteController.php
create mode 100644 app/Http/Controllers/User/KnowledgeController.php
create mode 100644 app/Http/Controllers/User/NoticeController.php
create mode 100755 app/Http/Controllers/User/OrderController.php
create mode 100755 app/Http/Controllers/User/PlanController.php
create mode 100644 app/Http/Controllers/User/ServerController.php
create mode 100644 app/Http/Controllers/User/StatController.php
create mode 100644 app/Http/Controllers/User/TelegramController.php
create mode 100644 app/Http/Controllers/User/TicketController.php
create mode 100755 app/Http/Controllers/User/UserController.php
create mode 100755 app/Http/Kernel.php
create mode 100755 app/Http/Middleware/Admin.php
create mode 100755 app/Http/Middleware/Authenticate.php
create mode 100755 app/Http/Middleware/CORS.php
create mode 100755 app/Http/Middleware/CheckForMaintenanceMode.php
create mode 100755 app/Http/Middleware/Client.php
create mode 100755 app/Http/Middleware/EncryptCookies.php
create mode 100755 app/Http/Middleware/ForceJson.php
create mode 100755 app/Http/Middleware/Language.php
create mode 100755 app/Http/Middleware/RedirectIfAuthenticated.php
create mode 100755 app/Http/Middleware/RequestLog.php
create mode 100644 app/Http/Middleware/Staff.php
create mode 100755 app/Http/Middleware/TrimStrings.php
create mode 100755 app/Http/Middleware/TrustProxies.php
create mode 100755 app/Http/Middleware/User.php
create mode 100755 app/Http/Middleware/VerifyCsrfToken.php
create mode 100755 app/Http/Requests/Admin/ConfigSave.php
create mode 100644 app/Http/Requests/Admin/CouponGenerate.php
create mode 100644 app/Http/Requests/Admin/KnowledgeCategorySave.php
create mode 100644 app/Http/Requests/Admin/KnowledgeCategorySort.php
create mode 100644 app/Http/Requests/Admin/KnowledgeSave.php
create mode 100644 app/Http/Requests/Admin/KnowledgeSort.php
create mode 100644 app/Http/Requests/Admin/MailSend.php
create mode 100644 app/Http/Requests/Admin/NoticeSave.php
create mode 100644 app/Http/Requests/Admin/OrderAssign.php
create mode 100644 app/Http/Requests/Admin/OrderFetch.php
create mode 100644 app/Http/Requests/Admin/OrderUpdate.php
create mode 100755 app/Http/Requests/Admin/PlanSave.php
create mode 100644 app/Http/Requests/Admin/PlanSort.php
create mode 100644 app/Http/Requests/Admin/PlanUpdate.php
create mode 100644 app/Http/Requests/Admin/ServerShadowsocksSave.php
create mode 100755 app/Http/Requests/Admin/ServerShadowsocksUpdate.php
create mode 100644 app/Http/Requests/Admin/ServerTrojanSave.php
create mode 100755 app/Http/Requests/Admin/ServerTrojanUpdate.php
create mode 100755 app/Http/Requests/Admin/ServerVmessSave.php
create mode 100755 app/Http/Requests/Admin/ServerVmessUpdate.php
create mode 100644 app/Http/Requests/Admin/UserFetch.php
create mode 100644 app/Http/Requests/Admin/UserGenerate.php
create mode 100644 app/Http/Requests/Admin/UserSendMail.php
create mode 100644 app/Http/Requests/Admin/UserUpdate.php
create mode 100644 app/Http/Requests/Passport/AuthForget.php
create mode 100644 app/Http/Requests/Passport/AuthLogin.php
create mode 100755 app/Http/Requests/Passport/AuthRegister.php
create mode 100644 app/Http/Requests/Passport/CommSendEmailVerify.php
create mode 100644 app/Http/Requests/Staff/UserUpdate.php
create mode 100755 app/Http/Requests/User/OrderSave.php
create mode 100644 app/Http/Requests/User/TicketSave.php
create mode 100644 app/Http/Requests/User/TicketWithdraw.php
create mode 100644 app/Http/Requests/User/UserChangePassword.php
create mode 100644 app/Http/Requests/User/UserTransfer.php
create mode 100644 app/Http/Requests/User/UserUpdate.php
create mode 100644 app/Http/Routes/AdminRoute.php
create mode 100644 app/Http/Routes/ClientRoute.php
create mode 100644 app/Http/Routes/GuestRoute.php
create mode 100644 app/Http/Routes/PassportRoute.php
create mode 100644 app/Http/Routes/ServerRoute.php
create mode 100644 app/Http/Routes/StaffRoute.php
create mode 100644 app/Http/Routes/UserRoute.php
create mode 100644 app/Jobs/OrderHandleJob.php
create mode 100644 app/Jobs/SendEmailJob.php
create mode 100644 app/Jobs/SendTelegramJob.php
create mode 100644 app/Jobs/TrafficFetchJob.php
create mode 100644 app/Logging/MysqlLogger.php
create mode 100644 app/Logging/MysqlLoggerHandler.php
create mode 100644 app/Models/CommissionLog.php
create mode 100644 app/Models/Coupon.php
create mode 100644 app/Models/InviteCode.php
create mode 100644 app/Models/Knowledge.php
create mode 100644 app/Models/Log.php
create mode 100644 app/Models/MailLog.php
create mode 100644 app/Models/Notice.php
create mode 100755 app/Models/Order.php
create mode 100644 app/Models/Payment.php
create mode 100755 app/Models/Plan.php
create mode 100755 app/Models/ServerGroup.php
create mode 100755 app/Models/ServerHysteria.php
create mode 100644 app/Models/ServerLog.php
create mode 100755 app/Models/ServerRoute.php
create mode 100644 app/Models/ServerShadowsocks.php
create mode 100644 app/Models/ServerStat.php
create mode 100644 app/Models/ServerTrojan.php
create mode 100755 app/Models/ServerVmess.php
create mode 100644 app/Models/Stat.php
create mode 100644 app/Models/StatServer.php
create mode 100644 app/Models/StatUser.php
create mode 100644 app/Models/Ticket.php
create mode 100644 app/Models/TicketMessage.php
create mode 100755 app/Models/User.php
create mode 100644 app/Payments/AlipayF2F.php
create mode 100644 app/Payments/BTCPay.php
create mode 100644 app/Payments/CoinPayments.php
create mode 100644 app/Payments/Coinbase.php
create mode 100644 app/Payments/EPay.php
create mode 100644 app/Payments/MGate.php
create mode 100644 app/Payments/StripeAlipay.php
create mode 100644 app/Payments/StripeCheckout.php
create mode 100644 app/Payments/StripeCredit.php
create mode 100644 app/Payments/StripeWepay.php
create mode 100644 app/Payments/WechatPayNative.php
create mode 100644 app/Plugins/Telegram/Commands/Bind.php
create mode 100644 app/Plugins/Telegram/Commands/GetLatestUrl.php
create mode 100644 app/Plugins/Telegram/Commands/ReplyTicket.php
create mode 100644 app/Plugins/Telegram/Commands/Traffic.php
create mode 100644 app/Plugins/Telegram/Commands/UnBind.php
create mode 100644 app/Plugins/Telegram/Telegram.php
create mode 100755 app/Providers/AppServiceProvider.php
create mode 100755 app/Providers/AuthServiceProvider.php
create mode 100755 app/Providers/BroadcastServiceProvider.php
create mode 100755 app/Providers/EventServiceProvider.php
create mode 100644 app/Providers/HorizonServiceProvider.php
create mode 100755 app/Providers/RouteServiceProvider.php
create mode 100644 app/Services/AuthService.php
create mode 100644 app/Services/CouponService.php
create mode 100644 app/Services/MailService.php
create mode 100644 app/Services/OrderService.php
create mode 100644 app/Services/PaymentService.php
create mode 100644 app/Services/PlanService.php
create mode 100644 app/Services/ServerService.php
create mode 100644 app/Services/StatisticalService.php
create mode 100644 app/Services/TelegramService.php
create mode 100644 app/Services/ThemeService.php
create mode 100644 app/Services/TicketService.php
create mode 100644 app/Services/UserService.php
create mode 100644 app/Utils/CacheKey.php
create mode 100644 app/Utils/Dict.php
create mode 100644 app/Utils/Helper.php
create mode 100755 artisan
create mode 100755 bootstrap/app.php
create mode 100755 bootstrap/cache/.gitignore
create mode 100755 composer.json
create mode 100755 config/app.php
create mode 100755 config/auth.php
create mode 100755 config/broadcasting.php
create mode 100755 config/cache.php
create mode 100644 config/cors.php
create mode 100755 config/database.php
create mode 100755 config/filesystems.php
create mode 100755 config/hashing.php
create mode 100644 config/horizon.php
create mode 100755 config/logging.php
create mode 100755 config/mail.php
create mode 100755 config/queue.php
create mode 100755 config/services.php
create mode 100755 config/session.php
create mode 100644 config/theme/.gitignore
create mode 100755 config/view.php
create mode 100644 database/.gitignore
create mode 100644 database/factories/UserFactory.php
create mode 100644 database/install.sql
create mode 100644 database/migrations/2019_08_19_000000_create_failed_jobs_table.php
create mode 100644 database/seeds/DatabaseSeeder.php
create mode 100644 database/update.sql
create mode 100644 init.sh
create mode 100644 library/AlipayF2F.php
create mode 100755 phpunit.xml
create mode 100644 pm2.yaml
create mode 100644 public/assets/admin/components.async.js
create mode 100644 public/assets/admin/components.chunk.css
create mode 100644 public/assets/admin/env.example.js
create mode 100644 public/assets/admin/static/Simple-Line-Icons.0cb0b9c5.woff2
create mode 100644 public/assets/admin/static/Simple-Line-Icons.78f07e2c.woff
create mode 100644 public/assets/admin/static/Simple-Line-Icons.d2285965.ttf
create mode 100644 public/assets/admin/static/Simple-Line-Icons.ed67e5a3.svg
create mode 100644 public/assets/admin/static/Simple-Line-Icons.f33df365.eot
create mode 100644 public/assets/admin/static/fa-brands-400.14c590d1.eot
create mode 100644 public/assets/admin/static/fa-brands-400.3e1b2a65.woff2
create mode 100644 public/assets/admin/static/fa-brands-400.5e8aa9ea.ttf
create mode 100644 public/assets/admin/static/fa-brands-400.91fd86e5.svg
create mode 100644 public/assets/admin/static/fa-brands-400.df02c782.woff
create mode 100644 public/assets/admin/static/fa-regular-400.285a9d2a.ttf
create mode 100644 public/assets/admin/static/fa-regular-400.5623624d.woff
create mode 100644 public/assets/admin/static/fa-regular-400.6b5ed912.svg
create mode 100644 public/assets/admin/static/fa-regular-400.aa66d0e0.eot
create mode 100644 public/assets/admin/static/fa-regular-400.ac21cac3.woff2
create mode 100644 public/assets/admin/static/fa-solid-900.3ded831d.woff
create mode 100644 public/assets/admin/static/fa-solid-900.42e1fbd2.eot
create mode 100644 public/assets/admin/static/fa-solid-900.649208f1.svg
create mode 100644 public/assets/admin/static/fa-solid-900.896e20e2.ttf
create mode 100644 public/assets/admin/static/fa-solid-900.d6d8d5da.woff2
create mode 100644 public/assets/admin/theme/black.css
create mode 100644 public/assets/admin/theme/darkblue.css
create mode 100644 public/assets/admin/theme/default.css
create mode 100644 public/assets/admin/theme/green.css
create mode 100644 public/assets/admin/umi.css
create mode 100644 public/assets/admin/umi.js
create mode 100644 public/assets/admin/vendors.async.js
create mode 100755 public/index.php
create mode 100755 public/robots.txt
create mode 100644 public/theme/.gitignore
create mode 100644 public/theme/v2board/assets/components.async.js
create mode 100644 public/theme/v2board/assets/components.chunk.css
create mode 100644 public/theme/v2board/assets/env.example.js
create mode 100644 public/theme/v2board/assets/i18n/en-US.js
create mode 100644 public/theme/v2board/assets/i18n/fa-IR.js
create mode 100644 public/theme/v2board/assets/i18n/ja-JP.js
create mode 100644 public/theme/v2board/assets/i18n/ko-KR.js
create mode 100644 public/theme/v2board/assets/i18n/vi-VN.js
create mode 100644 public/theme/v2board/assets/i18n/zh-CN.js
create mode 100644 public/theme/v2board/assets/i18n/zh-TW.js
create mode 100644 public/theme/v2board/assets/images/icon/Clash For Android.png
create mode 100644 public/theme/v2board/assets/images/icon/Clash For Windows.png
create mode 100644 public/theme/v2board/assets/images/icon/ClashX.png
create mode 100644 public/theme/v2board/assets/images/icon/QuantumultX.png
create mode 100644 public/theme/v2board/assets/images/icon/Shadowrocket.png
create mode 100644 public/theme/v2board/assets/images/icon/Stash.png
create mode 100644 public/theme/v2board/assets/images/icon/Surfboard.png
create mode 100644 public/theme/v2board/assets/images/icon/Surge.png
create mode 100644 public/theme/v2board/assets/static/Simple-Line-Icons.0cb0b9c5.woff2
create mode 100644 public/theme/v2board/assets/static/Simple-Line-Icons.78f07e2c.woff
create mode 100644 public/theme/v2board/assets/static/Simple-Line-Icons.d2285965.ttf
create mode 100644 public/theme/v2board/assets/static/Simple-Line-Icons.ed67e5a3.svg
create mode 100644 public/theme/v2board/assets/static/Simple-Line-Icons.f33df365.eot
create mode 100644 public/theme/v2board/assets/static/fa-brands-400.14c590d1.eot
create mode 100644 public/theme/v2board/assets/static/fa-brands-400.3e1b2a65.woff2
create mode 100644 public/theme/v2board/assets/static/fa-brands-400.5e8aa9ea.ttf
create mode 100644 public/theme/v2board/assets/static/fa-brands-400.91fd86e5.svg
create mode 100644 public/theme/v2board/assets/static/fa-brands-400.df02c782.woff
create mode 100644 public/theme/v2board/assets/static/fa-regular-400.285a9d2a.ttf
create mode 100644 public/theme/v2board/assets/static/fa-regular-400.5623624d.woff
create mode 100644 public/theme/v2board/assets/static/fa-regular-400.6b5ed912.svg
create mode 100644 public/theme/v2board/assets/static/fa-regular-400.aa66d0e0.eot
create mode 100644 public/theme/v2board/assets/static/fa-regular-400.ac21cac3.woff2
create mode 100644 public/theme/v2board/assets/static/fa-solid-900.3ded831d.woff
create mode 100644 public/theme/v2board/assets/static/fa-solid-900.42e1fbd2.eot
create mode 100644 public/theme/v2board/assets/static/fa-solid-900.649208f1.svg
create mode 100644 public/theme/v2board/assets/static/fa-solid-900.896e20e2.ttf
create mode 100644 public/theme/v2board/assets/static/fa-solid-900.d6d8d5da.woff2
create mode 100644 public/theme/v2board/assets/theme/black.css
create mode 100644 public/theme/v2board/assets/theme/darkblue.css
create mode 100644 public/theme/v2board/assets/theme/default.css
create mode 100644 public/theme/v2board/assets/theme/green.css
create mode 100644 public/theme/v2board/assets/umi.css
create mode 100644 public/theme/v2board/assets/umi.js
create mode 100644 public/theme/v2board/assets/vendors.async.js
create mode 100644 public/theme/v2board/config.json
create mode 100755 public/theme/v2board/dashboard.blade.php
create mode 100755 public/web.config
create mode 100755 resources/js/app.js
create mode 100755 resources/js/bootstrap.js
create mode 100644 resources/lang/en-US.json
create mode 100644 resources/lang/zh-CN.json
create mode 100644 resources/rules/.gitignore
create mode 100644 resources/rules/default.clash.yaml
create mode 100644 resources/rules/default.surfboard.conf
create mode 100644 resources/rules/default.surge.conf
create mode 100644 resources/rules/vpn.clash.yaml
create mode 100755 resources/sass/app.scss
create mode 100644 resources/views/admin.blade.php
create mode 100644 resources/views/errors/500.blade.php
create mode 100644 resources/views/mail/classic/mailLogin.blade.php
create mode 100644 resources/views/mail/classic/notify.blade.php
create mode 100644 resources/views/mail/classic/remindExpire.blade.php
create mode 100644 resources/views/mail/classic/remindTraffic.blade.php
create mode 100644 resources/views/mail/classic/verify.blade.php
create mode 100644 resources/views/mail/default/mailLogin.blade.php
create mode 100644 resources/views/mail/default/notify.blade.php
create mode 100644 resources/views/mail/default/remindExpire.blade.php
create mode 100644 resources/views/mail/default/remindTraffic.blade.php
create mode 100644 resources/views/mail/default/verify.blade.php
create mode 100755 routes/channels.php
create mode 100755 routes/console.php
create mode 100755 routes/web.php
create mode 100755 server.php
create mode 100755 storage/framework/cache/.gitignore
create mode 100755 storage/framework/sessions/.gitignore
create mode 100755 storage/framework/views/.gitignore
create mode 100755 storage/logs/.gitignore
create mode 100755 storage/views/.gitignore
create mode 100755 tests/Bootstrap.php
create mode 100755 tests/CreatesApplication.php
create mode 100755 tests/Feature/ExampleTest.php
create mode 100755 tests/TestCase.php
create mode 100755 tests/Unit/ExampleTest.php
diff --git a/.env.example b/.env.example
new file mode 100755
index 0000000..4f0fede
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,48 @@
+APP_NAME=V2Board
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=false
+APP_URL=http://localhost
+
+LOG_CHANNEL=stack
+
+DB_CONNECTION=mysql
+DB_HOST=localhost
+DB_PORT=3306
+DB_DATABASE=laravel
+DB_USERNAME=root
+DB_PASSWORD=123456
+
+BROADCAST_DRIVER=log
+CACHE_DRIVER=file
+QUEUE_CONNECTION=redis
+SESSION_DRIVER=redis
+SESSION_LIFETIME=120
+
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_DRIVER=smtp
+MAIL_HOST=smtp.mailtrap.io
+MAIL_PORT=2525
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_ENCRYPTION=null
+MAIL_FROM_ADDRESS=null
+MAIL_FROM_NAME=null
+MAILGUN_DOMAIN=
+MAILGUN_SECRET=
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+
+PUSHER_APP_ID=
+PUSHER_APP_KEY=
+PUSHER_APP_SECRET=
+PUSHER_APP_CLUSTER=mt1
+
+MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
+MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
diff --git a/.gitattributes b/.gitattributes
new file mode 100755
index 0000000..967315d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+* text=auto
+*.css linguist-vendored
+*.scss linguist-vendored
+*.js linguist-vendored
+CHANGELOG.md export-ignore
diff --git a/.gitignore b/.gitignore
index 297959a..98b07c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,21 @@
-/vendor/
-node_modules/
-npm-debug.log
-yarn-error.log
-
-# Laravel 4 specific
-bootstrap/compiled.php
-app/storage/
-
-# Laravel 5 & Lumen specific
-public/storage
-public/hot
-
-# Laravel 5 & Lumen specific with changed public path
-public_html/storage
-public_html/hot
-
-storage/*.key
+/node_modules
+/config/v2board.php
+/public/hot
+/public/storage
+/public/env.example.js
+/storage/*.key
+/vendor
.env
-Homestead.yaml
-Homestead.json
-/.vagrant
+.env.backup
.phpunit.result.cache
+.idea
+.lock
+Homestead.json
+Homestead.yaml
+npm-debug.log
+yarn-error.log
+composer.phar
+composer.lock
+yarn.lock
+docker-compose.yml
+.DS_Store
diff --git a/LICENSE b/LICENSE
index 71e76fb..b89b3f6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 lotusnetwork
+Copyright (c) 2019 Tokumeikoi and LotusNetwork
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index e768c2f..f0b6390 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,29 @@
-# lotusboard
-NeoBranch Only! | Enhanced V2board
+# Lotusboard
+
+The enhanced v2board
+
+### UserManual | 用戶手冊
+
+[Documents](https://lotusnetwork.github.io)
+
+Hysteria
+ - Multiple bugs fixed
+
+VLESS
+ - Add vless support
+ - Multi GUN mode on grpc
+ - other stuffs that Vmess has
+ - XTLS supported
+
+Vmess
+ - TLS fingerprint, firefox by default
+ - Websocket ed4096(0rtt enabled for xray)
+ - Subscription info was translated into English
+ - Auto zero encryption when TLS enabled
+
+
+Subscription:
+
+ - ClashVPN mode profile (Proxy all traffic except local and icmp), add &flag=gclh to fetch it
+
+ - Simplified the default clash config
diff --git a/app/Console/Commands/CheckCommission.php b/app/Console/Commands/CheckCommission.php
new file mode 100644
index 0000000..d956363
--- /dev/null
+++ b/app/Console/Commands/CheckCommission.php
@@ -0,0 +1,127 @@
+autoCheck();
+ $this->autoPayCommission();
+ }
+
+ public function autoCheck()
+ {
+ if ((int)config('v2board.commission_auto_check_enable', 1)) {
+ Order::where('commission_status', 0)
+ ->where('invite_user_id', '!=', NULL)
+ ->where('status', 3)
+ ->where('updated_at', '<=', strtotime('-3 day', time()))
+ ->update([
+ 'commission_status' => 1
+ ]);
+ }
+ }
+
+ public function autoPayCommission()
+ {
+ $orders = Order::where('commission_status', 1)
+ ->where('invite_user_id', '!=', NULL)
+ ->get();
+ foreach ($orders as $order) {
+ DB::beginTransaction();
+ if (!$this->payHandle($order->invite_user_id, $order)) {
+ DB::rollBack();
+ continue;
+ }
+ $order->commission_status = 2;
+ if (!$order->save()) {
+ DB::rollBack();
+ continue;
+ }
+ DB::commit();
+ }
+ }
+
+ public function payHandle($inviteUserId, Order $order)
+ {
+ $level = 3;
+ if ((int)config('v2board.commission_distribution_enable', 0)) {
+ $commissionShareLevels = [
+ 0 => (int)config('v2board.commission_distribution_l1'),
+ 1 => (int)config('v2board.commission_distribution_l2'),
+ 2 => (int)config('v2board.commission_distribution_l3')
+ ];
+ } else {
+ $commissionShareLevels = [
+ 0 => 100
+ ];
+ }
+ for ($l = 0; $l < $level; $l++) {
+ $inviter = User::find($inviteUserId);
+ if (!$inviter) continue;
+ if (!isset($commissionShareLevels[$l])) continue;
+ $commissionBalance = $order->commission_balance * ($commissionShareLevels[$l] / 100);
+ if (!$commissionBalance) continue;
+ if ((int)config('v2board.withdraw_close_enable', 0)) {
+ $inviter->balance = $inviter->balance + $commissionBalance;
+ } else {
+ $inviter->commission_balance = $inviter->commission_balance + $commissionBalance;
+ }
+ if (!$inviter->save()) {
+ DB::rollBack();
+ return false;
+ }
+ if (!CommissionLog::create([
+ 'invite_user_id' => $inviteUserId,
+ 'user_id' => $order->user_id,
+ 'trade_no' => $order->trade_no,
+ 'order_amount' => $order->total_amount,
+ 'get_amount' => $commissionBalance
+ ])) {
+ DB::rollBack();
+ return false;
+ }
+ $inviteUserId = $inviter->invite_user_id;
+ // update order actual commission balance
+ $order->actual_commission_balance = $order->actual_commission_balance + $commissionBalance;
+ }
+ return true;
+ }
+
+}
diff --git a/app/Console/Commands/CheckOrder.php b/app/Console/Commands/CheckOrder.php
new file mode 100755
index 0000000..fabdfe8
--- /dev/null
+++ b/app/Console/Commands/CheckOrder.php
@@ -0,0 +1,54 @@
+orderBy('created_at', 'ASC')
+ ->get();
+ foreach ($orders as $order) {
+ OrderHandleJob::dispatch($order->trade_no);
+ }
+ }
+}
diff --git a/app/Console/Commands/CheckServer.php b/app/Console/Commands/CheckServer.php
new file mode 100644
index 0000000..3e94ea7
--- /dev/null
+++ b/app/Console/Commands/CheckServer.php
@@ -0,0 +1,65 @@
+checkOffline();
+ }
+
+ private function checkOffline()
+ {
+ $serverService = new ServerService();
+ $servers = $serverService->getAllServers();
+ foreach ($servers as $server) {
+ if ($server['parent_id']) continue;
+ if ($server['last_check_at'] && (time() - $server['last_check_at']) > 1800) {
+ $telegramService = new TelegramService();
+ $message = sprintf(
+ "节点掉线通知\r\n----\r\n节点名称:%s\r\n节点地址:%s\r\n",
+ $server['name'],
+ $server['host']
+ );
+ $telegramService->sendMessageWithAdmin($message);
+ Cache::forget(CacheKey::get(sprintf("SERVER_%s_LAST_CHECK_AT", strtoupper($server['type'])), $server->id));
+ }
+ }
+ }
+}
diff --git a/app/Console/Commands/CheckTicket.php b/app/Console/Commands/CheckTicket.php
new file mode 100644
index 0000000..a4ea978
--- /dev/null
+++ b/app/Console/Commands/CheckTicket.php
@@ -0,0 +1,52 @@
+where('updated_at', '<=', time() - 24 * 3600)
+ ->where('reply_status', 0)
+ ->get();
+ foreach ($tickets as $ticket) {
+ if ($ticket->user_id === $ticket->last_reply_user_id) continue;
+ $ticket->status = 1;
+ $ticket->save();
+ }
+ }
+}
diff --git a/app/Console/Commands/ClearUser.php b/app/Console/Commands/ClearUser.php
new file mode 100644
index 0000000..30eb338
--- /dev/null
+++ b/app/Console/Commands/ClearUser.php
@@ -0,0 +1,51 @@
+where('transfer_enable', 0)
+ ->where('expired_at', 0)
+ ->where('last_login_at', NULL);
+ $count = $builder->count();
+ if ($builder->delete()) {
+ $this->info("已删除${count}位没有任何数据的用户");
+ }
+ }
+}
diff --git a/app/Console/Commands/ResetLog.php b/app/Console/Commands/ResetLog.php
new file mode 100644
index 0000000..8342b5c
--- /dev/null
+++ b/app/Console/Commands/ResetLog.php
@@ -0,0 +1,52 @@
+delete();
+ StatServer::where('record_at', '<', strtotime('-2 month', time()))->delete();
+ Log::where('created_at', '<', strtotime('-1 month', time()))->delete();
+ }
+}
diff --git a/app/Console/Commands/ResetPassword.php b/app/Console/Commands/ResetPassword.php
new file mode 100644
index 0000000..37bc4f8
--- /dev/null
+++ b/app/Console/Commands/ResetPassword.php
@@ -0,0 +1,54 @@
+argument('email'))->first();
+ if (!$user) abort(500, '邮箱不存在');
+ $password = Helper::guid(false);
+ $user->password = password_hash($password, PASSWORD_DEFAULT);
+ $user->password_algo = null;
+ if (!$user->save()) abort(500, '重置失败');
+ $this->info("!!!重置成功!!!");
+ $this->info("新密码为:{$password},请尽快修改密码。");
+ }
+}
diff --git a/app/Console/Commands/ResetTraffic.php b/app/Console/Commands/ResetTraffic.php
new file mode 100644
index 0000000..157e288
--- /dev/null
+++ b/app/Console/Commands/ResetTraffic.php
@@ -0,0 +1,164 @@
+builder = User::where('expired_at', '!=', NULL)
+ ->where('expired_at', '>', time());
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ ini_set('memory_limit', -1);
+ $resetMethods = Plan::select(
+ DB::raw("GROUP_CONCAT(`id`) as plan_ids"),
+ DB::raw("reset_traffic_method as method")
+ )
+ ->groupBy('reset_traffic_method')
+ ->get()
+ ->toArray();
+ foreach ($resetMethods as $resetMethod) {
+ $planIds = explode(',', $resetMethod['plan_ids']);
+ switch (true) {
+ case ($resetMethod['method'] === NULL): {
+ $resetTrafficMethod = config('v2board.reset_traffic_method', 0);
+ $builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
+ switch ((int)$resetTrafficMethod) {
+ // month first day
+ case 0:
+ $this->resetByMonthFirstDay($builder);
+ break;
+ // expire day
+ case 1:
+ $this->resetByExpireDay($builder);
+ break;
+ // no action
+ case 2:
+ break;
+ // year first day
+ case 3:
+ $this->resetByYearFirstDay($builder);
+ // year expire day
+ case 4:
+ $this->resetByExpireYear($builder);
+ }
+ break;
+ }
+ case ($resetMethod['method'] === 0): {
+ $builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
+ $this->resetByMonthFirstDay($builder);
+ break;
+ }
+ case ($resetMethod['method'] === 1): {
+ $builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
+ $this->resetByExpireDay($builder);
+ break;
+ }
+ case ($resetMethod['method'] === 2): {
+ break;
+ }
+ case ($resetMethod['method'] === 3): {
+ $builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
+ $this->resetByYearFirstDay($builder);
+ break;
+ }
+ case ($resetMethod['method'] === 4): {
+ $builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
+ $this->resetByExpireYear($builder);
+ break;
+ }
+ }
+ }
+ }
+
+ private function resetByExpireYear($builder):void
+ {
+ $users = [];
+ foreach ($builder->get() as $item) {
+ $expireDay = date('m-d', $item->expired_at);
+ $today = date('m-d');
+ if ($expireDay === $today) {
+ array_push($users, $item->id);
+ }
+ }
+ User::whereIn('id', $users)->update([
+ 'u' => 0,
+ 'd' => 0
+ ]);
+ }
+
+ private function resetByYearFirstDay($builder):void
+ {
+ if ((string)date('md') === '0101') {
+ $builder->update([
+ 'u' => 0,
+ 'd' => 0
+ ]);
+ }
+ }
+
+ private function resetByMonthFirstDay($builder):void
+ {
+ if ((string)date('d') === '01') {
+ $builder->update([
+ 'u' => 0,
+ 'd' => 0
+ ]);
+ }
+ }
+
+ private function resetByExpireDay($builder):void
+ {
+ $lastDay = date('d', strtotime('last day of +0 months'));
+ $users = [];
+ foreach ($builder->get() as $item) {
+ $expireDay = date('d', $item->expired_at);
+ $today = date('d');
+ if ($expireDay === $today) {
+ array_push($users, $item->id);
+ }
+
+ if (($today === $lastDay) && $expireDay >= $lastDay) {
+ array_push($users, $item->id);
+ }
+ }
+ User::whereIn('id', $users)->update([
+ 'u' => 0,
+ 'd' => 0
+ ]);
+ }
+}
diff --git a/app/Console/Commands/ResetUser.php b/app/Console/Commands/ResetUser.php
new file mode 100644
index 0000000..51197ae
--- /dev/null
+++ b/app/Console/Commands/ResetUser.php
@@ -0,0 +1,58 @@
+confirm("确定要重置所有用户安全信息吗?")) {
+ return;
+ }
+ ini_set('memory_limit', -1);
+ $users = User::all();
+ foreach ($users as $user)
+ {
+ $user->token = Helper::guid();
+ $user->uuid = Helper::guid(true);
+ $user->save();
+ $this->info("已重置用户{$user->email}的安全信息");
+ }
+ }
+}
diff --git a/app/Console/Commands/SendRemindMail.php b/app/Console/Commands/SendRemindMail.php
new file mode 100644
index 0000000..8a069fb
--- /dev/null
+++ b/app/Console/Commands/SendRemindMail.php
@@ -0,0 +1,51 @@
+remind_expire) $mailService->remindExpire($user);
+ if ($user->remind_traffic) $mailService->remindTraffic($user);
+ }
+ }
+}
diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php
new file mode 100644
index 0000000..667e616
--- /dev/null
+++ b/app/Console/Commands/Test.php
@@ -0,0 +1,41 @@
+info("__ ______ ____ _ ");
+ $this->info("\ \ / /___ \| __ ) ___ __ _ _ __ __| | ");
+ $this->info(" \ \ / / __) | _ \ / _ \ / _` | '__/ _` | ");
+ $this->info(" \ V / / __/| |_) | (_) | (_| | | | (_| | ");
+ $this->info(" \_/ |_____|____/ \___/ \__,_|_| \__,_| ");
+ if (\File::exists(base_path() . '/.env')) {
+ $securePath = config('v2board.secure_path', config('v2board.frontend_admin_path', hash('crc32b', config('app.key'))));
+ $this->info("访问 http(s)://你的站点/{$securePath} 进入管理面板,你可以在用户中心修改你的密码。");
+ abort(500, '如需重新安装请删除目录下.env文件');
+ }
+
+ if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
+ abort(500, '复制环境文件失败,请检查目录权限');
+ }
+ $this->saveToEnv([
+ 'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),
+ 'DB_HOST' => $this->ask('请输入数据库地址(默认:localhost)', 'localhost'),
+ 'DB_DATABASE' => $this->ask('请输入数据库名'),
+ 'DB_USERNAME' => $this->ask('请输入数据库用户名'),
+ 'DB_PASSWORD' => $this->ask('请输入数据库密码')
+ ]);
+ \Artisan::call('config:clear');
+ \Artisan::call('config:cache');
+ try {
+ DB::connection()->getPdo();
+ } catch (\Exception $e) {
+ abort(500, '数据库连接失败');
+ }
+ $file = \File::get(base_path() . '/database/install.sql');
+ if (!$file) {
+ abort(500, '数据库文件不存在');
+ }
+ $sql = str_replace("\n", "", $file);
+ $sql = preg_split("/;/", $sql);
+ if (!is_array($sql)) {
+ abort(500, '数据库文件格式有误');
+ }
+ $this->info('正在导入数据库请稍等...');
+ foreach ($sql as $item) {
+ try {
+ DB::select(DB::raw($item));
+ } catch (\Exception $e) {
+ }
+ }
+ $this->info('数据库导入完成');
+ $email = '';
+ while (!$email) {
+ $email = $this->ask('请输入管理员邮箱?');
+ }
+ $password = Helper::guid(false);
+ if (!$this->registerAdmin($email, $password)) {
+ abort(500, '管理员账号注册失败,请重试');
+ }
+
+ $this->info('一切就绪');
+ $this->info("管理员邮箱:{$email}");
+ $this->info("管理员密码:{$password}");
+
+ $defaultSecurePath = hash('crc32b', config('app.key'));
+ $this->info("访问 http(s)://你的站点/{$defaultSecurePath} 进入管理面板,你可以在用户中心修改你的密码。");
+ } catch (\Exception $e) {
+ $this->error($e->getMessage());
+ }
+ }
+
+ private function registerAdmin($email, $password)
+ {
+ $user = new User();
+ $user->email = $email;
+ if (strlen($password) < 8) {
+ abort(500, '管理员密码长度最小为8位字符');
+ }
+ $user->password = password_hash($password, PASSWORD_DEFAULT);
+ $user->uuid = Helper::guid(true);
+ $user->token = Helper::guid();
+ $user->is_admin = 1;
+ return $user->save();
+ }
+
+ private function saveToEnv($data = [])
+ {
+ function set_env_var($key, $value)
+ {
+ if (! is_bool(strpos($value, ' '))) {
+ $value = '"' . $value . '"';
+ }
+ $key = strtoupper($key);
+
+ $envPath = app()->environmentFilePath();
+ $contents = file_get_contents($envPath);
+
+ preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches);
+
+ $oldValue = count($matches) ? $matches[0] : '';
+
+ if ($oldValue) {
+ $contents = str_replace("{$oldValue}", "{$key}={$value}", $contents);
+ } else {
+ $contents = $contents . "\n{$key}={$value}\n";
+ }
+
+ $file = fopen($envPath, 'w');
+ fwrite($file, $contents);
+ return fclose($file);
+ }
+ foreach($data as $key => $value) {
+ set_env_var($key, $value);
+ }
+ return true;
+ }
+}
diff --git a/app/Console/Commands/V2boardStatistics.php b/app/Console/Commands/V2boardStatistics.php
new file mode 100644
index 0000000..a455aaf
--- /dev/null
+++ b/app/Console/Commands/V2boardStatistics.php
@@ -0,0 +1,131 @@
+statUser();
+ $this->statServer();
+ $this->stat();
+ $this->info('耗时' . (microtime(true) - $startAt));
+ }
+
+ private function statServer()
+ {
+ $createdAt = time();
+ $recordAt = strtotime('-1 day', strtotime(date('Y-m-d')));
+ $statService = new StatisticalService();
+ $statService->setStartAt($recordAt);
+ $statService->setServerStats();
+ $stats = $statService->getStatServer();
+ DB::beginTransaction();
+ foreach ($stats as $stat) {
+ if (!StatServer::insert([
+ 'server_id' => $stat['server_id'],
+ 'server_type' => $stat['server_type'],
+ 'u' => $stat['u'],
+ 'd' => $stat['d'],
+ 'created_at' => $createdAt,
+ 'updated_at' => $createdAt,
+ 'record_type' => 'd',
+ 'record_at' => $recordAt
+ ])) {
+ DB::rollback();
+ throw new \Exception('stat server fail');
+ }
+ }
+ DB::commit();
+ $statService->clearStatServer();
+ }
+
+ private function statUser()
+ {
+ $createdAt = time();
+ $recordAt = strtotime('-1 day', strtotime(date('Y-m-d')));
+ $statService = new StatisticalService();
+ $statService->setStartAt($recordAt);
+ $statService->setUserStats();
+ $stats = $statService->getStatUser();
+ DB::beginTransaction();
+ foreach ($stats as $stat) {
+ if (!StatUser::insert([
+ 'user_id' => $stat['user_id'],
+ 'u' => $stat['u'],
+ 'd' => $stat['d'],
+ 'server_rate' => $stat['server_rate'],
+ 'created_at' => $createdAt,
+ 'updated_at' => $createdAt,
+ 'record_type' => 'd',
+ 'record_at' => $recordAt
+ ])) {
+ DB::rollback();
+ throw new \Exception('stat user fail');
+ }
+ }
+ DB::commit();
+ $statService->clearStatUser();
+ }
+
+ private function stat()
+ {
+ $endAt = strtotime(date('Y-m-d'));
+ $startAt = strtotime('-1 day', $endAt);
+ $statisticalService = new StatisticalService();
+ $statisticalService->setStartAt($startAt);
+ $statisticalService->setEndAt($endAt);
+ $data = $statisticalService->generateStatData();
+ $data['record_at'] = $startAt;
+ $data['record_type'] = 'd';
+ $statistic = Stat::where('record_at', $startAt)
+ ->where('record_type', 'd')
+ ->first();
+ if ($statistic) {
+ $statistic->update($data);
+ return;
+ }
+ Stat::create($data);
+ }
+}
diff --git a/app/Console/Commands/V2boardUpdate.php b/app/Console/Commands/V2boardUpdate.php
new file mode 100644
index 0000000..e39d92b
--- /dev/null
+++ b/app/Console/Commands/V2boardUpdate.php
@@ -0,0 +1,63 @@
+getPdo();
+ $file = \File::get(base_path() . '/database/update.sql');
+ if (!$file) {
+ abort(500, '数据库文件不存在');
+ }
+ $sql = str_replace("\n", "", $file);
+ $sql = preg_split("/;/", $sql);
+ if (!is_array($sql)) {
+ abort(500, '数据库文件格式有误');
+ }
+ $this->info('正在导入数据库请稍等...');
+ foreach ($sql as $item) {
+ if (!$item) continue;
+ try {
+ DB::select(DB::raw($item));
+ } catch (\Exception $e) {
+ }
+ }
+ \Artisan::call('horizon:terminate');
+ $this->info('更新完毕,队列服务已重启,你无需进行任何操作。');
+ }
+}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
new file mode 100644
index 0000000..abd5517
--- /dev/null
+++ b/app/Console/Kernel.php
@@ -0,0 +1,56 @@
+command('v2board:statistics')->dailyAt('0:10');
+ // check
+ $schedule->command('check:order')->everyMinute();
+ $schedule->command('check:commission')->everyMinute();
+ $schedule->command('check:ticket')->everyMinute();
+ // reset
+ $schedule->command('reset:traffic')->daily();
+ $schedule->command('reset:log')->daily();
+ // send
+ $schedule->command('send:remindMail')->dailyAt('11:30');
+ // horizon metrics
+ $schedule->command('horizon:snapshot')->everyFiveMinutes();
+ }
+
+ /**
+ * Register the commands for the application.
+ *
+ * @return void
+ */
+ protected function commands()
+ {
+ $this->load(__DIR__ . '/Commands');
+
+ require base_path('routes/console.php');
+ }
+}
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
new file mode 100755
index 0000000..96e3a68
--- /dev/null
+++ b/app/Exceptions/Handler.php
@@ -0,0 +1,77 @@
+ $e->getMessage(),
+ 'exception' => get_class($e),
+ 'file' => $e->getFile(),
+ 'line' => $e->getLine(),
+ 'trace' => collect($e->getTrace())->map(function ($trace) {
+ return Arr::except($trace, ['args']);
+ })->all(),
+ ] : [
+ 'message' => $this->isHttpException($e) ? $e->getMessage() : __("Uh-oh, we've had some problems, we're working on it."),
+ ];
+ }
+}
diff --git a/app/Http/Controllers/Admin/ConfigController.php b/app/Http/Controllers/Admin/ConfigController.php
new file mode 100755
index 0000000..1abc8cd
--- /dev/null
+++ b/app/Http/Controllers/Admin/ConfigController.php
@@ -0,0 +1,202 @@
+ $files
+ ]);
+ }
+
+ public function getThemeTemplate()
+ {
+ $path = public_path('theme/');
+ $files = array_map(function ($item) use ($path) {
+ return str_replace($path, '', $item);
+ }, glob($path . '*'));
+ return response([
+ 'data' => $files
+ ]);
+ }
+
+ public function testSendMail(Request $request)
+ {
+ $obj = new SendEmailJob([
+ 'email' => $request->user['email'],
+ 'subject' => 'This is v2board test email',
+ 'template_name' => 'notify',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'content' => 'This is v2board test email',
+ 'url' => config('v2board.app_url')
+ ]
+ ]);
+ return response([
+ 'data' => true,
+ 'log' => $obj->handle()
+ ]);
+ }
+
+ public function setTelegramWebhook(Request $request)
+ {
+ $hookUrl = url(config('v2board.app_url') . '/api/v1/guest/telegram/webhook?access_token=' . md5(config('v2board.telegram_bot_token', $request->input('telegram_bot_token'))));
+ $telegramService = new TelegramService($request->input('telegram_bot_token'));
+ $telegramService->getMe();
+ $telegramService->setWebhook($hookUrl);
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function fetch(Request $request)
+ {
+ $key = $request->input('key');
+ $data = [
+ 'invite' => [
+ 'invite_force' => (int)config('v2board.invite_force', 0),
+ 'invite_commission' => config('v2board.invite_commission', 10),
+ 'invite_gen_limit' => config('v2board.invite_gen_limit', 5),
+ 'invite_never_expire' => config('v2board.invite_never_expire', 0),
+ 'commission_first_time_enable' => config('v2board.commission_first_time_enable', 1),
+ 'commission_auto_check_enable' => config('v2board.commission_auto_check_enable', 1),
+ 'commission_withdraw_limit' => config('v2board.commission_withdraw_limit', 100),
+ 'commission_withdraw_method' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
+ 'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0),
+ 'commission_distribution_enable' => config('v2board.commission_distribution_enable', 0),
+ 'commission_distribution_l1' => config('v2board.commission_distribution_l1'),
+ 'commission_distribution_l2' => config('v2board.commission_distribution_l2'),
+ 'commission_distribution_l3' => config('v2board.commission_distribution_l3')
+ ],
+ 'site' => [
+ 'logo' => config('v2board.logo'),
+ 'force_https' => (int)config('v2board.force_https', 0),
+ 'stop_register' => (int)config('v2board.stop_register', 0),
+ 'app_name' => config('v2board.app_name', 'V2Board'),
+ 'app_description' => config('v2board.app_description', 'V2Board is best!'),
+ 'app_url' => config('v2board.app_url'),
+ 'subscribe_url' => config('v2board.subscribe_url'),
+ 'try_out_plan_id' => (int)config('v2board.try_out_plan_id', 0),
+ 'try_out_hour' => (int)config('v2board.try_out_hour', 1),
+ 'tos_url' => config('v2board.tos_url'),
+ 'currency' => config('v2board.currency', 'CNY'),
+ 'currency_symbol' => config('v2board.currency_symbol', '¥'),
+ ],
+ 'subscribe' => [
+ 'plan_change_enable' => (int)config('v2board.plan_change_enable', 1),
+ 'reset_traffic_method' => (int)config('v2board.reset_traffic_method', 0),
+ 'surplus_enable' => (int)config('v2board.surplus_enable', 1),
+ 'new_order_event_id' => (int)config('v2board.new_order_event_id', 0),
+ 'renew_order_event_id' => (int)config('v2board.renew_order_event_id', 0),
+ 'change_order_event_id' => (int)config('v2board.change_order_event_id', 0),
+ 'show_info_to_server_enable' => (int)config('v2board.show_info_to_server_enable', 0)
+ ],
+ 'frontend' => [
+ 'frontend_theme' => config('v2board.frontend_theme', 'v2board'),
+ 'frontend_theme_sidebar' => config('v2board.frontend_theme_sidebar', 'light'),
+ 'frontend_theme_header' => config('v2board.frontend_theme_header', 'dark'),
+ 'frontend_theme_color' => config('v2board.frontend_theme_color', 'default'),
+ 'frontend_background_url' => config('v2board.frontend_background_url'),
+ ],
+ 'server' => [
+ 'server_token' => config('v2board.server_token'),
+ 'server_pull_interval' => config('v2board.server_pull_interval', 60),
+ 'server_push_interval' => config('v2board.server_push_interval', 60),
+ ],
+ 'email' => [
+ 'email_template' => config('v2board.email_template', 'default'),
+ 'email_host' => config('v2board.email_host'),
+ 'email_port' => config('v2board.email_port'),
+ 'email_username' => config('v2board.email_username'),
+ 'email_password' => config('v2board.email_password'),
+ 'email_encryption' => config('v2board.email_encryption'),
+ 'email_from_address' => config('v2board.email_from_address')
+ ],
+ 'telegram' => [
+ 'telegram_bot_enable' => config('v2board.telegram_bot_enable', 0),
+ 'telegram_bot_token' => config('v2board.telegram_bot_token'),
+ 'telegram_discuss_link' => config('v2board.telegram_discuss_link')
+ ],
+ 'app' => [
+ 'windows_version' => config('v2board.windows_version'),
+ 'windows_download_url' => config('v2board.windows_download_url'),
+ 'macos_version' => config('v2board.macos_version'),
+ 'macos_download_url' => config('v2board.macos_download_url'),
+ 'android_version' => config('v2board.android_version'),
+ 'android_download_url' => config('v2board.android_download_url')
+ ],
+ 'safe' => [
+ 'email_verify' => (int)config('v2board.email_verify', 0),
+ 'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),
+ 'secure_path' => config('v2board.secure_path', config('v2board.frontend_admin_path', hash('crc32b', config('app.key')))),
+ 'email_whitelist_enable' => (int)config('v2board.email_whitelist_enable', 0),
+ 'email_whitelist_suffix' => config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT),
+ 'email_gmail_limit_enable' => config('v2board.email_gmail_limit_enable', 0),
+ 'recaptcha_enable' => (int)config('v2board.recaptcha_enable', 0),
+ 'recaptcha_key' => config('v2board.recaptcha_key'),
+ 'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
+ 'register_limit_by_ip_enable' => (int)config('v2board.register_limit_by_ip_enable', 0),
+ 'register_limit_count' => config('v2board.register_limit_count', 3),
+ 'register_limit_expire' => config('v2board.register_limit_expire', 60),
+ 'password_limit_enable' => (int)config('v2board.password_limit_enable', 1),
+ 'password_limit_count' => config('v2board.password_limit_count', 5),
+ 'password_limit_expire' => config('v2board.password_limit_expire', 60)
+ ]
+ ];
+ if ($key && isset($data[$key])) {
+ return response([
+ 'data' => [
+ $key => $data[$key]
+ ]
+ ]);
+ };
+ // TODO: default should be in Dict
+ return response([
+ 'data' => $data
+ ]);
+ }
+
+ public function save(ConfigSave $request)
+ {
+ $data = $request->validated();
+ $config = config('v2board');
+ foreach (ConfigSave::RULES as $k => $v) {
+ if (!in_array($k, array_keys(ConfigSave::RULES))) {
+ unset($config[$k]);
+ continue;
+ }
+ if (array_key_exists($k, $data)) {
+ $config[$k] = $data[$k];
+ }
+ }
+ $data = var_export($config, 1);
+ if (!File::put(base_path() . '/config/v2board.php', " true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/CouponController.php b/app/Http/Controllers/Admin/CouponController.php
new file mode 100644
index 0000000..2a1561c
--- /dev/null
+++ b/app/Http/Controllers/Admin/CouponController.php
@@ -0,0 +1,137 @@
+input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
+ $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
+ $sort = $request->input('sort') ? $request->input('sort') : 'id';
+ $builder = Coupon::orderBy($sort, $sortType);
+ $total = $builder->count();
+ $coupons = $builder->forPage($current, $pageSize)
+ ->get();
+ return response([
+ 'data' => $coupons,
+ 'total' => $total
+ ]);
+ }
+
+ public function show(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数有误');
+ }
+ $coupon = Coupon::find($request->input('id'));
+ if (!$coupon) {
+ abort(500, '优惠券不存在');
+ }
+ $coupon->show = $coupon->show ? 0 : 1;
+ if (!$coupon->save()) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function generate(CouponGenerate $request)
+ {
+ if ($request->input('generate_count')) {
+ $this->multiGenerate($request);
+ return;
+ }
+
+ $params = $request->validated();
+ if (!$request->input('id')) {
+ if (!isset($params['code'])) {
+ $params['code'] = Helper::randomChar(8);
+ }
+ if (!Coupon::create($params)) {
+ abort(500, '创建失败');
+ }
+ } else {
+ try {
+ Coupon::find($request->input('id'))->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ private function multiGenerate(CouponGenerate $request)
+ {
+ $coupons = [];
+ $coupon = $request->validated();
+ $coupon['created_at'] = $coupon['updated_at'] = time();
+ $coupon['show'] = 1;
+ unset($coupon['generate_count']);
+ for ($i = 0;$i < $request->input('generate_count');$i++) {
+ $coupon['code'] = Helper::randomChar(8);
+ array_push($coupons, $coupon);
+ }
+ DB::beginTransaction();
+ if (!Coupon::insert(array_map(function ($item) use ($coupon) {
+ // format data
+ if (isset($item['limit_plan_ids']) && is_array($item['limit_plan_ids'])) {
+ $item['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
+ }
+ if (isset($item['limit_period']) && is_array($item['limit_period'])) {
+ $item['limit_period'] = json_encode($coupon['limit_period']);
+ }
+ return $item;
+ }, $coupons))) {
+ DB::rollBack();
+ abort(500, '生成失败');
+ }
+ DB::commit();
+ $data = "名称,类型,金额或比例,开始时间,结束时间,可用次数,可用于订阅,券码,生成时间\r\n";
+ foreach($coupons as $coupon) {
+ $type = ['', '金额', '比例'][$coupon['type']];
+ $value = ['', ($coupon['value'] / 100),$coupon['value']][$coupon['type']];
+ $startTime = date('Y-m-d H:i:s', $coupon['started_at']);
+ $endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
+ $limitUse = $coupon['limit_use'] ?? '不限制';
+ $createTime = date('Y-m-d H:i:s', $coupon['created_at']);
+ $limitPlanIds = isset($coupon['limit_plan_ids']) ? implode("/", $coupon['limit_plan_ids']) : '不限制';
+ $data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
+ }
+ echo $data;
+ }
+
+ public function drop(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数有误');
+ }
+ $coupon = Coupon::find($request->input('id'));
+ if (!$coupon) {
+ abort(500, '优惠券不存在');
+ }
+ if (!$coupon->delete()) {
+ abort(500, '删除失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/KnowledgeController.php b/app/Http/Controllers/Admin/KnowledgeController.php
new file mode 100644
index 0000000..aa5cb66
--- /dev/null
+++ b/app/Http/Controllers/Admin/KnowledgeController.php
@@ -0,0 +1,113 @@
+input('id')) {
+ $knowledge = Knowledge::find($request->input('id'))->toArray();
+ if (!$knowledge) abort(500, '知识不存在');
+ return response([
+ 'data' => $knowledge
+ ]);
+ }
+ return response([
+ 'data' => Knowledge::select(['title', 'id', 'updated_at', 'category', 'show'])
+ ->orderBy('sort', 'ASC')
+ ->get()
+ ]);
+ }
+
+ public function getCategory(Request $request)
+ {
+ return response([
+ 'data' => array_keys(Knowledge::get()->groupBy('category')->toArray())
+ ]);
+ }
+
+ public function save(KnowledgeSave $request)
+ {
+ $params = $request->validated();
+
+ if (!$request->input('id')) {
+ if (!Knowledge::create($params)) {
+ abort(500, '创建失败');
+ }
+ } else {
+ try {
+ Knowledge::find($request->input('id'))->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function show(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数有误');
+ }
+ $knowledge = Knowledge::find($request->input('id'));
+ if (!$knowledge) {
+ abort(500, '知识不存在');
+ }
+ $knowledge->show = $knowledge->show ? 0 : 1;
+ if (!$knowledge->save()) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function sort(KnowledgeSort $request)
+ {
+ DB::beginTransaction();
+ try {
+ foreach ($request->input('knowledge_ids') as $k => $v) {
+ $knowledge = Knowledge::find($v);
+ $knowledge->timestamps = false;
+ $knowledge->update(['sort' => $k + 1]);
+ }
+ } catch (\Exception $e) {
+ DB::rollBack();
+ abort(500, '保存失败');
+ }
+ DB::commit();
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数有误');
+ }
+ $knowledge = Knowledge::find($request->input('id'));
+ if (!$knowledge) {
+ abort(500, '知识不存在');
+ }
+ if (!$knowledge->delete()) {
+ abort(500, '删除失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/NoticeController.php b/app/Http/Controllers/Admin/NoticeController.php
new file mode 100644
index 0000000..5664b99
--- /dev/null
+++ b/app/Http/Controllers/Admin/NoticeController.php
@@ -0,0 +1,81 @@
+ Notice::orderBy('id', 'DESC')->get()
+ ]);
+ }
+
+ public function save(NoticeSave $request)
+ {
+ $data = $request->only([
+ 'title',
+ 'content',
+ 'img_url',
+ 'tags'
+ ]);
+ if (!$request->input('id')) {
+ if (!Notice::create($data)) {
+ abort(500, '保存失败');
+ }
+ } else {
+ try {
+ Notice::find($request->input('id'))->update($data);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+
+
+ public function show(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数有误');
+ }
+ $notice = Notice::find($request->input('id'));
+ if (!$notice) {
+ abort(500, '公告不存在');
+ }
+ $notice->show = $notice->show ? 0 : 1;
+ if (!$notice->save()) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ $notice = Notice::find($request->input('id'));
+ if (!$notice) {
+ abort(500, '公告不存在');
+ }
+ if (!$notice->delete()) {
+ abort(500, '删除失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/OrderController.php b/app/Http/Controllers/Admin/OrderController.php
new file mode 100644
index 0000000..c743809
--- /dev/null
+++ b/app/Http/Controllers/Admin/OrderController.php
@@ -0,0 +1,190 @@
+input('filter')) {
+ foreach ($request->input('filter') as $filter) {
+ if ($filter['key'] === 'email') {
+ $user = User::where('email', "%{$filter['value']}%")->first();
+ if (!$user) continue;
+ $builder->where('user_id', $user->id);
+ continue;
+ }
+ if ($filter['condition'] === '模糊') {
+ $filter['condition'] = 'like';
+ $filter['value'] = "%{$filter['value']}%";
+ }
+ $builder->where($filter['key'], $filter['condition'], $filter['value']);
+ }
+ }
+ }
+
+ public function detail(Request $request)
+ {
+ $order = Order::find($request->input('id'));
+ if (!$order) abort(500, '订单不存在');
+ $order['commission_log'] = CommissionLog::where('trade_no', $order->trade_no)->get();
+ if ($order->surplus_order_ids) {
+ $order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
+ }
+ return response([
+ 'data' => $order
+ ]);
+ }
+
+ public function fetch(OrderFetch $request)
+ {
+ $current = $request->input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
+ $orderModel = Order::orderBy('created_at', 'DESC');
+ if ($request->input('is_commission')) {
+ $orderModel->where('invite_user_id', '!=', NULL);
+ $orderModel->whereNotIn('status', [0, 2]);
+ $orderModel->where('commission_balance', '>', 0);
+ }
+ $this->filter($request, $orderModel);
+ $total = $orderModel->count();
+ $res = $orderModel->forPage($current, $pageSize)
+ ->get();
+ $plan = Plan::get();
+ for ($i = 0; $i < count($res); $i++) {
+ for ($k = 0; $k < count($plan); $k++) {
+ if ($plan[$k]['id'] == $res[$i]['plan_id']) {
+ $res[$i]['plan_name'] = $plan[$k]['name'];
+ }
+ }
+ }
+ return response([
+ 'data' => $res,
+ 'total' => $total
+ ]);
+ }
+
+ public function paid(Request $request)
+ {
+ $order = Order::where('trade_no', $request->input('trade_no'))
+ ->first();
+ if (!$order) {
+ abort(500, '订单不存在');
+ }
+ if ($order->status !== 0) abort(500, '只能对待支付的订单进行操作');
+
+ $orderService = new OrderService($order);
+ if (!$orderService->paid('manual_operation')) {
+ abort(500, '更新失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function cancel(Request $request)
+ {
+ $order = Order::where('trade_no', $request->input('trade_no'))
+ ->first();
+ if (!$order) {
+ abort(500, '订单不存在');
+ }
+ if ($order->status !== 0) abort(500, '只能对待支付的订单进行操作');
+
+ $orderService = new OrderService($order);
+ if (!$orderService->cancel()) {
+ abort(500, '更新失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function update(OrderUpdate $request)
+ {
+ $params = $request->only([
+ 'commission_status'
+ ]);
+
+ $order = Order::where('trade_no', $request->input('trade_no'))
+ ->first();
+ if (!$order) {
+ abort(500, '订单不存在');
+ }
+
+ try {
+ $order->update($params);
+ } catch (\Exception $e) {
+ abort(500, '更新失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function assign(OrderAssign $request)
+ {
+ $plan = Plan::find($request->input('plan_id'));
+ $user = User::where('email', $request->input('email'))->first();
+
+ if (!$user) {
+ abort(500, '该用户不存在');
+ }
+
+ if (!$plan) {
+ abort(500, '该订阅不存在');
+ }
+
+ $userService = new UserService();
+ if ($userService->isNotCompleteOrderByUserId($user->id)) {
+ abort(500, '该用户还有待支付的订单,无法分配');
+ }
+
+ DB::beginTransaction();
+ $order = new Order();
+ $orderService = new OrderService($order);
+ $order->user_id = $user->id;
+ $order->plan_id = $plan->id;
+ $order->period = $request->input('period');
+ $order->trade_no = Helper::guid();
+ $order->total_amount = $request->input('total_amount');
+
+ if ($order->period === 'reset_price') {
+ $order->type = 4;
+ } else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
+ $order->type = 3;
+ } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) {
+ $order->type = 2;
+ } else {
+ $order->type = 1;
+ }
+
+ $orderService->setInvite($user);
+
+ if (!$order->save()) {
+ DB::rollback();
+ abort(500, '订单创建失败');
+ }
+
+ DB::commit();
+
+ return response([
+ 'data' => $order->trade_no
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/PaymentController.php b/app/Http/Controllers/Admin/PaymentController.php
new file mode 100644
index 0000000..8b340cf
--- /dev/null
+++ b/app/Http/Controllers/Admin/PaymentController.php
@@ -0,0 +1,133 @@
+ $methods
+ ]);
+ }
+
+ public function fetch()
+ {
+ $payments = Payment::orderBy('sort', 'ASC')->get();
+ foreach ($payments as $k => $v) {
+ $notifyUrl = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}");
+ if ($v->notify_domain) {
+ $parseUrl = parse_url($notifyUrl);
+ $notifyUrl = $v->notify_domain . $parseUrl['path'];
+ }
+ $payments[$k]['notify_url'] = $notifyUrl;
+ }
+ return response([
+ 'data' => $payments
+ ]);
+ }
+
+ public function getPaymentForm(Request $request)
+ {
+ $paymentService = new PaymentService($request->input('payment'), $request->input('id'));
+ return response([
+ 'data' => $paymentService->form()
+ ]);
+ }
+
+ public function show(Request $request)
+ {
+ $payment = Payment::find($request->input('id'));
+ if (!$payment) abort(500, '支付方式不存在');
+ $payment->enable = !$payment->enable;
+ if (!$payment->save()) abort(500, '保存失败');
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function save(Request $request)
+ {
+ if (!config('v2board.app_url')) {
+ abort(500, '请在站点配置中配置站点地址');
+ }
+ $params = $request->validate([
+ 'name' => 'required',
+ 'icon' => 'nullable',
+ 'payment' => 'required',
+ 'config' => 'required',
+ 'notify_domain' => 'nullable|url',
+ 'handling_fee_fixed' => 'nullable|integer',
+ 'handling_fee_percent' => 'nullable|numeric|between:0.1,100'
+ ], [
+ 'name.required' => '显示名称不能为空',
+ 'payment.required' => '网关参数不能为空',
+ 'config.required' => '配置参数不能为空',
+ 'notify_domain.url' => '自定义通知域名格式有误',
+ 'handling_fee_fixed.integer' => '固定手续费格式有误',
+ 'handling_fee_percent.between' => '百分比手续费范围须在0.1-100之间'
+ ]);
+ if ($request->input('id')) {
+ $payment = Payment::find($request->input('id'));
+ if (!$payment) abort(500, '支付方式不存在');
+ try {
+ $payment->update($params);
+ } catch (\Exception $e) {
+ abort(500, $e->getMessage());
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+ $params['uuid'] = Helper::randomChar(8);
+ if (!Payment::create($params)) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ $payment = Payment::find($request->input('id'));
+ if (!$payment) abort(500, '支付方式不存在');
+ return response([
+ 'data' => $payment->delete()
+ ]);
+ }
+
+
+ public function sort(Request $request)
+ {
+ $request->validate([
+ 'ids' => 'required|array'
+ ], [
+ 'ids.required' => '参数有误',
+ 'ids.array' => '参数有误'
+ ]);
+ DB::beginTransaction();
+ foreach ($request->input('ids') as $k => $v) {
+ if (!Payment::find($v)->update(['sort' => $k + 1])) {
+ DB::rollBack();
+ abort(500, '保存失败');
+ }
+ }
+ DB::commit();
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/PlanController.php b/app/Http/Controllers/Admin/PlanController.php
new file mode 100755
index 0000000..72659d4
--- /dev/null
+++ b/app/Http/Controllers/Admin/PlanController.php
@@ -0,0 +1,125 @@
+get();
+ foreach ($plans as $k => $v) {
+ $plans[$k]->count = 0;
+ foreach ($counts as $kk => $vv) {
+ if ($plans[$k]->id === $counts[$kk]->plan_id) $plans[$k]->count = $counts[$kk]->count;
+ }
+ }
+ return response([
+ 'data' => $plans
+ ]);
+ }
+
+ public function save(PlanSave $request)
+ {
+ $params = $request->validated();
+ if ($request->input('id')) {
+ $plan = Plan::find($request->input('id'));
+ if (!$plan) {
+ abort(500, '该订阅不存在');
+ }
+ DB::beginTransaction();
+ // update user group id and transfer
+ try {
+ if ($request->input('force_update')) {
+ User::where('plan_id', $plan->id)->update([
+ 'group_id' => $params['group_id'],
+ 'transfer_enable' => $params['transfer_enable'] * 1073741824,
+ 'speed_limit' => $params['speed_limit']
+ ]);
+ }
+ $plan->update($params);
+ } catch (\Exception $e) {
+ DB::rollBack();
+ abort(500, '保存失败');
+ }
+ DB::commit();
+ return response([
+ 'data' => true
+ ]);
+ }
+ if (!Plan::create($params)) {
+ abort(500, '创建失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if (Order::where('plan_id', $request->input('id'))->first()) {
+ abort(500, '该订阅下存在订单无法删除');
+ }
+ if (User::where('plan_id', $request->input('id'))->first()) {
+ abort(500, '该订阅下存在用户无法删除');
+ }
+ if ($request->input('id')) {
+ $plan = Plan::find($request->input('id'));
+ if (!$plan) {
+ abort(500, '该订阅ID不存在');
+ }
+ }
+ return response([
+ 'data' => $plan->delete()
+ ]);
+ }
+
+ public function update(PlanUpdate $request)
+ {
+ $updateData = $request->only([
+ 'show',
+ 'renew'
+ ]);
+
+ $plan = Plan::find($request->input('id'));
+ if (!$plan) {
+ abort(500, '该订阅不存在');
+ }
+
+ try {
+ $plan->update($updateData);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function sort(PlanSort $request)
+ {
+ DB::beginTransaction();
+ foreach ($request->input('plan_ids') as $k => $v) {
+ if (!Plan::find($v)->update(['sort' => $k + 1])) {
+ DB::rollBack();
+ abort(500, '保存失败');
+ }
+ }
+ DB::commit();
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/GroupController.php b/app/Http/Controllers/Admin/Server/GroupController.php
new file mode 100644
index 0000000..0d50077
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/GroupController.php
@@ -0,0 +1,85 @@
+input('group_id')) {
+ return response([
+ 'data' => [ServerGroup::find($request->input('group_id'))]
+ ]);
+ }
+ $serverGroups = ServerGroup::get();
+ $serverService = new ServerService();
+ $servers = $serverService->getAllServers();
+ foreach ($serverGroups as $k => $v) {
+ $serverGroups[$k]['user_count'] = User::where('group_id', $v['id'])->count();
+ $serverGroups[$k]['server_count'] = 0;
+ foreach ($servers as $server) {
+ if (in_array($v['id'], $server['group_id'])) {
+ $serverGroups[$k]['server_count'] = $serverGroups[$k]['server_count']+1;
+ }
+ }
+ }
+ return response([
+ 'data' => $serverGroups
+ ]);
+ }
+
+ public function save(Request $request)
+ {
+ if (empty($request->input('name'))) {
+ abort(500, '组名不能为空');
+ }
+
+ if ($request->input('id')) {
+ $serverGroup = ServerGroup::find($request->input('id'));
+ } else {
+ $serverGroup = new ServerGroup();
+ }
+
+ $serverGroup->name = $request->input('name');
+ return response([
+ 'data' => $serverGroup->save()
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if ($request->input('id')) {
+ $serverGroup = ServerGroup::find($request->input('id'));
+ if (!$serverGroup) {
+ abort(500, '组不存在');
+ }
+ }
+
+ $servers = ServerVmess::all();
+ foreach ($servers as $server) {
+ if (in_array($request->input('id'), $server->group_id)) {
+ abort(500, '该组已被节点所使用,无法删除');
+ }
+ }
+
+ if (Plan::where('group_id', $request->input('id'))->first()) {
+ abort(500, '该组已被订阅所使用,无法删除');
+ }
+ if (User::where('group_id', $request->input('id'))->first()) {
+ abort(500, '该组已被用户所使用,无法删除');
+ }
+ return response([
+ 'data' => $serverGroup->delete()
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/HysteriaController.php b/app/Http/Controllers/Admin/Server/HysteriaController.php
new file mode 100644
index 0000000..3094831
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/HysteriaController.php
@@ -0,0 +1,113 @@
+validate([
+ 'show' => '',
+ 'name' => 'required',
+ 'group_id' => 'required|array',
+ 'route_id' => 'nullable|array',
+ 'parent_id' => 'nullable|integer',
+ 'host' => 'required',
+ 'port' => 'required',
+ 'server_port' => 'required',
+ 'tags' => 'nullable|array',
+ 'rate' => 'required|numeric',
+ 'up_mbps' => 'required|numeric|min:1',
+ 'down_mbps' => 'required|numeric|min:1',
+ 'server_name' => 'nullable',
+ 'insecure' => 'required|in:0,1'
+ ]);
+
+ if ($request->input('id')) {
+ $server = ServerHysteria::find($request->input('id'));
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ if (!ServerHysteria::create($params)) {
+ abort(500, '创建失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if ($request->input('id')) {
+ $server = ServerHysteria::find($request->input('id'));
+ if (!$server) {
+ abort(500, '节点ID不存在');
+ }
+ }
+ return response([
+ 'data' => $server->delete()
+ ]);
+ }
+
+ public function update(Request $request)
+ {
+ $request->validate([
+ 'show' => 'in:0,1'
+ ], [
+ 'show.in' => '显示状态格式不正确'
+ ]);
+ $params = $request->only([
+ 'show',
+ ]);
+
+ $server = ServerHysteria::find($request->input('id'));
+
+ if (!$server) {
+ abort(500, '该服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function copy(Request $request)
+ {
+ $server = ServerHysteria::find($request->input('id'));
+ $server->show = 0;
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ if (!ServerHysteria::create($server->toArray())) {
+ abort(500, '复制失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/ManageController.php b/app/Http/Controllers/Admin/Server/ManageController.php
new file mode 100644
index 0000000..d901f85
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/ManageController.php
@@ -0,0 +1,44 @@
+ $serverService->getAllServers()
+ ]);
+ }
+
+ public function sort(Request $request)
+ {
+ ini_set('post_max_size', '1m');
+ $params = $request->only(
+ 'shadowsocks',
+ 'vmess',
+ 'trojan',
+ 'hysteria'
+ ) ?? [];
+ DB::beginTransaction();
+ foreach ($params as $k => $v) {
+ $model = 'App\\Models\\Server' . ucfirst($k);
+ foreach($v as $id => $sort) {
+ if (!$model::find($id)->update(['sort' => $sort])) {
+ DB::rollBack();
+ abort(500, '保存失败');
+ }
+ }
+ }
+ DB::commit();
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/RouteController.php b/app/Http/Controllers/Admin/Server/RouteController.php
new file mode 100644
index 0000000..c305270
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/RouteController.php
@@ -0,0 +1,72 @@
+ $route) {
+ $array = json_decode($route->match, true);
+ if (is_array($array)) $routes[$k]['match'] = $array;
+ }
+ // TODO: remove on 1.8.0
+ return [
+ 'data' => $routes
+ ];
+ }
+
+ public function save(Request $request)
+ {
+ $params = $request->validate([
+ 'remarks' => 'required',
+ 'match' => 'required|array',
+ 'action' => 'required|in:block,dns',
+ 'action_value' => 'nullable'
+ ], [
+ 'remarks.required' => '备注不能为空',
+ 'match.required' => '匹配值不能为空',
+ 'action.required' => '动作类型不能为空',
+ 'action.in' => '动作类型参数有误'
+ ]);
+ $params['match'] = array_filter($params['match']);
+ // TODO: remove on 1.8.0
+ $params['match'] = json_encode($params['match']);
+ // TODO: remove on 1.8.0
+ if ($request->input('id')) {
+ try {
+ $route = ServerRoute::find($request->input('id'));
+ $route->update($params);
+ return [
+ 'data' => true
+ ];
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ }
+ if (!ServerRoute::create($params)) abort(500, '创建失败');
+ return [
+ 'data' => true
+ ];
+ }
+
+ public function drop(Request $request)
+ {
+ $route = ServerRoute::find($request->input('id'));
+ if (!$route) abort(500, '路由不存在');
+ if (!$route->delete()) abort(500, '删除失败');
+ return [
+ 'data' => true
+ ];
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/ShadowsocksController.php b/app/Http/Controllers/Admin/Server/ShadowsocksController.php
new file mode 100644
index 0000000..5ac1261
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/ShadowsocksController.php
@@ -0,0 +1,91 @@
+validated();
+ if ($request->input('id')) {
+ $server = ServerShadowsocks::find($request->input('id'));
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ if (!ServerShadowsocks::create($params)) {
+ abort(500, '创建失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if ($request->input('id')) {
+ $server = ServerShadowsocks::find($request->input('id'));
+ if (!$server) {
+ abort(500, '节点ID不存在');
+ }
+ }
+ return response([
+ 'data' => $server->delete()
+ ]);
+ }
+
+ public function update(ServerShadowsocksUpdate $request)
+ {
+ $params = $request->only([
+ 'show',
+ ]);
+
+ $server = ServerShadowsocks::find($request->input('id'));
+
+ if (!$server) {
+ abort(500, '该服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function copy(Request $request)
+ {
+ $server = ServerShadowsocks::find($request->input('id'));
+ $server->show = 0;
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ if (!ServerShadowsocks::create($server->toArray())) {
+ abort(500, '复制失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/TrojanController.php b/app/Http/Controllers/Admin/Server/TrojanController.php
new file mode 100644
index 0000000..68a318c
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/TrojanController.php
@@ -0,0 +1,99 @@
+validated();
+ if ($request->input('id')) {
+ $server = ServerTrojan::find($request->input('id'));
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ if (!ServerTrojan::create($params)) {
+ abort(500, '创建失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if ($request->input('id')) {
+ $server = ServerTrojan::find($request->input('id'));
+ if (!$server) {
+ abort(500, '节点ID不存在');
+ }
+ }
+ return response([
+ 'data' => $server->delete()
+ ]);
+ }
+
+ public function update(ServerTrojanUpdate $request)
+ {
+ $params = $request->only([
+ 'show',
+ ]);
+
+ $server = ServerTrojan::find($request->input('id'));
+
+ if (!$server) {
+ abort(500, '该服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function copy(Request $request)
+ {
+ $server = ServerTrojan::find($request->input('id'));
+ $server->show = 0;
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ if (!ServerTrojan::create($server->toArray())) {
+ abort(500, '复制失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+ public function viewConfig(Request $request)
+ {
+ $serverService = new ServerService();
+ $config = $serverService->getTrojanConfig($request->input('node_id'), 23333);
+ return response([
+ 'data' => $config
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/Server/VmessController.php b/app/Http/Controllers/Admin/Server/VmessController.php
new file mode 100644
index 0000000..f09565e
--- /dev/null
+++ b/app/Http/Controllers/Admin/Server/VmessController.php
@@ -0,0 +1,92 @@
+validated();
+
+ if ($request->input('id')) {
+ $server = ServerVmess::find($request->input('id'));
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ if (!ServerVmess::create($params)) {
+ abort(500, '创建失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if ($request->input('id')) {
+ $server = ServerVmess::find($request->input('id'));
+ if (!$server) {
+ abort(500, '节点ID不存在');
+ }
+ }
+ return response([
+ 'data' => $server->delete()
+ ]);
+ }
+
+ public function update(ServerVmessUpdate $request)
+ {
+ $params = $request->only([
+ 'show',
+ ]);
+
+ $server = ServerVmess::find($request->input('id'));
+
+ if (!$server) {
+ abort(500, '该服务器不存在');
+ }
+ try {
+ $server->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function copy(Request $request)
+ {
+ $server = ServerVmess::find($request->input('id'));
+ $server->show = 0;
+ if (!$server) {
+ abort(500, '服务器不存在');
+ }
+ if (!ServerVmess::create($server->toArray())) {
+ abort(500, '复制失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/StatController.php b/app/Http/Controllers/Admin/StatController.php
new file mode 100644
index 0000000..dccb00f
--- /dev/null
+++ b/app/Http/Controllers/Admin/StatController.php
@@ -0,0 +1,227 @@
+validate([
+ 'start_at' => '',
+ 'end_at' => ''
+ ]);
+
+ if (isset($params['start_at']) && isset($params['end_at'])) {
+ $stats = Stat::where('record_at', '>=', $params['start_at'])
+ ->where('record_at', '<', $params['end_at'])
+ ->get()
+ ->makeHidden(['record_at', 'created_at', 'updated_at', 'id', 'record_type'])
+ ->toArray();
+ } else {
+ $statisticalService = new StatisticalService();
+ return [
+ 'data' => $statisticalService->generateStatData()
+ ];
+ }
+
+ $stats = array_reduce($stats, function($carry, $item) {
+ foreach($item as $key => $value) {
+ if(isset($carry[$key]) && $carry[$key]) {
+ $carry[$key] += $value;
+ } else {
+ $carry[$key] = $value;
+ }
+ }
+ return $carry;
+ }, []);
+
+ return [
+ 'data' => $stats
+ ];
+ }
+
+ public function getStatRecord(Request $request)
+ {
+ $request->validate([
+ 'type' => 'required|in:paid_total,commission_total,register_count',
+ 'start_at' => '',
+ 'end_at' => ''
+ ]);
+
+ $statisticalService = new StatisticalService();
+ $statisticalService->setStartAt($request->input('start_at'));
+ $statisticalService->setEndAt($request->input('end_at'));
+ return [
+ 'data' => $statisticalService->getStatRecord($request->input('type'))
+ ];
+ }
+
+ public function getRanking(Request $request)
+ {
+ $request->validate([
+ 'type' => 'required|in:server_traffic_rank,user_consumption_rank,invite_rank',
+ 'start_at' => '',
+ 'end_at' => '',
+ 'limit' => 'nullable|integer'
+ ]);
+
+ $statisticalService = new StatisticalService();
+ $statisticalService->setStartAt($request->input('start_at'));
+ $statisticalService->setEndAt($request->input('end_at'));
+ return [
+ 'data' => $statisticalService->getRanking($request->input('type'), $request->input('limit') ?? 20)
+ ];
+ }
+
+ public function getOverride(Request $request)
+ {
+ return [
+ 'data' => [
+ 'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1')))
+ ->where('created_at', '<', time())
+ ->whereNotIn('status', [0, 2])
+ ->sum('total_amount'),
+ 'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
+ ->where('created_at', '<', time())
+ ->count(),
+ 'ticket_pending_total' => Ticket::where('status', 0)
+ ->count(),
+ 'commission_pending_total' => Order::where('commission_status', 0)
+ ->where('invite_user_id', '!=', NULL)
+ ->whereNotIn('status', [0, 2])
+ ->where('commission_balance', '>', 0)
+ ->count(),
+ 'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d')))
+ ->where('created_at', '<', time())
+ ->whereNotIn('status', [0, 2])
+ ->sum('total_amount'),
+ 'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
+ ->where('created_at', '<', strtotime(date('Y-m-1')))
+ ->whereNotIn('status', [0, 2])
+ ->sum('total_amount'),
+ 'commission_month_payout' => CommissionLog::where('created_at', '>=', strtotime(date('Y-m-1')))
+ ->where('created_at', '<', time())
+ ->sum('get_amount'),
+ 'commission_last_month_payout' => CommissionLog::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
+ ->where('created_at', '<', strtotime(date('Y-m-1')))
+ ->sum('get_amount'),
+ ]
+ ];
+ }
+
+ public function getOrder(Request $request)
+ {
+ $statistics = Stat::where('record_type', 'd')
+ ->limit(31)
+ ->orderBy('record_at', 'DESC')
+ ->get()
+ ->toArray();
+ $result = [];
+ foreach ($statistics as $statistic) {
+ $date = date('m-d', $statistic['record_at']);
+ $result[] = [
+ 'type' => '收款金额',
+ 'date' => $date,
+ 'value' => $statistic['paid_total'] / 100
+ ];
+ $result[] = [
+ 'type' => '收款笔数',
+ 'date' => $date,
+ 'value' => $statistic['paid_count']
+ ];
+ $result[] = [
+ 'type' => '佣金金额(已发放)',
+ 'date' => $date,
+ 'value' => $statistic['commission_total'] / 100
+ ];
+ $result[] = [
+ 'type' => '佣金笔数(已发放)',
+ 'date' => $date,
+ 'value' => $statistic['commission_count']
+ ];
+ }
+ $result = array_reverse($result);
+ return [
+ 'data' => $result
+ ];
+ }
+
+ public function getServerLastRank()
+ {
+ $servers = [
+ 'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(),
+ 'v2ray' => ServerVmess::where('parent_id', null)->get()->toArray(),
+ 'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray(),
+ 'vmess' => ServerVmess::where('parent_id', null)->get()->toArray(),
+ 'hysteria' => ServerHysteria::where('parent_id', null)->get()->toArray()
+ ];
+ $startAt = strtotime('-1 day', strtotime(date('Y-m-d')));
+ $endAt = strtotime(date('Y-m-d'));
+ $statistics = StatServer::select([
+ 'server_id',
+ 'server_type',
+ 'u',
+ 'd',
+ DB::raw('(u+d) as total')
+ ])
+ ->where('record_at', '>=', $startAt)
+ ->where('record_at', '<', $endAt)
+ ->where('record_type', 'd')
+ ->limit(10)
+ ->orderBy('total', 'DESC')
+ ->get()
+ ->toArray();
+ foreach ($statistics as $k => $v) {
+ foreach ($servers[$v['server_type']] as $server) {
+ if ($server['id'] === $v['server_id']) {
+ $statistics[$k]['server_name'] = $server['name'];
+ }
+ }
+ $statistics[$k]['total'] = $statistics[$k]['total'] / 1073741824;
+ }
+ array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
+ return [
+ 'data' => $statistics
+ ];
+ }
+
+ public function getStatUser(Request $request)
+ {
+ $request->validate([
+ 'user_id' => 'required|integer'
+ ]);
+ $current = $request->input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
+ $builder = StatUser::orderBy('record_at', 'DESC')->where('user_id', $request->input('user_id'));
+
+ $total = $builder->count();
+ $records = $builder->forPage($current, $pageSize)
+ ->get();
+ return [
+ 'data' => $records,
+ 'total' => $total
+ ];
+ }
+
+}
+
diff --git a/app/Http/Controllers/Admin/SystemController.php b/app/Http/Controllers/Admin/SystemController.php
new file mode 100644
index 0000000..cf33b84
--- /dev/null
+++ b/app/Http/Controllers/Admin/SystemController.php
@@ -0,0 +1,105 @@
+ [
+ 'schedule' => $this->getScheduleStatus(),
+ 'horizon' => $this->getHorizonStatus(),
+ 'schedule_last_runtime' => Cache::get(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null))
+ ]
+ ]);
+ }
+
+ public function getQueueWorkload(WorkloadRepository $workload)
+ {
+ return response([
+ 'data' => collect($workload->get())->sortBy('name')->values()->toArray()
+ ]);
+ }
+
+ protected function getScheduleStatus():bool
+ {
+ return (time() - 120) < Cache::get(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null));
+ }
+
+ protected function getHorizonStatus():bool
+ {
+ if (! $masters = app(MasterSupervisorRepository::class)->all()) {
+ return false;
+ }
+
+ return collect($masters)->contains(function ($master) {
+ return $master->status === 'paused';
+ }) ? false : true;
+ }
+
+ public function getQueueStats()
+ {
+ return response([
+ 'data' => [
+ 'failedJobs' => app(JobRepository::class)->countRecentlyFailed(),
+ 'jobsPerMinute' => app(MetricsRepository::class)->jobsProcessedPerMinute(),
+ 'pausedMasters' => $this->totalPausedMasters(),
+ 'periods' => [
+ 'failedJobs' => config('horizon.trim.recent_failed', config('horizon.trim.failed')),
+ 'recentJobs' => config('horizon.trim.recent'),
+ ],
+ 'processes' => $this->totalProcessCount(),
+ 'queueWithMaxRuntime' => app(MetricsRepository::class)->queueWithMaximumRuntime(),
+ 'queueWithMaxThroughput' => app(MetricsRepository::class)->queueWithMaximumThroughput(),
+ 'recentJobs' => app(JobRepository::class)->countRecent(),
+ 'status' => $this->getHorizonStatus(),
+ 'wait' => collect(app(WaitTimeCalculator::class)->calculate())->take(1),
+ ]
+ ]);
+ }
+
+ /**
+ * Get the total process count across all supervisors.
+ *
+ * @return int
+ */
+ protected function totalProcessCount()
+ {
+ $supervisors = app(SupervisorRepository::class)->all();
+
+ return collect($supervisors)->reduce(function ($carry, $supervisor) {
+ return $carry + collect($supervisor->processes)->sum();
+ }, 0);
+ }
+
+ /**
+ * Get the number of master supervisors that are currently paused.
+ *
+ * @return int
+ */
+ protected function totalPausedMasters()
+ {
+ if (! $masters = app(MasterSupervisorRepository::class)->all()) {
+ return 0;
+ }
+
+ return collect($masters)->filter(function ($master) {
+ return $master->status === 'paused';
+ })->count();
+ }
+}
+
diff --git a/app/Http/Controllers/Admin/ThemeController.php b/app/Http/Controllers/Admin/ThemeController.php
new file mode 100644
index 0000000..7db9d44
--- /dev/null
+++ b/app/Http/Controllers/Admin/ThemeController.php
@@ -0,0 +1,91 @@
+path = $path = public_path('theme/');
+ $this->themes = array_map(function ($item) use ($path) {
+ return str_replace($path, '', $item);
+ }, glob($path . '*'));
+ }
+
+ public function getThemes()
+ {
+ $themeConfigs = [];
+ foreach ($this->themes as $theme) {
+ $themeConfigFile = $this->path . "{$theme}/config.json";
+ if (!File::exists($themeConfigFile)) continue;
+ $themeConfig = json_decode(File::get($themeConfigFile), true);
+ if (!isset($themeConfig['configs']) || !is_array($themeConfig)) continue;
+ $themeConfigs[$theme] = $themeConfig;
+ if (config("theme.{$theme}")) continue;
+ $themeService = new ThemeService($theme);
+ $themeService->init();
+ }
+ return response([
+ 'data' => [
+ 'themes' => $themeConfigs,
+ 'active' => config('v2board.frontend_theme', 'v2board')
+ ]
+ ]);
+ }
+
+ public function getThemeConfig(Request $request)
+ {
+ $payload = $request->validate([
+ 'name' => 'required|in:' . join(',', $this->themes)
+ ]);
+ return response([
+ 'data' => config("theme.{$payload['name']}")
+ ]);
+ }
+
+ public function saveThemeConfig(Request $request)
+ {
+ $payload = $request->validate([
+ 'name' => 'required|in:' . join(',', $this->themes),
+ 'config' => 'required'
+ ]);
+ $payload['config'] = json_decode(base64_decode($payload['config']), true);
+ if (!$payload['config'] || !is_array($payload['config'])) abort(500, '参数有误');
+ $themeConfigFile = public_path("theme/{$payload['name']}/config.json");
+ if (!File::exists($themeConfigFile)) abort(500, '主题不存在');
+ $themeConfig = json_decode(File::get($themeConfigFile), true);
+ if (!isset($themeConfig['configs']) || !is_array($themeConfig)) abort(500, '主题配置文件有误');
+ $validateFields = array_column($themeConfig['configs'], 'field_name');
+ $config = [];
+ foreach ($validateFields as $validateField) {
+ $config[$validateField] = isset($payload['config'][$validateField]) ? $payload['config'][$validateField] : '';
+ }
+
+ File::ensureDirectoryExists(base_path() . '/config/theme/');
+
+ $data = var_export($config, 1);
+ if (!File::put(base_path() . "/config/theme/{$payload['name']}.php", " $config
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/TicketController.php b/app/Http/Controllers/Admin/TicketController.php
new file mode 100644
index 0000000..18b2926
--- /dev/null
+++ b/app/Http/Controllers/Admin/TicketController.php
@@ -0,0 +1,96 @@
+input('id')) {
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->first();
+ if (!$ticket) {
+ abort(500, '工单不存在');
+ }
+ $ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
+ for ($i = 0; $i < count($ticket['message']); $i++) {
+ if ($ticket['message'][$i]['user_id'] !== $ticket->user_id) {
+ $ticket['message'][$i]['is_me'] = true;
+ } else {
+ $ticket['message'][$i]['is_me'] = false;
+ }
+ }
+ return response([
+ 'data' => $ticket
+ ]);
+ }
+ $current = $request->input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
+ $model = Ticket::orderBy('updated_at', 'DESC');
+ if ($request->input('status') !== NULL) {
+ $model->where('status', $request->input('status'));
+ }
+ if ($request->input('reply_status') !== NULL) {
+ $model->whereIn('reply_status', $request->input('reply_status'));
+ }
+ if ($request->input('email') !== NULL) {
+ $user = User::where('email', $request->input('email'))->first();
+ if ($user) $model->where('user_id', $user->id);
+ }
+ $total = $model->count();
+ $res = $model->forPage($current, $pageSize)
+ ->get();
+ return response([
+ 'data' => $res,
+ 'total' => $total
+ ]);
+ }
+
+ public function reply(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ if (empty($request->input('message'))) {
+ abort(500, '消息不能为空');
+ }
+ $ticketService = new TicketService();
+ $ticketService->replyByAdmin(
+ $request->input('id'),
+ $request->input('message'),
+ $request->user['id']
+ );
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function close(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->first();
+ if (!$ticket) {
+ abort(500, '工单不存在');
+ }
+ $ticket->status = 1;
+ if (!$ticket->save()) {
+ abort(500, '关闭失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
new file mode 100644
index 0000000..d4184ab
--- /dev/null
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -0,0 +1,294 @@
+input('id'));
+ if (!$user) abort(500, '用户不存在');
+ $user->token = Helper::guid();
+ $user->uuid = Helper::guid(true);
+ return response([
+ 'data' => $user->save()
+ ]);
+ }
+
+ private function filter(Request $request, $builder)
+ {
+ $filters = $request->input('filter');
+ if ($filters) {
+ foreach ($filters as $k => $filter) {
+ if ($filter['condition'] === '模糊') {
+ $filter['condition'] = 'like';
+ $filter['value'] = "%{$filter['value']}%";
+ }
+ if ($filter['key'] === 'd' || $filter['key'] === 'transfer_enable') {
+ $filter['value'] = $filter['value'] * 1073741824;
+ }
+ if ($filter['key'] === 'invite_by_email') {
+ $user = User::where('email', $filter['condition'], $filter['value'])->first();
+ $inviteUserId = isset($user->id) ? $user->id : 0;
+ $builder->where('invite_user_id', $inviteUserId);
+ unset($filters[$k]);
+ continue;
+ }
+ $builder->where($filter['key'], $filter['condition'], $filter['value']);
+ }
+ }
+ }
+
+ public function fetch(UserFetch $request)
+ {
+ $current = $request->input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
+ $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
+ $sort = $request->input('sort') ? $request->input('sort') : 'created_at';
+ $userModel = User::select(
+ DB::raw('*'),
+ DB::raw('(u+d) as total_used')
+ )
+ ->orderBy($sort, $sortType);
+ $this->filter($request, $userModel);
+ $total = $userModel->count();
+ $res = $userModel->forPage($current, $pageSize)
+ ->get();
+ $plan = Plan::get();
+ for ($i = 0; $i < count($res); $i++) {
+ for ($k = 0; $k < count($plan); $k++) {
+ if ($plan[$k]['id'] == $res[$i]['plan_id']) {
+ $res[$i]['plan_name'] = $plan[$k]['name'];
+ }
+ }
+ $res[$i]['subscribe_url'] = Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $res[$i]['token']);
+ }
+ return response([
+ 'data' => $res,
+ 'total' => $total
+ ]);
+ }
+
+ public function getUserInfoById(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ $user = User::find($request->input('id'));
+ if ($user->invite_user_id) {
+ $user['invite_user'] = User::find($user->invite_user_id);
+ }
+ return response([
+ 'data' => $user
+ ]);
+ }
+
+ public function update(UserUpdate $request)
+ {
+ $params = $request->validated();
+ $user = User::find($request->input('id'));
+ if (!$user) {
+ abort(500, '用户不存在');
+ }
+ if (User::where('email', $params['email'])->first() && $user->email !== $params['email']) {
+ abort(500, '邮箱已被使用');
+ }
+ if (isset($params['password'])) {
+ $params['password'] = password_hash($params['password'], PASSWORD_DEFAULT);
+ $params['password_algo'] = NULL;
+ } else {
+ unset($params['password']);
+ }
+ if (isset($params['plan_id'])) {
+ $plan = Plan::find($params['plan_id']);
+ if (!$plan) {
+ abort(500, '订阅计划不存在');
+ }
+ $params['group_id'] = $plan->group_id;
+ }
+ if ($request->input('invite_user_email')) {
+ $inviteUser = User::where('email', $request->input('invite_user_email'))->first();
+ if ($inviteUser) {
+ $params['invite_user_id'] = $inviteUser->id;
+ }
+ } else {
+ $params['invite_user_id'] = null;
+ }
+
+ if (isset($params['banned']) && (int)$params['banned'] === 1) {
+ $authService = new AuthService($user);
+ $authService->removeAllSession();
+ }
+
+ try {
+ $user->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function dumpCSV(Request $request)
+ {
+ $userModel = User::orderBy('id', 'asc');
+ $this->filter($request, $userModel);
+ $res = $userModel->get();
+ $plan = Plan::get();
+ for ($i = 0; $i < count($res); $i++) {
+ for ($k = 0; $k < count($plan); $k++) {
+ if ($plan[$k]['id'] == $res[$i]['plan_id']) {
+ $res[$i]['plan_name'] = $plan[$k]['name'];
+ }
+ }
+ }
+
+ $data = "邮箱,余额,推广佣金,总流量,剩余流量,套餐到期时间,订阅计划,订阅地址\r\n";
+ foreach($res as $user) {
+ $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
+ $balance = $user['balance'] / 100;
+ $commissionBalance = $user['commission_balance'] / 100;
+ $transferEnable = $user['transfer_enable'] ? $user['transfer_enable'] / 1073741824 : 0;
+ $notUseFlow = (($user['transfer_enable'] - ($user['u'] + $user['d'])) / 1073741824) ?? 0;
+ $planName = $user['plan_name'] ?? '无订阅';
+ $subscribeUrl = Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $user['token']);
+ $data .= "{$user['email']},{$balance},{$commissionBalance},{$transferEnable},{$notUseFlow},{$expireDate},{$planName},{$subscribeUrl}\r\n";
+ }
+ echo "\xEF\xBB\xBF" . $data;
+ }
+
+ public function generate(UserGenerate $request)
+ {
+ if ($request->input('email_prefix')) {
+ if ($request->input('plan_id')) {
+ $plan = Plan::find($request->input('plan_id'));
+ if (!$plan) {
+ abort(500, '订阅计划不存在');
+ }
+ }
+ $user = [
+ 'email' => $request->input('email_prefix') . '@' . $request->input('email_suffix'),
+ 'plan_id' => isset($plan->id) ? $plan->id : NULL,
+ 'group_id' => isset($plan->group_id) ? $plan->group_id : NULL,
+ 'transfer_enable' => isset($plan->transfer_enable) ? $plan->transfer_enable * 1073741824 : 0,
+ 'expired_at' => $request->input('expired_at') ?? NULL,
+ 'uuid' => Helper::guid(true),
+ 'token' => Helper::guid()
+ ];
+ if (User::where('email', $user['email'])->first()) {
+ abort(500, '邮箱已存在于系统中');
+ }
+ $user['password'] = password_hash($request->input('password') ?? $user['email'], PASSWORD_DEFAULT);
+ if (!User::create($user)) {
+ abort(500, '生成失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+ if ($request->input('generate_count')) {
+ $this->multiGenerate($request);
+ }
+ }
+
+ private function multiGenerate(Request $request)
+ {
+ if ($request->input('plan_id')) {
+ $plan = Plan::find($request->input('plan_id'));
+ if (!$plan) {
+ abort(500, '订阅计划不存在');
+ }
+ }
+ $users = [];
+ for ($i = 0;$i < $request->input('generate_count');$i++) {
+ $user = [
+ 'email' => Helper::randomChar(6) . '@' . $request->input('email_suffix'),
+ 'plan_id' => isset($plan->id) ? $plan->id : NULL,
+ 'group_id' => isset($plan->group_id) ? $plan->group_id : NULL,
+ 'transfer_enable' => isset($plan->transfer_enable) ? $plan->transfer_enable * 1073741824 : 0,
+ 'expired_at' => $request->input('expired_at') ?? NULL,
+ 'uuid' => Helper::guid(true),
+ 'token' => Helper::guid(),
+ 'created_at' => time(),
+ 'updated_at' => time()
+ ];
+ $user['password'] = password_hash($request->input('password') ?? $user['email'], PASSWORD_DEFAULT);
+ array_push($users, $user);
+ }
+ DB::beginTransaction();
+ if (!User::insert($users)) {
+ DB::rollBack();
+ abort(500, '生成失败');
+ }
+ DB::commit();
+ $data = "账号,密码,过期时间,UUID,创建时间,订阅地址\r\n";
+ foreach($users as $user) {
+ $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
+ $createDate = date('Y-m-d H:i:s', $user['created_at']);
+ $password = $request->input('password') ?? $user['email'];
+ $subscribeUrl = Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $user['token']);
+ $data .= "{$user['email']},{$password},{$expireDate},{$user['uuid']},{$createDate},{$subscribeUrl}\r\n";
+ }
+ echo $data;
+ }
+
+ public function sendMail(UserSendMail $request)
+ {
+ $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
+ $sort = $request->input('sort') ? $request->input('sort') : 'created_at';
+ $builder = User::orderBy($sort, $sortType);
+ $this->filter($request, $builder);
+ $users = $builder->get();
+ foreach ($users as $user) {
+ SendEmailJob::dispatch([
+ 'email' => $user->email,
+ 'subject' => $request->input('subject'),
+ 'template_name' => 'notify',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'url' => config('v2board.app_url'),
+ 'content' => $request->input('content')
+ ]
+ ],
+ 'send_email_mass');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function ban(Request $request)
+ {
+ $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
+ $sort = $request->input('sort') ? $request->input('sort') : 'created_at';
+ $builder = User::orderBy($sort, $sortType);
+ $this->filter($request, $builder);
+ try {
+ $builder->update([
+ 'banned' => 1
+ ]);
+ } catch (\Exception $e) {
+ abort(500, '处理失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Client/ClientController.php b/app/Http/Controllers/Client/ClientController.php
new file mode 100644
index 0000000..a994e3e
--- /dev/null
+++ b/app/Http/Controllers/Client/ClientController.php
@@ -0,0 +1,62 @@
+input('flag')
+ ?? ($_SERVER['HTTP_USER_AGENT'] ?? '');
+ $flag = strtolower($flag);
+ $user = $request->user;
+ // account not expired and is not banned.
+ $userService = new UserService();
+ if ($userService->isAvailable($user)) {
+ $serverService = new ServerService();
+ $servers = $serverService->getAvailableServers($user);
+ $this->setSubscribeInfoToServers($servers, $user);
+ if ($flag) {
+ foreach (array_reverse(glob(app_path('Http//Controllers//Client//Protocols') . '/*.php')) as $file) {
+ $file = 'App\\Http\\Controllers\\Client\\Protocols\\' . basename($file, '.php');
+ $class = new $file($user, $servers);
+ if (strpos($flag, $class->flag) !== false) {
+ die($class->handle());
+ }
+ }
+ }
+ $class = new General($user, $servers);
+ die($class->handle());
+ }
+ }
+
+ private function setSubscribeInfoToServers(&$servers, $user)
+ {
+ if (!isset($servers[0])) return;
+ if (!(int)config('v2board.show_info_to_server_enable', 0)) return;
+ $useTraffic = $user['u'] + $user['d'];
+ $totalTraffic = $user['transfer_enable'];
+ $remainingTraffic = Helper::trafficConvert($totalTraffic - $useTraffic);
+ $expiredDate = $user['expired_at'] ? date('Y-m-d', $user['expired_at']) : 'No expiration';
+ $userService = new UserService();
+ $resetDay = $userService->getResetDay($user);
+ array_unshift($servers, array_merge($servers[0], [
+ 'name' => "Expired at {$expiredDate}",
+ ]));
+ if ($resetDay) {
+ array_unshift($servers, array_merge($servers[0], [
+ 'name' => "Reset usage after {$resetDay} day(s)",
+ ]));
+ }
+ array_unshift($servers, array_merge($servers[0], [
+ 'name' => "Remain: {$remainingTraffic}",
+ ]));
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/Clash.php b/app/Http/Controllers/Client/Protocols/Clash.php
new file mode 100644
index 0000000..1d53250
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/Clash.php
@@ -0,0 +1,272 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+ $appName = config('v2board.app_name', 'V2Board');
+ header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
+ header('profile-update-interval: 24');
+ header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName));
+ $defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
+ $customConfig = base_path() . '/resources/rules/custom.clash.yaml';
+ if (\File::exists($customConfig)) {
+ $config = Yaml::parseFile($customConfig);
+ } else {
+ $config = Yaml::parseFile($defaultConfig);
+ }
+ $proxy = [];
+ $proxies = [];
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks') {
+ array_push($proxy, self::buildShadowsocks($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ if ($item['type'] === 'vmess') {
+ if (is_array($item['tags']) && in_array("VLESS", $item['tags'])) {
+ array_push($proxy, self::buildVless($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ } else {
+ array_push($proxy, self::buildVmess($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ }
+ if ($item['type'] === 'trojan') {
+ array_push($proxy, self::buildTrojan($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ if ($item['type'] === 'hysteria') {
+ array_push($proxy, self::buildHysteria($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ }
+
+ $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
+ foreach ($config['proxy-groups'] as $k => $v) {
+ if (!is_array($config['proxy-groups'][$k]['proxies'])) $config['proxy-groups'][$k]['proxies'] = [];
+ $isFilter = false;
+ foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
+ foreach ($proxies as $dst) {
+ if (!$this->isRegex($src)) continue;
+ $isFilter = true;
+ $config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src]));
+ if ($this->isMatch($src, $dst)) {
+ array_push($config['proxy-groups'][$k]['proxies'], $dst);
+ }
+ }
+ if ($isFilter) continue;
+ }
+ if ($isFilter) continue;
+ $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
+ }
+ $config['proxy-groups'] = array_filter($config['proxy-groups'], function($group) {
+ return $group['proxies'];
+ });
+ $config['proxy-groups'] = array_values($config['proxy-groups']);
+ // Force the current subscription domain to be a direct rule
+ $subsDomain = $_SERVER['HTTP_HOST'];
+ if ($subsDomain) {
+ array_unshift($config['rules'], "DOMAIN,{$subsDomain},DIRECT");
+ }
+
+ $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
+ $yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
+ return $yaml;
+ }
+
+ public static function buildShadowsocks($password, $server)
+ {
+ if ($server['cipher'] === '2022-blake3-aes-128-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 16);
+ $userKey = Helper::uuidToBase64($password, 16);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ if ($server['cipher'] === '2022-blake3-aes-256-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 32);
+ $userKey = Helper::uuidToBase64($password, 32);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'ss';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['cipher'] = $server['cipher'];
+ $array['password'] = $password;
+ $array['udp'] = true;
+ return $array;
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'vmess';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['uuid'] = $uuid;
+ $array['alterId'] = 0;
+ $array['cipher'] = 'auto';
+ $array['udp'] = true;
+
+ if ($server['tls']) {
+ $array['tls'] = true;
+ $array['cipher'] = 'zero';
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $array['servername'] = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $array['network'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $array['http-opts']['path'] = $tcpSettings['header']['request']['path'][0];
+ }
+ if ($server['network'] === 'ws') {
+ $array['network'] = 'ws';
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ $array['ws-opts'] = [];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-opts']['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
+ }
+ $array['max-early-data'] = 4096;
+ $array['early-data-header-name'] = 'Sec-WebSocket-Protocol';
+ }
+ if ($server['network'] === 'grpc') {
+ $array['network'] = 'grpc';
+ if ($server['networkSettings']) {
+ $grpcSettings = $server['networkSettings'];
+ $array['grpc-opts'] = [];
+ if (isset($grpcSettings['serviceName'])) $array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
+ }
+ }
+
+ return $array;
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'trojan';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['password'] = $password;
+ $array['udp'] = true;
+ if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
+ if (!empty($server['allow_insecure'])) $array['skip-cert-verify'] = ($server['allow_insecure'] ? true : false);
+ return $array;
+ }
+
+ public static function buildHysteria($password, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'hysteria';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['auth_str'] = $password;
+// $array['obfs'] = $server['server_key'];
+ $array['protocol'] = 'udp';
+ $array['up'] = $server['up_mbps'];
+ $array['down'] = $server['down_mbps'];
+ if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
+ $array['skip-cert-verify'] = !empty($server['insecure']) ? true : false;
+ return $array;
+ }
+
+ public static function buildVless($uuid, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'vless';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['uuid'] = $uuid;
+ $array['udp'] = true;
+
+ if ($server['tls']) {
+ $array['tls'] = true;
+ if (is_array($server['tags']) && in_array("VLESS", $server['tags']) && in_array("XTLS", $server['tags'])) {
+ $array['flow'] = "xtls-rprx-vision-udp443";
+ }
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $array['servername'] = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $array['network'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $array['http-opts']['path'] = $tcpSettings['header']['request']['path'][0];
+ }
+ if ($server['network'] === 'ws') {
+ $array['network'] = 'ws';
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ $array['ws-opts'] = [];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-opts']['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
+ }
+ $array['max-early-data'] = 4096;
+ $array['early-data-header-name'] = 'Sec-WebSocket-Protocol';
+ }
+ if ($server['network'] === 'grpc') {
+ $array['network'] = 'grpc';
+ if ($server['networkSettings']) {
+ $grpcSettings = $server['networkSettings'];
+ $array['grpc-opts'] = [];
+ if (isset($grpcSettings['serviceName'])) $array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
+ }
+ }
+
+ return $array;
+ }
+
+ private function isMatch($exp, $str)
+ {
+ return @preg_match($exp, $str);
+ }
+
+ private function isRegex($exp)
+ {
+ return @preg_match($exp, null) !== false;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/GCLH.php b/app/Http/Controllers/Client/Protocols/GCLH.php
new file mode 100644
index 0000000..0f21b9e
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/GCLH.php
@@ -0,0 +1,272 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+ $appName = config('v2board.app_name', 'V2Board');
+ header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
+ header('profile-update-interval: 24');
+ header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName));
+ $defaultConfig = base_path() . '/resources/rules/vpn.clash.yaml';
+ $customConfig = base_path() . '/resources/rules/customvpn.clash.yaml';
+ if (\File::exists($customConfig)) {
+ $config = Yaml::parseFile($customConfig);
+ } else {
+ $config = Yaml::parseFile($defaultConfig);
+ }
+ $proxy = [];
+ $proxies = [];
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks') {
+ array_push($proxy, self::buildShadowsocks($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ if ($item['type'] === 'vmess') {
+ if (is_array($item['tags']) && in_array("VLESS", $item['tags'])) {
+ array_push($proxy, self::buildVless($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ } else {
+ array_push($proxy, self::buildVmess($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ }
+ if ($item['type'] === 'trojan') {
+ array_push($proxy, self::buildTrojan($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ if ($item['type'] === 'hysteria') {
+ array_push($proxy, self::buildHysteria($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ }
+
+ $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
+ foreach ($config['proxy-groups'] as $k => $v) {
+ if (!is_array($config['proxy-groups'][$k]['proxies'])) $config['proxy-groups'][$k]['proxies'] = [];
+ $isFilter = false;
+ foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
+ foreach ($proxies as $dst) {
+ if (!$this->isRegex($src)) continue;
+ $isFilter = true;
+ $config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src]));
+ if ($this->isMatch($src, $dst)) {
+ array_push($config['proxy-groups'][$k]['proxies'], $dst);
+ }
+ }
+ if ($isFilter) continue;
+ }
+ if ($isFilter) continue;
+ $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
+ }
+ $config['proxy-groups'] = array_filter($config['proxy-groups'], function($group) {
+ return $group['proxies'];
+ });
+ $config['proxy-groups'] = array_values($config['proxy-groups']);
+ // Force the current subscription domain to be a direct rule
+ $subsDomain = $_SERVER['HTTP_HOST'];
+ if ($subsDomain) {
+ array_unshift($config['rules'], "DOMAIN,{$subsDomain},DIRECT");
+ }
+
+ $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
+ $yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
+ return $yaml;
+ }
+
+ public static function buildShadowsocks($password, $server)
+ {
+ if ($server['cipher'] === '2022-blake3-aes-128-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 16);
+ $userKey = Helper::uuidToBase64($password, 16);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ if ($server['cipher'] === '2022-blake3-aes-256-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 32);
+ $userKey = Helper::uuidToBase64($password, 32);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'ss';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['cipher'] = $server['cipher'];
+ $array['password'] = $password;
+ $array['udp'] = true;
+ return $array;
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'vmess';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['uuid'] = $uuid;
+ $array['alterId'] = 0;
+ $array['cipher'] = 'auto';
+ $array['udp'] = true;
+
+ if ($server['tls']) {
+ $array['tls'] = true;
+ $array['cipher'] = 'zero';
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $array['servername'] = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $array['network'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $array['http-opts']['path'] = $tcpSettings['header']['request']['path'][0];
+ }
+ if ($server['network'] === 'ws') {
+ $array['network'] = 'ws';
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ $array['ws-opts'] = [];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-opts']['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
+ }
+ $array['max-early-data'] = 4096;
+ $array['early-data-header-name'] = 'Sec-WebSocket-Protocol';
+ }
+ if ($server['network'] === 'grpc') {
+ $array['network'] = 'grpc';
+ if ($server['networkSettings']) {
+ $grpcSettings = $server['networkSettings'];
+ $array['grpc-opts'] = [];
+ if (isset($grpcSettings['serviceName'])) $array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
+ }
+ }
+
+ return $array;
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'trojan';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['password'] = $password;
+ $array['udp'] = true;
+ if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
+ if (!empty($server['allow_insecure'])) $array['skip-cert-verify'] = ($server['allow_insecure'] ? true : false);
+ return $array;
+ }
+
+ public static function buildHysteria($password, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'hysteria';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['auth_str'] = $password;
+// $array['obfs'] = $server['server_key'];
+ $array['protocol'] = 'udp';
+ $array['up'] = $server['up_mbps'];
+ $array['down'] = $server['down_mbps'];
+ if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
+ $array['skip-cert-verify'] = !empty($server['insecure']) ? true : false;
+ return $array;
+ }
+
+ public static function buildVless($uuid, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'vless';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['uuid'] = $uuid;
+ $array['udp'] = true;
+
+ if ($server['tls']) {
+ $array['tls'] = true;
+ if (is_array($server['tags']) && in_array("VLESS", $server['tags']) && in_array("XTLS", $server['tags'])) {
+ $array['flow'] = "xtls-rprx-vision-udp443";
+ }
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $array['servername'] = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $array['network'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $array['http-opts']['path'] = $tcpSettings['header']['request']['path'][0];
+ }
+ if ($server['network'] === 'ws') {
+ $array['network'] = 'ws';
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ $array['ws-opts'] = [];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-opts']['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
+ }
+ $array['max-early-data'] = 4096;
+ $array['early-data-header-name'] = 'Sec-WebSocket-Protocol';
+ }
+ if ($server['network'] === 'grpc') {
+ $array['network'] = 'grpc';
+ if ($server['networkSettings']) {
+ $grpcSettings = $server['networkSettings'];
+ $array['grpc-opts'] = [];
+ if (isset($grpcSettings['serviceName'])) $array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
+ }
+ }
+
+ return $array;
+ }
+
+ private function isMatch($exp, $str)
+ {
+ return @preg_match($exp, $str);
+ }
+
+ private function isRegex($exp)
+ {
+ return @preg_match($exp, null) !== false;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/General.php b/app/Http/Controllers/Client/Protocols/General.php
new file mode 100644
index 0000000..675a05a
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/General.php
@@ -0,0 +1,182 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+ $uri = '';
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'vmess') {
+ if (is_array($item['tags']) && in_array("VLESS", $item['tags'])) {
+ $uri .= self::buildVless($user['uuid'], $item);
+ } else {
+ $uri .= self::buildVmess($user['uuid'], $item);
+ }
+ }
+ if ($item['type'] === 'shadowsocks') {
+ $uri .= self::buildShadowsocks($user['uuid'], $item);
+ }
+ if ($item['type'] === 'trojan') {
+ $uri .= self::buildTrojan($user['uuid'], $item);
+ }
+ if ($item['type'] === 'hysteria') {
+ $uri .= self::buildHysteria($user['uuid'], $item);
+ }
+ }
+ return base64_encode($uri);
+ }
+
+ public static function buildShadowsocks($password, $server)
+ {
+ if ($server['cipher'] === '2022-blake3-aes-128-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 16);
+ $userKey = Helper::uuidToBase64($password, 16);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ if ($server['cipher'] === '2022-blake3-aes-256-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 32);
+ $userKey = Helper::uuidToBase64($password, 32);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ $name = rawurlencode($server['name']);
+ $str = str_replace(
+ ['+', '/', '='],
+ ['-', '_', ''],
+ base64_encode("{$server['cipher']}:{$password}")
+ );
+ return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $config = [
+ "v" => "2",
+ "ps" => $server['name'],
+ "add" => $server['host'],
+ "port" => (string)$server['port'],
+ "id" => $uuid,
+ "aid" => '0',
+ "net" => $server['network'],
+ "type" => "none",
+ "host" => "",
+ "path" => "",
+ "tls" => $server['tls'] ? "tls" : "",
+ ];
+ if ($server['tls']) {
+ $config['fp'] = 'firefox';
+ $config['scy'] = 'zero';
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $config['sni'] = $tlsSettings['serverName'];
+ }
+ }
+ if ((string)$server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $config['type'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $config['path'] = $tcpSettings['header']['request']['path'][0];
+ if (isset($tcpSettings['header']['headers']['Host'][0])) $config['host'] = $tcpSettings['header']['headers']['Host'][0];
+ }
+ if ((string)$server['network'] === 'ws') {
+ $wsSettings = $server['networkSettings'];
+ if (isset($wsSettings['path'])) $config['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
+ }
+ if ((string)$server['network'] === 'grpc') {
+ $grpcSettings = $server['networkSettings'];
+ if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
+ }
+ return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $remote = filter_var($server['host'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '[' . $server['host'] . ']' : $server['host'];
+ $name = rawurlencode($server['name']);
+ $query = http_build_query([
+ 'allowInsecure' => $server['allow_insecure'],
+ 'peer' => $server['server_name'],
+ 'sni' => $server['server_name'],
+ 'fp' => 'firefox'
+ ]);
+ $uri = "trojan://{$password}@{$remote}:{$server['port']}?{$query}#{$name}";
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildHysteria($password, $server)
+ {
+ $remote = filter_var($server['host'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '[' . $server['host'] . ']' : $server['host'];
+ $name = rawurlencode($server['name']);
+ $query = http_build_query([
+ 'protocol' => 'udp',
+ 'auth' => $password,
+ 'insecure' => $server['insecure'],
+ 'peer' => $server['server_name'],
+ 'upmbps' => $server['up_mbps'],
+ 'downmbps' => $server['up_mbps']
+// 'obfsParam' => $server['server_key']
+ ]);
+ $uri = "hysteria://{$remote}:{$server['port']}?{$query}#{$name}";
+ $uri .= "\r\n";
+ return $uri;
+ }
+ public static function buildVless($uuid, $server)
+ {
+ $name = rawurlencode($server['name']);
+ $config = [];
+ $config['type'] = $server['network'];
+ $config['encryption'] = 'none';
+ $config['security'] = $server['tls'] ? "tls" : "none";
+ if ($server['tls']) {
+ $config['fp'] = 'firefox';
+ if (is_array($server['tags']) && in_array("VLESS", $server['tags']) && in_array("XTLS", $server['tags'])) {
+ $config['flow'] = "xtls-rprx-vision-udp443";
+ }
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $config['sni'] = $tlsSettings['serverName'];
+ }
+ }
+ if ((string)$server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $config['type'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $config['path'] = $tcpSettings['header']['request']['path'][0];
+ if (isset($tcpSettings['header']['headers']['Host'][0])) $config['host'] = $tcpSettings['header']['headers']['Host'][0];
+ }
+ if ((string)$server['network'] === 'ws') {
+ $wsSettings = $server['networkSettings'];
+ if (isset($wsSettings['path'])) $config['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
+ }
+ if ((string)$server['network'] === 'grpc') {
+ $grpcSettings = $server['networkSettings'];
+ if (isset($grpcSettings['serviceName'])) $config['serviceName'] = $grpcSettings['serviceName'];
+ $config['mode'] = 'multi';
+ }
+ $query = http_build_query($config);
+ $remote = filter_var($server['host'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '[' . $server['host'] . ']' : $server['host'];
+ $uri = "vless://{$uuid}@{$remote}:{$server['port']}?{$query}#{$name}";
+ $uri .= "\r\n";
+ return $uri;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/QuantumultX.php b/app/Http/Controllers/Client/Protocols/QuantumultX.php
new file mode 100644
index 0000000..61bab74
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/QuantumultX.php
@@ -0,0 +1,116 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+ $uri = '';
+ header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks') {
+ $uri .= self::buildShadowsocks($user['uuid'], $item);
+ }
+ if ($item['type'] === 'vmess') {
+ $uri .= self::buildVmess($user['uuid'], $item);
+ }
+ if ($item['type'] === 'trojan') {
+ $uri .= self::buildTrojan($user['uuid'], $item);
+ }
+ }
+ return base64_encode($uri);
+ }
+
+ public static function buildShadowsocks($password, $server)
+ {
+ $config = [
+ "shadowsocks={$server['host']}:{$server['port']}",
+ "method={$server['cipher']}",
+ "password={$password}",
+ 'fast-open=true',
+ 'udp-relay=true',
+ "tag={$server['name']}"
+ ];
+ $config = array_filter($config);
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $config = [
+ "vmess={$server['host']}:{$server['port']}",
+ 'method=chacha20-poly1305',
+ "password={$uuid}",
+ 'fast-open=true',
+ 'udp-relay=true',
+ "tag={$server['name']}"
+ ];
+
+ if ($server['tls']) {
+ if ($server['network'] === 'tcp')
+ array_push($config, 'obfs=over-tls');
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ array_push($config, 'tls-verification=' . ($tlsSettings['allowInsecure'] ? 'false' : 'true'));
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $host = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'ws') {
+ if ($server['tls'])
+ array_push($config, 'obfs=wss');
+ else
+ array_push($config, 'obfs=ws');
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ array_push($config, "obfs-uri={$wsSettings['path']}");
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']) && !isset($host))
+ $host = $wsSettings['headers']['Host'];
+ }
+ }
+ if (isset($host)) {
+ array_push($config, "obfs-host={$host}");
+ }
+
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $config = [
+ "trojan={$server['host']}:{$server['port']}",
+ "password={$password}",
+ 'over-tls=true',
+ $server['server_name'] ? "tls-host={$server['server_name']}" : "",
+ // Tips: allowInsecure=false = tls-verification=true
+ $server['allow_insecure'] ? 'tls-verification=false' : 'tls-verification=true',
+ 'fast-open=true',
+ 'udp-relay=true',
+ "tag={$server['name']}"
+ ];
+ $config = array_filter($config);
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/Shadowsocks.php b/app/Http/Controllers/Client/Protocols/Shadowsocks.php
new file mode 100644
index 0000000..1b472e6
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/Shadowsocks.php
@@ -0,0 +1,69 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+
+ $configs = [];
+ $subs = [];
+ $subs['servers'] = [];
+ $subs['bytes_used'] = '';
+ $subs['bytes_remaining'] = '';
+
+ $bytesUsed = $user['u'] + $user['d'];
+ $bytesRemaining = $user['transfer_enable'] - $bytesUsed;
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks') {
+ array_push($configs, self::SIP008($item, $user));
+ }
+ }
+
+ $subs['version'] = 1;
+ $subs['bytes_used'] = $bytesUsed;
+ $subs['bytes_remaining'] = $bytesRemaining;
+ $subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
+
+ return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);
+ }
+
+ public static function SIP008($server, $user)
+ {
+ $password = $user['uuid'];
+ if ($server['cipher'] === '2022-blake3-aes-128-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 16);
+ $userKey = Helper::uuidToBase64($password, 16);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ if ($server['cipher'] === '2022-blake3-aes-256-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 32);
+ $userKey = Helper::uuidToBase64($password, 32);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ $config = [
+ "id" => $server['id'],
+ "remarks" => $server['name'],
+ "server" => $server['host'],
+ "server_port" => $server['port'],
+ "password" => $password,
+ "method" => $server['cipher']
+ ];
+ return $config;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/Stash.php b/app/Http/Controllers/Client/Protocols/Stash.php
new file mode 100644
index 0000000..e6b46ba
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/Stash.php
@@ -0,0 +1,272 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+ $appName = config('v2board.app_name', 'V2Board');
+ header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
+ header('profile-update-interval: 24');
+ header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName));
+ $defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
+ $customConfig = base_path() . '/resources/rules/custom.clash.yaml';
+ if (\File::exists($customConfig)) {
+ $config = Yaml::parseFile($customConfig);
+ } else {
+ $config = Yaml::parseFile($defaultConfig);
+ }
+ $proxy = [];
+ $proxies = [];
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks') {
+ array_push($proxy, self::buildShadowsocks($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ if ($item['type'] === 'vmess') {
+ if (is_array($item['tags']) && in_array("VLESS", $item['tags'])) {
+ array_push($proxy, self::buildVless($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ } else {
+ array_push($proxy, self::buildVmess($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ }
+ if ($item['type'] === 'trojan') {
+ array_push($proxy, self::buildTrojan($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ if ($item['type'] === 'hysteria') {
+ array_push($proxy, self::buildHysteria($user['uuid'], $item));
+ array_push($proxies, $item['name']);
+ }
+ }
+
+ $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
+ foreach ($config['proxy-groups'] as $k => $v) {
+ if (!is_array($config['proxy-groups'][$k]['proxies'])) $config['proxy-groups'][$k]['proxies'] = [];
+ $isFilter = false;
+ foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
+ foreach ($proxies as $dst) {
+ if (!$this->isRegex($src)) continue;
+ $isFilter = true;
+ $config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src]));
+ if ($this->isMatch($src, $dst)) {
+ array_push($config['proxy-groups'][$k]['proxies'], $dst);
+ }
+ }
+ if ($isFilter) continue;
+ }
+ if ($isFilter) continue;
+ $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
+ }
+ $config['proxy-groups'] = array_filter($config['proxy-groups'], function($group) {
+ return $group['proxies'];
+ });
+ $config['proxy-groups'] = array_values($config['proxy-groups']);
+ // Force the current subscription domain to be a direct rule
+ $subsDomain = $_SERVER['HTTP_HOST'];
+ if ($subsDomain) {
+ array_unshift($config['rules'], "DOMAIN,{$subsDomain},DIRECT");
+ }
+
+ $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
+ $yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
+ return $yaml;
+ }
+
+ public static function buildShadowsocks($password, $server)
+ {
+ if ($server['cipher'] === '2022-blake3-aes-128-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 16);
+ $userKey = Helper::uuidToBase64($password, 16);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ if ($server['cipher'] === '2022-blake3-aes-256-gcm') {
+ $serverKey = Helper::getServerKey($server['created_at'], 32);
+ $userKey = Helper::uuidToBase64($password, 32);
+ $password = "{$serverKey}:{$userKey}";
+ }
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'ss';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['cipher'] = $server['cipher'];
+ $array['password'] = $password;
+ $array['udp'] = true;
+ return $array;
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'vmess';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['uuid'] = $uuid;
+ $array['alterId'] = 0;
+ $array['cipher'] = 'auto';
+ $array['udp'] = true;
+
+ if ($server['tls']) {
+ $array['tls'] = true;
+ $array['cipher'] = 'zero';
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $array['servername'] = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $array['network'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $array['http-opts']['path'] = $tcpSettings['header']['request']['path'][0];
+ }
+ if ($server['network'] === 'ws') {
+ $array['network'] = 'ws';
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ $array['ws-opts'] = [];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-opts']['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
+ }
+ $array['max-early-data'] = 4096;
+ $array['early-data-header-name'] = 'Sec-WebSocket-Protocol';
+ }
+ if ($server['network'] === 'grpc') {
+ $array['network'] = 'grpc';
+ if ($server['networkSettings']) {
+ $grpcSettings = $server['networkSettings'];
+ $array['grpc-opts'] = [];
+ if (isset($grpcSettings['serviceName'])) $array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
+ }
+ }
+
+ return $array;
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'trojan';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['password'] = $password;
+ $array['udp'] = true;
+ if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
+ if (!empty($server['allow_insecure'])) $array['skip-cert-verify'] = ($server['allow_insecure'] ? true : false);
+ return $array;
+ }
+
+ public static function buildHysteria($password, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'hysteria';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['auth_str'] = $password;
+// $array['obfs'] = $server['server_key'];
+ $array['protocol'] = 'udp';
+ $array['up'] = $server['up_mbps'];
+ $array['down'] = $server['down_mbps'];
+ if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
+ $array['skip-cert-verify'] = !empty($server['insecure']) ? true : false;
+ return $array;
+ }
+
+ public static function buildVless($uuid, $server)
+ {
+ $array = [];
+ $array['name'] = $server['name'];
+ $array['type'] = 'vless';
+ $array['server'] = $server['host'];
+ $array['port'] = $server['port'];
+ $array['uuid'] = $uuid;
+ $array['udp'] = true;
+
+ if ($server['tls']) {
+ $array['tls'] = true;
+ if (is_array($server['tags']) && in_array("VLESS", $server['tags']) && in_array("XTLS", $server['tags'])) {
+ $array['flow'] = "xtls-rprx-vision-udp443";
+ }
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ $array['servername'] = $tlsSettings['serverName'];
+ }
+ }
+ if ($server['network'] === 'tcp') {
+ $tcpSettings = $server['networkSettings'];
+ if (isset($tcpSettings['header']['type'])) $array['network'] = $tcpSettings['header']['type'];
+ if (isset($tcpSettings['header']['request']['path'][0])) $array['http-opts']['path'] = $tcpSettings['header']['request']['path'][0];
+ }
+ if ($server['network'] === 'ws') {
+ $array['network'] = 'ws';
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ $array['ws-opts'] = [];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-opts']['path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ $array['ws-path'] = "${wsSettings['path']}?ed=4096";
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ $array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
+ }
+ $array['max-early-data'] = 4096;
+ $array['early-data-header-name'] = 'Sec-WebSocket-Protocol';
+ }
+ if ($server['network'] === 'grpc') {
+ $array['network'] = 'grpc';
+ if ($server['networkSettings']) {
+ $grpcSettings = $server['networkSettings'];
+ $array['grpc-opts'] = [];
+ if (isset($grpcSettings['serviceName'])) $array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
+ }
+ }
+
+ return $array;
+ }
+
+ private function isMatch($exp, $str)
+ {
+ return @preg_match($exp, $str);
+ }
+
+ private function isRegex($exp)
+ {
+ return @preg_match($exp, null) !== false;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/Surfboard.php b/app/Http/Controllers/Client/Protocols/Surfboard.php
new file mode 100644
index 0000000..cdaf7b8
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/Surfboard.php
@@ -0,0 +1,161 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+
+ $appName = config('v2board.app_name', 'V2Board');
+ header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName).".conf");
+
+ $proxies = '';
+ $proxyGroup = '';
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks'
+ && in_array($item['cipher'], [
+ 'aes-128-gcm',
+ 'aes-192-gcm',
+ 'aes-256-gcm',
+ 'chacha20-ietf-poly1305'
+ ])
+ ) {
+ // [Proxy]
+ $proxies .= self::buildShadowsocks($user['uuid'], $item);
+ // [Proxy Group]
+ $proxyGroup .= $item['name'] . ', ';
+ }
+ if ($item['type'] === 'vmess') {
+ // [Proxy]
+ $proxies .= self::buildVmess($user['uuid'], $item);
+ // [Proxy Group]
+ $proxyGroup .= $item['name'] . ', ';
+ }
+ if ($item['type'] === 'trojan') {
+ // [Proxy]
+ $proxies .= self::buildTrojan($user['uuid'], $item);
+ // [Proxy Group]
+ $proxyGroup .= $item['name'] . ', ';
+ }
+ }
+
+ $defaultConfig = base_path() . '/resources/rules/default.surfboard.conf';
+ $customConfig = base_path() . '/resources/rules/custom.surfboard.conf';
+ if (\File::exists($customConfig)) {
+ $config = file_get_contents("$customConfig");
+ } else {
+ $config = file_get_contents("$defaultConfig");
+ }
+
+ // Subscription link
+ $subsURL = Helper::getSubscribeUrl("/api/v1/client/subscribe?token={$user['token']}");
+ $subsDomain = $_SERVER['HTTP_HOST'];
+
+ $config = str_replace('$subs_link', $subsURL, $config);
+ $config = str_replace('$subs_domain', $subsDomain, $config);
+ $config = str_replace('$proxies', $proxies, $config);
+ $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
+
+ $upload = round($user['u'] / (1024*1024*1024), 2);
+ $download = round($user['d'] / (1024*1024*1024), 2);
+ $useTraffic = $upload + $download;
+ $totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
+ $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
+ $subscribeInfo = "title={$appName}订阅信息, content=上传流量:{$upload}GB\\n下载流量:{$download}GB\\n剩余流量:{$useTraffic}GB\\n套餐流量:{$totalTraffic}GB\\n到期时间:{$expireDate}";
+ $config = str_replace('$subscribe_info', $subscribeInfo, $config);
+
+ return $config;
+ }
+
+
+ public static function buildShadowsocks($password, $server)
+ {
+ $config = [
+ "{$server['name']}=ss",
+ "{$server['host']}",
+ "{$server['port']}",
+ "encrypt-method={$server['cipher']}",
+ "password={$password}",
+ 'tfo=true',
+ 'udp-relay=true'
+ ];
+ $config = array_filter($config);
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $config = [
+ "{$server['name']}=vmess",
+ "{$server['host']}",
+ "{$server['port']}",
+ "username={$uuid}",
+ "vmess-aead=true",
+ 'tfo=true',
+ 'udp-relay=true'
+ ];
+
+ if ($server['tls']) {
+ array_push($config, 'tls=true');
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ array_push($config, "sni={$tlsSettings['serverName']}");
+ }
+ }
+ if ($server['network'] === 'ws') {
+ array_push($config, 'ws=true');
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ array_push($config, "ws-path={$wsSettings['path']}");
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ array_push($config, "ws-headers=Host:{$wsSettings['headers']['Host']}");
+ }
+ }
+
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $config = [
+ "{$server['name']}=trojan",
+ "{$server['host']}",
+ "{$server['port']}",
+ "password={$password}",
+ $server['server_name'] ? "sni={$server['server_name']}" : "",
+ 'tfo=true',
+ 'udp-relay=true'
+ ];
+ if (!empty($server['allow_insecure'])) {
+ array_push($config, $server['allow_insecure'] ? 'skip-cert-verify=true' : 'skip-cert-verify=false');
+ }
+ $config = array_filter($config);
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+}
diff --git a/app/Http/Controllers/Client/Protocols/Surge.php b/app/Http/Controllers/Client/Protocols/Surge.php
new file mode 100644
index 0000000..ca7cd1b
--- /dev/null
+++ b/app/Http/Controllers/Client/Protocols/Surge.php
@@ -0,0 +1,162 @@
+user = $user;
+ $this->servers = $servers;
+ }
+
+ public function handle()
+ {
+ $servers = $this->servers;
+ $user = $this->user;
+
+ $appName = config('v2board.app_name', 'V2Board');
+ header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName).".conf");
+
+ $proxies = '';
+ $proxyGroup = '';
+
+ foreach ($servers as $item) {
+ if ($item['type'] === 'shadowsocks'
+ && in_array($item['cipher'], [
+ 'aes-128-gcm',
+ 'aes-192-gcm',
+ 'aes-256-gcm',
+ 'chacha20-ietf-poly1305'
+ ])
+ ) {
+ // [Proxy]
+ $proxies .= self::buildShadowsocks($user['uuid'], $item);
+ // [Proxy Group]
+ $proxyGroup .= $item['name'] . ', ';
+ }
+ if ($item['type'] === 'vmess') {
+ // [Proxy]
+ $proxies .= self::buildVmess($user['uuid'], $item);
+ // [Proxy Group]
+ $proxyGroup .= $item['name'] . ', ';
+ }
+ if ($item['type'] === 'trojan') {
+ // [Proxy]
+ $proxies .= self::buildTrojan($user['uuid'], $item);
+ // [Proxy Group]
+ $proxyGroup .= $item['name'] . ', ';
+ }
+ }
+
+ $defaultConfig = base_path() . '/resources/rules/default.surge.conf';
+ $customConfig = base_path() . '/resources/rules/custom.surge.conf';
+ if (\File::exists($customConfig)) {
+ $config = file_get_contents("$customConfig");
+ } else {
+ $config = file_get_contents("$defaultConfig");
+ }
+
+ // Subscription link
+ $subsURL = Helper::getSubscribeUrl("/api/v1/client/subscribe?token={$user['token']}");
+ $subsDomain = $_SERVER['HTTP_HOST'];
+ $subsURL = 'https://' . $subsDomain . '/api/v1/client/subscribe?token=' . $user['token'];
+
+ $config = str_replace('$subs_link', $subsURL, $config);
+ $config = str_replace('$subs_domain', $subsDomain, $config);
+ $config = str_replace('$proxies', $proxies, $config);
+ $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
+
+ $upload = round($user['u'] / (1024*1024*1024), 2);
+ $download = round($user['d'] / (1024*1024*1024), 2);
+ $useTraffic = $upload + $download;
+ $totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
+ $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
+ $subscribeInfo = "title={$appName}订阅信息, content=上传流量:{$upload}GB\\n下载流量:{$download}GB\\n剩余流量:{$useTraffic}GB\\n套餐流量:{$totalTraffic}GB\\n到期时间:{$expireDate}";
+ $config = str_replace('$subscribe_info', $subscribeInfo, $config);
+
+ return $config;
+ }
+
+
+ public static function buildShadowsocks($password, $server)
+ {
+ $config = [
+ "{$server['name']}=ss",
+ "{$server['host']}",
+ "{$server['port']}",
+ "encrypt-method={$server['cipher']}",
+ "password={$password}",
+ 'tfo=true',
+ 'udp-relay=true'
+ ];
+ $config = array_filter($config);
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildVmess($uuid, $server)
+ {
+ $config = [
+ "{$server['name']}=vmess",
+ "{$server['host']}",
+ "{$server['port']}",
+ "username={$uuid}",
+ "vmess-aead=true",
+ 'tfo=true',
+ 'udp-relay=true'
+ ];
+
+ if ($server['tls']) {
+ array_push($config, 'tls=true');
+ if ($server['tlsSettings']) {
+ $tlsSettings = $server['tlsSettings'];
+ if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
+ array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
+ if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+ array_push($config, "sni={$tlsSettings['serverName']}");
+ }
+ }
+ if ($server['network'] === 'ws') {
+ array_push($config, 'ws=true');
+ if ($server['networkSettings']) {
+ $wsSettings = $server['networkSettings'];
+ if (isset($wsSettings['path']) && !empty($wsSettings['path']))
+ array_push($config, "ws-path={$wsSettings['path']}");
+ if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
+ array_push($config, "ws-headers=Host:{$wsSettings['headers']['Host']}");
+ }
+ }
+
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+
+ public static function buildTrojan($password, $server)
+ {
+ $config = [
+ "{$server['name']}=trojan",
+ "{$server['host']}",
+ "{$server['port']}",
+ "password={$password}",
+ $server['server_name'] ? "sni={$server['server_name']}" : "",
+ 'tfo=true',
+ 'udp-relay=true'
+ ];
+ if (!empty($server['allow_insecure'])) {
+ array_push($config, $server['allow_insecure'] ? 'skip-cert-verify=true' : 'skip-cert-verify=false');
+ }
+ $config = array_filter($config);
+ $uri = implode(',', $config);
+ $uri .= "\r\n";
+ return $uri;
+ }
+}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
new file mode 100755
index 0000000..af5cfb1
--- /dev/null
+++ b/app/Http/Controllers/Controller.php
@@ -0,0 +1,12 @@
+ [
+ 'tos_url' => config('v2board.tos_url'),
+ 'is_email_verify' => (int)config('v2board.email_verify', 0) ? 1 : 0,
+ 'is_invite_force' => (int)config('v2board.invite_force', 0) ? 1 : 0,
+ 'email_whitelist_suffix' => (int)config('v2board.email_whitelist_enable', 0)
+ ? $this->getEmailSuffix()
+ : 0,
+ 'is_recaptcha' => (int)config('v2board.recaptcha_enable', 0) ? 1 : 0,
+ 'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
+ 'app_description' => config('v2board.app_description'),
+ 'app_url' => config('v2board.app_url'),
+ 'logo' => config('v2board.logo'),
+ ]
+ ]);
+ }
+
+ private function getEmailSuffix()
+ {
+ $suffix = config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT);
+ if (!is_array($suffix)) {
+ return preg_split('/,/', $suffix);
+ }
+ return $suffix;
+ }
+}
diff --git a/app/Http/Controllers/Guest/PaymentController.php b/app/Http/Controllers/Guest/PaymentController.php
new file mode 100644
index 0000000..271e39e
--- /dev/null
+++ b/app/Http/Controllers/Guest/PaymentController.php
@@ -0,0 +1,49 @@
+notify($request->input());
+ if (!$verify) abort(500, 'verify error');
+ if (!$this->handle($verify['trade_no'], $verify['callback_no'])) {
+ abort(500, 'handle error');
+ }
+ die(isset($verify['custom_result']) ? $verify['custom_result'] : 'success');
+ } catch (\Exception $e) {
+ abort(500, 'fail');
+ }
+ }
+
+ private function handle($tradeNo, $callbackNo)
+ {
+ $order = Order::where('trade_no', $tradeNo)->first();
+ if (!$order) {
+ abort(500, 'order is not found');
+ }
+ if ($order->status !== 0) return true;
+ $orderService = new OrderService($order);
+ if (!$orderService->paid($callbackNo)) {
+ return false;
+ }
+ $telegramService = new TelegramService();
+ $message = sprintf(
+ "💰成功收款%s元\n———————————————\n订单号:%s",
+ $order->total_amount / 100,
+ $order->trade_no
+ );
+ $telegramService->sendMessageWithAdmin($message);
+ return true;
+ }
+}
diff --git a/app/Http/Controllers/Guest/PlanController.php b/app/Http/Controllers/Guest/PlanController.php
new file mode 100755
index 0000000..cadc6f9
--- /dev/null
+++ b/app/Http/Controllers/Guest/PlanController.php
@@ -0,0 +1,18 @@
+get();
+ return response([
+ 'data' => $plan
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Guest/TelegramController.php b/app/Http/Controllers/Guest/TelegramController.php
new file mode 100644
index 0000000..e0c6789
--- /dev/null
+++ b/app/Http/Controllers/Guest/TelegramController.php
@@ -0,0 +1,123 @@
+input('access_token') !== md5(config('v2board.telegram_bot_token'))) {
+ abort(401);
+ }
+
+ $this->telegramService = new TelegramService();
+ }
+
+ public function webhook(Request $request)
+ {
+ $this->formatMessage($request->input());
+ $this->formatChatJoinRequest($request->input());
+ $this->handle();
+ }
+
+ public function handle()
+ {
+ if (!$this->msg) return;
+ $msg = $this->msg;
+ $commandName = explode('@', $msg->command);
+
+ // To reduce request, only commands contains @ will get the bot name
+ if (count($commandName) == 2) {
+ $botName = $this->getBotName();
+ if ($commandName[1] === $botName){
+ $msg->command = $commandName[0];
+ }
+ }
+
+ try {
+ foreach (glob(base_path('app//Plugins//Telegram//Commands') . '/*.php') as $file) {
+ $command = basename($file, '.php');
+ $class = '\\App\\Plugins\\Telegram\\Commands\\' . $command;
+ if (!class_exists($class)) continue;
+ $instance = new $class();
+ if ($msg->message_type === 'message') {
+ if (!isset($instance->command)) continue;
+ if ($msg->command !== $instance->command) continue;
+ $instance->handle($msg);
+ return;
+ }
+ if ($msg->message_type === 'reply_message') {
+ if (!isset($instance->regex)) continue;
+ if (!preg_match($instance->regex, $msg->reply_text, $match)) continue;
+ $instance->handle($msg, $match);
+ return;
+ }
+ }
+ } catch (\Exception $e) {
+ $this->telegramService->sendMessage($msg->chat_id, $e->getMessage());
+ }
+ }
+
+ public function getBotName()
+ {
+ $response = $this->telegramService->getMe();
+ return $response->result->username;
+ }
+
+ private function formatMessage(array $data)
+ {
+ if (!isset($data['message'])) return;
+ if (!isset($data['message']['text'])) return;
+ $obj = new \StdClass();
+ $text = explode(' ', $data['message']['text']);
+ $obj->command = $text[0];
+ $obj->args = array_slice($text, 1);
+ $obj->chat_id = $data['message']['chat']['id'];
+ $obj->message_id = $data['message']['message_id'];
+ $obj->message_type = 'message';
+ $obj->text = $data['message']['text'];
+ $obj->is_private = $data['message']['chat']['type'] === 'private';
+ if (isset($data['message']['reply_to_message']['text'])) {
+ $obj->message_type = 'reply_message';
+ $obj->reply_text = $data['message']['reply_to_message']['text'];
+ }
+ $this->msg = $obj;
+ }
+
+ private function formatChatJoinRequest(array $data)
+ {
+ if (!isset($data['chat_join_request'])) return;
+ if (!isset($data['chat_join_request']['from']['id'])) return;
+ if (!isset($data['chat_join_request']['chat']['id'])) return;
+ $user = \App\Models\User::where('telegram_id', $data['chat_join_request']['from']['id'])
+ ->first();
+ if (!$user) {
+ $this->telegramService->declineChatJoinRequest(
+ $data['chat_join_request']['chat']['id'],
+ $data['chat_join_request']['from']['id']
+ );
+ return;
+ }
+ $userService = new \App\Services\UserService();
+ if (!$userService->isAvailable($user)) {
+ $this->telegramService->declineChatJoinRequest(
+ $data['chat_join_request']['chat']['id'],
+ $data['chat_join_request']['from']['id']
+ );
+ return;
+ }
+ $userService = new \App\Services\UserService();
+ $this->telegramService->approveChatJoinRequest(
+ $data['chat_join_request']['chat']['id'],
+ $data['chat_join_request']['from']['id']
+ );
+ }
+}
diff --git a/app/Http/Controllers/Passport/AuthController.php b/app/Http/Controllers/Passport/AuthController.php
new file mode 100644
index 0000000..1b47f1c
--- /dev/null
+++ b/app/Http/Controllers/Passport/AuthController.php
@@ -0,0 +1,307 @@
+validate([
+ 'email' => 'required|email:strict',
+ 'redirect' => 'nullable'
+ ]);
+
+ if (Cache::get(CacheKey::get('LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP', $params['email']))) {
+ abort(500, __('Sending frequently, please try again later'));
+ }
+
+ $user = User::where('email', $params['email'])->first();
+ if (!$user) {
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ $code = Helper::guid();
+ $key = CacheKey::get('TEMP_TOKEN', $code);
+ Cache::put($key, $user->id, 300);
+ Cache::put(CacheKey::get('LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP', $params['email']), time(), 60);
+
+
+ $redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
+ if (config('v2board.app_url')) {
+ $link = config('v2board.app_url') . $redirect;
+ } else {
+ $link = url($redirect);
+ }
+
+ SendEmailJob::dispatch([
+ 'email' => $user->email,
+ 'subject' => __('Login to :name', [
+ 'name' => config('v2board.app_name', 'V2Board')
+ ]),
+ 'template_name' => 'login',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'link' => $link,
+ 'url' => config('v2board.app_url')
+ ]
+ ]);
+
+ return response([
+ 'data' => $link
+ ]);
+
+ }
+
+ public function register(AuthRegister $request)
+ {
+ if ((int)config('v2board.register_limit_by_ip_enable', 0)) {
+ $registerCountByIP = Cache::get(CacheKey::get('REGISTER_IP_RATE_LIMIT', $request->ip())) ?? 0;
+ if ((int)$registerCountByIP >= (int)config('v2board.register_limit_count', 3)) {
+ abort(500, __('Register frequently, please try again after :minute minute', [
+ 'minute' => config('v2board.register_limit_expire', 60)
+ ]));
+ }
+ }
+ if ((int)config('v2board.recaptcha_enable', 0)) {
+ $recaptcha = new ReCaptcha(config('v2board.recaptcha_key'));
+ $recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
+ if (!$recaptchaResp->isSuccess()) {
+ abort(500, __('Invalid code is incorrect'));
+ }
+ }
+ if ((int)config('v2board.email_whitelist_enable', 0)) {
+ if (!Helper::emailSuffixVerify(
+ $request->input('email'),
+ config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT))
+ ) {
+ abort(500, __('Email suffix is not in the Whitelist'));
+ }
+ }
+ if ((int)config('v2board.email_gmail_limit_enable', 0)) {
+ $prefix = explode('@', $request->input('email'))[0];
+ if (strpos($prefix, '.') !== false || strpos($prefix, '+') !== false) {
+ abort(500, __('Gmail alias is not supported'));
+ }
+ }
+ if ((int)config('v2board.stop_register', 0)) {
+ abort(500, __('Registration has closed'));
+ }
+ if ((int)config('v2board.invite_force', 0)) {
+ if (empty($request->input('invite_code'))) {
+ abort(500, __('You must use the invitation code to register'));
+ }
+ }
+ if ((int)config('v2board.email_verify', 0)) {
+ if (empty($request->input('email_code'))) {
+ abort(500, __('Email verification code cannot be empty'));
+ }
+ if ((string)Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== (string)$request->input('email_code')) {
+ abort(500, __('Incorrect email verification code'));
+ }
+ }
+ $email = $request->input('email');
+ $password = $request->input('password');
+ $exist = User::where('email', $email)->first();
+ if ($exist) {
+ abort(500, __('Email already exists'));
+ }
+ $user = new User();
+ $user->email = $email;
+ $user->password = password_hash($password, PASSWORD_DEFAULT);
+ $user->uuid = Helper::guid(true);
+ $user->token = Helper::guid();
+ if ($request->input('invite_code')) {
+ $inviteCode = InviteCode::where('code', $request->input('invite_code'))
+ ->where('status', 0)
+ ->first();
+ if (!$inviteCode) {
+ if ((int)config('v2board.invite_force', 0)) {
+ abort(500, __('Invalid invitation code'));
+ }
+ } else {
+ $user->invite_user_id = $inviteCode->user_id ? $inviteCode->user_id : null;
+ if (!(int)config('v2board.invite_never_expire', 0)) {
+ $inviteCode->status = 1;
+ $inviteCode->save();
+ }
+ }
+ }
+
+ // try out
+ if ((int)config('v2board.try_out_plan_id', 0)) {
+ $plan = Plan::find(config('v2board.try_out_plan_id'));
+ if ($plan) {
+ $user->transfer_enable = $plan->transfer_enable * 1073741824;
+ $user->plan_id = $plan->id;
+ $user->group_id = $plan->group_id;
+ $user->expired_at = time() + (config('v2board.try_out_hour', 1) * 3600);
+ $user->speed_limit = $plan->speed_limit;
+ }
+ }
+
+ if (!$user->save()) {
+ abort(500, __('Register failed'));
+ }
+ if ((int)config('v2board.email_verify', 0)) {
+ Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
+ }
+
+ $user->last_login_at = time();
+ $user->save();
+
+ if ((int)config('v2board.register_limit_by_ip_enable', 0)) {
+ Cache::put(
+ CacheKey::get('REGISTER_IP_RATE_LIMIT', $request->ip()),
+ (int)$registerCountByIP + 1,
+ (int)config('v2board.register_limit_expire', 60) * 60
+ );
+ }
+
+ $authService = new AuthService($user);
+
+ return response()->json([
+ 'data' => $authService->generateAuthData($request)
+ ]);
+ }
+
+ public function login(AuthLogin $request)
+ {
+ $email = $request->input('email');
+ $password = $request->input('password');
+
+ if ((int)config('v2board.password_limit_enable', 1)) {
+ $passwordErrorCount = (int)Cache::get(CacheKey::get('PASSWORD_ERROR_LIMIT', $email), 0);
+ if ($passwordErrorCount >= (int)config('v2board.password_limit_count', 5)) {
+ abort(500, __('There are too many password errors, please try again after :minute minutes.', [
+ 'minute' => config('v2board.password_limit_expire', 60)
+ ]));
+ }
+ }
+
+ $user = User::where('email', $email)->first();
+ if (!$user) {
+ abort(500, __('Incorrect email or password'));
+ }
+ if (!Helper::multiPasswordVerify(
+ $user->password_algo,
+ $user->password_salt,
+ $password,
+ $user->password)
+ ) {
+ if ((int)config('v2board.password_limit_enable')) {
+ Cache::put(
+ CacheKey::get('PASSWORD_ERROR_LIMIT', $email),
+ (int)$passwordErrorCount + 1,
+ 60 * (int)config('v2board.password_limit_expire', 60)
+ );
+ }
+ abort(500, __('Incorrect email or password'));
+ }
+
+ if ($user->banned) {
+ abort(500, __('Your account has been suspended'));
+ }
+
+ $authService = new AuthService($user);
+ return response([
+ 'data' => $authService->generateAuthData($request)
+ ]);
+ }
+
+ public function token2Login(Request $request)
+ {
+ if ($request->input('token')) {
+ $redirect = '/#/login?verify=' . $request->input('token') . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
+ if (config('v2board.app_url')) {
+ $location = config('v2board.app_url') . $redirect;
+ } else {
+ $location = url($redirect);
+ }
+ return redirect()->to($location)->send();
+ }
+
+ if ($request->input('verify')) {
+ $key = CacheKey::get('TEMP_TOKEN', $request->input('verify'));
+ $userId = Cache::get($key);
+ if (!$userId) {
+ abort(500, __('Token error'));
+ }
+ $user = User::find($userId);
+ if (!$user) {
+ abort(500, __('The user does not '));
+ }
+ if ($user->banned) {
+ abort(500, __('Your account has been suspended'));
+ }
+ Cache::forget($key);
+ $authService = new AuthService($user);
+ return response([
+ 'data' => $authService->generateAuthData($request)
+ ]);
+ }
+ }
+
+ public function getQuickLoginUrl(Request $request)
+ {
+ $authorization = $request->input('auth_data') ?? $request->header('authorization');
+ if (!$authorization) abort(403, '未登录或登陆已过期');
+
+ $user = AuthService::decryptAuthData($authorization);
+ if (!$user) abort(403, '未登录或登陆已过期');
+
+ $code = Helper::guid();
+ $key = CacheKey::get('TEMP_TOKEN', $code);
+ Cache::put($key, $user['id'], 60);
+ $redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
+ if (config('v2board.app_url')) {
+ $url = config('v2board.app_url') . $redirect;
+ } else {
+ $url = url($redirect);
+ }
+ return response([
+ 'data' => $url
+ ]);
+ }
+
+ public function forget(AuthForget $request)
+ {
+ if ((string)Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== (string)$request->input('email_code')) {
+ abort(500, __('Incorrect email verification code'));
+ }
+ $user = User::where('email', $request->input('email'))->first();
+ if (!$user) {
+ abort(500, __('This email is not registered in the system'));
+ }
+ $user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
+ $user->password_algo = NULL;
+ $user->password_salt = NULL;
+ if (!$user->save()) {
+ abort(500, __('Reset failed'));
+ }
+ Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Passport/CommController.php b/app/Http/Controllers/Passport/CommController.php
new file mode 100644
index 0000000..9376601
--- /dev/null
+++ b/app/Http/Controllers/Passport/CommController.php
@@ -0,0 +1,82 @@
+ (int)config('v2board.email_verify', 0) ? 1 : 0
+ ]);
+ }
+
+ public function sendEmailVerify(CommSendEmailVerify $request)
+ {
+ if ((int)config('v2board.recaptcha_enable', 0)) {
+ $recaptcha = new ReCaptcha(config('v2board.recaptcha_key'));
+ $recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
+ if (!$recaptchaResp->isSuccess()) {
+ abort(500, __('Invalid code is incorrect'));
+ }
+ }
+ $email = $request->input('email');
+ if (Cache::get(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email))) {
+ abort(500, __('Email verification code has been sent, please request again later'));
+ }
+ $code = rand(100000, 999999);
+ $subject = config('v2board.app_name', 'V2Board') . __('Email verification code');
+
+ SendEmailJob::dispatch([
+ 'email' => $email,
+ 'subject' => $subject,
+ 'template_name' => 'verify',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'code' => $code,
+ 'url' => config('v2board.app_url')
+ ]
+ ]);
+
+ Cache::put(CacheKey::get('EMAIL_VERIFY_CODE', $email), $code, 300);
+ Cache::put(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email), time(), 60);
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function pv(Request $request)
+ {
+ $inviteCode = InviteCode::where('code', $request->input('invite_code'))->first();
+ if ($inviteCode) {
+ $inviteCode->pv = $inviteCode->pv + 1;
+ $inviteCode->save();
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ private function getEmailSuffix()
+ {
+ $suffix = config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT);
+ if (!is_array($suffix)) {
+ return preg_split('/,/', $suffix);
+ }
+ return $suffix;
+ }
+}
diff --git a/app/Http/Controllers/Server/UniProxyController.php b/app/Http/Controllers/Server/UniProxyController.php
new file mode 100644
index 0000000..c9d8446
--- /dev/null
+++ b/app/Http/Controllers/Server/UniProxyController.php
@@ -0,0 +1,139 @@
+input('token');
+ if (empty($token)) {
+ abort(500, 'token is null');
+ }
+ if ($token !== config('v2board.server_token')) {
+ abort(500, 'token is error');
+ }
+ $this->nodeType = $request->input('node_type');
+ if ($this->nodeType === 'v2ray') $this->nodeType = 'vmess';
+ $this->nodeId = $request->input('node_id');
+ $this->serverService = new ServerService();
+ $this->nodeInfo = $this->serverService->getServer($this->nodeId, $this->nodeType);
+ if (!$this->nodeInfo) abort(500, 'server is not exist');
+ }
+
+ // 后端获取用户
+ public function user(Request $request)
+ {
+ ini_set('memory_limit', -1);
+ Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_LAST_CHECK_AT', $this->nodeInfo->id), time(), 3600);
+ $users = $this->serverService->getAvailableUsers($this->nodeInfo->group_id);
+ $users = $users->toArray();
+
+ $response['users'] = $users;
+
+ $eTag = sha1(json_encode($response));
+ if (strpos($request->header('If-None-Match'), $eTag) !== false ) {
+ abort(304);
+ }
+
+ return response($response)->header('ETag', "\"{$eTag}\"");
+ }
+
+ // 后端提交数据
+ public function push(Request $request)
+ {
+ $data = file_get_contents('php://input');
+ $data = json_decode($data, true);
+ Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_ONLINE_USER', $this->nodeInfo->id), count($data), 3600);
+ Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_LAST_PUSH_AT', $this->nodeInfo->id), time(), 3600);
+ $userService = new UserService();
+ $userService->trafficFetch($this->nodeInfo->toArray(), $this->nodeType, $data);
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ // 后端获取配置
+ public function config(Request $request)
+ {
+ switch ($this->nodeType) {
+ case 'shadowsocks':
+ $response = [
+ 'server_port' => $this->nodeInfo->server_port,
+ 'cipher' => $this->nodeInfo->cipher,
+ 'obfs' => $this->nodeInfo->obfs,
+ 'obfs_settings' => $this->nodeInfo->obfs_settings
+ ];
+
+ if ($this->nodeInfo->cipher === '2022-blake3-aes-128-gcm') {
+ $response['server_key'] = Helper::getServerKey($this->nodeInfo->created_at, 16);
+ }
+ if ($this->nodeInfo->cipher === '2022-blake3-aes-256-gcm') {
+ $response['server_key'] = Helper::getServerKey($this->nodeInfo->created_at, 32);
+ }
+ break;
+ case 'vmess':
+ $response = [
+ 'server_port' => $this->nodeInfo->server_port,
+ 'network' => $this->nodeInfo->network,
+ 'networkSettings' => $this->nodeInfo->networkSettings,
+ 'tls' => $this->nodeInfo->tls
+ ];
+
+ if (is_array($this->nodeInfo->tags) && in_array("VLESS", $this->nodeInfo->tags)) {
+ $response['vless'] = true;
+ } else {
+ $response['vless'] = false;
+ }
+ break;
+ case 'trojan':
+ $response = [
+ 'host' => $this->nodeInfo->host,
+ 'server_port' => $this->nodeInfo->server_port,
+ 'server_name' => $this->nodeInfo->server_name,
+ ];
+ break;
+ case 'hysteria':
+ $response = [
+ 'host' => $this->nodeInfo->host,
+ 'server_port' => $this->nodeInfo->server_port,
+ 'server_name' => $this->nodeInfo->server_name,
+ 'up_mbps' => $this->nodeInfo->up_mbps,
+ 'down_mbps' => $this->nodeInfo->down_mbps,
+ 'obfs' => Helper::getServerKey($this->nodeInfo->created_at, 16)
+ ];
+ break;
+ }
+ $response['base_config'] = [
+ 'push_interval' => (int)config('v2board.server_push_interval', 60),
+ 'pull_interval' => (int)config('v2board.server_pull_interval', 60)
+ ];
+ if ($this->nodeInfo['route_id']) {
+ $response['routes'] = $this->serverService->getRoutes($this->nodeInfo['route_id']);
+ }
+ $eTag = sha1(json_encode($response));
+ if (strpos($request->header('If-None-Match'), $eTag) !== false ) {
+ abort(304);
+ }
+
+ return response($response)->header('ETag', "\"{$eTag}\"");
+ }
+}
diff --git a/app/Http/Controllers/Staff/NoticeController.php b/app/Http/Controllers/Staff/NoticeController.php
new file mode 100644
index 0000000..e2e6d5e
--- /dev/null
+++ b/app/Http/Controllers/Staff/NoticeController.php
@@ -0,0 +1,59 @@
+ Notice::orderBy('id', 'DESC')->get()
+ ]);
+ }
+
+ public function save(NoticeSave $request)
+ {
+ $data = $request->only([
+ 'title',
+ 'content',
+ 'img_url'
+ ]);
+ if (!$request->input('id')) {
+ if (!Notice::create($data)) {
+ abort(500, '保存失败');
+ }
+ } else {
+ try {
+ Notice::find($request->input('id'))->update($data);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function drop(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ $notice = Notice::find($request->input('id'));
+ if (!$notice) {
+ abort(500, '公告不存在');
+ }
+ if (!$notice->delete()) {
+ abort(500, '删除失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Staff/PlanController.php b/app/Http/Controllers/Staff/PlanController.php
new file mode 100755
index 0000000..7e48f1f
--- /dev/null
+++ b/app/Http/Controllers/Staff/PlanController.php
@@ -0,0 +1,41 @@
+where('plan_id', '!=', NULL)
+ ->where(function ($query) {
+ $query->where('expired_at', '>=', time())
+ ->orWhere('expired_at', NULL);
+ })
+ ->groupBy("plan_id")
+ ->get();
+ $plans = Plan::orderBy('sort', 'ASC')->get();
+ foreach ($plans as $k => $v) {
+ $plans[$k]->count = 0;
+ foreach ($counts as $kk => $vv) {
+ if ($plans[$k]->id === $counts[$kk]->plan_id) $plans[$k]->count = $counts[$kk]->count;
+ }
+ }
+ return response([
+ 'data' => $plans
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Staff/TicketController.php b/app/Http/Controllers/Staff/TicketController.php
new file mode 100644
index 0000000..592c2a4
--- /dev/null
+++ b/app/Http/Controllers/Staff/TicketController.php
@@ -0,0 +1,85 @@
+input('id')) {
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->first();
+ if (!$ticket) {
+ abort(500, '工单不存在');
+ }
+ $ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
+ for ($i = 0; $i < count($ticket['message']); $i++) {
+ if ($ticket['message'][$i]['user_id'] !== $ticket->user_id) {
+ $ticket['message'][$i]['is_me'] = true;
+ } else {
+ $ticket['message'][$i]['is_me'] = false;
+ }
+ }
+ return response([
+ 'data' => $ticket
+ ]);
+ }
+ $current = $request->input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
+ $model = Ticket::orderBy('created_at', 'DESC');
+ if ($request->input('status') !== NULL) {
+ $model->where('status', $request->input('status'));
+ }
+ $total = $model->count();
+ $res = $model->forPage($current, $pageSize)
+ ->get();
+ return response([
+ 'data' => $res,
+ 'total' => $total
+ ]);
+ }
+
+ public function reply(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ if (empty($request->input('message'))) {
+ abort(500, '消息不能为空');
+ }
+ $ticketService = new TicketService();
+ $ticketService->replyByAdmin(
+ $request->input('id'),
+ $request->input('message'),
+ $request->user['id']
+ );
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function close(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, '参数错误');
+ }
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->first();
+ if (!$ticket) {
+ abort(500, '工单不存在');
+ }
+ $ticket->status = 1;
+ if (!$ticket->save()) {
+ abort(500, '关闭失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Staff/UserController.php b/app/Http/Controllers/Staff/UserController.php
new file mode 100644
index 0000000..bf16f1d
--- /dev/null
+++ b/app/Http/Controllers/Staff/UserController.php
@@ -0,0 +1,107 @@
+input('id'))) {
+ abort(500, '参数错误');
+ }
+ $user = User::where('is_admin', 0)
+ ->where('id', $request->input('id'))
+ ->where('is_staff', 0)
+ ->first();
+ if (!$user) abort(500, '用户不存在');
+ return response([
+ 'data' => $user
+ ]);
+ }
+
+ public function update(UserUpdate $request)
+ {
+ $params = $request->validated();
+ $user = User::find($request->input('id'));
+ if (!$user) {
+ abort(500, '用户不存在');
+ }
+ if (User::where('email', $params['email'])->first() && $user->email !== $params['email']) {
+ abort(500, '邮箱已被使用');
+ }
+ if (isset($params['password'])) {
+ $params['password'] = password_hash($params['password'], PASSWORD_DEFAULT);
+ $params['password_algo'] = NULL;
+ } else {
+ unset($params['password']);
+ }
+ if (isset($params['plan_id'])) {
+ $plan = Plan::find($params['plan_id']);
+ if (!$plan) {
+ abort(500, '订阅计划不存在');
+ }
+ $params['group_id'] = $plan->group_id;
+ }
+
+ try {
+ $user->update($params);
+ } catch (\Exception $e) {
+ abort(500, '保存失败');
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function sendMail(UserSendMail $request)
+ {
+ $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
+ $sort = $request->input('sort') ? $request->input('sort') : 'created_at';
+ $builder = User::orderBy($sort, $sortType);
+ $this->filter($request, $builder);
+ $users = $builder->get();
+ foreach ($users as $user) {
+ SendEmailJob::dispatch([
+ 'email' => $user->email,
+ 'subject' => $request->input('subject'),
+ 'template_name' => 'notify',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'url' => config('v2board.app_url'),
+ 'content' => $request->input('content')
+ ]
+ ]);
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function ban(Request $request)
+ {
+ $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
+ $sort = $request->input('sort') ? $request->input('sort') : 'created_at';
+ $builder = User::orderBy($sort, $sortType);
+ $this->filter($request, $builder);
+ try {
+ $builder->update([
+ 'banned' => 1
+ ]);
+ } catch (\Exception $e) {
+ abort(500, '处理失败');
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/CommController.php b/app/Http/Controllers/User/CommController.php
new file mode 100644
index 0000000..49a44ca
--- /dev/null
+++ b/app/Http/Controllers/User/CommController.php
@@ -0,0 +1,41 @@
+ [
+ 'is_telegram' => (int)config('v2board.telegram_bot_enable', 0),
+ 'telegram_discuss_link' => config('v2board.telegram_discuss_link'),
+ 'stripe_pk' => config('v2board.stripe_pk_live'),
+ 'withdraw_methods' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
+ 'withdraw_close' => (int)config('v2board.withdraw_close_enable', 0),
+ 'currency' => config('v2board.currency', 'CNY'),
+ 'currency_symbol' => config('v2board.currency_symbol', '¥'),
+ 'commission_distribution_enable' => (int)config('v2board.commission_distribution_enable', 0),
+ 'commission_distribution_l1' => config('v2board.commission_distribution_l1'),
+ 'commission_distribution_l2' => config('v2board.commission_distribution_l2'),
+ 'commission_distribution_l3' => config('v2board.commission_distribution_l3')
+ ]
+ ]);
+ }
+
+ public function getStripePublicKey(Request $request)
+ {
+ $payment = Payment::where('id', $request->input('id'))
+ ->where('payment', 'StripeCredit')
+ ->first();
+ if (!$payment) abort(500, 'payment is not found');
+ return response([
+ 'data' => $payment->config['stripe_pk_live']
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/CouponController.php b/app/Http/Controllers/User/CouponController.php
new file mode 100644
index 0000000..52e80ca
--- /dev/null
+++ b/app/Http/Controllers/User/CouponController.php
@@ -0,0 +1,25 @@
+input('code'))) {
+ abort(500, __('Coupon cannot be empty'));
+ }
+ $couponService = new CouponService($request->input('code'));
+ $couponService->setPlanId($request->input('plan_id'));
+ $couponService->setUserId($request->user['id']);
+ $couponService->check();
+ return response([
+ 'data' => $couponService->getCoupon()
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/InviteController.php b/app/Http/Controllers/User/InviteController.php
new file mode 100644
index 0000000..9d5abc7
--- /dev/null
+++ b/app/Http/Controllers/User/InviteController.php
@@ -0,0 +1,88 @@
+user['id'])->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) {
+ abort(500, __('The maximum number of creations has been reached'));
+ }
+ $inviteCode = new InviteCode();
+ $inviteCode->user_id = $request->user['id'];
+ $inviteCode->code = Helper::randomChar(8);
+ return response([
+ 'data' => $inviteCode->save()
+ ]);
+ }
+
+ public function details(Request $request)
+ {
+ $current = $request->input('current') ? $request->input('current') : 1;
+ $pageSize = $request->input('page_size') >= 10 ? $request->input('page_size') : 10;
+ $builder = CommissionLog::where('invite_user_id', $request->user['id'])
+ ->where('get_amount', '>', 0)
+ ->select([
+ 'id',
+ 'trade_no',
+ 'order_amount',
+ 'get_amount',
+ 'created_at'
+ ])
+ ->orderBy('created_at', 'DESC');
+ $total = $builder->count();
+ $details = $builder->forPage($current, $pageSize)
+ ->get();
+ return response([
+ 'data' => $details,
+ 'total' => $total
+ ]);
+ }
+
+ public function fetch(Request $request)
+ {
+ $codes = InviteCode::where('user_id', $request->user['id'])
+ ->where('status', 0)
+ ->get();
+ $commission_rate = config('v2board.invite_commission', 10);
+ $user = User::find($request->user['id']);
+ if ($user->commission_rate) {
+ $commission_rate = $user->commission_rate;
+ }
+ $uncheck_commission_balance = (int)Order::where('status', 3)
+ ->where('commission_status', 0)
+ ->where('invite_user_id', $request->user['id'])
+ ->sum('commission_balance');
+ if (config('v2board.commission_distribution_enable', 0)) {
+ $uncheck_commission_balance = $uncheck_commission_balance * (config('v2board.commission_distribution_l1') / 100);
+ }
+ $stat = [
+ //已注册用户数
+ (int)User::where('invite_user_id', $request->user['id'])->count(),
+ //有效的佣金
+ (int)CommissionLog::where('invite_user_id', $request->user['id'])
+ ->sum('get_amount'),
+ //确认中的佣金
+ $uncheck_commission_balance,
+ //佣金比例
+ (int)$commission_rate,
+ //可用佣金
+ (int)$user->commission_balance
+ ];
+ return response([
+ 'data' => [
+ 'codes' => $codes,
+ 'stat' => $stat
+ ]
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/KnowledgeController.php b/app/Http/Controllers/User/KnowledgeController.php
new file mode 100644
index 0000000..51c7673
--- /dev/null
+++ b/app/Http/Controllers/User/KnowledgeController.php
@@ -0,0 +1,73 @@
+input('id')) {
+ $knowledge = Knowledge::where('id', $request->input('id'))
+ ->where('show', 1)
+ ->first()
+ ->toArray();
+ if (!$knowledge) abort(500, __('Article does not exist'));
+ $user = User::find($request->user['id']);
+ $userService = new UserService();
+ if (!$userService->isAvailable($user)) {
+ $this->formatAccessData($knowledge['body']);
+ }
+ $subscribeUrl = Helper::getSubscribeUrl("/api/v1/client/subscribe?token={$user['token']}");
+ $knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
+ $knowledge['body'] = str_replace('{{subscribeUrl}}', $subscribeUrl, $knowledge['body']);
+ $knowledge['body'] = str_replace('{{urlEncodeSubscribeUrl}}', urlencode($subscribeUrl), $knowledge['body']);
+ $knowledge['body'] = str_replace(
+ '{{safeBase64SubscribeUrl}}',
+ str_replace(
+ array('+', '/', '='),
+ array('-', '_', ''),
+ base64_encode($subscribeUrl)
+ ),
+ $knowledge['body']
+ );
+ return response([
+ 'data' => $knowledge
+ ]);
+ }
+ $builder = Knowledge::select(['id', 'category', 'title', 'updated_at'])
+ ->where('language', $request->input('language'))
+ ->where('show', 1)
+ ->orderBy('sort', 'ASC');
+ $keyword = $request->input('keyword');
+ if ($keyword) {
+ $builder = $builder->where(function ($query) use ($keyword) {
+ $query->where('title', 'LIKE', "%{$keyword}%")
+ ->orWhere('body', 'LIKE', "%{$keyword}%");
+ });
+ }
+
+ $knowledges = $builder->get()
+ ->groupBy('category');
+ return response([
+ 'data' => $knowledges
+ ]);
+ }
+
+ private function formatAccessData(&$body)
+ {
+ function getBetween($input, $start, $end){$substr = substr($input, strlen($start)+strpos($input, $start),(strlen($input) - strpos($input, $end))*(-1));return $start . $substr . $end;}
+ while (strpos($body, '') !== false) {
+ $accessData = getBetween($body, '', '');
+ if ($accessData) {
+ $body = str_replace($accessData, '
'. __('You must have a valid subscription to view content in this area') .'
', $body);
+ }
+ }
+ }
+}
diff --git a/app/Http/Controllers/User/NoticeController.php b/app/Http/Controllers/User/NoticeController.php
new file mode 100644
index 0000000..c94f3ae
--- /dev/null
+++ b/app/Http/Controllers/User/NoticeController.php
@@ -0,0 +1,26 @@
+input('current') ? $request->input('current') : 1;
+ $pageSize = 5;
+ $model = Notice::orderBy('created_at', 'DESC')
+ ->where('show', 1);
+ $total = $model->count();
+ $res = $model->forPage($current, $pageSize)
+ ->get();
+ return response([
+ 'data' => $res,
+ 'total' => $total
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/OrderController.php b/app/Http/Controllers/User/OrderController.php
new file mode 100755
index 0000000..925824c
--- /dev/null
+++ b/app/Http/Controllers/User/OrderController.php
@@ -0,0 +1,267 @@
+user['id'])
+ ->orderBy('created_at', 'DESC');
+ if ($request->input('status') !== null) {
+ $model->where('status', $request->input('status'));
+ }
+ $order = $model->get();
+ $plan = Plan::get();
+ for ($i = 0; $i < count($order); $i++) {
+ for ($x = 0; $x < count($plan); $x++) {
+ if ($order[$i]['plan_id'] === $plan[$x]['id']) {
+ $order[$i]['plan'] = $plan[$x];
+ }
+ }
+ }
+ return response([
+ 'data' => $order->makeHidden(['id', 'user_id'])
+ ]);
+ }
+
+ public function detail(Request $request)
+ {
+ $order = Order::where('user_id', $request->user['id'])
+ ->where('trade_no', $request->input('trade_no'))
+ ->first();
+ if (!$order) {
+ abort(500, __('Order does not exist or has been paid'));
+ }
+ $order['plan'] = Plan::find($order->plan_id);
+ $order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
+ if (!$order['plan']) {
+ abort(500, __('Subscription plan does not exist'));
+ }
+ if ($order->surplus_order_ids) {
+ $order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
+ }
+ return response([
+ 'data' => $order
+ ]);
+ }
+
+ public function save(OrderSave $request)
+ {
+ $userService = new UserService();
+ if ($userService->isNotCompleteOrderByUserId($request->user['id'])) {
+ abort(500, __('You have an unpaid or pending order, please try again later or cancel it'));
+ }
+
+ $planService = new PlanService($request->input('plan_id'));
+
+ $plan = $planService->plan;
+ $user = User::find($request->user['id']);
+
+ if (!$plan) {
+ abort(500, __('Subscription plan does not exist'));
+ }
+
+ if ($user->plan_id !== $plan->id && !$planService->haveCapacity() && $request->input('period') !== 'reset_price') {
+ abort(500, __('Current product is sold out'));
+ }
+
+ if ($plan[$request->input('period')] === NULL) {
+ abort(500, __('This payment period cannot be purchased, please choose another period'));
+ }
+
+ if ($request->input('period') === 'reset_price') {
+ if (!$userService->isAvailable($user) || $plan->id !== $user->plan_id) {
+ abort(500, __('Subscription has expired or no active subscription, unable to purchase Data Reset Package'));
+ }
+ }
+
+ if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
+ if ($request->input('period') !== 'reset_price') {
+ abort(500, __('This subscription has been sold out, please choose another subscription'));
+ }
+ }
+
+ if (!$plan->renew && $user->plan_id == $plan->id && $request->input('period') !== 'reset_price') {
+ abort(500, __('This subscription cannot be renewed, please change to another subscription'));
+ }
+
+
+ if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
+ abort(500, __('This subscription has expired, please change to another subscription'));
+ }
+
+ DB::beginTransaction();
+ $order = new Order();
+ $orderService = new OrderService($order);
+ $order->user_id = $request->user['id'];
+ $order->plan_id = $plan->id;
+ $order->period = $request->input('period');
+ $order->trade_no = Helper::generateOrderNo();
+ $order->total_amount = $plan[$request->input('period')];
+
+ if ($request->input('coupon_code')) {
+ $couponService = new CouponService($request->input('coupon_code'));
+ if (!$couponService->use($order)) {
+ DB::rollBack();
+ abort(500, __('Coupon failed'));
+ }
+ $order->coupon_id = $couponService->getId();
+ }
+
+ $orderService->setVipDiscount($user);
+ $orderService->setOrderType($user);
+ $orderService->setInvite($user);
+
+ if ($user->balance && $order->total_amount > 0) {
+ $remainingBalance = $user->balance - $order->total_amount;
+ $userService = new UserService();
+ if ($remainingBalance > 0) {
+ if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
+ DB::rollBack();
+ abort(500, __('Insufficient balance'));
+ }
+ $order->balance_amount = $order->total_amount;
+ $order->total_amount = 0;
+ } else {
+ if (!$userService->addBalance($order->user_id, - $user->balance)) {
+ DB::rollBack();
+ abort(500, __('Insufficient balance'));
+ }
+ $order->balance_amount = $user->balance;
+ $order->total_amount = $order->total_amount - $user->balance;
+ }
+ }
+
+ if (!$order->save()) {
+ DB::rollback();
+ abort(500, __('Failed to create order'));
+ }
+
+ DB::commit();
+
+ return response([
+ 'data' => $order->trade_no
+ ]);
+ }
+
+ public function checkout(Request $request)
+ {
+ $tradeNo = $request->input('trade_no');
+ $method = $request->input('method');
+ $order = Order::where('trade_no', $tradeNo)
+ ->where('user_id', $request->user['id'])
+ ->where('status', 0)
+ ->first();
+ if (!$order) {
+ abort(500, __('Order does not exist or has been paid'));
+ }
+ // free process
+ if ($order->total_amount <= 0) {
+ $orderService = new OrderService($order);
+ if (!$orderService->paid($order->trade_no)) abort(500, '');
+ return response([
+ 'type' => -1,
+ 'data' => true
+ ]);
+ }
+ $payment = Payment::find($method);
+ if (!$payment || $payment->enable !== 1) abort(500, __('Payment method is not available'));
+ $paymentService = new PaymentService($payment->payment, $payment->id);
+ $order->handling_amount = NULL;
+ if ($payment->handling_fee_fixed || $payment->handling_fee_percent) {
+ $order->handling_amount = round(($order->total_amount * ($payment->handling_fee_percent / 100)) + $payment->handling_fee_fixed);
+ }
+ $order->payment_id = $method;
+ if (!$order->save()) abort(500, __('Request failed, please try again later'));
+ $result = $paymentService->pay([
+ 'trade_no' => $tradeNo,
+ 'total_amount' => isset($order->handling_amount) ? ($order->total_amount + $order->handling_amount) : $order->total_amount,
+ 'user_id' => $order->user_id,
+ 'stripe_token' => $request->input('token')
+ ]);
+ return response([
+ 'type' => $result['type'],
+ 'data' => $result['data']
+ ]);
+ }
+
+ public function check(Request $request)
+ {
+ $tradeNo = $request->input('trade_no');
+ $order = Order::where('trade_no', $tradeNo)
+ ->where('user_id', $request->user['id'])
+ ->first();
+ if (!$order) {
+ abort(500, __('Order does not exist'));
+ }
+ return response([
+ 'data' => $order->status
+ ]);
+ }
+
+ public function getPaymentMethod()
+ {
+ $methods = Payment::select([
+ 'id',
+ 'name',
+ 'payment',
+ 'icon',
+ 'handling_fee_fixed',
+ 'handling_fee_percent'
+ ])
+ ->where('enable', 1)
+ ->orderBy('sort', 'ASC')
+ ->get();
+
+ return response([
+ 'data' => $methods
+ ]);
+ }
+
+ public function cancel(Request $request)
+ {
+ if (empty($request->input('trade_no'))) {
+ abort(500, __('Invalid parameter'));
+ }
+ $order = Order::where('trade_no', $request->input('trade_no'))
+ ->where('user_id', $request->user['id'])
+ ->first();
+ if (!$order) {
+ abort(500, __('Order does not exist'));
+ }
+ if ($order->status !== 0) {
+ abort(500, __('You can only cancel pending orders'));
+ }
+ $orderService = new OrderService($order);
+ if (!$orderService->cancel()) {
+ abort(500, __('Cancel failed'));
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/PlanController.php b/app/Http/Controllers/User/PlanController.php
new file mode 100755
index 0000000..6509d7d
--- /dev/null
+++ b/app/Http/Controllers/User/PlanController.php
@@ -0,0 +1,43 @@
+user['id']);
+ if ($request->input('id')) {
+ $plan = Plan::where('id', $request->input('id'))->first();
+ if (!$plan) {
+ abort(500, __('Subscription plan does not exist'));
+ }
+ if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
+ abort(500, __('Subscription plan does not exist'));
+ }
+ return response([
+ 'data' => $plan
+ ]);
+ }
+
+ $counts = PlanService::countActiveUsers();
+ $plans = Plan::where('show', 1)
+ ->orderBy('sort', 'ASC')
+ ->get();
+ foreach ($plans as $k => $v) {
+ if ($plans[$k]->capacity_limit === NULL) continue;
+ if (!isset($counts[$plans[$k]->id])) continue;
+ $plans[$k]->capacity_limit = $plans[$k]->capacity_limit - $counts[$plans[$k]->id]->count;
+ }
+ return response([
+ 'data' => $plans
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/ServerController.php b/app/Http/Controllers/User/ServerController.php
new file mode 100644
index 0000000..4931383
--- /dev/null
+++ b/app/Http/Controllers/User/ServerController.php
@@ -0,0 +1,38 @@
+user['id']);
+ $servers = [];
+ $userService = new UserService();
+ if ($userService->isAvailable($user)) {
+ $serverService = new ServerService();
+ $servers = $serverService->getAvailableServers($user);
+ }
+ $eTag = sha1(json_encode(array_column($servers, 'cache_key')));
+ if (strpos($request->header('If-None-Match'), $eTag) !== false ) {
+ abort(304);
+ }
+
+ return response([
+ 'data' => $servers
+ ])->header('ETag', "\"{$eTag}\"");
+ }
+}
diff --git a/app/Http/Controllers/User/StatController.php b/app/Http/Controllers/User/StatController.php
new file mode 100644
index 0000000..a842452
--- /dev/null
+++ b/app/Http/Controllers/User/StatController.php
@@ -0,0 +1,28 @@
+where('user_id', $request->user['id'])
+ ->where('record_at', '>=', strtotime(date('Y-m-1')))
+ ->orderBy('record_at', 'DESC');
+ return response([
+ 'data' => $builder->get()
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/User/TelegramController.php b/app/Http/Controllers/User/TelegramController.php
new file mode 100644
index 0000000..f7bba9e
--- /dev/null
+++ b/app/Http/Controllers/User/TelegramController.php
@@ -0,0 +1,27 @@
+getMe();
+ return response([
+ 'data' => [
+ 'username' => $response->result->username
+ ]
+ ]);
+ }
+
+ public function unbind(Request $request)
+ {
+ $user = User::where('user_id', $request->user['id'])->first();
+ }
+}
diff --git a/app/Http/Controllers/User/TicketController.php b/app/Http/Controllers/User/TicketController.php
new file mode 100644
index 0000000..14373b4
--- /dev/null
+++ b/app/Http/Controllers/User/TicketController.php
@@ -0,0 +1,198 @@
+input('id')) {
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->where('user_id', $request->user['id'])
+ ->first();
+ if (!$ticket) {
+ abort(500, __('Ticket does not exist'));
+ }
+ $ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
+ for ($i = 0; $i < count($ticket['message']); $i++) {
+ if ($ticket['message'][$i]['user_id'] == $ticket->user_id) {
+ $ticket['message'][$i]['is_me'] = true;
+ } else {
+ $ticket['message'][$i]['is_me'] = false;
+ }
+ }
+ return response([
+ 'data' => $ticket
+ ]);
+ }
+ $ticket = Ticket::where('user_id', $request->user['id'])
+ ->orderBy('created_at', 'DESC')
+ ->get();
+ return response([
+ 'data' => $ticket
+ ]);
+ }
+
+ public function save(TicketSave $request)
+ {
+ DB::beginTransaction();
+ if ((int)Ticket::where('status', 0)->where('user_id', $request->user['id'])->lockForUpdate()->count()) {
+ abort(500, __('There are other unresolved tickets'));
+ }
+ $ticket = Ticket::create(array_merge($request->only([
+ 'subject',
+ 'level'
+ ]), [
+ 'user_id' => $request->user['id']
+ ]));
+ if (!$ticket) {
+ DB::rollback();
+ abort(500, __('Failed to open ticket'));
+ }
+ $ticketMessage = TicketMessage::create([
+ 'user_id' => $request->user['id'],
+ 'ticket_id' => $ticket->id,
+ 'message' => $request->input('message')
+ ]);
+ if (!$ticketMessage) {
+ DB::rollback();
+ abort(500, __('Failed to open ticket'));
+ }
+ DB::commit();
+ $this->sendNotify($ticket, $request->input('message'));
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function reply(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, __('Invalid parameter'));
+ }
+ if (empty($request->input('message'))) {
+ abort(500, __('Message cannot be empty'));
+ }
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->where('user_id', $request->user['id'])
+ ->first();
+ if (!$ticket) {
+ abort(500, __('Ticket does not exist'));
+ }
+ if ($ticket->status) {
+ abort(500, __('The ticket is closed and cannot be replied'));
+ }
+ if ($request->user['id'] == $this->getLastMessage($ticket->id)->user_id) {
+ abort(500, __('Please wait for the technical enginneer to reply'));
+ }
+ $ticketService = new TicketService();
+ if (!$ticketService->reply(
+ $ticket,
+ $request->input('message'),
+ $request->user['id']
+ )) {
+ abort(500, __('Ticket reply failed'));
+ }
+ $this->sendNotify($ticket, $request->input('message'));
+ return response([
+ 'data' => true
+ ]);
+ }
+
+
+ public function close(Request $request)
+ {
+ if (empty($request->input('id'))) {
+ abort(500, __('Invalid parameter'));
+ }
+ $ticket = Ticket::where('id', $request->input('id'))
+ ->where('user_id', $request->user['id'])
+ ->first();
+ if (!$ticket) {
+ abort(500, __('Ticket does not exist'));
+ }
+ $ticket->status = 1;
+ if (!$ticket->save()) {
+ abort(500, __('Close failed'));
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ private function getLastMessage($ticketId)
+ {
+ return TicketMessage::where('ticket_id', $ticketId)
+ ->orderBy('id', 'DESC')
+ ->first();
+ }
+
+ public function withdraw(TicketWithdraw $request)
+ {
+ if ((int)config('v2board.withdraw_close_enable', 0)) {
+ abort(500, 'user.ticket.withdraw.not_support_withdraw');
+ }
+ if (!in_array(
+ $request->input('withdraw_method'),
+ config(
+ 'v2board.commission_withdraw_method',
+ Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT
+ )
+ )) {
+ abort(500, __('Unsupported withdrawal method'));
+ }
+ $user = User::find($request->user['id']);
+ $limit = config('v2board.commission_withdraw_limit', 100);
+ if ($limit > ($user->commission_balance / 100)) {
+ abort(500, __('The current required minimum withdrawal commission is :limit', ['limit' => $limit]));
+ }
+ DB::beginTransaction();
+ $subject = __('[Commission Withdrawal Request] This ticket is opened by the system');
+ $ticket = Ticket::create([
+ 'subject' => $subject,
+ 'level' => 2,
+ 'user_id' => $request->user['id']
+ ]);
+ if (!$ticket) {
+ DB::rollback();
+ abort(500, __('Failed to open ticket'));
+ }
+ $message = sprintf("%s\r\n%s",
+ __('Withdrawal method') . ":" . $request->input('withdraw_method'),
+ __('Withdrawal account') . ":" . $request->input('withdraw_account')
+ );
+ $ticketMessage = TicketMessage::create([
+ 'user_id' => $request->user['id'],
+ 'ticket_id' => $ticket->id,
+ 'message' => $message
+ ]);
+ if (!$ticketMessage) {
+ DB::rollback();
+ abort(500, __('Failed to open ticket'));
+ }
+ DB::commit();
+ $this->sendNotify($ticket, $message);
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ private function sendNotify(Ticket $ticket, string $message)
+ {
+ $telegramService = new TelegramService();
+ $telegramService->sendMessageWithAdmin("📮工单提醒 #{$ticket->id}\n———————————————\n主题:\n`{$ticket->subject}`\n内容:\n`{$message}`", true);
+ }
+}
diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php
new file mode 100755
index 0000000..c87b2af
--- /dev/null
+++ b/app/Http/Controllers/User/UserController.php
@@ -0,0 +1,239 @@
+user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ $authService = new AuthService($user);
+ return response([
+ 'data' => $authService->getSessions()
+ ]);
+ }
+
+ public function removeActiveSession(Request $request)
+ {
+ $user = User::find($request->user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ $authService = new AuthService($user);
+ return response([
+ 'data' => $authService->removeSession($request->input('session_id'))
+ ]);
+ }
+
+ public function checkLogin(Request $request)
+ {
+ $data = [
+ 'is_login' => $request->user['id'] ? true : false
+ ];
+ if ($request->user['is_admin']) {
+ $data['is_admin'] = true;
+ }
+ return response([
+ 'data' => $data
+ ]);
+ }
+
+ public function changePassword(UserChangePassword $request)
+ {
+ $user = User::find($request->user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ if (!Helper::multiPasswordVerify(
+ $user->password_algo,
+ $user->password_salt,
+ $request->input('old_password'),
+ $user->password)
+ ) {
+ abort(500, __('The old password is wrong'));
+ }
+ $user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
+ $user->password_algo = NULL;
+ $user->password_salt = NULL;
+ if (!$user->save()) {
+ abort(500, __('Save failed'));
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function info(Request $request)
+ {
+ $user = User::where('id', $request->user['id'])
+ ->select([
+ 'email',
+ 'transfer_enable',
+ 'last_login_at',
+ 'created_at',
+ 'banned',
+ 'remind_expire',
+ 'remind_traffic',
+ 'expired_at',
+ 'balance',
+ 'commission_balance',
+ 'plan_id',
+ 'discount',
+ 'commission_rate',
+ 'telegram_id',
+ 'uuid'
+ ])
+ ->first();
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ $user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
+ return response([
+ 'data' => $user
+ ]);
+ }
+
+ public function getStat(Request $request)
+ {
+ $stat = [
+ Order::where('status', 0)
+ ->where('user_id', $request->user['id'])
+ ->count(),
+ Ticket::where('status', 0)
+ ->where('user_id', $request->user['id'])
+ ->count(),
+ User::where('invite_user_id', $request->user['id'])
+ ->count()
+ ];
+ return response([
+ 'data' => $stat
+ ]);
+ }
+
+ public function getSubscribe(Request $request)
+ {
+ $user = User::where('id', $request->user['id'])
+ ->select([
+ 'plan_id',
+ 'token',
+ 'expired_at',
+ 'u',
+ 'd',
+ 'transfer_enable',
+ 'email',
+ 'uuid'
+ ])
+ ->first();
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ if ($user->plan_id) {
+ $user['plan'] = Plan::find($user->plan_id);
+ if (!$user['plan']) {
+ abort(500, __('Subscription plan does not exist'));
+ }
+ }
+ $user['subscribe_url'] = Helper::getSubscribeUrl("/api/v1/client/subscribe?token={$user['token']}");
+ $userService = new UserService();
+ $user['reset_day'] = $userService->getResetDay($user);
+ return response([
+ 'data' => $user
+ ]);
+ }
+
+ public function resetSecurity(Request $request)
+ {
+ $user = User::find($request->user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ $user->uuid = Helper::guid(true);
+ $user->token = Helper::guid();
+ if (!$user->save()) {
+ abort(500, __('Reset failed'));
+ }
+ return response([
+ 'data' => Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $user->token)
+ ]);
+ }
+
+ public function update(UserUpdate $request)
+ {
+ $updateData = $request->only([
+ 'remind_expire',
+ 'remind_traffic'
+ ]);
+
+ $user = User::find($request->user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ try {
+ $user->update($updateData);
+ } catch (\Exception $e) {
+ abort(500, __('Save failed'));
+ }
+
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function transfer(UserTransfer $request)
+ {
+ $user = User::find($request->user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+ if ($request->input('transfer_amount') > $user->commission_balance) {
+ abort(500, __('Insufficient commission balance'));
+ }
+ $user->commission_balance = $user->commission_balance - $request->input('transfer_amount');
+ $user->balance = $user->balance + $request->input('transfer_amount');
+ if (!$user->save()) {
+ abort(500, __('Transfer failed'));
+ }
+ return response([
+ 'data' => true
+ ]);
+ }
+
+ public function getQuickLoginUrl(Request $request)
+ {
+ $user = User::find($request->user['id']);
+ if (!$user) {
+ abort(500, __('The user does not exist'));
+ }
+
+ $code = Helper::guid();
+ $key = CacheKey::get('TEMP_TOKEN', $code);
+ Cache::put($key, $user->id, 60);
+ $redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
+ if (config('v2board.app_url')) {
+ $url = config('v2board.app_url') . $redirect;
+ } else {
+ $url = url($redirect);
+ }
+ return response([
+ 'data' => $url
+ ]);
+ }
+}
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
new file mode 100755
index 0000000..123e097
--- /dev/null
+++ b/app/Http/Kernel.php
@@ -0,0 +1,92 @@
+ [
+// \App\Http\Middleware\EncryptCookies::class,
+// \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+// \Illuminate\Session\Middleware\StartSession::class,
+ // \Illuminate\Session\Middleware\AuthenticateSession::class,
+// \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+// \App\Http\Middleware\VerifyCsrfToken::class,
+// \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ ],
+
+ 'api' => [
+// \App\Http\Middleware\EncryptCookies::class,
+// \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+// \Illuminate\Session\Middleware\StartSession::class,
+ \App\Http\Middleware\ForceJson::class,
+ \App\Http\Middleware\Language::class,
+ 'bindings',
+ ],
+ ];
+
+ /**
+ * The application's route middleware.
+ *
+ * These middleware may be assigned to groups or used individually.
+ *
+ * @var array
+ */
+ protected $routeMiddleware = [
+ 'auth' => \App\Http\Middleware\Authenticate::class,
+ 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+ 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
+ 'can' => \Illuminate\Auth\Middleware\Authorize::class,
+ 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+ 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
+ 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+ 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+ 'user' => \App\Http\Middleware\User::class,
+ 'admin' => \App\Http\Middleware\Admin::class,
+ 'client' => \App\Http\Middleware\Client::class,
+ 'staff' => \App\Http\Middleware\Staff::class,
+ 'log' => \App\Http\Middleware\RequestLog::class
+ ];
+
+ /**
+ * The priority-sorted list of middleware.
+ *
+ * This forces non-global middleware to always be in the given order.
+ *
+ * @var array
+ */
+ protected $middlewarePriority = [
+ \Illuminate\Session\Middleware\StartSession::class,
+ \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+ \App\Http\Middleware\Authenticate::class,
+ \Illuminate\Routing\Middleware\ThrottleRequests::class,
+ \Illuminate\Session\Middleware\AuthenticateSession::class,
+ \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ \Illuminate\Auth\Middleware\Authorize::class,
+ ];
+}
diff --git a/app/Http/Middleware/Admin.php b/app/Http/Middleware/Admin.php
new file mode 100755
index 0000000..c84a88c
--- /dev/null
+++ b/app/Http/Middleware/Admin.php
@@ -0,0 +1,30 @@
+input('auth_data') ?? $request->header('authorization');
+ if (!$authorization) abort(403, '未登录或登陆已过期');
+
+ $user = AuthService::decryptAuthData($authorization);
+ if (!$user || !$user['is_admin']) abort(403, '未登录或登陆已过期');
+ $request->merge([
+ 'user' => $user
+ ]);
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php
new file mode 100755
index 0000000..0eec715
--- /dev/null
+++ b/app/Http/Middleware/Authenticate.php
@@ -0,0 +1,21 @@
+expectsJson()) {
+ return route('login');
+ }
+ }
+}
diff --git a/app/Http/Middleware/CORS.php b/app/Http/Middleware/CORS.php
new file mode 100755
index 0000000..f737750
--- /dev/null
+++ b/app/Http/Middleware/CORS.php
@@ -0,0 +1,27 @@
+header('origin');
+ if (empty($origin)) {
+ $referer = $request->header('referer');
+ if (!empty($referer) && preg_match("/^((https|http):\/\/)?([^\/]+)/i", $referer, $matches)) {
+ $origin = $matches[0];
+ }
+ }
+ $response = $next($request);
+ $response->header('Access-Control-Allow-Origin', trim($origin, '/'));
+ $response->header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,HEAD');
+ $response->header('Access-Control-Allow-Headers', 'Origin,Content-Type,Accept,Authorization,X-Request-With');
+ $response->header('Access-Control-Allow-Credentials', 'true');
+ $response->header('Access-Control-Max-Age', 10080);
+
+ return $response;
+ }
+}
diff --git a/app/Http/Middleware/CheckForMaintenanceMode.php b/app/Http/Middleware/CheckForMaintenanceMode.php
new file mode 100755
index 0000000..35b9824
--- /dev/null
+++ b/app/Http/Middleware/CheckForMaintenanceMode.php
@@ -0,0 +1,17 @@
+input('token');
+ if (empty($token)) {
+ abort(403, 'token is null');
+ }
+ $user = User::where('token', $token)->first();
+ if (!$user) {
+ abort(403, 'token is error');
+ }
+ $request->merge([
+ 'user' => $user
+ ]);
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php
new file mode 100755
index 0000000..033136a
--- /dev/null
+++ b/app/Http/Middleware/EncryptCookies.php
@@ -0,0 +1,17 @@
+headers->set('accept', 'application/json');
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/Language.php b/app/Http/Middleware/Language.php
new file mode 100755
index 0000000..8bb51e7
--- /dev/null
+++ b/app/Http/Middleware/Language.php
@@ -0,0 +1,17 @@
+header('content-language')) {
+ App::setLocale($request->header('content-language'));
+ }
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php
new file mode 100755
index 0000000..a7ef27c
--- /dev/null
+++ b/app/Http/Middleware/RedirectIfAuthenticated.php
@@ -0,0 +1,26 @@
+check()) {
+ return redirect('/home');
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/RequestLog.php b/app/Http/Middleware/RequestLog.php
new file mode 100755
index 0000000..c1244cc
--- /dev/null
+++ b/app/Http/Middleware/RequestLog.php
@@ -0,0 +1,24 @@
+method() === 'POST') {
+ $path = $request->path();
+ info("POST {$path}");
+ };
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/Staff.php b/app/Http/Middleware/Staff.php
new file mode 100644
index 0000000..237d278
--- /dev/null
+++ b/app/Http/Middleware/Staff.php
@@ -0,0 +1,29 @@
+input('auth_data') ?? $request->header('authorization');
+ if (!$authorization) abort(403, '未登录或登陆已过期');
+
+ $user = AuthService::decryptAuthData($authorization);
+ if (!$user || !$user['is_staff']) abort(403, '未登录或登陆已过期');
+ $request->merge([
+ 'user' => $user
+ ]);
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php
new file mode 100755
index 0000000..5a50e7b
--- /dev/null
+++ b/app/Http/Middleware/TrimStrings.php
@@ -0,0 +1,18 @@
+input('auth_data') ?? $request->header('authorization');
+ if (!$authorization) abort(403, '未登录或登陆已过期');
+
+ $user = AuthService::decryptAuthData($authorization);
+ if (!$user) abort(403, '未登录或登陆已过期');
+ $request->merge([
+ 'user' => $user
+ ]);
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php
new file mode 100755
index 0000000..324a166
--- /dev/null
+++ b/app/Http/Middleware/VerifyCsrfToken.php
@@ -0,0 +1,24 @@
+ 'in:0,1',
+ 'invite_commission' => 'integer',
+ 'invite_gen_limit' => 'integer',
+ 'invite_never_expire' => 'in:0,1',
+ 'commission_first_time_enable' => 'in:0,1',
+ 'commission_auto_check_enable' => 'in:0,1',
+ 'commission_withdraw_limit' => 'nullable|numeric',
+ 'commission_withdraw_method' => 'nullable|array',
+ 'withdraw_close_enable' => 'in:0,1',
+ 'commission_distribution_enable' => 'in:0,1',
+ 'commission_distribution_l1' => 'nullable|numeric',
+ 'commission_distribution_l2' => 'nullable|numeric',
+ 'commission_distribution_l3' => 'nullable|numeric',
+ // site
+ 'logo' => 'nullable|url',
+ 'force_https' => 'in:0,1',
+ 'stop_register' => 'in:0,1',
+ 'app_name' => '',
+ 'app_description' => '',
+ 'app_url' => 'nullable|url',
+ 'subscribe_url' => 'nullable',
+ 'try_out_enable' => 'in:0,1',
+ 'try_out_plan_id' => 'integer',
+ 'try_out_hour' => 'numeric',
+ 'tos_url' => 'nullable|url',
+ 'currency' => '',
+ 'currency_symbol' => '',
+ // subscribe
+ 'plan_change_enable' => 'in:0,1',
+ 'reset_traffic_method' => 'in:0,1,2,3,4',
+ 'surplus_enable' => 'in:0,1',
+ 'new_order_event_id' => 'in:0,1',
+ 'renew_order_event_id' => 'in:0,1',
+ 'change_order_event_id' => 'in:0,1',
+ 'show_info_to_server_enable' => 'in:0,1',
+ // server
+ 'server_token' => 'nullable|min:16',
+ 'server_pull_interval' => 'integer',
+ 'server_push_interval' => 'integer',
+ // frontend
+ 'frontend_theme' => '',
+ 'frontend_theme_sidebar' => 'nullable|in:dark,light',
+ 'frontend_theme_header' => 'nullable|in:dark,light',
+ 'frontend_theme_color' => 'nullable|in:default,darkblue,black,green',
+ 'frontend_background_url' => 'nullable|url',
+ // email
+ 'email_template' => '',
+ 'email_host' => '',
+ 'email_port' => '',
+ 'email_username' => '',
+ 'email_password' => '',
+ 'email_encryption' => '',
+ 'email_from_address' => '',
+ // telegram
+ 'telegram_bot_enable' => 'in:0,1',
+ 'telegram_bot_token' => '',
+ 'telegram_discuss_id' => '',
+ 'telegram_channel_id' => '',
+ 'telegram_discuss_link' => 'nullable|url',
+ // app
+ 'windows_version' => '',
+ 'windows_download_url' => '',
+ 'macos_version' => '',
+ 'macos_download_url' => '',
+ 'android_version' => '',
+ 'android_download_url' => '',
+ // safe
+ 'email_whitelist_enable' => 'in:0,1',
+ 'email_whitelist_suffix' => 'nullable|array',
+ 'email_gmail_limit_enable' => 'in:0,1',
+ 'recaptcha_enable' => 'in:0,1',
+ 'recaptcha_key' => '',
+ 'recaptcha_site_key' => '',
+ 'email_verify' => 'in:0,1',
+ 'safe_mode_enable' => 'in:0,1',
+ 'register_limit_by_ip_enable' => 'in:0,1',
+ 'register_limit_count' => 'integer',
+ 'register_limit_expire' => 'integer',
+ 'secure_path' => 'min:8|regex:/^[\w-]*$/',
+ 'password_limit_enable' => 'in:0,1',
+ 'password_limit_count' => 'integer',
+ 'password_limit_expire' => 'integer',
+ ];
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return self::RULES;
+ }
+
+ public function messages()
+ {
+ // illiteracy prompt
+ return [
+ 'app_url.url' => '站点URL格式不正确,必须携带http(s)://',
+ 'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://',
+ 'server_token.min' => '通讯密钥长度必须大于16位',
+ 'tos_url.url' => '服务条款URL格式不正确,必须携带http(s)://',
+ 'telegram_discuss_link.url' => 'Telegram群组地址必须为URL格式,必须携带http(s)://',
+ 'logo.url' => 'LOGO URL格式不正确,必须携带https(s)://',
+ 'secure_path.min' => '后台路径长度最小为8位',
+ 'secure_path.regex' => '后台路径只能为字母或数字'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/CouponGenerate.php b/app/Http/Requests/Admin/CouponGenerate.php
new file mode 100644
index 0000000..70f3efc
--- /dev/null
+++ b/app/Http/Requests/Admin/CouponGenerate.php
@@ -0,0 +1,51 @@
+ 'nullable|integer|max:500',
+ 'name' => 'required',
+ 'type' => 'required|in:1,2',
+ 'value' => 'required|integer',
+ 'started_at' => 'required|integer',
+ 'ended_at' => 'required|integer',
+ 'limit_use' => 'nullable|integer',
+ 'limit_use_with_user' => 'nullable|integer',
+ 'limit_plan_ids' => 'nullable|array',
+ 'limit_period' => 'nullable|array',
+ 'code' => ''
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'generate_count.integer' => '生成数量必须为数字',
+ 'generate_count.max' => '生成数量最大为500个',
+ 'name.required' => '名称不能为空',
+ 'type.required' => '类型不能为空',
+ 'type.in' => '类型格式有误',
+ 'value.required' => '金额或比例不能为空',
+ 'value.integer' => '金额或比例格式有误',
+ 'started_at.required' => '开始时间不能为空',
+ 'started_at.integer' => '开始时间格式有误',
+ 'ended_at.required' => '结束时间不能为空',
+ 'ended_at.integer' => '结束时间格式有误',
+ 'limit_use.integer' => '最大使用次数格式有误',
+ 'limit_use_with_user.integer' => '限制用户使用次数格式有误',
+ 'limit_plan_ids.array' => '指定订阅格式有误',
+ 'limit_period.array' => '指定周期格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/KnowledgeCategorySave.php b/app/Http/Requests/Admin/KnowledgeCategorySave.php
new file mode 100644
index 0000000..9aabb7e
--- /dev/null
+++ b/app/Http/Requests/Admin/KnowledgeCategorySave.php
@@ -0,0 +1,29 @@
+ 'required',
+ 'language' => 'required'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'name.required' => '分类名称不能为空',
+ 'language.required' => '分类语言不能为空'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/KnowledgeCategorySort.php b/app/Http/Requests/Admin/KnowledgeCategorySort.php
new file mode 100644
index 0000000..c76f810
--- /dev/null
+++ b/app/Http/Requests/Admin/KnowledgeCategorySort.php
@@ -0,0 +1,28 @@
+ 'required|array'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'knowledge_category_ids.required' => '分类不能为空',
+ 'knowledge_category_ids.array' => '分类格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/KnowledgeSave.php b/app/Http/Requests/Admin/KnowledgeSave.php
new file mode 100644
index 0000000..15fee61
--- /dev/null
+++ b/app/Http/Requests/Admin/KnowledgeSave.php
@@ -0,0 +1,33 @@
+ 'required',
+ 'language' => 'required',
+ 'title' => 'required',
+ 'body' => 'required'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'title.required' => '标题不能为空',
+ 'category.required' => '分类不能为空',
+ 'body.required' => '内容不能为空',
+ 'language.required' => '语言不能为空'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/KnowledgeSort.php b/app/Http/Requests/Admin/KnowledgeSort.php
new file mode 100644
index 0000000..d29a899
--- /dev/null
+++ b/app/Http/Requests/Admin/KnowledgeSort.php
@@ -0,0 +1,28 @@
+ 'required|array'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'knowledge_ids.required' => '知识ID不能为空',
+ 'knowledge_ids.array' => '知识ID格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/MailSend.php b/app/Http/Requests/Admin/MailSend.php
new file mode 100644
index 0000000..86247a3
--- /dev/null
+++ b/app/Http/Requests/Admin/MailSend.php
@@ -0,0 +1,34 @@
+ 'required|in:1,2,3,4',
+ 'subject' => 'required',
+ 'content' => 'required',
+ 'receiver' => 'array'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'type.required' => '发送类型不能为空',
+ 'type.in' => '发送类型格式有误',
+ 'subject.required' => '主题不能为空',
+ 'content.required' => '内容不能为空',
+ 'receiver.array' => '收件人格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/NoticeSave.php b/app/Http/Requests/Admin/NoticeSave.php
new file mode 100644
index 0000000..0f6dc0b
--- /dev/null
+++ b/app/Http/Requests/Admin/NoticeSave.php
@@ -0,0 +1,33 @@
+ 'required',
+ 'content' => 'required',
+ 'img_url' => 'nullable|url',
+ 'tags' => 'nullable|array'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'title.required' => '标题不能为空',
+ 'content.required' => '内容不能为空',
+ 'img_url.url' => '图片URL格式不正确',
+ 'tags.array' => '标签格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/OrderAssign.php b/app/Http/Requests/Admin/OrderAssign.php
new file mode 100644
index 0000000..4b259a0
--- /dev/null
+++ b/app/Http/Requests/Admin/OrderAssign.php
@@ -0,0 +1,34 @@
+ 'required',
+ 'email' => 'required',
+ 'total_amount' => 'required',
+ 'period' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'plan_id.required' => '订阅不能为空',
+ 'email.required' => '邮箱不能为空',
+ 'total_amount.required' => '支付金额不能为空',
+ 'period.required' => '订阅周期不能为空',
+ 'period.in' => '订阅周期格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/OrderFetch.php b/app/Http/Requests/Admin/OrderFetch.php
new file mode 100644
index 0000000..9c4765b
--- /dev/null
+++ b/app/Http/Requests/Admin/OrderFetch.php
@@ -0,0 +1,32 @@
+ 'required|in:email,trade_no,status,commission_status,user_id,invite_user_id,callback_no,commission_balance',
+ 'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
+ 'filter.*.value' => ''
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'filter.*.key.required' => '过滤键不能为空',
+ 'filter.*.key.in' => '过滤键参数有误',
+ 'filter.*.condition.required' => '过滤条件不能为空',
+ 'filter.*.condition.in' => '过滤条件参数有误',
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/OrderUpdate.php b/app/Http/Requests/Admin/OrderUpdate.php
new file mode 100644
index 0000000..8a38d10
--- /dev/null
+++ b/app/Http/Requests/Admin/OrderUpdate.php
@@ -0,0 +1,29 @@
+ 'in:0,1,2,3',
+ 'commission_status' => 'in:0,1,3'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'status.in' => '销售状态格式不正确',
+ 'commission_status.in' => '佣金状态格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/PlanSave.php b/app/Http/Requests/Admin/PlanSave.php
new file mode 100755
index 0000000..2aa4583
--- /dev/null
+++ b/app/Http/Requests/Admin/PlanSave.php
@@ -0,0 +1,57 @@
+ 'required',
+ 'content' => '',
+ 'group_id' => 'required',
+ 'transfer_enable' => 'required',
+ 'month_price' => 'nullable|integer',
+ 'quarter_price' => 'nullable|integer',
+ 'half_year_price' => 'nullable|integer',
+ 'year_price' => 'nullable|integer',
+ 'two_year_price' => 'nullable|integer',
+ 'three_year_price' => 'nullable|integer',
+ 'onetime_price' => 'nullable|integer',
+ 'reset_price' => 'nullable|integer',
+ 'reset_traffic_method' => 'nullable|integer|in:0,1,2,3,4',
+ 'capacity_limit' => 'nullable|integer',
+ 'speed_limit' => 'nullable|integer'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'name.required' => '套餐名称不能为空',
+ 'type.required' => '套餐类型不能为空',
+ 'type.in' => '套餐类型格式有误',
+ 'group_id.required' => '权限组不能为空',
+ 'transfer_enable.required' => '流量不能为空',
+ 'month_price.integer' => '月付金额格式有误',
+ 'quarter_price.integer' => '季付金额格式有误',
+ 'half_year_price.integer' => '半年付金额格式有误',
+ 'year_price.integer' => '年付金额格式有误',
+ 'two_year_price.integer' => '两年付金额格式有误',
+ 'three_year_price.integer' => '三年付金额格式有误',
+ 'onetime_price.integer' => '一次性金额有误',
+ 'reset_price.integer' => '流量重置包金额有误',
+ 'reset_traffic_method.integer' => '流量重置方式格式有误',
+ 'reset_traffic_method.in' => '流量重置方式格式有误',
+ 'capacity_limit.integer' => '容纳用户量限制格式有误',
+ 'speed_limit.integer' => '限速格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/PlanSort.php b/app/Http/Requests/Admin/PlanSort.php
new file mode 100644
index 0000000..eb7987a
--- /dev/null
+++ b/app/Http/Requests/Admin/PlanSort.php
@@ -0,0 +1,28 @@
+ 'required|array'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'plan_ids.required' => '订阅计划ID不能为空',
+ 'plan_ids.array' => '订阅计划ID格式有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/PlanUpdate.php b/app/Http/Requests/Admin/PlanUpdate.php
new file mode 100644
index 0000000..d9463e2
--- /dev/null
+++ b/app/Http/Requests/Admin/PlanUpdate.php
@@ -0,0 +1,29 @@
+ 'in:0,1',
+ 'renew' => 'in:0,1'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'show.in' => '销售状态格式不正确',
+ 'renew.in' => '续费状态格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/ServerShadowsocksSave.php b/app/Http/Requests/Admin/ServerShadowsocksSave.php
new file mode 100644
index 0000000..6a29e07
--- /dev/null
+++ b/app/Http/Requests/Admin/ServerShadowsocksSave.php
@@ -0,0 +1,52 @@
+ '',
+ 'name' => 'required',
+ 'group_id' => 'required|array',
+ 'parent_id' => 'nullable|integer',
+ 'route_id' => 'nullable|array',
+ 'host' => 'required',
+ 'port' => 'required',
+ 'server_port' => 'required',
+ 'cipher' => 'required|in:aes-128-gcm,aes-192-gcm,aes-256-gcm,chacha20-ietf-poly1305,2022-blake3-aes-128-gcm,2022-blake3-aes-256-gcm',
+ 'obfs' => 'nullable|in:http',
+ 'obfs_settings' => 'nullable|array',
+ 'tags' => 'nullable|array',
+ 'rate' => 'required|numeric'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'name.required' => '节点名称不能为空',
+ 'group_id.required' => '权限组不能为空',
+ 'group_id.array' => '权限组格式不正确',
+ 'route_id.array' => '路由组格式不正确',
+ 'parent_id.integer' => '父节点格式不正确',
+ 'host.required' => '节点地址不能为空',
+ 'port.required' => '连接端口不能为空',
+ 'server_port.required' => '后端服务端口不能为空',
+ 'cipher.required' => '加密方式不能为空',
+ 'tags.array' => '标签格式不正确',
+ 'rate.required' => '倍率不能为空',
+ 'rate.numeric' => '倍率格式不正确',
+ 'obfs.in' => '混淆格式不正确',
+ 'obfs_settings.array' => '混淆设置格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/ServerShadowsocksUpdate.php b/app/Http/Requests/Admin/ServerShadowsocksUpdate.php
new file mode 100755
index 0000000..4de0d09
--- /dev/null
+++ b/app/Http/Requests/Admin/ServerShadowsocksUpdate.php
@@ -0,0 +1,28 @@
+ 'in:0,1'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'show.in' => '显示状态格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/ServerTrojanSave.php b/app/Http/Requests/Admin/ServerTrojanSave.php
new file mode 100644
index 0000000..40940ff
--- /dev/null
+++ b/app/Http/Requests/Admin/ServerTrojanSave.php
@@ -0,0 +1,49 @@
+ '',
+ 'name' => 'required',
+ 'group_id' => 'required|array',
+ 'route_id' => 'nullable|array',
+ 'parent_id' => 'nullable|integer',
+ 'host' => 'required',
+ 'port' => 'required',
+ 'server_port' => 'required',
+ 'allow_insecure' => 'nullable|in:0,1',
+ 'server_name' => 'nullable',
+ 'tags' => 'nullable|array',
+ 'rate' => 'required|numeric'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'name.required' => '节点名称不能为空',
+ 'group_id.required' => '权限组不能为空',
+ 'group_id.array' => '权限组格式不正确',
+ 'route_id.array' => '路由组格式不正确',
+ 'parent_id.integer' => '父节点格式不正确',
+ 'host.required' => '节点地址不能为空',
+ 'port.required' => '连接端口不能为空',
+ 'server_port.required' => '后端服务端口不能为空',
+ 'allow_insecure.in' => '允许不安全格式不正确',
+ 'tags.array' => '标签格式不正确',
+ 'rate.required' => '倍率不能为空',
+ 'rate.numeric' => '倍率格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/ServerTrojanUpdate.php b/app/Http/Requests/Admin/ServerTrojanUpdate.php
new file mode 100755
index 0000000..fe1786d
--- /dev/null
+++ b/app/Http/Requests/Admin/ServerTrojanUpdate.php
@@ -0,0 +1,28 @@
+ 'in:0,1'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'show.in' => '显示状态格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/ServerVmessSave.php b/app/Http/Requests/Admin/ServerVmessSave.php
new file mode 100755
index 0000000..3f05378
--- /dev/null
+++ b/app/Http/Requests/Admin/ServerVmessSave.php
@@ -0,0 +1,59 @@
+ '',
+ 'name' => 'required',
+ 'group_id' => 'required|array',
+ 'route_id' => 'nullable|array',
+ 'parent_id' => 'nullable|integer',
+ 'host' => 'required',
+ 'port' => 'required',
+ 'server_port' => 'required',
+ 'tls' => 'required',
+ 'tags' => 'nullable|array',
+ 'rate' => 'required|numeric',
+ 'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
+ 'networkSettings' => 'nullable|array',
+ 'ruleSettings' => 'nullable|array',
+ 'tlsSettings' => 'nullable|array',
+ 'dnsSettings' => 'nullable|array'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'name.required' => '节点名称不能为空',
+ 'group_id.required' => '权限组不能为空',
+ 'group_id.array' => '权限组格式不正确',
+ 'route_id.array' => '路由组格式不正确',
+ 'parent_id.integer' => '父ID格式不正确',
+ 'host.required' => '节点地址不能为空',
+ 'port.required' => '连接端口不能为空',
+ 'server_port.required' => '后端服务端口不能为空',
+ 'tls.required' => 'TLS不能为空',
+ 'tags.array' => '标签格式不正确',
+ 'rate.required' => '倍率不能为空',
+ 'rate.numeric' => '倍率格式不正确',
+ 'network.required' => '传输协议不能为空',
+ 'network.in' => '传输协议格式不正确',
+ 'networkSettings.array' => '传输协议配置有误',
+ 'ruleSettings.array' => '规则配置有误',
+ 'tlsSettings.array' => 'tls配置有误',
+ 'dnsSettings.array' => 'dns配置有误'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/ServerVmessUpdate.php b/app/Http/Requests/Admin/ServerVmessUpdate.php
new file mode 100755
index 0000000..607d74d
--- /dev/null
+++ b/app/Http/Requests/Admin/ServerVmessUpdate.php
@@ -0,0 +1,28 @@
+ 'in:0,1'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'show.in' => '显示状态格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/UserFetch.php b/app/Http/Requests/Admin/UserFetch.php
new file mode 100644
index 0000000..899c6a9
--- /dev/null
+++ b/app/Http/Requests/Admin/UserFetch.php
@@ -0,0 +1,33 @@
+ 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned,remarks,is_admin',
+ 'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
+ 'filter.*.value' => 'required'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'filter.*.key.required' => '过滤键不能为空',
+ 'filter.*.key.in' => '过滤键参数有误',
+ 'filter.*.condition.required' => '过滤条件不能为空',
+ 'filter.*.condition.in' => '过滤条件参数有误',
+ 'filter.*.value.required' => '过滤值不能为空'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/UserGenerate.php b/app/Http/Requests/Admin/UserGenerate.php
new file mode 100644
index 0000000..41b0722
--- /dev/null
+++ b/app/Http/Requests/Admin/UserGenerate.php
@@ -0,0 +1,33 @@
+ 'nullable|integer|max:500',
+ 'expired_at' => 'nullable|integer',
+ 'plan_id' => 'nullable|integer',
+ 'email_prefix' => 'nullable',
+ 'email_suffix' => 'required',
+ 'password' => 'nullable'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'generate_count.integer' => '生成数量必须为数字',
+ 'generate_count.max' => '生成数量最大为500个'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/UserSendMail.php b/app/Http/Requests/Admin/UserSendMail.php
new file mode 100644
index 0000000..f885c36
--- /dev/null
+++ b/app/Http/Requests/Admin/UserSendMail.php
@@ -0,0 +1,29 @@
+ 'required',
+ 'content' => 'required',
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'subject.required' => '主题不能为空',
+ 'content.required' => '发送内容不能为空'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Admin/UserUpdate.php b/app/Http/Requests/Admin/UserUpdate.php
new file mode 100644
index 0000000..c175f33
--- /dev/null
+++ b/app/Http/Requests/Admin/UserUpdate.php
@@ -0,0 +1,67 @@
+ 'required|email:strict',
+ 'password' => 'nullable|min:8',
+ 'transfer_enable' => 'numeric',
+ 'expired_at' => 'nullable|integer',
+ 'banned' => 'required|in:0,1',
+ 'plan_id' => 'nullable|integer',
+ 'commission_rate' => 'nullable|integer|min:0|max:100',
+ 'discount' => 'nullable|integer|min:0|max:100',
+ 'is_admin' => 'required|in:0,1',
+ 'is_staff' => 'required|in:0,1',
+ 'u' => 'integer',
+ 'd' => 'integer',
+ 'balance' => 'integer',
+ 'commission_type' => 'integer',
+ 'commission_balance' => 'integer',
+ 'remarks' => 'nullable',
+ 'speed_limit' => 'nullable|integer'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'email.required' => '邮箱不能为空',
+ 'email.email' => '邮箱格式不正确',
+ 'transfer_enable.numeric' => '流量格式不正确',
+ 'expired_at.integer' => '到期时间格式不正确',
+ 'banned.required' => '是否封禁不能为空',
+ 'banned.in' => '是否封禁格式不正确',
+ 'is_admin.required' => '是否管理员不能为空',
+ 'is_admin.in' => '是否管理员格式不正确',
+ 'is_staff.required' => '是否员工不能为空',
+ 'is_staff.in' => '是否员工格式不正确',
+ 'plan_id.integer' => '订阅计划格式不正确',
+ 'commission_rate.integer' => '推荐返利比例格式不正确',
+ 'commission_rate.nullable' => '推荐返利比例格式不正确',
+ 'commission_rate.min' => '推荐返利比例最小为0',
+ 'commission_rate.max' => '推荐返利比例最大为100',
+ 'discount.integer' => '专属折扣比例格式不正确',
+ 'discount.nullable' => '专属折扣比例格式不正确',
+ 'discount.min' => '专属折扣比例最小为0',
+ 'discount.max' => '专属折扣比例最大为100',
+ 'u.integer' => '上行流量格式不正确',
+ 'd.integer' => '下行流量格式不正确',
+ 'balance.integer' => '余额格式不正确',
+ 'commission_balance.integer' => '佣金格式不正确',
+ 'password.min' => '密码长度最小8位',
+ 'speed_limit.integer' => '限速格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/Passport/AuthForget.php b/app/Http/Requests/Passport/AuthForget.php
new file mode 100644
index 0000000..8106f28
--- /dev/null
+++ b/app/Http/Requests/Passport/AuthForget.php
@@ -0,0 +1,33 @@
+ 'required|email:strict',
+ 'password' => 'required|min:8',
+ 'email_code' => 'required'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'email.required' => __('Email can not be empty'),
+ 'email.email' => __('Email format is incorrect'),
+ 'password.required' => __('Password can not be empty'),
+ 'password.min' => __('Password must be greater than 8 digits'),
+ 'email_code.required' => __('Email verification code cannot be empty')
+ ];
+ }
+}
diff --git a/app/Http/Requests/Passport/AuthLogin.php b/app/Http/Requests/Passport/AuthLogin.php
new file mode 100644
index 0000000..6aa832c
--- /dev/null
+++ b/app/Http/Requests/Passport/AuthLogin.php
@@ -0,0 +1,31 @@
+ 'required|email:strict',
+ 'password' => 'required|min:8'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'email.required' => __('Email can not be empty'),
+ 'email.email' => __('Email format is incorrect'),
+ 'password.required' => __('Password can not be empty'),
+ 'password.min' => __('Password must be greater than 8 digits')
+ ];
+ }
+}
diff --git a/app/Http/Requests/Passport/AuthRegister.php b/app/Http/Requests/Passport/AuthRegister.php
new file mode 100755
index 0000000..63e053a
--- /dev/null
+++ b/app/Http/Requests/Passport/AuthRegister.php
@@ -0,0 +1,31 @@
+ 'required|email:strict',
+ 'password' => 'required|min:8'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'email.required' => __('Email can not be empty'),
+ 'email.email' => __('Email format is incorrect'),
+ 'password.required' => __('Password can not be empty'),
+ 'password.min' => __('Password must be greater than 8 digits')
+ ];
+ }
+}
diff --git a/app/Http/Requests/Passport/CommSendEmailVerify.php b/app/Http/Requests/Passport/CommSendEmailVerify.php
new file mode 100644
index 0000000..ff5ecdd
--- /dev/null
+++ b/app/Http/Requests/Passport/CommSendEmailVerify.php
@@ -0,0 +1,28 @@
+ 'required|email:strict'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'email.required' => __('Email can not be empty'),
+ 'email.email' => __('Email format is incorrect')
+ ];
+ }
+}
diff --git a/app/Http/Requests/Staff/UserUpdate.php b/app/Http/Requests/Staff/UserUpdate.php
new file mode 100644
index 0000000..be22144
--- /dev/null
+++ b/app/Http/Requests/Staff/UserUpdate.php
@@ -0,0 +1,56 @@
+ 'required|email:strict',
+ 'password' => 'nullable',
+ 'transfer_enable' => 'numeric',
+ 'expired_at' => 'nullable|integer',
+ 'banned' => 'required|in:0,1',
+ 'plan_id' => 'nullable|integer',
+ 'commission_rate' => 'nullable|integer|min:0|max:100',
+ 'discount' => 'nullable|integer|min:0|max:100',
+ 'u' => 'integer',
+ 'd' => 'integer',
+ 'balance' => 'integer',
+ 'commission_balance' => 'integer'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'email.required' => '邮箱不能为空',
+ 'email.email' => '邮箱格式不正确',
+ 'transfer_enable.numeric' => '流量格式不正确',
+ 'expired_at.integer' => '到期时间格式不正确',
+ 'banned.required' => '是否封禁不能为空',
+ 'banned.in' => '是否封禁格式不正确',
+ 'plan_id.integer' => '订阅计划格式不正确',
+ 'commission_rate.integer' => '推荐返利比例格式不正确',
+ 'commission_rate.nullable' => '推荐返利比例格式不正确',
+ 'commission_rate.min' => '推荐返利比例最小为0',
+ 'commission_rate.max' => '推荐返利比例最大为100',
+ 'discount.integer' => '专属折扣比例格式不正确',
+ 'discount.nullable' => '专属折扣比例格式不正确',
+ 'discount.min' => '专属折扣比例最小为0',
+ 'discount.max' => '专属折扣比例最大为100',
+ 'u.integer' => '上行流量格式不正确',
+ 'd.integer' => '下行流量格式不正确',
+ 'balance.integer' => '余额格式不正确',
+ 'commission_balance.integer' => '佣金格式不正确'
+ ];
+ }
+}
diff --git a/app/Http/Requests/User/OrderSave.php b/app/Http/Requests/User/OrderSave.php
new file mode 100755
index 0000000..449bcaa
--- /dev/null
+++ b/app/Http/Requests/User/OrderSave.php
@@ -0,0 +1,30 @@
+ 'required',
+ 'period' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'plan_id.required' => __('Plan ID cannot be empty'),
+ 'period.required' => __('Plan period cannot be empty'),
+ 'period.in' => __('Wrong plan period')
+ ];
+ }
+}
diff --git a/app/Http/Requests/User/TicketSave.php b/app/Http/Requests/User/TicketSave.php
new file mode 100644
index 0000000..412778f
--- /dev/null
+++ b/app/Http/Requests/User/TicketSave.php
@@ -0,0 +1,32 @@
+ 'required',
+ 'level' => 'required|in:0,1,2',
+ 'message' => 'required'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'subject.required' => __('Ticket subject cannot be empty'),
+ 'level.required' => __('Ticket level cannot be empty'),
+ 'level.in' => __('Incorrect ticket level format'),
+ 'message.required' => __('Message cannot be empty')
+ ];
+ }
+}
diff --git a/app/Http/Requests/User/TicketWithdraw.php b/app/Http/Requests/User/TicketWithdraw.php
new file mode 100644
index 0000000..d0da905
--- /dev/null
+++ b/app/Http/Requests/User/TicketWithdraw.php
@@ -0,0 +1,29 @@
+ 'required',
+ 'withdraw_account' => 'required'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'withdraw_method.required' => __('The withdrawal method cannot be empty'),
+ 'withdraw_account.required' => __('The withdrawal account cannot be empty')
+ ];
+ }
+}
diff --git a/app/Http/Requests/User/UserChangePassword.php b/app/Http/Requests/User/UserChangePassword.php
new file mode 100644
index 0000000..04e70c7
--- /dev/null
+++ b/app/Http/Requests/User/UserChangePassword.php
@@ -0,0 +1,30 @@
+ 'required',
+ 'new_password' => 'required|min:8'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'old_password.required' => __('Old password cannot be empty'),
+ 'new_password.required' => __('New password cannot be empty'),
+ 'new_password.min' => __('Password must be greater than 8 digits')
+ ];
+ }
+}
diff --git a/app/Http/Requests/User/UserTransfer.php b/app/Http/Requests/User/UserTransfer.php
new file mode 100644
index 0000000..478c825
--- /dev/null
+++ b/app/Http/Requests/User/UserTransfer.php
@@ -0,0 +1,29 @@
+ 'required|integer|min:1'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'transfer_amount.required' => __('The transfer amount cannot be empty'),
+ 'transfer_amount.integer' => __('The transfer amount parameter is wrong'),
+ 'transfer_amount.min' => __('The transfer amount parameter is wrong')
+ ];
+ }
+}
diff --git a/app/Http/Requests/User/UserUpdate.php b/app/Http/Requests/User/UserUpdate.php
new file mode 100644
index 0000000..5ba6604
--- /dev/null
+++ b/app/Http/Requests/User/UserUpdate.php
@@ -0,0 +1,29 @@
+ 'in:0,1',
+ 'remind_traffic' => 'in:0,1'
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'show.in' => __('Incorrect format of expiration reminder'),
+ 'renew.in' => __('Incorrect traffic alert format')
+ ];
+ }
+}
diff --git a/app/Http/Routes/AdminRoute.php b/app/Http/Routes/AdminRoute.php
new file mode 100644
index 0000000..03dc1df
--- /dev/null
+++ b/app/Http/Routes/AdminRoute.php
@@ -0,0 +1,144 @@
+group([
+ 'prefix' => config('v2board.secure_path', config('v2board.frontend_admin_path', hash('crc32b', config('app.key')))),
+ 'middleware' => ['admin', 'log']
+ ], function ($router) {
+ // Config
+ $router->get ('/config/fetch', 'Admin\\ConfigController@fetch');
+ $router->post('/config/save', 'Admin\\ConfigController@save');
+ $router->get ('/config/getEmailTemplate', 'Admin\\ConfigController@getEmailTemplate');
+ $router->get ('/config/getThemeTemplate', 'Admin\\ConfigController@getThemeTemplate');
+ $router->post('/config/setTelegramWebhook', 'Admin\\ConfigController@setTelegramWebhook');
+ $router->post('/config/testSendMail', 'Admin\\ConfigController@testSendMail');
+ // Plan
+ $router->get ('/plan/fetch', 'Admin\\PlanController@fetch');
+ $router->post('/plan/save', 'Admin\\PlanController@save');
+ $router->post('/plan/drop', 'Admin\\PlanController@drop');
+ $router->post('/plan/update', 'Admin\\PlanController@update');
+ $router->post('/plan/sort', 'Admin\\PlanController@sort');
+ // Server
+ $router->get ('/server/group/fetch', 'Admin\\Server\\GroupController@fetch');
+ $router->post('/server/group/save', 'Admin\\Server\\GroupController@save');
+ $router->post('/server/group/drop', 'Admin\\Server\\GroupController@drop');
+ $router->get ('/server/route/fetch', 'Admin\\Server\\RouteController@fetch');
+ $router->post('/server/route/save', 'Admin\\Server\\RouteController@save');
+ $router->post('/server/route/drop', 'Admin\\Server\\RouteController@drop');
+ $router->get ('/server/manage/getNodes', 'Admin\\Server\\ManageController@getNodes');
+ $router->post('/server/manage/sort', 'Admin\\Server\\ManageController@sort');
+ $router->group([
+ 'prefix' => 'server/trojan'
+ ], function ($router) {
+ $router->get ('fetch', 'Admin\\Server\\TrojanController@fetch');
+ $router->post('save', 'Admin\\Server\\TrojanController@save');
+ $router->post('drop', 'Admin\\Server\\TrojanController@drop');
+ $router->post('update', 'Admin\\Server\\TrojanController@update');
+ $router->post('copy', 'Admin\\Server\\TrojanController@copy');
+ $router->post('sort', 'Admin\\Server\\TrojanController@sort');
+ $router->post('viewConfig', 'Admin\\Server\\TrojanController@viewConfig');
+ });
+ $router->group([
+ 'prefix' => 'server/vmess'
+ ], function ($router) {
+ $router->get ('fetch', 'Admin\\Server\\VmessController@fetch');
+ $router->post('save', 'Admin\\Server\\VmessController@save');
+ $router->post('drop', 'Admin\\Server\\VmessController@drop');
+ $router->post('update', 'Admin\\Server\\VmessController@update');
+ $router->post('copy', 'Admin\\Server\\VmessController@copy');
+ $router->post('sort', 'Admin\\Server\\VmessController@sort');
+ });
+ $router->group([
+ 'prefix' => 'server/shadowsocks'
+ ], function ($router) {
+ $router->get ('fetch', 'Admin\\Server\\ShadowsocksController@fetch');
+ $router->post('save', 'Admin\\Server\\ShadowsocksController@save');
+ $router->post('drop', 'Admin\\Server\\ShadowsocksController@drop');
+ $router->post('update', 'Admin\\Server\\ShadowsocksController@update');
+ $router->post('copy', 'Admin\\Server\\ShadowsocksController@copy');
+ $router->post('sort', 'Admin\\Server\\ShadowsocksController@sort');
+ });
+ $router->group([
+ 'prefix' => 'server/hysteria'
+ ], function ($router) {
+ $router->get ('fetch', 'Admin\\Server\\HysteriaController@fetch');
+ $router->post('save', 'Admin\\Server\\HysteriaController@save');
+ $router->post('drop', 'Admin\\Server\\HysteriaController@drop');
+ $router->post('update', 'Admin\\Server\\HysteriaController@update');
+ $router->post('copy', 'Admin\\Server\\HysteriaController@copy');
+ $router->post('sort', 'Admin\\Server\\HysteriaController@sort');
+ });
+ // Order
+ $router->get ('/order/fetch', 'Admin\\OrderController@fetch');
+ $router->post('/order/update', 'Admin\\OrderController@update');
+ $router->post('/order/assign', 'Admin\\OrderController@assign');
+ $router->post('/order/paid', 'Admin\\OrderController@paid');
+ $router->post('/order/cancel', 'Admin\\OrderController@cancel');
+ $router->post('/order/detail', 'Admin\\OrderController@detail');
+ // User
+ $router->get ('/user/fetch', 'Admin\\UserController@fetch');
+ $router->post('/user/update', 'Admin\\UserController@update');
+ $router->get ('/user/getUserInfoById', 'Admin\\UserController@getUserInfoById');
+ $router->post('/user/generate', 'Admin\\UserController@generate');
+ $router->post('/user/dumpCSV', 'Admin\\UserController@dumpCSV');
+ $router->post('/user/sendMail', 'Admin\\UserController@sendMail');
+ $router->post('/user/ban', 'Admin\\UserController@ban');
+ $router->post('/user/resetSecret', 'Admin\\UserController@resetSecret');
+ $router->post('/user/setInviteUser', 'Admin\\UserController@setInviteUser');
+ // Stat
+ $router->get ('/stat/getStat', 'Admin\\StatController@getStat');
+ $router->get ('/stat/getOverride', 'Admin\\StatController@getOverride');
+ $router->get ('/stat/getServerLastRank', 'Admin\\StatController@getServerLastRank');
+ $router->get ('/stat/getOrder', 'Admin\\StatController@getOrder');
+ $router->get ('/stat/getStatUser', 'Admin\\StatController@getStatUser');
+ $router->get ('/stat/getRanking', 'Admin\\StatController@getRanking');
+ $router->get ('/stat/getStatRecord', 'Admin\\StatController@getStatRecord');
+ // Notice
+ $router->get ('/notice/fetch', 'Admin\\NoticeController@fetch');
+ $router->post('/notice/save', 'Admin\\NoticeController@save');
+ $router->post('/notice/update', 'Admin\\NoticeController@update');
+ $router->post('/notice/drop', 'Admin\\NoticeController@drop');
+ $router->post('/notice/show', 'Admin\\NoticeController@show');
+ // Ticket
+ $router->get ('/ticket/fetch', 'Admin\\TicketController@fetch');
+ $router->post('/ticket/reply', 'Admin\\TicketController@reply');
+ $router->post('/ticket/close', 'Admin\\TicketController@close');
+ // Coupon
+ $router->get ('/coupon/fetch', 'Admin\\CouponController@fetch');
+ $router->post('/coupon/generate', 'Admin\\CouponController@generate');
+ $router->post('/coupon/drop', 'Admin\\CouponController@drop');
+ $router->post('/coupon/show', 'Admin\\CouponController@show');
+ // Knowledge
+ $router->get ('/knowledge/fetch', 'Admin\\KnowledgeController@fetch');
+ $router->get ('/knowledge/getCategory', 'Admin\\KnowledgeController@getCategory');
+ $router->post('/knowledge/save', 'Admin\\KnowledgeController@save');
+ $router->post('/knowledge/show', 'Admin\\KnowledgeController@show');
+ $router->post('/knowledge/drop', 'Admin\\KnowledgeController@drop');
+ $router->post('/knowledge/sort', 'Admin\\KnowledgeController@sort');
+ // Payment
+ $router->get ('/payment/fetch', 'Admin\\PaymentController@fetch');
+ $router->get ('/payment/getPaymentMethods', 'Admin\\PaymentController@getPaymentMethods');
+ $router->post('/payment/getPaymentForm', 'Admin\\PaymentController@getPaymentForm');
+ $router->post('/payment/save', 'Admin\\PaymentController@save');
+ $router->post('/payment/drop', 'Admin\\PaymentController@drop');
+ $router->post('/payment/show', 'Admin\\PaymentController@show');
+ $router->post('/payment/sort', 'Admin\\PaymentController@sort');
+ // System
+ $router->get ('/system/getSystemStatus', 'Admin\\SystemController@getSystemStatus');
+ $router->get ('/system/getQueueStats', 'Admin\\SystemController@getQueueStats');
+ $router->get ('/system/getQueueWorkload', 'Admin\\SystemController@getQueueWorkload');
+ $router->get ('/system/getQueueMasters', '\\Laravel\\Horizon\\Http\\Controllers\\MasterSupervisorController@index');
+ $router->get ('/system/getSystemLog', 'Admin\\SystemController@getSystemLog');
+ // Theme
+ $router->get ('/theme/getThemes', 'Admin\\ThemeController@getThemes');
+ $router->post('/theme/saveThemeConfig', 'Admin\\ThemeController@saveThemeConfig');
+ $router->post('/theme/getThemeConfig', 'Admin\\ThemeController@getThemeConfig');
+ });
+ }
+}
diff --git a/app/Http/Routes/ClientRoute.php b/app/Http/Routes/ClientRoute.php
new file mode 100644
index 0000000..ebcb3bd
--- /dev/null
+++ b/app/Http/Routes/ClientRoute.php
@@ -0,0 +1,21 @@
+group([
+ 'prefix' => 'client',
+ 'middleware' => 'client'
+ ], function ($router) {
+ // Client
+ $router->get('/subscribe', 'Client\\ClientController@subscribe');
+ // App
+ $router->get('/app/getConfig', 'Client\\AppController@getConfig');
+ $router->get('/app/getVersion', 'Client\\AppController@getVersion');
+ });
+ }
+}
diff --git a/app/Http/Routes/GuestRoute.php b/app/Http/Routes/GuestRoute.php
new file mode 100644
index 0000000..1a58c5d
--- /dev/null
+++ b/app/Http/Routes/GuestRoute.php
@@ -0,0 +1,23 @@
+group([
+ 'prefix' => 'guest'
+ ], function ($router) {
+ // Plan
+ $router->get ('/plan/fetch', 'Guest\\PlanController@fetch');
+ // Telegram
+ $router->post('/telegram/webhook', 'Guest\\TelegramController@webhook');
+ // Payment
+ $router->match(['get', 'post'], '/payment/notify/{method}/{uuid}', 'Guest\\PaymentController@notify');
+ // Comm
+ $router->get ('/comm/config', 'Guest\\CommController@config');
+ });
+ }
+}
diff --git a/app/Http/Routes/PassportRoute.php b/app/Http/Routes/PassportRoute.php
new file mode 100644
index 0000000..b129eb2
--- /dev/null
+++ b/app/Http/Routes/PassportRoute.php
@@ -0,0 +1,25 @@
+group([
+ 'prefix' => 'passport'
+ ], function ($router) {
+ // Auth
+ $router->post('/auth/register', 'Passport\\AuthController@register');
+ $router->post('/auth/login', 'Passport\\AuthController@login');
+ $router->get ('/auth/token2Login', 'Passport\\AuthController@token2Login');
+ $router->post('/auth/forget', 'Passport\\AuthController@forget');
+ $router->post('/auth/getQuickLoginUrl', 'Passport\\AuthController@getQuickLoginUrl');
+ $router->post('/auth/loginWithMailLink', 'Passport\\AuthController@loginWithMailLink');
+ // Comm
+ $router->post('/comm/sendEmailVerify', 'Passport\\CommController@sendEmailVerify');
+ $router->post('/comm/pv', 'Passport\\CommController@pv');
+ });
+ }
+}
diff --git a/app/Http/Routes/ServerRoute.php b/app/Http/Routes/ServerRoute.php
new file mode 100644
index 0000000..d0c607b
--- /dev/null
+++ b/app/Http/Routes/ServerRoute.php
@@ -0,0 +1,19 @@
+group([
+ 'prefix' => 'server'
+ ], function ($router) {
+ $router->any('/{class}/{action}', function($class, $action) {
+ $ctrl = \App::make("\\App\\Http\\Controllers\\Server\\" . ucfirst($class) . "Controller");
+ return \App::call([$ctrl, $action]);
+ });
+ });
+ }
+}
diff --git a/app/Http/Routes/StaffRoute.php b/app/Http/Routes/StaffRoute.php
new file mode 100644
index 0000000..e9157bc
--- /dev/null
+++ b/app/Http/Routes/StaffRoute.php
@@ -0,0 +1,32 @@
+group([
+ 'prefix' => 'staff',
+ 'middleware' => 'staff'
+ ], function ($router) {
+ // Ticket
+ $router->get ('/ticket/fetch', 'Staff\\TicketController@fetch');
+ $router->post('/ticket/reply', 'Staff\\TicketController@reply');
+ $router->post('/ticket/close', 'Staff\\TicketController@close');
+ // User
+ $router->post('/user/update', 'Staff\\UserController@update');
+ $router->get ('/user/getUserInfoById', 'Staff\\UserController@getUserInfoById');
+ $router->post('/user/sendMail', 'Staff\\UserController@sendMail');
+ $router->post('/user/ban', 'Staff\\UserController@ban');
+ // Plan
+ $router->get ('/plan/fetch', 'Staff\\PlanController@fetch');
+ // Notice
+ $router->get ('/notice/fetch', 'Admin\\NoticeController@fetch');
+ $router->post('/notice/save', 'Admin\\NoticeController@save');
+ $router->post('/notice/update', 'Admin\\NoticeController@update');
+ $router->post('/notice/drop', 'Admin\\NoticeController@drop');
+ });
+ }
+}
diff --git a/app/Http/Routes/UserRoute.php b/app/Http/Routes/UserRoute.php
new file mode 100644
index 0000000..5b1efab
--- /dev/null
+++ b/app/Http/Routes/UserRoute.php
@@ -0,0 +1,64 @@
+group([
+ 'prefix' => 'user',
+ 'middleware' => 'user'
+ ], function ($router) {
+ // User
+ $router->get ('/resetSecurity', 'User\\UserController@resetSecurity');
+ $router->get ('/info', 'User\\UserController@info');
+ $router->post('/changePassword', 'User\\UserController@changePassword');
+ $router->post('/update', 'User\\UserController@update');
+ $router->get ('/getSubscribe', 'User\\UserController@getSubscribe');
+ $router->get ('/getStat', 'User\\UserController@getStat');
+ $router->get ('/checkLogin', 'User\\UserController@checkLogin');
+ $router->post('/transfer', 'User\\UserController@transfer');
+ $router->post('/getQuickLoginUrl', 'User\\UserController@getQuickLoginUrl');
+ $router->get ('/getActiveSession', 'User\\UserController@getActiveSession');
+ $router->post('/removeActiveSession', 'User\\UserController@removeActiveSession');
+ // Order
+ $router->post('/order/save', 'User\\OrderController@save');
+ $router->post('/order/checkout', 'User\\OrderController@checkout');
+ $router->get ('/order/check', 'User\\OrderController@check');
+ $router->get ('/order/detail', 'User\\OrderController@detail');
+ $router->get ('/order/fetch', 'User\\OrderController@fetch');
+ $router->get ('/order/getPaymentMethod', 'User\\OrderController@getPaymentMethod');
+ $router->post('/order/cancel', 'User\\OrderController@cancel');
+ // Plan
+ $router->get ('/plan/fetch', 'User\\PlanController@fetch');
+ // Invite
+ $router->get ('/invite/save', 'User\\InviteController@save');
+ $router->get ('/invite/fetch', 'User\\InviteController@fetch');
+ $router->get ('/invite/details', 'User\\InviteController@details');
+ // Notice
+ $router->get ('/notice/fetch', 'User\\NoticeController@fetch');
+ // Ticket
+ $router->post('/ticket/reply', 'User\\TicketController@reply');
+ $router->post('/ticket/close', 'User\\TicketController@close');
+ $router->post('/ticket/save', 'User\\TicketController@save');
+ $router->get ('/ticket/fetch', 'User\\TicketController@fetch');
+ $router->post('/ticket/withdraw', 'User\\TicketController@withdraw');
+ // Server
+ $router->get ('/server/fetch', 'User\\ServerController@fetch');
+ // Coupon
+ $router->post('/coupon/check', 'User\\CouponController@check');
+ // Telegram
+ $router->get ('/telegram/getBotInfo', 'User\\TelegramController@getBotInfo');
+ // Comm
+ $router->get ('/comm/config', 'User\\CommController@config');
+ $router->Post('/comm/getStripePublicKey', 'User\\CommController@getStripePublicKey');
+ // Knowledge
+ $router->get ('/knowledge/fetch', 'User\\KnowledgeController@fetch');
+ $router->get ('/knowledge/getCategory', 'User\\KnowledgeController@getCategory');
+ // Stat
+ $router->get ('/stat/getTrafficLog', 'User\\StatController@getTrafficLog');
+ });
+ }
+}
diff --git a/app/Jobs/OrderHandleJob.php b/app/Jobs/OrderHandleJob.php
new file mode 100644
index 0000000..d6626d1
--- /dev/null
+++ b/app/Jobs/OrderHandleJob.php
@@ -0,0 +1,54 @@
+onQueue('order_handle');
+ $this->order = Order::where('trade_no', $tradeNo)
+ ->lockForUpdate()
+ ->first();
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ if (!$this->order) return;
+ $orderService = new OrderService($this->order);
+ switch ($this->order->status) {
+ // cancel
+ case 0:
+ if ($this->order->created_at <= (time() - 1800)) {
+ $orderService->cancel();
+ }
+ break;
+ case 1:
+ $orderService->open();
+ break;
+ }
+ }
+}
diff --git a/app/Jobs/SendEmailJob.php b/app/Jobs/SendEmailJob.php
new file mode 100644
index 0000000..2f4fa15
--- /dev/null
+++ b/app/Jobs/SendEmailJob.php
@@ -0,0 +1,75 @@
+onQueue($queue);
+ $this->params = $params;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ if (config('v2board.email_host')) {
+ Config::set('mail.host', config('v2board.email_host', env('mail.host')));
+ Config::set('mail.port', config('v2board.email_port', env('mail.port')));
+ Config::set('mail.encryption', config('v2board.email_encryption', env('mail.encryption')));
+ Config::set('mail.username', config('v2board.email_username', env('mail.username')));
+ Config::set('mail.password', config('v2board.email_password', env('mail.password')));
+ Config::set('mail.from.address', config('v2board.email_from_address', env('mail.from.address')));
+ Config::set('mail.from.name', config('v2board.app_name', 'V2Board'));
+ }
+ $params = $this->params;
+ $email = $params['email'];
+ $subject = $params['subject'];
+ $params['template_name'] = 'mail.' . config('v2board.email_template', 'default') . '.' . $params['template_name'];
+ try {
+ Mail::send(
+ $params['template_name'],
+ $params['template_value'],
+ function ($message) use ($email, $subject) {
+ $message->to($email)->subject($subject);
+ }
+ );
+ } catch (\Exception $e) {
+ $error = $e->getMessage();
+ }
+
+ $log = [
+ 'email' => $params['email'],
+ 'subject' => $params['subject'],
+ 'template_name' => $params['template_name'],
+ 'error' => isset($error) ? $error : NULL
+ ];
+
+ MailLog::create($log);
+ $log['config'] = config('mail');
+ return $log;
+ }
+}
diff --git a/app/Jobs/SendTelegramJob.php b/app/Jobs/SendTelegramJob.php
new file mode 100644
index 0000000..8d30344
--- /dev/null
+++ b/app/Jobs/SendTelegramJob.php
@@ -0,0 +1,43 @@
+onQueue('send_telegram');
+ $this->telegramId = $telegramId;
+ $this->text = $text;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ $telegramService = new TelegramService();
+ $telegramService->sendMessage($this->telegramId, $this->text, 'markdown');
+ }
+}
diff --git a/app/Jobs/TrafficFetchJob.php b/app/Jobs/TrafficFetchJob.php
new file mode 100644
index 0000000..fd848d8
--- /dev/null
+++ b/app/Jobs/TrafficFetchJob.php
@@ -0,0 +1,57 @@
+onQueue('traffic_fetch');
+ $this->u = $u;
+ $this->d = $d;
+ $this->userId = $userId;
+ $this->server = $server;
+ $this->protocol = $protocol;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ $user = User::lockForUpdate()->find($this->userId);
+ if (!$user) return;
+
+ $user->t = time();
+ $user->u = $user->u + ($this->u * $this->server['rate']);
+ $user->d = $user->d + ($this->d * $this->server['rate']);
+ if (!$user->save()) {
+ info("流量更新失败\n未记录用户ID:{$this->userId}\n未记录上行:{$user->u}\n未记录下行:{$user->d}");
+ }
+ }
+}
diff --git a/app/Logging/MysqlLogger.php b/app/Logging/MysqlLogger.php
new file mode 100644
index 0000000..95b8185
--- /dev/null
+++ b/app/Logging/MysqlLogger.php
@@ -0,0 +1,11 @@
+pushHandler(new MysqlLoggerHandler());
+ });
+ }
+}
diff --git a/app/Logging/MysqlLoggerHandler.php b/app/Logging/MysqlLoggerHandler.php
new file mode 100644
index 0000000..c606b7a
--- /dev/null
+++ b/app/Logging/MysqlLoggerHandler.php
@@ -0,0 +1,44 @@
+all() ??[];
+ $log = [
+ 'title' => $record['message'],
+ 'level' => $record['level_name'],
+ 'host' => $record['request_host'] ?? request()->getSchemeAndHttpHost(),
+ 'uri' => $record['request_uri'] ?? request()->getRequestUri(),
+ 'method' => $record['request_method'] ?? request()->getMethod(),
+ 'ip' => request()->getClientIp(),
+ 'data' => json_encode($record['request_data']) ,
+ 'context' => isset($record['context']) ? json_encode($record['context']) : '',
+ 'created_at' => strtotime($record['datetime']),
+ 'updated_at' => strtotime($record['datetime']),
+ ];
+
+ LogModel::insert(
+ $log
+ );
+ }catch (\Exception $e){
+ Log::channel('daily')->error($e->getMessage().$e->getFile().$e->getTraceAsString());
+ }
+ }
+}
diff --git a/app/Models/CommissionLog.php b/app/Models/CommissionLog.php
new file mode 100644
index 0000000..744c5d5
--- /dev/null
+++ b/app/Models/CommissionLog.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/Coupon.php b/app/Models/Coupon.php
new file mode 100644
index 0000000..35a12a6
--- /dev/null
+++ b/app/Models/Coupon.php
@@ -0,0 +1,18 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'limit_plan_ids' => 'array',
+ 'limit_period' => 'array'
+ ];
+}
diff --git a/app/Models/InviteCode.php b/app/Models/InviteCode.php
new file mode 100644
index 0000000..8489897
--- /dev/null
+++ b/app/Models/InviteCode.php
@@ -0,0 +1,15 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/Knowledge.php b/app/Models/Knowledge.php
new file mode 100644
index 0000000..d24f46c
--- /dev/null
+++ b/app/Models/Knowledge.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/Log.php b/app/Models/Log.php
new file mode 100644
index 0000000..606b207
--- /dev/null
+++ b/app/Models/Log.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/MailLog.php b/app/Models/MailLog.php
new file mode 100644
index 0000000..0aef380
--- /dev/null
+++ b/app/Models/MailLog.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/Notice.php b/app/Models/Notice.php
new file mode 100644
index 0000000..dcab6e7
--- /dev/null
+++ b/app/Models/Notice.php
@@ -0,0 +1,17 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'tags' => 'array'
+ ];
+}
diff --git a/app/Models/Order.php b/app/Models/Order.php
new file mode 100755
index 0000000..576286e
--- /dev/null
+++ b/app/Models/Order.php
@@ -0,0 +1,17 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'surplus_order_ids' => 'array'
+ ];
+}
diff --git a/app/Models/Payment.php b/app/Models/Payment.php
new file mode 100644
index 0000000..5830367
--- /dev/null
+++ b/app/Models/Payment.php
@@ -0,0 +1,17 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'config' => 'array'
+ ];
+}
diff --git a/app/Models/Plan.php b/app/Models/Plan.php
new file mode 100755
index 0000000..4684396
--- /dev/null
+++ b/app/Models/Plan.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/ServerGroup.php b/app/Models/ServerGroup.php
new file mode 100755
index 0000000..24d38bf
--- /dev/null
+++ b/app/Models/ServerGroup.php
@@ -0,0 +1,15 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/ServerHysteria.php b/app/Models/ServerHysteria.php
new file mode 100755
index 0000000..11aac8f
--- /dev/null
+++ b/app/Models/ServerHysteria.php
@@ -0,0 +1,19 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'group_id' => 'array',
+ 'route_id' => 'array',
+ 'tags' => 'array'
+ ];
+}
diff --git a/app/Models/ServerLog.php b/app/Models/ServerLog.php
new file mode 100644
index 0000000..ef3590c
--- /dev/null
+++ b/app/Models/ServerLog.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/ServerRoute.php b/app/Models/ServerRoute.php
new file mode 100755
index 0000000..1ce46d0
--- /dev/null
+++ b/app/Models/ServerRoute.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ ];
+}
diff --git a/app/Models/ServerShadowsocks.php b/app/Models/ServerShadowsocks.php
new file mode 100644
index 0000000..4c83772
--- /dev/null
+++ b/app/Models/ServerShadowsocks.php
@@ -0,0 +1,20 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'group_id' => 'array',
+ 'route_id' => 'array',
+ 'tags' => 'array',
+ 'obfs_settings' => 'array'
+ ];
+}
diff --git a/app/Models/ServerStat.php b/app/Models/ServerStat.php
new file mode 100644
index 0000000..5006ded
--- /dev/null
+++ b/app/Models/ServerStat.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/ServerTrojan.php b/app/Models/ServerTrojan.php
new file mode 100644
index 0000000..b3b4170
--- /dev/null
+++ b/app/Models/ServerTrojan.php
@@ -0,0 +1,19 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'group_id' => 'array',
+ 'route_id' => 'array',
+ 'tags' => 'array'
+ ];
+}
diff --git a/app/Models/ServerVmess.php b/app/Models/ServerVmess.php
new file mode 100755
index 0000000..5a86557
--- /dev/null
+++ b/app/Models/ServerVmess.php
@@ -0,0 +1,23 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp',
+ 'group_id' => 'array',
+ 'route_id' => 'array',
+ 'tlsSettings' => 'array',
+ 'networkSettings' => 'array',
+ 'dnsSettings' => 'array',
+ 'ruleSettings' => 'array',
+ 'tags' => 'array'
+ ];
+}
diff --git a/app/Models/Stat.php b/app/Models/Stat.php
new file mode 100644
index 0000000..b0ed9ec
--- /dev/null
+++ b/app/Models/Stat.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/StatServer.php b/app/Models/StatServer.php
new file mode 100644
index 0000000..4ffa9b1
--- /dev/null
+++ b/app/Models/StatServer.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/StatUser.php b/app/Models/StatUser.php
new file mode 100644
index 0000000..07984d9
--- /dev/null
+++ b/app/Models/StatUser.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php
new file mode 100644
index 0000000..cd26663
--- /dev/null
+++ b/app/Models/Ticket.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/TicketMessage.php b/app/Models/TicketMessage.php
new file mode 100644
index 0000000..4673b33
--- /dev/null
+++ b/app/Models/TicketMessage.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Models/User.php b/app/Models/User.php
new file mode 100755
index 0000000..01c3eb5
--- /dev/null
+++ b/app/Models/User.php
@@ -0,0 +1,16 @@
+ 'timestamp',
+ 'updated_at' => 'timestamp'
+ ];
+}
diff --git a/app/Payments/AlipayF2F.php b/app/Payments/AlipayF2F.php
new file mode 100644
index 0000000..3fba0c1
--- /dev/null
+++ b/app/Payments/AlipayF2F.php
@@ -0,0 +1,93 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'app_id' => [
+ 'label' => '支付宝APPID',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'private_key' => [
+ 'label' => '支付宝私钥',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'public_key' => [
+ 'label' => '支付宝公钥',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'product_name' => [
+ 'label' => '自定义商品名称',
+ 'description' => '将会体现在支付宝账单中',
+ 'type' => 'input'
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ try {
+ $gateway = new \Library\AlipayF2F();
+ $gateway->setMethod('alipay.trade.precreate');
+ $gateway->setAppId($this->config['app_id']);
+ $gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
+ $gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
+ $gateway->setNotifyUrl($order['notify_url']);
+ $gateway->setBizContent([
+ 'subject' => $this->config['product_name'] ?? (config('v2board.app_name', 'V2Board') . ' - 订阅'),
+ 'out_trade_no' => $order['trade_no'],
+ 'total_amount' => $order['total_amount'] / 100
+ ]);
+ $gateway->send();
+ return [
+ 'type' => 0, // 0:qrcode 1:url
+ 'data' => $gateway->getQrCodeUrl()
+ ];
+ } catch (\Exception $e) {
+ abort(500, $e->getMessage());
+ }
+ }
+
+ public function notify($params)
+ {
+ if ($params['trade_status'] !== 'TRADE_SUCCESS') return false;
+ $gateway = new \Library\AlipayF2F();
+ $gateway->setAppId($this->config['app_id']);
+ $gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
+ $gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
+ try {
+ if ($gateway->verify($params)) {
+ /**
+ * Payment is successful
+ */
+ return [
+ 'trade_no' => $params['out_trade_no'],
+ 'callback_no' => $params['trade_no']
+ ];
+ } else {
+ /**
+ * Payment is not successful
+ */
+ return false;
+ }
+ } catch (\Exception $e) {
+ /**
+ * Payment is not successful
+ */
+ return false;
+ }
+ }
+}
diff --git a/app/Payments/BTCPay.php b/app/Payments/BTCPay.php
new file mode 100644
index 0000000..e9d6212
--- /dev/null
+++ b/app/Payments/BTCPay.php
@@ -0,0 +1,148 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'btcpay_url' => [
+ 'label' => 'API接口所在网址(包含最后的斜杠)',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'btcpay_storeId' => [
+ 'label' => 'storeId',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'btcpay_api_key' => [
+ 'label' => 'API KEY',
+ 'description' => '个人设置中的API KEY(非商店设置中的)',
+ 'type' => 'input',
+ ],
+ 'btcpay_webhook_key' => [
+ 'label' => 'WEBHOOK KEY',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ ];
+ }
+
+ public function pay($order) {
+
+ $params = [
+ 'jsonResponse' => true,
+ 'amount' => sprintf('%.2f', $order['total_amount'] / 100),
+ 'currency' => 'CNY',
+ 'metadata' => [
+ 'orderId' => $order['trade_no']
+ ]
+ ];
+
+ $params_string = @json_encode($params);
+
+ $ret_raw = self::_curlPost($this->config['btcpay_url'] . 'api/v1/stores/' . $this->config['btcpay_storeId'] . '/invoices', $params_string);
+
+ $ret = @json_decode($ret_raw, true);
+
+ if(empty($ret['checkoutLink'])) {
+ abort(500, "error!");
+ }
+ return [
+ 'type' => 1, // Redirect to url
+ 'data' => $ret['checkoutLink'],
+ ];
+ }
+
+ public function notify($params) {
+ $payload = trim(file_get_contents('php://input'));
+
+ $headers = getallheaders();
+
+ //IS Btcpay-Sig
+ //NOT BTCPay-Sig
+ //API doc is WRONG!
+ $headerName = 'Btcpay-Sig';
+ $signraturHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
+ $json_param = json_decode($payload, true);
+
+ $computedSignature = "sha256=" . \hash_hmac('sha256', $payload, $this->config['btcpay_webhook_key']);
+
+ if (!self::hashEqual($signraturHeader, $computedSignature)) {
+ abort(400, 'HMAC signature does not match');
+ return false;
+ }
+
+ //get order id store in metadata
+ $context = stream_context_create(array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' => "Authorization:" . "token " . $this->config['btcpay_api_key'] . "\r\n"
+ )
+ ));
+
+ $invoiceDetail = file_get_contents($this->config['btcpay_url'] . 'api/v1/stores/' . $this->config['btcpay_storeId'] . '/invoices/' . $json_param['invoiceId'], false, $context);
+ $invoiceDetail = json_decode($invoiceDetail, true);
+
+
+ $out_trade_no = $invoiceDetail['metadata']["orderId"];
+ $pay_trade_no=$json_param['invoiceId'];
+ return [
+ 'trade_no' => $out_trade_no,
+ 'callback_no' => $pay_trade_no
+ ];
+ http_response_code(200);
+ die('success');
+ }
+
+
+ private function _curlPost($url,$params=false){
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 300);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+ curl_setopt(
+ $ch, CURLOPT_HTTPHEADER, array('Authorization:' .'token '.$this->config['btcpay_api_key'], 'Content-Type: application/json')
+ );
+ $result = curl_exec($ch);
+ curl_close($ch);
+ return $result;
+ }
+
+
+ /**
+ * @param string $str1
+ * @param string $str2
+ * @return bool
+ */
+ private function hashEqual($str1, $str2)
+ {
+
+ if (function_exists('hash_equals')) {
+ return \hash_equals($str1, $str2);
+ }
+
+ if (strlen($str1) != strlen($str2)) {
+ return false;
+ } else {
+ $res = $str1 ^ $str2;
+ $ret = 0;
+
+ for ($i = strlen($res) - 1; $i >= 0; $i--) {
+ $ret |= ord($res[$i]);
+ }
+ return !$ret;
+ }
+ }
+
+}
+
diff --git a/app/Payments/CoinPayments.php b/app/Payments/CoinPayments.php
new file mode 100644
index 0000000..094bfc3
--- /dev/null
+++ b/app/Payments/CoinPayments.php
@@ -0,0 +1,104 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'coinpayments_merchant_id' => [
+ 'label' => 'Merchant ID',
+ 'description' => '商户 ID,填写您在 Account Settings 中得到的 ID',
+ 'type' => 'input',
+ ],
+ 'coinpayments_ipn_secret' => [
+ 'label' => 'IPN Secret',
+ 'description' => '通知密钥,填写您在 Merchant Settings 中自行设置的值',
+ 'type' => 'input',
+ ],
+ 'coinpayments_currency' => [
+ 'label' => '货币代码',
+ 'description' => '填写您的货币代码(大写),建议与 Merchant Settings 中的值相同',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+
+ // IPN notifications are slow, when the transaction is successful, we should return to the user center to avoid user confusion
+ $parseUrl = parse_url($order['return_url']);
+ $port = isset($parseUrl['port']) ? ":{$parseUrl['port']}" : '';
+ $successUrl = "{$parseUrl['scheme']}://{$parseUrl['host']}{$port}";
+
+ $params = [
+ 'cmd' => '_pay_simple',
+ 'reset' => 1,
+ 'merchant' => $this->config['coinpayments_merchant_id'],
+ 'item_name' => $order['trade_no'],
+ 'item_number' => $order['trade_no'],
+ 'want_shipping' => 0,
+ 'currency' => $this->config['coinpayments_currency'],
+ 'amountf' => sprintf('%.2f', $order['total_amount'] / 100),
+ 'success_url' => $successUrl,
+ 'cancel_url' => $order['return_url'],
+ 'ipn_url' => $order['notify_url']
+ ];
+
+ $params_string = http_build_query($params);
+
+ return [
+ 'type' => 1, // Redirect to url
+ 'data' => 'https://www.coinpayments.net/index.php?' . $params_string
+ ];
+ }
+
+ public function notify($params)
+ {
+
+ if (!isset($params['merchant']) || $params['merchant'] != trim($this->config['coinpayments_merchant_id'])) {
+ abort(500, 'No or incorrect Merchant ID passed');
+ }
+
+ $headers = getallheaders();
+
+ ksort($params);
+ reset($params);
+ $request = stripslashes(http_build_query($params));
+
+ $headerName = 'Hmac';
+ $signHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
+
+ $hmac = hash_hmac("sha512", $request, trim($this->config['coinpayments_ipn_secret']));
+
+ // if ($hmac != $signHeader) { <-- Use this if you are running a version of PHP below 5.6.0 without the hash_equals function
+ // abort(400, 'HMAC signature does not match');
+ // }
+
+ if (!hash_equals($hmac, $signHeader)) {
+ abort(400, 'HMAC signature does not match');
+ }
+
+ // HMAC Signature verified at this point, load some variables.
+ $status = $params['status'];
+ if ($status >= 100 || $status == 2) {
+ // payment is complete or queued for nightly payout, success
+ return [
+ 'trade_no' => $params['item_number'],
+ 'callback_no' => $params['txn_id'],
+ 'custom_result' => 'IPN OK'
+ ];
+ } else if ($status < 0) {
+ //payment error, this is usually final but payments will sometimes be reopened if there was no exchange rate conversion or with seller consent
+ abort(500, 'Payment Timed Out or Error');
+ } else {
+ //payment is pending, you can optionally add a note to the order page
+ die('IPN OK: pending');
+ }
+ }
+}
diff --git a/app/Payments/Coinbase.php b/app/Payments/Coinbase.php
new file mode 100644
index 0000000..ed6fba3
--- /dev/null
+++ b/app/Payments/Coinbase.php
@@ -0,0 +1,129 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'coinbase_url' => [
+ 'label' => '接口地址',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'coinbase_api_key' => [
+ 'label' => 'API KEY',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'coinbase_webhook_key' => [
+ 'label' => 'WEBHOOK KEY',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ ];
+ }
+
+ public function pay($order) {
+
+ $params = [
+ 'name' => '订阅套餐',
+ 'description' => '订单号 ' . $order['trade_no'],
+ 'pricing_type' => 'fixed_price',
+ 'local_price' => [
+ 'amount' => sprintf('%.2f', $order['total_amount'] / 100),
+ 'currency' => 'CNY'
+ ],
+ 'metadata' => [
+ "outTradeNo" => $order['trade_no'],
+ ],
+ ];
+
+ $params_string = http_build_query($params);
+
+ $ret_raw = self::_curlPost($this->config['coinbase_url'], $params_string);
+
+ $ret = @json_decode($ret_raw, true);
+
+ if(empty($ret['data']['hosted_url'])) {
+ abort(500, "error!");
+ }
+ return [
+ 'type' => 1,
+ 'data' => $ret['data']['hosted_url'],
+ ];
+ }
+
+ public function notify($params) {
+
+ $payload = trim(file_get_contents('php://input'));
+ $json_param = json_decode($payload, true);
+
+
+ $headerName = 'X-Cc-Webhook-Signature';
+ $headers = getallheaders();
+ $signatureHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
+ $computedSignature = \hash_hmac('sha256', $payload, $this->config['coinbase_webhook_key']);
+
+ if (!self::hashEqual($signatureHeader, $computedSignature)) {
+ abort(400, 'HMAC signature does not match');
+ }
+
+ $out_trade_no = $json_param['event']['data']['metadata']['outTradeNo'];
+ $pay_trade_no=$json_param['event']['id'];
+ return [
+ 'trade_no' => $out_trade_no,
+ 'callback_no' => $pay_trade_no
+ ];
+ http_response_code(200);
+ die('success');
+ }
+
+
+ private function _curlPost($url,$params=false){
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 300);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+ curl_setopt(
+ $ch, CURLOPT_HTTPHEADER, array('X-CC-Api-Key:' .$this->config['coinbase_api_key'], 'X-CC-Version: 2018-03-22')
+ );
+ $result = curl_exec($ch);
+ curl_close($ch);
+ return $result;
+ }
+
+
+ /**
+ * @param string $str1
+ * @param string $str2
+ * @return bool
+ */
+ public function hashEqual($str1, $str2)
+ {
+ if (function_exists('hash_equals')) {
+ return \hash_equals($str1, $str2);
+ }
+
+ if (strlen($str1) != strlen($str2)) {
+ return false;
+ } else {
+ $res = $str1 ^ $str2;
+ $ret = 0;
+
+ for ($i = strlen($res) - 1; $i >= 0; $i--) {
+ $ret |= ord($res[$i]);
+ }
+ return !$ret;
+ }
+ }
+
+}
+
diff --git a/app/Payments/EPay.php b/app/Payments/EPay.php
new file mode 100644
index 0000000..a87b97a
--- /dev/null
+++ b/app/Payments/EPay.php
@@ -0,0 +1,69 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'url' => [
+ 'label' => 'URL',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'pid' => [
+ 'label' => 'PID',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'key' => [
+ 'label' => 'KEY',
+ 'description' => '',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ $params = [
+ 'money' => $order['total_amount'] / 100,
+ 'name' => $order['trade_no'],
+ 'notify_url' => $order['notify_url'],
+ 'return_url' => $order['return_url'],
+ 'out_trade_no' => $order['trade_no'],
+ 'pid' => $this->config['pid']
+ ];
+ ksort($params);
+ reset($params);
+ $str = stripslashes(urldecode(http_build_query($params))) . $this->config['key'];
+ $params['sign'] = md5($str);
+ $params['sign_type'] = 'MD5';
+ return [
+ 'type' => 1, // 0:qrcode 1:url
+ 'data' => $this->config['url'] . '/submit.php?' . http_build_query($params)
+ ];
+ }
+
+ public function notify($params)
+ {
+ $sign = $params['sign'];
+ unset($params['sign']);
+ unset($params['sign_type']);
+ ksort($params);
+ reset($params);
+ $str = stripslashes(urldecode(http_build_query($params))) . $this->config['key'];
+ if ($sign !== md5($str)) {
+ return false;
+ }
+ return [
+ 'trade_no' => $params['out_trade_no'],
+ 'callback_no' => $params['trade_no']
+ ];
+ }
+}
diff --git a/app/Payments/MGate.php b/app/Payments/MGate.php
new file mode 100644
index 0000000..cb0be56
--- /dev/null
+++ b/app/Payments/MGate.php
@@ -0,0 +1,102 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'mgate_url' => [
+ 'label' => 'API地址',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'mgate_app_id' => [
+ 'label' => 'APPID',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'mgate_app_secret' => [
+ 'label' => 'AppSecret',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'mgate_source_currency' => [
+ 'label' => '源货币',
+ 'description' => '默认CNY',
+ 'type' => 'input'
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ $params = [
+ 'out_trade_no' => $order['trade_no'],
+ 'total_amount' => $order['total_amount'],
+ 'notify_url' => $order['notify_url'],
+ 'return_url' => $order['return_url']
+ ];
+ if (isset($this->config['mgate_source_currency'])) {
+ $params['source_currency'] = $this->config['mgate_source_currency'];
+ }
+ $params['app_id'] = $this->config['mgate_app_id'];
+ ksort($params);
+ $str = http_build_query($params) . $this->config['mgate_app_secret'];
+ $params['sign'] = md5($str);
+ $curl = new Curl();
+ $curl->setUserAgent('MGate');
+ $curl->setOpt(CURLOPT_SSL_VERIFYPEER, 0);
+ $curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
+ $result = $curl->response;
+ if (!$result) {
+ abort(500, '网络异常');
+ }
+ if ($curl->error) {
+ if (isset($result->errors)) {
+ $errors = (array)$result->errors;
+ abort(500, $errors[array_keys($errors)[0]][0]);
+ }
+ if (isset($result->message)) {
+ abort(500, $result->message);
+ }
+ abort(500, '未知错误');
+ }
+ $curl->close();
+ if (!isset($result->data->trade_no)) {
+ abort(500, '接口请求失败');
+ }
+ return [
+ 'type' => 1, // 0:qrcode 1:url
+ 'data' => $result->data->pay_url
+ ];
+ }
+
+ public function notify($params)
+ {
+ $sign = $params['sign'];
+ unset($params['sign']);
+ ksort($params);
+ reset($params);
+ $str = http_build_query($params) . $this->config['mgate_app_secret'];
+ if ($sign !== md5($str)) {
+ return false;
+ }
+ return [
+ 'trade_no' => $params['out_trade_no'],
+ 'callback_no' => $params['trade_no']
+ ];
+ }
+}
diff --git a/app/Payments/StripeAlipay.php b/app/Payments/StripeAlipay.php
new file mode 100644
index 0000000..4366913
--- /dev/null
+++ b/app/Payments/StripeAlipay.php
@@ -0,0 +1,117 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'currency' => [
+ 'label' => '货币单位',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_sk_live' => [
+ 'label' => 'SK_LIVE',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_webhook_key' => [
+ 'label' => 'WebHook密钥签名',
+ 'description' => '',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ $currency = $this->config['currency'];
+ $exchange = $this->exchange('CNY', strtoupper($currency));
+ if (!$exchange) {
+ abort(500, __('Currency conversion has timed out, please try again later'));
+ }
+ Stripe::setApiKey($this->config['stripe_sk_live']);
+ $source = Source::create([
+ 'amount' => floor($order['total_amount'] * $exchange),
+ 'currency' => $currency,
+ 'type' => 'alipay',
+ 'statement_descriptor' => $order['trade_no'],
+ 'metadata' => [
+ 'user_id' => $order['user_id'],
+ 'out_trade_no' => $order['trade_no'],
+ 'identifier' => ''
+ ],
+ 'redirect' => [
+ 'return_url' => $order['return_url']
+ ]
+ ]);
+ if (!$source['redirect']['url']) {
+ abort(500, __('Payment gateway request failed'));
+ }
+ return [
+ 'type' => 1,
+ 'data' => $source['redirect']['url']
+ ];
+ }
+
+ public function notify($params)
+ {
+ \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
+ try {
+ $event = \Stripe\Webhook::constructEvent(
+ file_get_contents('php://input'),
+ $_SERVER['HTTP_STRIPE_SIGNATURE'],
+ $this->config['stripe_webhook_key']
+ );
+ } catch (\Stripe\Error\SignatureVerification $e) {
+ abort(400);
+ }
+ switch ($event->type) {
+ case 'source.chargeable':
+ $object = $event->data->object;
+ \Stripe\Charge::create([
+ 'amount' => $object->amount,
+ 'currency' => $object->currency,
+ 'source' => $object->id,
+ 'metadata' => json_decode($object->metadata, true)
+ ]);
+ break;
+ case 'charge.succeeded':
+ $object = $event->data->object;
+ if ($object->status === 'succeeded') {
+ if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) {
+ die('order error');
+ }
+ $metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
+ $tradeNo = $metaData->out_trade_no;
+ return [
+ 'trade_no' => $tradeNo,
+ 'callback_no' => $object->id
+ ];
+ }
+ break;
+ default:
+ abort(500, 'event is not support');
+ }
+ die('success');
+ }
+
+ private function exchange($from, $to)
+ {
+ $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
+ $result = json_decode($result, true);
+ return $result['rates'][$to];
+ }
+}
diff --git a/app/Payments/StripeCheckout.php b/app/Payments/StripeCheckout.php
new file mode 100644
index 0000000..673f0cc
--- /dev/null
+++ b/app/Payments/StripeCheckout.php
@@ -0,0 +1,139 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'currency' => [
+ 'label' => '货币单位',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_sk_live' => [
+ 'label' => 'SK_LIVE',
+ 'description' => 'API 密钥',
+ 'type' => 'input',
+ ],
+ 'stripe_pk_live' => [
+ 'label' => 'PK_LIVE',
+ 'description' => 'API 公钥',
+ 'type' => 'input',
+ ],
+ 'stripe_webhook_key' => [
+ 'label' => 'WebHook 密钥签名',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_custom_field_name' => [
+ 'label' => '自定义字段名称',
+ 'description' => '例如可设置为“联系方式”,以便及时与客户取得联系',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ $currency = $this->config['currency'];
+ $exchange = $this->exchange('CNY', strtoupper($currency));
+ if (!$exchange) {
+ abort(500, __('Currency conversion has timed out, please try again later'));
+ }
+ $customFieldName = isset($this->config['stripe_custom_field_name']) ? $this->config['stripe_custom_field_name'] : 'Contact Infomation';
+
+ $params = [
+ 'success_url' => $order['return_url'],
+ 'cancel_url' => $order['return_url'],
+ 'client_reference_id' => $order['trade_no'],
+ 'line_items' => [
+ [
+ 'price_data' => [
+ 'currency' => $currency,
+ 'product_data' => [
+ 'name' => $order['trade_no']
+ ],
+ 'unit_amount' => floor($order['total_amount'] * $exchange)
+ ],
+ 'quantity' => 1
+ ]
+ ],
+ 'mode' => 'payment',
+ 'invoice_creation' => ['enabled' => true],
+ 'phone_number_collection' => ['enabled' => true],
+ 'custom_fields' => [
+ [
+ 'key' => 'contactinfo',
+ 'label' => ['type' => 'custom', 'custom' => $customFieldName],
+ 'type' => 'text',
+ ],
+ ],
+ // 'customer_email' => $user['email'] not support
+
+ ];
+
+ Stripe::setApiKey($this->config['stripe_sk_live']);
+ try {
+ $session = Session::create($params);
+ } catch (\Exception $e) {
+ info($e);
+ abort(500, "Failed to create order. Error: {$e->getMessage}");
+ }
+ return [
+ 'type' => 1, // 0:qrcode 1:url
+ 'data' => $session->url
+ ];
+ }
+
+ public function notify($params)
+ {
+ \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
+ try {
+ $event = \Stripe\Webhook::constructEvent(
+ file_get_contents('php://input'),
+ $_SERVER['HTTP_STRIPE_SIGNATURE'],
+ $this->config['stripe_webhook_key']
+ );
+ } catch (\Stripe\Error\SignatureVerification $e) {
+ abort(400);
+ }
+
+ switch ($event->type) {
+ case 'checkout.session.completed':
+ $object = $event->data->object;
+ if ($object->payment_status === 'paid') {
+ return [
+ 'trade_no' => $object->client_reference_id,
+ 'callback_no' => $object->payment_intent
+ ];
+ }
+ break;
+ case 'checkout.session.async_payment_succeeded':
+ $object = $event->data->object;
+ return [
+ 'trade_no' => $object->client_reference_id,
+ 'callback_no' => $object->payment_intent
+ ];
+ break;
+ default:
+ abort(500, 'event is not support');
+ }
+ die('success');
+ }
+
+ private function exchange($from, $to)
+ {
+ $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
+ $result = json_decode($result, true);
+ return $result['rates'][$to];
+ }
+}
diff --git a/app/Payments/StripeCredit.php b/app/Payments/StripeCredit.php
new file mode 100644
index 0000000..a066e83
--- /dev/null
+++ b/app/Payments/StripeCredit.php
@@ -0,0 +1,124 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'currency' => [
+ 'label' => '货币单位',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_sk_live' => [
+ 'label' => 'SK_LIVE',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_pk_live' => [
+ 'label' => 'PK_LIVE',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_webhook_key' => [
+ 'label' => 'WebHook密钥签名',
+ 'description' => '',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ info($order);
+ $currency = $this->config['currency'];
+ $exchange = $this->exchange('CNY', strtoupper($currency));
+ if (!$exchange) {
+ abort(500, __('Currency conversion has timed out, please try again later'));
+ }
+ Stripe::setApiKey($this->config['stripe_sk_live']);
+ try {
+ $charge = \Stripe\Charge::create([
+ 'amount' => floor($order['total_amount'] * $exchange),
+ 'currency' => $currency,
+ 'source' => $order['stripe_token'],
+ 'metadata' => [
+ 'user_id' => $order['user_id'],
+ 'out_trade_no' => $order['trade_no'],
+ 'identifier' => ''
+ ]
+ ]);
+ } catch (\Exception $e) {
+ info($e);
+ abort(500, __('Payment failed. Please check your credit card information'));
+ }
+ if (!$charge->paid) {
+ abort(500, __('Payment failed. Please check your credit card information'));
+ }
+ return [
+ 'type' => 2,
+ 'data' => $charge->paid
+ ];
+ }
+
+ public function notify($params)
+ {
+ \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
+ try {
+ $event = \Stripe\Webhook::constructEvent(
+ file_get_contents('php://input'),
+ $_SERVER['HTTP_STRIPE_SIGNATURE'],
+ $this->config['stripe_webhook_key']
+ );
+ } catch (\Stripe\Error\SignatureVerification $e) {
+ abort(400);
+ }
+ switch ($event->type) {
+ case 'source.chargeable':
+ $object = $event->data->object;
+ \Stripe\Charge::create([
+ 'amount' => $object->amount,
+ 'currency' => $object->currency,
+ 'source' => $object->id,
+ 'metadata' => json_decode($object->metadata, true)
+ ]);
+ break;
+ case 'charge.succeeded':
+ $object = $event->data->object;
+ if ($object->status === 'succeeded') {
+ if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) {
+ die('order error');
+ }
+ $metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
+ $tradeNo = $metaData->out_trade_no;
+ return [
+ 'trade_no' => $tradeNo,
+ 'callback_no' => $object->id
+ ];
+ }
+ break;
+ default:
+ abort(500, 'event is not support');
+ }
+ die('success');
+ }
+
+ private function exchange($from, $to)
+ {
+ $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
+ $result = json_decode($result, true);
+ return $result['rates'][$to];
+ }
+}
diff --git a/app/Payments/StripeWepay.php b/app/Payments/StripeWepay.php
new file mode 100644
index 0000000..8b0fc1b
--- /dev/null
+++ b/app/Payments/StripeWepay.php
@@ -0,0 +1,117 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'currency' => [
+ 'label' => '货币单位',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_sk_live' => [
+ 'label' => 'SK_LIVE',
+ 'description' => '',
+ 'type' => 'input',
+ ],
+ 'stripe_webhook_key' => [
+ 'label' => 'WebHook密钥签名',
+ 'description' => '',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ $currency = $this->config['currency'];
+ $exchange = $this->exchange('CNY', strtoupper($currency));
+ if (!$exchange) {
+ abort(500, __('Currency conversion has timed out, please try again later'));
+ }
+ Stripe::setApiKey($this->config['stripe_sk_live']);
+ $source = Source::create([
+ 'amount' => floor($order['total_amount'] * $exchange),
+ 'currency' => $currency,
+ 'type' => 'wechat',
+ 'statement_descriptor' => $order['trade_no'],
+ 'metadata' => [
+ 'user_id' => $order['user_id'],
+ 'out_trade_no' => $order['trade_no'],
+ 'identifier' => ''
+ ],
+ 'redirect' => [
+ 'return_url' => $order['return_url']
+ ]
+ ]);
+ if (!$source['wechat']['qr_code_url']) {
+ abort(500, __('Payment gateway request failed'));
+ }
+ return [
+ 'type' => 0,
+ 'data' => $source['wechat']['qr_code_url']
+ ];
+ }
+
+ public function notify($params)
+ {
+ \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
+ try {
+ $event = \Stripe\Webhook::constructEvent(
+ file_get_contents('php://input'),
+ $_SERVER['HTTP_STRIPE_SIGNATURE'],
+ $this->config['stripe_webhook_key']
+ );
+ } catch (\Stripe\Error\SignatureVerification $e) {
+ abort(400);
+ }
+ switch ($event->type) {
+ case 'source.chargeable':
+ $object = $event->data->object;
+ \Stripe\Charge::create([
+ 'amount' => $object->amount,
+ 'currency' => $object->currency,
+ 'source' => $object->id,
+ 'metadata' => json_decode($object->metadata, true)
+ ]);
+ break;
+ case 'charge.succeeded':
+ $object = $event->data->object;
+ if ($object->status === 'succeeded') {
+ if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) {
+ die('order error');
+ }
+ $metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
+ $tradeNo = $metaData->out_trade_no;
+ return [
+ 'trade_no' => $tradeNo,
+ 'callback_no' => $object->id
+ ];
+ }
+ break;
+ default:
+ abort(500, 'event is not support');
+ }
+ die('success');
+ }
+
+ private function exchange($from, $to)
+ {
+ $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
+ $result = json_decode($result, true);
+ return $result['rates'][$to];
+ }
+}
diff --git a/app/Payments/WechatPayNative.php b/app/Payments/WechatPayNative.php
new file mode 100644
index 0000000..99e5663
--- /dev/null
+++ b/app/Payments/WechatPayNative.php
@@ -0,0 +1,84 @@
+config = $config;
+ }
+
+ public function form()
+ {
+ return [
+ 'app_id' => [
+ 'label' => 'APPID',
+ 'description' => '绑定微信支付商户的APPID',
+ 'type' => 'input',
+ ],
+ 'mch_id' => [
+ 'label' => '商户号',
+ 'description' => '微信支付商户号',
+ 'type' => 'input',
+ ],
+ 'api_key' => [
+ 'label' => 'APIKEY(v1)',
+ 'description' => '',
+ 'type' => 'input',
+ ]
+ ];
+ }
+
+ public function pay($order)
+ {
+ $gateway = Omnipay::create('WechatPay_Native');
+ $gateway->setAppId($this->config['app_id']);
+ $gateway->setMchId($this->config['mch_id']);
+ $gateway->setApiKey($this->config['api_key']);
+ $gateway->setNotifyUrl($order['notify_url']);
+
+ $params = [
+ 'body' => $order['trade_no'],
+ 'out_trade_no' => $order['trade_no'],
+ 'total_fee' => $order['total_amount'],
+ 'spbill_create_ip' => '0.0.0.0',
+ 'fee_type' => 'CNY'
+ ];
+
+ $request = $gateway->purchase($params);
+ $response = $request->send();
+ $response = $response->getData();
+ if ($response['return_code'] !== 'SUCCESS') {
+ abort(500, $response['return_msg']);
+ }
+ return [
+ 'type' => 0,
+ 'data' => $response['code_url'],
+ 'custom_result' => ''
+ ];
+ }
+
+ public function notify($params)
+ {
+ $data = Helper::xml2array(file_get_contents('php://input'));
+ $gateway = Omnipay::create('WechatPay');
+ $gateway->setAppId($this->config['app_id']);
+ $gateway->setMchId($this->config['mch_id']);
+ $gateway->setApiKey($this->config['api_key']);
+ $response = $gateway->completePurchase([
+ 'request_params' => file_get_contents('php://input')
+ ])->send();
+
+ if (!$response->isPaid()) {
+ die('FAIL');
+ }
+
+ return [
+ 'trade_no' => $data['out_trade_no'],
+ 'callback_no' => $data['transaction_id']
+ ];
+ }
+}
diff --git a/app/Plugins/Telegram/Commands/Bind.php b/app/Plugins/Telegram/Commands/Bind.php
new file mode 100644
index 0000000..0242bec
--- /dev/null
+++ b/app/Plugins/Telegram/Commands/Bind.php
@@ -0,0 +1,38 @@
+is_private) return;
+ if (!isset($message->args[0])) {
+ abort(500, '参数有误,请携带订阅地址发送');
+ }
+ $subscribeUrl = $message->args[0];
+ $subscribeUrl = parse_url($subscribeUrl);
+ parse_str($subscribeUrl['query'], $query);
+ $token = $query['token'];
+ if (!$token) {
+ abort(500, '订阅地址无效');
+ }
+ $user = User::where('token', $token)->first();
+ if (!$user) {
+ abort(500, '用户不存在');
+ }
+ if ($user->telegram_id) {
+ abort(500, '该账号已经绑定了Telegram账号');
+ }
+ $user->telegram_id = $message->chat_id;
+ if (!$user->save()) {
+ abort(500, '设置失败');
+ }
+ $telegramService = $this->telegramService;
+ $telegramService->sendMessage($message->chat_id, '绑定成功');
+ }
+}
diff --git a/app/Plugins/Telegram/Commands/GetLatestUrl.php b/app/Plugins/Telegram/Commands/GetLatestUrl.php
new file mode 100644
index 0000000..0faff54
--- /dev/null
+++ b/app/Plugins/Telegram/Commands/GetLatestUrl.php
@@ -0,0 +1,21 @@
+telegramService;
+ $text = sprintf(
+ "%s的最新网址是:%s",
+ config('v2board.app_name', 'V2Board'),
+ config('v2board.app_url')
+ );
+ $telegramService->sendMessage($message->chat_id, $text, 'markdown');
+ }
+}
diff --git a/app/Plugins/Telegram/Commands/ReplyTicket.php b/app/Plugins/Telegram/Commands/ReplyTicket.php
new file mode 100644
index 0000000..e4a64d0
--- /dev/null
+++ b/app/Plugins/Telegram/Commands/ReplyTicket.php
@@ -0,0 +1,37 @@
+is_private) return;
+ $this->replayTicket($message, $match[1]);
+ }
+
+
+ private function replayTicket($msg, $ticketId)
+ {
+ $user = User::where('telegram_id', $msg->chat_id)->first();
+ if (!$user) {
+ abort(500, '用户不存在');
+ }
+ if (!$msg->text) return;
+ if (!($user->is_admin || $user->is_staff)) return;
+ $ticketService = new TicketService();
+ $ticketService->replyByAdmin(
+ $ticketId,
+ $msg->text,
+ $user->id
+ );
+ $telegramService = $this->telegramService;
+ $telegramService->sendMessage($msg->chat_id, "#`{$ticketId}` 的工单已回复成功", 'markdown');
+ $telegramService->sendMessageWithAdmin("#`{$ticketId}` 的工单已由 {$user->email} 进行回复", true);
+ }
+}
diff --git a/app/Plugins/Telegram/Commands/Traffic.php b/app/Plugins/Telegram/Commands/Traffic.php
new file mode 100644
index 0000000..d1412a0
--- /dev/null
+++ b/app/Plugins/Telegram/Commands/Traffic.php
@@ -0,0 +1,28 @@
+telegramService;
+ if (!$message->is_private) return;
+ $user = User::where('telegram_id', $message->chat_id)->first();
+ if (!$user) {
+ $telegramService->sendMessage($message->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
+ return;
+ }
+ $transferEnable = Helper::trafficConvert($user->transfer_enable);
+ $up = Helper::trafficConvert($user->u);
+ $down = Helper::trafficConvert($user->d);
+ $remaining = Helper::trafficConvert($user->transfer_enable - ($user->u + $user->d));
+ $text = "🚥流量查询\n———————————————\n计划流量:`{$transferEnable}`\n已用上行:`{$up}`\n已用下行:`{$down}`\n剩余流量:`{$remaining}`";
+ $telegramService->sendMessage($message->chat_id, $text, 'markdown');
+ }
+}
diff --git a/app/Plugins/Telegram/Commands/UnBind.php b/app/Plugins/Telegram/Commands/UnBind.php
new file mode 100644
index 0000000..6dde5cf
--- /dev/null
+++ b/app/Plugins/Telegram/Commands/UnBind.php
@@ -0,0 +1,26 @@
+is_private) return;
+ $user = User::where('telegram_id', $message->chat_id)->first();
+ $telegramService = $this->telegramService;
+ if (!$user) {
+ $telegramService->sendMessage($message->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
+ return;
+ }
+ $user->telegram_id = NULL;
+ if (!$user->save()) {
+ abort(500, '解绑失败');
+ }
+ $telegramService->sendMessage($message->chat_id, '解绑成功', 'markdown');
+ }
+}
diff --git a/app/Plugins/Telegram/Telegram.php b/app/Plugins/Telegram/Telegram.php
new file mode 100644
index 0000000..1797b04
--- /dev/null
+++ b/app/Plugins/Telegram/Telegram.php
@@ -0,0 +1,15 @@
+telegramService = new TelegramService();
+ }
+}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
new file mode 100755
index 0000000..d54a184
--- /dev/null
+++ b/app/Providers/AppServiceProvider.php
@@ -0,0 +1,28 @@
+app['view']->addNamespace('theme', public_path() . '/theme');
+ }
+}
diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php
new file mode 100755
index 0000000..3049068
--- /dev/null
+++ b/app/Providers/AuthServiceProvider.php
@@ -0,0 +1,30 @@
+ 'App\Policies\ModelPolicy',
+ ];
+
+ /**
+ * Register any authentication / authorization services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ $this->registerPolicies();
+
+ //
+ }
+}
diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php
new file mode 100755
index 0000000..395c518
--- /dev/null
+++ b/app/Providers/BroadcastServiceProvider.php
@@ -0,0 +1,21 @@
+email, [
+ //
+ ]);
+ });
+ }
+}
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
new file mode 100755
index 0000000..2b6a244
--- /dev/null
+++ b/app/Providers/RouteServiceProvider.php
@@ -0,0 +1,80 @@
+forceScheme('https');
+ }
+
+ parent::boot();
+ }
+
+ /**
+ * Define the routes for the application.
+ *
+ * @return void
+ */
+ public function map()
+ {
+ $this->mapApiRoutes();
+ $this->mapWebRoutes();
+
+ //
+ }
+
+ /**
+ * Define the "web" routes for the application.
+ *
+ * These routes all receive session state, CSRF protection, etc.
+ *
+ * @return void
+ */
+ protected function mapWebRoutes()
+ {
+ Route::middleware('web')
+ ->namespace($this->namespace)
+ ->group(base_path('routes/web.php'));
+ }
+
+ /**
+ * Define the "api" routes for the application.
+ *
+ * These routes are typically stateless.
+ *
+ * @return void
+ */
+ protected function mapApiRoutes()
+ {
+ Route::group([
+ 'prefix' => '/api/v1',
+ 'middleware' => 'api',
+ 'namespace' => $this->namespace
+ ], function ($router) {
+ foreach (glob(app_path('Http//Routes') . '/*.php') as $file) {
+ $this->app->make('App\\Http\\Routes\\' . basename($file, '.php'))->map($router);
+ }
+ });
+ }
+}
diff --git a/app/Services/AuthService.php b/app/Services/AuthService.php
new file mode 100644
index 0000000..078e9dd
--- /dev/null
+++ b/app/Services/AuthService.php
@@ -0,0 +1,104 @@
+user = $user;
+ }
+
+ public function generateAuthData(Request $request)
+ {
+ $guid = Helper::guid();
+ $authData = JWT::encode([
+ 'id' => $this->user->id,
+ 'session' => $guid,
+ ], config('app.key'), 'HS256');
+ self::addSession($this->user->id, $guid, [
+ 'ip' => $request->ip(),
+ 'login_at' => time(),
+ 'ua' => $request->userAgent()
+ ]);
+ return [
+ 'token' => $this->user->token,
+ 'is_admin' => $this->user->is_admin,
+ 'auth_data' => $authData
+ ];
+ }
+
+ public static function decryptAuthData($jwt)
+ {
+ try {
+ if (!Cache::has($jwt)) {
+ $data = (array)JWT::decode($jwt, new Key(config('app.key'), 'HS256'));
+ if (!self::checkSession($data['id'], $data['session'])) return false;
+ $user = User::select([
+ 'id',
+ 'email',
+ 'is_admin',
+ 'is_staff'
+ ])
+ ->find($data['id']);
+ if (!$user) return false;
+ Cache::put($jwt, $user->toArray(), 3600);
+ }
+ return Cache::get($jwt);
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ private static function checkSession($userId, $session)
+ {
+ $sessions = (array)Cache::get(CacheKey::get("USER_SESSIONS", $userId)) ?? [];
+ if (!in_array($session, array_keys($sessions))) return false;
+ return true;
+ }
+
+ private static function addSession($userId, $guid, $meta)
+ {
+ $cacheKey = CacheKey::get("USER_SESSIONS", $userId);
+ $sessions = (array)Cache::get($cacheKey, []);
+ $sessions[$guid] = $meta;
+ if (!Cache::put(
+ $cacheKey,
+ $sessions
+ )) return false;
+ return true;
+ }
+
+ public function getSessions()
+ {
+ return (array)Cache::get(CacheKey::get("USER_SESSIONS", $this->user->id), []);
+ }
+
+ public function removeSession($sessionId)
+ {
+ $cacheKey = CacheKey::get("USER_SESSIONS", $this->user->id);
+ $sessions = (array)Cache::get($cacheKey, []);
+ unset($sessions[$sessionId]);
+ if (!Cache::put(
+ $cacheKey,
+ $sessions
+ )) return false;
+ return true;
+ }
+
+ public function removeAllSession()
+ {
+ $cacheKey = CacheKey::get("USER_SESSIONS", $this->user->id);
+ return Cache::forget($cacheKey);
+ }
+}
diff --git a/app/Services/CouponService.php b/app/Services/CouponService.php
new file mode 100644
index 0000000..68c00c0
--- /dev/null
+++ b/app/Services/CouponService.php
@@ -0,0 +1,117 @@
+coupon = Coupon::where('code', $code)
+ ->lockForUpdate()
+ ->first();
+ }
+
+ public function use(Order $order):bool
+ {
+ $this->setPlanId($order->plan_id);
+ $this->setUserId($order->user_id);
+ $this->setPeriod($order->period);
+ $this->check();
+ switch ($this->coupon->type) {
+ case 1:
+ $order->discount_amount = $this->coupon->value;
+ break;
+ case 2:
+ $order->discount_amount = $order->total_amount * ($this->coupon->value / 100);
+ break;
+ }
+ if ($order->discount_amount > $order->total_amount) {
+ $order->discount_amount = $order->total_amount;
+ }
+ if ($this->coupon->limit_use !== NULL) {
+ if ($this->coupon->limit_use <= 0) return false;
+ $this->coupon->limit_use = $this->coupon->limit_use - 1;
+ if (!$this->coupon->save()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public function getId()
+ {
+ return $this->coupon->id;
+ }
+
+ public function getCoupon()
+ {
+ return $this->coupon;
+ }
+
+ public function setPlanId($planId)
+ {
+ $this->planId = $planId;
+ }
+
+ public function setUserId($userId)
+ {
+ $this->userId = $userId;
+ }
+
+ public function setPeriod($period)
+ {
+ $this->period = $period;
+ }
+
+ public function checkLimitUseWithUser():bool
+ {
+ $usedCount = Order::where('coupon_id', $this->coupon->id)
+ ->where('user_id', $this->userId)
+ ->whereNotIn('status', [0, 2])
+ ->count();
+ if ($usedCount >= $this->coupon->limit_use_with_user) return false;
+ return true;
+ }
+
+ public function check()
+ {
+ if (!$this->coupon || !$this->coupon->show) {
+ abort(500, __('Invalid coupon'));
+ }
+ if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {
+ abort(500, __('This coupon is no longer available'));
+ }
+ if (time() < $this->coupon->started_at) {
+ abort(500, __('This coupon has not yet started'));
+ }
+ if (time() > $this->coupon->ended_at) {
+ abort(500, __('This coupon has expired'));
+ }
+ if ($this->coupon->limit_plan_ids && $this->planId) {
+ if (!in_array($this->planId, $this->coupon->limit_plan_ids)) {
+ abort(500, __('The coupon code cannot be used for this subscription'));
+ }
+ }
+ if ($this->coupon->limit_period && $this->period) {
+ if (!in_array($this->period, $this->coupon->limit_period)) {
+ abort(500, __('The coupon code cannot be used for this period'));
+ }
+ }
+ if ($this->coupon->limit_use_with_user !== NULL && $this->userId) {
+ if (!$this->checkLimitUseWithUser()) {
+ abort(500, __('The coupon can only be used :limit_use_with_user per person', [
+ 'limit_use_with_user' => $this->coupon->limit_use_with_user
+ ]));
+ }
+ }
+ }
+}
diff --git a/app/Services/MailService.php b/app/Services/MailService.php
new file mode 100644
index 0000000..8e11fe7
--- /dev/null
+++ b/app/Services/MailService.php
@@ -0,0 +1,58 @@
+remind_traffic) return;
+ if (!$this->remindTrafficIsWarnValue($user->u, $user->d, $user->transfer_enable)) return;
+ $flag = CacheKey::get('LAST_SEND_EMAIL_REMIND_TRAFFIC', $user->id);
+ if (Cache::get($flag)) return;
+ if (!Cache::put($flag, 1, 24 * 3600)) return;
+ SendEmailJob::dispatch([
+ 'email' => $user->email,
+ 'subject' => __('The traffic usage in :app_name has reached 80%', [
+ 'app_name' => config('v2board.app_name', 'V2board')
+ ]),
+ 'template_name' => 'remindTraffic',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'url' => config('v2board.app_url')
+ ]
+ ]);
+ }
+
+ public function remindExpire(User $user)
+ {
+ if (!($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time())) return;
+ SendEmailJob::dispatch([
+ 'email' => $user->email,
+ 'subject' => __('The service in :app_name is about to expire', [
+ 'app_name' => config('v2board.app_name', 'V2board')
+ ]),
+ 'template_name' => 'remindExpire',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'url' => config('v2board.app_url')
+ ]
+ ]);
+ }
+
+ private function remindTrafficIsWarnValue($u, $d, $transfer_enable)
+ {
+ $ud = $u + $d;
+ if (!$ud) return false;
+ if (!$transfer_enable) return false;
+ $percentage = ($ud / $transfer_enable) * 100;
+ if ($percentage < 80) return false;
+ if ($percentage >= 100) return false;
+ return true;
+ }
+}
diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php
new file mode 100644
index 0000000..0133222
--- /dev/null
+++ b/app/Services/OrderService.php
@@ -0,0 +1,321 @@
+ 1,
+ 'quarter_price' => 3,
+ 'half_year_price' => 6,
+ 'year_price' => 12,
+ 'two_year_price' => 24,
+ 'three_year_price' => 36
+ ];
+ public $order;
+ public $user;
+
+ public function __construct(Order $order)
+ {
+ $this->order = $order;
+ }
+
+ public function open()
+ {
+ $order = $this->order;
+ $this->user = User::find($order->user_id);
+ $plan = Plan::find($order->plan_id);
+
+ if ($order->refund_amount) {
+ $this->user->balance = $this->user->balance + $order->refund_amount;
+ }
+ DB::beginTransaction();
+ if ($order->surplus_order_ids) {
+ try {
+ Order::whereIn('id', $order->surplus_order_ids)->update([
+ 'status' => 4
+ ]);
+ } catch (\Exception $e) {
+ DB::rollback();
+ abort(500, '开通失败');
+ }
+ }
+ switch ((string)$order->period) {
+ case 'onetime_price':
+ $this->buyByOneTime($plan);
+ break;
+ case 'reset_price':
+ $this->buyByResetTraffic();
+ break;
+ default:
+ $this->buyByPeriod($order, $plan);
+ }
+
+ switch ((int)$order->type) {
+ case 1:
+ $this->openEvent(config('v2board.new_order_event_id', 0));
+ break;
+ case 2:
+ $this->openEvent(config('v2board.renew_order_event_id', 0));
+ break;
+ case 3:
+ $this->openEvent(config('v2board.change_order_event_id', 0));
+ break;
+ }
+
+ $this->setSpeedLimit($plan->speed_limit);
+
+ if (!$this->user->save()) {
+ DB::rollBack();
+ abort(500, '开通失败');
+ }
+ $order->status = 3;
+ if (!$order->save()) {
+ DB::rollBack();
+ abort(500, '开通失败');
+ }
+
+ DB::commit();
+ }
+
+
+ public function setOrderType(User $user)
+ {
+ $order = $this->order;
+ if ($order->period === 'reset_price') {
+ $order->type = 4;
+ } else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id && ($user->expired_at > time() || $user->expired_at === NULL)) {
+ if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系客服或提交工单操作');
+ $order->type = 3;
+ if ((int)config('v2board.surplus_enable', 1)) $this->getSurplusValue($user, $order);
+ if ($order->surplus_amount >= $order->total_amount) {
+ $order->refund_amount = $order->surplus_amount - $order->total_amount;
+ $order->total_amount = 0;
+ } else {
+ $order->total_amount = $order->total_amount - $order->surplus_amount;
+ }
+ } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) { // 用户订阅未过期且购买订阅与当前订阅相同 === 续费
+ $order->type = 2;
+ } else { // 新购
+ $order->type = 1;
+ }
+ }
+
+ public function setVipDiscount(User $user)
+ {
+ $order = $this->order;
+ if ($user->discount) {
+ $order->discount_amount = $order->discount_amount + ($order->total_amount * ($user->discount / 100));
+ }
+ $order->total_amount = $order->total_amount - $order->discount_amount;
+ }
+
+ public function setInvite(User $user):void
+ {
+ $order = $this->order;
+ if ($user->invite_user_id && ($order->total_amount <= 0)) return;
+ $order->invite_user_id = $user->invite_user_id;
+ $inviter = User::find($user->invite_user_id);
+ if (!$inviter) return;
+ $isCommission = false;
+ switch ((int)$inviter->commission_type) {
+ case 0:
+ $commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
+ $isCommission = (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user)));
+ break;
+ case 1:
+ $isCommission = true;
+ break;
+ case 2:
+ $isCommission = !$this->haveValidOrder($user);
+ break;
+ }
+
+ if (!$isCommission) return;
+ if ($inviter && $inviter->commission_rate) {
+ $order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
+ } else {
+ $order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
+ }
+ }
+
+ private function haveValidOrder(User $user)
+ {
+ return Order::where('user_id', $user->id)
+ ->whereNotIn('status', [0, 2])
+ ->first();
+ }
+
+ private function getSurplusValue(User $user, Order $order)
+ {
+ if ($user->expired_at === NULL) {
+ $this->getSurplusValueByOneTime($user, $order);
+ } else {
+ $this->getSurplusValueByPeriod($user, $order);
+ }
+ }
+
+
+ private function getSurplusValueByOneTime(User $user, Order $order)
+ {
+ $lastOneTimeOrder = Order::where('user_id', $user->id)
+ ->where('period', 'onetime_price')
+ ->where('status', 3)
+ ->orderBy('id', 'DESC')
+ ->first();
+ if (!$lastOneTimeOrder) return;
+ $nowUserTraffic = $user->transfer_enable / 1073741824;
+ if (!$nowUserTraffic) return;
+ $paidTotalAmount = ($lastOneTimeOrder->total_amount + $lastOneTimeOrder->balance_amount);
+ if (!$paidTotalAmount) return;
+ $trafficUnitPrice = $paidTotalAmount / $nowUserTraffic;
+ $notUsedTraffic = $nowUserTraffic - (($user->u + $user->d) / 1073741824);
+ $result = $trafficUnitPrice * $notUsedTraffic;
+ $orderModel = Order::where('user_id', $user->id)->where('period', '!=', 'reset_price')->where('status', 3);
+ $order->surplus_amount = $result > 0 ? $result : 0;
+ $order->surplus_order_ids = array_column($orderModel->get()->toArray(), 'id');
+ }
+
+ private function getSurplusValueByPeriod(User $user, Order $order)
+ {
+ $orders = Order::where('user_id', $user->id)
+ ->where('period', '!=', 'reset_price')
+ ->where('period', '!=', 'onetime_price')
+ ->where('status', 3)
+ ->get()
+ ->toArray();
+ if (!$orders) return;
+ $orderAmountSum = 0;
+ $orderMonthSum = 0;
+ $lastValidateAt = 0;
+ foreach ($orders as $item) {
+ $period = self::STR_TO_TIME[$item['period']];
+ if (strtotime("+{$period} month", $item['created_at']) < time()) continue;
+ $lastValidateAt = $item['created_at'];
+ $orderMonthSum = $period + $orderMonthSum;
+ $orderAmountSum = $orderAmountSum + ($item['total_amount'] + $item['balance_amount'] + $item['surplus_amount'] - $item['refund_amount']);
+ }
+ if (!$lastValidateAt) return;
+ $expiredAtByOrder = strtotime("+{$orderMonthSum} month", $lastValidateAt);
+ if ($expiredAtByOrder < time()) return;
+ $orderSurplusSecond = $expiredAtByOrder - time();
+ $orderRangeSecond = $expiredAtByOrder - $lastValidateAt;
+ $avgPrice = $orderAmountSum / $orderRangeSecond;
+ $orderSurplusAmount = $avgPrice * $orderSurplusSecond;
+ if (!$orderSurplusSecond || !$orderSurplusAmount) return;
+ $order->surplus_amount = $orderSurplusAmount > 0 ? $orderSurplusAmount : 0;
+ $order->surplus_order_ids = array_column($orders, 'id');
+ }
+
+ public function paid(string $callbackNo)
+ {
+ $order = $this->order;
+ if ($order->status !== 0) return true;
+ $order->status = 1;
+ $order->paid_at = time();
+ $order->callback_no = $callbackNo;
+ if (!$order->save()) return false;
+ try {
+ OrderHandleJob::dispatchNow($order->trade_no);
+ } catch (\Exception $e) {
+ return false;
+ }
+ return true;
+ }
+
+ public function cancel():bool
+ {
+ $order = $this->order;
+ DB::beginTransaction();
+ $order->status = 2;
+ if (!$order->save()) {
+ DB::rollBack();
+ return false;
+ }
+ if ($order->balance_amount) {
+ $userService = new UserService();
+ if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
+ DB::rollBack();
+ return false;
+ }
+ }
+ DB::commit();
+ return true;
+ }
+
+ private function setSpeedLimit($speedLimit)
+ {
+ $this->user->speed_limit = $speedLimit;
+ }
+
+ private function buyByResetTraffic()
+ {
+ $this->user->u = 0;
+ $this->user->d = 0;
+ }
+
+ private function buyByPeriod(Order $order, Plan $plan)
+ {
+ // change plan process
+ if ((int)$order->type === 3) {
+ $this->user->expired_at = time();
+ }
+ $this->user->transfer_enable = $plan->transfer_enable * 1073741824;
+ // 从一次性转换到循环
+ if ($this->user->expired_at === NULL) $this->buyByResetTraffic();
+ // 新购
+ if ($order->type === 1) $this->buyByResetTraffic();
+ $this->user->plan_id = $plan->id;
+ $this->user->group_id = $plan->group_id;
+ $this->user->expired_at = $this->getTime($order->period, $this->user->expired_at);
+ }
+
+ private function buyByOneTime(Plan $plan)
+ {
+ $this->buyByResetTraffic();
+ $this->user->transfer_enable = $plan->transfer_enable * 1073741824;
+ $this->user->plan_id = $plan->id;
+ $this->user->group_id = $plan->group_id;
+ $this->user->expired_at = NULL;
+ }
+
+ private function getTime($str, $timestamp)
+ {
+ if ($timestamp < time()) {
+ $timestamp = time();
+ }
+ switch ($str) {
+ case 'month_price':
+ return strtotime('+1 month', $timestamp);
+ case 'quarter_price':
+ return strtotime('+3 month', $timestamp);
+ case 'half_year_price':
+ return strtotime('+6 month', $timestamp);
+ case 'year_price':
+ return strtotime('+12 month', $timestamp);
+ case 'two_year_price':
+ return strtotime('+24 month', $timestamp);
+ case 'three_year_price':
+ return strtotime('+36 month', $timestamp);
+ }
+ }
+
+ private function openEvent($eventId)
+ {
+ switch ((int) $eventId) {
+ case 0:
+ break;
+ case 1:
+ $this->buyByResetTraffic();
+ break;
+ }
+ }
+}
diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php
new file mode 100644
index 0000000..7b9d409
--- /dev/null
+++ b/app/Services/PaymentService.php
@@ -0,0 +1,67 @@
+method = $method;
+ $this->class = '\\App\\Payments\\' . $this->method;
+ if (!class_exists($this->class)) abort(500, 'gate is not found');
+ if ($id) $payment = Payment::find($id)->toArray();
+ if ($uuid) $payment = Payment::where('uuid', $uuid)->first()->toArray();
+ $this->config = [];
+ if (isset($payment)) {
+ $this->config = $payment['config'];
+ $this->config['enable'] = $payment['enable'];
+ $this->config['id'] = $payment['id'];
+ $this->config['uuid'] = $payment['uuid'];
+ $this->config['notify_domain'] = $payment['notify_domain'];
+ };
+ $this->payment = new $this->class($this->config);
+ }
+
+ public function notify($params)
+ {
+ if (!$this->config['enable']) abort(500, 'gate is not enable');
+ return $this->payment->notify($params);
+ }
+
+ public function pay($order)
+ {
+ // custom notify domain name
+ $notifyUrl = url("/api/v1/guest/payment/notify/{$this->method}/{$this->config['uuid']}");
+ if ($this->config['notify_domain']) {
+ $parseUrl = parse_url($notifyUrl);
+ $notifyUrl = $this->config['notify_domain'] . $parseUrl['path'];
+ }
+
+ return $this->payment->pay([
+ 'notify_url' => $notifyUrl,
+ 'return_url' => config('v2board.app_url') . '/#/order/' . $order['trade_no'],
+ 'trade_no' => $order['trade_no'],
+ 'total_amount' => $order['total_amount'],
+ 'user_id' => $order['user_id'],
+ 'stripe_token' => $order['stripe_token']
+ ]);
+ }
+
+ public function form()
+ {
+ $form = $this->payment->form();
+ $keys = array_keys($form);
+ foreach ($keys as $key) {
+ if (isset($this->config[$key])) $form[$key]['value'] = $this->config[$key];
+ }
+ return $form;
+ }
+}
diff --git a/app/Services/PlanService.php b/app/Services/PlanService.php
new file mode 100644
index 0000000..9689679
--- /dev/null
+++ b/app/Services/PlanService.php
@@ -0,0 +1,41 @@
+plan = Plan::lockForUpdate()->find($planId);
+ }
+
+ public function haveCapacity(): bool
+ {
+ if ($this->plan->capacity_limit === NULL) return true;
+ $count = self::countActiveUsers();
+ $count = $count[$this->plan->id]['count'] ?? 0;
+ return ($this->plan->capacity_limit - $count) > 0;
+ }
+
+ public static function countActiveUsers()
+ {
+ return User::select(
+ DB::raw("plan_id"),
+ DB::raw("count(*) as count")
+ )
+ ->where('plan_id', '!=', NULL)
+ ->where(function ($query) {
+ $query->where('expired_at', '>=', time())
+ ->orWhere('expired_at', NULL);
+ })
+ ->groupBy("plan_id")
+ ->get()
+ ->keyBy('plan_id');
+ }
+}
diff --git a/app/Services/ServerService.php b/app/Services/ServerService.php
new file mode 100644
index 0000000..0a424b5
--- /dev/null
+++ b/app/Services/ServerService.php
@@ -0,0 +1,279 @@
+get();
+ foreach ($vmess as $key => $v) {
+ if (!$v['show']) continue;
+ $vmess[$key]['type'] = 'vmess';
+ if (!in_array($user->group_id, $vmess[$key]['group_id'])) continue;
+ if (strpos($vmess[$key]['port'], '-') !== false) {
+ $vmess[$key]['port'] = Helper::randomPort($vmess[$key]['port']);
+ }
+ if ($vmess[$key]['parent_id']) {
+ $vmess[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_VMESS_LAST_CHECK_AT', $vmess[$key]['parent_id']));
+ } else {
+ $vmess[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_VMESS_LAST_CHECK_AT', $vmess[$key]['id']));
+ }
+ $servers[] = $vmess[$key]->toArray();
+ }
+
+
+ return $servers;
+ }
+
+ public function getAvailableTrojan(User $user):array
+ {
+ $servers = [];
+ $model = ServerTrojan::orderBy('sort', 'ASC');
+ $trojan = $model->get();
+ foreach ($trojan as $key => $v) {
+ if (!$v['show']) continue;
+ $trojan[$key]['type'] = 'trojan';
+ if (!in_array($user->group_id, $trojan[$key]['group_id'])) continue;
+ if (strpos($trojan[$key]['port'], '-') !== false) {
+ $trojan[$key]['port'] = Helper::randomPort($trojan[$key]['port']);
+ }
+ if ($trojan[$key]['parent_id']) {
+ $trojan[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$key]['parent_id']));
+ } else {
+ $trojan[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$key]['id']));
+ }
+ $servers[] = $trojan[$key]->toArray();
+ }
+ return $servers;
+ }
+
+ public function getAvailableHysteria(User $user)
+ {
+ $availableServers = [];
+ $model = ServerHysteria::orderBy('sort', 'ASC');
+ $servers = $model->get()->keyBy('id');
+ foreach ($servers as $key => $v) {
+ if (!$v['show']) continue;
+ $servers[$key]['type'] = 'hysteria';
+ $servers[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_HYSTERIA_LAST_CHECK_AT', $v['id']));
+ if (!in_array($user->group_id, $v['group_id'])) continue;
+ if (strpos($v['port'], '-') !== false) {
+ $servers[$key]['port'] = Helper::randomPort($v['port']);
+ }
+ if (isset($servers[$v['parent_id']])) {
+ $servers[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_HYSTERIA_LAST_CHECK_AT', $v['parent_id']));
+ $servers[$key]['created_at'] = $servers[$v['parent_id']]['created_at'];
+ }
+ $servers[$key]['server_key'] = Helper::getServerKey($servers[$key]['created_at'], 16);
+ $availableServers[] = $servers[$key]->toArray();
+ }
+ return $availableServers;
+ }
+
+ public function getAvailableShadowsocks(User $user)
+ {
+ $servers = [];
+ $model = ServerShadowsocks::orderBy('sort', 'ASC');
+ $shadowsocks = $model->get()->keyBy('id');
+ foreach ($shadowsocks as $key => $v) {
+ if (!$v['show']) continue;
+ $shadowsocks[$key]['type'] = 'shadowsocks';
+ $shadowsocks[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $v['id']));
+ if (!in_array($user->group_id, $v['group_id'])) continue;
+ if (strpos($v['port'], '-') !== false) {
+ $shadowsocks[$key]['port'] = Helper::randomPort($v['port']);
+ }
+ if (isset($shadowsocks[$v['parent_id']])) {
+ $shadowsocks[$key]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $v['parent_id']));
+ $shadowsocks[$key]['created_at'] = $shadowsocks[$v['parent_id']]['created_at'];
+ }
+ $servers[] = $shadowsocks[$key]->toArray();
+ }
+ return $servers;
+ }
+
+ public function getAvailableServers(User $user)
+ {
+ $servers = array_merge(
+ $this->getAvailableShadowsocks($user),
+ $this->getAvailableVmess($user),
+ $this->getAvailableTrojan($user),
+ $this->getAvailableHysteria($user)
+ );
+ $tmp = array_column($servers, 'sort');
+ array_multisort($tmp, SORT_ASC, $servers);
+ return array_map(function ($server) {
+ $server['port'] = (int)$server['port'];
+ $server['is_online'] = (time() - 300 > $server['last_check_at']) ? 0 : 1;
+ $server['cache_key'] = "{$server['type']}-{$server['id']}-{$server['updated_at']}-{$server['is_online']}";
+ return $server;
+ }, $servers);
+ }
+
+ public function getAvailableUsers($groupId)
+ {
+ return User::whereIn('group_id', $groupId)
+ ->whereRaw('u + d < transfer_enable')
+ ->where(function ($query) {
+ $query->where('expired_at', '>=', time())
+ ->orWhere('expired_at', NULL);
+ })
+ ->where('banned', 0)
+ ->select([
+ 'id',
+ 'uuid',
+ 'speed_limit'
+ ])
+ ->get();
+ }
+
+ public function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method)
+ {
+ if (($u + $d) < 10240) return true;
+ $timestamp = strtotime(date('Y-m-d'));
+ $serverLog = ServerLog::where('log_at', '>=', $timestamp)
+ ->where('log_at', '<', $timestamp + 3600)
+ ->where('server_id', $serverId)
+ ->where('user_id', $userId)
+ ->where('rate', $rate)
+ ->where('method', $method)
+ ->first();
+ if ($serverLog) {
+ try {
+ $serverLog->increment('u', $u);
+ $serverLog->increment('d', $d);
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ } else {
+ $serverLog = new ServerLog();
+ $serverLog->user_id = $userId;
+ $serverLog->server_id = $serverId;
+ $serverLog->u = $u;
+ $serverLog->d = $d;
+ $serverLog->rate = $rate;
+ $serverLog->log_at = $timestamp;
+ $serverLog->method = $method;
+ return $serverLog->save();
+ }
+ }
+
+ public function getAllShadowsocks()
+ {
+ $servers = ServerShadowsocks::orderBy('sort', 'ASC')
+ ->get()
+ ->toArray();
+ foreach ($servers as $k => $v) {
+ $servers[$k]['type'] = 'shadowsocks';
+ }
+ return $servers;
+ }
+
+ public function getAllVMess()
+ {
+ $servers = ServerVmess::orderBy('sort', 'ASC')
+ ->get()
+ ->toArray();
+ foreach ($servers as $k => $v) {
+ $servers[$k]['type'] = 'vmess';
+ }
+ return $servers;
+ }
+
+ public function getAllTrojan()
+ {
+ $servers = ServerTrojan::orderBy('sort', 'ASC')
+ ->get()
+ ->toArray();
+ foreach ($servers as $k => $v) {
+ $servers[$k]['type'] = 'trojan';
+ }
+ return $servers;
+ }
+
+ public function getAllHysteria()
+ {
+ $servers = ServerHysteria::orderBy('sort', 'ASC')
+ ->get()
+ ->toArray();
+ foreach ($servers as $k => $v) {
+ $servers[$k]['type'] = 'hysteria';
+ }
+ return $servers;
+ }
+
+ private function mergeData(&$servers)
+ {
+ foreach ($servers as $k => $v) {
+ $serverType = strtoupper($v['type']);
+ $servers[$k]['online'] = Cache::get(CacheKey::get("SERVER_{$serverType}_ONLINE_USER", $v['parent_id'] ?? $v['id']));
+ $servers[$k]['last_check_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_CHECK_AT", $v['parent_id'] ?? $v['id']));
+ $servers[$k]['last_push_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_PUSH_AT", $v['parent_id'] ?? $v['id']));
+ if ((time() - 300) >= $servers[$k]['last_check_at']) {
+ $servers[$k]['available_status'] = 0;
+ } else if ((time() - 300) >= $servers[$k]['last_push_at']) {
+ $servers[$k]['available_status'] = 1;
+ } else {
+ $servers[$k]['available_status'] = 2;
+ }
+ }
+ }
+
+ public function getAllServers()
+ {
+ $servers = array_merge(
+ $this->getAllShadowsocks(),
+ $this->getAllVMess(),
+ $this->getAllTrojan(),
+ $this->getAllHysteria()
+ );
+ $this->mergeData($servers);
+ $tmp = array_column($servers, 'sort');
+ array_multisort($tmp, SORT_ASC, $servers);
+ return $servers;
+ }
+
+ public function getRoutes(array $routeIds)
+ {
+ $routes = ServerRoute::select(['id', 'match', 'action', 'action_value'])->whereIn('id', $routeIds)->get();
+ // TODO: remove on 1.8.0
+ foreach ($routes as $k => $route) {
+ $array = json_decode($route->match, true);
+ if (is_array($array)) $routes[$k]['match'] = $array;
+ }
+ // TODO: remove on 1.8.0
+ return $routes;
+ }
+
+ public function getServer($serverId, $serverType)
+ {
+ switch ($serverType) {
+ case 'vmess':
+ return ServerVmess::find($serverId);
+ case 'shadowsocks':
+ return ServerShadowsocks::find($serverId);
+ case 'trojan':
+ return ServerTrojan::find($serverId);
+ case 'hysteria':
+ return ServerHysteria::find($serverId);
+ default:
+ return false;
+ }
+ }
+}
diff --git a/app/Services/StatisticalService.php b/app/Services/StatisticalService.php
new file mode 100644
index 0000000..d403912
--- /dev/null
+++ b/app/Services/StatisticalService.php
@@ -0,0 +1,283 @@
+startAt = $timestamp;
+ }
+
+ public function setEndAt($timestamp) {
+ $this->endAt = $timestamp;
+ }
+
+ public function setServerStats() {
+ $this->serverStats = Cache::get("stat_server_{$this->startAt}");
+ $this->serverStats = json_decode($this->serverStats, true) ?? [];
+ if (!is_array($this->serverStats)) {
+ $this->serverStats = [];
+ }
+ }
+
+ public function setUserStats() {
+ $this->userStats = Cache::get("stat_user_{$this->startAt}");
+ $this->userStats = json_decode($this->userStats, true) ?? [];
+ if (!is_array($this->userStats)) {
+ $this->userStats = [];
+ }
+ }
+
+ public function generateStatData(): array
+ {
+ $startAt = $this->startAt;
+ $endAt = $this->endAt;
+ if (!$startAt || !$endAt) {
+ $startAt = strtotime(date('Y-m-d'));
+ $endAt = strtotime('+1 day', $startAt);
+ }
+ $data = [];
+ $data['order_count'] = Order::where('created_at', '>=', $startAt)
+ ->where('created_at', '<', $endAt)
+ ->count();
+ $data['order_total'] = Order::where('created_at', '>=', $startAt)
+ ->where('created_at', '<', $endAt)
+ ->sum('total_amount');
+ $data['paid_count'] = Order::where('paid_at', '>=', $startAt)
+ ->where('paid_at', '<', $endAt)
+ ->whereNotIn('status', [0, 2])
+ ->count();
+ $data['paid_total'] = Order::where('paid_at', '>=', $startAt)
+ ->where('paid_at', '<', $endAt)
+ ->whereNotIn('status', [0, 2])
+ ->sum('total_amount');
+ $commissionLogBuilder = CommissionLog::where('created_at', '>=', $startAt)
+ ->where('created_at', '<', $endAt);
+ $data['commission_count'] = $commissionLogBuilder->count();
+ $data['commission_total'] = $commissionLogBuilder->sum('get_amount');
+ $data['register_count'] = User::where('created_at', '>=', $startAt)
+ ->where('created_at', '<', $endAt)
+ ->count();
+ $data['invite_count'] = User::where('created_at', '>=', $startAt)
+ ->where('created_at', '<', $endAt)
+ ->whereNotNull('invite_user_id')
+ ->count();
+ $data['transfer_used_total'] = StatServer::where('created_at', '>=', $startAt)
+ ->where('created_at', '<', $endAt)
+ ->select(DB::raw('SUM(u) + SUM(d) as total'))
+ ->value('total') ?? 0;
+ return $data;
+ }
+
+ public function statServer($serverId, $serverType, $u, $d)
+ {
+ $this->serverStats[$serverType] = $this->serverStats[$serverType] ?? [];
+ if (isset($this->serverStats[$serverType][$serverId])) {
+ $this->serverStats[$serverType][$serverId][0] += $u;
+ $this->serverStats[$serverType][$serverId][1] += $d;
+ } else {
+ $this->serverStats[$serverType][$serverId] = [$u, $d];
+ }
+ Cache::set("stat_server_{$this->startAt}", json_encode($this->serverStats));
+ }
+
+ public function statUser($rate, $userId, $u, $d)
+ {
+ $this->userStats[$rate] = $this->userStats[$rate] ?? [];
+ if (isset($this->userStats[$rate][$userId])) {
+ $this->userStats[$rate][$userId][0] += $u;
+ $this->userStats[$rate][$userId][1] += $d;
+ } else {
+ $this->userStats[$rate][$userId] = [$u, $d];
+ }
+ Cache::set("stat_user_{$this->startAt}", json_encode($this->userStats));
+ }
+
+ public function getStatUserByUserID($userId): array
+ {
+ $stats = [];
+ foreach (array_keys($this->userStats) as $rate) {
+ if (!isset($this->userStats[$rate][$userId])) continue;
+ $stats[] = [
+ 'record_at' => $this->startAt,
+ 'server_rate' => $rate,
+ 'u' => $this->userStats[$rate][$userId][0],
+ 'd' => $this->userStats[$rate][$userId][1],
+ 'user_id' => $userId
+ ];
+ }
+ return $stats;
+ }
+
+ public function getStatUser()
+ {
+ $stats = [];
+ foreach ($this->userStats as $k => $v) {
+ foreach (array_keys($v) as $userId) {
+ if (isset($v[$userId])) {
+ $stats[] = [
+ 'server_rate' => $k,
+ 'u' => $v[$userId][0],
+ 'd' => $v[$userId][1],
+ 'user_id' => $userId
+ ];
+ }
+ }
+ }
+ return $stats;
+ }
+
+
+ public function getStatServer()
+ {
+ $stats = [];
+ foreach ($this->serverStats as $serverType => $v) {
+ foreach (array_keys($v) as $serverId) {
+ if (isset($v[$serverId])) {
+ $stats[] = [
+ 'server_id' => $serverId,
+ 'server_type' => $serverType,
+ 'u' => $v[$serverId][0],
+ 'd' => $v[$serverId][1],
+ ];
+ }
+ }
+ }
+ return $stats;
+ }
+
+ public function clearStatUser()
+ {
+ Cache::forget("stat_user_{$this->startAt}");
+ }
+
+ public function clearStatServer()
+ {
+ Cache::forget("stat_server_{$this->startAt}");
+ }
+
+ public function getStatRecord($type)
+ {
+ switch ($type) {
+ case "paid_total": {
+ return Stat::select([
+ '*',
+ DB::raw('paid_total / 100 as paid_total')
+ ])
+ ->where('record_at', '>=', $this->startAt)
+ ->where('record_at', '<', $this->endAt)
+ ->orderBy('record_at', 'ASC')
+ ->get();
+ }
+ case "commission_total": {
+ return Stat::select([
+ '*',
+ DB::raw('commission_total / 100 as commission_total')
+ ])
+ ->where('record_at', '>=', $this->startAt)
+ ->where('record_at', '<', $this->endAt)
+ ->orderBy('record_at', 'ASC')
+ ->get();
+ }
+ case "register_count": {
+ return Stat::where('record_at', '>=', $this->startAt)
+ ->where('record_at', '<', $this->endAt)
+ ->orderBy('record_at', 'ASC')
+ ->get();
+ }
+ }
+ }
+
+ public function getRanking($type, $limit = 20)
+ {
+ switch ($type) {
+ case 'server_traffic_rank': {
+ return $this->buildServerTrafficRank($limit);
+ }
+ case 'user_consumption_rank': {
+ return $this->buildUserConsumptionRank($limit);
+ }
+ case 'invite_rank': {
+ return $this->buildInviteRank($limit);
+ }
+ }
+ }
+
+ private function buildInviteRank($limit)
+ {
+ $stats = User::select([
+ 'invite_user_id',
+ DB::raw('count(*) as count')
+ ])
+ ->where('created_at', '>=', $this->startAt)
+ ->where('created_at', '<', $this->endAt)
+ ->whereNotNull('invite_user_id')
+ ->groupBy('invite_user_id')
+ ->orderBy('count', 'DESC')
+ ->limit($limit)
+ ->get();
+
+ $users = User::whereIn('id', $stats->pluck('invite_user_id')->toArray())->get()->keyBy('id');
+ foreach ($stats as $k => $v) {
+ if (!isset($users[$v['invite_user_id']])) continue;
+ $stats[$k]['email'] = $users[$v['invite_user_id']]['email'];
+ }
+ return $stats;
+ }
+
+ private function buildUserConsumptionRank($limit)
+ {
+ $stats = StatUser::select([
+ 'user_id',
+ DB::raw('sum(u) as u'),
+ DB::raw('sum(d) as d'),
+ DB::raw('sum(u) + sum(d) as total')
+ ])
+ ->where('record_at', '>=', $this->startAt)
+ ->where('record_at', '<', $this->endAt)
+ ->groupBy('user_id')
+ ->orderBy('total', 'DESC')
+ ->limit($limit)
+ ->get();
+ $users = User::whereIn('id', $stats->pluck('user_id')->toArray())->get()->keyBy('id');
+ foreach ($stats as $k => $v) {
+ if (!isset($users[$v['user_id']])) continue;
+ $stats[$k]['email'] = $users[$v['user_id']]['email'];
+ }
+ return $stats;
+ }
+
+ private function buildServerTrafficRank($limit)
+ {
+ return StatServer::select([
+ 'server_id',
+ 'server_type',
+ DB::raw('sum(u) as u'),
+ DB::raw('sum(d) as d'),
+ DB::raw('sum(u) + sum(d) as total')
+ ])
+ ->where('record_at', '>=', $this->startAt)
+ ->where('record_at', '<', $this->endAt)
+ ->groupBy('server_id', 'server_type')
+ ->orderBy('total', 'DESC')
+ ->limit($limit)
+ ->get();
+ }
+}
diff --git a/app/Services/TelegramService.php b/app/Services/TelegramService.php
new file mode 100644
index 0000000..47ce192
--- /dev/null
+++ b/app/Services/TelegramService.php
@@ -0,0 +1,85 @@
+api = 'https://api.telegram.org/bot' . config('v2board.telegram_bot_token', $token) . '/';
+ }
+
+ public function sendMessage(int $chatId, string $text, string $parseMode = '')
+ {
+ if ($parseMode === 'markdown') {
+ $text = str_replace('_', '\_', $text);
+ }
+ $this->request('sendMessage', [
+ 'chat_id' => $chatId,
+ 'text' => $text,
+ 'parse_mode' => $parseMode
+ ]);
+ }
+
+ public function approveChatJoinRequest(int $chatId, int $userId)
+ {
+ $this->request('approveChatJoinRequest', [
+ 'chat_id' => $chatId,
+ 'user_id' => $userId
+ ]);
+ }
+
+ public function declineChatJoinRequest(int $chatId, int $userId)
+ {
+ $this->request('declineChatJoinRequest', [
+ 'chat_id' => $chatId,
+ 'user_id' => $userId
+ ]);
+ }
+
+ public function getMe()
+ {
+ return $this->request('getMe');
+ }
+
+ public function setWebhook(string $url)
+ {
+ return $this->request('setWebhook', [
+ 'url' => $url
+ ]);
+ }
+
+ private function request(string $method, array $params = [])
+ {
+ $curl = new Curl();
+ $curl->get($this->api . $method . '?' . http_build_query($params));
+ $response = $curl->response;
+ $curl->close();
+ if (!isset($response->ok)) abort(500, '请求失败');
+ if (!$response->ok) {
+ abort(500, '来自TG的错误:' . $response->description);
+ }
+ return $response;
+ }
+
+ public function sendMessageWithAdmin($message, $isStaff = false)
+ {
+ if (!config('v2board.telegram_bot_enable', 0)) return;
+ $users = User::where(function ($query) use ($isStaff) {
+ $query->where('is_admin', 1);
+ if ($isStaff) {
+ $query->orWhere('is_staff', 1);
+ }
+ })
+ ->where('telegram_id', '!=', NULL)
+ ->get();
+ foreach ($users as $user) {
+ SendTelegramJob::dispatch($user->telegram_id, $message);
+ }
+ }
+}
diff --git a/app/Services/ThemeService.php b/app/Services/ThemeService.php
new file mode 100644
index 0000000..036b875
--- /dev/null
+++ b/app/Services/ThemeService.php
@@ -0,0 +1,49 @@
+theme = $theme;
+ $this->path = $path = public_path('theme/');
+ }
+
+ public function init()
+ {
+ $themeConfigFile = $this->path . "{$this->theme}/config.json";
+ if (!File::exists($themeConfigFile)) abort(500, "{$this->theme}主题不存在");
+ $themeConfig = json_decode(File::get($themeConfigFile), true);
+ if (!isset($themeConfig['configs']) || !is_array($themeConfig)) abort(500, "{$this->theme}主题配置文件有误");
+ $configs = $themeConfig['configs'];
+ $data = [];
+ foreach ($configs as $config) {
+ $data[$config['field_name']] = isset($config['default_value']) ? $config['default_value'] : '';
+ }
+
+ $data = var_export($data, 1);
+ try {
+ if (!File::put(base_path() . "/config/theme/{$this->theme}.php", "theme}初始化失败");
+ }
+ } catch (\Exception $e) {
+ abort(500, '请检查V2Board目录权限');
+ }
+
+ try {
+ Artisan::call('config:cache');
+ while (true) {
+ if (config("theme.{$this->theme}")) break;
+ }
+ } catch (\Exception $e) {
+ abort(500, "{$this->theme}初始化失败");
+ }
+ }
+}
diff --git a/app/Services/TicketService.php b/app/Services/TicketService.php
new file mode 100644
index 0000000..f891adc
--- /dev/null
+++ b/app/Services/TicketService.php
@@ -0,0 +1,80 @@
+ $userId,
+ 'ticket_id' => $ticket->id,
+ 'message' => $message
+ ]);
+ if ($userId !== $ticket->user_id) {
+ $ticket->reply_status = 0;
+ } else {
+ $ticket->reply_status = 1;
+ }
+ if (!$ticketMessage || !$ticket->save()) {
+ DB::rollback();
+ return false;
+ }
+ DB::commit();
+ return $ticketMessage;
+ }
+
+ public function replyByAdmin($ticketId, $message, $userId):void
+ {
+ $ticket = Ticket::where('id', $ticketId)
+ ->first();
+ if (!$ticket) {
+ abort(500, '工单不存在');
+ }
+ $ticket->status = 0;
+ DB::beginTransaction();
+ $ticketMessage = TicketMessage::create([
+ 'user_id' => $userId,
+ 'ticket_id' => $ticket->id,
+ 'message' => $message
+ ]);
+ if ($userId !== $ticket->user_id) {
+ $ticket->reply_status = 0;
+ } else {
+ $ticket->reply_status = 1;
+ }
+ if (!$ticketMessage || !$ticket->save()) {
+ DB::rollback();
+ abort(500, '工单回复失败');
+ }
+ DB::commit();
+ $this->sendEmailNotify($ticket, $ticketMessage);
+ }
+
+ // 半小时内不再重复通知
+ private function sendEmailNotify(Ticket $ticket, TicketMessage $ticketMessage)
+ {
+ $user = User::find($ticket->user_id);
+ $cacheKey = 'ticket_sendEmailNotify_' . $ticket->user_id;
+ if (!Cache::get($cacheKey)) {
+ Cache::put($cacheKey, 1, 1800);
+ SendEmailJob::dispatch([
+ 'email' => $user->email,
+ 'subject' => '您在' . config('v2board.app_name', 'V2Board') . '的工单得到了回复',
+ 'template_name' => 'notify',
+ 'template_value' => [
+ 'name' => config('v2board.app_name', 'V2Board'),
+ 'url' => config('v2board.app_url'),
+ 'content' => "主题:{$ticket->subject}\r\n回复内容:{$ticketMessage->message}"
+ ]
+ ]);
+ }
+ }
+}
diff --git a/app/Services/UserService.php b/app/Services/UserService.php
new file mode 100644
index 0000000..ba82c91
--- /dev/null
+++ b/app/Services/UserService.php
@@ -0,0 +1,185 @@
+= (int)$today && (int)$day >= (int)$lastDay) {
+ return $lastDay - $today;
+ }
+ if ((int)$day >= (int)$today) {
+ return $day - $today;
+ }
+
+ return $lastDay - $today + $day;
+ }
+
+ private function calcResetDayByYearFirstDay(): int
+ {
+ $nextYear = strtotime(date("Y-01-01", strtotime('+1 year')));
+ return (int)(($nextYear - time()) / 86400);
+ }
+
+ private function calcResetDayByYearExpiredAt(int $expiredAt): int
+ {
+ $md = date('m-d', $expiredAt);
+ $nowYear = strtotime(date("Y-{$md}"));
+ $nextYear = strtotime('+1 year', $nowYear);
+ if ($nowYear > time()) {
+ return (int)(($nowYear - time()) / 86400);
+ }
+ return (int)(($nextYear - time()) / 86400);
+ }
+
+ public function getResetDay(User $user)
+ {
+ if (!isset($user->plan)) {
+ $user->plan = Plan::find($user->plan_id);
+ }
+ if ($user->expired_at <= time() || $user->expired_at === NULL) return null;
+ // if reset method is not reset
+ if ($user->plan->reset_traffic_method === 2) return null;
+ switch (true) {
+ case ($user->plan->reset_traffic_method === NULL): {
+ $resetTrafficMethod = config('v2board.reset_traffic_method', 0);
+ switch ((int)$resetTrafficMethod) {
+ // month first day
+ case 0:
+ return $this->calcResetDayByMonthFirstDay();
+ // expire day
+ case 1:
+ return $this->calcResetDayByExpireDay($user->expired_at);
+ // no action
+ case 2:
+ return null;
+ // year first day
+ case 3:
+ return $this->calcResetDayByYearFirstDay();
+ // year expire day
+ case 4:
+ return $this->calcResetDayByYearExpiredAt($user->expired_at);
+ }
+ break;
+ }
+ case ($user->plan->reset_traffic_method === 0): {
+ return $this->calcResetDayByMonthFirstDay();
+ }
+ case ($user->plan->reset_traffic_method === 1): {
+ return $this->calcResetDayByExpireDay($user->expired_at);
+ }
+ case ($user->plan->reset_traffic_method === 2): {
+ return null;
+ }
+ case ($user->plan->reset_traffic_method === 3): {
+ return $this->calcResetDayByYearFirstDay();
+ }
+ case ($user->plan->reset_traffic_method === 4): {
+ return $this->calcResetDayByYearExpiredAt($user->expired_at);
+ }
+ }
+ return null;
+ }
+
+ public function isAvailable(User $user)
+ {
+ if (!$user->banned && $user->transfer_enable && ($user->expired_at > time() || $user->expired_at === NULL)) {
+ return true;
+ }
+ return false;
+ }
+
+ public function getAvailableUsers()
+ {
+ return User::whereRaw('u + d < transfer_enable')
+ ->where(function ($query) {
+ $query->where('expired_at', '>=', time())
+ ->orWhere('expired_at', NULL);
+ })
+ ->where('banned', 0)
+ ->get();
+ }
+
+ public function getUnAvailbaleUsers()
+ {
+ return User::where(function ($query) {
+ $query->where('expired_at', '<', time())
+ ->orWhere('expired_at', 0);
+ })
+ ->where(function ($query) {
+ $query->where('plan_id', NULL)
+ ->orWhere('transfer_enable', 0);
+ })
+ ->get();
+ }
+
+ public function getUsersByIds($ids)
+ {
+ return User::whereIn('id', $ids)->get();
+ }
+
+ public function getAllUsers()
+ {
+ return User::all();
+ }
+
+ public function addBalance(int $userId, int $balance):bool
+ {
+ $user = User::lockForUpdate()->find($userId);
+ if (!$user) {
+ return false;
+ }
+ $user->balance = $user->balance + $balance;
+ if ($user->balance < 0) {
+ return false;
+ }
+ if (!$user->save()) {
+ return false;
+ }
+ return true;
+ }
+
+ public function isNotCompleteOrderByUserId(int $userId):bool
+ {
+ $order = Order::whereIn('status', [0, 1])
+ ->where('user_id', $userId)
+ ->first();
+ if (!$order) {
+ return false;
+ }
+ return true;
+ }
+
+ public function trafficFetch(array $server, string $protocol, array $data)
+ {
+ $statService = new StatisticalService();
+ $statService->setStartAt(strtotime(date('Y-m-d')));
+ $statService->setUserStats();
+ $statService->setServerStats();
+ foreach (array_keys($data) as $userId) {
+ $u = $data[$userId][0];
+ $d = $data[$userId][1];
+ TrafficFetchJob::dispatch($u, $d, $userId, $server, $protocol);
+ $statService->statServer($server['id'], $protocol, $u, $d);
+ $statService->statUser($server['rate'], $userId, $u, $d);
+ }
+ }
+}
diff --git a/app/Utils/CacheKey.php b/app/Utils/CacheKey.php
new file mode 100644
index 0000000..6b6222d
--- /dev/null
+++ b/app/Utils/CacheKey.php
@@ -0,0 +1,38 @@
+ '邮箱验证码',
+ 'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间',
+ 'SERVER_VMESS_ONLINE_USER' => '节点在线用户',
+ 'SERVER_VMESS_LAST_CHECK_AT' => '节点最后检查时间',
+ 'SERVER_VMESS_LAST_PUSH_AT' => '节点最后推送时间',
+ 'SERVER_TROJAN_ONLINE_USER' => 'trojan节点在线用户',
+ 'SERVER_TROJAN_LAST_CHECK_AT' => 'trojan节点最后检查时间',
+ 'SERVER_TROJAN_LAST_PUSH_AT' => 'trojan节点最后推送时间',
+ 'SERVER_SHADOWSOCKS_ONLINE_USER' => 'ss节点在线用户',
+ 'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
+ 'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
+ 'SERVER_HYSTERIA_ONLINE_USER' => 'hysteria节点在线用户',
+ 'SERVER_HYSTERIA_LAST_CHECK_AT' => 'hysteria节点最后检查时间',
+ 'SERVER_HYSTERIA_LAST_PUSH_AT' => 'hysteria节点最后推送时间',
+ 'TEMP_TOKEN' => '临时令牌',
+ 'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒',
+ 'SCHEDULE_LAST_CHECK_AT' => '计划任务最后检查时间',
+ 'REGISTER_IP_RATE_LIMIT' => '注册频率限制',
+ 'LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP' => '最后一次发送登入链接时间',
+ 'PASSWORD_ERROR_LIMIT' => '密码错误次数限制',
+ 'USER_SESSIONS' => '用户session'
+ ];
+
+ public static function get(string $key, $uniqueValue)
+ {
+ if (!in_array($key, array_keys(self::KEYS))) {
+ abort(500, 'key is not in cache key list');
+ }
+ return $key . '_' . $uniqueValue;
+ }
+}
diff --git a/app/Utils/Dict.php b/app/Utils/Dict.php
new file mode 100644
index 0000000..e0e6fe6
--- /dev/null
+++ b/app/Utils/Dict.php
@@ -0,0 +1,23 @@
+", "~", "+", "=", ",", "."
+ ));
+ }
+
+ $charsLen = count($chars) - 1;
+ shuffle($chars);
+ $str = '';
+ for ($i = 0; $i < $len; $i++) {
+ $str .= $chars[mt_rand(0, $charsLen)];
+ }
+ return $str;
+ }
+
+ public static function multiPasswordVerify($algo, $salt, $password, $hash)
+ {
+ switch($algo) {
+ case 'md5': return md5($password) === $hash;
+ case 'sha256': return hash('sha256', $password) === $hash;
+ case 'md5salt': return md5($password . $salt) === $hash;
+ default: return password_verify($password, $hash);
+ }
+ }
+
+ public static function emailSuffixVerify($email, $suffixs)
+ {
+ $suffix = preg_split('/@/', $email)[1];
+ if (!$suffix) return false;
+ if (!is_array($suffixs)) {
+ $suffixs = preg_split('/,/', $suffixs);
+ }
+ if (!in_array($suffix, $suffixs)) return false;
+ return true;
+ }
+
+ public static function trafficConvert(int $byte)
+ {
+ $kb = 1024;
+ $mb = 1048576;
+ $gb = 1073741824;
+ if ($byte > $gb) {
+ return round($byte / $gb, 2) . ' GB';
+ } else if ($byte > $mb) {
+ return round($byte / $mb, 2) . ' MB';
+ } else if ($byte > $kb) {
+ return round($byte / $kb, 2) . ' KB';
+ } else if ($byte < 0) {
+ return 0;
+ } else {
+ return round($byte, 2) . ' B';
+ }
+ }
+
+ public static function getSubscribeUrl($path)
+ {
+ $subscribeUrls = explode(',', config('v2board.subscribe_url'));
+ $subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
+ if ($subscribeUrl) return $subscribeUrl . $path;
+ return url($path);
+ }
+
+ public static function randomPort($range) {
+ $portRange = explode('-', $range);
+ return rand($portRange[0], $portRange[1]);
+ }
+}
diff --git a/artisan b/artisan
new file mode 100755
index 0000000..5c23e2e
--- /dev/null
+++ b/artisan
@@ -0,0 +1,53 @@
+#!/usr/bin/env php
+make(Illuminate\Contracts\Console\Kernel::class);
+
+$status = $kernel->handle(
+ $input = new Symfony\Component\Console\Input\ArgvInput,
+ new Symfony\Component\Console\Output\ConsoleOutput
+);
+
+/*
+|--------------------------------------------------------------------------
+| Shutdown The Application
+|--------------------------------------------------------------------------
+|
+| Once Artisan has finished running, we will fire off the shutdown events
+| so that any final work may be done by the application before we shut
+| down the process. This is the last thing to happen to the request.
+|
+*/
+
+$kernel->terminate($input, $status);
+
+exit($status);
diff --git a/bootstrap/app.php b/bootstrap/app.php
new file mode 100755
index 0000000..037e17d
--- /dev/null
+++ b/bootstrap/app.php
@@ -0,0 +1,55 @@
+singleton(
+ Illuminate\Contracts\Http\Kernel::class,
+ App\Http\Kernel::class
+);
+
+$app->singleton(
+ Illuminate\Contracts\Console\Kernel::class,
+ App\Console\Kernel::class
+);
+
+$app->singleton(
+ Illuminate\Contracts\Debug\ExceptionHandler::class,
+ App\Exceptions\Handler::class
+);
+
+/*
+|--------------------------------------------------------------------------
+| Return The Application
+|--------------------------------------------------------------------------
+|
+| This script returns the application instance. The instance is given to
+| the calling script so we can separate the building of the instances
+| from the actual running of the application and sending responses.
+|
+*/
+
+return $app;
diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore
new file mode 100755
index 0000000..d6b7ef3
--- /dev/null
+++ b/bootstrap/cache/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/composer.json b/composer.json
new file mode 100755
index 0000000..3463cc2
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,80 @@
+{
+ "name": "v2board/v2board",
+ "type": "project",
+ "description": "v2board is a proxy protocol manage.",
+ "keywords": [
+ "v2board",
+ "v2ray",
+ "shadowsocks",
+ "trojan",
+ "laravel"
+ ],
+ "license": "MIT",
+ "require": {
+ "php": "^7.3.0|^8.0",
+ "fideloper/proxy": "^4.4",
+ "firebase/php-jwt": "^6.3",
+ "fruitcake/laravel-cors": "^2.0",
+ "google/recaptcha": "^1.2",
+ "guzzlehttp/guzzle": "^7.4.3",
+ "laravel/framework": "^8.0",
+ "laravel/horizon": "^5.9.6",
+ "laravel/tinker": "^2.5",
+ "linfo/linfo": "^4.0",
+ "php-curl-class/php-curl-class": "^8.6",
+ "stripe/stripe-php": "^7.36.1",
+ "symfony/yaml": "^4.3"
+ },
+ "require-dev": {
+ "facade/ignition": "^2.3.6",
+ "fakerphp/faker": "^1.9.1",
+ "mockery/mockery": "^1.3.1",
+ "nunomaduro/collision": "^4.3",
+ "phpunit/phpunit": "^9.0"
+ },
+ "config": {
+ "optimize-autoloader": true,
+ "preferred-install": "dist",
+ "sort-packages": true
+ },
+ "extra": {
+ "laravel": {
+ "dont-discover": []
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "App\\": "app/",
+ "Library\\": "library/"
+ },
+ "classmap": [
+ "database/seeds",
+ "database/factories"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\": "tests/"
+ }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "scripts": {
+ "post-autoload-dump": [
+ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
+ "@php artisan package:discover --ansi"
+ ],
+ "post-root-package-install": [
+ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+ ],
+ "post-create-project-cmd": [
+ "@php artisan key:generate --ansi"
+ ]
+ },
+ "repositories": {
+ "packagist": {
+ "type": "composer",
+ "url": "https://packagist.org"
+ }
+ }
+}
diff --git a/config/app.php b/config/app.php
new file mode 100755
index 0000000..ce3decb
--- /dev/null
+++ b/config/app.php
@@ -0,0 +1,241 @@
+ env('APP_NAME', 'Laravel'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Application Environment
+ |--------------------------------------------------------------------------
+ |
+ | This value determines the "environment" your application is currently
+ | running in. This may determine how you prefer to configure various
+ | services the application utilizes. Set this in your ".env" file.
+ |
+ */
+
+ 'env' => env('APP_ENV', 'production'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Application Debug Mode
+ |--------------------------------------------------------------------------
+ |
+ | When your application is in debug mode, detailed error messages with
+ | stack traces will be shown on every error that occurs within your
+ | application. If disabled, a simple generic error page is shown.
+ |
+ */
+
+ 'debug' => env('APP_DEBUG', false),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Application URL
+ |--------------------------------------------------------------------------
+ |
+ | This URL is used by the console to properly generate URLs when using
+ | the Artisan command line tool. You should set this to the root of
+ | your application so that it is used when running Artisan tasks.
+ |
+ */
+
+ 'url' => env('APP_URL', 'http://localhost'),
+
+ 'asset_url' => env('ASSET_URL', null),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Application Timezone
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the default timezone for your application, which
+ | will be used by the PHP date and date-time functions. We have gone
+ | ahead and set this to a sensible default for you out of the box.
+ |
+ */
+
+ 'timezone' => 'Asia/Shanghai',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Application Locale Configuration
+ |--------------------------------------------------------------------------
+ |
+ | The application locale determines the default locale that will be used
+ | by the translation service provider. You are free to set this value
+ | to any of the locales which will be supported by the application.
+ |
+ */
+
+ 'locale' => 'zh-CN',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Application Fallback Locale
+ |--------------------------------------------------------------------------
+ |
+ | The fallback locale determines the locale to use when the current one
+ | is not available. You may change the value to correspond to any of
+ | the language folders that are provided through your application.
+ |
+ */
+
+ 'fallback_locale' => 'zh-CN',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Faker Locale
+ |--------------------------------------------------------------------------
+ |
+ | This locale will be used by the Faker PHP library when generating fake
+ | data for your database seeds. For example, this will be used to get
+ | localized telephone numbers, street address information and more.
+ |
+ */
+
+ 'faker_locale' => 'zh-CN',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Encryption Key
+ |--------------------------------------------------------------------------
+ |
+ | This key is used by the Illuminate encrypter service and should be set
+ | to a random, 32 character string, otherwise these encrypted strings
+ | will not be safe. Please do this before deploying an application!
+ |
+ */
+
+ 'key' => env('APP_KEY'),
+
+ 'cipher' => 'AES-256-CBC',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Autoloaded Service Providers
+ |--------------------------------------------------------------------------
+ |
+ | The service providers listed here will be automatically loaded on the
+ | request to your application. Feel free to add your own services to
+ | this array to grant expanded functionality to your applications.
+ |
+ */
+
+ 'providers' => [
+
+ /*
+ * Laravel Framework Service Providers...
+ */
+ Illuminate\Auth\AuthServiceProvider::class,
+ Illuminate\Broadcasting\BroadcastServiceProvider::class,
+ Illuminate\Bus\BusServiceProvider::class,
+ Illuminate\Cache\CacheServiceProvider::class,
+ Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
+ Illuminate\Cookie\CookieServiceProvider::class,
+ Illuminate\Database\DatabaseServiceProvider::class,
+ Illuminate\Encryption\EncryptionServiceProvider::class,
+ Illuminate\Filesystem\FilesystemServiceProvider::class,
+ Illuminate\Foundation\Providers\FoundationServiceProvider::class,
+ Illuminate\Hashing\HashServiceProvider::class,
+ Illuminate\Mail\MailServiceProvider::class,
+ Illuminate\Notifications\NotificationServiceProvider::class,
+ Illuminate\Pagination\PaginationServiceProvider::class,
+ Illuminate\Pipeline\PipelineServiceProvider::class,
+ Illuminate\Queue\QueueServiceProvider::class,
+ Illuminate\Redis\RedisServiceProvider::class,
+ Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
+ Illuminate\Session\SessionServiceProvider::class,
+ Illuminate\Translation\TranslationServiceProvider::class,
+ Illuminate\Validation\ValidationServiceProvider::class,
+ Illuminate\View\ViewServiceProvider::class,
+
+ /*
+ * Package Service Providers...
+ */
+
+ /*
+ * Application Service Providers...
+ */
+ App\Providers\AppServiceProvider::class,
+ App\Providers\AuthServiceProvider::class,
+ // App\Providers\BroadcastServiceProvider::class,
+ App\Providers\EventServiceProvider::class,
+ App\Providers\HorizonServiceProvider::class,
+ App\Providers\RouteServiceProvider::class,
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Class Aliases
+ |--------------------------------------------------------------------------
+ |
+ | This array of class aliases will be registered when this application
+ | is started. However, feel free to register as many as you wish as
+ | the aliases are "lazy" loaded so they don't hinder performance.
+ |
+ */
+
+ 'aliases' => [
+
+ 'App' => Illuminate\Support\Facades\App::class,
+ 'Arr' => Illuminate\Support\Arr::class,
+ 'Artisan' => Illuminate\Support\Facades\Artisan::class,
+ 'Auth' => Illuminate\Support\Facades\Auth::class,
+ 'Blade' => Illuminate\Support\Facades\Blade::class,
+ 'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
+ 'Bus' => Illuminate\Support\Facades\Bus::class,
+ 'Cache' => Illuminate\Support\Facades\Cache::class,
+ 'Config' => Illuminate\Support\Facades\Config::class,
+ 'Cookie' => Illuminate\Support\Facades\Cookie::class,
+ 'Crypt' => Illuminate\Support\Facades\Crypt::class,
+ 'DB' => Illuminate\Support\Facades\DB::class,
+ 'Eloquent' => Illuminate\Database\Eloquent\Model::class,
+ 'Event' => Illuminate\Support\Facades\Event::class,
+ 'File' => Illuminate\Support\Facades\File::class,
+ 'Gate' => Illuminate\Support\Facades\Gate::class,
+ 'Hash' => Illuminate\Support\Facades\Hash::class,
+ 'Lang' => Illuminate\Support\Facades\Lang::class,
+ 'Log' => Illuminate\Support\Facades\Log::class,
+ 'Mail' => Illuminate\Support\Facades\Mail::class,
+ 'Notification' => Illuminate\Support\Facades\Notification::class,
+ 'Password' => Illuminate\Support\Facades\Password::class,
+ 'Queue' => Illuminate\Support\Facades\Queue::class,
+ 'Redirect' => Illuminate\Support\Facades\Redirect::class,
+ 'Redis' => Illuminate\Support\Facades\Redis::class,
+ 'Request' => Illuminate\Support\Facades\Request::class,
+ 'Response' => Illuminate\Support\Facades\Response::class,
+ 'Route' => Illuminate\Support\Facades\Route::class,
+ 'Schema' => Illuminate\Support\Facades\Schema::class,
+ 'Session' => Illuminate\Support\Facades\Session::class,
+ 'Storage' => Illuminate\Support\Facades\Storage::class,
+ 'Str' => Illuminate\Support\Str::class,
+ 'URL' => Illuminate\Support\Facades\URL::class,
+ 'Validator' => Illuminate\Support\Facades\Validator::class,
+ 'View' => Illuminate\Support\Facades\View::class,
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | V2board version
+ |--------------------------------------------------------------------------
+ |
+ | The only modification by laravel config
+ |
+ */
+ 'version' => '1.7.4.1681103823832'
+];
diff --git a/config/auth.php b/config/auth.php
new file mode 100755
index 0000000..897dc82
--- /dev/null
+++ b/config/auth.php
@@ -0,0 +1,103 @@
+ [
+ 'guard' => 'web',
+ 'passwords' => 'users',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Authentication Guards
+ |--------------------------------------------------------------------------
+ |
+ | Next, you may define every authentication guard for your application.
+ | Of course, a great default configuration has been defined for you
+ | here which uses session storage and the Eloquent user provider.
+ |
+ | All authentication drivers have a user provider. This defines how the
+ | users are actually retrieved out of your database or other storage
+ | mechanisms used by this application to persist your user's data.
+ |
+ | Supported: "session", "token"
+ |
+ */
+
+ 'guards' => [
+ 'web' => [
+ 'driver' => 'session',
+ 'provider' => 'users',
+ ],
+
+ 'api' => [
+ 'driver' => 'token',
+ 'provider' => 'users',
+ 'hash' => false,
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | User Providers
+ |--------------------------------------------------------------------------
+ |
+ | All authentication drivers have a user provider. This defines how the
+ | users are actually retrieved out of your database or other storage
+ | mechanisms used by this application to persist your user's data.
+ |
+ | If you have multiple user tables or models you may configure multiple
+ | sources which represent each model / table. These sources may then
+ | be assigned to any extra authentication guards you have defined.
+ |
+ | Supported: "database", "eloquent"
+ |
+ */
+
+ 'providers' => [
+ 'users' => [
+ 'driver' => 'eloquent',
+ 'model' => App\User::class,
+ ],
+
+ // 'users' => [
+ // 'driver' => 'database',
+ // 'table' => 'users',
+ // ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Resetting Passwords
+ |--------------------------------------------------------------------------
+ |
+ | You may specify multiple password reset configurations if you have more
+ | than one user table or model in the application and you want to have
+ | separate password reset settings based on the specific user types.
+ |
+ | The expire time is the number of minutes that the reset token should be
+ | considered valid. This security feature keeps tokens short-lived so
+ | they have less time to be guessed. You may change this as needed.
+ |
+ */
+
+ 'passwords' => [
+ 'users' => [
+ 'provider' => 'users',
+ 'table' => 'password_resets',
+ 'expire' => 60,
+ ],
+ ],
+
+];
diff --git a/config/broadcasting.php b/config/broadcasting.php
new file mode 100755
index 0000000..3bba110
--- /dev/null
+++ b/config/broadcasting.php
@@ -0,0 +1,59 @@
+ env('BROADCAST_DRIVER', 'null'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Broadcast Connections
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define all of the broadcast connections that will be used
+ | to broadcast events to other systems or over websockets. Samples of
+ | each available type of connection are provided inside this array.
+ |
+ */
+
+ 'connections' => [
+
+ 'pusher' => [
+ 'driver' => 'pusher',
+ 'key' => env('PUSHER_APP_KEY'),
+ 'secret' => env('PUSHER_APP_SECRET'),
+ 'app_id' => env('PUSHER_APP_ID'),
+ 'options' => [
+ 'cluster' => env('PUSHER_APP_CLUSTER'),
+ 'useTLS' => true,
+ ],
+ ],
+
+ 'redis' => [
+ 'driver' => 'redis',
+ 'connection' => 'default',
+ ],
+
+ 'log' => [
+ 'driver' => 'log',
+ ],
+
+ 'null' => [
+ 'driver' => 'null',
+ ],
+
+ ],
+
+];
diff --git a/config/cache.php b/config/cache.php
new file mode 100755
index 0000000..aef6c4a
--- /dev/null
+++ b/config/cache.php
@@ -0,0 +1,103 @@
+ env('CACHE_DRIVER', 'file'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Cache Stores
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define all of the cache "stores" for your application as
+ | well as their drivers. You may even define multiple stores for the
+ | same cache driver to group types of items stored in your caches.
+ |
+ */
+
+ 'stores' => [
+
+ 'apc' => [
+ 'driver' => 'apc',
+ ],
+
+ 'array' => [
+ 'driver' => 'array',
+ ],
+
+ 'database' => [
+ 'driver' => 'database',
+ 'table' => 'cache',
+ 'connection' => null,
+ ],
+
+ 'file' => [
+ 'driver' => 'file',
+ 'path' => storage_path('framework/cache/data'),
+ ],
+
+ 'memcached' => [
+ 'driver' => 'memcached',
+ 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
+ 'sasl' => [
+ env('MEMCACHED_USERNAME'),
+ env('MEMCACHED_PASSWORD'),
+ ],
+ 'options' => [
+ // Memcached::OPT_CONNECT_TIMEOUT => 2000,
+ ],
+ 'servers' => [
+ [
+ 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
+ 'port' => env('MEMCACHED_PORT', 11211),
+ 'weight' => 100,
+ ],
+ ],
+ ],
+
+ 'redis' => [
+ 'driver' => 'redis',
+ 'connection' => 'cache',
+ ],
+
+ 'dynamodb' => [
+ 'driver' => 'dynamodb',
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'region' => env('AWS_V2BOARD_REGION', 'us-east-1'),
+ 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
+ 'endpoint' => env('DYNAMODB_ENDPOINT'),
+ ],
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Cache Key Prefix
+ |--------------------------------------------------------------------------
+ |
+ | When utilizing a RAM based store such as APC or Memcached, there might
+ | be other applications utilizing the same cache. So, we'll specify a
+ | value to get prefixed to all our keys so we can avoid collisions.
+ |
+ */
+
+ 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'),
+
+];
diff --git a/config/cors.php b/config/cors.php
new file mode 100644
index 0000000..558369d
--- /dev/null
+++ b/config/cors.php
@@ -0,0 +1,34 @@
+ ['api/*'],
+
+ 'allowed_methods' => ['*'],
+
+ 'allowed_origins' => ['*'],
+
+ 'allowed_origins_patterns' => [],
+
+ 'allowed_headers' => ['*'],
+
+ 'exposed_headers' => [],
+
+ 'max_age' => 0,
+
+ 'supports_credentials' => false,
+
+];
diff --git a/config/database.php b/config/database.php
new file mode 100755
index 0000000..9a87937
--- /dev/null
+++ b/config/database.php
@@ -0,0 +1,147 @@
+ env('DB_CONNECTION', 'mysql'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Database Connections
+ |--------------------------------------------------------------------------
+ |
+ | Here are each of the database connections setup for your application.
+ | Of course, examples of configuring each database platform that is
+ | supported by Laravel is shown below to make development simple.
+ |
+ |
+ | All database work in Laravel is done through the PHP PDO facilities
+ | so make sure you have the driver for your particular database of
+ | choice installed on your machine before you begin development.
+ |
+ */
+
+ 'connections' => [
+
+ 'sqlite' => [
+ 'driver' => 'sqlite',
+ 'url' => env('DATABASE_URL'),
+ 'database' => env('DB_DATABASE', database_path('database.sqlite')),
+ 'prefix' => '',
+ 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
+ ],
+
+ 'mysql' => [
+ 'driver' => 'mysql',
+ 'url' => env('DATABASE_URL'),
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '3306'),
+ 'database' => env('DB_DATABASE', 'forge'),
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => true,
+ 'engine' => null,
+ 'options' => extension_loaded('pdo_mysql') ? array_filter([
+ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+ ]) : [],
+ ],
+
+ 'pgsql' => [
+ 'driver' => 'pgsql',
+ 'url' => env('DATABASE_URL'),
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '5432'),
+ 'database' => env('DB_DATABASE', 'forge'),
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'charset' => 'utf8',
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'schema' => 'public',
+ 'sslmode' => 'prefer',
+ ],
+
+ 'sqlsrv' => [
+ 'driver' => 'sqlsrv',
+ 'url' => env('DATABASE_URL'),
+ 'host' => env('DB_HOST', 'localhost'),
+ 'port' => env('DB_PORT', '1433'),
+ 'database' => env('DB_DATABASE', 'forge'),
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'charset' => 'utf8',
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ ],
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Migration Repository Table
+ |--------------------------------------------------------------------------
+ |
+ | This table keeps track of all the migrations that have already run for
+ | your application. Using this information, we can determine which of
+ | the migrations on disk haven't actually been run in the database.
+ |
+ */
+
+ 'migrations' => 'migrations',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Redis Databases
+ |--------------------------------------------------------------------------
+ |
+ | Redis is an open source, fast, and advanced key-value store that also
+ | provides a richer body of commands than a typical key-value system
+ | such as APC or Memcached. Laravel makes it easy to dig right in.
+ |
+ */
+
+ 'redis' => [
+
+ 'client' => env('REDIS_CLIENT', 'phpredis'),
+
+ 'options' => [
+ 'cluster' => env('REDIS_CLUSTER', 'redis'),
+ 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'),
+ ],
+
+ 'default' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'password' => env('REDIS_PASSWORD', null),
+ 'port' => env('REDIS_PORT', 6379),
+ 'database' => env('REDIS_DB', 0),
+ ],
+
+ 'cache' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'password' => env('REDIS_PASSWORD', null),
+ 'port' => env('REDIS_PORT', 6379),
+ 'database' => env('REDIS_CACHE_DB', 1),
+ ],
+
+ ],
+
+];
diff --git a/config/filesystems.php b/config/filesystems.php
new file mode 100755
index 0000000..c83f0c6
--- /dev/null
+++ b/config/filesystems.php
@@ -0,0 +1,69 @@
+ env('FILESYSTEM_DRIVER', 'local'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Default Cloud Filesystem Disk
+ |--------------------------------------------------------------------------
+ |
+ | Many applications store files both locally and in the cloud. For this
+ | reason, you may specify a default "cloud" driver here. This driver
+ | will be bound as the Cloud disk implementation in the container.
+ |
+ */
+
+ 'cloud' => env('FILESYSTEM_CLOUD', 's3'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Filesystem Disks
+ |--------------------------------------------------------------------------
+ |
+ | Here you may configure as many filesystem "disks" as you wish, and you
+ | may even configure multiple disks of the same driver. Defaults have
+ | been setup for each driver as an example of the required options.
+ |
+ | Supported Drivers: "local", "ftp", "sftp", "s3"
+ |
+ */
+
+ 'disks' => [
+
+ 'local' => [
+ 'driver' => 'local',
+ 'root' => storage_path('app'),
+ ],
+
+ 'public' => [
+ 'driver' => 'local',
+ 'root' => storage_path('app/public'),
+ 'url' => env('APP_URL') . '/storage',
+ 'visibility' => 'public',
+ ],
+
+ 's3' => [
+ 'driver' => 's3',
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'region' => env('AWS_V2BOARD_REGION'),
+ 'bucket' => env('AWS_BUCKET'),
+ 'url' => env('AWS_URL'),
+ ],
+
+ ],
+
+];
diff --git a/config/hashing.php b/config/hashing.php
new file mode 100755
index 0000000..9146bfd
--- /dev/null
+++ b/config/hashing.php
@@ -0,0 +1,52 @@
+ 'bcrypt',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Bcrypt Options
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the configuration options that should be used when
+ | passwords are hashed using the Bcrypt algorithm. This will allow you
+ | to control the amount of time it takes to hash the given password.
+ |
+ */
+
+ 'bcrypt' => [
+ 'rounds' => env('BCRYPT_ROUNDS', 10),
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Argon Options
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the configuration options that should be used when
+ | passwords are hashed using the Argon algorithm. These will allow you
+ | to control the amount of time it takes to hash the given password.
+ |
+ */
+
+ 'argon' => [
+ 'memory' => 8192,
+ 'threads' => 2,
+ 'time' => 2,
+ ],
+
+];
diff --git a/config/horizon.php b/config/horizon.php
new file mode 100644
index 0000000..4ad9e24
--- /dev/null
+++ b/config/horizon.php
@@ -0,0 +1,190 @@
+getParser();
+
+return [
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Domain
+ |--------------------------------------------------------------------------
+ |
+ | This is the subdomain where Horizon will be accessible from. If this
+ | setting is null, Horizon will reside under the same domain as the
+ | application. Otherwise, this value will serve as the subdomain.
+ |
+ */
+
+ 'domain' => null,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Path
+ |--------------------------------------------------------------------------
+ |
+ | This is the URI path where Horizon will be accessible from. Feel free
+ | to change this path to anything you like. Note that the URI will not
+ | affect the paths of its internal API that aren't exposed to users.
+ |
+ */
+
+ 'path' => 'monitor',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Redis Connection
+ |--------------------------------------------------------------------------
+ |
+ | This is the name of the Redis connection where Horizon will store the
+ | meta information required for it to function. It includes the list
+ | of supervisors, failed jobs, job metrics, and other information.
+ |
+ */
+
+ 'use' => 'default',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Redis Prefix
+ |--------------------------------------------------------------------------
+ |
+ | This prefix will be used when storing all Horizon data in Redis. You
+ | may modify the prefix when you are running multiple installations
+ | of Horizon on the same server so that they don't have problems.
+ |
+ */
+
+ 'prefix' => env(
+ 'HORIZON_PREFIX',
+ Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
+ ),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Route Middleware
+ |--------------------------------------------------------------------------
+ |
+ | These middleware will get attached onto each Horizon route, giving you
+ | the chance to add your own middleware to this list or change any of
+ | the existing middleware. Or, you can simply stick with this list.
+ |
+ */
+
+ 'middleware' => ['admin'],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Queue Wait Time Thresholds
+ |--------------------------------------------------------------------------
+ |
+ | This option allows you to configure when the LongWaitDetected event
+ | will be fired. Every connection / queue combination may have its
+ | own, unique threshold (in seconds) before this event is fired.
+ |
+ */
+
+ 'waits' => [
+ 'redis:default' => 60,
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Job Trimming Times
+ |--------------------------------------------------------------------------
+ |
+ | Here you can configure for how long (in minutes) you desire Horizon to
+ | persist the recent and failed jobs. Typically, recent jobs are kept
+ | for one hour while all failed jobs are stored for an entire week.
+ |
+ */
+
+ 'trim' => [
+ 'recent' => 60,
+ 'pending' => 60,
+ 'completed' => 60,
+ 'recent_failed' => 10080,
+ 'failed' => 10080,
+ 'monitored' => 10080,
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Metrics
+ |--------------------------------------------------------------------------
+ |
+ | Here you can configure how many snapshots should be kept to display in
+ | the metrics graph. This will get used in combination with Horizon's
+ | `horizon:snapshot` schedule to define how long to retain metrics.
+ |
+ */
+
+ 'metrics' => [
+ 'trim_snapshots' => [
+ 'job' => 24,
+ 'queue' => 24,
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Fast Termination
+ |--------------------------------------------------------------------------
+ |
+ | When this option is enabled, Horizon's "terminate" command will not
+ | wait on all of the workers to terminate unless the --wait option
+ | is provided. Fast termination can shorten deployment delay by
+ | allowing a new instance of Horizon to start while the last
+ | instance will continue to terminate each of its workers.
+ |
+ */
+
+ 'fast_termination' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Memory Limit (MB)
+ |--------------------------------------------------------------------------
+ |
+ | This value describes the maximum amount of memory the Horizon worker
+ | may consume before it is terminated and restarted. You should set
+ | this value according to the resources available to your server.
+ |
+ */
+
+ 'memory_limit' => 32,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Queue Worker Configuration
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define the queue worker settings used by your application
+ | in all environments. These supervisors and settings handle all your
+ | queued jobs and will be provisioned by Horizon during deployment.
+ |
+ */
+
+ 'environments' => [
+ 'local' => [
+ 'V2board' => [
+ 'connection' => 'redis',
+ 'queue' => [
+ 'order_handle',
+ 'traffic_fetch',
+ 'send_email',
+ 'send_email_mass',
+ 'send_telegram',
+ ],
+ 'balance' => 'auto',
+ 'minProcesses' => 1,
+ 'maxProcesses' => (int)ceil($parser->getRam()['total'] / 1024 / 1024 / 1024 * 6),
+ 'tries' => 1,
+ 'balanceCooldown' => 3,
+ ],
+ ],
+ ],
+];
diff --git a/config/logging.php b/config/logging.php
new file mode 100755
index 0000000..6963847
--- /dev/null
+++ b/config/logging.php
@@ -0,0 +1,99 @@
+ 'mysql',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log Channels
+ |--------------------------------------------------------------------------
+ |
+ | Here you may configure the log channels for your application. Out of
+ | the box, Laravel uses the Monolog PHP logging library. This gives
+ | you a variety of powerful log handlers / formatters to utilize.
+ |
+ | Available Drivers: "single", "daily", "slack", "syslog",
+ | "errorlog", "monolog",
+ | "custom", "stack"
+ |
+ */
+
+ 'channels' => [
+ 'mysql' => [
+ 'driver' => 'custom',
+ 'via' => App\Logging\MysqlLogger::class,
+ ],
+
+ 'stack' => [
+ 'driver' => 'stack',
+ 'channels' => ['daily'],
+ 'ignore_exceptions' => false,
+ ],
+
+ 'single' => [
+ 'driver' => 'single',
+ 'path' => storage_path('logs/laravel.log'),
+ 'level' => 'debug',
+ ],
+
+ 'daily' => [
+ 'driver' => 'daily',
+ 'path' => storage_path('logs/laravel.log'),
+ 'level' => 'debug',
+ 'days' => 14,
+ ],
+
+ 'slack' => [
+ 'driver' => 'slack',
+ 'url' => env('LOG_SLACK_WEBHOOK_URL'),
+ 'username' => 'Laravel Log',
+ 'emoji' => ':boom:',
+ 'level' => 'critical',
+ ],
+
+ 'papertrail' => [
+ 'driver' => 'monolog',
+ 'level' => 'debug',
+ 'handler' => SyslogUdpHandler::class,
+ 'handler_with' => [
+ 'host' => env('PAPERTRAIL_URL'),
+ 'port' => env('PAPERTRAIL_PORT'),
+ ],
+ ],
+
+ 'stderr' => [
+ 'driver' => 'monolog',
+ 'handler' => StreamHandler::class,
+ 'formatter' => env('LOG_STDERR_FORMATTER'),
+ 'with' => [
+ 'stream' => 'php://stderr',
+ ],
+ ],
+
+ 'syslog' => [
+ 'driver' => 'syslog',
+ 'level' => 'debug',
+ ],
+
+ 'errorlog' => [
+ 'driver' => 'errorlog',
+ 'level' => 'debug',
+ ],
+ ],
+
+];
diff --git a/config/mail.php b/config/mail.php
new file mode 100755
index 0000000..3c65eb3
--- /dev/null
+++ b/config/mail.php
@@ -0,0 +1,136 @@
+ env('MAIL_DRIVER', 'smtp'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | SMTP Host Address
+ |--------------------------------------------------------------------------
+ |
+ | Here you may provide the host address of the SMTP server used by your
+ | applications. A default option is provided that is compatible with
+ | the Mailgun mail service which will provide reliable deliveries.
+ |
+ */
+
+ 'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | SMTP Host Port
+ |--------------------------------------------------------------------------
+ |
+ | This is the SMTP port used by your application to deliver e-mails to
+ | users of the application. Like the host we have set this value to
+ | stay compatible with the Mailgun e-mail application by default.
+ |
+ */
+
+ 'port' => env('MAIL_PORT', 587),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Global "From" Address
+ |--------------------------------------------------------------------------
+ |
+ | You may wish for all e-mails sent by your application to be sent from
+ | the same address. Here, you may specify a name and address that is
+ | used globally for all e-mails that are sent by your application.
+ |
+ */
+
+ 'from' => [
+ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
+ 'name' => env('MAIL_FROM_NAME', 'Example'),
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | E-Mail Encryption Protocol
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the encryption protocol that should be used when
+ | the application send e-mail messages. A sensible default using the
+ | transport layer security protocol should provide great security.
+ |
+ */
+
+ 'encryption' => env('MAIL_ENCRYPTION', 'tls'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | SMTP Server Username
+ |--------------------------------------------------------------------------
+ |
+ | If your SMTP server requires a username for authentication, you should
+ | set it here. This will get used to authenticate with your server on
+ | connection. You may also set the "password" value below this one.
+ |
+ */
+
+ 'username' => env('MAIL_USERNAME'),
+
+ 'password' => env('MAIL_PASSWORD'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Sendmail System Path
+ |--------------------------------------------------------------------------
+ |
+ | When using the "sendmail" driver to send e-mails, we will need to know
+ | the path to where Sendmail lives on this server. A default path has
+ | been provided here, which will work well on most of your systems.
+ |
+ */
+
+ 'sendmail' => '/usr/sbin/sendmail -bs',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Markdown Mail Settings
+ |--------------------------------------------------------------------------
+ |
+ | If you are using Markdown based email rendering, you may configure your
+ | theme and component paths here, allowing you to customize the design
+ | of the emails. Or, you may simply stick with the Laravel defaults!
+ |
+ */
+
+ 'markdown' => [
+ 'theme' => 'default',
+
+ 'paths' => [
+ resource_path('views/vendor/mail'),
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log Channel
+ |--------------------------------------------------------------------------
+ |
+ | If you are using the "log" driver, you may specify the logging channel
+ | if you prefer to keep mail messages separate from other log entries
+ | for simpler reading. Otherwise, the default channel will be used.
+ |
+ */
+
+ 'log_channel' => env('MAIL_LOG_CHANNEL'),
+
+];
diff --git a/config/queue.php b/config/queue.php
new file mode 100755
index 0000000..495c858
--- /dev/null
+++ b/config/queue.php
@@ -0,0 +1,88 @@
+ env('QUEUE_CONNECTION', 'sync'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Queue Connections
+ |--------------------------------------------------------------------------
+ |
+ | Here you may configure the connection information for each server that
+ | is used by your application. A default configuration has been added
+ | for each back-end shipped with Laravel. You are free to add more.
+ |
+ | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
+ |
+ */
+
+ 'connections' => [
+
+ 'sync' => [
+ 'driver' => 'sync',
+ ],
+
+ 'database' => [
+ 'driver' => 'database',
+ 'table' => 'jobs',
+ 'queue' => 'default',
+ 'retry_after' => 90,
+ ],
+
+ 'beanstalkd' => [
+ 'driver' => 'beanstalkd',
+ 'host' => 'localhost',
+ 'queue' => 'default',
+ 'retry_after' => 90,
+ 'block_for' => 0,
+ ],
+
+ 'sqs' => [
+ 'driver' => 'sqs',
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
+ 'queue' => env('SQS_QUEUE', 'your-queue-name'),
+ 'region' => env('AWS_V2BOARD_REGION', 'us-east-1'),
+ ],
+
+ 'redis' => [
+ 'driver' => 'redis',
+ 'connection' => 'default',
+ 'queue' => env('REDIS_QUEUE', 'default'),
+ 'retry_after' => 90,
+ 'block_for' => null,
+ ],
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Failed Queue Jobs
+ |--------------------------------------------------------------------------
+ |
+ | These options configure the behavior of failed queue job logging so you
+ | can control which database and table are used to store the jobs that
+ | have failed. You may change them to any database / table you wish.
+ |
+ */
+
+ 'failed' => [
+ 'driver' => env('QUEUE_FAILED_DRIVER', 'database'),
+ 'database' => env('DB_CONNECTION', 'mysql'),
+ 'table' => 'failed_jobs',
+ ],
+
+];
diff --git a/config/services.php b/config/services.php
new file mode 100755
index 0000000..950dc99
--- /dev/null
+++ b/config/services.php
@@ -0,0 +1,33 @@
+ [
+ 'domain' => env('MAILGUN_DOMAIN'),
+ 'secret' => env('MAILGUN_SECRET'),
+ 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
+ ],
+
+ 'postmark' => [
+ 'token' => env('POSTMARK_TOKEN'),
+ ],
+
+ 'ses' => [
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'region' => env('AWS_V2BOARD_REGION', 'us-east-1'),
+ ],
+
+];
diff --git a/config/session.php b/config/session.php
new file mode 100755
index 0000000..406d50e
--- /dev/null
+++ b/config/session.php
@@ -0,0 +1,199 @@
+ env('SESSION_DRIVER', 'file'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Lifetime
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify the number of minutes that you wish the session
+ | to be allowed to remain idle before it expires. If you want them
+ | to immediately expire on the browser closing, set that option.
+ |
+ */
+
+ 'lifetime' => env('SESSION_LIFETIME', 120),
+
+ 'expire_on_close' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Encryption
+ |--------------------------------------------------------------------------
+ |
+ | This option allows you to easily specify that all of your session data
+ | should be encrypted before it is stored. All encryption will be run
+ | automatically by Laravel and you can use the Session like normal.
+ |
+ */
+
+ 'encrypt' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session File Location
+ |--------------------------------------------------------------------------
+ |
+ | When using the native session driver, we need a location where session
+ | files may be stored. A default has been set for you but a different
+ | location may be specified. This is only needed for file sessions.
+ |
+ */
+
+ 'files' => storage_path('framework/sessions'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Database Connection
+ |--------------------------------------------------------------------------
+ |
+ | When using the "database" or "redis" session drivers, you may specify a
+ | connection that should be used to manage these sessions. This should
+ | correspond to a connection in your database configuration options.
+ |
+ */
+
+ 'connection' => env('SESSION_CONNECTION', null),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Database Table
+ |--------------------------------------------------------------------------
+ |
+ | When using the "database" session driver, you may specify the table we
+ | should use to manage the sessions. Of course, a sensible default is
+ | provided for you; however, you are free to change this as needed.
+ |
+ */
+
+ 'table' => 'sessions',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Cache Store
+ |--------------------------------------------------------------------------
+ |
+ | When using the "apc", "memcached", or "dynamodb" session drivers you may
+ | list a cache store that should be used for these sessions. This value
+ | must match with one of the application's configured cache "stores".
+ |
+ */
+
+ 'store' => env('SESSION_STORE', null),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Sweeping Lottery
+ |--------------------------------------------------------------------------
+ |
+ | Some session drivers must manually sweep their storage location to get
+ | rid of old sessions from storage. Here are the chances that it will
+ | happen on a given request. By default, the odds are 2 out of 100.
+ |
+ */
+
+ 'lottery' => [2, 100],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Cookie Name
+ |--------------------------------------------------------------------------
+ |
+ | Here you may change the name of the cookie used to identify a session
+ | instance by ID. The name specified here will get used every time a
+ | new session cookie is created by the framework for every driver.
+ |
+ */
+
+ 'cookie' => env(
+ 'SESSION_COOKIE',
+ Str::slug(env('APP_NAME', 'laravel'), '_') . '_session'
+ ),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Cookie Path
+ |--------------------------------------------------------------------------
+ |
+ | The session cookie path determines the path for which the cookie will
+ | be regarded as available. Typically, this will be the root path of
+ | your application but you are free to change this when necessary.
+ |
+ */
+
+ 'path' => '/',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session Cookie Domain
+ |--------------------------------------------------------------------------
+ |
+ | Here you may change the domain of the cookie used to identify a session
+ | in your application. This will determine which domains the cookie is
+ | available to in your application. A sensible default has been set.
+ |
+ */
+
+ 'domain' => env('SESSION_DOMAIN', null),
+
+ /*
+ |--------------------------------------------------------------------------
+ | HTTPS Only Cookies
+ |--------------------------------------------------------------------------
+ |
+ | By setting this option to true, session cookies will only be sent back
+ | to the server if the browser has a HTTPS connection. This will keep
+ | the cookie from being sent to you if it can not be done securely.
+ |
+ */
+
+ 'secure' => env('SESSION_SECURE_COOKIE', false),
+
+ /*
+ |--------------------------------------------------------------------------
+ | HTTP Access Only
+ |--------------------------------------------------------------------------
+ |
+ | Setting this value to true will prevent JavaScript from accessing the
+ | value of the cookie and the cookie will only be accessible through
+ | the HTTP protocol. You are free to modify this option if needed.
+ |
+ */
+
+ 'http_only' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Same-Site Cookies
+ |--------------------------------------------------------------------------
+ |
+ | This option determines how your cookies behave when cross-site requests
+ | take place, and can be used to mitigate CSRF attacks. By default, we
+ | do not enable this as other CSRF protection services are in place.
+ |
+ | Supported: "lax", "strict"
+ |
+ */
+
+ 'same_site' => null,
+
+];
diff --git a/config/theme/.gitignore b/config/theme/.gitignore
new file mode 100644
index 0000000..9b8775c
--- /dev/null
+++ b/config/theme/.gitignore
@@ -0,0 +1,2 @@
+*.php
+!.gitignore
diff --git a/config/view.php b/config/view.php
new file mode 100755
index 0000000..22b8a18
--- /dev/null
+++ b/config/view.php
@@ -0,0 +1,36 @@
+ [
+ resource_path('views'),
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Compiled View Path
+ |--------------------------------------------------------------------------
+ |
+ | This option determines where all the compiled Blade templates will be
+ | stored for your application. Typically, this is within the storage
+ | directory. However, as usual, you are free to change this value.
+ |
+ */
+
+ 'compiled' => env(
+ 'VIEW_COMPILED_PATH',
+ realpath(storage_path('framework/views'))
+ ),
+
+];
diff --git a/database/.gitignore b/database/.gitignore
new file mode 100644
index 0000000..97fc976
--- /dev/null
+++ b/database/.gitignore
@@ -0,0 +1,2 @@
+*.sqlite
+*.sqlite-journal
diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php
new file mode 100644
index 0000000..741edea
--- /dev/null
+++ b/database/factories/UserFactory.php
@@ -0,0 +1,28 @@
+define(User::class, function (Faker $faker) {
+ return [
+ 'name' => $faker->name,
+ 'email' => $faker->unique()->safeEmail,
+ 'email_verified_at' => now(),
+ 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+ 'remember_token' => Str::random(10),
+ ];
+});
diff --git a/database/install.sql b/database/install.sql
new file mode 100644
index 0000000..0e609f7
--- /dev/null
+++ b/database/install.sql
@@ -0,0 +1,447 @@
+-- Adminer 4.7.7 MySQL dump
+
+SET NAMES utf8;
+SET time_zone = '+00:00';
+SET foreign_key_checks = 0;
+SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
+
+SET NAMES utf8mb4;
+
+DROP TABLE IF EXISTS `failed_jobs`;
+CREATE TABLE `failed_jobs` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+ `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+ `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `v2_commission_log`;
+CREATE TABLE `v2_commission_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `invite_user_id` int(11) NOT NULL,
+ `user_id` int(11) NOT NULL,
+ `trade_no` char(36) NOT NULL,
+ `order_amount` int(11) NOT NULL,
+ `get_amount` int(11) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_coupon`;
+CREATE TABLE `v2_coupon` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `code` varchar(255) NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
+ `type` tinyint(1) NOT NULL,
+ `value` int(11) NOT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `limit_use` int(11) DEFAULT NULL,
+ `limit_use_with_user` int(11) DEFAULT NULL,
+ `limit_plan_ids` varchar(255) DEFAULT NULL,
+ `limit_period` varchar(255) DEFAULT NULL,
+ `started_at` int(11) NOT NULL,
+ `ended_at` int(11) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_invite_code`;
+CREATE TABLE `v2_invite_code` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `code` char(32) NOT NULL,
+ `status` tinyint(1) NOT NULL DEFAULT '0',
+ `pv` int(11) NOT NULL DEFAULT '0',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_knowledge`;
+CREATE TABLE `v2_knowledge` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `language` char(5) NOT NULL COMMENT '語言',
+ `category` varchar(255) NOT NULL COMMENT '分類名',
+ `title` varchar(255) NOT NULL COMMENT '標題',
+ `body` text NOT NULL COMMENT '內容',
+ `sort` int(11) DEFAULT NULL COMMENT '排序',
+ `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '顯示',
+ `created_at` int(11) NOT NULL COMMENT '創建時間',
+ `updated_at` int(11) NOT NULL COMMENT '更新時間',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知識庫';
+
+
+DROP TABLE IF EXISTS `v2_log`;
+CREATE TABLE `v2_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `level` varchar(11) DEFAULT NULL,
+ `host` varchar(255) DEFAULT NULL,
+ `uri` varchar(255) NOT NULL,
+ `method` varchar(11) NOT NULL,
+ `data` text,
+ `ip` varchar(128) DEFAULT NULL,
+ `context` text,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_mail_log`;
+CREATE TABLE `v2_mail_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `email` varchar(64) NOT NULL,
+ `subject` varchar(255) NOT NULL,
+ `template_name` varchar(255) NOT NULL,
+ `error` text,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_notice`;
+CREATE TABLE `v2_notice` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `content` text NOT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `img_url` varchar(255) DEFAULT NULL,
+ `tags` varchar(255) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_order`;
+CREATE TABLE `v2_order` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `invite_user_id` int(11) DEFAULT NULL,
+ `user_id` int(11) NOT NULL,
+ `plan_id` int(11) NOT NULL,
+ `coupon_id` int(11) DEFAULT NULL,
+ `payment_id` int(11) DEFAULT NULL,
+ `type` int(11) NOT NULL COMMENT '1新购2续费3升级',
+ `period` varchar(255) NOT NULL,
+ `trade_no` varchar(36) NOT NULL,
+ `callback_no` varchar(255) DEFAULT NULL,
+ `total_amount` int(11) NOT NULL,
+ `handling_amount` int(11) DEFAULT NULL,
+ `discount_amount` int(11) DEFAULT NULL,
+ `surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
+ `refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
+ `balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
+ `surplus_order_ids` text COMMENT '折抵订单',
+ `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
+ `commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
+ `commission_balance` int(11) NOT NULL DEFAULT '0',
+ `actual_commission_balance` int(11) DEFAULT NULL COMMENT '实际支付佣金',
+ `paid_at` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `trade_no` (`trade_no`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_payment`;
+CREATE TABLE `v2_payment` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `uuid` char(32) NOT NULL,
+ `payment` varchar(16) NOT NULL,
+ `name` varchar(255) NOT NULL,
+ `icon` varchar(255) DEFAULT NULL,
+ `config` text NOT NULL,
+ `notify_domain` varchar(128) DEFAULT NULL,
+ `handling_fee_fixed` int(11) DEFAULT NULL,
+ `handling_fee_percent` decimal(5,2) DEFAULT NULL,
+ `enable` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_plan`;
+CREATE TABLE `v2_plan` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `group_id` int(11) NOT NULL,
+ `transfer_enable` int(11) NOT NULL,
+ `name` varchar(255) NOT NULL,
+ `speed_limit` int(11) DEFAULT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `renew` tinyint(1) NOT NULL DEFAULT '1',
+ `content` text,
+ `month_price` int(11) DEFAULT NULL,
+ `quarter_price` int(11) DEFAULT NULL,
+ `half_year_price` int(11) DEFAULT NULL,
+ `year_price` int(11) DEFAULT NULL,
+ `two_year_price` int(11) DEFAULT NULL,
+ `three_year_price` int(11) DEFAULT NULL,
+ `onetime_price` int(11) DEFAULT NULL,
+ `reset_price` int(11) DEFAULT NULL,
+ `reset_traffic_method` tinyint(1) DEFAULT NULL,
+ `capacity_limit` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_server_group`;
+CREATE TABLE `v2_server_group` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_server_hysteria`;
+CREATE TABLE `v2_server_hysteria` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `group_id` varchar(255) NOT NULL,
+ `route_id` varchar(255) DEFAULT NULL,
+ `name` varchar(255) NOT NULL,
+ `parent_id` int(11) DEFAULT NULL,
+ `host` varchar(255) NOT NULL,
+ `port` varchar(11) NOT NULL,
+ `server_port` int(11) NOT NULL,
+ `tags` varchar(255) DEFAULT NULL,
+ `rate` varchar(11) NOT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `up_mbps` int(11) NOT NULL,
+ `down_mbps` int(11) NOT NULL,
+ `server_name` varchar(64) DEFAULT NULL,
+ `insecure` tinyint(1) NOT NULL DEFAULT '0',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_server_route`;
+CREATE TABLE `v2_server_route` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `remarks` varchar(255) NOT NULL,
+ `match` text NOT NULL,
+ `action` varchar(11) NOT NULL,
+ `action_value` varchar(255) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_server_shadowsocks`;
+CREATE TABLE `v2_server_shadowsocks` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `group_id` varchar(255) NOT NULL,
+ `route_id` varchar(255) DEFAULT NULL,
+ `parent_id` int(11) DEFAULT NULL,
+ `tags` varchar(255) DEFAULT NULL,
+ `name` varchar(255) NOT NULL,
+ `rate` varchar(11) NOT NULL,
+ `host` varchar(255) NOT NULL,
+ `port` varchar(11) NOT NULL,
+ `server_port` int(11) NOT NULL,
+ `cipher` varchar(255) NOT NULL,
+ `obfs` char(11) DEFAULT NULL,
+ `obfs_settings` varchar(255) DEFAULT NULL,
+ `show` tinyint(4) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_server_trojan`;
+CREATE TABLE `v2_server_trojan` (
+ `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '节点ID',
+ `group_id` varchar(255) NOT NULL COMMENT '节点组',
+ `route_id` varchar(255) DEFAULT NULL,
+ `parent_id` int(11) DEFAULT NULL COMMENT '父节点',
+ `tags` varchar(255) DEFAULT NULL COMMENT '节点标签',
+ `name` varchar(255) NOT NULL COMMENT '节点名称',
+ `rate` varchar(11) NOT NULL COMMENT '倍率',
+ `host` varchar(255) NOT NULL COMMENT '主机名',
+ `port` varchar(11) NOT NULL COMMENT '连接端口',
+ `server_port` int(11) NOT NULL COMMENT '服务端口',
+ `allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全',
+ `server_name` varchar(255) DEFAULT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示',
+ `sort` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
+
+
+DROP TABLE IF EXISTS `v2_server_vmess`;
+CREATE TABLE `v2_server_vmess` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `group_id` varchar(255) NOT NULL,
+ `route_id` varchar(255) DEFAULT NULL,
+ `name` varchar(255) NOT NULL,
+ `parent_id` int(11) DEFAULT NULL,
+ `host` varchar(255) NOT NULL,
+ `port` varchar(11) NOT NULL,
+ `server_port` int(11) NOT NULL,
+ `tls` tinyint(4) NOT NULL DEFAULT '0',
+ `tags` varchar(255) DEFAULT NULL,
+ `rate` varchar(11) NOT NULL,
+ `network` varchar(11) NOT NULL,
+ `rules` text,
+ `networkSettings` text,
+ `tlsSettings` text,
+ `ruleSettings` text,
+ `dnsSettings` text,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_stat`;
+CREATE TABLE `v2_stat` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `record_at` int(11) NOT NULL,
+ `record_type` char(1) NOT NULL,
+ `order_count` int(11) NOT NULL COMMENT '订单数量',
+ `order_total` int(11) NOT NULL COMMENT '订单合计',
+ `commission_count` int(11) NOT NULL,
+ `commission_total` int(11) NOT NULL COMMENT '佣金合计',
+ `paid_count` int(11) NOT NULL,
+ `paid_total` int(11) NOT NULL,
+ `register_count` int(11) NOT NULL,
+ `invite_count` int(11) NOT NULL,
+ `transfer_used_total` varchar(32) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `record_at` (`record_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单统计';
+
+
+DROP TABLE IF EXISTS `v2_stat_server`;
+CREATE TABLE `v2_stat_server` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `server_id` int(11) NOT NULL COMMENT '节点id',
+ `server_type` char(11) NOT NULL COMMENT '节点类型',
+ `u` bigint(20) NOT NULL,
+ `d` bigint(20) NOT NULL,
+ `record_type` char(1) NOT NULL COMMENT 'd day m month',
+ `record_at` int(11) NOT NULL COMMENT '记录时间',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `server_id_server_type_record_at` (`server_id`,`server_type`,`record_at`),
+ KEY `record_at` (`record_at`),
+ KEY `server_id` (`server_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
+
+
+DROP TABLE IF EXISTS `v2_stat_user`;
+CREATE TABLE `v2_stat_user` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `server_rate` decimal(10,2) NOT NULL,
+ `u` bigint(20) NOT NULL,
+ `d` bigint(20) NOT NULL,
+ `record_type` char(2) NOT NULL,
+ `record_at` int(11) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `server_rate_user_id_record_at` (`server_rate`,`user_id`,`record_at`),
+ KEY `user_id` (`user_id`),
+ KEY `record_at` (`record_at`),
+ KEY `server_rate` (`server_rate`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+DROP TABLE IF EXISTS `v2_ticket`;
+CREATE TABLE `v2_ticket` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `subject` varchar(255) NOT NULL,
+ `level` tinyint(1) NOT NULL,
+ `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
+ `reply_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0:待回复 1:已回复',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_ticket_message`;
+CREATE TABLE `v2_ticket_message` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `ticket_id` int(11) NOT NULL,
+ `message` text CHARACTER SET utf8mb4 NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `v2_user`;
+CREATE TABLE `v2_user` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `invite_user_id` int(11) DEFAULT NULL,
+ `telegram_id` bigint(20) DEFAULT NULL,
+ `email` varchar(64) NOT NULL,
+ `password` varchar(64) NOT NULL,
+ `password_algo` char(10) DEFAULT NULL,
+ `password_salt` char(10) DEFAULT NULL,
+ `balance` int(11) NOT NULL DEFAULT '0',
+ `discount` int(11) DEFAULT NULL,
+ `commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: period 2: onetime',
+ `commission_rate` int(11) DEFAULT NULL,
+ `commission_balance` int(11) NOT NULL DEFAULT '0',
+ `t` int(11) NOT NULL DEFAULT '0',
+ `u` bigint(20) NOT NULL DEFAULT '0',
+ `d` bigint(20) NOT NULL DEFAULT '0',
+ `transfer_enable` bigint(20) NOT NULL DEFAULT '0',
+ `banned` tinyint(1) NOT NULL DEFAULT '0',
+ `is_admin` tinyint(1) NOT NULL DEFAULT '0',
+ `last_login_at` int(11) DEFAULT NULL,
+ `is_staff` tinyint(1) NOT NULL DEFAULT '0',
+ `last_login_ip` int(11) DEFAULT NULL,
+ `uuid` varchar(36) NOT NULL,
+ `group_id` int(11) DEFAULT NULL,
+ `plan_id` int(11) DEFAULT NULL,
+ `speed_limit` int(11) DEFAULT NULL,
+ `remind_expire` tinyint(4) DEFAULT '1',
+ `remind_traffic` tinyint(4) DEFAULT '1',
+ `token` char(32) NOT NULL,
+ `expired_at` bigint(20) DEFAULT '0',
+ `remarks` text,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `email` (`email`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+-- 2023-05-23 17:01:12
diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php
new file mode 100644
index 0000000..389bdf7
--- /dev/null
+++ b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php
@@ -0,0 +1,35 @@
+bigIncrements('id');
+ $table->text('connection');
+ $table->text('queue');
+ $table->longText('payload');
+ $table->longText('exception');
+ $table->timestamp('failed_at')->useCurrent();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('failed_jobs');
+ }
+}
diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php
new file mode 100644
index 0000000..91cb6d1
--- /dev/null
+++ b/database/seeds/DatabaseSeeder.php
@@ -0,0 +1,16 @@
+call(UsersTableSeeder::class);
+ }
+}
diff --git a/database/update.sql b/database/update.sql
new file mode 100644
index 0000000..490003a
--- /dev/null
+++ b/database/update.sql
@@ -0,0 +1,686 @@
+ALTER TABLE `v2_server`
+ADD `last_check_at` int(11) NULL AFTER `rate`;
+
+ALTER TABLE `v2_server`
+ADD `network` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `rate`;
+
+ALTER TABLE `v2_server`
+ADD `settings` text COLLATE 'utf8_general_ci' NULL AFTER `network`;
+
+ALTER TABLE `v2_server`
+ADD `show` tinyint(1) NOT NULL DEFAULT '0' AFTER `settings`;
+
+ALTER TABLE `v2_user`
+CHANGE `enable` `enable` tinyint(1) NOT NULL DEFAULT '1' AFTER `transfer_enable`;
+
+ALTER TABLE `v2_order`
+ADD `type` int(11) NOT NULL COMMENT '1新购2续费3升级' AFTER `plan_id`;
+
+ALTER TABLE `v2_user`
+ADD `commission_rate` int(11) NULL AFTER `password`;
+
+ALTER TABLE `v2_user`
+ADD `balance` int(11) NOT NULL DEFAULT '0' AFTER `password`;
+
+CREATE TABLE `v2_notice` (
+ `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `title` varchar(255) NOT NULL,
+ `content` text NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+);
+
+ALTER TABLE `v2_notice`
+ADD `img_url` varchar(255) COLLATE 'utf8_general_ci' NULL AFTER `content`;
+
+CREATE TABLE `v2_ticket` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `subject` varchar(255) NOT NULL,
+ `level` tinyint(1) NOT NULL,
+ `status` tinyint(1) NOT NULL DEFAULT '0',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `v2_ticket_message` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `ticket_id` int(11) NOT NULL,
+ `message` varchar(255) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE `v2_ticket`
+ADD `last_reply_user_id` int(11) NOT NULL AFTER `user_id`;
+
+ALTER TABLE `v2_user`
+CHANGE `last_login_at` `last_login_at` int(11) NULL AFTER `is_admin`;
+
+ALTER TABLE `v2_server_log`
+CHANGE `node_id` `server_id` int(11) NOT NULL AFTER `user_id`,
+CHANGE `u` `u` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `server_id`,
+CHANGE `d` `d` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `u`,
+CHANGE `rate` `rate` int(11) NOT NULL AFTER `d`;
+
+ALTER TABLE `v2_server`
+DROP `last_check_at`;
+
+ALTER TABLE `v2_server`
+CHANGE `name` `name` varchar(255) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `group_id`;
+
+ALTER TABLE `v2_plan`
+CHANGE `month_price` `month_price` int(11) NULL DEFAULT '0' AFTER `content`,
+CHANGE `quarter_price` `quarter_price` int(11) NULL DEFAULT '0' AFTER `month_price`,
+CHANGE `half_year_price` `half_year_price` int(11) NULL DEFAULT '0' AFTER `quarter_price`,
+CHANGE `year_price` `year_price` int(11) NULL DEFAULT '0' AFTER `half_year_price`;
+
+ALTER TABLE `v2_server`
+ADD `parent_id` int(11) NULL AFTER `group_id`;
+
+CREATE TABLE `v2_mail_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `email` varchar(64) NOT NULL,
+ `subject` varchar(255) NOT NULL,
+ `template_name` varchar(255) NOT NULL,
+ `error` varchar(255) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+);
+
+CREATE TABLE `v2_coupon` (
+ `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `code` char(32) NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
+ `type` tinyint(1) NOT NULL,
+ `value` int(11) NOT NULL,
+ `limit_use` int(11) DEFAULT NULL,
+ `started_at` int(11) NOT NULL,
+ `ended_at` int(11) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+);
+
+ALTER TABLE `v2_order`
+ADD `discount_amount` int(11) NULL AFTER `total_amount`;
+
+ALTER TABLE `v2_server_log`
+CHANGE `rate` `rate` decimal(10,2) NOT NULL AFTER `d`;
+
+ALTER TABLE `v2_order`
+DROP `method`;
+
+ALTER TABLE `v2_invite_code`
+ADD `pv` int(11) NOT NULL DEFAULT '0' AFTER `status`;
+
+ALTER TABLE `v2_user`
+ADD `password_algo` char(10) COLLATE 'utf8_general_ci' NULL AFTER `password`;
+
+ALTER TABLE `v2_server`
+CHANGE `tls` `tls` tinyint(4) NOT NULL DEFAULT '0' AFTER `server_port`;
+
+ALTER TABLE `v2_server`
+ADD `rules` text COLLATE 'utf8_general_ci' NULL AFTER `settings`;
+
+CREATE TABLE `failed_jobs` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+ `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+ `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+ALTER TABLE `v2_user`
+ADD `discount` int(11) NULL AFTER `balance`;
+
+ALTER TABLE `v2_order`
+ADD `surplus_amount` int(11) NULL COMMENT '剩余价值' AFTER `discount_amount`;
+
+ALTER TABLE `v2_order`
+ADD `refund_amount` int(11) NULL COMMENT '退款金额' AFTER `surplus_amount`;
+
+ALTER TABLE `v2_tutorial`
+ADD `category_id` int(11) NOT NULL AFTER `id`;
+
+ALTER TABLE `v2_tutorial`
+DROP `description`;
+
+ALTER TABLE `v2_plan`
+CHANGE `month_price` `month_price` int(11) NULL AFTER `content`,
+CHANGE `quarter_price` `quarter_price` int(11) NULL AFTER `month_price`,
+CHANGE `half_year_price` `half_year_price` int(11) NULL AFTER `quarter_price`,
+CHANGE `year_price` `year_price` int(11) NULL AFTER `half_year_price`,
+ADD `onetime_price` int(11) NULL AFTER `year_price`;
+
+ALTER TABLE `v2_user`
+DROP `enable`,
+ADD `banned` tinyint(1) NOT NULL DEFAULT '0' AFTER `transfer_enable`;
+
+ALTER TABLE `v2_user`
+CHANGE `expired_at` `expired_at` bigint(20) NULL DEFAULT '0' AFTER `token`;
+
+ALTER TABLE `v2_tutorial`
+DROP `icon`;
+
+ALTER TABLE `v2_server`
+CHANGE `settings` `networkSettings` text COLLATE 'utf8_general_ci' NULL AFTER `network`,
+CHANGE `rules` `ruleSettings` text COLLATE 'utf8_general_ci' NULL AFTER `networkSettings`;
+
+ALTER TABLE `v2_server`
+CHANGE `tags` `tags` varchar(255) COLLATE 'utf8_general_ci' NULL AFTER `server_port`,
+CHANGE `rate` `rate` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `tags`,
+CHANGE `network` `network` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `rate`,
+CHANGE `networkSettings` `networkSettings` text COLLATE 'utf8_general_ci' NULL AFTER `network`,
+CHANGE `tls` `tls` tinyint(4) NOT NULL DEFAULT '0' AFTER `networkSettings`,
+ADD `tlsSettings` text COLLATE 'utf8_general_ci' NULL AFTER `tls`;
+
+ALTER TABLE `v2_order`
+ADD `balance_amount` int(11) NULL COMMENT '使用余额' AFTER `refund_amount`;
+
+ALTER TABLE `v2_server`
+CHANGE `network` `network` text COLLATE 'utf8_general_ci' NOT NULL AFTER `rate`,
+ADD `dnsSettings` text COLLATE 'utf8_general_ci' NULL AFTER `ruleSettings`;
+
+ALTER TABLE `v2_order`
+ADD `surplus_order_ids` text NULL COMMENT '折抵订单' AFTER `balance_amount`;
+
+ALTER TABLE `v2_order`
+CHANGE `status` `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵' AFTER `surplus_order_ids`;
+
+CREATE TABLE `v2_server_stat` (
+ `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `server_id` int(11) NOT NULL,
+ `u` varchar(255) NOT NULL,
+ `d` varchar(25) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+);
+
+ALTER TABLE `v2_tutorial`
+ADD `sort` int(11) NULL AFTER `show`;
+
+ALTER TABLE `v2_server`
+ADD `sort` int(11) NULL AFTER `show`;
+
+ALTER TABLE `v2_plan`
+ADD `sort` int(11) NULL AFTER `show`;
+
+ALTER TABLE `v2_plan`
+CHANGE `month_price` `month_price` int(11) NULL AFTER `content`,
+CHANGE `quarter_price` `quarter_price` int(11) NULL AFTER `month_price`,
+CHANGE `half_year_price` `half_year_price` int(11) NULL AFTER `quarter_price`,
+CHANGE `year_price` `year_price` int(11) NULL AFTER `half_year_price`,
+ADD `reset_price` int(11) NULL AFTER `onetime_price`;
+
+ALTER TABLE `v2_server_log`
+ADD `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
+
+ALTER TABLE `v2_server_log`
+ADD `log_at` int(11) NOT NULL AFTER `rate`;
+
+ALTER TABLE `v2_mail_log`
+CHANGE `error` `error` text COLLATE 'utf8_general_ci' NULL AFTER `template_name`;
+
+ALTER TABLE `v2_plan`
+CHANGE `month_price` `month_price` int(11) NULL AFTER `content`,
+CHANGE `quarter_price` `quarter_price` int(11) NULL AFTER `month_price`,
+CHANGE `half_year_price` `half_year_price` int(11) NULL AFTER `quarter_price`,
+CHANGE `year_price` `year_price` int(11) NULL AFTER `half_year_price`;
+
+ALTER TABLE `v2_server_log`
+ADD INDEX log_at (`log_at`);
+
+ALTER TABLE `v2_user`
+ADD `telegram_id` bigint NULL AFTER `invite_user_id`;
+
+ALTER TABLE `v2_server_stat`
+ADD `online` int(11) NOT NULL AFTER `d`;
+
+ALTER TABLE `v2_server_stat`
+ADD INDEX `created_at` (`created_at`);
+
+CREATE TABLE `v2_server_trojan` (
+ `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `group_id` varchar(255) NOT NULL,
+ `tags` varchar(255) NULL,
+ `name` varchar(255) NOT NULL,
+ `host` varchar(255) NOT NULL,
+ `port` int(11) NOT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+) COMMENT='trojan伺服器表' COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_server_stat`
+CHANGE `d` `d` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `u`,
+DROP `online`;
+
+ALTER TABLE `v2_user`
+CHANGE `v2ray_uuid` `uuid` varchar(36) COLLATE 'utf8_general_ci' NOT NULL AFTER `last_login_ip`;
+
+ALTER TABLE `v2_server_trojan`
+ADD `rate` varchar(11) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `name`;
+
+ALTER TABLE `v2_server_log`
+ADD `method` varchar(255) NOT NULL AFTER `rate`;
+
+ALTER TABLE `v2_coupon`
+ADD `limit_plan_ids` varchar(255) NULL AFTER `limit_use`;
+
+ALTER TABLE `v2_server_trojan`
+ADD `server_port` int(11) NOT NULL AFTER `port`;
+
+ALTER TABLE `v2_server_trojan`
+ADD `parent_id` int(11) NULL AFTER `group_id`;
+
+ALTER TABLE `v2_server_trojan`
+ADD `allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全' AFTER `server_port`,
+CHANGE `show` `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示' AFTER `allow_insecure`;
+
+ALTER TABLE `v2_server_trojan`
+ADD `server_name` varchar(255) NULL AFTER `allow_insecure`;
+
+UPDATE `v2_server` SET
+`ruleSettings` = NULL
+WHERE `ruleSettings` = '{}';
+
+ALTER TABLE `v2_plan`
+ADD `two_year_price` int(11) NULL AFTER `year_price`,
+ADD `three_year_price` int(11) NULL AFTER `two_year_price`;
+
+ALTER TABLE `v2_user`
+ADD `is_staff` tinyint(1) NOT NULL DEFAULT '0' AFTER `is_admin`;
+
+CREATE TABLE `v2_server_shadowsocks` (
+ `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `group_id` varchar(255) NOT NULL,
+ `parent_id` int(11) NULL,
+ `tags` varchar(255) NULL,
+ `name` varchar(255) NOT NULL,
+ `rate` varchar(11) NOT NULL,
+ `host` varchar(255) NOT NULL,
+ `port` int(11) NOT NULL,
+ `server_port` int(11) NOT NULL,
+ `cipher` varchar(255) NOT NULL,
+ `show` tinyint NOT NULL DEFAULT '0',
+ `sort` int(11) NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+) COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_coupon`
+CHANGE `code` `code` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `id`;
+
+CREATE TABLE `v2_knowledge` (
+ `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `language` char(5) NOT NULL COMMENT '語言',
+ `category` varchar(255) NOT NULL COMMENT '分類名',
+ `title` varchar(255) NOT NULL COMMENT '標題',
+ `body` text NOT NULL COMMENT '內容',
+ `sort` int(11) NULL COMMENT '排序',
+ `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '顯示',
+ `created_at` int(11) NOT NULL COMMENT '創建時間',
+ `updated_at` int(11) NOT NULL COMMENT '更新時間'
+) COMMENT='知識庫' COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_order`
+ADD `coupon_id` int(11) NULL AFTER `plan_id`;
+
+ALTER TABLE `v2_server_stat`
+ADD `method` varchar(255) NOT NULL AFTER `server_id`;
+
+ALTER TABLE `v2_server`
+ADD `alter_id` int(11) NOT NULL DEFAULT '1' AFTER `network`;
+
+ALTER TABLE `v2_user`
+DROP `v2ray_alter_id`,
+DROP `v2ray_level`;
+
+DROP TABLE `v2_server_stat`;
+
+CREATE TABLE `v2_stat_server` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `server_id` int(11) NOT NULL COMMENT '节点id',
+ `server_type` char(11) NOT NULL COMMENT '节点类型',
+ `u` varchar(255) NOT NULL,
+ `d` varchar(255) NOT NULL,
+ `record_type` char(1) NOT NULL COMMENT 'd day m month',
+ `record_at` int(11) NOT NULL COMMENT '记录时间',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
+
+ALTER TABLE `v2_stat_server`
+ADD UNIQUE `server_id_server_type_record_at` (`server_id`, `server_type`, `record_at`);
+
+ALTER TABLE `v2_stat_server`
+ADD INDEX `record_at` (`record_at`),
+ADD INDEX `server_id` (`server_id`);
+
+ALTER TABLE `v2_user`
+DROP `enable`;
+
+ALTER TABLE `v2_user`
+ ADD `remarks` text COLLATE 'utf8_general_ci' NULL AFTER `token`;
+
+CREATE TABLE `v2_payment` (
+ `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `payment` varchar(16) NOT NULL,
+ `name` varchar(255) NOT NULL,
+ `config` text NOT NULL,
+ `enable` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+) COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_order`
+ ADD `payment_id` int(11) NULL AFTER `coupon_id`;
+
+ALTER TABLE `v2_payment`
+ ADD `uuid` char(32) NOT NULL AFTER `id`;
+
+ALTER TABLE `v2_user`
+ ADD UNIQUE `email_deleted_at` (`email`, `deleted_at`),
+DROP INDEX `email`;
+
+ALTER TABLE `v2_user`
+DROP `deleted_at`;
+
+ALTER TABLE `v2_user`
+ ADD UNIQUE `email` (`email`),
+DROP INDEX `email_deleted_at`;
+
+ALTER TABLE `v2_user`
+ ADD `commission_type` tinyint NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime' AFTER `discount`;
+
+ALTER TABLE `v2_order`
+ ADD `paid_at` int(11) NULL AFTER `commission_balance`;
+
+ALTER TABLE `v2_server_log`
+ ADD INDEX `user_id` (`user_id`),
+ADD INDEX `server_id` (`server_id`);
+
+ALTER TABLE `v2_ticket_message`
+ CHANGE `message` `message` text COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `ticket_id`;
+
+ALTER TABLE `v2_coupon`
+ ADD `limit_use_with_user` int(11) NULL AFTER `limit_use`;
+
+ALTER TABLE `v2_user`
+ ADD `password_salt` char(10) COLLATE 'utf8_general_ci' NULL AFTER `password_algo`;
+
+CREATE TABLE `v2_commission_log` (
+ `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ `invite_user_id` int(11) NOT NULL,
+ `user_id` int(11) NOT NULL,
+ `trade_no` char(36) NOT NULL,
+ `order_amount` int(11) NOT NULL,
+ `get_amount` int(11) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL
+) COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_plan`
+ ADD `reset_traffic_method` tinyint(1) NULL AFTER `reset_price`;
+
+ALTER TABLE `v2_server`
+ RENAME TO `v2_server_v2ray`;
+
+ALTER TABLE `v2_payment`
+ ADD `icon` varchar(255) COLLATE 'utf8mb4_general_ci' NULL AFTER `name`;
+
+ALTER TABLE `v2_coupon`
+ ADD `limit_period` varchar(255) COLLATE 'utf8_general_ci' NULL AFTER `limit_plan_ids`;
+
+ALTER TABLE `v2_order`
+ CHANGE `cycle` `period` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `type`;
+
+ALTER TABLE `v2_server_v2ray`
+DROP `alter_id`;
+
+ALTER TABLE `v2_user`
+ CHANGE `commission_type` `commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: period 2: onetime' AFTER `discount`;
+
+ALTER TABLE `v2_coupon`
+ ADD `show` tinyint(1) NOT NULL DEFAULT '0' AFTER `value`;
+
+ALTER TABLE `v2_notice`
+ ADD `show` tinyint(1) NOT NULL DEFAULT '0' AFTER `content`;
+
+ALTER TABLE `v2_order`
+ ADD `actual_commission_balance` int(11) NULL COMMENT '实际支付佣金' AFTER `commission_balance`;
+
+ALTER TABLE `v2_server_v2ray`
+ CHANGE `port` `port` char(11) NOT NULL AFTER `host`;
+
+CREATE TABLE `v2_stat_user` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `server_id` int(11) NOT NULL,
+ `server_type` char(11) NOT NULL,
+ `server_rate` decimal(10,2) NOT NULL,
+ `u` bigint(20) NOT NULL,
+ `d` bigint(20) NOT NULL,
+ `record_type` char(2) NOT NULL,
+ `record_at` int(11) NOT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+ALTER TABLE `v2_payment`
+ ADD `notify_domain` varchar(128) COLLATE 'utf8mb4_general_ci' NULL AFTER `config`;
+
+ALTER TABLE `v2_stat_user`
+ ADD INDEX `server_id` (`server_id`),
+ADD INDEX `user_id` (`user_id`),
+ADD INDEX `record_at` (`record_at`);
+
+ALTER TABLE `v2_stat_server`
+ CHANGE `u` `u` bigint NOT NULL AFTER `server_type`,
+ CHANGE `d` `d` bigint NOT NULL AFTER `u`;
+
+ALTER TABLE `v2_payment`
+ ADD `handling_fee_fixed` int(11) NULL AFTER `notify_domain`,
+ADD `handling_fee_percent` decimal(5,2) NULL AFTER `handling_fee_fixed`;
+
+ALTER TABLE `v2_order`
+ ADD `handling_amount` int(11) NULL AFTER `total_amount`;
+
+DELIMITER $$
+
+DROP PROCEDURE IF EXISTS `path-2022-03-29` $$
+CREATE PROCEDURE `path-2022-03-29`()
+BEGIN
+
+ DECLARE IndexIsThere INTEGER;
+
+SELECT COUNT(1) INTO IndexIsThere
+FROM INFORMATION_SCHEMA.STATISTICS
+WHERE table_name = 'v2_stat_user'
+ AND index_name = 'server_id';
+
+IF IndexIsThere != 0 THEN
+ TRUNCATE TABLE `v2_stat_user`;
+END IF;
+
+END $$
+
+DELIMITER ;
+CALL `path-2022-03-29`();
+DROP PROCEDURE IF EXISTS `path-2022-03-29`;
+
+ALTER TABLE `v2_stat_user`
+ ADD UNIQUE `server_rate_user_id_record_at` (`server_rate`, `user_id`, `record_at`);
+ALTER TABLE `v2_stat_user`
+ ADD INDEX `server_rate` (`server_rate`);
+ALTER TABLE `v2_stat_user`
+DROP INDEX `server_id_user_id_record_at`;
+ALTER TABLE `v2_stat_user`
+DROP INDEX `server_id`;
+
+ALTER TABLE `v2_stat_user`
+DROP `server_id`;
+ALTER TABLE `v2_stat_user`
+DROP `server_type`;
+
+ALTER TABLE `v2_notice`
+ ADD `tags` varchar(255) COLLATE 'utf8_general_ci' NULL AFTER `img_url`;
+
+ALTER TABLE `v2_ticket`
+ADD `reply_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0:待回复 1:已回复' AFTER `status`;
+
+ALTER TABLE `v2_server_v2ray`
+DROP `settings`;
+
+ALTER TABLE `v2_ticket`
+DROP `last_reply_user_id`;
+
+ALTER TABLE `v2_server_shadowsocks`
+ ADD `obfs` char(11) NULL AFTER `cipher`,
+ADD `obfs_settings` varchar(255) NULL AFTER `obfs`;
+
+ALTER TABLE `v2_plan`
+ CHANGE `name` `name` varchar(255) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `transfer_enable`,
+ CHANGE `content` `content` text COLLATE 'utf8mb4_general_ci' NULL AFTER `renew`;
+
+ALTER TABLE `v2_mail_log`
+ COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_mail_log`
+ CHANGE `email` `email` varchar(64) NOT NULL AFTER `id`,
+ CHANGE `subject` `subject` varchar(255) NOT NULL AFTER `email`,
+ CHANGE `template_name` `template_name` varchar(255) NOT NULL AFTER `subject`,
+ CHANGE `error` `error` text NULL AFTER `template_name`;
+
+ALTER TABLE `v2_user`
+ ADD `speed_limit` int(11) NULL AFTER `plan_id`;
+
+ALTER TABLE `v2_plan`
+ ADD `speed_limit` int(11) NULL AFTER `transfer_enable`;
+ALTER TABLE `v2_server_v2ray`
+ CHANGE `port` `port` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `host`;
+ALTER TABLE `v2_server_shadowsocks`
+ CHANGE `port` `port` varchar(11) NOT NULL AFTER `host`;
+ALTER TABLE `v2_server_trojan`
+ CHANGE `port` `port` varchar(11) NOT NULL COMMENT '连接端口' AFTER `host`;
+
+ALTER TABLE `v2_server_shadowsocks`
+ ADD `route_id` varchar(255) COLLATE 'utf8mb4_general_ci' NULL AFTER `group_id`;
+
+ALTER TABLE `v2_server_trojan`
+ ADD `route_id` varchar(255) COLLATE 'utf8mb4_general_ci' NULL AFTER `group_id`;
+
+ALTER TABLE `v2_server_v2ray`
+ COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_server_v2ray`
+ CHANGE `group_id` `group_id` varchar(255) NOT NULL AFTER `id`,
+ CHANGE `route_id` `route_id` varchar(255) NULL AFTER `group_id`,
+ CHANGE `host` `host` varchar(255) NOT NULL AFTER `parent_id`,
+ CHANGE `port` `port` varchar(11) NOT NULL AFTER `host`,
+ CHANGE `tags` `tags` varchar(255) NULL AFTER `tls`,
+ CHANGE `rate` `rate` varchar(11) NOT NULL AFTER `tags`,
+ CHANGE `network` `network` text NOT NULL AFTER `rate`,
+ CHANGE `rules` `rules` text NULL AFTER `network`,
+ CHANGE `networkSettings` `networkSettings` text NULL AFTER `rules`,
+ CHANGE `tlsSettings` `tlsSettings` text NULL AFTER `networkSettings`,
+ CHANGE `ruleSettings` `ruleSettings` text NULL AFTER `tlsSettings`,
+ CHANGE `dnsSettings` `dnsSettings` text NULL AFTER `ruleSettings`;
+
+ALTER TABLE `v2_server_v2ray`
+ ADD `route_id` varchar(255) COLLATE 'utf8mb4_general_ci' NULL AFTER `group_id`;
+
+
+CREATE TABLE `v2_server_route` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `remarks` varchar(255) NOT NULL,
+ `match` varchar(255) NOT NULL,
+ `action` varchar(11) NOT NULL,
+ `action_value` varchar(255) DEFAULT NULL,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+ALTER TABLE `v2_server_route`
+ CHANGE `match` `match` text COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `remarks`;
+
+ALTER TABLE `v2_order`
+ ADD UNIQUE `trade_no` (`trade_no`);
+
+ALTER TABLE `v2_plan`
+ CHANGE `content` `content` text COLLATE 'utf8mb4_general_ci' NULL AFTER `renew`;
+
+ALTER TABLE `v2_plan`
+ COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_server_v2ray`
+ RENAME TO `v2_server_vmess`;
+
+ALTER TABLE `v2_server_vmess`
+ CHANGE `network` `network` varchar(11) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `rate`;
+
+DROP TABLE IF EXISTS `v2_server_hysteria`;
+CREATE TABLE `v2_server_hysteria` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `group_id` varchar(255) NOT NULL,
+ `route_id` varchar(255) DEFAULT NULL,
+ `name` varchar(255) NOT NULL,
+ `parent_id` int(11) DEFAULT NULL,
+ `host` varchar(255) NOT NULL,
+ `port` varchar(11) NOT NULL,
+ `server_port` int(11) NOT NULL,
+ `tags` varchar(255) DEFAULT NULL,
+ `rate` varchar(11) NOT NULL,
+ `show` tinyint(1) NOT NULL DEFAULT '0',
+ `sort` int(11) DEFAULT NULL,
+ `up_mbps` int(11) NOT NULL,
+ `down_mbps` int(11) NOT NULL,
+ `server_name` varchar(64) DEFAULT NULL,
+ `insecure` tinyint(1) NOT NULL DEFAULT '0',
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+ALTER TABLE `v2_plan`
+ ADD `capacity_limit` int(11) NULL AFTER `reset_traffic_method`;
+
+ALTER TABLE `v2_stat_order`
+ CHANGE `record_at` `record_at` int(11) NOT NULL AFTER `id`,
+ CHANGE `record_type` `record_type` char(1) COLLATE 'utf8_general_ci' NOT NULL AFTER `record_at`,
+ CHANGE `order_count` `paid_count` int(11) NOT NULL COMMENT '订单数量' AFTER `record_type`,
+ CHANGE `order_amount` `paid_total` int(11) NOT NULL COMMENT '订单合计' AFTER `paid_count`,
+ CHANGE `commission_count` `commission_count` int(11) NOT NULL AFTER `paid_total`,
+ CHANGE `commission_amount` `commission_total` int(11) NOT NULL COMMENT '佣金合计' AFTER `commission_count`,
+ ADD `order_count` int(11) NOT NULL AFTER `record_type`,
+ ADD `order_total` int(11) NOT NULL AFTER `order_count`,
+ ADD `register_count` int(11) NOT NULL AFTER `order_total`,
+ ADD `invite_count` int(11) NOT NULL AFTER `register_count`,
+ ADD `transfer_used_total` varchar(32) NOT NULL AFTER `invite_count`,
+ RENAME TO `v2_stat`;
+
+CREATE TABLE `v2_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `level` varchar(11) DEFAULT NULL,
+ `host` varchar(255) DEFAULT NULL,
+ `uri` varchar(255) NOT NULL,
+ `method` varchar(11) NOT NULL,
+ `data` text,
+ `ip` varchar(128) DEFAULT NULL,
+ `context` text,
+ `created_at` int(11) NOT NULL,
+ `updated_at` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/init.sh b/init.sh
new file mode 100644
index 0000000..d7501c8
--- /dev/null
+++ b/init.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+rm -rf composer.phar
+wget https://github.com/composer/composer/releases/latest/download/composer.phar -O composer.phar
+php composer.phar install -vvv
+php artisan v2board:install
+
+#if [ -f "/etc/init.d/bt" ]; then
+# chown -R www $(pwd);
+#fi
diff --git a/library/AlipayF2F.php b/library/AlipayF2F.php
new file mode 100644
index 0000000..e3ea583
--- /dev/null
+++ b/library/AlipayF2F.php
@@ -0,0 +1,164 @@
+buildQuery($data);
+ $res = "-----BEGIN PUBLIC KEY-----\n" .
+ wordwrap($this->alipayPublicKey, 64, "\n", true) .
+ "\n-----END PUBLIC KEY-----";
+ if ("RSA2" == $this->signType) {
+ $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
+ } else {
+ $result = (openssl_verify($data, base64_decode($sign), $res) === 1);
+ }
+ openssl_free_key(openssl_get_publickey($res));
+ return $result;
+ }
+
+ public function setBizContent($bizContent = [])
+ {
+ $this->bizContent = json_encode($bizContent);
+ }
+
+ public function setMethod($method)
+ {
+ $this->method = $method;
+ }
+
+ public function setAppId($appId)
+ {
+ $this->appId = $appId;
+ }
+
+ public function setPrivateKey($privateKey)
+ {
+ $this->privateKey = $privateKey;
+ }
+
+ public function setAlipayPublicKey($alipayPublicKey)
+ {
+ $this->alipayPublicKey = $alipayPublicKey;
+ }
+
+ public function setNotifyUrl($url)
+ {
+ $this->notifyUrl = $url;
+ }
+
+ public function send()
+ {
+ $response = Http::get('https://openapi.alipay.com/gateway.do', $this->buildParam())->json();
+ $resKey = str_replace('.', '_', $this->method) . '_response';
+ if (!isset($response[$resKey])) throw new \Exception('从支付宝请求失败');
+ $response = $response[$resKey];
+ if ($response['msg'] !== 'Success') throw new \Exception($response['sub_msg']);
+ $this->response = $response;
+ }
+
+ public function getQrCodeUrl()
+ {
+ $response = $this->response;
+ if (!isset($response['qr_code'])) throw new \Exception('获取付款二维码失败');
+ return $response['qr_code'];
+ }
+
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ public function buildParam(): array
+ {
+ $params = [
+ 'app_id' => $this->appId,
+ 'method' => $this->method,
+ 'charset' => 'UTF-8',
+ 'sign_type' => $this->signType,
+ 'timestamp' => date('Y-m-d H:m:s'),
+ 'biz_content' => $this->bizContent,
+ 'version' => '1.0',
+ '_input_charset' => 'UTF-8'
+ ];
+ if ($this->notifyUrl) $params['notify_url'] = $this->notifyUrl;
+ ksort($params);
+ $params['sign'] = $this->buildSign($this->buildQuery($params));
+ return $params;
+ }
+
+ public function buildQuery($query)
+ {
+ if (!$query) {
+ throw new \Exception('参数构造错误');
+ }
+ //将要 参数 排序
+ ksort($query);
+
+ //重新组装参数
+ $params = array();
+ foreach ($query as $key => $value) {
+ $params[] = $key . '=' . $value;
+ }
+ $data = implode('&', $params);
+ return $data;
+ }
+
+ private function buildSign(string $signData): string
+ {
+ $privateKey = $this->privateKey;
+ $p_key = array();
+ //如果私钥是 1行
+ if (!stripos($privateKey, "\n")) {
+ $i = 0;
+ while ($key_str = substr($privateKey, $i * 64, 64)) {
+ $p_key[] = $key_str;
+ $i++;
+ }
+ }
+ $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . implode("\n", $p_key);
+ $privateKey = $privateKey . "\n-----END RSA PRIVATE KEY-----";
+
+ //私钥
+ $privateId = openssl_pkey_get_private($privateKey, '');
+
+ // 签名
+ $signature = '';
+
+ if ("RSA2" == $this->signType) {
+
+ openssl_sign($signData, $signature, $privateId, OPENSSL_ALGO_SHA256);
+ } else {
+
+ openssl_sign($signData, $signature, $privateId, OPENSSL_ALGO_SHA1);
+ }
+
+ openssl_free_key($privateId);
+
+ //加密后的内容通常含有特殊字符,需要编码转换下
+ $signature = base64_encode($signature);
+ return $signature;
+ }
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100755
index 0000000..61b6b64
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ ./tests/Unit
+
+
+
+ ./tests/Feature
+
+
+
+
+ ./app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pm2.yaml b/pm2.yaml
new file mode 100644
index 0000000..48c2425
--- /dev/null
+++ b/pm2.yaml
@@ -0,0 +1,5 @@
+apps:
+ - name : 'V2Board'
+ script : 'php artisan horizon'
+ instances: 1
+ out_file : './storage/logs/queue/queue.log'
diff --git a/public/assets/admin/components.async.js b/public/assets/admin/components.async.js
new file mode 100644
index 0000000..0c53b44
--- /dev/null
+++ b/public/assets/admin/components.async.js
@@ -0,0 +1 @@
+(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[0],{"+BJd":function(e,t,c){"use strict";c("cIOH"),c("6MrE")},"+Gva":function(e,t,c){"use strict";var n=c("N2Kk"),r=c("L9pr"),o={placeholder:"\u8bf7\u9009\u62e9\u65f6\u95f4"},a=o;function l(){return l=Object.assign||function(e){for(var t=1;t0?c:null}}]),n}(o["Component"]);return t.defaultProps={allowClear:!0,showToday:!0},Object(l["polyfill"])(t),t}var P=c("wgp+"),T=c("61s2"),j=c("YMnH"),N=c("5lmr"),R=c("RlXo");function _(e){"@babel/helpers - typeof";return _="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_(e)}function A(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function F(){return F=Object.assign||function(e){for(var t=1;t-1||e.indexOf("h")>-1||e.indexOf("k")>-1,showMinute:e.indexOf("m")>-1,showSecond:e.indexOf("s")>-1}}var J=function(e){U(c,e);var t=q(c);function c(e){var n;I(this,c),n=t.call(this,e),n.getDefaultLocale=function(){var e=F(F({},R["a"]),n.props.locale);return e},n.handleOpenClose=function(e){var t=e.open,c=n.props.onOpenChange;c&&c(t)},n.saveTimePicker=function(e){n.timePickerRef=e},n.handleChange=function(e){"value"in n.props||n.setState({value:e});var t=n.props,c=t.onChange,r=t.format,o=void 0===r?"HH:mm:ss":r;c&&c(e,e&&e.format(o)||"")},n.renderTimePicker=function(e){return o["createElement"](p["a"],null,function(t){var c=t.getPopupContainer,r=t.getPrefixCls,a=n.props,l=a.getPopupContainer,i=a.prefixCls,u=a.className,f=a.addon,p=a.placeholder,v=X(a,["getPopupContainer","prefixCls","className","addon","placeholder"]),m=v.size,d=Object(h["a"])(v,["defaultValue","suffixIcon","allowEmpty","allowClear"]),y=n.getDefaultFormat(),b=r("time-picker",i),z=s()(u,A({},"".concat(b,"-").concat(m),!!m)),g=function(e){return f?o["createElement"]("div",{className:"".concat(b,"-panel-addon")},f(e)):null};return o["createElement"](N["a"],F({},Z(y),d,{allowEmpty:n.getAllowClear(),prefixCls:b,getPopupContainer:l||c,ref:n.saveTimePicker,format:y,className:z,value:n.state.value,placeholder:void 0===p?e.placeholder:p,onChange:n.handleChange,onOpen:n.handleOpenClose,onClose:n.handleOpenClose,addon:g,inputIcon:n.renderInputIcon(b),clearIcon:n.renderClearIcon(b)}))})};var r=e.value||e.defaultValue;if(r&&!Object(m["a"])(a).isMoment(r))throw new Error("The value/defaultValue of TimePicker must be a moment object after `antd@2.0`, see: https://u.ant.design/time-picker-value");return n.state={value:r},Object(v["a"])(!("allowEmpty"in e),"TimePicker","`allowEmpty` is deprecated. Please use `allowClear` instead."),n}return K(c,[{key:"getDefaultFormat",value:function(){var e=this.props,t=e.format,c=e.use12Hours;return t||(c?"h:mm:ss a":"HH:mm:ss")}},{key:"getAllowClear",value:function(){var e=this.props,t=e.allowClear,c=e.allowEmpty;return"allowClear"in this.props?t:c}},{key:"focus",value:function(){this.timePickerRef.focus()}},{key:"blur",value:function(){this.timePickerRef.blur()}},{key:"renderInputIcon",value:function(e){var t=this.props.suffixIcon,c=t&&o["isValidElement"](t)&&o["cloneElement"](t,{className:s()(t.props.className,"".concat(e,"-clock-icon"))})||o["createElement"](f["a"],{type:"clock-circle",className:"".concat(e,"-clock-icon")});return o["createElement"]("span",{className:"".concat(e,"-icon")},c)}},{key:"renderClearIcon",value:function(e){var t=this.props.clearIcon,c="".concat(e,"-clear");return t&&o["isValidElement"](t)?o["cloneElement"](t,{className:s()(t.props.className,c)}):o["createElement"](f["a"],{type:"close-circle",className:c,theme:"filled"})}},{key:"render",value:function(){return o["createElement"](j["a"],{componentName:"TimePicker",defaultLocale:this.getDefaultLocale()},this.renderTimePicker)}}],[{key:"getDerivedStateFromProps",value:function(e){return"value"in e?{value:e.value}:null}}]),c}(o["Component"]);J.defaultProps={align:{offset:[0,-2]},disabledHours:void 0,disabledMinutes:void 0,disabledSeconds:void 0,hideDisabledOptions:!1,placement:"bottomLeft",transitionName:"slide-up",focusOnOpen:!0},Object(l["polyfill"])(J);function $(e){"@babel/helpers - typeof";return $="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},$(e)}function ee(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function te(){return te=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var c=0,n=new Array(t);c0&&(e[1]=void 0);var r=Re(e,2),o=r[0],a=r[1];"function"===typeof c.onChange&&c.onChange(e,[y(o,c.format),y(a,c.format)])},n.handleOpenChange=function(e){"open"in n.props||n.setState({open:e}),!1===e&&n.clearHoverValue();var t=n.props.onOpenChange;t&&t(e)},n.handleShowDateChange=function(e){return n.setState({showDate:e})},n.handleHoverChange=function(e){return n.setState({hoverValue:e})},n.handleRangeMouseLeave=function(){n.state.open&&n.clearHoverValue()},n.handleCalendarInputSelect=function(e){var t=Re(e,1),c=t[0];c&&n.setState(function(t){var c=t.showDate;return{value:e,showDate:Ke(e)||c}})},n.handleRangeClick=function(e){"function"===typeof e&&(e=e()),n.setValue(e,!0);var t=n.props,c=t.onOk,r=t.onOpenChange;c&&c(e),r&&r(!1)},n.renderFooter=function(){var e=n.props,t=e.ranges,c=e.renderExtraFooter,r=Te(n),a=r.prefixCls,l=r.tagPrefixCls;if(!t&&!c)return null;var i=c?o["createElement"]("div",{className:"".concat(a,"-footer-extra"),key:"extra"},c()):null,u=t&&Object.keys(t).map(function(e){var c=t[e],r="function"===typeof c?c.call(Te(n)):c;return o["createElement"](ge["a"],{key:e,prefixCls:l,color:"blue",onClick:function(){return n.handleRangeClick(c)},onMouseEnter:function(){return n.setState({hoverValue:r})},onMouseLeave:n.handleRangeMouseLeave},e)}),s=u&&u.length>0?o["createElement"]("div",{className:"".concat(a,"-footer-extra ").concat(a,"-range-quick-selector"),key:"range"},u):null;return[s,i]},n.renderRangePicker=function(e){var t,c=e.getPrefixCls,r=Te(n),a=r.state,l=r.props,u=a.value,h=a.showDate,p=a.hoverValue,m=a.open,d=l.prefixCls,b=l.tagPrefixCls,z=l.popupStyle,g=l.style,M=l.disabledDate,C=l.disabledTime,H=l.showTime,O=l.showToday,V=l.ranges,w=l.onOk,S=l.locale,L=l.localeCode,k=l.format,x=l.dateRender,E=l.onCalendarChange,P=l.suffixIcon,T=l.separator,j=c("calendar",d),N=c("tag",b);n.prefixCls=j,n.tagPrefixCls=N,qe(u,L),qe(h,L),Object(v["a"])(!("onOK"in l),"RangePicker","It should be `RangePicker[onOk]`, instead of `onOK`!");var R=s()((t={},Ve(t,"".concat(j,"-time"),H),Ve(t,"".concat(j,"-range-with-ranges"),V),t)),_={onChange:n.handleChange},A={onOk:n.handleChange};l.timePicker?_.onChange=function(e){return n.handleChange(e)}:A={},"mode"in l&&(A.mode=l.mode);var F=Array.isArray(l.placeholder)?l.placeholder[0]:S.lang.rangePlaceholder[0],I=Array.isArray(l.placeholder)?l.placeholder[1]:S.lang.rangePlaceholder[1],D=o["createElement"](ye["a"],Oe({},A,{seperator:T,onChange:E,format:k,prefixCls:j,className:R,renderFooter:n.renderFooter,timePicker:l.timePicker,disabledDate:M,disabledTime:C,dateInputPlaceholder:[F,I],locale:S.lang,onOk:w,dateRender:x,value:h,onValueChange:n.handleShowDateChange,hoverValue:p,onHoverChange:n.handleHoverChange,onPanelChange:l.onPanelChange,showToday:O,onInputSelect:n.handleCalendarInputSelect})),K={};l.showTime&&(K.width=g&&g.width||350);var U=Re(u,2),B=U[0],q=U[1],W=!l.disabled&&l.allowClear&&u&&(B||q)?o["createElement"](f["a"],{type:"close-circle",className:"".concat(j,"-picker-clear"),onClick:n.clearSelection,theme:"filled"}):null,G=o["createElement"](Ce,{suffixIcon:P,prefixCls:j}),Y=function(e){var t=e.value,c=Re(t,2),n=c[0],r=c[1];return o["createElement"]("span",{className:l.pickerInputClass},o["createElement"]("input",{disabled:l.disabled,readOnly:!0,value:y(n,l.format),placeholder:F,className:"".concat(j,"-range-picker-input"),tabIndex:-1}),o["createElement"]("span",{className:"".concat(j,"-range-picker-separator")}," ",T," "),o["createElement"]("input",{disabled:l.disabled,readOnly:!0,value:y(r,l.format),placeholder:I,className:"".concat(j,"-range-picker-input"),tabIndex:-1}),W,G)};return o["createElement"]("span",{ref:n.savePicker,id:"number"===typeof l.id?l.id.toString():l.id,className:s()(l.className,l.pickerClass),style:Oe(Oe({},g),K),tabIndex:l.disabled?-1:0,onFocus:l.onFocus,onBlur:l.onBlur,onMouseEnter:l.onMouseEnter,onMouseLeave:l.onMouseLeave},o["createElement"](i["a"],Oe({},l,_,{calendar:D,value:u,open:m,onOpenChange:n.handleOpenChange,prefixCls:"".concat(j,"-picker-container"),style:z}),Y))};var r=e.value||e.defaultValue||[],l=Re(r,2),u=l[0],h=l[1];if(u&&!Object(m["a"])(a).isMoment(u)||h&&!Object(m["a"])(a).isMoment(h))throw new Error("The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, see: https://u.ant.design/date-picker-value");var p=!r||Be(r)?e.defaultPickerValue:r;return n.state={value:r,showDate:Ue(p||Object(m["a"])(a)()),open:e.open,hoverValue:[]},n}return Le(c,[{key:"componentDidUpdate",value:function(e,t){"open"in this.props||!t.open||this.state.open||this.focus()}},{key:"setValue",value:function(e,t){this.handleChange(e),!t&&this.props.showTime||"open"in this.props||this.setState({open:!1})}},{key:"focus",value:function(){this.picker.focus()}},{key:"blur",value:function(){this.picker.blur()}},{key:"render",value:function(){return o["createElement"](p["a"],null,this.renderRangePicker)}}],[{key:"getDerivedStateFromProps",value:function(e,t){var c=null;if("value"in e){var n=e.value||[];c={value:n},ze()(e.value,t.value)||(c=Oe(Oe({},c),{showDate:Ke(n,e.mode)||t.showDate}))}return"open"in e&&t.open!==e.open&&(c=Oe(Oe({},c),{open:e.open})),c}}]),c}(o["Component"]);We.defaultProps={allowClear:!0,showToday:!1,separator:"~"},Object(l["polyfill"])(We);var Ge=We;function Ye(e){"@babel/helpers - typeof";return Ye="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ye(e)}function Qe(){return Qe=Object.assign||function(e){for(var t=1;t0?{paddingLeft:t[0]/2,paddingRight:t[0]/2}:{}),t[1]>0?{paddingTop:t[1]/2,paddingBottom:t[1]/2}:{}),c)),n["createElement"]("div",s({},M,{style:c,className:V}),g)})},e}return v(c,[{key:"render",value:function(){return n["createElement"](i["a"],null,this.renderCol)}}]),c}(n["Component"]);O.propTypes={span:r["number"],order:r["number"],offset:r["number"],push:r["number"],pull:r["number"],className:r["string"],children:r["node"],xs:H,sm:H,md:H,lg:H,xl:H,xxl:H}},"/wGt":function(e,t,c){"use strict";var n=c("q1tI"),r=c("fcSX"),o=c("foW8"),a=c.n(o),l=c("TSYQ"),i=c.n(l),u=c("BGR+"),s=c("6CfX"),h=c("CtXQ"),f=c("H84U"),p=c("CWQg");function v(e){"@babel/helpers - typeof";return v="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},v(e)}function m(){return m=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},t=e.arrowWidth,c=void 0===t?5:t,n=e.horizontalArrowShift,r=void 0===n?16:n,o=e.verticalArrowShift,a=void 0===o?12:o,l=e.autoAdjustOverflow,s=void 0===l||l,h={left:{points:["cr","cl"],offset:[-4,0]},right:{points:["cl","cr"],offset:[4,0]},top:{points:["bc","tc"],offset:[0,-4]},bottom:{points:["tc","bc"],offset:[0,4]},topLeft:{points:["bl","tc"],offset:[-(r+c),-4]},leftTop:{points:["tr","cl"],offset:[-4,-(a+c)]},topRight:{points:["br","tc"],offset:[r+c,-4]},rightTop:{points:["tl","cr"],offset:[4,-(a+c)]},bottomRight:{points:["tr","bc"],offset:[r+c,4]},rightBottom:{points:["bl","cr"],offset:[4,a+c]},bottomLeft:{points:["tl","bc"],offset:[-(r+c),4]},leftBottom:{points:["br","cl"],offset:[-4,a+c]}};return Object.keys(h).forEach(function(t){h[t]=e.arrowPointAtCenter?u(u({},h[t]),{overflow:p(s),targetOffset:f}):u(u({},i["a"][t]),{overflow:p(s)}),h[t].ignoreShake=!0}),h}var m=c("H84U");function d(e){"@babel/helpers - typeof";return d="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},d(e)}function y(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function b(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function z(e,t){for(var c=0;c=0||n.indexOf("Bottom")>=0?a.top="".concat(o.height-t.offset[1],"px"):(n.indexOf("Top")>=0||n.indexOf("bottom")>=0)&&(a.top="".concat(-t.offset[1],"px")),n.indexOf("left")>=0||n.indexOf("Right")>=0?a.left="".concat(o.width-t.offset[0],"px"):(n.indexOf("right")>=0||n.indexOf("Left")>=0)&&(a.left="".concat(-t.offset[0],"px")),e.style.transformOrigin="".concat(a.left," ").concat(a.top)}},r.renderTooltip=function(e){var t=e.getPopupContainer,c=e.getPrefixCls,a=V(r),i=a.props,u=a.state,s=i.prefixCls,h=i.openClassName,f=i.getPopupContainer,p=i.getTooltipContainer,v=i.children,m=c("tooltip",s),d=u.visible;"visible"in i||!r.isNoTitle()||(d=!1);var b=x(n["isValidElement"](v)?v:n["createElement"]("span",null,v)),z=b.props,g=l()(z.className,y({},h||"".concat(m,"-open"),!0));return n["createElement"](o["a"],L({},r.props,{prefixCls:m,getTooltipContainer:f||p||t,ref:r.saveTooltip,builtinPlacements:r.getPlacements(),overlay:r.getOverlay(),visible:d,onVisibleChange:r.onVisibleChange,onPopupAlign:r.onPopupAlign}),d?n["cloneElement"](b,{className:g}):b)},r.state={visible:!!e.visible||!!e.defaultVisible},r}return g(c,[{key:"getPopupDomNode",value:function(){return this.tooltip.getPopupDomNode()}},{key:"getPlacements",value:function(){var e=this.props,t=e.builtinPlacements,c=e.arrowPointAtCenter,n=e.autoAdjustOverflow;return t||v({arrowPointAtCenter:c,verticalArrowShift:8,autoAdjustOverflow:n})}},{key:"isNoTitle",value:function(){var e=this.props,t=e.title,c=e.overlay;return!t&&!c&&0!==t}},{key:"getOverlay",value:function(){var e=this.props,t=e.title,c=e.overlay;return 0===t?t:c||t||""}},{key:"render",value:function(){return n["createElement"](m["a"],null,this.renderTooltip)}}],[{key:"getDerivedStateFromProps",value:function(e){return"visible"in e?{visible:e.visible}:null}}]),c}(n["Component"]);E.defaultProps={placement:"top",transitionName:"zoom-big-fast",mouseEnterDelay:.1,mouseLeaveDelay:.1,arrowPointAtCenter:!1,autoAdjustOverflow:!0},Object(r["polyfill"])(E);t["a"]=E},"3wW7":function(e,t,c){},"5Dmo":function(e,t,c){"use strict";c("cIOH"),c("5YgA")},"5NDa":function(e,t,c){"use strict";c("cIOH"),c("OnYD"),c("+L6B")},"5YgA":function(e,t,c){},"5rEg":function(e,t,c){"use strict";var n=c("q1tI"),r=c("17x9"),o=c("VCL8"),a=c("TSYQ"),l=c.n(a),i=c("BGR+"),u=c("CWQg"),s=c("CtXQ");function h(e){"@babel/helpers - typeof";return h="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h(e)}function f(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function p(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function v(e,t){for(var c=0;c1&&void 0!==arguments[1]&&arguments[1],c=e.getAttribute("id")||e.getAttribute("data-reactid")||e.getAttribute("name");if(t&&ze[c])return ze[c];var n=window.getComputedStyle(e),r=n.getPropertyValue("box-sizing")||n.getPropertyValue("-moz-box-sizing")||n.getPropertyValue("-webkit-box-sizing"),o=parseFloat(n.getPropertyValue("padding-bottom"))+parseFloat(n.getPropertyValue("padding-top")),a=parseFloat(n.getPropertyValue("border-bottom-width"))+parseFloat(n.getPropertyValue("border-top-width")),l=be.map(function(e){return"".concat(e,":").concat(n.getPropertyValue(e))}).join(";"),i={sizingStyle:l,paddingSize:o,borderSize:a,boxSizing:r};return t&&c&&(ze[c]=i),i}function Me(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;ve||(ve=document.createElement("textarea"),document.body.appendChild(ve)),e.getAttribute("wrap")?ve.setAttribute("wrap",e.getAttribute("wrap")):ve.removeAttribute("wrap");var r=ge(e,t),o=r.paddingSize,a=r.borderSize,l=r.boxSizing,i=r.sizingStyle;ve.setAttribute("style","".concat(i,";").concat(ye)),ve.value=e.value||e.placeholder||"";var u,s=Number.MIN_SAFE_INTEGER,h=Number.MAX_SAFE_INTEGER,f=ve.scrollHeight;if("border-box"===l?f+=a:"content-box"===l&&(f-=o),null!==c||null!==n){ve.value=" ";var p=ve.scrollHeight-o;null!==c&&(s=p*c,"border-box"===l&&(s=s+o+a),f=Math.max(s,f)),null!==n&&(h=p*n,"border-box"===l&&(h=h+o+a),u=f>h?"":"hidden",f=Math.min(h,f))}return{height:f,minHeight:s,maxHeight:h,overflowY:u}}var Ce=c("oHiP");function He(e){"@babel/helpers - typeof";return He="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},He(e)}function Oe(){return Oe=Object.assign||function(e){for(var t=1;t0&&(m=s.map(function(e){return"string"===typeof e?n["createElement"](V,{key:e,prefixCls:f,disabled:r.props.disabled,value:e,checked:r.state.value===e},e):n["createElement"](V,{key:"radio-group-value-options-".concat(e.value),prefixCls:f,disabled:e.disabled||r.props.disabled,value:e.value,checked:r.state.value===e.value},e.label)})),n["createElement"]("div",{className:v,style:o.style,onMouseEnter:o.onMouseEnter,onMouseLeave:o.onMouseLeave,id:o.id},m)},"value"in e)o=e.value;else if("defaultValue"in e)o=e.defaultValue;else{var a=F(e.children);o=a&&a.value}return r.state={value:o},r}return E(c,[{key:"getChildContext",value:function(){return{radioGroup:{onChange:this.onRadioChange,value:this.state.value,disabled:this.props.disabled,name:this.props.name}}}},{key:"shouldComponentUpdate",value:function(e,t){return!u()(this.props,e)||!u()(this.state,t)}},{key:"render",value:function(){return n["createElement"](s["a"],null,this.renderGroup)}}],[{key:"getDerivedStateFromProps",value:function(e){if("value"in e)return{value:e.value};var t=F(e.children);return t?{value:t.value}:null}}]),c}(n["Component"]);I.defaultProps={buttonStyle:"outline"},I.childContextTypes={radioGroup:r["any"]},Object(w["polyfill"])(I);var D=I;function K(e){"@babel/helpers - typeof";return K="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},K(e)}function U(){return U=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var c=0,n=new Array(t);c0));return n["createElement"](X.Provider,{value:{siderHook:this.getSiderHook()}},n["createElement"](l,A({className:u},i),r))}}]),c}(n["Component"]),ee=Z({suffixCls:"layout",tagName:"section",displayName:"Layout"})($),te=Z({suffixCls:"layout-header",tagName:"header",displayName:"Header"})(J),ce=Z({suffixCls:"layout-footer",tagName:"footer",displayName:"Footer"})(J),ne=Z({suffixCls:"layout-content",tagName:"main",displayName:"Content"})(J);ee.Header=te,ee.Footer=ce,ee.Content=ne;var re=c("CtXQ"),oe=function(e){return!isNaN(parseFloat(e))&&isFinite(e)},ae=oe;function le(e){"@babel/helpers - typeof";return le="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},le(e)}function ie(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function ue(){return ue=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:"";return e+=1,"".concat(t).concat(e)}}(),Ve=function(e){pe(c,e);var t=me(c);function c(e){var r,o,i;return se(this,c),r=t.call(this,e),r.responsiveHandler=function(e){r.setState({below:e.matches});var t=r.props.onBreakpoint;t&&t(e.matches),r.state.collapsed!==e.matches&&r.setCollapsed(e.matches,"responsive")},r.setCollapsed=function(e,t){"collapsed"in r.props||r.setState({collapsed:e});var c=r.props.onCollapse;c&&c(e,t)},r.toggle=function(){var e=!r.state.collapsed;r.setCollapsed(e,"clickTrigger")},r.belowShowChange=function(){r.setState(function(e){var t=e.belowShow;return{belowShow:!t}})},r.renderSider=function(e){var t,c=e.getPrefixCls,o=r.props,i=o.prefixCls,u=o.className,s=o.theme,h=o.collapsible,f=o.reverseArrow,p=o.trigger,v=o.style,m=o.width,d=o.collapsedWidth,y=o.zeroWidthTriggerStyle,b=ge(o,["prefixCls","className","theme","collapsible","reverseArrow","trigger","style","width","collapsedWidth","zeroWidthTriggerStyle"]),z=c("layout-sider",i),g=Object(l["a"])(b,["collapsed","defaultCollapsed","onCollapse","breakpoint","onBreakpoint","siderHook","zeroWidthTriggerStyle"]),M=r.state.collapsed?d:m,C=ae(M)?"".concat(M,"px"):String(M),H=0===parseFloat(String(d||0))?n["createElement"]("span",{onClick:r.toggle,className:"".concat(z,"-zero-width-trigger ").concat(z,"-zero-width-trigger-").concat(f?"right":"left"),style:y},n["createElement"](re["a"],{type:"bars"})):null,O={expanded:f?n["createElement"](re["a"],{type:"right"}):n["createElement"](re["a"],{type:"left"}),collapsed:f?n["createElement"](re["a"],{type:"left"}):n["createElement"](re["a"],{type:"right"})},V=r.state.collapsed?"collapsed":"expanded",w=O[V],S=null!==p?H||n["createElement"]("div",{className:"".concat(z,"-trigger"),onClick:r.toggle,style:{width:C}},p||w):null,L=ue(ue({},v),{flex:"0 0 ".concat(C),maxWidth:C,minWidth:C,width:C}),k=a()(u,z,"".concat(z,"-").concat(s),(t={},ie(t,"".concat(z,"-collapsed"),!!r.state.collapsed),ie(t,"".concat(z,"-has-trigger"),h&&null!==p&&!H),ie(t,"".concat(z,"-below"),!!r.state.below),ie(t,"".concat(z,"-zero-width"),0===parseFloat(C)),t));return n["createElement"]("aside",ue({className:k},g,{style:L}),n["createElement"]("div",{className:"".concat(z,"-children")},r.props.children),h||r.state.below&&H?S:null)},r.uniqueId=Oe("ant-sider-"),"undefined"!==typeof window&&(o=window.matchMedia),o&&e.breakpoint&&e.breakpoint in Ce&&(r.mql=o("(max-width: ".concat(Ce[e.breakpoint],")"))),i="collapsed"in e?e.collapsed:e.defaultCollapsed,r.state={collapsed:i,below:!1},r}return fe(c,[{key:"componentDidMount",value:function(){this.mql&&(this.mql.addListener(this.responsiveHandler),this.responsiveHandler(this.mql)),this.props.siderHook&&this.props.siderHook.addSider(this.uniqueId)}},{key:"componentWillUnmount",value:function(){this.mql&&this.mql.removeListener(this.responsiveHandler),this.props.siderHook&&this.props.siderHook.removeSider(this.uniqueId)}},{key:"render",value:function(){var e=this.state.collapsed,t=this.props.collapsedWidth;return n["createElement"](He.Provider,{value:{siderCollapsed:e,collapsedWidth:t}},n["createElement"](k["a"],null,this.renderSider))}}],[{key:"getDerivedStateFromProps",value:function(e){return"collapsed"in e?{collapsed:e.collapsed}:null}}]),c}(n["Component"]);Ve.defaultProps={collapsible:!1,defaultCollapsed:!1,reverseArrow:!1,width:200,collapsedWidth:80,style:{},theme:"dark"},Object(i["polyfill"])(Ve);n["Component"];function we(e){"@babel/helpers - typeof";return we="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},we(e)}function Se(){return Se=Object.assign||function(e){for(var t=1;t=0;(t||r)&&o.restoreModeVerticalFromInline()},o.handleClick=function(e){o.handleOpenChange([]);var t=o.props.onClick;t&&t(e)},o.handleOpenChange=function(e){o.setOpenKeys(e);var t=o.props.onOpenChange;t&&t(e)},o.renderMenu=function(e){var t=e.getPopupContainer,c=e.getPrefixCls,i=o.props,u=i.prefixCls,s=i.className,h=i.theme,f=i.collapsedWidth,p=Object(l["a"])(o.props,["collapsedWidth","siderCollapsed"]),v=o.getRealMenuMode(),m=o.getOpenMotionProps(v),d=c("menu",u),y=a()(s,"".concat(d,"-").concat(h),Qe({},"".concat(d,"-inline-collapsed"),o.getInlineCollapsed())),b=Ye({openKeys:o.state.openKeys,onOpenChange:o.handleOpenChange,className:y,mode:v},m);"inline"!==v&&(b.onClick=o.handleClick);var z=o.getInlineCollapsed()&&(0===f||"0"===f||"0px"===f);return z&&(b.openKeys=[]),n["createElement"](r["e"],Ye({getPopupContainer:t},p,b,{prefixCls:d,onTransitionEnd:o.handleTransitionEnd,onMouseEnter:o.handleMouseEnter}))},Object(Ie["a"])(!("onOpen"in e||"onClose"in e),"Menu","`onOpen` and `onClose` are removed, please use `onOpenChange` instead, see: https://u.ant.design/menu-on-open-change."),Object(Ie["a"])(!("inlineCollapsed"in e&&"inline"!==e.mode),"Menu","`inlineCollapsed` should only be used when `mode` is inline."),Object(Ie["a"])(!(void 0!==e.siderCollapsed&&"inlineCollapsed"in e),"Menu","`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead."),"openKeys"in e?i=e.openKeys:"defaultOpenKeys"in e&&(i=e.defaultOpenKeys),o.state={openKeys:i||[],switchingModeFromInline:!1,inlineOpenKeys:[],prevProps:e},o}return Je(c,[{key:"componentWillUnmount",value:function(){De["a"].cancel(this.mountRafId)}},{key:"setOpenKeys",value:function(e){"openKeys"in this.props||this.setState({openKeys:e})}},{key:"getRealMenuMode",value:function(){var e=this.getInlineCollapsed();if(this.state.switchingModeFromInline&&e)return"inline";var t=this.props.mode;return e?"vertical":t}},{key:"getInlineCollapsed",value:function(){var e=this.props.inlineCollapsed;return void 0!==this.props.siderCollapsed?this.props.siderCollapsed:e}},{key:"getOpenMotionProps",value:function(e){var t=this.props,c=t.openTransitionName,n=t.openAnimation,r=t.motion;return r?{motion:r}:n?(Object(Ie["a"])("string"===typeof n,"Menu","`openAnimation` do not support object. Please use `motion` instead."),{openAnimation:n}):c?{openTransitionName:c}:"horizontal"===e?{motion:{motionName:"slide-up"}}:"inline"===e?{motion:We}:{motion:{motionName:this.state.switchingModeFromInline?"":"zoom-big"}}}},{key:"restoreModeVerticalFromInline",value:function(){var e=this.state.switchingModeFromInline;e&&this.setState({switchingModeFromInline:!1})}},{key:"render",value:function(){return n["createElement"](p.Provider,{value:{inlineCollapsed:this.getInlineCollapsed()||!1,antdMenuTheme:this.props.theme}},n["createElement"](k["a"],null,this.renderMenu))}}],[{key:"getDerivedStateFromProps",value:function(e,t){var c=t.prevProps,n={prevProps:e};return"inline"===c.mode&&"inline"!==e.mode&&(n.switchingModeFromInline=!0),"openKeys"in e?n.openKeys=e.openKeys:((e.inlineCollapsed&&!c.inlineCollapsed||e.siderCollapsed&&!c.siderCollapsed)&&(n.switchingModeFromInline=!0,n.inlineOpenKeys=t.openKeys,n.openKeys=[]),(!e.inlineCollapsed&&c.inlineCollapsed||!e.siderCollapsed&&c.siderCollapsed)&&(n.openKeys=t.inlineOpenKeys,n.inlineOpenKeys=[])),n}}]),c}(n["Component"]);at.defaultProps={className:"",theme:"light",focusable:!1},Object(i["polyfill"])(at);var lt=function(e){$e(c,e);var t=tt(c);function c(){return Xe(this,c),t.apply(this,arguments)}return Je(c,[{key:"render",value:function(){var e=this;return n["createElement"](He.Consumer,null,function(t){return n["createElement"](at,Ye({},e.props,t))})}}]),c}(n["Component"]);lt.Divider=r["a"],lt.Item=Fe,lt.SubMenu=S,lt.ItemGroup=r["c"]},"CWI+":function(e,t,c){},CWQg:function(e,t,c){"use strict";c.d(t,"a",function(){return n});var n=function(){for(var e=arguments.length,t=new Array(e),c=0;c1&&void 0!==arguments[1]?arguments[1]:C;if(e){var c=this.definitions.get(e);return c&&"function"===typeof c.icon&&(c=s()({},c,{icon:c.icon(t.primaryColor,t.secondaryColor)})),c}}},{key:"setTwoToneColors",value:function(e){var t=e.primaryColor,c=e.secondaryColor;C.primaryColor=t,C.secondaryColor=c||Object(M["c"])(t)}},{key:"getTwoToneColors",value:function(){return s()({},C)}}]),t}(n["Component"]);H.displayName="IconReact",H.definitions=new M["a"];var O=H;function V(){return V=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},t=e.scriptUrl,c=e.extraCommonProps,r=void 0===c?{}:c;if("undefined"!==typeof document&&"undefined"!==typeof window&&"function"===typeof document.createElement&&"string"===typeof t&&t.length&&!S.has(t)){var o=document.createElement("script");o.setAttribute("src",t),o.setAttribute("data-namespace",t),S.add(t),document.body.appendChild(o)}var a=function(e){var t=e.type,c=e.children,o=w(e,["type","children"]),a=null;return e.type&&(a=n["createElement"]("use",{xlinkHref:"#".concat(t)})),c&&(a=c),n["createElement"]($,V({},r,o),a)};return a.displayName="Iconfont",a}var k=c("6CfX"),x={width:"1em",height:"1em",fill:"currentColor","aria-hidden":!0,focusable:"false"},E=/-fill$/,P=/-o$/,T=/-twotone$/;function j(e){var t=null;return E.test(e)?t="filled":P.test(e)?t="outlined":T.test(e)&&(t="twoTone"),t}function N(e){return e.replace(E,"").replace(P,"").replace(T,"")}function R(e,t){var c=e;return"filled"===t?c+="-fill":"outlined"===t?c+="-o":"twoTone"===t?c+="-twotone":Object(k["a"])(!1,"Icon","This icon '".concat(e,"' has unknown theme '").concat(t,"'")),c}function _(e){var t=e;switch(e){case"cross":t="close";break;case"interation":t="interaction";break;case"canlendar":t="calendar";break;case"colum-height":t="column-height";break;default:}return Object(k["a"])(t===e,"Icon","Icon '".concat(e,"' was a typo and is now deprecated, please use '").concat(t,"' instead.")),t}var A=c("YMnH");function F(e){return O.setTwoToneColors({primaryColor:e})}function I(){var e=O.getTwoToneColors();return e.primaryColor}function D(){return D=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var c=0,n=new Array(t);cr?o>=a?10+e:20+e:o<=a?10+e:e}},{key:"renderCurrentNumber",value:function(e,t,c){if("number"===typeof t){var r=this.getPositionByNum(t,c),o=this.state.animateStarted||void 0===H(this.lastCount)[c];return n["createElement"]("span",{className:"".concat(e,"-only"),style:{transition:o?"none":void 0,msTransform:"translateY(".concat(100*-r,"%)"),WebkitTransform:"translateY(".concat(100*-r,"%)"),transform:"translateY(".concat(100*-r,"%)")},key:c},O(r,"".concat(e,"-only-unit")))}return n["createElement"]("span",{key:"symbol",className:"".concat(e,"-symbol")},t)}},{key:"renderNumberElement",value:function(e){var t=this,c=this.state.count;return c&&Number(c)%1===0?H(c).map(function(c,n){return t.renderCurrentNumber(e,c,n)}).reverse():c}},{key:"render",value:function(){return n["createElement"](s["a"],null,this.renderScrollNumber)}},{key:"clearTimeout",value:function(e){function t(){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}(function(){this.timeout&&(clearTimeout(this.timeout),this.timeout=void 0)})}],[{key:"getDerivedStateFromProps",value:function(e,t){return"count"in e?t.count===e.count?null:{animateStarted:!0}:null}}]),c}(n["Component"]);V.defaultProps={count:null,onAnimated:function(){}},Object(u["polyfill"])(V);var w=V,S=c("09Wf");function L(e){"@babel/helpers - typeof";return L="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},L(e)}function k(){return k=Object.assign||function(e){for(var t=1;tc?"".concat(c,"+"):t;return n}},{key:"getDispayCount",value:function(){var e=this.isDot();return e?"":this.getNumberedDispayCount()}},{key:"getScrollNumberTitle",value:function(){var e=this.props,t=e.title,c=e.count;return t||("string"===typeof c||"number"===typeof c?c:void 0)}},{key:"getStyleWithOffset",value:function(){var e=this.props,t=e.offset,c=e.style;return t?k({right:-parseInt(t[0],10),marginTop:t[1]},c):c}},{key:"getBadgeClassName",value:function(e){var t,c=this.props,n=c.className,r=c.children;return i()(n,e,(t={},x(t,"".concat(e,"-status"),this.hasStatus()),x(t,"".concat(e,"-not-a-wrapper"),!r),t))}},{key:"hasStatus",value:function(){var e=this.props,t=e.status,c=e.color;return!!t||!!c}},{key:"isZero",value:function(){var e=this.getNumberedDispayCount();return"0"===e||0===e}},{key:"isDot",value:function(){var e=this.props.dot,t=this.isZero();return e&&!t||this.hasStatus()}},{key:"isHidden",value:function(){var e=this.props.showZero,t=this.getDispayCount(),c=this.isZero(),n=this.isDot(),r=null===t||void 0===t||""===t;return(r||c&&!e)&&!n}},{key:"renderStatusText",value:function(e){var t=this.props.text,c=this.isHidden();return c||!t?null:n["createElement"]("span",{className:"".concat(e,"-status-text")},t)}},{key:"renderDispayComponent",value:function(){var e=this.props.count,t=e;if(t&&"object"===L(t))return n["cloneElement"](t,{style:k(k({},this.getStyleWithOffset()),t.props&&t.props.style)})}},{key:"renderBadgeNumber",value:function(e,t){var c,r=this.props,o=r.status,a=r.count,l=r.color,u=this.getDispayCount(),s=this.isDot(),h=this.isHidden(),f=i()((c={},x(c,"".concat(e,"-dot"),s),x(c,"".concat(e,"-count"),!s),x(c,"".concat(e,"-multiple-words"),!s&&a&&a.toString&&a.toString().length>1),x(c,"".concat(e,"-status-").concat(o),!!o),x(c,"".concat(e,"-status-").concat(l),K(l)),c)),p=this.getStyleWithOffset();return l&&!K(l)&&(p=p||{},p.background=l),h?null:n["createElement"](w,{prefixCls:t,"data-show":!h,className:f,count:u,displayComponent:this.renderDispayComponent(),title:this.getScrollNumberTitle(),style:p,key:"scrollNumber"})}},{key:"render",value:function(){return n["createElement"](s["a"],null,this.renderBadge)}}]),c}(n["Component"]);U.defaultProps={count:null,showZero:!1,dot:!1,overflowCount:99},U.propTypes={count:r["node"],showZero:r["bool"],dot:r["bool"],overflowCount:r["number"]}},Mwp2:function(e,t,c){"use strict";c("cIOH"),c("3wW7"),c("R9oj"),c("T2oS"),c("DjyN"),c("1GLa")},NUBc:function(e,t,c){"use strict";var n=c("q1tI"),r=c("6+eU"),o=c("H4fg"),a=c("TSYQ"),l=c.n(a),i=c("2fM7");function u(e){"@babel/helpers - typeof";return u="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u(e)}function s(){return s=Object.assign||function(e){for(var t=1;t0?"-".concat(p):p,g=o()(v,b,"".concat(b,"-").concat(h),(c={},i(c,"".concat(b,"-with-text").concat(z),m),i(c,"".concat(b,"-dashed"),!!d),c));return n["createElement"]("div",l({className:g},y,{role:"separator"}),m&&n["createElement"]("span",{className:"".concat(b,"-inner-text")},m))})};t["a"]=s},PQMj:function(e,t,c){},Pwec:function(e,t,c){"use strict";c("cIOH"),c("WtSK")},R9oj:function(e,t,c){"use strict";c("cIOH"),c("pwpV")},RlXo:function(e,t,c){"use strict";var n={placeholder:"Select time"};t["a"]=n},Sdc0:function(e,t,c){"use strict";c.d(t,"a",function(){return S});var n=c("q1tI"),r=c("17x9"),o=c("XIdC"),a=c.n(o),l=c("TSYQ"),i=c.n(l),u=c("BGR+"),s=c("g0mS"),h=c("CtXQ"),f=c("H84U"),p=c("6CfX");function v(e){"@babel/helpers - typeof";return v="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},v(e)}function m(){return m=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:h,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:f;switch(e){case"topLeft":t={left:0,top:c,bottom:"auto"};break;case"topRight":t={right:0,top:c,bottom:"auto"};break;case"bottomLeft":t={left:0,top:"auto",bottom:n};break;default:t={right:0,top:"auto",bottom:n};break}return t}function d(e,t){var c=e.prefixCls,a=e.placement,s=void 0===a?p:a,h=e.getContainer,f=void 0===h?l:h,v=e.top,d=e.bottom,y=e.closeIcon,b=void 0===y?i:y,z="".concat(c,"-").concat(s);if(u[z])t(u[z]);else{var g=n["createElement"]("span",{className:"".concat(c,"-close-x")},b||n["createElement"](o["a"],{className:"".concat(c,"-close-icon"),type:"close"}));r["a"].newInstance({prefixCls:c,className:"".concat(c,"-").concat(s),style:m(s,v,d),getContainer:f,closeIcon:g},function(e){u[z]=e,t(e)})}}var y={success:"check-circle-o",info:"info-circle-o",error:"close-circle-o",warning:"exclamation-circle-o"};function b(e){var t=e.prefixCls||"ant-notification",c="".concat(t,"-notice"),r=void 0===e.duration?s:e.duration,a=null;if(e.icon)a=n["createElement"]("span",{className:"".concat(c,"-icon")},e.icon);else if(e.type){var l=y[e.type];a=n["createElement"](o["a"],{className:"".concat(c,"-icon ").concat(c,"-icon-").concat(e.type),type:l})}var i=!e.description&&a?n["createElement"]("span",{className:"".concat(c,"-message-single-line-auto-margin")}):null,u=e.placement,h=e.top,f=e.bottom,p=e.getContainer,v=e.closeIcon;d({prefixCls:t,placement:u,top:h,bottom:f,getContainer:p,closeIcon:v},function(t){t.notice({content:n["createElement"]("div",{className:a?"".concat(c,"-with-icon"):""},a,n["createElement"]("div",{className:"".concat(c,"-message")},i,e.message),n["createElement"]("div",{className:"".concat(c,"-description")},e.description),e.btn?n["createElement"]("span",{className:"".concat(c,"-btn")},e.btn):null),duration:r,closable:!0,onClose:e.onClose,onClick:e.onClick,key:e.key,style:e.style||{},className:e.className})})}var z={open:b,close:function(e){Object.keys(u).forEach(function(t){return u[t].removeNotice(e)})},config:v,destroy:function(){Object.keys(u).forEach(function(e){u[e].destroy(),delete u[e]})}};["success","info","warning","error"].forEach(function(e){z[e]=function(t){return z.open(a(a({},t),{type:e}))}}),z.warn=z.warning,t["a"]=z},Urep:function(e,t,c){},VXEj:function(e,t,c){"use strict";var n=c("q1tI"),r=c("17x9"),o=c("TSYQ"),a=c.n(o),l=c("BGR+"),i=c("W9HT"),u=c("H84U"),s=c("NUBc"),h=c("qrJ5"),f=c("/kpp");function p(e){if(!n["isValidElement"](e))return e;for(var t=arguments.length,c=new Array(t>1?t-1:0),r=1;r0&&n["createElement"]("ul",{className:"".concat(b,"-item-action"),key:"actions"},h.map(function(e,t){return n["createElement"]("li",{key:"".concat(b,"-item-action-").concat(t)},e,t!==h.length-1&&n["createElement"]("em",{className:"".concat(b,"-item-action-split")}))})),g=o?"div":"li",M=n["createElement"](g,w({},y,{className:a()("".concat(b,"-item"),d,m({},"".concat(b,"-item-no-flex"),!e.isFlexMode()))}),"vertical"===l&&v?[n["createElement"]("div",{className:"".concat(b,"-item-main"),key:"content"},s,z),n["createElement"]("div",{className:"".concat(b,"-item-extra"),key:"extra"},v)]:[s,z,p(v,{key:"extra"})]);return o?n["createElement"](f["a"],{span:k(o,"column"),xs:k(o,"xs"),sm:k(o,"sm"),md:k(o,"md"),lg:k(o,"lg"),xl:k(o,"xl"),xxl:k(o,"xxl")},M):M},e}return b(c,[{key:"isItemContainsTextNodeAndNotSingular",value:function(){var e,t=this.props.children;return n["Children"].forEach(t,function(t){"string"===typeof t&&(e=!0)}),e&&n["Children"].count(t)>1}},{key:"isFlexMode",value:function(){var e=this.props.extra,t=this.context.itemLayout;return"vertical"===t?!!e:!this.isItemContainsTextNodeAndNotSingular()}},{key:"render",value:function(){return n["createElement"](u["a"],null,this.renderItem)}}]),c}(n["Component"]);function E(e){"@babel/helpers - typeof";return E="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},E(e)}function P(e){return R(e)||N(e)||j(e)||T()}function T(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function j(e,t){if(e){if("string"===typeof e)return _(e,t);var c=Object.prototype.toString.call(e).slice(8,-1);return"Object"===c&&e.constructor&&(c=e.constructor.name),"Map"===c||"Set"===c?Array.from(e):"Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c)?_(e,t):void 0}}function N(e){if("undefined"!==typeof Symbol&&Symbol.iterator in Object(e))return Array.from(e)}function R(e){if(Array.isArray(e))return _(e)}function _(e,t){(null==t||t>e.length)&&(t=e.length);for(var c=0,n=new Array(t);cI&&(_.current=I);var D,K=C?n["createElement"]("div",{className:"".concat(E,"-pagination")},n["createElement"](s["a"],A({},_,{onChange:r.onPaginationChange,onShowSizeChange:r.onPaginationShowSizeChange}))):null,U=P(V);if(C&&V.length>(_.current-1)*_.pageSize&&(U=P(V).splice((_.current-1)*_.pageSize,_.pageSize)),D=j&&n["createElement"]("div",{style:{minHeight:53}}),U.length>0){var B=U.map(function(e,t){return r.renderItem(e,t)}),q=[];n["Children"].forEach(B,function(e,t){q.push(n["cloneElement"](e,{key:r.keys[t]}))}),D=H?n["createElement"](h["a"],{gutter:H.gutter},q):n["createElement"]("ul",{className:"".concat(E,"-items")},q)}else z||j||(D=r.renderEmpty(E,o));var W=_.position||"bottom";return n["createElement"]("div",A({className:R},Object(l["a"])(x,["rowKey","renderItem","locale"])),("top"===W||"both"===W)&&K,S&&n["createElement"]("div",{className:"".concat(E,"-header")},S),n["createElement"](i["a"],T,D,z),L&&n["createElement"]("div",{className:"".concat(E,"-footer")},L),M||("bottom"===W||"both"===W)&&K)};var o=e.pagination,u=o&&"object"===E(o)?o:{};return r.state={paginationCurrent:u.defaultCurrent||1,paginationSize:u.defaultPageSize||10},r}return K(c,[{key:"getChildContext",value:function(){return{grid:this.props.grid,itemLayout:this.props.itemLayout}}},{key:"triggerPaginationEvent",value:function(e){var t=this;return function(c,n){var r=t.props.pagination;t.setState({paginationCurrent:c,paginationSize:n}),r&&r[e]&&r[e](c,n)}}},{key:"isSomethingAfterLastItem",value:function(){var e=this.props,t=e.loadMore,c=e.pagination,n=e.footer;return!!(t||c||n)}},{key:"render",value:function(){return n["createElement"](u["a"],null,this.renderList)}}]),c}(n["Component"]);Z.Item=x,Z.childContextTypes={grid:r["any"],itemLayout:r["string"]},Z.defaultProps={dataSource:[],bordered:!1,split:!0,loading:!1,pagination:!1}},W9HT:function(e,t,c){"use strict";var n=c("q1tI"),r=c("17x9"),o=c("TSYQ"),a=c.n(o),l=c("BGR+"),i=c("sEfC"),u=c.n(i),s=c("H84U"),h=c("CWQg");function f(e){"@babel/helpers - typeof";return f="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f(e)}function p(){return p=Object.assign||function(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function k(e,t){if(e){if("string"===typeof e)return x(e,t);var c=Object.prototype.toString.call(e).slice(8,-1);return"Object"===c&&e.constructor&&(c=e.constructor.name),"Map"===c||"Set"===c?Array.from(e):"Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c)?x(e,t):void 0}}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var c=0,n=new Array(t);c0)for(var l,i=L(c);!(l=i()).done;){var u=l.value;if("undefined"!==typeof a[u]&&!a[u])return!1}else if(a.metaKey||a.ctrlKey||a.shiftKey||a.altKey)return!1;return a.key?a.key===r:a.keyCode===n}function j(e,t){var c=e.split("\n"),n=e.substr(0,t).split("\n"),r=n.length,o=n[n.length-1].length,a=c[n.length-1],l=n.length>1?n[n.length-2]:null,i=c.length>n.length?c[n.length]:null;return{line:r,col:o,beforeText:e.substr(0,t),afterText:e.substr(t),curLine:a,prevLine:l,nextLine:i}}for(var N={bold:["**","**"],italic:["*","*"],underline:["++","++"],strikethrough:["~~","~~"],quote:["\n> ","\n"],inlinecode:["`","`"],code:["\n```\n","\n```\n"]},R=1;R<=6;R++)N["h"+R]=["\n"+P("#",R)+" ","\n"];function _(e){for(var t=e.row,c=void 0===t?2:t,n=e.col,r=void 0===n?2:n,o=["|"],a=["|"],l=["|"],i="",u=1;u<=r;u++)o.push(" Head |"),l.push(" --- |"),a.push(" Data |");for(var s=1;s<=c;s++)i+="\n"+a.join("");return o.join("")+"\n"+l.join("")+i}function A(e,t){var c=t;if("\n"!==c.substr(0,1)&&(c="\n"+c),"unordered"===e)return c.length>1?c.replace(/\n/g,"\n* ").trim():"* ";var n=1;return c.length>1?c.replace(/\n/g,function(){return"\n"+n+++". "}).trim():"1. "}function F(e,t){return{text:e,newBlock:t,selection:{start:e.length,end:e.length}}}function I(e,t,c){if("undefined"!==typeof N[t])return{text:""+N[t][0]+e+N[t][1],selection:{start:N[t][0].length,end:N[t][0].length+e.length}};switch(t){case"tab":var n=1===c.tabMapValue?"\t":" ".repeat(c.tabMapValue),r=n+e.replace(/\n/g,"\n"+n),o=e.includes("\n")?e.match(/\n/g).length:0;return{text:r,selection:{start:c.tabMapValue,end:c.tabMapValue*(o+1)+e.length}};case"unordered":return F(A("unordered",e),!0);case"order":return F(A("order",e),!0);case"hr":return F("---",!0);case"table":return{text:_(c),newBlock:!0};case"image":return{text:"+")",selection:{start:2,end:e.length+2}};case"link":return{text:"["+e+"]("+(c.linkUrl||"")+")",selection:{start:1,end:e.length+1}}}return{text:e,selection:{start:0,end:e.length}}}var D=I;function K(e,t){var c={};return Object.keys(e).forEach(function(n){"undefined"!==typeof t[n]?"object"!==typeof t[n]?c[n]=t[n]:Array.isArray(t[n])?c[n]=[].concat(t[n]):c[n]=K(e[n],t[n]):c[n]=e[n]}),c}var U=function(e){for(var t=r()({},e),c=arguments.length,n=new Array(c>1?c-1:0),o=1;o=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function Z(e,t){if(e){if("string"===typeof e)return J(e,t);var c=Object.prototype.toString.call(e).slice(8,-1);return"Object"===c&&e.constructor&&(c=e.constructor.name),"Map"===c||"Set"===c?Array.from(e):"Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c)?J(e,t):void 0}}function J(e,t){(null==t||t>e.length)&&(t=e.length);for(var c=0,n=new Array(t);c0&&h.length>0&&(l="\n"+l,i&&(i.start++,i.end++));var f=u.afterText;c.start!==c.end&&(f=j(this.getMdValue(),c.end).afterText),""!==f.trim()&&"\n\n"!==f.substr(0,2)&&("\n"!==f.substr(0,1)&&(l+="\n"),l+="\n")}this.insertText(l,!0,i)},c.insertPlaceholder=function(e,t){var c=this;this.insertText(e,!0),t.then(function(t){var n=c.getMdValue().replace(e,t);c.setText(n)})},c.insertText=function(e,t,c){void 0===e&&(e=""),void 0===t&&(t=!1);var n=this.state.text,r=this.getSelection(),o=n.slice(0,r.start),a=n.slice(t?r.end:r.start,n.length);this.setText(o+e+a,void 0,c?{start:c.start+o.length,end:c.end+o.length}:{start:r.start,end:r.start})},c.setText=function(e,t,c){var n=this;void 0===e&&(e="");var r=this.config.onChangeTrigger,o=void 0===r?"both":r,a=e.replace(/\u21b5/g,"\n");if(this.state.text!==e){this.setState({text:a}),!this.props.onChange||"both"!==o&&"beforeRender"!==o||this.props.onChange({text:a,html:this.getHtmlValue()},t),this.emitter.emit(this.emitter.EVENT_CHANGE,e,t,"undefined"===typeof t),c&&setTimeout(function(){return n.setSelection(c)}),this.hasContentChanged||(this.hasContentChanged=!0);var l=this.renderHTML(a);"both"!==o&&"afterRender"!==o||l.then(function(){n.props.onChange&&n.props.onChange({text:n.state.text,html:n.getHtmlValue()},t)})}},c.getMdValue=function(){return this.state.text},c.getHtmlValue=function(){return"string"===typeof this.state.html?this.state.html:this.nodeMdPreview.current?this.nodeMdPreview.current.getHtml():""},c.onKeyboard=function(e){var t=this;Array.isArray(e)?e.forEach(function(e){return t.onKeyboard(e)}):this.keyboardListeners.includes(e)||this.keyboardListeners.push(e)},c.offKeyboard=function(e){var t=this;if(Array.isArray(e))e.forEach(function(e){return t.offKeyboard(e)});else{var c=this.keyboardListeners.indexOf(e);c>=0&&this.keyboardListeners.splice(c,1)}},c.handleKeyDown=function(e){for(var t,c=X(this.keyboardListeners);!(t=c()).done;){var n=t.value;if(T(e,n))return e.preventDefault(),void n.callback(e)}this.emitter.emit(this.emitter.EVENT_KEY_DOWN,e)},c.getEventType=function(e){switch(e){case"change":return this.emitter.EVENT_CHANGE;case"fullscreen":return this.emitter.EVENT_FULL_SCREEN;case"viewchange":return this.emitter.EVENT_VIEW_CHANGE;case"keydown":return this.emitter.EVENT_KEY_DOWN;case"editor_keydown":return this.emitter.EVENT_EDITOR_KEY_DOWN;case"blur":return this.emitter.EVENT_BLUR;case"focus":return this.emitter.EVENT_FOCUS;case"scroll":return this.emitter.EVENT_SCROLL}},c.on=function(e,t){var c=this.getEventType(e);c&&this.emitter.on(c,t)},c.off=function(e,t){var c=this.getEventType(e);c&&this.emitter.off(c,t)},c.setView=function(e){var t=this,c=r()({},this.state.view,e);this.setState({view:c},function(){t.emitter.emit(t.emitter.EVENT_VIEW_CHANGE,c)})},c.getView=function(){return r()({},this.state.view)},c.fullScreen=function(e){var t=this;this.state.fullScreen!==e&&this.setState({fullScreen:e},function(){t.emitter.emit(t.emitter.EVENT_FULL_SCREEN,e)})},c.registerPluginApi=function(e,t){this.pluginApis.set(e,t)},c.unregisterPluginApi=function(e){this.pluginApis.delete(e)},c.callPluginApi=function(e){var t=this.pluginApis.get(e);if(!t)throw new Error("API "+e+" not found");for(var c=arguments.length,n=new Array(c>1?c-1:0),r=1;r0&&e.onImageChanged(t.target.files[0])}}))},t}(V);ze.pluginName="image";var ge=function(e){function t(t){var c;return c=e.call(this,t)||this,c.handleKeyboard={key:"k",keyCode:75,aliasCommand:!0,withKey:["ctrlKey"],callback:function(){return c.editor.insertMarkdown("link")}},c}i()(t,e);var c=t.prototype;return c.componentDidMount=function(){this.editorConfig.shortcuts&&this.editor.onKeyboard(this.handleKeyboard)},c.componentWillUnmount=function(){this.editor.offKeyboard(this.handleKeyboard)},c.render=function(){var e=this;return u["createElement"]("span",{className:"button button-type-link",title:C.get("btnLink"),onClick:function(){return e.editor.insertMarkdown("link")}},u["createElement"](h,{type:"link"}))},t}(V);ge.pluginName="link";var Me=function(e){function t(t){var c;return c=e.call(this,t)||this,c.handleKeyboard={key:"7",keyCode:55,withKey:["ctrlKey","shiftKey"],aliasCommand:!0,callback:function(){return c.editor.insertMarkdown("order")}},c}i()(t,e);var c=t.prototype;return c.componentDidMount=function(){this.editorConfig.shortcuts&&this.editor.onKeyboard(this.handleKeyboard)},c.componentWillUnmount=function(){this.editor.offKeyboard(this.handleKeyboard)},c.render=function(){var e=this;return u["createElement"]("span",{className:"button button-type-ordered",title:C.get("btnOrdered"),onClick:function(){return e.editor.insertMarkdown("order")}},u["createElement"](h,{type:"list-ordered"}))},t}(V);Me.pluginName="list-ordered";var Ce=function(e){function t(t){var c;return c=e.call(this,t)||this,c.handleKeyboard={key:"8",keyCode:56,withKey:["ctrlKey","shiftKey"],aliasCommand:!0,callback:function(){return c.editor.insertMarkdown("unordered")}},c}i()(t,e);var c=t.prototype;return c.componentDidMount=function(){this.editorConfig.shortcuts&&this.editor.onKeyboard(this.handleKeyboard)},c.componentWillUnmount=function(){this.editor.offKeyboard(this.handleKeyboard)},c.render=function(){var e=this;return u["createElement"]("span",{className:"button button-type-unordered",title:C.get("btnUnordered"),onClick:function(){return e.editor.insertMarkdown("unordered")}},u["createElement"](h,{type:"list-unordered"}))},t}(V);Ce.pluginName="list-unordered";var He,Oe=100,Ve=function(){function e(e){void 0===e&&(e={}),this.record=[],this.recycle=[],this.initValue="";var t=e,c=t.maxSize,n=void 0===c?Oe:c;this.maxSize=n}var t=e.prototype;return t.push=function(e){var t=this.record.push(e);while(this.record.length>this.maxSize)this.record.shift();return t},t.get=function(){return this.record},t.getLast=function(){var e=this.record.length;return this.record[e-1]},t.undo=function(e){var t=this.record.pop();if("undefined"===typeof t)return this.initValue;if(t!==e)return this.recycle.push(t),t;var c=this.record.pop();return"undefined"===typeof c?(this.recycle.push(t),this.initValue):(this.recycle.push(t),c)},t.redo=function(){var e=this.recycle.pop();if("undefined"!==typeof e)return this.push(e),e},t.cleanRedo=function(){this.recycle=[]},t.getUndoCount=function(){return this.undo.length},t.getRedoCount=function(){return this.recycle.length},e}(),we=Ve,Se=function(e){function t(t){var c;return c=e.call(this,t)||this,c.handleKeyboards=[],c.lastPop=null,c.handleChange=c.handleChange.bind(a()(c)),c.handleRedo=c.handleRedo.bind(a()(c)),c.handleUndo=c.handleUndo.bind(a()(c)),c.handleKeyboards=[{key:"y",keyCode:89,withKey:["ctrlKey"],callback:c.handleRedo},{key:"z",keyCode:90,withKey:["metaKey","shiftKey"],callback:c.handleRedo},{key:"z",keyCode:90,aliasCommand:!0,withKey:["ctrlKey"],callback:c.handleUndo}],c.logger=new we({maxSize:c.editorConfig.loggerMaxSize}),c.editor.registerPluginApi("undo",c.handleUndo),c.editor.registerPluginApi("redo",c.handleRedo),c}i()(t,e);var c=t.prototype;return c.handleUndo=function(){var e=this.logger.undo(this.editor.getMdValue());"undefined"!==typeof e&&(this.pause(),this.lastPop=e,this.editor.setText(e),this.forceUpdate())},c.handleRedo=function(){var e=this.logger.redo();"undefined"!==typeof e&&(this.lastPop=e,this.editor.setText(e),this.forceUpdate())},c.handleChange=function(e,t,c){var n=this;if(this.logger.getLast()!==e&&(null===this.lastPop||this.lastPop!==e)){if(this.logger.cleanRedo(),c)return this.logger.push(e),this.lastPop=null,void this.forceUpdate();this.timerId&&(window.clearTimeout(this.timerId),this.timerId=0),this.timerId=window.setTimeout(function(){n.logger.getLast()!==e&&(n.logger.push(e),n.lastPop=null,n.forceUpdate()),window.clearTimeout(n.timerId),n.timerId=0},this.editorConfig.loggerInterval)}},c.componentDidMount=function(){var e=this;this.editor.on("change",this.handleChange),this.handleKeyboards.forEach(function(t){return e.editor.onKeyboard(t)}),this.logger.initValue=this.editor.getMdValue(),this.forceUpdate()},c.componentWillUnmount=function(){var e=this;this.timerId&&window.clearTimeout(this.timerId),this.editor.off("change",this.handleChange),this.editor.unregisterPluginApi("undo"),this.editor.unregisterPluginApi("redo"),this.handleKeyboards.forEach(function(t){return e.editor.offKeyboard(t)})},c.pause=function(){this.timerId&&(window.clearTimeout(this.timerId),this.timerId=void 0)},c.render=function(){var e=this.logger.getUndoCount()>1||this.logger.initValue!==this.editor.getMdValue(),t=this.logger.getRedoCount()>0;return u["createElement"](u["Fragment"],null,u["createElement"]("span",{className:"button button-type-undo "+(e?"":"disabled"),title:C.get("btnUndo"),onClick:this.handleUndo},u["createElement"](h,{type:"undo"})),u["createElement"]("span",{className:"button button-type-redo "+(t?"":"disabled"),title:C.get("btnRedo"),onClick:this.handleRedo},u["createElement"](h,{type:"redo"})))},t}(V);Se.pluginName="logger",function(e){e[e["SHOW_ALL"]=0]="SHOW_ALL",e[e["SHOW_MD"]=1]="SHOW_MD",e[e["SHOW_HTML"]=2]="SHOW_HTML"}(He||(He={}));var Le=function(e){function t(t){var c;return c=e.call(this,t)||this,c.handleClick=c.handleClick.bind(a()(c)),c.handleChange=c.handleChange.bind(a()(c)),c.state={view:c.editor.getView()},c}i()(t,e);var c=t.prototype;return c.handleClick=function(){switch(this.next){case He.SHOW_ALL:this.editor.setView({html:!0,md:!0});break;case He.SHOW_HTML:this.editor.setView({html:!0,md:!1});break;case He.SHOW_MD:this.editor.setView({html:!1,md:!0});break}},c.handleChange=function(e){this.setState({view:e})},c.componentDidMount=function(){this.editor.on("viewchange",this.handleChange)},c.componentWillUnmount=function(){this.editor.off("viewchange",this.handleChange)},c.getDisplayInfo=function(){var e=this.next;switch(e){case He.SHOW_ALL:return{icon:"view-split",title:"All"};case He.SHOW_HTML:return{icon:"visibility",title:"Preview"};default:return{icon:"keyboard",title:"Editor"}}},c.render=function(){if(this.isDisplay){var e=this.getDisplayInfo();return u["createElement"]("span",{className:"button button-type-mode",title:C.get("btnMode"+e.title),onClick:this.handleClick},u["createElement"](h,{type:e.icon}))}return null},O()(t,[{key:"isDisplay",get:function(){var e=this.editorConfig.canView;return!!e&&[e.html,e.md,e.both].filter(function(e){return e}).length>=2}},{key:"next",get:function(){var e=this.editorConfig.canView,t=this.state.view,c=[He.SHOW_ALL,He.SHOW_MD,He.SHOW_HTML];e&&(e.both||c.splice(c.indexOf(He.SHOW_ALL),1),e.md||c.splice(c.indexOf(He.SHOW_MD),1),e.html||c.splice(c.indexOf(He.SHOW_HTML),1));var n=He.SHOW_MD;if(t.html&&(n=He.SHOW_HTML),t.html&&t.md&&(n=He.SHOW_ALL),0===c.length)return n;if(1===c.length)return c[0];var r=c.indexOf(n);return r=0),e),d),w=f(f({},this.props),{children:null,inkBarAnimated:z,extraContent:l,style:r,prevIcon:H,nextIcon:O,className:V});return t=a?a(w,s["a"]):n["createElement"](s["a"],w),n["cloneElement"](t)}}]),c}(n["Component"]);V.defaultProps={animated:!0,type:"line"};var w=c("H84U"),S=c("6CfX"),L=function(e){if("undefined"!==typeof window&&window.document&&window.document.documentElement){var t=Array.isArray(e)?e:[e],c=window.document.documentElement;return t.some(function(e){return e in c.style})}return!1},k=L(["flex","webkitFlex","Flex","msFlex"]);function x(){return x=Object.assign||function(e){for(var t=1;t=0&&("small"===v||"large"===v)),"Tabs","`type=card|editable-card` doesn't have small or large size, it's by design.");var O=r("tabs",s),w=i()(p,(c={},E(c,"".concat(O,"-vertical"),"left"===y||"right"===y),E(c,"".concat(O,"-").concat(v),!!v),E(c,"".concat(O,"-card"),d.indexOf("card")>=0),E(c,"".concat(O,"-").concat(d),!0),E(c,"".concat(O,"-no-animation"),!H),c)),L=[];"editable-card"===d&&(L=[],n["Children"].forEach(b,function(t,c){if(!n["isValidElement"](t))return t;var r=t.props.closable;r="undefined"===typeof r||r;var o=r?n["createElement"](h["a"],{type:"close",className:"".concat(O,"-close-x"),onClick:function(c){return e.removeTab(t.key,c)}}):null;L.push(n["cloneElement"](t,{tab:n["createElement"]("div",{className:r?void 0:"".concat(O,"-tab-unclosable")},t.props.tab,o),key:t.key||c}))}),M||(C=n["createElement"]("span",null,n["createElement"](h["a"],{type:"plus",className:"".concat(O,"-new-tab"),onClick:e.createNewTab}),C))),C=C?n["createElement"]("div",{className:"".concat(O,"-extra-content")},C):null;var k=U(e.props,[]),T=i()("".concat(O,"-").concat(y,"-content"),d.indexOf("card")>=0&&"".concat(O,"-card-content"));return n["createElement"](o["b"],x({},e.props,{prefixCls:O,className:w,tabBarPosition:y,renderTabBar:function(){return n["createElement"](V,x({},Object(u["a"])(k,["className"]),{tabBarExtraContent:C}))},renderTabContent:function(){return n["createElement"](a["a"],{className:T,animated:H,animatedWithMargin:!0})},onChange:e.handleChange}),L.length>0?L:b)},e}return N(c,[{key:"componentDidMount",value:function(){var e=" no-flex",t=r["findDOMNode"](this);t&&!k&&-1===t.className.indexOf(e)&&(t.className+=e)}},{key:"render",value:function(){return n["createElement"](w["a"],null,this.renderTabs)}}]),c}(n["Component"]);B.TabPane=o["a"],B.defaultProps={hideAdd:!1,tabPosition:"top"}},ZTW2:function(e,t,c){},"Znn+":function(e,t,c){"use strict";c("cIOH"),c("9ama")},ZvpZ:function(e,t,c){"use strict";var n=c("H4fg"),r=c("61s2"),o=c("RlXo"),a=r["a"];t["a"]={locale:"en",Pagination:n["a"],DatePicker:r["a"],TimePicker:o["a"],Calendar:a,global:{placeholder:"Please select"},Table:{filterTitle:"Filter menu",filterConfirm:"OK",filterReset:"Reset",selectAll:"Select current page",selectInvert:"Invert current page",sortTitle:"Sort",expand:"Expand row",collapse:"Collapse row"},Modal:{okText:"OK",cancelText:"Cancel",justOkText:"OK"},Popconfirm:{okText:"OK",cancelText:"Cancel"},Transfer:{titles:["",""],searchPlaceholder:"Search here",itemUnit:"item",itemsUnit:"items"},Upload:{uploading:"Uploading...",removeFile:"Remove file",uploadError:"Upload error",previewFile:"Preview file",downloadFile:"Download file"},Empty:{description:"No Data"},Icon:{icon:"icon"},Text:{edit:"Edit",copy:"Copy",copied:"Copied",expand:"Expand"},PageHeader:{back:"Back"}}},bE4E:function(e,t,c){},bKJz:function(e,t,c){},bXwC:function(e,t,c){},bac3:function(e,t,c){"use strict";(function(e){c.d(t,"e",function(){return h}),c.d(t,"d",function(){return f}),c.d(t,"a",function(){return v}),c.d(t,"b",function(){return m}),c.d(t,"c",function(){return d}),c.d(t,"f",function(){return y});var n=c("QbLZ"),r=c.n(n),o=c("iCc5"),a=c.n(o),l=c("V7oC"),i=c.n(l),u=c("HXN9"),s=c("q1tI");function h(t){e&&Object({NODE_ENV:"production"})||console.error("[@ant-design/icons-react]: "+t+".")}function f(e){return"object"===typeof e&&"string"===typeof e.name&&"string"===typeof e.theme&&("object"===typeof e.icon||"function"===typeof e.icon)}function p(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return Object.keys(e).reduce(function(t,c){var n=e[c];switch(c){case"class":t.className=n,delete t["class"];break;default:t[c]=n}return t},{})}var v=function(){function e(){a()(this,e),this.collection={}}return i()(e,[{key:"clear",value:function(){this.collection={}}},{key:"delete",value:function(e){return delete this.collection[e]}},{key:"get",value:function(e){return this.collection[e]}},{key:"has",value:function(e){return Boolean(this.collection[e])}},{key:"set",value:function(e,t){return this.collection[e]=t,this}},{key:"size",get:function(){return Object.keys(this.collection).length}}]),e}();function m(e,t,c){return c?s["createElement"](e.tag,r()({key:t},p(e.attrs),c),(e.children||[]).map(function(c,n){return m(c,t+"-"+e.tag+"-"+n)})):s["createElement"](e.tag,r()({key:t},p(e.attrs)),(e.children||[]).map(function(c,n){return m(c,t+"-"+e.tag+"-"+n)}))}function d(e){return Object(u["generate"])(e)[0]}function y(e,t){switch(t){case"fill":return e+"-fill";case"outline":return e+"-o";case"twotone":return e+"-twotone";default:throw new TypeError("Unknown theme type: "+t+", name: "+e)}}}).call(this,c("Q2Ig"))},bbsP:function(e,t,c){"use strict";c("cIOH"),c("CWI+")},cIOH:function(e,t,c){},czTT:function(e,t,c){},d0bx:function(e,t,c){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var r=n(c("Zss7")),o=2,a=16,l=5,i=5,u=15,s=5,h=4;function f(e,t,c){var n;return n=Math.round(e.h)>=60&&Math.round(e.h)<=240?c?Math.round(e.h)-o*t:Math.round(e.h)+o*t:c?Math.round(e.h)+o*t:Math.round(e.h)-o*t,n<0?n+=360:n>=360&&(n-=360),n}function p(e,t,c){return 0===e.h&&0===e.s?e.s:(n=c?Math.round(100*e.s)-a*t:t===h?Math.round(100*e.s)+a:Math.round(100*e.s)+l*t,n>100&&(n=100),c&&t===s&&n>10&&(n=10),n<6&&(n=6),n);var n}function v(e,t,c){return c?Math.round(100*e.v)+i*t:Math.round(100*e.v)-u*t}function m(e){for(var t=[],c=r.default(e),n=s;n>0;n-=1){var o=c.toHsv(),a=r.default({h:f(o,n,!0),s:p(o,n,!0),v:v(o,n,!0)}).toHexString();t.push(a)}t.push(c.toHexString());for(n=1;n<=h;n+=1){o=c.toHsv(),a=r.default({h:f(o,n),s:p(o,n),v:v(o,n)}).toHexString();t.push(a)}return t}t.default=m},foW8:function(e,t,c){"use strict";t.__esModule=!0;var n=c("q1tI"),r=l(n),o=c("mdmE"),a=l(o);function l(e){return e&&e.__esModule?e:{default:e}}t.default=r.default.createContext||a.default,e.exports=t["default"]},g0mS:function(e,t,c){"use strict";c.d(t,"a",function(){return C});var n,r=c("q1tI"),o=c("i8i4"),a=c("/dDc"),l=c("oHiP"),i=c("H84U");function u(e){"@babel/helpers - typeof";return u="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u(e)}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function h(e,t){for(var c=0;c=0)){var r=e.props.insertExtraNode;e.extraNode=document.createElement("div");var o=y(e),l=o.extraNode;l.className="ant-click-animating-node";var i=e.getAttributeName();t.setAttribute(i,"true"),n=n||document.createElement("style"),c&&"#ffffff"!==c&&"rgb(255, 255, 255)"!==c&&M(c)&&!/rgba\(\d*, \d*, \d*, 0\)/.test(c)&&"transparent"!==c&&(e.csp&&e.csp.nonce&&(n.nonce=e.csp.nonce),l.style.borderColor=c,n.innerHTML="\n [ant-click-animating-without-extra-node='true']::after, .ant-click-animating-node {\n --antd-wave-shadow-color: ".concat(c,";\n }"),document.body.contains(n)||document.body.appendChild(n)),r&&t.appendChild(l),a["a"].addStartEventListener(t,e.onTransitionStart),a["a"].addEndEventListener(t,e.onTransitionEnd)}},e.onTransitionStart=function(t){if(!e.destroy){var c=Object(o["findDOMNode"])(y(e));t&&t.target===c&&(e.animationStart||e.resetEffect(c))}},e.onTransitionEnd=function(t){t&&"fadeEffect"===t.animationName&&e.resetEffect(t.target)},e.bindAnimationEvent=function(t){if(t&&t.getAttribute&&!t.getAttribute("disabled")&&!(t.className.indexOf("disabled")>=0)){var c=function(c){if("INPUT"!==c.target.tagName&&!g(c.target)){e.resetEffect(t);var n=getComputedStyle(t).getPropertyValue("border-top-color")||getComputedStyle(t).getPropertyValue("border-color")||getComputedStyle(t).getPropertyValue("background-color");e.clickWaveTimeoutId=window.setTimeout(function(){return e.onClick(t,n)},0),l["a"].cancel(e.animationStartId),e.animationStart=!0,e.animationStartId=Object(l["a"])(function(){e.animationStart=!1},10)}};return t.addEventListener("click",c,!0),{cancel:function(){t.removeEventListener("click",c,!0)}}}},e.renderWave=function(t){var c=t.csp,n=e.props.children;return e.csp=c,n},e}return f(c,[{key:"componentDidMount",value:function(){var e=Object(o["findDOMNode"])(this);e&&1===e.nodeType&&(this.instance=this.bindAnimationEvent(e))}},{key:"componentWillUnmount",value:function(){this.instance&&this.instance.cancel(),this.clickWaveTimeoutId&&clearTimeout(this.clickWaveTimeoutId),this.destroy=!0}},{key:"getAttributeName",value:function(){var e=this.props.insertExtraNode;return e?"ant-click-animating":"ant-click-animating-without-extra-node"}},{key:"resetEffect",value:function(e){if(e&&e!==this.extraNode&&e instanceof Element){var t=this.props.insertExtraNode,c=this.getAttributeName();e.setAttribute(c,"false"),n&&(n.innerHTML=""),t&&this.extraNode&&e.contains(this.extraNode)&&e.removeChild(this.extraNode),a["a"].removeStartEventListener(e,this.onTransitionStart),a["a"].removeEndEventListener(e,this.onTransitionEnd)}}},{key:"render",value:function(){return r["createElement"](i["a"],null,this.renderWave)}}]),c}(r["Component"])},g9YV:function(e,t,c){"use strict";c("cIOH"),c("pED+"),c("R9oj"),c("7Kak"),c("sRBo"),c("qVdP"),c("T2oS"),c("DjyN")},i8oR:function(e,t,c){},iQDF:function(e,t,c){"use strict";c("cIOH"),c("ZTW2"),c("5NDa"),c("pL63"),c("+BJd")},jCWc:function(e,t,c){"use strict";c("cIOH"),c("1GLa")},"jsC+":function(e,t,c){"use strict";var n=c("q1tI"),r=c("eDIo"),o=c("TSYQ"),a=c.n(o),l=c("H84U"),i=c("6CfX"),u=c("CtXQ"),s=c("CWQg");function h(e){"@babel/helpers - typeof";return h="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h(e)}function f(){return f=Object.assign||function(e){for(var t=1;t=0?"slide-down":"slide-up"}},{key:"render",value:function(){return n["createElement"](l["a"],null,this.renderDropDown)}}]),c}(n["Component"]);H.defaultProps={mouseEnterDelay:.15,mouseLeaveDelay:.1,placement:"bottomLeft"};var O=c("2/Rp");function V(e){"@babel/helpers - typeof";return V="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},V(e)}function w(){return w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var c=0,n=new Array(t);c0&&(d=r.getOptions().map(function(e){return n["createElement"](L,{prefixCls:p,key:e.value.toString(),disabled:"disabled"in e?e.disabled:o.disabled,value:e.value,checked:-1!==a.value.indexOf(e.value),onChange:e.onChange,className:"".concat(v,"-item")},e.label)}));var y=l()(v,u);return n["createElement"]("div",E({className:y,style:s},m),d)},r.state={value:e.value||e.defaultValue||[],registeredValues:[]},r}return I(c,[{key:"getChildContext",value:function(){return{checkboxGroup:{toggleOption:this.toggleOption,value:this.state.value,disabled:this.props.disabled,name:this.props.name,registerValue:this.registerValue,cancelValue:this.cancelValue}}}},{key:"shouldComponentUpdate",value:function(e,t){return!s()(this.props,e)||!s()(this.state,t)}},{key:"getOptions",value:function(){var e=this.props.options;return e.map(function(e){return"string"===typeof e?{label:e,value:e}:e})}},{key:"render",value:function(){return n["createElement"](h["a"],null,this.renderGroup)}}],[{key:"getDerivedStateFromProps",value:function(e){return"value"in e?{value:e.value||[]}:null}}]),c}(n["Component"]);Q.defaultProps={options:[]},Q.propTypes={defaultValue:r["array"],value:r["array"],options:r["array"].isRequired,onChange:r["func"]},Q.childContextTypes={checkboxGroup:r["any"]},Object(o["polyfill"])(Q);var X=Q;L.Group=X;t["a"]=L},lUTK:function(e,t,c){"use strict";c("cIOH"),c("x54q"),c("5Dmo")},mdmE:function(e,t,c){"use strict";t.__esModule=!0;var n=c("q1tI"),r=(u(n),c("17x9")),o=u(r),a=c("fZtv"),l=u(a),i=c("2W6z");u(i);function u(e){return e&&e.__esModule?e:{default:e}}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function h(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==typeof t&&"function"!==typeof t?e:t}function f(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var p=1073741823;function v(e,t){return e===t?0!==e||1/e===1/t:e!==e&&t!==t}function m(e){var t=[];return{on:function(e){t.push(e)},off:function(e){t=t.filter(function(t){return t!==e})},get:function(){return e},set:function(c,n){e=c,t.forEach(function(t){return t(e,n)})}}}function d(e){return Array.isArray(e)?e[0]:e}function y(e,t){var c,r,a="__create-react-context-"+(0,l.default)()+"__",i=function(e){function c(){var t,n,r;s(this,c);for(var o=arguments.length,a=Array(o),l=0;l1&&void 0!==arguments[1]?arguments[1]:1,c=o++,n=t;function l(){n-=1,n<=0?(e(),delete a[c]):a[c]=r()(l)}return a[c]=r()(l),c}l.cancel=function(e){void 0!==e&&(r.a.cancel(a[e]),delete a[e])},l.ids=a},"pED+":function(e,t,c){},pL63:function(e,t,c){},pwpV:function(e,t,c){},qCM6:function(e,t,c){},qVdP:function(e,t,c){"use strict";c("cIOH"),c("KAsB"),c("+L6B")},qrJ5:function(e,t,c){"use strict";var n,r=c("q1tI"),o=c("TSYQ"),a=c.n(o),l=c("17x9"),i=c("H84U"),u=c("o/2+"),s=c("CWQg");function h(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function f(){return f=Object.assign||function(e){for(var t=1;t0?{marginLeft:y[0]/-2,marginRight:y[0]/-2}:{}),y[1]>0?{marginTop:y[1]/-2,marginBottom:y[1]/-2}:{}),p),g=C({},m);return delete g.gutter,r["createElement"](u["a"].Provider,{value:{gutter:y}},r["createElement"]("div",C({},g,{className:b,style:z}),v))},e}return w(c,[{key:"componentDidMount",value:function(){var e=this;this.token=g.subscribe(function(t){var c=e.props.gutter;("object"===M(c)||Array.isArray(c)&&("object"===M(c[0])||"object"===M(c[1])))&&e.setState({screens:t})})}},{key:"componentWillUnmount",value:function(){g.unsubscribe(this.token)}},{key:"getGutter",value:function(){var e=[0,0],t=this.props.gutter,c=this.state.screens,n=Array.isArray(t)?t:[t,0];return n.forEach(function(t,n){if("object"===M(t))for(var r=0;re.length)&&(t=e.length);for(var c=0,n=new Array(t);c0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"children",c=[],n=function e(n){n.forEach(function(n){if(n[t]){var r=k({},n);delete r[t],c.push(r),n[t].length>0&&e(n[t])}else c.push(n)})};return n(e),c}function E(e,t){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"children";return e.map(function(e,n){var r={};return e[c]&&(r[c]=E(e[c],t,c)),k(k({},t(e,n)),r)})}function P(e,t){return e.reduce(function(e,c){if(t(c)&&e.push(c),c.children){var n=P(c.children,t);e.push.apply(e,H(n))}return e},[])}function T(e){var t=[];return n["Children"].forEach(e,function(e){if(n["isValidElement"](e)){var c=k({},e.props);e.key&&(c.key=e.key),e.type&&e.type.__ANT_TABLE_COLUMN_GROUP&&(c.children=T(c.children)),t.push(c)}}),t}function j(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(e||[]).forEach(function(e){var c=e.value,n=e.children;t[c.toString()]=c,j(n,t)}),t}function N(e){"@babel/helpers - typeof";return N="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},N(e)}function R(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function _(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function A(e,t){for(var c=0;c=0?delete c[e.key]:c[e.key]=e.keyPath,r.setState({keyPathOfSelectedItem:c})}},r.renderFilterIcon=function(){var e,t=r.props,c=t.column,o=t.locale,a=t.prefixCls,l=t.selectedKeys,i=l&&l.length>0,s=c.filterIcon;"function"===typeof s&&(s=s(i));var h=u()((e={},R(e,"".concat(a,"-selected"),"filtered"in c?c.filtered:i),R(e,"".concat(a,"-open"),r.getDropdownVisible()),e));return s?n["isValidElement"](s)?n["cloneElement"](s,{title:s.props.title||o.filterTitle,className:u()("".concat(a,"-icon"),h,s.props.className),onClick:G}):n["createElement"]("span",{className:u()("".concat(a,"-icon"),h)},s):n["createElement"](b["a"],{title:o.filterTitle,type:"filter",theme:"filled",className:h,onClick:G})};var o="filterDropdownVisible"in e.column&&e.column.filterDropdownVisible;return r.state={selectedKeys:e.selectedKeys,valueKeys:j(e.column.filters),keyPathOfSelectedItem:{},visible:o,prevProps:e},r}return F(c,[{key:"componentDidMount",value:function(){var e=this.props.column;this.setNeverShown(e)}},{key:"componentDidUpdate",value:function(){var e=this.props.column;this.setNeverShown(e)}},{key:"getDropdownVisible",value:function(){return!this.neverShown&&this.state.visible}},{key:"setVisible",value:function(e){var t=this.props.column;"filterDropdownVisible"in t||this.setState({visible:e}),t.onFilterDropdownVisibleChange&&t.onFilterDropdownVisibleChange(e)}},{key:"hasSubMenu",value:function(){var e=this.props.column.filters,t=void 0===e?[]:e;return t.some(function(e){return!!(e.children&&e.children.length>0)})}},{key:"confirmFilter",value:function(){var e=this.props,t=e.column,c=e.selectedKeys,n=e.confirmFilter,r=this.state,o=r.selectedKeys,a=r.valueKeys,l=t.filterDropdown;h()(o,c)||n(t,l?o:o.map(function(e){return a[e]}).filter(function(e){return void 0!==e}))}},{key:"renderMenus",value:function(e){var t=this,c=this.props,r=c.dropdownPrefixCls,o=c.prefixCls;return e.map(function(e){if(e.children&&e.children.length>0){var c=t.state.keyPathOfSelectedItem,a=Object.keys(c).some(function(t){return c[t].indexOf(e.value)>=0}),l=u()("".concat(o,"-dropdown-submenu"),R({},"".concat(r,"-submenu-contain-selected"),a));return n["createElement"](v["d"],{title:e.text,popupClassName:l,key:e.value.toString()},t.renderMenus(e.children))}return t.renderMenuItem(e)})}},{key:"renderMenuItem",value:function(e){var t=this.props.column,c=this.state.selectedKeys,r=!("filterMultiple"in t)||t.filterMultiple,o=(c||[]).map(function(e){return e.toString()}),a=r?n["createElement"](z["a"],{checked:o.indexOf(e.value.toString())>=0}):n["createElement"](g["a"],{checked:o.indexOf(e.value.toString())>=0});return n["createElement"](v["b"],{key:e.value},a,n["createElement"]("span",null,e.text))}},{key:"render",value:function(){var e=this,t=this.state.selectedKeys,c=this.props,r=c.column,o=c.locale,a=c.prefixCls,l=c.dropdownPrefixCls,i=c.getPopupContainer,s=!("filterMultiple"in r)||r.filterMultiple,h=u()(R({},"".concat(l,"-menu-without-submenu"),!this.hasSubMenu())),f=r.filterDropdown;f instanceof Function&&(f=f({prefixCls:"".concat(l,"-custom"),setSelectedKeys:function(t){return e.setSelectedKeys({selectedKeys:t})},selectedKeys:t,confirm:this.handleConfirm,clearFilters:this.handleClearFilters,filters:r.filters,visible:this.getDropdownVisible()}));var p=f?n["createElement"](C,{className:"".concat(a,"-dropdown")},f):n["createElement"](C,{className:"".concat(a,"-dropdown")},n["createElement"](v["e"],{multiple:s,onClick:this.handleMenuItemClick,prefixCls:"".concat(l,"-menu"),className:h,onSelect:this.setSelectedKeys,onDeselect:this.setSelectedKeys,selectedKeys:t&&t.map(function(e){return e.toString()}),getPopupContainer:i},this.renderMenus(r.filters)),n["createElement"]("div",{className:"".concat(a,"-dropdown-btns")},n["createElement"]("a",{className:"".concat(a,"-dropdown-link confirm"),onClick:this.handleConfirm},o.filterConfirm),n["createElement"]("a",{className:"".concat(a,"-dropdown-link clear"),onClick:this.handleClearFilters},o.filterReset)));return n["createElement"](y["a"],{trigger:["click"],placement:"bottomRight",overlay:p,visible:this.getDropdownVisible(),onVisibleChange:this.onVisibleChange,getPopupContainer:i,forceRender:!0},this.renderFilterIcon())}}],[{key:"getDerivedStateFromProps",value:function(e,t){var c=e.column,n=t.prevProps,r={prevProps:e};return"selectedKeys"in e&&!h()(n.selectedKeys,e.selectedKeys)&&(r.selectedKeys=e.selectedKeys),h()((n.column||{}).filters,(e.column||{}).filters)||(r.valueKeys=j(e.column.filters)),"filterDropdownVisible"in c&&(r.visible=c.filterDropdownVisible),r}}]),c}(n["Component"]);Y.defaultProps={column:{}},Object(f["polyfill"])(Y);var Q=Y;function X(){return X=Object.assign||function(e){for(var t=1;t=0:t.getState().selectedRowKeys.indexOf(n)>=0||c.indexOf(n)>=0,r}},{key:"subscribe",value:function(){var e=this,t=this.props.store;this.unsubscribe=t.subscribe(function(){var t=e.getCheckState(e.props);e.setState({checked:t})})}},{key:"render",value:function(){var e=this.props,t=e.type,c=e.rowIndex,r=se(e,["type","rowIndex"]),o=this.state.checked;return"radio"===t?n["createElement"](g["a"],$({checked:o,value:c},r)):n["createElement"](z["a"],$({checked:o},r))}}]),c}(n["Component"]),fe=c("BvKs");function pe(e){"@babel/helpers - typeof";return pe="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},pe(e)}function ve(e,t,c){return t in e?Object.defineProperty(e,t,{value:c,enumerable:!0,configurable:!0,writable:!0}):e[t]=c,e}function me(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function de(e,t){for(var c=0;c=0})}function Se(e){var t=e.store,c=e.data;if(!c.length)return!1;var n=we(Ve(Ve({},e),{data:c,type:"some",byDefaultChecked:!1}))&&!we(Ve(Ve({},e),{data:c,type:"every",byDefaultChecked:!1})),r=we(Ve(Ve({},e),{data:c,type:"some",byDefaultChecked:!0}))&&!we(Ve(Ve({},e),{data:c,type:"every",byDefaultChecked:!0}));return t.getState().selectionDirty?n:n||r}function Le(e){var t=e.store,c=e.data;return!!c.length&&(t.getState().selectionDirty?we(Ve(Ve({},e),{data:c,type:"every",byDefaultChecked:!1})):we(Ve(Ve({},e),{data:c,type:"every",byDefaultChecked:!1}))||we(Ve(Ve({},e),{data:c,type:"every",byDefaultChecked:!0})))}var ke=function(e){be(c,e);var t=ge(c);function c(e){var n;return me(this,c),n=t.call(this,e),n.state={checked:!1,indeterminate:!1},n.handleSelectAllChange=function(e){var t=e.target.checked;n.props.onSelect(t?"all":"removeAll",0,null)},n.defaultSelections=e.hideDefaultSelections?[]:[{key:"all",text:e.locale.selectAll},{key:"invert",text:e.locale.selectInvert}],n}return ye(c,[{key:"componentDidMount",value:function(){this.subscribe()}},{key:"componentWillUnmount",value:function(){this.unsubscribe&&this.unsubscribe()}},{key:"setCheckState",value:function(e){var t=Le(e),c=Se(e);this.setState(function(e){var n={};return c!==e.indeterminate&&(n.indeterminate=c),t!==e.checked&&(n.checked=t),n})}},{key:"subscribe",value:function(){var e=this,t=this.props.store;this.unsubscribe=t.subscribe(function(){e.setCheckState(e.props)})}},{key:"renderMenus",value:function(e){var t=this;return e.map(function(e,c){return n["createElement"](fe["a"].Item,{key:e.key||c},n["createElement"]("div",{onClick:function(){t.props.onSelect(e.key,c,e.onSelect)}},e.text))})}},{key:"render",value:function(){var e=this.props,t=e.disabled,c=e.prefixCls,r=e.selections,o=e.getPopupContainer,a=this.state,l=a.checked,i=a.indeterminate,s="".concat(c,"-selection"),h=null;if(r){var f=Array.isArray(r)?this.defaultSelections.concat(r):this.defaultSelections,p=n["createElement"](fe["a"],{className:"".concat(s,"-menu"),selectedKeys:[]},this.renderMenus(f));h=f.length>0?n["createElement"](y["a"],{overlay:p,getPopupContainer:o},n["createElement"]("div",{className:"".concat(s,"-down")},n["createElement"](b["a"],{type:"down"}))):null}return n["createElement"]("div",{className:s},n["createElement"](z["a"],{className:u()(ve({},"".concat(s,"-select-all-custom"),h)),checked:l,indeterminate:i,disabled:t,onChange:this.handleSelectAllChange}),h)}}],[{key:"getDerivedStateFromProps",value:function(e,t){var c=Le(e),n=Se(e),r={};return n!==t.indeterminate&&(r.indeterminate=n),c!==t.checked&&(r.checked=c),r}}]),c}(n["Component"]);Object(f["polyfill"])(ke);var xe=ke;function Ee(e){"@babel/helpers - typeof";return Ee="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ee(e)}function Pe(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Te(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&je(e,t)}function je(e,t){return je=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},je(e,t)}function Ne(e){var t=Ae();return function(){var c,n=Fe(e);if(t){var r=Fe(this).constructor;c=Reflect.construct(n,arguments,r)}else c=n.apply(this,arguments);return Re(this,c)}}function Re(e,t){return!t||"object"!==Ee(t)&&"function"!==typeof t?_e(e):t}function _e(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Ae(){if("undefined"===typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"===typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function Fe(e){return Fe=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},Fe(e)}var Ie=function(e){Te(c,e);var t=Ne(c);function c(){return Pe(this,c),t.apply(this,arguments)}return c}(n["Component"]);function De(e){"@babel/helpers - typeof";return De="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},De(e)}function Ke(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Ue(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Be(e,t)}function Be(e,t){return Be=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},Be(e,t)}function qe(e){var t=Ye();return function(){var c,n=Qe(e);if(t){var r=Qe(this).constructor;c=Reflect.construct(n,arguments,r)}else c=n.apply(this,arguments);return We(this,c)}}function We(e,t){return!t||"object"!==De(t)&&"function"!==typeof t?Ge(e):t}function Ge(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Ye(){if("undefined"===typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"===typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function Qe(e){return Qe=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},Qe(e)}var Xe=function(e){Ue(c,e);var t=qe(c);function c(){return Ke(this,c),t.apply(this,arguments)}return c}(n["Component"]);function Ze(e){"@babel/helpers - typeof";return Ze="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ze(e)}function Je(){return Je=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:"tr",t=function(t){nt(o,t);var c=ot(o);function o(e){var t;et(this,o),t=c.call(this,e),t.store=e.store;var n=t.store.getState(),r=n.selectedRowKeys;return t.state={selected:r.indexOf(e.rowKey)>=0},t}return ct(o,[{key:"componentDidMount",value:function(){this.subscribe()}},{key:"componentWillUnmount",value:function(){this.unsubscribe&&this.unsubscribe()}},{key:"subscribe",value:function(){var e=this,t=this.props,c=t.store,n=t.rowKey;this.unsubscribe=c.subscribe(function(){var t=e.store.getState(),c=t.selectedRowKeys,r=c.indexOf(n)>=0;r!==e.state.selected&&e.setState({selected:r})})}},{key:"render",value:function(){var t=Object(r["a"])(this.props,["prefixCls","rowKey","store"]),c=u()(this.props.className,$e({},"".concat(this.props.prefixCls,"-row-selected"),this.state.selected));return n["createElement"](e,Je(Je({},t),{className:c}),this.props.children)}}]),o}(n["Component"]);return t}Xe.__ANT_TABLE_COLUMN_GROUP=!0;var ht=c("xEkU"),ft=c.n(ht);function pt(e,t){if("undefined"===typeof window)return 0;var c=t?"pageYOffset":"pageXOffset",n=t?"scrollTop":"scrollLeft",r=e===window,o=r?e[c]:e[n];return r&&"number"!==typeof o&&(o=document.documentElement[n]),o}function vt(e,t,c,n){var r=c-t;return e/=n/2,e<1?r/2*e*e*e+t:r/2*((e-=2)*e*e+2)+t}function mt(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=t.getContainer,n=void 0===c?function(){return window}:c,r=t.callback,o=t.duration,a=void 0===o?450:o,l=n(),i=pt(l,!0),u=Date.now(),s=function t(){var c=Date.now(),n=c-u,o=vt(n>a?a:n,i,e,a);l===window?window.scrollTo(window.pageXOffset,o):l.scrollTop=o,n0&&void 0!==arguments[0]?arguments[0]:{},t=e&&e.body&&e.body.row;return Zt(Zt({},e),{body:Zt(Zt({},e.body),{row:st(t)})})};function lc(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e===t||["table","header","body"].every(function(c){return h()(e[c],t[c])})}function ic(e,t){return P(t||(e||{}).columns||[],function(e){return"undefined"!==typeof e.filteredValue})}function uc(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0,c={};return ic(e,t).forEach(function(e){var t=cc(e);c[t]=e.filteredValue}),c}function sc(e,t){return Object.keys(t).length!==Object.keys(e.filters).length||Object.keys(t).some(function(c){return t[c]!==e.filters[c]})}var hc=function(e){Bt(c,e);var t=Wt(c);function c(e){var o;Dt(this,c),o=t.call(this,e),o.setTableRef=function(e){o.rcTable=e},o.getCheckboxPropsByItem=function(e,t){var c=tc(o.props);if(!c.getCheckboxProps)return{};var n=o.getRecordKey(e,t);if(!o.props.checkboxPropsCache[n]){o.props.checkboxPropsCache[n]=c.getCheckboxProps(e)||{};var r=o.props.checkboxPropsCache[n];Object(At["a"])(!("checked"in r)&&!("defaultChecked"in r),"Table","Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.")}return o.props.checkboxPropsCache[n]},o.getRecordKey=function(e,t){var c=o.props.rowKey,n="function"===typeof c?c(e,t):e[c];return Object(At["a"])(void 0!==n,"Table","Each record in dataSource of table should have a unique `key` prop, or set `rowKey` of Table to an unique primary key, see https://u.ant.design/table-row-key"),void 0===n?t:n},o.onRow=function(e,t,c){var n=o.props.onRow,r=n?n(t,c):{};return Zt(Zt({},r),{prefixCls:e,store:o.props.store,rowKey:o.getRecordKey(t,c)})},o.generatePopupContainerFunc=function(e){var t=o.props.scroll,c=o.rcTable;return e||(t&&c?function(){return c.tableNode}:void 0)},o.scrollToFirstRow=function(){var e=o.props.scroll;e&&!1!==e.scrollToFirstRowOnChange&&mt(0,{getContainer:function(){return o.rcTable.bodyTable}})},o.handleFilter=function(e,t){var c=o.props,n=Zt({},o.state.pagination),r=Zt(Zt({},o.state.filters),It({},cc(e),t)),a=[];E(o.state.columns,function(e){e.children||a.push(cc(e))}),Object.keys(r).forEach(function(e){a.indexOf(e)<0&&delete r[e]}),c.pagination&&(n.current=1,n.onChange(n.current));var l={pagination:n,filters:{}},i=Zt({},r);ic(o.state).forEach(function(e){var t=cc(e);t&&delete i[t]}),Object.keys(i).length>0&&(l.filters=i),"object"===Ft(c.pagination)&&"current"in c.pagination&&(l.pagination=Zt(Zt({},n),{current:o.state.pagination.current})),o.setState(l,function(){o.scrollToFirstRow(),o.props.store.setState({selectionDirty:!1});var e=o.props.onChange;e&&e.apply(null,o.prepareParamsArguments(Zt(Zt({},o.state),{selectionDirty:!1,filters:r,pagination:n})))})},o.handleSelect=function(e,t,c){var n=c.target.checked,r=c.nativeEvent,a=o.props.store.getState().selectionDirty?[]:o.getDefaultSelection(),l=o.props.store.getState().selectedRowKeys.concat(a),i=o.getRecordKey(e,t),u=o.state.pivot,s=o.getFlatCurrentPageData(),h=t;if(o.props.expandedRowRender&&(h=s.findIndex(function(e){return o.getRecordKey(e,t)===i})),r.shiftKey&&void 0!==u&&h!==u){var f=[],p=Math.sign(u-h),v=Math.abs(u-h),m=0,d=function(){var e=h+m*p;m+=1;var t=s[e],c=o.getRecordKey(t,e),r=o.getCheckboxPropsByItem(t,e);r.disabled||(l.includes(c)?n||(l=l.filter(function(e){return c!==e}),f.push(c)):n&&(l.push(c),f.push(c)))};while(m<=v)d();o.setState({pivot:h}),o.props.store.setState({selectionDirty:!0}),o.setSelectedRowKeys(l,{selectWay:"onSelectMultiple",record:e,checked:n,changeRowKeys:f,nativeEvent:r})}else n?l.push(o.getRecordKey(e,h)):l=l.filter(function(e){return i!==e}),o.setState({pivot:h}),o.props.store.setState({selectionDirty:!0}),o.setSelectedRowKeys(l,{selectWay:"onSelect",record:e,checked:n,changeRowKeys:void 0,nativeEvent:r})},o.handleRadioSelect=function(e,t,c){var n=c.target.checked,r=c.nativeEvent,a=o.getRecordKey(e,t),l=[a];o.props.store.setState({selectionDirty:!0}),o.setSelectedRowKeys(l,{selectWay:"onSelect",record:e,checked:n,changeRowKeys:void 0,nativeEvent:r})},o.handleSelectRow=function(e,t,c){var n,r=o.getFlatCurrentPageData(),a=o.props.store.getState().selectionDirty?[]:o.getDefaultSelection(),l=o.props.store.getState().selectedRowKeys.concat(a),i=r.filter(function(e,t){return!o.getCheckboxPropsByItem(e,t).disabled}).map(function(e,t){return o.getRecordKey(e,t)}),u=[],s="onSelectAll";switch(e){case"all":i.forEach(function(e){l.indexOf(e)<0&&(l.push(e),u.push(e))}),s="onSelectAll",n=!0;break;case"removeAll":i.forEach(function(e){l.indexOf(e)>=0&&(l.splice(l.indexOf(e),1),u.push(e))}),s="onSelectAll",n=!1;break;case"invert":i.forEach(function(e){l.indexOf(e)<0?l.push(e):l.splice(l.indexOf(e),1),u.push(e),s="onSelectInvert"});break;default:break}o.props.store.setState({selectionDirty:!0});var h=o.props.rowSelection,f=2;if(h&&h.hideDefaultSelections&&(f=0),t>=f&&"function"===typeof c)return c(i);o.setSelectedRowKeys(l,{selectWay:s,checked:n,changeRowKeys:u})},o.handlePageChange=function(e){var t=o.props,c=Zt({},o.state.pagination);c.current=e||(c.current||1);for(var n=arguments.length,r=new Array(n>1?n-1:0),a=1;a0){var r=this.getSortStateFromColumns(t);nc(r.sortColumn,c)&&r.sortOrder===n||this.setState(r)}}},{key:"getDefaultSelection",value:function(){var e=this,t=tc(this.props);return t.getCheckboxProps?this.getFlatData().filter(function(t,c){return e.getCheckboxPropsByItem(t,c).defaultChecked}).map(function(t,c){return e.getRecordKey(t,c)}):[]}},{key:"getDefaultPagination",value:function(e){var t,c,n="object"===Ft(e.pagination)?e.pagination:{};return"current"in n?t=n.current:"defaultCurrent"in n&&(t=n.defaultCurrent),"pageSize"in n?c=n.pageSize:"defaultPageSize"in n&&(c=n.defaultPageSize),this.hasPagination(e)?Zt(Zt(Zt({},rc),n),{current:t||1,pageSize:c||10}):{}}},{key:"getSortOrderColumns",value:function(e){return P(e||(this.state||{}).columns||[],function(e){return"sortOrder"in e})}},{key:"getDefaultFilters",value:function(e){var t=uc(this.state,e),c=P(e||[],function(e){return"undefined"!==typeof e.defaultFilteredValue}),n=c.reduce(function(e,t){var c=cc(t);return e[c]=t.defaultFilteredValue,e},{});return Zt(Zt({},n),t)}},{key:"getDefaultSortOrder",value:function(e){var t=this.getSortStateFromColumns(e),c=P(e||[],function(e){return null!=e.defaultSortOrder})[0];return c&&!t.sortColumn?{sortColumn:c,sortOrder:c.defaultSortOrder}:t}},{key:"getSortStateFromColumns",value:function(e){var t=this.getSortOrderColumns(e).filter(function(e){return e.sortOrder})[0];return t?{sortColumn:t,sortOrder:t.sortOrder}:{sortColumn:null,sortOrder:null}}},{key:"getMaxCurrent",value:function(e){var t=this.state.pagination,c=t.current,n=t.pageSize;return(c-1)*n>=e?Math.floor((e-1)/n)+1:c}},{key:"getSorterFn",value:function(e){var t=e||this.state,c=t.sortOrder,n=t.sortColumn;if(c&&n&&"function"===typeof n.sorter)return function(e,t){var r=n.sorter(e,t,c);return 0!==r?"descend"===c?-r:r:0}}},{key:"getCurrentPageData",value:function(){var e,t,c=this.getLocalData(),n=this.state;return this.hasPagination()?(t=n.pagination.pageSize,e=this.getMaxCurrent(n.pagination.total||c.length)):(t=Number.MAX_VALUE,e=1),(c.length>t||t===Number.MAX_VALUE)&&(c=c.slice((e-1)*t,e*t)),c}},{key:"getFlatData",value:function(){var e=this.props.childrenColumnName;return x(this.getLocalData(null,!1),e)}},{key:"getFlatCurrentPageData",value:function(){var e=this.props.childrenColumnName;return x(this.getCurrentPageData(),e)}},{key:"getLocalData",value:function(e){var t=this,c=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],n=e||this.state,r=this.props.dataSource,o=r||[];o=o.slice(0);var a=this.getSorterFn(n);return a&&(o=this.recursiveSort(o,a)),c&&n.filters&&Object.keys(n.filters).forEach(function(e){var c=t.findColumn(e);if(c){var r=n.filters[e]||[];if(0!==r.length){var a=c.onFilter;o=a?o.filter(function(e){return r.some(function(t){return a(t,e)})}):o}}}),o}},{key:"setSelectedRowKeys",value:function(e,t){var c=this,n=t.selectWay,r=t.record,o=t.checked,a=t.changeRowKeys,l=t.nativeEvent,i=tc(this.props);!i||"selectedRowKeys"in i||this.props.store.setState({selectedRowKeys:e});var u=this.getFlatData();if(i.onChange||i[n]){var s=u.filter(function(t,n){return e.indexOf(c.getRecordKey(t,n))>=0});if(i.onChange&&i.onChange(e,s),"onSelect"===n&&i.onSelect)i.onSelect(r,o,s,l);else if("onSelectMultiple"===n&&i.onSelectMultiple){var h=u.filter(function(e,t){return a.indexOf(c.getRecordKey(e,t))>=0});i.onSelectMultiple(o,s,h)}else if("onSelectAll"===n&&i.onSelectAll){var f=u.filter(function(e,t){return a.indexOf(c.getRecordKey(e,t))>=0});i.onSelectAll(o,s,f)}else"onSelectInvert"===n&&i.onSelectInvert&&i.onSelectInvert(e)}}},{key:"toggleSortOrder",value:function(e){var t,c=e.sortDirections||this.props.sortDirections,n=this.state,r=n.sortOrder,o=n.sortColumn;if(nc(o,e)&&void 0!==r){var a=c.indexOf(r)+1;t=a===c.length?void 0:c[a]}else t=c[0];var l={sortOrder:t,sortColumn:t?e:null};0===this.getSortOrderColumns().length&&this.setState(l,this.scrollToFirstRow);var i=this.props.onChange;i&&i.apply(null,this.prepareParamsArguments(Zt(Zt({},this.state),l),e))}},{key:"hasPagination",value:function(e){return!1!==(e||this.props).pagination}},{key:"isSortColumn",value:function(e){var t=this.state.sortColumn;return!(!e||!t)&&cc(t)===cc(e)}},{key:"prepareParamsArguments",value:function(e,t){var c=Zt({},e.pagination);delete c.onChange,delete c.onShowSizeChange;var n=e.filters,r={},o=t;e.sortColumn&&e.sortOrder&&(o=e.sortColumn,r.column=e.sortColumn,r.order=e.sortOrder),o&&(r.field=o.dataIndex,r.columnKey=cc(o));var a={currentDataSource:this.getLocalData(e)};return[c,n,r,a]}},{key:"findColumn",value:function(e){var t;return E(this.state.columns,function(c){cc(c)===e&&(t=c)}),t}},{key:"recursiveSort",value:function(e,t){var c=this,n=this.props.childrenColumnName,r=void 0===n?"children":n;return e.sort(t).map(function(e){return e[r]?Zt(Zt({},e),It({},r,c.recursiveSort(e[r],t))):e})}},{key:"renderPagination",value:function(e,t){if(!this.hasPagination())return null;var c="default",r=this.state.pagination;r.size?c=r.size:"middle"!==this.props.size&&"small"!==this.props.size||(c="small");var o=r.position||"bottom",a=r.total||this.getLocalData().length;return a>0&&(o===t||"both"===o)?n["createElement"](dt["a"],Zt({key:"pagination-".concat(t)},r,{className:u()(r.className,"".concat(e,"-pagination")),onChange:this.handlePageChange,total:a,size:c,current:this.getMaxCurrent(a),onShowSizeChange:this.handleShowSizeChange})):null}},{key:"renderRowSelection",value:function(e){var t=this,c=e.prefixCls,r=e.locale,a=e.getPopupContainer,l=this.props.rowSelection,i=this.state.columns.concat();if(l){var s=this.getFlatCurrentPageData().filter(function(e,c){return!l.getCheckboxProps||!t.getCheckboxPropsByItem(e,c).disabled}),h=u()("".concat(c,"-selection-column"),It({},"".concat(c,"-selection-column-custom"),l.selections)),f=It({key:"selection-column",render:this.renderSelectionBox(l.type),className:h,fixed:l.fixed,width:l.columnWidth,title:l.columnTitle},o["INTERNAL_COL_DEFINE"],{className:"".concat(c,"-selection-col")});if("radio"!==l.type){var p=s.every(function(e,c){return t.getCheckboxPropsByItem(e,c).disabled});f.title=f.title||n["createElement"](xe,{store:this.props.store,locale:r,data:s,getCheckboxPropsByItem:this.getCheckboxPropsByItem,getRecordKey:this.getRecordKey,disabled:p,prefixCls:c,onSelect:this.handleSelectRow,selections:l.selections,hideDefaultSelections:l.hideDefaultSelections,getPopupContainer:this.generatePopupContainerFunc(a)})}"fixed"in l?f.fixed=l.fixed:i.some(function(e){return"left"===e.fixed||!0===e.fixed})&&(f.fixed="left"),i[0]&&"selection-column"===i[0].key?i[0]=f:i.unshift(f)}return i}},{key:"renderColumnsDropdown",value:function(e){var t=this,c=e.prefixCls,r=e.dropdownPrefixCls,o=e.columns,a=e.locale,l=e.getPopupContainer,i=this.state,s=i.sortOrder,h=i.filters;return E(o,function(e,o){var i,f,p,v=cc(e,o),m=e.onHeaderCell,d=t.isSortColumn(e);if(e.filters&&e.filters.length>0||e.filterDropdown){var y=v in h?h[v]:[];f=n["createElement"](Q,{locale:a,column:e,selectedKeys:y,confirmFilter:t.handleFilter,prefixCls:"".concat(c,"-filter"),dropdownPrefixCls:r||"ant-dropdown",getPopupContainer:t.generatePopupContainerFunc(l),key:"filter-dropdown"})}if(e.sorter){var z=e.sortDirections||t.props.sortDirections,g=d&&"ascend"===s,M=d&&"descend"===s,C=-1!==z.indexOf("ascend")&&n["createElement"](b["a"],{className:"".concat(c,"-column-sorter-up ").concat(g?"on":"off"),type:"caret-up",theme:"filled"}),H=-1!==z.indexOf("descend")&&n["createElement"](b["a"],{className:"".concat(c,"-column-sorter-down ").concat(M?"on":"off"),type:"caret-down",theme:"filled"});p=n["createElement"]("div",{title:a.sortTitle,className:u()("".concat(c,"-column-sorter-inner"),C&&H&&"".concat(c,"-column-sorter-inner-full")),key:"sorter"},C,H),m=function(c){var n={};e.onHeaderCell&&(n=Zt({},e.onHeaderCell(c)));var r=n.onClick;return n.onClick=function(){t.toggleSortOrder(e),r&&r.apply(void 0,arguments)},n}}return Zt(Zt({},e),{className:u()(e.className,(i={},It(i,"".concat(c,"-column-has-actions"),p||f),It(i,"".concat(c,"-column-has-filters"),f),It(i,"".concat(c,"-column-has-sorters"),p),It(i,"".concat(c,"-column-sort"),d&&s),i)),title:[n["createElement"]("span",{key:"title",className:"".concat(c,"-header-column")},n["createElement"]("div",{className:p?"".concat(c,"-column-sorters"):void 0},n["createElement"]("span",{className:"".concat(c,"-column-title")},t.renderColumnTitle(e.title)),n["createElement"]("span",{className:"".concat(c,"-column-sorter")},p))),f],onHeaderCell:m})})}},{key:"renderColumnTitle",value:function(e){var t=this.state,c=t.filters,n=t.sortOrder,r=t.sortColumn;return e instanceof Function?e({filters:c,sortOrder:n,sortColumn:r}):e}},{key:"render",value:function(){return n["createElement"](_t["a"],null,this.renderComponent)}}],[{key:"getDerivedStateFromProps",value:function(e,t){var c=t.prevProps,n=e.columns||T(e.children),r=Zt(Zt({},t),{prevProps:e,columns:n});if("pagination"in e||"pagination"in c){var o=Zt(Zt(Zt({},rc),t.pagination),e.pagination);o.current=o.current||1,o.pageSize=o.pageSize||10,r=Zt(Zt({},r),{pagination:!1!==e.pagination?o:oc})}e.rowSelection&&"selectedRowKeys"in e.rowSelection?e.store.setState({selectedRowKeys:e.rowSelection.selectedRowKeys||[]}):c.rowSelection&&!e.rowSelection&&e.store.setState({selectedRowKeys:[]}),"dataSource"in e&&e.dataSource!==c.dataSource&&e.store.setState({selectionDirty:!1}),e.setCheckboxPropsCache({});var a=ic(r,r.columns);if(a.length>0){var l=uc(r,r.columns),i=Zt({},r.filters);Object.keys(l).forEach(function(e){i[e]=l[e]}),sc(r,i)&&(r=Zt(Zt({},r),{filters:i}))}if(!lc(e.components,c.components)){var u=ac(e.components);r=Zt(Zt({},r),{components:u})}return r}}]),c}(n["Component"]);hc.propTypes={dataSource:l["array"],columns:l["array"],prefixCls:l["string"],useFixedHeader:l["bool"],rowSelection:l["object"],className:l["string"],size:l["string"],loading:l["oneOfType"]([l["bool"],l["object"]]),bordered:l["bool"],onChange:l["func"],locale:l["object"],dropdownPrefixCls:l["string"],sortDirections:l["array"],getPopupContainer:l["func"]},hc.defaultProps={dataSource:[],useFixedHeader:!1,className:"",size:"default",loading:!1,bordered:!1,indentSize:20,locale:{},rowKey:"key",showHeader:!0,sortDirections:["ascend","descend"],childrenColumnName:"children"},Object(f["polyfill"])(hc);var fc=function(e){Bt(c,e);var t=Wt(c);function c(e){var n;return Dt(this,c),n=t.call(this,e),n.setCheckboxPropsCache=function(e){return n.CheckboxPropsCache=e},n.CheckboxPropsCache={},n.store=Z({selectedRowKeys:tc(e).selectedRowKeys||[],selectionDirty:!1}),n}return Ut(c,[{key:"render",value:function(){return n["createElement"](hc,Zt({},this.props,{store:this.store,checkboxPropsCache:this.CheckboxPropsCache,setCheckboxPropsCache:this.setCheckboxPropsCache}))}}]),c}(n["Component"]);fc.displayName="withStore(Table)",fc.Column=Ie,fc.ColumnGroup=Xe;var pc=fc;t["a"]=pc},"wEI+":function(e,t,c){"use strict";var n=c("q1tI"),r=c("17x9"),o=c("wd/R"),a=c("veqR"),l=c("ul5b"),i=c("6CfX");function u(e){"@babel/helpers - typeof";return u="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u(e)}function s(){return s=Object.assign||function(e){for(var t=1;t*{line-height:1}.anticon svg{display:inline-block}.anticon:before{display:none}.anticon .anticon-icon{display:block}.anticon[tabindex]{cursor:pointer}.anticon-spin:before{display:inline-block;animation:loadingCircle 1s linear infinite}.anticon-spin{display:inline-block;animation:loadingCircle 1s linear infinite}.fade-appear,.fade-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.fade-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.fade-appear.fade-appear-active,.fade-enter.fade-enter-active{animation-name:antFadeIn;animation-play-state:running}.fade-leave.fade-leave-active{animation-name:antFadeOut;animation-play-state:running;pointer-events:none}.fade-appear,.fade-enter{opacity:0;animation-timing-function:linear}.fade-leave{animation-timing-function:linear}@keyframes antFadeIn{0%{opacity:0}to{opacity:1}}@keyframes antFadeOut{0%{opacity:1}to{opacity:0}}.move-up-appear,.move-up-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-up-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-up-appear.move-up-appear-active,.move-up-enter.move-up-enter-active{animation-name:antMoveUpIn;animation-play-state:running}.move-up-leave.move-up-leave-active{animation-name:antMoveUpOut;animation-play-state:running;pointer-events:none}.move-up-appear,.move-up-enter{opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.move-up-leave{animation-timing-function:cubic-bezier(.6,.04,.98,.34)}.move-down-appear,.move-down-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-down-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-down-appear.move-down-appear-active,.move-down-enter.move-down-enter-active{animation-name:antMoveDownIn;animation-play-state:running}.move-down-leave.move-down-leave-active{animation-name:antMoveDownOut;animation-play-state:running;pointer-events:none}.move-down-appear,.move-down-enter{opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.move-down-leave{animation-timing-function:cubic-bezier(.6,.04,.98,.34)}.move-left-appear,.move-left-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-left-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-left-appear.move-left-appear-active,.move-left-enter.move-left-enter-active{animation-name:antMoveLeftIn;animation-play-state:running}.move-left-leave.move-left-leave-active{animation-name:antMoveLeftOut;animation-play-state:running;pointer-events:none}.move-left-appear,.move-left-enter{opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.move-left-leave{animation-timing-function:cubic-bezier(.6,.04,.98,.34)}.move-right-appear,.move-right-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-right-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.move-right-appear.move-right-appear-active,.move-right-enter.move-right-enter-active{animation-name:antMoveRightIn;animation-play-state:running}.move-right-leave.move-right-leave-active{animation-name:antMoveRightOut;animation-play-state:running;pointer-events:none}.move-right-appear,.move-right-enter{opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.move-right-leave{animation-timing-function:cubic-bezier(.6,.04,.98,.34)}@keyframes antMoveDownIn{0%{transform:translateY(100%);transform-origin:0 0;opacity:0}to{transform:translateY(0);transform-origin:0 0;opacity:1}}@keyframes antMoveDownOut{0%{transform:translateY(0);transform-origin:0 0;opacity:1}to{transform:translateY(100%);transform-origin:0 0;opacity:0}}@keyframes antMoveLeftIn{0%{transform:translateX(-100%);transform-origin:0 0;opacity:0}to{transform:translateX(0);transform-origin:0 0;opacity:1}}@keyframes antMoveLeftOut{0%{transform:translateX(0);transform-origin:0 0;opacity:1}to{transform:translateX(-100%);transform-origin:0 0;opacity:0}}@keyframes antMoveRightIn{0%{transform:translateX(100%);transform-origin:0 0;opacity:0}to{transform:translateX(0);transform-origin:0 0;opacity:1}}@keyframes antMoveRightOut{0%{transform:translateX(0);transform-origin:0 0;opacity:1}to{transform:translateX(100%);transform-origin:0 0;opacity:0}}@keyframes antMoveUpIn{0%{transform:translateY(-100%);transform-origin:0 0;opacity:0}to{transform:translateY(0);transform-origin:0 0;opacity:1}}@keyframes antMoveUpOut{0%{transform:translateY(0);transform-origin:0 0;opacity:1}to{transform:translateY(-100%);transform-origin:0 0;opacity:0}}@keyframes loadingCircle{to{transform:rotate(1turn)}}[ant-click-animating-without-extra-node=true],[ant-click-animating=true]{position:relative}html{--antd-wave-shadow-color:#1890ff}.ant-click-animating-node,[ant-click-animating-without-extra-node=true]:after{position:absolute;top:0;right:0;bottom:0;left:0;display:block;border-radius:inherit;box-shadow:0 0 0 0 #1890ff;box-shadow:0 0 0 0 var(--antd-wave-shadow-color);opacity:.2;animation:fadeEffect 2s cubic-bezier(.08,.82,.17,1),waveEffect .4s cubic-bezier(.08,.82,.17,1);animation-fill-mode:forwards;content:"";pointer-events:none}@keyframes waveEffect{to{box-shadow:0 0 0 #1890ff;box-shadow:0 0 0 6px var(--antd-wave-shadow-color)}}@keyframes fadeEffect{to{opacity:0}}.slide-up-appear,.slide-up-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-up-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-up-appear.slide-up-appear-active,.slide-up-enter.slide-up-enter-active{animation-name:antSlideUpIn;animation-play-state:running}.slide-up-leave.slide-up-leave-active{animation-name:antSlideUpOut;animation-play-state:running;pointer-events:none}.slide-up-appear,.slide-up-enter{opacity:0;animation-timing-function:cubic-bezier(.23,1,.32,1)}.slide-up-leave{animation-timing-function:cubic-bezier(.755,.05,.855,.06)}.slide-down-appear,.slide-down-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-down-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-down-appear.slide-down-appear-active,.slide-down-enter.slide-down-enter-active{animation-name:antSlideDownIn;animation-play-state:running}.slide-down-leave.slide-down-leave-active{animation-name:antSlideDownOut;animation-play-state:running;pointer-events:none}.slide-down-appear,.slide-down-enter{opacity:0;animation-timing-function:cubic-bezier(.23,1,.32,1)}.slide-down-leave{animation-timing-function:cubic-bezier(.755,.05,.855,.06)}.slide-left-appear,.slide-left-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-left-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-left-appear.slide-left-appear-active,.slide-left-enter.slide-left-enter-active{animation-name:antSlideLeftIn;animation-play-state:running}.slide-left-leave.slide-left-leave-active{animation-name:antSlideLeftOut;animation-play-state:running;pointer-events:none}.slide-left-appear,.slide-left-enter{opacity:0;animation-timing-function:cubic-bezier(.23,1,.32,1)}.slide-left-leave{animation-timing-function:cubic-bezier(.755,.05,.855,.06)}.slide-right-appear,.slide-right-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-right-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.slide-right-appear.slide-right-appear-active,.slide-right-enter.slide-right-enter-active{animation-name:antSlideRightIn;animation-play-state:running}.slide-right-leave.slide-right-leave-active{animation-name:antSlideRightOut;animation-play-state:running;pointer-events:none}.slide-right-appear,.slide-right-enter{opacity:0;animation-timing-function:cubic-bezier(.23,1,.32,1)}.slide-right-leave{animation-timing-function:cubic-bezier(.755,.05,.855,.06)}@keyframes antSlideUpIn{0%{transform:scaleY(.8);transform-origin:0 0;opacity:0}to{transform:scaleY(1);transform-origin:0 0;opacity:1}}@keyframes antSlideUpOut{0%{transform:scaleY(1);transform-origin:0 0;opacity:1}to{transform:scaleY(.8);transform-origin:0 0;opacity:0}}@keyframes antSlideDownIn{0%{transform:scaleY(.8);transform-origin:100% 100%;opacity:0}to{transform:scaleY(1);transform-origin:100% 100%;opacity:1}}@keyframes antSlideDownOut{0%{transform:scaleY(1);transform-origin:100% 100%;opacity:1}to{transform:scaleY(.8);transform-origin:100% 100%;opacity:0}}@keyframes antSlideLeftIn{0%{transform:scaleX(.8);transform-origin:0 0;opacity:0}to{transform:scaleX(1);transform-origin:0 0;opacity:1}}@keyframes antSlideLeftOut{0%{transform:scaleX(1);transform-origin:0 0;opacity:1}to{transform:scaleX(.8);transform-origin:0 0;opacity:0}}@keyframes antSlideRightIn{0%{transform:scaleX(.8);transform-origin:100% 0;opacity:0}to{transform:scaleX(1);transform-origin:100% 0;opacity:1}}@keyframes antSlideRightOut{0%{transform:scaleX(1);transform-origin:100% 0;opacity:1}to{transform:scaleX(.8);transform-origin:100% 0;opacity:0}}.swing-appear,.swing-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.swing-appear.swing-appear-active,.swing-enter.swing-enter-active{animation-name:antSwingIn;animation-play-state:running}@keyframes antSwingIn{0%,to{transform:translateX(0)}20%{transform:translateX(-10px)}40%{transform:translateX(10px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}.zoom-appear,.zoom-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-appear.zoom-appear-active,.zoom-enter.zoom-enter-active{animation-name:antZoomIn;animation-play-state:running}.zoom-leave.zoom-leave-active{animation-name:antZoomOut;animation-play-state:running;pointer-events:none}.zoom-appear,.zoom-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}.zoom-big-appear,.zoom-big-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-big-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-big-appear.zoom-big-appear-active,.zoom-big-enter.zoom-big-enter-active{animation-name:antZoomBigIn;animation-play-state:running}.zoom-big-leave.zoom-big-leave-active{animation-name:antZoomBigOut;animation-play-state:running;pointer-events:none}.zoom-big-appear,.zoom-big-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-big-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}.zoom-big-fast-appear,.zoom-big-fast-enter{animation-duration:.1s;animation-fill-mode:both;animation-play-state:paused}.zoom-big-fast-leave{animation-duration:.1s;animation-fill-mode:both;animation-play-state:paused}.zoom-big-fast-appear.zoom-big-fast-appear-active,.zoom-big-fast-enter.zoom-big-fast-enter-active{animation-name:antZoomBigIn;animation-play-state:running}.zoom-big-fast-leave.zoom-big-fast-leave-active{animation-name:antZoomBigOut;animation-play-state:running;pointer-events:none}.zoom-big-fast-appear,.zoom-big-fast-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-big-fast-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}.zoom-up-appear,.zoom-up-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-up-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-up-appear.zoom-up-appear-active,.zoom-up-enter.zoom-up-enter-active{animation-name:antZoomUpIn;animation-play-state:running}.zoom-up-leave.zoom-up-leave-active{animation-name:antZoomUpOut;animation-play-state:running;pointer-events:none}.zoom-up-appear,.zoom-up-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-up-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}.zoom-down-appear,.zoom-down-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-down-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-down-appear.zoom-down-appear-active,.zoom-down-enter.zoom-down-enter-active{animation-name:antZoomDownIn;animation-play-state:running}.zoom-down-leave.zoom-down-leave-active{animation-name:antZoomDownOut;animation-play-state:running;pointer-events:none}.zoom-down-appear,.zoom-down-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-down-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}.zoom-left-appear,.zoom-left-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-left-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-left-appear.zoom-left-appear-active,.zoom-left-enter.zoom-left-enter-active{animation-name:antZoomLeftIn;animation-play-state:running}.zoom-left-leave.zoom-left-leave-active{animation-name:antZoomLeftOut;animation-play-state:running;pointer-events:none}.zoom-left-appear,.zoom-left-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-left-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}.zoom-right-appear,.zoom-right-enter{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-right-leave{animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.zoom-right-appear.zoom-right-appear-active,.zoom-right-enter.zoom-right-enter-active{animation-name:antZoomRightIn;animation-play-state:running}.zoom-right-leave.zoom-right-leave-active{animation-name:antZoomRightOut;animation-play-state:running;pointer-events:none}.zoom-right-appear,.zoom-right-enter{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(.08,.82,.17,1)}.zoom-right-leave{animation-timing-function:cubic-bezier(.78,.14,.15,.86)}@keyframes antZoomIn{0%{transform:scale(.2);opacity:0}to{transform:scale(1);opacity:1}}@keyframes antZoomOut{0%{transform:scale(1)}to{transform:scale(.2);opacity:0}}@keyframes antZoomBigIn{0%{transform:scale(.8);opacity:0}to{transform:scale(1);opacity:1}}@keyframes antZoomBigOut{0%{transform:scale(1)}to{transform:scale(.8);opacity:0}}@keyframes antZoomUpIn{0%{transform:scale(.8);transform-origin:50% 0;opacity:0}to{transform:scale(1);transform-origin:50% 0}}@keyframes antZoomUpOut{0%{transform:scale(1);transform-origin:50% 0}to{transform:scale(.8);transform-origin:50% 0;opacity:0}}@keyframes antZoomLeftIn{0%{transform:scale(.8);transform-origin:0 50%;opacity:0}to{transform:scale(1);transform-origin:0 50%}}@keyframes antZoomLeftOut{0%{transform:scale(1);transform-origin:0 50%}to{transform:scale(.8);transform-origin:0 50%;opacity:0}}@keyframes antZoomRightIn{0%{transform:scale(.8);transform-origin:100% 50%;opacity:0}to{transform:scale(1);transform-origin:100% 50%}}@keyframes antZoomRightOut{0%{transform:scale(1);transform-origin:100% 50%}to{transform:scale(.8);transform-origin:100% 50%;opacity:0}}@keyframes antZoomDownIn{0%{transform:scale(.8);transform-origin:50% 100%;opacity:0}to{transform:scale(1);transform-origin:50% 100%}}@keyframes antZoomDownOut{0%{transform:scale(1);transform-origin:50% 100%}to{transform:scale(.8);transform-origin:50% 100%;opacity:0}}.ant-motion-collapse-legacy{overflow:hidden}.ant-motion-collapse-legacy-active{transition:height .15s cubic-bezier(.645,.045,.355,1),opacity .15s cubic-bezier(.645,.045,.355,1)!important}.ant-motion-collapse{overflow:hidden;transition:height .15s cubic-bezier(.645,.045,.355,1),opacity .15s cubic-bezier(.645,.045,.355,1)!important}
+.ant-notification{box-sizing:border-box;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:fixed;z-index:1010;width:384px;max-width:calc(100vw - 32px);margin:0 24px 0 0}.ant-notification-bottomLeft,.ant-notification-topLeft{margin-right:0;margin-left:24px}.ant-notification-bottomLeft .ant-notification-fade-appear.ant-notification-fade-appear-active,.ant-notification-bottomLeft .ant-notification-fade-enter.ant-notification-fade-enter-active,.ant-notification-topLeft .ant-notification-fade-appear.ant-notification-fade-appear-active,.ant-notification-topLeft .ant-notification-fade-enter.ant-notification-fade-enter-active{animation-name:NotificationLeftFadeIn}.ant-notification-close-icon{font-size:14px;cursor:pointer}.ant-notification-notice{position:relative;margin-bottom:16px;padding:16px 24px;overflow:hidden;line-height:1.5;background:#fff;border-radius:4px;box-shadow:0 4px 12px rgba(0,0,0,.15)}.ant-notification-notice-message{display:inline-block;margin-bottom:8px;color:rgba(0,0,0,.85);font-size:16px;line-height:24px}.ant-notification-notice-message-single-line-auto-margin{display:block;width:calc(264px - 100%);max-width:4px;background-color:transparent;pointer-events:none}.ant-notification-notice-message-single-line-auto-margin:before{display:block;content:""}.ant-notification-notice-description{font-size:14px}.ant-notification-notice-closable .ant-notification-notice-message{padding-right:24px}.ant-notification-notice-with-icon .ant-notification-notice-message{margin-bottom:4px;margin-left:48px;font-size:16px}.ant-notification-notice-with-icon .ant-notification-notice-description{margin-left:48px;font-size:14px}.ant-notification-notice-icon{position:absolute;margin-left:4px;font-size:24px;line-height:24px}.anticon.ant-notification-notice-icon-success{color:#52c41a}.anticon.ant-notification-notice-icon-info{color:#1890ff}.anticon.ant-notification-notice-icon-warning{color:#faad14}.anticon.ant-notification-notice-icon-error{color:#f5222d}.ant-notification-notice-close{position:absolute;top:16px;right:22px;color:rgba(0,0,0,.45);outline:none}.ant-notification-notice-close:hover{color:rgba(0,0,0,.67)}.ant-notification-notice-btn{float:right;margin-top:16px}.ant-notification .notification-fade-effect{animation-duration:.24s;animation-timing-function:cubic-bezier(.645,.045,.355,1);animation-fill-mode:both}.ant-notification-fade-appear,.ant-notification-fade-enter{opacity:0;animation-duration:.24s;animation-timing-function:cubic-bezier(.645,.045,.355,1);animation-fill-mode:both;animation-play-state:paused}.ant-notification-fade-leave{animation-duration:.24s;animation-timing-function:cubic-bezier(.645,.045,.355,1);animation-fill-mode:both;animation-duration:.2s;animation-play-state:paused}.ant-notification-fade-appear.ant-notification-fade-appear-active,.ant-notification-fade-enter.ant-notification-fade-enter-active{animation-name:NotificationFadeIn;animation-play-state:running}.ant-notification-fade-leave.ant-notification-fade-leave-active{animation-name:NotificationFadeOut;animation-play-state:running}@keyframes NotificationFadeIn{0%{left:384px;opacity:0}to{left:0;opacity:1}}@keyframes NotificationLeftFadeIn{0%{right:384px;opacity:0}to{right:0;opacity:1}}@keyframes NotificationFadeOut{0%{max-height:150px;margin-bottom:16px;padding-top:16px 24px;padding-bottom:16px 24px;opacity:1}to{max-height:0;margin-bottom:0;padding-top:0;padding-bottom:0;opacity:0}}
+.ant-message{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:fixed;top:16px;left:0;z-index:1010;width:100%;pointer-events:none}.ant-message-notice{padding:8px;text-align:center}.ant-message-notice:first-child{margin-top:-8px}.ant-message-notice-content{display:inline-block;padding:10px 16px;background:#fff;border-radius:4px;box-shadow:0 4px 12px rgba(0,0,0,.15);pointer-events:all}.ant-message-success .anticon{color:#52c41a}.ant-message-error .anticon{color:#f5222d}.ant-message-warning .anticon{color:#faad14}.ant-message-info .anticon,.ant-message-loading .anticon{color:#1890ff}.ant-message .anticon{position:relative;top:1px;margin-right:8px;font-size:16px}.ant-message-notice.move-up-leave.move-up-leave-active{overflow:hidden;animation-name:MessageMoveOut;animation-duration:.3s}@keyframes MessageMoveOut{0%{max-height:150px;padding:8px;opacity:1}to{max-height:0;padding:0;opacity:0}}
+.ant-modal{box-sizing:border-box;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;top:100px;width:auto;margin:0 auto;padding:0 0 24px;pointer-events:none}.ant-modal-wrap{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;overflow:auto;outline:0;-webkit-overflow-scrolling:touch}.ant-modal-title{margin:0;color:rgba(0,0,0,.85);font-weight:500;font-size:16px;line-height:22px;word-wrap:break-word}.ant-modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:0;border-radius:4px;box-shadow:0 4px 12px rgba(0,0,0,.15);pointer-events:auto}.ant-modal-close{position:absolute;top:0;right:0;z-index:10;padding:0;color:rgba(0,0,0,.45);font-weight:700;line-height:1;text-decoration:none;background:transparent;border:0;outline:0;cursor:pointer;transition:color .3s}.ant-modal-close-x{display:block;width:56px;height:56px;font-size:16px;font-style:normal;line-height:56px;text-align:center;text-transform:none;text-rendering:auto}.ant-modal-close:focus,.ant-modal-close:hover{color:rgba(0,0,0,.75);text-decoration:none}.ant-modal-header{padding:16px 24px;color:rgba(0,0,0,.65);background:#fff;border-bottom:1px solid #e8e8e8;border-radius:4px 4px 0 0}.ant-modal-body{padding:24px;font-size:14px;line-height:1.5;word-wrap:break-word}.ant-modal-footer{padding:10px 16px;text-align:right;background:transparent;border-top:1px solid #e8e8e8;border-radius:0 0 4px 4px}.ant-modal-footer button+button{margin-bottom:0;margin-left:8px}.ant-modal.zoom-appear,.ant-modal.zoom-enter{transform:none;opacity:0;animation-duration:.3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-modal-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;height:100%;background-color:rgba(0,0,0,.45);filter:alpha(opacity=50)}.ant-modal-mask-hidden{display:none}.ant-modal-open{overflow:hidden}.ant-modal-centered{text-align:center}.ant-modal-centered:before{display:inline-block;width:0;height:100%;vertical-align:middle;content:""}.ant-modal-centered .ant-modal{top:0;display:inline-block;text-align:left;vertical-align:middle}@media (max-width:767px){.ant-modal{max-width:calc(100vw - 16px);margin:8px auto}.ant-modal-centered .ant-modal{flex:1 1}}.ant-modal-confirm .ant-modal-header{display:none}.ant-modal-confirm .ant-modal-close{display:none}.ant-modal-confirm .ant-modal-body{padding:32px 32px 24px}.ant-modal-confirm-body-wrapper{zoom:1}.ant-modal-confirm-body-wrapper:after,.ant-modal-confirm-body-wrapper:before{display:table;content:""}.ant-modal-confirm-body-wrapper:after{clear:both}.ant-modal-confirm-body .ant-modal-confirm-title{display:block;overflow:hidden;color:rgba(0,0,0,.85);font-weight:500;font-size:16px;line-height:1.4}.ant-modal-confirm-body .ant-modal-confirm-content{margin-top:8px;color:rgba(0,0,0,.65);font-size:14px}.ant-modal-confirm-body>.anticon{float:left;margin-right:16px;font-size:22px}.ant-modal-confirm-body>.anticon+.ant-modal-confirm-title+.ant-modal-confirm-content{margin-left:38px}.ant-modal-confirm .ant-modal-confirm-btns{float:right;margin-top:24px}.ant-modal-confirm .ant-modal-confirm-btns button+button{margin-bottom:0;margin-left:8px}.ant-modal-confirm-error .ant-modal-confirm-body>.anticon{color:#f5222d}.ant-modal-confirm-confirm .ant-modal-confirm-body>.anticon,.ant-modal-confirm-warning .ant-modal-confirm-body>.anticon{color:#faad14}.ant-modal-confirm-info .ant-modal-confirm-body>.anticon{color:#1890ff}.ant-modal-confirm-success .ant-modal-confirm-body>.anticon{color:#52c41a}
+.ant-btn{line-height:1.499;position:relative;display:inline-block;font-weight:400;white-space:nowrap;text-align:center;background-image:none;box-shadow:0 2px 0 rgba(0,0,0,.015);cursor:pointer;transition:all .3s cubic-bezier(.645,.045,.355,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;touch-action:manipulation;height:32px;padding:0 15px;font-size:14px;border-radius:4px;color:rgba(0,0,0,.65);background-color:#fff;border:1px solid #d9d9d9}.ant-btn>.anticon{line-height:1}.ant-btn,.ant-btn:active,.ant-btn:focus{outline:0}.ant-btn:not([disabled]):hover{text-decoration:none}.ant-btn:not([disabled]):active{outline:0;box-shadow:none}.ant-btn.disabled,.ant-btn[disabled]{cursor:not-allowed}.ant-btn.disabled>*,.ant-btn[disabled]>*{pointer-events:none}.ant-btn-lg{height:40px;padding:0 15px;font-size:16px;border-radius:4px}.ant-btn-sm{height:24px;padding:0 7px;font-size:14px;border-radius:4px}.ant-btn>a:only-child{color:currentColor}.ant-btn>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn:focus,.ant-btn:hover{color:#40a9ff;background-color:#fff;border-color:#40a9ff}.ant-btn:focus>a:only-child,.ant-btn:hover>a:only-child{color:currentColor}.ant-btn:focus>a:only-child:after,.ant-btn:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn.active,.ant-btn:active{color:#096dd9;background-color:#fff;border-color:#096dd9}.ant-btn.active>a:only-child,.ant-btn:active>a:only-child{color:currentColor}.ant-btn.active>a:only-child:after,.ant-btn:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-disabled,.ant-btn-disabled.active,.ant-btn-disabled:active,.ant-btn-disabled:focus,.ant-btn-disabled:hover,.ant-btn.disabled,.ant-btn.disabled.active,.ant-btn.disabled:active,.ant-btn.disabled:focus,.ant-btn.disabled:hover,.ant-btn[disabled],.ant-btn[disabled].active,.ant-btn[disabled]:active,.ant-btn[disabled]:focus,.ant-btn[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-disabled.active>a:only-child,.ant-btn-disabled:active>a:only-child,.ant-btn-disabled:focus>a:only-child,.ant-btn-disabled:hover>a:only-child,.ant-btn-disabled>a:only-child,.ant-btn.disabled.active>a:only-child,.ant-btn.disabled:active>a:only-child,.ant-btn.disabled:focus>a:only-child,.ant-btn.disabled:hover>a:only-child,.ant-btn.disabled>a:only-child,.ant-btn[disabled].active>a:only-child,.ant-btn[disabled]:active>a:only-child,.ant-btn[disabled]:focus>a:only-child,.ant-btn[disabled]:hover>a:only-child,.ant-btn[disabled]>a:only-child{color:currentColor}.ant-btn-disabled.active>a:only-child:after,.ant-btn-disabled:active>a:only-child:after,.ant-btn-disabled:focus>a:only-child:after,.ant-btn-disabled:hover>a:only-child:after,.ant-btn-disabled>a:only-child:after,.ant-btn.disabled.active>a:only-child:after,.ant-btn.disabled:active>a:only-child:after,.ant-btn.disabled:focus>a:only-child:after,.ant-btn.disabled:hover>a:only-child:after,.ant-btn.disabled>a:only-child:after,.ant-btn[disabled].active>a:only-child:after,.ant-btn[disabled]:active>a:only-child:after,.ant-btn[disabled]:focus>a:only-child:after,.ant-btn[disabled]:hover>a:only-child:after,.ant-btn[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn.active,.ant-btn:active,.ant-btn:focus,.ant-btn:hover{text-decoration:none;background:#fff}.ant-btn>i,.ant-btn>span{display:inline-block;transition:margin-left .3s cubic-bezier(.645,.045,.355,1);pointer-events:none}.ant-btn-primary{color:#fff;background-color:#1890ff;border-color:#1890ff;text-shadow:0 -1px 0 rgba(0,0,0,.12);box-shadow:0 2px 0 rgba(0,0,0,.045)}.ant-btn-primary>a:only-child{color:currentColor}.ant-btn-primary>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-primary:focus,.ant-btn-primary:hover{color:#fff;background-color:#40a9ff;border-color:#40a9ff}.ant-btn-primary:focus>a:only-child,.ant-btn-primary:hover>a:only-child{color:currentColor}.ant-btn-primary:focus>a:only-child:after,.ant-btn-primary:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-primary.active,.ant-btn-primary:active{color:#fff;background-color:#096dd9;border-color:#096dd9}.ant-btn-primary.active>a:only-child,.ant-btn-primary:active>a:only-child{color:currentColor}.ant-btn-primary.active>a:only-child:after,.ant-btn-primary:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-primary-disabled,.ant-btn-primary-disabled.active,.ant-btn-primary-disabled:active,.ant-btn-primary-disabled:focus,.ant-btn-primary-disabled:hover,.ant-btn-primary.disabled,.ant-btn-primary.disabled.active,.ant-btn-primary.disabled:active,.ant-btn-primary.disabled:focus,.ant-btn-primary.disabled:hover,.ant-btn-primary[disabled],.ant-btn-primary[disabled].active,.ant-btn-primary[disabled]:active,.ant-btn-primary[disabled]:focus,.ant-btn-primary[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-primary-disabled.active>a:only-child,.ant-btn-primary-disabled:active>a:only-child,.ant-btn-primary-disabled:focus>a:only-child,.ant-btn-primary-disabled:hover>a:only-child,.ant-btn-primary-disabled>a:only-child,.ant-btn-primary.disabled.active>a:only-child,.ant-btn-primary.disabled:active>a:only-child,.ant-btn-primary.disabled:focus>a:only-child,.ant-btn-primary.disabled:hover>a:only-child,.ant-btn-primary.disabled>a:only-child,.ant-btn-primary[disabled].active>a:only-child,.ant-btn-primary[disabled]:active>a:only-child,.ant-btn-primary[disabled]:focus>a:only-child,.ant-btn-primary[disabled]:hover>a:only-child,.ant-btn-primary[disabled]>a:only-child{color:currentColor}.ant-btn-primary-disabled.active>a:only-child:after,.ant-btn-primary-disabled:active>a:only-child:after,.ant-btn-primary-disabled:focus>a:only-child:after,.ant-btn-primary-disabled:hover>a:only-child:after,.ant-btn-primary-disabled>a:only-child:after,.ant-btn-primary.disabled.active>a:only-child:after,.ant-btn-primary.disabled:active>a:only-child:after,.ant-btn-primary.disabled:focus>a:only-child:after,.ant-btn-primary.disabled:hover>a:only-child:after,.ant-btn-primary.disabled>a:only-child:after,.ant-btn-primary[disabled].active>a:only-child:after,.ant-btn-primary[disabled]:active>a:only-child:after,.ant-btn-primary[disabled]:focus>a:only-child:after,.ant-btn-primary[disabled]:hover>a:only-child:after,.ant-btn-primary[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-group .ant-btn-primary:not(:first-child):not(:last-child){border-right-color:#40a9ff;border-left-color:#40a9ff}.ant-btn-group .ant-btn-primary:not(:first-child):not(:last-child):disabled{border-color:#d9d9d9}.ant-btn-group .ant-btn-primary:first-child:not(:last-child){border-right-color:#40a9ff}.ant-btn-group .ant-btn-primary:first-child:not(:last-child)[disabled]{border-right-color:#d9d9d9}.ant-btn-group .ant-btn-primary+.ant-btn-primary,.ant-btn-group .ant-btn-primary:last-child:not(:first-child){border-left-color:#40a9ff}.ant-btn-group .ant-btn-primary+.ant-btn-primary[disabled],.ant-btn-group .ant-btn-primary:last-child:not(:first-child)[disabled]{border-left-color:#d9d9d9}.ant-btn-ghost{color:rgba(0,0,0,.65);background-color:transparent;border-color:#d9d9d9}.ant-btn-ghost>a:only-child{color:currentColor}.ant-btn-ghost>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-ghost:focus,.ant-btn-ghost:hover{color:#40a9ff;background-color:transparent;border-color:#40a9ff}.ant-btn-ghost:focus>a:only-child,.ant-btn-ghost:hover>a:only-child{color:currentColor}.ant-btn-ghost:focus>a:only-child:after,.ant-btn-ghost:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-ghost.active,.ant-btn-ghost:active{color:#096dd9;background-color:transparent;border-color:#096dd9}.ant-btn-ghost.active>a:only-child,.ant-btn-ghost:active>a:only-child{color:currentColor}.ant-btn-ghost.active>a:only-child:after,.ant-btn-ghost:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-ghost-disabled,.ant-btn-ghost-disabled.active,.ant-btn-ghost-disabled:active,.ant-btn-ghost-disabled:focus,.ant-btn-ghost-disabled:hover,.ant-btn-ghost.disabled,.ant-btn-ghost.disabled.active,.ant-btn-ghost.disabled:active,.ant-btn-ghost.disabled:focus,.ant-btn-ghost.disabled:hover,.ant-btn-ghost[disabled],.ant-btn-ghost[disabled].active,.ant-btn-ghost[disabled]:active,.ant-btn-ghost[disabled]:focus,.ant-btn-ghost[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-ghost-disabled.active>a:only-child,.ant-btn-ghost-disabled:active>a:only-child,.ant-btn-ghost-disabled:focus>a:only-child,.ant-btn-ghost-disabled:hover>a:only-child,.ant-btn-ghost-disabled>a:only-child,.ant-btn-ghost.disabled.active>a:only-child,.ant-btn-ghost.disabled:active>a:only-child,.ant-btn-ghost.disabled:focus>a:only-child,.ant-btn-ghost.disabled:hover>a:only-child,.ant-btn-ghost.disabled>a:only-child,.ant-btn-ghost[disabled].active>a:only-child,.ant-btn-ghost[disabled]:active>a:only-child,.ant-btn-ghost[disabled]:focus>a:only-child,.ant-btn-ghost[disabled]:hover>a:only-child,.ant-btn-ghost[disabled]>a:only-child{color:currentColor}.ant-btn-ghost-disabled.active>a:only-child:after,.ant-btn-ghost-disabled:active>a:only-child:after,.ant-btn-ghost-disabled:focus>a:only-child:after,.ant-btn-ghost-disabled:hover>a:only-child:after,.ant-btn-ghost-disabled>a:only-child:after,.ant-btn-ghost.disabled.active>a:only-child:after,.ant-btn-ghost.disabled:active>a:only-child:after,.ant-btn-ghost.disabled:focus>a:only-child:after,.ant-btn-ghost.disabled:hover>a:only-child:after,.ant-btn-ghost.disabled>a:only-child:after,.ant-btn-ghost[disabled].active>a:only-child:after,.ant-btn-ghost[disabled]:active>a:only-child:after,.ant-btn-ghost[disabled]:focus>a:only-child:after,.ant-btn-ghost[disabled]:hover>a:only-child:after,.ant-btn-ghost[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-dashed{color:rgba(0,0,0,.65);background-color:#fff;border-color:#d9d9d9;border-style:dashed}.ant-btn-dashed>a:only-child{color:currentColor}.ant-btn-dashed>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-dashed:focus,.ant-btn-dashed:hover{color:#40a9ff;background-color:#fff;border-color:#40a9ff}.ant-btn-dashed:focus>a:only-child,.ant-btn-dashed:hover>a:only-child{color:currentColor}.ant-btn-dashed:focus>a:only-child:after,.ant-btn-dashed:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-dashed.active,.ant-btn-dashed:active{color:#096dd9;background-color:#fff;border-color:#096dd9}.ant-btn-dashed.active>a:only-child,.ant-btn-dashed:active>a:only-child{color:currentColor}.ant-btn-dashed.active>a:only-child:after,.ant-btn-dashed:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-dashed-disabled,.ant-btn-dashed-disabled.active,.ant-btn-dashed-disabled:active,.ant-btn-dashed-disabled:focus,.ant-btn-dashed-disabled:hover,.ant-btn-dashed.disabled,.ant-btn-dashed.disabled.active,.ant-btn-dashed.disabled:active,.ant-btn-dashed.disabled:focus,.ant-btn-dashed.disabled:hover,.ant-btn-dashed[disabled],.ant-btn-dashed[disabled].active,.ant-btn-dashed[disabled]:active,.ant-btn-dashed[disabled]:focus,.ant-btn-dashed[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-dashed-disabled.active>a:only-child,.ant-btn-dashed-disabled:active>a:only-child,.ant-btn-dashed-disabled:focus>a:only-child,.ant-btn-dashed-disabled:hover>a:only-child,.ant-btn-dashed-disabled>a:only-child,.ant-btn-dashed.disabled.active>a:only-child,.ant-btn-dashed.disabled:active>a:only-child,.ant-btn-dashed.disabled:focus>a:only-child,.ant-btn-dashed.disabled:hover>a:only-child,.ant-btn-dashed.disabled>a:only-child,.ant-btn-dashed[disabled].active>a:only-child,.ant-btn-dashed[disabled]:active>a:only-child,.ant-btn-dashed[disabled]:focus>a:only-child,.ant-btn-dashed[disabled]:hover>a:only-child,.ant-btn-dashed[disabled]>a:only-child{color:currentColor}.ant-btn-dashed-disabled.active>a:only-child:after,.ant-btn-dashed-disabled:active>a:only-child:after,.ant-btn-dashed-disabled:focus>a:only-child:after,.ant-btn-dashed-disabled:hover>a:only-child:after,.ant-btn-dashed-disabled>a:only-child:after,.ant-btn-dashed.disabled.active>a:only-child:after,.ant-btn-dashed.disabled:active>a:only-child:after,.ant-btn-dashed.disabled:focus>a:only-child:after,.ant-btn-dashed.disabled:hover>a:only-child:after,.ant-btn-dashed.disabled>a:only-child:after,.ant-btn-dashed[disabled].active>a:only-child:after,.ant-btn-dashed[disabled]:active>a:only-child:after,.ant-btn-dashed[disabled]:focus>a:only-child:after,.ant-btn-dashed[disabled]:hover>a:only-child:after,.ant-btn-dashed[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-danger{color:#fff;background-color:#ff4d4f;border-color:#ff4d4f;text-shadow:0 -1px 0 rgba(0,0,0,.12);box-shadow:0 2px 0 rgba(0,0,0,.045)}.ant-btn-danger>a:only-child{color:currentColor}.ant-btn-danger>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-danger:focus,.ant-btn-danger:hover{color:#fff;background-color:#ff7875;border-color:#ff7875}.ant-btn-danger:focus>a:only-child,.ant-btn-danger:hover>a:only-child{color:currentColor}.ant-btn-danger:focus>a:only-child:after,.ant-btn-danger:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-danger.active,.ant-btn-danger:active{color:#fff;background-color:#d9363e;border-color:#d9363e}.ant-btn-danger.active>a:only-child,.ant-btn-danger:active>a:only-child{color:currentColor}.ant-btn-danger.active>a:only-child:after,.ant-btn-danger:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-danger-disabled,.ant-btn-danger-disabled.active,.ant-btn-danger-disabled:active,.ant-btn-danger-disabled:focus,.ant-btn-danger-disabled:hover,.ant-btn-danger.disabled,.ant-btn-danger.disabled.active,.ant-btn-danger.disabled:active,.ant-btn-danger.disabled:focus,.ant-btn-danger.disabled:hover,.ant-btn-danger[disabled],.ant-btn-danger[disabled].active,.ant-btn-danger[disabled]:active,.ant-btn-danger[disabled]:focus,.ant-btn-danger[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-danger-disabled.active>a:only-child,.ant-btn-danger-disabled:active>a:only-child,.ant-btn-danger-disabled:focus>a:only-child,.ant-btn-danger-disabled:hover>a:only-child,.ant-btn-danger-disabled>a:only-child,.ant-btn-danger.disabled.active>a:only-child,.ant-btn-danger.disabled:active>a:only-child,.ant-btn-danger.disabled:focus>a:only-child,.ant-btn-danger.disabled:hover>a:only-child,.ant-btn-danger.disabled>a:only-child,.ant-btn-danger[disabled].active>a:only-child,.ant-btn-danger[disabled]:active>a:only-child,.ant-btn-danger[disabled]:focus>a:only-child,.ant-btn-danger[disabled]:hover>a:only-child,.ant-btn-danger[disabled]>a:only-child{color:currentColor}.ant-btn-danger-disabled.active>a:only-child:after,.ant-btn-danger-disabled:active>a:only-child:after,.ant-btn-danger-disabled:focus>a:only-child:after,.ant-btn-danger-disabled:hover>a:only-child:after,.ant-btn-danger-disabled>a:only-child:after,.ant-btn-danger.disabled.active>a:only-child:after,.ant-btn-danger.disabled:active>a:only-child:after,.ant-btn-danger.disabled:focus>a:only-child:after,.ant-btn-danger.disabled:hover>a:only-child:after,.ant-btn-danger.disabled>a:only-child:after,.ant-btn-danger[disabled].active>a:only-child:after,.ant-btn-danger[disabled]:active>a:only-child:after,.ant-btn-danger[disabled]:focus>a:only-child:after,.ant-btn-danger[disabled]:hover>a:only-child:after,.ant-btn-danger[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-link{color:#1890ff;background-color:transparent;border-color:transparent;box-shadow:none}.ant-btn-link>a:only-child{color:currentColor}.ant-btn-link>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-link:focus,.ant-btn-link:hover{color:#40a9ff;background-color:transparent;border-color:#40a9ff}.ant-btn-link:focus>a:only-child,.ant-btn-link:hover>a:only-child{color:currentColor}.ant-btn-link:focus>a:only-child:after,.ant-btn-link:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-link.active,.ant-btn-link:active{color:#096dd9;background-color:transparent;border-color:#096dd9}.ant-btn-link.active>a:only-child,.ant-btn-link:active>a:only-child{color:currentColor}.ant-btn-link.active>a:only-child:after,.ant-btn-link:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-link-disabled,.ant-btn-link-disabled.active,.ant-btn-link-disabled:active,.ant-btn-link-disabled:focus,.ant-btn-link-disabled:hover,.ant-btn-link.disabled,.ant-btn-link.disabled.active,.ant-btn-link.disabled:active,.ant-btn-link.disabled:focus,.ant-btn-link.disabled:hover,.ant-btn-link[disabled],.ant-btn-link[disabled].active,.ant-btn-link[disabled]:active,.ant-btn-link[disabled]:focus,.ant-btn-link[disabled]:hover{background-color:#f5f5f5;border-color:#d9d9d9}.ant-btn-link:active,.ant-btn-link:focus,.ant-btn-link:hover{border-color:transparent}.ant-btn-link-disabled,.ant-btn-link-disabled.active,.ant-btn-link-disabled:active,.ant-btn-link-disabled:focus,.ant-btn-link-disabled:hover,.ant-btn-link.disabled,.ant-btn-link.disabled.active,.ant-btn-link.disabled:active,.ant-btn-link.disabled:focus,.ant-btn-link.disabled:hover,.ant-btn-link[disabled],.ant-btn-link[disabled].active,.ant-btn-link[disabled]:active,.ant-btn-link[disabled]:focus,.ant-btn-link[disabled]:hover{color:rgba(0,0,0,.25);background-color:transparent;border-color:transparent;text-shadow:none;box-shadow:none}.ant-btn-link-disabled.active>a:only-child,.ant-btn-link-disabled:active>a:only-child,.ant-btn-link-disabled:focus>a:only-child,.ant-btn-link-disabled:hover>a:only-child,.ant-btn-link-disabled>a:only-child,.ant-btn-link.disabled.active>a:only-child,.ant-btn-link.disabled:active>a:only-child,.ant-btn-link.disabled:focus>a:only-child,.ant-btn-link.disabled:hover>a:only-child,.ant-btn-link.disabled>a:only-child,.ant-btn-link[disabled].active>a:only-child,.ant-btn-link[disabled]:active>a:only-child,.ant-btn-link[disabled]:focus>a:only-child,.ant-btn-link[disabled]:hover>a:only-child,.ant-btn-link[disabled]>a:only-child{color:currentColor}.ant-btn-link-disabled.active>a:only-child:after,.ant-btn-link-disabled:active>a:only-child:after,.ant-btn-link-disabled:focus>a:only-child:after,.ant-btn-link-disabled:hover>a:only-child:after,.ant-btn-link-disabled>a:only-child:after,.ant-btn-link.disabled.active>a:only-child:after,.ant-btn-link.disabled:active>a:only-child:after,.ant-btn-link.disabled:focus>a:only-child:after,.ant-btn-link.disabled:hover>a:only-child:after,.ant-btn-link.disabled>a:only-child:after,.ant-btn-link[disabled].active>a:only-child:after,.ant-btn-link[disabled]:active>a:only-child:after,.ant-btn-link[disabled]:focus>a:only-child:after,.ant-btn-link[disabled]:hover>a:only-child:after,.ant-btn-link[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-icon-only{width:32px;height:32px;padding:0;font-size:16px;border-radius:4px}.ant-btn-icon-only.ant-btn-lg{width:40px;height:40px;padding:0;font-size:18px;border-radius:4px}.ant-btn-icon-only.ant-btn-sm{width:24px;height:24px;padding:0;font-size:14px;border-radius:4px}.ant-btn-icon-only>i{vertical-align:middle}.ant-btn-round{height:32px;padding:0 16px;font-size:14px;border-radius:32px}.ant-btn-round.ant-btn-lg{height:40px;padding:0 20px;font-size:16px;border-radius:40px}.ant-btn-round.ant-btn-sm{height:24px;padding:0 12px;font-size:14px;border-radius:24px}.ant-btn-round.ant-btn-icon-only{width:auto}.ant-btn-circle,.ant-btn-circle-outline{min-width:32px;padding-right:0;padding-left:0;text-align:center;border-radius:50%}.ant-btn-circle-outline.ant-btn-lg,.ant-btn-circle.ant-btn-lg{min-width:40px;border-radius:50%}.ant-btn-circle-outline.ant-btn-sm,.ant-btn-circle.ant-btn-sm{min-width:24px;border-radius:50%}.ant-btn:before{position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;z-index:1;display:none;background:#fff;border-radius:inherit;opacity:.35;transition:opacity .2s;content:"";pointer-events:none}.ant-btn .anticon{transition:margin-left .3s cubic-bezier(.645,.045,.355,1)}.ant-btn .anticon.anticon-minus>svg,.ant-btn .anticon.anticon-plus>svg{shape-rendering:optimizeSpeed}.ant-btn.ant-btn-loading{position:relative}.ant-btn.ant-btn-loading:not([disabled]){pointer-events:none}.ant-btn.ant-btn-loading:before{display:block}.ant-btn.ant-btn-loading:not(.ant-btn-circle):not(.ant-btn-circle-outline):not(.ant-btn-icon-only){padding-left:29px}.ant-btn.ant-btn-loading:not(.ant-btn-circle):not(.ant-btn-circle-outline):not(.ant-btn-icon-only) .anticon:not(:last-child){margin-left:-14px}.ant-btn-sm.ant-btn-loading:not(.ant-btn-circle):not(.ant-btn-circle-outline):not(.ant-btn-icon-only){padding-left:24px}.ant-btn-sm.ant-btn-loading:not(.ant-btn-circle):not(.ant-btn-circle-outline):not(.ant-btn-icon-only) .anticon{margin-left:-17px}.ant-btn-group{position:relative;display:inline-block}.ant-btn-group>.ant-btn,.ant-btn-group>span>.ant-btn{position:relative}.ant-btn-group>.ant-btn.active,.ant-btn-group>.ant-btn:active,.ant-btn-group>.ant-btn:focus,.ant-btn-group>.ant-btn:hover,.ant-btn-group>span>.ant-btn.active,.ant-btn-group>span>.ant-btn:active,.ant-btn-group>span>.ant-btn:focus,.ant-btn-group>span>.ant-btn:hover{z-index:2}.ant-btn-group>.ant-btn:disabled,.ant-btn-group>span>.ant-btn:disabled{z-index:0}.ant-btn-group>.ant-btn-icon-only{font-size:14px}.ant-btn-group-lg>.ant-btn,.ant-btn-group-lg>span>.ant-btn{height:40px;padding:0 15px;font-size:16px;border-radius:0;line-height:38px}.ant-btn-group-lg>.ant-btn.ant-btn-icon-only{width:40px;height:40px;padding-right:0;padding-left:0}.ant-btn-group-sm>.ant-btn,.ant-btn-group-sm>span>.ant-btn{height:24px;padding:0 7px;font-size:14px;border-radius:0;line-height:22px}.ant-btn-group-sm>.ant-btn>.anticon,.ant-btn-group-sm>span>.ant-btn>.anticon{font-size:14px}.ant-btn-group-sm>.ant-btn.ant-btn-icon-only{width:24px;height:24px;padding-right:0;padding-left:0}.ant-btn+.ant-btn-group,.ant-btn-group+.ant-btn,.ant-btn-group+.ant-btn-group,.ant-btn-group .ant-btn+.ant-btn,.ant-btn-group .ant-btn+span,.ant-btn-group>span+span,.ant-btn-group span+.ant-btn{margin-left:-1px}.ant-btn-group .ant-btn-primary+.ant-btn:not(.ant-btn-primary):not([disabled]){border-left-color:transparent}.ant-btn-group .ant-btn{border-radius:0}.ant-btn-group>.ant-btn:first-child,.ant-btn-group>span:first-child>.ant-btn{margin-left:0}.ant-btn-group>.ant-btn:only-child{border-radius:4px}.ant-btn-group>span:only-child>.ant-btn{border-radius:4px}.ant-btn-group>.ant-btn:first-child:not(:last-child),.ant-btn-group>span:first-child:not(:last-child)>.ant-btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.ant-btn-group>.ant-btn:last-child:not(:first-child),.ant-btn-group>span:last-child:not(:first-child)>.ant-btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.ant-btn-group-sm>.ant-btn:only-child{border-radius:4px}.ant-btn-group-sm>span:only-child>.ant-btn{border-radius:4px}.ant-btn-group-sm>.ant-btn:first-child:not(:last-child),.ant-btn-group-sm>span:first-child:not(:last-child)>.ant-btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.ant-btn-group-sm>.ant-btn:last-child:not(:first-child),.ant-btn-group-sm>span:last-child:not(:first-child)>.ant-btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.ant-btn-group>.ant-btn-group{float:left}.ant-btn-group>.ant-btn-group:not(:first-child):not(:last-child)>.ant-btn{border-radius:0}.ant-btn-group>.ant-btn-group:first-child:not(:last-child)>.ant-btn:last-child{padding-right:8px;border-top-right-radius:0;border-bottom-right-radius:0}.ant-btn-group>.ant-btn-group:last-child:not(:first-child)>.ant-btn:first-child{padding-left:8px;border-top-left-radius:0;border-bottom-left-radius:0}.ant-btn:active>span,.ant-btn:focus>span{position:relative}.ant-btn>.anticon+span,.ant-btn>span+.anticon{margin-left:8px}.ant-btn-background-ghost{color:#fff;background:transparent!important;border-color:#fff}.ant-btn-background-ghost.ant-btn-primary{color:#1890ff;background-color:transparent;border-color:#1890ff;text-shadow:none}.ant-btn-background-ghost.ant-btn-primary>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-primary>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-primary:focus,.ant-btn-background-ghost.ant-btn-primary:hover{color:#40a9ff;background-color:transparent;border-color:#40a9ff}.ant-btn-background-ghost.ant-btn-primary:focus>a:only-child,.ant-btn-background-ghost.ant-btn-primary:hover>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-primary:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-primary.active,.ant-btn-background-ghost.ant-btn-primary:active{color:#096dd9;background-color:transparent;border-color:#096dd9}.ant-btn-background-ghost.ant-btn-primary.active>a:only-child,.ant-btn-background-ghost.ant-btn-primary:active>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-primary.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-primary-disabled,.ant-btn-background-ghost.ant-btn-primary-disabled.active,.ant-btn-background-ghost.ant-btn-primary-disabled:active,.ant-btn-background-ghost.ant-btn-primary-disabled:focus,.ant-btn-background-ghost.ant-btn-primary-disabled:hover,.ant-btn-background-ghost.ant-btn-primary.disabled,.ant-btn-background-ghost.ant-btn-primary.disabled.active,.ant-btn-background-ghost.ant-btn-primary.disabled:active,.ant-btn-background-ghost.ant-btn-primary.disabled:focus,.ant-btn-background-ghost.ant-btn-primary.disabled:hover,.ant-btn-background-ghost.ant-btn-primary[disabled],.ant-btn-background-ghost.ant-btn-primary[disabled].active,.ant-btn-background-ghost.ant-btn-primary[disabled]:active,.ant-btn-background-ghost.ant-btn-primary[disabled]:focus,.ant-btn-background-ghost.ant-btn-primary[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-background-ghost.ant-btn-primary-disabled.active>a:only-child,.ant-btn-background-ghost.ant-btn-primary-disabled:active>a:only-child,.ant-btn-background-ghost.ant-btn-primary-disabled:focus>a:only-child,.ant-btn-background-ghost.ant-btn-primary-disabled:hover>a:only-child,.ant-btn-background-ghost.ant-btn-primary-disabled>a:only-child,.ant-btn-background-ghost.ant-btn-primary.disabled.active>a:only-child,.ant-btn-background-ghost.ant-btn-primary.disabled:active>a:only-child,.ant-btn-background-ghost.ant-btn-primary.disabled:focus>a:only-child,.ant-btn-background-ghost.ant-btn-primary.disabled:hover>a:only-child,.ant-btn-background-ghost.ant-btn-primary.disabled>a:only-child,.ant-btn-background-ghost.ant-btn-primary[disabled].active>a:only-child,.ant-btn-background-ghost.ant-btn-primary[disabled]:active>a:only-child,.ant-btn-background-ghost.ant-btn-primary[disabled]:focus>a:only-child,.ant-btn-background-ghost.ant-btn-primary[disabled]:hover>a:only-child,.ant-btn-background-ghost.ant-btn-primary[disabled]>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-primary-disabled.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary-disabled:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary-disabled:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary-disabled:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary-disabled>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary.disabled.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary.disabled:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary.disabled:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary.disabled:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary.disabled>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary[disabled].active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary[disabled]:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary[disabled]:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary[disabled]:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-primary[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-danger{color:#ff4d4f;background-color:transparent;border-color:#ff4d4f;text-shadow:none}.ant-btn-background-ghost.ant-btn-danger>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-danger>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-danger:focus,.ant-btn-background-ghost.ant-btn-danger:hover{color:#ff7875;background-color:transparent;border-color:#ff7875}.ant-btn-background-ghost.ant-btn-danger:focus>a:only-child,.ant-btn-background-ghost.ant-btn-danger:hover>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-danger:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-danger.active,.ant-btn-background-ghost.ant-btn-danger:active{color:#d9363e;background-color:transparent;border-color:#d9363e}.ant-btn-background-ghost.ant-btn-danger.active>a:only-child,.ant-btn-background-ghost.ant-btn-danger:active>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-danger.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-danger-disabled,.ant-btn-background-ghost.ant-btn-danger-disabled.active,.ant-btn-background-ghost.ant-btn-danger-disabled:active,.ant-btn-background-ghost.ant-btn-danger-disabled:focus,.ant-btn-background-ghost.ant-btn-danger-disabled:hover,.ant-btn-background-ghost.ant-btn-danger.disabled,.ant-btn-background-ghost.ant-btn-danger.disabled.active,.ant-btn-background-ghost.ant-btn-danger.disabled:active,.ant-btn-background-ghost.ant-btn-danger.disabled:focus,.ant-btn-background-ghost.ant-btn-danger.disabled:hover,.ant-btn-background-ghost.ant-btn-danger[disabled],.ant-btn-background-ghost.ant-btn-danger[disabled].active,.ant-btn-background-ghost.ant-btn-danger[disabled]:active,.ant-btn-background-ghost.ant-btn-danger[disabled]:focus,.ant-btn-background-ghost.ant-btn-danger[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-background-ghost.ant-btn-danger-disabled.active>a:only-child,.ant-btn-background-ghost.ant-btn-danger-disabled:active>a:only-child,.ant-btn-background-ghost.ant-btn-danger-disabled:focus>a:only-child,.ant-btn-background-ghost.ant-btn-danger-disabled:hover>a:only-child,.ant-btn-background-ghost.ant-btn-danger-disabled>a:only-child,.ant-btn-background-ghost.ant-btn-danger.disabled.active>a:only-child,.ant-btn-background-ghost.ant-btn-danger.disabled:active>a:only-child,.ant-btn-background-ghost.ant-btn-danger.disabled:focus>a:only-child,.ant-btn-background-ghost.ant-btn-danger.disabled:hover>a:only-child,.ant-btn-background-ghost.ant-btn-danger.disabled>a:only-child,.ant-btn-background-ghost.ant-btn-danger[disabled].active>a:only-child,.ant-btn-background-ghost.ant-btn-danger[disabled]:active>a:only-child,.ant-btn-background-ghost.ant-btn-danger[disabled]:focus>a:only-child,.ant-btn-background-ghost.ant-btn-danger[disabled]:hover>a:only-child,.ant-btn-background-ghost.ant-btn-danger[disabled]>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-danger-disabled.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger-disabled:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger-disabled:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger-disabled:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger-disabled>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger.disabled.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger.disabled:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger.disabled:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger.disabled:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger.disabled>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger[disabled].active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger[disabled]:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger[disabled]:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger[disabled]:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-danger[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-link{color:#1890ff;background-color:transparent;border-color:transparent;text-shadow:none;color:#fff}.ant-btn-background-ghost.ant-btn-link>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-link>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-link:focus,.ant-btn-background-ghost.ant-btn-link:hover{color:#40a9ff;background-color:transparent;border-color:transparent}.ant-btn-background-ghost.ant-btn-link:focus>a:only-child,.ant-btn-background-ghost.ant-btn-link:hover>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-link:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-link:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-link.active,.ant-btn-background-ghost.ant-btn-link:active{color:#096dd9;background-color:transparent;border-color:transparent}.ant-btn-background-ghost.ant-btn-link.active>a:only-child,.ant-btn-background-ghost.ant-btn-link:active>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-link.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-background-ghost.ant-btn-link-disabled,.ant-btn-background-ghost.ant-btn-link-disabled.active,.ant-btn-background-ghost.ant-btn-link-disabled:active,.ant-btn-background-ghost.ant-btn-link-disabled:focus,.ant-btn-background-ghost.ant-btn-link-disabled:hover,.ant-btn-background-ghost.ant-btn-link.disabled,.ant-btn-background-ghost.ant-btn-link.disabled.active,.ant-btn-background-ghost.ant-btn-link.disabled:active,.ant-btn-background-ghost.ant-btn-link.disabled:focus,.ant-btn-background-ghost.ant-btn-link.disabled:hover,.ant-btn-background-ghost.ant-btn-link[disabled],.ant-btn-background-ghost.ant-btn-link[disabled].active,.ant-btn-background-ghost.ant-btn-link[disabled]:active,.ant-btn-background-ghost.ant-btn-link[disabled]:focus,.ant-btn-background-ghost.ant-btn-link[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-btn-background-ghost.ant-btn-link-disabled.active>a:only-child,.ant-btn-background-ghost.ant-btn-link-disabled:active>a:only-child,.ant-btn-background-ghost.ant-btn-link-disabled:focus>a:only-child,.ant-btn-background-ghost.ant-btn-link-disabled:hover>a:only-child,.ant-btn-background-ghost.ant-btn-link-disabled>a:only-child,.ant-btn-background-ghost.ant-btn-link.disabled.active>a:only-child,.ant-btn-background-ghost.ant-btn-link.disabled:active>a:only-child,.ant-btn-background-ghost.ant-btn-link.disabled:focus>a:only-child,.ant-btn-background-ghost.ant-btn-link.disabled:hover>a:only-child,.ant-btn-background-ghost.ant-btn-link.disabled>a:only-child,.ant-btn-background-ghost.ant-btn-link[disabled].active>a:only-child,.ant-btn-background-ghost.ant-btn-link[disabled]:active>a:only-child,.ant-btn-background-ghost.ant-btn-link[disabled]:focus>a:only-child,.ant-btn-background-ghost.ant-btn-link[disabled]:hover>a:only-child,.ant-btn-background-ghost.ant-btn-link[disabled]>a:only-child{color:currentColor}.ant-btn-background-ghost.ant-btn-link-disabled.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link-disabled:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link-disabled:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-link-disabled:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-link-disabled>a:only-child:after,.ant-btn-background-ghost.ant-btn-link.disabled.active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link.disabled:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link.disabled:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-link.disabled:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-link.disabled>a:only-child:after,.ant-btn-background-ghost.ant-btn-link[disabled].active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link[disabled]:active>a:only-child:after,.ant-btn-background-ghost.ant-btn-link[disabled]:focus>a:only-child:after,.ant-btn-background-ghost.ant-btn-link[disabled]:hover>a:only-child:after,.ant-btn-background-ghost.ant-btn-link[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-btn-two-chinese-chars:first-letter{letter-spacing:.34em}.ant-btn-two-chinese-chars>:not(.anticon){margin-right:-.34em;letter-spacing:.34em}.ant-btn-block{width:100%}.ant-btn:empty{vertical-align:top}a.ant-btn{padding-top:.1px;line-height:30px}a.ant-btn-lg{line-height:38px}a.ant-btn-sm{line-height:22px}
+.ant-table-wrapper{zoom:1}.ant-table-wrapper:after,.ant-table-wrapper:before{display:table;content:""}.ant-table-wrapper:after{clear:both}.ant-table{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;clear:both}.ant-table-body{transition:opacity .3s}.ant-table-empty .ant-table-body{overflow-x:auto!important;overflow-y:hidden!important}.ant-table table{width:100%;text-align:left;border-radius:4px 4px 0 0;border-collapse:separate;border-spacing:0}.ant-table-layout-fixed table{table-layout:fixed}.ant-table-thead>tr>th{color:rgba(0,0,0,.85);font-weight:500;text-align:left;background:#fafafa;border-bottom:1px solid #e8e8e8;transition:background .3s ease}.ant-table-thead>tr>th[colspan]:not([colspan="1"]){text-align:center}.ant-table-thead>tr>th .ant-table-filter-icon,.ant-table-thead>tr>th .anticon-filter{position:absolute;top:0;right:0;width:28px;height:100%;color:#bfbfbf;font-size:12px;text-align:center;cursor:pointer;transition:all .3s}.ant-table-thead>tr>th .ant-table-filter-icon>svg,.ant-table-thead>tr>th .anticon-filter>svg{position:absolute;top:50%;left:50%;margin-top:-5px;margin-left:-6px}.ant-table-thead>tr>th .ant-table-filter-selected.anticon{color:#1890ff}.ant-table-thead>tr>th .ant-table-column-sorter{display:table-cell;vertical-align:middle}.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner{height:1em;margin-top:.35em;margin-left:.57142857em;color:#bfbfbf;line-height:1em;text-align:center;transition:all .3s}.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner .ant-table-column-sorter-down,.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner .ant-table-column-sorter-up{display:inline-block;font-size:12px;font-size:11px\9;transform:scale(.91666667) rotate(0deg);display:block;height:1em;line-height:1em;transition:all .3s}:root .ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner .ant-table-column-sorter-down,:root .ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner .ant-table-column-sorter-up{font-size:12px}.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner .ant-table-column-sorter-down.on,.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner .ant-table-column-sorter-up.on{color:#1890ff}.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner-full{margin-top:-.15em}.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner-full .ant-table-column-sorter-down,.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner-full .ant-table-column-sorter-up{height:.5em;line-height:.5em}.ant-table-thead>tr>th .ant-table-column-sorter .ant-table-column-sorter-inner-full .ant-table-column-sorter-down{margin-top:.125em}.ant-table-thead>tr>th.ant-table-column-has-actions{position:relative;background-clip:padding-box;-webkit-background-clip:border-box}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters{padding-right:30px!important}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters .ant-table-filter-icon.ant-table-filter-open,.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters .anticon-filter.ant-table-filter-open{color:rgba(0,0,0,.45);background:#e5e5e5}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters:hover .ant-table-filter-icon:hover,.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters:hover .anticon-filter:hover{color:rgba(0,0,0,.45);background:#e5e5e5}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters:hover .ant-table-filter-icon:active,.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-filters:hover .anticon-filter:active{color:rgba(0,0,0,.65)}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters{cursor:pointer}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters:hover{background:#f2f2f2}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters:hover .ant-table-filter-icon,.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters:hover .anticon-filter{background:#f2f2f2}.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters:active .ant-table-column-sorter-down:not(.on),.ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters:active .ant-table-column-sorter-up:not(.on){color:rgba(0,0,0,.45)}.ant-table-thead>tr>th .ant-table-header-column{display:inline-block;max-width:100%;vertical-align:top}.ant-table-thead>tr>th .ant-table-header-column .ant-table-column-sorters{display:table}.ant-table-thead>tr>th .ant-table-header-column .ant-table-column-sorters>.ant-table-column-title{display:table-cell;vertical-align:middle}.ant-table-thead>tr>th .ant-table-header-column .ant-table-column-sorters>:not(.ant-table-column-sorter){position:relative}.ant-table-thead>tr>th .ant-table-header-column .ant-table-column-sorters:before{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;transition:all .3s;content:""}.ant-table-thead>tr>th .ant-table-header-column .ant-table-column-sorters:hover:before{background:rgba(0,0,0,.04)}.ant-table-thead>tr>th.ant-table-column-has-sorters{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-table-thead>tr:first-child>th:first-child{border-top-left-radius:4px}.ant-table-thead>tr:first-child>th:last-child{border-top-right-radius:4px}.ant-table-thead>tr:not(:last-child)>th[colspan]{border-bottom:0}.ant-table-tbody>tr>td{border-bottom:1px solid #e8e8e8;transition:all .3s,border 0s}.ant-table-tbody>tr,.ant-table-thead>tr{transition:all .3s,height 0s}.ant-table-tbody>tr.ant-table-row-hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,.ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,.ant-table-thead>tr.ant-table-row-hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,.ant-table-thead>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td{background:#e6f7ff}.ant-table-tbody>tr.ant-table-row-selected>td.ant-table-column-sort,.ant-table-thead>tr.ant-table-row-selected>td.ant-table-column-sort{background:#fafafa}.ant-table-tbody>tr:hover.ant-table-row-selected>td,.ant-table-thead>tr:hover.ant-table-row-selected>td{background:#fafafa}.ant-table-tbody>tr:hover.ant-table-row-selected>td.ant-table-column-sort,.ant-table-thead>tr:hover.ant-table-row-selected>td.ant-table-column-sort{background:#fafafa}.ant-table-thead>tr:hover{background:none}.ant-table-footer{position:relative;padding:16px;color:rgba(0,0,0,.85);background:#fafafa;border-top:1px solid #e8e8e8;border-radius:0 0 4px 4px}.ant-table-footer:before{position:absolute;top:-1px;left:0;width:100%;height:1px;background:#fafafa;content:""}.ant-table.ant-table-bordered .ant-table-footer{border:1px solid #e8e8e8}.ant-table-title{position:relative;top:1px;padding:16px 0;border-radius:4px 4px 0 0}.ant-table.ant-table-bordered .ant-table-title{padding-right:16px;padding-left:16px;border:1px solid #e8e8e8}.ant-table-title+.ant-table-content{position:relative;border-radius:4px 4px 0 0}.ant-table-bordered .ant-table-title+.ant-table-content,.ant-table-bordered .ant-table-title+.ant-table-content .ant-table-thead>tr:first-child>th,.ant-table-bordered .ant-table-title+.ant-table-content table{border-radius:0}.ant-table-without-column-header .ant-table-title+.ant-table-content,.ant-table-without-column-header table{border-radius:0}.ant-table-without-column-header.ant-table-bordered.ant-table-empty .ant-table-placeholder{border-top:1px solid #e8e8e8;border-radius:4px}.ant-table-tbody>tr.ant-table-row-selected td{color:inherit;background:#fafafa}.ant-table-thead>tr>th.ant-table-column-sort{background:#f5f5f5}.ant-table-tbody>tr>td.ant-table-column-sort{background:rgba(0,0,0,.01)}.ant-table-tbody>tr>td,.ant-table-thead>tr>th{padding:16px;overflow-wrap:break-word}.ant-table-expand-icon-th,.ant-table-row-expand-icon-cell{width:50px;min-width:50px;text-align:center}.ant-table-header{overflow:hidden;background:#fafafa}.ant-table-header table{border-radius:4px 4px 0 0}.ant-table-loading{position:relative}.ant-table-loading .ant-table-body{background:#fff;opacity:.5}.ant-table-loading .ant-table-spin-holder{position:absolute;top:50%;left:50%;height:20px;margin-left:-30px;line-height:20px}.ant-table-loading .ant-table-with-pagination{margin-top:-20px}.ant-table-loading .ant-table-without-pagination{margin-top:10px}.ant-table-bordered .ant-table-body>table,.ant-table-bordered .ant-table-fixed-left table,.ant-table-bordered .ant-table-fixed-right table,.ant-table-bordered .ant-table-header>table{border:1px solid #e8e8e8;border-right:0;border-bottom:0}.ant-table-bordered.ant-table-empty .ant-table-placeholder{border-right:1px solid #e8e8e8;border-left:1px solid #e8e8e8}.ant-table-bordered.ant-table-fixed-header .ant-table-header>table{border-bottom:0}.ant-table-bordered.ant-table-fixed-header .ant-table-body>table{border-top-left-radius:0;border-top-right-radius:0}.ant-table-bordered.ant-table-fixed-header .ant-table-body-inner>table,.ant-table-bordered.ant-table-fixed-header .ant-table-header+.ant-table-body>table{border-top:0}.ant-table-bordered .ant-table-thead>tr:not(:last-child)>th{border-bottom:1px solid #e8e8e8}.ant-table-bordered .ant-table-tbody>tr>td,.ant-table-bordered .ant-table-thead>tr>th{border-right:1px solid #e8e8e8}.ant-table-placeholder{position:relative;z-index:1;margin-top:-1px;padding:16px;color:rgba(0,0,0,.25);font-size:14px;text-align:center;background:#fff;border-top:1px solid #e8e8e8;border-bottom:1px solid #e8e8e8;border-radius:0 0 4px 4px}.ant-table-pagination.ant-pagination{float:right;margin:16px 0}.ant-table-filter-dropdown{position:relative;min-width:96px;margin-left:-8px;background:#fff;border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-table-filter-dropdown .ant-dropdown-menu{max-height:calc(100vh - 130px);overflow-x:hidden;border:0;border-radius:4px 4px 0 0;box-shadow:none}.ant-table-filter-dropdown .ant-dropdown-menu-item>label+span{padding-right:0}.ant-table-filter-dropdown .ant-dropdown-menu-sub{border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-table-filter-dropdown .ant-dropdown-menu .ant-dropdown-submenu-contain-selected .ant-dropdown-menu-submenu-title:after{color:#1890ff;font-weight:700;text-shadow:0 0 2px #bae7ff}.ant-table-filter-dropdown .ant-dropdown-menu-item{overflow:hidden}.ant-table-filter-dropdown>.ant-dropdown-menu>.ant-dropdown-menu-item:last-child,.ant-table-filter-dropdown>.ant-dropdown-menu>.ant-dropdown-menu-submenu:last-child .ant-dropdown-menu-submenu-title{border-radius:0}.ant-table-filter-dropdown-btns{padding:7px 8px;overflow:hidden;border-top:1px solid #e8e8e8}.ant-table-filter-dropdown-link{color:#1890ff}.ant-table-filter-dropdown-link:hover{color:#40a9ff}.ant-table-filter-dropdown-link:active{color:#096dd9}.ant-table-filter-dropdown-link.confirm{float:left}.ant-table-filter-dropdown-link.clear{float:right}.ant-table-selection{white-space:nowrap}.ant-table-selection-select-all-custom{margin-right:4px!important}.ant-table-selection .anticon-down{color:#bfbfbf;transition:all .3s}.ant-table-selection-menu{min-width:96px;margin-top:5px;margin-left:-30px;background:#fff;border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-table-selection-menu .ant-action-down{color:#bfbfbf}.ant-table-selection-down{display:inline-block;padding:0;line-height:1;cursor:pointer}.ant-table-selection-down:hover .anticon-down{color:rgba(0,0,0,.6)}.ant-table-row-expand-icon{color:#1890ff;text-decoration:none;cursor:pointer;transition:color .3s;display:inline-block;width:17px;height:17px;color:inherit;line-height:13px;text-align:center;background:#fff;border:1px solid #e8e8e8;border-radius:2px;outline:none;transition:all .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-table-row-expand-icon:focus,.ant-table-row-expand-icon:hover{color:#40a9ff}.ant-table-row-expand-icon:active{color:#096dd9}.ant-table-row-expand-icon:active,.ant-table-row-expand-icon:focus,.ant-table-row-expand-icon:hover{border-color:currentColor}.ant-table-row-expanded:after{content:"-"}.ant-table-row-collapsed:after{content:"+"}.ant-table-row-spaced{visibility:hidden}.ant-table-row-spaced:after{content:"."}.ant-table-row-cell-ellipsis,.ant-table-row-cell-ellipsis .ant-table-column-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ant-table-row-cell-ellipsis .ant-table-column-title{display:block}.ant-table-row-cell-break-word{word-wrap:break-word;word-break:break-word}tr.ant-table-expanded-row,tr.ant-table-expanded-row:hover{background:#fbfbfb}tr.ant-table-expanded-row td>.ant-table-wrapper{margin:-16px -16px -17px}.ant-table .ant-table-row-indent+.ant-table-row-expand-icon{margin-right:8px}.ant-table-scroll{overflow:auto;overflow-x:hidden}.ant-table-scroll table{min-width:100%}.ant-table-scroll table .ant-table-fixed-columns-in-body:not([colspan]){color:transparent}.ant-table-scroll table .ant-table-fixed-columns-in-body:not([colspan])>*{visibility:hidden}.ant-table-body-inner{height:100%}.ant-table-fixed-header>.ant-table-content>.ant-table-scroll>.ant-table-body{position:relative;background:#fff}.ant-table-fixed-header .ant-table-body-inner{overflow:scroll}.ant-table-fixed-header .ant-table-scroll .ant-table-header{margin-bottom:-20px;padding-bottom:20px;overflow:scroll;opacity:.9999}.ant-table-fixed-header .ant-table-scroll .ant-table-header::-webkit-scrollbar{border:solid #e8e8e8;border-width:0 0 1px}.ant-table-hide-scrollbar{scrollbar-color:transparent transparent;min-width:unset}.ant-table-hide-scrollbar::-webkit-scrollbar{min-width:inherit;background-color:transparent}.ant-table-bordered.ant-table-fixed-header .ant-table-scroll .ant-table-header::-webkit-scrollbar{border:1px solid #e8e8e8;border-left-width:0}.ant-table-bordered.ant-table-fixed-header .ant-table-scroll .ant-table-header.ant-table-hide-scrollbar .ant-table-thead>tr:only-child>th:last-child{border-right-color:transparent}.ant-table-fixed-left,.ant-table-fixed-right{position:absolute;top:0;z-index:1;overflow:hidden;border-radius:0;transition:box-shadow .3s ease}.ant-table-fixed-left table,.ant-table-fixed-right table{width:auto;background:#fff}.ant-table-fixed-header .ant-table-fixed-left .ant-table-body-outer .ant-table-fixed,.ant-table-fixed-header .ant-table-fixed-right .ant-table-body-outer .ant-table-fixed{border-radius:0}.ant-table-fixed-left{left:0;box-shadow:6px 0 6px -4px rgba(0,0,0,.15)}.ant-table-fixed-left .ant-table-header{overflow-y:hidden}.ant-table-fixed-left .ant-table-body-inner{margin-right:-20px;padding-right:20px}.ant-table-fixed-header .ant-table-fixed-left .ant-table-body-inner{padding-right:0}.ant-table-fixed-left,.ant-table-fixed-left table{border-radius:4px 0 0 0}.ant-table-fixed-left .ant-table-thead>tr>th:last-child{border-top-right-radius:0}.ant-table-fixed-right{right:0;box-shadow:-6px 0 6px -4px rgba(0,0,0,.15)}.ant-table-fixed-right,.ant-table-fixed-right table{border-radius:0 4px 0 0}.ant-table-fixed-right .ant-table-expanded-row{color:transparent;pointer-events:none}.ant-table-fixed-right .ant-table-thead>tr>th:first-child{border-top-left-radius:0}.ant-table.ant-table-scroll-position-left .ant-table-fixed-left{box-shadow:none}.ant-table.ant-table-scroll-position-right .ant-table-fixed-right{box-shadow:none}.ant-table colgroup>col.ant-table-selection-col{width:60px}.ant-table-thead>tr>th.ant-table-selection-column-custom .ant-table-selection{margin-right:-15px}.ant-table-tbody>tr>td.ant-table-selection-column,.ant-table-thead>tr>th.ant-table-selection-column{text-align:center}.ant-table-tbody>tr>td.ant-table-selection-column .ant-radio-wrapper,.ant-table-thead>tr>th.ant-table-selection-column .ant-radio-wrapper{margin-right:0}.ant-table-row[class*=ant-table-row-level-0] .ant-table-selection-column>span{display:inline-block}.ant-table-filter-dropdown-submenu .ant-checkbox-wrapper+span,.ant-table-filter-dropdown .ant-checkbox-wrapper+span{padding-left:8px}@supports (-moz-appearance:meterbar){.ant-table-thead>tr>th.ant-table-column-has-actions{background-clip:padding-box}}.ant-table-middle>.ant-table-content>.ant-table-footer,.ant-table-middle>.ant-table-title{padding:12px 8px}.ant-table-middle>.ant-table-content>.ant-table-body>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-body>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-thead>tr>th,.ant-table-middle>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-middle>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-thead>tr>th{padding:12px 8px}.ant-table-middle tr.ant-table-expanded-row td>.ant-table-wrapper{margin:-12px -8px -13px}.ant-table-small{border:1px solid #e8e8e8;border-radius:4px}.ant-table-small>.ant-table-content>.ant-table-footer,.ant-table-small>.ant-table-title{padding:8px}.ant-table-small>.ant-table-title{top:0;border-bottom:1px solid #e8e8e8}.ant-table-small>.ant-table-content>.ant-table-footer{background-color:transparent;border-top:1px solid #e8e8e8}.ant-table-small>.ant-table-content>.ant-table-footer:before{background-color:transparent}.ant-table-small>.ant-table-content>.ant-table-body{margin:0 8px}.ant-table-small>.ant-table-content>.ant-table-body>table,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table,.ant-table-small>.ant-table-content>.ant-table-header>table,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table{border:0}.ant-table-small>.ant-table-content>.ant-table-body>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-body>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-tbody>tr>td,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-thead>tr>th{padding:8px}.ant-table-small>.ant-table-content>.ant-table-body>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-header>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-thead>tr>th,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-thead>tr>th{background-color:transparent}.ant-table-small>.ant-table-content>.ant-table-body>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-header>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-thead>tr,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-thead>tr{border-bottom:1px solid #e8e8e8}.ant-table-small>.ant-table-content>.ant-table-body>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-header>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table>.ant-table-thead>tr>th.ant-table-column-sort,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table>.ant-table-thead>tr>th.ant-table-column-sort{background-color:rgba(0,0,0,.01)}.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-body-outer>.ant-table-body-inner>table,.ant-table-small>.ant-table-content>.ant-table-fixed-left>.ant-table-header>table,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-body-outer>.ant-table-body-inner>table,.ant-table-small>.ant-table-content>.ant-table-fixed-right>.ant-table-header>table,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-body>table,.ant-table-small>.ant-table-content>.ant-table-scroll>.ant-table-header>table{padding:0}.ant-table-small>.ant-table-content .ant-table-header{background-color:transparent;border-radius:4px 4px 0 0}.ant-table-small>.ant-table-content .ant-table-placeholder,.ant-table-small>.ant-table-content .ant-table-row:last-child td{border-bottom:0}.ant-table-small.ant-table-bordered{border-right:0}.ant-table-small.ant-table-bordered .ant-table-title{border:0;border-right:1px solid #e8e8e8;border-bottom:1px solid #e8e8e8}.ant-table-small.ant-table-bordered .ant-table-content{border-right:1px solid #e8e8e8}.ant-table-small.ant-table-bordered .ant-table-footer{border:0;border-top:1px solid #e8e8e8}.ant-table-small.ant-table-bordered .ant-table-footer:before{display:none}.ant-table-small.ant-table-bordered .ant-table-placeholder{border-right:0;border-bottom:0;border-left:0}.ant-table-small.ant-table-bordered .ant-table-tbody>tr>td:last-child,.ant-table-small.ant-table-bordered .ant-table-thead>tr>th.ant-table-row-cell-last{border-right:none}.ant-table-small.ant-table-bordered .ant-table-fixed-left .ant-table-tbody>tr>td:last-child,.ant-table-small.ant-table-bordered .ant-table-fixed-left .ant-table-thead>tr>th:last-child{border-right:1px solid #e8e8e8}.ant-table-small.ant-table-bordered .ant-table-fixed-right{border-right:1px solid #e8e8e8;border-left:1px solid #e8e8e8}.ant-table-small tr.ant-table-expanded-row td>.ant-table-wrapper{margin:-8px -8px -9px}.ant-table-small.ant-table-fixed-header>.ant-table-content>.ant-table-scroll>.ant-table-body{border-radius:0 0 4px 4px}
+.ant-empty{margin:0 8px;font-size:14px;line-height:22px;text-align:center}.ant-empty-image{height:100px;margin-bottom:8px}.ant-empty-image img{height:100%}.ant-empty-image svg{height:100%;margin:auto}.ant-empty-description{margin:0}.ant-empty-footer{margin-top:16px}.ant-empty-normal{margin:32px 0;color:rgba(0,0,0,.25)}.ant-empty-normal .ant-empty-image{height:40px}.ant-empty-small{margin:8px 0;color:rgba(0,0,0,.25)}.ant-empty-small .ant-empty-image{height:35px}
+.ant-radio-group{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";display:inline-block}.ant-radio-wrapper{box-sizing:border-box;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;margin:0 8px 0 0;white-space:nowrap;cursor:pointer}.ant-radio{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;line-height:1;white-space:nowrap;vertical-align:sub;outline:none;cursor:pointer}.ant-radio-input:focus+.ant-radio-inner,.ant-radio-wrapper:hover .ant-radio,.ant-radio:hover .ant-radio-inner{border-color:#1890ff}.ant-radio-input:focus+.ant-radio-inner{box-shadow:0 0 0 3px rgba(24,144,255,.08)}.ant-radio-checked:after{position:absolute;top:0;left:0;width:100%;height:100%;border:1px solid #1890ff;border-radius:50%;visibility:hidden;animation:antRadioEffect .36s ease-in-out;animation-fill-mode:both;content:""}.ant-radio-wrapper:hover .ant-radio:after,.ant-radio:hover:after{visibility:visible}.ant-radio-inner{position:relative;top:0;left:0;display:block;width:16px;height:16px;background-color:#fff;border:1px solid #d9d9d9;border-radius:100px;transition:all .3s}.ant-radio-inner:after{position:absolute;top:3px;left:3px;display:table;width:8px;height:8px;background-color:#1890ff;border-top:0;border-left:0;border-radius:8px;transform:scale(0);opacity:0;transition:all .3s cubic-bezier(.78,.14,.15,.86);content:" "}.ant-radio-input{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;cursor:pointer;opacity:0}.ant-radio-checked .ant-radio-inner{border-color:#1890ff}.ant-radio-checked .ant-radio-inner:after{transform:scale(1);opacity:1;transition:all .3s cubic-bezier(.78,.14,.15,.86)}.ant-radio-disabled .ant-radio-inner{background-color:#f5f5f5;border-color:#d9d9d9!important;cursor:not-allowed}.ant-radio-disabled .ant-radio-inner:after{background-color:rgba(0,0,0,.2)}.ant-radio-disabled .ant-radio-input{cursor:not-allowed}.ant-radio-disabled+span{color:rgba(0,0,0,.25);cursor:not-allowed}span.ant-radio+*{padding-right:8px;padding-left:8px}.ant-radio-button-wrapper{position:relative;display:inline-block;height:32px;margin:0;padding:0 15px;color:rgba(0,0,0,.65);line-height:30px;background:#fff;border:1px solid #d9d9d9;border-top:1.02px solid #d9d9d9;border-left:0;cursor:pointer;transition:color .3s,background .3s,border-color .3s}.ant-radio-button-wrapper a{color:rgba(0,0,0,.65)}.ant-radio-button-wrapper>.ant-radio-button{display:block;width:0;height:0;margin-left:0}.ant-radio-group-large .ant-radio-button-wrapper{height:40px;font-size:16px;line-height:38px}.ant-radio-group-small .ant-radio-button-wrapper{height:24px;padding:0 7px;line-height:22px}.ant-radio-button-wrapper:not(:first-child):before{position:absolute;top:0;left:-1px;display:block;width:1px;height:100%;background-color:#d9d9d9;content:""}.ant-radio-button-wrapper:first-child{border-left:1px solid #d9d9d9;border-radius:4px 0 0 4px}.ant-radio-button-wrapper:last-child{border-radius:0 4px 4px 0}.ant-radio-button-wrapper:first-child:last-child{border-radius:4px}.ant-radio-button-wrapper:hover{position:relative;color:#1890ff}.ant-radio-button-wrapper:focus-within{outline:3px solid rgba(24,144,255,.06)}.ant-radio-button-wrapper .ant-radio-inner,.ant-radio-button-wrapper input[type=checkbox],.ant-radio-button-wrapper input[type=radio]{width:0;height:0;opacity:0;pointer-events:none}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled){z-index:1;color:#1890ff;background:#fff;border-color:#1890ff;box-shadow:-1px 0 0 0 #1890ff}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):before{background-color:#1890ff!important;opacity:.1}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):first-child{border-color:#1890ff;box-shadow:none!important}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover{color:#40a9ff;border-color:#40a9ff;box-shadow:-1px 0 0 0 #40a9ff}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active{color:#096dd9;border-color:#096dd9;box-shadow:-1px 0 0 0 #096dd9}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):focus-within{outline:3px solid rgba(24,144,255,.06)}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled){color:#fff;background:#1890ff;border-color:#1890ff}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover{color:#fff;background:#40a9ff;border-color:#40a9ff}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active{color:#fff;background:#096dd9;border-color:#096dd9}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):focus-within{outline:3px solid rgba(24,144,255,.06)}.ant-radio-button-wrapper-disabled{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;cursor:not-allowed}.ant-radio-button-wrapper-disabled:first-child,.ant-radio-button-wrapper-disabled:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9}.ant-radio-button-wrapper-disabled:first-child{border-left-color:#d9d9d9}.ant-radio-button-wrapper-disabled.ant-radio-button-wrapper-checked{color:#fff;background-color:#e6e6e6;border-color:#d9d9d9;box-shadow:none}@keyframes antRadioEffect{0%{transform:scale(1);opacity:.5}to{transform:scale(1.6);opacity:0}}@supports (-moz-appearance:meterbar) and (background-blend-mode:difference,normal){.ant-radio{vertical-align:text-bottom}}
+@keyframes antCheckboxEffect{0%{transform:scale(1);opacity:.5}to{transform:scale(1.6);opacity:0}}.ant-checkbox{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;top:-.09em;display:inline-block;line-height:1;white-space:nowrap;vertical-align:middle;outline:none;cursor:pointer}.ant-checkbox-input:focus+.ant-checkbox-inner,.ant-checkbox-wrapper:hover .ant-checkbox-inner,.ant-checkbox:hover .ant-checkbox-inner{border-color:#1890ff}.ant-checkbox-checked:after{position:absolute;top:0;left:0;width:100%;height:100%;border:1px solid #1890ff;border-radius:2px;visibility:hidden;animation:antCheckboxEffect .36s ease-in-out;animation-fill-mode:backwards;content:""}.ant-checkbox-wrapper:hover .ant-checkbox:after,.ant-checkbox:hover:after{visibility:visible}.ant-checkbox-inner{position:relative;top:0;left:0;display:block;width:16px;height:16px;background-color:#fff;border:1px solid #d9d9d9;border-radius:2px;border-collapse:separate;transition:all .3s}.ant-checkbox-inner:after{position:absolute;top:50%;left:22%;display:table;width:5.71428571px;height:9.14285714px;border:2px solid #fff;border-top:0;border-left:0;transform:rotate(45deg) scale(0) translate(-50%,-50%);opacity:0;transition:all .1s cubic-bezier(.71,-.46,.88,.6),opacity .1s;content:" "}.ant-checkbox-input{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;width:100%;height:100%;cursor:pointer;opacity:0}.ant-checkbox-checked .ant-checkbox-inner:after{position:absolute;display:table;border:2px solid #fff;border-top:0;border-left:0;transform:rotate(45deg) scale(1) translate(-50%,-50%);opacity:1;transition:all .2s cubic-bezier(.12,.4,.29,1.46) .1s;content:" "}.ant-checkbox-checked .ant-checkbox-inner{background-color:#1890ff;border-color:#1890ff}.ant-checkbox-disabled{cursor:not-allowed}.ant-checkbox-disabled.ant-checkbox-checked .ant-checkbox-inner:after{border-color:rgba(0,0,0,.25);animation-name:none}.ant-checkbox-disabled .ant-checkbox-input{cursor:not-allowed}.ant-checkbox-disabled .ant-checkbox-inner{background-color:#f5f5f5;border-color:#d9d9d9!important}.ant-checkbox-disabled .ant-checkbox-inner:after{border-color:#f5f5f5;border-collapse:separate;animation-name:none}.ant-checkbox-disabled+span{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-checkbox-disabled:hover:after,.ant-checkbox-wrapper:hover .ant-checkbox-disabled:after{visibility:hidden}.ant-checkbox-wrapper{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";display:inline-block;line-height:unset;cursor:pointer}.ant-checkbox-wrapper.ant-checkbox-wrapper-disabled{cursor:not-allowed}.ant-checkbox-wrapper+.ant-checkbox-wrapper{margin-left:8px}.ant-checkbox+span{padding-right:8px;padding-left:8px}.ant-checkbox-group{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";display:inline-block}.ant-checkbox-group-item{display:inline-block;margin-right:8px}.ant-checkbox-group-item:last-child{margin-right:0}.ant-checkbox-group-item+.ant-checkbox-group-item{margin-left:0}.ant-checkbox-indeterminate .ant-checkbox-inner{background-color:#fff;border-color:#d9d9d9}.ant-checkbox-indeterminate .ant-checkbox-inner:after{top:50%;left:50%;width:8px;height:8px;background-color:#1890ff;border:0;transform:translate(-50%,-50%) scale(1);opacity:1;content:" "}.ant-checkbox-indeterminate.ant-checkbox-disabled .ant-checkbox-inner:after{background-color:rgba(0,0,0,.25);border-color:rgba(0,0,0,.25)}
+.ant-dropdown{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:absolute;top:-9999px;left:-9999px;z-index:1050;display:block}.ant-dropdown:before{position:absolute;top:-7px;right:0;bottom:-7px;left:-7px;z-index:-9999;opacity:.0001;content:" "}.ant-dropdown-wrap{position:relative}.ant-dropdown-wrap .ant-btn>.anticon-down{display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg)}:root .ant-dropdown-wrap .ant-btn>.anticon-down{font-size:12px}.ant-dropdown-wrap .anticon-down:before{transition:transform .2s}.ant-dropdown-wrap-open .anticon-down:before{transform:rotate(180deg)}.ant-dropdown-hidden,.ant-dropdown-menu-hidden{display:none}.ant-dropdown-menu{position:relative;margin:0;padding:4px 0;text-align:left;list-style-type:none;background-color:#fff;background-clip:padding-box;border-radius:4px;outline:none;box-shadow:0 2px 8px rgba(0,0,0,.15);-webkit-transform:translateZ(0)}.ant-dropdown-menu-item-group-title{padding:5px 12px;color:rgba(0,0,0,.45);transition:all .3s}.ant-dropdown-menu-submenu-popup{position:absolute;z-index:1050}.ant-dropdown-menu-submenu-popup>.ant-dropdown-menu{transform-origin:0 0}.ant-dropdown-menu-submenu-popup li,.ant-dropdown-menu-submenu-popup ul{list-style:none}.ant-dropdown-menu-submenu-popup ul{margin-right:.3em;margin-left:.3em;padding:0}.ant-dropdown-menu-item,.ant-dropdown-menu-submenu-title{clear:both;margin:0;padding:5px 12px;color:rgba(0,0,0,.65);font-weight:400;font-size:14px;line-height:22px;white-space:nowrap;cursor:pointer;transition:all .3s}.ant-dropdown-menu-item>.anticon:first-child,.ant-dropdown-menu-item>span>.anticon:first-child,.ant-dropdown-menu-submenu-title>.anticon:first-child,.ant-dropdown-menu-submenu-title>span>.anticon:first-child{min-width:12px;margin-right:8px;font-size:12px}.ant-dropdown-menu-item>a,.ant-dropdown-menu-submenu-title>a{display:block;margin:-5px -12px;padding:5px 12px;color:rgba(0,0,0,.65);transition:all .3s}.ant-dropdown-menu-item-selected,.ant-dropdown-menu-item-selected>a,.ant-dropdown-menu-submenu-title-selected,.ant-dropdown-menu-submenu-title-selected>a{color:#1890ff;background-color:#e6f7ff}.ant-dropdown-menu-item:hover,.ant-dropdown-menu-submenu-title:hover{background-color:#e6f7ff}.ant-dropdown-menu-item-disabled,.ant-dropdown-menu-submenu-title-disabled{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-dropdown-menu-item-disabled:hover,.ant-dropdown-menu-submenu-title-disabled:hover{color:rgba(0,0,0,.25);background-color:#fff;cursor:not-allowed}.ant-dropdown-menu-item-divider,.ant-dropdown-menu-submenu-title-divider{height:1px;margin:4px 0;overflow:hidden;line-height:0;background-color:#e8e8e8}.ant-dropdown-menu-item .ant-dropdown-menu-submenu-arrow,.ant-dropdown-menu-submenu-title .ant-dropdown-menu-submenu-arrow{position:absolute;right:8px}.ant-dropdown-menu-item .ant-dropdown-menu-submenu-arrow-icon,.ant-dropdown-menu-submenu-title .ant-dropdown-menu-submenu-arrow-icon{color:rgba(0,0,0,.45);font-style:normal;display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg)}:root .ant-dropdown-menu-item .ant-dropdown-menu-submenu-arrow-icon,:root .ant-dropdown-menu-submenu-title .ant-dropdown-menu-submenu-arrow-icon{font-size:12px}.ant-dropdown-menu-item-group-list{margin:0 8px;padding:0;list-style:none}.ant-dropdown-menu-submenu-title{padding-right:26px}.ant-dropdown-menu-submenu-vertical{position:relative}.ant-dropdown-menu-submenu-vertical>.ant-dropdown-menu{position:absolute;top:0;left:100%;min-width:100%;margin-left:4px;transform-origin:0 0}.ant-dropdown-menu-submenu.ant-dropdown-menu-submenu-disabled .ant-dropdown-menu-submenu-title,.ant-dropdown-menu-submenu.ant-dropdown-menu-submenu-disabled .ant-dropdown-menu-submenu-title .ant-dropdown-menu-submenu-arrow-icon{color:rgba(0,0,0,.25);background-color:#fff;cursor:not-allowed}.ant-dropdown-menu-submenu-selected .ant-dropdown-menu-submenu-title{color:#1890ff}.ant-dropdown.slide-down-appear.slide-down-appear-active.ant-dropdown-placement-bottomCenter,.ant-dropdown.slide-down-appear.slide-down-appear-active.ant-dropdown-placement-bottomLeft,.ant-dropdown.slide-down-appear.slide-down-appear-active.ant-dropdown-placement-bottomRight,.ant-dropdown.slide-down-enter.slide-down-enter-active.ant-dropdown-placement-bottomCenter,.ant-dropdown.slide-down-enter.slide-down-enter-active.ant-dropdown-placement-bottomLeft,.ant-dropdown.slide-down-enter.slide-down-enter-active.ant-dropdown-placement-bottomRight{animation-name:antSlideUpIn}.ant-dropdown.slide-up-appear.slide-up-appear-active.ant-dropdown-placement-topCenter,.ant-dropdown.slide-up-appear.slide-up-appear-active.ant-dropdown-placement-topLeft,.ant-dropdown.slide-up-appear.slide-up-appear-active.ant-dropdown-placement-topRight,.ant-dropdown.slide-up-enter.slide-up-enter-active.ant-dropdown-placement-topCenter,.ant-dropdown.slide-up-enter.slide-up-enter-active.ant-dropdown-placement-topLeft,.ant-dropdown.slide-up-enter.slide-up-enter-active.ant-dropdown-placement-topRight{animation-name:antSlideDownIn}.ant-dropdown.slide-down-leave.slide-down-leave-active.ant-dropdown-placement-bottomCenter,.ant-dropdown.slide-down-leave.slide-down-leave-active.ant-dropdown-placement-bottomLeft,.ant-dropdown.slide-down-leave.slide-down-leave-active.ant-dropdown-placement-bottomRight{animation-name:antSlideUpOut}.ant-dropdown.slide-up-leave.slide-up-leave-active.ant-dropdown-placement-topCenter,.ant-dropdown.slide-up-leave.slide-up-leave-active.ant-dropdown-placement-topLeft,.ant-dropdown.slide-up-leave.slide-up-leave-active.ant-dropdown-placement-topRight{animation-name:antSlideDownOut}.ant-dropdown-link>.anticon.anticon-down,.ant-dropdown-trigger>.anticon.anticon-down{display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg)}:root .ant-dropdown-link>.anticon.anticon-down,:root .ant-dropdown-trigger>.anticon.anticon-down{font-size:12px}.ant-dropdown-button{white-space:nowrap}.ant-dropdown-button.ant-btn-group>.ant-btn:last-child:not(:first-child){padding-right:8px;padding-left:8px}.ant-dropdown-button .anticon.anticon-down{display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg)}:root .ant-dropdown-button .anticon.anticon-down{font-size:12px}.ant-dropdown-menu-dark,.ant-dropdown-menu-dark .ant-dropdown-menu{background:#001529}.ant-dropdown-menu-dark .ant-dropdown-menu-item,.ant-dropdown-menu-dark .ant-dropdown-menu-item>a,.ant-dropdown-menu-dark .ant-dropdown-menu-submenu-title{color:hsla(0,0%,100%,.65)}.ant-dropdown-menu-dark .ant-dropdown-menu-item .ant-dropdown-menu-submenu-arrow:after,.ant-dropdown-menu-dark .ant-dropdown-menu-item>a .ant-dropdown-menu-submenu-arrow:after,.ant-dropdown-menu-dark .ant-dropdown-menu-submenu-title .ant-dropdown-menu-submenu-arrow:after{color:hsla(0,0%,100%,.65)}.ant-dropdown-menu-dark .ant-dropdown-menu-item:hover,.ant-dropdown-menu-dark .ant-dropdown-menu-item>a:hover,.ant-dropdown-menu-dark .ant-dropdown-menu-submenu-title:hover{color:#fff;background:transparent}.ant-dropdown-menu-dark .ant-dropdown-menu-item-selected,.ant-dropdown-menu-dark .ant-dropdown-menu-item-selected:hover,.ant-dropdown-menu-dark .ant-dropdown-menu-item-selected>a{color:#fff;background:#1890ff}
+.ant-spin{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:absolute;display:none;color:#1890ff;text-align:center;vertical-align:middle;opacity:0;transition:transform .3s cubic-bezier(.78,.14,.15,.86)}.ant-spin-spinning{position:static;display:inline-block;opacity:1}.ant-spin-nested-loading{position:relative}.ant-spin-nested-loading>div>.ant-spin{position:absolute;top:0;left:0;z-index:4;display:block;width:100%;height:100%;max-height:400px}.ant-spin-nested-loading>div>.ant-spin .ant-spin-dot{position:absolute;top:50%;left:50%;margin:-10px}.ant-spin-nested-loading>div>.ant-spin .ant-spin-text{position:absolute;top:50%;width:100%;padding-top:5px;text-shadow:0 1px 2px #fff}.ant-spin-nested-loading>div>.ant-spin.ant-spin-show-text .ant-spin-dot{margin-top:-20px}.ant-spin-nested-loading>div>.ant-spin-sm .ant-spin-dot{margin:-7px}.ant-spin-nested-loading>div>.ant-spin-sm .ant-spin-text{padding-top:2px}.ant-spin-nested-loading>div>.ant-spin-sm.ant-spin-show-text .ant-spin-dot{margin-top:-17px}.ant-spin-nested-loading>div>.ant-spin-lg .ant-spin-dot{margin:-16px}.ant-spin-nested-loading>div>.ant-spin-lg .ant-spin-text{padding-top:11px}.ant-spin-nested-loading>div>.ant-spin-lg.ant-spin-show-text .ant-spin-dot{margin-top:-26px}.ant-spin-container{position:relative;transition:opacity .3s}.ant-spin-container:after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:none\9;width:100%;height:100%;background:#fff;opacity:0;transition:all .3s;content:"";pointer-events:none}.ant-spin-blur{clear:both;overflow:hidden;opacity:.5;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}.ant-spin-blur:after{opacity:.4;pointer-events:auto}.ant-spin-tip{color:rgba(0,0,0,.45)}.ant-spin-dot{position:relative;display:inline-block;font-size:20px;width:1em;height:1em}.ant-spin-dot-item{position:absolute;display:block;width:9px;height:9px;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s linear infinite alternate}.ant-spin-dot-item:first-child{top:0;left:0}.ant-spin-dot-item:nth-child(2){top:0;right:0;animation-delay:.4s}.ant-spin-dot-item:nth-child(3){right:0;bottom:0;animation-delay:.8s}.ant-spin-dot-item:nth-child(4){bottom:0;left:0;animation-delay:1.2s}.ant-spin-dot-spin{transform:rotate(45deg);animation:antRotate 1.2s linear infinite}.ant-spin-sm .ant-spin-dot{font-size:14px}.ant-spin-sm .ant-spin-dot i{width:6px;height:6px}.ant-spin-lg .ant-spin-dot{font-size:32px}.ant-spin-lg .ant-spin-dot i{width:14px;height:14px}.ant-spin.ant-spin-show-text .ant-spin-text{display:block}@media (-ms-high-contrast:active),(-ms-high-contrast:none){.ant-spin-blur{background:#fff;opacity:.5}}@keyframes antSpinMove{to{opacity:1}}@keyframes antRotate{to{transform:rotate(405deg)}}
+.ant-pagination{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum"}.ant-pagination ol,.ant-pagination ul{margin:0;padding:0;list-style:none}.ant-pagination:after{display:block;clear:both;height:0;overflow:hidden;visibility:hidden;content:" "}.ant-pagination-total-text{display:inline-block;height:32px;margin-right:8px;line-height:30px;vertical-align:middle}.ant-pagination-item{display:inline-block;min-width:32px;height:32px;margin-right:8px;font-family:Arial;line-height:30px;text-align:center;vertical-align:middle;list-style:none;background-color:#fff;border:1px solid #d9d9d9;border-radius:4px;outline:0;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-pagination-item a{display:block;padding:0 6px;color:rgba(0,0,0,.65);transition:none}.ant-pagination-item a:hover{text-decoration:none}.ant-pagination-item:focus,.ant-pagination-item:hover{border-color:#1890ff;transition:all .3s}.ant-pagination-item:focus a,.ant-pagination-item:hover a{color:#1890ff}.ant-pagination-item-active{font-weight:500;background:#fff;border-color:#1890ff}.ant-pagination-item-active a{color:#1890ff}.ant-pagination-item-active:focus,.ant-pagination-item-active:hover{border-color:#40a9ff}.ant-pagination-item-active:focus a,.ant-pagination-item-active:hover a{color:#40a9ff}.ant-pagination-jump-next,.ant-pagination-jump-prev{outline:0}.ant-pagination-jump-next .ant-pagination-item-container,.ant-pagination-jump-prev .ant-pagination-item-container{position:relative}.ant-pagination-jump-next .ant-pagination-item-container .ant-pagination-item-link-icon,.ant-pagination-jump-prev .ant-pagination-item-container .ant-pagination-item-link-icon{display:inline-block;font-size:12px;font-size:12px\9;transform:scale(1) rotate(0deg);color:#1890ff;letter-spacing:-1px;opacity:0;transition:all .2s}:root .ant-pagination-jump-next .ant-pagination-item-container .ant-pagination-item-link-icon,:root .ant-pagination-jump-prev .ant-pagination-item-container .ant-pagination-item-link-icon{font-size:12px}.ant-pagination-jump-next .ant-pagination-item-container .ant-pagination-item-link-icon-svg,.ant-pagination-jump-prev .ant-pagination-item-container .ant-pagination-item-link-icon-svg{top:0;right:0;bottom:0;left:0;margin:auto}.ant-pagination-jump-next .ant-pagination-item-container .ant-pagination-item-ellipsis,.ant-pagination-jump-prev .ant-pagination-item-container .ant-pagination-item-ellipsis{position:absolute;top:0;right:0;bottom:0;left:0;display:block;margin:auto;color:rgba(0,0,0,.25);letter-spacing:2px;text-align:center;text-indent:.13em;opacity:1;transition:all .2s}.ant-pagination-jump-next:focus .ant-pagination-item-link-icon,.ant-pagination-jump-next:hover .ant-pagination-item-link-icon,.ant-pagination-jump-prev:focus .ant-pagination-item-link-icon,.ant-pagination-jump-prev:hover .ant-pagination-item-link-icon{opacity:1}.ant-pagination-jump-next:focus .ant-pagination-item-ellipsis,.ant-pagination-jump-next:hover .ant-pagination-item-ellipsis,.ant-pagination-jump-prev:focus .ant-pagination-item-ellipsis,.ant-pagination-jump-prev:hover .ant-pagination-item-ellipsis{opacity:0}.ant-pagination-jump-next,.ant-pagination-jump-prev,.ant-pagination-prev{margin-right:8px}.ant-pagination-jump-next,.ant-pagination-jump-prev,.ant-pagination-next,.ant-pagination-prev{display:inline-block;min-width:32px;height:32px;color:rgba(0,0,0,.65);font-family:Arial;line-height:32px;text-align:center;vertical-align:middle;list-style:none;border-radius:4px;cursor:pointer;transition:all .3s}.ant-pagination-next,.ant-pagination-prev{outline:0}.ant-pagination-next a,.ant-pagination-prev a{color:rgba(0,0,0,.65);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-pagination-next:hover a,.ant-pagination-prev:hover a{border-color:#40a9ff}.ant-pagination-next .ant-pagination-item-link,.ant-pagination-prev .ant-pagination-item-link{display:block;height:100%;font-size:12px;text-align:center;background-color:#fff;border:1px solid #d9d9d9;border-radius:4px;outline:none;transition:all .3s}.ant-pagination-next:focus .ant-pagination-item-link,.ant-pagination-next:hover .ant-pagination-item-link,.ant-pagination-prev:focus .ant-pagination-item-link,.ant-pagination-prev:hover .ant-pagination-item-link{color:#1890ff;border-color:#1890ff}.ant-pagination-disabled,.ant-pagination-disabled:focus,.ant-pagination-disabled:hover{cursor:not-allowed}.ant-pagination-disabled .ant-pagination-item-link,.ant-pagination-disabled:focus .ant-pagination-item-link,.ant-pagination-disabled:focus a,.ant-pagination-disabled:hover .ant-pagination-item-link,.ant-pagination-disabled:hover a,.ant-pagination-disabled a{color:rgba(0,0,0,.25);border-color:#d9d9d9;cursor:not-allowed}.ant-pagination-slash{margin:0 10px 0 5px}.ant-pagination-options{display:inline-block;margin-left:16px;vertical-align:middle}.ant-pagination-options-size-changer.ant-select{display:inline-block;width:auto;margin-right:8px}.ant-pagination-options-quick-jumper{display:inline-block;height:32px;line-height:32px;vertical-align:top}.ant-pagination-options-quick-jumper input{position:relative;display:inline-block;width:100%;height:32px;padding:4px 11px;color:rgba(0,0,0,.65);font-size:14px;line-height:1.5;background-color:#fff;background-image:none;border:1px solid #d9d9d9;border-radius:4px;transition:all .3s;width:50px;margin:0 8px}.ant-pagination-options-quick-jumper input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-pagination-options-quick-jumper input:-ms-input-placeholder{color:#bfbfbf}.ant-pagination-options-quick-jumper input::-webkit-input-placeholder{color:#bfbfbf}.ant-pagination-options-quick-jumper input:placeholder-shown{text-overflow:ellipsis}.ant-pagination-options-quick-jumper input:hover{border-color:#40a9ff;border-right-width:1px!important}.ant-pagination-options-quick-jumper input:focus{border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-pagination-options-quick-jumper input-disabled{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-pagination-options-quick-jumper input-disabled:hover{border-color:#d9d9d9;border-right-width:1px!important}.ant-pagination-options-quick-jumper input[disabled]{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-pagination-options-quick-jumper input[disabled]:hover{border-color:#d9d9d9;border-right-width:1px!important}textarea.ant-pagination-options-quick-jumper input{max-width:100%;height:auto;min-height:32px;line-height:1.5;vertical-align:bottom;transition:all .3s,height 0s}.ant-pagination-options-quick-jumper input-lg{height:40px;padding:6px 11px;font-size:16px}.ant-pagination-options-quick-jumper input-sm{height:24px;padding:1px 7px}.ant-pagination-simple .ant-pagination-next,.ant-pagination-simple .ant-pagination-prev{height:24px;line-height:24px;vertical-align:top}.ant-pagination-simple .ant-pagination-next .ant-pagination-item-link,.ant-pagination-simple .ant-pagination-prev .ant-pagination-item-link{height:24px;border:0}.ant-pagination-simple .ant-pagination-next .ant-pagination-item-link:after,.ant-pagination-simple .ant-pagination-prev .ant-pagination-item-link:after{height:24px;line-height:24px}.ant-pagination-simple .ant-pagination-simple-pager{display:inline-block;height:24px;margin-right:8px}.ant-pagination-simple .ant-pagination-simple-pager input{box-sizing:border-box;height:100%;margin-right:8px;padding:0 6px;text-align:center;background-color:#fff;border:1px solid #d9d9d9;border-radius:4px;outline:none;transition:border-color .3s}.ant-pagination-simple .ant-pagination-simple-pager input:hover{border-color:#1890ff}.ant-pagination.mini .ant-pagination-simple-pager,.ant-pagination.mini .ant-pagination-total-text{height:24px;line-height:24px}.ant-pagination.mini .ant-pagination-item{min-width:24px;height:24px;margin:0;line-height:22px}.ant-pagination.mini .ant-pagination-item:not(.ant-pagination-item-active){background:transparent;border-color:transparent}.ant-pagination.mini .ant-pagination-next,.ant-pagination.mini .ant-pagination-prev{min-width:24px;height:24px;margin:0;line-height:24px}.ant-pagination.mini .ant-pagination-next .ant-pagination-item-link,.ant-pagination.mini .ant-pagination-prev .ant-pagination-item-link{background:transparent;border-color:transparent}.ant-pagination.mini .ant-pagination-next .ant-pagination-item-link:after,.ant-pagination.mini .ant-pagination-prev .ant-pagination-item-link:after{height:24px;line-height:24px}.ant-pagination.mini .ant-pagination-jump-next,.ant-pagination.mini .ant-pagination-jump-prev{height:24px;margin-right:0;line-height:24px}.ant-pagination.mini .ant-pagination-options{margin-left:2px}.ant-pagination.mini .ant-pagination-options-quick-jumper{height:24px;line-height:24px}.ant-pagination.mini .ant-pagination-options-quick-jumper input{height:24px;padding:1px 7px;width:44px}.ant-pagination.ant-pagination-disabled{cursor:not-allowed}.ant-pagination.ant-pagination-disabled .ant-pagination-item{background:#f5f5f5;border-color:#d9d9d9;cursor:not-allowed}.ant-pagination.ant-pagination-disabled .ant-pagination-item a{color:rgba(0,0,0,.25);background:transparent;border:none;cursor:not-allowed}.ant-pagination.ant-pagination-disabled .ant-pagination-item-active{background:#dbdbdb;border-color:transparent}.ant-pagination.ant-pagination-disabled .ant-pagination-item-active a{color:#fff}.ant-pagination.ant-pagination-disabled .ant-pagination-item-link,.ant-pagination.ant-pagination-disabled .ant-pagination-item-link:focus,.ant-pagination.ant-pagination-disabled .ant-pagination-item-link:hover{color:rgba(0,0,0,.45);background:#f5f5f5;border-color:#d9d9d9;cursor:not-allowed}.ant-pagination.ant-pagination-disabled .ant-pagination-jump-next:focus .ant-pagination-item-link-icon,.ant-pagination.ant-pagination-disabled .ant-pagination-jump-next:hover .ant-pagination-item-link-icon,.ant-pagination.ant-pagination-disabled .ant-pagination-jump-prev:focus .ant-pagination-item-link-icon,.ant-pagination.ant-pagination-disabled .ant-pagination-jump-prev:hover .ant-pagination-item-link-icon{opacity:0}.ant-pagination.ant-pagination-disabled .ant-pagination-jump-next:focus .ant-pagination-item-ellipsis,.ant-pagination.ant-pagination-disabled .ant-pagination-jump-next:hover .ant-pagination-item-ellipsis,.ant-pagination.ant-pagination-disabled .ant-pagination-jump-prev:focus .ant-pagination-item-ellipsis,.ant-pagination.ant-pagination-disabled .ant-pagination-jump-prev:hover .ant-pagination-item-ellipsis{opacity:1}@media only screen and (max-width:992px){.ant-pagination-item-after-jump-prev,.ant-pagination-item-before-jump-next{display:none}}@media only screen and (max-width:576px){.ant-pagination-options{display:none}}
+.ant-select{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;outline:0}.ant-select ol,.ant-select ul{margin:0;padding:0;list-style:none}.ant-select>ul>li>a{padding:0;background-color:#fff}.ant-select-arrow{display:inline-block;color:inherit;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;position:absolute;top:50%;right:11px;margin-top:-6px;color:rgba(0,0,0,.25);font-size:12px;line-height:1;transform-origin:50% 50%}.ant-select-arrow>*{line-height:1}.ant-select-arrow svg{display:inline-block}.ant-select-arrow:before{display:none}.ant-select-arrow .ant-select-arrow-icon{display:block}.ant-select-arrow .ant-select-arrow-icon svg{transition:transform .3s}.ant-select-selection{display:block;box-sizing:border-box;background-color:#fff;border:1px solid #d9d9d9;border-top:1.02px solid #d9d9d9;border-radius:4px;outline:none;transition:all .3s cubic-bezier(.645,.045,.355,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-select-selection:hover{border-color:#40a9ff;border-right-width:1px!important}.ant-select-focused .ant-select-selection,.ant-select-selection:active,.ant-select-selection:focus{border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-select-selection__clear{position:absolute;top:50%;right:11px;z-index:1;display:inline-block;width:12px;height:12px;margin-top:-6px;color:rgba(0,0,0,.25);font-size:12px;font-style:normal;line-height:12px;text-align:center;text-transform:none;background:#fff;cursor:pointer;opacity:0;transition:color .3s ease,opacity .15s ease;text-rendering:auto}.ant-select-selection__clear:before{display:block}.ant-select-selection__clear:hover{color:rgba(0,0,0,.45)}.ant-select-selection:hover .ant-select-selection__clear{opacity:1}.ant-select-selection-selected-value{float:left;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ant-select-no-arrow .ant-select-selection-selected-value{padding-right:0}.ant-select-disabled{color:rgba(0,0,0,.25)}.ant-select-disabled .ant-select-selection{background:#f5f5f5;cursor:not-allowed}.ant-select-disabled .ant-select-selection:active,.ant-select-disabled .ant-select-selection:focus,.ant-select-disabled .ant-select-selection:hover{border-color:#d9d9d9;box-shadow:none}.ant-select-disabled .ant-select-selection__clear{display:none;visibility:hidden;pointer-events:none}.ant-select-disabled .ant-select-selection--multiple .ant-select-selection__choice{padding-right:10px;color:rgba(0,0,0,.33);background:#f5f5f5}.ant-select-disabled .ant-select-selection--multiple .ant-select-selection__choice__remove{display:none}.ant-select-selection--single{position:relative;height:32px;cursor:pointer}.ant-select-selection--single .ant-select-selection__rendered{margin-right:24px}.ant-select-no-arrow .ant-select-selection__rendered{margin-right:11px}.ant-select-selection__rendered{position:relative;display:block;margin-right:11px;margin-left:11px;line-height:30px}.ant-select-selection__rendered:after{display:inline-block;width:0;visibility:hidden;content:".";pointer-events:none}.ant-select-lg{font-size:16px}.ant-select-lg .ant-select-selection--single{height:40px}.ant-select-lg .ant-select-selection__rendered{line-height:38px}.ant-select-lg .ant-select-selection--multiple{min-height:40px}.ant-select-lg .ant-select-selection--multiple .ant-select-selection__rendered li{height:32px;line-height:32px}.ant-select-lg .ant-select-selection--multiple .ant-select-arrow,.ant-select-lg .ant-select-selection--multiple .ant-select-selection__clear{top:20px}.ant-select-sm .ant-select-selection--single{height:24px}.ant-select-sm .ant-select-selection__rendered{margin-left:7px;line-height:22px}.ant-select-sm .ant-select-selection--multiple{min-height:24px}.ant-select-sm .ant-select-selection--multiple .ant-select-selection__rendered li{height:16px;line-height:14px}.ant-select-sm .ant-select-selection--multiple .ant-select-arrow,.ant-select-sm .ant-select-selection--multiple .ant-select-selection__clear{top:12px}.ant-select-sm .ant-select-arrow,.ant-select-sm .ant-select-selection__clear{right:8px}.ant-select-disabled .ant-select-selection__choice__remove{color:rgba(0,0,0,.25);cursor:default}.ant-select-disabled .ant-select-selection__choice__remove:hover{color:rgba(0,0,0,.25)}.ant-select-search__field__wrap{position:relative;display:inline-block}.ant-select-search__field__placeholder,.ant-select-selection__placeholder{position:absolute;top:50%;right:9px;left:0;max-width:100%;height:20px;margin-top:-10px;overflow:hidden;color:#bfbfbf;line-height:20px;white-space:nowrap;text-align:left;text-overflow:ellipsis}.ant-select-search__field__placeholder{left:12px}.ant-select-search__field__mirror{position:absolute;top:0;left:0;white-space:pre;opacity:0;pointer-events:none}.ant-select-search--inline{position:absolute;width:100%;height:100%}.ant-select-search--inline .ant-select-search__field__wrap{width:100%;height:100%}.ant-select-search--inline .ant-select-search__field{width:100%;height:100%;font-size:100%;line-height:1;background:transparent;border-width:0;border-radius:4px;outline:0}.ant-select-search--inline>i{float:right}.ant-select-selection--multiple{min-height:32px;padding-bottom:3px;cursor:text;zoom:1}.ant-select-selection--multiple:after,.ant-select-selection--multiple:before{display:table;content:""}.ant-select-selection--multiple:after{clear:both}.ant-select-selection--multiple .ant-select-search--inline{position:static;float:left;width:auto;max-width:100%;padding:0}.ant-select-selection--multiple .ant-select-search--inline .ant-select-search__field{width:.75em;max-width:100%;padding:1px}.ant-select-selection--multiple .ant-select-selection__rendered{height:auto;margin-bottom:-3px;margin-left:5px}.ant-select-selection--multiple .ant-select-selection__placeholder{margin-left:6px}.ant-select-selection--multiple .ant-select-selection__rendered>ul>li,.ant-select-selection--multiple>ul>li{height:24px;margin-top:3px;line-height:22px}.ant-select-selection--multiple .ant-select-selection__choice{position:relative;float:left;max-width:99%;margin-right:4px;padding:0 20px 0 10px;overflow:hidden;color:rgba(0,0,0,.65);background-color:#fafafa;border:1px solid #e8e8e8;border-radius:2px;cursor:default;transition:padding .3s cubic-bezier(.645,.045,.355,1)}.ant-select-selection--multiple .ant-select-selection__choice__disabled{padding:0 10px}.ant-select-selection--multiple .ant-select-selection__choice__content{display:inline-block;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;transition:margin .3s cubic-bezier(.645,.045,.355,1)}.ant-select-selection--multiple .ant-select-selection__choice__remove{color:inherit;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;position:absolute;right:4px;color:rgba(0,0,0,.45);font-weight:700;line-height:inherit;cursor:pointer;transition:all .3s;display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg)}.ant-select-selection--multiple .ant-select-selection__choice__remove>*{line-height:1}.ant-select-selection--multiple .ant-select-selection__choice__remove svg{display:inline-block}.ant-select-selection--multiple .ant-select-selection__choice__remove:before{display:none}.ant-select-selection--multiple .ant-select-selection__choice__remove .ant-select-selection--multiple .ant-select-selection__choice__remove-icon{display:block}:root .ant-select-selection--multiple .ant-select-selection__choice__remove{font-size:12px}.ant-select-selection--multiple .ant-select-selection__choice__remove:hover{color:rgba(0,0,0,.75)}.ant-select-selection--multiple .ant-select-arrow,.ant-select-selection--multiple .ant-select-selection__clear{top:16px}.ant-select-allow-clear .ant-select-selection--multiple .ant-select-selection__rendered,.ant-select-show-arrow .ant-select-selection--multiple .ant-select-selection__rendered{margin-right:20px}.ant-select-open .ant-select-arrow-icon svg{transform:rotate(180deg)}.ant-select-open .ant-select-selection{border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-select-combobox .ant-select-arrow{display:none}.ant-select-combobox .ant-select-search--inline{float:none;width:100%;height:100%}.ant-select-combobox .ant-select-search__field__wrap{width:100%;height:100%}.ant-select-combobox .ant-select-search__field{position:relative;z-index:1;width:100%;height:100%;box-shadow:none;transition:all .3s cubic-bezier(.645,.045,.355,1),height 0s}.ant-select-combobox.ant-select-allow-clear .ant-select-selection:hover .ant-select-selection__rendered,.ant-select-combobox.ant-select-show-arrow .ant-select-selection:hover .ant-select-selection__rendered{margin-right:20px}.ant-select-dropdown{margin:0;padding:0;color:rgba(0,0,0,.65);font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:absolute;top:-9999px;left:-9999px;z-index:1050;box-sizing:border-box;font-size:14px;font-variant:normal;background-color:#fff;border-radius:4px;outline:none;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-select-dropdown.slide-up-appear.slide-up-appear-active.ant-select-dropdown-placement-bottomLeft,.ant-select-dropdown.slide-up-enter.slide-up-enter-active.ant-select-dropdown-placement-bottomLeft{animation-name:antSlideUpIn}.ant-select-dropdown.slide-up-appear.slide-up-appear-active.ant-select-dropdown-placement-topLeft,.ant-select-dropdown.slide-up-enter.slide-up-enter-active.ant-select-dropdown-placement-topLeft{animation-name:antSlideDownIn}.ant-select-dropdown.slide-up-leave.slide-up-leave-active.ant-select-dropdown-placement-bottomLeft{animation-name:antSlideUpOut}.ant-select-dropdown.slide-up-leave.slide-up-leave-active.ant-select-dropdown-placement-topLeft{animation-name:antSlideDownOut}.ant-select-dropdown-hidden{display:none}.ant-select-dropdown-menu{max-height:250px;margin-bottom:0;padding:4px 0;overflow:auto;list-style:none;outline:none}.ant-select-dropdown-menu-item-group-list{margin:0;padding:0}.ant-select-dropdown-menu-item-group-list>.ant-select-dropdown-menu-item{padding-left:20px}.ant-select-dropdown-menu-item-group-title{height:32px;padding:0 12px;color:rgba(0,0,0,.45);font-size:12px;line-height:32px}.ant-select-dropdown-menu-item-group-list .ant-select-dropdown-menu-item:first-child:not(:last-child),.ant-select-dropdown-menu-item-group:not(:last-child) .ant-select-dropdown-menu-item-group-list .ant-select-dropdown-menu-item:last-child{border-radius:0}.ant-select-dropdown-menu-item{position:relative;display:block;padding:5px 12px;overflow:hidden;color:rgba(0,0,0,.65);font-weight:400;font-size:14px;line-height:22px;white-space:nowrap;text-overflow:ellipsis;cursor:pointer;transition:background .3s ease}.ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled){background-color:#e6f7ff}.ant-select-dropdown-menu-item-selected{color:rgba(0,0,0,.65);font-weight:600;background-color:#fafafa}.ant-select-dropdown-menu-item-disabled{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-select-dropdown-menu-item-disabled:hover{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled){background-color:#e6f7ff}.ant-select-dropdown-menu-item-divider{height:1px;margin:1px 0;overflow:hidden;line-height:0;background-color:#e8e8e8}.ant-select-dropdown.ant-select-dropdown--multiple .ant-select-dropdown-menu-item{padding-right:32px}.ant-select-dropdown.ant-select-dropdown--multiple .ant-select-dropdown-menu-item .ant-select-selected-icon{position:absolute;top:50%;right:12px;color:transparent;font-weight:700;font-size:12px;text-shadow:0 .1px 0,.1px 0 0,0 -.1px 0,-.1px 0;transform:translateY(-50%);transition:all .2s}.ant-select-dropdown.ant-select-dropdown--multiple .ant-select-dropdown-menu-item:hover .ant-select-selected-icon{color:rgba(0,0,0,.87)}.ant-select-dropdown.ant-select-dropdown--multiple .ant-select-dropdown-menu-item-disabled .ant-select-selected-icon{display:none}.ant-select-dropdown.ant-select-dropdown--multiple .ant-select-dropdown-menu-item-selected .ant-select-selected-icon,.ant-select-dropdown.ant-select-dropdown--multiple .ant-select-dropdown-menu-item-selected:hover .ant-select-selected-icon{display:inline-block;color:#1890ff}.ant-select-dropdown--empty.ant-select-dropdown--multiple .ant-select-dropdown-menu-item{padding-right:12px}.ant-select-dropdown-container-open .ant-select-dropdown,.ant-select-dropdown-open .ant-select-dropdown{display:block}
+.ant-divider{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";background:#e8e8e8}.ant-divider,.ant-divider-vertical{position:relative;top:-.06em;display:inline-block;width:1px;height:.9em;margin:0 8px;vertical-align:middle}.ant-divider-horizontal{display:block;clear:both;width:100%;min-width:100%;height:1px;margin:24px 0}.ant-divider-horizontal.ant-divider-with-text-center,.ant-divider-horizontal.ant-divider-with-text-left,.ant-divider-horizontal.ant-divider-with-text-right{display:table;margin:16px 0;color:rgba(0,0,0,.85);font-weight:500;font-size:16px;white-space:nowrap;text-align:center;background:transparent}.ant-divider-horizontal.ant-divider-with-text-center:after,.ant-divider-horizontal.ant-divider-with-text-center:before,.ant-divider-horizontal.ant-divider-with-text-left:after,.ant-divider-horizontal.ant-divider-with-text-left:before,.ant-divider-horizontal.ant-divider-with-text-right:after,.ant-divider-horizontal.ant-divider-with-text-right:before{position:relative;top:50%;display:table-cell;width:50%;border-top:1px solid #e8e8e8;transform:translateY(50%);content:""}.ant-divider-horizontal.ant-divider-with-text-left .ant-divider-inner-text,.ant-divider-horizontal.ant-divider-with-text-right .ant-divider-inner-text{display:inline-block;padding:0 10px}.ant-divider-horizontal.ant-divider-with-text-left:before{top:50%;width:5%}.ant-divider-horizontal.ant-divider-with-text-left:after{top:50%;width:95%}.ant-divider-horizontal.ant-divider-with-text-right:before{top:50%;width:95%}.ant-divider-horizontal.ant-divider-with-text-right:after{top:50%;width:5%}.ant-divider-inner-text{display:inline-block;padding:0 24px}.ant-divider-dashed{background:none;border:dashed #e8e8e8;border-width:1px 0 0}.ant-divider-horizontal.ant-divider-with-text-center.ant-divider-dashed,.ant-divider-horizontal.ant-divider-with-text-left.ant-divider-dashed,.ant-divider-horizontal.ant-divider-with-text-right.ant-divider-dashed{border-top:0}.ant-divider-horizontal.ant-divider-with-text-center.ant-divider-dashed:after,.ant-divider-horizontal.ant-divider-with-text-center.ant-divider-dashed:before,.ant-divider-horizontal.ant-divider-with-text-left.ant-divider-dashed:after,.ant-divider-horizontal.ant-divider-with-text-left.ant-divider-dashed:before,.ant-divider-horizontal.ant-divider-with-text-right.ant-divider-dashed:after,.ant-divider-horizontal.ant-divider-with-text-right.ant-divider-dashed:before{border-style:dashed none none}.ant-divider-vertical.ant-divider-dashed{border-width:0 0 0 1px}
+.ant-tooltip{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:absolute;z-index:1060;display:block;max-width:250px;visibility:visible}.ant-tooltip-hidden{display:none}.ant-tooltip-placement-top,.ant-tooltip-placement-topLeft,.ant-tooltip-placement-topRight{padding-bottom:8px}.ant-tooltip-placement-right,.ant-tooltip-placement-rightBottom,.ant-tooltip-placement-rightTop{padding-left:8px}.ant-tooltip-placement-bottom,.ant-tooltip-placement-bottomLeft,.ant-tooltip-placement-bottomRight{padding-top:8px}.ant-tooltip-placement-left,.ant-tooltip-placement-leftBottom,.ant-tooltip-placement-leftTop{padding-right:8px}.ant-tooltip-inner{min-width:30px;min-height:32px;padding:6px 8px;color:#fff;text-align:left;text-decoration:none;word-wrap:break-word;background-color:rgba(0,0,0,.75);border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-tooltip-arrow{position:absolute;display:block;width:13.07106781px;height:13.07106781px;overflow:hidden;background:transparent;pointer-events:none}.ant-tooltip-arrow:before{position:absolute;top:0;right:0;bottom:0;left:0;display:block;width:5px;height:5px;margin:auto;background-color:rgba(0,0,0,.75);content:"";pointer-events:auto}.ant-tooltip-placement-top .ant-tooltip-arrow,.ant-tooltip-placement-topLeft .ant-tooltip-arrow,.ant-tooltip-placement-topRight .ant-tooltip-arrow{bottom:-5.07106781px}.ant-tooltip-placement-top .ant-tooltip-arrow:before,.ant-tooltip-placement-topLeft .ant-tooltip-arrow:before,.ant-tooltip-placement-topRight .ant-tooltip-arrow:before{box-shadow:3px 3px 7px rgba(0,0,0,.07);transform:translateY(-6.53553391px) rotate(45deg)}.ant-tooltip-placement-top .ant-tooltip-arrow{left:50%;transform:translateX(-50%)}.ant-tooltip-placement-topLeft .ant-tooltip-arrow{left:13px}.ant-tooltip-placement-topRight .ant-tooltip-arrow{right:13px}.ant-tooltip-placement-right .ant-tooltip-arrow,.ant-tooltip-placement-rightBottom .ant-tooltip-arrow,.ant-tooltip-placement-rightTop .ant-tooltip-arrow{left:-5.07106781px}.ant-tooltip-placement-right .ant-tooltip-arrow:before,.ant-tooltip-placement-rightBottom .ant-tooltip-arrow:before,.ant-tooltip-placement-rightTop .ant-tooltip-arrow:before{box-shadow:-3px 3px 7px rgba(0,0,0,.07);transform:translateX(6.53553391px) rotate(45deg)}.ant-tooltip-placement-right .ant-tooltip-arrow{top:50%;transform:translateY(-50%)}.ant-tooltip-placement-rightTop .ant-tooltip-arrow{top:5px}.ant-tooltip-placement-rightBottom .ant-tooltip-arrow{bottom:5px}.ant-tooltip-placement-left .ant-tooltip-arrow,.ant-tooltip-placement-leftBottom .ant-tooltip-arrow,.ant-tooltip-placement-leftTop .ant-tooltip-arrow{right:-5.07106781px}.ant-tooltip-placement-left .ant-tooltip-arrow:before,.ant-tooltip-placement-leftBottom .ant-tooltip-arrow:before,.ant-tooltip-placement-leftTop .ant-tooltip-arrow:before{box-shadow:3px -3px 7px rgba(0,0,0,.07);transform:translateX(-6.53553391px) rotate(45deg)}.ant-tooltip-placement-left .ant-tooltip-arrow{top:50%;transform:translateY(-50%)}.ant-tooltip-placement-leftTop .ant-tooltip-arrow{top:5px}.ant-tooltip-placement-leftBottom .ant-tooltip-arrow{bottom:5px}.ant-tooltip-placement-bottom .ant-tooltip-arrow,.ant-tooltip-placement-bottomLeft .ant-tooltip-arrow,.ant-tooltip-placement-bottomRight .ant-tooltip-arrow{top:-5.07106781px}.ant-tooltip-placement-bottom .ant-tooltip-arrow:before,.ant-tooltip-placement-bottomLeft .ant-tooltip-arrow:before,.ant-tooltip-placement-bottomRight .ant-tooltip-arrow:before{box-shadow:-3px -3px 7px rgba(0,0,0,.07);transform:translateY(6.53553391px) rotate(45deg)}.ant-tooltip-placement-bottom .ant-tooltip-arrow{left:50%;transform:translateX(-50%)}.ant-tooltip-placement-bottomLeft .ant-tooltip-arrow{left:13px}.ant-tooltip-placement-bottomRight .ant-tooltip-arrow{right:13px}
+.ant-switch{margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;box-sizing:border-box;min-width:44px;height:22px;line-height:20px;vertical-align:middle;background-color:rgba(0,0,0,.25);border:1px solid transparent;border-radius:100px;cursor:pointer;transition:all .36s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-switch-inner{display:block;margin-right:6px;margin-left:24px;color:#fff;font-size:12px}.ant-switch-loading-icon,.ant-switch:after{position:absolute;top:1px;left:1px;width:18px;height:18px;background-color:#fff;border-radius:18px;cursor:pointer;transition:all .36s cubic-bezier(.78,.14,.15,.86);content:" "}.ant-switch:after{box-shadow:0 2px 4px 0 rgba(0,35,11,.2)}.ant-switch:not(.ant-switch-disabled):active:after,.ant-switch:not(.ant-switch-disabled):active:before{width:24px}.ant-switch-loading-icon{z-index:1;display:none;font-size:12px;background:transparent}.ant-switch-loading-icon svg{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto}.ant-switch-loading .ant-switch-loading-icon{display:inline-block;color:rgba(0,0,0,.65)}.ant-switch-checked.ant-switch-loading .ant-switch-loading-icon{color:#1890ff}.ant-switch:focus{outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-switch:focus:hover{box-shadow:none}.ant-switch-small{min-width:28px;height:16px;line-height:14px}.ant-switch-small .ant-switch-inner{margin-right:3px;margin-left:18px;font-size:12px}.ant-switch-small:after{width:12px;height:12px}.ant-switch-small:active:after,.ant-switch-small:active:before{width:16px}.ant-switch-small .ant-switch-loading-icon{width:12px;height:12px}.ant-switch-small.ant-switch-checked .ant-switch-inner{margin-right:18px;margin-left:3px}.ant-switch-small.ant-switch-checked .ant-switch-loading-icon{left:100%;margin-left:-13px}.ant-switch-small.ant-switch-loading .ant-switch-loading-icon{font-weight:700;transform:scale(.66667)}.ant-switch-checked{background-color:#1890ff}.ant-switch-checked .ant-switch-inner{margin-right:24px;margin-left:6px}.ant-switch-checked:after{left:100%;margin-left:-1px;transform:translateX(-100%)}.ant-switch-checked .ant-switch-loading-icon{left:100%;margin-left:-19px}.ant-switch-disabled,.ant-switch-loading{cursor:not-allowed;opacity:.4}.ant-switch-disabled *,.ant-switch-loading *{cursor:not-allowed}.ant-switch-disabled:after,.ant-switch-disabled:before,.ant-switch-loading:after,.ant-switch-loading:before{cursor:not-allowed}@keyframes AntSwitchSmallLoadingCircle{0%{transform:rotate(0deg) scale(.66667);transform-origin:50% 50%}to{transform:rotate(1turn) scale(.66667);transform-origin:50% 50%}}
+
+
+.ant-input{box-sizing:border-box;margin:0;font-variant:tabular-nums;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;width:100%;height:32px;padding:4px 11px;color:rgba(0,0,0,.65);font-size:14px;line-height:1.5;background-color:#fff;background-image:none;border:1px solid #d9d9d9;border-radius:4px;transition:all .3s}.ant-input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-input:-ms-input-placeholder{color:#bfbfbf}.ant-input::-webkit-input-placeholder{color:#bfbfbf}.ant-input:placeholder-shown{text-overflow:ellipsis}.ant-input:hover{border-color:#40a9ff;border-right-width:1px!important}.ant-input:focus{border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-input-disabled{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-input-disabled:hover{border-color:#d9d9d9;border-right-width:1px!important}.ant-input[disabled]{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-input[disabled]:hover{border-color:#d9d9d9;border-right-width:1px!important}textarea.ant-input{max-width:100%;height:auto;min-height:32px;line-height:1.5;vertical-align:bottom;transition:all .3s,height 0s}.ant-input-lg{height:40px;padding:6px 11px;font-size:16px}.ant-input-sm{height:24px;padding:1px 7px}.ant-input-group{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:table;width:100%;border-collapse:separate;border-spacing:0}.ant-input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.ant-input-group>[class*=col-]{padding-right:8px}.ant-input-group>[class*=col-]:last-child{padding-right:0}.ant-input-group-addon,.ant-input-group-wrap,.ant-input-group>.ant-input{display:table-cell}.ant-input-group-addon:not(:first-child):not(:last-child),.ant-input-group-wrap:not(:first-child):not(:last-child),.ant-input-group>.ant-input:not(:first-child):not(:last-child){border-radius:0}.ant-input-group-addon,.ant-input-group-wrap{width:1px;white-space:nowrap;vertical-align:middle}.ant-input-group-wrap>*{display:block!important}.ant-input-group .ant-input{float:left;width:100%;margin-bottom:0;text-align:inherit}.ant-input-group .ant-input:focus{z-index:1;border-right-width:1px}.ant-input-group .ant-input:hover{z-index:1;border-right-width:1px}.ant-input-group-addon{position:relative;padding:0 11px;color:rgba(0,0,0,.65);font-weight:400;font-size:14px;text-align:center;background-color:#fafafa;border:1px solid #d9d9d9;border-radius:4px;transition:all .3s}.ant-input-group-addon .ant-select{margin:-5px -11px}.ant-input-group-addon .ant-select .ant-select-selection{margin:-1px;background-color:inherit;border:1px solid transparent;box-shadow:none}.ant-input-group-addon .ant-select-focused .ant-select-selection,.ant-input-group-addon .ant-select-open .ant-select-selection{color:#1890ff}.ant-input-group-addon>i:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;content:""}.ant-input-group-addon:first-child,.ant-input-group>.ant-input:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.ant-input-group-addon:first-child .ant-select .ant-select-selection,.ant-input-group>.ant-input:first-child .ant-select .ant-select-selection{border-top-right-radius:0;border-bottom-right-radius:0}.ant-input-group>.ant-input-affix-wrapper:not(:first-child) .ant-input{border-top-left-radius:0;border-bottom-left-radius:0}.ant-input-group>.ant-input-affix-wrapper:not(:last-child) .ant-input{border-top-right-radius:0;border-bottom-right-radius:0}.ant-input-group-addon:first-child{border-right:0}.ant-input-group-addon:last-child{border-left:0}.ant-input-group-addon:last-child,.ant-input-group>.ant-input:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.ant-input-group-addon:last-child .ant-select .ant-select-selection,.ant-input-group>.ant-input:last-child .ant-select .ant-select-selection{border-top-left-radius:0;border-bottom-left-radius:0}.ant-input-group-lg .ant-input,.ant-input-group-lg>.ant-input-group-addon{height:40px;padding:6px 11px;font-size:16px}.ant-input-group-sm .ant-input,.ant-input-group-sm>.ant-input-group-addon{height:24px;padding:1px 7px}.ant-input-group-lg .ant-select-selection--single{height:40px}.ant-input-group-sm .ant-select-selection--single{height:24px}.ant-input-group .ant-input-affix-wrapper{display:table-cell;float:left;width:100%}.ant-input-group.ant-input-group-compact{display:block;zoom:1}.ant-input-group.ant-input-group-compact:after,.ant-input-group.ant-input-group-compact:before{display:table;content:""}.ant-input-group.ant-input-group-compact:after{clear:both}.ant-input-group.ant-input-group-compact-addon:not(:first-child):not(:last-child),.ant-input-group.ant-input-group-compact-wrap:not(:first-child):not(:last-child),.ant-input-group.ant-input-group-compact>.ant-input:not(:first-child):not(:last-child){border-right-width:1px}.ant-input-group.ant-input-group-compact-addon:not(:first-child):not(:last-child):hover,.ant-input-group.ant-input-group-compact-wrap:not(:first-child):not(:last-child):hover,.ant-input-group.ant-input-group-compact>.ant-input:not(:first-child):not(:last-child):hover{z-index:1}.ant-input-group.ant-input-group-compact-addon:not(:first-child):not(:last-child):focus,.ant-input-group.ant-input-group-compact-wrap:not(:first-child):not(:last-child):focus,.ant-input-group.ant-input-group-compact>.ant-input:not(:first-child):not(:last-child):focus{z-index:1}.ant-input-group.ant-input-group-compact>*{display:inline-block;float:none;vertical-align:top;border-radius:0}.ant-input-group.ant-input-group-compact>:not(:last-child){margin-right:-1px;border-right-width:1px}.ant-input-group.ant-input-group-compact .ant-input{float:none}.ant-input-group.ant-input-group-compact>.ant-calendar-picker .ant-input,.ant-input-group.ant-input-group-compact>.ant-cascader-picker .ant-input,.ant-input-group.ant-input-group-compact>.ant-input-group-wrapper .ant-input,.ant-input-group.ant-input-group-compact>.ant-mention-wrapper .ant-mention-editor,.ant-input-group.ant-input-group-compact>.ant-select-auto-complete .ant-input,.ant-input-group.ant-input-group-compact>.ant-select>.ant-select-selection,.ant-input-group.ant-input-group-compact>.ant-time-picker .ant-time-picker-input{border-right-width:1px;border-radius:0}.ant-input-group.ant-input-group-compact>.ant-calendar-picker .ant-input:hover,.ant-input-group.ant-input-group-compact>.ant-cascader-picker .ant-input:hover,.ant-input-group.ant-input-group-compact>.ant-input-group-wrapper .ant-input:hover,.ant-input-group.ant-input-group-compact>.ant-mention-wrapper .ant-mention-editor:hover,.ant-input-group.ant-input-group-compact>.ant-select-auto-complete .ant-input:hover,.ant-input-group.ant-input-group-compact>.ant-select>.ant-select-selection:hover,.ant-input-group.ant-input-group-compact>.ant-time-picker .ant-time-picker-input:hover{z-index:1}.ant-input-group.ant-input-group-compact>.ant-calendar-picker .ant-input:focus,.ant-input-group.ant-input-group-compact>.ant-cascader-picker .ant-input:focus,.ant-input-group.ant-input-group-compact>.ant-input-group-wrapper .ant-input:focus,.ant-input-group.ant-input-group-compact>.ant-mention-wrapper .ant-mention-editor:focus,.ant-input-group.ant-input-group-compact>.ant-select-auto-complete .ant-input:focus,.ant-input-group.ant-input-group-compact>.ant-select>.ant-select-selection:focus,.ant-input-group.ant-input-group-compact>.ant-time-picker .ant-time-picker-input:focus{z-index:1}.ant-input-group.ant-input-group-compact>.ant-calendar-picker:first-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-cascader-picker:first-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-mention-wrapper:first-child .ant-mention-editor,.ant-input-group.ant-input-group-compact>.ant-select-auto-complete:first-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-select:first-child>.ant-select-selection,.ant-input-group.ant-input-group-compact>.ant-time-picker:first-child .ant-time-picker-input,.ant-input-group.ant-input-group-compact>:first-child{border-top-left-radius:4px;border-bottom-left-radius:4px}.ant-input-group.ant-input-group-compact>.ant-calendar-picker:last-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-cascader-picker-focused:last-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-cascader-picker:last-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-mention-wrapper:last-child .ant-mention-editor,.ant-input-group.ant-input-group-compact>.ant-select-auto-complete:last-child .ant-input,.ant-input-group.ant-input-group-compact>.ant-select:last-child>.ant-select-selection,.ant-input-group.ant-input-group-compact>.ant-time-picker:last-child .ant-time-picker-input,.ant-input-group.ant-input-group-compact>:last-child{border-right-width:1px;border-top-right-radius:4px;border-bottom-right-radius:4px}.ant-input-group.ant-input-group-compact>.ant-select-auto-complete .ant-input{vertical-align:top}.ant-input-group-wrapper{display:inline-block;width:100%;text-align:start;vertical-align:top}.ant-input-affix-wrapper{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;width:100%;text-align:start}.ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled){border-color:#40a9ff;border-right-width:1px!important}.ant-input-affix-wrapper .ant-input{position:relative;text-align:inherit}.ant-input-affix-wrapper .ant-input-prefix,.ant-input-affix-wrapper .ant-input-suffix{position:absolute;top:50%;z-index:2;display:flex;align-items:center;color:rgba(0,0,0,.65);line-height:0;transform:translateY(-50%)}.ant-input-affix-wrapper .ant-input-prefix :not(.anticon),.ant-input-affix-wrapper .ant-input-suffix :not(.anticon){line-height:1.5}.ant-input-affix-wrapper .ant-input-disabled~.ant-input-suffix .anticon{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-input-affix-wrapper .ant-input-prefix{left:12px}.ant-input-affix-wrapper .ant-input-suffix{right:12px}.ant-input-affix-wrapper .ant-input:not(:first-child){padding-left:30px}.ant-input-affix-wrapper .ant-input:not(:last-child){padding-right:30px}.ant-input-affix-wrapper.ant-input-affix-wrapper-input-with-clear-btn .ant-input:not(:last-child){padding-right:49px}.ant-input-affix-wrapper.ant-input-affix-wrapper-textarea-with-clear-btn .ant-input{padding-right:22px}.ant-input-affix-wrapper .ant-input{min-height:100%}.ant-input-password-icon{color:rgba(0,0,0,.45);cursor:pointer;transition:all .3s}.ant-input-password-icon:hover{color:#333}.ant-input-clear-icon{color:rgba(0,0,0,.25);font-size:12px;cursor:pointer;transition:color .3s;vertical-align:0}.ant-input-clear-icon:hover{color:rgba(0,0,0,.45)}.ant-input-clear-icon:active{color:rgba(0,0,0,.65)}.ant-input-clear-icon+i{margin-left:6px}.ant-input-textarea-clear-icon{color:rgba(0,0,0,.25);font-size:12px;cursor:pointer;transition:color .3s;position:absolute;top:0;right:0;margin:8px 8px 0 0}.ant-input-textarea-clear-icon:hover{color:rgba(0,0,0,.45)}.ant-input-textarea-clear-icon:active{color:rgba(0,0,0,.65)}.ant-input-textarea-clear-icon+i{margin-left:6px}.ant-input-search-icon{color:rgba(0,0,0,.45);cursor:pointer;transition:all .3s}.ant-input-search-icon:hover{color:rgba(0,0,0,.8)}.ant-input-search-enter-button input{border-right:0}.ant-input-search-enter-button+.ant-input-group-addon,.ant-input-search-enter-button input+.ant-input-group-addon{padding:0;border:0}.ant-input-search-enter-button+.ant-input-group-addon .ant-input-search-button,.ant-input-search-enter-button input+.ant-input-group-addon .ant-input-search-button{border-top-left-radius:0;border-bottom-left-radius:0}
+.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-nav-container{height:40px}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-ink-bar{visibility:hidden}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab{height:40px;margin:0 2px 0 0;padding:0 16px;line-height:38px;background:#fafafa;border:1px solid #e8e8e8;border-radius:4px 4px 0 0;transition:all .3s cubic-bezier(.645,.045,.355,1)}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab-active{height:40px;color:#1890ff;background:#fff;border-color:#e8e8e8;border-bottom:1px solid #fff}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab-active:before{border-top:2px solid transparent}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab-disabled{color:#1890ff;color:rgba(0,0,0,.25)}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab-inactive{padding:0}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-nav-wrap{margin-bottom:0}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab .ant-tabs-close-x{width:16px;height:16px;height:14px;margin-right:-5px;margin-left:3px;overflow:hidden;color:rgba(0,0,0,.45);font-size:12px;vertical-align:middle;transition:all .3s}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab .ant-tabs-close-x:hover{color:rgba(0,0,0,.85)}.ant-tabs.ant-tabs-card .ant-tabs-card-content>.ant-tabs-tabpane,.ant-tabs.ant-tabs-editable-card .ant-tabs-card-content>.ant-tabs-tabpane{transition:none!important}.ant-tabs.ant-tabs-card .ant-tabs-card-content>.ant-tabs-tabpane-inactive,.ant-tabs.ant-tabs-editable-card .ant-tabs-card-content>.ant-tabs-tabpane-inactive{overflow:hidden}.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab:hover .anticon-close{opacity:1}.ant-tabs-extra-content{line-height:45px}.ant-tabs-extra-content .ant-tabs-new-tab{position:relative;width:20px;height:20px;color:rgba(0,0,0,.65);font-size:12px;line-height:20px;text-align:center;border:1px solid #e8e8e8;border-radius:2px;cursor:pointer;transition:all .3s}.ant-tabs-extra-content .ant-tabs-new-tab:hover{color:#1890ff;border-color:#1890ff}.ant-tabs-extra-content .ant-tabs-new-tab svg{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto}.ant-tabs.ant-tabs-large .ant-tabs-extra-content{line-height:56px}.ant-tabs.ant-tabs-small .ant-tabs-extra-content{line-height:37px}.ant-tabs.ant-tabs-card .ant-tabs-extra-content{line-height:40px}.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-nav-container,.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-nav-container{height:100%}.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-tab,.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-tab{margin-bottom:8px;border-bottom:1px solid #e8e8e8}.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-tab-active,.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-tab-active{padding-bottom:4px}.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-tab:last-child,.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-tab:last-child{margin-bottom:8px}.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-new-tab,.ant-tabs-vertical.ant-tabs-card .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-new-tab{width:90%}.ant-tabs-vertical.ant-tabs-card.ant-tabs-left .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-nav-wrap{margin-right:0}.ant-tabs-vertical.ant-tabs-card.ant-tabs-left .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-tab{margin-right:1px;border-right:0;border-radius:4px 0 0 4px}.ant-tabs-vertical.ant-tabs-card.ant-tabs-left .ant-tabs-card-bar.ant-tabs-left-bar .ant-tabs-tab-active{margin-right:-1px;padding-right:18px}.ant-tabs-vertical.ant-tabs-card.ant-tabs-right .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-nav-wrap{margin-left:0}.ant-tabs-vertical.ant-tabs-card.ant-tabs-right .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-tab{margin-left:1px;border-left:0;border-radius:0 4px 4px 0}.ant-tabs-vertical.ant-tabs-card.ant-tabs-right .ant-tabs-card-bar.ant-tabs-right-bar .ant-tabs-tab-active{margin-left:-1px;padding-left:18px}.ant-tabs .ant-tabs-card-bar.ant-tabs-bottom-bar .ant-tabs-tab{height:auto;border-top:0;border-bottom:1px solid #e8e8e8;border-radius:0 0 4px 4px}.ant-tabs .ant-tabs-card-bar.ant-tabs-bottom-bar .ant-tabs-tab-active{padding-top:1px;padding-bottom:0;color:#1890ff}.ant-tabs{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;overflow:hidden;zoom:1}.ant-tabs:after,.ant-tabs:before{display:table;content:""}.ant-tabs:after{clear:both}.ant-tabs-ink-bar{position:absolute;bottom:1px;left:0;z-index:1;box-sizing:border-box;width:0;height:2px;background-color:#1890ff;transform-origin:0 0}.ant-tabs-bar{margin:0 0 16px;border-bottom:1px solid #e8e8e8;outline:none;transition:padding .3s cubic-bezier(.645,.045,.355,1)}.ant-tabs-nav-container{position:relative;box-sizing:border-box;margin-bottom:-1px;overflow:hidden;font-size:14px;line-height:1.5;white-space:nowrap;transition:padding .3s cubic-bezier(.645,.045,.355,1);zoom:1}.ant-tabs-nav-container:after,.ant-tabs-nav-container:before{display:table;content:""}.ant-tabs-nav-container:after{clear:both}.ant-tabs-nav-container-scrolling{padding-right:32px;padding-left:32px}.ant-tabs-bottom .ant-tabs-bottom-bar{margin-top:16px;margin-bottom:0;border-top:1px solid #e8e8e8;border-bottom:none}.ant-tabs-bottom .ant-tabs-bottom-bar .ant-tabs-ink-bar{top:1px;bottom:auto}.ant-tabs-bottom .ant-tabs-bottom-bar .ant-tabs-nav-container{margin-top:-1px;margin-bottom:0}.ant-tabs-tab-next,.ant-tabs-tab-prev{position:absolute;z-index:2;width:0;height:100%;color:rgba(0,0,0,.45);text-align:center;background-color:transparent;border:0;cursor:pointer;opacity:0;transition:width .3s cubic-bezier(.645,.045,.355,1),opacity .3s cubic-bezier(.645,.045,.355,1),color .3s cubic-bezier(.645,.045,.355,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}.ant-tabs-tab-next.ant-tabs-tab-arrow-show,.ant-tabs-tab-prev.ant-tabs-tab-arrow-show{width:32px;height:100%;opacity:1;pointer-events:auto}.ant-tabs-tab-next:hover,.ant-tabs-tab-prev:hover{color:rgba(0,0,0,.65)}.ant-tabs-tab-next-icon,.ant-tabs-tab-prev-icon{position:absolute;top:50%;left:50%;font-weight:700;font-style:normal;font-variant:normal;line-height:inherit;text-align:center;text-transform:none;transform:translate(-50%,-50%)}.ant-tabs-tab-next-icon-target,.ant-tabs-tab-prev-icon-target{display:block;display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg)}:root .ant-tabs-tab-next-icon-target,:root .ant-tabs-tab-prev-icon-target{font-size:12px}.ant-tabs-tab-btn-disabled{cursor:not-allowed}.ant-tabs-tab-btn-disabled,.ant-tabs-tab-btn-disabled:hover{color:rgba(0,0,0,.25)}.ant-tabs-tab-next{right:2px}.ant-tabs-tab-prev{left:0}:root .ant-tabs-tab-prev{-webkit-filter:none;filter:none}.ant-tabs-nav-wrap{margin-bottom:-1px;overflow:hidden}.ant-tabs-nav-scroll{overflow:hidden;white-space:nowrap}.ant-tabs-nav{position:relative;display:inline-block;box-sizing:border-box;margin:0;padding-left:0;list-style:none;transition:transform .3s cubic-bezier(.645,.045,.355,1)}.ant-tabs-nav:after,.ant-tabs-nav:before{display:table;content:" "}.ant-tabs-nav:after{clear:both}.ant-tabs-nav .ant-tabs-tab{position:relative;display:inline-block;box-sizing:border-box;height:100%;margin:0 32px 0 0;padding:12px 16px;text-decoration:none;cursor:pointer;transition:color .3s cubic-bezier(.645,.045,.355,1)}.ant-tabs-nav .ant-tabs-tab:before{position:absolute;top:-1px;left:0;width:100%;border-top:2px solid transparent;border-radius:4px 4px 0 0;transition:all .3s;content:"";pointer-events:none}.ant-tabs-nav .ant-tabs-tab:last-child{margin-right:0}.ant-tabs-nav .ant-tabs-tab:hover{color:#40a9ff}.ant-tabs-nav .ant-tabs-tab:active{color:#096dd9}.ant-tabs-nav .ant-tabs-tab .anticon{margin-right:8px}.ant-tabs-nav .ant-tabs-tab-active{color:#1890ff;font-weight:500}.ant-tabs-nav .ant-tabs-tab-disabled,.ant-tabs-nav .ant-tabs-tab-disabled:hover{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-tabs .ant-tabs-large-bar .ant-tabs-nav-container{font-size:16px}.ant-tabs .ant-tabs-large-bar .ant-tabs-tab{padding:16px}.ant-tabs .ant-tabs-small-bar .ant-tabs-nav-container{font-size:14px}.ant-tabs .ant-tabs-small-bar .ant-tabs-tab{padding:8px 16px}.ant-tabs-content:before{display:block;overflow:hidden;content:""}.ant-tabs .ant-tabs-bottom-content,.ant-tabs .ant-tabs-top-content{width:100%}.ant-tabs .ant-tabs-bottom-content>.ant-tabs-tabpane,.ant-tabs .ant-tabs-top-content>.ant-tabs-tabpane{flex-shrink:0;width:100%;-webkit-backface-visibility:hidden;opacity:1;transition:opacity .45s}.ant-tabs .ant-tabs-bottom-content>.ant-tabs-tabpane-inactive,.ant-tabs .ant-tabs-top-content>.ant-tabs-tabpane-inactive{height:0;padding:0!important;overflow:hidden;opacity:0;pointer-events:none}.ant-tabs .ant-tabs-bottom-content>.ant-tabs-tabpane-inactive input,.ant-tabs .ant-tabs-top-content>.ant-tabs-tabpane-inactive input{visibility:hidden}.ant-tabs .ant-tabs-bottom-content.ant-tabs-content-animated,.ant-tabs .ant-tabs-top-content.ant-tabs-content-animated{display:flex;flex-direction:row;transition:margin-left .3s cubic-bezier(.645,.045,.355,1);will-change:margin-left}.ant-tabs .ant-tabs-left-bar,.ant-tabs .ant-tabs-right-bar{height:100%;border-bottom:0}.ant-tabs .ant-tabs-left-bar .ant-tabs-tab-arrow-show,.ant-tabs .ant-tabs-right-bar .ant-tabs-tab-arrow-show{width:100%;height:32px}.ant-tabs .ant-tabs-left-bar .ant-tabs-tab,.ant-tabs .ant-tabs-right-bar .ant-tabs-tab{display:block;float:none;margin:0 0 16px;padding:8px 24px}.ant-tabs .ant-tabs-left-bar .ant-tabs-tab:last-child,.ant-tabs .ant-tabs-right-bar .ant-tabs-tab:last-child{margin-bottom:0}.ant-tabs .ant-tabs-left-bar .ant-tabs-extra-content,.ant-tabs .ant-tabs-right-bar .ant-tabs-extra-content{text-align:center}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-scroll,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-scroll{width:auto}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-container,.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-wrap,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-container,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-wrap{height:100%}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-container,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-container{margin-bottom:0}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-container.ant-tabs-nav-container-scrolling,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-container.ant-tabs-nav-container-scrolling{padding:32px 0}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-wrap,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-wrap{margin-bottom:0}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav,.ant-tabs .ant-tabs-right-bar .ant-tabs-nav{width:100%}.ant-tabs .ant-tabs-left-bar .ant-tabs-ink-bar,.ant-tabs .ant-tabs-right-bar .ant-tabs-ink-bar{top:0;bottom:auto;left:auto;width:2px;height:0}.ant-tabs .ant-tabs-left-bar .ant-tabs-tab-next,.ant-tabs .ant-tabs-right-bar .ant-tabs-tab-next{right:0;bottom:0;width:100%;height:32px}.ant-tabs .ant-tabs-left-bar .ant-tabs-tab-prev,.ant-tabs .ant-tabs-right-bar .ant-tabs-tab-prev{top:0;width:100%;height:32px}.ant-tabs .ant-tabs-left-content,.ant-tabs .ant-tabs-right-content{width:auto;margin-top:0!important;overflow:hidden}.ant-tabs .ant-tabs-left-bar{float:left;margin-right:-1px;margin-bottom:0;border-right:1px solid #e8e8e8}.ant-tabs .ant-tabs-left-bar .ant-tabs-tab{text-align:right}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-container{margin-right:-1px}.ant-tabs .ant-tabs-left-bar .ant-tabs-nav-wrap{margin-right:-1px}.ant-tabs .ant-tabs-left-bar .ant-tabs-ink-bar{right:1px}.ant-tabs .ant-tabs-left-content{padding-left:24px;border-left:1px solid #e8e8e8}.ant-tabs .ant-tabs-right-bar{float:right;margin-bottom:0;margin-left:-1px;border-left:1px solid #e8e8e8}.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-container{margin-left:-1px}.ant-tabs .ant-tabs-right-bar .ant-tabs-nav-wrap{margin-left:-1px}.ant-tabs .ant-tabs-right-bar .ant-tabs-ink-bar{left:1px}.ant-tabs .ant-tabs-right-content{padding-right:24px;border-right:1px solid #e8e8e8}.ant-tabs-bottom .ant-tabs-ink-bar-animated,.ant-tabs-top .ant-tabs-ink-bar-animated{transition:transform .3s cubic-bezier(.645,.045,.355,1),width .2s cubic-bezier(.645,.045,.355,1),left .3s cubic-bezier(.645,.045,.355,1)}.ant-tabs-left .ant-tabs-ink-bar-animated,.ant-tabs-right .ant-tabs-ink-bar-animated{transition:transform .3s cubic-bezier(.645,.045,.355,1),height .2s cubic-bezier(.645,.045,.355,1),top .3s cubic-bezier(.645,.045,.355,1)}.ant-tabs-no-animation>.ant-tabs-content>.ant-tabs-content-animated,.no-flex>.ant-tabs-content>.ant-tabs-content-animated{margin-left:0!important;transform:none!important}.ant-tabs-no-animation>.ant-tabs-content>.ant-tabs-tabpane-inactive,.no-flex>.ant-tabs-content>.ant-tabs-tabpane-inactive{height:0;padding:0!important;overflow:hidden;opacity:0;pointer-events:none}.ant-tabs-no-animation>.ant-tabs-content>.ant-tabs-tabpane-inactive input,.no-flex>.ant-tabs-content>.ant-tabs-tabpane-inactive input{visibility:hidden}.ant-tabs-left-content>.ant-tabs-content-animated,.ant-tabs-right-content>.ant-tabs-content-animated{margin-left:0!important;transform:none!important}.ant-tabs-left-content>.ant-tabs-tabpane-inactive,.ant-tabs-right-content>.ant-tabs-tabpane-inactive{height:0;padding:0!important;overflow:hidden;opacity:0;pointer-events:none}.ant-tabs-left-content>.ant-tabs-tabpane-inactive input,.ant-tabs-right-content>.ant-tabs-tabpane-inactive input{visibility:hidden}
+.ant-calendar-picker-container{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:absolute;z-index:1050;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}.ant-calendar-picker-container.slide-up-appear.slide-up-appear-active.ant-calendar-picker-container-placement-topLeft,.ant-calendar-picker-container.slide-up-appear.slide-up-appear-active.ant-calendar-picker-container-placement-topRight,.ant-calendar-picker-container.slide-up-enter.slide-up-enter-active.ant-calendar-picker-container-placement-topLeft,.ant-calendar-picker-container.slide-up-enter.slide-up-enter-active.ant-calendar-picker-container-placement-topRight{animation-name:antSlideDownIn}.ant-calendar-picker-container.slide-up-appear.slide-up-appear-active.ant-calendar-picker-container-placement-bottomLeft,.ant-calendar-picker-container.slide-up-appear.slide-up-appear-active.ant-calendar-picker-container-placement-bottomRight,.ant-calendar-picker-container.slide-up-enter.slide-up-enter-active.ant-calendar-picker-container-placement-bottomLeft,.ant-calendar-picker-container.slide-up-enter.slide-up-enter-active.ant-calendar-picker-container-placement-bottomRight{animation-name:antSlideUpIn}.ant-calendar-picker-container.slide-up-leave.slide-up-leave-active.ant-calendar-picker-container-placement-topLeft,.ant-calendar-picker-container.slide-up-leave.slide-up-leave-active.ant-calendar-picker-container-placement-topRight{animation-name:antSlideDownOut}.ant-calendar-picker-container.slide-up-leave.slide-up-leave-active.ant-calendar-picker-container-placement-bottomLeft,.ant-calendar-picker-container.slide-up-leave.slide-up-leave-active.ant-calendar-picker-container-placement-bottomRight{animation-name:antSlideUpOut}.ant-calendar-picker{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;outline:none;cursor:text;transition:opacity .3s}.ant-calendar-picker-input{outline:none}.ant-calendar-picker-input.ant-input{line-height:1.5}.ant-calendar-picker-input.ant-input-sm{padding-top:0;padding-bottom:0}.ant-calendar-picker:hover .ant-calendar-picker-input:not(.ant-input-disabled){border-color:#40a9ff}.ant-calendar-picker:focus .ant-calendar-picker-input:not(.ant-input-disabled){border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-calendar-picker-clear,.ant-calendar-picker-icon{position:absolute;top:50%;right:12px;z-index:1;width:14px;height:14px;margin-top:-7px;font-size:12px;line-height:14px;transition:all .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-calendar-picker-clear{z-index:2;color:rgba(0,0,0,.25);font-size:14px;background:#fff;cursor:pointer;opacity:0;pointer-events:none}.ant-calendar-picker-clear:hover{color:rgba(0,0,0,.45)}.ant-calendar-picker:hover .ant-calendar-picker-clear{opacity:1;pointer-events:auto}.ant-calendar-picker-icon{display:inline-block;color:rgba(0,0,0,.25);font-size:14px;line-height:1}.ant-input-disabled+.ant-calendar-picker-icon{cursor:not-allowed}.ant-calendar-picker-small .ant-calendar-picker-clear,.ant-calendar-picker-small .ant-calendar-picker-icon{right:8px}.ant-calendar{position:relative;width:280px;font-size:14px;line-height:1.5;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #fff;border-radius:4px;outline:none;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-calendar-input-wrap{height:34px;padding:6px 10px;border-bottom:1px solid #e8e8e8}.ant-calendar-input{width:100%;height:22px;color:rgba(0,0,0,.65);background:#fff;border:0;outline:0;cursor:auto}.ant-calendar-input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-calendar-input:-ms-input-placeholder{color:#bfbfbf}.ant-calendar-input::-webkit-input-placeholder{color:#bfbfbf}.ant-calendar-input:placeholder-shown{text-overflow:ellipsis}.ant-calendar-week-number{width:286px}.ant-calendar-week-number-cell{text-align:center}.ant-calendar-header{height:40px;line-height:40px;text-align:center;border-bottom:1px solid #e8e8e8;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-calendar-header a:hover{color:#40a9ff}.ant-calendar-header .ant-calendar-century-select,.ant-calendar-header .ant-calendar-decade-select,.ant-calendar-header .ant-calendar-month-select,.ant-calendar-header .ant-calendar-year-select{display:inline-block;padding:0 2px;color:rgba(0,0,0,.85);font-weight:500;line-height:40px}.ant-calendar-header .ant-calendar-century-select-arrow,.ant-calendar-header .ant-calendar-decade-select-arrow,.ant-calendar-header .ant-calendar-month-select-arrow,.ant-calendar-header .ant-calendar-year-select-arrow{display:none}.ant-calendar-header .ant-calendar-next-century-btn,.ant-calendar-header .ant-calendar-next-decade-btn,.ant-calendar-header .ant-calendar-next-month-btn,.ant-calendar-header .ant-calendar-next-year-btn,.ant-calendar-header .ant-calendar-prev-century-btn,.ant-calendar-header .ant-calendar-prev-decade-btn,.ant-calendar-header .ant-calendar-prev-month-btn,.ant-calendar-header .ant-calendar-prev-year-btn{position:absolute;top:0;display:inline-block;padding:0 5px;color:rgba(0,0,0,.45);font-size:16px;font-family:Arial,Hiragino Sans GB,Microsoft Yahei,"Microsoft Sans Serif",sans-serif;line-height:40px}.ant-calendar-header .ant-calendar-prev-century-btn,.ant-calendar-header .ant-calendar-prev-decade-btn,.ant-calendar-header .ant-calendar-prev-year-btn{left:7px;height:100%}.ant-calendar-header .ant-calendar-prev-century-btn:after,.ant-calendar-header .ant-calendar-prev-century-btn:before,.ant-calendar-header .ant-calendar-prev-decade-btn:after,.ant-calendar-header .ant-calendar-prev-decade-btn:before,.ant-calendar-header .ant-calendar-prev-year-btn:after,.ant-calendar-header .ant-calendar-prev-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-header .ant-calendar-prev-century-btn:hover:after,.ant-calendar-header .ant-calendar-prev-century-btn:hover:before,.ant-calendar-header .ant-calendar-prev-decade-btn:hover:after,.ant-calendar-header .ant-calendar-prev-decade-btn:hover:before,.ant-calendar-header .ant-calendar-prev-year-btn:hover:after,.ant-calendar-header .ant-calendar-prev-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-header .ant-calendar-prev-century-btn:after,.ant-calendar-header .ant-calendar-prev-decade-btn:after,.ant-calendar-header .ant-calendar-prev-year-btn:after{display:none}.ant-calendar-header .ant-calendar-prev-century-btn:after,.ant-calendar-header .ant-calendar-prev-decade-btn:after,.ant-calendar-header .ant-calendar-prev-year-btn:after{position:relative;left:-3px;display:inline-block}.ant-calendar-header .ant-calendar-next-century-btn,.ant-calendar-header .ant-calendar-next-decade-btn,.ant-calendar-header .ant-calendar-next-year-btn{right:7px;height:100%}.ant-calendar-header .ant-calendar-next-century-btn:after,.ant-calendar-header .ant-calendar-next-century-btn:before,.ant-calendar-header .ant-calendar-next-decade-btn:after,.ant-calendar-header .ant-calendar-next-decade-btn:before,.ant-calendar-header .ant-calendar-next-year-btn:after,.ant-calendar-header .ant-calendar-next-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-header .ant-calendar-next-century-btn:hover:after,.ant-calendar-header .ant-calendar-next-century-btn:hover:before,.ant-calendar-header .ant-calendar-next-decade-btn:hover:after,.ant-calendar-header .ant-calendar-next-decade-btn:hover:before,.ant-calendar-header .ant-calendar-next-year-btn:hover:after,.ant-calendar-header .ant-calendar-next-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-header .ant-calendar-next-century-btn:after,.ant-calendar-header .ant-calendar-next-decade-btn:after,.ant-calendar-header .ant-calendar-next-year-btn:after{display:none}.ant-calendar-header .ant-calendar-next-century-btn:after,.ant-calendar-header .ant-calendar-next-century-btn:before,.ant-calendar-header .ant-calendar-next-decade-btn:after,.ant-calendar-header .ant-calendar-next-decade-btn:before,.ant-calendar-header .ant-calendar-next-year-btn:after,.ant-calendar-header .ant-calendar-next-year-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-header .ant-calendar-next-century-btn:before,.ant-calendar-header .ant-calendar-next-decade-btn:before,.ant-calendar-header .ant-calendar-next-year-btn:before{position:relative;left:3px}.ant-calendar-header .ant-calendar-next-century-btn:after,.ant-calendar-header .ant-calendar-next-decade-btn:after,.ant-calendar-header .ant-calendar-next-year-btn:after{display:inline-block}.ant-calendar-header .ant-calendar-prev-month-btn{left:29px;height:100%}.ant-calendar-header .ant-calendar-prev-month-btn:after,.ant-calendar-header .ant-calendar-prev-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-header .ant-calendar-prev-month-btn:hover:after,.ant-calendar-header .ant-calendar-prev-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-header .ant-calendar-prev-month-btn:after{display:none}.ant-calendar-header .ant-calendar-next-month-btn{right:29px;height:100%}.ant-calendar-header .ant-calendar-next-month-btn:after,.ant-calendar-header .ant-calendar-next-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-header .ant-calendar-next-month-btn:hover:after,.ant-calendar-header .ant-calendar-next-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-header .ant-calendar-next-month-btn:after{display:none}.ant-calendar-header .ant-calendar-next-month-btn:after,.ant-calendar-header .ant-calendar-next-month-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-body{padding:8px 12px}.ant-calendar table{width:100%;max-width:100%;background-color:transparent;border-collapse:collapse}.ant-calendar table,.ant-calendar td,.ant-calendar th{text-align:center;border:0}.ant-calendar-calendar-table{margin-bottom:0;border-spacing:0}.ant-calendar-column-header{width:33px;padding:6px 0;line-height:18px;text-align:center}.ant-calendar-column-header .ant-calendar-column-header-inner{display:block;font-weight:400}.ant-calendar-week-number-header .ant-calendar-column-header-inner{display:none}.ant-calendar-cell{height:30px;padding:3px 0}.ant-calendar-date{display:block;width:24px;height:24px;margin:0 auto;padding:0;color:rgba(0,0,0,.65);line-height:22px;text-align:center;background:transparent;border:1px solid transparent;border-radius:2px;transition:background .3s ease}.ant-calendar-date-panel{position:relative;outline:none}.ant-calendar-date:hover{background:#e6f7ff;cursor:pointer}.ant-calendar-date:active{color:#fff;background:#40a9ff}.ant-calendar-today .ant-calendar-date{color:#1890ff;font-weight:700;border-color:#1890ff}.ant-calendar-selected-day .ant-calendar-date{background:#bae7ff}.ant-calendar-last-month-cell .ant-calendar-date,.ant-calendar-last-month-cell .ant-calendar-date:hover,.ant-calendar-next-month-btn-day .ant-calendar-date,.ant-calendar-next-month-btn-day .ant-calendar-date:hover{color:rgba(0,0,0,.25);background:transparent;border-color:transparent}.ant-calendar-disabled-cell .ant-calendar-date{position:relative;width:auto;color:rgba(0,0,0,.25);background:#f5f5f5;border:1px solid transparent;border-radius:0;cursor:not-allowed}.ant-calendar-disabled-cell .ant-calendar-date:hover{background:#f5f5f5}.ant-calendar-disabled-cell.ant-calendar-selected-day .ant-calendar-date:before{position:absolute;top:-1px;left:5px;width:24px;height:24px;background:rgba(0,0,0,.1);border-radius:2px;content:""}.ant-calendar-disabled-cell.ant-calendar-today .ant-calendar-date{position:relative;padding-right:5px;padding-left:5px}.ant-calendar-disabled-cell.ant-calendar-today .ant-calendar-date:before{position:absolute;top:-1px;left:5px;width:24px;height:24px;border:1px solid rgba(0,0,0,.25);border-radius:2px;content:" "}.ant-calendar-disabled-cell-first-of-row .ant-calendar-date{border-top-left-radius:4px;border-bottom-left-radius:4px}.ant-calendar-disabled-cell-last-of-row .ant-calendar-date{border-top-right-radius:4px;border-bottom-right-radius:4px}.ant-calendar-footer{padding:0 12px;line-height:38px;border-top:1px solid #e8e8e8}.ant-calendar-footer:empty{border-top:0}.ant-calendar-footer-btn{display:block;text-align:center}.ant-calendar-footer-extra{text-align:left}.ant-calendar .ant-calendar-clear-btn,.ant-calendar .ant-calendar-today-btn{display:inline-block;margin:0 0 0 8px;text-align:center}.ant-calendar .ant-calendar-clear-btn-disabled,.ant-calendar .ant-calendar-today-btn-disabled{color:rgba(0,0,0,.25);cursor:not-allowed}.ant-calendar .ant-calendar-clear-btn:only-child,.ant-calendar .ant-calendar-today-btn:only-child{margin:0}.ant-calendar .ant-calendar-clear-btn{position:absolute;top:7px;right:5px;display:none;width:20px;height:20px;margin:0;overflow:hidden;line-height:20px;text-align:center;text-indent:-76px}.ant-calendar .ant-calendar-clear-btn:after{display:inline-block;width:20px;color:rgba(0,0,0,.25);font-size:14px;line-height:1;text-indent:43px;transition:color .3s ease}.ant-calendar .ant-calendar-clear-btn:hover:after{color:rgba(0,0,0,.45)}.ant-calendar .ant-calendar-ok-btn{position:relative;display:inline-block;font-weight:400;white-space:nowrap;text-align:center;background-image:none;box-shadow:0 2px 0 rgba(0,0,0,.015);cursor:pointer;transition:all .3s cubic-bezier(.645,.045,.355,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;touch-action:manipulation;height:32px;color:#fff;background-color:#1890ff;border:1px solid #1890ff;text-shadow:0 -1px 0 rgba(0,0,0,.12);box-shadow:0 2px 0 rgba(0,0,0,.045);height:24px;padding:0 7px;font-size:14px;border-radius:4px;line-height:22px}.ant-calendar .ant-calendar-ok-btn>.anticon{line-height:1}.ant-calendar .ant-calendar-ok-btn,.ant-calendar .ant-calendar-ok-btn:active,.ant-calendar .ant-calendar-ok-btn:focus{outline:0}.ant-calendar .ant-calendar-ok-btn:not([disabled]):hover{text-decoration:none}.ant-calendar .ant-calendar-ok-btn:not([disabled]):active{outline:0;box-shadow:none}.ant-calendar .ant-calendar-ok-btn.disabled,.ant-calendar .ant-calendar-ok-btn[disabled]{cursor:not-allowed}.ant-calendar .ant-calendar-ok-btn.disabled>*,.ant-calendar .ant-calendar-ok-btn[disabled]>*{pointer-events:none}.ant-calendar .ant-calendar-ok-btn-lg{height:40px;padding:0 15px;font-size:16px;border-radius:4px}.ant-calendar .ant-calendar-ok-btn-sm{height:24px;padding:0 7px;font-size:14px;border-radius:4px}.ant-calendar .ant-calendar-ok-btn>a:only-child{color:currentColor}.ant-calendar .ant-calendar-ok-btn>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-calendar .ant-calendar-ok-btn:focus,.ant-calendar .ant-calendar-ok-btn:hover{color:#fff;background-color:#40a9ff;border-color:#40a9ff}.ant-calendar .ant-calendar-ok-btn:focus>a:only-child,.ant-calendar .ant-calendar-ok-btn:hover>a:only-child{color:currentColor}.ant-calendar .ant-calendar-ok-btn:focus>a:only-child:after,.ant-calendar .ant-calendar-ok-btn:hover>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-calendar .ant-calendar-ok-btn.active,.ant-calendar .ant-calendar-ok-btn:active{color:#fff;background-color:#096dd9;border-color:#096dd9}.ant-calendar .ant-calendar-ok-btn.active>a:only-child,.ant-calendar .ant-calendar-ok-btn:active>a:only-child{color:currentColor}.ant-calendar .ant-calendar-ok-btn.active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn:active>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-calendar .ant-calendar-ok-btn-disabled,.ant-calendar .ant-calendar-ok-btn-disabled.active,.ant-calendar .ant-calendar-ok-btn-disabled:active,.ant-calendar .ant-calendar-ok-btn-disabled:focus,.ant-calendar .ant-calendar-ok-btn-disabled:hover,.ant-calendar .ant-calendar-ok-btn.disabled,.ant-calendar .ant-calendar-ok-btn.disabled.active,.ant-calendar .ant-calendar-ok-btn.disabled:active,.ant-calendar .ant-calendar-ok-btn.disabled:focus,.ant-calendar .ant-calendar-ok-btn.disabled:hover,.ant-calendar .ant-calendar-ok-btn[disabled],.ant-calendar .ant-calendar-ok-btn[disabled].active,.ant-calendar .ant-calendar-ok-btn[disabled]:active,.ant-calendar .ant-calendar-ok-btn[disabled]:focus,.ant-calendar .ant-calendar-ok-btn[disabled]:hover{color:rgba(0,0,0,.25);background-color:#f5f5f5;border-color:#d9d9d9;text-shadow:none;box-shadow:none}.ant-calendar .ant-calendar-ok-btn-disabled.active>a:only-child,.ant-calendar .ant-calendar-ok-btn-disabled:active>a:only-child,.ant-calendar .ant-calendar-ok-btn-disabled:focus>a:only-child,.ant-calendar .ant-calendar-ok-btn-disabled:hover>a:only-child,.ant-calendar .ant-calendar-ok-btn-disabled>a:only-child,.ant-calendar .ant-calendar-ok-btn.disabled.active>a:only-child,.ant-calendar .ant-calendar-ok-btn.disabled:active>a:only-child,.ant-calendar .ant-calendar-ok-btn.disabled:focus>a:only-child,.ant-calendar .ant-calendar-ok-btn.disabled:hover>a:only-child,.ant-calendar .ant-calendar-ok-btn.disabled>a:only-child,.ant-calendar .ant-calendar-ok-btn[disabled].active>a:only-child,.ant-calendar .ant-calendar-ok-btn[disabled]:active>a:only-child,.ant-calendar .ant-calendar-ok-btn[disabled]:focus>a:only-child,.ant-calendar .ant-calendar-ok-btn[disabled]:hover>a:only-child,.ant-calendar .ant-calendar-ok-btn[disabled]>a:only-child{color:currentColor}.ant-calendar .ant-calendar-ok-btn-disabled.active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn-disabled:active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn-disabled:focus>a:only-child:after,.ant-calendar .ant-calendar-ok-btn-disabled:hover>a:only-child:after,.ant-calendar .ant-calendar-ok-btn-disabled>a:only-child:after,.ant-calendar .ant-calendar-ok-btn.disabled.active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn.disabled:active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn.disabled:focus>a:only-child:after,.ant-calendar .ant-calendar-ok-btn.disabled:hover>a:only-child:after,.ant-calendar .ant-calendar-ok-btn.disabled>a:only-child:after,.ant-calendar .ant-calendar-ok-btn[disabled].active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn[disabled]:active>a:only-child:after,.ant-calendar .ant-calendar-ok-btn[disabled]:focus>a:only-child:after,.ant-calendar .ant-calendar-ok-btn[disabled]:hover>a:only-child:after,.ant-calendar .ant-calendar-ok-btn[disabled]>a:only-child:after{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;content:""}.ant-calendar-range-picker-input{width:44%;height:99%;text-align:center;background-color:transparent;border:0;outline:0}.ant-calendar-range-picker-input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-calendar-range-picker-input:-ms-input-placeholder{color:#bfbfbf}.ant-calendar-range-picker-input::-webkit-input-placeholder{color:#bfbfbf}.ant-calendar-range-picker-input:placeholder-shown{text-overflow:ellipsis}.ant-calendar-range-picker-input[disabled]{cursor:not-allowed}.ant-calendar-range-picker-separator{display:inline-block;min-width:10px;height:100%;color:rgba(0,0,0,.45);white-space:nowrap;text-align:center;vertical-align:top;pointer-events:none}.ant-calendar-range{width:552px;overflow:hidden}.ant-calendar-range .ant-calendar-date-panel:after{display:block;clear:both;height:0;visibility:hidden;content:"."}.ant-calendar-range-part{position:relative;width:50%}.ant-calendar-range-left{float:left}.ant-calendar-range-left .ant-calendar-time-picker-inner{border-right:1px solid #e8e8e8}.ant-calendar-range-right{float:right}.ant-calendar-range-right .ant-calendar-time-picker-inner{border-left:1px solid #e8e8e8}.ant-calendar-range-middle{position:absolute;left:50%;z-index:1;height:34px;margin:1px 0 0;padding:0 200px 0 0;color:rgba(0,0,0,.45);line-height:34px;text-align:center;transform:translateX(-50%);pointer-events:none}.ant-calendar-range-right .ant-calendar-date-input-wrap{margin-left:-90px}.ant-calendar-range.ant-calendar-time .ant-calendar-range-middle{padding:0 10px 0 0;transform:translateX(-50%)}.ant-calendar-range .ant-calendar-today :not(.ant-calendar-disabled-cell) :not(.ant-calendar-last-month-cell) :not(.ant-calendar-next-month-btn-day) .ant-calendar-date{color:#1890ff;background:#bae7ff;border-color:#1890ff}.ant-calendar-range .ant-calendar-selected-end-date .ant-calendar-date,.ant-calendar-range .ant-calendar-selected-start-date .ant-calendar-date{color:#fff;background:#1890ff;border:1px solid transparent}.ant-calendar-range .ant-calendar-selected-end-date .ant-calendar-date:hover,.ant-calendar-range .ant-calendar-selected-start-date .ant-calendar-date:hover{background:#1890ff}.ant-calendar-range.ant-calendar-time .ant-calendar-range-right .ant-calendar-date-input-wrap{margin-left:0}.ant-calendar-range .ant-calendar-input-wrap{position:relative;height:34px}.ant-calendar-range .ant-calendar-input,.ant-calendar-range .ant-calendar-time-picker-input{position:relative;display:inline-block;width:100%;height:32px;color:rgba(0,0,0,.65);font-size:14px;line-height:1.5;background-color:#fff;background-image:none;border-radius:4px;transition:all .3s;height:24px;padding:4px 0;line-height:24px;border:0;box-shadow:none}.ant-calendar-range .ant-calendar-input::-moz-placeholder,.ant-calendar-range .ant-calendar-time-picker-input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-calendar-range .ant-calendar-input:-ms-input-placeholder,.ant-calendar-range .ant-calendar-time-picker-input:-ms-input-placeholder{color:#bfbfbf}.ant-calendar-range .ant-calendar-input::-webkit-input-placeholder,.ant-calendar-range .ant-calendar-time-picker-input::-webkit-input-placeholder{color:#bfbfbf}.ant-calendar-range .ant-calendar-input:placeholder-shown,.ant-calendar-range .ant-calendar-time-picker-input:placeholder-shown{text-overflow:ellipsis}.ant-calendar-range .ant-calendar-input:hover,.ant-calendar-range .ant-calendar-time-picker-input:hover{border-color:#40a9ff;border-right-width:1px!important}.ant-calendar-range .ant-calendar-input:focus,.ant-calendar-range .ant-calendar-time-picker-input:focus{border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-calendar-range .ant-calendar-input-disabled,.ant-calendar-range .ant-calendar-time-picker-input-disabled{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-calendar-range .ant-calendar-input-disabled:hover,.ant-calendar-range .ant-calendar-time-picker-input-disabled:hover{border-color:#d9d9d9;border-right-width:1px!important}.ant-calendar-range .ant-calendar-input[disabled],.ant-calendar-range .ant-calendar-time-picker-input[disabled]{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-calendar-range .ant-calendar-input[disabled]:hover,.ant-calendar-range .ant-calendar-time-picker-input[disabled]:hover{border-color:#d9d9d9;border-right-width:1px!important}textarea.ant-calendar-range .ant-calendar-input,textarea.ant-calendar-range .ant-calendar-time-picker-input{max-width:100%;height:auto;min-height:32px;line-height:1.5;vertical-align:bottom;transition:all .3s,height 0s}.ant-calendar-range .ant-calendar-input-lg,.ant-calendar-range .ant-calendar-time-picker-input-lg{height:40px;padding:6px 11px;font-size:16px}.ant-calendar-range .ant-calendar-input-sm,.ant-calendar-range .ant-calendar-time-picker-input-sm{height:24px;padding:1px 7px}.ant-calendar-range .ant-calendar-input:focus,.ant-calendar-range .ant-calendar-time-picker-input:focus{box-shadow:none}.ant-calendar-range .ant-calendar-time-picker-icon{display:none}.ant-calendar-range.ant-calendar-week-number{width:574px}.ant-calendar-range.ant-calendar-week-number .ant-calendar-range-part{width:286px}.ant-calendar-range .ant-calendar-decade-panel,.ant-calendar-range .ant-calendar-month-panel,.ant-calendar-range .ant-calendar-year-panel{top:34px}.ant-calendar-range .ant-calendar-month-panel .ant-calendar-year-panel{top:0}.ant-calendar-range .ant-calendar-decade-panel-table,.ant-calendar-range .ant-calendar-month-panel-table,.ant-calendar-range .ant-calendar-year-panel-table{height:208px}.ant-calendar-range .ant-calendar-in-range-cell{position:relative;border-radius:0}.ant-calendar-range .ant-calendar-in-range-cell>div{position:relative;z-index:1}.ant-calendar-range .ant-calendar-in-range-cell:before{position:absolute;top:4px;right:0;bottom:4px;left:0;display:block;background:#e6f7ff;border:0;border-radius:0;content:""}.ant-calendar-range .ant-calendar-footer-extra{float:left}div.ant-calendar-range-quick-selector{text-align:left}div.ant-calendar-range-quick-selector>a{margin-right:8px}.ant-calendar-range .ant-calendar-decade-panel-header,.ant-calendar-range .ant-calendar-header,.ant-calendar-range .ant-calendar-month-panel-header,.ant-calendar-range .ant-calendar-year-panel-header{border-bottom:0}.ant-calendar-range .ant-calendar-body,.ant-calendar-range .ant-calendar-decade-panel-body,.ant-calendar-range .ant-calendar-month-panel-body,.ant-calendar-range .ant-calendar-year-panel-body{border-top:1px solid #e8e8e8}.ant-calendar-range.ant-calendar-time .ant-calendar-time-picker{top:68px;z-index:2;width:100%;height:207px}.ant-calendar-range.ant-calendar-time .ant-calendar-time-picker-panel{height:267px;margin-top:-34px}.ant-calendar-range.ant-calendar-time .ant-calendar-time-picker-inner{height:100%;padding-top:40px;background:none}.ant-calendar-range.ant-calendar-time .ant-calendar-time-picker-combobox{display:inline-block;height:100%;background-color:#fff;border-top:1px solid #e8e8e8}.ant-calendar-range.ant-calendar-time .ant-calendar-time-picker-select{height:100%}.ant-calendar-range.ant-calendar-time .ant-calendar-time-picker-select ul{max-height:100%}.ant-calendar-range.ant-calendar-time .ant-calendar-footer .ant-calendar-time-picker-btn{margin-right:8px}.ant-calendar-range.ant-calendar-time .ant-calendar-today-btn{height:22px;margin:8px 12px;line-height:22px}.ant-calendar-range-with-ranges.ant-calendar-time .ant-calendar-time-picker{height:233px}.ant-calendar-range.ant-calendar-show-time-picker .ant-calendar-body{border-top-color:transparent}.ant-calendar-time-picker{position:absolute;top:40px;width:100%;background-color:#fff}.ant-calendar-time-picker-panel{position:absolute;z-index:1050;width:100%}.ant-calendar-time-picker-inner{position:relative;display:inline-block;width:100%;overflow:hidden;font-size:14px;line-height:1.5;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;outline:none}.ant-calendar-time-picker-combobox{width:100%}.ant-calendar-time-picker-column-1,.ant-calendar-time-picker-column-1 .ant-calendar-time-picker-select{width:100%}.ant-calendar-time-picker-column-2 .ant-calendar-time-picker-select{width:50%}.ant-calendar-time-picker-column-3 .ant-calendar-time-picker-select{width:33.33%}.ant-calendar-time-picker-column-4 .ant-calendar-time-picker-select{width:25%}.ant-calendar-time-picker-input-wrap{display:none}.ant-calendar-time-picker-select{position:relative;float:left;height:226px;overflow:hidden;font-size:14px;border-right:1px solid #e8e8e8}.ant-calendar-time-picker-select:hover{overflow-y:auto}.ant-calendar-time-picker-select:first-child{margin-left:0;border-left:0}.ant-calendar-time-picker-select:last-child{border-right:0}.ant-calendar-time-picker-select ul{width:100%;max-height:206px;margin:0;padding:0;list-style:none}.ant-calendar-time-picker-select li{width:100%;height:24px;margin:0;line-height:24px;text-align:center;list-style:none;cursor:pointer;transition:all .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-calendar-time-picker-select li:last-child:after{display:block;height:202px;content:""}.ant-calendar-time-picker-select li:hover{background:#e6f7ff}.ant-calendar-time-picker-select li:focus{color:#1890ff;font-weight:600;outline:none}li.ant-calendar-time-picker-select-option-selected{font-weight:600;background:#f5f5f5}li.ant-calendar-time-picker-select-option-disabled{color:rgba(0,0,0,.25)}li.ant-calendar-time-picker-select-option-disabled:hover{background:transparent;cursor:not-allowed}.ant-calendar-time .ant-calendar-day-select{display:inline-block;padding:0 2px;color:rgba(0,0,0,.85);font-weight:500;line-height:34px}.ant-calendar-time .ant-calendar-footer{position:relative;height:auto}.ant-calendar-time .ant-calendar-footer-btn{text-align:right}.ant-calendar-time .ant-calendar-footer .ant-calendar-today-btn{float:left;margin:0}.ant-calendar-time .ant-calendar-footer .ant-calendar-time-picker-btn{display:inline-block;margin-right:8px}.ant-calendar-time .ant-calendar-footer .ant-calendar-time-picker-btn-disabled{color:rgba(0,0,0,.25)}.ant-calendar-month-panel{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;background:#fff;border-radius:4px;outline:none}.ant-calendar-month-panel>div{display:flex;flex-direction:column;height:100%}.ant-calendar-month-panel-hidden{display:none}.ant-calendar-month-panel-header{height:40px;line-height:40px;text-align:center;border-bottom:1px solid #e8e8e8;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative}.ant-calendar-month-panel-header a:hover{color:#40a9ff}.ant-calendar-month-panel-header .ant-calendar-month-panel-century-select,.ant-calendar-month-panel-header .ant-calendar-month-panel-decade-select,.ant-calendar-month-panel-header .ant-calendar-month-panel-month-select,.ant-calendar-month-panel-header .ant-calendar-month-panel-year-select{display:inline-block;padding:0 2px;color:rgba(0,0,0,.85);font-weight:500;line-height:40px}.ant-calendar-month-panel-header .ant-calendar-month-panel-century-select-arrow,.ant-calendar-month-panel-header .ant-calendar-month-panel-decade-select-arrow,.ant-calendar-month-panel-header .ant-calendar-month-panel-month-select-arrow,.ant-calendar-month-panel-header .ant-calendar-month-panel-year-select-arrow{display:none}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn{position:absolute;top:0;display:inline-block;padding:0 5px;color:rgba(0,0,0,.45);font-size:16px;font-family:Arial,Hiragino Sans GB,Microsoft Yahei,"Microsoft Sans Serif",sans-serif;line-height:40px}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn{left:7px;height:100%}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn:hover:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn:hover:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn:after{display:none}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-year-btn:after{position:relative;left:-3px;display:inline-block}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn{right:7px;height:100%}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:hover:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:hover:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:after{display:none}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:before,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:before{position:relative;left:3px}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-century-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-decade-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-year-btn:after{display:inline-block}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn{left:29px;height:100%}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-month-panel-header .ant-calendar-month-panel-prev-month-btn:after{display:none}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn{right:29px;height:100%}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:hover:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:after{display:none}.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:after,.ant-calendar-month-panel-header .ant-calendar-month-panel-next-month-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-month-panel-body{flex:1 1}.ant-calendar-month-panel-footer{border-top:1px solid #e8e8e8}.ant-calendar-month-panel-footer .ant-calendar-footer-extra{padding:0 12px}.ant-calendar-month-panel-table{width:100%;height:100%;table-layout:fixed;border-collapse:separate}.ant-calendar-month-panel-selected-cell .ant-calendar-month-panel-month{color:#fff;background:#1890ff}.ant-calendar-month-panel-selected-cell .ant-calendar-month-panel-month:hover{color:#fff;background:#1890ff}.ant-calendar-month-panel-cell{text-align:center}.ant-calendar-month-panel-cell-disabled .ant-calendar-month-panel-month,.ant-calendar-month-panel-cell-disabled .ant-calendar-month-panel-month:hover{color:rgba(0,0,0,.25);background:#f5f5f5;cursor:not-allowed}.ant-calendar-month-panel-month{display:inline-block;height:24px;margin:0 auto;padding:0 8px;color:rgba(0,0,0,.65);line-height:24px;text-align:center;background:transparent;border-radius:2px;transition:background .3s ease}.ant-calendar-month-panel-month:hover{background:#e6f7ff;cursor:pointer}.ant-calendar-year-panel{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;background:#fff;border-radius:4px;outline:none}.ant-calendar-year-panel>div{display:flex;flex-direction:column;height:100%}.ant-calendar-year-panel-hidden{display:none}.ant-calendar-year-panel-header{height:40px;line-height:40px;text-align:center;border-bottom:1px solid #e8e8e8;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative}.ant-calendar-year-panel-header a:hover{color:#40a9ff}.ant-calendar-year-panel-header .ant-calendar-year-panel-century-select,.ant-calendar-year-panel-header .ant-calendar-year-panel-decade-select,.ant-calendar-year-panel-header .ant-calendar-year-panel-month-select,.ant-calendar-year-panel-header .ant-calendar-year-panel-year-select{display:inline-block;padding:0 2px;color:rgba(0,0,0,.85);font-weight:500;line-height:40px}.ant-calendar-year-panel-header .ant-calendar-year-panel-century-select-arrow,.ant-calendar-year-panel-header .ant-calendar-year-panel-decade-select-arrow,.ant-calendar-year-panel-header .ant-calendar-year-panel-month-select-arrow,.ant-calendar-year-panel-header .ant-calendar-year-panel-year-select-arrow{display:none}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn{position:absolute;top:0;display:inline-block;padding:0 5px;color:rgba(0,0,0,.45);font-size:16px;font-family:Arial,Hiragino Sans GB,Microsoft Yahei,"Microsoft Sans Serif",sans-serif;line-height:40px}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn{left:7px;height:100%}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn:hover:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn:hover:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn:after{display:none}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-year-btn:after{position:relative;left:-3px;display:inline-block}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn{right:7px;height:100%}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:hover:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:hover:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:after{display:none}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:before,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:before{position:relative;left:3px}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-century-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-decade-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-year-btn:after{display:inline-block}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn{left:29px;height:100%}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-year-panel-header .ant-calendar-year-panel-prev-month-btn:after{display:none}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn{right:29px;height:100%}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:hover:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:after{display:none}.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:after,.ant-calendar-year-panel-header .ant-calendar-year-panel-next-month-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-year-panel-body{flex:1 1}.ant-calendar-year-panel-footer{border-top:1px solid #e8e8e8}.ant-calendar-year-panel-footer .ant-calendar-footer-extra{padding:0 12px}.ant-calendar-year-panel-table{width:100%;height:100%;table-layout:fixed;border-collapse:separate}.ant-calendar-year-panel-cell{text-align:center}.ant-calendar-year-panel-year{display:inline-block;height:24px;margin:0 auto;padding:0 8px;color:rgba(0,0,0,.65);line-height:24px;text-align:center;background:transparent;border-radius:2px;transition:background .3s ease}.ant-calendar-year-panel-year:hover{background:#e6f7ff;cursor:pointer}.ant-calendar-year-panel-selected-cell .ant-calendar-year-panel-year{color:#fff;background:#1890ff}.ant-calendar-year-panel-selected-cell .ant-calendar-year-panel-year:hover{color:#fff;background:#1890ff}.ant-calendar-year-panel-last-decade-cell .ant-calendar-year-panel-year,.ant-calendar-year-panel-next-decade-cell .ant-calendar-year-panel-year{color:rgba(0,0,0,.25);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-calendar-decade-panel{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;flex-direction:column;background:#fff;border-radius:4px;outline:none}.ant-calendar-decade-panel-hidden{display:none}.ant-calendar-decade-panel-header{height:40px;line-height:40px;text-align:center;border-bottom:1px solid #e8e8e8;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative}.ant-calendar-decade-panel-header a:hover{color:#40a9ff}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-century-select,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-decade-select,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-month-select,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-year-select{display:inline-block;padding:0 2px;color:rgba(0,0,0,.85);font-weight:500;line-height:40px}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-century-select-arrow,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-decade-select-arrow,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-month-select-arrow,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-year-select-arrow{display:none}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn{position:absolute;top:0;display:inline-block;padding:0 5px;color:rgba(0,0,0,.45);font-size:16px;font-family:Arial,Hiragino Sans GB,Microsoft Yahei,"Microsoft Sans Serif",sans-serif;line-height:40px}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn{left:7px;height:100%}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn:hover:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn:hover:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn:after{display:none}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-year-btn:after{position:relative;left:-3px;display:inline-block}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn{right:7px;height:100%}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:hover:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:hover:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:after{display:none}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:before,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:before{position:relative;left:3px}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-century-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-decade-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-year-btn:after{display:inline-block}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn{left:29px;height:100%}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-prev-month-btn:after{display:none}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn{right:29px;height:100%}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:before{position:relative;top:-1px;display:inline-block;width:8px;height:8px;vertical-align:middle;border:0 solid #aaa;border-width:1.5px 0 0 1.5px;border-radius:1px;transform:rotate(-45deg) scale(.8);transition:all .3s;content:""}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:hover:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:hover:before{border-color:rgba(0,0,0,.65)}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:after{display:none}.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:after,.ant-calendar-decade-panel-header .ant-calendar-decade-panel-next-month-btn:before{transform:rotate(135deg) scale(.8)}.ant-calendar-decade-panel-body{flex:1 1}.ant-calendar-decade-panel-footer{border-top:1px solid #e8e8e8}.ant-calendar-decade-panel-footer .ant-calendar-footer-extra{padding:0 12px}.ant-calendar-decade-panel-table{width:100%;height:100%;table-layout:fixed;border-collapse:separate}.ant-calendar-decade-panel-cell{white-space:nowrap;text-align:center}.ant-calendar-decade-panel-decade{display:inline-block;height:24px;margin:0 auto;padding:0 6px;color:rgba(0,0,0,.65);line-height:24px;text-align:center;background:transparent;border-radius:2px;transition:background .3s ease}.ant-calendar-decade-panel-decade:hover{background:#e6f7ff;cursor:pointer}.ant-calendar-decade-panel-selected-cell .ant-calendar-decade-panel-decade{color:#fff;background:#1890ff}.ant-calendar-decade-panel-selected-cell .ant-calendar-decade-panel-decade:hover{color:#fff;background:#1890ff}.ant-calendar-decade-panel-last-century-cell .ant-calendar-decade-panel-decade,.ant-calendar-decade-panel-next-century-cell .ant-calendar-decade-panel-decade{color:rgba(0,0,0,.25);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-calendar-month .ant-calendar-month-header-wrap{position:relative;height:288px}.ant-calendar-month .ant-calendar-month-panel,.ant-calendar-month .ant-calendar-year-panel{top:0;height:100%}.ant-calendar-week-number-cell{opacity:.5}.ant-calendar-week-number .ant-calendar-body tr{cursor:pointer;transition:all .3s}.ant-calendar-week-number .ant-calendar-body tr:hover{background:#e6f7ff}.ant-calendar-week-number .ant-calendar-body tr.ant-calendar-active-week{font-weight:700;background:#bae7ff}.ant-calendar-week-number .ant-calendar-body tr .ant-calendar-selected-day .ant-calendar-date,.ant-calendar-week-number .ant-calendar-body tr .ant-calendar-selected-day:hover .ant-calendar-date{color:rgba(0,0,0,.65);background:transparent}
+.ant-time-picker-panel{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:absolute;z-index:1050;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}.ant-time-picker-panel-inner{position:relative;left:-2px;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border-radius:4px;outline:none;box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-time-picker-panel-input{width:100%;max-width:154px;margin:0;padding:0;line-height:normal;border:0;outline:0;cursor:auto}.ant-time-picker-panel-input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-time-picker-panel-input:-ms-input-placeholder{color:#bfbfbf}.ant-time-picker-panel-input::-webkit-input-placeholder{color:#bfbfbf}.ant-time-picker-panel-input:placeholder-shown{text-overflow:ellipsis}.ant-time-picker-panel-input-wrap{position:relative;padding:7px 2px 7px 12px;border-bottom:1px solid #e8e8e8}.ant-time-picker-panel-input-invalid{border-color:#f5222d}.ant-time-picker-panel-narrow .ant-time-picker-panel-input-wrap{max-width:112px}.ant-time-picker-panel-select{position:relative;float:left;width:56px;max-height:192px;overflow:hidden;font-size:14px;border-left:1px solid #e8e8e8}.ant-time-picker-panel-select:hover{overflow-y:auto}.ant-time-picker-panel-select:first-child{margin-left:0;border-left:0}.ant-time-picker-panel-select:last-child{border-right:0}.ant-time-picker-panel-select:only-child{width:100%}.ant-time-picker-panel-select ul{width:56px;margin:0;padding:0 0 160px;list-style:none}.ant-time-picker-panel-select li{width:100%;height:32px;margin:0;padding:0 0 0 12px;line-height:32px;text-align:left;list-style:none;cursor:pointer;transition:all .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-time-picker-panel-select li:focus{color:#1890ff;font-weight:600;outline:none}.ant-time-picker-panel-select li:hover{background:#e6f7ff}li.ant-time-picker-panel-select-option-selected{font-weight:600;background:#f5f5f5}li.ant-time-picker-panel-select-option-selected:hover{background:#f5f5f5}li.ant-time-picker-panel-select-option-disabled{color:rgba(0,0,0,.25)}li.ant-time-picker-panel-select-option-disabled:hover{background:transparent;cursor:not-allowed}li.ant-time-picker-panel-select-option-disabled:focus{color:rgba(0,0,0,.25);font-weight:inherit}.ant-time-picker-panel-combobox{zoom:1}.ant-time-picker-panel-combobox:after,.ant-time-picker-panel-combobox:before{display:table;content:""}.ant-time-picker-panel-combobox:after{clear:both}.ant-time-picker-panel-addon{padding:8px;border-top:1px solid #e8e8e8}.ant-time-picker-panel.slide-up-appear.slide-up-appear-active.ant-time-picker-panel-placement-topLeft,.ant-time-picker-panel.slide-up-appear.slide-up-appear-active.ant-time-picker-panel-placement-topRight,.ant-time-picker-panel.slide-up-enter.slide-up-enter-active.ant-time-picker-panel-placement-topLeft,.ant-time-picker-panel.slide-up-enter.slide-up-enter-active.ant-time-picker-panel-placement-topRight{animation-name:antSlideDownIn}.ant-time-picker-panel.slide-up-appear.slide-up-appear-active.ant-time-picker-panel-placement-bottomLeft,.ant-time-picker-panel.slide-up-appear.slide-up-appear-active.ant-time-picker-panel-placement-bottomRight,.ant-time-picker-panel.slide-up-enter.slide-up-enter-active.ant-time-picker-panel-placement-bottomLeft,.ant-time-picker-panel.slide-up-enter.slide-up-enter-active.ant-time-picker-panel-placement-bottomRight{animation-name:antSlideUpIn}.ant-time-picker-panel.slide-up-leave.slide-up-leave-active.ant-time-picker-panel-placement-topLeft,.ant-time-picker-panel.slide-up-leave.slide-up-leave-active.ant-time-picker-panel-placement-topRight{animation-name:antSlideDownOut}.ant-time-picker-panel.slide-up-leave.slide-up-leave-active.ant-time-picker-panel-placement-bottomLeft,.ant-time-picker-panel.slide-up-leave.slide-up-leave-active.ant-time-picker-panel-placement-bottomRight{animation-name:antSlideUpOut}.ant-time-picker{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;width:128px;outline:none;cursor:text;transition:opacity .3s}.ant-time-picker-input{position:relative;display:inline-block;width:100%;height:32px;padding:4px 11px;color:rgba(0,0,0,.65);font-size:14px;line-height:1.5;background-color:#fff;background-image:none;border:1px solid #d9d9d9;border-radius:4px;transition:all .3s}.ant-time-picker-input::-moz-placeholder{color:#bfbfbf;opacity:1}.ant-time-picker-input:-ms-input-placeholder{color:#bfbfbf}.ant-time-picker-input::-webkit-input-placeholder{color:#bfbfbf}.ant-time-picker-input:placeholder-shown{text-overflow:ellipsis}.ant-time-picker-input:hover{border-color:#40a9ff;border-right-width:1px!important}.ant-time-picker-input:focus{border-color:#40a9ff;border-right-width:1px!important;outline:0;box-shadow:0 0 0 2px rgba(24,144,255,.2)}.ant-time-picker-input-disabled{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-time-picker-input-disabled:hover{border-color:#d9d9d9;border-right-width:1px!important}textarea.ant-time-picker-input{max-width:100%;height:auto;min-height:32px;line-height:1.5;vertical-align:bottom;transition:all .3s,height 0s}.ant-time-picker-input-lg{height:40px;padding:6px 11px;font-size:16px}.ant-time-picker-input-sm{height:24px;padding:1px 7px}.ant-time-picker-input[disabled]{color:rgba(0,0,0,.25);background-color:#f5f5f5;cursor:not-allowed;opacity:1}.ant-time-picker-input[disabled]:hover{border-color:#d9d9d9;border-right-width:1px!important}.ant-time-picker-open{opacity:0}.ant-time-picker-clear,.ant-time-picker-icon{position:absolute;top:50%;right:11px;z-index:1;width:14px;height:14px;margin-top:-7px;color:rgba(0,0,0,.25);line-height:14px;transition:all .3s cubic-bezier(.645,.045,.355,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ant-time-picker-clear .ant-time-picker-clock-icon,.ant-time-picker-icon .ant-time-picker-clock-icon{display:block;color:rgba(0,0,0,.25);line-height:1}.ant-time-picker-clear{z-index:2;background:#fff;opacity:0;pointer-events:none}.ant-time-picker-clear:hover{color:rgba(0,0,0,.45)}.ant-time-picker:hover .ant-time-picker-clear{opacity:1;pointer-events:auto}.ant-time-picker-large .ant-time-picker-input{height:40px;padding:6px 11px;font-size:16px}.ant-time-picker-small .ant-time-picker-input{height:24px;padding:1px 7px}.ant-time-picker-small .ant-time-picker-clear,.ant-time-picker-small .ant-time-picker-icon{right:7px}@media not all and (min-resolution:0.001dpcm){@supports (-webkit-appearance:none) and (stroke-color:transparent){.ant-input{line-height:1.5}}}
+.ant-tag{box-sizing:border-box;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";display:inline-block;height:auto;margin:0 8px 0 0;padding:0 7px;font-size:12px;line-height:20px;white-space:nowrap;background:#fafafa;border:1px solid #d9d9d9;border-radius:4px;cursor:default;opacity:1;transition:all .3s cubic-bezier(.78,.14,.15,.86)}.ant-tag:hover{opacity:.85}.ant-tag,.ant-tag a,.ant-tag a:hover{color:rgba(0,0,0,.65)}.ant-tag>a:first-child:last-child{display:inline-block;margin:0 -8px;padding:0 8px}.ant-tag .anticon-close{display:inline-block;font-size:12px;font-size:10px\9;transform:scale(.83333333) rotate(0deg);margin-left:3px;color:rgba(0,0,0,.45);font-weight:700;cursor:pointer;transition:all .3s cubic-bezier(.78,.14,.15,.86)}:root .ant-tag .anticon-close{font-size:12px}.ant-tag .anticon-close:hover{color:rgba(0,0,0,.85)}.ant-tag-has-color{border-color:transparent}.ant-tag-has-color,.ant-tag-has-color .anticon-close,.ant-tag-has-color .anticon-close:hover,.ant-tag-has-color a,.ant-tag-has-color a:hover{color:#fff}.ant-tag-checkable{background-color:transparent;border-color:transparent}.ant-tag-checkable:not(.ant-tag-checkable-checked):hover{color:#1890ff}.ant-tag-checkable-checked,.ant-tag-checkable:active{color:#fff}.ant-tag-checkable-checked{background-color:#1890ff}.ant-tag-checkable:active{background-color:#096dd9}.ant-tag-hidden{display:none}.ant-tag-pink{color:#eb2f96;background:#fff0f6;border-color:#ffadd2}.ant-tag-pink-inverse{color:#fff;background:#eb2f96;border-color:#eb2f96}.ant-tag-magenta{color:#eb2f96;background:#fff0f6;border-color:#ffadd2}.ant-tag-magenta-inverse{color:#fff;background:#eb2f96;border-color:#eb2f96}.ant-tag-red{color:#f5222d;background:#fff1f0;border-color:#ffa39e}.ant-tag-red-inverse{color:#fff;background:#f5222d;border-color:#f5222d}.ant-tag-volcano{color:#fa541c;background:#fff2e8;border-color:#ffbb96}.ant-tag-volcano-inverse{color:#fff;background:#fa541c;border-color:#fa541c}.ant-tag-orange{color:#fa8c16;background:#fff7e6;border-color:#ffd591}.ant-tag-orange-inverse{color:#fff;background:#fa8c16;border-color:#fa8c16}.ant-tag-yellow{color:#fadb14;background:#feffe6;border-color:#fffb8f}.ant-tag-yellow-inverse{color:#fff;background:#fadb14;border-color:#fadb14}.ant-tag-gold{color:#faad14;background:#fffbe6;border-color:#ffe58f}.ant-tag-gold-inverse{color:#fff;background:#faad14;border-color:#faad14}.ant-tag-cyan{color:#13c2c2;background:#e6fffb;border-color:#87e8de}.ant-tag-cyan-inverse{color:#fff;background:#13c2c2;border-color:#13c2c2}.ant-tag-lime{color:#a0d911;background:#fcffe6;border-color:#eaff8f}.ant-tag-lime-inverse{color:#fff;background:#a0d911;border-color:#a0d911}.ant-tag-green{color:#52c41a;background:#f6ffed;border-color:#b7eb8f}.ant-tag-green-inverse{color:#fff;background:#52c41a;border-color:#52c41a}.ant-tag-blue{color:#1890ff;background:#e6f7ff;border-color:#91d5ff}.ant-tag-blue-inverse{color:#fff;background:#1890ff;border-color:#1890ff}.ant-tag-geekblue{color:#2f54eb;background:#f0f5ff;border-color:#adc6ff}.ant-tag-geekblue-inverse{color:#fff;background:#2f54eb;border-color:#2f54eb}.ant-tag-purple{color:#722ed1;background:#f9f0ff;border-color:#d3adf7}.ant-tag-purple-inverse{color:#fff;background:#722ed1;border-color:#722ed1}
+.ant-drawer{position:fixed;z-index:1000;width:0;height:100%;transition:transform .3s cubic-bezier(.7,.3,.1,1),height 0s ease .3s,width 0s ease .3s}.ant-drawer>*{transition:transform .3s cubic-bezier(.7,.3,.1,1),box-shadow .3s cubic-bezier(.7,.3,.1,1)}.ant-drawer-content-wrapper{position:absolute}.ant-drawer .ant-drawer-content{width:100%;height:100%}.ant-drawer-left,.ant-drawer-right{top:0;width:0;height:100%}.ant-drawer-left .ant-drawer-content-wrapper,.ant-drawer-right .ant-drawer-content-wrapper{height:100%}.ant-drawer-left.ant-drawer-open,.ant-drawer-right.ant-drawer-open{width:100%;transition:transform .3s cubic-bezier(.7,.3,.1,1)}.ant-drawer-left.ant-drawer-open.no-mask,.ant-drawer-right.ant-drawer-open.no-mask{width:0}.ant-drawer-left.ant-drawer-open .ant-drawer-content-wrapper{box-shadow:2px 0 8px rgba(0,0,0,.15)}.ant-drawer-right{right:0}.ant-drawer-right .ant-drawer-content-wrapper{right:0}.ant-drawer-right.ant-drawer-open .ant-drawer-content-wrapper{box-shadow:-2px 0 8px rgba(0,0,0,.15)}.ant-drawer-right.ant-drawer-open.no-mask{right:1px;transform:translateX(1px)}.ant-drawer-bottom,.ant-drawer-top{left:0;width:100%;height:0%}.ant-drawer-bottom .ant-drawer-content-wrapper,.ant-drawer-top .ant-drawer-content-wrapper{width:100%}.ant-drawer-bottom.ant-drawer-open,.ant-drawer-top.ant-drawer-open{height:100%;transition:transform .3s cubic-bezier(.7,.3,.1,1)}.ant-drawer-bottom.ant-drawer-open.no-mask,.ant-drawer-top.ant-drawer-open.no-mask{height:0%}.ant-drawer-top{top:0}.ant-drawer-top.ant-drawer-open .ant-drawer-content-wrapper{box-shadow:0 2px 8px rgba(0,0,0,.15)}.ant-drawer-bottom{bottom:0}.ant-drawer-bottom .ant-drawer-content-wrapper{bottom:0}.ant-drawer-bottom.ant-drawer-open .ant-drawer-content-wrapper{box-shadow:0 -2px 8px rgba(0,0,0,.15)}.ant-drawer-bottom.ant-drawer-open.no-mask{bottom:1px;transform:translateY(1px)}.ant-drawer.ant-drawer-open .ant-drawer-mask{height:100%;opacity:1;transition:none;animation:antdDrawerFadeIn .3s cubic-bezier(.7,.3,.1,1)}.ant-drawer-title{margin:0;color:rgba(0,0,0,.85);font-weight:500;font-size:16px;line-height:22px}.ant-drawer-content{position:relative;z-index:1;overflow:auto;background-color:#fff;background-clip:padding-box;border:0}.ant-drawer-close{position:absolute;top:0;right:0;z-index:10;display:block;width:56px;height:56px;padding:0;color:rgba(0,0,0,.45);font-weight:700;font-size:16px;font-style:normal;line-height:56px;text-align:center;text-transform:none;text-decoration:none;background:transparent;border:0;outline:0;cursor:pointer;transition:color .3s;text-rendering:auto}.ant-drawer-close:focus,.ant-drawer-close:hover{color:rgba(0,0,0,.75);text-decoration:none}.ant-drawer-header{position:relative;padding:16px 24px;color:rgba(0,0,0,.65);background:#fff;border-bottom:1px solid #e8e8e8;border-radius:4px 4px 0 0}.ant-drawer-header-no-title{color:rgba(0,0,0,.65);background:#fff}.ant-drawer-body{padding:24px;font-size:14px;line-height:1.5;word-wrap:break-word}.ant-drawer-wrapper-body{height:100%;overflow:auto}.ant-drawer-mask{position:absolute;top:0;left:0;width:100%;height:0;background-color:rgba(0,0,0,.45);opacity:0;filter:alpha(opacity=45);transition:opacity .3s linear,height 0s ease .3s}.ant-drawer-open-content{box-shadow:0 4px 12px rgba(0,0,0,.15)}@keyframes antdDrawerFadeIn{0%{opacity:0}to{opacity:1}}
+@font-face{font-family:rmel-iconfont;src:url(data:application/vnd.ms-fontobject;base64,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NS4BJyMOAd0VGwEBGxWRFRsbFWEcFBQcYRUbGxWRFRsBARsCK2EVGxsVkRUbAQEbKRySARsVYRwpGwEBGxWRFRsBHwEbFZEVGwEBGykcYRUbwwEbFWEcKRsBARsVkRUb/qscKRsBARsVkRUbGxUBtRQcYRUbGxWRFRsBARsAAAAAAAASAN4AAQAAAAAAAAAVAAAAAQAAAAAAAQANABUAAQAAAAAAAgAHACIAAQAAAAAAAwANACkAAQAAAAAABAANADYAAQAAAAAABQALAEMAAQAAAAAABgANAE4AAQAAAAAACgArAFsAAQAAAAAACwATAIYAAwABBAkAAAAqAJkAAwABBAkAAQAaAMMAAwABBAkAAgAOAN0AAwABBAkAAwAaAOsAAwABBAkABAAaAQUAAwABBAkABQAWAR8AAwABBAkABgAaATUAAwABBAkACgBWAU8AAwABBAkACwAmAaUKQ3JlYXRlZCBieSBpY29uZm9udApybWVsLWljb25mb250UmVndWxhcnJtZWwtaWNvbmZvbnRybWVsLWljb25mb250VmVyc2lvbiAxLjBybWVsLWljb25mb250R2VuZXJhdGVkIGJ5IHN2ZzJ0dGYgZnJvbSBGb250ZWxsbyBwcm9qZWN0Lmh0dHA6Ly9mb250ZWxsby5jb20ACgBDAHIAZQBhAHQAZQBkACAAYgB5ACAAaQBjAG8AbgBmAG8AbgB0AAoAcgBtAGUAbAAtAGkAYwBvAG4AZgBvAG4AdABSAGUAZwB1AGwAYQByAHIAbQBlAGwALQBpAGMAbwBuAGYAbwBuAHQAcgBtAGUAbAAtAGkAYwBvAG4AZgBvAG4AdABWAGUAcgBzAGkAbwBuACAAMQAuADAAcgBtAGUAbAAtAGkAYwBvAG4AZgBvAG4AdABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwADdGFiCGtleWJvYXJkBmRlbGV0ZQpjb2RlLWJsb2NrBGNvZGUKdmlzaWJpbGl0eQp2aWV3LXNwbGl0BGxpbmsEcmVkbwR1bmRvBGJvbGQGaXRhbGljDGxpc3Qtb3JkZXJlZA5saXN0LXVub3JkZXJlZAVxdW90ZQ1zdHJpa2V0aHJvdWdoCXVuZGVybGluZQR3cmFwCWZvbnQtc2l6ZQRncmlkBWltYWdlC2V4cGFuZC1sZXNzC2V4cGFuZC1tb3JlD2Z1bGxzY3JlZW4tZXhpdApmdWxsc2NyZWVuAAAAAAA=);src:url(data:font/ttf;base64,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) format("truetype")}.rmel-iconfont{font-family:rmel-iconfont!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.rmel-icon-tab:before{content:"\E76D"}.rmel-icon-keyboard:before{content:"\ED80"}.rmel-icon-delete:before{content:"\ED3C"}.rmel-icon-code-block:before{content:"\E941"}.rmel-icon-code:before{content:"\ED3B"}.rmel-icon-visibility:before{content:"\ED44"}.rmel-icon-view-split:before{content:"\ED45"}.rmel-icon-link:before{content:"\ED5F"}.rmel-icon-redo:before{content:"\ED60"}.rmel-icon-undo:before{content:"\ED61"}.rmel-icon-bold:before{content:"\ED6F"}.rmel-icon-italic:before{content:"\ED70"}.rmel-icon-list-ordered:before{content:"\ED71"}.rmel-icon-list-unordered:before{content:"\ED72"}.rmel-icon-quote:before{content:"\ED73"}.rmel-icon-strikethrough:before{content:"\ED74"}.rmel-icon-underline:before{content:"\ED75"}.rmel-icon-wrap:before{content:"\ED77"}.rmel-icon-font-size:before{content:"\ED78"}.rmel-icon-grid:before{content:"\ED8C"}.rmel-icon-image:before{content:"\ED8D"}.rmel-icon-expand-less:before{content:"\ED9F"}.rmel-icon-expand-more:before{content:"\EDA0"}.rmel-icon-fullscreen-exit:before{content:"\EDA1"}.rmel-icon-fullscreen:before{content:"\EDA2"}.rc-md-editor{padding-bottom:1px;position:relative;border:1px solid #e0e0e0;background:#fff;box-sizing:border-box;display:flex;flex-direction:column}.rc-md-editor.full{width:100%;height:100%!important;position:fixed;left:0;top:0;z-index:1000}.rc-md-editor .editor-container{flex:1 1;display:flex;width:100%;min-height:0;position:relative}.rc-md-editor .editor-container>.section{flex-grow:1;flex-shrink:1;flex-basis:1px;border-right:1px solid #e0e0e0}.rc-md-editor .editor-container>.section.in-visible{display:none}.rc-md-editor .editor-container>.section>.section-container{padding:10px 15px 15px}.rc-md-editor .editor-container>.section:last-child{border-radius:none}.rc-md-editor .editor-container .sec-md{min-height:0;min-width:0}.rc-md-editor .editor-container .sec-md .input{display:block;box-sizing:border-box;width:100%;height:100%;overflow-y:scroll;border:none;resize:none;outline:none;min-height:0;background:#fff;color:#333;font-size:14px;line-height:1.7}.rc-md-editor .editor-container .sec-html{min-height:0;min-width:0}.rc-md-editor .editor-container .sec-html .html-wrap{height:100%;box-sizing:border-box;overflow:auto}.custom-html-style{color:#333}.custom-html-style h1{font-size:32px;padding:0;border:none;font-weight:700;margin:32px 0;line-height:1.2}.custom-html-style h2{font-size:24px;padding:0;border:none;font-weight:700;margin:24px 0;line-height:1.7}.custom-html-style h3{font-size:18px;margin:18px 0;padding:0;line-height:1.7;border:none}.custom-html-style p{font-size:14px;line-height:1.7;margin:8px 0}.custom-html-style a{color:#0052d9}.custom-html-style a:hover{text-decoration:none}.custom-html-style strong{font-weight:700}.custom-html-style ol,.custom-html-style ul{font-size:14px;line-height:28px;padding-left:36px}.custom-html-style li{margin-bottom:8px;line-height:1.7}.custom-html-style hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.custom-html-style pre{display:block;padding:20px;line-height:28px;word-break:break-word}.custom-html-style code,.custom-html-style pre{background-color:#f5f5f5;font-size:14px;border-radius:0;overflow-x:auto}.custom-html-style code{padding:3px 0;margin:0;word-break:normal}.custom-html-style code:after,.custom-html-style code:before{letter-spacing:0}.custom-html-style blockquote{position:relative;margin:16px 0;padding:5px 8px 5px 30px;background:none repeat scroll 0 0 rgba(102,128,153,.05);color:#333;border:none;border-left:10px solid #d6dbdf}.custom-html-style img,.custom-html-style video{max-width:100%}.custom-html-style table{font-size:14px;line-height:1.7;max-width:100%;overflow:auto;border:1px solid #f6f6f6;border-collapse:collapse;border-spacing:0;box-sizing:border-box}.custom-html-style table td,.custom-html-style table th{word-break:break-all;word-wrap:break-word;white-space:normal}.custom-html-style table tr{border:1px solid #efefef}.custom-html-style table tr:nth-child(2n){background-color:transparent}.custom-html-style table th{text-align:center;font-weight:700;border:1px solid #efefef;padding:10px 6px;background-color:#f5f7fa;word-break:break-word}.custom-html-style table td{border:1px solid #efefef;text-align:left;padding:10px 15px;word-break:break-word;min-width:60px}.rc-md-editor .drop-wrap{display:block;position:absolute;left:0;top:28px;z-index:2;min-width:20px;padding:10px 0;text-align:center;background-color:#fff;border-color:#f1f1f1 #ddd #ddd #f1f1f1;border-style:solid;border-width:1px}.rc-md-editor .drop-wrap.hidden{display:none!important}.rc-md-editor .rc-md-navigation{min-height:38px;padding:0 8px;box-sizing:border-box;border-bottom:1px solid #e0e0e0;font-size:16px;background:#f5f5f5;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:row;justify-content:space-between}.rc-md-editor .rc-md-navigation.in-visible{display:none}.rc-md-editor .rc-md-navigation .navigation-nav{align-items:center;justify-content:center;font-size:14px;color:#757575}.rc-md-editor .rc-md-navigation .button-wrap,.rc-md-editor .rc-md-navigation .navigation-nav{display:flex;flex-direction:row}.rc-md-editor .rc-md-navigation .button-wrap{flex-wrap:wrap}.rc-md-editor .rc-md-navigation .button-wrap .button{position:relative;min-width:24px;height:28px;margin-left:3px;margin-right:3px;display:inline-block;cursor:pointer;line-height:28px;text-align:center;color:#757575}.rc-md-editor .rc-md-navigation .button-wrap .button:hover{color:#212121}.rc-md-editor .rc-md-navigation .button-wrap .button.disabled{color:#bdbdbd;cursor:not-allowed}.rc-md-editor .rc-md-navigation .button-wrap .button:first-child{margin-left:0}.rc-md-editor .rc-md-navigation .button-wrap .button:last-child{margin-right:0}.rc-md-editor .rc-md-navigation .button-wrap .rmel-iconfont{font-size:18px}.rc-md-editor .rc-md-navigation li,.rc-md-editor .rc-md-navigation ul{list-style:none;margin:0;padding:0}.rc-md-editor .rc-md-navigation .h1,.rc-md-editor .rc-md-navigation .h2,.rc-md-editor .rc-md-navigation .h3,.rc-md-editor .rc-md-navigation .h4,.rc-md-editor .rc-md-navigation .h5,.rc-md-editor .rc-md-navigation .h6,.rc-md-editor .rc-md-navigation h1,.rc-md-editor .rc-md-navigation h2,.rc-md-editor .rc-md-navigation h3,.rc-md-editor .rc-md-navigation h4,.rc-md-editor .rc-md-navigation h5,.rc-md-editor .rc-md-navigation h6{font-family:inherit;font-weight:500;color:inherit;padding:0;margin:0;line-height:1.1}.rc-md-editor .rc-md-navigation h1{font-size:34px}.rc-md-editor .rc-md-navigation h2{font-size:30px}.rc-md-editor .rc-md-navigation h3{font-size:24px}.rc-md-editor .rc-md-navigation h4{font-size:18px}.rc-md-editor .rc-md-navigation h5{font-size:14px}.rc-md-editor .rc-md-navigation h6{font-size:12px}.rc-md-editor .tool-bar{position:absolute;z-index:1;right:8px;top:8px}.rc-md-editor .tool-bar .button{min-width:24px;height:28px;margin-right:5px;display:inline-block;cursor:pointer;font-size:14px;line-height:28px;text-align:center;color:#999}.rc-md-editor .tool-bar .button:hover{color:#333}.rc-md-editor .rc-md-divider{display:block;width:1px;background-color:#e0e0e0}.rc-md-editor .table-list.wrap{position:relative;margin:0 10px;box-sizing:border-box}.rc-md-editor .table-list.wrap .list-item{position:absolute;top:0;left:0;display:inline-block;width:20px;height:20px;background-color:#e0e0e0;border-radius:3px}.rc-md-editor .table-list.wrap .list-item.active{background:#9e9e9e}.rc-md-editor .tab-map-list .list-item{width:120px;box-sizing:border-box}.rc-md-editor .tab-map-list .list-item:hover{background:#f5f5f5}.rc-md-editor .tab-map-list .list-item.active{font-weight:700}.rc-md-editor .header-list .list-item{width:100px;box-sizing:border-box;padding:8px 0}.rc-md-editor .header-list .list-item:hover{background:#f5f5f5}
+.ant-badge{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative;display:inline-block;color:unset;line-height:1}.ant-badge-count{min-width:20px;height:20px;padding:0 6px;color:#fff;font-weight:400;font-size:12px;line-height:20px;white-space:nowrap;text-align:center;background:#f5222d;border-radius:10px;box-shadow:0 0 0 1px #fff}.ant-badge-count a,.ant-badge-count a:hover{color:#fff}.ant-badge-multiple-words{padding:0 8px}.ant-badge-dot{width:6px;height:6px;background:#f5222d;border-radius:100%;box-shadow:0 0 0 1px #fff}.ant-badge-count,.ant-badge-dot,.ant-badge .ant-scroll-number-custom-component{position:absolute;top:0;right:0;z-index:1;transform:translate(50%,-50%);transform-origin:100% 0}.ant-badge-status{line-height:inherit;vertical-align:baseline}.ant-badge-status-dot{position:relative;top:-1px;display:inline-block;width:6px;height:6px;vertical-align:middle;border-radius:50%}.ant-badge-status-success{background-color:#52c41a}.ant-badge-status-processing{position:relative;background-color:#1890ff}.ant-badge-status-processing:after{position:absolute;top:0;left:0;width:100%;height:100%;border:1px solid #1890ff;border-radius:50%;animation:antStatusProcessing 1.2s ease-in-out infinite;content:""}.ant-badge-status-default{background-color:#d9d9d9}.ant-badge-status-error{background-color:#f5222d}.ant-badge-status-warning{background-color:#faad14}.ant-badge-status-pink{background:#eb2f96}.ant-badge-status-magenta{background:#eb2f96}.ant-badge-status-red{background:#f5222d}.ant-badge-status-volcano{background:#fa541c}.ant-badge-status-orange{background:#fa8c16}.ant-badge-status-yellow{background:#fadb14}.ant-badge-status-gold{background:#faad14}.ant-badge-status-cyan{background:#13c2c2}.ant-badge-status-lime{background:#a0d911}.ant-badge-status-green{background:#52c41a}.ant-badge-status-blue{background:#1890ff}.ant-badge-status-geekblue{background:#2f54eb}.ant-badge-status-purple{background:#722ed1}.ant-badge-status-text{margin-left:8px;color:rgba(0,0,0,.65);font-size:14px}.ant-badge-zoom-appear,.ant-badge-zoom-enter{animation:antZoomBadgeIn .3s cubic-bezier(.12,.4,.29,1.46);animation-fill-mode:both}.ant-badge-zoom-leave{animation:antZoomBadgeOut .3s cubic-bezier(.71,-.46,.88,.6);animation-fill-mode:both}.ant-badge-not-a-wrapper:not(.ant-badge-status){vertical-align:middle}.ant-badge-not-a-wrapper .ant-scroll-number{position:relative;top:auto;display:block}.ant-badge-not-a-wrapper .ant-badge-count{transform:none}@keyframes antStatusProcessing{0%{transform:scale(.8);opacity:.5}to{transform:scale(2.4);opacity:0}}.ant-scroll-number{overflow:hidden}.ant-scroll-number-only{display:inline-block;height:20px;transition:all .3s cubic-bezier(.645,.045,.355,1)}.ant-scroll-number-only>p.ant-scroll-number-only-unit{height:20px;margin:0}.ant-scroll-number-symbol{vertical-align:top}@keyframes antZoomBadgeIn{0%{transform:scale(0) translate(50%,-50%);opacity:0}to{transform:scale(1) translate(50%,-50%)}}@keyframes antZoomBadgeOut{0%{transform:scale(1) translate(50%,-50%)}to{transform:scale(0) translate(50%,-50%);opacity:0}}
+.ant-menu{box-sizing:border-box;font-size:14px;font-variant:tabular-nums;line-height:1.5;font-feature-settings:"tnum";margin:0;padding:0;color:rgba(0,0,0,.65);line-height:0;list-style:none;background:#fff;outline:none;box-shadow:0 2px 8px rgba(0,0,0,.15);transition:background .3s,width .2s;zoom:1}.ant-menu:after,.ant-menu:before{display:table;content:""}.ant-menu:after{clear:both}.ant-menu ol,.ant-menu ul{margin:0;padding:0;list-style:none}.ant-menu-hidden{display:none}.ant-menu-item-group-title{padding:8px 16px;color:rgba(0,0,0,.45);font-size:14px;line-height:1.5;transition:all .3s}.ant-menu-submenu,.ant-menu-submenu-inline{transition:border-color .3s cubic-bezier(.645,.045,.355,1),background .3s cubic-bezier(.645,.045,.355,1),padding .15s cubic-bezier(.645,.045,.355,1)}.ant-menu-submenu-selected{color:#1890ff}.ant-menu-item:active,.ant-menu-submenu-title:active{background:#e6f7ff}.ant-menu-submenu .ant-menu-sub{cursor:auto;transition:background .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.ant-menu-item>a{display:block;color:rgba(0,0,0,.65)}.ant-menu-item>a:hover{color:#1890ff}.ant-menu-item>a:before{position:absolute;top:0;right:0;bottom:0;left:0;background-color:transparent;content:""}.ant-menu-item>.ant-badge>a{color:rgba(0,0,0,.65)}.ant-menu-item>.ant-badge>a:hover{color:#1890ff}.ant-menu-item-divider{height:1px;overflow:hidden;line-height:0;background-color:#e8e8e8}.ant-menu-item-active,.ant-menu-item:hover,.ant-menu-submenu-active,.ant-menu-submenu-title:hover,.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open{color:#1890ff}.ant-menu-horizontal .ant-menu-item,.ant-menu-horizontal .ant-menu-submenu{margin-top:-1px}.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu .ant-menu-submenu-title:hover{background-color:transparent}.ant-menu-item-selected{color:#1890ff}.ant-menu-item-selected>a,.ant-menu-item-selected>a:hover{color:#1890ff}.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected{background-color:#e6f7ff}.ant-menu-inline,.ant-menu-vertical,.ant-menu-vertical-left{border-right:1px solid #e8e8e8}.ant-menu-vertical-right{border-left:1px solid #e8e8e8}.ant-menu-vertical-left.ant-menu-sub,.ant-menu-vertical-right.ant-menu-sub,.ant-menu-vertical.ant-menu-sub{min-width:160px;padding:0;border-right:0;transform-origin:0 0}.ant-menu-vertical-left.ant-menu-sub .ant-menu-item,.ant-menu-vertical-right.ant-menu-sub .ant-menu-item,.ant-menu-vertical.ant-menu-sub .ant-menu-item{left:0;margin-left:0;border-right:0}.ant-menu-vertical-left.ant-menu-sub .ant-menu-item:after,.ant-menu-vertical-right.ant-menu-sub .ant-menu-item:after,.ant-menu-vertical.ant-menu-sub .ant-menu-item:after{border-right:0}.ant-menu-vertical-left.ant-menu-sub>.ant-menu-item,.ant-menu-vertical-left.ant-menu-sub>.ant-menu-submenu,.ant-menu-vertical-right.ant-menu-sub>.ant-menu-item,.ant-menu-vertical-right.ant-menu-sub>.ant-menu-submenu,.ant-menu-vertical.ant-menu-sub>.ant-menu-item,.ant-menu-vertical.ant-menu-sub>.ant-menu-submenu{transform-origin:0 0}.ant-menu-horizontal.ant-menu-sub{min-width:114px}.ant-menu-item,.ant-menu-submenu-title{position:relative;display:block;margin:0;padding:0 20px;white-space:nowrap;cursor:pointer;transition:color .3s cubic-bezier(.645,.045,.355,1),border-color .3s cubic-bezier(.645,.045,.355,1),background .3s cubic-bezier(.645,.045,.355,1),padding .15s cubic-bezier(.645,.045,.355,1)}.ant-menu-item .anticon,.ant-menu-submenu-title .anticon{min-width:14px;margin-right:10px;font-size:14px;transition:font-size .15s cubic-bezier(.215,.61,.355,1),margin .3s cubic-bezier(.645,.045,.355,1)}.ant-menu-item .anticon+span,.ant-menu-submenu-title .anticon+span{opacity:1;transition:opacity .3s cubic-bezier(.645,.045,.355,1),width .3s cubic-bezier(.645,.045,.355,1)}.ant-menu>.ant-menu-item-divider{height:1px;margin:1px 0;padding:0;overflow:hidden;line-height:0;background-color:#e8e8e8}.ant-menu-submenu-popup{position:absolute;z-index:1050;background:#fff;border-radius:4px}.ant-menu-submenu-popup .submenu-title-wrapper{padding-right:20px}.ant-menu-submenu-popup:before{position:absolute;top:-7px;right:0;bottom:0;left:0;opacity:.0001;content:" "}.ant-menu-submenu>.ant-menu{background-color:#fff;border-radius:4px}.ant-menu-submenu>.ant-menu-submenu-title:after{transition:transform .3s cubic-bezier(.645,.045,.355,1)}.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title .ant-menu-submenu-arrow,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title .ant-menu-submenu-arrow,.ant-menu-submenu-vertical>.ant-menu-submenu-title .ant-menu-submenu-arrow{position:absolute;top:50%;right:16px;width:10px;transition:transform .3s cubic-bezier(.645,.045,.355,1)}.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical>.ant-menu-submenu-title .ant-menu-submenu-arrow:before{position:absolute;width:6px;height:1.5px;background:#fff;background:rgba(0,0,0,.65)\9;background-image:linear-gradient(90deg,rgba(0,0,0,.65),rgba(0,0,0,.65));background-image:none\9;border-radius:2px;transition:background .3s cubic-bezier(.645,.045,.355,1),transform .3s cubic-bezier(.645,.045,.355,1),top .3s cubic-bezier(.645,.045,.355,1);content:""}.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical>.ant-menu-submenu-title .ant-menu-submenu-arrow:before{transform:rotate(45deg) translateY(-2px)}.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical>.ant-menu-submenu-title .ant-menu-submenu-arrow:after{transform:rotate(-45deg) translateY(2px)}.ant-menu-submenu-inline>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:after,.ant-menu-submenu-inline>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical-left>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical-right>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:before,.ant-menu-submenu-vertical>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:after,.ant-menu-submenu-vertical>.ant-menu-submenu-title:hover .ant-menu-submenu-arrow:before{background:linear-gradient(90deg,#1890ff,#1890ff)}.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:before{transform:rotate(-45deg) translateX(2px)}.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:after{transform:rotate(45deg) translateX(-2px)}.ant-menu-submenu-open.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow{transform:translateY(-2px)}.ant-menu-submenu-open.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:after{transform:rotate(-45deg) translateX(-2px)}.ant-menu-submenu-open.ant-menu-submenu-inline>.ant-menu-submenu-title .ant-menu-submenu-arrow:before{transform:rotate(45deg) translateX(2px)}.ant-menu-vertical-left .ant-menu-submenu-selected,.ant-menu-vertical-right .ant-menu-submenu-selected,.ant-menu-vertical .ant-menu-submenu-selected{color:#1890ff}.ant-menu-vertical-left .ant-menu-submenu-selected>a,.ant-menu-vertical-right .ant-menu-submenu-selected>a,.ant-menu-vertical .ant-menu-submenu-selected>a{color:#1890ff}.ant-menu-horizontal{line-height:46px;white-space:nowrap;border:0;border-bottom:1px solid #e8e8e8;box-shadow:none}.ant-menu-horizontal>.ant-menu-item,.ant-menu-horizontal>.ant-menu-submenu{position:relative;top:1px;display:inline-block;vertical-align:bottom;border-bottom:2px solid transparent}.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover{color:#1890ff;border-bottom:2px solid #1890ff}.ant-menu-horizontal>.ant-menu-item>a{display:block;color:rgba(0,0,0,.65)}.ant-menu-horizontal>.ant-menu-item>a:hover{color:#1890ff}.ant-menu-horizontal>.ant-menu-item>a:before{bottom:-2px}.ant-menu-horizontal>.ant-menu-item-selected>a{color:#1890ff}.ant-menu-horizontal:after{display:block;clear:both;height:0;content:" "}.ant-menu-inline .ant-menu-item,.ant-menu-vertical-left .ant-menu-item,.ant-menu-vertical-right .ant-menu-item,.ant-menu-vertical .ant-menu-item{position:relative}.ant-menu-inline .ant-menu-item:after,.ant-menu-vertical-left .ant-menu-item:after,.ant-menu-vertical-right .ant-menu-item:after,.ant-menu-vertical .ant-menu-item:after{position:absolute;top:0;right:0;bottom:0;border-right:3px solid #1890ff;transform:scaleY(.0001);opacity:0;transition:transform .15s cubic-bezier(.215,.61,.355,1),opacity .15s cubic-bezier(.215,.61,.355,1);content:""}.ant-menu-inline .ant-menu-item,.ant-menu-inline .ant-menu-submenu-title,.ant-menu-vertical-left .ant-menu-item,.ant-menu-vertical-left .ant-menu-submenu-title,.ant-menu-vertical-right .ant-menu-item,.ant-menu-vertical-right .ant-menu-submenu-title,.ant-menu-vertical .ant-menu-item,.ant-menu-vertical .ant-menu-submenu-title{height:40px;margin-top:4px;margin-bottom:4px;padding:0 16px;overflow:hidden;font-size:14px;line-height:40px;text-overflow:ellipsis}.ant-menu-inline .ant-menu-submenu,.ant-menu-vertical-left .ant-menu-submenu,.ant-menu-vertical-right .ant-menu-submenu,.ant-menu-vertical .ant-menu-submenu{padding-bottom:.02px}.ant-menu-inline .ant-menu-item:not(:last-child),.ant-menu-vertical-left .ant-menu-item:not(:last-child),.ant-menu-vertical-right .ant-menu-item:not(:last-child),.ant-menu-vertical .ant-menu-item:not(:last-child){margin-bottom:8px}.ant-menu-inline>.ant-menu-item,.ant-menu-inline>.ant-menu-submenu>.ant-menu-submenu-title,.ant-menu-vertical-left>.ant-menu-item,.ant-menu-vertical-left>.ant-menu-submenu>.ant-menu-submenu-title,.ant-menu-vertical-right>.ant-menu-item,.ant-menu-vertical-right>.ant-menu-submenu>.ant-menu-submenu-title,.ant-menu-vertical>.ant-menu-item,.ant-menu-vertical>.ant-menu-submenu>.ant-menu-submenu-title{height:40px;line-height:40px}.ant-menu-inline{width:100%}.ant-menu-inline .ant-menu-item-selected:after,.ant-menu-inline .ant-menu-selected:after{transform:scaleY(1);opacity:1;transition:transform .15s cubic-bezier(.645,.045,.355,1),opacity .15s cubic-bezier(.645,.045,.355,1)}.ant-menu-inline .ant-menu-item,.ant-menu-inline .ant-menu-submenu-title{width:calc(100% + 1px)}.ant-menu-inline .ant-menu-submenu-title{padding-right:34px}.ant-menu-inline-collapsed{width:80px}.ant-menu-inline-collapsed>.ant-menu-item,.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item,.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title,.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title{left:0;padding:0 32px!important;text-overflow:clip}.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item .ant-menu-submenu-arrow,.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title .ant-menu-submenu-arrow,.ant-menu-inline-collapsed>.ant-menu-item .ant-menu-submenu-arrow,.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title .ant-menu-submenu-arrow{display:none}.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item .anticon,.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title .anticon,.ant-menu-inline-collapsed>.ant-menu-item .anticon,.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title .anticon{margin:0;font-size:16px;line-height:40px}.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item .anticon+span,.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title .anticon+span,.ant-menu-inline-collapsed>.ant-menu-item .anticon+span,.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title .anticon+span{display:inline-block;max-width:0;opacity:0}.ant-menu-inline-collapsed-tooltip{pointer-events:none}.ant-menu-inline-collapsed-tooltip .anticon{display:none}.ant-menu-inline-collapsed-tooltip a{color:hsla(0,0%,100%,.85)}.ant-menu-inline-collapsed .ant-menu-item-group-title{padding-right:4px;padding-left:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ant-menu-item-group-list{margin:0;padding:0}.ant-menu-item-group-list .ant-menu-item,.ant-menu-item-group-list .ant-menu-submenu-title{padding:0 16px 0 28px}.ant-menu-root.ant-menu-inline,.ant-menu-root.ant-menu-vertical,.ant-menu-root.ant-menu-vertical-left,.ant-menu-root.ant-menu-vertical-right{box-shadow:none}.ant-menu-sub.ant-menu-inline{padding:0;border:0;border-radius:0;box-shadow:none}.ant-menu-sub.ant-menu-inline>.ant-menu-item,.ant-menu-sub.ant-menu-inline>.ant-menu-submenu>.ant-menu-submenu-title{height:40px;line-height:40px;list-style-position:inside;list-style-type:disc}.ant-menu-sub.ant-menu-inline .ant-menu-item-group-title{padding-left:32px}.ant-menu-item-disabled,.ant-menu-submenu-disabled{color:rgba(0,0,0,.25)!important;background:none;border-color:transparent!important;cursor:not-allowed}.ant-menu-item-disabled>a,.ant-menu-submenu-disabled>a{color:rgba(0,0,0,.25)!important;pointer-events:none}.ant-menu-item-disabled>.ant-menu-submenu-title,.ant-menu-submenu-disabled>.ant-menu-submenu-title{color:rgba(0,0,0,.25)!important;cursor:not-allowed}.ant-menu-item-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-item-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-submenu-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-submenu-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before{background:rgba(0,0,0,.25)!important}.ant-menu-dark,.ant-menu-dark .ant-menu-sub{color:hsla(0,0%,100%,.65);background:#001529}.ant-menu-dark .ant-menu-sub .ant-menu-submenu-title .ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-title .ant-menu-submenu-arrow{opacity:.45;transition:all .3s}.ant-menu-dark .ant-menu-sub .ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-sub .ant-menu-submenu-title .ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-title .ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-title .ant-menu-submenu-arrow:before{background:#fff}.ant-menu-dark.ant-menu-submenu-popup{background:transparent}.ant-menu-dark .ant-menu-inline.ant-menu-sub{background:#000c17;box-shadow:inset 0 2px 8px rgba(0,0,0,.45)}.ant-menu-dark.ant-menu-horizontal{border-bottom:0}.ant-menu-dark.ant-menu-horizontal>.ant-menu-item,.ant-menu-dark.ant-menu-horizontal>.ant-menu-submenu{top:0;margin-top:0;border-color:#001529;border-bottom:0}.ant-menu-dark.ant-menu-horizontal>.ant-menu-item>a:before{bottom:0}.ant-menu-dark .ant-menu-item,.ant-menu-dark .ant-menu-item-group-title,.ant-menu-dark .ant-menu-item>a{color:hsla(0,0%,100%,.65)}.ant-menu-dark.ant-menu-inline,.ant-menu-dark.ant-menu-vertical,.ant-menu-dark.ant-menu-vertical-left,.ant-menu-dark.ant-menu-vertical-right{border-right:0}.ant-menu-dark.ant-menu-inline .ant-menu-item,.ant-menu-dark.ant-menu-vertical-left .ant-menu-item,.ant-menu-dark.ant-menu-vertical-right .ant-menu-item,.ant-menu-dark.ant-menu-vertical .ant-menu-item{left:0;margin-left:0;border-right:0}.ant-menu-dark.ant-menu-inline .ant-menu-item:after,.ant-menu-dark.ant-menu-vertical-left .ant-menu-item:after,.ant-menu-dark.ant-menu-vertical-right .ant-menu-item:after,.ant-menu-dark.ant-menu-vertical .ant-menu-item:after{border-right:0}.ant-menu-dark.ant-menu-inline .ant-menu-item,.ant-menu-dark.ant-menu-inline .ant-menu-submenu-title{width:100%}.ant-menu-dark .ant-menu-item-active,.ant-menu-dark .ant-menu-item:hover,.ant-menu-dark .ant-menu-submenu-active,.ant-menu-dark .ant-menu-submenu-open,.ant-menu-dark .ant-menu-submenu-selected,.ant-menu-dark .ant-menu-submenu-title:hover{color:#fff;background-color:transparent}.ant-menu-dark .ant-menu-item-active>a,.ant-menu-dark .ant-menu-item:hover>a,.ant-menu-dark .ant-menu-submenu-active>a,.ant-menu-dark .ant-menu-submenu-open>a,.ant-menu-dark .ant-menu-submenu-selected>a,.ant-menu-dark .ant-menu-submenu-title:hover>a{color:#fff}.ant-menu-dark .ant-menu-item-active>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-item-active>.ant-menu-submenu-title>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-item:hover>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-item:hover>.ant-menu-submenu-title>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-active>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-active>.ant-menu-submenu-title>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-open>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-open>.ant-menu-submenu-title>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-selected>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-selected>.ant-menu-submenu-title>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-title:hover>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow,.ant-menu-dark .ant-menu-submenu-title:hover>.ant-menu-submenu-title>.ant-menu-submenu-arrow{opacity:1}.ant-menu-dark .ant-menu-item-active>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-item-active>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-item-active>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-item-active>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-item:hover>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-item:hover>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-item:hover>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-item:hover>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-active>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-active>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-active>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-active>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-open>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-open>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-open>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-open>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-selected>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-selected>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-selected>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-selected>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-title:hover>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-title:hover>.ant-menu-submenu-title:hover>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-title:hover>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-title:hover>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before{background:#fff}.ant-menu-dark .ant-menu-item:hover{background-color:transparent}.ant-menu-dark .ant-menu-item-selected{color:#fff;border-right:0}.ant-menu-dark .ant-menu-item-selected:after{border-right:0}.ant-menu-dark .ant-menu-item-selected>a,.ant-menu-dark .ant-menu-item-selected>a:hover{color:#fff}.ant-menu-dark .ant-menu-item-selected .anticon{color:#fff}.ant-menu-dark .ant-menu-item-selected .anticon+span{color:#fff}.ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected,.ant-menu.ant-menu-dark .ant-menu-item-selected{background-color:#1890ff}.ant-menu-dark .ant-menu-item-disabled,.ant-menu-dark .ant-menu-item-disabled>a,.ant-menu-dark .ant-menu-submenu-disabled,.ant-menu-dark .ant-menu-submenu-disabled>a{color:hsla(0,0%,100%,.35)!important;opacity:.8}.ant-menu-dark .ant-menu-item-disabled>.ant-menu-submenu-title,.ant-menu-dark .ant-menu-submenu-disabled>.ant-menu-submenu-title{color:hsla(0,0%,100%,.35)!important}.ant-menu-dark .ant-menu-item-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-item-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before,.ant-menu-dark .ant-menu-submenu-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:after,.ant-menu-dark .ant-menu-submenu-disabled>.ant-menu-submenu-title>.ant-menu-submenu-arrow:before{background:hsla(0,0%,100%,.35)!important}
+.ant-row{position:relative;height:auto;margin-right:0;margin-left:0;zoom:1;display:block;box-sizing:border-box}.ant-row:after,.ant-row:before{display:table;content:""}.ant-row:after{clear:both}.ant-row+.ant-row:before{clear:both}.ant-row-flex{display:flex;flex-flow:row wrap}.ant-row-flex:after,.ant-row-flex:before{display:flex}.ant-row-flex-start{justify-content:flex-start}.ant-row-flex-center{justify-content:center}.ant-row-flex-end{justify-content:flex-end}.ant-row-flex-space-between{justify-content:space-between}.ant-row-flex-space-around{justify-content:space-around}.ant-row-flex-top{align-items:flex-start}.ant-row-flex-middle{align-items:center}.ant-row-flex-bottom{align-items:flex-end}.ant-col{position:relative;min-height:1px}.ant-col-1,.ant-col-2,.ant-col-3,.ant-col-4,.ant-col-5,.ant-col-6,.ant-col-7,.ant-col-8,.ant-col-9,.ant-col-10,.ant-col-11,.ant-col-12,.ant-col-13,.ant-col-14,.ant-col-15,.ant-col-16,.ant-col-17,.ant-col-18,.ant-col-19,.ant-col-20,.ant-col-21,.ant-col-22,.ant-col-23,.ant-col-24,.ant-col-lg-1,.ant-col-lg-2,.ant-col-lg-3,.ant-col-lg-4,.ant-col-lg-5,.ant-col-lg-6,.ant-col-lg-7,.ant-col-lg-8,.ant-col-lg-9,.ant-col-lg-10,.ant-col-lg-11,.ant-col-lg-12,.ant-col-lg-13,.ant-col-lg-14,.ant-col-lg-15,.ant-col-lg-16,.ant-col-lg-17,.ant-col-lg-18,.ant-col-lg-19,.ant-col-lg-20,.ant-col-lg-21,.ant-col-lg-22,.ant-col-lg-23,.ant-col-lg-24,.ant-col-md-1,.ant-col-md-2,.ant-col-md-3,.ant-col-md-4,.ant-col-md-5,.ant-col-md-6,.ant-col-md-7,.ant-col-md-8,.ant-col-md-9,.ant-col-md-10,.ant-col-md-11,.ant-col-md-12,.ant-col-md-13,.ant-col-md-14,.ant-col-md-15,.ant-col-md-16,.ant-col-md-17,.ant-col-md-18,.ant-col-md-19,.ant-col-md-20,.ant-col-md-21,.ant-col-md-22,.ant-col-md-23,.ant-col-md-24,.ant-col-sm-1,.ant-col-sm-2,.ant-col-sm-3,.ant-col-sm-4,.ant-col-sm-5,.ant-col-sm-6,.ant-col-sm-7,.ant-col-sm-8,.ant-col-sm-9,.ant-col-sm-10,.ant-col-sm-11,.ant-col-sm-12,.ant-col-sm-13,.ant-col-sm-14,.ant-col-sm-15,.ant-col-sm-16,.ant-col-sm-17,.ant-col-sm-18,.ant-col-sm-19,.ant-col-sm-20,.ant-col-sm-21,.ant-col-sm-22,.ant-col-sm-23,.ant-col-sm-24,.ant-col-xs-1,.ant-col-xs-2,.ant-col-xs-3,.ant-col-xs-4,.ant-col-xs-5,.ant-col-xs-6,.ant-col-xs-7,.ant-col-xs-8,.ant-col-xs-9,.ant-col-xs-10,.ant-col-xs-11,.ant-col-xs-12,.ant-col-xs-13,.ant-col-xs-14,.ant-col-xs-15,.ant-col-xs-16,.ant-col-xs-17,.ant-col-xs-18,.ant-col-xs-19,.ant-col-xs-20,.ant-col-xs-21,.ant-col-xs-22,.ant-col-xs-23,.ant-col-xs-24{position:relative;padding-right:0;padding-left:0}.ant-col-1,.ant-col-2,.ant-col-3,.ant-col-4,.ant-col-5,.ant-col-6,.ant-col-7,.ant-col-8,.ant-col-9,.ant-col-10,.ant-col-11,.ant-col-12,.ant-col-13,.ant-col-14,.ant-col-15,.ant-col-16,.ant-col-17,.ant-col-18,.ant-col-19,.ant-col-20,.ant-col-21,.ant-col-22,.ant-col-23,.ant-col-24{flex:0 0 auto;float:left}.ant-col-24{display:block;box-sizing:border-box;width:100%}.ant-col-push-24{left:100%}.ant-col-pull-24{right:100%}.ant-col-offset-24{margin-left:100%}.ant-col-order-24{order:24}.ant-col-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-push-23{left:95.83333333%}.ant-col-pull-23{right:95.83333333%}.ant-col-offset-23{margin-left:95.83333333%}.ant-col-order-23{order:23}.ant-col-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-push-22{left:91.66666667%}.ant-col-pull-22{right:91.66666667%}.ant-col-offset-22{margin-left:91.66666667%}.ant-col-order-22{order:22}.ant-col-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-push-21{left:87.5%}.ant-col-pull-21{right:87.5%}.ant-col-offset-21{margin-left:87.5%}.ant-col-order-21{order:21}.ant-col-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-push-20{left:83.33333333%}.ant-col-pull-20{right:83.33333333%}.ant-col-offset-20{margin-left:83.33333333%}.ant-col-order-20{order:20}.ant-col-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-push-19{left:79.16666667%}.ant-col-pull-19{right:79.16666667%}.ant-col-offset-19{margin-left:79.16666667%}.ant-col-order-19{order:19}.ant-col-18{display:block;box-sizing:border-box;width:75%}.ant-col-push-18{left:75%}.ant-col-pull-18{right:75%}.ant-col-offset-18{margin-left:75%}.ant-col-order-18{order:18}.ant-col-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-push-17{left:70.83333333%}.ant-col-pull-17{right:70.83333333%}.ant-col-offset-17{margin-left:70.83333333%}.ant-col-order-17{order:17}.ant-col-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-push-16{left:66.66666667%}.ant-col-pull-16{right:66.66666667%}.ant-col-offset-16{margin-left:66.66666667%}.ant-col-order-16{order:16}.ant-col-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-push-15{left:62.5%}.ant-col-pull-15{right:62.5%}.ant-col-offset-15{margin-left:62.5%}.ant-col-order-15{order:15}.ant-col-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-push-14{left:58.33333333%}.ant-col-pull-14{right:58.33333333%}.ant-col-offset-14{margin-left:58.33333333%}.ant-col-order-14{order:14}.ant-col-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-push-13{left:54.16666667%}.ant-col-pull-13{right:54.16666667%}.ant-col-offset-13{margin-left:54.16666667%}.ant-col-order-13{order:13}.ant-col-12{display:block;box-sizing:border-box;width:50%}.ant-col-push-12{left:50%}.ant-col-pull-12{right:50%}.ant-col-offset-12{margin-left:50%}.ant-col-order-12{order:12}.ant-col-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-push-11{left:45.83333333%}.ant-col-pull-11{right:45.83333333%}.ant-col-offset-11{margin-left:45.83333333%}.ant-col-order-11{order:11}.ant-col-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-push-10{left:41.66666667%}.ant-col-pull-10{right:41.66666667%}.ant-col-offset-10{margin-left:41.66666667%}.ant-col-order-10{order:10}.ant-col-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-push-9{left:37.5%}.ant-col-pull-9{right:37.5%}.ant-col-offset-9{margin-left:37.5%}.ant-col-order-9{order:9}.ant-col-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-push-8{left:33.33333333%}.ant-col-pull-8{right:33.33333333%}.ant-col-offset-8{margin-left:33.33333333%}.ant-col-order-8{order:8}.ant-col-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-push-7{left:29.16666667%}.ant-col-pull-7{right:29.16666667%}.ant-col-offset-7{margin-left:29.16666667%}.ant-col-order-7{order:7}.ant-col-6{display:block;box-sizing:border-box;width:25%}.ant-col-push-6{left:25%}.ant-col-pull-6{right:25%}.ant-col-offset-6{margin-left:25%}.ant-col-order-6{order:6}.ant-col-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-push-5{left:20.83333333%}.ant-col-pull-5{right:20.83333333%}.ant-col-offset-5{margin-left:20.83333333%}.ant-col-order-5{order:5}.ant-col-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-push-4{left:16.66666667%}.ant-col-pull-4{right:16.66666667%}.ant-col-offset-4{margin-left:16.66666667%}.ant-col-order-4{order:4}.ant-col-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-push-3{left:12.5%}.ant-col-pull-3{right:12.5%}.ant-col-offset-3{margin-left:12.5%}.ant-col-order-3{order:3}.ant-col-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-push-2{left:8.33333333%}.ant-col-pull-2{right:8.33333333%}.ant-col-offset-2{margin-left:8.33333333%}.ant-col-order-2{order:2}.ant-col-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-push-1{left:4.16666667%}.ant-col-pull-1{right:4.16666667%}.ant-col-offset-1{margin-left:4.16666667%}.ant-col-order-1{order:1}.ant-col-0{display:none}.ant-col-offset-0{margin-left:0}.ant-col-order-0{order:0}.ant-col-xs-1,.ant-col-xs-2,.ant-col-xs-3,.ant-col-xs-4,.ant-col-xs-5,.ant-col-xs-6,.ant-col-xs-7,.ant-col-xs-8,.ant-col-xs-9,.ant-col-xs-10,.ant-col-xs-11,.ant-col-xs-12,.ant-col-xs-13,.ant-col-xs-14,.ant-col-xs-15,.ant-col-xs-16,.ant-col-xs-17,.ant-col-xs-18,.ant-col-xs-19,.ant-col-xs-20,.ant-col-xs-21,.ant-col-xs-22,.ant-col-xs-23,.ant-col-xs-24{flex:0 0 auto;float:left}.ant-col-xs-24{display:block;box-sizing:border-box;width:100%}.ant-col-xs-push-24{left:100%}.ant-col-xs-pull-24{right:100%}.ant-col-xs-offset-24{margin-left:100%}.ant-col-xs-order-24{order:24}.ant-col-xs-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-xs-push-23{left:95.83333333%}.ant-col-xs-pull-23{right:95.83333333%}.ant-col-xs-offset-23{margin-left:95.83333333%}.ant-col-xs-order-23{order:23}.ant-col-xs-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-xs-push-22{left:91.66666667%}.ant-col-xs-pull-22{right:91.66666667%}.ant-col-xs-offset-22{margin-left:91.66666667%}.ant-col-xs-order-22{order:22}.ant-col-xs-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-xs-push-21{left:87.5%}.ant-col-xs-pull-21{right:87.5%}.ant-col-xs-offset-21{margin-left:87.5%}.ant-col-xs-order-21{order:21}.ant-col-xs-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-xs-push-20{left:83.33333333%}.ant-col-xs-pull-20{right:83.33333333%}.ant-col-xs-offset-20{margin-left:83.33333333%}.ant-col-xs-order-20{order:20}.ant-col-xs-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-xs-push-19{left:79.16666667%}.ant-col-xs-pull-19{right:79.16666667%}.ant-col-xs-offset-19{margin-left:79.16666667%}.ant-col-xs-order-19{order:19}.ant-col-xs-18{display:block;box-sizing:border-box;width:75%}.ant-col-xs-push-18{left:75%}.ant-col-xs-pull-18{right:75%}.ant-col-xs-offset-18{margin-left:75%}.ant-col-xs-order-18{order:18}.ant-col-xs-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-xs-push-17{left:70.83333333%}.ant-col-xs-pull-17{right:70.83333333%}.ant-col-xs-offset-17{margin-left:70.83333333%}.ant-col-xs-order-17{order:17}.ant-col-xs-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-xs-push-16{left:66.66666667%}.ant-col-xs-pull-16{right:66.66666667%}.ant-col-xs-offset-16{margin-left:66.66666667%}.ant-col-xs-order-16{order:16}.ant-col-xs-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-xs-push-15{left:62.5%}.ant-col-xs-pull-15{right:62.5%}.ant-col-xs-offset-15{margin-left:62.5%}.ant-col-xs-order-15{order:15}.ant-col-xs-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-xs-push-14{left:58.33333333%}.ant-col-xs-pull-14{right:58.33333333%}.ant-col-xs-offset-14{margin-left:58.33333333%}.ant-col-xs-order-14{order:14}.ant-col-xs-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-xs-push-13{left:54.16666667%}.ant-col-xs-pull-13{right:54.16666667%}.ant-col-xs-offset-13{margin-left:54.16666667%}.ant-col-xs-order-13{order:13}.ant-col-xs-12{display:block;box-sizing:border-box;width:50%}.ant-col-xs-push-12{left:50%}.ant-col-xs-pull-12{right:50%}.ant-col-xs-offset-12{margin-left:50%}.ant-col-xs-order-12{order:12}.ant-col-xs-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-xs-push-11{left:45.83333333%}.ant-col-xs-pull-11{right:45.83333333%}.ant-col-xs-offset-11{margin-left:45.83333333%}.ant-col-xs-order-11{order:11}.ant-col-xs-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-xs-push-10{left:41.66666667%}.ant-col-xs-pull-10{right:41.66666667%}.ant-col-xs-offset-10{margin-left:41.66666667%}.ant-col-xs-order-10{order:10}.ant-col-xs-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-xs-push-9{left:37.5%}.ant-col-xs-pull-9{right:37.5%}.ant-col-xs-offset-9{margin-left:37.5%}.ant-col-xs-order-9{order:9}.ant-col-xs-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-xs-push-8{left:33.33333333%}.ant-col-xs-pull-8{right:33.33333333%}.ant-col-xs-offset-8{margin-left:33.33333333%}.ant-col-xs-order-8{order:8}.ant-col-xs-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-xs-push-7{left:29.16666667%}.ant-col-xs-pull-7{right:29.16666667%}.ant-col-xs-offset-7{margin-left:29.16666667%}.ant-col-xs-order-7{order:7}.ant-col-xs-6{display:block;box-sizing:border-box;width:25%}.ant-col-xs-push-6{left:25%}.ant-col-xs-pull-6{right:25%}.ant-col-xs-offset-6{margin-left:25%}.ant-col-xs-order-6{order:6}.ant-col-xs-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-xs-push-5{left:20.83333333%}.ant-col-xs-pull-5{right:20.83333333%}.ant-col-xs-offset-5{margin-left:20.83333333%}.ant-col-xs-order-5{order:5}.ant-col-xs-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-xs-push-4{left:16.66666667%}.ant-col-xs-pull-4{right:16.66666667%}.ant-col-xs-offset-4{margin-left:16.66666667%}.ant-col-xs-order-4{order:4}.ant-col-xs-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-xs-push-3{left:12.5%}.ant-col-xs-pull-3{right:12.5%}.ant-col-xs-offset-3{margin-left:12.5%}.ant-col-xs-order-3{order:3}.ant-col-xs-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-xs-push-2{left:8.33333333%}.ant-col-xs-pull-2{right:8.33333333%}.ant-col-xs-offset-2{margin-left:8.33333333%}.ant-col-xs-order-2{order:2}.ant-col-xs-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-xs-push-1{left:4.16666667%}.ant-col-xs-pull-1{right:4.16666667%}.ant-col-xs-offset-1{margin-left:4.16666667%}.ant-col-xs-order-1{order:1}.ant-col-xs-0{display:none}.ant-col-push-0{left:auto}.ant-col-pull-0{right:auto}.ant-col-xs-push-0{left:auto}.ant-col-xs-pull-0{right:auto}.ant-col-xs-offset-0{margin-left:0}.ant-col-xs-order-0{order:0}@media (min-width:576px){.ant-col-sm-1,.ant-col-sm-2,.ant-col-sm-3,.ant-col-sm-4,.ant-col-sm-5,.ant-col-sm-6,.ant-col-sm-7,.ant-col-sm-8,.ant-col-sm-9,.ant-col-sm-10,.ant-col-sm-11,.ant-col-sm-12,.ant-col-sm-13,.ant-col-sm-14,.ant-col-sm-15,.ant-col-sm-16,.ant-col-sm-17,.ant-col-sm-18,.ant-col-sm-19,.ant-col-sm-20,.ant-col-sm-21,.ant-col-sm-22,.ant-col-sm-23,.ant-col-sm-24{flex:0 0 auto;float:left}.ant-col-sm-24{display:block;box-sizing:border-box;width:100%}.ant-col-sm-push-24{left:100%}.ant-col-sm-pull-24{right:100%}.ant-col-sm-offset-24{margin-left:100%}.ant-col-sm-order-24{order:24}.ant-col-sm-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-sm-push-23{left:95.83333333%}.ant-col-sm-pull-23{right:95.83333333%}.ant-col-sm-offset-23{margin-left:95.83333333%}.ant-col-sm-order-23{order:23}.ant-col-sm-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-sm-push-22{left:91.66666667%}.ant-col-sm-pull-22{right:91.66666667%}.ant-col-sm-offset-22{margin-left:91.66666667%}.ant-col-sm-order-22{order:22}.ant-col-sm-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-sm-push-21{left:87.5%}.ant-col-sm-pull-21{right:87.5%}.ant-col-sm-offset-21{margin-left:87.5%}.ant-col-sm-order-21{order:21}.ant-col-sm-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-sm-push-20{left:83.33333333%}.ant-col-sm-pull-20{right:83.33333333%}.ant-col-sm-offset-20{margin-left:83.33333333%}.ant-col-sm-order-20{order:20}.ant-col-sm-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-sm-push-19{left:79.16666667%}.ant-col-sm-pull-19{right:79.16666667%}.ant-col-sm-offset-19{margin-left:79.16666667%}.ant-col-sm-order-19{order:19}.ant-col-sm-18{display:block;box-sizing:border-box;width:75%}.ant-col-sm-push-18{left:75%}.ant-col-sm-pull-18{right:75%}.ant-col-sm-offset-18{margin-left:75%}.ant-col-sm-order-18{order:18}.ant-col-sm-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-sm-push-17{left:70.83333333%}.ant-col-sm-pull-17{right:70.83333333%}.ant-col-sm-offset-17{margin-left:70.83333333%}.ant-col-sm-order-17{order:17}.ant-col-sm-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-sm-push-16{left:66.66666667%}.ant-col-sm-pull-16{right:66.66666667%}.ant-col-sm-offset-16{margin-left:66.66666667%}.ant-col-sm-order-16{order:16}.ant-col-sm-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-sm-push-15{left:62.5%}.ant-col-sm-pull-15{right:62.5%}.ant-col-sm-offset-15{margin-left:62.5%}.ant-col-sm-order-15{order:15}.ant-col-sm-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-sm-push-14{left:58.33333333%}.ant-col-sm-pull-14{right:58.33333333%}.ant-col-sm-offset-14{margin-left:58.33333333%}.ant-col-sm-order-14{order:14}.ant-col-sm-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-sm-push-13{left:54.16666667%}.ant-col-sm-pull-13{right:54.16666667%}.ant-col-sm-offset-13{margin-left:54.16666667%}.ant-col-sm-order-13{order:13}.ant-col-sm-12{display:block;box-sizing:border-box;width:50%}.ant-col-sm-push-12{left:50%}.ant-col-sm-pull-12{right:50%}.ant-col-sm-offset-12{margin-left:50%}.ant-col-sm-order-12{order:12}.ant-col-sm-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-sm-push-11{left:45.83333333%}.ant-col-sm-pull-11{right:45.83333333%}.ant-col-sm-offset-11{margin-left:45.83333333%}.ant-col-sm-order-11{order:11}.ant-col-sm-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-sm-push-10{left:41.66666667%}.ant-col-sm-pull-10{right:41.66666667%}.ant-col-sm-offset-10{margin-left:41.66666667%}.ant-col-sm-order-10{order:10}.ant-col-sm-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-sm-push-9{left:37.5%}.ant-col-sm-pull-9{right:37.5%}.ant-col-sm-offset-9{margin-left:37.5%}.ant-col-sm-order-9{order:9}.ant-col-sm-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-sm-push-8{left:33.33333333%}.ant-col-sm-pull-8{right:33.33333333%}.ant-col-sm-offset-8{margin-left:33.33333333%}.ant-col-sm-order-8{order:8}.ant-col-sm-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-sm-push-7{left:29.16666667%}.ant-col-sm-pull-7{right:29.16666667%}.ant-col-sm-offset-7{margin-left:29.16666667%}.ant-col-sm-order-7{order:7}.ant-col-sm-6{display:block;box-sizing:border-box;width:25%}.ant-col-sm-push-6{left:25%}.ant-col-sm-pull-6{right:25%}.ant-col-sm-offset-6{margin-left:25%}.ant-col-sm-order-6{order:6}.ant-col-sm-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-sm-push-5{left:20.83333333%}.ant-col-sm-pull-5{right:20.83333333%}.ant-col-sm-offset-5{margin-left:20.83333333%}.ant-col-sm-order-5{order:5}.ant-col-sm-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-sm-push-4{left:16.66666667%}.ant-col-sm-pull-4{right:16.66666667%}.ant-col-sm-offset-4{margin-left:16.66666667%}.ant-col-sm-order-4{order:4}.ant-col-sm-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-sm-push-3{left:12.5%}.ant-col-sm-pull-3{right:12.5%}.ant-col-sm-offset-3{margin-left:12.5%}.ant-col-sm-order-3{order:3}.ant-col-sm-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-sm-push-2{left:8.33333333%}.ant-col-sm-pull-2{right:8.33333333%}.ant-col-sm-offset-2{margin-left:8.33333333%}.ant-col-sm-order-2{order:2}.ant-col-sm-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-sm-push-1{left:4.16666667%}.ant-col-sm-pull-1{right:4.16666667%}.ant-col-sm-offset-1{margin-left:4.16666667%}.ant-col-sm-order-1{order:1}.ant-col-sm-0{display:none}.ant-col-push-0{left:auto}.ant-col-pull-0{right:auto}.ant-col-sm-push-0{left:auto}.ant-col-sm-pull-0{right:auto}.ant-col-sm-offset-0{margin-left:0}.ant-col-sm-order-0{order:0}}@media (min-width:768px){.ant-col-md-1,.ant-col-md-2,.ant-col-md-3,.ant-col-md-4,.ant-col-md-5,.ant-col-md-6,.ant-col-md-7,.ant-col-md-8,.ant-col-md-9,.ant-col-md-10,.ant-col-md-11,.ant-col-md-12,.ant-col-md-13,.ant-col-md-14,.ant-col-md-15,.ant-col-md-16,.ant-col-md-17,.ant-col-md-18,.ant-col-md-19,.ant-col-md-20,.ant-col-md-21,.ant-col-md-22,.ant-col-md-23,.ant-col-md-24{flex:0 0 auto;float:left}.ant-col-md-24{display:block;box-sizing:border-box;width:100%}.ant-col-md-push-24{left:100%}.ant-col-md-pull-24{right:100%}.ant-col-md-offset-24{margin-left:100%}.ant-col-md-order-24{order:24}.ant-col-md-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-md-push-23{left:95.83333333%}.ant-col-md-pull-23{right:95.83333333%}.ant-col-md-offset-23{margin-left:95.83333333%}.ant-col-md-order-23{order:23}.ant-col-md-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-md-push-22{left:91.66666667%}.ant-col-md-pull-22{right:91.66666667%}.ant-col-md-offset-22{margin-left:91.66666667%}.ant-col-md-order-22{order:22}.ant-col-md-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-md-push-21{left:87.5%}.ant-col-md-pull-21{right:87.5%}.ant-col-md-offset-21{margin-left:87.5%}.ant-col-md-order-21{order:21}.ant-col-md-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-md-push-20{left:83.33333333%}.ant-col-md-pull-20{right:83.33333333%}.ant-col-md-offset-20{margin-left:83.33333333%}.ant-col-md-order-20{order:20}.ant-col-md-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-md-push-19{left:79.16666667%}.ant-col-md-pull-19{right:79.16666667%}.ant-col-md-offset-19{margin-left:79.16666667%}.ant-col-md-order-19{order:19}.ant-col-md-18{display:block;box-sizing:border-box;width:75%}.ant-col-md-push-18{left:75%}.ant-col-md-pull-18{right:75%}.ant-col-md-offset-18{margin-left:75%}.ant-col-md-order-18{order:18}.ant-col-md-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-md-push-17{left:70.83333333%}.ant-col-md-pull-17{right:70.83333333%}.ant-col-md-offset-17{margin-left:70.83333333%}.ant-col-md-order-17{order:17}.ant-col-md-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-md-push-16{left:66.66666667%}.ant-col-md-pull-16{right:66.66666667%}.ant-col-md-offset-16{margin-left:66.66666667%}.ant-col-md-order-16{order:16}.ant-col-md-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-md-push-15{left:62.5%}.ant-col-md-pull-15{right:62.5%}.ant-col-md-offset-15{margin-left:62.5%}.ant-col-md-order-15{order:15}.ant-col-md-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-md-push-14{left:58.33333333%}.ant-col-md-pull-14{right:58.33333333%}.ant-col-md-offset-14{margin-left:58.33333333%}.ant-col-md-order-14{order:14}.ant-col-md-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-md-push-13{left:54.16666667%}.ant-col-md-pull-13{right:54.16666667%}.ant-col-md-offset-13{margin-left:54.16666667%}.ant-col-md-order-13{order:13}.ant-col-md-12{display:block;box-sizing:border-box;width:50%}.ant-col-md-push-12{left:50%}.ant-col-md-pull-12{right:50%}.ant-col-md-offset-12{margin-left:50%}.ant-col-md-order-12{order:12}.ant-col-md-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-md-push-11{left:45.83333333%}.ant-col-md-pull-11{right:45.83333333%}.ant-col-md-offset-11{margin-left:45.83333333%}.ant-col-md-order-11{order:11}.ant-col-md-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-md-push-10{left:41.66666667%}.ant-col-md-pull-10{right:41.66666667%}.ant-col-md-offset-10{margin-left:41.66666667%}.ant-col-md-order-10{order:10}.ant-col-md-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-md-push-9{left:37.5%}.ant-col-md-pull-9{right:37.5%}.ant-col-md-offset-9{margin-left:37.5%}.ant-col-md-order-9{order:9}.ant-col-md-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-md-push-8{left:33.33333333%}.ant-col-md-pull-8{right:33.33333333%}.ant-col-md-offset-8{margin-left:33.33333333%}.ant-col-md-order-8{order:8}.ant-col-md-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-md-push-7{left:29.16666667%}.ant-col-md-pull-7{right:29.16666667%}.ant-col-md-offset-7{margin-left:29.16666667%}.ant-col-md-order-7{order:7}.ant-col-md-6{display:block;box-sizing:border-box;width:25%}.ant-col-md-push-6{left:25%}.ant-col-md-pull-6{right:25%}.ant-col-md-offset-6{margin-left:25%}.ant-col-md-order-6{order:6}.ant-col-md-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-md-push-5{left:20.83333333%}.ant-col-md-pull-5{right:20.83333333%}.ant-col-md-offset-5{margin-left:20.83333333%}.ant-col-md-order-5{order:5}.ant-col-md-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-md-push-4{left:16.66666667%}.ant-col-md-pull-4{right:16.66666667%}.ant-col-md-offset-4{margin-left:16.66666667%}.ant-col-md-order-4{order:4}.ant-col-md-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-md-push-3{left:12.5%}.ant-col-md-pull-3{right:12.5%}.ant-col-md-offset-3{margin-left:12.5%}.ant-col-md-order-3{order:3}.ant-col-md-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-md-push-2{left:8.33333333%}.ant-col-md-pull-2{right:8.33333333%}.ant-col-md-offset-2{margin-left:8.33333333%}.ant-col-md-order-2{order:2}.ant-col-md-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-md-push-1{left:4.16666667%}.ant-col-md-pull-1{right:4.16666667%}.ant-col-md-offset-1{margin-left:4.16666667%}.ant-col-md-order-1{order:1}.ant-col-md-0{display:none}.ant-col-push-0{left:auto}.ant-col-pull-0{right:auto}.ant-col-md-push-0{left:auto}.ant-col-md-pull-0{right:auto}.ant-col-md-offset-0{margin-left:0}.ant-col-md-order-0{order:0}}@media (min-width:992px){.ant-col-lg-1,.ant-col-lg-2,.ant-col-lg-3,.ant-col-lg-4,.ant-col-lg-5,.ant-col-lg-6,.ant-col-lg-7,.ant-col-lg-8,.ant-col-lg-9,.ant-col-lg-10,.ant-col-lg-11,.ant-col-lg-12,.ant-col-lg-13,.ant-col-lg-14,.ant-col-lg-15,.ant-col-lg-16,.ant-col-lg-17,.ant-col-lg-18,.ant-col-lg-19,.ant-col-lg-20,.ant-col-lg-21,.ant-col-lg-22,.ant-col-lg-23,.ant-col-lg-24{flex:0 0 auto;float:left}.ant-col-lg-24{display:block;box-sizing:border-box;width:100%}.ant-col-lg-push-24{left:100%}.ant-col-lg-pull-24{right:100%}.ant-col-lg-offset-24{margin-left:100%}.ant-col-lg-order-24{order:24}.ant-col-lg-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-lg-push-23{left:95.83333333%}.ant-col-lg-pull-23{right:95.83333333%}.ant-col-lg-offset-23{margin-left:95.83333333%}.ant-col-lg-order-23{order:23}.ant-col-lg-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-lg-push-22{left:91.66666667%}.ant-col-lg-pull-22{right:91.66666667%}.ant-col-lg-offset-22{margin-left:91.66666667%}.ant-col-lg-order-22{order:22}.ant-col-lg-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-lg-push-21{left:87.5%}.ant-col-lg-pull-21{right:87.5%}.ant-col-lg-offset-21{margin-left:87.5%}.ant-col-lg-order-21{order:21}.ant-col-lg-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-lg-push-20{left:83.33333333%}.ant-col-lg-pull-20{right:83.33333333%}.ant-col-lg-offset-20{margin-left:83.33333333%}.ant-col-lg-order-20{order:20}.ant-col-lg-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-lg-push-19{left:79.16666667%}.ant-col-lg-pull-19{right:79.16666667%}.ant-col-lg-offset-19{margin-left:79.16666667%}.ant-col-lg-order-19{order:19}.ant-col-lg-18{display:block;box-sizing:border-box;width:75%}.ant-col-lg-push-18{left:75%}.ant-col-lg-pull-18{right:75%}.ant-col-lg-offset-18{margin-left:75%}.ant-col-lg-order-18{order:18}.ant-col-lg-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-lg-push-17{left:70.83333333%}.ant-col-lg-pull-17{right:70.83333333%}.ant-col-lg-offset-17{margin-left:70.83333333%}.ant-col-lg-order-17{order:17}.ant-col-lg-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-lg-push-16{left:66.66666667%}.ant-col-lg-pull-16{right:66.66666667%}.ant-col-lg-offset-16{margin-left:66.66666667%}.ant-col-lg-order-16{order:16}.ant-col-lg-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-lg-push-15{left:62.5%}.ant-col-lg-pull-15{right:62.5%}.ant-col-lg-offset-15{margin-left:62.5%}.ant-col-lg-order-15{order:15}.ant-col-lg-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-lg-push-14{left:58.33333333%}.ant-col-lg-pull-14{right:58.33333333%}.ant-col-lg-offset-14{margin-left:58.33333333%}.ant-col-lg-order-14{order:14}.ant-col-lg-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-lg-push-13{left:54.16666667%}.ant-col-lg-pull-13{right:54.16666667%}.ant-col-lg-offset-13{margin-left:54.16666667%}.ant-col-lg-order-13{order:13}.ant-col-lg-12{display:block;box-sizing:border-box;width:50%}.ant-col-lg-push-12{left:50%}.ant-col-lg-pull-12{right:50%}.ant-col-lg-offset-12{margin-left:50%}.ant-col-lg-order-12{order:12}.ant-col-lg-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-lg-push-11{left:45.83333333%}.ant-col-lg-pull-11{right:45.83333333%}.ant-col-lg-offset-11{margin-left:45.83333333%}.ant-col-lg-order-11{order:11}.ant-col-lg-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-lg-push-10{left:41.66666667%}.ant-col-lg-pull-10{right:41.66666667%}.ant-col-lg-offset-10{margin-left:41.66666667%}.ant-col-lg-order-10{order:10}.ant-col-lg-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-lg-push-9{left:37.5%}.ant-col-lg-pull-9{right:37.5%}.ant-col-lg-offset-9{margin-left:37.5%}.ant-col-lg-order-9{order:9}.ant-col-lg-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-lg-push-8{left:33.33333333%}.ant-col-lg-pull-8{right:33.33333333%}.ant-col-lg-offset-8{margin-left:33.33333333%}.ant-col-lg-order-8{order:8}.ant-col-lg-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-lg-push-7{left:29.16666667%}.ant-col-lg-pull-7{right:29.16666667%}.ant-col-lg-offset-7{margin-left:29.16666667%}.ant-col-lg-order-7{order:7}.ant-col-lg-6{display:block;box-sizing:border-box;width:25%}.ant-col-lg-push-6{left:25%}.ant-col-lg-pull-6{right:25%}.ant-col-lg-offset-6{margin-left:25%}.ant-col-lg-order-6{order:6}.ant-col-lg-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-lg-push-5{left:20.83333333%}.ant-col-lg-pull-5{right:20.83333333%}.ant-col-lg-offset-5{margin-left:20.83333333%}.ant-col-lg-order-5{order:5}.ant-col-lg-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-lg-push-4{left:16.66666667%}.ant-col-lg-pull-4{right:16.66666667%}.ant-col-lg-offset-4{margin-left:16.66666667%}.ant-col-lg-order-4{order:4}.ant-col-lg-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-lg-push-3{left:12.5%}.ant-col-lg-pull-3{right:12.5%}.ant-col-lg-offset-3{margin-left:12.5%}.ant-col-lg-order-3{order:3}.ant-col-lg-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-lg-push-2{left:8.33333333%}.ant-col-lg-pull-2{right:8.33333333%}.ant-col-lg-offset-2{margin-left:8.33333333%}.ant-col-lg-order-2{order:2}.ant-col-lg-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-lg-push-1{left:4.16666667%}.ant-col-lg-pull-1{right:4.16666667%}.ant-col-lg-offset-1{margin-left:4.16666667%}.ant-col-lg-order-1{order:1}.ant-col-lg-0{display:none}.ant-col-push-0{left:auto}.ant-col-pull-0{right:auto}.ant-col-lg-push-0{left:auto}.ant-col-lg-pull-0{right:auto}.ant-col-lg-offset-0{margin-left:0}.ant-col-lg-order-0{order:0}}@media (min-width:1200px){.ant-col-xl-1,.ant-col-xl-2,.ant-col-xl-3,.ant-col-xl-4,.ant-col-xl-5,.ant-col-xl-6,.ant-col-xl-7,.ant-col-xl-8,.ant-col-xl-9,.ant-col-xl-10,.ant-col-xl-11,.ant-col-xl-12,.ant-col-xl-13,.ant-col-xl-14,.ant-col-xl-15,.ant-col-xl-16,.ant-col-xl-17,.ant-col-xl-18,.ant-col-xl-19,.ant-col-xl-20,.ant-col-xl-21,.ant-col-xl-22,.ant-col-xl-23,.ant-col-xl-24{flex:0 0 auto;float:left}.ant-col-xl-24{display:block;box-sizing:border-box;width:100%}.ant-col-xl-push-24{left:100%}.ant-col-xl-pull-24{right:100%}.ant-col-xl-offset-24{margin-left:100%}.ant-col-xl-order-24{order:24}.ant-col-xl-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-xl-push-23{left:95.83333333%}.ant-col-xl-pull-23{right:95.83333333%}.ant-col-xl-offset-23{margin-left:95.83333333%}.ant-col-xl-order-23{order:23}.ant-col-xl-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-xl-push-22{left:91.66666667%}.ant-col-xl-pull-22{right:91.66666667%}.ant-col-xl-offset-22{margin-left:91.66666667%}.ant-col-xl-order-22{order:22}.ant-col-xl-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-xl-push-21{left:87.5%}.ant-col-xl-pull-21{right:87.5%}.ant-col-xl-offset-21{margin-left:87.5%}.ant-col-xl-order-21{order:21}.ant-col-xl-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-xl-push-20{left:83.33333333%}.ant-col-xl-pull-20{right:83.33333333%}.ant-col-xl-offset-20{margin-left:83.33333333%}.ant-col-xl-order-20{order:20}.ant-col-xl-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-xl-push-19{left:79.16666667%}.ant-col-xl-pull-19{right:79.16666667%}.ant-col-xl-offset-19{margin-left:79.16666667%}.ant-col-xl-order-19{order:19}.ant-col-xl-18{display:block;box-sizing:border-box;width:75%}.ant-col-xl-push-18{left:75%}.ant-col-xl-pull-18{right:75%}.ant-col-xl-offset-18{margin-left:75%}.ant-col-xl-order-18{order:18}.ant-col-xl-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-xl-push-17{left:70.83333333%}.ant-col-xl-pull-17{right:70.83333333%}.ant-col-xl-offset-17{margin-left:70.83333333%}.ant-col-xl-order-17{order:17}.ant-col-xl-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-xl-push-16{left:66.66666667%}.ant-col-xl-pull-16{right:66.66666667%}.ant-col-xl-offset-16{margin-left:66.66666667%}.ant-col-xl-order-16{order:16}.ant-col-xl-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-xl-push-15{left:62.5%}.ant-col-xl-pull-15{right:62.5%}.ant-col-xl-offset-15{margin-left:62.5%}.ant-col-xl-order-15{order:15}.ant-col-xl-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-xl-push-14{left:58.33333333%}.ant-col-xl-pull-14{right:58.33333333%}.ant-col-xl-offset-14{margin-left:58.33333333%}.ant-col-xl-order-14{order:14}.ant-col-xl-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-xl-push-13{left:54.16666667%}.ant-col-xl-pull-13{right:54.16666667%}.ant-col-xl-offset-13{margin-left:54.16666667%}.ant-col-xl-order-13{order:13}.ant-col-xl-12{display:block;box-sizing:border-box;width:50%}.ant-col-xl-push-12{left:50%}.ant-col-xl-pull-12{right:50%}.ant-col-xl-offset-12{margin-left:50%}.ant-col-xl-order-12{order:12}.ant-col-xl-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-xl-push-11{left:45.83333333%}.ant-col-xl-pull-11{right:45.83333333%}.ant-col-xl-offset-11{margin-left:45.83333333%}.ant-col-xl-order-11{order:11}.ant-col-xl-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-xl-push-10{left:41.66666667%}.ant-col-xl-pull-10{right:41.66666667%}.ant-col-xl-offset-10{margin-left:41.66666667%}.ant-col-xl-order-10{order:10}.ant-col-xl-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-xl-push-9{left:37.5%}.ant-col-xl-pull-9{right:37.5%}.ant-col-xl-offset-9{margin-left:37.5%}.ant-col-xl-order-9{order:9}.ant-col-xl-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-xl-push-8{left:33.33333333%}.ant-col-xl-pull-8{right:33.33333333%}.ant-col-xl-offset-8{margin-left:33.33333333%}.ant-col-xl-order-8{order:8}.ant-col-xl-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-xl-push-7{left:29.16666667%}.ant-col-xl-pull-7{right:29.16666667%}.ant-col-xl-offset-7{margin-left:29.16666667%}.ant-col-xl-order-7{order:7}.ant-col-xl-6{display:block;box-sizing:border-box;width:25%}.ant-col-xl-push-6{left:25%}.ant-col-xl-pull-6{right:25%}.ant-col-xl-offset-6{margin-left:25%}.ant-col-xl-order-6{order:6}.ant-col-xl-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-xl-push-5{left:20.83333333%}.ant-col-xl-pull-5{right:20.83333333%}.ant-col-xl-offset-5{margin-left:20.83333333%}.ant-col-xl-order-5{order:5}.ant-col-xl-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-xl-push-4{left:16.66666667%}.ant-col-xl-pull-4{right:16.66666667%}.ant-col-xl-offset-4{margin-left:16.66666667%}.ant-col-xl-order-4{order:4}.ant-col-xl-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-xl-push-3{left:12.5%}.ant-col-xl-pull-3{right:12.5%}.ant-col-xl-offset-3{margin-left:12.5%}.ant-col-xl-order-3{order:3}.ant-col-xl-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-xl-push-2{left:8.33333333%}.ant-col-xl-pull-2{right:8.33333333%}.ant-col-xl-offset-2{margin-left:8.33333333%}.ant-col-xl-order-2{order:2}.ant-col-xl-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-xl-push-1{left:4.16666667%}.ant-col-xl-pull-1{right:4.16666667%}.ant-col-xl-offset-1{margin-left:4.16666667%}.ant-col-xl-order-1{order:1}.ant-col-xl-0{display:none}.ant-col-push-0{left:auto}.ant-col-pull-0{right:auto}.ant-col-xl-push-0{left:auto}.ant-col-xl-pull-0{right:auto}.ant-col-xl-offset-0{margin-left:0}.ant-col-xl-order-0{order:0}}@media (min-width:1600px){.ant-col-xxl-1,.ant-col-xxl-2,.ant-col-xxl-3,.ant-col-xxl-4,.ant-col-xxl-5,.ant-col-xxl-6,.ant-col-xxl-7,.ant-col-xxl-8,.ant-col-xxl-9,.ant-col-xxl-10,.ant-col-xxl-11,.ant-col-xxl-12,.ant-col-xxl-13,.ant-col-xxl-14,.ant-col-xxl-15,.ant-col-xxl-16,.ant-col-xxl-17,.ant-col-xxl-18,.ant-col-xxl-19,.ant-col-xxl-20,.ant-col-xxl-21,.ant-col-xxl-22,.ant-col-xxl-23,.ant-col-xxl-24{flex:0 0 auto;float:left}.ant-col-xxl-24{display:block;box-sizing:border-box;width:100%}.ant-col-xxl-push-24{left:100%}.ant-col-xxl-pull-24{right:100%}.ant-col-xxl-offset-24{margin-left:100%}.ant-col-xxl-order-24{order:24}.ant-col-xxl-23{display:block;box-sizing:border-box;width:95.83333333%}.ant-col-xxl-push-23{left:95.83333333%}.ant-col-xxl-pull-23{right:95.83333333%}.ant-col-xxl-offset-23{margin-left:95.83333333%}.ant-col-xxl-order-23{order:23}.ant-col-xxl-22{display:block;box-sizing:border-box;width:91.66666667%}.ant-col-xxl-push-22{left:91.66666667%}.ant-col-xxl-pull-22{right:91.66666667%}.ant-col-xxl-offset-22{margin-left:91.66666667%}.ant-col-xxl-order-22{order:22}.ant-col-xxl-21{display:block;box-sizing:border-box;width:87.5%}.ant-col-xxl-push-21{left:87.5%}.ant-col-xxl-pull-21{right:87.5%}.ant-col-xxl-offset-21{margin-left:87.5%}.ant-col-xxl-order-21{order:21}.ant-col-xxl-20{display:block;box-sizing:border-box;width:83.33333333%}.ant-col-xxl-push-20{left:83.33333333%}.ant-col-xxl-pull-20{right:83.33333333%}.ant-col-xxl-offset-20{margin-left:83.33333333%}.ant-col-xxl-order-20{order:20}.ant-col-xxl-19{display:block;box-sizing:border-box;width:79.16666667%}.ant-col-xxl-push-19{left:79.16666667%}.ant-col-xxl-pull-19{right:79.16666667%}.ant-col-xxl-offset-19{margin-left:79.16666667%}.ant-col-xxl-order-19{order:19}.ant-col-xxl-18{display:block;box-sizing:border-box;width:75%}.ant-col-xxl-push-18{left:75%}.ant-col-xxl-pull-18{right:75%}.ant-col-xxl-offset-18{margin-left:75%}.ant-col-xxl-order-18{order:18}.ant-col-xxl-17{display:block;box-sizing:border-box;width:70.83333333%}.ant-col-xxl-push-17{left:70.83333333%}.ant-col-xxl-pull-17{right:70.83333333%}.ant-col-xxl-offset-17{margin-left:70.83333333%}.ant-col-xxl-order-17{order:17}.ant-col-xxl-16{display:block;box-sizing:border-box;width:66.66666667%}.ant-col-xxl-push-16{left:66.66666667%}.ant-col-xxl-pull-16{right:66.66666667%}.ant-col-xxl-offset-16{margin-left:66.66666667%}.ant-col-xxl-order-16{order:16}.ant-col-xxl-15{display:block;box-sizing:border-box;width:62.5%}.ant-col-xxl-push-15{left:62.5%}.ant-col-xxl-pull-15{right:62.5%}.ant-col-xxl-offset-15{margin-left:62.5%}.ant-col-xxl-order-15{order:15}.ant-col-xxl-14{display:block;box-sizing:border-box;width:58.33333333%}.ant-col-xxl-push-14{left:58.33333333%}.ant-col-xxl-pull-14{right:58.33333333%}.ant-col-xxl-offset-14{margin-left:58.33333333%}.ant-col-xxl-order-14{order:14}.ant-col-xxl-13{display:block;box-sizing:border-box;width:54.16666667%}.ant-col-xxl-push-13{left:54.16666667%}.ant-col-xxl-pull-13{right:54.16666667%}.ant-col-xxl-offset-13{margin-left:54.16666667%}.ant-col-xxl-order-13{order:13}.ant-col-xxl-12{display:block;box-sizing:border-box;width:50%}.ant-col-xxl-push-12{left:50%}.ant-col-xxl-pull-12{right:50%}.ant-col-xxl-offset-12{margin-left:50%}.ant-col-xxl-order-12{order:12}.ant-col-xxl-11{display:block;box-sizing:border-box;width:45.83333333%}.ant-col-xxl-push-11{left:45.83333333%}.ant-col-xxl-pull-11{right:45.83333333%}.ant-col-xxl-offset-11{margin-left:45.83333333%}.ant-col-xxl-order-11{order:11}.ant-col-xxl-10{display:block;box-sizing:border-box;width:41.66666667%}.ant-col-xxl-push-10{left:41.66666667%}.ant-col-xxl-pull-10{right:41.66666667%}.ant-col-xxl-offset-10{margin-left:41.66666667%}.ant-col-xxl-order-10{order:10}.ant-col-xxl-9{display:block;box-sizing:border-box;width:37.5%}.ant-col-xxl-push-9{left:37.5%}.ant-col-xxl-pull-9{right:37.5%}.ant-col-xxl-offset-9{margin-left:37.5%}.ant-col-xxl-order-9{order:9}.ant-col-xxl-8{display:block;box-sizing:border-box;width:33.33333333%}.ant-col-xxl-push-8{left:33.33333333%}.ant-col-xxl-pull-8{right:33.33333333%}.ant-col-xxl-offset-8{margin-left:33.33333333%}.ant-col-xxl-order-8{order:8}.ant-col-xxl-7{display:block;box-sizing:border-box;width:29.16666667%}.ant-col-xxl-push-7{left:29.16666667%}.ant-col-xxl-pull-7{right:29.16666667%}.ant-col-xxl-offset-7{margin-left:29.16666667%}.ant-col-xxl-order-7{order:7}.ant-col-xxl-6{display:block;box-sizing:border-box;width:25%}.ant-col-xxl-push-6{left:25%}.ant-col-xxl-pull-6{right:25%}.ant-col-xxl-offset-6{margin-left:25%}.ant-col-xxl-order-6{order:6}.ant-col-xxl-5{display:block;box-sizing:border-box;width:20.83333333%}.ant-col-xxl-push-5{left:20.83333333%}.ant-col-xxl-pull-5{right:20.83333333%}.ant-col-xxl-offset-5{margin-left:20.83333333%}.ant-col-xxl-order-5{order:5}.ant-col-xxl-4{display:block;box-sizing:border-box;width:16.66666667%}.ant-col-xxl-push-4{left:16.66666667%}.ant-col-xxl-pull-4{right:16.66666667%}.ant-col-xxl-offset-4{margin-left:16.66666667%}.ant-col-xxl-order-4{order:4}.ant-col-xxl-3{display:block;box-sizing:border-box;width:12.5%}.ant-col-xxl-push-3{left:12.5%}.ant-col-xxl-pull-3{right:12.5%}.ant-col-xxl-offset-3{margin-left:12.5%}.ant-col-xxl-order-3{order:3}.ant-col-xxl-2{display:block;box-sizing:border-box;width:8.33333333%}.ant-col-xxl-push-2{left:8.33333333%}.ant-col-xxl-pull-2{right:8.33333333%}.ant-col-xxl-offset-2{margin-left:8.33333333%}.ant-col-xxl-order-2{order:2}.ant-col-xxl-1{display:block;box-sizing:border-box;width:4.16666667%}.ant-col-xxl-push-1{left:4.16666667%}.ant-col-xxl-pull-1{right:4.16666667%}.ant-col-xxl-offset-1{margin-left:4.16666667%}.ant-col-xxl-order-1{order:1}.ant-col-xxl-0{display:none}.ant-col-push-0{left:auto}.ant-col-pull-0{right:auto}.ant-col-xxl-push-0{left:auto}.ant-col-xxl-pull-0{right:auto}.ant-col-xxl-offset-0{margin-left:0}.ant-col-xxl-order-0{order:0}}
+.ant-list{box-sizing:border-box;margin:0;padding:0;color:rgba(0,0,0,.65);font-size:14px;font-variant:tabular-nums;line-height:1.5;list-style:none;font-feature-settings:"tnum";position:relative}.ant-list *{outline:none}.ant-list-pagination{margin-top:24px;text-align:right}.ant-list-pagination .ant-pagination-options{text-align:left}.ant-list-more{margin-top:12px;text-align:center}.ant-list-more button{padding-right:32px;padding-left:32px}.ant-list-spin{min-height:40px;text-align:center}.ant-list-empty-text{padding:16px;color:rgba(0,0,0,.25);font-size:14px;text-align:center}.ant-list-items{margin:0;padding:0;list-style:none}.ant-list-item{display:flex;align-items:center;justify-content:space-between;padding:12px 0}.ant-list-item-content{color:rgba(0,0,0,.65)}.ant-list-item-meta{display:flex;flex:1 1;align-items:flex-start;font-size:0}.ant-list-item-meta-avatar{margin-right:16px}.ant-list-item-meta-content{flex:1 0}.ant-list-item-meta-title{margin-bottom:4px;color:rgba(0,0,0,.65);font-size:14px;line-height:22px}.ant-list-item-meta-title>a{color:rgba(0,0,0,.65);transition:all .3s}.ant-list-item-meta-title>a:hover{color:#1890ff}.ant-list-item-meta-description{color:rgba(0,0,0,.45);font-size:14px;line-height:22px}.ant-list-item-action{flex:0 0 auto;margin-left:48px;padding:0;font-size:0;list-style:none}.ant-list-item-action>li{position:relative;display:inline-block;padding:0 8px;color:rgba(0,0,0,.45);font-size:14px;line-height:22px;text-align:center;cursor:pointer}.ant-list-item-action>li:first-child{padding-left:0}.ant-list-item-action-split{position:absolute;top:50%;right:0;width:1px;height:14px;margin-top:-7px;background-color:#e8e8e8}.ant-list-header{background:transparent}.ant-list-footer{background:transparent}.ant-list-footer,.ant-list-header{padding-top:12px;padding-bottom:12px}.ant-list-empty{padding:16px 0;color:rgba(0,0,0,.45);font-size:12px;text-align:center}.ant-list-split .ant-list-item{border-bottom:1px solid #e8e8e8}.ant-list-split .ant-list-item:last-child{border-bottom:none}.ant-list-split .ant-list-header{border-bottom:1px solid #e8e8e8}.ant-list-loading .ant-list-spin-nested-loading{min-height:32px}.ant-list-something-after-last-item .ant-spin-container>.ant-list-items>.ant-list-item:last-child{border-bottom:1px solid #e8e8e8}.ant-list-lg .ant-list-item{padding-top:16px;padding-bottom:16px}.ant-list-sm .ant-list-item{padding-top:8px;padding-bottom:8px}.ant-list-vertical .ant-list-item{align-items:normal}.ant-list-vertical .ant-list-item-main{display:block;flex:1 1}.ant-list-vertical .ant-list-item-extra{margin-left:40px}.ant-list-vertical .ant-list-item-meta{margin-bottom:16px}.ant-list-vertical .ant-list-item-meta-title{margin-bottom:12px;color:rgba(0,0,0,.85);font-size:16px;line-height:24px}.ant-list-vertical .ant-list-item-action{margin-top:16px;margin-left:auto}.ant-list-vertical .ant-list-item-action>li{padding:0 16px}.ant-list-vertical .ant-list-item-action>li:first-child{padding-left:0}.ant-list-grid .ant-col>.ant-list-item{display:block;max-width:100%;margin-bottom:16px;padding-top:0;padding-bottom:0;border-bottom:none}.ant-list-item-no-flex{display:block}.ant-list:not(.ant-list-vertical) .ant-list-item-no-flex .ant-list-item-action{float:right}.ant-list-bordered{border:1px solid #d9d9d9;border-radius:4px}.ant-list-bordered .ant-list-header{padding-right:24px;padding-left:24px}.ant-list-bordered .ant-list-footer{padding-right:24px;padding-left:24px}.ant-list-bordered .ant-list-item{padding-right:24px;padding-left:24px;border-bottom:1px solid #e8e8e8}.ant-list-bordered .ant-list-pagination{margin:16px 24px}.ant-list-bordered.ant-list-sm .ant-list-item{padding-right:16px;padding-left:16px}.ant-list-bordered.ant-list-sm .ant-list-footer,.ant-list-bordered.ant-list-sm .ant-list-header{padding:8px 16px}.ant-list-bordered.ant-list-lg .ant-list-footer,.ant-list-bordered.ant-list-lg .ant-list-header{padding:16px 24px}@media screen and (max-width:768px){.ant-list-item-action{margin-left:24px}.ant-list-vertical .ant-list-item-extra{margin-left:24px}}@media screen and (max-width:576px){.ant-list-item{flex-wrap:wrap}.ant-list-item-action{margin-left:12px}.ant-list-vertical .ant-list-item{flex-wrap:wrap-reverse}.ant-list-vertical .ant-list-item-main{min-width:220px}.ant-list-vertical .ant-list-item-extra{margin:auto auto 16px}}
diff --git a/public/assets/admin/env.example.js b/public/assets/admin/env.example.js
new file mode 100644
index 0000000..4be0112
--- /dev/null
+++ b/public/assets/admin/env.example.js
@@ -0,0 +1,17 @@
+window.settings = {
+ // 站点标题
+ title: 'V2Board',
+ // API
+ host: '',
+ // 主题
+ theme: {
+ sidebar: 'light',
+ header: 'dark',
+ color: 'default'
+ },
+ // 背景
+ background_url: '',
+ logo: '',
+ // 需与V2Board设置中的后台路径一致
+ secure_path: 'admin'
+}
diff --git a/public/assets/admin/static/Simple-Line-Icons.0cb0b9c5.woff2 b/public/assets/admin/static/Simple-Line-Icons.0cb0b9c5.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..c49fccf510eb41b4aac2bfa21b3b87c254083bf8
GIT binary patch
literal 30064
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
XVaD<2gWrVGOVQy$mL_vR2><{9vXVmp
literal 0
HcmV?d00001
diff --git a/public/assets/admin/static/Simple-Line-Icons.78f07e2c.woff b/public/assets/admin/static/Simple-Line-Icons.78f07e2c.woff
new file mode 100644
index 0000000000000000000000000000000000000000..b17d69491bf374d36e07339bedd3349caaa30d65
GIT binary patch
literal 81332
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