Vue2企业级实战:电商后台管理系统开发全流程解析
一、项目架构设计
电商后台管理系统技术栈:
- Vue2:核心框架
- Vue CLI:项目脚手架
- Vuex:状态管理
- Element UI:UI组件库
- Axios:HTTP请求库
二、项目初始化与配置
1. 使用Vue CLI创建项目
npm install -g @vue/cli
vue create ecommerce-admin
cd ecommerce-admin
vue add element
npm install axios vuex vue-router --save
2. 目录结构优化
src/
├── api/ # 接口封装
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── common/ # 全局通用组件
│ └── business/ # 业务组件
├── directive/ # 自定义指令
├── filters/ # 全局过滤器
├── mixins/ # 混入
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── styles/ # 全局样式
├── utils/ # 工具函数
├── views/ # 页面组件
├── App.vue # 根组件
└── main.js # 入口文件
三、核心功能实现
1. 权限控制方案
// router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/login',
component: () => import('@/views/Login'),
meta: { requiresAuth: false }
},
{
path: '/',
component: () => import('@/layouts/MainLayout'),
meta: { requiresAuth: true },
children: [
// 动态路由将通过addRoutes添加
]
}
]
})
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.getters.token) {
next('/login')
} else if (to.path === '/login' && store.getters.token) {
next('/')
} else {
// 动态添加路由
if (store.getters.menus.length === 0 && store.getters.token) {
store.dispatch('user/getUserInfo').then(() => {
const menus = store.getters.menus
const routes = generateRoutes(menus)
router.addRoutes(routes)
next({ ...to, replace: true })
}).catch(() => {
next('/login')
})
} else {
next()
}
}
})
function generateRoutes(menus) {
// 根据菜单数据生成路由配置
// ...
}
export default router
2. Vuex状态管理
// store/modules/user.js
const state = {
token: localStorage.getItem('token') || '',
userInfo: null,
menus: []
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
localStorage.setItem('token', token)
},
SET_USERINFO: (state, userInfo) => {
state.userInfo = userInfo
},
SET_MENUS: (state, menus) => {
state.menus = menus
}
}
const actions = {
login({ commit }, userInfo) {
return new Promise((resolve, reject) => {
login(userInfo).then(response => {
commit('SET_TOKEN', response.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
getUserInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(response => {
commit('SET_USERINFO', response.user)
commit('SET_MENUS', response.menus)
resolve(response)
}).catch(error => {
reject(error)
})
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
四、高级组件开发
1. 可编辑表格组件
<template>
<el-table :data="tableData" border>
<el-table-column
v-for="col in columns"
:key="col.prop"
:prop="col.prop"
:label="col.label"
:width="col.width">
<template slot-scope="scope">
<template v-if="scope.row.isEdit">
<el-input v-model="scope.row[col.prop]" />
</template>
<template v-else>
{{ scope.row[col.prop] }}
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button
v-if="!scope.row.isEdit"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
v-else
type="success"
@click="handleSave(scope.row)">
保存
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
props: {
data: Array,
columns: Array
},
data() {
return {
tableData: JSON.parse(JSON.stringify(this.data))
}
},
methods: {
handleEdit(row) {
this.$set(row, 'isEdit', true)
},
handleSave(row) {
this.$set(row, 'isEdit', false)
this.$emit('save', row)
}
}
}
</script>
2. 图片上传组件
<template>
<div class="upload-container">
<el-upload
:action="uploadUrl"
:headers="headers"
:multiple="false"
:limit="1"
:file-list="fileList"
:on-success="handleSuccess"
:before-upload="beforeUpload">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过2MB
</div>
</el-upload>
<img v-if="imageUrl" :src="imageUrl" class="preview" />
</div>
</template>
<script>
export default {
props: {
value: String
},
data() {
return {
uploadUrl: process.env.VUE_APP_BASE_API + '/upload',
headers: {
Authorization: 'Bearer ' + this.$store.getters.token
},
fileList: [],
imageUrl: this.value
}
},
methods: {
beforeUpload(file) {
const isImage = file.type.includes('image/')
const isLt2M = file.size / 1024 / 1024 < 2
if (!isImage) {
this.$message.error('只能上传图片格式!')
}
if (!isLt2M) {
this.$message.error('图片大小不能超过2MB!')
}
return isImage && isLt2M
},
handleSuccess(response) {
this.imageUrl = response.data.url
this.$emit('input', this.imageUrl)
}
},
watch: {
value(val) {
this.imageUrl = val
}
}
}
</script>
五、性能优化实践
1. 路由懒加载
// router/index.js
const UserManagement = () => import(/* webpackChunkName: "user" */ '@/views/system/UserManagement')
const RoleManagement = () => import(/* webpackChunkName: "role" */ '@/views/system/RoleManagement')
2. 组件按需加载
// 安装babel插件
npm install babel-plugin-component -D
// babel.config.js
module.exports = {
plugins: [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
3. 长列表优化
<template>
<virtual-list
:size="60"
:remain="8"
:data="listData">
<template v-slot:default="{ item }">
<div class="list-item">
{{ item.name }}
</div>
</template>
</virtual-list>
</template>
<script>
import VirtualList from 'vue-virtual-scroll-list'
export default {
components: { VirtualList },
data() {
return {
listData: [] // 大数据列表
}
}
}
</script>