feat: OPC 社区运营公共服务平台 Demo v1.3

一人公司/超级个体产业社区公共服务基础设施 Demo,包含:
- 18 个角色页面(运营商/OPC/企业/个人/学员/培训机构/服务机构/政府/审计/金融)
- 暖橙活力主题 UI(渐变毛玻璃 + 暖色侧栏)
- 五步运营闭环(选方向→育人才→建平台→立SOP→聚产业)
- 华强北硬件供应链接入
- 蜂群式交付 + 产业集聚效应展示
- 需求文档(REQ-001~118 全覆盖)

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-13 09:07:23 +08:00
co-authored by Devin
commit f307e877c9
30 changed files with 11397 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>培训认证 · OPC 公共服务平台 Demo</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body data-page="training">
<header class="topnav" id="topnav"></header>
<div class="container">
<div class="page-title">培训认证</div>
<div class="page-sub" id="tr-sub"></div>
<div id="learner-banner"></div>
<!-- REQ-081 闭环进度 -->
<div class="card">
<h3 id="tr-path-title">我的成长路径 <span class="req">REQ-081 · 学习—实训—认证—接单闭环</span></h3>
<div id="tr-steps"></div>
</div>
<div class="section grid grid-4" id="tr-stats"></div>
<!-- REQ-082/084 课程 -->
<div class="section">
<h2>课程中心 <span class="req">REQ-082/084 · 工具之外的职业素养</span></h2>
<div class="card">
<table id="course-tbl"></table>
<button class="btn sm" style="margin-top:10px" onclick="enroll()">+ 报名新课程</button>
</div>
</div>
<!-- REQ-083 实训 -->
<div class="section">
<div class="two-col">
<div class="card">
<h3>项目实训记录 <span class="req">REQ-083/086</span></h3>
<table id="prac-tbl"></table>
</div>
<div class="card">
<h3>我的证书 <span class="req">REQ-105/106</span></h3>
<ul class="list" id="cert-list"></ul>
<p class="muted" style="margin-top:8px">机构认证与第三方证书作为 L 级认证输入,不替代 L 级(REQ-107)。</p>
<button class="btn sm" style="margin-top:8px" onclick="verifyInput()">证书查验</button>
<button class="btn sm ghost" style="margin-top:8px" onclick="regThird()">登记第三方证书</button>
</div>
</div>
</div>
<!-- REQ-004/113 作品集(学员与 OPC 均可沉淀作品) -->
<div class="section">
<h2>我的作品 <span class="req">REQ-111/113 · 作品可上架橙果商城</span></h2>
<div class="card">
<table id="work-tbl"></table>
<button class="btn sm" style="margin-top:10px" onclick="addWork()">+ 上传作品</button>
<a class="btn sm ghost" style="margin-top:10px" href="market.html">去橙果商城看看</a>
</div>
</div>
<!-- REQ-085 认证→订单池 -->
<div class="section">
<div class="notice" id="join-notice"></div>
</div>
</div>
<div class="footer">OPC 公共服务平台 Demo · 培训认证</div>
<script src="assets/app.js"></script>
<script src="assets/store.js"></script>
<script>
function renderPage(DB) {
const T = DB.training;
const role = currentRole();
const isLearner = role === 'learner';
document.getElementById('tr-sub').textContent = isLearner
? `${T.learner.id} ${T.learner.name} · 学员档案:培训履历、认证与作品(REQ-111)`
: role === 'trainer'
? '学员侧课程与证书展示视图 · 机构管理请进入「机构管理」工作台(REQ-081)'
: '学习 — 练习 — 实训 — 认证 — 接单 闭环(REQ-081)';
// 学员转化横幅(REQ-112)
document.getElementById('learner-banner').innerHTML = isLearner ? `
<div class="notice" style="margin-bottom:16px;border-left-color:var(--primary);background:#eef2ff;color:var(--primary)">
您当前是培训学员,尚未入驻 OPC。<b>${T.learner.goal}</b><br>
<span style="font-size:12px">政策适配:可申请创业培训补贴 POLICY_0001(≤¥2,000/人,训后 90 天接单即达核销条件)</span>
<button class="btn sm" style="margin-left:10px" onclick="becomeOpc()">申请入驻成为 OPC</button>
</div>` : '';
document.getElementById('tr-path-title').innerHTML = isLearner
? '我的学员档案 <span class="req">REQ-111 · 履历—认证—作品</span>'
: '我的成长路径 <span class="req">REQ-081 · 学习—实训—认证—接单闭环</span>';
document.getElementById('tr-steps').innerHTML = STEPS(isLearner ? T.learnerSteps : T.steps);
document.getElementById('tr-stats').innerHTML = (isLearner
? T.learner.readiness.map(r => ({ label: r[0], num: '', badge: r[1], sub: '' }))
: T.stats.map(s => ({ label: s.label, num: s.num, sub: s.sub }))
).map(s => `<div class="card"><h3>${s.label}</h3>${s.badge ? B(s.badge) : `<div class="num">${s.num}</div>`}<p class="muted">${s.sub || ''}</p></div>`).join('');
renderCourses();
document.getElementById('prac-tbl').innerHTML =
`<tr><th>实训项目</th><th>验收标准</th><th>成绩</th><th>导师点评</th></tr>` +
T.practices.map(p => `<tr><td>${p.name}</td><td>${p.std}</td><td>${B(p.score)}</td><td>${p.comment}</td></tr>`).join('');
document.getElementById('cert-list').innerHTML = T.certs.map(c => LI(c.name, B(c.badge))).join('');
document.getElementById('work-tbl').innerHTML =
`<tr><th>作品编码</th><th>作品</th><th>能力标签</th><th>状态</th></tr>` +
T.works.map(w => `<tr><td>${w.code}</td><td>${w.name}</td><td>${w.tag}</td><td>${B(w.status)}</td></tr>`).join('');
document.getElementById('join-notice').textContent = isLearner
? 'REQ-112:入驻申请通过后,学员档案(培训履历、认证记录、作品)将自动带入 OPC 能力档案,进入五态入驻流程(REQ-013)。'
: 'REQ-085:通过认证后自动纳入订单池并按等级开放接单范围——L3 当前可承接:中型项目、客户沟通和协同管理类任务。';
}
function becomeOpc() {
confirmDlg('入驻成为 OPC(REQ-112)',
'将学员档案(培训履历、认证记录、4 件作品)带入 OPC 能力档案并发起入驻申请?入驻后经五态流程即可进入订单池接单。',
'发起入驻申请', () => {
DB.training.learnerSteps[4] = ['入驻成为 OPC', 'cur'];
renderPage(DB);
toast('入驻申请已提交:学员档案已带入能力档案,运营商审核中(REQ-013 五态入驻)');
});
}
let workSeq = 35;
function addWork() {
openModal({
title: '上传作品(REQ-111)',
body: `
<div class="field"><label>作品名称</label><input id="aw-n" placeholder="如:国庆海报模板包"></div>
<div class="field"><label>能力标签</label><input value="跨境电商视觉"></div>
<div class="checks"><label><input type="checkbox" checked> 原创声明 / 客户授权展示(REQ-004)</label></div>`,
actions: [
{ label: '取消', cls: 'ghost' },
{ label: '提交', onClick: m => {
const n = document.getElementById('aw-n').value.trim();
if (!n) { toast('请填写作品名称', 'warn'); return; }
m.remove();
DB.training.works.push({ code: 'WORK_00' + workSeq++, name: n, tag: '跨境电商视觉', status: ['已入档案', 'ok'] });
renderPage(DB);
toast('作品已入学员/能力档案,可进一步上架OPC橙果商城(REQ-113)');
} },
],
});
}
function renderCourses() {
document.getElementById('course-tbl').innerHTML =
`<tr><th>课程</th><th>提供方</th><th>类型</th><th>价格</th><th>进度</th><th>操作</th></tr>` +
DB.training.courses.map((c, i) => `<tr><td>${c.name}</td><td>${c.provider}</td><td>${B(c.type)}</td><td>${c.price}</td><td>${BAR(c.pct, c.pct >= 100 ? 'ok' : '')}</td><td>${courseAction(c, i)}</td></tr>`).join('');
}
function courseAction(c, i) {
if (c.action === 'study') return `<button class="btn sm" onclick="study(${i})">继续学习</button>`;
if (c.action === 'certDone') return `<button class="btn sm ghost" onclick="toast('结业证书已同步至能力档案')">结业证书</button>`;
if (c.action === 'record') return `<button class="btn sm ghost" onclick="toast('合规培训记录已同步至主体状态(REQ-015)')">培训记录</button>`;
if (c.action === 'enrolled') return `<span class="badge info">已报名待开课</span>`;
if (c.action === 'done') return `<span class="badge ok">已完课 · 待考试</span>`;
return '—';
}
function study(i) {
const c = DB.training.courses[i];
c.pct = Math.min(100, c.pct + 10);
if (c.pct >= 100) c.action = 'done';
renderCourses();
toast(c.pct >= 100 ? `「${c.name}」已完课,可预约认证考试` : `「${c.name}」学习进度 ${c.pct}%`);
}
function enroll() {
openModal({
title: '报名课程',
body: `<div class="field"><label>选择课程</label><select id="en-c">
<option>CRS_0102 AI 漫剧制作实训营 · TRN_00001 星火设计学院 · ¥2,600</option>
<option>CRS_0104 Midjourney 高阶技法 · TRN_00001 星火设计学院 · ¥980(审核中)</option>
<option>CRS_0103 设计基础公益课 · 平台共建 · 免费</option>
</select></div>
<p class="muted">收费课经第三方支付通道结算,明示退费规则(REQ-101);补贴课计入财政补贴绩效(REQ-109)。</p>`,
actions: [
{ label: '取消', cls: 'ghost' },
{ label: '去支付/报名', onClick: m => {
m.remove();
DB.training.courses.push({ code: 'CRS_0102', name: 'AI 漫剧制作实训营', provider: 'TRN_00001 星火设计学院', type: ['收费课', 'pri'], price: '¥2,600', pct: 0, action: 'enrolled' });
renderCourses();
toast('支付成功(第三方通道),已报名实训营');
} },
],
});
}
function verifyInput() {
openModal({
title: '证书查验(对外查验页演示)',
body: `<div class="field"><label>输入证书编号</label><input id="vc" placeholder="如 CERT_20260087 / CERT_20260042" value="CERT_20260042"></div>`,
actions: [
{ label: '取消', cls: 'ghost' },
{ label: '查验', onClick: m => {
const no = document.getElementById('vc').value.trim();
m.remove();
const inst = DB.institution.certs.find(c => c.no === no);
const isPlatform = no === DB.opc.cert.no;
if (inst || isPlatform) {
openModal({ title: '查验结果 · ' + no, body: `<table>
<tr><th>颁发主体</th><td>${inst ? DB.institution.code + ' ' + DB.institution.name : 'OPC 公共服务平台'}</td></tr>
<tr><th>持证人</th><td>${inst ? inst.holder : 'OPC_00017 张设计'}(脱敏)</td></tr>
<tr><th>认证项目</th><td>${inst ? inst.program : 'L3 能力认证'}</td></tr>
<tr><th>有效期</th><td>${inst ? inst.issued + ' ~ ' + inst.expire : '2026-04-01 ~ 2027-03-31'}</td></tr>
<tr><th>状态</th><td>${inst ? B(inst.status) : '<span class="badge ok">有效 · 防伪校验通过</span>'}</td></tr>
</table>` });
} else toast('未查询到该证书,请核对编号(REQ-105)', 'err');
} },
],
});
}
function regThird() {
openModal({
title: '第三方证书采认登记(REQ-106)',
body: `
<div class="field"><label>证书类型</label><select><option>人社职业技能等级证书</option><option>行业/厂商认证</option></select></div>
<div class="field"><label>证书名称与编号</label><input placeholder="如:电子商务师三级 / 2203xxxxxx"></div>
<div class="field"><label>证明材料</label><input type="file"></div>`,
actions: [
{ label: '取消', cls: 'ghost' },
{ label: '提交核验', onClick: m => { m.remove(); toast('已提交核验,通过后计入能力档案并作为 L 级认证输入'); } },
],
});
}
</script>
</body>
</html>