separate SharedItem component

This commit is contained in:
Weidi Deng 2020-12-13 16:21:13 +08:00
parent 621093e87d
commit ed8adedc37
6 changed files with 177 additions and 101 deletions

View File

@ -0,0 +1,112 @@
<template>
<div class="item"
role="button"
tabindex="0"
@click="click"
@dblclick="dblclick"
@touchstart="touchstart"
:data-dir="isDir"
:aria-label="name"
:aria-selected="isSelected">
<div>
<i class="material-icons">{{ icon }}</i>
</div>
<div>
<p class="name">{{ name }}</p>
<p v-if="isDir" class="size" data-order="-1">&mdash;</p>
<p v-else class="size" :data-order="humanSize()">{{ humanSize() }}</p>
<p class="modified">
<time :datetime="modified">{{ humanTime() }}</time>
</p>
</div>
</div>
</template>
<script>
import { mapMutations, mapGetters, mapState } from 'vuex'
import filesize from 'filesize'
import moment from 'moment'
export default {
name: 'sharedItem',
data: function () {
return {
touches: 0
}
},
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
computed: {
...mapState(['shared']),
...mapGetters(['sharedSelectedCount']),
isSelected () {
return (this.shared.selected.indexOf(this.index) !== -1)
},
icon () {
if (this.isDir) return 'folder'
if (this.type === 'image') return 'insert_photo'
if (this.type === 'audio') return 'volume_up'
if (this.type === 'video') return 'movie'
return 'insert_drive_file'
}
},
methods: {
...mapMutations(['addSharedSelected', 'removeSharedSelected', 'resetSharedSelected']),
humanSize: function () {
return filesize(this.size)
},
humanTime: function () {
return moment(this.modified).fromNow()
},
click: function (event) {
if (this.sharedSelectedCount !== 0) event.preventDefault()
if (this.$store.state.shared.selected.indexOf(this.index) !== -1) {
this.removeSharedSelected(this.index)
return
}
if (event.shiftKey && this.shared.selected.length > 0) {
let fi = 0
let la = 0
if (this.index > this.shared.selected[0]) {
fi = this.shared.selected[0] + 1
la = this.index
} else {
fi = this.index
la = this.shared.selected[0] - 1
}
for (; fi <= la; fi++) {
if (this.$store.state.shared.selected.indexOf(fi) == -1) {
this.addSharedSelected(fi)
}
}
return
}
if (!event.ctrlKey && !event.metaKey && !this.$store.state.shared.multiple) this.resetSharedSelected()
this.addSharedSelected(this.index)
},
dblclick: function () {
this.open()
},
touchstart () {
setTimeout(() => {
this.touches = 0
}, 300)
this.touches++
if (this.touches > 1) {
this.open()
}
},
open: function () {
this.$router.push({path: this.url})
}
}
}
</script>

View File

@ -63,5 +63,9 @@
} }
.share__box__items #listing.list .item .name { .share__box__items #listing.list .item .name {
width: auto; width: 50%;
}
.share__box__items #listing.list .item .modified {
width: 25%;
} }

View File

@ -12,7 +12,8 @@ const getters = {
let sum = state.upload.progress.reduce((acc, val) => acc + val) let sum = state.upload.progress.reduce((acc, val) => acc + val)
return Math.ceil(sum / state.upload.size * 100); return Math.ceil(sum / state.upload.size * 100);
} },
sharedSelectedCount: state => state.shared.selected.length
} }
export default getters export default getters

View File

@ -24,7 +24,11 @@ const state = {
showShell: false, showShell: false,
showMessage: null, showMessage: null,
showConfirm: null, showConfirm: null,
previewMode: false previewMode: false,
shared: {
selected: [],
multiple: false
}
} }
export default new Vuex.Store({ export default new Vuex.Store({

View File

@ -86,7 +86,17 @@ const mutations = {
}, },
setPreviewMode(state, value) { setPreviewMode(state, value) {
state.previewMode = value state.previewMode = value
} },
addSharedSelected: (state, value) => (state.shared.selected.push(value)),
removeSharedSelected: (state, value) => {
let i = state.shared.selected.indexOf(value)
if (i === -1) return
state.shared.selected.splice(i, 1)
},
resetSharedSelected: (state) => {
state.shared.selected = []
},
sharedMultiple: (state, value) => (state.shared.multiple = value)
} }
export default mutations export default mutations

View File

@ -13,10 +13,10 @@
<div class="share"> <div class="share">
<div class="share__box share__box__info"> <div class="share__box share__box__info">
<div class="share__box__header"> <div class="share__box__header">
{{ file.isDir ? hasSelected ? $t('download.downloadSelected') : $t('download.downloadFolder') : $t('download.downloadFile') }} {{ file.isDir ? sharedSelectedCount > 0 ? $t('download.downloadSelected') : $t('download.downloadFolder') : $t('download.downloadFile') }}
</div> </div>
<div class="share__box__element share__box__center share__box__icon"> <div class="share__box__element share__box__center share__box__icon">
<i class="material-icons">{{ file.isDir ? 'folder' : 'insert_drive_file'}}</i> <i class="material-icons">{{ icon }}</i>
</div> </div>
<div class="share__box__element"> <div class="share__box__element">
<strong>{{ $t('prompts.displayName') }}</strong> {{ file.name }} <strong>{{ $t('prompts.displayName') }}</strong> {{ file.name }}
@ -35,7 +35,7 @@
</div> </div>
<div v-if="file.isDir" class="share__box__element share__box__center"> <div v-if="file.isDir" class="share__box__element share__box__center">
<label> <label>
<input type="checkbox" v-model="multiple"> <input type="checkbox" :checked="shared.multiple" @click="toggleMultipleSelection">
{{ $t('buttons.selectMultiple') }} {{ $t('buttons.selectMultiple') }}
</label> </label>
</div> </div>
@ -45,19 +45,16 @@
{{ $t('files.files') }} {{ $t('files.files') }}
</div> </div>
<div id="listing" class="list"> <div id="listing" class="list">
<div class="item" v-for="(item) in file.items.slice(0, this.showLimit)" :key="base64(item.name)" <shared-item v-for="(item) in file.items.slice(0, this.showLimit)"
:aria-selected="selected.includes(item.name)" :key="base64(item.name)"
@click="click(item.name)" v-bind:index="item.index"
@dblclick="dblclick(item.name)" v-bind:name="item.name"
@touchstart="touchstart(item.name)" v-bind:isDir="item.isDir"
> v-bind:url="item.url"
<div> v-bind:modified="item.modified"
<i class="material-icons">{{ item.isDir ? 'folder' : (item.type==='image') ? 'insert_photo' : 'insert_drive_file' }}</i> v-bind:type="item.type"
</div> v-bind:size="item.size">
<div> </shared-item>
<p class="name">{{ item.name }}</p>
</div>
</div>
<div v-if="file.items.length > showLimit" class="item"> <div v-if="file.items.length > showLimit" class="item">
<div> <div>
<p class="name"> + {{ file.items.length - showLimit }} </p> <p class="name"> + {{ file.items.length - showLimit }} </p>
@ -70,26 +67,25 @@
</template> </template>
<script> <script>
import {mapState, mapMutations, mapGetters} from 'vuex';
import { share as api } from '@/api' import { share as api } from '@/api'
import { baseURL } from '@/utils/constants' import { baseURL } from '@/utils/constants'
import filesize from 'filesize' import filesize from 'filesize'
import moment from 'moment' import moment from 'moment'
import QrcodeVue from 'qrcode.vue' import QrcodeVue from 'qrcode.vue'
import SharedItem from "@/components/files/SharedItem"
export default { export default {
name: 'share', name: 'share',
components: { components: {
SharedItem,
QrcodeVue QrcodeVue
}, },
data: () => ({ data: () => ({
loaded: false, loaded: false,
notFound: false, notFound: false,
file: null, file: null,
showLimit: 500, showLimit: 500
multiple: false,
touches: 0,
selected: [],
firstSelected: -1
}), }),
watch: { watch: {
'$route': 'fetchData' '$route': 'fetchData'
@ -104,8 +100,14 @@ export default {
window.removeEventListener('keydown', this.keyEvent) window.removeEventListener('keydown', this.keyEvent)
}, },
computed: { computed: {
hasSelected: function () { ...mapState(['shared']),
return this.selected.length > 0 ...mapGetters(['sharedSelectedCount']),
icon: function () {
if (this.file.isDir) return 'folder'
if (this.file.type === 'image') return 'insert_photo'
if (this.file.type === 'audio') return 'volume_up'
if (this.file.type === 'video') return 'movie'
return 'insert_drive_file'
}, },
hash: function () { hash: function () {
return this.$route.params.pathMatch.split('/')[0] return this.$route.params.pathMatch.split('/')[0]
@ -131,11 +133,11 @@ export default {
return absoluteParts.slice(absoluteParts.length - len).join('/') return absoluteParts.slice(absoluteParts.length - len).join('/')
}, },
link: function () { link: function () {
if (!this.hasSelected) return `${baseURL}/api/public/dl/${this.hash}/${this.path}` if (this.sharedSelectedCount === 0) return `${baseURL}/api/public/dl/${this.hash}/${this.path}`
if (this.selected.length === 1) return `${baseURL}/api/public/dl/${this.hash}/${this.path}/${encodeURIComponent(this.selected[0])}` if (this.sharedSelectedCount === 1) return `${baseURL}/api/public/dl/${this.hash}/${this.path}/${encodeURIComponent(this.file.items[this.shared.selected[0]].name)}`
let files = [] let files = []
for (let s of this.selected) { for (let s of this.shared.selected) {
files.push(encodeURIComponent(s)) files.push(encodeURIComponent(this.file.items[s].name))
} }
return `${baseURL}/api/public/dl/${this.hash}/${this.path}/?files=${encodeURIComponent(files.join(','))}` return `${baseURL}/api/public/dl/${this.hash}/${this.path}/?files=${encodeURIComponent(files.join(','))}`
}, },
@ -187,96 +189,39 @@ export default {
} }
}, },
methods: { methods: {
...mapMutations([ 'resetSharedSelected' ]),
base64: function (name) { base64: function (name) {
return window.btoa(unescape(encodeURIComponent(name))) return window.btoa(unescape(encodeURIComponent(name)))
}, },
fetchData: async function () { fetchData: async function () {
this.loaded = false this.loaded = false
this.notFound = false this.notFound = false
this.multiple = false this.$store.commit('resetSharedSelected')
this.touches = 0 this.$store.commit('sharedMultiple', false)
this.selected = []
this.firstSelected = -1
try { try {
this.file = await api.getHash(encodeURIComponent(this.$route.params.pathMatch)) this.file = await api.getHash(encodeURIComponent(this.$route.params.pathMatch))
this.file.items = this.file.items.map((item, index) => {
item.index = index
item.url = `/share/${this.hash}/${this.path}/${encodeURIComponent(item.name)}`
return item
})
this.loaded = true this.loaded = true
} catch (e) { } catch (e) {
this.notFound = true this.notFound = true
} }
}, },
fileItemsIndexOf: function (name) {
return this.file.items.indexOf(this.file.items.filter(item => item.name === name)[0])
},
addSelected: function(name) {
this.selected.push(name)
},
removeSelected: function (name) {
let i = this.selected.indexOf(name)
if (i === -1) return
this.selected.splice(i, 1)
if (i === 0 && this.hasSelected) {
this.firstSelected = this.fileItemsIndexOf(this.selected[0])
}
},
resetSelected: function () {
this.selected = []
this.firstSelected = -1
},
click: function (name) {
if (this.hasSelected) event.preventDefault()
if (this.selected.indexOf(name) !== -1) {
this.removeSelected(name)
return
}
let index = this.fileItemsIndexOf(name)
if (event.shiftKey && this.hasSelected) {
let fi = 0
let la = 0
if (index > this.firstSelected) {
fi = this.firstSelected + 1
la = index
} else {
fi = index
la = this.firstSelected - 1
}
for (; fi <= la; fi++) {
if (this.selected.indexOf(this.file.items[fi].name) === -1) {
this.addSelected(this.file.items[fi].name)
}
}
return
}
if (!event.ctrlKey && !event.metaKey && !this.multiple) this.resetSelected()
if (this.firstSelected === -1) this.firstSelected = index
this.addSelected(name)
},
dblclick: function (name) {
this.$router.push({path: `/share/${this.hash}/${this.path}/${encodeURIComponent(name)}`})
},
touchstart (name) {
setTimeout(() => {
this.touches = 0
}, 300)
this.touches++
if (this.touches > 1) {
this.dblclick(name)
}
},
keyEvent (event) { keyEvent (event) {
// Esc! // Esc!
if (event.keyCode === 27) { if (event.keyCode === 27) {
// If we're on a listing, unselect all // If we're on a listing, unselect all
// files and folders. // files and folders.
if (this.hasSelected) { if (this.sharedSelectedCount > 0) {
this.resetSelected() this.resetSharedSelected()
} }
} }
},
toggleMultipleSelection () {
this.$store.commit('sharedMultiple', !this.shared.multiple)
} }
} }
} }