From 7ed23047e9349fdea2cca71cb3aef200f004b4b6 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Fri, 8 Aug 2025 22:03:11 +0900 Subject: [PATCH] =?UTF-8?q?feat(character-banner):=20=EC=BA=90=EB=A6=AD?= =?UTF-8?q?=ED=84=B0=20=EB=B0=B0=EB=84=88=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 리스트, 등록, 수정, 삭제 추가 - 페이징은 스크롤 로딩으로 구현 --- src/api/character.js | 73 +++- src/router/index.js | 11 +- src/views/Chat/CharacterBanner.vue | 548 +++++++++++++++++++++++++++++ 3 files changed, 622 insertions(+), 10 deletions(-) create mode 100644 src/views/Chat/CharacterBanner.vue diff --git a/src/api/character.js b/src/api/character.js index db59136..2c1f32c 100644 --- a/src/api/character.js +++ b/src/api/character.js @@ -8,9 +8,9 @@ async function getCharacterList(page = 1, size = 20) { } // 캐릭터 검색 -async function searchCharacters(keyword, page = 1, size = 20) { - return Vue.axios.get('/api/admin/chat/character/search', { - params: { keyword, page, size } +async function searchCharacters(searchTerm, page = 1, size = 20) { + return Vue.axios.get('/admin/chat/banner/search-character', { + params: { searchTerm, page: page - 1, size } }) } @@ -74,10 +74,75 @@ async function updateCharacter(characterData, image = null) { }) } +// 캐릭터 배너 리스트 조회 +async function getCharacterBannerList(page = 1, size = 20) { + return Vue.axios.get('/admin/chat/banner/list', { + params: { page: page - 1, size } + }) +} + +// 캐릭터 배너 등록 +async function createCharacterBanner(bannerData) { + const formData = new FormData() + + // 이미지 FormData에 추가 + if (bannerData.image) formData.append('image', bannerData.image) + + // 캐릭터 ID를 JSON 문자열로 변환하여 request 필드에 추가 + const requestData = { + characterId: bannerData.characterId + } + + formData.append('request', JSON.stringify(requestData)) + + return Vue.axios.post('/admin/chat/banner/register', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +// 캐릭터 배너 수정 +async function updateCharacterBanner(bannerData) { + const formData = new FormData() + + // 이미지가 있는 경우에만 FormData에 추가 + if (bannerData.image) formData.append('image', bannerData.image) + + // 캐릭터 ID와 배너 ID를 JSON 문자열로 변환하여 request 필드에 추가 + const requestData = { + characterId: bannerData.characterId, + bannerId: bannerData.bannerId + } + + formData.append('request', JSON.stringify(requestData)) + + return Vue.axios.put('/admin/chat/banner/update', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +// 캐릭터 배너 삭제 +async function deleteCharacterBanner(bannerId) { + return Vue.axios.delete(`/admin/chat/banner/${bannerId}`) +} + +// 캐릭터 배너 순서 변경 +async function updateCharacterBannerOrder(bannerIds) { + return Vue.axios.put('/admin/chat/banner/orders', {ids: bannerIds}) +} + export { getCharacterList, searchCharacters, getCharacter, createCharacter, - updateCharacter + updateCharacter, + getCharacterBannerList, + createCharacterBanner, + updateCharacterBanner, + deleteCharacterBanner, + updateCharacterBannerOrder } diff --git a/src/router/index.js b/src/router/index.js index 05ac4f3..7888f8c 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -265,12 +265,11 @@ const routes = [ name: 'CharacterForm', component: () => import(/* webpackChunkName: "character" */ '../views/Chat/CharacterForm.vue') }, - // TODO: CharacterBanner 컴포넌트가 구현되면 아래 라우트 주석 해제 - // { - // path: '/character/banner', - // name: 'CharacterBanner', - // component: () => import(/* webpackChunkName: "character" */ '../views/Chat/CharacterBanner.vue') - // }, + { + path: '/character/banner', + name: 'CharacterBanner', + component: () => import(/* webpackChunkName: "character" */ '../views/Chat/CharacterBanner.vue') + }, ] }, { diff --git a/src/views/Chat/CharacterBanner.vue b/src/views/Chat/CharacterBanner.vue new file mode 100644 index 0000000..530d4ab --- /dev/null +++ b/src/views/Chat/CharacterBanner.vue @@ -0,0 +1,548 @@ + + + + +