HomeView.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <template>
  2. <el-main class="min-w-[700px]" ref="mainRef">
  3. <el-row :gutter="20">
  4. <el-col :span="8">
  5. <el-card shadow="hover">
  6. <div class="flex justify-between items-center">
  7. <el-icon size="60" color="#405CFF">
  8. <CalendarClock16Filled />
  9. </el-icon>
  10. <div class="flex flex-col items-end">
  11. <span class="text-[#999] text-base">使用时长</span>
  12. <span class="text-2xl font-bold mt-2">{{ time }} (天)</span>
  13. </div>
  14. </div>
  15. </el-card>
  16. </el-col>
  17. <el-col :span="8">
  18. <el-card shadow="hover">
  19. <div class="flex justify-between items-center">
  20. <el-icon size="60" color="#15A8AA">
  21. <PersonAdd20Filled />
  22. </el-icon>
  23. <div class="flex flex-col items-end">
  24. <span class="text-[#999] text-base">今日新增</span>
  25. <span class="text-2xl font-bold mt-2">{{ userDatas.today }} (人)</span>
  26. </div>
  27. </div>
  28. </el-card>
  29. </el-col>
  30. <el-col :span="8">
  31. <el-card shadow="hover">
  32. <div class="flex justify-between items-center">
  33. <el-icon size="60" color="#928EFB">
  34. <Chat24Filled />
  35. </el-icon>
  36. <div class="flex flex-col items-end">
  37. <span class="text-[#999] text-base">今日互动</span>
  38. <span class="text-2xl font-bold mt-2">{{ chatDatas.today }} (次)</span>
  39. </div>
  40. </div>
  41. </el-card>
  42. </el-col>
  43. </el-row>
  44. <el-card shadow="hover" class="mt-5">
  45. <div class="flex">
  46. <el-tabs class="demo-tabs" v-model="activeName" tab-position="left">
  47. <el-tab-pane name="user">
  48. <template #label>
  49. <div class="flex items-center py-4">
  50. <el-icon size="24"><DataArea20Filled /></el-icon>
  51. <span class="ml-1"> 人数统计 </span>
  52. </div>
  53. </template>
  54. </el-tab-pane>
  55. <el-tab-pane name="chat">
  56. <template #label>
  57. <div class="flex items-center py-4">
  58. <el-icon size="24"><DataTreemap20Filled /></el-icon>
  59. <span class="ml-1">互动统计</span>
  60. </div>
  61. </template></el-tab-pane
  62. >
  63. </el-tabs>
  64. <div id="chart" class="h-[500px] flex-1"></div>
  65. </div>
  66. </el-card>
  67. </el-main>
  68. </template>
  69. <script setup>
  70. import {
  71. CalendarClock16Filled,
  72. PersonAdd20Filled,
  73. Chat24Filled,
  74. DataArea20Filled,
  75. DataTreemap20Filled
  76. } from '@vicons/fluent'
  77. import { useUserStore } from '@/stores/user'
  78. import { differenceInCalendarDays } from 'date-fns'
  79. import { onMounted, watch, ref, nextTick } from 'vue'
  80. import { useDark, useToggle, useResizeObserver } from '@vueuse/core'
  81. import { http } from '@/plugins/http'
  82. const isDark = useDark({
  83. storageKey: 'dark-mode-admin'
  84. })
  85. import * as echarts from 'echarts'
  86. const { user } = useUserStore()
  87. const time = differenceInCalendarDays(new Date(), new Date(user.createdAt))
  88. watch(isDark, () => {
  89. initChart()
  90. })
  91. const myChart = ref(null)
  92. const mainRef = ref(null)
  93. useResizeObserver(mainRef, () => {
  94. nextTick(() => {
  95. initChart()
  96. })
  97. })
  98. const activeName = ref('user')
  99. watch(activeName, () => {
  100. initChart()
  101. })
  102. async function initChart() {
  103. if (activeName.value === 'user') {
  104. await http
  105. .get('/admin/users/getDatas', {
  106. apiUserId: user.apiUserId
  107. })
  108. .then((res) => {
  109. userDatas.value = res
  110. })
  111. } else {
  112. await http
  113. .get('/chat/getDatas', {
  114. apiUserId: user.apiUserId
  115. })
  116. .then((res) => {
  117. chatDatas.value = res
  118. })
  119. }
  120. if (myChart.value) {
  121. myChart.value.dispose()
  122. myChart.value = null
  123. }
  124. var chartDom = document.getElementById('chart')
  125. myChart.value = echarts.init(chartDom, isDark.value ? 'dark' : 'light')
  126. var option
  127. option = {
  128. backgroundColor: isDark.value ? 'rgb(29, 30, 31)' : '#fff',
  129. title: [
  130. {
  131. // text: '标题',
  132. left: '2%'
  133. },
  134. {
  135. text: activeName.value === 'user' ? '全部用户' : '互动次数',
  136. // subtext: userDatas.value.api + userDatas.value.user + '人',
  137. subtext:
  138. activeName.value === 'user'
  139. ? userDatas.value.api + userDatas.value.user + '人'
  140. : chatDatas.value.api + chatDatas.value.user + '次',
  141. textStyle: {
  142. fontSize: 14,
  143. color: isDark.value ? '#fff' : '#000',
  144. fontWeight: 'normal'
  145. },
  146. subtextStyle: {
  147. fontSize: 28,
  148. color: isDark.value ? '#fff' : '#000',
  149. fontWeight: 'bold'
  150. },
  151. textAlign: 'center',
  152. x: '50%',
  153. y: '20%'
  154. }
  155. ],
  156. grid: [
  157. {
  158. top: '50%',
  159. width: '100%',
  160. left: 10,
  161. containLabel: true
  162. }
  163. ],
  164. xAxis: { type: 'category', data: Object.keys(userDatas.value.week) },
  165. yAxis: { gridIndex: 0 },
  166. tooltip: [
  167. {
  168. trigger: 'item',
  169. formatter: function (params) {
  170. if (params.seriesType === 'pie') {
  171. return `${params.seriesName} <br/>${params.name} : ${params.value}${
  172. activeName.value === 'user' ? '人' : '次'
  173. } (${params.percent}%)`
  174. } else {
  175. return `${params.seriesName} <br/>${params.name} : ${params.value}${
  176. activeName.value === 'user' ? '人' : '次'
  177. } `
  178. }
  179. }
  180. }
  181. ],
  182. legend: {
  183. left: 'center',
  184. top: 'bottom',
  185. data: ['注册用户', '内部用户']
  186. },
  187. toolbox: {
  188. show: true,
  189. feature: {
  190. mark: { show: true },
  191. dataView: { show: true, readOnly: false },
  192. restore: { show: true },
  193. saveAsImage: { show: true }
  194. }
  195. },
  196. series: [
  197. {
  198. name: '注册用户',
  199. type: 'bar',
  200. stack: 'one',
  201. // data: activeName.value === 'user' ? [5, 3, 2, 1, 6, 7, 4] : [30, 20, 13, 50, 21, 12, 30]
  202. data:
  203. activeName.value === 'user'
  204. ? Object.keys(userDatas.value.week).map((key) => {
  205. return userDatas.value.week[key].user
  206. })
  207. : Object.keys(chatDatas.value.week).map((key) => {
  208. return chatDatas.value.week[key].user
  209. })
  210. },
  211. {
  212. name: '内部用户',
  213. type: 'bar',
  214. stack: 'one',
  215. // data: activeName.value === 'user' ? [3, 4, 2, 4, 5, 6, 2] : [20, 12, 15, 16, 29, 39, 10]
  216. data:
  217. activeName.value === 'user'
  218. ? Object.keys(userDatas.value.week).map((key) => {
  219. return userDatas.value.week[key].api
  220. })
  221. : Object.keys(chatDatas.value.week).map((key) => {
  222. return chatDatas.value.week[key].api
  223. })
  224. },
  225. {
  226. id: 'pie',
  227. name: activeName.value === 'user' ? '用户人数' : '互动次数',
  228. type: 'pie',
  229. radius: ['20%', '40%'],
  230. avoidLabelOverlap: false,
  231. center: ['50%', '25%'],
  232. itemStyle: {
  233. borderRadius: 5
  234. },
  235. data: [
  236. {
  237. value: activeName.value === 'user' ? userDatas.value.user : chatDatas.value.user,
  238. name: '注册用户'
  239. },
  240. { value: activeName.value === 'user' ? userDatas.value.api : chatDatas.value.api, name: '内部用户' }
  241. ]
  242. }
  243. ]
  244. }
  245. option && myChart.value.setOption(option)
  246. }
  247. const userDatas = ref({
  248. today: 0
  249. })
  250. const chatDatas = ref({
  251. today: 0
  252. })
  253. const role = ref('admin')
  254. onMounted(() => {
  255. http.get(`/auth/admin/getRole`).then((res) => {
  256. if (res === 'api') {
  257. role.value = 'api'
  258. }
  259. initChart()
  260. })
  261. })
  262. </script>
  263. <style lang="less" scoped>
  264. .el-tabs {
  265. --el-tabs-header-height: 60px;
  266. }
  267. </style>