You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

703 lines
19 KiB

<template>
<view class="file-outerBox" @touchmove.prevent v-if="isOpen">
<view style="width: 100%; padding-top: --status-bar-height;"></view>
<view class="file-titel" :style="{backgroundColor: navBgColor}">
<view class="file-nav-leftBox" @click="backAddress">
<image src="./img/back.png" mode="widthFix" class="file-back-img"></image>
</view>
<text :style="titelStyle">
{{titel}}
</text>
<view class="file-nav-rightBox"></view>
</view>
<view class="file-address">
<view class="root-box" @click="backRoot">
内部存储
<image src="./img/rightjt.png" mode="widthFix" class="to-img-box"></image>
</view>
<scroll-view :scroll-x="true" class="address-scroll">
<view class="address-box" v-for="(item,index) in addressBar" @click="backFolder(item,index)">
{{item.name}}
<image src="./img/rightjt.png" mode="widthFix" class="to-img-box"></image>
</view>
</scroll-view>
</view>
<view class="">
<scroll-view :scroll-y="true" :style="{height: 'calc(100vh - ' + (barHeight*2 + 280) + 'rpx)'}">
<view class="select-tips" v-if="inaccessible">
<view style="line-height: 40rpx;">
无法访问的文件夹
</view>
<view style="line-height: 40rpx;">
建议前往安卓存储访问框架查看文件
</view>
</view>
<view class="folder-box" v-for="item in folderArr" @click="toFolder(item)">
<view class="folder-name-box">
<image src="./img/files.png" mode="widthFix" class="folder-img"></image>
<view class="name-box">{{item.name}}</view>
</view>
<view>
<image src="./img/comefile.png" mode="widthFix" class="toFolder-img"></image>
</view>
</view>
<view class="file-box" v-for="(item,index) in fileArr" @click="selectFile(index)">
<view class="file-name-box">
<image src="./img/file.png" mode="widthFix" class="file-img"></image>
<!-- <image :src="txtImg" mode="widthFix" class="file-img" v-if="item.type == 'txt'"></image>
<image :src="docImg" mode="widthFix" class="file-img" v-if="item.type == 'doc'"></image>
<image :src="pdfImg" mode="widthFix" class="file-img" v-if="item.type == 'pdf'"></image> -->
<view class="name-box">
{{item.name}}
</view>
</view>
<view>
<image src="./img/check.png" mode="widthFix" class="select-img" v-if="item.select"></image>
<image src="./img/checkout.png" mode="widthFix" class="select-img" v-else></image>
</view>
</view>
</scroll-view>
<u-toast ref="uToast"></u-toast>
</view>
<view class="">
<button type="default" class="select-foot-btn" :style="btnStyle" @click="uploadBtn">选择</button>
</view>
</view>
</template>
<script>
/*
*
* {property} 使用 v-model 绑定一个变量来控制组件的开启与关闭
* {property} navBgColor [String] 顶部标题栏背景色
* {property} folderImg [String] 文件夹的图片
* {property} backImg [String] 返回上一级图片
* {property} directionImg [String] 右指向箭头
* {property} enterImg [String] 进入文件夹箭头
* {property} fileImg [String] 未知文件通用图标,当前仅可识别 pdf、doc/docx、txt
* {property} txtImg [String] txt文件图标
* {property} docImg [String] doc/docx文件图标
* {property} pdfImg [String] pdf文件图标
* {property} selectedImg [String] 选中状态下的按钮图标
* {property} unselectedImg [String] 未选中状态下的按钮图标
* {property} titel [String] 标题文字,默认 '选择文件'
* {property} titelSize [String,Number] 标题文字大小,默认 36rpx
* {property} titelWeight [String,Number] 标题文字粗细,默认 600
* {property} titelColor [String] 标题文字颜色,默认 #373737
* {property} btnText [String] 底部按钮文字, 默认 '上传'
* {property} btnSize [String,Number] 底部按钮文字大小, 默认 36rpx
* {property} btnHeight [String,Number] 底部按钮高度, 默认 92rpx
* {property} btnBgColor [String] 底部按钮颜色, 默认 #6521e2
* {property} btnTextColor [String] 底部按钮文字颜色, 默认 #fff
* {property} filterArr [Array] 筛选文件类型,示例:['doc','PDF'],不区分大小写
*
* {event} confirm [Function] 点击上传按钮触发的回调事件,会返回选中文件的地址 event = [{name: name, url: path, sizeMB: sizeMb}]
* name: 文件名 url: 文件地址 sizeMB: 文件大小,单位MB
*/
import { read } from 'fs';
export default {
name: "nk-select-file",
props: {
value: {
type: Boolean,
default: false
},
docImg: {
type: String,
default: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-5c697db4-d920-4af3-90a0-19211379b881/501660e5-8386-473f-bfcb-34c0b2b4a2f8.png'
},
pdfImg: {
type: String,
default: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-5c697db4-d920-4af3-90a0-19211379b881/b1056050-6d27-454e-b2b7-51e3832cfe5e.png'
},
selectedImg: {
type: String,
default: './img/check.png'
},
unselectedImg: {
type: String,
default: './img/checkout.png'
},
titel: {
type: String,
default: '选择文件'
},
titelSize: {
type: [String, Number],
default: 36
},
titelWeight: {
type: [String, Number],
default: 600
},
titelColor: {
type: String,
default: '#373737'
},
btnText: {
type: String,
default: '上传'
},
btnSize: {
type: [String, Number],
default: 36
},
btnHeight: {
type: [String, Number],
default: 92
},
btnBgColor: {
type: String,
default: '#6521e2'
},
btnTextColor: {
type: String,
default: '#fff'
},
navBgColor: {
type: String,
default: '#fff'
},
filterArr: {
type: Array,
default () {
return []
}
}
},
data() {
return {
barHeight: '', // 状态栏高度
rootAddress: {}, // 根目录
addressBar: [], // 地址栏记录栈
folderArr: [], // 文件夹
fileArr: [], // 文件
selectArr: [], // 选中文件集合
isExit: true, // 退出
isOpen: false,
inaccessible: false, // 无法访问提示
titelStyle: {
fontSize: this.titelSize + 'rpx',
fontWeight: this.titelWeight,
color: this.titelColor
},
btnStyle: {
height: this.btnHeight + 'rpx',
backgroundColor: this.btnBgColor,
color: this.btnTextColor,
fontSize: this.btnSize + 'rpx'
},
filterReg: '',
};
},
watch: {
value(val) {
if (val) {
this.open();
} else {
this.close();
}
}
},
mounted() {
if (this.filterArr.length > 0) {
let str = this.filterArr.join("|");
this.filterReg = new RegExp(str, 'i');
}
},
methods: {
// 打开组件
open() {
this.isOpen = true;
this.getBarHeight();
this.getRootDirectory();
},
// 关闭组件
close() {
this.isOpen = false;
this.rootAddress = {}; // 根目录
this.addressBar = []; // 地址栏记录栈
this.folderArr = [];
this.fileArr = [];
this.selectArr = []; // 选中文件集合
this.$emit('input', false);
// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
this.$nextTick(() => {
this.$emit('change', false);
})
},
// 获取状态栏高度
getBarHeight() {
var self = this;
uni.getSystemInfo({
success(res) {
self.barHeight = res.statusBarHeight;
}
})
},
// 获取根目录
getRootDirectory() {
this.inaccessible = false;
// 修改退出状态,以便在点击返回按钮时最后一层返回的是根目录,再点击一次才会退出
this.isExit = false;
this.addressBar = [];
var environment = plus.android.importClass("android.os.Environment");
environment.getExternalStorageState() === environment.MEDIA_MOUNTED;
var sdRoot = environment.getExternalStorageDirectory();
var rootName = plus.android.invoke(sdRoot, "getName");
this.rootAddress = {
name: rootName,
file: sdRoot,
};
var files = plus.android.invoke(sdRoot, "listFiles");
var len = files.length;
for (let i = 0; i < len; i++) {
// 过滤隐藏文件
if (!plus.android.invoke(files[i], "isHidden")) {
// 判断是文件还是文件夹
if (plus.android.invoke(files[i], "isDirectory")) {
var folderName = plus.android.invoke(files[i], "getName")
this.folderArr.push({
name: folderName,
file: files[i]
})
} else {
var fileName = plus.android.invoke(files[i], "getName")
if (this.filterArr.length > 0) {
if (fileName.search(this.filterReg) < 0) {
continue;
}
}
if (fileName.search(/bin/i) > -1) {
// 二进制 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'bin',
select: false
})
}
// else if (fileName.search(/doc|docx/i) > -1) {
// // doc/docx 文件
// this.fileArr.push({
// name: fileName,
// file: files[i],
// type: 'doc',
// select: false
// })
// } else if (fileName.search(/pdf/i) > -1) {
// // pdf 文件
// this.fileArr.push({
// name: fileName,
// file: files[i],
// type: 'pdf',
// select: false
// })
// } else {
// // 其他文件
// this.fileArr.push({
// name: fileName,
// file: files[i],
// type: 'file',
// select: false
// })
// }
}
}
}
// 排序,不区分大小写
this.folderArr.sort(function(a, b) {
return a.name.toUpperCase() > b.name.toUpperCase() ? '1' : '-1'
});
this.fileArr.sort(function(a, b) {
return a.name.toUpperCase() > b.name.toUpperCase() ? '1' : '-1'
});
this.rootAddress.folderArr = this.folderArr;
this.rootAddress.fileArr = this.fileArr;
},
// 进入文件夹
toFolder(event) {
this.isExit = false; // 地址栈中存在新地址,重置退出状态
this.folderArr = [];
this.fileArr = [];
this.addressBar.push(event)
var files = plus.android.invoke(event.file, "listFiles");
if (files == null) {
this.inaccessible = true;
}
var len = files.length;
for (let i = 0; i < len; i++) {
// 过滤隐藏文件
if (!plus.android.invoke(files[i], "isHidden")) {
// 判断是文件还是文件夹
if (plus.android.invoke(files[i], "isDirectory")) {
var folderName = plus.android.invoke(files[i], "getName")
this.folderArr.push({
name: folderName,
file: files[i]
})
} else {
var fileName = plus.android.invoke(files[i], "getName")
if (this.filterArr.length > 0) {
if (fileName.search(this.filterReg) < 0) {
continue;
}
}
if (fileName.search(/txt/i) > -1) {
// txt 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'txt',
select: false
})
} else if (fileName.search(/doc|docx/i) > -1) {
// doc/docx 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'doc',
select: false
})
} else if (fileName.search(/pdf/i) > -1) {
// pdf 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'pdf',
select: false
})
} else {
// 其他文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'file',
select: false
})
}
}
}
}
// 排序,不区分大小写
this.folderArr.sort(function(a, b) {
return a.name.toUpperCase() > b.name.toUpperCase() ? '1' : '-1'
});
this.fileArr.sort(function(a, b) {
return a.name.toUpperCase() > b.name.toUpperCase() ? '1' : '-1'
});
},
// 返回根目录
backRoot() {
this.inaccessible = false;
this.addressBar = [];
this.folderArr = this.rootAddress.folderArr;
this.fileArr = this.rootAddress.fileArr;
},
// 返回上级文件夹
backFolder(event, index) {
this.inaccessible = false;
var len = this.addressBar.length;
if (index + 1 == len) {
// 点击当前文件夹--无事发生
return;
} else {
this.folderArr = [];
this.fileArr = [];
this.addressBar.splice(index + 1, len - index + 1)
var files = plus.android.invoke(event.file, "listFiles");
var len = files.length;
for (let i = 0; i < len; i++) {
// 过滤隐藏文件
if (!plus.android.invoke(files[i], "isHidden")) {
// 判断是文件还是文件夹
if (plus.android.invoke(files[i], "isDirectory")) {
var folderName = plus.android.invoke(files[i], "getName")
this.folderArr.push({
name: folderName,
file: files[i]
})
} else {
var fileName = plus.android.invoke(files[i], "getName");
if (this.filterArr.length > 0) {
if (fileName.search(this.filterReg) < 0) {
continue;
}
}
if (fileName.search(/txt/i) > -1) {
// txt 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'txt',
select: false
})
} else if (fileName.search(/doc|docx/i) > -1) {
// doc/docx 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'doc',
select: false
})
} else if (fileName.search(/pdf/i) > -1) {
// pdf 文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'pdf',
select: false
})
} else {
// 其他文件
this.fileArr.push({
name: fileName,
file: files[i],
type: 'file',
select: false
})
}
}
}
}
// 排序,不区分大小写
this.folderArr.sort(function(a, b) {
return a.name.toUpperCase() > b.name.toUpperCase() ? '1' : '-1'
});
this.fileArr.sort(function(a, b) {
return a.name.toUpperCase() > b.name.toUpperCase() ? '1' : '-1'
});
}
},
// 选中文件
selectFile(index) {
if (this.fileArr[index].select) {
// 取消选中
this.$set(this.fileArr[index], 'select', false);
let name = this.fileArr[index].name;
for (let i = 0; i < this.selectArr.length; i++) {
if (name == this.selectArr[i].name) {
this.selectArr.splice(i, 1);
break;
}
}
} else {
// 选中
this.$set(this.fileArr[index], 'select', true);
// 读文件
var FileInputStream = plus.android.importClass("java.io.FileInputStream");
var fileSize = new FileInputStream(this.fileArr[index].file);
// 获取文件的相对路径
var Path = plus.android.invoke(this.fileArr[index].file, "getPath");
var size = fileSize.available();
var sizeMb = size / 1048576;
sizeMb = sizeMb.toFixed(4);
this.selectArr.push({
name: this.fileArr[index].name,
url: Path,
sizeMB: sizeMb,
})
}
},
// 点击上传按钮
uploadBtn() {
if(this.selectArr.length==1){
this.$emit("confirm", this.selectArr);
this.close();
}else{
this.$refs.uToast.show({
message: "只能选择单个文件!",
type: "error",
});
}
},
// 点击返回
backAddress() {
// 先判断地址栈中是否还有地址
var len = this.addressBar.length;
if (len > 1) {
// 返回上级文件夹
let index = len - 2;
let event = this.addressBar[index];
this.backFolder(event, index);
} else {
// 退出文件选择
if (this.isExit) {
// 退出文件选择
this.close();
// this.$u.toast('在点击一次退出文件选择')
} else {
// 返回根目录
this.isExit = true; // 下一次再点击则退出
this.backRoot();
uni.showToast({
title: '再操作一次退出文件选择',
icon: 'none',
duration: 1000
});
}
}
}
}
}
</script>
<style scoped>
.file-outerBox {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1070;
padding-bottom: 40rpx;
background-color: #fff;
}
.file-titel {
width: 100%;
height: 80rpx;
line-height: 80rpx;
text-align: center;
// background-color: #FFFFFF;
padding: 0 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.file-nav-leftBox {
width: 60rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.file-back-img {
width: 36rpx;
}
.file-nav-rightBox {
width: 60rpx;
height: 100%;
}
.file-address {
width: 100%;
height: 60rpx;
background-color: #FBFBFB;
padding: 0 32rpx;
display: flex;
color: #373737;
font-size: 24rpx;
}
.address-scroll {
width: calc(100% - 116rpx);
white-space: nowrap;
height: 100%;
}
.address-box {
height: 60rpx;
line-height: 60rpx;
display: inline-block;
}
.to-img-box {
width: 20rpx;
}
.root-box {
width: 116rpx;
height: 60rpx;
line-height: 60rpx;
display: inline-block;
box-shadow: 10rpx 0 10rpx -10rpx rgba(8, 8, 8, 0.3);
}
.to-img-box {
width: 20rpx;
}
.folder-box {
height: 120rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 32rpx;
border-bottom: 1px solid #EEEEEE;
}
.folder-name-box {
width: 80%;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.folder-img {
width: 72rpx;
margin-right: 16rpx;
}
.name-box {
width: calc(100% - 100rpx);
overflow: hidden;
word-wrap: break-word;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.toFolder-img {
width: 28rpx;
}
.select-tips {
width: 100%;
height: 160rpx;
text-align: center;
font-size: 32rpx;
color: #888;
padding-top: 60rpx;
}
.file-box {
height: 120rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 32rpx;
border-bottom: 1px solid #EEEEEE;
}
.file-name-box {
width: 80%;
display: flex;
align-items: center;
// .type-file{
// width: 60rpx;
// margin-left: 10rpx;
// }
}
.file-img {
width: 72rpx;
margin-right: 16rpx;
}
.name-box {
width: calc(100% - 100rpx);
overflow: hidden;
word-wrap: break-word;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.select-img {
width: 32rpx;
}
.select-foot-btn {
width: calc(100% - 64rpx);
margin-top: 20rpx;
}
</style>