电商后台管理系统
电商后台管理系统
项目源码:码云
微信号:RanY_Luck
1、前端项目的技术栈
- vue
- vue-router
- Element-UI
- Axios
- Echarts
2、后端项目技术栈
- Node.js
- Express
- Jwt
- Mysql
- sequelize
项目前端初始化
- 安装Vue脚手架
- 通过Vue脚手架创建项目
- 配置Vue路由
- 配置Element—UI组件库
- 配置Axios库
- 初始化git远程仓库
- 将本地项目托管到github或码云中
1、安装Vue脚手架
npm install -g @vue/cli
2 、通过Vue脚手架创建项目(通过可视化创建)
vue ui
3、安装Element-UI
4、配置Axios库
5、配置less、less-loader开发依赖
项目所有依赖
启动服务并启动app
你会看到这样的一个画面
5、将本地项目托管到码云中
测试一下:
项目后端初始化
1、安装MySql数据库
2、安装Node.js环境
3、配置项目相关信息
4、启动项目
node app.js
5、使用Postman测试后台项目接口是否正常
登录/退出功能
原理
登录功能代码实现
通过Element-UI组件实现布局
- el-form
- el-form-item
- el-input
- el-button
- 字体图标
代码解读
main.js
删除views
清空页面
<template>
<div id="app">
</div>
</template>
<script>
// 导入helloword
export default {
name: 'app'
}
</script>
<!-- style样式 -->
<style>
</style>
创建一个comments目录
创建Login.vue文件
<template>
<div class="login_container">
<!-- 登录主体边框 -->
<div class="login_box">
<!-- 头像区域 -->
<div class="avater_box">
<img src="../assets/logo.png" alt="头像logo">
</div>
<!-- 登录表单区域 -->
<el-form :model="loginForm" :rules="rules" ref="loginFormRef" label-width="0px" class="login_form">
<!-- 用户名 -->
<el-form-item prop="username">
<el-input v-model="loginForm.username" placeholder="请输入账号" prefix-icon="iconfont icon-RectangleCopy"></el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item prop="password">
<el-input v-model="loginForm.password" placeholder="请输入密码" prefix-icon="iconfont icon-RectangleCopy1" type="password"></el-input>
</el-form-item>
<!-- 按钮区域 -->
<el-form-item class="btns">
<el-button type="primary" @click="login">登录</el-button>
<el-button type="info" @click="resetLoginForm('loginForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data () {
return {
// 这是登录表单的数据绑定对象
loginForm: {
username: 'admin',
password: '123456'
},
// 这是表单的验证规则对象
rules: {
// 验证用户名是否合法
username: [
{ required: true, message: '请输入登录账号', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }
],
// 验证密码是否合法
password: [
{ required: true, message: '请输入登录密码', trigger: 'blur' },
{ min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }
]
}
}
},
methods: {
// 点击重置按钮,重置登录表单
resetLoginForm () {
// 前提是先获取ref节点
this.$refs.loginFormRef.resetFields()
},
login () {
// 表单之前的预验证
this.$refs.loginFormRef.validate(async valid => {
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('登录成功');
// 1. 将登陆成功之后拿到后端返回的token值,保存到客服端也就是临时浏览器sessionStorage中
// 1.1 项目中出了登录之外的其他api接口,必须在登录之后才能访问
// 1.2 token只应在当前网站打开期间生效,所以将token保存在sessionStorage中
const data = res.data.token;
window.sessionStorage.setItem('token', data);
// 2. 通过编程式导航跳转到后台主页,路由地址是 /home
this.$router.push('/home');
})
}
}
}
</script>
<style lang="less" scoped="">
.login_container {
background: #2b4b6b;
height: 100%;
}
.login_box {
width: 450px;
height: 300px;
background-color: #fff;
border-radius: 3px;
position: absolute; // 绝对定位
left: 50%;
top: 50%;
transform: translate(-50%, -50%); // 在横轴上进行位移-50%,纵轴上位移-50%
// 语法嵌套
.avater_box {
height: 130px;
width: 130px;
border: 1px solid #eee; // 边框
border-radius: 50%; // 添加圆角边框
padding: 10px; // 内边距
box-shadow: 0 0 10px #ddd; // 添加阴影
position: absolute; // 绝对定位
left: 50%;
transform: translate(-50%, -50%); // 在横轴上进行位移-50%,纵轴上位移-50%
background-color: #fff;
img {
width: 100%;
height: 100%;
border-radius: 50%; // 添加圆角边框
background-color: #eee;
}
}
}
.btns {
display: flex; // 弹性布局
justify-content: flex-end; // 位于容器的结尾
}
.login_form {
position: absolute; // 绝对定位
bottom: 0; // 底部对齐
width: 100%;
padding: 0 20px;
box-sizing: border-box; // 将元素限制到宽度里面
}
</style>
<div id="app">
<!-- 路由占位符 -->
<router-view></router-view>
</div>
<script>
// 导入helloword
export default {
name: 'app'
}
</script>
<!-- style样式 -->
<style>
</style>
访问页面
现在需要让用户访问根目录就自动跳转到login页面
tips:
post返回值是一个Promise 对象,为了简化Promise操作,我们可以用async await 来优化
await 只能用在async的方法中,所以需要把vaild修饰成异步,这样就能直接拿到服务器返回的数据{ data: res }中的data 才是服务器的真实数据,然后重命名为res对象,然后在根据res的meta属性来判断是否为200,为200就登录成功并打印,不为200就失败并打印。
进行登录跳转
第一步:你需要进行组件导入
进行全局导入后,以后用到message就可以直接用this.$message.xxx()
第二步:代码使用
路由导航守卫控制访问权限
// 为路由对象,添加 beforeEach 导航守卫
router.beforeEach((to, form, next) =>{
// 如果用户访问的登录页,直接放行
if(to.path === '/login') return next()
// 从 sessionStorage 中获取保存的token值
const tokenStr = window.sessionStorage.getItem('token')
// 没有token,强制跳转到登录页
if(!tokenStr) return next('/login')
next()
})
判断用户在没有token了自动跳转到登录界面
需要进行router.js改写
退出功能
退出功能实现原理
基于token的方式实现退出比较简单,只需要销毁本地的token即可。这样,后续的请求就不会携带token,必须重新登录生成一个新的token之后才可以访问
// 清空token
window.sessionStorage.clear();
// 跳转到登录页
this.$router.push('/login')
主页布局
<el-container>
// 头部区域
<el-header></el-header>
<el-container>
// 侧边栏区域
<el-asider></el-asider>
// 右侧主体区域
<el-main></el-main>
</el-container>
</el-container>
左侧菜单布局
<el-menus>
<el-submenu>
// 这个template 是一级菜单的内容模版
<i class="el-icon-menu"></i>
<span>一级菜单</span>
// 在一级菜单中,可以嵌套二级菜单
<el-menu-item>
<i class="el-icon-menu"></i>
<span slot="title">二级菜单</span>
</el-menu-item>
</el-submenu>
</el-menus>
<!-- 侧边栏 -->
<el-aside width="200px">
<!-- 侧边栏菜单区 -->
<el-menu background-color="#333744" text-color="#fff" active-text-color="#ffd04b">
<!-- 一级菜单 -->
<el-submenu index="1">
<!-- 一级菜单的模板区 -->
<template slot="title">
<!-- 图标 -->
<i class="el-icon-location"></i>
<!-- 文本 -->
<span>一级菜单</span>
</template>
<!-- 二级菜单 -->
<el-menu-item index="1-4-1">
<!-- 图标 -->
<i class="el-icon-location"></i>
<!-- 文本 -->
<span>二级菜单</span>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
需要element.js进行注册
通过接口获取菜单数据
通过axios请求拦截器添加token,保证拥有获取数据的权限
// axios请求拦截(预处理)
axios.interceptors.request.use(config =>{
// 为请求头对象添加Token验证的Authorization字段
config.headers.Authorization = window.sessionStorage.getItem('token')
return config
})
对接口请求,获取详细信息:
就可以看到已经获取到数据,所有的一级菜单都放在了data数据中,二级菜单就嵌到了children中
拿到了数据 我们就需要渲染到页面中
第一步:需要在一级菜单进行v-for循环
第二步:需要在一级菜单的基础上对二级菜单再进行v-for循环
代码:
不过可以看到一级菜单的图标都为统一不是很好看,我们需要整改下
第一步:先自定义iconsObj
第二步:在一级菜单的图标中进行v-bind双向绑定
效果:
fix展开后对不齐的问题,先看效果:
经过分析:
于是可以在css选择器中添加个border-right: none;就可以了
fix可以将一级菜单全部展开,先看效果:
经过分析:element-ui是默认展开的,不过也提供了只保持一个子菜单展开的api
水平折叠收起菜单
经过分析:element-ui是默认不折叠的,不过也提供了只保持一个子菜单展开的api
第一步:在el-menu新增
第二步:自定义一个div,并赋予click事件
第三步:在data()定义初始值
第四步:动态赋值侧边栏宽度
路由重定向
第一步:创建个Welcome.vue文件
第二步:在router里面导入Welcome.vue
第三步:在Home.vue中渲染出Welcome.vue的内容
侧边栏路由跳转
步骤一:
步骤二:
效果:
用户列表的开发
第一步:在comments里新建一个user的文件夹,创一个Users.vue的文件
第二步:在router里面引用
效果:
fix点击二级菜单不会高亮,刷新后又重置
步骤一:在侧边栏增加default-active语法
第二步:我们需要动态渲染路径
组件创建了需要执行created生命周期函数
在点击不同连接的时候,我们需要给activePath重新赋值
这样就不会点了其他高亮然后再点回来不高亮啦(可能有点绕)
用户列表
增加面包屑
步骤一:element-ui给出了面包屑的方案
我们只需要复制过来
步骤二:需要在element.js中进行按需引入
效果:
增加Card卡片
步骤一:element-ui给出了卡片的方案
我们只需要复制过来删除不必要的东西
增加Layout布局之分栏间隔
步骤一:element-ui给出了卡片的方案
我们只需要复制过来删除不必要的东西
效果:
tips:
需要对card做样式修改。可以在global.css中做全局设定,当然也可以自建class进行修改样式
增加获取用户列表数据
第一步:查看接口文档
第二步:将接口请求参数定义到data中
第三步:进行axios请求
第四步:定义空列表
第五步:赋值数据到空列表中
增加渲染用户列表数据
快速入门Table表格(element-ui提供了方法)
第一步:
第二步:copy代码
第三步:重置样式
效果:
发现序号id,值非常大,不便于查看,我们需要自己创建一个索引,特别简单
效果:
增加状态开关
快速入手Switch开关(element-ui提供了方法)
步骤一:作用域插槽
步骤二:将v-model绑定到作用域的mg_state
效果:
fix更改后刷新重置,没有更改数据库值
步骤一:element-ui给了一个解决方法api
步骤二:修改用户状态接口(put)方法
步骤三:监听switch开关状态的改变
接口地址分析:
效果:
增加操作按钮
快速入手Tooltip 文字提示(element-ui提供了方法)
步骤一:
步骤二:在element.js按需导入
效果:
增加分页模块
快速入手Pagination分页(element-ui提供了方法)
步骤一:
事件名称 | 说明 |
---|---|
size-change | pageSize 改变时会触发 |
current-change | currentPage 改变时会触发 |
current-page | 当前页数 |
page-sizes | 每页显示个数选择器的下拉 |
page-size | 每页显示条目个数 |
layout | 组件布局,子组件名需要用逗号隔开 |
total | 总条目数 |
步骤二:传参
第三步:在element.js按需导入
第四步:重置样式(global.css)
效果:
增加搜索/清空功能
效果:
增加添加用户对话框
快速入手Dialog对话框(element-ui提供了方法)
第二步:进入element.js按需引入
第三步:初始化对话框为隐藏
第四步:在添加用户这个按钮上给一个click事件,并赋值为true
效果:
完善添加用户表单
步骤一:
model数据绑定对象 rules验证规则对象 ref引用对象 label-width确定了文本所占宽度
步骤二:添加表单用户的表单数据
步骤三:添加表单的验证规则对象
效果:
新增邮箱、手机校验规则
快速入手Form表单自定义校验规则(element-ui提供了方法)
步骤一:查看官方文档示例
因为邮箱/手机这种校验规则比较复杂,这里推荐大家下个插件
步骤二:增加校验规则
效果:
fix填写完表单后重新打开未重置效果
第一步:element-ui给出了Form表单重置方案
第二步:写一个close的关闭事件
第三步:
效果:
添加用户
请求预校验
步骤一:当点击确定时,我们需要预加载数据看是否符合规则
效果:
请求添加一个新用户
第一步:查看接口文档
第二步:发起axios请求
效果:
操作之编辑
增加修改用户对话框
第一步:使用Dialog对话框(element-ui给出了例子)
第二步:在编辑按钮上增加click事件
第三步:增加编辑对话框
第四步:methods增加显示为true
第五步:控制修改用户对话框显示与隐藏
效果:
增加根据用户id查询用户信息
第一步:使用scope.row.id获取到用户id
第二步:查看接口文档
第三步:进行接口文档的调用
第四步:在data中创建editForm
第五步:res的数据保存到第四步中
效果:点击编辑按钮后将id传入到api接口中
增加编辑表单
第一步:制作表单
第二步:定义验证规则
效果:
fix验证不通过存留状态
第一步:添加一个click事件
第二步:监听修改用户关闭对话框
增加提交修改预校验表单
第一步:在提交按钮上增加一个click事件
第二步:进行预校验
效果:
增加修改用户信息操作
步骤一:看后端接口
步骤二:在确定按钮上添加click事件
效果:
增加删除用户信息操作
第一步:导入MessageBox 弹框(element-ui给出了方案)
第二步:在删除按钮上增加一个click(id)事件
第三步:根据id删除对应用户信息(参考elementui给出的方案)
效果:
第四步:使用接口文档进行删除用户信息
第五步:调用接口
效果:
增加分配角色操作
第一步:导入Dialog对话框 弹框(element-ui给出了方案)
第二步:在分配角色按钮新增click事件
第三步:在data中定义对话框初始化为隐藏
第四步:点击角色分配按钮就进行展示为true
效果:
增加分配角色操作之下拉选择职位
第一步:在data创建接收对象
第二步:查看接口文档
第三步:接口请求
第四步:导入Select选择器(element-ui给出了方案)
第五步:赋予一个接收选中对象的值
第六步:进行组件注册
效果:
新增选择职位后点击确定应用
第一步:在确定按钮增加click事件
第二步:查看接口文档
第三步:接口调用
效果:
fix第二次选择进行重置
第一步:给分配角色一个close事件
第二步:重置列表
效果:
权限列表
增加权限列表路由
第一步:写Rights.vue
第二步:在路由引入步骤一
注意:因为在home里面继续跳转,所以权限列表为home的子路由
效果:
权限列表的基本布局
第一步:使用面包屑
第二步:使用card卡片
效果:
权限列表的数据获取
第一步:创建一个权限列表的接收对象
第二步:创建生命周期
第三步:查看接口文档
第四步:创建方法并声明
效果:
表格渲染
第一步:使用el-card
第二步:使用el-table表格
第三步:使用el-table-column 填入数据 label 定义列名 width定义列宽
第四步:使用el-tag标签
效果:
角色列表
增加角色列表路由
第一步:写Rights.vue
第二步:在路由引入步骤一
注意:因为在home里面继续跳转,所以权限列表为home的子路由
效果:
角色列表基本布局
第一步:使用面包屑
第二步:使用card卡片
角色列表的数据获取
第一步:创建一个权限列表的接收对象
第二步:创建生命周期
第三步:查看接口文档
第四步:创建方法并声明
效果:
表格渲染
第一步:使用el-card
第二步:使用el-row Layout布局
第三步:使用el-table/el-table-column 表格
效果:
新增下拉列表数据展示
第一步:使用插槽进行数据展示/v-for进行循环取出值
效果:
美化一级权限表格
第一步:增加样式
第二步:
效果:
美化二级权限表格
第一步:
效果:
美化三级权限表格
第一步:
效果:
总体效果:
美化权限表格
第一步:创建一个样式
第二步:将样式放到需要的位置
效果:
新增删除权限
第一步:使用可移除标签(element ui给出了方案)
第二步:点击移除标签是触发的事件
第三步:写二次弹框(element ui 给出了方案)
效果:
fix删除权限完整功能
第一步:查看接口文档
第二步:接口请求
效果:
新增分配权限获取所有权限数据
第一步:新增一个click事件
第二步:点击分配权限弹框(element ui 给出了方案)
第三步:默认分配权限对话框为隐藏
第四步:点击对话框就显示
效果:
第五步:查看接口文档
第六步:接口调用
效果:
新增分配权限树形组件
快速入门Tree树形控件(element-ui提供了方法)
第一步:
第二步:将控件树形树形控件属性绑定对象
第三步:进行组件注册
效果:
fix分配权限复选框/默认展开
第一步:在el-tree增加属性(element-ui给了方案)
效果:
获取角色原有权限
第一步:默认勾选节点key的数组(element ui给出了方案)
第二步:在data中定义一个接收对象
第三步:进行递归函数遍历权限接口
第四步:在分配权限增加click事件并拿到所有数据
第五步:将role进行传参,将获取到的id传入第三步defkeys中
效果:
fix点击不同角色分配权限造成权限id累积
第一步:新增close事件
第二步:监听分配权限对话框关闭事件,没关闭一次,就像defkeys赋值一个空数组
新增角色权限增加
第一步:新增一个click事件在确定按钮上
第二步:定义一个dom元素
第三步:选中权限点击确定
el-tree给了2个函数,getCheckedKeys 节点选择后返回节点的key所组成的数组
getHalfCheckedKeys 节点被选择,返回目前半选节点的key所组成的数组
tips:...为JS中展开运算符
let a={x:1,y:2};
let b={z:3};
let ab={...a,...b};
ab //{x:1,y:2,z:3}
效果:
fix勾选权限点击确定并应用
第一步:查看接口文档
第二步:进行接口请求
效果:
商品分类
增加商品分类骨架
第一步:需要重新创建一个分支取名为:goods_cate
第二步:在comments创建一个goods目录,创建Cate.vue
第三步:写基础骨架
// Cate.vue
<template>
<div>
商品分类组件
</div>
</template>
<script>
export default {
data() {
return {}
},
// 生命周期函数
created() {
},
// 所有的事件处理函数
methods: {
}
}
</script>
<style lang="less">
</style>
第四步:在路由引入
效果:
增加面包屑/按钮/卡片基础页面
第一步:引入面包屑
第二步:引入卡片视图
第三步:引入button按钮
效果:
获取商品分类后端数据
第一步:查看接口文档
第二步:创建data数据
第三步:创建生命周期
第四步:接口请求
效果:
首次使用Vue-table-with-tree插件
第一步:安装插件
进入到vue ui中依赖--安装依赖---vue-table-with-tree-grid
第二步:在main.js引入插件
第三步:运用插件
data:指定绑定数据;columns:表格各列的配置;selection-type:关闭复选框
expand-type:关闭展开项;show-index:增加索引;index-text:索引名称;border:显示纵向边框;show-row-hover:鼠标悬停是否高亮
第四步:录入数据
效果:
美化表格
第一步:增加自定义模版列渲染,表格数据,定义插槽
第二步:添加对应样式
效果:
新增排序/操作
第一步:增加自定义模版列渲染,表格数据,定义插槽
第二步:添加对应样式
效果:
新增分页区域
第一步:引入Pagination分页(element ui给出了方案)
size-change:监听pagesize事件;current-change:监听pagenum事件;current-page:当前页;page-sizes:显示下拉条数;page-size:默认显示条数;
total:总条数
第二步:在methods创建对应事件
效果:
新增添加分类弹出框
第一步:引入Dialog对话框(element ui给出了方案)
第二步:在data中初始化该对话框为隐藏
第三步:在添加分类button上增加一个click事件
第四步:在methods中处理事件
效果;
新增添加分类数据
第一步:引入Form表单(element ui给出了方案)
第二步:查看接口文档
第三步:添加分类相关数据
效果:
获取商品分类数据列表
第一步:查看接口文档
第二步:接口请求
第三步:在data中定义一个接收对象
第四步:在点击添加分类按钮时进行触发
效果:
新增父级分类下拉列表
第一步:引入Cascader级联选择器(element ui给出了方案)
步骤二:进行数据绑定
第三步:在勾选选项的时候触发日志打印
效果:
第四步:进行组注册
fix下拉数据过多导致显示不全
效果:
将分类名称数据绑定到父级分类中
第一步:进行数组length判断
第二步:给确定按钮增加一个click事件
第三步:日志打印
效果:
添加分类关闭清空已选数据
第一步:新增一个close事件
第二步:进行数据重置
效果:
新增点击确定添加新的分类
第一步:查看接口文档
第二步:调用接口
效果:
后续补写编辑/删除功能
分类参数
增加分类参数骨架
第一步:需要重新创建一个分支取名为:goods_params
第二步:在goods目录创建Params.vue
第三步:写基础骨架
<template>
<div>
分类参数
</div>
</template>
<script>
export default {
data() {
},
created() {
},
methods: {
}
}
</script>
<style lang="less" scoped="">
</style>
第四步:在路由引入
效果:
增加面包屑/警告框/卡片基础页面
第一步:引入面包屑
第二步:引入卡片视图
第三步:引入alert组件(element ui给出了方案)
第四步:进行组件注册
第五步:增加商品分类
效果:
商品分类数据获取
第一步:查看接口文档
第二步:调用接口
效果:
商品分类的级联选择框
第一步:引入Cascader级联选择器(element ui给出了方案)
第二步:在data中定义数据
第三步:
效果:
fix不在同一水平面上展示
在样式上添加属性:
效果:
控制级联选择器的选择范围
第一步:获取selectedCateKeys的长度后进行判断
效果:
新增动态参数/静态属性 Tabs 标签页
第一步:引入Tabs标签页(element ui给出了方案)
第二步:在data中加入activeName参数
第三步:进行tabs页签点击事件处理函数
第五步:注册组件
效果:
新增添加按钮和属性/并控制禁用
第一步:新增button按钮
第二步:新增一个计算属性(computed)
效果:
获取参数列表
第一步:查看接口文档
第二步:在computed创建一个函数获取到三级分类的id
第三步:接口调用
第四步:更改tabs标签页中的name
第五步:更改被激活的页签名称
效果:
fix切换标签没有重新进行请求
第一步:将handleChange中的数据抽离出来,创建一个getParamsData函数
第二步:单独添加该函数
效果:
动态参数表格和静态参数表格渲染
第一步:在data中定义空数组
第二步:进行判断数据属于哪个标签
第三步:进行表格渲染
效果:
新增添加参数/属性对话框/form表单校验
在添加参数按钮新增一个click事件
第一步:引入Dialog对话框(element ui给出了方案)
第二步:在data中定义对话框默认隐藏
第三步:在computed中新增titleText达到对话框titile赋值
第四步:引入Form表单(element ui给出了方案)
第五步:在data中新增属性
第六:监听添加对话框的关闭事件
效果:
新增点击确定添加动态参数或静态属性
第一步:查看接口文档
第二步:在确定按钮上新增click事件
第三步:接口请求
效果:
新增编辑功能
在编辑按钮增加一个click事件
第二步:在data中定义对话框默认隐藏
第三步:引入Form表单(element ui给出了方案)
第四步:在data中新增属性
第五步:关闭对话框进行重置
效果:
获取动态参数值
采用作用域插槽进行attr_id获取
第一步:查看接口文档
第二步:调用接口
效果:
新增修改属性值
第一步:查看接口文档
第二步:调用接口
效果:
新增删除功能
在删除按钮新增一个click事件
第一步:查看接口文档
第二步:接口调用
引入MessageBox弹框(element ui给出了方案)
效果:
新增展开页标签
第一步:将数据进行分割重组
第二步:在展开行新增tag标签页,for循环
效果:
新增New Tags
第一步:引入New Tags(element ui给出了方案)
第二步:在data中定义相关初始值
第三步:在methods中定义方法
第四步:修改下宽度
效果:
fix修改New Tags 数据 关联到其他New Tags
第一步:找到获取参数列表的方法
每一行数据都有自己的bool和value
第二步:使用作用域插槽指定数据
第三步:删除data中定义相关初始值
第四步:在click事件中传入scope.row
第五步:在函数的形参中传入row
效果:
fix点击New Tags 自动获取焦点
第一步:引入New Tags获取焦点(element ui给出了方案)
效果:
fix失去焦点文本框与按钮切换
第一步:在属性框增加scope.row
第二步:失去焦点后,从input过度到tags
效果:
fix判断用户是否输入无效字符(空格)进行重置
第一步:进行输入值长度判断
效果:
新增添加tags到数据库操作
第一步:查看接口文档
第二步:调用接口
效果:
新增删除tag标签
第一步:在tags中增加close事件
第二步:将添加tags到数据库的方法单独封装一下
第三步:删除对应参数可选项
效果:
fix选择三级分类后再选择二级分类数据依旧存在
第一步:
效果:
商品列表
增加商品列表参数骨架
第一步:需要重新创建一个分支取名为:goods_list
第二步:在goods目录创建List.vue
第三步:写基础骨架
<template>
<div>
商品列表
</div>
</template>
<script>
export default {
data() {
return {}
},
created() {
},
methods: {}
}
</script>
<style lang="less" scoped="">
</style>
第四步:在路由引入
效果:
增加面包屑/输入框/按钮
第一步:引入面包屑
第二步:引入卡片视图
第三步:引入输入框/按钮
效果:
商品列表数据获取
第一步:查看接口文档
第二步:定义参数
第三步:接口调用
效果:
新增表格区域数据渲染
第一步:
效果:
fix创建时间未格式化
第一步:全局定义格式化时间的过滤器
第二步:进行调用
新增分页标签功能
第一步:引入Pagination分页(element-ui提供了方法)
第二步:创建方法
效果:
新增搜索/清空功能
第一步:在input输入框增加数据绑定,清空,清空触发功能
效果:
新增删除功能
第一步:在删除按钮上新增click事件
第二步:查看接口文档
第三步:调用接口文档
引入MessageBox弹框(element-ui提供了方法)
效果:
新增添加商品单独页面
第一步:在添加商品新增一个click事件
第二步:在methods写一个路由跳转的方法
第三步:新增一个Add.vue
第四步:编写Add.vue骨架
第五步:在index.js注册该路由
效果:
新增添加商品
第一步:引入面包屑
第二步:引入卡片
第三步:引入Alert警告(element ui给出了方案)
第三步:引入Steps步骤条(elementui给出了方案)
效果:
新增tabs栏渲染
第一步:引入Tabs标签页(element ui给了方案)
效果:
新增步骤条与tab栏数据联动效果
第一步:新增name索引值、v-model数据绑定
第二步:长得像数字的字符串
第三步:
效果:
新增商品基本信息绘制
第一步:引入Form表单、表格校验
第二步:加入校验规则
效果:
商品分类的数据获取
第一步:查看接口文档
第二步:创建声明周期函数
第三步:调用接口函数
效果:
新增商品列表级联选择器
第一步:引入级联选择器
第二步:在addForm新增一个goods_cat接收数据和校验
第三步:级联选择器数据
效果:
控制Tabs标签切换
第一步:使用element ui 提供函数
第二步:判断是不是选中第一个tabs标签或是否选择了3级选择
效果:
商品参数面板对应数据获取
第一步:新增tab被选中时触发(element ui给出了方案)
第二步:查看接口文档
第三步:判断激活面板是否等于1、接口调用
第四步:请求成功后保存到manyTableData
效果:
新增商品参数面板绘制
第一步:在tab被选中时触发
第二步:引入复选框(element ui给出了方案)
第三步:进行组件注册
效果:
fix复选框样式优化
第一步:
效果:
商品属性面板对应数据获取
第一步:
第二步:将获取到的数据保存到onlyTableData
效果:
新增商品属性面板绘制
第一步:进行表单循环(和商品参数一致)
效果:
新增图片上传功能
第一步:引入Upload上传组件(element ui 给出了方案)
第二步:将Url写在data中,注意这个url是baseURL的根路径
第三步:写方法
第四步:组件注册
效果:
fix上传图片不成功
第一步:点击上传图片后在网络请求中可以看到是无效token,就证明发起这个请求并没有携带
第二步:Upload上传给了一个设置上传请求头部的事件
第三步:在el-upload中添加这个事件
第四步:在data中写入请求头
效果:
新增图片上传成功之后操作
第一步:Upload图片上传给出了文件上传成功时的钩子(element ui给出了方案)
第二步:将图片信息对象push到pisc中,先创建一个接收对象
第三步:写方法
效果:
新增图片移除操作
第一步:Upload图片上传给出了文件移除时的钩子(element ui给出了方案)
第二步:处理移动图片的操作
效果:
新增图片预览功能
第一步:Upload图片上传给出了文件上传时的钩子(element ui给出了方案)
第二步:引入Dialog对话框
第三步:获取图片预览地址
第四步:控制dialog对话框的显示与隐藏
当点击图片名称就触发
第五步:修改样式
效果:
新增富文本框
第一步:安装vue-quil-editor
第二步:man.js进行全局引用富文本编辑器
第三步:在Add.vue引入富文本
第四步:在addForm创建一个goods_introduce接收富文本内容
第五步:修饰样式
效果:
新增添加商品预验证
第一步:新增添加商品按钮、click事件
第二步:写click事件方法
知识点:validate是校验表单项prop值,校验是否通过
效果:
添加商品前数据转换
第一步:
第二步:引入深拷贝依赖库
第三步:在代码中导入深拷贝库官方地址
为什么需要进行深拷贝呢?因为在此之前级联选择器希望是数组,而另外个希望是字符串产生了分歧,所以同时满足就采用深拷贝。
效果:
添加商品时数据转换
第一步:
第二步:新增attrs数组
第三步:
效果:
完善添加商品
第一步:查看接文档
第二步:接口请求
效果:
新增订单管理分支
新增订单管理骨架
第一步:需要重新创建一个分支取名为:order
第二步:在order目录创建Order.vue
第三步:写基础骨架
<template>
<div>
订单管理
</div>
</template>
<script>
export default {
// 初始化基础数据节点
data(){
return {}
},
// 声明周期函数
created() {},
// 事件处理函数
methods: {}
}
</script>
<style lang="less" scoped="">
</style>
第四步:在路由引入
效果:
增加面包屑/卡片/输入框基础页面
第一步:引入面包屑
第二步:引入卡片
第三步:引入input搜索框
效果:
商品订单数据获取
第一步:查看接口文档
第二步:增加查询信息
第三步:接口请求
效果:
商品订单数据渲染
第一步:引入el-tab表格
第二步:
效果:
新增分页功能
第一步:引入Pagination分页
第二步:写对应方法
效果:
新增省市区/县联动效果
第一步:给编辑按钮新增clikc事件
第二步:引入Dialog对话框
第三步:控制默认对话框为隐藏
第四步:点击编辑按钮控制对话框显示
第五步:引入表单校验
第六步:表单校验规则
第七步:引入城市列表(citydata.js)
第八步:引入级联选择器,并将城市数据绑定到opthions
第九步:新增dialog对话框close事件
第十步:关闭对话框后触发清空表单事件
效果:
新增物流进度的对话框/物流进度数据获取
第一步:在定位图标新增click 事件
第二步:引入Dialog对话框
第三步:设置对话框为隐藏状态
第四步:查看接口文档
第五步:接口请求
效果:
新增时间线
第一步:引入时间线(element ui)给出了方案
第二步:注册组建
效果:
合并分支到master
Report报告
新增报表骨架
第一步:需要重新创建一个分支取名为:report
第二步:在report目录创建Report.vue
第三步:写基础骨架
<template>
<div>
报表组件
</div>
</template>
<script>
export default {
data() {},
created() {},
methods: {}
}
</script>
<style lang="less" scoped="">
</style>
第四步:在路由引入
效果:
添加面包屑/卡片
第一步:引入面包屑
第二步:引入卡片
效果:
引入Echars图表
第一步:安装Echars插件
第二步:在项目中引入Echarts
1.导入Echarts
2.创建一个Dom节点
3.创建一个mounted钩子 该钩子的作用是Dom创建完成之后才会执行,此时,页面上的元素已经渲染完毕了
4.准备Echarts展示数据和配置项
5.在页面上展示Echarts
效果:
数据报表渲染
第一步:查看接口文档
第二步:请求接口
第三步:进行数据合并
效果:
优化项目
新增顶部加载进度条
第一步:下载nprogress插件
第二步:在main.js导入nprogress
第三步:在发起axios请求前调用Nprogress.start()
第四步:在发起axios请求后调用Nprogress.done()
效果:
安装生产环境禁止console.*打印
第一步:在开发依赖下载对应插件
第二步:在build过程中,新增该插件
关于插件在生产环境禁止console.log()输出
第一步:下载插件【babel-plugin-transform-remove-console】
第二步:在根目录可以看到babel.config.js这个文件
第三步:进行判断为什么环境,如果为生产环境,则使用这个插件,非正式环境则不使用这个插件
效果:现在在开发环境,所以可以正常打印console.log
项目生产打包报告
- 生成打包报告
// 通过 vue-cli 的命令选项可以生成打包报告
// --report 选项可以生成 report.html 以帮助分析包内容
vue-cli-service build --report
- 通过可视化的UI面板直接查看报告
在可视化UI面板中,通过控制台和分析面板,可以方便看到项目中所存在的问题
修改webpack默认配置
第一步:在根目录创建vue.config.js这个配置文件
第二步:固定写法
// vue.config.js
// 这个文件中,应该导出一个包含了自定义配置选项的对象
module.exports = {
// 选项...
}
chainWebpack 自定义打包入口
- 新增一个自己的打包入口文件
第一步:将原来main.js修改成dev和prod模式
第二步:定义打包入口
通过 externals 加载外部CDN资源
默认情况下,通过import语法导入第三方依赖包,最终会被打包合并到同一个文件中,从而导致打包成功后,单个文件体积过大的问题。
第一步:配置externals
第二步:删除在main.js引入的资源,进行
第三步:在public文件夹下找到index.html
效果:编译后体积明显变小
通过CDN优化ElementUI的打包
- 在main-prod.js中,注释掉element-ui按需加载的代码
- 在index.html的头部区域中,通过CDN加载element-ui的js和css样式
效果:编译后体积明显变小
首页内容定制
在public/index.html首页中,可以根据isProd的值,来决定如何渲染页面结构
第一步:在vue.config.js中增加一个自定义属性
chainWebpack: config => {
// 发布模式默认打包入口
config.when(process.env.NODE_ENV === 'production', config => {
// html插件新增一个自定义属性
config.plugin('html').tap(args =>{
args[0].isProd = true
return args
})
})
// 开发模式默认打包入口
config.when(process.env.NODE_ENV === 'production', config => {
config.plugin('html').tap(args =>{
args[0].isProd = false
return args
})
})
}
第二步:在index.html中进行判断
效果:编译后体积明显变小
路由的懒加载
不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件
- 安装@babel/plugin-syntax-dynamic-import包
- 在babel.config.js配置文件中声明该插件
- 将路由改为按需加载的形式
效果:
项目上线
导入阿里icon到自己项目
第一步:首先去网站进行注册并选好需要的图标下载到本地
第二步:将这个目录放在src/assets里
第三步:去main.js进行引入
第四步:使用图标
git使用大全
创建子分支
git checkout -b 分支名字
查看当前所有分支
git branch
将代码添加到暂存区
git add .
查看上次提交之后是否有进行再次修改
git status
提交代码
git commit -m "备注"
切换主分支
git checkout master
合并代码
git merge 分支
推送到代码管理仓库(码云/github)
git push -u origin 仓库名(-u 是没有将分支推送到仓库的操作)
git push
目录结构
├─README
├─babel.config.js
├─package-lock.json
├─package.json
├─src
├─vue.config.js
└─电商管理后台 API 接口文档