feat(chat): 캐릭터 리스트, 추가/수정 폼, 배너

- response의 데이터 구조에 맞춰서 코드 수정
This commit is contained in:
Yu Sung
2025-08-12 21:09:08 +09:00
parent a3e82a81f8
commit ba248f7680
4 changed files with 119 additions and 61 deletions

View File

@@ -135,7 +135,7 @@
md="6"
>
<v-select
v-model="character.type"
v-model="character.characterType"
:items="typeOptions"
label="캐릭터 유형"
outlined
@@ -235,7 +235,7 @@
<v-row>
<v-col cols="12">
<v-textarea
v-model="character.conversationStyle"
v-model="character.speechStyle"
label="대화 스타일 (최대 200자)"
outlined
auto-grow
@@ -962,11 +962,11 @@ export default {
gender: '',
age: '',
mbti: '',
type: '',
characterType: '',
originalTitle: '',
originalLink: '',
speechPattern: '',
conversationStyle: '',
speechStyle: '',
appearance: '',
systemPrompt: '',
tags: [],
@@ -1277,6 +1277,27 @@ export default {
this.backgrounds.splice(index, 1);
},
// 공백과 null을 동일하게 취급하는 비교 함수
areEqualConsideringBlankNull(a, b) {
const a1 = (a === null || a === undefined) ? '' : a;
const b1 = (b === null || b === undefined) ? '' : b;
return a1 === b1;
},
// 로드된 캐릭터 데이터에서 null을 빈 문자열로 변환 (UI 표시용)
normalizeCharacterData(data) {
const result = { ...data };
const simpleFields = [
'name', 'systemPrompt', 'description', 'age', 'gender', 'mbti',
'characterType', 'originalTitle', 'originalLink', 'speechPattern',
'speechStyle', 'appearance', 'imageUrl'
];
simpleFields.forEach(f => {
if (result[f] == null) result[f] = '';
});
return result;
},
// 변경된 필드만 추출하는 함수
getChangedFields() {
if (!this.originalCharacter || !this.isEdit) {
@@ -1288,11 +1309,11 @@ export default {
age: this.character.age,
gender: this.character.gender,
mbti: this.character.mbti,
type: this.character.type,
characterType: this.character.characterType,
originalTitle: this.character.originalTitle,
originalLink: this.character.originalLink,
speechPattern: this.character.speechPattern,
speechStyle: this.character.conversationStyle,
speechStyle: this.character.speechStyle,
appearance: this.character.appearance,
tags: this.character.tags || [],
hobbies: this.character.hobbies || [],
@@ -1313,26 +1334,21 @@ export default {
// 기본 필드 비교
const simpleFields = [
'name', 'description', 'age', 'gender', 'mbti', 'type', 'originalTitle', 'originalLink',
'speechPattern', 'isActive'
'name', 'description', 'age', 'gender', 'mbti', 'characterType', 'originalTitle', 'originalLink',
'speechPattern', 'speechStyle', 'isActive'
];
simpleFields.forEach(field => {
if (this.character[field] !== this.originalCharacter[field]) {
if (!this.areEqualConsideringBlankNull(this.character[field], this.originalCharacter[field])) {
changedFields[field] = this.character[field];
}
});
// 특수 필드 매핑 처리 (conversationStyle은 API에서 speechStyle로 사용됨)
if (this.character.conversationStyle !== this.originalCharacter.conversationStyle) {
changedFields.speechStyle = this.character.conversationStyle;
}
if (this.character.systemPrompt !== this.originalCharacter.systemPrompt) {
if (!this.areEqualConsideringBlankNull(this.character.systemPrompt, this.originalCharacter.systemPrompt)) {
changedFields.systemPrompt = this.character.systemPrompt;
}
if (this.character.appearance !== this.originalCharacter.appearance) {
if (!this.areEqualConsideringBlankNull(this.character.appearance, this.originalCharacter.appearance)) {
changedFields.appearance = this.character.appearance;
}
@@ -1377,16 +1393,17 @@ export default {
const response = await getCharacter(id);
// API 응답에서 캐릭터 정보 설정
if (response && response.success === true && response.data) {
const data = response.data;
if (response && response.status === 200 && response.data.success === true) {
const data = response.data.data;
// 원본 데이터 저장 (깊은 복사)
this.originalCharacter = JSON.parse(JSON.stringify(data));
// 기본 데이터 설정
// 기본 데이터 설정 (null 값을 UI 표시를 위해 빈 문자열로 변환)
const normalized = this.normalizeCharacterData(data);
this.character = {
...this.character, // 기본 구조 유지
...data, // API 응답 데이터로 덮어쓰기
...normalized, // API 응답 데이터(정규화)로 덮어쓰기
image: null // 파일 입력은 초기화
};
@@ -1444,11 +1461,11 @@ export default {
response = await createCharacter(characterWithoutId);
}
if (response && response.success === true && response.data) {
this.notifySuccess(this.isEdit ? '캐릭터가 수정되었습니다.' : '캐릭터가 등록되었습니다.');
this.goBack();
if (response && response.status === 200 && response.data && response.data.success === true) {
this.notifySuccess(this.isEdit ? '캐릭터가 수정되었습니다.' : '캐릭터가 등록되었습니다.');
this.goBack();
} else {
this.notifyError('응답 데이터가 없습니다.');
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.');
}
} catch (e) {
console.error('캐릭터 저장 오류:', e);