@_tc/template-core 0.2.0-bate.1 → 0.2.0-bate.2

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/AGENT_README.md CHANGED
@@ -367,10 +367,13 @@ import '@_tc/template-core/fe/tailwind_ui.css'
367
367
 
368
368
  ```ts
369
369
  import {
370
+ clearRafTimer,
370
371
  api,
371
372
  get,
372
373
  post,
373
374
  request,
375
+ rafSetInterval,
376
+ rafSetTimeout,
374
377
  schemaEventBus,
375
378
  schemaStore,
376
379
  useSchemaStore,
@@ -401,6 +404,7 @@ import { Button, DataTable, Form, Input, Modal, Select } from '@_tc/template-cor
401
404
  常用前端能力:
402
405
 
403
406
  - 请求方法:`api`、`request`、`get`、`post`、`put`、`patch`、`del`。
407
+ - RAF 计时器:`rafSetTimeout`、`rafSetInterval`、`rafClearTimeout`、`rafClearInterval`、`clearRafTimer`。
404
408
  - 请求类型:`BaseResponse`、`PageParams`、`PageResponse`、`RequestConfig`、`ResponseConfig`、`AxiosError`。
405
409
  - 请求 header:自动添加 `s_t`、`s_sign`、`projk`;存在短 token 时添加 `Authorization: Bearer ${token}`。
406
410
  - 本地 key:短 token 是 `localStorage.auth_token`,项目 key 是 `localStorage.p_J_k`;刷新 token header 默认名是 `RT`。
@@ -410,6 +414,7 @@ import { Button, DataTable, Form, Input, Modal, Select } from '@_tc/template-cor
410
414
  - 多语言:`i18nStore.getState().addResources()`,配置文案可写 `$i18n::...`。
411
415
  - 主题:`ThemeSwitch` 会同步根节点 `dark` class 和 `localStorage`。
412
416
  - `Input type="password"` 会自动显示密码显隐按钮;`allowClear` 不作用于密码输入。
417
+ - RAF 计时器只在浏览器环境可用,不适合 Node 侧调用。
413
418
 
414
419
  不要直接依赖 `frontend/src/...`、`packages/react/ui/...` 这类包内部路径。
415
420
 
@@ -1 +1 @@
1
- const e=require(`../../packages/common/string/index.js`);var t=e=>JSON.stringify(e).replace(/</g,`\\u003c`).replace(/>/g,`\\u003e`).replace(/&/g,`\\u0026`).replace(/\u2028/g,`\\u2028`).replace(/\u2029/g,`\\u2029`),n=(n=>class{async renderPage(r,i){let a=r.path;if(n.extends.localCacheHtmlEtag.keepFreshFEBuildKey(),a.includes(`${n.options.apiPrefix}/`))await i();else{n.extends.logger.log(` - render page: `+r.params.page);try{let i=r.query.projk;Array.isArray(i)&&(i=e.joinStr(...i));let a={projKey:i??``,name:n.options?.name??``,env:n.envs.get(),basePath:t(`${n.options.pageBasePage}${r.params.page}`),projKeyJson:t(String(r.query.projk??``)),signKey:t(n.config.signKey),options:t(n.options)},o=n.extends.renderView(`${r.params.page}.entry.tpl`,r,a),s=e.joinStr(a.basePath,a.name,a.env,a.projKey);if(!n.extends.localCacheHtmlEtag.hasEtag(s)){let e=n.extends.crypto.hmacSign(o);n.extends.localCacheHtmlEtag.setEtag(s,e)}let c=n.extends.localCacheHtmlEtag.getEtag(s);c||console.log(`---- etag 缓存异常 请检查服务 ----`),r.etag=c??Date.now()+``}catch(e){console.log(`-------------- render view error --------------`),console.log(e),n.extends.generateErrorMessage(`template not found`,{status:404,showError:!0,code:0})}}}});module.exports=n;
1
+ const e=require(`../../packages/common/string/index.js`);var t=e=>JSON.stringify(e).replace(/</g,`\\u003c`).replace(/>/g,`\\u003e`).replace(/&/g,`\\u0026`).replace(/\u2028/g,`\\u2028`).replace(/\u2029/g,`\\u2029`),n=(n=>class{async renderPage(r,i){let a=r.path;if(n.extends.localCacheHtmlEtag.keepFreshFEBuildKey(),a.includes(`${n.options.apiPrefix}/`))await i();else{n.extends.logger.log(` - render page: `+r.params.page);try{let i=r.query.projk;Array.isArray(i)&&(i=e.joinStr(...i));let a={projKey:i??``,name:n.options?.name??``,env:n.envs.get(),basePath:t(`${n.options.pageBasePage}${r.params.page}`),projKeyJson:t(String(r.query.projk??``)),signKey:t(n.config.signKey),options:t(n.options)},o=n.extends.renderView(`${r.params.page}.entry.tpl`,r,a),s=e.joinStr(a.basePath,a.name,a.env,a.projKey);if(!n.extends.localCacheHtmlEtag.hasEtag(s)){let e=n.extends.crypto.hmacSign(o);n.extends.localCacheHtmlEtag.setEtag(s,e)}let c=n.extends.localCacheHtmlEtag.getEtag(s);if(c||console.log(`---- etag 缓存异常 请检查服务 ----`),r.etag=c??Date.now()+``,r.fresh){r.status=304;return}}catch(e){console.log(`-------------- render view error --------------`),console.log(e),n.extends.generateErrorMessage(`template not found`,{status:404,showError:!0,code:0})}}}});module.exports=n;
@@ -23,7 +23,10 @@ var t = (e) => JSON.stringify(e).replace(/</g, "\\u003c").replace(/>/g, "\\u003e
23
23
  n.extends.localCacheHtmlEtag.setEtag(s, e);
24
24
  }
25
25
  let c = n.extends.localCacheHtmlEtag.getEtag(s);
26
- c || console.log("---- etag 缓存异常 请检查服务 ----"), r.etag = c ?? Date.now() + "";
26
+ if (c || console.log("---- etag 缓存异常 请检查服务 ----"), r.etag = c ?? Date.now() + "", r.fresh) {
27
+ r.status = 304;
28
+ return;
29
+ }
27
30
  } catch (e) {
28
31
  console.log("-------------- render view error --------------"), console.log(e), n.extends.generateErrorMessage("template not found", {
29
32
  status: 404,
@@ -0,0 +1,7 @@
1
+ export type RafTimerId = number;
2
+ export type RafTimerCallback<Args extends unknown[] = []> = (...args: Args) => void;
3
+ export declare const rafSetTimeout: <Args extends unknown[]>(callback: RafTimerCallback<Args>, delay?: number, ...args: Args) => RafTimerId;
4
+ export declare const rafClearTimeout: (timerId: RafTimerId) => void;
5
+ export declare const rafSetInterval: <Args extends unknown[]>(callback: RafTimerCallback<Args>, delay?: number, ...args: Args) => RafTimerId;
6
+ export declare const rafClearInterval: (timerId: RafTimerId) => void;
7
+ export declare const clearRafTimer: (timerId: RafTimerId) => void;
@@ -0,0 +1,144 @@
1
+ //#region frontend/src/common/rafTimer.ts
2
+ var rafTimers = /* @__PURE__ */ new Map();
3
+ var rafTimerSeed = 0;
4
+ /**
5
+ * 统一修正延迟时间,避免传入 undefined 或负数。
6
+ *
7
+ * @param delay 原始延迟时间。
8
+ * @returns 不小于 0 的延迟时间。
9
+ */
10
+ var normalizeDelay = (delay) => Math.max(0, delay ?? 0);
11
+ /**
12
+ * 确认当前环境支持 requestAnimationFrame。
13
+ */
14
+ var assertRafAvailable = () => {
15
+ if (typeof requestAnimationFrame === "undefined" || typeof cancelAnimationFrame === "undefined") throw new Error("raf timer requires requestAnimationFrame and can only run in a browser environment.");
16
+ };
17
+ /**
18
+ * 创建唯一的计时器 id。
19
+ *
20
+ * @returns 新的计时器 id。
21
+ */
22
+ var createTimerId = () => {
23
+ rafTimerSeed += 1;
24
+ return rafTimerSeed;
25
+ };
26
+ /**
27
+ * 删除并取消指定计时器。
28
+ *
29
+ * 步骤:
30
+ * 1. 根据 id 查找计时器状态。
31
+ * 2. 如果计时器不存在,直接结束。
32
+ * 3. 取消下一帧回调,并从缓存中移除计时器。
33
+ *
34
+ * @param timerId 要取消的计时器 id。
35
+ */
36
+ var deleteTimer = (timerId) => {
37
+ const timer = rafTimers.get(timerId);
38
+ if (!timer) return;
39
+ cancelAnimationFrame(timer.frameId);
40
+ rafTimers.delete(timerId);
41
+ };
42
+ /**
43
+ * 每一帧执行一次的调度函数。
44
+ *
45
+ * 步骤:
46
+ * 1. 读取当前计时器状态。
47
+ * 2. 对比当前时间和起始时间/上次执行时间,判断是否到达延迟。
48
+ * 3. timeout 到点后执行一次并删除计时器。
49
+ * 4. interval 到点后执行回调,并更新上次执行时间。
50
+ * 5. 如果计时器仍然存在,继续注册下一帧。
51
+ *
52
+ * @param timerId 当前计时器 id。
53
+ * @param currentTime requestAnimationFrame 传入的当前时间戳。
54
+ */
55
+ var tick = (timerId, currentTime) => {
56
+ const timer = rafTimers.get(timerId);
57
+ if (!timer) return;
58
+ if (timer.type === "timeout" ? currentTime - timer.startTime >= timer.delay : currentTime - timer.lastRunTime >= timer.delay) {
59
+ if (timer.type === "timeout") {
60
+ rafTimers.delete(timerId);
61
+ timer.callback();
62
+ return;
63
+ }
64
+ timer.lastRunTime = currentTime;
65
+ timer.callback();
66
+ }
67
+ if (rafTimers.has(timerId)) timer.frameId = requestAnimationFrame((nextTime) => tick(timerId, nextTime));
68
+ };
69
+ /**
70
+ * 创建 requestAnimationFrame 计时器。
71
+ *
72
+ * 步骤:
73
+ * 1. 检查当前运行环境是否支持 requestAnimationFrame。
74
+ * 2. 创建计时器 id,并记录起始时间。
75
+ * 3. 保存计时器类型、延迟、回调和帧 id 等状态。
76
+ * 4. 注册第一帧调度。
77
+ * 5. 将计时器状态写入缓存,并返回计时器 id。
78
+ *
79
+ * @param type 计时器类型。
80
+ * @param callback 到点后执行的回调函数。
81
+ * @param delay 延迟或间隔时间,单位毫秒。
82
+ * @param args 传给回调函数的参数。
83
+ * @returns 计时器 id。
84
+ */
85
+ var createRafTimer = (type, callback, delay, args) => {
86
+ assertRafAvailable();
87
+ const timerId = createTimerId();
88
+ const startTime = performance.now();
89
+ const timer = {
90
+ type,
91
+ delay: normalizeDelay(delay),
92
+ callback: () => callback(...args ?? []),
93
+ startTime,
94
+ lastRunTime: startTime,
95
+ frameId: 0
96
+ };
97
+ timer.frameId = requestAnimationFrame((currentTime) => tick(timerId, currentTime));
98
+ rafTimers.set(timerId, timer);
99
+ return timerId;
100
+ };
101
+ /**
102
+ * 使用 requestAnimationFrame 驱动,在指定延迟后执行一次回调。
103
+ *
104
+ * @param callback 延迟结束后执行的回调函数。
105
+ * @param delay 延迟时间,单位毫秒,默认 0。
106
+ * @param args 传给回调函数的参数。
107
+ * @returns 计时器 id,可通过 rafClearTimeout 或 clearRafTimer 取消。
108
+ */
109
+ var rafSetTimeout = (callback, delay, ...args) => createRafTimer("timeout", callback, delay, args);
110
+ /**
111
+ * 取消 rafSetTimeout 创建的计时器。
112
+ *
113
+ * @param timerId rafSetTimeout 返回的计时器 id。
114
+ */
115
+ var rafClearTimeout = (timerId) => {
116
+ deleteTimer(timerId);
117
+ };
118
+ /**
119
+ * 使用 requestAnimationFrame 驱动,按指定间隔重复执行回调。
120
+ *
121
+ * @param callback 每次间隔结束后执行的回调函数。
122
+ * @param delay 间隔时间,单位毫秒,默认 0。
123
+ * @param args 传给回调函数的参数。
124
+ * @returns 计时器 id,可通过 rafClearInterval 或 clearRafTimer 取消。
125
+ */
126
+ var rafSetInterval = (callback, delay, ...args) => createRafTimer("interval", callback, delay, args);
127
+ /**
128
+ * 取消 rafSetInterval 创建的计时器。
129
+ *
130
+ * @param timerId rafSetInterval 返回的计时器 id。
131
+ */
132
+ var rafClearInterval = (timerId) => {
133
+ deleteTimer(timerId);
134
+ };
135
+ /**
136
+ * 取消本模块创建的任意 requestAnimationFrame 计时器。
137
+ *
138
+ * @param timerId rafSetTimeout 或 rafSetInterval 返回的计时器 id。
139
+ */
140
+ var clearRafTimer = (timerId) => {
141
+ deleteTimer(timerId);
142
+ };
143
+ //#endregion
144
+ export { clearRafTimer, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout };
@@ -6,6 +6,8 @@ export * from "./components/index";
6
6
  export type { FormFieldSchema, SchemaFormComponentsMap, SchemaFormNamespace, SelectProps } from "../../packages/react/ui/index";
7
7
  export { api, del, get, patch, post, put, request } from "./common/request";
8
8
  export type { AxiosError, BaseResponse, PageParams, PageResponse, RequestConfig, ResponseConfig } from "./common/request";
9
+ export { clearRafTimer, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout } from "./common/rafTimer";
10
+ export type { RafTimerCallback, RafTimerId } from "./common/rafTimer";
9
11
  export * from "./defaultPages/SchemaPage/data/eventInfo";
10
12
  export { merge } from "./defaultPages/SchemaPage/utils/permissions";
11
13
  export * from "./exportStore";
@@ -10,6 +10,7 @@ import { schemaEventBus } from "./stores/schemaEventBus.js";
10
10
  import { schemaStore, useSchemaStore } from "./stores/schemaStore.js";
11
11
  import "./exportStore.js";
12
12
  import { initApp } from "./main.js";
13
+ import { clearRafTimer, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout } from "./common/rafTimer.js";
13
14
  import AsyncSelect_default from "./components/AsyncSelect/index.js";
14
15
  import LanguageSwitch_default from "./components/LanguageSwitch/index.js";
15
16
  import { themeSwitchStorageKey } from "./components/ThemeSwitch/ThemeSwitch.js";
@@ -17,4 +18,4 @@ import ThemeSwitch_default from "./components/ThemeSwitch/index.js";
17
18
  import "./components/index.js";
18
19
  import { eventsInfo } from "./defaultPages/SchemaPage/data/eventInfo.js";
19
20
  import { merge } from "./defaultPages/SchemaPage/utils/permissions.js";
20
- export { AsyncSelect_default as AsyncSelect, FreezeState, HeaderView, LanguageSwitch_default as LanguageSwitch, ThemeSwitch_default as ThemeSwitch, api, apiFreezerStore, del, eventsInfo, generateRouter, get, getAuthToken, i18n, i18nStore, initApp, localKeyMap, merge, modeStore, patch, post, put, request, schemaEventBus, schemaStore, setAuthToken, themeSwitchStorageKey, useApiFreezer, useModeStore, useSchemaStore };
21
+ export { AsyncSelect_default as AsyncSelect, FreezeState, HeaderView, LanguageSwitch_default as LanguageSwitch, ThemeSwitch_default as ThemeSwitch, api, apiFreezerStore, clearRafTimer, del, eventsInfo, generateRouter, get, getAuthToken, i18n, i18nStore, initApp, localKeyMap, merge, modeStore, patch, post, put, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout, request, schemaEventBus, schemaStore, setAuthToken, themeSwitchStorageKey, useApiFreezer, useModeStore, useSchemaStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_tc/template-core",
3
- "version": "0.2.0-bate.1",
3
+ "version": "0.2.0-bate.2",
4
4
  "description": "A full-stack TypeScript admin framework powered by Koa, React, and Vite - monorepo root",
5
5
  "types": "./types/index.d.ts",
6
6
  "exports": {