package admin import ( "embed" "io/fs" "net/http" "strings" "github.com/gin-gonic/gin" ) //go:embed all:dist var distFS embed.FS // Register 把内嵌的后台 SPA 挂到根路径,保留 /api 给业务接口。 func Register(r *gin.Engine) { sub, err := fs.Sub(distFS, "dist") if err != nil { panic(err) } fileServer := http.FileServer(http.FS(sub)) handler := func(c *gin.Context) { reqPath := strings.TrimPrefix(c.Request.URL.Path, "/") // 若请求的是真实存在的静态资源,直出;否则回退 index.html(前端路由) if reqPath != "" { if f, err := sub.Open(reqPath); err == nil { _ = f.Close() c.Request.URL.Path = "/" + reqPath fileServer.ServeHTTP(c.Writer, c.Request) return } if strings.HasPrefix(reqPath, "assets/") { c.Status(http.StatusNotFound) return } } // SPA fallback data, err := fs.ReadFile(sub, "index.html") if err != nil { c.String(http.StatusInternalServerError, "admin dist missing") return } c.Data(http.StatusOK, "text/html; charset=utf-8", data) } r.GET("/", handler) r.NoRoute(func(c *gin.Context) { if (c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead) || strings.HasPrefix(c.Request.URL.Path, "/api/") || c.Request.URL.Path == "/api" { c.Status(http.StatusNotFound) return } handler(c) }) }