Commit d8eba72c58f003e6e8824396724e134b2ac316cb
1 parent
78e283b6
feat(src/packages): 组合里单个摄像头新增封面上传
Showing
4 changed files
with
146 additions
and
17 deletions
| 1 | 1 | <template> |
| 2 | 2 | <div class="videoPlay"> |
| 3 | - <video crossOrigin="anonymous" ref="videoRef" class="video-js vjs-default-skin vjs-big-play-centered" controls> | |
| 3 | + <video | |
| 4 | + style="object-fit: cover" | |
| 5 | + :poster="poster" | |
| 6 | + crossOrigin="anonymous" | |
| 7 | + ref="videoRef" | |
| 8 | + class="video-js vjs-default-skin vjs-big-play-centered" | |
| 9 | + controls | |
| 10 | + > | |
| 4 | 11 | <source :src="path" /> |
| 5 | 12 | </video> |
| 6 | 13 | </div> |
| 7 | 14 | </template> |
| 8 | 15 | <script lang="ts" setup> |
| 9 | -import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue' | |
| 16 | +import { nextTick, onBeforeUnmount, onMounted, ref, watch, toRefs } from 'vue' | |
| 10 | 17 | import videojs, { VideoJsPlayer } from 'video.js' |
| 11 | 18 | import type { VideoJsPlayerOptions } from 'video.js' |
| 12 | 19 | import 'video.js/dist/video-js.css' |
| ... | ... | @@ -17,6 +24,7 @@ const props = withDefaults( |
| 17 | 24 | path: string |
| 18 | 25 | autoPlay?: boolean |
| 19 | 26 | h?: number |
| 27 | + poster?: string | |
| 20 | 28 | }>(), |
| 21 | 29 | { autoPlay: false } |
| 22 | 30 | ) |
| ... | ... | @@ -33,19 +41,22 @@ const initPlay = async () => { |
| 33 | 41 | controls: true, |
| 34 | 42 | autoplay: props.autoPlay, |
| 35 | 43 | src: props?.path, |
| 44 | + poster: props?.poster, | |
| 36 | 45 | language: 'zh-CN', |
| 37 | - techOrder: ['html5'] | |
| 46 | + techOrder: ['html5'], | |
| 47 | + preload: 'none' | |
| 38 | 48 | } |
| 39 | 49 | player = videojs(videoRef.value, options, () => { |
| 40 | - videojs.log('播放器已经准备好了!') | |
| 50 | + videojs.log('Video is reading') | |
| 41 | 51 | if (props.autoPlay) { |
| 42 | 52 | player.play() |
| 43 | 53 | } |
| 44 | 54 | player.on('ended', () => { |
| 45 | - videojs.log('播放结束了!') | |
| 55 | + videojs.log('Play end') | |
| 46 | 56 | }) |
| 47 | 57 | player.on('error', () => { |
| 48 | - videojs.log('播放器解析出错!') | |
| 58 | + player.errorDisplay.close() | |
| 59 | + videojs.log('Play parse error') | |
| 49 | 60 | }) |
| 50 | 61 | }) |
| 51 | 62 | } |
| ... | ... | @@ -58,10 +69,10 @@ onMounted(() => { |
| 58 | 69 | watch( |
| 59 | 70 | () => props.path, |
| 60 | 71 | () => { |
| 61 | - player?.pause() | |
| 62 | - player?.src(props.path) | |
| 63 | - player?.load() | |
| 64 | - if (props.path) { | |
| 72 | + if (props.path && props.autoPlay) { | |
| 73 | + player?.pause() | |
| 74 | + player?.load() | |
| 75 | + player?.src(props.path) | |
| 65 | 76 | player?.play() |
| 66 | 77 | } |
| 67 | 78 | }, |
| ... | ... | @@ -74,11 +85,15 @@ onBeforeUnmount(() => { |
| 74 | 85 | player?.dispose() |
| 75 | 86 | }) |
| 76 | 87 | </script> |
| 88 | +<style> | |
| 89 | +.vjs-poster { | |
| 90 | + background-size: cover !important; | |
| 91 | +} | |
| 92 | +</style> | |
| 77 | 93 | <style lang="scss" scoped> |
| 78 | 94 | .videoPlay { |
| 79 | 95 | flex: 1; |
| 80 | 96 | height: v-bind('`${h}px`'); |
| 81 | - | |
| 82 | 97 | .video-js { |
| 83 | 98 | height: 100%; |
| 84 | 99 | width: 100%; | ... | ... |
| 1 | 1 | <template> |
| 2 | 2 | <CollapseItem name="播放器配置" :expanded="true"> |
| 3 | + <setting-item-box name="上传图片" :alone="true"> | |
| 4 | + <setting-item> | |
| 5 | + <n-card class="upload-box"> | |
| 6 | + <n-upload | |
| 7 | + :show-file-list="false" | |
| 8 | + v-model:file-list="uploadFileListRef" | |
| 9 | + :customRequest="customRequest" | |
| 10 | + :onBeforeUpload="beforeUploadHandle" | |
| 11 | + > | |
| 12 | + <n-upload-dragger> | |
| 13 | + <img v-if="optionData.poster" class="upload-show" :src="optionData.poster" alt="背景" /> | |
| 14 | + <div class="upload-img" v-show="!optionData.poster"> | |
| 15 | + <img src="@/assets/images/canvas/noImage.png" /> | |
| 16 | + <n-text class="upload-desc" depth="3"> | |
| 17 | + 图片需小于 {{ backgroundImageSize }}M ,格式为 png/jpg/gif 的文件 | |
| 18 | + </n-text> | |
| 19 | + </div> | |
| 20 | + </n-upload-dragger> | |
| 21 | + </n-upload> | |
| 22 | + </n-card> | |
| 23 | + </setting-item> | |
| 24 | + </setting-item-box> | |
| 3 | 25 | <setting-item-box name="源地址" :alone="true"> |
| 4 | 26 | <setting-item> |
| 5 | 27 | <n-input-group> |
| ... | ... | @@ -16,14 +38,94 @@ |
| 16 | 38 | </template> |
| 17 | 39 | |
| 18 | 40 | <script setup lang="ts"> |
| 19 | -import { PropType } from 'vue' | |
| 41 | +import { PropType, ref, nextTick } from 'vue' | |
| 20 | 42 | import { option } from './config' |
| 21 | 43 | import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' |
| 44 | +import { FileTypeEnum } from '@/enums/fileTypeEnum' | |
| 45 | +import { uploadFile } from '@/api/external/contentSave/content' | |
| 46 | +import { UploadCustomRequestOptions } from 'naive-ui' | |
| 47 | +import { backgroundImageSize } from '@/settings/designSetting' | |
| 48 | +import { fetchRouteParamsLocation } from '@/utils' | |
| 22 | 49 | |
| 23 | -defineProps({ | |
| 50 | +const props = defineProps({ | |
| 24 | 51 | optionData: { |
| 25 | 52 | type: Object as PropType<typeof option>, |
| 26 | 53 | required: true |
| 27 | 54 | } |
| 28 | 55 | }) |
| 56 | + | |
| 57 | +const uploadFileListRef = ref() | |
| 58 | + | |
| 59 | +// 上传图片前置处理 | |
| 60 | +// eslint-disable-next-line @typescript-eslint/ban-ts-comment | |
| 61 | +//@ts-ignore | |
| 62 | +const beforeUploadHandle = async ({ file }) => { | |
| 63 | + uploadFileListRef.value = [] | |
| 64 | + const type = file.file.type | |
| 65 | + const size = file.file.size | |
| 66 | + | |
| 67 | + if (size > 1024 * 1024 * backgroundImageSize) { | |
| 68 | + window['$message'].warning(`图片超出 ${backgroundImageSize}M 限制,请重新上传!`) | |
| 69 | + return false | |
| 70 | + } | |
| 71 | + if (type !== FileTypeEnum.PNG && type !== FileTypeEnum.JPEG && type !== FileTypeEnum.GIF) { | |
| 72 | + window['$message'].warning('文件格式不符合,请重新上传!') | |
| 73 | + return false | |
| 74 | + } | |
| 75 | + return true | |
| 76 | +} | |
| 77 | + | |
| 78 | +// 自定义上传操作 | |
| 79 | +const customRequest = (options: UploadCustomRequestOptions) => { | |
| 80 | + const { file } = options | |
| 81 | + nextTick(async () => { | |
| 82 | + if (file.file) { | |
| 83 | + // 修改名称 | |
| 84 | + const newNameFile = new File([file.file], `${fetchRouteParamsLocation()}_index_background.png`, { | |
| 85 | + type: file.file.type | |
| 86 | + }) | |
| 87 | + let uploadParams = new FormData() | |
| 88 | + uploadParams.append('file', newNameFile) | |
| 89 | + const uploadRes = await uploadFile(uploadParams) | |
| 90 | + if (uploadRes) { | |
| 91 | + props.optionData.poster = uploadRes?.fileStaticUri | |
| 92 | + window['$message'].success('添加图片成功!') | |
| 93 | + } | |
| 94 | + } else { | |
| 95 | + window['$message'].error('添加图片失败,请稍后重试!') | |
| 96 | + } | |
| 97 | + }) | |
| 98 | +} | |
| 29 | 99 | </script> |
| 100 | +<style lang="scss" scoped> | |
| 101 | +$uploadHeight: 193px; | |
| 102 | +.upload-box { | |
| 103 | + cursor: pointer; | |
| 104 | + margin-bottom: 20px; | |
| 105 | + @include deep() { | |
| 106 | + .n-card__content { | |
| 107 | + padding: 0; | |
| 108 | + overflow: hidden; | |
| 109 | + } | |
| 110 | + .n-upload-dragger { | |
| 111 | + padding: 5px; | |
| 112 | + } | |
| 113 | + } | |
| 114 | + .upload-show { | |
| 115 | + width: -webkit-fill-available; | |
| 116 | + height: $uploadHeight; | |
| 117 | + border-radius: 5px; | |
| 118 | + } | |
| 119 | + .upload-img { | |
| 120 | + display: flex; | |
| 121 | + flex-direction: column; | |
| 122 | + align-items: center; | |
| 123 | + img { | |
| 124 | + height: 150px; | |
| 125 | + } | |
| 126 | + .upload-desc { | |
| 127 | + padding: 10px 0; | |
| 128 | + } | |
| 129 | + } | |
| 130 | +} | |
| 131 | +</style> | ... | ... |
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <VideoPlay :h="h" :path="option.dataset" :autoPlay="autoplay" /> | |
| 3 | + <VideoPlay :h="h" :path="option.dataset" :autoPlay="autoplay" :poster="option.poster" /> | |
| 4 | 4 | </div> |
| 5 | 5 | </template> |
| 6 | 6 | <script setup lang="ts"> |
| ... | ... | @@ -18,10 +18,11 @@ const props = defineProps({ |
| 18 | 18 | |
| 19 | 19 | const { h } = toRefs(props.chartConfig.attr) |
| 20 | 20 | |
| 21 | -const { autoplay, dataset } = toRefs(props.chartConfig.option) | |
| 21 | +const { autoplay, dataset, poster } = toRefs(props.chartConfig.option) | |
| 22 | 22 | |
| 23 | 23 | const option = shallowReactive({ |
| 24 | - dataset: configOption.dataset | |
| 24 | + dataset: configOption.dataset, | |
| 25 | + poster: configOption.poster | |
| 25 | 26 | }) |
| 26 | 27 | |
| 27 | 28 | watch( |
| ... | ... | @@ -30,7 +31,17 @@ watch( |
| 30 | 31 | option.dataset = newData |
| 31 | 32 | }, |
| 32 | 33 | { |
| 33 | - immediate: true, | |
| 34 | + immediate: true | |
| 35 | + } | |
| 36 | +) | |
| 37 | + | |
| 38 | +watch( | |
| 39 | + () => poster?.value, | |
| 40 | + (newData: string) => { | |
| 41 | + option.poster = newData | |
| 42 | + }, | |
| 43 | + { | |
| 44 | + immediate: true | |
| 34 | 45 | } |
| 35 | 46 | ) |
| 36 | 47 | </script> | ... | ... |