Commit 3cd05182 authored by shirlyn.guo's avatar shirlyn.guo 🤡

Merge branch 'master' of https://gitlab.gsstcloud.com/digitalperson/digitalperson-fe into shirlyn

parents 5c798291 37546bd0
......@@ -14,7 +14,7 @@ pipeline {
node -v
pnpm -v
pnpm install
pnpm run build
pnpm run build:sit
cp -r ./dist ./build
'''
}
......
pipeline {
agent {
docker {
image 'cimg/node:18.18.2'
args '-v /root/jenkins/data/jobs/${JOB_NAME}/data/node_modules:${WORKSPACE}/node_modules -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker'
}
}
stages {
stage('BUILD PROJECT') {
steps {
sh '''
npm set registry https://registry.npmmirror.com/
npm install -g pnpm
node -v
pnpm -v
pnpm install
pnpm run build:uat
cp -r ./dist ./build
'''
}
}
stage('BUILD IMAGE') {
steps {
sh '''
cd build
docker build -t $registry_address/$image_name:latest .'''
}
}
stage('PUSH IMAGE') {
steps {
sh '''docker login --username=rcsadmin@gsst -p 9HRpm_hk registry.cn-shenzhen.aliyuncs.com
docker push $registry_address/$image_name:latest'''
}
}
stage('REMOVE LOCAL IMAGE') {
steps {
sh '''docker rmi -f $registry_address/$image_name:latest'''
}
}
stage('Cleanup') {
steps {
cleanWs(deleteDirs: true)
}
}
}
environment {
registry_address = 'registry.cn-shenzhen.aliyuncs.com/gsst'
image_name = 'digitalperson-fe'
}
}
......@@ -4,8 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- <title>DIGITAL_PERSON-FE</title> -->
<title>DIGITAL_PERSON_FE</title>
<title>萃想智能雲創</title>
</head>
<body>
......
export const BASE_URLS: Record<'DEV' | 'PROD', string> = {
DEV: 'https://digitalperson-sit.gsstcloud.com',
PROD: 'https://digitalperson-sit.gsstcloud.com',
PROD: 'https://digitalperson.gsstcloud.com',
}
export const AI_INDEX_URLS: Record<'DEV' | 'PROD', string> = {
......
......@@ -29,8 +29,14 @@ export function createRouterGuards(router: Router) {
next()
})
router.afterEach(() => {
document.title = import.meta.env.VITE_APP_NAME
router.afterEach((to) => {
let title = import.meta.env.VITE_APP_NAME
if (to.meta.title) {
title += ` - ${to.meta.title}`
}
document.title = title
window.$loadingBar.finish()
})
......
import { AudioConfig, LangType, VoiceType } from '@/store/types/creation'
import { AudioConfig, VoiceType } from '@/store/types/creation'
import { defineStore } from 'pinia'
function defaultAudioSetting(): AudioConfig {
return {
langType: LangType.CANTONESE,
voiceType: VoiceType.CANTONESE_FEMALE,
consumption: 0,
}
......@@ -17,10 +16,6 @@ export const useAudioSettingStore = defineStore('audio-setting-store', {
state: (): AudioConfig => getLocalState(),
actions: {
setLanType(langType: LangType) {
this.langType = langType
},
setVoiceType(voiceType: VoiceType) {
this.voiceType = voiceType
},
......
import { DraftConfig, LangType } from '@/store/types/creation'
import { DraftConfig, LangType, ScreenType } from '@/store/types/creation'
import { DriveType, TaskType } from '@/store/types/template'
import { defineStore } from 'pinia'
......@@ -7,6 +7,7 @@ function defaultDigitalCreation(): DraftConfig {
id: null,
coverUrl: null,
draftName: '',
pronunciationLanguage: LangType.CANTONESE,
taskType: TaskType.BASE_VIDEO,
inputImageUrl: null,
driveType: DriveType.TEXT,
......@@ -17,6 +18,7 @@ function defaultDigitalCreation(): DraftConfig {
pitch: '5',
inputAudioUrl: null,
figureId: null,
pageLayout: ScreenType.PORTRAIT,
width: 720,
height: 1280,
cameraId: null,
......@@ -34,7 +36,6 @@ function defaultDigitalCreation(): DraftConfig {
logoUrl: null,
bgmUrl: null,
materialUrl: null,
pronunciationLanguage: LangType.CANTONESE,
}
}
......@@ -114,7 +115,11 @@ export const useDigitalCreationStore = defineStore('digital-creation-store', {
this.enabled = subtitleEnabled
},
updateDigitalCreation(digitalCreation: DraftConfig) {
setPronunciationLanguage(pronunciationLanguage: LangType) {
this.pronunciationLanguage = pronunciationLanguage
},
updateDigitalCreation(digitalCreation: Partial<DraftConfig>) {
this.$state = { ...this.$state, ...digitalCreation }
},
......
......@@ -14,7 +14,7 @@ export function defaultDigitalHumanDialogue(): DigitalHumanDialogueConfig {
figureId: 'A2A_V2-xiaomeng2',
speed: 5,
intonation: 5,
timebreId: '5132',
timebreId: '101019',
},
backgroundInfo: {
backgroundUrl: 'https://digitalpeople-sit.gz.bcebos.com/share/backgroud/%E5%8C%BB%E5%AD%A61.jpg',
......
......@@ -28,7 +28,6 @@ export enum VoiceType {
}
export interface AudioConfig {
langType: LangType
voiceType: VoiceType
consumption: number
}
......@@ -73,6 +72,7 @@ export interface DraftConfig {
id: number | null
coverUrl: string | null
draftName: string
pronunciationLanguage: LangType
taskType: TaskType
inputImageUrl: string | null
driveType: DriveType
......@@ -83,6 +83,7 @@ export interface DraftConfig {
pitch: string
inputAudioUrl: string | null
figureId: string | null
pageLayout: ScreenType
width: number
height: number
cameraId: number | null
......@@ -100,7 +101,6 @@ export interface DraftConfig {
logoUrl: string | null
bgmUrl: string | null
materialUrl: string | null
pronunciationLanguage: LangType
memberId?: number
modifiedTime?: string
}
......
......@@ -168,11 +168,16 @@ function onImageLoaded(index: number) {
@click="handleClickImage(image)"
>
<img class="h-full w-full object-contain" :src="image.imageUrl" @load="onImageLoaded(index)" />
<div
class="absolute bottom-0 h-5 w-full truncate bg-gradient-to-t from-gray-600 px-1 text-xs leading-5 text-white"
>
{{ image.imageName }}
</div>
<n-popover trigger="hover" placement="bottom">
<template #trigger>
<div
class="absolute bottom-0 line-clamp-1 h-5 w-full break-all bg-gradient-to-t from-gray-600 px-1 text-xs leading-5 text-white"
>
{{ image.imageName }}
</div>
</template>
<span class="text-xs">{{ image.imageName }}</span>
</n-popover>
<div
v-if="currentBackgroundImageType === BackgroundImageType.PERSON"
class="absolute right-1 top-1 hidden h-7 w-7 cursor-pointer items-center justify-center rounded-md bg-black/40 p-1 group-hover:flex"
......@@ -180,6 +185,11 @@ function onImageLoaded(index: number) {
>
<CustomIcon icon="mi:delete" class="text-lg text-white" />
</div>
<CustomIcon
v-if="digitalCreationStore.backgroundImageUrl === image.imageUrl"
icon="si-glyph:checked"
class="text-blue absolute left-0 top-0 text-xs"
/>
</div>
</n-spin>
</n-gi>
......
......@@ -4,7 +4,7 @@ import CustomModal from '@/components/custom-modal/custom-modal.vue'
import { ScreenType } from '@/store/types/creation'
import { formatDateTime } from '@/utils/date-formatter'
import { FormInst } from 'naive-ui'
import { computed, reactive, ref } from 'vue'
import { computed, reactive, ref, watch } from 'vue'
interface Props {
isShowModal: boolean
......@@ -51,6 +51,16 @@ const showModal = computed({
},
})
watch(
() => showModal.value,
(newValue) => {
if (newValue) {
digitalHumanVideoFormData.title = '新建數字人視頻' + formatDateTime(new Date())
digitalHumanVideoFormData.pageLayout = ScreenType.PORTRAIT
}
},
)
function handleUpdatePageLayout(pageLayout: ScreenType) {
digitalHumanVideoFormData.pageLayout = pageLayout
}
......
<script setup lang="ts">
import { useDigitalCreationStore } from '@/store/modules/creation'
import { TimbreItem } from '@/store/types/creation'
import { computed, ref } from 'vue'
import { computed } from 'vue'
interface Props {
value?: TimbreItem
......@@ -11,19 +11,15 @@ interface Props {
interface Emits {
(e: 'click', value: TimbreItem): void
(e: 'toggle', value: boolean): void
(e: 'play', value: string): void
}
defineProps<Props>()
const emit = defineEmits<Emits>()
const digitalCreationStore = useDigitalCreationStore()
const digitalAudio = ref<HTMLAudioElement>()
const person = computed(() => digitalCreationStore.person)
function playAudio() {
digitalAudio.value?.play()
}
</script>
<template>
......@@ -39,7 +35,11 @@ function playAudio() {
<div class="flex-1 overflow-hidden">
<div class="mb-2 flex items-center gap-2">
<div class="max-w-32 truncate">{{ value?.name }}</div>
<CustomIcon class="cursor-pointer text-lg" icon="mingcute:volume-line" @click.stop.prevent="playAudio" />
<CustomIcon
class="cursor-pointer text-lg"
icon="mingcute:volume-line"
@click.stop.prevent="emit('play', value!.audioUrl)"
/>
</div>
<div class="flex gap-1">
<n-tag v-for="(style, index) in value?.style" :key="index" type="warning" size="tiny" round>{{ style }}</n-tag>
......@@ -49,6 +49,4 @@ function playAudio() {
<CustomIcon class="cursor-pointer text-lg" icon="ant-design:swap-outlined" @click="emit('toggle', true)" />
</div>
</div>
<audio ref="digitalAudio" :src="value?.audioUrl"></audio>
</template>
......@@ -2,9 +2,8 @@
import { fetchDigitalHumanTimbreList, fetchTimbreByExample } from '@/apis/digital-creation'
import { useAudioSettingStore } from '@/store/modules/audio-setting'
import { useDigitalCreationStore } from '@/store/modules/creation'
import { LangType, TimbreItem, VoiceType } from '@/store/types/creation'
import { computed, onMounted, ref, watch } from 'vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import DigitalAudioCard from './digital-audio-card.vue'
const audioSettingStore = useAudioSettingStore()
......@@ -36,15 +35,17 @@ const digitalTimbreValue = ref<TimbreItem>()
const digitalTimbreList = ref<TimbreItem[]>([])
const digitalTimbreFemaleList = ref<TimbreItem[]>([])
const digitalTimbreMaleList = ref<TimbreItem[]>([])
const showAll = ref(false)
const digitalAudio = ref<HTMLAudioElement>()
const audioSrc = ref('')
const searchName = ref('')
const showAll = ref(false)
const langType = computed({
get() {
return audioSettingStore.langType
return digitalCreationStore.pronunciationLanguage
},
set(value) {
audioSettingStore.setLanType(value)
digitalCreationStore.setPronunciationLanguage(value)
},
})
......@@ -84,6 +85,7 @@ watch(
langType.value = LangType.MANDARIN
} else {
digitalTimbreValue.value = digitalTimbreList.value[0]
langType.value = LangType.CANTONESE
}
}
},
......@@ -92,14 +94,15 @@ watch(
watch(
() => langType.value,
(newVal) => {
digitalCreationStore.setInputAudioUrl('')
if (newVal === LangType.CANTONESE) {
digitalCreationStore.setPerson('')
audioSettingStore.setVoiceType(VoiceType.CANTONESE_FEMALE)
} else {
if (digitalTimbreValue.value?.sex === '男') {
audioSettingStore.setVoiceType(VoiceType.MANDARIN_MALE)
} else {
audioSettingStore.setVoiceType(VoiceType.MANDARIN_FEMALE)
}
digitalCreationStore.setPerson(digitalTimbreValue.value?.timebreId || '')
audioSettingStore.setVoiceType(
digitalTimbreValue.value?.sex === '男' ? VoiceType.MANDARIN_MALE : VoiceType.MANDARIN_FEMALE,
)
}
},
)
......@@ -133,6 +136,14 @@ function handleClickAudioCard(timbreItem: TimbreItem) {
? audioSettingStore.setVoiceType(VoiceType.MANDARIN_MALE)
: audioSettingStore.setVoiceType(VoiceType.MANDARIN_FEMALE)
}
function playAudio(audioUrl: string) {
audioSrc.value && digitalAudio.value?.pause()
audioSrc.value = audioUrl
nextTick(() => {
digitalAudio.value?.play()
})
}
</script>
<template>
......@@ -153,6 +164,7 @@ function handleClickAudioCard(timbreItem: TimbreItem) {
:value="digitalTimbreValue"
show-toggle
@toggle="showAll = true"
@play="playAudio"
/>
<div class="mt-4 text-lg">聲音</div>
......@@ -198,8 +210,11 @@ function handleClickAudioCard(timbreItem: TimbreItem) {
:key="index"
:value="timbre"
@click="handleClickAudioCard"
@play="playAudio"
/>
</div>
</div>
<audio ref="digitalAudio" :src="audioSrc"></audio>
</div>
</template>
......@@ -57,15 +57,15 @@ const digitalImagePositionH = computed({
</n-input-number>
</div>
<div class="mt-4 flex">
<n-input-number v-model:value="digitalImagePositionW" class="flex-1">
<n-input v-model:value="digitalImagePositionW" readonly class="flex-1">
<template #prefix><div class="text-gray w-4 text-center text-xs">W</div></template>
</n-input-number>
</n-input>
<div class="flex w-4 items-center justify-center">
<CustomIcon class="text-gray text-xs" icon="fa6-solid:lock" />
</div>
<n-input-number v-model:value="digitalImagePositionH" class="flex-1">
<n-input v-model:value="digitalImagePositionH" readonly class="flex-1">
<template #prefix><div class="text-gray w-4 text-center text-xs">H</div></template>
</n-input-number>
</n-input>
</div>
</div>
</template>
......@@ -15,6 +15,7 @@ const audioPlaying = ref(false)
const previewContentWidth = ref(0)
const previewContentHeight = ref(0)
const previewContent = ref<HTMLElement>()
const digitalHumanImage = ref<HTMLImageElement>()
const digitalAudio = ref<HTMLAudioElement>()
const audioSetting = ref<AudioSetting>({
codec: 'mp3',
......@@ -31,7 +32,7 @@ const ttsSpeedMarks: { [speed: string]: number } = {
'4': -0.8,
'5': -0.5,
'6': 0,
'7': 1,
'7': 0.8,
}
const resizeObserver = new ResizeObserver((entries) => {
......@@ -42,12 +43,6 @@ const resizeObserver = new ResizeObserver((entries) => {
const isLandscape = computed(() => digitalCreationStore.width > digitalCreationStore.height)
// const digitalHumanWidth = computed(
// () => (digitalCreationStore.w * previewContentWidth.value) / (isLandscape.value ? 1920 : 1080),
// )
const digitalHumanHeight = computed(
() => (digitalCreationStore.h * previewContentHeight.value) / (isLandscape.value ? 1080 : 1920),
)
const digitalHumanLeft = computed(
() => (digitalCreationStore.x * previewContentWidth.value) / (isLandscape.value ? 1920 : 1080),
)
......@@ -128,7 +123,7 @@ function controlAudio() {
audioSetting.value.voiceType !== audioSettingStore.voiceType ||
audioSetting.value.speed !== ttsSpeedMarks[digitalCreationStore.speed] ||
audioSetting.value.volume !== Number(digitalCreationStore.volume) ||
(audioSettingStore.langType === LangType.MANDARIN &&
(digitalCreationStore.pronunciationLanguage === LangType.MANDARIN &&
audioSetting.value.pitch !== Number(digitalCreationStore.pitch))
) {
audioData.value = ''
......@@ -156,12 +151,11 @@ function controlAudio() {
/>
<img
v-if="digitalCreationStore.inputImageUrl"
ref="digitalHumanImage"
:src="digitalCreationStore.inputImageUrl"
class="absolute max-w-none object-cover"
class="absolute h-full max-w-none -translate-x-1/2 object-cover"
:style="{
// width: `${digitalHumanWidth}px`,
height: `${digitalHumanHeight}px`,
left: `${digitalHumanLeft}px`,
left: `calc(50% + ${digitalHumanLeft}px)`,
top: `${digitalHumanTop}px`,
}"
/>
......
......@@ -26,3 +26,10 @@ const text = computed({
/>
</div>
</template>
<style lang="scss" scoped>
:deep(.n-input.n-input--textarea .n-input-word-count) {
right: 0;
bottom: -20px;
}
</style>
......@@ -106,7 +106,7 @@ async function saveDraft(autoSave: boolean = true) {
const res = await saveDraftConfig<DraftConfig>(payload)
if (res.code === 0) {
autoSave ? (autoSaveSuccess.value = true) : window.$message.success('保存成功')
digitalCreationStore.updateDigitalCreation(res.data)
digitalCreationStore.updateDigitalCreation({ id: res.data.id })
const temp: DraftConfig = { ...res.data, modifiedTime: '' }
savedConfig.value = temp
}
......@@ -125,7 +125,7 @@ function confirmExport() {
window.$message.error('請輸入視頻名稱')
return false
}
if (!userCurrency.value) {
if (userCurrency.value < consumption.value) {
window.$message.error('餘額不足')
return false
}
......
......@@ -2,8 +2,9 @@
import { fetchDraftConfigById } from '@/apis/digital-creation'
import { fetchGetTaskConfig } from '@/apis/opus'
import { fetchDigitalHumanTemplateStatus } from '@/apis/template'
import { useAudioSettingStore } from '@/store/modules/audio-setting'
import { useDigitalCreationStore } from '@/store/modules/creation'
import { DraftConfig, ScreenType } from '@/store/types/creation'
import { DraftConfig, LangType, ScreenType, VoiceType } from '@/store/types/creation'
import { DigitalTemplate } from '@/store/types/template'
import { formatDateTime } from '@/utils/date-formatter'
import { onMounted } from 'vue'
......@@ -19,6 +20,7 @@ interface Props {
const props = defineProps<Props>()
const audioSettingStore = useAudioSettingStore()
const digitalCreationStore = useDigitalCreationStore()
onMounted(() => {
......@@ -39,6 +41,7 @@ async function getDigitalTemplate(id: string) {
id: null,
coverUrl: digitalTemplate.coverUrl,
draftName: '新建數字人視頻' + formatDateTime(new Date()),
pronunciationLanguage: digitalTemplate.ttsParams?.person ? LangType.MANDARIN : LangType.CANTONESE,
taskType: digitalTemplate.taskType,
inputImageUrl: digitalTemplate.digitalHumanImageUrl,
driveType: digitalTemplate.driveType,
......@@ -49,8 +52,13 @@ async function getDigitalTemplate(id: string) {
pitch: digitalTemplate.ttsParams?.pitch || digitalCreationStore.pitch,
inputAudioUrl: digitalTemplate.inputAudioUrl,
figureId: digitalTemplate.figureId,
width: digitalTemplate.videoParams?.width || digitalCreationStore.width,
height: digitalTemplate.videoParams?.height || digitalCreationStore.height,
pageLayout: digitalTemplate.videoParams?.pageLayout || digitalCreationStore.pageLayout,
width:
digitalTemplate.videoParams?.width ||
(digitalTemplate.videoParams?.pageLayout === ScreenType.LANDSCAPE ? 1280 : 720),
height:
digitalTemplate.videoParams?.height ||
(digitalTemplate.videoParams?.pageLayout === ScreenType.LANDSCAPE ? 720 : 1280),
cameraId: digitalTemplate.dhParams?.cameraId || null,
x: digitalTemplate.dhParams?.position.x || digitalCreationStore.x,
w:
......@@ -70,9 +78,9 @@ async function getDigitalTemplate(id: string) {
logoUrl: digitalTemplate.logoParams?.logoUrl || null,
bgmUrl: digitalTemplate.bgmParams?.bgmUrl || null,
materialUrl: digitalTemplate.materialUrl,
pronunciationLanguage: digitalCreationStore.pronunciationLanguage,
}
digitalCreationStore.updateDigitalCreation(draftConfig)
audioSettingStore.setVoiceType(draftConfig.person ? VoiceType.MANDARIN_FEMALE : VoiceType.CANTONESE_FEMALE)
}
}
......@@ -80,16 +88,49 @@ async function getDraft(id: string) {
const res = await fetchDraftConfigById<DraftConfig>(id)
if (res.code === 0) {
digitalCreationStore.updateDigitalCreation(res.data)
audioSettingStore.setVoiceType(res.data.person ? VoiceType.MANDARIN_FEMALE : VoiceType.CANTONESE_FEMALE)
}
}
async function getTaskConfig(id: string) {
const res = await fetchGetTaskConfig<DraftConfig>(id)
if (res.code === 0) {
digitalCreationStore.updateDigitalCreation({
...res.data,
const draftConfig: DraftConfig = {
id: null,
draftName: '新建數字人視頻' + formatDateTime(new Date()),
})
pronunciationLanguage: res.data.person ? LangType.MANDARIN : LangType.CANTONESE,
pageLayout: res.data.pageLayout || digitalCreationStore.pageLayout,
width: res.data.width || (res.data.pageLayout === ScreenType.LANDSCAPE ? 1280 : 720),
height: res.data.height || (res.data.pageLayout === ScreenType.LANDSCAPE ? 720 : 1280),
w: res.data.w || (res.data.pageLayout === ScreenType.LANDSCAPE ? 607 : 1080),
h: res.data.h || (res.data.pageLayout === ScreenType.LANDSCAPE ? 1080 : 1920),
coverUrl: res.data.coverUrl || digitalCreationStore.coverUrl,
taskType: res.data.taskType || digitalCreationStore.taskType,
inputImageUrl: res.data.inputImageUrl || digitalCreationStore.inputImageUrl,
driveType: res.data.driveType || digitalCreationStore.driveType,
text: res.data.text || digitalCreationStore.text,
person: res.data.person || digitalCreationStore.person,
speed: res.data.speed || digitalCreationStore.speed,
volume: res.data.volume || digitalCreationStore.volume,
pitch: res.data.pitch || digitalCreationStore.pitch,
inputAudioUrl: res.data.inputAudioUrl || digitalCreationStore.inputAudioUrl,
figureId: res.data.figureId || digitalCreationStore.figureId,
cameraId: res.data.cameraId || digitalCreationStore.cameraId,
x: res.data.x || digitalCreationStore.x,
y: res.data.y || digitalCreationStore.y,
subtitlePolicy: res.data.subtitlePolicy || digitalCreationStore.subtitlePolicy,
enabled: res.data.enabled || digitalCreationStore.enabled,
backgroundImageUrl: res.data.backgroundImageUrl || digitalCreationStore.backgroundImageUrl,
autoAnimoji: res.data.autoAnimoji || digitalCreationStore.autoAnimoji,
enablePalindrome: res.data.enablePalindrome || digitalCreationStore.enablePalindrome,
templateId: res.data.templateId || digitalCreationStore.templateId,
title: res.data.title || digitalCreationStore.title,
logoUrl: res.data.logoUrl || digitalCreationStore.logoUrl,
bgmUrl: res.data.bgmUrl || digitalCreationStore.bgmUrl,
materialUrl: res.data.materialUrl || digitalCreationStore.materialUrl,
}
digitalCreationStore.updateDigitalCreation(draftConfig)
audioSettingStore.setVoiceType(draftConfig.person ? VoiceType.MANDARIN_FEMALE : VoiceType.CANTONESE_FEMALE)
}
}
</script>
......
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import { computed, reactive, ref, watch } from 'vue'
import { FormInst } from 'naive-ui'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
......@@ -50,6 +50,16 @@ const showModal = computed({
},
})
watch(
() => showModal.value,
(newValue) => {
if (newValue) {
digitalHumanDialogueFormData.title = '新建對話' + formatDateTime(new Date())
digitalHumanDialogueFormData.pageLayout = 'vertical'
}
},
)
function handleUpdatePageLayout(pageLayout: 'vertical' | 'horizontal') {
digitalHumanDialogueFormData.pageLayout = pageLayout
}
......
......@@ -21,7 +21,7 @@ const speedMarks: { [speed: number]: string } = {
7: '1.5x',
}
const currentSelectedAudioType = ref('mandarin')
const currentSelectedAudioType = ref('cantonese')
const audioTypeList = [
{ label: '語言:普通話', value: 'mandarin', style: { fontSize: '12px' } },
......
......@@ -31,7 +31,7 @@ const figureId = computed(() => {
:class="figureId === imageItem.figureId ? 'border-blue' : 'border-transparent'"
@click="emit('click', imageItem.figureId as string)"
>
<img :src="imageItem.imageUrl" class="h-full w-full" @load="imageLoading = false" />
<img :src="imageItem.imageUrl" class="h-full w-full object-cover" @load="imageLoading = false" />
<div class="absolute bottom-0 h-5 w-full bg-gradient-to-t from-gray-600 px-1 text-xs leading-5 text-white">
{{ imageItem.imageName }}
</div>
......
......@@ -37,7 +37,9 @@ async function handleGetInfoByFigureId(figureId: string) {
</script>
<template>
<div class="relative h-[calc(100%-190px)] w-[calc(100%-32px)] overflow-hidden rounded-2xl bg-white">
<div
class="relative mx-4 my-[45px] aspect-[16/9] h-full max-w-full overflow-hidden rounded-2xl bg-white xl:my-[60px] 2xl:my-[85px]"
>
<div
v-if="isEmptyPreview"
class="flex h-full w-full select-none items-center justify-center text-xs text-[#84868c]"
......@@ -52,8 +54,11 @@ async function handleGetInfoByFigureId(figureId: string) {
class="absolute left-0 top-0 h-full w-full object-cover"
/>
<div class="absolute bottom-0 right-[5%] w-[320px]">
<img v-show="figureImageUrl" :src="figureImageUrl" alt="數字人" class="h-full w-full" />
</div>
<img
v-show="figureImageUrl"
:src="figureImageUrl"
alt="數字人"
class="absolute bottom-0 right-[10%] aspect-[9/16] h-[80%] object-cover"
/>
</div>
</template>
......@@ -21,7 +21,9 @@ const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const shareLink = computed(() => {
return `${AI_INDEX_URLS[window.ENV || 'DEV']}digital-work-preview/${props.configId}`
const ENV = import.meta.env.VITE_APP_ENV
return `${AI_INDEX_URLS[ENV]}digital-work-preview/${props.configId}`
})
const showModal = computed({
......
......@@ -11,14 +11,6 @@ const figureId = computed(() => {
return digitalHumanDialogueStore.humanInfo.figureId
})
watch(
() => figureId.value,
(newValue) => {
newValue && handleGetInfoByFigureId(newValue)
},
{ immediate: true },
)
const backgroundImageUrl = computed(() => {
return digitalHumanDialogueStore.backgroundInfo.backgroundUrl
})
......@@ -27,6 +19,14 @@ const isEmptyPreview = computed(() => {
return !figureImageUrl.value && !backgroundImageUrl.value
})
watch(
() => figureId.value,
(newValue) => {
newValue && handleGetInfoByFigureId(newValue)
},
{ immediate: true },
)
async function handleGetInfoByFigureId(figureId: string) {
const res = await fetchGetInfoByFigureId<{ imageUrl: string }>(figureId)
......@@ -37,7 +37,7 @@ async function handleGetInfoByFigureId(figureId: string) {
</script>
<template>
<div class="relative h-[667px] w-[375px] overflow-hidden rounded-2xl bg-white">
<div class="relative aspect-[9/16] h-full overflow-hidden rounded-2xl bg-white xl:my-[50px] 2xl:my-[100px]">
<div
v-if="isEmptyPreview"
class="flex h-full w-full select-none items-center justify-center text-xs text-[#84868c]"
......@@ -56,7 +56,7 @@ async function handleGetInfoByFigureId(figureId: string) {
v-show="figureImageUrl"
:src="figureImageUrl"
alt="數字人"
class="absolute bottom-0 left-[25px] h-[574px] w-[324px] object-cover"
class="absolute bottom-0 left-[10%] aspect-[9/16] h-[80%] object-cover"
/>
</div>
</template>
......@@ -34,9 +34,9 @@ watch(
(newValue) => {
isUnSavedDigitalHumanDialogueConfig.value = true
saveDialoguePayload.value = JSON.parse(JSON.stringify(newValue))
saveDialoguePayload.value.systemInfo.chitchatStatus = newValue?.systemInfo.chitchat ? 'Y' : 'N'
saveDialoguePayload.value.systemInfo.perambleStatus = newValue?.systemInfo.preamble ? 'Y' : 'N'
saveDialoguePayload.value.systemInfo.refuseAnswerStatus = newValue?.systemInfo.refuseAnswer ? 'Y' : 'N'
!newValue?.systemInfo.chitchat && (saveDialoguePayload.value.systemInfo.chitchatStatus = 'N')
!newValue?.systemInfo.preamble && (saveDialoguePayload.value.systemInfo.perambleStatus = 'N')
!newValue?.systemInfo.refuseAnswer && (saveDialoguePayload.value.systemInfo.refuseAnswerStatus = 'N')
},
{ deep: true },
)
......@@ -106,7 +106,7 @@ async function handleSaveDigitalHumanDialogueTitle() {
return
}
digitalHumanDialogueConfig.value.baseInfo.title = digitalHumanDialogueTitle.value
saveDialoguePayload.value.baseInfo.title = digitalHumanDialogueTitle.value
const res = await fetchSaveDigitalHumanDialogueConfig<DigitalHumanDialogueConfig>(saveDialoguePayload.value)
......
......@@ -39,9 +39,9 @@ const showModal = computed({
})
watch(
() => props.configTitle,
(newValue) => {
digitalHumanDialogueFormData.title = (newValue + '副本').slice(0, 30)
() => showModal.value,
() => {
digitalHumanDialogueFormData.title = (props.configTitle + '副本').slice(0, 30)
},
)
......
......@@ -36,8 +36,11 @@ export function createDarftTableColumns({
src: row.coverUrl,
alt: '作品圖',
class: 'h-full object-cover',
style: {
width: row.width > row.height ? '64px' : '20px',
},
})
: h('div', { class: 'border w-full h-full text-xs flex items-center justify-center' }, '暫無圖片'),
: h('div', { class: 'w-full h-full text-xs flex items-center justify-center' }, '暫無圖片'),
],
),
h(NEllipsis, {}, { default: () => row.draftName || '--' }),
......
......@@ -39,9 +39,9 @@ const showModal = computed({
})
watch(
() => props.draftName,
(newValue) => {
digitalHumanDraftFormData.draftName = (newValue + '副本').slice(0, 30)
() => showModal.value,
() => {
digitalHumanDraftFormData.draftName = (props.draftName + '副本').slice(0, 30)
},
)
......
......@@ -18,7 +18,7 @@ const taskTypeList = [
{ value: '', label: '全部' },
{
value: 'IMAGE_VIDEO',
label: '照片數人',
label: '照片數人',
},
{
value: 'BASE_VIDEO',
......
......@@ -16,7 +16,7 @@ const exchangeInfoForm = ref({
const exchangeInfoFormRules = shallowReadonly({
redemptionCode: {
required: true,
message: '請填禮包碼',
message: '請填禮包碼',
trigger: 'blur',
},
})
......@@ -51,7 +51,7 @@ function handleExchangeSubmit() {
setTimeout(() => {
window.$dialog.error({
title: '禮包兌換失敗',
content: '請重新兌換或聯客服',
content: '請重新兌換或聯客服',
contentStyle: { fontSize: '14px', marginTop: '20px' },
closable: false,
positiveText: '知道了',
......@@ -100,14 +100,16 @@ function onModalAfterLeave() {
</n-form>
<div class="mb-[20px] mt-[16px]">
<h2>兌換明:</h2>
<h2>兌換明:</h2>
<div class="mt-[6px] pl-[8px] leading-7">
<div>1、每個禮包碼僅限使用一次;</div>
<div>2、禮包內容和適用條件以活動説明爲準;</div>
<div>3、兌換時,請準確無誤地輸入禮包碼,包括字母大小冩、數字位數;</div>
<div>4、禮包碼一旦兌換成功,獎勵將直接髮放至您的賬戶中,不可轉移給其他賬戶;</div>
<div>5、本活動最終解釋權歸萃想智能雲創所有,如有任何爭議,萃想保留決定權;</div>
<div>6、如遇無法正常兌換或其他問題,請尋求客服幫助。客服聯繫方式:蒐索微信公衆號:萃想AI工坊,後颱留言。</div>
<div>2、禮包內容和適用條件以活動說明為准;</div>
<div>3、兌換時,請準確無誤地輸入禮包碼,包括字母大小寫、數位位數;</div>
<div>4、禮包碼一旦兌換成功,獎勵將直接發放至您的帳戶中,不可轉移給其他帳戶;</div>
<div>5、本活動最終解釋權歸萃想智慧雲創所有,如有任何爭議,萃想保留決定權;</div>
<div>
6、如遇無法正常兌換或其他問題,請尋求客服幫助。 客服聯繫方式:蒐索微信公眾號:萃想AI工坊,後臺留言。
</div>
</div>
</div>
......
......@@ -13,7 +13,7 @@ const userStore = useUserStore()
const dropdownOptions = shallowRef([
{
label: '退出登',
label: '退出登',
key: 'logout',
icon: () => h(Logout, { theme: 'outline', size: 12, strokeWidth: 3 }),
},
......
......@@ -18,7 +18,7 @@ const menuOptions = shallowReadonly<MenuOption[]>([
key: 'Videos',
children: [
{
label: '工作',
label: '工作',
key: 'Workbench',
icon: () => h(Workbench, { ...iconConfigFactory() }),
},
......
......@@ -17,7 +17,7 @@ const taskTypeList = [
{ value: '', label: '全部' },
{
value: 'IMAGE_VIDEO',
label: '照片數人',
label: '照片數人',
},
{
value: 'BASE_VIDEO',
......@@ -179,11 +179,13 @@ async function handleGetDigitalVideoWorkList() {
function handleGetTaskListUpdatePageNo(pageNo: number) {
pagingInfo.value.pageNo = pageNo
handleGetDigitalVideoWorkList()
}
function handleGetTaskListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
handleGetDigitalVideoWorkList()
}
</script>
......
......@@ -21,17 +21,17 @@ const userStore = useUserStore()
const digitalCreationStore = useDigitalCreationStore()
const digitalHumanDialogueStore = useDigitalHumanDialogueStore()
const ishowCreateDigitalHumanDialogueModal = ref(false)
const ishowCreateDigitalHumanVideoModal = ref(false)
const isShowCreateDigitalHumanDialogueModal = ref(false)
const isShowCreateDigitalHumanVideoModal = ref(false)
const userInfo = computed(() => userStore.userInfo)
function handleShowCreateDigitalHumanDialogueModal() {
ishowCreateDigitalHumanDialogueModal.value = true
isShowCreateDigitalHumanDialogueModal.value = true
}
function handleShowCreateDigitalHumanVideoModal() {
ishowCreateDigitalHumanVideoModal.value = true
isShowCreateDigitalHumanVideoModal.value = true
}
async function handleCreateDigitalHumanDialogue(digitalHumanDialogueForm: DigitalHumanDialogueForm) {
......@@ -48,7 +48,7 @@ async function handleCreateDigitalHumanDialogue(digitalHumanDialogueForm: Digita
if (res.code === 0) {
router.push({ name: 'DialogueDetail', params: { configId: res.data.baseInfo.configId } })
ishowCreateDigitalHumanDialogueModal.value = false
isShowCreateDigitalHumanDialogueModal.value = false
}
}
......@@ -58,6 +58,7 @@ async function handleCreateDigitalHumanVideo(digitalHumanVideoForm: DigitalHuman
payload.draftName = digitalHumanVideoForm.title
payload.width = digitalHumanVideoForm.pageLayout === ScreenType.PORTRAIT ? 1080 : 1920
payload.height = digitalHumanVideoForm.pageLayout === ScreenType.PORTRAIT ? 1920 : 1080
payload.figureId = digitalHumanVideoForm.pageLayout === ScreenType.PORTRAIT ? 'A2A_V2-gaoming' : 'A2A_V2-3to2_xinyi'
payload.inputImageUrl =
digitalHumanVideoForm.pageLayout === ScreenType.PORTRAIT
? 'https://demand-ai-sit.gz.bcebos.com/data/20240913/1726223058345.png'
......@@ -71,7 +72,7 @@ async function handleCreateDigitalHumanVideo(digitalHumanVideoForm: DigitalHuman
if (res.code === 0) {
router.push({ name: 'Creation', query: { draftId: res.data.id } })
ishowCreateDigitalHumanDialogueModal.value = false
isShowCreateDigitalHumanVideoModal.value = false
}
}
</script>
......@@ -154,21 +155,21 @@ async function handleCreateDigitalHumanVideo(digitalHumanVideoForm: DigitalHuman
<span v-else>-</span>
</div>
<div></div>
<div></div>
</div>
</div>
</div>
</div>
<CreateDigitalHumanDialogueModal
v-model:is-show-modal="ishowCreateDigitalHumanDialogueModal"
v-model:is-show-modal="isShowCreateDigitalHumanDialogueModal"
modal-title="新建對話"
:btn-loading="false"
@confirm="handleCreateDigitalHumanDialogue"
/>
<CreateDigitalHumanVideoModal
v-model:is-show-modal="ishowCreateDigitalHumanVideoModal"
v-model:is-show-modal="isShowCreateDigitalHumanVideoModal"
modal-title="新建視頻"
:btn-loading="false"
@confirm="handleCreateDigitalHumanVideo"
......
<script setup lang="ts">
import { fetchDraftsList } from '@/apis/drafts'
import { ScreenType } from '@/store/types/creation'
import { TaskType } from '@/store/types/template'
import { formatDateTime } from '@/utils/date-formatter'
import { Right } from '@icon-park/vue-next'
......@@ -12,6 +13,9 @@ interface CreationTemplateInfoItem {
coverUrl: string
modifiedTime: string
draftName: string
pageLayout: ScreenType
width: number
height: number
}
const router = useRouter()
......@@ -42,6 +46,9 @@ function getRecentCreationList() {
modifiedTime: item.modifiedTime,
coverUrl: item.coverUrl,
taskType: item.taskType,
pageLayout: item.pageLayout,
width: item.width,
height: item.height,
}))
fetchCreationTemplateInfoListLoading.value = false
......@@ -94,11 +101,17 @@ function handleToCreation(draftId: number) {
class="mr-[10px] inline-block pb-[16px]"
>
<div
class="relative mb-[12px] h-[145px] w-[145px] cursor-pointer overflow-hidden rounded-[12px] bg-[#f3f4fb]"
class="relative mb-[12px] flex h-[145px] w-[145px] cursor-pointer items-center justify-center overflow-hidden rounded-[12px] bg-[#f3f4fb]"
@click="handleToCreation(templateInfoItem.id)"
>
<img
class="z-1 relative h-full w-full object-contain transition-[scale] duration-300 ease-in-out hover:scale-110"
class="z-1 relative object-cover transition-[scale] duration-300 ease-in-out hover:scale-110"
:class="
templateInfoItem.pageLayout === ScreenType.LANDSCAPE ||
templateInfoItem.width > templateInfoItem.height
? 'aspect-[16/9]'
: 'aspect-[9/16] h-full'
"
:src="templateInfoItem.coverUrl"
alt="cover"
/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment