feat(web-console): add personalized seat weighing evolution
This commit is contained in:
@@ -1364,543 +1364,6 @@
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
/* Self-evolution journal ------------------------------------------------- */
|
||||
|
||||
.console-v2 .v2-self-evolution {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.console-v2[data-module="self-evolution"] .main-panel {
|
||||
grid-template-rows: 36px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.console-v2[data-module="self-evolution"] .v2-operation-shell,
|
||||
.console-v2[data-module="self-evolution"] .v2-output-shell {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.console-v2[data-module="self-evolution"] .v2-self-evolution {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: block;
|
||||
grid-row: 2;
|
||||
overflow: auto;
|
||||
background:
|
||||
radial-gradient(circle at 10% 0%, color-mix(in srgb, var(--v2-accent-soft) 68%, white), transparent 27%),
|
||||
var(--v2-surface);
|
||||
border: 1px solid color-mix(in srgb, var(--v2-accent) 14%, transparent);
|
||||
border-radius: clamp(17px, 1.4vw, 24px);
|
||||
box-shadow: var(--v2-shadow);
|
||||
}
|
||||
|
||||
.v2-evolution-shell {
|
||||
min-width: 820px;
|
||||
min-height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(330px, 1fr) auto;
|
||||
}
|
||||
|
||||
.v2-evolution-overview {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(300px, 1fr) auto;
|
||||
align-items: start;
|
||||
gap: clamp(20px, 3vw, 48px);
|
||||
padding: var(--v2-module-title-block) var(--v2-module-title-inline);
|
||||
border-bottom: 1px solid var(--v2-line);
|
||||
}
|
||||
|
||||
.v2-evolution-heading .section-kicker {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: var(--v2-accent-strong);
|
||||
font-size: 9px;
|
||||
font-weight: 850;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
|
||||
.v2-evolution-heading .section-kicker svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.v2-evolution-heading h1 {
|
||||
margin: 8px 0 0;
|
||||
color: var(--v2-ink);
|
||||
font-size: clamp(24px, 2vw, 34px);
|
||||
line-height: 1.06;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
.v2-evolution-heading > p {
|
||||
margin: 9px 0 0;
|
||||
color: var(--v2-muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.v2-evolution-summary {
|
||||
min-width: 430px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.25fr repeat(3, 1fr);
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 38%, white);
|
||||
border: 1px solid color-mix(in srgb, var(--v2-accent) 13%, transparent);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.v2-evolution-summary > div {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
align-content: center;
|
||||
padding: 11px 14px;
|
||||
border-left: 1px solid var(--v2-line);
|
||||
}
|
||||
|
||||
.v2-evolution-summary > div:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.v2-evolution-summary dt,
|
||||
.v2-evolution-summary small {
|
||||
color: var(--v2-muted);
|
||||
font-size: 8px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.v2-evolution-summary dd {
|
||||
margin: 2px 0 0;
|
||||
color: var(--v2-ink);
|
||||
font: 850 19px/1.1 "SFMono-Regular", Consolas, "Microsoft YaHei", monospace;
|
||||
}
|
||||
|
||||
.v2-evolution-summary .v2-evolution-current dd {
|
||||
color: var(--v2-accent-strong);
|
||||
}
|
||||
|
||||
.v2-evolution-workspace {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: grid;
|
||||
grid-template-columns: clamp(225px, 21vw, 290px) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.v2-evolution-timeline {
|
||||
min-width: 0;
|
||||
padding: 20px 18px 18px 24px;
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 13%, white);
|
||||
border-right: 1px solid var(--v2-line);
|
||||
}
|
||||
|
||||
.v2-evolution-timeline-heading {
|
||||
padding: 0 8px 12px 15px;
|
||||
}
|
||||
|
||||
.v2-evolution-timeline-heading span,
|
||||
.v2-evolution-next small {
|
||||
display: block;
|
||||
color: var(--v2-accent-strong);
|
||||
font-size: 8px;
|
||||
font-weight: 850;
|
||||
letter-spacing: 0.16em;
|
||||
}
|
||||
|
||||
.v2-evolution-timeline-heading strong {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--v2-ink);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.v2-evolution-version-list {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.v2-evolution-version-list::before {
|
||||
width: 1px;
|
||||
position: absolute;
|
||||
inset: 11px auto 11px 6px;
|
||||
content: "";
|
||||
background: color-mix(in srgb, var(--v2-accent) 24%, transparent);
|
||||
}
|
||||
|
||||
.v2-evolution-version {
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 2px 10px;
|
||||
padding: 8px 9px 8px 25px;
|
||||
color: var(--v2-muted);
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: var(--v2-sidebar-card-radius);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.v2-evolution-version::before {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 3px;
|
||||
z-index: 1;
|
||||
content: "";
|
||||
background: white;
|
||||
border: 2px solid color-mix(in srgb, var(--v2-accent) 42%, #a8b4b4);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.v2-evolution-version:hover {
|
||||
color: var(--v2-ink);
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 38%, transparent);
|
||||
}
|
||||
|
||||
.v2-evolution-version.active {
|
||||
color: var(--v2-accent-strong);
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 72%, white);
|
||||
}
|
||||
|
||||
.v2-evolution-version.active::before {
|
||||
background: var(--v2-accent);
|
||||
border-color: white;
|
||||
box-shadow: 0 0 0 4px color-mix(in srgb, var(--v2-accent) 14%, transparent);
|
||||
}
|
||||
|
||||
.v2-evolution-version > span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.v2-evolution-version strong {
|
||||
color: currentColor;
|
||||
font: 850 12px/1.2 "SFMono-Regular", Consolas, monospace;
|
||||
}
|
||||
|
||||
.v2-evolution-version time,
|
||||
.v2-evolution-version small {
|
||||
color: var(--v2-muted);
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.v2-evolution-version b {
|
||||
grid-column: 1 / -1;
|
||||
overflow: hidden;
|
||||
color: var(--v2-ink);
|
||||
font-size: 11px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-evolution-version small {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
align-self: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-evolution-detail {
|
||||
min-width: 0;
|
||||
padding: clamp(20px, 2.2vw, 32px) clamp(24px, 2.7vw, 42px) 20px;
|
||||
}
|
||||
|
||||
.v2-evolution-detail-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.v2-evolution-detail-heading > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.v2-evolution-status {
|
||||
padding: 4px 9px;
|
||||
color: var(--v2-accent-strong);
|
||||
background: var(--v2-accent-soft);
|
||||
border-radius: 999px;
|
||||
font-size: 9px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.v2-evolution-detail-version {
|
||||
color: var(--v2-muted);
|
||||
font: 800 11px/1 "SFMono-Regular", Consolas, monospace;
|
||||
}
|
||||
|
||||
.v2-evolution-detail-heading time {
|
||||
color: var(--v2-muted);
|
||||
font: 750 10px/1 "SFMono-Regular", Consolas, monospace;
|
||||
}
|
||||
|
||||
.v2-evolution-detail > h2 {
|
||||
margin: 12px 0 0;
|
||||
color: var(--v2-ink);
|
||||
font-size: clamp(20px, 1.7vw, 28px);
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.035em;
|
||||
}
|
||||
|
||||
.v2-evolution-lead {
|
||||
max-width: 860px;
|
||||
margin: 7px 0 0;
|
||||
color: var(--v2-muted);
|
||||
font-size: 11px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.v2-evolution-detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(240px, 0.92fr);
|
||||
gap: clamp(24px, 3vw, 48px);
|
||||
margin-top: 18px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--v2-line);
|
||||
}
|
||||
|
||||
.v2-evolution-detail-grid h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
margin: 0 0 12px;
|
||||
color: var(--v2-ink);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.v2-evolution-detail-grid h3 svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--v2-accent);
|
||||
}
|
||||
|
||||
.v2-evolution-changes > div {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.v2-evolution-changes > div > div {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 22px minmax(0, 1fr);
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.v2-evolution-changes > div > div > svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
padding: 5px;
|
||||
color: var(--v2-accent-strong);
|
||||
background: var(--v2-accent-soft);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.v2-evolution-changes p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.v2-evolution-changes p strong,
|
||||
.v2-evolution-changes p span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.v2-evolution-changes p strong {
|
||||
color: var(--v2-ink);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.v2-evolution-changes p span {
|
||||
margin-top: 2px;
|
||||
color: var(--v2-muted);
|
||||
font-size: 9px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.v2-evolution-evidence > div {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.v2-evolution-metric > div {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.v2-evolution-metric > div > span {
|
||||
overflow: hidden;
|
||||
color: var(--v2-ink);
|
||||
font-size: 9px;
|
||||
font-weight: 750;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-evolution-metric p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-evolution-metric del {
|
||||
color: #98a5a6;
|
||||
font-size: 8px;
|
||||
text-decoration-thickness: 1px;
|
||||
}
|
||||
|
||||
.v2-evolution-metric p svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
color: var(--v2-muted);
|
||||
}
|
||||
|
||||
.v2-evolution-metric p strong {
|
||||
color: var(--v2-accent-strong);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.v2-evolution-progress {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
overflow: hidden;
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 50%, #e7eceb);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.v2-evolution-progress i {
|
||||
width: var(--evolution-progress);
|
||||
height: 100%;
|
||||
display: block;
|
||||
background: linear-gradient(90deg, color-mix(in srgb, var(--v2-accent) 55%, white), var(--v2-accent));
|
||||
border-radius: inherit;
|
||||
transition: width 220ms ease;
|
||||
}
|
||||
|
||||
.v2-evolution-validation {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-top: 18px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid var(--v2-line);
|
||||
}
|
||||
|
||||
.v2-evolution-module-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.v2-evolution-module-tags span,
|
||||
.v2-evolution-next-steps span {
|
||||
padding: 4px 8px;
|
||||
color: var(--v2-accent-strong);
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 62%, white);
|
||||
border: 1px solid color-mix(in srgb, var(--v2-accent) 12%, transparent);
|
||||
border-radius: 999px;
|
||||
font-size: 8px;
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-evolution-validation > p {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 7px;
|
||||
margin: 0;
|
||||
color: var(--v2-muted);
|
||||
font-size: 9px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.v2-evolution-validation > p svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
flex: 0 0 auto;
|
||||
color: var(--v2-accent);
|
||||
}
|
||||
|
||||
.console-v2 [data-evolution-scope="admin"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.console-v2[data-demo-role="admin"] [data-evolution-scope="user"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.console-v2[data-demo-role="admin"] [data-evolution-scope="admin"] {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.v2-evolution-next {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 38px minmax(280px, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 13px;
|
||||
padding: 13px clamp(22px, 2.2vw, 34px);
|
||||
background: color-mix(in srgb, var(--v2-accent-soft) 58%, white);
|
||||
border-top: 1px solid color-mix(in srgb, var(--v2-accent) 15%, transparent);
|
||||
}
|
||||
|
||||
.v2-evolution-next-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: white;
|
||||
background: var(--v2-accent);
|
||||
border-radius: 11px;
|
||||
box-shadow: 0 7px 18px color-mix(in srgb, var(--v2-accent) 22%, transparent);
|
||||
}
|
||||
|
||||
.v2-evolution-next-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.v2-evolution-next strong {
|
||||
display: inline-block;
|
||||
margin-top: 2px;
|
||||
color: var(--v2-ink);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.v2-evolution-next p {
|
||||
display: inline;
|
||||
margin: 0 0 0 8px;
|
||||
color: var(--v2-muted);
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.v2-evolution-next-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.v2-evolution-next-steps svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
color: var(--v2-accent);
|
||||
}
|
||||
|
||||
.console-v2 .v2-inline-gesture .v2-camera-toggle {
|
||||
width: 42px;
|
||||
|
||||
+42
-211
@@ -504,215 +504,6 @@
|
||||
mainPanel.prepend(switcher);
|
||||
}
|
||||
|
||||
function buildEvolutionJournal(mainPanel) {
|
||||
const evolutionEntries = [
|
||||
{
|
||||
version: "v0.5",
|
||||
date: "2026.08.12",
|
||||
status: "当前版本",
|
||||
title: "统一主题与权限体验",
|
||||
summary: "把权限、主题与核心交互收束到同一套体验规则,让不同模块看起来、用起来都属于同一个系统。",
|
||||
changes: [
|
||||
["主题统一", "自扩展固定使用 user 绿色主题,避免权限语义混用。"],
|
||||
["孪生同步", "3D 模型渲染、玩具动作和车端动作统一跟随当前主题。"],
|
||||
["能力扩充", "新增自进化入口,让 user 与 admin 共同查看演进过程。"],
|
||||
],
|
||||
modules: ["自扩展", "数字孪生", "权限体系"],
|
||||
metrics: [
|
||||
["主题一致性", "72%", "100%", 100],
|
||||
["可见能力模块", "4 个", "5 个", 92],
|
||||
["待修正视觉项", "4 项", "0 项", 100],
|
||||
],
|
||||
userValidation: "核心板块已完成主题复核,权限色与交互反馈保持一致。",
|
||||
adminValidation: "前端契约 11/11 · 自标定/自修复检查 8/8",
|
||||
},
|
||||
{
|
||||
version: "v0.4",
|
||||
date: "2026.08.11",
|
||||
status: "稳定版本",
|
||||
title: "自修复闭环增强",
|
||||
summary: "从发现异常走向诊断、修复与验证闭环,同时补齐离线场景的默认内容,确保功能状态始终可见。",
|
||||
changes: [
|
||||
["故障覆盖", "补充六类量产故障用例,覆盖更真实的车端异常。"],
|
||||
["修复闭环", "形成发现、定位、决策、执行、验证五阶段流程。"],
|
||||
["离线韧性", "服务暂时离线时仍保留默认案例和演示路径。"],
|
||||
],
|
||||
modules: ["自修复", "故障诊断", "离线体验"],
|
||||
metrics: [
|
||||
["故障用例", "2 类", "6 类", 86],
|
||||
["修复阶段", "3 阶段", "5 阶段", 92],
|
||||
["离线可用性", "空白", "可演示", 100],
|
||||
],
|
||||
userValidation: "断开服务后仍可理解自修复能力,并可继续浏览默认案例。",
|
||||
adminValidation: "默认数据回退、流程状态与接口异常路径均已验证。",
|
||||
},
|
||||
{
|
||||
version: "v0.3",
|
||||
date: "2026.08.10",
|
||||
status: "能力升级",
|
||||
title: "外设与车端统一编排",
|
||||
summary: "将玩具动作、车端设备动作与数字孪生放进同一个控制流程,减少跨区域理解和操作成本。",
|
||||
changes: [
|
||||
["动作融合", "玩具动作与车端设备动作进入统一动作界面。"],
|
||||
["布局优化", "孪生界面位于左侧,动作界面固定在右侧。"],
|
||||
["映射精简", "移除重复外框,让实时映射关系更直接。"],
|
||||
],
|
||||
modules: ["自扩展", "外设学习", "整车控制"],
|
||||
metrics: [
|
||||
["动作入口", "2 处", "1 处", 100],
|
||||
["映射层级", "3 层", "2 层", 88],
|
||||
["协同对象", "玩具", "玩具 + 车端", 94],
|
||||
],
|
||||
userValidation: "动作与孪生左右分区清晰,学习结果可以直接用于统一控制。",
|
||||
adminValidation: "外设连接状态、动作映射与车端执行链路保持独立可追踪。",
|
||||
},
|
||||
{
|
||||
version: "v0.2",
|
||||
date: "2026.08.09",
|
||||
status: "体系建立",
|
||||
title: "多权限主题体系",
|
||||
summary: "建立 user 与 admin 的视觉身份,使权限差异不只体现在可用功能,也能被用户直观感知。",
|
||||
changes: [
|
||||
["角色主题", "user 使用绿色,admin 使用蓝色,形成稳定的权限认知。"],
|
||||
["能力边界", "按角色控制模块入口,同时保留清晰的切换反馈。"],
|
||||
["模型联动", "数字孪生模型与界面主题保持同色语义。"],
|
||||
],
|
||||
modules: ["权限体系", "主题系统", "数字孪生"],
|
||||
metrics: [
|
||||
["角色主题", "1 套", "2 套", 100],
|
||||
["权限反馈", "文字", "视觉 + 文字", 90],
|
||||
["主题联动", "界面", "界面 + 模型", 96],
|
||||
],
|
||||
userValidation: "绿色主题贯穿 user 可用能力,角色身份更加明确。",
|
||||
adminValidation: "角色切换事件可同步驱动界面变量与模型渲染参数。",
|
||||
},
|
||||
{
|
||||
version: "v0.1",
|
||||
date: "2026.08.08",
|
||||
status: "起点版本",
|
||||
title: "整车认知控制台诞生",
|
||||
summary: "完成从自然语言意图到整车状态回读的第一条闭环,建立后续能力持续进化的共同底座。",
|
||||
changes: [
|
||||
["自然语言控制", "用贴近日常表达的指令控制座舱功能。"],
|
||||
["整车孪生", "把座椅、空调、车窗等状态集中映射到一个界面。"],
|
||||
["执行回读", "串联 CAN 计划、设备执行与状态反馈。"],
|
||||
],
|
||||
modules: ["自编排", "数字孪生", "CAN 执行"],
|
||||
metrics: [
|
||||
["能力模块", "0 个", "1 个", 100],
|
||||
["控制链路", "分散", "闭环", 92],
|
||||
["状态视图", "单设备", "整车", 96],
|
||||
],
|
||||
userValidation: "自然语言、执行过程与结果反馈首次形成连续体验。",
|
||||
adminValidation: "意图解析、CAN 编译计划与硬件回读具备完整追踪链路。",
|
||||
},
|
||||
];
|
||||
|
||||
const versionButtons = evolutionEntries.map((entry, index) => `
|
||||
<button type="button" class="v2-evolution-version${index === 0 ? " active" : ""}"
|
||||
data-evolution-index="${index}" aria-pressed="${index === 0 ? "true" : "false"}">
|
||||
<span><strong>${entry.version}</strong><time datetime="${entry.date.replaceAll(".", "-")}">${entry.date.slice(5)}</time></span>
|
||||
<b>${entry.title}</b>
|
||||
<small>${entry.status}</small>
|
||||
</button>`).join("");
|
||||
|
||||
const evolution = make("section", "v2-self-evolution", `
|
||||
<div class="v2-evolution-shell">
|
||||
<header class="v2-evolution-overview">
|
||||
<div class="v2-evolution-heading v2-module-heading">
|
||||
<span class="section-kicker">SELF-EVOLUTION</span>
|
||||
<h1>能力进化日志</h1>
|
||||
<p>记录每次问题发现、能力升级与验证结果,见证系统持续进化。</p>
|
||||
</div>
|
||||
<dl class="v2-evolution-summary" aria-label="当前进化概览">
|
||||
<div class="v2-evolution-current"><dt>当前版本</dt><dd>v0.5</dd><small>能力融合期</small></div>
|
||||
<div><dt>累计迭代</dt><dd>5</dd><small>个版本</small></div>
|
||||
<div><dt>能力改进</dt><dd>12</dd><small>项记录</small></div>
|
||||
<div><dt>最近更新</dt><dd>08.12</dd><small>2026</small></div>
|
||||
</dl>
|
||||
</header>
|
||||
|
||||
<div class="v2-evolution-workspace">
|
||||
<aside class="v2-evolution-timeline" aria-label="版本进化时间轴">
|
||||
<div class="v2-evolution-timeline-heading">
|
||||
<span>EVOLUTION PATH</span>
|
||||
<strong>从起点到现在</strong>
|
||||
</div>
|
||||
<div class="v2-evolution-version-list">${versionButtons}</div>
|
||||
</aside>
|
||||
|
||||
<article class="v2-evolution-detail" id="v2EvolutionDetail" aria-live="polite">
|
||||
<header class="v2-evolution-detail-heading">
|
||||
<div>
|
||||
<span class="v2-evolution-status" id="v2EvolutionStatus"></span>
|
||||
<span class="v2-evolution-detail-version" id="v2EvolutionVersion"></span>
|
||||
</div>
|
||||
<time id="v2EvolutionDate"></time>
|
||||
</header>
|
||||
<h2 id="v2EvolutionTitle"></h2>
|
||||
<p class="v2-evolution-lead" id="v2EvolutionLead"></p>
|
||||
<div class="v2-evolution-detail-grid">
|
||||
<section class="v2-evolution-changes" aria-labelledby="v2EvolutionChangesTitle">
|
||||
<h3 id="v2EvolutionChangesTitle"><i data-lucide="git-commit-horizontal"></i> 本次进化</h3>
|
||||
<div id="v2EvolutionChangeList"></div>
|
||||
</section>
|
||||
<section class="v2-evolution-evidence" aria-labelledby="v2EvolutionEvidenceTitle">
|
||||
<h3 id="v2EvolutionEvidenceTitle"><i data-lucide="trending-up"></i> 变好的证据</h3>
|
||||
<div id="v2EvolutionMetrics"></div>
|
||||
</section>
|
||||
</div>
|
||||
<footer class="v2-evolution-validation">
|
||||
<div class="v2-evolution-module-tags" id="v2EvolutionModules" aria-label="关联模块"></div>
|
||||
<p><i data-lucide="badge-check"></i><span data-evolution-scope="user" id="v2EvolutionUserValidation"></span><span data-evolution-scope="admin" id="v2EvolutionAdminValidation"></span></p>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<footer class="v2-evolution-next">
|
||||
<span class="v2-evolution-next-icon"><i data-lucide="flag"></i></span>
|
||||
<div><small>NEXT EVOLUTION · v0.6</small><strong>自主评估闭环</strong><p>让系统主动发现能力退化,对比历史基线,并形成下一轮优化建议。</p></div>
|
||||
<div class="v2-evolution-next-steps" aria-label="下一版本计划"><span>自动复盘</span><i data-lucide="arrow-right"></i><span>对比基线</span><i data-lucide="arrow-right"></i><span>形成建议</span></div>
|
||||
</footer>
|
||||
</div>`);
|
||||
evolution.id = "v2SelfEvolution";
|
||||
evolution.setAttribute("aria-label", "自进化能力日志");
|
||||
mainPanel.append(evolution);
|
||||
|
||||
const renderEntry = (index) => {
|
||||
const entry = evolutionEntries[index];
|
||||
if (!entry) return;
|
||||
$("#v2EvolutionStatus", evolution).textContent = entry.status;
|
||||
$("#v2EvolutionVersion", evolution).textContent = entry.version;
|
||||
const date = $("#v2EvolutionDate", evolution);
|
||||
date.textContent = entry.date;
|
||||
date.dateTime = entry.date.replaceAll(".", "-");
|
||||
$("#v2EvolutionTitle", evolution).textContent = entry.title;
|
||||
$("#v2EvolutionLead", evolution).textContent = entry.summary;
|
||||
$("#v2EvolutionChangeList", evolution).innerHTML = entry.changes.map(([label, copy]) => `
|
||||
<div><i data-lucide="check"></i><p><strong>${label}</strong><span>${copy}</span></p></div>`).join("");
|
||||
$("#v2EvolutionMetrics", evolution).innerHTML = entry.metrics.map(([label, before, after, progress]) => `
|
||||
<div class="v2-evolution-metric">
|
||||
<div><span>${label}</span><p><del>${before}</del><i data-lucide="arrow-right"></i><strong>${after}</strong></p></div>
|
||||
<span class="v2-evolution-progress" style="--evolution-progress: ${progress}%"><i></i></span>
|
||||
</div>`).join("");
|
||||
$("#v2EvolutionModules", evolution).innerHTML = entry.modules
|
||||
.map((module) => `<span>${module}</span>`).join("");
|
||||
$("#v2EvolutionUserValidation", evolution).textContent = entry.userValidation;
|
||||
$("#v2EvolutionAdminValidation", evolution).textContent = entry.adminValidation;
|
||||
$$("[data-evolution-index]", evolution).forEach((button, buttonIndex) => {
|
||||
const active = buttonIndex === index;
|
||||
button.classList.toggle("active", active);
|
||||
button.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
};
|
||||
|
||||
$(".v2-evolution-version-list", evolution)?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-evolution-index]");
|
||||
if (button) renderEntry(Number(button.dataset.evolutionIndex));
|
||||
});
|
||||
renderEntry(0);
|
||||
}
|
||||
|
||||
function buildOutput(mainPanel) {
|
||||
const output = make("section", "v2-output-shell", `
|
||||
@@ -863,6 +654,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
function syncSidebarControlForModule(evolutionView = window.CognitiveAgentSelfEvolution?.getView?.()) {
|
||||
const evolutionMode = ui.activeModule === "self-evolution";
|
||||
const caption = $(".v2-control-section > .v2-field-caption");
|
||||
if (caption) caption.textContent = evolutionMode ? "进化视图" : "控制方式";
|
||||
const definitions = evolutionMode
|
||||
? [
|
||||
{ label: "座椅称重进化", icon: "scale", active: evolutionView !== "system" },
|
||||
{ label: "系统能力日志", icon: "scroll-text", active: evolutionView === "system" },
|
||||
]
|
||||
: [
|
||||
{ label: "自然语言控制", icon: "message-square-text", active: ui.controlMode === "natural" },
|
||||
{ label: "手势控制", icon: "scan-face", active: ui.controlMode === "gesture" },
|
||||
];
|
||||
$$('[data-v2-control]').forEach((button, index) => {
|
||||
const definition = definitions[index];
|
||||
if (!definition) return;
|
||||
const label = $("span", button);
|
||||
const icon = $("svg, i", button);
|
||||
if (label) label.textContent = definition.label;
|
||||
if (icon) icon.setAttribute("data-lucide", definition.icon);
|
||||
button.classList.toggle("active", definition.active);
|
||||
button.setAttribute("aria-pressed", String(definition.active));
|
||||
});
|
||||
const switcher = $(".v2-control-switch");
|
||||
switcher?.setAttribute("aria-label", evolutionMode ? "自进化视图" : "控制方式");
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
}
|
||||
|
||||
function setModule(module, { persist = true } = {}) {
|
||||
if (["self-calibration", "self-repair"].includes(module) && ui.role !== "admin") {
|
||||
setRole("admin", { allowModuleTransition: true });
|
||||
@@ -897,6 +716,7 @@
|
||||
} else {
|
||||
setControlMode(ui.controlMode, { triggerGesture: false });
|
||||
}
|
||||
syncSidebarControlForModule();
|
||||
window.dispatchEvent(new CustomEvent("cognitive-agent:module-change", {
|
||||
detail: { module: ui.activeModule },
|
||||
}));
|
||||
@@ -1059,7 +879,18 @@
|
||||
function bindEvents() {
|
||||
$("#v2RoleSelect")?.addEventListener("change", (event) => setRole(event.target.value));
|
||||
$$('[data-v2-control]').forEach((button) => {
|
||||
button.addEventListener("click", () => setControlMode(button.dataset.v2Control));
|
||||
button.addEventListener("click", () => {
|
||||
if (ui.activeModule === "self-evolution") {
|
||||
const view = button.dataset.v2Control === "gesture" ? "system" : "weighing";
|
||||
window.CognitiveAgentSelfEvolution?.setView?.(view);
|
||||
syncSidebarControlForModule(view);
|
||||
return;
|
||||
}
|
||||
setControlMode(button.dataset.v2Control);
|
||||
});
|
||||
});
|
||||
window.addEventListener("cognitive-agent:evolution-view-change", (event) => {
|
||||
if (ui.activeModule === "self-evolution") syncSidebarControlForModule(event.detail?.view);
|
||||
});
|
||||
$$('.v2-module-tab').forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
@@ -1159,7 +990,7 @@
|
||||
gestureDrawer,
|
||||
);
|
||||
buildModuleSwitcher(mainPanel);
|
||||
buildEvolutionJournal(mainPanel);
|
||||
window.CognitiveAgentSelfEvolution?.mount(mainPanel);
|
||||
|
||||
$(".pipeline", mainPanel)?.classList.add("v2-legacy-hidden");
|
||||
$(".topbar")?.classList.add("v2-legacy-hidden");
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
<link rel="icon" href="/icons/app-icon-192.png" />
|
||||
<title>智舱随行 · 整车智能控制台</title>
|
||||
<link rel="stylesheet" href="/styles.css?v=dual-seat-9" />
|
||||
<link rel="stylesheet" href="/console-v2.css?v=desktop-refactor-106" />
|
||||
<link rel="stylesheet" href="/console-v2.css?v=desktop-refactor-107" />
|
||||
<link rel="stylesheet" href="/self-evolution.css?v=seat-weighing-5" />
|
||||
<link rel="stylesheet" href="/self-extension.css?v=extension-19" />
|
||||
<link rel="stylesheet" href="/self-calibration.css?v=calibration-12" />
|
||||
<link rel="stylesheet" href="/self-repair.css?v=repair-21" />
|
||||
@@ -22,7 +23,8 @@
|
||||
<script src="/app.js?v=dual-seat-82" defer></script>
|
||||
<script type="module" src="/gesture-control.js?v=android-pad-7"></script>
|
||||
<script src="/pwa.js?v=android-pad-1" defer></script>
|
||||
<script src="/console-v2.js?v=desktop-refactor-35" defer></script>
|
||||
<script src="/console-v2.js?v=desktop-refactor-36" defer></script>
|
||||
<script src="/self-evolution.js?v=seat-weighing-2" defer></script>
|
||||
<script src="/self-extension.js?v=extension-21" defer></script>
|
||||
<script src="/self-calibration.js?v=calibration-8" defer></script>
|
||||
<script src="/self-repair.js?v=repair-21" defer></script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,494 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const $ = (selector, root = document) => root.querySelector(selector);
|
||||
const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];
|
||||
|
||||
const familyModels = [
|
||||
{
|
||||
id: "user-a",
|
||||
name: "用户 A",
|
||||
code: "USER-A",
|
||||
version: "v1.7",
|
||||
state: "enabled",
|
||||
stateLabel: "已启用",
|
||||
statusNote: "专属模型已启用",
|
||||
error: 0.18,
|
||||
samples: 8,
|
||||
sampleTarget: 11,
|
||||
sync: "2026.08.14 · 08:29",
|
||||
errors: [0.50, 0.44, 0.38, 0.32, 0.27, 0.23, 0.20, 0.18],
|
||||
parameters: [
|
||||
{ name: "零点偏移补偿", user: "已优化", before: "+0.38", after: "+0.11", progress: 88 },
|
||||
{ name: "坐姿角度补偿", user: "稳定", before: "0.64", after: "0.91", progress: 91 },
|
||||
{ name: "压力分布权重", user: "已优化", before: "0.72", after: "0.93", progress: 93 },
|
||||
{ name: "温度漂移补偿", user: "学习中", before: "42%", after: "76%", progress: 76 },
|
||||
],
|
||||
events: [
|
||||
{ time: "08.14 08:29", title: "模型 v1.7 验证通过并启用", meta: "有效样本 8", result: "±0.22 → ±0.18 kg", state: "已发布" },
|
||||
{ time: "08.14 08:27", title: "家庭称重与座椅采样配对", meta: "身份匹配成功", result: "采样质量 96%", state: "已进入学习" },
|
||||
{ time: "08.12 19:06", title: "模型 v1.6 参数更新", meta: "有效样本 7", result: "±0.24 → ±0.22 kg", state: "已验证" },
|
||||
],
|
||||
adminReference: "家庭体脂秤 62.40 kg · 座椅估计 62.58 kg",
|
||||
},
|
||||
{
|
||||
id: "user-b",
|
||||
name: "用户 B",
|
||||
code: "USER-B",
|
||||
version: "v1.3",
|
||||
state: "learning",
|
||||
stateLabel: "学习中",
|
||||
statusNote: "4 / 6 个有效样本",
|
||||
error: 0.31,
|
||||
samples: 4,
|
||||
sampleTarget: 6,
|
||||
sync: "2026.08.13 · 20:18",
|
||||
errors: [0.50, 0.43, 0.37, 0.31],
|
||||
parameters: [
|
||||
{ name: "零点偏移补偿", user: "学习中", before: "+0.41", after: "+0.24", progress: 62 },
|
||||
{ name: "坐姿角度补偿", user: "学习中", before: "0.61", after: "0.76", progress: 58 },
|
||||
{ name: "压力分布权重", user: "已优化", before: "0.69", after: "0.82", progress: 70 },
|
||||
{ name: "温度漂移补偿", user: "等待样本", before: "18%", after: "34%", progress: 34 },
|
||||
],
|
||||
events: [
|
||||
{ time: "08.13 20:22", title: "新参数验证未优于 v1.3,保持旧模型", meta: "本次未参与发布", result: "±0.31 → ±0.34 kg", state: "已回滚", tone: "warning" },
|
||||
{ time: "08.13 20:19", title: "检测到坐姿不稳定", meta: "异常样本已隔离", result: "采样质量 61%", state: "未参与更新", tone: "warning" },
|
||||
{ time: "08.10 07:51", title: "第 4 组有效样本完成配对", meta: "身份匹配成功", result: "±0.37 → ±0.31 kg", state: "学习中" },
|
||||
],
|
||||
adminReference: "家庭体脂秤 71.25 kg · 异常座椅估计 71.59 kg",
|
||||
},
|
||||
{
|
||||
id: "user-c",
|
||||
name: "用户 C",
|
||||
code: "USER-C",
|
||||
version: "—",
|
||||
state: "cold",
|
||||
stateLabel: "冷启动",
|
||||
statusNote: "等待首次家庭称重",
|
||||
error: 0.50,
|
||||
samples: 0,
|
||||
sampleTarget: 6,
|
||||
sync: "尚未同步",
|
||||
errors: [],
|
||||
parameters: [
|
||||
{ name: "零点偏移补偿", user: "等待数据", before: "—", after: "—", progress: 0 },
|
||||
{ name: "坐姿角度补偿", user: "等待数据", before: "—", after: "—", progress: 0 },
|
||||
{ name: "压力分布权重", user: "等待数据", before: "—", after: "—", progress: 0 },
|
||||
{ name: "温度漂移补偿", user: "等待数据", before: "—", after: "—", progress: 0 },
|
||||
],
|
||||
events: [
|
||||
{ time: "等待中", title: "尚无可用的车家配对样本", meta: "需要先在家庭体脂秤完成称重", result: "使用通用模型", state: "±0.50 kg" },
|
||||
],
|
||||
adminReference: "尚无家庭体脂秤参考值",
|
||||
},
|
||||
{
|
||||
id: "guest",
|
||||
name: "访客",
|
||||
code: "GENERIC",
|
||||
version: "通用",
|
||||
state: "generic",
|
||||
stateLabel: "通用模型",
|
||||
statusNote: "未识别用户使用通用模型",
|
||||
error: 0.50,
|
||||
samples: 0,
|
||||
sampleTarget: 0,
|
||||
sync: "无需同步",
|
||||
errors: [0.50],
|
||||
parameters: [
|
||||
{ name: "零点偏移补偿", user: "通用参数", before: "0.00", after: "0.00", progress: 50 },
|
||||
{ name: "坐姿角度补偿", user: "通用参数", before: "0.50", after: "0.50", progress: 50 },
|
||||
{ name: "压力分布权重", user: "通用参数", before: "0.68", after: "0.68", progress: 50 },
|
||||
{ name: "温度漂移补偿", user: "通用参数", before: "50%", after: "50%", progress: 50 },
|
||||
],
|
||||
events: [
|
||||
{ time: "当前策略", title: "未识别用户不创建专属模型", meta: "不保存个人参考体重", result: "通用模型基线", state: "±0.50 kg" },
|
||||
],
|
||||
adminReference: "访客模式不记录精确体重",
|
||||
},
|
||||
];
|
||||
|
||||
const systemEntries = [
|
||||
{
|
||||
version: "v0.5", date: "2026.08.12", status: "当前版本", title: "统一主题与权限体验",
|
||||
summary: "把权限、主题与核心交互收束到同一套体验规则,让不同模块看起来、用起来都属于同一个系统。",
|
||||
changes: [["主题统一", "自扩展固定使用 user 绿色主题,避免权限语义混用。"], ["孪生同步", "3D 模型渲染、玩具动作和车端动作统一跟随当前主题。"], ["能力扩充", "新增自进化入口,让 user 与 admin 共同查看演进过程。"]],
|
||||
modules: ["自扩展", "数字孪生", "权限体系"], metrics: [["主题一致性", "72%", "100%", 100], ["可见能力模块", "4 个", "5 个", 92], ["待修正视觉项", "4 项", "0 项", 100]],
|
||||
userValidation: "核心板块已完成主题复核,权限色与交互反馈保持一致。", adminValidation: "前端契约 11/11 · 自标定/自修复检查 8/8",
|
||||
},
|
||||
{
|
||||
version: "v0.4", date: "2026.08.11", status: "稳定版本", title: "自修复闭环增强",
|
||||
summary: "从发现异常走向诊断、修复与验证闭环,同时补齐离线场景的默认内容,确保功能状态始终可见。",
|
||||
changes: [["故障覆盖", "补充六类量产故障用例,覆盖更真实的车端异常。"], ["修复闭环", "形成发现、定位、决策、执行、验证五阶段流程。"], ["离线韧性", "服务暂时离线时仍保留默认案例和演示路径。"]],
|
||||
modules: ["自修复", "故障诊断", "离线体验"], metrics: [["故障用例", "2 类", "6 类", 86], ["修复阶段", "3 阶段", "5 阶段", 92], ["离线可用性", "空白", "可演示", 100]],
|
||||
userValidation: "断开服务后仍可理解自修复能力,并可继续浏览默认案例。", adminValidation: "默认数据回退、流程状态与接口异常路径均已验证。",
|
||||
},
|
||||
{
|
||||
version: "v0.3", date: "2026.08.10", status: "能力升级", title: "外设与车端统一编排",
|
||||
summary: "将玩具动作、车端设备动作与数字孪生放进同一个控制流程,减少跨区域理解和操作成本。",
|
||||
changes: [["动作融合", "玩具动作与车端设备动作进入统一动作界面。"], ["布局优化", "孪生界面位于左侧,动作界面固定在右侧。"], ["映射精简", "移除重复外框,让实时映射关系更直接。"]],
|
||||
modules: ["自扩展", "外设学习", "整车控制"], metrics: [["动作入口", "2 处", "1 处", 100], ["映射层级", "3 层", "2 层", 88], ["协同对象", "玩具", "玩具 + 车端", 94]],
|
||||
userValidation: "动作与孪生左右分区清晰,学习结果可以直接用于统一控制。", adminValidation: "外设连接状态、动作映射与车端执行链路保持独立可追踪。",
|
||||
},
|
||||
{
|
||||
version: "v0.2", date: "2026.08.09", status: "体系建立", title: "多权限主题体系",
|
||||
summary: "建立 user 与 admin 的视觉身份,使权限差异不只体现在可用功能,也能被用户直观感知。",
|
||||
changes: [["角色主题", "user 使用绿色,admin 使用蓝色,形成稳定的权限认知。"], ["能力边界", "按角色控制模块入口,同时保留清晰的切换反馈。"], ["模型联动", "数字孪生模型与界面主题保持同色语义。"]],
|
||||
modules: ["权限体系", "主题系统", "数字孪生"], metrics: [["角色主题", "1 套", "2 套", 100], ["权限反馈", "文字", "视觉 + 文字", 90], ["主题联动", "界面", "界面 + 模型", 96]],
|
||||
userValidation: "绿色主题贯穿 user 可用能力,角色身份更加明确。", adminValidation: "角色切换事件可同步驱动界面变量与模型渲染参数。",
|
||||
},
|
||||
{
|
||||
version: "v0.1", date: "2026.08.08", status: "起点版本", title: "整车认知控制台诞生",
|
||||
summary: "完成从自然语言意图到整车状态回读的第一条闭环,建立后续能力持续进化的共同底座。",
|
||||
changes: [["自然语言控制", "用贴近日常表达的指令控制座舱功能。"], ["整车孪生", "把座椅、空调、车窗等状态集中映射到一个界面。"], ["执行回读", "串联 CAN 计划、设备执行与状态反馈。"]],
|
||||
modules: ["自编排", "数字孪生", "CAN 执行"], metrics: [["能力模块", "0 个", "1 个", 100], ["控制链路", "分散", "闭环", 92], ["状态视图", "单设备", "整车", 96]],
|
||||
userValidation: "自然语言、执行过程与结果反馈首次形成连续体验。", adminValidation: "意图解析、CAN 编译计划与硬件回读具备完整追踪链路。",
|
||||
},
|
||||
];
|
||||
|
||||
let playbackTimer = null;
|
||||
let activeModelId = "user-a";
|
||||
let visibleSamples = null;
|
||||
|
||||
function make(tag, className, html) {
|
||||
const node = document.createElement(tag);
|
||||
if (className) node.className = className;
|
||||
if (html != null) node.innerHTML = html;
|
||||
return node;
|
||||
}
|
||||
|
||||
function formatError(value) {
|
||||
return `±${Number(value).toFixed(2)}`;
|
||||
}
|
||||
|
||||
function memberButtons() {
|
||||
return familyModels.map((model, index) => `
|
||||
<button type="button" class="v2-family-model${index === 0 ? " active" : ""}" data-family-model="${model.id}" aria-pressed="${index === 0 ? "true" : "false"}">
|
||||
<span class="v2-family-marker" aria-hidden="true"></span>
|
||||
<span class="v2-family-copy"><span><strong>${model.name}</strong><em data-model-state="${model.state}">${model.stateLabel}</em><small>${model.version}</small></span><small>${model.statusNote}</small></span>
|
||||
<span class="v2-family-error"><strong>${formatError(model.error)}</strong><small>kg</small></span>
|
||||
</button>`).join("");
|
||||
}
|
||||
|
||||
function systemVersionButtons() {
|
||||
return systemEntries.map((entry, index) => `
|
||||
<button type="button" class="v2-evolution-version${index === 0 ? " active" : ""}" data-system-evolution-index="${index}" aria-pressed="${index === 0 ? "true" : "false"}">
|
||||
<span><strong>${entry.version}</strong><time datetime="${entry.date.replaceAll(".", "-")}">${entry.date.slice(5)}</time></span>
|
||||
<b>${entry.title}</b><small>${entry.status}</small>
|
||||
</button>`).join("");
|
||||
}
|
||||
|
||||
function shellTemplate() {
|
||||
return `
|
||||
<div class="v2-evolution-shell">
|
||||
<header class="v2-evolution-overview">
|
||||
<div class="v2-evolution-heading v2-module-heading">
|
||||
<div class="v2-evolution-title-row">
|
||||
<div><span class="section-kicker" id="v2EvolutionKicker">SELF-EVOLUTION · SEAT WEIGHING</span><h1 id="v2EvolutionPageTitle">座椅称重模型进化</h1></div>
|
||||
<span class="v2-demo-data" id="v2EvolutionDataBadge"><i data-lucide="flask-conical"></i>演示数据</span>
|
||||
</div>
|
||||
<p id="v2EvolutionPageIntro">通过车家联动持续校准家庭成员专属模型,让称重误差从 ±0.50 kg 逐步收敛至 ±0.10 kg。</p>
|
||||
</div>
|
||||
<dl class="v2-evolution-summary" id="v2EvolutionSummary" aria-label="当前称重模型概览"></dl>
|
||||
</header>
|
||||
|
||||
<section class="v2-evolution-page active" data-evolution-page="weighing">
|
||||
<div class="v2-weighing-workspace">
|
||||
<aside class="v2-family-panel" aria-label="家庭成员专属模型">
|
||||
<div class="v2-family-heading"><span>FAMILY MODELS</span><strong>家庭成员模型</strong></div>
|
||||
<div class="v2-family-list">${memberButtons()}</div>
|
||||
<div class="v2-privacy-note"><i data-lucide="shield-check"></i><p>每个家庭成员拥有独立参数空间。精确体重仅向本人或已授权管理员展示。</p></div>
|
||||
<div class="v2-model-state-guide" aria-label="模型状态说明"><span data-state="cold">冷启动</span><span data-state="learning">学习中</span><span data-state="enabled">已启用</span><span data-state="warning">退化回滚</span></div>
|
||||
</aside>
|
||||
|
||||
<article class="v2-weighing-detail" aria-live="polite">
|
||||
<header class="v2-weighing-detail-heading">
|
||||
<div><span class="v2-model-badge" id="v2ModelStateBadge"></span><span class="v2-model-code" id="v2ModelCode"></span></div>
|
||||
<div class="v2-home-sync"><strong><i></i><span id="v2HomeSyncState">车家同步正常</span></strong><small id="v2HomeSyncTime"></small></div>
|
||||
</header>
|
||||
<div class="v2-model-title-row">
|
||||
<div><h2 id="v2ModelTitle"></h2><p id="v2ModelLead"></p></div>
|
||||
<button type="button" class="v2-play-evolution" id="v2PlayEvolution"><i data-lucide="play"></i><span>演示进化过程</span></button>
|
||||
</div>
|
||||
|
||||
<div class="v2-weighing-core">
|
||||
<section class="v2-convergence-card" aria-labelledby="v2ConvergenceTitle">
|
||||
<header><h3 id="v2ConvergenceTitle">验证误差收敛趋势</h3><div class="v2-chart-legend"><span data-line="baseline">通用模型</span><span data-line="personal">专属模型</span><span data-line="target">目标误差</span></div></header>
|
||||
<div class="v2-chart-wrap" id="v2EvolutionChart"></div>
|
||||
<p class="v2-sample-insight" id="v2SampleInsight"></p>
|
||||
</section>
|
||||
|
||||
<section class="v2-linkage-card" aria-labelledby="v2LinkageTitle">
|
||||
<header><h3 id="v2LinkageTitle">本轮车家联动闭环</h3><span id="v2LinkageProgress"></span></header>
|
||||
<ol id="v2LinkageSteps"></ol>
|
||||
<div class="v2-linkage-guard" id="v2LinkageGuard"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="v2-parameter-section" aria-labelledby="v2ParameterTitle">
|
||||
<header><h3 id="v2ParameterTitle">模型参数持续进化</h3><p><span data-evolution-scope="user">展示可解释参数状态</span><span data-evolution-scope="admin">管理员可见参数前后值与发布保障</span></p></header>
|
||||
<div class="v2-parameter-grid" id="v2ParameterGrid"></div>
|
||||
</section>
|
||||
|
||||
<section class="v2-model-events" aria-labelledby="v2EventsTitle">
|
||||
<header><h3 id="v2EventsTitle">最近进化记录</h3><p><span data-evolution-scope="user">仅显示与当前用户相关的状态</span><span data-evolution-scope="admin" id="v2AdminReference"></span></p></header>
|
||||
<div id="v2EventList"></div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="v2-evolution-page" data-evolution-page="system" hidden>
|
||||
<div class="v2-evolution-workspace">
|
||||
<aside class="v2-evolution-timeline" aria-label="系统版本进化时间轴"><div class="v2-evolution-timeline-heading"><span>EVOLUTION PATH</span><strong>从起点到现在</strong></div><div class="v2-evolution-version-list">${systemVersionButtons()}</div></aside>
|
||||
<article class="v2-evolution-detail" aria-live="polite">
|
||||
<header class="v2-evolution-detail-heading"><div><span class="v2-evolution-status" id="v2SystemStatus"></span><span class="v2-evolution-detail-version" id="v2SystemVersion"></span></div><time id="v2SystemDate"></time></header>
|
||||
<h2 id="v2SystemTitle"></h2><p class="v2-evolution-lead" id="v2SystemLead"></p>
|
||||
<div class="v2-evolution-detail-grid">
|
||||
<section class="v2-evolution-changes"><h3><i data-lucide="git-commit-horizontal"></i> 本次进化</h3><div id="v2SystemChangeList"></div></section>
|
||||
<section class="v2-evolution-evidence"><h3><i data-lucide="trending-up"></i> 变好的证据</h3><div id="v2SystemMetrics"></div></section>
|
||||
</div>
|
||||
<footer class="v2-evolution-validation"><div class="v2-evolution-module-tags" id="v2SystemModules"></div><p><i data-lucide="badge-check"></i><span data-evolution-scope="user" id="v2SystemUserValidation"></span><span data-evolution-scope="admin" id="v2SystemAdminValidation"></span></p></footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="v2-evolution-next" id="v2EvolutionFooter">
|
||||
<span class="v2-evolution-next-icon"><i data-lucide="flag"></i></span>
|
||||
<div><small id="v2MilestoneKicker">NEXT MILESTONE · TARGET ±0.10 kg</small><strong id="v2MilestoneTitle">继续积累高质量车家配对样本</strong><p id="v2MilestoneCopy"></p></div>
|
||||
<div class="v2-milestone-progress"><strong id="v2MilestoneCount"></strong><span><i id="v2MilestoneBar"></i></span><small id="v2MilestonePercent"></small></div>
|
||||
</footer>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function summaryTemplate(model, view) {
|
||||
if (view === "system") {
|
||||
return `<div class="v2-evolution-current"><dt>当前版本</dt><dd>v0.5</dd><small>能力融合期</small></div><div><dt>累计迭代</dt><dd>5</dd><small>个版本</small></div><div><dt>能力改进</dt><dd>12</dd><small>项记录</small></div><div><dt>最近更新</dt><dd>08.12</dd><small>2026</small></div>`;
|
||||
}
|
||||
return `<div><dt>当前用户</dt><dd>${model.name}</dd><small>${model.state === "generic" ? "隐私访客模式" : model.statusNote}</small></div><div class="v2-evolution-current"><dt>当前验证误差</dt><dd>${formatError(model.error)}</dd><small>kg · 演示数据</small></div><div><dt>目标误差</dt><dd>±0.10</dd><small>kg</small></div><div><dt>有效样本</dt><dd>${model.samples}</dd><small>${model.sampleTarget ? `组车家配对` : "无需配对"}</small></div>`;
|
||||
}
|
||||
|
||||
function chartTemplate(model, count = model.errors.length) {
|
||||
const values = model.errors.slice(0, count);
|
||||
const width = 720;
|
||||
const left = 48;
|
||||
const right = 698;
|
||||
const top = 18;
|
||||
const bottom = 184;
|
||||
const y = (error) => top + ((0.52 - error) / 0.44) * (bottom - top);
|
||||
const x = (index) => values.length <= 1 ? left : left + (index / (Math.max(model.errors.length, 2) - 1)) * (right - left);
|
||||
const grid = [0.5, 0.4, 0.3, 0.2, 0.1].map((error) => `
|
||||
<g class="v2-chart-grid"><line x1="${left}" y1="${y(error)}" x2="${right}" y2="${y(error)}"></line><text x="0" y="${y(error) + 3}">±${error.toFixed(2)}</text></g>`).join("");
|
||||
const points = values.map((error, index) => `${x(index)},${y(error)}`).join(" ");
|
||||
const circles = values.map((error, index) => `
|
||||
<g class="v2-chart-point${index === values.length - 1 ? " current" : ""}" data-sample-index="${index}" tabindex="0" role="button" aria-label="样本 ${index + 1},验证误差 ${formatError(error)} kg">
|
||||
<circle cx="${x(index)}" cy="${y(error)}" r="5"></circle><circle class="hit" cx="${x(index)}" cy="${y(error)}" r="13"></circle>
|
||||
</g>`).join("");
|
||||
const labels = model.errors.map((_, index) => `<text x="${left + (index / (Math.max(model.errors.length, 2) - 1)) * (right - left)}" y="207">样本${index + 1}</text>`).join("");
|
||||
const area = values.length > 1 ? `<polygon class="v2-chart-area" points="${left},${bottom} ${points} ${x(values.length - 1)},${bottom}"></polygon>` : "";
|
||||
const line = values.length > 1 ? `<polyline class="v2-personal-line" points="${points}"></polyline>` : "";
|
||||
const current = values.length ? values[values.length - 1] : null;
|
||||
return `<svg viewBox="0 0 ${width} 216" role="img" aria-label="${model.name} 专属称重模型验证误差收敛曲线">
|
||||
${grid}<line class="v2-baseline-line" x1="${left}" y1="${y(0.5)}" x2="${right}" y2="${y(0.5)}"></line><line class="v2-target-line" x1="${left}" y1="${y(0.1)}" x2="${right}" y2="${y(0.1)}"></line>
|
||||
${area}${line}${circles}<g class="v2-chart-labels">${labels}</g>
|
||||
${current != null ? `<g class="v2-current-label" transform="translate(${Math.min(x(values.length - 1) - 58, 630)} ${Math.max(y(current) - 31, 4)})"><rect width="70" height="24" rx="12"></rect><text x="35" y="16">当前 ${formatError(current)}</text></g>` : ""}
|
||||
</svg>${values.length ? "" : `<div class="v2-chart-empty"><i data-lucide="scale"></i><strong>等待首组车家配对样本</strong><span>当前继续使用通用模型 ±0.50 kg</span></div>`}`;
|
||||
}
|
||||
|
||||
function linkageTemplate(model) {
|
||||
const baseSteps = [
|
||||
["家庭体脂秤", "收到用户参考体重"], ["用户 ID 匹配", "家庭账号与车端身份一致"], ["座椅稳定采样", "坐姿、温度、座椅位置质量合格"], ["专属参数更新", "生成候选参数并离线验证"], ["验证并发布", "优于旧模型后正式生效"],
|
||||
];
|
||||
let done = model.state === "enabled" ? 5 : model.state === "learning" ? 3 : 0;
|
||||
if (model.state === "generic") done = 0;
|
||||
const list = baseSteps.map(([title, copy], index) => {
|
||||
const state = index < done ? "done" : index === done && model.state === "learning" ? "active" : "pending";
|
||||
const label = state === "done" ? "已完成" : state === "active" ? "进行中" : "等待";
|
||||
return `<li data-step-state="${state}"><span>${index + 1}</span><p><strong>${title}</strong><small>${copy}</small></p><em>${label}</em></li>`;
|
||||
}).join("");
|
||||
const guard = model.id === "user-b"
|
||||
? `<i data-lucide="rotate-ccw"></i><p><strong>退化保护已触发</strong><span>候选模型误差上升至 ±0.34 kg,已自动回滚并保留 v1.3。</span></p>`
|
||||
: model.state === "enabled"
|
||||
? `<i data-lucide="shield-check"></i><p><strong>发布保护正常</strong><span>只有验证结果优于当前模型时才会发布新参数。</span></p>`
|
||||
: `<i data-lucide="info"></i><p><strong>专属模型尚未发布</strong><span>样本不足时保持通用模型,不以未验证参数影响称重。</span></p>`;
|
||||
return { list, guard, done };
|
||||
}
|
||||
|
||||
function parameterTemplate(model) {
|
||||
return model.parameters.map((item) => `
|
||||
<article class="v2-parameter-card">
|
||||
<div><strong>${item.name}</strong><span data-evolution-scope="user">${item.user}</span><span data-evolution-scope="admin"><em>${item.before}</em><i data-lucide="arrow-right"></i><b>${item.after}</b></span></div>
|
||||
<small data-evolution-scope="user">${item.user === "等待数据" ? "等待有效车家样本" : "根据近期有效样本持续校准"}</small><small data-evolution-scope="admin">参数窗口 8 组 · 可回滚 · MODEL ${model.version}</small>
|
||||
<span class="v2-parameter-progress" style="--parameter-progress:${item.progress}%"><i></i></span>
|
||||
</article>`).join("");
|
||||
}
|
||||
|
||||
function eventTemplate(model) {
|
||||
return model.events.map((event) => `
|
||||
<article class="v2-model-event${event.tone === "warning" ? " warning" : ""}"><time>${event.time}</time><strong>${event.title}</strong><span>${event.meta}</span><span>${event.result}</span><em>${event.state}</em></article>`).join("");
|
||||
}
|
||||
|
||||
function renderModel(root, modelId, { keepPlayback = false } = {}) {
|
||||
const model = familyModels.find((item) => item.id === modelId) || familyModels[0];
|
||||
activeModelId = model.id;
|
||||
if (!keepPlayback) {
|
||||
clearInterval(playbackTimer);
|
||||
playbackTimer = null;
|
||||
visibleSamples = null;
|
||||
$("#v2PlayEvolution", root)?.classList.remove("playing");
|
||||
const playLabel = $("#v2PlayEvolution span", root);
|
||||
if (playLabel) playLabel.textContent = "演示进化过程";
|
||||
}
|
||||
$("#v2EvolutionSummary", root).innerHTML = summaryTemplate(model, "weighing");
|
||||
$$("[data-family-model]", root).forEach((button) => {
|
||||
const active = button.dataset.familyModel === model.id;
|
||||
button.classList.toggle("active", active);
|
||||
button.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
const badge = $("#v2ModelStateBadge", root);
|
||||
badge.textContent = model.state === "enabled" ? "专属模型已启用" : model.stateLabel;
|
||||
badge.dataset.modelState = model.state;
|
||||
$("#v2ModelCode", root).textContent = `MODEL ${model.version} · ${model.code}`;
|
||||
$("#v2HomeSyncState", root).textContent = model.state === "generic" ? "访客隐私模式" : model.sync === "尚未同步" ? "等待车家同步" : "车家同步正常";
|
||||
$("#v2HomeSyncTime", root).textContent = model.sync;
|
||||
$("#v2ModelTitle", root).textContent = `${model.name} · ${model.state === "generic" ? "通用称重模型" : "个性化称重模型"}`;
|
||||
$("#v2ModelLead", root).textContent = model.state === "enabled" ? "最近一次家庭参考值与车端座椅采样已完成配对,新参数通过验证并正式生效。" : model.state === "learning" ? "专属参数仍在学习;样本不足或验证退化时,车端继续使用已验证模型。" : model.state === "cold" ? "尚未收到家庭体脂秤参考值,当前安全使用通用模型。" : "未识别用户不会创建个人参数或保存精确体重。";
|
||||
const play = $("#v2PlayEvolution", root);
|
||||
play.disabled = model.errors.length < 2;
|
||||
play.title = play.disabled ? "当前没有可回放的专属模型样本" : "按有效样本顺序回放误差收敛过程";
|
||||
$("#v2EvolutionChart", root).innerHTML = chartTemplate(model, visibleSamples ?? model.errors.length);
|
||||
$("#v2SampleInsight", root).textContent = model.errors.length > 1 ? `已从通用模型基线 ±0.50 kg 收敛至 ${formatError(model.error)} kg;点击曲线节点可查看该次样本。` : "个性化模型仅使用身份匹配、时间有效且座椅采样稳定的数据。";
|
||||
const linkage = linkageTemplate(model);
|
||||
$("#v2LinkageSteps", root).innerHTML = linkage.list;
|
||||
$("#v2LinkageGuard", root).innerHTML = linkage.guard;
|
||||
$("#v2LinkageProgress", root).textContent = `${linkage.done} / 5 已完成`;
|
||||
$("#v2ParameterGrid", root).innerHTML = parameterTemplate(model);
|
||||
$("#v2EventList", root).innerHTML = eventTemplate(model);
|
||||
$("#v2AdminReference", root).textContent = model.adminReference;
|
||||
const remaining = Math.max(0, model.sampleTarget - model.samples);
|
||||
const percent = model.sampleTarget ? Math.min(100, Math.round((model.samples / model.sampleTarget) * 100)) : 100;
|
||||
$("#v2MilestoneCopy", root).textContent = model.state === "generic" ? "访客称重不会进入任何家庭成员的参数空间。" : model.state === "cold" ? "完成家庭体脂秤绑定后,首次有效配对将启动专属模型学习。" : remaining ? `预计再完成 ${remaining} 组有效样本后进入目标精度验证阶段。` : "样本数量已满足要求,正在持续验证 ±0.10 kg 目标。";
|
||||
$("#v2MilestoneCount", root).textContent = model.sampleTarget ? `${model.samples} / ${model.sampleTarget} 组` : "隐私模式";
|
||||
$("#v2MilestonePercent", root).textContent = model.sampleTarget ? `当前学习进度 ${percent}%` : "不留存个人数据";
|
||||
$("#v2MilestoneBar", root).style.width = `${percent}%`;
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
}
|
||||
|
||||
function renderSystemEntry(root, index) {
|
||||
const entry = systemEntries[index];
|
||||
if (!entry) return;
|
||||
$("#v2SystemStatus", root).textContent = entry.status;
|
||||
$("#v2SystemVersion", root).textContent = entry.version;
|
||||
const date = $("#v2SystemDate", root);
|
||||
date.textContent = entry.date;
|
||||
date.dateTime = entry.date.replaceAll(".", "-");
|
||||
$("#v2SystemTitle", root).textContent = entry.title;
|
||||
$("#v2SystemLead", root).textContent = entry.summary;
|
||||
$("#v2SystemChangeList", root).innerHTML = entry.changes.map(([label, copy]) => `<div><i data-lucide="check"></i><p><strong>${label}</strong><span>${copy}</span></p></div>`).join("");
|
||||
$("#v2SystemMetrics", root).innerHTML = entry.metrics.map(([label, before, after, progress]) => `<div class="v2-evolution-metric"><div><span>${label}</span><p><del>${before}</del><i data-lucide="arrow-right"></i><strong>${after}</strong></p></div><span class="v2-evolution-progress" style="--evolution-progress:${progress}%"><i></i></span></div>`).join("");
|
||||
$("#v2SystemModules", root).innerHTML = entry.modules.map((module) => `<span>${module}</span>`).join("");
|
||||
$("#v2SystemUserValidation", root).textContent = entry.userValidation;
|
||||
$("#v2SystemAdminValidation", root).textContent = entry.adminValidation;
|
||||
$$("[data-system-evolution-index]", root).forEach((button, buttonIndex) => {
|
||||
const active = buttonIndex === index;
|
||||
button.classList.toggle("active", active);
|
||||
button.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
}
|
||||
|
||||
function setView(root, view) {
|
||||
const system = view === "system";
|
||||
clearInterval(playbackTimer);
|
||||
playbackTimer = null;
|
||||
$$("[data-evolution-page]", root).forEach((page) => {
|
||||
const active = page.dataset.evolutionPage === view;
|
||||
page.classList.toggle("active", active);
|
||||
page.hidden = !active;
|
||||
});
|
||||
$("#v2EvolutionKicker", root).textContent = system ? "SELF-EVOLUTION · SYSTEM JOURNAL" : "SELF-EVOLUTION · SEAT WEIGHING";
|
||||
$("#v2EvolutionPageTitle", root).textContent = system ? "系统能力日志" : "座椅称重模型进化";
|
||||
$("#v2EvolutionPageIntro", root).textContent = system ? "保留每次问题发现、能力升级与验证结果,持续记录控制台系统能力演进。" : "通过车家联动持续校准家庭成员专属模型,让称重误差从 ±0.50 kg 逐步收敛至 ±0.10 kg。";
|
||||
$("#v2EvolutionDataBadge", root).innerHTML = system
|
||||
? '<i data-lucide="history"></i>历史记录'
|
||||
: '<i data-lucide="flask-conical"></i>演示数据';
|
||||
$("#v2EvolutionSummary", root).innerHTML = summaryTemplate(familyModels.find((item) => item.id === activeModelId), view);
|
||||
const footer = $("#v2EvolutionFooter", root);
|
||||
footer.hidden = system;
|
||||
root.dataset.evolutionView = view;
|
||||
window.dispatchEvent(new CustomEvent("cognitive-agent:evolution-view-change", {
|
||||
detail: { view },
|
||||
}));
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
}
|
||||
|
||||
function playEvolution(root) {
|
||||
const model = familyModels.find((item) => item.id === activeModelId);
|
||||
if (!model || model.errors.length < 2) return;
|
||||
clearInterval(playbackTimer);
|
||||
let count = 1;
|
||||
visibleSamples = count;
|
||||
const button = $("#v2PlayEvolution", root);
|
||||
button.classList.add("playing");
|
||||
$("#v2PlayEvolution span", root).textContent = "正在回放";
|
||||
renderModel(root, model.id, { keepPlayback: true });
|
||||
playbackTimer = window.setInterval(() => {
|
||||
count += 1;
|
||||
visibleSamples = count;
|
||||
renderModel(root, model.id, { keepPlayback: true });
|
||||
if (count >= model.errors.length) {
|
||||
clearInterval(playbackTimer);
|
||||
playbackTimer = null;
|
||||
button.classList.remove("playing");
|
||||
$("#v2PlayEvolution span", root).textContent = "再次回放";
|
||||
}
|
||||
}, 520);
|
||||
}
|
||||
|
||||
function bind(root) {
|
||||
$(".v2-family-list", root).addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-family-model]");
|
||||
if (button) renderModel(root, button.dataset.familyModel);
|
||||
});
|
||||
$(".v2-evolution-version-list", root).addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-system-evolution-index]");
|
||||
if (button) renderSystemEntry(root, Number(button.dataset.systemEvolutionIndex));
|
||||
});
|
||||
$("#v2PlayEvolution", root).addEventListener("click", () => playEvolution(root));
|
||||
$("#v2EvolutionChart", root).addEventListener("click", (event) => {
|
||||
const point = event.target.closest("[data-sample-index]");
|
||||
const model = familyModels.find((item) => item.id === activeModelId);
|
||||
if (!point || !model) return;
|
||||
const index = Number(point.dataset.sampleIndex);
|
||||
$("#v2SampleInsight", root).textContent = `样本 ${index + 1}:验证误差 ${formatError(model.errors[index])} kg。该节点来自固定演示数据,不代表实时算法结果。`;
|
||||
});
|
||||
$("#v2EvolutionChart", root).addEventListener("keydown", (event) => {
|
||||
if (!["Enter", " "].includes(event.key)) return;
|
||||
const point = event.target.closest("[data-sample-index]");
|
||||
if (point) { event.preventDefault(); point.dispatchEvent(new MouseEvent("click", { bubbles: true })); }
|
||||
});
|
||||
}
|
||||
|
||||
function mount(mainPanel) {
|
||||
const existing = $("#v2SelfEvolution", mainPanel);
|
||||
if (existing) return existing;
|
||||
const evolution = make("section", "v2-self-evolution", shellTemplate());
|
||||
evolution.id = "v2SelfEvolution";
|
||||
evolution.dataset.evolutionView = "weighing";
|
||||
evolution.setAttribute("aria-label", "自进化 · 座椅称重个性化模型进化");
|
||||
mainPanel.append(evolution);
|
||||
bind(evolution);
|
||||
renderModel(evolution, activeModelId);
|
||||
renderSystemEntry(evolution, 0);
|
||||
setView(evolution, "weighing");
|
||||
return evolution;
|
||||
}
|
||||
|
||||
window.CognitiveAgentSelfEvolution = {
|
||||
mount,
|
||||
getView: () => $("#v2SelfEvolution")?.dataset.evolutionView || "weighing",
|
||||
setView: (view) => {
|
||||
const root = $("#v2SelfEvolution");
|
||||
if (root) setView(root, view === "system" ? "system" : "weighing");
|
||||
},
|
||||
};
|
||||
}());
|
||||
@@ -1,4 +1,4 @@
|
||||
const CACHE_VERSION = "cognitive-agent-pad-v119";
|
||||
const CACHE_VERSION = "cognitive-agent-pad-v120";
|
||||
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
|
||||
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;
|
||||
const APP_SHELL = [
|
||||
@@ -9,8 +9,10 @@ const APP_SHELL = [
|
||||
"/app.js?v=dual-seat-82",
|
||||
"/gesture-control.js?v=android-pad-7",
|
||||
"/pwa.js?v=android-pad-1",
|
||||
"/console-v2.css?v=desktop-refactor-106",
|
||||
"/console-v2.js?v=desktop-refactor-35",
|
||||
"/console-v2.css?v=desktop-refactor-107",
|
||||
"/console-v2.js?v=desktop-refactor-36",
|
||||
"/self-evolution.css?v=seat-weighing-5",
|
||||
"/self-evolution.js?v=seat-weighing-2",
|
||||
"/self-extension.css?v=extension-19",
|
||||
"/self-extension.js?v=extension-21",
|
||||
"/self-calibration.css?v=calibration-12",
|
||||
|
||||
@@ -184,13 +184,21 @@ operations:
|
||||
) as response:
|
||||
gesture_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-35", timeout=3
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-36", timeout=3
|
||||
) as response:
|
||||
console_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/console-v2.css?v=desktop-refactor-106", timeout=3
|
||||
f"{self.base_url}/console-v2.css?v=desktop-refactor-107", timeout=3
|
||||
) as response:
|
||||
console_styles = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-evolution.js?v=seat-weighing-2", timeout=3
|
||||
) as response:
|
||||
evolution_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-evolution.css?v=seat-weighing-5", timeout=3
|
||||
) as response:
|
||||
evolution_styles = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.js?v=extension-21", timeout=3
|
||||
) as response:
|
||||
@@ -227,8 +235,10 @@ operations:
|
||||
self.assertIn('href="/styles.css?v=dual-seat-9"', html)
|
||||
self.assertIn('src="/gesture-control.js?v=android-pad-7"', html)
|
||||
self.assertNotIn('class="vehicle-system-hud"', html)
|
||||
self.assertIn('src="/console-v2.js?v=desktop-refactor-35"', html)
|
||||
self.assertIn('href="/console-v2.css?v=desktop-refactor-106"', html)
|
||||
self.assertIn('src="/console-v2.js?v=desktop-refactor-36"', html)
|
||||
self.assertIn('href="/console-v2.css?v=desktop-refactor-107"', html)
|
||||
self.assertIn('src="/self-evolution.js?v=seat-weighing-2"', html)
|
||||
self.assertIn('href="/self-evolution.css?v=seat-weighing-5"', html)
|
||||
self.assertIn('src="/self-extension.js?v=extension-21"', html)
|
||||
self.assertIn('href="/self-extension.css?v=extension-19"', html)
|
||||
self.assertNotIn("滚动查看更多", html)
|
||||
@@ -268,13 +278,26 @@ operations:
|
||||
self.assertIn('label: "自修复"', console_script)
|
||||
self.assertIn('id: "self-evolution", label: "自进化", available: true', console_script)
|
||||
self.assertIn(
|
||||
'evolution.setAttribute("aria-label", "自进化能力日志")',
|
||||
'window.CognitiveAgentSelfEvolution?.mount(mainPanel)',
|
||||
console_script,
|
||||
)
|
||||
self.assertIn('<span class="section-kicker">SELF-EVOLUTION</span>', console_script)
|
||||
self.assertIn('v2-evolution-heading v2-module-heading', console_script)
|
||||
self.assertIn('data-evolution-index', console_script)
|
||||
self.assertIn('NEXT EVOLUTION · v0.6', console_script)
|
||||
self.assertIn('caption.textContent = evolutionMode ? "进化视图" : "控制方式"', console_script)
|
||||
self.assertIn('{ label: "座椅称重进化", icon: "scale"', console_script)
|
||||
self.assertIn('{ label: "系统能力日志", icon: "scroll-text"', console_script)
|
||||
self.assertIn('aria-label", "自进化 · 座椅称重个性化模型进化"', evolution_script)
|
||||
self.assertIn('SELF-EVOLUTION · SEAT WEIGHING', evolution_script)
|
||||
self.assertIn('座椅称重模型进化', evolution_script)
|
||||
self.assertIn('系统能力日志', evolution_script)
|
||||
self.assertIn('data-family-model="${model.id}"', evolution_script)
|
||||
self.assertIn('用户 A', evolution_script)
|
||||
self.assertIn('用户 B', evolution_script)
|
||||
self.assertIn('用户 C', evolution_script)
|
||||
self.assertIn('访客', evolution_script)
|
||||
self.assertIn('退化保护已触发', evolution_script)
|
||||
self.assertIn('本次未参与发布', evolution_script)
|
||||
self.assertIn('演示进化过程', evolution_script)
|
||||
self.assertIn('家庭体脂秤', evolution_script)
|
||||
self.assertIn('目标误差', evolution_script)
|
||||
self.assertIn('["self-orchestration", "self-extension", "self-evolution"]', console_script)
|
||||
self.assertIn('roleSelect.disabled = ui.activeModule === "self-repair"', console_script)
|
||||
self.assertNotIn('label: "自诊断"', console_script)
|
||||
@@ -299,7 +322,7 @@ operations:
|
||||
self.assertIn('.v2-control-switch {', console_styles)
|
||||
self.assertIn('.v2-module-switcher {', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(5, minmax(0, 1fr))', console_styles)
|
||||
self.assertIn('[data-module="self-evolution"] .v2-self-evolution', console_styles)
|
||||
self.assertIn('[data-module="self-evolution"] .v2-self-evolution', evolution_styles)
|
||||
self.assertIn(
|
||||
'.console-v2 .v2-module-heading.v2-module-heading h1',
|
||||
console_styles,
|
||||
@@ -319,8 +342,14 @@ operations:
|
||||
'[data-module="self-repair"] .v2-self-repair',
|
||||
console_styles,
|
||||
)
|
||||
self.assertIn('.v2-evolution-version.active', console_styles)
|
||||
self.assertIn('[data-evolution-scope="admin"]', console_styles)
|
||||
self.assertIn('.v2-evolution-version.active', evolution_styles)
|
||||
self.assertIn('[data-evolution-scope="admin"]', evolution_styles)
|
||||
self.assertIn('.v2-weighing-workspace', evolution_styles)
|
||||
self.assertIn('.v2-family-model.active', evolution_styles)
|
||||
self.assertIn('.v2-personal-line', evolution_styles)
|
||||
self.assertIn('.v2-linkage-guard', evolution_styles)
|
||||
self.assertIn('.v2-parameter-grid', evolution_styles)
|
||||
self.assertIn('[data-demo-role="admin"] [data-evolution-scope="admin"]', evolution_styles)
|
||||
self.assertIn(
|
||||
'color-mix(in srgb, var(--v2-accent-soft) 86%, white)',
|
||||
console_styles,
|
||||
@@ -455,7 +484,9 @@ operations:
|
||||
self.assertIn("window.requestAnimationFrame(initSeatTwin)", app_script)
|
||||
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
|
||||
self.assertIn('"cognitive-agent:module-change"', app_script)
|
||||
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v119"', service_worker)
|
||||
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v120"', service_worker)
|
||||
self.assertIn('"/self-evolution.js?v=seat-weighing-2"', service_worker)
|
||||
self.assertIn('"/self-evolution.css?v=seat-weighing-5"', service_worker)
|
||||
self.assertIn(
|
||||
'"/assets/models/n7_seat.glb?v=e7f50f03"', service_worker
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user