index.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <template>
  2. <view class="db-page">
  3. <view class="db-header" :style="safeAreaStyle">
  4. <view class="db-header__nav">
  5. <view class="db-header__left">
  6. <text class="db-header__back" @click="goWorkbench">←</text>
  7. <text class="db-header__title">仪表盘</text>
  8. </view>
  9. <view /><!-- 胶囊占位 -->
  10. </view>
  11. </view>
  12. <view class="db-menu-grid">
  13. <view class="db-menu-item" @click="go(PAGE_PATHS.dbPlansList)">
  14. <view class="db-menu-icon">📋</view>
  15. <view class="db-menu-label">计划管理</view>
  16. </view>
  17. <view class="db-menu-item" @click="go(PAGE_PATHS.dbInventoryList)">
  18. <view class="db-menu-icon">📦</view>
  19. <view class="db-menu-label">出入库管理</view>
  20. </view>
  21. <view class="db-menu-item" @click="go(PAGE_PATHS.dbWeighingList)">
  22. <view class="db-menu-icon">⚖️</view>
  23. <view class="db-menu-label">地磅单管理</view>
  24. </view>
  25. </view>
  26. <view class="db-stat-grid">
  27. <view class="db-stat-card">
  28. <view class="db-stat-title">今日入库</view>
  29. <view class="db-stat-value">
  30. {{ inboundCount }}<text class="db-stat-suffix">车</text>
  31. </view>
  32. </view>
  33. <view class="db-stat-card">
  34. <view class="db-stat-title">今日出库</view>
  35. <view class="db-stat-value">
  36. {{ outboundCount }}<text class="db-stat-suffix">车</text>
  37. </view>
  38. </view>
  39. </view>
  40. <view v-for="block in chartBlocks" :key="block.title" class="db-chart-block">
  41. <view class="db-chart-title">{{ block.title }}</view>
  42. <view v-if="!block.rows.length" class="db-empty" style="padding: 16px">暂无数据</view>
  43. <view v-for="row in block.rows" :key="row.label" class="db-bar-row">
  44. <view class="db-bar-label">{{ row.label }}</view>
  45. <view class="db-bar-track">
  46. <view class="db-bar-planned" :style="{ width: barPct(row.planned, block.max) }" />
  47. <view class="db-bar-actual" :style="{ width: barPct(row.actual, block.max) }" />
  48. </view>
  49. <view class="db-bar-meta">计划 {{ fmt(row.planned) }} · 实绩 {{ fmt(row.actual) }}</view>
  50. </view>
  51. </view>
  52. </view>
  53. </template>
  54. <script setup>
  55. import { ref, computed, onMounted } from 'vue'
  56. import { onShow } from '@dcloudio/uni-app'
  57. import { useUserStore } from '@/stores/user'
  58. import { PAGE_PATHS } from '@/config/api'
  59. import { dashboardApi } from '@/pages/db/api/db'
  60. import { getSafeAreaStyle } from '@/utils/safeArea'
  61. const safeAreaStyle = getSafeAreaStyle()
  62. const userStore = useUserStore()
  63. const inboundByCargo = ref([])
  64. const outboundByCargo = ref([])
  65. const inboundByPartner = ref([])
  66. const outboundByPartner = ref([])
  67. const inboundCount = computed(() => {
  68. const set = new Set()
  69. inboundByCargo.value.forEach((i) => {
  70. if (Number(i.planned) > 0) set.add(i.label)
  71. })
  72. return set.size
  73. })
  74. const outboundCount = computed(() => {
  75. const set = new Set()
  76. outboundByCargo.value.forEach((i) => {
  77. if (Number(i.planned) > 0) set.add(i.label)
  78. })
  79. return set.size
  80. })
  81. const chartBlocks = computed(() => [
  82. pack('今日入库量(按货物,t)', inboundByCargo.value),
  83. pack('今日出库量(按货物,t)', outboundByCargo.value),
  84. pack('今日入库量(按往来单位+货物,t)', inboundByPartner.value),
  85. pack('今日出库量(按往来单位+货物,t)', outboundByPartner.value)
  86. ])
  87. function pack(title, rows) {
  88. const list = Array.isArray(rows) ? rows : []
  89. const max = Math.max(1, ...list.map((r) => Math.max(Number(r.planned) || 0, Number(r.actual) || 0)))
  90. return { title, rows: list, max }
  91. }
  92. function barPct(v, max) {
  93. const n = Number(v) || 0
  94. return `${Math.min(100, (n / max) * 100)}%`
  95. }
  96. function fmt(v) {
  97. const n = Number(v)
  98. return Number.isFinite(n) ? n.toFixed(2) : '0'
  99. }
  100. function goWorkbench() {
  101. uni.reLaunch({ url: PAGE_PATHS.workbench })
  102. }
  103. function go(url) {
  104. uni.navigateTo({ url })
  105. }
  106. async function load() {
  107. try {
  108. const [a, b, c, d] = await Promise.all([
  109. dashboardApi.getInboundByCargo(),
  110. dashboardApi.getOutboundByCargo(),
  111. dashboardApi.getInboundByPartnerCargo(),
  112. dashboardApi.getOutboundByPartnerCargo()
  113. ])
  114. inboundByCargo.value = a || []
  115. outboundByCargo.value = b || []
  116. inboundByPartner.value = c || []
  117. outboundByPartner.value = d || []
  118. } catch (e) {
  119. console.error(e)
  120. }
  121. }
  122. onMounted(load)
  123. onShow(() => {
  124. userStore.checkLoginStatus()
  125. if (!userStore.isLoggedIn) {
  126. uni.reLaunch({ url: PAGE_PATHS.login })
  127. }
  128. })
  129. </script>
  130. <style lang="scss">
  131. @import '@/styles/db-theme.scss';
  132. </style>