尚品汇(后台管理系统)学习笔记

 笔记

v-model实现父子组件通信:
  原始dom事件oninput,他经常结合表单元素一起使用,当表单原始内容发生变化时就触发一次回调
  v-model实现原理:value和input事件实现的,可以通过v-model实现父子组件的数据同步
  1.  在vue2中可以通过value与input事件实现v-model功能
      例如:
        <input type="text" :value="num" @input="num = $event.target.value" />
        data() {
          return {
            num: "1",
          };
        },     这样和 <input type="text" v-model="num">效果一样
  2. 实现父子组件数据同步
       在父组件中:
          <hello-world :value="num" @input="num = $event"></hello-world>
          给子组件标签传递一个value属性值为num的值
          绑定一个input自定义事件,触发后让num的值等于传过来的值
          可以简化为:
          <hello-world v-model="num"></hello-world>
          data() {
            return {
              num: "1",  设置num
            }
          }
       在子组件中:
          <input type="text" :value="value" @input="$emit('input',$event.target.value)"/>
          在子组件的input标签中设置value值为父组件传过来的值,
          绑定input原生事件,当触发input原生事件时触发绑定的input自定义事件,
          同时把子组件中input标签的value的值传过去
          props: ["value"] 接收传过来的属性

--------------------------------------------------------------------------------
属性修饰符sync(组件通信方式之一):
  与v-model类似
  例如:
     <hello-world :money="money" @update:money="money = $event"></hello-world>
  代表父组件给子组件传递money,给子组件绑定一个自定义事件update:money
  可以简写:
     <hello-world :money.sync="money"></hello-world>
  也代表父组件给子组件传递money,给子组件绑定一个自定义事件update:money
     <button @click="$emit('update:money', money-100)">
  子组件触发自定义事件,第二个参数等于包装成一个回调函数给父组件传递过去了
 
--------------------------------------------------------------------------------
$attrs与$listeners
  他们两者是组件实例的属性
  1.  $attrs可以获取到父组件给子组件传递props
      对于子组件来说,父组件的数据可以利用props接收,但要注意如果子组件用props接收了属性,
      在$attrs中就获取不到了
      <xxx v-bind="$attrs"></xxx> 这样可以把$attrs上父组传过来的数据传给xxx
      注意:这里必须要用v-bind不能用简写: 
  2. $listeners可以获取到父组件给子组件传递的自定义事件
     <xxx v-on="$listeners"></xxx>这样可以把$listeners中父给子传过来的自定义事件全绑定到xxx上
     注意:这里必须用v-on不能用简写@
    
--------------------------------------------------------------------------------
$children与$parent
   1. $children组件实的属性,可以获取到当前组件的全部子组件(数组)
      尽量不要以索引的形式写  this.$children[0] 
      因为如果子组件数量过多无法确定哪个是第一个,$children并不保证顺序
   2. $parent组件实例的属性,可以获取到当前子组件的父组件,进而可以操作父组件的数据与方法
      如果有多个父组件可能会误操作,就不建议使用

--------------------------------------------------------------------------------
后台管理系统:可以让用户通过一个可视化工具,可以实现对于数据库进行增删改查的操作
            而且需要注意,根据不同的角色(老板,员工),看到的,操作内容是不同的
            例如:老板可以操作(产品的上架,产品的下架,查看员工个人业绩等)
                 员工可以查看个人业绩
            对于后台管理系统项目,一般而言不需要注册

--------------------------------------------------------------------------------
模板介绍
简洁版: https://github.com/PanJiaChen/vue-admin-template
加强版: https://github.com/PanJiaChen/vue-element-admin
下载下来后先npn i把依赖包安装上
npm run dev运行(要把node版本降到16版本,16以上用不了)

build:   ----index.js webpack配置文件【很少修改这个文件】
mock:    ----mock数据的文件夹【模拟一些假的数据mockjs实现的】,因为咱们实际开发的时候,利用的是真实接口
node_modules:  ------项目依赖的模块
public:  ------ico图标,静态页面,publick文件夹里面经常放置一些静态资源,而且在项目打包的时候webpack
              不会编译这个文件夹,原封不动的打包到dist文件夹里面
src:
    -----程序员源代码的地方
    ------api文件夹:涉及请求相关的
    ------assets文件夹:里面放置一些静态资源(一般共享的),放在aseets文件夹里面静态资源,在 
          webpack打包
          的时候,会进行编译
    ------components文件夹:一般放置非路由组件获取全局组件
    ------icons这个文件夹的里面放置了一些svg矢量图
    ------layout文件夹:他里面放置一些组件与混入
    ------router文件夹:与路由相关的
    -----store文件夹:一定是与vuex先关的
    -----style文件夹:与样式先关的
    ------utils文件夹:request.js是axios二次封装文件
    ------views文件夹:里面放置的是路由组件
App.vue:根组件
main.js:入口文件
permission.js:与导航守卫先关、
settings:项目配置项文件
.env.development 开发环境下的配置文件
.env.producation  上线环境下的配置文件
.env.staging  测试环境的配置文件  这三个文件里的数据可以通过webpack对外暴露的process.env拿到

--------------------------------------------------------------------------------
在css中选择路径时使用@要在@前面加个~

--------------------------------------------------------------------------------
后台管理系统API接口在线文档:
http://39.98.123.211:8170/swagger-ui.html
http://39.98.123.211:8510/swagger-ui.html

--------------------------------------------------------------------------------
完成登录业务:
    静态组件完成
    书写API(换成真实的接口)
    axios二次封装
    换成真实接口之后需要解决代理跨域问题(解决代理跨域问题)

在vue.config.js中把devServer中mkck的假数据换成跨域代理
 proxy: {
     "/dev-api": {
       target: "http://39.98.123.211:8170",
       pathRewrite: { "^/dev-api": "" },
     },
   },

在utils/requests.js中的:
把config.headers["X-Token"] = getToken()
    换成:config.headers["token"] = getToken()
    这里的token是后台设置的
把if (res.code !== 20000)
    换成: if (res.code !== 20000 && res.code != 200)

--------------------------------------------------------------------------------
配置相同前缀不同target的代理:
  proxy: {
      "/dev-api/admin/acl": {// 匹配所有以 '/dev-api/admin/acl'为前缀的请求路径
        target: "http://39.98.123.211:8170/", // 代理目标的基础路径
        pathRewrite: { "^/dev-api": "" }, //让代理服务器拿到的地址不带设置的前缀,
                                             如果服务器地址本身带就不写
      },
      "/dev-api/admin/product": {// 匹配所有以 '/dev-api/admin/product'为前缀的请求路径
        target: "http://39.98.123.211:8510/", // 代理目标的基础路径
        pathRewrite: { "^/dev-api": "" }, //让代理服务器拿到的地址不带设置的前缀,
                                             如果服务器地址本身带就不写
      },
    },
1.把原来的/dev-api改为/dev-api/admin/acl
2.复制一份该跨域配置,改为/dev-api/admin/product
3.将新的配置的端口号从8170改为8510
4.其实就是针对登录和获取trademark分别做了代理

--------------------------------------------------------------------------------
修改品牌信息时删不了自带的,只能删自己或者其他人添加

--------------------------------------------------------------------------------
不能在data中用this.xxx收集data本身的数据
因为对象存储数据是无序存储

--------------------------------------------------------------------------------
如果需要深拷贝一个对象用xxx={...xxx},但这个对象的层级很深(对象套数组,数组套对象)
那么对象中的基本数据类型会被深拷贝,对象中的引用数据类型会被浅拷贝
1. 可以使用xxx= JSON.parse(JSON.stringify(xxx)) 进行深拷贝
2. 可以使用lodash中的深拷贝
   import cloneDeep from "lodash/cloneDeep" 按需引入深拷贝 
   要拷贝的对象= cloneDeep(被拷贝的对象);

--------------------------------------------------------------------------------
老版本气泡确认框的点击确认按钮的事件名称叫onConfirm
气泡确认框的<template>代码里必须写scope="{ row }"不然没效果

--------------------------------------------------------------------------------
接口(注意接口不要有空格):
/admin/acl/index/login    post 登录
/admin/acl/index/info    get 获取用户信息
/admin/acl/index/logout    post 退出登录
/admin/product/baseTrademark/{page}/{limit}   get 品牌列表
  post 添加品牌
参数:{
  logoUrl: "string", 品牌logo
  tmName: "string"   品牌名字
}
/admin/product/baseTrademark/update  put  修改品牌
参数:{
  id:0,
  logoUrl: "string", 品牌logo
  tmName: "string"   品牌名字
}
/admin/product/fileUpload   post  上传图片接口
/admin/product/baseTrademark/remove/{id}  delete  删除品牌
/admin/product/getCategory1  get 获取三级联动一级分类
/admin/product/getCategory2/{category1Id}   get 获取三级联动二级分类
/admin/product/getCategory3/{category2Id}   get 获取三级联动三级分类
/admin/product/attrInfoList/{category1Id}/{category2Id}/{category3Id}  get 获取平台属性
/admin/product/saveAttrInfo  post  添加,修改属性与属性值
参数:{
      attrName: "",   属性名
      attrValueList: [  属性名中的属性值
        {
         attrId: this.保存数据的对象.id,  属性名的id
         添加属性和属性值时,因为attrId是服务器返回的数据,添加属性值时没有attrId,attrId要写 
         undefined,
         修改属性和属性值时也可以添加属性值,这时服务器已经返回attrId,所以要加上服务器返回的 
         id,用this.保存数据的对象.id当添加时因为服务器没有返回id所以是undefined,修改时服务器 
         返回了id就是这个返回id的值
         valueName: ""    属性值
        }
       ],
      categoryId: 0,  category3Id
      categoryLevel: 3,  区分是几级id,都是3
     }
添加或修改完保存时
如果用户添加了很多属性值,且属性值为空的不应该提交给服务器
提交给服务器数据中不应该出现flag字段
/admin/product/deleteAttr/{attrId} delete  删除平台属性
/admin/product/{page}/{limit}    get  spu列表数据   
参数:
 page代表第几页  limit代表一页几个数据 
 还有一个参数category3Id(三级分类的id)  要用params参数带过去
 注意:params参数以对象形式传过去, params: { category3Id },
/admin/product/baseTrademark/getTrademarkList    get spu品牌数据
/admin/product/baseSaleAttrList    get 获取spu平台中全部的销售属性
/admin/product/getSpuById/{spuId}   get  获取某一个spu信息
/admin/product/spuImageList/{spuId} get 获取spu图片
/admin/product/saveSpuInfo   post  添加spu
/admin/product/updateSpuInfo  post  修改spu信息
添加和修改参数一样,区别是添加没有id,修改有id
注意1:保存时新添加的图片没有imgName和imgUrl要加上
新加上的图片的imgUrl不能用url要用response里的data
注意2:修改spu时照片墙照片不显示要用:file-list=""
参数:
{
  category3Id: 0, //三级分类id
  description: "",//描述
  tmId: 0, //品牌id
  spuName:"",//spu的名字
  id: 0,//添加参数不带id  通过有没有id判断新添加还是修改
  spuImageList: [  //照片墙数据
    {
      imgName: "",
      imgUrl: "",
    }
  ],
  spuSaleAttrList: [  //平台销售属性与属性值信息
    {
      baseSaleAttrId: 0,  // 属性id
      saleAttrName: "", //  属性名
      spuSaleAttrValueList: [  //属性值列表
        {
          baseSaleAttrId: 0, //销售属性的id
          saleAttrValueName: "", //销售属性值的名称
        }
      ]
    }
  ],
}
/admin/product/deleteSpu/{spuId} DELETE 删除spu
/admin/product/spuImageList/{spuId}  get  获取sku图片列表
/admin/product/spuSaleAttrList/{spuId} get  获取sku销售属性 
/admin/product/attrInfoList/{category1Id}/{category2Id}/{category3Id}
get 获取sku平台属性
/admin/product/saveSkuInfo  post  sku保存  注意:这里接口应该有问题,重量不填保存不了
参数:
{
  category3Id: 0,
  spuId: 0,
  tmId: 0, //tmid在传过来的row身上
  price: 0, //价格
  skuName: "",
  weight: "",
  skuDefaultImg: "",//默认图片
  skuDesc: "",
  skuAttrValueList: [ //平台属性列表
    {
      attrId: 0, //平台属性id
      valueId: 0, //平台属性值的id
    }
  ],
  skuImageList: [//图片列表
    {
      imgName: "",
      imgUrl: "",
      isDefault: "", //值为1为默认,为0是设置默认
      spuImgId: 0
    }
  ],
  skuSaleAttrValueList: [ //销售属性列表
    {
      saleAttrId: 0,//销售属性id
      saleAttrValueId: 0,//销售属性值的id
    }
  ],
}
/admin/product/findBySpuId/{spuId}  get sku列表展示
/admin/product/list/{page}/{limit}  
get sku模块数据  每条数据中有个isSale属性,1为上架,0为下架
/admin/product/onSale/{skuId}  get 上架
/admin/product/cancelSale/{skuId}  get  下架
/admin/product/deleteSku/{skuId}   删除sku
/admin/product/getSkuById/{skuId} get  sku详情数据
/admin/acl/user/{page}/{limit}  get  用户管理数据
/admin/acl/user/{page}/{limit}  get 用户管理点击查询后获取数据
参数: 带一个params参数username(表示要查询的值)
/admin/acl/user/save post 新增管理用户
参数:
{
   nickName:"",//用户名
   password:"", //用户密码
   username:"", //用户昵称
}
/admin/acl/user/remove/{id} delete  删除管理用户  注意:批量删除的接口有问题也用这个
/admin/acl/user/toAssign/{userId}  get  根据用户获取角色数据
/admin/acl/user/doAssign  post  根据用户分配角色
参数:用params带
     userId 用户id ,
     roleId [] 选择了的角色的id们
/admin/acl/user/update  put  修改用户
参数:
{
   nickName: "", //用户名
   username: "", //用户昵称
   id: "", //用户id
},
/admin/acl/role/{page}/{limit}  get 获取角色数据
/admin/acl/role/remove/{id} delete  删除角色
/admin/acl/role/{page}/{limit}  get 角色管理点击查询后获取数据
参数: 带一个params参数roleName(表示要查询的值)
/admin/acl/role/save  post  角色管理新增角色
参数:带一个data参数roleName(表示要添加的名字)
/admin/acl/role/batchRemove   delete 角色管理批量删除
参数:  data参数,一个数组数组的每一位是每个角色的id
/admin/acl/role/update  put  修改角色
参数:{id: "", roleName: "修改后的名字"}
/admin/acl/permission/toAssign/{roleId}  get  根据角色获取菜单,分配权限页面的菜单
/admin/acl/permission/doAssign   post 给角色分配权限
参数:{roleId:"",permissionId:[分配权限的所有id,xxx,xxx]}  params参数
/admin/acl/permission  get  获取菜单
/admin/acl/permission/save post  新增菜单
参数:
   {code:"权限值",level:"几级数组",name:"名称",pid:"",toCode:"跳转权限值",type:""}
   pid:不能用row的pid,要用row的id
/admin/acl/permission/update  put 修改菜单
参数:row
/admin/acl/permission/remove/{id}  delete  删除菜单



--------------------------------------------------------------------------------
Object.assign(): es6新增的方法可以合并对象
   this._data可以操作data中的响应式数据
   this.$options可以获取配置对象,配置对象的data函数执行,返回的响应式数据为空
   Object.assign(this._data,this.$options.data())


--------------------------------------------------------------------------------
深度选择器:
   如果使用了scoped那么子组件或第三方子组件的根标签会添加上相应的样式,更深层次不会
   如果想使用scoped的同时又想让父组件中写的样式对子组件或第三方子组件深层次的标签也有效,
   那么就要使用深层次选择器
   原理:自动在选择器前面添加一个data-v-hash值的属性选择器(scoped是在后面加)
   例如:/deep/ h1{...}会自动变成:[data-v-xxx] h1{...}
   注意:/deep/是Vue2种实现穿透的方案,在Vue3中推荐使用:deep()代替/deep/
   >>> 一般用于原生css
   /deep/ 一般用于less
   ::v-deep 一般用于scss

--------------------------------------------------------------------------------
canvas:
  canvas画布是HTML5中新增的一个特性,双闭合标签
  canvas标签默认宽度与高度是300*150
  浏览器认为canvas标签是一张图片
  给canvas画布添加文本内容没有任何意义也不会显示
  给canvas标签添加子节点也没有任何意义
  如果想操作canvas画布:画布中绘制图形、显示文字都必须通过js完成
  canvas标签的宽高必须通过canvas标签属性width|height设置,
    例如: <canvas height="500"> </canvas>  
    切记不能通过css样式去设置画布的宽度与高度,
    css样式设置宽高控制的是canvas元素在页面中占据空间的大小,不是画布实际的大小
    css样式设置宽高会让<canvas>标签元素在页面中占据的宽高与画布的宽高不一致

绘制线段:
  let canvas = document.querySelector("canvas")  通过js当中的"笔"去完成
  let cxt = canvas.getContext("2d")  获取画布的笔(上下文)
  cxt.moveTo(100, 100)  绘制线段:绘制线段的起点的设置(x,y)
  cxt.lineTo(100, 200)  其他点的设置:可以有多个
  cxt.lineTo(200, 300)
  cxt.lineTo(220, 250)
  cxt.fillStyle = "red"  设置图形的填充颜色
  cxt.fill()
  cxt.strokeStyle = "blue" 设置线段的颜色
  cxt.lineWidth = "20"  设置线段的宽度(没有单位)
  cxt.closePath()  设置起点与最后的终点连接在一起
  cxt.stroke()   stroke方法绘制线段(上面的步骤都必须在绘制线段之前)

绘制矩形:
   let canvas = document.querySelector("canvas") 获取dom节点
   let cxt = canvas.getContext("2d")  获取上下文
   第一种方式:
     cxt.strokeRect(100, 200, 100, 200) 参数为x,y,宽,高(这种只有描边没有填充颜色也无法填充颜色)
   第二种方式:
     cxt.fillStyle = "blue" 填充颜色,不填充默认黑色
     cxt.fill();
     cxt.fillRect(200, 200, 100, 200) 填充颜色必须在它之前才有效

绘制圆形:
  let canvas = document.querySelector("canvas")
  let cxt = canvas.getContext("2d") 获取上下文
  cxt.beginPath() 开始绘制圆
  cxt.arc(100, 100, 50, 0, 2 * Math.PI, true)
  获取圆形的方法:x,y,半径,起始弧度,结束弧度,是否逆时针绘制( 360度=2 * Math.PI)
  cxt.fillStyle = "red" 设置填充颜色
  cxt.fill()
  cxt.stroke() 绘制

清除画布:
  cxt.clearRect(x,y,宽,高)   

绘制文字:
  cxt.font = "20px 微软雅黑" 设置字体大小和字体
  cxt.fillStyle = "red" 设置字体颜色
  cxt.fillText("数据可视化", 50, 20) 绘制文字(设置字体大小和颜色要在绘制之前)
                  文字      x轴  y周
--------------------------------------------------------------------------------
svg:
  svg是一种基于xml的图像文件格式,英文全称Scalable Vector Graphics,意思是可缩放的矢量图形
  svg双闭合标签:默认宽度与高度300*150  svg绘制图形务必在svg标签内部使用绘制图形
  <svg>
   1. 绘制直线:
    <line x1="100" y1="100" x2="200" y2="200" stroke="red"  stroke-width="1"></line>
    x1 y1第一个点的坐标,x2 y2第二个点的坐标,stroke:线的颜色(不写不显示),stroke-width:线的粗细(可省略)
   2. 绘制折线:
    <polyline points="x1 y1,x2 y2,x3 y3,..." fill-opacity="0" stroke="red"></polyline>
    可以绘制多个点,多个点的时候,最好带有逗号(好区分)也可以不带逗号
    fill-opacity设置透明度(不设置是黑色)
    stroke设置线的颜色(如果透明度设为0而且不设置stroke就看不到了)
   3. 绘制矩形:
    <rect x="400" y="400" width="150" height="50" fill="blue"></rect>
    fill设置填充颜色(不设置默认是黑色) x,y左上角点坐标 width height宽高
   4. 绘制圆形:
    <circle cx="70" cy="95" r="50" fill="none" stroke="black"></circle>
    cx cy圆心点坐标 r圆的半径
   5. 绘制圆形或椭圆:
    <ellipse cx="100" cy="70" rx="100" ry="50" ></ellipse>
    cx cy圆心点坐标, rx ry x轴y轴的半径
   6. 绘制多边形:
    <polygon points="500,100,300,400,750 100"></polygon>
    x1 y1,x2 y2,x3 y3
   7. 任意图形:
    <path d="M 10 10 L 20 400 L 30 120 L 40 66  L 50 60 Z"></path>
    M x1 y1 L x1 y1 L x2 y2 L x3 y2... Z
    M移动到初始位置 L画线 Z将结束和开始点闭合
  </svg>:

--------------------------------------------------------------------------------------
day.js    https://dayjs.fenxianglu.cn/

--------------------------------------------------------------------------------------
hidden: true  隐藏路由

--------------------------------------------------------------------------------------
为什么不同用户登录我们的项目,菜单(路由)都是一样的?
因为咱们的路由‘死的’,不管你是谁,你能看见的,操作的菜单都是一样的

如何实现菜单的权限?不同的用户所能操作|查看菜单不一样的?
起始不同的用户(角色),登录的时候会向服务器发请求,服务器会把用户相应的菜单的权限的信息,
返回给我们,我们可以根据服务器返回的数据(信息),可以动态的设置路由,
可以根据不同的用户展示不同的菜单。

菜单权限:当用户获取用户信息的时候,服务器会把相应的用户拥有菜单的权限信息返回,需要根据用户身份对比出,当前这个用户需要展示哪些菜单

常量路由:就是不关用户是什么角色,都可以看见的路由
        (超级管理员,普通员工):登录、404、首页
异步路由:不同的用户(角色),需要过滤筛选出的路由,称之为异步路由
任意路由:当路径出现错误的时候重定向404

1. store/modules/user.js:
import { login, logout, getInfo } from "@/api/user";
import { getToken, setToken, removeToken } from "@/utils/auth";
import { resetRouter, anyRoutes, asyncRoutes, constantRoutes } from "@/router";
import router from "@/router";
const getDefaultState = () => {
  return {
    token: getToken(), //获取token
    name: "", //存储用户名
    avatar: "", //存储用户头像
    routes: [], //服务器返回的菜单信息【根据不同的角色:返回的标记信息,数组里面的元素是字符串】
    roles: [], //角色信息
    buttons: [], //按钮权限的信息
    resultAsyncRoutes: [], //对比之后【项目中已有的异步路由,与服务器返回的标记信息进行对比最终需要展示的路由】
    resultAllRputes: [], //用户最终需要展示全部路由
  };
};

const state = getDefaultState();
//定义一个函数:两个数组进行对比,对比出当前用户到底显示哪些异步路由
const computedAsyncRoutes = (asyncRoutes, routes) => {
  //过滤出当前用户【超级管理|普通员工】需要展示的异步路由
  return asyncRoutes.filter((item) => {
    //数组当中没有这个元素返回索引值-1,如果有这个元素返回的索引值一定不是-1
    if (routes.indexOf(item.name != -1)) {
      if (item.children && item.children.length) {
        item.children = computedAsyncRoutes(item.children, routes);
      }
      return true;
    }
  });
};
const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState());
  },
  SET_TOKEN: (state, token) => {
    state.token = token;
  },
  // SET_NAME: (state, name) => {
  //   state.name = name;
  // },
  // SET_AVATAR: (state, avatar) => {
  //   state.avatar = avatar;
  // },
  // 存储用户信息
  SET_USERINFO: (state, userInfo) => {
    state.name = userInfo.name; //用户名
    state.avatar = userInfo.avatar; //用户头像
    state.routes = userInfo.routes; //菜单权限标记
    state.buttons = userInfo.buttons; //按钮权限标记
    state.roles = userInfo.roles; //角色
  },
  //最终计算出的异步路由
  SET_RESULTASYNCROUTES: (state, asyncRoutes) => {
    //vuex保存当前用户的异步路由,注意,一个用户需要展示完成路由:常量、异步、任意路由
    state.resultAsyncRoutes = asyncRoutes;
    //计算出当前用户需要展示所有路由
    state.resultAllRputes = constantRoutes.concat(
      state.resultAsyncRoutes,
      anyRoutes
    );
    router.addRoutes(state.resultAllRputes); //给路由器添加新的路由
  },
};

const actions = {
  // user login
  async login({ commit }, userInfo) {
    const { username, password } = userInfo;
    let result = await login({ username: username.trim(), password: password });
    if (result.code == 20000) {
      commit("SET_TOKEN", result.data.token);
      setToken(result.data.token);
      return "ok";
    } else {
      return Promise.reject(new Error("faile"));
    }
    // return new Promise((resolve, reject) => {
    //   login({ username: username.trim(), password: password }).then(response => {
    //     const { data } = response
    //     commit('SET_TOKEN', data.token)
    //     setToken(data.token)
    //     resolve()
    //   }).catch(error => {
    //     reject(error)
    //   })
    // })
  },

  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token)
        .then((response) => {
          //获取用户信息:返回数据包含:用户名name、用户头像avatar、routes[返回的标志:不同的用户应该展示哪些菜单的标记]、roles(用户角色信息)、buttons【按钮的信息:按钮权限用的标记】
          const { data } = response;
          console.log(data);
          if (!data) {
            return reject("Verification failed, please Login again.");
          }
          // const { name, avatar } = data;
          // commit("SET_NAME", name);
          // commit("SET_AVATAR", avatar);
          commit("SET_USERINFO", data); //vuex存储用户全部信息
          commit(
            "SET_RESULTASYNCROUTES",
            computedAsyncRoutes(asyncRoutes, data.routes)
          );
          resolve(data);
        })
        .catch((error) => {
          reject(error);
        });
    });
  },

  // user logout
  logout({ commit, state }) {
    return new Promise((resolve, reject) => {
      logout(state.token)
        .then(() => {
          removeToken(); // must remove  token  first
          resetRouter();
          commit("RESET_STATE");
          resolve();
        })
        .catch((error) => {
          reject(error);
        });
    });
  },

  // remove token
  resetToken({ commit }) {
    return new Promise((resolve) => {
      removeToken(); // must remove  token  first
      commit("RESET_STATE");
      resolve();
    });
  },
};

export default {
  namespaced: true,
  state,
  mutations,
  actions,
};

2. router/index.js:
import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

/* Layout */
import Layout from "@/layout";

/**
 * Note: sub-menu only appear when route children.length >= 1
 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
 *
 * hidden: true                   if set true, item will not show in the sidebar(default is false)
 * alwaysShow: true               if set true, will always show the root menu
 *                                if not set alwaysShow, when item has more than one children route,
 *                                it will becomes nested mode, otherwise not show the root menu
 * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
 * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 * meta : {
    roles: ['admin','editor']    control the page roles (you can set multiple roles)
    title: 'title'               the name show in sidebar and breadcrumb (recommend set)
    icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
  }
 */

/**
 * constantRoutes
 * a base page that does not have permission requirements
 * all roles can be accessed
 */
//常量路由:就是不关用户是什么角色,都可以看见的路由
//(超级管理员,普通员工):登录、404、首页
export const constantRoutes = [
  {
    path: "/login",
    component: () => import("@/views/login/index"),
    hidden: true,
  },

  {
    path: "/404",
    component: () => import("@/views/404"),
    hidden: true,
  },

  {
    path: "/",
    component: Layout,
    redirect: "/dashboard",
    children: [
      {
        path: "dashboard",
        name: "Dashboard",
        component: () => import("@/views/dashboard/index"),
        meta: { title: "Dashboard", icon: "dashboard" },
      },
    ],
  },
  // 404 page must be placed at the end !!!
];
//异步路由:不同的用户(角色),需要过滤筛选出的路由,称之为异步路由
export const asyncRoutes = [
  {
    path: "/product",
    component: Layout,
    name: "product",
    meta: { title: "平台属性管理", icon: "el-icon-shopping-bag-2" },
    children: [
      {
        path: "attr",
        name: "attr",
        component: () => import("@/views/product/attr"),
        meta: { title: "平台属性管理" },
      },
      {
        path: "sku",
        name: "sku",
        component: () => import("@/views/product/sku"),
        meta: { title: "sku管理" },
      },
      {
        path: "spu",
        name: "spu",
        component: () => import("@/views/product/spu"),
        meta: { title: "spu管理" },
      },
      {
        path: "trademark",
        name: "trademark",
        component: () => import("@/views/product/trademark"),
        meta: { title: "品牌管理" },
      },
    ],
  },
  {
    path: "/alc",
    name: "alc",
    component: Layout,
    meta: { title: "权限管理", icon: "el-icon-sold-out" },
    children: [
      {
        path: "user",
        name: "user",
        component: () => import("@/views/alc/user"),
        meta: { title: "用户管理" },
      },
      {
        path: "role",
        name: "role",
        component: () => import("@/views/alc/role"),
        meta: { title: "角色管理" },
      },
      {
        path: "role/:id",
        name: "roleAuth",
        component: () => import("@/views/alc/role/roleAuth"),
        meta: {
          title: "角色授权",
          activeMenu: "/acl/role",
        },
        hidden: true,
      },
      {
        path: "permission",
        name: "permission",
        component: () => import("@/views/alc/permission"),
        meta: { title: "菜单管理" },
      },
    ],
  },
];
//任意路由:当路径出现错误的时候重定向404
export const anyRoutes = [{ path: "*", redirect: "/404", hidden: true }];
const createRouter = () =>
  new Router({
    // mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
  });

const router = createRouter();

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}
export default router;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端小马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值