Files
YG_FT/frontend/src/views/tenants/TenantDetailView.vue

114 lines
4.1 KiB
Vue
Raw Normal View History

2026-08-03 09:34:08 +08:00
<script setup lang="ts">
2026-08-03 16:20:21 +08:00
import { onMounted, reactive, ref } from 'vue'
2026-08-03 09:34:08 +08:00
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getTenant, setTenantQuota, type Tenant } from '@/api/modules/tenant'
const route = useRoute()
const router = useRouter()
const tenant = ref<Tenant | null>(null)
const loading = ref(false)
2026-08-03 16:20:21 +08:00
const quotaForm = reactive({ gpu: 0, storage: 0, maxProjects: 0 })
2026-08-17 09:15:36 +08:00
function parseQuota(quota: Record<string, unknown> | undefined | null | string) {
// 兼容处理quota 可能是 JSON 字符串或嵌套 { quota: {...} } 格式
let q: Record<string, unknown> = {}
if (typeof quota === 'string') {
try { q = JSON.parse(quota) || {} } catch { q = {} }
} else if (quota && typeof quota === 'object') {
q = quota
}
// 如果是嵌套格式 { quota: { gpu: 4, ... } },提取内层
if (q.quota && typeof q.quota === 'object' && !q.gpu) {
q = q.quota as Record<string, unknown>
}
2026-08-03 16:20:21 +08:00
return {
gpu: Number(q.gpu || q.gpu_quota || 0),
storage: Number(q.storage || q.storage_quota || 0),
maxProjects: Number(q.max_projects || 0),
}
}
function formatQuota(quota: Record<string, unknown> | undefined | null) {
const q = parseQuota(quota)
const parts: string[] = []
if (q.gpu > 0) parts.push(`GPU ${q.gpu}`)
if (q.storage > 0) parts.push(`存储 ${q.storage}GB`)
if (q.maxProjects > 0) parts.push(`项目 ${q.maxProjects}`)
return parts.length ? parts.join(' | ') : '—'
}
2026-08-03 09:34:08 +08:00
async function load() {
const id = route.params.id as string
loading.value = true
try {
tenant.value = await getTenant(id)
2026-08-03 16:20:21 +08:00
const q = parseQuota(tenant.value?.quota)
quotaForm.gpu = q.gpu
quotaForm.storage = q.storage
quotaForm.maxProjects = q.maxProjects
2026-08-03 09:34:08 +08:00
} finally {
loading.value = false
}
}
async function saveQuota() {
if (!tenant.value) return
2026-08-03 16:20:21 +08:00
const quota: Record<string, unknown> = {}
if (quotaForm.gpu > 0) quota.gpu = quotaForm.gpu
if (quotaForm.storage > 0) quota.storage = quotaForm.storage
if (quotaForm.maxProjects > 0) quota.max_projects = quotaForm.maxProjects
await setTenantQuota(tenant.value.id, quota)
ElMessage.success('配额已保存')
load()
2026-08-03 09:34:08 +08:00
}
onMounted(load)
</script>
<template>
<div class="page">
<el-page-header title="返回" @back="router.back()">
<template #content>
<span class="page-title">租户详情{{ tenant?.name }}</span>
</template>
</el-page-header>
<el-card class="section" v-loading="loading">
<template #header>基本信息</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="名称">{{ tenant?.name }}</el-descriptions-item>
2026-08-03 16:20:21 +08:00
<el-descriptions-item label="用户ID">{{ tenant?.code }}</el-descriptions-item>
2026-08-03 09:34:08 +08:00
<el-descriptions-item label="状态">{{ tenant?.status }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ tenant?.create_time }}</el-descriptions-item>
2026-08-03 16:20:21 +08:00
<el-descriptions-item label="配额">{{ formatQuota(tenant?.quota) }}</el-descriptions-item>
2026-08-03 09:34:08 +08:00
</el-descriptions>
<el-divider />
<div class="quota-edit">
2026-08-03 16:20:21 +08:00
<span class="label">配额设置0 表示不限制</span>
<el-form label-width="100px">
<el-form-item label="GPU 数量">
<el-input-number v-model="quotaForm.gpu" :min="0" :step="1" />
</el-form-item>
<el-form-item label="存储配额(GB)">
<el-input-number v-model="quotaForm.storage" :min="0" :step="10" />
</el-form-item>
<el-form-item label="最大项目数">
<el-input-number v-model="quotaForm.maxProjects" :min="0" :step="1" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveQuota">保存配额</el-button>
</el-form-item>
</el-form>
2026-08-03 09:34:08 +08:00
</div>
</el-card>
</div>
</template>
<style scoped lang="scss">
.page { padding: 16px; }
.section { margin-top: 16px; }
.page-title { font-size: 16px; font-weight: 600; }
.quota-edit { display: flex; flex-direction: column; gap: 12px; max-width: 480px; }
.label { font-size: 13px; color: #606266; }
</style>