index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <template>
  2. <div class="main_container custom_container">
  3. <div class="page_item end_item">
  4. <!-- 当前在线人数 -->
  5. <div class="page_search">
  6. <div class="search_content">
  7. <div class="content_left">
  8. <div class="grade_title">当前在线人数</div>
  9. </div>
  10. </div>
  11. </div>
  12. <div class="page_jg_20"></div>
  13. <ul class="cardBoard">
  14. <li v-for="card in cardBoardList" :key="card.label">
  15. <div class="label">{{ card.label }}</div>
  16. <div class="num">{{ card.value }}</div>
  17. </li>
  18. </ul>
  19. <!-- 模块列表 -->
  20. <div class="page_jg_20"></div>
  21. <TabSearch tabType="line" :tabList="onlineTypeTablist" :defaultTabKey="defaultOnlineTypeTab">
  22. </TabSearch>
  23. <!-- 时间范围 -->
  24. <div class="page_jg_20"></div>
  25. <TabSearch tabType="box" :tabList="dateRangeTablist" :defaultTabKey="defaultDateRangeTab">
  26. </TabSearch>
  27. <!-- 图表 -->
  28. <div class="page_jg_20"></div>
  29. <div class="chart_box">
  30. <!-- 某段时间的 某类型 在线人数统计 -->
  31. <div class="chart">
  32. <div class="page_search">
  33. <div class="search_content">
  34. <div class="content_left">
  35. <div class="grade_title">{{ onLineType }}在线人数统计</div>
  36. </div>
  37. </div>
  38. </div>
  39. <LineChart reportHeight="244px" :datax="somePeriodTimeLineData.datax"
  40. :datay="somePeriodTimeLineData.datay" :showCheckBox="false">
  41. </LineChart>
  42. </div>
  43. <!-- 某一时刻的 某类型 在线人数统计 -->
  44. <div class="chart">
  45. <div class="page_search">
  46. <div class="search_content">
  47. <div class="content_left">
  48. <div class="grade_title">{{ onLineTime }}时 / {{ onLineType }} 在线人数统计</div>
  49. </div>
  50. </div>
  51. </div>
  52. <MultipleBarCharts :datax="someoneTimeBarData.datax" :datay="someoneTimeBarData.datay"
  53. :height="244">
  54. </MultipleBarCharts>
  55. </div>
  56. <div class="chart">
  57. <!-- 某个学校的当天在线人数统计 -->
  58. <div class="page_search">
  59. <div class="search_content">
  60. <div class="content_left">
  61. <div class="grade_title">{{ schoolName }} / 当天在线人数统计</div>
  62. </div>
  63. </div>
  64. </div>
  65. <TabSearch :tabList="visualizationTypeTablist" tabType="box"
  66. :defaultTabKey="defaultVisualizationTypeTab"
  67. @tabChange="(val) => HandleTabChange('visualizationType', val)">
  68. </TabSearch>
  69. <LineChart v-if="defaultVisualizationTypeTab == 'chart'" reportHeight="208px"
  70. :datax="someSchoolOneDayBarData.datax" :datay="someSchoolOneDayBarData.datay"
  71. :showCheckBox="false">
  72. </LineChart>
  73. <template v-if="defaultVisualizationTypeTab == 'table'">
  74. <div class="page_search">
  75. <div class="search_content">
  76. <div class="content_left"></div>
  77. <div class="content_right">
  78. <el-button class="button_refresh">导出</el-button>
  79. </div>
  80. </div>
  81. </div>
  82. <div class="page_jg_20"></div>
  83. <Table :tableColumns="tableColumns" :tableData="someSchoolOneDayTableData">
  84. </Table>
  85. </template>
  86. </div>
  87. </div>
  88. </div>
  89. </div>
  90. </template>
  91. <script setup lang="ts">
  92. import TabSearch from '@/baseComponents/TabSearch.vue';
  93. import Table from '@/baseComponents/Table.vue';
  94. import { computed, reactive, ref, onMounted } from 'vue';
  95. import { onlineTypeTablist, dateRangeTablist, visualizationTypeTablist } from './tabSearch';
  96. import { tableColumns } from './table'
  97. import { getHeadData, getModuleList, getChartData } from '@/api/performanceMonitor'
  98. import MultipleBarCharts from '@/baseComponents/MultipleBarCharts.vue';
  99. import LineChart from '@/baseComponents/LineChart.vue';
  100. // 看板数据
  101. const boardMsg = reactive({
  102. teacherOnline: 0,
  103. studentOnline: 0,
  104. scaneOnline: 0,
  105. examinationPaperOnline: 0,
  106. analysisOnline: 0
  107. })
  108. // 看板列表
  109. const cardBoardList = computed(() => [
  110. {
  111. label: '教师在线',
  112. value: boardMsg.teacherOnline
  113. },
  114. {
  115. label: '学生在线',
  116. value: boardMsg.studentOnline
  117. },
  118. {
  119. label: '使用扫描人数',
  120. value: boardMsg.scaneOnline
  121. },
  122. {
  123. label: '使用阅卷人数',
  124. value: boardMsg.examinationPaperOnline
  125. },
  126. {
  127. label: '使用分析人数',
  128. value: boardMsg.analysisOnline
  129. },
  130. ])
  131. // 图表数据
  132. const onLineType = ref('学校教师') // 在线类型
  133. const schoolName = ref('北京师范大学附属中学') //学校名称
  134. // 某段时间的 某类型 在线人数统计
  135. const somePeriodTimeLineData = reactive({
  136. datax: ['0时', '1时', '2时', '3时', '4时', '5时', '6时', '7时', '8时', '9时', '10时'],
  137. datay: [
  138. [233, 43, 56, 43, 44, 34, 5, 65, 76, 87, 853],
  139. ]
  140. })
  141. // 某一时刻的 某类型 在线人数统计
  142. const onLineTime = ref(0)
  143. const someoneTimeBarData = reactive({
  144. datax: ['北京师范大学附属中学', '师达', '交大分校', '本部高中', '本部初中'],
  145. datay: [233, 43, 56, 43, 44]
  146. })
  147. // 某个学校的当天在线人数统计 折线图数据
  148. const someSchoolOneDayBarData = reactive({
  149. datax: ['0时', '1时', '2时', '3时', '4时', '5时', '6时', '7时', '8时', '9时', '10时'],
  150. datay: [
  151. [233, 43, 56, 43, 44, 34, 5, 65, 76, 87, 853],
  152. ]
  153. })
  154. // 某个学校的当天在线人数统计 表格数据
  155. const someSchoolOneDayTableData = ref([
  156. {
  157. num: 1, // 如果使用 type="index",这个字段其实可以不需要
  158. school: '北京师范大学附属中学',
  159. account: 'teacher01',
  160. name: '白语忱',
  161. operationDetail: '系统登录',
  162. operationTime: '2026-07-14 08:22:15',
  163. },
  164. {
  165. num: 2,
  166. school: '北京师范大学附属中学',
  167. account: 'teacher05',
  168. name: '曹延熠',
  169. operationDetail: '系统登录',
  170. operationTime: '2026-07-14 10:05:12',
  171. },
  172. ])
  173. // 处理tab切换相关操作
  174. const defaultOnlineTypeTab = ref('teacherOnline') // 默认的在线类型
  175. const defaultDateRangeTab = ref('today') // 默认的 时间范围
  176. const defaultVisualizationTypeTab = ref('chart') // 默认的 某个学校的当天在线人数 的可视化展示类型
  177. const HandleTabChange = (type: string, val: string) => {
  178. switch (type) {
  179. case 'visualizationType':
  180. defaultVisualizationTypeTab.value = val
  181. break
  182. }
  183. }
  184. // 获取头部数据
  185. const fetchHeadData = async () => {
  186. try {
  187. const res = await getHeadData()
  188. const data = res.data || res
  189. boardMsg.teacherOnline = data.teacherLoginNum ?? 0
  190. boardMsg.studentOnline = data.studentLoginNum ?? 0
  191. boardMsg.scaneOnline = data.scannedNum ?? 0
  192. boardMsg.examinationPaperOnline = data.markingNum ?? 0
  193. boardMsg.analysisOnline = data.reportNum ?? 0
  194. } catch (err) {
  195. console.error('getHeadData 请求失败:', err)
  196. }
  197. }
  198. // 获取模块列表
  199. const fetchModuleList = async () => {
  200. try {
  201. const res = await getModuleList()
  202. // console.log('getModuleList 响应数据:', res)
  203. const data = res.data || res
  204. // 筛选"在线"相关的模块,映射为 tabList 格式
  205. const onlineList = (data || []).map((item: any) => ({
  206. key: String(item.code),
  207. label: item.name
  208. }))
  209. if (onlineList.length) {
  210. onlineTypeTablist.value = onlineList
  211. defaultOnlineTypeTab.value = onlineList[0].key
  212. }
  213. } catch (err) {
  214. console.error('getModuleList 请求失败:', err)
  215. }
  216. }
  217. // 获取图表数据
  218. const fetchChartData = async () => {
  219. try {
  220. const res = await getChartData({
  221. schoolId: 0,
  222. timeType: 0,
  223. moduleCode: 0,
  224. startTime: '',
  225. endTime: '',
  226. })
  227. // console.log('getChartData 响应数据:', res)
  228. const data = res.data || res
  229. if (data) {
  230. // somePeriodTimeLineData.datax = data.map((item: any) => item.xname)
  231. // somePeriodTimeLineData.datay = data.map((item: any) => item.count)
  232. // somePeriodTimeLineData.datax = data.xname
  233. // somePeriodTimeLineData.datay = data.count
  234. }
  235. } catch (err) {
  236. console.error('getChartData 请求失败:', err)
  237. }
  238. }
  239. onMounted(() => {
  240. fetchHeadData()
  241. fetchModuleList()
  242. fetchChartData()
  243. })
  244. </script>
  245. <style lang="scss" scoped>
  246. .cardBoard {
  247. display: grid;
  248. grid-template-columns: repeat(5, 1fr);
  249. gap: 20px;
  250. li {
  251. background-color: #f2f2f2;
  252. padding: 20px;
  253. border-radius: 8px;
  254. .label {
  255. font-size: 16px;
  256. }
  257. .num {
  258. font-size: 34px;
  259. font-weight: 600;
  260. }
  261. }
  262. }
  263. .chart_box {
  264. display: flex;
  265. flex-direction: column;
  266. gap: 10px;
  267. .chart {
  268. border: 1px solid #dcdfe6;
  269. min-height: 320px;
  270. padding: 20px;
  271. }
  272. }
  273. </style>