diff --git a/apps/admin/vite.config.ts b/apps/admin/vite.config.ts index 5520ea0..3118525 100644 --- a/apps/admin/vite.config.ts +++ b/apps/admin/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { resolve } from 'path'; +import { resolveApiDevTarget } from '../../scripts/dev-api-target.mjs'; import { visualizer } from 'rollup-plugin-visualizer'; import AutoImport from 'unplugin-auto-import/vite'; import Components from 'unplugin-vue-components/vite'; @@ -8,6 +9,7 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; export default defineConfig(({ mode }) => { const analyze = process.env.ANALYZE === '1' || mode === 'analyze'; + const apiTarget = resolveApiDevTarget(); return { plugins: [ @@ -75,8 +77,8 @@ export default defineConfig(({ mode }) => { server: { port: 5174, proxy: { - '/api': { target: 'http://localhost:3000', changeOrigin: true }, - '/uploads': { target: 'http://localhost:3000', changeOrigin: true }, + '/api': { target: apiTarget, changeOrigin: true }, + '/uploads': { target: apiTarget, changeOrigin: true }, }, }, }; diff --git a/apps/player/src/i18n/en-US.ts b/apps/player/src/i18n/en-US.ts index 5dd5d6e..3c6602b 100644 --- a/apps/player/src/i18n/en-US.ts +++ b/apps/player/src/i18n/en-US.ts @@ -476,6 +476,7 @@ export default { place_failed: 'Bet failed', }, profile: { + settings: 'Settings', edit: 'Edit Profile', language: 'Language', avatar: 'Avatar', diff --git a/apps/player/src/i18n/ms-MY.ts b/apps/player/src/i18n/ms-MY.ts index b57d83e..38bf540 100644 --- a/apps/player/src/i18n/ms-MY.ts +++ b/apps/player/src/i18n/ms-MY.ts @@ -482,6 +482,7 @@ export default { place_failed: 'Pertaruhan gagal', }, profile: { + settings: 'Tetapan', edit: 'Edit Profil', language: 'Bahasa', avatar: 'Avatar', diff --git a/apps/player/src/i18n/zh-CN.ts b/apps/player/src/i18n/zh-CN.ts index 9b9f51d..b8a0752 100644 --- a/apps/player/src/i18n/zh-CN.ts +++ b/apps/player/src/i18n/zh-CN.ts @@ -476,6 +476,7 @@ export default { place_failed: '下注失败', }, profile: { + settings: '设置', edit: '修改资料', language: '语言', avatar: '选择头像', diff --git a/apps/player/src/views/ForgotPasswordView.vue b/apps/player/src/views/ForgotPasswordView.vue index 8d335b7..57c1350 100644 --- a/apps/player/src/views/ForgotPasswordView.vue +++ b/apps/player/src/views/ForgotPasswordView.vue @@ -102,7 +102,8 @@ function goLogin() {
-
+
+

{{ t('auth.forgot_password_title') }}

@@ -175,7 +176,8 @@ function goLogin() { - + +
@@ -184,7 +186,7 @@ function goLogin() { position: absolute; top: max(12px, env(safe-area-inset-top)); right: 16px; - z-index: 2; + z-index: 10; } .login-page { @@ -217,14 +219,15 @@ function goLogin() { z-index: 0; } -.login-page > * { +.login-stack { position: relative; z-index: 1; + width: 100%; + max-width: 320px; } .login-form { width: 100%; - max-width: 320px; display: flex; flex-direction: column; gap: 8px; diff --git a/apps/player/src/views/LoginView.vue b/apps/player/src/views/LoginView.vue index 859bd1b..d08bc3f 100644 --- a/apps/player/src/views/LoginView.vue +++ b/apps/player/src/views/LoginView.vue @@ -89,7 +89,8 @@ function goRegister() {
-
+
+
- - - + + +
@@ -166,7 +170,7 @@ function goRegister() { position: absolute; top: max(12px, env(safe-area-inset-top)); right: 16px; - z-index: 2; + z-index: 10; } .login-page { @@ -178,7 +182,7 @@ function goRegister() { flex-direction: column; align-items: center; justify-content: flex-end; - padding: 34vh 20px calc(15vh + max(28px, env(safe-area-inset-bottom))); + padding: 34vh 20px max(20px, env(safe-area-inset-bottom)); background-color: var(--bg-body); background-size: cover; background-position: center top; @@ -199,20 +203,32 @@ function goRegister() { z-index: 0; } -.login-page > * { +.login-stack { position: relative; z-index: 1; -} - -.login-form { width: 100%; max-width: 320px; display: flex; flex-direction: column; gap: 8px; +} + +.login-form { + width: 100%; + display: flex; + flex-direction: column; + gap: 8px; padding: 14px; } +.login-actions { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding: 0 14px 4px; +} + .auth-tabs { display: flex; gap: 6px; @@ -315,14 +331,14 @@ label { } .btn-skip { - margin-top: 2px; - padding: 8px 14px; + padding: 6px 14px; border: none; background: transparent; color: rgba(255, 255, 255, 0.55); font-size: 13px; font-weight: 600; cursor: pointer; + text-align: center; } .btn-skip:active { @@ -330,11 +346,7 @@ label { } .btn-skip-light { - position: absolute; - bottom: calc(20px + env(safe-area-inset-bottom)); - left: 50%; - transform: translateX(-50%); - padding: 8px 16px; + padding: 6px 16px; border: none; background: transparent; color: rgba(255, 255, 255, 0.45); @@ -342,6 +354,7 @@ label { font-weight: 300; cursor: pointer; white-space: nowrap; + text-align: center; } .btn-skip-light:active { diff --git a/apps/player/src/views/RegisterView.vue b/apps/player/src/views/RegisterView.vue index df327ba..ad9606c 100644 --- a/apps/player/src/views/RegisterView.vue +++ b/apps/player/src/views/RegisterView.vue @@ -105,7 +105,8 @@ const fieldError = () => {
-
+
+
@@ -214,7 +218,7 @@ const fieldError = () => { position: absolute; top: max(12px, env(safe-area-inset-top)); right: 16px; - z-index: 2; + z-index: 10; } .login-page { @@ -226,7 +230,7 @@ const fieldError = () => { flex-direction: column; align-items: center; justify-content: flex-end; - padding: 30vh 16px calc(10vh + max(20px, env(safe-area-inset-bottom))); + padding: 30vh 16px max(20px, env(safe-area-inset-bottom)); background-color: var(--bg-body); background-size: cover; background-position: center top; @@ -247,21 +251,33 @@ const fieldError = () => { z-index: 0; } -.login-page > * { +.login-stack { position: relative; z-index: 1; -} - -.login-form { - position: relative; width: 100%; max-width: 320px; display: flex; flex-direction: column; gap: 8px; +} + +.login-form { + position: relative; + width: 100%; + display: flex; + flex-direction: column; + gap: 8px; padding: 14px; } +.login-actions { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding: 0 14px 4px; +} + .login-form.is-busy { pointer-events: none; } @@ -403,14 +419,14 @@ label { } .btn-skip { - margin-top: 2px; - padding: 8px 14px; + padding: 6px 14px; border: none; background: transparent; color: rgba(255, 255, 255, 0.55); font-size: 13px; font-weight: 600; cursor: pointer; + text-align: center; } .btn-skip:active { @@ -418,11 +434,7 @@ label { } .btn-skip-light { - position: absolute; - bottom: calc(20px + env(safe-area-inset-bottom)); - left: 50%; - transform: translateX(-50%); - padding: 8px 16px; + padding: 6px 16px; border: none; background: transparent; color: rgba(255, 255, 255, 0.45); @@ -430,6 +442,7 @@ label { font-weight: 300; cursor: pointer; white-space: nowrap; + text-align: center; } .btn-skip-light:active { diff --git a/apps/player/vite.config.ts b/apps/player/vite.config.ts index bf00cfa..1704b59 100644 --- a/apps/player/vite.config.ts +++ b/apps/player/vite.config.ts @@ -1,6 +1,9 @@ import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { resolve } from 'path'; +import { resolveApiDevTarget } from '../../scripts/dev-api-target.mjs'; + +const apiTarget = resolveApiDevTarget(); export default defineConfig({ plugins: [vue()], @@ -37,8 +40,8 @@ export default defineConfig({ server: { port: 5173, proxy: { - '/api': { target: 'http://localhost:3000', changeOrigin: true }, - '/uploads': { target: 'http://localhost:3000', changeOrigin: true }, + '/api': { target: apiTarget, changeOrigin: true }, + '/uploads': { target: apiTarget, changeOrigin: true }, }, }, }); diff --git a/scripts/dev-api-target.mjs b/scripts/dev-api-target.mjs new file mode 100644 index 0000000..e914b0c --- /dev/null +++ b/scripts/dev-api-target.mjs @@ -0,0 +1,19 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); + +/** 本地 dev:与 apps/api/.env 的 PORT 对齐,供 player/admin Vite 代理使用 */ +export function resolveApiDevTarget() { + if (process.env.VITE_DEV_API_TARGET) return process.env.VITE_DEV_API_TARGET; + if (process.env.API_DEV_TARGET) return process.env.API_DEV_TARGET; + + let port = '3000'; + const envPath = resolve(repoRoot, 'apps/api/.env'); + if (existsSync(envPath)) { + const match = readFileSync(envPath, 'utf8').match(/^PORT=(\d+)\s*$/m); + if (match) port = match[1]; + } + return `http://localhost:${port}`; +}