feat: extend SMS signature and setting file support
Build and Deploy Vue3 / build (push) Successful in 1m25s
Build and Deploy Vue3 / deploy (push) Successful in 45s

This commit is contained in:
shiran
2026-07-14 18:17:05 +08:00
parent 2e3574a182
commit fa3bdcbc7e
3 changed files with 375 additions and 50 deletions
+76 -21
View File
@@ -1,7 +1,7 @@
<template>
<el-dialog
v-model="visible"
title="选择图片"
:title="props.acceptAll ? '选择文件' : '选择图片'"
width="900px"
append-to-body
@close="handleClose"
@@ -12,13 +12,13 @@
<el-tab-pane label="文件库" name="fileLibrary">
<div class="file-list-container">
<div class="file-list-header">
<h4>图片文件库</h4>
<h4>{{ props.acceptAll ? '文件库' : '图片文件库' }}</h4>
<div class="header-actions">
<span v-if="props.multiple && selectedIds.size > 0" class="selected-count">
已选 {{ selectedIds.size }} 个文件
</span>
<el-button type="primary" @click="switchToUpload" :icon="Upload">
上传新图片
{{ props.acceptAll ? '上传新文件' : '上传新图片' }}
</el-button>
</div>
</div>
@@ -52,10 +52,15 @@
</div>
<div class="file-preview">
<img
v-if="isImageFile(file.realName, file.url)"
:src="processImageUrl(file.url)"
:alt="file.realName"
@error="handleImageError"
/>
<div v-else class="file-type-icon">
<el-icon :size="32"><Document /></el-icon>
<span class="file-ext">{{ getFileExt(file.realName) }}</span>
</div>
</div>
<div class="file-info">
<p class="file-name" :title="file.realName">{{ file.realName }}</p>
@@ -63,7 +68,7 @@
</div>
</div>
</div>
<el-empty v-if="filteredFileList.length === 0 && !loading" description="暂无图片文件" />
<el-empty v-if="filteredFileList.length === 0 && !loading" :description="props.acceptAll ? '暂无文件' : '暂无图片文件'" />
<!-- 分页 -->
<div class="pagination-container" v-if="total > 0">
@@ -81,14 +86,14 @@
</div>
</el-tab-pane>
<!-- 上传图片 -->
<el-tab-pane label="上传图片" name="upload">
<!-- 上传文件 -->
<el-tab-pane :label="props.acceptAll ? '上传文件' : '上传图片'" name="upload">
<div class="upload-section">
<el-upload
:auto-upload="false"
:show-file-list="false"
:on-change="handleFileChange"
accept="image/*"
:accept="props.acceptAll ? undefined : 'image/*'"
multiple
drag
>
@@ -98,7 +103,7 @@
</div>
<template #tip>
<div class="el-upload__tip">
支持jpgpnggifwebp等图片格式单个文件不超过5MB
{{ props.acceptAll ? '支持所有文件类型,单个文件不超过50MB' : '支持jpg、png、gif、webp等图片格式单个文件不超过5MB' }}
</div>
</template>
</el-upload>
@@ -111,7 +116,10 @@
</div>
<div class="pending-list">
<div v-for="(file, index) in pendingFiles" :key="index" class="pending-item">
<img :src="file.previewUrl" class="pending-preview" />
<img v-if="file.isImage" :src="file.previewUrl" class="pending-preview" />
<div v-else class="pending-preview pending-file-icon">
<el-icon :size="20"><Document /></el-icon>
</div>
<span class="pending-name" :title="file.name">{{ file.name }}</span>
<span class="pending-size">{{ formatFileSize(file.size) }}</span>
<el-button type="danger" link size="small" @click="removePendingFile(index)">移除</el-button>
@@ -149,7 +157,7 @@
<script setup>
import { ref, watch, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Upload, UploadFilled, Search, Select, Delete } from '@element-plus/icons-vue'
import { Upload, UploadFilled, Search, Select, Delete, Document } from '@element-plus/icons-vue'
import { getFileList, getFileDetail, uploadFile } from '@/api/admin/file'
// Props
@@ -165,6 +173,10 @@ const props = defineProps({
multiple: {
type: Boolean,
default: false
},
acceptAll: {
type: Boolean,
default: false
}
})
@@ -349,22 +361,37 @@ const selectFile = (file) => {
}
}
// 判断是否为图片文件
const isImageFile = (name, url) => {
const imgExts = /\.(jpe?g|png|gif|webp|bmp|svg|ico|avif)$/i
if (name && imgExts.test(name)) return true
if (url && imgExts.test(url.split('?')[0])) return true
return false
}
const getFileExt = (name) => {
if (!name) return ''
const dotIndex = name.lastIndexOf('.')
if (dotIndex === -1) return ''
return name.slice(dotIndex + 1).toUpperCase()
}
// 文件选择变化(收集待上传文件)
const handleFileChange = (file) => {
const rawFile = file.raw
if (!rawFile) return
// 验证文件类型
const isImage = rawFile.type.startsWith('image/')
if (!isImage) {
ElMessage.error(`${rawFile.name} 不是图片文件,已跳过`)
return
if (!props.acceptAll) {
const isImage = rawFile.type.startsWith('image/')
if (!isImage) {
ElMessage.error(`${rawFile.name} 不是图片文件,已跳过`)
return
}
}
// 验证文件大小
const isLt5M = rawFile.size / 1024 / 1024 < 5
if (!isLt5M) {
ElMessage.error(`${rawFile.name} 超过 5MB,已跳过`)
const sizeLimit = props.acceptAll ? 50 : 5
if (rawFile.size / 1024 / 1024 > sizeLimit) {
ElMessage.error(`${rawFile.name} 超过 ${sizeLimit}MB,已跳过`)
return
}
@@ -372,12 +399,13 @@ const handleFileChange = (file) => {
const exists = pendingFiles.value.some(f => f.name === rawFile.name && f.size === rawFile.size)
if (exists) return
// 添加到待上传列表,生成本地预览URL
const fileIsImage = rawFile.type.startsWith('image/')
pendingFiles.value.push({
raw: rawFile,
name: rawFile.name,
size: rawFile.size,
previewUrl: URL.createObjectURL(rawFile)
isImage: fileIsImage,
previewUrl: fileIsImage ? URL.createObjectURL(rawFile) : ''
})
}
@@ -593,6 +621,25 @@ const handleConfirm = () => {
object-fit: cover;
}
.file-type-icon {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
color: #909399;
}
.file-ext {
font-size: 10px;
font-weight: 600;
color: #606266;
max-width: 60px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-info {
text-align: center;
}
@@ -670,6 +717,14 @@ const handleConfirm = () => {
border: 1px solid #ebeef5;
}
.pending-file-icon {
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f7fa;
color: #909399;
}
.pending-name {
flex: 1;
font-size: 13px;