• 技术栈 (推荐):
    • 前端: Vue 3 + Vite + Element Plus (或 Ant Design Vue)
    • 后端: Node.js + Express (或 Koa)
    • 数据库: MySQL (或 PostgreSQL)
    • 认证: JWT (JSON Web Tokens)
  • 核心模块:
    1. 仪表盘: 数据概览,关键指标。
    2. 商品管理: 添加、编辑、查询、删除商品。
    3. 库存管理: 查看库存,出入库记录。
    4. 订单管理: 创建订单,查询订单状态。
    5. 用户管理: 管理系统用户和角色权限。
    6. 认证登录: 用户登录和权限控制。

第一步:项目结构

一个清晰的项目结构是良好开发的开端。

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>

第四步:如何扩展这个模板

这个模板是一个坚实的基础,你可以通过以下方式扩展它:

  1. 增加更多模块:

    • 供应商管理: 记录供应商信息,采购订单。
    • 客户管理: 记录客户信息,销售订单。
    • 财务管理: 记录收支流水,生成简单的财务报表。
    • 报表中心: 使用 ECharts 或其他图表库,将销售数据、库存数据可视化。
  2. 增强功能:

    • 权限管理: 实现基于角色的访问控制,普通员工只能看订单,管理员才能删除商品。
    • 数据导入导出: 支持通过 Excel/CSV 批量导入商品或订单数据。
    • 实时通知: 使用 WebSocket 实现库存预警、新订单通知等。
    • 文件上传: 为商品添加图片功能。
  3. 优化体验:

    • 前端: 使用更复杂的状态管理 (Pinia/Vuex),优化组件结构,增加骨架屏、加载动画等。
    • 后端: 引入缓存 (Redis) 提高查询性能,编写更完善的单元测试。

这个模板为你提供了一个从零开始构建简单 ERP 系统的完整框架,它遵循了现代 Web 应用的最佳实践,如前后端分离、RESTful API 设计、ORM、JWT 认证和组件化开发。

你可以直接复制这些代码,并根据你的具体业务需求进行修改和填充,祝你开发顺利!