作者 xuqiang

提交

<template>
</template>
<script>
</script>
<style>
</style>
<template>
<view class="simpleConfirmWrap">
<view class="simpleAlert">
<view class="alertTitle">
删除提示
</view>
<view class="alertInfo">
{{alertTxt}}
</view>
... ... @@ -39,17 +42,23 @@
right: 0;
bottom: 0;
z-index: 20;
background: $uni-bg-color-mask;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
.simpleAlert{
width: 622rpx;
background: $uni-bg-bai-color;
background: #fff;
border-radius: 32rpx;
font-size: $uni-font-size-32;
font-size: 32rpx;
.alertTitle{
text-align: center;
padding-top: 32rpx;
font-size: 36rpx;
font-weight: 600;
}
.alertInfo{
padding: 48rpx;
padding:20rpx 48rpx 48rpx;
text-align: center;
border-bottom: 1rpx solid #ebedf0;
}
... ... @@ -58,12 +67,12 @@
display: flex;
.btnItem{
flex: 1;
font-size: $uni-font-size-32;
font-size: 32rpx;
text-align: center;
line-height: 96rpx;
}
.btnItem.active{
color: $uni-bg-main-color;
color: #4bb377;
border-left: 1rpx solid #ebedf0;
}
}
... ...
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/license/reportUpload",
"style": {
"navigationBarTitleText":"报告上传"
}
},
{
"path": "pages/index/license/uploadInfo",
"style": {
"navigationBarTitleText":"上传资料"
}
},
{
"path": "pages/index/license/otherQuestion",
"style": {
"navigationBarTitleText":"其他问题"
... ...
... ... @@ -3,7 +3,7 @@
<view class="questionItem">
<view class="name">
<text>此处是名称</text>
<image src="../../../static/image/ic_tj@2x.png" mode=""></image>
<image @click="showRecordMore = true" src="../../../static/image/ic_tj@2x.png" mode=""></image>
</view>
<view class="content">
我是详情我是详情我是详情我是详情我是详情我是详情我是详情我是详情我是详情我是详我是详情我是详情我是详情我是详情我是详情我是详情我是详
... ... @@ -13,7 +13,7 @@
<image src="../../../static/image/ic_bj@2x.png" mode=""></image>
编辑
</view>
<view class="btnItem">
<view @click="showDel = true" class="btnItem">
<image src="../../../static/image/ic_sc@2x.png" mode=""></image>
删除
</view>
... ... @@ -22,7 +22,7 @@
<view class="questionItem">
<view class="name">
<text>此处是名称</text>
<image src="../../../static/image/ic_tj@2x.png" mode=""></image>
<image @click="showRecordMore = true" src="../../../static/image/ic_tj@2x.png" mode=""></image>
</view>
<view class="content">
我是详情我是详情我是详情我是详情我是详情我是详情我是详情我是详情我是详情我是详我是详情我是详情我是详情我是详情我是详情我是详情我是详
... ... @@ -32,7 +32,7 @@
<image src="../../../static/image/ic_bj@2x.png" mode=""></image>
编辑
</view>
<view class="btnItem">
<view @click="showDel = true" class="btnItem">
<image src="../../../static/image/ic_sc@2x.png" mode=""></image>
删除
</view>
... ... @@ -76,16 +76,68 @@
</view>
</view>
</view>
<!-- 记录更多区域 -->
<view class="recordMoreWrap" v-if="showRecordMore">
<view class="recordMore">
<view class="title">
记录更多
</view>
<view class="recordTypeList">
<view class="recordTypeItem" @click="getLocation">
<view class="typeImg">
<image src="../../../static/image/icon_loc.png" mode=""></image>
</view>
<view class="typeName">
位置
</view>
</view>
<view class="recordTypeItem" @click="getAlbum">
<view class="typeImg">
<image src="../../../static/image/icon_xiangce.png" mode=""></image>
</view>
<view class="typeName">
文件
</view>
</view>
<view class="recordTypeItem" @click="getCamera">
<view class="typeImg">
<image src="../../../static/image/icon_cream.png" mode=""></image>
</view>
<view class="typeName">
拍摄
</view>
</view>
<view class="recordTypeItem" @click="clickDescInp">
<view class="typeImg">
<image src="../../../static/image/icon_beizhu.png" mode=""></image>
</view>
<view class="typeName">
备注
</view>
</view>
</view>
<view class="bottomBtn" @click="showRecordMore = false">
取消
</view>
</view>
</view>
<simple-confirm v-if="showDel" :alertTxt="'删除后不可恢复,是否确认删除'" @close="showDel = false"></simple-confirm>
</view>
</template>
<script>
import simpleConfirm from '@/components/simpleConfirm.vue'
export default{
data(){
return{
showDel:false,
showRecordMore:false,
showHandleQuestion:false,
reason:''
}
},
components:{
simpleConfirm
}
}
</script>
... ... @@ -218,4 +270,14 @@
}
}
}
/* 记录更多区域 */
.recordMoreWrap{position: fixed;top: 0;bottom: 0;left: 0;right: 0;background: rgba(0,0,0,0.5);display: flex;align-items: flex-end;}
.recordMoreWrap .recordMore{background: #fff;width: 100%;border-radius: 30rpx 30rpx 0 0;}
.recordMore .title{padding-top: 40rpx;font-size: 28rpx;text-align: center;}
.recordMore .recordTypeList{display: flex;padding: 40rpx 0 32rpx 0;border-bottom: 16rpx solid #f7f8fa;}
.recordTypeList .recordTypeItem{width: 25%;}
.recordTypeItem .typeImg{display: flex;justify-content: center;}
.recordTypeItem .typeName{padding-top: 16rpx;text-align: center;color: #969799;font-size: 24rpx;}
.typeImg image{width: 96rpx;height: 96rpx;}
.recordMore .bottomBtn{height: 96rpx;text-align: center;line-height: 96rpx;font-size: 32rpx;color: #646566;}
</style>
... ...
<template>
<view style="padding-top: 1rpx;">
<view class="chooseFile">
<image src="../../../static/image/ic_scwj@2x.png" mode=""></image>
选择上传文件
</view>
<view style="height: 130rpx;"></view>
<view class="bottomBtnWrap">
<view class="bottomBtn">
保存
</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.chooseFile{
margin-top: 32rpx;
padding: 0 32rpx;
display: flex;
align-items: center;
font-size: 32rpx;
image{width: 120rpx;height: 120rpx;margin-right: 10rpx;}
}
.bottomBtnWrap{
height: 132rpx;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
position: fixed;
bottom: 0;
width: 100%;
.bottomBtn{
width: 624rpx;
height: 100rpx;
background: #4bb377;
border-radius: 28rpx;
color: #fff;
text-align: center;
line-height: 100rpx;
font-size: 34rpx;
}
}
</style>
... ...
<template>
<view>
<l-file ref="lFile" :logo="logo" @up-success="onSuccess"></l-file>
<view class="infoList">
<view class="infoItem">
<text>1.现场检测表</text>
<image @click="uploadFile" src="../../../static/image/icon_add.png" mode=""></image>
</view>
<view class="infoItem">
<text>2.影像检查</text>
<image @click="uploadFile" src="../../../static/image/icon_add.png" mode=""></image>
</view>
<view class="infoItem">
<text>3.监测数据</text>
<image @click="uploadFile" src="../../../static/image/icon_add.png" mode=""></image>
</view>
<view class="imgList">
<view class="imgItem">
<image class="img" src="../../../static/image/del/Ellipse-1@2x.png" mode=""></image>
<image class="clearBtn" src="../../../static/image/icon_clear2.png" mode=""></image>
</view>
</view>
</view>
<view style="height: 130rpx;"></view>
<view class="bottomBtnWrap">
<view class="bottomBtn">
保存
</view>
</view>
</view>
</template>
<script>
export default{
data(){
return{
logo: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040',
}
},
methods:{
uploadFile(){
this.$refs.lFile.upload({
// #ifdef APP-PLUS
// nvue页面使用时请查阅nvue获取当前webview的api,当前示例为vue窗口
currentWebview: this.$mp.page.$getAppWebview(),
// #endif
url: '/dropbox/document/upload', //替换为你的
name: 'file',
header: { //根据你接口需求自定义
'Authorization': '',
'uid': '3871',
'client': 'app',
'accountid': 'IMED5274'
}
// body参数直接写key,value,如:
// key1: 'value1',
// key2: 'value2',
});
},
onSuccess(res) {
console.log('上传成功回调',JSON.stringify(res));
uni.showToast({
title: JSON.stringify(res),
icon: 'none'
})
}
}
}
</script>
<style lang="scss" scoped>
.infoList{
padding: 0 32rpx;
.infoItem{
height: 124rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
border-bottom: 2rpx solid #f2f3f5;
image{width: 44rpx;height: 44rpx;}
}
}
.bottomBtnWrap{
height: 132rpx;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
position: fixed;
bottom: 0;
width: 100%;
.bottomBtn{
width: 624rpx;
height: 100rpx;
background: #4bb377;
border-radius: 28rpx;
color: #fff;
text-align: center;
line-height: 100rpx;
font-size: 34rpx;
}
}
.imgList{display: flex;flex-wrap: wrap;margin-top: 10rpx;}
.imgList .imgItem{position: relative;width: 160rpx;height: 160rpx;margin: 10rpx 10rpx 0 0;}
.imgItem .img{width: 160rpx;height: 160rpx;}
.imgItem .clearBtn{width: 28rpx;height: 28rpx;position: absolute;right: -10rpx;top: -10rpx;}
</style>
... ...
## 1.2.4(2021-04-14)
1.修复下载进度条未显示问题;
2.增加取消下载
## 1.2.3(2021-04-12)
修复H5端第二次打开文件管理器时未清除上一次加载dom的问题
## 1.2.2(2021-04-02)
1.已不再维护非uniModules版本,需要的伙伴请导入uniModules版本插件。
2.本次升级为修复OSS直传时签名无效问题,普通接口不受影响
## 1.2.1(2021-04-01)
已支持uniModules版本;不清楚怎么使用可以导入示例项目
## 1.2.0(2021-04-01)
1.修复h5端上传附件为空文件的BUG
2.统一APP端与H5端返回参数
## 1.1.6(2021-03-31)
如uni_modules版本无法导入插件,可导入非uni_modules版或完整示例
## 1.1.5(2021-03-31)
如uni_modules版本无法导入插件,可导入非uni_modules版或完整示例
## 1.1.4(2021-03-31)
附件选择上传已兼容App、微信小程序、H5
... ...
<template>
<div class="content">
<div @click="onClose" class="fixed mask"></div>
<div v-if="showTip" align="center" class="fixed tis">
<div class="tis-content">
<div>
<img :src="logo" >
</div>
<div class="tis-progress">
努力上传中
<text v-if="progress<100">..{{progress}}%</text>
</div>
<div class="cancel">
<button @click="onAbort" type="button" class="cancel-btn">取消上传</button>
</div>
</div>
</div>
<div class="fixed file-content">
<view ref="input" class="btn">
<button type="button" class="btn-bg">打开文件管理器</button>
</view>
</div>
</div>
</template>
<script>
export default {
props: {
logo: {
type: String,
default: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040'
},
progress: {
type: [Number,String],
default: 0
},
showTip: {
type: Boolean,
default: false
}
},
data() {
return {
hFile: '',
}
},
mounted() {
this.hFile = document.createElement('input')
this.hFile.type = 'file';
this.hFile.value = '';
this.hFile.style.position = 'absolute';
this.hFile.style.zIndex = 93;
this.hFile.style.left = 0;
this.hFile.style.right = 0;
this.hFile.style.top = 0;
this.hFile.style.bottom = 0;
this.hFile.style.height = '60px';
this.hFile.style.width = '100%';
this.hFile.style.opacity = 0;
this.$refs.input.$el.appendChild(this.hFile);
},
methods: {
onAbort() {
this.$emit('abort',{});
},
onClose() {
this.$emit('close',{});
}
}
}
</script>
<style scoped>
.content {background: transparent;}
.fixed {position: fixed;bottom: 0;left: 0;right: 0;width: 100%;}
.content .mask {top: 0;background: rgba(0,0,0,.4);z-index: 90;}
.content .file-content {z-index: 91;height: 60px;background: #fff;text-align: center;}
.btn {position: relative;}
.btn .file {position: absolute;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;height: 60px;width: 100%;opacity: 0;}
.btn-bg {margin-top: 10px;background: #4bb377;color: #fff;width: 80%;height: 40px;line-height: 40px;border: 0;border-radius: 5px;}
.tis {top: 0;z-index: 95;display: flex;justify-content: center;align-items: center;}
.tis .tis-content {background: #fff;width: 60%;border-radius: 10px;padding: 20px 0;}
.tis .tis-content img {width: 50px;height: 50px;}
.tis-progress {margin: 10px 0;color: #999;}
.cancel-btn {margin-top: 30px;height: 30px;width: 60%;line-height: 30px;font-size: 14px;padding: 0 2em;background: #e3e3e3;color: #898989;border: 0!important;border-radius: 5px;}
.cancel-btn:after {height: 0!important;border: 0!important;}
</style>
... ...
<template>
<view>
<view class='t-toptips' :style="{top: top,background: cubgColor}" :class="[show?'t-top-show':'']">
<view v-if="loading" class="flex flex-sub">
<view class="flex flex-sub">
<view class="cu-progress flex-sub round striped active sm">
<view :style="{ background: color,width: value + '%'}"></view>
</view>
<text class="margin-left">{{value}}%</text>
</view>
<view @click="downOnAbort" v-if="value<100" class="close">取消</view>
</view>
<block v-else>{{msg}}</block>
</view>
<!-- #ifdef H5 -->
<h5-file
v-if="showH5"
ref="h5File"
:logo="logo"
:showTip="showTip"
:progress="value"
@abort="onAbort"
@close="onCloseH5"
></h5-file>
<!-- #endif -->
</view>
</template>
<script>
import h5File from './h5-file.vue';
export default {
components: {h5File},
props: {
logo: {
type: String,
default: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040'
},
top: {
type: String,
default: 'auto'
},
bgColor: {
type: String,
default: 'rgba(49, 126, 243, 0.5)',
},
color: {
type: String,
default: '#55aa00',
}
},
data() {
this.uploadTask = null;
this.downloadTask = null;
return {
cubgColor: '',
loading: false,
value: 5,
show: false,
msg: '执行完毕~',
showH5: false,
showTip: false,
}
},
methods: {
toast(title = '',{ duration = 2000, icon = 'none'} = {}) {
uni.showToast({title,duration,icon});
},
getRequest(url) {
let theRequest = new Object();
let index = url.indexOf("?");
if (index != -1) {
let str = url.substring(index+1);
let strs = str.split("&");
for(let i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return theRequest;
},
/*
上传说明:
currentWebview: 当前窗口webview对象
url:上传接口地址
name:上传文件key值
header: 上传接口请求头
...:body内其他参数
*/
appChooseFile({currentWebview,url,name = 'file',header,...formData} = {}) {
// #ifdef APP-PLUS
let wvPath = '/uni_modules/l-file/hybrid/html/index.html';
let wv = plus.webview.create("",wvPath,{
'uni-app': 'none',
top: 0,
height: '100%',
background: 'transparent'
},{
url,
header,
formData,
key: name,
logo: this.logo
});
wv.loadURL(wvPath);
currentWebview.append(wv);
wv.overrideUrlLoading({mode:'reject'},(e)=>{
let {fileName,str} = this.getRequest(e.url);
fileName = unescape(fileName);
str = unescape(str);
return this.handleBack(fileName,str);
});
// #endif
},
wxChooseFile(param) {
wx.chooseMessageFile({
count: 1,
type: 'file',
success: ({tempFiles}) => {
this.setdefUI();
this.handleWXUpload(param,tempFiles[0]);
},
fail: () => this.errorHandler('文件选择失败',this.upErr)
})
},
h5ChooseFile(param) {
this.showH5 = true;
this.value = 0;
this.$nextTick(()=>{
this.$refs.h5File.hFile.onchange = (event) => {
this.handleH5Upload(param, event.target.files[0]);
}
})
},
handleH5Upload({url,name = 'file',header,...data} = {},tempFile) {
let fileName = tempFile.name;
let formData = new FormData();
for (let keys in data) {formData.append(keys, data[keys]);}
formData.append(name, tempFile);
this.uploadTask = new XMLHttpRequest();
this.uploadTask.open("POST", url, true);
for (let keys in header) {this.uploadTask.setRequestHeader(keys, header[keys]);}
this.uploadTask.ontimeout = () => {
setTimeout(()=>{
this.showTip = false;
return this.errorHandler('请求超时',this.upErr);
},1000);
};
this.uploadTask.upload.addEventListener("progress",(event) => {
if(event.lengthComputable){
this.value = Math.ceil(event.loaded * 100 / event.total);
if (this.value > 100) {this.value=100;}
this.$forceUpdate();
}
}, false);
this.uploadTask.onreadystatechange = (ev) => {
if(this.uploadTask.readyState == 4) {
console.log('status:'+this.uploadTask.status);
if (this.uploadTask.status == 200) {
return this.handleBack(fileName,this.uploadTask.responseText);
}
else {
this.showTip = false;
if (this.uploadTask.status == 0) {
console.log('请检查请求头Content-Type与服务端是否匹配,并确认服务端已正确开启跨域');
}
return this.errorHandler('文件上传失败',this.upErr);
}
}
};
this.showTip = true;
this.uploadTask.send(formData);
},
handleWXUpload({url,name = 'file',header,...formData} = {},tempFile) {
let opt = {url,name,formData,header,filePath:tempFile.path};
let fileName = tempFile.name;
opt['fail'] = (e) => {
this.showTip = false;
return this.errorHandler('文件上传失败',this.upErr)
};
opt['success'] = (res) => {
if (res.statusCode==200) {
let data = JSON.parse(res.data);
//可自行添加后台返回状态验证
return this.onCommit(this.$emit('up-success',{fileName,data}));
}
return this.errorHandler('文件上传失败',this.upErr);
};
this.showTip = true;
this.uploadTask = uni.uploadFile(opt);
this.uploadTask&&this.uploadTask.onProgressUpdate(({progress = 0}) => {
if (progress <= 100) {
this.value = progress;
this.$forceUpdate();
}
});
},
onCloseH5() {
this.showH5 = false;
},
onAbort() {
this.uploadTask&&this.uploadTask.abort();
this.showTip = false;
},
downOnAbort() {
this.downloadTask&&this.downloadTask.abort();
this.onCommit(false,'已取消');
},
// app+h5返回内容,此处按实际项目修改
handleBack(fileName,str = '{}') {
console.log('可根据需求自行修改emit内容,服务端返回:'+ str);
try{
str = JSON.parse(str);
}catch(e){
str = {id:str};
}
return this.onCommit(this.$emit('up-success',{statusCode: 200,fileName,...str}));
},
/*
上传
*/
upload(param = {}) {
if (!param.url) {this.toast('上传地址不正确');return;}
if (this.loading) {this.toast('还有个文件玩命处理中,请稍候..');return;}
// #ifdef APP-PLUS
return this.appChooseFile(param);
// #endif
// #ifdef MP-WEIXIN
return this.wxChooseFile(param);
// #endif
// #ifdef H5
this.h5ChooseFile(param);
// #endif
},
/*
打开文件
*/
open(filePath) {
let system = uni.getSystemInfoSync().platform;
if(system == 'ios'){filePath = encodeURI(filePath);}
uni.openDocument({
filePath,
success: (res) => {console.log('打开文档成功');}
});
},
/*
APP自定义保存
*/
plusSaveFile({url,customName='',opt}) {
return new Promise((resolve,reject)=>{
// 可自行修改参数
// 参数api: http://www.html5plus.org/doc/zh_cn/downloader.html#plus.downloader.DownloadOptions
let downloadOptions = {
method: "GET",
timeout: 120,
retryInterval: 10
};
customName&&(downloadOptions['filename']=`_downloads/files/${customName}`);
downloadOptions = {...downloadOptions,...opt};
this.downloadTask = plus.downloader.createDownload(url, downloadOptions,(d, status)=>{
// 下载完成
if(status == 200){
let tempFilePath = d.filename;
this.value = 100;
this.onCommit(resolve(tempFilePath))
} else {
this.errorHandler('下载失败',reject)
}
this.downloadTask = null;
});
this.downloadTask.addEventListener('statechanged',({downloadedSize=0,state=0,totalSize=0}={})=>{
if (state===3) {
let total = totalSize>0?totalSize:fileSize;
let progressVal = Math.ceil(downloadedSize / total* 100);
this.value = progressVal>100?100:progressVal;
this.$forceUpdate()
}
},false);
this.downloadTask.start();
});
},
/*
下载
type: temporary=返回临时地址,save=长期保存到本地
*/
download({url,type = 'temporary',customName = '',...opt}) {
if (this.loading) {
this.toast('还有个文件玩命处理中,请稍候..');
return;
}
this.setdefUI();
// #ifdef APP-PLUS
if (type == 'save') {
return this.plusSaveFile({url,customName,opt});
}
// #endif
return new Promise((resolve,reject)=>{
url = encodeURI(url);
this.downloadTask = uni.downloadFile({
url,
success: ({statusCode,tempFilePath}) => {
if (statusCode === 200) {
// #ifndef H5
if (type == 'save') {
uni.saveFile({
tempFilePath,
success:({savedFilePath}) => this.onCommit(resolve(savedFilePath)),
fail: () => this.errorHandler('下载失败',reject)
});
}
else {
this.onCommit(resolve(tempFilePath))
}
// #endif
// #ifdef H5
this.onCommit(resolve(tempFilePath))
// #endif
}
},
fail: () => this.errorHandler('下载失败',reject)
});
this.downloadTask.onProgressUpdate(({progress = 0}) => {
if (progress <= 100) {
this.value = progress;
this.$forceUpdate();
}
});
})
},
onCommit(resolve,msg = '执行完毕~') {
this.msg = msg;
this.loading = false;
this.showTip = false;
this.cubgColor = 'rgba(57, 181, 74, 0.5)';
this.uploadTask = null;
this.downloadTask = null;
setTimeout(()=>{this.show = false;this.showH5 = false;},1500);
return resolve;
},
setdefUI() {
this.cubgColor = this.bgColor;
this.value = 0;
this.loading = true;
this.show = true;
},
upErr(errText) {
this.$emit('up-error',errText);
},
errorHandler(errText,reject) {
this.msg = errText;
this.loading = false;
this.cubgColor = 'rgba(229, 77, 66, 0.5)';
this.uploadTask = null;
this.downloadTask = null;
setTimeout(()=>{this.show = false;},1500);
return reject(errText);
}
}
}
</script>
<style scoped>
.t-toptips {
width: 100%;
padding: 18upx 30upx;
box-sizing: border-box;
position: fixed;
z-index: 90;
color: #fff;
font-size: 30upx;
left: 0;
display: flex;
align-items: center;
justify-content: center;
word-break: break-all;
opacity: 0;
transform: translateZ(0) translateY(-100%);
transition: all 0.3s ease-in-out;
}
.close {
width: 3em;
text-align: right;
}
.t-top-show {
transform: translateZ(0) translateY(0);
opacity: 1;
}
.flex {
display: flex;
align-items: center;
}
.flex-sub {
flex: 1;
}
.round {
border-radius: 5000upx;
}
/* ==================
进度条
==================== */
.cu-progress {
overflow: hidden;
height: 28upx;
background-color: #ebeef5;
display: inline-flex;
align-items: center;
width: 100%;
}
.cu-progress+view,
.cu-progress+text {
line-height: 1;
}
.cu-progress.xs {
height: 10upx;
}
.cu-progress.sm {
height: 20upx;
}
.cu-progress view {
width: 0;
height: 100%;
align-items: center;
display: flex;
justify-items: flex-end;
justify-content: space-around;
font-size: 20upx;
color: #ffffff;
transition: width 0.6s ease;
}
.cu-progress text {
align-items: center;
display: flex;
font-size: 20upx;
color: #333333;
text-indent: 10upx;
}
.cu-progress.text-progress {
padding-right: 60upx;
}
.cu-progress.striped view {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 72upx 72upx;
}
.cu-progress.active view {
animation: progress-stripes 2s linear infinite;
}
@keyframes progress-stripes {
from {
background-position: 72upx 0;
}
to {
background-position: 0 0;
}
}
</style>
... ...
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title class="title">[文件管理器]</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<style type="text/css">
.content {background: transparent;}
.fixed {position: fixed;bottom: 0;left: 0;right: 0;width: 100%;}
.content .mask {top: 0;background: rgba(0,0,0,.4);z-index: 90;}
.content .file-content {z-index: 91;height: 60px;background: #fff;text-align: center;}
.btn {position: relative;}
.btn .file {position: absolute;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;height: 60px;width: 100%;opacity: 0;}
.btn-bg {margin-top: 10px;background: #0066CC;color: #fff;width: 80%;height: 40px;border: 0;border-radius: 5px;}
.tis {top: 0;z-index: 95;display: none;justify-content: center;align-items: center;}
.tis .tis-content {background: #fff;width: 60%;border-radius: 10px;padding: 20px 0;}
.tis .tis-content img {width: 50px;height: 50px;}
.tis-progress {margin: 10px 0;color: #999;}
.cancel-btn {margin-top: 30px;height: 30px;line-height: 1;padding: 0 2em;background: #e3e3e3;color: #898989;border: 0;border-radius: 5px;}
</style>
</head>
<body>
<div class="content">
<div class="fixed mask"></div>
<div align="center" class="fixed tis">
<div class="tis-content">
<div class="logo"></div>
<div class="tis-progress">
努力上传中..
</div>
<div class="cancel">
<button type="button" class="cancel-btn">取消上传</button>
</div>
</div>
</div>
<div class="fixed file-content">
<div class="btn">
<button type="button" class="btn-bg">打开文件管理器</button>
<input class="file" type="file" />
</div>
</div>
</div>
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<script src="js/h5-uploader.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
... ...
let mask = document.querySelector(".mask");
let fileDom = document.querySelector(".file");
let tis = document.querySelector(".tis");
let logoDom = document.querySelector(".logo");
let progress = document.querySelector(".tis-progress");
let cancel = document.querySelector(".cancel-btn");
let createUpload = (file, url, key='file', header = {},data = {}) => {
console.log(`
上传地址:${url}\n
上传附件:${file.name} 附件大小:${file.size}\n
请求头:${JSON.stringify(header)}\n
业务参数:${JSON.stringify(data)}
`);
if (!url) {return;}
tis.style.display = 'flex';
let formData = new FormData();
for (let keys in data) {formData.append(keys, data[keys]);}
formData.append(key, file);
let xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
for (let keys in header) {
xhr.setRequestHeader(keys, header[keys]);
}
xhr.upload.addEventListener("progress", function(event) {
if(event.lengthComputable){
let percent = Math.ceil(event.loaded * 100 / event.total) + "%";
progress.innerText = `努力上传中..${percent}`;
}
}, false);
xhr.ontimeout = function(){
// xhr请求超时事件处理
progress.innerText = '请求超时';
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
};
xhr.onreadystatechange = (ev) => {
if(xhr.readyState == 4) {
console.log('status:'+xhr.status);
if (xhr.status == 200) {
progress.innerText = '上传成功';
console.log('服务端返回数据:'+xhr.responseText);
location.href = `callback?fileName=${escape(file.name)}&str=${escape(xhr.responseText)}`;
}
else {
progress.innerText = '上传失败了';
if (xhr.status == 0) {
console.log('请检查请求头Content-Type与服务端是否匹配,并确认服务端已正确开启跨域');
}
}
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
}
};
xhr.send(formData);
cancel.addEventListener("click", ()=>{
xhr.abort();
plus.webview.currentWebview().close();
});
}
mask.addEventListener("click", () => {
plus.webview.currentWebview().close();
});
document.addEventListener('UniAppJSBridgeReady', () => {
let {url,key,header,formData,logo} = plus.webview.currentWebview();
if (logo) {
let img = document.createElement('img');
img.src = logo;
logoDom.appendChild(img);
}
fileDom.value = '';
fileDom.addEventListener('change', (event) => {
let file = fileDom.files[0];
// 默认限制文件小于10M,可自行修改
if(file.size > (1024*1024 * 10)) {
plus.nativeUI.toast('单个文件请勿超过10M,请重新上传');
return;
}
createUpload(file, url, key,header,formData);
}, false);
});
\ No newline at end of file
... ...
{
"id": "l-file",
"displayName": "附件选择上传下载APP-H5-小程序",
"version": "1.2.4",
"description": "附件选择上传下载APP-H5-小程序",
"keywords": [
"附件",
"上传",
"下载",
"预览"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.1"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
\ No newline at end of file
... ...
# l-file
### 完整示例可导入示例项目运行
不要期待更新,我很懒~
希望能帮到你!
---
## 使用说明
| 属性| 是否必填| 值类型| 返回值| 说明|
| --------- | -------- | -----: | --: | --: |
| @up-success|否 | CallBack|{name,data} | 上传成功回调|
## ref调用
|作用 | 方法| 传入参数类型| 说明|
|---- | --------- | -------- | --: |
|下载| download|<URL,type,fileName>| type='save'为保存到本地,默认为获取临时路径|
|预览| open|URL| 预览文件|
|上传| upload|Object| 上传文件,参数见下方函数说明|
### vue:
``` javascript
// 以下代码写于根目录下第一个view顶部或跟在自定义导航栏后面
<l-file
ref="lFile"
@up-success="upSuccess"
></l-file>
```
---
* 函数说明
``` javascript
/* 预览临时文件 */
this.$refs.lFile.download({url})
.then(path=>{
this.$refs.lFile.open(path);
});
/**
* 保存到本地
* type 非save为临时下载
* customName 仅type=save生效 附件自定义名称需带后缀,自定义目录需以/结尾
* DownloadOptions 仅type=save生效 可选参数(http://www.html5plus.org/doc/zh_cn/downloader.html#plus.downloader.DownloadOptions)
* 默认下载到_downloads/files/ 可通过DownloadOptions自定义
*/
this.$refs.lFile.download({
url, //必填,附件网络地址
type:'save', //选填,非save为临时下载
customName:'自定义文件名需要带后缀.jpg',
//...DownloadOptions直接写key:value
// 例如:
method: 'GET'
})
.then(path=>{
this.localPath = path;
});
/*
选择文件并上传
currentWebview=当前窗口,仅app端需要传,且必传
url=上传服务器地址,必填
name=上传文件的key(选填,默认为file)
header=请求头(选填)
*/
this.$refs.lFile.upload({
// #ifdef APP-PLUS
currentWebview: this.$mp.page.$getAppWebview(),
// #endif
url: '', //你的上传附件接口地址
name: 'file',
header: {'Authorization':'token'},
// body参数直接写key,value,如:
// key1: 'value1',
// key2: 'value2',
});
```
## 温馨提示
* 文件上传
0. 如不清楚怎么调用可导入完整示例可查看使用方式
1. APP端请优先联调Android,上传成功后再运行iOS端,如iOS返回status=0则需要后端开启允许跨域;
2. header的Content-Type类型需要与服务端要求一致,否则收不到附件(服务端若没有明文规定则可不写,使用默认匹配)
3. java不清楚怎么配置跨域可以找我;若是php,跨域需要注意不能配置为*星号,需为file单独设置跨域,具体百度~
4. 欢迎加入QQ讨论群:701468256
5. 欢迎加入QQ讨论群:701468256
6. 欢迎加入QQ讨论群:701468256
\ No newline at end of file
... ...