mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-17 16:06:38 +08:00
perf: 美化数据看板
This commit is contained in:
parent
daf0be4915
commit
8f36a995ef
@ -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"`
|
||||
|
@ -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('.');
|
||||
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 = '';
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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
|
||||
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;
|
||||
// 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];
|
||||
}
|
@ -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>
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user