文章目录
- 前端项目初始化
- 后台项目的环境安装配置
- 登录
- 删掉不要组件
- router
- 目录
- vue实例
- 箭头函数
- 用less语法
- 全局样式表
- 登录样式
- element是按需导入的
- box-sizing
- v-model
- 使用Form Methods
- ref
- methods中的this
- 111登录前的预验证
- return 和 return false
- 如果return false 阻止表单发请求
- axios发请求
- promise
- 解构
- return 还能返回 log呢
- if语句
- message
- 登录成功后 token
- 通过路由导航守卫控制访问权限
- return
- if语句 (!
- 实现退出功能
- replace和push区别
- 传参两种方式
- 终端快捷键ctrl+~
- git
- 主页整体布局
- flex布局
- 子代选择器>
- 行内元素的margin-left是有效的
- 通过接口请求侧边栏菜单数据
- 两次token
- 请求头中带token到服务器
- router-view是占位符
- 通过接口请求侧边栏菜单数据
- 发现home.vue和login.vue算是子组件
- 字体图标
- 布尔值写在标签中必须加:
- 复合选择器
- letter-spacing
- cursor
- 布尔值
- 登录进到home之后,进到组件welcom
- sessionStorage
- box-shadow
- !important语法
- 标签中写数字也要加:
- 路由和请求地址的地址不是一个地址
- 发axios请求get、post 带参数区别
- 发请求,参数拼接,地址是动态的,没请求参数
- 作用域插槽
- methods
- 更新状态
- 实现搜索功能
- 如果是false
- 表单验证(包括自定义验证验证邮箱手机号)
- 预验证 整体表单验证
- 箭头函数之前加aync,一个参数 回调函数前加async
- 修改用户通过id实现
- 报错try catch
- 箭头函数的简单
- width 为啥可以直接写
- table中scope.row是每一列的所有的数据
- 用id做v-for的key值
- 设置最小宽度min-width
- 设置flex属性,让他里面的元素在纵向上居中对齐align-items
- 类的作用,定义上样式谁需要就给哪个标签加上这个类
- pre
- t-tag
- 根据权限id删除对应的权限
- 服了层层嵌套也不管只要在template中,都能是使用scope.row
- 注意有两个变量的这种地址是用反语号表示的
- 服了层层嵌套也不管只要在template中,都能是使用scope.row
- 页面上有个问题就是删除某个权限后,这时如果调用 this.getRoleList()会刷新页面重新渲染,因为删除权限返回的是整个角色的所有权限,这时候我们就可以获取role.children = delr.data;这个role是指这一列的数据scope.row
- scope.row是这一列的数据是个对象,你可以直接用scope.row。还可以通过点击事件把scope.row送出去
- 获取这一列三级权限的id,存在数组中,通过递归获取
- push id的时候不能加“”
- 使用arr.push的时候注意
- 调用element组件里的方法,发现要用ref
- 两个数组合并成一个新数组---展开运算符
- 数组拼接成以,分割的字符串
- 请求参数:通过 `请求体` 发送给后端
- 如果一个id发现是传不过来的
- 获取table这一列的数据,之后展示在对话框中
- element select框
- 对话框关闭之后重置选择项
- slot-scope接收这一行的数据
- 分页
- 扩展功能
- v-if和v-else
- 发现字体图标的颜色是可以变的
- 添加分类对话框
- 如果用get发请求,要用params参数
- 分析一下
- 数组最后一项的索引
- 监听对话框关闭事件,把里面的数据清空
- el-cascader关闭清空
- 表单验证是否为空
- 表单关闭时清空的办法
- 点击叉,确认,取消都触发close事件,如果确定的点击事件做的事情与触发close做的事情相违背呢,这时候会自动以确定事件为首要
- 通过计算属性禁用和启用按钮
- 计算属性
- disabled
- 表单重置
- 表单验证
- 发请求的格式总结
- 数据变
- 错误必须要return出去,不能不处理
- 错误处理.catch(err=>err)
- 可以return同时提示用户
- res.data已经不是从服务器请求来的res.data
- 表格中修改商品名称
- 三元表达式
- v-if v-else
- v-model
- 获取焦点
- \$nextTick
- native
- 数据获取-格式不行-格式修改-修改后直接存到item.good_attrs-再放到data-再展示
- 数据修改data-改data格式-发到服务器
- 不合法字体清除
- 根据索引删除数组中的一项元素
- total初值为0
- v-for
- console
- 表格中日期格式自定义处理 过滤器
- 请求参数时可以在data准备好
- this.$confirm调用返回promise
- 单击添加 通过路由导航的形式跳转到添加商品页面
- 字符串转化成数字
- 表单和面板
- 好多嵌套关系是不可分割的
- 设置间距margin及字体
- return null
- forEach 大写
- v-for的item
- 属性important
- if(){}else if(){}
- 图片上传地址
- token
- el-upload发请求
- 图片上传
- 图片上传参数
- 需要用到的请求参数,都写到一个对象,到时候直接用
- 服务器里的东西获取后,变成对象,再push进数组里
- 删除操作
- 网页图片显示
- 注意注意这个src是动态的
- 安装依赖
- vue.use(...)注册为全局可用的组件
- min-height和height
- 深拷贝
前端项目初始化
https://www.cnblogs.com/jimmy2019/p/13961509.html
创建项目
https://www.cnblogs.com/haitaoli/p/10304193.html
-
安装vue脚手架
-
脚手架创建项目(这就创建好vue项目了)
-
vue create **
-
使用图形化界面 vue ui 命令
以图形化界面创建和管理项目。上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。(d目录输入vue ui )利用babel就可以让我们在当前的项目中随意的使用这些新最新的es6,甚至es7的语法。说白了就是把各种javascript千奇百怪的语言统统专为浏览器可以认识的语言。
- 配置Element-UI组件库
- 3.1可以通过element插件快速集成element-ui
//进入项目文件夹后执行
cd shop
vue add element
按如下操作进行全局引入( Fully import(全局引入))
- 3.2从vue项目搭建的初始开始安装Element的步骤:
首先第一步是下载:打开命令行切换到vue项目根目录然后输入:npm i element-ui
安装完成后vue项目根目录下会多这个文件:package-lock.json 说明已经安装了element-ui插件
第二步骤:引入插件 在项目下的src下的main.js引入js和css如下:
import ElementUI from ‘element-ui’
import ‘element-ui/lib/theme-chalk/index.css’
第三步骤:让vue引用Element ui
Vue.use(ElementUI);
接下来测试能否使用 在src目录下的App.vue 文件template标签中随便写个组件 如图就代表安装且引用成功了
- 3.3图形化界面
点击右上角添加插件,输入插件名字:vue-cli-plugin-element
点击第一个,点击安装vue-cli-plugin-element
点击按需导入
点击任务,选择server,再点击运行,可以查看输出以及控制台
- 配置axios库
点击右上角添加依赖,输入名字:axios
点击第一个
运行时依赖不是开发依赖 - 项目托管到gitee
简易的命令行入门教程:
Git 全局设置:
git config --global user.name " "
git config --global user.email " "
创建 git 仓库:
mkdir vue_shop 建项目文件夹
cd vue_shop 进入项目文件夹
git init
touch README.md
git add README.md
git commit -m “first commit”
git remote add origin https://gitee.com/libudding/vue_shop.git
git push -u origin master
已有仓库(可以在gitee上建好)
cd **(或者是去本地文件目录命令行执行以下)
git remote add origin https://gitee.com/libudding/vue_shop.git
git push -u origin master
- 进到刚刚创建的项目,在项目中打开终端shift+右键,查看一下状态git status git add . git commit -m ’初始化项目‘ (只是在本地操作了仓库,如果需要把仓库上传到码云中需要)执行git remote add origin https://gitee.com/libudding/vue_shop.git
git push -u origin master
后台项目的环境安装配置
安装MySQL数据库(之后导入数据库)
安装Node.js
配置项目相关信息(安装项目后台依赖包)(在项目中打开终端 npm install 安装所有的依赖包)
启动项目(用node命令执行app.js)
使用postman测试后台项目接口是否正常
cls清屏
用node命令执行app.js node app.js
登录
登录技术点
- http无状态
跨域问题:前后端地址不一样,前后端分离必跨域。
跨域:域名 端口号 服务器名全一样叫同源,不一样就叫跨域
客户端服务器端就存在跨域问题了
客户端通过ajax访问服务器端数据
- 通过cookie在客服端记录状态 通过session在服务器端记录状态(前端和后台接口不存在跨域问题)
- 通过token维持登录状态(前端和服务器之间存在跨域问题)
- 登录-token原理(有点像session)
------------token就是记录服务器端和客服端身份校验的------
用户输入用户名密码登录,
服务器验证通过之后生成该用户的token并返回给客户端(token服务器生成,每个用户不一样),
接下来客户端存储该token(token记录了客服端的登录状态),
后续的所有请求都需要携带该token,发送请求,
服务器根据提交过来的token去验证是哪个用户,根据用户操作返回结果。
登录页面布局
- element-ui
el-form 整个是个大表单
el-form-item 用户密码登录重置都是
el-input 用户密码
el-button 登录重置
字体图标
开发新功能前都先创建个分支,开发完成后再合并到master主分支上
创建分支 git checkout -b login(创建了一个子分支,并且通过 checkout 命令切换到了login分支上,-b分支的意思)
查看项目中所有分支 git branch
开发完成后再merge到master分支
删掉不要组件
main.js是入口文件
App.vue是根组件
main.js导入了vue,导入了App.vue根组件,导入了路由,导入了element对应的组件。
new了个vue的实例,通过render函数把App根组件渲染到了页面上,同时也把路由挂载到了vue实例上。
打开App.vue发现template里放的就是整个页面的结构,注意template里要放个根标签。
再打开router.js(路由),将routes数组中的路由规则清除,然后将views删除,将components中的helloworld.vue删除
去导入的里面找对象,即去vue-router里面找对象,对象里面有install()方法,执行vue-router里面
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
]
})
router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
]
const router = new VueRouter({
routes
})
export default router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({
routes:[] 里面每加一个对象就是个路由规则,当用户访问/login,我们通过component属性指定要展示的组件
重定向/login
})
在app.vue中还要放个路由的占位符<router-view></router-view>这样我们通过路由匹配到的组件,都会被渲染到router-view里面去
目录
1、./是当前目录
2、…/是父级目录
3、/是根目录
vue实例
https://blog.youkuaiyun.com/l1996729/article/details/106297700
https://blog.youkuaiyun.com/qq_43672268/article/details/105134001?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242.0
$mount(’#app’) :手动挂载到id为app的dom中的意思。需要注意的是:该方法是直接挂载到入口文件index.html 的 id=app 的dom 元素上的
https://blog.youkuaiyun.com/github_37516320/article/details/78321391
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="app-1">
{{msg}}
</div>
================
<div id="app-2">
{{msg}}
</div>
=================
<div id="app-3">
{{msg}}
</div>
<script>
//在vue.js中,可以使用template给元素添加模板,但是元素中只能有一个根元素,不能出现两个或两个以上的根同级元素。还可以在模板中绑定数据、表达式等。下面利用实例说明如何添加模板
// 创建 Vue 实例,得到 ViewModel
new Vue({
el: '#app-1',
data: {
msg: '这是通过el属性获取挂载元素的outerHTML方式渲染'
}
});
//结论:如果vue实例中有template属性,会将该属性值进行编译,将编译后的虚拟dom直接替换掉vue实例绑定的元素(即el绑定的那个元素);
//注意:template属性中的dom结构只能有一个根元素,如果有多个根元素需要使用v-if、v-else、v-else-if设置成只显示其中一个根元素;
new Vue({
el: '#app-2',
data: {
msg: '这是通过el属性获取挂载元素的outerHTML方式渲染'
},
template: '<div>这是template属性模板渲染</div>'
});
//render
new Vue({
el: '#app-3',
data: {
msg: '这是通过el属性获取挂载元素的outerHTML方式渲染'
},
template: '<div>这是template属性模板渲染</div>',
render: function (createElement) {
return createElement('div',
// 参数2、这里相当于给标签加属性 例如:<div style='color:red,font-size: 14px'></div>
{
//给div绑定样式
style: {
width: '300px',
height: '400px',
color: 'pink'
},
//给div绑定点击事件
on: {
click: () => {
console.log('点击事件')
}
}
},
// 参数3、参数中渲染的标签的子元素数组(可选)
// [
// // 文本节点直接写就可以
// 'text'
// ]
'这是render属性方式渲染。'
);
}
});
</script>
</body>
</html>
箭头函数
https://blog.youkuaiyun.com/qq_32614411/article/details/80897256
类似于匿名函数
x=>xx
相当于
function (x){
return xx
}
function fn2(a, b) {
return a + b
}
var fn1 = function(a, b) {
return a + b
}
var fn1 = (a, b) => {
return a + b
}
(a, b) => {
return a + b
}
h => h(App)
function (h){
return h(App)
}
用less语法
必须配置less相关的loader,打开可视化工具安装依赖
在自己终端输入vue ui即可
或者在终端运行 npm install less-loader@4.1.0 --save-dev(开发依赖)不打dev是运行依赖
less-loader内部是依赖于less这个包的 ,所以 npm install less@3.9.0 --save-dev
全局样式表
main.js引入
登录样式
border-radius: 3px;
position: absolute;
border-radius: 50%;
box-shadow: h-shadow v-shadow blur spread color inset;
box-shadow: 10px 10px 5px #888888;
box-shadow: 0px 0px 10px #888888; 向外扩散10px
盒子里面放盒子再放图片
改这个图片的样式,可以改盒子2(可以在盒子1的样式里嵌套盒子2的样式)
盒子2 .avatar_box {
width: 130px;
height: 130px;
img {
width: 100%;
height: 100%;
}
element是按需导入的
需要哪些组件必须先导入才能正常使用
找到element.js,在这个文件中用到哪些组件就导入哪些组件(main.js入口文件中已经导入了element.js)
Vue.use(Button)把他们注册成全局可用的组件
按需导入并注册
.btns {
display: flex;
justify-content: flex-end;横轴上尾部对齐
}
icon element有提供,因为main.js引用了饿element所以直接用就行
如果觉得icon的图少,可以去阿里图标库下载,下载的css文件导入到main.js(入口文件中)
box-sizing
box-sizing: content-box|border-box
content-box默认值,说好宽多少就是多少,再设置padding值会撑的很大,破坏外部布局
v-model
v-model指令来实现表单元素和数据的双向绑定
input填的数据会自动同步到data中
使用Form Methods
拿到表单的实例对象(即form),直接访问resetFields这个函数resetFields()
如何拿到表单的实例对象:给<el-form>加个ref=“aaa”,现在就可以通过aaa调用resetFields了
ref
https://blog.youkuaiyun.com/qq_38128179/article/details/88876060
ref是vue的获取dom元素的方式
$refs指向ref写的名字,指向vue实例对象
在JavaScript中需要通过document.querySelector("#demo")来获取dom节点,然后再获取这个节点的值。在Vue中,我们不用获取dom节点,元素绑定ref之后,直接通过this.$refs即可调用,这样可以减少获取dom节点的消耗。
<p ref="p">Hello</p>
<children ref="children"></children>
this.$refs.p
this.$refs.children
methods中的this
this指向实例对象(就是那个new vue)
console.log(this) this里有$refs属性
这个aaa是当前元素的实例对象
this.$refs.aaa 指向当前aaa的dom
可以自己试试
111登录前的预验证
之前有个表单的数据验证,是为了验证是不是符合input写的名字字数是2-6
现在这个登录前的预验证:点击登录按钮不应该直接发起登录的网络请求,而是在发起请求之前,先对表单数据进行预验证,当验证通过之后才能发送请求。否则提示表单数据不合法。
发现这两种性质差不多
点击登录之后调用函数进行预验证 在element有 form methods中有个函数
return 和 return false
https://www.cnblogs.com/yalong/p/9811176.html
-
return返回null,起到中断方法执行的效果,只要不return false事件处理函数将会继续执行,表单将提交
-
return false,事件处理函数会取消事件,不再继续向下执行。比如表单将终止提交。
-
return ; return false return true 都会在函数内部阻止程序的执行。
-
只有 return false 会阻止表单的提交。
<form action="index.jsp" method="post" onsubmit="return submitTest();">
<INPUT value="www">
<input type="submit" value="submit">
</form>
<script>
function submitTest() {
return;
}
</script>
如果return false 阻止表单发请求
https://www.jb51.net/article/69483.htm
return;
此情况就是单纯的将控制权转交给主调函数继续执行。
return语句的普通应用并没有特殊之处,最需要注意的是return false的使用。事件处理函数返回false可以阻止默认事件的发生。
if(!valid) return;这里如果valid是false,!valid是true,则会执行return ,会跳出这个函数,不执行下面的。
如果valid是true,则说明填的信息是对的,则不执行return,而会执行后面的。
login() {
this.$refs.loginFromRef.validate(valid => {
// console.log(valid);
if (!valid) return;
this.$http.post(“login”, this.username).then(res => {})
}
axios发请求
对axios进行全局的配置
在main.js中导入这个包import axios from ‘axios’
把这个包挂载到vue的原型对象上面Vue.prototype.$http=axios。这样的话,每个vue的组件都可以通过this直接访问到$http,从而去发起ajax请求。
挂载完这个原型的属性之后,尽量为axios设置请求的根路径
请求写好之后,要开启mysql,运行后台代码node app.js
axios请求返回来的数据会加个壳,所以要解构赋值
promise
如果某个方法的返回值是promise,我们可以用async和await来简化这个promise(不再是promise了,而是个具体的响应对象)。我们可以在这个返回之前加上await(注意await只能用在被async修饰的方法中),所以把紧挨着await的方法修饰成异步的async方法
如果某个方法的返回值是promise,我们加个then也行
// login() {
// this.$refs.loginFromRef.validate(async valid => {
// // console.log(valid);
// if (!valid) return;
// const result = await this.$http.post("login", this.username);
// console.log(result);
// });
login() {
this.$refs.loginFromRef.validate(valid => {
// console.log(valid);
if (!valid) return;
this.$http.post("login", this.username).then(res => {
console.log(res);
});
});
解构
解构赋值 mdn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const result = await this.$http.post(“login”, this.username);
{data: {…}, status: 200, statusText: ‘OK’, headers: {…}, config: {…}, …}
返回的result是个对象,我们可以从result解构赋值出来
const result = await this.$http.post(“login”, this.username);
data属性重命名res
const {data:res} = await this.$http.post(“login”, this.username);
return 还能返回 log呢
if (res.meta.staus !== 200) return console.log(“登录失败”);
console.log(“登录成功”);
这句是满足条件返回登录失败,否则跳过这句执行登录成功
if语句
if (condition)
{
当条件为 true 时执行的代码
}
if (time<20)
{
x="Good day";
}
if (condition)
{
当条件为 true 时执行的代码
}
else
{
当条件不为 true 时执行的代码
}
if (time<20)
{
x="Good day";
}
else
{
x="Good evening";
}
message
这个element,和form有点不一样,除了导入,还需要进行全局挂载(即把message挂载到vue上)(即把 弹窗组件 挂载到vue的原型对象上)
登录成功后 token
- 将登录成功之后的token,保存到客户端的 sessionStorage 中
1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问
1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中(肯定不是localStorage因为这个是长久的)
window.sessionStorage.setItem(‘token’, res.data.token) - 通过编程式导航跳转到后台主页,路由地址是 /home
this.$router.push("/home");
为什么要保存token:
因为项目中除了登录之外的接口,其他接口必须在登录之后才能访问。想访问其他api接口,就要告诉他我登录了。我们可以在访问这些接口的时候提供一下token,因为token是登录成功之后,服务器颁发给我们的登录的令牌(登录的身份认证的信息)。
我们只要在访问这些接口的时候,携带了token,那么一定能访问成功。
所以token有必要保存在客户端。
通过路由导航守卫控制访问权限
如果用户没有登录,不能访问/home(看不到页面内容),如果用户通过url地址直接访问,则强制跳转到登录页面 。
打开开发者工具 Application的session Storage,如果登录了是有token的。选中token,清除,此时就是未登录状态
// 挂载路由导航守卫
router.beforeEach((to, from, next) => {
// to将要访问的路径
// from代表从哪个路径跳转而来
// next()放行 next('/login') 强制跳转
if (to.path === '/login') return next()
// 获取token
const tokenStr = window.sessionStorage.getItem('token')
if (!tokenStr) return next('/lohin')
next()
})
return
执行完retunrn后面的语句之后跳出函数,就不执行后面了,因为后面可能是请求成功之后要做的事情
注意这个return是必须要加的,不能直接写next(),因为这样会不退出函数,不然好几种判断都做有问题
if (res.meta.status !== 200) return this.$message.error(res.meta.msg);
if语句 (!
变量)表示变量为空
if (!token) 表示token是空
实现退出功能
基于token实现退出简单,只要销毁本地token。这样后续请求不携带token,必须重新登录生成一个新的token之后才可能访问页面。
清空token
window.sessionStorage.clear();
跳转
this.$router.push('/login');
replace和push区别
this.$router.replace(’/login’);即使点击返回按钮也不会回到这个页面。
加上replace: true后,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
//声明式:
// 编程式:
router.replace(…)
//push方法也可以传replace
this.$router.push({path: ‘/home’, replace: true})
传参两种方式
https://www.jianshu.com/p/b3f4c1b3aab2
使用query:
使用params:
终端快捷键ctrl+~
git
新建一个项目终端,输入命令‘git status’查看修改过的与新增的文件内容
将所有文件添加到暂存区:git add .
将所有代码提交到本地仓库:git commit -m “添加登录功能以及/home的基本结构”
查看分支: git branch
发现所有代码都被提交到了login分支 将login分支代码合并到master主分支,
先切换到master:git checkout master
在master分支进行代码合并:git merge login
将本地的master推送到远端的码云:git push
推送本地的子分支到码云,先切换到子分支:git checkout 分支名
直接git push不行,因为gitee没有新建login分支
第一次把分支推送到码云:git push -u origin 远端分支名
开发的时候才发现我的新功能,用户权限的功能竟然是在login分支上开发的,不合适,应该放在单独的user分支上,所以我们应该统一的迁移到新分支user上
git checkeout -b user(-b是新建分支,checkout是切换分支)(这一句的意思是先新建个分支user,然后我们再切换到user分支上)
我们切换完毕后,当前代码的修改也切换到了user分支上
接下来把所有的修改操作添加到暂存区git add . git commit -m ‘完成用户列表功能的开发’
码云中并没有新分支的名字,第一次把分支推送到码云:git push -u origin 远端分支名
发现所有代码都被提交到了user分支 将user分支代码合并到master主分支,
先切换到master:git checkout master
在master分支进行代码合并:git merge user
将本地的master推送到远端的码云:git push
写新功能要创建个新分支git checkout -b rights
把本地的rights新分支推送到云端进行保存git push -u origin rights
git push 加不加-u取决于,在远端有没有这个分支,如果没有-u,需要加-u,如果有就不用加了直接git push
orign是云端仓储的别名
因为也要在云端新建一个分支rights,所以要加rights
如果你在本地处于right分支,同时远端也建了right分支,这时你想把代码推进分支right中,你只需要git push 即可
主页整体布局
使用element的Container 布局容器,进行整体布局。
这个有个<el-container>可以替代temeplate内的第一个div标签,实现根元素的效果
每个element提供的组件,组件名称就是类名。比如<el-container>这种element的标签名也是类名
开发者工具看一下,height看一下,分析为什么没撑开页面,heigh设置成100%
flex布局
-
justify-content属性
justify-content属性定义了项目在主轴上的对齐方式。.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
} -
align-items属性
align-items属性定义项目在交叉轴上如何对齐。.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
左右两侧有内容,想到flex布局,要分成两大类标签,flex使得这两类标签左右分开两边。比如用布局用的div包裹一下span和img。而另一边的el-button不需要包裹
子代选择器>
如果您不希望选择任意的后代元素,而是希望缩小范围,只选择某个元素的子元素,请使用子元素选择器(Child selector)。
例如,如果您希望选择只作为 h1 元素子元素的 strong 元素,可以这样写:
h1 > strong {color:red;}
.el-header {
color: #fff;
font-size: 20px;
>div{
font-size: 12px;
}
}
行内元素的margin-left是有效的
行内元素与块级元素的区别之一就是 对于行内元素来说margin只有margin-left和margin-right有效,padding只有padding-left和padding-right有效,《css权威指南》上有写
通过接口请求侧边栏菜单数据
需要授权的 API ,必须在请求头中使用 Authorization
字段提供 token
令牌。
即除了登陆接口,其他接口必须要授权才能正常调用。
怎么授权,即登录成功之后发的token令牌,这个token令牌就是权限认证的资格。
如何在每一个api的请求头中添加 Authorization
字段?
通过axios请求拦截器添加token,保证拥有获取数据的权限
在main.js中,在axios挂载到原型对象之前,设置拦截器。
两次token
一个是通过路由导航守卫判断每个路径的名字,看有没有token,如果没有,router(‘/login’)。若有就继续下一步next()。因为上面是to其他地址的时候遇到路由导航守卫,所以next()后面就是要继续访问其他页面,就是要发请求了。
但是真正发请求的时候
想方位另一个页面,发请求的时候还有一个请求拦截器,在请求发送之前把token放在请求头中,传递给服务器
登录之后返回个token,保存到sessionstorage。
有一次用,是取出来判断是不是空,如果不是空,可以尝试访问home地址
之后一次用,是放到请求头中
请求头中带token到服务器
开发者工具,打开network,输入正确密码点击登录,发起一个login的登录,看一下login的请求头中有没有包括Authorization,发现有,但是值是null,因为发起的是登录请求,在登录期间服务器肯定没有给你颁发令牌。
如果登录之后再去调用其他接口,再次查看请求,就会被发现Authorization的值是个真正的token令牌。
服务器在接收到这个请求,会先判断这个Authorization是否符合要求,如果符合要求才会真正的进行响应,如果不存在,或者不符合要求,会驳回这次请求。
//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息
axios.interceptors.request.use(config => {
//为请求头对象,添加token验证的Authorization字段
console.log(config)
config.headers.Authorization = window.sessionStorage.getItem('token')
return config
})
// axios有个interceptors属性
// request是请求拦截器,通过use函数为请求拦截器挂载个回调函数,config是请求对象
// 在最后必须retun config 固定写法,return表示对请求头做好处理,放出去,发送到服务器
router-view是占位符
<router-view>作为占位,之后组件渲染的位置。
在路由切换时, 切换的是<router-view>挂载的组件, 其他内容不会发生改变.
这个标签相当于占位的作用。之后路径一变,组件渲染出来,替代router-view位置
App.vue是main.js分出去的。现在main.js导入了App.vue,那么App.vue中的<router-view>也相当于在main.js。
rouer的页面 index.js里面导入Home.vue和Login.vue的目的是,建立组件和路由的联系,之后路由一变,相关的组件就渲染到router-view位置了
所以这里都没有用到components,即没有用到组件乱引用,乱设置父子组件的情况。
通过接口请求侧边栏菜单数据
请求侧边栏数据
在created阶段请求左侧菜单数据
created写在methods之前
请求数据虽然是created,但是created肯定不会存太复杂的运算,肯定created中只是个调用,真正请求的发生是在methods中的。
created里面调用methods中的方法也是要加this的 this.getMenuList()
methods方法的前面也是可以加上async的,神奇
methods: {
async getMenuList() {
const result = await this.$http.get(“menus”);
}}加完async之后,它内部的await的地方就能返回不是promise的数据了
if (res.meta.status == 200) this.menuList = res.data;
改为
if (res.meta.status !== 200) return this.$message.error(this.meta.msg);
this.menuList = res.data;
发现home.vue和login.vue算是子组件
虽然没注册,是路由跳转的时候,整个文件通过路由调取整个组件,渲染到占位符上。但是这些文件里面的data是函数形式的 data(){return{} }
字体图标
字体图标就导入个css就能用了,注意css是导入到main.js中了,这样其他组件在用的时候就不用一直导入了,直接用。直接把iconfont类和图标独有的类加到标签中就行了。 或者直接用element中的,也是就把类加到标签就行了。
直接通过设置类名为 el-icon-iconName 来使用即可
布尔值写在标签中必须加:
<el-menu
background-color="#333744"
text-color="#fff"
active-text-color="#409EFF"
v-for="item in menuList "
:key=“item.id”
:unique-opened=“true”
>
还可以简写unique-opened,这样代表,我们希望把unique-opened值,设置为true
不加:不行,是字符串不行的,加了才能识别出是布尔值
??直接unique-opened:true或1??
复合选择器
.el-aside {
.el-menu {
border-right: 0px;
}
letter-spacing
letter-spacing 属性增加或减少字符间的空白(字符间距)。
该属性定义了在文本字符框之间插入多少空间。由于字符字形通常比其字符框要窄,指定长度值时,会调整字母之间通常的间隔。因此,normal 就相当于值为 0。
注释:允许使用负值,这会让字母之间挤得更紧。
cursor
cursor: pointer 鼠标放上去变成小手
布尔值
<el-aside width=“200px’”>
根据一个布尔值确定宽度是多少,不用非要写成isToggle=’true’ 有点if的感觉
<el-aside :width=“isToggle?‘64px’:‘200px’”>
登录进到home之后,进到组件welcom
welcome这个路由设置成home路由的子路由,home这个页面中嵌套显示welcome的子组件
const routes = [
{ path: ‘/’, redirect: ‘/login’ },
{ path: ‘/login’, component: Login },
{ path: ‘/test’, component: Test },
{
path: ‘/home’, component: Home, children: [
{ path: ‘/’, redirect: ‘/welcome’ },
{ path: ‘/welcome’, component: Welcome },
{ path: ‘/users’, component: Users } http://localhost:8080/users 注意这里可以不是home/users
]
}
]
但是没有加component等,这算是啥子组件啊,疑惑
知道点击侧边栏,为啥会渲染到home.vue的<router-view>了吧,这路由就是写在home中的啊,<router-view>也是写在home.vue的
就像app.vue中的<router-view>
home路由里面包含个子路由
/home会直接跳成/welcome
sessionStorage
有些值要存在session中,不然刷新就全没了,比如可以通过sessionstorage存储确定当前高亮
box-shadow
box-shadow: h-shadow v-shadow blur spread color inset;
!important语法
选择器{样式:值!import;}
a{color:green!important;}中间不能加分号;
标签中写数字也要加:
<el-row :gutter=“20”>
路由和请求地址的地址不是一个地址
虽然都是user,但是axios请求地址是服务器地址,有根地址。
路由地址是根据路由确定跳到哪个页面。
发axios请求get、post 带参数区别
login中post
loginForm: {
username: “admin”,
password: “123456”
},
axios.post(“login”, this.loginForm);
user中的get
queryInfo: {
query: “”,
pagenum: 1,
pagesize: 2
}
axios.get(“users”, {params:this.queryInfo})
axios请求的老是这种promise的数据。为了简化数据可以加个then或者async await
发请求,参数拼接,地址是动态的,没请求参数
this.$http.put('users/:uId/state/:type')
this.$http.put(`users/${userInfo.id}/state/${userInfo.mg_state}` );
this.$http.get('users/:id')
this.$http.get("users/" + id);
作用域插槽
使用作用域插槽的目的就是把子数据传过来,由父组件这边决定什么形式渲染出来。
布尔值按需渲染成开关,用到作用域插槽,获取这一行的数据,就可以点出来状态具体的值,按需渲染状态的效果。
slot 插槽
https://blog.youkuaiyun.com/houyibing930920/article/details/89513246
调用组件内部放一个摸板template,写个slot-scope,值随便写,接收数据。上面有个固定属性*.row,就是当前这一行的数据。
<el-table-column prop="mg_state" label="状态">
<template slot-scope="scope">{{scope.row.mg_state}}</template>
</el-table-column>
通过插槽接收scope,通过scope得出来row属性,得到这一行对应的数据
还有“操作”也是要拿到对应的id才能操作用户,修改删除用户,也是需要用这个作用域插槽
别的都是拿到数据,直接放进<el-table-column>里,如果需要进行样式的修改就需要借用template作用域插槽
注意:没有插槽的情况下,在组件标签内一些内容是不起任何作用的
methods
注意在created等中调用methods中的方法也是要加上this的
更新状态
更新状态的时候带着这一行去,因为需要根据这一行的id去数据库修改。
@change新状态的值,指的是这个状态改变之后的值。本地存的这个scope.row都变了。最新的状态
<el-table-column label="状态">
<template slot-scope="scope">
<!-- {{scope.row.mg_state}} -->
<el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch>
</template>
</el-table-column>
改失败,不更新,再搞回来
if (res.meta.status !== 200) {
userInfo.mg_state = !userInfo.mg_state;
return this.$message.error(“设置状态失败”);
}
实现搜索功能
输入的内容数据绑定,点击搜索发请求。
原来发请求的query那个就是关键字搜索,直接点击的时候调用发送用户请求的那个即可。
如果是false
if (!valid)
表单验证(包括自定义验证验证邮箱手机号)
data() {
var emailPass = (rule, value, callback) => {
var reg = /1+[@][0-9a-zA-Z_.-]+([.][a-zA-Z]+){1,2}KaTeX parse error: Expected 'EOF', got '}' at position 93: …入正确的邮箱")); }̲; var mobil…/;
if (reg.test(value)) return callback();
callback(new Error(“请输入正确的手机号”));
};
return {
queryInfo: {
query: “”,
pagenum: 1,
pagesize: 2
},
userList: [],
total: 0,
addDialogVisible: false,
addForm: {
username: “”,
passorword: “”,
email: “”,
mobile: “”
},
addRules: {
username: [
{ required: true, message: “请输入用户名”, trigger: “blur” },
{ min: 3, max: 15, message: “长度在 3 到 15 个字符”, trigger: “blur” }
],
password: [
{ required: true, message: “请输入密码”, trigger: “blur” },
{ min: 3, max: 15, message: “长度在 3 到 15 个字符”, trigger: “blur” }
],
email: [
{ required: true, message: “请输入邮箱”, trigger: “blur” },
{ validator: emailPass, trigger: “blur” }
],
mobile: [
{ required: true, message: “请输入手机号”, trigger: “blur” },
{ validator: mobilePass, trigger: “blur” }
]
}
};
},
预验证 整体表单验证
addUserSuccess() {
this.$refs.addFormRef.validate(valid => {
console.log(valid);
if (!valid) return;
});
}
箭头函数之前加aync,一个参数 回调函数前加async
addUserSuccess() {
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …} = await this.http.post(“users”, this.addForm);
if (res.meta.status !== 201) this.
m
e
s
s
a
g
e
.
e
r
r
o
r
(
"
添
加
用
户
失
败
"
)
;
t
h
i
s
.
message.error("添加用户失败"); this.
message.error("添加用户失败");this.message.success(“添加用户成功”);
this.addDialogVisible = false;
this.getUserList();
});
}
修改用户通过id实现
修改用户不是通过scope.row.emile实现的,而是通过点击修改哪一个的时候,连带着把id传进去。即点击修改按钮的单击事件的时候传进去用户id。
修改按钮button的外面,通过作用域插槽接收到了scope这个数据对象。通过scope.row拿到了这一行对应的数据信息。
报错try catch
deleteUser(id) {
const res = await this.$confirm(
"此操作将永久删除该用户, 是否继续?",
"删除用户",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}
).catch(err => err);
如果一个句子报错了,就在后面.catch捕获前面的所有错误,然后接收错误消息,return出去
catch捕获错误消息,并且把错误消息返回(return)给res进行接收。try catch这个错误之后,返回值打印的是cancle
用户点击取消的时候,catch
箭头函数的简单
.catch(err=>err)
函数体中如果只有一行代码且是return的,可以简写
.catch(err=>{return err})
花括号去掉的同时return也必须去掉
width 为啥可以直接写
table中scope.row是每一列的所有的数据
template里面写的是每一列的数据,需要用template接收数据,重新以自己想要的格式处理。
[{},{},{}] 则scope.row是个对象,里面的每个对象
用id做v-for的key值
设置最小宽度min-width
发现伸缩屏幕时,屏幕的布局被破坏了,为了防止发生这种事,在全局的css里面设置min-width
html,body,#app{
min-width: 800px;
}
当页面不足800时,会强制让页面宽是800,开始保持800的样子生成滚动条
设置flex属性,让他里面的元素在纵向上居中对齐align-items
.el-row {
display: flex;
align-items: center;
}
类的作用,定义上样式谁需要就给哪个标签加上这个类
pre
<template slot-scope="scope">
<pre>{{scope.row}}</pre>
<\template>
乱糟糟的数据
<template slot-scope="scope">
{{scope.row}}
<\template>
t-tag
t-tag 标签有两个事件,一个是点击时触发事件click,一个是关闭时触发的事件close(只要点击小叉就触发第二个事件,即close这个事件)
故可以在t-tag标签上搞个close事件 @close="。。。"
根据权限id删除对应的权限
服了层层嵌套也不管只要在template中,都能是使用scope.row
注意有两个变量的这种地址是用反语号表示的
服了层层嵌套也不管只要在template中,都能是使用scope.row
页面上有个问题就是删除某个权限后,这时如果调用 this.getRoleList()会刷新页面重新渲染,因为删除权限返回的是整个角色的所有权限,这时候我们就可以获取role.children = delr.data;这个role是指这一列的数据scope.row
scope.row是这一列的数据是个对象,你可以直接用scope.row。还可以通过点击事件把scope.row送出去
获取这一列三级权限的id,存在数组中,通过递归获取
最终的目的是获取到最后一项没有children
getRightCheckedId(node, arr) {
if (!node.childre) return arr.push("node.id");
node.children.forEach(item => getRightCheckedId(item, arr));
}
[
{
"id": 30,
"roleName": "主管",
"roleDesc": "技术负责人",
"children": [
{
"id": 101,
"authName": "商品管理",
"path": null,
"children": [
{
"id": 104,
"authName": "商品列表",
"path": null,
"children": [
{
"id": 105,
"authName": "添加商品",
"path": null
}
]
}
]
}
]
}
push id的时候不能加“”
arr.push(node.id)
使用arr.push的时候注意
有时候这个arr是每点开这一列的请求来的数据放进arr里面的,这时我们需要先把arr清空之后再用,不然arr越来越多
监听对话框的关闭事件(注意对话框element本来就可以通过确认取消等关闭,故它关闭事件是专门的就是@close,而不是给每个东西添加监听事件)关闭之后把arr清空。即为arr重新赋值即可 arr=[]
调用element组件里的方法,发现要用ref
this.$refs.addFormRef.validate
两个数组合并成一个新数组—展开运算符
const arr3=[…arr1,…arr2]
数组拼接成以,分割的字符串
arr.join(’,’)
请求参数:通过 请求体
发送给后端
- 请求路径:roles/:roleId/rights
- 请求方法:post
- 请求参数:通过
请求体
发送给后端
参数名 | 参数说明 | 备注 |
---|---|---|
:roleId | 角色 ID | 不能为空携带在url中 |
rids | 权限 ID 列表(字符串) | 以 , 分割的权限 ID 列表(获取所有被选中、叶子节点的key和半选中节点的key, 包括 1,2,3级节点) |
请求体指的是放在地址后面,单独用个对象存的那种
this.$http.post(`roles/${this.roleId}/rights`, { rids: idStr });
例2
- 请求路径:users/:id/role
- 请求方法:put
- 请求参数
参数名 | 参数说明 | 备注 |
---|---|---|
id | 用户 ID | 不能为空参数是url参数:id |
rid | 角色 id | 不能为空参数body参数 |
this.$http.put(
`users/${this.userInfo.id}/role`,
{
rid: this.selectRoleId
}
}
如果一个id发现是传不过来的
传不过来指,比如一个table里面,一个按钮弹出对话框里面,一个对话框点击确定的时候获取用户id
所以我们应该在弹对话框的时候(点击按钮的时候能把scope.row拿到角色id),直接把角色的id保存到data中,供后续使用
这个id不一定是最新的我看,但是也不管是不是老的,我也觉得不会变的
比如后面有个例子是点开分配角色按钮,展示用户角色,数据存到userInfo里去,最后在这个对话框确认分配的角色之后要发送请求,需要用到用户的id,这时候你就可以用那个userInfo里中的id
获取table这一列的数据,之后展示在对话框中
scope.row即可获取这一列数据,可以点击分配用户的时候,把这个数据传递给对话框
获取到这个数据要存到data中,之后展示在对话框页面中
在点击事件中设置这个值存到data中,之后对话框展示可以直接用
element select框
<el-select v-model="selectRoleId" placeholder="请选择">
<el-option
v-for="item in roleList"
:key="item.id"
:label="item.roleName" 这是表现出来的名字 主管
:value="item.id" 这是实际选中主管的话,其实选中的是主管的value值
></el-option>
</el-select>
roleList: [], 这是从服务器端请求来存在这的全部的角色列表
selectRoleId: "" 这就是你真实选中的那一个的值,比如你在框子里选中主管,那么这里存的就是主管的id
对话框关闭之后重置选择项
每次对话框打开都要显示请选择select让我选,而不是是我本身的角色
应该是在对话框关闭时每次清空一下对话框的选择 this.**=’’
@close=" "
还有用户信息,也是关闭对话框之后重置为空对象
this.selectRoleId = “”;
this.userInfo = {};
slot-scope接收这一行的数据
<template slot-scope=“scope”>
分页
关于分页一般参数里面要有两个数据,一个是当前的页码值,一个是当前每页显示多少条数据
<el-pagination
@size-change="handleSizeChange" 切换每页显示多少条的时候会触发并且把最新的数据传进去
@current-change="handleCurrentChange" 当前页数改变时触发,并且把最新的数据传进去
:current-page="queryInfo.pagenum" 当前的页码值
:page-sizes="[1, 2, 3, 5]" 数组,为了供pagesize的切换
:page-size="queryInfo.pagesize" 是每页显示多少条数据
layout="total, sizes, prev, pager, next, jumper"
:total="this.total"
></el-pagination>
data:{
return {
queryInfo: {
query: "",
pagenum: 1,当前的页码值
pagesize: 2 是每页显示多少条数据
},
}
为了显示分页效果,刚开始的时候必须要指定当前的页码值和每页显示多少条数据
扩展功能
扩展功能可以vue可视化面板,安装依赖插件,点击查看详情里面有用法示例
vue-table-with-tree-grid
v-if和v-else
发现字体图标的颜色是可以变的
<template slot="isok" slot-scope="scope">
<i class="el-icon-success" v-if="scope.row.cat_deleted===false" style="color:lightgreen"></i>
<i class="el-icon-error" v-else style="color:red"></i>
</template>
v-else后面啥也不跟哈,不跟条件的
添加分类对话框
分类名称
父级分类
这个添加分类注意一下,上面是必填项目,是自己填的。
如果下面什么也不填的话,就是默认将上面填的作为父级
如果上面填了,同时下面也填了的话,就是将上面的作为下面选择的子级
下面可以选择两个,如果你选择只选择一个家电,那么上面填的就是二级。如果你选择两个家电->彩色电视机,那么上面填的就是三级
如果下面什么也不选就是填的是一级
如果用get发请求,要用params参数
this.$http.get(“categories”, { params:{type: 2 }})
分析一下
<el-cascader
v-model="selectKeys" 里面存的是你选中的父级,是个数据。存的是id值,根据下面的props确定的
:options="parentCartList"里面存的是父级的所用分类数据列表,是你请求过来的
:props="cascaderProps"
@change="parentCartChange"每次下面的复选框一发生改变就触发change事件,同时也会把最新的数据selectKeys存在change事件中。
clearable
></el-cascader>
parentCartChange() {
console.log(this.selectKeys);
}
之前为了展示复选框,获取了2级父级分类,故数据parentCartList里面存的是父级的数据。
selectKeys里面存的是你选中的父级,是个数据,里面存的是父级分类的id值,每次下面的复选框一发生改变就触发change事件,同时也会把数据存在change事件中。
再看一下想发送添加数据分类请求需要用到参数,建一个数据对象
// 添加分类的表单数据对象
addCartForm: {
// 将要添加的分类的名称(就是上面输入的名称是必填的)
cat_name: “”,
// 父级分类的id(若没选的话,默认应该是0)
cat_pid: 0,
// 分类的等级,默认要添加的是一级分类(若没选的话,对应的level也是0)
cat_level: 0
}
如果下面选中一项大家电,则父级分类id是大家电的id
同时你填的那个数据也不是一级分类了,是二级分类,归属于大家电
所以我们就该监听变化的change事件,selectKeys存在change事件中。这个监听的目的就是更新addCartForm,之后好发送请求
只要选中项发生了变化,那么selectKeys数组肯定会变化,在里面判断数组长度如果是1个,如果是2个 即>0 说明有选中数据分类,如果等于0说明没有选中数据分类
cat_pid
选中大家电一个,那么selectKeys是个[1]
选中大家电/电视两个,那么selectKeys是个[1,3] 即无论如何,永远选择最后一项作为父级分类的id
cat_level
如果selectKeys数组是一项,说明选中一个,那么数组包含一项,那么level是1
如果selectKeys数组是两项,说明选中两个,那么数组包含两项,那么level是2
与数组的length是一致的
如果没选的话,那个父级数组肯定是空数组
注意注意这个addCartForm的改变时间是在那个复选框改变的时候触发改变的,而不是点击确认的时候触发改变的。注意close事件应该是点击叉,确认,取消都能触发
数组最后一项的索引
等于数组长度减一
arr[arr.length-1]
监听对话框关闭事件,把里面的数据清空
el-cascader关闭清空
这个通过this.$refs.addCartRef.resetFields()是没办法清空的,需要把这个级联选择器绑定到的数据清空,同时需要把addCartForm这个里面的数据也清空,下一次再选择的时候再重新填。
v-model=“selectKeys” 这个selectKeys清空,不然打开还是显示数据啊
<el-form :model="addCartForm" :rules="addCartRules" ref="addCartRef" label-width="100px">
<el-form-item label="分类名称" prop="cat_name">
<el-input v-model="addCartForm.cat_name"></el-input>
</el-form-item>
<el-form-item label="父级分类">
<el-cascader
v-model="selectKeys"
:options="parentCartList"
:props="cascaderProps"
@change="parentCartChange"
clearable
></el-cascader>
</el-form-item>
</el-form>
其实 <el-cascader>不用放在el-form中,也可以。
表单验证是否为空
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();
表单关闭时清空的办法
注意close事件应该是点击叉,确认,取消都能触发
1、把表单里面引用的数据清空
2、resetFields 对整个表单进行重置this.$refs.addCartRef.resetFields()
点击叉,确认,取消都触发close事件,如果确定的点击事件做的事情与触发close做的事情相违背呢,这时候会自动以确定事件为首要
本例中不相同,这是复选框的change事件触发的原始数据的修改
是close触发的把数据再修改为原始状态
通过计算属性禁用和启用按钮
计算属性
methods中可直接用计算属性,this.cartId 注意不加()
computed{
cateID() {
// 证明选中了三级分类
if (this.selectKeys == 3) {
return this.selectKeys[2];
}
// 如果没有return出去,证明没有选中三级分类
return null;
证明没有这个id值;
}
}
计算属性还可以直接在methods方法中调用,比如一个请求的发送就需要这个处理后的数据,那么就可以直接调用this.cateID
想不到的是在这里调用的时候也是不需要加()
disabled
1.HTML写法
禁止某input控件。
【注意】
【1】disabled包含字母d
【2】“disabled”而不是true或false。
2.javascript操作
【读取】
document.getElementById(“testButton”).disabled;
返回结果禁止时为true,使能时为false。
【设置】
document.getElementById(“testButton”).disabled = false;
设置禁止为true,使能为false。
<el-button
type="primary"
size="mini"
:disabled="isCheckedCate"
@click="addDialogVisible = true"
>添加参数</el-button>
表单重置
只要触发了关闭对话框,就将表单重置一下,防止下次打开还是上一次验证的情况
表单验证
点确定-预检验-通过发请求
发请求的格式总结
post和put不写params,get要写
this.$http.post{'baidu',{ }}
this.$http.put{'baidu',{ }}
this.$http.get{'baidu',{params:{a:b}}
数据变
vue中数据变化,渲染表格的页面会自动变
错误必须要return出去,不能不处理
element中警告提示删除框,如果按了取消删除,要捕获错误的 …catch(err=>err)
捕获之后return出去
错误处理.catch(err=>err)
将错误return出去
可以return同时提示用户
if (res.meta.status !== 200)
return this.$message.error("获取分类数据失败");
按我的思路这个return和这句也能写到一起,不清楚
handleChange(value) {
if (this.addForm.goods_cat.length !== 3) {
this.addForm.goods_cat = [];
return;
}
}
res.data已经不是从服务器请求来的res.data
从服务器请求数据,之后可对这个数据修改,再存到data中
// console.log(res.data);
res.data.forEach(item => {
item.attr_vals = item.attr_vals ? item.attr_vals.split(" ") : [];
// 这个相当于把从服务器获取的数据修改后,还放到服务器请求的数据 的原数据里面
item.inputVisible = false;
item.inputValue = "";
// 控制按钮的文本框切换显示与内容 是每个框子单独控制的
//这就是把请求来的数据给改了,添加新属性了
});
this.paramsList = res.data;
//把服务器请求的数据,做过以上修改之后,存到data中。
//这是的res.data已经不是从服务器请求来的res.data了
if (this.activeName == "many") {
this.manyTableData = res.data;
// console.log(this.manyTableData);
} else {
this.onlyTableData = res.data;
// console.log(this.onlyTableData);
}
表格中修改商品名称
表格中点了编辑按钮,首先会把原商品名反映在对话框中。
我们如何取出原商品名,并且展示在对话框中的呢,不是scope.row.good_name,即不要用渲染整个表格的那个旧数据。
而是把id传出去,根据id查询最新的参数名,然后获取的整个响应信息放在data的变量中,因为可能之后发编辑后的信息时会需要。
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button type="primary" @click="showEditDialog(scope.row.attr_id)">编辑</el-button>
<el-button type="danger" @click="removeParams(scope.row.attr_id)">删除</el-button>
</template>
</el-table-column>
三元表达式
item.attr_vals = item.attr_vals ? item.attr_vals.split(" ") : []
有点那种if(!item.attr_vals) 的感觉
这里如果item.attr_vals是空字符串,那么"".split(" “) 空字符串用空格分割,结果返回[” "]一个空字符串数组,这个空字符数组之后也会尝试渲染的,这是不对的,所以我们要把它变成空数组。所以借助三元表达式
v-if v-else
点按钮, e-tag消失,文本框展示
文本框失去焦点或者按enter(keyup.enter),文本框消失,el-tag展示
v-if v-else布尔值控制两者切换
v-model
v-model实现数据双向绑定,文本框的值绑定到v-model后面的变量上
获取焦点
<el-input
v-if="scope.row.inputVisible"
v-model="scope.row.inputValue"
ref="saveTagInput"
@keyup.enter.native="handleInputConfirm(scope.row)"
@blur="handleInputConfirm(scope.row)"
></el-input>
<el-button
v-else
@click="showInput(scope.row)"
>+ New Tag</el-button>
showInput(row) {
row.inputVisible = true;
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
});
}
$nextTick
$nextTick 方法的作用,当页面元素被重新渲染之后(以上点了显示input之后),才会调用回调函数的代码
比如你想让文本框获得焦点,就必须在页面重新渲染之后才可以执行这行代码。否则,你点击按钮时,文本框的显示被设为true,但是页面上文本框元素还没有渲染出来,这是没有获取到input
native
<el-input
class="input-new-tag"
v-if="scope.row.inputVisible"
v-model="scope.row.inputValue"
ref="saveTagInput"
size="small"
@keyup.enter.native="handleInputConfirm(scope.row)"
@blur="handleInputConfirm(scope.row)"
></el-input>
数据获取-格式不行-格式修改-修改后直接存到item.good_attrs-再放到data-再展示
数据修改data-改data格式-发到服务器
从服务器获取参数,获取的是个字符串形式的,有空格,我们取到之后想要分开展示每条。先split分隔开,之后我们把分成数组的,添加到data中。
之后我们修改,修改data,但是data只是本地渲染的,服务器刷新添加不上,这时候还需要我们把我们添加的,合并之后发送到服务器
不合法字体清除
if (row.inputValue.trim().length === 0) {
row.inputValue = "";
根据索引删除数组中的一项元素
根据v-for的索引,从数组中把对应的那一项删除。同时通过scope把删除后变了的数组一并修改到福区群
<el-tag
v-for="(item,i) in scope.row.attr_vals"
:key="i"
closable
@close="handleClose(i,scope.row)"
>{{item}}</el-tag>
handleClose(i, row) {
row.attr_vals.splice(i, 1);
this.saveAttrVals(row);
这个函数就是将对attrVals的操作保存到数据库中 row row
}
根据索引删除数组中的一项元素
splice会修改原数组的,这样row.attr_vals数组就少了一个元素,这时表格中绑定的变量那里也会少这个元素,之后服务器发起请求就行
total初值为0
v-for
数组 i 是索引,item是内容
<el-tag
v-for="(item,i) in scope.row.attr_vals"
:key="i"
closable
@close="handleClose(i,scope.row)"
>{{item}}</el-tag>
console
想演示单独的代码,可以在控制台用console
表格中日期格式自定义处理 过滤器
从数据库取出来的数据是毫秒的形式,想显示标准格式。在main.js注册一个全局的过滤器,通过构造函数vue定义
Vue.filter('dataFormat', function (originaVal) {
const dt = new Date(originaVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDay() + '').padStart(2, '0')
const h = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const s = (dt.getSeconds() + '').padStart(2, '0')
// return `yyyy-mm-dd hh:mm:ss`
return `${y}-${m}-${d} ${h}:${mm}:${s}`
})
过滤器的调用挺搞笑的
<el-table-column prop="add_time" width="140px" label="创建时间">
<template slot-scope="scope">
{{scope.row.add_time | dataFormat}}
</template>
</el-table-column>
请求参数时可以在data准备好
设置请求参数时可以在data中完全设好,之后请求时{params:this.queryInfo}
data() {
return {
queryInfo: {
query: "",
pagenum: 1,
pagesize: 10
}
this.$confirm调用返回promise
单击添加 通过路由导航的形式跳转到添加商品页面
跳转到指定的路由页面
this.$router.push("/goods/add");
现在这个路由并没有被监听 我们也没有创建指定组件显示对应的界面
字符串转化成数字
想要将一个长得像数字的字符串转化为数字 ‘0’—>0 '1—>1
最简单的方式就是减0 active-0
:active=“active - 0”
需要字符串的地方和需要数字的地方,需要用到一个变量,最好把这个变量设置成字符串,之后那个可以通过:active="active - 0"转化为数字
表单和面板
一个商品的信息拆分成了五个面板,每个面板里面只维护当前商品的部分信息,只有将五个面板合起来才是完整的商品信息数据。所以,应该在这五个面板外面统一的用一个form进行包裹
好多嵌套关系是不可分割的
不能中间突然加一个其他的标签,比如
<el-tabs>
<el-tab-pane>
设置间距margin及字体
发现设置间距margin一般直接通过组件,在element中,组件名就是类,直接用这个类。
而设置字体,如果在这个组件名类里设置不成功的话,可以去到控制台看看详细的类是什么,然后设置字体。
return null
如果没选中三级分类,就返回null
computed: {
cartId() {
if (this.addForm.goods_cat === 3) return this.addForm.goods_cat[2];
return null;
}
}
forEach 大写
v-for的item
item很厉害[{attr_name:’’,attr_vals:[]},{},{}] 你v-for的话,item的每一项就是{},他会自动搞三个el-form-item 每一项都差不多。下面再去遍历每一项的attr_vals数组里面的东西,根据个数渲染出多少个复选框
<el-form-item v-for="item in manyTableData" :key="item.attr_id" :label="item.attr_name">
<el-checkbox-group v-model="item.attr_vals">
<el-checkbox
:label="itemCheck"
v-for="(itemCheck,i) in item.attr_vals"
:key="i"
border
></el-checkbox>
</el-checkbox-group>
</el-form-item>
属性important
/* margin: 0 !important; */
margin: 0 1px 0 0 !important;
if(){}else if(){}
图片上传地址
action是要上传的后台的地址
如果地址只写一个upload,代表说,你要把图片上传到8080这个服务器里面的goods/add/upload中 。
这样是不对的,因为我们后台服务器的接口不是8080,而是8888。
去main.js找到之前配置的请求的根路径,那个才是后台服务器的根路径http://127.0.0.1:8888/api/private/v1/。
之后在这个根路径后面补全上传图片的地址http://127.0.0.1:8888/api/private/v1/upload
当前页面网页 http://localhost:8080/goods/add
<el-upload action="upload">不对
<el-upload action="http://127.0.0.1:8888/api/private/v1/upload">
on-preview是处理图片预览效果的事件。在这个事件里面处理预览的业务逻辑。
功能:1、点上传图片按钮,会出现图片文件选择对话框,选中图片之后,会出现你选中的图片的缩略图及名字。
2、图片名称会弹出选中图片的预览窗口,你点击名称的时候会触发一个事件,就是on-preview事件,在这个事件中处理预览的业务逻辑。
3、点击叉号移除上传好的图片时,触发on-remove
token
除了登录接口之外,其他接口都是有权限的,都需要在调用这些接口的同时提供token。之前在main.js中通过axios.request拦截器,为咱们的请求头都挂载了Authorization字段,字段的值都是token。用axios发请求都自己加token。
token在sessionStorage中存着呢。 config.headers.Authorization = window.sessionStorage.getItem(‘token’)
el-upload发请求
el-upload没有用axios发ajax请求,所以el-upload请求出错,出错原因,没带token。
el-upload内部自己封装了一套ajax,没携带Authorization字段,所以报了无效token的错误。
我们要想办法为el-upload指定token。
图片上传
图片上传完成后,并不代表你的操作就结束了,只是代表服务器存储了这张图,但是图片相关信息还要存储到添加表单中才算完事。
图片上传参数
on-success 文件上传成功时的钩子 function(response, file, fileList)
:on-success=“handleSuccess”
handleSuccess(response) {}
需要用到的请求参数,都写到一个对象,到时候直接用
这个对象里面还存着表格用的参数
addForm: {
goods_name: "",
goods_price: 0,
goods_weight: 0,
goods_number: 0,
goods_cat: [],
pics: []
}
服务器里的东西获取后,变成对象,再push进数组里
handleSuccess(response) {
console.log(response);
const picInfo = { pic: response.data.tmp_path };
this.addForm.pics.push(picInfo);
console.log(this.addForm);
}
删除操作
1、获取将要删除图片的临时路径
2、从pics数组中,找到这个图片对应的索引值(根据临时路径获取索引)findIndex
3、调用数组的splice方法,把图片信息对象,从pics数组中移除
网页图片显示
只知道图片的网络路径,想让他显示在页面上,只需要
<img :src="previewPath" class="previewImg" />
想让图片刚好充满全屏,可以加个class类,之后给类加上样式,width:100%
注意注意这个src是动态的
安装依赖
- -S,–save 安装包信息将加到dependencies(生产阶段的依赖)打包计算在内
npm install --save 或
npm install -S - -D, –save-dev 安装包信息将加到devDependencies(开发阶段的依赖)打包不计算在内
npm install --save-dev
npm install -D
vue.use(…)注册为全局可用的组件
min-height和height
.ql-editor{
min-height: 300px;
}
深拷贝
问题引出
这里需要的goods_cat是数组形式的,请注意这里是v-model,
一直一直双向绑定的,所以你想要把add时数据变成数组形式是不正常的
<el-cascader
v-model="addForm.goods_cat"
:options="cateList"
:props="cateProps"
@change="handleChange"
clearable
></el-cascader>
而添加商品的button按钮这里需要的是字符串形式的数据,
下面这样肯定不对,你直接把数据改成字符串形式,上面怎么用
this.addForm.goods_cat=this.addForm.goods_cat.join(',)
一个希望这个数据是数组,一个希望是字符串,所以我们用深拷贝。在操作goods_cat之前,我们把addForm这个对象做一下深拷贝。
深拷贝指的是,把这个addForm对象,原封不动的拷贝出新的一份,和原对象之间是互不相干的。
在下面拼接之前,先把addForm深拷贝一份。
方法:用到包lodash,包上边提供一个很好用的方法cloneDeep(obj) obj是要拷贝的对象
join拼接的时候用的是复制的一项
//将addForm进行深拷贝,避免goods_cat数组转换字符串之后导致级联选择器报错
const form = _.cloneDeep(this.addForm)
//将goods_cat从数组转换为"1,2,3"字符串形式
form.goods_cat = form.goods_cat.join(",")
0-9a-zA-Z_.- ↩︎