forked from github/dataease
Merge pull request #9648 from dataease/pr@dev-v2@refactor_video2
refactor(仪表板): 视频播放优化
This commit is contained in:
commit
bd9885a4de
@ -50,8 +50,8 @@
|
|||||||
"vue-router": "4.1.3",
|
"vue-router": "4.1.3",
|
||||||
"vue-types": "^5.0.2",
|
"vue-types": "^5.0.2",
|
||||||
"vue-uuid": "^3.0.0",
|
"vue-uuid": "^3.0.0",
|
||||||
"vue-video-player": "^6.0.0",
|
|
||||||
"vue3-ace-editor": "^2.2.2",
|
"vue3-ace-editor": "^2.2.2",
|
||||||
|
"vue3-video-play": "^1.3.2",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"web-storage-cache": "^1.1.1",
|
"web-storage-cache": "^1.1.1",
|
||||||
"xss": "^1.0.14"
|
"xss": "^1.0.14"
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
@ -444,7 +444,7 @@ const initOpenHandler = newWindow => {
|
|||||||
<component-group
|
<component-group
|
||||||
is-label
|
is-label
|
||||||
themes="light"
|
themes="light"
|
||||||
:base-width="115"
|
:base-width="215"
|
||||||
icon-name="dv-media"
|
icon-name="dv-media"
|
||||||
title="媒体"
|
title="媒体"
|
||||||
>
|
>
|
||||||
|
@ -230,7 +230,7 @@ eventBus.on('clearCanvas', clearCanvas)
|
|||||||
<component-group is-label :base-width="115" icon-name="dv-text" title="文本">
|
<component-group is-label :base-width="115" icon-name="dv-text" title="文本">
|
||||||
<text-group></text-group>
|
<text-group></text-group>
|
||||||
</component-group>
|
</component-group>
|
||||||
<component-group is-label :base-width="115" icon-name="dv-media" title="媒体">
|
<component-group is-label :base-width="215" icon-name="dv-media" title="媒体">
|
||||||
<media-group></media-group>
|
<media-group></media-group>
|
||||||
</component-group>
|
</component-group>
|
||||||
<component-group is-label :base-width="115" icon-name="dv-more-com" title="更多">
|
<component-group is-label :base-width="115" icon-name="dv-more-com" title="更多">
|
||||||
|
@ -54,6 +54,7 @@ const { icon, name, label, dragInfo, themes } = toRefs(props)
|
|||||||
.drag-component {
|
.drag-component {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
width: 88px !important;
|
width: 88px !important;
|
||||||
|
margin-right: 12px;
|
||||||
.icon-content {
|
.icon-content {
|
||||||
width: 88px !important;
|
width: 88px !important;
|
||||||
height: 64px !important;
|
height: 64px !important;
|
||||||
|
@ -50,11 +50,19 @@ const handleDragEnd = e => {
|
|||||||
drag-info="Picture&Picture"
|
drag-info="Picture&Picture"
|
||||||
v-on:click="newComponent('Picture')"
|
v-on:click="newComponent('Picture')"
|
||||||
></drag-component>
|
></drag-component>
|
||||||
|
<drag-component
|
||||||
|
:themes="themes"
|
||||||
|
icon="icon-video"
|
||||||
|
label="视频"
|
||||||
|
drag-info="DeVideo&DeVideo"
|
||||||
|
v-on:click="newComponent('DeVideo')"
|
||||||
|
></drag-component>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.group {
|
.group {
|
||||||
padding: 12px 8px;
|
padding: 12px 8px;
|
||||||
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -8,6 +8,82 @@ export const commonStyle = {
|
|||||||
opacity: 1
|
opacity: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 视频信息配置
|
||||||
|
export const VIDEO_LINKS_DE2 = {
|
||||||
|
videoType: 'web',
|
||||||
|
poster: null,
|
||||||
|
web: {
|
||||||
|
width: '100%', //播放器宽度
|
||||||
|
height: '100%', //播放器高度
|
||||||
|
color: '#409eff', //主题色
|
||||||
|
title: '', //视频名称
|
||||||
|
src: null, //视频源
|
||||||
|
muted: false, //静音
|
||||||
|
webFullScreen: false,
|
||||||
|
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
|
||||||
|
autoPlay: true, //自动播放
|
||||||
|
loop: false, //循环播放
|
||||||
|
mirror: false, //镜像画面
|
||||||
|
lightOff: false, //关灯模式
|
||||||
|
volume: 0.3, //默认音量大小
|
||||||
|
control: true, //是否显示控制
|
||||||
|
controlBtns: ['audioTrack', 'quality', 'volume', 'fullScreen'] //显示所有按钮,
|
||||||
|
},
|
||||||
|
rtmp: {
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
type: 'rtmp/mp4'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
height: 300,
|
||||||
|
techOrder: ['flash'],
|
||||||
|
autoplay: false,
|
||||||
|
controls: true,
|
||||||
|
flash: {
|
||||||
|
hls: {
|
||||||
|
withCredentials: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频信息配置
|
||||||
|
export const VIDEO_LINKS = {
|
||||||
|
videoType: 'web',
|
||||||
|
web: {
|
||||||
|
autoplay: true,
|
||||||
|
height: 300,
|
||||||
|
muted: true,
|
||||||
|
loop: true,
|
||||||
|
controlBar: {
|
||||||
|
timeDivider: false,
|
||||||
|
durationDisplay: false,
|
||||||
|
remainingTimeDisplay: false,
|
||||||
|
currentTimeDisplay: false, // 当前时间
|
||||||
|
volumeControl: false, // 声音控制键
|
||||||
|
fullscreenToggle: false,
|
||||||
|
pause: false
|
||||||
|
},
|
||||||
|
sources: [{}]
|
||||||
|
},
|
||||||
|
rtmp: {
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
type: 'rtmp/mp4'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
height: 300,
|
||||||
|
techOrder: ['flash'],
|
||||||
|
autoplay: false,
|
||||||
|
controls: true,
|
||||||
|
flash: {
|
||||||
|
hls: {
|
||||||
|
withCredentials: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 超链接配置
|
// 超链接配置
|
||||||
export const HYPERLINKS = {
|
export const HYPERLINKS = {
|
||||||
openMode: '_blank',
|
openMode: '_blank',
|
||||||
@ -77,7 +153,7 @@ export const commonAttr = {
|
|||||||
maintainRadio: false, // 布局时保持宽高比例
|
maintainRadio: false, // 布局时保持宽高比例
|
||||||
aspectRatio: 1, // 锁定时的宽高比例
|
aspectRatio: 1, // 锁定时的宽高比例
|
||||||
isShow: true, // 是否显示组件
|
isShow: true, // 是否显示组件
|
||||||
collapseName: ['position', 'background', 'style', 'picture', 'frameLinks'], // 编辑组件时记录当前使用的是哪个折叠面板,再次回来时恢复上次打开的折叠面板,优化用户体验
|
collapseName: ['position', 'background', 'style', 'picture', 'frameLinks', 'videoLinks'], // 编辑组件时记录当前使用的是哪个折叠面板,再次回来时恢复上次打开的折叠面板,优化用户体验
|
||||||
linkage: {
|
linkage: {
|
||||||
duration: 0, // 过渡持续时间
|
duration: 0, // 过渡持续时间
|
||||||
data: [
|
data: [
|
||||||
@ -169,6 +245,7 @@ const list = [
|
|||||||
width: 600,
|
width: 600,
|
||||||
height: 300
|
height: 300
|
||||||
},
|
},
|
||||||
|
videoLinks: VIDEO_LINKS_DE2,
|
||||||
matrixStyle: {}
|
matrixStyle: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="attr-list de-collapse-style">
|
<div class="attr-list de-collapse-style">
|
||||||
<CommonAttr :element="curComponent"></CommonAttr>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -8,6 +17,15 @@
|
|||||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||||
import CommonAttr from '@/custom-component/common/CommonAttr.vue'
|
import CommonAttr from '@/custom-component/common/CommonAttr.vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import VideoLinks from '@/custom-component/de-video/VideoLinks.vue'
|
||||||
const dvMainStore = dvMainStoreWithOut()
|
const dvMainStore = dvMainStoreWithOut()
|
||||||
const { curComponent } = storeToRefs(dvMainStore)
|
const { curComponent } = storeToRefs(dvMainStore)
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
themes?: EditorTheme
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
themes: 'dark'
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row ref="mainPlayer">
|
<el-row ref="mainPlayer">
|
||||||
<div v-if="element.videoLinks[element.videoLinks.videoType].sources[0].src" class="player">
|
<div v-if="element.videoLinks[element.videoLinks.videoType].src" class="player">
|
||||||
<video-player
|
<video-play v-bind="playerOptions" />
|
||||||
v-if="state.showVideo"
|
|
||||||
ref="videoPlayer"
|
|
||||||
class="vjs-custom-skin"
|
|
||||||
:options="editMode === 'preview' ? state.pOption : playerOptions"
|
|
||||||
:playsinline="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="info-class">
|
<div v-else class="info-class">
|
||||||
<span>{{ $t('panel.link_add_tips_pre') }}</span>
|
<span>{{ t('visualization.video_add_tips') }}</span>
|
||||||
<span>{{ $t('panel.video_add_tips') }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VideoPlayer from 'vue-video-player'
|
import { videoPlay } from 'vue3-video-play/lib/index' // 引入组件
|
||||||
import 'video.js/dist/video-js.css'
|
import 'vue3-video-play/dist/style.css' // 引入css
|
||||||
import { computed, nextTick, reactive, toRefs, watch } from 'vue'
|
import { computed, nextTick, reactive, toRefs, watch, onMounted, ref } from 'vue'
|
||||||
|
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
const { t } = useI18n()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
propValue: {
|
propValue: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -45,21 +40,24 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
pOption: {},
|
pOption: {
|
||||||
|
height: null
|
||||||
|
},
|
||||||
showVideo: true
|
showVideo: true
|
||||||
})
|
})
|
||||||
const { element, h } = toRefs(props)
|
const { element, h } = toRefs(props)
|
||||||
|
|
||||||
const moveFlag = computed(
|
onMounted(() => {
|
||||||
() => element.value.optStatus.dragging || element.value.optStatus.resizing
|
useEmitt({
|
||||||
)
|
name: 'videoLinksChange-' + element.value.id,
|
||||||
|
callback: function () {
|
||||||
const playerOptions = computed(() => {
|
videoLinksChange()
|
||||||
const videoPlayerOptions = element.value.videoLinks[element.value.videoLinks.videoType]
|
}
|
||||||
videoPlayerOptions.height = h
|
})
|
||||||
return videoPlayerOptions
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const playerOptions = computed(() => element.value.videoLinks[element.value.videoLinks.videoType])
|
||||||
|
|
||||||
const videoLinksChange = () => {
|
const videoLinksChange = () => {
|
||||||
state.showVideo = false
|
state.showVideo = false
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@ -83,18 +81,25 @@ watch(
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.info-class {
|
.info-class {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: rgba(255, 255, 255, 0.3);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #9ea6b2;
|
color: #9ea6b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.move-bg {
|
.player {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d-player-state {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
172
core/core-frontend/src/custom-component/de-video/VideoLinks.vue
Normal file
172
core/core-frontend/src/custom-component/de-video/VideoLinks.vue
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
<template>
|
||||||
|
<el-row>
|
||||||
|
<el-form ref="form" size="small" style="width: 100%">
|
||||||
|
<el-form-item :effect="themes" :label="t('visualization.auto_play')">
|
||||||
|
<el-switch
|
||||||
|
:effect="themes"
|
||||||
|
@change="onChange"
|
||||||
|
v-model="state.linkInfoTemp[state.linkInfoTemp.videoType].autoPlay"
|
||||||
|
size="mini"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="state.linkInfoTemp.videoType === 'web'"
|
||||||
|
:label="t('visualization.play_frequency')"
|
||||||
|
:effect="themes"
|
||||||
|
>
|
||||||
|
<el-radio-group
|
||||||
|
:effect="themes"
|
||||||
|
@change="onChange"
|
||||||
|
v-model="state.linkInfoTemp[state.linkInfoTemp.videoType].loop"
|
||||||
|
>
|
||||||
|
<el-radio :effect="themes" :label="false">{{ t('visualization.play_once') }}</el-radio>
|
||||||
|
<el-radio :effect="themes" :label="true">{{ t('visualization.play_circle') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :effect="themes" :label="t('visualization.video_links')">
|
||||||
|
<el-input
|
||||||
|
:effect="themes"
|
||||||
|
@blur="onChange"
|
||||||
|
v-model="state.linkInfoTemp[state.linkInfoTemp.videoType].src"
|
||||||
|
/>
|
||||||
|
<span class="tips-class"> Tips:{{ t('visualization.video_tips') }} </span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, toRefs, watch, computed } from 'vue'
|
||||||
|
import { dvMainStoreWithOut } from '../../store/modules/data-visualization/dvMain'
|
||||||
|
import { storeToRefs } from 'pinia/dist/pinia'
|
||||||
|
import { checkAddHttp, deepCopy } from '../../utils/utils'
|
||||||
|
import { snapshotStoreWithOut } from '../../store/modules/data-visualization/snapshot'
|
||||||
|
import { useI18n } from '../../hooks/web/useI18n'
|
||||||
|
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||||
|
const dvMainStore = dvMainStoreWithOut()
|
||||||
|
const { curComponent, curActiveTabInner } = storeToRefs(dvMainStore)
|
||||||
|
const snapshotStore = snapshotStoreWithOut()
|
||||||
|
const emits = defineEmits(['close'])
|
||||||
|
const popover = ref(null)
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
themes: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
linkInfo: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// 属性所属组件位置
|
||||||
|
attrPosition: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'panel'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { linkInfo, attrPosition } = toRefs(props)
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
linkInfoTemp: null,
|
||||||
|
componentType: null,
|
||||||
|
linkageActiveStatus: false,
|
||||||
|
editFilter: ['view', 'custom']
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
state.linkInfoTemp = deepCopy(linkInfo.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = () => {
|
||||||
|
state.linkInfoTemp[state.linkInfoTemp.videoType].src = checkAddHttp(
|
||||||
|
state.linkInfoTemp[state.linkInfoTemp.videoType].src
|
||||||
|
)
|
||||||
|
|
||||||
|
if (attrPosition.value === 'panel') {
|
||||||
|
curComponent.value.videoLinks = state.linkInfoTemp
|
||||||
|
} else {
|
||||||
|
curActiveTabInner.value.videoLinks = state.linkInfoTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
curComponent.value.videoLinks = state.linkInfoTemp
|
||||||
|
snapshotStore.recordSnapshotCache()
|
||||||
|
useEmitt().emitter.emit('videoLinksChange-' + curComponent.value.id)
|
||||||
|
}
|
||||||
|
const onBlur = () => {
|
||||||
|
state.linkInfoTemp.src = checkAddHttp(state.linkInfoTemp.src)
|
||||||
|
curComponent.value.frameLinks.src = state.linkInfoTemp.src
|
||||||
|
snapshotStore.recordSnapshotCache()
|
||||||
|
useEmitt().emitter.emit('frameLinksChange-' + curComponent.value.id)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
|
||||||
|
watch(
|
||||||
|
linkInfo.value,
|
||||||
|
() => {
|
||||||
|
init()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.slot-class {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.ellipsis-area {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
overflow: hidden; /*超出部分隐藏*/
|
||||||
|
white-space: nowrap; /*不换行*/
|
||||||
|
text-overflow: ellipsis; /*超出部分文字以...显示*/
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
color: #3d4d66;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-filed {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
overflow: hidden; /*超出部分隐藏*/
|
||||||
|
white-space: nowrap; /*不换行*/
|
||||||
|
text-overflow: ellipsis; /*超出部分文字以...显示*/
|
||||||
|
color: #3d4d66;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 35px;
|
||||||
|
height: 35px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips-class {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 8px;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #646a73;
|
||||||
|
|
||||||
|
&.hint-icon--dark {
|
||||||
|
color: #a6a6a6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.data-area-label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
@ -2126,7 +2126,7 @@ export default {
|
|||||||
play_circle: '循环播放',
|
play_circle: '循环播放',
|
||||||
video_links: '视频链接',
|
video_links: '视频链接',
|
||||||
web_url: '网页地址',
|
web_url: '网页地址',
|
||||||
video_add_tips: '添加视频信息...',
|
video_add_tips: '请在右侧添加视频信息...',
|
||||||
link_add_tips_pre: '请在右侧配置网页信息..',
|
link_add_tips_pre: '请在右侧配置网页信息..',
|
||||||
web_add_tips_suf: '添加网页信息...',
|
web_add_tips_suf: '添加网页信息...',
|
||||||
panel_view_result_show: '图表结果',
|
panel_view_result_show: '图表结果',
|
||||||
|
Loading…
Reference in New Issue
Block a user