智绘之泉N.0,也可以叫天琴AI绘画师

演示站(这个UI超好看):https://websim.ai/c/xQmkwDSfnOrHj2aiD
如图:

已经不知道在论坛看过多少个薅flux的代码了
这里再重写一个
也是不同的UI
代码逻辑是一模一样的
效果:


代码来源:https://linux.do/t/topic/182826?u=stellafortuna

老毛病了,我又把代码没备份就删掉了
简单重写了一下代码,UI变得简陋了一些,我哭 没有什么事是比UI难看更难受的:sob:
后端不支持生成更多图片 每次只能生成一张

代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>天琴AI绘画师</title>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;600&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-gradient-light: linear-gradient(135deg, #f6d365 0%, #fda085 100%);
            --bg-gradient-dark: linear-gradient(135deg, #30cfd0 0%, #330867 100%);
            --text-color-light: #333;
            --text-color-dark: #e0e0e0;
            --button-bg-light: #ff6b6b;
            --button-bg-dark: #4e54c8;
            --container-bg-light: rgba(255, 255, 255, 0.7);
            --container-bg-dark: rgba(16, 18, 27, 0.7);
            --input-border-light: #ff9a9e;
            --input-border-dark: #6a11cb;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Noto Sans SC', sans-serif;
            background: var(--bg-gradient-light);
            color: var(--text-color-light);
            transition: all 0.5s ease;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
            overflow-x: hidden;
        }

        body::before {
            content: '';
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: 
                radial-gradient(white, rgba(255,255,255,.2) 2px, transparent 40px),
                radial-gradient(white, rgba(255,255,255,.15) 1px, transparent 30px),
                radial-gradient(white, rgba(255,255,255,.1) 2px, transparent 40px);
            background-size: 550px 550px, 350px 350px, 250px 250px;
            background-position: 0 0, 40px 60px, 130px 270px;
            animation: starMove 200s linear infinite;
            z-index: -1;
        }

        @keyframes starMove {
            from { transform: translateY(0); }
            to { transform: translateY(-2000px); }
        }

        body.dark-theme {
            background: var(--bg-gradient-dark);
            color: var(--text-color-dark);
        }

        .container {
            background: var(--container-bg-light);
            padding: 2rem;
            border-radius: 20px;
            box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
            backdrop-filter: blur(4px);
            border: 1px solid rgba(255, 255, 255, 0.18);
            max-width: 600px;
            width: 100%;
            transition: all 0.5s ease;
            position: relative;
            overflow: hidden;
        }

        body.dark-theme .container {
            background: var(--container-bg-dark);
        }

        h1 {
            text-align: center;
            margin-bottom: 1rem;
            font-weight: 600;
            color: #ff6b6b;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
        }

        body.dark-theme h1 {
            color: #4e54c8;
        }

        textarea, input, button, select {
            width: 100%;
            padding: 0.75rem;
            margin-bottom: 1rem;
            box-sizing: border-box;
            border-radius: 10px;
            border: 2px solid var(--input-border-light);
            background: rgba(255, 255, 255, 0.8);
            transition: all 0.3s ease;
            font-family: 'Noto Sans SC', sans-serif;
        }

        body.dark-theme textarea, 
        body.dark-theme input, 
        body.dark-theme select {
            border-color: var(--input-border-dark);
            background: rgba(255, 255, 255, 0.1);
            color: var(--text-color-dark);
        }

        button {
            background-color: var(--button-bg-light);
            color: white;
            border: none;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        body.dark-theme button {
            background-color: var(--button-bg-dark);
        }

        button:hover {
            transform: translateY(-3px);
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
        }

        #result {
            margin-top: 1rem;
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
        }

        #result img {
            width: 100%;
            border-radius: 10px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
            transition: transform 0.3s ease;
        }

        #result img:hover {
            transform: scale(1.05);
        }

        .theme-toggle {
            position: fixed;
            top: 1rem;
            right: 1rem;
            z-index: 1000;
        }

        .theme-toggle button {
            background: none;
            border: none;
            font-size: 2rem;
            cursor: pointer;
            transition: transform 0.3s ease;
        }

        .theme-toggle button:hover {
            transform: rotate(360deg);
        }

        .loading {
            display: none;
            text-align: center;
            margin-top: 1rem;
        }

        .loading::after {
            content: '';
            display: inline-block;
            width: 30px;
            height: 30px;
            border: 3px solid #ff6b6b;
            border-radius: 50%;
            border-top-color: transparent;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        .error {
            color: #ff6b6b;
            text-align: center;
            margin-top: 1rem;
        }

        #history {
            margin-top: 2rem;
            border-top: 1px solid var(--input-border-light);
            padding-top: 1rem;
        }

        #history h2 {
            margin-bottom: 1rem;
        }

        #historyList {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
        }

        #historyList img {
            width: 100px;
            height: 100px;
            object-fit: cover;
            border-radius: 5px;
            cursor: pointer;
            transition: transform 0.3s ease;
        }

        #historyList img:hover {
            transform: scale(1.1);
        }

        @media (max-width: 600px) {
            .container {
                padding: 1rem;
            }

            h1 {
                font-size: 1.5rem;
            }

            textarea, input, button, select {
                font-size: 0.9rem;
            }

            #result {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="theme-toggle">
        <button id="themeToggle">🌓</button>
    </div>
    <div class="container">
        <h1>天琴AI绘画师</h1>
        <textarea id="prompt" rows="4" placeholder="请描述你想要生成的图像..."></textarea>
        <select id="aspectRatio">
            <option value="1:1">1:1 (正方形)</option>
            <option value="4:3">4:3 (标准)</option>
            <option value="16:9">16:9 (宽屏)</option>
        </select>
        <select id="outputFormat">
            <option value="webp">WebP</option>
            <option value="png">PNG</option>
            <option value="jpg">JPG</option>
        </select>
        <input type="number" id="numOutputs" min="1" max="4" value="1" placeholder="生成数量 (1-4)">
        <input type="number" id="outputQuality" min="1" max="100" value="90" placeholder="输出质量 (1-100)">
        <button id="generate">生成图像</button>
        <div class="loading" id="loading"></div>
        <div id="result"></div>
        <div class="error" id="error"></div>
        <div id="history">
            <h2>历史记录</h2>
            <div id="historyList"></div>
        </div>
    </div>

    <script>
        const themeToggle = document.getElementById('themeToggle');
        const body = document.body;
        const generateBtn = document.getElementById('generate');
        const promptInput = document.getElementById('prompt');
        const aspectRatioSelect = document.getElementById('aspectRatio');
        const outputFormatSelect = document.getElementById('outputFormat');
        const numOutputsInput = document.getElementById('numOutputs');
        const outputQualityInput = document.getElementById('outputQuality');
        const resultDiv = document.getElementById('result');
        const loadingDiv = document.getElementById('loading');
        const errorDiv = document.getElementById('error');
        const historyList = document.getElementById('historyList');

        let history = JSON.parse(localStorage.getItem('imageHistory')) || [];

        themeToggle.addEventListener('click', () => {
            body.classList.toggle('dark-theme');
            themeToggle.textContent = body.classList.contains('dark-theme') ? '🌞' : '🌓';
        });

        generateBtn.addEventListener('click', async () => {
            const prompt = promptInput.value;
            const aspectRatio = aspectRatioSelect.value;
            const outputFormat = outputFormatSelect.value;
            const numOutputs = numOutputsInput.value;
            const outputQuality = outputQualityInput.value;

            if (!prompt) {
                showError('请输入图像描述');
                return;
            }

            showLoading();
            try {
                const token = await getToken();
                const imageUrls = await generateImage(token, prompt, aspectRatio, outputFormat, numOutputs, outputQuality);
                showResult(imageUrls);
                addToHistory(imageUrls);
            } catch (error) {
                showError('生成图像时出错: ' + error.message);
            } finally {
                hideLoading();
            }
        });

        async function getToken() {
            try {
                const response = await fetch('https://fluxaiweb.com/flux/getToken');
                if (!response.ok) {
                    throw new Error('获取令牌失败');
                }
                const data = await response.json();
                return data.data.token;
            } catch (error) {
                throw new Error('获取令牌时出错: ' + error.message);
            }
        }

        async function generateImage(token, prompt, aspectRatio, outputFormat, numOutputs, outputQuality) {
            try {
                const response = await fetch('https://fluxaiweb.com/flux/generateImage', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'token': token
                    },
                    body: JSON.stringify({
                        prompt,
                        aspectRatio,
                        outputFormat,
                        numOutputs: parseInt(numOutputs),
                        outputQuality: parseInt(outputQuality)
                    })
                });

                if (!response.ok) {
                    throw new Error('生成图像失败');
                }

                const data = await response.json();
                return Array.isArray(data.data.images) ? data.data.images : [data.data.image];
            } catch (error) {
                throw new Error('生成图像时出错: ' + error.message);
            }
        }

        function showResult(imageUrls) {
            resultDiv.innerHTML = imageUrls.map(url => `
                <div>
                    <img src="${url}" alt="生成的图像" onclick="openImageInNewTab('${url}')">
                    <button onclick="downloadImage('${url}')">下载</button>
                </div>
            `).join('');
        }

        function showLoading() {
            loadingDiv.style.display = 'block';
            errorDiv.textContent = '';
        }

        function hideLoading() {
            loadingDiv.style.display = 'none';
        }

        function showError(message) {
            errorDiv.textContent = message;
        }

        function addToHistory(imageUrls) {
            history = [...imageUrls, ...history].slice(0, 20);
            localStorage.setItem('imageHistory', JSON.stringify(history));
            updateHistoryDisplay();
        }

        function updateHistoryDisplay() {
            historyList.innerHTML = history.map(url => `
                <img src="${url}" alt="历史图像" onclick="openImageInNewTab('${url}')">
            `).join('');
        }

        function openImageInNewTab(url) {
            window.open(url, '_blank');
        }

        function downloadImage(url) {
            const a = document.createElement('a');
            a.href = url;
            a.download = 'generated_image.' + url.split('.').pop();
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
        }

        // 初始化历史记录显示
        updateHistoryDisplay();
    </script>
</body>
</html>
10 个赞

没演示站吗? :tieba_025:

3 个赞

围观一手on

2 个赞

演不动,我去用websIM给你们整一个吧

1 个赞

放cf上 :tieba_025:

1 个赞


失败!

2 个赞

感觉你是CORS圣体

2 个赞

有了,我不会玩cf 其他的也不会,现在啥都不会,纯小白

2 个赞

这前端代码不错,放笔记本收藏了,感谢 :crazy_face:

1 个赞

去试试我主帖第一行的演示站
真不行,那只能说是电脑问题


唯一能用的一个HTML也G了!

403是欠费了
早就跑路了

哦哦 这HTML报废了
智慧之泉 智绘之泉呢?

不行啊,要梯子吗,好像API调用无返回

1 个赞

我这边无论在什么环境测试都是完全正常的
似乎你们用的话就会type error 通常是cors限制

我去用python驱动试试

可是原作者的我调用可行 只是一直画猫
我改了依然是这样!

1 个赞

Python 用原作者的就好 可行 我试过了

1 个赞

确定原作者的现在也可以用吗,我这边测试获取token正常,第二个获取图片的API返回数据有问题

1 个赞

我这边刚才测试获取令牌都失败了:rofl:

1 个赞