fix(player): login footer layout, lang switcher z-index, profile.settings i18n; dev Vite proxy reads API PORT

This commit is contained in:
2026-06-23 14:41:15 +08:00
parent 9cef985974
commit 0c5541fc57
9 changed files with 108 additions and 52 deletions

View File

@@ -1,6 +1,7 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import { resolve } from 'path'; import { resolve } from 'path';
import { resolveApiDevTarget } from '../../scripts/dev-api-target.mjs';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import AutoImport from 'unplugin-auto-import/vite'; import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite'; import Components from 'unplugin-vue-components/vite';
@@ -8,6 +9,7 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
const analyze = process.env.ANALYZE === '1' || mode === 'analyze'; const analyze = process.env.ANALYZE === '1' || mode === 'analyze';
const apiTarget = resolveApiDevTarget();
return { return {
plugins: [ plugins: [
@@ -75,8 +77,8 @@ export default defineConfig(({ mode }) => {
server: { server: {
port: 5174, port: 5174,
proxy: { proxy: {
'/api': { target: 'http://localhost:3000', changeOrigin: true }, '/api': { target: apiTarget, changeOrigin: true },
'/uploads': { target: 'http://localhost:3000', changeOrigin: true }, '/uploads': { target: apiTarget, changeOrigin: true },
}, },
}, },
}; };

View File

@@ -476,6 +476,7 @@ export default {
place_failed: 'Bet failed', place_failed: 'Bet failed',
}, },
profile: { profile: {
settings: 'Settings',
edit: 'Edit Profile', edit: 'Edit Profile',
language: 'Language', language: 'Language',
avatar: 'Avatar', avatar: 'Avatar',

View File

@@ -482,6 +482,7 @@ export default {
place_failed: 'Pertaruhan gagal', place_failed: 'Pertaruhan gagal',
}, },
profile: { profile: {
settings: 'Tetapan',
edit: 'Edit Profil', edit: 'Edit Profil',
language: 'Bahasa', language: 'Bahasa',
avatar: 'Avatar', avatar: 'Avatar',

View File

@@ -476,6 +476,7 @@ export default {
place_failed: '下注失败', place_failed: '下注失败',
}, },
profile: { profile: {
settings: '设置',
edit: '修改资料', edit: '修改资料',
language: '语言', language: '语言',
avatar: '选择头像', avatar: '选择头像',

View File

@@ -102,6 +102,7 @@ function goLogin() {
<div class="login-lang"> <div class="login-lang">
<LocaleSwitcher compact /> <LocaleSwitcher compact />
</div> </div>
<div class="login-stack">
<form @submit.prevent="submit" class="login-form ps-gold-frame"> <form @submit.prevent="submit" class="login-form ps-gold-frame">
<h2 class="form-title">{{ t('auth.forgot_password_title') }}</h2> <h2 class="form-title">{{ t('auth.forgot_password_title') }}</h2>
@@ -177,6 +178,7 @@ function goLogin() {
</button> </button>
</form> </form>
</div> </div>
</div>
</template> </template>
<style scoped> <style scoped>
@@ -184,7 +186,7 @@ function goLogin() {
position: absolute; position: absolute;
top: max(12px, env(safe-area-inset-top)); top: max(12px, env(safe-area-inset-top));
right: 16px; right: 16px;
z-index: 2; z-index: 10;
} }
.login-page { .login-page {
@@ -217,14 +219,15 @@ function goLogin() {
z-index: 0; z-index: 0;
} }
.login-page > * { .login-stack {
position: relative; position: relative;
z-index: 1; z-index: 1;
width: 100%;
max-width: 320px;
} }
.login-form { .login-form {
width: 100%; width: 100%;
max-width: 320px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;

View File

@@ -89,6 +89,7 @@ function goRegister() {
<div class="login-lang"> <div class="login-lang">
<LocaleSwitcher compact /> <LocaleSwitcher compact />
</div> </div>
<div class="login-stack">
<form @submit.prevent="submit" class="login-form ps-gold-frame"> <form @submit.prevent="submit" class="login-form ps-gold-frame">
<div class="auth-tabs"> <div class="auth-tabs">
<button <button
@@ -151,14 +152,17 @@ function goRegister() {
<button type="submit" class="btn-login btn-gold-outline" :disabled="loading"> <button type="submit" class="btn-login btn-gold-outline" :disabled="loading">
{{ t('auth.login') }} {{ t('auth.login') }}
</button> </button>
</form>
<div class="login-actions">
<button type="button" class="btn-skip" @click="goRegister"> <button type="button" class="btn-skip" @click="goRegister">
{{ t('auth.go_register') }} {{ t('auth.go_register') }}
</button> </button>
</form>
<button type="button" class="btn-skip-light" @click="continueBrowsing"> <button type="button" class="btn-skip-light" @click="continueBrowsing">
{{ t('auth.continue_browsing') }} {{ t('auth.continue_browsing') }}
</button> </button>
</div> </div>
</div>
</div>
</template> </template>
<style scoped> <style scoped>
@@ -166,7 +170,7 @@ function goRegister() {
position: absolute; position: absolute;
top: max(12px, env(safe-area-inset-top)); top: max(12px, env(safe-area-inset-top));
right: 16px; right: 16px;
z-index: 2; z-index: 10;
} }
.login-page { .login-page {
@@ -178,7 +182,7 @@ function goRegister() {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: flex-end; 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-color: var(--bg-body);
background-size: cover; background-size: cover;
background-position: center top; background-position: center top;
@@ -199,20 +203,32 @@ function goRegister() {
z-index: 0; z-index: 0;
} }
.login-page > * { .login-stack {
position: relative; position: relative;
z-index: 1; z-index: 1;
}
.login-form {
width: 100%; width: 100%;
max-width: 320px; max-width: 320px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
}
.login-form {
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
padding: 14px; padding: 14px;
} }
.login-actions {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: 0 14px 4px;
}
.auth-tabs { .auth-tabs {
display: flex; display: flex;
gap: 6px; gap: 6px;
@@ -315,14 +331,14 @@ label {
} }
.btn-skip { .btn-skip {
margin-top: 2px; padding: 6px 14px;
padding: 8px 14px;
border: none; border: none;
background: transparent; background: transparent;
color: rgba(255, 255, 255, 0.55); color: rgba(255, 255, 255, 0.55);
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
text-align: center;
} }
.btn-skip:active { .btn-skip:active {
@@ -330,11 +346,7 @@ label {
} }
.btn-skip-light { .btn-skip-light {
position: absolute; padding: 6px 16px;
bottom: calc(20px + env(safe-area-inset-bottom));
left: 50%;
transform: translateX(-50%);
padding: 8px 16px;
border: none; border: none;
background: transparent; background: transparent;
color: rgba(255, 255, 255, 0.45); color: rgba(255, 255, 255, 0.45);
@@ -342,6 +354,7 @@ label {
font-weight: 300; font-weight: 300;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
text-align: center;
} }
.btn-skip-light:active { .btn-skip-light:active {

View File

@@ -105,6 +105,7 @@ const fieldError = () => {
<div class="login-lang"> <div class="login-lang">
<LocaleSwitcher compact /> <LocaleSwitcher compact />
</div> </div>
<div class="login-stack">
<form @submit.prevent="submit" class="login-form ps-gold-frame" :class="{ 'is-busy': loading }"> <form @submit.prevent="submit" class="login-form ps-gold-frame" :class="{ 'is-busy': loading }">
<div v-if="loading" class="form-busy-overlay" aria-hidden="true"> <div v-if="loading" class="form-busy-overlay" aria-hidden="true">
<GoldSpinner :size="40" :active="true" /> <GoldSpinner :size="40" :active="true" />
@@ -199,14 +200,17 @@ const fieldError = () => {
</span> </span>
<span v-else>{{ t('auth.register_btn') }}</span> <span v-else>{{ t('auth.register_btn') }}</span>
</button> </button>
</form>
<div class="login-actions">
<button type="button" class="btn-skip" :disabled="loading" @click="goLogin"> <button type="button" class="btn-skip" :disabled="loading" @click="goLogin">
{{ t('auth.have_account') }} {{ t('auth.have_account') }}
</button> </button>
</form> <button type="button" class="btn-skip-light" :disabled="loading" @click="continueBrowsing">
<button type="button" class="btn-skip-light" @click="continueBrowsing">
{{ t('auth.continue_browsing') }} {{ t('auth.continue_browsing') }}
</button> </button>
</div> </div>
</div>
</div>
</template> </template>
<style scoped> <style scoped>
@@ -214,7 +218,7 @@ const fieldError = () => {
position: absolute; position: absolute;
top: max(12px, env(safe-area-inset-top)); top: max(12px, env(safe-area-inset-top));
right: 16px; right: 16px;
z-index: 2; z-index: 10;
} }
.login-page { .login-page {
@@ -226,7 +230,7 @@ const fieldError = () => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: flex-end; 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-color: var(--bg-body);
background-size: cover; background-size: cover;
background-position: center top; background-position: center top;
@@ -247,21 +251,33 @@ const fieldError = () => {
z-index: 0; z-index: 0;
} }
.login-page > * { .login-stack {
position: relative; position: relative;
z-index: 1; z-index: 1;
}
.login-form {
position: relative;
width: 100%; width: 100%;
max-width: 320px; max-width: 320px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
}
.login-form {
position: relative;
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
padding: 14px; padding: 14px;
} }
.login-actions {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: 0 14px 4px;
}
.login-form.is-busy { .login-form.is-busy {
pointer-events: none; pointer-events: none;
} }
@@ -403,14 +419,14 @@ label {
} }
.btn-skip { .btn-skip {
margin-top: 2px; padding: 6px 14px;
padding: 8px 14px;
border: none; border: none;
background: transparent; background: transparent;
color: rgba(255, 255, 255, 0.55); color: rgba(255, 255, 255, 0.55);
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
text-align: center;
} }
.btn-skip:active { .btn-skip:active {
@@ -418,11 +434,7 @@ label {
} }
.btn-skip-light { .btn-skip-light {
position: absolute; padding: 6px 16px;
bottom: calc(20px + env(safe-area-inset-bottom));
left: 50%;
transform: translateX(-50%);
padding: 8px 16px;
border: none; border: none;
background: transparent; background: transparent;
color: rgba(255, 255, 255, 0.45); color: rgba(255, 255, 255, 0.45);
@@ -430,6 +442,7 @@ label {
font-weight: 300; font-weight: 300;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
text-align: center;
} }
.btn-skip-light:active { .btn-skip-light:active {

View File

@@ -1,6 +1,9 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import { resolve } from 'path'; import { resolve } from 'path';
import { resolveApiDevTarget } from '../../scripts/dev-api-target.mjs';
const apiTarget = resolveApiDevTarget();
export default defineConfig({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
@@ -37,8 +40,8 @@ export default defineConfig({
server: { server: {
port: 5173, port: 5173,
proxy: { proxy: {
'/api': { target: 'http://localhost:3000', changeOrigin: true }, '/api': { target: apiTarget, changeOrigin: true },
'/uploads': { target: 'http://localhost:3000', changeOrigin: true }, '/uploads': { target: apiTarget, changeOrigin: true },
}, },
}, },
}); });

View File

@@ -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}`;
}