本次写一下我在开发过程中遇到的使用频率高点的部分以及小技巧。或者基础知识。
1.上传下载走过的坑
首先参看elementUI官方关于<el-upload>组件的文档 https://full-ui.fast-daily.tuya-inc.cn/#/zh-CN/component/upload
开发中参考文档如下:https://www.cnblogs.com/sinosaurus/p/9266060.html
image一、上传文件实现
两种实现方式:
1、直接action
<el-upload
class="upload-file"
drag
:action="doUpload"
:data="pppss">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
:action,必选参数,上传的地址,String类型。data()需要使用代理转发,要不然会有跨域的问题
:data,上传时附带的额外参数,object类型。用于传递其他的需要携带的参数,比如下面的srid
data(){
return {
,doUpload:'/api/up/file'
,pppss:{
srid:''
}
}
},
2、利用before-upload属性
此种方式有个弊端,就是action是必选的参数,那么action如果和post的url一致,总会请求2次,所以一般把action随便写一个url,虽然不影响最终效果,但是这样会在控制台总有404错误报出
<el-upload
class="upload-file"
drag
:action="doUpload"
:before-upload="beforeUpload"
ref="newupload"
multiple
:auto-upload="false">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
beforeUpload(file){
let fd = new FormData();
fd.append('file',file);//传文件
fd.append('srid',this.aqForm.srid);//传其他参数
axios.post('/api/up/file',fd).then(function(res){
alert('成功');
})
},
newSubmitForm(){//确定上传
this.$refs.newupload.submit();
}
二、常用方法介绍
1、动态改变action地址
action是一个必填参数,且其类型为string,我们把action写成:action,然后后面跟着一个方法名,调用方法,返回你想要的地址,实现动态的去修改上传地址
//html 代码
<el-upload :action="UploadUrl()" :on-success="UploadSuccess" :file-list="fileList">
<el-button size="small" type="primary" >点击上传</el-button>
</el-upload>
// js 代码在 methods中写入需要调用的方法
methods:{
UploadUrl:function(){
return "返回需要上传的地址";
}
}
2、在文件上传前做类型大小等限制
(1)一种方式是,加accpet属性
image(2)另一种方式是在上传前的触发函数里面去判断
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isGIF = file.type === 'image/gif';
const isPNG = file.type === 'image/png';
const isBMP = file.type === 'image/bmp';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG && !isGIF && !isPNG && !isBMP) {
this.common.errorTip('上传图片必须是JPG/GIF/PNG/BMP 格式!');
}
if (!isLt2M) {
this.common.errorTip('上传图片大小不能超过 2MB!');
}
return (isJPG || isBMP || isGIF || isPNG) && isLt2M;
},
3、同时传递form表单及有多个upload文件该如何传递?
newSubmitForm () {
this.$refs['newform'].validate((valid) => {
if (valid) {
//表单的数据
this.uploadForm.append('expName', this.newform.expName)
this.uploadForm.append('expSn', this.newform.expSn)
this.uploadForm.append('groupId', this.newgroupId)
this.uploadForm.append('subGroupId', this.newsubgroupId)
this.uploadForm.append('expvmDifficulty', this.newform.expvmDifficulty)
newExp(this.uploadForm).then(res => {
if (res.code === 400) {
this.$message.error(res.error)
} else if (res.code === 200) {
this.$message.success('上传成功!')
}
})
this.$refs.uploadhtml.submit() // 提交时分别触发各上传组件的before-upload函数
this.$refs.uploadfile.submit()
this.$refs.uploadvideo.submit()
} else {
console.log('error submit!!')
return false
}
})
},
newHtml (file) { // before-upload
this.uploadForm.append('html', file)
return false
},
newFiles (file) {
this.uploadForm.append('file[]', file)
return false
},
newVideo (file) {
this.uploadForm.append('video', file)
return false
}
export function newExp (data) {
return axios({
method: 'post', // 方式一定是post
url: '你的后台接收函数路径',
timeout: 20000,
data: data // 参数需要是单一的formData形式
})
}
注意:(1)对于多个上传组件来说,需要分别触发,去给FormData append数据
(2)接收多文件一定要是数组形式的file[],this.uploadForm.append('file[]', file)
4、如何传递文件和其他参数
就像第一节那样,如果不使用action实现上传,而使用before-upload属性也能实现上传的效果。
before-upload属性,这是一个function类型的属性,默认参数是当前文件,只要能传递这个文件也能实现效果
要传递这个方法就需要new一个formdata对象,然后对这个对象追加key和value,类似于postman测试时那样。
另外注意:传递formdata和data不能一起传递,要传递formdata就不能有data,所以对于其他参数的传递,也要改为
beforeUpload (file,id) {
let fd = new FormData()
fd.append('file', file)
fd.append('id',id)//其他参数
axios.post(url, fd, {
})
},
而不能使用这种又有FormData,又有data的模式
beforeUpload (file,id) {
let fd = new FormData()
fd.append('key', file, fileName)
axios.post(url, fd,{
data:{
id:id
},
headers: {
'Content-Type': 'multipart/form-data'
}
})
},
我开发中的案例如下:
<el-form :model="cVO" :rules="uploadRules" ref="uploadRulesRef">
<el-form-item
label="名称"
:label-width="120px"
prop="xxx"
>
<el-input
v-model="cVO.xxx"
autocomplete="off"
placeholder="请填写名称"
class="form-item"
></el-input>
</el-form-item>
<el-form-item
label="上传"
prop="url"
:label-width="120px"
>
<el-upload
class="form-item"
drag //是否启用拖拽上传
action="https://jsonplaceholder.typicode.com/posts/" //上传的路径
:before-upload="beforeUpload"
:file-list="fileList"
ref="uploadRef"
>
<div>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
</div>
</el-upload>
</el-form-item>
</el-form>
beforeUpload(zipFile) {
this.$refs['uploadRulesRef'].validate((valid) => {
if (!valid) {
this.fileList = []
return false
}
let fd = new FormData()
fd.append('zipFile', zipFile)
fd.append('xxx', this.cVO.xxx)
import(fd)
.then(() => {
this.$message.success('上传成功!')
})
.catch(() => {
return false
})
})
},
@RequestMapping(value ="import", method = { RequestMethod.POST })
@ResponseBody
public import( @RequestParam(value ="zipFile", required =false) MultipartFile multipartZipFile,
@RequestParam(value ="xxx", required =false) String xxx,
HttpServletRequest request)
2.解构赋值(es6)
在ES5中,开发者们为了从对象和数组中获取特定数据并赋值给变量,编写了许多看起来同质化的代码,如下例子。
let options = {
repeat: true,
save: false
};
// 从对象中提取数据
let repeat = options.repeat,
save = options.save;
这段代码从options对象中提取repeat和save的值,并将其存储为同名局部变量,提取的过程极为相似,如果要提取更多变量,则必须依次编写类似的代码来为变量赋值,如果其中还包含嵌套结构,只靠遍历是找不到真实信息的,必须要深入挖掘整个数据结构才能找到所需数据,所以ES6添加了解构功能,将数据结构打散的过程变得更加简单,可以从打散后更小的部分中获取所需信息。
对象解构
let node = {
type: "Identifier",
name: "foo"
},
type = "Literal",
name = 5;
function outputInfo(value) {
console.log(value === node); // true
}
outputInfo({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"
在这个示例中,声明变量type和name时初始化了一个值,在后面几行中,通过解构赋值的方法,从node对象读取相应的值重新为这两个变量赋值。
[注意]1.一定要用一对小括号包裹解构赋值语句,JS引擎将一对开放的花括号视为一个代码块。语法规定,代码块语句不允许出现在赋值语句左侧,添加小括号后可以将块语句转化为一个表达式,从而实现整个解构赋值过程。
2.解构赋值表达式(也就是=右侧的表达式)如果为null或undefined会导致程序抛出错误。也就是说,任何尝试读取null或undefined的属性的行为都会触发运行时错误。
解构赋值表达式的值与表达式右侧(也就是=右侧)的值相等,如此一来,在任何可以使用值的地方都可以使用解构赋值表达式。
当出现嵌套对象时,node.loc.start被存储在了新的局部变量localStart中。解构模式可以应用于任意层级深度的对象,且每一层都具备同等的功能。
let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
}
};
let { loc: { start }} = node;
console.log(start.line); // 1
console.log(start.column); // 1
let { loc: { start: localStart }} = node;
console.log(localStart.line); // 1
console.log(localStart.column); // 1
数组解构
与对象解构的语法相比,数组解构就简单多了,它使用的是数组字面量,且解构操作全部在数组内完成,而不是像对象字面量语法一样使用对象的命名属性。我们通过值在数组中的位置进行选取,且可以将其存储在任意变量中,未显式声明的元素都会直接被忽略。
let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
let [ , , thirdColor ] = colors;
console.log(thirdColor); // "blue"
firstColor = "black",
secondColor = "purple";
[ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
这段代码使用解构赋值语法从colors中获取第3个元素,thirdColor前的逗号是前方元素的占位符,无论数组中的元素有多少个,都可以通过这种方法提取想要的元素,不需要为每一个元素都指定变量名。
数组解构语法还有一个独特的用例:交换两个变量的值。在排序算法中,值交换是一个非常常见的操作,如果要在ES5中交换两个变量的值,则须引入第三个临时变量。如果使用数组解构赋值语法,就不再需要额外的变量了。
// 在 ES5 中互换值
let a = 1,
b = 2,
tmp;
tmp = a;
a = b;
b = tmp;
console.log(a); // 2
console.log(b); // 1
// 在 ES6 中互换值
let a = 1,
b = 2;
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1
在ES5中,开发者们经常使用concat()方法来克隆数组,在ES6中,可以通过不定元素的语法来实现相同的目标 。
// 在 ES5 中克隆数组
var colors = [ "red", "green", "blue" ];
var clonedColors = colors.concat();
console.log(clonedColors); //"[red,green,blue]"
// 在 ES6 中克隆数组
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;
console.log(clonedColors); //"[red,green,blue]"
函数具有不定参数,而在数组解构语法中有一个相似的概念——不定元素。在数组中,可以通过...语法将数组中的其余元素赋值给一个特定的变量
let colors = [ "red", "green", "blue" ];
let [ firstColor, ...restColors ] = colors;
console.log(firstColor); // "red"
console.log(restColors.length); // 2
console.log(restColors[0]); // "green"
console.log(restColors[1]); // "blue"
其他解构
字符串也可以解构赋值。这是因为,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
console.log(a);//"h"
console.log(b);//"e"
console.log(c);//"l"
console.log(d);//"l"
console.log(e);//"o"
const {length} = 'hello';
console.log(length);//5
数值和布尔值解构也可以结构赋值,如果等号右边是数值和布尔值,则会先转为对象解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
let {toString:s1} = 123;
console.log(s1 === Number.prototype.toString);//true
let {toString:s2} = true;
console.log(s2 === Boolean.prototype.toString);//true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
3.枚举
在后端为了一些固定值得字段重复使用,有着枚举的概念。使用枚举能够提高代码维护性,确保变量合法,提高代码可读性。后端的枚举类大多是为了各个模块的交互使用,或者是可读性等等,前端试用期使用枚举不仅仅可以有这些作用,同时可以用作前端下拉框,单选框,复选框的使用,更加直观。
在common或者个人模块下创建单独枚举js,如enums.js。然后导入到页面@/common/Enum.js并添加一个字面量对象。
logType: new Enum()
.add('INFO', '普通级别', 1)
.add('WARN', '警告级别', 2)
.add('ERROR', '错误级别', 3)
如需要表单选择可如下使用:
<el-select v-model="query.status" clearable placeholder="请选择">
<-- 传统做法 -->
<el-option value="1" :label="普通级别 "></el-option>
<el-option value="2" :label="警告级别 "></el-option>
<el-option value="3" :label="错误级别 "></el-option>
<-- 基于枚举类的方法 -->
<el-option v-for="item in enums.logType"
:key="item.value" :value="item.value" :label="item.label">
</el-option>
</el-select>
如需页面以及table显示枚举lable可如下使用:
//可以避免为每个枚举值进行判断后,再取其label,后端返回staus:1
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
<-- 传统做法:定义function通过switch或者if判断并返回label -->
{{ switch(scope.row.status) case 0:... case 1... case 2.... }}
<-- 基于枚举类的方法 -->
{{ enums.logType.getLabelByValue(scope.row.status) }}
</template>
</el-table-column>
4.组件的提取复用
作为一个好的前端,许需要具备的技能就是合理的抽取可复用写作自己的组件。既可以提高代码简洁性,如果后期需要修改功能又可以方便后期维护。我也在逐步的提高自己这方面的能力。提取组件的前提是能很好的学会父子组件的传值。
4.1搜索选择框
一般都是用在模糊搜索匹配上。比如很多公司内部项目需要关联某些人员,比如增加数据管理员,操作人,负责人之类的操作。就可以单独抽取一个支持模糊搜索的下拉框作为通用组件重复使用。
<el-select
v-model="userInfo"
filterable
remote //远程搜索
multiple //多选
:loading="this.load"
placeholder="请输入姓名或者工号"
:remote-method="getUsers"
@clear="clear" //可清除
@change="changeValue" //有需要自己定义
>
<el-option
v-for="item in userList"
:key="item.username"
:label="item.nick"
:value="item.username"
>
</el-option>
</el-select>
//js部分
clear() {
this.userName = ''
},
getUsers(value) {
setTimeout(() => {
this.load = false
if (value != '') {
this.getUserApi(value)
}
}, 200) //防抖
},
getUserApi(value) {
searchUser(value)
.then((res) => {
this.userList = res
})
.catch((error) => {
this.$message(error, 'error')
})
}
4.2验证校验框
很多时候我们在调用一些接口需要校验身份,或者先去确认一下数据权限之类的,就需要做这个啦。
<el-dialog
:visible.sync="showCheckDialog"
@close="closeImport"
append-to-body
title="校验身份"
>
<el-form :model="checkInfo">
<el-form-item label="验证码" :label-width="formLabelWidth">
<el-input
class="form-item-class"
placeholder="请输入验证码"
v-model="checkInfo.pwd"
clearable
>
</el-input>
</el-form-item>
<el-form-item :label-width="formLabelWidth">
<el-button
plain
type="primary"
icon="el-icon-download"
@click="updown()"
>下载</el-button
>
</el-form-item>
</el-form>
</el-dialog>
</template>
//js部分
closeImport() {
this.showCheckDialog = false
this.$emit('closeCheckDialog')
},
updownCertificateCrt(request) {
download(request)
.then((response) => {
if (response) {
const content = response.data
const blob = new Blob([content])
const fileName = response.fileName
if ('download' in document.createElement('a')) {
// 非IE下载
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
} else {
// IE10+下载
navigator.msSaveBlob(blob, fileName)
}
this.$message({
message: '下载成功',
type: 'success'
})
this.closeImport()
}
})
.catch((error) => {
this.$message(error, 'error')
})
}
5.开发中方便实用的小技巧
1.去重
//2.选择某个作为主键去重
function unique(arr) {
const res = new Map()
return arr.filter(
(arr) => !res.has(arr.id) && res.set(arr.id, 1)
)
}
},
//2.es6set去重
function unique (arr) {
return Array.from(new Set(arr))
}
//3.类似方法二的简化版
[...new Set(arr)]
//其实就是以下代码的简化版
function distinct(a, b) {
return Array.from(new Set([...a, ...b]))
}
2.一键复制功能实现
<el-button
:data-clipboard-text="this.info"
@click="onCopy"
>复制</el-button
>
//js部分
import Clipboard from 'clipboard' //引入复制组件
onCopy() {
var clipboard = new Clipboard('.tag-read')
clipboard.on(this.$message.success('复制成功!'), () => {
// 释放内存
clipboard.destroy()
})
}
网友评论