캐릭터 챗봇 큐레이션 추가
This commit is contained in:
341
src/views/Chat/CharacterCuration.vue
Normal file
341
src/views/Chat/CharacterCuration.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<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>
|
Reference in New Issue
Block a user