From 308a083f32a9208cf3bae9fa8afc0b1ae114da60 Mon Sep 17 00:00:00 2001 From: Yu Sung <hwchon1234@gmail.com> Date: Sat, 15 Mar 2025 00:25:45 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=BC=EB=B3=84=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EC=88=98=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/member_statistics.js | 10 + src/router/index.js | 5 + src/views/Can/CanStatus.vue | 2 +- src/views/Member/MemberStatisticsView.vue | 223 ++++++++++++++++++++++ 4 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/api/member_statistics.js create mode 100644 src/views/Member/MemberStatisticsView.vue diff --git a/src/api/member_statistics.js b/src/api/member_statistics.js new file mode 100644 index 0000000..b97dd49 --- /dev/null +++ b/src/api/member_statistics.js @@ -0,0 +1,10 @@ +import Vue from 'vue'; + +async function getStatistics(startDate, endDate, page) { + return Vue.axios.get( + "/admin/member/statistics?startDateStr=" + startDate + + "&endDateStr=" + endDate + "&page=" + (page - 1) + "&size=30" + ) +} + +export { getStatistics } diff --git a/src/router/index.js b/src/router/index.js index aa5cafd..b90c858 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -30,6 +30,11 @@ const routes = [ name: 'MemberList', component: () => import(/* webpackChunkName: "member" */ '../views/Member/MemberList') }, + { + path: '/member/statistics', + name: 'MemberStatistics', + component: () => import(/* webpackChunkName: "member" */ '../views/Member/MemberStatisticsView.vue') + }, { path: '/creator/tags', name: 'CreatorTags', diff --git a/src/views/Can/CanStatus.vue b/src/views/Can/CanStatus.vue index cdd53ac..feadbef 100644 --- a/src/views/Can/CanStatus.vue +++ b/src/views/Can/CanStatus.vue @@ -36,7 +36,7 @@ <v-col cols="2"> <v-btn block - color="#9970ff" + color="#3bb9f1" dark depressed @click="getChargeStatus" diff --git a/src/views/Member/MemberStatisticsView.vue b/src/views/Member/MemberStatisticsView.vue new file mode 100644 index 0000000..685f42b --- /dev/null +++ b/src/views/Member/MemberStatisticsView.vue @@ -0,0 +1,223 @@ +<template> + <div> + <v-toolbar dark> + <v-spacer /> + <v-toolbar-title>일별 전체 회원 수</v-toolbar-title> + <v-spacer /> + </v-toolbar> + + <br> + + <v-container> + <v-row> + <v-spacer /> + <v-col cols="2"> + <datetime + v-model="start_date" + class="datepicker" + format="YYYY-MM-DD" + /> + </v-col> + + <v-col cols="1"> + ~ + </v-col> + + <v-col cols="2"> + <datetime + v-model="end_date" + class="datepicker" + format="YYYY-MM-DD" + /> + </v-col> + + <v-col cols="1" /> + + <v-col cols="2"> + <v-btn + block + color="#3bb9f1" + dark + depressed + @click="getStatistics" + > + 조회 + </v-btn> + </v-col> + </v-row> + + <v-row> + <v-col> + <v-data-table + :headers="headers" + :items="items" + :loading="is_loading" + :items-per-page="-1" + class="elevation-1" + hide-default-footer + > + <template slot="body.prepend"> + <tr class="summary"> + <td> + 합계 + </td> + <td> + {{ total_sign_up_count }} + </td> + <td> + {{ total_sign_out_count }} + </td> + <td> + {{ total_payment_member_count }} + </td> + </tr> + </template> + + <template v-slot:item.date="{ item }"> + {{ item.date }} + </template> + + <template v-slot:item.signUpCount="{ item }"> + {{ item.signUpCount.toLocaleString() }} + </template> + + <template v-slot:item.signOutCount="{ item }"> + {{ item.signOutCount.toLocaleString() }} + </template> + + <template v-slot:item.paymentMemberCount="{ item }"> + {{ item.paymentMemberCount.toLocaleString() }} + </template> + </v-data-table> + </v-col> + </v-row> + <v-row class="text-center"> + <v-col> + <v-pagination + v-model="page" + :length="total_page" + circle + @input="getStatistics" + /> + </v-col> + </v-row> + </v-container> + </div> +</template> + +<script> +import * as api from "@/api/member_statistics" +import datetime from 'vuejs-datetimepicker'; + +export default { + name: "MemberStatisticsView", + components: {datetime}, + + data() { + return { + is_loading: false, + start_date: null, + end_date: null, + total_sign_up_count: 0, + total_sign_out_count: 0, + total_payment_member_count: 0, + page: 1, + total_page: 0, + items: [], + headers: [ + { + text: '날짜', + align: 'center', + sortable: false, + value: 'date', + }, + { + text: '회원가입 수', + align: 'center', + sortable: false, + value: 'signUpCount', + }, + { + text: '회원탈퇴 수', + align: 'center', + sortable: false, + value: 'signOutCount', + }, + { + text: '결제자 수', + align: 'center', + sortable: false, + value: 'paymentMemberCount', + }, + ] + } + }, + + async created() { + const date = new Date(); + const firstDate = new Date(date.getFullYear(), date.getMonth(), 1); + const lastDate = new Date(date.getFullYear(), date.getMonth() + 1, 0); + + let firstDateMonth = (firstDate.getMonth() + 1).toString() + if (firstDateMonth.length < 2) { + firstDateMonth = '0' + firstDateMonth + } + + + let lastDateMonth = (lastDate.getMonth() + 1).toString() + if (lastDateMonth.length < 2) { + lastDateMonth = '0' + lastDateMonth + } + + this.start_date = firstDate.getFullYear() + '-' + firstDateMonth + '-0' + firstDate.getDate() + this.end_date = lastDate.getFullYear() + '-' + lastDateMonth + '-' + lastDate.getDate() + + await this.getStatistics() + }, + + methods: { + notifyError(message) { + this.$dialog.notify.error(message) + }, + + notifySuccess(message) { + this.$dialog.notify.success(message) + }, + + async getStatistics() { + this.is_loading = true + + try { + const res = await api.getStatistics(this.start_date, this.end_date, this.page); + + if (res.status === 200 && res.data.success === true) { + const data = res.data.data + this.total_sign_up_count = data.totalSignUpCount + this.total_sign_out_count = data.totalSignOutCount + this.total_payment_member_count = data.totalPaymentMemberCount + this.items = data.items + + const totalPage = Math.ceil(data.totalCount / 30) + if (totalPage <= 0) + this.total_page = 1 + else + this.total_page = totalPage + } else { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + } + + this.is_loading = false + } catch (e) { + this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') + this.is_loading = false + } + }, + } +} +</script> + +<style scoped> +.summary { + background-color: #c4dbf1; +} +</style>