Forráskód Böngészése

fix: 闭环4处未闭环环节

1) 存档→打开恢复增值项勾选/数量/尺寸/备注(ex),不再重建默认丢用户勾选

2) 修改红标注值时厨房宽/高与总长度随新比例同步重算

3) 补充已知尺寸建立比例后同步刷新第二步结果面板(px→m)

4) 导出三级兜底链:/save_xlsx → /api/export/excel → 浏览器下载,样式取不到时回退内置默认不再中断
缪子怡 2 hete
szülő
commit
3739400e89
1 módosított fájl, 21 hozzáadás és 7 törlés
  1. 21 7
      AI快速报价_Demo2.optimizing.html

+ 21 - 7
AI快速报价_Demo2.optimizing.html

@@ -1874,7 +1874,8 @@ function qOpenQuote(q){
     $("dc-rule").textContent="存档恢复(原图未保存)";
     $("dc-rule").textContent="存档恢复(原图未保存)";
     $("q-ai-scene-chip").textContent="存档报价单(原图未保存)";$("q-status-drawing").textContent="⬜ 图纸:未随单保存";
     $("q-ai-scene-chip").textContent="存档报价单(原图未保存)";$("q-status-drawing").textContent="⬜ 图纸:未随单保存";
     $("q-btn-analyze").disabled=true;$("q-step2-next-btn").disabled=false;
     $("q-btn-analyze").disabled=true;$("q-step2-next-btn").disabled=false;
-    S.objs.forEach(o=>{if(!o.extras)o.extras=mkExtras(o.name||"",false);});
+    /* 存档→打开闭环:存档内增值项勾选/数量/尺寸/备注(ex)原样恢复,未存档时才重建默认,避免用户勾选丢失 */
+    S.objs.forEach(o=>{if(o.ex){o.extras=o.ex;delete o.ex;}if(!o.extras||!Object.keys(o.extras).length)o.extras=mkExtras(o.name||"",false);});
     rebuildObjs();recalc();renderAIResults();
     rebuildObjs();recalc();renderAIResults();
     $("editor-title").textContent=q.name;
     $("editor-title").textContent=q.name;
     switchView("editor");qOpenEditorStep(4);
     switchView("editor");qOpenEditorStep(4);
@@ -1946,7 +1947,7 @@ async function qExportExcel(){
       if(j.styles){const dec=s=>new TextDecoder().decode(Uint8Array.from(atob(s),c=>c.charCodeAt(0)));
       if(j.styles){const dec=s=>new TextDecoder().decode(Uint8Array.from(atob(s),c=>c.charCodeAt(0)));
         stylesRaw=dec(j.styles);themeRaw=j.theme?dec(j.theme):"";}}
         stylesRaw=dec(j.styles);themeRaw=j.theme?dec(j.theme):"";}}
   }catch(e){}
   }catch(e){}
-  if(!stylesRaw){toast("导出失败:无法读取参考表样式(请确认桌面存在 6.18号欧阳路预估报价.xlsx 且服务已启动)","toast-error");return;}
+  /* 样式闭环:参考表样式取不到(部署环境无 /ref_parts 或桌面无参考表)→ 回退 xlsxBuild 内置默认样式继续导出,不再中断 */
   const t=(x,s)=>({v:x,s:s});
   const t=(x,s)=>({v:x,s:s});
   const rows=[],merges=[],heights={};
   const rows=[],merges=[],heights={};
   const R=arr=>{rows.push(arr);heights[rows.length-1]=18;};
   const R=arr=>{rows.push(arr);heights[rows.length-1]=18;};
@@ -1996,9 +1997,20 @@ async function qExportExcel(){
   const bytes=xlsxBuild({rows,merges,heights,cols:COLS,stylesRaw,themeRaw});
   const bytes=xlsxBuild({rows,merges,heights,cols:COLS,stylesRaw,themeRaw});
   let bin="";for(let i=0;i<bytes.length;i++)bin+=String.fromCharCode(bytes[i]);
   let bin="";for(let i=0;i<bytes.length;i++)bin+=String.fromCharCode(bytes[i]);
   const fname=b.t+"_"+d.getFullYear()+p2(d.getMonth()+1)+p2(d.getDate())+"_"+p2(d.getHours())+p2(d.getMinutes())+".xlsx";
   const fname=b.t+"_"+d.getFullYear()+p2(d.getMonth()+1)+p2(d.getDate())+"_"+p2(d.getHours())+p2(d.getMinutes())+".xlsx";
-  fetch("/save_xlsx",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:fname,data:btoa(bin)})})
-    .then(r=>r.json()).then(j=>{toast(j.ok?"已导出到桌面 ✓ "+fname:"导出失败:"+j.err,j.ok?"toast-success":"toast-error");})
-    .catch(e=>toast("导出失败:"+e.message,"toast-error"));
+  /* 导出闭环:服务端写桌面(/save_xlsx)→ 备用接口(/api/export/excel)→ 浏览器下载,三级兜底保证任何部署环境都能导出 */
+  const b64=btoa(bin);
+  const download=()=>{const a=document.createElement("a");
+    a.href=URL.createObjectURL(new Blob([bytes],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}));
+    a.download=fname;document.body.appendChild(a);a.click();
+    setTimeout(()=>{URL.revokeObjectURL(a.href);a.remove();},800);
+    toast("已导出(浏览器下载):"+fname,"toast-success");};
+  fetch("/save_xlsx",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:fname,data:b64})})
+    .then(r=>{if(!r.ok)throw new Error("http"+r.status);return r.json();})
+    .then(j=>{if(j.ok)toast("已导出到桌面 ✓ "+fname,"toast-success");else throw new Error(j.err||"write failed");})
+    .catch(()=>fetch("/api/export/excel",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filename:fname,data:b64})})
+      .then(r=>{if(!r.ok)throw new Error("http"+r.status);return r.json();})
+      .then(j=>{toast("已导出到桌面 ✓ "+(j.filename||fname),"toast-success");})
+      .catch(download));
 }
 }
 
 
 /* ========== 规则配置表 ========== */
 /* ========== 规则配置表 ========== */
@@ -2018,8 +2030,10 @@ function renderRules(){
 }
 }
 
 
 /* ========== 事件与初始化 ========== */
 /* ========== 事件与初始化 ========== */
-$("q-red-anno").oninput=()=>{if(S.redArrow){const old=dimScale();const inf=annoInfer(S.scale600||0);if(!inf)return;S.scale=inf.scale;S.scaleSrc="红标注箭头校准(推断单位"+inf.unit+" · 与600mm锚点交叉校验)";const nw=dimScale();if(S.kitBox&&old&&nw)S.kitBox={wM:S.kitBox.wM/old*nw,hM:S.kitBox.hM/old*nw};renderAnnoInfer();recalc();renderAIResults();}};
-$("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");};
+$("q-red-anno").oninput=()=>{if(S.redArrow){const old=dimScale();const inf=annoInfer(S.scale600||0);if(!inf)return;S.scale=inf.scale;S.scaleSrc="红标注箭头校准(推断单位"+inf.unit+" · 与600mm锚点交叉校验)";const nw=dimScale();
+  if(S.kitBox&&old&&nw){S.kitBox={wM:S.kitBox.wM/old*nw,hM:S.kitBox.hM/old*nw};S.kitW=+Math.max(S.kitBox.wM,S.kitBox.hM).toFixed(2);S.kitH=+Math.min(S.kitBox.wM,S.kitBox.hM).toFixed(2);}   /* 厨房宽/高随新比例同步重算,总长度闭环 */
+  renderAnnoInfer();recalc();renderAIResults();}};
+$("btnFb").onclick=()=>{const px=+$("fbPx").value||0,mm=+$("fbMm").value||0;if(px>0&&mm>0){S.scale=(mm/1000)/px;S.scaleSrc="用户补充已知尺寸";recalc();renderAIResults();toast("比例已建立,柜体尺寸已换算为米");}else toast("请填写像素数与毫米数","toast-error");};   /* 补比例后同步刷新第二步结果面板(px→m),兑现"补充已知尺寸后自动换算" */
 
 
 qBindDrop($("q-upload-zone-unified"));qBindDrop($("q-canvas-drop"));   /* 点击 + 拖拽双通道上传/更换图纸 */
 qBindDrop($("q-upload-zone-unified"));qBindDrop($("q-canvas-drop"));   /* 点击 + 拖拽双通道上传/更换图纸 */
 renderTiers();renderRules();renderQuoteList();rebuildObjs();recalc();
 renderTiers();renderRules();renderQuoteList();rebuildObjs();recalc();