generated from Leo_Ding/web-template
修改视频部分内容
This commit is contained in:
parent
c1696191c3
commit
1ad5ad954c
351
src/components/Upload/UploadVideo.vue
Normal file
351
src/components/Upload/UploadVideo.vue
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="uploadVideoRef" class="x-upload x-upload-video" :class="{
|
||||||
|
'x-upload--disabled': disabled
|
||||||
|
}">
|
||||||
|
<a-upload
|
||||||
|
v-if="showUploadBtn"
|
||||||
|
:show-upload-list="false"
|
||||||
|
:multiple="multiple"
|
||||||
|
:before-upload="onBeforeUpload"
|
||||||
|
:custom-request="({ file }) => customRequest(file)"
|
||||||
|
:accept="accept"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
<div class="x-upload-btn" :class="{ 'x-upload-btn--hover': !disabled }" :style="{ width: `${width}px`, height: `${height}px` }">
|
||||||
|
<div class="x-upload-btn__icon">
|
||||||
|
<slot name="icon">
|
||||||
|
<plus-outlined />
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div v-if="text" class="x-upload-btn__txt">
|
||||||
|
<slot name="text">
|
||||||
|
{{ text }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</a-upload>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in fileList"
|
||||||
|
:key="item.key"
|
||||||
|
class="x-upload-item j-upload-item"
|
||||||
|
:class="{ 'x-upload-item--error': STATUS_ENUM.is('error', item.status) }"
|
||||||
|
:style="{ width: `${width}px`, height: `${height}px` }"
|
||||||
|
>
|
||||||
|
<video :src="item.src" controls :width="width" :height="height" />
|
||||||
|
|
||||||
|
<template v-if="['error', 'done'].includes(STATUS_ENUM.getKey(item.status))">
|
||||||
|
<div class="x-upload-actions">
|
||||||
|
<div v-if="!disabled" class="x-upload-action" @click="handleRemove(index)">
|
||||||
|
<delete-outlined />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div class="x-upload-status">
|
||||||
|
<template v-if="STATUS_ENUM.is('uploading', item.status)">
|
||||||
|
<div>{{ item.percent }}%</div>
|
||||||
|
<a-progress :show-info="false" :stroke-width="4" :percent="item.percent" />
|
||||||
|
</template>
|
||||||
|
<template v-if="STATUS_ENUM.is('wait', item.status)">
|
||||||
|
<div>{{ STATUS_ENUM.getDesc(item.status) }}</div>
|
||||||
|
<span class="x-upload-action" @click="handleCancel(item)">取消上传</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, computed, watch } from 'vue'
|
||||||
|
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
|
import { filesize } from 'filesize'
|
||||||
|
import filesizeParser from 'filesize-parser'
|
||||||
|
import { findIndex, includes, some } from 'lodash-es'
|
||||||
|
import { STATUS_ENUM } from './config'
|
||||||
|
import apis from '@/apis'
|
||||||
|
import { config } from '@/config'
|
||||||
|
defineOptions({
|
||||||
|
name: 'XUploadVideo',
|
||||||
|
})
|
||||||
|
defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Array],
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 320,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 180,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
maxSize: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '50M',
|
||||||
|
},
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: 'video/*',
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const fileList = ref([])
|
||||||
|
const uploadVideoRef = ref()
|
||||||
|
|
||||||
|
const loading = computed(() => fileList.value.some((o) => STATUS_ENUM.is('uploading', o.status)))
|
||||||
|
const showUploadBtn = computed(() => props.multiple || !fileList.value.length)
|
||||||
|
|
||||||
|
watch(() => props.modelValue, () => init())
|
||||||
|
onMounted(() => init())
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const currentValue = Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue]
|
||||||
|
fileList.value = currentValue.filter(Boolean).map((src) => getItem({ src }))
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBeforeUpload(file) {
|
||||||
|
const maxFileSize = typeof props.maxSize === 'number' ? props.maxSize : filesizeParser(props.maxSize)
|
||||||
|
const isVideo = file.type.startsWith('video/')
|
||||||
|
const isTooLarge = file.size > maxFileSize
|
||||||
|
|
||||||
|
if (!isVideo) {
|
||||||
|
message.error('只能上传视频文件')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTooLarge) {
|
||||||
|
message.error(`文件过大,最大不能超过 ${filesize(maxFileSize)}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function customRequest(file) {
|
||||||
|
const record = getItem({
|
||||||
|
key: file.uid,
|
||||||
|
status: STATUS_ENUM.getValue('wait'),
|
||||||
|
percent: 0,
|
||||||
|
file,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 预览地址设置
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = (e) => {
|
||||||
|
record.src = e.target.result
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
|
||||||
|
if (props.multiple) {
|
||||||
|
fileList.value.push(record)
|
||||||
|
} else {
|
||||||
|
fileList.value = [record]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loading.value) {
|
||||||
|
doUpload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doUpload() {
|
||||||
|
const index = findIndex(fileList.value, { status: STATUS_ENUM.getValue('wait') })
|
||||||
|
if (index === -1) return
|
||||||
|
|
||||||
|
const record = fileList.value[index]
|
||||||
|
record.status = STATUS_ENUM.getValue('uploading')
|
||||||
|
|
||||||
|
// 模拟进度
|
||||||
|
await simulateProgress(record)
|
||||||
|
|
||||||
|
// 模拟上传请求
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', record.file)
|
||||||
|
const { success, data } = await apis.common.uploadFile(formData)
|
||||||
|
|
||||||
|
if (config('http.code.success') === success) {
|
||||||
|
record.status = STATUS_ENUM.getValue('done')
|
||||||
|
record.src = data.url || record.src // 替换为真实地址
|
||||||
|
emitValue()
|
||||||
|
await doUpload()
|
||||||
|
} else {
|
||||||
|
record.status = STATUS_ENUM.getValue('error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function simulateProgress(record) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let percent = 0
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
percent += Math.random() * 10 + 5
|
||||||
|
if (percent >= 95) {
|
||||||
|
clearInterval(interval)
|
||||||
|
record.percent = 95
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
record.percent = Math.floor(percent)
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function handleRemove(index) {
|
||||||
|
fileList.value.splice(index, 1)
|
||||||
|
emitValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel({ key }) {
|
||||||
|
const index = fileList.value.findIndex((o) => o.key === key)
|
||||||
|
fileList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItem(obj) {
|
||||||
|
return {
|
||||||
|
key: nanoid(),
|
||||||
|
src: '',
|
||||||
|
status: STATUS_ENUM.getValue('done'),
|
||||||
|
percent: 100,
|
||||||
|
...obj,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitValue() {
|
||||||
|
const value = props.multiple
|
||||||
|
? fileList.value.filter((item) => STATUS_ENUM.is('done', item.status)).map((item) => item?.src ?? item)
|
||||||
|
: (fileList.value[0]?.src ?? '') || ''
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.x-upload {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
.x-upload-btn {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-btn {
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&--hover:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__txt {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
position: relative;
|
||||||
|
background: #f5f5f5;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.x-upload-actions {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--error::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px dashed red;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-actions {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
min-width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 0 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-status {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
color: #fff;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -16,6 +16,7 @@ import QrCode from './QrCode/QrCode.vue'
|
|||||||
import ResizeBox from './ResizeBox/ResizeBox.vue'
|
import ResizeBox from './ResizeBox/ResizeBox.vue'
|
||||||
import SearchBar from './SearchBar/SearchBar.vue'
|
import SearchBar from './SearchBar/SearchBar.vue'
|
||||||
import UploadImage from './Upload/UploadImage.vue'
|
import UploadImage from './Upload/UploadImage.vue'
|
||||||
|
import UploadVideo from './Upload/UploadVideo.vue'
|
||||||
import UploadInput from './Upload/UploadInput.vue'
|
import UploadInput from './Upload/UploadInput.vue'
|
||||||
import Scrollbar from './Scrollbar/Scrollbar.vue'
|
import Scrollbar from './Scrollbar/Scrollbar.vue'
|
||||||
import Cascader from './Cascader/Cascader.vue'
|
import Cascader from './Cascader/Cascader.vue'
|
||||||
@ -38,6 +39,7 @@ const componentList = [
|
|||||||
ResizeBox,
|
ResizeBox,
|
||||||
SearchBar,
|
SearchBar,
|
||||||
UploadImage,
|
UploadImage,
|
||||||
|
UploadVideo,
|
||||||
UploadInput,
|
UploadInput,
|
||||||
Scrollbar,
|
Scrollbar,
|
||||||
Cascader,
|
Cascader,
|
||||||
|
|||||||
@ -45,27 +45,32 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item label="视频" name="videoUrl">
|
<a-form-item :label="'视频'" name="videoUrl">
|
||||||
<a-upload
|
<x-upload-video v-model="formData.videoUrl" @imgChange="(val) => imgChange(val, 'videoUrl')" />
|
||||||
:action="uploadUrl"
|
|
||||||
:show-upload-list="false"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
:accept="'video/*'"
|
|
||||||
>
|
|
||||||
<a-button type="primary">上传视频</a-button>
|
|
||||||
</a-upload>
|
|
||||||
|
|
||||||
<video
|
|
||||||
v-if="formData.videoUrl"
|
|
||||||
:src="formData.videoUrl"
|
|
||||||
@imgChange="(val) => imgChange(val, 'videoUrl')"
|
|
||||||
controls
|
|
||||||
style="margin-top: 10px; width: 100%; max-width: 600px;"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<!-- <a-col :span="24">-->
|
||||||
|
<!-- <a-form-item label="视频" name="videoUrl">-->
|
||||||
|
<!-- <a-upload-->
|
||||||
|
<!-- :action="uploadUrl"-->
|
||||||
|
<!-- :show-upload-list="false"-->
|
||||||
|
<!-- :before-upload="beforeUpload"-->
|
||||||
|
<!-- :on-success="handleUploadSuccess"-->
|
||||||
|
<!-- :accept="'video/*'"-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <a-button type="primary">上传视频</a-button>-->
|
||||||
|
<!-- </a-upload>-->
|
||||||
|
|
||||||
|
<!-- <video-->
|
||||||
|
<!-- v-if="formData.videoUrl"-->
|
||||||
|
<!-- :src="formData.videoUrl"-->
|
||||||
|
<!-- @imgChange="(val) => imgChange(val, 'videoUrl')"-->
|
||||||
|
<!-- controls-->
|
||||||
|
<!-- style="margin-top: 10px; width: 100%; max-width: 600px;"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </a-form-item>-->
|
||||||
|
|
||||||
|
<!-- </a-col>-->
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-form>
|
</a-form>
|
||||||
@ -241,31 +246,6 @@ defineExpose({
|
|||||||
handleCreate,
|
handleCreate,
|
||||||
handleEdit,
|
handleEdit,
|
||||||
})
|
})
|
||||||
/**
|
|
||||||
* 视频上传
|
|
||||||
*/
|
|
||||||
function beforeUpload(file) {
|
|
||||||
const isVideo = file.type.startsWith('video/')
|
|
||||||
const isLt100M = file.size / 1024 / 1024 < 100
|
|
||||||
if (!isVideo) {
|
|
||||||
message.error('只能上传视频文件')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!isLt100M) {
|
|
||||||
message.error('视频大小不能超过 100MB')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUploadSuccess(response) {
|
|
||||||
formData.value.videoUrl = response.url
|
|
||||||
message.success('视频上传成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
function videoChange(url) {
|
|
||||||
console.log('视频改变了:', url)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped></style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user