diff --git a/miniprogram/app.ts b/miniprogram/app.ts index e180283..d8edd71 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -76,21 +76,16 @@ App({ }, checkLoginState() { const openId = wx.getStorageSync('openId'); - const unionId = wx.getStorageSync('unionId'); const userId = wx.getStorageSync('userId'); - - const isLoggedIn = !!(openId && unionId && userId); - + const isLoggedIn = !!(openId && userId); if (!isLoggedIn) { // 清理登录相关缓存 LOGIN_STORAGE_KEYS.forEach(key => wx.removeStorageSync(key)); } - // 记录上一次登录状态,用于判断是否从已登录变为未登录 const prev = this.globalData.isLogin; this.globalData.isLogin = isLoggedIn; this.globalData.wasLogin = prev; - return isLoggedIn; }, login(value: string) { diff --git a/miniprogram/pages/live/video/video.ts b/miniprogram/pages/live/video/video.ts index 0bb4758..92b92ca 100644 --- a/miniprogram/pages/live/video/video.ts +++ b/miniprogram/pages/live/video/video.ts @@ -62,7 +62,8 @@ Page({ ); return; } - + const bool = await app.login("video"); + if (!bool) return; // 开启防截屏(仅限当前页面生命周期内) //@ts-ignore wx.setVisualEffectOnCapture({ @@ -306,68 +307,61 @@ Page({ return; } - const { pass } = res.data || {}; + const { pass, redPackage } = res.data || {}; if (pass) { + wx.showModal({ + content: "🎉 领取红包!", + showCancel: false, + confirmText: "立即领取", + confirmColor: "#FF0000", + success: async (res) => { + if (res.confirm) { + if (redPackage?.packageInfo) { + try { + wx.showLoading({ title: "领取中", mask: true }); + if (wx.canIUse?.("requestMerchantTransfer")) { + await new Promise((resolve, reject) => { + // @ts-ignore + wx.requestMerchantTransfer({ + mchId: redPackage.mchId, + appId, + package: redPackage.packageInfo, + success: () => { + wx.showToast({ title: "领取成功", icon: "success" }); + resolve(); + }, + fail: (err: any) => { + console.error("拉起转账失败:", err); + wx.showToast({ title: "领取失败", icon: "none" }); + reject(new Error(err?.errMsg || "领取失败")); + }, + }); + }); + } else { + wx.showModal({ + content: "你的微信版本过低,请更新至最新版本。", + showCancel: false, + }); + throw new Error("微信版本过低"); + } + } catch (err: any) { + wx.hideLoading(); + const msg = err?.data?.msg || err?.errMsg || err?.message || "领取失败"; + wx.showToast({ title: msg, icon: "none" }); + } + } else { + await this.handleReceive(redPackage.redId); + } + } + }, + }); + } else { wx.showToast({ - title: "提交成功", - icon: "success", - duration: 2000, + title: "回答错误,您与红包擦肩而过", + icon: "none", + duration: 6000, }); } - // if (pass) { - // wx.showModal({ - // content: "🎉 领取红包!", - // showCancel: false, - // confirmText: "立即领取", - // confirmColor: "#FF0000", - // success: async (res) => { - // if (res.confirm) { - // if (redPackage?.packageInfo) { - // try { - // wx.showLoading({ title: "领取中", mask: true }); - // if (wx.canIUse?.("requestMerchantTransfer")) { - // await new Promise((resolve, reject) => { - // // @ts-ignore - // wx.requestMerchantTransfer({ - // mchId: redPackage.mchId, - // appId, - // package: redPackage.packageInfo, - // success: () => { - // wx.showToast({ title: "领取成功", icon: "success" }); - // resolve(); - // }, - // fail: (err: any) => { - // console.error("拉起转账失败:", err); - // wx.showToast({ title: "领取失败", icon: "none" }); - // reject(new Error(err?.errMsg || "领取失败")); - // }, - // }); - // }); - // } else { - // wx.showModal({ - // content: "你的微信版本过低,请更新至最新版本。", - // showCancel: false, - // }); - // throw new Error("微信版本过低"); - // } - // } catch (err: any) { - // wx.hideLoading(); - // const msg = err?.data?.msg || err?.errMsg || err?.message || "领取失败"; - // wx.showToast({ title: msg, icon: "none" }); - // } - // } else { - // await this.handleReceive(redPackage.redId); - // } - // } - // }, - // }); - // } else { - // wx.showToast({ - // title: "回答错误,您与红包擦肩而过", - // icon: "none", - // duration: 6000, - // }); - // } }, async handleReceive(id: any) { const { code } = this.data.store as Record; diff --git a/miniprogram/pages/login/login.scss b/miniprogram/pages/login/login.scss index 8cb30c8..7f14236 100644 --- a/miniprogram/pages/login/login.scss +++ b/miniprogram/pages/login/login.scss @@ -132,3 +132,109 @@ line-height: 34rpx; margin-left: 4rpx; } +/* ============================================ + 弹窗 — 设置头像和昵称 + ============================================ */ + + .popup-title { + display: block; + font-size: 35rpx; + font-weight: 600; + color: #000; + text-align: center; + margin-bottom: 10rpx; + } + + .popup-content { + display: flex; + flex-direction: column; + padding: 40rpx; + + // 左右布局:左头像 + 右按钮组 + .form-row { + display: flex; + align-items: center; + gap: 36rpx; + margin-bottom: 50rpx; + + // 左侧:头像选择器 + .avatar-picker { + flex-shrink: 0; + width: 140rpx; + height: 140rpx; + padding: 0; + background: transparent; + border: none; + display: flex; + align-items: center; + justify-content: center; + &::after { + border: none; + } + + .popup-avatar { + width: 140rpx; + height: 140rpx; + border-radius: 50%; + border: 3rpx solid $primary-color; + } + } + + // 右侧:按钮组(上下排列) + .btn-group { + flex: 1; + display: flex; + flex-direction: column; + gap: 20rpx; + + .form-btn { + width: 100%; + height: 72rpx; + line-height: 72rpx; + font-size: 26rpx; + color: #fff; + background: linear-gradient(90deg, #005bea 0%, #00c6fb 99%); + border-radius: 36rpx; + border: none; + &::after { + border: none; + } + + } + + // 昵称输入:外观伪装成按钮,click 唤起微信昵称弹层 + .form-input--nickname { + width: 100%; + height: 72rpx; + line-height: 72rpx; + font-size: 26rpx; + color: $primary-color; + background: transparent; + border: 2rpx solid $primary-color; + border-radius: 36rpx; + text-align: center; + padding: 0 20rpx; + box-sizing: border-box; + } + } + } + + // 提交按钮 + .form-submit { + width: 100%; + + .submit-btn { + width: 100%; + height: 96rpx; + line-height: 96rpx; + background: linear-gradient(90deg, #005bea 0%, #00c6fb 99%); + color: #fff; + font-size: 34rpx; + border-radius: 48rpx; + border: none; + &::after { + border: none; + } + } + } + } diff --git a/miniprogram/pages/login/login.ts b/miniprogram/pages/login/login.ts index 21919e5..e80eb02 100644 --- a/miniprogram/pages/login/login.ts +++ b/miniprogram/pages/login/login.ts @@ -79,7 +79,7 @@ function getObjectValue(source: Record, keys: string[]) { return undefined; } -function getStringValue(source: Record, keys: string[]):string { +function getStringValue(source: Record, keys: string[]): string { const value = getObjectValue(source, keys); if (value === undefined) return ""; @@ -213,12 +213,10 @@ Page({ agree: false, nickFocus: true, showPopup: false, + avatarUrl: defaultAvatarUrl, + nickname: '', safeBottom: app.globalData.safeBottom, showLoginImage: false, - userInfo: { - avatarUrl: defaultAvatarUrl, - nickName: "微信用户", - }, }, applyLoginDisplayConfig(options: Record = {}) { @@ -245,10 +243,7 @@ Page({ success: (res: any) => { const { data } = JSON.parse(res.data); this.setData({ - userInfo: { - ...this.data.userInfo, - avatarUrl: data, - }, + avatarUrl: data, }); }, fail: () => { @@ -275,7 +270,6 @@ Page({ const tabPages = [ "/pages/tab-bar/medicine-box/index", "/pages/tab-bar/shopping-cart/shopping-cart", - "/pages/tab-bar/course/course", "/pages/tab-bar/user/user", "/pages/tab-bar/points-mall/points-mall", "/pages/tab-bar/category/category", @@ -288,24 +282,8 @@ Page({ "/package-live/wxroom/index"; wx.removeStorageSync(WXROOM_REDIRECT_PATH_KEY); } else { - const store = app.globalData.store; - const state = wx.getStorageSync("state") ?? undefined; - const code = wx.getStorageSync("code") ?? undefined; - const groupId = wx.getStorageSync("groupId") ?? undefined; - - if (state && !code) { - if (self.data.back === "video") { - const storeParams = - typeof store === "string" ? store : this.buildQueryString(store); - fullPath = `/pages/live/video/video?${storeParams}`; - } else if (self.data.back === "red") { - fullPath = `/pages/common/red/red`; - } else if (self.data.back === "user") { - fullPath = `/pages/tab-bar/user/user`; - } else { - fullPath = `/pages/live/registration/registration?state=${state}`; - } - } else if (!state && code) { + const store = app.globalData.store; + const code = wx.getStorageSync("code") ?? undefined; if (self.data.back === "video") { const storeParams = typeof store === "string" ? store : this.buildQueryString(store); @@ -317,15 +295,7 @@ Page({ } else { fullPath = `/pages/live/registration/registration?code=${code}`; } - } else { - if (self.data.back === "room") { - fullPath = `/pages/live/room/room?groupId=${groupId}`; - } else if (self.data.back === "red") { - fullPath = `/pages/common/red/red`; - } else if (self.data.back === "user") { - fullPath = `/pages/tab-bar/user/user`; - } - } + } } else { const pages = getCurrentPages(); @@ -335,9 +305,9 @@ Page({ // 如果没有上一页,跳转到第一个可用 tab,而不是硬编码的 index const tabbarItems = app.globalData.tabbarItems; if (tabbarItems && tabbarItems.length > 0) { - wx.switchTab({ url: tabbarItems[0].pagePath }); + wx.switchTab({ url: tabbarItems[0].pagePath }); } else { - wx.switchTab({ url: "/pages/tab-bar/medicine-box/index" }); + wx.switchTab({ url: "/pages/tab-bar/medicine-box/index" }); } return; } @@ -376,65 +346,70 @@ Page({ }); if (!this.data.agree) return; } + this.setData({ + showPopup: true + }) + }, + closePopup() { + this.setData({ + showPopup: false + }) + }, + + + // 获取昵称(微信系统填入后失焦触发 bindblur) + onNicknameInput(e: any) { + const { value } = e.detail; + this.setData({ + nickname: value + }); + }, + // 提交登录 + async submitLogin() { + const { avatarUrl, nickname } = this.data; + if (!avatarUrl) { + wx.showToast({ + title: '请选择头像', + icon: 'none' + }); + return; + } + if (!nickname) { + wx.showToast({ + title: '请选择昵称', + icon: 'none' + }); + return; + } try { wx.showLoading({ title: "登录中" }); const loginRes = await wxLogin(); if (!loginRes.code) throw new Error(loginRes.errMsg); - const { avatarUrl, nickName } = this.data.userInfo; - const state = wx.getStorageSync("state"); let loginResult: LoginRes; - if (state) { - const reqData: Record = { - state: wx.getStorageSync("state") ?? undefined, - ...(nickName && nickName !== "微信用户" ? { nickName } : {}), - ...(avatarUrl && avatarUrl !== defaultAvatarUrl ? { avatarUrl } : {}), - }; + const reqData: Record = { + state: loginRes.code, + avatarUrl, + nickName: nickname + }; - loginResult = await wxRequest({ - url: `${BASE_URL}/api/auth/app-login?appId=${appId}&code=${loginRes.code}`, - method: "POST", - data: reqData, - }); - } else { - const reqData: Record = { - state: wx.getStorageSync("code") ?? undefined, - ...(nickName && nickName !== "微信用户" ? { nickName } : {}), - ...(avatarUrl && avatarUrl !== defaultAvatarUrl ? { avatarUrl } : {}), - }; + loginResult = await wxRequest({ + url: `${BASE_URL}/api/auth/app-auth-new?appId=${appId}&code=${loginRes.code}`, + method: "POST", + data: reqData, + }); - loginResult = await wxRequest({ - url: `${BASE_URL}/api/auth/app-auth?appId=${appId}&code=${loginRes.code}`, - method: "POST", - data: reqData, - }); - } - - if ( - loginResult?.statusCode !== 200 || - (loginResult.data.code && loginResult.data.code !== 200) - ) { + if (loginResult?.statusCode !== 200) { throw new Error(loginResult?.data?.msg || "登录失败"); } - const { - mpUserId, - id, - openId, - unionId, - nickName: resNickName, - avatarUrl: resAvatarUrl, - } = loginResult.data; - const storageData = { - openId, - userId: mpUserId, - mbuId: id, - unionId, - userName: resNickName, - avatarUrl: resAvatarUrl, + openId: loginResult.data.openId, + userId: loginResult.data.mpUserId, + userName: loginResult.data.nickName, + avatarUrl: loginResult.data.avatarUrl, }; Object.entries(storageData).forEach(([key, value]) => { @@ -442,18 +417,15 @@ Page({ }); app.globalData.isLogin = true; - this.triggerEvent("loginSuccess", { - userName: resNickName, - avatarUrl: resAvatarUrl, - }); + wx.showToast({ title: "登录成功", duration: 2000, }); + // 跳转到看课页面 this.redirectUrl(); } catch (err: any) { - console.log("登录失败:", err); app.globalData.isLogin = false; wx.showToast({ title: `登录失败:${err.message || err.errMsg}`, @@ -462,8 +434,10 @@ Page({ } finally { setTimeout(() => wx.hideToast(), 3000); } - }, + // 关闭弹窗 + this.closePopup(); + }, handleToggleAgree() { this.setData({ agree: !this.data.agree }); }, diff --git a/miniprogram/pages/login/login.wxml b/miniprogram/pages/login/login.wxml index d8cede0..ea1ed46 100644 --- a/miniprogram/pages/login/login.wxml +++ b/miniprogram/pages/login/login.wxml @@ -24,3 +24,32 @@ + + + + 手动设置头像和昵称 + + + + + + + + + + + + + + + + + + diff --git a/miniprogram/utils/request.ts b/miniprogram/utils/request.ts index 38bb459..6377a08 100644 --- a/miniprogram/utils/request.ts +++ b/miniprogram/utils/request.ts @@ -1,4 +1,5 @@ -import { BASE_URL } from "../env"; +import { BASE_URL, appId } from "../env"; +import { base64Encode } from "./crypto"; type RequestProps = { options: WechatMiniprogram.RequestOption; loadingTitle?: string; @@ -6,27 +7,23 @@ type RequestProps = { needLogin?: boolean; }; -// 生成设备指纹字符串 -const generateFingerprint = (): string => { - const info = wx.getSystemInfoSync(); - return [ - String(info.system), - String(info.platform), - String(info.SDKVersion), - String(info.screenWidth), - String(info.screenHeight), - ].join("&"); -}; - +// 每次请求动态生成 header,确保使用最新的 openId +const fingerprint = wx.getSystemInfoSync(); export const request = ( - { options, loadingTitle = "加载中", isLoading = true }: RequestProps + { options, loadingTitle = "加载中", isLoading = true, needLogin = true }: RequestProps ): Promise => { - + const app = getApp(); const header: any = { - "X-Fingerprint": generateFingerprint(), + "X-Fingerprint": fingerprint, }; - + if (needLogin) { + if (!app.checkLoginState()) { + return Promise.reject(new Error('请先登录')); + } + const openId = wx.getStorageSync("openId") || ""; + header.Authorization = `Basic ${base64Encode(`${openId}:${appId}`)}` + } return new Promise((resolve, reject) => { let showToast = false; @@ -47,13 +44,27 @@ export const request = ( if (!res.data.code || res.data.code === 200) { resolve(res.data as T); } else if (res.data.code === 401) { + // 保存当前页面路径,登录后跳转回来 + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + if (currentPage) { + const route = `/${currentPage.route}`; + const options = currentPage.options || {}; + const query = Object.entries(options) + .filter(([, value]) => value !== undefined && value !== null) + .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`) + .join("&"); + const fullPath = query ? `${route}?${query}` : route; + wx.setStorageSync("redirectPath", fullPath); + } wx.showModal({ title: "登录提示", content: "请先登录", confirmText: "去登录", success: (options) => { if (options.confirm) { - wx.navigateTo({ url: "/pages/login/login" }); + // 使用 redirectTo 替换当前页,登录页无返回按钮 + wx.redirectTo({ url: "/pages/login/login" }); } } });