AI_Painting_V2.0/src/components/dialogBox/index.vue

336 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Transition name="slide-up">
<div class="input-container" :class="{ generate : props.isGenerate}">
<div v-if="props.isGenerate" class="title">AI绘画2026</div>
<Sender :key="useDisplay.Sender_variant" v-model="prompt" :variant="useDisplay.Sender_variant" :placeholder="promptPlaceholder" :submit-btn-disabled="isgerenate.value" :auto-size="autoSizeConfig">
<template #prefix>
<div v-show="useDisplay.Sender_variant !== 'default'" class="prefix-self-wrap">
<div class="upload-btn">
<img src="@/assets/dialog/originalImage.svg" alt="" style="width: 16px;">
<span>上传原图</span>
</div>
<div class="upload-btn">
<img src="@/assets/dialog/referenceDiagram.svg" alt="" style="width: 16px;">
<span>上传参考图</span>
</div>
<Model v-model="model" />
<Proportion v-model="proportion" />
<Quantity v-model="quantity" />
</div>
</template>
<template #action-list>
<div style="display: flex; align-items: center; gap: 8px; height: 100%;">
<el-button v-if="isgerenate" round color="#626aef" style="animation: spin 1s linear infinite;">
<!-- <i-ep-loading /> -->
</el-button>
<div v-else class="gerenate" :class="{ isprompt: prompt }" @click="generate">
<img v-if="!prompt" src="@/assets/dialog/darkArrow.svg" alt="" />
<img v-else src="@/assets/dialog/writerArrow.svg" alt="" />
<div v-show="useDisplay.Sender_variant !== 'default'">生成</div>
</div>
</div>
</template>
</Sender>
<el-upload
v-show="false"
ref="uploadRef"
class="uploader"
:action="uploadurl"
multiple
:limit="1"
list-type="picture-card"
:before-upload="beforeUpload"
:on-success="SuccessSet"
:on-error="Errors"
:class="{ exceed: imageurl }"
/>
</div>
</Transition>
</template>
<script setup>
import Proportion from './proportion/index.vue'
import Quantity from './quantity/index.vue'
import Model from './model/index.vue'
import { Sender } from 'vue-element-plus-x'
import { useDisplayStore, useParamStore } from '@/stores'
import { generateSubImage } from '@/utils/websocket'
const props = defineProps({
isGenerate: {
type: Boolean,
default: false
}
})
console.log(props.isGenerate)
const useDisplay = useDisplayStore()
const useParams = useParamStore()
const uploadurl = import.meta.env.VITE_API_WORKFLOW_UPLOAD
const uploadRef = ref(null)
const model = ref('flux')
const proportion = ref('9:16')
const quantity = ref(1)
const promptPlaceholder = '结合图片,描述你想生成的画面和动作。例如:电影感剧照,氛围温聲。柔和色调,略带胶片颗粒感,连贯摆出棚拍的动作。'
const prompt = ref('')
const imageurl = ref('')
const imageurlShow = ref('')
const isgerenate = ref(false)
const autoSizeConfig = computed(() => {
if (useDisplay.Sender_variant !== 'default') {
return { minRows: 3, maxRows: 3 }
} else {
return { minRows: 1, maxRows: 1 }
}
})
// 处理图片选择
const handleSelect = async (url) => {
imageurlShow.value = url
// 从URL获取图片Blob对象
const initialFile = await fetch(url)
const blob = await initialFile.blob()
// 创建文件对象
const file = new File([blob], `selected_image_${Date.now()}.jpg`, { type: blob.type })
file.uid = genFileId()
console.log('file', file)
uploadRef.value.clearFiles() // 如果上传数量为1且已存在图片则清除已存在的图
// 获取当前文件列表
uploadRef.value.handleStart(file)
uploadRef.value.submit()
}
// 检查文件类型和大小
const beforeUpload = (rawFile) => {
console.log('beforeUpload', rawFile)
const allowedTypes = ['image/jpeg', 'image/png']
// 检查文件类型
if (!allowedTypes.includes(rawFile.type)) {
// eslint-disable-next-line no-undef
ElMessage.error('不支持的文件类型,请上传 JPG、PNG 格式的图片')
return false
}
// 检查文件大小限制为2MB
if (rawFile.size / 1024 / 1024 > 10) {
// eslint-disable-next-line no-undef
ElMessage.error('图片大小不能超过 10MB')
return false
}
}
// 成功上传后,将文件信息保存到 state 中
const SuccessSet = (response) => {
console.log('上传成功', response)
// eslint-disable-next-line no-undef
ElMessage.success('上传成功')
imageurl.value = response.url
}
// 错误处理
const Errors = (error) => {
console.log('上传失败', error)
// eslint-disable-next-line no-undef
ElMessage.error('上传失败,请重新选择图片')
imageurlShow.value = ''
}
const generate = async () => {
if (!imageurl.value && !prompt.value) {
// eslint-disable-next-line no-undef
ElMessage.error('请输入提示词')
return
}
isgerenate.value = true
useDisplay.isSubGerenate = true
console.log('生成开始', isgerenate.value)
await generateSubImage(3, { videoImg: imageurl.value, text: prompt.value, file_type: 'video', parentCreateTime: parentTime.value, parentIndex: parentIndex.value, parentTaskId: parentTaskId.value }, '生成视频')
console.log('生成中', isgerenate.value)
}
watch(() => useDisplay.isSubGerenate, (newValue) => {
console.log('生成状态', newValue)
if (!newValue) {
console.log('生成完成', isgerenate.value)
isgerenate.value = useDisplay.isSubGerenate
}
})
watch(() => useParams.AIvideoImage, (newValue) => {
if (newValue) {
if (newValue.url === imageurl.value) return
console.log('图片选择成功,打开视频页面', newValue)
parentTime.value = newValue.time
parentIndex.value = newValue.parentIndex
parentTaskId.value = newValue.parentTaskId
imageurl.value = newValue.url
handleSelect(newValue.url)
useParams.AIvideoImage = ''
}
})
</script>
<style lang="less" scoped>
/* 输入区域 */
.input-container {
width: 760px;
position: absolute;
bottom: 10px;
z-index: 100;
left: 50%;
transform: translateX(-50%);
border-radius: 10px;
}
.generate{
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 40px;
position: relative;
border: none;
box-shadow: none;
:deep(.el-sender){
background-color: #F8F9FA;
border: none;
box-shadow: none;
}
}
.prefix-self-wrap{
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
img{
height: 50px;
border-radius: 4px;
}
}
.title{
background-color: #FFF;
color: #333;
text-align: center;
font-family: "Alibaba PuHuiTi";
font-size: 24px;
font-style: normal;
font-weight: 500;
line-height: normal;
}
:deep(.el-sender){
background-color: #ffffff;
border: none;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.10);
}
:deep(.el-sender:focus-within){
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.10);
}
// 时间选择器
.select{
background: #ffffff;
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.10);
}
// 视频效果选择器
.upload-btn{
display: flex;
height: 40px;
padding: 0 15px;
justify-content: center;
align-items: center;
gap: 5px;
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.10);
background: #ffffff;
cursor: pointer;
position: relative;
}
.upload-btn:hover{
background: #E5E7EB;
}
/* 圆形按钮 */
.circle-btn {
position: absolute;
right: 0px;
top: 0px;
width: 18px;
height: 18px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 90;
transition: all 0.3s ease;
color: rgb(0, 0, 0);
font-size: 20px;
&:hover {
transform: scale(1.1);
// box-shadow: 0 6px 16px rgba(98, 106, 239, 0.6);
}
&:active {
transform: scale(0.95);
}
}
/* 过渡动画 */
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.slide-up-enter-from {
opacity: 0;
transform: translate(-50%, 100%);
}
.slide-up-leave-to {
opacity: 0;
transform: translate(-50%, 100%);
}
.gerenate{
display: inline-flex;
height: 40px;
padding: 0 20px;
justify-content: center;
align-items: center;
gap: 5px;
border-radius: 10px;
background: rgba(0, 15, 51, 0.10);
cursor: pointer;
color: #000F33;
text-align: center;
font-family: "Microsoft YaHei";
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.isprompt{
color: #ffffff;
background-color: #000F33;
}
// .gerenate:hover{
// background: rgba(0, 15, 51, 0.20);
// }
</style>