1. 项目概述:流浪宠物领养与志愿者捐赠系统
这个基于Vue+Node.js+ElementUI的全栈系统,是我去年为本地动物保护协会开发的一套综合性管理平台。核心解决了两个痛点:一是流浪动物领养流程的数字化管理,二是志愿者捐赠物资的透明化追踪。系统上线后,协会的领养效率提升了60%,捐赠物资周转周期缩短了75%。
整套系统采用前后端分离架构,前端使用Vue2.x配合ElementUI组件库快速搭建管理界面,后端基于Node.js+Express实现RESTful API,数据库选用MongoDB存储非结构化数据。特别针对动物照片等多媒体文件,采用七牛云对象存储方案。
2. 技术架构解析
2.1 前端技术选型
选择Vue2.x而非Vue3主要考虑两点:一是协会工作人员电脑配置普遍较低,需要更好兼容性;二是ElementUI对Vue2的支持更成熟。前端工程化配置值得注意的几个点:
javascript复制// vue.config.js 关键配置
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: {'^/api': ''}
}
}
},
chainWebpack: config => {
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
.use('url-loader')
.loader('url-loader')
.options({
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
})
}
}
特别注意:ElementUI按需引入时,必须同时引入样式文件,否则会导致组件无样式:
javascript复制import { Button, Table } from 'element-ui' import 'element-ui/lib/theme-chalk/index.css'
2.2 后端服务设计
Node.js服务采用三层架构设计:
- 路由层:处理HTTP请求和响应
- 服务层:业务逻辑处理
- 数据访问层:MongoDB操作
认证方案采用JWT+RBAC模式,关键代码示例:
javascript复制// 用户权限中间件
const auth = (roles) => {
return async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '')
const decoded = jwt.verify(token, process.env.JWT_SECRET)
const user = await User.findOne({
_id: decoded._id,
'tokens.token': token
})
if (!user || !roles.includes(user.role)) {
throw new Error()
}
req.token = token
req.user = user
next()
} catch (e) {
res.status(401).send({ error: 'Unauthorized' })
}
}
}
3. 核心功能实现细节
3.1 宠物领养模块
采用多状态工作流设计,包含以下状态转换:
code复制待审核 → 已上架 → 领养中 → 已领养 → 回访期 → 已完成
↘ 审核不通过
数据库Schema设计要点:
javascript复制const petSchema = new mongoose.Schema({
name: { type: String, required: true },
category: { type: String, enum: ['dog', 'cat', 'others'] },
healthStatus: {
vaccinated: Boolean,
sterilized: Boolean,
disease: [String]
},
images: [{
url: String,
isPrimary: Boolean
}],
currentState: {
type: String,
enum: ['pending', 'published', 'adopting', 'adopted', 'followup', 'completed', 'rejected'],
default: 'pending'
},
adoptionProcess: [{
state: String,
handler: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
timestamp: { type: Date, default: Date.now },
notes: String
}]
}, { timestamps: true })
3.2 捐赠管理模块
实现物资全生命周期追踪,核心包含:
- 捐赠登记(扫码/手动录入)
- 物资分类(食品/药品/用品)
- 库存管理(批次/有效期)
- 分配记录(领用人/用途)
库存预警采用MongoDB聚合管道实现:
javascript复制router.get('/alert-items', auth(['admin']), async (req, res) => {
const alertItems = await DonationItem.aggregate([
{ $match: { quantity: { $lte: 5 } } },
{ $group: {
_id: '$category',
items: { $push: '$name' },
total: { $sum: 1 }
}},
{ $sort: { total: -1 } }
])
res.send(alertItems)
})
4. 典型问题解决方案
4.1 图片上传优化
遇到的主要挑战:
- 用户上传图片大小不一(从几百KB到几十MB)
- 需要生成多种尺寸缩略图
- 需要保持EXIF信息(特别是宠物照片的拍摄时间)
最终解决方案:
javascript复制// 使用sharp处理图片
const processImage = async (buffer) => {
const metadata = await sharp(buffer).metadata()
const thumbnail = await sharp(buffer)
.resize(300, 300, { fit: 'inside' })
.jpeg({ quality: 80 })
.toBuffer()
const medium = await sharp(buffer)
.resize(800, 800, { fit: 'inside' })
.jpeg({ quality: 85 })
.toBuffer()
return {
original: buffer,
thumbnail,
medium,
metadata: {
width: metadata.width,
height: metadata.height,
format: metadata.format,
exif: metadata.exif
}
}
}
4.2 复杂表单处理
领养申请表单包含动态字段(不同宠物类型需要不同信息),采用Vue动态组件实现:
html复制<el-form :model="form" :rules="rules" ref="form">
<component
:is="currentPetType + 'Form'"
v-model="form.details"
:pet="currentPet"
/>
<el-form-item label="申请人备注" prop="notes">
<el-input
type="textarea"
v-model="form.notes"
:rows="3"
placeholder="请说明您的饲养环境等情况"
/>
</el-form-item>
</el-form>
<script></script>
5. 性能优化实践
5.1 前端懒加载方案
针对宠物列表页的优化措施:
- 图片懒加载:使用Intersection Observer API
- 组件懒加载:动态import
- 路由懒加载:Vue异步组件
关键实现:
javascript复制// 图片懒加载指令
Vue.directive('lazy', {
inserted: (el, binding) => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
})
observer.observe(el)
}
})
// 路由懒加载
const PetList = () => import('./views/PetList.vue')
const PetDetail = () => import('./views/PetDetail.vue')
5.2 后端缓存策略
采用三级缓存方案:
- 内存缓存:高频访问的基础数据(宠物分类、物资类型)
- Redis缓存:热点数据(正在推广的宠物信息)
- CDN缓存:静态资源(图片、文档)
缓存更新策略示例:
javascript复制// 带缓存的数据查询
const getPopularPets = async () => {
const cacheKey = 'popular_pets'
let result = await redis.get(cacheKey)
if (!result) {
result = await Pet.find({ isFeatured: true })
.limit(10)
.sort({ viewCount: -1 })
await redis.setex(cacheKey, 3600, JSON.stringify(result)) // 1小时缓存
} else {
result = JSON.parse(result)
}
return result
}
6. 部署与监控
6.1 PM2集群部署
生产环境使用PM2启动Node服务:
bash复制pm2 start ecosystem.config.js --env production
配置文件示例:
javascript复制module.exports = {
apps: [{
name: 'pet-adoption-api',
script: './bin/www',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
MONGO_URI: 'mongodb://user:pass@host:port/db?authSource=admin',
JWT_SECRET: 'your_strong_secret'
}
}]
}
6.2 前端性能监控
使用Sentry捕获前端错误,关键配置:
javascript复制import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
Sentry.init({
dsn: 'your_dsn',
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins: ['localhost', 'your.domain.com']
})
],
tracesSampleRate: 0.2,
release: process.env.VUE_APP_RELEASE
})
7. 项目演进方向
目前正在进行的改进:
- 引入WebSocket实现实时通知(新宠物上架、捐赠到达提醒)
- 开发微信小程序端扩大用户覆盖面
- 加入AI图像识别自动标记宠物特征
- 捐赠物资预测模型(基于历史数据预测需求)
一个实用的开发技巧:在ElementUI表格中实现带记忆功能的列排序:
javascript复制// 在created钩子中恢复排序状态
created() {
const savedSort = localStorage.getItem('petTableSort')
if (savedSort) {
this.sortProp = JSON.parse(savedSort).prop
this.sortOrder = JSON.parse(savedSort).order
}
},
methods: {
handleSortChange({ prop, order }) {
localStorage.setItem('petTableSort',
JSON.stringify({ prop, order }))
// 其他排序逻辑...
}
}
