generated from Leo_Ding/web-template
修改视频部分内容
This commit is contained in:
parent
a47ab30f19
commit
0504a726ae
192
src/components/GxUpload/index.vue
Normal file
192
src/components/GxUpload/index.vue
Normal 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 } // 确保response包含URL
|
||||||
|
}, 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>
|
||||||
@ -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)
|
|
||||||
|
|
||||||
if (config('http.code.success') === success) {
|
const { success, data } = await apis.common.uploadFile(formData)
|
||||||
record.status = STATUS_ENUM.getValue('done')
|
|
||||||
record.src = data.url || record.src // 替换为真实地址
|
if (config('http.code.success') === success) {
|
||||||
emitValue()
|
record.percent = 100
|
||||||
await doUpload()
|
record.status = STATUS_ENUM.getValue('done')
|
||||||
} else {
|
record.src = data.url || record.src
|
||||||
|
emitValue()
|
||||||
|
await doUpload() // 上传下一个
|
||||||
|
} else {
|
||||||
|
record.status = STATUS_ENUM.getValue('error')
|
||||||
|
message.error('上传失败,请重试')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
record.status = STATUS_ENUM.getValue('error')
|
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
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user