登录页面

This commit is contained in:
2026-07-12 15:17:54 +08:00
parent becf91da52
commit bfac6c6663
63 changed files with 4662 additions and 431 deletions
+3 -1
View File
@@ -1,5 +1,6 @@
import Taro from '@tarojs/taro';
import { ClientApp } from '@dukang/shared-types';
import { goLogin } from './auth-nav';
function resolveApiBase(): string {
const origin =
@@ -52,7 +53,7 @@ export function isLoggedIn(): boolean {
}
export function redirectToLogin() {
Taro.navigateTo({ url: '/pages/login/index' });
goLogin();
}
export function logout() {
@@ -119,4 +120,5 @@ export type UserProfile = {
id: string;
phone?: string | null;
nickname?: string | null;
avatarUrl?: string | null;
};
+60
View File
@@ -0,0 +1,60 @@
import Taro from '@tarojs/taro';
const TAB_PAGES = new Set([
'/pages/home/index',
'/pages/stores/index',
'/pages/benefit/index',
'/pages/mine/index',
]);
function currentPagePath(): string {
const pages = Taro.getCurrentPages();
const cur = pages[pages.length - 1] as
| { route?: string; options?: Record<string, string | undefined> }
| undefined;
if (!cur?.route) return '';
const path = cur.route.startsWith('/') ? cur.route : `/${cur.route}`;
if (path.includes('/pages/login/')) return '';
const opts = cur.options ?? {};
const qs = Object.entries(opts)
.filter(([, v]) => v != null && v !== '')
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
.join('&');
return qs ? `${path}?${qs}` : path;
}
/** 跳转登录页;默认带回当前页作为 return */
export function goLogin(returnPath?: string) {
const returnTo = returnPath ?? currentPagePath();
const url = returnTo
? `/pages/login/index?return=${encodeURIComponent(returnTo)}`
: '/pages/login/index';
Taro.navigateTo({ url }).catch(() => {
Taro.redirectTo({ url });
});
}
/** 登录成功后回到 return 页,或回退 / 首页 */
export function finishLoginNavigate(returnTo?: string) {
const raw = (returnTo || '').trim();
const target = raw ? decodeURIComponent(raw) : '';
const pathOnly = target.split('?')[0];
if (pathOnly && TAB_PAGES.has(pathOnly)) {
Taro.switchTab({ url: pathOnly });
return;
}
if (pathOnly && pathOnly.startsWith('/pages/')) {
Taro.redirectTo({ url: target }).catch(() => {
Taro.reLaunch({ url: pathOnly });
});
return;
}
const pages = Taro.getCurrentPages();
if (pages.length > 1) {
Taro.navigateBack();
return;
}
Taro.switchTab({ url: '/pages/home/index' });
}
+82
View File
@@ -0,0 +1,82 @@
import { useMemo } from 'react';
import Taro from '@tarojs/taro';
export type NavBarMetrics = {
/** 状态栏高度(刘海/灵动岛上方) */
statusBarHeight: number;
/** 导航栏总高度 = 状态栏 + 内容区 */
navBarHeight: number;
/** 标题栏内容区高度(与胶囊对齐) */
navContentHeight: number;
/** 右侧留白,避免与微信胶囊按钮重叠 */
navBarPaddingRight: number;
};
const H5_FALLBACK: NavBarMetrics = {
statusBarHeight: 0,
navBarHeight: 56,
navContentHeight: 56,
navBarPaddingRight: 16,
};
const WEAPP_FALLBACK: NavBarMetrics = {
statusBarHeight: 20,
navBarHeight: 64,
navContentHeight: 44,
navBarPaddingRight: 96,
};
/** 计算小程序自定义导航栏尺寸(对齐微信胶囊按钮) */
export function getNavBarMetrics(): NavBarMetrics {
if (process.env.TARO_ENV === 'h5') {
return H5_FALLBACK;
}
try {
const win = Taro.getWindowInfo?.() ?? Taro.getSystemInfoSync();
const menu = Taro.getMenuButtonBoundingClientRect();
const statusBarHeight = win.statusBarHeight ?? WEAPP_FALLBACK.statusBarHeight;
const navContentHeight =
menu.height > 0
? (menu.top - statusBarHeight) * 2 + menu.height
: WEAPP_FALLBACK.navContentHeight;
const navBarHeight = statusBarHeight + navContentHeight;
const navBarPaddingRight =
menu.width > 0
? Math.max(win.windowWidth - menu.left + 8, 16)
: WEAPP_FALLBACK.navBarPaddingRight;
return {
statusBarHeight,
navBarHeight,
navContentHeight,
navBarPaddingRight,
};
} catch {
return WEAPP_FALLBACK;
}
}
export function useNavBarMetrics(): NavBarMetrics {
return useMemo(() => getNavBarMetrics(), []);
}
/** 仅注入 CSS 变量,供 PageShell / sticky 子元素使用(不加 height/padding */
export function pageShellCssVars(metrics: NavBarMetrics): Record<string, string> {
return {
'--nav-bar-height': `${metrics.navBarHeight}px`,
'--nav-content-height': `${metrics.navContentHeight}px`,
'--nav-status-bar-height': `${metrics.statusBarHeight}px`,
'--nav-padding-right': `${metrics.navBarPaddingRight}px`,
};
}
/** 顶栏自身样式:statusBar padding + 总高 + 右侧胶囊避让 */
export function navBarStyle(metrics: NavBarMetrics): Record<string, string | number> {
return {
paddingTop: `${metrics.statusBarHeight}px`,
height: `${metrics.navBarHeight}px`,
paddingRight: `${metrics.navBarPaddingRight}px`,
...pageShellCssVars(metrics),
};
}
+12
View File
@@ -30,4 +30,16 @@ export function getProductImages(source?: ProductImageSource | null): string[] {
return PRODUCT_IMAGE_FALLBACK ? [PRODUCT_IMAGE_FALLBACK] : [];
}
/** 详情页顶部轮播 */
export function getProductCarouselImages(source?: ProductImageSource | null): string[] {
const carousel = uniqueUrls(source?.carouselUrls ?? []);
if (carousel.length > 0) return carousel;
return getProductImages(source);
}
/** 详情页图文长图 */
export function getProductDetailImages(source?: ProductImageSource | null): string[] {
return uniqueUrls(source?.detailImageUrls ?? []);
}
export const FALLBACK_CITY_CODE = '410100';
+15
View File
@@ -0,0 +1,15 @@
import type { WechatLoginResult } from '@dukang/shared-types';
import Taro from '@tarojs/taro';
import { request } from './api';
/** 小程序微信授权登录:Taro.login → /auth/login/wechat */
export async function loginWithWechat(): Promise<WechatLoginResult> {
const res = await Taro.login();
if (!res.code) {
throw new Error(res.errMsg || '微信登录失败,未获取到 code');
}
return request<WechatLoginResult>('/auth/login/wechat', {
method: 'POST',
data: { code: res.code, platform: 'mini' },
});
}