pages_chart.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import React from 'react';
  2. import {
  3. Layout, Menu, Button, Table, Space,
  4. Form, Input, Select, message, Modal,
  5. Card, Spin, Row, Col, Breadcrumb, Avatar,
  6. Dropdown, ConfigProvider, theme, Typography,
  7. Switch, Badge, Image, Upload, Divider, Descriptions,
  8. Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer
  9. } from 'antd';
  10. import {
  11. useQuery,
  12. } from '@tanstack/react-query';
  13. import { Line , Pie, Column} from "@ant-design/plots";
  14. import 'dayjs/locale/zh-cn';
  15. import { ChartAPI } from './api.ts';
  16. import { useTheme } from './hooks_sys.tsx';
  17. interface ChartTooltipInfo {
  18. items: Array<Record<string, any>>;
  19. title: string;
  20. }
  21. // 用户活跃度图表组件
  22. const UserActivityChart: React.FC = () => {
  23. const { isDark } = useTheme();
  24. const { data: activityData, isLoading } = useQuery({
  25. queryKey: ['userActivity'],
  26. queryFn: async () => {
  27. const response = await ChartAPI.getUserActivity();
  28. return response.data;
  29. }
  30. });
  31. if (isLoading) return <Spin />;
  32. const config = {
  33. data: activityData || [],
  34. xField: 'date',
  35. yField: 'count',
  36. smooth: true,
  37. theme: isDark ? 'dark' : 'light',
  38. color: '#1890ff',
  39. areaStyle: {
  40. fill: 'l(270) 0:#1890ff10 1:#1890ff',
  41. },
  42. };
  43. return (
  44. <Card title="用户活跃度趋势" bordered={false}>
  45. <Line {...config} />
  46. </Card>
  47. );
  48. };
  49. // 文件上传统计图表组件
  50. const FileUploadsChart: React.FC = () => {
  51. const { isDark } = useTheme();
  52. const { data: uploadsData, isLoading } = useQuery({
  53. queryKey: ['fileUploads'],
  54. queryFn: async () => {
  55. const response = await ChartAPI.getFileUploads();
  56. return response.data;
  57. }
  58. });
  59. if (isLoading) return <Spin />;
  60. const config = {
  61. data: uploadsData || [],
  62. xField: 'month',
  63. yField: 'count',
  64. theme: isDark ? 'dark' : 'light',
  65. color: '#52c41a',
  66. label: {
  67. position: 'middle',
  68. style: {
  69. fill: '#FFFFFF',
  70. opacity: 0.6,
  71. },
  72. },
  73. meta: {
  74. month: {
  75. alias: '月份',
  76. },
  77. count: {
  78. alias: '上传数量',
  79. },
  80. },
  81. };
  82. return (
  83. <Card title="文件上传统计" bordered={false}>
  84. <Column {...config} />
  85. </Card>
  86. );
  87. };
  88. // 文件类型分布图表组件
  89. const FileTypesChart: React.FC = () => {
  90. const { isDark } = useTheme();
  91. const { data: typesData, isLoading } = useQuery({
  92. queryKey: ['fileTypes'],
  93. queryFn: async () => {
  94. const response = await ChartAPI.getFileTypes();
  95. return response.data;
  96. }
  97. });
  98. if (isLoading) return <Spin />;
  99. const config = {
  100. data: typesData || [],
  101. angleField: 'value',
  102. colorField: 'type',
  103. radius: 0.8,
  104. theme: isDark ? 'dark' : 'light',
  105. label: {
  106. type: 'spider',
  107. labelHeight: 28,
  108. content: '{name}\n{percentage}',
  109. },
  110. interactions: [
  111. {
  112. type: 'element-active',
  113. },
  114. ],
  115. };
  116. return (
  117. <Card title="文件类型分布" bordered={false}>
  118. <Pie {...config} />
  119. </Card>
  120. );
  121. };
  122. // 仪表盘概览组件
  123. const DashboardOverview: React.FC = () => {
  124. const { data: overviewData, isLoading } = useQuery({
  125. queryKey: ['dashboardOverview'],
  126. queryFn: async () => {
  127. const response = await ChartAPI.getDashboardOverview();
  128. return response.data;
  129. }
  130. });
  131. if (isLoading) return <Spin />;
  132. return (
  133. <Row gutter={[16, 16]}>
  134. <Col xs={12} sm={12} md={6}>
  135. <Card bordered={false}>
  136. <Statistic
  137. title="用户总数"
  138. value={overviewData?.userCount || 0}
  139. valueStyle={{ color: '#1890ff' }}
  140. />
  141. </Card>
  142. </Col>
  143. <Col xs={12} sm={12} md={6}>
  144. <Card bordered={false}>
  145. <Statistic
  146. title="文件总数"
  147. value={overviewData?.fileCount || 0}
  148. valueStyle={{ color: '#52c41a' }}
  149. />
  150. </Card>
  151. </Col>
  152. <Col xs={12} sm={12} md={6}>
  153. <Card bordered={false}>
  154. <Statistic
  155. title="文章总数"
  156. value={overviewData?.articleCount || 0}
  157. valueStyle={{ color: '#faad14' }}
  158. />
  159. </Card>
  160. </Col>
  161. <Col xs={12} sm={12} md={6}>
  162. <Card bordered={false}>
  163. <Statistic
  164. title="今日登录"
  165. value={overviewData?.todayLoginCount || 0}
  166. valueStyle={{ color: '#722ed1' }}
  167. />
  168. </Card>
  169. </Col>
  170. </Row>
  171. );
  172. };
  173. // 图表仪表盘页面组件
  174. export const ChartDashboardPage: React.FC = () => {
  175. return (
  176. <div className="chart-dashboard">
  177. <DashboardOverview />
  178. <div style={{ height: 24 }} />
  179. <Row gutter={[16, 16]}>
  180. <Col xs={24} lg={12}>
  181. <UserActivityChart />
  182. </Col>
  183. <Col xs={24} lg={12}>
  184. <FileUploadsChart />
  185. </Col>
  186. <Col xs={24}>
  187. <FileTypesChart />
  188. </Col>
  189. </Row>
  190. </div>
  191. );
  192. };