Vue后台管理系统

Vue后台管理系统

文章目录


前言

一、项目概述

1.1 电商项目基本业务概述

Vue后台管理系统

1.2 电商后台管理系统的功能

Vue后台管理系统

1.3 电商后台管理系统的开发模式(前后端分离)

Vue后台管理系统

二、项目初始化

2.1 通过 Vue 脚手架创建项目

2.1.1 配置 Vue 路由 插件要选择Router

2.1.2 配置 Element-UI 组件库 import方式需要选择import on demand

2.1.3 配置 axios 库 添加axios依赖

Vue后台管理系统

2.1.4 初始化 git 远程仓库需要在github或gitee创建远程仓库

2.1.5 将本地项目托管到 Github 或 码云 中

1、在项目的根目录打开PowerShell

2、git add .

3、git commit -m "说明"

4、按照新建仓库给的连接远程仓库和提交命令继续操作,与远程仓库建立联系

Vue后台管理系统

2.1.6 梳理项目结构

格式化App.vue

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

格式化 router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [

]

const router = new VueRouter({
  routes
})

/*------------实现导航守卫控制页面访问权限----------------*/
/** 
	param: to     将要访问到的路径
	param: from   代表从哪个路径跳转而来
	param:next   代表一个函数,表示放行 
*/
router.beforeEach((to, from, next) => {
  if(to.path === '/login') return next();
  const tokenStr = sessionStorage.getItem("token");
  
  // 判断token是否存在,如果不存在则代表未登录,则强制路由到登录页面
  if(!tokenStr) return next('./login');
  next();
})

格式化 main.js

/*------------------引入element-ui和其他样式--------------------*/ 
import './plugins/element.js'
import './assets/css/global.css'
import './assets/fonts/iconfont.css'

/*------------------封装axios和加载进度条--------------------*/ 
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import axios from 'axios'
Vue.prototype.$http = axios
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'

/*------------------通过axios拦截器添加token验证--------------------*/ 
// 文档通常会说明后台会对操作数据的api进行根据token令牌授权的操作
// 根据实际的文档常常需要在请求头添加内容是token令牌的字段 Authorization
axios.interceptors.request.use(config => {
  NProgress.start();
  // 添加请求头
  config.headers.Authorization = window.sessionStorage.getItem('token');
  return config
})
axios.interceptors.response.use(config => {
  NProgress.done();
  return config
})

/*------------------定义过滤器--------------------*/ 
Vue.filter('dateFormat', function(date) {
  const dateObj = new Date(date);
  const y = dateObj.getFullYear();
  const m = (dateObj.getMonth() + 1 + '').padStart(2, '0');
  const d = (dateObj.getDay() + '').padStart(2, '0');
  const hh = (dateObj.getHours() + '').padStart(2, '0');
  const MM = (dateObj.getMinutes()+ '').padStart(2, '0');
  const ss = (dateObj.getSeconds() + '').padStart(2, '0');
  return `${y}-${m}-${d} ${hh}:${MM}:${ss}`;
})

格式化 element.js

import Vue from 'vue'
import { 
	Button,
    Message, 
    MessageBox 
} from 'element-ui'
Vue.prototype.$message = Message

// 实现基于element-ui的message全局弹框组的配置
Vue.prototype.$confirm = MessageBox.confirm

2.2 后台项目的环境安装配置

2.2.1 导入数据库

1、停止本地mysql80服务

2、启动phpstudy,开启mysql的服务

Vue后台管理系统

3、先创建数据库,再导入sql脚本

Vue后台管理系统

2.2.2 修改项目的config文件修改用户和密码为自己刚刚创建的数据库的用户和密码

Vue后台管理系统

2.2.3 在项目根目录打开powershell运行node ./app.js

2.2.4 使用postman看看接口是否跑起来了,如果有数据返回则服务器运行成功

Vue后台管理系统



三、实现功能业务的一般流程

3.1 开始新业务开发,首先创建并切换到新分支 git checkout -b 新分支名

3.2 新建组件并初始化结构 template、script、style

3.3 在router/index.js创建并引入路由 {path: '路径' ,component: 组件名}

3.4 实现页面ui结构 通过原生标签和element-ui配合组成页面结构


3.5 定义数据和自封装函数来实现业务功能

3.5.1 加载页面的时候就触发获取列表数据的事件,所以应该把该事件的触发放到created周期

3.5.2 当在html页面上需要用到某些数据的时候先到data里面找,如果没有则用作用域插槽的scope.row来获取当前表格行的数据

Vue后台管理系统

3.6 将本地代码提交到码云上

3.6.1 把文件提交到暂存区 git add .

Vue后台管理系统

提交到仓库 git commit -m "说明"

Vue后台管理系统

提交到远程仓库的功能分支 git push -u origin 分支名

Vue后台管理系统

切换为主分支并合并分支 git checkout master、git merge 分支名

Vue后台管理系统

提交到远程仓库 git push

Vue后台管理系统



四、登录/退出功能

3.1 登录业务流程

Vue后台管理系统

3.2 技术选择

3.2.1 方案1:cookie和session记录登录状态

3.2.2 方案2:token 维持登录状态

Vue后台管理系统

方案 使用场景
cookie、session 服务器和客户端不跨域
token 服务器和客户端跨域,常用

Vue后台管理系统

3.3 登录页面的实现过程

3.3.1 表单的数据绑定

Vue后台管理系统

3.3.2 表单的数据校验

Vue后台管理系统

3.3.3 表单重置

 methods: {
    resetLoginForm: function() {
        // this指向当前组件实例,refs是存有含有ref属性的组件的对象,resetFields()方法是重置所有的表单项
        this.$refs.loginFormRef.resetFields()
    }
},

Vue后台管理系统

3.3.4 表单提交前先通过validate函数预校验再提交,登录后需要有路由指向

login: function() {
    this.$refs.loginFormRef.validate(async valid => {
    	// 1、预校验
        if(!valid) return;
        const { data: res } = await this.$http.post("login",this.loginForm);
        if(res.meta.status != 200) return this.$message.error('登陆失败!')
        this.$message.success('登陆成功!')
       
        // 2、将token保存到客户端的sessionStorage中
        //  2.1 项目中除了登录之后的其他API接口,必须在登录之后才能访问
        //  2.2 token只应在当前网站打开期间生效,所以将token保存在sessionStorage中
        window.sessionStorage.setItem("token", res.data.token);
        
        // 3、通过编程式导航到后台主页,路由地址是 /home
        this.$router.push("/home");
    })
}


四、主页功能

4.1 主页布局

Vue后台管理系统

4.2 登录后退出功能

	// 在已登录放行的页面定义
    methods: {
        exit: function() {
        	// 移除token并跳转到登陆页面
            window.sessionStorage.removeItem("token");
            this.$router.push('/login');
        }
    }

4.3 v-for循环渲染菜单列表

Vue后台管理系统

4.4 给其他功能组件提供占位

Vue后台管理系统



五、其他业务功能的布局和功能实现

5.1 基于element-ui绘制用户列表的组件

5.3.1 页内导航 BreadCrumb

5.3.2 主要内容面板 Card

5.3.3 栅格系统 col、row

5.3.4 数据表格 el-table

5.3.5 分页导航 el-pagination


5.2 基于el-table渲染数据列表

5.2.1 获取用户信息列表

5.2.2 渲染数据列表

5.2.3 实现分页展示用户数据


5.3 实现增删改查功能

5.3.1 添加功能常需要弹出对话框 el-dialog

1、对话框的显隐标志visible需要用v-bind绑定而不是v-model v-bind:visible="..."

2、对话框要添加关闭事件@close="关闭事件名",不然点击关闭按钮会无反应

3、关闭对话框的同时需要清空表单,该操作最好添加在关闭事件上 this.$refs.表单实例对象.resetFields();

4、对话框内容常常是表单,需要遵守表单的规则


5.3.2 删除功能常需要弹出消息框 基于messageBox的confirm

方法名() {
	this.$confirm('此操作将永久删除..., 是否继续?', '消息框标题', {
	  confirmButtonText: '确定',
	  cancelButtonText: '取消',
	  type: 'warning'
	}).then(() => {
	  // 在这里发送删除请求
	  this.$message({
	    type: 'success',
	    message: '删除成功!'
	  });
	}).catch(() => {
	  this.$message({
	    type: 'info',
	    message: '已取消删除'
	  });
	});
}

5.3.3 修改功能也常需要弹出对话框 el-dialog

5.3.4 搜索功能 设计接口的时候把搜索所需要的信息设计为可空



六、引入的功能组件

6.1 树形表格 vue-table-with-tree-grid

6.1.1 安装 npm i vue-table-with-tree-grid -S

6.1.2 引入

import ZkTable from 'vue-table-with-tree-grid'
Vue.use(ZkTable)
Vue.component('tree-table',ZkTable)

6.1.3 使用

https://github.com/connie1992/vue-table-with-tree-grid-icon
Vue后台管理系统

Vue后台管理系统

<!-- 数据列表 -->
<tree-table :data="cateList" :columns="columns" 
:selection-type="false" :expand-type="false" 
:show-index="true" index-text="#" :border="true" 
:stripe="true" :show-row-hover="false" class="treetable">
    <!-- 是否有效列 -->
    <template slot="isok" slot-scope="scope">
        <i class="el-icon-success iconSuccess" v-if="scope.row.cat_deleted == false"></i>
        <i class="el-icon-error iconError" v-else></i>
    </template>

    <!-- 排序列 -->
    <template slot="sort" slot-scope="scope">
        <el-tag v-if="scope.row.cat_level == 0" size="mini">一级</el-tag>
        <el-tag type="success" v-else-if="scope.row.cat_level == 1" size="mini">二级</el-tag>
        <el-tag type="warning" v-else size="mini">三级</el-tag>
    </template>

    <!-- 操作列 -->
    <template slot="action" >
        <el-button type="primary" icon="el-icon-edit" size="mini">编辑</el-button>
        <el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
    </template>
</tree-table>
columns: [
    {
        label: '分类名称',
        prop: 'cat_name'
    },
    {
        label: "是否有效",
        // 将当前列作为模板列
        type: 'template',
        template: 'isok'
    },
    {
        label: '排序',
        type: 'template',
        template: 'sort'
    },
    {
        label: '操作',
        type: 'template',
        template: 'action'                    
    }
],

6.2 富文本编辑器 vue-quill-editor

6.2.1 下载 npm install vue-quill-editor -S

6.2.2 引入

import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
Vue.use(VueQuillEditor)

6.2.3 使用

<quill-editor v-model="addForm.goods_introduce"/>

6.3 第三方可视化库 Echarts

https://echarts.apache.org/zh/index.html

6.2.1 下载 npm install echarts -S

6.2.2 引入

import * as echarts from 'echarts';

6.2.3 使用

<div id="main" style="width: 750px;height:400px;"></div>
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 绘制图表
myChart.setOption({
    title: {
        text: 'ECharts 入门示例'
    },
    tooltip: {},
    xAxis: {
        data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
    },
    yAxis: {},
    series: [{
        name: '销量',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]
    }]
});

6.4 第三方工具库,主要降低 array、number、objects、string 等等的使用难度 Lodash

https://www.lodashjs.com/

6.2.1 下载 npm i --save lodash

6.2.2 引入

import _ from 'lodash'

6.2.3 使用

<div id="main" style="width: 750px;height:400px;"></div>
// 深拷贝
const form =  _.cloneDeep(this.addForm);

// 合并对象
const result =  _.merge(res.data, this.options)

七、项目优化

7.1 项目优化策略

7.1.1 生成打包报告

1、在可视化的UI面板中,通过控制台和分析面板,可以方便地看到项目中所存在的问题

Vue后台管理系统

2、清除所有的console.log语句

下载包 npm install babel-plugin-transform-remove-console --save-dev或安装开发依赖
/*----------------------------------babel.comfig.js---------------------------------------*/
// 这是项目发布阶段才用的babel插件
const prodPlugins = []
if(process.env.NODE_ENV === 'production') {
  // 只在发布阶段清除console语句
  prodPlugins.push('transform-remove-console')
}
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [    
    // 发布产品时候的插件数组
    ...prodPlugins,
  ]
}

3、为开发模式与发布模式指定不同的打包入口

开发模式的入口文件为 src/main-dev.js
发布模式的入口文件为 src/main-prod.js
vue.config.js 导出的配置对象中,新增 configureWebpack 或 chainWebpack 节点,来自定义 webpack 的打包配置
① chainWebpack 通过链式编程的形式,来修改默认的 webpack 配置
② configureWebpack 通过操作对象的形式,来修改默认的 webpack 配置
module.exports = {
	// 使用chainWebpack节点
    chainWebpack: config => {
        config.when(process.env.NODE_ENV === 'production',config => {
        	// 获取app的入口文件对象,先清空再添加路径
            config.entry('app').clear().add('./src/main-prod.js')
        })
        config.when(process.env.NODE_ENV === 'development',config => {
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}

7.1.2 第三方库启用 CDN

通过 import 语法导入的第三方依赖包,最终会被打包合并到同一个文件中,从而导致打包成功后,单文件体积过大的问题

module.exports = {
	// 使用chainWebpack节点
    chainWebpack: config => {
        config.when(process.env.NODE_ENV === 'production',config => {
        	// 获取app的入口文件对象,先清空再添加路径
            config.entry('app').clear().add('./src/main-prod.js')

			// 通过 webpack 的 externals 节点,来配置并加载外部的 CDN 资源。凡是声明在externals 中的第三方依赖包,都不会被打包
			config.set('externals', {
                vue: 'Vue', 
                axios: 'axios',
                lodash: '_',
                echarts: 'echarts',
                nprogress: 'NProgress', 'vue-quill-editor': 'VueQuillEditor'
            })
        })
        config.when(process.env.NODE_ENV === 'development',config => {
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}

在main-prod.js中删除上述externals中的第三方包的css文件 删除红框部分

Vue后台管理系统

在public/index.html中引入样式和js文件

    <link rel="stylesheet" href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" />
    <!-- 富文本编辑器 的样式表文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.core.min.css" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.snow.min.css" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.bubble.min.css" />
 	<script src="https://cdn.staticfile.org/vue/2.5.22/vue.min.js"></script>
    <script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
    <script src="https://cdn.staticfile.org/lodash.js/4.17.11/lodash.min.js"></script>
    <script src="https://cdn.staticfile.org/echarts/4.1.0/echarts.min.js"></script>
    <script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
    <!-- 富文本编辑器的 js 文件 -->
    <script src="https://cdn.staticfile.org/quill/1.3.4/quill.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-quill-editor@3.0.4/dist/vue-quill-editor.js"></script>

7.1.3 Element-UI 组件按需加载

注释掉引入elementui的语句

Vue后台管理系统

在public/index.html中引入样式和js文件

<!-- element-ui 的样式表文件 --> 
<link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.15.1/theme-chalk/index.css" />

<!-- element-ui 的 js 文件 -->
<script src="https://cdn.staticfile.org/element-ui/2.15.1/index.js"></script>

7.1.4 首页内容定制

不同的打包环境下,首页内容可能会有所不同。我们可以通过插件的方式在进行定制

chainWebpack: config => {
	 config.when(process.env.NODE_ENV === 'production', config => {
		 config.plugin('html').tap(args => {
			 args[0].isProd = true
			 return args
		 })
	 })
	 config.when(process.env.NODE_ENV === 'development', config => {
		 config.plugin('html').tap(args => {
			 args[0].isProd = false
			 return args
		 })
	 })
}

根据 isProd 的值,来决定如何渲染页面结构

<!– 按需渲染页面的标题 -->
<title><%= htmlWebpackPlugin.options.isProd ? '' : 'dev - ' %>电商后台管理系统</title>
<!– 按需加载外部的 CDN 资源 -->
<% if(htmlWebpackPlugin.options.isProd) { %>
<!—通过 externals 加载的外部 CDN 资源-->
<% } %>

7.1.5 路由懒加载

安装开发依赖 @babel/plugin-syntax-dynamic-import

在babel.config.js 配置文件中声明该插件

Vue后台管理系统

将路由改为按需加载的形式

const Login = () => import(/* webpackChunkName: "login_home_welcome" */ '../components/Login.vue')
const Home = () => import(/* webpackChunkName: "login_home_welcome" */ '../components/Home.vue')
const Welcome = () => import(/* webpackChunkName: "login_home_welcome" */ '../components/Welcome.vue')
// import Login from '../components/Login.vue'
// import Home from '../components/Home.vue'
// import Welcome from '../components/Welcome.vue'

const Users = () => import(/* webpackChunkName: "Users_Rights_Roles" */ '../components/Users.vue')
const Rights  = () => import(/* webpackChunkName: "Users_Rights_Roles" */ '../components/power/Rights.vue')
const Roles = () => import(/* webpackChunkName: "Users_Rights_Roles" */ '../components/power/Roles.vue')
// import Users from '../components/Users.vue'
// import Rights from '../components/power/Rights.vue'
// import Roles from '../components/power/Roles.vue'


const Cate = () => import(/* webpackChunkName: "Cates_Params_List_Add" */ '../components/goods/Cate.vue')
const Params = () => import(/* webpackChunkName: "Cates_Params_List_Add" */ '../components/goods/Params.vue')
const List = () => import(/* webpackChunkName: "Cates_Params_List_Add" */ '../components/goods/List.vue')
const Add = () => import(/* webpackChunkName: "Cates_Params_List_Add" */ '../components/goods/Add.vue')
// import Cate from '../components/goods/Cate.vue'
// import Params from '../components/goods/Params.vue'
// import List from '../components/goods/List.vue'
// import Add from '../components/goods/Add.vue'

const Order = () => import(/* webpackChunkName: "Order_Report" */ '../components/order/order.vue')
const Report = () => import(/* webpackChunkName: "Order_Report" */ '../components/report/Report.vue')
// import Order from '../components/order/order.vue'
// import Report from '../components/report/Report.vue'

总结

上一篇:vue组件


下一篇:vue 自定义attribute继承