관리자 목록과 이미지 설정 개선
This commit is contained in:
@@ -36,6 +36,9 @@ const { data: analyticsSummary, refresh: refreshSummary } = await useFetch('/adm
|
||||
query: analyticsQuery,
|
||||
default: () => ({
|
||||
todayVisitors: 0,
|
||||
totalVisitors: 0,
|
||||
totalPageViews: 0,
|
||||
totalPosts: 0,
|
||||
visitorsLast7Days: 0,
|
||||
pageViewsLast30Days: 0,
|
||||
onlineNow: 0,
|
||||
@@ -106,6 +109,29 @@ const {
|
||||
const selectedTrendDetailLabel = computed(() => selectedTrendRange.value.label || '오늘')
|
||||
const trafficSources = computed(() => selectedTrendDetail.value?.sources || [])
|
||||
const trafficDevices = computed(() => selectedTrendDetail.value?.devices || [])
|
||||
const totalDeviceVisitors = computed(() => {
|
||||
return trafficDevices.value.reduce((sum, row) => sum + Number(row.visitors || 0), 0)
|
||||
})
|
||||
const groupedTrafficDevices = computed(() => {
|
||||
const groups = new Map()
|
||||
|
||||
for (const row of trafficDevices.value) {
|
||||
const key = row.deviceType || '기타'
|
||||
const existing = groups.get(key) || {
|
||||
deviceType: key,
|
||||
pageViews: 0,
|
||||
visitors: 0,
|
||||
rows: []
|
||||
}
|
||||
|
||||
existing.pageViews += Number(row.pageViews || 0)
|
||||
existing.visitors += Number(row.visitors || 0)
|
||||
existing.rows.push(row)
|
||||
groups.set(key, existing)
|
||||
}
|
||||
|
||||
return [...groups.values()].sort((a, b) => b.pageViews - a.pageViews || b.visitors - a.visitors)
|
||||
})
|
||||
const topPosts = computed(() => selectedTrendDetail.value?.topPosts || [])
|
||||
const topPages = computed(() => selectedTrendDetail.value?.topPages || [])
|
||||
const trendDetailError = computed(() => selectedTrendDetailError.value ? '선택한 기간 통계 상세를 불러오지 못했습니다.' : '')
|
||||
@@ -240,6 +266,21 @@ const formatTrafficPercent = (value) => {
|
||||
return `${Math.round((Number(value || 0) / total) * 100)}%`
|
||||
}
|
||||
|
||||
/**
|
||||
* 디바이스 방문자 비율을 표시한다.
|
||||
* @param {number} value - 방문자 수
|
||||
* @returns {string} 비율
|
||||
*/
|
||||
const formatDeviceVisitorPercent = (value) => {
|
||||
const total = totalDeviceVisitors.value
|
||||
|
||||
if (!total) {
|
||||
return '0%'
|
||||
}
|
||||
|
||||
return `${Math.round((Number(value || 0) / total) * 100)}%`
|
||||
}
|
||||
|
||||
const chartMetrics = computed(() => [
|
||||
{
|
||||
key: 'visitors',
|
||||
@@ -487,9 +528,13 @@ watch(trendRows, () => {
|
||||
<strong class="ml-2 text-lg text-ink">{{ realtime.summary.onlineNow }}</strong>
|
||||
</p>
|
||||
<p class="admin-dashboard__summary-item text-muted">
|
||||
오늘 접속자
|
||||
오늘 방문자
|
||||
<strong class="ml-2 text-lg text-ink">{{ analyticsSummary.todayVisitors }}</strong>
|
||||
</p>
|
||||
<p class="admin-dashboard__summary-item text-muted">
|
||||
누적 방문자
|
||||
<strong class="ml-2 text-lg text-ink">{{ analyticsSummary.totalVisitors }}</strong>
|
||||
</p>
|
||||
<p class="admin-dashboard__summary-item text-muted">
|
||||
게시물 수
|
||||
<strong class="ml-2 text-lg text-ink">{{ posts.length }}</strong>
|
||||
@@ -832,27 +877,39 @@ watch(trendRows, () => {
|
||||
디바이스
|
||||
</h3>
|
||||
<ul
|
||||
v-if="trafficDevices.length"
|
||||
v-if="groupedTrafficDevices.length"
|
||||
class="mt-3 space-y-2"
|
||||
>
|
||||
<li
|
||||
v-for="row in trafficDevices"
|
||||
:key="`${row.deviceType}-${row.osName}`"
|
||||
class="flex items-center justify-between gap-3 border-b border-line pb-2 text-xs last:border-b-0 last:pb-0"
|
||||
v-for="group in groupedTrafficDevices"
|
||||
:key="group.deviceType"
|
||||
class="border-b border-line pb-2 text-xs last:border-b-0 last:pb-0"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium text-ink">
|
||||
{{ row.deviceType }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-[11px] text-muted">
|
||||
{{ row.osName }}
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<p class="font-medium text-ink">
|
||||
{{ group.deviceType }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-[11px] text-muted">
|
||||
{{ group.rows.map((row) => row.osName).filter(Boolean).join(' / ') || '-' }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-right text-muted">
|
||||
<span class="font-semibold text-ink">{{ group.pageViews }}</span>회
|
||||
<br>
|
||||
{{ group.visitors }}명 · {{ formatDeviceVisitorPercent(group.visitors) }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-right text-muted">
|
||||
<span class="font-semibold text-ink">{{ row.pageViews }}</span>회
|
||||
<br>
|
||||
{{ row.visitors }}명
|
||||
</p>
|
||||
<ul class="mt-2 space-y-1 border-t border-line pt-2 text-[11px] text-muted">
|
||||
<li
|
||||
v-for="row in group.rows"
|
||||
:key="`${group.deviceType}-${row.osName}`"
|
||||
class="flex items-center justify-between gap-2"
|
||||
>
|
||||
<span>{{ row.osName || '-' }}</span>
|
||||
<span>{{ row.visitors }}명 · {{ formatDeviceVisitorPercent(row.visitors) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
@@ -926,9 +983,9 @@ watch(trendRows, () => {
|
||||
</p>
|
||||
<p class="mt-1 text-[11px] leading-snug text-muted">
|
||||
IP {{ session.ipAddress || '-' }}
|
||||
<span v-if="Number(session.pageCount || 0) > 1">
|
||||
· 열린 페이지 {{ session.pageCount }}개
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="Number(session.pageCount || 0) > 1" class="mt-0.5 text-[11px] leading-snug text-muted">
|
||||
열린 페이지 {{ session.pageCount }}개
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user