feat(character): 캐릭터 폼에 성격 특성, 세계관, 기억 기능 개선
- 성격 특성(personalities): 제목(trait)과 설명(description) 입력 UI 구현, 최대 10개 - 세계관(배경)(backgrounds): 제목(topic)과 설명(description) 입력 UI 구현, 최대 10개 - 기억(memories): 제목(title), 기억(content), 감정(emotion) 입력 UI 구현, 최대 20개
This commit is contained in:
parent
72b1627f3f
commit
7f56d0b423
|
@ -163,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개, 띄어쓰기 불가)
|
태그를 입력하고 엔터를 누르면 추가됩니다. (50자 이내, 최대 20개)
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
@ -178,31 +178,6 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- 세계관 (배경이야기) -->
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12">
|
|
||||||
<v-textarea
|
|
||||||
v-model="character.worldView"
|
|
||||||
label="세계관 (배경이야기)"
|
|
||||||
outlined
|
|
||||||
auto-grow
|
|
||||||
rows="4"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- 성격 특성 -->
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12">
|
|
||||||
<v-textarea
|
|
||||||
v-model="character.personality"
|
|
||||||
label="성격 특성"
|
|
||||||
outlined
|
|
||||||
auto-grow
|
|
||||||
rows="4"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- 말투/특징적 표현 -->
|
<!-- 말투/특징적 표현 -->
|
||||||
<v-row>
|
<v-row>
|
||||||
|
@ -575,6 +550,191 @@
|
||||||
</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="5">
|
||||||
|
<v-text-field
|
||||||
|
v-model="newBackgroundTopic"
|
||||||
|
label="주제 (최대 100자)"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
counter="100"
|
||||||
|
:rules="[v => v.length <= 100 || '최대 100자까지 입력 가능합니다']"
|
||||||
|
@keyup.enter="addBackground"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-textarea
|
||||||
|
v-model="newBackgroundDescription"
|
||||||
|
label="배경 설명 (최대 1000자)"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
auto-grow
|
||||||
|
rows="2"
|
||||||
|
counter="1000"
|
||||||
|
:rules="[v => v.length <= 1000 || '최대 1000자까지 입력 가능합니다']"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="1">
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
class="mt-1"
|
||||||
|
block
|
||||||
|
:disabled="!newBackgroundTopic.trim() || !newBackgroundDescription.trim() || backgrounds.length >= 10"
|
||||||
|
@click="addBackground"
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-card
|
||||||
|
outlined
|
||||||
|
class="memory-container"
|
||||||
|
>
|
||||||
|
<v-list
|
||||||
|
v-if="backgrounds.length > 0"
|
||||||
|
class="memory-list"
|
||||||
|
>
|
||||||
|
<v-list-item
|
||||||
|
v-for="(background, index) in backgrounds"
|
||||||
|
:key="index"
|
||||||
|
class="memory-item"
|
||||||
|
>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title class="memory-text font-weight-bold">
|
||||||
|
{{ background.topic }}
|
||||||
|
</v-list-item-title>
|
||||||
|
<v-list-item-subtitle class="memory-text mt-1">
|
||||||
|
{{ background.description }}
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
</v-list-item-content>
|
||||||
|
<v-list-item-action>
|
||||||
|
<v-btn
|
||||||
|
small
|
||||||
|
color="error"
|
||||||
|
class="delete-btn"
|
||||||
|
@click="removeBackground(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="5">
|
||||||
|
<v-text-field
|
||||||
|
v-model="newPersonalityTrait"
|
||||||
|
label="특성 제목 (최대 100자)"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
counter="100"
|
||||||
|
:rules="[v => v.length <= 100 || '최대 100자까지 입력 가능합니다']"
|
||||||
|
@keyup.enter="addPersonality"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-textarea
|
||||||
|
v-model="newPersonalityDescription"
|
||||||
|
label="특성 설명 (최대 500자)"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
auto-grow
|
||||||
|
rows="2"
|
||||||
|
counter="500"
|
||||||
|
:rules="[v => v.length <= 500 || '최대 500자까지 입력 가능합니다']"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="1">
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
class="mt-1"
|
||||||
|
block
|
||||||
|
:disabled="!newPersonalityTrait.trim() || !newPersonalityDescription.trim() || personalities.length >= 10"
|
||||||
|
@click="addPersonality"
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-card
|
||||||
|
outlined
|
||||||
|
class="memory-container"
|
||||||
|
>
|
||||||
|
<v-list
|
||||||
|
v-if="personalities.length > 0"
|
||||||
|
class="memory-list"
|
||||||
|
>
|
||||||
|
<v-list-item
|
||||||
|
v-for="(personality, index) in personalities"
|
||||||
|
:key="index"
|
||||||
|
class="memory-item"
|
||||||
|
>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title class="memory-text font-weight-bold">
|
||||||
|
{{ personality.trait }}
|
||||||
|
</v-list-item-title>
|
||||||
|
<v-list-item-subtitle class="memory-text mt-1">
|
||||||
|
{{ personality.description }}
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
</v-list-item-content>
|
||||||
|
<v-list-item-action>
|
||||||
|
<v-btn
|
||||||
|
small
|
||||||
|
color="error"
|
||||||
|
class="delete-btn"
|
||||||
|
@click="removePersonality(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">
|
||||||
|
@ -586,12 +746,36 @@
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="11">
|
<v-col cols="4">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="newMemory"
|
v-model="newMemoryTitle"
|
||||||
label="새 기억 추가"
|
label="제목 (최대 100자)"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
counter="100"
|
||||||
|
:rules="[v => v.length <= 100 || '최대 100자까지 입력 가능합니다']"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="4">
|
||||||
|
<v-textarea
|
||||||
|
v-model="newMemoryContent"
|
||||||
|
label="기억 내용 (최대 1000자)"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
auto-grow
|
||||||
|
rows="2"
|
||||||
|
counter="1000"
|
||||||
|
:rules="[v => v.length <= 1000 || '최대 1000자까지 입력 가능합니다']"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="3">
|
||||||
|
<v-text-field
|
||||||
|
v-model="newMemoryEmotion"
|
||||||
|
label="감정 (최대 50자)"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
counter="50"
|
||||||
|
:rules="[v => v.length <= 50 || '최대 50자까지 입력 가능합니다']"
|
||||||
@keyup.enter="addMemory"
|
@keyup.enter="addMemory"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
@ -600,7 +784,7 @@
|
||||||
color="primary"
|
color="primary"
|
||||||
class="mt-1"
|
class="mt-1"
|
||||||
block
|
block
|
||||||
:disabled="!newMemory.trim()"
|
:disabled="!newMemoryTitle.trim() || !newMemoryContent.trim() || memories.length >= 20"
|
||||||
@click="addMemory"
|
@click="addMemory"
|
||||||
>
|
>
|
||||||
추가
|
추가
|
||||||
|
@ -624,9 +808,18 @@
|
||||||
class="memory-item"
|
class="memory-item"
|
||||||
>
|
>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title class="memory-text">
|
<v-list-item-title class="memory-text font-weight-bold">
|
||||||
{{ memory }}
|
{{ memory.title }}
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
|
<v-list-item-subtitle class="memory-text mt-1">
|
||||||
|
{{ memory.content }}
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
<v-list-item-subtitle
|
||||||
|
v-if="memory.emotion"
|
||||||
|
class="memory-text mt-1 font-italic"
|
||||||
|
>
|
||||||
|
감정: {{ memory.emotion }}
|
||||||
|
</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action>
|
<v-list-item-action>
|
||||||
<v-btn
|
<v-btn
|
||||||
|
@ -644,7 +837,7 @@
|
||||||
v-else
|
v-else
|
||||||
class="text-center grey--text"
|
class="text-center grey--text"
|
||||||
>
|
>
|
||||||
기억이 없습니다. 위 입력창에서 기억을 추가해주세요.
|
기억이 없습니다. 위 입력창에서 기억을 추가해주세요. (최대 20개)
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
@ -702,17 +895,25 @@ export default {
|
||||||
isFormValid: false,
|
isFormValid: false,
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
previewImage: null,
|
previewImage: null,
|
||||||
newMemory: '',
|
newMemoryTitle: '',
|
||||||
|
newMemoryContent: '',
|
||||||
|
newMemoryEmotion: '',
|
||||||
newRelationship: '',
|
newRelationship: '',
|
||||||
newHobby: '',
|
newHobby: '',
|
||||||
newValue: '',
|
newValue: '',
|
||||||
newGoal: '',
|
newGoal: '',
|
||||||
|
newPersonalityTrait: '',
|
||||||
|
newPersonalityDescription: '',
|
||||||
|
newBackgroundTopic: '',
|
||||||
|
newBackgroundDescription: '',
|
||||||
tags: [],
|
tags: [],
|
||||||
memories: [],
|
memories: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
hobbies: [],
|
hobbies: [],
|
||||||
values: [],
|
values: [],
|
||||||
goals: [],
|
goals: [],
|
||||||
|
personalities: [],
|
||||||
|
backgrounds: [],
|
||||||
character: {
|
character: {
|
||||||
id: null,
|
id: null,
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -841,9 +1042,40 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
addMemory() {
|
addMemory() {
|
||||||
if (this.newMemory.trim()) {
|
if (this.newMemoryTitle.trim() && this.newMemoryContent.trim()) {
|
||||||
this.memories.unshift(this.newMemory.trim());
|
if (this.memories.length >= 20) {
|
||||||
this.newMemory = '';
|
this.notifyError('기억은 최대 20개까지 등록 가능합니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 글자수 제한 적용
|
||||||
|
let title = this.newMemoryTitle.trim();
|
||||||
|
let content = this.newMemoryContent.trim();
|
||||||
|
let emotion = this.newMemoryEmotion.trim();
|
||||||
|
|
||||||
|
if (title.length > 100) {
|
||||||
|
title = title.substring(0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.length > 1000) {
|
||||||
|
content = content.substring(0, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emotion.length > 50) {
|
||||||
|
emotion = emotion.substring(0, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새 기억 객체 생성 및 추가
|
||||||
|
this.memories.unshift({
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
emotion
|
||||||
|
});
|
||||||
|
|
||||||
|
// 입력 필드 초기화
|
||||||
|
this.newMemoryTitle = '';
|
||||||
|
this.newMemoryContent = '';
|
||||||
|
this.newMemoryEmotion = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -931,6 +1163,76 @@ export default {
|
||||||
this.goals.splice(index, 1);
|
this.goals.splice(index, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addPersonality() {
|
||||||
|
if (this.newPersonalityTrait.trim() && this.newPersonalityDescription.trim()) {
|
||||||
|
if (this.personalities.length >= 10) {
|
||||||
|
this.notifyError('성격 특성은 최대 10개까지 등록 가능합니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 글자수 제한 적용
|
||||||
|
let trait = this.newPersonalityTrait.trim();
|
||||||
|
let description = this.newPersonalityDescription.trim();
|
||||||
|
|
||||||
|
if (trait.length > 100) {
|
||||||
|
trait = trait.substring(0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description.length > 500) {
|
||||||
|
description = description.substring(0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새 성격 특성 객체 생성 및 추가
|
||||||
|
this.personalities.unshift({
|
||||||
|
trait,
|
||||||
|
description
|
||||||
|
});
|
||||||
|
|
||||||
|
// 입력 필드 초기화
|
||||||
|
this.newPersonalityTrait = '';
|
||||||
|
this.newPersonalityDescription = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removePersonality(index) {
|
||||||
|
this.personalities.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
addBackground() {
|
||||||
|
if (this.newBackgroundTopic.trim() && this.newBackgroundDescription.trim()) {
|
||||||
|
if (this.backgrounds.length >= 10) {
|
||||||
|
this.notifyError('세계관 정보는 최대 10개까지 등록 가능합니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 글자수 제한 적용
|
||||||
|
let topic = this.newBackgroundTopic.trim();
|
||||||
|
let description = this.newBackgroundDescription.trim();
|
||||||
|
|
||||||
|
if (topic.length > 100) {
|
||||||
|
topic = topic.substring(0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description.length > 1000) {
|
||||||
|
description = description.substring(0, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새 세계관 객체 생성 및 추가
|
||||||
|
this.backgrounds.unshift({
|
||||||
|
topic,
|
||||||
|
description
|
||||||
|
});
|
||||||
|
|
||||||
|
// 입력 필드 초기화
|
||||||
|
this.newBackgroundTopic = '';
|
||||||
|
this.newBackgroundDescription = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeBackground(index) {
|
||||||
|
this.backgrounds.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
async loadCharacter(id) {
|
async loadCharacter(id) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
try {
|
try {
|
||||||
|
@ -947,13 +1249,15 @@ 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.relationships = data.relationships || [];
|
||||||
this.hobbies = data.hobbies || [];
|
this.hobbies = data.hobbies || [];
|
||||||
this.values = data.values || [];
|
this.values = data.values || [];
|
||||||
this.goals = data.goals || [];
|
this.goals = data.goals || [];
|
||||||
|
this.personalities = data.personalities || [];
|
||||||
|
this.backgrounds = data.backgrounds || [];
|
||||||
} else {
|
} else {
|
||||||
this.notifyError('캐릭터 정보를 불러올 수 없습니다.');
|
this.notifyError('캐릭터 정보를 불러올 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
@ -976,13 +1280,15 @@ 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.relationships = [...this.relationships];
|
||||||
this.character.hobbies = [...this.hobbies];
|
this.character.hobbies = [...this.hobbies];
|
||||||
this.character.values = [...this.values];
|
this.character.values = [...this.values];
|
||||||
this.character.goals = [...this.goals];
|
this.character.goals = [...this.goals];
|
||||||
|
this.character.personalities = [...this.personalities];
|
||||||
|
this.character.backgrounds = [...this.backgrounds];
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue