|
|
|
@@ -249,6 +249,8 @@ window.switchTab = switchTab;
|
|
|
|
window.switchTenant = (tenant) => {
|
|
|
|
window.switchTenant = (tenant) => {
|
|
|
|
state.tenant = tenant;
|
|
|
|
state.tenant = tenant;
|
|
|
|
state.projectView = null;
|
|
|
|
state.projectView = null;
|
|
|
|
|
|
|
|
const label = tenant.replace("·无界", "");
|
|
|
|
|
|
|
|
document.querySelector("#workspaceTitle").textContent = label + " OPC 工作台";
|
|
|
|
load();
|
|
|
|
load();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@@ -298,7 +300,7 @@ function renderProjectTasks(projectId) {
|
|
|
|
${phases.map((phase) => {
|
|
|
|
${phases.map((phase) => {
|
|
|
|
const pt = tasks.filter((t) => t.phase === phase);
|
|
|
|
const pt = tasks.filter((t) => t.phase === phase);
|
|
|
|
if (!pt.length) return "";
|
|
|
|
if (!pt.length) return "";
|
|
|
|
return `<div class="task-group"><div class="task-group-hd"><span class="task-group-icon"><i data-lucide="layers"></i></span><span class="task-group-label">${phase}</span><span class="task-group-n">${pt.length}</span></div><div class="task-group-list" data-phase="${phase}" ondrop="handleTaskDrop(event, ${projectId}, '${phase}')" ondragover="event.preventDefault(); event.currentTarget.classList.add('drag-over')" ondragleave="event.currentTarget.classList.remove('drag-over')">${pt.map((t) => `<div class="task-row ${t.status === 'done' ? 'task-done' : ''}" data-id="${t.id}" draggable="true" ondragstart="handleTaskDragStart(event, ${t.id})" ondragend="event.currentTarget.classList.remove('dragging')"><span class="task-dot" onclick="event.stopPropagation(); toggleTaskDone(${t.id}, ${projectId})"><i data-lucide="${t.status === 'done' ? 'check-circle' : 'circle'}"></i></span><div class="task-main" onclick="openTaskForm(${projectId}, ${t.id})"><span class="task-name">${t.task}</span>${t.notes ? `<span class="task-desc">${t.notes}</span>` : ""}${t.blockers ? `<span class="task-blocker">⚠ ${t.blockers}</span>` : ""}</div><span class="task-col">${t.owner || ""}</span><span class="task-col-badge">${t.due_date || ""}</span></div>`).join("")}</div></div>`;
|
|
|
|
return `<div class="task-group"><div class="task-group-hd"><span class="task-group-icon"><i data-lucide="layers"></i></span><span class="task-group-label">${phase}</span><span class="task-group-n">${pt.length}</span></div><div class="task-group-list" data-phase="${phase}" ondrop="handleTaskDrop(event, ${projectId}, '${phase}')" ondragover="event.preventDefault(); event.currentTarget.classList.add('drag-over')" ondragleave="event.currentTarget.classList.remove('drag-over')">${pt.map((t) => `<div class="task-row ${t.status === 'done' ? 'task-done' : ''}" data-id="${t.id}" draggable="true" ondragstart="handleTaskDragStart(event, ${t.id})" ondragend="event.currentTarget.classList.remove('dragging')"><span class="task-dot" onclick="event.stopPropagation(); toggleTaskDone(${t.id}, ${projectId})"><i data-lucide="${t.status === 'done' ? 'check-circle' : 'circle'}"></i></span><span class="task-grip"><i data-lucide="grip-vertical"></i></span><div class="task-main" onclick="openTaskForm(${projectId}, ${t.id})"><span class="task-name">${t.task}</span>${t.notes ? `<span class="task-desc">${t.notes}</span>` : ""}${t.blockers ? `<span class="task-blocker">⚠ ${t.blockers}</span>` : ""}</div><span class="task-col">${t.owner || ""}</span><span class="task-col-badge">${t.due_date || ""}</span></div>`).join("")}</div></div>`;
|
|
|
|
}).join("")}
|
|
|
|
}).join("")}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="task-drawer-${projectId}" class="task-drawer">
|
|
|
|
<div id="task-drawer-${projectId}" class="task-drawer">
|
|
|
|
@@ -516,7 +518,7 @@ function openDrawer(resource, id) {
|
|
|
|
const multilineFields = ["customer_need", "current_deliverable", "risks", "next_action", "version_goal", "feature_list", "notes"];
|
|
|
|
const multilineFields = ["customer_need", "current_deliverable", "risks", "next_action", "version_goal", "feature_list", "notes"];
|
|
|
|
const followupTarget = resource === "sales" ? "sales" : resource === "proposals" ? "proposal" : resource === "operations" ? "operation" : resource === "products" ? "product" : "";
|
|
|
|
const followupTarget = resource === "sales" ? "sales" : resource === "proposals" ? "proposal" : resource === "operations" ? "operation" : resource === "products" ? "product" : "";
|
|
|
|
const title = item.target_customer || item.project_name || item.customer_or_project_name || item.product_name;
|
|
|
|
const title = item.target_customer || item.project_name || item.customer_or_project_name || item.product_name;
|
|
|
|
drawer.innerHTML = `<div class="drawer-panel"><div class="sticky top-0 z-10 flex items-center justify-between border-b border-slate-200 bg-white/95 px-5 py-3 backdrop-blur"><div><p class="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-400">Detail Drawer</p><div class="flex items-center gap-2"><h2 class="drawer-title text-[17px] font-semibold leading-6 text-slate-900">${title}</h2><span id="drawerSaveStatus" class="save-status"></span></div></div><div class="flex items-center gap-2"><button class="btn btn-ghost btn-sm text-red-600 hover:bg-red-50" onclick="deleteOperation(${id})"><i data-lucide="trash-2"></i>删除</button><button class="btn btn-ghost btn-sm" onclick="closeDrawer()">关闭</button></div></div><div class="grid gap-5 p-5">
|
|
|
|
drawer.innerHTML = `<div class="drawer-panel"><div class="sticky top-0 z-10 flex items-center justify-between border-b border-slate-200 bg-white/95 px-5 py-3 backdrop-blur"><div><p class="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-400">Detail Drawer</p><div class="flex items-center gap-2"><h2 class="drawer-title text-[17px] font-semibold leading-6 text-slate-900">${title}</h2><span id="drawerSaveStatus" class="save-status"></span></div></div><div class="flex items-center gap-2"><button class="btn btn-ghost btn-sm text-red-600 hover:bg-red-50" onclick="deleteDrawerItem('${resource}', ${id})"><i data-lucide="trash-2"></i>删除</button><button class="btn btn-ghost btn-sm" onclick="closeDrawer()">关闭</button></div></div><div class="grid gap-5 p-5">
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h3 class="drawer-section-title">属性</h3>
|
|
|
|
<h3 class="drawer-section-title">属性</h3>
|
|
|
|
<form id="drawerForm" class="drawer-fields">
|
|
|
|
<form id="drawerForm" class="drawer-fields">
|
|
|
|
@@ -618,10 +620,10 @@ function bindDrawerAutosave(resource, id, item) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.openDrawer = openDrawer;
|
|
|
|
window.openDrawer = openDrawer;
|
|
|
|
window.deleteOperation = async (id) => {
|
|
|
|
window.deleteDrawerItem = async (resource, id) => {
|
|
|
|
if (!confirm("确认删除该项目?此操作不可撤销。")) return;
|
|
|
|
if (!confirm("确认删除?此操作不可撤销。")) return;
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
await api(`/api/operations/${id}`, { method: "DELETE" });
|
|
|
|
await api(`/api/${resource}/${id}`, { method: "DELETE" });
|
|
|
|
closeDrawer();
|
|
|
|
closeDrawer();
|
|
|
|
await load();
|
|
|
|
await load();
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
|