캐릭터 챗봇 #74
							
								
								
									
										514
									
								
								src/views/Chat/CharacterList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								src/views/Chat/CharacterList.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,514 @@ | |||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <v-toolbar dark> | ||||||
|  |       <v-spacer /> | ||||||
|  |       <v-toolbar-title>캐릭터 리스트</v-toolbar-title> | ||||||
|  |       <v-spacer /> | ||||||
|  |     </v-toolbar> | ||||||
|  |  | ||||||
|  |     <br> | ||||||
|  |  | ||||||
|  |     <v-container> | ||||||
|  |       <v-row> | ||||||
|  |         <v-col cols="4"> | ||||||
|  |           <v-btn | ||||||
|  |             color="primary" | ||||||
|  |             dark | ||||||
|  |             @click="showAddDialog" | ||||||
|  |           > | ||||||
|  |             캐릭터 추가 | ||||||
|  |           </v-btn> | ||||||
|  |         </v-col> | ||||||
|  |         <v-spacer /> | ||||||
|  |         <v-col cols="6"> | ||||||
|  |           <v-text-field | ||||||
|  |             v-model="search_word" | ||||||
|  |             label="캐릭터 이름 검색" | ||||||
|  |             @keyup.enter="search" | ||||||
|  |           > | ||||||
|  |             <v-btn | ||||||
|  |               slot="append" | ||||||
|  |               color="#9970ff" | ||||||
|  |               dark | ||||||
|  |               @click="search" | ||||||
|  |             > | ||||||
|  |               검색 | ||||||
|  |             </v-btn> | ||||||
|  |           </v-text-field> | ||||||
|  |         </v-col> | ||||||
|  |       </v-row> | ||||||
|  |       <v-row> | ||||||
|  |         <v-col> | ||||||
|  |           <v-simple-table class="elevation-10"> | ||||||
|  |             <template> | ||||||
|  |               <thead> | ||||||
|  |                 <tr> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     ID | ||||||
|  |                   </th> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     이미지 | ||||||
|  |                   </th> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     이름 | ||||||
|  |                   </th> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     설명 | ||||||
|  |                   </th> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     상태 | ||||||
|  |                   </th> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     생성일 | ||||||
|  |                   </th> | ||||||
|  |                   <th class="text-center"> | ||||||
|  |                     관리 | ||||||
|  |                   </th> | ||||||
|  |                 </tr> | ||||||
|  |               </thead> | ||||||
|  |               <tbody> | ||||||
|  |                 <tr | ||||||
|  |                   v-for="item in characters" | ||||||
|  |                   :key="item.id" | ||||||
|  |                 > | ||||||
|  |                   <td>{{ item.id }}</td> | ||||||
|  |                   <td align="center"> | ||||||
|  |                     <v-img | ||||||
|  |                       max-width="70" | ||||||
|  |                       max-height="70" | ||||||
|  |                       :src="item.imageUrl" | ||||||
|  |                       class="rounded-circle" | ||||||
|  |                     /> | ||||||
|  |                   </td> | ||||||
|  |                   <td>{{ item.name }}</td> | ||||||
|  |                   <td style="max-width: 200px !important; word-break:break-all; height: auto;"> | ||||||
|  |                     <vue-show-more-text | ||||||
|  |                       :text="item.description" | ||||||
|  |                       :lines="3" | ||||||
|  |                     /> | ||||||
|  |                   </td> | ||||||
|  |                   <td> | ||||||
|  |                     <v-chip | ||||||
|  |                       :color="item.isActive ? 'green' : 'red'" | ||||||
|  |                       text-color="white" | ||||||
|  |                       small | ||||||
|  |                     > | ||||||
|  |                       {{ item.isActive ? '활성' : '비활성' }} | ||||||
|  |                     </v-chip> | ||||||
|  |                   </td> | ||||||
|  |                   <td>{{ item.createdAt }}</td> | ||||||
|  |                   <td> | ||||||
|  |                     <v-row> | ||||||
|  |                       <v-col> | ||||||
|  |                         <v-btn | ||||||
|  |                           small | ||||||
|  |                           color="primary" | ||||||
|  |                           :disabled="is_loading" | ||||||
|  |                           @click="showEditDialog(item)" | ||||||
|  |                         > | ||||||
|  |                           수정 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-col> | ||||||
|  |                       <v-col> | ||||||
|  |                         <v-btn | ||||||
|  |                           small | ||||||
|  |                           color="error" | ||||||
|  |                           :disabled="is_loading" | ||||||
|  |                           @click="deleteConfirm(item)" | ||||||
|  |                         > | ||||||
|  |                           삭제 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-col> | ||||||
|  |                     </v-row> | ||||||
|  |                   </td> | ||||||
|  |                 </tr> | ||||||
|  |               </tbody> | ||||||
|  |             </template> | ||||||
|  |           </v-simple-table> | ||||||
|  |         </v-col> | ||||||
|  |       </v-row> | ||||||
|  |       <v-row class="text-center"> | ||||||
|  |         <v-col> | ||||||
|  |           <v-pagination | ||||||
|  |             v-model="page" | ||||||
|  |             :length="total_page" | ||||||
|  |             circle | ||||||
|  |             @input="next" | ||||||
|  |           /> | ||||||
|  |         </v-col> | ||||||
|  |       </v-row> | ||||||
|  |     </v-container> | ||||||
|  |  | ||||||
|  |     <!-- 캐릭터 추가/수정 다이얼로그 --> | ||||||
|  |     <v-dialog | ||||||
|  |       v-model="show_dialog" | ||||||
|  |       max-width="600px" | ||||||
|  |       persistent | ||||||
|  |     > | ||||||
|  |       <v-card> | ||||||
|  |         <v-card-title> | ||||||
|  |           {{ is_edit ? '캐릭터 수정' : '캐릭터 추가' }} | ||||||
|  |         </v-card-title> | ||||||
|  |         <v-card-text> | ||||||
|  |           <v-container> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-text-field | ||||||
|  |                   v-model="character.name" | ||||||
|  |                   label="이름" | ||||||
|  |                   required | ||||||
|  |                 /> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-textarea | ||||||
|  |                   v-model="character.description" | ||||||
|  |                   label="설명" | ||||||
|  |                   required | ||||||
|  |                 /> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-file-input | ||||||
|  |                   v-model="character.image" | ||||||
|  |                   label="캐릭터 이미지" | ||||||
|  |                   accept="image/*" | ||||||
|  |                   prepend-icon="mdi-camera" | ||||||
|  |                   show-size | ||||||
|  |                   truncate-length="15" | ||||||
|  |                 /> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-switch | ||||||
|  |                   v-model="character.isActive" | ||||||
|  |                   label="활성화" | ||||||
|  |                 /> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |           </v-container> | ||||||
|  |         </v-card-text> | ||||||
|  |         <v-card-actions> | ||||||
|  |           <v-spacer /> | ||||||
|  |           <v-btn | ||||||
|  |             color="blue darken-1" | ||||||
|  |             text | ||||||
|  |             @click="closeDialog" | ||||||
|  |           > | ||||||
|  |             취소 | ||||||
|  |           </v-btn> | ||||||
|  |           <v-btn | ||||||
|  |             color="blue darken-1" | ||||||
|  |             text | ||||||
|  |             @click="saveCharacter" | ||||||
|  |           > | ||||||
|  |             저장 | ||||||
|  |           </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> | ||||||
|  |           "{{ selected_character.name }}"을(를) 삭제하시겠습니까? | ||||||
|  |         </v-card-text> | ||||||
|  |         <v-card-actions v-show="!is_loading"> | ||||||
|  |           <v-spacer /> | ||||||
|  |           <v-btn | ||||||
|  |             color="blue darken-1" | ||||||
|  |             text | ||||||
|  |             @click="closeDeleteDialog" | ||||||
|  |           > | ||||||
|  |             취소 | ||||||
|  |           </v-btn> | ||||||
|  |           <v-btn | ||||||
|  |             color="red darken-1" | ||||||
|  |             text | ||||||
|  |             @click="deleteCharacter" | ||||||
|  |           > | ||||||
|  |             확인 | ||||||
|  |           </v-btn> | ||||||
|  |         </v-card-actions> | ||||||
|  |       </v-card> | ||||||
|  |     </v-dialog> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import VueShowMoreText from 'vue-show-more-text' | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   name: "CharacterList", | ||||||
|  |  | ||||||
|  |   components: { VueShowMoreText }, | ||||||
|  |  | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       is_loading: false, | ||||||
|  |       show_dialog: false, | ||||||
|  |       show_delete_confirm_dialog: false, | ||||||
|  |       is_edit: false, | ||||||
|  |       page: 1, | ||||||
|  |       total_page: 0, | ||||||
|  |       search_word: '', | ||||||
|  |       character: { | ||||||
|  |         id: null, | ||||||
|  |         name: '', | ||||||
|  |         description: '', | ||||||
|  |         image: null, | ||||||
|  |         imageUrl: '', | ||||||
|  |         isActive: true, | ||||||
|  |         createdAt: '' | ||||||
|  |       }, | ||||||
|  |       characters: [], | ||||||
|  |       selected_character: {} | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   async created() { | ||||||
|  |     await this.getCharacters() | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   methods: { | ||||||
|  |     notifyError(message) { | ||||||
|  |       this.$dialog.notify.error(message) | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     notifySuccess(message) { | ||||||
|  |       this.$dialog.notify.success(message) | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     showAddDialog() { | ||||||
|  |       this.is_edit = false | ||||||
|  |       this.character = { | ||||||
|  |         id: null, | ||||||
|  |         name: '', | ||||||
|  |         description: '', | ||||||
|  |         image: null, | ||||||
|  |         imageUrl: '', | ||||||
|  |         isActive: true, | ||||||
|  |         createdAt: '' | ||||||
|  |       } | ||||||
|  |       this.show_dialog = true | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     showEditDialog(item) { | ||||||
|  |       this.is_edit = true | ||||||
|  |       this.selected_character = item | ||||||
|  |       this.character = { | ||||||
|  |         id: item.id, | ||||||
|  |         name: item.name, | ||||||
|  |         description: item.description, | ||||||
|  |         image: null, | ||||||
|  |         imageUrl: item.imageUrl, | ||||||
|  |         isActive: item.isActive, | ||||||
|  |         createdAt: item.createdAt | ||||||
|  |       } | ||||||
|  |       this.show_dialog = true | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     closeDialog() { | ||||||
|  |       this.show_dialog = false | ||||||
|  |       this.character = { | ||||||
|  |         id: null, | ||||||
|  |         name: '', | ||||||
|  |         description: '', | ||||||
|  |         image: null, | ||||||
|  |         imageUrl: '', | ||||||
|  |         isActive: true, | ||||||
|  |         createdAt: '' | ||||||
|  |       } | ||||||
|  |       this.selected_character = {} | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     deleteConfirm(item) { | ||||||
|  |       this.selected_character = item | ||||||
|  |       this.show_delete_confirm_dialog = true | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     closeDeleteDialog() { | ||||||
|  |       this.show_delete_confirm_dialog = false | ||||||
|  |       this.selected_character = {} | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     async saveCharacter() { | ||||||
|  |       if ( | ||||||
|  |         this.character.name === null || | ||||||
|  |         this.character.name === undefined || | ||||||
|  |         this.character.name.trim().length <= 0 | ||||||
|  |       ) { | ||||||
|  |         this.notifyError("이름을 입력하세요") | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if ( | ||||||
|  |         this.character.description === null || | ||||||
|  |         this.character.description === undefined || | ||||||
|  |         this.character.description.trim().length <= 0 | ||||||
|  |       ) { | ||||||
|  |         this.notifyError("설명을 입력하세요") | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (this.is_loading) return; | ||||||
|  |  | ||||||
|  |       this.is_loading = true | ||||||
|  |  | ||||||
|  |       try { | ||||||
|  |         // 여기에 API 호출 코드를 추가합니다. | ||||||
|  |         // 예시: | ||||||
|  |         // const res = this.is_edit | ||||||
|  |         //   ? await api.updateCharacter(this.character) | ||||||
|  |         //   : await api.createCharacter(this.character); | ||||||
|  |  | ||||||
|  |         // API 호출이 없으므로 임시로 성공 처리 | ||||||
|  |         setTimeout(() => { | ||||||
|  |           this.closeDialog() | ||||||
|  |           this.notifySuccess(this.is_edit ? '수정되었습니다.' : '추가되었습니다.') | ||||||
|  |           this.getCharacters() | ||||||
|  |           this.is_loading = false | ||||||
|  |         }, 1000) | ||||||
|  |       } catch (e) { | ||||||
|  |         this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||||
|  |         this.is_loading = false | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     async deleteCharacter() { | ||||||
|  |       if (this.is_loading) return; | ||||||
|  |       this.is_loading = true | ||||||
|  |  | ||||||
|  |       try { | ||||||
|  |         // 여기에 API 호출 코드를 추가합니다. | ||||||
|  |         // 예시: | ||||||
|  |         // const res = await api.deleteCharacter(this.selected_character.id); | ||||||
|  |  | ||||||
|  |         // API 호출이 없으므로 임시로 성공 처리 | ||||||
|  |         setTimeout(() => { | ||||||
|  |           this.closeDeleteDialog() | ||||||
|  |           this.notifySuccess('삭제되었습니다.') | ||||||
|  |           this.getCharacters() | ||||||
|  |           this.is_loading = false | ||||||
|  |         }, 1000) | ||||||
|  |       } catch (e) { | ||||||
|  |         this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||||
|  |         this.is_loading = false | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     async next() { | ||||||
|  |       if (this.search_word.length < 2) { | ||||||
|  |         this.search_word = '' | ||||||
|  |         await this.getCharacters() | ||||||
|  |       } else { | ||||||
|  |         await this.searchCharacters() | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     async getCharacters() { | ||||||
|  |       this.is_loading = true | ||||||
|  |       try { | ||||||
|  |         // 여기에 API 호출 코드를 추가합니다. | ||||||
|  |         // 예시: | ||||||
|  |         // const res = await api.getCharacters(this.page); | ||||||
|  |  | ||||||
|  |         // API 호출이 없으므로 임시 데이터 생성 | ||||||
|  |         setTimeout(() => { | ||||||
|  |           // 임시 데이터 | ||||||
|  |           const mockData = { | ||||||
|  |             totalCount: 15, | ||||||
|  |             items: Array.from({ length: 10 }, (_, i) => ({ | ||||||
|  |               id: i + 1 + (this.page - 1) * 10, | ||||||
|  |               name: `캐릭터 ${i + 1 + (this.page - 1) * 10}`, | ||||||
|  |               description: `이것은 캐릭터 ${i + 1 + (this.page - 1) * 10}에 대한 설명입니다. 이 캐릭터는 다양한 특성을 가지고 있습니다.`, | ||||||
|  |               imageUrl: 'https://via.placeholder.com/150', | ||||||
|  |               isActive: Math.random() > 0.3, | ||||||
|  |               createdAt: new Date().toISOString().split('T')[0] | ||||||
|  |             })) | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           const total_page = Math.ceil(mockData.totalCount / 10) | ||||||
|  |           this.characters = mockData.items | ||||||
|  |  | ||||||
|  |           if (total_page <= 0) | ||||||
|  |             this.total_page = 1 | ||||||
|  |           else | ||||||
|  |             this.total_page = total_page | ||||||
|  |  | ||||||
|  |           this.is_loading = false | ||||||
|  |         }, 500) | ||||||
|  |       } catch (e) { | ||||||
|  |         this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||||
|  |         this.is_loading = false | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     async search() { | ||||||
|  |       this.page = 1 | ||||||
|  |       await this.searchCharacters() | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     async searchCharacters() { | ||||||
|  |       if (this.search_word.length === 0) { | ||||||
|  |         await this.getCharacters() | ||||||
|  |       } else if (this.search_word.length < 2) { | ||||||
|  |         this.notifyError('검색어를 2글자 이상 입력하세요.') | ||||||
|  |       } else { | ||||||
|  |         this.is_loading = true | ||||||
|  |         try { | ||||||
|  |           // 여기에 API 호출 코드를 추가합니다. | ||||||
|  |           // 예시: | ||||||
|  |           // const res = await api.searchCharacters(this.search_word, this.page); | ||||||
|  |  | ||||||
|  |           // API 호출이 없으므로 임시 데이터 생성 | ||||||
|  |           setTimeout(() => { | ||||||
|  |             // 검색 결과 임시 데이터 | ||||||
|  |             const filteredItems = Array.from({ length: 3 }, (_, i) => ({ | ||||||
|  |               id: i + 1, | ||||||
|  |               name: `${this.search_word} 캐릭터 ${i + 1}`, | ||||||
|  |               description: `이것은 ${this.search_word} 캐릭터 ${i + 1}에 대한 설명입니다.`, | ||||||
|  |               imageUrl: 'https://via.placeholder.com/150', | ||||||
|  |               isActive: true, | ||||||
|  |               createdAt: new Date().toISOString().split('T')[0] | ||||||
|  |             })) | ||||||
|  |  | ||||||
|  |             const mockData = { | ||||||
|  |               totalCount: 3, | ||||||
|  |               items: filteredItems | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             const total_page = Math.ceil(mockData.totalCount / 10) | ||||||
|  |             this.characters = mockData.items | ||||||
|  |  | ||||||
|  |             if (total_page <= 0) | ||||||
|  |               this.total_page = 1 | ||||||
|  |             else | ||||||
|  |               this.total_page = total_page | ||||||
|  |  | ||||||
|  |             this.is_loading = false | ||||||
|  |           }, 500) | ||||||
|  |         } catch (e) { | ||||||
|  |           this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||||
|  |           this.is_loading = false | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .v-data-table { | ||||||
|  |   width: 100%; | ||||||
|  | } | ||||||
|  | </style> | ||||||
		Reference in New Issue
	
	Block a user