@_tc/template-core 0.0.1-bate.58 → 0.0.1-bate.59

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.
Files changed (33) hide show
  1. package/.skills/tc-component-usage-skills/SKILL.md +5 -0
  2. package/.skills/tc-component-usage-skills/reference/component-api.md +3 -0
  3. package/.skills/tc-generator/reference/example.md +4 -0
  4. package/.skills/tc-generator/reference/project-template/config/config.default.js +4 -0
  5. package/AGENT_README.md +11 -0
  6. package/README.md +21 -2
  7. package/cjs/config/config.default.js +1 -1
  8. package/cjs/packages/core/index.js +1 -1
  9. package/cjs/packages/core/loader/middleware.js +1 -1
  10. package/esm/app/{middleware → middlewares}/api-params-verify.js +1 -1
  11. package/esm/app/{middleware → middlewares}/api-sign-verify.js +1 -1
  12. package/esm/app/{middleware → middlewares}/error-handle.js +1 -1
  13. package/esm/app/{middleware → middlewares}/project-handler.js +1 -1
  14. package/esm/app/{middleware → middlewares}/request-parameter-parsing.js +1 -1
  15. package/esm/config/config.default.js +4 -0
  16. package/esm/packages/core/index.js +37 -33
  17. package/esm/packages/core/loader/middleware.js +1 -1
  18. package/fe/frontend/src/common/request.js +1 -1
  19. package/fe/packages/react/ui/components/Input/Input.js +93 -39
  20. package/fe/packages/react/ui/components/testPage/index.js +29 -3
  21. package/package.json +1 -1
  22. package/types/config/config.default.d.ts +27 -1
  23. package/types/packages/core/loader/config.d.ts +1 -1
  24. /package/cjs/app/{middleware → middlewares}/api-params-verify.js +0 -0
  25. /package/cjs/app/{middleware → middlewares}/api-sign-verify.js +0 -0
  26. /package/cjs/app/{middleware → middlewares}/error-handle.js +0 -0
  27. /package/cjs/app/{middleware → middlewares}/project-handler.js +0 -0
  28. /package/cjs/app/{middleware → middlewares}/request-parameter-parsing.js +0 -0
  29. /package/types/app/{middleware → middlewares}/api-params-verify.d.ts +0 -0
  30. /package/types/app/{middleware → middlewares}/api-sign-verify.d.ts +0 -0
  31. /package/types/app/{middleware → middlewares}/error-handle.d.ts +0 -0
  32. /package/types/app/{middleware → middlewares}/project-handler.d.ts +0 -0
  33. /package/types/app/{middleware → middlewares}/request-parameter-parsing.d.ts +0 -0
@@ -31,8 +31,13 @@ Every input-like component supports both modes. Check for `value !== undefined`
31
31
 
32
32
  // Uncontrolled
33
33
  <Input defaultValue="hello" />
34
+
35
+ // Password fields get a built-in show/hide toggle
36
+ <Input type="password" value={password} onChange={setPassword} />
34
37
  ```
35
38
 
39
+ For `type="password"`, do not add a separate eye icon or clear button. The built-in password toggle is automatic; `allowClear` is hidden on password inputs.
40
+
36
41
  ### Form Integration (rc-field-form)
37
42
 
38
43
  `Form` / `FormItem` / `SchemaForm` wrap `rc-field-form`. `FormItem` auto-clones children to inject `value`/`onChange`. Prefer `SchemaForm` for data-driven forms:
@@ -45,12 +45,15 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, '
45
45
  onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void
46
46
  allowClear?: boolean | { clearIcon: ReactNode }
47
47
  addonAfter?: ReactNode
48
+ inputClassName?: string
48
49
  className?: string
49
50
  }
50
51
  ```
51
52
 
52
53
  Note: `onChange` callback receives `(stringValue, event)` — two arguments, not one.
53
54
 
55
+ Use `type="password"` for password fields. The component shows a built-in visibility toggle, supports controlled and uncontrolled values, and removes the native DOM `value` attribute while preserving the input value property. `allowClear` is intentionally hidden for password inputs. `addonAfter` shares the right-side accessory area with the password toggle.
56
+
54
57
  ---
55
58
 
56
59
  ## InputNumber
@@ -48,6 +48,10 @@ main().catch((error) => {
48
48
  ```js
49
49
  module.exports = {
50
50
  port: 9000,
51
+ auth: {
52
+ ATKey: "Authorization",
53
+ RTKey: "RT",
54
+ },
51
55
  };
52
56
  ```
53
57
 
@@ -1,5 +1,9 @@
1
1
  module.exports = {
2
2
  port: 9000,
3
+ auth: {
4
+ ATKey: "Authorization",
5
+ RTKey: "RT",
6
+ },
3
7
  // Optional: override the default `.template-core/template-core.sqlite` path.
4
8
  // db: {
5
9
  // path: "data/app.sqlite",
package/AGENT_README.md CHANGED
@@ -74,9 +74,15 @@ main().catch((error) => {
74
74
  // config/config.default.ts
75
75
  export default {
76
76
  port: 9000,
77
+ auth: {
78
+ ATKey: 'Authorization',
79
+ RTKey: 'RT',
80
+ },
77
81
  }
78
82
  ```
79
83
 
84
+ `auth.ATKey` / `auth.RTKey` 是业务鉴权中间件读取短 token 和刷新 token header 的默认约定;默认分别是 `Authorization` / `RT`。
85
+
80
86
  访问内置后台:
81
87
 
82
88
  ```text
@@ -93,6 +99,7 @@ http://localhost:9000/dash?projk=demo
93
99
  app/controller/**/*.(js|ts) -> app.controller
94
100
  app/service/**/*.(js|ts) -> app.service
95
101
  app/middleware/**/*.(js|ts) -> app.middlewares
102
+ app/middleware.(js|ts) -> 全局中间件编排,按 frame -> business 顺序执行
96
103
  app/router/**/*.(js|ts) -> Koa router
97
104
  app/router-guard.(js|ts) -> 兜底路由守卫
98
105
  app/router-schema/**/*.(js|ts) -> app.routerSchema
@@ -378,10 +385,14 @@ import { Button, DataTable, Form, Input, Modal, Select } from '@_tc/template-cor
378
385
  常用前端能力:
379
386
 
380
387
  - 请求方法:`request`、`get`、`post`、`put`、`patch`、`del`。
388
+ - 请求 header:自动添加 `s_t`、`s_sign`、`projk`;存在短 token 时添加 `Authorization: Bearer ${token}`。
389
+ - 本地 key:短 token 是 `localStorage.auth_token`,项目 key 是 `localStorage.p_J_k`;刷新 token header 默认名是 `RT`。
390
+ - `getAuthToken()` 是同步函数,读取前无需 `await`。
381
391
  - Schema 通信:`schemaEventBus`、`eventsInfo`、`merge`。
382
392
  - 共享状态:`modeStore`、`schemaStore`、`apiFreezerStore` 及对应 hooks。
383
393
  - 多语言:`i18nStore.getState().addResources()`,配置文案可写 `$i18n::...`。
384
394
  - 主题:`ThemeSwitch` 会同步根节点 `dark` class 和 `localStorage`。
395
+ - `Input type="password"` 会自动显示密码显隐按钮;`allowClear` 不作用于密码输入。
385
396
 
386
397
  不要直接依赖 `frontend/src/...`、`packages/react/ui/...` 这类包内部路径。
387
398
 
package/README.md CHANGED
@@ -101,7 +101,22 @@ main().catch((error) => {
101
101
  | `homePage` | `string` | 否 | - | 应用首页路径 |
102
102
  | `additionalPublicPaths` | `string | string[]` | 否 | - | 追加静态资源目录和 Nunjucks 模板查找目录,必须传绝对路径 |
103
103
 
104
- 框架会自动读取 `config/config.default.ts` 中的配置(如 `port`、`env` 等)。
104
+ 框架会自动读取 `config/config.default.ts` 中的配置(如 `port`、`env`、`auth` 等)。
105
+
106
+ 常用默认配置示例:
107
+
108
+ ```ts
109
+ // config/config.default.ts
110
+ export default {
111
+ port: 9000,
112
+ auth: {
113
+ ATKey: 'Authorization',
114
+ RTKey: 'RT',
115
+ },
116
+ }
117
+ ```
118
+
119
+ `auth.ATKey` / `auth.RTKey` 用于约定业务鉴权中间件读取短 token 和刷新 token 的 header 名;默认分别为 `Authorization` 和 `RT`。
105
120
 
106
121
  静态资源挂载和 Nunjucks 模板查找顺序均为:框架 `app/public`、项目 `app/public`、`additionalPublicPaths`。当 `buildFE` 输出到自定义目录时,可以用 `additionalPublicPaths` 把该目录交给 Koa 提供静态资源访问和模板渲染。
107
122
 
@@ -506,6 +521,8 @@ export function Toolbar() {
506
521
  }
507
522
  ```
508
523
 
524
+ `Authorization` 默认对应服务端配置 `config.auth.ATKey`。前端本地 token 存在 `localStorage.auth_token`,项目 key 存在 `localStorage.p_J_k`;刷新 token header 名预留为 `config.auth.RTKey`,默认是 `RT`。
525
+
509
526
  **签名机制**:
510
527
  - 签名密钥:服务端从 `app.config.signKey` 读取,并在页面渲染时注入为 `window._signKey`
511
528
  - 签名算法:`md5(签名密钥_${时间戳})`
@@ -583,7 +600,7 @@ try {
583
600
  | 组件 | 用途 | 导入路径 |
584
601
  | --- | --- | --- |
585
602
  | `Button` | 按钮组件,支持多种样式和尺寸 | `@_tc/template-core/fe/rc` |
586
- | `Input` | 输入框组件 | `@_tc/template-core/fe/rc` |
603
+ | `Input` | 输入框组件,支持清空、右侧附加内容和密码显隐 | `@_tc/template-core/fe/rc` |
587
604
  | `InputNumber` | 数字输入框组件 | `@_tc/template-core/fe/rc` |
588
605
  | `Textarea` | 多行文本输入框组件 | `@_tc/template-core/fe/rc` |
589
606
  | `Select` | 下拉选择组件,支持单选和多选 | `@_tc/template-core/fe/rc` |
@@ -666,6 +683,7 @@ function MyComponent() {
666
683
  </Button>
667
684
 
668
685
  <Input placeholder="请输入内容" />
686
+ <Input type="password" placeholder="请输入密码" />
669
687
 
670
688
  <Select
671
689
  options={[
@@ -691,6 +709,7 @@ function MyComponent() {
691
709
  app/controller/**/*.(js|ts) -> app.controller
692
710
  app/service/**/*.(js|ts) -> app.service
693
711
  app/middleware/**/*.(js|ts) -> app.middlewares
712
+ app/middleware.(js|ts) -> 全局中间件编排,按框架 -> 项目顺序执行
694
713
  app/router/**/*.(js|ts) -> Koa router
695
714
  app/router-schema/**/*.(js|ts) -> app.routerSchema
696
715
  app/extend/*.(js|ts) -> app.extends
@@ -1 +1 @@
1
- var e={frameName:`TemplateCore`,signKey:`sakjdfnkjwjfnfkjkjldljksndf`,apiSignVerify:{whiteList:[]},notFoundRedirectCount:5};module.exports=e;
1
+ var e={frameName:`TemplateCore`,signKey:`sakjdfnkjwjfnfkjkjldljksndf`,apiSignVerify:{whiteList:[]},auth:{ATKey:`Authorization`,RTKey:`RT`},notFoundRedirectCount:5};module.exports=e;
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../_virtual/_rolldown/runtime.js`),t=require(`./env.js`),n=require(`../utils/path.js`),r=require(`../utils/runFileFn.js`);require(`../utils/index.js`);const i=require(`./loader/config.js`),a=require(`./loader/controller.js`),o=require(`./loader/extend.js`),s=require(`./loader/middleware.js`),c=require(`./loader/model.js`),l=require(`./loader/router.js`),u=require(`./loader/router-schema.js`),d=require(`./loader/service.js`);let f=require(`koa`);f=e.__toESM(f,1);var p=c,m=async(e={})=>{let r=new f.default;r.hooks={},r.hooks.initing?.();let{frameBaseDir:c,baseDir:p,...m}=e;r.options=m,r.options.pageBasePage=r.options.pageBasePage??`/`,r.options.apiPrefix=r.options.apiPrefix||`/api`;let g=r.baseDir=p??process.cwd(),_=r.frameBaseDir=c??n.resolve(__dirname,`..`);r.businessAppPath=n.resolve(g,`app`),r.frameAppPath=n.resolve(_,`app`),r.paths=[{type:`frame`,path:r.frameAppPath},{type:`business`,path:r.businessAppPath}],r.envs=t(),console.log(`-- [init] env: ${r.envs.get()} --`),i(r),console.log(`-- [init] loader config done --`),await o(r),console.log(`-- [init] loader extend done --`),u(r),console.log(`-- [init] loader routerSchema done --`),d(r),console.log(`-- [init] loader service done --`),a(r),console.log(`-- [init] loader controller done --`),s(r),console.log(`-- [init] loader middleware done --`),h(r),l(r),console.log(`-- [init] loader router done --`);try{let e=r.config.port||process.env.PORT||`8080`,n=parseInt(e+``,10),i=process.env.IP||`0.0.0.0`;r.listen(n,i),console.log(`Server port: http://localhost:${n}`),console.log(`run env is ${t().get()}`)}catch(e){console.error(e)}return r.hooks.inited?.(),r},h=e=>{e.paths.forEach(t=>{for(let i of[`ts`,`js`]){let a=`middleware.${i}`,o=n.resolve(t.path,a);try{r.runFileFn(o)?.(e),console.log(`-- [init] loader ${t.type} appMiddleware ${a} done -- (${t.path})`);break}catch(e){let n=e;if(!(n?.code===`MODULE_NOT_FOUND`&&typeof n?.message==`string`&&(n.message.includes(`Cannot find module '${o}'`)||n.message.includes(`Cannot find module "${o}"`))))throw console.log(`-- [error] loader ${t.type} appMiddleware ${a} error -- (${o})`),e}}})};exports.loaderModel=p,exports.start=m;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../_virtual/_rolldown/runtime.js`),t=require(`./paths.js`),n=require(`./env.js`),r=require(`../utils/path.js`),i=require(`../utils/runFileFn.js`);require(`../utils/index.js`);const a=require(`./loader/config.js`),o=require(`./loader/controller.js`),s=require(`./loader/extend.js`),c=require(`./loader/middleware.js`),l=require(`./loader/model.js`),u=require(`./loader/router.js`),d=require(`./loader/router-schema.js`),f=require(`./loader/service.js`);let p=require(`koa`);p=e.__toESM(p,1);var m=l,h=async(e={})=>{let t=new p.default;t.hooks={},t.hooks.initing?.();let{frameBaseDir:i,baseDir:l,...m}=e;t.options=m,t.options.pageBasePage=t.options.pageBasePage??`/`,t.options.apiPrefix=t.options.apiPrefix||`/api`;let h=t.baseDir=l??process.cwd(),_=t.frameBaseDir=i??r.resolve(__dirname,`..`);t.businessAppPath=r.resolve(h,`app`),t.frameAppPath=r.resolve(_,`app`),t.paths=[{type:`frame`,path:t.frameAppPath},{type:`business`,path:t.businessAppPath}],t.envs=n(),console.log(`-- [init] env: ${t.envs.get()} --`),a(t),console.log(`-- [init] loader config done --`),await s(t),console.log(`-- [init] loader extend done --`),d(t),console.log(`-- [init] loader routerSchema done --`),f(t),console.log(`-- [init] loader service done --`),o(t),console.log(`-- [init] loader controller done --`),c(t),console.log(`-- [init] loader middleware done --`),g(t),u(t),console.log(`-- [init] loader router done --`);try{let e=t.config.port||process.env.PORT||`8080`,r=parseInt(e+``,10),i=process.env.IP||`0.0.0.0`;t.listen(r,i),console.log(`Server port: http://localhost:${r}`),console.log(`run env is ${n().get()}`)}catch(e){console.error(e)}return t.hooks.inited?.(),t},g=e=>{[`frame`,`business`].map(n=>({type:n,path:t.getAppPath(e,n)})).forEach(t=>{for(let n of[`ts`,`js`]){let a=`middleware.${n}`,o=r.resolve(t.path,a);try{i.runFileFn(o)?.(e),console.log(`-- [init] loader ${t.type} appMiddleware ${a} done -- (${t.path})`);break}catch(e){let n=e;if(!(n?.code===`MODULE_NOT_FOUND`&&typeof n?.message==`string`&&(n.message.includes(`Cannot find module '${o}'`)||n.message.includes(`Cannot find module "${o}"`))))throw console.log(`-- [error] loader ${t.type} appMiddleware ${a} error -- (${o})`),e}}})};exports.loaderModel=m,exports.start=h;
@@ -1 +1 @@
1
- const e=require(`../paths.js`),t=require(`../../utils/loadFile.js`),n=require(`../../utils/runFileFn.js`);require(`../../utils/index.js`);var r=r=>{r.middlewares=e.getAppPaths(r).reduce((e,i)=>{let a=t.loadFileFn(i,`middleware`,{loadFileCallback:e=>n.runFileFn(e)(r)});return{...e,...a}},{})};module.exports=r;
1
+ const e=require(`../paths.js`),t=require(`../../utils/loadFile.js`),n=require(`../../utils/runFileFn.js`);require(`../../utils/index.js`);var r=r=>{r.middlewares=e.getAppPaths(r).reduce((e,i)=>{let a=t.loadFileFn(i,`middlewares`,{loadFileCallback:e=>n.runFileFn(e)(r)});return{...e,...a}},{})};module.exports=r;
@@ -1,5 +1,5 @@
1
1
  import e from "ajv";
2
- //#region app/middleware/api-params-verify.ts
2
+ //#region app/middlewares/api-params-verify.ts
3
3
  var t = new e(), n = "http://json-schema.org/draft-07/schema#", r = (e, r, i) => {
4
4
  if (r[e] && i[e]) {
5
5
  i[e].$schema = n;
@@ -1,5 +1,5 @@
1
1
  import e from "md5";
2
- //#region app/middleware/api-sign-verify.ts
2
+ //#region app/middlewares/api-sign-verify.ts
3
3
  var t = 1e3 * 60 * 10, n = (n) => async (r, i) => {
4
4
  let { path: a, method: o } = r;
5
5
  if (a.includes(n.options.apiPrefix) && !n.config?.apiSignVerify?.whiteList?.includes(a)) {
@@ -1,4 +1,4 @@
1
- //#region app/middleware/error-handle.ts
1
+ //#region app/middlewares/error-handle.ts
2
2
  var e = {}, t = (t) => async (n, r) => {
3
3
  try {
4
4
  await r();
@@ -1,4 +1,4 @@
1
- //#region app/middleware/project-handler.ts
1
+ //#region app/middlewares/project-handler.ts
2
2
  var e = (e) => async (t, n) => {
3
3
  let r = t.headers.projk, { path: i } = t;
4
4
  if (i.indexOf(`${e.options.apiPrefix}/proj/`) !== -1 && (!r || ["undefined", "null"].includes(r))) {
@@ -1,4 +1,4 @@
1
- //#region app/middleware/request-parameter-parsing.ts
1
+ //#region app/middlewares/request-parameter-parsing.ts
2
2
  var e = (e) => typeof e == "object" && !!e && !Array.isArray(e), t = ((t) => async (n, r) => {
3
3
  if (n.path.includes(`${t.options.apiPrefix}/`)) {
4
4
  let { query: t, headers: r } = n, i = n.request.body;
@@ -3,6 +3,10 @@ var e = {
3
3
  frameName: "TemplateCore",
4
4
  signKey: "sakjdfnkjwjfnfkjkjldljksndf",
5
5
  apiSignVerify: { whiteList: [] },
6
+ auth: {
7
+ ATKey: "Authorization",
8
+ RTKey: "RT"
9
+ },
6
10
  notFoundRedirectCount: 5
7
11
  };
8
12
  //#endregion
@@ -1,50 +1,54 @@
1
- import e from "./env.js";
2
- import { resolve as t } from "../utils/path.js";
3
- import { runFileFn as n } from "../utils/runFileFn.js";
1
+ import { getAppPath as e } from "./paths.js";
2
+ import t from "./env.js";
3
+ import { resolve as n } from "../utils/path.js";
4
+ import { runFileFn as r } from "../utils/runFileFn.js";
4
5
  import "../utils/index.js";
5
- import r from "./loader/config.js";
6
- import i from "./loader/controller.js";
7
- import a from "./loader/extend.js";
8
- import o from "./loader/middleware.js";
9
- import s from "./loader/model.js";
10
- import c from "./loader/router.js";
11
- import l from "./loader/router-schema.js";
12
- import u from "./loader/service.js";
13
- import d from "koa";
6
+ import i from "./loader/config.js";
7
+ import a from "./loader/controller.js";
8
+ import o from "./loader/extend.js";
9
+ import s from "./loader/middleware.js";
10
+ import c from "./loader/model.js";
11
+ import l from "./loader/router.js";
12
+ import u from "./loader/router-schema.js";
13
+ import d from "./loader/service.js";
14
+ import f from "koa";
14
15
  //#region packages/core/index.ts
15
- var f = s, p = async (n = {}) => {
16
- let s = new d();
17
- s.hooks = {}, s.hooks.initing?.();
18
- let { frameBaseDir: f, baseDir: p, ...h } = n;
19
- s.options = h, s.options.pageBasePage = s.options.pageBasePage ?? "/", s.options.apiPrefix = s.options.apiPrefix || "/api";
20
- let g = s.baseDir = p ?? process.cwd(), _ = s.frameBaseDir = f ?? t(__dirname, "..");
21
- s.businessAppPath = t(g, "app"), s.frameAppPath = t(_, "app"), s.paths = [{
16
+ var p = c, m = async (e = {}) => {
17
+ let r = new f();
18
+ r.hooks = {}, r.hooks.initing?.();
19
+ let { frameBaseDir: c, baseDir: p, ...m } = e;
20
+ r.options = m, r.options.pageBasePage = r.options.pageBasePage ?? "/", r.options.apiPrefix = r.options.apiPrefix || "/api";
21
+ let g = r.baseDir = p ?? process.cwd(), _ = r.frameBaseDir = c ?? n(__dirname, "..");
22
+ r.businessAppPath = n(g, "app"), r.frameAppPath = n(_, "app"), r.paths = [{
22
23
  type: "frame",
23
- path: s.frameAppPath
24
+ path: r.frameAppPath
24
25
  }, {
25
26
  type: "business",
26
- path: s.businessAppPath
27
- }], s.envs = e(), console.log(`-- [init] env: ${s.envs.get()} --`), r(s), console.log("-- [init] loader config done --"), await a(s), console.log("-- [init] loader extend done --"), l(s), console.log("-- [init] loader routerSchema done --"), u(s), console.log("-- [init] loader service done --"), i(s), console.log("-- [init] loader controller done --"), o(s), console.log("-- [init] loader middleware done --"), m(s), c(s), console.log("-- [init] loader router done --");
27
+ path: r.businessAppPath
28
+ }], r.envs = t(), console.log(`-- [init] env: ${r.envs.get()} --`), i(r), console.log("-- [init] loader config done --"), await o(r), console.log("-- [init] loader extend done --"), u(r), console.log("-- [init] loader routerSchema done --"), d(r), console.log("-- [init] loader service done --"), a(r), console.log("-- [init] loader controller done --"), s(r), console.log("-- [init] loader middleware done --"), h(r), l(r), console.log("-- [init] loader router done --");
28
29
  try {
29
- let t = s.config.port || process.env.PORT || "8080", n = parseInt(t + "", 10), r = process.env.IP || "0.0.0.0";
30
- s.listen(n, r), console.log(`Server port: http://localhost:${n}`), console.log(`run env is ${e().get()}`);
30
+ let e = r.config.port || process.env.PORT || "8080", n = parseInt(e + "", 10), i = process.env.IP || "0.0.0.0";
31
+ r.listen(n, i), console.log(`Server port: http://localhost:${n}`), console.log(`run env is ${t().get()}`);
31
32
  } catch (e) {
32
33
  console.error(e);
33
34
  }
34
- return s.hooks.inited?.(), s;
35
- }, m = (e) => {
36
- e.paths.forEach((r) => {
35
+ return r.hooks.inited?.(), r;
36
+ }, h = (t) => {
37
+ ["frame", "business"].map((n) => ({
38
+ type: n,
39
+ path: e(t, n)
40
+ })).forEach((e) => {
37
41
  for (let i of ["ts", "js"]) {
38
- let a = `middleware.${i}`, o = t(r.path, a);
42
+ let a = `middleware.${i}`, o = n(e.path, a);
39
43
  try {
40
- n(o)?.(e), console.log(`-- [init] loader ${r.type} appMiddleware ${a} done -- (${r.path})`);
44
+ r(o)?.(t), console.log(`-- [init] loader ${e.type} appMiddleware ${a} done -- (${e.path})`);
41
45
  break;
42
- } catch (e) {
43
- let t = e;
44
- if (!(t?.code === "MODULE_NOT_FOUND" && typeof t?.message == "string" && (t.message.includes(`Cannot find module '${o}'`) || t.message.includes(`Cannot find module "${o}"`)))) throw console.log(`-- [error] loader ${r.type} appMiddleware ${a} error -- (${o})`), e;
46
+ } catch (t) {
47
+ let n = t;
48
+ if (!(n?.code === "MODULE_NOT_FOUND" && typeof n?.message == "string" && (n.message.includes(`Cannot find module '${o}'`) || n.message.includes(`Cannot find module "${o}"`)))) throw console.log(`-- [error] loader ${e.type} appMiddleware ${a} error -- (${o})`), t;
45
49
  }
46
50
  }
47
51
  });
48
52
  };
49
53
  //#endregion
50
- export { f as loaderModel, p as start };
54
+ export { p as loaderModel, m as start };
@@ -5,7 +5,7 @@ import "../../utils/index.js";
5
5
  //#region packages/core/loader/middleware.ts
6
6
  var r = (r) => {
7
7
  r.middlewares = e(r).reduce((e, i) => {
8
- let a = t(i, "middleware", { loadFileCallback: (e) => n(e)(r) });
8
+ let a = t(i, "middlewares", { loadFileCallback: (e) => n(e)(r) });
9
9
  return {
10
10
  ...e,
11
11
  ...a
@@ -96,7 +96,7 @@ var removeBaseurlFromURL = (config) => {
96
96
  };
97
97
  api.interceptors.request.use(async (config) => {
98
98
  removeBaseurlFromURL(config);
99
- const token = await getAuthToken();
99
+ const token = getAuthToken();
100
100
  const aauto = getApiAuth();
101
101
  const time = (/* @__PURE__ */ new Date()).getTime();
102
102
  config.headers = {
@@ -1,24 +1,55 @@
1
1
  import { cn } from "../../lib/utils.js";
2
2
  import { useInputController } from "../hooks/useInputController.js";
3
- import { forwardRef, useCallback, useMemo } from "react";
3
+ import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { X } from "lucide-react";
5
+ import { Eye, EyeOff, X } from "lucide-react";
6
6
  //#region packages/react/ui/components/Input/Input.tsx
7
7
  var Input = forwardRef(({ className, inputClassName, type, value: controlledValue, onChange, defaultValue, allowClear, disabled, addonAfter, ...props }, ref) => {
8
+ const isFileInput = type === "file";
9
+ const isPasswordInput = type === "password";
8
10
  const { setInternalValue, setIsFocused, setRefs, controllerRef, isControlledRef, isFocused, value, hasValue } = useInputController({
9
11
  defaultValue,
10
12
  controlledValue,
11
13
  ref
12
14
  });
13
- const isFileInput = type === "file";
15
+ const [innerType, setInnerType] = useState(type);
16
+ const isMountedRef = useRef(false);
17
+ const currentType = isPasswordInput ? innerType ?? "password" : type;
18
+ const isPasswordVisible = currentType === "text";
19
+ const removePasswordValueAttribute = useCallback(() => {
20
+ if (!isPasswordInput) return;
21
+ Promise.resolve().then(() => {
22
+ if (!isMountedRef.current) return;
23
+ const input = controllerRef.current;
24
+ if (input?.getAttribute("type") === "password" && input.hasAttribute("value")) input.removeAttribute("value");
25
+ });
26
+ }, [controllerRef, isPasswordInput]);
27
+ useEffect(() => {
28
+ isMountedRef.current = true;
29
+ return () => {
30
+ isMountedRef.current = false;
31
+ };
32
+ }, []);
33
+ useEffect(() => {
34
+ setInnerType(type);
35
+ }, [type]);
36
+ useEffect(() => {
37
+ removePasswordValueAttribute();
38
+ }, [
39
+ currentType,
40
+ removePasswordValueAttribute,
41
+ value
42
+ ]);
14
43
  const handleChange = useCallback((e) => {
15
44
  const newValue = e.target.value;
45
+ removePasswordValueAttribute();
16
46
  if (!isFileInput && !isControlledRef.current) setInternalValue(newValue);
17
47
  onChange?.(newValue, e);
18
48
  }, [
19
49
  isFileInput,
20
50
  isControlledRef,
21
51
  onChange,
52
+ removePasswordValueAttribute,
22
53
  setInternalValue
23
54
  ]);
24
55
  const handleClear = useCallback((e) => {
@@ -36,6 +67,12 @@ var Input = forwardRef(({ className, inputClassName, type, value: controlledValu
36
67
  onChange,
37
68
  setInternalValue
38
69
  ]);
70
+ const handleTogglePassword = useCallback((e) => {
71
+ e.preventDefault();
72
+ e.stopPropagation();
73
+ if (isPasswordVisible) removePasswordValueAttribute();
74
+ setInnerType((current) => current === "password" ? "text" : "password");
75
+ }, [isPasswordVisible, removePasswordValueAttribute]);
39
76
  const clearIcon = useMemo(() => {
40
77
  if (allowClear) if (typeof allowClear === "boolean") return /* @__PURE__ */ jsx("span", {
41
78
  className: cn("w-5 h-5 rounded-full", "bg-muted hover:bg-muted/80", "text-foreground", "transition-colors", "flex items-center justify-center", "focus:outline-none"),
@@ -43,44 +80,61 @@ var Input = forwardRef(({ className, inputClassName, type, value: controlledValu
43
80
  });
44
81
  else return allowClear.clearIcon;
45
82
  }, [allowClear]);
46
- const showClearIcon = hasValue && clearIcon && !disabled;
83
+ const showClearIcon = !isPasswordInput && hasValue && clearIcon && !disabled;
84
+ const showPasswordToggle = isPasswordInput && !disabled;
85
+ const rightAccessoryCount = Number(Boolean(showClearIcon)) + Number(showPasswordToggle) + Number(Boolean(addonAfter));
86
+ const hasRightAccessories = rightAccessoryCount > 0;
47
87
  return /* @__PURE__ */ jsxs("div", {
48
88
  className: cn("tc-ui-input relative w-full", className),
49
- children: [
50
- /* @__PURE__ */ jsx("input", {
51
- type,
52
- ref: setRefs,
53
- ...!isFileInput && { value },
54
- onChange: handleChange,
55
- className: cn("flex h-10 w-full rounded-lg px-3 py-0 text-sm", "border transition-colors duration-200", "placeholder:text-muted-foreground", "focus:outline-none", showClearIcon && "pr-10", addonAfter && "pr-12", disabled ? "cursor-not-allowed bg-muted border-border text-muted-foreground" : "bg-background", !disabled && !isFocused && "border-border", !disabled && isFocused && "border-theme ring-2 ring-theme/25", inputClassName),
56
- onFocus: (e) => {
57
- if (!disabled) {
58
- setIsFocused(true);
59
- props.onFocus?.(e);
60
- }
61
- },
62
- onBlur: (e) => {
63
- if (!disabled) {
64
- setIsFocused(false);
65
- props.onBlur?.(e);
66
- }
67
- },
68
- disabled,
69
- ...props
70
- }),
71
- showClearIcon && /* @__PURE__ */ jsx("button", {
72
- type: "button",
73
- onClick: handleClear,
74
- "aria-label": "Clear input",
75
- title: "Clear input",
76
- className: cn("absolute right-3 top-1/2 -translate-y-1/2"),
77
- children: clearIcon
78
- }),
79
- addonAfter && /* @__PURE__ */ jsx("div", {
80
- className: "absolute right-2 top-1/2 -translate-y-1/2",
81
- children: addonAfter
82
- })
83
- ]
89
+ children: [/* @__PURE__ */ jsx("input", {
90
+ type: currentType,
91
+ ref: setRefs,
92
+ ...!isFileInput && { value },
93
+ onChange: handleChange,
94
+ className: cn("flex h-10 w-full rounded-lg px-3 py-0 text-sm", "border transition-colors duration-200", "placeholder:text-muted-foreground", "focus:outline-none", rightAccessoryCount === 1 && "pr-10", rightAccessoryCount === 2 && "pr-20", rightAccessoryCount >= 3 && "pr-28", disabled ? "cursor-not-allowed bg-muted border-border text-muted-foreground" : "bg-background", !disabled && !isFocused && "border-border", !disabled && isFocused && "border-theme ring-2 ring-theme/25", inputClassName),
95
+ onFocus: (e) => {
96
+ if (!disabled) {
97
+ removePasswordValueAttribute();
98
+ setIsFocused(true);
99
+ props.onFocus?.(e);
100
+ }
101
+ },
102
+ onBlur: (e) => {
103
+ if (!disabled) {
104
+ removePasswordValueAttribute();
105
+ setIsFocused(false);
106
+ props.onBlur?.(e);
107
+ }
108
+ },
109
+ disabled,
110
+ ...props
111
+ }), hasRightAccessories && /* @__PURE__ */ jsxs("div", {
112
+ className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-1",
113
+ children: [
114
+ showClearIcon && /* @__PURE__ */ jsx("button", {
115
+ type: "button",
116
+ onMouseDown: (e) => e.preventDefault(),
117
+ onClick: handleClear,
118
+ "aria-label": "Clear input",
119
+ title: "Clear input",
120
+ className: "flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-foreground/8",
121
+ children: clearIcon
122
+ }),
123
+ showPasswordToggle && /* @__PURE__ */ jsx("button", {
124
+ type: "button",
125
+ onMouseDown: (e) => e.preventDefault(),
126
+ onClick: handleTogglePassword,
127
+ "aria-label": isPasswordVisible ? "Hide password" : "Show password",
128
+ title: isPasswordVisible ? "Hide password" : "Show password",
129
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-foreground/8 hover:text-foreground",
130
+ children: isPasswordVisible ? /* @__PURE__ */ jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx(Eye, { size: 16 })
131
+ }),
132
+ addonAfter && /* @__PURE__ */ jsx("div", {
133
+ className: "flex h-7 items-center text-muted-foreground",
134
+ children: addonAfter
135
+ })
136
+ ]
137
+ })]
84
138
  });
85
139
  });
86
140
  Input.displayName = "Input";
@@ -209,14 +209,40 @@ function InputDemo() {
209
209
  return /* @__PURE__ */ jsxs(Section, {
210
210
  title: "Input",
211
211
  children: [
212
- /* @__PURE__ */ jsx(Input, { placeholder: "普通输入框" }),
212
+ /* @__PURE__ */ jsx(Input, {
213
+ placeholder: "普通输入框",
214
+ className: "w-48"
215
+ }),
216
+ /* @__PURE__ */ jsx(Input, {
217
+ allowClear: true,
218
+ defaultValue: "可清空内容",
219
+ placeholder: "清空按钮",
220
+ className: "w-48"
221
+ }),
222
+ /* @__PURE__ */ jsx(Input, {
223
+ type: "password",
224
+ placeholder: "密码输入框",
225
+ className: "w-48"
226
+ }),
227
+ /* @__PURE__ */ jsx(Input, {
228
+ type: "password",
229
+ defaultValue: "template-core",
230
+ placeholder: "密码默认值",
231
+ className: "w-48"
232
+ }),
213
233
  /* @__PURE__ */ jsx(Input, {
214
234
  type: "password",
215
- placeholder: "密码输入框"
235
+ placeholder: "密码 + addon",
236
+ addonAfter: /* @__PURE__ */ jsx("span", {
237
+ className: "text-xs text-muted-foreground",
238
+ children: "PWD"
239
+ }),
240
+ className: "w-56"
216
241
  }),
217
242
  /* @__PURE__ */ jsx(Input, {
218
243
  disabled: true,
219
- placeholder: "禁用状态"
244
+ placeholder: "禁用状态",
245
+ className: "w-48"
220
246
  })
221
247
  ]
222
248
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_tc/template-core",
3
- "version": "0.0.1-bate.58",
3
+ "version": "0.0.1-bate.59",
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": {
@@ -40,6 +40,32 @@ export interface DefaultAppConfig {
40
40
  viewsPath?: string;
41
41
  nunjucks?: import('nunjucks').ConfigureOptions;
42
42
  notFoundRedirectCount?: number;
43
+ /**
44
+ * 登陆验证相关
45
+ */
46
+ auth?: {
47
+ /**
48
+ * 短token 获取的key
49
+ * @default 'Authorization'
50
+ */
51
+ ATKey?: string;
52
+ /**
53
+ * 长token 获取的key
54
+ * @default 'RT'
55
+ */
56
+ RTKey?: string;
57
+ };
43
58
  }
44
- declare const _default: DefaultAppConfig;
59
+ declare const _default: {
60
+ frameName: string;
61
+ signKey: string;
62
+ apiSignVerify: {
63
+ whiteList: never[];
64
+ };
65
+ auth: {
66
+ ATKey: string;
67
+ RTKey: string;
68
+ };
69
+ notFoundRedirectCount: number;
70
+ };
45
71
  export default _default;
@@ -2,7 +2,7 @@ import type { KoaApp } from "../types";
2
2
  /**
3
3
  * config loader 只处理一级
4
4
  *
5
- * @param app 加载 xx/config/ 下的文件
5
+ * @param app 加载 xx/config/ 下的文件 也就是根目录下的
6
6
  * config/config.default.(js|ts)
7
7
  */
8
8
  declare const _default: (app: KoaApp) => void;