From 09cfc6ca1c86eb77e6d151d30a21e9317103f7aa Mon Sep 17 00:00:00 2001 From: SunYF <123@hard_man.com> Date: Tue, 22 Sep 2026 11:43:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(bj=5Fpower=5Fmes):=20=E5=B5=8C=E5=85=A5?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E9=9D=99=E6=80=81=E8=B5=84=E6=BA=90=E5=B9=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AESPA=E8=B7=AF=E7=94=B1=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现将前端构建产物打包进二进制文件,同时配置兜底路由支持Vue Router的history模式,让8888端口可以同时提供API服务和前端页面访问 --- bj_power_mes/bj_power_mes.go | 48 +++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/bj_power_mes/bj_power_mes.go b/bj_power_mes/bj_power_mes.go index 052abfc..e4aa2ea 100644 --- a/bj_power_mes/bj_power_mes.go +++ b/bj_power_mes/bj_power_mes.go @@ -4,9 +4,13 @@ import ( "bufio" "context" "crypto/subtle" + "embed" "flag" "fmt" + "io/fs" + "net/http" "os" + "path" "strings" "bj_power_mes/common/logx" @@ -22,6 +26,12 @@ import ( "github.com/zeromicro/go-zero/rest" ) +// 前端静态资源(Vue 构建产物:frontend/ npm run build 输出到 public/)。 +// 8888 一个端口同时提供管理后台页面与 API。 +// +//go:embed all:public +var webFS embed.FS + var configFile = flag.String("f", "etc/bj_power_mes-api.yaml", "the config file") // promptPassword 从终端读取一行输入(不隐藏回显,用于运维终端操作) @@ -116,6 +126,30 @@ func runResetAll(c config.Config) { fmt.Println("reset-all: 已清空并重建全部表,写入基础数据(管理员 admin/123456)。") } +// newStaticHandler 嵌入式静态文件:命中则直接返回;未命中的非 /api 路径 +// 回退到 index.html(SPA history 路由),由浏览器端 Vue Router 接管。 +func newStaticHandler(fsys embed.FS) http.Handler { + sub, err := fs.Sub(fsys, "public") + if err != nil { + panic(fmt.Sprintf("嵌入静态目录不可用: %v", err)) + } + fileServer := http.FileServer(http.FS(sub)) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + name := strings.TrimPrefix(path.Clean(r.URL.Path), "/") + if name == "" { + name = "index.html" + } + if f, openErr := sub.Open(name); openErr == nil { + f.Close() + fileServer.ServeHTTP(w, r) + return + } + r2 := r.Clone(r.Context()) + r2.URL.Path = "/" + fileServer.ServeHTTP(w, r2) + }) +} + func main() { flag.Parse() @@ -158,7 +192,19 @@ func main() { os.Exit(1) } - server := rest.MustNewServer(c.RestConf) + staticHandler := newStaticHandler(webFS) + + // 兜底路由:已注册的 /api/* 走正常匹配;未注册的非 /api 路径走嵌入式静态资源 + //(/api 下未注册的路径仍返回 404,不回退页面) + notFound := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.URL.Path, "/api/") || r.URL.Path == "/api" { + http.NotFound(w, r) + return + } + staticHandler.ServeHTTP(w, r) + }) + + server := rest.MustNewServer(c.RestConf, rest.WithNotFoundHandler(notFound)) defer server.Stop() ctx := svc.NewServiceContext(c)