import React, {useEffect, useRef, useState} from 'react'; 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, modelColorMap, renderNumber, renderQuota, renderQuotaNumberWithDigit, stringToColor } from "../../helpers/render"; const Detail = (props) => { const formRef = useRef(); let now = new Date(); const [inputs, setInputs] = useState({ username: '', token_name: '', model_name: '', start_timestamp: localStorage.getItem('data_export_default_time') === 'hour' ? timestamp2string(now.getTime() / 1000 - 86400) : (localStorage.getItem('data_export_default_time') === 'week' ? timestamp2string(now.getTime() / 1000 - 86400 * 30) : timestamp2string(now.getTime() / 1000 - 86400 * 7)), end_timestamp: timestamp2string(now.getTime() / 1000 + 3600), channel: '', data_export_default_time: '' }); const {username, model_name, start_timestamp, end_timestamp, channel} = inputs; const isAdminUser = isAdmin(); const initialized = useRef(false) const [modelDataChart, setModelDataChart] = useState(null); const [modelDataPieChart, setModelDataPieChart] = useState(null); const [loading, setLoading] = useState(false); const [quotaData, setQuotaData] = useState([]); const [consumeQuota, setConsumeQuota] = useState(0); const [times, setTimes] = useState(0); const [dataExportDefaultTime, setDataExportDefaultTime] = useState(localStorage.getItem('data_export_default_time') || 'hour'); const handleInputChange = (value, name) => { if (name === 'data_export_default_time') { setDataExportDefaultTime(value); return } setInputs((inputs) => ({...inputs, [name]: value})); }; const spec_line = { type: 'bar', data: [ { id: 'barData', values: [] } ], xField: 'Time', yField: 'Usage', seriesField: 'Model', stack: true, legends: { visible: true }, title: { visible: true, text: '模型消耗分布', subtext: '0' }, bar: { // The state style of bar state: { hover: { stroke: '#000', lineWidth: 1 } } }, tooltip: { mark: { content: [ { key: datum => datum['Model'], value: datum => renderQuotaNumberWithDigit(datum['Usage'], 4) } ] }, dimension: { content: [ { key: datum => datum['Model'], value: datum => datum['Usage'] } ], updateContent: array => { // sort by value array.sort((a, b) => b.value - a.value); // add $ for (let i = 0; i < array.length; i++) { array[i].value = renderQuotaNumberWithDigit(array[i].value, 4); } return array; } } }, color: { specified: modelColorMap } }; const spec_pie = { type: 'pie', data: [ { id: 'id0', values: [ {type: 'null', value: '0'}, ] } ], outerRadius: 0.8, innerRadius: 0.5, padAngle: 0.6, valueField: 'value', categoryField: 'type', pie: { style: { cornerRadius: 10 }, state: { hover: { outerRadius: 0.85, stroke: '#000', lineWidth: 1 }, selected: { outerRadius: 0.85, stroke: '#000', lineWidth: 1 } } }, title: { visible: true, text: '模型调用次数占比' }, legends: { visible: true, orient: 'left' }, label: { visible: true }, tooltip: { mark: { content: [ { key: datum => datum['type'], value: datum => renderNumber(datum['value']) } ] } }, color: { specified: modelColorMap } }; const loadQuotaData = async (lineChart, pieChart) => { setLoading(true); let url = ''; let localStartTimestamp = Date.parse(start_timestamp) / 1000; let localEndTimestamp = Date.parse(end_timestamp) / 1000; if (isAdminUser) { url = `/api/data/?username=${username}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&default_time=${dataExportDefaultTime}`; } else { url = `/api/data/self/?start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&default_time=${dataExportDefaultTime}`; } const res = await API.get(url); const {success, message, data} = res.data; if (success) { setQuotaData(data); if (data.length === 0) { data.push({ 'count': 0, 'model_name': '无数据', 'quota': 0, 'created_at': now.getTime() / 1000 }) } // 根据dataExportDefaultTime重制时间粒度 let timeGranularity = 3600; if (dataExportDefaultTime === 'day') { timeGranularity = 86400; } else if (dataExportDefaultTime === 'week') { timeGranularity = 604800; } data.forEach(item => { item['created_at'] = Math.floor(item['created_at'] / timeGranularity) * timeGranularity; }); updateChart(lineChart, pieChart, data); } else { showError(message); } setLoading(false); }; const refresh = async () => { await loadQuotaData(modelDataChart, modelDataPieChart); }; const initChart = async () => { let lineChart = modelDataChart if (!modelDataChart) { lineChart = new VChart(spec_line, {dom: 'model_data'}); setModelDataChart(lineChart); lineChart.renderAsync(); } let pieChart = modelDataPieChart if (!modelDataPieChart) { pieChart = new VChart(spec_pie, {dom: 'model_pie'}); setModelDataPieChart(pieChart); pieChart.renderAsync(); } console.log('init vchart'); await loadQuotaData(lineChart, pieChart) } const updateChart = (lineChart, pieChart, data) => { if (isAdminUser) { // 将所有用户合并 } let pieData = []; let lineData = []; let consumeQuota = 0; let times = 0; for (let i = 0; i < data.length; i++) { const item = data[i]; consumeQuota += item.quota; times += item.count; // 合并model_name let pieItem = pieData.find(it => it.type === item.model_name); if (pieItem) { pieItem.value += item.count; } else { pieData.push({ "type": item.model_name, "value": item.count }); } // 合并created_at和model_name 为 lineData, created_at 数据类型是小时的时间戳 // 转换日期格式 let createTime = timestamp2string1(item.created_at, dataExportDefaultTime); let lineItem = lineData.find(it => it.Time === createTime && it.Model === item.model_name); if (lineItem) { lineItem.Usage += parseFloat(getQuotaWithUnit(item.quota)); } else { lineData.push({ "Time": createTime, "Model": item.model_name, "Usage": parseFloat(getQuotaWithUnit(item.quota)) }); } } setConsumeQuota(consumeQuota); setTimes(times); // sort by count pieData.sort((a, b) => b.value - a.value); spec_pie.title.subtext = `总计:${renderNumber(times)}`; spec_pie.data[0].values = pieData; spec_line.title.subtext = `总计:${renderQuota(consumeQuota, 2)}`; spec_line.data[0].values = lineData; pieChart.updateSpec(spec_pie); lineChart.updateSpec(spec_line); // pieChart.updateData('id0', pieData); // lineChart.updateData('barData', lineData); pieChart.reLayout(); lineChart.reLayout(); } useEffect(() => { // setDataExportDefaultTime(localStorage.getItem('data_export_default_time')); // if (dataExportDefaultTime === 'day') { // // 设置开始时间为7天前 // let st = timestamp2string(now.getTime() / 1000 - 86400 * 7) // inputs.start_timestamp = st; // formRef.current.formApi.setValue('start_timestamp', st); // } if (!initialized.current) { initialized.current = true; initChart(); } }, []); return ( <>

数据看板

<> handleInputChange(value, 'start_timestamp')}/> handleInputChange(value, 'end_timestamp')}/> handleInputChange(value, 'data_export_default_time')}> { isAdminUser && <> handleInputChange(value, 'username')}/> }
); }; export default Detail;