diff --git a/src/configs/element.ts b/src/configs/element.ts
index 5730f818..3533065e 100644
--- a/src/configs/element.ts
+++ b/src/configs/element.ts
@@ -5,6 +5,7 @@ export const ELEMENT_TYPE_ZH = {
line: '线条',
chart: '图表',
table: '表格',
+ video: '视频',
}
export const MIN_SIZE = {
@@ -13,4 +14,5 @@ export const MIN_SIZE = {
shape: 15,
chart: 200,
table: 20,
+ video: 250,
}
\ No newline at end of file
diff --git a/src/hooks/useCreateElement.ts b/src/hooks/useCreateElement.ts
index f30ee9a5..8d0ec32e 100644
--- a/src/hooks/useCreateElement.ts
+++ b/src/hooks/useCreateElement.ts
@@ -222,6 +222,22 @@ export default () => {
if (data.isCurve) newElement.curve = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]
createElement(newElement)
}
+
+ /**
+ * 创建视频元素
+ * @param src 视频地址
+ */
+ const createVideoElement = (src: string) => {
+ createElement({
+ type: 'video',
+ id: createRandomCode(),
+ width: 500,
+ height: 300,
+ left: (VIEWPORT_SIZE - 500) / 2,
+ top: (VIEWPORT_SIZE * viewportRatio.value - 300) / 2,
+ src,
+ })
+ }
return {
createImageElement,
@@ -230,5 +246,6 @@ export default () => {
createTextElement,
createShapeElement,
createLineElement,
+ createVideoElement,
}
}
\ No newline at end of file
diff --git a/src/plugins/icon.ts b/src/plugins/icon.ts
index 77fc75fa..a33ace74 100644
--- a/src/plugins/icon.ts
+++ b/src/plugins/icon.ts
@@ -84,6 +84,12 @@ import {
AlignTextTopOne,
AlignTextBottomOne,
AlignTextMiddleOne,
+ Pause,
+ VolumeMute,
+ VolumeNotice,
+ VolumeSmall,
+ LoopOnce,
+ VideoTwo,
} from '@icon-park/vue-next'
export default {
@@ -95,6 +101,7 @@ export default {
app.component('IconConnection', Connection)
app.component('IconChartProportion', ChartProportion)
app.component('IconInsertTable', InsertTable)
+ app.component('IconVideoTwo', VideoTwo)
// 锁定与解锁
app.component('IconLock', Lock)
@@ -195,5 +202,12 @@ export default {
app.component('IconLogout', Logout)
app.component('IconClear', Clear)
app.component('IconFolderClose', FolderClose)
+
+ // 视频播放器
+ app.component('IconPause', Pause)
+ app.component('IconVolumeMute', VolumeMute)
+ app.component('IconVolumeNotice', VolumeNotice)
+ app.component('IconVolumeSmall', VolumeSmall)
+ app.component('IconLoopOnce', LoopOnce)
}
}
\ No newline at end of file
diff --git a/src/types/slides.ts b/src/types/slides.ts
index dccd5f2e..4435bf34 100644
--- a/src/types/slides.ts
+++ b/src/types/slides.ts
@@ -7,6 +7,7 @@ export const enum ElementTypes {
LINE = 'line',
CHART = 'chart',
TABLE = 'table',
+ VIDEO = 'video',
}
/**
@@ -461,7 +462,23 @@ export interface PPTTableElement extends PPTBaseElement {
}
-export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement
+/**
+ * 视频元素
+ *
+ * type: 元素类型(video)
+ *
+ * src: 视频地址
+ *
+ * poster: 预览封面
+ */
+export interface PPTVideoElement extends PPTBaseElement {
+ type: 'video';
+ src: string;
+ poster?: string;
+}
+
+
+export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement | PPTVideoElement
/**
diff --git a/src/views/Editor/Canvas/EditableElement.vue b/src/views/Editor/Canvas/EditableElement.vue
index 7d9d514b..64d139bc 100644
--- a/src/views/Editor/Canvas/EditableElement.vue
+++ b/src/views/Editor/Canvas/EditableElement.vue
@@ -37,6 +37,7 @@ import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
import LineElement from '@/views/components/element/LineElement/index.vue'
import ChartElement from '@/views/components/element/ChartElement/index.vue'
import TableElement from '@/views/components/element/TableElement/index.vue'
+import VideoElement from '@/views/components/element/VideoElement/index.vue'
export default defineComponent({
name: 'editable-element',
@@ -71,6 +72,7 @@ export default defineComponent({
[ElementTypes.LINE]: LineElement,
[ElementTypes.CHART]: ChartElement,
[ElementTypes.TABLE]: TableElement,
+ [ElementTypes.VIDEO]: VideoElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/Editor/Canvas/Operate/VideoElementOperate.vue b/src/views/Editor/Canvas/Operate/VideoElementOperate.vue
new file mode 100644
index 00000000..0b1fb29a
--- /dev/null
+++ b/src/views/Editor/Canvas/Operate/VideoElementOperate.vue
@@ -0,0 +1,74 @@
+
+
+
+
+ scaleElement($event, elementInfo, point.direction)"
+ />
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/Editor/Canvas/Operate/index.vue b/src/views/Editor/Canvas/Operate/index.vue
index a22eb207..916633c9 100644
--- a/src/views/Editor/Canvas/Operate/index.vue
+++ b/src/views/Editor/Canvas/Operate/index.vue
@@ -48,6 +48,7 @@ import ShapeElementOperate from './ShapeElementOperate.vue'
import LineElementOperate from './LineElementOperate.vue'
import ChartElementOperate from './ChartElementOperate.vue'
import TableElementOperate from './TableElementOperate.vue'
+import VideoElementOperate from './VideoElementOperate.vue'
import LinkHandler from './LinkHandler.vue'
export default defineComponent({
@@ -107,6 +108,7 @@ export default defineComponent({
[ElementTypes.LINE]: LineElementOperate,
[ElementTypes.CHART]: ChartElementOperate,
[ElementTypes.TABLE]: TableElementOperate,
+ [ElementTypes.VIDEO]: VideoElementOperate,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/Editor/CanvasTool/VideoInput.vue b/src/views/Editor/CanvasTool/VideoInput.vue
new file mode 100644
index 00000000..92fdeb17
--- /dev/null
+++ b/src/views/Editor/CanvasTool/VideoInput.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
diff --git a/src/views/Editor/CanvasTool/index.vue b/src/views/Editor/CanvasTool/index.vue
index c0fd272c..8f6fc4c3 100644
--- a/src/views/Editor/CanvasTool/index.vue
+++ b/src/views/Editor/CanvasTool/index.vue
@@ -53,6 +53,17 @@
+
+
+ { createVideoElement(src); videoInputVisible = false }"
+ />
+
+
+
+
+
@@ -80,6 +91,7 @@ import ShapePool from './ShapePool.vue'
import LinePool from './LinePool.vue'
import ChartPool from './ChartPool.vue'
import TableGenerator from './TableGenerator.vue'
+import VideoInput from './VideoInput.vue'
export default defineComponent({
name: 'canvas-tool',
@@ -88,6 +100,7 @@ export default defineComponent({
LinePool,
ChartPool,
TableGenerator,
+ VideoInput,
},
setup() {
const store = useStore()
@@ -100,7 +113,7 @@ export default defineComponent({
const { scaleCanvas, setCanvasPercentage } = useScaleCanvas()
const { redo, undo } = useHistorySnapshot()
- const { createImageElement, createChartElement, createTableElement } = useCreateElement()
+ const { createImageElement, createChartElement, createTableElement, createVideoElement } = useCreateElement()
const insertImageElement = (files: File[]) => {
const imageFile = files[0]
@@ -112,6 +125,7 @@ export default defineComponent({
const linePoolVisible = ref(false)
const chartPoolVisible = ref(false)
const tableGeneratorVisible = ref(false)
+ const videoInputVisible = ref(false)
// 绘制文字范围
const drawText = () => {
@@ -152,11 +166,13 @@ export default defineComponent({
linePoolVisible,
chartPoolVisible,
tableGeneratorVisible,
+ videoInputVisible,
drawText,
drawShape,
drawLine,
createChartElement,
createTableElement,
+ createVideoElement,
}
},
})
diff --git a/src/views/Editor/Toolbar/ElementAnimationPanel.vue b/src/views/Editor/Toolbar/ElementAnimationPanel.vue
index 1564183d..e9ebf128 100644
--- a/src/views/Editor/Toolbar/ElementAnimationPanel.vue
+++ b/src/views/Editor/Toolbar/ElementAnimationPanel.vue
@@ -1,7 +1,7 @@
-
+
@@ -34,7 +34,7 @@
diff --git a/src/views/Editor/Toolbar/ElementStylePanel/VideoStylePanel.vue b/src/views/Editor/Toolbar/ElementStylePanel/VideoStylePanel.vue
new file mode 100644
index 00000000..27f9d1c4
--- /dev/null
+++ b/src/views/Editor/Toolbar/ElementStylePanel/VideoStylePanel.vue
@@ -0,0 +1,91 @@
+
+
+
视频预览封面
+
+
setVideoPoster(files)">
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/Editor/Toolbar/ElementStylePanel/index.vue b/src/views/Editor/Toolbar/ElementStylePanel/index.vue
index 5c9ccdcc..8d3f74d4 100644
--- a/src/views/Editor/Toolbar/ElementStylePanel/index.vue
+++ b/src/views/Editor/Toolbar/ElementStylePanel/index.vue
@@ -18,6 +18,7 @@ import ShapeStylePanel from './ShapeStylePanel.vue'
import LineStylePanel from './LineStylePanel.vue'
import ChartStylePanel from './ChartStylePanel/index.vue'
import TableStylePanel from './TableStylePanel.vue'
+import VideoStylePanel from './VideoStylePanel.vue'
export default defineComponent({
name: 'element-style-panel',
@@ -35,6 +36,7 @@ export default defineComponent({
[ElementTypes.LINE]: LineStylePanel,
[ElementTypes.CHART]: ChartStylePanel,
[ElementTypes.TABLE]: TableStylePanel,
+ [ElementTypes.VIDEO]: VideoStylePanel,
}
return panelMap[handleElement.value.type] || null
})
diff --git a/src/views/Screen/ScreenElement.vue b/src/views/Screen/ScreenElement.vue
index 58b2f047..bc9848c1 100644
--- a/src/views/Screen/ScreenElement.vue
+++ b/src/views/Screen/ScreenElement.vue
@@ -30,6 +30,7 @@ import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeE
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
import ScreenChartElement from '@/views/components/element/ChartElement/ScreenChartElement.vue'
import BaseTableElement from '@/views/components/element/TableElement/BaseTableElement.vue'
+import ScreenVideoElement from '@/views/components/element/VideoElement/ScreenVideoElement.vue'
export default defineComponent({
name: 'screen-element',
@@ -56,6 +57,7 @@ export default defineComponent({
[ElementTypes.LINE]: BaseLineElement,
[ElementTypes.CHART]: ScreenChartElement,
[ElementTypes.TABLE]: BaseTableElement,
+ [ElementTypes.VIDEO]: ScreenVideoElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/components/ThumbnailSlide/ThumbnailElement.vue b/src/views/components/ThumbnailSlide/ThumbnailElement.vue
index c37a5a02..98af4616 100644
--- a/src/views/components/ThumbnailSlide/ThumbnailElement.vue
+++ b/src/views/components/ThumbnailSlide/ThumbnailElement.vue
@@ -24,6 +24,7 @@ import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeE
import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
import BaseChartElement from '@/views/components/element/ChartElement/BaseChartElement.vue'
import BaseTableElement from '@/views/components/element/TableElement/BaseTableElement.vue'
+import BaseVideoElement from '@/views/components/element/VideoElement/BaseVideoElement.vue'
export default defineComponent({
name: 'base-element',
@@ -46,6 +47,7 @@ export default defineComponent({
[ElementTypes.LINE]: BaseLineElement,
[ElementTypes.CHART]: BaseChartElement,
[ElementTypes.TABLE]: BaseTableElement,
+ [ElementTypes.VIDEO]: BaseVideoElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/components/element/VideoElement/BaseVideoElement.vue b/src/views/components/element/VideoElement/BaseVideoElement.vue
new file mode 100644
index 00000000..1c09ba51
--- /dev/null
+++ b/src/views/components/element/VideoElement/BaseVideoElement.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
diff --git a/src/views/components/element/VideoElement/ScreenVideoElement.vue b/src/views/components/element/VideoElement/ScreenVideoElement.vue
new file mode 100644
index 00000000..abf624cf
--- /dev/null
+++ b/src/views/components/element/VideoElement/ScreenVideoElement.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
diff --git a/src/views/components/element/VideoElement/VideoPlayer/index.vue b/src/views/components/element/VideoElement/VideoPlayer/index.vue
new file mode 100644
index 00000000..abd8ac1b
--- /dev/null
+++ b/src/views/components/element/VideoElement/VideoPlayer/index.vue
@@ -0,0 +1,381 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
handleMousedownVolumeBar($event)"
+ @touchstart="$event => handleMousedownVolumeBar($event)"
+ @click="$event => handleClickVolumeBar($event)"
+ >
+
+
+
+
+ {{ptime}} / {{dtime}}
+
+
+
+
+
+
handleMousedownPlayBar($event)"
+ @touchstart="$event => handleMousedownPlayBar($event)"
+ @mousemove="$event => handleMousemovePlayBar($event)"
+ @mouseenter="playBarTimeVisible = true"
+ @mouseleave="playBarTimeVisible = false"
+ >
+
{{playBarTime}}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/components/element/VideoElement/VideoPlayer/style.scss b/src/views/components/element/VideoElement/VideoPlayer/style.scss
new file mode 100644
index 00000000..c9246b42
--- /dev/null
+++ b/src/views/components/element/VideoElement/VideoPlayer/style.scss
@@ -0,0 +1,353 @@
+// player
+.player {
+ position: relative;
+ overflow: hidden;
+ user-select: none;
+ line-height: 1;
+ transform-origin: 0 0;
+
+ &.playing {
+ @media (min-width: 900px) {
+ .controller-mask {
+ opacity: 0;
+ }
+ .controller {
+ opacity: 0;
+ }
+
+ &:hover {
+ .controller-mask {
+ opacity: 1;
+ }
+ .controller {
+ opacity: 1;
+ }
+ }
+ }
+ }
+
+ &.loading {
+ .bezel .diplayer-loading-icon {
+ display: block;
+ }
+ }
+
+ &.hide-controller {
+ cursor: none;
+
+ .controller-mask {
+ opacity: 0;
+ transform: translateY(100%);
+ }
+ .controller {
+ opacity: 0;
+ transform: translateY(100%);
+ }
+ }
+}
+
+.video-wrap {
+ position: relative;
+ background: #000;
+ font-size: 0;
+ width: 100%;
+ height: 100%;
+
+ .video {
+ width: 100%;
+ height: 100%;
+ }
+}
+
+// controller
+.controller-mask {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==) repeat-x bottom;
+ height: 98px;
+ width: 100%;
+ position: absolute;
+ bottom: 0;
+ transition: all 0.3s ease;
+}
+.controller {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 41px;
+ padding: 0 20px;
+ user-select: none;
+ transition: all 0.3s ease;
+
+ .bar-wrap {
+ padding: 5px 0;
+ cursor: pointer;
+ position: absolute;
+ bottom: 33px;
+ width: calc(100% - 40px);
+ height: 3px;
+
+ &:hover .bar .played .thumb {
+ transform: scale(1);
+ }
+
+ .bar-time {
+ position: absolute;
+ left: 0;
+ top: -20px;
+ border-radius: 4px;
+ padding: 5px 7px;
+ background-color: rgba(0, 0, 0, 0.62);
+ color: #fff;
+ font-size: 12px;
+ text-align: center;
+ opacity: 1;
+ transition: opacity 0.1s ease-in-out;
+ word-wrap: normal;
+ word-break: normal;
+ z-index: 2;
+ pointer-events: none;
+
+ &.hidden {
+ opacity: 0;
+ }
+ }
+ .bar {
+ position: relative;
+ height: 3px;
+ width: 100%;
+ background: rgba(255, 255, 255, 0.2);
+ cursor: pointer;
+
+ .loaded {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ background: rgba(255, 255, 255, 0.4);
+ height: 3px;
+ transition: all 0.5s ease;
+ will-change: width;
+ }
+ .played {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ height: 3px;
+ will-change: width;
+ background-color: $themeColor;
+
+ .thumb {
+ position: absolute;
+ top: 0;
+ right: 5px;
+ margin-top: -4px;
+ margin-right: -10px;
+ height: 11px;
+ width: 11px;
+ border-radius: 50%;
+ cursor: pointer;
+ transition: all 0.3s ease-in-out;
+ transform: scale(0);
+ background-color: $themeColor;
+ }
+ }
+ }
+ }
+ .icons {
+ height: 38px;
+ position: absolute;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+
+ &.icons-right {
+ right: 8px;
+ }
+ .time {
+ line-height: 38px;
+ color: #eee;
+ text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
+ vertical-align: middle;
+ font-size: 13px;
+ cursor: default;
+ }
+ .icon {
+ width: 40px;
+ height: 100%;
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ font-size: 22px;
+
+ &.play-icon {
+ font-size: 26px;
+ }
+
+ .icon-content {
+ transition: all 0.2s ease-in-out;
+ opacity: 0.8;
+ color: #fff;
+ }
+ &.active .icon-content {
+ color: $themeColor;
+ opacity: 1;
+ }
+ &:hover .icon-content {
+ opacity: 1;
+ }
+ }
+ .volume {
+ height: 100%;
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+
+ &:hover {
+ .volume-bar-wrap .volume-bar {
+ width: 45px;
+ }
+ .volume-bar-wrap .volume-bar .volume-bar-inner .thumb {
+ transform: scale(1);
+ }
+ }
+ &.volume-active {
+ .volume-bar-wrap .volume-bar {
+ width: 45px;
+ }
+ .volume-bar-wrap .volume-bar .volume-bar-inner .thumb {
+ transform: scale(1);
+ }
+ }
+ }
+ .volume-bar-wrap {
+ display: inline-block;
+ margin: 0 15px 0 -5px;
+ vertical-align: middle;
+ height: 100%;
+ }
+ .volume-bar {
+ position: relative;
+ top: 17px;
+ width: 0;
+ height: 3px;
+ background: #aaa;
+ transition: all 0.3s ease-in-out;
+
+ .volume-bar-inner {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ height: 100%;
+ transition: all 0.1s ease;
+ will-change: width;
+ background-color: $themeColor;
+
+ .thumb {
+ position: absolute;
+ top: 0;
+ right: 5px;
+ margin-top: -4px;
+ margin-right: -10px;
+ height: 11px;
+ width: 11px;
+ border-radius: 50%;
+ cursor: pointer;
+ transition: all 0.3s ease-in-out;
+ transform: scale(0);
+ background-color: $themeColor;
+ }
+ }
+ }
+ .loop {
+ display: inline-block;
+ height: 100%;
+ }
+ }
+}
+
+// bezel
+.bezel {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ font-size: 22px;
+ color: #fff;
+ pointer-events: none;
+
+ .bezel-icon {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: -26px 0 0 -26px;
+ height: 52px;
+ width: 52px;
+ padding: 12px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 50%;
+ opacity: 0;
+ pointer-events: none;
+ font-size: 40px;
+
+ &.bezel-transition {
+ animation: bezel-hide 0.5s linear;
+ }
+
+ @keyframes bezel-hide {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(2);
+ }
+ }
+ }
+ .loading-icon {
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: -18px 0 0 -18px;
+ height: 36px;
+ width: 36px;
+ pointer-events: none;
+
+ .loading-hide {
+ display: none;
+ }
+ .loading-dot {
+ animation: loading-dot-fade 0.8s ease infinite;
+ opacity: 0;
+ transform-origin: 4px 4px;
+
+ @for $i from 7 through 1 {
+ &.loading-dot-#{$i} {
+ animation-delay: 0.1s * $i;
+ }
+ }
+ }
+ @keyframes loading-dot-fade {
+ 0% {
+ opacity: 0.7;
+ transform: scale(1.2, 1.2);
+ }
+ 50% {
+ opacity: 0.25;
+ transform: scale(0.9, 0.9);
+ }
+ to {
+ opacity: 0.25;
+ transform: scale(0.85, 0.85);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/views/components/element/VideoElement/VideoPlayer/useMSE.ts b/src/views/components/element/VideoElement/VideoPlayer/useMSE.ts
new file mode 100644
index 00000000..cb80cf6d
--- /dev/null
+++ b/src/views/components/element/VideoElement/VideoPlayer/useMSE.ts
@@ -0,0 +1,41 @@
+/* eslint-disable */
+
+import { onMounted, Ref } from 'vue'
+
+export default (
+ src: string,
+ videoRef: Ref
,
+) => {
+ onMounted(() => {
+ if (!videoRef.value) return
+
+ let type = 'normal'
+ if (/m3u8(#|\?|$)/i.exec(src)) type = 'hls'
+ else if (/.flv(#|\?|$)/i.exec(src)) type = 'flv'
+
+ if (videoRef.value && type === 'hls' && (videoRef.value.canPlayType('application/x-mpegURL') || videoRef.value.canPlayType('application/vnd.apple.mpegURL'))) {
+ type = 'normal'
+ }
+
+ if (type === 'hls') {
+ const Hls = (window as any).Hls
+
+ if (Hls && Hls.isSupported()) {
+ const hls = new Hls()
+ hls.loadSource(src)
+ hls.attachMedia(videoRef.value)
+ }
+ }
+ else if (type === 'flv') {
+ const flvjs = (window as any).flvjs
+ if (flvjs && flvjs.isSupported()) {
+ const flvPlayer = flvjs.createPlayer({
+ type: 'flv',
+ url: src,
+ })
+ flvPlayer.attachMediaElement(videoRef.value)
+ flvPlayer.load()
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/views/components/element/VideoElement/index.vue b/src/views/components/element/VideoElement/index.vue
new file mode 100644
index 00000000..b8e80994
--- /dev/null
+++ b/src/views/components/element/VideoElement/index.vue
@@ -0,0 +1,121 @@
+
+
+
handleSelectElement($event, false)"
+ >
+
+
handleSelectElement($event)"
+ >
+
+
+
+
+
+
+