From ddddbfbb7cefd4c29317c89f86f5c0685ba1a3be Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Mon, 25 Aug 2025 19:18:40 +0800 Subject: [PATCH] 'commit' --- dsLightRag/Start.py | 7 +- dsLightRag/static/Midjourney/mj.html | 670 ++------------------------- dsLightRag/static/Midjourney/mj.js | 506 ++++++++++++++++++++ 3 files changed, 549 insertions(+), 634 deletions(-) create mode 100644 dsLightRag/static/Midjourney/mj.js diff --git a/dsLightRag/Start.py b/dsLightRag/Start.py index 6307577f..500b2cf0 100644 --- a/dsLightRag/Start.py +++ b/dsLightRag/Start.py @@ -36,8 +36,8 @@ logger.addHandler(handler) @asynccontextmanager async def lifespan(_: FastAPI): - pool = await init_postgres_pool() - app.state.pool = pool + #pool = await init_postgres_pool() + #app.state.pool = pool asyncio.create_task(train_document_task()) @@ -45,7 +45,8 @@ async def lifespan(_: FastAPI): yield finally: # 应用关闭时销毁连接池 - await close_postgres_pool(pool) + #await close_postgres_pool(pool) + pass app = FastAPI(lifespan=lifespan) diff --git a/dsLightRag/static/Midjourney/mj.html b/dsLightRag/static/Midjourney/mj.html index c18b4e6d..522fa1e5 100644 --- a/dsLightRag/static/Midjourney/mj.html +++ b/dsLightRag/static/Midjourney/mj.html @@ -8,7 +8,7 @@ - + + + - +
@@ -85,9 +71,7 @@
- -
-
-

历史记录

- -
-
- -
- -

暂无历史记录

-

开始生成图像以保存到历史记录

-
-
-
- - + @@ -429,571 +402,6 @@
- \ No newline at end of file diff --git a/dsLightRag/static/Midjourney/mj.js b/dsLightRag/static/Midjourney/mj.js new file mode 100644 index 00000000..22f38a3e --- /dev/null +++ b/dsLightRag/static/Midjourney/mj.js @@ -0,0 +1,506 @@ +// DOM加载完成后执行 +document.addEventListener('DOMContentLoaded', function () { + // 主题切换功能 + const themeToggle = document.getElementById('themeToggle'); + themeToggle.addEventListener('click', function () { + document.documentElement.classList.toggle('dark'); + localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light'); + }); + + // 检查本地存储的主题偏好 + if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { + document.documentElement.classList.add('dark'); + } + + // 移动端菜单切换 + const menuToggle = document.getElementById('menuToggle'); + const mobileMenu = document.getElementById('mobileMenu'); + menuToggle.addEventListener('click', function () { + mobileMenu.classList.toggle('hidden'); + }); + + // 选项卡切换 + const textToImageTab = document.getElementById('textToImageTab'); + const imageToImageTab = document.getElementById('imageToImageTab'); + const textToImagePanel = document.getElementById('textToImagePanel'); + const imageToImagePanel = document.getElementById('imageToImagePanel'); + + textToImageTab.addEventListener('click', function () { + textToImageTab.classList.add('text-primary', 'border-primary'); + textToImageTab.classList.remove('text-gray-500', 'border-transparent'); + imageToImageTab.classList.remove('text-primary', 'border-primary'); + imageToImageTab.classList.add('text-gray-500', 'border-transparent'); + textToImagePanel.classList.remove('hidden'); + imageToImagePanel.classList.add('hidden'); + }); + + imageToImageTab.addEventListener('click', function () { + imageToImageTab.classList.add('text-primary', 'border-primary'); + imageToImageTab.classList.remove('text-gray-500', 'border-transparent'); + textToImageTab.classList.remove('text-primary', 'border-primary'); + textToImageTab.classList.add('text-gray-500', 'border-transparent'); + imageToImagePanel.classList.remove('hidden'); + textToImagePanel.classList.add('hidden'); + }); + + // 图生图相关功能 + const browseImage = document.getElementById('browseImage'); + const imageUpload = document.getElementById('imageUpload'); + const imageUploadArea = document.getElementById('imageUploadArea'); + const imagePreview = document.getElementById('imagePreview'); + const previewImage = document.getElementById('previewImage'); + const removeImage = document.getElementById('removeImage'); + const imageStrength = document.getElementById('imageStrength'); + const strengthValue = document.getElementById('strengthValue'); + + browseImage.addEventListener('click', function () { + imageUpload.click(); + }); + + imageUploadArea.addEventListener('dragover', function (e) { + e.preventDefault(); + imageUploadArea.classList.add('border-primary'); + }); + + imageUploadArea.addEventListener('dragleave', function () { + imageUploadArea.classList.remove('border-primary'); + }); + + imageUploadArea.addEventListener('drop', function (e) { + e.preventDefault(); + imageUploadArea.classList.remove('border-primary'); + if (e.dataTransfer.files.length) { + imageUpload.files = e.dataTransfer.files; + handleImageUpload(); + } + }); + + imageUpload.addEventListener('change', handleImageUpload); + + function handleImageUpload() { + if (imageUpload.files && imageUpload.files[0]) { + const reader = new FileReader(); + reader.onload = function (e) { + previewImage.src = e.target.result; + imagePreview.classList.remove('hidden'); + }; + reader.readAsDataURL(imageUpload.files[0]); + } + } + + removeImage.addEventListener('click', function () { + imagePreview.classList.add('hidden'); + imageUpload.value = ''; + }); + + imageStrength.addEventListener('input', function () { + strengthValue.textContent = this.value; + }); + + // 随机提示词功能 - 移到顶层DOMContentLoaded回调中 + const randomPromptBtn = document.getElementById('randomPromptBtn'); + const randomImagePromptBtn = document.getElementById('randomImagePromptBtn'); + const promptInput = document.getElementById('prompt'); + const imagePromptInput = document.getElementById('imagePrompt'); + + if (randomPromptBtn && promptInput) { + randomPromptBtn.addEventListener('click', function () { + const randomPrompt = textPromptExamples[Math.floor(Math.random() * textPromptExamples.length)]; + promptInput.value = randomPrompt; + }); + } + + if (randomImagePromptBtn && imagePromptInput) { + randomImagePromptBtn.addEventListener('click', function () { + const randomPrompt = imagePromptExamples[Math.floor(Math.random() * imagePromptExamples.length)]; + imagePromptInput.value = randomPrompt; + }); + } + + // 生成按钮点击事件 + const generateBtn = document.getElementById('generateBtn'); + generateBtn.addEventListener('click', generateImage); + + // 重新生成按钮点击事件 + const regenerateBtn = document.getElementById('regenerateBtn'); + regenerateBtn.addEventListener('click', function () { + // 隐藏结果区域 + document.getElementById('resultSection').classList.add('hidden'); + // 显示状态区域 + document.getElementById('statusSection').classList.remove('hidden'); + // 重新生成 + generateImage(); + }); + + // 放大按钮点击事件 + const upscaleBtn = document.getElementById('upscaleBtn'); + upscaleBtn.addEventListener('click', function () { + alert('图像放大功能即将实现'); + }); + + // 变体按钮点击事件 + const variationBtn = document.getElementById('variationBtn'); + variationBtn.addEventListener('click', function () { + alert('生成变体功能即将实现'); + }); + + // 下载按钮点击事件 + const downloadBtn = document.getElementById('downloadBtn'); + downloadBtn.addEventListener('click', function () { + const resultImage = document.getElementById('resultImage'); + const link = document.createElement('a'); + link.href = resultImage.src; + link.download = 'midjourney-image.png'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }); + + + // 生成图像函数 + async function generateImage() { + let prompt, base64Array = null; + + // 检查是文生图还是图生图 + if (!textToImagePanel.classList.contains('hidden')) { + // 文生图 + prompt = document.getElementById('prompt').value.trim(); + if (!prompt) { + alert('请输入提示词'); + return; + } + } else { + // 图生图 + prompt = document.getElementById('imagePrompt').value.trim(); + if (!prompt) { + alert('请输入提示词'); + return; + } + + if (!imageUpload.files || !imageUpload.files[0]) { + alert('请上传参考图片'); + return; + } + + // 转换图片为base64 + const reader = new FileReader(); + reader.onload = function (e) { + base64Array = [e.target.result.split(',')[1]]; + submitGenerateRequest(prompt, base64Array); + }; + reader.readAsDataURL(imageUpload.files[0]); + return; + } + + // 获取表单元素并进行空值检查 + const promptElement = document.getElementById('prompt'); + const aspectRatioElement = document.getElementById('aspectRatio'); + const qualityElement = document.getElementById('quality'); + const styleElement = document.getElementById('style'); + + // 检查是否所有必要元素都存在 + if (!promptElement || !aspectRatioElement || !qualityElement || !styleElement) { + showError('表单元素缺失,请检查页面配置'); + return; + } + + // 创建FormData并添加值 + const formData = new FormData(); + formData.append('prompt', promptElement.value); + formData.append('aspect_ratio', aspectRatioElement.value); + formData.append('quality', qualityElement.value); + formData.append('style', styleElement.value); + + // 调用提交函数 + submitGenerateRequest(formData); + } + + // 提交生成请求函数 + async function submitGenerateRequest(formData) { + // 验证formData是有效的FormData对象 + if (!(formData instanceof FormData)) { + showError('无效的表单数据格式'); + return; + } + + // 提取表单数据 + const prompt = formData.get('prompt'); + + // 验证必填字段 + if (!prompt) { + showError('请输入提示词'); + return; + } + + // 从formData提取值(确保表单元素有正确的name属性) + const promptText = formData.get('prompt')?.trim(); + const selectedAspectRatio = formData.get('aspect_ratio'); + const selectedQuality = formData.get('quality'); + const selectedStyle = formData.get('style'); + + // 验证必要参数 + if (!promptText) { + showError('提示词不能为空'); + return; + } + + if (!selectedAspectRatio || !selectedQuality || !selectedStyle) { + showError('请选择完整的生成参数'); + return; + } + + try { + const response = await fetch('/api/mj/imagine', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + prompt: promptText, + aspect_ratio: selectedAspectRatio, + quality: selectedQuality, + style: selectedStyle + }) + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => null); + throw new Error(`生成请求失败: ${errorData?.message || response.statusText}`); + } + + const result = await response.json(); + console.log('Backend response:', result); // Add this line + + if (!result || !result.task_id) { + showError('生成失败: 未获取到任务ID'); + return null; + } + + // 显示状态区域并开始轮询 + document.getElementById('statusSection').classList.remove('hidden'); + pollTaskStatus(result.task_id, promptText); + return result.task_id; + } catch (error) { + console.error('提交生成请求失败:', error); + showError('提交请求失败: ' + error.message); + return null; + } + } + + // 轮询任务状态函数 + function pollTaskStatus(taskId, prompt) { + // 轮询配置 + const queryInterval = 3000; // 3秒查询一次 + const maxQueries = 120; // 最多查询120次(6分钟) + let queryCount = 0; // 当前查询次数 + const startTime = new Date().getTime(); // 开始时间 + let timerId = null; // 用于存储setTimeout的ID + + // 定义内部轮询函数 + function checkStatus() { + // 检查是否超过最大查询次数 + if (queryCount >= maxQueries) { + const elapsedTime = Math.round((new Date().getTime() - startTime) / 1000); + document.getElementById('status-display').innerHTML = ` 图像生成超时(已等待${elapsedTime}秒),您可以稍后在历史记录中查看,或重新生成。`; + document.getElementById('progress-text').textContent = '生成超时'; + // 恢复生成按钮 + generateBtn.disabled = false; + generateBtn.innerHTML = '生成图像'; + return; + } + + // 发起状态查询请求 + fetch(`/api/mj/task_status?task_id=${taskId}`) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + queryCount++; + // 获取DOM元素 + const statusMessage = document.getElementById('status-display'); + const progressFill = document.getElementById('progress-bar'); + const progressText = document.getElementById('progress-text'); + + // 处理错误状态 + if (data.error) { + statusMessage.innerHTML = ` 生成失败: ${data.error}`; + statusMessage.style.color = '#ef4444'; + // 恢复生成按钮 + generateBtn.disabled = false; + generateBtn.innerHTML = '生成图像'; + return; + } + + // 更新状态信息 + statusMessage.innerHTML = ` AI 正在创作中...`; + statusMessage.style.color = ''; // 重置颜色 + + // 更新进度条 + let progress = data.progress || 0; + // 正确解析进度值,处理"32%"这样的字符串 + if (typeof progress === 'string' && progress.endsWith('%')) { + progress = parseFloat(progress); + } else if (typeof progress === 'string') { + progress = parseFloat(progress) || 0; + } + // 添加NaN检查 + if (isNaN(progress) || progress < 0) progress = 0; + if (progress > 100) progress = 100; + const elapsedTime = Math.round((new Date().getTime() - startTime) / 1000); + progressFill.style.width = `${progress}%`; + progressText.textContent = `${Math.round(progress)}%`; + statusMessage.innerHTML += ` (已等待${elapsedTime}秒)`; + + // 任务完成 + if (data.status === 'completed') { + statusMessage.innerHTML = ' 图像生成完成!'; + statusMessage.style.color = '#10b981'; + + // 确保进度为100% + progressFill.style.width = '100%'; + progressText.textContent = '100%'; + + // 显示结果 + setTimeout(() => { + showResult(data.image_url, taskId, prompt); + }, 1000); + + // 清除定时器 + if (timerId) { + clearTimeout(timerId); + timerId = null; + } + } + // 任务失败 + else if (data.status === 'failed') { + statusMessage.innerHTML = ` 生成失败: ${data.error || '未知错误'}`; + statusMessage.style.color = '#ef4444'; + // 恢复生成按钮 + generateBtn.disabled = false; + generateBtn.innerHTML = '生成图像'; + } + // 继续轮询 + else { + timerId = setTimeout(checkStatus, queryInterval); + } + }) + .catch(error => { + console.error('检查任务状态失败:', error); + // 在错误情况下仍继续轮询直到达到最大次数 + if (queryCount < maxQueries) { + queryCount++; + timerId = setTimeout(checkStatus, queryInterval); + } else { + document.getElementById('status-display').innerHTML = ` 连接服务器失败,请重试`; + document.getElementById('status-display').style.color = '#ef4444'; + // 恢复生成按钮 + generateBtn.disabled = false; + generateBtn.innerHTML = '生成图像'; + } + }); + } + + // 立即开始第一次检查 + checkStatus(); + } + + // 显示结果函数 + function showResult(imageUrl, taskId, prompt) { + // 隐藏状态区域 + document.getElementById('statusSection').classList.add('hidden'); + + // 显示结果区域 + const resultSection = document.getElementById('resultSection'); + resultSection.classList.remove('hidden'); + + // 设置结果图像 + const resultImage = document.getElementById('resultImage'); + resultImage.src = imageUrl; + + // 恢复生成按钮 + generateBtn.disabled = false; + generateBtn.innerHTML = '生成图像'; + + } + + // 添加 showError function after checkTaskStatus + function showError(message) { + const errorElement = document.getElementById('error-message'); + if (errorElement) { + errorElement.textContent = message; + errorElement.classList.remove('hidden'); + // Auto-hide after 5 seconds + setTimeout(() => errorElement.classList.add('hidden'), 5000); + } else { + // Fallback if error element not found + alert(message); + } + } + + // 文生图示例提示词库 + const textPromptExamples = [ + "一只穿着太空服的柯基犬在火星表面行走,背景是红色星球和远处的地球,科幻风格,高清细节", + "中国传统水墨画风格的山间小屋,雾气缭绕,远处有瀑布,意境悠远,留白艺术", + "蒸汽朋克风格的城市景观,空中漂浮着飞艇,机械结构细节丰富,黄昏光影,8K分辨率", + "可爱的卡通熊猫宇航员在太空中吃竹子,周围有星星和行星,迪士尼风格,明亮色彩", + "赛博朋克风格的东京雨夜,霓虹灯闪烁,全息广告,湿润的街道反射, Blade Runner 氛围", + "中世纪奇幻城堡坐落在山顶,周围有龙和魔法光芒,日落时分,史诗电影场景", + "极简主义风格的室内设计,白色为主色调,原木家具,大窗户,自然光,北欧风格", + "未来科技感的跑车设计,流线型车身,透明车顶,悬浮效果,银色金属质感,未来城市背景", + "水彩风格的森林动物聚会,兔子、狐狸和松鼠围坐在蘑菇旁,童话风格,柔和色彩", + "复古80年代风格的街机游戏厅,霓虹灯光,老式游戏机,像素艺术,怀旧氛围" + ]; + + // 图生图示例提示词库 + const imagePromptExamples = [ + "将图片转换为梵高风格的油画,星月夜笔触,鲜艳色彩", + "变成卡通风格,线条简洁,色彩明亮,适合儿童绘本", + "转换为赛博朋克风格,添加霓虹灯效果和未来科技元素", + "模拟水墨画效果,黑白灰三色,强调意境和笔触", + "变成3D渲染风格,添加立体效果和真实光影", + "转换为像素艺术,8-bit风格,适合复古游戏场景", + "模拟水彩画效果,柔和的色彩过渡和纹理", + "变成低多边形风格,几何图形构成,简约美学", + "转换为蒸汽朋克风格,添加齿轮、铜制元素和机械结构", + "模拟素描效果,铅笔线条,强调轮廓和阴影" + ]; + + // 页面加载完成后绑定事件 + document.addEventListener('DOMContentLoaded', function () { + // 文生图随机按钮 + const randomPromptBtn = document.getElementById('randomPromptBtn'); + const promptInput = document.getElementById('prompt'); + + // 图生图随机按钮 + const randomImagePromptBtn = document.getElementById('randomImagePromptBtn'); + const imagePromptInput = document.getElementById('imagePrompt'); + + // 文生图随机功能 + if (randomPromptBtn && promptInput) { + randomPromptBtn.addEventListener('click', function () { + const randomIndex = Math.floor(Math.random() * textPromptExamples.length); + promptInput.value = textPromptExamples[randomIndex]; + // 添加输入框焦点效果 + promptInput.focus(); + setTimeout(() => promptInput.blur(), 100); + }); + } + + // 图生图随机功能 + if (randomImagePromptBtn && imagePromptInput) { + randomImagePromptBtn.addEventListener('click', function () { + const randomIndex = Math.floor(Math.random() * imagePromptExamples.length); + imagePromptInput.value = imagePromptExamples[randomIndex]; + // 添加输入框焦点效果 + imagePromptInput.focus(); + setTimeout(() => imagePromptInput.blur(), 100); + }); + } + + // 初始化历史记录网格 + updateHistoryGrid(); + }); +}); \ No newline at end of file