updata:移动端添加语言切换功能

This commit is contained in:
rainv123
2025-09-26 14:33:06 +08:00
parent d022dd385a
commit dfcac51312
23 changed files with 2248 additions and 589 deletions
+149 -38
View File
@@ -3,7 +3,7 @@
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "登陆"
"navigationBarTitleText": "Login"
}
}
</route>
@@ -15,6 +15,9 @@ import { login } from '@/api/auth'
import { useConfigStore } from '@/store'
import { getEnvBaseUrl } from '@/utils'
import { toast } from '@/utils/toast'
// 导入国际化相关功能
import { t, changeLanguage, getSupportedLanguages, initI18n } from '@/i18n'
import type { Language } from '@/store/lang'
// 获取屏幕边界到安全区域距离
let safeAreaInsets
@@ -136,28 +139,28 @@ async function handleLogin() {
// 表单验证
if (loginType.value === 'username') {
if (!formData.value.username) {
toast.warning('请输入用户名')
toast.warning(t('login.enterUsername'))
return
}
}
else {
if (!formData.value.mobile) {
toast.warning('请输入手机号')
toast.warning(t('login.enterPhone'))
return
}
// 手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(formData.value.mobile)) {
toast.warning('请输入正确的手机号')
toast.warning(t('login.enterPhone'))
return
}
}
if (!formData.value.password) {
toast.warning('请输入密码')
toast.warning(t('login.enterPassword'))
return
}
if (!formData.value.captcha) {
toast.warning('请输入验证码')
toast.warning(t('login.enterCaptcha'))
return
}
@@ -177,7 +180,7 @@ async function handleLogin() {
uni.setStorageSync('token', response.token)
uni.setStorageSync('expire', response.expire)
toast.success('登录成功')
toast.success(t('message.loginSuccess'))
// 跳转到主页
setTimeout(() => {
@@ -189,6 +192,10 @@ async function handleLogin() {
catch (error: any) {
// 登录失败重新获取验证码
refreshCaptcha()
// 处理验证码错误 - 从error.message中解析错误码
if (error.message.includes('请求错误[10067]')) {
toast.warning(t('login.captchaError'))
}
}
finally {
loading.value = false
@@ -200,14 +207,26 @@ onLoad(() => {
refreshCaptcha()
})
// 语言切换相关
const showLanguageSheet = ref(false)
const supportedLanguages = getSupportedLanguages()
// 初始化国际化
initI18n()
// 切换语言
function handleLanguageChange(lang: Language) {
changeLanguage(lang)
showLanguageSheet.value = false
}
// 组件挂载时确保配置已加载
onMounted(async () => {
if (!configStore.config.name) {
try {
await configStore.fetchPublicConfig()
}
catch (error) {
console.error('获取配置失败:', error)
} catch (error) {
console.error(t('login.fetchConfigError'), error)
}
}
})
@@ -219,21 +238,25 @@ onMounted(async () => {
<view class="logo-section">
<wd-img :width="80" :height="80" round src="/static/logo.png" class="logo" />
<text class="welcome-text">
欢迎回来
{{ t('login.welcomeBack') }}
</text>
<text class="subtitle">
请登录您的账户
{{ t('login.pleaseLogin') }}
</text>
</view>
</view>
<!-- 右上角服务端设置按钮 -->
<view
class="server-btn"
:style="{ top: `${safeAreaInsets?.top + 10}px` }"
@click="goToServerSetting"
>
<wd-icon name="setting" custom-class="server-icon" />
<!-- 右上角按钮 -->
<view class="top-right-buttons" :style="{ top: `${safeAreaInsets?.top + 10}px` }">
<!-- 语言切换按钮 -->
<view class="lang-btn" @click="showLanguageSheet = true">
<text class="lang-text-icon">🌐</text>
</view>
<!-- 服务端设置按钮 -->
<view class="server-btn" @click="goToServerSetting">
<wd-icon name="setting" custom-class="server-icon" />
</view>
</view>
<view class="form-container">
@@ -253,7 +276,7 @@ onMounted(async () => {
v-model="formData.mobile"
custom-class="styled-input"
no-border
placeholder="请输入手机号码"
:placeholder="t('login.enterPhone')"
type="number"
:maxlength="11"
/>
@@ -270,7 +293,7 @@ onMounted(async () => {
v-model="formData.username"
custom-class="styled-input"
no-border
placeholder="请输入用户名"
:placeholder="t('login.enterUsername')"
/>
</view>
</view>
@@ -282,7 +305,7 @@ onMounted(async () => {
v-model="formData.password"
custom-class="styled-input"
no-border
placeholder="请输入密码"
:placeholder="t('login.enterPassword')"
clearable
show-password
:maxlength="20"
@@ -296,7 +319,7 @@ onMounted(async () => {
v-model="formData.captcha"
custom-class="styled-input"
no-border
placeholder="请输入验证码"
:placeholder="t('login.enterCaptcha')"
:maxlength="6"
/>
<view class="captcha-image" @click="refreshCaptcha">
@@ -307,7 +330,7 @@ onMounted(async () => {
<view class="forgot-password">
<text class="forgot-text">
忘记密码
{{ t('login.forgotPassword') }}
</text>
</view>
@@ -315,15 +338,15 @@ onMounted(async () => {
class="login-btn"
@click="handleLogin"
>
{{ loading ? '登录中...' : '登录' }}
{{ loading ? t('login.loggingIn') : t('login.loginButton') }}
</view>
<view class="register-hint">
<text class="hint-text">
还没有账户
{{ t('login.noAccount') }}
</text>
<text class="register-link" @click="goToRegister">
立即注册
{{ t('login.registerNow') }}
</text>
</view>
@@ -352,7 +375,7 @@ onMounted(async () => {
<!-- 区号选择弹窗 -->
<wd-action-sheet
v-model="showAreaCodeSheet"
title="选择国家/地区"
:title="t('login.selectCountry')"
:close-on-click-modal="true"
@close="closeAreaCodeSheet"
>
@@ -386,11 +409,33 @@ onMounted(async () => {
custom-class="confirm-btn"
@click="closeAreaCodeSheet"
>
确认
{{ t('login.confirm') }}
</wd-button>
</view>
</view>
</wd-action-sheet>
<!-- 语言选择弹窗 -->
<wd-action-sheet
v-model="showLanguageSheet"
:title="t('login.selectLanguage')"
:close-on-click-modal="true"
>
<view class="language-sheet">
<scroll-view scroll-y class="language-list">
<view
v-for="lang in supportedLanguages"
:key="lang.code"
class="language-item"
@click="handleLanguageChange(lang.code)"
>
<text class="language-name">
{{ lang.name }}
</text>
</view>
</scroll-view>
</view>
</wd-action-sheet>
</view>
</template>
@@ -793,20 +838,52 @@ onMounted(async () => {
}
}
}
.server-btn {
// 右上角按钮组
.top-right-buttons {
position: absolute;
right: 20rpx; // 距离右边距
top: 40rpx; // 顶部稍微下移,不贴状态栏
right: 20rpx;
display: flex;
gap: 20rpx;
z-index: 999;
}
// 语言切换按钮
.lang-btn {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
cursor: pointer;
background: rgba(255, 255, 255, 0.15); // 半透明背景,更好看
border-radius: 24rpx; // 圆形按钮
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.2); // 阴影
background: rgba(255, 255, 255, 0.15);
border-radius: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.2);
&:active {
transform: scale(0.95);
}
.lang-text-icon {
font-size: 28rpx;
color: #FFFFFF;
}
&:hover {
background: rgba(255, 255, 255, 0.25);
}
}
// 服务端设置按钮
.server-btn {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
background: rgba(255, 255, 255, 0.15);
border-radius: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.2);
&:active {
transform: scale(0.95);
@@ -814,11 +891,45 @@ onMounted(async () => {
.server-icon {
font-size: 28rpx;
color: #FFFFFF; // 白色图标
color: #FFFFFF;
}
&:hover {
background: rgba(255, 255, 255, 0.25); // 悬停效果
background: rgba(255, 255, 255, 0.25);
}
}
// 语言选择弹窗样式
.language-sheet {
background: #ffffff;
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
.language-list {
max-height: 60vh;
padding: 0 40rpx;
.language-item {
display: flex;
align-items: center;
padding: 32rpx 0;
border-bottom: 1rpx solid #f8f9fa;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background-color: #f8f9fa;
}
&:last-child {
border-bottom: none;
}
.language-name {
font-size: 32rpx;
color: #333333;
}
}
}
}
</style>