diff --git a/components/BubbleTitle.vue b/components/BubbleTitle.vue index 4716e2b..0c941f4 100644 --- a/components/BubbleTitle.vue +++ b/components/BubbleTitle.vue @@ -28,7 +28,7 @@ const props = defineProps({ -
+
diff --git a/components/SlideCreateCourse.vue b/components/SlideCreateCourse.vue index c322f7a..7489079 100644 --- a/components/SlideCreateCourse.vue +++ b/components/SlideCreateCourse.vue @@ -41,7 +41,6 @@ watchEffect(() => { }) const onCreateCourseSubmit = async (event: FormSubmitEvent) => { - console.log(event.data) if (!selected_file.value) { toast.add({ title: '未选择文件', @@ -167,6 +166,11 @@ const onCreateCourseSubmit = async (event: FormSubmitEvent)
+ +import { type InferType, number, object, string } from 'yup' +import ModalDigitalHumanSelect from '~/components/ModalDigitalHumanSelect.vue' +import type { FormSubmitEvent } from '#ui/types' +import { useFetchWrapped } from '~/composables/useFetchWrapped' + +const emit = defineEmits(['success']) + +const slide = useSlideover() +const toast = useToast() +const loginState = useLoginState() + +const creationForm = ref() +const creationPending = ref(false) +const isDigitalSelectorOpen = ref(false) + +const createCourseSchema = object({ + title: string().trim().min(4, '标题必须大于4个字符').max(20, '标题不能超过20个字符').required('请输入视频标题'), + content: string().trim().min(4, '内容必须大于4个字符').max(1000, '内容不能超过1000个字符').required('请输入驱动文本内容'), + digital_human_id: number().not([0], '请选择数字人'), + speed: number().default(1.0).min(0.5).max(1.5).required(), +}) + +type CreateCourseSchema = InferType + +const createCourseState = reactive({ + title: undefined, + content: undefined, + digital_human_id: 0, + speed: 1.0, +}) + +const selected_digital_human = ref(null) + +watchEffect(() => { + if (selected_digital_human.value) { + createCourseState.digital_human_id = selected_digital_human.value.model_id || selected_digital_human.value.id! + } +}) + +const onCreateCourseGreenSubmit = async (event: FormSubmitEvent) => { + creationPending.value = true + useFetchWrapped>('App.Digital_VideoTask.Create', { + token: loginState.token!, + user_id: loginState.user.id, + title: event.data.title, + content: event.data.content, + digital_human_id: event.data.digital_human_id, + speed: 2 - event.data.speed, + device_id: 'XSHAssistant Web', + }).then(res => { + if (!!res.data.task_id) { + toast.add({ + title: '创建成功', + description: '视频已加入生成队列', + color: 'green', + icon: 'i-tabler-check', + }) + emit('success') + slide.close() + } else { + toast.add({ + title: '创建失败', + description: res.msg || '未知错误', + color: 'red', + icon: 'i-tabler-alert-triangle', + }) + } + creationPending.value = false + }).catch(e => { + creationPending.value = false + toast.add({ + title: '创建失败', + description: e.message || '未知错误', + color: 'red', + icon: 'i-tabler-alert-triangle', + }) + }) +} + + + + + \ No newline at end of file diff --git a/components/aigc/course-generate/CGTaskCard.vue b/components/aigc/generation/CGTaskCard.vue similarity index 99% rename from components/aigc/course-generate/CGTaskCard.vue rename to components/aigc/generation/CGTaskCard.vue index 9d90eed..c9cb632 100644 --- a/components/aigc/course-generate/CGTaskCard.vue +++ b/components/aigc/generation/CGTaskCard.vue @@ -3,6 +3,7 @@ import type { PropType } from 'vue' import dayjs from 'dayjs' import { useDownload } from '~/composables/useDownload' import gsap from 'gsap' +import SRTEditor from '~/components/aigc/generation/SRTEditor.vue' const toast = useToast() const { metaSymbol } = useShortcuts() @@ -284,7 +285,7 @@ const copyTaskId = (extraMessage?: string) => { /> - diff --git a/components/aigc/generation/GBTaskCard.vue b/components/aigc/generation/GBTaskCard.vue new file mode 100644 index 0000000..2718304 --- /dev/null +++ b/components/aigc/generation/GBTaskCard.vue @@ -0,0 +1,188 @@ + + + + + \ No newline at end of file diff --git a/components/aigc/course-generate/SRTEditor.vue b/components/aigc/generation/SRTEditor.vue similarity index 100% rename from components/aigc/course-generate/SRTEditor.vue rename to components/aigc/generation/SRTEditor.vue diff --git a/composables/useTourState.ts b/composables/useTourState.ts new file mode 100644 index 0000000..636c177 --- /dev/null +++ b/composables/useTourState.ts @@ -0,0 +1,32 @@ +export const useTourState = defineStore('tour_state', () => { + const tourState = ref<{ [key: string]: boolean }>({}) + + const isTourDone = (tourId: string) => tourState.value[tourId] || false + const setTourDone = (tourId: string) => { + tourState.value = { + ...tourState.value, + [tourId]: true, + } + } + const autoDriveTour = (tourId: string, driver: ReturnType) => { + if (isTourDone(tourId)) return + driver.setConfig({ + ...driver.getConfig(), + onDestroyed: () => setTourDone(tourId), + }) + driver.drive() + } + + return { + tourState, + isTourDone, + setTourDone, + autoDriveTour, + } +}, { + persist: { + key: 'xsh_assistant_tour_state', + storage: persistedState.localStorage, + paths: ['tourState'], + }, +}) \ No newline at end of file diff --git a/nuxt.config.ts b/nuxt.config.ts index 171eb96..6d3aace 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -17,6 +17,8 @@ export default defineNuxtConfig({ '@vite-pwa/nuxt', '@nuxtjs/google-fonts', '@nuxt/image', + '@vueuse/nuxt', + 'nuxt-driver.js', ], icon: { @@ -30,7 +32,7 @@ export default defineNuxtConfig({ dayjs: { locales: ['zh', 'en'], - plugins: ['relativeTime', 'utc', 'timezone'], + plugins: ['relativeTime', 'utc', 'timezone', 'duration'], defaultLocale: 'zh', defaultTimezone: 'Asia/Shanghai', }, diff --git a/package.json b/package.json index f31bec2..366e26e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "idb-keyval": "^6.2.1", "markdown-it": "^14.1.0", "nuxt": "^3.12.4", + "nuxt-driver.js": "^0.0.11", "radix-vue": "^1.9.2", "vue": "^3.4.34", "vue-router": "^4.4.0", @@ -38,6 +39,8 @@ "@tailwindcss/typography": "^0.5.13", "@types/markdown-it": "^13.0.9", "@vite-pwa/nuxt": "^0.5.0", + "@vueuse/core": "^10.11.1", + "@vueuse/nuxt": "^10.11.1", "dayjs-nuxt": "^2.1.9", "sass": "^1.77.8" }, diff --git a/pages/aigc/generation.vue b/pages/aigc/generation.vue index 3b7965c..26bc1e7 100644 --- a/pages/aigc/generation.vue +++ b/pages/aigc/generation.vue @@ -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; +} \ No newline at end of file diff --git a/pages/aigc/generation/course.vue b/pages/aigc/generation/course.vue index 2249243..d19ad50 100644 --- a/pages/aigc/generation/course.vue +++ b/pages/aigc/generation/course.vue @@ -1,8 +1,8 @@ \ No newline at end of file diff --git a/pages/aigc/generation/green-screen.vue b/pages/aigc/generation/green-screen.vue index 35c443c..d062f33 100644 --- a/pages/aigc/generation/green-screen.vue +++ b/pages/aigc/generation/green-screen.vue @@ -1,10 +1,204 @@ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b0df6b..568db13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: nuxt: specifier: ^3.12.4 version: 3.12.4(@parcel/watcher@2.4.1)(@types/node@20.14.12)(idb-keyval@6.2.1)(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.19.1)(sass@1.77.8)(terser@5.31.3)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)) + nuxt-driver.js: + specifier: ^0.0.11 + version: 0.0.11(magicast@0.3.4)(rollup@4.19.1) radix-vue: specifier: ^1.9.2 version: 1.9.2(vue@3.4.34) @@ -90,6 +93,12 @@ importers: '@vite-pwa/nuxt': specifier: ^0.5.0 version: 0.5.0(magicast@0.3.4)(rollup@4.19.1)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(workbox-build@7.1.1)(workbox-window@7.1.0) + '@vueuse/core': + specifier: ^10.11.1 + version: 10.11.1(vue@3.4.34) + '@vueuse/nuxt': + specifier: ^10.11.1 + version: 10.11.1(magicast@0.3.4)(nuxt@3.12.4(@parcel/watcher@2.4.1)(@types/node@20.14.12)(idb-keyval@6.2.1)(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.19.1)(sass@1.77.8)(terser@5.31.3)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)))(rollup@4.19.1)(vue@3.4.34) dayjs-nuxt: specifier: ^2.1.9 version: 2.1.9(magicast@0.3.4)(rollup@4.19.1) @@ -1835,6 +1844,9 @@ packages: '@vueuse/core@10.11.0': resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==} + '@vueuse/core@10.11.1': + resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} + '@vueuse/integrations@10.11.0': resolution: {integrity: sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg==} peerDependencies: @@ -1882,9 +1894,20 @@ packages: '@vueuse/metadata@10.11.0': resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==} + '@vueuse/metadata@10.11.1': + resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} + + '@vueuse/nuxt@10.11.1': + resolution: {integrity: sha512-UiaYSIwOkmUVn8Gl1AqtLWYR12flO+8sEu9X0Y1fNjSR7EWy9jMuiCvOGqwtoeTsqfHrivl0d5HfMzr11GFnMA==} + peerDependencies: + nuxt: ^3.0.0 + '@vueuse/shared@10.11.0': resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==} + '@vueuse/shared@10.11.1': + resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -2547,6 +2570,9 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + driver.js@1.3.1: + resolution: {integrity: sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ==} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -3602,6 +3628,9 @@ packages: engines: {node: ^16.10.0 || >=18.0.0} hasBin: true + nuxt-driver.js@0.0.11: + resolution: {integrity: sha512-WzcNjEk7VGyMrt6vKAQGXiaf/sHp7cfGYtZ1O6fxow3DYWJ+a2NHsLE9ejumSSH753JKzmLNpJRrX0YYFCnl9g==} + nuxt@3.12.4: resolution: {integrity: sha512-/ddvyc2kgYYIN2UEjP8QIz48O/W3L0lZm7wChIDbOCj0vF/yLLeZHBaTb3aNvS9Hwp269nfjrm8j/mVxQK4RhA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -6493,7 +6522,7 @@ snapshots: '@tailwindcss/container-queries': 0.1.1(tailwindcss@3.4.7) '@tailwindcss/forms': 0.5.7(tailwindcss@3.4.7) '@tailwindcss/typography': 0.5.13(tailwindcss@3.4.7) - '@vueuse/core': 10.11.0(vue@3.4.34) + '@vueuse/core': 10.11.1(vue@3.4.34) '@vueuse/integrations': 10.11.0(fuse.js@6.6.2)(idb-keyval@6.2.1)(vue@3.4.34) '@vueuse/math': 10.11.0(vue@3.4.34) defu: 6.1.4 @@ -7165,6 +7194,16 @@ snapshots: - '@vue/composition-api' - vue + '@vueuse/core@10.11.1(vue@3.4.34)': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 10.11.1 + '@vueuse/shared': 10.11.1(vue@3.4.34) + vue-demi: 0.14.10(vue@3.4.34) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + '@vueuse/integrations@10.11.0(fuse.js@6.6.2)(idb-keyval@6.2.1)(vue@3.4.34)': dependencies: '@vueuse/core': 10.11.0(vue@3.4.34) @@ -7187,6 +7226,23 @@ snapshots: '@vueuse/metadata@10.11.0': {} + '@vueuse/metadata@10.11.1': {} + + '@vueuse/nuxt@10.11.1(magicast@0.3.4)(nuxt@3.12.4(@parcel/watcher@2.4.1)(@types/node@20.14.12)(idb-keyval@6.2.1)(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.19.1)(sass@1.77.8)(terser@5.31.3)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)))(rollup@4.19.1)(vue@3.4.34)': + dependencies: + '@nuxt/kit': 3.12.4(magicast@0.3.4)(rollup@4.19.1) + '@vueuse/core': 10.11.1(vue@3.4.34) + '@vueuse/metadata': 10.11.1 + local-pkg: 0.5.0 + nuxt: 3.12.4(@parcel/watcher@2.4.1)(@types/node@20.14.12)(idb-keyval@6.2.1)(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.19.1)(sass@1.77.8)(terser@5.31.3)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)) + vue-demi: 0.14.10(vue@3.4.34) + transitivePeerDependencies: + - '@vue/composition-api' + - magicast + - rollup + - supports-color + - vue + '@vueuse/shared@10.11.0(vue@3.4.34)': dependencies: vue-demi: 0.14.10(vue@3.4.34) @@ -7194,6 +7250,13 @@ snapshots: - '@vue/composition-api' - vue + '@vueuse/shared@10.11.1(vue@3.4.34)': + dependencies: + vue-demi: 0.14.10(vue@3.4.34) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + abbrev@1.1.1: {} abort-controller@3.0.0: @@ -7866,6 +7929,8 @@ snapshots: dotenv@16.4.5: {} + driver.js@1.3.1: {} + duplexer@0.1.2: {} eastasianwidth@0.2.0: {} @@ -9126,6 +9191,15 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + nuxt-driver.js@0.0.11(magicast@0.3.4)(rollup@4.19.1): + dependencies: + '@nuxt/kit': 3.12.4(magicast@0.3.4)(rollup@4.19.1) + driver.js: 1.3.1 + transitivePeerDependencies: + - magicast + - rollup + - supports-color + nuxt@3.12.4(@parcel/watcher@2.4.1)(@types/node@20.14.12)(idb-keyval@6.2.1)(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.19.1)(sass@1.77.8)(terser@5.31.3)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)): dependencies: '@nuxt/devalue': 2.0.2 @@ -9645,7 +9719,7 @@ snapshots: '@internationalized/date': 3.5.5 '@internationalized/number': 3.5.3 '@tanstack/vue-virtual': 3.8.3(vue@3.4.34) - '@vueuse/core': 10.11.0(vue@3.4.34) + '@vueuse/core': 10.11.1(vue@3.4.34) '@vueuse/shared': 10.11.0(vue@3.4.34) aria-hidden: 1.2.4 defu: 6.1.4 diff --git a/typings/types.d.ts b/typings/types.d.ts index 44e26f8..908b881 100644 --- a/typings/types.d.ts +++ b/typings/types.d.ts @@ -40,6 +40,24 @@ interface DigitalHumanItem { digital_human_id: number } +interface GBVideoItem { + id: number + user_id: number + task_id: string + create_time: number + complete_time: number + progress: number + duration?: number + digital_human_id: number + title: string + content: string + bg_img: string + video_url?: string + video_cover?: string + subtitle?: string + speed: number +} + // Common request and response schemas namespace req { namespace user { @@ -115,6 +133,29 @@ namespace req { page?: number perpage?: number } + + /** + * @param title 任务标题筛选 + */ + interface GBVideoList { + to_user_id: number + page?: number + perpage?: number + title?: string + } + + interface GBVideoCreate { + device_id: string + digital_human_id: number + title: string + content: string + bg_img?: string + speed?: number + } + + interface GBVideoDelete { + task_id: string + } } interface AssistantTemplateList { @@ -191,6 +232,21 @@ namespace resp { video_sub_id: number url: string } + + /** + * @param request_id 任务 ID,同 task_id + */ + interface GBVideoCreate { + data_id: string + task_id: string + } + + /** + * @param code 1: 删除成功, 0: 删除失败 + */ + interface GBVideoDelete { + code: 0 | 1 + } } }