@coze-arch/cli 0.0.8-beta.0 → 0.0.9
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/lib/__templates__/expo/AGENTS.md +180 -17
- package/lib/__templates__/expo/README.md +180 -17
- package/lib/__templates__/expo/client/app/+not-found.tsx +4 -19
- package/lib/__templates__/expo/client/app/_layout.tsx +16 -22
- package/lib/__templates__/expo/client/components/ColorSchemeUpdater.tsx +43 -0
- package/lib/__templates__/expo/client/components/Provider.tsx +18 -0
- package/lib/__templates__/expo/client/components/Screen.tsx +1 -1
- package/lib/__templates__/expo/client/global.css +267 -0
- package/lib/__templates__/expo/client/metro.config.js +8 -1
- package/lib/__templates__/expo/client/package.json +3 -1
- package/lib/__templates__/expo/client/screens/demo/index.tsx +7 -13
- package/lib/__templates__/expo/client/uniwind-types.d.ts +10 -0
- package/lib/__templates__/expo/pnpm-lock.yaml +495 -47
- package/lib/__templates__/nextjs/next.config.ts +1 -1
- package/lib/__templates__/taro/project.config.json +9 -1
- package/lib/cli.js +4 -2
- package/package.json +4 -2
- package/lib/__templates__/expo/client/components/ThemedText.tsx +0 -33
- package/lib/__templates__/expo/client/components/ThemedView.tsx +0 -37
- package/lib/__templates__/expo/client/constants/theme.ts +0 -177
- package/lib/__templates__/expo/client/hooks/useColorScheme.tsx +0 -48
- package/lib/__templates__/expo/client/hooks/useTheme.ts +0 -33
- package/lib/__templates__/expo/client/screens/demo/styles.ts +0 -28
|
@@ -7,33 +7,49 @@
|
|
|
7
7
|
- Expo 代码在 client 目录,Express.js 代码在 server 目录
|
|
8
8
|
- 本模板默认无 Tab Bar,可按需改造
|
|
9
9
|
|
|
10
|
-
目录结构说明
|
|
11
|
-
|
|
12
|
-
├── server/ # 服务端代码根目录 (Express.js)
|
|
13
|
-
| ├── src/
|
|
14
|
-
│ │ └── index.ts # Express 入口文件
|
|
15
|
-
| └── package.json # 服务端 package.json
|
|
16
10
|
├── client/ # React Native 前端代码
|
|
17
11
|
│ ├── app/ # Expo Router 路由目录(仅路由配置)
|
|
18
12
|
│ │ ├── _layout.tsx # 根布局文件(必需,务必阅读)
|
|
19
|
-
│ │
|
|
20
|
-
│ │ └── index.tsx # re-export home.tsx
|
|
13
|
+
│ │ └── index.tsx # 首页
|
|
21
14
|
│ ├── screens/ # 页面实现目录(与 app/ 路由对应)
|
|
22
|
-
│ │ └── demo/ #
|
|
23
|
-
│ │
|
|
24
|
-
│ │ └── styles.ts # 页面样式
|
|
15
|
+
│ │ └── demo/ # 示例页面
|
|
16
|
+
│ │ └── index.tsx
|
|
25
17
|
│ ├── components/ # 可复用组件
|
|
26
18
|
│ │ └── Screen.tsx # 页面容器组件(必用)
|
|
27
19
|
│ ├── hooks/ # 自定义 Hooks
|
|
28
20
|
│ ├── contexts/ # React Context 代码
|
|
29
|
-
│ ├── constants/ # 常量定义(如主题配置)
|
|
30
21
|
│ ├── utils/ # 工具函数
|
|
31
22
|
│ ├── assets/ # 静态资源
|
|
32
23
|
| └── package.json # Expo 应用 package.json
|
|
24
|
+
├── server/ # 服务端代码根目录 (Express.js)
|
|
25
|
+
| ├── src/
|
|
26
|
+
│ │ └── index.ts # 服务端入口文件
|
|
27
|
+
| └── package.json # 服务端 package.json
|
|
33
28
|
├── package.json
|
|
34
29
|
├── .cozeproj # 预置脚手架脚本(禁止修改)
|
|
35
30
|
└── .coze # 配置文件(禁止修改)
|
|
36
31
|
|
|
32
|
+
## 样式方案
|
|
33
|
+
|
|
34
|
+
基于 tailwindcss 进行样式开发(底层基于 Uniwind)
|
|
35
|
+
|
|
36
|
+
写法示例:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<View className="flex-1 bg-white dark:bg-gray-900 p-4"></View>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Text
|
|
44
|
+
className="text-lg font-bold text-gray-900 dark:text-white"
|
|
45
|
+
selectionColorClassName="accent-blue-500"
|
|
46
|
+
>
|
|
47
|
+
Hello World
|
|
48
|
+
</Text>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Uniwind 官方文档:https://docs.uniwind.dev/llms.txt
|
|
52
|
+
|
|
37
53
|
## 如何进行静态校验(TSC + ESLint)
|
|
38
54
|
|
|
39
55
|
```bash
|
|
@@ -47,6 +63,157 @@ npm run lint:client
|
|
|
47
63
|
npm run lint:server
|
|
48
64
|
```
|
|
49
65
|
|
|
66
|
+
## 如何修改主题模式(跟随系统、固定暗色、固定亮色)
|
|
67
|
+
|
|
68
|
+
默认为跟随系统,如果用户明确指定为“暗色”或“亮色”,需要修改 `client/components/ColorSchemeUpdater.tsx` 的 `DEFAULT_THEME` 变量为合适的值
|
|
69
|
+
|
|
70
|
+
## 路由及 Tab Bar 实现规范
|
|
71
|
+
|
|
72
|
+
### 方案一:无 Tab Bar(Stack 导航)
|
|
73
|
+
|
|
74
|
+
适用于线性流程应用,采用简化的目录结构:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
client/app/
|
|
78
|
+
├── _layout.tsx # 根布局(Stack 导航配置)
|
|
79
|
+
├── index.tsx # 应用入口
|
|
80
|
+
├── detail.tsx # 详情页(通过 params 传递数据)
|
|
81
|
+
└── +not-found.tsx # 404 页面
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**根布局配置** `client/app/_layout.tsx`:
|
|
85
|
+
```tsx
|
|
86
|
+
import { Stack } from 'expo-router';
|
|
87
|
+
|
|
88
|
+
export default function RootLayout() {
|
|
89
|
+
return (
|
|
90
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
91
|
+
<Stack.Screen name="index" />
|
|
92
|
+
<Stack.Screen name="detail" />
|
|
93
|
+
</Stack>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**应用入口** `client/app/index.tsx`:
|
|
99
|
+
```tsx
|
|
100
|
+
export { default } from "@/screens/home";
|
|
101
|
+
```
|
|
102
|
+
> **禁止事项**:无 Tab Bar 场景下,不得创建 `(tabs)` 目录。
|
|
103
|
+
|
|
104
|
+
### 方案二:有 Tab Bar(Tabs 导航)
|
|
105
|
+
|
|
106
|
+
采用路由分组实现底部导航栏:
|
|
107
|
+
```
|
|
108
|
+
client/app/
|
|
109
|
+
├── _layout.tsx # 根布局
|
|
110
|
+
├── (tabs)/
|
|
111
|
+
│ ├── _layout.tsx # Tab 导航配置
|
|
112
|
+
│ ├── index.tsx # 默认 Tab(必须存在)
|
|
113
|
+
│ ├── discover.tsx # 发现页
|
|
114
|
+
│ └── profile.tsx # 个人中心
|
|
115
|
+
├── detail.tsx # Tab 外的独立页面(通过 params 传递数据)
|
|
116
|
+
└── +not-found.tsx
|
|
117
|
+
```
|
|
118
|
+
> **⚠️ [CRITICAL]**: `app/index.tsx` 优先级高于 `(tabs)/index.tsx`,会导致首页无 Tab Bar。**当有(tabs)/index.tsx时必须删除 `app/index.tsx`**。
|
|
119
|
+
|
|
120
|
+
**根布局配置** `client/app/_layout.tsx`:
|
|
121
|
+
```tsx
|
|
122
|
+
import { Stack } from 'expo-router';
|
|
123
|
+
|
|
124
|
+
export default function RootLayout() {
|
|
125
|
+
return (
|
|
126
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
127
|
+
<Stack.Screen name="(tabs)" />
|
|
128
|
+
<Stack.Screen name="detail" />
|
|
129
|
+
</Stack>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**应用入口** `client/app/(tabs)/index.tsx`:
|
|
135
|
+
```tsx
|
|
136
|
+
export { default } from "@/screens/home";
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Tab 布局配置** `client/app/(tabs)/_layout.tsx`:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { Tabs } from 'expo-router';
|
|
143
|
+
import { Platform } from 'react-native';
|
|
144
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
145
|
+
import { FontAwesome6 } from '@expo/vector-icons';
|
|
146
|
+
import { useCSSVariable } from 'uniwind';
|
|
147
|
+
|
|
148
|
+
export default function TabLayout() {
|
|
149
|
+
const insets = useSafeAreaInsets();
|
|
150
|
+
const [background, muted, accent, border] = useCSSVariable([
|
|
151
|
+
'--color-background',
|
|
152
|
+
'--color-muted',
|
|
153
|
+
'--color-accent',
|
|
154
|
+
'--color-border',
|
|
155
|
+
]) as string[];
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<Tabs
|
|
159
|
+
screenOptions={{
|
|
160
|
+
headerShown: false,
|
|
161
|
+
tabBarStyle: {
|
|
162
|
+
backgroundColor: background,
|
|
163
|
+
borderTopWidth: 1,
|
|
164
|
+
borderTopColor: border,
|
|
165
|
+
// 移动端:标准高度 50px + 底部安全区
|
|
166
|
+
// Web端:固定60px,无需安全区
|
|
167
|
+
height: Platform.OS === 'web' ? 60 : 50 + insets.bottom,
|
|
168
|
+
// 移动端:内容区域底部 padding 防止内容被遮挡
|
|
169
|
+
paddingBottom: Platform.OS === 'web' ? 0 : insets.bottom,
|
|
170
|
+
},
|
|
171
|
+
tabBarActiveTintColor: accent,
|
|
172
|
+
tabBarInactiveTintColor: muted,
|
|
173
|
+
tabBarItemStyle: {
|
|
174
|
+
// Web 端必须显式指定 item 高度,防止 Tab Bar 高度塌陷或图标显示异常
|
|
175
|
+
height: Platform.OS === 'web' ? 60 : undefined,
|
|
176
|
+
},
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{/* name 必须与文件名完全一致 */}
|
|
180
|
+
<Tabs.Screen
|
|
181
|
+
name="index"
|
|
182
|
+
options={{
|
|
183
|
+
title: '首页',
|
|
184
|
+
tabBarIcon: ({ color }) => (
|
|
185
|
+
<FontAwesome6 name="house" size={20} color={color} />
|
|
186
|
+
),
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
<Tabs.Screen
|
|
190
|
+
name="discover"
|
|
191
|
+
options={{
|
|
192
|
+
title: '发现',
|
|
193
|
+
tabBarIcon: ({ color }) => (
|
|
194
|
+
<FontAwesome6 name="compass" size={20} color={color} />
|
|
195
|
+
),
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
<Tabs.Screen
|
|
199
|
+
name="profile"
|
|
200
|
+
options={{
|
|
201
|
+
title: '我的',
|
|
202
|
+
tabBarIcon: ({ color }) => (
|
|
203
|
+
<FontAwesome6 name="user" size={20} color={color} />
|
|
204
|
+
),
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
</Tabs>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Tab 页面文件** `client/app/(tabs)/index.tsx`:
|
|
213
|
+
```tsx
|
|
214
|
+
export { default } from "@/screens/home";
|
|
215
|
+
```
|
|
216
|
+
|
|
50
217
|
## 依赖管理与模块导入规范
|
|
51
218
|
|
|
52
219
|
### 依赖安装
|
|
@@ -83,8 +250,4 @@ import { Screen } from '../../../components/Screen';
|
|
|
83
250
|
|
|
84
251
|
## 本地开发
|
|
85
252
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
coze dev
|
|
90
|
-
```
|
|
253
|
+
`coze dev`:用来首次启动前后端服务,也可以用来重启前后端服务(该命令会先尝试杀掉占用端口的进程,再启动服务)
|
|
@@ -7,33 +7,49 @@
|
|
|
7
7
|
- Expo 代码在 client 目录,Express.js 代码在 server 目录
|
|
8
8
|
- 本模板默认无 Tab Bar,可按需改造
|
|
9
9
|
|
|
10
|
-
目录结构说明
|
|
11
|
-
|
|
12
|
-
├── server/ # 服务端代码根目录 (Express.js)
|
|
13
|
-
| ├── src/
|
|
14
|
-
│ │ └── index.ts # Express 入口文件
|
|
15
|
-
| └── package.json # 服务端 package.json
|
|
16
10
|
├── client/ # React Native 前端代码
|
|
17
11
|
│ ├── app/ # Expo Router 路由目录(仅路由配置)
|
|
18
12
|
│ │ ├── _layout.tsx # 根布局文件(必需,务必阅读)
|
|
19
|
-
│ │
|
|
20
|
-
│ │ └── index.tsx # re-export home.tsx
|
|
13
|
+
│ │ └── index.tsx # 首页
|
|
21
14
|
│ ├── screens/ # 页面实现目录(与 app/ 路由对应)
|
|
22
|
-
│ │ └── demo/ #
|
|
23
|
-
│ │
|
|
24
|
-
│ │ └── styles.ts # 页面样式
|
|
15
|
+
│ │ └── demo/ # 示例页面
|
|
16
|
+
│ │ └── index.tsx
|
|
25
17
|
│ ├── components/ # 可复用组件
|
|
26
18
|
│ │ └── Screen.tsx # 页面容器组件(必用)
|
|
27
19
|
│ ├── hooks/ # 自定义 Hooks
|
|
28
20
|
│ ├── contexts/ # React Context 代码
|
|
29
|
-
│ ├── constants/ # 常量定义(如主题配置)
|
|
30
21
|
│ ├── utils/ # 工具函数
|
|
31
22
|
│ ├── assets/ # 静态资源
|
|
32
23
|
| └── package.json # Expo 应用 package.json
|
|
24
|
+
├── server/ # 服务端代码根目录 (Express.js)
|
|
25
|
+
| ├── src/
|
|
26
|
+
│ │ └── index.ts # 服务端入口文件
|
|
27
|
+
| └── package.json # 服务端 package.json
|
|
33
28
|
├── package.json
|
|
34
29
|
├── .cozeproj # 预置脚手架脚本(禁止修改)
|
|
35
30
|
└── .coze # 配置文件(禁止修改)
|
|
36
31
|
|
|
32
|
+
## 样式方案
|
|
33
|
+
|
|
34
|
+
基于 tailwindcss 进行样式开发(底层基于 Uniwind)
|
|
35
|
+
|
|
36
|
+
写法示例:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<View className="flex-1 bg-white dark:bg-gray-900 p-4"></View>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Text
|
|
44
|
+
className="text-lg font-bold text-gray-900 dark:text-white"
|
|
45
|
+
selectionColorClassName="accent-blue-500"
|
|
46
|
+
>
|
|
47
|
+
Hello World
|
|
48
|
+
</Text>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Uniwind 官方文档:https://docs.uniwind.dev/llms.txt
|
|
52
|
+
|
|
37
53
|
## 如何进行静态校验(TSC + ESLint)
|
|
38
54
|
|
|
39
55
|
```bash
|
|
@@ -47,6 +63,157 @@ npm run lint:client
|
|
|
47
63
|
npm run lint:server
|
|
48
64
|
```
|
|
49
65
|
|
|
66
|
+
## 如何修改主题模式(跟随系统、固定暗色、固定亮色)
|
|
67
|
+
|
|
68
|
+
默认为跟随系统,如果用户明确指定为“暗色”或“亮色”,需要修改 `client/components/ColorSchemeUpdater.tsx` 的 `DEFAULT_THEME` 变量为合适的值
|
|
69
|
+
|
|
70
|
+
## 路由及 Tab Bar 实现规范
|
|
71
|
+
|
|
72
|
+
### 方案一:无 Tab Bar(Stack 导航)
|
|
73
|
+
|
|
74
|
+
适用于线性流程应用,采用简化的目录结构:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
client/app/
|
|
78
|
+
├── _layout.tsx # 根布局(Stack 导航配置)
|
|
79
|
+
├── index.tsx # 应用入口
|
|
80
|
+
├── detail.tsx # 详情页(通过 params 传递数据)
|
|
81
|
+
└── +not-found.tsx # 404 页面
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**根布局配置** `client/app/_layout.tsx`:
|
|
85
|
+
```tsx
|
|
86
|
+
import { Stack } from 'expo-router';
|
|
87
|
+
|
|
88
|
+
export default function RootLayout() {
|
|
89
|
+
return (
|
|
90
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
91
|
+
<Stack.Screen name="index" />
|
|
92
|
+
<Stack.Screen name="detail" />
|
|
93
|
+
</Stack>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**应用入口** `client/app/index.tsx`:
|
|
99
|
+
```tsx
|
|
100
|
+
export { default } from "@/screens/home";
|
|
101
|
+
```
|
|
102
|
+
> **禁止事项**:无 Tab Bar 场景下,不得创建 `(tabs)` 目录。
|
|
103
|
+
|
|
104
|
+
### 方案二:有 Tab Bar(Tabs 导航)
|
|
105
|
+
|
|
106
|
+
采用路由分组实现底部导航栏:
|
|
107
|
+
```
|
|
108
|
+
client/app/
|
|
109
|
+
├── _layout.tsx # 根布局
|
|
110
|
+
├── (tabs)/
|
|
111
|
+
│ ├── _layout.tsx # Tab 导航配置
|
|
112
|
+
│ ├── index.tsx # 默认 Tab(必须存在)
|
|
113
|
+
│ ├── discover.tsx # 发现页
|
|
114
|
+
│ └── profile.tsx # 个人中心
|
|
115
|
+
├── detail.tsx # Tab 外的独立页面(通过 params 传递数据)
|
|
116
|
+
└── +not-found.tsx
|
|
117
|
+
```
|
|
118
|
+
> **⚠️ [CRITICAL]**: `app/index.tsx` 优先级高于 `(tabs)/index.tsx`,会导致首页无 Tab Bar。**当有(tabs)/index.tsx时必须删除 `app/index.tsx`**。
|
|
119
|
+
|
|
120
|
+
**根布局配置** `client/app/_layout.tsx`:
|
|
121
|
+
```tsx
|
|
122
|
+
import { Stack } from 'expo-router';
|
|
123
|
+
|
|
124
|
+
export default function RootLayout() {
|
|
125
|
+
return (
|
|
126
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
127
|
+
<Stack.Screen name="(tabs)" />
|
|
128
|
+
<Stack.Screen name="detail" />
|
|
129
|
+
</Stack>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**应用入口** `client/app/(tabs)/index.tsx`:
|
|
135
|
+
```tsx
|
|
136
|
+
export { default } from "@/screens/home";
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Tab 布局配置** `client/app/(tabs)/_layout.tsx`:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { Tabs } from 'expo-router';
|
|
143
|
+
import { Platform } from 'react-native';
|
|
144
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
145
|
+
import { FontAwesome6 } from '@expo/vector-icons';
|
|
146
|
+
import { useCSSVariable } from 'uniwind';
|
|
147
|
+
|
|
148
|
+
export default function TabLayout() {
|
|
149
|
+
const insets = useSafeAreaInsets();
|
|
150
|
+
const [background, muted, accent, border] = useCSSVariable([
|
|
151
|
+
'--color-background',
|
|
152
|
+
'--color-muted',
|
|
153
|
+
'--color-accent',
|
|
154
|
+
'--color-border',
|
|
155
|
+
]) as string[];
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<Tabs
|
|
159
|
+
screenOptions={{
|
|
160
|
+
headerShown: false,
|
|
161
|
+
tabBarStyle: {
|
|
162
|
+
backgroundColor: background,
|
|
163
|
+
borderTopWidth: 1,
|
|
164
|
+
borderTopColor: border,
|
|
165
|
+
// 移动端:标准高度 50px + 底部安全区
|
|
166
|
+
// Web端:固定60px,无需安全区
|
|
167
|
+
height: Platform.OS === 'web' ? 60 : 50 + insets.bottom,
|
|
168
|
+
// 移动端:内容区域底部 padding 防止内容被遮挡
|
|
169
|
+
paddingBottom: Platform.OS === 'web' ? 0 : insets.bottom,
|
|
170
|
+
},
|
|
171
|
+
tabBarActiveTintColor: accent,
|
|
172
|
+
tabBarInactiveTintColor: muted,
|
|
173
|
+
tabBarItemStyle: {
|
|
174
|
+
// Web 端必须显式指定 item 高度,防止 Tab Bar 高度塌陷或图标显示异常
|
|
175
|
+
height: Platform.OS === 'web' ? 60 : undefined,
|
|
176
|
+
},
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{/* name 必须与文件名完全一致 */}
|
|
180
|
+
<Tabs.Screen
|
|
181
|
+
name="index"
|
|
182
|
+
options={{
|
|
183
|
+
title: '首页',
|
|
184
|
+
tabBarIcon: ({ color }) => (
|
|
185
|
+
<FontAwesome6 name="house" size={20} color={color} />
|
|
186
|
+
),
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
<Tabs.Screen
|
|
190
|
+
name="discover"
|
|
191
|
+
options={{
|
|
192
|
+
title: '发现',
|
|
193
|
+
tabBarIcon: ({ color }) => (
|
|
194
|
+
<FontAwesome6 name="compass" size={20} color={color} />
|
|
195
|
+
),
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
<Tabs.Screen
|
|
199
|
+
name="profile"
|
|
200
|
+
options={{
|
|
201
|
+
title: '我的',
|
|
202
|
+
tabBarIcon: ({ color }) => (
|
|
203
|
+
<FontAwesome6 name="user" size={20} color={color} />
|
|
204
|
+
),
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
</Tabs>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Tab 页面文件** `client/app/(tabs)/index.tsx`:
|
|
213
|
+
```tsx
|
|
214
|
+
export { default } from "@/screens/home";
|
|
215
|
+
```
|
|
216
|
+
|
|
50
217
|
## 依赖管理与模块导入规范
|
|
51
218
|
|
|
52
219
|
### 依赖安装
|
|
@@ -83,8 +250,4 @@ import { Screen } from '../../../components/Screen';
|
|
|
83
250
|
|
|
84
251
|
## 本地开发
|
|
85
252
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
coze dev
|
|
90
|
-
```
|
|
253
|
+
`coze dev`:用来首次启动前后端服务,也可以用来重启前后端服务(该命令会先尝试杀掉占用端口的进程,再启动服务)
|
|
@@ -1,30 +1,15 @@
|
|
|
1
|
-
import { View, Text
|
|
1
|
+
import { View, Text } from 'react-native';
|
|
2
2
|
import { Link } from 'expo-router';
|
|
3
|
-
import { useTheme } from '@/hooks/useTheme';
|
|
4
|
-
import { Spacing } from '@/constants/theme';
|
|
5
3
|
|
|
6
4
|
export default function NotFoundScreen() {
|
|
7
|
-
const { theme } = useTheme();
|
|
8
|
-
|
|
9
5
|
return (
|
|
10
|
-
<View
|
|
11
|
-
<Text>
|
|
6
|
+
<View className="flex-1 justify-center items-center bg-background">
|
|
7
|
+
<Text className="text-foreground">
|
|
12
8
|
页面不存在
|
|
13
9
|
</Text>
|
|
14
|
-
<Link href="/"
|
|
10
|
+
<Link href="/" className="text-accent mt-6">
|
|
15
11
|
返回首页
|
|
16
12
|
</Link>
|
|
17
13
|
</View>
|
|
18
14
|
);
|
|
19
15
|
}
|
|
20
|
-
|
|
21
|
-
const styles = StyleSheet.create({
|
|
22
|
-
container: {
|
|
23
|
-
flex: 1,
|
|
24
|
-
justifyContent: 'center',
|
|
25
|
-
alignItems: 'center',
|
|
26
|
-
},
|
|
27
|
-
gohome: {
|
|
28
|
-
marginTop: Spacing['2xl'],
|
|
29
|
-
},
|
|
30
|
-
});
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
3
1
|
import { Stack } from 'expo-router';
|
|
4
2
|
import { StatusBar } from 'expo-status-bar';
|
|
5
3
|
import { LogBox } from 'react-native';
|
|
6
4
|
import Toast from 'react-native-toast-message';
|
|
7
|
-
import {
|
|
8
|
-
|
|
5
|
+
import { Provider } from '@/components/Provider';
|
|
6
|
+
|
|
7
|
+
import '../global.css';
|
|
9
8
|
|
|
10
9
|
LogBox.ignoreLogs([
|
|
11
10
|
"TurboModuleRegistry.getEnforcing(...): 'RNMapsAirModule' could not be found",
|
|
@@ -14,23 +13,18 @@ LogBox.ignoreLogs([
|
|
|
14
13
|
|
|
15
14
|
export default function RootLayout() {
|
|
16
15
|
return (
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
</Stack>
|
|
31
|
-
<Toast />
|
|
32
|
-
</GestureHandlerRootView>
|
|
33
|
-
</ColorSchemeProvider>
|
|
34
|
-
</AuthProvider>
|
|
16
|
+
<Provider>
|
|
17
|
+
<Stack
|
|
18
|
+
screenOptions={{
|
|
19
|
+
animation: 'slide_from_right',
|
|
20
|
+
gestureEnabled: true,
|
|
21
|
+
gestureDirection: 'horizontal',
|
|
22
|
+
headerShown: false
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
<Stack.Screen name="index" options={{ title: "" }} />
|
|
26
|
+
</Stack>
|
|
27
|
+
<Toast />
|
|
28
|
+
</Provider>
|
|
35
29
|
);
|
|
36
30
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Fragment, useEffect, type ReactNode } from 'react';
|
|
2
|
+
import { ColorSchemeName, Platform } from 'react-native';
|
|
3
|
+
import { Uniwind } from 'uniwind'
|
|
4
|
+
|
|
5
|
+
// system: 跟随系统变化
|
|
6
|
+
// light: 固定为 light 主题
|
|
7
|
+
// dark: 固定为 dark 主题
|
|
8
|
+
const DEFAULT_THEME: 'system' | 'light' | 'dark' = 'system'
|
|
9
|
+
|
|
10
|
+
const WebOnlyColorSchemeUpdater = function ({ children }: { children?: ReactNode }) {
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
Uniwind.setTheme(DEFAULT_THEME);
|
|
13
|
+
}, []);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
function handleMessage(e: MessageEvent<{ event: string; colorScheme: ColorSchemeName; } | undefined>) {
|
|
17
|
+
if (e.data?.event === 'coze.workbench.colorScheme') {
|
|
18
|
+
const cs = e.data.colorScheme;
|
|
19
|
+
if (typeof cs === 'string') {
|
|
20
|
+
Uniwind.setTheme(cs);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (Platform.OS === 'web') {
|
|
26
|
+
window.addEventListener('message', handleMessage, false);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
if (Platform.OS === 'web') {
|
|
31
|
+
window.removeEventListener('message', handleMessage, false);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
return <Fragment>
|
|
37
|
+
{children}
|
|
38
|
+
</Fragment>
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
WebOnlyColorSchemeUpdater,
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AuthProvider } from '@/contexts/AuthContext';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
|
+
import { WebOnlyColorSchemeUpdater } from './ColorSchemeUpdater';
|
|
5
|
+
|
|
6
|
+
function Provider({ children }: { children: ReactNode }) {
|
|
7
|
+
return <WebOnlyColorSchemeUpdater>
|
|
8
|
+
<AuthProvider>
|
|
9
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
10
|
+
{children}
|
|
11
|
+
</GestureHandlerRootView>
|
|
12
|
+
</AuthProvider>
|
|
13
|
+
</WebOnlyColorSchemeUpdater>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
Provider,
|
|
18
|
+
}
|
|
@@ -142,7 +142,7 @@ const KeyboardAwareScrollable = ({
|
|
|
142
142
|
|
|
143
143
|
export const Screen = ({
|
|
144
144
|
children,
|
|
145
|
-
backgroundColor = '
|
|
145
|
+
backgroundColor = 'var(--background)',
|
|
146
146
|
statusBarStyle = 'dark',
|
|
147
147
|
statusBarColor = 'transparent',
|
|
148
148
|
safeAreaEdges = ['top', 'left', 'right', 'bottom'],
|