feat: 兼容claude格式
This commit is contained in:
17
Yi.Ai.Vue3/src/api/systemStatistics/index.ts
Normal file
17
Yi.Ai.Vue3/src/api/systemStatistics/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { post } from '@/utils/request';
|
||||
import type {
|
||||
ProfitStatisticsInput,
|
||||
ProfitStatisticsOutput,
|
||||
TokenStatisticsInput,
|
||||
TokenStatisticsOutput,
|
||||
} from './types';
|
||||
|
||||
// 获取利润统计数据
|
||||
export function getProfitStatistics(data: ProfitStatisticsInput) {
|
||||
return post<ProfitStatisticsOutput>('/system-statistics/profit', data).json();
|
||||
}
|
||||
|
||||
// 获取指定日期各模型Token统计
|
||||
export function getTokenStatistics(data: TokenStatisticsInput) {
|
||||
return post<TokenStatisticsOutput>('/system-statistics/token', data).json();
|
||||
}
|
||||
41
Yi.Ai.Vue3/src/api/systemStatistics/types.ts
Normal file
41
Yi.Ai.Vue3/src/api/systemStatistics/types.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// 利润统计输入
|
||||
export interface ProfitStatisticsInput {
|
||||
currentCost: number;
|
||||
}
|
||||
|
||||
// 利润统计输出
|
||||
export interface ProfitStatisticsOutput {
|
||||
date: string;
|
||||
totalUsedTokens: number;
|
||||
totalUsedTokensInHundredMillion: number;
|
||||
totalRemainingTokens: number;
|
||||
totalRemainingTokensInHundredMillion: number;
|
||||
currentCost: number;
|
||||
costPerHundredMillion: number;
|
||||
totalCost: number;
|
||||
totalRevenue: number;
|
||||
profitRate: number;
|
||||
costAt200Price: number;
|
||||
}
|
||||
|
||||
// Token统计输入
|
||||
export interface TokenStatisticsInput {
|
||||
date: string;
|
||||
}
|
||||
|
||||
// 模型Token统计DTO
|
||||
export interface ModelTokenStatisticsDto {
|
||||
modelId: string;
|
||||
modelName: string;
|
||||
tokens: number;
|
||||
tokensInWan: number;
|
||||
count: number;
|
||||
cost: number;
|
||||
costPerHundredMillion: number;
|
||||
}
|
||||
|
||||
// Token统计输出
|
||||
export interface TokenStatisticsOutput {
|
||||
date: string;
|
||||
modelStatistics: ModelTokenStatisticsDto[];
|
||||
}
|
||||
219
Yi.Ai.Vue3/src/pages/console/system-statistics/index.vue
Normal file
219
Yi.Ai.Vue3/src/pages/console/system-statistics/index.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { getProfitStatistics, getTokenStatistics } from '@/api/systemStatistics';
|
||||
import type { ProfitStatisticsOutput, TokenStatisticsOutput } from '@/api/systemStatistics/types';
|
||||
|
||||
// ==================== 利润统计 ====================
|
||||
const profitLoading = ref(false);
|
||||
const profitData = ref<ProfitStatisticsOutput | null>(null);
|
||||
const currentCost = ref<number>(0);
|
||||
|
||||
// 获取利润统计
|
||||
async function fetchProfitStatistics() {
|
||||
if (!currentCost.value || currentCost.value <= 0) {
|
||||
ElMessage.warning('请输入当前成本');
|
||||
return;
|
||||
}
|
||||
|
||||
profitLoading.value = true;
|
||||
try {
|
||||
const res = await getProfitStatistics({
|
||||
currentCost: currentCost.value,
|
||||
});
|
||||
profitData.value = res.data;
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error.message || '获取利润统计失败');
|
||||
}
|
||||
finally {
|
||||
profitLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Token统计 ====================
|
||||
const tokenLoading = ref(false);
|
||||
const tokenData = ref<TokenStatisticsOutput | null>(null);
|
||||
const selectedDate = ref<string>(new Date().toISOString().split('T')[0]);
|
||||
|
||||
// 获取Token统计
|
||||
async function fetchTokenStatistics() {
|
||||
if (!selectedDate.value) {
|
||||
ElMessage.warning('请选择日期');
|
||||
return;
|
||||
}
|
||||
|
||||
tokenLoading.value = true;
|
||||
try {
|
||||
const res = await getTokenStatistics({
|
||||
date: selectedDate.value,
|
||||
});
|
||||
tokenData.value = res.data;
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error.message || '获取Token统计失败');
|
||||
}
|
||||
finally {
|
||||
tokenLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
// 页面加载时不自动获取数据,等待用户输入参数后手动获取
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="system-statistics">
|
||||
<div class="statistics-container">
|
||||
<!-- 利润统计卡片 -->
|
||||
<el-card class="statistics-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<h3>利润统计</h3>
|
||||
<el-button type="primary" size="small" :icon="Refresh" :loading="profitLoading" @click="fetchProfitStatistics">
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="input-section">
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="当前成本(RMB)">
|
||||
<el-input-number
|
||||
v-model="currentCost"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="请输入当前成本"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="profitData" v-loading="profitLoading" class="data-display">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="日期">{{ profitData.date }}</el-descriptions-item>
|
||||
<el-descriptions-item label="当前成本">{{ profitData.currentCost.toFixed(2) }} RMB</el-descriptions-item>
|
||||
<el-descriptions-item label="尊享包已消耗">
|
||||
{{ profitData.totalUsedTokensInHundredMillion.toFixed(2) }}亿 ({{ profitData.totalUsedTokens }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="尊享包剩余库存">
|
||||
{{ profitData.totalRemainingTokensInHundredMillion.toFixed(2) }}亿 ({{ profitData.totalRemainingTokens }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="1亿Token成本">
|
||||
{{ profitData.costPerHundredMillion.toFixed(2) }} RMB
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总成本">
|
||||
{{ profitData.totalCost.toFixed(2) }} RMB
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总收益">
|
||||
{{ profitData.totalRevenue.toFixed(2) }} RMB
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="利润率">
|
||||
<el-tag :type="profitData.profitRate > 0 ? 'success' : 'danger'">
|
||||
{{ profitData.profitRate.toFixed(1) }}%
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="按200售价计算成本" :span="2">
|
||||
{{ profitData.costAt200Price.toFixed(2) }} RMB
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- Token统计卡片 -->
|
||||
<el-card class="statistics-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<h3>Token统计</h3>
|
||||
<el-button type="primary" size="small" :icon="Refresh" :loading="tokenLoading" @click="fetchTokenStatistics">
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="input-section">
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="选择日期">
|
||||
<el-date-picker
|
||||
v-model="selectedDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="tokenData" v-loading="tokenLoading" class="data-display">
|
||||
<div class="date-info">
|
||||
<h4>{{ tokenData.date }}</h4>
|
||||
</div>
|
||||
|
||||
<el-table :data="tokenData.modelStatistics" border stripe>
|
||||
<el-table-column prop="modelName" label="模型名称" min-width="150" />
|
||||
<el-table-column prop="modelId" label="模型ID" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="Token消耗" min-width="120">
|
||||
<template #default="{ row }">
|
||||
{{ row.tokensInWan.toFixed(0) }}w
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="count" label="使用次数" width="100" />
|
||||
</el-table>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.system-statistics {
|
||||
padding: 20px;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.statistics-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.input-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.data-display {
|
||||
.date-info {
|
||||
margin-bottom: 16px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user