1623 lines
56 KiB
HTML
1623 lines
56 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<meta name="theme-color" content="#f4f7fb" />
|
||
<title>从初学者需求到完整项目计划 · AxiOmron PCBCheck</title>
|
||
<style>
|
||
:root {
|
||
--ink: #10233f;
|
||
--ink-soft: #40536d;
|
||
--muted: #6d7e92;
|
||
--canvas: #f4f7fb;
|
||
--surface: #ffffff;
|
||
--surface-soft: #edf4fa;
|
||
--line: #d9e3ee;
|
||
--primary: #0877bd;
|
||
--primary-dark: #075b91;
|
||
--primary-soft: #e4f3fb;
|
||
--teal: #0e8f83;
|
||
--teal-soft: #e2f5f1;
|
||
--orange: #d86b18;
|
||
--orange-soft: #fff0e3;
|
||
--danger: #c84343;
|
||
--danger-soft: #fdeaea;
|
||
--shadow: 0 18px 55px rgba(28, 58, 90, .10);
|
||
--font-sans: "Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI", Arial, sans-serif;
|
||
--font-mono: "Cascadia Mono", Consolas, monospace;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
|
||
html,
|
||
body {
|
||
width: 100%;
|
||
height: 100%;
|
||
margin: 0;
|
||
overflow: hidden;
|
||
background: var(--canvas);
|
||
}
|
||
|
||
body {
|
||
color: var(--ink);
|
||
font-family: var(--font-sans);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
button { font: inherit; }
|
||
|
||
button:focus-visible {
|
||
outline: 3px solid rgba(8, 119, 189, .32);
|
||
outline-offset: 3px;
|
||
}
|
||
|
||
.progress-track {
|
||
position: fixed;
|
||
z-index: 50;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
height: 5px;
|
||
background: #dfe8f1;
|
||
}
|
||
|
||
.progress-track span {
|
||
display: block;
|
||
width: 12.5%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, var(--primary), var(--teal));
|
||
transition: width .28s ease-out;
|
||
}
|
||
|
||
.brand-mark {
|
||
position: fixed;
|
||
z-index: 30;
|
||
top: 26px;
|
||
left: clamp(24px, 4vw, 64px);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 11px;
|
||
color: var(--ink-soft);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
letter-spacing: .08em;
|
||
}
|
||
|
||
.brand-mark::before {
|
||
width: 30px;
|
||
height: 8px;
|
||
border-radius: 999px;
|
||
background: linear-gradient(90deg, var(--primary), var(--teal));
|
||
content: "";
|
||
}
|
||
|
||
.deck {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100dvh;
|
||
}
|
||
|
||
.slide {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: grid;
|
||
grid-template-rows: 1fr auto;
|
||
min-height: 100%;
|
||
padding: clamp(72px, 8vh, 96px) clamp(28px, 7vw, 128px) clamp(84px, 10vh, 112px);
|
||
overflow: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translateX(34px);
|
||
transition: opacity .24s ease-out, transform .28s ease-out;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: #b8c7d8 transparent;
|
||
}
|
||
|
||
.slide::before {
|
||
position: absolute;
|
||
z-index: -2;
|
||
top: -24%;
|
||
right: -10%;
|
||
width: min(50vw, 760px);
|
||
aspect-ratio: 1;
|
||
border-radius: 50%;
|
||
background: radial-gradient(circle, rgba(8, 119, 189, .10), transparent 68%);
|
||
content: "";
|
||
}
|
||
|
||
.slide::after {
|
||
position: absolute;
|
||
z-index: -2;
|
||
bottom: -30%;
|
||
left: -14%;
|
||
width: min(42vw, 650px);
|
||
aspect-ratio: 1;
|
||
border-radius: 50%;
|
||
background: radial-gradient(circle, rgba(14, 143, 131, .08), transparent 70%);
|
||
content: "";
|
||
}
|
||
|
||
.slide.is-active {
|
||
z-index: 2;
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transform: translateX(0);
|
||
}
|
||
|
||
.slide-body {
|
||
align-self: center;
|
||
width: min(100%, 1280px);
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.slide-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
width: min(100%, 1280px);
|
||
margin: 28px auto 0;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
letter-spacing: .04em;
|
||
}
|
||
|
||
.slide-index {
|
||
color: var(--primary);
|
||
font-family: var(--font-mono);
|
||
}
|
||
|
||
.eyebrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin: 0 0 18px;
|
||
color: var(--primary);
|
||
font-family: var(--font-mono);
|
||
font-size: clamp(11px, .9vw, 14px);
|
||
font-weight: 700;
|
||
letter-spacing: .1em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.eyebrow::before {
|
||
width: 28px;
|
||
height: 2px;
|
||
background: currentColor;
|
||
content: "";
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
p { margin-top: 0; }
|
||
|
||
h1 {
|
||
max-width: 980px;
|
||
margin-bottom: 24px;
|
||
font-size: clamp(48px, 6.2vw, 92px);
|
||
line-height: 1.08;
|
||
letter-spacing: -.05em;
|
||
}
|
||
|
||
h2 {
|
||
max-width: 1050px;
|
||
margin-bottom: 28px;
|
||
font-size: clamp(36px, 4.2vw, 64px);
|
||
line-height: 1.16;
|
||
letter-spacing: -.04em;
|
||
}
|
||
|
||
h3 {
|
||
margin-bottom: 10px;
|
||
font-size: clamp(18px, 1.4vw, 22px);
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.lede {
|
||
max-width: 820px;
|
||
margin-bottom: 0;
|
||
color: var(--ink-soft);
|
||
font-size: clamp(18px, 1.55vw, 24px);
|
||
line-height: 1.72;
|
||
}
|
||
|
||
.accent { color: var(--primary); }
|
||
.teal { color: var(--teal); }
|
||
.orange { color: var(--orange); }
|
||
|
||
.cover-grid {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.4fr) minmax(270px, .6fr);
|
||
gap: clamp(42px, 7vw, 110px);
|
||
align-items: end;
|
||
}
|
||
|
||
.cover-card {
|
||
padding: 28px;
|
||
border: 1px solid var(--line);
|
||
border-radius: 22px;
|
||
background: rgba(255, 255, 255, .92);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.cover-card .label,
|
||
.mini-label {
|
||
color: var(--muted);
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: .08em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.cover-card strong {
|
||
display: block;
|
||
margin: 12px 0 24px;
|
||
font-size: 27px;
|
||
line-height: 1.28;
|
||
}
|
||
|
||
.meta-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
padding: 13px 0;
|
||
border-top: 1px solid var(--line);
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.meta-row b { color: var(--ink); }
|
||
|
||
.compare-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto 1fr;
|
||
gap: 22px;
|
||
align-items: stretch;
|
||
margin-top: 34px;
|
||
}
|
||
|
||
.compare-card,
|
||
.project-card,
|
||
.level-card,
|
||
.debug-card,
|
||
.step-card,
|
||
.rule-card,
|
||
.takeaway-card {
|
||
border: 1px solid var(--line);
|
||
border-radius: 20px;
|
||
background: rgba(255, 255, 255, .94);
|
||
box-shadow: 0 12px 38px rgba(28, 58, 90, .07);
|
||
}
|
||
|
||
.compare-card { padding: 28px; }
|
||
.compare-card.is-now { border-color: #abd8ed; background: var(--primary-soft); }
|
||
|
||
.compare-card p {
|
||
margin: 0;
|
||
color: var(--ink-soft);
|
||
font-size: clamp(16px, 1.25vw, 20px);
|
||
line-height: 1.65;
|
||
}
|
||
|
||
.compare-arrow {
|
||
display: grid;
|
||
width: 54px;
|
||
place-items: center;
|
||
color: var(--primary);
|
||
font-size: 30px;
|
||
font-weight: 300;
|
||
}
|
||
|
||
.analogy {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
max-width: 890px;
|
||
margin-top: 24px;
|
||
padding: 16px 20px;
|
||
border-left: 4px solid var(--teal);
|
||
color: var(--ink-soft);
|
||
background: var(--teal-soft);
|
||
font-size: clamp(16px, 1.25vw, 20px);
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.project-layout {
|
||
display: grid;
|
||
grid-template-columns: .78fr 1.22fr;
|
||
gap: clamp(32px, 6vw, 86px);
|
||
align-items: center;
|
||
}
|
||
|
||
.project-evidence-layout {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.35fr) minmax(310px, .65fr);
|
||
gap: 22px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.app-shot-card {
|
||
position: relative;
|
||
margin: 0;
|
||
padding: 10px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--line);
|
||
border-radius: 20px;
|
||
background: var(--surface);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.app-shot-frame {
|
||
aspect-ratio: 16 / 8.75;
|
||
overflow: hidden;
|
||
border: 1px solid #cbd7e4;
|
||
border-radius: 13px;
|
||
background: #e9eff5;
|
||
}
|
||
|
||
.app-shot-frame img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: left top;
|
||
}
|
||
|
||
.shot-badge {
|
||
position: absolute;
|
||
top: 23px;
|
||
right: 23px;
|
||
padding: 7px 10px;
|
||
border: 1px solid rgba(255, 255, 255, .65);
|
||
border-radius: 999px;
|
||
color: #fff;
|
||
background: rgba(16, 35, 63, .82);
|
||
font-size: 11px;
|
||
font-weight: 800;
|
||
letter-spacing: .04em;
|
||
}
|
||
|
||
.app-shot-card figcaption {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 18px;
|
||
padding: 11px 7px 2px;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.app-shot-card figcaption b { color: var(--ink); }
|
||
|
||
.flow-title {
|
||
display: block;
|
||
margin-bottom: 10px;
|
||
color: var(--ink-soft);
|
||
font-size: 14px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.flow-list.compact { gap: 8px; }
|
||
|
||
.flow-list.compact .flow-item {
|
||
min-height: 54px;
|
||
padding: 8px 13px;
|
||
grid-template-columns: 38px 1fr;
|
||
}
|
||
|
||
.flow-list.compact .flow-no {
|
||
width: 32px;
|
||
height: 32px;
|
||
}
|
||
|
||
.flow-list.compact .flow-item b { font-size: 14px; }
|
||
|
||
.flow-list.compact .flow-item span:last-child { display: none; }
|
||
|
||
.project-summary {
|
||
padding: 30px;
|
||
border-left: 5px solid var(--primary);
|
||
background: var(--surface);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.project-summary strong {
|
||
display: block;
|
||
margin-bottom: 12px;
|
||
font-size: 26px;
|
||
}
|
||
|
||
.project-summary p {
|
||
margin: 0;
|
||
color: var(--ink-soft);
|
||
font-size: 17px;
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.flow-list { display: grid; gap: 12px; }
|
||
|
||
.flow-item {
|
||
display: grid;
|
||
grid-template-columns: 46px 1fr auto;
|
||
gap: 16px;
|
||
align-items: center;
|
||
min-height: 68px;
|
||
padding: 13px 18px;
|
||
border: 1px solid var(--line);
|
||
border-radius: 15px;
|
||
background: var(--surface);
|
||
}
|
||
|
||
.flow-no {
|
||
display: grid;
|
||
width: 38px;
|
||
height: 38px;
|
||
place-items: center;
|
||
border-radius: 50%;
|
||
color: #fff;
|
||
background: var(--primary);
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.flow-item b { font-size: 17px; }
|
||
.flow-item span:last-child { color: var(--muted); font-size: 12px; font-weight: 700; }
|
||
|
||
.level-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 18px;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.level-card { position: relative; padding: 28px; overflow: hidden; }
|
||
|
||
.level-card::before {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
height: 5px;
|
||
background: var(--primary);
|
||
content: "";
|
||
}
|
||
|
||
.level-card:nth-child(2)::before { background: var(--teal); }
|
||
.level-card:nth-child(3)::before { background: var(--orange); }
|
||
|
||
.level-badge {
|
||
display: inline-block;
|
||
margin-bottom: 18px;
|
||
padding: 7px 11px;
|
||
border-radius: 999px;
|
||
color: var(--primary-dark);
|
||
background: var(--primary-soft);
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.level-card:nth-child(2) .level-badge { color: #086a62; background: var(--teal-soft); }
|
||
.level-card:nth-child(3) .level-badge { color: #9c4a10; background: var(--orange-soft); }
|
||
|
||
.level-card p {
|
||
margin: 0;
|
||
color: var(--ink-soft);
|
||
font-size: 16px;
|
||
line-height: 1.65;
|
||
}
|
||
|
||
.level-footer {
|
||
margin-top: 22px;
|
||
color: var(--ink-soft);
|
||
font-size: clamp(16px, 1.35vw, 20px);
|
||
text-align: center;
|
||
}
|
||
|
||
.debug-layout {
|
||
display: grid;
|
||
grid-template-columns: .92fr 1.08fr;
|
||
gap: clamp(30px, 5vw, 72px);
|
||
align-items: center;
|
||
}
|
||
|
||
.bug-box {
|
||
padding: 26px;
|
||
border: 1px solid #efc7c7;
|
||
border-radius: 18px;
|
||
background: var(--danger-soft);
|
||
}
|
||
|
||
.bug-box strong {
|
||
display: block;
|
||
margin: 10px 0 16px;
|
||
color: #8b2e2e;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.log-line {
|
||
margin: 7px 0 0;
|
||
color: #653d3d;
|
||
font-family: var(--font-mono);
|
||
font-size: 13px;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.mail-analogy {
|
||
margin: 18px 0 0;
|
||
padding: 18px 20px;
|
||
border-left: 4px solid var(--primary);
|
||
color: var(--ink-soft);
|
||
background: var(--primary-soft);
|
||
font-size: 17px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.logic-compare {
|
||
display: grid;
|
||
grid-template-columns: .84fr 1.16fr;
|
||
gap: 12px;
|
||
}
|
||
|
||
.logic-panel {
|
||
min-height: 176px;
|
||
padding: 19px;
|
||
border: 1px solid var(--line);
|
||
border-radius: 16px;
|
||
background: var(--surface);
|
||
}
|
||
|
||
.logic-panel.is-wrong {
|
||
border-color: #efc7c7;
|
||
background: var(--danger-soft);
|
||
}
|
||
|
||
.logic-panel.is-right {
|
||
border-color: #b9dfd8;
|
||
background: var(--teal-soft);
|
||
}
|
||
|
||
.logic-panel pre {
|
||
margin: 14px 0 0;
|
||
white-space: pre-wrap;
|
||
color: var(--ink);
|
||
font-family: var(--font-mono);
|
||
font-size: clamp(11px, .86vw, 13px);
|
||
line-height: 1.75;
|
||
}
|
||
|
||
.debug-clue {
|
||
margin: 12px 0 0;
|
||
padding: 11px 14px;
|
||
border-left: 4px solid var(--orange);
|
||
color: #75401a;
|
||
background: var(--orange-soft);
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.debug-card { overflow: hidden; }
|
||
|
||
.debug-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
padding: 18px 22px;
|
||
border-bottom: 1px solid var(--line);
|
||
background: #f9fbfd;
|
||
}
|
||
|
||
.status-dot {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.status-dot::before {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: #9cafc2;
|
||
content: "";
|
||
}
|
||
|
||
.debug-body { padding: 24px; }
|
||
|
||
.scenario-buttons {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 12px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.scenario-button,
|
||
.nav-button {
|
||
min-height: 46px;
|
||
border: 1px solid var(--line);
|
||
border-radius: 12px;
|
||
color: var(--ink);
|
||
background: var(--surface);
|
||
cursor: pointer;
|
||
font-weight: 700;
|
||
transition: color .18s ease, background .18s ease, border-color .18s ease, transform .18s ease;
|
||
}
|
||
|
||
.scenario-button:hover,
|
||
.scenario-button.is-selected {
|
||
border-color: var(--primary);
|
||
color: #fff;
|
||
background: var(--primary);
|
||
}
|
||
|
||
.scenario-result {
|
||
min-height: 170px;
|
||
padding: 20px;
|
||
border: 1px solid var(--line);
|
||
border-radius: 14px;
|
||
background: #f8fafc;
|
||
}
|
||
|
||
.scenario-result h3 { margin-bottom: 12px; }
|
||
|
||
.result-line {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 20px;
|
||
padding: 10px 0;
|
||
border-top: 1px solid var(--line);
|
||
color: var(--ink-soft);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.result-line b { color: var(--ink); }
|
||
|
||
.method-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 12px;
|
||
margin-top: 28px;
|
||
}
|
||
|
||
.step-card { padding: 22px 18px; }
|
||
|
||
.step-number {
|
||
display: block;
|
||
margin-bottom: 28px;
|
||
color: var(--primary);
|
||
font-family: var(--font-mono);
|
||
font-size: 28px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.step-card p {
|
||
margin: 0;
|
||
color: var(--ink-soft);
|
||
font-size: 14px;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.prompt-box {
|
||
margin: 0;
|
||
padding: 17px 22px;
|
||
border-left: 4px solid var(--teal);
|
||
color: #16524e;
|
||
background: var(--teal-soft);
|
||
font-size: clamp(15px, 1.15vw, 18px);
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.evidence-row {
|
||
display: grid;
|
||
grid-template-columns: .9fr 1.1fr;
|
||
gap: 14px;
|
||
align-items: stretch;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.test-proof {
|
||
overflow: hidden;
|
||
border: 1px solid #cbd7e4;
|
||
border-radius: 14px;
|
||
color: #dcebf7;
|
||
background: #12243a;
|
||
box-shadow: 0 10px 28px rgba(18, 36, 58, .12);
|
||
}
|
||
|
||
.test-proof-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
padding: 10px 14px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, .12);
|
||
color: #99abc0;
|
||
font-family: var(--font-mono);
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
letter-spacing: .05em;
|
||
}
|
||
|
||
.test-proof-head b { color: #82dfc7; }
|
||
|
||
.test-row {
|
||
display: grid;
|
||
grid-template-columns: 46px 1fr;
|
||
gap: 10px;
|
||
align-items: center;
|
||
padding: 8px 14px;
|
||
border-top: 1px solid rgba(255, 255, 255, .08);
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.pass-label {
|
||
color: #82dfc7;
|
||
font-family: var(--font-mono);
|
||
font-size: 10px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.requirement-layout,
|
||
.question-layout,
|
||
.contract-layout,
|
||
.plan-layout,
|
||
.phase-layout {
|
||
display: grid;
|
||
gap: clamp(24px, 4vw, 60px);
|
||
align-items: center;
|
||
}
|
||
|
||
.requirement-layout { grid-template-columns: .72fr 1.28fr; }
|
||
.question-layout { grid-template-columns: .72fr 1.28fr; }
|
||
.contract-layout,
|
||
.plan-layout,
|
||
.phase-layout { grid-template-columns: 1fr; }
|
||
|
||
.requirement-intro,
|
||
.question-intro {
|
||
align-self: center;
|
||
}
|
||
|
||
.quote-card {
|
||
margin-top: 24px;
|
||
padding: 22px 24px;
|
||
border-left: 5px solid var(--orange);
|
||
border-radius: 0 16px 16px 0;
|
||
color: var(--ink);
|
||
background: var(--orange-soft);
|
||
box-shadow: 0 10px 28px rgba(216, 107, 24, .08);
|
||
font-size: clamp(17px, 1.25vw, 20px);
|
||
font-weight: 700;
|
||
line-height: 1.65;
|
||
}
|
||
|
||
.small-note {
|
||
margin: 18px 0 0;
|
||
color: var(--muted);
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.requirement-grid,
|
||
.question-grid,
|
||
.contract-grid,
|
||
.plan-grid,
|
||
.phase-grid,
|
||
.boundary-row {
|
||
display: grid;
|
||
gap: 12px;
|
||
}
|
||
|
||
.requirement-grid { grid-template-columns: repeat(3, 1fr); }
|
||
.question-grid,
|
||
.contract-grid,
|
||
.plan-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.phase-grid { grid-template-columns: repeat(4, 1fr); }
|
||
.boundary-row { grid-template-columns: repeat(3, 1fr); margin-top: 18px; }
|
||
|
||
.requirement-card,
|
||
.question-card,
|
||
.contract-card,
|
||
.plan-card,
|
||
.phase-card,
|
||
.boundary-card {
|
||
border: 1px solid var(--line);
|
||
border-radius: 16px;
|
||
background: rgba(255, 255, 255, .94);
|
||
box-shadow: 0 10px 28px rgba(28, 58, 90, .06);
|
||
}
|
||
|
||
.requirement-card {
|
||
min-height: 92px;
|
||
padding: 16px;
|
||
}
|
||
|
||
.requirement-card strong,
|
||
.question-card strong,
|
||
.contract-card strong,
|
||
.plan-card strong,
|
||
.phase-card strong,
|
||
.boundary-card strong {
|
||
display: block;
|
||
margin-bottom: 7px;
|
||
color: var(--ink);
|
||
font-size: 15px;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.requirement-card span,
|
||
.question-card p,
|
||
.contract-card p,
|
||
.plan-card p,
|
||
.phase-card p,
|
||
.boundary-card p {
|
||
margin: 0;
|
||
color: var(--ink-soft);
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.requirement-card .card-number,
|
||
.phase-card .card-number {
|
||
display: block;
|
||
margin-bottom: 10px;
|
||
color: var(--primary);
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
font-weight: 800;
|
||
letter-spacing: .08em;
|
||
}
|
||
|
||
.question-intro .quote-card {
|
||
border-left-color: var(--primary);
|
||
color: var(--primary-dark);
|
||
background: var(--primary-soft);
|
||
}
|
||
|
||
.question-card,
|
||
.contract-card,
|
||
.plan-card {
|
||
min-height: 148px;
|
||
padding: 20px;
|
||
}
|
||
|
||
.question-card::before,
|
||
.contract-card::before,
|
||
.plan-card::before {
|
||
display: block;
|
||
width: 30px;
|
||
height: 4px;
|
||
margin-bottom: 18px;
|
||
border-radius: 999px;
|
||
background: var(--primary);
|
||
content: "";
|
||
}
|
||
|
||
.question-card:nth-child(2)::before,
|
||
.contract-card:nth-child(2)::before,
|
||
.plan-card:nth-child(2)::before { background: var(--teal); }
|
||
|
||
.question-card:nth-child(3)::before,
|
||
.contract-card:nth-child(3)::before,
|
||
.plan-card:nth-child(3)::before { background: var(--orange); }
|
||
|
||
.question-card:nth-child(4)::before,
|
||
.contract-card:nth-child(4)::before,
|
||
.plan-card:nth-child(4)::before { background: #7b61a8; }
|
||
|
||
.card-kicker {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
color: var(--muted);
|
||
font-family: var(--font-mono);
|
||
font-size: 10px;
|
||
font-weight: 800;
|
||
letter-spacing: .08em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.contract-flow {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 9px;
|
||
margin-top: 22px;
|
||
color: var(--primary-dark);
|
||
font-size: 13px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.contract-flow span {
|
||
padding: 9px 12px;
|
||
border: 1px solid #abd8ed;
|
||
border-radius: 999px;
|
||
background: var(--primary-soft);
|
||
}
|
||
|
||
.contract-flow i {
|
||
color: var(--muted);
|
||
font-style: normal;
|
||
}
|
||
|
||
.plan-card {
|
||
min-height: 168px;
|
||
}
|
||
|
||
.plan-card strong { font-size: 20px; }
|
||
|
||
.plan-card p { font-size: 15px; line-height: 1.6; }
|
||
|
||
.plan-foot,
|
||
.phase-foot {
|
||
margin: 20px auto 0;
|
||
color: var(--ink-soft);
|
||
font-size: clamp(15px, 1.2vw, 18px);
|
||
line-height: 1.6;
|
||
text-align: center;
|
||
}
|
||
|
||
.phase-card {
|
||
min-height: 134px;
|
||
padding: 17px;
|
||
}
|
||
|
||
.phase-card strong { font-size: 15px; }
|
||
.phase-card p { font-size: 12px; }
|
||
|
||
.phase-card:nth-child(2) { background: #f1faf8; }
|
||
.phase-card:nth-child(3) { background: #fff8f1; }
|
||
.phase-card:nth-child(4) { background: #f6f2fb; }
|
||
.phase-card:nth-child(5) { background: #f3f8fc; }
|
||
.phase-card:nth-child(6) { background: #f5faf8; }
|
||
.phase-card:nth-child(7) { background: #fff8f1; }
|
||
|
||
.phase-foot {
|
||
padding: 13px 18px;
|
||
border-left: 4px solid var(--teal);
|
||
background: var(--teal-soft);
|
||
}
|
||
|
||
.boundary-card {
|
||
padding: 17px 18px;
|
||
}
|
||
|
||
.boundary-card strong { font-size: 16px; }
|
||
.boundary-card p { font-size: 13px; }
|
||
|
||
.boundary-card:nth-child(2) { border-top: 4px solid var(--teal); }
|
||
.boundary-card:nth-child(3) { border-top: 4px solid var(--orange); }
|
||
|
||
.boundary-note {
|
||
margin-top: 20px;
|
||
padding: 15px 18px;
|
||
border: 1px solid #abd8ed;
|
||
border-radius: 14px;
|
||
color: var(--primary-dark);
|
||
background: var(--primary-soft);
|
||
font-size: 15px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.rule-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16px;
|
||
margin-top: 28px;
|
||
}
|
||
|
||
.rule-card {
|
||
display: grid;
|
||
grid-template-columns: 54px 1fr;
|
||
gap: 18px;
|
||
align-items: start;
|
||
padding: 22px;
|
||
}
|
||
|
||
.rule-no {
|
||
display: grid;
|
||
width: 48px;
|
||
height: 48px;
|
||
place-items: center;
|
||
border-radius: 14px;
|
||
color: var(--primary-dark);
|
||
background: var(--primary-soft);
|
||
font-family: var(--font-mono);
|
||
font-weight: 800;
|
||
}
|
||
|
||
.rule-card:nth-child(4) .rule-no { color: #973e0c; background: var(--orange-soft); }
|
||
|
||
.rule-card p {
|
||
margin: 0;
|
||
color: var(--ink-soft);
|
||
font-size: 15px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.finish-layout {
|
||
display: grid;
|
||
grid-template-columns: 1.08fr .92fr;
|
||
gap: clamp(40px, 7vw, 108px);
|
||
align-items: center;
|
||
}
|
||
|
||
.finish-statement {
|
||
max-width: 760px;
|
||
color: var(--ink-soft);
|
||
font-size: clamp(20px, 1.7vw, 27px);
|
||
line-height: 1.65;
|
||
}
|
||
|
||
.takeaway-list { display: grid; gap: 14px; }
|
||
|
||
.takeaway-card {
|
||
padding: 22px 24px;
|
||
border-left: 5px solid var(--primary);
|
||
}
|
||
|
||
.takeaway-card:nth-child(2) { border-left-color: var(--teal); }
|
||
.takeaway-card:nth-child(3) { border-left-color: var(--orange); }
|
||
|
||
.takeaway-card b { display: block; margin-bottom: 6px; font-size: 18px; }
|
||
.takeaway-card span { color: var(--muted); font-size: 14px; line-height: 1.5; }
|
||
|
||
.deck-controls {
|
||
position: fixed;
|
||
z-index: 40;
|
||
right: clamp(20px, 3vw, 50px);
|
||
bottom: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.nav-button {
|
||
min-width: 86px;
|
||
padding: 0 18px;
|
||
box-shadow: 0 8px 24px rgba(28, 58, 90, .08);
|
||
}
|
||
|
||
.nav-button.primary {
|
||
border-color: var(--primary);
|
||
color: #fff;
|
||
background: var(--primary);
|
||
}
|
||
|
||
.nav-button:hover { transform: translateY(-1px); }
|
||
.nav-button:disabled { opacity: .42; cursor: default; transform: none; }
|
||
|
||
.dots {
|
||
position: fixed;
|
||
z-index: 40;
|
||
bottom: 38px;
|
||
left: clamp(20px, 4vw, 64px);
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.dot {
|
||
width: 9px;
|
||
height: 9px;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
background: #b9c8d7;
|
||
cursor: pointer;
|
||
transition: width .18s ease, border-radius .18s ease, background .18s ease;
|
||
}
|
||
|
||
.dot.is-active { width: 26px; border-radius: 999px; background: var(--primary); }
|
||
|
||
@media (max-width: 920px) {
|
||
html,
|
||
body { overflow: auto; }
|
||
|
||
.deck { min-height: 100dvh; }
|
||
.slide { position: relative; display: none; min-height: 100dvh; padding: 76px 24px 104px; overflow-x: hidden; overflow-y: auto; }
|
||
.slide.is-active { display: grid; }
|
||
.cover-grid,
|
||
.project-layout,
|
||
.project-evidence-layout,
|
||
.debug-layout,
|
||
.finish-layout,
|
||
.requirement-layout,
|
||
.question-layout { grid-template-columns: 1fr; }
|
||
.compare-grid { grid-template-columns: 1fr; }
|
||
.compare-arrow { width: auto; height: 30px; transform: rotate(90deg); }
|
||
.level-grid { grid-template-columns: 1fr; }
|
||
.method-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.evidence-row { grid-template-columns: 1fr; }
|
||
.rule-grid { grid-template-columns: 1fr; }
|
||
.requirement-grid,
|
||
.question-grid,
|
||
.contract-grid,
|
||
.plan-grid,
|
||
.phase-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.brand-mark { top: 22px; }
|
||
.slide-footer span:first-child { display: none; }
|
||
.slide-footer { justify-content: flex-end; }
|
||
}
|
||
|
||
@media (max-width: 560px) {
|
||
h1 { font-size: clamp(42px, 13vw, 64px); }
|
||
h2 { font-size: clamp(32px, 9vw, 46px); }
|
||
.method-grid,
|
||
.logic-compare,
|
||
.scenario-buttons,
|
||
.requirement-grid,
|
||
.question-grid,
|
||
.contract-grid,
|
||
.plan-grid,
|
||
.phase-grid,
|
||
.boundary-row { grid-template-columns: 1fr; }
|
||
.flow-item { grid-template-columns: 42px 1fr; }
|
||
.flow-item span:last-child { display: none; }
|
||
.slide-footer { display: none; }
|
||
.deck-controls { right: 16px; bottom: 18px; }
|
||
.dots { left: 16px; bottom: 31px; }
|
||
.nav-button { min-width: 66px; padding: 0 12px; }
|
||
}
|
||
|
||
@media (max-height: 800px) and (min-width: 921px) {
|
||
.slide { padding-top: 68px; padding-bottom: 76px; }
|
||
h1 { font-size: clamp(44px, 5.4vw, 76px); }
|
||
h2 { margin-bottom: 20px; font-size: clamp(32px, 3.7vw, 52px); }
|
||
.project-evidence-layout { grid-template-columns: minmax(0, 1.3fr) minmax(290px, .7fr); gap: 16px; }
|
||
.app-shot-frame { aspect-ratio: 16 / 7.15; }
|
||
.app-shot-card figcaption { padding-top: 7px; font-size: 11px; }
|
||
.flow-list.compact { gap: 6px; }
|
||
.flow-list.compact .flow-item { min-height: 46px; padding-top: 6px; padding-bottom: 6px; }
|
||
.level-card { padding: 22px; }
|
||
.method-grid { margin-top: 18px; }
|
||
.step-card { padding: 16px; }
|
||
.step-number { margin-bottom: 18px; }
|
||
.evidence-row { margin-top: 12px; }
|
||
.prompt-box { padding: 13px 16px; }
|
||
.test-row { padding-top: 6px; padding-bottom: 6px; }
|
||
.rule-grid { margin-top: 20px; }
|
||
.rule-card { padding: 17px; }
|
||
.requirement-card { min-height: 78px; padding: 13px; }
|
||
.question-card,
|
||
.contract-card,
|
||
.plan-card { min-height: 126px; padding: 16px; }
|
||
.question-card::before,
|
||
.contract-card::before,
|
||
.plan-card::before { margin-bottom: 12px; }
|
||
.plan-layout { gap: 14px; }
|
||
.plan-layout h2 { margin-bottom: 8px; }
|
||
.plan-card { min-height: 112px; padding: 14px; }
|
||
.plan-card::before { margin-bottom: 9px; }
|
||
.plan-card strong { font-size: 17px; }
|
||
.plan-card p { font-size: 13px; line-height: 1.45; }
|
||
.plan-foot { margin-top: 8px; font-size: 14px; line-height: 1.35; }
|
||
.phase-card { min-height: 108px; padding: 13px; }
|
||
.phase-foot { margin-top: 14px; padding: 10px 13px; font-size: 14px; }
|
||
.slide-footer { margin-top: 16px; }
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
scroll-behavior: auto !important;
|
||
transition-duration: .01ms !important;
|
||
animation-duration: .01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="progress-track" aria-hidden="true"><span id="progressBar"></span></div>
|
||
<div class="brand-mark">AI PROGRAMMING · FIELD CASE</div>
|
||
|
||
<main class="deck" id="deck" aria-live="polite">
|
||
<section class="slide is-active" data-slide="0" data-title="封面" aria-label="第 1 页:封面">
|
||
<div class="slide-body cover-grid">
|
||
<div>
|
||
<p class="eyebrow">3 MONTHS · ONE REAL PROJECT</p>
|
||
<h1>从写 <span class="orange">if-else</span><br>到 <span class="accent">Vibe Coding</span></h1>
|
||
<p class="lede">我如何用 AI 搭建一套 PCB 目检放行上位机,以及普通人怎样上手、调试和避坑。</p>
|
||
</div>
|
||
<aside class="cover-card" aria-label="汇报信息">
|
||
<span class="label">CASE STUDY</span>
|
||
<strong>AxiOmron<br>PCBCheck</strong>
|
||
<div class="meta-row"><span>项目</span><b>工业上位机</b></div>
|
||
<div class="meta-row"><span>重点</span><b>上手 · 调试 · 边界</b></div>
|
||
<div class="meta-row"><span>时长</span><b>10–12 MIN</b></div>
|
||
</aside>
|
||
</div>
|
||
<footer class="slide-footer"><span>一段真实的 AI 编程实践</span><span class="slide-index">01 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="1" data-title="初始需求" aria-label="第 2 页:初始需求">
|
||
<div class="slide-body requirement-layout">
|
||
<div class="requirement-intro">
|
||
<p class="eyebrow">01 / THE FIRST REQUEST</p>
|
||
<h2>最开始,我只想让它<br><span class="orange">先跑起来</span>。</h2>
|
||
<div class="quote-card">“我需要写一个程序:PCB 到位后扫码,根据条码检查文件是否存在,再把结果返回给 PLC。”</div>
|
||
<p class="small-note">这就是《开发流程.txt》里的最初版本:目标很清楚,但工程边界还没有被说出来。</p>
|
||
</div>
|
||
<div class="requirement-grid" aria-label="初始需求清单">
|
||
<article class="requirement-card"><span class="card-number">01</span><strong>PCB 到位后开始处理</strong><span>收到触发,进入本轮任务。</span></article>
|
||
<article class="requirement-card"><span class="card-number">02</span><strong>读取一个条码</strong><span>拿到 PCB 身份信息。</span></article>
|
||
<article class="requirement-card"><span class="card-number">03</span><strong>检查对应文件</strong><span>根据条码查找指定文件。</span></article>
|
||
<article class="requirement-card"><span class="card-number">04</span><strong>文件存在显示正常</strong><span>给出 OK 结果。</span></article>
|
||
<article class="requirement-card"><span class="card-number">05</span><strong>文件不存在显示异常</strong><span>给出 NG 结果。</span></article>
|
||
<article class="requirement-card"><span class="card-number">06</span><strong>页面显示当前状态</strong><span>看到条码、结果和日志。</span></article>
|
||
<article class="requirement-card"><span class="card-number">07</span><strong>保存最近处理记录</strong><span>之后可以回看过程。</span></article>
|
||
<article class="requirement-card"><span class="card-number">08</span><strong>先使用模拟数据</strong><span>不一开始连接真实设备。</span></article>
|
||
<article class="requirement-card"><span class="card-number">09</span><strong>模拟正常、异常、故障</strong><span>页面提供可控演示入口。</span></article>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>初版需求:先把业务主线讲清楚</span><span class="slide-index">02 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="2" data-title="第一次对话" aria-label="第 3 页:第一次对话">
|
||
<div class="slide-body question-layout">
|
||
<div class="question-intro">
|
||
<p class="eyebrow">02 / ASK BEFORE BUILDING</p>
|
||
<h2>真正开始写代码前,<br>我先问:<span class="accent">还缺什么?</span></h2>
|
||
<div class="quote-card">“还需要哪些信息才能开始写程序?”</div>
|
||
<p class="small-note">这一步把“能不能写”变成“哪些事实还没有确认”。</p>
|
||
</div>
|
||
<div class="question-grid" aria-label="第一次对话中需要补齐的信息">
|
||
<article class="question-card"><span class="card-kicker">01 · PLC</span><strong>触发和握手怎么定义?</strong><p>型号、协议、点位、保持/脉冲、复位、应答、超时和异常恢复。</p></article>
|
||
<article class="question-card"><span class="card-kicker">02 · SCANNERS</span><strong>两把扫码枪怎么返回?</strong><p>IP、端口、通信角色、结束符、编码、超时、双枪关系和断线行为。</p></article>
|
||
<article class="question-card"><span class="card-kicker">03 · FILE RULES</span><strong>什么样的文件算通过?</strong><p>条码格式、文件名规则、精确/模糊匹配、目录类型、空文件和正在写入的处理。</p></article>
|
||
<article class="question-card"><span class="card-kicker">04 · APP & RECORDS</span><strong>程序要怎样被使用和追溯?</strong><p>界面、配置、日志、记录、权限、模拟入口、部署方式和是否需要数据库。</p></article>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>好的协作不是马上给答案,而是先把问题问完整</span><span class="slide-index">03 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="3" data-title="需求逐步完善" aria-label="第 4 页:需求逐步完善">
|
||
<div class="slide-body contract-layout">
|
||
<div>
|
||
<p class="eyebrow">03 / TURN QUESTIONS INTO CONTRACTS</p>
|
||
<h2>需求不再只是“做什么”,<br>还要说清楚<span class="teal">“边界在哪里”</span>。</h2>
|
||
</div>
|
||
<div class="contract-grid" aria-label="需求完善后的四类工程契约">
|
||
<article class="contract-card"><span class="card-kicker">PLC CONTRACT</span><strong>到位、复位、结果、应答</strong><p>定义一轮任务什么时候开始、什么时候结束,以及长保持信号如何避免重复触发。</p></article>
|
||
<article class="contract-card"><span class="card-kicker">SCANNER CONTRACT</span><strong>双路并发、超时、报文边界</strong><p>不把一次 TCP Receive 想当然地当成完整条码,明确无结束符和迟到结果的处理。</p></article>
|
||
<article class="contract-card"><span class="card-kicker">FILE CONTRACT</span><strong>路径安全、结果分类</strong><p>把条码清洗、文件存在、目录不可访问和路径越界区分成可追踪的结果。</p></article>
|
||
<article class="contract-card"><span class="card-kicker">APP CONTRACT</span><strong>状态可见、过程可回看</strong><p>把设备状态、当前任务、日志、统计和模拟场景放进可观察的界面闭环。</p></article>
|
||
</div>
|
||
<div class="contract-flow" aria-label="需求演进链路">
|
||
<span>功能描述</span><i>→</i><span>条件边界</span><i>→</i><span>失败策略</span><i>→</i><span>验收证据</span>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>每补充一个边界,现场就少一个靠猜的地方</span><span class="slide-index">04 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="4" data-title="完整项目计划" aria-label="第 5 页:完整项目计划">
|
||
<div class="slide-body plan-layout">
|
||
<div>
|
||
<p class="eyebrow">04 / THE COMPLETE PROJECT PLAN</p>
|
||
<h2>对话的终点,<br>是一份<span class="accent">可以执行的计划</span>。</h2>
|
||
</div>
|
||
<div class="plan-grid" aria-label="完整项目计划的四个层面">
|
||
<article class="plan-card"><span class="card-kicker">01 · GOAL</span><strong>目标</strong><p>让 PCB 从到位、扫码、文件校验到 PLC 结果反馈,形成一条可解释、可追溯的业务闭环。</p></article>
|
||
<article class="plan-card"><span class="card-kicker">02 · WORKFLOW</span><strong>流程</strong><p>到位触发 → 双枪扫描 → 条码校验 → 文件判断 → 结果保持 → 应答/复位 → 记录完成。</p></article>
|
||
<article class="plan-card"><span class="card-kicker">03 · ARCHITECTURE</span><strong>架构</strong><p>当前项目落地为 .NET 8 + WPF;设备通信、流程编排、界面状态和配置日志保持清晰边界。</p></article>
|
||
<article class="plan-card"><span class="card-kicker">04 · ACCEPTANCE</span><strong>测试与验收</strong><p>单元测试和 Fake 设备先验证逻辑,再用真实 PLC、扫描枪和文件服务完成现场联调。</p></article>
|
||
</div>
|
||
<p class="plan-foot"><strong>计划的价值:</strong>把“我想要一个程序”翻译成目标、状态、接口、阶段、测试和交付门槛。</p>
|
||
</div>
|
||
<footer class="slide-footer"><span>完整计划不是凭空出现的,而是对话逐项补齐后的结果</span><span class="slide-index">05 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="5" data-title="开发阶段路线图" aria-label="第 6 页:开发阶段路线图">
|
||
<div class="slide-body phase-layout">
|
||
<div>
|
||
<p class="eyebrow">05 / BUILD IN RISK ORDER</p>
|
||
<h2>计划被拆成七个小闭环,<br>每一步都有<span class="teal">可以验证的产物</span>。</h2>
|
||
</div>
|
||
<div class="phase-grid" aria-label="七个开发阶段">
|
||
<article class="phase-card"><span class="card-number">01</span><strong>需求与协议确认</strong><p>点位、握手、报文、结果码和文件规则。</p></article>
|
||
<article class="phase-card"><span class="card-number">02</span><strong>项目骨架</strong><p>WPF、配置、日志、依赖注入和基础状态。</p></article>
|
||
<article class="phase-card"><span class="card-number">03</span><strong>PLC 通信</strong><p>读写、超时、断线、重连和模拟接口。</p></article>
|
||
<article class="phase-card"><span class="card-number">04</span><strong>双扫描枪</strong><p>并发触发、无结束符、首个有效结果。</p></article>
|
||
<article class="phase-card"><span class="card-number">05</span><strong>流程编排</strong><p>任务去重、文件安全、结果保持和恢复。</p></article>
|
||
<article class="phase-card"><span class="card-number">06</span><strong>状态界面</strong><p>设备、任务、统计、日志和操作权限。</p></article>
|
||
<article class="phase-card"><span class="card-number">07</span><strong>测试与交付</strong><p>单测、Fake 流程、异常测试和现场验收。</p></article>
|
||
</div>
|
||
<p class="phase-foot">计划不是一次写死:事实确认后可以调整实现,但每次调整都要回到边界、证据和验收标准。</p>
|
||
</div>
|
||
<footer class="slide-footer"><span>AI 加快每个小闭环,我负责决定闭环是否成立</span><span class="slide-index">06 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="6" data-title="Vibe Coding 上手难度" aria-label="第 7 页:Vibe Coding 上手难度">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">06 / HOW HARD IS IT</p>
|
||
<h2>上手不难,<br>难的是从“能跑”走到<span class="orange">“可靠”</span>。</h2>
|
||
<div class="level-grid">
|
||
<article class="level-card">
|
||
<span class="level-badge">门槛低</span>
|
||
<h3>做出原型</h3>
|
||
<p>会描述需求、会运行项目,就能很快看到第一个结果。</p>
|
||
</article>
|
||
<article class="level-card">
|
||
<span class="level-badge">需要方法</span>
|
||
<h3>做到可用</h3>
|
||
<p>要会拆小任务、补充上下文,并检查 AI 实际改了什么。</p>
|
||
</article>
|
||
<article class="level-card">
|
||
<span class="level-badge">仍需专业判断</span>
|
||
<h3>做到可靠</h3>
|
||
<p>需要理解业务风险,并验证断网、超时和错误输入等异常场景。</p>
|
||
</article>
|
||
</div>
|
||
<p class="level-footer"><strong>它降低的是写代码的门槛,</strong>没有消除软件工程本身的难度。</p>
|
||
</div>
|
||
<footer class="slide-footer"><span>有代码基础,上手会更快;没有代码基础,也可以从小工具开始</span><span class="slide-index">07 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="7" data-title="实际项目运行" aria-label="第 8 页:实际项目运行">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">07 / THE PROJECT RUNS</p>
|
||
<h2>这不是概念图,<br><span class="teal">程序真的运行起来了</span>。</h2>
|
||
<div class="project-evidence-layout">
|
||
<figure class="app-shot-card">
|
||
<div class="app-shot-frame">
|
||
<img src="assets/axiomron-dashboard-report.png" width="2575" height="1407" alt="AxiOmron PCBCheck WPF 主界面,展示 PLC、扫码枪、文件校验、安灯、流程状态与运行日志" />
|
||
</div>
|
||
<span class="shot-badge">真实 WPF 运行界面</span>
|
||
<figcaption><b>设备状态、当前条码、流程和日志集中展示</b><span>本地展示环境 · 未连接真实设备</span></figcaption>
|
||
</figure>
|
||
<div>
|
||
<span class="flow-title">一块 PCB 的 5 步旅程</span>
|
||
<div class="flow-list compact" aria-label="PCB 放行流程">
|
||
<div class="flow-item"><span class="flow-no">01</span><b>PLC 通知 PCB 到位</b><span>START</span></div>
|
||
<div class="flow-item"><span class="flow-no">02</span><b>两把扫码枪读取条码</b><span>SCAN</span></div>
|
||
<div class="flow-item"><span class="flow-no">03</span><b>根据条码检查产品文件</b><span>CHECK</span></div>
|
||
<div class="flow-item"><span class="flow-no">04</span><b>发送结果并等待确认</b><span>ACK</span></div>
|
||
<div class="flow-item"><span class="flow-no">05</span><b>保存记录并回到空闲</b><span>DONE</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>运行界面是本地证据,不等同于真实 PLC / SFTP 现场验收</span><span class="slide-index">08 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="8" data-title="真实调试案例" aria-label="第 9 页:真实调试案例">
|
||
<div class="slide-body debug-layout">
|
||
<div>
|
||
<p class="eyebrow">08 / A REAL DEBUGGING CASE</p>
|
||
<h2>信号写成功了,<br>为什么<span class="orange">文件不能删</span>?</h2>
|
||
<div class="logic-compare" aria-label="修改前后的代码逻辑对比">
|
||
<article class="logic-panel is-wrong">
|
||
<span class="mini-label">最初想法 · 有风险</span>
|
||
<pre><code>await WriteReleaseAsync();
|
||
await DeleteFileAsync();</code></pre>
|
||
</article>
|
||
<article class="logic-panel is-right">
|
||
<span class="mini-label">调试后 · 等待业务确认</span>
|
||
<pre><code>await WriteReleaseAsync();
|
||
if (ackReceived)
|
||
await DeleteFileAsync();</code></pre>
|
||
</article>
|
||
</div>
|
||
<p class="debug-clue">LOG 放行信号写入成功 → 等待 PLC 确认超时</p>
|
||
<p class="mail-analogy">像邮件显示“发送成功”,不代表对方已经阅读并执行。</p>
|
||
</div>
|
||
<section class="debug-card" aria-label="调试情景演示">
|
||
<div class="debug-head"><b>调试情景演示</b><span class="status-dot" id="scenarioStatus">等待选择</span></div>
|
||
<div class="debug-body">
|
||
<div class="scenario-buttons">
|
||
<button class="scenario-button" id="ackButton" type="button">收到 PLC 确认</button>
|
||
<button class="scenario-button" id="timeoutButton" type="button">等待确认超时</button>
|
||
</div>
|
||
<div class="scenario-result" id="scenarioResult">
|
||
<span class="mini-label">点击上方按钮</span>
|
||
<h3>比较两种结果</h3>
|
||
<div class="result-line"><span>PLC 业务结果</span><b>待确认</b></div>
|
||
<div class="result-line"><span>产品文件</span><b>保持不动</b></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
<footer class="slide-footer"><span>调试结论:协议成功 ≠ 业务完成</span><span class="slide-index">09 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="9" data-title="五步调试法" aria-label="第 10 页:五步调试法">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">09 / DEBUG WITH AI</p>
|
||
<h2>程序出错时,<br>我用这套<span class="accent">五步调试法</span>。</h2>
|
||
<div class="method-grid">
|
||
<article class="step-card"><span class="step-number">01</span><h3>先复现</h3><p>说明做了什么、期望什么、实际发生什么。</p></article>
|
||
<article class="step-card"><span class="step-number">02</span><h3>给证据</h3><p>提供报错、日志、配置和最近的修改。</p></article>
|
||
<article class="step-card"><span class="step-number">03</span><h3>讲流程</h3><p>先让 AI 解释调用过程,不急着修改。</p></article>
|
||
<article class="step-card"><span class="step-number">04</span><h3>小步改</h3><p>一次只处理一个原因,修改后立即运行。</p></article>
|
||
<article class="step-card"><span class="step-number">05</span><h3>正反测</h3><p>既测试成功,也测试超时、断网和错误输入。</p></article>
|
||
</div>
|
||
<div class="evidence-row">
|
||
<div class="prompt-box"><strong>我的常用提示:</strong>“先不要改代码。请根据日志找到调用流程,列出最可能的三个原因,以及如何逐个验证。”</div>
|
||
<section class="test-proof" aria-label="定向测试结果">
|
||
<div class="test-proof-head"><span>dotnet test · targeted</span><b>3 PASSED · 666 ms</b></div>
|
||
<div class="test-row"><span class="pass-label">PASS</span><span>收到 PLC 确认且开关开启 → 删除文件</span></div>
|
||
<div class="test-row"><span class="pass-label">PASS</span><span>等待 PLC 确认超时 → 保留文件</span></div>
|
||
<div class="test-row"><span class="pass-label">PASS</span><span>删除功能开关关闭 → 保留文件</span></div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>证据越完整,AI 越不容易靠猜</span><span class="slide-index">10 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="10" data-title="工程边界与验收" aria-label="第 11 页:工程边界与验收">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">10 / BOUNDARIES & ACCEPTANCE</p>
|
||
<h2>Vibe Coding 很快,<br>但这些<span class="orange">不能省</span>。</h2>
|
||
<div class="rule-grid">
|
||
<article class="rule-card"><span class="rule-no">01</span><div><h3>任务要拆小</h3><p>每一步都应该能运行、能观察、能撤回。</p></div></article>
|
||
<article class="rule-card"><span class="rule-no">02</span><div><h3>修改要看懂</h3><p>至少确认改了哪些文件、为什么改、影响到哪里。</p></div></article>
|
||
<article class="rule-card"><span class="rule-no">03</span><div><h3>模拟不等于现场</h3><p>Fake 设备只能先验证逻辑,不能替代真实 PLC、扫描枪和文件服务。</p></div></article>
|
||
<article class="rule-card"><span class="rule-no">04</span><div><h3>高风险动作默认关闭</h3><p>删除、付款、放行等动作必须有明确条件和可控开关。</p></div></article>
|
||
</div>
|
||
<div class="boundary-row" aria-label="交付边界">
|
||
<article class="boundary-card"><strong>异常可恢复</strong><p>断线、超时、目录不可访问,都要有状态、日志和恢复策略。</p></article>
|
||
<article class="boundary-card"><strong>过程可追溯</strong><p>每一轮任务都能回看输入、判断、结果和失败原因。</p></article>
|
||
<article class="boundary-card"><strong>现场才算验收</strong><p>真实点位、通信边界、权限和连续生产稳定性必须现场确认。</p></article>
|
||
</div>
|
||
<div class="boundary-note"><strong>交付门槛:</strong>本地构建与测试通过只是软件证据;真实设备和现场权限通过,才是生产验收。</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>AI 可以给建议,但业务责任和最终判断仍然属于人</span><span class="slide-index">11 / 12</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="11" data-title="总结" aria-label="第 12 页:总结">
|
||
<div class="slide-body finish-layout">
|
||
<div>
|
||
<p class="eyebrow">11 / TAKEAWAY</p>
|
||
<h2>从一句话需求,<br>到<span class="accent">可验证的完整计划</span>。</h2>
|
||
<p class="finish-statement">AI 没有替我完成工程判断,而是帮我更快地提问、拆解、实现和验证,最后把一个模糊需求变成能运行、能追溯、知道边界的项目。</p>
|
||
</div>
|
||
<div class="takeaway-list">
|
||
<article class="takeaway-card"><b>会提问</b><span>把需求里的空白和风险问出来</span></article>
|
||
<article class="takeaway-card"><b>会拆解</b><span>把自然语言变成流程、阶段和验收标准</span></article>
|
||
<article class="takeaway-card"><b>会验证</b><span>把本地证据与真实现场验收分开</span></article>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>谢谢 · AxiOmron.PcbCheck</span><span class="slide-index">12 / 12</span></footer>
|
||
</section>
|
||
</main>
|
||
|
||
<nav class="dots" id="dots" aria-label="汇报页面导航"></nav>
|
||
<div class="deck-controls" aria-label="翻页控制">
|
||
<button class="nav-button" id="previousButton" type="button">上一页</button>
|
||
<button class="nav-button primary" id="nextButton" type="button">下一页</button>
|
||
</div>
|
||
|
||
<script>
|
||
const slides = Array.from(document.querySelectorAll('.slide'));
|
||
const progressBar = document.getElementById('progressBar');
|
||
const dotsContainer = document.getElementById('dots');
|
||
const previousButton = document.getElementById('previousButton');
|
||
const nextButton = document.getElementById('nextButton');
|
||
const ackButton = document.getElementById('ackButton');
|
||
const timeoutButton = document.getElementById('timeoutButton');
|
||
const scenarioStatus = document.getElementById('scenarioStatus');
|
||
const scenarioResult = document.getElementById('scenarioResult');
|
||
let activeSlideIndex = 0;
|
||
|
||
/**
|
||
* 根据页面数量统一更新页脚编号和无障碍页面标题。
|
||
* @returns {void} 只同步每个页面的展示元数据,不返回数据。
|
||
*/
|
||
function syncSlideMetadata() {
|
||
const totalLabel = String(slides.length).padStart(2, '0');
|
||
slides.forEach((slide, index) => {
|
||
const pageNumber = String(index + 1).padStart(2, '0');
|
||
const title = slide.dataset.title || `第 ${index + 1} 页`;
|
||
const indexLabel = slide.querySelector('.slide-index');
|
||
if (indexLabel) indexLabel.textContent = `${pageNumber} / ${totalLabel}`;
|
||
slide.setAttribute('aria-label', `第 ${index + 1} 页:${title}`);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 创建汇报页面圆点导航,并为每个圆点绑定跳转行为。
|
||
* @returns {void} 只更新页面导航结构,不返回数据。
|
||
*/
|
||
function createDots() {
|
||
slides.forEach((_, index) => {
|
||
const dot = document.createElement('button');
|
||
dot.type = 'button';
|
||
dot.className = 'dot';
|
||
dot.setAttribute('aria-label', `跳转到第 ${index + 1} 页`);
|
||
dot.addEventListener('click', () => setSlide(index));
|
||
dotsContainer.appendChild(dot);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 切换到指定汇报页面,并同步进度条、圆点状态与翻页按钮状态。
|
||
* @param {number} index 目标页面的零基索引,超出范围时会自动限制到有效区间。
|
||
* @returns {void} 只更新当前页面状态,不返回数据。
|
||
*/
|
||
function setSlide(index) {
|
||
activeSlideIndex = Math.max(0, Math.min(slides.length - 1, index));
|
||
slides.forEach((slide, slideIndex) => {
|
||
const isActive = slideIndex === activeSlideIndex;
|
||
slide.classList.toggle('is-active', isActive);
|
||
slide.setAttribute('aria-hidden', String(!isActive));
|
||
});
|
||
|
||
Array.from(dotsContainer.children).forEach((dot, dotIndex) => {
|
||
const isActive = dotIndex === activeSlideIndex;
|
||
dot.classList.toggle('is-active', isActive);
|
||
dot.setAttribute('aria-current', isActive ? 'page' : 'false');
|
||
});
|
||
|
||
progressBar.style.width = `${((activeSlideIndex + 1) / slides.length) * 100}%`;
|
||
previousButton.disabled = activeSlideIndex === 0;
|
||
nextButton.disabled = activeSlideIndex === slides.length - 1;
|
||
}
|
||
|
||
/**
|
||
* 按指定方向移动汇报页面。
|
||
* @param {number} offset 页面偏移量,正数向后,负数向前。
|
||
* @returns {void} 通过调用页面切换函数更新界面。
|
||
*/
|
||
function moveSlide(offset) {
|
||
setSlide(activeSlideIndex + offset);
|
||
}
|
||
|
||
/**
|
||
* 展示“收到 PLC 确认”的调试情景,说明确认后才允许执行文件删除。
|
||
* @returns {void} 只更新情景卡片的可视内容和按钮状态。
|
||
*/
|
||
function showAcknowledgedScenario() {
|
||
ackButton.classList.add('is-selected');
|
||
timeoutButton.classList.remove('is-selected');
|
||
scenarioStatus.textContent = '业务已确认';
|
||
scenarioResult.innerHTML = `
|
||
<span class="mini-label">情景 A · 收到确认</span>
|
||
<h3 class="teal">允许执行后续动作</h3>
|
||
<div class="result-line"><span>PLC 业务结果</span><b>已确认放行</b></div>
|
||
<div class="result-line"><span>产品文件</span><b>按配置安全删除</b></div>`;
|
||
}
|
||
|
||
/**
|
||
* 展示“PLC 确认超时”的调试情景,说明协议写入成功时仍需保留产品文件。
|
||
* @returns {void} 只更新情景卡片的可视内容和按钮状态。
|
||
*/
|
||
function showTimeoutScenario() {
|
||
timeoutButton.classList.add('is-selected');
|
||
ackButton.classList.remove('is-selected');
|
||
scenarioStatus.textContent = '确认超时';
|
||
scenarioResult.innerHTML = `
|
||
<span class="mini-label">情景 B · 确认超时</span>
|
||
<h3 class="orange">停止危险的后续动作</h3>
|
||
<div class="result-line"><span>PLC 业务结果</span><b>未得到确认</b></div>
|
||
<div class="result-line"><span>产品文件</span><b>保留,不删除</b></div>`;
|
||
}
|
||
|
||
previousButton.addEventListener('click', () => moveSlide(-1));
|
||
nextButton.addEventListener('click', () => moveSlide(1));
|
||
ackButton.addEventListener('click', showAcknowledgedScenario);
|
||
timeoutButton.addEventListener('click', showTimeoutScenario);
|
||
|
||
document.addEventListener('keydown', event => {
|
||
if (event.target instanceof HTMLButtonElement) return;
|
||
if (event.key === 'ArrowRight' || event.key === 'PageDown' || event.key === ' ') moveSlide(1);
|
||
if (event.key === 'ArrowLeft' || event.key === 'PageUp') moveSlide(-1);
|
||
if (event.key === 'Home') setSlide(0);
|
||
if (event.key === 'End') setSlide(slides.length - 1);
|
||
});
|
||
|
||
syncSlideMetadata();
|
||
createDots();
|
||
setSlide(0);
|
||
</script>
|
||
</body>
|
||
</html>
|