携手创作,共同成长!这是我参与「AdminJS · 8 月更文挑战」的第N天,点击查看活动详情 >>PS:第一天。
1.防抖
// 防抖
function debounce (callback,time){
let timer =null
return function (e) {
// this
// arguments[0] => e
clearTimeout(timer)
timer = setTimeout(() => {
// callback(e) // window.callback()
// this => ?
callback.call(this, e) // window.callback()
// callback.apply(this, arguments)
}, time)
}
}
const oDiv = document.querySelector('.boxbox')
oDiv.onmousemove = debounce(function(e){
let x = e.pageX - this.offsetLeft
let y = e.pageY - this.offsetTop
this.innerHTML = `x: ${x}, y: ${y}`
},500)
2.节流
// 节流
function throttle(callback, time) {
let bBar = true
return function () {
if (!bBar) return
bBar = false
setTimeout(() => {
callback.apply(this, arguments)
bBar = true
}, time)
}
}
const oDiv = document.querySelector('.boxbox')
oDiv.onmousemove = throttle(function (e) {
const x = e.pageX - this.offsetLeft
const y = e.pageY - this.offsetTop
this.innerHTML = `x: ${x}, y: ${y}`
}, 500)
3.深度拷贝
const obj1 = {
name: 'ifer',
age: 18,
s: Symbol(),
fn: function () {
return this
},
reg: /\d/,
un: undefined,
d: new Date(),
n: 2n,
}
const clone = (target) => {
// 使用万能检测数据的方法检测数据
const type = Object.prototype.toString.call(target)
// #1 日期或正则,'[Object date]' / '[Object regexp]'
if (/(date|regexp)/i.test(type)) return new target.constructor(target)
// #2 错误对象,'[Object error]'
if (/error/i.test(type)) return new target.constructor(target.message)
// #3 函数,'[Object function]'
if (/function/i.test(type)) return new Function('return ' + target.toString())()
// #4 简单数据类型
if (target === null || typeof target !== 'object') return target
// #5 数组或对象
const result = new target.constructor()
for (let attr in target) {
result[attr] = clone(target[attr])
}
return result
}
const obj2 = clone(obj1)
console.log(obj2)
4.树形数据结构转换
const arr = [
{ id: "a", pid: "", name: "总裁办" },
{ id: "b", pid: "", name: "行政部" },
{ id: "c", pid: "", name: "财务部" },
{ id: "d", pid: "c", name: "财务核算部" },
{ id: "e", pid: "c", name: "税务管理部" },
{ id: "f", pid: "e", name: "税务管理部-分部" },
];
// 函数版
function tranListToTreeData(list) {
// 1. 定义两个变量
const treeList = []; const map = {}
// 2. 建立一个映射关系,并给每个元素补充children属性.
// 映射关系: 目的是让我们能通过id快速找到对应的元素
// 补充children:让后边的计算更方便
list.forEach(item => {
if (!item.children) {
item.children = []
}
map[item.id] = item
})
// 循环
list.forEach(item => {
// 对于每一个元素来说,先找它的上级
// 如果能找到,说明它有上级,则要把它添加到上级的children中去
// 如果找不到,说明它没有上级,直接添加到 treeList
const parent = map[item.pid]
// 如果存在上级则表示item不是最顶层的数据
if (parent) {
parent.children.push(item)
} else {
// 如果不存在上级 则是顶层数据,直接添加
treeList.push(item)
}
})
// 返回
return treeList
}
console.log( tranListToTreeData(arr));
5.获取URL hash后面的参数
export getHashQueryString = (key) => {
const after = window.location.href.split('?')[1]
if (after) {
const reg = new RegExp(`(^|&)${ key }=([^&]*)(&|$)`)
const r = after.match(reg)
if (r != null) {
return decodeURIComponent(r[2])
}
return null
}
return null
}
6.判断浏览器是否是移动端
export function isMobile() {
const agent = navigator.userAgent;
const k = ["android", "iphone", "ipod", "ipad", "windows phone", "mqqbrowser"];
let flag = false;
// Windows
if (agent.indexOf("Windows NT") < 0 || (agent.indexOf("Windows NT") >= 0 && agent.indexOf("compatible; MSIE 9.0;") >= 0)) {
// Mac PC
if (agent.indexOf("Windows NT") < 0 && agent.indexOf("Macintosh") < 0) {
for (let item of k) {
if (agent.indexOf(item) >= 0) {
flag = true;
break;
}
}
}
}
return flag;
}
7.判断文件类型
export function checkFileName(fileName, list) {
if (typeof fileName !== 'string') return;
let name = fileName.toLowerCase();
return list.some(i => name.endsWith(`.${i}`) === true)
}
export function isImage(fileName) {
return checkFileName(fileName, ['png', 'jpeg', 'jpg', 'png', 'bmp'])
}
export function isH5Video(fileName) {
return checkFileName(fileName, ['mp4', 'webm', 'ogg'])
}
export function isPdf(fileName) {
return checkFileName(fileName, ['pdf'])
}
export function isWord(fileName) {
return checkFileName(fileName, ['doc', 'docx'])
}
export function isExcel(fileName) {
return checkFileName(fileName, ['xlsx', 'xls'])
}
8.查询元素是否存在某个 class
export function hasClass(el, className) {
let reg = new RegExp('(^|\s)' + className + '(\s|$)');
return reg.test(el.className);
}
9.给某个元素添加 class
export function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let curClass = el.className.split(' ');
curClass.push(className);
el.className = curClass.join(' ');
}
10.图片上传
- 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
- 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
上传
</button>
<script>
oBtn.onclick = function() {
oFile()
}
</script>
- 监听 type file 框的 onchange 事件,通过
e.targetf.files
拿到文件信息。
oFile.onchange = function() {
e.target.files
}
- 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
}
- 使用 axios 把数据传到后端。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
}
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
this.value = ''
}
10.图片预览是怎么做的?
- 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
- 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
11.随机生成颜色
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
// 防抖
function debounce (callback,time){
let timer =null
return function (e) {
// this
// arguments[0] => e
clearTimeout(timer)
timer = setTimeout(() => {
// callback(e) // window.callback()
// this => ?
callback.call(this, e) // window.callback()
// callback.apply(this, arguments)
}, time)
}
}
const oDiv = document.querySelector('.boxbox')
oDiv.onmousemove = debounce(function(e){
let x = e.pageX - this.offsetLeft
let y = e.pageY - this.offsetTop
this.innerHTML = `x: ${x}, y: ${y}`
},500)
// 节流
function throttle(callback, time) {
let bBar = true
return function () {
if (!bBar) return
bBar = false
setTimeout(() => {
callback.apply(this, arguments)
bBar = true
}, time)
}
}
const oDiv = document.querySelector('.boxbox')
oDiv.onmousemove = throttle(function (e) {
const x = e.pageX - this.offsetLeft
const y = e.pageY - this.offsetTop
this.innerHTML = `x: ${x}, y: ${y}`
}, 500)
3.深度拷贝
const obj1 = {
name: 'ifer',
age: 18,
s: Symbol(),
fn: function () {
return this
},
reg: /\d/,
un: undefined,
d: new Date(),
n: 2n,
}
const clone = (target) => {
// 使用万能检测数据的方法检测数据
const type = Object.prototype.toString.call(target)
// #1 日期或正则,'[Object date]' / '[Object regexp]'
if (/(date|regexp)/i.test(type)) return new target.constructor(target)
// #2 错误对象,'[Object error]'
if (/error/i.test(type)) return new target.constructor(target.message)
// #3 函数,'[Object function]'
if (/function/i.test(type)) return new Function('return ' + target.toString())()
// #4 简单数据类型
if (target === null || typeof target !== 'object') return target
// #5 数组或对象
const result = new target.constructor()
for (let attr in target) {
result[attr] = clone(target[attr])
}
return result
}
const obj2 = clone(obj1)
console.log(obj2)
4.树形数据结构转换
const arr = [
{ id: "a", pid: "", name: "总裁办" },
{ id: "b", pid: "", name: "行政部" },
{ id: "c", pid: "", name: "财务部" },
{ id: "d", pid: "c", name: "财务核算部" },
{ id: "e", pid: "c", name: "税务管理部" },
{ id: "f", pid: "e", name: "税务管理部-分部" },
];
// 函数版
function tranListToTreeData(list) {
// 1. 定义两个变量
const treeList = []; const map = {}
// 2. 建立一个映射关系,并给每个元素补充children属性.
// 映射关系: 目的是让我们能通过id快速找到对应的元素
// 补充children:让后边的计算更方便
list.forEach(item => {
if (!item.children) {
item.children = []
}
map[item.id] = item
})
// 循环
list.forEach(item => {
// 对于每一个元素来说,先找它的上级
// 如果能找到,说明它有上级,则要把它添加到上级的children中去
// 如果找不到,说明它没有上级,直接添加到 treeList
const parent = map[item.pid]
// 如果存在上级则表示item不是最顶层的数据
if (parent) {
parent.children.push(item)
} else {
// 如果不存在上级 则是顶层数据,直接添加
treeList.push(item)
}
})
// 返回
return treeList
}
console.log( tranListToTreeData(arr));
5.获取URL hash后面的参数
export getHashQueryString = (key) => {
const after = window.location.href.split('?')[1]
if (after) {
const reg = new RegExp(`(^|&)${ key }=([^&]*)(&|$)`)
const r = after.match(reg)
if (r != null) {
return decodeURIComponent(r[2])
}
return null
}
return null
}
6.判断浏览器是否是移动端
export function isMobile() {
const agent = navigator.userAgent;
const k = ["android", "iphone", "ipod", "ipad", "windows phone", "mqqbrowser"];
let flag = false;
// Windows
if (agent.indexOf("Windows NT") < 0 || (agent.indexOf("Windows NT") >= 0 && agent.indexOf("compatible; MSIE 9.0;") >= 0)) {
// Mac PC
if (agent.indexOf("Windows NT") < 0 && agent.indexOf("Macintosh") < 0) {
for (let item of k) {
if (agent.indexOf(item) >= 0) {
flag = true;
break;
}
}
}
}
return flag;
}
7.判断文件类型
export function checkFileName(fileName, list) {
if (typeof fileName !== 'string') return;
let name = fileName.toLowerCase();
return list.some(i => name.endsWith(`.${i}`) === true)
}
export function isImage(fileName) {
return checkFileName(fileName, ['png', 'jpeg', 'jpg', 'png', 'bmp'])
}
export function isH5Video(fileName) {
return checkFileName(fileName, ['mp4', 'webm', 'ogg'])
}
export function isPdf(fileName) {
return checkFileName(fileName, ['pdf'])
}
export function isWord(fileName) {
return checkFileName(fileName, ['doc', 'docx'])
}
export function isExcel(fileName) {
return checkFileName(fileName, ['xlsx', 'xls'])
}
8.查询元素是否存在某个 class
export function hasClass(el, className) {
let reg = new RegExp('(^|\s)' + className + '(\s|$)');
return reg.test(el.className);
}
9.给某个元素添加 class
export function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let curClass = el.className.split(' ');
curClass.push(className);
el.className = curClass.join(' ');
}
10.图片上传
- 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
- 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
上传
</button>
<script>
oBtn.onclick = function() {
oFile()
}
</script>
- 监听 type file 框的 onchange 事件,通过
e.targetf.files
拿到文件信息。
oFile.onchange = function() {
e.target.files
}
- 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
}
- 使用 axios 把数据传到后端。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
}
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
this.value = ''
}
10.图片预览是怎么做的?
- 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
- 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
11.随机生成颜色
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
const obj1 = {
name: 'ifer',
age: 18,
s: Symbol(),
fn: function () {
return this
},
reg: /\d/,
un: undefined,
d: new Date(),
n: 2n,
}
const clone = (target) => {
// 使用万能检测数据的方法检测数据
const type = Object.prototype.toString.call(target)
// #1 日期或正则,'[Object date]' / '[Object regexp]'
if (/(date|regexp)/i.test(type)) return new target.constructor(target)
// #2 错误对象,'[Object error]'
if (/error/i.test(type)) return new target.constructor(target.message)
// #3 函数,'[Object function]'
if (/function/i.test(type)) return new Function('return ' + target.toString())()
// #4 简单数据类型
if (target === null || typeof target !== 'object') return target
// #5 数组或对象
const result = new target.constructor()
for (let attr in target) {
result[attr] = clone(target[attr])
}
return result
}
const obj2 = clone(obj1)
console.log(obj2)
const arr = [
{ id: "a", pid: "", name: "总裁办" },
{ id: "b", pid: "", name: "行政部" },
{ id: "c", pid: "", name: "财务部" },
{ id: "d", pid: "c", name: "财务核算部" },
{ id: "e", pid: "c", name: "税务管理部" },
{ id: "f", pid: "e", name: "税务管理部-分部" },
];
// 函数版
function tranListToTreeData(list) {
// 1. 定义两个变量
const treeList = []; const map = {}
// 2. 建立一个映射关系,并给每个元素补充children属性.
// 映射关系: 目的是让我们能通过id快速找到对应的元素
// 补充children:让后边的计算更方便
list.forEach(item => {
if (!item.children) {
item.children = []
}
map[item.id] = item
})
// 循环
list.forEach(item => {
// 对于每一个元素来说,先找它的上级
// 如果能找到,说明它有上级,则要把它添加到上级的children中去
// 如果找不到,说明它没有上级,直接添加到 treeList
const parent = map[item.pid]
// 如果存在上级则表示item不是最顶层的数据
if (parent) {
parent.children.push(item)
} else {
// 如果不存在上级 则是顶层数据,直接添加
treeList.push(item)
}
})
// 返回
return treeList
}
console.log( tranListToTreeData(arr));
5.获取URL hash后面的参数
export getHashQueryString = (key) => {
const after = window.location.href.split('?')[1]
if (after) {
const reg = new RegExp(`(^|&)${ key }=([^&]*)(&|$)`)
const r = after.match(reg)
if (r != null) {
return decodeURIComponent(r[2])
}
return null
}
return null
}
6.判断浏览器是否是移动端
export function isMobile() {
const agent = navigator.userAgent;
const k = ["android", "iphone", "ipod", "ipad", "windows phone", "mqqbrowser"];
let flag = false;
// Windows
if (agent.indexOf("Windows NT") < 0 || (agent.indexOf("Windows NT") >= 0 && agent.indexOf("compatible; MSIE 9.0;") >= 0)) {
// Mac PC
if (agent.indexOf("Windows NT") < 0 && agent.indexOf("Macintosh") < 0) {
for (let item of k) {
if (agent.indexOf(item) >= 0) {
flag = true;
break;
}
}
}
}
return flag;
}
7.判断文件类型
export function checkFileName(fileName, list) {
if (typeof fileName !== 'string') return;
let name = fileName.toLowerCase();
return list.some(i => name.endsWith(`.${i}`) === true)
}
export function isImage(fileName) {
return checkFileName(fileName, ['png', 'jpeg', 'jpg', 'png', 'bmp'])
}
export function isH5Video(fileName) {
return checkFileName(fileName, ['mp4', 'webm', 'ogg'])
}
export function isPdf(fileName) {
return checkFileName(fileName, ['pdf'])
}
export function isWord(fileName) {
return checkFileName(fileName, ['doc', 'docx'])
}
export function isExcel(fileName) {
return checkFileName(fileName, ['xlsx', 'xls'])
}
8.查询元素是否存在某个 class
export function hasClass(el, className) {
let reg = new RegExp('(^|\s)' + className + '(\s|$)');
return reg.test(el.className);
}
9.给某个元素添加 class
export function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let curClass = el.className.split(' ');
curClass.push(className);
el.className = curClass.join(' ');
}
10.图片上传
- 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
- 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
上传
</button>
<script>
oBtn.onclick = function() {
oFile()
}
</script>
- 监听 type file 框的 onchange 事件,通过
e.targetf.files
拿到文件信息。
oFile.onchange = function() {
e.target.files
}
- 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
}
- 使用 axios 把数据传到后端。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
}
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
this.value = ''
}
10.图片预览是怎么做的?
- 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
- 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
11.随机生成颜色
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
export getHashQueryString = (key) => {
const after = window.location.href.split('?')[1]
if (after) {
const reg = new RegExp(`(^|&)${ key }=([^&]*)(&|$)`)
const r = after.match(reg)
if (r != null) {
return decodeURIComponent(r[2])
}
return null
}
return null
}
export function isMobile() {
const agent = navigator.userAgent;
const k = ["android", "iphone", "ipod", "ipad", "windows phone", "mqqbrowser"];
let flag = false;
// Windows
if (agent.indexOf("Windows NT") < 0 || (agent.indexOf("Windows NT") >= 0 && agent.indexOf("compatible; MSIE 9.0;") >= 0)) {
// Mac PC
if (agent.indexOf("Windows NT") < 0 && agent.indexOf("Macintosh") < 0) {
for (let item of k) {
if (agent.indexOf(item) >= 0) {
flag = true;
break;
}
}
}
}
return flag;
}
7.判断文件类型
export function checkFileName(fileName, list) {
if (typeof fileName !== 'string') return;
let name = fileName.toLowerCase();
return list.some(i => name.endsWith(`.${i}`) === true)
}
export function isImage(fileName) {
return checkFileName(fileName, ['png', 'jpeg', 'jpg', 'png', 'bmp'])
}
export function isH5Video(fileName) {
return checkFileName(fileName, ['mp4', 'webm', 'ogg'])
}
export function isPdf(fileName) {
return checkFileName(fileName, ['pdf'])
}
export function isWord(fileName) {
return checkFileName(fileName, ['doc', 'docx'])
}
export function isExcel(fileName) {
return checkFileName(fileName, ['xlsx', 'xls'])
}
8.查询元素是否存在某个 class
export function hasClass(el, className) {
let reg = new RegExp('(^|\s)' + className + '(\s|$)');
return reg.test(el.className);
}
9.给某个元素添加 class
export function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let curClass = el.className.split(' ');
curClass.push(className);
el.className = curClass.join(' ');
}
10.图片上传
- 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
- 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
上传
</button>
<script>
oBtn.onclick = function() {
oFile()
}
</script>
- 监听 type file 框的 onchange 事件,通过
e.targetf.files
拿到文件信息。
oFile.onchange = function() {
e.target.files
}
- 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
}
- 使用 axios 把数据传到后端。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
}
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
this.value = ''
}
10.图片预览是怎么做的?
- 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
- 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
11.随机生成颜色
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
export function checkFileName(fileName, list) {
if (typeof fileName !== 'string') return;
let name = fileName.toLowerCase();
return list.some(i => name.endsWith(`.${i}`) === true)
}
export function isImage(fileName) {
return checkFileName(fileName, ['png', 'jpeg', 'jpg', 'png', 'bmp'])
}
export function isH5Video(fileName) {
return checkFileName(fileName, ['mp4', 'webm', 'ogg'])
}
export function isPdf(fileName) {
return checkFileName(fileName, ['pdf'])
}
export function isWord(fileName) {
return checkFileName(fileName, ['doc', 'docx'])
}
export function isExcel(fileName) {
return checkFileName(fileName, ['xlsx', 'xls'])
}
export function hasClass(el, className) {
let reg = new RegExp('(^|\s)' + className + '(\s|$)');
return reg.test(el.className);
}
9.给某个元素添加 class
export function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let curClass = el.className.split(' ');
curClass.push(className);
el.className = curClass.join(' ');
}
10.图片上传
- 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
- 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
上传
</button>
<script>
oBtn.onclick = function() {
oFile()
}
</script>
- 监听 type file 框的 onchange 事件,通过
e.targetf.files
拿到文件信息。
oFile.onchange = function() {
e.target.files
}
- 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
}
- 使用 axios 把数据传到后端。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
}
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
this.value = ''
}
10.图片预览是怎么做的?
- 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
- 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
11.随机生成颜色
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
export function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let curClass = el.className.split(' ');
curClass.push(className);
el.className = curClass.join(' ');
}
- 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
- 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
上传
</button>
<script>
oBtn.onclick = function() {
oFile()
}
</script>
- 监听 type file 框的 onchange 事件,通过
e.targetf.files
拿到文件信息。
oFile.onchange = function() {
e.target.files
}
- 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
}
- 使用 axios 把数据传到后端。
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
}
oFile.onchange = function() {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'xxx')
axios.post('/upload', formData)
this.value = ''
}
10.图片预览是怎么做的?
- 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
- 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
11.随机生成颜色
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
oBtn.onclick = function () {
oFile.click()
}
/* oFile.onchange = function () {
// 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
oImg.src = URL.createObjectURL(this.files[0])
} */
oFile.onchange = function () {
const reader = new FileReader()
// 可以把文件信息读成 base64
reader.readAsDataURL(this.files[0])
reader.onload = function () {
oImg.src = this.result
}
}
</script>
export function randomColor() {
return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}
12.下载文件
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
13.英转中(导出表格)
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}`
//手动写一个具有对应关系的对象
const userRelations = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber'
}
//第一个参数是枚举对象,第二个参数是原数组
function demandArr(userRelations,arr){
//创建一个空数组
const newArr = []
arr.forEach(item => {
//创建一个空对象
const obj = {}
//循环数组里的每一个对象
for (const k in item) {
const englishKey = userRelations[k]
obj[englishKey] = item[k]
}
newArr.push(obj)
})
}
// console.log(newArr)
14.中转英(导入表格)
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
15.路由守卫及页面权限控制
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
const data = [
{
入职日期: 44505,
姓名: "小张",
工号: "9002",
手机号: "13800000252",
转正日期: 44892,
部门: "总裁办"
},
{
入职日期: 44506,
姓名: "小李",
工号: 9006,
手机号: "13810000512",
转正日期: 44893,
部门: "总裁办",
}
]
const transExcel = (results) => {
const mapInfo = {
'入职日期': 'timeOfEntry',
'手机号': 'mobile',
'姓名': 'username',
'转正日期': 'correctionTime',
'工号': 'workNumber',
'部门': 'departmentName',
}
// results.reduce((sum,item) => {
// Object.keys(item)
// },{}
return results.map(item => {
const maps = {}
Object.keys(item).forEach(value => {
maps[mapInfo[value]] = item[value]
})
})
return maps
}
console.log(transExcel(data));
import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
document.title = 'hr人力项目--' + to.meta.title
// console.log(to, '跳转至', from)
// document.title = getPageTitle(to.meta.title)
NProgress.start() // 开启进度条
const token = store.state.user.token
if (token) {
// 已经登陆
if (to.path === '/login') {
// 如果当前在登录页,那么跳转首页
next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
NProgress.done() // 关闭进度条
} else {
if (!store.getters.userId) {
// 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token 2)并未在登录页上 ]
const menuList = await store.dispatch('user/getUserInfo')
console.log(menuList, 'menuListdsadasdsdsa')
console.log(asyncRoutes, 'asyncRoutes')
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0]
console.log(route.children[0], 'route.children[0].name')
return menuList.includes(routeName)
})
filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
// console.log(filterRoutes, 'filterRoutesfilterRoutes')
router.addRoutes(filterRoutes)
store.commit('menus/setMenuList', filterRoutes)
// next({ ...to, replace: true })
next(to.path)
// 重新加载页面
} else {
next()
}
// 如果没有在登录页,那么放行,
}
} else {
// 没有登录
if (whiteList.includes(to.path)) {
// 如果此时在白名单页面上,那么放行
next()
} else {
// 如果此时不在白名单页面上,那么跳转至登录页
next('/login')
NProgress.done() // 关闭进度条
}
}
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
NProgress.done() // 关闭进度条
})
16.依据后台返回的用户权限进行右侧菜单渲染
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
17.文件下载
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>
<!-- 菜单区域 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
<!-- template:模板标签 不能使用:key -->
<template v-for="(item,index) in menusList">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item
:index="item.indexPath"
:key="item.indexPath"
v-if="!item.children"
><i :class="item.icon"></i>{{ item.title }}</el-menu-item
>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="index" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<el-menu-item
:index="childItem.indexPath"
v-for="childItem in item.children"
:key="childItem.indexPath"
><i :class="childItem.icon"></i
>{{ childItem.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
// 返回数据
{
"code": 0,
"message": "获取左侧菜单成功!",
"data": [
{
"indexPath": "/home",
"title": "首页",
"icon": "el-icon-s-home",
"children": null
},
{
"indexPath": "2",
"title": "文章管理",
"icon": "el-icon-s-order",
"children": [
{
"indexPath": "/art-cate",
"title": "文章分类",
"icon": "el-icon-s-data"
},
{
"indexPath": "/art-list",
"title": "文章列表",
"icon": "el-icon-document-copy"
}
]
},
{
"indexPath": "3",
"title": "个人中心",
"icon": "el-icon-user-solid",
"children": [
{
"indexPath": "/user-info",
"title": "基本资料",
"icon": "el-icon-s-operation"
},
{
"indexPath": "/user-avatar",
"title": "更换头像",
"icon": "el-icon-camera"
},
{
"indexPath": "/user-pwd",
"title": "重置密码",
"icon": "el-icon-key"
}
]
}
]
}
<body>
<a href="#" download>点击发大财</a>
<button >点击开豪车</button>
<script>
/**
* 下载文件
* @param {String} path - 下载地址/下载请求地址。
* @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
*/
function downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
document.querySelector('a').addEventListener('click',function(){
console.log(11);
downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
</script>