Browse Source

refactor: replace jquery ajax with htmx in /user

M1Screw 2 years ago
parent
commit
4fa80f9124

+ 1 - 1
app/routes.php

@@ -27,7 +27,7 @@ return static function (Slim\App $app): void {
         $group->get('', App\Controllers\UserController::class . ':index');
         $group->get('/', App\Controllers\UserController::class . ':index');
         // 签到
-        $group->post('/checkin', App\Controllers\UserController::class . ':doCheckin');
+        $group->post('/checkin', App\Controllers\UserController::class . ':checkin');
         // 公告
         $group->get('/announcement', App\Controllers\UserController::class . ':announcement');
         // 文档

+ 44 - 49
composer.lock

@@ -69,16 +69,16 @@
         },
         {
             "name": "aws/aws-crt-php",
-            "version": "v1.2.2",
+            "version": "v1.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/awslabs/aws-crt-php.git",
-                "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9"
+                "reference": "5545a4fa310aec39f54279fdacebcce33b3ff382"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/2f1dc7b7eda080498be96a4a6d683a41583030e9",
-                "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9",
+                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/5545a4fa310aec39f54279fdacebcce33b3ff382",
+                "reference": "5545a4fa310aec39f54279fdacebcce33b3ff382",
                 "shasum": ""
             },
             "require": {
@@ -117,26 +117,26 @@
             ],
             "support": {
                 "issues": "https://github.com/awslabs/aws-crt-php/issues",
-                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.2"
+                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.3"
             },
-            "time": "2023-07-20T16:49:55+00:00"
+            "time": "2023-10-16T20:10:06+00:00"
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.283.9",
+            "version": "3.283.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "0233b9f3f2155dac35c829ce4fc1b7cdb6ff8c0a"
+                "reference": "348b68edcc83062c329cf7540c4c92d061d27d9c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0233b9f3f2155dac35c829ce4fc1b7cdb6ff8c0a",
-                "reference": "0233b9f3f2155dac35c829ce4fc1b7cdb6ff8c0a",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/348b68edcc83062c329cf7540c4c92d061d27d9c",
+                "reference": "348b68edcc83062c329cf7540c4c92d061d27d9c",
                 "shasum": ""
             },
             "require": {
-                "aws/aws-crt-php": "^1.0.4",
+                "aws/aws-crt-php": "^1.2.3",
                 "ext-json": "*",
                 "ext-pcre": "*",
                 "ext-simplexml": "*",
@@ -212,9 +212,9 @@
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.283.9"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.283.11"
             },
-            "time": "2023-10-20T20:03:26+00:00"
+            "time": "2023-10-24T18:10:38+00:00"
         },
         {
             "name": "bacon/bacon-qr-code",
@@ -1237,7 +1237,7 @@
         },
         {
             "name": "illuminate/collections",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/collections.git",
@@ -1292,7 +1292,7 @@
         },
         {
             "name": "illuminate/conditionable",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/conditionable.git",
@@ -1338,7 +1338,7 @@
         },
         {
             "name": "illuminate/container",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/container.git",
@@ -1389,7 +1389,7 @@
         },
         {
             "name": "illuminate/contracts",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/contracts.git",
@@ -1437,16 +1437,16 @@
         },
         {
             "name": "illuminate/database",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/database.git",
-                "reference": "7b82234921a80bfc7a8fe67ff3ba3043c384fc04"
+                "reference": "bfe1d2bcd955db6325709c36d098b2d9bd4d7c5d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/database/zipball/7b82234921a80bfc7a8fe67ff3ba3043c384fc04",
-                "reference": "7b82234921a80bfc7a8fe67ff3ba3043c384fc04",
+                "url": "https://api.github.com/repos/illuminate/database/zipball/bfe1d2bcd955db6325709c36d098b2d9bd4d7c5d",
+                "reference": "bfe1d2bcd955db6325709c36d098b2d9bd4d7c5d",
                 "shasum": ""
             },
             "require": {
@@ -1502,11 +1502,11 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-10-09T18:36:56+00:00"
+            "time": "2023-10-20T10:10:54+00:00"
         },
         {
             "name": "illuminate/macroable",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/macroable.git",
@@ -1552,7 +1552,7 @@
         },
         {
             "name": "illuminate/pagination",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/pagination.git",
@@ -1602,16 +1602,16 @@
         },
         {
             "name": "illuminate/support",
-            "version": "v10.28.0",
+            "version": "v10.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/support.git",
-                "reference": "dd3b3275a9dbd30736363f232e6bc2970d7681e9"
+                "reference": "e46e5864314d59fa690637e51d6cd2113acb2e7b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/support/zipball/dd3b3275a9dbd30736363f232e6bc2970d7681e9",
-                "reference": "dd3b3275a9dbd30736363f232e6bc2970d7681e9",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/e46e5864314d59fa690637e51d6cd2113acb2e7b",
+                "reference": "e46e5864314d59fa690637e51d6cd2113acb2e7b",
                 "shasum": ""
             },
             "require": {
@@ -1669,7 +1669,7 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-10-06T13:41:04+00:00"
+            "time": "2023-10-18T14:12:13+00:00"
         },
         {
             "name": "irazasyed/telegram-bot-sdk",
@@ -3241,31 +3241,26 @@
         },
         {
             "name": "php-http/promise",
-            "version": "1.1.0",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-http/promise.git",
-                "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88"
+                "reference": "ef4905bfb492ff389eb7f12e26925a0f20073050"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
-                "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
+                "url": "https://api.github.com/repos/php-http/promise/zipball/ef4905bfb492ff389eb7f12e26925a0f20073050",
+                "reference": "ef4905bfb492ff389eb7f12e26925a0f20073050",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.1 || ^8.0"
             },
             "require-dev": {
-                "friends-of-phpspec/phpspec-code-coverage": "^4.3.2",
-                "phpspec/phpspec": "^5.1.2 || ^6.2"
+                "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3",
+                "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.1-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Http\\Promise\\": "src/"
@@ -3292,9 +3287,9 @@
             ],
             "support": {
                 "issues": "https://github.com/php-http/promise/issues",
-                "source": "https://github.com/php-http/promise/tree/1.1.0"
+                "source": "https://github.com/php-http/promise/tree/1.2.0"
             },
-            "time": "2020-07-07T09:29:14+00:00"
+            "time": "2023-10-24T09:20:26+00:00"
         },
         {
             "name": "phpmailer/phpmailer",
@@ -4373,16 +4368,16 @@
         },
         {
             "name": "sentry/sentry",
-            "version": "3.21.0",
+            "version": "3.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/getsentry/sentry-php.git",
-                "reference": "624aafc22b84b089ffa43b71fb01e0096505ec4f"
+                "reference": "c0e3df5a5c1d133cd9461e7672568ff07042c19d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/624aafc22b84b089ffa43b71fb01e0096505ec4f",
-                "reference": "624aafc22b84b089ffa43b71fb01e0096505ec4f",
+                "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/c0e3df5a5c1d133cd9461e7672568ff07042c19d",
+                "reference": "c0e3df5a5c1d133cd9461e7672568ff07042c19d",
                 "shasum": ""
             },
             "require": {
@@ -4400,7 +4395,7 @@
                 "psr/http-factory": "^1.0",
                 "psr/http-factory-implementation": "^1.0",
                 "psr/log": "^1.0|^2.0|^3.0",
-                "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0",
+                "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0|^7.0",
                 "symfony/polyfill-php80": "^1.17"
             },
             "conflict": {
@@ -4457,7 +4452,7 @@
             ],
             "support": {
                 "issues": "https://github.com/getsentry/sentry-php/issues",
-                "source": "https://github.com/getsentry/sentry-php/tree/3.21.0"
+                "source": "https://github.com/getsentry/sentry-php/tree/3.22.0"
             },
             "funding": [
                 {
@@ -4469,7 +4464,7 @@
                     "type": "custom"
                 }
             ],
-            "time": "2023-07-31T15:31:24+00:00"
+            "time": "2023-10-23T20:34:53+00:00"
         },
         {
             "name": "slim/http",

+ 88 - 389
resources/views/tabler/user/edit.tpl

@@ -74,13 +74,24 @@
                                                 <div class="card-footer">
                                                     <div class="d-flex">
                                                         {if $public_setting['reg_email_verify'] && $config['enable_change_email']}
-                                                            <a id="email-verify" class="btn btn-link">获取验证码</a>
-                                                            <button id="modify-email"
-                                                                    class="btn btn-primary ms-auto">修改
+                                                            <button id="email-verify" class="btn btn-link"
+                                                                    hx-post="/user/send" hx-swap="none"
+                                                                    hx-vals='js:{ email: document.getElementById("newemail").value }'>
+                                                                获取验证码
+                                                            </button>
+                                                            <button id="modify-email" class="btn btn-primary ms-auto"
+                                                                    hx-post="/user/email" hx-swap="none"
+                                                                    hx-vals='js:{
+                                                                        newemail: document.getElementById("new-email").value,
+                                                                        emailcode: document.getElementById("email-code").value
+                                                                    }'>
+                                                                修改
                                                             </button>
                                                         {elseif $config['enable_change_email']}
-                                                            <button id="modify-email"
-                                                                    class="btn btn-primary ms-auto">修改
+                                                            <button id="modify-email" class="btn btn-primary ms-auto"
+                                                                    hx-post="/user/email" hx-swap="none"
+                                                                    hx-vals='js:{ newemail: document.getElementById("new-email").value }'>
+                                                                修改
                                                             </button>
                                                         {else}
                                                             <button id="modify-email" class="btn btn-primary ms-auto"
@@ -97,13 +108,17 @@
                                                     <h3 class="card-title">用戶名</h3>
                                                     <p>当前用戶名:<code>{$user->user_name}</code></p>
                                                     <div class="mb-3">
-                                                        <input id="new-nickname" type="text" class="form-control"
+                                                        <input id="new-username" type="text" class="form-control"
                                                                placeholder="新用戶名" autocomplete="off">
                                                     </div>
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="modify-username" class="btn btn-primary ms-auto">修改</a>
+                                                        <button id="modify-username" class="btn btn-primary ms-auto"
+                                                           hx-post="/user/username" hx-swap="none"
+                                                           hx-vals='js:{ newusername: document.getElementById("new-username").value }'>
+                                                            修改
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -157,7 +172,8 @@
                                                 {if $user->im_type !== 0}
                                                     <div class="card-footer">
                                                         <div class="d-flex">
-                                                            <button id="unbind-im" class="btn btn-red ms-auto">
+                                                            <button id="unbind-im" class="btn btn-red ms-auto"
+                                                                    hx-post="/user/unbind_im" hx-swap="none">
                                                                 解绑
                                                             </button>
                                                         </div>
@@ -204,7 +220,7 @@
                                                                 </select>
                                                             </div>
                                                             <div class="mb-3">
-                                                                <input id="2fa-test-code" type="text"
+                                                                <input id="ga-test-code" type="text"
                                                                        class="form-control"
                                                                        placeholder="测试两步认证验证码">
                                                             </div>
@@ -216,9 +232,20 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="reset-2fa" class="btn btn-link">重置</a>
-                                                        <a id="test-2fa" class="btn btn-link">测试</a>
-                                                        <a id="save-2fa" class="btn btn-primary ms-auto">设置</a>
+                                                        <button id="reset-2fa" class="btn btn-link"
+                                                                hx-post="/user/ga_reset" hx-swap="none">
+                                                            重置
+                                                        </button>
+                                                        <button id="test-2fa" class="btn btn-link"
+                                                                hx-post="/user/ga_check" hx-swap="none"
+                                                                hx-vals='js:{ code: document.getElementById("ga-test-code").value }'>
+                                                            测试
+                                                        </button>
+                                                        <button id="save-2fa" class="btn btn-primary ms-auto"
+                                                                hx-post="/user/ga_set" hx-swap="none"
+                                                                hx-vals='js:{ enable: document.getElementById("ga-enable").value }'>
+                                                            设置
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -242,7 +269,7 @@
                                                     </div>
                                                     <div class="mb-3">
                                                         <form>
-                                                            <input id="again-new-password" type="password"
+                                                            <input id="repeat-new-password" type="password"
                                                                    class="form-control" placeholder="再次输入新密码"
                                                                    autocomplete="off">
                                                         </form>
@@ -250,8 +277,14 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="modify-login-passwd"
-                                                           class="btn btn-primary ms-auto">修改</a>
+                                                        <button id="modify-login-passwd" class="btn btn-primary ms-auto"
+                                                                hx-post="/user/password" hx-swap="none"
+                                                                hx-vals='js:{
+                                                                pwd: document.getElementById("new-password").value
+                                                                repwd: document.getElementById("repeat-new-password").value
+                                                                oldpwd: document.getElementById("password").value }'>
+                                                            修改
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -279,8 +312,11 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="modify-user-method"
-                                                           class="btn btn-primary ms-auto">修改</a>
+                                                        <button id="modify-user-method" class="btn btn-primary ms-auto"
+                                                                hx-post="/user/method" hx-swap="none"
+                                                                hx-vals='js:{ method: document.getElementById("user-method").value }'>
+                                                            修改
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -294,8 +330,10 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="reset-sub-url"
-                                                           class="btn btn-primary ms-auto bg-red">重置</a>
+                                                        <button id="reset-sub-url" class="btn btn-primary ms-auto bg-red"
+                                                                hx-post="/user/url_reset" hx-swap="none">
+                                                            重置
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -310,8 +348,10 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="reset-passwd"
-                                                           class="btn btn-primary ms-auto bg-red">重置</a>
+                                                        <button id="reset-passwd" class="btn btn-primary ms-auto bg-red"
+                                                                hx-post="/user/passwd_reset" hx-swap="none">
+                                                            重置
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -325,7 +365,7 @@
                                                 <div class="card-body">
                                                     <h3 class="card-title">每日流量报告</h3>
                                                     <div class="mb-3">
-                                                        <select id="daily-report" class="form-select">
+                                                        <select id="daily-mail" class="form-select">
                                                             <option value="0"
                                                                     {if $user->daily_mail_enable === 0}selected{/if}>
                                                                 不接收
@@ -343,8 +383,11 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="modify-daily-report"
-                                                           class="btn btn-primary ms-auto">修改</a>
+                                                        <button id="modify-daily-mail" class="btn btn-primary ms-auto"
+                                                                hx-post="/user/daily_mail" hx-swap="none"
+                                                                hx-vals='js:{ mail: document.getElementById("daily-mail").value }'>
+                                                            修改
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -369,8 +412,11 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="modify-contact-method"
-                                                           class="btn btn-primary ms-auto">修改</a>
+                                                        <button id="modify-contact-method" class="btn btn-primary ms-auto"
+                                                                hx-post="/user/contact_method" hx-swap="none"
+                                                                hx-vals='js:{ contact: document.getElementById("contact-method").value }'>
+                                                            修改
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -391,8 +437,11 @@
                                                 </div>
                                                 <div class="card-footer">
                                                     <div class="d-flex">
-                                                        <a id="modify-user-theme"
-                                                           class="btn btn-primary ms-auto">修改</a>
+                                                        <button id="modify-user-theme" class="btn btn-primary ms-auto"
+                                                                hx-post="/user/theme" hx-swap="none"
+                                                                hx-vals='js:{ theme: document.getElementById("user-theme").value }'>
+                                                            修改
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -409,11 +458,11 @@
                                                         <h3 class="card-title">删除账户数据</h3>
                                                     </div>
                                                     <div class="card-footer">
-                                                        <a href="#" class="btn btn-red" data-bs-toggle="modal"
+                                                        <button class="btn btn-red" data-bs-toggle="modal"
                                                            data-bs-target="#destroy-account">
                                                             <i class="ti ti-trash icon"></i>
                                                             确认删除
-                                                        </a>
+                                                        </button>
                                                     </div>
                                                 </div>
                                             </div>
@@ -432,7 +481,7 @@
         <div class="modal modal-blur fade" id="destroy-account" tabindex="-1" role="dialog" aria-hidden="true">
             <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
                 <div class="modal-content">
-                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                    <button class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                     <div class="modal-status bg-danger"></div>
                     <div class="modal-body text-center py-4">
                         <i class="ti ti-alert-circle icon mb-2 text-danger icon-lg" style="font-size:3.5rem;"></i>
@@ -443,8 +492,7 @@
                         <div class="py-3">
                             <form>
                                 <input id="confirm-passwd" type="password" class="form-control"
-                                       placeholder="输入登录密码"
-                                       autocomplete="off">
+                                       placeholder="输入登录密码" autocomplete="off">
                             </form>
                         </div>
                     </div>
@@ -452,63 +500,16 @@
                         <div class="w-100">
                             <div class="row">
                                 <div class="col">
-                                    <a href="#" class="btn w-100" data-bs-dismiss="modal">
+                                    <button class="btn w-100" data-bs-dismiss="modal">
                                         取消
-                                    </a>
-                                </div>
-                                <div class="col">
-                                    <a href="#" id="confirm-destroy" class="btn btn-danger w-100"
-                                       data-bs-dismiss="modal">
-                                        确认
-                                    </a>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </div>
-        <div class="modal modal-blur fade" id="destroy-account-success" tabindex="-1" role="dialog" aria-hidden="true">
-            <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
-                <div class="modal-content">
-                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
-                    <div class="modal-status bg-success"></div>
-                    <div class="modal-body text-center py-4">
-                        <i class="ti ti-circle-check icon mb-2 text-green icon-lg" style="font-size:3.5rem;"></i>
-                        <h3>删除成功</h3>
-                        <p id="success-message" class="text-secondary">删除成功</p>
-                    </div>
-                    <div class="modal-footer">
-                        <div class="w-100">
-                            <div class="row">
-                                <div class="col">
-                                    <a href="#" class="btn w-100" data-bs-dismiss="modal">
-                                        好
-                                    </a>
+                                    </button>
                                 </div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </div>
-        <div class="modal modal-blur fade" id="destroy-account-fail" tabindex="-1" role="dialog" aria-hidden="true">
-            <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
-                <div class="modal-content">
-                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
-                    <div class="modal-status bg-danger"></div>
-                    <div class="modal-body text-center py-4">
-                        <i class="ti ti-circle-x icon mb-2 text-danger icon-lg" style="font-size:3.5rem;"></i>
-                        <h3>删除失败</h3>
-                        <p id="error-message" class="text-secondary">删除失败</p>
-                    </div>
-                    <div class="modal-footer">
-                        <div class="w-100">
-                            <div class="row">
                                 <div class="col">
-                                    <a href="#" class="btn btn-danger w-100" data-bs-dismiss="modal">
+                                    <button href="#" id="confirm-kill" class="btn btn-danger w-100" data-bs-dismiss="modal"
+                                            hx-post="/user/kill" hx-swap="none"
+                                            hx-vals='js:{ passwd: document.getElementById("confirm-passwd").value }'>
                                         确认
-                                    </a>
+                                    </button>
                                 </div>
                             </div>
                         </div>
@@ -519,7 +520,7 @@
     {/if}
 
     <script>
-        var qrcode = new QRCode('qrcode', {
+        let qrcode = new QRCode('qrcode', {
             text: "{$gaurl}",
             width: 128,
             height: 128,
@@ -528,308 +529,6 @@
             correctLevel: QRCode.CorrectLevel.H
         });
 
-        var clipboard = new ClipboardJS('.copy');
-        clipboard.on('success', function (e) {
-            $('#success-noreload-message').text('已复制到剪切板');
-            $('#success-noreload-dialog').modal('show');
-        });
-
-        $("#modify-email").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/email",
-                dataType: "json",
-                data: {
-                    {if $public_setting['reg_email_verify']}
-                    emailcode: $('#email-code').val(),
-                    {/if}
-                    newemail: $('#new-email').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#email-verify").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/send",
-                dataType: "json",
-                data: {
-                    email: $('#new-email').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#modify-username").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/username",
-                dataType: "json",
-                data: {
-                    newusername: $('#new-nickname').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#modify-user-method").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/method",
-                dataType: "json",
-                data: {
-                    method: $('#user-method').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#reset-sub-url").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/url_reset",
-                dataType: "json",
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#reset-passwd").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/passwd_reset",
-                dataType: "json",
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#modify-login-passwd").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/password",
-                dataType: "json",
-                data: {
-                    pwd: $('#new-password').val(),
-                    repwd: $('#again-new-password').val(),
-                    oldpwd: $('#password').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#unbind-im").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/unbind_im",
-                dataType: "json",
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#reset-2fa").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/ga_reset",
-                dataType: "json",
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#test-2fa").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/ga_check",
-                dataType: "json",
-                data: {
-                    code: $('#2fa-test-code').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#save-2fa").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/ga_set",
-                dataType: "json",
-                data: {
-                    enable: $('#ga-enable').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#modify-daily-report").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/daily_mail",
-                dataType: "json",
-                data: {
-                    mail: $('#daily-report').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#modify-contact-method").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/contact_method",
-                dataType: "json",
-                data: {
-                    contact: $('#contact-method').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        $("#modify-user-theme").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/theme",
-                dataType: "json",
-                data: {
-                    theme: $('#user-theme').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                        window.setTimeout("location.reload()", {$config['jump_delay']});
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-
-        {if $config['enable_kill']}
-        $("#confirm-destroy").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/kill",
-                dataType: "json",
-                data: {
-                    passwd: $('#confirm-passwd').val(),
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#destroy-account-success').modal('show');
-                    } else {
-                        $('#error-message').text(data.msg);
-                        $('#destroy-account-fail').modal('show');
-                    }
-                }
-            })
-        });
-        {/if}
-
         {if $user->im_type === 0 && $user->im_value === ''}
         $("#imtype").on('change', function () {
             if ($(this).val() === '0') {

+ 18 - 43
resources/views/tabler/user/footer.tpl

@@ -22,30 +22,6 @@
     </div>
 </div>
 
-<div class="modal modal-blur fade" id="success-noreload-dialog" tabindex="-1" role="dialog" aria-hidden="true">
-    <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
-        <div class="modal-content">
-            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
-            <div class="modal-status bg-success"></div>
-            <div class="modal-body text-center py-4">
-                <i class="ti ti-circle-check icon mb-2 text-green icon-lg" style="font-size:3.5rem;"></i>
-                <p id="success-noreload-message" class="text-secondary">成功</p>
-            </div>
-            <div class="modal-footer">
-                <div class="w-100">
-                    <div class="row">
-                        <div class="col">
-                            <a id="success-noreload-confirm" href="" class="btn w-100" data-bs-dismiss="modal">
-                                好
-                            </a>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-
 <div class="modal modal-blur fade" id="fail-dialog" tabindex="-1" role="dialog" aria-hidden="true">
     <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
         <div class="modal-content">
@@ -95,28 +71,27 @@
 </div>
 <!-- js -->
 <script>
-    $("#switch_theme_mode").click(function () {
-        $.ajax({
-            type: "POST",
-            url: "/user/switch_theme_mode",
-            dataType: "json",
-            success: function (data) {
-                if (data.ret === 1) {
-                    $('#success-message').text(data.msg);
-                    $('#success-dialog').modal('show');
-                    window.setTimeout("location.reload()", {$config['jump_delay']});
-                } else {
-                    $('#fail-message').text(data.msg);
-                    $('#fail-dialog').modal('show');
-                }
-            }
-        })
-    });
+    htmx.on("htmx:afterRequest", function(evt) {
+        if (evt.detail.xhr.getResponseHeader('HX-Refresh') === 'true' ||
+            typeof evt.detail.xhr.getResponseHeader('HX-Trigger') !== 'undefined')
+        {
+            return;
+        }
+
+        let data = JSON.parse(evt.detail.xhr.response);
+        let successDialog = new bootstrap.Modal(document.getElementById('success-dialog'));
+        let failDialog = new bootstrap.Modal(document.getElementById('fail-dialog'));
 
-    $("#success-confirm").click(function () {
-        location.reload();
+        if (data.ret === 1) {
+            document.getElementById("success-message").innerHTML = data.msg;
+            successDialog.show();
+        } else {
+            document.getElementById("fail-message").innerHTML = data.msg;
+            failDialog.show();
+        }
     });
 </script>
+
 <script src="//{$config['jsdelivr_url']}/npm/@tabler/core@latest/dist/js/tabler.min.js"></script>
 <script>console.table([['数据库查询', '执行时间'], ['{count($queryLog)} 次', '{$optTime} ms']])</script>
 

+ 9 - 3
resources/views/tabler/user/header.tpl

@@ -14,8 +14,8 @@
     <!-- JS files -->
     <script src="/assets/js/fuck.min.js"></script>
     <script src="//{$config['jsdelivr_url']}/npm/qrcode_js@latest/qrcode.min.js"></script>
-    <script src="//{$config['jsdelivr_url']}/npm/clipboard@latest/dist/clipboard.min.js"></script>
     <script src="//{$config['jsdelivr_url']}/npm/jquery/dist/jquery.min.js"></script>
+    <script src="//{$config['jsdelivr_url']}/npm/htmx.org@latest/dist/htmx.min.js"></script>
     <style>
         .home-subtitle {
             font-size: 14px;
@@ -55,9 +55,15 @@
                     </a>
                     <div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
                         {if $user->is_dark_mode}
-                            <a id="switch_theme_mode" class="dropdown-item">浅色模式</a>
+                            <a id="switch-theme-mode" class="dropdown-item"
+                               hx-post="/user/switch_theme_mode" hx-swap="none">
+                                浅色模式
+                            </a>
                         {else}
-                            <a id="switch_theme_mode" class="dropdown-item">深色模式</a>
+                            <a id="switch_theme_mode" class="dropdown-item"
+                               hx-post="/user/switch_theme_mode" hx-swap="none">
+                                深色模式
+                            </a>
                         {/if}
                         <a href="/user/logout" class="dropdown-item">登出</a>
                     </div>

+ 16 - 32
resources/views/tabler/user/index.tpl

@@ -1,5 +1,7 @@
 {include file='user/header.tpl'}
 
+<script src="//{$config['jsdelivr_url']}/npm/clipboard@latest/dist/clipboard.min.js"></script>
+
 <div class="page-wrapper">
     <div class="container-xl">
         <div class="page-header d-print-none text-white">
@@ -574,7 +576,16 @@
                                                 <div id="geetest"></div>
                                             {/if}
                                         {/if}
-                                        <button id="check-in" class="btn btn-primary ms-auto">签到</button>
+                                        <button id="check-in" class="btn btn-primary ms-auto"
+                                                hx-post="/user/checkin" hx-swap="none"
+                                                {if $public_setting['captcha_provider'] === 'turnstile'}
+                                                    hx-vals='js:{ turnstile: document.querySelector("[name=cf-turnstile-response]").value }'
+                                                {/if}
+                                                {if $public_setting['captcha_provider'] === 'geetest'}
+                                                    hx-vals='js:{ geetest: geetest_result }'
+                                                {/if}>
+                                            签到
+                                        </button>
                                     {/if}
                                 </div>
                             </div>
@@ -586,37 +597,10 @@
     </div>
 
     <script>
-        var clipboard = new ClipboardJS('.copy');
+        let clipboard = new ClipboardJS('.copy');
         clipboard.on('success', function (e) {
-            $('#success-noreload-message').text('已复制到剪切板');
-            $('#success-noreload-dialog').modal('show');
-        });
-
-        $("#check-in").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/checkin",
-                dataType: "json",
-                data: {
-                    {if $public_setting['enable_checkin_captcha'] && $user->isAbleToCheckin()}
-                    {if $public_setting['captcha_provider'] === 'turnstile'}
-                    turnstile: $('input[name=cf-turnstile-response]').val(),
-                    {/if}
-                    {if $public_setting['captcha_provider'] === 'geetest'}
-                    geetest: geetest_result,
-                    {/if}
-                    {/if}
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
+            $('#success-message').text('已复制到剪切板');
+            $('#success-dialog').modal('show');
         });
     </script>
 
@@ -627,7 +611,7 @@
     {if $public_setting['captcha_provider'] === 'geetest'}
         <script src="https://static.geetest.com/v4/gt4.js"></script>
         <script>
-            var geetest_result = '';
+            let geetest_result = '';
             initGeetest4({
                 captchaId: '{$captcha['geetest_id']}',
                 product: 'float',

+ 11 - 23
resources/views/tabler/user/invite.tpl

@@ -1,5 +1,7 @@
 {include file='user/header.tpl'}
 
+<script src="//{$config['jsdelivr_url']}/npm/clipboard@latest/dist/clipboard.min.js"></script>
+
 <div class="page-wrapper">
     <div class="container-xl">
         <div class="page-header d-print-none text-white">
@@ -43,9 +45,12 @@
                                 </div>
                                 <div class="card-footer">
                                     <div class="d-flex">
-                                        <a id="reset-url" class="btn text-red btn-link">重置</a>
-                                        <a data-clipboard-text="{$invite_url}"
-                                           class="copy btn btn-primary ms-auto">复制</a>
+                                        <button id="reset-url" class="btn text-red btn-link"
+                                                hx-post="/user/invite_reset" hx-swap="none">
+                                            重置
+                                        </button>
+                                        <button data-clipboard-text="{$invite_url}"
+                                           class="copy btn btn-primary ms-auto">复制</button>
                                     </div>
                                 </div>
                             </div>
@@ -88,27 +93,10 @@
     </div>
 
     <script>
-        var clipboard = new ClipboardJS('.copy');
+        let clipboard = new ClipboardJS('.copy');
         clipboard.on('success', function (e) {
-            $('#success-noreload-message').text('已复制到剪切板');
-            $('#success-noreload-dialog').modal('show');
-        });
-
-        $("#reset-url").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/invite_reset",
-                dataType: "json",
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
+            $('#success-message').text('已复制到剪切板');
+            $('#success-dialog').modal('show');
         });
     </script>
 

+ 1 - 1
resources/views/tabler/user/invoice/index.tpl

@@ -41,7 +41,7 @@
     </div>
 
     <script>
-        var table = $('#data_table').DataTable({
+        let table = $('#data_table').DataTable({
             ajax: {
                 url: '/user/invoice/ajax',
                 type: 'POST',

+ 4 - 24
resources/views/tabler/user/money.tpl

@@ -78,34 +78,14 @@
                 </div>
                 <div class="modal-footer">
                     <button type="button" class="btn me-auto" data-bs-dismiss="modal">取消</button>
-                    <button id="apply-giftcard"
-                            type="button" class="btn btn-primary" data-bs-dismiss="modal">兑换
+                    <button id="apply-giftcard" class="btn btn-primary" data-bs-dismiss="modal"
+                            hx-post="/user/giftcard" hx-swap="none"
+                            hx-vals='js:{ giftcard: document.getElementById("giftcard").value }'>
+                        兑换
                     </button>
                 </div>
             </div>
         </div>
     </div>
 
-    <script>
-        $("#apply-giftcard").click(function () {
-            $.ajax({
-                url: '/user/giftcard',
-                type: 'POST',
-                dataType: "json",
-                data: {
-                    giftcard: $('#giftcard').val(),
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-    </script>
-
 {include file='user/footer.tpl'}

+ 3 - 3
resources/views/tabler/user/order/create.tpl

@@ -165,11 +165,11 @@
                 },
                 success: function (data) {
                     if (data.ret === 1) {
-                        $('#success-noreload-message').text(data.msg);
-                        $('#success-noreload-dialog').modal('show');
+                        $('#success-message').text(data.msg);
+                        $('#success-dialog').modal('show');
                         setTimeout(function () {
                             $(location).attr('href', '/user/invoice/' + data.invoice_id + '/view');
-                        }, 1500);
+                        }, $config['jump_delay']);
                     } else {
                         $('#fail-message').text(data.msg);
                         $('#fail-dialog').modal('show');

+ 1 - 1
resources/views/tabler/user/order/index.tpl

@@ -41,7 +41,7 @@
     </div>
 
     <script>
-        var table = $('#data_table').DataTable({
+        let table = $('#data_table').DataTable({
             ajax: {
                 url: '/user/order/ajax',
                 type: 'POST',

+ 9 - 12
resources/views/tabler/user/rate.tpl

@@ -1,7 +1,5 @@
 {include file='user/header.tpl'}
 
-<script src="//{$config['jsdelivr_url']}/npm/htmx.org@latest/dist/htmx.min.js"></script>
-
 <div class="page-wrapper">
     <div class="container-xl">
         <div class="page-header d-print-none text-white">
@@ -32,11 +30,9 @@
                                         <div class="dropdown-menu dropdown-menu-end">
                                             {foreach $nodes as $node}
                                             <a class="dropdown-item" hx-post="/user/rate" hx-swap="none"
-                                               hx-vals='
-                                                {
-                                                   "node_id": "{$node['id']}"
-                                                }
-                                            '>{$node['name']}</a>
+                                                hx-vals='{ "node_id": "{$node['id']}" }'>
+                                                {$node['name']}
+                                            </a>
                                             {/foreach}
                                         </div>
                                     </div>
@@ -51,7 +47,7 @@
     </div>
 
     <script>
-        htmx.on("htmx:afterRequest", function(evt) {
+        document.body.addEventListener("drawChart", function(evt){
             var chart = window.ApexCharts && new ApexCharts(document.getElementById('rate-chart'), {
                 chart: {
                     type: "bar",
@@ -112,7 +108,8 @@
                     axisBorder: {
                         show: false,
                     },
-                    categories: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],
+                    categories: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12',
+                        '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],
                 },
                 yaxis: {
                     title: {
@@ -128,15 +125,15 @@
                     show: false,
                 },
             });
-            document.getElementById('dropdown-toggle').innerHTML = JSON.parse(evt.detail.xhr.response).msg;
+            document.getElementById('dropdown-toggle').innerHTML = evt.detail.msg;
             chart.render();
             chart.updateOptions({
                 series: [{
                     name: "倍率",
-                    data: JSON.parse(evt.detail.xhr.response).data
+                    data: evt.detail.data
                 }],
             });
-        });
+        })
     </script>
 
     <script src="//{$config['jsdelivr_url']}/npm/@tabler/core@latest/dist/libs/apexcharts/dist/apexcharts.min.js"></script>

+ 7 - 26
resources/views/tabler/user/ticket/index.tpl

@@ -106,36 +106,17 @@
                 </div>
                 <div class="modal-footer">
                     <button type="button" class="btn me-auto" data-bs-dismiss="modal">取消</button>
-                    <button id="create-ticket-button" type="button" class="btn btn-primary"
-                            data-bs-dismiss="modal">创建
+                    <button id="create-ticket-button" class="btn btn-primary" data-bs-dismiss="modal"
+                            hx-post="/user/ticket" hx-swap="none"
+                            hx-vals='js:{
+                            title: document.getElementById("ticket-title").value
+                            comment: document.getElementById("ticket-comment").value
+                            type: document.getElementById("ticket-type").value }'>
+                        创建
                     </button>
                 </div>
             </div>
         </div>
     </div>
 
-    <script>
-        $("#create-ticket-button").click(function () {
-            $.ajax({
-                type: "POST",
-                url: "/user/ticket",
-                dataType: "json",
-                data: {
-                    title: $('#ticket-title').val(),
-                    comment: $('#ticket-comment').val(),
-                    type: $('#ticket-type').val(),
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-    </script>
-
 {include file='user/footer.tpl'}

+ 7 - 26
resources/views/tabler/user/ticket/view.tpl

@@ -107,42 +107,23 @@
             <div class="modal-content">
                 <div class="modal-header">
                     <h5 class="modal-title">添加回复</h5>
-                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                    <button class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                 </div>
                 <div class="modal-body">
                     <div class="mb-3">
-                        <textarea id="reply-comment" class="form-control" rows="10"
-                                  placeholder="请输入回复内容"></textarea>
+                        <textarea id="reply-comment" class="form-control" rows="15" placeholder="请输入回复内容"></textarea>
                     </div>
                 </div>
                 <div class="modal-footer">
                     <button type="button" class="btn me-auto" data-bs-dismiss="modal">取消</button>
-                    <button id="reply" type="button" class="btn btn-primary" data-bs-dismiss="modal">回复</button>
+                    <button id="reply" class="btn btn-primary" data-bs-dismiss="modal"
+                            hx-put="/user/ticket/{$ticket->id}" hx-swap="none"
+                            hx-vals='js:{ comment: document.getElementById("reply-comment").value }'>
+                        回复
+                    </button>
                 </div>
             </div>
         </div>
     </div>
 
-    <script>
-        $("#reply").click(function () {
-            $.ajax({
-                url: "/user/ticket/{$ticket->id}",
-                type: 'PUT',
-                dataType: "json",
-                data: {
-                    comment: $('#reply-comment').val()
-                },
-                success: function (data) {
-                    if (data.ret === 1) {
-                        $('#success-message').text(data.msg);
-                        $('#success-dialog').modal('show');
-                    } else {
-                        $('#fail-message').text(data.msg);
-                        $('#fail-dialog').modal('show');
-                    }
-                }
-            })
-        });
-    </script>
-
     {include file='user/footer.tpl'}

+ 8 - 2
src/Controllers/User/InfoController.php

@@ -284,7 +284,10 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::success($response, '修改成功');
+        return $response->withHeader('HX-Refresh', 'true')->withJson([
+            'ret' => 1,
+            'msg' => '修改成功',
+        ]);
     }
 
     public function sendToGulag(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -304,7 +307,10 @@ final class InfoController extends BaseController
             Auth::logout();
             $user->kill();
 
-            return ResponseHelper::success($response, '你的帐号已被送去古拉格劳动改造,再见');
+            return $response->withHeader('HX-Refresh', 'true')->withJson([
+                'ret' => 1,
+                'msg' => '你的帐号已被送去古拉格劳动改造,再见',
+            ]);
         }
 
         return ResponseHelper::error($response, '自助账号删除未启用');

+ 11 - 1
src/Controllers/User/RateController.php

@@ -14,6 +14,7 @@ use Slim\Http\Response;
 use Slim\Http\ServerRequest;
 use voku\helper\AntiXSS;
 use function array_fill;
+use function json_encode;
 
 final class RateController extends BaseController
 {
@@ -74,6 +75,15 @@ final class RateController extends BaseController
             $rates = array_fill(0, 24, $node->traffic_rate);
         }
 
-        return ResponseHelper::successWithData($response, $node->name, $rates);
+        $event = json_encode([
+            'drawChart' => [
+                'msg' => $node->name,
+                'data' => $rates,
+            ],
+        ]);
+
+        return $response->withHeader('HX-Trigger', $event)->withJson([
+            'ret' => 1,
+        ]);
     }
 }

+ 20 - 24
src/Controllers/UserController.php

@@ -138,14 +138,7 @@ final class UserController extends BaseController
             ->fetch('user/invite.tpl'));
     }
 
-    public function logout(ServerRequest $request, Response $response, array $args): Response
-    {
-        Auth::logout();
-
-        return $response->withStatus(302)->withHeader('Location', '/');
-    }
-
-    public function doCheckIn(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function checkin(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         if (! $_ENV['enable_checkin']) {
             return ResponseHelper::error($response, '暂时还不能签到');
@@ -165,19 +158,27 @@ final class UserController extends BaseController
             return ResponseHelper::error($response, (string) $checkin['msg']);
         }
 
-        return $response->withJson([
+        return $response->withHeader('HX-Refresh', 'true')->withJson([
             'ret' => 1,
-            'trafficInfo' => [
-                'todayUsedTraffic' => $this->user->todayUsedTraffic(),
-                'lastUsedTraffic' => $this->user->lastUsedTraffic(),
-                'unUsedTraffic' => $this->user->unusedTraffic(),
-            ],
-            'traffic' => Tools::autoBytes($this->user->transfer_enable),
-            'unflowtraffic' => $this->user->transfer_enable,
             'msg' => $checkin['msg'],
         ]);
     }
 
+    public function switchThemeMode(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    {
+        $user = $this->user;
+        $user->is_dark_mode = $user->is_dark_mode === 1 ? 0 : 1;
+
+        if (! $user->save()) {
+            return ResponseHelper::error($response, '切换失败');
+        }
+
+        return $response->withHeader('HX-Refresh', 'true')->withJson([
+            'ret' => 1,
+            'msg' => '切换成功',
+        ]);
+    }
+
     /**
      * @throws Exception
      */
@@ -190,15 +191,10 @@ final class UserController extends BaseController
             ->fetch('user/banned.tpl'));
     }
 
-    public function switchThemeMode(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function logout(ServerRequest $request, Response $response, array $args): Response
     {
-        $user = $this->user;
-        $user->is_dark_mode = $user->is_dark_mode === 1 ? 0 : 1;
-
-        if (! $user->save()) {
-            return ResponseHelper::error($response, '切换失败');
-        }
+        Auth::logout();
 
-        return ResponseHelper::success($response, '切换成功');
+        return $response->withStatus(302)->withHeader('Location', '/');
     }
 }

+ 1 - 0
src/Models/User.php

@@ -301,6 +301,7 @@ final class User extends Model
             } catch (Exception $e) {
                 $traffic = 0;
             }
+
             $this->transfer_enable += Tools::toMB($traffic);
             $this->last_check_in_time = time();
             $this->save();