릴리스: v1.3.53 관리자 featured/custom item 로직 분리
This commit is contained in:
203
frontend/src/composables/useAdminCustomItems.js
Normal file
203
frontend/src/composables/useAdminCustomItems.js
Normal file
@@ -0,0 +1,203 @@
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
export function useAdminCustomItems({
|
||||
api,
|
||||
toast,
|
||||
customItems,
|
||||
customItemPage,
|
||||
customItemLimit,
|
||||
customItemPageCount,
|
||||
customItemQuery,
|
||||
customItemOrphanOnly,
|
||||
customItemModalOpen,
|
||||
customItemDeleteModalOpen,
|
||||
customItemModalHistoryActive,
|
||||
modalTargetCustomItem,
|
||||
customItemModalDraftLabel,
|
||||
customItemModalLabelSaving,
|
||||
customItemModalTargetGameId,
|
||||
customItemModalGameQuery,
|
||||
customItemModalGameSort,
|
||||
games,
|
||||
selectedGameId,
|
||||
refreshCustomItems,
|
||||
loadGame,
|
||||
setTab,
|
||||
selectAdminGame,
|
||||
resetMessages,
|
||||
success,
|
||||
error,
|
||||
}) {
|
||||
function submitCustomItemSearch() {
|
||||
customItemPage.value = 1
|
||||
refreshCustomItems()
|
||||
}
|
||||
|
||||
function toggleCustomItemOrphanOnly() {
|
||||
customItemPage.value = 1
|
||||
refreshCustomItems()
|
||||
}
|
||||
|
||||
function changeCustomItemLimit(limit) {
|
||||
customItemLimit.value = limit
|
||||
customItemPage.value = 1
|
||||
refreshCustomItems()
|
||||
}
|
||||
|
||||
function moveCustomItemPage(direction) {
|
||||
const nextPage = customItemPage.value + direction
|
||||
if (nextPage < 1 || nextPage > customItemPageCount.value) return
|
||||
customItemPage.value = nextPage
|
||||
refreshCustomItems()
|
||||
}
|
||||
|
||||
function pushCustomItemModalHistoryState() {
|
||||
if (typeof window === 'undefined') return
|
||||
window.history.pushState({ ...(window.history.state || {}), adminCustomItemModal: true }, '', window.location.href)
|
||||
customItemModalHistoryActive.value = true
|
||||
}
|
||||
|
||||
function openCustomItemModal(item) {
|
||||
modalTargetCustomItem.value = item || null
|
||||
customItemModalDraftLabel.value = item?.label || ''
|
||||
customItemModalTargetGameId.value = ''
|
||||
customItemModalGameQuery.value = ''
|
||||
customItemModalGameSort.value = 'recent'
|
||||
customItemModalOpen.value = true
|
||||
pushCustomItemModalHistoryState()
|
||||
}
|
||||
|
||||
function closeCustomItemModal({ fromPopState = false } = {}) {
|
||||
customItemModalOpen.value = false
|
||||
customItemDeleteModalOpen.value = false
|
||||
modalTargetCustomItem.value = null
|
||||
customItemModalDraftLabel.value = ''
|
||||
customItemModalLabelSaving.value = false
|
||||
customItemModalTargetGameId.value = ''
|
||||
customItemModalGameQuery.value = ''
|
||||
customItemModalGameSort.value = 'recent'
|
||||
|
||||
if (fromPopState) {
|
||||
customItemModalHistoryActive.value = false
|
||||
return
|
||||
}
|
||||
|
||||
if (customItemModalHistoryActive.value && typeof window !== 'undefined') {
|
||||
customItemModalHistoryActive.value = false
|
||||
window.history.back()
|
||||
}
|
||||
}
|
||||
|
||||
function openCustomItemDeleteModal(item) {
|
||||
if (!item) return
|
||||
if (item.sourceType === 'user' && (item.usageCount > 0 || item.linkedGames.length > 0)) {
|
||||
error.value = '사용 중이거나 템플릿에 연결된 사용자 업로드 이미지는 먼저 참조를 정리해야 삭제할 수 있어요.'
|
||||
return
|
||||
}
|
||||
modalTargetCustomItem.value = item
|
||||
customItemDeleteModalOpen.value = true
|
||||
}
|
||||
|
||||
function closeCustomItemDeleteModal() {
|
||||
customItemDeleteModalOpen.value = false
|
||||
}
|
||||
|
||||
function jumpToGameAdmin(gameId) {
|
||||
if (!gameId) return
|
||||
closeCustomItemModal()
|
||||
setTab('game-admin')
|
||||
nextTick(() => {
|
||||
selectAdminGame(gameId)
|
||||
})
|
||||
}
|
||||
|
||||
async function removeCustomItem(item = modalTargetCustomItem.value) {
|
||||
resetMessages()
|
||||
if (!item) return
|
||||
if (item.sourceType === 'user' && (item.usageCount > 0 || item.linkedGames.length > 0)) {
|
||||
error.value = '사용 중이거나 템플릿에 연결된 사용자 업로드 이미지는 먼저 참조를 정리해야 삭제할 수 있어요.'
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await api.deleteAdminCustomItem(item.id)
|
||||
closeCustomItemDeleteModal()
|
||||
closeCustomItemModal()
|
||||
await refreshCustomItems()
|
||||
success.value = item.sourceType === 'template' ? '선택한 템플릿 아이템을 제거했어요.' : '사용자 업로드 이미지를 삭제했어요.'
|
||||
} catch (e) {
|
||||
error.value = item.sourceType === 'template' ? '템플릿 아이템 제거에 실패했어요.' : '사용자 업로드 이미지 삭제에 실패했어요.'
|
||||
}
|
||||
}
|
||||
|
||||
async function removeUnusedCustomItems() {
|
||||
resetMessages()
|
||||
const ok = window.confirm('현재 검색 조건에 맞는 미사용 커스텀 이미지를 모두 삭제할까요?')
|
||||
if (!ok) return
|
||||
|
||||
try {
|
||||
const data = await api.deleteAdminUnusedCustomItems({ q: customItemQuery.value })
|
||||
await refreshCustomItems()
|
||||
success.value = `${data.deletedCount || 0}개의 미사용 사용자 업로드 이미지를 삭제했어요.`
|
||||
} catch (e) {
|
||||
error.value = '미사용 커스텀 이미지 일괄 삭제에 실패했어요.'
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCustomItemModalLabel() {
|
||||
const item = modalTargetCustomItem.value
|
||||
const nextLabel = customItemModalDraftLabel.value.trim().slice(0, 60)
|
||||
if (!item || !nextLabel || nextLabel === item.label || customItemModalLabelSaving.value) return
|
||||
|
||||
try {
|
||||
customItemModalLabelSaving.value = true
|
||||
const data = await api.updateAdminCustomItemLabel(item.id, { label: nextLabel, sourceType: item.sourceType })
|
||||
item.label = data.item?.label || nextLabel
|
||||
customItemModalDraftLabel.value = item.label
|
||||
customItems.value = customItems.value.map((entry) => (entry.id === item.id ? { ...entry, label: item.label } : entry))
|
||||
toast.success('아이템 이름을 변경했어요.')
|
||||
} catch (e) {
|
||||
error.value = '아이템 이름 변경에 실패했어요.'
|
||||
} finally {
|
||||
customItemModalLabelSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function promoteCustomItem(item) {
|
||||
resetMessages()
|
||||
if (!customItemModalTargetGameId.value) {
|
||||
error.value = '추가할 게임을 먼저 선택해주세요.'
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
item.isPromoting = true
|
||||
await api.promoteAdminCustomItem(item.id, { gameId: customItemModalTargetGameId.value })
|
||||
const targetGameName = games.value.find((game) => game.id === customItemModalTargetGameId.value)?.name || customItemModalTargetGameId.value
|
||||
if (selectedGameId.value === customItemModalTargetGameId.value) await loadGame()
|
||||
closeCustomItemModal()
|
||||
success.value = `"${item.label}" 이미지를 ${targetGameName} 템플릿으로 추가했어요.`
|
||||
} catch (e) {
|
||||
error.value = '선택한 이미지를 템플릿으로 추가하지 못했어요.'
|
||||
} finally {
|
||||
item.isPromoting = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
submitCustomItemSearch,
|
||||
toggleCustomItemOrphanOnly,
|
||||
changeCustomItemLimit,
|
||||
moveCustomItemPage,
|
||||
pushCustomItemModalHistoryState,
|
||||
openCustomItemModal,
|
||||
closeCustomItemModal,
|
||||
openCustomItemDeleteModal,
|
||||
closeCustomItemDeleteModal,
|
||||
jumpToGameAdmin,
|
||||
removeCustomItem,
|
||||
removeUnusedCustomItems,
|
||||
saveCustomItemModalLabel,
|
||||
promoteCustomItem,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user