1255 lines
39 KiB
HTML
1255 lines
39 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>3 个月,我从写 if-else 到 Vibe Coding</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;
|
||
}
|
||
|
||
.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 { 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; }
|
||
.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 { 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; }
|
||
.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" 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>8–10 MIN</b></div>
|
||
</aside>
|
||
</div>
|
||
<footer class="slide-footer"><span>一段真实的 AI 编程实践</span><span class="slide-index">01 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="1" aria-label="第 2 页:什么是 Vibe Coding">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">01 / WHAT IS VIBE CODING</p>
|
||
<h2>改变的不是“要不要思考”,<br>而是<span class="accent">把时间花在哪里</span>。</h2>
|
||
<div class="compare-grid">
|
||
<article class="compare-card">
|
||
<span class="mini-label">过去</span>
|
||
<h3>自己翻译成每一行代码</h3>
|
||
<p>需求 → 查资料 → 写代码 → 运行 → 修改</p>
|
||
</article>
|
||
<div class="compare-arrow" aria-hidden="true">→</div>
|
||
<article class="compare-card is-now">
|
||
<span class="mini-label">现在</span>
|
||
<h3>先描述目标,再检查结果</h3>
|
||
<p>讲清需求 → AI 实现 → 我来验证 → 继续迭代</p>
|
||
</article>
|
||
</div>
|
||
<div class="analogy"><strong>一个简单比喻:</strong>像和一名速度很快的程序员结对工作——AI 负责加速,我负责方向与验收。</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>Vibe Coding 不是一句话生成全部,而是持续对话与验证</span><span class="slide-index">02 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="2" aria-label="第 3 页:项目案例">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">02 / THE PROJECT</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">03 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="3" aria-label="第 4 页:上手难度">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">03 / 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">04 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="4" aria-label="第 5 页:真实调试案例">
|
||
<div class="slide-body debug-layout">
|
||
<div>
|
||
<p class="eyebrow">04 / 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">05 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="5" aria-label="第 6 页:五步调试法">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">05 / 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">06 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="6" aria-label="第 7 页:注意事项">
|
||
<div class="slide-body">
|
||
<p class="eyebrow">06 / FOUR THINGS TO REMEMBER</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>模拟测试不能替代真实设备、真实网络和实际权限。</p></div></article>
|
||
<article class="rule-card"><span class="rule-no">04</span><div><h3>高风险动作默认关闭</h3><p>删除、付款、放行等动作必须有明确条件和可控开关。</p></div></article>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>AI 可以给建议,但业务责任和最终判断仍然属于人</span><span class="slide-index">07 / 08</span></footer>
|
||
</section>
|
||
|
||
<section class="slide" data-slide="7" aria-label="第 8 页:总结">
|
||
<div class="slide-body finish-layout">
|
||
<div>
|
||
<p class="eyebrow">07 / 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>知道什么时候不能完全相信 AI</span></article>
|
||
</div>
|
||
</div>
|
||
<footer class="slide-footer"><span>谢谢 · AxiOmron.PcbCheck</span><span class="slide-index">08 / 08</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 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);
|
||
});
|
||
|
||
createDots();
|
||
setSlide(0);
|
||
</script>
|
||
</body>
|
||
</html>
|