Files
OPC/Demo/training.html
T
freedakandDevin f307e877c9 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>
2026-09-13 09:07:23 +08:00

224 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>