Commit b0a9b5d1 authored by Nature's avatar Nature

文件选择组建

parent 6957ac6a
......@@ -534,6 +534,10 @@ showActionSheetButton() {
### Note
hls-easy-ui#0.0.4
[添加懒加载组建](https://github.com/hilongjw/vue-lazyload)
[添加文件选择组建 h-file](/packages/components/HFile/README.md)
[添加单选框组建 h-radio](/packages/components/Radio/README.md)
[添加checkBox组建](/packages/components/CheckBox/README.md)
......
文件上传组建 h-file 用于选择本地文件预览上传
```html
<template>
<h-view>
<h-content>
<h-file
:file-list="fileList" v-model="fileList" :upload="upload"
:upload-config="uploadConfig" :disabled="disable" :accept="accept" :beforeRead="beforeRead"
:afterRead="afterRead" :maxCount="maxCount" :max-size="maxSize" :previewImage="previewImage"
:deleteImage="deleteImage"
:result-type="resultType"
@upload="uploaded"
@delete="deletePic"
@oversize="oversize"/>
</h-content>
</h-view>
</template>
<script>
export default {
name: 'FileTest',
data () {
return {
fileList: [], // 文件数组 必填
disable: false, // 是否禁用文件上传 默认false 可选
accept: 'image/png,image/jpeg', // 接受的文件类型 可选
upload: false, // 选取完成是否自动上传 默认 false 可选
maxSize: 2000000, // 文件大小限制,单位为byte 可选 超过大小的文件会自动取消
maxCount: 5, // 文件上传数量限制 可选超过数量自动隐藏选择按钮
deleteImage: true, // 是否删除文件 默认true 可选
previewImage: true, // 是否在选取完成后展示预览图 默认true 可选
resultType: 'dataUrl', // 文件读取结果类型 默认base64 可选值为text
uploadConfig: { // 文件上传配置
uploadUrl: $config.rootPath + '/app/fileUploadSvc?sysName=HLS_APP&apiName=attachment_upload', // 上传的URL
params: { // 上传的额外参数
user_id: 1,
source_type: 'HLS_DOC_FILE_TEMPLET',
pkvalue: 42,
},
uploadSuccess: this.fileSuccess, // 上传成功后的回调函数 用于给文件添加服务端唯一标示或其他
},
}
},
methods: {
/**
* email事件 可选
* 删除文件后触发 返回当前文件以及 对应的index
* @param file
* @param index
*/
deletePic (file, index) {
debugger
},
/**
* 可选
* 文件选择完成后读取前触发 返回当前选择的文件数组
* 返回 ture 或者promise对象 标示继续读取,否则终止 操作
* @param files 选择的文件数组
* @returns {boolean}
*/
beforeRead (files) {
files.forEach((file, index) => {
console.log(file.size)
})
return true
},
/**
* 可选
* 文件读取成功后触发添加了文件的content内容
* @param files 选择的文件数组
* @returns {boolean}
*/
afterRead (files) {
files.forEach((item, index) => {
console.log(item.file.name)
})
return true
},
/**
* email事件 可选
* 全部文件上传成功后触发
* @param fileList 全部文件数组
*/
uploaded (fileList) {
debugger
},
/**
* email事件 可选
* 文件大小超过配置的大小
* @param files
*/
oversize (files) {
debugger
},
/**
* 每个文件上传成功后的回调 可选配置
* @param fileList 文件数组
* @param result 上传返回结果
*/
fileSuccess (fileList, result) {
this.fileList = fileList
this.fileList.forEach((itemFile, index) => {
if (result.response && itemFile.file && !itemFile.attachment_id && result.response.fileName === itemFile.file.name) {
itemFile.attachment_id = result.response.attachment_id
}
})
},
},
}
</script>
<style scoped lang="less">
</style>
```
默认插槽 slot 用于自定义选择文件样式
v-model 绑定文件数组
fileList 文件数组 [{file:File,content:''base64 or url'}]
disable: false, // 是否禁用文件上传 默认false 可选
accept: 'image/png,image/jpeg', // 接受的文件类型 可选
upload: false, // 选取完成是否自动上传 默认 false 可选
maxSize: 2000000, // 文件大小限制,单位为byte 可选 超过大小的文件会自动取消
maxCunt: 5, // 文件上传数量限制 可选超过数量自动隐藏选择按钮
deleteImage: true, // 是否删除文件 默认true 可选
previewImage: true, // 是否在选取完成后展示预览图 默认true 可选
resultType: 'dataUrl', // 文件读取结果类型 默认base64 可选值为text
uploadConfig: { // 文件上传配置
uploadUrl: $config.rootPath + '/app/fileUploadSvc?sysName=HLS_APP&apiName=attachment_upload', // 上传的URL
params: { // 上传的额外参数
user_id: 1,
source_type: 'HLS_DOC_FILE_TEMPLET',
pkvalue: 42,
},
uploadSuccess: this.fileSuccess, // 上传成功后的回调函数 用于给文件添加服务端唯一标示或其他
},
/**
* @Author think
* @Date 2019-07-11 10:07
*/
<template>
<section class="h-file">
<div class="review-wrap">
<div v-for="(item,index) in fileList" v-if="previewImage" class="review-img">
<img v-lazy="item.content" :key="index" @click="showBigPic(index)">
<i v-if="deleteImage" class="icon ion-trash-a" @click="deleteFile(item,index)"/>
</div>
<section v-show="fileList.length < maxCount" class="file-upload">
<slot/>
<img v-if="!slot" class="default-img" src="./plus.png">
<input
ref="input" :accept="accept" :disabled="disabled" type="file"
multiple
@change="change">
</section>
</div>
</section>
</template>
<script>
import ShowPicturePlugin from '../ShowPicture/index'
import { toArray, readFile, isOversize, isImageDataUrl } from './utils'
import axios from 'axios'
export default {
name: 'HFile',
props: {
disabled: {
type: Boolean,
default: false,
},
afterRead: {
type: Function,
default: () => { return true },
},
beforeRead: {
type: Function,
default: () => { return true },
},
uploadConfig: {
type: Object,
default: () => {},
},
upload: {
type: Boolean,
default: false,
},
fileList: {
type: Array,
default: () => [],
},
accept: {
type: String,
default: 'image/*',
},
maxSize: {
type: Number,
default: Number.MAX_VALUE,
},
maxCount: {
type: Number,
default: Number.MAX_VALUE,
},
previewImage: {
type: Boolean,
default: true,
},
resultType: {
type: String,
default: 'dataUrl',
},
deleteImage: {
type: Boolean,
default: true,
},
},
data () {
return {
slot: false,
fileReadList: [],
uploadFiles: [],
}
},
mounted () {
let vm = this
if (this.$slots.default) {
vm.slot = true
} else {
vm.slot = false
}
},
methods: {
resetInput () {
/* istanbul ignore else */
if (this.$refs.input) {
this.$refs.input.value = ''
}
},
change ($event) {
let vm = this
let { files } = event.target
if (vm.disabled || !files.length) {
return
}
files = files.length === 1 ? files[0] : [].slice.call(files)
if (vm.beforeRead) {
const response = vm.beforeRead(toArray(files))
if (!response) {
vm.resetInput()
return
}
if (response.then) {
response
.then(() => {
vm.fileRead(files)
})
.catch(vm.resetInput)
}
}
vm.fileRead(files)
},
fileRead (files) {
const oversize = isOversize(files, this.maxSize)
if (oversize) {
files = files.filter(file => file.size <= this.maxSize)
}
if (Array.isArray(files)) {
const maxCount = this.maxCount - this.fileList.length
if (files.length > maxCount) {
files = files.slice(0, maxCount)
}
Promise.all(files.map(file => readFile(file, this.resultType))).then(contents => {
const fileList = files.map((file, index) => ({
file,
content: contents[index],
}))
this.onAfterRead(fileList, oversize)
})
} else {
readFile(files, this.resultType).then(content => {
this.onAfterRead({ file: files, content: content }, oversize)
})
}
},
onAfterRead (files, oversize) {
if (oversize) {
this.$emit('oversize', toArray(files))
// return
}
this.resetInput()
this.$emit('input', [...this.fileList, ...toArray(files)])
if (this.afterRead) {
this.afterRead(toArray(files))
}
if (this.upload) {
this.fileReadList = [...this.fileList, ...toArray(files)]
this.uploadFiles = Object.assign(this.uploadFiles, toArray(files))
this.onUploadFile()
}
},
onUploadFile () {
let vm = this
if (this.upload) {
if (vm.uploadConfig) {
let itemFile = vm.uploadFiles.pop()
let url = vm.uploadConfig.uploadUrl
let uploadParams = vm.uploadConfig.params
let uploadSuccess = vm.uploadConfig.uploadSuccess
let param = new FormData()
for (let key in uploadParams) {
param.append(key, uploadParams[key])
}
param.append('fileName', itemFile.file.name)
param.append('file', itemFile.file)
axios.post(url, param, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + window.localStorage.access_token,
},
}).then(function (result) {
/* vm.fileReadList.forEach((itemFile, index) => {
if (result.response && itemFile.file && !itemFile.attachment_id && result.response.fileName === itemFile.file.name) {
itemFile.attachment_id = result.response.attachment_id
}
}) */
if (uploadSuccess) {
uploadSuccess(itemFile, result)
}
if (vm.uploadFiles.length > 0) {
vm.onUploadFile()
} else {
vm.$emit('upload', vm.fileReadList)
vm.$emit('input', vm.fileReadList)
vm.fileReadList = []
}
})
}
}
},
deleteFile (file, index) {
const fileList = this.fileList.slice(0)
fileList.splice(index, 1)
this.$emit('input', fileList)
this.$emit('delete', file, index)
},
showBigPic (index) {
let vm = this
if (vm.previewImage) {
const imageFiles = vm.fileList
.filter(item => isImageDataUrl(item.content))
ShowPicturePlugin.show({
imgList: imageFiles,
startPosition: index,
})
}
},
},
}
</script>
<style lang="less">
.h-file {
display: flex;
flex-wrap: wrap;
.review-wrap {
display: flex;
flex-wrap: wrap;
.review-img {
position: relative;
display: block;
img {
width: 65px;
height: 65px;
margin: 0 10px 10px 0;
object-fit: cover
}
.icon {
position: absolute;
right: 10px;
bottom: 12px;
font-size: 16px;
width: 16px;
height: 16px;
display: flex;
color: #fff;
background-color: rgba(0, 0, 0, 0.45);
justify-content: center;
}
}
.file-upload {
position: relative;
display: inline-block;
//width: 75px;
//height: 75px;
.default-img {
width: 65px;
height: 65px;
margin: 0 10px 10px 0;
object-fit: cover
}
input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
cursor: pointer;
opacity: 0;
}
}
}
}
</style>
export function toArray (item) {
if (Array.isArray(item)) {
return item
}
return [item]
}
export function readFile (file, resultType) {
return new Promise(resolve => {
const reader = new FileReader()
reader.onload = event => {
resolve(event.target.result)
}
if (resultType === 'dataUrl') {
reader.readAsDataURL(file)
} else if (resultType === 'text') {
reader.readAsText(file)
}
})
}
export function isOversize (files, maxSize) {
return toArray(files).some(file => file.size > maxSize)
}
export function isImageDataUrl (dataUrl) {
return dataUrl.indexOf('data:image') === 0
}
......@@ -9,4 +9,12 @@ showBigPicture查看大图 插件形式调用
})
},
let list = [{imgUrl:'base64'},{imgUrl:'http://XXX'}]
showBigPicture() {
ShowPicturePlugin.show({
imgList: list,
startPosition: 1,
})
},
```
......@@ -8,13 +8,27 @@
v-hls-img-zoom
v-tap="hide"
v-longtap="sharePicture"
:src="imgUrl"
v-lazy="imgUrl"
v-if="!imgList.length"
:alt="imgTitle">
<swipe
v-if="imgList.length" :index.sync="startPosition" :auto="false" :show-indicators="false">
<swipe-item
v-for="(item,index) in imgList" :key="index">
<img
v-hls-img-zoom
v-tap="hide"
v-longtap="sharePicture"
v-lazy="item.imgUrl || item.content"
:alt="imgTitle">
</swipe-item>
</swipe>
</div>
<div v-if="imgList.length" class="indicators">{{ startPosition + 1 }}/{{ imgList.length }}</div>
</div>
</div>
</template>
<style lang="less" scoped>
<style lang="less">
.show-picture-backdrop {
-webkit-transition: background-color 150ms ease-in-out;
transition: background-color 150ms ease-in-out;
......@@ -25,12 +39,15 @@
width: 100%;
height: 100%;
background-color: transparent;
.weui-tab {
width: 100%;
}
&.active {
background-color: rgba(0, 0, 0, 0.6);
}
.show-picture-wrapper {
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
......@@ -38,7 +55,7 @@
transition: all cubic-bezier(0.36, 0.66, 0.04, 1) 500ms;
position: absolute;
width: 100%;
background-color: rgba(0,0,0,.5);
background-color: rgba(0, 0, 0, .5);
//max-width: 500px;
height: 100%;
margin: auto;
......@@ -48,54 +65,111 @@
-webkit-align-items: center;
justify-content: center;
-webkit-justify-content: center;
.pad {
//width: 100%;
//height: 100%;
display: -webkit-flex;
display: flex;
justify-content: center;
align-items: center;
-webkit-justify-content: center;
-webkit-align-items: center;
img {
width: 100%;
height: 100%;
display: -webkit-flex;
display: flex;
justify-content: center;
align-items: center;
-webkit-justify-content: center;
-webkit-align-items: center;
object-fit: cover;
}
}
}
.show-picture-up {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.hls-swipe {
.hls-swipe-track {
display: flex !important;
align-items: center !important;
.hls-swipe-item {
display: flex;
justify-content: center;
img {
width: auto;
height: auto;
}
}
}
}
.indicators{
top:20px;
position: absolute;
color: #fff;
font-size: 16px;
}
}
.modal-open [vum-show-picture] {
pointer-events: auto;
}
.platform-ios{
.show-picture-backdrop {
.indicators{
top:40px;
}
}
}
// iPhoneX适配
@media (device-width: 375px) and (device-height: 812px) and (-webkit-min-device-pixel-ratio: 3) {
.platform-ios {
.show-picture-backdrop {
.indicators{
top:60px;
}
}
}
}
// iPhoneX Max适配
@media (device-width: 414px) and (device-height: 896px) {
.platform-ios {
.show-picture-backdrop {
.indicators{
top:60px;
}
}
}
}
</style>
<script>
import assign from 'object-assign'
import SwipeItem from '../Swipe/SwipeItem'
import Swipe from '../Swipe/index'
export default {
component: {
'swipe': Swipe,
'swipe-item': SwipeItem,
},
data () {
return {
defaultOptions: {
imgUrl: '',
imgList: [],
startPosition: 0,
imgTitle: '',
index: '1',
width: '100',
},
imgUrl: '',
imgList: [],
startPosition: 0,
imgTitle: '',
index: '1',
state: 0,
width: '100',
}
......@@ -116,6 +190,8 @@ export default {
this.imgUrl = _options.imgUrl
this.imgTitle = _options.imgTitle
this.width = _options.width
this.imgList = _options.imgList
this.startPosition = _options.startPosition
this.state = 1
if (!this.width) {
this.width = 100
......@@ -136,7 +212,7 @@ export default {
},
sharePicture () {
// var reg = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/
window.plugins.socialsharing.share(null, null, this.imgUrl, null)
// window.plugins.socialsharing.share(null, null, this.imgUrl, null)
},
},
}
......
import VueLazyload from 'vue-lazyload'
import HView from './HView/index'
import HHeader from './HHeader/index'
import HContent from './HContent/index'
......@@ -20,12 +21,17 @@ import CheckBox from './CheckBox/index'
import RadioGroup from './Radio/index'
import Radio from './Radio/radio'
import HFile from './HFile/index'
import BottomTab from './BottomTab/index'
import TabButton from './BottomTab/tab-button'
import Modal from './Modal/Modal'
import errLoadingPic from '../common/picture/errloading.jpg'
export default (Vue) => {
Vue.use(VueLazyload, {error: errLoadingPic,
loading: errLoadingPic})
Vue.component('h-view', HView)
Vue.component('h-header', HHeader)
Vue.component('h-content', HContent)
......@@ -50,4 +56,5 @@ export default (Vue) => {
Vue.component('h-check', CheckBox)
Vue.component('radio-group', RadioGroup)
Vue.component('h-radio', Radio)
Vue.component('h-file', HFile)
}
......@@ -22,6 +22,7 @@ import TopTip from './components/TopTip/index'
import Stab from './components/STab/index'
import TabItem from './components/STab/tab-item'
import TabButton from './components/BottomTab/tab-button'
import HFile from './components/HFile/index'
import componentInstall from './components/component'
// compontenPlugins
......@@ -62,6 +63,7 @@ export {
TabItem,
BottomTab,
TabButton,
HFile,
ActionSheetPlugin,
ShowPicturePlugin,
SelectPlugin,
......
/**
* @Author think
* @Date 2019-07-11 11:04
*/
<template>
<h-view>
<h-content>
<h-file
:fileList="fileList" v-model="fileList" :upload="upload"
:uploadConfig="uploadConfig" :disabled="disable" :accept="accept" :beforeRead="beforeRead"
:afterRead="afterRead" :maxCount="maxCount" :max-size="maxSize" :previewImage="previewImage"
:deleteImage="deleteImage"
:result-type="resultType"
@upload="uploaded"
@delete="deletePic"
@oversize="oversize">
<div class="file-chose">
文件上传 <i class="icon ion-ios-cloud-upload"/>
</div>
</h-file>
</h-content>
</h-view>
</template>
<script>
export default {
name: 'FileTest',
data () {
return {
fileList: [{'file': '', content: 'xxx'}], // 文件数组 必填
disable: false, // 是否禁用文件上传 默认false 可选
accept: 'image/png,image/jpeg', // 接受的文件类型 可选
upload: false, // 选取完成是否自动上传 默认 false 可选
maxSize: 2000000, // 文件大小限制,单位为byte 可选 超过大小的文件会自动取消
maxCount: 5, // 文件上传数量限制 可选超过数量自动隐藏选择按钮
deleteImage: true, // 是否删除文件 默认true 可选
previewImage: true, // 是否在选取完成后展示预览图 默认true 可选
resultType: 'dataUrl', // 文件读取结果类型 默认base64 可选值为text
uploadConfig: { // 文件上传配置
uploadUrl: $config.rootPath + '/app/fileUploadSvc?sysName=HLS_APP&apiName=attachment_upload', // 上传的URL
params: { // 上传的额外参数
user_id: 1,
source_type: 'HLS_DOC_FILE_TEMPLET',
pkvalue: 42,
},
uploadSuccess: this.fileSuccess, // 上传成功后的回调函数 用于给文件添加服务端唯一标示或其他
},
}
},
methods: {
/**
* email事件 可选
* 删除文件后触发 返回当前文件以及 对应的index
* @param file
* @param index
*/
deletePic (file, index) {
debugger
},
/**
* 可选
* 文件选择完成后读取前触发 返回当前选择的文件数组
* 返回 ture 或者promise对象 标示继续读取,否则终止 操作
* @param files 选择的文件数组
* @returns {boolean}
*/
beforeRead (files) {
files.forEach((file, index) => {
console.log(file.size)
})
return true
},
/**
* 可选
* 文件读取成功后触发添加了文件的content内容
* @param files 选择的文件数组
* @returns {boolean}
*/
afterRead (files) {
files.forEach((item, index) => {
console.log(item.file.name)
})
return true
},
/**
* email事件 可选
* 全部文件上传成功后触发
* @param fileList 全部文件数组
*/
uploaded (fileList) {
debugger
},
/**
* email事件 可选
* 文件大小超过配置的大小
* @param files
*/
oversize (files) {
debugger
},
/**
* 每个文件上传成功后的回调 可选配置
* @param fileList 文件数组
* @param result 上传返回结果
*/
fileSuccess (fileList, result) {
this.fileList = fileList
debugger
this.fileList.forEach((itemFile, index) => {
if (result.response && itemFile.file && !itemFile.attachment_id && result.response.fileName === itemFile.file.name) {
itemFile.attachment_id = result.response.attachment_id
}
})
},
},
}
</script>
<style scoped lang="less">
.content{
.h-file{
.file-chose{
width: 110px;
height: 44px;
background-color: #07c160;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
border-radius: 4px;
.icon{
margin-left: 10px;
font-size: 20px;
//color: #fff;
}
}
}
}
</style>
......@@ -6,6 +6,7 @@ import Home from '@/pages/home'
// test工具类
import HlsPopup from '@/pages/hlsPopup'
import Radio from '@/pages/radioTest'
import HFile from '@/pages/fileTest'
Vue.use(Router)
......@@ -19,6 +20,7 @@ export default new Router({
// test工具类
{path: '/hls-popup', component: HlsPopup, name: 'HlsPopup', meta: {keepAlive: false}},
{path: '/Radio', component: Radio, name: 'Radio', meta: {keepAlive: true}},
{path: '/HFile', component: HFile, name: 'HFile', meta: {keepAlive: true}},
],
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
......
......@@ -8429,6 +8429,11 @@ vue-infinite-scroll@^2.0.2:
resolved "https://registry.yarnpkg.com/vue-infinite-scroll/-/vue-infinite-scroll-2.0.2.tgz#ca37a91fe92ee0ad3b74acf8682c00917144b711"
integrity sha512-n+YghR059YmciANGJh9SsNWRi1YZEBVlODtmnb/12zI+4R72QZSWd+EuZ5mW6auEo/yaJXgxzwsuhvALVnm73A==
vue-lazyload@1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/vue-lazyload/-/vue-lazyload-1.2.3.tgz#901f9ec15c7e6ca78781a2bae4a343686bdedb2c"
integrity sha512-DC0ZwxanbRhx79tlA3zY5OYJkH8FYp3WBAnAJbrcuoS8eye1P73rcgAZhyxFSPUluJUTelMB+i/+VkNU/qVm7g==
vue-loader@^13.3.0:
version "13.7.3"
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.7.3.tgz#e07440f78230a639d00ada4da7b96d0e9d62037f"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment