feat:【AI 大模型】image/index 下的部分页面,使用 unocss

This commit is contained in:
YunaiV
2025-07-27 10:51:04 +08:00
parent 9087ffb1bd
commit fef64d1f4e
3 changed files with 126 additions and 241 deletions

View File

@ -5,139 +5,111 @@
@close="handleDrawerClose"
custom-class="drawer-class"
>
<!-- 图片 -->
<div class="item">
<div class="body">
<el-image
class="image"
:src="detail?.picUrl"
:preview-src-list="[detail.picUrl]"
preview-teleported
/>
</div>
</div>
<!-- 时间 -->
<div class="item">
<div class="tip">时间</div>
<div class="body">
<div>提交时间{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}</div>
<div>生成时间{{ formatTime(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}</div>
</div>
</div>
<!-- 模型 -->
<div class="item">
<div class="tip">模型</div>
<div class="body"> {{ detail.model }}({{ detail.height }}x{{ detail.width }}) </div>
</div>
<!-- 提示词 -->
<div class="item">
<div class="tip">提示词</div>
<div class="body">
{{ detail.prompt }}
</div>
</div>
<!-- 地址 -->
<div class="item">
<div class="tip">图片地址</div>
<div class="body">
{{ detail.picUrl }}
</div>
<!-- 图片预览 -->
<div class="mb-5">
<el-image
:src="detail?.picUrl"
:preview-src-list="[detail.picUrl]"
preview-teleported
class="w-full rounded-2"
fit="contain"
/>
</div>
<!-- 基础信息 -->
<el-descriptions title="基础信息" :column="1" :label-width="100" border>
<el-descriptions-item label="提交时间">
{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
</el-descriptions-item>
<el-descriptions-item label="生成时间">
{{ formatTime(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}
</el-descriptions-item>
<el-descriptions-item label="模型">
{{ detail.model }}({{ detail.height }}x{{ detail.width }})
</el-descriptions-item>
<el-descriptions-item label="提示词">
<div class="break-words">{{ detail.prompt }}</div>
</el-descriptions-item>
<el-descriptions-item label="图片地址">
<div class="break-all text-xs">{{ detail.picUrl }}</div>
</el-descriptions-item>
</el-descriptions>
<!-- StableDiffusion 专属区域 -->
<div
class="item"
v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.sampler"
<el-descriptions
v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && hasStableDiffusionOptions"
title="StableDiffusion 参数"
:column="1"
:label-width="100"
border
class="mt-5"
>
<div class="tip">采样方法</div>
<div class="body">
<el-descriptions-item v-if="detail?.options?.sampler" label="采样方法">
{{
StableDiffusionSamplers.find(
(item: ImageModelVO) => item.key === detail?.options?.sampler
)?.name
}}
</div>
</div>
<div
class="item"
v-if="
detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.clipGuidancePreset
"
>
<div class="tip">CLIP</div>
<div class="body">
</el-descriptions-item>
<el-descriptions-item v-if="detail?.options?.clipGuidancePreset" label="CLIP">
{{
StableDiffusionClipGuidancePresets.find(
(item: ImageModelVO) => item.key === detail?.options?.clipGuidancePreset
)?.name
}}
</div>
</div>
<div
class="item"
v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.stylePreset"
>
<div class="tip">风格</div>
<div class="body">
</el-descriptions-item>
<el-descriptions-item v-if="detail?.options?.stylePreset" label="风格">
{{
StableDiffusionStylePresets.find(
(item: ImageModelVO) => item.key === detail?.options?.stylePreset
)?.name
}}
</div>
</div>
<div
class="item"
v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.steps"
>
<div class="tip">迭代步数</div>
<div class="body">
</el-descriptions-item>
<el-descriptions-item v-if="detail?.options?.steps" label="迭代步数">
{{ detail?.options?.steps }}
</div>
</div>
<div
class="item"
v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.scale"
>
<div class="tip">引导系数</div>
<div class="body">
</el-descriptions-item>
<el-descriptions-item v-if="detail?.options?.scale" label="引导系数">
{{ detail?.options?.scale }}
</div>
</div>
<div
class="item"
v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.seed"
>
<div class="tip">随机因子</div>
<div class="body">
</el-descriptions-item>
<el-descriptions-item v-if="detail?.options?.seed" label="随机因子">
{{ detail?.options?.seed }}
</div>
</div>
</el-descriptions-item>
</el-descriptions>
<!-- Dall3 专属区域 -->
<div class="item" v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style">
<div class="tip">风格选择</div>
<div class="body">
<el-descriptions
v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style"
title="DALL-E 3 参数"
:column="1"
:label-width="100"
border
class="mt-5"
>
<el-descriptions-item label="风格选择">
{{ Dall3StyleList.find((item: ImageModelVO) => item.key === detail?.options?.style)?.name }}
</div>
</div>
</el-descriptions-item>
</el-descriptions>
<!-- Midjourney 专属区域 -->
<div
class="item"
v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.version"
<el-descriptions
v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && hasMidjourneyOptions"
title="Midjourney 参数"
:column="1"
:label-width="100"
border
class="mt-5"
>
<div class="tip">模型版本</div>
<div class="body">
<el-descriptions-item v-if="detail?.options?.version" label="模型版本">
{{ detail?.options?.version }}
</div>
</div>
<div
class="item"
v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.referImageUrl"
>
<div class="tip">参考图</div>
<div class="body">
<el-image :src="detail.options.referImageUrl" />
</div>
</div>
</el-descriptions-item>
<el-descriptions-item v-if="detail?.options?.referImageUrl" label="参考图">
<el-image
:src="detail.options.referImageUrl"
class="max-w-[200px] rounded-2"
fit="contain"
/>
</el-descriptions-item>
</el-descriptions>
</el-drawer>
</template>
@ -156,6 +128,25 @@ import { formatTime } from '@/utils'
const showDrawer = ref<boolean>(false) // 是否显示
const detail = ref<ImageVO>({} as ImageVO) // 图片详细信息
// 计算属性:判断是否有 StableDiffusion 选项
const hasStableDiffusionOptions = computed(() => {
const options = detail.value?.options
return (
options?.sampler ||
options?.clipGuidancePreset ||
options?.stylePreset ||
options?.steps ||
options?.scale ||
options?.seed
)
})
// 计算属性:判断是否有 Midjourney 选项
const hasMidjourneyOptions = computed(() => {
const options = detail.value?.options
return options?.version || options?.referImageUrl
})
const props = defineProps({
show: {
type: Boolean,
@ -175,7 +166,7 @@ const handleDrawerClose = async () => {
/** 监听 drawer 是否打开 */
const { show } = toRefs(props)
watch(show, async (newValue, oldValue) => {
watch(show, async (newValue, _oldValue) => {
showDrawer.value = newValue as boolean
})
@ -186,7 +177,7 @@ const getImageDetail = async (id: number) => {
/** 监听 id 变化,加载最新图片详情 */
const { id } = toRefs(props)
watch(id, async (newVal, oldVal) => {
watch(id, async (newVal, _oldVal) => {
if (newVal) {
await getImageDetail(newVal)
}
@ -194,31 +185,3 @@ watch(id, async (newVal, oldVal) => {
const emits = defineEmits(['handleDrawerClose'])
</script>
<style scoped lang="scss">
.item {
margin-bottom: 20px;
width: 100%;
overflow: hidden;
word-wrap: break-word;
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.tip {
font-weight: bold;
font-size: 16px;
}
.body {
margin-top: 10px;
color: #616161;
.taskImage {
border-radius: 10px;
}
}
}
</style>

View File

@ -1,12 +1,19 @@
<template>
<el-card class="dr-task" body-class="task-card" shadow="never">
<el-card
class="wh-full"
:body-style="{ margin: 0, padding: 0, height: '100%', position: 'relative' }"
shadow="never"
>
<template #header>
绘画任务
<!-- TODO @fan看看怎么优化下这个样子哈 -->
<el-button @click="handleViewPublic">绘画作品</el-button>
</template>
<!-- 图片列表 -->
<div class="task-image-list" ref="imageListRef">
<div
class="relative flex flex-row flex-wrap content-start h-full overflow-auto p-5 pb-[140px] box-border [&>div]:mr-5 [&>div]:mb-5"
ref="imageListRef"
>
<ImageCard
v-for="image in imageList"
:key="image.id"
@ -15,7 +22,9 @@
@on-mj-btn-click="handleImageMidjourneyButtonClick"
/>
</div>
<div class="task-image-pagination">
<div
class="absolute bottom-[60px] h-[50px] leading-[90px] w-full z-[999] bg-white flex flex-row justify-center items-center"
>
<Pagination
:total="pageTotal"
v-model:page="queryParams.pageNo"
@ -150,12 +159,12 @@ const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
}
// 下载
if (type === 'download') {
await download.image({ url: imageDetail.picUrl })
download.image({ url: imageDetail.picUrl })
return
}
// 重新生成
if (type === 'regeneration') {
await emits('onRegeneration', imageDetail)
emits('onRegeneration', imageDetail)
return
}
}
@ -197,49 +206,3 @@ onUnmounted(async () => {
}
})
</script>
<style lang="scss">
.dr-task {
width: 100%;
height: 100%;
}
.task-card {
margin: 0;
padding: 0;
height: 100%;
position: relative;
}
.task-image-list {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-content: flex-start;
height: 100%;
overflow: auto;
padding: 20px 20px 140px;
box-sizing: border-box; /* 确保内边距不会增加高度 */
> div {
margin-right: 20px;
margin-bottom: 20px;
}
> div:last-of-type {
//margin-bottom: 100px;
}
}
.task-image-pagination {
position: absolute;
bottom: 60px;
height: 50px;
line-height: 90px;
width: 100%;
z-index: 999;
background-color: #ffffff;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
</style>

View File

@ -1,11 +1,15 @@
<!-- image -->
<template>
<div class="ai-image">
<div class="left">
<div class="segmented">
<el-segmented v-model="selectPlatform" :options="platformOptions" />
<div class="absolute inset-0 flex flex-row wh-full">
<div class="flex flex-col p-5 w-[390px]">
<div class="mb-[30px]">
<el-segmented
v-model="selectPlatform"
:options="platformOptions"
class="w-[350px] !bg-[#ececec] [--el-border-radius-base:16px] [--el-segmented-item-selected-color:#fff]"
/>
</div>
<div class="modal-switch-container">
<div class="h-full overflow-y-auto">
<Common
v-if="selectPlatform === 'common'"
ref="commonRef"
@ -32,7 +36,7 @@
/>
</div>
</div>
<div class="main">
<div class="flex-1 bg-white">
<ImageList ref="imageListRef" @on-regeneration="handleRegeneration" />
</div>
</div>
@ -79,10 +83,10 @@ const platformOptions = [
const models = ref<ModelVO[]>([]) // 模型列表
/** 绘画 start */
const handleDrawStart = async (platform: string) => {}
const handleDrawStart = async (_platform: string) => {}
/** 绘画 complete */
const handleDrawComplete = async (platform: string) => {
const handleDrawComplete = async (_platform: string) => {
await imageListRef.value.getImageList()
}
@ -108,48 +112,3 @@ onMounted(async () => {
models.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.IMAGE)
})
</script>
<style scoped lang="scss">
.ai-image {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
display: flex;
flex-direction: row;
height: 100%;
width: 100%;
.left {
display: flex;
flex-direction: column;
padding: 20px;
width: 390px;
.segmented .el-segmented {
--el-border-radius-base: 16px;
--el-segmented-item-selected-color: #fff;
background-color: #ececec;
width: 350px;
}
.modal-switch-container {
height: 100%;
overflow-y: auto;
margin-top: 30px;
}
}
.main {
flex: 1;
background-color: #fff;
}
.right {
width: 350px;
background-color: #f7f8fa;
}
}
</style>