feat: 绿幕视频创建和微课视频创建

This commit is contained in:
2024-08-17 17:56:35 +08:00
parent 24629f8720
commit e48a744f60
14 changed files with 797 additions and 41 deletions

View File

@@ -73,4 +73,27 @@ onMounted(() => {
.subpage-leave-to {
@apply opacity-0 translate-x-4;
}
.loading-screen-leave-active {
@apply transition-all duration-300;
}
.loading-screen-leave-to {
@apply opacity-0;
}
.card-move,
.card-enter-active,
.card-leave-active {
@apply transition-all duration-300;
}
.card-enter-from,
.card-leave-to {
@apply opacity-0;
}
.card-leave-active {
@apply absolute;
}
</style>

View File

@@ -1,8 +1,8 @@
<script lang="ts" setup>
import CGTaskCard from '~/components/aigc/course-generate/CGTaskCard.vue'
import { useFetchWrapped } from '~/composables/useFetchWrapped'
import CGTaskCard from '~/components/aigc/generation/CGTaskCard.vue'
import ModalAuthentication from '~/components/ModalAuthentication.vue'
import SlideCreateCourse from '~/components/SlideCreateCourse.vue'
import { useFetchWrapped } from '~/composables/useFetchWrapped'
const toast = useToast()
const modal = useModal()
@@ -90,7 +90,7 @@ onMounted(() => {
</script>
<template>
<div class="font-sans h-full">
<div>
<div class="p-4 pb-0">
<BubbleTitle subtitle="VIDEOS" title="我的微课视频">
<template #action>
@@ -102,20 +102,22 @@ onMounted(() => {
size="md"
variant="solid"
@click="() => {
if (!loginState.is_logged_in) {
modal.open(ModalAuthentication)
return
}
onCreateCourseClick()
}"
if (!loginState.is_logged_in) {
modal.open(ModalAuthentication)
return
}
onCreateCourseClick()
}"
/>
</template>
</BubbleTitle>
<GradientDivider/>
</div>
<Transition name="loading-screen">
<div v-if="courseList?.data.items.length === 0"
class="w-full h-full flex flex-col justify-center items-center gap-2 bg-neutral-100 dark:bg-neutral-900">
<div
v-if="courseList?.data.items.length === 0"
class="w-full py-20 flex flex-col justify-center items-center gap-2"
>
<Icon class="text-7xl text-neutral-300 dark:text-neutral-700" name="i-tabler-photo-hexagon"/>
<p class="text-sm text-neutral-500 dark:text-neutral-400">
没有记录
@@ -137,7 +139,7 @@ onMounted(() => {
</TransitionGroup>
</div>
<div class="flex justify-end mt-4">
<UPagination v-model="page" :page-count="16" :total="courseList?.data.total || 0"/>
<UPagination v-model="page" :max="9" :page-count="16" :total="courseList?.data.total || 0"/>
</div>
</div>
</Transition>
@@ -145,26 +147,5 @@ onMounted(() => {
</template>
<style scoped>
.loading-screen-leave-active {
@apply transition-all duration-300;
}
.loading-screen-leave-to {
@apply opacity-0;
}
.card-move,
.card-enter-active,
.card-leave-active {
@apply transition-all duration-300;
}
.card-enter-from,
.card-leave-to {
@apply opacity-0;
}
.card-leave-active {
@apply absolute;
}
</style>

View File

@@ -1,10 +1,204 @@
<script lang="ts" setup>
import { useFetchWrapped } from '~/composables/useFetchWrapped'
import GBTaskCard from '~/components/aigc/generation/GBTaskCard.vue'
import { useTourState } from '~/composables/useTourState'
import SlideCreateCourseGreen from '~/components/SlideCreateCourseGreen.vue'
const route = useRoute()
const slide = useSlideover()
const toast = useToast()
const loginState = useLoginState()
const tourState = useTourState()
const page = ref(1)
const pageCount = ref(15)
const searchInput = ref('')
const debounceSearch = refDebounced(searchInput, 1000)
watch(debounceSearch, () => page.value = 1)
const {
data: videoList,
refresh: refreshVideoList,
} = useAsyncData(
() => useFetchWrapped<
req.gen.GBVideoList & AuthedRequest,
BaseResponse<PagedData<GBVideoItem>>
>('App.Digital_VideoTask.GetList', {
token: loginState.token!,
user_id: loginState.user.id,
to_user_id: loginState.user.id,
page: page.value,
perpage: pageCount.value,
title: debounceSearch.value,
}), {
watch: [page, pageCount, debounceSearch],
},
)
const onCreateCourseGreenClick = () => {
slide.open(SlideCreateCourseGreen, {
onSuccess: () => {
refreshVideoList()
},
})
}
const onCourseGreenDelete = (task: GBVideoItem) => {
if (!task.task_id) return
useFetchWrapped<
req.gen.GBVideoDelete & AuthedRequest,
BaseResponse<resp.gen.GBVideoDelete>
>('App.Digital_VideoTask.Delete', {
token: loginState.token!,
user_id: loginState.user.id,
task_id: task.task_id,
}).then(res => {
if (res.data.code === 1) {
refreshVideoList()
toast.add({
title: '删除成功',
description: '已删除任务记录',
color: 'green',
icon: 'i-tabler-check',
})
} else {
toast.add({
title: '删除失败',
description: res.msg || '未知错误',
color: 'red',
icon: 'i-tabler-alert-triangle',
})
}
})
}
const beforeLeave = (el: any) => {
el.style.width = `${ el.offsetWidth }px`
el.style.height = `${ el.offsetHeight }px`
}
const leave = (el: any, done: Function) => {
el.style.position = 'absolute'
el.style.transition = 'none' // 取消过渡动画
el.style.opacity = 0 // 立即隐藏元素
done()
}
onMounted(() => {
const i = setInterval(refreshVideoList, 1000 * 5)
onBeforeUnmount(() => clearInterval(i))
const driver = useDriver({
showProgress: true,
animate: true,
smoothScroll: true,
disableActiveInteraction: true,
popoverOffset: 12,
progressText: '{{current}} / {{total}}',
prevBtnText: '上一步',
nextBtnText: '下一步',
doneBtnText: '完成',
steps: [
{
element: '#button-create',
popover: {
title: '新建视频',
description: '点击这里开始新建绿幕视频',
},
},
{
element: '#input-search',
popover: {
title: '搜索生成记录',
description: '在这里输入视频标题,可以搜索符合条件的生成记录',
},
},
],
})
tourState.autoDriveTour(route.fullPath, driver)
})
</script>
<template>
<div>
绿幕视频生成
<div class="h-full">
<div class="p-4 pb-0">
<BubbleTitle
:subtitle="!debounceSearch ? 'GB VIDEOS' : 'SEARCH...'"
:title="!debounceSearch ? '我的绿幕视频' : `标题搜索:${debounceSearch.toLocaleUpperCase()}`"
>
<template #action>
<UButtonGroup size="md">
<UInput
id="input-search"
v-model="searchInput"
:autofocus="false"
:ui="{ icon: { trailing: { pointer: '' } } }"
autocomplete="off"
placeholder="标题搜索"
variant="outline"
>
<template #trailing>
<UButton
v-show="searchInput !== ''"
:padded="false"
color="gray"
icon="i-tabler-x"
variant="link"
@click="searchInput = ''"
/>
</template>
</UInput>
</UButtonGroup>
<UButton
id="button-create"
:trailing="false"
color="primary"
icon="i-tabler-plus"
label="新建"
size="md"
variant="solid"
@click="onCreateCourseGreenClick"
/>
</template>
</BubbleTitle>
<GradientDivider/>
</div>
<Transition name="loading-screen">
<div
v-if="videoList?.data.items.length === 0"
class="w-full py-20 flex flex-col justify-center items-center gap-2"
>
<Icon class="text-7xl text-neutral-300 dark:text-neutral-700" name="i-tabler-photo-hexagon"/>
<p class="text-sm text-neutral-500 dark:text-neutral-400">
没有记录
</p>
</div>
<div v-else>
<div class="p-4">
<div class="relative grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-3 fhd:grid-cols-5 gap-4">
<TransitionGroup
name="card"
@beforeLeave="beforeLeave"
@leave="leave"
>
<GBTaskCard
v-for="(v, i) in videoList?.data.items"
:key="v.task_id"
:video="v"
@delete="v => onCourseGreenDelete(v)"
/>
</TransitionGroup>
</div>
<div class="flex justify-end mt-4">
<UPagination v-model="page" :max="9" :page-count="pageCount" :total="videoList?.data.total || 0"/>
</div>
</div>
</div>
</Transition>
</div>
</template>