06488f0237
功能: - Go后端 (Gin + GORM + PostgreSQL) - UniApp用户端 (iOS/Android/小程序) - DaisyUI5后台管理 - JWT认证 + 微信登录 - 盲选加权算法 - 会员系统 + 优惠券 - 打分评价 + 偏好学习
215 lines
4.9 KiB
Vue
215 lines
4.9 KiB
Vue
<!--
|
||
小程序登录引导页
|
||
微信小程序只能用 wx.login() 获取 code,不能收集密码
|
||
流程: wx.login() → 后端 code2session → 自动注册/登录 → 返回 JWT
|
||
-->
|
||
<template>
|
||
<view class="login-page">
|
||
<view class="login-hero">
|
||
<text class="logo">🎲</text>
|
||
<text class="app-name">帮我选</text>
|
||
<text class="app-desc">每一次选择,都是惊喜</text>
|
||
</view>
|
||
|
||
<view class="login-body">
|
||
<!-- 方式一: 微信一键登录 (小程序) -->
|
||
<button class="login-btn primary" @click="wechatLogin" :loading="logining">
|
||
<text class="login-icon">💬</text>
|
||
<text>微信一键登录</text>
|
||
</button>
|
||
|
||
<text class="login-hint">登录即同意《用户协议》和《隐私政策》</text>
|
||
|
||
<!-- 方式二: 手机号登录 (备选) -->
|
||
<view class="divider">
|
||
<text>或</text>
|
||
</view>
|
||
|
||
<button class="login-btn secondary" @click="phoneLogin">
|
||
<text class="login-icon">📱</text>
|
||
<text>手机号快捷登录</text>
|
||
</button>
|
||
</view>
|
||
|
||
<view class="login-footer">
|
||
<text class="feature-item">🎲 盲选吃喝玩乐</text>
|
||
<text class="feature-item">🤖 AI 智能推荐</text>
|
||
<text class="feature-item">🎁 优惠券联动</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import { authApi } from '@/api/index.js'
|
||
import { useUserStore } from '@/store/user.js'
|
||
|
||
const logining = ref(false)
|
||
const userStore = useUserStore()
|
||
|
||
// ─── 微信一键登录 ───
|
||
async function wechatLogin() {
|
||
logining.value = true
|
||
|
||
// 1. 获取微信 code
|
||
try {
|
||
const loginRes = await new Promise((resolve, reject) => {
|
||
wx.login({
|
||
success: resolve,
|
||
fail: reject
|
||
})
|
||
})
|
||
|
||
const code = loginRes.code
|
||
if (!code) {
|
||
uni.showToast({ title: '获取登录凭证失败', icon: 'none' })
|
||
logining.value = false
|
||
return
|
||
}
|
||
|
||
// 2. 发送到后端换取 JWT
|
||
const data = await authApi.wechatLogin({ code })
|
||
|
||
// 3. 保存 token + 用户信息
|
||
uni.setStorageSync('token', data.token)
|
||
uni.setStorageSync('refresh_token', data.refresh_token)
|
||
uni.setStorageSync('userInfo', data.user)
|
||
userStore.setUserInfo(data.user)
|
||
userStore.setHasMember(data.has_member || false)
|
||
|
||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||
|
||
// 4. 跳转首页
|
||
setTimeout(() => {
|
||
uni.switchTab({ url: '/pages/index/index' })
|
||
}, 500)
|
||
|
||
} catch (e) {
|
||
console.error('wechat login failed:', e)
|
||
uni.showToast({ title: '登录失败,请重试', icon: 'none' })
|
||
logining.value = false
|
||
}
|
||
}
|
||
|
||
// ─── 手机号登录 (小程序需要用户授权手机号) ───
|
||
function phoneLogin() {
|
||
// 小程序的 getPhoneNumber 需要 <button open-type="getPhoneNumber">
|
||
// 这里提示用户使用微信登录,因为手机号登录需要后端支持
|
||
uni.showToast({
|
||
title: '请使用微信一键登录',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
|
||
// ─── 检查是否已有token (自动登录) ───
|
||
onLoad(() => {
|
||
const token = uni.getStorageSync('token')
|
||
if (token) {
|
||
// 有 token → 直接进首页
|
||
uni.switchTab({ url: '/pages/index/index' })
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.login-page {
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #FF6B35 0%, #FF8C42 60%, #F5F5F5 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.login-hero {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding-top: 100rpx;
|
||
|
||
.logo {
|
||
font-size: 140rpx;
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
.app-name {
|
||
font-size: 56rpx;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
display: block;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
.app-desc {
|
||
font-size: 28rpx;
|
||
color: rgba(255,255,255,0.8);
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.login-body {
|
||
background: #fff;
|
||
border-radius: 40rpx 40rpx 0 0;
|
||
padding: 60rpx 50rpx;
|
||
min-height: 400rpx;
|
||
|
||
.login-btn {
|
||
width: 100%;
|
||
height: 90rpx;
|
||
border-radius: 45rpx;
|
||
border: none;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
margin-bottom: 24rpx;
|
||
|
||
&.primary {
|
||
background: linear-gradient(135deg, #FF6B35, #FF8C42);
|
||
color: #fff;
|
||
box-shadow: 0 8rpx 24rpx rgba(255,107,53,0.3);
|
||
}
|
||
&.secondary {
|
||
background: #fff;
|
||
color: #333;
|
||
border: 2rpx solid #eee;
|
||
}
|
||
|
||
.login-icon {
|
||
font-size: 36rpx;
|
||
}
|
||
}
|
||
|
||
.login-hint {
|
||
display: block;
|
||
text-align: center;
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
margin: 30rpx 0;
|
||
}
|
||
|
||
.divider {
|
||
text-align: center;
|
||
color: #ccc;
|
||
font-size: 24rpx;
|
||
margin: 20rpx 0;
|
||
}
|
||
}
|
||
|
||
.login-footer {
|
||
background: #fff;
|
||
padding: 40rpx;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
justify-content: center;
|
||
|
||
.feature-item {
|
||
font-size: 22rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
</style>
|