更新
This commit is contained in:
13
src/api/system.js
Normal file
13
src/api/system.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import request from '../utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
* @returns {Promise} 菜单数据
|
||||||
|
*/
|
||||||
|
export function upload(params) {
|
||||||
|
return request({
|
||||||
|
url: '/system/file/upload',
|
||||||
|
method: 'post',
|
||||||
|
data: params
|
||||||
|
})
|
||||||
|
}
|
||||||
186
src/components/ImageUpload/file.vue
Normal file
186
src/components/ImageUpload/file.vue
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<div class="file-upload">
|
||||||
|
<a-upload
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
:custom-request="customUpload"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:accept="accept"
|
||||||
|
:max-count="maxCount"
|
||||||
|
:disabled="disabled"
|
||||||
|
:multiple="multiple"
|
||||||
|
@change="handleChange"
|
||||||
|
@remove="handleRemove"
|
||||||
|
>
|
||||||
|
<a-button v-if="!disabled">
|
||||||
|
<upload-outlined />
|
||||||
|
上传文件
|
||||||
|
</a-button>
|
||||||
|
</a-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { UploadOutlined } from '@ant-design/icons-vue'
|
||||||
|
import uploadConfig from '@/config/upload'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 文件列表
|
||||||
|
modelValue: {
|
||||||
|
type: [Array, String],
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 最大上传数量,默认1为单文件上传
|
||||||
|
maxCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
// 接受的文件类型,例如 '.pdf,.doc,.docx' 或 '*'
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: '*'
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否支持多选
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否返回URL字符串(单文件)或URL数组(多文件)
|
||||||
|
returnUrl: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change', 'remove'])
|
||||||
|
|
||||||
|
// 文件列表
|
||||||
|
const fileList = ref([])
|
||||||
|
|
||||||
|
// 初始化文件列表
|
||||||
|
const initFileList = () => {
|
||||||
|
if (props.modelValue) {
|
||||||
|
if (typeof props.modelValue === 'string') {
|
||||||
|
// 单文件上传,字符串格式
|
||||||
|
fileList.value = props.modelValue
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
uid: '-1',
|
||||||
|
name: 'file',
|
||||||
|
status: 'done',
|
||||||
|
url: props.modelValue,
|
||||||
|
response: {
|
||||||
|
src: props.modelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
} else if (Array.isArray(props.modelValue)) {
|
||||||
|
// 多文件上传,数组格式
|
||||||
|
fileList.value = props.modelValue.map((url, index) => ({
|
||||||
|
uid: `-${index}`,
|
||||||
|
name: `file${index}`,
|
||||||
|
status: 'done',
|
||||||
|
url: url,
|
||||||
|
response: {
|
||||||
|
src: url
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听外部值变化
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
() => {
|
||||||
|
initFileList()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自定义上传
|
||||||
|
const customUpload = (options) => {
|
||||||
|
const { file, onProgress, onSuccess, onError } = options
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(uploadConfig.filename || 'file', file)
|
||||||
|
|
||||||
|
// 使用文件上传API对象
|
||||||
|
const apiObj = uploadConfig.apiObjFile || uploadConfig.apiObj
|
||||||
|
|
||||||
|
apiObj(formData, {
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
||||||
|
onProgress({ percent }, file)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const data = uploadConfig.parseData(res)
|
||||||
|
if (data.code === uploadConfig.successCode) {
|
||||||
|
onSuccess(data, file)
|
||||||
|
message.success('上传成功')
|
||||||
|
} else {
|
||||||
|
onError(new Error(data.msg || '上传失败'))
|
||||||
|
message.error(data.msg || '上传失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
onError(error)
|
||||||
|
message.error('上传失败:' + error.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传前校验
|
||||||
|
const beforeUpload = (file) => {
|
||||||
|
const maxSizeMB = uploadConfig.maxSizeFile || uploadConfig.maxSize || 10
|
||||||
|
const maxSizeBytes = maxSizeMB * 1024 * 1024
|
||||||
|
|
||||||
|
if (file.size > maxSizeBytes) {
|
||||||
|
message.error(`文件大小不能超过 ${maxSizeMB}MB`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件列表变化
|
||||||
|
const handleChange = ({ fileList: newFileList }) => {
|
||||||
|
fileList.value = newFileList
|
||||||
|
|
||||||
|
// 提取成功的文件URL
|
||||||
|
const successFiles = newFileList
|
||||||
|
.filter((file) => file.status === 'done' && (file.url || file.response?.src))
|
||||||
|
.map((file) => file.url || file.response?.src)
|
||||||
|
|
||||||
|
// 触发更新事件
|
||||||
|
if (props.returnUrl) {
|
||||||
|
// 返回URL字符串或数组
|
||||||
|
const value = props.maxCount === 1 ? successFiles[0] || '' : successFiles
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
emit('change', value, newFileList)
|
||||||
|
} else {
|
||||||
|
// 返回完整文件列表
|
||||||
|
emit('update:modelValue', newFileList)
|
||||||
|
emit('change', newFileList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件移除
|
||||||
|
const handleRemove = (file) => {
|
||||||
|
emit('remove', file)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-upload {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
383
src/components/ImageUpload/index.vue
Normal file
383
src/components/ImageUpload/index.vue
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
<template>
|
||||||
|
<div class="image-upload">
|
||||||
|
<a-upload
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
list-type="picture-card"
|
||||||
|
:custom-request="customUpload"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:accept="accept"
|
||||||
|
:max-count="maxCount"
|
||||||
|
:disabled="disabled"
|
||||||
|
:show-upload-list="{ showPreviewIcon: true, showRemoveIcon: !disabled }"
|
||||||
|
@preview="handlePreview"
|
||||||
|
@change="handleChange"
|
||||||
|
@drop="handleDrop"
|
||||||
|
@dragenter="handleDragEnter"
|
||||||
|
@dragleave="handleDragLeave"
|
||||||
|
class="custom-upload"
|
||||||
|
:class="{ 'drag-over': isDragOver }"
|
||||||
|
>
|
||||||
|
<div v-if="fileList.length < maxCount && !disabled" class="upload-area">
|
||||||
|
<loading-outlined v-if="uploading" class="upload-icon" />
|
||||||
|
<plus-outlined v-else class="upload-icon" />
|
||||||
|
<div class="ant-upload-text">{{ uploading ? '上传中...' : uploadText }}</div>
|
||||||
|
<div v-if="tip" class="ant-upload-tip">{{ tip }}</div>
|
||||||
|
</div>
|
||||||
|
</a-upload>
|
||||||
|
<a-modal
|
||||||
|
:open="previewVisible"
|
||||||
|
:title="previewTitle"
|
||||||
|
:footer="null"
|
||||||
|
:width="800"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<img alt="图片预览" style="width: 100%; max-height: 600px; object-fit: contain;" :src="previewImage" />
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, computed } from 'vue'
|
||||||
|
import { message, Modal } from 'ant-design-vue'
|
||||||
|
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'
|
||||||
|
import uploadConfig from '@/config/upload'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 图片列表
|
||||||
|
modelValue: {
|
||||||
|
type: [Array, String],
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 最大上传数量,默认1为单图上传
|
||||||
|
maxCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
// 接受的文件类型
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: 'image/*'
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否返回URL字符串(单图)或URL数组(多图)
|
||||||
|
returnUrl: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 上传按钮文字
|
||||||
|
uploadText: {
|
||||||
|
type: String,
|
||||||
|
default: '上传图片'
|
||||||
|
},
|
||||||
|
// 提示文字
|
||||||
|
tip: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 最小宽度(像素)
|
||||||
|
minWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 最大宽度(像素)
|
||||||
|
maxWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 最小高度(像素)
|
||||||
|
minHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 最大高度(像素)
|
||||||
|
maxHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 是否删除前确认
|
||||||
|
confirmBeforeRemove: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 自定义上传按钮内容
|
||||||
|
customUploadBtn: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change', 'preview', 'remove', 'uploadSuccess', 'uploadError'])
|
||||||
|
|
||||||
|
// 文件列表
|
||||||
|
const fileList = ref([])
|
||||||
|
|
||||||
|
// 预览相关
|
||||||
|
const previewVisible = ref(false)
|
||||||
|
const previewImage = ref('')
|
||||||
|
const previewTitle = computed(() => {
|
||||||
|
return previewImage.value ? '图片预览' : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 上传状态
|
||||||
|
const uploading = ref(false)
|
||||||
|
|
||||||
|
// 拖拽状态
|
||||||
|
const isDragOver = ref(false)
|
||||||
|
|
||||||
|
// 初始化文件列表
|
||||||
|
const initFileList = () => {
|
||||||
|
if (props.modelValue) {
|
||||||
|
if (typeof props.modelValue === 'string') {
|
||||||
|
// 单图上传,字符串格式
|
||||||
|
fileList.value = props.modelValue
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
uid: '-1',
|
||||||
|
name: 'image.png',
|
||||||
|
status: 'done',
|
||||||
|
url: props.modelValue
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
} else if (Array.isArray(props.modelValue)) {
|
||||||
|
// 多图上传,数组格式
|
||||||
|
fileList.value = props.modelValue.map((url, index) => ({
|
||||||
|
uid: `-${index}`,
|
||||||
|
name: `image${index}.png`,
|
||||||
|
status: 'done',
|
||||||
|
url: url
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听外部值变化
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
() => {
|
||||||
|
initFileList()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自定义上传
|
||||||
|
const customUpload = (options) => {
|
||||||
|
const { file, onProgress, onSuccess, onError } = options
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(uploadConfig.filename || 'file', file)
|
||||||
|
|
||||||
|
uploading.value = true
|
||||||
|
|
||||||
|
uploadConfig.apiObj(formData, {
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
||||||
|
onProgress({ percent }, file)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const data = uploadConfig.parseData(res)
|
||||||
|
if (data.code === uploadConfig.successCode) {
|
||||||
|
onSuccess(data, file)
|
||||||
|
message.success('上传成功')
|
||||||
|
emit('uploadSuccess', data, file)
|
||||||
|
} else {
|
||||||
|
onError(new Error(data.msg || '上传失败'))
|
||||||
|
message.error(data.msg || '上传失败')
|
||||||
|
emit('uploadError', data.msg || '上传失败', file)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
onError(error)
|
||||||
|
message.error('上传失败:' + error.message)
|
||||||
|
emit('uploadError', error.message, file)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uploading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传前校验
|
||||||
|
const beforeUpload = async (file) => {
|
||||||
|
// 文件大小校验
|
||||||
|
const maxSizeMB = uploadConfig.maxSize || 10
|
||||||
|
const maxSizeBytes = maxSizeMB * 1024 * 1024
|
||||||
|
|
||||||
|
if (file.size > maxSizeBytes) {
|
||||||
|
message.error(`图片大小不能超过 ${maxSizeMB}MB`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片尺寸校验
|
||||||
|
if (props.minWidth || props.maxWidth || props.minHeight || props.maxHeight) {
|
||||||
|
try {
|
||||||
|
const dimensions = await getImageDimensions(file)
|
||||||
|
const { width, height } = dimensions
|
||||||
|
|
||||||
|
if (props.minWidth && width < props.minWidth) {
|
||||||
|
message.error(`图片宽度不能小于 ${props.minWidth}px`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (props.maxWidth && width > props.maxWidth) {
|
||||||
|
message.error(`图片宽度不能大于 ${props.maxWidth}px`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (props.minHeight && height < props.minHeight) {
|
||||||
|
message.error(`图片高度不能小于 ${props.minHeight}px`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (props.maxHeight && height > props.maxHeight) {
|
||||||
|
message.error(`图片高度不能大于 ${props.maxHeight}px`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error('图片尺寸校验失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片尺寸
|
||||||
|
const getImageDimensions = (file) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image()
|
||||||
|
const reader = new FileReader()
|
||||||
|
|
||||||
|
reader.onload = (e) => {
|
||||||
|
img.src = e.target.result
|
||||||
|
img.onload = () => {
|
||||||
|
resolve({ width: img.width, height: img.height })
|
||||||
|
}
|
||||||
|
img.onerror = reject
|
||||||
|
}
|
||||||
|
reader.onerror = reject
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理预览
|
||||||
|
const handlePreview = async (file) => {
|
||||||
|
if (!file.url && !file.preview) {
|
||||||
|
file.preview = await getBase64(file.originFileObj)
|
||||||
|
}
|
||||||
|
previewImage.value = file.url || file.preview
|
||||||
|
previewVisible.value = true
|
||||||
|
emit('preview', file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Base64
|
||||||
|
const getBase64 = (file) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
reader.onload = () => resolve(reader.result)
|
||||||
|
reader.onerror = (error) => reject(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件列表变化
|
||||||
|
const handleChange = ({ fileList: newFileList }) => {
|
||||||
|
// 更新文件列表,确保上传成功的文件有正确的 url
|
||||||
|
const updatedFileList = newFileList.map((file) => {
|
||||||
|
// 如果文件上传成功且有响应数据但没有 url,则设置 url
|
||||||
|
if (file.status === 'done' && file.response?.src && !file.url) {
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
url: file.response.src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
})
|
||||||
|
|
||||||
|
fileList.value = updatedFileList
|
||||||
|
|
||||||
|
// 过滤掉失败的文件
|
||||||
|
const validFileList = updatedFileList.filter((file) => file.status !== 'error')
|
||||||
|
|
||||||
|
// 提取成功的文件URL
|
||||||
|
const successFiles = validFileList
|
||||||
|
.filter((file) => file.status === 'done' && (file.url || file.response?.src))
|
||||||
|
.map((file) => file.url || file.response?.src)
|
||||||
|
|
||||||
|
// 触发更新事件
|
||||||
|
if (props.returnUrl) {
|
||||||
|
// 返回URL字符串或数组
|
||||||
|
const value = props.maxCount === 1 ? successFiles[0] || '' : successFiles
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
emit('change', value, validFileList)
|
||||||
|
} else {
|
||||||
|
// 返回完整文件列表
|
||||||
|
emit('update:modelValue', validFileList)
|
||||||
|
emit('change', validFileList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽相关
|
||||||
|
const handleDragEnter = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
isDragOver.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragLeave = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
isDragOver.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDrop = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
isDragOver.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消预览
|
||||||
|
const handleCancel = () => {
|
||||||
|
previewVisible.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.image-upload {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-text {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-tip {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-over {
|
||||||
|
border: 2px dashed #1890ff;
|
||||||
|
background-color: rgba(24, 144, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-over .upload-icon {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
335
src/components/README.md
Normal file
335
src/components/README.md
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
# 上传组件使用说明
|
||||||
|
|
||||||
|
本项目提供了两个基于 Ant Design Vue 的二次封装上传组件:
|
||||||
|
|
||||||
|
1. **ImageUpload** - 图片上传组件
|
||||||
|
2. **FileUpload** - 文件上传组件
|
||||||
|
|
||||||
|
## ImageUpload 图片上传组件
|
||||||
|
|
||||||
|
### 功能特性
|
||||||
|
- 支持单图和多图上传
|
||||||
|
- 图片预览功能(支持大图预览)
|
||||||
|
- 支持拖拽上传(带拖拽视觉反馈)
|
||||||
|
- 文件大小限制(从配置文件读取)
|
||||||
|
- 图片尺寸限制(支持最小/最大宽高限制)
|
||||||
|
- 上传进度显示和上传状态反馈
|
||||||
|
- 错误文件自动过滤
|
||||||
|
- 上传成功/失败事件回调
|
||||||
|
- 自定义上传按钮文字和提示信息
|
||||||
|
- 禁用状态下隐藏删除图标
|
||||||
|
- 自动上传到服务器
|
||||||
|
- 支持 v-model 双向绑定
|
||||||
|
|
||||||
|
### Props 参数
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
|------|------|------|--------|
|
||||||
|
| v-model / modelValue | 图片列表(单图传字符串,多图传数组) | String / Array | [] |
|
||||||
|
| maxCount | 最大上传数量 | Number | 1 |
|
||||||
|
| accept | 接受的文件类型 | String | 'image/*' |
|
||||||
|
| disabled | 是否禁用 | Boolean | false |
|
||||||
|
| returnUrl | 是否返回URL(true)或完整文件列表(false) | Boolean | true |
|
||||||
|
| uploadText | 上传按钮文字 | String | '上传图片' |
|
||||||
|
| tip | 提示文字(显示在上传按钮下方) | String | '' |
|
||||||
|
| minWidth | 图片最小宽度(像素),0表示不限制 | Number | 0 |
|
||||||
|
| maxWidth | 图片最大宽度(像素),0表示不限制 | Number | 0 |
|
||||||
|
| minHeight | 图片最小高度(像素),0表示不限制 | Number | 0 |
|
||||||
|
| maxHeight | 图片最大高度(像素),0表示不限制 | Number | 0 |
|
||||||
|
| confirmBeforeRemove | 删除前是否确认 | Boolean | false |
|
||||||
|
| customUploadBtn | 自定义上传按钮内容(函数返回VNode) | Function | null |
|
||||||
|
|
||||||
|
### Events 事件
|
||||||
|
|
||||||
|
| 事件名 | 说明 | 回调参数 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| update:modelValue | 值变化时触发 | value: 图片URL或URL数组 |
|
||||||
|
| change | 文件列表变化时触发 | value: 图片URL或URL数组, fileList: 完整文件列表 |
|
||||||
|
| preview | 预览图片时触发 | file: 当前预览的文件对象 |
|
||||||
|
| uploadSuccess | 上传成功时触发 | data: 响应数据, file: 上传的文件 |
|
||||||
|
| uploadError | 上传失败时触发 | errorMsg: 错误信息, file: 上传的文件 |
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
|
||||||
|
#### 单图上传
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ImageUpload v-model="imageUrl" />
|
||||||
|
<p>图片URL: {{ imageUrl }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ImageUpload from '@/components/ImageUpload/index.vue'
|
||||||
|
|
||||||
|
const imageUrl = ref('')
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 多图上传
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="imageList"
|
||||||
|
:max-count="5"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ImageUpload from '@/components/ImageUpload/index.vue'
|
||||||
|
|
||||||
|
const imageList = ref([])
|
||||||
|
|
||||||
|
const handleChange = (value, fileList) => {
|
||||||
|
console.log('图片URL数组:', value)
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 限制文件类型
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="imageUrl"
|
||||||
|
accept="image/jpeg,image/png"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 限制图片尺寸
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="imageUrl"
|
||||||
|
:min-width="800"
|
||||||
|
:max-width="1920"
|
||||||
|
:min-height="600"
|
||||||
|
:max-height="1080"
|
||||||
|
tip="尺寸要求:800x600 ~ 1920x1080"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 自定义上传文字和提示
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="imageUrl"
|
||||||
|
upload-text="点击上传"
|
||||||
|
tip="支持 JPG、PNG 格式,不超过 10MB"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 监听上传事件
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="imageUrl"
|
||||||
|
@upload-success="handleUploadSuccess"
|
||||||
|
@upload-error="handleUploadError"
|
||||||
|
@preview="handlePreview"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import ImageUpload from '@/components/ImageUpload/index.vue'
|
||||||
|
|
||||||
|
const handleUploadSuccess = (data, file) => {
|
||||||
|
console.log('上传成功:', data)
|
||||||
|
console.log('文件信息:', file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUploadError = (errorMsg, file) => {
|
||||||
|
console.log('上传失败:', errorMsg)
|
||||||
|
console.log('文件信息:', file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreview = (file) => {
|
||||||
|
console.log('预览文件:', file)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FileUpload 文件上传组件
|
||||||
|
|
||||||
|
### 功能特性
|
||||||
|
- 支持单文件和多文件上传
|
||||||
|
- 文件列表展示
|
||||||
|
- 支持多选上传
|
||||||
|
- 文件大小限制(从配置文件读取)
|
||||||
|
- 自动上传到服务器
|
||||||
|
- 支持 v-model 双向绑定
|
||||||
|
|
||||||
|
### Props 参数
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
|------|------|------|--------|
|
||||||
|
| v-model / modelValue | 文件列表(单文件传字符串,多文件传数组) | String / Array | [] |
|
||||||
|
| maxCount | 最大上传数量 | Number | 1 |
|
||||||
|
| accept | 接受的文件类型(例如 '.pdf,.doc,.docx' 或 '*') | String | '*' |
|
||||||
|
| disabled | 是否禁用 | Boolean | false |
|
||||||
|
| multiple | 是否支持多选 | Boolean | false |
|
||||||
|
| returnUrl | 是否返回URL(true)或完整文件列表(false) | Boolean | true |
|
||||||
|
|
||||||
|
### Events 事件
|
||||||
|
|
||||||
|
| 事件名 | 说明 | 回调参数 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| update:modelValue | 值变化时触发 | value: 文件URL或URL数组 |
|
||||||
|
| change | 文件列表变化时触发 | value: 文件URL或URL数组, fileList: 完整文件列表 |
|
||||||
|
| remove | 移除文件时触发 | file: 被移除的文件对象 |
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
|
||||||
|
#### 单文件上传
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<FileUpload v-model="fileUrl" />
|
||||||
|
<p>文件URL: {{ fileUrl }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import FileUpload from '@/components/FileUpload/index.vue'
|
||||||
|
|
||||||
|
const fileUrl = ref('')
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 多文件上传
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<FileUpload
|
||||||
|
v-model="fileList"
|
||||||
|
:max-count="10"
|
||||||
|
:multiple="true"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import FileUpload from '@/components/FileUpload/index.vue'
|
||||||
|
|
||||||
|
const fileList = ref([])
|
||||||
|
|
||||||
|
const handleChange = (value, fileList) => {
|
||||||
|
console.log('文件URL数组:', value)
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 限制文件类型
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<FileUpload
|
||||||
|
v-model="fileUrl"
|
||||||
|
accept=".pdf,.doc,.docx,.xls,.xlsx"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 监听文件移除
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<FileUpload
|
||||||
|
v-model="fileList"
|
||||||
|
@remove="handleRemove"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import FileUpload from '@/components/FileUpload/index.vue'
|
||||||
|
|
||||||
|
const handleRemove = (file) => {
|
||||||
|
console.log('移除的文件:', file)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
|
上传组件的配置位于 `src/config/upload.js` 文件中:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export default {
|
||||||
|
apiObj: systemAPI.upload, // 图片上传请求API对象
|
||||||
|
filename: "file", // form请求时文件的key
|
||||||
|
successCode: 1, // 请求完成代码
|
||||||
|
maxSize: 10, // 最大图片大小 默认10MB
|
||||||
|
parseData: function (res) {
|
||||||
|
return {
|
||||||
|
code: res.code, // 分析状态字段结构
|
||||||
|
fileName: res.data.name, // 分析文件名称
|
||||||
|
src: res.data.url, // 分析图片远程地址结构
|
||||||
|
msg: res.message // 分析描述字段结构
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apiObjFile: systemAPI.upload, // 文件上传请求API对象
|
||||||
|
maxSizeFile: 10 // 最大文件大小 默认10MB
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置项说明
|
||||||
|
|
||||||
|
| 配置项 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| apiObj | 图片上传API方法 |
|
||||||
|
| filename | FormData 中文件的字段名 |
|
||||||
|
| successCode | 上传成功的响应码 |
|
||||||
|
| maxSize | 图片最大大小限制(MB) |
|
||||||
|
| parseData | 响应数据解析函数,将后端响应转换为标准格式 |
|
||||||
|
| apiObjFile | 文件上传API方法(如未配置则使用 apiObj) |
|
||||||
|
| maxSizeFile | 文件最大大小限制(MB) |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **v-model 数据格式**:
|
||||||
|
- 单图/单文件上传:使用字符串类型 `imageUrl = 'http://example.com/image.jpg'`
|
||||||
|
- 多图/多文件上传:使用数组类型 `imageList = ['http://example.com/1.jpg', 'http://example.com/2.jpg']`
|
||||||
|
|
||||||
|
2. **返回值控制**:
|
||||||
|
- `returnUrl: true`(默认):返回 URL 字符串或数组
|
||||||
|
- `returnUrl: false`:返回完整的文件列表对象(包含 uid, name, status, url 等字段)
|
||||||
|
|
||||||
|
3. **API 响应格式**:
|
||||||
|
- 确保后端 API 返回的数据格式与 `parseData` 函数的解析逻辑一致
|
||||||
|
|
||||||
|
4. **文件大小限制**:
|
||||||
|
- 图片上传使用 `maxSize` 配置
|
||||||
|
- 文件上传使用 `maxSizeFile` 配置
|
||||||
|
|
||||||
|
5. **图片尺寸限制**:
|
||||||
|
- 设置 `minWidth`、`maxWidth`、`minHeight`、`maxHeight` 可以限制图片尺寸
|
||||||
|
- 尺寸校验会在上传前进行,不符合要求的文件将被拒绝
|
||||||
|
- 推荐配合 `tip` 属性显示尺寸要求提示
|
||||||
|
|
||||||
|
6. **上传状态反馈**:
|
||||||
|
- 上传过程中显示加载动画
|
||||||
|
- 上传成功/失败会显示相应的提示消息
|
||||||
|
- 可通过 `@upload-success` 和 `@upload-error` 事件监听上传结果
|
||||||
|
|
||||||
|
7. **拖拽上传**:
|
||||||
|
- 组件支持拖拽上传,拖拽时会有视觉反馈
|
||||||
|
- 拖拽区域会显示蓝色边框和背景色变化
|
||||||
|
|
||||||
|
8. **错误处理**:
|
||||||
|
- 上传失败的文件会自动从列表中过滤
|
||||||
|
- 只有上传成功的文件会被包含在最终结果中
|
||||||
20
src/config/upload.js
Normal file
20
src/config/upload.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { upload } from "@/api/system";
|
||||||
|
|
||||||
|
//上传配置
|
||||||
|
|
||||||
|
export default {
|
||||||
|
apiObj: upload, //上传请求API对象
|
||||||
|
filename: "file", //form请求时文件的key
|
||||||
|
successCode: 1, //请求完成代码
|
||||||
|
maxSize: 10, //最大文件大小 默认10MB
|
||||||
|
parseData: function (res) {
|
||||||
|
return {
|
||||||
|
code: res.code, //分析状态字段结构
|
||||||
|
fileName: res.data.name,//分析文件名称
|
||||||
|
src: res.data.url, //分析图片远程地址结构
|
||||||
|
msg: res.message //分析描述字段结构
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apiObjFile: upload, //附件上传请求API对象
|
||||||
|
maxSizeFile: 10 //最大文件大小 默认10MB
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
314
src/pages/system/setting/index.vue
Normal file
314
src/pages/system/setting/index.vue
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
<template>
|
||||||
|
<div class="upload-demo">
|
||||||
|
<a-card title="图片上传组件示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="单图上传">
|
||||||
|
<ImageUpload v-model="singleImage" />
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ singleImage || '暂无图片' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="多图上传(最多5张)">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="multipleImages"
|
||||||
|
:max-count="5"
|
||||||
|
@change="handleImageChange"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p v-if="multipleImages.length > 0">
|
||||||
|
{{ multipleImages.join(', ') }}
|
||||||
|
</p>
|
||||||
|
<p v-else>暂无图片</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="图片尺寸限制示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="限制图片尺寸 800x600">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="sizeImage"
|
||||||
|
:min-width="800"
|
||||||
|
:max-width="1920"
|
||||||
|
:min-height="600"
|
||||||
|
:max-height="1080"
|
||||||
|
tip="尺寸要求:800x600 ~ 1920x1080"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ sizeImage || '暂无图片' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="自定义上传文字">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="customImage"
|
||||||
|
upload-text="点击选择图片"
|
||||||
|
tip="支持 JPG、PNG 格式,最大 10MB"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ customImage || '暂无图片' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="上传事件监听示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="监听上传事件">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="eventImage"
|
||||||
|
@upload-success="handleUploadSuccess"
|
||||||
|
@upload-error="handleUploadError"
|
||||||
|
@preview="handlePreview"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ eventImage || '暂无图片' }}</p>
|
||||||
|
<p v-if="eventLog" class="event-log">
|
||||||
|
<strong>事件日志:</strong>
|
||||||
|
<pre>{{ eventLog }}</pre>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="文件上传组件示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="单文件上传">
|
||||||
|
<FileUpload v-model="singleFile" />
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ singleFile || '暂无文件' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="多文件上传">
|
||||||
|
<FileUpload
|
||||||
|
v-model="multipleFiles"
|
||||||
|
:max-count="10"
|
||||||
|
:multiple="true"
|
||||||
|
@change="handleFileChange"
|
||||||
|
@remove="handleFileRemove"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p v-if="multipleFiles.length > 0">
|
||||||
|
{{ multipleFiles.join(', ') }}
|
||||||
|
</p>
|
||||||
|
<p v-else>暂无文件</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="限制文件类型示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="仅支持 JPG/PNG 图片">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="jpgImage"
|
||||||
|
accept="image/jpeg,image/png"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="仅支持 PDF/Word 文档">
|
||||||
|
<FileUpload
|
||||||
|
v-model="documentFile"
|
||||||
|
accept=".pdf,.doc,.docx"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="禁用状态示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="禁用图片上传">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="disabledImage"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="禁用文件上传">
|
||||||
|
<FileUpload
|
||||||
|
v-model="disabledFile"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="返回完整文件列表示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="返回完整文件对象">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="fullFileList"
|
||||||
|
:return-url="false"
|
||||||
|
@change="handleFullListChange"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>完整文件列表:</strong>
|
||||||
|
<pre>{{ JSON.stringify(fullFileList, null, 2) }}</pre>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ImageUpload from '@/components/ImageUpload/index.vue'
|
||||||
|
import FileUpload from '@/components/FileUpload/index.vue'
|
||||||
|
|
||||||
|
// 单图上传
|
||||||
|
const singleImage = ref('')
|
||||||
|
|
||||||
|
// 多图上传
|
||||||
|
const multipleImages = ref([])
|
||||||
|
|
||||||
|
// 单文件上传
|
||||||
|
const singleFile = ref('')
|
||||||
|
|
||||||
|
// 多文件上传
|
||||||
|
const multipleFiles = ref([])
|
||||||
|
|
||||||
|
// 限制类型
|
||||||
|
const jpgImage = ref('')
|
||||||
|
const documentFile = ref('')
|
||||||
|
|
||||||
|
// 禁用状态
|
||||||
|
const disabledImage = ref('')
|
||||||
|
const disabledFile = ref('')
|
||||||
|
|
||||||
|
// 完整文件列表
|
||||||
|
const fullFileList = ref([])
|
||||||
|
|
||||||
|
// 新增示例
|
||||||
|
const sizeImage = ref('')
|
||||||
|
const customImage = ref('')
|
||||||
|
const eventImage = ref('')
|
||||||
|
const eventLog = ref('')
|
||||||
|
|
||||||
|
// 图片变化事件
|
||||||
|
const handleImageChange = (value, fileList) => {
|
||||||
|
console.log('图片URL数组:', value)
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件变化事件
|
||||||
|
const handleFileChange = (value, fileList) => {
|
||||||
|
console.log('文件URL数组:', value)
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件移除事件
|
||||||
|
const handleFileRemove = (file) => {
|
||||||
|
console.log('移除的文件:', file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完整文件列表变化事件
|
||||||
|
const handleFullListChange = (value, fileList) => {
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传成功事件
|
||||||
|
const handleUploadSuccess = (data, file) => {
|
||||||
|
eventLog.value = `上传成功\n文件名: ${file.name}\n响应数据: ${JSON.stringify(data, null, 2)}`
|
||||||
|
console.log('上传成功:', data, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传失败事件
|
||||||
|
const handleUploadError = (errorMsg, file) => {
|
||||||
|
eventLog.value = `上传失败\n文件名: ${file.name}\n错误信息: ${errorMsg}`
|
||||||
|
console.log('上传失败:', errorMsg, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览事件
|
||||||
|
const handlePreview = (file) => {
|
||||||
|
eventLog.value = `预览图片\n文件名: ${file.name}\n状态: ${file.status}`
|
||||||
|
console.log('预览文件:', file)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.upload-demo {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-card {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-card :deep(.ant-card-body) {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-card :deep(.ant-card-head-title) {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result p {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result pre {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log {
|
||||||
|
margin-top: 12px !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
background-color: #f0f2f5 !important;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log pre {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
314
src/pages/upload/index.vue
Normal file
314
src/pages/upload/index.vue
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
<template>
|
||||||
|
<div class="upload-demo">
|
||||||
|
<a-card title="图片上传组件示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="单图上传">
|
||||||
|
<ImageUpload v-model="singleImage" />
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ singleImage || '暂无图片' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="多图上传(最多5张)">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="multipleImages"
|
||||||
|
:max-count="5"
|
||||||
|
@change="handleImageChange"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p v-if="multipleImages.length > 0">
|
||||||
|
{{ multipleImages.join(', ') }}
|
||||||
|
</p>
|
||||||
|
<p v-else>暂无图片</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="图片尺寸限制示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="限制图片尺寸 800x600">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="sizeImage"
|
||||||
|
:min-width="800"
|
||||||
|
:max-width="1920"
|
||||||
|
:min-height="600"
|
||||||
|
:max-height="1080"
|
||||||
|
tip="尺寸要求:800x600 ~ 1920x1080"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ sizeImage || '暂无图片' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="自定义上传文字">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="customImage"
|
||||||
|
upload-text="点击选择图片"
|
||||||
|
tip="支持 JPG、PNG 格式,最大 10MB"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ customImage || '暂无图片' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="上传事件监听示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="监听上传事件">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="eventImage"
|
||||||
|
@upload-success="handleUploadSuccess"
|
||||||
|
@upload-error="handleUploadError"
|
||||||
|
@preview="handlePreview"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ eventImage || '暂无图片' }}</p>
|
||||||
|
<p v-if="eventLog" class="event-log">
|
||||||
|
<strong>事件日志:</strong>
|
||||||
|
<pre>{{ eventLog }}</pre>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="文件上传组件示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="单文件上传">
|
||||||
|
<FileUpload v-model="singleFile" />
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p>{{ singleFile || '暂无文件' }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="多文件上传">
|
||||||
|
<FileUpload
|
||||||
|
v-model="multipleFiles"
|
||||||
|
:max-count="10"
|
||||||
|
:multiple="true"
|
||||||
|
@change="handleFileChange"
|
||||||
|
@remove="handleFileRemove"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>结果:</strong>
|
||||||
|
<p v-if="multipleFiles.length > 0">
|
||||||
|
{{ multipleFiles.join(', ') }}
|
||||||
|
</p>
|
||||||
|
<p v-else>暂无文件</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="限制文件类型示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="仅支持 JPG/PNG 图片">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="jpgImage"
|
||||||
|
accept="image/jpeg,image/png"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="仅支持 PDF/Word 文档">
|
||||||
|
<FileUpload
|
||||||
|
v-model="documentFile"
|
||||||
|
accept=".pdf,.doc,.docx"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="禁用状态示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="禁用图片上传">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="disabledImage"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="禁用文件上传">
|
||||||
|
<FileUpload
|
||||||
|
v-model="disabledFile"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="返回完整文件列表示例" class="demo-card">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card type="inner" title="返回完整文件对象">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="fullFileList"
|
||||||
|
:return-url="false"
|
||||||
|
@change="handleFullListChange"
|
||||||
|
/>
|
||||||
|
<div class="result">
|
||||||
|
<strong>完整文件列表:</strong>
|
||||||
|
<pre>{{ JSON.stringify(fullFileList, null, 2) }}</pre>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ImageUpload from '@/components/ImageUpload/index.vue'
|
||||||
|
import FileUpload from '@/components/FileUpload/index.vue'
|
||||||
|
|
||||||
|
// 单图上传
|
||||||
|
const singleImage = ref('')
|
||||||
|
|
||||||
|
// 多图上传
|
||||||
|
const multipleImages = ref([])
|
||||||
|
|
||||||
|
// 单文件上传
|
||||||
|
const singleFile = ref('')
|
||||||
|
|
||||||
|
// 多文件上传
|
||||||
|
const multipleFiles = ref([])
|
||||||
|
|
||||||
|
// 限制类型
|
||||||
|
const jpgImage = ref('')
|
||||||
|
const documentFile = ref('')
|
||||||
|
|
||||||
|
// 禁用状态
|
||||||
|
const disabledImage = ref('')
|
||||||
|
const disabledFile = ref('')
|
||||||
|
|
||||||
|
// 完整文件列表
|
||||||
|
const fullFileList = ref([])
|
||||||
|
|
||||||
|
// 新增示例
|
||||||
|
const sizeImage = ref('')
|
||||||
|
const customImage = ref('')
|
||||||
|
const eventImage = ref('')
|
||||||
|
const eventLog = ref('')
|
||||||
|
|
||||||
|
// 图片变化事件
|
||||||
|
const handleImageChange = (value, fileList) => {
|
||||||
|
console.log('图片URL数组:', value)
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件变化事件
|
||||||
|
const handleFileChange = (value, fileList) => {
|
||||||
|
console.log('文件URL数组:', value)
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件移除事件
|
||||||
|
const handleFileRemove = (file) => {
|
||||||
|
console.log('移除的文件:', file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完整文件列表变化事件
|
||||||
|
const handleFullListChange = (value, fileList) => {
|
||||||
|
console.log('完整文件列表:', fileList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传成功事件
|
||||||
|
const handleUploadSuccess = (data, file) => {
|
||||||
|
eventLog.value = `上传成功\n文件名: ${file.name}\n响应数据: ${JSON.stringify(data, null, 2)}`
|
||||||
|
console.log('上传成功:', data, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传失败事件
|
||||||
|
const handleUploadError = (errorMsg, file) => {
|
||||||
|
eventLog.value = `上传失败\n文件名: ${file.name}\n错误信息: ${errorMsg}`
|
||||||
|
console.log('上传失败:', errorMsg, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览事件
|
||||||
|
const handlePreview = (file) => {
|
||||||
|
eventLog.value = `预览图片\n文件名: ${file.name}\n状态: ${file.status}`
|
||||||
|
console.log('预览文件:', file)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.upload-demo {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-card {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-card :deep(.ant-card-body) {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-card :deep(.ant-card-head-title) {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result p {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result pre {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log {
|
||||||
|
margin-top: 12px !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
background-color: #f0f2f5 !important;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log pre {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user