提交 b074c5fd authored 作者: 王鹏飞's avatar 王鹏飞

chore: 修改视频上传

上级 a567b70d
...@@ -75,9 +75,19 @@ export function checkLocalFile(params: { file_name: string }) { ...@@ -75,9 +75,19 @@ export function checkLocalFile(params: { file_name: string }) {
} }
// 上传文件 // 上传文件
export function uploadLocalFile(data: { file: File; file_name: string; is_continuingly?: number; now_package_num: number; total_package_num: number }) { export function uploadLocalFile(
return httpRequest.post('/api/lab/v1/common/file/upload', data, { data: { file: File | Blob; file_name: string; is_continuingly?: number; now_package_num: number; total_package_num: number },
withCredentials: false, options = {}
headers: { 'Content-Type': 'multipart/form-data' } ) {
}) return httpRequest.post(
'/api/lab/v1/common/file/upload',
data,
Object.assign(
{
withCredentials: false,
headers: { 'Content-Type': 'multipart/form-data' }
},
options
)
)
} }
import { getLocalFileChunk, uploadLocalFile } from '@/api/base'
interface FileItem {
file: File
url: string
name: string
progress: number
abortController: AbortController
}
interface UploadOptions {
multiple?: boolean
autoUpload?: boolean
onUploadStarted?: (file: FileItem) => void
onUploadSucceed?: (file: FileItem) => void
onUploadFailed?: (file: FileItem, error: any) => void
onUploadProgress?: (file: FileItem, progress: number) => void
onUploadEnd?: () => void
}
export function useUpload(options: UploadOptions = {}) {
options = Object.assign({ autoUpload: true, multiple: false }, options)
const files = ref<FileItem[]>([])
const uploading = ref(false)
function addFile(file: File) {
// 检查文件是否已经在队列中
const existingFileIndex = files.value.findIndex(f => f.name === file.name)
// 如果文件已经在队列中,并且上传已经开始,则直接返回
if (existingFileIndex !== -1 && files.value[existingFileIndex].progress > 0) return
if (!options.multiple) stopUpload()
const abortController = new AbortController()
files.value.push({ url: '', name: file.name, file, progress: 0, abortController })
if (options.autoUpload && !uploading.value) startUpload()
}
async function startUpload() {
uploading.value = true
for (const file of files.value) {
if (file.progress === 0) {
// 只处理尚未开始上传的文件
try {
options?.onUploadStarted?.(file)
await uploadFile(file)
options?.onUploadSucceed?.(file)
} catch (error) {
options?.onUploadFailed?.(file, error)
}
}
}
uploading.value = false
options?.onUploadEnd?.()
}
async function uploadFile(file: FileItem) {
const {
data: { detail }
} = await getLocalFileChunk({ file_name: file.file.name, file_size: file.file.size })
const fileName = detail.file_name
const chunkSize = detail.chunk_size
const totalChunks = Math.ceil(file.file.size / chunkSize)
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const chunk = file.file.slice(chunkIndex * chunkSize, (chunkIndex + 1) * chunkSize)
await uploadChunk({ file, chunk, chunkIndex, fileName, totalChunks })
}
}
async function uploadChunk({
file,
chunk,
chunkIndex,
fileName,
totalChunks
}: {
file: FileItem
chunk: Blob
chunkIndex: number
fileName: string
totalChunks: number
}) {
const {
data: { detail }
} = await uploadLocalFile(
{ file_name: fileName, file: chunk, now_package_num: chunkIndex + 1, total_package_num: totalChunks },
{
signal: file.abortController.signal,
onUploadProgress(event: ProgressEvent) {
updateProgress({ file, event, chunkIndex, totalChunks })
}
}
)
file.url = detail.uri
}
function updateProgress({ event, chunkIndex, totalChunks, file }: { file: FileItem; event: ProgressEvent; chunkIndex: number; totalChunks: number }) {
if (event.lengthComputable) {
const totalSize = event.total * totalChunks
const loadedSize = event.loaded + chunkIndex * event.total
const progressPercent = (loadedSize / totalSize) * 100
file.progress = parseFloat(progressPercent.toFixed(2))
options?.onUploadProgress?.(file, file.progress)
}
}
function cancelUpload(file: FileItem) {
file.abortController.abort()
files.value = files.value.filter(item => item.name !== file.name)
}
function stopUpload() {
files.value.forEach(cancelUpload) // 停止所有文件的上传
files.value.length = 0
uploading.value = false // 更新上传状态
}
return { files, uploading, addFile, startUpload, cancelUpload, stopUpload }
}
<script setup lang="ts"> <script setup lang="ts">
import VideoDetail from './VideoDetail.vue' import VideoDetail from './VideoDetail.vue'
import { getCreateAuth, updateAuth } from '@/api/base'
import { CircleClose } from '@element-plus/icons-vue' import { CircleClose } from '@element-plus/icons-vue'
const idShowMore = ref(false) import { useUpload } from '@/composables/useUpload'
// uploadInfo 包含要上传的文件信息 const idShowMore = ref(false)
interface UploadInfo {
bucket: string
checkpoint: { file: File; name: string; fileSize: number; partSize: number; uploadId: string }
endpoint: string
file: File
fileHash: string
isImage: boolean
loaded: number
object: string
region: string
retry: boolean
ri: string
state: string
userData: string
videoId: string
videoInfo: any
progress: number
}
const emit = defineEmits(['upload', 'canClose']) const emit = defineEmits(['upload', 'canClose'])
let uploader = createUploader() const { files, addFile, cancelUpload } = useUpload({
let fileList = $ref<UploadInfo[]>([]) multiple: true,
onUploadSucceed(file) {
emit('upload', file)
}
})
const fileChange = (event: Event) => { const fileChange = (event: Event) => {
const element = event.currentTarget as HTMLInputElement const element = event.currentTarget as HTMLInputElement
let files: FileList | null = element.files let files: FileList | null = element.files
if (!files) return if (!files) return
for (const file of files) { for (const file of files) {
// 是否重复上传 addFile(file)
const hasRepeat = !!fileList.find(
item =>
item.file.name === file.name && item.file.size === file.size && item.file.lastModified === file.lastModified
)
!hasRepeat && uploader.addFile(file, null, null, null, '{"Vod":{}}')
} }
uploader.startUpload()
fileList = uploader._uploadList
}
function updateFileList(uploadInfo: UploadInfo) {
if (!uploadInfo) return
fileList = fileList.map(item => {
if (item.ri === uploadInfo.ri) {
return { ...item, ...uploadInfo }
}
return item
})
}
function createUploader() {
return new window['AliyunUpload'].Vod({
//userID,必填,您可以使用阿里云账号访问账号中心(https://account.console.aliyun.com/),即可查看账号ID
userId: '1303984639806000',
//上传到视频点播的地域,默认值为'cn-shanghai',
//eu-central-1,ap-southeast-1
region: 'cn-shanghai',
//分片大小默认1 MB,不能小于100 KB(100*1024)
partSize: 1048576,
//并行上传分片个数,默认5
parallel: 5,
//网络原因失败时,重新上传次数,默认为3
retryCount: 3,
//网络原因失败时,重新上传间隔时间,默认为2秒
retryDuration: 2,
//开始上传
onUploadstarted: onUploadStarted,
//文件上传成功
onUploadSucceed: onUploadSucceed,
//文件上传失败
onUploadFailed: onUploadFailed,
//文件上传进度,单位:字节
onUploadProgress: onUploadProgress,
//上传凭证或STS token超时
onUploadTokenExpired: onUploadTokenExpired,
//全部文件上传结束
onUploadEnd: onUploadEnd
})
}
// 开始上传
function onUploadStarted(uploadInfo: UploadInfo) {
console.log('onUploadStarted', uploadInfo)
getCreateAuth({ title: uploadInfo.file.name, file_name: uploadInfo.file.name }).then(res => {
uploader.setUploadAuthAndAddress(uploadInfo, res.data.upload_auth, res.data.upload_address, res.data.source_id)
})
updateFileList(uploadInfo)
emit('canClose', { closeStatus: false })
}
// 文件上传成功
function onUploadSucceed(uploadInfo: UploadInfo) {
console.log('onUploadSucceed', uploadInfo)
updateFileList(uploadInfo)
emit('upload', uploadInfo)
}
//文件上传失败
function onUploadFailed(uploadInfo: UploadInfo, code: number, message: string) {
console.log(uploadInfo, '111111')
console.log('onUploadFailed', uploadInfo, code, message)
updateFileList(uploadInfo)
}
//文件上传进度,单位:字节
function onUploadProgress(uploadInfo: UploadInfo, totalSize: number, loadedPercent: number) {
console.log('onUploadProgress', uploadInfo.file.name, uploadInfo, totalSize, loadedPercent)
updateFileList(uploadInfo)
}
//上传凭证或STS token超时
function onUploadTokenExpired(uploadInfo: UploadInfo) {
console.log('onUploadTokenExpired', uploadInfo)
updateAuth({ source_id: uploadInfo.videoId }).then(res => {
uploader.resumeUploadWithAuth(res.data.UploadAuth)
})
updateFileList(uploadInfo)
}
// 全部文件上传结束
function onUploadEnd(uploadInfo: UploadInfo) {
console.log('onUploadEnd', uploadInfo)
updateFileList(uploadInfo)
emit('canClose', { closeStatus: true })
} }
const handleView = () => { const handleView = () => {
idShowMore.value = true idShowMore.value = true
} }
// 进度条
function percentage(value: number) {
return parseFloat((value ? value * 100 : 0).toFixed(2))
}
// 删除上传文件 // 删除上传文件
const deleteFile = function (index: number) { const deleteFile = function (index: number, item: any) {
console.log(index, 'deleteFile', fileList) console.log(index, 'deleteFile', files)
fileList.splice(index, 1) cancelUpload(item)
uploader.deleteFile(index)
} }
defineExpose({ uploader, fileList: $$(fileList) }) defineExpose({ files: $$(files) })
</script> </script>
<template> <template>
<div class="upload-video" style="display: flex; flex-direction: column; align-items: flex-start"> <div class="upload-video" style="display: flex; flex-direction: column; align-items: flex-start">
...@@ -146,19 +40,19 @@ defineExpose({ uploader, fileList: $$(fileList) }) ...@@ -146,19 +40,19 @@ defineExpose({ uploader, fileList: $$(fileList) })
<input accept=".mp4" type="file" id="fileUpload" multiple @change="fileChange" /> <input accept=".mp4" type="file" id="fileUpload" multiple @change="fileChange" />
</div> </div>
<div class="tips">推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传。</div> <div class="tips">推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传。</div>
<div v-for="(item, index) in fileList.slice(0, 3)" :key="index"> <div v-for="(item, index) in files.slice(0, 3)" :key="index">
<div class="video-info"> <div class="video-info">
<span class="name">{{ item.file?.name }}</span> <span class="name">{{ item.name }}</span>
<el-progress style="width: 200px" :percentage="percentage(item.loaded)" class="view" /> <el-progress style="width: 200px" :percentage="item.progress" class="view" />
<div v-if="percentage(item.loaded) == 100">上传成功</div> <div v-if="item.progress == 100">上传成功</div>
<el-icon v-else @click="deleteFile(index)"><CircleClose /></el-icon> <el-icon v-else @click="deleteFile(index, item)"><CircleClose /></el-icon>
</div> </div>
</div> </div>
<el-link style="padding-top: 3px" :underline="false" type="primary" v-if="fileList.length > 3" @click="handleView" <el-link style="padding-top: 3px" :underline="false" type="primary" v-if="files.length > 3" @click="handleView"
>{{ fileList.length }}个文件,查看更多</el-link >{{ files.length }}个文件,查看更多</el-link
> >
</div> </div>
<VideoDetail :videoList="fileList" v-model:modelValue="idShowMore" v-if="idShowMore === true" /> <VideoDetail :videoList="files" v-model:modelValue="idShowMore" v-if="idShowMore === true" />
</template> </template>
<style lang="scss"> <style lang="scss">
.demo-progress { .demo-progress {
......
<script setup lang="ts"> <script setup lang="ts">
import { getCreateAuth, updateAuth } from '@/api/base'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
/** import { useUpload } from '@/composables/useUpload'
* upload 上传状态 {code: -1(待上传)0(成功) 1(开始上传) 2(上传失败), msg: '上传信息'}
* progress 上传进度
**/
interface IUpload {
code: number
name?: string
msg?: string
progress?: number
videoId?: string
}
let uploadData = $ref<IUpload>({ code: -1 })
const emit = defineEmits(['upload']) const emit = defineEmits(['upload'])
const form: any = reactive({
timeout: '', const { files, uploading, addFile } = useUpload({
partSize: '', onUploadSucceed(file) {
parallel: '', emit('upload', file)
retryCount: '', }
retryDuration: '',
region: 'cn-shanghai',
userId: '1303984639806000',
file: null,
authProgress: 0,
uploader: null,
statusText: ''
}) })
const fileChange = (e: any) => { const file = computed(() => {
form.file = e.target.files[0] return files.value[0]
if (form.file.name.indexOf('.mp4') === -1) { })
const fileChange = async (event: Event) => {
const element = event.currentTarget as HTMLInputElement
const files = element.files
if (!files) return
const [file] = files
if (file.name.indexOf('.mp4') === -1) {
ElMessage('请上传mp4格式视频') ElMessage('请上传mp4格式视频')
return return
} }
var userData = '{"Vod":{}}' addFile(file)
if (form.uploader) {
form.uploader.stopUpload()
form.authProgress = 0
form.statusText = ''
}
form.uploader = createUploader()
form.uploader.addFile(form.file, null, null, null, userData)
form.uploader.startUpload()
}
const createUploader: any = () => {
const w = window as any
const uploader = new w.AliyunUpload.Vod({
timeout: form.timeout || 60000,
partSize: form.partSize || 1048576,
parallel: form.parallel || 5,
retryCount: form.retryCount || 3,
retryDuration: form.retryDuration || 2,
region: form.region,
userId: form.userId,
// 开始上传
onUploadstarted: function (uploadInfo: any) {
const fileData = JSON.parse(window.localStorage.fileData || '{}')
// 判断有没有上传过
const isFile = !!fileData.sourceId
if (!isFile) {
// 没上传过请求凭证上传
getCreateAuth({ title: uploadInfo.file.name, file_name: uploadInfo.file.name }).then((data: any) => {
window.localStorage.fileData = JSON.stringify({
uploadAuth: data.data.upload_auth,
uploadAddress: data.data.upload_address,
videoId: data.data.source_id,
fileName: uploadInfo.file.name,
fileSize: uploadInfo.file.size
})
uploader.setUploadAuthAndAddress(
uploadInfo,
data.data.upload_auth,
data.data.upload_address,
data.data.source_id
)
})
} else {
// 上传过判断一下上次上传的文件和本次上传的文件一不一样,一样的话继续上传
if (fileData.fileName === uploadInfo.file.name && fileData.fileSize === uploadInfo.file.size) {
uploader.setUploadAuthAndAddress(uploadInfo, fileData.uploadAuth, fileData.uploadAddress, fileData.videoId)
} else {
getCreateAuth({ title: uploadInfo.file.name, file_name: uploadInfo.file.name }).then((data: any) => {
uploader.setUploadAuthAndAddress(
uploadInfo,
data.data.upload_auth,
data.data.upload_address,
data.data.source_id
)
})
}
}
uploadData = {
code: 1,
name: uploadInfo.file.name,
msg: '开始上传'
}
},
// 文件上传成功
onUploadSucceed: function (uploadInfo: any) {
const fileData = window.localStorage.fileData ? JSON.parse(window.localStorage.fileData) : {}
uploadData = {
code: 0,
name: uploadInfo.file.name,
videoId: fileData.videoId,
msg: '上传成功'
}
emit('upload', { videoId: fileData.videoId, name: uploadInfo.file.name })
},
// 文件上传失败
// code:any, message:any
onUploadFailed: function (uploadInfo: any) {
uploadData = {
code: 2,
name: uploadInfo.file.name,
msg: '文件上传失败'
}
},
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
onUploadProgress: function (uploadInfo: any, totalSize: any, progress: any) {
let progressPercent = Math.ceil(progress * 100)
form.authProgress = progressPercent
uploadData.progress = progressPercent
},
// 上传凭证超时
onUploadTokenExpired: function (uploadInfo: any) {
const fileData = JSON.parse(window.localStorage.fileData || '{}')
updateAuth({ source_id: fileData.videoId }).then(({ data }) => {
let uploadAuth = data.UploadAuth
window.localStorage.fileData = JSON.stringify({
uploadAuth: data.data.upload_auth,
uploadAddress: data.data.upload_address,
videoId: data.data.source_id,
fileName: uploadInfo.file.name,
fileSize: uploadInfo.file.size
})
uploader.resumeUploadWithAuth(uploadAuth)
})
}
})
return uploader
} }
</script> </script>
<template> <template>
...@@ -148,18 +30,16 @@ const createUploader: any = () => { ...@@ -148,18 +30,16 @@ const createUploader: any = () => {
<div class="upload-video"> <div class="upload-video">
<div class="upload-btn"> <div class="upload-btn">
本地文件 本地文件
<!-- accept=".mp4" --> <input accept=".mp4" type="file" id="fileUpload" @change="fileChange" />
<input accept=".mp4" type="file" id="fileUpload" @change="fileChange($event)" />
</div> </div>
<div class="demo-progress" v-if="uploadData.code === 1"> <div class="demo-progress" v-if="uploading">
<el-progress style="width: 340px" :percentage="uploadData.progress" /> <el-progress style="width: 340px" :percentage="file.progress" />
<!-- <span> {{ uploadData.progress }}% </span> -->
</div> </div>
<div class="error video-info" v-if="uploadData.code === 2"> <!-- <div class="error video-info" v-if="uploadData.code === 2">
<div class="name">上传失败(请重新选择文件进行上传)</div> <div class="name">上传失败(请重新选择文件进行上传)</div>
</div> </div> -->
<div class="video-info" v-if="uploadData.code === 0"> <div class="video-info" v-else>
<div class="name">{{ uploadData.name }}</div> <div class="name">{{ file?.name }}</div>
</div> </div>
</div> </div>
<div class="tips">推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传。</div> <div class="tips">推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传。</div>
......
<script lang="ts" setup> <script lang="ts" setup>
interface UploadInfo { interface UploadInfo {
bucket: string
checkpoint: { file: File; name: string; fileSize: number; partSize: number; uploadId: string }
endpoint: string
file: File file: File
fileHash: string
isImage: boolean
loaded: number
object: string
region: string
retry: boolean
ri: string
state: string
userData: string
videoId: string
videoInfo: any
progress: number progress: number
} }
interface Props { interface Props {
...@@ -40,19 +26,13 @@ const handleSizeChange = (val: any) => { ...@@ -40,19 +26,13 @@ const handleSizeChange = (val: any) => {
const handleCurrentChange = (val: any) => { const handleCurrentChange = (val: any) => {
page.currentPage = val page.currentPage = val
} }
function percentage(value: number) {
return parseFloat((value ? value * 100 : 0).toFixed(2))
}
</script> </script>
<template> <template>
<el-dialog :model-value="props.modelValue" title="更多视频文件" :before-close="handleCancel" width="30vw"> <el-dialog :model-value="props.modelValue" title="更多视频文件" :before-close="handleCancel" width="30vw">
<div <div v-for="(item, index) in props.videoList.slice((page.currentPage - 1) * page.size, page.currentPage * page.size)" :key="index">
v-for="(item, index) in props.videoList.slice((page.currentPage - 1) * page.size, page.currentPage * page.size)"
:key="index"
>
<div class="video-info"> <div class="video-info">
<span class="name">{{ item.file?.name }}</span> <span class="name">{{ item.file?.name }}</span>
<el-progress style="width: 200px" :percentage="percentage(item.loaded)" class="view" /> <el-progress style="width: 200px" :percentage="item.progress" class="view" />
</div> </div>
</div> </div>
<el-pagination <el-pagination
...@@ -62,8 +42,7 @@ function percentage(value: number) { ...@@ -62,8 +42,7 @@ function percentage(value: number) {
:page-sizes="[5, 10, 20, 30, 50]" :page-sizes="[5, 10, 20, 30, 50]"
:total="props.videoList.length" :total="props.videoList.length"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange">
>
</el-pagination> </el-pagination>
</el-dialog> </el-dialog>
</template> </template>
......
...@@ -2,15 +2,14 @@ ...@@ -2,15 +2,14 @@
import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue' import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue'
const props = defineProps(['data']) const props = defineProps(['data'])
const videoOptions = $computed(() => { const videoOptions = $computed(() => {
return { const playInfoList = props.data.play_auth.play_info_list
sources: [ let src = ''
{ if (Array.isArray(playInfoList)) {
src: props.data.play_auth.play_info_list.find((item: any) => { src = playInfoList.find((item: any) => item.Definition === 'SD')?.PlayURL
return item.Definition === 'SD' } else {
}).PlayURL src = playInfoList
}
]
} }
return { sources: [{ src }] }
}) })
</script> </script>
<template> <template>
......
...@@ -39,13 +39,14 @@ const uploadMultipleVideoRef = $ref<InstanceType<typeof UploadMultipleVideo> | n ...@@ -39,13 +39,14 @@ const uploadMultipleVideoRef = $ref<InstanceType<typeof UploadMultipleVideo> | n
// } // }
// 上传视频成功 // 上传视频成功
const uploadVideo = (data: any) => { const uploadVideo = (data: any) => {
const { file, videoId } = data const { file } = data
const params = { const params = {
name: file.name.slice(0, file.name.lastIndexOf('.')), name: file.name.slice(0, file.name.lastIndexOf('.')),
source: '2', source: '2',
classification: form.classification, classification: form.classification,
knowledge_points: '', knowledge_points: '',
source_id: videoId, source_id: data.url,
size: file.size,
cover: '' cover: ''
} }
createVideo(params).then(() => { createVideo(params).then(() => {
...@@ -71,8 +72,7 @@ const goBack = function () { ...@@ -71,8 +72,7 @@ const goBack = function () {
:data="selectTree" :data="selectTree"
clearable clearable
placeholder="视频分类" placeholder="视频分类"
:default-expanded-keys="selectTree.length ? [selectTree[0]?.id] : []" :default-expanded-keys="selectTree.length ? [selectTree[0]?.id] : []" />
/>
</el-form-item> </el-form-item>
<el-form-item label="视频文件" prop="source_id" v-if="form.classification !== ''"> <el-form-item label="视频文件" prop="source_id" v-if="form.classification !== ''">
<UploadMultipleVideo @upload="uploadVideo" @canClose="handleClose" ref="uploadMultipleVideoRef" /> <UploadMultipleVideo @upload="uploadVideo" @canClose="handleClose" ref="uploadMultipleVideoRef" />
......
...@@ -60,7 +60,7 @@ const swiperItemHandle = (url: string) => { ...@@ -60,7 +60,7 @@ const swiperItemHandle = (url: string) => {
// form表单 // form表单
let form = reactive({ let form = reactive({
data: { name: '', source: '2', classification: '', knowledge_points: '', cover: '', source_id: '' } data: { name: '', source: '2', classification: '', knowledge_points: '', cover: '', source_id: '', size: '' }
}) })
// 表单验证 // 表单验证
const rules = { const rules = {
...@@ -139,7 +139,8 @@ const protocol = ref(false) ...@@ -139,7 +139,8 @@ const protocol = ref(false)
// 上传视频成功 // 上传视频成功
const uploadVideo = (data: any) => { const uploadVideo = (data: any) => {
form.data.source_id = data.videoId form.data.source_id = data.url
form.data.size = data.file.size
const name = data.name const name = data.name
form.data.name = name.slice(0, name.lastIndexOf('.')) form.data.name = name.slice(0, name.lastIndexOf('.'))
} }
...@@ -192,7 +193,7 @@ const changeProtocol = (data: any) => { ...@@ -192,7 +193,7 @@ const changeProtocol = (data: any) => {
<el-icon :size="50" color="#ccc" v-if="form.data.cover == ''"> <el-icon :size="50" color="#ccc" v-if="form.data.cover == ''">
<PictureFilled></PictureFilled> <PictureFilled></PictureFilled>
</el-icon> </el-icon>
<div v-else class="cover-img" :style="`background-image:url(${form.data.cover})`"></div> <div v-else class="cover-img" :style="`background-image:url('${form.data.cover}')`"></div>
</div> </div>
<div class="video-cover_right"> <div class="video-cover_right">
<UploadImg accept=".jpg,.jpeg,.gif,.png" v-model="form.data.cover"></UploadImg> <UploadImg accept=".jpg,.jpeg,.gif,.png" v-model="form.data.cover"></UploadImg>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论