// products.js — 产品迭代模块 function formHtml(fields, button) { return `
${fields.map((f) => ``).join("")}
`; } async function createResource(event, resource) { event.preventDefault(); const form = event.currentTarget; const data = Object.fromEntries(new FormData(form).entries()); data.tenant = state.tenant; try { const result = await api(`/api/${resource}`, { method: "POST", body: JSON.stringify({ data }) }); const targetMap = { sales: "sales", proposals: "proposal", operations: "operation", products: "product" }; const resType = targetMap[resource] || resource; const name = data.project_name || data.target_customer || data.customer_or_project_name || data.product_name || ""; if (result.id && name) logActivity(resType, result.id, "创建了" + name); form.reset(); await load(); } catch (error) { toast("创建失败:" + error.message, "error"); } } window.createSales = (event) => createResource(event, "sales"); window.createProposal = (event) => createResource(event, "proposals"); window.createOperation = async (event) => { await createResource(event, "operations"); if (typeof closeNewProjectModal === "function") closeNewProjectModal(); }; window.openProductDrawer = () => { const drawer = document.querySelector("#productDrawer"); drawer.innerHTML = `
新增产品版本
`; drawer.classList.add("open"); if (window.lucide) window.lucide.createIcons(); }; window.closeProductDrawer = () => { document.querySelector("#productDrawer").classList.remove("open"); }; window.cycleProductStatus = async (id) => { const products = state.data.products || []; const product = products.find(x => x.id === id); if (!product) return; const statuses = ["规划中", "开发中", "测试中", "已上线", "已取消"]; const current = statuses.indexOf(product.status) >= 0 ? product.status : "规划中"; const newStatus = statuses[(statuses.indexOf(current) + 1) % statuses.length]; try { await api(`/api/products/${id}`, { method: "PUT", body: JSON.stringify({ data: { status: newStatus } }) }); product.status = newStatus; renderProducts(); } catch (error) { toast("更新失败:" + error.message, "error"); } }; window.editProductDate = (event, id) => { event.stopPropagation(); const products = state.data.products || []; const product = products.find(x => x.id === id); if (!product) return; const span = event.currentTarget; const td = span.parentElement; const currentValue = product.launch_date || ""; const input = document.createElement("input"); input.type = "date"; input.className = "form-ctrl form-ctrl-sm w-full"; input.value = currentValue; input.addEventListener("change", async () => { const newValue = input.value; try { await api(`/api/products/${id}`, { method: "PUT", body: JSON.stringify({ data: { launch_date: newValue } }) }); product.launch_date = newValue; td.innerHTML = `${newValue || '—'}`; } catch (e) { toast("修改失败:" + e.message, "error"); } }); input.addEventListener("blur", () => { td.innerHTML = `${currentValue || '—'}`; }); td.innerHTML = ""; td.appendChild(input); input.focus(); }; window.addNewFeature = () => { const list = document.querySelector("#newFeatureList"); if (!list) return; const idx = list.children.length; const div = document.createElement("div"); div.className = "feature-item"; div.innerHTML = `${idx+1}.`; list.appendChild(div); if (window.lucide) window.lucide.createIcons(); }; window.removeNewFeature = (btn) => { const div = btn.closest(".feature-item"); if (!div) return; div.remove(); const list = document.querySelector("#newFeatureList"); if (list) list.querySelectorAll(".feature-num").forEach((el, i) => { el.textContent = (i+1) + "."; }); }; window.submitProductDrawer = async (event) => { event.preventDefault(); const form = event.currentTarget; const data = Object.fromEntries(new FormData(form).entries()); const featureInputs = form.querySelectorAll("#newFeatureList input"); data.feature_list = [...featureInputs].map(el => el.value.trim()).filter(Boolean).join("\n"); data.platform = ""; data.tenant = state.tenant; try { const result = await api("/api/products", { method: "POST", body: JSON.stringify({ data }) }); form.reset(); closeProductDrawer(); if (result.id) logActivity("product", result.id, "创建了产品版本「" + data.product_name + " " + data.version + "」"); await load(); } catch (error) { toast("创建失败:" + error.message, "error"); } }; window.addFeature = (id) => { const list = document.querySelector(`#featureList_${id}`); if (!list) return; const idx = list.children.length; const div = document.createElement("div"); div.className = "feature-item"; div.innerHTML = `${idx+1}.`; list.appendChild(div); if (window.lucide) window.lucide.createIcons(); saveFeatureList(id); }; window.removeFeature = (id, idx) => { const list = document.querySelector(`#featureList_${id}`); if (!list) return; const items = list.querySelectorAll(".feature-item"); if (items[idx]) items[idx].remove(); list.querySelectorAll(".feature-num").forEach((el, i) => { el.textContent = (i+1) + "."; }); saveFeatureList(id); }; window.saveFeatureList = (id) => { const list = document.querySelector(`#featureList_${id}`); if (!list) return; const values = [...list.querySelectorAll("input")].map(el => el.value.trim()).filter(Boolean); const data = values.join("\n"); api(`/api/products/${id}`, { method: "PUT", body: JSON.stringify({ data: { feature_list: data } }) }); const product = (state.data.products || []).find(x => x.id === id); if (product) product.feature_list = data; }; function renderProducts() { const items = state.data.products || []; document.querySelector("#products").innerHTML = `
${items.map((p) => `

${esc(p.product_name)}

${esc(p.status) || '规划中'}
${esc(p.version)} · ${esc(p.launch_date) || '—'}

${esc(p.version_goal) || '—'}

${esc(p.feature_list) || '—'}
`).join("")}
`; }