feat: 图生图功能
feat: 参考图片(图生图)选择器 ui: 文件上传(预留) composable ui: 登录过期提示 ui: 调整部分 ui
This commit is contained in:
17
app.vue
17
app.vue
@@ -1,8 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import ModalAuthentication from '~/components/ModalAuthentication.vue';
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
const router = useRouter()
|
||||||
|
const modal = useModal()
|
||||||
const loginState = useLoginState()
|
const loginState = useLoginState()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loginState.checkSession()
|
loginState.checkSession().then(res => {
|
||||||
|
if (!res) {
|
||||||
|
toast.add({
|
||||||
|
title: '登录失效',
|
||||||
|
description: '登录已过期,请重新登录',
|
||||||
|
color: 'red',
|
||||||
|
icon: 'i-tabler-alert-triangle',
|
||||||
|
})
|
||||||
|
modal.open(ModalAuthentication)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
127
components/aigc/ReferenceFigureSelector.vue
Normal file
127
components/aigc/ReferenceFigureSelector.vue
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type {PropType} from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<File | null>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: '选择图片进行图生图',
|
||||||
|
},
|
||||||
|
textOnSelect: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update'])
|
||||||
|
|
||||||
|
const fileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
|
const selected_file = ref<File | null>(null)
|
||||||
|
const image_dataurl = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
watch(() => props.value, async (newVal) => {
|
||||||
|
handleFileInput({target: {files: [newVal!]}})
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleTrashClick = () => {
|
||||||
|
fileInput.value!.value = '';
|
||||||
|
selected_file.value = null
|
||||||
|
image_dataurl.value = ''
|
||||||
|
emit('update', null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileInput = (event: { target: any; }) => {
|
||||||
|
if (event.target.files) {
|
||||||
|
const file = event.target.files[0]
|
||||||
|
if (!file) return
|
||||||
|
selected_file.value = file
|
||||||
|
loading.value = true
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = (e) => {
|
||||||
|
image_dataurl.value = e.target?.result as string
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
reader.onerror = (e) => {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(selected_file.value!)
|
||||||
|
emit('update', selected_file.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full bg-neutral-200/50 dark:bg-neutral-700/50 rounded-md flex justify-between items-center p-1.5 gap-2 relative
|
||||||
|
hover:bg-neutral-200/80 hover:dark:bg-neutral-700/80 transition border dark:border-neutral-700 cursor-pointer"
|
||||||
|
:class="{'cursor-pointer': !loading, 'cursor-not-allowed': loading}"
|
||||||
|
@click="() => !loading && fileInput?.click()">
|
||||||
|
<input ref="fileInput" type="file" class="hidden" @change="handleFileInput" accept="image/*"/>
|
||||||
|
<Transition name="trash-btn" mode="out-in">
|
||||||
|
<button type="button" @click.stop.prevent="handleTrashClick" v-if="!!selected_file"
|
||||||
|
class="absolute -top-1 -right-1 bg-white dark:bg-black rounded-full p-1 shadow-lg border dark:border-neutral-700">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24">
|
||||||
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M4 7h16m-10 4v6m4-6v6M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12M9 7V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</Transition>
|
||||||
|
<div class="w-12 h-12 rounded-md overflow-hidden">
|
||||||
|
<Transition name="preview-swap" mode="out-in">
|
||||||
|
<div v-if="loading"
|
||||||
|
class="w-full h-full flex justify-center items-center rounded-md border-2 border-dashed border-neutral-400 dark:border-neutral-600 text-neutral-400 dark:text-neutral-600">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||||
|
opacity=".25"/>
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z">
|
||||||
|
<animateTransform attributeName="transform" dur="0.75s" repeatCount="indefinite" type="rotate"
|
||||||
|
values="0 12 12;360 12 12"/>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!selected_file"
|
||||||
|
class="w-full h-full flex justify-center items-center rounded-md border-2 border-dashed border-neutral-400 dark:border-neutral-600 text-neutral-400 dark:text-neutral-600">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
|
||||||
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M12 5v14m-7-7h14"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<img v-else class="w-12 h-12 rounded-md object-cover" :src="image_dataurl" :key="selected_file.name"
|
||||||
|
alt="Preview">
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 flex justify-center">
|
||||||
|
<p class="text-neutral-400/80 dark:text-neutral-500 text-sm font-medium select-none text-center">
|
||||||
|
{{ selected_file ? textOnSelect : text }}
|
||||||
|
<span v-if="selected_file && textOnSelect" class="block text-[10px] text-center">
|
||||||
|
{{ selected_file?.name || textOnSelect }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.trash-btn-enter-active,
|
||||||
|
.trash-btn-leave-active {
|
||||||
|
@apply transition duration-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trash-btn-enter-from,
|
||||||
|
.trash-btn-leave-to {
|
||||||
|
@apply opacity-0 scale-75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-swap-enter-active,
|
||||||
|
.preview-swap-leave-active {
|
||||||
|
@apply transition duration-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-swap-enter-from,
|
||||||
|
.preview-swap-leave-to {
|
||||||
|
@apply blur-sm;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -17,7 +17,7 @@ const props = defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-neutral-50 dark:bg-neutral-900 px-1.5 py-1 rounded-lg flex flex-col gap-1 shadow">
|
<div class="bg-neutral-50 dark:bg-neutral-900 px-1.5 py-1 rounded flex flex-col gap-1 shadow">
|
||||||
<div class="flex items-center gap-1 text-sm">
|
<div class="flex items-center gap-1 text-sm">
|
||||||
<UIcon v-if="icon" :name="icon" class="text-base inline-block"/>
|
<UIcon v-if="icon" :name="icon" class="text-base inline-block"/>
|
||||||
<div class="flex-1 flex items-center truncate whitespace-nowrap overflow-hidden">
|
<div class="flex-1 flex items-center truncate whitespace-nowrap overflow-hidden">
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'i-tabler-photo-filled',
|
default: 'i-tabler-photo-filled',
|
||||||
},
|
},
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
prompt: {
|
prompt: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
@@ -26,6 +23,7 @@ const props = defineProps({
|
|||||||
type: Object as PropType<ResultBlockMeta>,
|
type: Object as PropType<ResultBlockMeta>,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits(['use-reference'])
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
@@ -37,7 +35,9 @@ const cachedImagesInterval = ref<NodeJS.Timeout | null>(null)
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
cachedImagesInterval.value = setInterval(async () => {
|
cachedImagesInterval.value = setInterval(async () => {
|
||||||
cachedImages.value = await get(props.fid) as string[] || []
|
const res = await get(props.fid) as string[] || []
|
||||||
|
if (res.length === cachedImages.value.length) return
|
||||||
|
cachedImages.value = res
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -54,6 +54,23 @@ const handle_download = (url: string) => {
|
|||||||
a.click()
|
a.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handle_use_reference = async (blob_url: string) => {
|
||||||
|
fetch(blob_url)
|
||||||
|
.then(res => res.blob())
|
||||||
|
.then(blob => {
|
||||||
|
const file = new File([blob], `xsh_drawing-${props.meta?.datetime! * 1000}.png`, {type: 'image/png'})
|
||||||
|
emit('use-reference', file)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.add({
|
||||||
|
title: '转换失败',
|
||||||
|
description: '无法获取图片数据',
|
||||||
|
color: 'red',
|
||||||
|
icon: 'i-tabler-circle-x',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const copyToClipboard = (text: string) => {
|
const copyToClipboard = (text: string) => {
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
toast.add({
|
toast.add({
|
||||||
@@ -78,7 +95,7 @@ const copyToClipboard = (text: string) => {
|
|||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<UIcon :name="icon"/>
|
<UIcon :name="icon"/>
|
||||||
<h1 class="text-sm font-semibold">
|
<h1 class="text-sm font-semibold">
|
||||||
{{ title }}
|
{{ meta.type || 'AI 智能绘图' }}
|
||||||
</h1>
|
</h1>
|
||||||
<UDivider class="flex-1" size="sm"/>
|
<UDivider class="flex-1" size="sm"/>
|
||||||
<UButton color="black" size="xs" icon="i-tabler-info-circle"
|
<UButton color="black" size="xs" icon="i-tabler-info-circle"
|
||||||
@@ -103,7 +120,8 @@ const copyToClipboard = (text: string) => {
|
|||||||
scale-105 opacity-0 group-hover:scale-100 group-hover:opacity-100 transition">
|
scale-105 opacity-0 group-hover:scale-100 group-hover:opacity-100 transition">
|
||||||
<div class="w-full flex justify-end gap-1 p-1">
|
<div class="w-full flex justify-end gap-1 p-1">
|
||||||
<UTooltip text="以此图为参考创作">
|
<UTooltip text="以此图为参考创作">
|
||||||
<UButton color="indigo" variant="soft" size="2xs" icon="i-tabler-copy" square></UButton>
|
<UButton color="indigo" variant="soft" size="2xs" icon="i-tabler-copy" square
|
||||||
|
@click="handle_use_reference(url)"></UButton>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
<UTooltip text="下载">
|
<UTooltip text="下载">
|
||||||
<UButton color="indigo" variant="soft" size="2xs" icon="i-tabler-download" square
|
<UButton color="indigo" variant="soft" size="2xs" icon="i-tabler-download" square
|
||||||
@@ -124,6 +142,10 @@ const copyToClipboard = (text: string) => {
|
|||||||
<UIcon class="text-sm" name="i-tabler-box-seam"/>
|
<UIcon class="text-sm" name="i-tabler-box-seam"/>
|
||||||
{{ meta.modal }}
|
{{ meta.modal }}
|
||||||
</UBadge>
|
</UBadge>
|
||||||
|
<UBadge v-if="meta.style" color="green" variant="subtle" class="text-[10px] font-bold gap-0.5">
|
||||||
|
<UIcon class="text-sm" name="i-tabler-christmas-tree"/>
|
||||||
|
{{ meta.style }}
|
||||||
|
</UBadge>
|
||||||
<UBadge v-if="meta.cost" color="amber" variant="subtle" class="text-[10px] font-bold gap-0.5">
|
<UBadge v-if="meta.cost" color="amber" variant="subtle" class="text-[10px] font-bold gap-0.5">
|
||||||
<UIcon class="text-sm" name="i-solar-fire-bold"/>
|
<UIcon class="text-sm" name="i-solar-fire-bold"/>
|
||||||
{{ meta.cost }}
|
{{ meta.cost }}
|
||||||
|
|||||||
2
components/aigc/drawing/index.d.ts
vendored
2
components/aigc/drawing/index.d.ts
vendored
@@ -3,5 +3,7 @@ export declare interface ResultBlockMeta {
|
|||||||
cost?: string
|
cost?: string
|
||||||
ratio?: string
|
ratio?: string
|
||||||
id?: string
|
id?: string
|
||||||
|
style?: string
|
||||||
datetime?: number
|
datetime?: number
|
||||||
|
type?: string
|
||||||
}
|
}
|
||||||
38
composables/useFileGo.ts
Normal file
38
composables/useFileGo.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import {useFetchWrapped} from '~/composables/useFetchWrapped';
|
||||||
|
|
||||||
|
const putOSS = (file: File, signed_url: string) => new Promise<void>((resolve, reject) => {
|
||||||
|
let xhr = new XMLHttpRequest()
|
||||||
|
xhr.open('PUT', signed_url as string)
|
||||||
|
xhr.setRequestHeader('Content-Type', '')
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.onerror = reject
|
||||||
|
xhr.send(file)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const useFileGo = (file: File) => new Promise<string>((resolve, reject) => {
|
||||||
|
useFetchWrapped<req.file.Upload & AuthedRequest, string>('App.Tools_Upload.GoOss', {
|
||||||
|
token: useLoginState().token!,
|
||||||
|
user_id: useLoginState().user.id,
|
||||||
|
file_name: file.name,
|
||||||
|
file_type: file.type,
|
||||||
|
file_size: file.size,
|
||||||
|
}).then(async res => {
|
||||||
|
if (res !== '') {
|
||||||
|
try {
|
||||||
|
await putOSS(file, res)
|
||||||
|
let url = res.split('?')[0]
|
||||||
|
resolve(url)
|
||||||
|
} catch (err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -18,6 +18,7 @@ export default defineNuxtConfig({
|
|||||||
families: {
|
families: {
|
||||||
Rubik: '100..900',
|
Rubik: '100..900',
|
||||||
'Noto Sans SC': '100..900',
|
'Noto Sans SC': '100..900',
|
||||||
|
'Barlow Condensed': '100..900'
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {useFetchWrapped} from '~/composables/useFetchWrapped';
|
|||||||
import type {ResultBlockMeta} from '~/components/aigc/drawing';
|
import type {ResultBlockMeta} from '~/components/aigc/drawing';
|
||||||
import {useHistory} from '~/composables/useHistory';
|
import {useHistory} from '~/composables/useHistory';
|
||||||
import {del, set} from 'idb-keyval';
|
import {del, set} from 'idb-keyval';
|
||||||
|
import ReferenceFigureSelector from '~/components/aigc/ReferenceFigureSelector.vue';
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: '绘画 | XSH AI',
|
title: '绘画 | XSH AI',
|
||||||
@@ -52,8 +53,6 @@ const handle_stick_mousedown = (e: MouseEvent, min: number = 240, max: number =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const histories = ref<HistoryItem[]>([])
|
|
||||||
|
|
||||||
const defaultRatios = [
|
const defaultRatios = [
|
||||||
{
|
{
|
||||||
ratio: '1:1',
|
ratio: '1:1',
|
||||||
@@ -177,6 +176,28 @@ const defaultStyles: StyleItem[] = [
|
|||||||
value: 116,
|
value: 116,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
const img2imgStyles: StyleItem[] = [
|
||||||
|
{
|
||||||
|
label: '水彩画',
|
||||||
|
value: 106,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '2.5D',
|
||||||
|
value: 110,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '日系动漫',
|
||||||
|
value: 201,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '美系动漫',
|
||||||
|
value: 202,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '唯美古风',
|
||||||
|
value: 203,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const defaultFormSchema = object({
|
const defaultFormSchema = object({
|
||||||
prompt: string().required('请输入提示词'),
|
prompt: string().required('请输入提示词'),
|
||||||
@@ -186,6 +207,7 @@ const defaultFormSchema = object({
|
|||||||
label: string(),
|
label: string(),
|
||||||
value: number(),
|
value: number(),
|
||||||
}).required('请选择风格'),
|
}).required('请选择风格'),
|
||||||
|
file: string().nullable(),
|
||||||
})
|
})
|
||||||
|
|
||||||
type DefaultFormSchema = InferType<typeof defaultFormSchema>
|
type DefaultFormSchema = InferType<typeof defaultFormSchema>
|
||||||
@@ -195,6 +217,14 @@ const defaultFormState = reactive({
|
|||||||
negative_prompt: '',
|
negative_prompt: '',
|
||||||
resolution: '1024:768',
|
resolution: '1024:768',
|
||||||
styles: defaultStyles.find(item => item.value === 401),
|
styles: defaultStyles.find(item => item.value === 401),
|
||||||
|
file: null,
|
||||||
|
})
|
||||||
|
watch(() => defaultFormState.file, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
defaultFormState.styles = img2imgStyles[0]
|
||||||
|
} else {
|
||||||
|
defaultFormState.styles = defaultStyles.find(item => item.value === 401)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
|
const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
|
||||||
@@ -203,24 +233,27 @@ const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
generating.value = true
|
generating.value = true
|
||||||
|
const styleItem = event.data.styles as StyleItem
|
||||||
|
if (!event.data.file) delete event.data.file
|
||||||
// generate a uuid
|
// generate a uuid
|
||||||
const fid = Math.random().toString(36).substring(2)
|
const fid = Math.random().toString(36).substring(2)
|
||||||
const meta: ResultBlockMeta = {
|
const meta: ResultBlockMeta = {
|
||||||
cost: '1000',
|
cost: '1000',
|
||||||
modal: '混元大模型',
|
modal: '混元大模型',
|
||||||
|
style: styleItem.label,
|
||||||
ratio: event.data.resolution,
|
ratio: event.data.resolution,
|
||||||
datetime: dayjs().unix(),
|
datetime: dayjs().unix(),
|
||||||
|
type: event.data.file ? '智能图生图' : '智能文生图',
|
||||||
}
|
}
|
||||||
history.text2img.unshift({
|
history.text2img.unshift({
|
||||||
fid,
|
fid,
|
||||||
meta,
|
meta,
|
||||||
prompt: event.data.prompt,
|
prompt: event.data.prompt,
|
||||||
})
|
})
|
||||||
const styleItem = event.data.styles as StyleItem
|
|
||||||
useFetchWrapped<
|
useFetchWrapped<
|
||||||
HunYuan.Text2Img.req & AuthedRequest,
|
(HunYuan.Text2Img.req | HunYuan.Img2Img.req) & AuthedRequest,
|
||||||
BaseResponse<HunYuan.Text2Img.resp>
|
BaseResponse<HunYuan.resp>
|
||||||
>('App.Assistant_HunYuan.TenTextToImg', {
|
>(event.data.file ? 'App.Assistant_HunYuan.TenImgToImg' : 'App.Assistant_HunYuan.TenTextToImg', {
|
||||||
token: loginState.token as string,
|
token: loginState.token as string,
|
||||||
user_id: loginState.user.id,
|
user_id: loginState.user.id,
|
||||||
device_id: 'web',
|
device_id: 'web',
|
||||||
@@ -292,9 +325,18 @@ const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
|
|||||||
resize/>
|
resize/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
</OptionBlock>
|
</OptionBlock>
|
||||||
|
<OptionBlock icon="i-tabler-library-photo" label="参考图片">
|
||||||
|
<UFormGroup name="input_image">
|
||||||
|
<ReferenceFigureSelector
|
||||||
|
:value="defaultFormState.file"
|
||||||
|
@update="file => {defaultFormState.file = file; console.log('recv', file)}"
|
||||||
|
text="选择参考图片" text-on-select="已选择参考图"/>
|
||||||
|
</UFormGroup>
|
||||||
|
</OptionBlock>
|
||||||
<OptionBlock icon="i-tabler-photo-hexagon" label="图片风格">
|
<OptionBlock icon="i-tabler-photo-hexagon" label="图片风格">
|
||||||
<UFormGroup name="styles">
|
<UFormGroup name="styles">
|
||||||
<USelectMenu v-model="defaultFormState.styles" :options="defaultStyles"></USelectMenu>
|
<USelectMenu v-model="defaultFormState.styles"
|
||||||
|
:options="defaultFormState.file ? img2imgStyles : defaultStyles"></USelectMenu>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
</OptionBlock>
|
</OptionBlock>
|
||||||
<OptionBlock icon="i-tabler-article-off" label="图片比例">
|
<OptionBlock icon="i-tabler-article-off" label="图片比例">
|
||||||
@@ -310,7 +352,7 @@ const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
|
|||||||
{{ generating ? '生成中' : '生成' }}
|
{{ generating ? '生成中' : '生成' }}
|
||||||
</UButton>
|
</UButton>
|
||||||
<p class="text-xs text-neutral-400 dark:text-neutral-500 font-bold">
|
<p class="text-xs text-neutral-400 dark:text-neutral-500 font-bold">
|
||||||
生成即代表您同意<a href="https://baidu.com" target="_blank"
|
生成即代表您同意<a href="#" target="_blank"
|
||||||
class="underline underline-offset-2">用户许可协议</a>
|
class="underline underline-offset-2">用户许可协议</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -333,9 +375,9 @@ const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
|
|||||||
<Icon name="i-tabler-photo-hexagon" class="text-7xl text-neutral-300 dark:text-neutral-700"/>
|
<Icon name="i-tabler-photo-hexagon" 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>
|
||||||
</div>
|
</div>
|
||||||
<ResultBlock v-else v-for="(result, k) in history.text2img"
|
<ResultBlock v-else v-for="(result, k) in history.text2img" :fid="result.fid" :meta="result.meta"
|
||||||
title="文生图" :fid="result.fid" :meta="result.meta"
|
:prompt="result.prompt" :key="result.fid"
|
||||||
:prompt="result.prompt" :key="result.fid">
|
@use-reference="file => {defaultFormState.file = file}">
|
||||||
<template #header-right>
|
<template #header-right>
|
||||||
<UPopover overlay>
|
<UPopover overlay>
|
||||||
<UButton color="black" size="xs" icon="i-tabler-trash" variant="ghost"></UButton>
|
<UButton color="black" size="xs" icon="i-tabler-trash" variant="ghost"></UButton>
|
||||||
|
|||||||
30
typings/schema.d.ts
vendored
30
typings/schema.d.ts
vendored
@@ -47,6 +47,14 @@ namespace req {
|
|||||||
sms_code: string
|
sms_code: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace file {
|
||||||
|
interface Upload {
|
||||||
|
file_name: string
|
||||||
|
file_type: string
|
||||||
|
file_size: number
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace resp {
|
namespace resp {
|
||||||
@@ -80,6 +88,12 @@ namespace resp {
|
|||||||
namespace HunYuan {
|
namespace HunYuan {
|
||||||
type Resolution = '768:768' | '768:1024' | '1024:768'
|
type Resolution = '768:768' | '768:1024' | '1024:768'
|
||||||
|
|
||||||
|
interface resp {
|
||||||
|
data_id: string | number
|
||||||
|
request_id: string
|
||||||
|
request_image: string
|
||||||
|
}
|
||||||
|
|
||||||
namespace Text2Img {
|
namespace Text2Img {
|
||||||
interface req {
|
interface req {
|
||||||
device_id: string
|
device_id: string
|
||||||
@@ -88,14 +102,16 @@ namespace HunYuan {
|
|||||||
styles: number
|
styles: number
|
||||||
resolution: string
|
resolution: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface resp {
|
|
||||||
data_id: string | number
|
|
||||||
request_id: string
|
|
||||||
request_image: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Img2Text {
|
namespace Img2Img {
|
||||||
|
interface req {
|
||||||
|
device_id: string
|
||||||
|
prompt: string
|
||||||
|
negative_prompt?: string
|
||||||
|
styles: number
|
||||||
|
resolution: string
|
||||||
|
file: File
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user