forked from github/dataease
feat(仪表板): 支持流媒体视频
This commit is contained in:
parent
b234b4cfa2
commit
cb34fb6659
@ -27,6 +27,7 @@
|
|||||||
"element-plus-secondary": "^0.5.8",
|
"element-plus-secondary": "^0.5.8",
|
||||||
"element-resize-detector": "^1.2.4",
|
"element-resize-detector": "^1.2.4",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
|
"flv.js": "^1.6.2",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"js-base64": "^3.7.5",
|
"js-base64": "^3.7.5",
|
||||||
|
1
core/core-frontend/src/assets/svg/icon-stream.svg
Normal file
1
core/core-frontend/src/assets/svg/icon-stream.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715769658859" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4704" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1023.853735 948.673618v75.25325H596.248536V948.673618zM511.926868 0.073132a511.926868 511.926868 0 0 1 511.926867 511.926868h-75.253249a436.600486 436.600486 0 1 0-436.600486 436.600486h5.119269v75.253249h-5.119269a511.926868 511.926868 0 1 1 0-1023.853735z m511.926867 803.505785v75.25325H596.248536v-75.180117zM362.590487 290.116269l192.11884 110.941866 192.11884 110.941865-192.11884 110.941865-192.11884 110.941866V290.189402zM1023.853735 658.484217v75.399514H596.248536V658.630481zM437.843737 420.438223v182.831024l79.202399-45.780888 79.2024-45.707756-79.2024-45.707756z" p-id="4705"></path></svg>
|
After Width: | Height: | Size: 937 B |
@ -8,6 +8,19 @@ export const commonStyle = {
|
|||||||
opacity: 1
|
opacity: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 流媒体视频信息配置
|
||||||
|
export const STREAMMEDIALINKS = {
|
||||||
|
videoType: 'flv',
|
||||||
|
flv: {
|
||||||
|
type: 'flv',
|
||||||
|
isLive: false,
|
||||||
|
cors: true, // 允许跨域
|
||||||
|
loop: true,
|
||||||
|
autoplay: false
|
||||||
|
// url: null // 网络动画视频
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 视频信息配置
|
// 视频信息配置
|
||||||
export const VIDEO_LINKS_DE2 = {
|
export const VIDEO_LINKS_DE2 = {
|
||||||
videoType: 'web',
|
videoType: 'web',
|
||||||
@ -248,6 +261,25 @@ const list = [
|
|||||||
videoLinks: VIDEO_LINKS_DE2,
|
videoLinks: VIDEO_LINKS_DE2,
|
||||||
matrixStyle: {}
|
matrixStyle: {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'DeStreamMedia',
|
||||||
|
name: '媒体',
|
||||||
|
label: '媒体',
|
||||||
|
innerType: 'DeStreamMedia',
|
||||||
|
editing: false,
|
||||||
|
canvasActive: false,
|
||||||
|
icon: 'icon-stream',
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
sizeX: 36,
|
||||||
|
sizeY: 14,
|
||||||
|
style: {
|
||||||
|
width: 600,
|
||||||
|
height: 300
|
||||||
|
},
|
||||||
|
streamMediaLinks: STREAMMEDIALINKS,
|
||||||
|
matrixStyle: {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'DeFrame',
|
component: 'DeFrame',
|
||||||
name: '网页',
|
name: '网页',
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div class="attr-list de-collapse-style">
|
||||||
|
<CommonAttr :themes="themes" :element="curComponent">
|
||||||
|
<el-collapse-item
|
||||||
|
:effect="themes"
|
||||||
|
title="流媒体信息"
|
||||||
|
name="videoLinks"
|
||||||
|
v-if="curComponent && curComponent.videoLinks"
|
||||||
|
>
|
||||||
|
<video-links :link-info="curComponent.videoLinks" :themes="themes"></video-links>
|
||||||
|
</el-collapse-item>
|
||||||
|
</CommonAttr>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||||
|
import CommonAttr from '@/custom-component/common/CommonAttr.vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import VideoLinks from '@/custom-component/de-video/VideoLinks.vue'
|
||||||
|
const dvMainStore = dvMainStoreWithOut()
|
||||||
|
const { curComponent } = storeToRefs(dvMainStore)
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
themes?: EditorTheme
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
themes: 'dark'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<el-row ref="mainPlayer" style="width: 100%; height: 100%">
|
||||||
|
<div
|
||||||
|
v-if="element.streamMediaLinks[element.streamMediaLinks.videoType].url"
|
||||||
|
class="video-container"
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
:ref="'player-' + element.id"
|
||||||
|
class="centered-video"
|
||||||
|
name="centeredVideo"
|
||||||
|
:loop="state.pOption.loop"
|
||||||
|
:controls="inScreen"
|
||||||
|
muted
|
||||||
|
/>
|
||||||
|
<div v-if="editMode === 'edit'" class="stream-mask edit-mask-stream" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="info-stream-class">
|
||||||
|
<span>{{ $t('panel.link_add_tips_pre') }}</span>
|
||||||
|
<i class="icon iconfont icon-chaolianjie" />
|
||||||
|
<span>{{ $t('panel.stream_media_add_tips') }}</span>
|
||||||
|
</div>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import flvjs from 'flv.js'
|
||||||
|
import '@/style/custom-theme.css'
|
||||||
|
import { onMounted, reactive, toRefs } from 'vue'
|
||||||
|
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
propValue: {
|
||||||
|
type: String,
|
||||||
|
require: true
|
||||||
|
},
|
||||||
|
element: {
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
editMode: {
|
||||||
|
type: String,
|
||||||
|
require: false,
|
||||||
|
default: 'edit'
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
require: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
h: {
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
inScreen: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { propValue, element, editMode } = toRefs(props)
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
pOption: element.value.streamMediaLinks[element.value.streamMediaLinks.videoType],
|
||||||
|
flvPlayer: null,
|
||||||
|
videoShow: true
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
useEmitt({
|
||||||
|
name: 'streamMediaLinksChange-' + element.value.id,
|
||||||
|
callback: function () {
|
||||||
|
initOption()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const initOption = () => {
|
||||||
|
if (flvjs.isSupported() && state.pOption.url) {
|
||||||
|
destroyPlayer()
|
||||||
|
const video = this.$refs['player-' + element.value.id]
|
||||||
|
if (video) {
|
||||||
|
try {
|
||||||
|
state.flvPlayer = flvjs.createPlayer(state.pOption, {
|
||||||
|
enableWorker: false, // 不启用分离线程
|
||||||
|
enableStashBuffer: false, // 关闭IO隐藏缓冲区
|
||||||
|
isLive: state.pOption.isLive,
|
||||||
|
lazyLoad: false
|
||||||
|
})
|
||||||
|
state.flvPlayer.attachMediaElement(video)
|
||||||
|
state.flvPlayer.load()
|
||||||
|
state.flvPlayer.play()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('flvjs err ignore', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const destroyPlayer = () => {
|
||||||
|
// Destroy
|
||||||
|
if (state.flvPlayer) {
|
||||||
|
state.flvPlayer.pause()
|
||||||
|
state.flvPlayer.destroy()
|
||||||
|
state.flvPlayer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.info-stream-class {
|
||||||
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(245, 245, 220, 0.3);
|
||||||
|
font-size: 12px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-bg {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-mask {
|
||||||
|
display: flex;
|
||||||
|
height: calc(100% - 60px) !important;
|
||||||
|
width: 100% !important;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-mask-stream {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
181
core/core-frontend/src/style/custom-theme.css
Executable file
181
core/core-frontend/src/style/custom-theme.css
Executable file
@ -0,0 +1,181 @@
|
|||||||
|
.vjs-custom-skin > .video-js {
|
||||||
|
width: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
font-family: "AlibabaPuHuiTi","Helvetica Neue","Hiragino Sans GB","Segoe UI","Microsoft YaHei","微软雅黑",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-menu-button-inline.vjs-slider-active,.vjs-custom-skin > .video-js .vjs-menu-button-inline:focus,.vjs-custom-skin > .video-js .vjs-menu-button-inline:hover,.video-js.vjs-no-flex .vjs-menu-button-inline {
|
||||||
|
width: 10em
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-controls-disabled .vjs-big-play-button {
|
||||||
|
display: none!important
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control {
|
||||||
|
width: 3em
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control.vjs-live-control{
|
||||||
|
width: auto;
|
||||||
|
padding-left: .5em;
|
||||||
|
letter-spacing: .1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-menu-button-inline:before {
|
||||||
|
width: 1.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-menu-button-inline .vjs-menu {
|
||||||
|
left: 3em
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-paused.vjs-has-started.vjs-custom-skin > .video-js .vjs-big-play-button,.video-js.vjs-ended .vjs-big-play-button,.video-js.vjs-paused .vjs-big-play-button {
|
||||||
|
display: block
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-load-progress div,.vjs-seeking .vjs-big-play-button,.vjs-waiting .vjs-big-play-button {
|
||||||
|
display: none!important
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-mouse-display:after,.vjs-custom-skin > .video-js .vjs-play-progress:after {
|
||||||
|
padding: 0 .4em .3em
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-js.vjs-ended .vjs-loading-spinner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-js.vjs-ended .vjs-big-play-button {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-js.vjs-ended .vjs-big-play-button,.video-js.vjs-paused .vjs-big-play-button,.vjs-paused.vjs-has-started.vjs-custom-skin > .video-js .vjs-big-play-button {
|
||||||
|
display: block
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-big-play-button {
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -1.5em;
|
||||||
|
margin-top: -1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-big-play-button {
|
||||||
|
background-color: rgba(0,0,0,0.45);
|
||||||
|
font-size: 3.5em;
|
||||||
|
/*border-radius: 50%;*/
|
||||||
|
height: 2em !important;
|
||||||
|
line-height: 2em !important;
|
||||||
|
margin-top: -1em !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-js:hover .vjs-big-play-button,.vjs-custom-skin > .video-js .vjs-big-play-button:focus,.vjs-custom-skin > .video-js .vjs-big-play-button:active {
|
||||||
|
background-color: rgba(36,131,213,0.9)
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-loading-spinner {
|
||||||
|
border-color: rgba(36,131,213,0.8)
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar2 {
|
||||||
|
background-color: #000000
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar {
|
||||||
|
/*background-color: rgba(0,0,0,0.3) !important;*/
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 14px
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-play-progress,.vjs-custom-skin > .video-js .vjs-volume-level {
|
||||||
|
background-color: #2483d5
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-play-progress:before {
|
||||||
|
top: -0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-progress-control:hover .vjs-progress-holder {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-menu-button-popup.vjs-volume-menu-button-vertical .vjs-menu {
|
||||||
|
left: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-menu li {
|
||||||
|
padding: 0;
|
||||||
|
line-height: 2em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-family: "AlibabaPuHuiTi","Helvetica Neue","Hiragino Sans GB","Segoe UI","Microsoft YaHei","微软雅黑",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-time-tooltip,
|
||||||
|
.vjs-custom-skin > .video-js .vjs-mouse-display:after,
|
||||||
|
.vjs-custom-skin > .video-js .vjs-play-progress:after {
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0;
|
||||||
|
width: 3em;
|
||||||
|
height: 1.5em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
top: -3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-menu-button-popup .vjs-menu {
|
||||||
|
width: 5em;
|
||||||
|
left: -1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-menu-button-popup.vjs-volume-menu-button-vertical .vjs-menu {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-resolution-button .vjs-menu {
|
||||||
|
/*order: 4;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*排序顺序*/
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-play-control {
|
||||||
|
order: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-time-control {
|
||||||
|
min-width: 1em;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 .1em;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-playback-rate .vjs-playback-rate-value{
|
||||||
|
font-size: 1.2em;
|
||||||
|
line-height: 2.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-progress-control.vjs-control {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-volume-menu-button {
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-resolution-button {
|
||||||
|
order: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-resolution-button .vjs-resolution-button-label {
|
||||||
|
display: block;
|
||||||
|
line-height: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-playback-rate {
|
||||||
|
order: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-fullscreen-control {
|
||||||
|
order: 6;
|
||||||
|
}
|
@ -29,6 +29,8 @@ import DeFrame from '@/custom-component/de-frame/ComponentFrame.vue'
|
|||||||
import DeFrameAttr from '@/custom-component/de-frame/Attr.vue'
|
import DeFrameAttr from '@/custom-component/de-frame/Attr.vue'
|
||||||
import DeVideo from '@/custom-component/de-video/Component.vue'
|
import DeVideo from '@/custom-component/de-video/Component.vue'
|
||||||
import DeVideoAttr from '@/custom-component/de-video/Attr.vue'
|
import DeVideoAttr from '@/custom-component/de-video/Attr.vue'
|
||||||
|
import DeStreamMedia from '@/custom-component/de-stream-media/Component.vue'
|
||||||
|
import DeStreamMediaAttr from '@/custom-component/de-stream-media/Attr.vue'
|
||||||
export const componentsMap = {
|
export const componentsMap = {
|
||||||
VText: VText,
|
VText: VText,
|
||||||
VQuery,
|
VQuery,
|
||||||
@ -60,7 +62,9 @@ export const componentsMap = {
|
|||||||
DeFrame: DeFrame,
|
DeFrame: DeFrame,
|
||||||
DeFrameAttr: DeFrameAttr,
|
DeFrameAttr: DeFrameAttr,
|
||||||
DeVideo: DeVideo,
|
DeVideo: DeVideo,
|
||||||
DeVideoAttr: DeVideoAttr
|
DeVideoAttr: DeVideoAttr,
|
||||||
|
DeStreamMedia: DeStreamMedia,
|
||||||
|
DeStreamMediaAttr: DeStreamMediaAttr
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function findComponent(key) {
|
export default function findComponent(key) {
|
||||||
|
Loading…
Reference in New Issue
Block a user