Commit d8eba72c58f003e6e8824396724e134b2ac316cb
1 parent
78e283b6
feat(src/packages): 组合里单个摄像头新增封面上传
Showing
4 changed files
with
146 additions
and
17 deletions
1 | <template> | 1 | <template> |
2 | <div class="videoPlay"> | 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 | <source :src="path" /> | 11 | <source :src="path" /> |
5 | </video> | 12 | </video> |
6 | </div> | 13 | </div> |
7 | </template> | 14 | </template> |
8 | <script lang="ts" setup> | 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 | import videojs, { VideoJsPlayer } from 'video.js' | 17 | import videojs, { VideoJsPlayer } from 'video.js' |
11 | import type { VideoJsPlayerOptions } from 'video.js' | 18 | import type { VideoJsPlayerOptions } from 'video.js' |
12 | import 'video.js/dist/video-js.css' | 19 | import 'video.js/dist/video-js.css' |
@@ -17,6 +24,7 @@ const props = withDefaults( | @@ -17,6 +24,7 @@ const props = withDefaults( | ||
17 | path: string | 24 | path: string |
18 | autoPlay?: boolean | 25 | autoPlay?: boolean |
19 | h?: number | 26 | h?: number |
27 | + poster?: string | ||
20 | }>(), | 28 | }>(), |
21 | { autoPlay: false } | 29 | { autoPlay: false } |
22 | ) | 30 | ) |
@@ -33,19 +41,22 @@ const initPlay = async () => { | @@ -33,19 +41,22 @@ const initPlay = async () => { | ||
33 | controls: true, | 41 | controls: true, |
34 | autoplay: props.autoPlay, | 42 | autoplay: props.autoPlay, |
35 | src: props?.path, | 43 | src: props?.path, |
44 | + poster: props?.poster, | ||
36 | language: 'zh-CN', | 45 | language: 'zh-CN', |
37 | - techOrder: ['html5'] | 46 | + techOrder: ['html5'], |
47 | + preload: 'none' | ||
38 | } | 48 | } |
39 | player = videojs(videoRef.value, options, () => { | 49 | player = videojs(videoRef.value, options, () => { |
40 | - videojs.log('播放器已经准备好了!') | 50 | + videojs.log('Video is reading') |
41 | if (props.autoPlay) { | 51 | if (props.autoPlay) { |
42 | player.play() | 52 | player.play() |
43 | } | 53 | } |
44 | player.on('ended', () => { | 54 | player.on('ended', () => { |
45 | - videojs.log('播放结束了!') | 55 | + videojs.log('Play end') |
46 | }) | 56 | }) |
47 | player.on('error', () => { | 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,10 +69,10 @@ onMounted(() => { | ||
58 | watch( | 69 | watch( |
59 | () => props.path, | 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 | player?.play() | 76 | player?.play() |
66 | } | 77 | } |
67 | }, | 78 | }, |
@@ -74,11 +85,15 @@ onBeforeUnmount(() => { | @@ -74,11 +85,15 @@ onBeforeUnmount(() => { | ||
74 | player?.dispose() | 85 | player?.dispose() |
75 | }) | 86 | }) |
76 | </script> | 87 | </script> |
88 | +<style> | ||
89 | +.vjs-poster { | ||
90 | + background-size: cover !important; | ||
91 | +} | ||
92 | +</style> | ||
77 | <style lang="scss" scoped> | 93 | <style lang="scss" scoped> |
78 | .videoPlay { | 94 | .videoPlay { |
79 | flex: 1; | 95 | flex: 1; |
80 | height: v-bind('`${h}px`'); | 96 | height: v-bind('`${h}px`'); |
81 | - | ||
82 | .video-js { | 97 | .video-js { |
83 | height: 100%; | 98 | height: 100%; |
84 | width: 100%; | 99 | width: 100%; |
@@ -6,6 +6,7 @@ import cloneDeep from 'lodash/cloneDeep' | @@ -6,6 +6,7 @@ import cloneDeep from 'lodash/cloneDeep' | ||
6 | export const option = { | 6 | export const option = { |
7 | dataset: '', | 7 | dataset: '', |
8 | autoplay: false, | 8 | autoplay: false, |
9 | + poster: '' | ||
9 | } | 10 | } |
10 | 11 | ||
11 | export default class Config extends PublicConfigClass implements CreateComponentType { | 12 | export default class Config extends PublicConfigClass implements CreateComponentType { |
1 | <template> | 1 | <template> |
2 | <CollapseItem name="播放器配置" :expanded="true"> | 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 | <setting-item-box name="源地址" :alone="true"> | 25 | <setting-item-box name="源地址" :alone="true"> |
4 | <setting-item> | 26 | <setting-item> |
5 | <n-input-group> | 27 | <n-input-group> |
@@ -16,14 +38,94 @@ | @@ -16,14 +38,94 @@ | ||
16 | </template> | 38 | </template> |
17 | 39 | ||
18 | <script setup lang="ts"> | 40 | <script setup lang="ts"> |
19 | -import { PropType } from 'vue' | 41 | +import { PropType, ref, nextTick } from 'vue' |
20 | import { option } from './config' | 42 | import { option } from './config' |
21 | import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | 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 | optionData: { | 51 | optionData: { |
25 | type: Object as PropType<typeof option>, | 52 | type: Object as PropType<typeof option>, |
26 | required: true | 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 | </script> | 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 | <template> | 1 | <template> |
2 | <div> | 2 | <div> |
3 | - <VideoPlay :h="h" :path="option.dataset" :autoPlay="autoplay" /> | 3 | + <VideoPlay :h="h" :path="option.dataset" :autoPlay="autoplay" :poster="option.poster" /> |
4 | </div> | 4 | </div> |
5 | </template> | 5 | </template> |
6 | <script setup lang="ts"> | 6 | <script setup lang="ts"> |
@@ -18,10 +18,11 @@ const props = defineProps({ | @@ -18,10 +18,11 @@ const props = defineProps({ | ||
18 | 18 | ||
19 | const { h } = toRefs(props.chartConfig.attr) | 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 | const option = shallowReactive({ | 23 | const option = shallowReactive({ |
24 | - dataset: configOption.dataset | 24 | + dataset: configOption.dataset, |
25 | + poster: configOption.poster | ||
25 | }) | 26 | }) |
26 | 27 | ||
27 | watch( | 28 | watch( |
@@ -30,7 +31,17 @@ watch( | @@ -30,7 +31,17 @@ watch( | ||
30 | option.dataset = newData | 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 | </script> | 47 | </script> |