diff --git a/src/api/audio_content.js b/src/api/audio_content.js index c5db580..fd968f3 100644 --- a/src/api/audio_content.js +++ b/src/api/audio_content.js @@ -100,6 +100,22 @@ async function updateItemInCurationOrders(curationId, itemIds) { ) } +async function getHashTagCurations() { + return Vue.axios.get("/admin/audio-content/tag/curation") +} + +async function saveHashTagCuration(request) { + return Vue.axios.post("/admin/audio-content/tag/curation", request) +} + +async function modifyHashTagCuration(request) { + return Vue.axios.put("/admin/audio-content/tag/curation", request) +} + +async function updateHashTagCurationOrders(ids) { + return Vue.axios.put('/admin/audio-content/tag/curation/orders', {ids: ids}) +} + export { getAudioContentList, searchAudioContent, @@ -119,5 +135,9 @@ export { searchContentItem, addItemToCuration, removeItemInCuration, - updateItemInCurationOrders + updateItemInCurationOrders, + getHashTagCurations, + saveHashTagCuration, + modifyHashTagCuration, + updateHashTagCurationOrders } diff --git a/src/router/index.js b/src/router/index.js index 197a58f..1cac859 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -85,6 +85,11 @@ const routes = [ name: 'ContentCurationDetail', component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentCurationDetail.vue') }, + { + path: '/content/tag/curation', + name: 'ContentHashTagCuration', + component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentHashTagCuration.vue') + }, { path: '/content/series/list', name: 'ContentSeriesList', diff --git a/src/views/Content/ContentHashTagCuration.vue b/src/views/Content/ContentHashTagCuration.vue new file mode 100644 index 0000000..e417790 --- /dev/null +++ b/src/views/Content/ContentHashTagCuration.vue @@ -0,0 +1,429 @@ +<template> + <div> + <v-toolbar dark> + <v-spacer /> + <v-toolbar-title>태그 큐레이션</v-toolbar-title> + <v-spacer /> + </v-toolbar> + + <br> + + <v-container> + <v-row> + <v-spacer /> + <v-col cols="3"> + <v-btn + block + color="#3bb9f1" + dark + depressed + @click="showWriteDialog" + > + 태그 등록 + </v-btn> + </v-col> + </v-row> + <v-row> + <v-col> + <v-data-table + :headers="headers" + :items="curations" + :loading="is_loading" + item-key="id" + class="elevation-1" + hide-default-footer + disable-pagination + > + <template v-slot:body="props"> + <draggable + v-model="props.items" + tag="tbody" + @end="onDropCallback(props.items)" + > + <tr + v-for="(item, index) in props.items" + :key="index" + > + <td + @click="handleItemClick(item)" + > + {{ item.tag }} + </td> + <td> + <h3 v-if="item.isAdult"> + O + </h3> + <h3 v-else> + X + </h3> + </td> + <td> + <v-row> + <v-col /> + <v-col> + <v-btn + :disabled="is_loading" + @click="showModifyDialog(item)" + > + 수정 + </v-btn> + </v-col> + + <v-col> + <v-btn + :disabled="is_loading" + @click="deleteConfirm(item)" + > + 삭제 + </v-btn> + </v-col> + <v-col /> + </v-row> + </td> + </tr> + </draggable> + </template> + </v-data-table> + </v-col> + </v-row> + </v-container> + + <v-row> + <v-dialog + v-model="show_write_dialog" + max-width="1000px" + persistent + > + <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-text-field + v-model="curation.tag" + label="태그" + required + /> + </v-col> + </v-row> + </v-card-text> + <v-card-text> + <v-row> + <v-col cols="4"> + 19금 + </v-col> + <v-col cols="8"> + <input + v-model="curation.is_adult" + type="checkbox" + > + </v-col> + </v-row> + </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 + 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-row> + + <v-dialog + v-model="show_delete_confirm_dialog" + max-width="400px" + persistent + > + <v-card> + <v-card-text /> + <v-card-text> + "{{ selected_curation.tag }}"을 삭제하시겠습니까? + </v-card-text> + <v-card-actions v-show="!is_loading"> + <v-spacer /> + <v-btn + color="blue darken-1" + text + @click="deleteCancel" + > + 취소 + </v-btn> + <v-btn + color="blue darken-1" + text + @click="deleteCuration" + > + 확인 + </v-btn> + </v-card-actions> + </v-card> + </v-dialog> + </div> +</template> + +<script> +import Draggable from 'vuedraggable'; +import * as api from "@/api/audio_content" + +export default { + name: "ContentHashTagCuration", + components: {Draggable}, + data() { + return { + is_loading: false, + is_modify: false, + show_delete_confirm_dialog: false, + show_write_dialog: false, + selected_curation: {}, + curation: {is_adult: false}, + curations: [], + headers: [ + { + text: '태그', + align: 'center', + sortable: false, + value: 'tag', + }, + { + text: '19금', + align: 'center', + sortable: false, + value: 'isAdult', + }, + { + text: '관리', + align: 'center', + sortable: false, + value: 'management' + }, + ], + } + }, + + async created() { + await this.getHashTagCurations(); + }, + + methods: { + notifyError(message) { + this.$dialog.notify.error(message) + }, + + notifySuccess(message) { + this.$dialog.notify.success(message) + }, + + showWriteDialog() { + this.show_write_dialog = true + }, + + showModifyDialog(item) { + this.is_modify = true + this.selected_curation = item + + this.curation.id = item.id + this.curation.tag = item.tag + this.curation.is_adult = item.isAdult + + this.show_write_dialog = true + }, + + cancel() { + this.curation = {is_adult: false} + this.selected_curation = {} + + this.is_modify = false + this.show_write_dialog = false + }, + + handleItemClick(item) { + this.$router.push( + { + name: 'ContentHashTagCurationDetail', + params: { + curation_id: item.id, + tag: item.tag, + is_adult: item.isAdult + } + } + ) + }, + + validate() { + if ( + this.curation.tag === null || + this.curation.tag === undefined || + this.curation.tag.trim().length <= 0 + ) { + this.notifyError("태그를 입력하세요") + return false + } + + return true + }, + + deleteConfirm(curation) { + this.selected_curation = curation + this.show_delete_confirm_dialog = true + }, + + deleteCancel() { + this.selected_curation = {} + this.show_delete_confirm_dialog = false + }, + + async getHashTagCurations() { + this.is_loading = true + + try { + const res = await api.getHashTagCurations() + if (res.status === 200 && res.data.success === true) { + this.curations = res.data.data + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async submit() { + if (!this.validate()) return; + if (this.is_loading) return; + + this.isLoading = true + + try { + const request = { + tag: this.curation.tag, + isAdult: this.curation.is_adult + } + + const res = await api.saveHashTagCuration(request) + if (res.status === 200 && res.data.success === true) { + this.cancel() + this.notifySuccess('등록되었습니다.') + + this.curations = [] + await this.getHashTagCurations() + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async modify() { + if (!this.validate()) return; + if (this.is_loading) return; + this.isLoading = true + + try { + let request = {id: this.curation.id} + if (this.selected_curation.tag !== this.curation.tag && this.curation.tag.trim().length > 0) { + request.tag = this.curation.tag + } + + if (this.selected_curation.isAdult !== this.curation.is_adult) { + request.isAdult = this.curation.is_adult + } + + const res = await api.modifyHashTagCuration(request) + if (res.status === 200 && res.data.success === true) { + this.cancel() + this.notifySuccess('수정되었습니다.') + + this.curations = [] + await this.getHashTagCurations() + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async deleteCuration() { + if (this.is_loading) return; + this.is_loading = true + + try { + let request = {id: this.selected_curation.id, isActive: false} + + const res = await api.modifyHashTagCuration(request) + if (res.status === 200 && res.data.success === true) { + this.show_delete_confirm_dialog = false + this.cancel() + this.notifySuccess('삭제되었습니다.') + + this.curations = [] + await this.getHashTagCurations() + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + + async onDropCallback(items) { + this.curations = items + const ids = items.map((item) => { + return item.id + }) + + try { + this.is_loading = true + const res = await api.updateHashTagCurationOrders(ids) + if (res.status === 200 && res.data.success === true) { + this.notifySuccess(res.data.message) + } else { + this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } finally { + this.is_loading = false + } + }, + } +} +</script>