Files
step2urdf/app/static/app.js
T
sunxianghui 93773f3887 Initial commit: step2urdf tool with handtuned JSON import.
Includes FastAPI backend, vendored step2urdf frontend, and A7 handtuned arm JSON for URDF generation.
2026-08-26 15:32:47 +08:00

115 lines
3.5 KiB
JavaScript

const state = {
jobId: null,
draft: null,
};
const $ = (id) => document.getElementById(id);
function pretty(obj) {
return JSON.stringify(obj, null, 2);
}
async function refreshHealth() {
const el = $("health");
try {
const res = await fetch("/api/health");
const data = await res.json();
const z = data.zhipu_configured ? `智谱 OK (${data.zhipu_model})` : "智谱未配置";
const g = data.geometry?.available ? "几何后端可用" : "几何后端未装(占位 mesh)";
el.textContent = `${z} · ${g}`;
el.className = "pill " + (data.zhipu_configured ? "ok" : "warn");
} catch (e) {
el.textContent = "后端未连接";
el.className = "pill warn";
}
}
$("btnParse").addEventListener("click", async () => {
const file = $("file").files?.[0];
if (!file) {
$("parseOut").textContent = "请先选择 STEP 文件";
$("parseOut").classList.add("error");
return;
}
$("parseOut").classList.remove("error");
$("parseOut").textContent = "解析中…";
const fd = new FormData();
fd.append("file", file);
try {
const res = await fetch("/api/parse", { method: "POST", body: fd });
if (!res.ok) throw new Error(await res.text());
const data = await res.json();
state.jobId = data.job_id;
$("btnPropose").disabled = false;
$("parseOut").textContent = pretty({
job_id: data.job_id,
root_name: data.root_name,
product_count: data.product_names?.length,
product_names: data.product_names,
stats: data.stats,
});
} catch (e) {
$("parseOut").textContent = String(e);
$("parseOut").classList.add("error");
}
});
$("btnPropose").addEventListener("click", async () => {
if (!state.jobId) return;
$("proposeOut").classList.remove("error");
$("proposeOut").textContent = "调用智谱中(可能需要几十秒)…";
try {
const res = await fetch("/api/propose", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
job_id: state.jobId,
profile: $("profile").value,
robot_name: $("robotName").value || "A7",
extra_hint: $("hint").value || "",
}),
});
if (!res.ok) throw new Error(await res.text());
const draft = await res.json();
state.draft = draft;
$("draftEditor").value = pretty(draft);
$("btnExport").disabled = false;
$("proposeOut").textContent = `links=${draft.links?.length || 0}, joints=${draft.joints?.length || 0}\n` +
(draft.notes || []).map((n) => `- ${n}`).join("\n");
} catch (e) {
$("proposeOut").textContent = String(e);
$("proposeOut").classList.add("error");
}
});
$("btnExport").addEventListener("click", async () => {
if (!state.jobId) return;
$("exportOut").classList.remove("error");
try {
const draft = JSON.parse($("draftEditor").value);
state.draft = draft;
const res = await fetch("/api/export", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
job_id: state.jobId,
draft,
include_meshes: true,
}),
});
if (!res.ok) throw new Error(await res.text());
const meta = await res.json();
$("exportOut").textContent = pretty(meta);
const name = draft.name || "robot";
const a = $("dlLink");
a.hidden = false;
a.href = `/api/jobs/${state.jobId}/download/${encodeURIComponent(name)}`;
a.textContent = `下载 ${name}.urdf`;
} catch (e) {
$("exportOut").textContent = String(e);
$("exportOut").classList.add("error");
}
});
refreshHealth();