diff --git a/src/components/DiyEditor/components/ComponentContainerProperty.vue b/src/components/DiyEditor/components/ComponentContainerProperty.vue index 0cfbbe1e..5c5ed9a0 100644 --- a/src/components/DiyEditor/components/ComponentContainerProperty.vue +++ b/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> diff --git a/src/components/DiyEditor/components/mobile/VideoPlayer/config.ts b/src/components/DiyEditor/components/mobile/VideoPlayer/config.ts new file mode 100644 index 00000000..30501cb8 --- /dev/null +++ b/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> diff --git a/src/components/DiyEditor/components/mobile/VideoPlayer/index.vue b/src/components/DiyEditor/components/mobile/VideoPlayer/index.vue new file mode 100644 index 00000000..a62dea08 --- /dev/null +++ b/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> diff --git a/src/components/DiyEditor/components/mobile/VideoPlayer/property.vue b/src/components/DiyEditor/components/mobile/VideoPlayer/property.vue new file mode 100644 index 00000000..96f317d6 --- /dev/null +++ b/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> diff --git a/src/components/DiyEditor/util.ts b/src/components/DiyEditor/util.ts index 0f246ac1..29b44cf6 100644 --- a/src/components/DiyEditor/util.ts +++ b/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: '会员组件', diff --git a/src/components/UploadFile/src/UploadFile.vue b/src/components/UploadFile/src/UploadFile.vue index c8a3b972..42ed4f1d 100644 --- a/src/components/UploadFile/src/UploadFile.vue +++ b/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">