- 电子药箱: 药品库存管理 + 手动录入 + 今日用药打卡 - 药品百科: 搜索 + 分类筛选 + 20种药品静态数据 - 惠教中心: 4条药品使用指南课程 - 我的: 家庭成员信息管理 - 自定义TabBar + navigation-bar组件 - SVG药品分类插图
489 lines
15 KiB
TypeScript
489 lines
15 KiB
TypeScript
import { appId, BASE_URL } from "../../env";
|
|
import { LoginRes } from "./login.d";
|
|
import { wxLogin, wxRequest } from "../../utils/wx-api";
|
|
|
|
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";
|
|
|
|
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({
|
|
data: {
|
|
back: "",
|
|
agree: false,
|
|
nickFocus: true,
|
|
showPopup: false,
|
|
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 {
|
|
return Object.entries(params)
|
|
.map(
|
|
([key, value]) =>
|
|
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
)
|
|
.join("&");
|
|
},
|
|
|
|
redirectUrl() {
|
|
const self = this;
|
|
let fullPath = "";
|
|
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",
|
|
];
|
|
|
|
if (self.data.back) {
|
|
if (self.data.back === "wxroom") {
|
|
fullPath =
|
|
wx.getStorageSync(WXROOM_REDIRECT_PATH_KEY) ||
|
|
"/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) {
|
|
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 });
|
|
} else {
|
|
wx.switchTab({ url: "/pages/tab-bar/medicine-box/index" });
|
|
}
|
|
return;
|
|
}
|
|
|
|
const route = `/${currentPage.route}`;
|
|
const options = currentPage.options || {};
|
|
const query = this.buildQueryString(options);
|
|
fullPath = query ? `${route}?${query}` : route;
|
|
|
|
if (tabPages.includes(route)) {
|
|
wx.switchTab({ url: route });
|
|
return;
|
|
}
|
|
|
|
wx.redirectTo({ url: route });
|
|
return;
|
|
}
|
|
|
|
if (tabPages.includes(fullPath)) {
|
|
wx.switchTab({ url: fullPath });
|
|
} else {
|
|
wx.redirectTo({ url: fullPath });
|
|
}
|
|
},
|
|
|
|
async handleLogin() {
|
|
if (!this.data.agree) {
|
|
wx.showModal({
|
|
title: "请先勾选用户协议和隐私政策",
|
|
icon: "none",
|
|
success: (options) => {
|
|
if (options.confirm) {
|
|
this.setData({ agree: true });
|
|
}
|
|
},
|
|
});
|
|
if (!this.data.agree) 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<string, any> = {
|
|
state: wx.getStorageSync("state") ?? undefined,
|
|
...(nickName && nickName !== "微信用户" ? { nickName } : {}),
|
|
...(avatarUrl && avatarUrl !== defaultAvatarUrl ? { avatarUrl } : {}),
|
|
};
|
|
|
|
loginResult = await wxRequest<LoginRes>({
|
|
url: `${BASE_URL}/api/auth/app-login?appId=${appId}&code=${loginRes.code}`,
|
|
method: "POST",
|
|
data: reqData,
|
|
});
|
|
} else {
|
|
const reqData: Record<string, any> = {
|
|
state: wx.getStorageSync("code") ?? undefined,
|
|
...(nickName && nickName !== "微信用户" ? { nickName } : {}),
|
|
...(avatarUrl && avatarUrl !== defaultAvatarUrl ? { avatarUrl } : {}),
|
|
};
|
|
|
|
loginResult = await wxRequest<LoginRes>({
|
|
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)
|
|
) {
|
|
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,
|
|
};
|
|
|
|
Object.entries(storageData).forEach(([key, value]) => {
|
|
wx.setStorageSync(key, value);
|
|
});
|
|
|
|
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}`,
|
|
icon: "none",
|
|
});
|
|
} finally {
|
|
setTimeout(() => wx.hideToast(), 3000);
|
|
}
|
|
},
|
|
|
|
handleToggleAgree() {
|
|
this.setData({ agree: !this.data.agree });
|
|
},
|
|
|
|
openPrivacyContract() {
|
|
// @ts-ignore
|
|
wx.openPrivacyContract();
|
|
},
|
|
|
|
onLoad(options: any) {
|
|
if (options.back) {
|
|
this.setData({ back: options.back });
|
|
}
|
|
this.applyLoginDisplayConfig(options);
|
|
},
|
|
|
|
onShow() {
|
|
setTimeout(() => {
|
|
this.setData({ nickFocus: true });
|
|
}, 200);
|
|
},
|
|
});
|