Pārlūkot izejas kodu

perf: 美化数据看板

CaIon 2 gadi atpakaļ
vecāks
revīzija
8f36a995ef
3 mainītis faili ar 127 papildinājumiem un 98 dzēšanām
  1. 1 1
      model/usedata.go
  2. 94 85
      web/src/helpers/render.js
  3. 32 12
      web/src/pages/Detail/index.js

+ 1 - 1
model/usedata.go

@@ -12,7 +12,7 @@ type QuotaData struct {
 	Id        int    `json:"id"`
 	UserID    int    `json:"user_id" gorm:"index"`
 	Username  string `json:"username" gorm:"index:idx_qdt_model_user_name,priority:2;size:64;default:''"`
-	ModelName string `json:"model_name" gorm:"index;index:idx_qdt_model_user_name,priority:1;size:64;default:''"`
+	ModelName string `json:"model_name" gorm:"index:idx_qdt_model_user_name,priority:1;size:64;default:''"`
 	CreatedAt int64  `json:"created_at" gorm:"bigint;index:idx_qdt_created_at,priority:2"`
 	Count     int    `json:"count" gorm:"default:0"`
 	Quota     int    `json:"quota" gorm:"default:0"`

+ 94 - 85
web/src/helpers/render.js

@@ -1,120 +1,129 @@
-import { Label } from 'semantic-ui-react';
+import {Label} from 'semantic-ui-react';
 import {Tag} from "@douyinfe/semi-ui";
 
 export function renderText(text, limit) {
-  if (text.length > limit) {
-    return text.slice(0, limit - 3) + '...';
-  }
-  return text;
+    if (text.length > limit) {
+        return text.slice(0, limit - 3) + '...';
+    }
+    return text;
 }
 
 export function renderGroup(group) {
-  if (group === '') {
-    return <Tag size='large'>default</Tag>;
-  }
-  let groups = group.split(',');
-  groups.sort();
-  return <>
-    {groups.map((group) => {
-      if (group === 'vip' || group === 'pro') {
-        return <Tag size='large' color='yellow'>{group}</Tag>;
-      } else if (group === 'svip' || group === 'premium') {
-        return <Tag size='large' color='red'>{group}</Tag>;
-      }
-      if (group === 'default') {
-        return <Tag size='large'>{group}</Tag>;
-      } else {
-        return <Tag size='large' color={stringToColor(group)}>{group}</Tag>;
-      }
-    })}
-  </>;
+    if (group === '') {
+        return <Tag size='large'>default</Tag>;
+    }
+    let groups = group.split(',');
+    groups.sort();
+    return <>
+        {groups.map((group) => {
+            if (group === 'vip' || group === 'pro') {
+                return <Tag size='large' color='yellow'>{group}</Tag>;
+            } else if (group === 'svip' || group === 'premium') {
+                return <Tag size='large' color='red'>{group}</Tag>;
+            }
+            if (group === 'default') {
+                return <Tag size='large'>{group}</Tag>;
+            } else {
+                return <Tag size='large' color={stringToColor(group)}>{group}</Tag>;
+            }
+        })}
+    </>;
 }
 
 export function renderNumber(num) {
-  if (num >= 1000000000) {
-    return (num / 1000000000).toFixed(1) + 'B';
-  } else if (num >= 1000000) {
-    return (num / 1000000).toFixed(1) + 'M';
-  } else if (num >= 10000) {
-    return (num / 1000).toFixed(1) + 'k';
-  } else {
+    if (num >= 1000000000) {
+        return (num / 1000000000).toFixed(1) + 'B';
+    } else if (num >= 1000000) {
+        return (num / 1000000).toFixed(1) + 'M';
+    } else if (num >= 10000) {
+        return (num / 1000).toFixed(1) + 'k';
+    } else {
+        return num;
+    }
+}
+
+export function renderQuotaNumberWithDigit(num, digits = 2) {
+    let displayInCurrency = localStorage.getItem('display_in_currency');
+    num = num.toFixed(digits);
+    if (displayInCurrency) {
+        return '$' + num;
+    }
     return num;
-  }
 }
 
 export function renderNumberWithPoint(num) {
-  num = num.toFixed(2);
-  if (num >= 100000) {
-    // Convert number to string to manipulate it
-    let numStr = num.toString();
-    // Find the position of the decimal point
-    let decimalPointIndex = numStr.indexOf('.');
-
-    let wholePart = numStr;
-    let decimalPart = '';
-
-    // If there is a decimal point, split the number into whole and decimal parts
-    if (decimalPointIndex !== -1) {
-      wholePart = numStr.slice(0, decimalPointIndex);
-      decimalPart = numStr.slice(decimalPointIndex);
-    }
+    num = num.toFixed(2);
+    if (num >= 100000) {
+        // Convert number to string to manipulate it
+        let numStr = num.toString();
+        // Find the position of the decimal point
+        let decimalPointIndex = numStr.indexOf('.');
 
-    // Take the first two and last two digits of the whole number part
-    let shortenedWholePart = wholePart.slice(0, 2) + '..' + wholePart.slice(-2);
+        let wholePart = numStr;
+        let decimalPart = '';
 
-    // Return the formatted number
-    return shortenedWholePart + decimalPart;
-  }
+        // If there is a decimal point, split the number into whole and decimal parts
+        if (decimalPointIndex !== -1) {
+            wholePart = numStr.slice(0, decimalPointIndex);
+            decimalPart = numStr.slice(decimalPointIndex);
+        }
 
-  // If the number is less than 100,000, return it unmodified
-  return num;
+        // Take the first two and last two digits of the whole number part
+        let shortenedWholePart = wholePart.slice(0, 2) + '..' + wholePart.slice(-2);
+
+        // Return the formatted number
+        return shortenedWholePart + decimalPart;
+    }
+
+    // If the number is less than 100,000, return it unmodified
+    return num;
 }
 
 export function getQuotaPerUnit() {
-  let quotaPerUnit = localStorage.getItem('quota_per_unit');
-  quotaPerUnit = parseFloat(quotaPerUnit);
-  return quotaPerUnit;
+    let quotaPerUnit = localStorage.getItem('quota_per_unit');
+    quotaPerUnit = parseFloat(quotaPerUnit);
+    return quotaPerUnit;
 }
 
 export function getQuotaWithUnit(quota, digits = 6) {
-  let quotaPerUnit = localStorage.getItem('quota_per_unit');
-  quotaPerUnit = parseFloat(quotaPerUnit);
-  return (quota / quotaPerUnit).toFixed(digits);
+    let quotaPerUnit = localStorage.getItem('quota_per_unit');
+    quotaPerUnit = parseFloat(quotaPerUnit);
+    return (quota / quotaPerUnit).toFixed(digits);
 }
 
 export function renderQuota(quota, digits = 2) {
-  let quotaPerUnit = localStorage.getItem('quota_per_unit');
-  let displayInCurrency = localStorage.getItem('display_in_currency');
-  quotaPerUnit = parseFloat(quotaPerUnit);
-  displayInCurrency = displayInCurrency === 'true';
-  if (displayInCurrency) {
-    return '$' + (quota / quotaPerUnit).toFixed(digits);
-  }
-  return renderNumber(quota);
+    let quotaPerUnit = localStorage.getItem('quota_per_unit');
+    let displayInCurrency = localStorage.getItem('display_in_currency');
+    quotaPerUnit = parseFloat(quotaPerUnit);
+    displayInCurrency = displayInCurrency === 'true';
+    if (displayInCurrency) {
+        return '$' + (quota / quotaPerUnit).toFixed(digits);
+    }
+    return renderNumber(quota);
 }
 
 export function renderQuotaWithPrompt(quota, digits) {
-  let displayInCurrency = localStorage.getItem('display_in_currency');
-  displayInCurrency = displayInCurrency === 'true';
-  if (displayInCurrency) {
-    return `(等价金额:${renderQuota(quota, digits)})`;
-  }
-  return '';
+    let displayInCurrency = localStorage.getItem('display_in_currency');
+    displayInCurrency = displayInCurrency === 'true';
+    if (displayInCurrency) {
+        return `(等价金额:${renderQuota(quota, digits)})`;
+    }
+    return '';
 }
 
 const colors = ['amber', 'blue', 'cyan', 'green', 'grey', 'indigo',
-  'light-blue', 'lime', 'orange', 'pink',
-  'purple', 'red', 'teal', 'violet', 'yellow'
+    'light-blue', 'lime', 'orange', 'pink',
+    'purple', 'red', 'teal', 'violet', 'yellow'
 ]
 
 export function stringToColor(str) {
-  let sum = 0;
-  // 对字符串中的每个字符进行操作
-  for (let i = 0; i < str.length; i++) {
-    // 将字符的ASCII值加到sum中
-    sum += str.charCodeAt(i);
-  }
-  // 使用模运算得到个位数
-  let i = sum % colors.length;
-  return colors[i];
+    let sum = 0;
+    // 对字符串中的每个字符进行操作
+    for (let i = 0; i < str.length; i++) {
+        // 将字符的ASCII值加到sum中
+        sum += str.charCodeAt(i);
+    }
+    // 使用模运算得到个位数
+    let i = sum % colors.length;
+    return colors[i];
 }

+ 32 - 12
web/src/pages/Detail/index.js

@@ -1,9 +1,9 @@
 import React, {useEffect, useRef, useState} from 'react';
-import {Button, Col, Form, Layout, Row} from "@douyinfe/semi-ui";
+import {Button, Col, Form, Layout, Row, Spin} from "@douyinfe/semi-ui";
 import VChart from '@visactor/vchart';
 import {useEffectOnce} from "usehooks-ts";
 import {API, isAdmin, showError, timestamp2string, timestamp2string1} from "../../helpers";
-import {getQuotaWithUnit} from "../../helpers/render";
+import {getQuotaWithUnit, renderNumber, renderQuotaNumberWithDigit} from "../../helpers/render";
 
 const Detail = (props) => {
 
@@ -48,7 +48,7 @@ const Detail = (props) => {
         },
         title: {
             visible: true,
-            text: '模型消耗分布'
+            text: '模型消耗分布(小时)'
         },
         bar: {
             // The state style of bar
@@ -58,6 +58,24 @@ const Detail = (props) => {
                     lineWidth: 1
                 }
             }
+        },
+        tooltip: {
+            mark: {
+                content: [
+                    {
+                        key: datum => datum['Model'],
+                        value: datum => renderQuotaNumberWithDigit(datum['Usage'], 4)
+                    }
+                ]
+            },
+            dimension: {
+                content: [
+                    {
+                        key: datum => datum['Model'],
+                        value: datum => renderQuotaNumberWithDigit(datum['Usage'], 4)
+                    }
+                ]
+            }
         }
     };
 
@@ -110,7 +128,7 @@ const Detail = (props) => {
                 content: [
                     {
                         key: datum => datum['type'],
-                        value: datum => datum['value']
+                        value: datum => renderNumber(datum['value'])
                     }
                 ]
             }
@@ -215,7 +233,7 @@ const Detail = (props) => {
         <>
             <Layout>
                 <Layout.Header>
-                    <h3>数据看板(24H)</h3>
+                    <h3>数据看板</h3>
                 </Layout.Header>
                 <Layout.Content>
                     <Form layout='horizontal' style={{marginTop: 10}}>
@@ -239,16 +257,18 @@ const Detail = (props) => {
                             {/*}*/}
                             <Form.Section>
                                 <Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"
-                                        onClick={refresh}>查询</Button>
+                                        onClick={refresh} loading={loading}>查询</Button>
                             </Form.Section>
                         </>
                     </Form>
-                    <div style={{height: 500}}>
-                        <div id="model_pie" style={{width: '100%', minWidth: 100}}></div>
-                    </div>
-                    <div style={{height: 500}}>
-                        <div id="model_data" style={{width: '100%', minWidth: 100}}></div>
-                    </div>
+                    <Spin spinning={loading}>
+                        <div style={{height: 500}}>
+                            <div id="model_pie" style={{width: '100%', minWidth: 100}}></div>
+                        </div>
+                        <div style={{height: 500}}>
+                            <div id="model_data" style={{width: '100%', minWidth: 100}}></div>
+                        </div>
+                    </Spin>
                 </Layout.Content>
             </Layout>
         </>