front/src/utils/statusTag.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

146 lines
4.5 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.

/**
* 列表状态标签:按信息强弱分档(低饱和墨色),对齐 OMS 主色 #007979。
*
* 强弱(弱 → 强):
* - mute 弱信息:否 / 关闭 / 删除 / 无效
* - neutral 基线:草稿 / 普通 / 未知
* - brand 流程推进:进行中 / 已启用
* - wait 待办提醒:待处理 / 待审核 / 预警中
* - ok 正向终态:成功 / 已完成
* - alert 负向终态:失败 / 问题 / 超时
* 布尔「是」单独用 BOOLEAN_YES_TAG 高亮绿,不走上述分档。
*/
export type StatusTagTone = 'mute' | 'neutral' | 'brand' | 'wait' | 'ok' | 'alert'
export interface StatusTagColors {
tagColor: string
tagBgColor: string
tagBorder: string
/** 可选行底 tint(极淡) */
rowBg?: string
}
export const STATUS_TAG_TONES: Record<StatusTagTone, StatusTagColors> = {
mute: {
tagColor: '#8b939b',
tagBgColor: '#f7f8f9',
tagBorder: '#e7e9eb',
},
neutral: {
tagColor: '#5c656d',
tagBgColor: '#f3f4f5',
tagBorder: '#e2e4e6',
},
brand: {
tagColor: '#1a6b6b',
tagBgColor: '#eef5f5',
tagBorder: '#d2e3e3',
},
wait: {
tagColor: '#7a6a52',
tagBgColor: '#f5f2ed',
tagBorder: '#e4ddd2',
rowBg: '#faf8f5',
},
ok: {
tagColor: '#456654',
tagBgColor: '#f0f4f1',
tagBorder: '#d6e0d9',
},
alert: {
tagColor: '#8a5c58',
tagBgColor: '#f5f0ef',
tagBorder: '#e4d9d7',
rowBg: '#faf6f5',
},
}
export function getStatusTagTone(tone: StatusTagTone): StatusTagColors {
return STATUS_TAG_TONES[tone]
}
export type StatusTagVariant = 'solid' | 'ghost'
function escapeAttr(text: string): string {
return String(text)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
}
/** Vue / a-tag 内联样式 */
export function getStatusTagStyle(
toneOrColors: StatusTagTone | StatusTagColors | null | undefined,
variant: StatusTagVariant = 'solid',
): Record<string, string> {
const colors =
!toneOrColors
? STATUS_TAG_TONES.neutral
: typeof toneOrColors === 'string'
? STATUS_TAG_TONES[toneOrColors]
: toneOrColors
if (variant === 'ghost') {
return {
color: colors.tagColor,
background: 'transparent',
border: `1px solid ${colors.tagBorder}`,
borderRadius: '3px',
fontWeight: '500',
}
}
return {
color: colors.tagColor,
background: colors.tagBgColor,
border: `1px solid ${colors.tagBorder}`,
borderRadius: '3px',
fontWeight: '500',
}
}
/** Handsontable 单元格 HTML */
export function buildStatusTagHtml(
text: string,
toneOrColors: StatusTagTone | StatusTagColors | null | undefined = 'neutral',
variant: StatusTagVariant = 'solid',
): string {
const colors =
!toneOrColors
? STATUS_TAG_TONES.neutral
: typeof toneOrColors === 'string'
? STATUS_TAG_TONES[toneOrColors]
: toneOrColors
const safe = escapeAttr(text)
if (variant === 'ghost') {
return `<span style="display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:3px;font-size:11px;font-weight:500;line-height:18px;color:${colors.tagColor};background:transparent;border:1px solid ${colors.tagBorder};white-space:nowrap;">${safe}</span>`
}
return `<span style="display:inline-flex;align-items:center;height:22px;padding:0 8px;border-radius:3px;font-size:12px;font-weight:500;line-height:20px;letter-spacing:0.02em;color:${colors.tagColor};background:${colors.tagBgColor};border:1px solid ${colors.tagBorder};white-space:nowrap;">${safe}</span>`
}
/** 布尔「是」专用高亮绿(比状态档 ok 更醒目) */
export const BOOLEAN_YES_TAG: StatusTagColors = {
tagColor: '#1a7f37',
tagBgColor: '#e6f7eb',
tagBorder: '#a8d9b5',
}
/** 布尔:是 → 高亮绿,否 → mute(弱信息) */
export function buildBooleanTagHtml(text: string, positive: boolean): string {
return buildStatusTagHtml(text, positive ? BOOLEAN_YES_TAG : 'mute')
}
/** Vue / a-tag:布尔列样式 */
export function getBooleanTagStyle(positive: boolean): Record<string, string> {
return getStatusTagStyle(positive ? BOOLEAN_YES_TAG : 'mute')
}
/** 金额正负:仅改文字色,不做标签(避免抢状态列) */
export function getMoneyToneColor(positive: boolean): string {
return positive ? STATUS_TAG_TONES.ok.tagColor : STATUS_TAG_TONES.alert.tagColor
}
/** 强调文字色(超时/预警行内数字等) */
export function getEmphasisTextColor(tone: StatusTagTone = 'alert'): string {
return STATUS_TAG_TONES[tone].tagColor
}