# hrsass中台管理系统 **Repository Path**: sdwl_git/hrscss ## Basic Information - **Project Name**: hrsass中台管理系统 - **Description**: 中台管理项目,这仅仅是一次演练 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-04-19 - **Last Updated**: 2023-08-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## try catch finally 校验 **try**成功后的回调 **catch**失败后的回调 **finally**不论成功还是失败都执行的回调 ```js try { this.loading=true await this['user/login'](this.loginForm) // 登录成功之后 this.$router.push('/') } catch (error) { console.log(error, '登录失败') } finally { this.loading=false } ``` ## 导航守卫 ![](https://s1.ax1x.com/2022/08/16/vw6B7Q.png) ```js // 权限拦截在路由跳转 导航守卫 import router from "./router"; import store from "./store"; import nProgress from "nprogress"; //引入进度条插件 import 'nprogress/nprogress.css' // 前置守卫 // next() 放行 // next(false) 跳转终止 // next(地址) 跳转到某个地址 const whiteList = ['/login', '/404'] //请求白名单 router.beforeEach((to, from, next) => { nProgress.start() //开启进度条 if (store.getters.token) { // 如果有token if (to.path === '/login') { // 如果要访问的是 登录页 next('/') //跳到主页 } else { next() } } else { if (whiteList.indexOf(to.path) > -1) { // 表示要去的地址在白名单 next() } else { next('/login') } } nProgress.done() //解决手动切换地址时 进度条不关闭的情况 }) // 后置守卫 router.afterEach(() => { nProgress.done() }) ``` ## scss 同级选择和子集选择 (&.) **使用&连接符** ```scss //sass .side-container { float: left; height: 100vh; transition: width 0.5s; background-color: #263238; &.unfolded { width: 195px; .logo { width: 195px; height: 64px; } } &.folded { width: 64px; .logo { width: 64px; height: 64px; padding: 10px; box-sizing: border-box; } } } ``` **不适用连接符** ```scss // sass .side-container { float: left; height: 100vh; transition: width 0.5s; background-color: #263238; .unfolded { width: 195px; .logo { width: 195px; height: 64px; } } .folded { width: 64px; .logo { width: 64px; height: 64px; padding: 10px; box-sizing: border-box; } } } ``` **多类选择器:\**.side-container.unfolded\**与\**.side-container .unfolded\**是不一样的,前者是\**同级关系\**,后者是\**父子级关系\**。** ## 默认全部导入方法 **全部导入** ```js import * as directives from './directives' ``` ## 对象转数组 Object.keys **示例代码** ```js // simple array var arr = ['a', 'b', 'c']; console.log(Object.keys(arr)); // console: ['0', '1', '2'] // array like object var obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.keys(obj)); // console: ['0', '1', '2'] // array like object with random key ordering var anObj = { 100: 'a', 2: 'b', 7: 'c' }; console.log(Object.keys(anObj)); // console: ['2', '7', '100'] // getFoo is a property which isn't enumerable var myObj = Object.create({}, { getFoo: { value: function () { return this.foo; } } }); myObj.foo = 1; console.log(Object.keys(myObj)); // console: ['foo'] ``` ## 自定义指令用法实例 > ### **本节主要解决异常图片情况** **`目标`**: 通过自定义指令的形式解决异常图片的处理 > ### **自定义指令** 首先定义第一个自定义指令 **`v-imagerror`** ```js export const imagerror = { // 指令对象 会在当前的dom元素插入到节点之后执行 inserted(dom, options) { // options是 指令中的变量的解释 其中有一个属性叫做 value // dom 表示当前指令作用的dom对象 // dom认为此时就是图片 // 当图片有地址 但是地址没有加载成功的时候 会报错 会触发图片的一个事件 => onerror dom.onerror = function() { // 当图片出现异常的时候 会将指令配置的默认图片设置为该图片的内容 // dom可以注册error事件 dom.src = options.value // 这里不能写死 } } } ``` > ### **在main.js完成自定义指令全局注册** 然后,在**`main.js`**中完成对于该文件中所有指令的全局注册 ```js import * as directives from '@/directives' // 注册自定义指令 // 遍历所有的导出的指令对象 完成自定义全局注册 Object.keys(directives).forEach(key => { // 注册自定义指令 Vue.directive(key, directives[key]) }) ``` 针对上面的引入语法 **`import * as 变量`** 得到的是一个对象**`{ 变量1:对象1,变量2: 对象2 ... }`**, 所以可以采用对象遍历的方法进行处理 指令注册成功,可以在**`navbar.vue`**中直接使用了 ```vue ``` ```js data() { return { defaultImg: require('@/assets/common/head.jpg') } }, ``` ## 函数式组件 `functional`为`true`,表示该组件为一个函数式组件 函数式组件: 没有`data`状态,没有`响应式数据`,只会接收`props`属性, 没有`this`, 他就是一个函数 ```js export default { name: 'MenuItem', functional: true, props: { title: { type: String, default: '' } }, } ``` ## debugger 调试 > ### debugger 可以在js中写入这个代码来进行调试 ```js function tranListToTreeDate(list, rootValue) { debugger } ``` ## native 修饰符 > #### 作用:唤醒该组件原生的事件 ## 利用sync修饰符关闭新增弹层 > 这里我们学习一个新的技巧,**`sync修饰符`** 按照常规,想要让父组件更新**`showDialog`**的话,需要这样做 ```js // 子组件 this.$emit('changedialog', false) //触发事件 // 父组件 method(value) { this.showDialog = value } ``` > 但是,vuejs为我们提供了**`sync修饰符`**,它提供了一种简写模式 也就是 ```js // 子组件 update:固定写法 (update:props名称, 值) this.$emit('update:showDialog', false) //触发事件 // 父组件 sync修饰符 ``` 只要用sync修饰,就可以省略父组件的监听和方法,直接将值赋值给showDialog **取消按钮和关闭** ```js // 点击确定时触发 btnOK() { this.$refs.deptForm.validate(async isOK => { if (isOK) { // 表示可以提交了 await addDepartments({ ...this.formData, pid: this.treeNode.id }) // 调用新增接口 添加父部门的id this.$emit('addDepts') // 告诉父组件 新增数据成功 重新拉取数据 // update:props名称 this.$emit('update:showDialog', false) } }) } ``` ## 插槽的两种写法 **定义一个插槽** ```html
``` **两种写法:** ```html // 第一种写法 template 标签嵌套使用 // 第二种写法 slot 直接引用 导入excel ``` ## 全局注册组件 > 为了方便所有的页面都可以不用引用该组件,可以进行全局注册 提供注册入口 **`src/componets/index.js`** ```js // 该文件负责所有的公共的组件的全局注册 Vue.use import PageTools from './PageTools' export default { install(Vue) { // 注册全局的通用栏组件对象 Vue.component('PageTools', PageTools) } } ``` > 在入口处进行注册 **`src/main.js`** ```js import Component from '@/components' Vue.use(Component) // 注册自己的插件 ``` ## 分页 实现 逻辑 **接口** **Path:** /sys/user **Method:** GET **Query** | 参数名称 | 是否必须 | 示例 | 备注 | | -------- | -------- | ---- | -------- | | page | 是 | 1 | 页码 | | size | 是 | 10 | 每页条数 | **返回数据** | 名称 | 类型 | 是否必须 | 默认值 | 备注 | 其他信息 | | ------------- | --------- | -------- | ------ | ---- | ----------------- | | success | boolean | 非必须 | | | | | code | number | 非必须 | | | | | message | string | 非必须 | | | | | data | object | 非必须 | | | | | ├─ total | number | 非必须 | | | | | ├─ rows | object [] | 非必须 | | | item 类型: object | | ├─├─ id | string | 必须 | | | | | ├─├─ username | string | 必须 | | | | | ├─├─ password | string | 必须 | | | | > ### 1.调用接口 ```js /** * 获取员工列表 */ export function getEmployeeList(params){ return request({ url: '/sys/user', params }) } ``` > ### 2.组件中定义 > `list`负责接收所需要的数据 > > `page`负责控制分页 > > loading控制加载样式 ```js ``` > ### 页面中去绑定 > > 表格控件绑定 数据 > > 绑定到上方定义的list 数据 > 表示 显示为`序号` > 将`list数据`中的`username`绑定到这个位置 ```html ``` ## Excel 插件 XLSX > ### **`扩展` 复杂表头的导出** > 当需要导出复杂表头的时候,vue-element-admin同样支持该类操作 **vue-element-admin** 提供的导出方法中有 **multiHeader**和**merges** 的参数 | 参数 | 说明 | 类型 | 可选值 | 默认值 | | ----------- | -------------- | ----- | ------ | ------ | | multiHeader | 复杂表头的部分 | Array | / | [[]] | | merges | 需要合并的部分 | Array | / | [] | multiHeader里面是一个二维数组,里面的一个元素是一行表头,假设你想得到一个如图的结构 ![image-20201014152302636](H:/Desktop/YXD笔记/assets/image-20201014152302636.png) mutiHeader应该这样定义 ```js const multiHeader = [['姓名', '主要信息', '', '', '', '', '部门']] ``` multiHeader中的一行表头中的字段的个数需要和真正的列数相等,假设想要跨列,多余的空间需要定义成空串 它主要对应的是标准的表头 ```js const header = ['姓名', '手机号', '入职日期', '聘用形式', '转正日期', '工号', '部门'] ``` 如果,我们要实现其合并的效果, 需要设定merges选项 ```js const merges = ['A1:A2', 'B1:F1', 'G1:G2'] ``` merges的顺序是没关系的,只要配置这两个属性,就可以导出复杂表头的excel了 ```js exportData() { const headers = { '姓名': 'username', '手机号': 'mobile', '入职日期': 'timeOfEntry', '聘用形式': 'formOfEmployment', '转正日期': 'correctionTime', '工号': 'workNumber', '部门': 'departmentName' } // 导出excel import('@/vendor/Export2Excel').then(async excel => { // excel是引入文件的导出对象 // 导出 header从哪里来 // data从哪里来 // 现在没有一个接口获取所有的数据 // 获取员工的接口 页码 每页条数 100 1 10000 const { rows } = await getEmployeeList({ page: 1, size: this.page.total }) const data = this.formatJson(headers, rows) // 返回的data就是 要导出的结构 const multiHeader = [['姓名', '主要信息', '', '', '', '', '部门']] const merges = ['A1:A2', 'B1:F1', 'G1:G2'] excel.export_json_to_excel({ header: Object.keys(headers), data, filename: '员工资料表', multiHeader, // 复杂表头 merges // 合并选项 }) // excel.export_json_to_excel({ // header: ['姓名', '工资'], // data: [['张三', 3000], ['李四', 5000]], // filename: '员工工资表' // }) // [{ username: '张三',mobile: 13112345678 }] => [[]] // 要转化 数据结构 还要和表头的顺序对应上 // 要求转出的标题是中文 }) }, // 将表头数据和数据进行对应 // [{}] => [[]] formatJson(headers, rows) { return rows.map(item => { // item是一个对象 { mobile: 132111,username: '张三' } // ["手机号", "姓名", "入职日期" 。。] return Object.keys(headers).map(key => { // 需要判断 字段 if (headers[key] === 'timeOfEntry' || headers[key] === 'correctionTime') { // 格式化日期 return formatDate(item[headers[key]]) } else if (headers[key] === 'formOfEmployment') { const obj = EmployeeEnum.hireType.find(obj => obj.id === item[headers[key]]) return obj ? obj.value : '未知' } return item[headers[key]] }) // ["132", '张三’, ‘’,‘’,‘’d] }) // return rows.map(item => Object.keys(headers).map(key => item[headers[key]])) // 需要处理时间格式问题 } ``` ## 动态路由传参 > ? 的含义就是 id参数,可传可不传 ```js path: "detail/:id?", //动态路由传参 ``` ## 图片校验文件大小和类型 ```js beforeUpload(file) { // 要开始做文件上传的检查了 // 文件类型 文件大小 const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png'] if (!types.includes(file.type)) { this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!') return false } // 检查大小 const maxSize = 5 * 1024 * 1024 if (maxSize < file.size) { this.$message.error('图片大小最大不能超过5M') return false } return true } ```