This commit is contained in:
2026-04-07 14:50:23 +09:00
commit b4e485502b
4778 changed files with 2017091 additions and 0 deletions

View File

@@ -0,0 +1,328 @@
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<div class="pb-8 px-4 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-xl font-semibold text-gray-900">
API 사용 통계 {{ hero == 'usage' ? '(사용량)' : '(단어)' }}
</h1>
<p class="mt-2 text-sm text-gray-700">
사용량 통계를 보여 줍니다. 구분 항목으로 시간별, 날짜별,
월별 구분이 가능합니다.
</p>
</div>
<select
id="targetUnit"
v-model="targetUnit"
name="targetUnit"
class="mt-0 block pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"
@change="onChange($event)"
>
<option
v-for="unit in units"
:key="unit.key"
:selected="unit.current"
:value="unit.key"
>
<span class="truncate">
{{ unit.key }}
</span>
</option>
</select>
<Datepicker
v-model="date"
class="w-64 mt-4 sm:mt-0 sm:ml-2 sm:flex-none"
range
multi-calendars
multi-calendars-solo
:format="inputFormat"
:preview-format="previewFormat"
@update:modelValue="handleDate"
/>
</div>
<StatisticsTable1
:headings="listHeadings"
:actions="listActions"
:keys="listKeys"
:data="listData"
:action-key="actionKey"
:column-filter="columnFilter"
:do-action="doAction"
/>
<div class="p-0 rounded-bl-2xl rounded-br-2xl md:px-0">
<button
type="button"
class="relative inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none"
@click="doDownload()"
>
<span> 엑셀파일로 다운로드 </span>
</button>
</div>
</div>
</template>
<script setup lang="ts">
import Datepicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css';
definePageMeta({
middleware: 'check-auth-admin',
});
const router = useRouter();
const route = useRoute();
const hero = ref(route.params.hero);
const targetUID = ref(route.query.uid ? route.query.uid : 'all');
const targetKey = ref(route.query.key ? route.query.key : 'all');
const targetUnit = ref(route.query.unit ? route.query.unit : 'day');
const { $dayjs } = useNuxtApp();
const targetDate = ref($dayjs(new Date().toISOString()).format('YYYY'));
const targetDateMonth = ref($dayjs(new Date().toISOString()).format('YYYY'));
const targetDateDay = ref({
month: new Date().getMonth(),
year: new Date().getFullYear,
});
const targetDateHour = ref($dayjs(new Date().toISOString()));
const units = ref([
{ current: targetUnit.value == 'year', key: 'year' },
{ current: targetUnit.value == 'month', key: 'month' },
{ current: targetUnit.value == 'day', key: 'day' },
{ current: targetUnit.value == 'hour', key: 'hour' },
]);
function onChange(e) {
console.log('targetUnit.value=', targetUnit.value);
navigateTo(
'/admin/statistics/byterm/' +
hero.value +
'?unit=' +
targetUnit.value +
'&uid=' +
targetUID.value +
'&key=' +
targetKey.value
);
refresh();
units.value = [
{ current: targetUnit.value == 'year', key: 'year' },
{ current: targetUnit.value == 'month', key: 'month' },
{ current: targetUnit.value == 'day', key: 'day' },
{ current: targetUnit.value == 'hour', key: 'hour' },
];
refresh();
}
const date = ref([]);
const inputFormat = (date) => {
// console.log('huk date=', date);
if (date.length == 1) {
const day = date[0].getDate();
const month = date[0].getMonth() + 1;
const year = date[0].getFullYear();
return `${day}/${month}/${year}`;
} else if (date.length == 2) {
const day1 = date[0].getDate();
const month1 = date[0].getMonth() + 1;
const year1 = date[0].getFullYear();
const day2 = date[1].getDate();
const month2 = date[1].getMonth() + 1;
const year2 = date[1].getFullYear();
return `${year1}-${month1}-${day1} ~ ${year2}-${month2}-${day2}`;
}
};
const previewFormat = (date) => {
// console.log('huk date=', date);
if (date.length == 1) {
const day = date[0].getDate();
const month = date[0].getMonth() + 1;
const year = date[0].getFullYear();
return `${day}/${month}/${year}`;
} else if (date.length == 2) {
const day1 = date[0].getDate();
const month1 = date[0].getMonth() + 1;
const year1 = date[0].getFullYear();
const day2 = date[1].getDate();
const month2 = date[1].getMonth() + 1;
const year2 = date[1].getFullYear();
return `${year1}-${month1}-${day1} ~ ${year2}-${month2}-${day2}`;
}
};
const endDate = new Date();
const startDate = new Date(new Date().setDate(endDate.getDate() - 7));
date.value = [startDate, endDate];
const startDateTag = ref($dayjs(startDate.toISOString()).format('YYYYMMDDHH'));
const endDateTag = ref($dayjs(endDate.toISOString()).format('YYYYMMDDHH'));
function doDownload() {
const anchor = document.createElement('a');
let urlBase = '';
const currentHost = window.location.host.toLowerCase();
const currentProtocol = window.location.protocol;
const currentDomain = _utils.getDomain(window.location.href);
const apiBaseUrl = _crossCtl.config['API_BASE_URL'];
console.log('currentHost=', currentHost);
console.log('currentProtocol=', currentProtocol);
console.log('currentDomain=', currentDomain);
console.log('apiBaseUrl=', apiBaseUrl);
if (apiBaseUrl.indexOf(currentHost) == -1) {
urlBase = apiBaseUrl;
} else {
urlBase = '/api/';
}
console.log('urlBase=', urlBase);
anchor.href =
urlBase +
'local/download/report_' +
hero.value +
'_' +
startDateTag.value +
'_' +
endDateTag.value +
'.xlsx?tag=' +
hero.value +
'&startDateTag=' +
startDateTag.value +
'&endDateTag=' +
endDateTag.value +
'&unit=' +
targetUnit.value +
'&uid=' +
targetUID.value +
'&key=' +
targetKey.value;
anchor.target = '_blank';
anchor.click();
}
const listHeadings =
hero.value == 'usage'
? [
{
title: 'date',
key: 'date_tag',
},
{
title: 'total',
key: 'total',
},
{
title: 'hit',
key: 'hit',
},
{
title: 'size',
key: 'size',
},
]
: [
{
title: 'word',
key: 'word',
widthRatio: '100',
},
{
title: 'count',
key: 'count_sum',
},
];
const listActions = [];
const actionKey = 'serial';
const listKeys = [
'serial',
'date_tag',
'total',
'hit',
'size',
'uniq_ip',
'uniq_referrer',
'updated',
];
const listData = ref([]);
function columnFilter(key, val) {
// console.log('columnFilter(), key = ', key, ', val = ', val);
return val;
}
function doAction(tag, target) {
console.log('on doAction(), tag=', tag, ', target=', target);
router.push({
name: 'key-edit',
params: { target: target },
});
}
async function refresh() {
const responseJson = await _crossCtl.doComm(
'local/select',
hero.value == 'usage'
? 'admin:statistics:usage'
: 'admin:statistics:word',
{
unit: targetUnit.value,
uid: targetUID.value,
key: targetKey.value,
startDateTag: startDateTag.value,
endDateTag: endDateTag.value,
}
);
for (let i = 0; i < responseJson['data'].length; i++) {
responseJson['data'][i]['hit_ratio'] =
(
(responseJson['data'][i]['hit'] /
responseJson['data'][i]['total']) *
100
).toFixed(2) + '%';
responseJson['data'][i]['size_avg'] = _utils.formatBytes(
responseJson['data'][i]['size'] / responseJson['data'][i]['total'],
2
);
}
listData.value = responseJson['data'];
console.log('listData.value=', listData.value);
}
function handleDate(date) {
console.log('huk date = ', date);
startDateTag.value = $dayjs(date[0].toISOString()).format('YYYYMMDDHH');
endDateTag.value = $dayjs(date[1].toISOString()).format('YYYYMMDDHH');
refresh();
}
refresh();
</script>

View File

@@ -0,0 +1,194 @@
<template>
<div class="m-8">
<div class="space-y-8 divide-y divide-gray-200 sm:space-y-5">
<div>
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-xl font-semibold text-gray-900">
전체 사용량 통계
</h1>
<p class="mt-2 text-sm text-gray-700">
전체 서버의 사용량 통계를 확인 합니다.
</p>
</div>
<img
v-if="inPregressFlag"
width="32"
src="/loading-load-2.gif"
/>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<button
type="button"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
@click="
navigateTo('/admin/statistics/byterm/usage')
"
>
상세 사용량 통계
</button>
<button
type="button"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
@click="navigateTo('/admin/statistics/byterm/word')"
>
상세 단어 통계
</button>
</div>
</div>
<section aria-labelledby="applicant-information-title">
<div class="bg-white mt-3 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h2
id="applicant-information-title"
class="text-lg font-medium leading-6 text-gray-900"
>
최근 24시간 사용량
</h2>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<LineChart
:chart-data="lineChartData"
:chart-options="lineChartOptions"
/>
</div>
<!--
<div>
<a
href="javascript:void(0)"
class="block bg-gray-50 px-4 py-4 text-center text-sm font-medium text-gray-500 hover:text-gray-700 sm:rounded-b-lg"
@click="refresh()"
>새로 고침</a
>
</div>
-->
</div>
</section>
<section aria-labelledby="applicant-information-title">
<div class="bg-white mt-3 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h2
id="applicant-information-title"
class="text-lg font-medium leading-6 text-gray-900"
>
최근 24시간 키별 점유율
</h2>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<PieChart
:chart-data="pieChartData"
:chart-options="pieChartOptions"
/>
</div>
<!--
<div>
<a
href="javascript:void(0)"
class="block bg-gray-50 px-4 py-4 text-center text-sm font-medium text-gray-500 hover:text-gray-700 sm:rounded-b-lg"
@click="refresh()"
>새로 고침</a
>
</div>
-->
</div>
</section>
</div>
</div>
<div class="pt-5">
<div class="flex justify-end"></div>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
middleware: 'check-auth-op',
});
const inPregressFlag = ref(true);
const responseJson = await _crossCtl.doComm(
'local/select',
'admin:dashboard',
{}
);
let rawData1 = [];
let rawData2 = [];
console.log('responseJson=', responseJson);
inPregressFlag.value = false;
if (responseJson['responseMessage'] == 'ok') {
rawData1 = responseJson['result']['adminDashData1'];
rawData2 = responseJson['result']['adminDashData2'];
} else {
alert(responseJson['responseMessage']);
}
const lineChartData = {
labels: rawData1.map((item) => item['date_tag']),
datasets: [
{
label: 'Total',
backgroundColor: '#00D8FF',
data: rawData1.map((item) => item['total']),
},
{
label: 'Hit',
backgroundColor: '#f87979',
data: rawData1.map((item) => item['hit']),
},
],
};
const lineChartOptions = {
responsive: true,
maintainAspectRatio: false,
};
const pieChartData = {
labels: rawData2.map((item) => item['key_name']),
datasets: [
{
// backgroundColor: ['#41B883', '#E46651', '#00D8FF', '#DD1B16'],
data: rawData2.map((item) => item['total']),
},
],
};
const pieChartOptions = {
responsive: true,
maintainAspectRatio: false,
};
const chartData = {
labels: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
data: [40, 20, 12, 39, 10, 40, 39, 80, 40, 20, 12, 11],
},
],
};
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
};
</script>

View File

@@ -0,0 +1,121 @@
<template>
<div>
<div class="mt-5 sm:px-3 lg:px-5">전체 통계 페이지 대시보드</div>
<LineChart
:chart-data="lineChartData"
:chart-options="lineChartOptions"
/>
<PieChart :chart-data="pieChartData" :chart-options="pieChartOptions" />
<div class="mt-5 sm:px-3 lg:px-5">
<a
href="javascript:void(0)"
class="text-base font-medium text-indigo-700 hover:text-indigo-600"
@click="navigateTo('/admin/statistics/byterm/usage')"
>기간별 사용량 통계 화면으로 &rarr;</a
>
</div>
<div class="mt-5 sm:px-3 lg:px-5">
<a
href="javascript:void(0)"
class="text-base font-medium text-indigo-700 hover:text-indigo-600"
@click="navigateTo('/admin/statistics/byterm/word')"
>기간별 단어 통계 화면으로 &rarr;</a
>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
middleware: 'check-auth-admin',
});
/*
const route = useRoute();
let hero: string | string[] = '';
hero = route.params['hero'];
*/
const responseJson = await _crossCtl.doComm(
'local/select',
'admin:dashboard',
{}
);
let rawData1 = [];
let rawData2 = [];
console.log('responseJson=', responseJson);
if (responseJson['responseMessage'] == 'ok') {
rawData1 = responseJson['result']['adminDashData1'];
rawData2 = responseJson['result']['adminDashData2'];
}
const lineChartData = {
labels: rawData1.map((item) => item['date_tag']),
datasets: [
{
label: 'Total',
backgroundColor: '#00D8FF',
data: rawData1.map((item) => item['total']),
},
{
label: 'Hit',
backgroundColor: '#f87979',
data: rawData1.map((item) => item['hit']),
},
],
};
const lineChartOptions = {
responsive: true,
maintainAspectRatio: false,
};
const pieChartData = {
labels: rawData2.map((item) => item['key_name']),
datasets: [
{
// backgroundColor: ['#41B883', '#E46651', '#00D8FF', '#DD1B16'],
data: rawData2.map((item) => item['total']),
},
],
};
const pieChartOptions = {
responsive: true,
maintainAspectRatio: false,
};
const chartData = {
labels: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
data: [40, 20, 12, 39, 10, 40, 39, 80, 40, 20, 12, 11],
},
],
};
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
};
</script>