在AI编程时代,人人都能编程。我以"个人记账助手"小程序为例,详细演示整个开发过程。
项目概述
项目名称 :个人记账助手
功能描述 :用户可以记录日常收支,查看统计图表,管理账单分类
技术栈 :微信小程序原生开发 + 云开发
开发方式 :Vibe Coding + AI辅助
第一阶段:需求分析与架构设计
与AI对话进行需求梳理
开发者描述:
我想开发一个微信小程序,功能是个人记账。用户可以:
1. 添加收入和支出记录
2. 选择消费分类(餐饮、交通、购物等)
3. 查看月度和年度统计
4. 支持图表展示
5. 数据云端存储
请帮我设计整体架构和页面结构。
AI助手回应:
基于您的需求,我为您设计以下架构:
页面结构:
├── pages/
│ ├── index/ # 首页(记录列表)
│ ├── add/ # 添加记录页面
│ ├── statistics/ # 统计页面
│ └── profile/ # 个人中心
功能模块:
1. 数据管理模块(CRUD操作)
2. 分类管理模块
3. 统计计算模块
4. 图表展示模块
5. 云存储模块
技术选型建议:
- 使用微信云开发作为后端
- ECharts小程序版本用于图表
- WeUI组件库优化界面
项目初始化
需求描述:
帮我生成微信小程序的基础配置文件,包括app.json、app.js、app.wxss,
设置好页面路由和基础样式。
AI生成的配置文件:
app.json:
json
{
"pages": [
"pages/index/index",
"pages/add/add",
"pages/statistics/statistics",
"pages/profile/profile"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#1AAD19",
"navigationBarTitleText": "记账助手",
"navigationBarTextStyle": "white",
"backgroundColor": "#f8f8f8"
},
"tabBar": {
"color": "#666",
"selectedColor": "#1AAD19",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [
{
"pagePath": "pages/index/index",
"text": "记录",
"iconPath": "images/record.png",
"selectedIconPath": "images/record-active.png"
},
{
"pagePath": "pages/statistics/statistics",
"text": "统计",
"iconPath": "images/chart.png",
"selectedIconPath": "images/chart-active.png"
},
{
"pagePath": "pages/profile/profile",
"text": "我的",
"iconPath": "images/profile.png",
"selectedIconPath": "images/profile-active.png"
}
]
},
"cloud": true
}
第二阶段:核心功能开发
首页记录列表
需求描述:
创建首页,显示最近的收支记录列表,包括:
- 顶部显示当月收支总额
- 记录列表显示日期、分类、金额、备注
- 支持下拉刷新和上拉加载更多
- 右下角添加按钮跳转到添加页面
AI生成的首页代码:
pages/index/index.wxml:
xml
<view class="container">
<!-- 月度统计卡片 -->
<view class="summary-card">
<view class="summary-item">
<text class="summary-label">本月收入</text>
<text class="summary-amount income">+{{monthIncome}}</text>
</view>
<view class="summary-item">
<text class="summary-label">本月支出</text>
<text class="summary-amount expense">-{{monthExpense}}</text>
</view>
<view class="summary-item">
<text class="summary-label">结余</text>
<text class="summary-amount {{monthBalance >= 0 ? 'income' : 'expense'}}">
{{monthBalance >= 0 ? '+' : ''}}{{monthBalance}}
</text>
</view>
</view>
<!-- 记录列表 -->
<scroll-view class="record-list"
scroll-y="true"
bindscrolltolower="loadMore"
refresher-enabled="true"
bindrefresherrefresh="onRefresh">
<view wx:for="{{records}}" wx:key="id" class="record-item">
<view class="record-left">
<image class="category-icon" src="{{item.categoryIcon}}"></image>
<view class="record-info">
<text class="record-category">{{item.categoryName}}</text>
<text class="record-note">{{item.note || '无备注'}}</text>
</view>
</view>
<view class="record-right">
<text class="record-amount {{item.type === 'income' ? 'income' : 'expense'}}">
{{item.type === 'income' ? '+' : '-'}}{{item.amount}}
</text>
<text class="record-date">{{item.date}}</text>
</view>
</view>
</scroll-view>
<!-- 添加按钮 -->
<view class="add-btn" bindtap="goToAdd">
<image src="/images/add.png"></image>
</view>
</view>
pages/index/index.js:
javascript
Page({
data: {
records: [],
monthIncome: 0,
monthExpense: 0,
monthBalance: 0,
page: 1,
hasMore: true
},
onLoad() {
this.initCloud();
this.loadRecords();
this.calculateMonthSummary();
},
// 初始化云开发
initCloud() {
if (!wx.cloud) {
console.error('请使用 2.2.3 或以上的基础库以使用云能力');
} else {
wx.cloud.init({
traceUser: true,
});
}
},
// 加载记录列表
async loadRecords() {
wx.showLoading({ title: '加载中...' });
try {
const db = wx.cloud.database();
const result = await db.collection('records')
.orderBy('createTime', 'desc')
.limit(20)
.skip((this.data.page - 1) * 20)
.get();
const newRecords = result.data.map(item => ({
...item,
date: this.formatDate(item.createTime)
}));
this.setData({
records: this.data.page === 1 ? newRecords : [...this.data.records, ...newRecords],
hasMore: result.data.length === 20
});
} catch (error) {
wx.showToast({ title: '加载失败', icon: 'error' });
}
wx.hideLoading();
},
// 计算月度统计
async calculateMonthSummary() {
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
try {
const db = wx.cloud.database();
const result = await db.collection('records')
.where({
createTime: db.command.gte(startOfMonth).and(db.command.lte(endOfMonth))
})
.get();
let income = 0, expense = 0;
result.data.forEach(record => {
if (record.type === 'income') {
income += record.amount;
} else {
expense += record.amount;
}
});
this.setData({
monthIncome: income.toFixed(2),
monthExpense: expense.toFixed(2),
monthBalance: (income - expense).toFixed(2)
});
} catch (error) {
console.error('统计计算失败', error);
}
},
// 格式化日期
formatDate(date) {
const d = new Date(date);
return `${d.getMonth() + 1}-${d.getDate()}`;
},
// 跳转到添加页面
goToAdd() {
wx.navigateTo({
url: '/pages/add/add'
});
},
// 下拉刷新
onRefresh() {
this.setData({ page: 1 });
this.loadRecords();
this.calculateMonthSummary();
},
// 上拉加载更多
loadMore() {
if (this.data.hasMore) {
this.setData({ page: this.data.page + 1 });
this.loadRecords();
}
}
});
添加记录页面
需求描述:
创建添加记录页面,包括:
- 收入/支出切换选项
- 金额输入(数字键盘)
- 分类选择(网格布局)
- 日期选择器
- 备注输入
- 提交按钮
AI生成的添加页面代码:
pages/add/add.wxml:
xml
<view class="container">
<!-- 类型切换 -->
<view class="type-switch">
<view class="switch-item {{recordType === 'expense' ? 'active' : ''}}"
bindtap="switchType" data-type="expense">
支出
</view>
<view class="switch-item {{recordType === 'income' ? 'active' : ''}}"
bindtap="switchType" data-type="income">
收入
</view>
</view>
<!-- 金额输入 -->
<view class="amount-section">
<text class="currency">¥</text>
<input class="amount-input"
type="digit"
placeholder="0.00"
value="{{amount}}"
bindinput="onAmountInput" />
</view>
<!-- 分类选择 -->
<view class="category-section">
<text class="section-title">选择分类</text>
<view class="category-grid">
<view wx:for="{{categories[recordType]}}"
wx:key="id"
class="category-item {{selectedCategory.id === item.id ? 'selected' : ''}}"
bindtap="selectCategory"
data-category="{{item}}">
<image class="category-icon" src="{{item.icon}}"></image>
<text class="category-name">{{item.name}}</text>
</view>
</view>
</view>
<!-- 日期选择 -->
<view class="date-section">
<text class="section-title">日期</text>
<picker mode="date"
value="{{date}}"
bindchange="onDateChange">
<view class="date-picker">
<text>{{date}}</text>
<image class="arrow-icon" src="/images/arrow-right.png"></image>
</view>
</picker>
</view>
<!-- 备注输入 -->
<view class="note-section">
<text class="section-title">备注</text>
<textarea class="note-input"
placeholder="添加备注信息(可选)"
value="{{note}}"
bindinput="onNoteInput"
maxlength="100">
</textarea>
</view>
<!-- 提交按钮 -->
<button class="submit-btn" bindtap="submitRecord">保存记录</button>
</view>
pages/add/add.js:
javascript
Page({
data: {
recordType: 'expense',
amount: '',
selectedCategory: {},
date: '',
note: '',
categories: {
expense: [
{ id: 1, name: '餐饮', icon: '/images/food.png' },
{ id: 2, name: '交通', icon: '/images/transport.png' },
{ id: 3, name: '购物', icon: '/images/shopping.png' },
{ id: 4, name: '娱乐', icon: '/images/entertainment.png' },
{ id: 5, name: '医疗', icon: '/images/medical.png' },
{ id: 6, name: '教育', icon: '/images/education.png' }
],
income: [
{ id: 7, name: '工资', icon: '/images/salary.png' },
{ id: 8, name: '奖金', icon: '/images/bonus.png' },
{ id: 9, name: '投资', icon: '/images/investment.png' },
{ id: 10, name: '其他', icon: '/images/other.png' }
]
}
},
onLoad() {
// 设置默认日期为今天
const today = new Date();
const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
this.setData({ date: dateStr });
},
// 切换收支类型
switchType(e) {
const type = e.currentTarget.dataset.type;
this.setData({
recordType: type,
selectedCategory: {} // 重置分类选择
});
},
// 金额输入
onAmountInput(e) {
let value = e.detail.value;
// 限制小数点后两位
if (value.includes('.')) {
const parts = value.split('.');
if (parts[1] && parts[1].length > 2) {
value = parts[0] + '.' + parts[1].substring(0, 2);
}
}
this.setData({ amount: value });
},
// 选择分类
selectCategory(e) {
const category = e.currentTarget.dataset.category;
this.setData({ selectedCategory: category });
},
// 日期选择
onDateChange(e) {
this.setData({ date: e.detail.value });
},
// 备注输入
onNoteInput(e) {
this.setData({ note: e.detail.value });
},
// 提交记录
async submitRecord() {
// 数据验证
if (!this.data.amount || parseFloat(this.data.amount) <= 0) {
wx.showToast({ title: '请输入有效金额', icon: 'error' });
return;
}
if (!this.data.selectedCategory.id) {
wx.showToast({ title: '请选择分类', icon: 'error' });
return;
}
wx.showLoading({ title: '保存中...' });
try {
const db = wx.cloud.database();
await db.collection('records').add({
data: {
type: this.data.recordType,
amount: parseFloat(this.data.amount),
categoryId: this.data.selectedCategory.id,
categoryName: this.data.selectedCategory.name,
categoryIcon: this.data.selectedCategory.icon,
date: this.data.date,
note: this.data.note,
createTime: new Date()
}
});
wx.showToast({ title: '保存成功', icon: 'success' });
// 返回首页并刷新
setTimeout(() => {
wx.navigateBack();
}, 1500);
} catch (error) {
wx.showToast({ title: '保存失败', icon: 'error' });
console.error('保存记录失败', error);
}
wx.hideLoading();
}
});
第三阶段:数据可视化
统计页面开发
需求描述:
创建统计页面,包括:
- 月度/年度切换
- 收支趋势图表(折线图)
- 支出分类占比(饼图)
- 详细数据列表
使用ECharts实现图表功能
AI生成统计页面代码:
pages/statistics/statistics.js:
javascript
import * as echarts from '../../libs/ec-canvas/echarts';
Page({
data: {
ec: {
onInit: null
},
pieChart: {
onInit: null
},
periodType: 'month', // month/year
currentPeriod: '',
totalIncome: 0,
totalExpense: 0,
categoryStats: []
},
onLoad() {
this.initCharts();
this.loadStatistics();
},
// 初始化图表
initCharts() {
this.setData({
'ec.onInit': this.initTrendChart,
'pieChart.onInit': this.initPieChart
});
},
// 初始化趋势图
initTrendChart(canvas, width, height, dpr) {
const chart = echarts.init(canvas, null, {
width: width,
height: height,
devicePixelRatio: dpr
});
const option = {
title: {
text: '收支趋势',
left: 'center',
textStyle: { fontSize: 16 }
},
legend: {
data: ['收入', '支出'],
bottom: 10
},
grid: {
left: 40,
right: 40,
top: 60,
bottom: 60
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: [{
name: '收入',
type: 'line',
data: [],
itemStyle: { color: '#1AAD19' }
}, {
name: '支出',
type: 'line',
data: [],
itemStyle: { color: '#E64340' }
}]
};
chart.setOption(option);
this.trendChart = chart;
return chart;
},
// 初始化饼图
initPieChart(canvas, width, height, dpr) {
const chart = echarts.init(canvas, null, {
width: width,
height: height,
devicePixelRatio: dpr
});
const option = {
title: {
text: '支出分类',
left: 'center',
textStyle: { fontSize: 16 }
},
series: [{
type: 'pie',
radius: ['30%', '70%'],
center: ['50%', '60%'],
data: [],
label: {
formatter: '{b}: {d}%'
}
}]
};
chart.setOption(option);
this.pieChart = chart;
return chart;
},
// 加载统计数据
async loadStatistics() {
wx.showLoading({ title: '加载中...' });
try {
const db = wx.cloud.database();
const now = new Date();
let startDate, endDate;
if (this.data.periodType === 'month') {
startDate = new Date(now.getFullYear(), now.getMonth(), 1);
endDate = new Date(now.getFullYear(), now.getMonth() + 1, 0);
this.setData({ currentPeriod: `${now.getFullYear()}年${now.getMonth() + 1}月` });
} else {
startDate = new Date(now.getFullYear(), 0, 1);
endDate = new Date(now.getFullYear(), 11, 31);
this.setData({ currentPeriod: `${now.getFullYear()}年` });
}
const result = await db.collection('records')
.where({
createTime: db.command.gte(startDate).and(db.command.lte(endDate))
})
.get();
this.processStatistics(result.data);
} catch (error) {
wx.showToast({ title: '加载失败', icon: 'error' });
}
wx.hideLoading();
},
// 处理统计数据
processStatistics(records) {
let totalIncome = 0;
let totalExpense = 0;
const categoryMap = new Map();
const dailyStats = new Map();
records.forEach(record => {
if (record.type === 'income') {
totalIncome += record.amount;
} else {
totalExpense += record.amount;
// 分类统计
const categoryName = record.categoryName;
if (categoryMap.has(categoryName)) {
categoryMap.set(categoryName, categoryMap.get(categoryName) + record.amount);
} else {
categoryMap.set(categoryName, record.amount);
}
}
// 日期统计
const dateKey = record.date;
if (!dailyStats.has(dateKey)) {
dailyStats.set(dateKey, { income: 0, expense: 0 });
}
if (record.type === 'income') {
dailyStats.get(dateKey).income += record.amount;
} else {
dailyStats.get(dateKey).expense += record.amount;
}
});
this.setData({
totalIncome: totalIncome.toFixed(2),
totalExpense: totalExpense.toFixed(2)
});
this.updateCharts(dailyStats, categoryMap);
},
// 更新图表
updateCharts(dailyStats, categoryMap) {
// 更新趋势图
const dates = Array.from(dailyStats.keys()).sort();
const incomeData = dates.map(date => dailyStats.get(date).income);
const expenseData = dates.map(date => dailyStats.get(date).expense);
if (this.trendChart) {
this.trendChart.setOption({
xAxis: { data: dates },
series: [{
data: incomeData
}, {
data: expenseData
}]
});
}
// 更新饼图
const pieData = Array.from(categoryMap.entries()).map(([name, value]) => ({
name,
value: value.toFixed(2)
}));
if (this.pieChart) {
this.pieChart.setOption({
series: [{
data: pieData
}]
});
}
}
});
第四阶段:优化与部署
性能优化
需求描述:
对小程序进行性能优化,包括:
- 图片懒加载
- 数据缓存机制
- 分页加载优化
- 代码分包
AI提供的优化方案:
javascript
// 数据缓存管理
class CacheManager {
static cache = new Map();
static set(key, data, expireTime = 5 * 60 * 1000) {
this.cache.set(key, {
data,
expire: Date.now() + expireTime
});
}
static get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expire) {
this.cache.delete(key);
return null;
}
return item.data;
}
}
// 图片懒加载组件
Component({
properties: {
src: String,
defaultSrc: String
},
data: {
loaded: false,
currentSrc: ''
},
lifetimes: {
attached() {
this.setData({
currentSrc: this.data.defaultSrc || '/images/placeholder.png'
});
this.createIntersectionObserver()
.relativeToViewport()
.observe('.lazy-image', (res) => {
if (res.intersectionRatio > 0 && !this.data.loaded) {
this.loadImage();
}
});
}
},
methods: {
loadImage() {
wx.previewImage({
urls: [this.data.src],
success: () => {
this.setData({
currentSrc: this.data.src,
loaded: true
});
}
});
}
}
});
云开发配置
需求描述:
配置微信云开发环境,包括:
- 数据库集合设计
- 云函数部署
- 安全规则设置
AI生成的云开发配置:
javascript
// 云函数:统计数据聚合
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext();
const db = cloud.database();
try {
const { type, startDate, endDate } = event;
// 使用聚合查询提高性能
const result = await db.collection('records')
.where({
_openid: wxContext.OPENID,
createTime: db.command.gte(new Date(startDate))
.and(db.command.lte(new Date(endDate)))
})
.aggregate()
.group({
_id: '$categoryName',
totalAmount: db.command.sum('$amount'),
count: db.command.sum(1)
})
.end();
return {
success: true,
data: result.list
};
} catch (error) {
return {
success: false,
error: error.message
};
}
};
总结与展望
通过这个完整的Vibe Coding开发案例,我们可以看到AI辅助编程在微信小程序开发中的强大威力:
开发效率提升
- 需求到代码:直接描述需求即可生成代码框架
- 快速迭代:修改需求后快速调整实现
- 最佳实践:AI自动应用行业最佳实践
学习成本降低
- 语法学习:无需深入学习复杂语法
- 框架理解:通过实例快速理解框架特性
- 问题解决:AI提供调试和优化建议
代码质量保证
- 结构规范:生成的代码结构清晰规范
- 性能优化:自动应用性能优化技巧
- 安全考虑:内置安全最佳实践
这种开发方式特别适合:
- 快速原型验证
- 个人项目开发
- 学习新技术栈
- 提升开发效率
随着AI技术的不断发展,Vibe Coding将成为软件开发的重要趋势,让更多人能够参与到软件创造中来。