Merge branch 'dev'

This commit is contained in:
2026-11-02 12:04:32 +08:00
38 changed files with 1615 additions and 942 deletions
+39 -8
View File
@@ -1,9 +1,10 @@
/**
* 一次性补资料链接的路径解析。
*
* 公开链接形如:`https://<域名>/supplement/<token>`,与后台 HashRouter 完全隔离。
* 命名空间判定用于路由分发:`/supplement` 及子路径整段交给公开页,
* 避免落到后台路由或打手端 SPA。
* 公开链接有两种形态:
* - `https://<域名>/supplement/`:输入平台订单号;
* - `https://<域名>/supplement/<平台订单号>`:直接打开对应订单的补资料页。
* 与后台 HashRouter 完全隔离。
*/
export function isCollectMaterialLinkPath(pathname: unknown): boolean {
@@ -11,14 +12,44 @@ export function isCollectMaterialLinkPath(pathname: unknown): boolean {
}
/**
* 只认 `/supplement/<一段 token>`:
* 只认 `/supplement/<一段 code>`:
* - `/supplement/abc` 命中,结尾多余斜杠容忍;
* - `/supplement`、`/supplement/a/b` 不命中(页面展示链接无效)。
* - `/supplement`、`/supplement/a/b` 不命中(由入口页/页面自己展示无效)。
*/
export function resolveCollectMaterialLinkToken(pathname: unknown): string {
export function resolveCollectMaterialLinkCode(pathname: unknown): string {
const segments = splitPathSegments(pathname)
if (segments.length !== 2 || segments[0] !== 'supplement') return ''
return decodeTokenSegment(segments[1])
return decodePathSegment(segments[1])
}
/** 统一拼补资料链接;origin 结尾多余斜杠会被去掉。 */
export function buildCollectMaterialLinkUrl(origin: unknown, code: unknown): string {
const base = String(origin ?? '')
.trim()
.replace(/\/+$/, '')
const normalized = String(code ?? '').trim()
if (!base || !normalized) return ''
return `${base}/supplement/${encodeURIComponent(normalized)}`
}
/**
* 入口页输入归一化:去空格;如果用户粘贴了完整 `/supplement/<订单号>` 链接,
* 自动提取最后一段,避免把整条 URL 当成订单号。
*/
export function normalizeOrderNoInput(value: unknown): string {
const raw = String(value ?? '').trim()
if (!raw) return ''
const marker = '/supplement/'
const index = raw.indexOf(marker)
if (index < 0) return raw
const segment = raw
.slice(index + marker.length)
.split(/[?#]/)[0]
.replace(/\/+$/, '')
.trim()
return decodePathSegment(segment)
}
function splitPathSegments(pathname: unknown): string[] {
@@ -28,7 +59,7 @@ function splitPathSegments(pathname: unknown): string[] {
return normalized.split(/[?#]/)[0].replace(/\/+$/, '').split('/').filter(Boolean)
}
function decodeTokenSegment(segment: string): string {
function decodePathSegment(segment: string): string {
try {
return decodeURIComponent(segment)
} catch {
@@ -0,0 +1,24 @@
/**
* 打手验收前置判断:拼单订单缺份额时是否应该拦截。
*
* 管理端「指派打手」是整单指派,不会创建 work_order_shares;
* 这类订单虽然 sharing.enabled=true,但接单人就是当前打手,
* 必须按整单验收放行,否则会被前端防呆误拦。
*/
export type AcceptanceShareGuardInput = {
shareCount: number
sharingEnabled: boolean
assignedWorkerId: number | null | undefined
currentWorkerId: number
}
export function shouldBlockAcceptanceForMissingShares(input: AcceptanceShareGuardInput): boolean {
if (input.shareCount > 0) return false
if (input.sharingEnabled !== true) return false
const currentWorkerId = Number(input.currentWorkerId || 0)
// 本地会话里拿不到打手 ID 时保持旧行为,避免误放行。
if (!Number.isFinite(currentWorkerId) || currentWorkerId <= 0) return true
return Number(input.assignedWorkerId || 0) !== currentWorkerId
}