营销:增加商城装修组件【视频播放】
							parent
							
								
									fa710b560d
								
							
						
					
					
						commit
						bb2b7ffd03
					
				| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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: '会员组件',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue