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