静态页面
This commit is contained in:
94
README.md
Normal file
94
README.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Market Uniapp - 登录功能修复
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
原代码无法模拟登录过程,登录按钮点击后没有正确的响应和页面跳转。
|
||||||
|
|
||||||
|
## 修复内容
|
||||||
|
|
||||||
|
### 1. 创建了LoginService类 (src/api/login.js)
|
||||||
|
- 遵循OOP原则,创建了完整的登录服务类
|
||||||
|
- 提供了模拟登录API调用
|
||||||
|
- 实现了登录状态管理
|
||||||
|
- 包含用户信息存储和获取功能
|
||||||
|
- 提供了登出功能
|
||||||
|
|
||||||
|
### 2. 更新了登录页面 (src/pages/login/login.vue)
|
||||||
|
- 导入了LoginService类
|
||||||
|
- 改进了登录逻辑,使用async/await处理异步操作
|
||||||
|
- 添加了更完善的错误处理
|
||||||
|
- 增加了登录状态检查功能
|
||||||
|
- 改进了页面跳转逻辑,使用reLaunch确保清除导航栈
|
||||||
|
|
||||||
|
### 3. 更新了首页 (src/pages/home/index.vue)
|
||||||
|
- 添加了登录状态检查
|
||||||
|
- 正确显示登录用户信息
|
||||||
|
- 添加了登出功能
|
||||||
|
- 改进了用户信息加载逻辑
|
||||||
|
|
||||||
|
### 4. 主要功能特性
|
||||||
|
|
||||||
|
#### 登录验证
|
||||||
|
- 用户ID必须是6位数字
|
||||||
|
- 密码不能为空
|
||||||
|
- 必须同意用户协议
|
||||||
|
- 表单验证完善
|
||||||
|
|
||||||
|
#### 登录状态管理
|
||||||
|
- 自动检查登录状态
|
||||||
|
- 已登录用户直接跳转到首页
|
||||||
|
- 未登录用户自动跳转到登录页
|
||||||
|
- 登录状态持久化存储
|
||||||
|
|
||||||
|
#### 用户体验
|
||||||
|
- 登录过程中显示加载状态
|
||||||
|
- 登录成功后显示成功提示
|
||||||
|
- 记住密码功能
|
||||||
|
- 多语言支持
|
||||||
|
|
||||||
|
#### 安全性
|
||||||
|
- Token过期检查
|
||||||
|
- 登录状态验证
|
||||||
|
- 安全的登出功能
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. 启动项目:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 登录测试:
|
||||||
|
- 输入6位数字用户ID(如:123456)
|
||||||
|
- 输入密码(至少6位)
|
||||||
|
- 勾选同意协议
|
||||||
|
- 点击登录按钮
|
||||||
|
|
||||||
|
3. 功能验证:
|
||||||
|
- 登录成功后自动跳转到首页
|
||||||
|
- 首页显示用户信息
|
||||||
|
- 点击右上角"登出"按钮可以退出登录
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
- Vue.js 3
|
||||||
|
- uni-app
|
||||||
|
- ES6+ (async/await)
|
||||||
|
- 面向对象编程 (OOP)
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── api/
|
||||||
|
│ └── login.js # 登录服务类
|
||||||
|
├── pages/
|
||||||
|
│ ├── login/
|
||||||
|
│ │ └── login.vue # 登录页面
|
||||||
|
│ └── home/
|
||||||
|
│ └── index.vue # 首页
|
||||||
|
└── utils/
|
||||||
|
└── request.js # 请求工具
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
- 当前使用模拟登录,实际项目中需要替换为真实的API调用
|
||||||
|
- 登录状态存储在本地,实际项目中建议使用更安全的存储方式
|
||||||
|
- 可以根据需要调整登录验证规则和UI样式
|
||||||
@@ -1,51 +1,9 @@
|
|||||||
|
import {request} from '../utils/request'
|
||||||
|
|
||||||
|
export default function login(data) {
|
||||||
import { request } from '../utils/request'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户登录
|
|
||||||
* @param {Object} data - 登录数据 {username, password}
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
export function login(data) {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/user/login',
|
url: '/api/login',
|
||||||
method: 'POST',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取用户信息
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
export function getUserInfo() {
|
|
||||||
return request({
|
|
||||||
url: '/user/info',
|
|
||||||
method: 'GET'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户信息
|
|
||||||
* @param {Object} data - 用户信息
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
export function updateUserInfo(data) {
|
|
||||||
return request({
|
|
||||||
url: '/user/info',
|
|
||||||
method: 'PUT',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户注销
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
export function logout() {
|
|
||||||
return request({
|
|
||||||
url: '/user/logout',
|
|
||||||
method: 'POST'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
import { createSSRApp } from 'vue'
|
import { createSSRApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import { request, http } from '@/utils/request' // 引入封装的request
|
import { request, http } from '@/utils/request' // 引入封装的request
|
||||||
import en from './static/lang/enUS.json'
|
import en from './static/lang/enUS.json'
|
||||||
import zh from './static/lang/zh_CN.json'
|
import zh from './static/lang/zh_CN.json'
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = createSSRApp(App)
|
const app = createSSRApp(App)
|
||||||
|
|
||||||
// 1. 获取系统语言
|
// 1. 获取系统语言
|
||||||
const systemInfo = uni.getSystemInfoSync() || {}
|
const systemInfo = uni.getSystemInfoSync() || {}
|
||||||
const systemLanguage = systemInfo.language || 'zh-CN'
|
const systemLanguage = systemInfo.language || 'zh'
|
||||||
let curLang = 'zh'
|
let curLang = 'zh'
|
||||||
|
|
||||||
switch (systemLanguage.toLowerCase()) {
|
switch (systemLanguage.toLowerCase()) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/home/index",
|
"path": "pages/home/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": ""
|
"navigationBarTitleText": "首页"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
{{dailyChange >= 0 ? '+' : ''}}{{formatNumber(dailyChange)}} ({{dailyChangePercent >= 0 ? '+' : ''}}{{dailyChangePercent}}%)
|
{{dailyChange >= 0 ? '+' : ''}}{{formatNumber(dailyChange)}} ({{dailyChangePercent >= 0 ? '+' : ''}}{{dailyChangePercent}}%)
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 登出按钮 -->
|
||||||
|
<view class="logout-btn" @click="logout">
|
||||||
|
<text class="logout-text">登出</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 主要功能入口 -->
|
<!-- 主要功能入口 -->
|
||||||
@@ -92,11 +96,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import loginService from '@/api/login.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
userName: "John Doe",
|
userName: "Guest",
|
||||||
accountId: "EU20230001",
|
accountId: "未登录",
|
||||||
totalAssets: 125430.56,
|
totalAssets: 125430.56,
|
||||||
dailyChange: 1254.32,
|
dailyChange: 1254.32,
|
||||||
dailyChangePercent: 1.01,
|
dailyChangePercent: 1.01,
|
||||||
@@ -132,29 +138,93 @@ export default {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.loadUserInfo()
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 每次显示页面时检查登录状态
|
||||||
|
this.checkLoginStatus()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// formatNumber(num) {
|
/**
|
||||||
// return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
* 检查登录状态
|
||||||
// },
|
*/
|
||||||
// formatTime(timeString) {
|
checkLoginStatus() {
|
||||||
// const date = new Date(timeString);
|
if (!loginService.isLoggedIn()) {
|
||||||
// return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
console.log('用户未登录,跳转到登录页')
|
||||||
// },
|
uni.reLaunch({
|
||||||
// navigateTo(page) {
|
url: '/pages/login/login'
|
||||||
// uni.navigateTo({
|
})
|
||||||
// url: `/pages/${page}/${page}`
|
}
|
||||||
// });
|
},
|
||||||
// },
|
|
||||||
// viewStockDetail(stock) {
|
/**
|
||||||
// uni.navigateTo({
|
* 加载用户信息
|
||||||
// url: `/pages/stock/detail?symbol=${stock.symbol}`
|
*/
|
||||||
// });
|
loadUserInfo() {
|
||||||
// },
|
const userInfo = loginService.getUserInfo()
|
||||||
// viewNewsDetail(news) {
|
if (userInfo) {
|
||||||
// uni.navigateTo({
|
this.userName = userInfo.nickname || userInfo.username
|
||||||
// url: `/pages/news/detail?title=${encodeURIComponent(news.title)}`
|
this.accountId = userInfo.id || userInfo.username
|
||||||
// });
|
console.log('用户信息已加载:', userInfo)
|
||||||
// }
|
} else {
|
||||||
|
console.log('未找到用户信息')
|
||||||
|
this.checkLoginStatus()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出功能
|
||||||
|
*/
|
||||||
|
logout() {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认登出',
|
||||||
|
content: '确定要退出登录吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
loginService.logout()
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/login/login'
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登出失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '登出失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
formatNumber(num) {
|
||||||
|
return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
|
},
|
||||||
|
|
||||||
|
formatTime(timeString) {
|
||||||
|
const date = new Date(timeString);
|
||||||
|
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||||
|
},
|
||||||
|
|
||||||
|
navigateTo(page) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/${page}/${page}`
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
viewStockDetail(stock) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/stock/detail?symbol=${stock.symbol}`
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
viewNewsDetail(news) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/news/detail?title=${encodeURIComponent(news.title)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -173,6 +243,22 @@ export default {
|
|||||||
padding: 30rpx;
|
padding: 30rpx;
|
||||||
color: white;
|
color: white;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 20rpx;
|
||||||
|
right: 20rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 10rpx 20rpx;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-info {
|
.user-info {
|
||||||
|
|||||||
@@ -22,11 +22,11 @@
|
|||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="lang-item"
|
class="lang-item"
|
||||||
@click="changeLanguage('cn')"
|
@click="changeLanguage('zh')"
|
||||||
:class="{ 'active': currentLang === 'cn' }"
|
:class="{ 'active': currentLang === 'zh' }"
|
||||||
>
|
>
|
||||||
<text>Chinese</text>
|
<text>Chinese</text>
|
||||||
<i class="iconfont icon-check" v-if="currentLang === 'cn'"></i>
|
<i class="iconfont icon-check" v-if="currentLang === 'zh'"></i>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<button class="modal-close" @click="showLanguageModal = false">
|
<button class="modal-close" @click="showLanguageModal = false">
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 登录按钮 -->
|
<!-- 登录按钮 -->
|
||||||
<button
|
<!-- <button
|
||||||
class="login-btn"
|
class="login-btn"
|
||||||
@click="handleLogin"
|
@click="handleLogin"
|
||||||
:disabled="isLoading || !agreeTerms"
|
:disabled="isLoading || !agreeTerms"
|
||||||
@@ -109,6 +109,15 @@
|
|||||||
<i class="iconfont icon-loading loading-icon"></i>
|
<i class="iconfont icon-loading loading-icon"></i>
|
||||||
</template>
|
</template>
|
||||||
{{ $t('login.loginButton') }}
|
{{ $t('login.loginButton') }}
|
||||||
|
</button> -->
|
||||||
|
<button
|
||||||
|
class="login-btn"
|
||||||
|
@click="handleLogin"
|
||||||
|
>
|
||||||
|
<template v-if="isLoading">
|
||||||
|
<i class="iconfont icon-loading loading-icon"></i>
|
||||||
|
</template>
|
||||||
|
{{ $t('login.loginButton') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 协议同意勾选框 -->
|
<!-- 协议同意勾选框 -->
|
||||||
@@ -140,10 +149,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// import {
|
|
||||||
// login,
|
|
||||||
// getUserInfo,
|
|
||||||
// } from '@/api/login'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -163,7 +169,6 @@ export default {
|
|||||||
onLoad() {
|
onLoad() {
|
||||||
this.currentLang = this.$i18n.locale
|
this.currentLang = this.$i18n.locale
|
||||||
this.loadRememberedAccount()
|
this.loadRememberedAccount()
|
||||||
// console.log(login(1))
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
togglePasswordVisibility() {
|
togglePasswordVisibility() {
|
||||||
@@ -200,41 +205,51 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleLogin() {
|
handleLogin() {
|
||||||
// if (!this.agreeTerms) {
|
// if (!this.agreeTerms) {
|
||||||
// this.showError = true
|
// this.showError = true
|
||||||
// this.errorMessage = this.$t('login.errors.agreeTermsFirst')
|
// this.errorMessage = this.$t('login.errors.agreeTermsFirst')
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if (!this.username) {
|
if (!this.username) {
|
||||||
// this.showError = true
|
this.showError = true
|
||||||
// this.errorMessage = this.$t('login.errors.emptyUsername')
|
this.errorMessage = this.$t('login.errors.emptyUsername')
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (this.username.length !== 6 || !/^\d{6}$/.test(this.username)) {
|
if (this.username.length !== 6 || !/^\d{6}$/.test(this.username)) {
|
||||||
// this.showError = true
|
this.showError = true
|
||||||
// this.errorMessage = this.$t('login.errors.invalidIdCard')
|
this.errorMessage = this.$t('login.errors.invalidIdCard')
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (!this.password) {
|
if (!this.password) {
|
||||||
// this.showError = true
|
this.showError = true
|
||||||
// this.errorMessage = this.$t('login.errors.emptyPassword')
|
this.errorMessage = this.$t('login.errors.emptyPassword')
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
// this.isLoading = true
|
this.isLoading = true
|
||||||
// this.showError = false
|
this.showError = false
|
||||||
|
|
||||||
setTimeout(() => {
|
// 模拟登录过程
|
||||||
console.log('登录信息:', { username: this.username, password: this.password })
|
setTimeout(() => {
|
||||||
this.saveRememberedAccount()
|
console.log('登录信息:', { username: this.username, password: this.password })
|
||||||
uni.redirectTo({ url: '/pages/home/index' })
|
this.saveRememberedAccount()
|
||||||
console.log('登录成功')
|
this.isLoading = false
|
||||||
this.isLoading = false
|
|
||||||
}, 1500)
|
// 确保只跳转一次
|
||||||
},
|
uni.redirectTo({
|
||||||
|
url: '/pages/home/index',
|
||||||
|
success: () => {
|
||||||
|
console.log('跳转成功')
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('跳转失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
},
|
||||||
|
|
||||||
navigateToRegister() {
|
navigateToRegister() {
|
||||||
uni.redirectTo({ url: '/pages/register/register' })
|
uni.redirectTo({ url: '/pages/register/register' })
|
||||||
@@ -556,5 +571,4 @@ export default {
|
|||||||
from { transform: rotate(0deg); }
|
from { transform: rotate(0deg); }
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="register-container">
|
<view class="register-container">
|
||||||
<!-- 语言选择文字按钮(点击文字即可弹出窗口) -->
|
<!-- 语言选择文字按钮 -->
|
||||||
<view class="language-text-btn" @click="showLanguageModal = true">
|
<view class="language-text-btn" @click="showLanguageModal = true">
|
||||||
<text class="lang-text">{{ $t('login.changelanguage') }}</text>
|
<text class="lang-text">{{ $t('login.changelanguage') }}</text>
|
||||||
<i class="iconfont icon-arrow-down"></i>
|
<i class="iconfont icon-arrow-down"></i>
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="lang-item"
|
class="lang-item"
|
||||||
@click="changeLanguage('cn')"
|
@click="changeLanguage('zh')"
|
||||||
:class="{ 'active': currentLang === 'cn' }"
|
:class="{ 'active': currentLang === 'zh' }"
|
||||||
>
|
>
|
||||||
<text>中文</text>
|
<text>Chinese</text>
|
||||||
<i class="iconfont icon-check" v-if="currentLang === 'cn'"></i>
|
<i class="iconfont icon-check" v-if="currentLang === 'zh'"></i>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<button class="modal-close" @click="showLanguageModal = false">
|
<button class="modal-close" @click="showLanguageModal = false">
|
||||||
@@ -54,12 +54,12 @@
|
|||||||
:placeholder="$t('register.idCardPlaceholder')"
|
:placeholder="$t('register.idCardPlaceholder')"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
maxlength="6"
|
maxlength="6"
|
||||||
@input="validateIdCard"
|
@input="handleIdCardInput"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 密码输入(带显示/隐藏切换) -->
|
<!-- 密码输入 -->
|
||||||
<view class="form-group">
|
<view class="form-group">
|
||||||
<text class="input-label">{{ $t('register.passwordLabel') }}</text>
|
<text class="input-label">{{ $t('register.passwordLabel') }}</text>
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
@@ -71,13 +71,14 @@
|
|||||||
class="input-field"
|
class="input-field"
|
||||||
@input="validatePassword"
|
@input="validatePassword"
|
||||||
/>
|
/>
|
||||||
<button
|
<view class="toggle-btn-wrapper">
|
||||||
class="toggle-btn"
|
<view
|
||||||
@click.stop="togglePasswordVisibility"
|
class="toggle-btn"
|
||||||
:class="{ active: showPassword }"
|
@click.stop="togglePasswordVisibility"
|
||||||
>
|
>
|
||||||
<i class="iconfont" :class="showPassword ? 'icon-eye' : 'icon-eye-close'"></i>
|
<i class="iconfont" :class="showPassword ? 'icon-eye' : 'icon-eye-close'"></i>
|
||||||
</button>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="password-tip" v-if="showPasswordTip">{{ $t('register.passwordTip') }}</text>
|
<text class="password-tip" v-if="showPasswordTip">{{ $t('register.passwordTip') }}</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -94,13 +95,14 @@
|
|||||||
class="input-field"
|
class="input-field"
|
||||||
@input="validateConfirmPassword"
|
@input="validateConfirmPassword"
|
||||||
/>
|
/>
|
||||||
<button
|
<view class="toggle-btn-wrapper">
|
||||||
class="toggle-btn"
|
<view
|
||||||
@click.stop="toggleConfirmPasswordVisibility"
|
class="toggle-btn"
|
||||||
:class="{ active: showConfirmPassword }"
|
@click.stop="toggleConfirmPasswordVisibility"
|
||||||
>
|
>
|
||||||
<i class="iconfont" :class="showConfirmPassword ? 'icon-eye' : 'icon-eye-close'"></i>
|
<i class="iconfont" :class="showConfirmPassword ? 'icon-eye' : 'icon-eye-close'"></i>
|
||||||
</button>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -154,15 +156,31 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 注册按钮 -->
|
<!-- 注册按钮 -->
|
||||||
<button
|
<!-- <button
|
||||||
class="register-btn"
|
class="register-btn"
|
||||||
@click="handleRegister"
|
@click="handleRegister"
|
||||||
:disabled="isLoading || !agreeTerms || formInvalid"
|
:disabled="isLoading || !agreeTerms || formInvalid"
|
||||||
>
|
>
|
||||||
<template v-if="isLoading">
|
<template v-if="isLoading">
|
||||||
<i class="iconfont icon-loading loading-icon"></i>
|
<i class="iconfont icon-loading loading-icon"></i>
|
||||||
|
{{ $t('register.registerProcessing') }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ $t('register.registerButton') }}
|
||||||
|
</template>
|
||||||
|
</button> -->
|
||||||
|
<button
|
||||||
|
class="register-btn"
|
||||||
|
@click="handleRegister"
|
||||||
|
|
||||||
|
>
|
||||||
|
<template v-if="isLoading">
|
||||||
|
<i class="iconfont icon-loading loading-icon"></i>
|
||||||
|
{{ $t('register.registerProcessing') }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ $t('register.registerButton') }}
|
||||||
</template>
|
</template>
|
||||||
{{ $t('register.registerButton') }}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 登录链接 -->
|
<!-- 登录链接 -->
|
||||||
@@ -193,7 +211,7 @@ export default {
|
|||||||
showError: false,
|
showError: false,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
currentLang: 'en', // 默认英文
|
currentLang: 'en',
|
||||||
showLanguageModal: false,
|
showLanguageModal: false,
|
||||||
showPassword: false,
|
showPassword: false,
|
||||||
showConfirmPassword: false,
|
showConfirmPassword: false,
|
||||||
@@ -206,18 +224,44 @@ export default {
|
|||||||
this.currentLang = this.$i18n.locale
|
this.currentLang = this.$i18n.locale
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 切换密码显示状态
|
// 处理身份证输入(自动转换小写x为大写X)
|
||||||
|
handleIdCardInput() {
|
||||||
|
if (this.idCardLast6 && this.idCardLast6.length > 0) {
|
||||||
|
const lastChar = this.idCardLast6.slice(-1)
|
||||||
|
if (lastChar === 'x') {
|
||||||
|
this.idCardLast6 = this.idCardLast6.slice(0, -1) + 'X'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.validateIdCard()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 验证身份证后六位(最后一位支持大写X)
|
||||||
|
validateIdCard() {
|
||||||
|
if (!this.idCardLast6 || this.idCardLast6.trim() === '') {
|
||||||
|
this.setError(this.$t('register.errors.emptyIdCard'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const idReg = /^\d{5}[0-9X]$/
|
||||||
|
if (!idReg.test(this.idCardLast6)) {
|
||||||
|
this.setError(this.$t('register.errors.invalidIdCard'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearError()
|
||||||
|
this.checkFormValidity()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
togglePasswordVisibility() {
|
togglePasswordVisibility() {
|
||||||
this.showPassword = !this.showPassword
|
this.showPassword = !this.showPassword
|
||||||
this.showPasswordTip = true
|
this.showPasswordTip = true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 切换确认密码显示状态
|
|
||||||
toggleConfirmPasswordVisibility() {
|
toggleConfirmPasswordVisibility() {
|
||||||
this.showConfirmPassword = !this.showConfirmPassword
|
this.showConfirmPassword = !this.showConfirmPassword
|
||||||
},
|
},
|
||||||
|
|
||||||
// 切换语言
|
|
||||||
changeLanguage(lang) {
|
changeLanguage(lang) {
|
||||||
this.currentLang = lang
|
this.currentLang = lang
|
||||||
this.$i18n.locale = lang
|
this.$i18n.locale = lang
|
||||||
@@ -225,20 +269,7 @@ export default {
|
|||||||
this.showLanguageModal = false
|
this.showLanguageModal = false
|
||||||
},
|
},
|
||||||
|
|
||||||
// 验证身份证后六位
|
|
||||||
validateIdCard() {
|
|
||||||
const idReg = /^\d{6}$/
|
|
||||||
if (this.idCardLast6 && !idReg.test(this.idCardLast6)) {
|
|
||||||
this.setError(this.$t('register.errors.invalidIdCard'))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
this.checkFormValidity()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
|
|
||||||
// 验证密码
|
|
||||||
validatePassword() {
|
validatePassword() {
|
||||||
// 密码规则:至少8位,包含数字和字母
|
|
||||||
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
|
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
|
||||||
if (this.password && !pwdReg.test(this.password)) {
|
if (this.password && !pwdReg.test(this.password)) {
|
||||||
this.setError(this.$t('register.errors.invalidPassword'))
|
this.setError(this.$t('register.errors.invalidPassword'))
|
||||||
@@ -249,7 +280,6 @@ export default {
|
|||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 验证确认密码
|
|
||||||
validateConfirmPassword() {
|
validateConfirmPassword() {
|
||||||
if (this.confirmPassword && this.confirmPassword !== this.password) {
|
if (this.confirmPassword && this.confirmPassword !== this.password) {
|
||||||
this.setError(this.$t('register.errors.passwordMismatch'))
|
this.setError(this.$t('register.errors.passwordMismatch'))
|
||||||
@@ -259,7 +289,6 @@ export default {
|
|||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 验证邮箱
|
|
||||||
validateEmail() {
|
validateEmail() {
|
||||||
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||||
if (this.email && !emailReg.test(this.email)) {
|
if (this.email && !emailReg.test(this.email)) {
|
||||||
@@ -270,59 +299,49 @@ export default {
|
|||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 检查表单有效性
|
|
||||||
checkFormValidity() {
|
checkFormValidity() {
|
||||||
const idReg = /^\d{6}$/
|
const idReg = /^\d{5}[0-9X]$/
|
||||||
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
|
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
|
||||||
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||||
|
|
||||||
const isIdValid = idReg.test(this.idCardLast6)
|
const isIdValid = this.idCardLast6 && idReg.test(this.idCardLast6)
|
||||||
const isPwdValid = pwdReg.test(this.password)
|
const isPwdValid = this.password && pwdReg.test(this.password)
|
||||||
const isConfirmValid = this.confirmPassword === this.password
|
const isConfirmValid = this.confirmPassword && this.confirmPassword === this.password
|
||||||
const isEmailValid = emailReg.test(this.email)
|
const isEmailValid = this.email && emailReg.test(this.email)
|
||||||
|
|
||||||
this.formInvalid = !(isIdValid && isPwdValid && isConfirmValid && isEmailValid)
|
this.formInvalid = !(isIdValid && isPwdValid && isConfirmValid && isEmailValid)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 设置错误信息
|
|
||||||
setError(message) {
|
setError(message) {
|
||||||
this.showError = true
|
this.showError = true
|
||||||
this.errorMessage = message
|
this.errorMessage = message
|
||||||
},
|
},
|
||||||
|
|
||||||
// 处理注册
|
clearError() {
|
||||||
|
this.showError = false
|
||||||
|
this.errorMessage = ''
|
||||||
|
},
|
||||||
|
|
||||||
handleRegister() {
|
handleRegister() {
|
||||||
// 再次验证表单
|
|
||||||
if (!this.validateIdCard() || !this.validatePassword() || !this.validateEmail()) {
|
if (!this.validateIdCard() || !this.validatePassword() || !this.validateEmail()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证协议同意
|
// if (!this.agreeTerms) {
|
||||||
if (!this.agreeTerms) {
|
// this.setError(this.$t('register.errors.agreeTermsFirst'))
|
||||||
this.setError(this.$t('register.errors.agreeTermsFirst'))
|
// return
|
||||||
return
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟注册
|
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
this.showError = false
|
this.showError = false
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('注册信息:', {
|
|
||||||
idCardLast6: this.idCardLast6,
|
|
||||||
password: this.password,
|
|
||||||
inviteCode: this.inviteCode,
|
|
||||||
email: this.email
|
|
||||||
})
|
|
||||||
|
|
||||||
// 注册成功提示
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: this.$t('register.registerSuccess'),
|
title: this.$t('register.registerSuccess'),
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
})
|
})
|
||||||
|
|
||||||
// 跳转登录页面
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.navigateTo({ url: '/pages/login/login' })
|
uni.navigateTo({ url: '/pages/login/login' })
|
||||||
}, 2000)
|
}, 2000)
|
||||||
@@ -331,12 +350,10 @@ export default {
|
|||||||
}, 2000)
|
}, 2000)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 跳转到登录页面
|
|
||||||
navigateToLogin() {
|
navigateToLogin() {
|
||||||
uni.navigateTo({ url: '/pages/login/login' })
|
uni.navigateTo({ url: '/pages/login/login' })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 打开协议详情
|
|
||||||
openAgreement(type) {
|
openAgreement(type) {
|
||||||
const title = type === 'terms'
|
const title = type === 'terms'
|
||||||
? this.$t('register.userAgreement')
|
? this.$t('register.userAgreement')
|
||||||
@@ -523,6 +540,24 @@ export default {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toggle-btn {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-btn .iconfont {
|
||||||
|
margin: 0;
|
||||||
|
color: #999;
|
||||||
|
font-size: 34rpx;
|
||||||
|
}
|
||||||
|
|
||||||
/* 邀请码提示 */
|
/* 邀请码提示 */
|
||||||
.invite-tip {
|
.invite-tip {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -568,6 +603,7 @@ export default {
|
|||||||
.register-btn {
|
.register-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 90rpx;
|
height: 90rpx;
|
||||||
|
z-index: 100;
|
||||||
line-height: 90rpx;
|
line-height: 90rpx;
|
||||||
border-radius: 80rpx;
|
border-radius: 80rpx;
|
||||||
background-color: #007aff !important;
|
background-color: #007aff !important;
|
||||||
@@ -612,6 +648,4 @@ export default {
|
|||||||
from { transform: rotate(0deg); }
|
from { transform: rotate(0deg); }
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"index.Loading": "Loading...",
|
|
||||||
"index.errMsg": "Failed to load",
|
|
||||||
"login.loginButton": "Login",
|
"login.loginButton": "Login",
|
||||||
"login.usernamePlaceholder": "Please enter username",
|
"login.usernamePlaceholder": "Please enter username",
|
||||||
"login.idCardLabel": "Last 6 digits of ID card",
|
"login.idCardLabel": "Last 6 digits of ID card",
|
||||||
@@ -22,7 +20,7 @@
|
|||||||
|
|
||||||
"register.chooseLanguage": "Select Language",
|
"register.chooseLanguage": "Select Language",
|
||||||
"register.languagechangeclose": "Done",
|
"register.languagechangeclose": "Done",
|
||||||
"register.appTitle": "European & American Stock Software",
|
"register.appTitle": "European Stock Software",
|
||||||
"register.registerDesc": "Create an account to continue",
|
"register.registerDesc": "Create an account to continue",
|
||||||
"register.idCardLabel": "Last 6 digits of ID card",
|
"register.idCardLabel": "Last 6 digits of ID card",
|
||||||
"register.idCardPlaceholder": "Please enter last 6 digits of ID card",
|
"register.idCardPlaceholder": "Please enter last 6 digits of ID card",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createSSRApp } from "vue";
|
|||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import en from './enUS.json';
|
import en from './enUS.json';
|
||||||
import cn from './zh_CN.json';
|
import zh from './zh_CN.json';
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
// 创建 Vue 应用
|
// 创建 Vue 应用
|
||||||
@@ -10,8 +10,8 @@ export function createApp() {
|
|||||||
|
|
||||||
// 获取系统语言(更安全的写法)
|
// 获取系统语言(更安全的写法)
|
||||||
const systemInfo = uni.getSystemInfoSync() || {};
|
const systemInfo = uni.getSystemInfoSync() || {};
|
||||||
const systemLanguage = systemInfo.language || 'cn';
|
const systemLanguage = systemInfo.language || 'zh';
|
||||||
let cur_lang = 'cn';
|
let cur_lang = 'zh';
|
||||||
|
|
||||||
// 语言判断(转换为小写更可靠)
|
// 语言判断(转换为小写更可靠)
|
||||||
switch (systemLanguage.toLowerCase()) {
|
switch (systemLanguage.toLowerCase()) {
|
||||||
@@ -22,7 +22,7 @@ export function createApp() {
|
|||||||
case 'zh':
|
case 'zh':
|
||||||
case 'zh-cn':
|
case 'zh-cn':
|
||||||
case 'zh-hans':
|
case 'zh-hans':
|
||||||
cur_lang = 'cn';
|
cur_lang = 'zh';
|
||||||
break;
|
break;
|
||||||
/* 如果需要印尼语
|
/* 如果需要印尼语
|
||||||
case 'id':
|
case 'id':
|
||||||
@@ -38,7 +38,7 @@ const i18n = createI18n({
|
|||||||
fallbackLocale: 'en', // 建议英文作为回退
|
fallbackLocale: 'en', // 建议英文作为回退
|
||||||
messages: {
|
messages: {
|
||||||
en,
|
en,
|
||||||
cn
|
zh
|
||||||
},
|
},
|
||||||
missing: (locale, key) => {
|
missing: (locale, key) => {
|
||||||
console.error(`[i18n] 严重: '${key}' 在 '${locale}' 语言包中不存在`);
|
console.error(`[i18n] 严重: '${key}' 在 '${locale}' 语言包中不存在`);
|
||||||
|
|||||||
@@ -28,11 +28,13 @@
|
|||||||
|
|
||||||
"register.chooseLanguage": "选择语言",
|
"register.chooseLanguage": "选择语言",
|
||||||
"register.languagechangeclose": "完成",
|
"register.languagechangeclose": "完成",
|
||||||
"register.appTitle": "欧美股票软件",
|
"register.appTitle": "欧洲股票软件",
|
||||||
"register.registerDesc": "创建一个账号以继续",
|
"register.registerDesc": "创建一个账号以继续",
|
||||||
"register.idCardLabel": "身份证号码后六位",
|
"register.idCardLabel": "身份证号码后六位",
|
||||||
"register.idCardPlaceholder": "请输入身份证号码后六位",
|
"register.idCardPlaceholder": "请输入身份证号码后六位",
|
||||||
|
"register.errors.emptyIdCard": "请输入身份证号码",
|
||||||
"register.passwordLabel": "密码",
|
"register.passwordLabel": "密码",
|
||||||
|
"register.passwordTip": "密码长度不能小于6位",
|
||||||
"“register.passwordPlaceholder": "请输入密码",
|
"“register.passwordPlaceholder": "请输入密码",
|
||||||
"register.confirmPasswordLabel": "确认密码",
|
"register.confirmPasswordLabel": "确认密码",
|
||||||
"register.passwordPlaceholder": "请输入确认密码",
|
"register.passwordPlaceholder": "请输入确认密码",
|
||||||
@@ -41,6 +43,16 @@
|
|||||||
"register.inviteCodePlaceholder": "请输入邀请码",
|
"register.inviteCodePlaceholder": "请输入邀请码",
|
||||||
"register.emailLabel": "邮箱",
|
"register.emailLabel": "邮箱",
|
||||||
"register.emailPlaceholder": "请输入邮箱",
|
"register.emailPlaceholder": "请输入邮箱",
|
||||||
|
"register.errors.emailRequired": "邮箱不能为空",
|
||||||
|
"register.idCardLast6Error": "请输入正确的身份证号码",
|
||||||
|
"register.errors.invalidPassword": "密码至少8位,必须包含数字和字母",
|
||||||
|
"register.errors.emptyPassword": "密码不能为空",
|
||||||
|
"register.errors.confirmPasswordRequired": "请再次输入密码",
|
||||||
|
"register.errors.invalidEmail": "请输入正确的邮箱",
|
||||||
|
"register.errors.passwordMismatch": "两次输入的密码不一致",
|
||||||
|
"register.errors.invalidIdCard": "请输入正确的身份证号",
|
||||||
|
"register.errors.agreeTermsFirst": "请先阅读并同意用户协议和隐私政策",
|
||||||
|
"register.registerProcessing": "注册中...",
|
||||||
"register.agreeTermsPrefix": "我已阅读并同意",
|
"register.agreeTermsPrefix": "我已阅读并同意",
|
||||||
"register.userAgreement": "用户协议",
|
"register.userAgreement": "用户协议",
|
||||||
"register.and": "和",
|
"register.and": "和",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// utils/request.js
|
// utils/request.js
|
||||||
|
|
||||||
const BASE_URL = process.env.VUE_APP_API_BASE_URL || '/api'
|
const BASE_URL = process.env.VUE_APP_API_BASE_URL
|
||||||
const TOKEN_KEY = 'auth_token'
|
const TOKEN_KEY = 'auth_token'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,4 +131,4 @@ export const http = {
|
|||||||
|
|
||||||
delete: (url, data, options) =>
|
delete: (url, data, options) =>
|
||||||
request({ url, data, method: 'DELETE', ...options })
|
request({ url, data, method: 'DELETE', ...options })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user