Merge remote-tracking branch 'origin/main'
# Conflicts: # layouts/default.vue # nuxt.config.ts # package.json # yarn.lock
This commit is contained in:
8
app.vue
8
app.vue
@@ -6,6 +6,12 @@ const router = useRouter()
|
|||||||
const modal = useModal()
|
const modal = useModal()
|
||||||
const loginState = useLoginState()
|
const loginState = useLoginState()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
titleTemplate(title) {
|
||||||
|
return title ? `${title} - 眩生花 AI 助手` : '眩生花 AI 助手'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
viewport: 'width=device-width, initial-scale=1.0, user-scalable=no',
|
viewport: 'width=device-width, initial-scale=1.0, user-scalable=no',
|
||||||
})
|
})
|
||||||
@@ -27,6 +33,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<NuxtLoadingIndicator />
|
||||||
|
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<NuxtPage/>
|
<NuxtPage/>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
html {
|
html {
|
||||||
font-family: 'Rubik', 'Noto Sans SC', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
components/BubbleTitle.vue
Normal file
38
components/BubbleTitle.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
bubble: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="relative font-sans select-none">
|
||||||
|
<h1
|
||||||
|
v-if="subtitle"
|
||||||
|
class="text-base text-neutral-300 italic tracking-wide font-black leading-none"
|
||||||
|
>{{ subtitle }}</h1>
|
||||||
|
|
||||||
|
<h1 class="text-xl font-bold text-neutral-700 leading-none relative z-[1]">
|
||||||
|
{{ title }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="bubble"
|
||||||
|
class="absolute -left-1.5 -bottom-1.5 w-4 h-4 rounded-full bg-primary-500/50 z-[0]"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
19
components/GradientDivider.vue
Normal file
19
components/GradientDivider.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
vertical: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="bg-gradient-to-r from-primary-500/50 to-primary-300/50 rounded-full my-4"
|
||||||
|
:class="{'w-full h-[1px]': !vertical, 'w-[1px] h-full': vertical}"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,24 +1,38 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
|
import ModalAuthentication from '~/components/ModalAuthentication.vue'
|
||||||
|
|
||||||
const loginState = useLoginState()
|
const loginState = useLoginState()
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
contentClass: {
|
contentClass: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: '',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const modal = useModal()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<ClientOnly>
|
||||||
<div v-if="!loginState.is_logged_in"
|
<div v-if="!loginState.is_logged_in"
|
||||||
class="w-full flex flex-col justify-center items-center gap-2 bg-neutral-100 dark:bg-neutral-900">
|
class="w-full flex flex-col justify-center items-center gap-2 bg-neutral-100 dark:bg-neutral-900">
|
||||||
<Icon name="i-tabler-user-circle" class="text-7xl text-neutral-300 dark:text-neutral-700"/>
|
<Icon name="i-tabler-user-circle" class="text-7xl text-neutral-300 dark:text-neutral-700"/>
|
||||||
<p class="text-sm text-neutral-500 dark:text-neutral-400">请登录后使用</p>
|
<p class="text-sm text-neutral-500 dark:text-neutral-400">请登录后使用</p>
|
||||||
<UButton class="mt-2 font-bold" to="/login" color="black" variant="solid" size="xs">登录</UButton>
|
<UButton
|
||||||
|
class="mt-2 font-bold"
|
||||||
|
color="black"
|
||||||
|
variant="solid"
|
||||||
|
size="xs"
|
||||||
|
@click="modal.open(ModalAuthentication)"
|
||||||
|
>
|
||||||
|
登录
|
||||||
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
<div :class="contentClass" v-else>
|
<div :class="contentClass" v-else>
|
||||||
<slot/>
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
|
</ClientOnly>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
37
components/ppt/NavItem.vue
Normal file
37
components/ppt/NavItem.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: 'i-tabler-photo-filled',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const active = computed(() => {
|
||||||
|
return route.path === props.to
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NuxtLink
|
||||||
|
class="px-4 py-3 flex items-center gap-2 rounded-lg transition cursor-pointer"
|
||||||
|
:class="active ? 'bg-primary text-white' : 'hover:bg-neutral-200'"
|
||||||
|
:to="to"
|
||||||
|
>
|
||||||
|
<Icon :name="icon" class="text-xl inline"/>
|
||||||
|
<h1 class="text-[14px] font-medium">{{ label }}</h1>
|
||||||
|
</NuxtLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
93
components/ppt/PPTGenerationRecord.vue
Normal file
93
components/ppt/PPTGenerationRecord.vue
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
record: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const dayjs = useDayjs()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="relative w-full aspect-video rounded-lg overflow-hidden shadow"
|
||||||
|
>
|
||||||
|
<NuxtImg :src="record.video_cover"></NuxtImg>
|
||||||
|
<div class="absolute inset-0 flex flex-col justify-between bg-black/10 bg-gradient-to-t from-black/20">
|
||||||
|
<div class="flex justify-between items-start p-2.5 gap-2">
|
||||||
|
<div>
|
||||||
|
<UButton icon="i-solar-play-circle-bold-duotone">预览</UButton>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-end gap-1">
|
||||||
|
<UTooltip :text="record.task_id" :close-delay="300">
|
||||||
|
<h1 class="text-white text-xs font-bold font-sans">
|
||||||
|
ID: {{ record.task_id.slice(0, 6) }}
|
||||||
|
</h1>
|
||||||
|
</UTooltip>
|
||||||
|
<UTooltip :text="dayjs(record.create_time * 1000).format('YYYY-MM-DD HH:mm:ss')">
|
||||||
|
<h1 class="text-white text-xs font-bold font-sans">
|
||||||
|
{{ dayjs(record.create_time * 1000).fromNow() }}
|
||||||
|
</h1>
|
||||||
|
</UTooltip>
|
||||||
|
</div>
|
||||||
|
<!-- <UProgress-->
|
||||||
|
<!-- size="md"-->
|
||||||
|
<!-- indicator-->
|
||||||
|
<!-- :ui="{-->
|
||||||
|
<!-- wrapper: 'flex-col-reverse',-->
|
||||||
|
<!-- progress: {-->
|
||||||
|
<!-- base: '!bg-opacity-50'-->
|
||||||
|
<!-- }-->
|
||||||
|
<!-- }"-->
|
||||||
|
<!-- :value="10"-->
|
||||||
|
<!-- :max="100"-->
|
||||||
|
<!-- :animation="'carousel'"-->
|
||||||
|
<!-- />-->
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center p-2.5 gap-2">
|
||||||
|
<div class="overflow-hidden whitespace-nowrap">
|
||||||
|
<UTooltip
|
||||||
|
:text="record.title"
|
||||||
|
:popper="{
|
||||||
|
placement: 'bottom-start'
|
||||||
|
}"
|
||||||
|
:open-delay="300"
|
||||||
|
:close-delay="300"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
<h1 class="text-white text-base font-bold font-sans drop-shadow overflow-hidden text-ellipsis leading-none">
|
||||||
|
{{ record.title }}
|
||||||
|
</h1>
|
||||||
|
</UTooltip>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 whitespace-nowrap flex gap-1.5">
|
||||||
|
<UButton
|
||||||
|
size="xs"
|
||||||
|
color="red"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-tabler-trash"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-solar-subtitles-linear"
|
||||||
|
>
|
||||||
|
字幕
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
size="xs"
|
||||||
|
icon="i-tabler-download"
|
||||||
|
>
|
||||||
|
下载
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -17,8 +17,8 @@ import { trimObject } from '@uniiem/object-trim'
|
|||||||
import ModalAuthentication from '~/components/ModalAuthentication.vue'
|
import ModalAuthentication from '~/components/ModalAuthentication.vue'
|
||||||
import NewSessionScreen from '~/components/aigc/chat/NewSessionScreen.vue'
|
import NewSessionScreen from '~/components/aigc/chat/NewSessionScreen.vue'
|
||||||
|
|
||||||
useHead({
|
useSeoMeta({
|
||||||
title: '聊天 | XSH AI',
|
title: '聊天',
|
||||||
})
|
})
|
||||||
|
|
||||||
const dayjs = useDayjs()
|
const dayjs = useDayjs()
|
||||||
|
|||||||
53
pages/aigc/generation.vue
Normal file
53
pages/aigc/generation.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import NavItem from '~/components/ppt/NavItem.vue'
|
||||||
|
|
||||||
|
useSeoMeta({
|
||||||
|
title: '智能生成',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full flex relative">
|
||||||
|
|
||||||
|
<div class="absolute -translate-x-full md:sticky md:translate-x-0 z-10 flex flex-col h-[calc(100vh-4rem)] bg-neutral-100 dark:bg-neutral-900 p-4 w-full md:w-[300px]
|
||||||
|
border-r border-neutral-200 dark:border-neutral-700 transition-all duration-300 ease-out">
|
||||||
|
<div class="flex flex-col flex-1 overflow-auto overflow-x-hidden">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<ClientOnly>
|
||||||
|
<NavItem
|
||||||
|
icon="tabler:presentation-analytics"
|
||||||
|
label="微课视频生成"
|
||||||
|
to="/aigc/generation/video-generate"
|
||||||
|
/>
|
||||||
|
<NavItem
|
||||||
|
icon="tabler:user-screen"
|
||||||
|
label="数字讲师"
|
||||||
|
to="/aigc/generation/digital-teachers"
|
||||||
|
/>
|
||||||
|
</ClientOnly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LoginNeededContent
|
||||||
|
content-class="h-[calc(100vh-4rem)] flex-1 bg-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<Transition name="subpage" mode="out-in">
|
||||||
|
<NuxtPage :page-key="route => route.fullPath"/>
|
||||||
|
</Transition>
|
||||||
|
</LoginNeededContent>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.subpage-enter-active,
|
||||||
|
.subpage-leave-active {
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subpage-enter-from,
|
||||||
|
.subpage-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
16
pages/aigc/generation/digital-teachers.vue
Normal file
16
pages/aigc/generation/digital-teachers.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
useSeoMeta({
|
||||||
|
title: '数字化教师',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<BubbleTitle title="数字化讲师" subtitle="DIGITAL" />
|
||||||
|
<GradientDivider />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
69
pages/aigc/generation/video-generate.vue
Normal file
69
pages/aigc/generation/video-generate.vue
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
useSeoMeta({
|
||||||
|
title: '课程视频生成',
|
||||||
|
})
|
||||||
|
|
||||||
|
const testItem = {
|
||||||
|
'id': 1599,
|
||||||
|
'device_id': 'Test_Device_V3',
|
||||||
|
'user_id': 1,
|
||||||
|
'task_id': 'SQOeN1j2heRoQeGGTFh3Tu2WP9kUcz4L',
|
||||||
|
'create_time': 1713408239,
|
||||||
|
'token': 'not use',
|
||||||
|
'progress': 100,
|
||||||
|
'digital_human_id': 40696,
|
||||||
|
'complete_time': 1713409821,
|
||||||
|
'duration': 1578819,
|
||||||
|
'video_url': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-04-18/75d1e1cee595a7f5758c59289d1a74b9.mp4',
|
||||||
|
'subtitle_url': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-04-18/a95cfef4524e90f5509a5c248e5c2061.srt',
|
||||||
|
'video_cover': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-04-18/b4161a85573fc09be82fa7cf7dd9abfa.png',
|
||||||
|
'custom_video': '[]',
|
||||||
|
'title': '1-2 一键启动:零基础快速搭建Keil开发环境实操教程',
|
||||||
|
'ppt_url': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/material/2024-04-18/0a8827a1ae32ece196536a19bab1dff5.pptx',
|
||||||
|
'opening_url': '',
|
||||||
|
'ending_url': '',
|
||||||
|
'video_duration': 507,
|
||||||
|
'message': 'ok',
|
||||||
|
'speed': 1,
|
||||||
|
}
|
||||||
|
const testItem2 = {
|
||||||
|
'id': 1599,
|
||||||
|
'device_id': 'Test_Device_V3',
|
||||||
|
'user_id': 1,
|
||||||
|
'task_id': 'SQOeN1j2heRoQeGGTFh3Tu2WP9kUcz4L',
|
||||||
|
'create_time': 1713408239,
|
||||||
|
'token': 'not use',
|
||||||
|
'progress': null,
|
||||||
|
'digital_human_id': 40696,
|
||||||
|
'complete_time': 1713409821,
|
||||||
|
'duration': 1578819,
|
||||||
|
'video_url': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-04-18/75d1e1cee595a7f5758c59289d1a74b9.mp4',
|
||||||
|
'subtitle_url': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-04-18/a95cfef4524e90f5509a5c248e5c2061.srt',
|
||||||
|
'video_cover': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-04-18/b4161a85573fc09be82fa7cf7dd9abfa.png',
|
||||||
|
'custom_video': '[]',
|
||||||
|
'title': '1-2 一键启动:零基础快速搭建Keil开发环境实操教程',
|
||||||
|
'ppt_url': 'https://static-xsh.oss-cn-chengdu.aliyuncs.com/material/2024-04-18/0a8827a1ae32ece196536a19bab1dff5.pptx',
|
||||||
|
'opening_url': '',
|
||||||
|
'ending_url': '',
|
||||||
|
'video_duration': 507,
|
||||||
|
'message': 'ok',
|
||||||
|
'speed': 1,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<BubbleTitle title="我的微课" subtitle="VIDEOS"/>
|
||||||
|
<GradientDivider/>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-4">
|
||||||
|
<PPTGenerationRecord :record="testItem"/>
|
||||||
|
<PPTGenerationRecord :record="testItem2"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -12,8 +12,8 @@ import {useHistory} from '~/composables/useHistory';
|
|||||||
import {del, set} from 'idb-keyval';
|
import {del, set} from 'idb-keyval';
|
||||||
import ReferenceFigureSelector from '~/components/aigc/ReferenceFigureSelector.vue';
|
import ReferenceFigureSelector from '~/components/aigc/ReferenceFigureSelector.vue';
|
||||||
|
|
||||||
useHead({
|
useSeoMeta({
|
||||||
title: '绘画 | XSH AI',
|
title: '绘画',
|
||||||
})
|
})
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|||||||
Reference in New Issue
Block a user