You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
6.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 将设计文档中的 branding_json 拆为表单字段,供运营可视化编辑。
*/
export interface BrandingForm {
documentTitle: string
login: {
markLine: string
headline: string
slogan: string
cardWelcome: string
footerCopyright: string
logoUrl: string
faviconUrl: string
themePrimary: string
}
apply: {
pageTitle: string
headerSubtitle: string
}
review: {
pageTitle: string
footerCopyright: string
}
admin: {
loginTitle: string
footerCopyright: string
sidebarProductName: string
}
}
export function emptyBrandingForm(): BrandingForm {
return {
documentTitle: '',
login: {
markLine: '',
headline: '',
slogan: '',
cardWelcome: '',
footerCopyright: '',
logoUrl: '',
faviconUrl: '',
themePrimary: '',
},
apply: { pageTitle: '', headerSubtitle: '' },
review: { pageTitle: '', footerCopyright: '' },
admin: { loginTitle: '', footerCopyright: '', sidebarProductName: '' },
}
}
function asRecord(v: unknown): Record<string, unknown> {
return v !== null && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {}
}
/** 去掉零宽字符与首尾空白 */
export function normalizeDisplayWhitespace(s: string): string {
return s.replace(/[\u200B-\u200D\uFEFF]/gu, '').trim()
}
/** 是否有可见字符(用于未配置、仅空格或零宽符时不覆盖端上默认文案) */
export function hasVisibleText(s: string): boolean {
return /\S/u.test(normalizeDisplayWhitespace(s))
}
function str(r: Record<string, unknown>, key: string): string {
const x = r[key]
if (x == null) return ''
if (typeof x === 'string') return x
if (typeof x === 'number' && Number.isFinite(x)) return String(x)
return ''
}
export function brandingFormFromApi(json: unknown | null | undefined): BrandingForm {
const b = emptyBrandingForm()
if (json == null) return b
let parsed: unknown = json
if (typeof parsed === 'string') {
const t = parsed.trim()
if (!t) return b
try {
parsed = JSON.parse(t) as unknown
} catch {
return b
}
}
const root = asRecord(parsed)
b.documentTitle = normalizeDisplayWhitespace(str(root, 'documentTitle'))
const login = asRecord(root.login)
b.login.markLine = normalizeDisplayWhitespace(str(login, 'markLine'))
b.login.headline = normalizeDisplayWhitespace(str(login, 'headline'))
b.login.slogan = normalizeDisplayWhitespace(str(login, 'slogan'))
b.login.cardWelcome = normalizeDisplayWhitespace(str(login, 'cardWelcome'))
b.login.footerCopyright = normalizeDisplayWhitespace(str(login, 'footerCopyright'))
b.login.logoUrl = normalizeDisplayWhitespace(str(login, 'logoUrl'))
b.login.faviconUrl = normalizeDisplayWhitespace(str(login, 'faviconUrl'))
b.login.themePrimary = normalizeDisplayWhitespace(str(login, 'themePrimary'))
const apply = asRecord(root.apply)
b.apply.pageTitle = normalizeDisplayWhitespace(str(apply, 'pageTitle'))
b.apply.headerSubtitle = normalizeDisplayWhitespace(str(apply, 'headerSubtitle'))
const review = asRecord(root.review)
b.review.pageTitle = normalizeDisplayWhitespace(str(review, 'pageTitle'))
b.review.footerCopyright = normalizeDisplayWhitespace(str(review, 'footerCopyright'))
const admin = asRecord(root.admin)
b.admin.loginTitle = normalizeDisplayWhitespace(str(admin, 'loginTitle'))
b.admin.footerCopyright = normalizeDisplayWhitespace(str(admin, 'footerCopyright'))
b.admin.sidebarProductName = normalizeDisplayWhitespace(str(admin, 'sidebarProductName'))
return b
}
/** 选手端报名页主标题(导航栏 / 表单大标题共用) */
export function participantApplyPageTitle(brand: BrandingForm, competitionName: string): string {
const a = brand.apply?.pageTitle
if (hasVisibleText(a)) return normalizeDisplayWhitespace(a)
const n = normalizeDisplayWhitespace(competitionName)
return n ? `${n} · 赛事报名` : '赛事报名'
}
/** 评审工作台顶栏标题(对齐 branding.review.pageTitle) */
export function reviewWorkspacePageTitle(brand: BrandingForm, competitionName: string): string {
const r = brand.review?.pageTitle
if (hasVisibleText(r)) return normalizeDisplayWhitespace(r)
const n = normalizeDisplayWhitespace(competitionName)
return n ? `${n} · 项目评审` : '项目评审'
}
/** 去掉空字符串键,减少存储体积 */
export function brandingJsonFromForm(form: BrandingForm): Record<string, unknown> | null {
const out: Record<string, unknown> = {}
if (form.documentTitle.trim()) out.documentTitle = form.documentTitle.trim()
const login: Record<string, string> = {}
if (form.login.markLine.trim()) login.markLine = form.login.markLine.trim()
if (form.login.headline.trim()) login.headline = form.login.headline.trim()
if (form.login.slogan.trim()) login.slogan = form.login.slogan.trim()
if (form.login.cardWelcome.trim()) login.cardWelcome = form.login.cardWelcome.trim()
if (form.login.footerCopyright.trim()) login.footerCopyright = form.login.footerCopyright.trim()
if (form.login.logoUrl.trim()) login.logoUrl = form.login.logoUrl.trim()
if (form.login.faviconUrl.trim()) login.faviconUrl = form.login.faviconUrl.trim()
if (form.login.themePrimary.trim()) login.themePrimary = form.login.themePrimary.trim()
if (Object.keys(login).length) out.login = login
const apply: Record<string, string> = {}
if (form.apply.pageTitle.trim()) apply.pageTitle = form.apply.pageTitle.trim()
if (form.apply.headerSubtitle.trim()) apply.headerSubtitle = form.apply.headerSubtitle.trim()
if (Object.keys(apply).length) out.apply = apply
const review: Record<string, string> = {}
if (form.review.pageTitle.trim()) review.pageTitle = form.review.pageTitle.trim()
if (form.review.footerCopyright.trim()) review.footerCopyright = form.review.footerCopyright.trim()
if (Object.keys(review).length) out.review = review
const admin: Record<string, string> = {}
if (form.admin.loginTitle.trim()) admin.loginTitle = form.admin.loginTitle.trim()
if (form.admin.footerCopyright.trim()) admin.footerCopyright = form.admin.footerCopyright.trim()
if (form.admin.sidebarProductName.trim()) admin.sidebarProductName = form.admin.sidebarProductName.trim()
if (Object.keys(admin).length) out.admin = admin
return Object.keys(out).length ? out : null
}