feat(admin): 관리자 콘솔 — 소셜 로그인 + 회원관리 (사용자 인증과 분리)

- 관리자 전용 store(adminAuth)·API(adminAuth·adminMembers)로 사용자 auth와 분리, 공용 저수준(http·tokenStore)만 재사용
- 관리자 레이아웃(헤더+좌측 사이드메뉴+바디+풋터), 웹 전용
- 구글 로그인(GIS) + 개발자 로그인(dev, admin@dog.com) — /admin/login
- 회원관리 q-table: 검색·상태/구독/권한 인라인 변경·삭제, 본인 행 잠금
- 라우터 /admin 경로 + 별도 관리자 가드, http에 put/del 추가, 401 분기(관리자→admin-login), Dialog 등록

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-07-11 08:59:20 +09:00
parent 0c538e0658
commit 85c925008a
10 changed files with 634 additions and 5 deletions
+67
View File
@@ -0,0 +1,67 @@
<template>
<q-layout view="hHh Lpr lFf">
<!-- 헤더 -->
<q-header elevated class="bg-primary text-white">
<q-toolbar>
<q-btn flat dense round icon="menu" aria-label="메뉴" @click="drawer = !drawer" />
<q-toolbar-title class="text-weight-bold">
<q-icon name="pets" class="q-mr-sm" />산책갈개 관리자
</q-toolbar-title>
<q-chip dense color="white" text-color="primary" icon="admin_panel_settings">
{{ admin.member?.nickname ?? '관리자' }}
</q-chip>
<q-btn flat dense round icon="logout" aria-label="로그아웃" @click="onLogout" />
</q-toolbar>
</q-header>
<!-- 좌측 사이드 메뉴 -->
<q-drawer v-model="drawer" show-if-above bordered :width="240" class="bg-grey-1">
<q-list padding>
<q-item-label header class="text-grey-7">관리 메뉴</q-item-label>
<q-item
v-for="item in menu"
:key="item.name"
clickable
:to="{ name: item.name }"
active-class="text-primary bg-blue-1"
exact
>
<q-item-section avatar>
<q-icon :name="item.icon" />
</q-item-section>
<q-item-section>{{ item.label }}</q-item-section>
</q-item>
</q-list>
</q-drawer>
<!-- 바디 -->
<q-page-container>
<router-view />
</q-page-container>
<!-- 풋터 -->
<q-footer class="bg-white text-grey-6">
<div class="text-caption text-center q-py-sm">© 2026 산책갈개 · 관리자 콘솔</div>
</q-footer>
</q-layout>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useAdminAuthStore } from '@/stores/adminAuth'
const router = useRouter()
const admin = useAdminAuthStore()
const drawer = ref(true)
const menu = [{ name: 'admin-members', label: '회원 관리', icon: 'group' }]
async function onLogout() {
await admin.logout()
await router.replace({ name: 'admin-login' })
}
</script>