<template>
|
<div class="upload-logo-test">
|
<div class="page-card">
|
<h3 class="card-title">上传Logo到腾讯云COS</h3>
|
|
<!-- 说明信息 -->
|
<el-alert
|
title="功能说明"
|
type="info"
|
:closable="false"
|
show-icon
|
style="margin-bottom: 20px;"
|
>
|
<p>此页面用于将UI目录下的logo.jpg文件上传到腾讯云COS存储桶中。</p>
|
<p>上传成功后,可以在评委管理等功能中使用该logo作为头像。</p>
|
</el-alert>
|
|
<!-- Logo预览 -->
|
<el-card shadow="never" style="margin-bottom: 20px;">
|
<template #header>
|
<span>Logo文件预览</span>
|
</template>
|
<div class="logo-preview">
|
<div class="logo-container">
|
<img
|
src="/UI/logo.jpg"
|
alt="Logo"
|
class="logo-image"
|
@load="onLogoLoad"
|
@error="onLogoError"
|
/>
|
</div>
|
<div class="logo-info">
|
<p><strong>文件名:</strong> logo.jpg</p>
|
<p><strong>路径:</strong> UI/logo.jpg</p>
|
<p><strong>状态:</strong>
|
<el-tag :type="logoStatus === 'loaded' ? 'success' : 'danger'">
|
{{ logoStatus === 'loaded' ? '文件正常' : '文件加载失败' }}
|
</el-tag>
|
</p>
|
</div>
|
</div>
|
</el-card>
|
|
<!-- 上传操作 -->
|
<el-card shadow="never" style="margin-bottom: 20px;">
|
<template #header>
|
<span>上传操作</span>
|
</template>
|
|
<div class="upload-section">
|
<!-- 自动上传 -->
|
<div class="upload-method">
|
<h4>方式一:自动上传</h4>
|
<p>尝试自动获取UI目录下的logo.jpg文件并上传</p>
|
<el-button
|
type="primary"
|
:loading="autoUploading"
|
@click="autoUploadLogo"
|
:disabled="logoStatus !== 'loaded'"
|
>
|
<el-icon><Upload /></el-icon>
|
{{ autoUploading ? '上传中...' : '自动上传Logo' }}
|
</el-button>
|
</div>
|
|
<el-divider />
|
|
<!-- 手动选择上传 -->
|
<div class="upload-method">
|
<h4>方式二:手动选择</h4>
|
<p>如果自动上传失败,可以手动选择logo文件上传</p>
|
<div class="manual-upload">
|
<el-upload
|
ref="uploadRef"
|
:auto-upload="false"
|
:show-file-list="false"
|
:on-change="handleFileSelect"
|
accept="image/*"
|
>
|
<el-button type="success">
|
<el-icon><FolderOpened /></el-icon>
|
选择Logo文件
|
</el-button>
|
</el-upload>
|
|
<div v-if="selectedFile" class="selected-file">
|
<div class="file-info">
|
<el-icon><Picture /></el-icon>
|
<span>{{ selectedFile.name }}</span>
|
<span class="file-size">({{ formatFileSize(selectedFile.size) }})</span>
|
</div>
|
<el-button
|
type="primary"
|
:loading="manualUploading"
|
@click="manualUploadLogo"
|
>
|
{{ manualUploading ? '上传中...' : '上传' }}
|
</el-button>
|
</div>
|
</div>
|
</div>
|
</div>
|
</el-card>
|
|
<!-- 上传进度 -->
|
<el-card v-if="uploadProgress.show" shadow="never" style="margin-bottom: 20px;">
|
<template #header>
|
<span>上传进度</span>
|
</template>
|
<div class="progress-section">
|
<div class="progress-info">
|
<span>{{ uploadProgress.fileName }}</span>
|
<span>{{ uploadProgress.status }}</span>
|
</div>
|
<el-progress
|
:percentage="uploadProgress.percent"
|
:status="uploadProgress.type"
|
/>
|
</div>
|
</el-card>
|
|
<!-- 上传结果 -->
|
<el-card v-if="uploadResult.show" shadow="never">
|
<template #header>
|
<span>上传结果</span>
|
</template>
|
<div class="result-section">
|
<el-alert
|
:title="uploadResult.success ? '上传成功!' : '上传失败!'"
|
:type="uploadResult.success ? 'success' : 'error'"
|
:closable="false"
|
show-icon
|
>
|
<div v-if="uploadResult.success">
|
<p><strong>Logo已成功上传到腾讯云COS!</strong></p>
|
<p><strong>访问URL:</strong></p>
|
<el-input
|
v-model="uploadResult.url"
|
readonly
|
style="margin: 8px 0;"
|
>
|
<template #append>
|
<el-button @click="copyUrl">复制</el-button>
|
</template>
|
</el-input>
|
<div class="result-actions">
|
<el-button type="primary" @click="previewLogo">预览Logo</el-button>
|
<el-button type="success" @click="useInJudge">在评委管理中使用</el-button>
|
<el-button @click="saveToDatabase">保存到数据库</el-button>
|
</div>
|
</div>
|
<div v-else>
|
<p><strong>错误信息:</strong> {{ uploadResult.error }}</p>
|
<p><strong>解决建议:</strong></p>
|
<ul>
|
<li>检查COS配置信息是否正确</li>
|
<li>确认存储桶权限设置</li>
|
<li>检查网络连接</li>
|
<li>尝试使用手动选择方式上传</li>
|
</ul>
|
</div>
|
</el-alert>
|
</div>
|
</el-card>
|
|
<!-- 使用说明 -->
|
<el-card shadow="never" style="margin-top: 20px;">
|
<template #header>
|
<span>使用说明</span>
|
</template>
|
<div class="instructions">
|
<h4>上传前准备:</h4>
|
<ol>
|
<li>确保已在 <code>web/src/utils/cos-config.ts</code> 中配置正确的COS信息</li>
|
<li>确认存储桶具有写入权限</li>
|
<li>确认logo.jpg文件存在于UI目录下</li>
|
</ol>
|
|
<h4>上传后用途:</h4>
|
<ul>
|
<li>作为评委头像的默认图片</li>
|
<li>在系统各个模块中作为品牌标识</li>
|
<li>可以在数据库中记录该图片的URL供后续使用</li>
|
</ul>
|
|
<h4>注意事项:</h4>
|
<ul>
|
<li>上传的文件将存储在COS的 <code>avatars/</code> 目录下</li>
|
<li>文件名会自动添加时间戳以避免重复</li>
|
<li>上传成功后请妥善保存返回的URL</li>
|
</ul>
|
</div>
|
</el-card>
|
</div>
|
</div>
|
</template>
|
|
<script setup lang="ts">
|
import { ref, reactive } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import { uploadToCOS } from '@/utils/cos'
|
import { useRouter } from 'vue-router'
|
|
const router = useRouter()
|
|
// 组件状态
|
const logoStatus = ref<'loading' | 'loaded' | 'error'>('loading')
|
const autoUploading = ref(false)
|
const manualUploading = ref(false)
|
const selectedFile = ref<File | null>(null)
|
|
// 上传进度
|
const uploadProgress = reactive({
|
show: false,
|
fileName: '',
|
percent: 0,
|
status: '',
|
type: undefined as 'success' | 'exception' | undefined
|
})
|
|
// 上传结果
|
const uploadResult = reactive({
|
show: false,
|
success: false,
|
url: '',
|
error: ''
|
})
|
|
// Logo加载成功
|
const onLogoLoad = () => {
|
logoStatus.value = 'loaded'
|
}
|
|
// Logo加载失败
|
const onLogoError = () => {
|
logoStatus.value = 'error'
|
}
|
|
// 自动上传Logo
|
const autoUploadLogo = async () => {
|
autoUploading.value = true
|
uploadProgress.show = true
|
uploadProgress.fileName = 'logo.jpg'
|
uploadProgress.percent = 0
|
uploadProgress.status = '获取文件中...'
|
uploadResult.show = false
|
|
try {
|
// 尝试获取logo文件
|
const logoFile = await fetchLogoFile()
|
|
if (!logoFile) {
|
throw new Error('无法获取logo文件,请尝试手动选择上传')
|
}
|
|
uploadProgress.status = '开始上传...'
|
|
// 模拟进度更新
|
const progressInterval = setInterval(() => {
|
if (uploadProgress.percent < 90) {
|
uploadProgress.percent += Math.random() * 20
|
uploadProgress.status = `上传中... ${Math.round(uploadProgress.percent)}%`
|
}
|
}, 200)
|
|
// 执行上传
|
const url = await uploadToCOS(logoFile, 'avatars/')
|
|
// 清除进度定时器
|
clearInterval(progressInterval)
|
|
// 更新进度为完成
|
uploadProgress.percent = 100
|
uploadProgress.status = '上传完成'
|
uploadProgress.type = 'success'
|
|
// 显示结果
|
uploadResult.show = true
|
uploadResult.success = true
|
uploadResult.url = url
|
|
ElMessage.success('Logo上传成功!')
|
|
} catch (error: any) {
|
console.error('自动上传失败:', error)
|
|
uploadProgress.percent = 0
|
uploadProgress.status = '上传失败'
|
uploadProgress.type = 'exception'
|
|
uploadResult.show = true
|
uploadResult.success = false
|
uploadResult.error = error.message || '自动上传失败'
|
|
ElMessage.error('自动上传失败,请尝试手动选择上传')
|
} finally {
|
autoUploading.value = false
|
}
|
}
|
|
// 获取logo文件
|
const fetchLogoFile = async (): Promise<File | null> => {
|
try {
|
// 尝试从UI目录获取
|
const response = await fetch('/UI/logo.jpg')
|
if (response.ok) {
|
const blob = await response.blob()
|
return new File([blob], 'logo.jpg', { type: 'image/jpeg' })
|
}
|
} catch (error) {
|
console.log('从UI目录获取logo失败')
|
}
|
|
return null
|
}
|
|
// 手动文件选择
|
const handleFileSelect = (file: any) => {
|
const newFile = file.raw as File
|
|
// 检查文件类型
|
if (!newFile.type.startsWith('image/')) {
|
ElMessage.error('请选择图片文件')
|
return
|
}
|
|
selectedFile.value = newFile
|
uploadResult.show = false
|
}
|
|
// 手动上传Logo
|
const manualUploadLogo = async () => {
|
if (!selectedFile.value) {
|
ElMessage.error('请先选择文件')
|
return
|
}
|
|
manualUploading.value = true
|
uploadProgress.show = true
|
uploadProgress.fileName = selectedFile.value.name
|
uploadProgress.percent = 0
|
uploadProgress.status = '开始上传...'
|
uploadResult.show = false
|
|
try {
|
// 模拟进度更新
|
const progressInterval = setInterval(() => {
|
if (uploadProgress.percent < 90) {
|
uploadProgress.percent += Math.random() * 20
|
uploadProgress.status = `上传中... ${Math.round(uploadProgress.percent)}%`
|
}
|
}, 200)
|
|
// 执行上传
|
const url = await uploadToCOS(selectedFile.value, 'avatars/')
|
|
// 清除进度定时器
|
clearInterval(progressInterval)
|
|
// 更新进度为完成
|
uploadProgress.percent = 100
|
uploadProgress.status = '上传完成'
|
uploadProgress.type = 'success'
|
|
// 显示结果
|
uploadResult.show = true
|
uploadResult.success = true
|
uploadResult.url = url
|
|
ElMessage.success('Logo上传成功!')
|
|
} catch (error: any) {
|
console.error('手动上传失败:', error)
|
|
uploadProgress.percent = 0
|
uploadProgress.status = '上传失败'
|
uploadProgress.type = 'exception'
|
|
uploadResult.show = true
|
uploadResult.success = false
|
uploadResult.error = error.message || '手动上传失败'
|
|
ElMessage.error('手动上传失败')
|
} finally {
|
manualUploading.value = false
|
}
|
}
|
|
// 复制URL
|
const copyUrl = async () => {
|
try {
|
await navigator.clipboard.writeText(uploadResult.url)
|
ElMessage.success('URL已复制到剪贴板')
|
} catch (error) {
|
ElMessage.error('复制失败')
|
}
|
}
|
|
// 预览Logo
|
const previewLogo = () => {
|
window.open(uploadResult.url, '_blank')
|
}
|
|
// 在评委管理中使用
|
const useInJudge = () => {
|
router.push('/judge')
|
ElMessage.info('已跳转到评委管理页面,可以使用上传的Logo作为头像')
|
}
|
|
// 保存到数据库
|
const saveToDatabase = async () => {
|
try {
|
// 这里应该调用后端API保存媒体信息到数据库
|
// 示例:await MediaApi.saveMedia({
|
// name: 'logo.jpg',
|
// path: uploadResult.url,
|
// targetType: 'system_logo',
|
// targetId: 0
|
// })
|
|
ElMessage.success('Logo信息已保存到数据库(模拟)')
|
} catch (error) {
|
ElMessage.error('保存到数据库失败')
|
}
|
}
|
|
// 格式化文件大小
|
const formatFileSize = (bytes: number): string => {
|
if (bytes === 0) return '0 B'
|
const k = 1024
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.upload-logo-test {
|
.card-title {
|
margin-bottom: 20px;
|
color: #303133;
|
font-size: 18px;
|
font-weight: 600;
|
}
|
|
.logo-preview {
|
display: flex;
|
gap: 20px;
|
align-items: center;
|
|
.logo-container {
|
flex-shrink: 0;
|
|
.logo-image {
|
width: 120px;
|
height: 120px;
|
object-fit: contain;
|
border: 2px solid #e4e7ed;
|
border-radius: 8px;
|
background-color: #f5f7fa;
|
}
|
}
|
|
.logo-info {
|
flex: 1;
|
|
p {
|
margin: 8px 0;
|
|
strong {
|
color: #606266;
|
margin-right: 8px;
|
}
|
}
|
}
|
}
|
|
.upload-section {
|
.upload-method {
|
margin-bottom: 20px;
|
|
h4 {
|
margin-bottom: 8px;
|
color: #303133;
|
}
|
|
p {
|
margin-bottom: 16px;
|
color: #606266;
|
font-size: 14px;
|
}
|
}
|
|
.manual-upload {
|
display: flex;
|
align-items: center;
|
gap: 16px;
|
|
.selected-file {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
padding: 8px 12px;
|
border: 1px solid #e4e7ed;
|
border-radius: 4px;
|
background-color: #f5f7fa;
|
|
.file-info {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
|
.file-size {
|
color: #909399;
|
font-size: 12px;
|
}
|
}
|
}
|
}
|
}
|
|
.progress-section {
|
.progress-info {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 8px;
|
font-size: 14px;
|
color: #606266;
|
}
|
}
|
|
.result-section {
|
.result-actions {
|
margin-top: 16px;
|
display: flex;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
}
|
|
.instructions {
|
h4 {
|
margin: 16px 0 8px 0;
|
color: #303133;
|
|
&:first-child {
|
margin-top: 0;
|
}
|
}
|
|
ol, ul {
|
margin: 0;
|
padding-left: 20px;
|
|
li {
|
margin-bottom: 4px;
|
line-height: 1.5;
|
color: #606266;
|
}
|
}
|
|
code {
|
background-color: #f5f7fa;
|
padding: 2px 4px;
|
border-radius: 2px;
|
font-family: 'Courier New', monospace;
|
color: #e6a23c;
|
}
|
}
|
}
|
</style>
|