@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.
- package/bin/create-talknote-h5.js +14 -0
- package/package.json +1 -1
- package/template/.agents/skills/add-h5-page/SKILL.md +116 -0
- package/template/.eslintrc.js +2 -0
- package/template/README.md +143 -117
- package/template/_gitignore +0 -3
- package/template/config/config.ts +49 -4
- package/template/config/proxy.ts +28 -31
- package/template/docker/nginx/error_page.conf +51 -0
- package/template/docker/nginx/nginx.conf +54 -0
- package/template/docker/nginx/webApp.conf +13 -0
- package/template/docker/webApp.Dockerfile +12 -0
- package/template/package.json +6 -2
- package/template/packages/xiaozhi-chat/README.md +53 -0
- package/template/packages/xiaozhi-chat/XiaozhiChatPage.less +50 -0
- package/template/packages/xiaozhi-chat/XiaozhiChatPage.tsx +172 -0
- package/template/packages/xiaozhi-chat/assets/images/bg.jpg +0 -0
- package/template/packages/xiaozhi-chat/assets/images/icon-arrow-back.png +0 -0
- package/template/packages/xiaozhi-chat/assets/images/icon-close.png +0 -0
- package/template/packages/xiaozhi-chat/assets/images/icon-voice.png +0 -0
- package/template/packages/xiaozhi-chat/assets/images/keyboard.png +0 -0
- package/template/packages/xiaozhi-chat/assets/images/send-icon.png +0 -0
- package/template/packages/xiaozhi-chat/assets/images/stop-icon.png +0 -0
- package/template/packages/xiaozhi-chat/components/AssistantRichContent/index.less +3 -0
- package/template/packages/xiaozhi-chat/components/AssistantRichContent/index.tsx +44 -0
- package/template/packages/xiaozhi-chat/components/ChatHeader/index.less +84 -0
- package/template/packages/xiaozhi-chat/components/ChatHeader/index.tsx +44 -0
- package/template/packages/xiaozhi-chat/components/ChatInput/HoldToTalk.less +59 -0
- package/template/packages/xiaozhi-chat/components/ChatInput/HoldToTalk.tsx +167 -0
- package/template/packages/xiaozhi-chat/components/ChatInput/index.less +189 -0
- package/template/packages/xiaozhi-chat/components/ChatInput/index.tsx +203 -0
- package/template/packages/xiaozhi-chat/components/DeepThinkingWrapper/index.less +80 -0
- package/template/packages/xiaozhi-chat/components/DeepThinkingWrapper/index.tsx +54 -0
- package/template/packages/xiaozhi-chat/components/MarkdownContent/index.less +209 -0
- package/template/packages/xiaozhi-chat/components/MarkdownContent/index.tsx +41 -0
- package/template/packages/xiaozhi-chat/components/MessageList/index.less +74 -0
- package/template/packages/xiaozhi-chat/components/MessageList/index.tsx +195 -0
- package/template/packages/xiaozhi-chat/components/ThinkingCompleteEntry/index.less +56 -0
- package/template/packages/xiaozhi-chat/components/ThinkingCompleteEntry/index.tsx +65 -0
- package/template/packages/xiaozhi-chat/components/ToolCallList/index.less +41 -0
- package/template/packages/xiaozhi-chat/components/ToolCallList/index.tsx +51 -0
- package/template/packages/xiaozhi-chat/components/TypingIndicator/index.less +35 -0
- package/template/packages/xiaozhi-chat/components/TypingIndicator/index.tsx +12 -0
- package/template/packages/xiaozhi-chat/components/Welcome/index.less +105 -0
- package/template/packages/xiaozhi-chat/components/Welcome/index.tsx +84 -0
- package/template/packages/xiaozhi-chat/hooks/useChatSession.ts +235 -0
- package/template/packages/xiaozhi-chat/hooks/useRealtimeTranscription.ts +226 -0
- package/template/packages/xiaozhi-chat/hooks/useTypewriter.ts +215 -0
- package/template/packages/xiaozhi-chat/hooks/useViewportHeight.ts +43 -0
- package/template/packages/xiaozhi-chat/hooks/useVoiceRecording.ts +128 -0
- package/template/packages/xiaozhi-chat/index.ts +11 -0
- package/template/packages/xiaozhi-chat/package.json +34 -0
- package/template/packages/xiaozhi-chat/services/chatApi.ts +585 -0
- package/template/packages/xiaozhi-chat/types.ts +80 -0
- package/template/packages/xiaozhi-chat/utils/bridge.ts +131 -0
- package/template/packages/xiaozhi-chat/utils/deepThinking.ts +200 -0
- package/template/packages/xiaozhi-chat/utils/parseAssistantContent.ts +233 -0
- package/template/packages/xiaozhi-chat/utils/typewriter.ts +265 -0
- package/template/scripts/dev.js +6 -7
- package/template/src/app.tsx +1 -73
- package/template/src/assets/error-network.png +0 -0
- package/template/src/assets/error.png +0 -0
- package/template/src/assets/success.png +0 -0
- package/template/src/assets/welcome.png +0 -0
- package/template/src/components/Header/index.less +70 -0
- package/template/src/components/Header/index.tsx +46 -0
- package/template/src/components/TabBar/index.less +13 -0
- package/template/src/components/TabBar/index.tsx +58 -0
- package/template/src/global.css +27 -0
- package/template/src/layouts/BlankLayout/index.less +5 -0
- package/template/src/layouts/BlankLayout/index.tsx +14 -0
- package/template/src/layouts/HeaderLayout/index.less +25 -0
- package/template/src/layouts/HeaderLayout/index.tsx +42 -0
- package/template/src/layouts/LayoutOutlet.tsx +7 -0
- package/template/src/layouts/SSOLoginLayout/index.less +28 -0
- package/template/src/layouts/SSOLoginLayout/index.tsx +92 -0
- package/template/src/layouts/TabBarLayout/index.less +21 -0
- package/template/src/layouts/TabBarLayout/index.tsx +29 -0
- package/template/src/layouts/index.tsx +30 -0
- package/template/src/layouts/usePageHeaderProps.ts +9 -0
- package/template/src/pages/index/index.css +5 -5
- package/template/src/pages/index/index.tsx +15 -2
- package/template/src/pages/list/index.tsx +5 -5
- package/template/src/pages/test/index.tsx +12 -0
- package/template/src/pages/xiaozhi-chat/index.less +4 -0
- package/template/src/pages/xiaozhi-chat/index.tsx +26 -0
- package/template/src/routes.ts +49 -0
- package/template/src/services/api.ts +7 -2
- package/template/src/theme/variables.css +5 -0
- package/template/src/utils/ensureSsoLogin.ts +28 -0
- package/template/src/utils/goBack.ts +10 -0
- package/template/src/utils/request.ts +106 -0
- package/template/tsconfig.json +4 -1
- package/template/typings.d.ts +15 -0
- package/template/yarn.lock +2168 -61
- package/template/src/assets/demoIcon/home.png +0 -0
- package/template/src/assets/demoIcon/home1.png +0 -0
- package/template/src/assets/demoIcon/list.png +0 -0
- package/template/src/assets/demoIcon/list1.png +0 -0
- package/template/src/assets/demoIcon/setting.png +0 -0
- package/template/src/assets/demoIcon/setting1.png +0 -0
- 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
|
@@ -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`.
|
package/template/.eslintrc.js
CHANGED
|
@@ -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,
|
package/template/README.md
CHANGED
|
@@ -1,153 +1,179 @@
|
|
|
1
|
-
|
|
1
|
+
# 智在记录 H5
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-

|
|
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
|
-
|
|
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
|
-
|
|
55
|
+
```
|
|
56
|
+
前端:GET /server/nes/oauth2/ticketLogin
|
|
57
|
+
实际:GET {DEV_SERVER}nes/oauth2/ticketLogin
|
|
58
|
+
```
|
|
24
59
|
|
|
25
|
-
|
|
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
|
-
|
|
66
|
+
```bash
|
|
67
|
+
# 使用 .env.local 或默认地址
|
|
68
|
+
yarn dev
|
|
30
69
|
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
77
|
+
长期改本地后端:在项目根新建 `.env.local`(已 gitignore, 不会提交到仓库影响他人):
|
|
37
78
|
|
|
38
|
-
```
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
+
### 接口前缀 `API_PREFIX`
|
|
131
92
|
|
|
132
|
-
|
|
93
|
+
业务请求默认带前缀 `/server/nes`(见 `config/config.ts` 的 `define` 与 `src/utils/request.ts`)。开发态该前缀命中 `/server` 代理;需要改前缀时:
|
|
133
94
|
|
|
134
|
-
```
|
|
135
|
-
yarn
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
+
不需要时把对应配置删掉或注释即可。
|
package/template/_gitignore
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
});
|
package/template/config/proxy.ts
CHANGED
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
|
|
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
|
|
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:
|
|
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
|
+
}
|