优化:文件选择组件

This commit is contained in:
jmal 2020-12-30 18:01:24 +08:00
parent 380b39ae78
commit 88ade6d799
6 changed files with 230 additions and 96 deletions

View File

@ -1,27 +1,17 @@
<template> <template>
<div> <div>
<el-dialog
title="选择头像"
:visible.sync="dialogSelectFile"
top="7.5vh"
width="70%"
:append-to-body="true"
>
<select-file></select-file>
</el-dialog>
<div class="container"> <div class="container">
<div class="img-container" v-loading="loading"> <div class="img-container" v-loading="loading">
<div class="img-region" v-show="avatar.length > 0 || fileImg.size"> <div class="img-region">
<img :src="sourceImg" ref="image" style="max-width: 100%;" alt=""> <img :src="sourceImg" ref="image" style="max-width: 100%;" alt="">
</div> </div>
<div class="button-region"> <div class="button-region">
<el-button v-if="fileImg.size" type="primary" @click="sureSava">保存头像</el-button> <el-button type="primary" @click="sureSava">保存头像</el-button>
<!-- <el-button @click="uploadImg">{{avatar.length === 0?'选择头像':'重新选择'}}</el-button>--> <el-button v-show="selectImg.id" type="primary" @click="noCropSave(selectImg.id)">不剪裁保存</el-button>
<el-button @click="selectImg">选择头像</el-button>
</div> </div>
</div> </div>
<el-divider v-show="avatar.length > 0 || fileImg.size" direction="vertical"></el-divider> <el-divider direction="vertical"></el-divider>
<div v-show="avatar.length > 0 || fileImg.size" class="avatar-preview"> <div class="avatar-preview">
<h3>头像预览</h3> <h3>头像预览</h3>
<div class="before big"></div> <div class="before big"></div>
<span>大头像100*100</span> <span>大头像100*100</span>
@ -35,19 +25,19 @@
<script> <script>
import "@/assets/css/cropper.css" import "@/assets/css/cropper.css"
import Cropper from 'cropperjs' import Cropper from 'cropperjs'
import SelectFile from "@/components/ShowFile/SelectFile"; import fileConfig from "@/utils/file-config";
export default { export default {
name: 'CropperDialog', name: 'CropperDialog',
components: {SelectFile}, components: {},
props: { props: {
fileImg: { fileImg: {
type: File, type: Blob,
default: null default: {}
}, },
dialogVisible: { selectImg: {
type: Boolean, type: Object,
default: false default: {}
}, },
avatar: { avatar: {
type: String, type: String,
@ -58,32 +48,18 @@
return { return {
loading: true, loading: true,
cropper: null, cropper: null,
sourceImg: '', sourceImg: ''
dialogSelectFile: false
} }
}, },
mounted() { mounted() {
this.$nextTick(()=>{ this.$nextTick(()=>{
this.initCropper() this.initCropper()
this.loading = false this.loading = false
this.changeDialog(this.dialogVisible)
this.changImg(this.fileImg) this.changImg(this.fileImg)
this.setSelectImg(this.selectImg)
}) })
}, },
watch: {
dialogVisible(val) {
this.changeDialog(val)
}
},
methods: { methods: {
changeDialog(value){
if(value){
if(this.avatar.length === 0){
// 没有任何头像,直接代开文件选择框
this.selectImg()
}
}
},
initCropper(){ initCropper(){
this.cropper = new Cropper(this.$refs.image, { this.cropper = new Cropper(this.$refs.image, {
responsive: true, responsive: true,
@ -113,20 +89,30 @@
// autoCropArea: 0.6, // autoCropArea: 0.6,
}); });
}, },
selectImg() { setSelectImg(selectImg) {
this.dialogSelectFile = true if(selectImg.id){
}, this.loading = true
changImg(file) { this.cropper.reset()
this.loading = true let url = fileConfig.previewUrl(this.$store.state.user.name, selectImg) + '?o=crop&w=1024'
const that = this this.sourceImg = url
let reader = new FileReader(); this.cropper.replace(url)
reader.readAsDataURL(file); this.loading = false
reader.onload = function(){
that.cropper.replace(reader.result)
that.loading = false
} }
}, },
sureSava(){ changImg(file) {
if(file.size > 0){
this.loading = true
this.cropper.reset()
const that = this
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(){
that.cropper.replace(reader.result)
that.loading = false
}
}
},
sureSava() {
let cro = this.cropper.getCroppedCanvas({ let cro = this.cropper.getCroppedCanvas({
maxWidth: 1204 , maxWidth: 1204 ,
maxHeight: 1024, maxHeight: 1024,
@ -138,6 +124,10 @@
cro.toBlob((blob)=>{ cro.toBlob((blob)=>{
this.$emit('croppedCanvas', dataURL, blob) this.$emit('croppedCanvas', dataURL, blob)
}) })
},
// 不剪裁保存
noCropSave(fileId) {
this.$emit('noCropSave', fileId)
} }
}, },
} }
@ -202,10 +192,4 @@
top: 0; top: 0;
margin-top: 0; margin-top: 0;
} }
>>>.el-dialog__body {
padding: 0;
}
>>>.el-dialog__header {
padding: 16px 20px 10px;
}
</style> </style>

View File

@ -1,16 +1,32 @@
<template> <template>
<div> <div>
<show-file <el-dialog
query-file-type="image" class="select-file-dialog"
:defaultGrid="true" :title="title"
:orderCustom="true" :visible.sync="dialogVisible"
:show-upload-button="false" top="7.5vh"
:show-search-button="false" width="70%"
:show-more-item="false" :append-to-body="true"
:show-share-item="false" @close="handleClose"
:less-client-height="lessClientHeight" >
:sortable="sortable"> <show-file
</show-file> select-file
query-file-type="image"
:defaultGrid="true"
:orderCustom="true"
:show-upload-button="false"
:show-search-button="false"
:show-more-item="false"
:show-share-item="false"
:less-client-height="lessClientHeight"
:sortable="sortable"
@selectedFile="selectedFile"
>
</show-file>
<div slot="footer" class="dialog-footer">
<el-button :disabled="!selectedFileRow.size" type="primary" @click="select">选择</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
@ -19,18 +35,64 @@ import ShowFile from "@/components/ShowFile/ShowFile";
export default { export default {
name: 'SelectFile', name: 'SelectFile',
components: {ShowFile}, props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择文件'
}
},
components: { ShowFile },
data() { data() {
return { return {
dialogVisible: false,
sortable: { sortable: {
prop: 'updateDate', order: 'descending' prop: 'updateDate', order: 'descending'
}, },
selectedFileRow: {},
lessClientHeight: document.body.clientHeight * 0.15 + 150 lessClientHeight: document.body.clientHeight * 0.15 + 150
} }
}, },
watch: {
visible: function(vlaue) {
if(vlaue){
this.dialogVisible = true
}
},
},
methods: {
selectedFile(row){
this.selectedFileRow = row
},
select(){
this.handleClose()
this.$emit("select", this.selectedFileRow)
},
handleClose() {
this.dialogVisible = false
this.$emit('update:visible', this.dialogVisible)
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.select-file-dialog {
>>>.el-dialog__body {
padding: 0;
}
>>>.el-dialog__header {
padding: 16px 20px 10px;
}
>>>.el-dialog__footer {
position: absolute;
width: 100%;
bottom: 0px;
padding: 0px 20px 20px;
background: linear-gradient(rgba(255,255,255,0), #fff);
}
}
</style> </style>

View File

@ -185,7 +185,7 @@
<template v-for="(item,index) in tableHead"> <template v-for="(item,index) in tableHead">
<!--索引--> <!--索引-->
<pl-table-column <pl-table-column
v-if="index === 0" v-if="index === 0 && !selectFile"
:key="index" :key="index"
:index="index" :index="index"
type="selection" type="selection"
@ -313,7 +313,7 @@
element-loading-text="文件加载中" element-loading-text="文件加载中"
element-loading-spinner="el-icon-loading" element-loading-spinner="el-icon-loading"
element-loading-background="#f6f7fa88"> element-loading-background="#f6f7fa88">
<div class="checkbox-group-header"> <div class="checkbox-group-header" v-show="!selectFile">
<div class="select-operation"> <div class="select-operation">
<van-checkbox class="grid-all-checkbox" @click="clickGridAllCheckBox" v-model="allChecked"> <van-checkbox class="grid-all-checkbox" @click="clickGridAllCheckBox" v-model="allChecked">
{{ selectRowData.length > 0 ? '已选择 ' + this.tableHead[2].label : "选择" }} {{ selectRowData.length > 0 ? '已选择 ' + this.tableHead[2].label : "选择" }}
@ -347,25 +347,32 @@
<div <div
class="grid-time van-grid-item__content van-grid-item__content--center van-grid-item__content--square" class="grid-time van-grid-item__content van-grid-item__content--center van-grid-item__content--square"
:style="{ :style="{
'background': queryFileType === 'image' ? selectRowData.includes(item)?'#caeaf991':'url('+imageUrl+item.id+')' : selectRowData.includes(item)?'#caeaf991':'', 'background': queryFileType === 'image' ? 'url('+imageUrl+item.id+')' : selectRowData.includes(item)?'#caeaf991':'',
'background-size': 'cover', 'background-size': 'cover',
'background-position': 'center', 'background-position': 'center',
'border': selectRowData.includes(item)?'solid 1px #7bd7ff':'', 'border': selectRowData.includes(item)?'solid 1px #409eff':'',
}" }"
@mouseover="gridItemHover(item,index)" @mouseover="gridItemHover(item,index)"
@mouseout="gridItemOut(item,index)" @mouseout="gridItemOut(item,index)"
@click="gridItemClick(item)" @click="gridItemClick(item)"
@contextmenu.prevent="rowContextmenu(item)" @contextmenu.prevent="rowContextmenu(item)"
> >
<van-checkbox v-if="gridHoverItemIndex === index || selectRowData.includes(item)" <van-checkbox v-show="!selectFile" v-if="gridHoverItemIndex === index || selectRowData.includes(item)"
class="grid-item-checkbox" :name="item" class="grid-item-checkbox" :name="item"
@click.stop="clickGridItemCheckBox(item,index)"/> @click.stop="clickGridItemCheckBox(item,index)"/>
<div class="grid-item-icon"> <div :class="{'grid-item-icon':true, 'grid-item-icon-image': queryFileType === 'image' ? gridHoverItemIndex === index || selectRowData.includes(item) : false}">
<icon-file :style="{'display': gridHoverItemIndex === index || selectRowData.includes(item) || queryFileType !== 'image' ? 'block': 'none'}" :item="item" :image-url="imageUrl" :audio-cover-url="audioCoverUrl" <icon-file v-show="queryFileType !== 'image'" :item="item" :image-url="imageUrl" :audio-cover-url="audioCoverUrl"
:grid="true"></icon-file> :grid="true"></icon-file>
</div> </div>
<!--<el-tooltip effect="light" :content="item.name" placement="top">--> <!--<el-tooltip effect="light" :content="item.name" placement="top">-->
<span :title="item.name" class="grid-item-text">{{ item.name }}</span> <!-- <div v-if="queryFileType === 'image'" :class="{'grid-item-text': true, 'grid-item-text-image': queryFileType === 'image'}">{{ item.name }}>-->
<!-- {{ item.w + ' x ' + item.h }}-->
<!-- </div>-->
<div
:title="item.name"
v-show="queryFileType === 'image' ? gridHoverItemIndex === index || selectRowData.includes(item) : true"
:class="{'grid-item-text': true, 'grid-item-text-image': queryFileType === 'image'}">{{ item.name }}
</div>
<!--</el-tooltip>--> <!--</el-tooltip>-->
</div> </div>
</van-grid-item> </van-grid-item>
@ -403,6 +410,9 @@
<el-form-item label="类型:" class="details-name"> <el-form-item label="类型:" class="details-name">
<span>{{ rowContextData.isFolder ? '文件夹' : rowContextData.contentType }}</span> <span>{{ rowContextData.isFolder ? '文件夹' : rowContextData.contentType }}</span>
</el-form-item> </el-form-item>
<el-form-item v-show="rowContextData.w && rowContextData.h" label="分辨率:" class="details-resolution">
<span>{{ rowContextData.w + ' x ' + rowContextData.h }}</span>
</el-form-item>
<el-form-item label="大小:"> <el-form-item label="大小:">
<span> {{ rowContextData.size }}字节 {{ rowContextData.size > 0 ? '(' + formatSize(rowContextData.size) + ')' : '' }}</span> <span> {{ rowContextData.size }}字节 {{ rowContextData.size > 0 ? '(' + formatSize(rowContextData.size) + ')' : '' }}</span>
</el-form-item> </el-form-item>
@ -555,6 +565,10 @@ export default {
FileTree FileTree
}, },
props: { props: {
selectFile: {
type: Boolean,
defalut: false
},
lessClientHeight: { lessClientHeight: {
type: Number, type: Number,
default: 136, default: 136,
@ -1006,10 +1020,9 @@ export default {
containerResize(ddd) { containerResize(ddd) {
const container = document.querySelector(".dashboard-container") const container = document.querySelector(".dashboard-container")
let clientWidth = container.clientWidth let clientWidth = container.clientWidth
console.log('this.lessClientHeight', this.lessClientHeight)
this.clientHeight = document.documentElement.clientHeight - this.lessClientHeight this.clientHeight = document.documentElement.clientHeight - this.lessClientHeight
if(clientWidth > 1024){ if(clientWidth > 1024){
this.gridColumnNum = this.queryFileType === 'image' ? Math.round(clientWidth / 100 - 7) : Math.round(clientWidth / 100 - 4) this.gridColumnNum = this.queryFileType === 'image' ? Math.round(clientWidth / 100 - 5) : Math.round(clientWidth / 100 - 4)
} else { } else {
this.gridColumnNum = this.queryFileType === 'image' ? Math.round(clientWidth / 100 - 3) : Math.round(clientWidth / 100 - 2) this.gridColumnNum = this.queryFileType === 'image' ? Math.round(clientWidth / 100 - 3) : Math.round(clientWidth / 100 - 2)
} }
@ -1027,6 +1040,9 @@ export default {
}, },
// 画矩形选区 // 画矩形选区
darwRectangle() { darwRectangle() {
if(this.selectFile){
return
}
const _this = this const _this = this
let $$ = function (id) { let $$ = function (id) {
return document.getElementById(id) return document.getElementById(id)
@ -1171,6 +1187,9 @@ export default {
}, },
// 行拖拽 // 行拖拽
rowDrop() { rowDrop() {
if(this.selectFile){
return
}
if (this.fileListScrollTop > 0 && this.$route.path !== '/') { if (this.fileListScrollTop > 0 && this.$route.path !== '/') {
return return
} }
@ -2078,13 +2097,16 @@ export default {
}, },
// 单元格点击事件 // 单元格点击事件
cellClick(row, column) { cellClick(row, column) {
if(this.selectFile){
this.fileClick(row)
return
}
clearTimeout(this.Loop); clearTimeout(this.Loop);
if (this.editingIndex === -1) { if (this.editingIndex === -1) {
const columnIndex = column.index const columnIndex = column.index
if (columnIndex === 2) { if (columnIndex === 2) {
if (this.selectRowData.length < 1) { if (this.selectRowData.length < 1) {
if (row.index !== this.editingIndex) { if (row.index !== this.editingIndex) {
// this.fileClick(row)
this.editingIndex = -1 this.editingIndex = -1
} }
} }
@ -2203,6 +2225,9 @@ export default {
}, },
// 鼠标右击 // 鼠标右击
rowContextmenu(row) { rowContextmenu(row) {
if(this.selectFile){
return
}
if (this.$refs.fileListTable.tableSelectData.length > 1 && this.$refs.fileListTable.tableSelectData.findIndex(item => item.index === row.index) > -1) { if (this.$refs.fileListTable.tableSelectData.length > 1 && this.$refs.fileListTable.tableSelectData.findIndex(item => item.index === row.index) > -1) {
this.menusIsMultiple = true this.menusIsMultiple = true
this.menus = this.multipleRightMenus this.menus = this.multipleRightMenus
@ -2847,6 +2872,20 @@ export default {
this.openDir(row) this.openDir(row)
} }
} else { } else {
if (this.selectFile){
let selectFile = row
const selectData = this.$refs.fileListTable.tableSelectData
if(selectData.length < 1 || selectData[0].id !== row.id){
this.$refs.fileListTable.clearSelection()
this.$refs.fileListTable.toggleRowSelection([{row: row}])
this.pinSelect(null, row)
} else {
this.$refs.fileListTable.clearSelection()
selectFile = {}
}
this.$emit("selectedFile", selectFile)
return
}
if (row.contentType.indexOf('image') > -1) { if (row.contentType.indexOf('image') > -1) {
// 图片 // 图片
this.imagePreviewVisible = true this.imagePreviewVisible = true

View File

@ -85,7 +85,7 @@
if(visible){ if(visible){
this.images = [] this.images = []
let viewIndex = 0 let viewIndex = 0
this.fileList.forEach(element => { this.fileList.forEach((element,index) => {
if(!element.isFolder && element.contentType.indexOf('image') > -1){ if(!element.isFolder && element.contentType.indexOf('image') > -1){
let url = fileConfig.previewUrl(this.$store.state.user.name, element) let url = fileConfig.previewUrl(this.$store.state.user.name, element)
if(this.shareId){ if(this.shareId){

View File

@ -293,6 +293,11 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
/deep/ .grid-item-text-image {
position: absolute;
top: 80%;
color: #ffffff;
}
/deep/ .grid-item-text:hover { /deep/ .grid-item-text:hover {
color: #19ACF9; color: #19ACF9;
} }
@ -312,9 +317,16 @@
width: 84px; width: 84px;
height: 84px; height: 84px;
} }
.grid-item-icon-image {
width: calc(100% + 16px);
height: 100%;
border-radius: 10px;
margin: 0;
backdrop-filter: contrast(0.5);
}
} }
/deep/ .grid-time:hover { /deep/ .grid-time:hover {
background: #caeaf991!important; border: solid 1px #409eff;
cursor: pointer; cursor: pointer;
} }
.checkbox-group-header { .checkbox-group-header {

View File

@ -1,7 +1,15 @@
<template> <template>
<div class="container" v-wechat-title="title"> <div class="container" v-wechat-title="title">
<el-dialog class="dialog-avatar" title="修改头像" :visible.sync="dialogAvatar"> <select-file title="选择头像" @select="selectedFile" :visible.sync="dialogSelectFile"></select-file>
<cropper-dialog ref="cropperDialog" :file-img="fileImg" :avatar="userInfo.avatar" :dialog-visible.sync="dialogAvatar" @croppedCanvas="saveAvatar"></cropper-dialog> <el-dialog class="dialog-avatar" title="修改头像" :visible.sync="dialogAvatar" @close="handleCloseDialogAvatar">
<cropper-dialog
ref="cropperDialog"
:file-img="fileImg"
:select-img="selectedImgRow"
:avatar="userInfo.avatar"
@noCropSave="saveNoCropAvatar"
@croppedCanvas="saveAvatar">
</cropper-dialog>
</el-dialog> </el-dialog>
<el-dialog class="dialog-cm" :title="passwordFormTitle" :visible.sync="dialogChangePassword"> <el-dialog class="dialog-cm" :title="passwordFormTitle" :visible.sync="dialogChangePassword">
<el-form ref="passwordForm" :model="passwordForm" :rules="rules" label-width="120px"> <el-form ref="passwordForm" :model="passwordForm" :rules="rules" label-width="120px">
@ -26,7 +34,8 @@
<el-avatar icon="el-icon-user-solid" class="avatar-value" shape="circle" :size="100" fit="fit" :src="srcImage" ></el-avatar> <el-avatar icon="el-icon-user-solid" class="avatar-value" shape="circle" :size="100" fit="fit" :src="srcImage" ></el-avatar>
</div> </div>
<div class="avatar-button"> <div class="avatar-button">
<el-button type="primary" icon="el-icon-upload2" circle @click="uploadImg"></el-button> <el-button title="选择文件" type="primary" icon="el-icon-folder" circle @click="selectImg"></el-button>
<el-button title="上传" type="primary" icon="el-icon-upload2" circle @click="uploadImg"></el-button>
<input ref="selectImg" type="file" style="display: none;" accept="image/*" @change="changImg"></input> <input ref="selectImg" type="file" style="display: none;" accept="image/*" @change="changImg"></input>
</div> </div>
<!-- <el-avatar class="avatar-overlay" shape="circle" :size="100" @click.native="dialogAvatar=true"> 修改头像 </el-avatar>--> <!-- <el-avatar class="avatar-overlay" shape="circle" :size="100" @click.native="dialogAvatar=true"> 修改头像 </el-avatar>-->
@ -60,11 +69,13 @@
import { getInfo, userUpdate, validOldPass, updatePass} from '@/api/user' import { getInfo, userUpdate, validOldPass, updatePass} from '@/api/user'
import { formatSize } from '@/utils/number' import { formatSize } from '@/utils/number'
import CropperDialog from '@/components/Cropper/dialog' import CropperDialog from '@/components/Cropper/dialog'
import SelectFile from "@/components/ShowFile/SelectFile"
import fileConfig from "@/utils/file-config";
export default { export default {
name: 'cusomerInfo', name: 'cusomerInfo',
components: { components: {
CropperDialog CropperDialog, SelectFile
}, },
props: { props: {
}, },
@ -102,7 +113,8 @@
checkPass: '', checkPass: '',
}, },
srcImage: '', srcImage: '',
fileImg: '', fileImg: new Blob(),
selectedImgRow: {},
rules: { rules: {
oldPassword: [ oldPassword: [
{ required: true, message: '请输入旧密码', trigger: 'blur' }, { required: true, message: '请输入旧密码', trigger: 'blur' },
@ -117,7 +129,8 @@
], ],
}, },
validOldPass: false, validOldPass: false,
passwordFormTitle: '旧密码' passwordFormTitle: '旧密码',
dialogSelectFile: false,
} }
}, },
computed: { computed: {
@ -129,19 +142,35 @@
uploadImg() { uploadImg() {
this.$refs.selectImg.click() this.$refs.selectImg.click()
}, },
selectImg() {
this.dialogSelectFile = true
},
selectedFile(row) {
this.dialogAvatar = true
this.selectedImgRow = row
if(this.$refs.cropperDialog){
this.$refs.cropperDialog.setSelectImg(this.selectedImgRow)
}
},
handleCloseDialogAvatar() {
this.$refs.selectImg.value = ''
},
changImg(e) { changImg(e) {
const files = e.target.files const files = e.target.files
console.log(files) if(files.length < 1){
if(files.length > 0 && files[0].size/1024/1024 > 2){ return
console.log('sdfsd') }
if(files[0].size/1024/1024 > 5){
this.$message({ this.$message({
message: '选择的图片不能超过2M', message: '选择的图片不能超过5M',
type: 'warning' type: 'warning'
}); });
return return
this.fileImg = files[0] }
this.dialogAvatar = true this.fileImg = files[0]
console.log(this.$refs.cropperDialog) this.dialogAvatar = true
if(this.$refs.cropperDialog){
this.$refs.cropperDialog.changImg(files[0])
} }
}, },
getInfo(){ getInfo(){
@ -240,6 +269,14 @@
data.append('blobAvatar',blob) data.append('blobAvatar',blob)
this.userUpdate(data,dataURL) this.userUpdate(data,dataURL)
}, },
// 保存不剪裁的头像
saveNoCropAvatar(fileId) {
this.dialogAvatar = false
let data = new FormData();
data.append('id', this.$store.state.user.userId)
data.append('avatar', fileId)
this.userUpdate(data)
},
// 修改用户信息操作 // 修改用户信息操作
userUpdate(data, dataURL){ userUpdate(data, dataURL){
this.userUpdateLoading = true this.userUpdateLoading = true
@ -250,7 +287,7 @@
type: 'success', type: 'success',
duration: 1000 duration: 1000
}) })
if(dataURL){ if(dataURL || res.data){
this.srcImage = this.imageUrl + res.data this.srcImage = this.imageUrl + res.data
this.$store.state.user.avatar = res.data this.$store.state.user.avatar = res.data
} }