kailong321200875 1 рік тому
батько
коміт
7b5bbedbcc

+ 9 - 0
mock/role/index.ts

@@ -301,6 +301,14 @@ const adminList = [
         meta: {
           title: 'router.imageCropping'
         }
+      },
+      {
+        path: 'video-player',
+        component: 'views/Components/VideoPlayer',
+        name: 'VideoPlayer',
+        meta: {
+          title: 'router.videoPlayer'
+        }
       }
     ]
   },
@@ -660,6 +668,7 @@ const testList: string[] = [
   '/components/input-password',
   '/components/waterfall',
   '/components/image-cropping',
+  '/components/video-player',
   'function',
   '/function/multiple-tabs',
   '/function/multiple-tabs-demo/:id',

+ 2 - 1
package.json

@@ -54,7 +54,8 @@
     "vue-i18n": "9.7.0",
     "vue-json-pretty": "^2.2.4",
     "vue-router": "^4.2.5",
-    "vue-types": "^5.1.1"
+    "vue-types": "^5.1.1",
+    "xgplayer": "^3.0.10"
   },
   "devDependencies": {
     "@commitlint/cli": "^18.4.2",

+ 27 - 0
src/components/VideoPlayer/index.ts

@@ -0,0 +1,27 @@
+import { VNode, createVNode, render } from 'vue'
+import VideoPlayer from './src/VideoPlayer.vue'
+import { isClient } from '@/utils/is'
+import { VideoPlayerViewer } from '@/components/VideoPlayerViewer'
+import { toAnyString } from '@/utils'
+
+export { VideoPlayer }
+
+let instance: Nullable<VNode> = null
+
+export function createVideoViewer(options: { url: string; poster?: string; show?: boolean }) {
+  if (!isClient) return
+  const { url, poster } = options
+
+  const propsData: Partial<{ url: string; poster?: string; show?: boolean; id?: string }> = {}
+  const container = document.createElement('div')
+  const id = toAnyString()
+  container.id = id
+  propsData.url = url
+  propsData.poster = poster
+  propsData.show = true
+  propsData.id = id
+
+  document.body.appendChild(container)
+  instance = createVNode(VideoPlayerViewer, propsData)
+  render(instance, container)
+}

+ 59 - 0
src/components/VideoPlayer/src/VideoPlayer.vue

@@ -0,0 +1,59 @@
+<script setup lang="ts">
+import Player from 'xgplayer'
+import { ref, unref, onMounted, watch, onBeforeUnmount, nextTick } from 'vue'
+import 'xgplayer/dist/index.min.css'
+
+const props = defineProps({
+  url: {
+    type: String,
+    default: '',
+    required: true
+  },
+  poster: {
+    type: String,
+    default: ''
+  }
+})
+
+const playerRef = ref<Player>()
+
+const videoEl = ref<HTMLDivElement>()
+
+const intiPlayer = () => {
+  if (!unref(videoEl)) return
+  new Player({
+    autoplay: false,
+    ...props,
+    el: unref(videoEl)
+  })
+}
+
+onMounted(() => {
+  intiPlayer()
+})
+
+watch(
+  () => props,
+  async (newProps) => {
+    await nextTick()
+    if (newProps) {
+      unref(playerRef)?.setConfig(newProps)
+    }
+  },
+  {
+    deep: true
+  }
+)
+
+onBeforeUnmount(() => {
+  unref(playerRef)?.destroy()
+})
+
+defineExpose({
+  playerExpose: () => unref(playerRef)
+})
+</script>
+
+<template>
+  <div ref="videoEl"></div>
+</template>

+ 3 - 0
src/components/VideoPlayerViewer/index.ts

@@ -0,0 +1,3 @@
+import VideoPlayerViewer from './src/VideoPlayerViewer.vue'
+
+export { VideoPlayerViewer }

+ 49 - 0
src/components/VideoPlayerViewer/src/VideoPlayerViewer.vue

@@ -0,0 +1,49 @@
+<script setup lang="ts">
+import { VideoPlayer } from '@/components/VideoPlayer'
+import { ElOverlay } from 'element-plus'
+import { ref, nextTick } from 'vue'
+import { Icon } from '@/components/Icon'
+
+const props = defineProps({
+  show: {
+    type: Boolean,
+    default: false
+  },
+  url: {
+    type: String,
+    default: '',
+    required: true
+  },
+  poster: {
+    type: String,
+    default: ''
+  },
+  id: {
+    type: String,
+    default: ''
+  }
+})
+
+const visible = ref(props.show)
+
+const close = async () => {
+  visible.value = false
+  await nextTick()
+  const wrap = document.getElementById(props.id)
+  if (!wrap) return
+  document.body.removeChild(wrap)
+}
+</script>
+<template>
+  <ElOverlay v-show="visible" @click="close">
+    <div class="w-full h-full flex justify-center items-center relative" @click="close">
+      <div
+        class="w-44px h-44px color-[#fff] bg-[var(--el-text-color-regular)] rounded-full border-[#fff] flex justify-center items-center cursor-pointer absolute top-40px right-40px"
+        @click="close"
+      >
+        <Icon icon="ep:close" :size="24" />
+      </div>
+      <VideoPlayer :url="url" :poster="poster" />
+    </div>
+  </ElOverlay>
+</template>

+ 2 - 1
src/locales/en.ts

@@ -178,7 +178,8 @@ export default {
     iconPicker: 'Icon picker',
     request: 'Request',
     waterfall: 'Waterfall',
-    imageCropping: 'Image cropping'
+    imageCropping: 'Image cropping',
+    videoPlayer: 'Video player'
   },
   permission: {
     hasPermission: 'Please set the operation permission value'

+ 2 - 1
src/locales/zh-CN.ts

@@ -176,7 +176,8 @@ export default {
     iconPicker: '图标选择器',
     request: '请求',
     waterfall: '瀑布流',
-    imageCropping: '图片裁剪'
+    imageCropping: '图片裁剪',
+    videoPlayer: '视频播放器'
   },
   permission: {
     hasPermission: '请设置操作权限值'

+ 8 - 0
src/router/index.ts

@@ -342,6 +342,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
         meta: {
           title: t('router.imageCropping')
         }
+      },
+      {
+        path: 'video-player',
+        component: () => import('@/views/Components/VideoPlayer.vue'),
+        name: 'VideoPlayer',
+        meta: {
+          title: t('router.videoPlayer')
+        }
       }
     ]
   },

+ 24 - 0
src/views/Components/VideoPlayer.vue

@@ -0,0 +1,24 @@
+<script setup lang="ts">
+import { VideoPlayer, createVideoViewer } from '@/components/VideoPlayer'
+import { ContentWrap } from '@/components/ContentWrap'
+import { ElButton, ElDivider } from 'element-plus'
+
+const showVideo = () => {
+  createVideoViewer({
+    url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4',
+    poster: '//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/poster.jpg'
+  })
+}
+</script>
+
+<template>
+  <ContentWrap title="视频播放器">
+    <VideoPlayer
+      url="//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4"
+      poster="//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/poster.jpg"
+    />
+
+    <ElDivider />
+    <ElButton type="primary" @click="showVideo">弹窗展示</ElButton>
+  </ContentWrap>
+</template>