ui: drawing design
This commit is contained in:
BIN
assets/example/1.jpg
Normal file
BIN
assets/example/1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 336 KiB |
BIN
assets/example/2.jpg
Normal file
BIN
assets/example/2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/example/3.jpg
Normal file
BIN
assets/example/3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 411 KiB |
38
components/ImagePlaceholder.vue
Normal file
38
components/ImagePlaceholder.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
gradient: {
|
||||||
|
type: String,
|
||||||
|
default: '90deg, #FFC0CB 0%, #FFC0CB 100%'
|
||||||
|
},
|
||||||
|
aspect: {
|
||||||
|
type: String,
|
||||||
|
default: '16/9'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const elem = ref<HTMLElement>()
|
||||||
|
const size = computed(() => {
|
||||||
|
return {
|
||||||
|
width: elem.value?.getBoundingClientRect().width.toFixed(0),
|
||||||
|
height: elem.value?.getBoundingClientRect().height.toFixed(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="elem" class="gradient-background flex justify-center items-center"
|
||||||
|
:style="`aspect-ratio: ${aspect};`">
|
||||||
|
<ClientOnly>
|
||||||
|
<h1 class="text-white/80 drop-shadow-2xl text-sm font-bold">
|
||||||
|
{{ size.width }} x {{ size.height }}
|
||||||
|
</h1>
|
||||||
|
</ClientOnly>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.gradient-background {
|
||||||
|
@apply rounded-lg;
|
||||||
|
@apply bg-gradient-to-r from-indigo-800 to-purple-600;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -56,7 +56,7 @@ const obtainSmsCode = () => {
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
Modals
|
登录眩生花 AI 助手
|
||||||
</h3>
|
</h3>
|
||||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="modal.close()"/>
|
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="modal.close()"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -109,6 +109,20 @@ body {
|
|||||||
@apply bg-neutral-50 dark:bg-neutral-950 bg-fixed;
|
@apply bg-neutral-50 dark:bg-neutral-950 bg-fixed;
|
||||||
@apply bg-[url('~/assets/background-pattern.svg')] dark:bg-[url('~/assets/background-pattern-dark.svg')];
|
@apply bg-[url('~/assets/background-pattern.svg')] dark:bg-[url('~/assets/background-pattern-dark.svg')];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
@apply w-1.5 h-1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
@apply bg-neutral-300/20 dark:bg-neutral-700/20;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
@apply rounded;
|
||||||
|
@apply bg-neutral-300 dark:bg-neutral-700;
|
||||||
|
@apply hover:bg-neutral-400 hover:dark:bg-neutral-600;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -120,11 +134,11 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@apply min-h-[calc(100vh-4rem)] pt-16;
|
@apply h-screen pt-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
@apply h-16 bg-white border-t;
|
@apply h-16 bg-white border-t z-30;
|
||||||
@apply dark:bg-neutral-900 dark:border-neutral-800;
|
@apply dark:bg-neutral-900 dark:border-neutral-800;
|
||||||
@apply flex flex-row items-center justify-between px-4;
|
@apply flex flex-row items-center justify-between px-4;
|
||||||
}
|
}
|
||||||
|
|||||||
97
pages/aigc/drawing/components/ResultBlock.vue
Normal file
97
pages/aigc/drawing/components/ResultBlock.vue
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type {ResultBlockMeta} from "~/pages/aigc/drawing/components/index";
|
||||||
|
import type {PropType} from "vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: 'i-tabler-photo-filled'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
type: Array,
|
||||||
|
default: (): Array<string> => []
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
type: Object as PropType<ResultBlockMeta>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const expand_prompt = ref(false)
|
||||||
|
const show_meta = ref(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="">
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UIcon :name="icon"/>
|
||||||
|
<h1 class="text-sm font-semibold">
|
||||||
|
{{ title }}
|
||||||
|
</h1>
|
||||||
|
<UDivider class="flex-1" size="sm"/>
|
||||||
|
<UButton color="black" size="xs" icon="i-tabler-info-circle"
|
||||||
|
:variant="show_meta ? 'solid' : 'ghost'" :disabled="!meta"
|
||||||
|
@click="show_meta = !show_meta"></UButton>
|
||||||
|
<slot name="header-right"/>
|
||||||
|
</div>
|
||||||
|
<div v-if="prompt" class="flex items-start gap-2 mt-1 mb-2">
|
||||||
|
<UIcon name="i-tabler-article" class="mt-0.5"/>
|
||||||
|
<p class="text-sm flex-1 text-ellipsis cursor-pointer"
|
||||||
|
:class="{'line-clamp-1': !expand_prompt, 'line-clamp-none': expand_prompt}"
|
||||||
|
@click="expand_prompt = !expand_prompt">
|
||||||
|
{{ prompt }}
|
||||||
|
</p>
|
||||||
|
<UButton color="gray" size="xs" icon="i-tabler-copy" variant="ghost" class="-mt-1"></UButton>
|
||||||
|
</div>
|
||||||
|
<div v-if="images.length > 0" class="flex items-center overflow-x-auto h-64 gap-2 pb-2 snap-x">
|
||||||
|
<img v-for="(url, i) in images" class="result-image" :src="url" alt="" :key="i">
|
||||||
|
</div>
|
||||||
|
<Transition v-if="meta" name="meta">
|
||||||
|
<div v-if="show_meta" class="w-full flex items-center gap-2 flex-wrap whitespace-nowrap pb-2 mt-2">
|
||||||
|
<UBadge v-if="meta.modal" color="black" variant="solid" class="text-[10px] font-bold gap-0.5">
|
||||||
|
<UIcon class="text-sm" name="i-tabler-box-seam"/>
|
||||||
|
{{ meta.modal }}
|
||||||
|
</UBadge>
|
||||||
|
<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"/>
|
||||||
|
{{ meta.cost }}
|
||||||
|
</UBadge>
|
||||||
|
<UBadge v-if="meta.ratio" color="indigo" variant="subtle" class="text-[10px] font-bold gap-0.5">
|
||||||
|
<UIcon class="text-sm" name="i-tabler-aspect-ratio"/>
|
||||||
|
{{ meta.ratio }}
|
||||||
|
</UBadge>
|
||||||
|
<UBadge v-if="meta.id" color="indigo" variant="subtle" class="text-[10px] font-bold gap-0.5">
|
||||||
|
<UIcon class="text-sm" name="i-tabler-number"/>
|
||||||
|
{{ meta.id }}
|
||||||
|
</UBadge>
|
||||||
|
<UBadge v-if="meta.datetime" color="indigo" variant="subtle" class="text-[10px] font-bold gap-0.5">
|
||||||
|
<UIcon class="text-sm" name="i-tabler-calendar-month"/>
|
||||||
|
{{ dayjs(meta.datetime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
|
</UBadge>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.meta-enter-active,
|
||||||
|
.meta-leave-active {
|
||||||
|
@apply transition duration-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-enter-from,
|
||||||
|
.meta-leave-to {
|
||||||
|
@apply opacity-0 -translate-y-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-image {
|
||||||
|
@apply snap-start;
|
||||||
|
@apply h-full aspect-auto object-cover rounded-lg shadow-md;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
7
pages/aigc/drawing/components/index.d.ts
vendored
Normal file
7
pages/aigc/drawing/components/index.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export declare interface ResultBlockMeta {
|
||||||
|
modal?: string
|
||||||
|
cost?: string
|
||||||
|
ratio?: string
|
||||||
|
id?: string
|
||||||
|
datetime?: number
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import image1 from '~/assets/example/1.jpg';
|
||||||
|
import image2 from '~/assets/example/2.jpg';
|
||||||
|
import image3 from '~/assets/example/3.jpg';
|
||||||
import OptionBlock from "~/pages/aigc/drawing/components/OptionBlock.vue";
|
import OptionBlock from "~/pages/aigc/drawing/components/OptionBlock.vue";
|
||||||
|
import ResultBlock from "~/pages/aigc/drawing/components/ResultBlock.vue";
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: '绘画 | XSH AI'
|
title: '绘画 | XSH AI'
|
||||||
@@ -32,12 +36,25 @@ const handle_mousedown = (e: MouseEvent, min: number = 240, max: number = 400) =
|
|||||||
window.addEventListener('mouseup', handle_mouseup)
|
window.addEventListener('mouseup', handle_mouseup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const images = [
|
||||||
|
image1,
|
||||||
|
image2,
|
||||||
|
image3,
|
||||||
|
'https://w.wallhaven.cc/full/jx/wallhaven-jxl31y.png',
|
||||||
|
'https://w.wallhaven.cc/full/6d/wallhaven-6d7xmx.jpg',
|
||||||
|
]
|
||||||
|
const images2 = [
|
||||||
|
image1,
|
||||||
|
image2,
|
||||||
|
image3,
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full flex">
|
<div class="w-full flex">
|
||||||
<div ref="leftSection"
|
<div ref="leftSection"
|
||||||
class="relative h-[calc(100vh-4rem)] overflow-hidden bg-neutral-200 dark:bg-neutral-800 transition-all"
|
class="hidden md:block relative h-[calc(100vh-4rem)] overflow-hidden bg-neutral-200 dark:bg-neutral-800 transition-all"
|
||||||
style="width: 320px">
|
style="width: 320px">
|
||||||
<div ref="leftHandler"
|
<div ref="leftHandler"
|
||||||
class="absolute inset-0 left-auto hidden xl:flex flex-col justify-center items-center cursor-ew-resize px-1 group"
|
class="absolute inset-0 left-auto hidden xl:flex flex-col justify-center items-center cursor-ew-resize px-1 group"
|
||||||
@@ -60,8 +77,28 @@ const handle_mousedown = (e: MouseEvent, min: number = 240, max: number = 400) =
|
|||||||
</OptionBlock>
|
</OptionBlock>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1 h-screen flex flex-col gap-4 bg-neutral-100 dark:bg-neutral-900 p-4 overflow-y-auto">
|
||||||
results
|
<ResultBlock :images="images"
|
||||||
|
title="XX大模型 · 文生图" :meta="{
|
||||||
|
id: 'd166429411dfc6722e54c032cdba97a2',
|
||||||
|
aspect: '9:16',
|
||||||
|
cost: '1500',
|
||||||
|
modal: '混元大模型',
|
||||||
|
ratio: '16:9',
|
||||||
|
datetime: 1709106270
|
||||||
|
}"
|
||||||
|
prompt="这是, 一组, 测试用的, 提示词, 很长, 很长很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长, 很长">
|
||||||
|
<template #header-right>
|
||||||
|
<UButton color="gray" size="xs" icon="i-tabler-trash" variant="ghost"></UButton>
|
||||||
|
</template>
|
||||||
|
</ResultBlock>
|
||||||
|
<ResultBlock :images="images2"
|
||||||
|
title="XX大模型 · 图生图"
|
||||||
|
prompt="这是, 一组, 测试用的, 提示词">
|
||||||
|
<template #header-right>
|
||||||
|
<UButton color="gray" size="xs" icon="i-tabler-trash" variant="ghost"></UButton>
|
||||||
|
</template>
|
||||||
|
</ResultBlock>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user