feat: OPC 平台功能增强 v1.4 · 8 项需求变更实施

CHG-006 抢单订单池:OPC 工作台加抢单池(REQ-128/129)
CHG-004 政策落地:OPC 加算力券申领/核销+贷款+租金,运营商加政策跟踪(REQ-124/125/126)
CHG-001 组队模块:新增 team.html 组队协同页(REQ-119/120/121)
CHG-003 活动管理:运营工作台加活动创建/发布/报名管理(REQ-122/123)
CHG-005 OPC 财务分析:OPC 工作台加财务看板(REQ-127)
CHG-008 平台撮合归集:运营工作台加录入入口(REQ-130)
CHG-002 入口跳转:确认已正确(OPC→opc.html)
CHG-007 服务中心定位:明确为运营商服务资源管理

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
2026-09-14 08:47:29 +08:00
co-authored by Devin
parent f307e877c9
commit 13cad398dc
10 changed files with 1009 additions and 2 deletions
+119
View File
@@ -51,6 +51,50 @@
<div class="card"><table id="pool-tbl"></table></div>
</div>
<!-- REQ-128 抢单订单池 -->
<div class="section">
<h2>抢单订单池 <span class="req">REQ-128/129 · 标准化任务公开抢单 · 先到先得</span></h2>
<div class="card"><table id="grab-tbl"></table></div>
<p class="muted" style="margin-top:8px">运营商将标准化任务放入抢单池,OPC 按 SOP 标准自主抢单;抢单后锁定 30 分钟内确认接单,超时释放回池。</p>
</div>
<!-- REQ-124/125 政策落地:算力券 + 贷款 + 租金 -->
<div class="section">
<h2>政策落地 · 算力券 / 贷款 / 租金 <span class="req">REQ-124/125 · 全链路可见</span></h2>
<div class="three-col" style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px">
<div class="card">
<h3>算力券申领与核销 <span class="req">REQ-124</span></h3>
<p class="muted">余额 <b id="vc-bal">¥8,000</b> / ¥10,000</p>
<table id="vc-tbl" style="margin-top:8px"></table>
<button class="btn sm" style="margin-top:8px" onclick="applyCompute()">申领算力券</button>
</div>
<div class="card">
<h3>贷款支持 <span class="req">REQ-125</span></h3>
<table id="loan-tbl"></table>
<button class="btn sm" style="margin-top:8px" onclick="applyLoan()">申请贷款</button>
</div>
<div class="card">
<h3>租金补贴</h3>
<table id="rent-tbl"></table>
</div>
</div>
</div>
<!-- REQ-127 OPC 财务分析 -->
<div class="section">
<h2>财务分析 <span class="req">REQ-127 · 接单收入 - 算力 - 社保 = 净收益</span></h2>
<div class="two-col">
<div class="card">
<h3>本月收支概览</h3>
<table id="fin-tbl"></table>
</div>
<div class="card">
<h3>财务建议</h3>
<ul class="list" id="fin-suggest"></ul>
</div>
</div>
</div>
<!-- REQ-043/046 收入与结算 -->
<div class="section">
<h2>收入与结算 <span class="req">REQ-043/046 · 分账核算 + 保证金托管</span></h2>
@@ -118,6 +162,9 @@ function renderPage(DB) {
<p class="muted" style="margin-top:6px">${c.next}</p>`;
renderPool();
renderGrab();
renderPolicySupport();
renderFinance();
document.getElementById('stl-summary').innerHTML = O.settlement.summary.map(s => LI(s[0], s[1])).join('');
document.getElementById('stl-tbl').innerHTML =
`<tr><th>结算单</th><th>关联订单</th><th>金额</th><th>状态</th></tr>` +
@@ -252,6 +299,78 @@ function verifyCert() {
</table>`,
});
}
/* === 抢单订单池 === */
function renderGrab() {
const pool = DB.opc.grabPool || [];
document.getElementById('grab-tbl').innerHTML =
`<tr><th>订单号</th><th>类型</th><th>交付物</th><th>预算</th><th>截止</th><th>SOP</th><th>已抢单/上限</th><th>状态</th><th>操作</th></tr>` +
pool.map((o, i) => `<tr>
<td>${o.id}</td><td>${B(o.type)}</td><td>${o.items}</td><td>${o.budget}</td><td>${o.ddl}</td>
<td><code>${o.sop}</code></td><td>${o.count}</td><td>${B(o.status)}</td>
<td>${o.status[1] === 'ok' ? `<button class="btn sm" onclick="grabOrder(${i})">立即抢单</button>` : '<span class="muted">已满</span>'}</td>
</tr>`).join('');
}
function grabOrder(i) {
const o = DB.opc.grabPool[i];
confirmDlg('抢单确认', `${o.items} · ${o.budget} · 截止 ${o.ddl}。抢单后 30 分钟内确认接单,超时释放。`, '确认抢单', () => {
toast('抢单成功!请在 30 分钟内确认接单,否则任务释放回池');
o.count = o.count.split('/')[0] + '/' + o.count.split('/')[1];
const cur = parseInt(o.count) + 1;
o.count = cur + '/' + o.count.split('/')[1];
if (cur >= parseInt(o.count.split('/')[1])) o.status = ['已满', 'gray'];
renderGrab();
});
}
/* === 政策落地 === */
function renderPolicySupport() {
const P = DB.opc.policySupport;
if (!P) return;
// 算力券
document.getElementById('vc-bal').textContent = `¥${P.compute.balance.toLocaleString()}`;
document.getElementById('vc-tbl').innerHTML =
`<tr><th>券号</th><th>金额</th><th>关联</th><th>状态</th></tr>` +
P.compute.records.map(r => `<tr><td>${r.code}</td><td>${r.amount}</td><td class="muted">${r.target}</td><td>${B(r.status)}</td></tr>`).join('');
// 贷款
document.getElementById('loan-tbl').innerHTML =
`<tr><th>编号</th><th>类型</th><th>金额</th><th>利率</th><th>状态</th></tr>` +
P.loan.records.map(r => `<tr><td>${r.code}</td><td>${r.type}</td><td>${r.amount}</td><td>${r.rate}</td><td>${B(r.status)}</td></tr>`).join('');
// 租金
document.getElementById('rent-tbl').innerHTML =
`<tr><th>编号</th><th>类型</th><th>金额</th><th>状态</th></tr>` +
P.rent.records.map(r => `<tr><td>${r.code}</td><td>${r.type}</td><td>${r.amount}</td><td>${B(r.status)}</td></tr>`).join('');
}
function applyCompute() {
confirmDlg('申领算力券', '按细则可申领 ≤¥10,000/年。当前余额 ¥8,000,可追加申领 ¥2,000。', '确认申领', () => {
toast('算力券申领已提交,经信局审核后发放至券台账');
});
}
function applyLoan() {
openModal({
title: '申请贷款支持',
body: `<div class="field"><label>贷款类型</label><select><option>创业担保贷款(政府贴息 2%)</option><option>算力信用贷(年化 3.85%)</option><option>经营性贷款(年化 4.5%)</option></select></div>
<div class="field-row"><div class="field"><label>申请额度</label><input placeholder="如:¥20 万"></div>
<div class="field"><label>期限</label><select><option>6 个月</option><option selected>12 个月</option><option>24 个月</option></select></div></div>
<p class="muted">贷款申请将关联 OPC 信用档案与订单收入流水,运营商初审后推送合作金融机构。</p>`,
actions: [
{ label: '取消', cls: 'ghost' },
{ label: '提交申请', onClick: m => { m.remove(); toast('贷款申请已提交,运营商初审中'); } },
],
});
}
/* === 财务分析 === */
function renderFinance() {
const F = DB.opc.finance;
if (!F) return;
document.getElementById('fin-tbl').innerHTML =
`<tr><th>项目</th><th>金额</th><th>占比</th><th>说明</th></tr>` +
`<tr><td><b>接单收入</b></td><td><b>${F.income.total}</b></td><td>—</td><td>${F.income.orders} 单 · 均 ${F.income.avg}</td></tr>` +
F.costs.map(c => `<tr><td>${c.item}</td><td style="color:var(--danger)">-${c.amount}</td><td>${c.pct || '—'}%</td><td class="muted">${c.detail}</td></tr>`).join('') +
`<tr style="border-top:2px solid var(--primary)"><td><b>净收益</b></td><td><b style="color:var(--success)">${F.net}</b></td><td>—</td><td>税后</td></tr>`;
document.getElementById('fin-suggest').innerHTML = F.suggestions.map(s => `<li><span>${s}</span></li>`).join('');
}
</script>
</body>
</html>