【黑马】Vue全家桶-项目实战(学习打卡08)
- PC后台管理系统
- 功能:用户账号(登录退出,用户管理,权限管理),商品管理(商品分类,分类参数,商品信息,订单),数据统计
- 系统采用前后端分离的开发模式 前端项目是基于Vue的SPA(单页应用程序)项目
- 包含所有所需要的素材 phpStudy、mysql数据库数据、后台项目vue_api_server
- 技术栈:Vue,Vue-Router,Element-UI,Axios,Echarts
项目概述
1. 电商项目基本业务概述
根据不同的应用场景,电商系统一般都提供了 PC 端
、移动 APP
、移动 Web
、微信小程序
等多种终端访问方式。这些不同终端都共用同一个数据库和同一个API接口服务器。
本教程主要探讨PC后台管理
项目,是给商城的后台管理员使用的。
2. 电商后台管理系统的功能
电商后台管理系统用于管理用户账号、商品分类、商品信息、订单、数据统计等业务功能。
3. 采用的开发模式(前后端分离)
电商后台管理系统整体采用前后端分离的开发模式,其中前端项目是基于 Vue 技术栈实现的 SPA 应用程序。
用户
-> 前端项目(SPA)
-> 后端项目
-> 数据库
后端负责写接口,前端负责调接口的开发模式,我们叫做前后端分离的开发模式。这是主流的开发模式,效率高,易维护。
- 前后端分离:
- 后端:负责操作数据库,并向前端暴露一些接口
- 前端:主要负责绘制页面,并通过Ajax技术调用后端提供的API接口,
4. 系统的技术选型
【前端项目技术栈】
Vue
Vue-router
:路由Element-UI
:前端UI组件库Axios
:发起网络数据请求Echarts
:绘制图像报表
【后端项目技术栈】
Node.js
Express
Jwt
:一个状态保持的工具,可以模拟Session那样的登录记录功能Mysql
:后台数据库Sequelize
:操作数据库的框架
【项目初始化】
1. 前端项目初始化步骤
安装 Vue 脚手架
通过 Vue 脚手架创建项目(过程请看之前笔记)
创建新项目的最后一步配置,不选择历史模式的Vue路由,把那一项开关关上,使用哈希模式的路由
通过vue ui面板的
插件
中,安装vue-cli-plugin-element
,并配置为按需导入通过vue ui面板的
依赖
中,安装axios
到运行依赖码云账号的申请和SSH密钥的添加(以前弄过的可以跳过)
- 注册或登录 码云(Gitee),点击右上角头像,进入
设置
->SSH公钥
,我们将按照官方教程“怎样生成公钥”来进行如下操作。 - 复制命令
ssh-keygen -t ed25519 -C "xxxxx@xxxxx.com"
,打开一个本地cmd终端,粘贴执行,注意引号中要替换为自己码云的邮箱。连续按三次回车,即可生成密钥,并被保存到了C:\Users\你的用户名\.ssh
目录下了。 - 按路径找到那个生成的
.pub
结尾的公钥文件,右键随便用一个编辑器打开,把里面的一坨代码全选复制,粘贴到码云的公钥中,随便起一个标题名字,点击确定。 - 测试一下密钥能否正常使用,在终端输入命令
ssh -T git@gitee.com
并执行,选择yes并回车。当你再一次执行命令ssh -T git@gitee.com
的时候,就可以正常返回你的账号信息了。(首次使用需要确认并添加主机到本机SSH可信列表。若返回Hi XXX! You've successfully authenticated, but Gitee.com does not provide shell access.
内容,则证明添加成功。)
- 注册或登录 码云(Gitee),点击右上角头像,进入
初始化 git 远程仓库
- 打开码云,点击右上角头像左侧的加号,选择
新建仓库
。仓库名称随便填比如vue_shop
,如果有生成Readme那个选项的话可以取消勾选,然后点击创建。 - 按照提示,打开终端,分别执行
git config --global user.name "码云用户名"
和git config --global user.email "邮箱"
这两句命令。 - 因为我们刚刚已经创建的本地的vue项目仓库,按照提示,我们复制远程仓库链接
git remote add origin https://gitee.com/码云用户名/vue_shop.git
。打开本地的vue_shop项目文件夹,右键打开终端(Powershell窗口或者Git Bash Here都可以),右键粘贴执行。可以git remote -v
查看是否正常返回仓库地址。
- 打开码云,点击右上角头像左侧的加号,选择
将本地项目托管到 Github 或 Gitee 中
- 执行
git push -u origin "master"
命令,把本地仓库和云端仓库做一下关联。执行后弹出窗口,让你输入码云的账号邮箱和密码。 - 到你的码云个人主页里,就可以看到本地仓库已经上传成功了。今后我们在本地每做完一个功能,都需要将代码提交到码云中。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
- 执行
2. 后台项目的环境安装配置
- 安装 MySQL 数据库:
- 双击执行素材包中的
phpStudy20161103.exe
,一步一步安装到本地就行了。PhPstudy是一个集成环境,叫做小皮面板。 - Windows电脑这时如果弹出两条分别关于
MySQL
和Apache
的防火墙警告,点击允许就行了。然后就可以看到Apache和MySQL都处于运行中的状态。 - 我们在开发过程中只需用到MySQL,那个Apache不需要,得把它停掉。点击
其他选项菜单
,找到服务管理器
,选择Apache
,点击停止
。光让MySQL正常运行就行啦。 - 现在导入一个数据库,从而支持API接口服务器的正常运行。找到素材中的
vue_api_server.rar
压缩包,解压到当前文件夹。点开文件夹中的db目录,里面的mydb.sql
脚本就是我们的数据库脚本文件。 - 点击PhPstudy面板上的
MySQL管理器
,选择MySQL导入导出
。输入密码(默认为root),点击按钮选择要还原的文件
,选中mydb.sql
脚本文件,然后输入还原到数据库
的名称mydb
,点击导入,会弹出cmd终端窗口。(如果闪退的话,请把mydb.sql复制到桌面再导入,因为路径中不能有任何中文)耐心等待,运行过程会很长,知道它运行完自动关闭黑窗口。 - 你可以点击PhPstudy面板的
其他选项菜单
,点击MySQL工具
,选择打开数据库目录
,只要能看到一个文件夹叫做mydb,并且里面有很多相关文件,那就证明数据库还原完成了。
- 双击执行素材包中的
- 安装 Node.js 环境
- 配置项目相关信息
- 启动项目
- 素材中我们上面解压的
vue_api_server
目录就是我们的后台API项目,只不过这个项目要先安装依赖包才可以正常运行。你可以先把该文件夹挪到合适的地方,在它里面打开终端,执行npm install
命令安装所有依赖项。耐心等待,会看到文件夹中多了一个名为node_modules的目录。 - 终端别关,现在输入
node app.js
命令把API接口项目跑起来。 - 我们可以打开素材包中的
电商管理后台 API 接口文档.md
看看整个项目为我们提供了哪些可用的接口,接下来我们就要测试接口了。
- 素材中我们上面解压的
- 使用 Postman 测试后台项目接口是否正常
- 找到素材中的
Postman-win32-6.7.3-Setup.exe
安装包,双击执行即可安装到电脑。首次使用请用邮箱注册,QQ邮箱的话尽量不要使用QQ相同的密码,保护好自己。注册后点击邮箱中刚收到的邮件的激活按钮即可注册成功。返回Postman,选择Sign In(登录),输入账号(邮箱或用户名)和密码,即可进入面板。 - 比如我们要验证登录验证接口,在Postman中把请求方式改为
POST
,输入框的请求路径填入接口基准地址http://127.0.0.1:8888/api/private/v1/
,然后后面再加个请求路径login
,变成完整的请求地址http://127.0.0.1:8888/api/private/v1/login
。由于登录带参数,我们点击输入框下面的Body(表单体)选项,数据选择普通的x-www-form-urlencoded
这种数据格式就行。然后在下面键值对(KEY 和 VALUE)表单中,根据文档填入需要提供的数据username
和password
,比如张三
和123456
,点击Send
按钮发送请求。稍等片刻,返回结果:可以看到状态码400表示登录失败。现在我们再请求一次,用户和密码分别是1
2
3
4
5
6
7{
"data": null,
"meta": {
"msg": "无效token",
"status": 400
}
}admin
和123456
,发送:登陆成功,并返回了用户名、电话、邮箱、以及token。其中token是用来进行客户端与服务器端的登录状态保存的,类似于Node中的session,我们通过token来验证这个人有没有登录。1
2
3
4
5
6
7
8
9
10
11
12
13
14{
"data": {
"id": 500,
"rid": 0,
"username": "admin",
"mobile": "12345678",
"email": "adsfad@qq.com",
"token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2NDk5NjE4MDYsImV4cCI6MTY1MDA0ODIwNn0.pXl8YMiCovNnC_rdyTuYWyX102X7PUn0TpscNNdyC8M"
},
"meta": {
"msg": "登录成功",
"status": 200
}
}
- 找到素材中的
【登录/退出功能】
1. 登录概述
登录业务流程
- 在登录页面输入用户名和密码
- 调用后台接口进行验证
- 通过验证之后,根据后台的响应状态跳转到项目主页
登录业务的相关技术点
- http 是无状态的
- 通过 cookie 在客户端记录状态,通过 session 在服务器端记录状态
- 通过 token 方式维持状态
如果客户端和服务器不存在跨域问题,推荐使用cookie和session结合来记录登录状态;
如果存在跨域问题,推荐使用token方式来维持登录状态。
2. 登录:token原理分析
3. 登录功能实现
(1) 登录页面的绘制
- 通过 Element-UI 组件实现布局
- el-form 表单组件
- el-form-item 子项
- el-input 输入框
- el-button 按钮
- 字体图标
VScode打开vue_shop
项目文件夹,在绘制页面前,新建终端查看一下当前工作区是否干净:
1 | # 执行命令 git status |
如上所示就是代表不干净,工作树中还有未提交的git记录,应该是上面项目初始化时安装完依赖和插件之后没有提交,那就执行命令提交一下:
1 | git add . |
现在gitee上应该有提交记录了,再次查看工作树是否干净:
1 | # 执行命令 git status |
现在就可以进行登陆页面的绘制了。
在撰写代码之前,我们首先创建一个分支。在开发中,只要是开发新功能了,尽量把新功能都放到一个新的分支上进行开发。当功能开发完成后,再把分支合并到master主分支上。
1 | # 创建login分支,并通过checkout切换到该分支 |
打开cmd窗口,运行vue ui
命令打开可视化面板,运行
项目,启动APP
弹出页面,可以看到默认的vue项目主页。
想删掉默认内容新建空白页面的话就到App.vue
中修改相应内容,然后删除router.js
中没用的路由,再删除components
文件夹下没用的组件。然后打开项目页面看看效果,没问题就可以书写代码了。
绘制过程略,请看视频。
因为视频教程比较老(3年了),你可能会遇到以下问题:
- 【vue eslint】报错Component name “xxxxx“ should always be multi-word.eslintvue/解决方案
那就别叫Login.vue
,改成VueLogin.vue
- Vue中关于Can’t resolve ‘less-loader’
在可视化面板中安装“开发依赖”:less-loader
和less
就行- vue遇见的问题:imported multiple times
原因是element-ui import次数过多,不要学视频中的,我们将要引入的组件写在一起,只写一个import { Button, Form, FormItem, Input } from 'element-ui'
- 报错:
Expected indentation of 2 spaces but found 4
在Vue单文件的<script>
中书写代码时,别用Tab键直接缩进4个空格,而是用空格键手动敲俩就行- 报错:
Missing space before function parentheses space-before-function-paren
原因是函数名称或function关键字与开始参数之间缺少空格,通过提示找到缺少的空格应该加在data ()
之间- 报错:
error Extra semicolon semi
把提示的地方的分号删掉就行了。出现报错的原因是因为使用了eslint,这个是eslint的规范报错,所以能不用分号就不用分号。- 页面空白,并且报错
[Vue warn]: Property or method "loginForm" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
检查一下你是不是和我一样把data
写成date
了……- 为表单的数据绑定示例步骤:
- 为
<el-form>
添加:model="loginForm"
属性- 在数据data的返回值return里定义数据绑定对象
loginForm
并书写其属性- 把其属性通过
v-model="loginForm.username"
形式绑定到对应<el-input>
中- 为表单添加验证规则的步骤:
- 为
<el-form>
通过属性绑定,绑定一个:rules="rules"
校验对象- 在
data
数据中定义这个数据对象rules:{}
,其中每个属性都是一个验证规则- 为不同的
<el-form-item>
通过prop="name"
来指定不同的验证规则- 为表单重置:
- 想要拿到表单实例对象,可以为
<el-form>
添加ref="loginFormRef"
引用,现在这个引用对象的名称loginFormRef
就是表单的实例对象- 为重置按钮绑定点击事件
@click="resetLoginFrom"
- 在方法
methods
中定义处理函数resetLoginFrom
,处理函数中,通过引用对象名称loginFormRef
获取实例,直接使用表单resetFields方法重置表单:this.$refs.loginFormRef.resetFields()
- 和表单重置按钮差不多,若想为登录按钮添加表单登录之前的预验证,可以使用validate方法
- 如果某方法的返回值是
Promise
,那我们可以用async
、await
来简化这个Promise操作。注意await只能用在被async修饰的方法中,所以我们要把紧挨着await的那个方法修饰成异步的async方法。
(2) 实现登录
- 通过 axios 调用登录验证接口
- 登录成功之后保持用户 token 信息
- 跳转到项目主页
1
2
3
4
5
6
7
8const {data: res } = await this.$http.post('login', this.loginForm)
if (res.meta.status !== 200)return this.$message.error('登录失败!')
// 提示登录成功
this.$message.success('登录成功!')
// 把登录成功的token保存到sessionStorage
window.sessionStorage.setItem('token', res.data.token)
// 使用编程式导航,跳转到后台主页
this.$router.push('/home')
为什么要把token保存在
sessionStorage
中而不是localStorage
中?
因为 localStorage 是持久化的存储机制,而sessionStorage是会话期间的存储机制。
(3) 路由导航守卫控制访问权限
如果用户没有登录,但是直接通过URL访问特定页面,需要重新导航到登录页面。
1 | // 为路由对象,添加 beforeEach 导航守卫 |
- 其中 to 代表将要访问的路径
- 其中 from 代表从哪个路径跳转而来
- 其中 next 是一个函数,表示放行
- 第一种
next()
放行- 第二种
next('/login')
强制跳转
(4) 退出登录实现原理
基于 token 的方式实现退出比较简单,只需要销毁本地的 token 即可。这样,后续的请求就不会携带
token ,必须重新登录生成一个新的 token 之后才可以访问页面。
1 | // 清空token |
4. 处理项目中语法警告问题
相信在撰写上方代码的时候,每次修改完保存时总是出现各种语法警告。比如“Vue单文件组件名称必须是多单词名称
”、“某一行应该是2个缩进空格但是发现了4个
”、“Vue单组件文件最后一行必须有换行符
”、“if()判断语句的if和括号之间必须有空格
”、“Vue单组件文件中空白行不能超过两行
”、“某一行的最后不允许有空白占位符
”、“某一行末尾发现了多余的分号
”、“某处不应写双引号而是应写成单引号
”……几乎每次保存重新编译时都有报错提醒,甚是磨人。
这是因为我们有时候写的语法不符合eslint语法规范,被它检查出来了。并且VScode右键格式化(Shift+Alt+F
)的代码与eslint语法不一致,导致很烦人。
那如何才能解决这个问题呢?在项目的根目录中创建一个配置文件.prettierrc.js
,专门来告诉编辑器在格式化代码的时候如何进行相关的代码格式化:
1 | module.exports = { |
其中,还可以到.eslintrc.js
中禁用space-before-function-paren
的语法规则,这样以后函数名与小括号之间没有空格也不会报错了:
1 | // 文件.eslintrc.js中rules里,添加下方代码花括号中的最后一行 |
此外,可以依次点击VSCode的 【
文件file
】 => 、首选项preferences
】 => 【设置settings
】 => 【上方输入tab
回车】 => 【找到下方TabSize
配置项,将4修改为2】,这样写代码的时候Tab缩进值就是2了。
5. 优化一下Element-UI中按需组件的导入形式
就是把scr
=> plugins
=> element.js
文件中导入组件的语句合并成一行。
其实这个我之前遇到过了,因为多行导入会报错的,所以从一开始我就是写成了一行:
1 | import Vue from 'vue' |
6. 提交登录功能代码
登录功能写好后就可以提交到Git仓库了(Gitub/Gitee)。
先关掉vue ui可视化面板中的运行状态,然后在VScode中新建终端,先运行git status
查看当前项目中源代码的状态:
1 | PS C:\Users\10272\vue_shop> git status |
上半部分的红色文件代表发生了修改,下半部分的红色文件代表是我们新增的。把所有文件全部添加到暂存区:
1 | git add . |
可以再次运行命令 git status
查看仓库代码状态。可以发现,所有的文件全部变成了绿色,说明被添加到了暂存区。
现在运行如下命令把这些代码提交一下,双引号里是提交的信息,你想写啥写啥:
1 | git commit -m "完成了登录功能" |
现在就把暂存区中的代码提交到了本地仓库中了。可以执行git branch
命令查看一下分支:
1 | PS C:\Users\10272\vue_shop> git branch |
可以看到当前我们处于login
分支,刚才我们git commit
提交的文件都被放到了login分支中进行了保存了。我们现在可以把login分支中的所有代码更新/合并到master
分支。
首先第一步,要先切换到master分支,因为你要合并到哪个分支,必须先切换到哪个分支,然后再从这个分支主动合并login分支就行了:
1 | # 切换到主分支 |
切换到master分支后,执行合并命令:
1 | git merge login |
此时master中的代码也变成了最新的,下面进行远程的推送,把本地的master分支推送到码云(Gitee)中:
1 | git push |
此时就已经把本地master中的代码推送到了码云中,可以打开码云看一下提交情况。
可以发现,云端仓库只有一个master分支,我们如何把本地的login分支推送到云端进行保存呢?先切换分支:
1 | # 切换分支 |
由于我们是第一次将login分支推送到云端,因此需要加上-u
参数:
1 | git push -u origin login |
由于我们第一次推送login分支时,加上了-u参数,Git不但会把本地的login分支内容推送的远程新的login分支,还会把本地的login分支和远程的login分支关联起来,在以后的推送或者拉取时就可以简化命令(
git push
/git pull
)。不带任何参数的git push,默认只推送当前分支,这叫做simple方式。
【主页布局】
1. 整体布局
整体布局:先上下划分,再左右划分。
1 | <el-container> |
2. 左侧菜单布局
左侧菜单布局:菜单分为二级,并且可以折叠。
1 | <el-menu> |
3. 通过接口获取菜单数据
除了登录接口,其他所有API接口在请求时都需要token令牌
通过axios请求拦截器添加token,保证拥有获取数据的权限
1 | // axios请求拦截,相当于对请求头进行一次预处理 |
4. 动态渲染菜单数据并进行路由控制
- 通过 v-for 双层循环分别进行一级菜单和二级菜单的渲染
- 通过路由相关属性启用菜单的路由功能
1 | <el-menu router> |
【用户管理】
1. 用户管理概述
通过后台管理用户的账号信息,具体包括用户信息的展示、添加、修改、删除、角色分配、账号启用/注销等功能。
- 用户信息列表展示
- 添加用户
- 修改用户
- 删除用户
- 启用或禁用用户
- 用户角色分配
2. 用户信息列表展示
(1) 用户列表布局
- 面包屑导航 el-breadcrumb
- 用户数据卡片视图区域 el-card
- 搜索框 el-input
- Element-UI 栅格系统基本使用 el-row
- 表格布局 el-table
- 图标按钮
- Tooltip 文字提示
(2) 用户状态列和操作列处理
(3) 表格数据填充
(4) 表格数据分页
(5) 搜索功能
3. 用户状态控制
4. 添加用户
(1) 添加用户表单弹窗布局
(2) 表单验证
- https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=53
- 自定义校验规则:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=54
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 验证邮箱的规则
var checkEmail = (rule, value, callback) => {
// 验证邮箱的正则表达式
const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/
if (regEmail.test(value)) {
// 合法的邮箱
return callback()
}
// 返回一个错误提示
callback(new Error('请输入合法的邮箱'))
}
// 验证手机号码的规则
var checkMobile = (rule, value, callback) => {
// 验证手机号码的正则表达式
const regMobile = /^(0|86|17951)?(13[0-9]|15[0123456789]|17[678]|18|[0-9]|14[57])[0-9]{8}$/
if (regMobile.test(value)) {
return callback()
}
// 返回一个错误提示
callback(new Error('请输入合法的手机号码'))
}
(3) 表单提交
表单重置:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=55
1
2
3
4// 监听添加用户对话框的关闭事件
addDialogClosed () {
this.$refs.addFormRef.resetFields()
},表单提交预验证:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=56
1
2
3
4
5
6
7
8// 点击按钮,添加新用户
addUser () {
this.$refs.addFormRef.validate(valid => {
// console.log(valid)
if (!valid) return
// 可以发起添加用户的网络请求
})
}添加用户表单提交:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=57
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 点击按钮,添加新用户
addUser () {
this.$refs.addFormRef.validate(async valid => {
// console.log(valid)
if (!valid) return
// 可以发起添加用户的网络请求
const { data: res } = await this.$http.post('users', this.addForm)
if (res.meta.status !== 201) {
this.$message.error('添加用户失败!')
}
this.$message.success('添加用户成功!')
// 隐藏添加用户的对话框
this.addDialogVisible = false
// 重新获取用户列表数据
this.getUserList()
})
}
5. 修改用户
根据 ID 查询用户信息:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=59
修改按钮外面通过作用域插槽接收到了scope
这个数据对象,那么通过scope.row
就拿到了这一行对应的数据信息1
2
3
4
5
6
7
8
9
10
11
12<el-table-column label="操作" width="180px">
<template slot-scope="scope">
<!-- 修改按钮 -->
<el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button>
<!-- 删除按钮 -->
<el-button type="danger" icon="el-icon-delete" size="mini"></el-button>
<!-- 分配角色 -->
<el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false">
<el-button type="warning" icon="el-icon-setting" size="mini"></el-button>
</el-tooltip>
</template>
</el-table-column>提交修改前的表单预验证:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=62
6. 删除用户
- 删除操作的消息提示框:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=64
- 实现删除用户的操作:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=65
7. user分支的创建和代码的推送
用户管理功能写完了,现在提交代码到git仓库。
1 | # 查看当前分支 |
1 | # 新建并切换到user分支(checkout切换;-b新建) |
1 | # 检查当前分支文件修改状态 |
1 | # 检查当前分支文件修改状态 |
1 | # 检查当前分支文件修改状态 |
1 | # 查看当前分支 |
1 | # 查看当前分支 |
【权限管理】
1. 权限管理业务分析
通过权限管理模块控制不同的用户可以进行哪些操作,具体可以通过角色的方式进行控制,即每个用户分配一个特定的角色,角色包括不同的功能权限。
1 | # 查看当前分支 |
1 | # 查看当前分支 |
2. 权限列表展示
- 开发权限列表对应规格:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=68
- 权限列表的基本页面布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=69
- 权限列表的数据获取:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=70
- 将权限列表数据渲染成table表格:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=71
3. 角色列表展示
- 用户的角色和权限之间的关系:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=72
- 角色列表路由的展示:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=73
- 角色列表的基础布局与数据获取:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=74
- 角色列表的数据渲染:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=75
4. 用户角色分配
(1) 展示角色对话框
- 实现用户角色对话框布局
- 控制角色对话框显示和隐藏
- 角色对话框显示时,加载角色列表数据
(2) 完成角色分配功能
5. 角色权限分配
(1) 表格行展开效果
(2) 渲染一级权限菜单
(3) 渲染二、三级权限菜单
- 角色二级权限的渲染:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=80
- 角色三级权限的渲染:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=81
(4) 删除角色下的权限
(5) 给角色分配权限流程
- 实现角色分配权限对话框布局
- 控制对话框的显示和隐藏
- 对话框显示时调用后台接口加载权限列表数据
- 完成树形权限菜单的展示
- 选中默认的权限
- 保存选中的权限,调用后台接口完成角色权限的分配
(6) 实现权限分配对话框布局
- 实现对话框布局效果
- 控制对话框显示和隐藏
(7) 渲染权限的树形结构
- 树形组件 el-tree 的基本使用:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=86
- 树形控件的使用优化:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=87
- 权限数据的加载与填充:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=89
(8) 设置默认权限菜单选中
- 获取所有叶子节点的 id
- 设置权限节点选中
(9) 完成角色授权
- 获取所有选中的权限节点 id
- 调用接口完成角色权限的分配:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=91
- 分配角色下拉菜单:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=93
- 分配角色:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=94
6. rights分支的创建和代码的推送
1 | # 查看当前分支 |
1 | git add . |
1 | # 切换到主分支 |
1 | # 查看当前分支 |
【分类管理】
界面演示:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=96
1. 商品分类概述
商品分类用于在购物时,快速找到所要购买的商品,可以通过电商平台主页直观的看到。
1 | # 创建商品管理分支 |
1 | # 首次推送到云端(参数-u) |
2. 商品分类列表
基本布局与数据获取
- 实现基本布局
- 实现分类列表数据加载
3. 树形表格
(1) 第三方树形表格的基本使用
安装第三方插件实现Element-UI没有的树形table表格:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=101
打开 Vue 面板,选择【依赖】这一项,点击【安装依赖】,选择【运行依赖】,搜索 vue-table-with-tree-grid
也可以手动安装依赖包:
npm i vue-table-with-tree-grid -S
基本使用:
1 | # 在 main.js 中导入 |
组件的配置与使用:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=101
(2) 实现分类树形列表
- 实现树形列表布局并进行数据填充
- 自定义表格列:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=102
4. 分页功能
- 实现分页组件效果
- 分页组件数据处理:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=104
5. 添加分类
(1) 实现分类树形列表
- 实现添加分类对话框布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=105
- 控制对话框显示和隐藏
- 获取父级分类数据列表:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=106
(2) 实现分类级联菜单效果
- 实现级联菜单效果
- 级联菜单数据加载与填充:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=107
Element新版本(比如2.15.7)的级联选择器有几处与视频教程中(版本1.6.0)有些不一样:
- 一个是原来单独的属性
expandTrigger
归到了Props中,成为了配置对象的一项; - 另一个是菜单会过高超出页面区域,只需在
global.css
中添加全局样式即可:1
2
3
4/* 级联选择器 */
.el-cascader-panel {
height: 300px;
} - 视频教程中可选择任意一级菜单的
change-on-select
属性,在新版本中归到了Props中的checkStrictly: true
属性上,表示是否严格的遵守父子节点不互相关联
(3) 控制父级分类的选择
父级分类选择时,获取对应的分类 id。
(4) 完成分类添加
将分类名称、分类等级和父分类 id 提交到后台,完成分类添加。
1 | # 查看分支 |
1 | # Git三连 |
1 | # 查看分支 |
【参数管理】
1. 参数管理概述
商品参数用于显示商品的固定的特征信息,可以通过电商平台商品详情页面直观的看到。
1 | # 查看分支 |
2. 商品分类选择
(1) 选择商品分类
- 页面基本布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=115
- 加载商品分类数据:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=116
- 实现商品分类的级联选择效果:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=117
(2) 控制级联菜单分类选择
- 只允许选择三级分类
- 通过计算属性的方式获取分类 ID:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=120
其中,Vue中这次添加的 computed
节点,与data
、created
、methods
平级,用于计算属性,用法和data中类似,直接this.isBtnDisabled
、this.cateId
即可获取值。
1 | export default { |
3. 实现参数列表
(1) 根据选择的商品分类加载对应的参数数据
- 参数列表布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=124
- 根据分类 id 加载参数列表数据
- 删除参数与确认弹框:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=129
(2) 处理标签数据格式
将字符串形式的数据分隔为数组:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=130
1 | res.data.forEach(item => { |
(3) 控制添加标签文本框的显示
新建Tag样式的实现:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=132
$nextTick
的执行时机是DOM 更新完毕之后:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=1341
2
3
4
5// $nextTick方法的作用,就是当页面上元素被重新渲染之后,才会执行函数回调中的代码
this.$nextTick(_ => {
// 让文本框自动获得焦点
this.$refs.saveTagInput.$refs.input.focus();
});
(4) 实现标签动态添加的文本框控制逻辑
- 控制标签输入域的显示和隐藏
- 对输入的内容进行数据绑定
(5) 实现标签的添加和删除操作
添加标签和删除标签使用的是同一个接口,参数是一样的。
https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=137
4. 实现动态参数与静态属性添加
- 动态参数与静态属性表单重用
- 添加动态参数与静态属性使用的是同一个接口,参数是一样的
https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=139
5. 代码的推送
1 | # 查看分支 |
1 | git add . |
1 | git checkout master |
【商品管理】
1. 商品管理概述
商品管理模块用于维护电商平台的商品信息,包括商品的类型、参数、图片、详情等信息。通过商品管理模块可以实现商品的添加、修改、展示和删除等功能。
1 | git checkout -b goods_list |
1. 商品列表
https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=142
- 实现商品列表布局效果:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=142
- 调用后台接口获取商品列表数据:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=143
- 商品列表表格渲染:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=144
- 在
main.js
注册全局过滤器:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=1441
2
3
4
5
6
7
8
9
10
11
12
13
14// 格式化时间的过滤器
Vue.filter('dateFormat', function(originVal) {
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})1
2
3
4
5
6<el-table-column label="创建时间" prop="add_time" width="140px">
<template slot-scope="scope">
<!-- 用数线调用过滤器 -->
{{scope.row.add_time | dateFormat}}
</template>
</el-table-column> - 分页选择器:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=146
- 搜索与清空:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=147
2. 添加商品
(1) 基本布局与分布条效果
- 添加商品基本布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=150
- 分布条组件用法
(2) 商品信息选项卡Tab布局效果
- Tab 组件的基本使用
(3) 商品基本信息
- 商品基本信息表单布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=155
- 表单数据绑定
- 表单验证
(4) 商品分类信息
- 商品分类布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=157
- 商品分类数据加载
- 阻止Tab标签页切换:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=159
(5) 商品动态参数
- 获取商品动态参数数据:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=160
- 商品动态参数布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=161
(6) 商品静态属性
- 获取商品静态属性数据:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=163
- 商品静态属性布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=164
(7) 商品图片上传
- 图片上传组件基本使用:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=165
- 配置图片上传组件 headers 请求头对象:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=166
1
2
3
4
5<!-- action表示图片上传地址 -->
<el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" list-type="picture" :headers="headerObj">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>1
2
3
4// 图片上传组件的 headers 请求头对象
headerObj: {
Authorization: window.sessionStorage.getItem('token')
} - 完成图片上传:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=167
- 图片移除:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=168
- 图片预览:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=169
(8) 商品详情
富文本编辑器基本使用:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=170
打开 vue-ui 可视化工具面板,点击
【依赖】
=>右上角【安装依赖】
=>选择【运行依赖】
=>查找vue-quill-editor
(8) 完成商品添加
处理商品相关数据格式
调用接口完成商品添加:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=174
(9) 代码的合并与提交
1 | git status |
1 | git checkout master |
【订单管理】
1. 订单管理概述
订单管理模块用于维护商品的订单信息,可以查看订单的商品信息、物流信息,并且可以根据实际的运营情况对订
单做适当的调整。
1 | git checkout -b order |
2. 订单列表
(1) 订单列表展示
- 订单数据加载:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=178
- 订单列表布局:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=179
(2) 查看订单地址信息
- 使用vue-项目实战day6素材中的
citydata.js
:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=181 - 省市区三级联动效果:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=181
- 省市区数据格式分析
(3) 查看订单物流信息
调用接口获取物流数据
由于源代码中所使用的第三方API接口响应数据失效,此功能无法正常使用。
源代码有关部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14// 自动匹配运单号所属的物流公司
function autoComNumber(orderno) {
const url = `https://www.kuaidi100.com/autonumber/autoComNum?resultv2=1&text=${orderno}`
return new Promise(function(resolve, reject) {
request(url, (err, response, body) => {
if (err) return reject({ status: 500, msg: err.message })
// resolve(body)
// console.log(body.num)
body = JSON.parse(body)
if (body.auto.length <= 0) return reject({ status: 501, msg: '无对应的物流公司' })
resolve({ status: 200, msg: body.auto[0], comCode: body.auto[0].comCode })
})
})
}终端窗口报错代码:
1
2E:\vue_api_server\modules\Logistics.js:13
if (body.auto.length <= 0) return reject({ status: 501, msg: '无对应的物流公司' })考虑到黑马的这个Vue课程录制时间已久,文档中用于测试的真实物流单号
804909574412544580
相关数据早已被物流公司清除。所以此次请求只能根据文档自定义返回数据:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81// 展示物流进度对话框
showProgressBox() {
// 此接口已失效
// const { data: res } = await this.$http.get('/kuaidi/804909574412544580')
// 此次请求自定义接口返回数据
const res = {
data: [
{
time: '2018-05-10 09:39:00',
ftime: '2018-05-10 09:39:00',
context: '已签收,感谢使用顺丰,期待再次为您服务',
location: ''
},
{
time: '2018-05-10 08:23:00',
ftime: '2018-05-10 08:23:00',
context: '[北京市]北京海淀育新小区营业点派件员 顺丰速运 95338正在为您派件',
location: ''
},
{
time: '2018-05-10 07:32:00',
ftime: '2018-05-10 07:32:00',
context: '快件到达 [北京海淀育新小区营业点]',
location: ''
},
{
time: '2018-05-10 02:03:00',
ftime: '2018-05-10 02:03:00',
context: '快件在[北京顺义集散中心]已装车,准备发往 [北京海淀育新小区营业点]',
location: ''
},
{
time: '2018-05-09 23:05:00',
ftime: '2018-05-09 23:05:00',
context: '快件到达 [北京顺义集散中心]',
location: ''
},
{
time: '2018-05-09 21:21:00',
ftime: '2018-05-09 21:21:00',
context: '快件在[北京宝胜营业点]已装车,准备发往 [北京顺义集散中心]',
location: ''
},
{
time: '2018-05-09 13:07:00',
ftime: '2018-05-09 13:07:00',
context: '顺丰速运 已收取快件',
location: ''
},
{
time: '2018-05-09 12:25:03',
ftime: '2018-05-09 12:25:03',
context: '卖家发货',
location: ''
},
{
time: '2018-05-09 12:22:24',
ftime: '2018-05-09 12:22:24',
context: '您的订单将由HLA(北京海淀区清河中街店)门店安排发货。',
location: ''
},
{
time: '2018-05-08 21:36:04',
ftime: '2018-05-08 21:36:04',
context: '商品已经下单',
location: ''
}
],
meta: { status: 200, message: '获取物流信息成功!' }
}
if (res.meta.status !== 200) {
return this.$message.error('获取物流进度失败')
}
this.progressInfo = res.data
this.progressVisible = true
console.log(this.progressInfo)
}实现物流信息列表时间线效果:https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=183
3. 代码提交与推送
1 | git branch |
1 | git checkout master |
【数据统计】
1 | git checkout -b report |
1. 数据统计概述
数据统计模块主要用于统计电商平台运营过程的中的各种统计数据,并通过直观的可视化方式展示出来,方便相关
运营和管理人员查看。
2. 用户来源数据统计报表
(1) Echarts 第三方可视化库的基本使用
- 打开Vue-cli可视化面板,点击【依赖】菜单,点击安装依赖,选择运行依赖,搜索
echarts
,选中并安装。
https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=188
1 | // 1. 引入Echarts |
1 | <!-- 2. 为Echarts准备一个具备大小(宽高)的Dom --> |
1 | // 生命周期mounted在Dom创建渲染完成之后执行 |
(2) 实现用户来源数据统计报表
- 调用接口获取后台接口数据
- 通过echarts的api实现报表效果
- 合并数据实现折线图鼠标跟随效果(引入Lodash,使用深拷贝)
https://www.bilibili.com/video/BV1mQ4y1r7yZ?p=188
1 | data() { |
3. 代码提交与推送
1 | git add . |
完成于2022年8月23日 22:06
问题解答
Vue当属性是布尔值的时候为什么要加冒号进行数据绑定?
比如下方enterable
这个属性1
2
3<el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false">
<el-button type="warning" icon="el-icon-setting" size="mini"></el-button>
</el-tooltip>或者下方
line-width
属性。是由于数据类型的关系吗?比如Boolean+Number
类型因为是properties
就需要加:,而String
因为是attribute
所以不需要?1
2
3
4
5<tab :line-width="2" active-color="#fc378c">
<tab-item :selected="demo2 === item"
v-for="item in list2"
@click="demo2 = item"></tab-item>
</tab>【参考回答】:差不多就是这个意思,加上冒号会首先将这个属性当作变量解析,没加冒号就直接是字符串。冒号vue的v-bind简写语法。
在反引号中可以用
${}
来包裹变量,实现字符串拼接1
2// this.$http.put(`users/:uId/state/:type`)
this.$http.put(`users/${userinfo.id}/state/${userinfo.mg_state}`)
参考内容
项目开发阶段本地重新运行开始编码的准备流程:
- 运行
phpStudy20161103.exe
。因为我们光让MySQL
正常运行就行,所以点击其他选项菜单,找到服务管理器,选择Apache
,点击停止
。 - 在
vue_api_server
目录(后台API项目)打开CMD终端窗口,输入node app.js
命令把API后端接口项目跑起来。 - 在桌面重新打开一个CMD终端窗口,执行
vue ui
,并把前端项目运行起来。 - 打在 VSCode 中打开相应项目文件夹,这时候就可看着教程视频开始愉快地编码了。
- 运行
VSCode快捷键
Shift
+Alt
+F
:Vue单组件代码格式化Shift
+Alt
+鼠标拖动
:竖直方向选中多行同时输入Ctrl
+D
:添加下文中与选中字符相同的内容(每按一下多选一个)
Vue项目实战(ElementUI,Git,项目上线与发布)(pink老师本名貌似叫刘龙宾,该课程大概是2019年录制的)
插件和依赖有什么区别? - CSDN
- 依赖:运行时开发时都需要用到的包
- 插件:在项目开的发时需要,但是在项目运行时不需要
-
- 原因是vue-cli给的Network地址不一定正确,要输入本机IPV4地址才可以
-
1
2
3
4
5
6
7// Vue中get请求,参数需要使用params
this.$http.get('/operation/customer/question/edits',{ params: { id: 10 } })
// Vue中post请求,参数直接可以是json对象
this.$http.post(`categories/${this.cateId}/attributes`, {
attr_name: this.addForm.attr_name,
attr_sel: this.activeName
})params 是用于拼接 url 的,get 请求传参就是拼到 url 中,而 data 是放在 request body 中的,用于 post 请求
网友的项目和笔记地址:https://gitee.com/wer36/vue_admin