# rz_biji
**Repository Path**: ifercarly/rzbiji
## Basic Information
- **Project Name**: rz_biji
- **Description**: 人资项目记录
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-07-15
- **Last Updated**: 2021-08-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: 项目记录
## README
## 0. 关于包管理工具 Yarn
```bash
npm i -g yarn # 全局安装 yarn 这个工具
yarn -v # 查看版本号,能出来就证明安装成功
yarn # 安装 package.json 内部依赖所有包,相当于 npm i
yarn add vue # npm i vue
yarn add webpack -D # npm i webpack -D
```
## 1. 前端如何处理跨域
反向代理:代理了真实的服务器,变成了 `webpack-dev-server` 创建的服务器和真实服务器之间发请求,服务器之间发请求是不存在跨域的,`webpack-dev-server` 拿到数据之后再给前端,前端和 `webpack-dev-server` 创建的服务器之间也是不存在跨域的
如果说你是用 `vue-cli` 创建的项目,只需要在 `vue.config.js` 这个配置文件中,修改 `devServer` 中 proxy 选项就可以了
其实 `devServer` 这个选项最终操作的是这个插件 `webpack-dev-server`
## 2. 登录的流程
```vue
handleLogin() {
// 手动校验
this.$refs.loginForm.validate(async isOk => {
if (isOk) {
try {
this.loading = true
// 1. 有可能 push 之后展示的组件需要用到 Vuex 中的信息
// 2. 如果页面做了访问控制,如果不加 await,会先进行跳转,此时还没有 token,可能都跳转不了
await this['user/login'](this.loginForm)
this.$router.push('/')
} catch (error) {
console.log(error)
} finally {
this.loading = false
}
}
})
}
```
## 3. 如何做界面访问控制
## 4. 获取用户信息在时机
1、第一种方式:在 NavBar 组件里面去触发获取用户信息的 action
2、第二种方式:在路由导航守卫里面去触发获取用户信息的 action

## 5. 使用默认导出数据的几种方式
1、第一种方式
`sub.js`
```js
export default (a, b) => a + b;
```
`test.js`
```js
// sum 叫啥无所谓
import sum from './sub.js';
console.log(sum(1, 2));
```
2、第二种方式
`test.js`
```js
// mod 叫啥无所谓
import * as mod from './sub.js';
// console.log(mod); // { default: (a, b) => a + b }
console.log(mod.default(1, 2));
```
3、第三种方式
`sub.js`
```js
export default (a, b) => a + b;
```
test.js
```js
import { default as sub } from './sub.js';
console.log(sub(1, 3));
```
4、第四种方式
`sub.js`
```js
export default (a, b) => a + b;
```
`test.js`
```js
export { default } from './sub.js';
```
`index.js`
```js
import { default as sum } from './test.js';
console.log(sum(1, 2));
```
5、第五种方式
`sub.js`
```js
export default (a, b) => a + b;
```
`test.js`
```js
export { default as sum } from './sub.js';
```
`index.js`
```js
import { sum } from './test.js';
console.log(sum(1, 2));
```
## 6. TOKEN 的超时处理
### 6.1 纯前端处理

### 6.2 响应拦截器里面根据后端返回的状态标识进行处理
## 7. 弹出超时,代码的执行过程是怎样的?

刷新,整个项目的代码会重新跑一次!
1、permission.js

2、src\store\modules\user.js

3、src\utils\request.js
请求拦截器里面如果正常返回内容,会先到响应拦截器的第一个参数,如果返回的是一个错误的 Promise,会先到响应拦截器的第二个参数

4、src\utils\request.js

5、src\store\modules\user.js

## 8. mixins 和 extends 执行顺序
```vue
Hello World
```
## 9. 了解 Vue.extend
```html
Document
```
动态弹框案例
toast.js
```js
import Vue from 'vue'
import MyToast from './MyToast.vue'
// 期望根据传过来的模板,生成构造函数
const MyToastConstructor = Vue.extend(MyToast)
function $mytoast(show, msg, duration = 2000) {
// 可以根据这个构造函数生成组件实例
const toastCmp = new MyToastConstructor({
el: document.createElement('div'), // 把生成的组件实例挂到 div 上
data() {
return {
show,
msg
}
}
})
// 把组件怼到页面上面
document.body.appendChild(toastCmp.$el)
setTimeout(() => {
document.body.removeChild(toastCmp.$el)
}, duration)
}
export default {
install(Vue) {
Vue.prototype.$mytoast = $mytoast
}
}
```
`MyToast.vue`
```vue
{{ msg }}
```
main.js
```js
import Toast from './toast'
Vue.use(Toast)
```
App.vue
```vue
```
## 10. array-to-tree
### 0. 将数组转成树
需求
```javascript
const arr = [
{ id: 'a', pid: '', name: '总裁办' },
{ id: 'b', pid: '', name: '行政部' },
{ id: 'c', pid: '', name: '财务部' },
{ id: 'd', pid: 'c', name: '财务核算部' },
{ id: 'e', pid: 'c', name: '税务管理部' }
];
```
结果
```javascript
const arr = [
{ id: 'a', pid: '', name: '总裁办' },
{ id: 'b', pid: '', name: '行政部' },
{
id: 'c',
pid: '',
name: '财务部',
children: [
{ id: 'd', pid: 'c', name: '财务核算部' },
{ id: 'e', pid: 'c', name: '税务管理部' }
],
},
];
```
### 1. 递归
```javascript
const fn = (list, rootId) => {
const arr = [];
list.forEach(item => {
if (item.pid === rootId) {
const children = fn(list, item.id);
if (children.length) {
item.children = children;
}
arr.push(item);
}
});
return arr;
};
```
拆分
```javascript
const fn1 = (list, rootId) => {
const arr = [];
list.forEach(item => {
if (item.pid === rootId) {
const children = fn2(list, item.id);
if (children.length) {
item.children = children;
}
arr.push(item);
}
});
return arr;
};
const fn2 = (list, rootId) => {
const arr = [];
list.forEach(item => {
if (item.pid === rootId) {
const children = fn3(list, item.id);
if (children.length) {
item.children = children;
}
arr.push(item);
}
});
return arr;
};
const fn3 = (list, rootId) => {
const arr = [];
list.forEach(item => {
if (item.pid === rootId) {
// 代码执行不会用到 fn4,所以没有定义 fn4 也不会出错
const children = fn4(list, item.id);
if (children.length) {
item.children = children;
}
arr.push(item);
}
});
return arr;
};
```
**调用 fn1 第 1 次循环**
1、第 1 次调用 fn1,传递`完整数据 list`,此时 rootId 为 ""
2、新建数组,循环 list,判断第 1 项的 pid 是否等于 "",发现相等,进入 if
3、调用 fn2,传递`完整数据 list`,rootId 为当前项的 id,就是 'a'
4、新建数组,循环 list,依次判断每一项的 pid 是否等于 'a'
5、发现没有 pid 等于 'a',所以不会进入 if,等循环结束,返回空数组给 fn2,然后赋值给 children
6、此时 fn1 函数调用时的第 1 次循环,把 item 装进 arr
**调用 fn1 第 2 次循环**
1、循环 list,判断第 2 项的 pid 是否等于 "",发现相等,进入 if
2、调用 fn2,传递`完整数据 list`,rootId 为当前项的 id,就是 'b'
3、新建数组,循环 list,依次判断每一项的 pid 是否等于 'b'
4、发现没有 pid 等于 'b',所以不会进入 if,等循环结束,返回空数组给 fn2,然后赋值给 children
5、此时 fn1 函数调用时的第 2 次循环,把 item 装进 arr
**调用 fn1 第 3 次循环**
1、循环 list,判断第 3 项的 pid 是否等于 "",发现相等,进入 if
2、调用 fn2,传递`完整数据 list`,rootId 为当前项的 id,就是 'c'
3、新建数组,循环 list,依次判断每一项的 pid 是否等于 'c'
4、发现第 4 项的 pid 等于 'c',进入 if(注意调用 fn2 的循环还没有走完!)
5、调用 fn3,传递`完整数据 list`,rootId 为当前项的 id,就是 'd'
6、新建数组,循环 list,依次判断每一项的 pid 是否等于 'd'
7、发现没有 pid 等于 'd',所以不会进入 if,等循环结束,返回空数组给 fn3,然后赋值给 children
8、此时 fn2 函数调用时的第 4 次循环,把 item 装进 arr,注意这个 arr 是 fn2 函数中的 arr
9、继续,发现第 5 项的 pid 等于 'c',进入 if
10、调用 fn3,传递`完整数据 list`,rootId 为当前项的 id,就是 'e'
11、新建数组,循环 list,依次判断每一项的 pid 是否等于 'e'
12、发现没有 pid 等于 'e',所以不会进入 if,等循环结束,返回空数组给 fn3,然后赋值给 children
13、此时 fn2 函数调用时的第 5 次循环,把 item 装进 arr,注意这个 arr 是 fn2 函数中的 arr
14、fn2 执行完毕返回的 arr 中就包含了最后 2 项,返回给 fn2 调用处
15、...
**调用 fn1 第 4 次循环**
继续循环到第 4 项、第 5 项的 pid 都不等于 "",最终 fn1 函数返回前面收集好的 arr
### 2. array-to-tree
```javascript
const arrayToTree = require('array-to-tree');
arrayToTree(arr, { parentProperty: 'pid', customID: 'id' }); // result is ok
```
### 3. performant-array-to-tree
更建议用这个,怎么用,自己找答案
```js
this.list = arrayToTree(await getPermissionList(), {
parentId: 'pid', // 设置嵌套的字段
rootParentIds: { '0': true }, // 设置根节点
dataField: null // 不要嵌套,children 里面直接是对象
})
```
## 11. 关于 Excel 的解析
xlsx:拿到上传的文件交给 xlsx 解析成具体的数据
## 12. ES2020 动态导入
```html
Document
```
index.js
```js
export const sum = (a, b) => a + b;
```
注意:要在服务器环境下测试
## 13. 图片上传组件
1、图片预览
```vue
```
2、根据 `fileList` 的长度控制上传按钮显示与否
```vue
```
3、展示本地图片
```vue
```
4、上传之前的校验
```vue
```
5、自定义上传操作
```vue
```
6、上传成功后展示腾讯云返回的图片地址
```js
export default {
methods: {
upload(params) {
if (params.file) {
cos.putObject({
Bucket: 'ifer-1253924894', // 存储桶
Region: 'ap-nanjing', // 地域
Key: params.file.name, // 文件名
Body: params.file, // 要上传的文件对象
StorageClass: 'STANDARD', // 上传的模式类型 直接默认 标准模式即可
}, (err, data) => {
if (!err && data.statusCode === 200) {
this.fileList = this.fileList.map(item => {
if (item.uid === this.currentFileUid) {
return { url: 'http://' + data.Location, upload: true }
}
return item
})
}
})
}
}
}
}
```
7、上传进度的处理
```js
export default {
methods: {
beforeUpload(file) {
// #1
this.showPercent = true
},
upload(params) {
if (params.file) {
cos.putObject({
// #2
onProgress: (params) => {
this.percent = params.percent * 100
}
}, (err, data) => {
if (!err && data.statusCode === 200) {
// #3
setTimeout(() => {
this.showPercent = false // 隐藏进度条
this.percent = 0 // 进度归0
}, 2000)
}
})
}
}
}
}
```
8、展示【个人详情】中的员工头像和员工照片
```js
export default {
created() {
this.getPersonalDetail()
},
methods: {
async getUserDetailById() {
this.userInfo = await getUserDetailById(this.userId)
// 有 staffPhoto 并且去除左右空格后还是有的,排除了 ' ' 这种情况
if (this.userInfo.staffPhoto && this.userInfo.staffPhoto.trim()) {
// 这里我们赋值,同时需要给赋值的地址一个标记 upload: true
this.$refs.staffPhoto.fileList = [{ url: this.userInfo.staffPhoto, upload: true }]
}
},
}
}
```
9、保存信息的时候【带上照片】
```js
export default {
methods: {
async savePersonal() {
const fileList = this.$refs.myStaffPhoto.fileList
// 判断是不是有一个没有上传完成
if (fileList.some(item => !item.upload)) {
this.$message.warning('您当前还有图片没有上传完成!')
return
}
await updatePersonal({ ...this.formData, staffPhoto: fileList && fileList.length ? fileList[0].url : ' ' })
this.$message.success('保存基础信息成功')
}
}
}
```
## 14. 图片转二维码
`qr-code`
## 15. 自定义打印区域
`vue-print-nb`
## 16. 页面访问和权限控制

## 17. 按钮/功能级别的权限控制
## 18. 全屏/切换主题
## 19. 国际化(重点掌握 i18n)
## 20. 总结
1、背景
2、功能
3、技术栈:`vue-element-admin`
4、项目中的难点和亮点
5、解决方案
