556 lines
19 KiB
Vue
556 lines
19 KiB
Vue
<template>
|
||
<div class="w-full animate-reveal">
|
||
<div class="mb-6">
|
||
<h1 class="text-2xl font-serif italic text-white">OSS配置管理</h1>
|
||
</div>
|
||
|
||
<!-- Tab导航 -->
|
||
<div class="mb-6 border-b border-white/10">
|
||
<div class="flex gap-4">
|
||
<button
|
||
v-for="tab in tabs"
|
||
:key="tab.value"
|
||
@click="switchTab(tab.value)"
|
||
:class="[
|
||
'px-4 py-2 text-sm font-medium transition-colors border-b-2',
|
||
activeTab === tab.value
|
||
? 'text-art-accent border-art-accent'
|
||
: 'text-art-muted border-transparent hover:text-white'
|
||
]"
|
||
>
|
||
{{ tab.label }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 表单 -->
|
||
<div class="admin-card p-6">
|
||
<form @submit.prevent="saveOSSConfig" class="space-y-6">
|
||
<div class="space-y-2">
|
||
<label for="ossName" class="block text-xs font-medium text-art-muted uppercase tracking-wider">配置名称</label>
|
||
<input
|
||
type="text"
|
||
id="ossName"
|
||
v-model="ossForm.name"
|
||
required
|
||
class="admin-input"
|
||
:placeholder="`${getStorageTypeLabel(activeTab)}配置名称`"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="storageType" class="block text-xs font-medium text-art-muted uppercase tracking-wider">存储类型</label>
|
||
<input
|
||
type="text"
|
||
id="storageType"
|
||
:value="getStorageTypeLabel(activeTab)"
|
||
disabled
|
||
class="admin-input bg-white/5 text-art-muted cursor-not-allowed"
|
||
>
|
||
</div>
|
||
|
||
<!-- 阿里云OSS字段 -->
|
||
<template v-if="activeTab === 'aliyun'">
|
||
<div class="space-y-2">
|
||
<label for="ossAccessKeyId" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
OSS Access Key ID <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="ossAccessKeyId"
|
||
v-model="ossForm.ossAccessKeyId"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="OSS Access Key ID"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="ossAccessKeySecret" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
OSS Access Key Secret <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="password"
|
||
id="ossAccessKeySecret"
|
||
v-model="ossForm.ossAccessKeySecret"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="OSS Access Key Secret"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="ossEndpoint" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
OSS Endpoint <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="ossEndpoint"
|
||
v-model="ossForm.ossEndpoint"
|
||
required
|
||
class="admin-input"
|
||
placeholder="例如: https://oss-cn-beijing.aliyuncs.com"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="ossBucket" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
OSS Bucket <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="ossBucket"
|
||
v-model="ossForm.ossBucket"
|
||
required
|
||
class="admin-input"
|
||
placeholder="存储桶名称"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="ossDomain" class="block text-xs font-medium text-art-muted uppercase tracking-wider">OSS Domain</label>
|
||
<input
|
||
type="text"
|
||
id="ossDomain"
|
||
v-model="ossForm.ossDomain"
|
||
class="admin-input"
|
||
placeholder="例如: https://cdn.example.com"
|
||
>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 腾讯云COS字段 -->
|
||
<template v-if="activeTab === 'qcloud'">
|
||
<div class="space-y-2">
|
||
<label for="qcloudSecretId" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
QCloud Secret ID <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="qcloudSecretId"
|
||
v-model="ossForm.qcloudSecretId"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="QCloud Secret ID"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qcloudSecretKey" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
QCloud Secret Key <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="password"
|
||
id="qcloudSecretKey"
|
||
v-model="ossForm.qcloudSecretKey"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="QCloud Secret Key"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qcloudRegion" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
QCloud Region <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="qcloudRegion"
|
||
v-model="ossForm.qcloudRegion"
|
||
required
|
||
class="admin-input"
|
||
placeholder="例如: ap-beijing"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qcloudBucket" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
QCloud Bucket <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="qcloudBucket"
|
||
v-model="ossForm.qcloudBucket"
|
||
required
|
||
class="admin-input"
|
||
placeholder="存储桶名称"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qcloudDomain" class="block text-xs font-medium text-art-muted uppercase tracking-wider">QCloud Domain</label>
|
||
<input
|
||
type="text"
|
||
id="qcloudDomain"
|
||
v-model="ossForm.qcloudDomain"
|
||
class="admin-input"
|
||
placeholder="例如: https://cdn.example.com"
|
||
>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 七牛云字段 -->
|
||
<template v-if="activeTab === 'qiniu'">
|
||
<div class="space-y-2">
|
||
<label for="qiniuAccessKey" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
Qiniu Access Key <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="qiniuAccessKey"
|
||
v-model="ossForm.qiniuAccessKey"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="Qiniu Access Key"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qiniuSecretKey" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
Qiniu Secret Key <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="password"
|
||
id="qiniuSecretKey"
|
||
v-model="ossForm.qiniuSecretKey"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="Qiniu Secret Key"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qiniuBucket" class="block text-xs font-medium text-art-muted uppercase tracking-wider">
|
||
Qiniu Bucket <span class="text-red-400">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="qiniuBucket"
|
||
v-model="ossForm.qiniuBucket"
|
||
required
|
||
class="admin-input"
|
||
placeholder="存储桶名称"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qiniuRegion" class="block text-xs font-medium text-art-muted uppercase tracking-wider">Qiniu Region</label>
|
||
<input
|
||
type="text"
|
||
id="qiniuRegion"
|
||
v-model="ossForm.qiniuRegion"
|
||
class="admin-input"
|
||
placeholder="例如: z0 (华东), z1 (华北), z2 (华南)"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="qiniuDomain" class="block text-xs font-medium text-art-muted uppercase tracking-wider">Qiniu Domain</label>
|
||
<input
|
||
type="text"
|
||
id="qiniuDomain"
|
||
v-model="ossForm.qiniuDomain"
|
||
class="admin-input"
|
||
placeholder="例如: https://cdn.example.com"
|
||
>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 本地存储不需要额外字段 -->
|
||
<div v-if="activeTab === 'local'" class="p-4 bg-white/5 rounded text-sm text-art-muted">
|
||
本地存储不需要配置额外的密钥信息
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label class="flex items-center gap-2 cursor-pointer">
|
||
<input
|
||
type="checkbox"
|
||
v-model="ossForm.isActive"
|
||
:true-value="1"
|
||
:false-value="0"
|
||
class="w-4 h-4 rounded border-white/20 bg-white/5 text-art-accent focus:ring-art-accent"
|
||
>
|
||
<span class="text-sm text-art-muted">启用此配置</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="pt-4 flex justify-between items-center">
|
||
<button
|
||
v-if="currentConfig && !isDefaultConfig(currentConfig)"
|
||
type="button"
|
||
@click="deleteOSSConfig"
|
||
class="admin-btn-danger"
|
||
>
|
||
删除配置
|
||
</button>
|
||
<div v-else></div>
|
||
<div class="flex gap-3">
|
||
<button type="submit" class="admin-btn-primary" :disabled="isSubmitting">
|
||
{{ isSubmitting ? '保存中...' : (currentConfig ? '保存' : '创建') }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, computed, watch } from 'vue'
|
||
import { getOSSConfigs, createOSSConfig, updateOSSConfig, deleteOSSConfig as deleteOSSConfigApi, OSSConfig } from '../../services/api'
|
||
import { useToast } from '../../composables/useToast'
|
||
|
||
const toast = useToast()
|
||
|
||
// Tab管理
|
||
const activeTab = ref<string>('local')
|
||
const tabs = [
|
||
{ value: 'local', label: '本地存储' },
|
||
{ value: 'aliyun', label: '阿里云' },
|
||
{ value: 'qcloud', label: '腾讯云' },
|
||
{ value: 'qiniu', label: '七牛云' }
|
||
]
|
||
|
||
// OSS配置管理
|
||
const ossConfigs = ref<OSSConfig[]>([])
|
||
const isSubmitting = ref(false)
|
||
|
||
const ossForm = ref({
|
||
id: 0,
|
||
name: '',
|
||
storageType: '',
|
||
// 阿里云OSS专用字段
|
||
ossAccessKeyId: '',
|
||
ossAccessKeySecret: '',
|
||
ossEndpoint: '',
|
||
ossBucket: '',
|
||
ossDomain: '',
|
||
// 腾讯云COS专用字段
|
||
qcloudSecretId: '',
|
||
qcloudSecretKey: '',
|
||
qcloudRegion: '',
|
||
qcloudBucket: '',
|
||
qcloudDomain: '',
|
||
// 七牛云专用字段
|
||
qiniuAccessKey: '',
|
||
qiniuSecretKey: '',
|
||
qiniuBucket: '',
|
||
qiniuRegion: '',
|
||
qiniuDomain: '',
|
||
isActive: 0 as number
|
||
})
|
||
|
||
// 存储类型选项
|
||
const storageTypeOptions = [
|
||
{ value: 'local', label: '本地存储' },
|
||
{ value: 'qcloud', label: '腾讯云COS' },
|
||
{ value: 'aliyun', label: '阿里云OSS' },
|
||
{ value: 'qiniu', label: '七牛云' }
|
||
]
|
||
|
||
// 获取存储类型标签
|
||
const getStorageTypeLabel = (type: string): string => {
|
||
const option = storageTypeOptions.find(opt => opt.value === type)
|
||
return option ? option.label : type
|
||
}
|
||
|
||
// 获取当前tab对应的配置
|
||
const currentConfig = computed(() => {
|
||
// 优先查找已启用的配置
|
||
const activeConfig = ossConfigs.value.find(c => c.storageType === activeTab.value && c.isActive === 1)
|
||
if (activeConfig) return activeConfig
|
||
// 如果没有已启用的,查找任意配置
|
||
return ossConfigs.value.find(c => c.storageType === activeTab.value)
|
||
})
|
||
|
||
// 判断是否为默认配置
|
||
const isDefaultConfig = (config: OSSConfig | undefined): boolean => {
|
||
if (!config) return false
|
||
const defaultNames = ['本地存储', '阿里云OSS', '腾讯云COS', '七牛云']
|
||
return defaultNames.includes(config.name)
|
||
}
|
||
|
||
// 切换Tab时加载对应配置
|
||
const switchTab = (tabValue: string) => {
|
||
activeTab.value = tabValue
|
||
loadConfigForTab()
|
||
}
|
||
|
||
// 加载当前tab的配置
|
||
const loadConfigForTab = () => {
|
||
const config = currentConfig.value
|
||
if (config) {
|
||
// 填充表单
|
||
ossForm.value = {
|
||
id: config.id,
|
||
name: config.name,
|
||
storageType: config.storageType,
|
||
ossAccessKeyId: (config as any).ossAccessKeyId === '***' ? '' : (config as any).ossAccessKeyId || '',
|
||
ossAccessKeySecret: (config as any).ossAccessKeySecret === '***' ? '' : (config as any).ossAccessKeySecret || '',
|
||
ossEndpoint: (config as any).ossEndpoint || '',
|
||
ossBucket: (config as any).ossBucket || '',
|
||
ossDomain: (config as any).ossDomain || '',
|
||
qcloudSecretId: (config as any).qcloudSecretId === '***' ? '' : (config as any).qcloudSecretId || '',
|
||
qcloudSecretKey: (config as any).qcloudSecretKey === '***' ? '' : (config as any).qcloudSecretKey || '',
|
||
qcloudRegion: (config as any).qcloudRegion || '',
|
||
qcloudBucket: (config as any).qcloudBucket || '',
|
||
qcloudDomain: (config as any).qcloudDomain || '',
|
||
qiniuAccessKey: (config as any).qiniuAccessKey === '***' ? '' : (config as any).qiniuAccessKey || '',
|
||
qiniuSecretKey: (config as any).qiniuSecretKey === '***' ? '' : (config as any).qiniuSecretKey || '',
|
||
qiniuBucket: (config as any).qiniuBucket || '',
|
||
qiniuRegion: (config as any).qiniuRegion || '',
|
||
qiniuDomain: (config as any).qiniuDomain || '',
|
||
isActive: config.isActive
|
||
}
|
||
} else {
|
||
// 清空表单,设置默认值
|
||
const defaultNames: Record<string, string> = {
|
||
local: '本地存储',
|
||
aliyun: '阿里云OSS',
|
||
qcloud: '腾讯云COS',
|
||
qiniu: '七牛云'
|
||
}
|
||
ossForm.value = {
|
||
id: 0,
|
||
name: defaultNames[activeTab.value] || '',
|
||
storageType: activeTab.value,
|
||
ossAccessKeyId: '',
|
||
ossAccessKeySecret: '',
|
||
ossEndpoint: '',
|
||
ossBucket: '',
|
||
ossDomain: '',
|
||
qcloudSecretId: '',
|
||
qcloudSecretKey: '',
|
||
qcloudRegion: '',
|
||
qcloudBucket: '',
|
||
qcloudDomain: '',
|
||
qiniuAccessKey: '',
|
||
qiniuSecretKey: '',
|
||
qiniuBucket: '',
|
||
qiniuRegion: '',
|
||
qiniuDomain: '',
|
||
isActive: activeTab.value === 'local' ? 1 : 0 // 本地存储默认启用
|
||
}
|
||
}
|
||
}
|
||
|
||
// OSS配置相关函数
|
||
const fetchOSSConfigs = async () => {
|
||
try {
|
||
ossConfigs.value = await getOSSConfigs()
|
||
|
||
// 设置默认tab:优先选择已启用的配置
|
||
const activeConfig = ossConfigs.value.find(c => c.isActive === 1)
|
||
if (activeConfig) {
|
||
activeTab.value = activeConfig.storageType
|
||
} else {
|
||
activeTab.value = 'local' // 默认本地存储
|
||
}
|
||
|
||
// 加载当前tab的配置
|
||
loadConfigForTab()
|
||
} catch (error) {
|
||
console.error('Error fetching OSS configs:', error)
|
||
toast.showToast('获取OSS配置失败', 'error')
|
||
}
|
||
}
|
||
|
||
const saveOSSConfig = async () => {
|
||
// 设置存储类型
|
||
ossForm.value.storageType = activeTab.value
|
||
|
||
// 验证必填字段
|
||
if (activeTab.value === 'aliyun') {
|
||
if (!ossForm.value.ossAccessKeyId || !ossForm.value.ossAccessKeySecret || !ossForm.value.ossEndpoint || !ossForm.value.ossBucket) {
|
||
toast.showToast('请填写所有必填字段', 'error')
|
||
return
|
||
}
|
||
} else if (activeTab.value === 'qcloud') {
|
||
if (!ossForm.value.qcloudSecretId || !ossForm.value.qcloudSecretKey || !ossForm.value.qcloudRegion || !ossForm.value.qcloudBucket) {
|
||
toast.showToast('请填写所有必填字段', 'error')
|
||
return
|
||
}
|
||
} else if (activeTab.value === 'qiniu') {
|
||
if (!ossForm.value.qiniuAccessKey || !ossForm.value.qiniuSecretKey || !ossForm.value.qiniuBucket) {
|
||
toast.showToast('请填写所有必填字段', 'error')
|
||
return
|
||
}
|
||
}
|
||
|
||
isSubmitting.value = true
|
||
try {
|
||
const updateData: any = {
|
||
name: ossForm.value.name,
|
||
storageType: ossForm.value.storageType,
|
||
isActive: ossForm.value.isActive
|
||
}
|
||
|
||
// 根据存储类型设置对应字段
|
||
if (activeTab.value === 'aliyun') {
|
||
updateData.ossAccessKeyId = ossForm.value.ossAccessKeyId
|
||
updateData.ossAccessKeySecret = ossForm.value.ossAccessKeySecret
|
||
updateData.ossEndpoint = ossForm.value.ossEndpoint
|
||
updateData.ossBucket = ossForm.value.ossBucket
|
||
updateData.ossDomain = ossForm.value.ossDomain || ''
|
||
} else if (activeTab.value === 'qcloud') {
|
||
updateData.qcloudSecretId = ossForm.value.qcloudSecretId
|
||
updateData.qcloudSecretKey = ossForm.value.qcloudSecretKey
|
||
updateData.qcloudRegion = ossForm.value.qcloudRegion
|
||
updateData.qcloudBucket = ossForm.value.qcloudBucket
|
||
updateData.qcloudDomain = ossForm.value.qcloudDomain || ''
|
||
} else if (activeTab.value === 'qiniu') {
|
||
updateData.qiniuAccessKey = ossForm.value.qiniuAccessKey
|
||
updateData.qiniuSecretKey = ossForm.value.qiniuSecretKey
|
||
updateData.qiniuBucket = ossForm.value.qiniuBucket
|
||
updateData.qiniuRegion = ossForm.value.qiniuRegion || ''
|
||
updateData.qiniuDomain = ossForm.value.qiniuDomain || ''
|
||
}
|
||
|
||
if (ossForm.value.id > 0) {
|
||
// 更新
|
||
await updateOSSConfig(ossForm.value.id, updateData)
|
||
toast.showToast('OSS配置更新成功', 'success')
|
||
} else {
|
||
// 创建
|
||
await createOSSConfig({
|
||
...updateData,
|
||
createdAt: '',
|
||
updatedAt: ''
|
||
})
|
||
toast.showToast('OSS配置创建成功', 'success')
|
||
}
|
||
|
||
await fetchOSSConfigs()
|
||
} catch (error: any) {
|
||
console.error('Error saving OSS config:', error)
|
||
toast.showToast(error.message || (ossForm.value.id > 0 ? '更新OSS配置失败' : '创建OSS配置失败'), 'error')
|
||
} finally {
|
||
isSubmitting.value = false
|
||
}
|
||
}
|
||
|
||
const deleteOSSConfig = async () => {
|
||
if (!currentConfig.value) return
|
||
|
||
if (isDefaultConfig(currentConfig.value)) {
|
||
toast.showToast('默认配置不能删除', 'error')
|
||
return
|
||
}
|
||
|
||
if (confirm(`确定要删除这个OSS配置吗?`)) {
|
||
try {
|
||
await deleteOSSConfigApi(currentConfig.value.id)
|
||
toast.showToast('OSS配置删除成功', 'success')
|
||
await fetchOSSConfigs()
|
||
} catch (error: any) {
|
||
console.error('Error deleting OSS config:', error)
|
||
toast.showToast(error.message || '删除OSS配置失败', 'error')
|
||
}
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
fetchOSSConfigs()
|
||
})
|
||
</script>
|