- 技术栈 (推荐):
- 前端:
Vue 3+Vite+Element Plus(或Ant Design Vue) - 后端:
Node.js+Express(或Koa) - 数据库:
MySQL(或PostgreSQL) - 认证:
JWT(JSON Web Tokens)
- 前端:
- 核心模块:
- 仪表盘: 数据概览,关键指标。
- 商品管理: 添加、编辑、查询、删除商品。
- 库存管理: 查看库存,出入库记录。
- 订单管理: 创建订单,查询订单状态。
- 用户管理: 管理系统用户和角色权限。
- 认证登录: 用户登录和权限控制。
第一步:项目结构
一个清晰的项目结构是良好开发的开端。
simple-erp-template/
├── client/ # 前端项目 (Vue 3)
│ ├── public/
│ ├── src/
│ │ ├── assets/ # 静态资源
│ │ ├── components/ # 公共组件
│ │ ├── views/ # 页面视图
│ │ │ ├── Dashboard.vue
│ │ │ ├── ProductManagement.vue
│ │ │ ├── InventoryManagement.vue
│ │ │ ├── OrderManagement.vue
│ │ │ └── UserManagement.vue
│ │ ├── router/ # 路由配置
│ │ ├── store/ # 状态管理 (Pinia)
│ │ ├── utils/ # 工具函数 (如 api.js)
│ │ ├── App.vue
│ │ └── main.js
│ ├── package.json
│ └── vite.config.js
│
├── server/ # 后端项目 (Node.js + Express)
│ ├── config/ # 配置文件 (如数据库连接)
│ ├── controllers/ # 控制器 (处理业务逻辑)
│ │ ├── auth.controller.js
│ │ ├── product.controller.js
│ │ ├── inventory.controller.js
│ │ └── order.controller.js
│ ├── models/ # 数据模型 (数据库表结构)
│ │ ├── User.js
│ │ ├── Product.js
│ │ ├── Inventory.js
│ │ └── Order.js
│ ├── routes/ # 路由定义
│ │ ├── auth.routes.js
│ │ ├── product.routes.js
│ │ ├── inventory.routes.js
│ │ └── order.routes.js
│ ├── middleware/ # 中间件 (如认证、日志)
│ │ └── auth.middleware.js
│ ├── .env # 环境变量
│ ├── .gitignore
│ └── server.js # 服务器入口文件
│
└── README.md # 项目说明
第二步:后端核心代码实现
后端负责提供数据接口和业务逻辑。
数据库模型 (server/models/Product.js)
使用 Sequelize (一个 Node.js 的 ORM 库) 来定义数据模型,方便操作数据库。
// server/models/Product.js
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database'); // 引入数据库连接
const Product = sequelize.define('Product', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
},
sku: {
type: DataTypes.STRING,
unique: true, // SKU必须唯一
allowNull: false,
},
price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
stock: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
defaultValue: 'active',
},
});
module.exports = Product;
其他模型 (User.js, Inventory.js, Order.js) 也类似定义,Inventory 可以和 Product 关联,Order 可以包含多个 Product。
路由 (server/routes/product.routes.js)
定义 API 的 URL 路径。
// server/routes/product.routes.js
const express = require('express');
const router = express.Router();
const productController = require('../controllers/product.controller');
const { authMiddleware } = require('../middleware/auth.middleware'); // 引入认证中间件
// 所有商品路由都需要登录
router.use(authMiddleware);
// 获取所有商品
router.get('/', productController.getAllProducts);
// 根据 ID 获取单个商品
router.get('/:id', productController.getProductById);
// 创建新商品
router.post('/', productController.createProduct);
// 更新商品
router.put('/:id', productController.updateProduct);
// 删除商品
router.delete('/:id', productController.deleteProduct);
module.exports = router;
控制器 (server/controllers/product.controller.js)
处理具体的业务逻辑,与模型交互。
// server/controllers/product.controller.js
const Product = require('../models/Product');
// 获取所有商品
exports.getAllProducts = async (req, res) => {
try {
const products = await Product.findAll();
res.json({ success: true, data: products });
} catch (error) {
res.status(500).json({ success: false, message: '服务器错误' });
}
};
// ... createProduct, updateProduct, deleteProduct 方法类似
// 关键点:使用 try...catch 捕获错误,使用 Sequelize 提供的方法 (findAll, create, update, destroy) 操作数据库。
认证中间件 (server/middleware/auth.middleware.js)
使用 JWT 来保护需要登录才能访问的路由。
// server/middleware/auth.middleware.js
const jwt = require('jsonwebtoken');
const { JWT_SECRET } = process.env; // 从环境变量获取密钥
exports.authMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ message: '无访问权限,请先登录' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded; // 将用户信息附加到请求对象上
next();
} catch (error) {
res.status(401).json({ message: '无效的令牌' });
}
};
服务器入口 (server/server.js)
整合所有路由和中间件。
// server/server.js
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const sequelize = require('./config/database');
const app = express();
app.use(cors()); // 允许前端跨域请求
app.use(express.json()); // 解析 JSON 请求体
// 引入路由
app.use('/api/auth', require('./routes/auth.routes'));
app.use('/api/products', require('./routes/product.routes'));
// ... 其他路由
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器出错了!');
});
// 启动服务器并连接数据库
const PORT = process.env.PORT || 5000;
sequelize.sync({ force: false }) // force: true 会清空数据库,生产环境务必设为 false
.then(() => {
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
});
第三步:前端核心代码实现
前端负责展示数据、用户交互,并与后端 API 通信。
API 工具 (client/src/utils/api.js)
封装 axios,统一处理请求、响应和错误。
// client/src/utils/api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:5000/api', // 后端API地址
timeout: 10000,
});
// 请求拦截器:自动添加 JWT token
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器:统一处理错误
api.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response && error.response.status === 401) {
// token 失效,跳转到登录页
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default api;
路由 (client/src/router/index.js)
定义前端页面路由,并添加导航守卫进行权限控制。
// client/src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Dashboard from '../views/Dashboard.vue';
import ProductManagement from '../views/ProductManagement.vue';
import Login from '../views/Login.vue'; // 假设你有登录页
const routes = [
{
path: '/',
redirect: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }, // 需要登录
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: { requiresAuth: true },
},
{
path: '/products',
name: 'ProductManagement',
component: ProductManagement,
meta: { requiresAuth: true },
},
{
path: '/login',
name: 'Login',
component: Login,
meta: { guest: true }, // 仅未登录用户可访问
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 导航守卫
router.beforeEach((to, from, next) => {
const isAuthenticated = !!localStorage.getItem('token');
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated) {
next('/login');
} else {
next();
}
} else if (to.matched.some(record => record.meta.guest)) {
if (isAuthenticated) {
next('/dashboard');
} else {
next();
}
} else {
next();
}
});
export default router;
视图 - 商品管理 (client/src/views/ProductManagement.vue)
这是一个典型的页面组件,包含数据获取、展示和操作。
<template>
<div class="product-management">
<h1>商品管理</h1>
<!-- 操作按钮 -->
<el-button type="primary" @click="showAddDialog = true">添加商品</el-button>
<!-- 商品列表 -->
<el-table :data="products" style="width: 100%">
<el-table-column prop="name" label="商品名称" />
<el-table-column prop="sku" label="SKU" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="stock" label="库存" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加/编辑对话框 -->
<el-dialog v-model="showAddDialog" title="添加商品">
<el-form :model="currentProduct" label-width="120px">
<el-form-item label="商品名称">
<el-input v-model="currentProduct.name" />
</el-form-item>
<el-form-item label="SKU">
<el-input v-model="currentProduct.sku" />
</el-form-item>
<el-form-item label="价格">
<el-input-number v-model="currentProduct.price" />
</el-form-item>
<el-form-item label="库存">
<el-input-number v-model="currentProduct.stock" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showAddDialog = false">取消</el-button>
<el-button type="primary" @click="saveProduct">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import api from '../utils/api';
const products = ref([]);
const showAddDialog = ref(false);
const currentProduct = ref({ name: '', sku: '', price: 0, stock: 0 });
const isEditing = ref(false);
// 获取商品列表
const fetchProducts = async () => {
try {
const response = await api.get('/products');
products.value = response.data.data;
} catch (error) {
console.error('获取商品失败:', error);
ElMessage.error('获取商品列表失败');
}
};
// 保存商品(新增或编辑)
const saveProduct = async () => {
try {
if (isEditing.value) {
await api.put(`/products/${currentProduct.value.id}`, currentProduct.value);
ElMessage.success('商品更新成功');
} else {
await api.post('/products', currentProduct.value);
ElMessage.success('商品添加成功');
}
showAddDialog.value = false;
fetchProducts(); // 刷新列表
} catch (error) {
ElMessage.error('操作失败');
}
};
const handleEdit = (product) => {
currentProduct.value = { ...product };
isEditing.value = true;
showAddDialog.value = true;
};
const handleDelete = async (product) => {
try {
await api.delete(`/products/${product.id}`);
ElMessage.success('商品删除成功');
fetchProducts();
} catch (error) {
ElMessage.error('删除失败');
}
};
onMounted(() => {
fetchProducts();
});
</script>
第四步:如何扩展这个模板
这个模板是一个坚实的基础,你可以通过以下方式扩展它:
-
增加更多模块:
- 供应商管理: 记录供应商信息,采购订单。
- 客户管理: 记录客户信息,销售订单。
- 财务管理: 记录收支流水,生成简单的财务报表。
- 报表中心: 使用 ECharts 或其他图表库,将销售数据、库存数据可视化。
-
增强功能:
- 权限管理: 实现基于角色的访问控制,普通员工只能看订单,管理员才能删除商品。
- 数据导入导出: 支持通过 Excel/CSV 批量导入商品或订单数据。
- 实时通知: 使用 WebSocket 实现库存预警、新订单通知等。
- 文件上传: 为商品添加图片功能。
-
优化体验:
- 前端: 使用更复杂的状态管理 (Pinia/Vuex),优化组件结构,增加骨架屏、加载动画等。
- 后端: 引入缓存 (Redis) 提高查询性能,编写更完善的单元测试。
这个模板为你提供了一个从零开始构建简单 ERP 系统的完整框架,它遵循了现代 Web 应用的最佳实践,如前后端分离、RESTful API 设计、ORM、JWT 认证和组件化开发。
你可以直接复制这些代码,并根据你的具体业务需求进行修改和填充,祝你开发顺利!
