From ab4c9fdb8fc09a3ee0e4578569c4c1f4417bb722 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Thu, 12 Dec 2024 14:56:16 +0800 Subject: [PATCH 1/3] feat: Enhance color mapping and chart rendering in Detail component - Added base and extended color palettes for improved model color mapping. - Introduced a new `modelToColor` function to dynamically assign colors based on model names. - Updated the Detail component to utilize the new color mapping for pie and line charts. - Refactored chart data handling to support dynamic color assignment and improved data visualization. - Cleaned up unused state variables and optimized data loading logic for better performance. --- web/src/helpers/render.js | 63 +++++- web/src/pages/Detail/index.js | 350 +++++++++++++++++----------------- 2 files changed, 239 insertions(+), 174 deletions(-) diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js index ef537eedf..345849b28 100644 --- a/web/src/helpers/render.js +++ b/web/src/helpers/render.js @@ -268,6 +268,44 @@ const colors = [ 'yellow', ]; +// 基础10色色板 (N ≤ 10) +const baseColors = [ + '#1664FF', // 主色 + '#1AC6FF', + '#FF8A00', + '#3CC780', + '#7442D4', + '#FFC400', + '#304D77', + '#B48DEB', + '#009488', + '#FF7DDA' +]; + +// 扩展20色色板 (10 < N ≤ 20) +const extendedColors = [ + '#1664FF', + '#B2CFFF', + '#1AC6FF', + '#94EFFF', + '#FF8A00', + '#FFCE7A', + '#3CC780', + '#B9EDCD', + '#7442D4', + '#DDC5FA', + '#FFC400', + '#FAE878', + '#304D77', + '#8B959E', + '#B48DEB', + '#EFE3FF', + '#009488', + '#59BAA8', + '#FF7DDA', + '#FFCFEE' +]; + export const modelColorMap = { 'dall-e': 'rgb(147,112,219)', // 深紫色 // 'dall-e-2': 'rgb(147,112,219)', // 介于紫色和蓝色之间的色调 @@ -312,14 +350,33 @@ export const modelColorMap = { 'claude-2.1': 'rgb(255,209,190)', // 浅橙色(略有区别) }; +export function modelToColor(modelName) { + // 1. 如果模型在预定义的 modelColorMap 中,使用预定义颜色 + if (modelColorMap[modelName]) { + return modelColorMap[modelName]; + } + + // 2. 生成一个稳定的数字作为索引 + let hash = 0; + for (let i = 0; i < modelName.length; i++) { + hash = ((hash << 5) - hash) + modelName.charCodeAt(i); + hash = hash & hash; // Convert to 32-bit integer + } + hash = Math.abs(hash); + + // 3. 根据模型名称长度选择不同的色板 + const colorPalette = modelName.length > 10 ? extendedColors : baseColors; + + // 4. 使用hash值选择颜色 + const index = hash % colorPalette.length; + return colorPalette[index]; +} + 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]; } diff --git a/web/src/pages/Detail/index.js b/web/src/pages/Detail/index.js index f334dd3f6..a00b3453d 100644 --- a/web/src/pages/Detail/index.js +++ b/web/src/pages/Detail/index.js @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { initVChartSemiTheme } from '@visactor/vchart-semi-theme'; import { Button, Col, Form, Layout, Row, Spin } from '@douyinfe/semi-ui'; -import VChart from '@visactor/vchart'; +import { VChart } from "@visactor/react-vchart"; import { API, isAdmin, @@ -17,6 +17,7 @@ import { renderQuota, renderQuotaNumberWithDigit, stringToColor, + modelToColor, } from '../../helpers/render'; const Detail = (props) => { @@ -40,8 +41,6 @@ const Detail = (props) => { 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); @@ -49,23 +48,68 @@ const Detail = (props) => { 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: [], + const [pieData, setPieData] = useState([{ type: 'null', value: '0' }]); + const [lineData, setLineData] = useState([]); + const [spec_pie, setSpecPie] = useState({ + type: 'pie', + data: [{ + id: 'id0', + values: pieData + }], + 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: '模型调用次数占比', + subtext: `总计:${renderNumber(times)}`, + }, + legends: { + visible: true, + orient: 'left', + }, + label: { + visible: true, + }, + tooltip: { + mark: { + content: [ + { + key: (datum) => datum['type'], + value: (datum) => renderNumber(datum['value']), + }, + ], + }, + }, + color: { + specified: modelColorMap, + }, + }); + const [spec_line, setSpecLine] = useState({ + type: 'bar', + data: [{ + id: 'barData', + values: lineData + }], xField: 'Time', yField: 'Usage', seriesField: 'Model', @@ -77,7 +121,7 @@ const Detail = (props) => { title: { visible: true, text: '模型消耗分布', - subtext: '0', + subtext: `总计:${renderQuota(consumeQuota, 2)}`, }, bar: { // The state style of bar @@ -129,196 +173,160 @@ const Detail = (props) => { 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 [modelColors, setModelColors] = useState({}); + + const handleInputChange = (value, name) => { + if (name === 'data_export_default_time') { + setDataExportDefaultTime(value); + return; + } + setInputs((inputs) => ({ ...inputs, [name]: value })); }; - const loadQuotaData = async (lineChart, pieChart) => { + const loadQuotaData = async () => { 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, - }); + try { + 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}`; } - // 根据dataExportDefaultTime重制时间粒度 - let timeGranularity = 3600; - if (dataExportDefaultTime === 'day') { - timeGranularity = 86400; - } else if (dataExportDefaultTime === 'week') { - timeGranularity = 604800; + 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; + } + // sort created_at + data.sort((a, b) => a.created_at - b.created_at); + data.forEach((item) => { + item['created_at'] = + Math.floor(item['created_at'] / timeGranularity) * timeGranularity; + }); + updateChartData(data); + } else { + showError(message); } - // sort created_at - data.sort((a, b) => a.created_at - b.created_at); - data.forEach((item) => { - item['created_at'] = - Math.floor(item['created_at'] / timeGranularity) * timeGranularity; - }); - updateChart(lineChart, pieChart, data); - } else { - showError(message); + } finally { + setLoading(false); } - setLoading(false); }; const refresh = async () => { - await loadQuotaData(modelDataChart, modelDataPieChart); + await loadQuotaData(); }; 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); + await loadQuotaData(); }; - const updateChart = (lineChart, pieChart, data) => { - if (isAdminUser) { - // 将所有用户合并 - } - let pieData = []; - let lineData = []; - let consumeQuota = 0; - let times = 0; + const updateChartData = (data) => { + let newPieData = []; + let newLineData = []; + let totalQuota = 0; + let totalTimes = 0; + let uniqueModels = new Set(); + + // 首先收集所有唯一的模型名称 + data.forEach(item => uniqueModels.add(item.model_name)); + + // 为每个唯一的模型生成或获取颜色 + const newModelColors = {}; + Array.from(uniqueModels).forEach((modelName) => { + // 优先使用 modelColorMap 中的颜色,然后是已存在的颜色,最后使用新的颜色生成函数 + newModelColors[modelName] = modelColorMap[modelName] || + modelColors[modelName] || + modelToColor(modelName); // 使用新的颜色生成函数替代 stringToColor + }); + setModelColors(newModelColors); + for (let i = 0; i < data.length; i++) { const item = data[i]; - consumeQuota += item.quota; - times += item.count; + totalQuota += item.quota; + totalTimes += item.count; // 合并model_name - let pieItem = pieData.find((it) => it.type === item.model_name); + let pieItem = newPieData.find((it) => it.type === item.model_name); if (pieItem) { pieItem.value += item.count; } else { - pieData.push({ + newPieData.push({ type: item.model_name, value: item.count, }); } - // 合并created_at和model_name 为 lineData, created_at 数据类型是小时的时间戳 - // 转换日期格式 + // 合并created_at和model_name 为 lineData let createTime = timestamp2string1( item.created_at, dataExportDefaultTime, ); - let lineItem = lineData.find( + let lineItem = newLineData.find( (it) => it.Time === createTime && it.Model === item.model_name, ); if (lineItem) { lineItem.Usage += parseFloat(getQuotaWithUnit(item.quota)); } else { - lineData.push({ + newLineData.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; + newPieData.sort((a, b) => b.value - a.value); - spec_line.title.subtext = `总计:${renderQuota(consumeQuota, 2)}`; - spec_line.data[0].values = lineData; - pieChart.updateSpec(spec_pie); - lineChart.updateSpec(spec_line); + // 更新图表配置和数据 + setSpecPie(prev => ({ + ...prev, + data: [{ id: 'id0', values: newPieData }], + title: { + ...prev.title, + subtext: `总计:${renderNumber(totalTimes)}` + }, + color: { + specified: newModelColors + } + })); - // pieChart.updateData('id0', pieData); - // lineChart.updateData('barData', lineData); - pieChart.reLayout(); - lineChart.reLayout(); + setSpecLine(prev => ({ + ...prev, + data: [{ id: 'barData', values: newLineData }], + title: { + ...prev.title, + subtext: `总计:${renderQuota(totalQuota, 2)}` + }, + color: { + specified: newModelColors + } + })); + + setPieData(newPieData); + setLineData(newLineData); + setConsumeQuota(totalQuota); + setTimes(totalTimes); }; 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) { initVChartSemiTheme({ isWatchingThemeSwitch: true, @@ -405,16 +413,16 @@ const Detail = (props) => {
-
+
-
+
From b2d36b946d03f68abf7311bfaff44b46bb1ad565 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Thu, 12 Dec 2024 16:11:17 +0800 Subject: [PATCH 2/3] feat: Update SiderBar and Detail components for improved navigation and data visualization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed the '模型价格' (Pricing) link from the SiderBar for a cleaner interface. - Added a new '数据看板' (Data Dashboard) link to the SiderBar, enhancing navigation options. - Refactored the Detail component to include user context and style context for better state management. - Introduced new state variables to track token consumption and updated data handling for charts. - Enhanced the layout with additional cards and tabs for displaying user quota and usage statistics. - Improved data processing logic for pie and line charts, ensuring accurate representation of user data. --- web/src/components/SiderBar.js | 26 ++--- web/src/pages/Detail/index.js | 204 +++++++++++++++++++++++++-------- 2 files changed, 165 insertions(+), 65 deletions(-) diff --git a/web/src/components/SiderBar.js b/web/src/components/SiderBar.js index d282e40e0..0bad31425 100644 --- a/web/src/components/SiderBar.js +++ b/web/src/components/SiderBar.js @@ -73,12 +73,6 @@ const SiderBar = () => { to: '/playground', icon: , }, - { - text: '模型价格', - itemKey: 'pricing', - to: '/pricing', - icon: , - }, { text: '渠道', itemKey: 'channel', @@ -102,6 +96,16 @@ const SiderBar = () => { to: '/token', icon: , }, + { + text: '数据看板', + itemKey: 'detail', + to: '/detail', + icon: , + className: + localStorage.getItem('enable_data_export') === 'true' + ? 'semi-navigation-item-normal' + : 'tableHiddle', + }, { text: '兑换码', itemKey: 'redemption', @@ -128,16 +132,6 @@ const SiderBar = () => { to: '/log', icon: , }, - { - text: '数据看板', - itemKey: 'detail', - to: '/detail', - icon: , - className: - localStorage.getItem('enable_data_export') === 'true' - ? 'semi-navigation-item-normal' - : 'tableHiddle', - }, { text: '绘图', itemKey: 'midjourney', diff --git a/web/src/pages/Detail/index.js b/web/src/pages/Detail/index.js index a00b3453d..e202f0866 100644 --- a/web/src/pages/Detail/index.js +++ b/web/src/pages/Detail/index.js @@ -1,7 +1,7 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import { initVChartSemiTheme } from '@visactor/vchart-semi-theme'; -import { Button, Col, Form, Layout, Row, Spin } from '@douyinfe/semi-ui'; +import { Button, Card, Col, Descriptions, Form, Layout, Row, Spin, Tabs } from '@douyinfe/semi-ui'; import { VChart } from "@visactor/react-vchart"; import { API, @@ -19,10 +19,14 @@ import { stringToColor, modelToColor, } from '../../helpers/render'; +import { UserContext } from '../../context/User/index.js'; +import { StyleContext } from '../../context/Style/index.js'; const Detail = (props) => { const formRef = useRef(); let now = new Date(); + const [userState, userDispatch] = useContext(UserContext); + const [styleState, styleDispatch] = useContext(StyleContext); const [inputs, setInputs] = useState({ username: '', token_name: '', @@ -44,6 +48,7 @@ const Detail = (props) => { const [loading, setLoading] = useState(false); const [quotaData, setQuotaData] = useState([]); const [consumeQuota, setConsumeQuota] = useState(0); + const [consumeTokens, setConsumeTokens] = useState(0); const [times, setTimes] = useState(0); const [dataExportDefaultTime, setDataExportDefaultTime] = useState( localStorage.getItem('data_export_default_time') || 'hour', @@ -245,25 +250,30 @@ const Detail = (props) => { let totalQuota = 0; let totalTimes = 0; let uniqueModels = new Set(); + let totalTokens = 0; - // 首先收集所有唯一的模型名称 - data.forEach(item => uniqueModels.add(item.model_name)); + // 收集所有唯一的模型名称和时间点 + let uniqueTimes = new Set(); + data.forEach(item => { + uniqueModels.add(item.model_name); + uniqueTimes.add(timestamp2string1(item.created_at, dataExportDefaultTime)); + totalTokens += item.token_used; + }); - // 为每个唯一的模型生成或获取颜色 + // 处理颜色映射 const newModelColors = {}; Array.from(uniqueModels).forEach((modelName) => { - // 优先使用 modelColorMap 中的颜色,然后是已存在的颜色,最后使用新的颜色生成函数 newModelColors[modelName] = modelColorMap[modelName] || modelColors[modelName] || - modelToColor(modelName); // 使用新的颜色生成函数替代 stringToColor + modelToColor(modelName); }); setModelColors(newModelColors); - for (let i = 0; i < data.length; i++) { - const item = data[i]; + // 处理饼图数据 + for (let item of data) { totalQuota += item.quota; totalTimes += item.count; - // 合并model_name + let pieItem = newPieData.find((it) => it.type === item.model_name); if (pieItem) { pieItem.value += item.count; @@ -273,27 +283,47 @@ const Detail = (props) => { value: item.count, }); } - // 合并created_at和model_name 为 lineData - let createTime = timestamp2string1( - item.created_at, - dataExportDefaultTime, - ); - let lineItem = newLineData.find( - (it) => it.Time === createTime && it.Model === item.model_name, - ); - if (lineItem) { - lineItem.Usage += parseFloat(getQuotaWithUnit(item.quota)); - } else { + } + + // 处理柱状图数据 + let timePoints = Array.from(uniqueTimes); + if (timePoints.length < 7) { + // 根据时间粒度生成合适的时间点 + const generateTimePoints = () => { + let lastTime = Math.max(...data.map(item => item.created_at)); + let points = []; + let interval = dataExportDefaultTime === 'hour' ? 3600 + : dataExportDefaultTime === 'day' ? 86400 + : 604800; + + for (let i = 0; i < 7; i++) { + points.push(timestamp2string1(lastTime - (i * interval), dataExportDefaultTime)); + } + return points.reverse(); + }; + + timePoints = generateTimePoints(); + } + + // 为每个时间点和模型生成数据 + timePoints.forEach(time => { + Array.from(uniqueModels).forEach(model => { + let existingData = data.find(item => + timestamp2string1(item.created_at, dataExportDefaultTime) === time && + item.model_name === model + ); + newLineData.push({ - Time: createTime, - Model: item.model_name, - Usage: parseFloat(getQuotaWithUnit(item.quota)), + Time: time, + Model: model, + Usage: existingData ? parseFloat(getQuotaWithUnit(existingData.quota)) : 0 }); - } - } + }); + }); - // sort by count + // 排序 newPieData.sort((a, b) => b.value - a.value); + newLineData.sort((a, b) => a.Time.localeCompare(b.Time)); // 更新图表配置和数据 setSpecPie(prev => ({ @@ -324,9 +354,21 @@ const Detail = (props) => { setLineData(newLineData); setConsumeQuota(totalQuota); setTimes(totalTimes); + setConsumeTokens(totalTokens); + }; + + const getUserData = async () => { + let res = await API.get(`/api/user/self`); + const {success, message, data} = res.data; + if (success) { + userDispatch({type: 'login', payload: data}); + } else { + showError(message); + } }; useEffect(() => { + getUserData() if (!initialized.current) { initVChartSemiTheme({ isWatchingThemeSwitch: true, @@ -397,33 +439,97 @@ const Detail = (props) => { /> )} + - -
- -
-
- -
+ + + + + + {renderQuota(userState?.user?.quota)} + + + {renderQuota(userState?.user?.used_quota)} + + + {userState.user?.request_count} + + + + + + + + + {renderQuota(consumeQuota)} + + + {consumeTokens} + + + {times} + + + + + + + + + {renderNumber( + times / + ((Date.parse(end_timestamp) - + Date.parse(start_timestamp)) / + 60000), + ).toFixed(3)} + + + {renderNumber( + consumeTokens / + ((Date.parse(end_timestamp) - + Date.parse(start_timestamp)) / + 60000), + ).toFixed(3)} + + + + + + + + +
+ +
+
+ +
+ +
+
+ +
+
From 6625563f80f0e5652a6cbef0ae8ceaa1d4b526fb Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Thu, 12 Dec 2024 16:18:14 +0800 Subject: [PATCH 3/3] feat: Enhance quota data handling and CSS styling - Updated the `increaseQuotaData` function to include `tokenUsed` parameter for better quota tracking. - Modified the `GetAllQuotaDates` function to sum `token_used` alongside `count` and `quota` for comprehensive data retrieval. - Improved CSS styles for better layout responsiveness, including padding adjustments for navigation elements and description cards. --- model/usedata.go | 11 ++++++----- web/src/index.css | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/model/usedata.go b/model/usedata.go index 473533394..1255b0bed 100644 --- a/model/usedata.go +++ b/model/usedata.go @@ -85,7 +85,7 @@ func SaveQuotaDataCache() { //quotaDataDB.Count += quotaData.Count //quotaDataDB.Quota += quotaData.Quota //DB.Table("quota_data").Save(quotaDataDB) - increaseQuotaData(quotaData.UserID, quotaData.Username, quotaData.ModelName, quotaData.Count, quotaData.Quota, quotaData.CreatedAt) + increaseQuotaData(quotaData.UserID, quotaData.Username, quotaData.ModelName, quotaData.Count, quotaData.Quota, quotaData.CreatedAt, quotaData.TokenUsed) } else { DB.Table("quota_data").Create(quotaData) } @@ -94,11 +94,12 @@ func SaveQuotaDataCache() { common.SysLog(fmt.Sprintf("保存数据看板数据成功,共保存%d条数据", size)) } -func increaseQuotaData(userId int, username string, modelName string, count int, quota int, createdAt int64) { +func increaseQuotaData(userId int, username string, modelName string, count int, quota int, createdAt int64, tokenUsed int) { err := DB.Table("quota_data").Where("user_id = ? and username = ? and model_name = ? and created_at = ?", userId, username, modelName, createdAt).Updates(map[string]interface{}{ - "count": gorm.Expr("count + ?", count), - "quota": gorm.Expr("quota + ?", quota), + "count": gorm.Expr("count + ?", count), + "quota": gorm.Expr("quota + ?", quota), + "token_used": gorm.Expr("token_used + ?", tokenUsed), }).Error if err != nil { common.SysLog(fmt.Sprintf("increaseQuotaData error: %s", err)) @@ -127,6 +128,6 @@ func GetAllQuotaDates(startTime int64, endTime int64, username string) (quotaDat // 从quota_data表中查询数据 // only select model_name, sum(count) as count, sum(quota) as quota, model_name, created_at from quota_data group by model_name, created_at; //err = DB.Table("quota_data").Where("created_at >= ? and created_at <= ?", startTime, endTime).Find("aDatas).Error - err = DB.Table("quota_data").Select("model_name, sum(count) as count, sum(quota) as quota, created_at").Where("created_at >= ? and created_at <= ?", startTime, endTime).Group("model_name, created_at").Find("aDatas).Error + err = DB.Table("quota_data").Select("model_name, sum(count) as count, sum(quota) as quota, sum(token_used) as token_used, created_at").Where("created_at >= ? and created_at <= ?", startTime, endTime).Group("model_name, created_at").Find("aDatas).Error return quotaDatas, err } diff --git a/web/src/index.css b/web/src/index.css index 2596cd615..f06183774 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -21,7 +21,21 @@ body { font-weight: 600 !important; } +.semi-descriptions-double-small .semi-descriptions-item { + padding-right: 30px; +} + +.panel-desc-card { + /*min-width: 320px;*/ +} + @media only screen and (max-width: 767px) { + #root > section > header > section > div > div > div > div.semi-navigation-header-list-outer > div.semi-navigation-list-wrapper > ul > div > a > li { + padding: 0 5px; + } + #root > section > header > section > div > div > div > div.semi-navigation-footer > div:nth-child(1) > a > li { + padding: 0 5px; + } .semi-table-tbody, .semi-table-row, .semi-table-row-cell {