Explorar o código

营销:增加商城装修组件【视频播放】

owen hai 1 ano
pai
achega
bb2b7ffd03

+ 7 - 8
src/components/DiyEditor/components/ComponentContainerProperty.vue

@@ -26,7 +26,7 @@
                 :label="data.label"
                 :prop="data.prop"
                 :label-width="node.level === 1 ? '80px' : '62px'"
-                class="tree-form-item w-full m-b-0!"
+                class="w-full m-b-0!"
               >
                 <el-slider
                   v-model="formData[data.prop]"
@@ -40,6 +40,7 @@
               </el-form-item>
             </template>
           </el-tree>
+          <slot name="style" :formData="formData"></slot>
         </el-form>
       </el-card>
     </el-tab-pane>
@@ -153,12 +154,10 @@ const handleSliderChange = (prop: string) => {
 </script>
 
 <style scoped lang="scss">
-.tree-form-item {
-  :deep(.el-slider__runway) {
-    margin-right: 16px;
-  }
-  :deep(.el-input-number) {
-    width: 50px;
-  }
+:deep(.el-slider__runway) {
+  margin-right: 16px;
+}
+:deep(.el-input-number) {
+  width: 50px;
 }
 </style>

+ 37 - 0
src/components/DiyEditor/components/mobile/VideoPlayer/config.ts

@@ -0,0 +1,37 @@
+import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
+
+/** 视频播放属性 */
+export interface VideoPlayerProperty {
+  // 视频链接
+  videoUrl: string
+  // 封面链接
+  posterUrl: string
+  // 是否自动播放
+  autoplay: boolean
+  // 组件样式
+  style: VideoPlayerStyle
+}
+
+// 视频播放样式
+export interface VideoPlayerStyle extends ComponentStyle {
+  // 视频高度
+  height: number
+}
+
+// 定义组件
+export const component = {
+  id: 'VideoPlayer',
+  name: '视频播放',
+  icon: 'ep:video-play',
+  property: {
+    videoUrl: '',
+    posterUrl: '',
+    autoplay: false,
+    style: {
+      bgType: 'color',
+      bgColor: '#fff',
+      marginBottom: 8,
+      height: 300
+    } as ComponentStyle
+  }
+} as DiyComponent<VideoPlayerProperty>

+ 30 - 0
src/components/DiyEditor/components/mobile/VideoPlayer/index.vue

@@ -0,0 +1,30 @@
+<template>
+  <div class="w-full" :style="{ height: `${property.style.height}px` }">
+    <el-image class="w-full w-full" :src="property.posterUrl" v-if="property.posterUrl" />
+    <video
+      v-else
+      class="w-full w-full"
+      :src="property.videoUrl"
+      :poster="property.posterUrl"
+      :autoplay="property.autoplay"
+      controls
+    ></video>
+  </div>
+</template>
+<script setup lang="ts">
+import { VideoPlayerProperty } from './config'
+
+/** 视频播放 */
+defineOptions({ name: 'VideoPlayer' })
+
+defineProps<{ property: VideoPlayerProperty }>()
+</script>
+
+<style scoped lang="scss">
+/* 图片 */
+img {
+  width: 100%;
+  height: 100%;
+  display: block;
+}
+</style>

+ 55 - 0
src/components/DiyEditor/components/mobile/VideoPlayer/property.vue

@@ -0,0 +1,55 @@
+<template>
+  <ComponentContainerProperty v-model="formData.style">
+    <template #style="{ formData }">
+      <el-form-item label="高度" prop="height">
+        <el-slider
+          v-model="formData.height"
+          :max="500"
+          :min="100"
+          show-input
+          input-size="small"
+          :show-input-controls="false"
+        />
+      </el-form-item>
+    </template>
+    <el-form label-width="80px" :model="formData">
+      <el-form-item label="上传视频" prop="videoUrl">
+        <UploadFile
+          v-model="formData.videoUrl"
+          :file-type="['mp4']"
+          :limit="1"
+          :file-size="100"
+          class="min-w-80px"
+        />
+      </el-form-item>
+      <el-form-item label="上传封面" prop="posterUrl">
+        <UploadImg
+          v-model="formData.posterUrl"
+          draggable="false"
+          height="80px"
+          width="100%"
+          class="min-w-80px"
+        >
+          <template #tip> 建议宽度750 </template>
+        </UploadImg>
+      </el-form-item>
+      <el-form-item label="自动播放" prop="autoplay">
+        <el-switch v-model="formData.autoplay" />
+      </el-form-item>
+    </el-form>
+  </ComponentContainerProperty>
+</template>
+
+<script setup lang="ts">
+import { VideoPlayerProperty } from './config'
+import { usePropertyForm } from '@/components/DiyEditor/util'
+
+// 视频播放属性面板
+defineOptions({ name: 'VideoPlayerProperty' })
+
+const props = defineProps<{ modelValue: VideoPlayerProperty }>()
+const emit = defineEmits(['update:modelValue'])
+const { formData } = usePropertyForm(props.modelValue, emit)
+</script>
+
+<style scoped lang="scss"></style>

+ 1 - 1
src/components/DiyEditor/util.ts

@@ -109,7 +109,7 @@ export const PAGE_LIBS = [
       'TitleBar'
     ]
   },
-  { name: '图文组件', extended: true, components: ['ImageBar', 'Carousel'] },
+  { name: '图文组件', extended: true, components: ['ImageBar', 'Carousel', 'VideoPlayer'] },
   { name: '商品组件', extended: true, components: ['ProductCard'] },
   {
     name: '会员组件',

+ 39 - 16
src/components/UploadFile/src/UploadFile.vue

@@ -33,11 +33,10 @@
   </div>
 </template>
 <script lang="ts" setup>
-import { PropType } from 'vue'
-
 import { propTypes } from '@/utils/propTypes'
 import { getAccessToken, getTenantId } from '@/utils/auth'
 import type { UploadInstance, UploadUserFile, UploadProps, UploadRawFile } from 'element-plus'
+import { isArray, isString } from '@/utils/is'
 
 defineOptions({ name: 'UploadFile' })
 
@@ -45,10 +44,7 @@ const message = useMessage() // 消息弹窗
 const emit = defineEmits(['update:modelValue'])
 
 const props = defineProps({
-  modelValue: {
-    type: Array as PropType<UploadUserFile[]>,
-    required: true
-  },
+  modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
   title: propTypes.string.def('文件上传'),
   updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
   fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
@@ -62,7 +58,7 @@ const props = defineProps({
 const valueRef = ref(props.modelValue)
 const uploadRef = ref<UploadInstance>()
 const uploadList = ref<UploadUserFile[]>([])
-const fileList = ref<UploadUserFile[]>(props.modelValue)
+const fileList = ref<UploadUserFile[]>([])
 const uploadNumber = ref<number>(0)
 const uploadHeaders = ref({
   Authorization: 'Bearer ' + getAccessToken(),
@@ -109,7 +105,7 @@ const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
     fileList.value = fileList.value.concat(uploadList.value)
     uploadList.value = []
     uploadNumber.value = 0
-    emit('update:modelValue', listToString(fileList.value))
+    emitUpdateModelValue()
   }
 }
 // 文件数超出提示
@@ -125,20 +121,47 @@ const handleRemove = (file) => {
   const findex = fileList.value.map((f) => f.name).indexOf(file.name)
   if (findex > -1) {
     fileList.value.splice(findex, 1)
-    emit('update:modelValue', listToString(fileList.value))
+    emitUpdateModelValue()
   }
 }
 const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
   console.log(uploadFile)
 }
-// 对象转成指定字符串分隔
-const listToString = (list: UploadUserFile[], separator?: string) => {
-  let strs = ''
-  separator = separator || ','
-  for (let i in list) {
-    strs += list[i].url + separator
+
+// 监听模型绑定值变动
+watch(
+  () => props.modelValue,
+  () => {
+    const files: string[] = []
+    // 情况1:字符串
+    if (isString(props.modelValue)) {
+      // 情况1.1:逗号分隔的多值
+      if (props.modelValue.includes(',')) {
+        files.concat(props.modelValue.split(','))
+      } else {
+        files.push(props.modelValue)
+      }
+    } else if (isArray(props.modelValue)) {
+      // 情况2:字符串
+      files.concat(props.modelValue)
+    } else {
+      throw new Error('不支持的 modelValue 类型')
+    }
+    fileList.value = files.map((url: string) => {
+      return { url, name: url.substring(url.lastIndexOf('/') + 1) } as UploadUserFile
+    })
+  },
+  { immediate: true }
+)
+// 发送文件链接列表更新
+const emitUpdateModelValue = () => {
+  // 情况1:数组结果
+  let result: string | string[] = fileList.value.map((file) => file.url!)
+  // 情况2:逗号分隔的字符串
+  if (isString(props.modelValue)) {
+    result = result.join(',')
   }
-  return strs != '' ? strs.substr(0, strs.length - 1) : ''
+  emit('update:modelValue', result)
 }
 </script>
 <style scoped lang="scss">