AI 生成: 一个基于Flask的AI一句话生成图片、图标、Logo的Web应用,使用Pollinations AI免费图像生成API。

This commit is contained in:
admin
2026-09-29 18:09:39 +08:00
commit 50e35d42c0
6 changed files with 564 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
// AI 图片生成器 - 前端逻辑
const promptEl = document.getElementById("prompt");
const typeEl = document.getElementById("type");
const sizeEl = document.getElementById("size");
const styleEl = document.getElementById("style");
const generateBtn = document.getElementById("generateBtn");
const statusEl = document.getElementById("status");
const placeholderEl = document.getElementById("placeholder");
const resultImageEl = document.getElementById("resultImage");
const actionsEl = document.getElementById("actions");
const downloadBtn = document.getElementById("downloadBtn");
// 设置状态提示
function setStatus(message, isError = false) {
statusEl.textContent = message || "";
statusEl.classList.toggle("error", Boolean(isError));
}
// 显示加载中
function setLoading(loading) {
generateBtn.disabled = loading;
generateBtn.textContent = loading ? "⏳ 生成中..." : "✨ 生成图片";
}
// 调用后端生成图片
async function generateImage() {
const prompt = promptEl.value.trim();
if (!prompt) {
setStatus("请先输入图片描述", true);
promptEl.focus();
return;
}
setLoading(true);
setStatus("正在生成,请稍候...");
try {
const response = await fetch("/api/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
prompt: prompt,
type: typeEl.value,
size: sizeEl.value,
style: styleEl.value,
}),
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.error || "生成失败,请稍后重试");
}
// 显示结果图
resultImageEl.src = data.image_url;
resultImageEl.classList.remove("hidden");
placeholderEl.classList.add("hidden");
actionsEl.classList.remove("hidden");
downloadBtn.href = data.image_url;
setStatus("生成成功 ✅");
} catch (err) {
setStatus(err.message || "网络错误,请稍后重试", true);
} finally {
setLoading(false);
}
}
// 绑定事件
generateBtn.addEventListener("click", generateImage);
// 支持 Ctrl + Enter 快捷生成
promptEl.addEventListener("keydown", (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
generateImage();
}
});
+205
View File
@@ -0,0 +1,205 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #0f1220;
--panel: #1a1f35;
--panel-2: #232a47;
--text: #e8ecff;
--muted: #9aa3c7;
--primary: #7c5cff;
--primary-hover: #6a4bee;
--border: #2c3457;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Microsoft YaHei", sans-serif;
background: radial-gradient(circle at top, #1b2140 0%, var(--bg) 60%);
color: var(--text);
min-height: 100vh;
padding: 24px 16px;
}
.app {
max-width: 1100px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 24px;
}
.header h1 {
font-size: 28px;
margin-bottom: 6px;
}
.subtitle {
color: var(--muted);
font-size: 14px;
}
.main {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 860px) {
.main {
grid-template-columns: 1fr;
}
}
.panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 16px;
padding: 20px;
}
.field {
display: block;
margin-bottom: 14px;
}
.field-label {
display: block;
font-size: 13px;
color: var(--muted);
margin-bottom: 6px;
}
textarea,
select {
width: 100%;
background: var(--panel-2);
border: 1px solid var(--border);
border-radius: 10px;
color: var(--text);
padding: 10px 12px;
font-size: 14px;
font-family: inherit;
outline: none;
transition: border-color 0.2s;
}
textarea:focus,
select:focus {
border-color: var(--primary);
}
textarea {
resize: vertical;
}
.row {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
}
@media (max-width: 520px) {
.row {
grid-template-columns: 1fr;
}
}
.btn-primary {
width: 100%;
background: var(--primary);
color: #fff;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
}
.btn-primary:hover:not(:disabled) {
background: var(--primary-hover);
}
.btn-primary:active:not(:disabled) {
transform: scale(0.99);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-secondary {
display: inline-block;
background: var(--panel-2);
color: var(--text);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 16px;
font-size: 14px;
text-decoration: none;
cursor: pointer;
transition: background 0.2s;
}
.btn-secondary:hover {
background: #2c3560;
}
.status {
margin-top: 10px;
font-size: 13px;
color: var(--muted);
min-height: 18px;
}
.status.error {
color: #ff7b7b;
}
.preview-panel {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 380px;
}
.placeholder {
text-align: center;
color: var(--muted);
}
.placeholder-icon {
font-size: 48px;
margin-bottom: 10px;
}
.result-image {
max-width: 100%;
border-radius: 12px;
border: 1px solid var(--border);
display: block;
}
.actions {
margin-top: 16px;
}
.hidden {
display: none !important;
}
.footer {
text-align: center;
color: var(--muted);
font-size: 12px;
margin-top: 24px;
}