当前位置:   article > 正文

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

尚品汇后台管理系统

 笔记

  1. v-model实现父子组件通信:
  2. 原始dom事件oninput,他经常结合表单元素一起使用,当表单原始内容发生变化时就触发一次回调
  3. v-model实现原理:valueinput事件实现的,可以通过v-model实现父子组件的数据同步
  4. 1. 在vue2中可以通过valueinput事件实现v-model功能
  5. 例如:
  6. <input type="text" :value="num" @input="num = $event.target.value" />
  7. data() {
  8. return {
  9. num: "1",
  10. };
  11. }, 这样和 <input type="text" v-model="num">效果一样
  12. 2. 实现父子组件数据同步
  13. 在父组件中:
  14. <hello-world :value="num" @input="num = $event"></hello-world>
  15. 给子组件标签传递一个value属性值为num的值
  16. 绑定一个input自定义事件,触发后让num的值等于传过来的值
  17. 可以简化为:
  18. <hello-world v-model="num"></hello-world>
  19. data() {
  20. return {
  21. num: "1", 设置num
  22. }
  23. }
  24. 在子组件中:
  25. <input type="text" :value="value" @input="$emit('input',$event.target.value)"/>
  26. 在子组件的input标签中设置value值为父组件传过来的值,
  27. 绑定input原生事件,当触发input原生事件时触发绑定的input自定义事件,
  28. 同时把子组件中input标签的value的值传过去
  29. props: ["value"] 接收传过来的属性
  30. --------------------------------------------------------------------------------
  31. 属性修饰符sync(组件通信方式之一):
  32. 与v-model类似
  33. 例如:
  34. <hello-world :money="money" @update:money="money = $event"></hello-world>
  35. 代表父组件给子组件传递money,给子组件绑定一个自定义事件update:money
  36. 可以简写:
  37. <hello-world :money.sync="money"></hello-world>
  38. 也代表父组件给子组件传递money,给子组件绑定一个自定义事件update:money
  39. <button @click="$emit('update:money', money-100)">
  40. 子组件触发自定义事件,第二个参数等于包装成一个回调函数给父组件传递过去了
  41. --------------------------------------------------------------------------------
  42. $attrs与$listeners
  43. 他们两者是组件实例的属性
  44. 1. $attrs可以获取到父组件给子组件传递props
  45. 对于子组件来说,父组件的数据可以利用props接收,但要注意如果子组件用props接收了属性,
  46. 在$attrs中就获取不到了
  47. <xxx v-bind="$attrs"></xxx> 这样可以把$attrs上父组传过来的数据传给xxx
  48. 注意:这里必须要用v-bind不能用简写:
  49. 2. $listeners可以获取到父组件给子组件传递的自定义事件
  50. <xxx v-on="$listeners"></xxx>这样可以把$listeners中父给子传过来的自定义事件全绑定到xxx上
  51. 注意:这里必须用v-on不能用简写@
  52. --------------------------------------------------------------------------------
  53. $children与$parent
  54. 1. $children组件实的属性,可以获取到当前组件的全部子组件(数组)
  55. 尽量不要以索引的形式写 this.$children[0]
  56. 因为如果子组件数量过多无法确定哪个是第一个,$children并不保证顺序
  57. 2. $parent组件实例的属性,可以获取到当前子组件的父组件,进而可以操作父组件的数据与方法
  58. 如果有多个父组件可能会误操作,就不建议使用
  59. --------------------------------------------------------------------------------
  60. 后台管理系统:可以让用户通过一个可视化工具,可以实现对于数据库进行增删改查的操作
  61. 而且需要注意,根据不同的角色(老板,员工),看到的,操作内容是不同的
  62. 例如:老板可以操作(产品的上架,产品的下架,查看员工个人业绩等)
  63. 员工可以查看个人业绩
  64. 对于后台管理系统项目,一般而言不需要注册
  65. --------------------------------------------------------------------------------
  66. 模板介绍
  67. 简洁版: https://github.com/PanJiaChen/vue-admin-template
  68. 加强版: https://github.com/PanJiaChen/vue-element-admin
  69. 下载下来后先npn i把依赖包安装上
  70. npm run dev运行(要把node版本降到16版本,16以上用不了)
  71. build: ----index.js webpack配置文件【很少修改这个文件】
  72. mock: ----mock数据的文件夹【模拟一些假的数据mockjs实现的】,因为咱们实际开发的时候,利用的是真实接口
  73. node_modules: ------项目依赖的模块
  74. public: ------ico图标,静态页面,publick文件夹里面经常放置一些静态资源,而且在项目打包的时候webpack
  75. 不会编译这个文件夹,原封不动的打包到dist文件夹里面
  76. src:
  77. -----程序员源代码的地方
  78. ------api文件夹:涉及请求相关的
  79. ------assets文件夹:里面放置一些静态资源(一般共享的),放在aseets文件夹里面静态资源,在
  80. webpack打包
  81. 的时候,会进行编译
  82. ------components文件夹:一般放置非路由组件获取全局组件
  83. ------icons这个文件夹的里面放置了一些svg矢量图
  84. ------layout文件夹:他里面放置一些组件与混入
  85. ------router文件夹:与路由相关的
  86. -----store文件夹:一定是与vuex先关的
  87. -----style文件夹:与样式先关的
  88. ------utils文件夹:request.js是axios二次封装文件
  89. ------views文件夹:里面放置的是路由组件
  90. App.vue:根组件
  91. main.js:入口文件
  92. permission.js:与导航守卫先关、
  93. settings:项目配置项文件
  94. .env.development 开发环境下的配置文件
  95. .env.producation 上线环境下的配置文件
  96. .env.staging 测试环境的配置文件 这三个文件里的数据可以通过webpack对外暴露的process.env拿到
  97. --------------------------------------------------------------------------------
  98. 在css中选择路径时使用@要在@前面加个~
  99. --------------------------------------------------------------------------------
  100. 后台管理系统API接口在线文档:
  101. http://39.98.123.211:8170/swagger-ui.html
  102. http://39.98.123.211:8510/swagger-ui.html
  103. --------------------------------------------------------------------------------
  104. 完成登录业务:
  105. 静态组件完成
  106. 书写API(换成真实的接口)
  107. axios二次封装
  108. 换成真实接口之后需要解决代理跨域问题(解决代理跨域问题)
  109. 在vue.config.js中把devServer中mkck的假数据换成跨域代理
  110. proxy: {
  111. "/dev-api": {
  112. target: "http://39.98.123.211:8170",
  113. pathRewrite: { "^/dev-api": "" },
  114. },
  115. },
  116. 在utils/requests.js中的:
  117. 把config.headers["X-Token"] = getToken()
  118. 换成:config.headers["token"] = getToken()
  119. 这里的token是后台设置的
  120. if (res.code !== 20000)
  121. 换成: if (res.code !== 20000 && res.code != 200)
  122. --------------------------------------------------------------------------------
  123. 配置相同前缀不同target的代理:
  124. proxy: {
  125. "/dev-api/admin/acl": {// 匹配所有以 '/dev-api/admin/acl'为前缀的请求路径
  126. target: "http://39.98.123.211:8170/", // 代理目标的基础路径
  127. pathRewrite: { "^/dev-api": "" }, //让代理服务器拿到的地址不带设置的前缀,
  128. 如果服务器地址本身带就不写
  129. },
  130. "/dev-api/admin/product": {// 匹配所有以 '/dev-api/admin/product'为前缀的请求路径
  131. target: "http://39.98.123.211:8510/", // 代理目标的基础路径
  132. pathRewrite: { "^/dev-api": "" }, //让代理服务器拿到的地址不带设置的前缀,
  133. 如果服务器地址本身带就不写
  134. },
  135. },
  136. 1.把原来的/dev-api改为/dev-api/admin/acl
  137. 2.复制一份该跨域配置,改为/dev-api/admin/product
  138. 3.将新的配置的端口号从8170改为8510
  139. 4.其实就是针对登录和获取trademark分别做了代理
  140. --------------------------------------------------------------------------------
  141. 修改品牌信息时删不了自带的,只能删自己或者其他人添加
  142. --------------------------------------------------------------------------------
  143. 不能在data中用this.xxx收集data本身的数据
  144. 因为对象存储数据是无序存储
  145. --------------------------------------------------------------------------------
  146. 如果需要深拷贝一个对象用xxx={...xxx},但这个对象的层级很深(对象套数组,数组套对象)
  147. 那么对象中的基本数据类型会被深拷贝,对象中的引用数据类型会被浅拷贝
  148. 1. 可以使用xxx= JSON.parse(JSON.stringify(xxx)) 进行深拷贝
  149. 2. 可以使用lodash中的深拷贝
  150. import cloneDeep from "lodash/cloneDeep" 按需引入深拷贝
  151. 要拷贝的对象= cloneDeep(被拷贝的对象);
  152. --------------------------------------------------------------------------------
  153. 老版本气泡确认框的点击确认按钮的事件名称叫onConfirm
  154. 气泡确认框的<template>代码里必须写scope="{ row }"不然没效果
  155. --------------------------------------------------------------------------------
  156. 接口(注意接口不要有空格):
  157. /admin/acl/index/login post 登录
  158. /admin/acl/index/info get 获取用户信息
  159. /admin/acl/index/logout post 退出登录
  160. /admin/product/baseTrademark/{page}/{limit} get 品牌列表
  161. post 添加品牌
  162. 参数:{
  163. logoUrl: "string", 品牌logo
  164. tmName: "string" 品牌名字
  165. }
  166. /admin/product/baseTrademark/update put 修改品牌
  167. 参数:{
  168. id:0,
  169. logoUrl: "string", 品牌logo
  170. tmName: "string" 品牌名字
  171. }
  172. /admin/product/fileUpload post 上传图片接口
  173. /admin/product/baseTrademark/remove/{id} delete 删除品牌
  174. /admin/product/getCategory1 get 获取三级联动一级分类
  175. /admin/product/getCategory2/{category1Id} get 获取三级联动二级分类
  176. /admin/product/getCategory3/{category2Id} get 获取三级联动三级分类
  177. /admin/product/attrInfoList/{category1Id}/{category2Id}/{category3Id} get 获取平台属性
  178. /admin/product/saveAttrInfo post 添加,修改属性与属性值
  179. 参数:{
  180. attrName: "", 属性名
  181. attrValueList: [ 属性名中的属性值
  182. {
  183. attrId: this.保存数据的对象.id, 属性名的id
  184. 添加属性和属性值时,因为attrId是服务器返回的数据,添加属性值时没有attrId,attrId要写
  185. undefined,
  186. 修改属性和属性值时也可以添加属性值,这时服务器已经返回attrId,所以要加上服务器返回的
  187. id,用this.保存数据的对象.id当添加时因为服务器没有返回id所以是undefined,修改时服务器
  188. 返回了id就是这个返回id的值
  189. valueName: "" 属性值
  190. }
  191. ],
  192. categoryId: 0, category3Id
  193. categoryLevel: 3, 区分是几级id,都是3
  194. }
  195. 添加或修改完保存时
  196. 如果用户添加了很多属性值,且属性值为空的不应该提交给服务器
  197. 提交给服务器数据中不应该出现flag字段
  198. /admin/product/deleteAttr/{attrId} delete 删除平台属性
  199. /admin/product/{page}/{limit} get spu列表数据
  200. 参数:
  201. page代表第几页 limit代表一页几个数据
  202. 还有一个参数category3Id(三级分类的id) 要用params参数带过去
  203. 注意:params参数以对象形式传过去, params: { category3Id },
  204. /admin/product/baseTrademark/getTrademarkList get spu品牌数据
  205. /admin/product/baseSaleAttrList get 获取spu平台中全部的销售属性
  206. /admin/product/getSpuById/{spuId} get 获取某一个spu信息
  207. /admin/product/spuImageList/{spuId} get 获取spu图片
  208. /admin/product/saveSpuInfo post 添加spu
  209. /admin/product/updateSpuInfo post 修改spu信息
  210. 添加和修改参数一样,区别是添加没有id,修改有id
  211. 注意1:保存时新添加的图片没有imgName和imgUrl要加上
  212. 新加上的图片的imgUrl不能用url要用response里的data
  213. 注意2:修改spu时照片墙照片不显示要用:file-list=""
  214. 参数:
  215. {
  216. category3Id: 0, //三级分类id
  217. description: "",//描述
  218. tmId: 0, //品牌id
  219. spuName:"",//spu的名字
  220. id: 0,//添加参数不带id 通过有没有id判断新添加还是修改
  221. spuImageList: [ //照片墙数据
  222. {
  223. imgName: "",
  224. imgUrl: "",
  225. }
  226. ],
  227. spuSaleAttrList: [ //平台销售属性与属性值信息
  228. {
  229. baseSaleAttrId: 0, // 属性id
  230. saleAttrName: "", // 属性名
  231. spuSaleAttrValueList: [ //属性值列表
  232. {
  233. baseSaleAttrId: 0, //销售属性的id
  234. saleAttrValueName: "", //销售属性值的名称
  235. }
  236. ]
  237. }
  238. ],
  239. }
  240. /admin/product/deleteSpu/{spuId} DELETE 删除spu
  241. /admin/product/spuImageList/{spuId} get 获取sku图片列表
  242. /admin/product/spuSaleAttrList/{spuId} get 获取sku销售属性
  243. /admin/product/attrInfoList/{category1Id}/{category2Id}/{category3Id}
  244. get 获取sku平台属性
  245. /admin/product/saveSkuInfo post sku保存 注意:这里接口应该有问题,重量不填保存不了
  246. 参数:
  247. {
  248. category3Id: 0,
  249. spuId: 0,
  250. tmId: 0, //tmid在传过来的row身上
  251. price: 0, //价格
  252. skuName: "",
  253. weight: "",
  254. skuDefaultImg: "",//默认图片
  255. skuDesc: "",
  256. skuAttrValueList: [ //平台属性列表
  257. {
  258. attrId: 0, //平台属性id
  259. valueId: 0, //平台属性值的id
  260. }
  261. ],
  262. skuImageList: [//图片列表
  263. {
  264. imgName: "",
  265. imgUrl: "",
  266. isDefault: "", //值为1为默认,为0是设置默认
  267. spuImgId: 0
  268. }
  269. ],
  270. skuSaleAttrValueList: [ //销售属性列表
  271. {
  272. saleAttrId: 0,//销售属性id
  273. saleAttrValueId: 0,//销售属性值的id
  274. }
  275. ],
  276. }
  277. /admin/product/findBySpuId/{spuId} get sku列表展示
  278. /admin/product/list/{page}/{limit}
  279. get sku模块数据 每条数据中有个isSale属性,1为上架,0为下架
  280. /admin/product/onSale/{skuId} get 上架
  281. /admin/product/cancelSale/{skuId} get 下架
  282. /admin/product/deleteSku/{skuId} 删除sku
  283. /admin/product/getSkuById/{skuId} get sku详情数据
  284. /admin/acl/user/{page}/{limit} get 用户管理数据
  285. /admin/acl/user/{page}/{limit} get 用户管理点击查询后获取数据
  286. 参数: 带一个params参数username(表示要查询的值)
  287. /admin/acl/user/save post 新增管理用户
  288. 参数:
  289. {
  290. nickName:"",//用户名
  291. password:"", //用户密码
  292. username:"", //用户昵称
  293. }
  294. /admin/acl/user/remove/{id} delete 删除管理用户 注意:批量删除的接口有问题也用这个
  295. /admin/acl/user/toAssign/{userId} get 根据用户获取角色数据
  296. /admin/acl/user/doAssign post 根据用户分配角色
  297. 参数:用params带
  298. userId 用户id ,
  299. roleId [] 选择了的角色的id们
  300. /admin/acl/user/update put 修改用户
  301. 参数:
  302. {
  303. nickName: "", //用户名
  304. username: "", //用户昵称
  305. id: "", //用户id
  306. },
  307. /admin/acl/role/{page}/{limit} get 获取角色数据
  308. /admin/acl/role/remove/{id} delete 删除角色
  309. /admin/acl/role/{page}/{limit} get 角色管理点击查询后获取数据
  310. 参数: 带一个params参数roleName(表示要查询的值)
  311. /admin/acl/role/save post 角色管理新增角色
  312. 参数:带一个data参数roleName(表示要添加的名字)
  313. /admin/acl/role/batchRemove delete 角色管理批量删除
  314. 参数: data参数,一个数组数组的每一位是每个角色的id
  315. /admin/acl/role/update put 修改角色
  316. 参数:{id: "", roleName: "修改后的名字"}
  317. /admin/acl/permission/toAssign/{roleId} get 根据角色获取菜单,分配权限页面的菜单
  318. /admin/acl/permission/doAssign post 给角色分配权限
  319. 参数:{roleId:"",permissionId:[分配权限的所有id,xxx,xxx]} params参数
  320. /admin/acl/permission get 获取菜单
  321. /admin/acl/permission/save post 新增菜单
  322. 参数:
  323. {code:"权限值",level:"几级数组",name:"名称",pid:"",toCode:"跳转权限值",type:""}
  324. pid:不能用row的pid,要用row的id
  325. /admin/acl/permission/update put 修改菜单
  326. 参数:row
  327. /admin/acl/permission/remove/{id} delete 删除菜单
  328. --------------------------------------------------------------------------------
  329. Object.assign(): es6新增的方法可以合并对象
  330. this._data可以操作data中的响应式数据
  331. this.$options可以获取配置对象,配置对象的data函数执行,返回的响应式数据为空
  332. Object.assign(this._data,this.$options.data())
  333. --------------------------------------------------------------------------------
  334. 深度选择器:
  335. 如果使用了scoped那么子组件或第三方子组件的根标签会添加上相应的样式,更深层次不会
  336. 如果想使用scoped的同时又想让父组件中写的样式对子组件或第三方子组件深层次的标签也有效,
  337. 那么就要使用深层次选择器
  338. 原理:自动在选择器前面添加一个data-v-hash值的属性选择器(scoped是在后面加)
  339. 例如:/deep/ h1{...}会自动变成:[data-v-xxx] h1{...}
  340. 注意:/deep/是Vue2种实现穿透的方案,在Vue3中推荐使用:deep()代替/deep/
  341. >>> 一般用于原生css
  342. /deep/ 一般用于less
  343. ::v-deep 一般用于scss
  344. --------------------------------------------------------------------------------
  345. canvas:
  346. canvas画布是HTML5中新增的一个特性,双闭合标签
  347. canvas标签默认宽度与高度是300*150
  348. 浏览器认为canvas标签是一张图片
  349. 给canvas画布添加文本内容没有任何意义也不会显示
  350. 给canvas标签添加子节点也没有任何意义
  351. 如果想操作canvas画布:画布中绘制图形、显示文字都必须通过js完成
  352. canvas标签的宽高必须通过canvas标签属性width|height设置,
  353. 例如: <canvas height="500"> </canvas>
  354. 切记不能通过css样式去设置画布的宽度与高度,
  355. css样式设置宽高控制的是canvas元素在页面中占据空间的大小,不是画布实际的大小
  356. css样式设置宽高会让<canvas>标签元素在页面中占据的宽高与画布的宽高不一致
  357. 绘制线段:
  358. let canvas = document.querySelector("canvas") 通过js当中的"笔"去完成
  359. let cxt = canvas.getContext("2d") 获取画布的笔(上下文)
  360. cxt.moveTo(100, 100) 绘制线段:绘制线段的起点的设置(x,y)
  361. cxt.lineTo(100, 200) 其他点的设置:可以有多个
  362. cxt.lineTo(200, 300)
  363. cxt.lineTo(220, 250)
  364. cxt.fillStyle = "red" 设置图形的填充颜色
  365. cxt.fill()
  366. cxt.strokeStyle = "blue" 设置线段的颜色
  367. cxt.lineWidth = "20" 设置线段的宽度(没有单位)
  368. cxt.closePath() 设置起点与最后的终点连接在一起
  369. cxt.stroke() stroke方法绘制线段(上面的步骤都必须在绘制线段之前)
  370. 绘制矩形:
  371. let canvas = document.querySelector("canvas") 获取dom节点
  372. let cxt = canvas.getContext("2d") 获取上下文
  373. 第一种方式:
  374. cxt.strokeRect(100, 200, 100, 200) 参数为x,y,宽,高(这种只有描边没有填充颜色也无法填充颜色)
  375. 第二种方式:
  376. cxt.fillStyle = "blue" 填充颜色,不填充默认黑色
  377. cxt.fill();
  378. cxt.fillRect(200, 200, 100, 200) 填充颜色必须在它之前才有效
  379. 绘制圆形:
  380. let canvas = document.querySelector("canvas")
  381. let cxt = canvas.getContext("2d") 获取上下文
  382. cxt.beginPath() 开始绘制圆
  383. cxt.arc(100, 100, 50, 0, 2 * Math.PI, true)
  384. 获取圆形的方法:x,y,半径,起始弧度,结束弧度,是否逆时针绘制( 360=2 * Math.PI)
  385. cxt.fillStyle = "red" 设置填充颜色
  386. cxt.fill()
  387. cxt.stroke() 绘制
  388. 清除画布:
  389. cxt.clearRect(x,y,宽,高)
  390. 绘制文字:
  391. cxt.font = "20px 微软雅黑" 设置字体大小和字体
  392. cxt.fillStyle = "red" 设置字体颜色
  393. cxt.fillText("数据可视化", 50, 20) 绘制文字(设置字体大小和颜色要在绘制之前)
  394. 文字 x轴 y周
  395. --------------------------------------------------------------------------------
  396. svg:
  397. svg是一种基于xml的图像文件格式,英文全称Scalable Vector Graphics,意思是可缩放的矢量图形
  398. svg双闭合标签:默认宽度与高度300*150 svg绘制图形务必在svg标签内部使用绘制图形
  399. <svg>
  400. 1. 绘制直线:
  401. <line x1="100" y1="100" x2="200" y2="200" stroke="red" stroke-width="1"></line>
  402. x1 y1第一个点的坐标,x2 y2第二个点的坐标,stroke:线的颜色(不写不显示),stroke-width:线的粗细(可省略)
  403. 2. 绘制折线:
  404. <polyline points="x1 y1,x2 y2,x3 y3,..." fill-opacity="0" stroke="red"></polyline>
  405. 可以绘制多个点,多个点的时候,最好带有逗号(好区分)也可以不带逗号
  406. fill-opacity设置透明度(不设置是黑色)
  407. stroke设置线的颜色(如果透明度设为0而且不设置stroke就看不到了)
  408. 3. 绘制矩形:
  409. <rect x="400" y="400" width="150" height="50" fill="blue"></rect>
  410. fill设置填充颜色(不设置默认是黑色) x,y左上角点坐标 width height宽高
  411. 4. 绘制圆形:
  412. <circle cx="70" cy="95" r="50" fill="none" stroke="black"></circle>
  413. cx cy圆心点坐标 r圆的半径
  414. 5. 绘制圆形或椭圆:
  415. <ellipse cx="100" cy="70" rx="100" ry="50" ></ellipse>
  416. cx cy圆心点坐标, rx ry x轴y轴的半径
  417. 6. 绘制多边形:
  418. <polygon points="500,100,300,400,750 100"></polygon>
  419. x1 y1,x2 y2,x3 y3
  420. 7. 任意图形:
  421. <path d="M 10 10 L 20 400 L 30 120 L 40 66 L 50 60 Z"></path>
  422. M x1 y1 L x1 y1 L x2 y2 L x3 y2... Z
  423. M移动到初始位置 L画线 Z将结束和开始点闭合
  424. </svg>
  425. --------------------------------------------------------------------------------------
  426. day.js https://dayjs.fenxianglu.cn/
  427. --------------------------------------------------------------------------------------
  428. hidden: true 隐藏路由
  429. --------------------------------------------------------------------------------------
  430. 为什么不同用户登录我们的项目,菜单(路由)都是一样的?
  431. 因为咱们的路由‘死的’,不管你是谁,你能看见的,操作的菜单都是一样的
  432. 如何实现菜单的权限?不同的用户所能操作|查看菜单不一样的?
  433. 起始不同的用户(角色),登录的时候会向服务器发请求,服务器会把用户相应的菜单的权限的信息,
  434. 返回给我们,我们可以根据服务器返回的数据(信息),可以动态的设置路由,
  435. 可以根据不同的用户展示不同的菜单。
  436. 菜单权限:当用户获取用户信息的时候,服务器会把相应的用户拥有菜单的权限信息返回,需要根据用户身份对比出,当前这个用户需要展示哪些菜单
  437. 常量路由:就是不关用户是什么角色,都可以看见的路由
  438. (超级管理员,普通员工):登录、404、首页
  439. 异步路由:不同的用户(角色),需要过滤筛选出的路由,称之为异步路由
  440. 任意路由:当路径出现错误的时候重定向404
  441. 1. store/modules/user.js:
  442. import { login, logout, getInfo } from "@/api/user";
  443. import { getToken, setToken, removeToken } from "@/utils/auth";
  444. import { resetRouter, anyRoutes, asyncRoutes, constantRoutes } from "@/router";
  445. import router from "@/router";
  446. const getDefaultState = () => {
  447. return {
  448. token: getToken(), //获取token
  449. name: "", //存储用户名
  450. avatar: "", //存储用户头像
  451. routes: [], //服务器返回的菜单信息【根据不同的角色:返回的标记信息,数组里面的元素是字符串】
  452. roles: [], //角色信息
  453. buttons: [], //按钮权限的信息
  454. resultAsyncRoutes: [], //对比之后【项目中已有的异步路由,与服务器返回的标记信息进行对比最终需要展示的路由】
  455. resultAllRputes: [], //用户最终需要展示全部路由
  456. };
  457. };
  458. const state = getDefaultState();
  459. //定义一个函数:两个数组进行对比,对比出当前用户到底显示哪些异步路由
  460. const computedAsyncRoutes = (asyncRoutes, routes) => {
  461. //过滤出当前用户【超级管理|普通员工】需要展示的异步路由
  462. return asyncRoutes.filter((item) => {
  463. //数组当中没有这个元素返回索引值-1,如果有这个元素返回的索引值一定不是-1
  464. if (routes.indexOf(item.name != -1)) {
  465. if (item.children && item.children.length) {
  466. item.children = computedAsyncRoutes(item.children, routes);
  467. }
  468. return true;
  469. }
  470. });
  471. };
  472. const mutations = {
  473. RESET_STATE: (state) => {
  474. Object.assign(state, getDefaultState());
  475. },
  476. SET_TOKEN: (state, token) => {
  477. state.token = token;
  478. },
  479. // SET_NAME: (state, name) => {
  480. // state.name = name;
  481. // },
  482. // SET_AVATAR: (state, avatar) => {
  483. // state.avatar = avatar;
  484. // },
  485. // 存储用户信息
  486. SET_USERINFO: (state, userInfo) => {
  487. state.name = userInfo.name; //用户名
  488. state.avatar = userInfo.avatar; //用户头像
  489. state.routes = userInfo.routes; //菜单权限标记
  490. state.buttons = userInfo.buttons; //按钮权限标记
  491. state.roles = userInfo.roles; //角色
  492. },
  493. //最终计算出的异步路由
  494. SET_RESULTASYNCROUTES: (state, asyncRoutes) => {
  495. //vuex保存当前用户的异步路由,注意,一个用户需要展示完成路由:常量、异步、任意路由
  496. state.resultAsyncRoutes = asyncRoutes;
  497. //计算出当前用户需要展示所有路由
  498. state.resultAllRputes = constantRoutes.concat(
  499. state.resultAsyncRoutes,
  500. anyRoutes
  501. );
  502. router.addRoutes(state.resultAllRputes); //给路由器添加新的路由
  503. },
  504. };
  505. const actions = {
  506. // user login
  507. async login({ commit }, userInfo) {
  508. const { username, password } = userInfo;
  509. let result = await login({ username: username.trim(), password: password });
  510. if (result.code == 20000) {
  511. commit("SET_TOKEN", result.data.token);
  512. setToken(result.data.token);
  513. return "ok";
  514. } else {
  515. return Promise.reject(new Error("faile"));
  516. }
  517. // return new Promise((resolve, reject) => {
  518. // login({ username: username.trim(), password: password }).then(response => {
  519. // const { data } = response
  520. // commit('SET_TOKEN', data.token)
  521. // setToken(data.token)
  522. // resolve()
  523. // }).catch(error => {
  524. // reject(error)
  525. // })
  526. // })
  527. },
  528. // get user info
  529. getInfo({ commit, state }) {
  530. return new Promise((resolve, reject) => {
  531. getInfo(state.token)
  532. .then((response) => {
  533. //获取用户信息:返回数据包含:用户名name、用户头像avatar、routes[返回的标志:不同的用户应该展示哪些菜单的标记]、roles(用户角色信息)、buttons【按钮的信息:按钮权限用的标记】
  534. const { data } = response;
  535. console.log(data);
  536. if (!data) {
  537. return reject("Verification failed, please Login again.");
  538. }
  539. // const { name, avatar } = data;
  540. // commit("SET_NAME", name);
  541. // commit("SET_AVATAR", avatar);
  542. commit("SET_USERINFO", data); //vuex存储用户全部信息
  543. commit(
  544. "SET_RESULTASYNCROUTES",
  545. computedAsyncRoutes(asyncRoutes, data.routes)
  546. );
  547. resolve(data);
  548. })
  549. .catch((error) => {
  550. reject(error);
  551. });
  552. });
  553. },
  554. // user logout
  555. logout({ commit, state }) {
  556. return new Promise((resolve, reject) => {
  557. logout(state.token)
  558. .then(() => {
  559. removeToken(); // must remove token first
  560. resetRouter();
  561. commit("RESET_STATE");
  562. resolve();
  563. })
  564. .catch((error) => {
  565. reject(error);
  566. });
  567. });
  568. },
  569. // remove token
  570. resetToken({ commit }) {
  571. return new Promise((resolve) => {
  572. removeToken(); // must remove token first
  573. commit("RESET_STATE");
  574. resolve();
  575. });
  576. },
  577. };
  578. export default {
  579. namespaced: true,
  580. state,
  581. mutations,
  582. actions,
  583. };
  584. 2. router/index.js:
  585. import Vue from "vue";
  586. import Router from "vue-router";
  587. Vue.use(Router);
  588. /* Layout */
  589. import Layout from "@/layout";
  590. /**
  591. * Note: sub-menu only appear when route children.length >= 1
  592. * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
  593. *
  594. * hidden: true if set true, item will not show in the sidebar(default is false)
  595. * alwaysShow: true if set true, will always show the root menu
  596. * if not set alwaysShow, when item has more than one children route,
  597. * it will becomes nested mode, otherwise not show the root menu
  598. * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
  599. * name:'router-name' the name is used by <keep-alive> (must set!!!)
  600. * meta : {
  601. roles: ['admin','editor'] control the page roles (you can set multiple roles)
  602. title: 'title' the name show in sidebar and breadcrumb (recommend set)
  603. icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
  604. breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
  605. activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
  606. }
  607. */
  608. /**
  609. * constantRoutes
  610. * a base page that does not have permission requirements
  611. * all roles can be accessed
  612. */
  613. //常量路由:就是不关用户是什么角色,都可以看见的路由
  614. //(超级管理员,普通员工):登录、404、首页
  615. export const constantRoutes = [
  616. {
  617. path: "/login",
  618. component: () => import("@/views/login/index"),
  619. hidden: true,
  620. },
  621. {
  622. path: "/404",
  623. component: () => import("@/views/404"),
  624. hidden: true,
  625. },
  626. {
  627. path: "/",
  628. component: Layout,
  629. redirect: "/dashboard",
  630. children: [
  631. {
  632. path: "dashboard",
  633. name: "Dashboard",
  634. component: () => import("@/views/dashboard/index"),
  635. meta: { title: "Dashboard", icon: "dashboard" },
  636. },
  637. ],
  638. },
  639. // 404 page must be placed at the end !!!
  640. ];
  641. //异步路由:不同的用户(角色),需要过滤筛选出的路由,称之为异步路由
  642. export const asyncRoutes = [
  643. {
  644. path: "/product",
  645. component: Layout,
  646. name: "product",
  647. meta: { title: "平台属性管理", icon: "el-icon-shopping-bag-2" },
  648. children: [
  649. {
  650. path: "attr",
  651. name: "attr",
  652. component: () => import("@/views/product/attr"),
  653. meta: { title: "平台属性管理" },
  654. },
  655. {
  656. path: "sku",
  657. name: "sku",
  658. component: () => import("@/views/product/sku"),
  659. meta: { title: "sku管理" },
  660. },
  661. {
  662. path: "spu",
  663. name: "spu",
  664. component: () => import("@/views/product/spu"),
  665. meta: { title: "spu管理" },
  666. },
  667. {
  668. path: "trademark",
  669. name: "trademark",
  670. component: () => import("@/views/product/trademark"),
  671. meta: { title: "品牌管理" },
  672. },
  673. ],
  674. },
  675. {
  676. path: "/alc",
  677. name: "alc",
  678. component: Layout,
  679. meta: { title: "权限管理", icon: "el-icon-sold-out" },
  680. children: [
  681. {
  682. path: "user",
  683. name: "user",
  684. component: () => import("@/views/alc/user"),
  685. meta: { title: "用户管理" },
  686. },
  687. {
  688. path: "role",
  689. name: "role",
  690. component: () => import("@/views/alc/role"),
  691. meta: { title: "角色管理" },
  692. },
  693. {
  694. path: "role/:id",
  695. name: "roleAuth",
  696. component: () => import("@/views/alc/role/roleAuth"),
  697. meta: {
  698. title: "角色授权",
  699. activeMenu: "/acl/role",
  700. },
  701. hidden: true,
  702. },
  703. {
  704. path: "permission",
  705. name: "permission",
  706. component: () => import("@/views/alc/permission"),
  707. meta: { title: "菜单管理" },
  708. },
  709. ],
  710. },
  711. ];
  712. //任意路由:当路径出现错误的时候重定向404
  713. export const anyRoutes = [{ path: "*", redirect: "/404", hidden: true }];
  714. const createRouter = () =>
  715. new Router({
  716. // mode: 'history', // require service support
  717. scrollBehavior: () => ({ y: 0 }),
  718. routes: constantRoutes,
  719. });
  720. const router = createRouter();
  721. // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
  722. export function resetRouter() {
  723. const newRouter = createRouter();
  724. router.matcher = newRouter.matcher; // reset router
  725. }
  726. export default router;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/69858
推荐阅读
相关标签
  

闽ICP备14008679号