feat: 电子药箱小程序 - 4Tab药品管理
- 电子药箱: 药品库存管理 + 手动录入 + 今日用药打卡 - 药品百科: 搜索 + 分类筛选 + 20种药品静态数据 - 惠教中心: 4条药品使用指南课程 - 我的: 家庭成员信息管理 - 自定义TabBar + navigation-bar组件 - SVG药品分类插图
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.934 19.9344H19.934V4.73438H8.33398V8.73438" stroke="#FE8D29" stroke-width="0.8" stroke-linejoin="round"/>
|
||||
<path d="M4.7334 8.73047H11.9334V19.9305H4.7334V8.73047Z" fill="#FE8D29" stroke="#FE8D29" stroke-width="0.8" stroke-linejoin="round"/>
|
||||
<path d="M7.93359 17.5312H8.73359" stroke="white" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.9336 17.5312H14.7336" stroke="#FE8D29" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 595 B |
@@ -0,0 +1,12 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5324 11.0853C17.4708 7.60394 14.6294 4.80078 11.1334 4.80078C7.59876 4.80078 4.7334 7.66614 4.7334 11.2008C4.7334 12.8688 5.37152 14.3878 6.41697 15.5269L5.93757 18.4111L8.7834 17.1887C10.0373 17.6057 11.2206 17.7099 12.3334 17.5014" fill="white"/>
|
||||
<path d="M17.5324 11.0853C17.4708 7.60394 14.6294 4.80078 11.1334 4.80078C7.59876 4.80078 4.7334 7.66614 4.7334 11.2008C4.7334 12.8688 5.37152 14.3878 6.41697 15.5269L5.93757 18.4111L8.7834 17.1887C10.0373 17.6057 11.2206 17.7099 12.3334 17.5014" stroke="white" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.78281 10.5868C9.27985 10.5868 9.68281 10.1809 9.68281 9.68012C9.68281 9.1794 9.27985 8.77344 8.78281 8.77344C8.28577 8.77344 7.88281 9.1794 7.88281 9.68012C7.88281 10.1809 8.28577 10.5868 8.78281 10.5868Z" fill="#FE8D29"/>
|
||||
<path d="M12.3834 10.5868C12.8804 10.5868 13.2834 10.1809 13.2834 9.68012C13.2834 9.1794 12.8804 8.77344 12.3834 8.77344C11.8864 8.77344 11.4834 9.1794 11.4834 9.68012C11.4834 10.1809 11.8864 10.5868 12.3834 10.5868Z" fill="#FE8D29"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2379 18.3725C17.5386 18.8929 16.6719 19.2008 15.7332 19.2008C13.4136 19.2008 11.5332 17.3204 11.5332 15.0008C11.5332 12.6812 13.4136 10.8008 15.7332 10.8008C18.0528 10.8008 19.9332 12.6812 19.9332 15.0008C19.9332 15.6407 19.7901 16.2471 19.5342 16.7899" fill="white"/>
|
||||
<path d="M18.2379 18.3725C17.5386 18.8929 16.6719 19.2008 15.7332 19.2008C13.4136 19.2008 11.5332 17.3204 11.5332 15.0008C11.5332 12.6812 13.4136 10.8008 15.7332 10.8008C18.0528 10.8008 19.9332 12.6812 19.9332 15.0008C19.9332 15.6407 19.7901 16.2471 19.5342 16.7899" stroke="#FE8D29" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.5345 16.7891L19.9336 19.1999L18.2383 18.3717" fill="white"/>
|
||||
<path d="M19.5345 16.7891L19.9336 19.1999L18.2383 18.3717" stroke="#FE8D29" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.008 14.7194C16.6352 14.7194 16.333 14.4149 16.333 14.0394C16.333 13.6638 16.6352 13.3594 17.008 13.3594C17.3808 13.3594 17.683 13.6638 17.683 14.0394C17.683 14.4149 17.3808 14.7194 17.008 14.7194Z" fill="#FE8D29"/>
|
||||
<path d="M14.3078 14.7194C13.935 14.7194 13.6328 14.4149 13.6328 14.0394C13.6328 13.6638 13.935 13.3594 14.3078 13.3594C14.6806 13.3594 14.9828 13.6638 14.9828 14.0394C14.9828 14.4149 14.6806 14.7194 14.3078 14.7194Z" fill="#FE8D29"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="18" cy="20" r="10" fill="white"/>
|
||||
<circle cx="31" cy="18" r="8" fill="white" fill-opacity="0.88"/>
|
||||
<circle cx="15" cy="18" r="1.8" fill="#FE8D29"/>
|
||||
<circle cx="21" cy="18" r="1.8" fill="#FE8D29"/>
|
||||
<circle cx="28" cy="17" r="1.5" fill="#FE8D29"/>
|
||||
<circle cx="33" cy="17" r="1.5" fill="#FE8D29"/>
|
||||
<path d="M12 28L16.8 24.8L18.7 31.2L12 28Z" fill="white"/>
|
||||
<path d="M27.4 28L30.8 25.8L32.1 30L27.4 28Z" fill="white" fill-opacity="0.88"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 566 B |
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
export type LoginRes = {
|
||||
data: {
|
||||
code?: number;
|
||||
avatarUrl:string;
|
||||
msg?: string,
|
||||
appId: string,
|
||||
id: number,
|
||||
mpId: number,
|
||||
openId: number,
|
||||
mpUserId: number,
|
||||
statusCode: string,
|
||||
unionId: string,
|
||||
nickName:string
|
||||
},
|
||||
statusCode: number,
|
||||
msg: string
|
||||
}
|
||||
|
||||
export type userInfoRes = {
|
||||
openId: string,
|
||||
mbuId: number,
|
||||
nickName: string,
|
||||
gender: number,
|
||||
city: string,
|
||||
province: string,
|
||||
country: string,
|
||||
avatarUrl: string,
|
||||
unionId: string,
|
||||
watermark: {
|
||||
appid: string,
|
||||
timestamp: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar",
|
||||
"popup": "/components/popup/popup"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
@import "../../variables";
|
||||
|
||||
.loginContainer {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.contentWrap {
|
||||
flex: 1;
|
||||
padding-bottom: 120rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.heroPanel {
|
||||
position: relative;
|
||||
height: 1332rpx;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.heroImage {
|
||||
position: absolute;
|
||||
// top: 68rpx;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
width: 750rpx;
|
||||
}
|
||||
|
||||
.actionPanel {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 626rpx;
|
||||
margin: -300rpx auto 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 48rpx;
|
||||
// padding-bottom: 72rpx;
|
||||
}
|
||||
|
||||
.actionPanelSingle {
|
||||
margin-top: -700rpx;
|
||||
}
|
||||
|
||||
.loginButton {
|
||||
box-sizing: border-box;
|
||||
width: 626rpx;
|
||||
height: 92rpx;
|
||||
padding: 0 32rpx;
|
||||
border-radius: 76rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wechatLogin {
|
||||
background: #fe8d29;
|
||||
box-shadow: 0 20rpx 40rpx rgba(254, 141, 41, 0.24);
|
||||
}
|
||||
|
||||
.phoneLogin {
|
||||
background: #fff1d6;
|
||||
}
|
||||
|
||||
.loginIcon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 8rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.phoneIcon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.loginLabel {
|
||||
font-size: 32rpx;
|
||||
font-weight: 400;
|
||||
line-height: 44rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.phoneLabel {
|
||||
color: #fe8d29;
|
||||
}
|
||||
|
||||
.privacyFooter {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-top: 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
z-index: 3;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.checkboxLabel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.radioTrigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.radioTrigger radio {
|
||||
transform: scale(0.7);
|
||||
// transform-origin: left center;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.privacyText {
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
.linkText {
|
||||
color: #fe8d29;
|
||||
line-height: 34rpx;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
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);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
<navigation-bar title="登录" back="{{!back}}" color="black" background="#FFF" />
|
||||
<view class="loginContainer">
|
||||
<view class="contentWrap">
|
||||
<view class="heroPanel">
|
||||
</view>
|
||||
<view class="actionPanel actionPanelSingle">
|
||||
<view class="loginButton wechatLogin" bind:tap="handleLogin">
|
||||
<image class="loginIcon" src="./assets/wechat-login-icon-figma.svg" mode="aspectFit"></image>
|
||||
<text class="loginLabel">微信登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="privacyFooter" style="padding-bottom: {{safeBottom}}rpx;">
|
||||
<view class="checkboxLabel">
|
||||
<view class="radioTrigger" catchtap="handleToggleAgree">
|
||||
<radio-group>
|
||||
<radio value="agree" checked="{{agree}}" color="#FE8D29" />
|
||||
</radio-group>
|
||||
</view>
|
||||
<text class="privacyText">我已阅读并同意</text>
|
||||
<text class="linkText" bindtap="openPrivacyContract">《用户协议》</text>
|
||||
<text class="privacyText">和</text>
|
||||
<text class="linkText" bindtap="openPrivacyContract">《隐私政策》</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
Reference in New Issue
Block a user