@_tc/template-core 0.2.0-bate.0 → 0.2.0-bate.10
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/.skills/tc-component-usage-skills/reference/template-core-frontend.md +8 -1
- package/.skills/tc-generator/SKILL.md +3 -2
- package/.skills/tc-generator/reference/example.md +1 -1
- package/.skills/tc-generator/reference/model-schema.md +18 -0
- package/.skills/tc-generator/reference/project-template/index.js +1 -1
- package/.skills/tc-generator/reference/runtime-extensions.md +22 -2
- package/AGENT_README.md +35 -14
- package/README.md +238 -33
- package/cjs/app/controller/view.js +1 -1
- package/cjs/app/{extend → extends}/db.js +1 -1
- package/cjs/app/extends/localCacheHtmlEtag.js +1 -0
- package/cjs/app/extends/render-view.js +1 -0
- package/cjs/app/middleware.js +1 -1
- package/cjs/app/view/entry.tpl +0 -16
- package/cjs/bundler/buildFE.js +1 -0
- package/cjs/bundler/index.js +1 -1
- package/cjs/bundler/state.js +1 -1
- package/cjs/bundler/utils.js +3 -3
- package/cjs/packages/common/index.js +1 -1
- package/cjs/packages/common/rafTimer.js +1 -0
- package/cjs/packages/common/string/index.js +1 -1
- package/cjs/packages/core/index.js +1 -1
- package/cjs/packages/core/loader/controller.js +1 -1
- package/cjs/packages/core/loader/extend.js +1 -1
- package/cjs/packages/core/loader/middleware.js +1 -1
- package/cjs/packages/core/loader/router-schema.js +1 -1
- package/cjs/packages/core/loader/router.js +1 -1
- package/cjs/packages/core/loader/service.js +1 -1
- package/cjs/packages/utils/runFileFn.js +1 -1
- package/esm/app/controller/view.js +28 -15
- package/esm/app/{extend → extends}/$fetch.js +1 -1
- package/esm/app/{extend → extends}/crypto.js +1 -1
- package/esm/app/{extend → extends}/db.js +2 -2
- package/esm/app/{extend → extends}/generateErrorMessage.js +1 -1
- package/esm/app/extends/localCacheHtmlEtag.js +41 -0
- package/esm/app/{extend → extends}/logger.js +1 -1
- package/esm/app/{extend → extends}/parsingParamsOnUrl.js +1 -1
- package/esm/app/extends/render-view.js +18 -0
- package/esm/app/middleware.js +8 -12
- package/esm/app/view/entry.tpl +0 -16
- package/esm/bundler/buildFE.js +14 -0
- package/esm/bundler/index.js +2 -14
- package/esm/bundler/state.js +5 -1
- package/esm/bundler/utils.js +130 -104
- package/esm/packages/common/index.js +7 -6
- package/esm/packages/common/rafTimer.js +47 -0
- package/esm/packages/common/string/index.js +11 -2
- package/esm/packages/core/index.js +34 -28
- package/esm/packages/core/loader/controller.js +6 -6
- package/esm/packages/core/loader/extend.js +6 -6
- package/esm/packages/core/loader/middleware.js +6 -6
- package/esm/packages/core/loader/router-schema.js +7 -7
- package/esm/packages/core/loader/router.js +13 -13
- package/esm/packages/core/loader/service.js +6 -6
- package/esm/packages/utils/runFileFn.js +10 -10
- package/fe/frontend/apps/dash/Dashboard.js +6 -4
- package/fe/frontend/apps/dash/dash.entry.js +4 -0
- package/fe/frontend/src/common/generateMenuData.d.ts +4 -1
- package/fe/frontend/src/common/generateMenuData.js +3 -3
- package/fe/frontend/src/common/language.d.ts +12 -1
- package/fe/frontend/src/common/language.js +25 -4
- package/fe/frontend/src/common/rafTimer.d.ts +1 -0
- package/fe/frontend/src/common/rafTimer.js +2 -0
- package/fe/frontend/src/common/request.js +1 -0
- package/fe/frontend/src/common/theme.d.ts +7 -0
- package/fe/frontend/src/common/theme.js +33 -0
- package/fe/frontend/src/components/BasePage/HeaderView.js +2 -2
- package/fe/frontend/src/components/LanguageSwitch/LanguageSwitch.js +22 -6
- package/fe/frontend/src/components/ThemeSwitch/ThemeSwitch.d.ts +3 -2
- package/fe/frontend/src/components/ThemeSwitch/ThemeSwitch.js +6 -29
- package/fe/frontend/src/components/ThemeSwitch/index.d.ts +1 -0
- package/fe/frontend/src/components/ThemeSwitch/index.js +3 -2
- package/fe/frontend/src/components/index.js +2 -2
- package/fe/frontend/src/defaultPages/NotFoundPage/index.js +4 -3
- package/fe/frontend/src/defaultPages/SchemaPage/components/CallCom/DetailPanel.js +14 -9
- package/fe/frontend/src/defaultPages/SchemaPage/components/CallCom/PopFrom.js +8 -7
- package/fe/frontend/src/defaultPages/SchemaPage/components/SchemaSearch/index.js +20 -24
- package/fe/frontend/src/defaultPages/SchemaPage/components/SchemaTable/PreviewImage.d.ts +10 -0
- package/fe/frontend/src/defaultPages/SchemaPage/components/SchemaTable/PreviewImage.js +17 -0
- package/fe/frontend/src/defaultPages/SchemaPage/components/SchemaTable/data.d.ts +5 -0
- package/fe/frontend/src/defaultPages/SchemaPage/components/SchemaTable/data.js +8 -0
- package/fe/frontend/src/defaultPages/SchemaPage/components/SchemaTable/index.js +50 -32
- package/fe/frontend/src/defaultPages/SchemaPage/index.css +0 -4
- package/fe/frontend/src/defaultPages/SchemaPage/index.js +5 -3
- package/fe/frontend/src/defaultPages/SlotPage/index.js +6 -3
- package/fe/frontend/src/hooks/useText.d.ts +3 -0
- package/fe/frontend/src/hooks/useText.js +14 -0
- package/fe/frontend/src/index.d.ts +11 -3
- package/fe/frontend/src/index.js +8 -3
- package/fe/frontend/src/language/index.d.ts +0 -2
- package/fe/frontend/src/language/index.js +1 -7
- package/fe/frontend/src/language/resources.d.ts +2 -0
- package/fe/frontend/src/language/resources.js +9 -0
- package/fe/model/types/data/schema.d.ts +24 -2
- package/fe/model/types/index.d.ts +1 -0
- package/fe/packages/common/i18n/default.js +3 -0
- package/fe/packages/common/i18n/en-US.js +5 -2
- package/fe/packages/common/i18n/index.js +13 -0
- package/fe/packages/common/i18n/locales.js +7 -0
- package/fe/packages/common/index.d.ts +1 -0
- package/fe/packages/common/rafTimer.d.ts +7 -0
- package/fe/packages/common/rafTimer.js +168 -0
- package/fe/packages/common/string/index.d.ts +9 -0
- package/fe/packages/react/hooks/index.d.ts +9 -0
- package/fe/packages/react/hooks/index.js +8 -0
- package/fe/packages/react/hooks/useRefState.d.ts +4 -12
- package/fe/packages/react/hooks/useRefState.js +18 -8
- package/fe/packages/react/ui/components/DataTable/dataTableWidth.d.ts +12 -0
- package/fe/packages/react/ui/components/DataTable/dataTableWidth.js +33 -0
- package/fe/packages/react/ui/components/DataTable/dataTableWidth.test.js +39 -0
- package/fe/packages/react/ui/components/DataTable/index.d.ts +1 -1
- package/fe/packages/react/ui/components/DataTable/index.js +12 -32
- package/fe/packages/react/ui/components/Textarea/Textarea.d.ts +1 -1
- package/fe/packages/react/ui/components/Textarea/Textarea.js +1 -1
- package/fe/packages/react/ui/components/types/baseType.d.ts +21 -0
- package/fe/packages/react/ui/components/types/baseType.js +0 -0
- package/model/frontend/src/common/language.d.ts +12 -1
- package/model/frontend/src/hooks/useText.d.ts +3 -0
- package/model/frontend/src/language/index.d.ts +0 -2
- package/model/frontend/src/language/resources.d.ts +2 -0
- package/model/model/types/data/schema.d.ts +24 -2
- package/model/model/types/index.d.ts +1 -0
- package/model/packages/common/index.d.ts +1 -0
- package/model/packages/common/rafTimer.d.ts +7 -0
- package/model/packages/common/string/index.d.ts +9 -0
- package/model/packages/react/ui/components/DataTable/dataTableWidth.d.ts +12 -0
- package/model/packages/react/ui/components/DataTable/index.d.ts +1 -1
- package/model/packages/react/ui/components/Textarea/Textarea.d.ts +1 -1
- package/model/packages/react/ui/components/types/baseType.d.ts +21 -0
- package/package.json +5 -5
- package/types/app/extends/localCacheHtmlEtag.d.ts +10 -0
- package/types/app/{extend → extends}/render-view.d.ts +1 -1
- package/types/app/service/base.d.ts +1 -1
- package/types/app/service/project.d.ts +1 -1
- package/types/app/type.d.ts +2 -1
- package/types/app/typings.d.ts +10 -8
- package/types/bundler/buildFE.d.ts +2 -0
- package/types/bundler/index.d.ts +1 -2
- package/types/bundler/state.d.ts +4 -0
- package/types/bundler/utils.d.ts +5 -0
- package/types/index.d.ts +3 -3
- package/types/packages/common/i18n/default.d.ts +4 -0
- package/types/packages/common/i18n/en-US.d.ts +6 -2
- package/types/packages/common/i18n/index.d.ts +21 -0
- package/types/packages/common/i18n/locales.d.ts +10 -0
- package/types/packages/common/i18n/types.d.ts +40 -0
- package/types/packages/common/index.d.ts +1 -0
- package/types/packages/common/rafTimer.d.ts +44 -0
- package/types/packages/common/string/index.d.ts +16 -0
- package/types/packages/core/loader/extend.d.ts +1 -1
- package/types/packages/core/types.d.ts +4 -0
- package/cjs/app/extend/render-view.js +0 -1
- package/esm/app/extend/render-view.js +0 -25
- /package/cjs/app/{extend → extends}/$fetch.js +0 -0
- /package/cjs/app/{extend → extends}/crypto.js +0 -0
- /package/cjs/app/{extend → extends}/generateErrorMessage.js +0 -0
- /package/cjs/app/{extend → extends}/logger.js +0 -0
- /package/cjs/app/{extend → extends}/parsingParamsOnUrl.js +0 -0
- /package/types/app/{extend → extends}/$fetch.d.ts +0 -0
- /package/types/app/{extend → extends}/crypto.d.ts +0 -0
- /package/types/app/{extend → extends}/db.d.ts +0 -0
- /package/types/app/{extend → extends}/generateErrorMessage.d.ts +0 -0
- /package/types/app/{extend → extends}/logger.d.ts +0 -0
- /package/types/app/{extend → extends}/parsingParamsOnUrl.d.ts +0 -0
package/README.md
CHANGED
|
@@ -116,9 +116,9 @@ export default {
|
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
`auth.ATKey` / `auth.RTKey`
|
|
119
|
+
`auth.ATKey` / `auth.RTKey` 用于约定项目鉴权中间件读取短 token 和刷新 token 的 header 名;默认分别为 `Authorization` 和 `RT`。
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
静态资源挂载顺序为:项目 `app/public`、框架 `app/public`、`additionalPublicPaths`。Nunjucks 模板查找顺序为:项目 `app/public/dist`、框架 `app/public/dist`、`additionalPublicPaths/dist`。这些目录由 `app.publicsPath` 统一维护。当 `buildFE` 输出到自定义目录时,可以用 `additionalPublicPaths` 把该目录交给 Koa 提供静态资源访问和模板渲染;如果需要模板查找也命中追加目录,请传绝对路径数组。
|
|
122
122
|
|
|
123
123
|
### buildFE 参数说明
|
|
124
124
|
|
|
@@ -127,7 +127,7 @@ buildFE(mode: 'dev' | 'prod', options?: BuildFEOptions)
|
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
**mode**:构建模式
|
|
130
|
-
- `'dev'
|
|
130
|
+
- `'dev'`:开发模式,构建一次并监听项目 `frontend/` 变化后重建
|
|
131
131
|
- `'prod'`:生产模式,构建优化后的静态资源
|
|
132
132
|
|
|
133
133
|
**options.output**:构建输出位置
|
|
@@ -135,6 +135,10 @@ buildFE(mode: 'dev' | 'prod', options?: BuildFEOptions)
|
|
|
135
135
|
- `'run'`:输出到项目根目录的 `app/public/dist`,适合 TypeScript 编译后的项目
|
|
136
136
|
- `() => string`:自定义函数,返回输出目录;建议返回绝对路径
|
|
137
137
|
|
|
138
|
+
**options.minifyHtml**:是否压缩 Vite 输出的 HTML 内容后再写出最终 `.tpl` / `.html` 文件,默认 `false`
|
|
139
|
+
|
|
140
|
+
构建完成后会在输出目录写入 `FEBuildKey`。服务端渲染 HTML 时会读取 `public/dist/FEBuildKey`,构建 key 变化后自动清空本地 HTML ETag 缓存。
|
|
141
|
+
|
|
138
142
|
示例:
|
|
139
143
|
|
|
140
144
|
```ts
|
|
@@ -148,6 +152,9 @@ await buildFE('prod', { output: 'run' })
|
|
|
148
152
|
await buildFE('prod', {
|
|
149
153
|
output: () => join(__dirname, 'dist/public')
|
|
150
154
|
})
|
|
155
|
+
|
|
156
|
+
// 压缩最终模板输出
|
|
157
|
+
await buildFE('prod', { minifyHtml: true })
|
|
151
158
|
```
|
|
152
159
|
|
|
153
160
|
配置文件:
|
|
@@ -169,20 +176,23 @@ http://localhost:9000/dash?projk=demo
|
|
|
169
176
|
|
|
170
177
|
## 常用公开入口
|
|
171
178
|
|
|
172
|
-
完整 exports 以发布包内 `package.json` 为准,除下表外还包含 `./fe/main`、`./fe/*`、`./fe/rc/*`、`./fe/rc/hooks
|
|
179
|
+
完整 exports 以发布包内 `package.json` 为准,除下表外还包含 `./fe/main`、`./fe/common/request`、`./fe/*`、`./fe/rc/*`、`./fe/rc/hooks` 等子路径。
|
|
173
180
|
|
|
174
181
|
| 入口 | 用途 |
|
|
175
182
|
| --- | --- |
|
|
176
183
|
| `@_tc/template-core` | 服务启动、基础 Controller/Service、Koa 类型 |
|
|
177
184
|
| `@_tc/template-core/bundler` | 构建入口,提供前端资源构建 `buildFE` 和消费方 Node/backend 构建 `buildBE` |
|
|
178
|
-
| `@_tc/template-core/fe` |
|
|
185
|
+
| `@_tc/template-core/fe` | 前端初始化、动态组件渲染辅助、Dash 路由扩展、请求方法、SchemaPage 事件、token 工具、共享状态和内置前端组件 |
|
|
186
|
+
| `@_tc/template-core/fe/main` | 前端初始化入口 |
|
|
187
|
+
| `@_tc/template-core/fe/common/request` | 请求实例、请求方法和请求类型的显式子路径 |
|
|
179
188
|
| `@_tc/template-core/fe/rc` | UI/SchemaForm 组件和类型 |
|
|
189
|
+
| `@_tc/template-core/fe/rc/hooks` | React hooks 汇总入口 |
|
|
180
190
|
| `@_tc/template-core/fe/tailwind_ui.css` | 前端全局样式入口,源码对应 `frontend/src/main.css` |
|
|
181
191
|
| `@_tc/template-core/model` | model 配置类型 |
|
|
182
192
|
|
|
183
193
|
### 后端类型
|
|
184
194
|
|
|
185
|
-
`@_tc/template-core` 会导出常用 Node
|
|
195
|
+
`@_tc/template-core` 会导出常用 Node 侧类型,项目编写 `app/*` 文件时建议优先复用这些类型。
|
|
186
196
|
|
|
187
197
|
| 类型 | 使用场景 |
|
|
188
198
|
| --- | --- |
|
|
@@ -280,7 +290,7 @@ VS Code 建议安装官方 Tailwind CSS IntelliSense 插件,并在工作区 `.
|
|
|
280
290
|
## 内置功能
|
|
281
291
|
|
|
282
292
|
- Koa 服务启动:`serverStart(options)`。
|
|
283
|
-
- 约定式加载:自动加载 `config`、`
|
|
293
|
+
- 约定式加载:自动加载 `config`、`extends`、`router-schema`、`service`、`controller`、`middlewares`、根级 `middleware`、`router`。
|
|
284
294
|
- 双层 app:框架内置 `app/` 和项目 `app/` 一起加载。
|
|
285
295
|
- 默认项目接口:
|
|
286
296
|
- `GET /api/project/model_list`
|
|
@@ -292,8 +302,8 @@ VS Code 建议安装官方 Tailwind CSS IntelliSense 插件,并在工作区 `.
|
|
|
292
302
|
- Schema CRUD:根据 `model` 中的 `schemaConfig` 生成搜索、表格、新增、编辑、详情。
|
|
293
303
|
- 内置 UI 组件:提供丰富的 React 组件库,详见下方"内置 UI 组件"章节。
|
|
294
304
|
- API 中间件:错误处理、静态资源、body parser、项目上下文、接口签名、参数校验。
|
|
295
|
-
- 前端构建:扫描框架和项目的 `frontend/**/*.entry.{ts,tsx,js,jsx}`,输出服务端可渲染的 `.entry.tpl`
|
|
296
|
-
- 类型扩展:支持扩展 SchemaForm、CallCom、KoaApp。
|
|
305
|
+
- 前端构建:扫描框架和项目的 `frontend/**/*.entry.{ts,tsx,js,jsx}`,输出服务端可渲染的 `.entry.tpl`,并写入 `FEBuildKey` 供 HTML ETag 缓存失效使用。
|
|
306
|
+
- 类型扩展:支持扩展 SchemaForm、SchemaTable 单元格渲染组件、CallCom、KoaApp。
|
|
297
307
|
|
|
298
308
|
## 前端公共能力
|
|
299
309
|
|
|
@@ -303,21 +313,36 @@ VS Code 建议安装官方 Tailwind CSS IntelliSense 插件,并在工作区 `.
|
|
|
303
313
|
import {
|
|
304
314
|
api,
|
|
305
315
|
apiFreezerStore,
|
|
316
|
+
addLanguageResources,
|
|
306
317
|
AsyncSelect,
|
|
318
|
+
clearAuthToken,
|
|
307
319
|
eventsInfo,
|
|
320
|
+
getCurrentLanguage,
|
|
321
|
+
getFallbackLanguage,
|
|
308
322
|
FreezeState,
|
|
309
323
|
get,
|
|
324
|
+
getSupportedLanguages,
|
|
325
|
+
getAuthToken,
|
|
310
326
|
LanguageSwitch,
|
|
327
|
+
localKeyMap,
|
|
311
328
|
merge,
|
|
312
329
|
modeStore,
|
|
313
330
|
post,
|
|
331
|
+
renderImportComponent,
|
|
332
|
+
registerFrontendI18nResources,
|
|
314
333
|
request,
|
|
315
334
|
schemaEventBus,
|
|
316
335
|
schemaStore,
|
|
336
|
+
setFallbackLanguage,
|
|
337
|
+
setLanguage,
|
|
338
|
+
setResources,
|
|
339
|
+
setAuthToken,
|
|
317
340
|
ThemeSwitch,
|
|
341
|
+
t,
|
|
318
342
|
useApiFreezer,
|
|
319
343
|
useModeStore,
|
|
320
344
|
useSchemaStore,
|
|
345
|
+
useText,
|
|
321
346
|
} from '@_tc/template-core/fe'
|
|
322
347
|
|
|
323
348
|
import type {
|
|
@@ -333,6 +358,8 @@ import type {
|
|
|
333
358
|
PageResponse,
|
|
334
359
|
RequestConfig,
|
|
335
360
|
ResponseConfig,
|
|
361
|
+
SchemaTableRenderComponent,
|
|
362
|
+
SchemaTableRenderComponentsMap,
|
|
336
363
|
SchemaFormComponentsMap,
|
|
337
364
|
SchemaFormNamespace,
|
|
338
365
|
SelectProps,
|
|
@@ -345,30 +372,50 @@ import type {
|
|
|
345
372
|
| --- | --- |
|
|
346
373
|
| 应用启动 | `initApp` |
|
|
347
374
|
| 内置前端组件 | `AsyncSelect`、`LanguageSwitch`、`ThemeSwitch` |
|
|
375
|
+
| 组件加载辅助 | `renderImportComponent` |
|
|
348
376
|
| Dash 扩展类型 | `DashRoutesExtender`、`DashRoutesContext`、`DashComponentsMap`、`DashHeaderUserAreaProps`、`DashRouteGuard` |
|
|
349
|
-
| SchemaPage 类型 | `CallComComponentsMap`、`CallComRenderer` |
|
|
377
|
+
| SchemaPage 类型 | `CallComComponentsMap`、`CallComRenderer`、`SchemaTableRenderComponent`、`SchemaTableRenderComponentsMap` |
|
|
350
378
|
| SchemaForm 类型 | `FormFieldSchema`、`SchemaFormComponentsMap`、`SchemaFormNamespace`、`SelectProps` |
|
|
351
379
|
| 请求方法 | `api`、`request`、`get`、`post`、`put`、`patch`、`del` |
|
|
352
380
|
| 请求类型 | `BaseResponse`、`PageParams`、`PageResponse`、`RequestConfig`、`ResponseConfig`、`AxiosError` |
|
|
381
|
+
| RAF 风格计时器 | `rafSetTimeout`、`rafSetInterval`、`rafClearTimeout`、`rafClearInterval`、`clearRafTimer`、`RafTimerId`、`RafTimerCallback` |
|
|
382
|
+
| Token 工具 | `getAuthToken`、`setAuthToken`、`clearAuthToken`、`localKeyMap` |
|
|
383
|
+
| 多语言工具 | `useText`、`getText`、`t`、`registerFrontendI18nResources`、`addLanguageResources`、`setLanguage`、`setFallbackLanguage`、`setResources`、`getCurrentLanguage`、`getFallbackLanguage`、`getSupportedLanguages` |
|
|
353
384
|
| 请求冻结 | `apiFreezerStore`、`useApiFreezer`、`FreezeState`、`ApiFreezerRequestMatcher` |
|
|
354
385
|
| 事件工具 | `eventsInfo`、`merge` |
|
|
355
386
|
| 共享状态 | `modeStore`、`useModeStore`、`schemaStore`、`useSchemaStore`、`schemaEventBus` |
|
|
356
387
|
|
|
357
388
|
`modeStore` 管项目模型和当前项目数据,`schemaStore` 管当前 Schema 页面的 schema/cache,`schemaEventBus` 用来在 Search、Table、CallCom 之间通信。
|
|
358
389
|
|
|
390
|
+
`renderImportComponent` 封装了 `React.lazy` + `Suspense`,默认会用页面骨架屏作为 fallback。`getAuthToken()`、`setAuthToken()` 和 `clearAuthToken()` 用于读写或清理本地短 token,`localKeyMap` 暴露对应的本地存储 key 常量。
|
|
391
|
+
|
|
359
392
|
只使用请求层时,也可以从 `@_tc/template-core/fe/common/request` 导入 `api`、请求方法和请求类型;这是发布包显式子路径,不要下钻到 `frontend/src/common/request`。
|
|
360
393
|
|
|
361
394
|
### 前端多语言
|
|
362
395
|
|
|
363
|
-
TemplateCore 前端通过 `@_tc/template-core/fe` 暴露 i18n
|
|
396
|
+
TemplateCore 前端通过 `@_tc/template-core/fe` 暴露 i18n 能力。框架内置中文、英文资源,但不会在普通自定义页面 import 前端入口时自动注册,避免非 Dashboard 页面把框架文案整包带入运行时资源。
|
|
397
|
+
|
|
398
|
+
默认 Dashboard 入口会自动调用 `registerFrontendI18nResources()`,所以 Dashboard、Schema CRUD、内置页头、语言切换等框架页面可以直接使用框架内置文案。自定义入口如果也要复用这些框架文案,需要在渲染前手动注册:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { initApp, registerFrontendI18nResources } from '@_tc/template-core/fe'
|
|
402
|
+
|
|
403
|
+
initApp(App, {
|
|
404
|
+
beforeRender: async () => {
|
|
405
|
+
await registerFrontendI18nResources()
|
|
406
|
+
},
|
|
407
|
+
})
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
项目可以继续追加自己的资源或新增语种。
|
|
364
411
|
|
|
365
412
|
**追加自定义语言资源**:
|
|
366
413
|
|
|
367
414
|
```ts
|
|
368
|
-
import {
|
|
415
|
+
import { addLanguageResources } from '@_tc/template-core/fe'
|
|
369
416
|
|
|
370
|
-
|
|
371
|
-
|
|
417
|
+
addLanguageResources('zh-CN', {
|
|
418
|
+
app: {
|
|
372
419
|
product: {
|
|
373
420
|
menu: '商品管理',
|
|
374
421
|
name: '商品名称',
|
|
@@ -378,8 +425,8 @@ i18nStore.getState().addResources('zh-CN', {
|
|
|
378
425
|
},
|
|
379
426
|
})
|
|
380
427
|
|
|
381
|
-
|
|
382
|
-
|
|
428
|
+
addLanguageResources('en-US', {
|
|
429
|
+
app: {
|
|
383
430
|
product: {
|
|
384
431
|
menu: 'Products',
|
|
385
432
|
name: 'Product name',
|
|
@@ -390,17 +437,49 @@ i18nStore.getState().addResources('en-US', {
|
|
|
390
437
|
})
|
|
391
438
|
```
|
|
392
439
|
|
|
440
|
+
**新增语种并切换**:
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
import { addLanguageResources, setLanguage } from '@_tc/template-core/fe'
|
|
444
|
+
|
|
445
|
+
addLanguageResources('ja-JP', {
|
|
446
|
+
app: {
|
|
447
|
+
product: {
|
|
448
|
+
menu: '商品管理',
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
setLanguage('ja-JP')
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
`addLanguageResources()` 用于追加或新增某个语种的文案,默认会深合并同语种资源;传 `{ merge: false }` 可以替换该语种资源。`setResources(resources, { merge: true })` 可批量合并多语种资源。`LanguageSwitch` 默认会把已注册语种加入选项;需要自定义展示文案时仍可传入 `options`。
|
|
457
|
+
|
|
458
|
+
React 组件 render 中使用 `useText()` 读取文案。它会订阅语言和资源变化,调用 `setLanguage()` 后组件会自动重渲染:
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
import { useText } from '@_tc/template-core/fe'
|
|
462
|
+
|
|
463
|
+
function ProductTitle() {
|
|
464
|
+
const text = useText()
|
|
465
|
+
|
|
466
|
+
return <h1>{text('$i18n::app.product.menu')}</h1>
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
`getText()` 是普通函数,适合请求错误、日志、校验工具或事件回调等非 React 场景;它不会主动触发 React 组件重渲染。
|
|
471
|
+
|
|
393
472
|
资源注册时不写 `$i18n::` 前缀,配置中使用时才写:
|
|
394
473
|
|
|
395
474
|
```ts
|
|
396
|
-
const title = '$i18n::
|
|
475
|
+
const title = '$i18n::app.product.createTitle'
|
|
397
476
|
```
|
|
398
477
|
|
|
399
478
|
**在 model / Schema 配置中使用**:
|
|
400
479
|
|
|
401
480
|
```ts
|
|
402
481
|
{
|
|
403
|
-
name: '$i18n::
|
|
482
|
+
name: '$i18n::app.product.menu',
|
|
404
483
|
moduleType: 'schema',
|
|
405
484
|
schemaConfig: {
|
|
406
485
|
schema: {
|
|
@@ -408,7 +487,7 @@ const title = '$i18n::business.product.createTitle'
|
|
|
408
487
|
properties: {
|
|
409
488
|
name: {
|
|
410
489
|
type: 'string',
|
|
411
|
-
label: '$i18n::
|
|
490
|
+
label: '$i18n::app.product.name',
|
|
412
491
|
minLength: 2,
|
|
413
492
|
createFormOption: {
|
|
414
493
|
comType: 'input',
|
|
@@ -422,7 +501,7 @@ const title = '$i18n::business.product.createTitle'
|
|
|
422
501
|
tableConfig: {
|
|
423
502
|
headerButtons: [
|
|
424
503
|
{
|
|
425
|
-
label: '$i18n::
|
|
504
|
+
label: '$i18n::app.product.create',
|
|
426
505
|
eventKey: 'callComponent',
|
|
427
506
|
eventOption: { comName: 'createForm' },
|
|
428
507
|
},
|
|
@@ -430,7 +509,7 @@ const title = '$i18n::business.product.createTitle'
|
|
|
430
509
|
},
|
|
431
510
|
componentConfig: {
|
|
432
511
|
createForm: {
|
|
433
|
-
title: '$i18n::
|
|
512
|
+
title: '$i18n::app.product.createTitle',
|
|
434
513
|
saveBtnText: '$i18n::common.submit',
|
|
435
514
|
},
|
|
436
515
|
},
|
|
@@ -469,7 +548,7 @@ export function Toolbar() {
|
|
|
469
548
|
}
|
|
470
549
|
```
|
|
471
550
|
|
|
472
|
-
`LanguageSwitch` 默认提供 `zh-CN` 和 `en-US`,也支持传入 `options`
|
|
551
|
+
`LanguageSwitch` 默认提供 `zh-CN` 和 `en-US`,也支持传入 `options` 覆盖语言列表。组件当前是按钮 + 下拉菜单形态,`option.label` 支持 ReactNode,`buttonClassName` 可自定义触发按钮样式。组件内部会调用 `i18nStore.getState().setLanguage()`,因此会沿用 `tc_language` 的持久化行为。
|
|
473
552
|
|
|
474
553
|
**AJV 校验文案**:
|
|
475
554
|
|
|
@@ -495,7 +574,7 @@ export function Toolbar() {
|
|
|
495
574
|
}
|
|
496
575
|
```
|
|
497
576
|
|
|
498
|
-
`ThemeSwitch` 会切换 `document.documentElement` 上的 `dark` class,并同步 `color-scheme`。默认写入 localStorage,key 是 `tc_theme`。刷新页面后会优先读取本地主题;没有本地主题时,读取当前根节点是否已有 `dark` class
|
|
577
|
+
`ThemeSwitch` 会切换 `document.documentElement` 上的 `dark` class,并同步 `color-scheme`。默认写入 localStorage,key 是 `tc_theme`。刷新页面后会优先读取本地主题;没有本地主题时,读取当前根节点是否已有 `dark` class。默认 Dashboard 会在渲染前调用 `initThemeMode()`,让已保存主题尽早生效。
|
|
499
578
|
|
|
500
579
|
常用参数:
|
|
501
580
|
|
|
@@ -507,6 +586,21 @@ export function Toolbar() {
|
|
|
507
586
|
| `persist` | 是否写入 localStorage,默认 `true`。 |
|
|
508
587
|
| `storageKey` | 自定义本地存储 key,默认 `tc_theme`。 |
|
|
509
588
|
|
|
589
|
+
如需在自定义入口或组件外手动初始化/切换主题,可以从 `@_tc/template-core/fe` 导入主题工具:
|
|
590
|
+
|
|
591
|
+
```ts
|
|
592
|
+
import { applyThemeMode, getCurrentThemeMode, initThemeMode, themeSwitchStorageKey } from '@_tc/template-core/fe'
|
|
593
|
+
|
|
594
|
+
initThemeMode()
|
|
595
|
+
applyThemeMode('dark', true)
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
RAF 风格计时器由 `@tc/common/rafTimer` 提供,浏览器优先使用 `requestAnimationFrame`,缺少 RAF 时自动降级到 `setTimeout`。
|
|
599
|
+
|
|
600
|
+
`@_tc/template-core/fe/rc` 和 `@_tc/template-core/fe/rc/hooks` 也会随发布包提供对应 UI 与 hooks 入口;hooks 汇总入口包含 `useBreadcrumb`、`useExecuteOnce`、`useInit`、`useLanguage`、`usePagination`、`useRefState`、`useWatch`。
|
|
601
|
+
|
|
602
|
+
`useRefState` 支持可选的 `delayTiming` 参数,传入大于 0 的值时会延迟 state 提交,并复用 `@tc/common/rafTimer` 的浏览器优先、Node/SSR 自动降级计时器实现。
|
|
603
|
+
|
|
510
604
|
### 前端请求封装详解
|
|
511
605
|
|
|
512
606
|
框架内置的请求封装基于 `@tc/common/http`,底层使用原生 `fetch` API,实现了类似 Axios 的接口和拦截器机制,提供了签名、鉴权、错误处理等能力。
|
|
@@ -516,6 +610,7 @@ export function Toolbar() {
|
|
|
516
610
|
- `timeout`:15000ms(15秒)
|
|
517
611
|
- `credentials`:`'include'`,支持跨域携带 cookie
|
|
518
612
|
- `window._signKey`:由服务端页面模板从 `app.config.signKey` 注入
|
|
613
|
+
- 默认会先设置 `Content-Type: application/json`;如果是上传文件、`FormData` 或其他内容类型,请在请求配置里显式覆盖
|
|
519
614
|
|
|
520
615
|
**请求拦截器**:
|
|
521
616
|
|
|
@@ -738,7 +833,7 @@ app/middlewares/**/*.(js|ts) -> app.middlewares
|
|
|
738
833
|
app/middleware.(js|ts) -> 全局中间件编排,按框架 -> 项目顺序执行
|
|
739
834
|
app/router/**/*.(js|ts) -> Koa router
|
|
740
835
|
app/router-schema/**/*.(js|ts) -> app.routerSchema
|
|
741
|
-
app/
|
|
836
|
+
app/extends/*.(js|ts) -> app.extends
|
|
742
837
|
config/config.default.(js|ts) -> app.config
|
|
743
838
|
config/config.{env}.(js|ts) -> app.config
|
|
744
839
|
model/**/*.(js|ts) -> 内置 project service 按需读取的项目模型配置
|
|
@@ -872,10 +967,10 @@ const unsafeUsers = app.extends.db.queryDB(
|
|
|
872
967
|
)
|
|
873
968
|
```
|
|
874
969
|
|
|
875
|
-
|
|
970
|
+
项目可以用自己的 `app/extends/db.ts` 覆盖默认实现。建议保留同一组方法名,这样 controller/service 调用方不用调整。
|
|
876
971
|
|
|
877
972
|
```ts
|
|
878
|
-
// app/
|
|
973
|
+
// app/extends/db.ts
|
|
879
974
|
import type { DB, DBFactory } from '@_tc/template-core'
|
|
880
975
|
|
|
881
976
|
interface DataOptions {
|
|
@@ -988,7 +1083,7 @@ export default (app: KoaApp, router: Router) => {
|
|
|
988
1083
|
- 每个 `app/router/*.ts` 文件都会获得独立的 `koa-router` 实例。
|
|
989
1084
|
- 可以通过 `router.level = number` 控制挂载顺序,数值越小越早挂载。
|
|
990
1085
|
- 默认 `level` 是 `0`,框架兜底 router 是 `99`。
|
|
991
|
-
- 通配、兜底、重定向类路由建议设置较大的 `level
|
|
1086
|
+
- 通配、兜底、重定向类路由建议设置较大的 `level`,避免抢先匹配项目 API。
|
|
992
1087
|
|
|
993
1088
|
```ts
|
|
994
1089
|
import type { KoaApp, Router } from '@_tc/template-core'
|
|
@@ -1277,7 +1372,7 @@ DELETE {api} -> 删除
|
|
|
1277
1372
|
|
|
1278
1373
|
### 服务端兜底路由守卫
|
|
1279
1374
|
|
|
1280
|
-
|
|
1375
|
+
项目可以创建 `app/router-guard.ts` 或 `app/router-guard.js`。它只在所有正常 `app/router/*` 路由都没有命中时执行。
|
|
1281
1376
|
|
|
1282
1377
|
```ts
|
|
1283
1378
|
import type { Ctx, KoaApp } from '@_tc/template-core'
|
|
@@ -1302,7 +1397,7 @@ export default (_app: KoaApp) => {
|
|
|
1302
1397
|
|
|
1303
1398
|
### 扩展 Dashboard 顶部用户区域
|
|
1304
1399
|
|
|
1305
|
-
|
|
1400
|
+
项目可以创建 `frontend/extended/dash/components.tsx`,通过组件映射填充内置 Dashboard 页头右侧区域。
|
|
1306
1401
|
|
|
1307
1402
|
```tsx
|
|
1308
1403
|
// frontend/extended/dash/components.tsx
|
|
@@ -1323,7 +1418,7 @@ export default components
|
|
|
1323
1418
|
|
|
1324
1419
|
### 扩展 Dashboard 登录守卫
|
|
1325
1420
|
|
|
1326
|
-
|
|
1421
|
+
项目可以创建 `frontend/extended/dash/routeGuard.ts`,用于在 Dashboard 内置 `homePage` / 首菜单重定向前做登录判断。
|
|
1327
1422
|
|
|
1328
1423
|
```ts
|
|
1329
1424
|
// frontend/extended/dash/routeGuard.ts
|
|
@@ -1339,9 +1434,9 @@ export default dashRouteGuard
|
|
|
1339
1434
|
返回值规则:
|
|
1340
1435
|
|
|
1341
1436
|
- 返回 `string`:框架使用 `window.location.href` 跳转,登录页不需要在 Dash 路由下。
|
|
1342
|
-
- 返回非 `string`:中断 Dashboard
|
|
1437
|
+
- 返回非 `string`:中断 Dashboard 内置重定向,使用方可自行跳转或处理提示。
|
|
1343
1438
|
- `DashRouteGuardResult` 是 `string | undefined`;`undefined`(包括已登录分支的隐式返回)也会打断 Dashboard 内置重定向。
|
|
1344
|
-
-
|
|
1439
|
+
- 没有自定义 `routeGuard` 文件时,框架使用兜底空对象,不影响默认 Dashboard 重定向。
|
|
1345
1440
|
|
|
1346
1441
|
### 扩展 SchemaForm 字段组件
|
|
1347
1442
|
|
|
@@ -1511,6 +1606,90 @@ remark: {
|
|
|
1511
1606
|
}
|
|
1512
1607
|
```
|
|
1513
1608
|
|
|
1609
|
+
### 扩展 SchemaTable 单元格渲染组件
|
|
1610
|
+
|
|
1611
|
+
SchemaPage 表格列支持通过 `tableOption.renderComponent` 使用内置或自定义注册的渲染组件。内置组件:
|
|
1612
|
+
|
|
1613
|
+
- `PreviewImage`:把当前单元格值渲染成图片预览;值是数组时直接作为图片列表,值是单个字符串时自动转成单图数组。
|
|
1614
|
+
|
|
1615
|
+
直接使用内置组件:
|
|
1616
|
+
|
|
1617
|
+
```ts
|
|
1618
|
+
cover: {
|
|
1619
|
+
type: 'string',
|
|
1620
|
+
label: '封面',
|
|
1621
|
+
tableOption: {
|
|
1622
|
+
renderComponent: 'PreviewImage',
|
|
1623
|
+
renderComponentProps: {
|
|
1624
|
+
width: 48,
|
|
1625
|
+
height: 48,
|
|
1626
|
+
},
|
|
1627
|
+
},
|
|
1628
|
+
}
|
|
1629
|
+
```
|
|
1630
|
+
|
|
1631
|
+
自定义渲染组件时,先声明 props 类型:
|
|
1632
|
+
|
|
1633
|
+
```ts
|
|
1634
|
+
// typing/template-core.d.ts
|
|
1635
|
+
import '@_tc/template-core/model'
|
|
1636
|
+
|
|
1637
|
+
declare module '@_tc/template-core/model' {
|
|
1638
|
+
export namespace SchemaTableNamespace {
|
|
1639
|
+
interface RenderComponentPropsMap {
|
|
1640
|
+
PriceCell: {
|
|
1641
|
+
value?: unknown
|
|
1642
|
+
record?: Record<string, unknown>
|
|
1643
|
+
rowIndex?: number
|
|
1644
|
+
fieldKey?: string
|
|
1645
|
+
currency?: string
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
注册运行时组件:
|
|
1653
|
+
|
|
1654
|
+
```ts
|
|
1655
|
+
// frontend/extended/SchemaPage/SchemaTable/data.ts
|
|
1656
|
+
import { lazy } from 'react'
|
|
1657
|
+
import type { SchemaTableRenderComponentsMap } from '@_tc/template-core/fe'
|
|
1658
|
+
|
|
1659
|
+
const componentsMap = {
|
|
1660
|
+
PriceCell: lazy(() => import('../../components/PriceCell')),
|
|
1661
|
+
} satisfies SchemaTableRenderComponentsMap
|
|
1662
|
+
|
|
1663
|
+
export default componentsMap
|
|
1664
|
+
```
|
|
1665
|
+
|
|
1666
|
+
组件会收到 `value`、`record`、`rowIndex`、`fieldKey`,以及 `renderComponentProps` 中的自定义参数:
|
|
1667
|
+
|
|
1668
|
+
```tsx
|
|
1669
|
+
// frontend/components/PriceCell.tsx
|
|
1670
|
+
export default function PriceCell(props: {
|
|
1671
|
+
value?: unknown
|
|
1672
|
+
currency?: string
|
|
1673
|
+
}) {
|
|
1674
|
+
return <span>{props.currency ?? '¥'}{Number(props.value ?? 0).toFixed(2)}</span>
|
|
1675
|
+
}
|
|
1676
|
+
```
|
|
1677
|
+
|
|
1678
|
+
model 中使用:
|
|
1679
|
+
|
|
1680
|
+
```ts
|
|
1681
|
+
price: {
|
|
1682
|
+
type: 'number',
|
|
1683
|
+
label: '价格',
|
|
1684
|
+
tableOption: {
|
|
1685
|
+
renderComponent: 'PriceCell',
|
|
1686
|
+
renderComponentProps: {
|
|
1687
|
+
currency: '¥',
|
|
1688
|
+
},
|
|
1689
|
+
},
|
|
1690
|
+
}
|
|
1691
|
+
```
|
|
1692
|
+
|
|
1514
1693
|
## 如何配置类型扩展
|
|
1515
1694
|
|
|
1516
1695
|
### tsconfig
|
|
@@ -1610,6 +1789,22 @@ declare module '@_tc/template-core/model' {
|
|
|
1610
1789
|
- `OptionMap` 对应字段上的 `xxxOption`。
|
|
1611
1790
|
- 运行时组件注册在 `frontend/extended/SchemaPage/CallCom/data.ts`。
|
|
1612
1791
|
|
|
1792
|
+
### SchemaTable 渲染组件类型扩展
|
|
1793
|
+
|
|
1794
|
+
扩展目标:
|
|
1795
|
+
|
|
1796
|
+
```ts
|
|
1797
|
+
declare module '@_tc/template-core/model' {
|
|
1798
|
+
export namespace SchemaTableNamespace {
|
|
1799
|
+
interface RenderComponentPropsMap {}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
```
|
|
1803
|
+
|
|
1804
|
+
- `RenderComponentPropsMap` 的 key 对应 `tableOption.renderComponent`。
|
|
1805
|
+
- 运行时组件注册在 `frontend/extended/SchemaPage/SchemaTable/data.ts`。
|
|
1806
|
+
- 组件运行时会自动收到 `value`、`record`、`rowIndex`、`fieldKey`;`renderComponentProps` 只配置自定义参数。
|
|
1807
|
+
|
|
1613
1808
|
### KoaApp 类型扩展
|
|
1614
1809
|
|
|
1615
1810
|
使用方推荐增强根包 `@_tc/template-core`:
|
|
@@ -1792,6 +1987,16 @@ await buildFE('prod', {
|
|
|
1792
1987
|
})
|
|
1793
1988
|
```
|
|
1794
1989
|
|
|
1990
|
+
也可以压缩最终模板输出:
|
|
1991
|
+
|
|
1992
|
+
```ts
|
|
1993
|
+
await buildFE('prod', {
|
|
1994
|
+
minifyHtml: true,
|
|
1995
|
+
})
|
|
1996
|
+
```
|
|
1997
|
+
|
|
1998
|
+
构建完成后会在输出目录写入 `FEBuildKey`,服务端渲染 HTML 时会用它判断本地 HTML ETag 缓存是否需要清空。
|
|
1999
|
+
|
|
1795
2000
|
## Node/backend 构建 buildBE
|
|
1796
2001
|
|
|
1797
2002
|
消费方 Node/backend 侧构建使用 `buildBE()`。它复用发布包内的 `scripts/vite-build/buildEntries`,只是提供消费方默认配置。
|
|
@@ -1953,6 +2158,6 @@ await buildBE({
|
|
|
1953
2158
|
- `model` 数组合并依赖稳定 `key`。
|
|
1954
2159
|
- Dashboard 默认需要 URL 带 `?projk=项目key`。
|
|
1955
2160
|
- Dashboard 登录守卫放在 `frontend/extended/dash/routeGuard.ts`;登录页通常不在 Dash 路由下,返回登录页 URL 即可。
|
|
1956
|
-
-
|
|
2161
|
+
- 服务端兜底守卫放在项目根目录的 `app/router-guard.ts|js`。
|
|
1957
2162
|
- 前端入口必须命名为 `.entry.tsx`、`.entry.ts`、`.entry.jsx` 或 `.entry.js`。
|
|
1958
2163
|
- 入口同目录的 `.html` 只能作为插槽文件使用,不能写完整 HTML 文档;页面外壳始终由 `app/view/entry.tpl` 生成。
|
|
@@ -1 +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);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;
|
|
@@ -6,7 +6,7 @@ require(`../../_virtual/_rolldown/runtime.js`);let e=require(`node:fs`),t=requir
|
|
|
6
6
|
FROM tc_framework_data
|
|
7
7
|
WHERE namespace = ?
|
|
8
8
|
ORDER BY updated_at DESC, data_key ASC
|
|
9
|
-
LIMIT ? OFFSET ?`,[d(e?.namespace),t,n]).map(e=>_(e))},countDBData:e=>{let t=y.getDBFirst(`SELECT COUNT(1) AS count_value FROM tc_framework_data WHERE namespace = ?`,[d(e?.namespace)]);return Number(t?.count_value??0)},clearDBData:e=>y.runDB(`DELETE FROM tc_framework_data WHERE namespace = ?`,[d(e?.namespace)]).changes,closeDB:()=>{c||=(i?.close(),!0)}};return y}function a(e){let t=`node:sqlite`,n;try{n=require(t)}catch(e){throw Error(`[db] 默认数据库依赖 Node.js 内置 ${t},当前运行环境不可用。请升级 Node.js,或在业务项目覆盖 app/
|
|
9
|
+
LIMIT ? OFFSET ?`,[d(e?.namespace),t,n]).map(e=>_(e))},countDBData:e=>{let t=y.getDBFirst(`SELECT COUNT(1) AS count_value FROM tc_framework_data WHERE namespace = ?`,[d(e?.namespace)]);return Number(t?.count_value??0)},clearDBData:e=>y.runDB(`DELETE FROM tc_framework_data WHERE namespace = ?`,[d(e?.namespace)]).changes,closeDB:()=>{c||=(i?.close(),!0)}};return y}function a(e){let t=`node:sqlite`,n;try{n=require(t)}catch(e){throw Error(`[db] 默认数据库依赖 Node.js 内置 ${t},当前运行环境不可用。请升级 Node.js,或在业务项目覆盖 app/extends/db.ts。${y(e)}`)}if(!n.DatabaseSync)throw Error(`[db] ${t} 未提供 DatabaseSync,无法初始化默认 SQLite 数据库。`);return new n.DatabaseSync(e)}function o(e){e.exec(`
|
|
10
10
|
CREATE TABLE IF NOT EXISTS tc_framework_data (
|
|
11
11
|
namespace TEXT NOT NULL,
|
|
12
12
|
data_key TEXT NOT NULL,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require(`../../_virtual/_rolldown/runtime.js`);let t=require(`fs`),n=require(`path`);n=e.__toESM(n);var r=`./dist/FEBuildKey`,i={etags:{},FEBuildKey:null},a=(e=>{let a=(e,t)=>{i.etags[e]=t},o=e=>!!s(e),s=e=>i.etags[e]||null,c=e=>{i.FEBuildKey=e},l=()=>i.FEBuildKey||null,u=()=>{for(let i of e.publicsPath){let e=n.default.resolve(i,r);if((0,t.existsSync)(e))return e}},d=()=>{let e,n=u();return n&&(e=(0,t.readFileSync)(n,`utf-8`)),e===l()};return{keepFreshFEBuildKey:()=>{if(!d()){let e=u();if(e){let n=(0,t.readFileSync)(e,`utf-8`);if(n){c(n),i.etags={};return}}console.log(`--- 没找到前端构建key文件 ---`),console.log(`--- filePath --- -> `,e)}},hasEtag:o,getEtag:s,setEtag:a}});module.exports=a;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require(`../../_virtual/_rolldown/runtime.js`);let t=require(`node:path`);t=e.__toESM(t);let n=require(`nunjucks`);n=e.__toESM(n);var r=e=>{let r=e.publicsPath.map(e=>t.default.resolve(e,`./dist`));console.log(r);let i=n.default.configure(r,{autoescape:!0,throwOnUndefined:!1,trimBlocks:!1,lstripBlocks:!1,noCache:e.envs.get()===`local`,...typeof e.config.nunjucks==`object`?e.config.nunjucks:{}});return e.extends.nunjucksEnv=i,(e,t,n)=>t.body=i.render(e,Object.assign({},t.state,n))};module.exports=r;
|
package/cjs/app/middleware.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.js`)
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.js`);let t=require(`koa-compress`);t=e.__toESM(t);let n=require(`koa-bodyparser`);n=e.__toESM(n);let r=require(`koa-static`);r=e.__toESM(r);var i=(0,n.default)({formLimit:`1000mb`,enableTypes:[`form`,`json`,`text`]}),a=e=>{let n=e.envs.isLocal()?{maxage:0,setHeaders(e){e.setHeader(`Cache-Control`,`no-store, no-cache, must-revalidate, proxy-revalidate`),e.setHeader(`Pragma`,`no-cache`),e.setHeader(`Expires`,`0`)}}:{maxage:365*24*60*60*1e3};e.use(e.middlewares.errorHandle),e.use((0,t.default)({threshold:2048})),e.publicsPath.forEach(t=>e.use((0,r.default)(t,n))),e.use(i),e.use(e.middlewares.requestParameterParsing),e.use(e.middlewares.projectHandler),!e.envs.isLocal()&&e.use(e.middlewares.apiSignVerify),e.use(e.middlewares.apiParamsVerify)};module.exports=a;
|
package/cjs/app/view/entry.tpl
CHANGED
|
@@ -3,22 +3,6 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<script>
|
|
7
|
-
;(() => {
|
|
8
|
-
const root = document.documentElement
|
|
9
|
-
let theme = root.classList.contains('dark') ? 'dark' : 'light'
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const storedTheme = window.localStorage.getItem('tc_theme')
|
|
13
|
-
if (storedTheme === 'light' || storedTheme === 'dark') {
|
|
14
|
-
theme = storedTheme
|
|
15
|
-
}
|
|
16
|
-
} catch (_error) {}
|
|
17
|
-
|
|
18
|
-
root.classList.toggle('dark', theme === 'dark')
|
|
19
|
-
root.style.colorScheme = theme
|
|
20
|
-
})()
|
|
21
|
-
</script>
|
|
22
6
|
<link rel="icon" type="image/x-icon" href="/static/logo.png" />
|
|
23
7
|
<title>{{ name }}</title>
|
|
24
8
|
<!-- TC_HEAD_SLOT -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../packages/common/log/index.js`),t=require(`./state.js`),n=require(`./dev.js`),r=require(`./prod.js`);var i=(i,a)=>{switch(t.setBuildFEOptions(a),i){case`dev`:return n.dev();case`prod`:return r.prod();default:return e.logRed(`Usage: npx tsx scripts/index.ts [dev|prod]`),Promise.resolve()}};exports.buildFE=i;
|
package/cjs/bundler/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./buildBE.js`),t=require(
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./buildBE.js`),t=require(`./buildFE.js`);exports.buildBE=e.buildBE,exports.buildFE=t.buildFE;
|
package/cjs/bundler/state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={options:{output:`frame
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={options:{output:`frame`,minifyHtml:!1}};function t(t){e.options={output:`frame`,minifyHtml:!1,...t}}exports.buildFESharedState=e,exports.setBuildFEOptions=t;
|
package/cjs/bundler/utils.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.js`),t=require(`../packages/
|
|
2
|
-
`))}return
|
|
3
|
-
`))},
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.js`),t=require(`../packages/common/log/index.js`),n=require(`../packages/utils/path.js`);require(`../packages/utils/index.js`);const r=require(`./state.js`);let i=require(`fs`),a=require(`path`);a=e.__toESM(a);let o=require(`glob`);o=e.__toESM(o);let s=require(`vite`),c=require(`@tailwindcss/vite`);c=e.__toESM(c);let l=require(`module`),u=require(`url`);var d=typeof __filename==`string`?__filename:(0,u.fileURLToPath)({}.url),f=a.default.dirname(d),p=process.cwd(),m=(0,l.createRequire)(d),h=a.default.resolve(f,`../../`),g=a.default.resolve(f,`../`),_=a.default.resolve(h,`./fe/frontend`),v=a.default.resolve(p,`.`),y=a.default.resolve(v,`./frontend`),b=e=>a.default.dirname(m.resolve(`${e}/package.json`)),x=a.default.join(v,`.tc-entry-html`),S=`<!-- {{InsertScript}} -->`,C={head:`<!-- TC_HEAD_SLOT -->`,bodyBeforeRoot:`<!-- TC_BODY_BEFORE_ROOT_SLOT -->`,bodyAfterRoot:`<!-- TC_BODY_AFTER_ROOT_SLOT -->`},w=[`head`,`body-before-root`,`body-after-root`],T=()=>({head:``,"body-before-root":``,"body-after-root":``}),E=e=>e.trim().replace(/<!--(?!\s*(?:tc-slot:|\/tc-slot:))[\s\S]*?-->/g,``).replace(/>\s+</g,`><`),D=e=>{let t=[],n=e.replace(/<(script|style|textarea|pre)\b[\s\S]*?<\/\1>/gi,e=>{let n=`__TC_HTML_PRESERVED_${t.length}__`;return t.push(e),n}).replace(/<!--(?!\s*(?:tc-slot:|\/tc-slot:|\{\{InsertScript\}\}|TC_))[\s\S]*?-->/g,``).replace(/>\s+</g,`><`).trim();return t.reduce((e,t,n)=>e.replace(`__TC_HTML_PRESERVED_${n}__`,t),n)},O=e=>e.split(a.default.sep).join(`/`),k=e=>{let t=O(e);return t.startsWith(`.`)||t.startsWith(`/`)?t:`./${t}`},A=e=>/<html[\s>]/i.test(e)&&e.includes(`window['_basePath']`)&&/<div\s+id=["']root["']\s*><\/div>/i.test(e),j=e=>{let n=T(),r=(0,i.readFileSync)(e,`utf-8`),a=/<!--\s*tc-slot:([\w-]+)\s*-->([\s\S]*?)<!--\s*\/tc-slot:\1\s*-->/g,o=!1,s=r;if(r.replace(a,(t,r,i)=>{if(o=!0,!w.includes(r))throw Error(`Invalid html slot "${r}" in ${e}. Allowed slots: ${w.join(`, `)}.`);return n[r]+=i.trim()?`${i}\n`:``,s=s.replace(t,``),t}),s.trim()){if(A(r))return t.logYellow(`ignored legacy generated entry html: ${e}`),T();throw Error([`Invalid custom entry html: ${e}.`,`Custom entry html can only contain tc-slot blocks, for example:`,`<!-- tc-slot:head -->`,`<meta name="description" content="..." />`,`<!-- /tc-slot:head -->`].join(`
|
|
2
|
+
`))}return o&&t.logYellow(`using custom entry html slots: ${e}`),n},M=(e,t)=>{let r=a.default.basename(e),s=[a.default.join(e,`${t}.html`),a.default.join(e,`${r}.html`)].find(e=>(0,i.existsSync)(e));if(s)return s;if(o.sync(n.resolve(e,`*.html`)).length)throw Error([`Entry html filename is not supported in ${e}.`,`Use ${t}.html or ${r}.html as a tc-slot file.`].join(`
|
|
3
|
+
`))},N=(e,t)=>e.replace(C.head,E(t.head)).replace(C.bodyBeforeRoot,E(t[`body-before-root`])).replace(C.bodyAfterRoot,E(t[`body-after-root`])),P=(e=new Date)=>{let t=(e,t=2)=>e.toString().padStart(t,`0`);return[e.getFullYear(),t(e.getMonth()+1),t(e.getDate()),t(e.getHours())].join(`/`)+`:${t(e.getMinutes())}:${t(e.getSeconds())}:${t(e.getMilliseconds(),3)}`},F=(e,t,n,r)=>{let o=a.default.join(x,`${n}.html`),s=k(a.default.relative(a.default.dirname(o),t));return(0,i.writeFileSync)(o,N(e,r).replace(S,`<script type="module" src="${s}"><\/script>`)),o};function I(){t.logWhite(`frameFEPath: `+_),t.logWhite(`runFEPath: `+y);let e=[_,y].reduce((e,t)=>{let r=[`js`,`jsx`,`ts`,`tsx`].map(e=>n.resolve(t,`/**`,`/*.`+e)),i=o.sync(r).filter(e=>e.includes(`.entry.`)).filter(e=>!e.includes(`.d.ts`));return[...e,...i]},[]),r={};if(!e.length)return r;(0,i.rmSync)(x,{recursive:!0,force:!0}),(0,i.mkdirSync)(x,{recursive:!0});let s=(0,i.readFileSync)(a.default.resolve(g,`./app/view/entry.tpl`),`utf-8`);for(let t=0;t<e.length;t++){let n=e[t],i=a.default.resolve(n,`../`),o=M(i,a.default.basename(n).replace(/\.entry\.[^.]+$/,``)),c=o?a.default.basename(o,`.html`):i.split(a.default.sep).at(-1);r[c]=F(s,n,c,o?j(o):T())}return r}function L(){let e=r.buildFESharedState.options.output??`frame`;return e!==`frame`&&t.logRed(`Please pay attention to the build output.`),e===`run`?a.default.join(v,`./app/public/dist`):typeof e==`function`?e():a.default.join(g,`./app/public/dist`)}var R=async e=>{let n=(await import(`chokidar`)).watch(y,{ignored:/(^|[\/\\])\../,persistent:!0,ignoreInitial:!0}),r=!1;n.on(`change`,async n=>{if(!r){r=!0,t.logYellow(`changed: ${n}`);try{await e?.()}catch(e){t.logRed(e)}finally{r=!1}}}),t.logYellow(`watching ${y}/...`)};function z(){return{name:`flatten-html`,apply:`build`,closeBundle(){let e=L(),t=P();(0,i.writeFileSync)(n.resolve(e,`./FEBuildKey`),t)}}}function B(e=`.html`){let n=[...new Set(Array.isArray(e)?e:[e])];return{name:`flatten-html`,apply:`build`,closeBundle(){let e=L();if(!(0,i.existsSync)(e))return;let s=r.buildFESharedState.options.minifyHtml??!1,c=o.sync(`**/*.html`,{cwd:e,dot:!0});for(let t of c){let r=a.default.join(e,t),o=(0,i.readFileSync)(r,`utf-8`),c=s?D(o):o,l=a.default.basename(t,`.html`),u=`.entry`;l.indexOf(u)===-1&&(l+=u);for(let t of n){let n=l+t,o=a.default.join(e,n);r===o?s&&(0,i.writeFileSync)(o,c):(0,i.writeFileSync)(o,c)}n.some(t=>r===a.default.join(e,l+t))||(0,i.unlinkSync)(r)}let l=[...new Set(c.map(e=>a.default.dirname(e)))].sort().reverse(),u=e=>{if(!((0,i.existsSync)(e)?(0,i.readdirSync)(e):[1]).length){(0,i.rmdirSync)(e);let t=e.split(a.default.sep).slice(0,-1);t.length&&u(t.join(a.default.sep))}};for(let t of l)t&&t!==`.`&&u(a.default.join(e,t));let d=n.flatMap(t=>o.sync(`**/*`+t,{cwd:e,dot:!0}));for(let e of d)t.logYellow(`final file path -----> `+e)}}}function V(e){let t=[``,`.ts`,`.tsx`,`.js`,`.jsx`];return e.reduce((e,[n,r,o=y,s=`./defaultAlias`])=>{let c=t.map(e=>a.default.resolve(o,r+e)).find(e=>(0,i.existsSync)(e)),l=a.default.resolve(f,s);return c&&(l=c),e[n]=l,e},{})}var H=(...e)=>new RegExp(e.join(`[\\/]`)),U=e=>`_${e}_tcC`,W=async(e,n)=>{try{await(0,s.build)({configFile:!1,base:`/dist`,plugins:[(await import(`@vitejs/plugin-react`)).default(),B([`.tpl`]),z(),(0,c.default)()],build:{outDir:L(),emptyOutDir:!0,sourcemap:n===`dev`?`inline`:`hidden`,rolldownOptions:{input:e,output:{codeSplitting:{groups:[{name:U(`__nm`),test:H(`node_modules`),maxSize:5e5,minShareCount:2,priority:1},{name:U(`r`),test:H(`node_modules`,`react`),minShareCount:2,maxSize:5e5,priority:5},{name:U(`r-d`),test:H(`node_modules`,`react-dom`),minShareCount:2,maxSize:2e5,priority:10},{name:U(`r-r-d`),test:H(`node_modules`,`react-router-dom`),minShareCount:2,maxSize:5e5,priority:10},{name:U(`ajv`),test:H(`node_modules`,`ajv`),minShareCount:2,maxSize:2e5,priority:10},{name:U(`ui-lib`),test:H(`components`),minShareCount:2,maxSize:2e5,priority:5},{name:U(`ui-f-lib`),test:H(`components`,`.*?[Ff]orm.*?`),minShareCount:2,maxSize:5e4,priority:10}]}}}},resolve:{alias:{react:b(`react`),"react-dom":b(`react-dom`),"@tc/ui-react/hooks":a.default.resolve(f,`../../fe/packages/react/hooks/`),"@tc/ui-react":a.default.resolve(f,`../../fe/packages/react/ui/`),...V([[`@tc/scalability/SchemaForm/frameData`,`./extended/SchemaForm/data`,_],[`@tc/scalability/SchemaForm/data`,`./extended/SchemaForm/data`],[`@tc/scalability/SchemaPage/CallCom/data`,`./extended/SchemaPage/CallCom/data`],[`@tc/scalability/SchemaPage/SchemaTable/data`,`./extended/SchemaPage/SchemaTable/data`],[`@tc/scalability/dash/customRoutes`,`./extended/dash/customRoutes`],[`@tc/scalability/dash/init`,`./extended/dash/init`],[`@tc/scalability/dash/components`,`./extended/dash/components`],[`@tc/scalability/dash/routeGuard`,`./extended/dash/routeGuard`,void 0,`./defaultRouteGuard`]])}}}),t.logJoinColorized(t.colorize(`builded`,`green`),t.colorize(` -> `,`white`),t.colorize(`time: `,`cyan`),t.colorize(new Date,`pink`))}catch(e){if(t.logRed(e),n!==`dev`)throw e}};exports.VBuildFE=W,exports.entries=I,exports.generateBuildKeyPlugin=z,exports.outDir=L,exports.watchFiles=R;
|