PicList/src/renderer/pages/Gallery.vue

566 lines
15 KiB
Vue
Raw Normal View History

2017-12-06 22:26:29 -05:00
<template>
<div id="gallery-view">
<div class="view-title">
{{ $T('GALLERY') }} - {{ filterList.length }} <i class="el-icon-caret-bottom" @click="toggleHandleBar" :class="{'active': handleBarActive}"></i>
2017-12-06 22:26:29 -05:00
</div>
<transition name="el-zoom-in-top">
<el-row v-show="handleBarActive">
<el-col :span="20" :offset="2">
<el-row class="handle-bar" :gutter="16">
<el-col :span="12">
<el-select
v-model="choosedPicBed"
multiple
collapse-tags
size="mini"
style="width: 100%"
:placeholder="$T('CHOOSE_SHOWED_PICBED')">
<el-option
2018-12-23 10:15:00 -05:00
v-for="item in picBed"
:key="item.type"
:label="item.name"
:value="item.type">
</el-option>
</el-select>
</el-col>
<el-col :span="12">
<el-select
v-model="pasteStyle"
size="mini"
style="width: 100%"
@change="handlePasteStyleChange"
:placeholder="$T('CHOOSE_PASTE_FORMAT')">
<el-option
v-for="(value, key) in pasteStyleMap"
:key="key"
:label="key"
:value="value">
</el-option>
</el-select>
</el-col>
</el-row>
<el-row class="handle-bar" :gutter="16">
<el-col :span="12">
<el-input
:placeholder="$T('SEARCH')"
size="mini"
v-model="searchText">
2018-07-23 10:24:18 -04:00
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
</el-input>
</el-col>
<el-col :span="4">
<div class="item-base copy round" :class="{ active: isMultiple(choosedList)}" @click="multiCopy">
{{ $T('COPY') }}
</div>
</el-col>
<el-col :span="4">
2019-01-11 08:13:16 -05:00
<div class="item-base delete round" :class="{ active: isMultiple(choosedList)}" @click="multiRemove">
{{ $T('DELETE') }}
</div>
</el-col>
<el-col :span="4">
<div class="item-base all-pick round" :class="{ active: filterList.length > 0}" @click="toggleSelectAll">
{{ isAllSelected ? $T('CANCEL') : $T('SELECT_ALL') }}
</div>
</el-col>
</el-row>
</el-col>
</el-row>
</transition>
<el-row class="gallery-list" :class="{ small: handleBarActive }">
2017-12-06 22:26:29 -05:00
<el-col :span="20" :offset="2">
<el-row :gutter="16">
<gallerys
2021-08-01 02:50:25 -04:00
:images="filterList"
2017-12-06 22:26:29 -05:00
:index="idx"
@close="handleClose"
:options="options"
></gallerys>
2021-08-01 02:50:25 -04:00
<el-col :span="6" v-for="(item, index) in filterList" :key="item.id" class="gallery-list__img">
2019-12-19 06:17:21 -05:00
<div
2017-12-06 22:26:29 -05:00
class="gallery-list__item"
@click="zoomImage(index)"
>
<img v-lazy="item.imgUrl" class="gallery-list__item-img">
2017-12-06 22:26:29 -05:00
</div>
<div class="gallery-list__tool-panel">
2019-01-11 04:22:33 -05:00
<i class="el-icon-document" @click="copy(item)"></i>
2018-04-08 11:43:34 -04:00
<i class="el-icon-edit-outline" @click="openDialog(item)"></i>
2017-12-06 22:26:29 -05:00
<i class="el-icon-delete" @click="remove(item.id)"></i>
<el-checkbox v-model="choosedList[item.id ? item.id : '']" class="pull-right" @change="(val) => handleChooseImage(val, index)"></el-checkbox>
2019-12-19 06:17:21 -05:00
</div>
2017-12-06 22:26:29 -05:00
</el-col>
</el-row>
</el-col>
</el-row>
2018-04-08 11:43:34 -04:00
<el-dialog
:visible.sync="dialogVisible"
:title="$T('CHANGE_IMAGE_URL')"
2018-04-08 11:43:34 -04:00
width="500px"
:modal-append-to-body="false"
>
<el-input v-model="imgInfo.imgUrl"></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{ $T('CANCEL') }}</el-button>
<el-button type="primary" @click="confirmModify">{{ $T('CONFIRM') }}</el-button>
2018-04-08 11:43:34 -04:00
</span>
</el-dialog>
2017-12-06 22:26:29 -05:00
</div>
</template>
2019-12-19 06:17:21 -05:00
<script lang="ts">
// @ts-ignore
2017-12-06 22:26:29 -05:00
import gallerys from 'vue-gallery'
import { Component, Vue, Watch } from 'vue-property-decorator'
import { IResult } from '@picgo/store/dist/types'
import { PASTE_TEXT } from '#/events/constants'
2019-12-19 06:17:21 -05:00
import {
ipcRenderer,
clipboard,
IpcRendererEvent
} from 'electron'
@Component({
2017-12-06 22:26:29 -05:00
name: 'gallery',
components: {
gallerys
2019-12-19 06:17:21 -05:00
}
})
export default class extends Vue {
images: ImgInfo[] = []
idx: null | number = null
options = {
titleProperty: 'fileName',
urlProperty: 'imgUrl',
closeOnSlideClick: true
}
2019-12-19 06:17:21 -05:00
dialogVisible = false
imgInfo = {
id: '',
2019-12-19 06:17:21 -05:00
imgUrl: ''
}
choosedList: IObjT<boolean> = {}
2019-12-19 06:17:21 -05:00
choosedPicBed: string[] = []
lastChoosed: number = -1
isShiftKeyPress: boolean = false
2019-12-19 06:17:21 -05:00
searchText = ''
handleBarActive = false
pasteStyle = ''
pasteStyleMap = {
Markdown: 'markdown',
HTML: 'HTML',
URL: 'URL',
UBB: 'UBB',
Custom: 'Custom'
}
picBed: IPicBedType[] = []
@Watch('$route')
handleRouteUpdate (to: any, from: any) {
console.log(to, from)
if (from.name === 'gallery') {
this.clearChoosedList()
}
if (to.name === 'gallery') {
this.updateGallery()
}
2019-12-19 06:17:21 -05:00
}
2021-07-26 12:15:11 -04:00
async created () {
ipcRenderer.on('updateGallery', () => {
2021-07-26 12:15:11 -04:00
this.$nextTick(async () => {
2021-08-01 02:50:25 -04:00
this.updateGallery()
2018-08-08 02:39:40 -04:00
})
})
2019-12-19 06:17:21 -05:00
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
2021-08-01 02:50:25 -04:00
this.updateGallery()
2019-12-19 06:17:21 -05:00
}
mounted () {
document.addEventListener('keydown', this.handleDetectShiftKey)
document.addEventListener('keyup', this.handleDetectShiftKey)
}
handleDetectShiftKey (event: KeyboardEvent) {
if (event.key === 'Shift') {
if (event.type === 'keydown') {
this.isShiftKeyPress = true
} else if (event.type === 'keyup') {
this.isShiftKeyPress = false
}
}
}
2019-12-19 06:17:21 -05:00
get filterList () {
return this.getGallery()
}
get isAllSelected () {
const values = Object.values(this.choosedList)
if (values.length === 0) {
return false
} else {
return this.filterList.every(item => {
return this.choosedList[item.id!]
})
}
}
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
2019-12-19 06:17:21 -05:00
this.picBed = picBeds
}
2021-07-26 12:15:11 -04:00
getGallery (): ImgInfo[] {
if (this.searchText || this.choosedPicBed.length > 0) {
return this.images
.filter(item => {
let isInChoosedPicBed = true
2021-07-26 23:58:24 -04:00
let isIncludesSearchText = true
2021-07-26 12:15:11 -04:00
if (this.choosedPicBed.length > 0) {
isInChoosedPicBed = this.choosedPicBed.some(type => type === item.type)
}
2021-07-26 23:58:24 -04:00
if (this.searchText) {
isIncludesSearchText = item.fileName?.includes(this.searchText) || false
}
return isIncludesSearchText && isInChoosedPicBed
2021-07-26 12:15:11 -04:00
})
2019-12-19 06:17:21 -05:00
} else {
2021-07-26 12:15:11 -04:00
return this.images
2019-12-19 06:17:21 -05:00
}
}
2021-08-01 02:50:25 -04:00
async updateGallery () {
this.images = (await this.$$db.get({ orderBy: 'desc' })).data
}
2021-07-26 12:15:11 -04:00
@Watch('filterList')
handleFilterListChange () {
this.clearChoosedList()
}
handleChooseImage (val: boolean, index: number) {
if (val === true) {
this.handleBarActive = true
if (this.lastChoosed !== -1 && this.isShiftKeyPress) {
const min = Math.min(this.lastChoosed, index)
const max = Math.max(this.lastChoosed, index)
for (let i = min + 1; i < max; i++) {
const id = this.filterList[i].id!
this.$set(this.choosedList, id, true)
}
}
this.lastChoosed = index
}
}
clearChoosedList () {
this.isShiftKeyPress = false
Object.keys(this.choosedList).forEach(key => {
this.choosedList[key] = false
})
this.lastChoosed = -1
}
2019-12-19 06:17:21 -05:00
zoomImage (index: number) {
this.idx = index
this.changeZIndexForGallery(true)
}
2019-12-19 06:17:21 -05:00
changeZIndexForGallery (isOpen: boolean) {
if (isOpen) {
// @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 101
} else {
// @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 10
}
}
2019-12-19 06:17:21 -05:00
handleClose () {
this.idx = null
this.changeZIndexForGallery(false)
}
async copy (item: ImgInfo) {
const copyLink = await ipcRenderer.invoke(PASTE_TEXT, item)
2019-12-19 06:17:21 -05:00
const obj = {
title: this.$T('COPY_LINK_SUCCEED'),
2019-12-19 06:17:21 -05:00
body: copyLink,
icon: item.url || item.imgUrl
}
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
}
remove (id?: string) {
if (id) {
this.$confirm(this.$T('TIPS_REMOVE_LINK'), this.$T('TIPS_NOTICE'), {
confirmButtonText: this.$T('CONFIRM'),
cancelButtonText: this.$T('CANCEL'),
type: 'warning'
}).then(async () => {
const file = await this.$$db.getById(id)
await this.$$db.removeById(id)
ipcRenderer.send('removeFiles', [file])
const obj = {
title: this.$T('OPERATION_SUCCEED'),
body: ''
}
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
this.updateGallery()
}).catch((e) => {
console.log(e)
2018-07-24 11:15:19 -04:00
return true
})
}
2019-12-19 06:17:21 -05:00
}
2019-12-19 06:17:21 -05:00
openDialog (item: ImgInfo) {
this.imgInfo.id = item.id!
2019-12-19 06:17:21 -05:00
this.imgInfo.imgUrl = item.imgUrl as string
this.dialogVisible = true
}
async confirmModify () {
await this.$$db.updateById(this.imgInfo.id, {
imgUrl: this.imgInfo.imgUrl
})
2019-12-19 06:17:21 -05:00
const obj = {
title: this.$T('CHANGE_IMAGE_URL_SUCCEED'),
2019-12-19 06:17:21 -05:00
body: this.imgInfo.imgUrl,
icon: this.imgInfo.imgUrl
}
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
this.dialogVisible = false
2021-08-01 02:50:25 -04:00
this.updateGallery()
2019-12-19 06:17:21 -05:00
}
2019-12-19 06:17:21 -05:00
choosePicBed (type: string) {
const idx = this.choosedPicBed.indexOf(type)
2019-12-19 06:17:21 -05:00
if (idx !== -1) {
this.choosedPicBed.splice(idx, 1)
} else {
this.choosedPicBed.push(type)
}
}
2019-12-19 06:17:21 -05:00
cleanSearch () {
this.searchText = ''
}
isMultiple (obj: IObj) {
2019-12-19 06:17:21 -05:00
return Object.values(obj).some(item => item)
}
toggleSelectAll () {
const result = !this.isAllSelected
this.filterList.forEach(item => {
this.$set(this.choosedList, item.id!, result)
})
}
2019-12-19 06:17:21 -05:00
multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
const multiRemoveNumber = Object.values(this.choosedList).filter(item => item).length
if (multiRemoveNumber) {
this.$confirm(this.$T('TIPS_WILL_REMOVE_CHOOSED_IMAGES', {
m: multiRemoveNumber
}), this.$T('TIPS_NOTICE'), {
confirmButtonText: this.$T('CONFIRM'),
cancelButtonText: this.$T('CANCEL'),
2017-12-06 22:26:29 -05:00
type: 'warning'
}).then(async () => {
const files: IResult<ImgInfo>[] = []
const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i]
2019-12-19 06:17:21 -05:00
if (this.choosedList[key]) {
const file = await this.$$db.getById<ImgInfo>(key)
if (file) {
files.push(file)
await this.$$db.removeById(key)
}
2019-12-19 06:17:21 -05:00
}
}
this.clearChoosedList()
this.choosedList = {} // 只有删除才能将这个置空
2017-12-06 22:26:29 -05:00
const obj = {
title: this.$T('OPERATION_SUCCEED'),
body: ''
2017-12-06 22:26:29 -05:00
}
2019-12-19 06:17:21 -05:00
ipcRenderer.send('removeFiles', files)
const myNotification = new Notification(obj.title, obj)
2017-12-06 22:26:29 -05:00
myNotification.onclick = () => {
return true
}
2021-08-01 02:50:25 -04:00
this.updateGallery()
2017-12-06 22:26:29 -05:00
}).catch(() => {
return true
})
2019-12-19 06:17:21 -05:00
}
}
async multiCopy () {
2019-12-19 06:17:21 -05:00
if (Object.values(this.choosedList).some(item => item)) {
const copyString: string[] = []
2019-12-19 06:17:21 -05:00
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i]
2019-12-19 06:17:21 -05:00
if (this.choosedList[key]) {
const item = await this.$$db.getById<ImgInfo>(key)
if (item) {
const txt = await ipcRenderer.invoke(PASTE_TEXT, item)
copyString.push(txt)
this.choosedList[key] = false
}
2019-12-19 06:17:21 -05:00
}
}
2018-04-08 11:43:34 -04:00
const obj = {
title: this.$T('BATCH_COPY_LINK_SUCCEED'),
body: copyString.join('\n')
2018-04-08 11:43:34 -04:00
}
2019-12-19 06:17:21 -05:00
const myNotification = new Notification(obj.title, obj)
clipboard.writeText(copyString.join('\n'))
2018-04-08 11:43:34 -04:00
myNotification.onclick = () => {
return true
}
2017-12-06 22:26:29 -05:00
}
2019-12-19 06:17:21 -05:00
}
2019-12-19 06:17:21 -05:00
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
}
// getPasteStyle () {
// this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
// }
async handlePasteStyleChange (val: string) {
this.saveConfig('settings.pasteStyle', val)
2019-12-19 06:17:21 -05:00
this.pasteStyle = val
}
beforeDestroy () {
2019-12-19 06:17:21 -05:00
ipcRenderer.removeAllListeners('updateGallery')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
2017-12-06 22:26:29 -05:00
}
}
</script>
<style lang='stylus'>
.view-title
color #eee
font-size 20px
text-align center
2018-04-28 04:22:35 -04:00
margin 10px auto
.sub-title
font-size 14px
.el-icon-caret-bottom
cursor: pointer
transition all .2s ease-in-out
&.active
transform: rotate(180deg)
2019-01-23 22:29:48 -05:00
#gallery-view
height 100%
2018-06-19 01:45:07 -04:00
.item-base
background #2E2E2E
text-align center
2018-06-20 08:45:12 -04:00
padding 5px 0
2018-06-19 01:45:07 -04:00
cursor pointer
font-size 13px
transition all .2s ease-in-out
height: 28px
box-sizing: border-box
2018-07-23 10:24:18 -04:00
&.copy
cursor not-allowed
background #49B1F5
&.active
cursor pointer
background #1B9EF3
color #fff
2018-06-19 01:45:07 -04:00
&.delete
cursor not-allowed
2018-06-20 08:45:12 -04:00
background #F47466
2018-07-23 10:24:18 -04:00
&.active
cursor pointer
background #F15140
color #fff
&.all-pick
cursor not-allowed
background #69C282
&.active
cursor pointer
background #44B363
color #fff
2017-12-06 22:26:29 -05:00
#gallery-view
position relative
.round
border-radius 14px
2018-06-19 01:45:07 -04:00
.pull-right
float right
2017-12-06 22:26:29 -05:00
.gallery-list
height 360px
box-sizing border-box
padding 8px 0
overflow-y auto
overflow-x hidden
position absolute
top: 38px
transition all .2s ease-in-out .1s
2018-07-06 11:54:36 -04:00
width 100%
&.small
height: 287px
top: 113px
2018-06-19 01:45:07 -04:00
&__img
2017-12-06 22:26:29 -05:00
height 150px
position relative
margin-bottom 16px
&__item
width 100%
height 120px
transition all .2s ease-in-out
cursor pointer
margin-bottom 8px
overflow hidden
display flex
2017-12-06 22:26:29 -05:00
&-fake
position absolute
top 0
2019-12-19 06:17:21 -05:00
left 0
2017-12-06 22:26:29 -05:00
opacity 0
width 100%
z-index -1
&:hover
transform scale(1.1)
&-img
width 100%
object-fit fill
2017-12-06 22:26:29 -05:00
&__tool-panel
color #ddd
i
cursor pointer
transition all .2s ease-in-out
&.el-icon-document
&:hover
color #49B1F5
2018-04-08 11:43:34 -04:00
&.el-icon-edit-outline
&:hover
color #69C282
2017-12-06 22:26:29 -05:00
&.el-icon-delete
&:hover
color #F15140
2018-06-19 01:45:07 -04:00
.handle-bar
color #ddd
margin-bottom 10px
2018-06-20 08:45:12 -04:00
.el-input__inner
border-radius 14px
2019-12-19 06:17:21 -05:00
</style>