front/src/utils/headerFilterLimits.ts
zzh f1d219ae48 feat: ship billing UI for additional services, conditions, and fee insights
Add product/channel billing condition editors, place-order additional services, billing simulator, forecast-to-inbound fee delta and margin warnings, insurance-aware claim tips, OMS preset dict seeds, and related AI skills/system pages.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 18:09:22 +08:00

97 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 表头批量/筛选护栏 + 订单列表默认时间窗(前后端应对齐)
*/
import dayjs, { type Dayjs } from 'dayjs'
/** 批量单号 / IN 值上限 */
export const MAX_HEADER_BATCH_VALUES = 500
/** 单个筛选值最大字符数 */
export const MAX_HEADER_FILTER_VALUE_LEN = 128
/** filters[] 最大条数(前端组装时裁剪) */
export const MAX_HEADER_FILTERS = 20
/** 订单列表默认仅查近 N 天(无精确单号时) */
export const ORDER_LIST_DEFAULT_DAYS = 90
/** 导出最大行数(与后端一致) */
export const ORDER_EXPORT_MAX_ROWS = 10000
export function clampBatchValues(values: string[], max = MAX_HEADER_BATCH_VALUES): {
values: string[]
truncated: boolean
originalCount: number
} {
const unique = Array.from(
new Set(
values
.map((v) => String(v || '').trim().slice(0, MAX_HEADER_FILTER_VALUE_LEN))
.filter(Boolean),
),
)
if (unique.length <= max) {
return { values: unique, truncated: false, originalCount: unique.length }
}
return {
values: unique.slice(0, max),
truncated: true,
originalCount: unique.length,
}
}
export function normalizeBatchInputText(text: string, max = MAX_HEADER_BATCH_VALUES): {
value: string
truncated: boolean
originalCount: number
keptCount: number
} {
const lines = String(text || '')
.split(/[\n\r]+/)
.flatMap((line) => line.split(/[,,\t;;]/))
.map((v) => v.trim())
.filter(Boolean)
const { values, truncated, originalCount } = clampBatchValues(lines, max)
return {
value: values.join(','),
truncated,
originalCount,
keptCount: values.length,
}
}
export function buildDefaultCreateTimeRange(
days = ORDER_LIST_DEFAULT_DAYS,
): [Dayjs, Dayjs] {
const end = dayjs().endOf('day')
const start = dayjs()
.subtract(Math.max(days, 1) - 1, 'day')
.startOf('day')
return [start, end]
}
export function formatCreateTimeRangeParam(
range: (Date | string | Dayjs)[] | undefined | null,
): { createTimeStart: string; createTimeEnd: string } | null {
if (!range || range.length < 2 || !range[0] || !range[1]) return null
return {
createTimeStart: dayjs(range[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
createTimeEnd: dayjs(range[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
}
}
/** 是否与「默认近 N 天」窗口一致(允许差 1 分钟内) */
export function isDefaultCreateTimeRange(
range: (Date | string | Dayjs)[] | undefined | null,
days = ORDER_LIST_DEFAULT_DAYS,
): boolean {
if (!range || range.length < 2 || !range[0] || !range[1]) return false
const [defStart, defEnd] = buildDefaultCreateTimeRange(days)
const start = dayjs(range[0])
const end = dayjs(range[1])
return (
Math.abs(start.startOf('day').diff(defStart, 'minute')) < 2 &&
Math.abs(end.endOf('day').diff(defEnd, 'minute')) < 2
)
}