@agile-team/mach-report 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -113,13 +113,13 @@ export {
113
113
  k as M,
114
114
  K as P,
115
115
  N as S,
116
- G as a,
117
- j as b,
118
- Z as c,
119
- X as d,
120
- V as e,
121
- q as f,
122
- v as g,
116
+ v as a,
117
+ G as b,
118
+ j as c,
119
+ Z as d,
120
+ X as e,
121
+ V as f,
122
+ q as g,
123
123
  B as h,
124
124
  b as i,
125
125
  I as j,
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("./grid-geometry-DBH9ExuY.cjs"),x=require("./textwrap-DqiFwAV3.cjs"),Qe=require("./page-window-index-DEjnM6IB.cjs"),et=require("./report-params-D1gZ1DME.cjs");function ye(e){if(!e||e.kind==="date")return;const t=e.digits;if(!(t!==void 0&&(!Number.isInteger(t)||t<0||t>100))){if(e.kind==="percent"){const n=t??2;return`0${n?`.${"0".repeat(n)}`:""}%`}return t===void 0?e.thousands?"#,##0.###############":void 0:`${e.thousands?"#,##0":"0"}${t?`.${"0".repeat(t)}`:""}`}}function Oe(e){const t=/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})$/.exec(e.trim());if(t){const r=new Date(Number(t[1]),Number(t[2])-1,Number(t[3]));return Number.isNaN(r.getTime())?null:r}const n=new Date(e);return Number.isNaN(n.getTime())?null:n}function pe(e,t){return e.toLocaleString("en-US",{minimumFractionDigits:t,maximumFractionDigits:t})}function tt(e,t){const n=r=>String(r).padStart(2,"0");return t.replace(/YYYY/g,String(e.getFullYear())).replace(/MM/g,n(e.getMonth()+1)).replace(/DD/g,n(e.getDate())).replace(/hh/g,n(e.getHours())).replace(/mm/g,n(e.getMinutes())).replace(/ss/g,n(e.getSeconds()))}function nt(e,t,n){return n!=null?t?pe(e,n):e.toFixed(n):t?pe(e):String(e)}function de(e){const t=typeof e=="number"?e:Number(String(e).replace(/,/g,""));return Number.isFinite(t)?t:null}function Me(e,t){if(e==null||e===""||!t)return String(e??"");switch(t.kind){case"number":{const n=de(e);return n==null?String(e):nt(n,t.thousands===!0,t.digits)}case"percent":{const n=de(e);return n==null?String(e):`${pe(n*100,t.digits??2)}%`}case"date":{const n=t.pattern??"YYYY-MM-DD",r=e instanceof Date?e:Oe(String(e));return r?tt(r,n):String(e)}default:return String(e)}}function Fe(e,t){const n=e[t.field];if(n==null||n==="")return!1;if(typeof t.value=="number"){const s=de(n);if(s==null)return!1;switch(t.op){case">":return s>t.value;case">=":return s>=t.value;case"<":return s<t.value;case"<=":return s<=t.value;case"==":return s===t.value;case"!=":return s!==t.value}}const r=String(n),o=String(t.value);switch(t.op){case"==":return r===o;case"!=":return r!==o;case">":return r>o;case">=":return r>=o;case"<":return r<o;case"<=":return r<=o}return!1}const rt=new Set(["text","image","barcode","qrcode","chart","line","rect","ellipse"]);function K(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ot(e,t,n,r){const o=e[t];(typeof o!="number"||!Number.isFinite(o))&&r.push({path:`${n}.${t}`,message:`应为有限数字,实际 ${JSON.stringify(o)}`})}function he(e,t,n,r=[]){if(!K(e)){n.push({path:t,message:"组件应为对象"});return}const o=e.kind;typeof o!="string"?n.push({path:`${t}.kind`,message:"组件缺少 kind 字符串"}):rt.has(o)||r.push({path:`${t}.kind`,message:`未知组件类型 ${JSON.stringify(o)},将以兜底方式渲染`});for(const a of["leftMm","topMm","widthMm","heightMm"])ot(e,a,t,n);typeof e.leftMm=="number"&&e.leftMm<-100&&n.push({path:`${t}.leftMm`,message:"几何数值异常(leftMm<-100)"});for(const a of["widthMm","heightMm"])typeof e[a]=="number"&&e[a]<0&&n.push({path:`${t}.${a}`,message:"组件尺寸不能为负数"});const s=e.grid;s!==void 0&&(!K(s)||!Array.isArray(s.cells)?n.push({path:`${t}.grid.cells`,message:"grid.cells 应为二维数组"}):s.cells.forEach((a,l)=>{if(!Array.isArray(a)){n.push({path:`${t}.grid.cells.${l}`,message:"行应为数组"});return}a.forEach((c,f)=>{c!=null&&K(c)&&Array.isArray(c.children)&&c.children.forEach((h,m)=>{he(h,`${t}.grid.cells.${l}.${f}.children.${m}`,n,r)})})}));const i=e.richParagraphs;i!==void 0&&(Array.isArray(i)?i.forEach((a,l)=>{(!K(a)||!Array.isArray(a.segments))&&n.push({path:`${t}.richParagraphs.${l}`,message:"段落缺少 segments 数组"})}):n.push({path:`${t}.richParagraphs`,message:"richParagraphs 应为数组"}))}function Be(e,t,n,r=[]){if(!K(e)){n.push({path:t,message:"页应为对象"});return}const o=e.pageWidthMm,s=e.pageHeightMm;(typeof o!="number"||!(o>0&&o<3e3))&&n.push({path:`${t}.pageWidthMm`,message:`页宽非法: ${JSON.stringify(o)}`}),(typeof s!="number"||!(s>0&&s<5e3))&&n.push({path:`${t}.pageHeightMm`,message:`页高非法: ${JSON.stringify(s)}`});const i=e.components;Array.isArray(i)?Array.isArray(i)&&i.forEach((a,l)=>he(a,`${t}.components.${l}`,n,r)):n.push({path:`${t}.components`,message:"components 应为数组"})}function be(e){const t=[],n=[],r=K(e)?e.pages:void 0;if(!Array.isArray(r))t.push({path:"$.pages",message:e==null?"渲染计划为空":"渲染计划缺少 pages 数组"});else{r.forEach((s,i)=>Be(s,`$.pages.${i}`,t,n)),r.length>2e3&&n.push({path:"$.pages",message:`页数异常大(${r.length} 页),渲染与打印耗时可能显著增加`});const o=r.reduce((s,i)=>s+(K(i)&&Array.isArray(i.components)?i.components.length:0),0);o>5e4&&n.push({path:"$.pages",message:`组件总数异常大(${o} 个),可能触发浏览器性能限制`})}return{ok:t.length===0,errors:t,warnings:n}}function it(e){const t=be(e);if(!t.ok){const n=t.errors.slice(0,3).map(r=>`${r.path}: ${r.message}`).join("; ");throw new Error(`渲染计划校验失败(${t.errors.length} 处): ${n}`)}return e.pages}function Re(e){return{widthMm:e.widthMm,heightMm:e.heightMm,marginTopMm:e.marginTopMm??0,marginBottomMm:e.marginBottomMm??0,marginLeftMm:e.marginLeftMm??0,marginRightMm:e.marginRightMm??0,components:[]}}function st(e,t){return t.heightMm-(e.marginBottomMm??t.marginBottomMm)}function Le(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number")return String(e);if(typeof e=="boolean")return e?"是":"否";if(e instanceof Date)return e.toISOString().slice(0,10);try{return JSON.stringify(e)}catch{return String(e)}}function Ie(e,t){let n=e.textMetrics.get(t);if(!n){const r=g.resolveFontSize(t).fontSizePt;n={measureOptions:{fontSizePt:r,bold:t.bold,letterSpacingPt:t.letterSpacing===void 0?void 0:t.letterSpacing*72/96},lineMm:r*(t.lineHeight??g.LINE_HEIGHT)*25.4/72},e.textMetrics.set(t,n)}return n}function De(e,t){return t?.kind==="number"||t?.kind==="percent"?"right":t?.kind==="date"||e instanceof Date||typeof e=="boolean"?"center":typeof e=="number"&&Number.isFinite(e)?"right":"left"}function at(e){if(e===null||typeof e=="string"||typeof e=="boolean"||typeof e=="number"&&Number.isFinite(e))return e}function lt(e,t){const n=e.fontSizePt??g.DEFAULT_FONT_PT,r=e.paddingMm??1,o=e.columns.map(d=>d.align??d.style?.align??e.align??"left"),s=e.columns.map((d,p)=>({fontSize:n,verticalAlign:"middle",...d.style,align:o[p]==="auto"?"left":o[p]})),i=s.map((d,p)=>o[p]==="auto"?{left:d,center:{...d,align:"center"},right:{...d,align:"right"}}:void 0),a=e.columns.map((d,p)=>d.rules?.map(S=>{const T=i[p];if(T)return{left:{...T.left,...S.style},center:{...T.center,...S.style},right:{...T.right,...S.style}};const u={...s[p],...S.style};return{left:u,center:u,right:u}})),l=e.columns.map(d=>({fontSize:n,bold:e.headerBold??!0,align:"center",verticalAlign:"middle",backgroundColor:e.headerBackgroundColor,...d.style||{},...e.headerAlign===void 0?{}:{align:e.headerAlign},...e.headerStyle,...d.headerAlign===void 0?{}:{align:d.headerAlign},...d.headerStyle})),c=e.columns.map(d=>d.header);let f=e.rowHeightMm??0;for(let d=0;d<c.length;d++){const p=l[d],S=g.resolveFontSize(p).fontSizePt,T=x.wrapText(c[d],e.columns[d].widthMm-r*2,{fontSizePt:S,bold:p.bold},t),u=S*(p.lineHeight??g.LINE_HEIGHT)*25.4/72;f=Math.max(f,Math.max(T.length,1)*u+r*2)}const h=c.map((d,p)=>({text:d,style:l[p]})),m=e.border===!1?{borderTop:!1,borderRight:!1,borderBottom:!1,borderLeft:!1}:{borderBottom:!0,borderLeft:!0,borderRight:!0,borderTop:!0},w=e.columns.map(d=>d.widthMm);return{fontSizePt:n,paddingMm:r,cellStyles:s,autoStyles:i,ruleStyles:a,textMetrics:new WeakMap,numberFormats:e.columns.map(d=>ye(d.format)),borderStyle:m,headerHeightMm:f,headerGrid:{cells:[h],colWidthsMm:w,rowHeightsMm:[f]},colWidthsMm:w,columns:e.columns}}function Ce(e,t,n){const r=e.columns;let o=e.rowHeightFloorMm;const s=new Array(r.length);for(let i=0;i<r.length;i++){const a=r[i],l=t[a.field],c=e.autoStyles[i]?De(l,a.format):"left";let f=e.autoStyles[i]?.[c]??e.cellStyles[i];if(a.rules)for(let S=0;S<a.rules.length;S++){const T=a.rules[S];if(Fe(t,T.when)){f=e.ruleStyles[i][S][c];break}}const h=a.format?Me(l,a.format):Le(l),m=Ie(e,f),w=x.wrapText(h,a.widthMm-e.paddingMm*2,m.measureOptions,n);o=Math.max(o,w.length*m.lineMm+e.paddingMm*2);const d=at(l),p={text:h,style:f};d!==void 0&&(p.value=d),typeof d=="number"&&e.numberFormats[i]!==void 0&&(p.numberFormat=e.numberFormats[i]),s[i]=p}return{grid:{cells:[s],colWidthsMm:e.colWidthsMm,rowHeightsMm:[o]},heightMm:o}}function ze(e,t,n={}){const r=typeof n=="number"?{mmPerRow:n}:n,o=r.mmPerRow??0,s=r.measurer??x.heuristicMeasurer,i=[],a=[];let l=0;const c=h=>`${h}_${(++l).toString(36)}`,f=[];for(const h of e.pages){const m=Re(h),w=()=>st(h,m),d=m.marginTopMm,p=[],S=()=>{if(a.push({pageWidthMm:m.widthMm,pageHeightMm:m.heightMm,marginTopMm:m.marginTopMm,marginBottomMm:m.marginBottomMm,marginLeftMm:m.marginLeftMm,marginRightMm:m.marginRightMm,components:m.components}),p.length>0){const T=a.length;for(const u of p){const k={...u,text:String(u.text??"").replace(/\{page\}/g,String(T))};a[a.length-1].components.push(k),k.text.includes("{totalPages}")&&f.push(k)}}};for(const T of h.components){if(T.kind!=="list"){if(T.kind==="text"&&typeof T.text=="string"){const $=T.text;if($.includes("{page}")||$.includes("{totalPages}")){p.push(T);continue}}m.components.push(T);continue}const u=T,k=t[u.dataset];Array.isArray(k)||i.push(`数据集 ${u.dataset} 未提供数据,列表 ${u.id??""} 输出为空`);const A=Array.isArray(k)?k:[],v=u.columns.reduce(($,M)=>$+M.widthMm,0),V=v>u.widthMm?u.widthMm/v:1,y=V===1?u:{...u,columns:u.columns.map($=>({...$,widthMm:$.widthMm*V}))},E={...lt(y,s),rowHeightFloorMm:y.rowHeightMm??0},Z={kind:"rect",leftMm:u.leftMm,topMm:0,widthMm:u.widthMm,heightMm:E.headerHeightMm,grid:E.headerGrid,nid:c("listh"),style:E.borderStyle};let b=u.topMm;const H=($,M)=>{$.components.push({...Z,topMm:M})};H(m,b),b+=E.headerHeightMm+o;const D=$=>{if(b+$>w()){S();const M=Re(h);m.components=M.components,m.heightMm=M.heightMm,y.headerEveryPage!==!1?(H(m,u.topMm),b=u.topMm+E.headerHeightMm):b=d}},P=($,M,N)=>{const R=y.headerEveryPage!==!1?u.topMm+E.headerHeightMm:d,F=w()-R;M>F&&i.push(`列表 ${u.id??u.dataset} 行高 ${M.toFixed(2)}mm 超出单页可用高度 ${F.toFixed(2)}mm,预览与打印可能截断内容`),D(M),m.components.push({kind:"rect",leftMm:u.leftMm,topMm:b,widthMm:u.widthMm,heightMm:M,grid:$,nid:c(N),style:E.borderStyle}),b+=M},O=()=>Ce(E,{},s).heightMm,_=($,M,N,R)=>{const F=[],ie=Math.max(1,Math.min(R,y.columns.length)),Ne={fontSize:E.fontSizePt,...N};F.push({text:$,colSpan:ie,style:{bold:!0,...Ne}});for(let G=ie;G<y.columns.length;G++){const te=y.columns[G],ne=M.get(te.field),le=E.autoStyles[G]?.[De(ne?.value,te.format)]??E.cellStyles[G];F.push({...ne,text:ne?.text??"",style:{...le,...Ne}})}let se=E.rowHeightFloorMm,ae=0;for(const G of F){let te=0;const ne=ae+(G.colSpan??1);for(;ae<ne;ae++)te+=E.colWidthsMm[ae]??0;const le=Ie(E,G.style),Ze=x.wrapText(G.text??"",te-E.paddingMm*2,le.measureOptions,s);se=Math.max(se,Ze.length*le.lineMm+E.paddingMm*2)}return{grid:{cells:[F],colWidthsMm:E.colWidthsMm,rowHeightsMm:[se]},heightMm:se}},Y=($,M)=>{let N=0;for(const R of $){const F=typeof R[M]=="number"?R[M]:Number(R[M]);Number.isFinite(F)&&(N+=F)}return N},oe=($,M)=>{const R=y.columns.find(ie=>ie.field===M)?.format??{kind:"number",thousands:!0},F=Y($,M);return{text:Me(F,R),value:F,numberFormat:ye(R)}},j=y.groupBy,ke=$=>{const M=y.columns.findIndex(N=>$.includes(N.field));return M>0?M:1},ee=[];if(j){const $=new Map;for(const M of A){const N=Le(M[j.field]),R=$.get(N);R?R.push(M):$.set(N,[M])}for(const[M,N]of $){ee.push({kind:"groupHeader",label:(j.headerTemplate??"{value}").replace(/\{value\}/g,M)});for(const R of N)ee.push({kind:"row",row:R});j.subtotal&&j.subtotal.length>0&&ee.push({kind:"subtotal",label:j.subtotalLabel??"小计",rows:N})}}else for(const $ of A)ee.push({kind:"row",row:$});const xe=O();for(const $ of ee)if($.kind==="row"){const M=Ce(E,$.row,s);P(M.grid,M.heightMm,"listr")}else if($.kind==="groupHeader"){const M=_($.label,new Map,j?.headerStyle??{},y.columns.length);j?.keepWithNext!==!1&&b+M.heightMm+xe>w()&&D(M.heightMm+xe),P(M.grid,M.heightMm,"listg")}else if($.kind==="subtotal"){const M=new Map;for(const R of j.subtotal??[])M.set(R,oe($.rows,R));const N=_($.label,M,j?.subtotalStyle??{},ke(j.subtotal??[]));P(N.grid,N.heightMm,"listst")}if(y.grandTotal){const $=typeof y.grandTotal=="object"?y.grandTotal:void 0,M=$?.fields??j?.subtotal??[];if(M.length>0){const N=new Map;for(const F of M)N.set(F,oe(A,F));const R=_($?.label??"合计",N,$?.style??{},ke(M));P(R.grid,R.heightMm,"listgt")}else i.push(`列表 ${u.id??""} 配置了 grandTotal 但未提供合计字段(groupBy.subtotal 或 grandTotal.fields)`)}}S()}for(const h of f){const m=h.text;typeof m=="string"&&m.includes("{totalPages}")&&(h.text=m.replace(/\{totalPages\}/g,String(a.length)))}return{plan:{schemaVersion:x.RENDER_PLAN_SCHEMA_VERSION,pages:a},warnings:i}}const ct={root:"mr-root",page:"mr-page",comp:"mr-comp",text:"mr-text",lines:"mr-lines",line:"mr-line",rich:"mr-rich",paragraph:"mr-paragraph",segment:"mr-segment",image:"mr-image",shape:"mr-shape",grid:"mr-grid",row:"mr-row",cell:"mr-cell",unknown:"mr-unknown"};function L(e,t){return t?.classNames?.[e]??ct[e]}class W{constructor(){this.props=[]}add(t,n){return n==null||n===!1||n===""?this:(this.props.push([t,String(n)]),this)}mm(t,n,r,o=2){return this.add(t,`${g.round(g.mmToPx(n,r),o)}px`)}toString(){let t="";for(const[n,r]of this.props)t+=`${n}:${r};`;return t}applyTo(t){const n=this.toString();n&&(t.style.cssText=n)}}function je(e,t,n){const r=t.style||{};e.add("color",r.color).add("background-color",r.backgroundColor).add("font-family",r.fontFamily).add("font-weight",r.bold?"700":void 0).add("font-style",r.italic?"italic":void 0).add("text-decoration",r.underline?"underline":void 0).add("letter-spacing",r.letterSpacing!=null?`${r.letterSpacing}pt`:void 0).add("opacity",r.opacity!=null?String(r.opacity):void 0).add("transform",r.rotateDeg?`rotate(${r.rotateDeg}deg)`:void 0);const o=g.resolveFontSize(r,n);e.add("font-size",`${g.round(o.fontSizePx,2)}px`)}function dt(e,t,n,r,o){const s=o.createElement("table");if(s.className=L("grid",r),new W().add("width","100%").add("height","100%").add("border-collapse","collapse").add("table-layout",e.colWidthsMm?"fixed":"auto").applyTo(s),e.colWidthsMm){const c=o.createElement("colgroup");for(const f of e.colWidthsMm){const h=o.createElement("col");new W().mm("width",f,n).applyTo(h),c.appendChild(h)}s.appendChild(c)}const i=g.resolveBoxBorders(t.style),a=g.resolveStroke(t.style),l=i.inner?`${g.round(a.widthPt*n/72,2)}px solid ${a.color}`:"";return e.cells.forEach((c,f)=>{const h=o.createElement("tr");h.className=L("row",r),e.rowHeightsMm?.[f]!=null&&new W().mm("height",e.rowHeightsMm[f],n).applyTo(h),(c||[]).forEach(m=>{const w=o.createElement("td");w.className=L("cell",r),m?.colSpan&&(w.colSpan=m.colSpan),m?.rowSpan&&(w.rowSpan=m.rowSpan);const d=new W;if(l&&d.add("border",l),m?.style){const p=m.style;d.add("text-align",p.align).add("vertical-align",p.verticalAlign).add("background-color",p.backgroundColor).add("color",p.color).add("font-weight",p.bold?"700":void 0),d.add("font-size",`${g.round(g.resolveFontSize(p,n).fontSizePx,2)}px`)}if(d.applyTo(w),m?.text!=null&&(w.textContent=m.text),m?.children?.length)for(const p of m.children)w.appendChild(we(p,n,r,o));h.appendChild(w)}),s.appendChild(h)}),s}function mt(e,t,n,r){const o=r.createElement("div");o.className=L("text",n);const s=e.style||{},i=new W().add("text-align",s.align).add("display","flex").add("flex-direction","column").add("justify-content",s.verticalAlign==="middle"?"center":s.verticalAlign==="bottom"?"flex-end":"flex-start").add("overflow","hidden");if(je(i,e,t),i.applyTo(o),x.isRichText(e)){const a=r.createElement("div");a.className=L("rich",n);for(const l of e.richParagraphs){const c=r.createElement("div");c.className=L("paragraph",n),new W().add("text-align",l.align).add("line-height",l.lineHeight!=null?String(l.lineHeight):void 0).mm("margin-top",l.spaceBeforeMm??0,t).mm("margin-bottom",l.spaceAfterMm??0,t).mm("text-indent",l.indentMm??0,t).applyTo(c);for(const h of l.segments||[]){const m=r.createElement("span");if(m.className=L("segment",n),m.textContent=h.kind==="field"?h.field??"":h.text??"",h.style){const w=new W().add("color",h.style.color).add("font-weight",h.style.bold?"700":void 0);w.add("font-size",`${g.round(g.resolveFontSize(h.style,t).fontSizePx,2)}px`),w.applyTo(m)}c.appendChild(m)}a.appendChild(c)}return o.appendChild(a),o}if(Array.isArray(e.lines)&&e.lines.length>0){const a=r.createElement("div");a.className=L("lines",n),s.lineHeight!=null&&new W().add("line-height",String(s.lineHeight)).applyTo(a);for(const l of e.lines){const c=r.createElement("div");c.className=L("line",n),c.textContent=l,a.appendChild(c)}return o.appendChild(a),o}return o.textContent=e.text??"",o}function ft(e,t,n){const r=n.createElement("img");return r.className=L("image",t),r.src=e.imageData,new W().add("width","100%").add("height","100%").add("object-fit","contain").add("display","block").applyTo(r),r.alt=e.kind,r}function _e(e,t,n,r){const o=r.createElement("div");o.className=L("shape",n);const s=e.style||{},i=g.resolveStroke(s),a=g.resolveBoxBorders(s),l=`${g.round(i.widthPt*t/72,2)}px solid ${i.color}`,c=new W;return e.grid||e.kind==="rect"?(a.top&&c.add("border-top",l),a.right&&c.add("border-right",l),a.bottom&&c.add("border-bottom",l),a.left&&c.add("border-left",l)):e.kind==="ellipse"?(a.inner&&c.add("border",l).add("border-radius","50%"),s.backgroundColor&&c.add("background-color",s.backgroundColor)):e.kind==="line"&&c.add("height","0").add("border-top",l),c.add("box-sizing","border-box").add("width","100%").add("height","100%"),c.applyTo(o),o}function we(e,t,n,r){const o=r.createElement("div");o.className=`${L("comp",n)} mr-kind-${e.kind}`;const s=new W().add("position","absolute").mm("left",e.leftMm,t).mm("top",e.topMm,t).mm("width",e.widthMm,t).mm("height",e.heightMm,t).add("box-sizing","border-box");if(je(s,e,t),s.applyTo(o),x.isImageBackedComponent(e))return o.appendChild(ft(e,n,r)),o;if(e.kind==="text")return o.appendChild(mt(e,t,n,r)),o;if(e.grid){const a=_e(e,t,n,r);return a.appendChild(dt(e.grid,e,t,n,r)),o.appendChild(a),o}if(e.kind==="line"||e.kind==="rect"||e.kind==="ellipse"||e.kind==="chart")return o.appendChild(_e(e,t,n,r)),o;const i=r.createElement("div");return i.className=L("unknown",n),i.textContent=e.fallbackText??"",o.appendChild(i),o}function We(e,t,n,r){const o=r.createElement("div");o.className=L("page",n),new W().add("position","relative").mm("width",e.pageWidthMm,t).mm("height",e.pageHeightMm,t).add("background-color","#fff").add("overflow","hidden").add("box-sizing","border-box").applyTo(o);for(const s of e.components)o.appendChild(we(s,t,n,r));return o}function ht(e,t,n={}){const r=n.dpi??96,o=t.createElement("div");return o.className=L("root",n),o.dataset.schemaVersion=e.schemaVersion,e.pages.forEach(s=>{o.appendChild(We(s,r,n,t))}),o}function Ve(e){return Qe.createPageWindowIndex(e.pageHeightsPx,e.gapPx).window(e.viewportHeightPx,e.scrollTopPx,e.overscan)}const Se=96/25.4;function $e(e,t=96){const n=t/96*Se;return{widthPx:Math.max(1,Math.round(e.pageWidthMm*n)),heightPx:Math.max(1,Math.round(e.pageHeightMm*n))}}function gt(e,t,n){const r=document.createElement("canvas"),{widthPx:o,heightPx:s}=$e(e,n);r.width=Math.round(o*t),r.height=Math.round(s*t),r.style.width=`${o}px`,r.style.height=`${s}px`;const i=r.getContext("2d");if(!i)throw new Error("当前环境不支持 Canvas 2D(测试需真浏览器,见 e2e/canvas.spec.ts)");return i.scale(t,t),i.fillStyle="#ffffff",i.fillRect(0,0,o,s),{canvas:r,ctx:i}}function ut(e,t=96){return g.resolveFontSize(e,t).fontSizePx}function Ge(e,t,n,r,o,s,i,a="top"){e.font=`${t.bold?"bold ":""}${t.fontSizePx}px ${i}`,e.fillStyle=t.color,e.textBaseline="alphabetic";const l=t.fontSizePx*t.lineHeight,c=t.text===""?[""]:t.text.split(`
2
- `),f=c.length*l;let h=r;a==="middle"?h=r+(s-f)/2:a==="bottom"&&(h=r+s-f),c.forEach((m,w)=>{e.textAlign=t.align==="center"?"center":t.align==="right"?"right":"left";const d=t.align==="center"?n+o/2:t.align==="right"?n+o:n;e.fillText(m,d,h+w*l+t.fontSizePx*.8,o)}),e.textAlign="left"}function pt(e,t,n,r,o,s,i,a,l,c,f){const h=g.resolveGridLayout(t,s/a,i/a),m=g.resolveBoxBorders(n.style),w=g.resolveStroke(n.style);for(const d of h.cells){const p=d.cell.style?.backgroundColor;p&&(e.fillStyle=p,e.fillRect(r+d.xMm*a,o+d.yMm*a,d.widthMm*a,d.heightMm*a))}if(m.inner){e.strokeStyle=w.color,e.lineWidth=g.ptToPx(w.widthPt,l);for(const d of h.edges)e.beginPath(),d.kind==="v"?(e.moveTo(r+d.pos*a,o+d.from*a),e.lineTo(r+d.pos*a,o+d.to*a)):(e.moveTo(r+d.from*a,o+d.pos*a),e.lineTo(r+d.to*a,o+d.pos*a)),e.stroke();e.beginPath(),m.top&&(e.moveTo(r,o),e.lineTo(r+s,o)),m.right&&(e.moveTo(r+s,o),e.lineTo(r+s,o+i)),m.bottom&&(e.moveTo(r+s,o+i),e.lineTo(r,o+i)),m.left&&(e.moveTo(r,o+i),e.lineTo(r,o)),e.stroke()}for(const d of h.cells){const p=typeof d.cell.text=="string"?d.cell.text:"";if(!p)continue;const S=d.cell.style,T=S?.fontSize??g.DEFAULT_FONT_PT,u=x.wrapText(p,d.widthMm-1,{fontSizePt:T},f).join(`
3
- `);Ge(e,{text:u,fontSizePx:g.resolveFontSize(S,l).fontSizePx,bold:S?.bold??!1,color:S?.color??g.resolveTextColor(n.style,g.DEFAULT_TEXT_COLOR),align:S?.align??"left",lineHeight:g.LINE_HEIGHT},r+(d.xMm+.5)*a,o+d.yMm*a,Math.max(0,(d.widthMm-1)*a),d.heightMm*a,c,S?.verticalAlign??"middle")}}function Ue(e,t,n,r,o,s){const i=t.leftMm*n,a=t.topMm*n,l=t.widthMm*n,c=t.heightMm*n,f=t.style??{};if(t.grid){pt(e,t.grid,t,i,a,l,c,n,r,o,s);return}switch(t.kind){case"text":{const h=f.fontSize??g.DEFAULT_FONT_PT,m=x.isRichText(t)?t.richParagraphs.map(w=>(w.segments??[]).map(d=>d.text??d.field??"").join("")).join(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("./grid-geometry-DBH9ExuY.cjs"),C=require("./textwrap-DqiFwAV3.cjs"),ot=require("./page-window-index-DEjnM6IB.cjs"),rt=require("./report-params-D1gZ1DME.cjs");function be(e){if(!e||e.kind==="date")return;const t=e.digits;if(!(t!==void 0&&(!Number.isInteger(t)||t<0||t>100))){if(e.kind==="percent"){const n=t??2;return`0${n?`.${"0".repeat(n)}`:""}%`}return t===void 0?e.thousands?"#,##0.###############":void 0:`${e.thousands?"#,##0":"0"}${t?`.${"0".repeat(t)}`:""}`}}function Fe(e){const t=/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})$/.exec(e.trim());if(t){const o=new Date(Number(t[1]),Number(t[2])-1,Number(t[3]));return Number.isNaN(o.getTime())?null:o}const n=new Date(e);return Number.isNaN(n.getTime())?null:n}function Me(e,t){return e.toLocaleString("en-US",{minimumFractionDigits:t,maximumFractionDigits:t})}function it(e,t){const n=o=>String(o).padStart(2,"0");return t.replace(/YYYY/g,String(e.getFullYear())).replace(/MM/g,n(e.getMonth()+1)).replace(/DD/g,n(e.getDate())).replace(/hh/g,n(e.getHours())).replace(/mm/g,n(e.getMinutes())).replace(/ss/g,n(e.getSeconds()))}function st(e,t,n){return n!=null?t?Me(e,n):e.toFixed(n):t?Me(e):String(e)}function me(e){const t=typeof e=="number"?e:Number(String(e).replace(/,/g,""));return Number.isFinite(t)?t:null}function we(e,t){if(e==null||e===""||!t)return String(e??"");switch(t.kind){case"number":{const n=me(e);return n==null?String(e):st(n,t.thousands===!0,t.digits)}case"percent":{const n=me(e);return n==null?String(e):`${Me(n*100,t.digits??2)}%`}case"date":{const n=t.pattern??"YYYY-MM-DD",o=e instanceof Date?e:Fe(String(e));return o?it(o,n):String(e)}default:return String(e)}}function Le(e,t){const n=e[t.field];if(n==null||n==="")return!1;if(typeof t.value=="number"){const s=me(n);if(s==null)return!1;switch(t.op){case">":return s>t.value;case">=":return s>=t.value;case"<":return s<t.value;case"<=":return s<=t.value;case"==":return s===t.value;case"!=":return s!==t.value}}const o=String(n),r=String(t.value);switch(t.op){case"==":return o===r;case"!=":return o!==r;case">":return o>r;case">=":return o>=r;case"<":return o<r;case"<=":return o<=r}return!1}const at=new Set(["text","image","barcode","qrcode","chart","line","rect","ellipse"]);function X(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function lt(e,t,n,o){const r=e[t];(typeof r!="number"||!Number.isFinite(r))&&o.push({path:`${n}.${t}`,message:`应为有限数字,实际 ${JSON.stringify(r)}`})}function ge(e,t,n,o=[]){if(!X(e)){n.push({path:t,message:"组件应为对象"});return}const r=e.kind;typeof r!="string"?n.push({path:`${t}.kind`,message:"组件缺少 kind 字符串"}):at.has(r)||o.push({path:`${t}.kind`,message:`未知组件类型 ${JSON.stringify(r)},将以兜底方式渲染`});for(const l of["leftMm","topMm","widthMm","heightMm"])lt(e,l,t,n);typeof e.leftMm=="number"&&e.leftMm<-100&&n.push({path:`${t}.leftMm`,message:"几何数值异常(leftMm<-100)"});for(const l of["widthMm","heightMm"])typeof e[l]=="number"&&e[l]<0&&n.push({path:`${t}.${l}`,message:"组件尺寸不能为负数"});const s=e.table;if(s!==void 0){const l=`${t}.table`;if(!X(s))n.push({path:l,message:"逻辑表来源应为对象"});else{(typeof s.id!="string"||!s.id.trim())&&n.push({path:`${l}.id`,message:"逻辑表 ID 应为非空字符串"});const d=s.role;["header","body","group-header","subtotal","grand-total"].includes(d)||n.push({path:`${l}.role`,message:"未知逻辑表行角色"});const m=s.rowIndex;(typeof m!="number"||!Number.isInteger(m)||(d==="header"?m!==-1:m<0))&&n.push({path:`${l}.rowIndex`,message:"表头索引应为 -1,内容行索引应为非负整数"}),(!Array.isArray(s.columnKeys)||s.columnKeys.some(h=>typeof h!="string"))&&n.push({path:`${l}.columnKeys`,message:"逻辑列字段应为字符串数组"}),s.layout!==void 0&&s.layout!=="page"&&s.layout!=="wide"&&n.push({path:`${l}.layout`,message:"布局应为 page 或 wide"}),s.title!==void 0&&typeof s.title!="string"&&n.push({path:`${l}.title`,message:"逻辑表名称应为字符串"})}}const i=e.grid;i!==void 0&&(!X(i)||!Array.isArray(i.cells)?n.push({path:`${t}.grid.cells`,message:"grid.cells 应为二维数组"}):i.cells.forEach((l,d)=>{if(!Array.isArray(l)){n.push({path:`${t}.grid.cells.${d}`,message:"行应为数组"});return}l.forEach((m,h)=>{m!=null&&X(m)&&Array.isArray(m.children)&&m.children.forEach((f,u)=>{ge(f,`${t}.grid.cells.${d}.${h}.children.${u}`,n,o)})})}));const a=e.richParagraphs;a!==void 0&&(Array.isArray(a)?a.forEach((l,d)=>{(!X(l)||!Array.isArray(l.segments))&&n.push({path:`${t}.richParagraphs.${d}`,message:"段落缺少 segments 数组"})}):n.push({path:`${t}.richParagraphs`,message:"richParagraphs 应为数组"}))}function Ie(e,t,n,o=[]){if(!X(e)){n.push({path:t,message:"页应为对象"});return}const r=e.pageWidthMm,s=e.pageHeightMm;(typeof r!="number"||!Number.isFinite(r)||r<=0)&&n.push({path:`${t}.pageWidthMm`,message:`页宽非法: ${JSON.stringify(r)}`}),(typeof s!="number"||!(s>0&&s<5e3))&&n.push({path:`${t}.pageHeightMm`,message:`页高非法: ${JSON.stringify(s)}`}),e.layout!==void 0&&e.layout!=="page"&&e.layout!=="wide"&&n.push({path:`${t}.layout`,message:"布局应为 page 或 wide"});const i=e.components;Array.isArray(i)?Array.isArray(i)&&i.forEach((a,l)=>ge(a,`${t}.components.${l}`,n,o)):n.push({path:`${t}.components`,message:"components 应为数组"})}function $e(e){const t=[],n=[],o=X(e)?e.pages:void 0;if(!Array.isArray(o))t.push({path:"$.pages",message:e==null?"渲染计划为空":"渲染计划缺少 pages 数组"});else{o.forEach((s,i)=>Ie(s,`$.pages.${i}`,t,n)),o.length>2e3&&n.push({path:"$.pages",message:`页数异常大(${o.length} 页),渲染与打印耗时可能显著增加`});const r=o.reduce((s,i)=>s+(X(i)&&Array.isArray(i.components)?i.components.length:0),0);r>5e4&&n.push({path:"$.pages",message:`组件总数异常大(${r} 个),可能触发浏览器性能限制`})}return{ok:t.length===0,errors:t,warnings:n}}function dt(e){const t=$e(e);if(!t.ok){const n=t.errors.slice(0,3).map(o=>`${o.path}: ${o.message}`).join("; ");throw new Error(`渲染计划校验失败(${t.errors.length} 处): ${n}`)}return e.pages}function _e(e){return{widthMm:e.widthMm,heightMm:e.heightMm,marginTopMm:e.marginTopMm??0,marginBottomMm:e.marginBottomMm??0,marginLeftMm:e.marginLeftMm??0,marginRightMm:e.marginRightMm??0,components:[]}}function ct(e,t){return t.heightMm-(e.layout==="wide"?t.marginBottomMm:e.marginBottomMm??t.marginBottomMm)}function De(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number")return String(e);if(typeof e=="boolean")return e?"是":"否";if(e instanceof Date)return e.toISOString().slice(0,10);try{return JSON.stringify(e)}catch{return String(e)}}function ze(e,t){let n=e.textMetrics.get(t);if(!n){const o=g.resolveFontSize(t).fontSizePt;n={measureOptions:{fontSizePt:o,bold:t.bold,letterSpacingPt:t.letterSpacing===void 0?void 0:t.letterSpacing*72/96},lineMm:o*(t.lineHeight??g.LINE_HEIGHT)*25.4/72},e.textMetrics.set(t,n)}return n}function je(e,t){return t?.kind==="number"||t?.kind==="percent"?"right":t?.kind==="date"||e instanceof Date||typeof e=="boolean"?"center":typeof e=="number"&&Number.isFinite(e)?"right":"left"}function mt(e){if(e===null||typeof e=="string"||typeof e=="boolean"||typeof e=="number"&&Number.isFinite(e))return e}function ft(e,t){const n=e.fontSizePt??g.DEFAULT_FONT_PT,o=e.paddingMm??1,r=e.columns.map(c=>c.align??c.style?.align??e.align??"left"),s=e.columns.map((c,w)=>({fontSize:n,verticalAlign:"middle",...c.style,align:r[w]==="auto"?"left":r[w]})),i=s.map((c,w)=>r[w]==="auto"?{left:c,center:{...c,align:"center"},right:{...c,align:"right"}}:void 0),a=e.columns.map((c,w)=>c.rules?.map(M=>{const E=i[w];if(E)return{left:{...E.left,...M.style},center:{...E.center,...M.style},right:{...E.right,...M.style}};const S={...s[w],...M.style};return{left:S,center:S,right:S}})),l=e.columns.map(c=>({fontSize:n,bold:e.headerBold??!0,align:"center",verticalAlign:"middle",backgroundColor:e.headerBackgroundColor,...c.style||{},...e.headerAlign===void 0?{}:{align:e.headerAlign},...e.headerStyle,...c.headerAlign===void 0?{}:{align:c.headerAlign},...c.headerStyle})),d=e.columns.map(c=>c.header);let m=e.rowHeightMm??0;for(let c=0;c<d.length;c++){const w=l[c],M=g.resolveFontSize(w).fontSizePt,E=C.wrapText(d[c],e.columns[c].widthMm-o*2,{fontSizePt:M,bold:w.bold},t),S=M*(w.lineHeight??g.LINE_HEIGHT)*25.4/72;m=Math.max(m,Math.max(E.length,1)*S+o*2)}const h=d.map((c,w)=>({text:c,style:l[w]})),f=e.border===!1?{borderTop:!1,borderRight:!1,borderBottom:!1,borderLeft:!1}:{borderBottom:!0,borderLeft:!0,borderRight:!0,borderTop:!0},u=e.columns.map(c=>c.widthMm);return{fontSizePt:n,paddingMm:o,cellStyles:s,autoStyles:i,ruleStyles:a,textMetrics:new WeakMap,numberFormats:e.columns.map(c=>be(c.format)),borderStyle:f,headerHeightMm:m,headerGrid:{cells:[h],colWidthsMm:u,rowHeightsMm:[m]},colWidthsMm:u,columns:e.columns}}function Oe(e,t,n){const o=e.columns;let r=e.rowHeightFloorMm;const s=new Array(o.length);for(let i=0;i<o.length;i++){const a=o[i],l=t[a.field],d=e.autoStyles[i]?je(l,a.format):"left";let m=e.autoStyles[i]?.[d]??e.cellStyles[i];if(a.rules)for(let M=0;M<a.rules.length;M++){const E=a.rules[M];if(Le(t,E.when)){m=e.ruleStyles[i][M][d];break}}const h=a.format?we(l,a.format):De(l),f=ze(e,m),u=C.wrapText(h,a.widthMm-e.paddingMm*2,f.measureOptions,n);r=Math.max(r,u.length*f.lineMm+e.paddingMm*2);const c=mt(l),w={text:h,style:m};c!==void 0&&(w.value=c),typeof c=="number"&&e.numberFormats[i]!==void 0&&(w.numberFormat=e.numberFormats[i]),s[i]=w}return{grid:{cells:[s],colWidthsMm:e.colWidthsMm,rowHeightsMm:[r]},heightMm:r}}function We(e,t,n={}){const o=typeof n=="number"?{mmPerRow:n}:n,r=o.mmPerRow??0,s=o.measurer??C.heuristicMeasurer,i=[],a=[];let l=0;const d=h=>`${h}_${(++l).toString(36)}`,m=[];for(const[h,f]of e.pages.entries()){const u=_e(f),c=()=>ct(f,u),w=u.marginTopMm,M=[],E=(S=!1)=>{if(a.push({pageWidthMm:u.widthMm,pageHeightMm:u.heightMm,...f.layout===void 0?{}:{layout:f.layout},marginTopMm:u.marginTopMm,marginBottomMm:u.marginBottomMm,marginLeftMm:u.marginLeftMm,marginRightMm:u.marginRightMm,components:u.components}),M.length>0){const x=a.length;for(const T of M){const O={...T,text:String(T.text??"").replace(/\{page\}/g,String(x))};a[a.length-1].components.push(O),O.text.includes("{totalPages}")&&m.push(O)}}if(f.layout==="wide"){const x=a[a.length-1],T=x.components.reduce((L,P)=>Math.max(L,P.topMm+P.heightMm),0),O=S?f.marginBottomMm??0:0;x.pageHeightMm=Math.max(1,T+O),x.marginBottomMm=O}};for(const[S,x]of f.components.entries()){if(x.kind!=="list"){if(x.kind==="text"&&typeof x.text=="string"){const $=x.text;if($.includes("{page}")||$.includes("{totalPages}")){M.push(x);continue}}u.components.push(x);continue}const T=x,O=t[T.dataset];Array.isArray(O)||i.push(`数据集 ${T.dataset} 未提供数据,列表 ${T.id??""} 输出为空`);const L=Array.isArray(O)?O:[],P=T.columns.reduce(($,b)=>$+b.widthMm,0),v=P>T.widthMm?T.widthMm/P:1,p=v===1?T:{...T,columns:T.columns.map($=>({...$,widthMm:$.widthMm*v}))},y={id:`table:${h}:${S}`,columnKeys:T.columns.map($=>$.field),...T.layout===void 0?{}:{layout:T.layout},...T.title===void 0?{}:{title:T.title}};let B=0;const A={...ft(p,s),rowHeightFloorMm:p.rowHeightMm??0},k={kind:"rect",leftMm:T.leftMm,topMm:0,widthMm:T.widthMm,heightMm:A.headerHeightMm,grid:A.headerGrid,nid:d("listh"),table:{...y,role:"header",rowIndex:-1},style:A.borderStyle};let R=T.topMm;const I=($,b)=>{$.components.push({...k,topMm:b})};I(u,R),R+=A.headerHeightMm+r;const J=$=>{if(R+$>c()){E();const b=_e(f);u.components=b.components,u.heightMm=b.heightMm,f.layout==="wide"&&(u.marginTopMm=0,u.marginBottomMm=0);const N=f.layout==="wide"?0:T.topMm;p.headerEveryPage!==!1?(I(u,N),R=N+A.headerHeightMm):R=f.layout==="wide"?0:w}},Q=($,b,N,_)=>{const z=f.layout==="wide"?0:T.topMm,ee=p.headerEveryPage!==!1?z+A.headerHeightMm:f.layout==="wide"?0:w,oe=c()-ee;b>oe&&i.push(`列表 ${T.id??T.dataset} 行高 ${b.toFixed(2)}mm 超出单页可用高度 ${oe.toFixed(2)}mm,预览与打印可能截断内容`),J(b),u.components.push({kind:"rect",leftMm:T.leftMm,topMm:R,widthMm:T.widthMm,heightMm:b,grid:$,nid:d(N),table:{...y,role:_,rowIndex:B++},style:A.borderStyle}),R+=b},et=()=>Oe(A,{},s).heightMm,pe=($,b,N,_)=>{const z=[],ee=Math.max(1,Math.min(_,p.columns.length)),oe={fontSize:A.fontSizePt,...N};z.push({text:$,colSpan:ee,style:{bold:!0,...oe}});for(let Y=ee;Y<p.columns.length;Y++){const re=p.columns[Y],ie=b.get(re.field),de=A.autoStyles[Y]?.[je(ie?.value,re.format)]??A.cellStyles[Y];z.push({...ie,text:ie?.text??"",style:{...de,...oe}})}let ae=A.rowHeightFloorMm,le=0;for(const Y of z){let re=0;const ie=le+(Y.colSpan??1);for(;le<ie;le++)re+=A.colWidthsMm[le]??0;const de=ze(A,Y.style),nt=C.wrapText(Y.text??"",re-A.paddingMm*2,de.measureOptions,s);ae=Math.max(ae,nt.length*de.lineMm+A.paddingMm*2)}return{grid:{cells:[z],colWidthsMm:A.colWidthsMm,rowHeightsMm:[ae]},heightMm:ae}},tt=($,b)=>{let N=0;for(const _ of $){const z=typeof _[b]=="number"?_[b]:Number(_[b]);Number.isFinite(z)&&(N+=z)}return N},Ne=($,b)=>{const _=p.columns.find(ee=>ee.field===b)?.format??{kind:"number",thousands:!0},z=tt($,b);return{text:we(z,_),value:z,numberFormat:be(_)}},V=p.groupBy,Re=$=>{const b=p.columns.findIndex(N=>$.includes(N.field));return b>0?b:1},ne=[];if(V){const $=new Map;for(const b of L){const N=De(b[V.field]),_=$.get(N);_?_.push(b):$.set(N,[b])}for(const[b,N]of $){ne.push({kind:"groupHeader",label:(V.headerTemplate??"{value}").replace(/\{value\}/g,b)});for(const _ of N)ne.push({kind:"row",row:_});V.subtotal&&V.subtotal.length>0&&ne.push({kind:"subtotal",label:V.subtotalLabel??"小计",rows:N})}}else for(const $ of L)ne.push({kind:"row",row:$});const Ce=et();for(const $ of ne)if($.kind==="row"){const b=Oe(A,$.row,s);Q(b.grid,b.heightMm,"listr","body")}else if($.kind==="groupHeader"){const b=pe($.label,new Map,V?.headerStyle??{},p.columns.length);V?.keepWithNext!==!1&&R+b.heightMm+Ce>c()&&J(b.heightMm+Ce),Q(b.grid,b.heightMm,"listg","group-header")}else if($.kind==="subtotal"){const b=new Map;for(const _ of V.subtotal??[])b.set(_,Ne($.rows,_));const N=pe($.label,b,V?.subtotalStyle??{},Re(V.subtotal??[]));Q(N.grid,N.heightMm,"listst","subtotal")}if(p.grandTotal){const $=typeof p.grandTotal=="object"?p.grandTotal:void 0,b=$?.fields??V?.subtotal??[];if(b.length>0){const N=new Map;for(const z of b)N.set(z,Ne(L,z));const _=pe($?.label??"合计",N,$?.style??{},Re(b));Q(_.grid,_.heightMm,"listgt","grand-total")}else i.push(`列表 ${T.id??""} 配置了 grandTotal 但未提供合计字段(groupBy.subtotal 或 grandTotal.fields)`)}}E(!0)}for(const h of m){const f=h.text;typeof f=="string"&&f.includes("{totalPages}")&&(h.text=f.replace(/\{totalPages\}/g,String(a.length)))}return{plan:{schemaVersion:C.RENDER_PLAN_SCHEMA_VERSION,pages:a},warnings:i}}const ht={root:"mr-root",page:"mr-page",comp:"mr-comp",text:"mr-text",lines:"mr-lines",line:"mr-line",rich:"mr-rich",paragraph:"mr-paragraph",segment:"mr-segment",image:"mr-image",shape:"mr-shape",grid:"mr-grid",row:"mr-row",cell:"mr-cell",unknown:"mr-unknown"};function D(e,t){return t?.classNames?.[e]??ht[e]}class G{constructor(){this.props=[]}add(t,n){return n==null||n===!1||n===""?this:(this.props.push([t,String(n)]),this)}mm(t,n,o,r=2){return this.add(t,`${g.round(g.mmToPx(n,o),r)}px`)}toString(){let t="";for(const[n,o]of this.props)t+=`${n}:${o};`;return t}applyTo(t){const n=this.toString();n&&(t.style.cssText=n)}}function Ve(e,t,n){const o=t.style||{};e.add("color",o.color).add("background-color",o.backgroundColor).add("font-family",o.fontFamily).add("font-weight",o.bold?"700":void 0).add("font-style",o.italic?"italic":void 0).add("text-decoration",o.underline?"underline":void 0).add("letter-spacing",o.letterSpacing!=null?`${o.letterSpacing}pt`:void 0).add("opacity",o.opacity!=null?String(o.opacity):void 0).add("transform",o.rotateDeg?`rotate(${o.rotateDeg}deg)`:void 0);const r=g.resolveFontSize(o,n);e.add("font-size",`${g.round(r.fontSizePx,2)}px`)}function gt(e,t,n,o,r){const s=r.createElement("table");if(s.className=D("grid",o),new G().add("width","100%").add("height","100%").add("border-collapse","collapse").add("table-layout",e.colWidthsMm?"fixed":"auto").applyTo(s),e.colWidthsMm){const d=r.createElement("colgroup");for(const m of e.colWidthsMm){const h=r.createElement("col");new G().mm("width",m,n).applyTo(h),d.appendChild(h)}s.appendChild(d)}const i=g.resolveBoxBorders(t.style),a=g.resolveStroke(t.style),l=i.inner?`${g.round(a.widthPt*n/72,2)}px solid ${a.color}`:"";return e.cells.forEach((d,m)=>{const h=r.createElement("tr");h.className=D("row",o),e.rowHeightsMm?.[m]!=null&&new G().mm("height",e.rowHeightsMm[m],n).applyTo(h),(d||[]).forEach(f=>{const u=r.createElement("td");u.className=D("cell",o),f?.colSpan&&(u.colSpan=f.colSpan),f?.rowSpan&&(u.rowSpan=f.rowSpan);const c=new G;if(l&&c.add("border",l),f?.style){const w=f.style;c.add("text-align",w.align).add("vertical-align",w.verticalAlign).add("background-color",w.backgroundColor).add("color",w.color).add("font-weight",w.bold?"700":void 0),c.add("font-size",`${g.round(g.resolveFontSize(w,n).fontSizePx,2)}px`)}if(c.applyTo(u),f?.text!=null&&(u.textContent=f.text),f?.children?.length)for(const w of f.children)u.appendChild(Se(w,n,o,r));h.appendChild(u)}),s.appendChild(h)}),s}function ut(e,t,n,o){const r=o.createElement("div");r.className=D("text",n);const s=e.style||{},i=new G().add("text-align",s.align).add("display","flex").add("flex-direction","column").add("justify-content",s.verticalAlign==="middle"?"center":s.verticalAlign==="bottom"?"flex-end":"flex-start").add("overflow","hidden");if(Ve(i,e,t),i.applyTo(r),C.isRichText(e)){const a=o.createElement("div");a.className=D("rich",n);for(const l of e.richParagraphs){const d=o.createElement("div");d.className=D("paragraph",n),new G().add("text-align",l.align).add("line-height",l.lineHeight!=null?String(l.lineHeight):void 0).mm("margin-top",l.spaceBeforeMm??0,t).mm("margin-bottom",l.spaceAfterMm??0,t).mm("text-indent",l.indentMm??0,t).applyTo(d);for(const h of l.segments||[]){const f=o.createElement("span");if(f.className=D("segment",n),f.textContent=h.kind==="field"?h.field??"":h.text??"",h.style){const u=new G().add("color",h.style.color).add("font-weight",h.style.bold?"700":void 0);u.add("font-size",`${g.round(g.resolveFontSize(h.style,t).fontSizePx,2)}px`),u.applyTo(f)}d.appendChild(f)}a.appendChild(d)}return r.appendChild(a),r}if(Array.isArray(e.lines)&&e.lines.length>0){const a=o.createElement("div");a.className=D("lines",n),s.lineHeight!=null&&new G().add("line-height",String(s.lineHeight)).applyTo(a);for(const l of e.lines){const d=o.createElement("div");d.className=D("line",n),d.textContent=l,a.appendChild(d)}return r.appendChild(a),r}return r.textContent=e.text??"",r}function pt(e,t,n){const o=n.createElement("img");return o.className=D("image",t),o.src=e.imageData,new G().add("width","100%").add("height","100%").add("object-fit","contain").add("display","block").applyTo(o),o.alt=e.kind,o}function He(e,t,n,o){const r=o.createElement("div");r.className=D("shape",n);const s=e.style||{},i=g.resolveStroke(s),a=g.resolveBoxBorders(s),l=`${g.round(i.widthPt*t/72,2)}px solid ${i.color}`,d=new G;return e.grid||e.kind==="rect"?(a.top&&d.add("border-top",l),a.right&&d.add("border-right",l),a.bottom&&d.add("border-bottom",l),a.left&&d.add("border-left",l)):e.kind==="ellipse"?(a.inner&&d.add("border",l).add("border-radius","50%"),s.backgroundColor&&d.add("background-color",s.backgroundColor)):e.kind==="line"&&d.add("height","0").add("border-top",l),d.add("box-sizing","border-box").add("width","100%").add("height","100%"),d.applyTo(r),r}function Se(e,t,n,o){const r=o.createElement("div");r.className=`${D("comp",n)} mr-kind-${e.kind}`;const s=new G().add("position","absolute").mm("left",e.leftMm,t).mm("top",e.topMm,t).mm("width",e.widthMm,t).mm("height",e.heightMm,t).add("box-sizing","border-box");if(Ve(s,e,t),s.applyTo(r),C.isImageBackedComponent(e))return r.appendChild(pt(e,n,o)),r;if(e.kind==="text")return r.appendChild(ut(e,t,n,o)),r;if(e.grid){const a=He(e,t,n,o);return a.appendChild(gt(e.grid,e,t,n,o)),r.appendChild(a),r}if(e.kind==="line"||e.kind==="rect"||e.kind==="ellipse"||e.kind==="chart")return r.appendChild(He(e,t,n,o)),r;const i=o.createElement("div");return i.className=D("unknown",n),i.textContent=e.fallbackText??"",r.appendChild(i),r}function Ue(e,t,n,o){const r=o.createElement("div");r.className=D("page",n),new G().add("position","relative").mm("width",e.pageWidthMm,t).mm("height",e.pageHeightMm,t).add("background-color","#fff").add("color",g.DEFAULT_TEXT_COLOR).add("overflow","hidden").add("box-sizing","border-box").applyTo(r);for(const s of e.components)r.appendChild(Se(s,t,n,o));return r}function yt(e,t,n={}){const o=n.dpi??96,r=t.createElement("div");return r.className=D("root",n),r.dataset.schemaVersion=e.schemaVersion,e.pages.forEach(s=>{r.appendChild(Ue(s,o,n,t))}),r}function Ge(e){return ot.createPageWindowIndex(e.pageHeightsPx,e.gapPx).window(e.viewportHeightPx,e.scrollTopPx,e.overscan)}const Te=96/25.4;function ve(e,t=96){const n=t/96*Te;return{widthPx:Math.max(1,Math.round(e.pageWidthMm*n)),heightPx:Math.max(1,Math.round(e.pageHeightMm*n))}}function Mt(e,t,n){const o=document.createElement("canvas"),{widthPx:r,heightPx:s}=ve(e,n);o.width=Math.round(r*t),o.height=Math.round(s*t),o.style.width=`${r}px`,o.style.height=`${s}px`;const i=o.getContext("2d");if(!i)throw new Error("当前环境不支持 Canvas 2D(测试需真浏览器,见 e2e/canvas.spec.ts)");return i.scale(t,t),i.fillStyle="#ffffff",i.fillRect(0,0,r,s),{canvas:o,ctx:i}}function bt(e,t=96){return g.resolveFontSize(e,t).fontSizePx}function Je(e,t,n,o,r,s,i,a="top"){e.font=`${t.bold?"bold ":""}${t.fontSizePx}px ${i}`,e.fillStyle=t.color,e.textBaseline="alphabetic";const l=t.fontSizePx*t.lineHeight,d=t.text===""?[""]:t.text.split(`
2
+ `),m=d.length*l;let h=o;a==="middle"?h=o+(s-m)/2:a==="bottom"&&(h=o+s-m),d.forEach((f,u)=>{e.textAlign=t.align==="center"?"center":t.align==="right"?"right":"left";const c=t.align==="center"?n+r/2:t.align==="right"?n+r:n;e.fillText(f,c,h+u*l+t.fontSizePx*.8,r)}),e.textAlign="left"}function wt(e,t,n,o,r,s,i,a,l,d,m){const h=g.resolveGridLayout(t,s/a,i/a),f=g.resolveBoxBorders(n.style),u=g.resolveStroke(n.style);for(const c of h.cells){const w=c.cell.style?.backgroundColor;w&&(e.fillStyle=w,e.fillRect(o+c.xMm*a,r+c.yMm*a,c.widthMm*a,c.heightMm*a))}if(f.inner){e.strokeStyle=u.color,e.lineWidth=g.ptToPx(u.widthPt,l);for(const c of h.edges)e.beginPath(),c.kind==="v"?(e.moveTo(o+c.pos*a,r+c.from*a),e.lineTo(o+c.pos*a,r+c.to*a)):(e.moveTo(o+c.from*a,r+c.pos*a),e.lineTo(o+c.to*a,r+c.pos*a)),e.stroke();e.beginPath(),f.top&&(e.moveTo(o,r),e.lineTo(o+s,r)),f.right&&(e.moveTo(o+s,r),e.lineTo(o+s,r+i)),f.bottom&&(e.moveTo(o+s,r+i),e.lineTo(o,r+i)),f.left&&(e.moveTo(o,r+i),e.lineTo(o,r)),e.stroke()}for(const c of h.cells){const w=typeof c.cell.text=="string"?c.cell.text:"";if(!w)continue;const M=c.cell.style,E=M?.fontSize??g.DEFAULT_FONT_PT,S=C.wrapText(w,c.widthMm-1,{fontSizePt:E},m).join(`
3
+ `);Je(e,{text:S,fontSizePx:g.resolveFontSize(M,l).fontSizePx,bold:M?.bold??!1,color:M?.color??g.resolveTextColor(n.style,g.DEFAULT_TEXT_COLOR),align:M?.align??"left",lineHeight:g.LINE_HEIGHT},o+(c.xMm+.5)*a,r+c.yMm*a,Math.max(0,(c.widthMm-1)*a),c.heightMm*a,d,M?.verticalAlign??"middle")}}function Ye(e,t,n,o,r,s){const i=t.leftMm*n,a=t.topMm*n,l=t.widthMm*n,d=t.heightMm*n,m=t.style??{};if(t.grid){wt(e,t.grid,t,i,a,l,d,n,o,r,s);return}switch(t.kind){case"text":{const h=m.fontSize??g.DEFAULT_FONT_PT,f=C.isRichText(t)?t.richParagraphs.map(u=>(u.segments??[]).map(c=>c.text??c.field??"").join("")).join(`
4
4
  `):Array.isArray(t.lines)&&t.lines.length>0?t.lines.join(`
5
- `):x.wrapText(t.text??"",t.widthMm,{fontSizePt:h},s).join(`
6
- `);Ge(e,{text:m,fontSizePx:g.resolveFontSize(f,r).fontSizePx,bold:f.bold??!1,color:g.resolveTextColor(f),align:f.align??"left",lineHeight:g.resolveLineHeight(f)},i,a,l,c,o,f.verticalAlign);break}case"rect":{const h=g.resolveStroke(f),m=g.resolveBoxBorders(f);f.backgroundColor&&(e.fillStyle=f.backgroundColor,e.fillRect(i,a,l,c)),m.inner&&(e.strokeStyle=h.color,e.lineWidth=g.ptToPx(h.widthPt,r),e.beginPath(),m.top&&(e.moveTo(i,a),e.lineTo(i+l,a)),m.right&&(e.moveTo(i+l,a),e.lineTo(i+l,a+c)),m.bottom&&(e.moveTo(i+l,a+c),e.lineTo(i,a+c)),m.left&&(e.moveTo(i,a+c),e.lineTo(i,a)),e.stroke());break}case"line":{const h=g.resolveStroke({...f,borderColor:f.lineColor??f.borderColor});e.strokeStyle=h.color,e.lineWidth=g.ptToPx(h.widthPt,r),e.beginPath(),e.moveTo(i,a+c/2),e.lineTo(i+l,a+c/2),e.stroke();break}case"ellipse":{const h=g.resolveStroke(f);e.strokeStyle=h.color,e.lineWidth=g.ptToPx(h.widthPt,r),f.backgroundColor&&(e.fillStyle=f.backgroundColor,e.beginPath(),e.ellipse(i+l/2,a+c/2,l/2,c/2,0,0,Math.PI*2),e.fill()),e.beginPath(),e.ellipse(i+l/2,a+c/2,l/2,c/2,0,0,Math.PI*2),e.stroke();break}}}async function yt(e,t={}){if(typeof document>"u")throw new Error("renderPlanToImages 需要浏览器环境(SSR/Node 请在客户端钩子中调用)");const{format:n="png",quality:r=.92,...o}=t,{canvases:s}=Je(e,o),i=n==="jpeg"?"image/jpeg":"image/png",a=[];for(const l of s)a.push(await new Promise(c=>l.toBlob(f=>c(f),i,r))??new Blob([],{type:i}));return{blobs:a}}function Je(e,t={}){if(typeof document>"u")throw new Error("renderPlanToCanvas 需要浏览器环境(SSR 请在客户端钩子中调用)");const n=t.dpr??(typeof devicePixelRatio=="number"?devicePixelRatio:1),r=t.dpi??96,o=r/96*Se,s=t.fontFamily??'"Microsoft YaHei", sans-serif',i=t.measurer??x.heuristicMeasurer,a=Math.max(0,t.start??0),l=Math.min(e.pages.length-1,t.end??e.pages.length-1),c=[];for(let f=a;f<=l;f++){const h=e.pages[f],{canvas:m,ctx:w}=gt(h,n,r);for(const d of h.components)Ue(w,d,o,r,s,i);c.push(m)}return{canvases:c}}function Mt(e,t={}){const n=t.dpr??(typeof devicePixelRatio=="number"?devicePixelRatio:1),r=t.dpi??96,o=r/96*Se,s=t.fontFamily??'"Microsoft YaHei", sans-serif',i=t.measurer??x.heuristicMeasurer,a=t.overscan??1,l=t.gapPx??18,c=t.pagesPerFrame??1,f=e.pages.map(b=>b.pageHeightMm*o),h=Math.max(1,...e.pages.map(b=>b.pageWidthMm*o)),m={start:0,end:-1};let w=null,d=null,p=null,S=null,T=[],u=0,k=[];function A(){const b=w;b.innerHTML="",d=document.createElement("div"),d.style.cssText=`width:${h}px;margin:0 auto;`,p=document.createElement("div"),S=document.createElement("div"),d.appendChild(p),T=[];const H=Math.min(e.pages.length,3+a*2);for(let D=0;D<H;D++){const P=document.createElement("div");P.style.cssText=`margin:0 auto ${l}px;box-shadow:0 2px 8px rgba(0,0,0,.35);background:#fff;`,d.appendChild(P),T.push(P)}d.appendChild(S),b.appendChild(d)}function v(b,H){const D=e.pages[H],{widthPx:P,heightPx:O}=$e(D,r);let _=b.firstElementChild;(!_||_.tagName!=="CANVAS")&&(_=document.createElement("canvas"),b.innerHTML="",b.appendChild(_)),_.width=Math.round(P*n),_.height=Math.round(O*n),_.style.width=`${P}px`,_.style.height=`${O}px`;const Y=_.getContext("2d");if(Y){Y.setTransform(1,0,0,1,0,0),Y.scale(n,n),Y.fillStyle="#ffffff",Y.fillRect(0,0,P,O);for(const oe of D.components)Ue(Y,oe,o,r,s,i);_.dataset.canvasPage=String(H),b.style.height=`${O}px`,b.style.width=`${P}px`}}function V(b){m.start=b.start,m.end=b.end,p.style.height=`${b.start*l+f.slice(0,b.start).reduce((P,O)=>P+O,0)}px`;const H=e.pages.length-1-b.end;S.style.height=`${H*l+f.slice(b.end+1).reduce((P,O)=>P+O,0)}px`;const D=b.end-b.start+1;T.forEach((P,O)=>{if(O<D){P.style.display="";const _=b.start+O;P.style.height=`${f[_]??0}px`}else P.style.display="none"}),k=[];for(let P=b.start;P<=b.end;P++){const O=T[P-b.start];if(!O)continue;O.firstElementChild?.dataset?.canvasPage!==String(P)&&k.push(P)}y()}function y(){u||k.length===0||(u=requestAnimationFrame(()=>{u=0;let b=Math.max(1,c);for(;k.length>0&&b-- >0;){const H=k.shift(),D=T[H-m.start];D&&v(D,H)}k.length>0&&y()}))}function E(){u||(u=requestAnimationFrame(()=>{u=0;const b=w,H=Ve({pageHeightsPx:f,viewportHeightPx:b.clientHeight,scrollTopPx:b.scrollTop,overscan:a,gapPx:l});H.start!==m.start||H.end!==m.end?V(H):y()}))}let Z=null;return{attach(b){if(typeof document>"u")throw new Error("createCanvasPager 需要浏览器环境(SSR 请在客户端钩子中调用 attach)");w=b,A(),b.addEventListener("scroll",E,{passive:!0}),typeof ResizeObserver<"u"&&(Z=new ResizeObserver(()=>E()),Z.observe(b)),E()},destroy(){u&&cancelAnimationFrame(u),u=0,Z?.disconnect(),Z=null,w?.removeEventListener("scroll",E),k=[],d?.remove(),w=null},window(){return{...m}}}}function Te(e,t,n){let r,o;if(typeof e=="string"){const s=g.PAPER_SIZES[e];if(!s)throw new Error(`未知纸张预设 "${e}"(可用:${Object.keys(g.PAPER_SIZES).join("/")})`);r=s.widthMm,o=s.heightMm}else typeof e=="number"?(r=e,o=t??297):(r=e.widthMm,o=e.heightMm);return n?{widthMm:o,heightMm:r}:{widthMm:r,heightMm:o}}class Ye{constructor(){this.pages=[],this.pending=null,this.templateParams=void 0}params(t){return this.templateParams=t,this}page(t="a4",n={},r){this.collect();const s=r!=null&&!("margins"in r)&&!("landscape"in r)&&("marginTopMm"in r||"marginBottomMm"in r||"marginLeftMm"in r||"marginRightMm"in r)?{margins:r}:typeof n=="number"?r??{}:n,{widthMm:i,heightMm:a}=Te(t,typeof n=="number"?n:void 0,s.landscape),l=s.margins??{};return this.pending=new qe(this,{widthMm:i,heightMm:a,marginTopMm:l.marginTopMm??10,marginBottomMm:l.marginBottomMm??10,marginLeftMm:l.marginLeftMm??10,marginRightMm:l.marginRightMm??10}),this.pending}collect(){this.pending&&(this.pages.push(this.pending.buildPage()),this.pending=null)}build(){if(this.collect(),this.pages.length===0)throw new Error("模板至少需要一页:先调用 .page() 添加内容");return this.templateParams?{params:this.templateParams,pages:this.pages}:{pages:this.pages}}}class qe{constructor(t,n){this.parent=t,this.page=n,this.components=[]}text(t,n={},r={}){return this.components.push({kind:"text",leftMm:n.leftMm??0,topMm:n.topMm??0,widthMm:n.widthMm??60,heightMm:n.heightMm??8,text:t,style:r}),this}list(t,n,r,o={}){return this.components.push({kind:"list",leftMm:n.leftMm??10,topMm:n.topMm??30,widthMm:n.widthMm??180,dataset:t,columns:r,...o}),this}rect(t,n={}){return this.components.push({kind:"rect",...t,style:n}),this}line(t,n,r,o={}){return this.components.push({kind:"line",leftMm:t,topMm:n,widthMm:r,heightMm:.5,style:o}),this}barcode(t,n){return this.components.push({kind:"barcode",leftMm:n.leftMm??0,topMm:n.topMm??0,widthMm:n.widthMm??40,heightMm:n.heightMm??12,fallbackText:t}),this}qrcode(t,n,r,o){return this.components.push({kind:"qrcode",leftMm:r,topMm:o,widthMm:n,heightMm:n,fallbackText:t}),this}buildPage(){return{...this.page,components:this.components}}done(){return this.parent}build(){return this.done().build()}}function bt(){return new Ye}function U(e,t,n=!1){if(!Number.isFinite(e)||(n?e<0:e<=0))throw new Error(`${t} 必须是有限${n?"非负":"正"}数`)}function ce(e,t,n){if(e!==void 0&&!["left","center","right","justify",...n?["auto"]:[]].includes(e))throw new Error(`${t} 必须是 left/center/right/justify${n?"/auto":""}`)}function wt(e){if(!e||typeof e!="object")throw new Error("标准明细模板需要 options 对象");if(!Array.isArray(e.columns)||e.columns.length===0)throw new Error("columns 至少需要一列");const t=e.paper===void 0?"a4":e.paper;if(typeof t!="string"&&(typeof t!="object"||t===null)||e.landscape!==void 0&&typeof e.landscape!="boolean")throw new Error("paper 必须是纸张预设名或 { widthMm, heightMm } 对象,landscape 必须是布尔值");if(typeof t=="string"&&!Object.prototype.hasOwnProperty.call(g.PAPER_SIZES,t))throw new Error(`未知纸张预设 "${t}"(可用:${Object.keys(g.PAPER_SIZES).join("/")})`);const{widthMm:n,heightMm:r}=Te(t,void 0,e.landscape);if(U(n,"纸张 widthMm"),U(r,"纸张 heightMm"),e.margins!==void 0&&(!e.margins||typeof e.margins!="object"||Array.isArray(e.margins)))throw new Error("margins 必须是页边距对象");const o=e.margins??{},s={marginTopMm:o.marginTopMm===void 0?10:o.marginTopMm,marginBottomMm:o.marginBottomMm===void 0?10:o.marginBottomMm,marginLeftMm:o.marginLeftMm===void 0?10:o.marginLeftMm,marginRightMm:o.marginRightMm===void 0?10:o.marginRightMm};for(const[A,v]of Object.entries(s))U(v,A,!0);const i=n-s.marginLeftMm-s.marginRightMm,a=r-s.marginTopMm-s.marginBottomMm;if(i<=0||a<=0)throw new Error("页边距占满纸张,必须保留正的可用宽度和高度");const l=e.dataset===void 0?"detail":e.dataset;if(typeof l!="string"||!l.trim())throw new Error("dataset 必须是非空字符串");if(e.title!==void 0&&typeof e.title!="string")throw new Error("title 必须是字符串");const c=e.titleHeightMm===void 0?10:e.titleHeightMm,f=e.titleGapMm===void 0?4:e.titleGapMm;U(c,"titleHeightMm"),U(f,"titleGapMm",!0);const h=e.title!==void 0&&e.title.length>0,m=h?c+f:0;if(m>=a)throw new Error("标题高度与间距占满页面,必须为表格保留可用高度");if(e.listOptions!==void 0&&(!e.listOptions||typeof e.listOptions!="object"||Array.isArray(e.listOptions)))throw new Error("listOptions 必须是列表配置对象");const w=e.listOptions??{};ce(w.align,"listOptions.align",!0),ce(w.headerAlign,"listOptions.headerAlign",!1);for(const A of["fontSizePt","rowHeightMm","paddingMm"]){const v=w[A];v!==void 0&&U(v,`listOptions.${A}`,A==="paddingMm")}let d=0,p=0;for(const[A,v]of e.columns.entries()){if(!v||typeof v.header!="string"||typeof v.field!="string"||!v.field.trim())throw new Error(`columns[${A}] 需要字符串 header 与非空 field`);ce(v.align,`columns[${A}].align`,!0),ce(v.headerAlign,`columns[${A}].headerAlign`,!1),v.flex!==void 0&&U(v.flex,`columns[${A}].flex`),v.widthMm!==void 0?(U(v.widthMm,`columns[${A}].widthMm`),d+=v.widthMm):p=Math.max(p,v.flex??1)}if(d>i)throw new Error(`固定列宽 ${d}mm 超出页面可用宽度 ${i}mm`);const S=i-d;if(p>0&&S<=0)throw new Error("固定列占满页面可用宽度,自动列需要正的剩余宽度");const T=p>0?e.columns.reduce((A,v)=>A+(v.widthMm===void 0?(v.flex??1)/p:0),0):0,u=e.columns.map(({widthMm:A,flex:v,...V},y)=>{const E=A??S*((v??1)/p)/T;return U(E,`columns[${y}] 分配后的 widthMm`),{...V,widthMm:E}}),k={widthMm:n,heightMm:r,...s,components:[]};return h&&k.components.push({kind:"text",text:e.title,leftMm:s.marginLeftMm,topMm:s.marginTopMm,widthMm:i,heightMm:c,style:{fontSize:16,bold:!0,align:"center",verticalAlign:"middle",...e.titleStyle}}),k.components.push({align:"auto",headerAlign:"center",...w,kind:"list",dataset:l,columns:u,leftMm:s.marginLeftMm,topMm:s.marginTopMm+m,widthMm:p>0?i:d}),e.params===void 0?{pages:[k]}:{params:e.params,pages:[k]}}const Ee="1.0";class X extends Error{constructor(t){super(`模板校验失败:${t.slice(0,3).map(n=>`${n.path}: ${n.message}`).join("; ")}`),this.errors=t,this.name="ReportTemplateError"}}const St=new Set(["__proto__","prototype","constructor"]),$t=new Set(["schemaVersion","id","name","revision","description","template"]),Tt=new Set(["left","center","right","justify"]),ue=2e5,Et=64,ve=1e7;function C(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function Pe(e,t){const n=new Set;let r=0,o=0,s=!1;const i=(a,l,c)=>{if(s)return;if(++r>ue){r===ue+1&&t.push({path:l,message:"模板超过 JSON 节点安全上限"});return}if(c>Et){t.push({path:l,message:"模板嵌套层级过深"});return}if(typeof a=="string"){o+=a.length,o>ve&&(s=!0,t.push({path:l,message:"模板文本超过 1000 万字符的安全上限"}));return}if(a===null||typeof a=="boolean"||typeof a=="number"&&Number.isFinite(a))return;if(!a||typeof a!="object"){t.push({path:l,message:"模板只允许 JSON 值,不允许函数、undefined 或非有限数字"});return}if(n.has(a)){t.push({path:l,message:"模板不能包含循环引用"});return}const f=Object.getPrototypeOf(a);if(Array.isArray(a)&&f!==Array.prototype||!Array.isArray(a)&&f!==Object.prototype&&f!==null){t.push({path:l,message:"模板对象必须使用普通 JSON 对象原型"});return}if(Array.isArray(a)&&(Object.keys(a).length!==a.length||Object.keys(a).some(h=>!/^(0|[1-9]\d*)$/.test(h)||Number(h)>=a.length))){t.push({path:l,message:"模板数组不能包含空位或额外属性"});return}if(Object.getOwnPropertySymbols(a).length>0){t.push({path:l,message:"模板不能包含 Symbol 键"});return}n.add(a);for(const[h,m]of Object.entries(Object.getOwnPropertyDescriptors(a)))if(!(Array.isArray(a)&&h==="length")&&(St.has(h)?t.push({path:`${l}.${h}`,message:"模板不能包含可能污染原型的键"}):m.get||m.set||!m.enumerable?t.push({path:`${l}.${h}`,message:"模板不能包含访问器或不可枚举字段"}):i(m.value,`${l}.${h}`,c+1),r>ue||s))break;n.delete(a)};i(e,"$",0)}function B(e,t,n,r,o=!1){const s=e[t];s===void 0&&!o||(typeof s!="string"||o&&!s.trim())&&r.push({path:`${n}.${t}`,message:o?"应为非空字符串":"应为字符串"})}function z(e,t,n,r,o=0,s=!1){const i=e[t];i===void 0&&!s||(typeof i!="number"||!Number.isFinite(i)||i<o)&&r.push({path:`${n}.${t}`,message:`应为大于等于 ${o} 的有限数字`})}function re(e,t,n,r){e[t]!==void 0&&typeof e[t]!="boolean"&&r.push({path:`${n}.${t}`,message:"应为布尔值"})}function Q(e,t,n,r=!1){e!==void 0&&!Tt.has(e)&&!(r&&e==="auto")&&n.push({path:t,message:`未知对齐方式${r?"(支持 auto)":""}`})}function J(e,t,n){if(e!==void 0){if(!C(e)){n.push({path:t,message:"样式应为对象"});return}Q(e.align,`${t}.align`,n),e.verticalAlign!==void 0&&!["top","middle","bottom"].includes(e.verticalAlign)&&n.push({path:`${t}.verticalAlign`,message:"未知垂直对齐方式"});for(const r of["fontSize","fontSizePx","lineWidthPt","lineHeight"])z(e,r,t,n);for(const r of["letterSpacing","rotateDeg"])z(e,r,t,n,-1/0);z(e,"opacity",t,n),typeof e.opacity=="number"&&e.opacity>1&&n.push({path:`${t}.opacity`,message:"透明度不能大于 1"});for(const r of["bold","italic","underline","borderTop","borderRight","borderBottom","borderLeft"])re(e,r,t,n);for(const r of["fontFamily","color","backgroundColor","lineColor","borderColor"])B(e,r,t,n)}}function vt(e,t,n){if(e===void 0)return;if(!Array.isArray(e)){n.push({path:t,message:"参数定义应为数组"});return}const r=new Set;e.forEach((o,s)=>{const i=`${t}.${s}`;if(!C(o)){n.push({path:i,message:"参数定义应为对象"});return}B(o,"field",i,n,!0),typeof o.field=="string"&&(r.has(o.field)&&n.push({path:`${i}.field`,message:"参数名重复"}),r.add(o.field)),o.type!==void 0&&!["text","number","date","select"].includes(o.type)&&n.push({path:`${i}.type`,message:"未知参数类型"});for(const a of["label","defaultValue","placeholder"])B(o,a,i,n);re(o,"required",i,n),o.options!==void 0&&(Array.isArray(o.options)?o.options.forEach((a,l)=>{const c=`${i}.options.${l}`;if(!C(a))n.push({path:c,message:"选项应为对象"});else for(const f of["label","value"])typeof a[f]!="string"&&n.push({path:`${c}.${f}`,message:"选项标签与值应为字符串(允许空白选项)"})}):n.push({path:`${i}.options`,message:"选项应为数组"}))})}function Pt(e,t,n){for(const r of["leftMm","topMm"])z(e,r,t,n,-1/0,!0);z(e,"widthMm",t,n,Number.MIN_VALUE,!0),B(e,"dataset",t,n,!0),!Array.isArray(e.columns)||e.columns.length===0?n.push({path:`${t}.columns`,message:"列表至少需要一列"}):e.columns.forEach((r,o)=>{const s=`${t}.columns.${o}`;if(!C(r)){n.push({path:s,message:"列应为对象"});return}if(typeof r.header!="string"&&n.push({path:`${s}.header`,message:"表头应为字符串(允许空白表头)"}),B(r,"field",s,n,!0),z(r,"widthMm",s,n,Number.MIN_VALUE,!0),J(r.style,`${s}.style`,n),J(r.headerStyle,`${s}.headerStyle`,n),Q(r.align,`${s}.align`,n,!0),Q(r.headerAlign,`${s}.headerAlign`,n),r.format!==void 0)if(!C(r.format)||!["number","percent","date"].includes(r.format.kind))n.push({path:`${s}.format`,message:"未知列格式"});else{const i=r.format.digits;i!==void 0&&(typeof i!="number"||!Number.isInteger(i)||i<0||i>100)&&n.push({path:`${s}.format.digits`,message:"小数位应为 0–100 的整数"}),re(r.format,"thousands",`${s}.format`,n),B(r.format,"pattern",`${s}.format`,n)}r.rules!==void 0&&(Array.isArray(r.rules)?r.rules.forEach((i,a)=>{const l=`${s}.rules.${a}`;if(!C(i)||!C(i.when)){n.push({path:l,message:"条件格式缺少 when 对象"});return}B(i.when,"field",`${l}.when`,n,!0),[">",">=","<","<=","==","!="].includes(i.when.op)||n.push({path:`${l}.when.op`,message:"未知条件运算符"}),typeof i.when.value!="number"&&typeof i.when.value!="string"&&n.push({path:`${l}.when.value`,message:"条件值应为数字或字符串"}),J(i.style,`${l}.style`,n)}):n.push({path:`${s}.rules`,message:"条件格式应为数组"}))});for(const r of["fontSizePt","rowHeightMm"])z(e,r,t,n,Number.MIN_VALUE);z(e,"paddingMm",t,n);for(const r of["headerBold","headerEveryPage","border"])re(e,r,t,n);if(B(e,"headerBackgroundColor",t,n),Q(e.align,`${t}.align`,n,!0),Q(e.headerAlign,`${t}.headerAlign`,n),J(e.headerStyle,`${t}.headerStyle`,n),e.groupBy!==void 0)if(!C(e.groupBy))n.push({path:`${t}.groupBy`,message:"分组应为对象"});else{B(e.groupBy,"field",`${t}.groupBy`,n,!0);for(const r of["headerTemplate","subtotalLabel"])B(e.groupBy,r,`${t}.groupBy`,n);for(const r of["headerStyle","subtotalStyle"])J(e.groupBy[r],`${t}.groupBy.${r}`,n);re(e.groupBy,"keepWithNext",`${t}.groupBy`,n),e.groupBy.subtotal!==void 0&&(!Array.isArray(e.groupBy.subtotal)||e.groupBy.subtotal.some(r=>typeof r!="string"))&&n.push({path:`${t}.groupBy.subtotal`,message:"小计字段应为字符串数组"})}e.grandTotal!==void 0&&typeof e.grandTotal!="boolean"&&(C(e.grandTotal)?(B(e.grandTotal,"label",`${t}.grandTotal`,n),J(e.grandTotal.style,`${t}.grandTotal.style`,n),e.grandTotal.fields!==void 0&&(!Array.isArray(e.grandTotal.fields)||e.grandTotal.fields.some(r=>typeof r!="string"))&&n.push({path:`${t}.grandTotal.fields`,message:"合计字段应为字符串数组"})):n.push({path:`${t}.grandTotal`,message:"合计应为布尔值或对象"}))}function Xe(e,t,n){J(e.style,`${t}.style`,n);for(const o of["text","rawText","imageData","fallbackText","nid","rkey"])B(e,o,t,n);if(e.lines!==void 0&&(!Array.isArray(e.lines)||e.lines.some(o=>typeof o!="string"))&&n.push({path:`${t}.lines`,message:"文本行应为字符串数组"}),Array.isArray(e.richParagraphs)&&e.richParagraphs.forEach((o,s)=>{if(!C(o))return;const i=`${t}.richParagraphs.${s}`;Q(o.align,`${i}.align`,n);for(const a of["lineHeight","spaceBeforeMm","spaceAfterMm","indentMm"])z(o,a,i,n);Array.isArray(o.segments)&&o.segments.forEach((a,l)=>{const c=`${i}.segments.${l}`;if(!C(a)){n.push({path:c,message:"富文本片段应为对象"});return}a.kind!==void 0&&a.kind!=="text"&&a.kind!=="field"&&n.push({path:`${c}.kind`,message:"未知富文本片段类型"});for(const f of["text","field"])B(a,f,c,n);J(a.style,`${c}.style`,n)})}),!C(e.grid))return;const r=e.grid;z(r,"gapMm",`${t}.grid`,n);for(const o of["colWidthsMm","rowHeightsMm"]){const s=r[o];s!==void 0&&(!Array.isArray(s)||s.some(i=>typeof i!="number"||!Number.isFinite(i)||i<0))&&n.push({path:`${t}.grid.${o}`,message:"网格尺寸应为有限非负数字数组"})}Array.isArray(r.cells)&&r.cells.forEach((o,s)=>{Array.isArray(o)&&o.forEach((i,a)=>{const l=`${t}.grid.cells.${s}.${a}`;if(i!==null){if(!C(i)){n.push({path:l,message:"网格单元格应为对象或 null"});return}for(const c of["text","rawText","numberFormat"])B(i,c,l,n);for(const c of["colSpan","rowSpan"]){const f=i[c];f!==void 0&&(typeof f!="number"||!Number.isInteger(f)||f<=0)&&n.push({path:`${l}.${c}`,message:"跨度应为正整数"})}i.value!==void 0&&i.value!==null&&!["string","number","boolean"].includes(typeof i.value)&&n.push({path:`${l}.value`,message:"Excel 原始值应为字符串、数字、布尔值或 null"}),J(i.style,`${l}.style`,n),i.children!==void 0&&(Array.isArray(i.children)?i.children.forEach((c,f)=>{C(c)&&Xe(c,`${l}.children.${f}`,n)}):n.push({path:`${l}.children`,message:"嵌套组件应为数组"}))}})})}function Ae(e){const t=[],n=[];return Pe(e,t),t.length?{ok:!1,errors:t,warnings:n}:(C(e)?(e.schemaVersion!==void 0&&e.schemaVersion!==x.RENDER_PLAN_SCHEMA_VERSION&&t.push({path:"$.schemaVersion",message:"不支持的模板 schemaVersion"}),vt(e.params,"$.params",t),!Array.isArray(e.pages)||e.pages.length===0?t.push({path:"$.pages",message:"模板至少需要一页"}):e.pages.forEach((r,o)=>{const s=`$.pages.${o}`;if(!C(r)){t.push({path:s,message:"页应为对象"});return}z(r,"widthMm",s,t,Number.MIN_VALUE,!0),z(r,"heightMm",s,t,Number.MIN_VALUE,!0),typeof r.widthMm=="number"&&r.widthMm>=3e3&&t.push({path:`${s}.widthMm`,message:"页宽应小于 3000mm"}),typeof r.heightMm=="number"&&r.heightMm>=5e3&&t.push({path:`${s}.heightMm`,message:"页高应小于 5000mm"});for(const i of["marginTopMm","marginBottomMm","marginLeftMm","marginRightMm"])z(r,i,s,t);typeof r.widthMm=="number"&&Number(r.marginLeftMm??0)+Number(r.marginRightMm??0)>=r.widthMm&&t.push({path:s,message:"横向页边距占满页面"}),typeof r.heightMm=="number"&&Number(r.marginTopMm??0)+Number(r.marginBottomMm??0)>=r.heightMm&&t.push({path:s,message:"纵向页边距占满页面"}),Array.isArray(r.components)?r.components.forEach((i,a)=>{const l=`${s}.components.${a}`;C(i)&&i.kind==="list"?Pt(i,l,t):(he(i,l,t,n),C(i)&&Xe(i,l,t))}):t.push({path:`${s}.components`,message:"组件应为数组"})})):t.push({path:"$",message:"模板应为对象"}),{ok:t.length===0,errors:t,warnings:n})}function Ke(e){const t=[],n=[];if(Pe(e,t),t.length)return{ok:!1,errors:t,warnings:n};if(!C(e))t.push({path:"$",message:"模板文档应为对象"});else{for(const o of Object.keys(e))$t.has(o)||t.push({path:`$.${o}`,message:"模板文档存在未知字段;只保存模板与描述,不保存取数或业务数据"});e.schemaVersion!==Ee&&t.push({path:"$.schemaVersion",message:"不支持的模板文档版本"});for(const o of["id","name"])B(e,o,"$",t,!0);for(const o of["revision","description"])B(e,o,"$",t);const r=Ae(e.template);t.push(...r.errors.map(o=>({...o,path:`$.template${o.path.slice(1)}`}))),n.push(...r.warnings.map(o=>({...o,path:`$.template${o.path.slice(1)}`})))}return{ok:t.length===0,errors:t,warnings:n}}function ge(e){const t=Ke(e);if(!t.ok)throw new X(t.errors)}function At(e){const t=[];if(Pe(e,t),t.length)throw new X(t);const n={...e,schemaVersion:e.schemaVersion??Ee};return ge(n),me(n)}function kt(e){let t=e;if(typeof e=="string"){if(e.length>ve)throw new X([{path:"$",message:"模板 JSON 超过 1000 万字符的导出/导入上限"}]);try{t=JSON.parse(e)}catch{throw new X([{path:"$",message:"模板 JSON 语法无效"}])}}return ge(t),me(t)}function xt(e){ge(e);const t=JSON.stringify(e,null,2);if(t.length>ve)throw new X([{path:"$",message:"模板 JSON 超过 1000 万字符的导出/导入上限"}]);return t}function me(e){return JSON.parse(JSON.stringify(e))}function Nt(e=[]){const t=new Map,n={get:r=>{const o=t.get(r);return o?me(o):void 0},list:()=>Array.from(t.values(),({template:r,...o})=>({...o})),register:(r,o={})=>{if(ge(r),t.has(r.id)&&!o.replace)throw new X([{path:"$.id",message:`模板 ID ${r.id} 已注册;更新需显式 replace:true`}]);t.set(r.id,me(r))},remove:r=>t.delete(r)};for(const r of e)n.register(r);return n}function Rt(e){if(!("mapData"in e))return e;const{load:t,mapData:n,...r}=e;return{...r,load:async o=>{const s=await t(o);q(o.signal);try{return n(s,o)}catch(i){throw new I("validate",`报表 ${o.reportId} 数据映射失败`,i)}}}}class I extends Error{constructor(t,n,r){super(n),this.name="ReportSourceError",this.code=t,this.cause=r}}function fe(e){const t=new Error("报表加载已取消");return t.name="AbortError",Object.defineProperty(t,"cause",{value:e.reason}),t}function q(e){if(e?.aborted)throw fe(e)}function He(e,t){return t?new Promise((n,r)=>{const o=()=>t.removeEventListener("abort",s),s=()=>{o(),r(fe(t))};t.addEventListener("abort",s,{once:!0}),t.aborted&&s(),Promise.resolve(e).then(i=>{o(),n(i)},i=>{o(),r(i)})}):Promise.resolve(e)}function Ct(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new I("validate",`报表 ${t} 的数据集应为对象`);for(const[n,r]of Object.entries(e))if(!Array.isArray(r)||r.some(o=>!o||typeof o!="object"||Array.isArray(o)))throw new I("validate",`报表 ${t} 的数据集 ${n} 应为对象数组`)}function _t(e,t={}){function n(o){const s=Object.prototype.hasOwnProperty.call(e,o)?e[o]:void 0;if(!s)throw new I("config",`未注册报表 ${o}`);return s}return Object.assign(async({reportIds:o,params:s,signal:i,onWarning:a})=>{q(i);const l=[...o],c={...s};if(l.length===0)throw new I("config","缺少报表 ID");const f=l.map(n),h=[],m=[];for(const[S,T]of f.entries()){q(i);const u=l[S],k={reportId:u,params:{...c},signal:i};let A;if(typeof T.template=="function")try{A=await He(T.template(k),i),q(i);const y=Ae(A);if(!y.ok)throw new I("validate",`报表 ${u} 模板校验失败:${y.errors.slice(0,3).map(E=>`${E.path}: ${E.message}`).join("; ")}`);m.push(...y.warnings.map(E=>`${u}: ${E.path}: ${E.message}`))}catch(y){throw i?.aborted?fe(i):y instanceof I?y:y instanceof X?new I("validate",`报表 ${u} 模板校验失败:${y.message}`,y):new I("fetch",`报表 ${u} 模板加载失败:${y instanceof Error?y.message:String(y)}`,y)}else A=T.template;let v;try{v=await He(T.load?T.load(k):T.datasets??{},i)}catch(y){throw i?.aborted?fe(i):y instanceof I?y:new I("fetch",`报表 ${u} 取数失败:${y instanceof Error?y.message:String(y)}`,y)}q(i),Ct(v,u);let V;try{V=ze(A,v,T.pagination)}catch(y){throw new I("validate",`报表 ${u} 排版失败:${y instanceof Error?y.message:String(y)}`,y)}for(const y of V.plan.pages)h.push(y);m.push(...V.warnings.map(y=>`${u}: ${y}`))}q(i);const w={schemaVersion:x.RENDER_PLAN_SCHEMA_VERSION,pages:h},d=be(w);if(!d.ok)throw new I("validate",`渲染计划校验失败:${d.errors.slice(0,3).map(S=>`${S.path}: ${S.message}`).join("; ")}`);m.push(...d.warnings.map(S=>`${S.path}: ${S.message}`));const p=a??t.onWarning??(S=>console.warn("[mach-report]",S));for(const S of m)q(i),p(S);return q(i),w},{getParamDefs:o=>et.getReportParamDefs(e,o)})}exports.CSS_DPI=g.CSS_DPI;exports.DEFAULT_BORDER_PT=g.DEFAULT_BORDER_PT;exports.DEFAULT_FONT_PT=g.DEFAULT_FONT_PT;exports.DEFAULT_STROKE_COLOR=g.DEFAULT_STROKE_COLOR;exports.DEFAULT_TEXT_COLOR=g.DEFAULT_TEXT_COLOR;exports.LINE_HEIGHT=g.LINE_HEIGHT;exports.MM_PER_INCH=g.MM_PER_INCH;exports.PAPER_SIZES=g.PAPER_SIZES;exports.SCHEMA_VERSION_MACH=g.SCHEMA_VERSION_MACH;exports.mmToPx=g.mmToPx;exports.mmToPxFactor=g.mmToPxFactor;exports.ptToPx=g.ptToPx;exports.pxToMm=g.pxToMm;exports.resolveBoxBorders=g.resolveBoxBorders;exports.resolveFontSize=g.resolveFontSize;exports.resolveGridLayout=g.resolveGridLayout;exports.resolveLineHeight=g.resolveLineHeight;exports.resolveStroke=g.resolveStroke;exports.resolveTextColor=g.resolveTextColor;exports.round=g.round;exports.RENDER_PLAN_SCHEMA_VERSION=x.RENDER_PLAN_SCHEMA_VERSION;exports.charWidthEm=x.charWidthEm;exports.createCanvasMeasurer=x.createCanvasMeasurer;exports.createHeuristicMeasurer=x.createHeuristicMeasurer;exports.heuristicMeasurer=x.heuristicMeasurer;exports.isImageBackedComponent=x.isImageBackedComponent;exports.isRichText=x.isRichText;exports.measureTextMm=x.measureTextMm;exports.wrapText=x.wrapText;exports.PageBuilder=qe;exports.REPORT_TEMPLATE_DOCUMENT_VERSION=Ee;exports.ReportSourceError=I;exports.ReportTemplateError=X;exports.TemplateBuilder=Ye;exports.assertRenderPlan=it;exports.classOf=L;exports.computePageWindow=Ve;exports.createCanvasPager=Mt;exports.createReportSource=_t;exports.createTableTemplate=wt;exports.createTemplate=bt;exports.createTemplateRegistry=Nt;exports.defineReport=Rt;exports.defineReportTemplate=At;exports.evalStyleRule=Fe;exports.formatValue=Me;exports.measurePageSizePx=$e;exports.paginateTemplate=ze;exports.parseDateLocal=Oe;exports.parseReportTemplate=kt;exports.planFontSizePx=ut;exports.renderComponent=we;exports.renderPage=We;exports.renderPlan=ht;exports.renderPlanToCanvas=Je;exports.renderPlanToImages=yt;exports.resolvePaper=Te;exports.serializeReportTemplate=xt;exports.toExcelNumberFormat=ye;exports.toNumber=de;exports.validateComponent=he;exports.validatePage=Be;exports.validateRenderPlan=be;exports.validateReportTemplate=Ae;exports.validateReportTemplateDocument=Ke;
5
+ `):C.wrapText(t.text??"",t.widthMm,{fontSizePt:h},s).join(`
6
+ `);Je(e,{text:f,fontSizePx:g.resolveFontSize(m,o).fontSizePx,bold:m.bold??!1,color:g.resolveTextColor(m),align:m.align??"left",lineHeight:g.resolveLineHeight(m)},i,a,l,d,r,m.verticalAlign);break}case"rect":{const h=g.resolveStroke(m),f=g.resolveBoxBorders(m);m.backgroundColor&&(e.fillStyle=m.backgroundColor,e.fillRect(i,a,l,d)),f.inner&&(e.strokeStyle=h.color,e.lineWidth=g.ptToPx(h.widthPt,o),e.beginPath(),f.top&&(e.moveTo(i,a),e.lineTo(i+l,a)),f.right&&(e.moveTo(i+l,a),e.lineTo(i+l,a+d)),f.bottom&&(e.moveTo(i+l,a+d),e.lineTo(i,a+d)),f.left&&(e.moveTo(i,a+d),e.lineTo(i,a)),e.stroke());break}case"line":{const h=g.resolveStroke({...m,borderColor:m.lineColor??m.borderColor});e.strokeStyle=h.color,e.lineWidth=g.ptToPx(h.widthPt,o),e.beginPath(),e.moveTo(i,a+d/2),e.lineTo(i+l,a+d/2),e.stroke();break}case"ellipse":{const h=g.resolveStroke(m);e.strokeStyle=h.color,e.lineWidth=g.ptToPx(h.widthPt,o),m.backgroundColor&&(e.fillStyle=m.backgroundColor,e.beginPath(),e.ellipse(i+l/2,a+d/2,l/2,d/2,0,0,Math.PI*2),e.fill()),e.beginPath(),e.ellipse(i+l/2,a+d/2,l/2,d/2,0,0,Math.PI*2),e.stroke();break}}}async function $t(e,t={}){if(typeof document>"u")throw new Error("renderPlanToImages 需要浏览器环境(SSR/Node 请在客户端钩子中调用)");const{format:n="png",quality:o=.92,...r}=t,{canvases:s}=qe(e,r),i=n==="jpeg"?"image/jpeg":"image/png",a=[];for(const l of s)a.push(await new Promise(d=>l.toBlob(m=>d(m),i,o))??new Blob([],{type:i}));return{blobs:a}}function qe(e,t={}){if(typeof document>"u")throw new Error("renderPlanToCanvas 需要浏览器环境(SSR 请在客户端钩子中调用)");const n=t.dpr??(typeof devicePixelRatio=="number"?devicePixelRatio:1),o=t.dpi??96,r=o/96*Te,s=t.fontFamily??'"Microsoft YaHei", sans-serif',i=t.measurer??C.heuristicMeasurer,a=Math.max(0,t.start??0),l=Math.min(e.pages.length-1,t.end??e.pages.length-1),d=[];for(let m=a;m<=l;m++){const h=e.pages[m],{canvas:f,ctx:u}=Mt(h,n,o);for(const c of h.components)Ye(u,c,r,o,s,i);d.push(f)}return{canvases:d}}function St(e,t={}){const n=t.dpr??(typeof devicePixelRatio=="number"?devicePixelRatio:1),o=t.dpi??96,r=o/96*Te,s=t.fontFamily??'"Microsoft YaHei", sans-serif',i=t.measurer??C.heuristicMeasurer,a=t.overscan??1,l=t.gapPx??18,d=t.pagesPerFrame??1,m=e.pages.map(y=>y.pageHeightMm*r),h=Math.max(1,...e.pages.map(y=>y.pageWidthMm*r)),f={start:0,end:-1};let u=null,c=null,w=null,M=null,E=[],S=0,x=[];function T(){const y=u;y.innerHTML="",c=document.createElement("div"),c.style.cssText=`width:${h}px;margin:0 auto;`,w=document.createElement("div"),M=document.createElement("div"),c.appendChild(w),E=[];const B=Math.min(e.pages.length,3+a*2);for(let A=0;A<B;A++){const k=document.createElement("div");k.style.cssText=`margin:0 auto ${l}px;box-shadow:0 2px 8px rgba(0,0,0,.35);background:#fff;`,c.appendChild(k),E.push(k)}c.appendChild(M),y.appendChild(c)}function O(y,B){const A=e.pages[B],{widthPx:k,heightPx:R}=ve(A,o);let I=y.firstElementChild;(!I||I.tagName!=="CANVAS")&&(I=document.createElement("canvas"),y.innerHTML="",y.appendChild(I)),I.width=Math.round(k*n),I.height=Math.round(R*n),I.style.width=`${k}px`,I.style.height=`${R}px`;const J=I.getContext("2d");if(J){J.setTransform(1,0,0,1,0,0),J.scale(n,n),J.fillStyle="#ffffff",J.fillRect(0,0,k,R);for(const Q of A.components)Ye(J,Q,r,o,s,i);I.dataset.canvasPage=String(B),y.style.height=`${R}px`,y.style.width=`${k}px`}}function L(y){f.start=y.start,f.end=y.end,w.style.height=`${y.start*l+m.slice(0,y.start).reduce((k,R)=>k+R,0)}px`;const B=e.pages.length-1-y.end;M.style.height=`${B*l+m.slice(y.end+1).reduce((k,R)=>k+R,0)}px`;const A=y.end-y.start+1;E.forEach((k,R)=>{if(R<A){k.style.display="";const I=y.start+R;k.style.height=`${m[I]??0}px`}else k.style.display="none"}),x=[];for(let k=y.start;k<=y.end;k++){const R=E[k-y.start];if(!R)continue;R.firstElementChild?.dataset?.canvasPage!==String(k)&&x.push(k)}P()}function P(){S||x.length===0||(S=requestAnimationFrame(()=>{S=0;let y=Math.max(1,d);for(;x.length>0&&y-- >0;){const B=x.shift(),A=E[B-f.start];A&&O(A,B)}x.length>0&&P()}))}function v(){S||(S=requestAnimationFrame(()=>{S=0;const y=u,B=Ge({pageHeightsPx:m,viewportHeightPx:y.clientHeight,scrollTopPx:y.scrollTop,overscan:a,gapPx:l});B.start!==f.start||B.end!==f.end?L(B):P()}))}let p=null;return{attach(y){if(typeof document>"u")throw new Error("createCanvasPager 需要浏览器环境(SSR 请在客户端钩子中调用 attach)");u=y,T(),y.addEventListener("scroll",v,{passive:!0}),typeof ResizeObserver<"u"&&(p=new ResizeObserver(()=>v()),p.observe(y)),v()},destroy(){S&&cancelAnimationFrame(S),S=0,p?.disconnect(),p=null,u?.removeEventListener("scroll",v),x=[],c?.remove(),u=null},window(){return{...f}}}}function Ee(e,t,n){let o,r;if(typeof e=="string"){const s=g.PAPER_SIZES[e];if(!s)throw new Error(`未知纸张预设 "${e}"(可用:${Object.keys(g.PAPER_SIZES).join("/")})`);o=s.widthMm,r=s.heightMm}else typeof e=="number"?(o=e,r=t??297):(o=e.widthMm,r=e.heightMm);return n?{widthMm:r,heightMm:o}:{widthMm:o,heightMm:r}}class Ke{constructor(){this.pages=[],this.pending=null,this.templateParams=void 0}params(t){return this.templateParams=t,this}page(t="a4",n={},o){this.collect();const s=o!=null&&!("margins"in o)&&!("landscape"in o)&&("marginTopMm"in o||"marginBottomMm"in o||"marginLeftMm"in o||"marginRightMm"in o)?{margins:o}:typeof n=="number"?o??{}:n,{widthMm:i,heightMm:a}=Ee(t,typeof n=="number"?n:void 0,s.landscape),l=s.margins??{};return this.pending=new Xe(this,{widthMm:i,heightMm:a,marginTopMm:l.marginTopMm??10,marginBottomMm:l.marginBottomMm??10,marginLeftMm:l.marginLeftMm??10,marginRightMm:l.marginRightMm??10}),this.pending}collect(){this.pending&&(this.pages.push(this.pending.buildPage()),this.pending=null)}build(){if(this.collect(),this.pages.length===0)throw new Error("模板至少需要一页:先调用 .page() 添加内容");return this.templateParams?{params:this.templateParams,pages:this.pages}:{pages:this.pages}}}class Xe{constructor(t,n){this.parent=t,this.page=n,this.components=[]}text(t,n={},o={}){return this.components.push({kind:"text",leftMm:n.leftMm??0,topMm:n.topMm??0,widthMm:n.widthMm??60,heightMm:n.heightMm??8,text:t,style:o}),this}list(t,n,o,r={}){return this.components.push({kind:"list",leftMm:n.leftMm??10,topMm:n.topMm??30,widthMm:n.widthMm??180,dataset:t,columns:o,...r}),this}rect(t,n={}){return this.components.push({kind:"rect",...t,style:n}),this}line(t,n,o,r={}){return this.components.push({kind:"line",leftMm:t,topMm:n,widthMm:o,heightMm:.5,style:r}),this}barcode(t,n){return this.components.push({kind:"barcode",leftMm:n.leftMm??0,topMm:n.topMm??0,widthMm:n.widthMm??40,heightMm:n.heightMm??12,fallbackText:t}),this}qrcode(t,n,o,r){return this.components.push({kind:"qrcode",leftMm:o,topMm:r,widthMm:n,heightMm:n,fallbackText:t}),this}buildPage(){return{...this.page,components:this.components}}done(){return this.parent}build(){return this.done().build()}}function Tt(){return new Ke}function U(e,t,n=!1){if(!Number.isFinite(e)||(n?e<0:e<=0))throw new Error(`${t} 必须是有限${n?"非负":"正"}数`)}function ce(e,t,n){if(e!==void 0&&!["left","center","right","justify",...n?["auto"]:[]].includes(e))throw new Error(`${t} 必须是 left/center/right/justify${n?"/auto":""}`)}function vt(e){if(!e||typeof e!="object")throw new Error("标准明细模板需要 options 对象");if(!Array.isArray(e.columns)||e.columns.length===0)throw new Error("columns 至少需要一列");const t=e.layout??"page";if(t!=="page"&&t!=="wide")throw new Error("layout 必须是 page 或 wide");if(t==="wide"&&(e.paper!==void 0||e.landscape!==void 0))throw new Error('宽表布局不能同时指定 paper 或 landscape;标准纸型请使用 layout: "page"');if(t!=="wide"&&e.columnWidthMm!==void 0)throw new Error('columnWidthMm 仅适用于 layout: "wide"');const n=e.columnWidthMm??30;U(n,"columnWidthMm");const o=e.paper===void 0?"a4":e.paper;if(typeof o!="string"&&(typeof o!="object"||o===null)||e.landscape!==void 0&&typeof e.landscape!="boolean")throw new Error("paper 必须是纸张预设名或 { widthMm, heightMm } 对象,landscape 必须是布尔值");if(typeof o=="string"&&!Object.prototype.hasOwnProperty.call(g.PAPER_SIZES,o))throw new Error(`未知纸张预设 "${o}"(可用:${Object.keys(g.PAPER_SIZES).join("/")})`);const r=Ee(o,void 0,e.landscape);let s=r.widthMm;const i=r.heightMm;if(U(s,"纸张 widthMm"),U(i,"纸张 heightMm"),e.margins!==void 0&&(!e.margins||typeof e.margins!="object"||Array.isArray(e.margins)))throw new Error("margins 必须是页边距对象");const a=e.margins??{},l={marginTopMm:a.marginTopMm===void 0?10:a.marginTopMm,marginBottomMm:a.marginBottomMm===void 0?10:a.marginBottomMm,marginLeftMm:a.marginLeftMm===void 0?10:a.marginLeftMm,marginRightMm:a.marginRightMm===void 0?10:a.marginRightMm};for(const[P,v]of Object.entries(l))U(v,P,!0);let d=s-l.marginLeftMm-l.marginRightMm;const m=i-l.marginTopMm-l.marginBottomMm;if(t==="page"&&d<=0||m<=0)throw new Error("页边距占满纸张,必须保留正的可用宽度和高度");const h=e.dataset===void 0?"detail":e.dataset;if(typeof h!="string"||!h.trim())throw new Error("dataset 必须是非空字符串");if(e.title!==void 0&&typeof e.title!="string")throw new Error("title 必须是字符串");const f=e.titleHeightMm===void 0?10:e.titleHeightMm,u=e.titleGapMm===void 0?4:e.titleGapMm;U(f,"titleHeightMm"),U(u,"titleGapMm",!0);const c=e.title!==void 0&&e.title.length>0,w=c?f+u:0;if(w>=m)throw new Error("标题高度与间距占满页面,必须为表格保留可用高度");if(e.listOptions!==void 0&&(!e.listOptions||typeof e.listOptions!="object"||Array.isArray(e.listOptions)))throw new Error("listOptions 必须是列表配置对象");const M=e.listOptions??{};ce(M.align,"listOptions.align",!0),ce(M.headerAlign,"listOptions.headerAlign",!1);for(const P of["fontSizePt","rowHeightMm","paddingMm"]){const v=M[P];v!==void 0&&U(v,`listOptions.${P}`,P==="paddingMm")}let E=0,S=0;for(const[P,v]of e.columns.entries()){if(!v||typeof v.header!="string"||typeof v.field!="string"||!v.field.trim())throw new Error(`columns[${P}] 需要字符串 header 与非空 field`);ce(v.align,`columns[${P}].align`,!0),ce(v.headerAlign,`columns[${P}].headerAlign`,!1),v.flex!==void 0&&U(v.flex,`columns[${P}].flex`),v.widthMm!==void 0?(U(v.widthMm,`columns[${P}].widthMm`),E+=v.widthMm):S=Math.max(S,v.flex??1)}if(t==="page"&&E>d)throw new Error(`固定列宽 ${E}mm 超出页面可用宽度 ${d}mm`);const x=d-E;if(t==="page"&&S>0&&x<=0)throw new Error("固定列占满页面可用宽度,自动列需要正的剩余宽度");const T=S>0?e.columns.reduce((P,v)=>P+(v.widthMm===void 0?(v.flex??1)/S:0),0):0,O=e.columns.map(({widthMm:P,flex:v,...p},y)=>{const B=P??(t==="wide"?n*(v??1):x*((v??1)/S)/T);return U(B,`columns[${y}] 分配后的 widthMm`),{...p,widthMm:B}});t==="wide"&&(d=O.reduce((P,v)=>P+v.widthMm,0),U(d,"宽表总列宽"),s=d+l.marginLeftMm+l.marginRightMm,U(s,"宽表总宽度"));const L={widthMm:s,heightMm:i,...l,components:[],...t==="wide"?{layout:t}:{}};return c&&L.components.push({kind:"text",text:e.title,leftMm:l.marginLeftMm,topMm:l.marginTopMm,widthMm:d,heightMm:f,style:{fontSize:16,bold:!0,align:"center",verticalAlign:"middle",...e.titleStyle}}),L.components.push({align:"auto",headerAlign:"center",...M,kind:"list",...t==="wide"?{layout:t}:{},...c?{title:e.title}:{},dataset:h,columns:O,leftMm:l.marginLeftMm,topMm:l.marginTopMm+w,widthMm:t==="wide"||S>0?d:E}),e.params===void 0?{pages:[L]}:{params:e.params,pages:[L]}}const Pe="1.0";class Z extends Error{constructor(t){super(`模板校验失败:${t.slice(0,3).map(n=>`${n.path}: ${n.message}`).join("; ")}`),this.errors=t,this.name="ReportTemplateError"}}const Et=new Set(["__proto__","prototype","constructor"]),Pt=new Set(["schemaVersion","id","name","revision","description","template"]),At=new Set(["left","center","right","justify"]),ye=2e5,xt=64,Ae=1e7;function H(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function xe(e,t){const n=new Set;let o=0,r=0,s=!1;const i=(a,l,d)=>{if(s)return;if(++o>ye){o===ye+1&&t.push({path:l,message:"模板超过 JSON 节点安全上限"});return}if(d>xt){t.push({path:l,message:"模板嵌套层级过深"});return}if(typeof a=="string"){r+=a.length,r>Ae&&(s=!0,t.push({path:l,message:"模板文本超过 1000 万字符的安全上限"}));return}if(a===null||typeof a=="boolean"||typeof a=="number"&&Number.isFinite(a))return;if(!a||typeof a!="object"){t.push({path:l,message:"模板只允许 JSON 值,不允许函数、undefined 或非有限数字"});return}if(n.has(a)){t.push({path:l,message:"模板不能包含循环引用"});return}const m=Object.getPrototypeOf(a);if(Array.isArray(a)&&m!==Array.prototype||!Array.isArray(a)&&m!==Object.prototype&&m!==null){t.push({path:l,message:"模板对象必须使用普通 JSON 对象原型"});return}if(Array.isArray(a)&&(Object.keys(a).length!==a.length||Object.keys(a).some(h=>!/^(0|[1-9]\d*)$/.test(h)||Number(h)>=a.length))){t.push({path:l,message:"模板数组不能包含空位或额外属性"});return}if(Object.getOwnPropertySymbols(a).length>0){t.push({path:l,message:"模板不能包含 Symbol 键"});return}n.add(a);for(const[h,f]of Object.entries(Object.getOwnPropertyDescriptors(a)))if(!(Array.isArray(a)&&h==="length")&&(Et.has(h)?t.push({path:`${l}.${h}`,message:"模板不能包含可能污染原型的键"}):f.get||f.set||!f.enumerable?t.push({path:`${l}.${h}`,message:"模板不能包含访问器或不可枚举字段"}):i(f.value,`${l}.${h}`,d+1),o>ye||s))break;n.delete(a)};i(e,"$",0)}function F(e,t,n,o,r=!1){const s=e[t];s===void 0&&!r||(typeof s!="string"||r&&!s.trim())&&o.push({path:`${n}.${t}`,message:r?"应为非空字符串":"应为字符串"})}function W(e,t,n,o,r=0,s=!1){const i=e[t];i===void 0&&!s||(typeof i!="number"||!Number.isFinite(i)||i<r)&&o.push({path:`${n}.${t}`,message:`应为大于等于 ${r} 的有限数字`})}function se(e,t,n,o){e[t]!==void 0&&typeof e[t]!="boolean"&&o.push({path:`${n}.${t}`,message:"应为布尔值"})}function te(e,t,n,o=!1){e!==void 0&&!At.has(e)&&!(o&&e==="auto")&&n.push({path:t,message:`未知对齐方式${o?"(支持 auto)":""}`})}function q(e,t,n){if(e!==void 0){if(!H(e)){n.push({path:t,message:"样式应为对象"});return}te(e.align,`${t}.align`,n),e.verticalAlign!==void 0&&!["top","middle","bottom"].includes(e.verticalAlign)&&n.push({path:`${t}.verticalAlign`,message:"未知垂直对齐方式"});for(const o of["fontSize","fontSizePx","lineWidthPt","lineHeight"])W(e,o,t,n);for(const o of["letterSpacing","rotateDeg"])W(e,o,t,n,-1/0);W(e,"opacity",t,n),typeof e.opacity=="number"&&e.opacity>1&&n.push({path:`${t}.opacity`,message:"透明度不能大于 1"});for(const o of["bold","italic","underline","borderTop","borderRight","borderBottom","borderLeft"])se(e,o,t,n);for(const o of["fontFamily","color","backgroundColor","lineColor","borderColor"])F(e,o,t,n)}}function kt(e,t,n){if(e===void 0)return;if(!Array.isArray(e)){n.push({path:t,message:"参数定义应为数组"});return}const o=new Set;e.forEach((r,s)=>{const i=`${t}.${s}`;if(!H(r)){n.push({path:i,message:"参数定义应为对象"});return}F(r,"field",i,n,!0),typeof r.field=="string"&&(o.has(r.field)&&n.push({path:`${i}.field`,message:"参数名重复"}),o.add(r.field)),r.type!==void 0&&!["text","number","date","select"].includes(r.type)&&n.push({path:`${i}.type`,message:"未知参数类型"});for(const a of["label","defaultValue","placeholder"])F(r,a,i,n);se(r,"required",i,n),r.options!==void 0&&(Array.isArray(r.options)?r.options.forEach((a,l)=>{const d=`${i}.options.${l}`;if(!H(a))n.push({path:d,message:"选项应为对象"});else for(const m of["label","value"])typeof a[m]!="string"&&n.push({path:`${d}.${m}`,message:"选项标签与值应为字符串(允许空白选项)"})}):n.push({path:`${i}.options`,message:"选项应为数组"}))})}function Nt(e,t,n){for(const o of["leftMm","topMm"])W(e,o,t,n,-1/0,!0);W(e,"widthMm",t,n,Number.MIN_VALUE,!0),F(e,"dataset",t,n,!0),F(e,"title",t,n),e.layout!==void 0&&e.layout!=="page"&&e.layout!=="wide"&&n.push({path:`${t}.layout`,message:"布局应为 page 或 wide"}),!Array.isArray(e.columns)||e.columns.length===0?n.push({path:`${t}.columns`,message:"列表至少需要一列"}):e.columns.forEach((o,r)=>{const s=`${t}.columns.${r}`;if(!H(o)){n.push({path:s,message:"列应为对象"});return}if(typeof o.header!="string"&&n.push({path:`${s}.header`,message:"表头应为字符串(允许空白表头)"}),F(o,"field",s,n,!0),W(o,"widthMm",s,n,Number.MIN_VALUE,!0),q(o.style,`${s}.style`,n),q(o.headerStyle,`${s}.headerStyle`,n),te(o.align,`${s}.align`,n,!0),te(o.headerAlign,`${s}.headerAlign`,n),o.format!==void 0)if(!H(o.format)||!["number","percent","date"].includes(o.format.kind))n.push({path:`${s}.format`,message:"未知列格式"});else{const i=o.format.digits;i!==void 0&&(typeof i!="number"||!Number.isInteger(i)||i<0||i>100)&&n.push({path:`${s}.format.digits`,message:"小数位应为 0–100 的整数"}),se(o.format,"thousands",`${s}.format`,n),F(o.format,"pattern",`${s}.format`,n)}o.rules!==void 0&&(Array.isArray(o.rules)?o.rules.forEach((i,a)=>{const l=`${s}.rules.${a}`;if(!H(i)||!H(i.when)){n.push({path:l,message:"条件格式缺少 when 对象"});return}F(i.when,"field",`${l}.when`,n,!0),[">",">=","<","<=","==","!="].includes(i.when.op)||n.push({path:`${l}.when.op`,message:"未知条件运算符"}),typeof i.when.value!="number"&&typeof i.when.value!="string"&&n.push({path:`${l}.when.value`,message:"条件值应为数字或字符串"}),q(i.style,`${l}.style`,n)}):n.push({path:`${s}.rules`,message:"条件格式应为数组"}))});for(const o of["fontSizePt","rowHeightMm"])W(e,o,t,n,Number.MIN_VALUE);W(e,"paddingMm",t,n);for(const o of["headerBold","headerEveryPage","border"])se(e,o,t,n);if(F(e,"headerBackgroundColor",t,n),te(e.align,`${t}.align`,n,!0),te(e.headerAlign,`${t}.headerAlign`,n),q(e.headerStyle,`${t}.headerStyle`,n),e.groupBy!==void 0)if(!H(e.groupBy))n.push({path:`${t}.groupBy`,message:"分组应为对象"});else{F(e.groupBy,"field",`${t}.groupBy`,n,!0);for(const o of["headerTemplate","subtotalLabel"])F(e.groupBy,o,`${t}.groupBy`,n);for(const o of["headerStyle","subtotalStyle"])q(e.groupBy[o],`${t}.groupBy.${o}`,n);se(e.groupBy,"keepWithNext",`${t}.groupBy`,n),e.groupBy.subtotal!==void 0&&(!Array.isArray(e.groupBy.subtotal)||e.groupBy.subtotal.some(o=>typeof o!="string"))&&n.push({path:`${t}.groupBy.subtotal`,message:"小计字段应为字符串数组"})}e.grandTotal!==void 0&&typeof e.grandTotal!="boolean"&&(H(e.grandTotal)?(F(e.grandTotal,"label",`${t}.grandTotal`,n),q(e.grandTotal.style,`${t}.grandTotal.style`,n),e.grandTotal.fields!==void 0&&(!Array.isArray(e.grandTotal.fields)||e.grandTotal.fields.some(o=>typeof o!="string"))&&n.push({path:`${t}.grandTotal.fields`,message:"合计字段应为字符串数组"})):n.push({path:`${t}.grandTotal`,message:"合计应为布尔值或对象"}))}function Ze(e,t,n){q(e.style,`${t}.style`,n);for(const r of["text","rawText","imageData","fallbackText","nid","rkey"])F(e,r,t,n);if(e.lines!==void 0&&(!Array.isArray(e.lines)||e.lines.some(r=>typeof r!="string"))&&n.push({path:`${t}.lines`,message:"文本行应为字符串数组"}),Array.isArray(e.richParagraphs)&&e.richParagraphs.forEach((r,s)=>{if(!H(r))return;const i=`${t}.richParagraphs.${s}`;te(r.align,`${i}.align`,n);for(const a of["lineHeight","spaceBeforeMm","spaceAfterMm","indentMm"])W(r,a,i,n);Array.isArray(r.segments)&&r.segments.forEach((a,l)=>{const d=`${i}.segments.${l}`;if(!H(a)){n.push({path:d,message:"富文本片段应为对象"});return}a.kind!==void 0&&a.kind!=="text"&&a.kind!=="field"&&n.push({path:`${d}.kind`,message:"未知富文本片段类型"});for(const m of["text","field"])F(a,m,d,n);q(a.style,`${d}.style`,n)})}),!H(e.grid))return;const o=e.grid;W(o,"gapMm",`${t}.grid`,n);for(const r of["colWidthsMm","rowHeightsMm"]){const s=o[r];s!==void 0&&(!Array.isArray(s)||s.some(i=>typeof i!="number"||!Number.isFinite(i)||i<0))&&n.push({path:`${t}.grid.${r}`,message:"网格尺寸应为有限非负数字数组"})}Array.isArray(o.cells)&&o.cells.forEach((r,s)=>{Array.isArray(r)&&r.forEach((i,a)=>{const l=`${t}.grid.cells.${s}.${a}`;if(i!==null){if(!H(i)){n.push({path:l,message:"网格单元格应为对象或 null"});return}for(const d of["text","rawText","numberFormat"])F(i,d,l,n);for(const d of["colSpan","rowSpan"]){const m=i[d];m!==void 0&&(typeof m!="number"||!Number.isInteger(m)||m<=0)&&n.push({path:`${l}.${d}`,message:"跨度应为正整数"})}i.value!==void 0&&i.value!==null&&!["string","number","boolean"].includes(typeof i.value)&&n.push({path:`${l}.value`,message:"Excel 原始值应为字符串、数字、布尔值或 null"}),q(i.style,`${l}.style`,n),i.children!==void 0&&(Array.isArray(i.children)?i.children.forEach((d,m)=>{H(d)&&Ze(d,`${l}.children.${m}`,n)}):n.push({path:`${l}.children`,message:"嵌套组件应为数组"}))}})})}function ke(e){const t=[],n=[];return xe(e,t),t.length?{ok:!1,errors:t,warnings:n}:(H(e)?(e.schemaVersion!==void 0&&e.schemaVersion!==C.RENDER_PLAN_SCHEMA_VERSION&&t.push({path:"$.schemaVersion",message:"不支持的模板 schemaVersion"}),kt(e.params,"$.params",t),!Array.isArray(e.pages)||e.pages.length===0?t.push({path:"$.pages",message:"模板至少需要一页"}):e.pages.forEach((o,r)=>{const s=`$.pages.${r}`;if(!H(o)){t.push({path:s,message:"页应为对象"});return}W(o,"widthMm",s,t,Number.MIN_VALUE,!0),W(o,"heightMm",s,t,Number.MIN_VALUE,!0),o.layout!==void 0&&o.layout!=="page"&&o.layout!=="wide"&&t.push({path:`${s}.layout`,message:"布局应为 page 或 wide"}),typeof o.heightMm=="number"&&o.heightMm>=5e3&&t.push({path:`${s}.heightMm`,message:"页高应小于 5000mm"});for(const i of["marginTopMm","marginBottomMm","marginLeftMm","marginRightMm"])W(o,i,s,t);typeof o.widthMm=="number"&&Number(o.marginLeftMm??0)+Number(o.marginRightMm??0)>=o.widthMm&&t.push({path:s,message:"横向页边距占满页面"}),typeof o.heightMm=="number"&&Number(o.marginTopMm??0)+Number(o.marginBottomMm??0)>=o.heightMm&&t.push({path:s,message:"纵向页边距占满页面"}),Array.isArray(o.components)?o.components.forEach((i,a)=>{const l=`${s}.components.${a}`;H(i)&&i.kind==="list"?Nt(i,l,t):(ge(i,l,t,n),H(i)&&Ze(i,l,t))}):t.push({path:`${s}.components`,message:"组件应为数组"})})):t.push({path:"$",message:"模板应为对象"}),{ok:t.length===0,errors:t,warnings:n})}function Qe(e){const t=[],n=[];if(xe(e,t),t.length)return{ok:!1,errors:t,warnings:n};if(!H(e))t.push({path:"$",message:"模板文档应为对象"});else{for(const r of Object.keys(e))Pt.has(r)||t.push({path:`$.${r}`,message:"模板文档存在未知字段;只保存模板与描述,不保存取数或业务数据"});e.schemaVersion!==Pe&&t.push({path:"$.schemaVersion",message:"不支持的模板文档版本"});for(const r of["id","name"])F(e,r,"$",t,!0);for(const r of["revision","description"])F(e,r,"$",t);const o=ke(e.template);t.push(...o.errors.map(r=>({...r,path:`$.template${r.path.slice(1)}`}))),n.push(...o.warnings.map(r=>({...r,path:`$.template${r.path.slice(1)}`})))}return{ok:t.length===0,errors:t,warnings:n}}function ue(e){const t=Qe(e);if(!t.ok)throw new Z(t.errors)}function Rt(e){const t=[];if(xe(e,t),t.length)throw new Z(t);const n={...e,schemaVersion:e.schemaVersion??Pe};return ue(n),fe(n)}function Ct(e){let t=e;if(typeof e=="string"){if(e.length>Ae)throw new Z([{path:"$",message:"模板 JSON 超过 1000 万字符的导出/导入上限"}]);try{t=JSON.parse(e)}catch{throw new Z([{path:"$",message:"模板 JSON 语法无效"}])}}return ue(t),fe(t)}function _t(e){ue(e);const t=JSON.stringify(e,null,2);if(t.length>Ae)throw new Z([{path:"$",message:"模板 JSON 超过 1000 万字符的导出/导入上限"}]);return t}function fe(e){return JSON.parse(JSON.stringify(e))}function Ot(e=[]){const t=new Map,n={get:o=>{const r=t.get(o);return r?fe(r):void 0},list:()=>Array.from(t.values(),({template:o,...r})=>({...r})),register:(o,r={})=>{if(ue(o),t.has(o.id)&&!r.replace)throw new Z([{path:"$.id",message:`模板 ID ${o.id} 已注册;更新需显式 replace:true`}]);t.set(o.id,fe(o))},remove:o=>t.delete(o)};for(const o of e)n.register(o);return n}function Ht(e){if(!("mapData"in e))return e;const{load:t,mapData:n,...o}=e;return{...o,load:async r=>{const s=await t(r);K(r.signal);try{return n(s,r)}catch(i){throw new j("validate",`报表 ${r.reportId} 数据映射失败`,i)}}}}class j extends Error{constructor(t,n,o){super(n),this.name="ReportSourceError",this.code=t,this.cause=o}}function he(e){const t=new Error("报表加载已取消");return t.name="AbortError",Object.defineProperty(t,"cause",{value:e.reason}),t}function K(e){if(e?.aborted)throw he(e)}function Be(e,t){return t?new Promise((n,o)=>{const r=()=>t.removeEventListener("abort",s),s=()=>{r(),o(he(t))};t.addEventListener("abort",s,{once:!0}),t.aborted&&s(),Promise.resolve(e).then(i=>{r(),n(i)},i=>{r(),o(i)})}):Promise.resolve(e)}function Bt(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new j("validate",`报表 ${t} 的数据集应为对象`);for(const[n,o]of Object.entries(e))if(!Array.isArray(o)||o.some(r=>!r||typeof r!="object"||Array.isArray(r)))throw new j("validate",`报表 ${t} 的数据集 ${n} 应为对象数组`)}function Ft(e,t={}){function n(r){const s=Object.prototype.hasOwnProperty.call(e,r)?e[r]:void 0;if(!s)throw new j("config",`未注册报表 ${r}`);return s}return Object.assign(async({reportIds:r,params:s,signal:i,onWarning:a})=>{K(i);const l=[...r],d={...s};if(l.length===0)throw new j("config","缺少报表 ID");const m=l.map(n),h=[],f=[];for(const[M,E]of m.entries()){K(i);const S=l[M],x={reportId:S,params:{...d},signal:i};let T;if(typeof E.template=="function")try{T=await Be(E.template(x),i),K(i);const p=ke(T);if(!p.ok)throw new j("validate",`报表 ${S} 模板校验失败:${p.errors.slice(0,3).map(y=>`${y.path}: ${y.message}`).join("; ")}`);f.push(...p.warnings.map(y=>`${S}: ${y.path}: ${y.message}`))}catch(p){throw i?.aborted?he(i):p instanceof j?p:p instanceof Z?new j("validate",`报表 ${S} 模板校验失败:${p.message}`,p):new j("fetch",`报表 ${S} 模板加载失败:${p instanceof Error?p.message:String(p)}`,p)}else T=E.template;let O;try{O=await Be(E.load?E.load(x):E.datasets??{},i)}catch(p){throw i?.aborted?he(i):p instanceof j?p:new j("fetch",`报表 ${S} 取数失败:${p instanceof Error?p.message:String(p)}`,p)}K(i),Bt(O,S);let L;try{L=We(T,O,E.pagination)}catch(p){throw new j("validate",`报表 ${S} 排版失败:${p instanceof Error?p.message:String(p)}`,p)}const P=new Map,v=p=>{let y=P.get(p);return y===void 0&&(y=JSON.stringify([M,S,p]),P.set(p,y)),y};for(const p of L.plan.pages)h.push({...p,components:p.components.map(y=>y.table?{...y,table:{...y.table,id:v(y.table.id)}}:y)});f.push(...L.warnings.map(p=>`${S}: ${p}`))}K(i);const u={schemaVersion:C.RENDER_PLAN_SCHEMA_VERSION,pages:h},c=$e(u);if(!c.ok)throw new j("validate",`渲染计划校验失败:${c.errors.slice(0,3).map(M=>`${M.path}: ${M.message}`).join("; ")}`);f.push(...c.warnings.map(M=>`${M.path}: ${M.message}`));const w=a??t.onWarning??(M=>console.warn("[mach-report]",M));for(const M of f)K(i),w(M);return K(i),u},{getParamDefs:r=>rt.getReportParamDefs(e,r)})}exports.CSS_DPI=g.CSS_DPI;exports.DEFAULT_BORDER_PT=g.DEFAULT_BORDER_PT;exports.DEFAULT_FONT_PT=g.DEFAULT_FONT_PT;exports.DEFAULT_STROKE_COLOR=g.DEFAULT_STROKE_COLOR;exports.DEFAULT_TEXT_COLOR=g.DEFAULT_TEXT_COLOR;exports.LINE_HEIGHT=g.LINE_HEIGHT;exports.MM_PER_INCH=g.MM_PER_INCH;exports.PAPER_SIZES=g.PAPER_SIZES;exports.SCHEMA_VERSION_MACH=g.SCHEMA_VERSION_MACH;exports.mmToPx=g.mmToPx;exports.mmToPxFactor=g.mmToPxFactor;exports.ptToPx=g.ptToPx;exports.pxToMm=g.pxToMm;exports.resolveBoxBorders=g.resolveBoxBorders;exports.resolveFontSize=g.resolveFontSize;exports.resolveGridLayout=g.resolveGridLayout;exports.resolveLineHeight=g.resolveLineHeight;exports.resolveStroke=g.resolveStroke;exports.resolveTextColor=g.resolveTextColor;exports.round=g.round;exports.RENDER_PLAN_SCHEMA_VERSION=C.RENDER_PLAN_SCHEMA_VERSION;exports.charWidthEm=C.charWidthEm;exports.createCanvasMeasurer=C.createCanvasMeasurer;exports.createHeuristicMeasurer=C.createHeuristicMeasurer;exports.heuristicMeasurer=C.heuristicMeasurer;exports.isImageBackedComponent=C.isImageBackedComponent;exports.isRichText=C.isRichText;exports.measureTextMm=C.measureTextMm;exports.wrapText=C.wrapText;exports.PageBuilder=Xe;exports.REPORT_TEMPLATE_DOCUMENT_VERSION=Pe;exports.ReportSourceError=j;exports.ReportTemplateError=Z;exports.TemplateBuilder=Ke;exports.assertRenderPlan=dt;exports.classOf=D;exports.computePageWindow=Ge;exports.createCanvasPager=St;exports.createReportSource=Ft;exports.createTableTemplate=vt;exports.createTemplate=Tt;exports.createTemplateRegistry=Ot;exports.defineReport=Ht;exports.defineReportTemplate=Rt;exports.evalStyleRule=Le;exports.formatValue=we;exports.measurePageSizePx=ve;exports.paginateTemplate=We;exports.parseDateLocal=Fe;exports.parseReportTemplate=Ct;exports.planFontSizePx=bt;exports.renderComponent=Se;exports.renderPage=Ue;exports.renderPlan=yt;exports.renderPlanToCanvas=qe;exports.renderPlanToImages=$t;exports.resolvePaper=Ee;exports.serializeReportTemplate=_t;exports.toExcelNumberFormat=be;exports.toNumber=me;exports.validateComponent=ge;exports.validatePage=Ie;exports.validateRenderPlan=$e;exports.validateReportTemplate=ke;exports.validateReportTemplateDocument=Qe;
package/dist/index.d.cts CHANGED
@@ -133,8 +133,8 @@ export declare function createReportSource(reports: ReportRegistry, options?: {
133
133
  }): ReportSource;
134
134
 
135
135
  /**
136
- * 创建一页标准明细模板;分页与格式化继续由 paginateTemplate 处理。
137
- * 固定列保持毫米宽度,自动列共享余量;全部固定列时表格宽度为列宽之和。
136
+ * 创建明细模板;分页与格式化继续由 paginateTemplate 处理。
137
+ * page 模式自动列共享纸张余量;wide 模式按自然列宽扩展,不缩小字号或压缩列。
138
138
  */
139
139
  export declare function createTableTemplate(options: TableTemplateOptions): ReportTemplate;
140
140
 
@@ -276,6 +276,10 @@ export declare interface ListColumn {
276
276
  export declare interface ListComponent {
277
277
  kind: "list";
278
278
  id?: string;
279
+ /** 宽表标记用于连续 Excel;不改变低层列表的列宽和分页算法。 */
280
+ layout?: "page" | "wide";
281
+ /** 逻辑表名称,供 Excel Sheet 命名;不额外生成标题组件。 */
282
+ title?: string;
279
283
  leftMm: number;
280
284
  topMm: number;
281
285
  widthMm: number;
@@ -416,6 +420,7 @@ export declare interface PlanComponent {
416
420
  richParagraphs?: RichParagraph[];
417
421
  imageData?: string;
418
422
  grid?: PlanGrid;
423
+ table?: PlanTableFragment;
419
424
  style?: ComponentStyle;
420
425
  meta?: Record<string, unknown>;
421
426
  /** MachReport 扩展字段:未知 kind 的兜底渲染标记 */
@@ -437,6 +442,8 @@ export declare interface PlanGrid {
437
442
  }
438
443
 
439
444
  export declare interface PlanPage {
445
+ /** wide 表示完整自然列宽;页面仅为预览的垂直窗口分段。 */
446
+ layout?: "page" | "wide";
440
447
  pageWidthMm: number;
441
448
  pageHeightMm: number;
442
449
  marginTopMm?: number;
@@ -446,6 +453,19 @@ export declare interface PlanPage {
446
453
  components: PlanComponent[];
447
454
  }
448
455
 
456
+ /** 列表的逻辑行来源;用于连续 Excel 重组,避免按文本猜测重复表头。 */
457
+ export declare interface PlanTableFragment {
458
+ /** 在整个计划中唯一的逻辑表身份;同一表的分页片段共享身份。 */
459
+ id: string;
460
+ /** 表头固定 -1;其余行(包括组头/小计/总计)从 0 连续递增。 */
461
+ rowIndex: number;
462
+ role: "header" | "body" | "group-header" | "subtotal" | "grand-total";
463
+ /** 按物理列顺序保存字段身份;不以表头显示文本判断列结构。 */
464
+ columnKeys: string[];
465
+ layout?: "page" | "wide";
466
+ title?: string;
467
+ }
468
+
449
469
  export declare function ptToPx(pt: number, dpi?: number): number;
450
470
 
451
471
  export declare function pxToMm(value: number, dpi?: number): number;
@@ -675,16 +695,20 @@ export declare interface StyleRule {
675
695
  style: Record<string, unknown>;
676
696
  }
677
697
 
678
- /** 标准明细列:显式 widthMm 优先;其余列按 flex 分配剩余可用宽度。 */
698
+ /** 明细列:显式 widthMm 优先;纸张模式按 flex 分余量,宽表模式按 flex 放大基准列宽。 */
679
699
  export declare type TableTemplateColumn = Omit<ListColumn, "widthMm"> & {
680
700
  widthMm?: number;
681
701
  /** 自动列的宽度权重,默认 1(有 widthMm 时不参与分配)。 */
682
702
  flex?: number;
683
703
  };
684
704
 
685
- export declare type TableTemplateListOptions = Partial<Omit<ListComponent, "kind" | "leftMm" | "topMm" | "widthMm" | "dataset" | "columns">>;
705
+ export declare type TableTemplateListOptions = Partial<Omit<ListComponent, "kind" | "leftMm" | "topMm" | "widthMm" | "dataset" | "columns" | "layout" | "title">>;
686
706
 
687
707
  export declare interface TableTemplateOptions extends PageOptions {
708
+ /** page 保持旧版纸张布局;wide 按所有列的自然宽度扩展,纵向分段仅供预览与渲染。 */
709
+ layout?: "page" | "wide";
710
+ /** 宽表模式自动列的基准宽度,默认 30mm;widthMm 优先,flex 为基准宽度的倍数。 */
711
+ columnWidthMm?: number;
688
712
  title?: string;
689
713
  /** 标题占高,默认 10mm;没有标题时不占空间。 */
690
714
  titleHeightMm?: number;
@@ -693,7 +717,7 @@ export declare interface TableTemplateOptions extends PageOptions {
693
717
  titleStyle?: ComponentStyle;
694
718
  dataset?: string;
695
719
  columns: readonly TableTemplateColumn[];
696
- /** 默认 A4;页边距各默认 10mm。 */
720
+ /** 纸张模式默认 A4;与 layout: "wide" 互斥;页边距各默认 10mm。 */
697
721
  paper?: PaperName | {
698
722
  widthMm: number;
699
723
  heightMm: number;
@@ -741,6 +765,8 @@ export declare type TemplateComponent = PlanComponent | ListComponent;
741
765
  export declare interface TemplatePage {
742
766
  widthMm: number;
743
767
  heightMm: number;
768
+ /** 宽表保持全列自然宽度,页高只控制内部纵向分段。 */
769
+ layout?: "page" | "wide";
744
770
  marginTopMm?: number;
745
771
  marginBottomMm?: number;
746
772
  marginLeftMm?: number;
package/dist/index.d.ts CHANGED
@@ -133,8 +133,8 @@ export declare function createReportSource(reports: ReportRegistry, options?: {
133
133
  }): ReportSource;
134
134
 
135
135
  /**
136
- * 创建一页标准明细模板;分页与格式化继续由 paginateTemplate 处理。
137
- * 固定列保持毫米宽度,自动列共享余量;全部固定列时表格宽度为列宽之和。
136
+ * 创建明细模板;分页与格式化继续由 paginateTemplate 处理。
137
+ * page 模式自动列共享纸张余量;wide 模式按自然列宽扩展,不缩小字号或压缩列。
138
138
  */
139
139
  export declare function createTableTemplate(options: TableTemplateOptions): ReportTemplate;
140
140
 
@@ -276,6 +276,10 @@ export declare interface ListColumn {
276
276
  export declare interface ListComponent {
277
277
  kind: "list";
278
278
  id?: string;
279
+ /** 宽表标记用于连续 Excel;不改变低层列表的列宽和分页算法。 */
280
+ layout?: "page" | "wide";
281
+ /** 逻辑表名称,供 Excel Sheet 命名;不额外生成标题组件。 */
282
+ title?: string;
279
283
  leftMm: number;
280
284
  topMm: number;
281
285
  widthMm: number;
@@ -416,6 +420,7 @@ export declare interface PlanComponent {
416
420
  richParagraphs?: RichParagraph[];
417
421
  imageData?: string;
418
422
  grid?: PlanGrid;
423
+ table?: PlanTableFragment;
419
424
  style?: ComponentStyle;
420
425
  meta?: Record<string, unknown>;
421
426
  /** MachReport 扩展字段:未知 kind 的兜底渲染标记 */
@@ -437,6 +442,8 @@ export declare interface PlanGrid {
437
442
  }
438
443
 
439
444
  export declare interface PlanPage {
445
+ /** wide 表示完整自然列宽;页面仅为预览的垂直窗口分段。 */
446
+ layout?: "page" | "wide";
440
447
  pageWidthMm: number;
441
448
  pageHeightMm: number;
442
449
  marginTopMm?: number;
@@ -446,6 +453,19 @@ export declare interface PlanPage {
446
453
  components: PlanComponent[];
447
454
  }
448
455
 
456
+ /** 列表的逻辑行来源;用于连续 Excel 重组,避免按文本猜测重复表头。 */
457
+ export declare interface PlanTableFragment {
458
+ /** 在整个计划中唯一的逻辑表身份;同一表的分页片段共享身份。 */
459
+ id: string;
460
+ /** 表头固定 -1;其余行(包括组头/小计/总计)从 0 连续递增。 */
461
+ rowIndex: number;
462
+ role: "header" | "body" | "group-header" | "subtotal" | "grand-total";
463
+ /** 按物理列顺序保存字段身份;不以表头显示文本判断列结构。 */
464
+ columnKeys: string[];
465
+ layout?: "page" | "wide";
466
+ title?: string;
467
+ }
468
+
449
469
  export declare function ptToPx(pt: number, dpi?: number): number;
450
470
 
451
471
  export declare function pxToMm(value: number, dpi?: number): number;
@@ -675,16 +695,20 @@ export declare interface StyleRule {
675
695
  style: Record<string, unknown>;
676
696
  }
677
697
 
678
- /** 标准明细列:显式 widthMm 优先;其余列按 flex 分配剩余可用宽度。 */
698
+ /** 明细列:显式 widthMm 优先;纸张模式按 flex 分余量,宽表模式按 flex 放大基准列宽。 */
679
699
  export declare type TableTemplateColumn = Omit<ListColumn, "widthMm"> & {
680
700
  widthMm?: number;
681
701
  /** 自动列的宽度权重,默认 1(有 widthMm 时不参与分配)。 */
682
702
  flex?: number;
683
703
  };
684
704
 
685
- export declare type TableTemplateListOptions = Partial<Omit<ListComponent, "kind" | "leftMm" | "topMm" | "widthMm" | "dataset" | "columns">>;
705
+ export declare type TableTemplateListOptions = Partial<Omit<ListComponent, "kind" | "leftMm" | "topMm" | "widthMm" | "dataset" | "columns" | "layout" | "title">>;
686
706
 
687
707
  export declare interface TableTemplateOptions extends PageOptions {
708
+ /** page 保持旧版纸张布局;wide 按所有列的自然宽度扩展,纵向分段仅供预览与渲染。 */
709
+ layout?: "page" | "wide";
710
+ /** 宽表模式自动列的基准宽度,默认 30mm;widthMm 优先,flex 为基准宽度的倍数。 */
711
+ columnWidthMm?: number;
688
712
  title?: string;
689
713
  /** 标题占高,默认 10mm;没有标题时不占空间。 */
690
714
  titleHeightMm?: number;
@@ -693,7 +717,7 @@ export declare interface TableTemplateOptions extends PageOptions {
693
717
  titleStyle?: ComponentStyle;
694
718
  dataset?: string;
695
719
  columns: readonly TableTemplateColumn[];
696
- /** 默认 A4;页边距各默认 10mm。 */
720
+ /** 纸张模式默认 A4;与 layout: "wide" 互斥;页边距各默认 10mm。 */
697
721
  paper?: PaperName | {
698
722
  widthMm: number;
699
723
  heightMm: number;
@@ -741,6 +765,8 @@ export declare type TemplateComponent = PlanComponent | ListComponent;
741
765
  export declare interface TemplatePage {
742
766
  widthMm: number;
743
767
  heightMm: number;
768
+ /** 宽表保持全列自然宽度,页高只控制内部纵向分段。 */
769
+ layout?: "page" | "wide";
744
770
  marginTopMm?: number;
745
771
  marginBottomMm?: number;
746
772
  marginLeftMm?: number;