diff --git a/src/views/Series/ContentSeriesNew.vue b/src/views/Series/ContentSeriesNew.vue index edb2bc3..5b87648 100644 --- a/src/views/Series/ContentSeriesNew.vue +++ b/src/views/Series/ContentSeriesNew.vue @@ -1,9 +1,481 @@ <template> <div> - 새로운 시리즈 + <v-toolbar dark> + <v-spacer /> + <v-toolbar-title>새로운 시리즈</v-toolbar-title> + <v-spacer /> + </v-toolbar> + + <br> + + <v-dialog + v-model="show_write_dialog" + max-width="1000px" + persistent + > + <template v-slot:activator="{ on, attrs }"> + <v-container> + <v-row> + <v-col cols="9" /> + <v-spacer /> + <v-col> + <v-btn + block + color="#3BB9F1" + dark + depressed + v-bind="attrs" + v-on="on" + > + 새로운 시리즈 등록 + </v-btn> + </v-col> + </v-row> + <draggable + v-model="recommend_series_list" + class="row" + @end="onDropCallback(recommend_series_list)" + > + <v-col + v-for="(item, i) in recommend_series_list" + :key="i" + cols="3" + > + <v-card> + <v-card-title> + <v-spacer /> + <v-img :src="item.imageUrl" /> + <v-spacer /> + </v-card-title> + <v-card-actions> + <v-spacer /> + <v-btn + text + @click="showModifyRecommendSeriesDialog(item)" + > + 수정 + </v-btn> + <v-btn + text + @click="deleteConfirm(item)" + > + 삭제 + </v-btn> + <v-spacer /> + </v-card-actions> + </v-card> + </v-col> + </draggable> + </v-container> + </template> + + <v-card> + <v-card-title v-if="is_modify === true"> + 새로운 시리즈 수정 + </v-card-title> + <v-card-title v-else> + 새로운 시리즈 등록 + </v-card-title> + + <v-card-text> + <v-row align="center"> + <v-col cols="4"> + 시리즈 + </v-col> + <v-col cols="8"> + <v-combobox + v-model="recommend_series.series_title" + :items="series" + :loading="is_loading" + :search-input.sync="search_query_series" + label="시리즈를 검색하세요" + item-text="name" + item-value="value" + no-data-text="No results found" + hide-selected + clearable + @change="onSelectSeries" + @update:search-input="onSearchSeriesUpdate" + /> + </v-col> + </v-row> + </v-card-text> + + <div class="image-select"> + <label for="image"> + 새로운 시리즈 이미지 등록 + </label> + <v-file-input + id="image" + v-model="recommend_series.image" + @change="imageAdd" + /> + </div> + <img + v-if="recommend_series.image_url" + :src="recommend_series.image_url" + alt="" + class="image-preview" + > + <v-card-actions v-show="!is_loading"> + <v-spacer /> + <v-btn + color="blue darken-1" + text + @click="cancel" + > + 취소 + </v-btn> + <v-btn + v-if="is_modify === true" + color="blue darken-1" + text + @click="modify" + > + 수정 + </v-btn> + <v-btn + v-else + color="blue darken-1" + text + @click="submit" + > + 등록 + </v-btn> + </v-card-actions> + </v-card> + </v-dialog> + + <v-dialog + v-model="show_delete_confirm_dialog" + max-width="400px" + persistent + > + <v-card> + <v-card-text /> + <v-card-text> + 삭제하시겠습니까? + </v-card-text> + <v-card-actions v-show="!is_loading"> + <v-spacer /> + <v-btn + color="blue darken-1" + text + @click="cancel" + > + 취소 + </v-btn> + <v-btn + color="blue darken-1" + text + @click="deleteRecommendSeries" + > + 확인 + </v-btn> + </v-card-actions> + </v-card> + </v-dialog> </div> </template> -<style scoped> +<script> +import Draggable from "vuedraggable"; +import debounce from "lodash/debounce"; +import * as api from "@/api/audio_content_series_recommend" +import * as seriesApi from "@/api/audio_content_series"; + +export default { + name: "ContentSeriesNew", + components: {Draggable}, + + data() { + return { + is_selecting: false, + is_loading: false, + is_modify: false, + show_write_dialog: false, + show_delete_confirm_dialog: false, + selected_recommend_series: {}, + recommend_series: {}, + recommend_series_list: [], + series: [], + search_query_series: '', + } + }, + + watch: { + search_query_series() { + if (!this.is_selecting) { + this.debouncedSearchSeries(); + } + } + }, + + async created() { + await this.getRecommendSeriesList(); + }, + + mounted() { + this.debouncedSearchSeries = debounce(this.searchSeries, 500); + }, + + methods: { + imageAdd(payload) { + const file = payload; + if (file) { + this.recommend_series.image_url = URL.createObjectURL(file) + URL.revokeObjectURL(file) + } else { + this.recommend_series.image_url = null + } + }, + + cancel() { + this.is_modify = false + this.is_selecting = false + this.show_write_dialog = false + this.show_delete_confirm_dialog = false + this.recommend_series = {} + this.selected_recommend_series = {} + this.search_query_series = '' + this.series = [] + }, + + notifyError(message) { + this.$dialog.notify.error(message) + }, + + notifySuccess(message) { + this.$dialog.notify.success(message) + }, + + showModifyRecommendSeriesDialog(recommendSeries) { + this.is_modify = true + this.selected_recommend_series = recommendSeries + + this.is_selecting = true; // 선택 중 플래그 활성화 + + this.recommend_series.id = recommendSeries.id + this.recommend_series.image_url = recommendSeries.imageUrl + this.recommend_series.series_id = recommendSeries.seriesId + this.recommend_series.series_title = recommendSeries.seriesTitle + + setTimeout(() => { + this.is_selecting = false; // 선택 상태 해제 + }, 1000); + + this.show_write_dialog = true + }, + + deleteConfirm(recommendSeries) { + this.selected_recommend_series = recommendSeries + this.show_delete_confirm_dialog = true + }, + + onSelectSeries(value) { + this.recommend_series.series_id = value.value + this.is_selecting = true; // 선택 중 플래그 활성화 + setTimeout(() => { + this.is_selecting = false; // 선택 상태 해제 + }, 0); + }, + + onSearchSeriesUpdate(value) { + if (!this.is_selecting) { + this.search_query_series = value + } + }, + + validate() { + if (this.recommend_series.series_id === null || this.recommend_series.series_id === undefined) { + this.notifyError("시리즈를 선택하세요") + return false; + } + + return true; + }, + + async getRecommendSeriesList() { + this.is_loading = true + try { + const res = await api.getRecommendSeriesList(false) + if (res.status === 200 && res.data.success === true) { + this.recommend_series_list = res.data.data + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + this.is_loading = false + } finally { + this.is_loading = false + } + }, + + async submit() { + if (!this.validate()) return; + if (this.is_loading === true) return; + + this.is_loading = true + + try { + const formData = new FormData() + formData.append("image", this.recommend_series.image) + + let request = { + seriesId: this.recommend_series.series_id, + isFree: false + } + + formData.append("request", JSON.stringify(request)) + + const res = await api.saveRecommendSeries(formData) + if (res.status === 200 && res.data.success === true) { + this.cancel() + this.notifySuccess('등록되었습니다.') + + this.recommend_series_list = [] + await this.getRecommendSeriesList() + } else { + this.notifyError(res.data.message); + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async modify() { + if (this.is_loading) return; + + this.is_loading = true + try { + const formData = new FormData() + let request = {id: this.recommend_series.id} + + if (this.recommend_series.image !== null) { + formData.append("image", this.recommend_series.image) + } + + if ( + this.selected_recommend_series.series_id !== this.recommend_series.series_id && + this.recommend_series.series_id !== null && + this.recommend_series.series_id !== undefined + ) { + request.seriesId = this.recommend_series.series_id + } + + formData.append("request", JSON.stringify(request)) + + const res = await api.modifyRecommendSeries(formData) + if (res.status === 200 && res.data.success === true) { + this.cancel() + this.notifySuccess('수정되었습니다.') + + this.recommend_series_list = [] + await this.getRecommendSeriesList() + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async deleteRecommendSeries() { + if (this.is_loading) return; + this.is_loading = true + + try { + const formData = new FormData() + formData.append("request", JSON.stringify({id: this.selected_recommend_series.id, isActive: false})) + const res = await api.modifyRecommendSeries(formData) + if (res.status === 200 && res.data.success === true) { + this.show_delete_confirm_dialog = false + this.cancel() + this.notifySuccess('삭제되었습니다.') + + this.recommend_series_list = [] + await this.getRecommendSeriesList() + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async onDropCallback(items) { + const ids = items.map((item) => { + return item.id + }) + + const res = await api.updateRecommendSeriesOrders(ids) + if (res.status === 200 && res.data.success === true) { + this.notifySuccess(res.data.message) + } + }, + + async searchSeries() { + if (this.search_query_series === null || this.search_query_series.length < 2) { + this.series = []; + return; + } + + this.is_loading = true; + + try { + const res = await seriesApi.searchSeriesList(this.search_query_series); + + if (res.status === 200 && res.data.success === true) { + this.series = res.data.data.map((item) => { + return {name: item.title, value: item.id} + }) + } else { + this.series = [] + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + this.is_loading = false + } finally { + this.is_loading = false + } + }, + } +} +</script> + +<style scoped> +.image-select label { + display: inline-block; + padding: 10px 20px; + background-color: #232d4a; + color: #fff; + vertical-align: middle; + font-size: 15px; + cursor: pointer; + border-radius: 5px; +} + +.v-file-input { + position: absolute; + width: 0; + height: 0; + padding: 0; + overflow: hidden; + border: 0; +} + +.image-preview { + max-width: 100%; + width: 250px; + object-fit: cover; + margin-top: 10px; +} </style>