diff --git a/miniprogram/app.json b/miniprogram/app.json index ba16689..8e09715 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -15,7 +15,8 @@ "pages/common/red/red", "pages/live/room/room", "pages/live/h5Room/index", - "pages/live/wxroom/index" + "pages/live/wxroom/index", + "pages/webview-auth/index" ], "subPackages": [ { @@ -52,16 +53,6 @@ "navigationBarTextStyle": "black", "navigationStyle": "custom" }, - "rendererOptions": { - "skyline": { - "defaultDisplayBlock": true, - "defaultContentBox": true, - "tagNameStyleIsolation": "legacy", - "disableABTest": true, - "sdkVersionBegin": "3.0.0", - "sdkVersionEnd": "15.255.255" - } - }, "componentFramework": "glass-easel", "sitemapLocation": "sitemap.json", "lazyCodeLoading": "requiredComponents", diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 26b2e5a..14b4876 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -1,31 +1,42 @@ // app.ts import { startSSE, stopSSE } from './utils/server-sent-events' import { checkUpdate } from './utils/updateManager' + // @ts-ignore const systemInfo = wx.getWindowInfo(); + +// 登录相关 storage key 常量 +const LOGIN_KEYS = ["mbuId", "userName", "openId", "unionId", "addressId", "defualtAddress"] as const; + export { }; + App({ globalData: { - isLogin: false, // 当前是否登录 - wasLogin: false, // 之前是否登录过,用来判断是不是 登录状态失效 + isLogin: false, + wasLogin: false, safeBottom: systemInfo.safeArea.bottom - systemInfo.safeArea.height, safeTop: systemInfo.safeArea.top * 2, store: {}, - joinedArray: [], //加入的频道,加入了就不会重复加入 + joinedArray: [], + template: null, + showStock: false, + tabbarMap: {}, + tabbarItems: [], sseStore: { chat: [], system: [], product: [] }, - isModalShowing: false, //防止有多个登录弹窗打开 + isModalShowing: false, }, async onLaunch(props: any) { - wx.removeStorageSync("state"); + // 处理 state 参数 if (props.query.state) { wx.setStorageSync("state", props.query.state) } else { wx.removeStorageSync("state") } + // 检查小程序版本更新 checkUpdate(); @@ -43,13 +54,8 @@ App({ const ok = openId && unionId && userId; if (!ok) { - // 清理 - wx.removeStorageSync("mbuId"); - wx.removeStorageSync("userName"); - wx.removeStorageSync("openId"); - wx.removeStorageSync("unionId"); - wx.removeStorageSync("addressId"); - wx.removeStorageSync("defualtAddress"); + // 清理登录相关数据 + LOGIN_KEYS.forEach(key => wx.removeStorageSync(key)); } // 保存上一次的登录状态,用来判断是否从 已登录 变成 未登录 @@ -65,9 +71,14 @@ App({ } return true; }, + getPageParams() { + return this.globalData.store; + }, + async getTemplate() { + return this.globalData.template; + }, redirectLogin() { if (!this.checkLoginState()) { - //已有modal在展示,就不展示了 if (this.globalData.isModalShowing) { return false; } @@ -97,4 +108,4 @@ App({ return true; } -}); +}); \ No newline at end of file diff --git a/miniprogram/env.ts b/miniprogram/env.ts index a101fae..701e3f9 100644 --- a/miniprogram/env.ts +++ b/miniprogram/env.ts @@ -3,8 +3,8 @@ const accountInfo = wx.getAccountInfoSync(); const baseURLMap: Record = { develop: "https://rsc.test.rsjk.org.cn", trial: "https://rsc.test.rsjk.org.cn", - release: "https://mp.qbxfu.cn" - // release: "https://rsc.test.rsjk.org.cn" + // release: "https://mp.qbxfu.cn" + release: "https://rsc.test.rsjk.org.cn" } export const BASE_URL = baseURLMap[accountInfo.miniProgram.envVersion] diff --git a/miniprogram/package-live/red-history/red-history.ts b/miniprogram/package-live/red-history/red-history.ts index 568dae5..92dd981 100644 --- a/miniprogram/package-live/red-history/red-history.ts +++ b/miniprogram/package-live/red-history/red-history.ts @@ -1,8 +1,8 @@ import { request } from "../../utils/request"; -import { appId } from "../../env"; import { receiveRedPacket } from "../../utils/util"; const app = getApp(); + const formatDate = (isoString: string): string => { const date = new Date(isoString); const year = date.getFullYear(); @@ -41,6 +41,13 @@ const isRetry = (state: number) => { return FAILED_STATES.includes(state); }; +// 领取后延迟刷新的定时器 ID,用于在切 tab 或页面卸载时取消 +let refreshTimer: ReturnType | null = null; + +// 缓存登录信息,避免每次请求重复读取 storage +let cachedOpenId = wx.getStorageSync("openId"); +let cachedAppId = wx.getStorageSync("appId"); + Page({ data: { list: [] as any[], @@ -60,7 +67,6 @@ Page({ pendingAmount: '0.00', expiredAmount: '0.00', }, - store: {}, safeBottom: app.globalData.safeBottom, safeTop: app.globalData.safeTop, }, @@ -71,6 +77,8 @@ Page({ onReachBottom() { if (this.data.loading || !this.data.hasMore) return; + // 有延迟刷新待执行时,不触发滚动加载,避免重复请求 + if (refreshTimer) return; const nextPage = this.data.current + 1; this.fetchRecords(nextPage, true, this.data.tabs[this.data.activeTab].type); }, @@ -79,15 +87,31 @@ Page({ const index = e.detail.index; const tab = this.data.tabs[index]; this.setData({ activeTab: index }); + // 切换 tab 时取消待执行的延迟刷新,避免用旧 tab 发无效请求 + if (refreshTimer) { + clearTimeout(refreshTimer); + refreshTimer = null; + } this.fetchRecords(1, false, tab.type); }, + onUnload() { + // 页面卸载时取消待执行的延迟刷新 + if (refreshTimer) { + clearTimeout(refreshTimer); + refreshTimer = null; + } + }, + async handleReceive(e: any) { const { id, outrewardsid } = e.currentTarget.dataset; + + // 防止重复点击 + if (this.data.loading) return; + const params: Record = { - appId: appId, - outRewardsId: outrewardsid, - openId: wx.getStorageSync("openId"), + appId: cachedAppId, + openId: cachedOpenId, }; if (outrewardsid) params.outRewardsId = outrewardsid; if (id) params.redId = id; @@ -96,11 +120,11 @@ Page({ await receiveRedPacket( params, '/app/general/receive-red', - appId + cachedAppId ); // 乐观更新:先立即更新本地数据,避免后端状态同步延迟导致展示不友好 - const { list, summary, activeTab, tabs } = this.data; + const { list, summary } = this.data; const targetItem = list.find((item: any) => item.id === id); if (targetItem) { @@ -122,22 +146,21 @@ Page({ this.setData({ list: updatedList, summary: updatedSummary }); } - // 确保后端有足够时间同步,直接刷新 - setTimeout(() => { - this.fetchRecords(1, false, tabs[activeTab].type); + // 确保后端有足够时间同步后刷新,使用实时 activeTab 避免闭包过期 + this.setData({ loading: true }); + refreshTimer = setTimeout(() => { + refreshTimer = null; + this.setData({ loading: false }); + this.fetchRecords(1, false, this.data.tabs[this.data.activeTab].type); }, 3000); } catch (err) { + this.setData({ loading: false }); console.error('领取红包失败:', err); } }, async fetchRecords(page: number, append = false, type: number) { - const openId = wx.getStorageSync("openId"); - - this.setData({ - store: app.globalData.store, - }); - if (!openId) { + if (!cachedOpenId) { wx.showToast({ title: "请先登录", icon: "none" }); return; } @@ -149,20 +172,18 @@ Page({ } try { - const params: Record = {}; - params.openId = openId; - params.appId = appId; - params.current = page; - params.pageSize = 15; - params.type = type; - const res = await request({ options: { url: "/app/saas/query-red", - data: params, + data: { + openId: cachedOpenId, + appId: cachedAppId, + current: page, + pageSize: this.data.pageSize, + type, + }, }, isLoading: !append, - // needLogin: false, }); const records = res.data?.records || res.records || []; @@ -203,4 +224,4 @@ Page({ } } }, -}); +}); \ No newline at end of file diff --git a/miniprogram/package-live/wxroom/index.ts b/miniprogram/package-live/wxroom/index.ts index 582d8c4..5c6a1ad 100644 --- a/miniprogram/package-live/wxroom/index.ts +++ b/miniprogram/package-live/wxroom/index.ts @@ -1,7 +1,6 @@ // package-live/wxroom/index.ts import VolcMiniSdk, { EVENTS } from "../volc-mini-sdk/index"; import { request } from "../../utils/request"; -import { appId } from "../../env"; import { aesDecrypt, aesEncrypt } from "../../utils/crypto"; import { AES_KEY, AES_IV } from "../../env"; import { receiveRedPacket, lockPromise } from "../../utils/util"; @@ -206,6 +205,7 @@ Page({ } }), handleReceive: lockPromise(async function (data: any) { + const appId = wx.getStorageSync("appId"); const params = { appId: appId, outRewardsId: data.redPacketId, diff --git a/miniprogram/pages/live/red-history/red-history.ts b/miniprogram/pages/live/red-history/red-history.ts index 6ab6c3c..87f4cba 100644 --- a/miniprogram/pages/live/red-history/red-history.ts +++ b/miniprogram/pages/live/red-history/red-history.ts @@ -1,4 +1,3 @@ -import { appId } from "../../../env"; import { request } from "../../../utils/request"; import { receiveRedPacket } from "../../../utils/util"; @@ -76,7 +75,7 @@ Page({ async handleReceive(e: any) { const { id } = e.currentTarget.dataset; const { code } = app.globalData.store || {}; - + const appId = wx.getStorageSync("appId"); try { await receiveRedPacket( { appId, code, openId: wx.getStorageSync("openId"), redId: id }, diff --git a/miniprogram/pages/live/report/report.ts b/miniprogram/pages/live/report/report.ts index c4c9960..a3282da 100644 --- a/miniprogram/pages/live/report/report.ts +++ b/miniprogram/pages/live/report/report.ts @@ -1,4 +1,3 @@ -import { appId } from "../../../env"; import { request } from "../../../utils/request"; Page({ @@ -118,7 +117,7 @@ Page({ seeId: wx.getStorageSync("seeId") || '', code: wx.getStorageSync("code") || '', openId: wx.getStorageSync("openId") || '', - appId, + appId: wx.getStorageSync("appId") || '', type: this.data.selectedReason, description: this.data.description, }, diff --git a/miniprogram/pages/live/video/video.scss b/miniprogram/pages/live/video/video.scss index 43bac60..6e3fa10 100644 --- a/miniprogram/pages/live/video/video.scss +++ b/miniprogram/pages/live/video/video.scss @@ -5,6 +5,31 @@ page { height: 100%; } +/* loading 遮罩:未登录时显示,防止页面内容一闪 */ +.fullscreen-loading { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #f9f9f9; + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} +.fullscreen-loading .loading-spinner { + width: 48rpx; + height: 48rpx; + border: 4rpx solid #e0e0e0; + border-top-color: #07c160; + border-radius: 50%; + animation: fullscreen-spin 0.8s linear infinite; +} +@keyframes fullscreen-spin { + to { transform: rotate(360deg); } +} + .scrollarea { height: 90vh; display: flex; diff --git a/miniprogram/pages/live/video/video.ts b/miniprogram/pages/live/video/video.ts index 0f7bf8f..ade1436 100644 --- a/miniprogram/pages/live/video/video.ts +++ b/miniprogram/pages/live/video/video.ts @@ -5,7 +5,7 @@ import { htmlToWxNodes } from "../../../utils/html-to-wx-nodes"; import { request } from "../../../utils/request"; import { version, appversion, appId } from "../../../env"; import { receiveRedPacket } from "../../../utils/util"; - +const app = getApp() const isMobileEnvironment = (): boolean => { const platform = wx.getSystemInfoSync().platform?.toLowerCase() || ''; return platform !== 'windows' && platform !== 'mac'; @@ -24,18 +24,34 @@ Page({ active: 0, canSubmit: false, safeBottom: 0, + initialLoading: false, }, /** * 生命周期函数--监听页面加载 */ async onLoad(options: any) { - const app = getApp(); + const openId = wx.getStorageSync("openId"); + if (!openId) { + // 未登录:保持 loading 遮罩,跳转 web-view 授权 + const code = options.code || ""; + const url = `/pages/webview-auth/index?back=video&code=${code}` + wx.navigateTo({ url }); + return; + } + + // 已登录(含授权返回):隐藏 loading 遮罩,直接进入页面 + this.setData({ initialLoading: true }); + wx.hideShareMenu({ menus: ["shareAppMessage"], // 单独禁用好友分享 }); new UrlParamsHandler(options); - app.globalData.store = options; + // 合并 URL 参数与 globalData.store 中已有的登录信息(来自 webview-auth) + app.globalData.store = { + ...app.globalData.store, + ...options, + }; let safeBottom = 0; //@ts-ignore if (wx.getWindowInfo) { @@ -52,7 +68,7 @@ Page({ } this.setData({ safeBottom: safeBottom, - store: options, + store: app.globalData.store, }); if (!isMobileEnvironment()) { @@ -95,6 +111,9 @@ Page({ * 生命周期函数--监听页面显示 */ onShow() { + const openId = wx.getStorageSync("openId"); + if (!openId) return; + this.setData({ initialLoading: true }); this.search(); }, diff --git a/miniprogram/pages/live/video/video.wxml b/miniprogram/pages/live/video/video.wxml index cee4eb7..a509a05 100644 --- a/miniprogram/pages/live/video/video.wxml +++ b/miniprogram/pages/live/video/video.wxml @@ -1,4 +1,8 @@ + + + + 举报 @@ -36,4 +40,5 @@ - \ No newline at end of file + + diff --git a/miniprogram/pages/login/login.ts b/miniprogram/pages/login/login.ts index 21919e5..4ead099 100644 --- a/miniprogram/pages/login/login.ts +++ b/miniprogram/pages/login/login.ts @@ -3,261 +3,13 @@ import { LoginRes } from "./login.d"; import { wxLogin, wxRequest } from "../../utils/wx-api"; const app = getApp(); -const defaultAvatarUrl = - "https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0"; - -type LoginDisplayConfig = { - showLoginImage: boolean; -}; - -type PartialLoginDisplayConfig = Partial; - -const loginConfigKeys = [ - "login", - "loginConfig", - "loginPage", - "auth", - "authConfig", - "authPage", -]; const WXROOM_REDIRECT_PATH_KEY = "wxroomRedirectPath"; -function isPlainObject(value: unknown): value is Record { - return Object.prototype.toString.call(value) === "[object Object]"; -} - -function hasOwnValue(source: Record, keys: string[]) { - return keys.some((key) => source[key] !== undefined && source[key] !== null); -} - -function normalizeString(value: unknown) { - return typeof value === "string" ? value.trim() : ""; -} - -function normalizeBoolean(value: unknown) { - if (typeof value === "boolean") return value; - if (typeof value === "number") return value > 0; - if (typeof value !== "string") return undefined; - - const normalized = value.trim().toLowerCase(); - if (["1", "true", "yes", "show", "visible", "enabled"].includes(normalized)) { - return true; - } - if (["0", "false", "no", "hide", "hidden", "disabled"].includes(normalized)) { - return false; - } - return undefined; -} - -function normalizeSwitchValue(value: unknown) { - const normalized = normalizeBoolean(value); - if (normalized !== undefined) return normalized; - if (typeof value === "string") return value.trim().length > 0; - if (Array.isArray(value)) return value.length > 0; - if (isPlainObject(value)) { - const innerToggle = getBooleanValue(value, ["show", "visible", "enabled", "value"]); - if (innerToggle !== undefined) return innerToggle; - return !!getStringValue(value, [ - "url", - "src", - "image", - "imageUrl", - "background", - "backgroundImage", - ]); - } - return undefined; -} - -function getObjectValue(source: Record, keys: string[]) { - for (const key of keys) { - const value = source[key]; - if (value !== undefined && value !== null && value !== "") { - return value; - } - } - return undefined; -} - -function getStringValue(source: Record, keys: string[]):string { - const value = getObjectValue(source, keys); - if (value === undefined) return ""; - - if (isPlainObject(value)) { - return getStringValue(value, [ - "url", - "src", - "image", - "imageUrl", - "background", - "backgroundImage", - ]); - } - - return normalizeString(value); -} - -function getBooleanValue(source: Record, keys: string[]) { - const value = getObjectValue(source, keys); - return normalizeBoolean(value); -} - -function pushConfigCandidate(list: Record[], value: unknown) { - if (isPlainObject(value)) { - list.push(value); - } -} - -function collectLoginConfigCandidates(template: unknown) { - const candidates: Record[] = []; - if (!isPlainObject(template)) return candidates; - - if ( - hasOwnValue(template, [ - "showLoginImage", - "showLoginBackground", - "showBackgroundImage", - "showImage", - "imageSwitch", - "backgroundSwitch", - "loginImage", - "heroImage", - "loginBg", - "loginBackground", - "backgroundImage", - "backgroundUrl", - "background", - "image", - ]) - ) { - candidates.push(template); - } - - loginConfigKeys.forEach((key) => { - pushConfigCandidate(candidates, template[key]); - pushConfigCandidate(candidates, template.pageConfig?.[key]); - pushConfigCandidate(candidates, template.pages?.[key]); - pushConfigCandidate(candidates, template.property?.[key]); - }); - - if (Array.isArray(template.pages?.login)) { - template.pages.login.forEach((item: any) => { - pushConfigCandidate(candidates, item); - pushConfigCandidate(candidates, item?.props); - }); - } - - Object.entries(template).forEach(([key, value]) => { - if (!/login|auth/i.test(key)) return; - - if (Array.isArray(value)) { - value.forEach((item) => { - if (!isPlainObject(item)) return; - pushConfigCandidate(candidates, item); - pushConfigCandidate(candidates, item.props); - }); - return; - } - - pushConfigCandidate(candidates, value); - }); - - return candidates; -} - -function readLoginImageSwitch(source: Record) { - const imageValue = getObjectValue(source, [ - "showLoginImage", - "showLoginBackground", - "showBackgroundImage", - "showImage", - "imageSwitch", - "backgroundSwitch", - "loginImage", - "heroImage", - "loginBackground", - "loginBg", - "backgroundImage", - "backgroundUrl", - "background", - "image", - "imageUrl", - ]); - - return normalizeSwitchValue(imageValue); -} - -function resolveLoginDisplayConfigFromSource(source: unknown): PartialLoginDisplayConfig { - if (!isPlainObject(source)) return {}; - - const showLoginImage = readLoginImageSwitch(source); - - return { - ...(showLoginImage !== undefined ? { showLoginImage } : {}), - }; -} - -function mergeLoginDisplayConfig( - current: PartialLoginDisplayConfig, - next: PartialLoginDisplayConfig -) { - return { - ...current, - ...(next.showLoginImage !== undefined ? { showLoginImage: next.showLoginImage } : {}), - }; -} - Page({ data: { back: "", agree: false, - nickFocus: true, - showPopup: false, safeBottom: app.globalData.safeBottom, - showLoginImage: false, - userInfo: { - avatarUrl: defaultAvatarUrl, - nickName: "微信用户", - }, - }, - - applyLoginDisplayConfig(options: Record = {}) { - const template = app.globalData.template as Record | null; - const config = collectLoginConfigCandidates(template).reduce( - (result, item) => - mergeLoginDisplayConfig(result, resolveLoginDisplayConfigFromSource(item)), - {} as PartialLoginDisplayConfig - ); - const optionConfig = resolveLoginDisplayConfigFromSource(options); - const merged = mergeLoginDisplayConfig(config, optionConfig); - - this.setData({ - showLoginImage: merged.showLoginImage ?? false, - }); - }, - - async onChooseAvatar(e: any) { - const { avatarUrl } = e.detail; - wx.uploadFile({ - url: `${BASE_URL}/api/operation/sys-media/upload-oss`, - filePath: avatarUrl, - name: "media", - success: (res: any) => { - const { data } = JSON.parse(res.data); - this.setData({ - userInfo: { - ...this.data.userInfo, - avatarUrl: data, - }, - }); - }, - fail: () => { - wx.showToast({ - title: "上传头像失败", - icon: "none", - }); - }, - }); }, buildQueryString(params: Record): string { @@ -288,56 +40,55 @@ 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; + 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`; + 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) { + 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?code=${code}`; + } } else { - fullPath = `/pages/live/registration/registration?state=${state}`; + 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 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?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(); const currentPage = pages[pages.length - 2]; if (!currentPage) { - // 如果没有上一页,跳转到第一个可用 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; } @@ -382,33 +133,24 @@ Page({ 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 } : {}), - }; - loginResult = await wxRequest({ url: `${BASE_URL}/api/auth/app-login?appId=${appId}&code=${loginRes.code}`, method: "POST", - data: reqData, + data: { + state: wx.getStorageSync("state") ?? undefined, + }, }); } 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?appId=${appId}&code=${loginRes.code}`, method: "POST", - data: reqData, + data: { + state: wx.getStorageSync("code") ?? undefined, + }, }); } @@ -442,10 +184,6 @@ Page({ }); app.globalData.isLogin = true; - this.triggerEvent("loginSuccess", { - userName: resNickName, - avatarUrl: resAvatarUrl, - }); wx.showToast({ title: "登录成功", duration: 2000, @@ -477,12 +215,5 @@ Page({ if (options.back) { this.setData({ back: options.back }); } - this.applyLoginDisplayConfig(options); }, - - onShow() { - setTimeout(() => { - this.setData({ nickFocus: true }); - }, 200); - }, -}); +}); \ No newline at end of file diff --git a/miniprogram/pages/webview-auth/index.json b/miniprogram/pages/webview-auth/index.json new file mode 100644 index 0000000..7911198 --- /dev/null +++ b/miniprogram/pages/webview-auth/index.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "授权登录", + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/webview-auth/index.ts b/miniprogram/pages/webview-auth/index.ts new file mode 100644 index 0000000..811702f --- /dev/null +++ b/miniprogram/pages/webview-auth/index.ts @@ -0,0 +1,74 @@ +import { BASE_URL } from "../../env"; + +Page({ + data: { + webViewUrl: "", + loading: true, + backUrl: "", + code: "", + authCompleted: false, // 防止重复处理 + }, + + onLoad(options: any) { + if (options.back) { + this.setData({ backUrl: decodeURIComponent(options.back) }); + } + const code = options.code || ""; + this.setData({ code }) + + setTimeout(() => { + const authUrl = `${BASE_URL}/h5/oauth/auth-success.html?entryCode=${encodeURIComponent(code)}` + this.setData({ + webViewUrl: authUrl, + loading: true, + }); + }, 50); + }, + + + /** web-view 首次加载完成,隐藏骨架屏 */ + onWebViewLoad() { + this.setData({ loading: false }); + }, + + + onMessage(e: any) { + + const dataList = e.detail?.data || []; + console.log("onMessage===========》", dataList); + if (dataList.length === 0) return; + + const data = dataList.reduce((acc: any, curr: any) => ({ ...acc, ...curr }), {}); +console.log("onMessage", data); + // 先存储缓存,再操作页面状态 + const fields: Record = { + openId: data.openId || '', + unionId: data.unionId || '', + appId: data.appId || '', + userName: data.nickname || '微信用户', + avatarUrl: data.avatar || '', + code: data.code || '', + userId: data.userId || '', + }; + + Object.entries(fields).forEach(([key, value]) => { + if (value) { + try { + wx.setStorageSync(key, value); + } catch (err: any) { + console.log("缓存写入失败 key:", key, "error:", err?.message || err); + } + } + }); + + getApp().globalData.isLogin = true; + + getApp().globalData.store = { + ...getApp().globalData.store, + ...fields, + }; + + try { this.setData({ authCompleted: true }); } catch (_) { } + }, + +}); diff --git a/miniprogram/pages/webview-auth/index.wxml b/miniprogram/pages/webview-auth/index.wxml new file mode 100644 index 0000000..56e2896 --- /dev/null +++ b/miniprogram/pages/webview-auth/index.wxml @@ -0,0 +1,13 @@ + + + + + 正在加载授权页面... + + + diff --git a/miniprogram/pages/webview-auth/index.wxss b/miniprogram/pages/webview-auth/index.wxss new file mode 100644 index 0000000..37528cd --- /dev/null +++ b/miniprogram/pages/webview-auth/index.wxss @@ -0,0 +1,61 @@ +/* pages/webview-auth/index.wxss */ +.container { + width: 100%; + height: 100vh; + overflow: hidden; + position: relative; +} + +.loading { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #fff; + z-index: 10; +} + +.loading-spinner { + width: 40rpx; + height: 40rpx; + border: 4rpx solid #e5e5e5; + border-top-color: #07c160; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +.loading-text { + margin-top: 20rpx; + font-size: 28rpx; + color: #999; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.cancel-btn { + position: fixed; + bottom: 40rpx; + left: 50%; + transform: translateX(-50%); + width: 300rpx; + height: 72rpx; + line-height: 72rpx; + text-align: center; + background: #e64340; + color: #fff; + font-size: 28rpx; + border-radius: 36rpx; + z-index: 999; + box-shadow: 0 4rpx 12rpx rgba(230, 67, 64, 0.3); +} + +.cancel-btn:active { + opacity: 0.8; +} diff --git a/miniprogram/utils/request.ts b/miniprogram/utils/request.ts index 63dcb84..c8f56ba 100644 --- a/miniprogram/utils/request.ts +++ b/miniprogram/utils/request.ts @@ -1,4 +1,4 @@ -import { BASE_URL, appId } from "../env"; +import { BASE_URL } from "../env"; import { base64Encode } from "./crypto"; type RequestProps = { options: WechatMiniprogram.RequestOption; @@ -23,6 +23,7 @@ export const request = ( return Promise.reject(new Error('请先登录')); } const openId = wx.getStorageSync("openId") || ""; + const appId = wx.getStorageSync("appId") || ""; header.Authorization = `Basic ${base64Encode(`${openId}:${appId}`)}` } diff --git a/project.config.json b/project.config.json index 84f3a92..46fb70f 100644 --- a/project.config.json +++ b/project.config.json @@ -52,5 +52,5 @@ "ignore": [], "include": [] }, - "appid": "wx2bf5f8e9decdceaf" + "appid": "wx53f37c2f7e3a147e" } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..16c8541 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "strictNullChecks": true, + "noImplicitAny": true, + "module": "CommonJS", + "target": "ES2020", + "allowJs": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "alwaysStrict": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strict": true, + "strictPropertyInitialization": true, + "ignoreDeprecations": "6.0", + "baseUrl": ".", + "types": ["miniprogram-api-typings"], + "paths": { + "@vant/weapp/*": ["path/to/node_modules/@vant/weapp/dist/*"] + }, + "lib": ["ES2020"], + "typeRoots": ["./typings"], + "rootDir": "./miniprogram", + "outDir": "./dist" + }, + "include": ["./**/*.ts"], + "exclude": ["node_modules", "dist"] +}