f17a6ccbac
- Make AI routing rules DB-driven (read from system_configs, removed from config.py) - Add translation quota tracking to LLM translation (OpenAIProvider) - Add Alibaba MT ECS RAM role support (STS token, no AccessKey needed) - Fix admin sidebar link for AI模型配置 page - Fix Quota.vue API path (quotas → translation-quotas) - Fix login auto-redirect to dashboard - Add provider dropdown selects to AI routing config UI - Clean up stale ai_provider_* system_configs records - Remove OpencodeGo, Spark providers (code + DB) - Update deploy config: nginx port 8000, systemd cwd
87 lines
3.5 KiB
Vue
87 lines
3.5 KiB
Vue
<template>
|
|
<div>
|
|
<div v-for="q in quotas" :key="q.version" class="quota-card">
|
|
<el-card shadow="never">
|
|
<template #header>
|
|
<div class="quota-header">
|
|
<span class="quota-version">{{ { ecommerce: '电商版', general: '通用版', llm: 'AI模型翻译' }[q.version] || q.version }}</span>
|
|
<el-tag size="small" v-if="q.description">{{ q.description }}</el-tag>
|
|
</div>
|
|
</template>
|
|
<div class="quota-stat">
|
|
<span class="quota-label">{{ q.current_month || '当前月' }}</span>
|
|
<el-progress :percentage="quotaPercent(q)" :stroke-width="20" />
|
|
<span class="quota-value">{{ q.used_chars }} / {{ q.monthly_limit }}</span>
|
|
</div>
|
|
<div class="quota-actions">
|
|
<el-form :inline="true" size="small">
|
|
<el-form-item label="月限额">
|
|
<el-input-number :model-value="q._edit_limit !== undefined ? q._edit_limit : q.monthly_limit" :min="1000" :step="10000" style="width:140px" @update:model-value="q._edit_limit=$event" />
|
|
</el-form-item>
|
|
<el-form-item label="启用">
|
|
<el-switch :model-value="q._edit_enabled !== undefined ? q._edit_enabled : q.enabled" @update:model-value="q._edit_enabled=$event" />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="save(q)">保存</el-button>
|
|
<el-button @click="resetQuota(q.version)">重置用量</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</el-card>
|
|
</div>
|
|
<el-empty v-if="!quotas.length" description="暂无配额数据" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { listQuotas, updateQuota, resetQuota as resetApi } from '@/api'
|
|
|
|
const quotas = ref([])
|
|
|
|
function quotaPercent(q) {
|
|
const limit = q.monthly_limit || 1
|
|
return Math.min(100, Math.round((q.used_chars / limit) * 100))
|
|
}
|
|
|
|
async function load() {
|
|
try {
|
|
const r = await listQuotas()
|
|
quotas.value = (r.items || r || []).map(q => ({ ...q, _edit_limit: undefined, _edit_enabled: undefined }))
|
|
} catch (e) { console.error(e) }
|
|
}
|
|
|
|
async function save(q) {
|
|
try {
|
|
const data = {}
|
|
if (q._edit_limit !== undefined && q._edit_limit !== null) data.monthly_limit = q._edit_limit
|
|
if (q._edit_enabled !== undefined && q._edit_enabled !== null) data.enabled = q._edit_enabled
|
|
if (!Object.keys(data).length) { ElMessage.info('无改动'); return }
|
|
await updateQuota(q.version, data)
|
|
if (data.monthly_limit !== undefined) q.monthly_limit = data.monthly_limit
|
|
if (data.enabled !== undefined) q.enabled = data.enabled
|
|
q._edit_limit = undefined; q._edit_enabled = undefined
|
|
ElMessage.success('已保存')
|
|
} catch (e) { ElMessage.error(e?.detail || '保存失败') }
|
|
}
|
|
|
|
async function resetQuota(version) {
|
|
try { await resetApi(version); ElMessage.success('已重置'); load() }
|
|
catch (e) { ElMessage.error(e?.detail || '重置失败') }
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.quota-card { margin-bottom: 16px; }
|
|
.quota-header { display: flex; align-items: center; gap: 12px; }
|
|
.quota-version { font-size: 15px; font-weight: 600; }
|
|
.quota-stat { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; }
|
|
.quota-label { width: 80px; font-size: 13px; color: #666; flex-shrink: 0; }
|
|
.el-progress { flex: 1; }
|
|
.quota-value { width: 180px; font-size: 13px; color: #333; text-align: right; flex-shrink: 0; }
|
|
.quota-actions { }
|
|
</style>
|