718 lines
35 KiB
Vue
718 lines
35 KiB
Vue
<template>
|
||
<div id="media-library">
|
||
<div class="container-fluid">
|
||
<div class="row no-gutters">
|
||
<div class="close-window" @click="onCloseWindow"><i class="fa fa-close"></i></div>
|
||
<div class="col-md-2 col-categories" v-show="!isMobile || _d.onlyShowCategory">
|
||
<div class="row row-head no-gutters">
|
||
<div class="col">
|
||
<div class="controls">
|
||
<div v-if="_d.addingNewCategory">
|
||
<input type="text" class="form-control" v-model="_d.currentEditingCategoryText" @click.prevent.stop @keypress.enter="onAddCategory">
|
||
<button class="btn btn-sm btn-success" @click.prevent.stop="onAddCategory"><i class="fa fa-check"></i></button>
|
||
<button class="btn btn-sm btn-outline-secondary" @click.prevent.stop="onCancelCategoryAdding"><i class="fa fa-close"></i></button>
|
||
</div>
|
||
<div v-if="!_d.addingNewCategory">
|
||
<button class="btn btn-success btn-sm" @click="_d.addingNewCategory=true"><i class="fa fa-plus"></i></button>
|
||
<button class="btn btn-primary btn-sm" @click="onEditCategory(_d.currentCategory)"><i class="fa fa-edit"></i></button>
|
||
<button class="btn btn-danger btn-sm" @click="onDeleteCategory(_d.currentCategory)"><i class="fa fa-close"></i></button>
|
||
<button class="btn btn-outline-secondary btn-sm return" v-if="isMobile" @click="_d.onlyShowCategory=false"><i class="fa fa-arrow-right"></i></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row row-body no-gutters">
|
||
<div class="col">
|
||
<div class="category-list-wrapper">
|
||
<div class="message">
|
||
<div class="alert alert-success" v-if="_d.categorySuccessMessage">{{ _d.categorySuccessMessage }}</div>
|
||
<div class="alert alert-danger" v-if="_d.categoryErrorMessage">
|
||
{{ _d.categoryErrorMessage }}
|
||
<button type="button" class="close" aria-label="Close" @click="_d.categoryErrorMessage=null">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="spinner-border" role="status" v-if="_d.categoryFetching">
|
||
<span class="sr-only">Loading...</span>
|
||
</div>
|
||
<nav class="nav flex-column category-list" :class="{dragging: this._d.currentDraggingCategoryId}" ref="categoryList">
|
||
<a class="nav-link category"
|
||
href="#"
|
||
:draggable="!isUneditableCategory(category.id)"
|
||
:data-id="category.id"
|
||
:class="{current: _d.currentCategory == category.id, editing: isCategoryEditing(category.id)}"
|
||
@dragenter.capture.prevent.stop="onDragEnterCategory($event, category.id)"
|
||
@dragleave.capture.prevent.stop="onDragLeaveCategory($event)"
|
||
@drop="onDropOnCategory($event, category.id)"
|
||
@dragstart.stop="onDragCategory(category.id)"
|
||
@dragover.prevent.stop
|
||
@dragend.capture.prevent.stop="onDropCategory"
|
||
@click.prevent="switchCategory(category.id)"
|
||
v-for="category in _d.categories">
|
||
<span v-if="!isCategoryEditing(category.id)">
|
||
<span class="name">{{ category.name }}</span>
|
||
<span class="badge badge-secondary">{{ category.count }}</span>
|
||
<button class="btn btn-sm edit-category" v-if="!isUneditableCategory(category.id)" @click.prevent.stop="onEditCategory(category.id)">
|
||
<i class="fa fa-edit"></i>
|
||
</button>
|
||
<button class="btn btn-sm remove-category" v-if="!isUneditableCategory(category.id)" @click.prevent.stop="onDeleteCategory(category.id)">
|
||
<i class="fa fa-close"></i>
|
||
</button>
|
||
</span>
|
||
<span v-if="isCategoryEditing(category.id)">
|
||
<input type="text" class="form-control" v-model="_d.currentEditingCategoryText" @click.prevent.stop>
|
||
<button class="btn btn-sm btn-primary" @click.prevent.stop="onUpdateCategoryName()"><i class="fa fa-check"></i></button>
|
||
<button class="btn btn-sm btn-outline-secondary" @click.prevent.stop="cancelCategoryEditing"><i class="fa fa-close"></i></button>
|
||
</span>
|
||
</a>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row row-footer no-gutters">
|
||
<div class="col"></div>
|
||
</div>
|
||
</div>
|
||
<div class="col col-images" v-show="!isMobile || (isMobile && !_d.onlyShowCategory && !_d.onlyShowMediaInfo)">
|
||
<div class="row row-head no-gutters">
|
||
<div class="col">
|
||
<ul class="nav nav-tabs tabs">
|
||
<li class="nav-item" v-if="isMobile">
|
||
<a href="#" class="nav-link" @click="_d.onlyShowCategory=true">
|
||
{{ _d.text.tabCategory }}
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="#" @drag.prevent @click="_d.isUploadingPage=true"
|
||
v-bind:class="{active: _d.isUploadingPage}">{{ _d.text.tabUpload }}</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="#" @drag.prevent @click="_d.isUploadingPage=false"
|
||
v-bind:class="{active: !_d.isUploadingPage}">{{ _d.text.tabBrowse }}</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="row row-body no-gutters">
|
||
<div class="col">
|
||
<div class="alert alert-success image-message" v-if="_d.imageSuccessMessage">
|
||
{{ _d.imageSuccessMessage }}
|
||
</div>
|
||
<div class="alert alert-danger image-message" v-if="_d.imageErrorMessage">
|
||
{{ _d.imageErrorMessage }}
|
||
<button type="button" class="close" aria-label="Close" @click="_d.imageErrorMessage=null">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="tab-page upload" v-show="_d.isUploadingPage">
|
||
<form class="upload-zone" v-bind:action="_d.formAction" ref="uploadZone">
|
||
<input type="hidden" name="_token" v-bind:value="_d.csrfToken">
|
||
<div class="fallback">
|
||
<input name="media_file" type="file" multiple />
|
||
</div>
|
||
<div class="upload-tip"> {{ _d.text.dragOrClickToUpload }} </div>
|
||
</form>
|
||
</div>
|
||
<div class="tab-page browse" v-bind:class="{dragging : !isCurrentDraggingMediaIdsEmpty }" v-show="!_d.isUploadingPage" @scroll="onScrollBrowsing($event)">
|
||
<div v-for="media in _d.medias"
|
||
:key="media.id"
|
||
@dragend="onDropMedia"
|
||
@dragstart="onDragMedia(media.id)"
|
||
draggable="true"
|
||
class="img-wrapper"
|
||
v-bind:class="{ selected: isMediaSelected(media)}"
|
||
@click="onClickMedia($event, media)"
|
||
@click.ctrl="onCtrlClickMedia($event, media)">
|
||
<div class="img" :style="{ 'background-image': 'url(' + media.url + ')' }">
|
||
</div>
|
||
</div>
|
||
<div class="spinner-border" role="status" v-if="_d.fetchingLock">
|
||
<span class="sr-only">Loading...</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row row-footer no-gutters">
|
||
<div class="col"></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-2 col-media-info" v-show="!isMobile || _d.onlyShowMediaInfo">
|
||
<div class="row row-head" v-if="isMobile">
|
||
<div class="col">
|
||
<button class="btn btn-outline-secondary btn-sm return" @click="_d.onlyShowMediaInfo=false"><i class="fa fa-arrow-left"></i></button>
|
||
</div>
|
||
</div>
|
||
<div class="row row-body no-gutters">
|
||
<div class="col">
|
||
<div class="media-info-wrapper" :class="{'select-mode': _d.selectMode}">
|
||
<div class="message">
|
||
<div class="alert alert-danger" v-if="_d.infoErrorMessage">
|
||
{{ _d.infoErrorMessage }}
|
||
<button type="button" class="close" aria-label="Close" @click="_d.infoErrorMessage=null">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="alert alert-success" v-if="_d.infoSuccessMessage">{{ _d.infoSuccessMessage }}</div>
|
||
</div>
|
||
<div class="info" :class="{hidden: !_d.currentSelectedMedia}">
|
||
<div class="url">
|
||
<span class="label font-weight-bold">{{ _d.text.url }} :</span>
|
||
<div class="content">
|
||
<a v-bind:href="_d.currentSelectedMedia.url" target="_blank">{{ _d.currentSelectedMedia.url }}</a>
|
||
</div>
|
||
</div>
|
||
<div class="size">
|
||
<span class="label font-weight-bold">{{ _d.text.fileSize }}:</span>
|
||
<span class="content">{{ fileSize }}</span>
|
||
</div>
|
||
<div class="date">
|
||
<span class="label font-weight-bold">{{ _d.text.date }}:</span>
|
||
<span class="content">{{ _d.currentSelectedMedia.date }}</span>
|
||
</div>
|
||
<div class="dimension">
|
||
<span class="label font-weight-bold">{{ _d.text.size }}:</span>
|
||
<span class="content">{{ dimension }}</span>
|
||
</div>
|
||
<div class="category">
|
||
<span class="label font-weight-bold">{{ _d.text.category }}:</span>
|
||
<select name="" id="" v-model="_d.currentSelectedMedia.category.id" v-if="_d.currentSelectedMedia.category">
|
||
<option :value="category.id" v-for="category in this.mediaSettableCategories">{{ category.name }}</option>
|
||
</select>
|
||
<button class="btn btn-success btn-sm" @click.prevent="onUpdateMediaCategory()" v-if="_d.currentSelectedMedia">{{ _d.text.update }}</button>
|
||
</div>
|
||
<div class="description">
|
||
<span class="label font-weight-bold">{{ _d.text.description }}</span>
|
||
<div class="content">
|
||
<textarea class="form-control" v-model="_d.currentSelectedMedia.description"></textarea>
|
||
</div>
|
||
<button class="btn btn-success btn-sm" @click.prevent="onUpdateMediaDescription($event)">{{ _d.text.update }}</button>
|
||
<button class="btn btn-outline-danger btn-sm" @click.prevent="onDeleteMedia($event)">{{ _d.text.delete }}</button>
|
||
</div>
|
||
<div class="img">
|
||
<img v-bind:src="_d.currentSelectedMedia.url" alt="">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row row-footer no-gutters">
|
||
<div class="col">
|
||
<div class="select-medias" v-if="_d.selectMode">
|
||
<button class="btn btn-primary" @click="onDoneSelectedMedias" :disabled="isSelectedMediasEmpty">{{ _d.text.select }}</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: "media-library",
|
||
props: ['_d'],
|
||
computed: {
|
||
isMobile() {
|
||
let isMobile = this._d.windowWidth < 768;
|
||
return isMobile;
|
||
},
|
||
mediaSettableCategories() {
|
||
return this._d.categories.filter(category => !this.isAllCategory(category.id));
|
||
},
|
||
// 是否為單選模式
|
||
isSingleSelectionMode() {
|
||
return this._d.single;
|
||
},
|
||
isSelectedMediasEmpty() {
|
||
return this._d.selectedMedias.length ? false : true;
|
||
},
|
||
selectedMediaIds() {
|
||
return this._d.selectedMedias.map(_media => _media.id);
|
||
},
|
||
isCurrentDraggingMediaIdsEmpty() {
|
||
return this._d.currentDraggingMediaIds ? false : true;
|
||
},
|
||
dimension() {
|
||
if(this._d.currentSelectedMedia.width !== null && this._d.currentSelectedMedia.height !== null) {
|
||
return this._d.currentSelectedMedia.width + 'x' + this._d.currentSelectedMedia.height;
|
||
} else {
|
||
return '';
|
||
}
|
||
},
|
||
fileSize() {
|
||
if(this._d.currentSelectedMedia.sizeInBytes) {
|
||
let size = this._d.currentSelectedMedia.sizeInBytes;
|
||
|
||
if(size < 1024)
|
||
return size + ' Bytes';
|
||
|
||
let unit = 0;
|
||
while(size >= 1024) {
|
||
unit++;
|
||
size /= 1024;
|
||
}
|
||
size = parseFloat(size).toFixed(2)
|
||
|
||
switch(unit) {
|
||
case 1:
|
||
size += ' KB';
|
||
break;
|
||
case 2:
|
||
size += ' MB';
|
||
break;
|
||
case 3:
|
||
size += ' GB';
|
||
break;
|
||
}
|
||
return size;
|
||
}
|
||
return '';
|
||
}
|
||
},
|
||
methods: {
|
||
// 設定已選擇的媒體
|
||
setSelectedMedias(selectedMedias) {
|
||
this._d.selectedMedias = selectedMedias;
|
||
},
|
||
// 清空被選擇的媒體
|
||
clearSelectedMedia() {
|
||
this.setSelectedMedias([]);
|
||
},
|
||
// 加入媒體至被選擇的媒體中
|
||
addToSelectedMedia(media) {
|
||
this._d.selectedMedias.push(media)
|
||
},
|
||
// 媒體是否已被選擇(by ID)
|
||
isMediaIdSelected(id) {
|
||
let foundMedias = this._d.selectedMedias.find(_media => _media.id == id);
|
||
return foundMedias ? true : false;
|
||
},
|
||
// 媒體是否已被選擇
|
||
isMediaSelected(media) {
|
||
return this.isMediaIdSelected(media.id);
|
||
},
|
||
// 從被選擇的媒體中刪除媒體
|
||
deleteFromSelectedMedia(media) {
|
||
this.setSelectedMedias(this._d.selectedMedias.filter(_media => _media.id != media.id));
|
||
},
|
||
// 從被選擇的媒體中刪除或新增媒體
|
||
addOrDeleteSelectedMedia(media) {
|
||
if(this.isMediaSelected(media)) {
|
||
this.unselectMedia(media);
|
||
} else {
|
||
this.addToSelectedMedia(media);
|
||
}
|
||
},
|
||
// 設定單一個媒體至被選擇的媒體
|
||
setSingleSelectedMedia(media) {
|
||
this.clearSelectedMedia();
|
||
this.addToSelectedMedia(media);
|
||
this.setCurrentSelectedMedia(media);
|
||
},
|
||
// 反選擇媒體
|
||
unselectMedia(media) {
|
||
this.deleteFromSelectedMedia(media);
|
||
this.clearCurrentSelectedMedia();
|
||
},
|
||
// 清除目前點選的媒體
|
||
clearCurrentSelectedMedia() {
|
||
this._d.currentSelectedMedia = false
|
||
},
|
||
// 設定目前點選的媒體
|
||
setCurrentSelectedMedia(media) {
|
||
this._d.currentSelectedMedia = media;
|
||
if(this.isMobile) {
|
||
this._d.onlyShowMediaInfo = true;
|
||
}
|
||
},
|
||
// 取得媒體資料
|
||
findMedia(id) {
|
||
return this._d.medias.find(media => media.id == id);
|
||
},
|
||
// 分類ID為"所有"
|
||
isAllCategory(id) {
|
||
return id == 'all';
|
||
},
|
||
// 分類ID為"未分類"
|
||
isUncategorizedCategory(id) {
|
||
return id == 'uncategorized';
|
||
},
|
||
// 無法編輯的分類
|
||
isUneditableCategory(id) {
|
||
return this.isUncategorizedCategory(id) || this.isAllCategory(id)
|
||
},
|
||
// 特定ID的分類正在編輯
|
||
isCategoryEditing(id) {
|
||
return this._d.currentEditingCategory == id;
|
||
},
|
||
// 取得分類資料
|
||
findCategory(id) {
|
||
return this._d.categories.find(category => category.id == id);
|
||
},
|
||
// 取得分類名稱
|
||
getCategoryName(id) {
|
||
let category = this.findCategory(id);
|
||
return category ? category.name : null;
|
||
},
|
||
// 刪除媒體
|
||
removeMediaFromList(mediaId) {
|
||
this._d.medias = this._d.medias.filter(media => media.id != mediaId);
|
||
if(this._d.currentSelectedMedia.id == mediaId) {
|
||
this.clearCurrentSelectedMedia();
|
||
}
|
||
},
|
||
// click事件,選擇媒體
|
||
onClickMedia(e, media, fromMulti = false) {
|
||
if(e.ctrlKey && !fromMulti) {
|
||
return;
|
||
}
|
||
if(
|
||
(this.isSingleSelectionMode && this.isMediaSelected(media)) ||
|
||
(!this.isSingleSelectionMode && this.isMediaSelected(media) && this._d.selectedMedias.length == 1)
|
||
) {
|
||
this.unselectMedia(media);
|
||
} else {
|
||
this.setSingleSelectedMedia(media);
|
||
}
|
||
},
|
||
// click事件,多選媒體
|
||
onCtrlClickMedia(e, media) {
|
||
this.setCurrentSelectedMedia(media);
|
||
if(this.isSingleSelectionMode) {
|
||
this.onClickMedia(e, media, true);
|
||
} else {
|
||
this.addOrDeleteSelectedMedia(media);
|
||
}
|
||
},
|
||
// click事件,回傳選擇的媒體
|
||
onDoneSelectedMedias() {
|
||
let data = [], idString = '';
|
||
this._d.selectedMedias.forEach(_media => {
|
||
data.push({
|
||
id: _media.id,
|
||
url: _media.url
|
||
})
|
||
})
|
||
idString = data.map(media => media.id).join(',')
|
||
|
||
this.$root.$emit('media_selected', {
|
||
idString: idString,
|
||
medias: data
|
||
})
|
||
this.onCloseWindow();
|
||
},
|
||
// click事件,關閉視窗
|
||
onCloseWindow() {
|
||
this.$root.$emit('close');
|
||
},
|
||
onUpdateMediaCategory() {
|
||
this.$root.$emit('update_category_for_medias', this._d.currentSelectedMedia.category.id, this._d.currentSelectedMedia.id, false);
|
||
},
|
||
// click事件,更新媒體描述
|
||
onUpdateMediaDescription(e) {
|
||
this.$root.$emit('update_media_description', e, this._d.currentSelectedMedia)
|
||
},
|
||
// click事件,刪除媒體
|
||
onDeleteMedia(e) {
|
||
this.$root.$emit('delete_media', e, this._d.currentSelectedMedia)
|
||
},
|
||
// 更新媒體的分類名稱
|
||
updateCategoryForMedia(mediaId, categoryId) {
|
||
let media = this._d.medias.find(media => media.id == mediaId);
|
||
if(media) {
|
||
let category = this.findCategory(categoryId);
|
||
if(category) {
|
||
media.category.name = category.name;
|
||
media.category.id = categoryId;
|
||
}
|
||
}
|
||
},
|
||
// 設定目前正在拖曳的媒體ID
|
||
setCurrentDraggingMediaIds(ids) {
|
||
this._d.currentDraggingMediaIds = ids;
|
||
},
|
||
// 清除目前正在拖曳的媒體ID
|
||
clearCurrentDraggingMediaIds() {
|
||
this._d.currentDraggingMediaIds = null;
|
||
},
|
||
// click事件,刪除所有選擇的媒體
|
||
deleteSelectedMedias() {
|
||
//TODO 批次刪除媒體
|
||
},
|
||
// scroll事件,載入更多媒體
|
||
onScrollBrowsing(e) {
|
||
if(this._d.fetchingEnd) return;
|
||
let ele = e.target;
|
||
if(ele.scrollTop >= ele.scrollHeight - ele.clientHeight - 10) {
|
||
if(!this._d.fetchingLock) {
|
||
this.$root.$emit('fetch_medias', 14);
|
||
}
|
||
}
|
||
},
|
||
clearInfoMessages() {
|
||
this._d.infoSuccessMessage = '';
|
||
this._d.infoErrorMessage = '';
|
||
},
|
||
showInfoSuccessMessage(message) {
|
||
this._d.infoSuccessMessage = message;
|
||
setTimeout(() => {
|
||
this._d.infoSuccessMessage = '';
|
||
}, 5000);
|
||
},
|
||
showInfoErrorMessage(message) {
|
||
this._d.infoErrorMessage = message;
|
||
},
|
||
clearCategoryMessages() {
|
||
this._d.categorySuccessMessage = '';
|
||
this._d.categoryErrorMessage = '';
|
||
},
|
||
showCategorySuccessMessage(message) {
|
||
this._d.categorySuccessMessage = message;
|
||
setTimeout(() => {
|
||
this._d.categorySuccessMessage = '';
|
||
}, 1000);
|
||
},
|
||
showCategoryErrorMessage(message) {
|
||
this._d.categoryErrorMessage = message;
|
||
},
|
||
clearImageMessages() {
|
||
this._d.imageSuccessMessage = '';
|
||
this._d.imageErrorMessage = '';
|
||
},
|
||
showImageSuccessMessage(message) {
|
||
this._d.imageSuccessMessage = message;
|
||
setTimeout(() => {
|
||
this._d.imageSuccessMessage = '';
|
||
}, 1000);
|
||
},
|
||
showImageErrorMessage(message) {
|
||
this._d.imageErrorMessage = message;
|
||
},
|
||
// drag事件,拖曳媒體
|
||
onDragMedia(id) {
|
||
if(!this.isSelectedMediasEmpty && this.isMediaIdSelected(id)) {
|
||
this.setCurrentDraggingMediaIds(this.selectedMediaIds);
|
||
} else {
|
||
this.setCurrentDraggingMediaIds(id);
|
||
}
|
||
},
|
||
// dragenter事件,拖曳媒體或分類至分類上
|
||
onDragEnterCategory(e, id) {
|
||
if(this._d.currentDraggingCategoryId) {
|
||
if(this.isUneditableCategory(id)) return;
|
||
if(!this.isCurrentDraggingMediaIdsEmpty) return;
|
||
|
||
let element = this.findCategoryElementById(id);
|
||
this._d.currentDraggingOverCategoryId = id;
|
||
if(this.isCategoryDragSourceBefore(this._d.currentDraggingCategoryId, id)) {
|
||
this.insertAfter(this._d.categoryDraggingIndicator, element)
|
||
} else {
|
||
this.insertBefore(this._d.categoryDraggingIndicator, element)
|
||
}
|
||
} else if(!this.isCurrentDraggingMediaIdsEmpty) {
|
||
if(!this.isAllCategory(id) && this._d.currentCategory != id) {
|
||
e.target.classList.add('on-drag');
|
||
}
|
||
}
|
||
},
|
||
// dragleave事件,拖曳媒體出分類
|
||
onDragLeaveCategory(e) {
|
||
e.target.classList.remove('on-drag');
|
||
},
|
||
// dragend事件,放開拖曳媒體
|
||
onDropMedia() {
|
||
this.clearCurrentDraggingMediaIds();
|
||
},
|
||
// drop事件,拖曳媒體並drop至分類上
|
||
onDropOnCategory(e, id) {
|
||
e.target.classList.remove('on-drag');
|
||
if(!this.isAllCategory(id) && this._d.currentCategory != id && !this.isCurrentDraggingMediaIdsEmpty) {
|
||
this.$root.$emit('update_category_for_medias', id, this._d.currentDraggingMediaIds);
|
||
}
|
||
},
|
||
// 切換分類
|
||
switchCategory(id) {
|
||
if(this._d.currentEditingCategory) {
|
||
this.cancelCategoryEditing();
|
||
}
|
||
|
||
if(!this._d.fetchingLock && !this._d.currentEditingCategory) {
|
||
this.clearSelectedMedia();
|
||
this.clearCurrentSelectedMedia();
|
||
this._d.medias = [];
|
||
this._d.currentCategory = id;
|
||
this._d.lastFetchedMediaId = null;
|
||
this._d.fetchingEnd = false;
|
||
this.$root.$emit('fetch_medias', 35);
|
||
this.$root.$emit('remove_all_uploaded_files');
|
||
}
|
||
},
|
||
// 減少分類的媒體數量
|
||
substractCategoryCount(id, number, substractFromAll = true) {
|
||
let category = this.findCategory(id);
|
||
if(category) {
|
||
category.count -= number;
|
||
}
|
||
if(substractFromAll) {
|
||
let category = this.findCategory('all');
|
||
if(category) {
|
||
category.count -= number;
|
||
}
|
||
}
|
||
},
|
||
// 增加分類的媒體數量
|
||
addCategoryCount(id, number, addToAll = true) {
|
||
let category = this.findCategory(id);
|
||
if(category) {
|
||
category.count += number;
|
||
}
|
||
if(addToAll) {
|
||
let category = this.findCategory('all');
|
||
if(category) {
|
||
category.count += number;
|
||
}
|
||
}
|
||
},
|
||
// click事件,編輯分類
|
||
onEditCategory(id) {
|
||
if(!this.isUneditableCategory(id)) {
|
||
this._d.currentEditingCategory = id;
|
||
this._d.currentEditingCategoryText = this.getCategoryName(id);
|
||
}
|
||
},
|
||
// 從列表刪除分類
|
||
deleteCategoryFromList(id) {
|
||
this._d.categories = this._d.categories.filter(category => category.id != id);
|
||
if(id == this._d.currentCategory) {
|
||
this._d.medias = [];
|
||
this._d.currentCategory = null;
|
||
}
|
||
},
|
||
// click事件,刪除分類
|
||
onDeleteCategory(id) {
|
||
if(!this.isUneditableCategory(id) && id) {
|
||
if(confirm(this._d.text.deleteConfirmation)) {
|
||
this.$root.$emit('delete_category', id);
|
||
}
|
||
}
|
||
},
|
||
// 取消分類編輯
|
||
cancelCategoryEditing() {
|
||
this._d.currentEditingCategory = null;
|
||
this._d.currentEditingCategoryText = null;
|
||
},
|
||
// click事件,更新分類名稱
|
||
onUpdateCategoryName() {
|
||
this.$root.$emit('update_category_name', this._d.currentEditingCategory, this._d.currentEditingCategoryText)
|
||
},
|
||
// 更新分類名稱
|
||
updateListCategoryName(id, name) {
|
||
let category = this.findCategory(id);
|
||
if(category) {
|
||
category.name = name;
|
||
}
|
||
},
|
||
// click事件,取消新增分類
|
||
onCancelCategoryAdding() {
|
||
this._d.addingNewCategory = false;
|
||
},
|
||
// click事件,新增分類
|
||
onAddCategory() {
|
||
this.$root.$emit('add_category', this._d.currentEditingCategoryText);
|
||
},
|
||
// 新增分類至列表開頭
|
||
addCategoryToList(id, name) {
|
||
let _categories = [];
|
||
_categories = _categories.concat(this._d.categories.filter(category => this.isUneditableCategory(category.id)))
|
||
_categories.push({
|
||
id: id,
|
||
name: name,
|
||
count: 0,
|
||
})
|
||
_categories = _categories.concat(this._d.categories.filter(category => !this.isUneditableCategory(category.id)))
|
||
this._d.categories = _categories;
|
||
},
|
||
// 從分類列表中找尋分類的element
|
||
findCategoryElementById(id) {
|
||
let element = document.querySelector('.category[data-id="' + id + '"]');
|
||
return element;
|
||
},
|
||
// insert after
|
||
insertAfter(newNode, referenceNode) {
|
||
referenceNode.parentNode.appendChild(newNode);
|
||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
|
||
},
|
||
// insert before
|
||
insertBefore(newNode, referenceNode) {
|
||
referenceNode.parentNode.appendChild(newNode);
|
||
referenceNode.parentNode.insertBefore(newNode, referenceNode);
|
||
},
|
||
// 拖曳的分類比目的分類還前面
|
||
isCategoryDragSourceBefore(sourceId, targetId) {
|
||
let dragSourceIndex = 0, dragTargetIndex = 0;
|
||
this._d.categories.forEach((category, index) => {
|
||
if(category.id == sourceId) {
|
||
dragSourceIndex = index;
|
||
}
|
||
if(category.id == targetId) {
|
||
dragTargetIndex = index;
|
||
}
|
||
});
|
||
return dragSourceIndex < dragTargetIndex;
|
||
},
|
||
// dragstart事件,拖曳分類
|
||
onDragCategory(id) {
|
||
this._d.currentDraggingCategoryId = id;
|
||
|
||
let indicatorElement = document.createElement('span')
|
||
indicatorElement.className = 'dragging-indicator';
|
||
this._d.categoryDraggingIndicator = indicatorElement;
|
||
},
|
||
// 交換分類的順序
|
||
switchCategoryOrder(draggingSourceId, draggingTargetId) {
|
||
if(draggingSourceId == draggingTargetId) return;
|
||
let _categories = [];
|
||
let category = this.findCategory(draggingSourceId);
|
||
let insertBefore = this.isCategoryDragSourceBefore(draggingSourceId, draggingTargetId);
|
||
|
||
if(!insertBefore) {
|
||
this._d.categories.reverse();
|
||
}
|
||
|
||
this._d.categories.forEach((_category, index) => {
|
||
if(_category.id != category.id) {
|
||
_categories.push(_category)
|
||
}
|
||
if(_category.id == draggingTargetId) {
|
||
_categories.push(category);
|
||
}
|
||
})
|
||
|
||
if(!insertBefore) {
|
||
_categories.reverse();
|
||
}
|
||
this._d.categories = _categories
|
||
this.$root.$emit('update_category_order',
|
||
_categories
|
||
.filter(category => !this.isUneditableCategory(category.id))
|
||
.map(category => category.id)
|
||
.join(',')
|
||
);
|
||
},
|
||
// dragend事件,拖曳分類並放置分類上
|
||
onDropCategory() {
|
||
let indicator = document.querySelector('.dragging-indicator')
|
||
indicator.parentNode.removeChild(indicator);
|
||
this.switchCategoryOrder(this._d.currentDraggingCategoryId, this._d.currentDraggingOverCategoryId);
|
||
this._d.currentDraggingOverCategoryId = null;
|
||
this._d.currentDraggingCategoryId = null;
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
</style>
|