Files
nl-blogs/client/src/pages/admin/OSSConfigs.vue
2026-01-19 20:21:09 +08:00

556 lines
19 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.
<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>