完成新瀑布流组件整合

This commit is contained in:
RockYang
2025-04-03 16:48:56 +08:00
parent 0746cd49f4
commit c797b35f5a
10 changed files with 1350 additions and 535 deletions

View File

@@ -11,8 +11,18 @@
<el-form-item label="生图模型">
<template #default>
<div class="form-item-inner">
<el-select v-model="selectedModel" style="width: 150px" placeholder="请选择模型" @change="changeModel">
<el-option v-for="v in models" :label="v.name" :value="v" :key="v.value" />
<el-select
v-model="selectedModel"
style="width: 150px"
placeholder="请选择模型"
@change="changeModel"
>
<el-option
v-for="v in models"
:label="v.name"
:value="v"
:key="v.value"
/>
</el-select>
</div>
</template>
@@ -24,7 +34,12 @@
<template #default>
<div class="form-item-inner">
<el-select v-model="params.quality" style="width: 150px">
<el-option v-for="v in qualities" :label="v.name" :value="v.value" :key="v.value" />
<el-option
v-for="v in qualities"
:label="v.name"
:value="v.value"
:key="v.value"
/>
</el-select>
</div>
</template>
@@ -36,7 +51,12 @@
<template #default>
<div class="form-item-inner">
<el-select v-model="params.size" style="width: 150px">
<el-option v-for="v in sizes" :label="v" :value="v" :key="v" />
<el-option
v-for="v in sizes"
:label="v"
:value="v"
:key="v"
/>
</el-select>
</div>
</template>
@@ -48,9 +68,18 @@
<template #default>
<div class="form-item-inner">
<el-select v-model="params.style" style="width: 150px">
<el-option v-for="v in styles" :label="v.name" :value="v.value" :key="v.value" />
<el-option
v-for="v in styles"
:label="v.name"
:value="v.value"
:key="v.value"
/>
</el-select>
<el-tooltip content="生动使模型倾向于生成超真实和戏剧性的图像" raw-content placement="right">
<el-tooltip
content="生动使模型倾向于生成超真实和戏剧性的图像"
raw-content
placement="right"
>
<el-icon class="info-icon">
<InfoFilled />
</el-icon>
@@ -73,8 +102,17 @@
</div>
<el-row class="text-info">
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
<el-button
class="generate-btn"
size="small"
@click="generatePrompt"
color="#5865f2"
:disabled="isGenerating"
>
<i
class="iconfont icon-chuangzuo"
style="margin-right: 5px"
></i>
<span>生成专业绘画指令</span>
</el-button>
</el-row>
@@ -82,7 +120,10 @@
<div class="text-info">
<el-row :gutter="10">
<el-text type="primary"
>每次绘图消耗 <el-text type="warning">{{ dallPower }}算力</el-text></el-text
>每次绘图消耗
<el-text type="warning"
>{{ dallPower }}算力</el-text
></el-text
>
<el-text type="primary"
>当前可用
@@ -93,7 +134,9 @@
</el-form>
</div>
<div class="submit-btn">
<el-button type="primary" :dark="false" round @click="generate"> 立即生成 </el-button>
<el-button type="primary" :dark="false" round @click="generate">
立即生成
</el-button>
</div>
</div>
<div class="task-list-box pl-6 pr-6 pb-4 pt-4 h-dvh">
@@ -103,154 +146,162 @@
<task-list :list="runningJobs" />
<template v-if="finishedJobs.length > 0">
<h2 class="text-xl">创作记录</h2>
<div class="finish-job-list">
<div class="finish-job-list mt-3">
<div v-if="finishedJobs.length > 0">
<!-- <v3-waterfall
id="waterfall"
<Waterfall
:list="finishedJobs"
srcKey="img_thumb"
:gap="20"
:bottomGap="-10"
:colWidth="colWidth"
:distanceToScroll="100"
:isLoading="loading"
:isOver="isOver"
@scrollReachBottom="fetchFinishJobs()"
:row-key="waterfallOptions.rowKey"
:gutter="waterfallOptions.gutter"
:has-around-gutter="waterfallOptions.hasAroundGutter"
:width="waterfallOptions.width"
:breakpoints="waterfallOptions.breakpoints"
:img-selector="waterfallOptions.imgSelector"
:background-color="waterfallOptions.backgroundColor"
:animation-effect="waterfallOptions.animationEffect"
:animation-duration="waterfallOptions.animationDuration"
:animation-delay="waterfallOptions.animationDelay"
:animation-cancel="waterfallOptions.animationCancel"
:lazyload="waterfallOptions.lazyload"
:load-props="waterfallOptions.loadProps"
:cross-origin="waterfallOptions.crossOrigin"
:align="waterfallOptions.align"
:is-loading="loading"
:is-over="isOver"
@afterRender="loading = false"
>
<template #default="slotProp">
<div class="job-item">
<el-image
v-if="slotProp.item.img_url !== ''"
@click="previewImg(slotProp.item)"
:src="slotProp.item['img_thumb']"
fit="cover"
loading="lazy"
>
<template #placeholder>
<div class="image-slot">正在加载图片</div>
</template>
<template #error>
<div class="image-slot">
<el-icon>
<Picture />
</el-icon>
</div>
</template>
</el-image>
<el-image v-else-if="slotProp.item.progress === 101">
<template #error>
<div class="image-slot">
<div class="err-msg-container">
<div class="title">任务失败</div>
<div class="opt">
<el-popover title="错误详情" trigger="click" :width="250" :content="slotProp.item['err_msg']" placement="top">
<template #reference>
<el-button type="info">详情</el-button>
</template>
</el-popover>
<el-button type="danger" @click="removeImage(slotProp.item)">删除</el-button>
<template #default="{ item, url }">
<div
class="bg-gray-900 rounded-lg shadow-md overflow-hidden transition-all duration-300 ease-linear hover:shadow-md hover:shadow-purple-800 group"
>
<div class="overflow-hidden rounded-lg">
<LazyImg
:url="url"
v-if="item.progress === 100"
class="cursor-pointer transition-all duration-300 ease-linear group-hover:scale-105"
@click="previewImg(item)"
/>
<el-image v-else-if="item.progress === 101">
<template #error>
<div class="image-slot">
<div class="err-msg-container">
<div class="title">任务失败</div>
<div class="opt">
<el-popover
title="错误详情"
trigger="click"
:width="250"
:content="item['err_msg']"
placement="top"
>
<template #reference>
<el-button type="info"
>详情</el-button
>
</template>
</el-popover>
<el-button
type="danger"
@click="removeImage(item)"
>删除</el-button
>
</div>
</div>
</div>
</template>
</el-image>
</div>
<div
class="px-4 pt-2 pb-4 border-t border-t-gray-800"
v-if="item.progress === 100"
>
<div
class="pt-3 flex justify-center items-center border-t border-t-gray-600 border-opacity-50"
>
<div class="flex">
<el-tooltip
content="取消分享"
placement="top"
v-if="item.publish"
>
<el-button
type="warning"
@click="publishImage(item, false)"
circle
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
</el-tooltip>
<el-tooltip
content="分享"
placement="top"
v-else
>
<el-button
type="success"
@click="publishImage(item, true)"
circle
>
<i class="iconfont icon-share-bold"></i>
</el-button>
</el-tooltip>
<el-tooltip
content="复制提示词"
placement="top"
>
<el-button
type="info"
circle
class="copy-prompt"
:data-clipboard-text="item.prompt"
>
<i class="iconfont icon-file"></i>
</el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
type="danger"
:icon="Delete"
@click="removeImage(item)"
circle
/>
</el-tooltip>
</div>
</template>
</el-image>
<el-image v-else>
<template #error>
<div class="image-slot">
<i class="iconfont icon-loading"></i>
<span>正在下载图片</span>
</div>
</template>
</el-image>
<div class="remove">
<el-tooltip content="删除" placement="top">
<el-button type="danger" :icon="Delete" @click="removeImage(slotProp.item)" circle />
</el-tooltip>
<el-tooltip content="取消分享" placement="top" v-if="slotProp.item.publish">
<el-button type="warning" @click="publishImage(slotProp.item, false)" circle>
<i class="iconfont icon-cancel-share"></i>
</el-button>
</el-tooltip>
<el-tooltip content="分享" placement="top" v-else>
<el-button type="success" @click="publishImage(slotProp.item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
</el-tooltip>
<el-tooltip content="复制提示词" placement="top">
<el-button type="info" circle class="copy-prompt" :data-clipboard-text="slotProp.item.prompt">
<i class="iconfont icon-file"></i>
</el-button>
</el-tooltip>
</div>
</div>
</div>
</template>
</Waterfall>
<template #footer>
<div class="no-more-data">
<span>没有更多数据了</span>
<div class="flex justify-center py-10">
<img
:src="waterfallOptions.loadProps.loading"
class="max-w-[50px] max-h-[50px]"
v-if="loading"
/>
<div v-else>
<button
class="px-5 py-2 rounded-full bg-purple-700 text-md text-white cursor-pointer hover:bg-purple-800 transition-all duration-300"
@click="fetchFinishJobs"
v-if="!isOver"
>
加载更多
</button>
<div class="no-more-data" v-else>
<span class="text-gray-500 mr-2">没有更多数据了</span>
<i class="iconfont icon-face"></i>
</div>
</template>
</v3-waterfall> -->
<Waterfall
ref="waterfall"
:list="finishedJobs"
:row-key="options.rowKey"
:gutter="options.gutter"
:has-around-gutter="options.hasAroundGutter"
:width="options.width"
:breakpoints="options.breakpoints"
:img-selector="options.imgSelector"
:background-color="options.backgroundColor"
:animation-effect="options.animationEffect"
:animation-duration="options.animationDuration"
:animation-delay="options.animationDelay"
:animation-cancel="options.animationCancel"
:lazyload="options.lazyload"
:load-props="options.loadProps"
:cross-origin="options.crossOrigin"
:align="options.align"
@afterRender="afterRender"
>
<template #default="{ item, url, index }">
<div class="bg-gray-900 rounded-lg shadow-md overflow-hidden transition-all duration-300 ease-linear hover:shadow-lg hover:shadow-gray-600 group" @click="handleClick(item)">
<div class="overflow-hidden">
<LazyImg :url="url" title="title" :alt="item.name" class="cursor-pointer transition-all duration-300 ease-linear group-hover:scale-105" @load="imageLoad" @error="imageError" @success="imageSuccess" />
</div>
<div class="px-4 pt-2 pb-4 border-t border-t-gray-800">
<h2 class="pb-4 text-gray-50 group-hover:text-yellow-300">
{{ item.name }}
</h2>
<div class="pt-3 flex justify-between items-center border-t border-t-gray-600 border-opacity-50">
<div class="text-gray-50">
$ {{ item.price }}
</div>
<div>
<button class="px-3 h-7 rounded-full bg-red-500 text-sm text-white shadow-lg transition-all duration-300 hover:bg-red-600" @click.stop="handleDelete(item, index)">
删除
</button>
</div>
</div>
</div>
</div>
</template>
</Waterfall>
</div>
</div>
</div>
<el-empty :image-size="100" :image="nodata" description="暂无记录" v-else />
<div v-show="!loading" class="flex justify-center py-10 bg-gray-900">
<button class="px-5 py-2 rounded-full bg-gray-700 text-md text-white cursor-pointer hover:bg-gray-800 transition-all duration-300" @click="fetchFinishJobs">
加载更多
</button>
</div>
<el-empty
:image-size="100"
:image="nodata"
description="暂无记录"
v-else
/>
</div>
</template>
<!-- end finish job list-->
</div>
</div>
@@ -285,109 +336,22 @@ import { useSharedStore } from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue";
import { showMessageError, showMessageOK } from "@/utils/dialog";
import BScrollBox from "@/components/ui/BScrollBox.vue";
import { LazyImg, Waterfall } from 'vue-waterfall-plugin-next'
import 'vue-waterfall-plugin-next/dist/style.css'
import error from '@/assets/img/failed.png'
import { LazyImg, Waterfall } from "vue-waterfall-plugin-next";
import "vue-waterfall-plugin-next/dist/style.css";
const listBoxHeight = ref(0);
// const paramBoxHeight = ref(0)
const isLogin = ref(false);
const loading = ref(true);
const colWidth = ref(220);
const isOver = ref(false);
const previewURL = ref("");
const store = useSharedStore();
const models = ref([]);
const waterfallOptions = store.waterfallOptions;
const resizeElement = function () {
listBoxHeight.value = window.innerHeight - 58;
// paramBoxHeight.value = window.innerHeight - 110
};
const options = ref({
// 唯一key值
rowKey: 'id',
// 卡片之间的间隙
gutter: 10,
// 是否有周围的gutter
hasAroundGutter: true,
// 卡片在PC上的宽度
width: 200,
// 自定义行显示个数,主要用于对移动端的适配
breakpoints: {
3840: {
// 4K下
rowPerView: 8,
},
2560: {
// 2K下
rowPerView: 7,
},
1920: {
// 2K下
rowPerView: 6,
},
1600: {
// 2K下
rowPerView: 5,
},
1366: {
// 2K下
rowPerView: 4,
},
800: {
// 当屏幕宽度小于等于800
rowPerView: 3,
},
500: {
// 当屏幕宽度小于等于500
rowPerView: 2,
},
},
// 动画效果
animationEffect: 'animate__fadeInUp',
// 动画时间
animationDuration: 1000,
// 动画延迟
animationDelay: 300,
animationCancel: false,
// 背景色
backgroundColor: '#2C2E3A',
// imgSelector
imgSelector: 'img_thumb',
// 加载配置
loadProps: {
loading,
error,
ratioCalculator: (width, height) => {
console.log("width, height", width, height)
return height / width
},
},
// 是否懒加载
lazyload: true,
align: 'center',
})
function imageLoad(url) {
console.log(`${url}: 加载完成`)
}
function imageError(url) {
console.error(`${url}: 加载失败`)
}
function imageSuccess(url) {
console.log(`${url}: 加载成功`)
}
function afterRender() {
loading.value = false
console.log('计算完成')
}
resizeElement();
window.onresize = () => {
resizeElement();
@@ -397,7 +361,15 @@ const qualities = [
{ name: "高清", value: "hd" },
];
const dalleSizes = ["1024x1024", "1792x1024", "1024x1792"];
const fluxSizes = ["1024x1024", "1024x768", "768x1024", "1280x960", "960x1280", "1366x768", "768x1366"];
const fluxSizes = [
"1024x1024",
"1024x768",
"768x1024",
"1280x960",
"960x1280",
"1366x768",
"768x1366",
];
const sizes = ref(dalleSizes);
const styles = [
{ name: "生动", value: "vivid" },
@@ -487,7 +459,10 @@ const fetchRunningJobs = () => {
httpGet(`/api/dall/jobs?finish=false`)
.then((res) => {
// 如果任务有更新,则更新已完成任务列表
if (res.data.items && res.data.items.length !== runningJobs.value.length) {
if (
res.data.items &&
res.data.items.length !== runningJobs.value.length
) {
page.value = 0;
fetchFinishJobs();
}
@@ -514,25 +489,28 @@ const fetchFinishJobs = () => {
loading.value = true;
page.value = page.value + 1;
httpGet(`/api/dall/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`)
httpGet(
`/api/dall/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`
)
.then((res) => {
if (res.data.items.length < pageSize.value) {
isOver.value = true;
loading.value = false;
}
const imageList = res.data.items;
for (let i = 0; i < imageList.length; i++) {
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
imageList[i]["img_thumb"] =
imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
}
if (page.value === 1) {
finishedJobs.value = imageList;
} else {
finishedJobs.value = finishedJobs.value.concat(imageList);
}
loading.value = false;
})
.catch((e) => {
ElMessage.error("获取任务失败:" + e.message);
loading.value = false;
});
};
@@ -635,6 +613,6 @@ const changeModel = (model) => {
</script>
<style lang="stylus">
@import "@/assets/css/image-dall.styl"
@import "@/assets/css/custom-scroll.styl"
@import '@/assets/css/image-dall.styl';
@import '@/assets/css/custom-scroll.styl';
</style>