阿里云OSS设置

This commit is contained in:
李琦
2026-07-14 10:05:33 +08:00
parent 8ac9f591a0
commit 02cf290bad
16 changed files with 782 additions and 244 deletions

View File

@@ -165,7 +165,7 @@ const uploadFile = async (file: File) => {
if (props.categoryId) {
formData.append('categoryId', props.categoryId.toString())
}
formData.append('storageType', 'local')
formData.append('storageType', 'aliyun')
const result = await parseApiResponse<{ fileUrl: string }>(await authFetch(`${API_BASE}/admin/attachments/upload`, {
method: 'POST',

View File

@@ -366,7 +366,7 @@ const uploadSingleFile = async (file: File): Promise<string | null> => {
if (props.categoryId) {
formData.append('categoryId', props.categoryId.toString())
}
formData.append('storageType', 'local')
formData.append('storageType', 'aliyun')
const result = await parseApiResponse<{ fileUrl: string }>(await authFetch(`${API_BASE}/admin/attachments/upload`, {
method: 'POST',

View File

@@ -46,7 +46,7 @@ const uploadFile = async (file: File) => {
const formData = new FormData()
formData.append('file', file)
if (props.categoryId) formData.append('categoryId', props.categoryId.toString())
formData.append('storageType', 'local')
formData.append('storageType', 'aliyun')
const result = await parseApiResponse<{ fileUrl: string }>(
await authFetch(`${API_BASE}/admin/attachments/upload`, { method: 'POST', headers: {}, body: formData })
)

View File

@@ -14,7 +14,7 @@ export function useMdMediaUpload(editorRef: Ref<ExposeParam | undefined>) {
const formData = new FormData()
formData.append('file', file)
formData.append('categoryId', '1')
formData.append('storageType', 'local')
formData.append('storageType', 'aliyun')
const response = await authFetch('/api/admin/attachments/upload', {
method: 'POST',

View File

@@ -293,7 +293,7 @@ const uploadFileWithFile = async (file: File) => {
if (uploadCategoryId.value > 0) {
formData.append('categoryId', uploadCategoryId.value.toString())
}
formData.append('storageType', 'local')
formData.append('storageType', 'aliyun')
await parseApiResponse(await authFetch(`${API_BASE}/admin/attachments/upload`, {
method: 'POST',

View File

@@ -59,9 +59,9 @@
type="text"
id="ossAccessKeyId"
v-model="ossForm.ossAccessKeyId"
required
:required="!currentConfig"
class="admin-input font-mono"
placeholder="OSS Access Key ID"
placeholder="留空则保持原密钥;新建时必填"
>
</div>
@@ -73,9 +73,9 @@
type="password"
id="ossAccessKeySecret"
v-model="ossForm.ossAccessKeySecret"
required
:required="!currentConfig"
class="admin-input font-mono"
placeholder="OSS Access Key Secret"
placeholder="留空则保持原密钥;新建时必填"
>
</div>
@@ -285,6 +285,15 @@
</button>
<div v-else></div>
<div class="flex gap-3">
<button
v-if="currentConfig && currentConfig.id > 0"
type="button"
class="admin-btn-secondary"
:disabled="isTesting || isSubmitting"
@click="testConnection"
>
{{ isTesting ? '测试中...' : '测试连接' }}
</button>
<button type="submit" class="admin-btn-primary" :disabled="isSubmitting">
{{ isSubmitting ? '保存中...' : (currentConfig ? '保存' : '创建') }}
</button>
@@ -297,7 +306,7 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { getOSSConfigs, createOSSConfig, updateOSSConfig, deleteOSSConfig as deleteOSSConfigApi, OSSConfig } from '../../services/api'
import { getOSSConfigs, createOSSConfig, updateOSSConfig, deleteOSSConfig as deleteOSSConfigApi, testOSSConfig, OSSConfig } from '../../services/api'
import { useToast } from '../../composables/useToast'
const toast = useToast()
@@ -314,6 +323,7 @@ const tabs = [
// OSS配置管理
const ossConfigs = ref<OSSConfig[]>([])
const isSubmitting = ref(false)
const isTesting = ref(false)
const ossForm = ref({
id: 0,
@@ -455,23 +465,32 @@ const fetchOSSConfigs = async () => {
}
}
const secretOrKeep = (value: string, isUpdate: boolean) => {
if (value) return value
return isUpdate ? '***' : ''
}
const saveOSSConfig = async () => {
// 设置存储类型
ossForm.value.storageType = activeTab.value
// 验证必填字段
const isUpdate = ossForm.value.id > 0
// 验证必填字段(更新时密钥可留空表示保持原值)
if (activeTab.value === 'aliyun') {
if (!ossForm.value.ossAccessKeyId || !ossForm.value.ossAccessKeySecret || !ossForm.value.ossEndpoint || !ossForm.value.ossBucket) {
if ((!isUpdate && (!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) {
if ((!isUpdate && (!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) {
if ((!isUpdate && (!ossForm.value.qiniuAccessKey || !ossForm.value.qiniuSecretKey)) ||
!ossForm.value.qiniuBucket) {
toast.showToast('请填写所有必填字段', 'error')
return
}
@@ -487,31 +506,29 @@ const saveOSSConfig = async () => {
// 根据存储类型设置对应字段
if (activeTab.value === 'aliyun') {
updateData.ossAccessKeyId = ossForm.value.ossAccessKeyId
updateData.ossAccessKeySecret = ossForm.value.ossAccessKeySecret
updateData.ossAccessKeyId = secretOrKeep(ossForm.value.ossAccessKeyId, isUpdate)
updateData.ossAccessKeySecret = secretOrKeep(ossForm.value.ossAccessKeySecret, isUpdate)
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.qcloudSecretId = secretOrKeep(ossForm.value.qcloudSecretId, isUpdate)
updateData.qcloudSecretKey = secretOrKeep(ossForm.value.qcloudSecretKey, isUpdate)
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.qiniuAccessKey = secretOrKeep(ossForm.value.qiniuAccessKey, isUpdate)
updateData.qiniuSecretKey = secretOrKeep(ossForm.value.qiniuSecretKey, isUpdate)
updateData.qiniuBucket = ossForm.value.qiniuBucket
updateData.qiniuRegion = ossForm.value.qiniuRegion || ''
updateData.qiniuDomain = ossForm.value.qiniuDomain || ''
}
if (ossForm.value.id > 0) {
// 更新
if (isUpdate) {
await updateOSSConfig(ossForm.value.id, updateData)
toast.showToast('OSS配置更新成功', 'success')
} else {
// 创建
await createOSSConfig({
...updateData,
createdAt: '',
@@ -529,6 +546,24 @@ const saveOSSConfig = async () => {
}
}
const testConnection = async () => {
if (!currentConfig.value?.id) {
toast.showToast('请先保存配置再测试连接', 'error')
return
}
isTesting.value = true
try {
await testOSSConfig(currentConfig.value.id)
toast.showToast('连接测试成功', 'success')
} catch (error: any) {
console.error('Error testing OSS config:', error)
toast.showToast(error.message || '连接测试失败', 'error')
} finally {
isTesting.value = false
}
}
const deleteOSSConfig = async () => {
if (!currentConfig.value) return

View File

@@ -549,7 +549,7 @@ const uploadMediaFile = async (file: File): Promise<string | null> => {
const formData = new FormData()
formData.append('file', file)
formData.append('categoryId', '1')
formData.append('storageType', 'local')
formData.append('storageType', 'aliyun')
const response = await authFetch('/api/admin/attachments/upload', {
method: 'POST',

View File

@@ -2001,6 +2001,19 @@ export const deleteOSSConfig = async (id: number): Promise<void> => {
}
}
export const testOSSConfig = async (id: number): Promise<{ storageType: string; name: string }> => {
try {
const response = await authFetch(`${API_BASE}/admin/oss-configs/${id}/test`, {
method: 'POST',
headers: getAuthHeaders()
})
return await parseApiResponse<{ storageType: string; name: string }>(response)
} catch (error) {
console.error('Test OSS config error:', error)
throw error
}
}
// 附件相关类型
export interface Attachment {
id: number