diff --git a/.env.development b/.env.development index a7153f9..94d18c7 100644 --- a/.env.development +++ b/.env.development @@ -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 \ No newline at end of file diff --git a/components.d.ts b/components.d.ts index e33d7d1..752d737 100644 --- a/components.d.ts +++ b/components.d.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable */ /* prettier-ignore */ // @ts-nocheck // Generated by vite-plugin-uni-components diff --git a/package.json b/package.json index afaf1db..a893965 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "vue-uniapp-template", + "name": "youlai-uniapp-template", "version": "0.0.0", "scripts": { "dev:app": "uni -p app", diff --git a/src/api/auth.ts b/src/api/auth.ts index 144e40c..22c95a9 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -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 { const mobileSafe = encodeURIComponent(mobile); @@ -79,34 +82,46 @@ const AuthAPI = { }, /** - * 微信小程序登录(个人小程序) + * 微信小程序静默登录 + * + * 适用场景:个人小程序 + * - 已绑定手机号的用户:直接返回 token,登录成功 + * - 未绑定手机号的用户:返回 openid,需调用绑定手机号接口 */ - wechatMiniappLogin(code: string): Promise { + wechatMiniappSilentLogin(code: string): Promise { return publicRequest({ - 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 { + const loginCodeSafe = encodeURIComponent(data.loginCode); + const phoneCodeSafe = encodeURIComponent(data.phoneCode); return publicRequest({ - 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 { + const openidSafe = encodeURIComponent(data.openid); + const mobileSafe = encodeURIComponent(data.mobile); + const smsCodeSafe = encodeURIComponent(data.smsCode); return publicRequest({ - 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, }); }, diff --git a/src/api/user.ts b/src/api/user.ts index 91e7332..c8ed57b 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -93,6 +93,14 @@ const UserAPI = { }); }, + /** 解绑第三方账号 */ + unbindSocial(platform: string) { + return request({ + url: `${USER_BASE_URL}/social?platform=${encodeURIComponent(platform)}`, + method: "DELETE", + }); + }, + /** * 发送手机/邮箱验证码 * diff --git a/src/pages.json b/src/pages.json index 30e39e9..929ea5e 100644 --- a/src/pages.json +++ b/src/pages.json @@ -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": [] -} \ No newline at end of file +} diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue index 683062d..dc52d62 100644 --- a/src/pages/login/index.vue +++ b/src/pages/login/index.vue @@ -71,6 +71,11 @@ + + + 演示环境验证码:123456 + + @@ -108,7 +113,7 @@ - + @@ -118,9 +123,12 @@ - - - + @@ -183,6 +191,11 @@ + + + 演示环境验证码:123456 + + @@ -221,7 +234,7 @@ const smsTimer = ref | 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; } diff --git a/src/pages/mine/settings/account/index.vue b/src/pages/mine/settings/account/index.vue index cf86417..3300912 100644 --- a/src/pages/mine/settings/account/index.vue +++ b/src/pages/mine/settings/account/index.vue @@ -21,6 +21,9 @@ is-link @click="handleOpenDialog(DialogType.EMAIL)" /> + + + @@ -211,6 +214,25 @@ const mobileTimer = ref | null>(null); const emailCountdown = ref(0); const emailTimer = ref | 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(); diff --git a/src/store/modules/user-store.ts b/src/store/modules/user-store.ts index ad5701c..e42976c 100644 --- a/src/store/modules/user-store.ts +++ b/src/store/modules/user-store.ts @@ -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); diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts index b90bd02..5496945 100644 --- a/src/types/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable */ /* prettier-ignore */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols @@ -325,4 +325,4 @@ declare module 'vue' { readonly watchPostEffect: UnwrapRef readonly watchSyncEffect: UnwrapRef } -} +} \ No newline at end of file diff --git a/src/types/uni-pages.d.ts b/src/types/uni-pages.d.ts index 03aa1f6..2c41fa3 100644 --- a/src/types/uni-pages.d.ts +++ b/src/types/uni-pages.d.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable */ /* prettier-ignore */ // @ts-nocheck // Generated by vite-plugin-uni-pages