캐릭터 챗봇 #74
| @@ -99,35 +99,21 @@ | |||||||
|                 /> |                 /> | ||||||
|               </v-col> |               </v-col> | ||||||
|  |  | ||||||
|               <!-- 생년월일 --> |               <!-- 나이 --> | ||||||
|               <v-col |               <v-col | ||||||
|                 cols="12" |                 cols="12" | ||||||
|                 md="6" |                 md="6" | ||||||
|               > |               > | ||||||
|                 <v-menu |  | ||||||
|                   v-model="birthDateMenu" |  | ||||||
|                   :close-on-content-click="false" |  | ||||||
|                   transition="scale-transition" |  | ||||||
|                   offset-y |  | ||||||
|                   min-width="auto" |  | ||||||
|                 > |  | ||||||
|                   <template v-slot:activator="{ on, attrs }"> |  | ||||||
|                 <v-text-field |                 <v-text-field | ||||||
|                       v-model="formattedBirthDate" |                   v-model="character.age" | ||||||
|                       label="생년월일" |                   label="나이" | ||||||
|                       readonly |                   type="number" | ||||||
|  |                   min="0" | ||||||
|                   outlined |                   outlined | ||||||
|                   dense |                   dense | ||||||
|                       v-bind="attrs" |                   :rules="ageRules" | ||||||
|                       v-on="on" |                   @input="validateNumberInput" | ||||||
|                 /> |                 /> | ||||||
|                   </template> |  | ||||||
|                   <v-date-picker |  | ||||||
|                     v-model="character.birthDate" |  | ||||||
|                     locale="ko" |  | ||||||
|                     @input="birthDateMenu = false" |  | ||||||
|                   /> |  | ||||||
|                 </v-menu> |  | ||||||
|               </v-col> |               </v-col> | ||||||
|             </v-row> |             </v-row> | ||||||
|  |  | ||||||
| @@ -145,20 +131,6 @@ | |||||||
|                   dense |                   dense | ||||||
|                 /> |                 /> | ||||||
|               </v-col> |               </v-col> | ||||||
|  |  | ||||||
|               <!-- 연령제한 - 스위치 형태 --> |  | ||||||
|               <v-col |  | ||||||
|                 cols="12" |  | ||||||
|                 md="6" |  | ||||||
|               > |  | ||||||
|                 <v-switch |  | ||||||
|                   v-model="character.ageRestricted" |  | ||||||
|                   label="연령제한" |  | ||||||
|                   color="primary" |  | ||||||
|                   hide-details |  | ||||||
|                   class="mt-4" |  | ||||||
|                 /> |  | ||||||
|               </v-col> |  | ||||||
|             </v-row> |             </v-row> | ||||||
|  |  | ||||||
|             <!-- 태그 - 입력 후 띄어쓰기 할 때마다 Chip 형태로 변경 --> |             <!-- 태그 - 입력 후 띄어쓰기 할 때마다 Chip 형태로 변경 --> | ||||||
| @@ -173,6 +145,7 @@ | |||||||
|                   deletable-chips |                   deletable-chips | ||||||
|                   outlined |                   outlined | ||||||
|                   dense |                   dense | ||||||
|  |                   :rules="tagRules" | ||||||
|                   @keydown.space.prevent="addTag" |                   @keydown.space.prevent="addTag" | ||||||
|                 > |                 > | ||||||
|                   <template v-slot:selection="{ attrs, item, select, selected }"> |                   <template v-slot:selection="{ attrs, item, select, selected }"> | ||||||
| @@ -190,7 +163,7 @@ | |||||||
|                 <div |                 <div | ||||||
|                   class="caption grey--text text--darken-1 custom-caption" |                   class="caption grey--text text--darken-1 custom-caption" | ||||||
|                 > |                 > | ||||||
|                   태그를 입력하고 엔터를 누르면 추가됩니다. |                   태그를 입력하고 엔터 또는 스페이스바를 누르면 추가됩니다. (50자 이내, 최대 20개, 띄어쓰기 불가) | ||||||
|                 </div> |                 </div> | ||||||
|               </v-col> |               </v-col> | ||||||
|             </v-row> |             </v-row> | ||||||
| @@ -218,19 +191,6 @@ | |||||||
|               </v-col> |               </v-col> | ||||||
|             </v-row> |             </v-row> | ||||||
|  |  | ||||||
|             <!-- 인물 관계 --> |  | ||||||
|             <v-row> |  | ||||||
|               <v-col cols="12"> |  | ||||||
|                 <v-textarea |  | ||||||
|                   v-model="character.relationships" |  | ||||||
|                   label="인물 관계" |  | ||||||
|                   outlined |  | ||||||
|                   auto-grow |  | ||||||
|                   rows="4" |  | ||||||
|                 /> |  | ||||||
|               </v-col> |  | ||||||
|             </v-row> |  | ||||||
|  |  | ||||||
|             <!-- 성격 특성 --> |             <!-- 성격 특성 --> | ||||||
|             <v-row> |             <v-row> | ||||||
|               <v-col cols="12"> |               <v-col cols="12"> | ||||||
| @@ -249,10 +209,42 @@ | |||||||
|               <v-col cols="12"> |               <v-col cols="12"> | ||||||
|                 <v-textarea |                 <v-textarea | ||||||
|                   v-model="character.speechPattern" |                   v-model="character.speechPattern" | ||||||
|                   label="말투/특징적 표현" |                   label="말투/특징적 표현 (최대 500자)" | ||||||
|                   outlined |                   outlined | ||||||
|                   auto-grow |                   auto-grow | ||||||
|                   rows="4" |                   rows="4" | ||||||
|  |                   counter="500" | ||||||
|  |                   :rules="[v => v.length <= 500 || '최대 500자까지 입력 가능합니다']" | ||||||
|  |                 /> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |  | ||||||
|  |             <!-- 대화 스타일 --> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-textarea | ||||||
|  |                   v-model="character.conversationStyle" | ||||||
|  |                   label="대화 스타일 (최대 200자)" | ||||||
|  |                   outlined | ||||||
|  |                   auto-grow | ||||||
|  |                   rows="3" | ||||||
|  |                   counter="200" | ||||||
|  |                   :rules="[v => v.length <= 200 || '최대 200자까지 입력 가능합니다']" | ||||||
|  |                 /> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |  | ||||||
|  |             <!-- 외모 설명 --> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-textarea | ||||||
|  |                   v-model="character.appearance" | ||||||
|  |                   label="외모 설명 (최대 1000자)" | ||||||
|  |                   outlined | ||||||
|  |                   auto-grow | ||||||
|  |                   rows="5" | ||||||
|  |                   counter="1000" | ||||||
|  |                   :rules="[v => v.length <= 1000 || '최대 1000자까지 입력 가능합니다']" | ||||||
|                 /> |                 /> | ||||||
|               </v-col> |               </v-col> | ||||||
|             </v-row> |             </v-row> | ||||||
| @@ -275,6 +267,314 @@ | |||||||
|               </v-col> |               </v-col> | ||||||
|             </v-row> |             </v-row> | ||||||
|  |  | ||||||
|  |             <!-- 인물 관계 --> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-divider class="my-4" /> | ||||||
|  |                 <h3 class="mb-2"> | ||||||
|  |                   인물 관계 | ||||||
|  |                 </h3> | ||||||
|  |  | ||||||
|  |                 <v-row> | ||||||
|  |                   <v-col cols="12"> | ||||||
|  |                     <v-row> | ||||||
|  |                       <v-col cols="11"> | ||||||
|  |                         <v-text-field | ||||||
|  |                           v-model="newRelationship" | ||||||
|  |                           label="새 인물 관계 추가 (최대 200자)" | ||||||
|  |                           outlined | ||||||
|  |                           dense | ||||||
|  |                           counter="200" | ||||||
|  |                           :rules="[v => v.length <= 200 || '최대 200자까지 입력 가능합니다']" | ||||||
|  |                           @keyup.enter="addRelationship" | ||||||
|  |                         /> | ||||||
|  |                       </v-col> | ||||||
|  |                       <v-col cols="1"> | ||||||
|  |                         <v-btn | ||||||
|  |                           color="primary" | ||||||
|  |                           class="mt-1" | ||||||
|  |                           block | ||||||
|  |                           :disabled="!newRelationship.trim() || relationships.length >= 10" | ||||||
|  |                           @click="addRelationship" | ||||||
|  |                         > | ||||||
|  |                           추가 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-col> | ||||||
|  |                     </v-row> | ||||||
|  |                   </v-col> | ||||||
|  |                 </v-row> | ||||||
|  |  | ||||||
|  |                 <v-card | ||||||
|  |                   outlined | ||||||
|  |                   class="memory-container" | ||||||
|  |                 > | ||||||
|  |                   <v-list | ||||||
|  |                     v-if="relationships.length > 0" | ||||||
|  |                     class="memory-list" | ||||||
|  |                   > | ||||||
|  |                     <v-list-item | ||||||
|  |                       v-for="(relationship, index) in relationships" | ||||||
|  |                       :key="index" | ||||||
|  |                       class="memory-item" | ||||||
|  |                     > | ||||||
|  |                       <v-list-item-content> | ||||||
|  |                         <v-list-item-title class="memory-text"> | ||||||
|  |                           {{ relationship }} | ||||||
|  |                         </v-list-item-title> | ||||||
|  |                       </v-list-item-content> | ||||||
|  |                       <v-list-item-action> | ||||||
|  |                         <v-btn | ||||||
|  |                           small | ||||||
|  |                           color="error" | ||||||
|  |                           class="delete-btn" | ||||||
|  |                           @click="removeRelationship(index)" | ||||||
|  |                         > | ||||||
|  |                           삭제 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-list-item-action> | ||||||
|  |                     </v-list-item> | ||||||
|  |                   </v-list> | ||||||
|  |                   <v-card-text | ||||||
|  |                     v-else | ||||||
|  |                     class="text-center grey--text" | ||||||
|  |                   > | ||||||
|  |                     인물 관계가 없습니다. 위 입력창에서 인물 관계를 추가해주세요. (최대 10개) | ||||||
|  |                   </v-card-text> | ||||||
|  |                 </v-card> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |  | ||||||
|  |             <!-- 취미 목록 --> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-divider class="my-4" /> | ||||||
|  |                 <h3 class="mb-2"> | ||||||
|  |                   취미 목록 | ||||||
|  |                 </h3> | ||||||
|  |  | ||||||
|  |                 <v-row> | ||||||
|  |                   <v-col cols="12"> | ||||||
|  |                     <v-row> | ||||||
|  |                       <v-col cols="11"> | ||||||
|  |                         <v-text-field | ||||||
|  |                           v-model="newHobby" | ||||||
|  |                           label="새 취미 추가 (최대 100자)" | ||||||
|  |                           outlined | ||||||
|  |                           dense | ||||||
|  |                           counter="100" | ||||||
|  |                           :rules="[v => v.length <= 100 || '최대 100자까지 입력 가능합니다']" | ||||||
|  |                           @keyup.enter="addHobby" | ||||||
|  |                         /> | ||||||
|  |                       </v-col> | ||||||
|  |                       <v-col cols="1"> | ||||||
|  |                         <v-btn | ||||||
|  |                           color="primary" | ||||||
|  |                           class="mt-1" | ||||||
|  |                           block | ||||||
|  |                           :disabled="!newHobby.trim() || hobbies.length >= 10" | ||||||
|  |                           @click="addHobby" | ||||||
|  |                         > | ||||||
|  |                           추가 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-col> | ||||||
|  |                     </v-row> | ||||||
|  |                   </v-col> | ||||||
|  |                 </v-row> | ||||||
|  |  | ||||||
|  |                 <v-card | ||||||
|  |                   outlined | ||||||
|  |                   class="memory-container" | ||||||
|  |                 > | ||||||
|  |                   <v-list | ||||||
|  |                     v-if="hobbies.length > 0" | ||||||
|  |                     class="memory-list" | ||||||
|  |                   > | ||||||
|  |                     <v-list-item | ||||||
|  |                       v-for="(hobby, index) in hobbies" | ||||||
|  |                       :key="index" | ||||||
|  |                       class="memory-item" | ||||||
|  |                     > | ||||||
|  |                       <v-list-item-content> | ||||||
|  |                         <v-list-item-title class="memory-text"> | ||||||
|  |                           {{ hobby }} | ||||||
|  |                         </v-list-item-title> | ||||||
|  |                       </v-list-item-content> | ||||||
|  |                       <v-list-item-action> | ||||||
|  |                         <v-btn | ||||||
|  |                           small | ||||||
|  |                           color="error" | ||||||
|  |                           class="delete-btn" | ||||||
|  |                           @click="removeHobby(index)" | ||||||
|  |                         > | ||||||
|  |                           삭제 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-list-item-action> | ||||||
|  |                     </v-list-item> | ||||||
|  |                   </v-list> | ||||||
|  |                   <v-card-text | ||||||
|  |                     v-else | ||||||
|  |                     class="text-center grey--text" | ||||||
|  |                   > | ||||||
|  |                     취미가 없습니다. 위 입력창에서 취미를 추가해주세요. (최대 10개) | ||||||
|  |                   </v-card-text> | ||||||
|  |                 </v-card> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |  | ||||||
|  |             <!-- 가치관 목록 --> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-divider class="my-4" /> | ||||||
|  |                 <h3 class="mb-2"> | ||||||
|  |                   가치관 목록 | ||||||
|  |                 </h3> | ||||||
|  |  | ||||||
|  |                 <v-row> | ||||||
|  |                   <v-col cols="12"> | ||||||
|  |                     <v-row> | ||||||
|  |                       <v-col cols="11"> | ||||||
|  |                         <v-text-field | ||||||
|  |                           v-model="newValue" | ||||||
|  |                           label="새 가치관 추가 (최대 100자)" | ||||||
|  |                           outlined | ||||||
|  |                           dense | ||||||
|  |                           counter="100" | ||||||
|  |                           :rules="[v => v.length <= 100 || '최대 100자까지 입력 가능합니다']" | ||||||
|  |                           @keyup.enter="addValue" | ||||||
|  |                         /> | ||||||
|  |                       </v-col> | ||||||
|  |                       <v-col cols="1"> | ||||||
|  |                         <v-btn | ||||||
|  |                           color="primary" | ||||||
|  |                           class="mt-1" | ||||||
|  |                           block | ||||||
|  |                           :disabled="!newValue.trim() || values.length >= 10" | ||||||
|  |                           @click="addValue" | ||||||
|  |                         > | ||||||
|  |                           추가 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-col> | ||||||
|  |                     </v-row> | ||||||
|  |                   </v-col> | ||||||
|  |                 </v-row> | ||||||
|  |  | ||||||
|  |                 <v-card | ||||||
|  |                   outlined | ||||||
|  |                   class="memory-container" | ||||||
|  |                 > | ||||||
|  |                   <v-list | ||||||
|  |                     v-if="values.length > 0" | ||||||
|  |                     class="memory-list" | ||||||
|  |                   > | ||||||
|  |                     <v-list-item | ||||||
|  |                       v-for="(value, index) in values" | ||||||
|  |                       :key="index" | ||||||
|  |                       class="memory-item" | ||||||
|  |                     > | ||||||
|  |                       <v-list-item-content> | ||||||
|  |                         <v-list-item-title class="memory-text"> | ||||||
|  |                           {{ value }} | ||||||
|  |                         </v-list-item-title> | ||||||
|  |                       </v-list-item-content> | ||||||
|  |                       <v-list-item-action> | ||||||
|  |                         <v-btn | ||||||
|  |                           small | ||||||
|  |                           color="error" | ||||||
|  |                           class="delete-btn" | ||||||
|  |                           @click="removeValue(index)" | ||||||
|  |                         > | ||||||
|  |                           삭제 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-list-item-action> | ||||||
|  |                     </v-list-item> | ||||||
|  |                   </v-list> | ||||||
|  |                   <v-card-text | ||||||
|  |                     v-else | ||||||
|  |                     class="text-center grey--text" | ||||||
|  |                   > | ||||||
|  |                     가치관이 없습니다. 위 입력창에서 가치관을 추가해주세요. (최대 10개) | ||||||
|  |                   </v-card-text> | ||||||
|  |                 </v-card> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |  | ||||||
|  |             <!-- 목표 --> | ||||||
|  |             <v-row> | ||||||
|  |               <v-col cols="12"> | ||||||
|  |                 <v-divider class="my-4" /> | ||||||
|  |                 <h3 class="mb-2"> | ||||||
|  |                   목표 | ||||||
|  |                 </h3> | ||||||
|  |  | ||||||
|  |                 <v-row> | ||||||
|  |                   <v-col cols="12"> | ||||||
|  |                     <v-row> | ||||||
|  |                       <v-col cols="11"> | ||||||
|  |                         <v-text-field | ||||||
|  |                           v-model="newGoal" | ||||||
|  |                           label="새 목표 추가 (최대 200자)" | ||||||
|  |                           outlined | ||||||
|  |                           dense | ||||||
|  |                           counter="200" | ||||||
|  |                           :rules="[v => v.length <= 200 || '최대 200자까지 입력 가능합니다']" | ||||||
|  |                           @keyup.enter="addGoal" | ||||||
|  |                         /> | ||||||
|  |                       </v-col> | ||||||
|  |                       <v-col cols="1"> | ||||||
|  |                         <v-btn | ||||||
|  |                           color="primary" | ||||||
|  |                           class="mt-1" | ||||||
|  |                           block | ||||||
|  |                           :disabled="!newGoal.trim() || goals.length >= 10" | ||||||
|  |                           @click="addGoal" | ||||||
|  |                         > | ||||||
|  |                           추가 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-col> | ||||||
|  |                     </v-row> | ||||||
|  |                   </v-col> | ||||||
|  |                 </v-row> | ||||||
|  |  | ||||||
|  |                 <v-card | ||||||
|  |                   outlined | ||||||
|  |                   class="memory-container" | ||||||
|  |                 > | ||||||
|  |                   <v-list | ||||||
|  |                     v-if="goals.length > 0" | ||||||
|  |                     class="memory-list" | ||||||
|  |                   > | ||||||
|  |                     <v-list-item | ||||||
|  |                       v-for="(goal, index) in goals" | ||||||
|  |                       :key="index" | ||||||
|  |                       class="memory-item" | ||||||
|  |                     > | ||||||
|  |                       <v-list-item-content> | ||||||
|  |                         <v-list-item-title class="memory-text"> | ||||||
|  |                           {{ goal }} | ||||||
|  |                         </v-list-item-title> | ||||||
|  |                       </v-list-item-content> | ||||||
|  |                       <v-list-item-action> | ||||||
|  |                         <v-btn | ||||||
|  |                           small | ||||||
|  |                           color="error" | ||||||
|  |                           class="delete-btn" | ||||||
|  |                           @click="removeGoal(index)" | ||||||
|  |                         > | ||||||
|  |                           삭제 | ||||||
|  |                         </v-btn> | ||||||
|  |                       </v-list-item-action> | ||||||
|  |                     </v-list-item> | ||||||
|  |                   </v-list> | ||||||
|  |                   <v-card-text | ||||||
|  |                     v-else | ||||||
|  |                     class="text-center grey--text" | ||||||
|  |                   > | ||||||
|  |                     목표가 없습니다. 위 입력창에서 목표를 추가해주세요. (최대 10개) | ||||||
|  |                   </v-card-text> | ||||||
|  |                 </v-card> | ||||||
|  |               </v-col> | ||||||
|  |             </v-row> | ||||||
|  |  | ||||||
|             <!-- 기억/메모리 --> |             <!-- 기억/메모리 --> | ||||||
|             <v-row> |             <v-row> | ||||||
|               <v-col cols="12"> |               <v-col cols="12"> | ||||||
| @@ -402,10 +702,17 @@ export default { | |||||||
|       isFormValid: false, |       isFormValid: false, | ||||||
|       isEdit: false, |       isEdit: false, | ||||||
|       previewImage: null, |       previewImage: null, | ||||||
|       birthDateMenu: false, |  | ||||||
|       newMemory: '', |       newMemory: '', | ||||||
|  |       newRelationship: '', | ||||||
|  |       newHobby: '', | ||||||
|  |       newValue: '', | ||||||
|  |       newGoal: '', | ||||||
|       tags: [], |       tags: [], | ||||||
|       memories: [], |       memories: [], | ||||||
|  |       relationships: [], | ||||||
|  |       hobbies: [], | ||||||
|  |       values: [], | ||||||
|  |       goals: [], | ||||||
|       character: { |       character: { | ||||||
|         id: null, |         id: null, | ||||||
|         name: '', |         name: '', | ||||||
| @@ -415,17 +722,25 @@ export default { | |||||||
|         isActive: true, |         isActive: true, | ||||||
|         createdAt: '', |         createdAt: '', | ||||||
|         gender: '', |         gender: '', | ||||||
|         birthDate: null, |         age: '', | ||||||
|         mbti: '', |         mbti: '', | ||||||
|         ageRestricted: false, |  | ||||||
|         worldView: '', |         worldView: '', | ||||||
|         relationships: '', |         relationships: '', | ||||||
|         personality: '', |         personality: '', | ||||||
|         speechPattern: '', |         speechPattern: '', | ||||||
|  |         conversationStyle: '', | ||||||
|  |         appearance: '', | ||||||
|         systemPrompt: '', |         systemPrompt: '', | ||||||
|         tags: [], |         tags: [], | ||||||
|         memories: [] |         memories: [] | ||||||
|       }, |       }, | ||||||
|  |       ageRules: [ | ||||||
|  |         v => !!v || '나이를 입력하세요', | ||||||
|  |         v => (v && !isNaN(v) && parseInt(v) >= 0) || '유효한 나이를 입력하세요' | ||||||
|  |       ], | ||||||
|  |       tagRules: [ | ||||||
|  |         v => v.length <= 20 || '태그는 최대 20개까지 등록 가능합니다' | ||||||
|  |       ], | ||||||
|       nameRules: [ |       nameRules: [ | ||||||
|         v => !!v || '이름을 입력하세요', |         v => !!v || '이름을 입력하세요', | ||||||
|         v => (v && v.trim().length > 0) || '이름을 입력하세요' |         v => (v && v.trim().length > 0) || '이름을 입력하세요' | ||||||
| @@ -448,10 +763,6 @@ export default { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   computed: { |   computed: { | ||||||
|     formattedBirthDate() { |  | ||||||
|       if (!this.character.birthDate) return ''; |  | ||||||
|       return this.character.birthDate; |  | ||||||
|     } |  | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   watch: { |   watch: { | ||||||
| @@ -487,6 +798,13 @@ export default { | |||||||
|       this.$router.push('/character'); |       this.$router.push('/character'); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     validateNumberInput() { | ||||||
|  |       // 숫자만 입력 가능하도록 처리 | ||||||
|  |       if (this.character.age && isNaN(this.character.age)) { | ||||||
|  |         this.character.age = this.character.age.replace(/[^0-9]/g, ''); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     createImagePreview(file) { |     createImagePreview(file) { | ||||||
|       if (!file) return; |       if (!file) return; | ||||||
|  |  | ||||||
| @@ -500,10 +818,21 @@ export default { | |||||||
|     addTag() { |     addTag() { | ||||||
|       const lastTag = this.tags[this.tags.length - 1]; |       const lastTag = this.tags[this.tags.length - 1]; | ||||||
|       if (lastTag && typeof lastTag === 'string' && lastTag.trim()) { |       if (lastTag && typeof lastTag === 'string' && lastTag.trim()) { | ||||||
|         this.tags.splice(this.tags.length - 1, 1, lastTag.trim()); |         // 띄어쓰기 제거 및 50자 제한 | ||||||
|  |         let processedTag = lastTag.trim().replace(/\s+/g, ''); | ||||||
|  |         if (processedTag.length > 50) { | ||||||
|  |           processedTag = processedTag.substring(0, 50); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 태그 개수 제한 (20개) | ||||||
|  |         if (this.tags.length <= 20) { | ||||||
|  |           this.tags.splice(this.tags.length - 1, 1, processedTag); | ||||||
|           this.$nextTick(() => { |           this.$nextTick(() => { | ||||||
|             this.tags.push(''); |             this.tags.push(''); | ||||||
|           }); |           }); | ||||||
|  |         } else { | ||||||
|  |           this.notifyError('태그는 최대 20개까지 등록 가능합니다.'); | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| @@ -513,7 +842,7 @@ export default { | |||||||
|  |  | ||||||
|     addMemory() { |     addMemory() { | ||||||
|       if (this.newMemory.trim()) { |       if (this.newMemory.trim()) { | ||||||
|         this.memories.push(this.newMemory.trim()); |         this.memories.unshift(this.newMemory.trim()); | ||||||
|         this.newMemory = ''; |         this.newMemory = ''; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
| @@ -522,6 +851,86 @@ export default { | |||||||
|       this.memories.splice(index, 1); |       this.memories.splice(index, 1); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     addRelationship() { | ||||||
|  |       if (this.newRelationship.trim()) { | ||||||
|  |         if (this.relationships.length >= 10) { | ||||||
|  |           this.notifyError('인물 관계는 최대 10개까지 등록 가능합니다.'); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.newRelationship.length > 200) { | ||||||
|  |           this.newRelationship = this.newRelationship.substring(0, 200); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.relationships.unshift(this.newRelationship.trim()); | ||||||
|  |         this.newRelationship = ''; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     removeRelationship(index) { | ||||||
|  |       this.relationships.splice(index, 1); | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     addHobby() { | ||||||
|  |       if (this.newHobby.trim()) { | ||||||
|  |         if (this.hobbies.length >= 10) { | ||||||
|  |           this.notifyError('취미는 최대 10개까지 등록 가능합니다.'); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.newHobby.length > 100) { | ||||||
|  |           this.newHobby = this.newHobby.substring(0, 100); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.hobbies.unshift(this.newHobby.trim()); | ||||||
|  |         this.newHobby = ''; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     removeHobby(index) { | ||||||
|  |       this.hobbies.splice(index, 1); | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     addValue() { | ||||||
|  |       if (this.newValue.trim()) { | ||||||
|  |         if (this.values.length >= 10) { | ||||||
|  |           this.notifyError('가치관은 최대 10개까지 등록 가능합니다.'); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.newValue.length > 100) { | ||||||
|  |           this.newValue = this.newValue.substring(0, 100); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.values.unshift(this.newValue.trim()); | ||||||
|  |         this.newValue = ''; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     removeValue(index) { | ||||||
|  |       this.values.splice(index, 1); | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     addGoal() { | ||||||
|  |       if (this.newGoal.trim()) { | ||||||
|  |         if (this.goals.length >= 10) { | ||||||
|  |           this.notifyError('목표는 최대 10개까지 등록 가능합니다.'); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.newGoal.length > 200) { | ||||||
|  |           this.newGoal = this.newGoal.substring(0, 200); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.goals.unshift(this.newGoal.trim()); | ||||||
|  |         this.newGoal = ''; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     removeGoal(index) { | ||||||
|  |       this.goals.splice(index, 1); | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     async loadCharacter(id) { |     async loadCharacter(id) { | ||||||
|       this.isLoading = true; |       this.isLoading = true; | ||||||
|       try { |       try { | ||||||
| @@ -538,9 +947,13 @@ export default { | |||||||
|             image: null        // 파일 입력은 초기화 |             image: null        // 파일 입력은 초기화 | ||||||
|           }; |           }; | ||||||
|  |  | ||||||
|           // 태그와 메모리 설정 |           // 태그, 메모리, 인물관계, 취미, 가치관, 목표 설정 | ||||||
|           this.tags = data.tags || []; |           this.tags = data.tags || []; | ||||||
|           this.memories = data.memories || []; |           this.memories = data.memories || []; | ||||||
|  |           this.relationships = data.relationships || []; | ||||||
|  |           this.hobbies = data.hobbies || []; | ||||||
|  |           this.values = data.values || []; | ||||||
|  |           this.goals = data.goals || []; | ||||||
|         } else { |         } else { | ||||||
|           this.notifyError('캐릭터 정보를 불러올 수 없습니다.'); |           this.notifyError('캐릭터 정보를 불러올 수 없습니다.'); | ||||||
|         } |         } | ||||||
| @@ -563,9 +976,13 @@ export default { | |||||||
|       this.isLoading = true; |       this.isLoading = true; | ||||||
|  |  | ||||||
|       try { |       try { | ||||||
|         // 태그와 메모리 데이터 설정 |         // 태그, 메모리, 인물관계, 취미, 가치관, 목표 데이터 설정 | ||||||
|         this.character.tags = this.tags.filter(tag => tag && typeof tag === 'string' && tag.trim()); |         this.character.tags = this.tags.filter(tag => tag && typeof tag === 'string' && tag.trim()); | ||||||
|         this.character.memories = [...this.memories]; |         this.character.memories = [...this.memories]; | ||||||
|  |         this.character.relationships = [...this.relationships]; | ||||||
|  |         this.character.hobbies = [...this.hobbies]; | ||||||
|  |         this.character.values = [...this.values]; | ||||||
|  |         this.character.goals = [...this.goals]; | ||||||
|  |  | ||||||
|         let response; |         let response; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user