perf: 美化数据看板

This commit is contained in:
CaIon 2024-01-08 11:32:27 +08:00
parent daf0be4915
commit 8f36a995ef
3 changed files with 126 additions and 97 deletions

View File

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

View File

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

View File

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