基于nodejs+vue开发的官网前端和管理后台模版代码
本文介绍了基于Node.js+Vue的官网前端和管理后台模板的完整搭建指南。技术栈选用Vue 3+Element Plus+Pinia+Vite,后端采用Express/Koa框架。项目结构清晰划分client前端和server后端目录,包含路由配置、状态管理、API接口等模块。核心代码展示了前端路由配置(区分官网路由和管理后台路由,带权限验证)和管理后台布局实现(使用Element Plus组件
·
以下是一个基于 Node.js + Vue 的官网前端和管理后台模板从0到1的完整搭建指南,包含代码示例和关键步骤:
一、技术栈选择
模块 | 技术方案 | 说明 |
---|---|---|
前端框架 | Vue 3 + Composition API | 官网和管理台统一技术栈 |
UI组件库 | Element Plus | 管理台专用 |
状态管理 | Pinia | Vue官方推荐状态库 |
路由 | Vue Router | 支持动态路由和权限控制 |
HTTP库 | Axios + 拦截器 | 统一请求管理 |
构建工具 | Vite | 极速启动 + 热更新 |
后端框架 | Express/Koa | 提供API接口 |
数据库 | MongoDB/Mysql | 按业务需求选择 |
二、项目结构设计
project/
├── client/ # 前端代码
│ ├── public/ # 静态资源
│ ├── src/
│ │ ├── assets/ # 图片/字体等
│ │ ├── components/ # 公共组件
│ │ ├── layouts/ # 页面布局
│ │ │ ├── MainLayout.vue # 官网布局
│ │ │ └── AdminLayout.vue # 管理后台布局
│ │ ├── router/ # 路由配置
│ │ ├── store/ # 状态管理
│ │ ├── views/ # 页面组件
│ │ │ ├── front/ # 官网页面
│ │ │ └── admin/ # 管理后台页面
│ │ ├── utils/ # 工具函数
│ │ └── App.vue # 根组件
│ └── vite.config.js # Vite配置
│
├── server/ # 后端代码
│ ├── config/ # 配置文件
│ ├── controllers/ # 控制器
│ ├── models/ # 数据库模型
│ ├── routes/ # API路由
│ ├── middlewares/ # 中间件
│ └── app.js # 入口文件
│
├── .env # 环境变量
└── package.json # 项目配置
三、核心代码实现
1. 前端路由配置 (src/router/index.js
)
import { createRouter, createWebHistory } from 'vue-router'
import MainLayout from '@/layouts/MainLayout.vue'
import AdminLayout from '@/layouts/AdminLayout.vue'
// 官网路由
const frontRoutes = [
{ path: '/', name: 'Home', component: () => import('@/views/front/Home.vue') },
{ path: '/about', name: 'About', component: () => import('@/views/front/About.vue') },
{ path: '/contact', name: 'Contact', component: () => import('@/views/front/Contact.vue') }
]
// 管理后台路由(需鉴权)
const adminRoutes = [
{
path: '/admin',
component: AdminLayout,
meta: { requiresAuth: true },
children: [
{ path: 'dashboard', name: 'Dashboard', component: () => import('@/views/admin/Dashboard.vue') },
{ path: 'users', name: 'UserList', component: () => import('@/views/admin/UserList.vue') },
{ path: 'settings', name: 'Settings', component: () => import('@/views/admin/Settings.vue') }
]
},
{ path: '/admin/login', name: 'AdminLogin', component: () => import('@/views/admin/Login.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: MainLayout,
children: frontRoutes
},
...adminRoutes,
{ path: '/:pathMatch(.*)*', redirect: '/' } // 404处理
]
})
// 路由守卫
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('token')
if (to.matched.some(record => record.meta.requiresAuth) {
if (!isAuthenticated) {
next({ name: 'AdminLogin' })
} else {
next()
}
} else {
next()
}
})
export default router
2. 管理后台布局 (src/layouts/AdminLayout.vue
)
<template>
<div class="admin-layout">
<el-container>
<!-- 侧边栏 -->
<el-aside width="200px">
<div class="logo">管理后台</div>
<el-menu router :default-active="$route.path">
<el-menu-item index="/admin/dashboard">
<el-icon><dashboard /></el-icon>
<span>仪表盘</span>
</el-menu-item>
<el-menu-item index="/admin/users">
<el-icon><user /></el-icon>
<span>用户管理</span>
</el-menu-item>
<el-submenu index="3">
<template #title>
<el-icon><setting /></el-icon>
<span>系统设置</span>
</template>
<el-menu-item index="/admin/settings/basic">基础设置</el-menu-item>
<el-menu-item index="/admin/settings/security">安全设置</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 主内容区 -->
<el-container>
<el-header>
<div class="header-right">
<el-dropdown>
<span class="el-dropdown-link">
<el-avatar :size="30" src="https://avatar.png" />
Admin
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-main>
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { Dashboard, User, Setting } from '@element-plus/icons-vue'
const router = useRouter()
const logout = () => {
localStorage.removeItem('token')
router.push({ name: 'AdminLogin' })
}
</script>
<style scoped>
.admin-layout {
height: 100vh;
}
.el-header {
background: #fff;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: flex-end;
}
.el-aside {
background: #304156;
}
.logo {
height: 60px;
line-height: 60px;
text-align: center;
color: #fff;
font-size: 18px;
font-weight: bold;
}
</style>
3. 用户管理页面 (src/views/admin/UserList.vue
)
<template>
<div class="user-list">
<el-card>
<template #header>
<div class="card-header">
<span>用户管理</span>
<el-button type="primary" @click="handleAdd">新增用户</el-button>
</div>
</template>
<el-table :data="userList" v-loading="loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="createdAt" label="创建时间" />
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="fetchUsers"
layout="prev, pager, next"
/>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import api from '@/api'
const userList = ref([])
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
const fetchUsers = async () => {
try {
loading.value = true
const res = await api.getUsers({
page: currentPage.value,
pageSize: pageSize.value
})
userList.value = res.data.list
total.value = res.data.total
} catch (error) {
ElMessage.error('获取用户列表失败')
} finally {
loading.value = false
}
}
const handleAdd = () => {
// 打开添加用户对话框
}
const handleEdit = (user) => {
// 打开编辑用户对话框
}
const handleDelete = (id) => {
ElMessageBox.confirm('确定删除该用户?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await api.deleteUser(id)
ElMessage.success('删除成功')
fetchUsers()
})
}
onMounted(() => {
fetchUsers()
})
</script>
<style scoped>
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.pagination {
margin-top: 20px;
justify-content: flex-end;
}
</style>
4. API 封装 (src/api/index.js
)
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
})
// 请求拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
// 业务错误处理
if (res.code !== 200) {
ElMessage({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 未认证处理
if (res.code === 401) {
localStorage.removeItem('token')
router.push({ name: 'AdminLogin' })
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
ElMessage({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
// API方法封装
export default {
// 用户相关
login(data) {
return service.post('/auth/login', data)
},
getUsers(params) {
return service.get('/users', { params })
},
getUser(id) {
return service.get(`/users/${id}`)
},
createUser(data) {
return service.post('/users', data)
},
updateUser(id, data) {
return service.put(`/users/${id}`, data)
},
deleteUser(id) {
return service.delete(`/users/${id}`)
},
// 其他业务API...
}
5. 后端Express基础框架 (server/app.js
)
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const authRoutes = require('./routes/auth')
const userRoutes = require('./routes/users')
const app = express()
const PORT = process.env.PORT || 3000
// 数据库连接
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err))
// 中间件
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// 路由
app.use('/api/auth', authRoutes)
app.use('/api/users', userRoutes)
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).json({ message: 'Internal Server Error' })
})
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
6. 用户模型 (server/models/User.js
)
const mongoose = require('mongoose')
const bcrypt = require('bcryptjs')
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['admin', 'editor', 'viewer'],
default: 'viewer'
},
createdAt: {
type: Date,
default: Date.now
}
})
// 密码加密
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next()
try {
const salt = await bcrypt.genSalt(10)
this.password = await bcrypt.hash(this.password, salt)
next()
} catch (error) {
next(error)
}
})
// 密码验证方法
userSchema.methods.comparePassword = async function(candidatePassword) {
return bcrypt.compare(candidatePassword, this.password)
}
module.exports = mongoose.model('User', userSchema)
四、开发流程步骤
步骤1:初始化项目
# 创建项目目录
mkdir my-project && cd my-project
# 初始化前端项目
npm create vite@latest client -- --template vue
cd client
npm install axios pinia vue-router element-plus @element-plus/icons-vue
# 初始化后端项目
cd ..
mkdir server && cd server
npm init -y
npm install express mongoose bcryptjs cors dotenv jsonwebtoken
步骤2:配置环境变量 (.env
)
# 前端
VITE_API_BASE_URL=http://localhost:3000/api
# 后端
PORT=3000
MONGODB_URI=mongodb://localhost:27017/myapp
JWT_SECRET=myapp_secret_key
步骤3:实现核心功能模块
-
官网前端:
- 首页:产品介绍、特色功能
- 关于页面:公司介绍、团队信息
- 联系页面:联系方式、反馈表单
-
管理后台:
- 登录认证:JWT实现
- 仪表盘:数据概览
- 用户管理:CRUD操作
- 角色权限:RBAC控制
- 系统设置:基础配置
步骤4:API接口开发
// server/routes/users.js
const express = require('express')
const router = express.Router()
const User = require('../models/User')
const authMiddleware = require('../middlewares/auth')
// 获取用户列表
router.get('/', authMiddleware('admin'), async (req, res) => {
try {
const { page = 1, pageSize = 10 } = req.query
const skip = (page - 1) * pageSize
const [users, total] = await Promise.all([
User.find().skip(skip).limit(Number(pageSize)),
User.countDocuments()
])
res.json({
code: 200,
data: {
list: users,
total,
page: Number(page),
pageSize: Number(pageSize)
}
})
} catch (error) {
res.status(500).json({ code: 500, message: '服务器错误' })
}
})
// 其他用户操作接口...
五、优化建议
1. 性能优化
// Vite配置优化 (vite.config.js)
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
element: ['element-plus'],
vendor: ['vue', 'vue-router', 'pinia']
}
}
}
},
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
})
2. 安全加固
// 后端安全中间件 (server/middlewares/security.js)
const helmet = require('helmet')
const rateLimit = require('express-rate-limit')
// 设置安全头部
app.use(helmet())
// API请求限流
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100次请求
message: '请求过于频繁,请稍后再试'
})
app.use('/api/', apiLimiter)
3. 部署方案
# 前端构建
cd client
npm run build
# 部署到Nginx (示例配置)
server {
listen 80;
server_name example.com;
location / {
root /var/www/client/dist;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# PM2管理Node进程
pm2 start server/app.js --name "myapp-backend"
六、模板扩展方向
- 多主题支持:通过CSS变量实现主题切换
- 国际化:集成vue-i18n支持多语言
- 微前端架构:使用qiankun拆分模块
- 低代码平台:集成可视化表单/流程设计器
- 数据分析:集成ECharts数据可视化
通过以上方案,您可以从零开始搭建一个功能完善的前后台管理系统模板。开发过程中注意保持代码模块化,便于后续扩展和维护。
更多推荐
所有评论(0)