这版是公共号授权版

This commit is contained in:
cao123
2026-08-12 11:47:51 +08:00
parent d0776f9676
commit e47df01af9
18 changed files with 365 additions and 378 deletions
+2 -11
View File
@@ -15,7 +15,8 @@
"pages/common/red/red", "pages/common/red/red",
"pages/live/room/room", "pages/live/room/room",
"pages/live/h5Room/index", "pages/live/h5Room/index",
"pages/live/wxroom/index" "pages/live/wxroom/index",
"pages/webview-auth/index"
], ],
"subPackages": [ "subPackages": [
{ {
@@ -52,16 +53,6 @@
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationStyle": "custom" "navigationStyle": "custom"
}, },
"rendererOptions": {
"skyline": {
"defaultDisplayBlock": true,
"defaultContentBox": true,
"tagNameStyleIsolation": "legacy",
"disableABTest": true,
"sdkVersionBegin": "3.0.0",
"sdkVersionEnd": "15.255.255"
}
},
"componentFramework": "glass-easel", "componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json", "sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents", "lazyCodeLoading": "requiredComponents",
+24 -13
View File
@@ -1,31 +1,42 @@
// app.ts // app.ts
import { startSSE, stopSSE } from './utils/server-sent-events' import { startSSE, stopSSE } from './utils/server-sent-events'
import { checkUpdate } from './utils/updateManager' import { checkUpdate } from './utils/updateManager'
// @ts-ignore // @ts-ignore
const systemInfo = wx.getWindowInfo(); const systemInfo = wx.getWindowInfo();
// 登录相关 storage key 常量
const LOGIN_KEYS = ["mbuId", "userName", "openId", "unionId", "addressId", "defualtAddress"] as const;
export { }; export { };
App<IAppOption>({ App<IAppOption>({
globalData: { globalData: {
isLogin: false, // 当前是否登录 isLogin: false,
wasLogin: false, // 之前是否登录过,用来判断是不是 登录状态失效 wasLogin: false,
safeBottom: systemInfo.safeArea.bottom - systemInfo.safeArea.height, safeBottom: systemInfo.safeArea.bottom - systemInfo.safeArea.height,
safeTop: systemInfo.safeArea.top * 2, safeTop: systemInfo.safeArea.top * 2,
store: {}, store: {},
joinedArray: [], //加入的频道,加入了就不会重复加入 joinedArray: [],
template: null,
showStock: false,
tabbarMap: {},
tabbarItems: [],
sseStore: { sseStore: {
chat: [], chat: [],
system: [], system: [],
product: [] product: []
}, },
isModalShowing: false, //防止有多个登录弹窗打开 isModalShowing: false,
}, },
async onLaunch(props: any) { async onLaunch(props: any) {
wx.removeStorageSync("state"); // 处理 state 参数
if (props.query.state) { if (props.query.state) {
wx.setStorageSync("state", props.query.state) wx.setStorageSync("state", props.query.state)
} else { } else {
wx.removeStorageSync("state") wx.removeStorageSync("state")
} }
// 检查小程序版本更新 // 检查小程序版本更新
checkUpdate(); checkUpdate();
@@ -43,13 +54,8 @@ App<IAppOption>({
const ok = openId && unionId && userId; const ok = openId && unionId && userId;
if (!ok) { if (!ok) {
// 清理 // 清理登录相关数据
wx.removeStorageSync("mbuId"); LOGIN_KEYS.forEach(key => wx.removeStorageSync(key));
wx.removeStorageSync("userName");
wx.removeStorageSync("openId");
wx.removeStorageSync("unionId");
wx.removeStorageSync("addressId");
wx.removeStorageSync("defualtAddress");
} }
// 保存上一次的登录状态,用来判断是否从 已登录 变成 未登录 // 保存上一次的登录状态,用来判断是否从 已登录 变成 未登录
@@ -65,9 +71,14 @@ App<IAppOption>({
} }
return true; return true;
}, },
getPageParams() {
return this.globalData.store;
},
async getTemplate() {
return this.globalData.template;
},
redirectLogin() { redirectLogin() {
if (!this.checkLoginState()) { if (!this.checkLoginState()) {
//已有modal在展示,就不展示了
if (this.globalData.isModalShowing) { if (this.globalData.isModalShowing) {
return false; return false;
} }
+2 -2
View File
@@ -3,8 +3,8 @@ const accountInfo = wx.getAccountInfoSync();
const baseURLMap: Record<string, string> = { const baseURLMap: Record<string, string> = {
develop: "https://rsc.test.rsjk.org.cn", develop: "https://rsc.test.rsjk.org.cn",
trial: "https://rsc.test.rsjk.org.cn", trial: "https://rsc.test.rsjk.org.cn",
release: "https://mp.qbxfu.cn" // release: "https://mp.qbxfu.cn"
// release: "https://rsc.test.rsjk.org.cn" release: "https://rsc.test.rsjk.org.cn"
} }
export const BASE_URL = baseURLMap[accountInfo.miniProgram.envVersion] export const BASE_URL = baseURLMap[accountInfo.miniProgram.envVersion]
@@ -1,8 +1,8 @@
import { request } from "../../utils/request"; import { request } from "../../utils/request";
import { appId } from "../../env";
import { receiveRedPacket } from "../../utils/util"; import { receiveRedPacket } from "../../utils/util";
const app = getApp<IAppOption>(); const app = getApp<IAppOption>();
const formatDate = (isoString: string): string => { const formatDate = (isoString: string): string => {
const date = new Date(isoString); const date = new Date(isoString);
const year = date.getFullYear(); const year = date.getFullYear();
@@ -41,6 +41,13 @@ const isRetry = (state: number) => {
return FAILED_STATES.includes(state); return FAILED_STATES.includes(state);
}; };
// 领取后延迟刷新的定时器 ID,用于在切 tab 或页面卸载时取消
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
// 缓存登录信息,避免每次请求重复读取 storage
let cachedOpenId = wx.getStorageSync("openId");
let cachedAppId = wx.getStorageSync("appId");
Page({ Page({
data: { data: {
list: [] as any[], list: [] as any[],
@@ -60,7 +67,6 @@ Page({
pendingAmount: '0.00', pendingAmount: '0.00',
expiredAmount: '0.00', expiredAmount: '0.00',
}, },
store: {},
safeBottom: app.globalData.safeBottom, safeBottom: app.globalData.safeBottom,
safeTop: app.globalData.safeTop, safeTop: app.globalData.safeTop,
}, },
@@ -71,6 +77,8 @@ Page({
onReachBottom() { onReachBottom() {
if (this.data.loading || !this.data.hasMore) return; if (this.data.loading || !this.data.hasMore) return;
// 有延迟刷新待执行时,不触发滚动加载,避免重复请求
if (refreshTimer) return;
const nextPage = this.data.current + 1; const nextPage = this.data.current + 1;
this.fetchRecords(nextPage, true, this.data.tabs[this.data.activeTab].type); this.fetchRecords(nextPage, true, this.data.tabs[this.data.activeTab].type);
}, },
@@ -79,15 +87,31 @@ Page({
const index = e.detail.index; const index = e.detail.index;
const tab = this.data.tabs[index]; const tab = this.data.tabs[index];
this.setData({ activeTab: index }); this.setData({ activeTab: index });
// 切换 tab 时取消待执行的延迟刷新,避免用旧 tab 发无效请求
if (refreshTimer) {
clearTimeout(refreshTimer);
refreshTimer = null;
}
this.fetchRecords(1, false, tab.type); this.fetchRecords(1, false, tab.type);
}, },
onUnload() {
// 页面卸载时取消待执行的延迟刷新
if (refreshTimer) {
clearTimeout(refreshTimer);
refreshTimer = null;
}
},
async handleReceive(e: any) { async handleReceive(e: any) {
const { id, outrewardsid } = e.currentTarget.dataset; const { id, outrewardsid } = e.currentTarget.dataset;
// 防止重复点击
if (this.data.loading) return;
const params: Record<string, any> = { const params: Record<string, any> = {
appId: appId, appId: cachedAppId,
outRewardsId: outrewardsid, openId: cachedOpenId,
openId: wx.getStorageSync("openId"),
}; };
if (outrewardsid) params.outRewardsId = outrewardsid; if (outrewardsid) params.outRewardsId = outrewardsid;
if (id) params.redId = id; if (id) params.redId = id;
@@ -96,11 +120,11 @@ Page({
await receiveRedPacket( await receiveRedPacket(
params, params,
'/app/general/receive-red', '/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); const targetItem = list.find((item: any) => item.id === id);
if (targetItem) { if (targetItem) {
@@ -122,22 +146,21 @@ Page({
this.setData({ list: updatedList, summary: updatedSummary }); this.setData({ list: updatedList, summary: updatedSummary });
} }
// 确保后端有足够时间同步,直接刷新 // 确保后端有足够时间同步后刷新,使用实时 activeTab 避免闭包过期
setTimeout(() => { this.setData({ loading: true });
this.fetchRecords(1, false, tabs[activeTab].type); refreshTimer = setTimeout(() => {
refreshTimer = null;
this.setData({ loading: false });
this.fetchRecords(1, false, this.data.tabs[this.data.activeTab].type);
}, 3000); }, 3000);
} catch (err) { } catch (err) {
this.setData({ loading: false });
console.error('领取红包失败:', err); console.error('领取红包失败:', err);
} }
}, },
async fetchRecords(page: number, append = false, type: number) { async fetchRecords(page: number, append = false, type: number) {
const openId = wx.getStorageSync("openId"); if (!cachedOpenId) {
this.setData({
store: app.globalData.store,
});
if (!openId) {
wx.showToast({ title: "请先登录", icon: "none" }); wx.showToast({ title: "请先登录", icon: "none" });
return; return;
} }
@@ -149,20 +172,18 @@ Page({
} }
try { try {
const params: Record<string, any> = {};
params.openId = openId;
params.appId = appId;
params.current = page;
params.pageSize = 15;
params.type = type;
const res = await request({ const res = await request({
options: { options: {
url: "/app/saas/query-red", url: "/app/saas/query-red",
data: params, data: {
openId: cachedOpenId,
appId: cachedAppId,
current: page,
pageSize: this.data.pageSize,
type,
},
}, },
isLoading: !append, isLoading: !append,
// needLogin: false,
}); });
const records = res.data?.records || res.records || []; const records = res.data?.records || res.records || [];
+1 -1
View File
@@ -1,7 +1,6 @@
// package-live/wxroom/index.ts // package-live/wxroom/index.ts
import VolcMiniSdk, { EVENTS } from "../volc-mini-sdk/index"; import VolcMiniSdk, { EVENTS } from "../volc-mini-sdk/index";
import { request } from "../../utils/request"; import { request } from "../../utils/request";
import { appId } from "../../env";
import { aesDecrypt, aesEncrypt } from "../../utils/crypto"; import { aesDecrypt, aesEncrypt } from "../../utils/crypto";
import { AES_KEY, AES_IV } from "../../env"; import { AES_KEY, AES_IV } from "../../env";
import { receiveRedPacket, lockPromise } from "../../utils/util"; import { receiveRedPacket, lockPromise } from "../../utils/util";
@@ -206,6 +205,7 @@ Page({
} }
}), }),
handleReceive: lockPromise(async function (data: any) { handleReceive: lockPromise(async function (data: any) {
const appId = wx.getStorageSync("appId");
const params = { const params = {
appId: appId, appId: appId,
outRewardsId: data.redPacketId, outRewardsId: data.redPacketId,
@@ -1,4 +1,3 @@
import { appId } from "../../../env";
import { request } from "../../../utils/request"; import { request } from "../../../utils/request";
import { receiveRedPacket } from "../../../utils/util"; import { receiveRedPacket } from "../../../utils/util";
@@ -76,7 +75,7 @@ Page({
async handleReceive(e: any) { async handleReceive(e: any) {
const { id } = e.currentTarget.dataset; const { id } = e.currentTarget.dataset;
const { code } = app.globalData.store || {}; const { code } = app.globalData.store || {};
const appId = wx.getStorageSync("appId");
try { try {
await receiveRedPacket( await receiveRedPacket(
{ appId, code, openId: wx.getStorageSync("openId"), redId: id }, { appId, code, openId: wx.getStorageSync("openId"), redId: id },
+1 -2
View File
@@ -1,4 +1,3 @@
import { appId } from "../../../env";
import { request } from "../../../utils/request"; import { request } from "../../../utils/request";
Page({ Page({
@@ -118,7 +117,7 @@ Page({
seeId: wx.getStorageSync("seeId") || '', seeId: wx.getStorageSync("seeId") || '',
code: wx.getStorageSync("code") || '', code: wx.getStorageSync("code") || '',
openId: wx.getStorageSync("openId") || '', openId: wx.getStorageSync("openId") || '',
appId, appId: wx.getStorageSync("appId") || '',
type: this.data.selectedReason, type: this.data.selectedReason,
description: this.data.description, description: this.data.description,
}, },
+25
View File
@@ -5,6 +5,31 @@ page {
height: 100%; 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 { .scrollarea {
height: 90vh; height: 90vh;
display: flex; display: flex;
+23 -4
View File
@@ -5,7 +5,7 @@ import { htmlToWxNodes } from "../../../utils/html-to-wx-nodes";
import { request } from "../../../utils/request"; import { request } from "../../../utils/request";
import { version, appversion, appId } from "../../../env"; import { version, appversion, appId } from "../../../env";
import { receiveRedPacket } from "../../../utils/util"; import { receiveRedPacket } from "../../../utils/util";
const app = getApp<IAppOption>()
const isMobileEnvironment = (): boolean => { const isMobileEnvironment = (): boolean => {
const platform = wx.getSystemInfoSync().platform?.toLowerCase() || ''; const platform = wx.getSystemInfoSync().platform?.toLowerCase() || '';
return platform !== 'windows' && platform !== 'mac'; return platform !== 'windows' && platform !== 'mac';
@@ -24,18 +24,34 @@ Page({
active: 0, active: 0,
canSubmit: false, canSubmit: false,
safeBottom: 0, safeBottom: 0,
initialLoading: false,
}, },
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
async onLoad(options: any) { async onLoad(options: any) {
const app = getApp<IAppOption>(); 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({ wx.hideShareMenu({
menus: ["shareAppMessage"], // 单独禁用好友分享 menus: ["shareAppMessage"], // 单独禁用好友分享
}); });
new UrlParamsHandler(options); new UrlParamsHandler(options);
app.globalData.store = options; // 合并 URL 参数与 globalData.store 中已有的登录信息(来自 webview-auth
app.globalData.store = {
...app.globalData.store,
...options,
};
let safeBottom = 0; let safeBottom = 0;
//@ts-ignore //@ts-ignore
if (wx.getWindowInfo) { if (wx.getWindowInfo) {
@@ -52,7 +68,7 @@ Page({
} }
this.setData({ this.setData({
safeBottom: safeBottom, safeBottom: safeBottom,
store: options, store: app.globalData.store,
}); });
if (!isMobileEnvironment()) { if (!isMobileEnvironment()) {
@@ -95,6 +111,9 @@ Page({
* 生命周期函数--监听页面显示 * 生命周期函数--监听页面显示
*/ */
onShow() { onShow() {
const openId = wx.getStorageSync("openId");
if (!openId) return;
this.setData({ initialLoading: true });
this.search(); this.search();
}, },
+5
View File
@@ -1,4 +1,8 @@
<!--pages/video/video.wxml--> <!--pages/video/video.wxml-->
<view wx:if="{{!initialLoading}}" class="fullscreen-loading">
<view class="loading-spinner"></view>
</view>
<view wx:else>
<navigation-bar title="" back="{{false}}" bind:handleBack="handleBack" color="black" background="#FFF"> <navigation-bar title="" back="{{false}}" bind:handleBack="handleBack" color="black" background="#FFF">
<view slot="left" class="report-nav-btn" bind:tap="handleCustomReport">举报</view> <view slot="left" class="report-nav-btn" bind:tap="handleCustomReport">举报</view>
</navigation-bar> </navigation-bar>
@@ -37,3 +41,4 @@
</view> </view>
</van-tabs> </van-tabs>
</scroll-view> </scroll-view>
</view>
+43 -312
View File
@@ -3,261 +3,13 @@ import { LoginRes } from "./login.d";
import { wxLogin, wxRequest } from "../../utils/wx-api"; import { wxLogin, wxRequest } from "../../utils/wx-api";
const app = getApp<IAppOption>(); const app = getApp<IAppOption>();
const defaultAvatarUrl =
"https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0";
type LoginDisplayConfig = {
showLoginImage: boolean;
};
type PartialLoginDisplayConfig = Partial<LoginDisplayConfig>;
const loginConfigKeys = [
"login",
"loginConfig",
"loginPage",
"auth",
"authConfig",
"authPage",
];
const WXROOM_REDIRECT_PATH_KEY = "wxroomRedirectPath"; const WXROOM_REDIRECT_PATH_KEY = "wxroomRedirectPath";
function isPlainObject(value: unknown): value is Record<string, any> {
return Object.prototype.toString.call(value) === "[object Object]";
}
function hasOwnValue(source: Record<string, any>, 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<string, any>, 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<string, any>, 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<string, any>, keys: string[]) {
const value = getObjectValue(source, keys);
return normalizeBoolean(value);
}
function pushConfigCandidate(list: Record<string, any>[], value: unknown) {
if (isPlainObject(value)) {
list.push(value);
}
}
function collectLoginConfigCandidates(template: unknown) {
const candidates: Record<string, any>[] = [];
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<string, any>) {
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({ Page({
data: { data: {
back: "", back: "",
agree: false, agree: false,
nickFocus: true,
showPopup: false,
safeBottom: app.globalData.safeBottom, safeBottom: app.globalData.safeBottom,
showLoginImage: false,
userInfo: {
avatarUrl: defaultAvatarUrl,
nickName: "微信用户",
},
},
applyLoginDisplayConfig(options: Record<string, any> = {}) {
const template = app.globalData.template as Record<string, any> | 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, any>): string { buildQueryString(params: Record<string, any>): string {
@@ -288,56 +40,55 @@ Page({
"/package-live/wxroom/index"; "/package-live/wxroom/index";
wx.removeStorageSync(WXROOM_REDIRECT_PATH_KEY); wx.removeStorageSync(WXROOM_REDIRECT_PATH_KEY);
} else { } else {
const store = app.globalData.store; const store = app.globalData.store;
const state = wx.getStorageSync("state") ?? undefined; const state = wx.getStorageSync("state") ?? undefined;
const code = wx.getStorageSync("code") ?? undefined; const code = wx.getStorageSync("code") ?? undefined;
const groupId = wx.getStorageSync("groupId") ?? undefined; const groupId = wx.getStorageSync("groupId") ?? undefined;
if (state && !code) { if (state && !code) {
if (self.data.back === "video") { if (self.data.back === "video") {
const storeParams = const storeParams =
typeof store === "string" ? store : this.buildQueryString(store); typeof store === "string" ? store : this.buildQueryString(store);
fullPath = `/pages/live/video/video?${storeParams}`; fullPath = `/pages/live/video/video?${storeParams}`;
} else if (self.data.back === "red") { } else if (self.data.back === "red") {
fullPath = `/pages/common/red/red`; fullPath = `/pages/common/red/red`;
} else if (self.data.back === "user") { } else if (self.data.back === "user") {
fullPath = `/pages/tab-bar/user/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 { } 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 { } else {
const pages = getCurrentPages(); const pages = getCurrentPages();
const currentPage = pages[pages.length - 2]; const currentPage = pages[pages.length - 2];
if (!currentPage) { if (!currentPage) {
// 如果没有上一页,跳转到第一个可用 tab,而不是硬编码的 index
const tabbarItems = app.globalData.tabbarItems; const tabbarItems = app.globalData.tabbarItems;
if (tabbarItems && tabbarItems.length > 0) { if (tabbarItems && tabbarItems.length > 0) {
wx.switchTab({ url: tabbarItems[0].pagePath }); wx.switchTab({ url: tabbarItems[0].pagePath });
} else { } else {
wx.switchTab({ url: "/pages/tab-bar/medicine-box/index" }); wx.switchTab({ url: "/pages/tab-bar/medicine-box/index" });
} }
return; return;
} }
@@ -382,33 +133,24 @@ Page({
const loginRes = await wxLogin(); const loginRes = await wxLogin();
if (!loginRes.code) throw new Error(loginRes.errMsg); if (!loginRes.code) throw new Error(loginRes.errMsg);
const { avatarUrl, nickName } = this.data.userInfo;
const state = wx.getStorageSync("state"); const state = wx.getStorageSync("state");
let loginResult: LoginRes; let loginResult: LoginRes;
if (state) { if (state) {
const reqData: Record<string, any> = {
state: wx.getStorageSync("state") ?? undefined,
...(nickName && nickName !== "微信用户" ? { nickName } : {}),
...(avatarUrl && avatarUrl !== defaultAvatarUrl ? { avatarUrl } : {}),
};
loginResult = await wxRequest<LoginRes>({ loginResult = await wxRequest<LoginRes>({
url: `${BASE_URL}/api/auth/app-login?appId=${appId}&code=${loginRes.code}`, url: `${BASE_URL}/api/auth/app-login?appId=${appId}&code=${loginRes.code}`,
method: "POST", method: "POST",
data: reqData, data: {
state: wx.getStorageSync("state") ?? undefined,
},
}); });
} else { } else {
const reqData: Record<string, any> = {
state: wx.getStorageSync("code") ?? undefined,
...(nickName && nickName !== "微信用户" ? { nickName } : {}),
...(avatarUrl && avatarUrl !== defaultAvatarUrl ? { avatarUrl } : {}),
};
loginResult = await wxRequest<LoginRes>({ loginResult = await wxRequest<LoginRes>({
url: `${BASE_URL}/api/auth/app-auth?appId=${appId}&code=${loginRes.code}`, url: `${BASE_URL}/api/auth/app-auth?appId=${appId}&code=${loginRes.code}`,
method: "POST", method: "POST",
data: reqData, data: {
state: wx.getStorageSync("code") ?? undefined,
},
}); });
} }
@@ -442,10 +184,6 @@ Page({
}); });
app.globalData.isLogin = true; app.globalData.isLogin = true;
this.triggerEvent("loginSuccess", {
userName: resNickName,
avatarUrl: resAvatarUrl,
});
wx.showToast({ wx.showToast({
title: "登录成功", title: "登录成功",
duration: 2000, duration: 2000,
@@ -477,12 +215,5 @@ Page({
if (options.back) { if (options.back) {
this.setData({ back: options.back }); this.setData({ back: options.back });
} }
this.applyLoginDisplayConfig(options);
},
onShow() {
setTimeout(() => {
this.setData({ nickFocus: true });
}, 200);
}, },
}); });
@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "授权登录",
"usingComponents": {}
}
+74
View File
@@ -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<string, string> = {
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<IAppOption>().globalData.isLogin = true;
getApp<IAppOption>().globalData.store = {
...getApp<IAppOption>().globalData.store,
...fields,
};
try { this.setData({ authCompleted: true }); } catch (_) { }
},
});
+13
View File
@@ -0,0 +1,13 @@
<!--pages/webview-auth/index.wxml-->
<view class="container">
<view class="loading" wx:if="{{loading && webViewUrl}}">
<view class="loading-spinner"></view>
<text class="loading-text">正在加载授权页面...</text>
</view>
<web-view
wx:if="{{webViewUrl}}"
src="{{webViewUrl}}"
bindmessage="onMessage"
bindload="onWebViewLoad"
></web-view>
</view>
+61
View File
@@ -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;
}
+2 -1
View File
@@ -1,4 +1,4 @@
import { BASE_URL, appId } from "../env"; import { BASE_URL } from "../env";
import { base64Encode } from "./crypto"; import { base64Encode } from "./crypto";
type RequestProps = { type RequestProps = {
options: WechatMiniprogram.RequestOption; options: WechatMiniprogram.RequestOption;
@@ -23,6 +23,7 @@ export const request = <T = any>(
return Promise.reject(new Error('请先登录')); return Promise.reject(new Error('请先登录'));
} }
const openId = wx.getStorageSync("openId") || ""; const openId = wx.getStorageSync("openId") || "";
const appId = wx.getStorageSync("appId") || "";
header.Authorization = `Basic ${base64Encode(`${openId}:${appId}`)}` header.Authorization = `Basic ${base64Encode(`${openId}:${appId}`)}`
} }
+1 -1
View File
@@ -52,5 +52,5 @@
"ignore": [], "ignore": [],
"include": [] "include": []
}, },
"appid": "wx2bf5f8e9decdceaf" "appid": "wx53f37c2f7e3a147e"
} }
+33
View File
@@ -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"]
}