merge(dev_jacy): fix phone bind session bounce and mini-user UX
This commit is contained in:
@@ -76,7 +76,18 @@ function parseBody(data: unknown): { code?: number; message?: string } {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 统一请求:注入 USER_MINI + Bearer,解包 { code, message, data } */
|
function isOnLoginPage(): boolean {
|
||||||
|
try {
|
||||||
|
const pages = Taro.getCurrentPages();
|
||||||
|
const cur = pages[pages.length - 1] as { route?: string } | undefined;
|
||||||
|
const route = cur?.route || '';
|
||||||
|
return route.includes('pages/login');
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 统一请求:注入 CLIENT_APP + Bearer,解包 { code, message, data } */
|
||||||
export async function request<T = unknown>(path: string, options: ReqOptions = {}): Promise<T> {
|
export async function request<T = unknown>(path: string, options: ReqOptions = {}): Promise<T> {
|
||||||
const header: Record<string, string> = {
|
const header: Record<string, string> = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -96,8 +107,12 @@ export async function request<T = unknown>(path: string, options: ReqOptions = {
|
|||||||
const body = parseBody(res.data);
|
const body = parseBody(res.data);
|
||||||
|
|
||||||
if (status === 401 || body?.code === 401) {
|
if (status === 401 || body?.code === 401) {
|
||||||
clearAuth();
|
// 仅清掉「发起本请求时」仍在使用的 token,避免登录页旧 /auth/me 竞态清掉刚写入的新 token
|
||||||
redirectToLogin();
|
const stillCurrent = !!token && getToken() === token;
|
||||||
|
if (stillCurrent) {
|
||||||
|
clearAuth();
|
||||||
|
if (!isOnLoginPage()) redirectToLogin();
|
||||||
|
}
|
||||||
throw new Error(body?.message || '登录已过期,请重新登录');
|
throw new Error(body?.message || '登录已过期,请重新登录');
|
||||||
}
|
}
|
||||||
if (status === 404 && body.code === undefined) {
|
if (status === 404 && body.code === undefined) {
|
||||||
|
|||||||
@@ -42,24 +42,31 @@ export function goLogin(returnPath?: string, extras?: Record<string, string>) {
|
|||||||
/** 登录成功后回到 return 页,或回退 / 首页 */
|
/** 登录成功后回到 return 页,或回退 / 首页 */
|
||||||
export function finishLoginNavigate(returnTo?: string) {
|
export function finishLoginNavigate(returnTo?: string) {
|
||||||
const raw = (returnTo || '').trim();
|
const raw = (returnTo || '').trim();
|
||||||
const target = raw ? decodeURIComponent(raw) : '';
|
let target = '';
|
||||||
|
try {
|
||||||
|
target = raw ? decodeURIComponent(raw) : '';
|
||||||
|
} catch {
|
||||||
|
target = raw;
|
||||||
|
}
|
||||||
|
// 防止 return 仍指向登录页造成死循环
|
||||||
const pathOnly = target.split('?')[0];
|
const pathOnly = target.split('?')[0];
|
||||||
|
if (!pathOnly || pathOnly.includes('/pages/login')) {
|
||||||
if (pathOnly && TAB_PAGES.has(pathOnly)) {
|
Taro.reLaunch({ url: '/pages/home/index' });
|
||||||
Taro.switchTab({ url: pathOnly });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pathOnly && pathOnly.startsWith('/pages/')) {
|
|
||||||
|
if (TAB_PAGES.has(pathOnly)) {
|
||||||
|
Taro.switchTab({ url: pathOnly }).catch(() => {
|
||||||
|
Taro.reLaunch({ url: pathOnly });
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pathOnly.startsWith('/pages/')) {
|
||||||
Taro.redirectTo({ url: target }).catch(() => {
|
Taro.redirectTo({ url: target }).catch(() => {
|
||||||
Taro.reLaunch({ url: pathOnly });
|
Taro.reLaunch({ url: pathOnly });
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pages = Taro.getCurrentPages();
|
Taro.reLaunch({ url: '/pages/home/index' });
|
||||||
if (pages.length > 1) {
|
|
||||||
Taro.navigateBack();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Taro.switchTab({ url: '/pages/home/index' });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,12 @@ export async function ensurePayReady(returnPath: string): Promise<boolean> {
|
|||||||
|
|
||||||
goLogin(returnPath, { needWechat: '1' });
|
goLogin(returnPath, { needWechat: '1' });
|
||||||
return false;
|
return false;
|
||||||
} catch {
|
} catch (e) {
|
||||||
goLogin(returnPath);
|
// 仅会话失效时踢回登录;网络/业务错误不误清登录态
|
||||||
|
const msg = e instanceof Error ? e.message : '';
|
||||||
|
if (/登录已过期|重新登录|401/.test(msg) || !isLoggedIn()) {
|
||||||
|
goLogin(returnPath);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { DEFAULT_REGION, regionFromGeo, type RegionSelection } from './region-da
|
|||||||
import { FALLBACK_CITY_CODE } from './product-images';
|
import { FALLBACK_CITY_CODE } from './product-images';
|
||||||
|
|
||||||
export const GPS_CITY_STORAGE_KEY = 'dukang_gps_city';
|
export const GPS_CITY_STORAGE_KEY = 'dukang_gps_city';
|
||||||
|
/** 用户拒绝定位后持久化,避免首页/门店每次 useDidShow 再弹授权 */
|
||||||
|
const LOCATION_DENIED_KEY = 'dukang_location_denied';
|
||||||
|
|
||||||
export type ResolvedUserCity = {
|
export type ResolvedUserCity = {
|
||||||
province: string;
|
province: string;
|
||||||
@@ -30,7 +32,35 @@ const FALLBACK_CITY: ResolvedUserCity = {
|
|||||||
displayCity: '郑州市',
|
displayCity: '郑州市',
|
||||||
};
|
};
|
||||||
|
|
||||||
let locationPrompted = false;
|
function isLocationDenied(): boolean {
|
||||||
|
try {
|
||||||
|
return Taro.getStorageSync(LOCATION_DENIED_KEY) === '1';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function markLocationDenied() {
|
||||||
|
try {
|
||||||
|
Taro.setStorageSync(LOCATION_DENIED_KEY, '1');
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLocationDenied() {
|
||||||
|
try {
|
||||||
|
Taro.removeStorageSync(LOCATION_DENIED_KEY);
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDenyMessage(errMsg?: string): boolean {
|
||||||
|
return /auth deny|authorize|permission|denied|拒绝|用户拒绝|getLocation:fail/i.test(
|
||||||
|
errMsg || '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function readCache(): GpsCityCache | null {
|
function readCache(): GpsCityCache | null {
|
||||||
try {
|
try {
|
||||||
@@ -55,6 +85,12 @@ function writeCache(data: ResolvedUserCity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 拒绝或失败后写入兜底城市,避免短时间内反复调起定位 */
|
||||||
|
function cacheFallbackAndMaybeDeny(denied: boolean) {
|
||||||
|
if (denied) markLocationDenied();
|
||||||
|
writeCache(FALLBACK_CITY);
|
||||||
|
}
|
||||||
|
|
||||||
async function reportLocationToServer(payload: {
|
async function reportLocationToServer(payload: {
|
||||||
latitude?: number;
|
latitude?: number;
|
||||||
longitude?: number;
|
longitude?: number;
|
||||||
@@ -98,14 +134,12 @@ function toResolved(data: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function promptLocationAuth() {
|
async function promptLocationAuthOnce() {
|
||||||
if (locationPrompted) return;
|
|
||||||
locationPrompted = true;
|
|
||||||
await Taro.showModal({
|
await Taro.showModal({
|
||||||
title: '位置授权',
|
title: '位置授权',
|
||||||
content: '需要获取您的位置以展示所在城市的商品与门店',
|
content: '需要获取您的位置以展示所在城市的商品与门店。拒绝后将默认使用郑州市,不会再次弹窗。',
|
||||||
confirmText: '去授权',
|
confirmText: '知道了',
|
||||||
showCancel: true,
|
showCancel: false,
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,11 +161,14 @@ async function resolveViaH5Jssdk(): Promise<ResolvedUserCity | null> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!outcome.location) {
|
if (!outcome.location) {
|
||||||
|
const denied = isDenyMessage(outcome.errMsg);
|
||||||
await reportLocationToServer({
|
await reportLocationToServer({
|
||||||
sdk: outcome.sdk,
|
sdk: outcome.sdk,
|
||||||
status: 'fail',
|
status: 'fail',
|
||||||
errMsg: outcome.errMsg,
|
errMsg: outcome.errMsg,
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
// 失败一律缓存兜底,避免首页/门店每次进入再次调起微信定位弹窗
|
||||||
|
cacheFallbackAndMaybeDeny(denied);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,9 +180,13 @@ async function resolveViaH5Jssdk(): Promise<ResolvedUserCity | null> {
|
|||||||
status: 'success',
|
status: 'success',
|
||||||
});
|
});
|
||||||
const resolved = toResolved(data);
|
const resolved = toResolved(data);
|
||||||
if (resolved) writeCache(resolved);
|
if (resolved) {
|
||||||
|
clearLocationDenied();
|
||||||
|
writeCache(resolved);
|
||||||
|
}
|
||||||
return resolved;
|
return resolved;
|
||||||
} catch {
|
} catch {
|
||||||
|
cacheFallbackAndMaybeDeny(false);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +194,10 @@ async function resolveViaH5Jssdk(): Promise<ResolvedUserCity | null> {
|
|||||||
/** 获取并解析用户当前城市;失败返回郑州市兜底 */
|
/** 获取并解析用户当前城市;失败返回郑州市兜底 */
|
||||||
export async function resolveUserCity(force = false): Promise<ResolvedUserCity> {
|
export async function resolveUserCity(force = false): Promise<ResolvedUserCity> {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
|
if (isLocationDenied()) {
|
||||||
|
const cached = readCache();
|
||||||
|
return cached ?? FALLBACK_CITY;
|
||||||
|
}
|
||||||
const cached = readCache();
|
const cached = readCache();
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
}
|
}
|
||||||
@@ -176,20 +221,22 @@ export async function resolveUserCity(force = false): Promise<ResolvedUserCity>
|
|||||||
});
|
});
|
||||||
const resolved = toResolved(data);
|
const resolved = toResolved(data);
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
|
clearLocationDenied();
|
||||||
writeCache(resolved);
|
writeCache(resolved);
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const errMsg = err instanceof Error ? err.message : String(err);
|
const errMsg = err instanceof Error ? err.message : String(err);
|
||||||
const denied = /auth deny|authorize|permission|拒绝/i.test(errMsg);
|
const denied = isDenyMessage(errMsg);
|
||||||
if (denied) {
|
if (denied && !isLocationDenied()) {
|
||||||
await promptLocationAuth();
|
await promptLocationAuthOnce();
|
||||||
}
|
}
|
||||||
await reportLocationToServer({
|
await reportLocationToServer({
|
||||||
sdk: 'jssdk',
|
sdk: 'jssdk',
|
||||||
status: 'fail',
|
status: 'fail',
|
||||||
errMsg: errMsg.slice(0, 200),
|
errMsg: errMsg.slice(0, 200),
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
cacheFallbackAndMaybeDeny(denied);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALLBACK_CITY;
|
return FALLBACK_CITY;
|
||||||
|
|||||||
@@ -66,8 +66,15 @@ export default function LoginPage() {
|
|||||||
setCompleteMode(null);
|
setCompleteMode(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 完善资料场景才拉 profile;普通登录勿抢跑 /auth/me,避免旧 token 401 与短信登录竞态
|
||||||
|
if (!needPhone && !needWechat) {
|
||||||
|
setCompleteMode(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cancelled = false;
|
||||||
fetchUserProfile()
|
fetchUserProfile()
|
||||||
.then((me) => {
|
.then((me) => {
|
||||||
|
if (cancelled) return;
|
||||||
if (needPhone && !me.phoneVerified) {
|
if (needPhone && !me.phoneVerified) {
|
||||||
setCompleteMode('phone');
|
setCompleteMode('phone');
|
||||||
return;
|
return;
|
||||||
@@ -76,13 +83,14 @@ export default function LoginPage() {
|
|||||||
setCompleteMode('wechat');
|
setCompleteMode('wechat');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (needPhone || needWechat) {
|
finishLoginNavigate(returnTo);
|
||||||
finishLoginNavigate(returnTo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setCompleteMode(null);
|
|
||||||
})
|
})
|
||||||
.catch(() => setCompleteMode(null));
|
.catch(() => {
|
||||||
|
if (!cancelled) setCompleteMode(null);
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
}, [needPhone, needWechat, returnTo]);
|
}, [needPhone, needWechat, returnTo]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -103,6 +111,7 @@ export default function LoginPage() {
|
|||||||
data: SessionPayload | WechatLoginResult,
|
data: SessionPayload | WechatLoginResult,
|
||||||
phone?: string,
|
phone?: string,
|
||||||
wxInfo?: MiniWechatProfile | null,
|
wxInfo?: MiniWechatProfile | null,
|
||||||
|
successToast = '登录成功',
|
||||||
) {
|
) {
|
||||||
if (!data.accessToken) return;
|
if (!data.accessToken) return;
|
||||||
if (phone) saveUserPhone(phone);
|
if (phone) saveUserPhone(phone);
|
||||||
@@ -116,7 +125,7 @@ export default function LoginPage() {
|
|||||||
.then((me) => resolveDefaultUserPhone(me))
|
.then((me) => resolveDefaultUserPhone(me))
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
toast('登录成功', 'success');
|
toast(successToast, 'success');
|
||||||
finishLoginNavigate(returnTo);
|
finishLoginNavigate(returnTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,19 +197,26 @@ export default function LoginPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (completeMode === 'phone' && isLoggedIn()) {
|
if (completeMode === 'phone' && isLoggedIn()) {
|
||||||
await request('/auth/phone/bind', {
|
// bind 返回新 session(合并账号后旧 guest JWT 立刻失效),必须落盘后再离开
|
||||||
|
const data = await request<SessionPayload>('/auth/phone/bind', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { phone: normalized, code: code.trim() },
|
data: { phone: normalized, code: code.trim() },
|
||||||
});
|
});
|
||||||
saveUserPhone(normalized);
|
if (!data?.accessToken) {
|
||||||
toast('手机号验证成功', 'success');
|
setMsg('手机号验证成功但会话未返回,请重新登录');
|
||||||
finishLoginNavigate(returnTo);
|
return;
|
||||||
|
}
|
||||||
|
applySessionAndLeave(data, normalized, null, '手机号验证成功');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = await request<SessionPayload>('/auth/login/sms', {
|
const data = await request<SessionPayload>('/auth/login/sms', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { phone: normalized, code: code.trim() },
|
data: { phone: normalized, code: code.trim() },
|
||||||
});
|
});
|
||||||
|
if (!data?.accessToken) {
|
||||||
|
setMsg('登录成功但未返回令牌,请重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
applySessionAndLeave(data, normalized);
|
applySessionAndLeave(data, normalized);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setMsg(e instanceof Error ? e.message : '登录失败');
|
setMsg(e instanceof Error ? e.message : '登录失败');
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import { useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
|
import Taro, { useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
|
||||||
import PageShell from '../../components/PageShell';
|
import PageShell from '../../components/PageShell';
|
||||||
import SubPageHeader from '../../components/SubPageHeader';
|
import SubPageHeader from '../../components/SubPageHeader';
|
||||||
import ShareNavButton from '../../components/ShareNavButton';
|
import ShareNavButton from '../../components/ShareNavButton';
|
||||||
import WechatShareReady from '../../components/WechatShareReady';
|
import WechatShareReady from '../../components/WechatShareReady';
|
||||||
import { request, toast } from '../../lib/api';
|
import { request, toast } from '../../lib/api';
|
||||||
|
import { buildPayUrl } from '../../lib/checkout-nav';
|
||||||
import {
|
import {
|
||||||
DEFAULT_SHARE_DESC,
|
DEFAULT_SHARE_DESC,
|
||||||
DEFAULT_SHARE_TITLE,
|
DEFAULT_SHARE_TITLE,
|
||||||
@@ -21,6 +22,19 @@ type OrderDetail = {
|
|||||||
qty?: number;
|
qty?: number;
|
||||||
addressSnapshot?: string;
|
addressSnapshot?: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
|
originOrderId?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const STATUS_LABELS: Record<string, string> = {
|
||||||
|
PENDING_PAY: '待付款',
|
||||||
|
PENDING_SHIP: '待发货',
|
||||||
|
OUT_WAREHOUSE: '出库中',
|
||||||
|
SHIPPING: '配送中',
|
||||||
|
PENDING_RECEIVE: '待签收',
|
||||||
|
COMPLETED: '已完成',
|
||||||
|
CANCELLED: '已取消',
|
||||||
|
REFUNDING: '退款中',
|
||||||
|
REFUNDED: '已退款',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function OrderDetailPage() {
|
export default function OrderDetailPage() {
|
||||||
@@ -35,6 +49,8 @@ export default function OrderDetailPage() {
|
|||||||
.catch((e) => toast(e instanceof Error ? e.message : '加载失败'));
|
.catch((e) => toast(e instanceof Error ? e.message : '加载失败'));
|
||||||
}, [orderId]);
|
}, [orderId]);
|
||||||
|
|
||||||
|
const canPay = !!order && order.status === 'PENDING_PAY' && !order.originOrderId;
|
||||||
|
|
||||||
const sharePayload = useMemo(
|
const sharePayload = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
title: order?.productName
|
title: order?.productName
|
||||||
@@ -52,8 +68,13 @@ export default function OrderDetailPage() {
|
|||||||
query: orderId ? `id=${orderId}` : '',
|
query: orderId ? `id=${orderId}` : '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
function goPay() {
|
||||||
|
if (!order) return;
|
||||||
|
Taro.navigateTo({ url: buildPayUrl({ orderId: order.id }) });
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageShell variant="sub" className="order-detail-page">
|
<PageShell variant="sub" className={`order-detail-page${canPay ? ' order-detail-page--with-pay' : ''}`}>
|
||||||
<WechatShareReady payload={sharePayload} />
|
<WechatShareReady payload={sharePayload} />
|
||||||
<SubPageHeader
|
<SubPageHeader
|
||||||
title="订单详情"
|
title="订单详情"
|
||||||
@@ -66,7 +87,9 @@ export default function OrderDetailPage() {
|
|||||||
<>
|
<>
|
||||||
<View className="order-card">
|
<View className="order-card">
|
||||||
<Text className="order-card-title">订单状态</Text>
|
<Text className="order-card-title">订单状态</Text>
|
||||||
<Text className="order-list-status">{order.status || '处理中'}</Text>
|
<Text className="order-list-status">
|
||||||
|
{STATUS_LABELS[order.status || ''] || order.status || '处理中'}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="order-card">
|
<View className="order-card">
|
||||||
<Text className="order-card-title">商品信息</Text>
|
<Text className="order-card-title">商品信息</Text>
|
||||||
@@ -103,6 +126,20 @@ export default function OrderDetailPage() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{canPay && order && (
|
||||||
|
<View className="pay-bar order-detail-pay-bar">
|
||||||
|
<View className="order-confirm-total">
|
||||||
|
<Text className="order-confirm-total-label">待支付</Text>
|
||||||
|
<Text className="order-confirm-total-value">
|
||||||
|
¥{Number(order.payAmount ?? 0).toFixed(2)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View className="order-confirm-submit" onClick={goPay}>
|
||||||
|
去付款
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.order-detail-page--with-pay .sub-page-body {
|
||||||
|
padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-detail-pay-bar {
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.order-card {
|
.order-card {
|
||||||
background: var(--color-card);
|
background: var(--color-card);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
|
|||||||
Reference in New Issue
Block a user