Browse Source

app2.0 附件上传引用插件

娄底高职分支
杨晓琪 2 years ago
parent
commit
c8a8830e48
4 changed files with 763 additions and 81 deletions
  1. +55
    -78
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue
  2. +392
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/lsj-upload/LsjFile.js
  3. +311
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/lsj-upload/lsj-upload.vue
  4. +5
    -3
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/login.vue

+ 55
- 78
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue View File

@@ -30,19 +30,24 @@
<text>{{file.name}}</text>
</view>
</view>

<view v-if="!readonly && value.length < Number(number)" @click="chooseFile" class="solids">
<l-icon type="file" />
</view>
<!-- app中的文件选择 -->
<view v-if="!readonly && value.length < Number(number)" @click="chooseFile" class="solids">
<l-icon type="file" />
<lsjupload v-if="isApp" ref="lsjUpload" height="80px" width="100%" :option="{}" :count="1" :size="100"
@change="chooseChange" style="opacity: 0;"></lsjupload>
</view>
</view>
</view>
</view>
</template>

<script>
import lsjupload from '@/components/lsj-upload/lsj-upload.vue'
export default {
name: 'l-upload-file',

components:{
lsjupload
},
props: {
number: { default: 1 },
readonly: {},
@@ -50,8 +55,50 @@ export default {
title: {},
required: {}
},
data() {
return {
isApp:true
}
},
mounted() {
this.init()
},

methods: {
init(){
// #ifndef APP-PLUS
this.isApp = false
// #endif
},
chooseChange(files) {
let array = Array.from(files);
if (array.length) {
this.$refs.lsjUpload.clear()
}
let tempFilePaths = [],
tempFiles = [];
array.forEach(item => {
tempFilePaths.push(item[1].path)
tempFiles.push(item[1].file)
})
if(!tempFilePaths.length){
return
}
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
tempFilePaths.map((t,i) => ({ path: t, type: tempFiles[i].type, size:tempFiles[i].size, name:tempFiles[i].name, noUpdated:true} ))
)
this.$emit('input', newList)
this.$emit('change', newList)
this.$emit('add')
// #ifdef APP-PLUS
this.$refs.lsjUpload.show()
// #endif
},
getFileExt(path) {
return /\.(\w{2,5})$/.exec(path)[1] || null
},
@@ -80,44 +127,15 @@ export default {
},

chooseFile() {
// #ifdef MP-DINGTALK
// dd.chooseImage({
// count: Number(this.number),
// success: ({ filePaths }) => {
// if (filePaths) {
// const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// filePaths.map(t => ({ path: t, type: this.getFileExt(t) }))
// )
// this.$emit('input', newList)
// this.$emit('change', newList)
// this.$emit('add')
// }
// }
// })
// #endif

// #ifndef MP-DINGTALK
// uni.chooseFile({
// count: Number(this.number),
// sizeType: ['original', 'compressed'],
// sourceType: ['album', 'camera'],
// success: ({ tempFilePaths }) => {
// const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))
// )
// this.$emit('input', newList)
// this.$emit('change', newList)
// this.$emit('add')
// }
// })
// #ifdef APP-PLUS
return
// #endif
uni.chooseFile({
count: Number(this.number),
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: ({ tempFilePaths,tempFiles }) => {
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))
tempFilePaths.map((t,i) => ({ path: t, type: tempFiles[i].type, size:tempFiles[i].size, name:tempFiles[i].name, noUpdated:true} ))
)
this.$emit('input', newList)
@@ -125,56 +143,15 @@ export default {
this.$emit('add')
}
})
// #endif
},

async fileClick(index) {
// if(typeof this.value[index] == "string"){
// uni.previewImage({ urls: this.value.filter(item=>typeof this.value[index] == "string"), current: this.value[index] })
// return
// }
const { path, type, uid, size = 0 } = this.value[index]
if (this.isImgFile(type)) {
uni.previewImage({ urls: [path], current: path })
}else{
uni.openDocument({ filePath: path, fileType: type })
}
// if (this.isImgFile(type)) {
// uni.previewImage({ urls: [path], current: path })
// } else if (this.isDocFile(type)) {
// // #ifndef H5 || MP-DINGTALK
// if (size >= 50 * 1024 * 1024) {
// this.TOAST('小程序端无法下载超过50MB的文件,此文件大小为${size}KB,超过限制')
// return
// }
// // #endif

// // #ifndef MP-DINGTALK
// const tempFilePath = await this.HTTP_DOWNLOAD(uid)
// uni.openDocument({ filePath: tempFilePath, fileType: type })
// // #endif

// // #ifdef MP-DINGTALK
// this.TOAST('钉钉小程序只支持查看图片文件')
// // #endif
// } else {
// // #ifndef MP-DINGTALK
// this.TOAST('小程序端只支持打开图片和文档(word、pdf等)文件')
// // #endif

// // #ifdef MP-DINGTALK
// this.TOAST('钉钉小程序只支持查看图片文件')
// // #endif

// // #ifdef APP-VUE
// const tempFilePath = await this.HTTP_DOWNLOAD(uid)
// uni.openDocument({ filePath: tempFilePath, fileType: type })
// // #endif

// // #ifdef H5
// await this.HTTP_DOWNLOAD(uid)
// // #endif
// }
}
}
}


+ 392
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/lsj-upload/LsjFile.js View File

@@ -0,0 +1,392 @@
export class LsjFile {
constructor(data) {
this.dom = null;
// files.type = waiting(等待上传)|| loading(上传中)|| success(成功) || fail(失败)
this.files = new Map();
this.debug = data.debug || false;
this.id = data.id;
this.width = data.width;
this.height = data.height;
this.option = data.option;
this.instantly = data.instantly;
this.prohibited = data.prohibited;
this.onchange = data.onchange;
this.onprogress = data.onprogress;
this.uploadHandle = this._uploadHandle;
// #ifdef MP-WEIXIN
this.uploadHandle = this._uploadHandleWX;
// #endif
}
/**
* 创建File节点
* @param {string}path webview地址
*/
create(path) {
if (!this.dom) {
// #ifdef H5
let dom = document.createElement('input');
dom.type = 'file'
dom.value = ''
dom.style.height = this.height
dom.style.width = this.width
dom.style.position = 'absolute'
dom.style.top = 0
dom.style.left = 0
dom.style.right = 0
dom.style.bottom = 0
dom.style.opacity = 0
dom.style.zIndex = 999
dom.accept = this.prohibited.accept;
if (this.prohibited.count > 1) {
dom.multiple = 'multiple';
}
dom.onchange = event => {
for (let file of event.target.files) {
this.addFile(file);
}
this.dom.value = '';
};
this.dom = dom;
// #endif
// #ifdef APP-PLUS
let styles = {
top: '-100px',
left: 0,
width: '1px',
height: '1px',
background: 'transparent'
};
let extras = {
debug: this.debug,
instantly: this.instantly,
prohibited: this.prohibited,
}
this.dom = plus.webview.create(path, this.id, styles,extras);
this.setData(this.option);
this._overrideUrlLoading();
// #endif
return this.dom;
}
}
copyObject(obj) {
if (typeof obj !== "undefined") {
return JSON.parse(JSON.stringify(obj));
} else {
return obj;
}
}
/**
* 自动根据字符串路径设置对象中的值 支持.和[]
* @param {Object} dataObj 数据源
* @param {String} name 支持a.b 和 a[b]
* @param {String} value 值
* setValue(dataObj, name, value);
*/
setValue(dataObj, name, value) {
// 通过正则表达式 查找路径数据
let dataValue;
if (typeof value === "object") {
dataValue = this.copyObject(value);
} else {
dataValue = value;
}
let regExp = new RegExp("([\\w$]+)|\\[(:\\d)\\]", "g");
const patten = name.match(regExp);
// 遍历路径 逐级查找 最后一级用于直接赋值
for (let i = 0; i < patten.length - 1; i++) {
let keyName = patten[i];
if (typeof dataObj[keyName] !== "object") dataObj[keyName] = {};
dataObj = dataObj[keyName];
}
// 最后一级
dataObj[patten[patten.length - 1]] = dataValue;
this.debug&&console.log('参数更新后',JSON.stringify(this.option));
}
/**
* 设置上传参数
* @param {object|string}name 上传参数,支持a.b 和 a[b]
*/
setData() {
let [name,value = ''] = arguments;
if (typeof name === 'object') {
Object.assign(this.option,name);
}
else {
this.setValue(this.option,name,value);
}
this.debug&&console.log(JSON.stringify(this.option));
// #ifdef APP-PLUS
this.dom.evalJS(`vm.setData('${JSON.stringify(this.option)}')`);
// #endif
}
/**
* 上传
* @param {string}name 文件名称
*/
async upload(name='') {
if (!this.option.url) {
throw Error('未设置上传地址');
}
// #ifndef APP-PLUS
if (name && this.files.has(name)) {
await this.uploadHandle(this.files.get(name));
}
else {
for (let item of this.files.values()) {
if (item.type === 'waiting' || item.type === 'fail') {
await this.uploadHandle(item);
}
}
}
// #endif
// #ifdef APP-PLUS
this.dom&&this.dom.evalJS(`vm.upload('${name}')`);
// #endif
}
// 选择文件change
addFile(file) {
let name = file.name;
this.debug&&console.log('文件名称',name,'大小',file.size);
if (file) {
// 限制文件格式
let path = '';
let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
let formats = this.prohibited.formats.toLowerCase();
if (formats&&!formats.includes(suffix)) {
this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
return false;
}
// 限制文件大小
if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
this.toast(`附件大小请勿超过${this.prohibited.size}M`)
return false;
}
// #ifndef MP-WEIXIN
path = URL.createObjectURL(file);
// #endif
// #ifdef MP-WEIXIN
path = file.path;
// #endif
this.files.set(file.name,{file,path,name: file.name,size: file.size,progress: 0,type: 'waiting'});
// #ifndef MP-WEIXIN
this.onchange(this.files);
this.instantly&&this.upload();
// #endif
// #ifdef MP-WEIXIN
return true;
// #endif
}
}
/**
* 移除文件
* @param {string}name 不传name默认移除所有文件,传入name移除指定name的文件
*/
clear(name='') {
// #ifdef APP-PLUS
this.dom&&this.dom.evalJS(`vm.clear('${name}')`);
// #endif
if (!name) {
this.files.clear();
}
else {
this.files.delete(name);
}
return this.onchange(this.files);
}
/**
* 提示框
* @param {string}msg 轻提示内容
*/
toast(msg) {
uni.showToast({
title: msg,
icon: 'none'
});
}
/**
* 微信小程序选择文件
* @param {number}count 可选择文件数量
*/
chooseMessageFile(type,count) {
wx.chooseMessageFile({
count: count,
type: type,
success: ({ tempFiles }) => {
for (let file of tempFiles) {
let next = this.addFile(file);
if (!next) {return}
}
this.onchange(this.files);
this.instantly&&this.upload();
},
fail: () => {
this.toast(`打开失败`);
}
})
}
_overrideUrlLoading() {
this.dom.overrideUrlLoading({ mode: 'reject' }, e => {
let {retype,item,files,end} = this._getRequest(
e.url
);
let _this = this;
switch (retype) {
case 'updateOption':
this.dom.evalJS(`vm.setData('${JSON.stringify(_this.option)}')`);
break
case 'change':
try {
_this.files = new Map([..._this.files,...JSON.parse(unescape(files))]);
} catch (e) {
return console.error('出错了,请检查代码')
}
_this.onchange(_this.files);
break
case 'progress':
try {
item = JSON.parse(unescape(item));
} catch (e) {
return console.error('出错了,请检查代码')
}
_this._changeFilesItem(item,end);
break
default:
break
}
})
}
_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
}
_changeFilesItem(item,end=false) {
this.debug&&console.log('onprogress',JSON.stringify(item));
this.onprogress(item,end);
this.files.set(item.name,item);
}
_uploadHandle(item) {
item.type = 'loading';
delete item.responseText;
return new Promise((resolve,reject)=>{
this.debug&&console.log('option',JSON.stringify(this.option));
let {url,name,method='POST',header,formData} = this.option;
let form = new FormData();
for (let keys in formData) {
form.append(keys, formData[keys])
}
form.append(name, item.file);
let xmlRequest = new XMLHttpRequest();
xmlRequest.open(method, url, true);
for (let keys in header) {
xmlRequest.setRequestHeader(keys, header[keys])
}
xmlRequest.upload.addEventListener(
'progress',
event => {
if (event.lengthComputable) {
let progress = Math.ceil((event.loaded * 100) / event.total)
if (progress <= 100) {
item.progress = progress;
this._changeFilesItem(item);
}
}
},
false
);
xmlRequest.ontimeout = () => {
console.error('请求超时')
item.type = 'fail';
this._changeFilesItem(item,true);
return resolve(false);
}
xmlRequest.onreadystatechange = ev => {
if (xmlRequest.readyState == 4) {
if (xmlRequest.status == 200) {
this.debug&&console.log('上传完成:' + xmlRequest.responseText)
item['responseText'] = xmlRequest.responseText;
item.type = 'success';
this._changeFilesItem(item,true);
return resolve(true);
} else if (xmlRequest.status == 0) {
console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配,服务端已正确开启跨域,并且nginx未拦截阻止请求')
}
console.error('--ERROR--:status = ' + xmlRequest.status)
item.type = 'fail';
this._changeFilesItem(item,true);
return resolve(false);
}
}
xmlRequest.send(form)
});
}
_uploadHandleWX(item) {
item.type = 'loading';
delete item.responseText;
return new Promise((resolve,reject)=>{
this.debug&&console.log('option',JSON.stringify(this.option));
let form = {filePath: item.file.path,...this.option };
form['fail'] = ({ errMsg = '' }) => {
console.error('--ERROR--:' + errMsg)
item.type = 'fail';
this._changeFilesItem(item,true);
return resolve(false);
}
form['success'] = res => {
if (res.statusCode == 200) {
this.debug&&console.log('上传完成,微信端返回不一定是字符串,根据接口返回格式判断是否需要JSON.parse:' + res.data)
item['responseText'] = res.data;
item.type = 'success';
this._changeFilesItem(item,true);
return resolve(true);
}
item.type = 'fail';
this._changeFilesItem(item,true);
return resolve(false);
}
let xmlRequest = uni.uploadFile(form);
xmlRequest.onProgressUpdate(({ progress = 0 }) => {
if (progress <= 100) {
item.progress = progress;
this._changeFilesItem(item);
}
})
});
}
}

+ 311
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/lsj-upload/lsj-upload.vue View File

@@ -0,0 +1,311 @@
<template>
<view class="lsj-file" :style="[getStyles]">
<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: 9 },
// 允许上传的文件格式(多个以逗号隔开)
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
},
onchange: this.onchange,
onprogress: this.onprogress,
});
this.create();
},
beforeDestroy() {
// #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() {
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>
.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>

+ 5
- 3
Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/login.vue View File

@@ -34,9 +34,11 @@
<l-icon slot="title" type="lock" />
</l-input>
<l-button @click="login(null)" size="lg" color="blue" class="margin-top-sm block" block>登 录</l-button>
<view class="otherLogin">
<navigator url="/pages/weixinLogin" class="textBtn">微信登录</text></navigator>
</view>
<!-- #ifndef APP-PLUS -->
<view class="otherLogin">
<navigator url="/pages/weixinLogin" class="textBtn">微信登录</text></navigator>
</view>
<!-- #endif -->
<!-- <l-button v-if="enableSignUp" @click="signUp" size="lg" line="blue" class="margin-top-sm block" block>
教师注册
</l-button> -->


Loading…
Cancel
Save