初始版本
This commit is contained in:
419
app/pages/developer/cloudCredentials.vue
Normal file
419
app/pages/developer/cloudCredentials.vue
Normal file
@@ -0,0 +1,419 @@
|
||||
<template>
|
||||
<div class="dev-page">
|
||||
<!-- 页面头部 -->
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2 class="page-title">☁️ 云账号凭证</h2>
|
||||
<p class="page-desc">管理阿里云、腾讯云、华为云等云服务商的账号凭证,用于创建存储桶和云资源。</p>
|
||||
</div>
|
||||
<a-button type="primary" @click="showCreateModal = true">
|
||||
+ 添加云账号
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div class="page-body">
|
||||
<!-- 使用提示 -->
|
||||
<a-alert
|
||||
class="mb-5"
|
||||
show-icon
|
||||
type="info"
|
||||
message="凭证说明"
|
||||
description="云账号凭证用于调用云服务商 API 创建存储桶等资源。AccessKeySecret 会被加密存储,不会以明文形式返回。请妥善保管,丢失后需重新创建。"
|
||||
/>
|
||||
|
||||
<!-- 凭证列表 -->
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">我的云账号凭证</span>
|
||||
<a-tag color="blue">{{ credentialList.length }} 个</a-tag>
|
||||
</div>
|
||||
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="credentialList.length === 0 && !loading" class="empty-state">
|
||||
<div class="empty-icon">☁️</div>
|
||||
<div class="empty-title">还没有云账号凭证</div>
|
||||
<div class="empty-desc">添加云服务商账号,开始创建云存储桶</div>
|
||||
<a-button type="primary" class="mt-4" @click="showCreateModal = true">添加云账号</a-button>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
v-else
|
||||
:columns="columns"
|
||||
:data-source="credentialList"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
class="credential-table"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'provider'">
|
||||
<span class="provider-cell">
|
||||
<!-- <span class="provider-icon">{{ getProviderIcon(record.provider) }}</span>-->
|
||||
{{ getProviderLabel(record.provider) }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 1 ? 'green' : 'default'">
|
||||
{{ record.status === 1 ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'testStatus'">
|
||||
<a-tag v-if="record.testStatus === 1" color="green">
|
||||
<template #icon><CheckCircleOutlined /></template>
|
||||
连接正常
|
||||
</a-tag>
|
||||
<a-tag v-else-if="record.testStatus === 2" color="error">
|
||||
<template #icon><CloseCircleOutlined /></template>
|
||||
连接失败
|
||||
</a-tag>
|
||||
<a-tag v-else color="default">
|
||||
<template #icon><QuestionCircleOutlined /></template>
|
||||
未测试
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<a-space>
|
||||
<a-tooltip title="测试连接">
|
||||
<a-button size="small" :loading="record.testing" @click="testConnection(record)">
|
||||
<template #icon><ApiOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="编辑">
|
||||
<a-button size="small" @click="editCredential(record)">
|
||||
<template #icon><EditOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="record.status === 1 ? '禁用' : '启用'">
|
||||
<a-button size="small" @click="toggleStatus(record)">
|
||||
<template #icon><StopOutlined v-if="record.status === 1" /><CheckCircleOutlined v-else /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-popconfirm
|
||||
title="确认删除该云账号凭证?此操作不可撤销。"
|
||||
ok-text="删除"
|
||||
ok-type="danger"
|
||||
cancel-text="取消"
|
||||
@confirm="deleteCredential(record.id)"
|
||||
>
|
||||
<a-button size="small" danger>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 创建/编辑云账号凭证弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="showCreateModal"
|
||||
:title="editingId ? '编辑云账号凭证' : '添加云账号凭证'"
|
||||
:ok-text="editingId ? '保存' : '创建'"
|
||||
cancel-text="取消"
|
||||
:confirm-loading="saving"
|
||||
@ok="handleSave"
|
||||
>
|
||||
<a-form layout="vertical" class="mt-2">
|
||||
<a-form-item label="云服务商" required>
|
||||
<a-select
|
||||
v-model:value="form.provider"
|
||||
placeholder="选择云服务商"
|
||||
:disabled="!!editingId"
|
||||
>
|
||||
<a-select-option v-for="opt in providerOptions" :key="opt.value" :value="opt.value">
|
||||
<span>{{ opt.icon }} {{ opt.label }}</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="凭证名称" required>
|
||||
<a-input
|
||||
v-model:value="form.name"
|
||||
placeholder="例如:阿里云生产账号、腾讯云测试账号..."
|
||||
:maxlength="50"
|
||||
show-count
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="AccessKeyId" required>
|
||||
<a-input
|
||||
v-model:value="form.accessKeyId"
|
||||
placeholder="请输入 AccessKeyId"
|
||||
:maxlength="100"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="AccessKeySecret" required>
|
||||
<a-input-password
|
||||
v-model:value="form.accessKeySecret"
|
||||
placeholder="请输入 AccessKeySecret"
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注">
|
||||
<a-textarea
|
||||
v-model:value="form.remark"
|
||||
:rows="2"
|
||||
placeholder="可选,用途说明"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
QuestionCircleOutlined,
|
||||
ApiOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
StopOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import {
|
||||
pageCloudCredential,
|
||||
createCloudCredential,
|
||||
updateCloudCredential,
|
||||
removeCloudCredential,
|
||||
testCloudCredential,
|
||||
CLOUD_PROVIDER_OPTIONS,
|
||||
getProviderLabel,
|
||||
getProviderIcon,
|
||||
} from '@/api/app/cloudCredential'
|
||||
import type { AppCloudCredential, AppCloudCredentialParam } from '@/api/app/cloudCredential/model'
|
||||
|
||||
definePageMeta({ layout: 'developer' })
|
||||
useHead({ title: '云账号凭证 - 开发者中心' })
|
||||
|
||||
const columns = [
|
||||
// { title: '名称', dataIndex: 'name', key: 'name', width: 180 },
|
||||
{ title: '服务商', key: 'provider', width: 140 },
|
||||
{ title: 'AK', dataIndex: 'accessKeyId', key: 'accessKeyId', width: 180 },
|
||||
{ title: '状态', key: 'status', width: 80 },
|
||||
{ title: '连接测试', key: 'testStatus', width: 100 },
|
||||
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 160 },
|
||||
{ title: '操作', key: 'actions', width: 200 },
|
||||
]
|
||||
|
||||
const providerOptions = CLOUD_PROVIDER_OPTIONS
|
||||
|
||||
const showCreateModal = ref(false)
|
||||
const editingId = ref<number | null>(null)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
provider: '',
|
||||
name: '',
|
||||
accessKeyId: '',
|
||||
accessKeySecret: '',
|
||||
remark: '',
|
||||
})
|
||||
|
||||
// 凭证列表
|
||||
const credentialList = ref<AppCloudCredential[]>([])
|
||||
|
||||
// 加载凭证列表
|
||||
async function loadCredentials() {
|
||||
loading.value = true
|
||||
try {
|
||||
const params: AppCloudCredentialParam = { page: 1, limit: 100 }
|
||||
console.log('请求参数:', params)
|
||||
const result = await pageCloudCredential(params)
|
||||
console.log('返回结果:', result)
|
||||
console.log('列表数据:', result?.list)
|
||||
credentialList.value = (result?.list || []).map((item: any) => ({
|
||||
...item,
|
||||
testing: false,
|
||||
}))
|
||||
console.log('最终数据:', credentialList.value)
|
||||
} catch (error: any) {
|
||||
console.error('加载云账号凭证失败:', error)
|
||||
console.error('错误详情:', error.response?.data, error.config?.url)
|
||||
message.error(error.message || '加载失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
async function testConnection(record: AppCloudCredential) {
|
||||
record.testing = true
|
||||
try {
|
||||
const result = await testCloudCredential(record.id!)
|
||||
if (result?.success) {
|
||||
message.success(result.message || '连接测试成功')
|
||||
record.testStatus = 1
|
||||
record.testMessage = result.message
|
||||
} else {
|
||||
message.error(result?.message || '连接测试失败')
|
||||
record.testStatus = 2
|
||||
record.testMessage = result?.message
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('测试连接失败:', error)
|
||||
message.error(error.message || '测试连接失败')
|
||||
record.testStatus = 2
|
||||
record.testMessage = error.message
|
||||
} finally {
|
||||
record.testing = false
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑凭证
|
||||
function editCredential(record: AppCloudCredential) {
|
||||
editingId.value = record.id || null
|
||||
form.provider = record.provider || ''
|
||||
form.name = record.name || ''
|
||||
form.accessKeyId = ''
|
||||
form.accessKeySecret = ''
|
||||
form.remark = record.remark || ''
|
||||
showCreateModal.value = true
|
||||
}
|
||||
|
||||
// 切换状态
|
||||
async function toggleStatus(record: AppCloudCredential) {
|
||||
try {
|
||||
const newStatus = record.status === 1 ? 0 : 1
|
||||
await updateCloudCredential({
|
||||
id: record.id,
|
||||
status: newStatus,
|
||||
})
|
||||
record.status = newStatus
|
||||
message.success(newStatus === 1 ? '已启用' : '已禁用')
|
||||
} catch (error: any) {
|
||||
console.error('更新状态失败:', error)
|
||||
message.error(error.message || '操作失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除凭证
|
||||
async function deleteCredential(id: number) {
|
||||
try {
|
||||
await removeCloudCredential(id)
|
||||
await loadCredentials()
|
||||
message.success('云账号凭证已删除')
|
||||
} catch (error: any) {
|
||||
console.error('删除凭证失败:', error)
|
||||
message.error(error.message || '删除失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存凭证
|
||||
async function handleSave() {
|
||||
if (!form.provider) {
|
||||
message.error('请选择云服务商')
|
||||
return
|
||||
}
|
||||
if (!form.name.trim()) {
|
||||
message.error('请输入凭证名称')
|
||||
return
|
||||
}
|
||||
if (!form.accessKeyId.trim()) {
|
||||
message.error('请输入 AccessKeyId')
|
||||
return
|
||||
}
|
||||
if (!form.accessKeySecret.trim()) {
|
||||
message.error('请输入 AccessKeySecret')
|
||||
return
|
||||
}
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
if (editingId.value) {
|
||||
await updateCloudCredential({
|
||||
id: editingId.value,
|
||||
name: form.name,
|
||||
accessKeyId: form.accessKeyId || undefined,
|
||||
accessKeySecret: form.accessKeySecret || undefined,
|
||||
remark: form.remark,
|
||||
})
|
||||
message.success('云账号凭证已更新')
|
||||
} else {
|
||||
await createCloudCredential({
|
||||
provider: form.provider,
|
||||
name: form.name,
|
||||
accessKeyId: form.accessKeyId,
|
||||
accessKeySecret: form.accessKeySecret,
|
||||
remark: form.remark,
|
||||
status: 1,
|
||||
})
|
||||
message.success('云账号凭证已创建')
|
||||
}
|
||||
showCreateModal.value = false
|
||||
resetForm()
|
||||
await loadCredentials()
|
||||
} catch (error: any) {
|
||||
console.error('保存云账号凭证失败:', error)
|
||||
message.error(error.message || '保存失败,请稍后重试')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
editingId.value = null
|
||||
form.provider = ''
|
||||
form.name = ''
|
||||
form.accessKeyId = ''
|
||||
form.accessKeySecret = ''
|
||||
form.remark = ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCredentials()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dev-page {
|
||||
@apply p-6;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
@apply flex justify-between items-start mb-6;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
@apply text-2xl font-bold m-0;
|
||||
}
|
||||
|
||||
.page-desc {
|
||||
@apply text-gray-500 mt-1;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
@apply max-w-5xl;
|
||||
}
|
||||
|
||||
.provider-cell {
|
||||
@apply flex items-center gap-2;
|
||||
}
|
||||
|
||||
.provider-icon {
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
.credential-table {
|
||||
@apply mt-4;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
@apply py-12 text-center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
@apply text-5xl mb-4;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
@apply text-lg font-medium text-gray-700;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
@apply text-gray-500 mt-1 mb-4;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user