editor 富文本编辑器上传图片,视频功能更新
This commit is contained in:
parent
cf6a60ad3a
commit
6360dff110
@ -2,7 +2,7 @@
|
|||||||
import { ElCard, ElButton } from 'element-plus'
|
import { ElCard, ElButton } from 'element-plus'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { ref, onMounted, defineEmits } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { Sticky } from '@/components/Sticky'
|
import { Sticky } from '@/components/Sticky'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@ -15,9 +15,11 @@ defineProps({
|
|||||||
title: propTypes.string.def(''),
|
title: propTypes.string.def(''),
|
||||||
message: propTypes.string.def('')
|
message: propTypes.string.def('')
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['back'])
|
const emit = defineEmits(['back'])
|
||||||
const offset = ref(85)
|
const offset = ref(85)
|
||||||
const contentDetailWrap = ref()
|
const contentDetailWrap = ref()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
offset.value = contentDetailWrap.value.getBoundingClientRect().top
|
offset.value = contentDetailWrap.value.getBoundingClientRect().top
|
||||||
})
|
})
|
||||||
|
@ -6,6 +6,9 @@ import { propTypes } from '@/utils/propTypes'
|
|||||||
import { isNumber } from '@/utils/is'
|
import { isNumber } from '@/utils/is'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useLocaleStore } from '@/store/modules/locale'
|
import { useLocaleStore } from '@/store/modules/locale'
|
||||||
|
import { uploadImageToOSS, uploadVideoToOSS } from '@/api/vadmin/system/files'
|
||||||
|
|
||||||
|
// editor 官方文档:https://www.wangeditor.com/v5/getting-started.html
|
||||||
|
|
||||||
const localeStore = useLocaleStore()
|
const localeStore = useLocaleStore()
|
||||||
|
|
||||||
@ -79,7 +82,48 @@ const editorConfig = computed((): IEditorConfig => {
|
|||||||
},
|
},
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
scroll: true,
|
scroll: true,
|
||||||
uploadImgShowBase64: true
|
uploadImgShowBase64: true,
|
||||||
|
MENU_CONF: {
|
||||||
|
uploadImage: {
|
||||||
|
// 自定义上传图片
|
||||||
|
// 官方文档:https://www.wangeditor.com/v5/menu-config.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%8A%E4%BC%A0
|
||||||
|
async customUpload(file: File, insertFn: InsertImageType) {
|
||||||
|
// 上传图片前的检查
|
||||||
|
if (!['image/jpeg', 'image/gif', 'image/png'].includes(file.type)) {
|
||||||
|
return ElMessage.error(`${file.name}上传失败:上传图片只能是 JPG/GIF/PNG/ 格式!`)
|
||||||
|
}
|
||||||
|
if (!(file.size / 1024 / 1024 < 2)) {
|
||||||
|
return ElMessage.error(`${file.name}上传失败:上传图片大小不能超过 2MB!`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自己实现上传,并得到图片 url
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('path', 'editor/image')
|
||||||
|
const res = await uploadImageToOSS(formData)
|
||||||
|
insertFn(res.data, '', res.data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
uploadVideo: {
|
||||||
|
// 自定义上传视频
|
||||||
|
// 官方文档:https://www.wangeditor.com/v5/menu-config.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%9F%E8%83%BD-1
|
||||||
|
async customUpload(file: File, insertFn: InsertVideoType) {
|
||||||
|
// 上传视频前的检查
|
||||||
|
if (!['video/mp4', 'video/mpeg'].includes(file.type)) {
|
||||||
|
return ElMessage.error(`${file.name}上传失败:上传视频只能是 mp4/mpeg/ 格式!`)
|
||||||
|
}
|
||||||
|
if (!(file.size / 1024 / 1024 < 5)) {
|
||||||
|
return ElMessage.error(`${file.name}上传失败:上传视频大小不能超过 5MB!`)
|
||||||
|
}
|
||||||
|
// 自己实现上传,并得到视频 url
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('path', 'editor/video')
|
||||||
|
const res = await uploadVideoToOSS(formData)
|
||||||
|
insertFn(res.data, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
props.editorConfig || {}
|
props.editorConfig || {}
|
||||||
)
|
)
|
||||||
|
@ -173,7 +173,7 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
|
|||||||
// 如果为 false,则说明是点击按钮,则获取当前选择行数据进行删除
|
// 如果为 false,则说明是点击按钮,则获取当前选择行数据进行删除
|
||||||
delListApi: async (
|
delListApi: async (
|
||||||
multiple: boolean,
|
multiple: boolean,
|
||||||
ids: string[] | number[] | number = [],
|
ids: string[] | number[] | number | string = [],
|
||||||
message = true
|
message = true
|
||||||
) => {
|
) => {
|
||||||
const tableRef = await getTable()
|
const tableRef = await getTable()
|
||||||
|
3
kinit-admin/src/types/editor.d.ts
vendored
Normal file
3
kinit-admin/src/types/editor.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
type InsertImageType = (url: string, alt: string, href: string) => void
|
||||||
|
|
||||||
|
type InsertVideoType = (url: string, poster: string = '') => void
|
@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
|
|||||||
"""
|
"""
|
||||||
系统版本
|
系统版本
|
||||||
"""
|
"""
|
||||||
VERSION = "1.10.3"
|
VERSION = "1.10.4"
|
||||||
|
|
||||||
"""安全警告: 不要在生产中打开调试运行!"""
|
"""安全警告: 不要在生产中打开调试运行!"""
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
@ -282,8 +282,6 @@ class UserDal(DalBase):
|
|||||||
更新当前用户头像
|
更新当前用户头像
|
||||||
"""
|
"""
|
||||||
result = await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).upload_image("avatar", file)
|
result = await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).upload_image("avatar", file)
|
||||||
if not result:
|
|
||||||
raise CustomException(msg="上传失败", code=status.HTTP_ERROR)
|
|
||||||
user.avatar = result
|
user.avatar = result
|
||||||
await self.flush(user)
|
await self.flush(user)
|
||||||
return result
|
return result
|
||||||
|
@ -113,8 +113,18 @@ async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())):
|
|||||||
@app.post("/upload/image/to/oss", summary="上传图片到阿里云OSS")
|
@app.post("/upload/image/to/oss", summary="上传图片到阿里云OSS")
|
||||||
async def upload_image_to_oss(file: UploadFile, path: str = Form(...)):
|
async def upload_image_to_oss(file: UploadFile, path: str = Form(...)):
|
||||||
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(path, file)
|
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(path, file)
|
||||||
if not result:
|
return SuccessResponse(result)
|
||||||
return ErrorResponse(msg="上传失败")
|
|
||||||
|
|
||||||
|
@app.post("/upload/video/to/oss", summary="上传视频到阿里云OSS")
|
||||||
|
async def upload_video_to_oss(file: UploadFile, path: str = Form(...)):
|
||||||
|
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_video(path, file)
|
||||||
|
return SuccessResponse(result)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/upload/file/to/oss", summary="上传文件到阿里云OSS")
|
||||||
|
async def upload_file_to_oss(file: UploadFile, path: str = Form(...)):
|
||||||
|
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_file(path, file)
|
||||||
return SuccessResponse(result)
|
return SuccessResponse(result)
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ from fastapi import UploadFile
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
import oss2 # 安装依赖库:pip install oss2
|
import oss2 # 安装依赖库:pip install oss2
|
||||||
from oss2.models import PutObjectResult
|
from oss2.models import PutObjectResult
|
||||||
|
from core.exception import CustomException
|
||||||
from core.logger import logger
|
from core.logger import logger
|
||||||
|
from utils import status
|
||||||
from utils.file.compress.cpressJPG import compress_jpg_png
|
from utils.file.compress.cpressJPG import compress_jpg_png
|
||||||
from utils.file.file_manage import FileManage
|
from utils.file.file_manage import FileManage
|
||||||
from utils.file.file_base import FileBase
|
from utils.file.file_base import FileBase
|
||||||
@ -45,15 +47,19 @@ class AliyunOSS(FileBase):
|
|||||||
self.bucket = oss2.Bucket(auth, bucket.endpoint, bucket.bucket)
|
self.bucket = oss2.Bucket(auth, bucket.endpoint, bucket.bucket)
|
||||||
self.baseUrl = bucket.baseUrl
|
self.baseUrl = bucket.baseUrl
|
||||||
|
|
||||||
async def upload_image(self, path: str, file: UploadFile, compress: bool = False) -> str:
|
async def upload_image(self, path: str, file: UploadFile, compress: bool = False, max_size: int = 10) -> str:
|
||||||
"""
|
"""
|
||||||
上传图片
|
上传图片
|
||||||
|
|
||||||
:param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。
|
:param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。
|
||||||
:param file: 文件对象
|
:param file: 文件对象
|
||||||
:param compress: 是否压缩该文件
|
:param compress: 是否压缩该文件
|
||||||
|
:param max_size: 图片文件最大值,单位 MB,默认 10MB
|
||||||
:return: 上传后的文件oss链接
|
:return: 上传后的文件oss链接
|
||||||
"""
|
"""
|
||||||
|
# 验证图片类型
|
||||||
|
await self.validate_file(file, max_size, self.IMAGE_ACCEPT)
|
||||||
|
# 生成文件路径
|
||||||
path = self.generate_path(path, file.filename)
|
path = self.generate_path(path, file.filename)
|
||||||
if compress:
|
if compress:
|
||||||
# 压缩图片
|
# 压缩图片
|
||||||
@ -63,14 +69,23 @@ class AliyunOSS(FileBase):
|
|||||||
file_data = f.read()
|
file_data = f.read()
|
||||||
else:
|
else:
|
||||||
file_data = await file.read()
|
file_data = await file.read()
|
||||||
result = self.bucket.put_object(path, file_data)
|
return await self.__upload_file_to_oss(path, file_data)
|
||||||
assert isinstance(result, PutObjectResult)
|
|
||||||
if result.status != 200:
|
async def upload_video(self, path: str, file: UploadFile, max_size: int = 100) -> str:
|
||||||
logger.error(f"图片上传到OSS失败,状态码:{result.status}")
|
"""
|
||||||
print("图片上传路径", path)
|
上传视频
|
||||||
print(f"图片上传到OSS失败,状态码:{result.status}")
|
|
||||||
return ""
|
:param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。
|
||||||
return self.baseUrl + path
|
:param file: 文件对象
|
||||||
|
:param max_size: 视频文件最大值,单位 MB,默认 100MB
|
||||||
|
:return: 上传后的文件oss链接
|
||||||
|
"""
|
||||||
|
# 验证图片类型
|
||||||
|
await self.validate_file(file, max_size, self.VIDEO_ACCEPT)
|
||||||
|
# 生成文件路径
|
||||||
|
path = self.generate_path(path, file.filename)
|
||||||
|
file_data = await file.read()
|
||||||
|
return await self.__upload_file_to_oss(path, file_data)
|
||||||
|
|
||||||
async def upload_file(self, path: str, file: UploadFile) -> str:
|
async def upload_file(self, path: str, file: UploadFile) -> str:
|
||||||
"""
|
"""
|
||||||
@ -82,9 +97,19 @@ class AliyunOSS(FileBase):
|
|||||||
"""
|
"""
|
||||||
path = self.generate_path(path, file.filename)
|
path = self.generate_path(path, file.filename)
|
||||||
file_data = await file.read()
|
file_data = await file.read()
|
||||||
|
return await self.__upload_file_to_oss(path, file_data)
|
||||||
|
|
||||||
|
async def __upload_file_to_oss(self, path: str, file_data: bytes) -> str:
|
||||||
|
"""
|
||||||
|
上传文件到OSS
|
||||||
|
|
||||||
|
:param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。
|
||||||
|
:param file_data: 文件数据
|
||||||
|
:return: 上传后的文件oss链接
|
||||||
|
"""
|
||||||
result = self.bucket.put_object(path, file_data)
|
result = self.bucket.put_object(path, file_data)
|
||||||
assert isinstance(result, PutObjectResult)
|
assert isinstance(result, PutObjectResult)
|
||||||
if result.status != 200:
|
if result.status != 200:
|
||||||
logger.error(f"文件上传到OSS失败,状态码:{result.status}")
|
logger.error(f"文件上传到OSS失败,状态码:{result.status}")
|
||||||
return ""
|
raise CustomException("上传文件失败", code=status.HTTP_ERROR)
|
||||||
return self.baseUrl + path
|
return self.baseUrl + path
|
||||||
|
@ -17,7 +17,7 @@ from utils import status
|
|||||||
class FileBase:
|
class FileBase:
|
||||||
|
|
||||||
IMAGE_ACCEPT = ["image/png", "image/jpeg", "image/gif", "image/x-icon"]
|
IMAGE_ACCEPT = ["image/png", "image/jpeg", "image/gif", "image/x-icon"]
|
||||||
VIDEO_ACCEPT = ["audio/mp4", "video/mp4", "video/mpeg"]
|
VIDEO_ACCEPT = ["video/mp4", "video/mpeg"]
|
||||||
ALL_ACCEPT = [*IMAGE_ACCEPT, *VIDEO_ACCEPT]
|
ALL_ACCEPT = [*IMAGE_ACCEPT, *VIDEO_ACCEPT]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -16,10 +16,12 @@ https://api.ip138.com/ip/?ip=58.16.180.3&datatype=jsonp&token=cc87f3c77747bccbaa
|
|||||||
aiohttp 异步请求文档:https://docs.aiohttp.org/en/stable/client_quickstart.html
|
aiohttp 异步请求文档:https://docs.aiohttp.org/en/stable/client_quickstart.html
|
||||||
"""
|
"""
|
||||||
from aiohttp import TCPConnector
|
from aiohttp import TCPConnector
|
||||||
|
|
||||||
from application.settings import IP_PARSE_TOKEN, IP_PARSE_ENABLE
|
from application.settings import IP_PARSE_TOKEN, IP_PARSE_ENABLE
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from core.logger import logger
|
from core.logger import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class IPLocationOut(BaseModel):
|
class IPLocationOut(BaseModel):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user