Kaynağa Gözat

feat(StatisticsView): 优化余额展示和刷新功能

- 为每个渠道添加余额加载状态和超时重试功能
- 实现单个渠道余额刷新功能
- 优化余额数据的获取和展示逻辑
- 添加骨架屏加载效果
wui 6 ay önce
ebeveyn
işleme
ff5872f09d
1 değiştirilmiş dosya ile 109 ekleme ve 31 silme
  1. 109 31
      src/views/StatisticsView.vue

+ 109 - 31
src/views/StatisticsView.vue

@@ -5,12 +5,34 @@
     <div class="myTable" :style="{ width: '100%', marginTop: '20px' }">
     <div class="myTable" :style="{ width: '100%', marginTop: '20px' }">
         <ElTable :data="tableData" stripe show-summary>
         <ElTable :data="tableData" stripe show-summary>
             <ElTableColumn prop="channel" label="渠道" align="center" />
             <ElTableColumn prop="channel" label="渠道" align="center" />
-            z
             <ElTableColumn label="取码量" align="center">
             <ElTableColumn label="取码量" align="center">
                 <ElTableColumn prop="yesterdayData" label="昨日数据" align="center" sortable />
                 <ElTableColumn prop="yesterdayData" label="昨日数据" align="center" sortable />
                 <ElTableColumn prop="todayData" label="今日数据" align="center" sortable />
                 <ElTableColumn prop="todayData" label="今日数据" align="center" sortable />
             </ElTableColumn>
             </ElTableColumn>
-            <ElTableColumn prop="balance" label="余额" align="center" />
+            <ElTableColumn prop="balance" label="余额" align="center">
+                <template #default="{ row }">
+                    <div style="display: flex; align-items: center; justify-content: center; gap: 8px">
+                        <ElSkeleton style="width: 100%" animated :loading="row.balanceLoading" :count="1">
+                            <template #template>
+                                <div style="display: flex; align-items: center; justify-content: center">
+                                    <ElSkeletonItem variant="text" style="width: 100px"/>
+                                </div>
+                            </template>
+                            <template #default>
+                                <span>{{ row.balance }}</span>
+                            </template>
+                        </ElSkeleton>
+                        <ElButton 
+                            v-if="row.balance === '超时'" 
+                            :icon="Refresh" 
+                            circle
+                            text
+                            size="small"
+                            @click="refreshChannelBalance(row.channel)"
+                        />
+                    </div>
+                </template>
+            </ElTableColumn>
         </ElTable>
         </ElTable>
     </div>
     </div>
     <div style="margin-top: 10px">
     <div style="margin-top: 10px">
@@ -55,49 +77,79 @@ const numData = ref([])
 const sentHourData = ref([])
 const sentHourData = ref([])
 const backupData = ref([])
 const backupData = ref([])
 
 
+// 支持的平台列表
+const supportedPlatforms = {
+    durian: 0,
+    durian02: 0,
+    xyz: 0,
+    usapanel: 0,
+    dashboard: 0,
+    smspva: 0,
+    smspva02: 0,
+    smstiger: 0
+}
+
 onMounted(() => {
 onMounted(() => {
-    codeStatistics()
-    hourSentStatistics()
-    numStatistics()
-    backupStatistics()
+    refresh()
 })
 })
 
 
 async function refresh() {
 async function refresh() {
-    codeStatistics()
-    hourSentStatistics()
-    numStatistics()
-    backupStatistics()
+    await Promise.all([
+        codeStatistics(),
+        hourSentStatistics(),
+        numStatistics(),
+        backupStatistics()
+    ])
 }
 }
 
 
 async function codeStatistics() {
 async function codeStatistics() {
     tableData.value = []
     tableData.value = []
     const quantityResponse = await http.get('/task/codeStatistics')
     const quantityResponse = await http.get('/task/codeStatistics')
-    const balanceResponse = await http.get('/task/balanceStatistics')
-
-    // 将余额数据转为 Map,方便后续快速查找
-    const balanceMap = new Map(Object.entries(balanceResponse))
 
 
-    // 遍历数量数据,将数量和余额合并
-    quantityResponse.forEach((item) => {
-        const balance = balanceMap.get(item.channel)
-        tableData.value.push({
-            channel: item.channel,
+    const dataMap = new Map(
+        quantityResponse.map(item => [item.channel, {
             todayData: parseInt(item.todayData, 10) || 0,
             todayData: parseInt(item.todayData, 10) || 0,
-            yesterdayData: parseInt(item.yesterdayData, 10) || 0,
-            balance: balance !== undefined ? parseInt(balance, 10) : '-'
-        })
+            yesterdayData: parseInt(item.yesterdayData, 10) || 0
+        }])
+    )
 
 
-        balanceMap.delete(item.channel)
+    tableData.value = Object.keys(supportedPlatforms).map(channel => {
+        const data = dataMap.get(channel) || { todayData: 0, yesterdayData: 0 }
+        return {
+            channel,
+            todayData: data.todayData,
+            yesterdayData: data.yesterdayData,
+            balance: 0,
+            balanceLoading: false
+        }
     })
     })
 
 
-    for (const [channel, balance] of balanceMap) {
-        tableData.value.push({
-            channel,
-            todayData: 0,
-            yesterdayData: 0,
-            balance: parseInt(balance, 10)
-        })
-    }
+    const balancePromises = tableData.value.map(row => {
+        if (row.channel in supportedPlatforms) {
+            row.balanceLoading = true
+            return Promise.race([
+                http.get(`/task/balanceStatistics/${row.channel}`)
+                    .then(response => {
+                        const balance = response[row.channel]
+                        row.balance = balance ? parseFloat(balance).toFixed(4) : 0
+                    })
+                    .catch(error => {
+                        console.error(`获取${row.channel}余额失败:`, error)
+                        row.balance = error.message === 'Timeout' ? '超时' : 0
+                    })
+                    .finally(() => {
+                        row.balanceLoading = false
+                    }),
+                new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 15000))
+            ]).catch((error) => {
+                row.balance = error.message === 'Timeout' ? '超时' : 0
+                row.balanceLoading = false
+            })
+        }
+        return Promise.resolve()
+    })
+
+    await Promise.all(balancePromises)
 }
 }
 
 
 async function hourSentStatistics() {
 async function hourSentStatistics() {
@@ -114,4 +166,30 @@ async function backupStatistics() {
     backupData.value = []
     backupData.value = []
     backupData.value = await http.get(`/task/backupStatistics`)
     backupData.value = await http.get(`/task/backupStatistics`)
 }
 }
+
+async function refreshChannelBalance(channel) {
+    const row = tableData.value.find(item => item.channel === channel)
+    if (!row) return
+
+    row.balanceLoading = true
+    try {
+        const response = await Promise.race([
+            http.get(`/task/balanceStatistics/${channel}`),
+            new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 30000))
+        ])
+        const balance = response[channel]
+        row.balance = balance ? parseFloat(balance).toFixed(4) : 0
+    } catch (error) {
+        console.error(`获取${channel}余额失败:`, error)
+        row.balance = error.message === 'Timeout' ? '超时' : 0
+    } finally {
+        row.balanceLoading = false
+    }
+}
 </script>
 </script>
+
+<style scoped>
+.el-skeleton {
+    --el-skeleton-text-width: 100%;
+}
+</style>