| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448 |
- <template>
- <div class="page-container">
- <el-descriptions title="" :column="1" border>
- <el-descriptions-item label="图片上传">
- <div class="upload-container">
- <el-upload
- ref="uploadRef"
- class="upload-list"
- :action="uploadUrl"
- :http-request="customUpload"
- :headers="uploadHeaders"
- list-type="picture-card"
- :on-preview="handlePreview"
- :on-remove="handleRemove"
- :on-change="handleChange"
- :before-upload="beforeUpload"
- :on-success="handleSuccess"
- :on-error="handleError"
- :on-exceed="handleExceed"
- :limit="30"
- :multiple="true"
- accept="image/jpeg,image/png">
- <el-icon><Plus /></el-icon>
- <template #tip>
- <div class="el-upload__tip">
- 只能上传 <b>jpg/png</b> 文件,且 <b>不超过30张</b>
- </div>
- </template>
- <template #file="{ file }">
- </template>
- </el-upload>
- </div>
- </el-descriptions-item>
- </el-descriptions>
-
- <el-descriptions
- title=""
- column="7"
- border
- class="queue-status-box">
-
- <el-descriptions-item label="总任务数">
- <el-tag type="info">{{ queuestats['总任务数'] }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="待处理">
- <el-tag type="warning">{{ queuestats['待处理'] }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="处理中">
- <el-tag type="primary">{{ queuestats['处理中'] }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="成功">
- <el-tag type="success">{{ queuestats['成功'] }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="失败">
- <el-tag type="danger">{{ queuestats['失败'] }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="操作">
- <el-button type="danger" size="small" @click="stopQueueclick" :loading="isLoading" :disabled="isLoading">全部停止处理</el-button>
- <el-button type="primary" size="small" @click="handleViewQueue" :loading="isLoading">任务列表</el-button>
- </el-descriptions-item>
- </el-descriptions>
-
- <el-dialog
- v-model="queueDialogVisible"
- :before-close="queueDialog"
- title="队列任务详情"
- width="700px"
- destroy-on-close
- >
- <div class="dialog-scroll-wrapper">
- <template v-if="queueData.tasks_preview && queueData.tasks_preview.length">
- <el-card
- v-for="(task, index) in queueData.tasks_preview"
- :key="task.id"
- class="task-card"
- >
- <div class="task-grid">
- <div><strong>任务 ID:</strong>{{ task.id }}</div>
- <div><strong>尝试次数:</strong>{{ task.attempts }}</div>
-
- <div>
- <strong>状态:</strong>
- <el-tag :type="index === 0 ? 'success' : 'warning'">
- {{ index === 0 ? '处理中' : '待处理' }}
- </el-tag>
- </div>
-
- <div class="task-image">
- <el-image
- style="width: 120px; height: 120px; border: 1px solid #ccc"
- :src="formatImageUrl(task.data.sourceDir + '/' + task.data.file_name)"
- fit="cover"
- :preview-src-list="[formatImageUrl(task.data.sourceDir + '/' + task.data.file_name)]"
- />
- </div>
- </div>
- </el-card>
- </template>
-
- <template v-else>
- <div class="empty-queue">当前无任务在队列中。</div>
- </template>
- </div>
- </el-dialog>
-
- <br>
- <el-table :data="tableData" border height="450" v-loading="loading">
- <el-table-column prop="id" label="ID" width="50" />
- <el-table-column prop="old_image_url" label="原图目录" width="360" />
- <el-table-column prop="new_image_url" label="出图目录" width="360" />
- <el-table-column prop="old_img_count" label="图片数量" width="90" />
- <el-table-column prop="new_image_count" label="总数量" width="90" />
- <el-table-column prop="queueLog_model_name" label="任务执行状态" width="110" />
- <el-table-column align="center" label="操作" width="230">
- <template #default="{ row, $index }" >
- <el-button @click="record_deleteRow(row,$index)" type="primary" size="small"
- style="font-size: 16px;padding: 0px;width: 80px;">
- 查看详情
- </el-button>
- <el-button @click="packRow(row,$index)" type="primary" size="small"
- style="font-size: 16px;padding: 0px;width: 100px;">
- 打包压缩zip
- </el-button>
- </template>
- </el-table-column>
- </el-table>
-
-
-
- <el-dialog v-model="zipview" title="请选择打包尺寸" width="500px" top="20%">
- <el-radio-group v-model="zipselectedOption" size="medium">
- <el-radio label="1024x1024">1024 x 1024</el-radio>
- <el-radio label="1024x1303">1024 x 1303</el-radio>
- <el-radio label="图生图">图生图</el-radio>
- <el-radio label="高清放大">高清放大</el-radio>
- </el-radio-group>
-
- <template #footer>
- <div style="text-align: right">
- <el-button @click="zipview = false">取消</el-button>
- <el-button type="primary" @click="confirmPack">确认打包</el-button>
- </div>
- </template>
- </el-dialog>
-
-
- <el-dialog v-model="showDescDialog" title="" width="890px" top="0%">
- <div style="display: flex; gap: 10px; max-height: 70vh; overflow: hidden;">
- <!-- 左侧图片区域,自适应图片尺寸,可滚动查看完整图片 -->
- <div style="flex: 1; display: flex; align-items: start; justify-content: center; border: 1px solid #eee; overflow: auto; max-height: 80vh;">
- <el-image
- :src="formatImageUrl(path_image_url)"
- fit="contain"
- style="max-width: 100%; height: auto;"
- :preview-src-list="[formatImageUrl(path_image_url)]"
- :initial-index="0"
- />
- </div>
-
- <!-- 图像描述弹窗内容区域 -->
- <div style="width: 350px; white-space: pre-wrap; overflow-y: auto; max-height: 63vh; border: 1px solid #f0f0f0; padding: 10px;">
- <div v-html="activeDescription"></div>
- </div>
- </div>
-
- <el-descriptions title="" :column="1" border>
- <el-descriptions-item v-if="img_id" label="ID">
- <span>{{ img_id }}</span>
- </el-descriptions-item>
-
- <el-descriptions-item v-if="img_id" label="模型">
- <span>{{ model }}</span>
- </el-descriptions-item>
-
- <el-descriptions-item label="图片路径" width="50">
- <span>{{ path_image_url }}</span>
- </el-descriptions-item>
- </el-descriptions>
- </el-dialog>
-
- <el-dialog
- v-model="TaskqueueVisible"
- :before-close="TaskqueueDialog"
- title=""
- width="95%"
- top="4vh"
- destroy-on-close
- >
- <el-table
- :data="table_Taskqueue"
- row-key="id"
- border
- style="width: 100%; height: 60vh;"
- v-loading="loading"
- highlight-current-row
- tooltip-effect="dark"
- :row-style="{ height: '40px' }"
- :cell-style="{ padding: '4px' }"
- :header-cell-style="{ padding: '4px' }"
- :header-row-style="{ height: '40px' }"
- @selection-change="SelectionChange"
- :show-overflow-tooltip="true"
- >
- <el-table-column type="selection" width="60" align="center" />
- <el-table-column prop="id" label="任务ID" width="100" align="center" />
- <el-table-column prop="task_id" label="子任务ID" width="100" align="center" />
- <el-table-column prop="log" label="状态" width="160" align="center" />
- </el-table>
- </el-dialog>
-
- <el-dialog
- v-model="mlinventoryVisible"
- :before-close="mlcloseDialog"
- title=""
- width="100%"
- style="height: 97%;margin: 0px;"
- top="2vh"
- destroy-on-close
- >
- <div class="dialog-body">
- <!-- 左侧表格 -->
- <div class="dialog-left">
- <el-form ref="elSearchFormRef" class="demo-form-inline">
- <el-form-item>
- <el-button type="primary" title="" @click="Refresh_Status" :loading="isLoading">刷新执行状态</el-button>
-
- <span>选择状态:</span>
- <el-select
- v-model="filterStatus"
- placeholder="全部"
- @change="handleFilterChange"
- style="width: 120px;"
- >
- <el-option label="全部" value="全部" />
- <el-option label="已出图" value="已出图" />
- <el-option label="未出图" value="未出图" />
- <el-option label="图生文" value="图生文" />
- <el-option label="文生文" value="文生文" />
- <el-option label="文生图" value="文生图" />
- <el-option label="图生图" value="图生图" />
- <el-option label="高清放大" value="高清放大" />
- </el-select>
- </el-form-item>
- </el-form>
-
- <el-table
- :data="tableData_lsit"
- row-key="id"
- border
- style="width: 100%; height: 80vh;"
- v-loading="loading"
- highlight-current-row
- tooltip-effect="dark"
- :row-style="{ height: '0px' }"
- :cell-style="{ padding: '0px' }"
- :header-cell-style="{ padding: '0px' }"
- :header-row-style="{ height: '0px' }"
- @selection-change="SelectionChange"
- :show-overflow-tooltip="true"
- @row-click="handleRowClick"
- >
- <el-table-column type="selection" width="50" align="center" />
- <el-table-column prop="id" label="序号" width="60" align="center" />
-
- <el-table-column label="状态" width="86" align="center">
- <template #default="{ row }">
- <el-tag v-if="row.status === 1" type="success">已出图</el-tag>
- <el-tag v-else type="danger">未出图</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="status_name" label="操作" width="70" align="center" />
- <el-table-column prop="queue_status" label="状态说明" width="140" align="center" />
- <el-table-column prop="same_count" label="出图数量" width="90" align="center" />
- <el-table-column label="原图预览" width="120">
- <template #default="{ row }">
- <img
- :src="formatImageUrl(row.path)"
- style="width: 90px; height: 70px; object-fit: contain; cursor: pointer;"
- @click="showDescription(row, 'old')"
- />
- </template>
- </el-table-column>
-
- <el-table-column label="原图尺寸" width="120" align="center">
- <template #default="{ row }">
- {{ row.width }} x {{ row.height }}
- </template>
- </el-table-column>
- </el-table>
-
- <div class="gva-pagination">
- <el-pagination
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- :current-page="page"
- :page-sizes="[10, 30, 50, 100, 500,1000]"
- :page-size="pageSize"
- layout="total, sizes, prev, pager, next"
- :total="total"
- />
- </div>
- </div>
-
- <!-- 右侧详情 -->
- <div class="dialog-right">
- <div class="dialog-right-scroll">
- <el-card shadow="hover" body-style="padding: 0px;">
- <el-form :model="mlformdata" label-position="top">
- <el-descriptions column="1" border>
-
- <el-descriptions-item label="操作">
- <el-button type="primary" title="选择图片,图生文" @click="imgtotext" :loading="isLoading">图生文</el-button>
- <el-button type="primary" title="选择图片,文生文" @click="texttotxt" :loading="isLoading">文生文</el-button>
- <el-button type="primary" title="选择图片,文生图" @click="texttoimg" :loading="isLoading">文生图</el-button>
- <el-button type="primary" title="选择图片,图生图" @click="imgtoimg" :loading="isLoading">图生图</el-button>
- <el-button type="primary" title="选择图片,图像高清放大处理" @click="singleimg" :loading="isLoading">图像高清放大处理</el-button>
- <el-button type="primary" title="选中图片,执行顺序:图生文->文生文->文生图->图生图" @click="handleSelected" :loading="isLoading" :disabled="isLoading">执选择任务</el-button>
- <el-button type="primary" title="执行当前所有图片,执行顺序:图生文->文生文->文生图->图生图" @click="handleAll" :loading="isLoading">执行全部任务</el-button>
- </el-descriptions-item>
-
- <el-descriptions-item label="文生文模型">
- <el-radio-group v-model="txttotxt_selectedOption">
- <el-radio label="gemini-2.0-flash">gemini-2.0-flash</el-radio>
- <el-radio label="gtp-4">gtp-4</el-radio>
- </el-radio-group>
- </el-descriptions-item>
-
- <el-descriptions-item label="文生图模型">
- <el-radio-group v-model="selectedOption">
- <el-radio label="black-forest-labs/FLUX.1-kontext-pro">flux-pro</el-radio>
- <el-radio label="dall-e-3">dall-e-3</el-radio>
- <el-radio label="gpt-image-1">gpt-image-1</el-radio>
- </el-radio-group>
- </el-descriptions-item>
-
- <!-- <el-descriptions-item label="图生图模型">
- <el-select
- v-model="selectedModel"
- placeholder="请选择模型"
- filterable
- style="width: 100%"
- >
- <el-option
- v-for="(model, index) in modelList"
- :key="index"
- :label="model.title"
- :value="model.model_name"
- />
- </el-select>
- </el-descriptions-item> -->
-
- <el-descriptions-item label="出图预览">
- <div style="display: flex; gap: 20px; align-items: flex-start;">
- <!-- 左侧:图片列表区域 -->
- <div style="flex: 1;">
- <div class="image-preview-wrap" style="height: 140px;">
- <div
- v-for="(img, index) in imageList"
- :key="'fixed-' + index"
- class="image-item"
- @click="showDescription(img, 'new')"
- >
- <img :src="formatImageUrl(img.new_image_url)" />
- 1024x1024
- </div>
- </div>
-
- <div class="image-preview-wrap" style="margin-top: 16px; height: 167px;">
- <div
- v-for="(img, index) in imageList"
- :key="'imgtoimg-' + index"
- class="image-itemsimg"
- @click="showDescription(img, 'san')"
- >
- <img :src="formatImageUrl(img.imgtoimg_url)" />
- 图生图
- </div>
- </div>
-
- <div class="image-preview-wrap" style="margin-top: 16px; height: 140px;">
- <div
- v-for="(img, index) in imageList"
- :key="'custom-' + index"
- class="image-items"
- @click="showDescription(img, 'custom')"
- >
- <img :src="formatImageUrl(img.custom_image_url)" />
- 高清放大
- </div>
- </div>
- </div>
-
- <!-- 右侧:文字描述区域 -->
- <div
- style="
- width: 500px;
- white-space: pre-wrap;
- overflow-y: auto;
- max-height: 50vh;
- border: 1px solid #f0f0f0;
- padding: 10px;
- "
- >
- <div v-html="activeDescription"></div>
- </div>
- </div>
- </el-descriptions-item>
-
-
- <el-descriptions-item label="出图尺寸">
- <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 12px;">
- <el-input v-model="width" placeholder="宽度" size="small" style="width: 80px;" type="number" min="1" />
- <span style="font-weight: bold;">×</span>
- <el-input v-model="height" placeholder="高度" size="small" style="width: 80px;" type="number" min="1" />
- </div>
- </el-descriptions-item>
-
- <!-- <el-descriptions-item label="执行次数">
- <el-input-number v-model="num" :step="1" />
- </el-descriptions-item> -->
-
- <el-descriptions-item label="任务队列">
- <el-table
- :data="table_queue" border
- :show-overflow-tooltip="true"
- v-loading="loading"
- tooltip-effect="dark"
- :row-style="{ height: '30px' }"
- :cell-style="{ padding: '2px' }"
- :header-cell-style="{ padding: '0px' }"
- :header-row-style="{ height: '30px' }"
- style="width: 100%; height: 21vh;"
- >
- <el-table-column prop="id" label="ID" width="50" />
- <el-table-column prop="status" label="状态" width="100" />
- <el-table-column prop="model" label="队列模型" width="170" />
- <el-table-column prop="model_name" label="队列模型" width="90" />
- <el-table-column prop="image_count" label="执行数量" width="90" />
- <el-table-column prop="排队中的数量" label="队列中" width="70" />
- <el-table-column prop="处理中数量" label="处理中" width="70" />
- <el-table-column prop="已完成数量" label="成功" width="70" />
- <el-table-column prop="失败数量" label="失败" width="70" />
- </el-table>
- </el-descriptions-item>
-
- </el-descriptions>
- </el-form>
- </el-card>
- </div>
- </div>
-
-
- </div>
- </el-dialog>
-
- </div>
- </template>
- <script setup>
- import {ref,reactive,toRaw} from 'vue'
- import {ElMessage, ElMessageBox,ElLoading } from 'element-plus'
- import axios from 'axios'
- import {Plus,Delete,CircleCheck,CircleClose} from '@element-plus/icons-vue'
- import {stopQueueProcesses, queueStats, viewQueueStatus,ImgUpload,getPreviewSubDirs,getPreviewimg,getlsit,
- imageToText,
- textToImage,Template_ids,sd_models,
- packImagess,getUploadPath,get_queue_logs,getTaskProgress
- } from '@/api/mes/job'
- //获取服务器地址
- const formatImageUrl = (path) => {
- if (!path) return ''
- const base = 'http://20.0.16.128:9093'
- return `${base}/${path.replace(/^public\//, '')}`
- }
- // --------------------- 图片上传 ---------------------
- const tableData = ref([])
- const getPreviewSubDirslist = async () => {
- const res = await getPreviewSubDirs()
- const processedData = res.data.map(item => {
- return {
- ...item,
- new_image_url: `${item.new_image_url}${item.name}/`
- };
- });
- tableData.value = processedData;
- }
- getPreviewSubDirslist()
- // 环境配置
- const basePath = import.meta.env.VITE_BASE_PATH || 'http://20.0.16.128'
- const uploadsPort = import.meta.env.VITE_UPLOADS_PORT || '9093'
- const uploadUrl = ref(`${basePath}:${uploadsPort}/api/Facility/ImgUpload`)
- const uploadHeaders = {
- 'Content-Type': 'multipart/form-data',
- 'Authorization': 'Bearer ' + localStorage.getItem('token')
- }
- // 文件列表
- const fileList = ref([])
- const uploadRef = ref(null)
- // 获取本地文件URL
- const getObjectUrl = (file) => {
- return URL.createObjectURL(file)
- }
- // 上传前校验
- const beforeUpload = (file) => {
- const isImage = file.type === 'image/jpeg' || file.type === 'image/png'
- const isLt5M = file.size / 1024 / 1024 < 5 // 限制5MB
-
- if (!isImage) {
- ElMessage.error('只能上传 JPG/PNG 格式的图片!')
- return false
- }
-
- if (!isLt5M) {
- ElMessage.error('图片大小不能超过 5MB!')
- return false
- }
-
- // 显示加载中
- const loading = ElLoading.service({
- lock: true,
- text: '正在上传图片...',
- background: 'rgba(0, 0, 0, 0.7)'
- })
-
- // 将loading实例附加到file对象上,以便后续关闭
- file.loadingInstance = loading
-
- return true
- }
- // 自定义上传逻辑
- const customUpload = async (options) => {
- const { file, onProgress, onSuccess, onError } = options
- try {
- const formData = new FormData()
- formData.append('image', file)
- const response = await axios.post(uploadUrl.value, formData, {
- headers: uploadHeaders,
- onUploadProgress: (progressEvent) => {
- const percent = Math.round(
- (progressEvent.loaded * 100) / progressEvent.total
- )
- onProgress({ percent }, file)
- },
- timeout: 60000 // 60秒超时
- })
- } catch (error) {
-
- } finally {
- // 关闭加载中
- if (file.loadingInstance) {
- file.loadingInstance.close()
- }
- }
- }
- // 预览图片
- const handlePreview = (file) => {
- if (file.url) {
- window.open(file.url, '_blank')
- } else if (file.raw) {
- const url = getObjectUrl(file.raw)
- window.open(url, '_blank')
- URL.revokeObjectURL(url) // 释放内存
- }
- }
- // 上传成功
- const handleSuccess = (response, file, fileList) => {
- getPreviewSubDirslist()
- file.loadingInstance?.close()
- }
- // 上传失败
- const handleError = (error, file, fileList) => {
- console.error('上传错误:', error)
- ElMessage.error(`${file.name} 上传失败`)
- file.loadingInstance?.close()
- }
- // 超出限制
- const handleExceed = (files, fileList) => {
- ElMessage.warning(`最多只能上传 30 张图片,您已选择 ${files.length} 张,共 ${files.length + fileList.length} 张`)
- }
- // --------------------- 查看队列任务 ---------------------
- const queuestats = ref({
- 总任务数: 0,
- 待处理: 0,
- 处理中: 0,
- 成功: 0,
- 失败: 0
- })
- //队列状态
- const queueStats_list = async () => {
- const res = await queueStats()
- console.log(res)
- if (res.code === 0) {
- queuestats.value = res.data
- }
- }
- queueStats_list()
- //停止处理
- const stopQueueLoading = ref(false)
- const stopQueueclick = async () => {
- if (stopQueueLoading.value) return
- try {
- await ElMessageBox.confirm(
- '确定要停止所有队列任务吗?',
- '确认操作',
- {
- confirmButtonText: '清空队列',
- cancelButtonText: '取消',
- type: 'warning',
- }
- )
- stopQueueLoading.value = true
- const res = await stopQueueProcesses()
- if (res.code === 0) {
- ElMessage.success(res.msg || '已成功停止队列任务')
- }
- } catch (err) {
- if (err !== 'cancel') {
- ElMessage.error('操作失败,请稍后重试')
- }
- } finally {
- stopQueueLoading.value = false
- }
- }
- //任务列表
- const queueDialogVisible = ref(false)
- // 队列数据
- const queueData = ref({
- tasks_preview: [],
- count: 0
- })
- const QueueStatusisLoading = ref(false)
- // 任务列表按钮
- const handleViewQueue = async () => {
- queueStats_list()
- getPreviewSubDirslist()
- QueueStatusisLoading.value = true
- try {
- const res = await viewQueueStatus()
- if (res.code === 0) {
- queueData.value = res
- queueDialogVisible.value = true
- } else {
- ElMessage.error(res.msg || '查询失败')
- }
- } catch (err) {
- ElMessage.error('请求异常')
- } finally {
- QueueStatusisLoading.value = false
- }
- }
- const queueDialog = async () => {
- queueDialogVisible.value = false
- }
- // --------------------- 查看详情 ---------------------
- const fetchPreviewImages = async () => {
- loading.value = true;
- const params = {
- path: _resrow.value.old_image_url,
- page: page.value,
- limit: pageSize.value
- };
-
- if (filterStatus.value === '已出图') {
- params.status = 1
- } else if (filterStatus.value === '未出图') {
- params.status = 0
- }else if(filterStatus.value === '图生文') {
- params.status_name = "图生文"
- }else if(filterStatus.value === '文生文') {
- params.status_name = "文生文"
- }else if(filterStatus.value === '文生图') {
- params.status_name = "文生图"
- }else if(filterStatus.value === '图生图') {
- params.status_name = "图生图"
- }else if(filterStatus.value === '高清放大') {
- params.status_name = "高清放大"
- }
- // 获取分页数据
- const res = await getPreviewimg(params);
- if (res.code === 0) {
- tableData_lsit.value = res.data;
- total.value = res.total;
- }
- loading.value = false;
- };
- // 分页
- const page = ref(1)
- const total = ref(0)
- const pageSize = ref(50)
- // 页码变化
- const handleCurrentChange = (val) => {
- page.value = val;
- fetchPreviewImages();
- };
- // 每页数量变化
- const handleSizeChange = (val) => {
- pageSize.value = val;
- page.value = 1;
- fetchPreviewImages();
- };
- //---------------选择框----------------
- const filterStatus = ref('全部')
- const handleFilterChange = () => {
- // console.log('当前选择状态:', filterStatus.value);
- page.value = 1;
- fetchPreviewImages();
- };
- const mlinventoryVisible = ref(false)
- const loading = ref(false)
- const width = ref('')
- const height = ref('')
- const mlformdata = ref({})
- const tableData_lsit = ref([])
- const _resrow = ref('')
- const TaskqueueVisible = ref(false)
- const table_queue = ref([])
- const table_Taskqueue = ref([])
- //SD模型
- const selectedModel = ref('Realistic_Vision_V5.0-inpainting.safetensors [cf5d9eb09b]')
- const modelList = ref([])
- //查看详情按钮
- const record_deleteRow = async (row) => {
- //打开弹窗
- mlinventoryVisible.value = true;
-
- //获取当前文件夹行信息
- _resrow.value = row;
-
- //清空信息保持数据最新
- imageList.value = []
- img_id.value = '';
- model.value = '';
- new_image_url.value = '';
- imgtoimg_url.value = '';
- path_image_url.value = '';
- activeDescription.value = '';
-
- // 获取 SD 模型列表
- const sd_models_list = await sd_models();
- modelList.value = sd_models_list.data
-
- // 任务队列
- const queue_logs = await get_queue_logs({ old_image_file: row['old_image_url'] });
- table_queue.value = queue_logs.data;
-
- // 出图尺寸
- const res = await Template_ids();
- console.log(res)
- height.value = res.data.height
- width.value = res.data.width
-
- await fetchPreviewImages();
- }
- const record_queueRow = async (row) => {
- TaskqueueVisible.value = true;
-
- console.log(row['id'])
- const getTaskProgress_data = await getTaskProgress({ id: row['id'] });
- table_Taskqueue.value = getTaskProgress_data.data.data;
- }
- const TaskqueueDialog = async (row) => {
- TaskqueueVisible.value = false;
- }
- const imageList = ref([]);
- const _oldrow = ref('');
- //点击当前行
- const handleRowClick = async (row) => {
- imgtoimg_url.value = ''
- activeDescription.value = ''
- // fetchPreviewImages();
-
- console.log("当前行信息",row)
-
- // 文本内容高亮处理
- const zh = highlightKeywords(row['chinese_description'] || '');
- const en = highlightKeywords(row['english_description'] || '');
- const name = highlightKeywords(row['img_name'] || '');
-
- activeDescription.value = `第一段:\n${zh}\n\n第二段:\n${en}\n\n第三段:\n${name}`;
-
- _oldrow.value = row
- const res = await getlsit({ path: row['path'] });
- if (res.data === null) {
- imageList.value = [];
- } else {
- imageList.value = res.data.map(item => ({
- id: item.id,
- model: item.model,
- chinese_description: item.chinese_description,
- english_description: item.english_description,
- img_name: item.img_name,
- new_image_url: item.new_image_url,
- imgtoimg_url:item.imgtoimg_url,
- custom_image_url: item.custom_image_url,
- size: item.size
- }));
- const queue_logs = await get_queue_logs({ old_image_file: _resrow.value['old_image_url'] });
- table_queue.value = queue_logs.data;
- }
- };
- //关闭按钮
- const mlcloseDialog = () => {
- mlinventoryVisible.value = false
- }
- /**
- * 点击图片查看显示
- */
- const activeDescription = ref('');
- const path_image_url = ref('');
- const img_id = ref('');
- const model = ref('');
- const new_image_url = ref('');
- const imgtoimg_url = ref('');
- const showDescDialog = ref(false);
- // 高亮关键词为红色
- const highlightKeywords = (text) => {
- const keywords = ['几何', 'geometry', 'geometric'];
- keywords.forEach(word => {
- const reg = new RegExp(word, 'gi');
- text = text.replace(reg, match => `<span style="color:red">${match}</span>`);
- });
- return text;
- };
- // 显示图像描述
- const showDescription = async (img, type) => {
- showDescDialog.value = true;
-
- // 任务队列
- Refresh_Status()
-
- // 公共字段初始化
- img_id.value = img.id || '';
- model.value = img.model || '';
- path_image_url.value = '';
- // 文本内容高亮处理
- const zh = highlightKeywords(img.chinese_description || '');
- const en = highlightKeywords(img.english_description || '');
- const name = highlightKeywords(img.img_name || '');
- if (type === 'new') {
-
- path_image_url.value = img.new_image_url || '图片未生成';
- activeDescription.value = `第一段:\n${zh}\n\n第二段:\n${en}\n\n第三段:\n${name}`;
- imgtoimg_url.value = img.imgtoimg_url || '图片未生成';
-
- } else if (type === 'custom') {
-
- path_image_url.value = img.custom_image_url || '图片未生成';
- activeDescription.value = `第一段:\n${zh}\n\n第二段:\n${en}\n\n第三段:\n${name}`;
- imgtoimg_url.value = img.imgtoimg_url || '图片未生成';
-
- }else if (type === 'san') {
-
- path_image_url.value = img.imgtoimg_url || '图片未生成';
- imgtoimg_url.value = img.imgtoimg_url || '图片未生成';
- activeDescription.value = '';
- model.value = '';
-
- }else if (type === 'old') {
-
- path_image_url.value = img.path || '暂无说明';
- imgtoimg_url.value = '';
- activeDescription.value = '';
-
- } else {
- // console.log('无效的类型');
- }
- };
- //复选框
- const _parh = ref('')
- const SelectionChange = (rows) => {
- _parh.value = rows.map(item => item.path);
- };
- //查看详情右侧区域
- const isLoading = ref(false)
- //执行次数默认值
- const num = ref(1)
- //文生文模型下拉(默认值)
- const txttotxt_selectedOption = ref('gemini-2.0-flash')
- //文生图模型下拉(默认值)
- const selectedOption = ref('black-forest-labs/FLUX.1-kontext-pro')
- //刷新表格任务状态
- const Refresh_Status = async () => {
- // 开始 loading
- loading.value = true;
-
- fetchPreviewImages();
- // 任务队列
- const queue_logs = await get_queue_logs({ old_image_file: _resrow.value['old_image_url'] });
- table_queue.value = queue_logs.data;
-
- // 延迟2秒再关闭loading
- await new Promise(resolve => setTimeout(resolve, 3000));
- isLoading.value = false;
- }
- //执行图生文、选择执行选择图片,没有选择执行当前页图片处理
- const imgtotext = () => {
- processImages(toRaw(_parh.value),'图生文');
- };
- //执行文生文、选择执行选择图片,没有选择执行当前页图片处理
- const texttotxt = () => {
- processImages(toRaw(_parh.value),'文生文');
- };
- //执行文生图、选择执行选择图片
- const texttoimg = () => {
- processImages(toRaw(_parh.value),'文生图');
- };
- //执行图生图、选择执行选择图片
- const imgtoimg = () => {
- processImages(toRaw(_parh.value),'图生图');
- };
- const singleimg = () => {
- processImages(toRaw(_parh.value),'高清放大');
- };
- //执行选择任务
- const handleSelected = () => {
- processImages(toRaw(_parh.value),'');
- };
- //执行全部任务
- const allTableData = ref([]); // 全部数据缓存
- const handleAll = async () => {
- try {
- await ElMessageBox.confirm(
- '确定要执行全部任务吗?',
- '确认操作',
- {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }
- );
- // 开始 loading
- loading.value = true;
- // 发起全量请求
- const allParams = {
- path: _resrow.value.old_image_url,
- page: 1,
- limit: 10000
- };
- const allRes = await getPreviewimg(allParams);
- console.log(allRes);
-
- if (allRes.code === 0) {
- // 全部原始数据
- allTableData.value = allRes.data;
-
- // 只保留 status 为 0 且 status_name 为空的项
- const filteredPaths = toRaw(allTableData.value)
- .filter(item => item.status === 0 && !item.status_name)
- .map(item => item.path);
-
- processImages(filteredPaths, '');
- } else {
- ElMessage.error(allRes.msg || '获取全部图片失败');
- }
- } catch (err) {
- // 用户点击取消时,不做任何操作
- if (err !== 'cancel') {
- ElMessage.error('操作取消');
- }
- } finally {
- // 无论成功/失败/异常,最后关闭 loading
- loading.value = false;
- }
- };
- //操作 统一执行操作按钮处理方法
- const processImages = async (files,type) => {
- //判断是否选择图片列表
- if (!Array.isArray(files) || files.length === 0) {
- ElMessage.warning('请选择要处理的图片')
- return
- }
- //生成图片是1024,所有需要限制尺寸不可以超过原始尺寸
- // if (width.value >= 1024 || height.value >= 1024) {
- // ElMessage.warning('尺寸不可超过1024')
- // return
- // }
- //加载开启
- isLoading.value = true
- const failList = []
- const payload = {
- old_image_file:_resrow.value['old_image_url'],
- type:type,
- selectedOption:selectedOption.value,
- txttotxt_selectedOption:txttotxt_selectedOption.value,
- batch:files,
- num:num.value,
- width: width.value,
- height: height.value
- }
-
- //打印调试区域
- // console.log(payload);
- // await new Promise(resolve => setTimeout(resolve, 2000));
- // isLoading.value = false;
- // return;
-
- try {
- const res = await imageToText(payload)
- } catch (e) {
- failList.push(`第 ${batchStart + i + 1} 张 第 ${j + 1} 次`)
- }
- ElMessage.success(`共处理 ${files.length} 张 * ${num.value} 次,共 ${files.length * num.value} 次`)
- if (failList.length > 0) {
- console.warn('失败列表:', failList)
- }
-
- // 延迟2秒再关闭loading
- await new Promise(resolve => setTimeout(resolve, 3000));
- isLoading.value = false;
-
- const queue_logs = await get_queue_logs({ old_image_file: _resrow.value['old_image_url'] });
- table_queue.value = queue_logs.data;
-
- }
- // --------------------- 打包zip ---------------------
- const zipview = ref(false)
- const ziprow = ref('')
- const zipselectedOption = ref('1024x1303') // 默认选中
- //点击打包按钮
- const packRow = async (row) => {
- zipview.value = true;
-
- ziprow.value = row
- }
- //确定按钮
- const confirmPack = async (row) => {
- isLoading.value = true;
- try {
- const res = await getPreviewimg({
- path: ziprow.value.old_image_url,
- page: 1,
- limit: 10000
- });
- if (res.code !== 0 || !res.data || res.data.length === 0) {
- ElMessage.warning('未找到出图记录');
- return;
- }
- const imagePaths = res.data.flatMap(item => {
- const paths = [];
- if (item.custom_image_url?.trim()) {
- paths.push({ type: 'custom_image_url', path: item.custom_image_url });
- }
- if (item.imgtoimg_url?.trim()) {
- paths.push({ type: 'imgtoimg_url', path: item.imgtoimg_url });
- }
- return paths;
- });
- if (imagePaths.length === 0) {
- ElMessage.warning('没有可打包的图片');
- return;
- }
- const packRes = await packImagess({ paths: imagePaths });
- if (packRes.code === 0 && packRes.download_url) {
- ElMessage.success('打包成功,正在下载...');
- // ✅ 替换为服务的真实 IP 和端口
- const basePath = import.meta.env.VITE_BASE_PATH || 'http://20.0.16.128';
- const uploadsPort = import.meta.env.VITE_UPLOADS_PORT || '9093';
- let url = packRes.download_url;
- try {
- const parsedUrl = new URL(url);
- if (parsedUrl.hostname === '127.0.0.1' || parsedUrl.hostname === 'localhost') {
- parsedUrl.hostname = new URL(basePath).hostname;
- parsedUrl.port = uploadsPort;
- url = parsedUrl.toString();
- }
- } catch (e) {
- console.error('URL 解析失败:', e);
- }
- const link = document.createElement('a');
- link.href = url;
- link.setAttribute('download', '');
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- } else {
- ElMessage.error(`打包失败:${packRes.msg || '未知错误'}`);
- }
- } catch (err) {
- ElMessage.error('打包请求异常');
- console.error(err);
- } finally {
- isLoading.value = false;
- }
- };
- </script>
- <style scoped>
- .page-container {
- padding: 20px;
- background: #fff;
- }
- .input-container {
- margin-bottom: 20px;
- }
- .gva-table-box {
- padding-bottom: 20px;
- }
- .table-container {
- margin-top: 20px;
- }
- /* 表格行高细节调整 */
- :deep(.el-table td .cell) {
- line-height: 20px !important;
- }
- :deep(.el-tabs__header) {
- margin-bottom: 0;
- }
- /* 选中高亮 */
- :deep(.el-table__body tr.current-row) > td {
- background: #ff80ff !important;
- }
- /* 新增上传区域样式 */
- .upload-container {
- margin: 20px 0;
- padding: 20px;
- border: 1px dashed #dcdfe6;
- border-radius: 4px;
- background-color: #f5f7fa;
- }
- .upload-list {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
- .upload-item {
- position: relative;
- width: 100px;
- height: 100px;
- border-radius: 4px;
- overflow: hidden;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- }
- .upload-item-thumbnail {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- /* 修改上传卡片容器 */
- :deep(.el-upload--picture-card) {
- --el-upload-picture-card-size: 100px;
- }
- :deep(.el-upload-list--picture-card) {
- --el-upload-list-picture-card-size: 100px;
- }
- .upload-item-actions {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- padding: 8px;
- background: rgba(0, 0, 0, 0.6);
- color: white;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .upload-item-name {
- flex: 1;
- font-size: 12px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin-right: 8px;
- }
- .upload-progress {
- font-size: 12px;
- color: #409eff;
- }
- .upload-item-status {
- margin: 0 8px;
- }
- .success-icon {
- color: #67c23a;
- }
- .error-icon {
- color: #f56c6c;
- }
- .upload-item-delete {
- cursor: pointer;
- color: #f56c6c;
- }
- .upload-item-delete:hover {
- color: #ff0000;
- }
- :deep(.desc-overlay) {
- position: absolute;
- top: 0;
- left: 0;
- width: 120px;
- height: 120px;
- background: rgba(0, 0, 0, 0.75);
- color: #fff;
- font-size: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 8px;
- box-sizing: border-box;
- border-radius: 8px;
- text-align: center;
- word-break: break-word;
- line-height: 1.4;
- z-index: 10;
- }
- :deep(.fade-enter-active,
- .fade-leave-active) {
- transition: opacity 0.3s;
- }
- :deep(.fade-enter-from,
- .fade-leave-to) {
- opacity: 0;
- }
- .dialog-scroll-wrapper {
- max-height: 500px;
- overflow-y: auto;
- padding-right: 10px;
- }
- .task-card {
- margin-bottom: 12px;
- }
- .task-grid {
- display: grid;
- grid-template-columns: 1fr;
- row-gap: 6px;
- font-size: 14px;
- line-height: 1.6;
- }
- .task-prompt {
- word-break: break-word;
- white-space: pre-wrap;
- }
- .task-image {
- margin-top: 8px;
- }
- .image-preview-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.7);
- display: flex;
- justify-content: center;
- align-items: center;
- z-index: 9999;
- }
- .image-preview {
- max-width: 90%;
- max-height: 90%;
- object-fit: contain;
- cursor: pointer;
- }
- .dialog-body {
- height: 93vh;
- display: flex;
- gap: 20px;
- overflow: hidden;
- }
- /* 左侧表格区域 */
- .dialog-left {
- flex: 1.4;
- height: 100%;
- overflow-y: auto;
- }
- /* 右侧详情区域 */
- .dialog-right {
- flex: 2;
- height: 100%;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- }
- .dialog-right-scroll {
- flex: 1;
- overflow-y: auto;
- padding-right: 5px;
- }
- /* 按钮区域自动换行不拥挤 */
- .button-group {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- margin-top: 5px;
- }
- /* 图片容器 */
- .image-preview-wrap {
- display: flex;
- flex-wrap: wrap;
- gap: 16px;
- }
- .image-item {
- position: relative;
- text-align: center;
- width: 110px;
- }
- .image-item img {
- width: 120px;
- height: 120px;
- border: 1px solid #ccc;
- object-fit: contain;
- }
- .image-item div {
- margin-top: 4px;
- font-size: 12px;
- color: #666;
- }
- .image-items {
- position: relative;
- text-align: center;
- width: 107px;
- }
- .image-items img {
- width: 95px;
- height: 120px;
- border: 1px solid #ccc;
- object-fit: contain;
- }
- .image-items div {
- margin-top: 4px;
- font-size: 12px;
- color: #666;
- }
- .image-itemsimg {
- position: relative;
- text-align: center;
- width: 110px;
- }
- .image-itemsimg img {
- width: 118px;
- height: 150px;
- border: 1px solid #ccc;
- object-fit: contain;
- }
- .image-itemsimg div {
- margin-top: 4px;
- font-size: 12px;
- color: #666;
- }
- </style>
|