ソースを参照

app2.0 使用lsj插件完成app端上传文件的适配

娄底高职分支
杨晓琪 2年前
コミット
aebac4867c
8個のファイルの変更501行の追加184行の削除
  1. +1
    -1
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/common/custompage.js
  2. +1
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/customform.vue
  3. +290
    -180
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue
  4. +12
    -3
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/lsj-upload/lsj-upload.vue
  5. +3
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/EducationalAdministration/StuAttendanceLeave/single.vue
  6. +2
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/PersonnelManagement/MeetingManagement/single.vue
  7. +8
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/static/html/js/vue.min.js
  8. +184
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/static/html/uploadFile.html

+ 1
- 1
Learun.Framework.Ultimate V7/LearunApp-2.2.0/common/custompage.js ファイルの表示

@@ -249,7 +249,7 @@ export default {
}
for (const item of val) {
if (item.uid) {
if (item.uid || item.folderId) {
// uploadUid = item.uid
continue
}


+ 1
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/customform.vue ファイルの表示

@@ -131,6 +131,7 @@
v-else-if="item.type === 'upload'"
@input="setValue(item.__valuePath__, $event)"
:value="getValue(item.__valuePath__)"
:labelId="item.id"
:required="Boolean(item.verify)"
:readonly="!isEdit(item)"
:title="item.title"


+ 290
- 180
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue ファイルの表示

@@ -1,190 +1,300 @@
<template>
<view>
<view class="cu-form-group" style="border-bottom: none; padding-bottom: 0;">
<view class="title">
<text v-if="required" class="lr-required">*</text>
{{ title || '' }}
</view>
</view>

<view class="cu-form-group" style="border-top: none;">
<view class="grid col-4 grid-square flex-sub">
<view v-for="(file, index) in value" :key="index" class="bg-img" style="position: relative;">
<view v-if="file.noUpdated" class="mask"></view>
<image
v-if="isImgFile(file.type)"
@click="fileClick(index)"
:src="file.path?file.path:file"
:webp="file.type === 'webp'"
mode="aspectFill"
></image>
<view v-else-if="isDocFile(file.type)" @click="fileClick(index)" class="file-icon solids">
<l-icon type="text" />
</view>
<view v-else class="file-icon solids" @click="fileClick(index)"><l-icon type="text" /></view>

<view v-if="!readonly" @click.stop="delFile(index,file.uid)" class="cu-tag bg-red" style="height: 24px; width: 24px;">
<l-icon type="close" color="white" style="width: 18px; height: 24px; font-size: 24px;" />
</view>
<view class="fileName">
<text>{{file.name}}</text>
</view>
</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 class="cu-form-group" style="border-bottom: none; padding-bottom: 0;">
<view class="title">
<text v-if="required" class="lr-required">*</text>
{{ title || '' }}
</view>
<!-- #ifdef APP-PLUS -->
<view v-if="formData.folderId&&!readonly && value.length < Number(number)" style="display: inline-block;padding-left: 20px;">
<lsjupload ref="lsjUpload" height="32px" width="80px" :option="{
url:API+'/learun/adms/annexes/wxupload',formData
}" :instantly="true" :count="1" :size="100" @change="chooseChange">
</lsjupload>
</view>
<!-- #endif -->
</view>
</view>
</view>
</view>

<view class="cu-form-group" style="border-top: none;">
<view class="grid col-4 grid-square flex-sub">
<view v-for="(file, index) in value" :key="index" class="bg-img" style="position: relative;">
<view v-if="file.noUpdated" class="mask"></view>
<image v-if="isImgFile(file.type)" @click="fileClick(index)" :src="file.path?file.path:file"
:webp="file.type === 'webp'" mode="aspectFill"></image>
<view v-else-if="isDocFile(file.type)" @click="fileClick(index)" class="file-icon solids">
<l-icon type="text" />
</view>
<view v-else class="file-icon solids" @click="fileClick(index)">
<l-icon type="text" />
</view>

<view v-if="!readonly" @click.stop="delFile(index,file.uid)" class="cu-tag bg-red"
style="height: 24px; width: 24px;">
<l-icon type="close" color="white" style="width: 18px; height: 24px; font-size: 24px;" />
</view>
<view class="fileName">
<text>{{file.name}}</text>
</view>
</view>
<!-- #ifndef APP-PLUS -->
<view v-if="!readonly && value.length < Number(number)" @click="chooseFile" class="solids">
<l-icon type="file" />
</view>
<!-- #endif -->
</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: {},
value: { 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
import lsjupload from '@/components/lsj-upload/lsj-upload.vue'
export default {
name: 'l-upload-file',
components: {
lsjupload,
},
props: {
number: {
default: 1
},
labelId: {
default: ''
},
tableName: {
default: ''
},
fieldName: {
default: ''
},
readonly: {},
value: {
default: () => []
},
title: {},
required: {}
},
data() {
return {
formData: {}
}
},
created() {
// #ifdef APP-PLUS
if(this.labelId){
var guid = undefined
// 先生成一个guid
guid = this.newguid();
// 取出当前列对应的labelId
var labeId = this.labelId
// 从缓存取出当前审批流程所有的guid
var guids = JSON.parse(uni.getStorageSync('guids'))
if (guids && JSON.stringify(guids) !== '{}' && guids[labeId]) {
guid = guids[labeId]
}
this.formData.loginMark=this.getLoginMark(),
this.formData.token=this.GET_GLOBAL('token')
this.formData['folderId'] = guid
}else{
if(!this.tableName||!this.fieldName)return
var uploadUid = '',
tableName=this.tableName,
fieldName=this.fieldName
let folderIds = uni.getStorageSync('folderIds');
if(folderIds){
folderIds = JSON.parse(folderIds)
if(folderIds[tableName]&&folderIds[tableName][fieldName]){
uploadUid = folderIds[tableName][fieldName]
}
}
if(!uploadUid){
uploadUid = this.newguid()
}
this.formData.loginMark=this.getLoginMark(),
this.formData.token=this.GET_GLOBAL('token')
this.formData['folderId'] = uploadUid
}
// #endif
},

methods: {
async chooseChange(files) {
let array = Array.from(files);
if (array.length) {
await this.$refs.lsjUpload.clear()
}
let tempFilePaths = [],
tempFiles = [];
array.forEach(item => {
tempFilePaths.push(item[1].path)
tempFiles.push({
name: item[1].name,
size: item[1].size,
folderId: this.formData.folderId
})
})
if (!tempFilePaths.length) {
return
}
// let fileName = tempFiles[tempFiles.length]
// let uploadRes = await this.$refs.lsjUpload.upload(tempFiles[i].name)
// if(!uploadRes){
// this.TOAST('上传失败!')
// 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,
folderId:tempFiles[i].folderId,
noUpdated: false
}))
)
this.$emit('input', newList)
this.$emit('change', newList)
this.$emit('add')
},

getFileExt(path) {
return /\.(\w{2,5})$/.exec(path)[1] || null
},

isImgFile(type) {
const typeString = (type || '').toLowerCase()
return ['jpg', 'image/jpg', 'jpeg', 'image/jpeg', 'png', 'image/png', 'gif', 'image/gif', 'bmp',
'image/bmp', 'webp', 'image/webp', 'image'
].includes(typeString)
},

isDocFile(type) {
const typeString = (type || '').toLowerCase()
return ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'].includes(typeString)
},

async delFile(index, fileId) {
if (!(await this.CONFIRM('删除文件', '确定要删除该文件吗?', true))) {
return
}
const newList = JSON.parse(JSON.stringify(this.value))
newList.splice(index, 1)
this.$emit('input', newList)
this.$emit('change')
this.$emit('del')
//物理删除
this.DELETE_FILE(fileId);
},

chooseFile() {
// #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, i) => ({
path: t,
type: tempFiles[i].type,
size: tempFiles[i].size,
name: tempFiles[i].name,
noUpdated: false
}))
)
this.$emit('input', newList)
this.$emit('change', newList)
this.$emit('add')
}
})
},

async fileClick(index) {
const {
path,
type,
uid,
size = 0
} = this.value[index]
if (this.isImgFile(type)) {
uni.previewImage({
urls: [path],
current: path
})
} else {
// #ifdef APP-PLUS
uni.downloadFile({
url: path, //仅为示例,并非真实的资源
success: (res) => {
if (res.statusCode === 200) {
uni.openDocument({
filePath: res.tempFilePath,
fileType: type
})
}else{
this.TOAST('下载失败!')
}
}
});
return
// #endif
uni.openDocument({
filePath: path,
fileType: type
})
}
},


newguid() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
},
}
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
},

isImgFile(type) {
const typeString = (type || '').toLowerCase()
return ['jpg','image/jpg','jpeg','image/jpeg', 'png', 'image/png','gif', 'image/gif','bmp', 'image/bmp','webp', 'image/webp','image'].includes(typeString)
},

isDocFile(type) {
const typeString = (type || '').toLowerCase()
return ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'].includes(typeString)
},

async delFile(index,fileId) {
if (!(await this.CONFIRM('删除文件', '确定要删除该文件吗?', true))) {
return
}
const newList = JSON.parse(JSON.stringify(this.value))
newList.splice(index, 1)
this.$emit('input', newList)
this.$emit('change')
this.$emit('del')
//物理删除
this.DELETE_FILE(fileId);
},

chooseFile() {
// #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,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')
}
})
},

async fileClick(index) {
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 })
}
}
}
}
}
</script>

<style lang="less" scoped>
.file-icon {
line-height: 100%;
position: static;
}
.fileName{
padding: 2px 2px;
margin-bottom: 2px;
text-align: center;
position: absolute;
bottom: 0px;
width: 100%;
background: rgba(0,0,0,0.2);
color: #fff;
font-size: 12px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.mask{
position: absolute;
top: 0;left: 0;
width: 100%;
height: 100%;
// background: rgba(255,252,153,0.2);
background: rgba(0,0,0,0.7);
z-index: 100;
pointer-events: none;
}
.file-icon {
line-height: 100%;
position: static;
}

.fileName {
padding: 2px 2px;
margin-bottom: 2px;
text-align: center;
position: absolute;
bottom: 0px;
width: 100%;
background: rgba(0, 0, 0, 0.2);
color: #fff;
font-size: 12px;

text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
// background: rgba(255,252,153,0.2);
background: rgba(0, 0, 0, 0.7);
z-index: 100;
pointer-events: none;
}
</style>

+ 12
- 3
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/lsj-upload/lsj-upload.vue ファイルの表示

@@ -1,7 +1,7 @@
<template>
<view class="lsj-file" :style="[getStyles]">
<view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
<slot><view class="defview" :style="[getStyles]">附件上传</view></slot>
<slot><view class="defview" :style="[getStyles]">点击上传</view></slot>
</view>
</view>
</template>
@@ -246,8 +246,9 @@ export default {
// 创建选择器
create() {
// 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去,并将此处path改成服务器上的地址
let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
let path = '/static/html/uploadFile.html';
let dom = this.lsjFile.create(path);
// #ifdef H5
this.$refs.lsj.$el.appendChild(dom);
// #endif
@@ -286,7 +287,15 @@ export default {
title: msg,
icon: 'none'
});
}
},
newguid() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
},
}
}
</script>


+ 3
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/EducationalAdministration/StuAttendanceLeave/single.vue ファイルの表示

@@ -38,7 +38,10 @@
@input="setValue('StuMail.Files', $event)"
:value="getValue('StuMail.Files')"
:readonly="!edit"
:number="9"
title="附件上传"
tableName="StuMail"
fieldName="Files"
/>
<l-textarea
@input="setValue('StuMail.LeaveReason', $event)"


+ 2
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/PersonnelManagement/MeetingManagement/single.vue ファイルの表示

@@ -72,6 +72,8 @@
:readonly="!edit"
:number="9"
title="附件上传"
tableName="MeetingManagement"
fieldName='Files'
/>
</view>



+ 8
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/static/html/js/vue.min.js
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 184
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/static/html/uploadFile.html ファイルの表示

@@ -0,0 +1,184 @@
<!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;}
.btn {position: relative;top: 0;left: 0;bottom: 0;right: 0;}
.btn .file {position: fixed;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;width: 100%;opacity: 0;}
</style>
</head>

<body>
<div id="content" class="content">
<div class="btn">
<input @change="onChange" :accept="accept" ref="file" class="file" type="file" />
</div>
</div>
<script type="text/javascript" src="js/vue.min.js"></script>
<script type="text/javascript">
let _this;
var vm = new Vue({
el: '#content',
data: {
accept: '',
},
mounted() {
console.log('加载webview');
_this = this;
this.files = new Map();
document.addEventListener('plusready', (e)=>{
let {debug,instantly,prohibited} = plus.webview.currentWebview();
this.debug = debug;
this.instantly = instantly;
this.prohibited = prohibited;
this.accept = prohibited.accept;
location.href = 'callback?retype=updateOption';
}, false);
},
methods: {
toast(msg) {
plus.nativeUI.toast(msg);
},
clear(name) {
if (!name) {
this.files.clear();
return;
}
this.files.delete(name);
},
setData(option='{}') {
this.debug&&console.log('更新参数:'+option);
try{
_this.option = JSON.parse(option);
}catch(e){
console.error('参数设置错误')
}
},
async upload(name=''){
if (name && this.files.has(name)) {
await this.createUpload(this.files.get(name));
}
else {
for (let item of this.files.values()) {
if (item.type === 'waiting' || item.type === 'fail') {
await this.createUpload(item);
}
}
}
},
openFile(url){
plus.runtime.openFile(url)
},
onChange(e) {
let fileDom = this.$refs.file;
let file = fileDom.files[0];
let name = file.name;
fileDom.value = '';
this.debug&&console.log('文件名称',name,'大小',file.size);
if (file) {
// 限制文件格式
let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
let formats = this.prohibited.formats.toLowerCase();
if (formats&&!formats.includes(suffix)) {
this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
return;
}
console.log('this.size',this.prohibited.size);
// 限制文件大小
if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
this.toast(`附件大小请勿超过${this.prohibited.size}M`)
return;
}
let path = URL.createObjectURL(file);
this.files.set(file.name,{file,path,name: file.name,size: file.size,progress: 0,type: 'waiting'});
this.callChange();
this.instantly&&this.upload();
}
},
/**
* @returns {Map} 已选择的文件Map集
*/
callChange() {
location.href = 'callback?retype=change&files=' + escape(JSON.stringify([...this.files]));
},
/**
* @returns {object} 正在处理的当前对象
*/
changeFilesItem(item,end='') {
this.files.set(item.name,item);
location.href = 'callback?retype=progress&end='+ end +'&item=' + escape(JSON.stringify(item));
},
createUpload(item) {
this.debug&&console.log('准备上传,option=:'+JSON.stringify(this.option));
item.type = 'loading';
delete item.responseText;
return new Promise((resolve,reject)=>{
let {url,name,method='POST',header={},formData={}} = this.option;
let form = new FormData();
for (let keys in formData) {
form.append(keys, formData[keys])
console.log(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)
});
}
}
});
</script>
</body>

</html>

読み込み中…
キャンセル
保存