forked from natuka/web.puabadge.com
新增:京东下单页面
This commit is contained in:
1 parent
8e8cde9ef5
commit
007c675603
25 files changed
+3264
-25
No files matched your search
@@ -0,0 +1,103 @@
|
||||
export type FlyRect = {
|
||||
left: number
|
||||
top: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
function prefersReducedMotion() {
|
||||
return typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
}
|
||||
|
||||
/** 二次贝塞尔:起点 → 控制点 → 终点 */
|
||||
function quad(p0: number, p1: number, p2: number, t: number) {
|
||||
const u = 1 - t
|
||||
return u * u * p0 + 2 * u * t * p1 + t * t * p2
|
||||
}
|
||||
|
||||
function easeInOut(t: number) {
|
||||
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
|
||||
}
|
||||
|
||||
/** 将图片从源矩形沿抛物线飞到目标矩形,动画结束后移除 */
|
||||
export function flyImageToRect(url: string, from: FlyRect, to: FlyRect, duration = 860): Promise<void> {
|
||||
if (typeof document === 'undefined' || !url || from.width <= 0 || from.height <= 0) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (prefersReducedMotion()) return Promise.resolve()
|
||||
|
||||
const ghost = document.createElement('img')
|
||||
ghost.src = url
|
||||
ghost.alt = ''
|
||||
ghost.className = 'jd-img-fly'
|
||||
ghost.setAttribute('aria-hidden', 'true')
|
||||
Object.assign(ghost.style, {
|
||||
position: 'fixed',
|
||||
left: `${from.left}px`,
|
||||
top: `${from.top}px`,
|
||||
width: `${from.width}px`,
|
||||
height: `${from.height}px`,
|
||||
margin: '0',
|
||||
padding: '0',
|
||||
border: '0',
|
||||
borderRadius: '12px',
|
||||
objectFit: 'contain',
|
||||
objectPosition: 'center',
|
||||
background: '#e8eaed',
|
||||
zIndex: '10000',
|
||||
pointerEvents: 'none',
|
||||
boxShadow: '0 12px 28px rgba(15, 23, 42, 0.22)',
|
||||
transformOrigin: 'top left',
|
||||
willChange: 'transform, opacity, border-radius',
|
||||
} as CSSStyleDeclaration)
|
||||
|
||||
document.body.appendChild(ghost)
|
||||
|
||||
const sxEnd = to.width / Math.max(1, from.width)
|
||||
const syEnd = to.height / Math.max(1, from.height)
|
||||
const dx = to.left - from.left
|
||||
const dy = to.top - from.top
|
||||
|
||||
const lift = Math.min(160, Math.max(72, Math.hypot(dx, dy) * 0.28))
|
||||
const cx = dx * 0.5
|
||||
const cy = dy * 0.5 - lift
|
||||
|
||||
const steps = 18
|
||||
const keyframes: Keyframe[] = []
|
||||
for (let i = 0; i <= steps; i += 1) {
|
||||
const raw = i / steps
|
||||
const t = easeInOut(raw)
|
||||
const x = quad(0, cx, dx, t)
|
||||
const y = quad(0, cy, dy, t)
|
||||
const scaleX = 1 + (sxEnd - 1) * t
|
||||
const scaleY = 1 + (syEnd - 1) * t
|
||||
const radius = 12 + (10 - 12) * t
|
||||
keyframes.push({
|
||||
offset: raw,
|
||||
transform: `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`,
|
||||
opacity: String(1 - 0.08 * t),
|
||||
borderRadius: `${radius}px`,
|
||||
})
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const finish = () => {
|
||||
ghost.remove()
|
||||
resolve()
|
||||
}
|
||||
const anim = ghost.animate(keyframes, {
|
||||
duration,
|
||||
easing: 'linear',
|
||||
fill: 'forwards',
|
||||
})
|
||||
anim.onfinish = finish
|
||||
anim.oncancel = finish
|
||||
})
|
||||
}
|
||||
|
||||
export function rectFromElement(el: Element | null | undefined): FlyRect | null {
|
||||
if (!el) return null
|
||||
const box = el.getBoundingClientRect()
|
||||
if (box.width <= 0 || box.height <= 0) return null
|
||||
return { left: box.left, top: box.top, width: box.width, height: box.height }
|
||||
}
|
||||
+53
-18
@@ -69,30 +69,65 @@ const requestHandler = (config: InternalAxiosRequestConfig): InternalAxiosReques
|
||||
// Add a request interceptor
|
||||
request.interceptors.request.use(requestHandler, errorHandler)
|
||||
|
||||
function coerceJsonPayload(payload: any) {
|
||||
// 后端若用 text/plain 返回 JSON,axios 不会自动 parse,这里补一层
|
||||
if (typeof payload !== 'string') return payload
|
||||
const text = payload.replace(/^\uFEFF/, '').trim()
|
||||
if (!text) return payload
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch {
|
||||
const start = text.indexOf('{')
|
||||
const end = text.lastIndexOf('}')
|
||||
if (start >= 0 && end > start) {
|
||||
try {
|
||||
return JSON.parse(text.slice(start, end + 1))
|
||||
} catch {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
// 响应拦截器
|
||||
const responseHandler = (response: { data: any }) => {
|
||||
if (!response?.data)
|
||||
if (!response?.data && response?.data !== 0 && response?.data !== false)
|
||||
return response
|
||||
|
||||
const { code, data, message } = response.data
|
||||
|
||||
if (!code)
|
||||
return response.data
|
||||
|
||||
// 拦截时间
|
||||
switch (code) {
|
||||
case 1000:
|
||||
if (Array.isArray(data)) {
|
||||
// transfter(data, timezone.info);
|
||||
} else if (data && data.list !== undefined && data.list?.length > 0) {
|
||||
// transfter(data.list, timezone.info);
|
||||
}
|
||||
return data
|
||||
default:
|
||||
return Promise.reject(new Error(message))
|
||||
// 兼容偶发双层:{ data: { code, data, message } }
|
||||
let payload = coerceJsonPayload(response.data)
|
||||
if (
|
||||
payload &&
|
||||
typeof payload === 'object' &&
|
||||
payload.code == null &&
|
||||
payload.data &&
|
||||
typeof payload.data === 'object' &&
|
||||
payload.data.code != null
|
||||
) {
|
||||
payload = payload.data
|
||||
}
|
||||
|
||||
return response.data
|
||||
if (!payload || typeof payload !== 'object')
|
||||
return payload
|
||||
|
||||
const { code, data, message } = payload
|
||||
|
||||
if (code == null || code === '')
|
||||
return payload
|
||||
|
||||
// 兼容 code 为字符串 "1000"
|
||||
const numericCode = Number(code)
|
||||
if (numericCode === 1000) {
|
||||
if (Array.isArray(data)) {
|
||||
// transfter(data, timezone.info);
|
||||
} else if (data && data.list !== undefined && data.list?.length > 0) {
|
||||
// transfter(data.list, timezone.info);
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(message || '请求失败'))
|
||||
}
|
||||
|
||||
// Add a response interceptor
|
||||
|
||||
Reference in new issue
Block a user