ES6暴露
export { default as login } from "./acl/login"; // {default: {}}
export { default as user } from "./acl/user";
export { default as role } from "./acl/role";
export { default as permission } from "./acl/permission";
export { default as category } from "./category";
export { default as clientUser } from "./clientUser";
// 1. 引入默认暴露的内容重命名为order
// 2. 将order给分别暴露出去
export { default as order } from "./order";
export { default as trademark } from "./product/trademark";
// import { default as order } from './order' // 完整引入
// import order from './order' // 完整引入的简写
// export order;
/*
{
login: { login() {}... },
user,
role,
permission,
category,
clientUser,
order,
trademark
}
*/
两种特殊引入介绍
//当你 import result from './xxx' 引入时
//实际相当于 import { default as result } from './xxx' 写法
import result from "./test/es-module/test1";
import { default as result } from "./test/es-module/test1";
// 引入所有重命名为result
import * as result from "./test/es-module/test1";
默认导出
/* 默认导出,只能有一个 */
const a = 1;
// const b = 2;
export default a;
// export default b
默认暴露实际暴露的内容:
{
default: 1
}
当你 import result from ‘./xxx’ 引入时
实际相当于 import { default as result } from ‘./xxx’ 写法
import result from "./test/es-module/test1";
import { default as result } from "./test/es-module/test1";
console.log(result);
// 引入所有重命名为result
import * as result from "./test/es-module/test1";
console.log(result);
- 若果暴露多个
分别导出
/* 分别导出,可以有多个 */
export const c = 4;
export const d = 5;
import { c, d } from "./test/es-module/test1";
console.log(c, d);
// 引入所有重命名为result
import * as result from "./test/es-module/test1";
console.log(result);
分别暴露实际暴露的内容:
{
b: 2,
c: 3
}
默认暴露和分别暴露可以共存
{
b: 2,
c: 3,
default: 1
}
统一暴露
/* 统一暴露 */
const e = 6;
const f = 7;
export { e, f };
import { e, f } from "./test/es-module/test1";
console.log(e, f);
import * as result from "./test/es-module/test1";
console.log(result);
深度选择器
lang="less"
完整写法
深度选择器 /deep/
lang="sass"
可以省略 {}
可以省略 ;
深度选择器 /deep/ >>>
lang="scss"
完整写法
lang="stylus"
可以省略 {}
可以省略 :
可以省略 ;
scoped
让样式只在当前组件生效
问题:加上scoped,会让设置的子组件样式失效
解决:使用深度选择器
添加scoped以后样式失效
<style lang="sass" scoped>
>>>
<style lang="sass" scoped>
>>>.trademark-table
margin: 20px 0
>>>.trademark-image
width: 100px
>>>.trademark-pagination
text-align: right
>>>.el-pagination__sizes
margin-left: 250px
</style>
/deep/
<style lang="sass" scoped>
/deep/.trademark-table
margin: 20px 0
/deep/.trademark-image
width: 100px
/deep/.trademark-pagination
text-align: right
/deep/.el-pagination__sizes
margin-left: 250px
</style>
.sync/scope/$event
.sync
:count="count"
v-bind:count="count" //单向数据流 / 强制绑定数据
//子组件只能读取不能修改
//问题:子组件需要修改数据
//解决:数据源在哪,更新数据的方法就定义在哪
:count.sync="count" //给子组件传递xxx数据以及更新数据的方法update:xxx
//相当于:
:count="count" @update:count="xxx"
.sync用于父子通信(子向父)
-->
<Test :count="count" :updateCount="updateCount" />
<Test :count.sync="count" />
scope
scope代表所有数据
scope.row 代表当前行所有数据
$event
:page-size.sync="limit" //可以让limit更新变成同步更新
:current-page.sync="page" //可以让page更新变成同步更新
$event
1. 在DOM事件中代表 event
<button @click="handle(123, $event)"></button>
// 触发事件是浏览器的行为,所以$event代表event
2. 在自定义事件中代表 第一个参数
<button @aaa="handle($event)"></button>
假设这样触发自定义事件: this.$emit('aaa', 123, 456);
那么$event就为123(第一个参数)
src图片
//前提允许跨域
action="http://182.92.128.115/admin/product/fileUpload"
目标服务器地址: 代理配置中 (vue.config.js)
//不允许跨域,就使用proxy
action="/dev-api/admin/product/fileUpload"
/dev-api -> request.js 代理
在main.js中定义 Vue.prototype.$BASE_API = process.env.VUE_APP_BASE_API
:action="`${$BASE_API}/admin/product/fileUpload`"
错误解决
input无法自动聚焦
Autofocus processing was blocked because a document's URL has a fragment '#/product/attr/list'.
// input
autofocus
ref="attrInput"
// span
edit(row) {
// 设置属性,让其确认显示哪个,手动聚焦
this.$set(row, "edit", true);
this.$nextTick(() => {
this.$refs.attrInput.focus();
});
},
keyup.enter无法使用
事件修饰符:
.native
专门给组件绑定事件使用的
会给组件中的第一个标签绑定相应的原生DOM事件
@keyup.enter.native="row.edit = false"
响应式数据
直接给对象添加新属性不是响应式数据, 通过this.$set添加的属性才是响应式
this.$set(row, "edit", true);
深度克隆
// 深度克隆:防止对象中对象还存在引用关系
this.attr = JSON.parse(JSON.stringify(attr));
效验与自定义效验
// 效验
rules: {
spuName: [
{ required: true, message: "请输入SPU名称", trigger: "blur" },
],
tmId: [{ required: true, message: "请选择品牌名称" }],
description: [{ required: true, message: "请输入品牌描述" }],
spuImage: [{ validator: this.spuImageValidator, required: true }],
sale: [{ validator: this.saleValidator, required: true }],
},
// 属性选择效验
saleValidator(rule, value, callback) {
if (this.spuSaleAttr.length === 0) {
callback(new Error("请至少选择一个销售属性"));
return;
}
const res = this.spuSaleAttr.some(
(item) => item.spuSaleAttrValueList.length === 0
);
if (res) {
callback(new Error("请至少选择一个销售属性名称"));
return;
}
callback();
},
权限管理
功能介绍
-
菜单管理
- 设置用户可以访问的所有路由菜单和相应的按钮权限
-
角色管理
- 设置角色的访问权限(路由权限和按钮权限)
-
用户管理
- 给创建的用户绑定上某个角色(就会拥有这个角色的权限)
使用某个用户登录,只能访问这个用户对应角色拥有的权限路由和权限按钮
原理实现
- 路由权限
- 在全局路由前置守卫完成的
- 首先从 vuex 中取出 token,判断是否有 token
-
有(代表用户之前登录过)
- 优化:取出用户数据判断是否存在
- 存在说明之前登录成功过且数据没问题,直接放行
- 不存在,说明之前没有登录成功过,需要进行下面步骤
- 需要验证 token 是否有效(是否是真实 token 且没有过期)
- 通过发送请求请求用户数据,此时在拦截器中请求头会携带上了 token,后台会自动进行验证 token 的有效性(try catch)
- 有效
- 得到了用户数据(其中包含了按钮权限数据和菜单权限数据)
- 菜单权限数据需要处理组件,组件需要使用真正的组件,使用递归遍历处理所有菜单
- 通过 router.addRoutes(),来动态添加路由,从而路由生效
- 一上来路由中只有登录、404、首页等路由配置,其他都没有,所以一上来用户只能访问这些页面
- 一旦动态添加路由,用户访问的路由就更多了,从而实现路由的权限控制
- 跳转到 to.path
- 失效
- 清空 token,跳转到登录页面
- 优化:取出用户数据判断是否存在
-
没有(代表用户之前没有登录过)
- 判断要去路由路径(to.path)是否是 login
- 是 调用 next()放行
- 不是 调用 next({ path: ‘/login’ })强行跳转到 login
- 有一个对用户体验更好的小功能 - 重定向
- 跳转到 login 时加上一个 query 参数 redirect,值为 to.path
- 当将来登录成功时,会拿到 redirect 进行跳转,而不是首页
- 有一个对用户体验更好的小功能 - 重定向
- 判断要去路由路径(to.path)是否是 login
-
- 按钮权限
- 在全局路由前置守卫获取用户数据的时候,同时也获取到了按钮权限列表,存在了 vuex 中
- 按钮权限列表就是一个数组。数组有 n 个按钮的权限值(字符串、id)
- 每一个路由组件的功能按钮也会有自己的权限值
- 判断路由组件的功能按钮的权限值在不在请求回来按钮权限列表中
- 在说明有权限。通过 v-if 来显示
- 不在说明没有权限。通过 v-if 来隐藏
- 从而实现按钮权限控制
- 优化:因为很多按钮都有相同的判断,设计一个公共判断的方法 hasBtnPermission,挂载到 Vue 的原型上,从而所有组件实例都可以复用
理解区别2组业务概念
SPU与SKU
SPU: 某个商品所有相关信息的集合, 包括所有可选择的图片, 可选择的平台属性与销售属性
SKU: SPU下确定了图片列表/平台属性与销售属性数据的信息集合, 商品唯一标识
关系: 一个SPU下可以对应多个SKU
平台属性与销售属性
平台属性: 用于(出现)商品搜索的商品描述信息, 包含属性名与一系列的属性值
销售属性: 出现在商品详情界面的商品描述信息, 包含属性名与一系列的属性值
作用域样式与深度样式选择器
不声明scoped, 样式是全局的, 可以匹配影响所有的组件
声明scoped, 就不会影响到子组件内部样式(根标签除外)
标签的变化:
当前组件的所有标签都增加一个data自定义属性: data-v-2e8d0da5 => 用来标识当前组件的
子组件的根标签也会有此data属性, 子标签没有
样式的选择器变化: 选择器的最右边添加了当前组件data属性的选择
.test2 .t2[data-v-2e8d0da5] {
color: red;
}
==> 能匹配当前组件的所有元素
==> 还能匹配子组件根标签, 但不可能匹配其子标签
使用深度选择器, 修改子组件内部任意标签的样式
编码:
.test2 >>> .t22 {
font-size: 30px;
}
.test2 {
/deep/ .t22 {
color: hotpink;
}
}
没用深度
.test2 .t2[data-v-2e8d0da5] {
color: red;
}
用了深度
.test2[data-v-2e8d0da5] .t22 {
font-size: 30px;
}
==> 属性选择从最右边转移到了左边 ==> 此选择对目标元素没有当前data属性的要求 ==> 可以子组件任意标签
修改第三方组件库内部的样式: 使用deep选择器