2025-12-15 11:02:37 +08:00

266 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<image class="logo" src="/static/logo.png" mode="widthFix"></image>
<!-- #ifdef MP-TOUTIAO -->
<view class="login-form">
<view class="input-row">
<text class="label">账号</text>
<input
type="text"
v-model="account"
class="input-field"
placeholder="请输入账号"
/>
</view>
<view class="input-row">
<text class="label">密码</text>
<input
type="password"
v-model="pwd"
class="input-field"
placeholder="请输入密码"
/>
</view>
<!-- 记住账号密码 -->
<view class="remember-row">
<checkbox :value="remember" @change="handleRememberChange" />
<text class="remember-text">记住账号密码</text>
</view>
<!-- 按钮区域 -->
<view class="button-group">
<button class="btn login-btn" @click="handleLogin">登录</button>
</view>
<!-- 注册链接 -->
<view class="register-link">
<text class="register-text" @click="goToRegister">还没有账号点击注册</text>
</view>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="title">微信登录</view>
<button class="btn" open-type="getPhoneNumber" :disabled="loading" @getphonenumber="onGetPhoneNumber">授权手机号快速登录</button>
<!-- #endif -->
<view class="agreements">
<text>注册或登录即表示您已阅读并同意</text>
<text class="link" @tap="toUserAgreement">用户协议</text>
<text></text>
<text class="link" @tap="toPurchaseAgreement">购买协议</text>
</view>
<view v-if="needBindPhone" class="tip">登录成功请绑定手机号以完成登录</view>
<view v-if="error" class="error">{{ error }}</view>
</view>
</template>
<script setup>
import { ref, computed,onMounted } from 'vue'
import { wechatLogin, bindPhone, getUserStats, getPointsBalance } from '../../api/appUser'
const loading = ref(false)
const error = ref('')
const needBindPhone = ref(false)
const account =ref("")
const pwd = ref ("")
const remember=ref(false)
const loggedIn = computed(() => !!uni.getStorageSync('token'))
onMounted(() => {
try {
const saved = uni.getStorageSync('loginInfo')
if (saved && saved.account && saved.pwd) {
account.value = saved.account
pwd.value = saved.pwd
remember.value = true
}
} catch (e) {
console.error('读取本地登录信息失败', e)
}
})
// 切换“记住密码”
const handleRememberChange = (e) => {
remember.value = e.detail.value.length > 0
}
function goToRegister() { uni.navigateTo({ url: '/pages/register/register' }) }
function onLogin() {}
function toUserAgreement() { uni.navigateTo({ url: '/pages/agreement/user' }) }
function toPurchaseAgreement() { uni.navigateTo({ url: '/pages/agreement/purchase' }) }
function onGetPhoneNumber(e) {
const phoneCode = e.detail.code
console.log('login_flow start getPhoneNumber, codeExists:', !!phoneCode)
if (!phoneCode) {
uni.showToast({ title: '未授权手机号', icon: 'none' })
console.error('login_flow error: missing phoneCode')
return
}
loading.value = true
error.value = ''
uni.login({
provider: 'weixin',
success: async (res) => {
try {
const loginCode = res.code
console.log('login_flow uni.login success, loginCode exists:', !!loginCode)
const inviterCode = uni.getStorageSync('inviter_code')
console.log('login_flow using inviter_code:', inviterCode)
const data = await wechatLogin(loginCode, inviterCode)
console.log('login_flow wechatLogin response user_id:', data && data.user_id)
const token = data && data.token
const user_id = data && data.user_id
const avatar = data && data.avatar
const nickname = data && data.nickname
const invite_code = data && data.invite_code
const openid = data && data.openid
uni.setStorageSync('user_info', data || {})
if (token) {
uni.setStorageSync('token', token)
console.log('login_flow token stored')
}
if (user_id) {
uni.setStorageSync('user_id', user_id)
console.log('login_flow user_id stored:', user_id)
}
if (avatar) {
uni.setStorageSync('avatar', avatar)
}
if (nickname) {
uni.setStorageSync('nickname', nickname)
}
if (invite_code) {
uni.setStorageSync('invite_code', invite_code)
}
if (openid) {
uni.setStorageSync('openid', openid)
}
console.log('login_flow bindPhone start')
try {
// 首次绑定前短暂延迟,确保服务端 token 生效
await new Promise(r => setTimeout(r, 600))
const bindRes = await bindPhone(user_id, phoneCode, { 'X-Suppress-Auth-Modal': true })
const phoneNumber = (bindRes && (bindRes.phone || bindRes.phone_number || bindRes.mobile)) || ''
if (phoneNumber) uni.setStorageSync('phone_number', phoneNumber)
} catch (bindErr) {
if (bindErr && bindErr.statusCode === 401) {
console.warn('login_flow bindPhone 401, try re-login and retry')
// 重新获取登录 code
const relogin = await new Promise((resolve, reject) => {
uni.login({ provider: 'weixin', success: resolve, fail: reject })
})
const data2 = await wechatLogin(relogin.code, inviterCode)
const token2 = data2 && data2.token
const user2 = data2 && data2.user_id
if (token2) uni.setStorageSync('token', token2)
if (user2) uni.setStorageSync('user_id', user2)
// 再次延迟后重试绑定
await new Promise(r => setTimeout(r, 600))
const bindRes2 = await bindPhone(user2 || user_id, phoneCode, { 'X-Suppress-Auth-Modal': true })
const phoneNumber2 = (bindRes2 && (bindRes2.phone || bindRes2.phone_number || bindRes2.mobile)) || ''
if (phoneNumber2) uni.setStorageSync('phone_number', phoneNumber2)
} else {
throw bindErr
}
}
uni.setStorageSync('phone_bound', true)
console.log('login_flow bindPhone success, phone_bound stored')
try {
const stats = await getUserStats(user_id)
console.log('login_flow getUserStats success')
const balance = await getPointsBalance(user_id)
console.log('login_flow getPointsBalance success')
uni.setStorageSync('user_stats', stats)
const b = balance && balance.balance !== undefined ? balance.balance : balance
uni.setStorageSync('points_balance', b)
} catch (e) {
console.error('login_flow fetch stats/points error:', e && (e.message || e.errMsg))
}
uni.showToast({ title: '登录并绑定成功', icon: 'success' })
console.log('login_flow navigate to index')
uni.reLaunch({ url: '/pages/index/index' })
} catch (err) {
console.error('login_flow error:', err && (err.message || err.errMsg), 'status:', err && err.statusCode)
error.value = err.message || '登录或绑定失败'
} finally {
loading.value = false
}
},
fail: (e) => {
console.error('login_flow uni.login fail:', e && e.errMsg)
error.value = '微信登录失败'
loading.value = false
}
})
}
</script>
<style scoped>
.login-form {
padding: 20px;
}
.input-row {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.remember-row {
display: flex;
align-items: center;
margin-bottom: 60rpx;
}
.remember-text {
margin-left: 16rpx;
font-size: 28rpx;
color: #666;
}
.register-link {
text-align: center;
}
.register-text {
font-size: 26rpx;
color: #999;
text-decoration: underline; /* 可选:加下划线 */
}
.register-text:active {
color: #ff6700; /* 点击时变色 */
}
.label {
width: 60px; /* 固定宽度,保证对齐 */
font-size: 16px;
color: #333;
}
.input-field {
flex: 1;
height: 40px;
border: 2px solid #ccc; /* 边框加粗 */
border-radius: 4px;
padding: 0 10px;
font-size: 16px;
/* 去除 iOS 默认样式 */
-webkit-appearance: none;
outline: none;
}
.container { padding: 40rpx; display: flex; flex-direction: column; align-items: center }
.logo { width: 200rpx; margin-top: 100rpx; margin-bottom: 40rpx }
.title { font-size: 36rpx; margin-bottom: 20rpx }
.btn { width: 80%; margin-top: 20rpx }
.agreements { margin-top: 16rpx; font-size: 24rpx; color: #666; display: flex; flex-wrap: wrap; justify-content: center }
.link { color: #007AFF; margin: 0 6rpx }
.tip { color: #666; margin-top: 12rpx }
.error { color: #e43; margin-top: 20rpx }
</style>