Vibe Coding到底是什么:什么是 Vibe Coding?AI编程?
Vibe Coding到底是什么:未来的编程方式,还是一场「氛围革命」?你是否想过,有一天写代码不再需要「敲键盘」,而只需要说出想法,AI 就能帮你完成?这并不是科幻小说,而是正在流行的 Vibe Coding(氛围编程)。🔍 什么是 Vibe Coding?Vibe Coding,也被称为 氛围编程 或 沉浸式编程,是一种新兴的 AI 辅助编码范式。它的核心思想是:👉 用自然语言提示,而不是
Vibe Coding到底是什么:未来的编程方式,还是一场「氛围革命」?
你是否想过,有一天写代码不再需要「敲键盘」,而只需要说出想法,AI 就能帮你完成?
这并不是科幻小说,而是正在流行的 Vibe Coding(氛围编程)。
🔍 什么是 Vibe Coding?
Vibe Coding,也被称为 氛围编程 或 沉浸式编程,是一种新兴的 AI 辅助编码范式。
它的核心思想是:
👉 用自然语言提示,而不是逐行写代码。
👉 AI(如 GPT、Claude、Copilot 等)生成可执行程序。
👉 开发者只需提出创意,不必纠结底层实现细节。
📌 简单来说,就是 「说想法 → 看结果 → 再调整」 的循环。
Andrej Karpathy(OpenAI 联合创始人,前特斯拉 AI 负责人)在 2025 年 2 月提出了这个概念,他说:
“这不太算是写程序——我只是说出需求,运行它,然后复制粘贴,它大多能工作。”
这就是所谓的 vibe(氛围):你可能并不理解代码的每一行,但依然能让它跑起来。
、
文章目录
📈 背景与流行
- 2025 年 2 月:Karpathy 首次提出 “Vibe Coding”,被视为从 Software 1.0(人类写代码) 到 Software 3.0(自然语言指导 AI 写代码) 的跨越。
- 2025 年 3 月:被 Merriam-Webster 收录为新兴流行语。
- 全球媒体报道:Tom’s Guide、Wired、FT、Business Insider 等纷纷讨论,认为这可能改变未来的编程方式。
📊 趋势图 :
🚀 优势与应用场景
-
降低门槛
非程序员也能轻松“写代码”,非常适合个人工具、小型项目和快速原型。 -
开发效率提升
通过自然语言生成代码,减少写模板代码的负担。 -
现代化工作流
工具如 Cursor、Replit、GitHub Copilot、ChatGPT、Claude 等,让开发变成对话式体验。 -
行业认可
AWS 高管指出:Vibe Coding 已显著提高生产力,一些企业的生产代码已有相当比例由 AI 生成。
📊 应用场景示意表:
场景 | Vibe Coding 优势 |
---|---|
原型设计 | 几分钟完成 demo,快速验证创意 |
个人工具 | 无需团队协作,AI 辅助即可落地 |
教育/学习 | 让编程学习更直观,用“对话”取代语法记忆 |
企业研发 | 减少重复劳动,提升整体交付速度 |
⚠️ 局限性与风险
然而,Vibe Coding 并不是“万能钥匙”,它也存在不少挑战:
- 代码质量不稳定:AI 生成的代码可能不符合最佳实践,维护成本高。
- 安全与合规风险:非技术员工可能绕过 IT 审查,带来“影子 IT”问题。
- 不适合企业级复杂项目:缺乏稳定性和可扩展性。
- 技术债务累积:盲目依赖 AI,后期难以维护。
- 开发者能力退化:长期不用“手写代码”,可能削弱工程师的基础功底。
📊 风险对比表:
风险类别 | 具体表现 | 潜在后果 |
---|---|---|
质量风险 | 代码冗余、效率低、不规范 | 项目维护困难、性能下降 |
安全风险 | 影子 IT、绕过审查、数据合规隐患 | 企业安全事故、监管风险 |
能力退化风险 | 开发者过度依赖 AI,缺乏代码理解 | 技术能力下滑、学习成本增加 |
企业级风险 | 无法支撑复杂系统与业务逻辑 | 企业应用受限、无法规模化 |
典型案例
提示词:
根据如下资料,设计一款 web小游戏, 那一年,Rust帮少年初成,Node.js帮人多势众…
C帮、C++帮依旧坐镇江湖,Python帮更是笑看风云,AI话事人。
可谁能想到,最能打的,居然是喊着‘PHP是宇宙最强’的一群人?😂
好吧好吧~既然现实世界吵不赢你们
那我只好亲自下场了,咱到游戏里正面刚一把,谁赢了听谁的,不管咋样,游戏中JAVA必须是第一大帮!
JAVA帮 VS 所有帮 ,不服来战!
那一年…GO帮、Ruby帮、Perl帮、Scala帮、Lua帮、叫不出名帮…只能在旁边站如喽啰,瑟瑟发抖。😂
代码如下:
import React, { useEffect, useMemo, useRef, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Swords, ShieldHalf, Flame, Zap, TimerReset, Trophy, Repeat, Play } from "lucide-react";
// --- 轻量 UI 基础:直接用 Tailwind,避免外部依赖耦合 ---
// 你可以把这个组件嵌到任意 React 应用中;Tailwind 可选,但强烈推荐以获得最佳观感。
// 默认导出一个可预览的完整小游戏组件。
// 技能类型
type Skill = "RALLY" | "SHIELD" | "STUN" | "BURST" | "BURN" | "WEAKEN";
type Faction = {
id: string;
name: string;
emoji: string;
color: string; // tailwind 主色,用于按钮 & 血条
baseAtk: number;
baseHP: number;
skill: Skill;
skillName: string;
skillDesc: string;
};
const factions: Faction[] = [
{ id: "java", name: "JAVA帮", emoji: "☕", color: "amber", baseAtk: 28, baseHP: 1200, skill: "RALLY", skillName: "JVM超频", skillDesc: "6秒内攻击+30%" },
{ id: "rust", name: "Rust帮", emoji: "🦀", color: "orange", baseAtk: 27, baseHP: 980, skill: "WEAKEN", skillName: "借用检查", skillDesc: "6秒内敌方攻击-30%" },
{ id: "node", name: "Node.js帮", emoji: "🟢", color: "green", baseAtk: 24, baseHP: 960, skill: "BURST", skillName: "事件循环连击", skillDesc: "立即额外造成一轮猛击" },
{ id: "c", name: "C帮", emoji: "⚙️", color: "slate", baseAtk: 30, baseHP: 1020, skill: "BURST", skillName: "指针穿刺", skillDesc: "立即造成高额伤害" },
{ id: "cpp", name: "C++帮", emoji: "🧩", color: "zinc", baseAtk: 28, baseHP: 1040, skill: "SHIELD", skillName: "模板护体", skillDesc: "6秒内受到伤害-35%" },
{ id: "python", name: "Python帮", emoji: "🐍", color: "yellow", baseAtk: 23, baseHP: 1000, skill: "BURN", skillName: "AI召唤", skillDesc: "5秒内每秒灼烧伤害" },
{ id: "php", name: "PHP帮", emoji: "🐘", color: "pink", baseAtk: 26, baseHP: 940, skill: "RALLY", skillName: "宇宙最强", skillDesc: "6秒内攻击+30%(嘴强王者增幅)" },
{ id: "go", name: "GO帮", emoji: "🐹", color: "cyan", baseAtk: 25, baseHP: 980, skill: "RALLY", skillName: "协程风暴", skillDesc: "6秒内攻击+30%" },
{ id: "ruby", name: "Ruby帮", emoji: "💎", color: "rose", baseAtk: 22, baseHP: 970, skill: "SHIELD", skillName: "优雅护盾", skillDesc: "6秒内受到伤害-35%" },
{ id: "perl", name: "Perl帮", emoji: "🦪", color: "violet", baseAtk: 24, baseHP: 930, skill: "STUN", skillName: "正则风暴", skillDesc: "使敌方眩晕2.5秒" },
{ id: "scala", name: "Scala帮", emoji: "📚", color: "red", baseAtk: 24, baseHP: 950, skill: "WEAKEN", skillName: "函数式合流", skillDesc: "6秒内敌方攻击-30%" },
{ id: "lua", name: "Lua帮", emoji: "🌙", color: "blue", baseAtk: 24, baseHP: 920, skill: "BURST", skillName: "脚本连击", skillDesc: "立即造成一段猛击" },
{ id: "others", name: "叫不出名帮", emoji: "❓", color: "teal", baseAtk: 22, baseHP: 900, skill: "WEAKEN", skillName: "神秘气场", skillDesc: "6秒内敌方攻击-30%" },
];
type Combatant = {
faction: Faction;
atk: number;
maxHP: number;
hp: number;
};
type BattleState = {
timeLeft: number;
you: Combatant;
enemy: Combatant;
youBuffUntil: number; // rally
youShieldUntil: number; // shield
enemyStunnedUntil: number; // stun applied to enemy
enemyWeakenUntil: number; // weaken applied to enemy
burnOnEnemyUntil: number; // DoT end time
skillCooldownUntil: number; // your active skill cd
logs: { id: number; who: "you" | "enemy"; dmg: number }[];
finished: boolean;
result?: "win" | "lose" | "draw";
};
// Java 必须是第一大帮:预置江湖榜(声望)。对局结束后会加分,但 JAVA 始终第一。
const initialPrestige = [
{ id: "java", name: "JAVA帮", value: 10000 },
{ id: "python", name: "Python帮", value: 3200 },
{ id: "cpp", name: "C++帮", value: 3000 },
{ id: "c", name: "C帮", value: 2900 },
{ id: "rust", name: "Rust帮", value: 2600 },
{ id: "php", name: "PHP帮", value: 2500 },
{ id: "go", name: "GO帮", value: 2400 },
{ id: "node", name: "Node.js帮", value: 2300 },
{ id: "ruby", name: "Ruby帮", value: 1800 },
{ id: "perl", name: "Perl帮", value: 1500 },
{ id: "scala", name: "Scala帮", value: 1400 },
{ id: "lua", name: "Lua帮", value: 1300 },
{ id: "others", name: "叫不出名帮", value: 900 },
] as const;
function clamp(n: number, min = 0, max = Number.MAX_SAFE_INTEGER) {
return Math.max(min, Math.min(max, n));
}
function formatHP(hp: number) {
return Math.ceil(hp);
}
function pickFaction(id: string) {
return factions.find(f => f.id === id)!;
}
function makeCombatant(f: Faction, bias?: { atkMul?: number; hpMul?: number }): Combatant {
return {
faction: f,
atk: f.baseAtk * (bias?.atkMul ?? 1),
maxHP: f.baseHP * (bias?.hpMul ?? 1),
hp: f.baseHP * (bias?.hpMul ?? 1),
};
}
function colorClass(color: string) {
// 生成几个常用 Tailwind 类,尽量避免硬编码颜色名导致的冲突
return {
bg: `bg-${color}-600`,
bgSoft: `bg-${color}-500/20`,
text: `text-${color}-600`,
ring: `ring-${color}-600`,
gradFrom: `from-${color}-500`,
gradTo: `to-${color}-700`,
} as const;
}
export default function LanguageClashGame() {
const [phase, setPhase] = useState<"menu" | "battle" | "result">("menu");
const [chosen, setChosen] = useState<Faction | null>(null);
const [battle, setBattle] = useState<BattleState | null>(null);
const [prestige, setPrestige] = useState(() => initialPrestige.map(p => ({ ...p })));
const java = useMemo(() => pickFaction("java"), []);
// 开始战斗:如果你选 JAVA,则对手为“诸帮联军”;否则对手为“JAVA帮”(带隐藏加成)
function startBattle(f: Faction) {
// 组装敌人
let enemy: Combatant;
if (f.id === "java") {
const horde: Faction = {
id: "horde",
name: "诸帮联军",
emoji: "⚔️",
color: "indigo",
baseAtk: 24,
baseHP: 1300,
skill: "STUN",
skillName: "围攻",
skillDesc: "短暂眩晕 JAVA 队",
};
enemy = makeCombatant(horde);
} else {
// JAVA 作为 Boss 拥有隐藏护体(确保“第一大帮”的设定氛围)
enemy = makeCombatant(java, { atkMul: 1.15, hpMul: 1.15 });
}
const you = makeCombatant(f, f.id === "php" ? { atkMul: 1.05 } : undefined); // 给 PHP 一点小面子 🐘
const now = Date.now();
setBattle({
timeLeft: 45,
you,
enemy,
youBuffUntil: 0,
youShieldUntil: 0,
enemyStunnedUntil: 0,
enemyWeakenUntil: 0,
burnOnEnemyUntil: 0,
skillCooldownUntil: now + 1500, // 开局 1.5s 后可放技能
logs: [],
finished: false,
});
setPhase("battle");
}
// 战斗主循环(每秒一跳)
useEffect(() => {
if (phase !== "battle" || !battle) return;
let raf: number;
const tickMs = 1000;
let last = Date.now();
const loop = () => {
const now = Date.now();
if (now - last >= tickMs) {
last = now;
setBattle(prev => {
if (!prev || prev.finished) return prev;
const t = { ...prev } as BattleState;
// 倒计时
t.timeLeft = clamp(t.timeLeft - 1, 0, 999);
// 计算状态开关
const rallyActive = now < t.youBuffUntil;
const shieldActive = now < t.youShieldUntil;
const enemyStunned = now < t.enemyStunnedUntil;
const enemyWeaken = now < t.enemyWeakenUntil;
const burnTicking = now < t.burnOnEnemyUntil;
// 你对敌:
const atkMul = rallyActive ? 1.3 : 1;
let dmgYou = t.you.atk * atkMul * (t.enemy.faction.skill === "SHIELD" && !enemyStunned ? 1 : 1); // 占位,后面按盾减伤
// 灼烧:每秒额外固定伤害
if (burnTicking) {
dmgYou += 30;
}
// 敌对你:
const enemyAtkMul = enemyWeaken ? 0.7 : 1;
let dmgEnemy = enemyStunned ? 0 : t.enemy.atk * enemyAtkMul;
// 盾牌减伤
const enemyHasShield = false; // 敌方的盾只在技能里出现,这里由技能触发逻辑单独处理
const youHasShield = shieldActive;
if (youHasShield) {
dmgEnemy *= 0.65; // 35% 伤害减免
}
// 应用伤害(加入一点浮动)
const jitter = (x: number) => x * (0.9 + Math.random() * 0.2);
const dealt = Math.max(1, jitter(dmgYou));
const taken = Math.max(0, jitter(dmgEnemy));
t.enemy.hp = clamp(t.enemy.hp - dealt, 0, t.enemy.maxHP);
t.you.hp = clamp(t.you.hp - taken, 0, t.you.maxHP);
t.logs = [
...t.logs,
{ id: now + Math.random(), who: "you", dmg: dealt },
taken > 0 ? { id: now + Math.random() + 1, who: "enemy", dmg: taken } : undefined,
].filter(Boolean) as any;
// 结束判定
if (t.enemy.hp <= 0 || t.you.hp <= 0 || t.timeLeft <= 0) {
t.finished = true;
const result: "win" | "lose" | "draw" =
t.enemy.hp <= 0 && t.you.hp > 0
? "win"
: t.you.hp <= 0 && t.enemy.hp > 0
? "lose"
: t.enemy.hp === t.you.hp
? "draw"
: t.enemy.hp < t.you.hp
? "win"
: "lose";
t.result = result;
}
return t;
});
}
raf = requestAnimationFrame(loop);
};
raf = requestAnimationFrame(loop);
return () => cancelAnimationFrame(raf);
}, [phase, battle]);
// 施放技能(仅你方可主动释放)
function castSkill() {
if (!battle) return;
const now = Date.now();
if (now < battle.skillCooldownUntil) return; // 冷却中
const skill = battle.you.faction.skill;
setBattle(prev => {
if (!prev) return prev;
const t = { ...prev } as BattleState;
const cd = 12000; // 12s 冷却
t.skillCooldownUntil = now + cd;
switch (skill) {
case "RALLY":
t.youBuffUntil = Math.max(t.youBuffUntil, now + 6000);
break;
case "SHIELD":
t.youShieldUntil = Math.max(t.youShieldUntil, now + 6000);
break;
case "STUN":
t.enemyStunnedUntil = Math.max(t.enemyStunnedUntil, now + 2500);
break;
case "BURST":
// 立即造成一段额外伤害(随你方攻击力浮动)
const burst = battle.you.atk * 3.2 * (0.9 + Math.random() * 0.2);
t.enemy.hp = clamp(t.enemy.hp - burst, 0, t.enemy.maxHP);
t.logs = [...t.logs, { id: now + Math.random(), who: "you", dmg: burst }];
break;
case "BURN":
t.burnOnEnemyUntil = Math.max(t.burnOnEnemyUntil, now + 5000);
break;
case "WEAKEN":
t.enemyWeakenUntil = Math.max(t.enemyWeakenUntil, now + 6000);
break;
}
return t;
});
}
// 结束后:更新江湖榜(JAVA 仍第一)
useEffect(() => {
if (phase !== "result" || !battle || !battle.result || !chosen) return;
setPrestige(prev => {
const copy = prev.map(p => ({ ...p }));
const delta = battle.result === "win" ? 120 : battle.result === "draw" ? 60 : 25;
const entry = copy.find(p => p.id === chosen.id);
if (entry) entry.value += delta;
// JAVA 作为第一:如果由于叠加出现逼近,给 JAVA 追加一点点“背景加成”保持第一气质
const javaRow = copy.find(p => p.id === "java");
if (javaRow) {
const maxOther = Math.max(...copy.filter(p => p.id !== "java").map(p => p.value));
if (javaRow.value <= maxOther) javaRow.value = maxOther + 1; // 保持严格第一
}
return copy;
});
}, [phase, battle, chosen]);
function endBattleToResult() {
setPhase("result");
}
useEffect(() => {
if (phase === "battle" && battle?.finished) {
const t = setTimeout(() => endBattleToResult(), 800);
return () => clearTimeout(t);
}
}, [phase, battle?.finished]);
function resetAll() {
setPhase("menu");
setBattle(null);
setChosen(null);
}
// UI 工具
const coolDownLeft = battle ? Math.max(0, battle.skillCooldownUntil - Date.now()) : 0;
const cdPct = battle ? 1 - clamp(coolDownLeft / 12000, 0, 1) : 1;
const youColor = colorClass(chosen?.color || "gray");
const enemyColor = colorClass(battle?.enemy.faction.color || "gray");
return (
<div className="w-full min-h-dvh bg-gradient-to-b from-slate-900 to-slate-950 text-slate-100 flex items-center justify-center p-4">
<div className="w-full max-w-5xl mx-auto">
<header className="mb-4 text-center">
<motion.h1
className="text-3xl md:text-5xl font-extrabold tracking-tight"
initial={{ y: -12, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
>
那一年 · 帮派争锋:编程语言之战
</motion.h1>
<p className="text-slate-300 mt-2 leading-relaxed max-w-3xl mx-auto">
Rust帮少年初成,Node.js帮人多势众;C/C++坐镇江湖,Python笑看风云,AI话事人。\
PHP高呼“宇宙最强”,GO、Ruby、Perl、Scala、Lua、叫不出名帮瑟瑟发抖。\
<span className="font-semibold">JAVA必须是第一大帮!</span> 你属于哪个帮派?可敢一战!
</p>
</header>
{phase === "menu" && (
<motion.section
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="grid md:grid-cols-3 gap-4"
>
<div className="md:col-span-2">
<div className="rounded-2xl bg-slate-800/50 p-4 md:p-6 ring-1 ring-white/10 shadow-xl">
<div className="flex items-center gap-2 mb-4">
<Swords className="w-5 h-5" />
<h2 className="text-xl font-bold">选择你的帮派</h2>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{factions.map(f => (
<button
key={f.id}
onClick={() => setChosen(f)}
className={`group relative overflow-hidden rounded-xl px-3 py-3 text-left transition border border-white/10 hover:border-white/20 ${chosen?.id === f.id ? `ring-2 ${colorClass(f.color).ring}` : ""} bg-slate-900/50`}
>
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{f.emoji}</span>
<span className="font-semibold">{f.name}</span>
</div>
<div className="text-xs text-slate-300">ATK {f.baseAtk} · HP {f.baseHP}</div>
<div className="text-[11px] text-slate-400 mt-1 flex items-center gap-1">
{f.skill === "RALLY" && <Zap className="w-3 h-3" />}
{f.skill === "SHIELD" && <ShieldHalf className="w-3 h-3" />}
{f.skill === "STUN" && <TimerReset className="w-3 h-3" />}
{f.skill === "BURST" && <Swords className="w-3 h-3" />}
{f.skill === "BURN" && <Flame className="w-3 h-3" />}
{f.skill === "WEAKEN" && <ShieldHalf className="w-3 h-3 rotate-180" />}
<span className="truncate">{f.skillName} · {f.skillDesc}</span>
</div>
<div className={`absolute inset-0 bg-gradient-to-br ${colorClass(f.color).gradFrom} ${colorClass(f.color).gradTo} opacity-0 group-hover:opacity-10 transition`} />
</button>
))}
</div>
<div className="mt-4 flex items-center justify-between gap-3">
<div className="text-slate-300 text-sm">
规则:45 秒自动对战,每秒互相出手。你可手动释放本帮技能(12 秒冷却)。
</div>
<button
disabled={!chosen}
onClick={() => chosen && startBattle(chosen)}
className={`inline-flex items-center gap-2 rounded-xl px-4 py-2 font-semibold transition disabled:opacity-50 disabled:cursor-not-allowed ${chosen ? `${youColor.bg} hover:opacity-90` : "bg-slate-700"}`}
>
<Play className="w-4 h-4" /> 开战!
</button>
</div>
</div>
</div>
<div>
<div className="rounded-2xl bg-slate-800/50 p-4 md:p-6 ring-1 ring-white/10 shadow-xl h-full">
<div className="flex items-center gap-2 mb-4">
<Trophy className="w-5 h-5" />
<h2 className="text-xl font-bold">江湖榜 · 声望(JAVA 永远 NO.1)</h2>
</div>
<ul className="space-y-2 max-h-80 overflow-auto pr-1">
{prestige
.slice()
.sort((a, b) => b.value - a.value)
.map((row, i) => (
<li key={row.id} className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<span className={`text-xs inline-flex w-6 h-6 items-center justify-center rounded-full ${i === 0 ? "bg-amber-500 text-black" : "bg-slate-700"}`}>{i + 1}</span>
<span className="text-sm">{initialPrestige.find(p => p.id === row.id)?.name || row.id}</span>
</div>
<span className="text-sm tabular-nums text-slate-300">{row.value}</span>
</li>
))}
</ul>
</div>
</div>
</motion.section>
)}
{phase === "battle" && battle && (
<motion.section
key="battle"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="rounded-2xl bg-slate-800/50 ring-1 ring-white/10 shadow-xl p-4 md:p-6"
>
<div className="flex items-center justify-between">
<div className="text-sm text-slate-300">剩余时间:<span className="font-mono">{battle.timeLeft}s</span></div>
<button onClick={resetAll} className="text-sm text-slate-300 hover:text-white underline/30">返回选人</button>
</div>
<div className="grid md:grid-cols-2 gap-4 mt-4">
{/* 你方 */}
<div className="rounded-xl p-4 bg-slate-900/60 border border-white/10">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className="text-2xl">{battle.you.faction.emoji}</span>
<div>
<div className="font-bold">{battle.you.faction.name}</div>
<div className="text-xs text-slate-400">ATK {battle.you.atk.toFixed(0)} · HP {battle.you.maxHP}</div>
</div>
</div>
<div className="text-right">
<div className="text-xs text-slate-400 mb-1">生命</div>
<HPBar current={battle.you.hp} max={battle.you.maxHP} color={youColor} />
</div>
</div>
<div className="mt-3 flex items-center gap-3">
<button
onClick={castSkill}
disabled={Date.now() < battle.skillCooldownUntil || battle.finished}
className={`relative overflow-hidden inline-flex items-center gap-2 rounded-xl px-4 py-2 font-semibold transition disabled:opacity-50 ${youColor.bg} hover:opacity-90`}
>
{battle.you.faction.skill === "RALLY" && <Zap className="w-4 h-4" />}
{battle.you.faction.skill === "SHIELD" && <ShieldHalf className="w-4 h-4" />}
{battle.you.faction.skill === "STUN" && <TimerReset className="w-4 h-4" />}
{battle.you.faction.skill === "BURST" && <Swords className="w-4 h-4" />}
{battle.you.faction.skill === "BURN" && <Flame className="w-4 h-4" />}
{battle.you.faction.skill === "WEAKEN" && <ShieldHalf className="w-4 h-4 rotate-180" />}
施放 · {battle.you.faction.skillName}
<span className="ml-2 text-xs opacity-80">{Date.now() < battle.skillCooldownUntil ? `${Math.ceil((battle.skillCooldownUntil - Date.now())/1000)}s` : "就绪"}</span>
{/* CD 进度条 */}
<span className="absolute inset-0 -z-0 opacity-20">
<motion.span
key={cdPct}
initial={{ width: 0 }}
animate={{ width: `${cdPct * 100}%` }}
transition={{ duration: 0.3 }}
className={`block h-full ${youColor.bg}`}
/>
</span>
</button>
</div>
</div>
{/* 敌方 */}
<div className="rounded-xl p-4 bg-slate-900/60 border border-white/10">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className="text-2xl">{battle.enemy.faction.emoji}</span>
<div>
<div className="font-bold">{battle.enemy.faction.name}</div>
<div className="text-xs text-slate-400">ATK {battle.enemy.atk.toFixed(0)} · HP {battle.enemy.maxHP}</div>
</div>
</div>
<div className="text-right">
<div className="text-xs text-slate-400 mb-1">生命</div>
<HPBar current={battle.enemy.hp} max={battle.enemy.maxHP} color={enemyColor} />
</div>
</div>
<div className="mt-3 text-xs text-slate-400">
对手将自动出手;部分对手拥有 Boss 光环(例如 JAVA)。
</div>
</div>
</div>
{/* 伤害飘字 */}
<div className="relative h-24 mt-6">
<AnimatePresence initial={false}>
{battle.logs.slice(-6).map(e => (
<motion.div
key={e.id}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: -16 }}
exit={{ opacity: 0, y: -28 }}
transition={{ duration: 0.6 }}
className={`absolute left-1/2 -translate-x-1/2 text-sm font-semibold ${e.who === "you" ? youColor.text : enemyColor.text}`}
>
{e.who === "you" ? "↘ 对敌 -" : "↗ 受击 -"} {Math.round(e.dmg)}
</motion.div>
))}
</AnimatePresence>
</div>
{battle.finished && (
<div className="mt-4 text-center">
<motion.div initial={{ scale: 0.9, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} className="inline-flex items-center gap-2 rounded-xl px-4 py-2 bg-slate-900/80 ring-1 ring-white/10">
{battle.result === "win" && <span className="text-green-400 font-bold">你赢了!</span>}
{battle.result === "lose" && <span className="text-rose-400 font-bold">你输了…</span>}
{battle.result === "draw" && <span className="text-amber-300 font-bold">平局!</span>}
<button onClick={endBattleToResult} className="ml-3 underline/30 hover:text-white">查看战报</button>
</motion.div>
</div>
)}
</motion.section>
)}
{phase === "result" && battle && chosen && (
<motion.section
key="result"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="rounded-2xl bg-slate-800/50 ring-1 ring-white/10 shadow-xl p-4 md:p-6"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Trophy className="w-5 h-5" />
<h2 className="text-xl font-bold">战报 · {chosen.name} vs {battle.enemy.faction.name}</h2>
</div>
<div className="text-sm text-slate-300">用时:{45 - battle.timeLeft}s</div>
</div>
<div className="grid md:grid-cols-3 gap-4 mt-4">
<div className="md:col-span-2 rounded-xl p-4 bg-slate-900/60 border border-white/10">
<div className="flex items-center justify-center gap-3 text-2xl font-extrabold">
<span className="text-green-400">{battle.result === "win" ? "胜" : battle.result === "lose" ? "负" : "平"}</span>
<span>·</span>
<span className="text-slate-300">{chosen.emoji} {chosen.name}</span>
<span>VS</span>
<span className="text-slate-300">{battle.enemy.faction.emoji} {battle.enemy.faction.name}</span>
</div>
<div className="mt-4 grid grid-cols-2 gap-3">
<Stat label="剩余我方HP" value={`${formatHP(battle.you.hp)}/${battle.you.maxHP}`} />
<Stat label="剩余敌方HP" value={`${formatHP(battle.enemy.hp)}/${battle.enemy.maxHP}`} />
<Stat label="技能" value={`${chosen.skillName}`} />
<Stat label="击打轮数" value={`${Math.min(45, 45 - battle.timeLeft)}`} />
</div>
<div className="mt-4 flex items-center gap-3">
<button onClick={() => startBattle(chosen)} className={`inline-flex items-center gap-2 rounded-xl px-4 py-2 font-semibold ${youColor.bg} hover:opacity-90`}>
<Repeat className="w-4 h-4" /> 再战一把
</button>
<button onClick={resetAll} className="inline-flex items-center gap-2 rounded-xl px-4 py-2 font-semibold bg-slate-700 hover:opacity-90">
返回选人
</button>
</div>
</div>
<div className="rounded-xl p-4 bg-slate-900/60 border border-white/10">
<div className="flex items-center gap-2 mb-2">
<Trophy className="w-5 h-5" />
<h3 className="font-bold">江湖榜(实时)</h3>
</div>
<ul className="space-y-2 max-h-80 overflow-auto pr-1">
{prestige
.slice()
.sort((a, b) => b.value - a.value)
.map((row, i) => (
<li key={row.id} className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<span className={`text-xs inline-flex w-6 h-6 items-center justify-center rounded-full ${i === 0 ? "bg-amber-500 text-black" : "bg-slate-700"}`}>{i + 1}</span>
<span className="text-sm">{initialPrestige.find(p => p.id === row.id)?.name || row.id}</span>
</div>
<span className="text-sm tabular-nums text-slate-300">{row.value}</span>
</li>
))}
</ul>
<p className="mt-3 text-xs text-slate-400">说明:战后按胜负给本帮追加声望。JAVA 帮享有“时代红利”,榜上永居第一。</p>
</div>
</div>
</motion.section>
)}
<footer className="mt-6 text-center text-xs text-slate-400">
小技巧:RALLY 提升我方攻击;SHIELD 减伤;STUN 控制;BURST 爆发;BURN 持续伤害;WEAKEN 降低敌攻。\
如果你不是 JAVA 阵营,对面 JAVA 可能带有 Boss 光环~
</footer>
</div>
</div>
);
}
function Stat({ label, value }: { label: string; value: React.ReactNode }) {
return (
<div className="rounded-lg bg-slate-800/60 border border-white/10 p-3">
<div className="text-xs text-slate-400">{label}</div>
<div className="text-sm font-semibold">{value}</div>
</div>
);
}
function HPBar({ current, max, color }: { current: number; max: number; color: ReturnType<typeof colorClass> }) {
const pct = Math.max(0, Math.min(1, current / max));
return (
<div className="w-44 bg-slate-700/60 rounded-lg overflow-hidden">
<motion.div
initial={false}
animate={{ width: `${pct * 100}%` }}
transition={{ type: "spring", stiffness: 120, damping: 20 }}
className={`h-3 ${color.bg}`}
/>
</div>
);
}
运行:
📝 总结观点:Vibe Coding 是什么?
一句话总结:
Vibe Coding = 用自然语言写代码,让创意落地更快,但需警惕质量与安全隐患。
📌 关键要点复盘:
- 定义:自然语言提示 AI 生成代码,用户关注创意而非实现。
- 优势:降低门槛、加速原型、提升效率。
- 适用范围:小型项目、个人应用、教育与学习场景。
- 风险:质量、合规、技术债务与开发者能力退化。
- 企业视角:适合作为协作工具,而不是完全替代编程。
📚 延伸阅读
- Tom’s Guide:Vibe Coding 如何让编程人人可用
- 金融时报:Vibe Coding 是新的“自己动手”
- The Economic Times:为什么 Vibe Coding 不能只靠氛围
- Omni Ekonomi:Vibe Coding 的“影子 IT”风险
👉 你觉得 Vibe Coding 是 编程的未来,还是一场短暂的潮流?
欢迎在评论区聊聊你的看法!
更多推荐
所有评论(0)