计划编写练习:电商购物车功能
场景描述
你的团队需要实现一个电商购物车功能。产品经理提供了需求文档,你需要使用 writing-plans 技能将其分解为可执行的开发任务。
需求文档
markdown
# 购物车功能需求
## 核心功能
### 1. 添加商品
- 用户可以点击"加入购物车"按钮添加商品
- 如果商品已在购物车中,则增加数量
- 显示添加成功的提示
### 2. 查看购物车
- 显示购物车中所有商品
- 每个商品显示:图片、名称、单价、数量、小计
- 显示商品总数和总金额
### 3. 修改数量
- 用户可以通过 +/- 按钮调整商品数量
- 数量不能小于 1
- 数量修改后实时更新小计和总计
### 4. 删除商品
- 用户可以删除购物车中的商品
- 删除前显示确认对话框
### 5. 数据持久化
- 购物车数据保存在 LocalStorage
- 用户刷新页面后数据保留
## 技术约束
- 使用 Svelte 框架
- 使用 TypeScript
- 遵循 TDD 开发流程你的任务
使用 writing-plans 技能:
- 分析需求:理解每个功能点的具体要求
- 设计架构:确定组件结构和数据流
- 分解任务:将需求分解为 2-5 分钟的小任务
- 编写计划:生成详细的实现计划
计划编写指南
任务分解原则
每个任务应该是:
- 原子性:一个任务只做一件事
- 可验证:有明确的完成标准
- 短小精悍:2-5 分钟内可完成
- 独立可测:可以独立测试
任务格式
markdown
## Task N: 任务标题
### 文件
- 创建/修改的文件列表
### 操作
1. 具体操作步骤
2. 包含必要的代码片段
### 验证
- 预期结果
- 验证命令设计思考
在写计划前,思考:
- 状态管理:购物车状态如何存储和管理?
- 组件结构:需要哪些组件?职责如何划分?
- 数据流:数据如何在组件间流动?
- 测试策略:如何保证质量?
提示
提示 1:组件结构
建议的组件结构:
text
src/
├── stores/
│ └── cart.ts # 购物车状态管理
├── lib/
│ ├── Cart.svelte # 购物车容器
│ ├── CartItem.svelte # 单个商品
│ ├── CartSummary.svelte # 价格汇总
│ └── AddToCart.svelte # 添加按钮
└── types/
└── cart.ts # 类型定义提示 2:状态管理
使用 Svelte Store:
typescript
// stores/cart.ts
import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';
import type { CartItem, Product } from '../types/cart';
// 从 LocalStorage 初始化
const stored = browser
? JSON.parse(localStorage.getItem('cart') || '[]')
: [];
export const cartItems = writable<CartItem[]>(stored);
// 自动保存到 LocalStorage
cartItems.subscribe(items => {
if (browser) {
localStorage.setItem('cart', JSON.stringify(items));
}
});
// 派生数据:总数和总价
export const cartCount = derived(cartItems,
items => items.reduce((sum, item) => sum + item.quantity, 0)
);
export const cartTotal = derived(cartItems,
items => items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
// 操作方法
export function addToCart(product: Product) {
// 实现见下文
}
export function updateQuantity(id: string, quantity: number) {
// 实现见下文
}
export function removeFromCart(id: string) {
// 实现见下文
}提示 3:任务分解
从最简单开始:
- 首先建立类型定义
- 然后实现状态管理
- 再实现 UI 组件
- 最后实现交互功能
提示 4:TDD 思考
每个任务都应该包含测试:
markdown
Task: 创建 CartItem 类型
验证: TypeScript 编译通过,类型检查正确
Task: 实现 addToCart 方法
验证: 单元测试通过
- 添加新商品:购物车数量 +1
- 添加已存在商品:数量 +1参考答案
markdown
# 购物车功能实现计划
## 设计概述
### 组件结构
- `Cart.svelte` - 购物车主容器
- `CartItem.svelte` - 单个商品行
- `CartSummary.svelte` - 价格汇总
- `AddToCart.svelte` - 添加按钮
### 状态管理
使用 Svelte Store + LocalStorage 持久化
---
## Task 1: 创建类型定义
### 文件
- 创建 `src/types/cart.ts`
### 操作
```typescript
// src/types/cart.ts
export interface Product {
id: string;
name: string;
price: number;
imageUrl: string;
}
export interface CartItem {
productId: string;
name: string;
price: number;
imageUrl: string;
quantity: number;
}验证
- TypeScript 编译通过
- 无类型错误
Task 2: 创建购物车 Store
文件
- 创建
src/stores/cart.ts
操作
typescript
// src/stores/cart.ts
import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';
import type { CartItem, Product } from '../types/cart';
const stored = browser
? JSON.parse(localStorage.getItem('cart') || '[]')
: [];
export const cartItems = writable<CartItem[]>(stored);
cartItems.subscribe(items => {
if (browser) {
localStorage.setItem('cart', JSON.stringify(items));
}
});
export const cartCount = derived(cartItems,
items => items.reduce((sum, item) => sum + item.quantity, 0)
);
export const cartTotal = derived(cartItems,
items => items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);验证
- TypeScript 编译通过
- Store 可正常导入
Task 3: 实现 addToCart 方法
文件
- 修改
src/stores/cart.ts
操作
typescript
export function addToCart(product: Product) {
cartItems.update(items => {
const existing = items.find(item => item.productId === product.id);
if (existing) {
return items.map(item =>
item.productId === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
}
return [...items, {
productId: product.id,
name: product.name,
price: product.price,
imageUrl: product.imageUrl,
quantity: 1
}];
});
}验证
- 单元测试:添加新商品
- 单元测试:添加已存在商品
Task 4: 实现 updateQuantity 方法
文件
- 修改
src/stores/cart.ts
操作
typescript
export function updateQuantity(productId: string, quantity: number) {
if (quantity < 1) return;
cartItems.update(items =>
items.map(item =>
item.productId === productId
? { ...item, quantity }
: item
)
);
}验证
- 单元测试:数量正确更新
- 单元测试:数量 < 1 时不更新
Task 5: 实现 removeFromCart 方法
文件
- 修改
src/stores/cart.ts
操作
typescript
export function removeFromCart(productId: string) {
cartItems.update(items =>
items.filter(item => item.productId !== productId)
);
}验证
- 单元测试:商品从购物车移除
Task 6: 创建 CartItem 组件
文件
- 创建
src/lib/CartItem.svelte
操作
svelte
<script lang="ts">
import type { CartItem } from '../types/cart';
import { updateQuantity, removeFromCart } from '../stores/cart';
export let item: CartItem;
</script>
<div class="cart-item">
<img src={item.imageUrl} alt={item.name} />
<div class="info">
<h3>{item.name}</h3>
<p class="price">${item.price.toFixed(2)}</p>
</div>
<div class="quantity">
<button on:click={() => updateQuantity(item.productId, item.quantity - 1)}>-</button>
<span>{item.quantity}</span>
<button on:click={() => updateQuantity(item.productId, item.quantity + 1)}>+</button>
</div>
<p class="subtotal">${(item.price * item.quantity).toFixed(2)}</p>
<button class="remove" on:click={() => removeFromCart(item.productId)}>×</button>
</div>验证
- 组件可正常渲染
- 显示正确的商品信息
Task 7: 创建 CartSummary 组件
文件
- 创建
src/lib/CartSummary.svelte
操作
svelte
<script lang="ts">
import { cartCount, cartTotal } from '../stores/cart';
</script>
<div class="cart-summary">
<p>商品总数:<strong>{$cartCount}</strong></p>
<p>总计:<strong>${$cartTotal.toFixed(2)}</strong></p>
</div>验证
- 显示正确的总数和总价
Task 8: 创建 Cart 主容器
文件
- 创建
src/lib/Cart.svelte
操作
svelte
<script lang="ts">
import { cartItems } from '../stores/cart';
import CartItem from './CartItem.svelte';
import CartSummary from './CartSummary.svelte';
</script>
<div class="cart">
<h2>购物车</h2>
{#if $cartItems.length === 0}
<p class="empty">购物车是空的</p>
{:else}
<div class="items">
{#each $cartItems as item (item.productId)}
<CartItem {item} />
{/each}
</div>
<CartSummary />
{/if}
</div>验证
- 空购物车显示提示
- 有商品时显示列表
Task 9: 创建 AddToCart 组件
文件
- 创建
src/lib/AddToCart.svelte
操作
svelte
<script lang="ts">
import { addToCart } from '../stores/cart';
import type { Product } from '../types/cart';
export let product: Product;
let added = false;
function handleAdd() {
addToCart(product);
added = true;
setTimeout(() => added = false, 2000);
}
</script>
<button
class="add-to-cart"
class:added
on:click={handleAdd}
>
{added ? '已添加 ✓' : '加入购物车'}
</button>验证
- 点击后显示"已添加"
- 商品出现在购物车中
Task 10: 集成测试
文件
- 创建
src/routes/+page.svelte(测试页面)
操作
svelte
<script lang="ts">
import Cart from '../lib/Cart.svelte';
import AddToCart from '../lib/AddToCart.svelte';
const demoProducts = [
{ id: '1', name: '商品 A', price: 99.99, imageUrl: '/placeholder.jpg' },
{ id: '2', name: '商品 B', price: 149.99, imageUrl: '/placeholder.jpg' },
];
</script>
<main>
<section class="products">
{#each demoProducts as product}
<div class="product">
<h3>{product.name}</h3>
<p>${product.price}</p>
<AddToCart {product} />
</div>
{/each}
</section>
<Cart />
</main>验证
- 完整流程测试
- 添加商品 -> 查看购物车 -> 修改数量 -> 删除商品
完成标准
- [ ] 所有 TypeScript 类型定义完整
- [ ] Store 方法单元测试通过
- [ ] 所有组件正常渲染
- [ ] LocalStorage 持久化正常
- [ ] 刷新页面后数据保留
## 学习要点
### 1. 任务粒度
好的任务粒度:
- 太大:实现购物车功能(无法验证)
- 太小:创建一个文件(无意义)
- 刚好:实现 addToCart 方法(可验证、2-5分钟)
### 2. 渐进式开发
从核心开始:
1. 类型定义 → 2. 状态管理 → 3. UI 组件 → 4. 交互功能
### 3. 可验证性
每个任务都有明确的验证标准:
- 编译通过
- 测试通过
- 功能正常
### 4. 架构先行
写计划前先思考架构:
- 组件职责划分
- 数据流向
- 状态管理策略
## 常见错误
| 错误 | 正确做法 |
|------|----------|
| 任务太大(实现整个功能) | 分解为可验证的小任务 |
| 缺少验证标准 | 每个任务有明确验证方式 |
| 忽略测试 | 每个功能任务包含测试 |
| 不考虑依赖关系 | 按依赖顺序排列任务 |
## 进阶练习
完成基础练习后,尝试:
1. **添加优惠券功能**:输入优惠码,计算折扣
2. **添加库存检查**:商品有库存限制
3. **添加批量操作**:全选、批量删除
## 相关技能
- [writing-plans](/skills/writing-plans) - 计划编写技能
- [brainstorming](/skills/brainstorming) - 需求探索
- [test-driven-development](/skills/test-driven-development) - TDD 核心技能
- [executing-plans](/skills/executing-plans) - 计划执行