feat: 视频组件
This commit is contained in:
@@ -112,11 +112,18 @@
|
|||||||
<GeneratorPhotoComponent
|
<GeneratorPhotoComponent
|
||||||
v-else-if="
|
v-else-if="
|
||||||
item.toolCall &&
|
item.toolCall &&
|
||||||
item.toolCall.componentName ===
|
item.toolCall.componentName === CompName.aigcPhotoGeneratorCard
|
||||||
CompName.aigcPhotoGeneratorCard
|
|
||||||
"
|
"
|
||||||
:toolCall="item.toolCall"
|
:toolCall="item.toolCall"
|
||||||
/>
|
/>
|
||||||
|
<AigcPhotoCard
|
||||||
|
v-else-if="
|
||||||
|
item.toolCall &&
|
||||||
|
item.toolCall.componentName === CompName.videoCard
|
||||||
|
"
|
||||||
|
:toolCall="item.toolCall"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- <ZModuleC01
|
<!-- <ZModuleC01
|
||||||
v-else-if="
|
v-else-if="
|
||||||
item.toolCall &&
|
item.toolCall &&
|
||||||
@@ -248,6 +255,7 @@ import DetailCardCompontent from "../../ChatModule/DetailCardCompontent/index.vu
|
|||||||
import OpenMapComponent from "../../ChatModule/OpenMapComponent/index.vue";
|
import OpenMapComponent from "../../ChatModule/OpenMapComponent/index.vue";
|
||||||
import AnswerComponent from "../../ChatModule/AnswerComponent/index.vue";
|
import AnswerComponent from "../../ChatModule/AnswerComponent/index.vue";
|
||||||
import GeneratorPhotoComponent from "../../ChatModule/GeneratorPhotoComponent/index.vue";
|
import GeneratorPhotoComponent from "../../ChatModule/GeneratorPhotoComponent/index.vue";
|
||||||
|
import AigcPhotoCard from "../../ChatModule/AigcPhotoCard/index.vue";
|
||||||
|
|
||||||
import ZModuleC01 from "../../ChatModule/ZModuleC01/index.vue";
|
import ZModuleC01 from "../../ChatModule/ZModuleC01/index.vue";
|
||||||
import LongTextGuideCardPreview from "../../ChatModule/LongTextGuideCardPreview/index.vue";
|
import LongTextGuideCardPreview from "../../ChatModule/LongTextGuideCardPreview/index.vue";
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<view
|
<view class="w-full bg-white border-box border-ff overflow-hidden rounded-20 flex flex-col">
|
||||||
class="aigc-photo-card relative rounded-24 overflow-hidden w-full"
|
<!-- 占位撑开 -->
|
||||||
:class="{ 'is-disabled': disabled }"
|
<view class="w-vw"></view>
|
||||||
@click="handleAction"
|
|
||||||
>
|
|
||||||
<image
|
|
||||||
class="aigc-photo-card__image block w-full"
|
|
||||||
:src="data.cover"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
<view class="aigc-photo-card__shade"></view>
|
|
||||||
|
|
||||||
<view class="aigc-photo-card__play flex flex-items-center flex-justify-center rounded-full">
|
<view class="aigc-photo-card relative rounded-24 overflow-hidden w-full"
|
||||||
<view class="aigc-photo-card__triangle"></view>
|
:class="{ 'is-disabled': disabled, 'is-empty': !videoUrl }" @click="handleAction">
|
||||||
</view>
|
<video v-if="videoUrl" :id="videoId" class="aigc-photo-card__video block w-full" :src="videoUrl" :poster="poster"
|
||||||
|
:controls="!disabled" :show-center-play-btn="!disabled" :show-play-btn="!disabled"
|
||||||
<view class="aigc-photo-card__title color-white font-size-16 font-900">
|
:show-fullscreen-btn="!disabled" :enable-progress-gesture="!disabled"
|
||||||
{{ data.title }}
|
:direction="fullscreenDirection" object-fit="cover" @click.stop @play="handlePlay"
|
||||||
|
@fullscreenchange="handleFullScreenChange" @error="handleError" />
|
||||||
|
<view
|
||||||
|
v-if="videoUrl && !disabled"
|
||||||
|
class="aigc-photo-card__fullscreen flex flex-items-center flex-justify-center color-white font-size-12"
|
||||||
|
@click.stop="requestFullScreen"
|
||||||
|
>
|
||||||
|
全屏
|
||||||
|
</view>
|
||||||
|
<view v-if="!videoUrl" class="aigc-photo-card__empty flex flex-items-center flex-justify-center color-white font-size-14">
|
||||||
|
视频地址为空
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed, getCurrentInstance } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
toolCall: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
@@ -33,12 +43,75 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["select", "action"]);
|
const emit = defineEmits(["select", "action", "play", "fullscreenchange", "error"]);
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
const videoId = `aigc-video-${Math.random().toString(36).slice(2, 10)}`;
|
||||||
|
|
||||||
|
const cardData = computed(() => ({
|
||||||
|
...props.data,
|
||||||
|
...(props.toolCall?.componentNameParams || {}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const videoUrl = computed(
|
||||||
|
() => cardData.value.videoUrl || cardData.value.url || cardData.value.src || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
const poster = computed(
|
||||||
|
() => cardData.value.poster || cardData.value.cover || cardData.value.coverUrl || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
const title = computed(() => cardData.value.title || cardData.value.name || "");
|
||||||
|
|
||||||
|
const fullscreenDirection = computed(
|
||||||
|
() => cardData.value.fullscreenDirection ?? cardData.value.direction ?? 90
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventPayload = computed(() => {
|
||||||
|
if (props.toolCall && Object.keys(props.toolCall).length > 0) {
|
||||||
|
return {
|
||||||
|
...props.toolCall,
|
||||||
|
componentNameParams: cardData.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return cardData.value;
|
||||||
|
});
|
||||||
|
|
||||||
const handleAction = () => {
|
const handleAction = () => {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
emit("select", props.data);
|
emit("select", eventPayload.value);
|
||||||
emit("action", props.data);
|
emit("action", eventPayload.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePlay = () => {
|
||||||
|
if (props.disabled) return;
|
||||||
|
emit("play", eventPayload.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestFullScreen = () => {
|
||||||
|
if (!videoUrl.value || props.disabled) return;
|
||||||
|
|
||||||
|
const videoContext = uni.createVideoContext(
|
||||||
|
videoId,
|
||||||
|
instance?.proxy || instance
|
||||||
|
);
|
||||||
|
videoContext?.play?.();
|
||||||
|
videoContext?.requestFullScreen?.({
|
||||||
|
direction: Number(fullscreenDirection.value),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFullScreenChange = (event) => {
|
||||||
|
emit("fullscreenchange", {
|
||||||
|
event,
|
||||||
|
data: eventPayload.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleError = (event) => {
|
||||||
|
emit("error", {
|
||||||
|
event,
|
||||||
|
data: eventPayload.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export default {
|
|||||||
id: "xiaoqikong-flying",
|
id: "xiaoqikong-flying",
|
||||||
title: "沉浸式飞越 · 小七孔",
|
title: "沉浸式飞越 · 小七孔",
|
||||||
cover: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1000&q=80",
|
cover: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1000&q=80",
|
||||||
|
videoUrl: "https://oss.nianxx.cn/config/03e179416ff9c4990eba9e70a4448a6f.mp4",
|
||||||
action: {
|
action: {
|
||||||
type: "play",
|
type: "play",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.aigc-photo-card {
|
.aigc-photo-card {
|
||||||
height: 272px;
|
height: 220px;
|
||||||
background: #0f172a;
|
background: #0f172a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,44 +11,43 @@
|
|||||||
opacity: 0.55;
|
opacity: 0.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aigc-photo-card__image {
|
.aigc-photo-card__video {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aigc-photo-card__shade {
|
.aigc-photo-card.is-disabled .aigc-photo-card__video {
|
||||||
position: absolute;
|
pointer-events: none;
|
||||||
inset: 0;
|
|
||||||
background: rgba(15, 23, 42, 0.38);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.aigc-photo-card__play {
|
.aigc-photo-card__fullscreen {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
top: 10px;
|
||||||
top: 50%;
|
right: 10px;
|
||||||
width: 76px;
|
min-width: 44px;
|
||||||
height: 76px;
|
height: 28px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.48);
|
padding: 0 10px;
|
||||||
background: rgba(255, 255, 255, 0.28);
|
border-radius: 14px;
|
||||||
transform: translate(-50%, -50%);
|
background: rgba(15, 23, 42, 0.72);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aigc-photo-card__triangle {
|
.aigc-photo-card__fullscreen:active {
|
||||||
width: 0;
|
opacity: 0.82;
|
||||||
height: 0;
|
}
|
||||||
margin-left: 6px;
|
|
||||||
border-top: 17px solid transparent;
|
.aigc-photo-card__empty {
|
||||||
border-bottom: 17px solid transparent;
|
height: 100%;
|
||||||
border-left: 23px solid #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.aigc-photo-card__title {
|
.aigc-photo-card__title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 72px;
|
top: 16px;
|
||||||
|
padding: 0 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.32);
|
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.32);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user