h5的话任何上传组件都是可以的,app的话需要调用起文件管理,这个就是很麻烦,找了半天没有,本人是结合uView组件库写的,废话不多说,直接上代码!!!!
子组件:
<template>
<view ref="parent" class="lsj-file" :style="[getStyles]" :class="loding ? 'active' : ''">
<view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
<slot>
<view class="defview" :style="[getStyles]">附件上传</view>
</slot>
</view>
</view>
</template>
<script>
// 查看文档:https://ext.dcloud.net.cn/plugin?id=5459
import {
LsjFile
} from './LsjFile.js'
export default {
name: 'Lsj-upload',
props: {
// 打印日志
debug: {
type: Boolean,
default: false
},
// 自动上传
instantly: {
type: Boolean,
default: false
},
// 上传接口参数设置
option: {
type: Object,
default: () => {}
},
// 文件大小上限
size: {
type: Number,
default: 10
},
// 文件选择个数上限,超出后不触发点击
count: {
type: Number,
default: 2
},
loding: {
type: Boolean,
default: false
},
// 是否允许多选文件
multiple: {
type: Boolean,
default: true
},
// 允许上传的文件格式(多个以逗号隔开)
formats: {
type: String,
default: ''
},
// input file选择限制
accept: {
type: String,
default: ''
},
// 微信选择文件类型
//all=从所有文件选择,
//video=只能选择视频文件,
//image=只能选择图片文件,
//file=可以选择除了图片和视频之外的其它的文件
wxFileType: {
type: String,
default: 'all'
},
// webviewID需唯一,不同窗口也不要同Id
childId: {
type: String,
default: 'lsjUpload'
},
// 文件选择触发面宽度
width: {
type: String,
default: '100%'
},
// 文件选择触发面高度
height: {
type: String,
default: '80rpx'
},
// top,left,bottom,right仅position=absolute时才需要传入
top: {
type: [String, Number],
default: ''
},
left: {
type: [String, Number],
default: ''
},
bottom: {
type: [String, Number],
default: ''
},
right: {
type: [String, Number],
default: ''
},
// nvue不支持跟随窗口滚动
position: {
type: String,
// #ifdef APP-NVUE
default: 'absolute',
// #endif
// #ifndef APP-NVUE
default: 'static',
// #endif
},
},
data() {
return {
}
},
watch: {
option(v) {
// #ifdef APP-PLUS
this.lsjFile && this.show();
// #endif
}
},
updated() {
// #ifdef APP-PLUS
if (this.isShow) {
this.lsjFile && this.show();
}
// #endif
},
computed: {
getStyles() {
let styles = {
width: this.width,
height: this.height
}
if (this.position == 'absolute') {
styles['top'] = this.top
styles['bottom'] = this.bottom
styles['left'] = this.left
styles['right'] = this.right
styles['position'] = 'fixed'
}
return styles
}
},
mounted() {
this._size = 0;
let WEBID = this.childId + new Date().getTime();
this.lsjFile = new LsjFile({
id: WEBID,
debug: this.debug,
width: this.width,
height: this.height,
option: this.option,
instantly: this.instantly,
// 限制条件
prohibited: {
// 大小
size: this.size,
// 允许上传的格式
formats: this.formats,
// 限制选择的格式
accept: this.accept,
count: this.count,
// 是否多选
multiple: this.multiple,
},
onchange: this.onchange,
onprogress: this.onprogress,
});
this.create();
// 需判断是否当前页显示
uni.$on('lsjShow', this.show);
},
beforeDestroy() {
uni.$off('lsjShow', this.show);
// #ifdef APP-PLUS
this.lsjFile.dom.close();
// #endif
},
methods: {
setFiles(array) {
if (array instanceof Map) {
for (let [key, item] of array) {
item['progress'] = 100;
item['type'] = 'success';
this.lsjFile.files.set(key, item);
}
} else if (Array.isArray(array)) {
array.forEach(item => {
if (item.name) {
item['progress'] = 100;
item['type'] = 'success';
this.lsjFile.files.set(item.name, item);
}
});
}
this.onchange(this.lsjFile.files);
},
setData() {
this.lsjFile && this.lsjFile.setData(...arguments);
},
getDomStyles(callback) {
// #ifndef APP-NVUE
let view = uni
.createSelectorQuery()
.in(this)
.select('.lsj-file')
view.fields({
size: true,
rect: true
},
({
height,
width,
top,
left,
right,
bottom
}) => {
uni.createSelectorQuery()
.selectViewport()
.scrollOffset(({
scrollTop
}) => {
return callback({
top: parseInt(top) + parseInt(scrollTop) + 'px',
left: parseInt(left) + 'px',
width: parseInt(width) + 'px',
height: parseInt(height) + 'px'
})
})
.exec()
}
).exec()
// #endif
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
dom.getComponentRect(this.$refs.lsj, ({
size: {
height,
width,
top,
left,
right,
bottom
}
}) => {
return callback({
top: parseInt(top) + 'px',
left: parseInt(left) + 'px',
width: parseInt(width) + 'px',
height: parseInt(height) + 'px',
right: parseInt(right) + 'px',
bottom: parseInt(bottom) + 'px'
})
})
// #endif
},
show() {
if (this._size && (this._size >= this.count)) {
return;
}
this.isShow = true;
// #ifdef APP-PLUS
this.lsjFile && this.getDomStyles(styles => {
this.lsjFile.dom.setStyle(styles)
});
// #endif
// #ifdef H5
this.lsjFile.dom.style.display = 'inline'
// #endif
},
hide() {
this.isShow = false;
// #ifdef APP-PLUS
this.lsjFile && this.lsjFile.dom.setStyle({
top: '-100px',
left: '0px',
width: '1px',
height: '100px',
});
// #endif
// #ifdef H5
this.lsjFile.dom.style.display = 'none'
// #endif
},
/**
* 手动提交上传
* @param {string}name 文件名称,不传则上传所有type等于waiting和fail的文件
*/
upload(name) {
this.lsjFile && this.lsjFile.upload(name);
},
/**
* @returns {Map} 已选择的文件Map集
*/
onchange(files) {
this.$emit('change', files);
this._size = files.size;
return files.size >= this.count ? this.hide() : this.show();
},
/**
* @returns {object} 当前上传中的对象
*/
onprogress(item, end = false) {
this.$emit('progress', item);
if (end) {
setTimeout(() => {
this.$emit('uploadEnd', item);
}, 0);
}
},
/**
* 移除组件内缓存的某条数据
* @param {string}name 文件名称,不指定默认清除所有文件
*/
clear(name) {
this.lsjFile.clear(name);
},
// 创建选择器
create() {
// 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去,并将此处path改成服务器上的地址
let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
let dom = this.lsjFile.create(path);
// #ifdef H5
this.$refs.lsj.$el.appendChild(dom);
// #endif
// #ifndef APP-PLUS
this.show();
// #endif
// #ifdef APP-PLUS
dom.setStyle({
position: this.position
});
dom.loadURL(path);
setTimeout(() => {
// #ifdef APP-NVUE
plus.webview.currentWebview().append(dom);
// #endif
// #ifndef APP-NVUE
this.$root.$scope.$getAppWebview().append(dom);
// #endif
this.show();
}, 300)
// #endif
},
// 点击选择附件
onClick() {
if (this._size >= this.count) {
this.toast(`只允许上传${this.count}个文件`);
return;
}
// #ifdef MP-WEIXIN
if (!this.isShow) {
return;
}
let count = this.count - this._size;
this.lsjFile.chooseMessageFile(this.wxFileType, count);
// #endif
},
toast(msg) {
uni.showToast({
title: msg,
icon: 'none'
});
}
}
}
</script>
<style scoped>
.active {
display: none !important;
}
.lsj-file {
display: inline-block;
}
.defview {
background-color: #007aff;
color: #fff;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.hFile {
position: relative;
overflow: hidden;
}
</style>
父组件:
<template>
<view class="content">
<view class="uploadBtn">
<!-- lsj-upload里面的:width="width" :height="height" :count="count"上传数量 -->
<lsj-upload :loding="loding" :count="count" childId="upload1" :option="option" :size="size" :formats="formats"
:debug="debug" :instantly="instantly" @uploadEnd="onuploadEnd" @change="onChange">
<!-- class="btn"里面的 :style="{width: width,height: height}" -->
<view class="btn"
style="width: 100%;height: 80rpx;background-color: #007AFF;color: #fff;border-radius: 10rpx;display: flex;align-items: center;justify-content: center;font-size: 28rpx;">
<text>上传文件</text>
</view>
</lsj-upload>
<view class="btn" v-if="loding"
style="width: 100%;height: 80rpx;background-color: #007AFF;color: #fff;border-radius: 10rpx;display: flex;align-items: center;justify-content: center;font-size: 28rpx;">
<u-loading-icon color="#fff" text="上传中..." textSize="24"></u-loading-icon>
</view>
</view>
<view v-if="files">
<view @click="clickItem(item)" style="margin-bottom: 20rpx;" class="list-box" v-for="(item, index) in files">
<text>{{item.name}}</text>
<view class="x-shape" @click.stop="deletItem(index)">
<u-icon name="close" color="#000" size="28"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
import storage from '@/common/storage.js'
export default {
data() {
return {
// 上传接口参数
option: {
// 上传服务器地址,需要替换为你的接口地址
url: 'http://192.168.8.106:8769/portal/system/file/v1/upload?tenantId=-1', // 该地址非真实路径,需替换为你项目自己的接口地址
type: 'post',
responseType: 'arrayBuffer',
// 上传附件的key
name: 'file',
// 根据你接口需求自定义请求头,默认不要写content-type,让浏览器自适配
header: {
Authorization: storage.getItemSync('token') ? storage.getItemSync('token') : ''
},
// 根据你接口需求自定义body参数
formData: {
// 'orderId': 1000
}
},
// 上传的时候有动画
loding: false,
// 选择文件后是否立即自动上传,true=选择后立即上传
instantly: true,
// 必传宽高且宽高应与slot宽高保持一致
// width: '180rpx',
// height: '180rpx',
// 限制允许上传的格式,空串=不限制,默认为空
formats: '',
// 文件上传大小限制
size: 30,
// 文件数量限制
count: 2,
// 是否多选
multiple: true,
// 文件回显列表
files: [],
// 是否打印日志
debug: false,
// 演示用
tabIndex: 0,
}
},
methods: {
// 某文件上传结束回调(成功失败都回调)
onuploadEnd(item) {
if (item.type === 'success') {
this.loding = false
// 更新当前窗口状态变化的文件
this.files.push(item)
} else {
this.loding = false
return uni.$u.toast('上传失败')
}
},
// 点击每一条上传
clickItem(item) {
let obj = JSON.parse(item.responseText)
obj.token = storage.getItemSync('token')
this.$api.preview(item.fileId).then(res => {
uni.navigateTo({
url: '/pages/FilePreview/FilePreview?url=' + JSON.stringify(obj)
})
})
},
// 删除每一条
deletItem(index) {
this.files.splice(index, 1) // 删除H5后端返回成功的文件数据
},
// 文件选择回调
onChange(files) {
this.loding = true
},
}
}
</script>
<style>
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 130rpx;
line-height: 130rpx;
text-align: center;
font-size: 32rpx;
background-color: #3F536E;
color: #fff;
z-index: 9999;
}
/* 按钮距离顶部高度 */
/* .content {
padding-top: 130rpx;
} */
.uploadBtn {
width: 100%;
height: 80rpx;
position: relative;
}
::v-deep .u-loading-icon__text {
color: #fff !important;
}
// 文件上传
.list-box {
width: 100vw;
height: 80rpx;
display: flex;
align-items: center;
padding: 0 30rpx;
box-sizing: border-box;
justify-content: space-between;
background-color: #eee;
overflow: hidden;
.x-shape {
width: 100rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>