修改视频部分内容

This commit is contained in:
qingyu 2025-06-26 17:27:48 +08:00
parent a47ab30f19
commit 0504a726ae
5 changed files with 274 additions and 19 deletions

View File

@ -0,0 +1,192 @@
<template>
<div class="clearfix">
<a-upload list-type="picture-card" :multiple="multiple" v-model:file-list="fileList" @preview="handlePreview"
@change="handleChange" :customRequest="handleCustomRequest" :beforeUpload="beforeUpload">
<div v-if="fileList.length < fileNumber" >
<plus-outlined />
<div class="ant-upload-text">上传</div>
</div>
</a-upload>
<a-modal :open="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" v-if="fileType==='img'" style="width: 100%" :src="previewImage" />
<video v-if="fileType==='video'" :src="previewImage" controls autoplay loop muted ></video>
</a-modal>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { UploadOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { config } from '@/config'
import apis from '@/apis'
defineOptions({
name: 'GxUpload',
})
const fileType=ref('img')
const previewVisible = ref(false)
const previewImage = ref('')
const props = defineProps({
modelValue: { type: Array, default: () => [] },
headers: { type: Object, default: () => ({}) },
multiple: { type: Boolean, default: false },
maxSize: { type: Number, default: 10 }, // MB
acceptTypes: { type: String, default: '*' },
listType: { type: String, default: 'text' },
disabled: { type: Boolean, default: false },
uploadText: { type: String },
fileNumber: { type: Number, default: 6 },
width: {
type: Number,
default: 120,
},
height: {
type: Number,
default: 120,
},
});
const emit = defineEmits(['update:modelValue', 'uploadSuccess', 'uploadError']);
const uploadUrl = config('http.apiBasic') + '/api/v1/upload'
const fileList = ref([]);
//
onMounted(() => {
console.log(' props.modelValue', props.modelValue)
fileList.value = props.modelValue.map(url => ({
uid: `preview-${Date.now()}-${Math.random()}`,
name: url.substring(url.lastIndexOf('/') + 1),
status: 'done',
url: url
}));
});
//
const beforeUpload = (file) => {
// const isValidType = props.acceptTypes === '*' ||
// props.acceptTypes.split(',').some(type => file.name.endsWith(type.replace('*', '')));
// const isValidSize = file.size / 1024 / 1024 < props.maxSize;
// if (!isValidType) {
// message.error(` ${props.acceptTypes} `);
// return false;
// }
// if (!isValidSize) {
// message.error(` ${props.maxSize}MB`);
// return false;
// }
return true;
};
const handleCancel = () => {
previewVisible.value = false;
};
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 handlePreview = async (file) => {
console.log(file.name)
const list=['.avi','.mp4','.mov','.wmv','.mkv','.m4v']
const fileSuffix=file.name.substring(file.name.lastIndexOf('.'))
if(list.includes(fileSuffix)){
fileType.value='video'
}
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj)
}
previewImage.value = file.url || file.preview;
console.log(previewImage.value)
previewVisible.value = true;
};
// handleChange
const handleChange = ({ file, fileList: updatedList }) => {
//
if (file.status === 'done') {
const response = file.response;
if (response && response.url) {
// URL
const targetFile = updatedList.find(f => f.uid === file.uid);
if (targetFile) {
targetFile.url = response.url;
message.success(`${file.name} 上传成功`);
}
//
const urls = updatedList
.filter(item => item.status === 'done')
.map(item => item.url);
emit('update:modelValue', urls);
}
} else if (file.status === 'error') {
message.error(`${file.name} 上传失败`);
}
};
//
const handleCustomRequest = async (options) => {
const { file, onProgress, onSuccess, onError } = options;
try {
const formData = new FormData();
formData.append('file', file);
const { data } = await apis.common.uploadFile(formData);
const fullUrl = config('http.apiBasic') + data;
//
onSuccess({
uid: file.uid,
name: file.name,
status: 'done',
url: fullUrl,
response: { url: fullUrl } // responseURL
}, file);
//
emit('uploadSuccess', data);
} catch (err) {
onError(err);
message.error('上传失败');
emit('uploadError', err);
}
};
// v-model
watch(() => props.modelValue, (newVal) => {
// fileList.value=[...newVal]
//
const doneFiles = fileList.value.filter(f => f.status === 'done');
const doneUrls = doneFiles.map(f => f.url);
//
if (JSON.stringify(newVal) !== JSON.stringify(doneUrls)) {
fileList.value = [
...newVal.map(url => {
return {
uid: `preview-${Date.now()}-${Math.random()}`,
name: url.substring(url.lastIndexOf('/') + 1),
status: 'done',
url: url
}
}),
...fileList.value.filter(f => f.status !== 'done')
];
}
}, { deep: true });
</script>
<style>
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
</style>

View File

@ -164,8 +164,9 @@ function customRequest(file) {
} else { } else {
fileList.value = [record] fileList.value = [record]
} }
console.log(loading.value)
if (!loading.value) { if (!loading.value) {
console.log(1111)
doUpload() doUpload()
} }
} }
@ -210,24 +211,31 @@ async function doUpload() {
const record = fileList.value[index] const record = fileList.value[index]
record.status = STATUS_ENUM.getValue('uploading') record.status = STATUS_ENUM.getValue('uploading')
// try {
await simulateProgress(record) await simulateProgress(record)
//
const formData = new FormData() const formData = new FormData()
formData.append('file', record.file) formData.append('file', record.file)
const { success, data } = await apis.common.uploadFile(formData) const { success, data } = await apis.common.uploadFile(formData)
if (config('http.code.success') === success) { if (config('http.code.success') === success) {
record.percent = 100
record.status = STATUS_ENUM.getValue('done') record.status = STATUS_ENUM.getValue('done')
record.src = data.url || record.src // record.src = data.url || record.src
emitValue() emitValue()
await doUpload() await doUpload() //
} else { } else {
record.status = STATUS_ENUM.getValue('error') record.status = STATUS_ENUM.getValue('error')
message.error('上传失败,请重试')
}
} catch (err) {
record.status = STATUS_ENUM.getValue('error')
message.error('上传异常,请检查网络或稍后再试')
} }
} }
async function simulateProgress(record) { async function simulateProgress(record) {
return new Promise((resolve) => { return new Promise((resolve) => {
let percent = 0 let percent = 0

View File

@ -328,3 +328,9 @@ export const myTrim = (str, char, type = 'right') => {
} }
return str.replace(/^\s+|\s+$/g, '') return str.replace(/^\s+|\s+$/g, '')
} }
/**截取图片路径域名部分 */
export const spliceUrl=(fullUrl)=>{
if(!fullUrl) return null
const pathOnly = fullUrl.replace(/^https?:\/\/[^\/]+/, '');
return pathOnly
}

View File

@ -46,7 +46,8 @@
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
<a-form-item :label="'视频'" name="videoUrl"> <a-form-item :label="'视频'" name="videoUrl">
<x-upload-video v-model="videoUrl" @imgChange="(val) => imgChange(val, 'videoUrl')" /> <!-- <x-upload-video v-model="videoUrl" @imgChange="(val) => imgChange(val, 'videoUrl')" />-->
<gx-upload v-model="videoUrl" :fileNumber="1" />
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- <a-col :span="24">--> <!-- <a-col :span="24">-->
@ -86,6 +87,8 @@ import { useForm, useModal } from '@/hooks'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import GxUpload from '@/components/GxUpload/index.vue'
import { spliceUrl } from '@/utils/util'
const emit = defineEmits(['ok']) const emit = defineEmits(['ok'])
const { t } = useI18n() // t const { t } = useI18n() // t
const { modal, showModal, hideModal, showLoading, hideLoading } = useModal() const { modal, showModal, hideModal, showLoading, hideLoading } = useModal()
@ -96,7 +99,7 @@ const rolesValue = ref([])
const roles = ref([]) const roles = ref([])
const fullImg = ref('') const fullImg = ref('')
const smallImg = ref('') const smallImg = ref('')
const videoUrl = ref('') const videoUrl = ref([''])
formRules.value = { formRules.value = {
title: { required: true, message: '请输入标题' }, title: { required: true, message: '请输入标题' },
subheading: { required: false, message: '请输入内容' }, subheading: { required: false, message: '请输入内容' },
@ -145,7 +148,7 @@ async function handleEdit(record = {}) {
console.log(formData.value) console.log(formData.value)
fullImg.value = config('http.apiBasic') + data.fullImg fullImg.value = config('http.apiBasic') + data.fullImg
smallImg.value = config('http.apiBasic') + data.smallImg smallImg.value = config('http.apiBasic') + data.smallImg
videoUrl.value = config('http.apiBasic') + data.videoUrl videoUrl.value = [config('http.apiBasic') + data.videoUrl]
} }
/** /**
* 确定 * 确定
@ -156,13 +159,15 @@ function handleOk() {
console.log(values) console.log(values)
try { try {
showLoading() showLoading()
//console.log(videoUrl.value)
const params = { const params = {
...values, ...values,
fullImg: formData.value.fullImg, fullImg: formData.value.fullImg,
smallImg: formData.value.smallImg, smallImg: formData.value.smallImg,
videoUrl: formData.value.videoUrl, videoUrl:videoUrl.value&&spliceUrl(videoUrl.value[0]),
pushAt:dayjs(formData.value.pushAt).format("YYYY-MM-DD"), pushAt:dayjs(formData.value.pushAt).format("YYYY-MM-DD"),
} }
let result = null let result = null
console.log(modal.value.type) console.log(modal.value.type)
switch (modal.value.type) { switch (modal.value.type) {
@ -226,7 +231,7 @@ function formatArr(data, type = '') {
function handleCancel() { function handleCancel() {
fullImg.value = '' fullImg.value = ''
smallImg.value = '' smallImg.value = ''
videoUrl.value = '' videoUrl.value = ['']
hideModal() hideModal()
} }

View File

@ -51,8 +51,11 @@
<template v-if="'smallImg' === column.dataIndex"> <template v-if="'smallImg' === column.dataIndex">
<a-image :width="60" :src="config('http.apiBasic') + record.smallImg || $imageErr.imgErr" /> <a-image :width="60" :src="config('http.apiBasic') + record.smallImg || $imageErr.imgErr" />
</template> </template>
<!-- <template v-if="'videoUrl' === column.dataIndex">-->
<!-- <video :width="60" :src="config('http.apiBasic') + record.videoUrl || $imageErr.imgErr"/>-->
<!-- </template>-->
<template v-if="'videoUrl' === column.dataIndex"> <template v-if="'videoUrl' === column.dataIndex">
<video :width="60" :src="config('http.apiBasic') + record.videoUrl || $imageErr.imgErr"/> <span @click="handlePreview(record.videoUrl)" style="cursor: pointer; color: #1890ff;" >点击预览</span>
</template> </template>
<template v-if="column.dataIndex === 'title'"> <template v-if="column.dataIndex === 'title'">
<a-tooltip :title="record.title"> <a-tooltip :title="record.title">
@ -91,6 +94,24 @@
</a-row> </a-row>
<edit-dialog ref="editDialogRef" @ok="onOk"></edit-dialog> <edit-dialog ref="editDialogRef" @ok="onOk"></edit-dialog>
<!-- 移出表格放在 template 最底部 -->
<el-dialog
v-model="dialogVisible"
title="视频预览"
width="60%"
@close="handleClose"
>
<video
v-if="currentVideoUrl"
controls
autoplay
style="width: 100%"
:src="config('http.apiBasic') + currentVideoUrl"
></video>
</el-dialog>
</template> </template>
<script setup> <script setup>
@ -215,6 +236,29 @@ async function onOk() {
message.success(t('component.message.success.delete')) message.success(t('component.message.success.delete'))
await getPageList() await getPageList()
} }
/**
* 视频预览
*/
const dialogVisible = ref(false)
const currentVideoUrl = ref('')
//
const handlePreview = (videoUrl) => {
if (!videoUrl) {
ElMessage.error('视频地址无效')
return
}
currentVideoUrl.value = videoUrl
dialogVisible.value = true
}
//
const handleClose = () => {
currentVideoUrl.value = ''
dialogVisible.value = false
}
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>