Просмотр исходного кода

chore: 初始化项目基线(AI快速报价页面、本地预览服务、柜体素材与Git忽略配置)

缪子怡 2 недель назад
Сommit
d110f60280
100 измененных файлов с 29156 добавлено и 0 удалено
  1. 13 0
      .gitignore
  2. 1744 0
      AI快速报价_Demo2.optimizing.html
  3. 432 0
      _check.js
  4. 50 0
      _check_console.js
  5. 23 0
      _check_syntax.js
  6. 6 0
      _chk.js
  7. 16 0
      _console_result.json
  8. 11956 0
      _inline_script.js
  9. 14450 0
      _old_recovered.html
  10. BIN
      _ref_quote.zip
  11. 2 0
      _ref_quote/[Content_Types].xml
  12. 2 0
      _ref_quote/_rels/.rels
  13. 2 0
      _ref_quote/docProps/app.xml
  14. 2 0
      _ref_quote/docProps/core.xml
  15. 2 0
      _ref_quote/docProps/custom.xml
  16. 1 0
      _ref_quote/xl/_rels/cellimages.xml.rels
  17. 2 0
      _ref_quote/xl/_rels/workbook.xml.rels
  18. 1 0
      _ref_quote/xl/cellimages.xml
  19. BIN
      _ref_quote/xl/media/image1.png
  20. BIN
      _ref_quote/xl/media/image10.png
  21. BIN
      _ref_quote/xl/media/image11.png
  22. BIN
      _ref_quote/xl/media/image12.png
  23. BIN
      _ref_quote/xl/media/image13.png
  24. BIN
      _ref_quote/xl/media/image14.png
  25. BIN
      _ref_quote/xl/media/image15.png
  26. BIN
      _ref_quote/xl/media/image16.png
  27. BIN
      _ref_quote/xl/media/image17.png
  28. BIN
      _ref_quote/xl/media/image18.png
  29. BIN
      _ref_quote/xl/media/image2.png
  30. BIN
      _ref_quote/xl/media/image3.png
  31. BIN
      _ref_quote/xl/media/image4.png
  32. BIN
      _ref_quote/xl/media/image5.png
  33. BIN
      _ref_quote/xl/media/image6.png
  34. BIN
      _ref_quote/xl/media/image7.png
  35. BIN
      _ref_quote/xl/media/image8.png
  36. BIN
      _ref_quote/xl/media/image9.png
  37. 1 0
      _ref_quote/xl/sharedStrings.xml
  38. 1 0
      _ref_quote/xl/styles.xml
  39. 1 0
      _ref_quote/xl/theme/theme1.xml
  40. 2 0
      _ref_quote/xl/workbook.xml
  41. 1 0
      _ref_quote/xl/worksheets/sheet1.xml
  42. 111 0
      _refdump.txt
  43. 106 0
      _server.py
  44. 35 0
      _xchk.py
  45. 15 0
      _xdump.py
  46. 16 0
      _xdump2.py
  47. BIN
      _xz1.zip
  48. 2 0
      _xz1/[Content_Types].xml
  49. 2 0
      _xz1/_rels/.rels
  50. 2 0
      _xz1/docProps/app.xml
  51. 2 0
      _xz1/docProps/core.xml
  52. 2 0
      _xz1/docProps/custom.xml
  53. 1 0
      _xz1/xl/_rels/cellimages.xml.rels
  54. 2 0
      _xz1/xl/_rels/workbook.xml.rels
  55. 1 0
      _xz1/xl/cellimages.xml
  56. BIN
      _xz1/xl/media/image1.png
  57. BIN
      _xz1/xl/media/image10.png
  58. BIN
      _xz1/xl/media/image11.png
  59. BIN
      _xz1/xl/media/image12.png
  60. BIN
      _xz1/xl/media/image13.png
  61. BIN
      _xz1/xl/media/image14.png
  62. BIN
      _xz1/xl/media/image15.png
  63. BIN
      _xz1/xl/media/image16.png
  64. BIN
      _xz1/xl/media/image17.png
  65. BIN
      _xz1/xl/media/image18.png
  66. BIN
      _xz1/xl/media/image2.png
  67. BIN
      _xz1/xl/media/image3.png
  68. BIN
      _xz1/xl/media/image4.png
  69. BIN
      _xz1/xl/media/image5.png
  70. BIN
      _xz1/xl/media/image6.png
  71. BIN
      _xz1/xl/media/image7.png
  72. BIN
      _xz1/xl/media/image8.png
  73. BIN
      _xz1/xl/media/image9.png
  74. 1 0
      _xz1/xl/sharedStrings.xml
  75. 1 0
      _xz1/xl/styles.xml
  76. 1 0
      _xz1/xl/theme/theme1.xml
  77. 2 0
      _xz1/xl/workbook.xml
  78. 1 0
      _xz1/xl/worksheets/sheet1.xml
  79. 33 0
      _xzparse.js
  80. 110 0
      _xzrows.txt
  81. BIN
      annotation.png
  82. BIN
      assets/furniture/balcony-cabinet-source.png
  83. BIN
      assets/furniture/balcony-cabinet.png
  84. BIN
      assets/furniture/contact-sheet.png
  85. BIN
      assets/furniture/dining-sideboard-source.png
  86. BIN
      assets/furniture/dining-sideboard.png
  87. BIN
      assets/furniture/entry-cabinet-source.png
  88. BIN
      assets/furniture/entry-cabinet.png
  89. BIN
      assets/furniture/kitchen-cabinet-source.png
  90. BIN
      assets/furniture/kitchen-cabinet.png
  91. BIN
      assets/furniture/laundry-cabinet-source.png
  92. BIN
      assets/furniture/laundry-cabinet.png
  93. BIN
      assets/furniture/living-display-cabinet-source.png
  94. BIN
      assets/furniture/living-display-cabinet.png
  95. BIN
      assets/furniture/living-tv-cabinet-source.png
  96. BIN
      assets/furniture/living-tv-cabinet.png
  97. BIN
      assets/furniture/master-wardrobe-source.png
  98. BIN
      assets/furniture/master-wardrobe.png
  99. BIN
      assets/furniture/secondary-wardrobe-source.png
  100. BIN
      assets/furniture/secondary-wardrobe.png

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+# 浏览器自动化/调试产生的用户数据目录(非项目文件,体积大,禁止入库)
+.edge-*/
+
+# 本地缓存与扫描产物
+.npm-cache/
+_recovery_scan/
+
+# Python 缓存
+__pycache__/
+*.pyc
+
+# Node
+node_modules/

Разница между файлами не показана из-за своего большого размера
+ 1744 - 0
AI快速报价_Demo2.optimizing.html


+ 432 - 0
_check.js

@@ -0,0 +1,432 @@
+
+"use strict";
+/* ========== 规则数据(完整规则手册) ========== */
+const MATS=[{n:"双饰面",min:3000,max:3500},{n:"PET",min:4500,max:5000},{n:"混油",min:6000,max:7000}];
+const WARD=[["板门补差价",538,"元/㎡"],["玻璃门补差价",1852,"元/㎡"],["灯带",386,"元/㎡或元/m"],["变压器+开关",536,"元/套"],["自弹器",115,"元/个"],["见光板",1138,"元/㎡"],["铰链",40,"元/个"],["护墙板",1852,"元/㎡"],["拉手",226,"元/个"],["抽屉",1300,"元/个"],["金属拉手(普通)",225,"元/m"],["金属拉手(铝格栅)",680,"元/个"],["衣杆",288,"元/m"],["圆弧",1560,"元/m"]];
+const DOORS=[["开放","开放柜","无门",1748,2380],["开放","装饰柜","无门",868,null],["双饰面板","双饰面-平板门","18MM",1748,2380],["双饰面板","双饰面-平板门","25MM",1918,2550],["双饰面板","双饰面+轻奢卡条","18MM",2216,2848],["双饰面板","双饰面+轻奢卡条","25MM",2386,3018],["水晶板门","水晶板门","18MM",2120,2960],["水晶板门","水晶板门","25MM",2290,3130],["水晶板门","水晶板门+轻奢卡条","18MM",2590,3430],["水晶板门","水晶板门+轻奢卡条","25MM",2758,3598],["铝框门","KM2611门板","生态板/PET",2488,3360],["铝框门","铝框玻璃门(有缝)","灰玻/茶玻/黑玻",2488,3248]];
+const KIT_FIX=[["fridge","冰箱吊柜",2622,"元/m"],["tall","高柜",5950,"元/m"],["counter","台面(石英石)",1480,"元/m"],["over","台面超出部分",2480,"元/m"],["side","见光板",1138,"元/㎡"]];
+const KIT_ACC=[["碗篮",1268],["锅篮",1628],["自弹器",115],["切角",500],["台下盆",500],["包管",500]];
+const RULES=[["目测估算像素/毫米比例","用图纸标注(如有)或厨房台面深度600mm(自动模式)"],["只看到红框部分","完整测量整个红框边界"],["厨房尺寸目测估算","用量尺/标准图/标注/CAD数据"],["混淆W/H方向","取 max(W,H) 为沿墙长度"],["厨房按面积整体计价","分项按延米/按个计价"],["有红框时跳过AI识别","AI始终先执行"],["平面图上假装能识别护墙板","平面图只能识别柜体和厨房,护墙板需手动指定或CAD图"],["护墙板用不同公式","与柜体相同:最长边×2.5=面积"],["报价单不区分基础项和增值项","必须分为两部分"],["橱柜地柜/吊柜固定死单价","支持12种门板配置下拉选择"],["使用门洞/床等不可靠隐式参照物","只保留「厨房台面深度600mm」"],["无标注时要求用户手动标注尺寸","AI全自动:600mm全图锚点,无需用户标注"],["不告知用户精度等级","UI必须标注「精确值」或「估算值」"],["平面图斜线填充误判为护墙板","一律不自动识别为护墙板"]];
+
+/* ========== 状态 ========== */
+const S={ft:"plan",redbox:false,scale:null,scaleSrc:"",objs:[],mat:0,rate:10,
+  ward:WARD.map(()=>({on:false,v:0})),cfgBase:2,cfgHang:2,
+  len:{base:0,hang:0,fridge:0,tall:0,counter:0,over:0,side:0},acc:KIT_ACC.map(()=>0)};
+const $=id=>document.getElementById(id);
+const fmt=n=>(Math.round(n)||0).toLocaleString("zh-CN");
+const fmt2=n=>(Math.round(n*100)/100).toLocaleString("zh-CN",{maximumFractionDigits:2});
+const unit=()=>{const m=MATS[S.mat];return m.min+(m.max-m.min)*(S.rate-10)/5;};
+const dimScale=()=>S.ft==="cad"?0.001:(S.scale||0);
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+function precision(){if(S.ft==="cad")return{cls:"exact",txt:"精确值 ±0.1-1%"};if(S.redbox)return{cls:"est",txt:"估算值 ±2-5%(红框+标注)"};return S.scale?{cls:"est",txt:"估算值 ±5-8%(600mm锚点)"}:{cls:"na",txt:"精度:待识别"};}
+function toast(msg,cls){const t=document.createElement("div");t.className="toast "+(cls||"toast-info");t.textContent=msg;document.body.appendChild(t);setTimeout(()=>t.remove(),2600);}
+
+/* ========== 视图/步骤导航 ========== */
+function switchView(v){
+  ["quotes","editor","rules"].forEach(k=>{
+    $("view-"+k).classList.toggle("hidden",k!==v);
+    $("nav-"+k).classList.toggle("active",k===v);
+  });
+}
+const STEP_NAMES=["上传资料","AI解析","自动计价","报价单"];
+function qOpenEditorStep(n){
+  for(let i=1;i<=4;i++)$("step-"+i).classList.toggle("hidden",i!==n);
+  document.querySelectorAll("#steps-bar .step-dot").forEach(d=>{
+    const s=+d.dataset.step;
+    d.classList.toggle("active",s===n);
+    d.classList.toggle("done",s<n);
+  });
+  $("editor-step-breadcrumb").textContent=`步骤 ${n}/4 · ${STEP_NAMES[n-1]}`;
+  if(n===2)$("q-step2-fh-display").textContent=($("q-floor-height").value||"—")+" m";
+  if(n===3)recalc();
+  if(n===4){recalc();renderTiers();}
+  window.scrollTo({top:0,behavior:"smooth"});
+}
+function qNewQuote(){switchView("editor");qOpenEditorStep(1);}
+function backToList(){qSaveQuote(true);switchView("quotes");}
+function qToggleRecogConfig(){const p=$("q-recog-config-panel");p.style.display=p.style.display==="none"?"block":"none";}
+
+/* ========== 画布与叠加 ========== */
+let img=null,overlays=[];
+const cv=$("q-ai-canvas"),ctx=cv.getContext("2d");
+function draw(){if(!img)return;const k=cv.width/img.naturalWidth;ctx.clearRect(0,0,cv.width,cv.height);ctx.drawImage(img,0,0,cv.width,cv.height);
+  overlays.forEach(o=>{ctx.lineWidth=3;
+    if(o.t==="anchor"){ctx.strokeStyle="#E53E3E";ctx.beginPath();ctx.moveTo(o.x*k,o.y*k);ctx.lineTo((o.x+o.w)*k,(o.y+o.h)*k);ctx.stroke();}
+    else if(o.t==="red"){ctx.strokeStyle="#E53E3E";ctx.strokeRect(o.x*k,o.y*k,o.w*k,o.h*k);}
+    else{ctx.strokeStyle=o.t==="cab"?"#3182CE":o.t==="panel"?"#38A169":"#805AD5";ctx.strokeRect(o.x*k,o.y*k,o.w*k,o.h*k);
+      ctx.fillStyle=ctx.strokeStyle;ctx.font="bold 14px sans-serif";ctx.fillText(o.label||"",o.x*k+4,o.y*k+16);}});}
+function showImg(im){img=im;cv.width=1000;cv.height=Math.round(1000*im.naturalHeight/im.naturalWidth);draw();
+  $("q-btn-analyze").disabled=false;
+  $("q-status-drawing").textContent="✅ 图纸:已上传("+im.naturalWidth+"×"+im.naturalHeight+")";
+  $("q-status-drawing").style.background="#C6F6D5";$("q-status-drawing").style.color="#276749";}
+
+/* ========== 示例图纸生成 ========== */
+function genSample(u){
+  const c=document.createElement("canvas");c.width=1600;c.height=1100;const g=c.getContext("2d");
+  g.fillStyle="#fff";g.fillRect(0,0,1600,1100);
+  g.strokeStyle="#000";g.lineWidth=10;g.strokeRect(60,60,1480,980);
+  g.lineWidth=4;g.strokeStyle="#000";
+  g.beginPath();g.moveTo(800,60);g.lineTo(800,400);g.stroke();          // 内墙
+  g.fillStyle="#333";                                                   // 厨房(深色填充带,AI可解算台面深度)
+  if(u){g.beginPath();g.moveTo(100,100);g.lineTo(140,100);g.lineTo(140,220);g.lineTo(380,220);g.lineTo(380,100);g.lineTo(420,100);g.lineTo(420,260);g.lineTo(100,260);g.closePath();g.fill();}
+  else{g.beginPath();g.moveTo(100,100);g.lineTo(540,100);g.lineTo(540,140);g.lineTo(140,140);g.lineTo(140,360);g.lineTo(100,360);g.closePath();g.fill();}
+  g.strokeStyle="#000";g.lineWidth=3;                                   // 柜体(矩形+叉线)
+  [[1150,120,300,80],[1150,700,240,70],[620,940,200,70],[300,700,70,220]].forEach(r=>{g.strokeRect(...r);g.beginPath();g.moveTo(r[0],r[1]);g.lineTo(r[0]+r[2],r[1]+r[3]);g.moveTo(r[0]+r[2],r[1]);g.lineTo(r[0],r[1]+r[3]);g.stroke();});
+  g.lineWidth=2;                                                        // 护墙板斜线填充(AI 必须忽略,规则#14)
+  for(let i=0;i<12;i++){g.beginPath();g.moveTo(620+i*26,120);g.lineTo(640+i*26,160);g.stroke();}
+  g.fillStyle="#999";g.font="26px sans-serif";g.fillText("厨房",220,320);g.fillText("主卧",1250,450);g.fillText("客厅",600,600);
+  if(u){g.strokeStyle="#E53E3E";g.lineWidth=8;g.strokeRect(80,80,360,200);}   // 红框
+  const im=new Image();im.src=c.toDataURL("image/png");return im;
+}
+function qUseMockDrawing(isU){const im=genSample(isU);im.onload=()=>{showImg(im);toast(isU?"已载入示例户型图(U型厨房+红框)":"已载入示例户型图(L型厨房)");qStartAnalysis();};}
+function qHandleUnified(inp){const f=inp.files[0];if(!f)return;
+  if(/\.(pdf|dwg|dxf)$/i.test(f.name)){alert("PDF/DWG 请先转 JPG/PNG(Demo 限制)");return;}
+  const rd=new FileReader();rd.onload=()=>{const im=new Image();im.onload=()=>{showImg(im);qStartAnalysis();};im.src=rd.result;};rd.readAsDataURL(f);}
+function qStartAnalysis(){if(!img){toast("请先上传图纸或选择示例","toast-error");return;}qOpenEditorStep(2);runAI();}
+/* ========== AI 识别引擎 v3(墙体分离 + 柜体特征分类) ========== */
+function logAI(html,cls){const el=$("q-ai-log");const s=document.createElement("span");if(cls)s.className=cls;s.innerHTML=html;el.appendChild(s);el.scrollTop=el.scrollHeight;}
+function setProg(i,st){$("q-ai-prog-"+i).className="ai-progress-step "+(st||"");}
+function erodeMask(m,w,h,it){let cur=m;for(let t=0;t<it;t++){const o=new Uint8Array(w*h);
+  for(let y=1;y<h-1;y++)for(let x=1;x<w-1;x++){const i=y*w+x;
+    if(cur[i]&&cur[i-1]&&cur[i+1]&&cur[i-w]&&cur[i+w]&&cur[i-w-1]&&cur[i-w+1]&&cur[i+w-1]&&cur[i+w+1])o[i]=1;}
+  cur=o;}return cur;}
+function dilateMask(m,w,h,it){let cur=m;for(let t=0;t<it;t++){const o=new Uint8Array(cur);
+  for(let y=1;y<h-1;y++)for(let x=1;x<w-1;x++){const i=y*w+x;
+    if(cur[i]){o[i-1]=1;o[i+1]=1;o[i-w]=1;o[i+w]=1;}}
+  cur=o;}return cur;}
+function wallThickness(wm,w,h){const runs=[];
+  for(let y=0;y<h;y++){let r=0;for(let x=0;x<=w;x++){const v=x<w?wm[y*w+x]:0;if(v)r++;else{if(r>=3&&r<=40)runs.push(r);r=0;}}}
+  for(let x=0;x<w;x++){let r=0;for(let y=0;y<=h;y++){const v=y<h?wm[y*w+x]:0;if(v)r++;else{if(r>=3&&r<=40)runs.push(r);r=0;}}}
+  if(!runs.length)return 6;runs.sort((a,b)=>a-b);return runs[runs.length>>1];}
+async function runAI(){
+  $("q-ai-log").innerHTML="";overlays=[];S.redbox=false;S.scale=null;S.scaleSrc="";S.objs=[];S.ft="plan";
+  $("q-step2-next-btn").disabled=true;
+  setProg(0,"running");setProg(1,"");setProg(2,"");
+  logAI("▶ AI 全图识别启动(优先级:无红框全图 &gt; 红框校准)","b");await sleep(250);
+  const AW=500,k=AW/img.naturalWidth,AH=Math.round(img.naturalHeight*k);
+  const oc=document.createElement("canvas");oc.width=AW;oc.height=AH;const og=oc.getContext("2d");
+  og.drawImage(img,0,0,AW,AH);const d=og.getImageData(0,0,AW,AH).data;
+  const ink=new Uint8Array(AW*AH);
+  let rx0=1e9,ry0=1e9,rx1=-1,ry1=-1,rc=0;
+  for(let i=0;i<AW*AH;i++){const r=d[i*4],g=d[i*4+1],b=d[i*4+2];
+    if(r>140&&r-g>50&&r-b>50){rc++;if(i%AW<rx0)rx0=i%AW;if(i%AW>rx1)rx1=i%AW;const y=(i/AW)|0;if(y<ry0)ry0=y;if(y>ry1)ry1=y;}
+    else if(.299*r+.587*g+.114*b<140)ink[i]=1;}
+  logAI("· 步骤1 结构扫描:"+AW+"×"+AH+" 栅格化,墨线像素 "+ink.reduce((a,v)=>a+v,0));await sleep(200);
+  if(rc>150){S.redbox=true;overlays.push({t:"red",x:rx0/k,y:ry0/k,w:(rx1-rx0)/k,h:(ry1-ry0)/k});logAI("· 检测到红框 → 场景B(有红框+标注,±2-5%)。AI 仍先执行全图识别(规则#6)","w");}
+  else logAI("· 未检测到红框 → 场景A(无红框,AI全自动)");
+  $("q-ai-scene-chip").textContent=S.redbox?"场景B · 红框+标注":"场景A · 无红框全自动";
+  await sleep(200);
+  /* 连通域(全墨线) */
+  const cc=mask=>{const vis=new Uint8Array(mask.length),out=[];
+    for(let s=0;s<mask.length;s++){if(!mask[s]||vis[s])continue;
+      const q=[s];vis[s]=1;let x0=1e9,y0=1e9,x1=-1,y1=-1,area=0,hh=0;
+      while(hh<q.length){const c=q[hh++];area++;const cx=c%AW,cy=(c/AW)|0;
+        if(cx<x0)x0=cx;if(cx>x1)x1=cx;if(cy<y0)y0=cy;if(cy>y1)y1=cy;
+        const nb=[c-1,c+1,c-AW,c+AW];for(const n of nb){if(n>=0&&n<mask.length&&mask[n]&&!vis[n]){vis[n]=1;q.push(n);}}}
+      out.push({x:x0,y:y0,w:x1-x0,h:y1-y0,area});}
+    return out;};
+  const compsAll=cc(ink).sort((a,b)=>b.w*b.h-a.w*a.h);
+  logAI("· 连通域分析:"+compsAll.length+" 个结构体");await sleep(200);
+  setProg(0,"done");setProg(1,"running");
+  /* 模式判定:示意图(实心厨房色块) vs 彩色户型图(墙体分离) */
+  const solid=compsAll.find(c=>{const f=c.area/(c.w*c.h);return f>0.8&&c.w*c.h>0.02*AW*AH&&c.w<0.6*AW&&c.h<0.6*AH;});
+  let kitchen=null;const wards=[];let excluded=0;
+  if(solid){
+    /* —— 示意图路径:探针判定 L/U + 二次方程解台面深度 —— */
+    const c=solid,probes=[[c.x+c.w/2,c.y+3],[c.x+c.w/2,c.y+c.h-3],[c.x+3,c.y+c.h/2],[c.x+c.w-3,c.y+c.h/2]]
+      .map(p=>ink[((p[1]|0)*AW+(p[0]|0))]?1:0).reduce((a,v)=>a+v,0);
+    const sL=c.w+c.h,discL=sL*sL-4*c.area,sU=c.w+2*c.h,discU=sU*sU-8*c.area,sU2=2*c.w+c.h,discU2=sU2*sU2-8*c.area;
+    let dd=0;
+    if(probes===2&&discL>0)dd=(sL-Math.sqrt(discL))/2;
+    else if(probes===3&&discU>0)dd=(sU-Math.sqrt(discU))/4;
+    else if(probes===3&&discU2>0)dd=(sU2-Math.sqrt(discU2))/4;
+    if(dd>.02*AW&&dd<.12*AW)kitchen={...c,dd};
+    if(kitchen){
+      const sc=0.6/(kitchen.dd/k);S.scale=sc;S.scaleSrc="AI定位厨房台面深度=600mm锚点(全图通用)";
+      overlays.push({t:"kitchen",x:kitchen.x/k,y:kitchen.y/k,w:kitchen.w/k,h:kitchen.h/k,label:"厨房"});
+      overlays.push({t:"anchor",x:kitchen.x/k,y:kitchen.y/k,w:kitchen.dd/k,h:0});
+      const kt=kitchen.w>=kitchen.h?"L":"U";
+      if(kt==="L"){$("kA").value=(Math.max(kitchen.w,kitchen.h)*sc).toFixed(2);$("kC").value=(Math.min(kitchen.w,kitchen.h)*sc).toFixed(2);$("kB").value="";}
+      else{$("kA").value=(kitchen.h*sc).toFixed(2);$("kB").value=(kitchen.h*sc).toFixed(2);$("kC").value=(kitchen.w*sc).toFixed(2);}
+      logAI("· 定位厨房(示意图模式):台面深度 "+Math.round(kitchen.dd)+"px = 600mm → 1px="+(sc*1000).toFixed(1)+"mm","b");
+    }
+    let cn=0;
+    for(const c2 of compsAll){
+      if(cn>=8)break;
+      if(c2.w>.6*AW&&c2.h>.6*AH)continue;
+      if(kitchen&&c2.x>=kitchen.x-4&&c2.y>=kitchen.y-4&&c2.x+c2.w<=kitchen.x+kitchen.w+4&&c2.y+c2.h<=kitchen.y+kitchen.h+4)continue;
+      const f=c2.area/(c2.w*c2.h);
+      if(c2.w>.03*AW&&c2.h>.03*AW&&Math.min(c2.w,c2.h)>=.018*AW&&f>=.10&&f<.7){
+        cn++;wards.push(c2);}}
+  }else{
+    /* —— 彩色户型图路径:墙体分离 + 柜体特征分类 —— */
+    logAI("· 彩色户型图模式:腐蚀法分离墙体(粗线)与家具(细线)","b");await sleep(200);
+    let wm=erodeMask(ink,AW,AH,2),eit=2;
+    if(wm.reduce((a,v)=>a+v,0)<0.005*AW*AH){wm=erodeMask(ink,AW,AH,1);eit=1;}
+    let T=wallThickness(wm,AW,AH)+2*eit;T=Math.max(4,Math.min(24,T));
+    logAI("· 墙厚先验 T≈"+T+"px(仅用于形状判定,不作尺寸参照,规则#11)");
+    const wcore=dilateMask(wm,AW,AH,1);
+    const wz=dilateMask(wm,AW,AH,Math.max(3,Math.round(T*0.7)));
+    const thin=new Uint8Array(AW*AH);for(let i=0;i<AW*AH;i++)thin[i]=ink[i]&&!wcore[i];
+    const comps=cc(thin).sort((a,b)=>b.w*b.h-a.w*a.h);
+    const att=c=>{let hit=0,tot=0;
+      for(let x=c.x;x<=c.x+c.w;x+=2){for(const y of[c.y,c.y+c.h]){tot++;if(wz[((y|0)*AW+(x|0))])hit++;}}
+      for(let y=c.y;y<=c.y+c.h;y+=2){for(const x of[c.x,c.x+c.w]){tot++;if(wz[((y|0)*AW+(x|0))])hit++;}}
+      return hit/Math.max(1,tot);};
+    const hatch=c=>{let rt=0,ct=0,nr=0,nc=0;
+      for(let y=c.y+2;y<c.y+c.h-1;y+=2){nr++;let t=0,pv=0;for(let x=c.x;x<=c.x+c.w;x++){const v=thin[y*AW+x]?1:0;if(v&&!pv)t++;pv=v;}rt+=t;}
+      for(let x=c.x+2;x<c.x+c.w-1;x+=2){nc++;let t=0,pv=0;for(let y=c.y;y<=c.y+c.h;y++){const v=thin[y*AW+x]?1:0;if(v&&!pv)t++;pv=v;}ct+=t;}
+      return Math.max(rt/Math.max(1,nr),ct/Math.max(1,nc));};
+    const xpat=c=>{let h1=0,h2=0;const N=16;
+      for(let i=1;i<N;i++){if(thin[((c.y+c.h*i/N)|0)*AW+((c.x+c.w*i/N)|0)])h1++;
+        if(thin[((c.y+c.h-c.h*i/N)|0)*AW+((c.x+c.w*i/N)|0)])h2++;}
+      return Math.min(h1,h2)/N;};
+    const smalls=comps.filter(s=>s.w>=0.5*T&&s.w<=2.5*T&&s.h>=0.5*T&&s.h<=2.5*T);
+    const kidsOf=c=>smalls.filter(s=>s!==c&&s.x>c.x&&s.y>c.y&&s.x+s.w<c.x+c.w&&s.y+s.h<c.y+c.h).length;
+    const bands=[];
+    for(const c of comps){
+      if(c.w>0.55*AW||c.h>0.55*AH)continue;
+      const short=Math.min(c.w,c.h),long=Math.max(c.w,c.h);
+      if(short<1.2*T)continue;                                        // 噪点/小符号
+      const a=att(c),f=c.area/(c.w*c.h);
+      /* 橱柜台面带:贴墙 + 长条 + 深度≈3T(600mm) + 内含水槽/灶具符号 */
+      if(a>=0.25&&long>=2.2*short&&short>=2.2*T&&short<=3.8*T&&(kidsOf(c)>=1||(f>=0.1&&f<=0.35&&long>=3*short))){bands.push(c);continue;}
+      /* 衣柜:贴墙 + 斜线填充 或 X交叉 */
+      if(a>=0.3&&short>=1.5*T&&short<=5*T&&long>=1.4*short&&(hatch(c)>=4||xpat(c)>=0.55)){wards.push(c);continue;}
+      /* 近方形 X交叉柜(玄关柜等) */
+      if(a>=0.3&&xpat(c)>=0.55&&short>=1.5*T&&short<=6*T&&long<2.2*short){wards.push(c);continue;}
+      excluded++;}
+    logAI("· 柜体判定标准:贴墙率≥30% 且(斜线填充 / X交叉 / 600mm台面带+水槽灶具)","b");
+    logAI("· 已排除床/沙发/桌椅/洁具等独立家具 "+excluded+" 个(不贴墙或特征不符)");await sleep(200);
+    /* 厨房:台面带聚类 → 600mm 锚点 */
+    if(bands.length){
+      bands.sort((a,b)=>b.w*b.h-a.w*a.h);
+      const cluster=[bands[0]];
+      for(const b of bands.slice(1)){if(cluster.some(c=>Math.abs(c.x-b.x)<6*T&&Math.abs(c.y-b.y)<6*T||
+        (b.x<c.x+c.w+6*T&&c.x<b.x+b.w+6*T&&b.y<c.y+c.h+6*T&&c.y<b.y+b.h+6*T)))cluster.push(b);}
+      const b0=cluster[0],dNat=Math.min(b0.w,b0.h)/k;
+      S.scale=0.6/dNat;S.scaleSrc="AI定位橱柜台面深度=600mm锚点(全图通用)";
+      let ux0=1e9,uy0=1e9,ux1=-1,uy1=-1;
+      cluster.forEach(c=>{ux0=Math.min(ux0,c.x);uy0=Math.min(uy0,c.y);ux1=Math.max(ux1,c.x+c.w);uy1=Math.max(uy1,c.y+c.h);});
+      kitchen={x:ux0,y:uy0,w:ux1-ux0,h:uy1-uy0};
+      overlays.push({t:"kitchen",x:ux0/k,y:uy0/k,w:(ux1-ux0)/k,h:(uy1-uy0)/k,label:"厨房"});
+      overlays.push({t:"anchor",x:b0.x/k,y:b0.y/k,w:Math.min(b0.w,b0.h)/k,h:0});
+      const lens=cluster.map(c=>Math.max(c.w,c.h)*S.scale).sort((a,b)=>b-a);
+      if(lens.length===1){$("kA").value=lens[0].toFixed(2);$("kB").value="";$("kC").value="";logAI("· 厨房:检测到1条台面带 "+lens[0].toFixed(2)+"m,请在下一步补充另一侧边长","w");}
+      else if(lens.length===2){$("kA").value=lens[0].toFixed(2);$("kC").value=lens[1].toFixed(2);$("kB").value="";}
+      else{$("kA").value=lens[0].toFixed(2);$("kB").value=lens[1].toFixed(2);$("kC").value=lens[2].toFixed(2);}
+      logAI("· 定位厨房:"+cluster.length+" 条台面带,深度 "+Math.round(dNat)+"px = 600mm → 1px="+(S.scale*1000).toFixed(1)+"mm","b");
+    }else logAI("· 未能自动定位厨房台面带 → 请在下一步补充一个已知尺寸","e");
+  }
+  draw();await sleep(200);
+  setProg(1,"done");setProg(2,"running");
+  /* 柜体输出 */
+  let cn=0;
+  for(const c of wards){
+    if(cn>=8)break;
+    cn++;S.objs.push({name:"柜体"+cn,kind:"cab",src:"AI",w:Math.round(c.w/k),h:Math.round(c.h/k)});
+    overlays.push({t:"cab",x:c.x/k,y:c.y/k,w:c.w/k,h:c.h/k,label:"柜体"+cn});}
+  logAI("· 柜体识别:"+cn+" 个(max(W,H)×比例×2.5=投影面积)","b");await sleep(200);
+  logAI("· 护墙板:平面图斜线填充不可靠 → <b>不自动识别</b>(规则#14),如需请在下一步手动添加","w");await sleep(150);
+  logAI("· 尺寸计算完成。精度等级:"+precision().txt,"b");
+  setProg(2,"done");draw();rebuildObjs();recalc();
+  $("q-step2-next-btn").disabled=false;
+}
+
+/* ========== 对象表(步骤3) ========== */
+function rebuildObjs(){
+  const tb=$("objBody");tb.innerHTML="";
+  S.objs.forEach(p=>{const tr=document.createElement("tr");
+    tr.innerHTML=`<td><input type="text" value="${p.name}" style="width:90px" data-f="name"></td><td>${p.src}</td>
+    <td><input type="number" value="${p.w}" data-f="w"></td><td><input type="number" value="${p.h}" data-f="h"></td>
+    <td class="text-right" data-o="len">-</td><td class="text-right" data-o="area">-</td><td class="text-right" data-o="amt">-</td>
+    <td><button class="btn btn-danger btn-sm">删</button></td>`;
+    tr.querySelectorAll("input").forEach(inp=>inp.oninput=()=>{p[inp.dataset.f]=inp.dataset.f==="name"?inp.value:(+inp.value||0);recalc();});
+    tr.querySelector("button").onclick=()=>{S.objs.splice(S.objs.indexOf(p),1);rebuildObjs();recalc();};
+    p._tr=tr;tb.appendChild(tr);});
+}
+$("addCab").onclick=()=>{S.objs.push({name:"柜体"+(S.objs.length+1),kind:"cab",src:"手动",w:0,h:0});rebuildObjs();recalc();};
+$("addPanel").onclick=()=>{S.objs.push({name:"护墙板"+(S.objs.filter(o=>o.kind==="panel").length+1),kind:"panel",src:"手动",w:0,h:0});rebuildObjs();recalc();};
+
+/* ========== 增值项 / 厨房分项(步骤3表格) ========== */
+function buildWard(){$("wardBody").innerHTML="";WARD.forEach((w,i)=>{const tr=document.createElement("tr");
+  tr.innerHTML=`<td><input type="checkbox" style="width:16px"></td><td>${w[0]}</td><td class="text-right">${fmt(w[1])} ${w[2]}</td><td><input type="number" min="0" step="any" placeholder="0"></td><td class="text-right">¥0</td>`;
+  const cb=tr.children[0].firstChild,inp=tr.children[3].firstChild;
+  cb.checked=S.ward[i].on;inp.value=S.ward[i].v||"";inp.disabled=!S.ward[i].on;
+  cb.onchange=()=>{S.ward[i].on=cb.checked;inp.disabled=!cb.checked;recalc();};
+  inp.oninput=()=>{S.ward[i].v=+inp.value||0;recalc();};
+  w._tr=tr;$("wardBody").appendChild(tr);});}
+function buildKit(){const tb=$("kitBody");tb.innerHTML="";
+  const opts=base=>DOORS.map((d,i)=>d[4]===null&&base?null:`<option value="${i}">${d[0]}·${d[1]}·${d[2]} ${base?fmt(d[4])+"元/m":fmt(d[3])+"元/m"}</option>`).filter(Boolean).join("");
+  const mk=h=>{const tr=document.createElement("tr");tr.innerHTML=h;tb.appendChild(tr);return tr;};
+  mk(`<td>地柜(长=厨房可计费总长)</td><td><select id="selBase">${opts(true)}</select> <input type="number" id="lenBase" step="0.01" style="width:70px"> m</td><td class="text-right" id="pBase">-</td><td class="text-right" id="aBase">¥0</td>`);
+  mk(`<td>吊柜</td><td><select id="selHang">${opts(false)}</select> <input type="number" id="lenHang" step="0.01" value="0" style="width:70px"> m</td><td class="text-right" id="pHang">-</td><td class="text-right" id="aHang">¥0</td>`);
+  KIT_FIX.forEach(f=>mk(`<td>${f[1]}</td><td><input type="number" id="len_${f[0]}" step="0.01" value="0" style="width:70px"> ${f[3]==="元/㎡"?"㎡":"m"}</td><td class="text-right">${fmt(f[2])} ${f[3]}</td><td class="text-right" id="a_${f[0]}">¥0</td>`));
+  KIT_ACC.forEach((a,i)=>mk(`<td>${a[0]}</td><td><input type="number" id="acc_${i}" step="1" value="0" style="width:70px"> 个</td><td class="text-right">${fmt(a[1])} 元/个</td><td class="text-right" id="aa_${i}">¥0</td>`));
+  $("selBase").value=S.cfgBase;$("selHang").value=S.cfgHang;
+  $("selBase").onchange=e=>{S.cfgBase=+e.target.value;recalc();};
+  $("selHang").onchange=e=>{S.cfgHang=+e.target.value;recalc();};
+  $("lenBase").oninput=e=>{S.len.base=+e.target.value||0;recalc();};
+  $("lenHang").oninput=e=>{S.len.hang=+e.target.value||0;recalc();};
+  KIT_FIX.forEach(f=>{const el=$("len_"+f[0]);el.value=S.len[f[0]]||"";el.oninput=e=>{S.len[f[0]]=+e.target.value||0;recalc();};});
+  KIT_ACC.forEach((a,i)=>{const el=$("acc_"+i);el.value=S.acc[i]||"";el.oninput=e=>{S.acc[i]=+e.target.value||0;recalc();};});}
+
+/* ========== 计算引擎 ========== */
+let LASTQ={baseSum:0,kSum:0,wSum:0,varArea:0,baseRows:[],valueRows:[],kitLines:[],cabLines:[],panelLines:[],wardLines:[]};
+function recalc(){
+  const pr=precision(),sc=dimScale();
+  $("precBadge").className="badge "+pr.cls;$("precBadge").textContent=pr.txt;
+  $("q-quote-prec").className="badge "+pr.cls;$("q-quote-prec").textContent=pr.txt;
+  $("sceneChip").textContent="场景:"+(S.ft==="cad"?"CAD矢量":S.redbox?"B 有红框+标注":"A 无红框");
+  $("scaleInfo").textContent=S.ft==="cad"?"CAD矢量直读(mm)":(S.scale?`1px=${(sc*1000).toFixed(2)}mm(${S.scaleSrc})`:"比例未建立");
+  /* 基础项 */
+  let baseSum=0,varArea=0;const baseRows=[],cabLines=[],panelLines=[];
+  S.objs.forEach(p=>{const len=Math.max(p.w||0,p.h||0)*sc,area=len*2.5,amt=area*unit();baseSum+=amt;varArea+=area;
+    baseRows.push({name:p.name,kind:p.kind,src:p.src,len,area,amt});
+    const line=`<div class="total-line"><span>${p.name}[${p.src}]:max边 ${fmt2(len)}m ×2.5 = ${fmt2(area)}㎡ × ¥${fmt(unit())}</span><span class="num">¥${fmt(amt)}</span></div>`;
+    (p.kind==="panel"?panelLines:cabLines).push(line);
+    if(p._tr){p._tr.querySelector('[data-o="len"]').textContent=fmt2(len);p._tr.querySelector('[data-o="area"]').textContent=fmt2(area);p._tr.querySelector('[data-o="amt"]').textContent="¥"+fmt(amt);}});
+  $("baseTotal").textContent="¥"+fmt(baseSum);
+  /* 厨房判定 */
+  const sides=[$("kA").value,$("kB").value,$("kC").value].map(v=>+v||0).filter(v=>v>0);
+  let kT=0,kt="未判定",kF="";
+  if(sides.length>=2){const mn=Math.min(...sides),mx=[...sides].sort((a,b)=>b-a);
+    if(mn<=2){kt="L型";kT=Math.max(mx[0]+mx[mx.length-1]-1.6,0);kF=`L:${fmt2(mx[0])}+${fmt2(mx[mx.length-1])}−0.6−1.0`;}
+    else if(sides.length>=3){kt="U型";kT=Math.max(mx[0]+mx[1]+mx[2]-2.2,0);kF=`U:${fmt2(mx[0])}+${fmt2(mx[1])}+${fmt2(mx[2])}−0.6−0.6−1.0`;}}
+  $("kType").textContent=kt;$("kType").className="chip "+(kt==="U型"?"u":kt==="L型"?"l":"n");
+  $("kFormula").textContent=kT>0?`${kF} = ${fmt2(kT)}m 可计费总长`:kF;
+  if(document.activeElement!==$("lenBase")){$("lenBase").value=kT?kT.toFixed(2):"";S.len.base=kT;}
+  if(document.activeElement!==$("len_counter")){$("len_counter").value=kT?kT.toFixed(2):"";S.len.counter=kT;}
+  $("fbRow").style.display=S.scale||S.ft==="cad"?"none":"flex";
+  /* 增值项:衣柜 */
+  let wSum=0;const wardLines=[],valueRows=[];
+  WARD.forEach((w,i)=>{const v=S.ward[i].on?S.ward[i].v:0,a=v*w[1];wSum+=a;
+    if(w._tr)w._tr.children[4].textContent="¥"+fmt(a);
+    if(v>0&&S.ward[i].on){wardLines.push(`<div class="total-line"><span>${w[0]} ×${fmt2(v)}</span><span class="num">¥${fmt(a)}</span></div>`);
+      valueRows.push({item:"衣柜增值·"+w[0],spec:w[2],qty:fmt2(v),price:fmt(w[1])+" "+w[2],amt:a});}});
+  /* 增值项:厨房 */
+  let kSum=0;const kitLines=[];const db=DOORS[S.cfgBase],dh=DOORS[S.cfgHang];
+  $("pBase").textContent=fmt(db[4]||0)+" 元/m";$("pHang").textContent=fmt(dh[3])+" 元/m";
+  const aB=S.len.base*(db[4]||0);kSum+=aB;$("aBase").textContent="¥"+fmt(aB);
+  if(aB){kitLines.push(`<div class="total-line"><span>地柜[${db[1]} ${db[2]}] ${fmt2(S.len.base)}m × ¥${fmt(db[4]||0)}</span><span class="num">¥${fmt(aB)}</span></div>`);
+    valueRows.push({item:"橱柜·地柜",spec:`${db[0]}·${db[1]}·${db[2]}`,qty:fmt2(S.len.base)+" m",price:fmt(db[4]||0)+" 元/m",amt:aB});}
+  const aH=S.len.hang*dh[3];kSum+=aH;$("aHang").textContent="¥"+fmt(aH);
+  if(aH){kitLines.push(`<div class="total-line"><span>吊柜[${dh[1]} ${dh[2]}] ${fmt2(S.len.hang)}m × ¥${fmt(dh[3])}</span><span class="num">¥${fmt(aH)}</span></div>`);
+    valueRows.push({item:"橱柜·吊柜",spec:`${dh[0]}·${dh[1]}·${dh[2]}`,qty:fmt2(S.len.hang)+" m",price:fmt(dh[3])+" 元/m",amt:aH});}
+  KIT_FIX.forEach(f=>{const a=S.len[f[0]]*f[2];kSum+=a;$("a_"+f[0]).textContent="¥"+fmt(a);
+    if(a){kitLines.push(`<div class="total-line"><span>${f[1]} ${fmt2(S.len[f[0]])}${f[3]==="元/㎡"?"㎡":"m"} × ¥${fmt(f[2])}</span><span class="num">¥${fmt(a)}</span></div>`);
+      valueRows.push({item:"橱柜·"+f[1],spec:"—",qty:fmt2(S.len[f[0]])+(f[3]==="元/㎡"?" ㎡":" m"),price:fmt(f[2])+" "+f[3],amt:a});}});
+  KIT_ACC.forEach((ac,i)=>{const a=S.acc[i]*ac[1];kSum+=a;$("aa_"+i).textContent="¥"+fmt(a);
+    if(a){kitLines.push(`<div class="total-line"><span>${ac[0]} ×${S.acc[i]}</span><span class="num">¥${fmt(a)}</span></div>`);
+      valueRows.push({item:"橱柜·"+ac[0],spec:"配件",qty:S.acc[i]+" 个",price:fmt(ac[1])+" 元/个",amt:a});}});
+  $("valueTotal").textContent="¥"+fmt(wSum+kSum);
+  $("grandTotal").textContent="¥"+fmt(baseSum+wSum+kSum);
+  LASTQ={baseSum,kSum,wSum,varArea,baseRows,valueRows,kitLines,cabLines,panelLines,wardLines};
+  renderStep4();
+}
+
+/* ========== 步骤4:报价单渲染 ========== */
+function renderTiers(){
+  const box=$("q-ai-tier-selector");box.innerHTML="";
+  MATS.forEach((m,i)=>{const d=document.createElement("div");
+    d.className="tier-card"+(i===S.mat?" selected":"");
+    d.innerHTML=`<div class="tier-name">${m.n}</div><div class="tier-price">¥${fmt(m.min)} — ¥${fmt(m.max)}/㎡</div><div class="tier-note">${i===S.mat?"✓ 已选择 · 当前单价 ¥"+fmt(unit()):"点击选择"}</div>`;
+    d.onclick=()=>{S.mat=i;renderTiers();recalc();};
+    box.appendChild(d);});
+}
+function qSetMat(i){S.mat=i;renderTiers();recalc();}
+function renderStep4(){
+  const m=MATS[S.mat],Q=LASTQ;
+  const fixed=Q.kSum+Q.wSum;
+  const lo=Q.varArea*m.min+fixed,hi=Q.varArea*m.max+fixed,cur=Q.varArea*unit()+fixed;
+  $("q-quote-meta").textContent=`生成日期:${new Date().toLocaleDateString("zh-CN")} | 材质档:${m.n} | 浮动率:${S.rate}%`;
+  $("q-step5-range").textContent=`¥${fmt(lo)} — ¥${fmt(hi)}`;
+  $("q-step5-rate-text").textContent=`预估总价区间(${m.n}档单价 ¥${fmt(m.min)}~¥${fmt(m.max)}/㎡) | ${precision().txt}`;
+  $("q-ai-range-total").textContent="¥"+fmt(cur);
+  $("q-ai-range-note").textContent=`当前参考总价(${m.n} ¥${fmt(unit())}/㎡ × ${fmt2(Q.varArea)}㎡ + 增值项 ¥${fmt(fixed)})`;
+  $("q-ai-float-badge").textContent=S.rate+"%";
+  $("q-ai-float-slider").value=S.rate;
+  $("global-float-slider").value=S.rate;$("global-float-badge").textContent=S.rate+"%";
+  document.querySelectorAll(".float-preset button[data-aifp]").forEach(b=>b.classList.toggle("active",+b.dataset.aifp===Math.round(S.rate)));
+  $("q-ai-float-result").innerHTML=`当前单价:<b>¥${fmt2(unit())}/㎡</b>(${m.n} ${fmt(m.min)}~${fmt(m.max)},浮动率 ${S.rate}%)· 投影面积 <b>${fmt2(Q.varArea)}㎡</b> · 基础项 ¥${fmt(Q.baseSum)} + 增值项 ¥${fmt(fixed)} = 总计 <b>¥${fmt(cur)}</b>`;
+  /* 空间卡片 */
+  const cards=[];
+  if(Q.kitLines.length)cards.push(["🍳 厨房","¥"+fmt(Q.kSum),Q.kitLines]);
+  if(Q.cabLines.length)cards.push(["🗄️ 柜体(投影面积)","¥"+fmt(LASTQ.baseRows.filter(r=>r.kind==="cab").reduce((a,r)=>a+r.amt,0)),Q.cabLines]);
+  if(Q.panelLines.length)cards.push(["🧱 护墙板(手动)","¥"+fmt(LASTQ.baseRows.filter(r=>r.kind==="panel").reduce((a,r)=>a+r.amt,0)),Q.panelLines]);
+  if(Q.wardLines.length)cards.push(["👔 衣柜增值项","¥"+fmt(Q.wSum),Q.wardLines]);
+  $("q-space-cards-container").innerHTML=cards.length?cards.map(c=>`<div class="space-card"><div class="space-header"><span>${c[0]}</span><span style="color:var(--primary)">${c[1]}</span></div><div class="space-lines">${c[2].join("")}</div></div>`).join(""):'<div class="note" style="text-align:center;">暂无识别结果,请先完成步骤 1-3</div>';
+  /* 明细表 */
+  $("q-quote-base-body").innerHTML=Q.baseRows.length?Q.baseRows.map(r=>`<tr><td>${r.kind==="panel"?"护墙板·":"柜体·"}${r.name}</td><td>${r.src}</td><td class="text-right">${fmt2(r.len)}</td><td class="text-right">${fmt2(r.area)}</td><td class="text-right">${fmt(unit())}</td><td class="text-right">¥${fmt(r.amt)}</td></tr>`).join(""):'<tr><td colspan="6" class="note">暂无</td></tr>';
+  $("q-quote-value-body").innerHTML=Q.valueRows.length?Q.valueRows.map(r=>`<tr><td>${r.item}</td><td>${r.spec}</td><td class="text-right">${r.qty}</td><td class="text-right">${r.price}</td><td class="text-right">¥${fmt(r.amt)}</td></tr>`).join(""):'<tr><td colspan="5" class="note">暂无</td></tr>';
+  $("q-grand-total-value").textContent="¥"+fmt(cur);
+  $("q-grand-total-area").textContent=`投影面积 ${fmt2(Q.varArea)}㎡ · ${m.n} ¥${fmt2(unit())}/㎡`;
+  $("q-grand-total-note").textContent=cur>0?`基础项 ¥${fmt(Q.baseSum)} + 增值项 ¥${fmt(fixed)}(${precision().txt})`:"上传图纸并完成 AI 识别后自动计算";
+  $("q-step5-disclaimer").textContent=`⚠️ 声明:本报价为 AI 辅助生成的预估价格(${precision().txt}),最终成交价以实际量尺和确认为准。护墙板平面图不自动识别;仅「厨房台面深度600mm」为隐式参照物。`;
+  $("global-est-range").textContent=`¥${fmt(lo)} ~ ¥${fmt(hi)}`;
+  $("global-est-mid").textContent="参考总价: ¥"+fmt(cur);
+}
+function qOnAiFloatChange(v){S.rate=Math.min(15,Math.max(10,+v||10));renderTiers();recalc();}
+function qSetAiFloat(v){S.rate=Math.min(15,Math.max(10,+v||10));renderTiers();recalc();}
+
+/* ========== 报价单管理 ========== */
+let QUOTES=[];
+try{QUOTES=JSON.parse(localStorage.getItem("aiq_new_quotes")||"[]");}catch(e){QUOTES=[];}
+function saveQuotes(){try{localStorage.setItem("aiq_new_quotes",JSON.stringify(QUOTES));}catch(e){}}
+function snapshot(){return JSON.stringify({ft:S.ft,redbox:S.redbox,scale:S.scale,scaleSrc:S.scaleSrc,mat:S.mat,rate:S.rate,cfgBase:S.cfgBase,cfgHang:S.cfgHang,len:S.len,acc:S.acc,ward:S.ward,objs:S.objs.map(o=>({name:o.name,kind:o.kind,src:o.src,w:o.w,h:o.h}))});}
+function qSaveQuote(silent){
+  if(!S.objs.length&&!LASTQ.baseSum&&!LASTQ.kSum&&!LASTQ.wSum){if(!silent)toast("暂无计价内容,请先上传图纸完成AI识别","toast-error");return;}
+  const m=MATS[S.mat],fixed=LASTQ.kSum+LASTQ.wSum;
+  const q={id:Date.now(),name:("报价单 "+(QUOTES.length+1)),date:new Date().toLocaleString("zh-CN"),mat:m.n,rate:S.rate,
+    lo:LASTQ.varArea*m.min+fixed,hi:LASTQ.varArea*m.max+fixed,cur:LASTQ.varArea*unit()+fixed,prec:precision().txt,snap:snapshot()};
+  QUOTES.unshift(q);saveQuotes();renderQuoteList();
+  if(!silent)toast("已存入报价单列表 ✓","toast-success");
+}
+function renderQuoteList(){
+  const tb=$("quote-table-body");tb.innerHTML="";
+  $("quote-empty").style.display=QUOTES.length?"none":"block";
+  QUOTES.forEach((q,i)=>{const tr=document.createElement("tr");
+    tr.innerHTML=`<td><b>${q.name}</b></td><td>${q.date}</td><td>${q.mat}(${q.rate}%)</td>
+    <td class="text-right">¥${fmt(q.lo)} — ¥${fmt(q.hi)}</td><td><span class="badge est" style="font-size:10px;">${q.prec}</span></td>
+    <td></td>`;
+    const td=tr.children[5];
+    const b1=document.createElement("button");b1.className="btn btn-outline btn-sm";b1.textContent="打开";
+    b1.onclick=e=>{e.stopPropagation();qOpenQuote(q);};
+    const b2=document.createElement("button");b2.className="btn btn-danger btn-sm";b2.textContent="删除";b2.style.marginLeft="6px";
+    b2.onclick=e=>{e.stopPropagation();QUOTES.splice(i,1);saveQuotes();renderQuoteList();toast("已删除");};
+    td.appendChild(b1);td.appendChild(b2);
+    tr.style.cursor="pointer";tr.onclick=()=>qOpenQuote(q);
+    tb.appendChild(tr);});
+}
+function qOpenQuote(q){
+  try{const snap=JSON.parse(q.snap);
+    Object.assign(S,{ft:snap.ft,redbox:snap.redbox,scale:snap.scale,scaleSrc:snap.scaleSrc,mat:snap.mat,rate:snap.rate,cfgBase:snap.cfgBase,cfgHang:snap.cfgHang,len:snap.len,acc:snap.acc,ward:snap.ward,objs:snap.objs});
+    $("kA").value="";$("kB").value="";$("kC").value="";
+    /* 重新从对象推导厨房边(若曾识别)——此处保持手动值,直接重算 */
+    rebuildObjs();buildWard();buildKit();recalc();
+    $("editor-title").textContent=q.name;
+    switchView("editor");qOpenEditorStep(4);
+    toast("已打开:"+q.name);
+  }catch(e){toast("打开失败","toast-error");}
+}
+
+/* ========== 规则配置表 ========== */
+function switchRuleTab(t){
+  document.querySelectorAll("#view-rules .tab").forEach(el=>el.classList.remove("active"));
+  event.target.classList.add("active");
+  document.querySelectorAll(".rule-tab-content").forEach(c=>c.classList.add("hidden"));
+  $("rule-tab-"+t).classList.remove("hidden");
+}
+function renderRules(){
+  $("rule-table-mats").innerHTML=MATS.map(m=>`<tr><td><b>${m.n}</b></td><td class="text-right">${fmt(m.min)}</td><td class="text-right">${fmt(m.max)}</td></tr>`).join("");
+  $("rule-table-wardrobe").innerHTML=WARD.map((w,i)=>`<tr><td>${i+1}</td><td>${w[0]}</td><td class="text-right">${fmt(w[1])}</td><td>${w[2]}</td></tr>`).join("");
+  $("rule-table-kitchen").innerHTML=DOORS.map((d,i)=>`<tr><td>${i+1}</td><td>${d[0]}</td><td>${d[1]}</td><td>${d[2]}</td><td class="text-right">${fmt(d[3])}</td><td class="text-right">${d[4]===null?"—(装饰柜无地柜价)":fmt(d[4])}</td></tr>`).join("");
+  $("rule-table-kitchen-fix").innerHTML=KIT_FIX.map(f=>`<tr><td>${f[1]}</td><td class="text-right">${fmt(f[2])}</td><td>${f[3]}</td></tr>`).join("")+KIT_ACC.map(a=>`<tr><td>${a[0]}(配件)</td><td class="text-right">${fmt(a[1])}</td><td>元/个</td></tr>`).join("");
+  $("rule-table-ban").innerHTML=RULES.map((r,i)=>`<tr><td>${i+1}</td><td style="color:#C53030;">🚫 ${r[0]}</td><td style="color:#276749;">✓ ${r[1]}</td></tr>`).join("");
+}
+
+/* ========== 事件与初始化 ========== */
+$("kA").oninput=$("kB").oninput=$("kC").oninput=()=>recalc();
+$("btnFb").onclick=()=>{const px=+$("fbPx").value||0,mm=+$("fbMm").value||0;if(px>0&&mm>0){S.scale=(mm/1000)/px;S.scaleSrc="用户补充已知尺寸";recalc();toast("比例已建立");}else toast("请填写像素数与毫米数","toast-error");};
+
+buildWard();buildKit();renderTiers();renderRules();renderQuoteList();recalc();

+ 50 - 0
_check_console.js

@@ -0,0 +1,50 @@
+const fs = require('fs');
+const { chromium } = require('playwright-core');
+
+const OUT = 'd:\\AI文件夹\\预算报价\\_console_result.json';
+const EDGE = 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe';
+const URL_ = 'http://127.0.0.1:8931/AI快速报价_Demo2.optimizing.html';
+
+(async () => {
+  const logs = { console: [], pageerror: [], requestfailed: [] };
+  const profileDir = 'd:\\AI文件夹\\预算报价\\.edge-console-' + Date.now();
+  const ctx = await chromium.launchPersistentContext(profileDir, {
+    executablePath: EDGE,
+    headless: true,
+    args: ['--no-sandbox', '--disable-gpu', '--disable-breakpad', '--disable-crash-reporter']
+  });
+  const page = ctx.pages()[0] || await ctx.newPage();
+  page.on('console', msg => {
+    logs.console.push({ type: msg.type(), text: msg.text().slice(0, 500) });
+  });
+  page.on('pageerror', err => {
+    logs.pageerror.push(String(err && err.message || err).slice(0, 500) + ' | ' + String(err && err.stack || '').slice(0, 800));
+  });
+  page.on('requestfailed', req => {
+    logs.requestfailed.push(req.url().slice(0, 200) + ' :: ' + (req.failure() && req.failure().errorText));
+  });
+  page.on('response', res => {
+    if (res.status() >= 400) logs.requestfailed.push('HTTP ' + res.status() + ' ' + res.url().slice(0, 200));
+  });
+  try {
+    await page.goto(URL_, { waitUntil: 'load', timeout: 30000 });
+    await page.waitForTimeout(6000);
+    // 尝试触发一次默认识别相关的初始化函数(若存在)
+    try {
+      await page.evaluate(() => {
+        if (typeof qUpdateRuleDescription === 'function') { try { qUpdateRuleDescription(); } catch(e){} }
+      });
+    } catch (e) {}
+    await page.waitForTimeout(2000);
+    logs.title = await page.title();
+    logs.ok = true;
+  } catch (e) {
+    logs.error = String(e).slice(0, 500);
+  }
+  fs.writeFileSync(OUT, JSON.stringify(logs, null, 2), 'utf8');
+  await ctx.close();
+  process.exit(0);
+})().catch(e => {
+  try { fs.writeFileSync(OUT, JSON.stringify({ fatal: String(e).slice(0, 500) }), 'utf8'); } catch (_) {}
+  process.exit(1);
+});

+ 23 - 0
_check_syntax.js

@@ -0,0 +1,23 @@
+const fs = require('fs');
+const path = 'd:\\AI文件夹\\预算报价\\AI快速报价_Demo2.optimizing.html';
+const html = fs.readFileSync(path, 'utf8');
+const startTag = '<script>';
+const endTag = '</script>';
+const startIdx = html.indexOf(startTag, html.indexOf('</script>') + 9);
+const endIdx = html.lastIndexOf(endTag);
+const js = html.slice(startIdx + startTag.length, endIdx);
+fs.writeFileSync('d:\\AI文件夹\\预算报价\\_inline_script.js', js, 'utf8');
+try {
+  new Function(js);
+  console.log('INLINE_SCRIPT_SYNTAX_OK');
+} catch (e) {
+  console.log('INLINE_SCRIPT_SYNTAX_ERROR: ' + e.message);
+}
+// 检查 vendor
+try {
+  const vendor = fs.readFileSync('d:\\AI文件夹\\预算报价\\assets\\vendor\\exceljs.min.js', 'utf8');
+  new Function(vendor);
+  console.log('VENDOR_SYNTAX_OK');
+} catch (e) {
+  console.log('VENDOR_SYNTAX_ERROR: ' + e.message);
+}

+ 6 - 0
_chk.js

@@ -0,0 +1,6 @@
+const fs=require('fs');
+const html=fs.readFileSync('d:/AI文件夹/预算报价/AI快速报价_Demo2.optimizing.html','utf8');
+const m=html.match(/<script>([\s\S]*?)<\/script>/);
+if(!m){console.log('NO SCRIPT');process.exit(1);}
+const vm=require('vm');
+try{new vm.Script(m[1]);console.log('script parse OK');}catch(e){console.log('PARSE ERROR:',e.message);const mm=e.stack.match(/<anonymous>:(\d+)/);if(mm){const ln=+mm[1];const lines=m[1].split('\n');console.log('LINE',ln,':',lines[ln-1]);console.log('PREV',ln-1,':',lines[ln-2]);console.log('NEXT',ln+1,':',lines[ln]);}process.exit(1);}

+ 16 - 0
_console_result.json

@@ -0,0 +1,16 @@
+{
+  "console": [
+    {
+      "type": "verbose",
+      "text": "[DOM] Password field is not contained in a form: (More info: https://www.chromium.org/developers/design-documents/create-amazing-password-forms) %o"
+    },
+    {
+      "type": "error",
+      "text": "Failed to load resource: the server responded with a status of 404 (File not found)"
+    }
+  ],
+  "pageerror": [],
+  "requestfailed": [],
+  "title": "AI快速报价系统 - Demo",
+  "ok": true
+}

Разница между файлами не показана из-за своего большого размера
+ 11956 - 0
_inline_script.js


Разница между файлами не показана из-за своего большого размера
+ 14450 - 0
_old_recovered.html


BIN
_ref_quote.zip


+ 2 - 0
_ref_quote/[Content_Types].xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="png" ContentType="image/png"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/custom.xml" ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml"/><Override PartName="/xl/cellimages.xml" ContentType="application/vnd.wps-officedocument.cellimage+xml"/><Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>

+ 2 - 0
_ref_quote/_rels/.rels

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" Target="docProps/custom.xml"/></Relationships>

+ 2 - 0
_ref_quote/docProps/app.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>WPS 表格</Application><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>工作表</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="1" baseType="lpstr"><vt:lpstr>全屋柜类报价</vt:lpstr></vt:vector></TitlesOfParts></Properties>

+ 2 - 0
_ref_quote/docProps/core.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:creator>高金慧</dc:creator><cp:lastModifiedBy>Hasee</cp:lastModifiedBy><dcterms:created xsi:type="dcterms:W3CDTF">2023-03-06T07:21:00Z</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">2026-06-18T14:52:53Z</dcterms:modified></cp:coreProperties>

+ 2 - 0
_ref_quote/docProps/custom.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2" name="ICV"><vt:lpwstr>1964BCFB9F60464A9EE96792B3052139_13</vt:lpwstr></property><property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="3" name="KSOProductBuildVer"><vt:lpwstr>2052-12.1.0.26895</vt:lpwstr></property><property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="4" name="CalculationRule"><vt:i4>0</vt:i4></property></Properties>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_ref_quote/xl/_rels/cellimages.xml.rels


+ 2 - 0
_ref_quote/xl/_rels/workbook.xml.rels

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId4" Type="http://www.wps.cn/officeDocument/2020/cellImage" Target="cellimages.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/></Relationships>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_ref_quote/xl/cellimages.xml


BIN
_ref_quote/xl/media/image1.png


BIN
_ref_quote/xl/media/image10.png


BIN
_ref_quote/xl/media/image11.png


BIN
_ref_quote/xl/media/image12.png


BIN
_ref_quote/xl/media/image13.png


BIN
_ref_quote/xl/media/image14.png


BIN
_ref_quote/xl/media/image15.png


BIN
_ref_quote/xl/media/image16.png


BIN
_ref_quote/xl/media/image17.png


BIN
_ref_quote/xl/media/image18.png


BIN
_ref_quote/xl/media/image2.png


BIN
_ref_quote/xl/media/image3.png


BIN
_ref_quote/xl/media/image4.png


BIN
_ref_quote/xl/media/image5.png


BIN
_ref_quote/xl/media/image6.png


BIN
_ref_quote/xl/media/image7.png


BIN
_ref_quote/xl/media/image8.png


BIN
_ref_quote/xl/media/image9.png


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_ref_quote/xl/sharedStrings.xml


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_ref_quote/xl/styles.xml


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_ref_quote/xl/theme/theme1.xml


+ 2 - 0
_ref_quote/xl/workbook.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:dbsheet="http://web.wps.cn/et/2021/dbsheet"><fileVersion appName="xl" lastEdited="3" lowestEdited="5" rupBuild="9302"/><workbookPr/><bookViews><workbookView windowWidth="27945" windowHeight="12960"/></bookViews><sheets><sheet name="全屋柜类报价" sheetId="4" r:id="rId1"/></sheets><calcPr calcId="191029"/><extLst><ext uri="{B58B0392-4F1F-4190-BB64-5DF3571DCE5F}" xmlns:xcalcf="http://schemas.microsoft.com/office/spreadsheetml/2018/calcfeatures"><xcalcf:calcFeatures><xcalcf:feature name="microsoft.com:RD"/><xcalcf:feature name="microsoft.com:Single"/><xcalcf:feature name="microsoft.com:FV"/><xcalcf:feature name="microsoft.com:CNMTM"/><xcalcf:feature name="microsoft.com:LET_WF"/><xcalcf:feature name="microsoft.com:LAMBDA_WF"/><xcalcf:feature name="microsoft.com:ARRAYTEXT_WF"/></xcalcf:calcFeatures></ext></extLst></workbook>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_ref_quote/xl/worksheets/sheet1.xml


+ 111 - 0
_refdump.txt

@@ -0,0 +1,111 @@
+1 | A1[s5]=0 ; B1[s5]= ; C1[s5]= ; D1[s5]= ; E1[s5]= ; F1[s6]= ; G1[s5]= ; H1[s5]= ; I1[s5]= ; J1[s5]=
+2 | A2[s7]=1 ; B2[s7]=2 ; C2[s7]= ; D2[s7]=3 ; E2[s7]=4 ; F2[s8]=5 ; G2[s7]=6 ; H2[s7]=7 ; I2[s7]=8 ; J2[s7]=
+3 | A3[s9]=1 ; B3[s10]=9 ; C3[s9]=10 ; D3[s11]=0.79 ; E3[s11]=2.5 ; F3[s12]=1.975 ; G3[s13]=1852 ; H3[s14]=3657.7 ; I3[s11]=11 ; J3[s15]==DISPIMG(&quot;ID_E4EA15CFCB02496A9DC75348D7B35697&quot;,1)
+4 | A4[s9]= ; B4[s10]= ; C4[s9]=12 ; D4[s11]=0.79 ; E4[s16]=2.5 ; F4[s12]=1.975 ; G4[s17]=538 ; H4[s14]=1062.55 ; I4[s11]=13 ; J4[s18]=
+5 | A5[s9]= ; B5[s10]= ; C5[s19]=14 ; D5[s20]= ; E5[s20]=1.58 ; F5[s20]= ; G5[s20]=386 ; H5[s21]=609.88 ; I5[s20]=15 ; J5[s18]=
+6 | A6[s9]= ; B6[s10]= ; C6[s19]=16 ; D6[s20]= ; E6[s20]=1 ; F6[s20]= ; G6[s20]=536 ; H6[s21]=536 ; I6[s20]=17 ; J6[s18]=
+7 | A7[s19]= ; B7[s22]= ; C7[s19]=18 ; D7[s23]= ; E7[s17]=4 ; F7[s24]= ; G7[s17]=115 ; H7[s21]=460 ; I7[s20]=19 ; J7[s18]= ; K7[s3]= ; L7[s3]=
+8 | A8[s19]= ; B8[s22]= ; C8[s19]=20 ; D8[s17]=0.54 ; E8[s11]=2.5 ; F8[s24]=1.35 ; G8[s17]=1138 ; H8[s21]=1536.3 ; I8[s20]=21 ; J8[s18]= ; K8[s3]= ; L8[s3]=
+9 | A9[s25]= ; B9[s25]= ; C9[s19]=22 ; D9[s23]= ; E9[s17]=12 ; F9[s24]= ; G9[s17]=40 ; H9[s21]=480 ; I9[s20]=23 ; J9[s26]==DISPIMG(&quot;ID_501377114C6844EDA02CD3057E500A3C&quot;,1)
+10 | A10[s19]= ; B10[s22]= ; C10[s27]=24 ; D10[s27]= ; E10[s27]= ; F10[s28]= ; G10[s27]= ; H10[s29]=8342.43 ; I10[s19]=25 ; J10[s30]=
+11 | A11[s19]= ; B11[s22]= ; C11[s31]=26 ; D11[s31]= ; E11[s31]= ; F11[s32]= ; G11[s31]= ; H11[s33]=7508.187 ; I11[s34]=25 ; J11[s35]=
+12 | A12[s7]=1 ; B12[s36]=2 ; C12[s37]= ; D12[s7]=3 ; E12[s7]=4 ; F12[s8]=5 ; G12[s7]=6 ; H12[s7]=7 ; I12[s7]=8 ; J12[s7]=
+13 | A13[s25]=2 ; B13[s22]=27 ; C13[s9]=10 ; D13[s11]=4.85 ; E13[s11]=2.5 ; F13[s12]=12.125 ; G13[s13]=1852 ; H13[s14]=22455.5 ; I13[s11]=11 ; J13[s38]==DISPIMG(&quot;ID_AC9C657C8C414522BC92BF4AF10ED043&quot;,1)
+14 | A14[s25]= ; B14[s22]= ; C14[s9]=12 ; D14[s11]=4.85 ; E14[s16]=2.5 ; F14[s12]=12.125 ; G14[s17]=538 ; H14[s14]=6523.25 ; I14[s11]=13 ; J14[s39]=
+15 | A15[s25]= ; B15[s22]= ; C15[s19]=18 ; D15[s23]= ; E15[s17]=19 ; F15[s24]= ; G15[s17]=115 ; H15[s21]=2185 ; I15[s20]=19 ; J15[s39]=
+16 | A16[s25]= ; B16[s22]= ; C16[s19]=20 ; D16[s17]=0.35 ; E16[s11]=2.5 ; F16[s24]=0.875 ; G16[s17]=1138 ; H16[s21]=1991.5 ; I16[s20]=21 ; J16[s39]=
+17 | A17[s25]= ; B17[s22]= ; C17[s19]=22 ; D17[s23]= ; E17[s17]=55 ; F17[s24]= ; G17[s17]=40 ; H17[s21]=2200 ; I17[s20]=23 ; J17[s40]==DISPIMG(&quot;ID_3133123047CC44E19D73D3C67FC275D5&quot;,1)
+18 | A18[s25]= ; B18[s22]= ; C18[s27]=24 ; D18[s27]= ; E18[s27]= ; F18[s28]= ; G18[s27]= ; H18[s29]=35355.25 ; I18[s19]=25 ; J18[s40]=
+19 | A19[s9]= ; B19[s22]= ; C19[s31]=26 ; D19[s31]= ; E19[s31]= ; F19[s32]= ; G19[s31]= ; H19[s33]=31819.725 ; I19[s34]=25 ; J19[s41]=
+20 | A20[s7]=1 ; B20[s7]=2 ; C20[s7]= ; D20[s7]=3 ; E20[s7]=4 ; F20[s8]=5 ; G20[s7]=6 ; H20[s7]=7 ; I20[s7]=8 ; J20[s7]=
+21 | A21[s9]=3 ; B21[s10]=28 ; C21[s9]=10 ; D21[s11]=1.215 ; E21[s11]=2.5 ; F21[s12]=3.0375 ; G21[s13]=1852 ; H21[s14]=5625.45 ; I21[s11]=11 ; J21[s15]==DISPIMG(&quot;ID_FA33022ED481467AB913BAE3EE707A66&quot;,1)
+22 | A22[s9]= ; B22[s10]= ; C22[s9]=12 ; D22[s11]=1.215 ; E22[s16]=2.5 ; F22[s12]=3.0375 ; G22[s17]=538 ; H22[s14]=1634.175 ; I22[s11]=13 ; J22[s18]=
+23 | A23[s9]= ; B23[s10]= ; C23[s19]=18 ; D23[s17]= ; E23[s17]=2 ; F23[s24]= ; G23[s17]=115 ; H23[s21]=230 ; I23[s20]=19 ; J23[s18]=
+24 | A24[s9]= ; B24[s10]= ; C24[s19]=29 ; D24[s17]=0.28 ; E24[s16]=2.5 ; F24[s12]=0.7 ; G24[s13]=1852 ; H24[s14]=1296.4 ; I24[s11]=21 ; J24[s18]=
+25 | A25[s19]= ; B25[s22]= ; C25[s19]=20 ; D25[s17]=0.6 ; E25[s11]=2.5 ; F25[s24]=1.5 ; G25[s17]=1138 ; H25[s21]=3414 ; I25[s20]=21 ; J25[s18]=
+26 | A26[s25]= ; B26[s25]= ; C26[s19]=22 ; D26[s23]= ; E26[s17]=7 ; F26[s24]= ; G26[s17]=40 ; H26[s21]=280 ; I26[s20]=23 ; J26[s26]==DISPIMG(&quot;ID_18E2A164CD14464AB806E414E213D621&quot;,1)
+27 | A27[s19]= ; B27[s22]= ; C27[s27]=24 ; D27[s27]= ; E27[s27]= ; F27[s28]= ; G27[s27]= ; H27[s29]=12480.025 ; I27[s19]=25 ; J27[s30]=
+28 | A28[s19]= ; B28[s22]= ; C28[s31]=26 ; D28[s31]= ; E28[s31]= ; F28[s32]= ; G28[s31]= ; H28[s33]=11232.0225 ; I28[s34]=25 ; J28[s35]=
+29 | A29[s7]=1 ; B29[s7]=2 ; C29[s7]= ; D29[s7]=3 ; E29[s7]=4 ; F29[s8]=5 ; G29[s7]=6 ; H29[s7]=7 ; I29[s7]=8 ; J29[s7]=
+30 | A30[s9]=4 ; B30[s10]=30 ; C30[s9]=10 ; D30[s11]=0.89 ; E30[s11]=2.5 ; F30[s12]=2.225 ; G30[s13]=1852 ; H30[s14]=4120.7 ; I30[s11]=11 ; J30[s15]==DISPIMG(&quot;ID_2BE1A910939B44728CC4CAF1CF126F50&quot;,1)
+31 | A31[s9]= ; B31[s10]= ; C31[s9]=31 ; D31[s11]=0.89 ; E31[s16]=2.5 ; F31[s12]=2.225 ; G31[s13]=1852 ; H31[s14]=4120.7 ; I31[s11]=13 ; J31[s18]=
+32 | A32[s9]= ; B32[s10]= ; C32[s19]=14 ; D32[s20]= ; E32[s20]=5.34 ; F32[s20]= ; G32[s20]=386 ; H32[s21]=2061.24 ; I32[s20]=15 ; J32[s18]=
+33 | A33[s9]= ; B33[s10]= ; C33[s19]=16 ; D33[s20]= ; E33[s20]=1 ; F33[s20]= ; G33[s20]=536 ; H33[s21]=536 ; I33[s20]=17 ; J33[s18]=
+34 | A34[s19]= ; B34[s22]= ; C34[s19]=32 ; D34[s17]= ; E34[s17]=2 ; F34[s24]= ; G34[s17]=226 ; H34[s21]=452 ; I34[s20]=33 ; J34[s18]=
+35 | A35[s19]= ; B35[s22]= ; C35[s19]=20 ; D35[s17]=0.3 ; E35[s11]=2.5 ; F35[s24]=0.75 ; G35[s17]=1138 ; H35[s21]=1707 ; I35[s20]=21 ; J35[s18]=
+36 | A36[s25]= ; B36[s25]= ; C36[s19]=22 ; D36[s23]= ; E36[s17]=15 ; F36[s24]= ; G36[s17]=40 ; H36[s21]=600 ; I36[s20]=23 ; J36[s26]==DISPIMG(&quot;ID_103743CCB36E475F8775D3808699B502&quot;,1)
+37 | A37[s42]= ; B37[s42]= ; C37[s9]=10 ; D37[s11]=0.45 ; E37[s11]=2.5 ; F37[s12]=1.125 ; G37[s13]=1852 ; H37[s14]=2083.5 ; I37[s11]=11 ; J37[s43]=
+38 | A38[s42]= ; B38[s44]= ; C38[s9]=31 ; D38[s11]=0.45 ; E38[s16]=2.5 ; F38[s12]=1.125 ; G38[s13]=1852 ; H38[s14]=2083.5 ; I38[s11]=13 ; J38[s43]=
+39 | A39[s42]= ; B39[s44]= ; C39[s19]=18 ; D39[s17]= ; E39[s17]=2 ; F39[s24]= ; G39[s17]=115 ; H39[s21]=230 ; I39[s20]=19 ; J39[s43]=
+40 | A40[s19]= ; B40[s22]= ; C40[s27]=24 ; D40[s27]= ; E40[s27]= ; F40[s28]= ; G40[s27]= ; H40[s29]=17994.64 ; I40[s19]=25 ; J40[s30]=
+41 | A41[s19]= ; B41[s22]= ; C41[s31]=26 ; D41[s31]= ; E41[s31]= ; F41[s32]= ; G41[s31]= ; H41[s33]=16195.176 ; I41[s34]=25 ; J41[s35]=
+42 | A42[s7]=1 ; B42[s36]=2 ; C42[s37]= ; D42[s7]=3 ; E42[s7]=4 ; F42[s8]=5 ; G42[s7]=6 ; H42[s7]=7 ; I42[s7]=8 ; J42[s7]=
+43 | A43[s25]=5 ; B43[s22]=34 ; C43[s9]=10 ; D43[s17]=1.5 ; E43[s17]=1 ; F43[s24]=1.5 ; G43[s17]=1852 ; H43[s21]=2778 ; I43[s11]=11 ; J43[s26]==DISPIMG(&quot;ID_73636484E08A4CE7A0E4D74827C86BEF&quot;,1)
+44 | A44[s25]= ; B44[s22]= ; C44[s9]=12 ; D44[s17]=1.5 ; E44[s17]=2.5 ; F44[s24]=3.75 ; G44[s17]=538 ; H44[s21]=2017.5 ; I44[s11]=13 ; J44[s30]=
+45 | A45[s25]= ; B45[s22]= ; C45[s19]=18 ; D45[s17]= ; E45[s17]=3 ; F45[s24]= ; G45[s17]=115 ; H45[s21]=345 ; I45[s20]=19 ; J45[s30]=
+46 | A46[s25]= ; B46[s22]= ; C46[s19]=20 ; D46[s17]=0.6 ; E46[s17]=1 ; F46[s24]=0.6 ; G46[s17]=1138 ; H46[s21]=1365.6 ; I46[s20]=21 ; J46[s45]==DISPIMG(&quot;ID_9BB61A6811834602A2DE7894BBB1C112&quot;,1)
+47 | A47[s25]= ; B47[s22]= ; C47[s19]=22 ; D47[s23]= ; E47[s17]=9 ; F47[s24]= ; G47[s17]=40 ; H47[s21]=360 ; I47[s20]=23 ; J47[s45]=
+48 | A48[s25]= ; B48[s22]= ; C48[s46]=24 ; D48[s27]= ; E48[s27]= ; F48[s28]= ; G48[s27]= ; H48[s29]=6866.1 ; I48[s19]=25 ; J48[s45]=
+49 | A49[s9]= ; B49[s22]= ; C49[s47]=26 ; D49[s31]= ; E49[s31]= ; F49[s32]= ; G49[s31]= ; H49[s33]=6179.49 ; I49[s34]=25 ; J49[s48]=
+50 | A50[s7]=1 ; B50[s7]=2 ; C50[s7]= ; D50[s7]=3 ; E50[s7]=4 ; F50[s8]=5 ; G50[s7]=6 ; H50[s7]=7 ; I50[s7]=8 ; J50[s7]=
+51 | A51[s9]=6 ; B51[s10]=35 ; C51[s9]=36 ; D51[s11]=2 ; E51[s11]=0.9 ; F51[s12]=1.8 ; G51[s13]=1852 ; H51[s14]=3333.6 ; I51[s11]=11 ; J51[s15]==DISPIMG(&quot;ID_9220E21280E541689F5257B7EE0631BF&quot;,1)
+52 | A52[s9]= ; B52[s10]= ; C52[s9]=12 ; D52[s11]=2 ; E52[s11]=0.9 ; F52[s24]=1.8 ; G52[s17]=538 ; H52[s21]=968.4 ; I52[s11]=13 ; J52[s49]=
+53 | A53[s9]= ; B53[s10]= ; C53[s50]=37 ; D53[s11]=2 ; E53[s11]=0.9 ; F53[s12]=1.8 ; G53[s13]=1852 ; H53[s14]=3333.6 ; I53[s11]=11 ; J53[s49]=
+54 | A54[s9]= ; B54[s10]= ; C54[s9]=12 ; D54[s11]=2 ; E54[s11]=0.9 ; F54[s24]=1.8 ; G54[s17]=538 ; H54[s21]=968.4 ; I54[s11]=13 ; J54[s49]=
+55 | A55[s9]= ; B55[s10]= ; C55[s19]=38 ; D55[s23]= ; E55[s17]=3 ; F55[s24]= ; G55[s17]=1300 ; H55[s21]=3900 ; I55[s20]=39 ; J55[s18]=
+56 | A56[s9]= ; B56[s10]= ; C56[s19]=32 ; D56[s17]= ; E56[s17]=1 ; F56[s24]= ; G56[s17]=226 ; H56[s21]=226 ; I56[s20]=33 ; J56[s18]=
+57 | A57[s9]= ; B57[s10]= ; C57[s19]=18 ; D57[s17]= ; E57[s17]=5 ; F57[s24]= ; G57[s17]=115 ; H57[s21]=575 ; I57[s20]=19 ; J57[s18]=
+58 | A58[s9]= ; B58[s10]= ; C58[s19]=33 ; D58[s17]= ; E58[s17]=3.6 ; F58[s24]= ; G58[s17]=225 ; H58[s21]=810 ; I58[s20]=40 ; J58[s18]=
+59 | A59[s9]= ; B59[s10]= ; C59[s19]=14 ; D59[s20]= ; E59[s20]=3.5 ; F59[s20]= ; G59[s20]=386 ; H59[s21]=1351 ; I59[s20]=15 ; J59[s30]==DISPIMG(&quot;ID_9D42EE889FCE4FB3AE75C719BCFEF642&quot;,1)
+60 | A60[s9]= ; B60[s10]= ; C60[s19]=16 ; D60[s20]= ; E60[s20]=1 ; F60[s20]= ; G60[s20]=536 ; H60[s21]=536 ; I60[s20]=17 ; J60[s30]=
+61 | A61[s19]= ; B61[s22]= ; C61[s19]=20 ; D61[s17]=0.45 ; E61[s17]=1 ; F61[s24]=0.45 ; G61[s17]=1138 ; H61[s21]=2048.4 ; I61[s20]=21 ; J61[s30]=
+62 | A62[s19]= ; B62[s22]= ; C62[s19]=22 ; D62[s23]= ; E62[s17]=24 ; F62[s24]= ; G62[s17]=40 ; H62[s21]=960 ; I62[s20]=23 ; J62[s30]=
+63 | A63[s19]= ; B63[s22]= ; C63[s27]=24 ; D63[s27]= ; E63[s27]= ; F63[s28]= ; G63[s27]= ; H63[s29]=19010.4 ; I63[s19]=25 ; J63[s30]=
+64 | A64[s19]= ; B64[s22]= ; C64[s31]=26 ; D64[s31]= ; E64[s31]= ; F64[s32]= ; G64[s31]= ; H64[s33]=17109.36 ; I64[s34]=25 ; J64[s35]=
+65 | A65[s7]=1 ; B65[s36]=2 ; C65[s37]= ; D65[s7]=3 ; E65[s7]=4 ; F65[s8]=5 ; G65[s7]=6 ; H65[s7]=7 ; I65[s7]=8 ; J65[s7]=
+66 | A66[s25]=7 ; B66[s22]=41 ; C66[s9]=10 ; D66[s17]=3.74 ; E66[s17]=2.5 ; F66[s24]=9.35 ; G66[s17]=1852 ; H66[s21]=17316.2 ; I66[s11]=11 ; J66[s51]==DISPIMG(&quot;ID_2AC174C5DC294067B4EF25C14160211A&quot;,1)
+67 | A67[s25]= ; B67[s22]= ; C67[s9]=12 ; D67[s17]=3.74 ; E67[s17]=2.5 ; F67[s24]=9.35 ; G67[s17]=538 ; H67[s21]=5030.3 ; I67[s11]=13 ; J67[s52]=
+68 | A68[s25]= ; B68[s22]= ; C68[s9]=42 ; D68[s17]= ; E68[s17]=6 ; F68[s24]= ; G68[s17]=288 ; H68[s21]=1728 ; I68[s53]=43 ; J68[s52]=
+69 | A69[s25]= ; B69[s22]= ; C69[s9]=33 ; D69[s17]= ; E69[s17]=8 ; F69[s24]= ; G69[s17]=680 ; H69[s21]=5440 ; I69[s20]=33 ; J69[s52]=
+70 | A70[s25]= ; B70[s22]= ; C70[s19]=38 ; D70[s23]= ; E70[s17]=3 ; F70[s24]= ; G70[s17]=1300 ; H70[s21]=3900 ; I70[s20]=39 ; J70[s52]==DISPIMG(&quot;ID_67D9B0FCFD6744A6816C254F6792C19D&quot;,1)
+71 | A71[s25]= ; B71[s22]= ; C71[s9]=33 ; D71[s17]= ; E71[s17]=2.4 ; F71[s24]= ; G71[s17]=225 ; H71[s21]=540 ; I71[s20]=33 ; J71[s52]=
+72 | A72[s25]= ; B72[s22]= ; C72[s9]=20 ; D72[s17]=0.6 ; E72[s17]=2.5 ; F72[s24]=1.5 ; G72[s17]=1138 ; H72[s21]=3414 ; I72[s20]=21 ; J72[s52]=
+73 | A73[s25]= ; B73[s22]= ; C73[s19]=22 ; D73[s23]= ; E73[s17]=40 ; F73[s24]= ; G73[s17]=40 ; H73[s21]=1600 ; I73[s20]=23 ; J73[s52]=
+74 | A74[s25]= ; B74[s22]= ; C74[s46]=24 ; D74[s27]= ; E74[s27]= ; F74[s28]= ; G74[s27]= ; H74[s29]=38968.5 ; I74[s19]=25 ; J74[s52]=
+75 | A75[s9]= ; B75[s22]= ; C75[s47]=26 ; D75[s31]= ; E75[s31]= ; F75[s32]= ; G75[s31]= ; H75[s33]=35071.65 ; I75[s34]=25 ; J75[s54]=
+76 | A76[s7]=1 ; B76[s36]=2 ; C76[s37]= ; D76[s7]=3 ; E76[s7]=4 ; F76[s8]=5 ; G76[s7]=6 ; H76[s7]=7 ; I76[s7]=8 ; J76[s7]=
+77 | A77[s25]=8 ; B77[s55]=44 ; C77[s9]=10 ; D77[s11]=2.28 ; E77[s11]=1.8 ; F77[s12]=4.104 ; G77[s13]=2796 ; H77[s14]=11474.784 ; I77[s11]=45 ; J77[s26]==DISPIMG(&quot;ID_7EEDBBF788294959AADAF66EB33F5C6A&quot;,1)
+78 | A78[s9]= ; B78[s10]= ; C78[s9]=12 ; D78[s11]=2.28 ; E78[s11]=1.8 ; F78[s12]=4.104 ; G78[s17]=1380 ; H78[s14]=5663.52 ; I78[s11]=46 ; J78[s30]=
+79 | A79[s25]= ; B79[s10]= ; C79[s9]=33 ; D79[s17]= ; E79[s17]=5 ; F79[s24]= ; G79[s17]=680 ; H79[s21]=3400 ; I79[s20]=33 ; J79[s30]=
+80 | A80[s25]= ; B80[s10]= ; C80[s19]=38 ; D80[s23]= ; E80[s17]=2 ; F80[s24]= ; G80[s17]=1300 ; H80[s21]=2600 ; I80[s20]=39 ; J80[s30]=
+81 | A81[s25]= ; B81[s10]= ; C81[s9]=42 ; D81[s17]= ; E81[s17]=4 ; F81[s24]= ; G81[s17]=288 ; H81[s21]=1152 ; I81[s53]=43 ; J81[s30]=
+82 | A82[s25]= ; B82[s10]= ; C82[s19]=18 ; D82[s23]= ; E82[s17]=2 ; F82[s24]= ; G82[s17]=115 ; H82[s21]=230 ; I82[s20]=19 ; J82[s56]==DISPIMG(&quot;ID_1C9497FAEE644C4ABE019DCF4F82653B&quot;,1)
+83 | A83[s25]= ; B83[s10]= ; C83[s19]=47 ; D83[s23]= ; E83[s17]=1.8 ; F83[s24]= ; G83[s17]=1560 ; H83[s21]=2808 ; I83[s53]=43 ; J83[s56]=
+84 | A84[s25]= ; B84[s10]= ; C84[s19]=22 ; D84[s23]= ; E84[s17]=31 ; F84[s24]= ; G84[s17]=40 ; H84[s21]=1240 ; I84[s20]=23 ; J84[s56]=
+85 | A85[s25]= ; B85[s55]= ; C85[s57]=24 ; D85[s58]= ; E85[s58]= ; F85[s59]= ; G85[s46]= ; H85[s29]=28568.304 ; I85[s19]=25 ; J85[s56]=
+86 | A86[s9]= ; B86[s10]= ; C86[s31]=26 ; D86[s31]= ; E86[s31]= ; F86[s32]= ; G86[s31]= ; H86[s33]=25711.4736 ; I86[s34]=25 ; J86[s11]=
+87 | A87[s7]=1 ; B87[s7]=2 ; C87[s7]= ; D87[s60]=3 ; E87[s60]=48 ; F87[s60]=49 ; G87[s60]=6 ; H87[s60]=7 ; I87[s60]=8 ; J87[s60]=
+88 | A88[s61]=9 ; B88[s62]=50 ; C88[s63]=51 ; D88[s11]=4.18 ; E88[s11]=3 ; F88[s11]=1.18 ; G88[s11]=2380 ; H88[s14]=2808.4 ; I88[s11]=25 ; J88[s64]==DISPIMG(&quot;ID_48125B3195674FE29FC05A6DB93A4D86&quot;,1)
+89 | A89[s65]= ; B89[s66]= ; C89[s63]=52 ; D89[s11]=3.21 ; E89[s11]=1 ; F89[s11]=2.21 ; G89[s11]=1748 ; H89[s14]=3863.08 ; I89[s11]=25 ; J89[s67]=
+90 | A90[s65]= ; B90[s66]= ; C90[s63]=53 ; D90[s11]= ; E90[s11]=0.97 ; F90[s11]= ; G90[s11]=2622 ; H90[s14]=2543.34 ; I90[s11]=25 ; J90[s67]=
+91 | A91[s65]= ; B91[s66]= ; C91[s63]=54 ; D91[s11]= ; E91[s11]=0.88 ; F91[s11]= ; G91[s11]=5950 ; H91[s14]=5236 ; I91[s11]=25 ; J91[s67]=
+92 | A92[s65]= ; B92[s66]= ; C92[s63]=55 ; D92[s11]=0.6 ; E92[s11]=2.5 ; F92[s11]=1.5 ; G92[s11]=1138 ; H92[s14]=5121 ; I92[s11]=25 ; J92[s67]=
+93 | A93[s65]= ; B93[s66]= ; C93[s63]=56 ; D93[s11]=3.21 ; E93[s11]=3 ; F93[s11]= ; G93[s11]=1480 ; H93[s14]=4440 ; I93[s11]=25 ; J93[s67]=
+94 | A94[s65]= ; B94[s66]= ; C94[s63]=56 ; D94[s11]= ; E94[s11]=0.21 ; F94[s11]= ; G94[s11]=2480 ; H94[s14]=520.8 ; I94[s11]= ; J94[s67]=
+95 | A95[s65]= ; B95[s66]= ; C95[s63]=57 ; D95[s11]= ; E95[s11]= ; F95[s11]=1 ; G95[s11]=1268 ; H95[s14]=1268 ; I95[s11]=25 ; J95[s67]==DISPIMG(&quot;ID_B7BA95BA2F404CCD84DF8911867DBD47&quot;,1)
+96 | A96[s65]= ; B96[s66]= ; C96[s63]=58 ; D96[s11]= ; E96[s11]= ; F96[s11]=1 ; G96[s11]=1628 ; H96[s14]=1628 ; I96[s11]=25 ; J96[s67]=
+97 | A97[s65]= ; B97[s66]= ; C97[s63]=59 ; D97[s11]= ; E97[s11]= ; F97[s11]=4 ; G97[s11]=115 ; H97[s14]=460 ; I97[s11]=25 ; J97[s67]=
+98 | A98[s65]= ; B98[s55]= ; C98[s63]=60 ; D98[s11]= ; E98[s11]= ; F98[s11]=1 ; G98[s11]=500 ; H98[s14]=500 ; I98[s11]=25 ; J98[s67]=
+99 | A99[s65]= ; B99[s55]= ; C99[s63]=61 ; D99[s11]= ; E99[s11]= ; F99[s11]=1 ; G99[s11]=500 ; H99[s14]=500 ; I99[s11]=25 ; J99[s67]=
+100 | A100[s65]= ; B100[s22]= ; C100[s63]=62 ; D100[s11]= ; E100[s11]= ; F100[s11]=1 ; G100[s11]=500 ; H100[s14]=500 ; I100[s11]=25 ; J100[s67]=
+101 | A101[s65]= ; B101[s66]= ; C101[s68]=24 ; D101[s68]= ; E101[s68]= ; F101[s68]= ; G101[s68]= ; H101[s69]=29388.62 ; I101[s70]=25 ; J101[s67]=
+102 | A102[s65]= ; B102[s66]= ; C102[s71]=63 ; D102[s71]= ; E102[s71]= ; F102[s71]= ; G102[s71]= ; H102[s72]=26449.758 ; I102[s73]= ; J102[s67]=
+103 | A103[s65]= ; B103[s74]= ; C103[s71]=64 ; D103[s71]= ; E103[s71]= ; F103[s71]= ; G103[s71]= ; H103[s75]=11199 ; I103[s76]= ; J103[s77]=
+104 | A104[s78]= ; B104[s79]= ; C104[s71]=65 ; D104[s71]= ; E104[s71]= ; F104[s71]= ; G104[s71]= ; H104[s33]=37648.758 ; I104[s34]= ; J104[s53]=
+105 | A105[s80]=66 ; B105[s80]= ; C105[s80]= ; D105[s80]= ; E105[s80]= ; F105[s81]= ; G105[s80]= ; H105[s80]= ; I105[s82]=196974.269 ; J105[s82]=
+106 | A106[s83]=67 ; B106[s84]= ; C106[s84]= ; D106[s84]= ; E106[s84]= ; F106[s85]= ; G106[s84]= ; H106[s86]= ; I106[s87]=188475.8421 ; J106[s87]=
+107 | A107[s88]=68 ; B107[s88]= ; C107[s88]= ; D107[s88]= ; E107[s88]= ; F107[s89]= ; G107[s88]= ; H107[s88]= ; I107[s88]= ; J107[s88]=
+108 | A108[s90]=69 ; B108[s90]= ; C108[s90]= ; D108[s90]= ; E108[s90]= ; F108[s91]= ; G108[s90]= ; H108[s90]= ; I108[s90]= ; J108[s90]=
+109 | A109[s90]=70 ; B109[s90]= ; C109[s90]= ; D109[s90]= ; E109[s90]= ; F109[s91]= ; G109[s90]= ; H109[s90]= ; I109[s90]= ; J109[s90]=
+110 | A110[s90]=71 ; B110[s90]= ; C110[s90]= ; D110[s90]= ; E110[s90]= ; F110[s91]= ; G110[s90]= ; H110[s90]= ; I110[s90]= ; J110[s90]=
+MERGES: ['A1:J1', 'B2:C2', 'I2:J2', 'C10:G10', 'C11:G11', 'B12:C12', 'I12:J12', 'C18:G18', 'C19:G19', 'B20:C20', 'I20:J20', 'C27:G27', 'C28:G28', 'B29:C29', 'I29:J29', 'C40:G40', 'C41:G41', 'B42:C42', 'I42:J42', 'C48:G48', 'C49:G49', 'B50:C50', 'I50:J50', 'C63:G63', 'C64:G64', 'B65:C65', 'I65:J65', 'C74:G74', 'C75:G75', 'B76:C76', 'I76:J76', 'C85:G85', 'C86:G86', 'B87:C87', 'I87:J87', 'C101:G101', 'C102:G102', 'C103:G103', 'C104:G104', 'A105:H105', 'I105:J105', 'A106:H106', 'I106:J106', 'A107:J107', 'A108:J108', 'A109:J109', 'A110:J110', 'A3:A11', 'A13:A19', 'A21:A28', 'A30:A41', 'A43:A49', 'A51:A64', 'A66:A75', 'A77:A86', 'A88:A104', 'B3:B11', 'B13:B19', 'B21:B28', 'B30:B41', 'B43:B49', 'B51:B64', 'B66:B75', 'B77:B86', 'B88:B102', 'J3:J8', 'J9:J11', 'J13:J15', 'J17:J19', 'J21:J25', 'J26:J28', 'J30:J35', 'J36:J41', 'J43:J45', 'J46:J49', 'J51:J57', 'J59:J64', 'J66:J69', 'J70:J75', 'J77:J81', 'J82:J86', 'J88:J93', 'J95:J102']

+ 106 - 0
_server.py

@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+# 静态文件服务:对 Trae 预览注入的 /@vite/* 热更新脚本返回 200 空JS,
+# 避免 404 导致预览面板中断主文档加载(net::ERR_ABORTED)
+import http.server
+import socketserver
+import os
+
+os.chdir(os.path.dirname(os.path.abspath(__file__)))
+
+
+class H(http.server.SimpleHTTPRequestHandler):
+    # HTTP/1.1 keep-alive:避免 HTTP/1.0 每请求关连接,预览面板复用连接时遇到 RST 而 ERR_ABORTED
+    protocol_version = "HTTP/1.1"
+
+    def _vite(self):
+        self.send_response(200)
+        self.send_header("Content-Type", "text/javascript")
+        self.send_header("Cache-Control", "no-store")
+        self.send_header("Content-Length", "2")
+        self.end_headers()
+        self.wfile.write(b"//")
+
+    def _strip_cond(self):
+        # 剥离条件请求头 → 永远 200 全量正文:
+        # 预览面板需改写 HTML 正文注入热更新脚本,304 无正文会使其放弃本次加载并重发(net::ERR_ABORTED)
+        for h in ("If-Modified-Since", "If-None-Match"):
+            if h in self.headers:
+                del self.headers[h]
+
+    def send_header(self, keyword, value):
+        # 注意:Python 3.14 的 send_head 用的是 "Content-type"(小写t),比较必须忽略大小写
+        if keyword.lower() == "content-type" and "text/html" in value:
+            super().send_header("Cache-Control", "no-store")
+        super().send_header(keyword, value)
+
+    PAGE = "/AI%E5%BF%AB%E9%80%9F%E6%8A%A5%E4%BB%B7_Demo2.optimizing.html"
+
+    def do_GET(self):
+        # 根路径/短别名 → 同一页面:预览面板用最短的纯ASCII URL,避免长百分号编码URL
+        q = self.path.split("?", 1)[0]
+        if q in ("/", "/index.html", "/index"):
+            self.path = self.PAGE + ("?" + self.path.split("?", 1)[1] if "?" in self.path else "")
+        if self.path.startswith("/@vite/"):
+            self._vite()
+        elif self.path.startswith("/ref_parts"):
+            # 读取参考报价表的 styles.xml / theme.xml(base64),供前端导出时原样套用
+            import json, base64, zipfile
+            try:
+                rp = os.path.join(os.path.join(os.path.expanduser("~"), "Desktop"), "6.18号欧阳路预估报价.xlsx")
+                z = zipfile.ZipFile(rp)
+                theme = z.read("xl/theme/theme1.xml") if "xl/theme/theme1.xml" in z.namelist() else b""
+                resp = json.dumps({"styles": base64.b64encode(z.read("xl/styles.xml")).decode(),
+                                   "theme": base64.b64encode(theme).decode()}).encode()
+                self.send_response(200)
+            except Exception as e:
+                resp = json.dumps({"err": str(e)}).encode()
+                self.send_response(500)
+            self.send_header("Content-Type", "application/json")
+            self.send_header("Cache-Control", "no-store")
+            self.send_header("Content-Length", str(len(resp)))
+            self.end_headers()
+            self.wfile.write(resp)
+        else:
+            self._strip_cond()
+            super().do_GET()
+
+    def do_HEAD(self):
+        if self.path.startswith("/@vite/"):
+            self._vite()
+        else:
+            self._strip_cond()
+            super().do_HEAD()
+
+    def do_POST(self):
+        # 导出Excel:前端生成xlsx字节(base64) → 服务端写入桌面
+        if self.path == "/save_xlsx":
+            import json, base64
+            try:
+                ln = int(self.headers.get("Content-Length", 0))
+                d = json.loads(self.rfile.read(ln).decode("utf-8"))
+                name = os.path.basename(d["name"])
+                data = base64.b64decode(d["data"])
+                path = os.path.join(os.path.join(os.path.expanduser("~"), "Desktop"), name)
+                with open(path, "wb") as f:
+                    f.write(data)
+                resp = json.dumps({"ok": True, "path": path}, ensure_ascii=False).encode("utf-8")
+                self.send_response(200)
+            except Exception as e:
+                resp = json.dumps({"ok": False, "err": str(e)}, ensure_ascii=False).encode("utf-8")
+                self.send_response(500)
+            self.send_header("Content-Type", "application/json")
+            self.send_header("Cache-Control", "no-store")
+            self.send_header("Content-Length", str(len(resp)))
+            self.end_headers()
+            self.wfile.write(resp)
+        else:
+            self.send_response(404)
+            # HTTP/1.1 keep-alive 下必须给出 Content-Length,否则客户端会挂起等待正文
+            self.send_header("Content-Length", "0")
+            self.end_headers()
+
+
+socketserver.ThreadingTCPServer.allow_reuse_address = True
+s = socketserver.ThreadingTCPServer(("0.0.0.0", 8002), H)
+print("server ready on 8002")
+s.serve_forever()

+ 35 - 0
_xchk.py

@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# 临时校验脚本:检查参考表样式索引覆盖 + 校验导出的xlsx
+import zipfile, re, os, sys, json
+
+rp = os.path.join(os.path.expanduser("~"), "Desktop", "6.18号欧阳路预估报价.xlsx")
+z = zipfile.ZipFile(rp)
+s = z.read("xl/styles.xml").decode("utf-8")
+m = re.search(r'<cellXfs count="(\d+)">', s)
+print("ref cellXfs count:", m.group(1) if m else "N/A")
+print("ref has theme1:", "xl/theme/theme1.xml" in z.namelist())
+print("ref names:", z.namelist())
+
+# 若提供了导出文件路径,则校验其结构
+if len(sys.argv) > 1:
+    p = sys.argv[1]
+    zz = zipfile.ZipFile(p)
+    bad = zz.testzip()
+    print("zip testzip:", "OK" if bad is None else ("BAD:" + str(bad)))
+    print("entries:", zz.namelist())
+    import xml.dom.minidom as md
+    for name in zz.namelist():
+        data = zz.read(name)
+        try:
+            md.parseString(data)
+            print("xml ok:", name, len(data), "bytes")
+        except Exception as e:
+            print("XML FAIL:", name, e)
+    sheet = zz.read("xl/worksheets/sheet1.xml").decode("utf-8")
+    rows = len(re.findall(r'<row ', sheet))
+    merges = len(re.findall(r'<mergeCell ', sheet))
+    maxs = [int(x) for x in re.findall(r'<c r="[A-Z]+\d+" s="(\d+)"', sheet)]
+    print("sheet rows:", rows, "merges:", merges, "max style idx:", max(maxs) if maxs else 0)
+    st = zz.read("xl/styles.xml").decode("utf-8")
+    mm = re.search(r'<cellXfs count="(\d+)">', st)
+    print("exported cellXfs count:", mm.group(1) if mm else "N/A")

+ 15 - 0
_xdump.py

@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# 临时脚本:导出xlsx内容抽查(每行单元格值)
+import zipfile, re, sys
+z = zipfile.ZipFile(sys.argv[1])
+s = z.read("xl/worksheets/sheet1.xml").decode("utf-8")
+rows = re.findall(r'<row r="(\d+)"[^>]*>(.*?)</row>', s, re.S)
+for rn, body in rows:
+    cells = re.findall(r'<c r="([A-Z]+\d+)"[^/>]*?(?:/>|>(.*?)</c>)', body, re.S)
+    vals = []
+    for ref, inner in cells:
+        m = re.findall(r'<t[^>]*>([^<]*)</t>|<v>([^<]*)</v>', inner)
+        v = " ".join(a or b for a, b in m)
+        if v:
+            vals.append(ref + "=" + v)
+    print(rn, "|", " ; ".join(vals))

+ 16 - 0
_xdump2.py

@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# 临时脚本:导出xlsx逐格+合并区诊断
+import zipfile, re, sys
+z = zipfile.ZipFile(sys.argv[1])
+s = z.read("xl/worksheets/sheet1.xml").decode("utf-8")
+rows = re.findall(r'<row r="(\d+)"[^>]*>(.*?)</row>', s, re.S)
+for rn, body in rows:
+    cells = re.findall(r'<c r="([A-Z]+\d+)"([^/>]*)(?:/>|>(.*?)</c>)', body, re.S)
+    vals = []
+    for ref, attr, inner in cells:
+        m = re.findall(r'<t[^>]*>([^<]*)</t>|<v>([^<]*)</v>', inner)
+        v = " ".join(a or b for a, b in m)
+        st = re.search(r's="(\d+)"', attr)
+        vals.append(ref + ("[s" + st.group(1) + "]" if st else "") + "=" + v)
+    print(rn, "|", " ; ".join(vals))
+print("MERGES:", re.findall(r'<mergeCell ref="([^"]+)"', s))


+ 2 - 0
_xz1/[Content_Types].xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="png" ContentType="image/png"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/custom.xml" ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml"/><Override PartName="/xl/cellimages.xml" ContentType="application/vnd.wps-officedocument.cellimage+xml"/><Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>

+ 2 - 0
_xz1/_rels/.rels

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" Target="docProps/custom.xml"/></Relationships>

+ 2 - 0
_xz1/docProps/app.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>WPS 表格</Application><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>工作表</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="1" baseType="lpstr"><vt:lpstr>全屋柜类报价</vt:lpstr></vt:vector></TitlesOfParts></Properties>

+ 2 - 0
_xz1/docProps/core.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:creator>高金慧</dc:creator><cp:lastModifiedBy>Hasee</cp:lastModifiedBy><dcterms:created xsi:type="dcterms:W3CDTF">2023-03-06T07:21:00Z</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">2026-06-18T14:52:53Z</dcterms:modified></cp:coreProperties>

+ 2 - 0
_xz1/docProps/custom.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2" name="ICV"><vt:lpwstr>1964BCFB9F60464A9EE96792B3052139_13</vt:lpwstr></property><property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="3" name="KSOProductBuildVer"><vt:lpwstr>2052-12.1.0.26895</vt:lpwstr></property><property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="4" name="CalculationRule"><vt:i4>0</vt:i4></property></Properties>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_xz1/xl/_rels/cellimages.xml.rels


+ 2 - 0
_xz1/xl/_rels/workbook.xml.rels

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId4" Type="http://www.wps.cn/officeDocument/2020/cellImage" Target="cellimages.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/></Relationships>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_xz1/xl/cellimages.xml


BIN
_xz1/xl/media/image1.png


BIN
_xz1/xl/media/image10.png


BIN
_xz1/xl/media/image11.png


BIN
_xz1/xl/media/image12.png


BIN
_xz1/xl/media/image13.png


BIN
_xz1/xl/media/image14.png


BIN
_xz1/xl/media/image15.png


BIN
_xz1/xl/media/image16.png


BIN
_xz1/xl/media/image17.png


BIN
_xz1/xl/media/image18.png


BIN
_xz1/xl/media/image2.png


BIN
_xz1/xl/media/image3.png


BIN
_xz1/xl/media/image4.png


BIN
_xz1/xl/media/image5.png


BIN
_xz1/xl/media/image6.png


BIN
_xz1/xl/media/image7.png


BIN
_xz1/xl/media/image8.png


BIN
_xz1/xl/media/image9.png


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_xz1/xl/sharedStrings.xml


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_xz1/xl/styles.xml


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_xz1/xl/theme/theme1.xml


+ 2 - 0
_xz1/xl/workbook.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:dbsheet="http://web.wps.cn/et/2021/dbsheet"><fileVersion appName="xl" lastEdited="3" lowestEdited="5" rupBuild="9302"/><workbookPr/><bookViews><workbookView windowWidth="27945" windowHeight="12960"/></bookViews><sheets><sheet name="全屋柜类报价" sheetId="4" r:id="rId1"/></sheets><calcPr calcId="191029"/><extLst><ext uri="{B58B0392-4F1F-4190-BB64-5DF3571DCE5F}" xmlns:xcalcf="http://schemas.microsoft.com/office/spreadsheetml/2018/calcfeatures"><xcalcf:calcFeatures><xcalcf:feature name="microsoft.com:RD"/><xcalcf:feature name="microsoft.com:Single"/><xcalcf:feature name="microsoft.com:FV"/><xcalcf:feature name="microsoft.com:CNMTM"/><xcalcf:feature name="microsoft.com:LET_WF"/><xcalcf:feature name="microsoft.com:LAMBDA_WF"/><xcalcf:feature name="microsoft.com:ARRAYTEXT_WF"/></xcalcf:calcFeatures></ext></extLst></workbook>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
_xz1/xl/worksheets/sheet1.xml


+ 33 - 0
_xzparse.js

@@ -0,0 +1,33 @@
+const fs = require('fs');
+const dec = s => String(s).replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#10;/g, ' ').replace(/&quot;/g, '"');
+const ss = fs.readFileSync('d:/AI文件夹/预算报价/_xz1/xl/sharedStrings.xml', 'utf8');
+const sis = [];
+let m;
+const siRe = /<si>([\s\S]*?)<\/si>/g;
+while ((m = siRe.exec(ss))) {
+  const inner = m[1];
+  const ts = [...inner.matchAll(/<t[^>]*>([\s\S]*?)<\/t>/g)].map(x => x[1]).join('');
+  sis.push(dec(ts));
+}
+const sh = fs.readFileSync('d:/AI文件夹/预算报价/_xz1/xl/worksheets/sheet1.xml', 'utf8');
+const rowRe = /<row[^>]*r="(\d+)"[^>]*>([\s\S]*?)<\/row>/g;
+const out = [];
+while ((m = rowRe.exec(sh))) {
+  const rn = m[1], body = m[2];
+  const cells = [];
+  const cre = /<c r="([A-Z]+)\d+"([^/>]*)(?:\/>|>([\s\S]*?)<\/c>)/g;
+  let cm;
+  while ((cm = cre.exec(body))) {
+    const col = cm[1], attrs = cm[2] || '', inner = cm[3] || '';
+    const t = (attrs.match(/t="([^"]+)"/) || [])[1] || 'n';
+    const f = (inner.match(/<f[^>]*>([\s\S]*?)<\/f>/) || [])[1] || '';
+    const v = (inner.match(/<v>([\s\S]*?)<\/v>/) || [])[1] || '';
+    let val = v;
+    if (t === 's') val = sis[+v] || '';
+    else if (t === 'inlineStr') val = (inner.match(/<t[^>]*>([\s\S]*?)<\/t>/) || [])[1] || '';
+    cells.push(col + '=' + dec(val) + (f ? '{' + dec(f) + '}' : ''));
+  }
+  if (cells.length) out.push(rn + ': ' + cells.join(' | '));
+}
+fs.writeFileSync('d:/AI文件夹/预算报价/_xzrows.txt', out.join('\r\n'), 'utf8');
+console.log('rows', out.length);

+ 110 - 0
_xzrows.txt

@@ -0,0 +1,110 @@
+1: A=万骊全屋定制预算表 | B= | C= | D= | E= | F= | G= | H= | I= | J=
+2: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+3: A=1 | B=玄关柜 | C=柜体 | D=0.79 | E=2.5 | F=1.975{E3*D3} | G=1852 | H=3657.7{G3*E3*D3} | I=生态澳松板 | J==DISPIMG("ID_E4EA15CFCB02496A9DC75348D7B35697",1){_xlfn.DISPIMG("ID_E4EA15CFCB02496A9DC75348D7B35697",1)}
+4: A= | B= | C=板门补差价 | D=0.79 | E=2.5 | F=1.975{E4*D4} | G=538{1138-600} | H=1062.55{G4*E4*D4} | I=双饰面补差价 | J=
+5: A= | B= | C=灯带 | D= | E=1.58{D3*2} | F= | G=386 | H=609.88{G5*E5} | I=3000K暖光灯 | J=
+6: A= | B= | C=变压器+开关 | D= | E=1 | F= | G=536{150+386} | H=536 | I=功能五金 | J=
+7: A= | B= | C=自弹器 | D= | E=4 | F= | G=115 | H=460 | I=海蒂诗自弹器 | J= | K= | L=
+8: A= | B= | C=见光板 | D=0.54 | E=2.5 | F=1.35{E8*D8} | G=1138 | H=1536.3{G8*F8} | I=双饰面 | J= | K= | L=
+9: A= | B= | C=铰链 | D= | E=12{4*3} | F= | G=40 | H=480{G9*E9} | I=奥地利百隆阻尼铰链 | J==DISPIMG("ID_501377114C6844EDA02CD3057E500A3C",1){_xlfn.DISPIMG("ID_501377114C6844EDA02CD3057E500A3C",1)}
+10: A= | B= | C=总价  | D= | E= | F= | G= | H=8342.43{SUM(H3:H9)} | I=/ | J=
+11: A= | B= | C=折后合计  | D= | E= | F= | G= | H=7508.187{H10*0.9} | I=/ | J=
+12: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+13: A=2 | B=客厅电视柜 | C=柜体 | D=4.85 | E=2.5 | F=12.125{E13*D13} | G=1852 | H=22455.5{G13*E13*D13} | I=生态澳松板 | J==DISPIMG("ID_AC9C657C8C414522BC92BF4AF10ED043",1){_xlfn.DISPIMG("ID_AC9C657C8C414522BC92BF4AF10ED043",1)}
+14: A= | B= | C=板门补差价 | D=4.85 | E=2.5 | F=12.125 | G=538{1138-600} | H=6523.25{G14*E14*D14} | I=双饰面补差价 | J=
+15: A= | B= | C=自弹器 | D= | E=19 | F= | G=115 | H=2185{G15*E15} | I=海蒂诗自弹器 | J=
+16: A= | B= | C=见光板 | D=0.35 | E=2.5 | F=0.875 | G=1138 | H=1991.5{G16*F16*2} | I=双饰面 | J=
+17: A= | B= | C=铰链 | D= | E=55{11*5} | F= | G=40 | H=2200{G17*E17} | I=奥地利百隆阻尼铰链 | J==DISPIMG("ID_3133123047CC44E19D73D3C67FC275D5",1){_xlfn.DISPIMG("ID_3133123047CC44E19D73D3C67FC275D5",1)}
+18: A= | B= | C=总价  | D= | E= | F= | G= | H=35355.25{SUM(H13:H17)} | I=/ | J=
+19: A= | B= | C=折后合计  | D= | E= | F= | G= | H=31819.725{H18*0.9} | I=/ | J=
+20: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+21: A=3 | B=洗衣机柜 | C=柜体 | D=1.215{0.565+0.65} | E=2.5 | F=3.0375{E21*D21} | G=1852 | H=5625.45{G21*E21*D21} | I=生态澳松板 | J==DISPIMG("ID_FA33022ED481467AB913BAE3EE707A66",1){_xlfn.DISPIMG("ID_FA33022ED481467AB913BAE3EE707A66",1)}
+22: A= | B= | C=板门补差价 | D=1.215{0.565+0.65} | E=2.5 | F=3.0375{E22*D22} | G=538{1138-600} | H=1634.175 | I=双饰面补差价 | J=
+23: A= | B= | C=自弹器 | D= | E=2 | F= | G=115 | H=230{G23*E23} | I=海蒂诗自弹器 | J=
+24: A= | B= | C=护墙板 | D=0.28 | E=2.5 | F=0.7{E24*D24} | G=1852 | H=1296.4 | I=双饰面 | J=
+25: A= | B= | C=见光板 | D=0.6 | E=2.5 | F=1.5{E25*D25} | G=1138 | H=3414{G25*F25*2} | I=双饰面 | J=
+26: A= | B= | C=铰链 | D= | E=7{5+2} | F= | G=40 | H=280{G26*E26} | I=奥地利百隆阻尼铰链 | J==DISPIMG("ID_18E2A164CD14464AB806E414E213D621",1){_xlfn.DISPIMG("ID_18E2A164CD14464AB806E414E213D621",1)}
+27: A= | B= | C=总价  | D= | E= | F= | G= | H=12480.025{SUM(H21:H26)} | I=/ | J=
+28: A= | B= | C=折后合计  | D= | E= | F= | G= | H=11232.0225{H27*0.9} | I=/ | J=
+29: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+30: A=4 | B=客厅展示柜 | C=柜体 | D=0.89 | E=2.5 | F=2.225{E30*D30} | G=1852 | H=4120.7{G30*E30*D30} | I=生态澳松板 | J==DISPIMG("ID_2BE1A910939B44728CC4CAF1CF126F50",1){_xlfn.DISPIMG("ID_2BE1A910939B44728CC4CAF1CF126F50",1)}
+31: A= | B= | C=玻璃门补差价 | D=0.89 | E=2.5 | F=2.225 | G=1852 | H=4120.7{G31*E31*D31} | I=双饰面补差价 | J=
+32: A= | B= | C=灯带 | D= | E=5.34{D30*6} | F= | G=386 | H=2061.24{G32*E32} | I=3000K暖光灯 | J=
+33: A= | B= | C=变压器+开关 | D= | E=1 | F= | G=536{150+386} | H=536 | I=功能五金 | J=
+34: A= | B= | C=拉手 | D= | E=2 | F= | G=226 | H=452 | I=金属拉手 | J=
+35: A= | B= | C=见光板 | D=0.3 | E=2.5 | F=0.75 | G=1138 | H=1707{G35*F35*2} | I=双饰面 | J=
+36: A= | B= | C=铰链 | D= | E=15{5*3} | F= | G=40 | H=600{G36*E36} | I=奥地利百隆阻尼铰链 | J==DISPIMG("ID_103743CCB36E475F8775D3808699B502",1){_xlfn.DISPIMG("ID_103743CCB36E475F8775D3808699B502",1)}
+37: A= | B= | C=柜体 | D=0.45 | E=2.5 | F=1.125{E37*D37} | G=1852 | H=2083.5{G37*E37*D37} | I=生态澳松板 | J=
+38: A= | B= | C=玻璃门补差价 | D=0.45 | E=2.5 | F=1.125{E38*D38} | G=1852 | H=2083.5{G38*E38*D38} | I=双饰面补差价 | J=
+39: A= | B= | C=自弹器 | D= | E=2 | F= | G=115 | H=230{G39*E39} | I=海蒂诗自弹器 | J=
+40: A= | B= | C=总价  | D= | E= | F= | G= | H=17994.64{SUM(H30:H39)} | I=/ | J=
+41: A= | B= | C=折后合计  | D= | E= | F= | G= | H=16195.176{H40*0.9} | I=/ | J=
+42: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+43: A=5 | B=阳台柜 | C=柜体 | D=1.5 | E=1 | F=1.5{E43*D43} | G=1852 | H=2778{G43*F43} | I=生态澳松板 | J==DISPIMG("ID_73636484E08A4CE7A0E4D74827C86BEF",1){_xlfn.DISPIMG("ID_73636484E08A4CE7A0E4D74827C86BEF",1)}
+44: A= | B= | C=板门补差价 | D=1.5 | E=2.5 | F=3.75 | G=538 | H=2017.5{G44*F44} | I=双饰面补差价 | J=
+45: A= | B= | C=自弹器 | D= | E=3 | F= | G=115 | H=345{G45*E45} | I=海蒂诗自弹器 | J=
+46: A= | B= | C=见光板 | D=0.6 | E=1 | F=0.6 | G=1138 | H=1365.6{G46*F46*2} | I=双饰面 | J==DISPIMG("ID_9BB61A6811834602A2DE7894BBB1C112",1){_xlfn.DISPIMG("ID_9BB61A6811834602A2DE7894BBB1C112",1)}
+47: A= | B= | C=铰链 | D= | E=9{3*3} | F= | G=40 | H=360{G47*E47} | I=奥地利百隆阻尼铰链 | J=
+48: A= | B= | C=总价  | D= | E= | F= | G= | H=6866.1{SUM(H43:H47)} | I=/ | J=
+49: A= | B= | C=折后合计  | D= | E= | F= | G= | H=6179.49{H48*0.9} | I=/ | J=
+50: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+51: A=6 | B=餐边柜 | C=吊柜柜体 | D=2 | E=0.9 | F=1.8{E51*D51} | G=1852 | H=3333.6{G51*E51*D51} | I=生态澳松板 | J==DISPIMG("ID_9220E21280E541689F5257B7EE0631BF",1){_xlfn.DISPIMG("ID_9220E21280E541689F5257B7EE0631BF",1)}
+52: A= | B= | C=板门补差价 | D=2 | E=0.9 | F=1.8 | G=538 | H=968.4{G52*F52} | I=双饰面补差价 | J=
+53: A= | B= | C=地柜柜体 | D=2 | E=0.9 | F=1.8 | G=1852 | H=3333.6{G53*E53*D53} | I=生态澳松板 | J=
+54: A= | B= | C=板门补差价 | D=2 | E=0.9 | F=1.8 | G=538 | H=968.4{G54*F54} | I=双饰面补差价 | J=
+55: A= | B= | C=抽屉 | D= | E=3 | F= | G=1300 | H=3900{G55*E55} | I=奥地利百隆导轨 | J=
+56: A= | B= | C=拉手 | D= | E=1 | F= | G=226 | H=226{G56*E56} | I=金属拉手 | J=
+57: A= | B= | C=自弹器 | D= | E=5 | F= | G=115 | H=575{G57*E57} | I=海蒂诗自弹器 | J=
+58: A= | B= | C=金属拉手 | D= | E=3.6{2+0.8*2} | F= | G=225 | H=810 | I=金屬 | J=
+59: A= | B= | C=灯带 | D= | E=3.5{2+1.5} | F= | G=386 | H=1351 | I=3000K暖光灯 | J==DISPIMG("ID_9D42EE889FCE4FB3AE75C719BCFEF642",1){_xlfn.DISPIMG("ID_9D42EE889FCE4FB3AE75C719BCFEF642",1)}
+60: A= | B= | C=变压器+开关 | D= | E=1 | F= | G=536{150+386} | H=536 | I=功能五金 | J=
+61: A= | B= | C=见光板 | D=0.45 | E=1 | F=0.45{E61*D61} | G=1138 | H=2048.4{G61*F61*4} | I=双饰面 | J=
+62: A= | B= | C=铰链 | D= | E=24{8*3} | F= | G=40 | H=960 | I=奥地利百隆阻尼铰链 | J=
+63: A= | B= | C=总价  | D= | E= | F= | G= | H=19010.4{SUM(H51:H62)} | I=/ | J=
+64: A= | B= | C=折后合计  | D= | E= | F= | G= | H=17109.36{H63*0.9} | I=/ | J=
+65: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+66: A=7 | B=次卧 | C=柜体 | D=3.74{2.64+1.1} | E=2.5 | F=9.35{E66*D66} | G=1852 | H=17316.2{G66*F66} | I=生态澳松板 | J==DISPIMG("ID_2AC174C5DC294067B4EF25C14160211A",1){_xlfn.DISPIMG("ID_2AC174C5DC294067B4EF25C14160211A",1)}
+67: A= | B= | C=板门补差价 | D=3.74{2.64+1.1} | E=2.5 | F=9.35{E67*D67} | G=538 | H=5030.3{G67*F67} | I=双饰面补差价 | J=
+68: A= | B= | C=衣杆 | D= | E=6 | F= | G=288 | H=1728{G68*E68} | I=金属 | J=
+69: A= | B= | C=金属拉手 | D= | E=8 | F= | G=680 | H=5440{G69*E69} | I=金属拉手 | J=
+70: A= | B= | C=抽屉 | D= | E=3 | F= | G=1300 | H=3900 | I=奥地利百隆导轨 | J==DISPIMG("ID_67D9B0FCFD6744A6816C254F6792C19D",1){_xlfn.DISPIMG("ID_67D9B0FCFD6744A6816C254F6792C19D",1)}
+71: A= | B= | C=金属拉手 | D= | E=2.4{0.8*3} | F= | G=225 | H=540 | I=金属拉手 | J=
+72: A= | B= | C=见光板 | D=0.6 | E=2.5 | F=1.5{E72*D72} | G=1138 | H=3414{G72*F72*2} | I=双饰面 | J=
+73: A= | B= | C=铰链 | D= | E=40{8*5} | F= | G=40 | H=1600{G73*E73} | I=奥地利百隆阻尼铰链 | J=
+74: A= | B= | C=总价  | D= | E= | F= | G= | H=38968.5{SUM(H66:H73)} | I=/ | J=
+75: A= | B= | C=折后合计  | D= | E= | F= | G= | H=35071.65{H74*0.9} | I=/ | J=
+76: A=序号 | B=品名 | C= | D=宽(米) | E=高(米) | F=面积(m²) | G=投影单价 | H=总价 | I=备注 | J=
+77: A=8 | B=主卧 | C=柜体 | D=2.28 | E=1.8 | F=4.104{E77*D77} | G=2796 | H=11474.784{G77*E77*D77} | I=铝格栅板 | J==DISPIMG("ID_7EEDBBF788294959AADAF66EB33F5C6A",1){_xlfn.DISPIMG("ID_7EEDBBF788294959AADAF66EB33F5C6A",1)}
+78: A= | B= | C=板门补差价 | D=2.28 | E=1.8 | F=4.104{E78*D78} | G=1380{1980-600} | H=5663.52 | I=铝格栅板补差价 | J=
+79: A= | B= | C=金属拉手 | D= | E=5 | F= | G=680 | H=3400{G79*E79} | I=金属拉手 | J=
+80: A= | B= | C=抽屉 | D= | E=2 | F= | G=1300 | H=2600 | I=奥地利百隆导轨 | J=
+81: A= | B= | C=衣杆 | D= | E=4 | F= | G=288 | H=1152 | I=金属 | J=
+82: A= | B= | C=自弹器 | D= | E=2 | F= | G=115 | H=230{G82*E82} | I=海蒂诗自弹器 | J==DISPIMG("ID_1C9497FAEE644C4ABE019DCF4F82653B",1){_xlfn.DISPIMG("ID_1C9497FAEE644C4ABE019DCF4F82653B",1)}
+83: A= | B= | C=圆弧 | D= | E=1.8 | F= | G=1560 | H=2808{G83*E83} | I=金属 | J=
+84: A= | B= | C=铰链 | D= | E=31{5*5+2*3} | F= | G=40 | H=1240 | I=奥地利百隆阻尼铰链 | J=
+85: A= | B= | C=总价  | D= | E= | F= | G= | H=28568.304{SUM(H77:H84)} | I=/ | J=
+86: A= | B= | C=折后合计  | D= | E= | F= | G= | H=25711.4736{H85*0.9} | I=/ | J=
+87: A=序号 | B=品名 | C= | D=宽(米) | E=套餐(米) | F=超出(米) | G=投影单价 | H=总价 | I=备注 | J=
+88: A=9 | B=厨房(包含金属拉手、铰链、金属踢脚线) | C=地柜 | D=4.18{0.97+3.21} | E=3 | F=1.18{D88-E88} | G=2380 | H=2808.4{G88*F88} | I=/ | J==DISPIMG("ID_48125B3195674FE29FC05A6DB93A4D86",1){_xlfn.DISPIMG("ID_48125B3195674FE29FC05A6DB93A4D86",1)}
+89: A= | B= | C=吊柜 | D=3.21 | E=1 | F=2.21{D89-E89} | G=1748 | H=3863.08{G89*F89} | I=/ | J=
+90: A= | B= | C=冰箱吊柜 | D= | E=0.97 | F= | G=2622{G89*1.5} | H=2543.34 | I=/ | J=
+91: A= | B= | C=高柜 | D= | E=0.88{0.88} | F= | G=5950{G88*2.5} | H=5236 | I=/ | J=
+92: A= | B= | C=见光板*2 | D=0.6 | E=2.5 | F=1.5{E92*D92} | G=1138 | H=5121{G92*F92*3} | I=/ | J=
+93: A= | B= | C=台面 | D=3.21 | E=3{E88} | F= | G=1480 | H=4440{G93*E93} | I=/ | J=
+94: A= | B= | C=台面 | D= | E=0.21{D93-E93} | F= | G=2480 | H=520.8{G94*E94} | I= | J=
+95: A= | B= | C=碗篮 | D= | E= | F=1 | G=1268 | H=1268{G95} | I=/ | J==DISPIMG("ID_B7BA95BA2F404CCD84DF8911867DBD47",1){_xlfn.DISPIMG("ID_B7BA95BA2F404CCD84DF8911867DBD47",1)}
+96: A= | B= | C=锅篮 | D= | E= | F=1 | G=1628 | H=1628{G96} | I=/ | J=
+97: A= | B= | C=自弹 | D= | E= | F=4 | G=115 | H=460{F97*G97} | I=/ | J=
+98: A= | B= | C=切角 | D= | E= | F=1 | G=500 | H=500{G98*F98} | I=/ | J=
+99: A= | B= | C=台下盆 | D= | E= | F=1 | G=500 | H=500{G99*F99} | I=/ | J=
+100: A= | B= | C=包管 | D= | E= | F=1 | G=500 | H=500{G100*F100} | I=/ | J=
+101: A= | B= | C=总价  | D= | E= | F= | G= | H=29388.62{SUM(H88:H100)} | I=/ | J=
+102: A= | B= | C=厨房套餐超出折后合计: | D= | E= | F= | G= | H=26449.758{SUM(H101*0.9)} | I= | J=
+103: A= | B= | C=双饰面套餐:3+3+1=11199元 | D= | E= | F= | G= | H=11199 | I= | J=
+104: A= | B= | C=双饰面套餐合计: | D= | E= | F= | G= | H=37648.758{H102+H103} | I= | J=
+105: A=全屋原价合计: | B= | C= | D= | E= | F= | G= | H= | I=196974.269{H10+H18+H27+H40+H48+H63+H74+H85+H101} | J=
+106: A=全屋折后合计: | B= | C= | D= | E= | F= | G= | H= | I=188475.8421{I105*0.9+H103} | J=
+107: A=注意:此份报价仅作为前期与客户沟通的预估价格,具体以实际设计方案为主,此份仅作参考 | B= | C= | D= | E= | F= | G= | H= | I= | J=
+108: A=家居顾问:孙烨 | B= | C= | D= | E= | F= | G= | H= | I= | J=
+109: A=门店地址:万骊总部旗舰店 | B= | C= | D= | E= | F= | G= | H= | I= | J=
+110: A=当日日期:2026.6.18 | B= | C= | D= | E= | F= | G= | H= | I= | J=

BIN
annotation.png


BIN
assets/furniture/balcony-cabinet-source.png


BIN
assets/furniture/balcony-cabinet.png


BIN
assets/furniture/contact-sheet.png


BIN
assets/furniture/dining-sideboard-source.png


BIN
assets/furniture/dining-sideboard.png


BIN
assets/furniture/entry-cabinet-source.png


BIN
assets/furniture/entry-cabinet.png


BIN
assets/furniture/kitchen-cabinet-source.png


BIN
assets/furniture/kitchen-cabinet.png


BIN
assets/furniture/laundry-cabinet-source.png


BIN
assets/furniture/laundry-cabinet.png


BIN
assets/furniture/living-display-cabinet-source.png


BIN
assets/furniture/living-display-cabinet.png


BIN
assets/furniture/living-tv-cabinet-source.png


BIN
assets/furniture/living-tv-cabinet.png


BIN
assets/furniture/master-wardrobe-source.png


BIN
assets/furniture/master-wardrobe.png


BIN
assets/furniture/secondary-wardrobe-source.png


BIN
assets/furniture/secondary-wardrobe.png


Некоторые файлы не были показаны из-за большого количества измененных файлов