Nuxt3+naive-ui表单和上传插件的使用,上传视频,上传图片自定义上传

03-18 1550阅读

Nuxt3+naive-ui表单和上传插件的使用

上代码把,代码里有注释,anyscript【捂脸】将就看

  
提交
import { xx, xx} from "@/utils/api" import { NConfigProvider, NSelect, NUpload, NForm, NFormItem } from 'naive-ui' import type { UploadInst, UploadFileInfo, UploadCustomRequestOptions } from 'naive-ui' import { nextTick } from 'vue' // 组件的样式重置,可以根据官网右下角的编辑后导出 const themeOverrides = { common: { primaryColor: '#000000', primaryColorHover: "#000000" }, Form: { feedbackPadding: "0px 0 0 2px", feedbackHeightMedium: "14px", feedbackFontSizeMedium: "12px" } } // 性别选择项 const sexOptions = [ { label: "男", value: 1 }, { label: '女', value: 2 }, { label: '保密', value: 3 } ] let model = reactive({ sex: null }) let photos = ref([]) let videos = ref([]) let errorsObj = reactive({ sex: '', photos: '', videos: '' }) const statetoken = useUserToken() let auth = statetoken.value['token'] ? statetoken.value['token'] : '' let videosList = ref([]) //主要为了自定义上传后,原本进入页面回显的视频拿不到一些自定义添加的字段,所以用它来另外存一下 let photosList = ref([]) //主要为了自定义上传后,原本进入页面回显的视频拿不到一些自定义添加的字段,所以用它来另外存一下 // 视频播放相关设置 const options = reactive({ width: "800px", //播放器高度 height: "450px", //播放器高度 color: "#409eff", //主题色 title: "", //视频名称 src: "https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/IronMan.mp4", //视频源 poster: '', muted: false, //静音 webFullScreen: false, speedRate: ["0.75", "1.0", "1.25", "1.5", "2.0"], //播放倍速 autoPlay: false, //自动播放 loop: false, //循环播放 mirror: false, //镜像画面 ligthOff: false, //关灯模式 volume: 0.3, //默认音量大小 control: true, //是否显示控制 controlBtns: [ "audioTrack", "quality", "speedRate", "volume", "setting", "pip", "pageFullScreen", "fullScreen", ], //显示所有按钮, }); const iframeURL = ref('') const videoVisible = ref(false) const getFileURL = (file) => { var url = null; if (window.URL != undefined) { // mozilla(firefox) url = window.URL.createObjectURL(file); } else if (window.webkitURL != undefined) { // webkit or chrome url = window.webkitURL.createObjectURL(file); } return url; } // 截取视频第一帧 const getVideoBase64 = (url) => { return new Promise(function (resolve) { let dataURL = ""; const video = document.createElement("video"); video.setAttribute("crossOrigin", "anonymous"); // 处理跨域 video.setAttribute("src", url); video.setAttribute("preload", "auto"); video.addEventListener("loadeddata", function () { const canvas = document.createElement("canvas"); console.log("video.clientWidth", video.videoWidth); // 视频宽 console.log("video.clientHeight", video.videoHeight); // 视频高 const width = video.videoWidth || 400; // canvas的尺寸和图片一样 const height = video.videoHeight || 240; // 设置默认宽高为 400 240 canvas.width = width; canvas.height = height; canvas.getContext("2d").drawImage(video, 0, 0, width, height); // 绘制canvas dataURL = canvas.toDataURL("image/jpeg"); // 转换为base64 resolve(dataURL); }) }) } // base64转图片file const getFileFromBase64 = (base64URL, filename) => { var arr = base64URL.split(","), bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, { type: "image/png" }); } // 上传视频 const customRequestVideo = async ({ file, onFinish, onError }: UploadCustomRequestOptions) => { console.log('file', file) const objUrl = getFileURL(file.file as File) const objBase = await getVideoBase64(objUrl) // 截取视频第一帧 const objCoverFile = await getFileFromBase64(objBase, `${file.name.split('.')[0]}.jpg`) // 第一帧转file const videoformData = new FormData() videoformData.append('file', file.file as File) videoformData.append('cover', objCoverFile as File) // 后端要我截取封面图传给他,话说在服务端截取不好一些吗,谁叫我人微言轻呢 const { data: dataV, pending, refresh, error } = await useFetch('http://xxx/upload/video', { key: uuid(), method: 'post', headers: { Authorization: auth }, body: videoformData, }) console.log('接口返回', dataV.value['data']) // 上传file给后端后,接口返回的视频地址和封面图地址,加到videos的url和thumbnaiUrl上 if (dataV.value['success']) { onFinish() errorsObj.videos = '' videos.value.map(it => { if (it.id === file.id) { it.url = dataV.value['data']['file']['path'] it.status = 'finished' it.thumbnailUrl = dataV.value['data']['cover']['path'] it.type = null // 一定要把type设置为null,不然视频的类型为video/mp4,在页面缩率图thumbnailUrl会不起作用 it['extension'] = dataV.value['data']['file']['extension'] // 提交的时候后端需要的自定义参数 it['size'] = dataV.value['data']['file']['size'] // 提交的时候后端需要的自定义参数 videosList.value.push(it) // 提交的时候后端需要的自定义参数在添加文件之后之前videos里的会没有,所以我要用videosList重新保存一下,并在删除的时候要从videosList里删除 } }) } else { onError() errorsObj.photos = dataV.value['data']['errors']['image'] + ',请删除后重新选择' } console.log('videos', videos.value, videosList.value) } // 删除视频 const handleRemoveVideo = (data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) => { console.log('remove', data.file) let arr = [] videosList.value.forEach(it => { if (it.id !== data.file.id) { arr.push(it) } }) videosList.value = arr console.log('jj', videosList.value) } // 预览视频,把视频的url给iframe const handlePreviewVideo = (file: UploadFileInfo) => { console.log('预览', file) options.src = file['url'] options.poster = file['thumbnailUrl'] iframeURL.value = file['url']l videoVisible.value = true } // 上传照片 const customRequestPhoto = async ({ file, onFinish, onError, onProgress }: UploadCustomRequestOptions) => { console.log('file', file) const photoformData = new FormData() photoformData.append('image', file.file as File) const { data: dataV, pending, refresh, error } = await useFetch('http://xxx/upload/image', { key: uuid(), method: 'post', headers: { Authorization: 'auth }, body: photoformData, }) console.log('接口返回', dataV.value['data']) if (dataV.value['success']) { onFinish() // 上传接口成功调用后记得调用onFinish,不然你上传的时候看看file都打印的啥 errorsObj.photos = '' photos.value.map(it => { if (it.id === file.id) { it.url = dataV.value['data']['file']['path'] it['extension'] = dataV.value['data']['file']['extension'] it['size'] = dataV.value['data']['file']['size'] photosList.value.push(it) } }) console.log('photos', photos.value, photosList.value) } else { errorsObj.photos = dataV.value['data']['errors']['image'] + ',请删除后重新选择' onError() } } // 删除照片 const handleRemovePhoto = (data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) => { console.log('remove', data.file) let arr = [] photosList.value.forEach(it => { if (it.id !== data.file.id) { arr.push(it) } }) photosList.value = arr } // 提交 const handleSubmit = async () => { console.log('提交', videos.value, videosList.value) console.log('提交', photos.value, photosList.value) // 因为videos.value和photos.value里一些自己加的字段没有,所以采用videosList.value 和photosList.value数据进行提交 } // url转file const getImageFileFromUrl = (url, imageName, type) => { return new Promise((resolve, reject) => { let blob = null; let imgFile = null; let xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.setRequestHeader("Accept", type); // xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // xhr.setAttribute("crossOrigin", "anonymous"); // 处理跨域 xhr.responseType = "blob"; xhr.onload = () => { blob = xhr.response; imgFile = new File([blob], imageName, { type }); resolve(imgFile); }; xhr.onerror = e => { reject(e); }; xhr.send(); }); } // 获取已设置 const getSetInfo = async () => { await nextTick() getInfo({ key: uuid() }).then(async (res) => { model.sex= res.sex res.videos.length && res.videos.forEach(async (it, index) => { let obj = {} obj['id'] = index.toString() obj['name'] = it.name obj['status'] = 'finished' // 要给视频设置已完成状态,不然预览不了 obj['url'] = it.path // 设置视频的url,path是后端返回的url字段 obj['thumbnailUrl'] = it.cover // 要给视频设置封面图 v.push(obj) }) res.photos.length && res.photos.forEach(async (it, index) => { let ff = await getImageFileFromUrl(it.path, it.name, "image/png") // 一个把url转为file文件的方法 let obj = {} obj['id'] = index.toString() obj['name'] = it.name obj['status'] = 'finished' obj['url'] = it.path obj['file'] = ff //这个file可以不需要,只是调用customRequestPhoto photos.value里一开始回显的文件file会没有 obj['size'] = it.size obj['extension'] = it.extension // 提交的时候后端需要的一些字段,本身插件没有该字段 obj['thumbnailUrl'] = it.path p.push(obj) }) setTimeout(() => { videos.value = v photos.value = p photosList.value = p videosList.value = v console.log('set-p', photos.value, photosList.value) console.log('set', videos.value, videosList.value) }, 500) }) } getSetInfo()

Nuxt3+naive-ui表单和上传插件的使用,上传视频,上传图片自定义上传

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]