23 lines
2.8 KiB
TypeScript
23 lines
2.8 KiB
TypeScript
import {createElement} from 'react';
|
||
import {ideas} from '../content/ideas';
|
||
import {Ref17Cover} from '../components/Ref17Cover';
|
||
import {SourceCover,selections} from '../components/SourceCover';
|
||
const record=(v:unknown):v is Record<string,unknown>=>!!v&&typeof v==='object'&&!Array.isArray(v);
|
||
function color(value:unknown,fallback:string){if(value===undefined)return fallback;if(typeof value!=='string'||!/^#[0-9a-f]{6}$/i.test(value))throw Error('颜色必须为 #RRGGBB');return value;}
|
||
function text(value:unknown,fallback:string,max:number,lines=1){if(value===undefined)return fallback;if(typeof value!=='string'||!value.trim()||Array.from(value).length>max||value.split('\n').length>lines)throw Error('文字长度或行数超限');return value;}
|
||
function keys(input:Record<string,unknown>,allowed:string[]){if(Object.keys(input).some(k=>!allowed.includes(k)))throw Error('包含不支持的参数');}
|
||
export function renderCover(slug:string,input:unknown){
|
||
if(!record(input))throw Error('需要 JSON 对象');
|
||
if(slug==='layout-selection'){
|
||
keys(input,['layoutId','fields','color','accent']);const layout=selections.find(l=>l.id===input.layoutId);if(!layout)throw Error('layoutId 必须为 ref-01、ref-07 或 ref-26');
|
||
if(input.fields!==undefined&&!record(input.fields))throw Error('fields 必须为对象');const supplied=(input.fields||{}) as Record<string,unknown>,defaults=Object.fromEntries(Object.entries(layout.fields).filter(([,v])=>v!==undefined)) as Record<string,string>;
|
||
keys(supplied,Object.keys(defaults));const fields=Object.fromEntries(Object.entries(defaults).map(([key,value])=>[key,text(supplied[key],value,120,4)]));
|
||
const values={fields,color:color(input.color,'#83918a'),accent:color(input.accent,'#c3cec5')};return {node:createElement(SourceCover,{layout,values}),data:{version:3,tool:slug,layoutId:layout.id,template:layout,...values}};
|
||
}
|
||
const idea=ideas.find(i=>i.slug===slug);if(!idea?.template)throw Error('这个方案不支持封面渲染');keys(input,['slide','heading','body','color','accent','masthead']);
|
||
const index=input.slide??0;if(typeof index!=='number'||!Number.isInteger(index)||!idea.slides[index])throw Error('slide 必须是 0–3 的整数');const initial=idea.slides[index];
|
||
const slide={...initial,heading:text(input.heading,initial.heading,6),body:text(input.body,initial.body,360,3),color:color(input.color,initial.color),accent:color(input.accent,initial.accent!)};
|
||
const masthead=input.masthead??idea.template.masthead;if(!Array.isArray(masthead)||masthead.length!==2||!masthead.every(v=>typeof v==='string'&&/^[A-Za-z ]{1,8}$/.test(v)&&v.trim()))throw Error('masthead 为两行、每行 1–8 个英文字母或空格');
|
||
return {node:createElement(Ref17Cover,{template:idea.template,slide,masthead}),data:{version:2,tool:slug,template:{...idea.template,masthead},slide}};
|
||
}
|