[2024-07-16](UPDATE): 上传页面完成
This commit is contained in:
parent
534466b40b
commit
65a1d849b6
@ -16,7 +16,7 @@ full_bin = "./tmp/main picgo ./deploy/picgo-dev.yml"
|
|||||||
include_dir = []
|
include_dir = []
|
||||||
include_ext = ["go", "html", "js", "css", "yml"]
|
include_ext = ["go", "html", "js", "css", "yml"]
|
||||||
include_file = []
|
include_file = []
|
||||||
kill_delay = "0s"
|
kill_delay = "1s"
|
||||||
log = "build-errors.log"
|
log = "build-errors.log"
|
||||||
poll = false
|
poll = false
|
||||||
poll_interval = 0
|
poll_interval = 0
|
||||||
@ -39,7 +39,7 @@ main_only = false
|
|||||||
time = false
|
time = false
|
||||||
|
|
||||||
[misc]
|
[misc]
|
||||||
clean_on_exit = false
|
clean_on_exit = true
|
||||||
|
|
||||||
[proxy]
|
[proxy]
|
||||||
app_port = 0
|
app_port = 0
|
||||||
|
@ -5,17 +5,41 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TemplateHandler(w http.ResponseWriter, data any, filenames ...string) {
|
// noRender 是一个自定义模板函数,忽略不渲染传入的内容
|
||||||
// 解析模板文件
|
func noRender(s string) template.HTML {
|
||||||
tmpl, err := template.ParseFiles(filenames...)
|
return template.HTML(s)
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染模板并写入响应
|
func TemplateHandler(w http.ResponseWriter, data any, name string, filenames ...string) {
|
||||||
err = tmpl.Execute(w, data)
|
// 创建一个新的基础模板,并将自定义函数注册到模板中
|
||||||
|
baseTmpl := template.New("base").Funcs(template.FuncMap{
|
||||||
|
"noRender": noRender,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加一个简单的内容以确保基础模板不是空的
|
||||||
|
_, err := baseTmpl.Parse(`{{define "base"}}{{end}}`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
Logger.Errorf("Error creating base template: %v", err)
|
||||||
|
http.Error(w, "Error executing template", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析布局模板及其子模板
|
||||||
|
_, err = baseTmpl.ParseFiles(filenames...)
|
||||||
|
if err != nil {
|
||||||
|
Logger.Errorf("Error parsing templates: %v", err)
|
||||||
|
http.Error(w, "Error executing template", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载所有模板文件
|
||||||
|
tmpl, err := baseTmpl.Clone()
|
||||||
|
if err != nil {
|
||||||
|
Logger.Errorf("error cloning base template: %v", err)
|
||||||
|
http.Error(w, "Error executing template", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
err = tmpl.ExecuteTemplate(w, name, data)
|
||||||
|
if err != nil {
|
||||||
|
Logger.Errorf("Error executing template: %v", err)
|
||||||
|
http.Error(w, "Error executing template", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
31
handler/domain.go
Normal file
31
handler/domain.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/csrf"
|
||||||
|
"net/http"
|
||||||
|
"picgo/corelib"
|
||||||
|
"picgo/data"
|
||||||
|
"picgo/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DomainHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == "GET" {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
user model.SysUser
|
||||||
|
)
|
||||||
|
username := r.Context().Value("username").(string)
|
||||||
|
if user, err = data.SysUserGetCacheByUsername(username); err != nil {
|
||||||
|
http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmpData := map[string]interface{}{
|
||||||
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
|
}
|
||||||
|
tmpData["Title"] = "域名管理"
|
||||||
|
tmpData["Active"] = r.URL.Path
|
||||||
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/domain.html")
|
||||||
|
}
|
||||||
|
}
|
@ -4,25 +4,28 @@ import (
|
|||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
"net/http"
|
"net/http"
|
||||||
"picgo/corelib"
|
"picgo/corelib"
|
||||||
|
"picgo/data"
|
||||||
|
"picgo/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
user model.SysUser
|
||||||
|
)
|
||||||
|
username := r.Context().Value("username").(string)
|
||||||
|
if user, err = data.SysUserGetCacheByUsername(username); err != nil {
|
||||||
|
http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
tmpData := map[string]interface{}{
|
tmpData := map[string]interface{}{
|
||||||
csrf.TemplateTag: csrf.TemplateField(r),
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
}
|
}
|
||||||
tmpData["Title"] = "Dashboard"
|
tmpData["Title"] = "Dashboard"
|
||||||
tmpData["Active"] = r.URL.Path
|
tmpData["Active"] = r.URL.Path
|
||||||
//data := struct {
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
// Title string
|
|
||||||
// Active string
|
|
||||||
// //csrfField string
|
|
||||||
//}{
|
|
||||||
// Title: "Dashboard",
|
|
||||||
// Active: r.URL.Path,
|
|
||||||
// //csrfField: string(csrf.TemplateField(r)),
|
|
||||||
//}
|
|
||||||
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
corelib.TemplateHandler(w, tmpData, "view/layout.html", "view/index.html")
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/index.html")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
tmpData := map[string]interface{}{
|
tmpData := map[string]interface{}{
|
||||||
csrf.TemplateTag: csrf.TemplateField(r),
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
}
|
}
|
||||||
corelib.TemplateHandler(w, tmpData, "view/login.html")
|
corelib.TemplateHandler(w, tmpData, "login.html", "view/login.html")
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
loginService(w, r)
|
loginService(w, r)
|
||||||
default:
|
default:
|
||||||
|
32
handler/picture.go
Normal file
32
handler/picture.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/csrf"
|
||||||
|
"net/http"
|
||||||
|
"picgo/corelib"
|
||||||
|
"picgo/data"
|
||||||
|
"picgo/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PictureHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == "GET" {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
user model.SysUser
|
||||||
|
)
|
||||||
|
username := r.Context().Value("username").(string)
|
||||||
|
if user, err = data.SysUserGetCacheByUsername(username); err != nil {
|
||||||
|
http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmpData := map[string]interface{}{
|
||||||
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
|
}
|
||||||
|
tmpData["Title"] = "图片管理"
|
||||||
|
tmpData["Active"] = r.URL.Path
|
||||||
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/picture.html")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"picgo/corelib"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ProfileHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method == "GET" {
|
|
||||||
data := struct {
|
|
||||||
Title string
|
|
||||||
Active string
|
|
||||||
}{
|
|
||||||
Title: "Admin Dashboard",
|
|
||||||
Active: r.URL.Path,
|
|
||||||
}
|
|
||||||
corelib.TemplateHandler(w, data, "view/layout.html", "view/profile.html")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"picgo/corelib"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method == "GET" {
|
|
||||||
data := struct {
|
|
||||||
Title string
|
|
||||||
Active string
|
|
||||||
}{
|
|
||||||
Title: "Admin Dashboard",
|
|
||||||
Active: r.URL.Path,
|
|
||||||
}
|
|
||||||
corelib.TemplateHandler(w, data, "view/layout.html", "view/settings.html")
|
|
||||||
}
|
|
||||||
}
|
|
32
handler/user.go
Normal file
32
handler/user.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/csrf"
|
||||||
|
"net/http"
|
||||||
|
"picgo/corelib"
|
||||||
|
"picgo/data"
|
||||||
|
"picgo/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == "GET" {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
user model.SysUser
|
||||||
|
)
|
||||||
|
username := r.Context().Value("username").(string)
|
||||||
|
if user, err = data.SysUserGetCacheByUsername(username); err != nil {
|
||||||
|
http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmpData := map[string]interface{}{
|
||||||
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
|
}
|
||||||
|
tmpData["Title"] = "用户管理"
|
||||||
|
tmpData["Active"] = r.URL.Path
|
||||||
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/user.html")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"picgo/configs"
|
"picgo/configs"
|
||||||
"picgo/corelib"
|
"picgo/corelib"
|
||||||
"picgo/data"
|
"picgo/data"
|
||||||
|
"picgo/model"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,14 +24,14 @@ func LoginMiddleware(next http.Handler) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
//user model.SysUser
|
user model.SysUser
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if _, err = data.SysUserSelectByUsername(username); err != nil {
|
if user, err = data.SysUserSelectByUsername(username); err != nil {
|
||||||
http.Redirect(w, r, "/login", http.StatusFound)
|
http.Redirect(w, r, "/login", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ctx := context.WithValue(r.Context(), "username", user.Username)
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -24,18 +24,17 @@ func InitRouter() *mux.Router {
|
|||||||
)
|
)
|
||||||
// 创建新的路由器
|
// 创建新的路由器
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
// 处理静态文件
|
// 不需要鉴权路由
|
||||||
//staticDir := "static"
|
|
||||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", handler.StaticHandler()))
|
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", handler.StaticHandler()))
|
||||||
r.HandleFunc("/login", handler.LoginHandler)
|
r.HandleFunc("/login", handler.LoginHandler).Methods(http.MethodGet, http.MethodPost) // 登录
|
||||||
r.HandleFunc("/captcha", handler.CaptchaHandler)
|
r.HandleFunc("/captcha", handler.CaptchaHandler).Methods(http.MethodGet) // 验证码接口
|
||||||
|
// 需要鉴权路由
|
||||||
r.HandleFunc("/settings", handler.SettingsHandler)
|
r.Handle("/", middleware.LoginMiddleware(http.HandlerFunc(handler.IndexHandler))).Methods(http.MethodGet) // 后台首页
|
||||||
r.HandleFunc("/profile", handler.ProfileHandler)
|
r.Handle("/domain", middleware.LoginMiddleware(http.HandlerFunc(handler.DomainHandler))).Methods(http.MethodGet) // 域名管理
|
||||||
r.HandleFunc("/api/v1/upload", handler.UploadFileHandler)
|
r.Handle("/user", middleware.LoginMiddleware(http.HandlerFunc(handler.UserHandler))).Methods(http.MethodGet) // 用户管理
|
||||||
// 路由鉴权
|
r.Handle("/picture", middleware.LoginMiddleware(http.HandlerFunc(handler.PictureHandler))).Methods(http.MethodGet) // 图片管理
|
||||||
r.Handle("/", middleware.LoginMiddleware(http.HandlerFunc(handler.IndexHandler))).Methods(http.MethodGet)
|
r.Handle("/api/v1/upload", middleware.LoginMiddleware(http.HandlerFunc(handler.UploadFileHandler))).Methods(http.MethodPost) // 图片上传接口
|
||||||
// 应用 CORS 中间件
|
// 应用 CORS CSRF 中间件
|
||||||
http.Handle("/", middleware.CorsMiddleware(CSRF(r)))
|
http.Handle("/", middleware.CorsMiddleware(CSRF(r)))
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
45
view/domain.html
Normal file
45
view/domain.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{{define "style"}}
|
||||||
|
{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
|
||||||
|
<div class="alert alert-primary form-inline" role="alert">
|
||||||
|
<button type="button" id="add-domain" class="btn btn-primary" data-toggle="modal"
|
||||||
|
data-target="#DomainModal"><i
|
||||||
|
class="bi bi-plus-lg"></i> 添加域名
|
||||||
|
</button>
|
||||||
|
<div class="input-group mr-3" style="position: absolute; right: 0">
|
||||||
|
<input type="text" id="search-input" class="form-control" placeholder="域名" aria-label="域名" aria-describedby="searc-btn">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-primary" id="search-btn" type="button"><i class="bi bi-search"></i> 搜索
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr class="table-primary">
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col">First</th>
|
||||||
|
<th scope="col">Last</th>
|
||||||
|
<th scope="col">Handle</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">1</th>
|
||||||
|
<td>Mark</td>
|
||||||
|
<td>Otto</td>
|
||||||
|
<td>@mdo</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">2</th>
|
||||||
|
<td>Jacob</td>
|
||||||
|
<td>Thornton</td>
|
||||||
|
<td>@fat</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{end}}
|
||||||
|
{{define "script"}}
|
||||||
|
{{end}}
|
@ -9,7 +9,7 @@
|
|||||||
<div class="card-body" style="border-style:dashed; border-color:#98bf21; display: block">
|
<div class="card-body" style="border-style:dashed; border-color:#98bf21; display: block">
|
||||||
<input type="file" class="form-control-file" id="upload-input-file" style="display:none;">
|
<input type="file" class="form-control-file" id="upload-input-file" style="display:none;">
|
||||||
<div id="upload-btn-click">
|
<div id="upload-btn-click">
|
||||||
<div style="height: 100px"></div>
|
<div style="height: 80px"></div>
|
||||||
<div class="ant-upload-drag-container">
|
<div class="ant-upload-drag-container">
|
||||||
<p class="ant-upload-drag-icon"><img src="/static/img/add.svg"></p>
|
<p class="ant-upload-drag-icon"><img src="/static/img/add.svg"></p>
|
||||||
<p class="ant-upload-text" style="font-size: 14px; margin: 0;">拖拽文件到此区域或<span
|
<p class="ant-upload-text" style="font-size: 14px; margin: 0;">拖拽文件到此区域或<span
|
||||||
@ -17,33 +17,44 @@
|
|||||||
<p class="ant-upload-hint" style="font-size: 12px; margin: 4px 0 0;">
|
<p class="ant-upload-hint" style="font-size: 12px; margin: 4px 0 0;">
|
||||||
只支持上传图片文件,最大支持2M,上传后支持复制url</p>
|
只支持上传图片文件,最大支持2M,上传后支持复制url</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 100px"></div>
|
<div style="height: 80px"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-muted" style="background-color: #fcf8e3">
|
<div class="card-footer text-muted" style="background-color: #fcf8e3">
|
||||||
<div id="preview"></div>
|
<div class="container">
|
||||||
<div class="card" style="width: 18rem;">
|
<div class="row" id="preview">
|
||||||
<img src="https://a.520gexing.com/uploads/allimg/2023092607/z52gncuk0ei.jpg" class="card-img-top" alt="">
|
<div class="col-sm">
|
||||||
|
<div class="card" style="width: 14rem;">
|
||||||
|
<img src="http://img.520touxiang.com/uploads/allimg/2016063019/4b1sswvy0mj.jpg" class="card-img-top" alt="">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>https://a.520gexing.com/uploads/allimg/2023092607/z52gncuk0ei.jpg</p>
|
<p>http://img.520touxiang.com/uploads/allimg/2016063019/4b1sswvy0mj.jpg</p>
|
||||||
|
<button type="button" class="btn btn-primary img-copy-url-btn">复制链接</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="card" style="width: 14rem;">
|
||||||
|
<img src="http://img.520touxiang.com/uploads/allimg/2016063019/4b1sswvy0mj.jpg" class="card-img-top" alt="">
|
||||||
|
<div class="card-body">
|
||||||
|
<p>http://img.520touxiang.com/uploads/allimg/2016063019/4b1sswvy0mj.jpg</p>
|
||||||
<button type="button" class="btn btn-primary img-copy-url-btn">复制链接</button>
|
<button type="button" class="btn btn-primary img-copy-url-btn">复制链接</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "script"}}
|
{{define "script"}}
|
||||||
{{/*
|
{{noRender `<script id="tpl-image" type="text/html">
|
||||||
<script id="tpl-image" type="text/html">
|
|
||||||
<div class="card" style="width: 18rem;">
|
<div class="card" style="width: 18rem;">
|
||||||
<img src="{{ url }}" class="card-img-top" alt="...">
|
<img src="{{ url }}" class="card-img-top" alt="...">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<button type="button" class="btn btn-primary">点击复制按钮</button>
|
<button type="button" class="btn btn-primary">点击复制按钮</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script> */}}
|
</script>`}}
|
||||||
<script src="/static/js/index.js"></script>
|
<script src="/static/js/index.js"></script>
|
||||||
{{end}}
|
{{end}}
|
@ -20,33 +20,44 @@
|
|||||||
<div class="container" style="padding-left: 0; padding-right: 0">
|
<div class="container" style="padding-left: 0; padding-right: 0">
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
<a class="navbar-brand" href="/">后台管理</a>
|
<a class="navbar-brand" href="/">后台管理</a>
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
|
||||||
|
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse pl-5" id="navbarNav">
|
<div class="collapse navbar-collapse pl-5" id="navbarNav">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item active">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/user">用户管理</a>
|
<a class="nav-link {{if eq .Active "/"}}active{{end}}" href="/">图片上传</a>
|
||||||
|
</li>
|
||||||
|
{{ if .IsSuper }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{if eq .Active "/user"}}active{{end}}" href="/user">用户管理</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/domain">域名管理</a>
|
<a class="nav-link {{if eq .Active "/domain"}}active{{end}}" href="/domain">域名管理</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/picture">图片管理</a>
|
<a class="nav-link {{if eq .Active "/picture"}}active{{end}}" href="/picture">图片管理</a>
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/upload">图片上传</a>
|
|
||||||
</li>
|
</li>
|
||||||
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container" style="padding: 20px">
|
||||||
<div class="container">
|
|
||||||
{{ .csrfField }}
|
{{ .csrfField }}
|
||||||
{{template "content" .}}
|
{{template "content" .}}
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
let pathname = window.location.pathname
|
||||||
|
let links = $("nav-link");
|
||||||
|
for (let i = 0; i < links.length; i++) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
{{template "script" .}}
|
{{template "script" .}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
10
view/picture.html
Normal file
10
view/picture.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{{define "style"}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<h1 class="h2">Picture</h1>
|
||||||
|
<p>Picture page content goes here.</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "script"}}
|
||||||
|
{{end}}
|
@ -1,4 +0,0 @@
|
|||||||
{{define "content"}}
|
|
||||||
<h1 class="h2">Profile</h1>
|
|
||||||
<p>Profile page content goes here.</p>
|
|
||||||
{{end}}
|
|
@ -1,4 +0,0 @@
|
|||||||
{{define "content"}}
|
|
||||||
<h1 class="h2">Settings</h1>
|
|
||||||
<p>Settings page content goes here.</p>
|
|
||||||
{{end}}
|
|
10
view/user.html
Normal file
10
view/user.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{{define "style"}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<h1 class="h2">User</h1>
|
||||||
|
<p>User page content goes here.</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "script"}}
|
||||||
|
{{end}}
|
Loading…
Reference in New Issue
Block a user