| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- <template>
- <view class="login-page">
- <view class="title">登录</view>
- <view class="tabs">
- <view class="tab" :class="{ active: loginType === 'password' }" @click="loginType = 'password'">密码登录</view>
- <view class="tab" :class="{ active: loginType === 'sms' }" @click="loginType = 'sms'">验证码登录</view>
- </view>
- <view class="form">
- <input class="input" v-model="mobile" placeholder="手机号" type="number" />
- <input v-if="loginType === 'password'" class="input" v-model="password" placeholder="密码" password />
- <view v-else class="sms-row">
- <input class="input sms-input" v-model="code" placeholder="验证码" type="number" />
- <view class="sms-btn" :class="{ disabled: sending || countdown > 0 }" @click="onSendCode">
- <text>{{ countdown > 0 ? countdown + 's' : (sending ? '发送中' : '获取验证码') }}</text>
- </view>
- </view>
- <view class="submit" :class="{ disabled: loading }" @click="onLogin">
- <text>{{ loading ? '登录中...' : '登录' }}</text>
- </view>
- </view>
- </view>
- </template>
- <script>
- import { login, loginBySms, sendSmsCode, setToken, getCurrentUserInfo, getUserIdFromToken } from '../../utils/api'
- const USER_KEY = 'current_user'
- export default {
- data() {
- return {
- loginType: 'password',
- mobile: '',
- password: '',
- code: '',
- loading: false,
- sending: false,
- countdown: 0,
- timer: null
- }
- },
- onUnload() {
- if (this.timer) clearInterval(this.timer)
- this.timer = null
- },
- methods: {
- async onSendCode() {
- if (this.sending || this.countdown > 0) return
- if (!this.mobile) {
- uni.showToast({ title: '请输入手机号', icon: 'none' })
- return
- }
- this.sending = true
- try {
- // 与桌面端保持一致,后端期望 platform: 'pc'
- await sendSmsCode(this.mobile)
- uni.showToast({ title: '验证码已发送', icon: 'none' })
- this.countdown = 60
- this.timer = setInterval(() => {
- this.countdown -= 1
- if (this.countdown <= 0) {
- clearInterval(this.timer)
- this.timer = null
- this.countdown = 0
- }
- }, 1000)
- } catch (e) {
- uni.showToast({ title: e.message || '发送失败', icon: 'none' })
- } finally {
- this.sending = false
- }
- },
- async onLogin() {
- if (this.loading) return
- if (!this.mobile) {
- uni.showToast({ title: '请输入手机号', icon: 'none' })
- return
- }
- if (this.loginType === 'password' && !this.password) {
- uni.showToast({ title: '请输入密码', icon: 'none' })
- return
- }
- if (this.loginType === 'sms' && !this.code) {
- uni.showToast({ title: '请输入验证码', icon: 'none' })
- return
- }
- this.loading = true
- try {
- const data =
- this.loginType === 'password'
- ? await login(this.mobile, this.password)
- : await loginBySms(this.mobile, this.code)
- const accessToken = data.access_token
- if (!accessToken) throw new Error('登录失败:未返回 access_token')
- setToken(accessToken)
- // 尽量补全 user 信息:优先用 login 返回的 user,否则拉 /users/me
- let user = data.user || null
- if (!user || !user.id) {
- const id = getUserIdFromToken(accessToken)
- user = user || {}
- if (id) user.id = id
- }
- if (!user || !user.id || !user.name) {
- try {
- const me = await getCurrentUserInfo(accessToken)
- // 兼容不同后端包裹
- user = me.user || me.data || me
- } catch (e) {}
- }
- try {
- uni.setStorageSync(USER_KEY, user || {})
- } catch (e) {}
- uni.reLaunch({ url: '/pages/index/index' })
- } catch (e) {
- uni.showToast({ title: e.message || '登录失败', icon: 'none' })
- } finally {
- this.loading = false
- }
- }
- }
- }
- </script>
- <style scoped>
- .login-page {
- min-height: 100vh;
- background: #fff;
- padding: 64rpx 48rpx;
- box-sizing: border-box;
- }
- .title {
- font-size: 44rpx;
- font-weight: 700;
- color: #222;
- margin-bottom: 48rpx;
- }
- .tabs {
- display: flex;
- gap: 24rpx;
- margin-bottom: 32rpx;
- }
- .tab {
- padding: 16rpx 24rpx;
- border-radius: 999rpx;
- background: #f0f0f0;
- color: #666;
- font-size: 26rpx;
- }
- .tab.active {
- background: rgba(37, 150, 83, 0.12);
- color: #259653;
- }
- .form {
- margin-top: 24rpx;
- }
- .input {
- height: 88rpx;
- padding: 0 24rpx;
- background: #f7f7f7;
- border-radius: 16rpx;
- font-size: 28rpx;
- margin-bottom: 20rpx;
- }
- .sms-row {
- display: flex;
- align-items: center;
- gap: 16rpx;
- }
- .sms-input {
- flex: 1;
- margin-bottom: 0;
- }
- .sms-btn {
- height: 88rpx;
- padding: 0 24rpx;
- border-radius: 16rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- background: #259653;
- color: #fff;
- font-size: 26rpx;
- }
- .sms-btn.disabled {
- opacity: 0.6;
- }
- .submit {
- margin-top: 32rpx;
- height: 92rpx;
- border-radius: 18rpx;
- background: #259653;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- font-size: 30rpx;
- }
- .submit.disabled {
- opacity: 0.7;
- }
- </style>
|