操作系统应用(三十六)golang语言ER实体图开发—东方仙盟筑基期

Mermaid 图标渲染工具改为 Go 语言实现,核心思路是:用 Go 搭建 Web 服务 (处理 HTTP 请求),保留前端界面和交互逻辑(复用原 HTML/CSS/JS),通过 Go 的文件服务提供静态资源。以下是完整实现方案:

一、项目结构

复制代码
mermaid-render-go/
├── static/                # 静态资源目录(存放前端文件)
│   ├── index.html         # 前端界面(复用原HTML,仅修改路径)
│   └── css/               # (可选,若拆分CSS)
│   └── js/                # (可选,若拆分JS)
└── main.go                # Go后端服务代码

二、Go 后端代码(main.go)

功能:搭建 HTTP 服务,提供静态文件访问(前端页面、Mermaid 库),无需额外后端逻辑(渲染由前端 JS 完成)。

go

运行

复制代码
package main

import (
	"log"
	"net/http"
	"path/filepath"
	"strings"
)

func main() {
	// 1. 配置静态文件路由:将/static路径映射到本地static目录
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	// 2. 根路径路由:访问localhost:8080时返回index.html
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 处理根路径请求
		if r.URL.Path == "/" {
			r.URL.Path = "/index.html" // 重定向到index.html
		}

		// 确保请求路径指向static目录下的文件
		filePath := filepath.Join("static", strings.TrimPrefix(r.URL.Path, "/"))
		// 提供文件访问
		http.ServeFile(w, r, filePath)
	})

	// 3. 启动服务
	log.Println("东方仙盟图标渲染工具已启动:http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

三、前端文件(static/index.html)

复用原 HTML 逻辑,仅修改 2 处:

  1. 移除外部 MermaidCDN 依赖(通过 Go 服务提供,或保留 CDN 均可,此处保留 CDN 更简洁);
  2. 确保路径正确(因 Go 服务提供静态文件,无需修改路径,直接复用)。

html

预览

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>未来之窗东方仙盟图标渲染工具</title>
    <!-- 保留CDN依赖(无需本地部署Mermaid,简化配置) -->
    <script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.1/dist/mermaid.min.js"></script>
    <style>
        /* 全局样式 - 黑色修仙风格(完全复用原CSS) */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: '楷体', 'KaiTi', serif;
        }
        
        body {
            background: #0a0a0a;
            background-image: 
                radial-gradient(circle at 10% 20%, rgba(121, 85, 72, 0.1) 0%, transparent 20%),
                radial-gradient(circle at 90% 80%, rgba(66, 133, 244, 0.1) 0%, transparent 20%),
                linear-gradient(to bottom, rgba(0,0,0,0.9), rgba(15,15,15,0.95));
            color: #e0e0e0;
            min-height: 100vh;
            overflow-x: hidden;
        }
        
        /* 标题样式 */
        .header {
            text-align: center;
            padding: 2rem 1rem;
            border-bottom: 1px solid rgba(212, 175, 55, 0.3);
            position: relative;
            overflow: hidden;
        }
        
        .header::before {
            content: "";
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: url("data:image/svg+xml,%3Csvg width='100' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10 Q 25 20, 50 10 T 100 10' fill='none' stroke='rgba(212, 175, 55, 0.2)' stroke-width='1'/%3E%3C/svg%3E");
            opacity: 0.3;
        }
        
        h1 {
            font-size: 2.2rem;
            color: #ffd700;
            text-shadow: 0 0 10px rgba(255, 215, 0, 0.5), 0 0 20px rgba(255, 215, 0, 0.3);
            letter-spacing: 0.1em;
            position: relative;
            z-index: 1;
        }
        
        .subtitle {
            color: #87ceeb;
            margin-top: 0.5rem;
            font-size: 1rem;
            letter-spacing: 0.2em;
            text-shadow: 0 0 8px rgba(135, 206, 235, 0.4);
        }
        
        /* 主容器 */
        .container {
            display: flex;
            flex-wrap: wrap;
            padding: 1.5rem;
            gap: 1.5rem;
            max-width: 1600px;
            margin: 0 auto;
            flex: 1;
        }
        
        /* 左侧输入区 */
        .input-panel {
            flex: 1;
            min-width: 300px;
            background: rgba(15, 15, 20, 0.8);
            border: 1px solid rgba(212, 175, 55, 0.3);
            border-radius: 8px;
            padding: 1rem;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.5), inset 0 0 10px rgba(212, 175, 55, 0.1);
        }
        
        .panel-title {
            color: #ffd700;
            margin-bottom: 1rem;
            padding-bottom: 0.5rem;
            border-bottom: 1px solid rgba(212, 175, 55, 0.2);
            font-size: 1.2rem;
            letter-spacing: 0.1em;
        }
        
        textarea {
            width: 100%;
            height: 400px;
            background: rgba(5, 5, 10, 0.9);
            border: 1px solid rgba(135, 206, 235, 0.3);
            border-radius: 4px;
            padding: 1rem;
            color: #e0e0e0;
            font-size: 1rem;
            resize: vertical;
            outline: none;
            transition: all 0.3s ease;
            font-family: 'Consolas', monospace;
        }
        
        textarea:focus {
            border-color: #87ceeb;
            box-shadow: 0 0 10px rgba(135, 206, 235, 0.3);
        }
        
        /* 右侧预览区 */
        .preview-panel {
            flex: 1.5;
            min-width: 400px;
            background: rgba(15, 15, 20, 0.8);
            border: 1px solid rgba(212, 175, 55, 0.3);
            border-radius: 8px;
            padding: 1rem;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.5), inset 0 0 10px rgba(212, 175, 55, 0.1);
            display: flex;
            flex-direction: column;
        }
        
        #preview-container {
            flex: 1;
            background: rgba(5, 5, 10, 0.95);
            border: 1px solid rgba(135, 206, 235, 0.2);
            border-radius: 4px;
            padding: 1.5rem;
            overflow: auto;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        /* 占位提示 */
        .placeholder {
            text-align: center;
            color: #888;
            padding: 2rem;
            line-height: 1.8;
        }
        
        .placeholder i {
            font-size: 3rem;
            color: rgba(212, 175, 55, 0.3);
            display: block;
            margin-bottom: 1rem;
        }
        
        /* 底部版权 */
        .footer {
            text-align: center;
            padding: 1.5rem;
            border-top: 1px solid rgba(212, 175, 55, 0.3);
            color: #87ceeb;
            font-size: 1rem;
            letter-spacing: 0.2em;
            background: rgba(5, 5, 10, 0.9);
        }
        
        .footer::before {
            content: "✧";
            margin-right: 0.5rem;
            color: #ffd700;
        }
        
        .footer::after {
            content: "✧";
            margin-left: 0.5rem;
            color: #ffd700;
        }
        
        /* 响应式调整 */
        @media (max-width: 768px) {
            .container {
                flex-direction: column;
            }
            
            .input-panel, .preview-panel {
                min-width: 100%;
            }
            
            textarea {
                height: 300px;
            }
            
            h1 {
                font-size: 1.8rem;
            }
        }
        
        /* Mermaid渲染样式适配(修仙风格定制) */
        .mermaid {
            max-width: 100%;
            max-height: 100%;
        }
        
        .mermaid .node rect, 
        .mermaid .node circle, 
        .mermaid .node ellipse,
        .mermaid .node polygon,
        .mermaid .node path {
            fill: #1a1a2e !important;
            stroke: #87ceeb !important;
            stroke-width: 2px !important;
            rx: 8px !important;
            ry: 8px !important;
        }
        
        .mermaid .node text {
            fill: #e0e0e0 !important;
            font-size: 14px !important;
            font-family: '楷体', 'KaiTi', serif !important;
        }
        
        .mermaid .edgePath path,
        .mermaid .edge path {
            stroke: #ffd700 !important;
            stroke-width: 2px !important;
            fill: none !important;
        }
        
        .mermaid .arrowhead path {
            fill: #ffd700 !important;
            stroke: #ffd700 !important;
        }
        
        .mermaid .label text {
            fill: #87ceeb !important;
            font-size: 13px !important;
        }
        
        .mermaid .title {
            fill: #ffd700 !important;
            font-size: 16px !important;
            text-shadow: 0 0 5px rgba(255, 215, 0, 0.3) !important;
        }
        
        /* 隐藏临时渲染容器 */
        #mermaid-temp-container {
            display: none !important;
        }
    </style>
</head>
<body>
    <!-- 头部标题 -->
    <div class="header">
        <h1>未来之窗东方仙盟图标渲染工具</h1>
        <div class="subtitle">以术法之力,绘天地之形</div>
    </div>
    
    <!-- 主内容区 -->
    <div class="container">
        <!-- 左侧输入面板 -->
        <div class="input-panel">
            <div class="panel-title">输入仙盟术法典籍</div>
            <textarea id="mermaid-code" placeholder="在此输入Mermaid图表代码...
示例1(流程图):
graph TD
    A[仙盟总坛] --> B[灵虚殿]
    A --> C[万法阁]
    B --> D[青云峰]
    C --> E[藏经楼]
    D --> F[弟子院]
    E --> F

示例2(时序图):
sequenceDiagram
    弟子->>灵虚殿: 求见掌门
    灵虚殿-->>弟子: 准入
    弟子->>掌门: 汇报修行进度
    掌门-->>弟子: 赐下丹药

示例3(类图):
classDiagram
    class 修仙者 {
        +姓名: string
        +修为: int
        +修炼()
        +渡劫()
    }
    class 掌门 {
        +执掌宗门()
        +传授功法()
    }
    修仙者 <|-- 掌门"></textarea>
        </div>
        
        <!-- 右侧预览面板 -->
        <div class="preview-panel">
            <div class="panel-title">图标显化结果</div>
            <div id="preview-container">
                <div class="placeholder">
                    <i>📜</i>
                    <p>输入术法典籍,即刻显化图标</p>
                    <p style="font-size: 0.9rem; margin-top: 0.5rem;">支持流程图、时序图、类图等多种形制</p>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 底部版权 -->
    <div class="footer">东方仙盟 版权所有</div>
    
    <!-- 临时渲染容器 -->
    <div id="mermaid-temp-container"></div>

    <script>
        // 初始化Mermaid(完全复用原JS逻辑)
        mermaid.initialize({
            startOnLoad: false,
            theme: 'dark',
            themeVariables: {
                primaryColor: '#1a1a2e',
                primaryTextColor: '#e0e0e0',
                primaryBorderColor: '#87ceeb',
                lineColor: '#ffd700',
                secondaryColor: '#2a2a3e',
                tertiaryColor: '#0f0f1a'
            },
            fontFamily: '"楷体", "KaiTi", serif',
            fontSize: '14px',
            arrowMarkerAbsolute: false,
            logLevel: 3,
            securityLevel: 'loose'
        });

        document.addEventListener('DOMContentLoaded', function() {
            const textarea = document.getElementById('mermaid-code');
            const previewContainer = document.getElementById('preview-container');
            const tempContainer = document.getElementById('mermaid-temp-container');
            
            textarea.addEventListener('input', debounce(renderMermaid, 500));
            
            function debounce(func, delay) {
                let timer;
                return function() {
                    clearTimeout(timer);
                    timer = setTimeout(() => func.apply(this, arguments), delay);
                };
            }
            
            async function renderMermaid() {
                const code = textarea.value.trim();
                if (!code) {
                    previewContainer.innerHTML = `
                        <div class="placeholder">
                            <i>📜</i>
                            <p>输入术法典籍,即刻显化图标</p>
                            <p style="font-size: 0.9rem; margin-top: 0.5rem;">支持流程图、时序图、类图等多种形制</p>
                        </div>
                    `;
                    return;
                }
                
                try {
                    tempContainer.innerHTML = '';
                    const chartId = `mermaid-chart-${Date.now()}`;
                    const chartContainer = document.createElement('div');
                    chartContainer.id = chartId;
                    chartContainer.className = 'mermaid';
                    tempContainer.appendChild(chartContainer);
                    
                    const renderResult = await mermaid.render(chartId, code, chartContainer);
                    previewContainer.innerHTML = renderResult.svg;
                    tempContainer.removeChild(chartContainer);
                    
                } catch (error) {
                    let errorMsg = error.message || '未知错误';
                    if (errorMsg.includes('getBoundingClientRect')) {
                        errorMsg = '图表渲染容器初始化失败,请刷新页面重试';
                    } else if (errorMsg.includes('Parse error')) {
                        errorMsg = '语法解析错误,请检查代码格式';
                    }
                    
                    previewContainer.innerHTML = `
                        <div class="placeholder" style="color: #ff6b6b;">
                            <i>⚠️</i>
                            <p>术法典籍有误,无法显化</p>
                            <p style="font-size: 0.9rem; margin-top: 0.5rem;">错误信息:${errorMsg}</p>
                            <p style="font-size: 0.8rem; margin-top: 0.3rem;">请检查语法格式后重试</p>
                        </div>
                    `;
                }
            }
            
            if (textarea.value.trim()) {
                renderMermaid();
            }
        });
    </script>
</body>
</html>

四、运行步骤

  1. 安装 Go 环境 :确保本地安装 Go 1.18+(下载地址:https://golang.org/dl/);

  2. 创建项目目录 :按上述结构创建mermaid-render-go和static目录;

  3. 复制文件 :

    • 将main.go放入根目录;
    • 将修改后的index.html放入static目录;
  4. 启动服务 :

    bash

    运行

    复制代码
    cd mermaid-render-go
    go run main.go
  5. 访问工具 :打开浏览器,输入http://localhost:8080,即可使用(功能与原 HTML 版完全一致)。

五、核心说明

  1. Go 的角色:仅作为 Web 服务器,提供静态文件(HTML/CSS/JS)访问,不参与 Mermaid 渲染逻辑;
  2. 渲染逻辑:仍由前端 JS 完成(复用原 Mermaid 初始化和渲染代码),确保功能一致;
  3. 优势 :
    • 可部署到服务器(支持跨设备访问);
    • 无需依赖本地浏览器打开 HTML 文件;
    • 保持原修仙风格 UI 和交互体验;
  4. 扩展方向 :
    • 若需保存图表,可在 Go 中添加文件存储接口;
    • 若需共享图表,可添加生成临时链接功能;
    • 若需离线使用,可将 Mermaid 库下载到static目录,修改 HTML 中 CDN 路径为本地路径。

阿雪技术观

让我们积极投身于技术共享的浪潮中,不仅仅是作为受益者,更要成为贡献者。无论是分享自己的代码、撰写技术博客,还是参与开源项目的维护和改进,每一个小小的举动都可能成为推动技术进步的巨大力量

Embrace open source and sharing, witness the miracle of technological progress, and enjoy the happy times of humanity! Let's actively join the wave of technology sharing. Not only as beneficiaries, but also as contributors. Whether sharing our own code, writing technical blogs, or participating in the maintenance and improvement of open source projects, every small action may become a huge force driving technological progrss.

相关推荐
行百里er5 小时前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
高频因子挖掘机6 小时前
只取最近 60 个交易日,却每次拉全历史:量化脚本变慢的原因与改法
后端·github·api
孟健7 小时前
从5000亿Token到83%字节匹配:AI逆向工程与重构的工程闭环实践
后端·架构·ai编程
码事漫谈8 小时前
丢得静悄悄,谈一谈 Agent 的记忆是怎么没的
后端
迪飞特科技8 小时前
智慧物业平台实战:Spring Boot 整合小程序实现报修巡检费用全流程
spring boot·后端·小程序
考虑考虑8 小时前
@JsonInclude注解使用
spring boot·后端·spring
IT_陈寒9 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端
网易云信9 小时前
大模型能上岗做销售吗?我们让 12 款国产模型交了 420 份作业
前端·后端
CCblandy9 小时前
HTTP 长连接 3 类故障实测:[WinError 10053]、99 个 TIME_WAIT、69 次多余握手,4 种对策怎么选
后端·python·网络协议·http·云原生·性能优化·架构
蜗牛互联网9 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python