sodalive-vuejs-admin/src/views/Chat/CharacterCuration.vue

342 lines
9.5 KiB
Vue

<template>
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>캐릭터 큐레이션</v-toolbar-title>
<v-spacer />
</v-toolbar>
<v-container>
<v-row class="mb-4">
<v-col
cols="12"
sm="4"
>
<v-btn
color="primary"
dark
@click="showWriteDialog"
>
큐레이션 등록
</v-btn>
</v-col>
</v-row>
<v-row>
<v-col>
<v-data-table
:headers="headers"
:items="curations"
:loading="isLoading"
item-key="id"
class="elevation-1"
hide-default-footer
disable-pagination
>
<template v-slot:body="props">
<draggable
v-model="props.items"
tag="tbody"
@end="onDragEnd(props.items)"
>
<tr
v-for="item in props.items"
:key="item.id"
>
<td @click="goDetail(item)">
{{ item.title }}
</td>
<td @click="goDetail(item)">
<h3 v-if="item.isAdult">
O
</h3>
<h3 v-else>
X
</h3>
</td>
<td>
<v-row>
<v-col class="text-center">
<v-btn
small
color="primary"
:disabled="isLoading"
@click="showModifyDialog(item)"
>
수정
</v-btn>
</v-col>
<v-col class="text-center">
<v-btn
small
color="error"
:disabled="isLoading"
@click="confirmDelete(item)"
>
삭제
</v-btn>
</v-col>
</v-row>
</td>
</tr>
</draggable>
</template>
</v-data-table>
</v-col>
</v-row>
</v-container>
<!-- 등록/수정 다이얼로그 -->
<v-dialog
v-model="showDialog"
max-width="600px"
persistent
>
<v-card>
<v-card-title>
<span class="headline">{{ isModify ? '큐레이션 수정' : '큐레이션 등록' }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="form.title"
label="제목"
outlined
required
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-checkbox
v-model="form.isAdult"
label="19금"
/>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
color="blue darken-1"
@click="closeDialog"
>
취소
</v-btn>
<v-btn
text
color="blue darken-1"
:loading="isSubmitting"
:disabled="!isFormValid || isSubmitting"
@click="saveCuration"
>
저장
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 삭제 확인 다이얼로그 -->
<v-dialog
v-model="showDeleteDialog"
max-width="400px"
>
<v-card>
<v-card-title class="headline">
큐레이션 삭제
</v-card-title>
<v-card-text>"{{ selectedCuration && selectedCuration.title }}"() 삭제하시겠습니까?</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
color="blue darken-1"
@click="showDeleteDialog = false"
>
취소
</v-btn>
<v-btn
text
color="red darken-1"
:loading="isSubmitting"
@click="deleteCuration"
>
삭제
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import draggable from 'vuedraggable';
import {
getCharacterCurationList,
createCharacterCuration,
updateCharacterCuration,
deleteCharacterCuration,
updateCharacterCurationOrder
} from '@/api/character';
export default {
name: 'CharacterCuration',
components: { draggable },
data() {
return {
isLoading: false,
isSubmitting: false,
curations: [],
headers: [
{ text: '제목', align: 'center', sortable: false, value: 'title' },
{ text: '19금', align: 'center', sortable: false, value: 'isAdult' },
{ text: '관리', align: 'center', sortable: false, value: 'management' }
],
showDialog: false,
isModify: false,
form: { id: null, title: '', isAdult: false },
selectedCuration: null,
showDeleteDialog: false
};
},
computed: {
isFormValid() {
return this.form.title && this.form.title.trim().length > 0;
}
},
async created() {
await this.loadCurations();
},
methods: {
notifyError(message) { this.$dialog.notify.error(message); },
notifySuccess(message) { this.$dialog.notify.success(message); },
async loadCurations() {
this.isLoading = true;
try {
const res = await getCharacterCurationList();
if (res.status === 200 && res.data && res.data.success === true) {
this.curations = res.data.data || [];
} else {
this.notifyError(res.data.message || '목록을 불러오지 못했습니다.');
}
} catch (e) {
this.notifyError('목록을 불러오지 못했습니다.');
} finally {
this.isLoading = false;
}
},
onDragEnd(items) {
const ids = items.map(i => i.id);
this.updateOrders(ids);
},
async updateOrders(ids) {
try {
const res = await updateCharacterCurationOrder(ids);
if (res.status === 200 && res.data && res.data.success === true) {
this.notifySuccess('순서가 변경되었습니다.');
} else {
this.notifyError(res.data.message || '순서 변경에 실패했습니다.');
}
} catch (e) {
this.notifyError('순서 변경에 실패했습니다.');
}
},
goDetail(item) {
this.$router.push({
name: 'CharacterCurationDetail',
params: { curationId: item.id, title: item.title, isAdult: item.isAdult }
});
},
showWriteDialog() {
this.isModify = false;
this.form = { id: null, title: '', isAdult: false };
this.showDialog = true;
},
showModifyDialog(item) {
this.isModify = true;
this.form = { id: item.id, title: item.title, isAdult: item.isAdult };
this.showDialog = true;
},
closeDialog() {
this.showDialog = false;
this.form = { id: null, title: '', isAdult: false };
},
async saveCuration() {
if (this.isSubmitting || !this.isFormValid) return;
this.isSubmitting = true;
try {
if (this.isModify) {
const payload = { id: this.form.id };
if (this.form.title) payload.title = this.form.title;
payload.isAdult = this.form.isAdult;
const res = await updateCharacterCuration(payload);
if (res.status === 200 && res.data && res.data.success === true) {
this.notifySuccess('수정되었습니다.');
this.closeDialog();
await this.loadCurations();
} else {
this.notifyError(res.data.message || '수정에 실패했습니다.');
}
} else {
const res = await createCharacterCuration({
title: this.form.title,
isAdult: this.form.isAdult,
isActive: true
});
if (res.status === 200 && res.data && res.data.success === true) {
this.notifySuccess('등록되었습니다.');
this.closeDialog();
await this.loadCurations();
} else {
this.notifyError(res.data.message || '등록에 실패했습니다.');
}
}
} catch (e) {
this.notifyError('저장 중 오류가 발생했습니다.');
} finally {
this.isSubmitting = false;
}
},
confirmDelete(item) {
this.selectedCuration = item;
this.showDeleteDialog = true;
},
async deleteCuration() {
if (!this.selectedCuration) return;
this.isSubmitting = true;
try {
const res = await deleteCharacterCuration(this.selectedCuration.id);
if (res.status === 200 && res.data && res.data.success === true) {
this.notifySuccess('삭제되었습니다.');
this.showDeleteDialog = false;
await this.loadCurations();
} else {
this.notifyError(res.data.message || '삭제에 실패했습니다.');
}
} catch (e) {
this.notifyError('삭제에 실패했습니다.');
} finally {
this.isSubmitting = false;
}
}
}
};
</script>
<style scoped>
</style>