feat: 优化token用量查看
This commit is contained in:
@@ -62,8 +62,8 @@ async function fetchTokenList() {
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取Token列表失败:', error);
|
||||
ElMessage.error('获取Token列表失败');
|
||||
console.error('获取API密钥列表失败:', error);
|
||||
ElMessage.error('获取API密钥列表失败');
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
@@ -142,11 +142,11 @@ async function handleFormSubmit(data: TokenFormData) {
|
||||
|
||||
if (formMode.value === 'create') {
|
||||
await createToken(submitData);
|
||||
ElMessage.success('Token创建成功');
|
||||
ElMessage.success('API密钥创建成功');
|
||||
}
|
||||
else {
|
||||
await editToken(submitData);
|
||||
ElMessage.success('Token更新成功');
|
||||
ElMessage.success('API密钥更新成功');
|
||||
}
|
||||
|
||||
showFormDialog.value = false;
|
||||
@@ -154,7 +154,7 @@ async function handleFormSubmit(data: TokenFormData) {
|
||||
}
|
||||
catch (error) {
|
||||
console.error('操作失败:', error);
|
||||
ElMessage.error(formMode.value === 'create' ? '创建Token失败' : '编辑Token失败');
|
||||
ElMessage.error(formMode.value === 'create' ? '创建API密钥失败' : '编辑API密钥失败');
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
@@ -168,7 +168,7 @@ async function handleDelete(row: TokenItem) {
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除 Token "${row.name}" 吗?删除后将无法恢复`,
|
||||
`确定要删除 API密钥 "${row.name}" 吗?删除后将无法恢复`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定删除',
|
||||
@@ -180,7 +180,7 @@ async function handleDelete(row: TokenItem) {
|
||||
|
||||
operatingTokenId.value = row.id;
|
||||
await deleteToken(row.id);
|
||||
ElMessage.success('Token已删除');
|
||||
ElMessage.success('API密钥已删除');
|
||||
await fetchTokenList();
|
||||
}
|
||||
catch (error) {
|
||||
@@ -202,11 +202,11 @@ async function handleToggle(row: TokenItem) {
|
||||
operatingTokenId.value = row.id;
|
||||
if (row.isDisabled) {
|
||||
await enableToken(row.id);
|
||||
ElMessage.success(`Token "${row.name}" 已启用`);
|
||||
ElMessage.success(`API密钥 "${row.name}" 已启用`);
|
||||
}
|
||||
else {
|
||||
await disableToken(row.id);
|
||||
ElMessage.success(`Token "${row.name}" 已禁用`);
|
||||
ElMessage.success(`API密钥 "${row.name}" 已禁用`);
|
||||
}
|
||||
await fetchTokenList();
|
||||
}
|
||||
@@ -307,8 +307,8 @@ onMounted(async () => {
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="header-text">
|
||||
<span class="header-title">API Token 管理中心</span>
|
||||
<span class="header-subtitle">管理您的 API 访问密钥,每个 Token 拥有独立的配额和使用统计</span>
|
||||
<span class="header-title">API密钥管理中心</span>
|
||||
<span class="header-subtitle">管理您的 API 访问密钥,每个 API密钥 拥有独立的配额和使用统计</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
@@ -324,7 +324,7 @@ onMounted(async () => {
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" :icon="Plus" size="default" @click="showCreateDialog">
|
||||
新增 Token
|
||||
新增 API密钥
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
@@ -347,7 +347,7 @@ onMounted(async () => {
|
||||
>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="Token 名称"
|
||||
label="API密钥 名称"
|
||||
min-width="180"
|
||||
sortable
|
||||
show-overflow-tooltip
|
||||
@@ -544,9 +544,9 @@ onMounted(async () => {
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else-if="!loading" class="empty-container">
|
||||
<el-empty description="暂无Token,点击下方按钮创建您的第一个 API Token">
|
||||
<el-empty description="暂无API密钥,点击下方按钮创建您的第一个 API密钥">
|
||||
<el-button type="primary" size="large" :icon="Plus" @click="showCreateDialog">
|
||||
创建第一个 Token
|
||||
创建第一个 API密钥
|
||||
</el-button>
|
||||
</el-empty>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,15 @@ import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { getPremiumPackageTokenUsage } from '@/api';
|
||||
import { showProductPackage } from '@/utils/product-package.ts';
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false,
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
refresh: [];
|
||||
}>();
|
||||
|
||||
// 注册必要的组件
|
||||
echarts.use([
|
||||
EPieChart,
|
||||
@@ -35,15 +44,6 @@ interface Props {
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false,
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
refresh: [];
|
||||
}>();
|
||||
|
||||
// 饼图相关
|
||||
const tokenPieChart = ref(null);
|
||||
let tokenPieChartInstance: any = null;
|
||||
@@ -454,8 +454,8 @@ onBeforeUnmount(() => {
|
||||
<i-ep-pie-chart />
|
||||
</el-icon>
|
||||
<div class="header-text">
|
||||
<span class="header-title">各Token用量占比</span>
|
||||
<span class="header-subtitle">Premium Token Usage Distribution</span>
|
||||
<span class="header-title">各API密钥用量占比</span>
|
||||
<span class="header-subtitle">Premium APIKEY Usage Distribution</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -509,7 +509,9 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<template #image>
|
||||
<div class="custom-empty-image">
|
||||
<el-icon class="empty-main-icon"><i-ep-pie-chart /></el-icon>
|
||||
<el-icon class="empty-main-icon">
|
||||
<i-ep-pie-chart />
|
||||
</el-icon>
|
||||
<div class="empty-decoration">
|
||||
<div class="decoration-circle" />
|
||||
<div class="decoration-circle" />
|
||||
@@ -519,8 +521,12 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="empty-description">
|
||||
<h3 class="empty-title">暂无Token使用数据</h3>
|
||||
<p class="empty-text">当您开始使用Token后,这里将展示各Token的用量占比统计</p>
|
||||
<h3 class="empty-title">
|
||||
暂无Token使用数据
|
||||
</h3>
|
||||
<p class="empty-text">
|
||||
当您开始使用Token后,这里将展示各Token的用量占比统计
|
||||
</p>
|
||||
<div class="empty-tips">
|
||||
<el-icon><i-ep-info-filled /></el-icon>
|
||||
<span>创建并使用Token后即可查看详细的用量分析</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
interface TokenFormData {
|
||||
id?: string;
|
||||
@@ -23,8 +23,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
name: '',
|
||||
expireTime: '',
|
||||
premiumQuotaLimit: 0,
|
||||
quotaUnit: '万'
|
||||
})
|
||||
quotaUnit: '万',
|
||||
}),
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -36,7 +36,7 @@ const localFormData = ref<TokenFormData>({
|
||||
name: '',
|
||||
expireTime: '',
|
||||
premiumQuotaLimit: 0,
|
||||
quotaUnit: '万'
|
||||
quotaUnit: '万',
|
||||
});
|
||||
|
||||
const submitting = ref(false);
|
||||
@@ -49,7 +49,7 @@ const quotaUnitOptions = [
|
||||
{ label: '百', value: '百', multiplier: 100 },
|
||||
{ label: '千', value: '千', multiplier: 1000 },
|
||||
{ label: '万', value: '万', multiplier: 10000 },
|
||||
{ label: '亿', value: '亿', multiplier: 100000000 }
|
||||
{ label: '亿', value: '亿', multiplier: 100000000 },
|
||||
];
|
||||
|
||||
// 监听visible变化,重置表单
|
||||
@@ -69,16 +69,20 @@ watch(() => props.visible, (newVal) => {
|
||||
if (quota >= 100000000 && quota % 100000000 === 0) {
|
||||
displayValue = quota / 100000000;
|
||||
unit = '亿';
|
||||
} else if (quota >= 10000 && quota % 10000 === 0) {
|
||||
}
|
||||
else if (quota >= 10000 && quota % 10000 === 0) {
|
||||
displayValue = quota / 10000;
|
||||
unit = '万';
|
||||
} else if (quota >= 1000 && quota % 1000 === 0) {
|
||||
}
|
||||
else if (quota >= 1000 && quota % 1000 === 0) {
|
||||
displayValue = quota / 1000;
|
||||
unit = '千';
|
||||
} else if (quota >= 100 && quota % 100 === 0) {
|
||||
}
|
||||
else if (quota >= 100 && quota % 100 === 0) {
|
||||
displayValue = quota / 100;
|
||||
unit = '百';
|
||||
} else if (quota >= 10 && quota % 10 === 0) {
|
||||
}
|
||||
else if (quota >= 10 && quota % 10 === 0) {
|
||||
displayValue = quota / 10;
|
||||
unit = '十';
|
||||
}
|
||||
@@ -90,15 +94,16 @@ watch(() => props.visible, (newVal) => {
|
||||
localFormData.value = {
|
||||
...props.formData,
|
||||
premiumQuotaLimit: displayValue,
|
||||
quotaUnit: unit
|
||||
quotaUnit: unit,
|
||||
};
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// 新增模式:重置表单
|
||||
localFormData.value = {
|
||||
name: '',
|
||||
expireTime: '',
|
||||
premiumQuotaLimit: 1,
|
||||
quotaUnit: '万'
|
||||
quotaUnit: '万',
|
||||
};
|
||||
neverExpire.value = false;
|
||||
unlimitedQuota.value = false;
|
||||
@@ -123,14 +128,15 @@ watch(unlimitedQuota, (newVal) => {
|
||||
|
||||
// 关闭对话框
|
||||
function handleClose() {
|
||||
if (submitting.value) return;
|
||||
if (submitting.value)
|
||||
return;
|
||||
emit('update:visible', false);
|
||||
}
|
||||
|
||||
// 确认提交
|
||||
async function handleConfirm() {
|
||||
if (!localFormData.value.name.trim()) {
|
||||
ElMessage.warning('请输入Token名称');
|
||||
ElMessage.warning('请输入API密钥名称');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,17 +163,18 @@ async function handleConfirm() {
|
||||
const submitData: TokenFormData = {
|
||||
...localFormData.value,
|
||||
expireTime: neverExpire.value ? '' : localFormData.value.expireTime,
|
||||
premiumQuotaLimit: actualQuota
|
||||
premiumQuotaLimit: actualQuota,
|
||||
};
|
||||
|
||||
emit('confirm', submitData);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
// 注意:这里不设置 submitting.value = false
|
||||
// 因为父组件会关闭对话框,watch会重置状态
|
||||
}
|
||||
}
|
||||
|
||||
const dialogTitle = computed(() => props.mode === 'create' ? '新增 Token' : '编辑 Token');
|
||||
const dialogTitle = computed(() => props.mode === 'create' ? '新增 API密钥' : '编辑 API密钥');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -180,7 +187,7 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 Token' : '
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form :model="localFormData" label-width="110px" label-position="right">
|
||||
<el-form-item label="Token名称" required>
|
||||
<el-form-item label="API密钥名称" required>
|
||||
<el-input
|
||||
v-model="localFormData.name"
|
||||
placeholder="例如:生产环境、测试环境、开发环境"
|
||||
@@ -222,7 +229,7 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 Token' : '
|
||||
</div>
|
||||
<div v-if="!neverExpire" class="form-hint">
|
||||
<el-icon><i-ep-warning /></el-icon>
|
||||
Token将在过期时间后自动失效
|
||||
API密钥将在过期时间后自动失效
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
@@ -262,21 +269,21 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 Token' : '
|
||||
</div>
|
||||
<div v-if="!unlimitedQuota" class="form-hint">
|
||||
<el-icon><i-ep-info-filled /></el-icon>
|
||||
超出配额后Token将无法继续使用
|
||||
超出配额后API密钥将无法继续使用
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="submitting">
|
||||
<el-button :disabled="submitting" @click="handleClose">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleConfirm"
|
||||
:loading="submitting"
|
||||
:disabled="submitting"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
{{ mode === 'create' ? '创建' : '保存' }}
|
||||
</el-button>
|
||||
|
||||
@@ -58,9 +58,10 @@ const hasModelData = computed(() => modelUsageData.value.length > 0);
|
||||
|
||||
// 计算属性:当前选择的token名称
|
||||
const selectedTokenName = computed(() => {
|
||||
if (!selectedTokenId.value) return '全部Token';
|
||||
if (!selectedTokenId.value)
|
||||
return '全部API密钥';
|
||||
const token = tokenOptions.value.find(t => t.tokenId === selectedTokenId.value);
|
||||
return token?.name || '未知Token';
|
||||
return token?.name || '未知API密钥';
|
||||
});
|
||||
|
||||
// 获取可选择的Token列表
|
||||
@@ -74,8 +75,8 @@ async function fetchTokenOptions() {
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取Token列表失败:', error);
|
||||
ElMessage.error('获取Token列表失败');
|
||||
console.error('获取API密钥列表失败:', error);
|
||||
ElMessage.error('获取TAPI密钥列表失败');
|
||||
}
|
||||
finally {
|
||||
tokenOptionsLoading.value = false;
|
||||
@@ -514,7 +515,7 @@ onBeforeUnmount(() => {
|
||||
<div class="header-actions">
|
||||
<el-select
|
||||
v-model="selectedTokenId"
|
||||
placeholder="选择Token"
|
||||
placeholder="选择API密钥"
|
||||
clearable
|
||||
filterable
|
||||
:loading="tokenOptionsLoading"
|
||||
@@ -523,7 +524,9 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<el-option label="全部Token" value="">
|
||||
<div class="token-option">
|
||||
<el-icon class="option-icon all-icon"><i-ep-folder-opened /></el-icon>
|
||||
<el-icon class="option-icon all-icon">
|
||||
<i-ep-folder-opened />
|
||||
</el-icon>
|
||||
<span class="option-label">全部Token</span>
|
||||
</div>
|
||||
</el-option>
|
||||
|
||||
Reference in New Issue
Block a user