chanjs 2.8.10 → 2.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "chanjs",
4
- "version": "2.8.10",
4
+ "version": "2.8.11",
5
5
  "description": "chanjs基于 Node.js + Express 5 的标准 HMVC 框架(NHMVC),纯 JavaScript(ESM)开发。",
6
6
  "main": "index.js",
7
7
  "module": "index.js",
package/utils/index.js CHANGED
@@ -44,5 +44,5 @@ export { tree, treeById } from "./tree.js";
44
44
  export { filterFields } from "./filter.js";
45
45
 
46
46
  // 分页工具
47
- export { pages } from "./pages.js";
47
+ export { pages, parsePage, parsePageSize, pageHtml } from "./pages.js";
48
48
  export { escapeLike } from "./escape.js";
package/utils/pages.js CHANGED
@@ -4,6 +4,73 @@
4
4
  * 样式由项目 CSS(如 .blince-pagination)统一维护,不依赖 Tailwind 等第三方框架
5
5
  */
6
6
 
7
+ /**
8
+ * 解析页码:非数字/0/负数统一回落 1,防 NaN 或负数穿透进 SQL LIMIT/OFFSET
9
+ * (?page=abc 这类脏值若直接 parseInt 后进查询,NaN 会让 knex LIMIT 报错)
10
+ * @param {*} value 页码来源(req.query / req.params 原始值)
11
+ * @returns {number} 至少为 1 的整数页码
12
+ */
13
+ export function parsePage(value) {
14
+ return Math.max(1, parseInt(value, 10) || 1);
15
+ }
16
+
17
+ /**
18
+ * 解析每页条数:非数字回落 fallback,并钳制 [1, max] 区间防大值拖垮 DB
19
+ * @param {*} value pageSize 来源(req.query / req.params 原始值)
20
+ * @param {number} [fallback=10] 非法/缺省时的默认条数
21
+ * @param {number} [max=100] 上限(来自公开 query 的值必须有上限)
22
+ * @returns {number} 钳制后的整数条数
23
+ */
24
+ export function parsePageSize(value, fallback = 10, max = 100) {
25
+ return Math.min(Math.max(parseInt(value, 10) || fallback, 1), max);
26
+ }
27
+
28
+ /**
29
+ * 渲染 query 式分页 HTML(?current=N,保留筛选参数)
30
+ * 与 pages() 的分工:pages() 面向 /list-2.html 路径式分页(SEO 静态 URL,带省略号);
31
+ * 本函数面向带筛选条件的列表页——URLSearchParams 自动编码参数,天然防注入
32
+ * @param {Object} opts
33
+ * @param {string} opts.baseUrl 基础路径(如 /blog.html)
34
+ * @param {Object} [opts.query] 当前 URL query 对象,翻页时原样保留(剔除 current)
35
+ * @param {number} opts.total 总记录数
36
+ * @param {number} [opts.pageSize=10] 每页条数
37
+ * @param {number} [opts.current=1] 当前页码
38
+ * @param {string} [opts.unit="篇"] 统计文案量词(篇/家/条…)
39
+ * @param {number} [opts.window=2] 当前页前后各显示页数(窗口共 2*window+1 页)
40
+ * @returns {string} 总页数 ≤1 时返回空串
41
+ */
42
+ export function pageHtml({ baseUrl, query, total, pageSize = 10, current = 1, unit = "篇", window = 2 }) {
43
+ total = parseInt(total) || 0;
44
+ current = parsePage(current);
45
+ pageSize = parsePageSize(pageSize);
46
+ const totalPage = Math.max(1, Math.ceil(total / pageSize));
47
+ if (totalPage <= 1) return "";
48
+
49
+ const base = new URLSearchParams();
50
+ Object.entries(query || {}).forEach(([k, v]) => {
51
+ if (k === "current" || v === undefined || v === null || v === "") return;
52
+ base.set(k, v);
53
+ });
54
+ const link = (n) => {
55
+ const p = new URLSearchParams(base);
56
+ if (n > 1) p.set("current", n);
57
+ const s = p.toString();
58
+ return `${baseUrl}${s ? "?" + s : ""}`;
59
+ };
60
+ const pageLink = (n) =>
61
+ n === current ? `<span class="current">${n}</span>` : `<a href="${link(n)}">${n}</a>`;
62
+
63
+ let html = "";
64
+ if (current > 1) html += `<a href="${link(current - 1)}">上一页</a>`;
65
+ let start = Math.max(1, current - window);
66
+ let end = Math.min(totalPage, start + window * 2);
67
+ start = Math.max(1, end - window * 2);
68
+ for (let i = start; i <= end; i++) html += pageLink(i);
69
+ if (current < totalPage) html += `<a href="${link(current + 1)}">下一页</a>`;
70
+ html += `<span class="disabled">共 ${total} ${unit} / ${totalPage} 页</span>`;
71
+ return html;
72
+ }
73
+
7
74
  /**
8
75
  * 渲染分页HTML
9
76
  * @param {number} current 当前页码