不愧是白宫严选!5 分钟写完一个月的代码量,原型图秒变代码!附源码~

职场   2025-01-17 13:05   福建  

都年底了,产品部门也不懂的消停,整了十几个原型图过来~~

不过现在我们开发都不自己写代码了,大模型这么火爆,被美帝加入严选的智谱  Flash 系列普惠模型,都免费开放了,而且 API 调用起来也方便,必须安排个原型图转代码工具,再多的任务也没问题。

今天我就打算用这些免费的模型给产品部门上点强度~~

大概思路如下,利用免费的智普大模型  glm-4v-flash 把产品部门给我的原型图直接转换为代码,先转成 HTML 代码演示下。

整个功能就是读取一个目录下的所有原型图片,然后点击左侧🫲文件列表中的图片,再右侧🫱点击 “AI 解读” 按钮来解析原型图片,解析完成后,API 接口返回生成的代码,代码可以在前端显示预览的效果:

另外,我们也可以点击 “切换源码/预览” 按钮,查看源代码,效果图如下:

整个效果生成如下,现在 glm-4v-flash 的性能还是挺快的:

再来一个任务列表的原型图,生成页面效果如下:

切换到源代码模式查看代码:

以上所有功能实现是基于免费的智普大模型  glm-4v-flash不愧是被白宫严选的“大模型”,不用钱的用起来就是舒服。

智谱官方也直接硬刚了,即便遭遇制裁,智谱 AI 依然底气十足,表示掌握全链路大模型核心技术,制裁不会对其业务产生实质影响,并将继续参与全球人工智能竞争,推动技术发展。

接下来直接上源代码了,有兴趣的可以尝试下。


1、安装

先安装下智谱的 Python SDK 库,当然也可以其他的语言,官方文档都有提供详细说明,我这采用的是 Python:

pip install zhipuai

服务端采用 FastAPI 来实现文件读取和 API 的请求功能,安装 FastAPI 和 Uvicorn:

pip install fastapi uvicorn

前端采用的是 SSE 调用,所以还要安装一个专门用于处理 Server-Sent Events (SSE) 的库:

pip install sse-starlette

在代码中导入 EventSourceResponse: 

from sse_starlette.sse import EventSourceResponse

2、源码

前端部分需要引入的第三方 CDN 库,如下所示:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script><link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism-tomorrow.min.css" rel="stylesheet" /><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/prism.min.js"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

前端 HTML 代码使用 bootstrap5 来实现:

<div class="container-fluid p-0">    <!-- 头部 -->    <header class="bg-dark  text-white text-center py-3" >        <h1>原型图转代码</h1>    </header>        <!-- 主体 -->    <main class="row no-gutters my-2 mx-4 border">        <!-- 左侧文件目录 -->        <aside class="col-md-2" id="file-list"> <!-- 左侧菜单宽度调整为 col-md-2 -->            <div class="card">                <div class="card-header">文件列表</div>                <div class="card-body">                    <ul class="list-group">                        <!-- 文件列表通过 JS 动态生成 -->                    </ul>                </div>            </div>        </aside>                <!-- 右侧代码阅读器 -->        <section class="col-md-10"> <!-- 右侧内容宽度调整为 col-md-10 -->            <div class="card">                <div class="card-header">                    <button id="parseButton" class="btn btn-warning btn-lg transition-all">                        <span id="buttonText">AI 解析图片</span>                        <span id="loadingSpinner" class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>                    </button>                    <button id="toggleSourceButton" class="btn btn-secondary btn-lg transition-all">切换源码/预览</button>                </div>                                <div class="card-body">                    <!-- 图片和代码并列排列 -->                    <div class="image-code-container">                        <div id="imageContainer" class="image-container" style="display:none"></div>                        <div class="code-output-container">                            <div id="output" class="alert alert-secondary mt-2" role="alert" >...</div>                        </div>                    </div>                </div>            </div>        </section>    </main>
<!-- 底部 --> <footer class="bg-secondary text-white text-center py-2"> <p>© 2024 原型转换工具 - WWW.RUNOOB</p> </footer>
<!-- Toast 提示 --> <div id="toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast-header"> <strong class="me-auto">提示</strong> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> </div> <div class="toast-body"> 解析完成! </div> </div></div>

CSS 代码如下:

body {    background: linear-gradient(to bottom right, #f6e7fc, #e3f4fc);}#file-list {    height: 80vh;    overflow-y: auto;    border-right: 1px solid #ddd;}.code-container {    height: 60vh;    border-left: 1px solid #ddd;}.img-fluid {    max-width: 100%;    height: auto;}.transition-all {    transition: all 0.3s ease;}/* 图片和代码并列排列 */.image-code-container {    display: flex;    gap: 1rem; /* 图片和代码之间的间距 */}.image-container {    flex: 1;    max-width: 50%; /* 图片占一半宽度 */}.code-output-container {    flex: 1;    max-width: 70%; /* 代码占一半宽度 */ }/* Toast 样式 */.toast {    position: fixed;    top: 20px;    right: 20px;    z-index: 1000;}/* 源码模式样式 */#output pre {    background: #2d2d2d;    color: #f8f9fa;    padding: 1rem;    border-radius: 5px;    overflow-x: auto; /* 水平滚动 */    overflow-y: auto; /* 垂直滚动 */    white-space: pre-wrap; /* 保留换行 */    max-height: 60vh; /* 限制高度 */}

前端 JS 代码部分

$(document).ready(function () {    const fileList = $("#file-list .list-group");    let filenamePath = '';    let isSourceMode = false; // 当前是否为源码模式    let currentMarkdown = ''; // 当前解析的 Markdown 内容    let currentHtml = ''; // 当前解析的 HTML 内容
// 从 FastAPI 后端获取文件列表 $.get('http://127.0.0.1:8000/files', function (data) { const files = data.files; files.forEach(file => { const listItem = $(`<li class="list-group-item transition-all">${file}</li>`); listItem.on("click", function() { $('.list-group-item').removeClass('active'); $(this).addClass('active'); $("#output").html(''); loadFile(file); }); fileList.append(listItem); }); });
// 加载文件内容 function loadFile(filename) { filename = `http://127.0.0.1:8000/file/${filename}`; if (isImageFile(filename)) { convertImageUrlToBase64(filename) .then(base64String => { const img = $('<img class="img-fluid">').attr('src', base64String); $('#imageContainer').empty().append(img).show(); }) .catch(error => { console.error('Error:', error); }); } else { $('#imageContainer').html(`${filename} 不是图片文件,无法显示。`).show(); } }
// 点击 AI 解析按钮 $("#parseButton").click(function () { const base64Code = $("#imageContainer img").attr("src"); if (!base64Code) { alert("请设置图片!"); return; }
// 显示加载效果 $("#loadingSpinner").show(); $("#buttonText").text("解析中..."); $("#parseButton").prop("disabled", true);
const eventSource = new EventSource(`http://127.0.0.1:8000/start-parsing?image=${encodeURIComponent(base64Code)}`); $("#output").html(''); let _markdownContent = ''; eventSource.onmessage = function(event) { const data = JSON.parse(event.data); const content = data.content; if (content === "[DONE]") { eventSource.close();
// 隐藏加载效果 $("#loadingSpinner").hide(); $("#buttonText").text("AI 解读"); $("#parseButton").prop("disabled", false);
// 显示完成提示 const toast = new bootstrap.Toast(document.getElementById('toast')); toast.show();
// 保存当前内容 currentMarkdown = _markdownContent; // currentHtml = marked.parse(currentMarkdown); // 解析 Markdown 为 HTML currentHtml = currentMarkdown; console.log(currentHtml); // 替换开头的 ```html currentHtml = currentHtml.replace(/^```html/, ''); // 替换结尾的 ``` currentHtml = currentHtml.replace(/```$/, ''); updateOutput(); // 更新显示内容
console.log("Stream completed."); } else { _markdownContent = content; } }; eventSource.onerror = function (err) { eventSource.close();
// 隐藏加载效果 $("#loadingSpinner").hide(); $("#buttonText").text("AI 解读"); $("#parseButton").prop("disabled", false);
console.error("Error:", err); }; });
// 切换源码/预览模式 $("#toggleSourceButton").click(function () { isSourceMode = !isSourceMode; // 切换模式 updateOutput(); // 更新显示内容 });
// 更新显示内容 function updateOutput() { if (isSourceMode) { // 源码模式:显示原始的 Markdown 内容 $("#output").html(`<pre><code class="language-html">${escapeHtml(currentMarkdown)}</code></pre>`); Prism.highlightAll(); // 高亮代码 } else { // 预览模式:显示解析后的 HTML 内容 $("#output").html(currentHtml); } }
// 转义 HTML 特殊字符 function escapeHtml(html) { return html.replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); }
// 判断文件是否为图片的函数 function isImageFile(filename) { const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']; const extension = filename.split('.').pop().toLowerCase(); return imageExtensions.includes(extension); }
function convertImageUrlToBase64(url) { return new Promise((resolve, reject) => { fetch(url) .then(response => response.blob()) .then(blob => { const reader = new FileReader(); reader.onload = () => { const base64Data = reader.result; const pngBase64Data = base64Data.replace(/^data:.+;base64,/, 'data:image/png;base64,'); resolve(pngBase64Data); }; reader.onerror = () => reject(new Error('Failed to read file')); reader.readAsDataURL(blob); }) .catch(error => reject(error)); }); }});

后端部分源代码:

# 指定根目录下的 `files` 目录base_dir = Path(__file__).parent / "files"# 配置静态文件服务,用于提供前端页面app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/", response_class=FileResponse)async def read_root(): return FileResponse("image.html", media_type="text/html")
@app.get("/files")async def get_files(): try: if not base_dir.exists(): return {"error": "The files directory does not exist"} files = get_files_recursive(base_dir) return {"files": files} except Exception as e: return {"error": str(e)} @app.get("/start-parsing")async def start_parsing(request: Request): img_base = request.query_params.get('image') client = ZhipuAI(api_key=apiKey) """ 接收图片的 Base64 数据并解析 """ response = client.chat.completions.create( model="glm-4v-flash", # 填写需要调用的模型名称 messages=[ { "role": "user", "content": [ { "type": "image_url", "image_url": { "url": img_base } }, { "type": "text", "text": "你是一个开放人员,图片内容是一个原型设计稿,使用 bootstrap 生成一个跟他一摸一样网页代码,只需要输出代码即可。" } ] } ] )
# 生成器:逐步输出流式数据 async def event_stream(): for chunk in response: delta_content = response.choices[0].message.content if delta_content: yield f"{json.dumps({'content': delta_content})}" #yield "data: [DONE]\n\n" # 结束标志 yield f"{json.dumps({'content': '[DONE]'})}"# 结束标志 return EventSourceResponse(event_stream())

3、APIKey 申请
用的智谱 SDK 需要到官方平台去申请 APIKey。
智谱 AI 开放平台:https://bigmodel.cn?utm_source=2&utm_campaign=yrgzh&_channel_track_key=qY2wSfNO
智谱现在免费开放了 Flash 系列模型 API,包含: 
  • 语言模型 GLM-4-Flash 
  • 图像理解模型 GLM-4V-Flash
  • 图像生成模型 CogView-3-Flash
  • 视频生成模型 CogVideoX-Flash
这些都免费了,还要啥自行车,实在太香~~~

另外,近期智谱还是上新了 GLM-Realtime API ,具备了实时视频通话,跨文本、音频、视频推理的能力,AI 通话时人还可以插嘴。

GLM-Realtime  还能用摄像头互动、读屏幕信息、理解对话环境,还创新实现清唱功能。

👇点击「阅读原文」,申请 APIKey!👇

菜鸟教程
学的不仅是技术,更是梦想!
 最新文章