Files
YG_FT/frontend/src/views/approvals/ComputeAccessRequestView.vue
wuyongtao 6f0e82f351 feat: 平台治理与权限体系完善,存储进度/GPU预留/审批中心与日志整合
- 平台治理: 租户用户权限层次、资源ACL、审批中心与审批模板、访问申请
- 存储: MinIO 存储进度迁移、对象存储安全加固与测试
- 计算: GPU 资源预留、compute 轮询与同步增强
- 权限: permission v2 迁移、权限安全验收测试
- 日志: 后端运行日志中文说明、操作日志整合
- 数据处理/评测: 数据转换与模型评测优化

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 09:49:48 +08:00

93 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getGpuRequestOptions, requestGpuAccess, type GpuRequestOption } from '@/api/modules/approval'
const loading = ref(false)
const submitting = ref(false)
const loadError = ref('')
const nodes = ref<GpuRequestOption[]>([])
const form = reactive({ node_id: '', gpu_indices: [] as number[], reason: '' })
const selectedNode = computed(() => nodes.value.find((node) => node.id === form.node_id) || null)
async function load() {
loading.value = true
try {
loadError.value = ''
const result = await getGpuRequestOptions()
nodes.value = result.nodes || []
if (!nodes.value.some((node) => node.id === form.node_id)) {
form.node_id = nodes.value[0]?.id || ''
form.gpu_indices = []
}
} catch {
loadError.value = '可申请算力加载失败,请刷新后重试。'
} finally {
loading.value = false
}
}
function changeNode() {
form.gpu_indices = []
}
async function submit() {
if (!form.node_id) return ElMessage.warning('请选择算力节点')
if (!form.gpu_indices.length) return ElMessage.warning('请选择至少一张算力卡')
submitting.value = true
try {
const result = await requestGpuAccess({
assignments: form.gpu_indices.map((gpu_index) => ({ node_id: form.node_id, gpu_index })),
reason: form.reason.trim() || undefined,
})
ElMessage.success(result.approval_required === false ? '算力分配成功' : '算力申请已提交,等待审批')
form.gpu_indices = []
form.reason = ''
} finally {
submitting.value = false
await load()
}
}
onMounted(load)
</script>
<template>
<div class="compute-request" v-loading="loading">
<el-alert v-if="loadError" type="error" show-icon :closable="false" :title="loadError" class="state-alert" />
<el-alert v-else-if="!nodes.length && !loading" type="info" show-icon :closable="false" title="当前没有可申请的空闲算力卡,请稍后重试或联系管理员。" class="state-alert" />
<el-card shadow="never">
<template #header><span>申请算力卡</span></template>
<el-form label-width="100px" class="request-form">
<el-form-item label="算力节点">
<el-select v-model="form.node_id" filterable placeholder="选择在线算力节点" style="width: min(520px, 100%)" @change="changeNode">
<el-option v-for="node in nodes" :key="node.id" :label="`${node.name}${node.code}`" :value="node.id" />
</el-select>
</el-form-item>
<el-form-item label="算力卡">
<el-checkbox-group v-if="selectedNode" v-model="form.gpu_indices">
<el-checkbox v-for="gpu in selectedNode.gpus" :key="gpu.index" :value="gpu.index">
GPU {{ gpu.index }} · {{ gpu.name }} · {{ gpu.memory_total_gb }} GB
</el-checkbox>
</el-checkbox-group>
<span v-else class="muted">请先选择算力节点</span>
</el-form-item>
<el-form-item label="申请理由">
<el-input v-model="form.reason" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="说明训练、推理或评测用途" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitting" :disabled="!nodes.length" @click="submit">提交算力申请</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<style scoped lang="scss">
.compute-request { padding: 16px; }
.state-alert { margin-bottom: 16px; }
.request-form { max-width: 760px; }
.muted { color: #94a3b8; }
</style>