DashboardView.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. <template>
  2. <div class="p-4 md:p-8">
  3. <!-- 统计卡片区域 -->
  4. <a-row :gutter="[16, 16]" class="mb-4 md:mb-8">
  5. <!-- 工厂总数卡片 -->
  6. <a-col :xs="24" :sm="12" :lg="6">
  7. <a-card class="stat-card bg-gradient-to-br from-blue-500 to-blue-600 text-white rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-300 !border-0">
  8. <div class="flex items-center justify-between">
  9. <div>
  10. <div class="text-sm opacity-80 mb-2">工厂总数</div>
  11. <div class="text-4xl font-bold">{{ dashboardData.factory_count }}</div>
  12. <div class="text-sm mt-2 opacity-70">家</div>
  13. </div>
  14. <ShopOutlined class="text-5xl opacity-80" />
  15. </div>
  16. </a-card>
  17. </a-col>
  18. <!-- 启用工厂卡片 -->
  19. <a-col :xs="24" :sm="12" :lg="6">
  20. <a-card class="stat-card bg-gradient-to-br from-green-500 to-green-600 text-white rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-300 !border-0">
  21. <div class="flex items-center justify-between">
  22. <div>
  23. <div class="text-sm opacity-80 mb-2">启用工厂</div>
  24. <div class="text-4xl font-bold">{{ dashboardData.enabled_factory_count }}</div>
  25. <div class="text-sm mt-2 opacity-70">家</div>
  26. </div>
  27. <CheckCircleOutlined class="text-5xl opacity-80" />
  28. </div>
  29. </a-card>
  30. </a-col>
  31. <!-- 系统用户卡片 -->
  32. <a-col :xs="24" :sm="12" :lg="6">
  33. <a-card class="stat-card bg-gradient-to-br from-purple-500 to-purple-600 text-white rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-300 !border-0">
  34. <div class="flex items-center justify-between">
  35. <div>
  36. <div class="text-sm opacity-80 mb-2">系统用户</div>
  37. <div class="text-4xl font-bold">{{ dashboardData.user_count }}</div>
  38. <div class="text-sm mt-2 opacity-70">人</div>
  39. </div>
  40. <UserOutlined class="text-5xl opacity-80" />
  41. </div>
  42. </a-card>
  43. </a-col>
  44. <!-- 审计日志卡片 -->
  45. <a-col :xs="24" :sm="12" :lg="6">
  46. <a-card class="stat-card bg-gradient-to-br from-orange-500 to-orange-600 text-white rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-300 !border-0">
  47. <div class="flex items-center justify-between">
  48. <div>
  49. <div class="text-sm opacity-80 mb-2">审计日志</div>
  50. <div class="text-4xl font-bold">{{ dashboardData.log_count }}</div>
  51. <div class="text-sm mt-2 opacity-70">条</div>
  52. </div>
  53. <FileTextOutlined class="text-5xl opacity-80" />
  54. </div>
  55. </a-card>
  56. </a-col>
  57. </a-row>
  58. <!-- 快捷操作卡片 -->
  59. <a-card class="rounded-xl shadow-md !border-0" title="快捷操作">
  60. <template #extra>
  61. <span class="text-gray-400 text-sm">常用功能快速访问</span>
  62. </template>
  63. <a-space size="large">
  64. <a-button
  65. type="primary"
  66. size="large"
  67. @click="router.push('/factories')"
  68. class="shadow-md hover:shadow-lg transition-shadow"
  69. >
  70. <ShopOutlined class="mr-2" />
  71. 管理工厂
  72. </a-button>
  73. <a-button
  74. size="large"
  75. @click="router.push('/users')"
  76. class="shadow-sm hover:shadow-md transition-shadow"
  77. >
  78. <UserOutlined class="mr-2" />
  79. 系统用户
  80. </a-button>
  81. </a-space>
  82. </a-card>
  83. </div>
  84. </template>
  85. <script setup lang="ts">
  86. import { ref, onMounted } from 'vue';
  87. import { useRouter } from 'vue-router';
  88. import { message } from 'ant-design-vue';
  89. import { ShopOutlined, CheckCircleOutlined, UserOutlined, FileTextOutlined } from '@ant-design/icons-vue';
  90. import { SystemFactoryApi, SystemUserApi, AuditLogApi, createApiClient } from '@smartcut/api-client';
  91. import { STORAGE_KEYS } from '@smartcut/shared-utils';
  92. import { getApiBaseUrl } from '@/apiConfig';
  93. const router = useRouter();
  94. const dashboardData = ref({
  95. factory_count: 0,
  96. enabled_factory_count: 0,
  97. user_count: 0,
  98. log_count: 0
  99. });
  100. const apiClient = createApiClient({
  101. baseURL: getApiBaseUrl(),
  102. tokenKey: STORAGE_KEYS.PLATFORM_TOKEN
  103. });
  104. const factoryApi = new SystemFactoryApi(apiClient);
  105. const userApi = new SystemUserApi(apiClient);
  106. const auditLogApi = new AuditLogApi(apiClient);
  107. onMounted(async () => {
  108. // 并行获取三组数据,任一失败不影响其他卡片
  109. const [factoryRes, userRes, logRes] = await Promise.allSettled([
  110. factoryApi.listFactories(),
  111. userApi.getUsers({ page: 1, page_size: 1 }),
  112. auditLogApi.getAuditLogs({ page: 1, page_size: 1 })
  113. ]);
  114. if (factoryRes.status === 'fulfilled') {
  115. dashboardData.value.factory_count = factoryRes.value.total;
  116. dashboardData.value.enabled_factory_count = factoryRes.value.list.filter(f => f.status === 1).length;
  117. }
  118. if (userRes.status === 'fulfilled') {
  119. dashboardData.value.user_count = userRes.value.total;
  120. }
  121. if (logRes.status === 'fulfilled') {
  122. dashboardData.value.log_count = logRes.value.total;
  123. }
  124. // 三组全部失败时才提示错误
  125. if (factoryRes.status === 'rejected' && userRes.status === 'rejected' && logRes.status === 'rejected') {
  126. message.error('加载看板数据失败');
  127. }
  128. });
  129. </script>