补全页加分页,每页 30 行

218 行在手机上堆成一屏一行有五万多像素长,分页后第 1 页只有八千多。

页码会夹紧:?page=999 停在最后一页而不是给空白页,解析不出来回第一页。
提交后留在同一页——处理完的行会从列表里消失,停在这儿才看得见剩下的。
翻页前若这一页有改过还没提交的下拉,先确认一次(预选的建议不算「改过」)。
This commit is contained in:
tamakyi
2026-09-19 18:25:16 +08:00
parent b311ba202b
commit 824c1e3b3d
3 changed files with 152 additions and 6 deletions
+53 -5
View File
@@ -3,7 +3,9 @@ package web
import (
"context"
"fmt"
"html/template"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
@@ -82,6 +84,21 @@ type FillupData struct {
Categories []fillupCategory // 类别下拉选项
Total int
WithHint int
// 分页。218 行在手机上堆成一屏一行有五万多像素长,一页 30 行刚好。
PageNo int
PageCount int
}
// fillupPerPage 是补全页每页的行数。
const fillupPerPage = 30
// PageLink 生成带页码的链接,供模板里的上一页/下一页用。
func (d FillupData) PageLink(no int) template.URL {
if no < 1 {
no = 1
}
return template.URL("/admin/fillup?page=" + strconv.Itoa(no))
}
// activityOption 只是给下拉用的(活动 id + 名字)。
@@ -189,13 +206,26 @@ func (s *Server) adminFillupPage(w http.ResponseWriter, r *http.Request) {
s.serverError(w, r, err)
return
}
d := FillupData{Page: p, Rows: rows, Activities: acts, Categories: fillupCategories(), Total: len(rows)}
total := len(rows)
hinted := 0
for _, row := range rows {
if row.ActHint != "" || row.CatHint != "" {
d.WithHint++
hinted++
}
}
s.render(w, "admin/fillup", d)
pageCount := (total + fillupPerPage - 1) / fillupPerPage
pageNo := clampPage(r.URL.Query().Get("page"), pageCount)
from := (pageNo - 1) * fillupPerPage
to := from + fillupPerPage
if to > total {
to = total
}
rows = rows[from:to]
s.render(w, "admin/fillup", FillupData{
Page: p, Rows: rows, Activities: acts, Categories: fillupCategories(),
Total: total, WithHint: hinted, PageNo: pageNo, PageCount: pageCount,
})
}
// activityOptions 列出该数据集的活动,供下拉选择。
@@ -451,7 +481,25 @@ func (s *Server) adminFillupAmap(w http.ResponseWriter, r *http.Request) {
s.fillupFlash(w, r, "ok", msg)
}
// fillupFlash 把提示带回补全页本身(adminFlash 是跳后台首页的)。
// fillupFlash 把提示带回补全页本身(adminFlash 是跳后台首页的),并保留当前页码——
// 处理完的那几行会从列表里消失,停在同一页才看得见剩下的。
func (s *Server) fillupFlash(w http.ResponseWriter, r *http.Request, kind, msg string) {
http.Redirect(w, r, "/admin/fillup?"+kind+"="+urlQueryEscape(msg), http.StatusSeeOther)
q := url.Values{}
q.Set(kind, msg)
if pg := strings.TrimSpace(r.PostFormValue("page")); pg != "" {
q.Set("page", pg)
}
http.Redirect(w, r, "/admin/fillup?"+q.Encode(), http.StatusSeeOther)
}
// clampPage 解析页码并夹到有效范围;解析不出来就回第一页。
func clampPage(raw string, pageCount int) int {
no, err := strconv.Atoi(strings.TrimSpace(raw))
if err != nil || no < 1 {
no = 1
}
if pageCount > 0 && no > pageCount {
no = pageCount
}
return no
}