|
@@ -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();
|