parent
0cda4612a9
commit
c26eb543e0
2
.env.dev
2
.env.dev
@ -7,6 +7,8 @@ VITE_DEV=true
|
|||||||
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||||
# VITE_BASE_URL='http://dofast.demo.huizhizao.vip:20001'
|
# VITE_BASE_URL='http://dofast.demo.huizhizao.vip:20001'
|
||||||
|
|
||||||
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ VITE_DEV=true
|
|||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://localhost:48080'
|
VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ VITE_DEV=false
|
|||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://localhost:48080'
|
VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ VITE_DEV=false
|
|||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||||
|
|
||||||
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ VITE_DEV=false
|
|||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://localhost:48080'
|
VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
||||||
|
|
||||||
|
@ -6,6 +6,14 @@ export interface FilePageReqVO extends PageParam {
|
|||||||
createTime?: Date[]
|
createTime?: Date[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 文件预签名地址 Response VO
|
||||||
|
export interface FilePresignedUrlRespVO {
|
||||||
|
// 文件配置编号
|
||||||
|
configId: number
|
||||||
|
// 文件预签名地址
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
// 查询文件列表
|
// 查询文件列表
|
||||||
export const getFilePage = (params: FilePageReqVO) => {
|
export const getFilePage = (params: FilePageReqVO) => {
|
||||||
return request.get({ url: '/infra/file/page', params })
|
return request.get({ url: '/infra/file/page', params })
|
||||||
@ -15,3 +23,16 @@ export const getFilePage = (params: FilePageReqVO) => {
|
|||||||
export const deleteFile = (id: number) => {
|
export const deleteFile = (id: number) => {
|
||||||
return request.delete({ url: '/infra/file/delete?id=' + id })
|
return request.delete({ url: '/infra/file/delete?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取文件预签名地址
|
||||||
|
export const getFilePresignedUrl = (fileName: string) => {
|
||||||
|
return request.get<FilePresignedUrlRespVO>({
|
||||||
|
url: '/infra/file/presigned-url',
|
||||||
|
params: { fileName }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
export const createFile = (data: any) => {
|
||||||
|
return request.post({ url: '/infra/file/create', data })
|
||||||
|
}
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
<el-upload
|
<el-upload
|
||||||
ref="uploadRef"
|
ref="uploadRef"
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
:action="updateUrl"
|
:action="uploadUrl"
|
||||||
:auto-upload="autoUpload"
|
:auto-upload="autoUpload"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="beforeUpload"
|
||||||
:drag="drag"
|
:drag="drag"
|
||||||
:headers="uploadHeaders"
|
|
||||||
:limit="props.limit"
|
:limit="props.limit"
|
||||||
:multiple="props.limit > 1"
|
:multiple="props.limit > 1"
|
||||||
:on-error="excelUploadError"
|
:on-error="excelUploadError"
|
||||||
@ -16,6 +15,7 @@
|
|||||||
:on-remove="handleRemove"
|
:on-remove="handleRemove"
|
||||||
:on-success="handleFileSuccess"
|
:on-success="handleFileSuccess"
|
||||||
:show-file-list="true"
|
:show-file-list="true"
|
||||||
|
:http-request="httpRequest"
|
||||||
class="upload-file-uploader"
|
class="upload-file-uploader"
|
||||||
name="file"
|
name="file"
|
||||||
>
|
>
|
||||||
@ -36,9 +36,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
|
||||||
import type { UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
|
import type { UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
|
||||||
import { isString } from '@/utils/is'
|
import { isString } from '@/utils/is'
|
||||||
|
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||||
|
import { UploadFile } from 'element-plus/es/components/upload/src/upload'
|
||||||
|
|
||||||
defineOptions({ name: 'UploadFile' })
|
defineOptions({ name: 'UploadFile' })
|
||||||
|
|
||||||
@ -48,7 +49,6 @@ const emit = defineEmits(['update:modelValue'])
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||||
title: propTypes.string.def('文件上传'),
|
title: propTypes.string.def('文件上传'),
|
||||||
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
|
|
||||||
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||||
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
||||||
limit: propTypes.number.def(5), // 数量限制
|
limit: propTypes.number.def(5), // 数量限制
|
||||||
@ -62,10 +62,8 @@ const uploadRef = ref<UploadInstance>()
|
|||||||
const uploadList = ref<UploadUserFile[]>([])
|
const uploadList = ref<UploadUserFile[]>([])
|
||||||
const fileList = ref<UploadUserFile[]>([])
|
const fileList = ref<UploadUserFile[]>([])
|
||||||
const uploadNumber = ref<number>(0)
|
const uploadNumber = ref<number>(0)
|
||||||
const uploadHeaders = ref({
|
|
||||||
Authorization: 'Bearer ' + getAccessToken(),
|
const { uploadUrl, httpRequest } = useUpload()
|
||||||
'tenant-id': getTenantId()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 文件上传之前判断
|
// 文件上传之前判断
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||||
@ -120,10 +118,10 @@ const excelUploadError: UploadProps['onError'] = (): void => {
|
|||||||
message.error('导入数据失败,请您重新上传!')
|
message.error('导入数据失败,请您重新上传!')
|
||||||
}
|
}
|
||||||
// 删除上传文件
|
// 删除上传文件
|
||||||
const handleRemove = (file) => {
|
const handleRemove = (file: UploadFile) => {
|
||||||
const findex = fileList.value.map((f) => f.name).indexOf(file.name)
|
const index = fileList.value.map((f) => f.name).indexOf(file.name)
|
||||||
if (findex > -1) {
|
if (index > -1) {
|
||||||
fileList.value.splice(findex, 1)
|
fileList.value.splice(index, 1)
|
||||||
emitUpdateModelValue()
|
emitUpdateModelValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
<el-upload
|
<el-upload
|
||||||
:id="uuid"
|
:id="uuid"
|
||||||
:accept="fileType.join(',')"
|
:accept="fileType.join(',')"
|
||||||
:action="updateUrl"
|
:action="uploadUrl"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="beforeUpload"
|
||||||
:class="['upload', drag ? 'no-border' : '']"
|
:class="['upload', drag ? 'no-border' : '']"
|
||||||
:drag="drag"
|
:drag="drag"
|
||||||
:headers="uploadHeaders"
|
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:on-error="uploadError"
|
:on-error="uploadError"
|
||||||
:on-success="uploadSuccess"
|
:on-success="uploadSuccess"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
|
:http-request="httpRequest"
|
||||||
>
|
>
|
||||||
<template v-if="modelValue">
|
<template v-if="modelValue">
|
||||||
<img :src="modelValue" class="upload-image" />
|
<img :src="modelValue" class="upload-image" />
|
||||||
@ -50,8 +50,8 @@ import type { UploadProps } from 'element-plus'
|
|||||||
|
|
||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
|
||||||
import { createImageViewer } from '@/components/ImageViewer'
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
|
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||||
|
|
||||||
defineOptions({ name: 'UploadImg' })
|
defineOptions({ name: 'UploadImg' })
|
||||||
|
|
||||||
@ -70,7 +70,6 @@ type FileTypes =
|
|||||||
// 接受父组件参数
|
// 接受父组件参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: propTypes.string.def(''),
|
modelValue: propTypes.string.def(''),
|
||||||
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
|
|
||||||
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
||||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||||
fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
|
fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
|
||||||
@ -101,10 +100,7 @@ const deleteImg = () => {
|
|||||||
emit('update:modelValue', '')
|
emit('update:modelValue', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadHeaders = ref({
|
const { uploadUrl, httpRequest } = useUpload()
|
||||||
Authorization: 'Bearer ' + getAccessToken(),
|
|
||||||
'tenant-id': getTenantId()
|
|
||||||
})
|
|
||||||
|
|
||||||
const editImg = () => {
|
const editImg = () => {
|
||||||
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
<el-upload
|
<el-upload
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
:accept="fileType.join(',')"
|
:accept="fileType.join(',')"
|
||||||
:action="updateUrl"
|
:action="uploadUrl"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="beforeUpload"
|
||||||
:class="['upload', drag ? 'no-border' : '']"
|
:class="['upload', drag ? 'no-border' : '']"
|
||||||
:drag="drag"
|
:drag="drag"
|
||||||
:headers="uploadHeaders"
|
|
||||||
:limit="limit"
|
:limit="limit"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:on-error="uploadError"
|
:on-error="uploadError"
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
:on-success="uploadSuccess"
|
:on-success="uploadSuccess"
|
||||||
|
:http-request="httpRequest"
|
||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
>
|
>
|
||||||
<div class="upload-empty">
|
<div class="upload-empty">
|
||||||
@ -50,7 +50,7 @@ import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
|
|||||||
import { ElNotification } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||||
|
|
||||||
defineOptions({ name: 'UploadImgs' })
|
defineOptions({ name: 'UploadImgs' })
|
||||||
|
|
||||||
@ -70,7 +70,6 @@ type FileTypes =
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||||
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
|
|
||||||
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
||||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||||
limit: propTypes.number.def(5), // 最大图片上传数 ==> 非必传(默认为 5张)
|
limit: propTypes.number.def(5), // 最大图片上传数 ==> 非必传(默认为 5张)
|
||||||
@ -81,10 +80,7 @@ const props = defineProps({
|
|||||||
borderradius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px)
|
borderradius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||||
})
|
})
|
||||||
|
|
||||||
const uploadHeaders = ref({
|
const { uploadUrl, httpRequest } = useUpload()
|
||||||
Authorization: 'Bearer ' + getAccessToken(),
|
|
||||||
'tenant-id': getTenantId()
|
|
||||||
})
|
|
||||||
|
|
||||||
const fileList = ref<UploadUserFile[]>([])
|
const fileList = ref<UploadUserFile[]>([])
|
||||||
const uploadNumber = ref<number>(0)
|
const uploadNumber = ref<number>(0)
|
||||||
@ -121,7 +117,6 @@ const emit = defineEmits<UploadEmits>()
|
|||||||
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||||
message.success('上传成功')
|
message.success('上传成功')
|
||||||
// 删除自身
|
// 删除自身
|
||||||
debugger
|
|
||||||
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
|
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
|
||||||
fileList.value.splice(index, 1)
|
fileList.value.splice(index, 1)
|
||||||
uploadList.value.push({ name: res.data, url: res.data })
|
uploadList.value.push({ name: res.data, url: res.data })
|
||||||
|
87
src/components/UploadFile/src/useUpload.ts
Normal file
87
src/components/UploadFile/src/useUpload.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { getAccessToken, getTenantId } from '@/utils/auth'
|
||||||
|
import * as FileApi from '@/api/infra/file'
|
||||||
|
import CryptoJS from 'crypto-js'
|
||||||
|
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
|
||||||
|
import { ajaxUpload } from 'element-plus/es/components/upload/src/ajax'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export const useUpload = () => {
|
||||||
|
// 后端上传地址
|
||||||
|
const uploadUrl = import.meta.env.VITE_UPLOAD_URL
|
||||||
|
// 是否使用前端直连上传
|
||||||
|
const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
|
||||||
|
// 重写ElUpload上传方法
|
||||||
|
const httpRequest = async (options: UploadRequestOptions) => {
|
||||||
|
// 模式一:前端上传
|
||||||
|
if (isClientUpload) {
|
||||||
|
// 1.1 生成文件名称
|
||||||
|
const fileName = await generateFileName(options.file)
|
||||||
|
// 1.2 获取文件预签名地址
|
||||||
|
const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
|
||||||
|
// 1.3 上传文件(不能使用ElUpload的ajaxUpload方法的原因:其使用的是FormData上传,Minio不支持)
|
||||||
|
return axios.put(presignedInfo.url, options.file).then(() => {
|
||||||
|
// 1.4. 记录文件信息到后端
|
||||||
|
const fileVo = createFile(presignedInfo.configId, fileName, presignedInfo.url, options.file)
|
||||||
|
// 通知成功,数据格式保持与后端上传的返回结果一致
|
||||||
|
return { data: fileVo.url }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 模式二:后端上传(需要增加后端身份认证请求头)
|
||||||
|
options.headers['Authorization'] = 'Bearer ' + getAccessToken()
|
||||||
|
options.headers['tenant-id'] = getTenantId()
|
||||||
|
// 使用ElUpload的上传方法
|
||||||
|
return ajaxUpload(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadUrl,
|
||||||
|
httpRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文件信息
|
||||||
|
* @param configId 文件配置编号
|
||||||
|
* @param name 文件名称
|
||||||
|
* @param url 文件地址
|
||||||
|
* @param file 文件
|
||||||
|
*/
|
||||||
|
function createFile(configId: number, name: string, url: string, file: UploadRawFile) {
|
||||||
|
const fileVo = {
|
||||||
|
configId: configId,
|
||||||
|
path: name,
|
||||||
|
// 移除预签名参数:参数只在上传时有用,查看时不需要
|
||||||
|
url: url.substring(0, url.indexOf('?')),
|
||||||
|
name: file.name,
|
||||||
|
type: file.type,
|
||||||
|
size: file.size
|
||||||
|
}
|
||||||
|
FileApi.createFile(fileVo)
|
||||||
|
return fileVo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件名称(使用算法SHA256)
|
||||||
|
* @param file 要上传的文件
|
||||||
|
*/
|
||||||
|
async function generateFileName(file: UploadRawFile) {
|
||||||
|
// 读取文件内容
|
||||||
|
const data = await file.arrayBuffer()
|
||||||
|
const wordArray = CryptoJS.lib.WordArray.create(data)
|
||||||
|
// 计算SHA256
|
||||||
|
const sha256 = CryptoJS.SHA256(wordArray).toString()
|
||||||
|
// 拼接后缀
|
||||||
|
const ext = file.name.substring(file.name.lastIndexOf('.'))
|
||||||
|
return `${sha256}${ext}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传类型
|
||||||
|
*/
|
||||||
|
enum UPLOAD_TYPE {
|
||||||
|
// 客户端直接上传(只支持S3服务)
|
||||||
|
CLIENT = 'client',
|
||||||
|
// 客户端发送到后端上传
|
||||||
|
SERVER = 'server'
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user