@talknote/create-talknote-h5 0.1.3 → 0.1.5

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 (102) hide show
  1. package/bin/create-talknote-h5.js +14 -0
  2. package/package.json +1 -1
  3. package/template/.agents/skills/add-h5-page/SKILL.md +116 -0
  4. package/template/.eslintrc.js +2 -0
  5. package/template/README.md +143 -117
  6. package/template/_gitignore +0 -3
  7. package/template/config/config.ts +49 -4
  8. package/template/config/proxy.ts +28 -31
  9. package/template/docker/nginx/error_page.conf +51 -0
  10. package/template/docker/nginx/nginx.conf +54 -0
  11. package/template/docker/nginx/webApp.conf +13 -0
  12. package/template/docker/webApp.Dockerfile +12 -0
  13. package/template/package.json +6 -2
  14. package/template/packages/xiaozhi-chat/README.md +53 -0
  15. package/template/packages/xiaozhi-chat/XiaozhiChatPage.less +50 -0
  16. package/template/packages/xiaozhi-chat/XiaozhiChatPage.tsx +172 -0
  17. package/template/packages/xiaozhi-chat/assets/images/bg.jpg +0 -0
  18. package/template/packages/xiaozhi-chat/assets/images/icon-arrow-back.png +0 -0
  19. package/template/packages/xiaozhi-chat/assets/images/icon-close.png +0 -0
  20. package/template/packages/xiaozhi-chat/assets/images/icon-voice.png +0 -0
  21. package/template/packages/xiaozhi-chat/assets/images/keyboard.png +0 -0
  22. package/template/packages/xiaozhi-chat/assets/images/send-icon.png +0 -0
  23. package/template/packages/xiaozhi-chat/assets/images/stop-icon.png +0 -0
  24. package/template/packages/xiaozhi-chat/components/AssistantRichContent/index.less +3 -0
  25. package/template/packages/xiaozhi-chat/components/AssistantRichContent/index.tsx +44 -0
  26. package/template/packages/xiaozhi-chat/components/ChatHeader/index.less +84 -0
  27. package/template/packages/xiaozhi-chat/components/ChatHeader/index.tsx +44 -0
  28. package/template/packages/xiaozhi-chat/components/ChatInput/HoldToTalk.less +59 -0
  29. package/template/packages/xiaozhi-chat/components/ChatInput/HoldToTalk.tsx +167 -0
  30. package/template/packages/xiaozhi-chat/components/ChatInput/index.less +189 -0
  31. package/template/packages/xiaozhi-chat/components/ChatInput/index.tsx +203 -0
  32. package/template/packages/xiaozhi-chat/components/DeepThinkingWrapper/index.less +80 -0
  33. package/template/packages/xiaozhi-chat/components/DeepThinkingWrapper/index.tsx +54 -0
  34. package/template/packages/xiaozhi-chat/components/MarkdownContent/index.less +209 -0
  35. package/template/packages/xiaozhi-chat/components/MarkdownContent/index.tsx +41 -0
  36. package/template/packages/xiaozhi-chat/components/MessageList/index.less +74 -0
  37. package/template/packages/xiaozhi-chat/components/MessageList/index.tsx +195 -0
  38. package/template/packages/xiaozhi-chat/components/ThinkingCompleteEntry/index.less +56 -0
  39. package/template/packages/xiaozhi-chat/components/ThinkingCompleteEntry/index.tsx +65 -0
  40. package/template/packages/xiaozhi-chat/components/ToolCallList/index.less +41 -0
  41. package/template/packages/xiaozhi-chat/components/ToolCallList/index.tsx +51 -0
  42. package/template/packages/xiaozhi-chat/components/TypingIndicator/index.less +35 -0
  43. package/template/packages/xiaozhi-chat/components/TypingIndicator/index.tsx +12 -0
  44. package/template/packages/xiaozhi-chat/components/Welcome/index.less +105 -0
  45. package/template/packages/xiaozhi-chat/components/Welcome/index.tsx +84 -0
  46. package/template/packages/xiaozhi-chat/hooks/useChatSession.ts +235 -0
  47. package/template/packages/xiaozhi-chat/hooks/useRealtimeTranscription.ts +226 -0
  48. package/template/packages/xiaozhi-chat/hooks/useTypewriter.ts +215 -0
  49. package/template/packages/xiaozhi-chat/hooks/useViewportHeight.ts +43 -0
  50. package/template/packages/xiaozhi-chat/hooks/useVoiceRecording.ts +128 -0
  51. package/template/packages/xiaozhi-chat/index.ts +11 -0
  52. package/template/packages/xiaozhi-chat/package.json +34 -0
  53. package/template/packages/xiaozhi-chat/services/chatApi.ts +585 -0
  54. package/template/packages/xiaozhi-chat/types.ts +80 -0
  55. package/template/packages/xiaozhi-chat/utils/bridge.ts +131 -0
  56. package/template/packages/xiaozhi-chat/utils/deepThinking.ts +200 -0
  57. package/template/packages/xiaozhi-chat/utils/parseAssistantContent.ts +233 -0
  58. package/template/packages/xiaozhi-chat/utils/typewriter.ts +265 -0
  59. package/template/scripts/dev.js +6 -7
  60. package/template/src/app.tsx +1 -73
  61. package/template/src/assets/error-network.png +0 -0
  62. package/template/src/assets/error.png +0 -0
  63. package/template/src/assets/success.png +0 -0
  64. package/template/src/assets/welcome.png +0 -0
  65. package/template/src/components/Header/index.less +70 -0
  66. package/template/src/components/Header/index.tsx +46 -0
  67. package/template/src/components/TabBar/index.less +13 -0
  68. package/template/src/components/TabBar/index.tsx +58 -0
  69. package/template/src/global.css +27 -0
  70. package/template/src/layouts/BlankLayout/index.less +5 -0
  71. package/template/src/layouts/BlankLayout/index.tsx +14 -0
  72. package/template/src/layouts/HeaderLayout/index.less +25 -0
  73. package/template/src/layouts/HeaderLayout/index.tsx +42 -0
  74. package/template/src/layouts/LayoutOutlet.tsx +7 -0
  75. package/template/src/layouts/SSOLoginLayout/index.less +28 -0
  76. package/template/src/layouts/SSOLoginLayout/index.tsx +92 -0
  77. package/template/src/layouts/TabBarLayout/index.less +21 -0
  78. package/template/src/layouts/TabBarLayout/index.tsx +29 -0
  79. package/template/src/layouts/index.tsx +30 -0
  80. package/template/src/layouts/usePageHeaderProps.ts +9 -0
  81. package/template/src/pages/index/index.css +5 -5
  82. package/template/src/pages/index/index.tsx +15 -2
  83. package/template/src/pages/list/index.tsx +5 -5
  84. package/template/src/pages/test/index.tsx +12 -0
  85. package/template/src/pages/xiaozhi-chat/index.less +4 -0
  86. package/template/src/pages/xiaozhi-chat/index.tsx +26 -0
  87. package/template/src/routes.ts +49 -0
  88. package/template/src/services/api.ts +7 -2
  89. package/template/src/theme/variables.css +5 -0
  90. package/template/src/utils/ensureSsoLogin.ts +28 -0
  91. package/template/src/utils/goBack.ts +10 -0
  92. package/template/src/utils/request.ts +106 -0
  93. package/template/tsconfig.json +4 -1
  94. package/template/typings.d.ts +15 -0
  95. package/template/yarn.lock +2168 -61
  96. package/template/src/assets/demoIcon/home.png +0 -0
  97. package/template/src/assets/demoIcon/home1.png +0 -0
  98. package/template/src/assets/demoIcon/list.png +0 -0
  99. package/template/src/assets/demoIcon/list1.png +0 -0
  100. package/template/src/assets/demoIcon/setting.png +0 -0
  101. package/template/src/assets/demoIcon/setting1.png +0 -0
  102. package/template/src/assets/yay.jpg +0 -0
@@ -10,6 +10,16 @@ const SPECIAL_FILE_NAMES = {
10
10
  _gitignore: '.gitignore',
11
11
  _npmrc: '.npmrc',
12
12
  };
13
+ const SKIP_ENTRY_NAMES = new Set([
14
+ 'node_modules',
15
+ '.umi',
16
+ '.umi-production',
17
+ '.mfsu',
18
+ 'dist',
19
+ 'coverage',
20
+ '.DS_Store',
21
+ '.eslintcache',
22
+ ]);
13
23
 
14
24
  function printUsageError(message) {
15
25
  process.stderr.write(`Error: ${message}\n${USAGE}\n`);
@@ -35,6 +45,9 @@ function removeTree(targetPath) {
35
45
 
36
46
  function copyTemplateEntries(sourceDirectory, targetDirectory) {
37
47
  for (const entry of fs.readdirSync(sourceDirectory, { withFileTypes: true })) {
48
+ if (SKIP_ENTRY_NAMES.has(entry.name)) {
49
+ continue;
50
+ }
38
51
  const targetName = SPECIAL_FILE_NAMES[entry.name] || entry.name;
39
52
  const sourcePath = path.join(sourceDirectory, entry.name);
40
53
  const targetPath = path.join(targetDirectory, targetName);
@@ -117,6 +130,7 @@ if (require.main === module) {
117
130
  }
118
131
 
119
132
  module.exports = {
133
+ SKIP_ENTRY_NAMES,
120
134
  copyTemplateEntries,
121
135
  main,
122
136
  removeTree,
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "registry": "https://registry.npmjs.org/"
6
6
  },
7
- "version": "0.1.3",
7
+ "version": "0.1.5",
8
8
  "description": "Create a Talknote Alita H5 project.",
9
9
  "license": "MIT",
10
10
  "private": false,
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: add-h5-page
3
+ description: >-
4
+ Adds Alita H5 pages using convention files plus runtime layout in src/routes.ts
5
+ (tabBar, header, or blank). Use when creating a page, adding a route, choosing
6
+ layout, configuring headerProps, adding a TabBar item, or avoiding notch
7
+ occlusion on blank layout (vibe-element safe-area-view).
8
+ ---
9
+
10
+ # Add H5 page
11
+
12
+ Paths are relative to the Alita app root (`src/`, `config/`). In the createTalknoteH5 scaffold that is `template/`.
13
+
14
+ Do **not** put pages in `config/routes.ts` or `config/config.ts`. Layout is runtime: `src/routes.ts` (HMR). Page files are convention: `src/pages/<name>/index.tsx`.
15
+
16
+ ## Choose layout
17
+
18
+ | Need | `layout` | `headerProps` | Also |
19
+ |------|----------|---------------|------|
20
+ | Bottom tabs (home, list, …) | `tabBar` | set → Header; omit → no Header | add `navItems` in `src/components/TabBar/index.tsx` |
21
+ | Title + back, no bottom bar (detail, form, settings) | `header` | required (`title`; `showBack` defaults true) | — |
22
+ | Custom chrome (chat, camera, full-screen) | `blank` | omit | page owns header/back; wrap content with `vibe-element="safe-area-view"` `vibe-edges="top"` |
23
+
24
+ Unlisted paths default to **`tabBar`**. Always register the path in `src/routes.ts`.
25
+
26
+ Do not import layout components from `src/routes.ts` (circular). Layout map stays in `src/layouts/index.tsx`.
27
+
28
+ Do not wrap the page in Header/TabBar yourself for `tabBar` / `header`. For `blank`, the page may render its own header.
29
+
30
+ ## Steps
31
+
32
+ 1. Pick layout from the table. Ask if chrome is unclear.
33
+ 2. Add `src/pages/<name>/index.tsx` (and `index.less` if needed). Path is `/<name>`. Index page is `src/pages/index/` → `/`. Nested: `src/pages/foo/bar/` → `/foo/bar`.
34
+ 3. Append an entry to `routes` in `src/routes.ts` (see snippets). Do not put `**/` inside block comments (it closes `/*`).
35
+ 4. If `layout: 'tabBar'` **and** it is a root tab: add `{ path, text, icon }` to `navItems`. Do not add secondary pages (chat, detail) to TabBar.
36
+ 5. Optional keepalive: add a regex in `config/config.ts` `keepalive`. Requires **restart**.
37
+ 6. **Restart `yarn dev`** after adding/removing a page file (convention scan). Changing only `src/routes.ts` does not.
38
+
39
+ ## `src/routes.ts` snippets
40
+
41
+ Tab with Header (like `/`, `/list`):
42
+
43
+ ```ts
44
+ {
45
+ path: '/order',
46
+ layout: 'tabBar',
47
+ headerProps: { title: 'Orders', showBack: false },
48
+ },
49
+ ```
50
+
51
+ Tab, no Header:
52
+
53
+ ```ts
54
+ { path: '/feed', layout: 'tabBar' },
55
+ ```
56
+
57
+ Detail (Header only):
58
+
59
+ ```ts
60
+ {
61
+ path: '/order-detail',
62
+ layout: 'header',
63
+ headerProps: { title: 'Order detail' },
64
+ },
65
+ ```
66
+
67
+ Immersive (`blank`, like `/xiaozhi-chat`):
68
+
69
+ ```ts
70
+ { path: '/xiaozhi-chat', layout: 'blank' },
71
+ ```
72
+
73
+ `headerProps` fields: `title`, `showBack`, `extra`, `onBack`. Prefer `title` / `showBack` in the route table. Custom back belongs in the page (blank) or via Header `onBack` (`@/utils/goBack` already used by `Header`).
74
+
75
+ ## Blank layout: notch / safe area
76
+
77
+ For `blank` layout, keep content out of the notch / status bar. Put `vibe-element="safe-area-view"` and `vibe-edges="top"` on the content container.
78
+
79
+ Do not add these on `tabBar` / `header` pages: `Header` already has `vibe-element="header"`, and the native container applies the top safe area.
80
+
81
+ ```tsx
82
+ export default function TestPage() {
83
+ return (
84
+ <div style={{ backgroundColor: '#fff' }}>
85
+ <div vibe-element="safe-area-view" vibe-edges="top">
86
+ …
87
+ </div>
88
+ </div>
89
+ );
90
+ }
91
+ ```
92
+
93
+ If a `blank` page already has a custom header with `vibe-element="header"`, still put `safe-area-view` on the **outer content container** (see `packages/xiaozhi-chat/XiaozhiChatPage.tsx`).
94
+
95
+ ## Page file
96
+
97
+ ```tsx
98
+ import React from 'react';
99
+ import styles from './index.less';
100
+
101
+ export default function OrderPage() {
102
+ return <div className={styles.page}>…</div>;
103
+ }
104
+ ```
105
+
106
+ - Default export a component. Use `alita` for `useNavigate` / `useLocation` / `useSearchParams`.
107
+ - Do not use antd-mobile `NavBar` for the app header; chrome is `Header` + layouts.
108
+ - `blank` pages that need a back control: implement it in the page (see `src/pages/xiaozhi-chat/index.tsx`).
109
+ - Theme tokens live in `src/theme/variables.css` (`--color-primary`, spacing, radius).
110
+
111
+ ## Do not
112
+
113
+ - Do not edit `src/layouts/index.tsx` just to add a page.
114
+ - Do not add `routes` to `config/config.ts`.
115
+ - Do not expect `config/*.ts` (except watching `config.ts`) or new page files to hot-reload.
116
+ - Do not put a new layout name in `routes.ts` without adding it to `layoutMap` in `src/layouts/index.tsx`.
@@ -5,6 +5,8 @@ module.exports = {
5
5
  'no-use-before-define': ['warn', { functions: true, classes: true, variables: true }],
6
6
  'global-require': 'off',
7
7
  'no-empty': ['warn', { allowEmptyCatch: false }],
8
+ // 原生容器识别的自定义属性,需原样写在 DOM 上
9
+ 'react/no-unknown-property': ['error', { ignore: ['vibe-element', 'vibe-edges'] }],
8
10
  },
9
11
  parserOptions: {
10
12
  ecmaVersion: 12,
@@ -1,153 +1,179 @@
1
- <h1 align="center">Alita Pro</h1>
1
+ # 智在记录 H5
2
2
 
3
- 帮助你快速使用 alita, 基于Umi 的移动端开发框架
3
+ 面向「智在记录」APP 内嵌 H5 的 Alita 工程(基于 Umi)。提供移动端适配、约定式路由、运行时布局、开发代理和统一请求封装,用于在 APP WebView 中承载业务页面。
4
+
5
+ ## 技术栈
6
+
7
+ - **框架**:Alita 3(Umi)
8
+ - **UI**:antd-mobile 5、lucide-react
9
+ - **包管理**:Yarn
10
+ - **Node**:>= 18
11
+
12
+ ## 目录结构
4
13
 
5
14
  ```
15
+ ├── config/
16
+ │ ├── config.ts # 工程配置(keepalive、代理、API_PREFIX 等)
17
+ │ └── proxy.ts # 开发环境接口代理
18
+ ├── scripts/
19
+ │ └── dev.js # yarn dev 启动脚本,可注入代理地址
20
+ ├── src/
21
+ │ ├── pages/ # 约定式页面:src/pages/<name>/index.tsx → /<name>
22
+ │ ├── layouts/ # 运行时布局(tabBar / header / blank + SSO 闸门)
23
+ │ ├── components/ # Header、TabBar 等
24
+ │ ├── routes.ts # 路由布局与 SSO 开关
25
+ │ ├── services/ # 接口定义
26
+ │ ├── utils/request.ts # 统一请求(前缀、解包)
27
+ │ └── theme/ # 主题 CSS 变量
28
+ └── packages/ # 本地子包(内置问小智聊天 xiaozhi-chat)
29
+ ```
30
+
31
+ ## 启动
32
+
33
+ ```bash
6
34
  yarn
7
35
  yarn dev
8
36
  ```
9
37
 
10
- ### 首页
11
-
12
- ![image](https://user-images.githubusercontent.com/11746742/179654827-2ca22b45-0ec9-471e-8238-9bfa56d864a0.png)
38
+ 浏览器打开终端提示的本地地址即可。`yarn start` 与 `yarn dev` 相同。
13
39
 
14
- ### 列表页
40
+ 常用脚本:
15
41
 
16
- 列表页支持滚动加载更多,固定头部,状态保持
42
+ | 命令 | 说明 |
43
+ |------|------|
44
+ | `yarn dev` | 开发服务 |
45
+ | `yarn build` | 生产构建,产物在 `dist/` |
46
+ | `yarn format` | Prettier 格式化 |
47
+ | `yarn eslint` | ESLint 检查 |
17
48
 
18
- ![image](https://user-images.githubusercontent.com/11746742/179654907-585061f1-02cc-464e-ac75-c6ae12b2c666.png)
49
+ 新增或删除 `src/pages/**/index.tsx` 后需要**重启** `yarn dev`(约定式路由扫描)。只改 `src/routes.ts` 会热更新,不必重启。
19
50
 
51
+ ## 代理服务
20
52
 
21
- ### 关于尺寸标注
53
+ 开发时浏览器请求同源 `/server/*`,由 `config/proxy.ts` 转发到后端,并去掉 `/server` 前缀:
22
54
 
23
- 100% 宽为 750px,可在蓝湖等工具中将标准改为 750px,这样可以直接使用标注的尺寸用于 caa class 中的尺寸,最终运行会根据手机适配,将 px 自动转为对应的 rem 值。
55
+ ```
56
+ 前端:GET /server/nes/oauth2/ticketLogin
57
+ 实际:GET {DEV_SERVER}nes/oauth2/ticketLogin
58
+ ```
24
59
 
25
- 值得注意的是,写在内联样式 style 中的 px 值不会被转化,需要手动写明 rem 单位,直接将 px 值除于 100 即可,如 750px,在 style 中应该写为 7.5rem
60
+ 目标地址优先级:
26
61
 
27
- ### 关于路由
62
+ 1. 命令行传入的地址(`scripts/dev.js` 写入 `process.env.DEV_SERVER`)
63
+ 2. 环境变量 / `.env.local` 中的 `DEV_SERVER`
64
+ 3. `config/proxy.ts` 里的 `DEFAULT_DEV_SERVER`
28
65
 
29
- 采用约定式路由,只有 `src/pages/**/index.tsx` 会被识别成路由,可使用 npx alita g pages name 来新增页面。
66
+ ```bash
67
+ # 使用 .env.local 或默认地址
68
+ yarn dev
30
69
 
31
- > Q: 为什么仅 index.tsx 会被识别成路由?如果我想要嵌套的路由,如 hello/hero 我该怎么办?
32
- > A: 仅 index.tsx 被识别成路由,会让整个文件结构变得很干净,在移动端上不需要嵌套路由,一般短位链接优势更大
70
+ # 本次启动临时指定后端
71
+ yarn dev http://127.0.0.1:8000
33
72
 
34
- ### 自带头部
73
+ # 指定代理地址,并改本地端口
74
+ yarn dev http://127.0.0.1:8000 --port 8081
75
+ ```
35
76
 
36
- 可在 `src/app.tsx#L24` 中配置
77
+ 长期改本地后端:在项目根新建 `.env.local`(已 gitignore, 不会提交到仓库影响他人):
37
78
 
38
- ```tsx
39
- const titleList: TitleListItem[] = [
40
- {
41
- pagePath: '/',
42
- title: '首页',
43
- },
44
- {
45
- pagePath: '/list',
46
- title: '列表',
47
- },
48
- ];
79
+ ```bash
80
+ DEV_SERVER=http://127.0.0.1:8000
49
81
  ```
50
82
 
51
- 也可以在页面中独立配置
52
-
53
- ```tsx
54
- import { setPageNavBar, useLocation } from 'alita';
55
- import { LeftOutline } from 'antd-mobile-icons';
56
- import { useEffect } from 'react';
57
-
58
- const location = useLocation();
59
- useEffect(() => {
60
- setPageNavBar({
61
- pagePath: location.pathname,
62
- navBar: {
63
- mode: 'dark',
64
- pageTitle: '首页动态标题',
65
- pageBackground: '#fff',
66
- back: '返回',
67
- icon: <LeftOutline />,
68
- leftContent: <div>首页</div>,
69
- rightContent: <div>详情</div>,
70
- onLeftClick: (navigate) => {
71
- navigate(-1);
72
- },
73
- hideNavBar: false,
74
- },
75
- });
76
- }, []);
77
- ```
83
+ 增加代理规则时,在 `config/proxy.ts` 按同样结构追加 path → `{ target, changeOrigin, pathRewrite }`。
78
84
 
79
- > 关闭默认 NavBar 上述 hideNavBar 配置改为 hideNavBar: true
80
-
81
- ### 底部 Tabs
82
-
83
- 可在 `src/app.tsx#L42` 中配置
84
-
85
- ```tsx
86
- const tabList: TabBarListItem[] = [
87
- {
88
- pagePath: '/',
89
- text: '首页',
90
- iconPath: HomeGary,
91
- selectedIconPath: HomeBlue,
92
- title: '首页',
93
- iconSize: '',
94
- badge: '',
95
- },
96
- {
97
- pagePath: '/list',
98
- text: '列表',
99
- iconPath: ListGary,
100
- selectedIconPath: ListBlue,
101
- title: '列表',
102
- iconSize: '',
103
- badge: '',
104
- },
105
- ];
106
- ```
85
+ **注意:**
107
86
 
108
- 也可以在页面中独立配置
109
-
110
- ```tsx
111
- import { setTabBarList, useLocation } from 'alita';
112
- import { useEffect } from 'react';
113
-
114
- const location = useLocation();
115
- useEffect(() => {
116
- setTabBarList({
117
- pagePath: location.pathname,
118
- text: '首页',
119
- iconSize: '18px',
120
- badge: '12',
121
- onPress: () => {
122
- navigate(location.pathname, { replace: true });
123
- return '';
124
- },
125
- remove: false,
126
- });
127
- }, []);
128
- ```
87
+ - 代理**只在** `yarn dev` 生效,不会打进生产包。线上由容器 / 网关转发。
88
+ - 改 `config/proxy.ts` 或 `.env.local` 后需要重启 dev。
89
+ - 生产构建不走本文件。
129
90
 
130
- > 关闭 app.ts 中已经配置好的 Tab ,上述 remove 配置改为 remove: true
91
+ ### 接口前缀 `API_PREFIX`
131
92
 
132
- ### 格式化
93
+ 业务请求默认带前缀 `/server/nes`(见 `config/config.ts` 的 `define` 与 `src/utils/request.ts`)。开发态该前缀命中 `/server` 代理;需要改前缀时:
133
94
 
134
- ```
135
- yarn format
95
+ ```bash
96
+ API_PREFIX=/server/nes yarn dev
136
97
  ```
137
98
 
138
- ### 构建
99
+ ## 请求
139
100
 
101
+ 业务接口走 `src/utils/request.ts` 的 `request` / `get` / `post`,不要直接用 `alita` 的 `request`。
102
+
103
+ ```ts
104
+ import { get, post, request } from '@/utils/request';
105
+
106
+ // 默认:拼 API_PREFIX,并按 NES 信封解包 resultObject
107
+ const data = await post('/oauth2/ticketLogin', { ticket });
108
+
109
+ // 不拼前缀、不解包
110
+ await request('/api/hello', { method: 'POST', prefix: false, unwrap: false });
140
111
  ```
141
- yarn build
112
+
113
+ 请求会带 cookie(`credentials: 'include'`)。在 APP 内还会通过 `@talknote/h5-bridge` 注入原生 header / token(见 `src/app.tsx`)。
114
+
115
+ ## 路由与布局
116
+
117
+ 页面文件是约定式的:只有 `src/pages/**/index.tsx` 会成为路由。
118
+
119
+ | 页面文件 | 访问路径 |
120
+ |----------|----------|
121
+ | `src/pages/index/index.tsx` | `/` |
122
+ | `src/pages/list/index.tsx` | `/list` |
123
+ | `src/pages/foo/bar/index.tsx` | `/foo/bar` |
124
+
125
+ 布局**不写**在 `config/config.ts`,而在 `src/routes.ts`(运行时,可 HMR):
126
+
127
+ | `layout` | 场景 | `headerProps` |
128
+ |----------|------|----------------|
129
+ | `tabBar` | 首页、列表等带底栏 | 有则出 Header,省略则无顶栏 |
130
+ | `header` | 详情、表单、设置 | 必填(`title`;`showBack` 默认 true) |
131
+ | `blank` | 聊天、全屏、自定义顶栏 | 省略;页面自己管顶栏 |
132
+
133
+ 未登记的路径默认 **`blank`**。底栏 Tab 还需在 `src/components/TabBar/index.tsx` 的 `navItems` 里加一项。
134
+
135
+ ```ts
136
+ // src/routes.ts
137
+ {
138
+ path: '/order',
139
+ layout: 'tabBar',
140
+ headerProps: { title: '订单', showBack: false },
141
+ },
142
+ {
143
+ path: '/order-detail',
144
+ layout: 'header',
145
+ headerProps: { title: '订单详情' },
146
+ },
147
+ { path: '/xiaozhi-chat', layout: 'blank' },
142
148
  ```
143
149
 
144
- ### 调试工具
150
+ `blank` 页面内容需避开刘海 / 状态栏:在内容容器上加 `vibe-element="safe-area-view"` 和 `vibe-edges="top"`。`tabBar` / `header` 不用加,Header 已带 `vibe-element="header"`。
151
+
152
+ 需要离开后仍保留滚动位置等状态时,把路由正则加到 `config/config.ts` 的 `keepalive`,然后**重启** dev。
153
+
154
+ ## SSO 登录
145
155
 
146
- 在 `config/config.ts` 中增加配置
156
+ APP 内首次进入会走 ticket 登录(`window.vibeNoteBridge.getAuthTicket` → `POST /oauth2/ticketLogin`),由 `SSOLoginLayout` 闸门控制。
147
157
 
158
+ 开关在 `src/routes.ts`:
159
+
160
+ ```ts
161
+ export const ssoLoginEnabled = false; // true 时必须在 APP 内才能进入
148
162
  ```
149
- aconsole: {
150
- console: {},
151
- inspx: {},
152
- },
153
- ```
163
+
164
+ 本地浏览器调试请保持 `false`。在 APP 外且开关为 `true` 时会提示「请在智在记录APP里访问」。
165
+
166
+ ## 尺寸与主题
167
+
168
+ 设计稿宽度按 **750px**。写在 class / less 里的 `px` 会转成 rem;写在内联 `style` 里的 `px` **不会**转换,需手写 rem(数值 ÷ 100),例如 `750px` → `7.5rem`。
169
+
170
+ 主题 token 在 `src/theme/variables.css`(`--color-primary`、间距、圆角等),优先用变量,不要硬编码颜色。
171
+
172
+ ## 调试
173
+
174
+ 开发态默认开启 `aconsole`(`config/config.ts`):
175
+
176
+ - **console**:vConsole
177
+ - **inspx**:布局标尺
178
+
179
+ 不需要时把对应配置删掉或注释即可。
@@ -13,9 +13,6 @@ yarn-error.log
13
13
 
14
14
  /coverage
15
15
  .idea
16
- yarn.lock
17
- package-lock.json
18
- pnpm-lock.yaml
19
16
  *bak
20
17
 
21
18
 
@@ -1,16 +1,61 @@
1
+ /**
2
+ * Alita 工程配置(基于 Umi)
3
+ *
4
+ * 本文件是项目入口配置,`yarn dev` / `yarn build` 都会读取。
5
+ * 代理规则单独放在 ./proxy.ts,仅开发环境生效。
6
+ *
7
+ * 更多配置:https://alitajs.com/docs/config
8
+ */
9
+ import path from 'path';
1
10
  import { defineConfig } from 'alita';
2
11
  import proxy from './proxy';
3
12
 
4
13
  export default defineConfig({
14
+ // 应用类型。h5 会启用移动端适配(750 设计稿 → rem)
5
15
  appType: 'h5',
16
+
17
+ // 需要缓存的路由正则;匹配的页面离开后状态会保留
6
18
  keepalive: [/list/],
19
+
20
+ // 资源路径(默认使用hash模式,设置为./,适配在不同环境的网关)
21
+ publicPath: process.env.PUBLIC_PATH || './',
22
+
23
+ // 路由模式,使用hash,url带#
24
+ history: {
25
+ type: 'hash',
26
+ },
27
+
28
+ // 开发态调试面板:console 为 vConsole,inspx 为布局标尺
7
29
  aconsole: {
8
30
  console: {},
9
31
  inspx: {},
10
32
  },
33
+
34
+ // 开发代理,见 ./proxy.ts;生产构建不会打进产物
11
35
  proxy,
12
- mobileLayout: 'mobile5',
13
- mfsu: {},
14
- hash: false,
15
- npmClient: ''
36
+
37
+ // 关闭 Alita 约定式 mobileLayout。页面约定式路由;布局见 src/routes.ts(运行时)
38
+ mobileLayout: false,
39
+ conventionLayout: true,
40
+
41
+ // 关闭框架注入到 #root 的页面 loading(ant-spin)
42
+ preloadding: false,
43
+
44
+ mfsu: {
45
+ esbuild: true,
46
+ },
47
+ npmClient: 'yarn',
48
+ esbuildMinifyIIFE: true,
49
+
50
+ // 开启 hash 模式,让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。
51
+ hash: true,
52
+
53
+ define: {
54
+ 'process.env.API_PREFIX': process.env.API_PREFIX || '/server/nes',
55
+ },
56
+
57
+ // 本地子包源码直引,保证 less / 图片可被主工程编译
58
+ alias: {
59
+ '@talknote/xiaozhi-chat': path.join(__dirname, '../packages/xiaozhi-chat'),
60
+ },
16
61
  });
@@ -1,39 +1,36 @@
1
- const BASE_URL = 'http://172.21.72.205:10000/';
1
+ /**
2
+ * 开发环境接口代理(仅 `yarn dev` / `alita dev` 生效)
3
+ *
4
+ * 浏览器请求 `/server/xxx` 会被转发到后端,并去掉 `/server` 前缀:
5
+ * 前端:GET /server/foo
6
+ * 实际:GET {DEV_SERVER}foo
7
+ *
8
+ * 目标地址优先级:
9
+ * 1. process.env.DEV_SERVER(.env.local / 环境变量;命令行地址由 scripts/dev.js 注入)
10
+ * 2. 下方 DEFAULT_DEV_SERVER
11
+ *
12
+ * 用法(与 scripts/dev.js 配合):
13
+ * yarn dev # 走 .env.local / 默认 DEV_SERVER
14
+ * yarn dev http://1.2.3.4:10000 # 临时改代理地址
15
+ * yarn dev http://1.2.3.4:10000 --port 8081
16
+ *
17
+ * 说明:
18
+ * - 改本地地址:在 .env.local 写 DEV_SERVER=http://...
19
+ * - 加新代理:按同样结构再写一条 path → { target, changeOrigin, pathRewrite }
20
+ * - 生产环境不走本文件,接口由容器/网关转发
21
+ */
22
+ const DEFAULT_DEV_SERVER = 'http://10.10.179.140:8058/eco-oppo/server/';
2
23
 
3
- const DEV_URL = process.env.BASE_URL || `${BASE_URL}`;
24
+ const DEV_SERVER = process.env.DEV_SERVER || `${DEFAULT_DEV_SERVER}`;
25
+
26
+ if (process.env.NODE_ENV === 'development') {
27
+ console.log('[dev] API服务地址: ', DEV_SERVER);
28
+ }
4
29
 
5
30
  export default {
6
- '/portal': {
7
- target: BASE_URL,
8
- changeOrigin: true,
9
- },
10
31
  '/server': {
11
- target: DEV_URL,
32
+ target: DEV_SERVER,
12
33
  changeOrigin: true,
13
34
  pathRewrite: { '^/server': '' },
14
35
  },
15
- '/app': {
16
- target: DEV_URL,
17
- changeOrigin: true,
18
- // pathRewrite: { '^/app': '' },
19
- },
20
- '/login': {
21
- target: DEV_URL,
22
- changeOrigin: true,
23
- // pathRewrite: { '^/login': '' },
24
- },
25
- '/lcdp': {
26
- target: `${DEV_URL}`,
27
- changeOrigin: true,
28
- },
29
- '/img': {
30
- target: BASE_URL,
31
- changeOrigin: true,
32
- pathRewrite: { '^/img': '' },
33
- },
34
- '/engine': {
35
- target: BASE_URL,
36
- changeOrigin: true,
37
- pathRewrite: { '^/engine': 'engine' },
38
- },
39
36
  };
@@ -0,0 +1,51 @@
1
+ # 自定义错误页面,避免在响应体中出现 nginx 字眼, 满足一些现场的安全扫描需要
2
+
3
+ error_page 301 /301.html;
4
+ error_page 400 /400.html;
5
+ error_page 404 /404.html;
6
+ error_page 405 /405.html;
7
+ error_page 413 /413.html;
8
+ error_page 500 /500.html;
9
+ error_page 502 /502.html;
10
+ error_page 503 /503.html;
11
+ error_page 504 /504.html;
12
+ location /301.html {
13
+ add_header 'Content-Type' 'text/html';
14
+ return 200 "<html><head><title>301 Moved Permanently</title></head><body><center><h1>301 Moved Permanently</h1></center></body></html>";
15
+ }
16
+ location /400.html {
17
+ add_header 'Content-Type' 'text/html';
18
+ return 200 "<html><head><title>400 Bad Request</title></head><body><center><h1>400 Bad Request</h1></center></body></html>";
19
+ }
20
+ location /404.html {
21
+ add_header 'Content-Type' 'text/html';
22
+ try_files $uri @404_fallback;
23
+ }
24
+ location @404_fallback {
25
+ add_header 'Content-Type' 'text/html';
26
+ return 200 "<html><head><title>404 Not Found</title></head><body><center><h1>404 Not Found</h1></center></body></html>";
27
+ }
28
+ location /405.html {
29
+ add_header 'Content-Type' 'text/html';
30
+ return 200 "<html><head><title>405 Not Allowed</title></head><body><center><h1>405 Not Allowed</h1></center></body></html>";
31
+ }
32
+ location /413.html {
33
+ add_header 'Content-Type' 'text/html';
34
+ return 200 "<html><head><title>413 Request Entity Too Large</title></head><body><center><h1>413 Request Entity Too Large</h1></center></body></html>";
35
+ }
36
+ location /500.html {
37
+ add_header 'Content-Type' 'text/html';
38
+ return 200 "<html><head><title>500 Internal Server Error</title></head><body><center><h1>500 Internal Server Error</h1></center></body></html>";
39
+ }
40
+ location /502.html {
41
+ add_header 'Content-Type' 'text/html';
42
+ return 200 "<html><head><title>502 Bad Gateway</title></head><body><center><h1>502 Bad Gateway</h1></center></body></html>";
43
+ }
44
+ location /503.html {
45
+ add_header 'Content-Type' 'text/html';
46
+ return 200 "<html><head><title>503 Service Unavailable</title></head><body><center><h1>503 Service Unavailable</h1></center></body></html>";
47
+ }
48
+ location /504.html {
49
+ add_header 'Content-Type' 'text/html';
50
+ return 200 "<html><head><title>504 Gateway Timeout</title></head><body><center><h1>504 Gateway Timeout</h1></center></body></html>";
51
+ }