From e627ab0726880ddde8469393756c182c1881ed03 Mon Sep 17 00:00:00 2001 From: veypi Date: Thu, 26 Feb 2026 05:42:28 +0800 Subject: [PATCH] feat(ui): Add OAuth callback page and improve auth flow - Add new OAuth callback page with loading states and error handling - Create reusable icon component (ico.html) for SVG icons - Remove deprecated public.html layout, merge into default - Update login page with improved third-party auth integration - Add i18n translations for OAuth-related messages - Update routes to include callback page and handle auth redirects - Enhance vbase.js with OAuth utilities and token management --- ui/env.js | 13 +- ui/langs.json | 32 +- ui/layout/default.html | 20 +- ui/layout/ico.html | 186 ++++++++++ ui/layout/public.html | 35 -- ui/page/auth/callback.html | 568 +++++++++++++++++++++++++++++++ ui/page/auth/login.html | 81 +++-- ui/page/sys/oauth/providers.html | 20 +- ui/routes.js | 5 +- ui/vbase.js | 81 ++++- 10 files changed, 943 insertions(+), 98 deletions(-) create mode 100644 ui/layout/ico.html delete mode 100644 ui/layout/public.html create mode 100644 ui/page/auth/callback.html diff --git a/ui/env.js b/ui/env.js index 0a53ae3..820d544 100644 --- a/ui/env.js +++ b/ui/env.js @@ -4,19 +4,26 @@ import VBase from './vbase.js' export default async ($env) => { // Load i18n try { - const langs = await (await fetch('/langs.json')).json() + const langs = await (await fetch($env.scoped + '/langs.json')).json() $env.$i18n.load(langs) } catch (e) { console.error('Failed to load langs.json', e) } // Initialize VBase Service - const vbase = new VBase('vb', '/'); // Relative path + const vbase = new VBase('vb', $env.scoped); // Relative path $env.$vbase = vbase; - // Wrap Axios + // Wrap Axios: add auth header vbase.wrapAxios($env.$axios); + $env.$axios.interceptors.response.use(function(response) { + return response?.data + }, function(error) { + let data = error.response ? error.response.data : error.response + return Promise.reject(data?.message || data); + }); + // Router Guard $env.$router.beforeEnter = async (to, from, next) => { const isAuth = to.meta && to.meta.auth; diff --git a/ui/langs.json b/ui/langs.json index 029c169..8bcf1e5 100644 --- a/ui/langs.json +++ b/ui/langs.json @@ -41,6 +41,21 @@ "auth.register_failed": "Registration failed", "auth.invalid_response": "Invalid server response", "auth.oauth_not_ready": "{provider} login is not ready yet", + "auth.logging_in": "Logging in...", + "auth.back_to_login": "Back to Login", + "auth.bind_account": "Bind Account", + "auth.bind_new_desc": "Create a new account to bind with {provider}", + "auth.sign_up_bind": "Sign Up & Bind", + "auth.bind_existing": "Bind Existing", + "auth.bind_exist_desc": "Bind your {provider} to an existing account", + "auth.account": "Username/Email", + "auth.login_bind": "Login & Bind", + "auth.bind_login_tip": "To keep connected with us please login with your personal info", + "auth.bind_register_tip": "Enter your personal details and start journey with us", + "auth.bind_success": "Binding successful", + "auth.fill_required": "Please fill in all required fields", + "auth.username_required": "Username is required", + "common.processing": "Processing...", "common.actions": "Actions", "common.back": "Back", "common.cancel": "Cancel", @@ -199,7 +214,22 @@ "auth.register_success": "注册成功", "auth.register_failed": "注册失败", "auth.invalid_response": "服务器响应异常", - "auth.oauth_not_ready": "{provider} 登录暂未开放", + "auth.oauth_not_ready": "{provider} 登录尚未就绪", + "auth.logging_in": "登录中...", + "auth.back_to_login": "返回登录", + "auth.bind_account": "绑定账号", + "auth.bind_new_desc": "创建新账号并绑定 {provider}", + "auth.sign_up_bind": "注册并绑定", + "auth.bind_existing": "绑定已有账号", + "auth.bind_exist_desc": "将 {provider} 绑定到现有账号", + "auth.account": "用户名/邮箱", + "auth.login_bind": "登录并绑定", + "auth.bind_login_tip": "请登录您的个人账号以保持连接", + "auth.bind_register_tip": "输入您的个人信息,开启您的旅程", + "auth.bind_success": "绑定成功", + "auth.fill_required": "请填写所有必填项", + "auth.username_required": "用户名必填", + "common.processing": "处理中...", "common.actions": "操作", "common.back": "返回", "common.cancel": "取消", diff --git a/ui/layout/default.html b/ui/layout/default.html index bb45e0d..d1fac62 100644 --- a/ui/layout/default.html +++ b/ui/layout/default.html @@ -109,17 +109,9 @@
- + - - - -
- -
+
@@ -154,14 +146,6 @@ toggleCollapse = () => { collapsed = !collapsed; }; - - logout = () => { - $env.$vbase.logout(); - }; - - goToProfile = () => { - $router.push('/profile'); - }; diff --git a/ui/layout/ico.html b/ui/layout/ico.html new file mode 100644 index 0000000..b9e16c4 --- /dev/null +++ b/ui/layout/ico.html @@ -0,0 +1,186 @@ + + + + + + +
+ 用户头像 + {{ user.nickname || user.username }} + + +
+ +
+ +
+ + + + + + + diff --git a/ui/layout/public.html b/ui/layout/public.html deleted file mode 100644 index fa2ab99..0000000 --- a/ui/layout/public.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - VBase - - - - -
- - -
- - - diff --git a/ui/page/auth/callback.html b/ui/page/auth/callback.html new file mode 100644 index 0000000..bd2305a --- /dev/null +++ b/ui/page/auth/callback.html @@ -0,0 +1,568 @@ + + + + + + + {{ $t('auth.bind_account') || 'Bind Account' }} + + + + +
+ +
+ + +
+
+

{{ $t('common.processing') || 'Processing...' }}

+
+ + +
+

Error

+

{{ error }}

+ + {{ $t('auth.back_to_login') || 'Back to Login' }} + +
+ + +
+ + + + + + + + +
+
+
+

{{ $t('auth.welcome_back') || 'Welcome Back!' }}

+

{{ $t('auth.bind_login_tip') || 'To keep connected with us please login with your personal info' }}

+ +
+
+

{{ $t('auth.hello_friend') || 'Hello, Friend!' }}

+

{{ $t('auth.bind_register_tip') || 'Enter your personal details and start journey with us' }}

+ +
+
+
+
+ + + + + + + diff --git a/ui/page/auth/login.html b/ui/page/auth/login.html index ee8c54f..f0c5a16 100644 --- a/ui/page/auth/login.html +++ b/ui/page/auth/login.html @@ -7,8 +7,8 @@ {{ isSignUp ? $t('auth.register') : $t('auth.login') }}