feat: 整合swagger-ui文档主题, 并优化文档页面渲染

This commit is contained in:
2026-01-08 12:18:07 +08:00
parent e673ee2479
commit c18887d5a7
22 changed files with 22239 additions and 10 deletions

2
go.mod
View File

@@ -6,7 +6,7 @@ toolchain go1.24.2
require (
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20260107152122-a877079712ee
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260108031051-bf46e2083c84
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260108040723-8d5895324da5
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251207071238-9aa24be3d708
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4

2
go.sum
View File

@@ -2,6 +2,8 @@ git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20260107152122-a877079712ee h1:cUr
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20260107152122-a877079712ee/go.mod h1:EO06OJ2rIS+s3CaXoGiX6yTuyhKFeSrgHukOY7FofHw=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260108031051-bf46e2083c84 h1:1+erCrsFvz5QuHKdraVuH7KmokUoqJULaKpWXMT6m3Y=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260108031051-bf46e2083c84/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260108040723-8d5895324da5 h1:v02Z5/rLXxRUGMZLBPgOnL0hHN36bbH1tGTC+40pF1E=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260108040723-8d5895324da5/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251207071238-9aa24be3d708 h1:yVr38JAgPwS/6JeYdHLDa8PXU3fTa2dnENL1JGdu3ns=
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251207071238-9aa24be3d708/go.mod h1:iwegDT/6mX2INyxMAYxsL9pZdpEixiu1GdpHIG2It1k=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=

View File

@@ -11,6 +11,7 @@ import (
"testing"
"time"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/rate_limit"
"github.com/gin-gonic/gin"
)
@@ -19,7 +20,7 @@ func TestNewServer(t *testing.T) {
s := NewServer(9087, WithRateLimitInstance(rate_limit.MemoryClient), WithDocConfig(&DocConfig{
Enable: true,
UiTheme: "elements",
UiTheme: consts.SwaggerUIThemeSwaggerUI.String(),
BaseUri: "",
Flag: "test-server",
ServerList: nil,

View File

@@ -8,6 +8,7 @@
package router
import (
"embed"
"net/http"
"path/filepath"
"strings"
@@ -46,21 +47,23 @@ func (su *SwaggerUI) Handler() func(ctx *gin.Context) {
// YDoc-Lucky-UI 主题处理
return su.HandleLuckyUI()
case consts.SwaggerUIThemeDefault:
return su.HandleSwaggerUI()
return su.HandleSwaggerDefaultUI()
case consts.SwaggerUIThemeRedocFree:
// redoc免费版
return su.HandleRedocFreeUI()
case consts.SwaggerUIThemeElements:
return su.HandleElements()
return su.renderWebPage(theme.ElementsFiles, su.uiTheme)
case consts.SwaggerUIThemeSwaggerUI:
return su.renderWebPage(theme.SwaggerUIFiles, su.uiTheme)
default:
return su.HandleSwaggerUI()
return su.HandleSwaggerDefaultUI()
}
}
func (su *SwaggerUI) HandleElements() gin.HandlerFunc {
func (su *SwaggerUI) renderWebPage(themeFiles embed.FS, themeUI consts.SwaggerUITheme) func(ctx *gin.Context) {
return func(ctx *gin.Context) {
fileRealPath := strings.TrimPrefix(ctx.Request.RequestURI, su.baseUri)
byteData, _ := theme.ElementsFiles.ReadFile(filepath.Join("elements", fileRealPath))
byteData, _ := themeFiles.ReadFile(filepath.Join(themeUI.String(), fileRealPath))
if strings.Contains(ctx.Request.RequestURI, ".html") {
byteData = []byte(strings.ReplaceAll(string(byteData), "{{BASE_URI}}", su.baseUri))
byteData = []byte(strings.ReplaceAll(string(byteData), "{{DOC_TITLE}}", su.docTitle))
@@ -73,7 +76,7 @@ func (su *SwaggerUI) HandleElements() gin.HandlerFunc {
uriArr := strings.Split(ctx.Request.RequestURI, ".")
contentType := "text/" + uriArr[len(uriArr)-1]
if strings.HasSuffix(ctx.Request.RequestURI, "png") {
contentType = "image/png"
contentType = consts.MimeTypeImagePng
} else if strings.HasSuffix(ctx.Request.RequestURI, "js") {
contentType = "application/javascript"
}
@@ -116,8 +119,8 @@ func (su *SwaggerUI) HandleKnife4goUI() func(ctx *gin.Context) {
return knife4goGin.WrapHandler(knife4goFiles.Handler, resetOption)
}
// HandleSwaggerUI ...
func (su *SwaggerUI) HandleSwaggerUI() func(ctx *gin.Context) {
// HandleSwaggerDefaultUI ...
func (su *SwaggerUI) HandleSwaggerDefaultUI() func(ctx *gin.Context) {
return ginSwagger.WrapHandler(swaggerFiles.Handler)
}

View File

@@ -17,3 +17,6 @@ var RedocFreeIndexContent string
//go:embed elements/*
var ElementsFiles embed.FS
//go:embed swagger-ui/*
var SwaggerUIFiles embed.FS

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{{DOC_TITLE}}</title>
<!-- 默认主题 -->
<link href="3.x/theme-material.css" id="swagger-theme" rel="stylesheet" type="text/css">
<link href="swagger-ui.css" rel="stylesheet" type="text/css">
<style>
.theme-selector {
position: fixed;
top: 20px;
right: 20px;
z-index: 100;
background: white;
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
select {
padding: 5px;
border-radius: 3px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="theme-selector">
<label for="theme-select">选择主题: </label>
<select id="theme-select" onchange="changeTheme(this.value)">
<option value="theme-default">默认</option>
<option value="3.x-theme-material">3.x-Material</option>
<option value="3.x-theme-monokai">3.x-Monokai</option>
<option value="3.x-theme-feeling-blue">3.x-Feeling Blue</option>
<option value="3.x-theme-flattop">3.x-Flattop</option>
<option value="3.x-theme-newspaper">3.x-Newspaper</option>
<option value="3.x-theme-outline">3.x-Outline</option>
<option value="2.x-theme-felling-blue.css">2.x-theme-felling-blue.css</option>
<option value="2.x-theme-flattop.css">2.x-theme-flattop.css</option>
<option value="2.x-theme-material.css">2.x-theme-material.css</option>
<option value="2.x-theme-monokai.css">2.x-theme-monokai.css</option>
<option value="2.x-theme-muted.css">2.x-theme-muted.css</option>
<option value="2.x-theme-newspaper.css">2.x-theme-newspaper.css</option>
<option value="2.x-theme-outline.css">2.x-theme-outline.css</option>
</select>
</div>
<div id="swagger-ui"></div>
<script src="swagger-ui-bundle.js"></script>
<script src="swagger-ui-standalone-preset.js"></script>
<script>
// 主题映射表
const themes = {
'theme-default': '',
'3.x-theme-material': '3.x/theme-material.css',
'3.x-theme-monokai': '3.x/theme-monokai.css',
'3.x-theme-feeling-blue': '3.x/theme-feeling-blue.css',
'3.x-theme-flattop': '3.x/theme-flattop.css',
'3.x-theme-newspaper': '3.x/theme-newspaper.css',
'3.x-theme-outline': '3.x/theme-outline.css',
'2.x-theme-felling-blue.css': '2.x/theme-felling-blue.css',
'2.x-theme-flattop.css': '2.x/theme-felling-flattop.css',
'2.x-theme-material.css': '2.x/theme-material.css',
'2.x-theme-monokai.css': '2.x/theme-monokai.css',
'2.x-theme-muted.css': '2.x/theme-muted.css',
'2.x-theme-newspaper.css': '2.x/theme-newspaper.css',
'2.x-theme-outline.css': '2.x/theme-outline.css',
};
// 切换主题函数
function changeTheme(themeName) {
const themeLink = document.getElementById('swagger-theme');
if (themeName === 'theme-default') {
themeLink.href = themes['3.x-theme-material']; // 使用默认主题
} else {
themeLink.href = themes[themeName];
}
// 保存到本地存储
localStorage.setItem('swagger-theme', themeName);
}
// 页面加载时恢复主题
window.onload = function() {
// 从本地存储恢复主题
const savedTheme = localStorage.getItem('swagger-theme') || 'theme-material';
document.getElementById('theme-select').value = savedTheme;
if (savedTheme !== 'theme-default') {
document.getElementById('swagger-theme').href = themes[savedTheme];
}
// 初始化 Swagger UI
window.ui = SwaggerUIBundle({
url: "{{DOC_PATH}}", // 你的 API 文档地址
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
filter: true,
defaultModelsExpandDepth: 1,
defaultModelExpandDepth: 1
});
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long