feat: 更新微信小程序登录流程和演示环境配置

This commit is contained in:
Ray.Hao
2026-03-05 16:57:54 +08:00
parent 67ada6263b
commit 5bf7f0561f
11 changed files with 137 additions and 64 deletions

View File

@@ -7,8 +7,8 @@ VITE_APP_PORT = 4096
VITE_APP_BASE_API = '/dev-api'
# API 服务器的 URL
#VITE_APP_API_URL = http://localhost:8989
VITE_APP_API_URL = https://api.youlai.tech
VITE_APP_API_URL = http://localhost:8000
# VITE_APP_API_URL = https://api.youlai.tech
# WebSocket 服务器的 URL不配置默认关闭 WebSocket
#VITE_APP_WS_ENDPOINT= ws://localhost:4096/ws

2
components.d.ts vendored
View File

@@ -1,4 +1,4 @@
/* eslint-disable */
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-components

View File

@@ -1,5 +1,5 @@
{
"name": "vue-uniapp-template",
"name": "youlai-uniapp-template",
"version": "0.0.0",
"scripts": {
"dev:app": "uni -p app",

View File

@@ -1,6 +1,7 @@
import request, { publicRequest } from "@/utils/request";
const AUTH_BASE_URL = "/api/v1/auth";
const WECHAT_MINIAPP_AUTH_BASE_URL = "/api/v1/wechat/miniapp/auth";
export interface LoginData {
username: string;
@@ -40,7 +41,7 @@ export interface WechatMiniappPhoneLoginData {
export interface WechatMiniappBindMobileData {
openid: string;
mobile: string;
code: string;
smsCode: string;
}
const AuthAPI = {
@@ -57,6 +58,8 @@ const AuthAPI = {
/**
* 发送短信验证码
*
* 演示环境说明:短信服务未配置,验证码固定为 123456
*/
sendSmsLoginCode(mobile: string): Promise<void> {
const mobileSafe = encodeURIComponent(mobile);
@@ -79,34 +82,46 @@ const AuthAPI = {
},
/**
* 微信小程序登录(个人小程序)
* 微信小程序静默登录
*
* 适用场景:个人小程序
* - 已绑定手机号的用户:直接返回 token登录成功
* - 未绑定手机号的用户:返回 openid需调用绑定手机号接口
*/
wechatMiniappLogin(code: string): Promise<WechatMiniappLoginResult> {
wechatMiniappSilentLogin(code: string): Promise<WechatMiniappLoginResult> {
return publicRequest<WechatMiniappLoginResult>({
url: `${AUTH_BASE_URL}/wechat-miniapp/login?code=${encodeURIComponent(code)}`,
url: `${WECHAT_MINIAPP_AUTH_BASE_URL}/silent-login?code=${encodeURIComponent(code)}`,
method: "POST",
});
},
/**
* 微信小程序一键登录(企业小程序)
* 微信小程序手机号快捷登录
*
* 适用场景:企业认证小程序(已开通手机号快捷登录权限)
* 一步完成登录,无需绑定流程,自动创建新用户
*/
wechatMiniappPhoneLogin(data: WechatMiniappPhoneLoginData): Promise<LoginResult> {
const loginCodeSafe = encodeURIComponent(data.loginCode);
const phoneCodeSafe = encodeURIComponent(data.phoneCode);
return publicRequest<LoginResult>({
url: `${AUTH_BASE_URL}/wechat-miniapp/phone-login`,
url: `${WECHAT_MINIAPP_AUTH_BASE_URL}/phone-login?loginCode=${loginCodeSafe}&phoneCode=${phoneCodeSafe}`,
method: "POST",
data,
});
},
/**
* 微信小程序绑定手机号
*
* 演示环境说明:短信服务未配置,验证码固定为 123456
*/
wechatMiniappBindMobile(data: WechatMiniappBindMobileData): Promise<LoginResult> {
const openidSafe = encodeURIComponent(data.openid);
const mobileSafe = encodeURIComponent(data.mobile);
const smsCodeSafe = encodeURIComponent(data.smsCode);
return publicRequest<LoginResult>({
url: `${AUTH_BASE_URL}/wechat-miniapp/bind-mobile`,
url: `${WECHAT_MINIAPP_AUTH_BASE_URL}/bind-mobile?openid=${openidSafe}&mobile=${mobileSafe}&smsCode=${smsCodeSafe}`,
method: "POST",
data,
});
},

View File

@@ -93,6 +93,14 @@ const UserAPI = {
});
},
/** 解绑第三方账号 */
unbindSocial(platform: string) {
return request({
url: `${USER_BASE_URL}/social?platform=${encodeURIComponent(platform)}`,
method: "DELETE",
});
},
/**
* 发送手机/邮箱验证码
*

View File

@@ -152,15 +152,10 @@
"backgroundColorTop": "@bgColorTop",
"backgroundColorBottom": "@bgColorBottom",
"enablePullDownRefresh": false,
"onReachBottomDistance": 50,
"animationType": "pop-in",
"animationDuration": 300
"onReachBottomDistance": 50
},
"tabBar": {
"custom": true,
"customize": true,
"overlay": true,
"height": "0",
"color": "@tabColor",
"selectedColor": "@tabSelectedColor",
"backgroundColor": "@tabBgColor",
@@ -179,4 +174,4 @@
},
"__esModule": true,
"subPackages": []
}
}

View File

@@ -71,6 +71,11 @@
</view>
</view>
<!-- 演示环境提示 -->
<view v-if="loginMode === 'SMS'" class="demo-tip">
<text class="demo-tip-text">演示环境验证码123456</text>
</view>
<!-- 登录按钮 -->
<button class="btn-primary" :disabled="loading || !canSubmit" @click="handleLogin">
{{ loading ? "登录中..." : " " }}
@@ -87,7 +92,7 @@
</view>
</view>
<!-- #ifdef MP-WECHAT -->
<!-- #ifdef MP-WEIXIN -->
<!-- 微信登录区域 -->
<view v-else class="form-area">
<!-- 企业一键登录 -->
@@ -96,7 +101,7 @@
open-type="getPhoneNumber"
@getphonenumber="handleWechatPhoneLogin"
>
<wd-icon name="wechat" size="20" color="#fff" class="mr-2" />
<image class="btn-wechat-icon" src="/static/icons/weixin.png" mode="aspectFit" />
微信一键登录
</button>
@@ -108,7 +113,7 @@
</view>
<!-- #endif -->
<!-- #ifdef MP-WECHAT -->
<!-- #ifdef MP-WEIXIN -->
<!-- 分割线 -->
<view v-if="loginMode !== 'WECHAT'" class="divider">
<view class="divider-line" />
@@ -118,9 +123,12 @@
<!-- 微信登录入口 -->
<view v-if="loginMode !== 'WECHAT'" class="wechat-entry">
<view class="wechat-icon" @click="loginMode = 'WECHAT'">
<wd-icon name="wechat" size="24" color="#fff" />
</view>
<image
class="wechat-icon-img"
src="/static/icons/weixin.png"
mode="aspectFit"
@click="loginMode = 'WECHAT'"
/>
</view>
<!-- #endif -->
@@ -183,6 +191,11 @@
</view>
</view>
<!-- 演示环境提示 -->
<view class="demo-tip">
<text class="demo-tip-text">演示环境验证码123456</text>
</view>
<button class="btn-bind" :disabled="bindLoading" @click="handleBindMobile">
{{ bindLoading ? "绑定中..." : "确认绑定" }}
</button>
@@ -221,7 +234,7 @@ const smsTimer = ref<ReturnType<typeof setInterval> | null>(null);
const formData = ref({
username: "admin",
password: "123456",
code: "1234",
code: "123456",
});
const redirect = ref("/pages/index/index");
@@ -459,7 +472,7 @@ const handleBindMobile = async () => {
await userStore.bindMobileForWechatMiniapp({
openid: wechatOpenid.value,
mobile,
code,
smsCode: code,
});
await userStore.getInfo();
showBindMobilePopup.value = false;
@@ -502,8 +515,8 @@ const navigateToAgreement = (type: string) => {
.bg-circle {
position: absolute;
border-radius: 50%;
filter: blur(64px);
border-radius: 50%;
}
.bg-circle-1 {
@@ -515,8 +528,8 @@ const navigateToAgreement = (type: string) => {
}
.bg-circle-2 {
bottom: -80px;
right: -80px;
bottom: -80px;
width: 320px;
height: 320px;
background-color: rgba(59, 130, 246, 0.15);
@@ -562,9 +575,9 @@ const navigateToAgreement = (type: string) => {
width: 100%;
padding: 32px;
background-color: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(24px);
border-radius: 24px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(24px);
}
:global(.dark) .login-card {
@@ -573,8 +586,8 @@ const navigateToAgreement = (type: string) => {
// 卡片头部
.card-header {
text-align: center;
margin-bottom: 32px;
text-align: center;
}
.card-title {
@@ -589,9 +602,9 @@ const navigateToAgreement = (type: string) => {
.card-desc {
display: block;
margin-top: 8px;
font-size: 14px;
color: #6b7280;
margin-top: 8px;
}
:global(.dark) .card-desc {
@@ -641,11 +654,11 @@ const navigateToAgreement = (type: string) => {
// 主按钮
.btn-primary {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: 600;
color: #fff;
@@ -659,18 +672,18 @@ const navigateToAgreement = (type: string) => {
}
&:disabled {
opacity: 0.5;
box-shadow: none;
opacity: 0.5;
}
}
// 微信按钮
.btn-wechat {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: 600;
color: #fff;
@@ -683,13 +696,19 @@ const navigateToAgreement = (type: string) => {
}
}
.btn-wechat-icon {
width: 20px;
height: 20px;
margin-right: 8px;
}
// 绑定按钮
.btn-bind {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: 600;
color: #fff;
@@ -703,11 +722,11 @@ const navigateToAgreement = (type: string) => {
// 验证码按钮
.sms-btn {
display: flex;
align-items: center;
justify-content: center;
height: 32px;
padding: 0 16px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: 500;
border-radius: 8px;
@@ -751,10 +770,10 @@ const navigateToAgreement = (type: string) => {
}
.switch-link {
margin-left: 4px;
font-size: 14px;
font-weight: 500;
color: #2563eb;
margin-left: 4px;
border-bottom: 1px solid #2563eb;
}
@@ -790,19 +809,13 @@ const navigateToAgreement = (type: string) => {
// 微信登录入口
.wechat-entry {
display: flex;
justify-content: center;
gap: 32px;
justify-content: center;
}
.wechat-icon {
.wechat-icon-img {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background-color: #22c55e;
border-radius: 50%;
box-shadow: 0 4px 6px -1px rgba(34, 197, 94, 0.3);
&:active {
transform: scale(0.95);
@@ -813,8 +826,8 @@ const navigateToAgreement = (type: string) => {
.agreement {
display: flex;
align-items: flex-start;
margin-top: 24px;
padding-top: 16px;
margin-top: 24px;
border-top: 1px solid #f3f4f6;
}
@@ -824,8 +837,8 @@ const navigateToAgreement = (type: string) => {
.agreement-text {
font-size: 12px;
color: #6b7280;
line-height: 1.5;
color: #6b7280;
}
:global(.dark) .agreement-text {
@@ -847,17 +860,37 @@ const navigateToAgreement = (type: string) => {
.bind-title {
display: block;
margin-bottom: 24px;
font-size: 18px;
font-weight: 600;
color: #111827;
text-align: center;
margin-bottom: 24px;
}
:global(.dark) .bind-title {
color: #fff;
}
// 演示环境提示
.demo-tip {
display: flex;
justify-content: center;
margin-top: 12px;
}
.demo-tip-text {
padding: 4px 12px;
font-size: 12px;
color: #f59e0b;
background-color: #fef3c7;
border-radius: 4px;
}
:global(.dark) .demo-tip-text {
color: #fbbf24;
background-color: rgba(251, 191, 36, 0.1);
}
// 暗黑模式适配
:deep(.wd-checkbox__label) {
display: flex;
@@ -865,7 +898,7 @@ const navigateToAgreement = (type: string) => {
}
:deep(.wd-popup__close) {
right: 16px;
top: 16px;
right: 16px;
}
</style>

View File

@@ -21,6 +21,9 @@
is-link
@click="handleOpenDialog(DialogType.EMAIL)"
/>
<!-- #ifdef MP-WEIXIN -->
<wd-cell title="微信" value="解绑" is-link @click="handleUnbindWechat" />
<!-- #endif -->
</wd-cell-group>
</wd-card>
@@ -211,6 +214,25 @@ const mobileTimer = ref<ReturnType<typeof setInterval> | null>(null);
const emailCountdown = ref(0);
const emailTimer = ref<ReturnType<typeof setTimeout> | null>(null);
const handleUnbindWechat = async () => {
uni.showModal({
title: "提示",
content: "确定要解绑微信吗?解绑后将无法使用微信小程序登录",
success: async (res) => {
if (!res.confirm) return;
try {
await UserAPI.unbindSocial("WECHAT_MINI");
uni.showToast({ title: "解绑成功", icon: "success" });
loadUserProfile();
} catch (e) {
console.error("解绑微信失败", e);
uni.showToast({ title: "解绑失败", icon: "none" });
}
},
});
};
/** 加载用户信息 */
const loadUserProfile = async () => {
userProfile.value = await UserAPI.getProfile();

View File

@@ -44,10 +44,10 @@ export const useUserStore = defineStore("user", () => {
});
};
// 微信小程序登录(个人小程序)
// 微信小程序静默登录
const loginByWechatMiniapp = (code: string) => {
return new Promise((resolve, reject) => {
AuthAPI.wechatMiniappLogin(code)
AuthAPI.wechatMiniappSilentLogin(code)
.then((data) => {
if (data.accessToken) {
setAccessToken(data.accessToken);

View File

@@ -1,4 +1,4 @@
/* eslint-disable */
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
@@ -325,4 +325,4 @@ declare module 'vue' {
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
}
}
}

View File

@@ -1,4 +1,4 @@
/* eslint-disable */
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-pages