最近一直比较忙碌,年后4个人花了一个半月的时间,撸了一套供应链系统,虽说需求比较清晰,但业务及流程较为繁琐。整体项目依旧采用前后端分离方式,前端使用vue+element的框架。在开发了大量功能后,才遇到了一些实际问题,对前端并不精通只能求助网络,不过仔细看看都不复杂,网上的讲解也都比较详细,所以这里也只简单将使用vue及axios过程中遇到的问题做个总结。
Axios跨域携带cookie
使用Axios默认是不带cookie的,倘若需要,则需要在添加withCredentials: true
属性。
import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL: process.env.BASE_API, // api 的 base_url
timeout: 20000, // 请求超时时间
withCredentials: true
})
vue组件中的双向绑定
Vue2中组件的props的数据流动改为了只能单向流动,即只能由组件外(调用组件方)通过组件的DOM属性attribute传递props给组件内,组件内只能被动接收组件外传递过来的数据,并且在组件内,不能修改由外层传来的props数据。组件内不能修改props的值,但可以通过事件通知外部。
假设我们定义的组件CustomWin如下:
<template>
<el-dialog title="选择窗口" :visible.sync="myV1">
<!-- 内容 -->
<el-button @click="myV1 = false">取 消</el-button>
</el-dialog>
</template>
<script>
export default {
props: {
selectVisible: {
type: Boolean,
default: false
}
},
data() {
return {
myV1:null
};
},
created() {
this.initDict();
},
watch: {
selectVisible(val) {
this.myV1 = val;
},
myV1(val){
this.$emit("on-selectVisible-change",val);
}
},
methods: {
}
};
</script>
这里的例子很简单,上面是我们定义的一个组件,那我们的外部可以是下面这样,通过onSelectVisibleChange
事件来获取内部传递出Value:
<template>
<div class="app-container">
<CustomWin :selectVisible="selectVisible"
@on-selectVisible-change="onSelectVisibleChange"></CustomWin>
</div>
</template>
<script>
import CustomWin from "@/components/CustomWin";
export default {
components: {CustomWin},
data() {
return {
selectVisible:false
}
},
methods: {
//...
onSelectVisibleChange(val){
this.selectVisible = val
}
}
}
</script>
ElementUI的Upload上传文件组件自定义上传
这个其实官网的Demo已经很清楚了,这里再啰嗦下。Upload组件我不使用默认的上传方法,而是使用自定义的上传,那么可以设置action为空,并设置:http-request
属性。
<el-upload class="upload-demo" ref="upload" action="" :http-request="uploadAttach" :on-preview="handlePreview" :on-remove="handleRemove" :before-remove="beforeRemove" multiple="" :show-file-list="false">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
Upload组件:http-request
属性覆盖默认的上传行为,可以自定义上传的实现。
uploadAttach(file){
let param = new FormData()
param.append('files',file.file)
uploadAttachment(param).then(response => {
//...
})
},
handleRemove(file) {
//...
},
handlePreview(file) {
//...
},
beforeRemove(file, fileList) {
//...
}
自定义的请求如下:
//上传附件
export function uploadAttachment(params) {
return request({
url: '/attachment/upload',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: params
})
}
使用Axios下载附件
首先我们在后端响应时设置Access-Control-Expose-Headers
允许浏览器访问自定义头。
//...
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(newFileName, "UTF-8"));
response.setHeader("Content-Length", String.valueOf(fileLength));
//将允许浏览器访问的头放入白名单
response.setHeader("Access-Control-Expose-Headers", "FileName");
//FileName 为自定义头
response.setHeader("FileName", URLEncoder.encode(newFileName, "UTF-8"));
//...
其次,在我们点击下载按钮的事件中当然要加上responseType: 'blob'
用于后续拦截响应时的检查判断。
export function downloadDetail(params) {
return request({
url: '/test/user/download',
method: 'get',
responseType: 'blob',
params
})
}
最后在前端,响应的拦截器中加入判断是否是附件,主要判断responseType
是否是blob
。
service.interceptors.response.use(
response => {
//判断响应类型是否是附件
if(response.config && response.config.responseType == 'blob') {
const blob = new Blob([response.data], { type: 'application/octet-stream;charset=utf-8' });
let filename = decodeURI(response.headers['filename']);
if ('download' in document.createElement('a')) {
const downloadElement = document.createElement('a');
let href = '';
if(window.URL){
href = window.URL.createObjectURL(blob);
}else{
href = window.webkitURL.createObjectURL(blob);
}
downloadElement.href = href;
downloadElement.download = filename;
document.body.appendChild(downloadElement);
downloadElement.click();
if(window.URL){
window.URL.revokeObjectURL(href);
}else{
window.webkitURL.revokeObjectURL(href);
}
document.body.removeChild(downloadElement);
} else {
navigator.msSaveBlob(blob, filename);
}
return;
}
},
error => {
//...
return Promise.reject(error)
}
注:这里遇到的一个问题是如何在前端获取后端响应过来的文件名?
1. 可以看到在上面的代码中我们增加了自定义头,并允许浏览器访问。
2. 拦截器中获取自定义头,并设置为创建的新元素的download属性。
3. 其中对于带有中文的文件名,这里我采用了简单的处理,使用urlEncode对其编码并在前端使用decodeURI解码。