uniapp官方并没有提供app端的本地文件选取api,官方人员提供的思路是使用webview,内嵌html使用<input type="file"/>实现,我从插件市场找到的插件看源码学习了一下,基于这个思路,可以利用renderjs更方便的实现
template的写法有点不同
<template>
<button @click="openFilePicker">上传文件</button>
<view v-show="false" v-html="input" :props="mergeProps" :change:props="file.renderProps"></view>
</template>
// 这里自定义自己的样式
// v-html绑定则是需要渲染的input组件
// props绑定需要监听的属性
// change:props则是对props实行监听,类似于watch
// 只要props绑定的属性值变化,就会执行change所绑定的事件
普通script中定义属性,还有openFilePicker方法
<script>
export default {
data() {
return {
input:'',
inputId:'',
uploadUrl:''// 上传接口地址,
}
},
computed: {
mergeProps(){
return {
input: this.input,
inputId: this.inputId,
uploadUrl: this.uploadUrl
};
}
},
methods: {
openFilePicker(){
// 生成一个随机id,使其产生变化,从而触发change事件
this.inputId = Date.now().toString(36) + Math.random().toString(36);
// 渲染input组件
this.input = `<input type="file" id="file-upload-${this.inputId}" />`;
},
handleEmits(e){
// 上传成功后,数据会回调到这里,然后写自己的业务逻辑
...
}
}
}
</script>
在renderjs中实现上传方法
// 在script标签上声明renderjs,给一个module名字,上面change绑定的方法就需要这个名字获取
<script module="file" lang="renderjs">
export default {
methods: {
// change事件实际绑定的方法
renderProps(data){
// data则是props绑定的属性值,业务需要什么值,就从props传,renderjs无法直接获取
if(data.input) return
this.$nextTick(() => {
// renderjs可以直接使用web端的document属性获取dom
const dom = document.getElementById(`file-upload-${data.inputId}`);
// 注册input组件的change事件,即选取文件后的回调
dom.addEventListener('change', (e) => {
// e.target.files为选取的文件列表,返回一个数组
const files = e.target.files
// 使用xhr上传
const xhr = new XMLHttpRequest()
let formData = new FormData()
formData.append('file',files[0])
xhr.open('POST', data.uploadUrl)
xhr.onreadystatechange = ()=> {
if(xhr.readyState == 4){
const res = JSON.parse(xhr.responseText)
if(res.code===200){
// res.data为上传接口返回数据
// 使用$ownerInstance.callMethod调用上面script中的方法,类似$emit()
this.$ownerInstance.callMethod('handleEmits', res.data);
}
}
}
xhr.send(formData)
})
}
}
}
}
</script>