@zero-bits/taro-components 1.0.0 → 1.0.2
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/README.md +261 -291
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,211 +1,257 @@
|
|
|
1
|
-
# @zero-bits/taro
|
|
1
|
+
# @zero-bits/taro-components
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
针对 Taro 环境深度定制的企业级基础组件库,包含 **View**、**Text** 布局增强组件、**Permission 全局权限状态管理** 以及常用 **Hooks**。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @zero-bits/taro-components
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
在入口文件中引入样式:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import '@zero-bits/taro-components/index.css'
|
|
15
|
+
```
|
|
4
16
|
|
|
5
17
|
## 目录
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- [4. 请求主动强杀 (Abort)](#4-请求主动强杀-abort)
|
|
11
|
-
- [Permission 权限组件](#permission-权限组件)
|
|
18
|
+
|
|
19
|
+
- [View 布局组件](#view-布局组件)
|
|
20
|
+
- [Text 文本组件](#text-文本组件)
|
|
21
|
+
- [Permission 权限管理](#permission-权限管理)
|
|
12
22
|
- [1. 创建全局 Store](#1-创建全局-store)
|
|
13
23
|
- [2. 挂载 Provider](#2-挂载-provider)
|
|
14
24
|
- [3. 在组件中使用权限状态](#3-在组件中使用权限状态)
|
|
15
|
-
- [
|
|
16
|
-
- [1. 钩子工厂初始化](#1-钩子工厂初始化)
|
|
17
|
-
- [2. useQuery (声明式读取)](#2-usequery-声明式读取)
|
|
18
|
-
- [3. useMutation (命令式写入)](#3-usemutation-命令式写入)
|
|
25
|
+
- [useWeappUpdate 自动更新](#useweappupdate-自动更新)
|
|
19
26
|
|
|
20
27
|
---
|
|
21
28
|
|
|
22
|
-
##
|
|
29
|
+
## View 布局组件
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
对 `@tarojs/components` 的 `View` 进行深度封装,提供大量 Flex 布局与样式的 **Props 语法糖**,告别内联 `style` 对象,代码更简洁。
|
|
25
32
|
|
|
26
|
-
###
|
|
33
|
+
### 引入
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
```tsx
|
|
36
|
+
import { View } from '@zero-bits/taro-components'
|
|
37
|
+
```
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
39
|
+
### Props 一览
|
|
40
|
+
|
|
41
|
+
#### Flex 布局
|
|
42
|
+
|
|
43
|
+
| Prop | 类型 | 说明 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `row` | `boolean` | `flex-direction: row`,主轴水平 |
|
|
46
|
+
| `column` | `boolean` | `flex-direction: column`,主轴垂直 |
|
|
47
|
+
| `flex` | `boolean` | `flex: 1`,自动占满剩余空间 |
|
|
48
|
+
| `wrap` | `boolean` | `flex-wrap: wrap`,换行 |
|
|
49
|
+
| `between` | `boolean` | `justify-content: space-between` |
|
|
50
|
+
| `around` | `'justify-around' \| 'content-around'` | 主轴平均分布 |
|
|
51
|
+
| `justifyItems` | `'flex-start' \| 'flex-end' \| 'center'` | 主轴对齐方式 |
|
|
52
|
+
| `alignItems` | `'flex-start' \| 'flex-end' \| 'center'` | 交叉轴对齐方式 |
|
|
53
|
+
| `alignSelf` | `string` | 自身对齐方式 |
|
|
54
|
+
| `gap` | `string \| number` | 子元素间距 |
|
|
55
|
+
| `rowGap` | `string \| number` | 行间距 |
|
|
56
|
+
| `columnGap` | `string \| number` | 列间距 |
|
|
57
|
+
| `flexShrink` | `number \| string` | 收缩比例 |
|
|
58
|
+
| `flexGrow` | `number \| string` | 放大比例 |
|
|
59
|
+
| `flexBasis` | `string \| number` | 初始大小 |
|
|
60
|
+
|
|
61
|
+
#### 尺寸与定位
|
|
62
|
+
|
|
63
|
+
| Prop | 类型 | 说明 |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `full` | `boolean` | `width: 100%; height: 100%` |
|
|
66
|
+
| `width` | `string \| number` | 宽度(数字自动加 `px`) |
|
|
67
|
+
| `height` | `string \| number` | 高度 |
|
|
68
|
+
| `position` | `'static' \| 'relative' \| 'absolute' \| 'fixed' \| 'sticky'` | 定位方式 |
|
|
69
|
+
| `zIndex` | `number \| string` | 层级 |
|
|
70
|
+
| `top / right / bottom / left` | `number \| string` | 定位偏移量 |
|
|
71
|
+
| `overflow` | `'hidden' \| 'visible' \| 'scroll'` | 溢出处理 |
|
|
72
|
+
|
|
73
|
+
#### 外观
|
|
74
|
+
|
|
75
|
+
| Prop | 类型 | 说明 |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `bgColor` | `string` | 背景色 |
|
|
78
|
+
| `radius` | `string \| number` | 圆角 |
|
|
79
|
+
| `border` | `boolean` | 显示边框 |
|
|
80
|
+
| `borderColor` | `string` | 边框颜色 |
|
|
81
|
+
| `borderWidth` | `number \| string` | 边框宽度,默认 `1` |
|
|
82
|
+
| `borderStyle` | `'solid' \| 'dashed' \| 'dotted'` | 边框样式,默认 `solid` |
|
|
83
|
+
| `opacity` | `number \| string` | 透明度 |
|
|
84
|
+
| `hidden` | `boolean` | 隐藏(`display: none`) |
|
|
85
|
+
| `hover` | `boolean` | 点击降低透明度的悬停效果 |
|
|
86
|
+
| `skeleton` | `boolean` | 骨架屏闪烁效果 |
|
|
87
|
+
|
|
88
|
+
#### 间距(数字自动加 `px`)
|
|
89
|
+
|
|
90
|
+
| Prop | 对应 CSS |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `margin` | `margin` |
|
|
93
|
+
| `mTop` | `margin-top` |
|
|
94
|
+
| `mBottom` | `margin-bottom` |
|
|
95
|
+
| `mLeft` | `margin-left` |
|
|
96
|
+
| `mRight` | `margin-right` |
|
|
97
|
+
| `mHorizontal` | `margin-left` + `margin-right` |
|
|
98
|
+
| `mVertical` | `margin-top` + `margin-bottom` |
|
|
99
|
+
| `padding` | `padding` |
|
|
100
|
+
| `pTop` | `padding-top` |
|
|
101
|
+
| `pBottom` | `padding-bottom` |
|
|
102
|
+
| `pLeft` | `padding-left` |
|
|
103
|
+
| `pRight` | `padding-right` |
|
|
104
|
+
| `pHorizontal` | `padding-left` + `padding-right` |
|
|
105
|
+
| `pVertical` | `padding-top` + `padding-bottom` |
|
|
106
|
+
|
|
107
|
+
#### 安全区域
|
|
108
|
+
|
|
109
|
+
| Prop | 类型 | 说明 |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| `safeArea` | `'top' \| 'bottom' \| 'both'` | 自动叠加 `env(safe-area-inset-*)` |
|
|
112
|
+
|
|
113
|
+
### 使用示例
|
|
77
114
|
|
|
78
|
-
|
|
79
|
-
|
|
115
|
+
```tsx
|
|
116
|
+
import { View } from '@zero-bits/taro-components'
|
|
117
|
+
|
|
118
|
+
// 水平居中布局
|
|
119
|
+
<View row alignItems="center" justifyItems="center" gap={12}>
|
|
120
|
+
<View width={40} height={40} bgColor="#f0f0f0" radius={8} />
|
|
121
|
+
<View flex column>
|
|
122
|
+
<View height={16} bgColor="#333" radius={4} />
|
|
123
|
+
</View>
|
|
124
|
+
</View>
|
|
125
|
+
|
|
126
|
+
// 底部安全区域
|
|
127
|
+
<View safeArea="bottom" bgColor="#fff" pHorizontal={16}>
|
|
128
|
+
<Button>提交</Button>
|
|
129
|
+
</View>
|
|
130
|
+
|
|
131
|
+
// 绝对定位遮罩
|
|
132
|
+
<View position="absolute" top={0} left={0} full bgColor="rgba(0,0,0,0.5)" />
|
|
133
|
+
|
|
134
|
+
// 骨架屏占位
|
|
135
|
+
<View skeleton width={200} height={20} radius={4} />
|
|
80
136
|
```
|
|
81
137
|
|
|
82
|
-
|
|
138
|
+
---
|
|
83
139
|
|
|
84
|
-
|
|
140
|
+
## Text 文本组件
|
|
85
141
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// 1. 发起请求
|
|
93
|
-
request.get('/v1/user/profile')
|
|
94
|
-
.then((data) => {
|
|
95
|
-
// data 已经是提纯后的有效数据,没有 code/msg 外壳
|
|
96
|
-
// 且它绝对是通过了 2XX 和业务 code === 0 双重校验的
|
|
97
|
-
console.log('获取用户信息成功:', data)
|
|
98
|
-
})
|
|
99
|
-
.catch((err) => {
|
|
100
|
-
// 全局弹窗已经替你报错了,如果是主动取消,可以选择忽略
|
|
101
|
-
if (err instanceof AbortError) return
|
|
102
|
-
console.error('获取失败兜底:', err)
|
|
103
|
-
})
|
|
104
|
-
}, [])
|
|
105
|
-
|
|
106
|
-
return <View>Hello OnionRequest</View>
|
|
107
|
-
}
|
|
142
|
+
对 `@tarojs/components` 的 `Text` 进行封装,提供字体、间距、溢出省略等 Props 语法糖。
|
|
143
|
+
|
|
144
|
+
### 引入
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { Text } from '@zero-bits/taro-components'
|
|
108
148
|
```
|
|
109
149
|
|
|
110
|
-
###
|
|
150
|
+
### Props 一览
|
|
111
151
|
|
|
112
|
-
|
|
152
|
+
| Prop | 类型 | 说明 |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| `fontSize` | `string \| number` | 字体大小 |
|
|
155
|
+
| `color` | `string` | 字体颜色 |
|
|
156
|
+
| `bold` | `boolean \| number \| string` | 字体粗细,`true` 等同于 `bold` |
|
|
157
|
+
| `lineHeight` | `string \| number` | 行高 |
|
|
158
|
+
| `textAlign` | `'left' \| 'center' \| 'right' \| 'justify'` | 对齐方式 |
|
|
159
|
+
| `decoration` | `'underline' \| 'line-through' \| 'none'` | 文字装饰 |
|
|
160
|
+
| `clamp` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | 溢出省略显示行数 |
|
|
161
|
+
| `selectable` | `boolean` | 是否可长按选中 |
|
|
162
|
+
| `bgColor` | `string` | 背景色 |
|
|
163
|
+
| `width` | `string \| number` | 宽度 |
|
|
164
|
+
| `height` | `string \| number` | 高度 |
|
|
165
|
+
| `skeleton` | `boolean` | 骨架屏效果 |
|
|
166
|
+
| `style` | `CSSProperties` | 自定义样式(与上述 Props 合并) |
|
|
113
167
|
|
|
114
|
-
|
|
115
|
-
// 纯图片上传
|
|
116
|
-
const res1 = await request.upload('/v1/upload', 'wxfile://tmp_xxx.jpg')
|
|
117
|
-
|
|
118
|
-
// 带额外表单参数的上传
|
|
119
|
-
const res2 = await request.upload('/v1/upload', 'wxfile://tmp_xxx.jpg', {
|
|
120
|
-
userId: '10086',
|
|
121
|
-
type: 'avatar'
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
// 自定义上传字段名 (比如后端要求字段名是 'avatar' 而不是默认的 'file')
|
|
125
|
-
const res3 = await request.upload('/v1/upload', 'wxfile://tmp_xxx.jpg', null, {
|
|
126
|
-
name: 'avatar'
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
// 文件下载(二进制流)
|
|
130
|
-
const res4 = await request.get('/v1/report/export', null, {
|
|
131
|
-
responseType: 'arraybuffer' // 引擎会自动探测到流并静默放行,避免提纯引发崩溃
|
|
132
|
-
})
|
|
133
|
-
```
|
|
168
|
+
> 同样支持所有 `margin` / `padding` 系列间距 Props,与 `View` 一致。
|
|
134
169
|
|
|
135
|
-
###
|
|
170
|
+
### 使用示例
|
|
136
171
|
|
|
137
|
-
|
|
172
|
+
```tsx
|
|
173
|
+
import { Text } from '@zero-bits/taro-components'
|
|
138
174
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
import { request } from '@/utils/request'
|
|
175
|
+
// 标题
|
|
176
|
+
<Text fontSize={18} bold color="#333" lineHeight={28}>用户名称</Text>
|
|
142
177
|
|
|
143
|
-
|
|
144
|
-
|
|
178
|
+
// 两行省略
|
|
179
|
+
<Text clamp={2} fontSize={14} color="#666">
|
|
180
|
+
这是一段很长的描述文字,超过两行之后会自动省略显示...
|
|
181
|
+
</Text>
|
|
145
182
|
|
|
146
|
-
|
|
147
|
-
|
|
183
|
+
// 价格文字
|
|
184
|
+
<Text fontSize={24} bold color="#f00" decoration="underline">¥99.00</Text>
|
|
148
185
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
reqRef.current?.abort()
|
|
152
|
-
}
|
|
153
|
-
}, [])
|
|
154
|
-
}
|
|
186
|
+
// 骨架屏占位
|
|
187
|
+
<Text skeleton fontSize={14} width={120}>占位文字</Text>
|
|
155
188
|
```
|
|
156
189
|
|
|
157
190
|
---
|
|
158
191
|
|
|
159
|
-
## Permission
|
|
192
|
+
## Permission 权限管理
|
|
193
|
+
|
|
194
|
+
基于 `useSyncExternalStore` 构建的脱离 React 上下文的全局权限管理器,支持在请求拦截器中触发注销,自动驱动全站 UI 响应。
|
|
160
195
|
|
|
161
|
-
|
|
196
|
+
### 引入
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { createPermissionStore, PermissionProvider } from '@zero-bits/taro-components'
|
|
200
|
+
import { usePermission } from '@zero-bits/taro-components'
|
|
201
|
+
```
|
|
162
202
|
|
|
163
203
|
### 1. 创建全局 Store
|
|
164
204
|
|
|
165
205
|
在项目中创建 `src/store/permission.ts`:
|
|
166
206
|
|
|
167
207
|
```typescript
|
|
168
|
-
import { createPermissionStore } from '@zero-bits/taro
|
|
208
|
+
import { createPermissionStore } from '@zero-bits/taro-components'
|
|
169
209
|
|
|
170
|
-
//
|
|
210
|
+
// 定义权限对象类型
|
|
171
211
|
interface MyPermissions {
|
|
172
212
|
canAddUser: boolean
|
|
173
213
|
canDeleteUser: boolean
|
|
214
|
+
canExport: boolean
|
|
174
215
|
}
|
|
175
216
|
|
|
217
|
+
// 定义用户信息类型
|
|
176
218
|
interface MyUser {
|
|
177
219
|
id: string
|
|
178
220
|
name: string
|
|
179
|
-
role:
|
|
221
|
+
role: 'admin' | 'editor' | 'viewer'
|
|
180
222
|
}
|
|
181
223
|
|
|
182
|
-
// 初始化全局单例 Store
|
|
224
|
+
// 初始化全局单例 Store(整个应用只需一个)
|
|
183
225
|
export const permissionStore = createPermissionStore<MyPermissions, MyUser>()
|
|
184
226
|
```
|
|
185
227
|
|
|
186
|
-
|
|
228
|
+
可以在任何模块(如请求拦截器)中直接操作:
|
|
229
|
+
|
|
187
230
|
```typescript
|
|
188
|
-
|
|
231
|
+
import { permissionStore } from '@/store/permission'
|
|
232
|
+
|
|
233
|
+
// 在请求拦截器的 Token 刷新失败回调中
|
|
189
234
|
onRefreshFailed: () => {
|
|
190
|
-
permissionStore.denied()
|
|
235
|
+
permissionStore.denied() // 触发全局登出,PermissionProvider 的 onDenied 会自动响应
|
|
191
236
|
}
|
|
192
237
|
```
|
|
193
238
|
|
|
194
239
|
### 2. 挂载 Provider
|
|
195
240
|
|
|
196
|
-
|
|
241
|
+
在小程序入口文件 `app.tsx` 中包裹 `PermissionProvider`:
|
|
197
242
|
|
|
198
|
-
```
|
|
243
|
+
```tsx
|
|
199
244
|
import { PropsWithChildren } from 'react'
|
|
200
|
-
import
|
|
245
|
+
import Taro from '@tarojs/taro'
|
|
246
|
+
import { PermissionProvider } from '@zero-bits/taro-components'
|
|
201
247
|
import { permissionStore } from '@/store/permission'
|
|
202
248
|
|
|
203
249
|
function App({ children }: PropsWithChildren) {
|
|
204
250
|
return (
|
|
205
|
-
<PermissionProvider
|
|
251
|
+
<PermissionProvider
|
|
206
252
|
store={permissionStore}
|
|
207
253
|
onDenied={() => {
|
|
208
|
-
//
|
|
254
|
+
// 当 permissionStore.denied() 被调用时,统一执行登出跳转
|
|
209
255
|
Taro.reLaunch({ url: '/pages/login/index' })
|
|
210
256
|
}}
|
|
211
257
|
>
|
|
@@ -213,31 +259,31 @@ function App({ children }: PropsWithChildren) {
|
|
|
213
259
|
</PermissionProvider>
|
|
214
260
|
)
|
|
215
261
|
}
|
|
262
|
+
|
|
216
263
|
export default App
|
|
217
264
|
```
|
|
218
265
|
|
|
219
266
|
### 3. 在组件中使用权限状态
|
|
220
267
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
import { usePermission } from '@zero-bits/taro
|
|
225
|
-
import { Button, View, Text } from '@tarojs/components'
|
|
268
|
+
```tsx
|
|
269
|
+
import { Button } from '@tarojs/components'
|
|
270
|
+
import { View, Text } from '@zero-bits/taro-components'
|
|
271
|
+
import { usePermission } from '@zero-bits/taro-components'
|
|
226
272
|
|
|
227
273
|
export default function Dashboard() {
|
|
228
|
-
const {
|
|
229
|
-
isGranted,
|
|
230
|
-
user,
|
|
231
|
-
permissions,
|
|
232
|
-
granted,
|
|
233
|
-
denied
|
|
234
|
-
} = usePermission()
|
|
274
|
+
const {
|
|
275
|
+
isGranted, // boolean - 是否已登录/授权
|
|
276
|
+
user, // MyUser | null - 用户信息
|
|
277
|
+
permissions, // MyPermissions | null - 权限字典
|
|
278
|
+
granted, // (user, permissions) => void - 设置登录态
|
|
279
|
+
denied // () => void - 清除登录态(触发 onDenied)
|
|
280
|
+
} = usePermission<MyPermissions, MyUser>()
|
|
235
281
|
|
|
236
282
|
// 模拟登录
|
|
237
283
|
const handleLogin = () => {
|
|
238
284
|
granted(
|
|
239
|
-
{ id: '
|
|
240
|
-
{ canAddUser: true, canDeleteUser: false }
|
|
285
|
+
{ id: 'u001', name: '张三', role: 'admin' },
|
|
286
|
+
{ canAddUser: true, canDeleteUser: false, canExport: true }
|
|
241
287
|
)
|
|
242
288
|
}
|
|
243
289
|
|
|
@@ -246,168 +292,92 @@ export default function Dashboard() {
|
|
|
246
292
|
}
|
|
247
293
|
|
|
248
294
|
return (
|
|
249
|
-
<View>
|
|
250
|
-
<Text
|
|
251
|
-
|
|
295
|
+
<View column pHorizontal={16}>
|
|
296
|
+
<Text fontSize={16} bold>欢迎,{user?.name}</Text>
|
|
297
|
+
|
|
252
298
|
{/* 细粒度权限控制 */}
|
|
253
|
-
{permissions
|
|
254
|
-
{permissions
|
|
255
|
-
|
|
256
|
-
|
|
299
|
+
{permissions?.canAddUser && <Button>新增用户</Button>}
|
|
300
|
+
{permissions?.canDeleteUser && <Button>删除用户</Button>}
|
|
301
|
+
{permissions?.canExport && <Button>导出数据</Button>}
|
|
302
|
+
|
|
257
303
|
<Button onClick={denied}>退出登录</Button>
|
|
258
304
|
</View>
|
|
259
305
|
)
|
|
260
306
|
}
|
|
261
307
|
```
|
|
262
308
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
## Axios 适配器 (Taro Request)
|
|
266
|
-
|
|
267
|
-
如果您或您的团队更倾向于使用传统的 Axios 生态(或者需要复用已有的 Axios 拦截器与代码库),我们同样提供了一套极其严谨的 **Taro Axios 适配器**,完美抹平了 Web 与小程序的网络层差异。
|
|
268
|
-
|
|
269
|
-
### 1. 初始化 Axios 引擎
|
|
309
|
+
#### `usePermission` 返回值
|
|
270
310
|
|
|
271
|
-
|
|
311
|
+
| 字段 | 类型 | 说明 |
|
|
312
|
+
|---|---|---|
|
|
313
|
+
| `isGranted` | `boolean` | 当前是否处于已授权(登录)状态 |
|
|
314
|
+
| `user` | `U \| null` | 当前用户信息,未登录时为 `null` |
|
|
315
|
+
| `permissions` | `P \| null` | 当前权限字典,未登录时为 `null` |
|
|
316
|
+
| `granted` | `(user: U, permissions: P) => void` | 设置登录态与权限 |
|
|
317
|
+
| `denied` | `() => void` | 清除登录态,触发 Provider 的 `onDenied` 回调 |
|
|
272
318
|
|
|
273
|
-
|
|
274
|
-
import { initializeRequest, request, taroAdapter, defaultTransformRequest } from '@zero-bits/taro/request'
|
|
275
|
-
|
|
276
|
-
// 全局初始化
|
|
277
|
-
initializeRequest({
|
|
278
|
-
baseURL: 'https://api.your-company.com',
|
|
279
|
-
timeout: 10000,
|
|
280
|
-
|
|
281
|
-
// 核心:强制 Axios 使用 Taro 的网络底层
|
|
282
|
-
adapter: taroAdapter,
|
|
283
|
-
|
|
284
|
-
// 推荐:使用我们深度防御后的序列化方法
|
|
285
|
-
transformRequest: [defaultTransformRequest]
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
// 按需添加 Axios 传统的拦截器
|
|
289
|
-
request.interceptors.request.use(config => {
|
|
290
|
-
config.headers.Authorization = 'Bearer token123'
|
|
291
|
-
return config
|
|
292
|
-
})
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### 2. 发起请求
|
|
296
|
-
|
|
297
|
-
配置完毕后,它完全等同于一个标准的 Axios 实例:
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
import { request } from '@/utils/request'
|
|
301
|
-
|
|
302
|
-
// GET
|
|
303
|
-
const res1 = await request.get('/v1/users')
|
|
304
|
-
|
|
305
|
-
// POST
|
|
306
|
-
const res2 = await request.post('/v1/users', { name: 'Zou' })
|
|
307
|
-
|
|
308
|
-
// 文件上传 (原生适配)
|
|
309
|
-
// 只要同时包含了 filePath、name 并且 Content-Type 为 multipart/form-data,
|
|
310
|
-
// 适配器底层会自动切换为 Taro.uploadFile
|
|
311
|
-
const res3 = await request.post('/v1/upload', {
|
|
312
|
-
filePath: 'wxfile://tmp_xxx.jpg',
|
|
313
|
-
name: 'file',
|
|
314
|
-
type: 'avatar'
|
|
315
|
-
}, {
|
|
316
|
-
headers: {
|
|
317
|
-
'Content-Type': 'multipart/form-data'
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
// 取消请求 (支持 AbortController 规范)
|
|
322
|
-
const controller = new AbortController()
|
|
323
|
-
request.get('/v1/long-polling', { signal: controller.signal })
|
|
324
|
-
controller.abort() // 原生打断 Taro.RequestTask
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
**对比总结**:
|
|
328
|
-
- 如果您是从零搭建微信小程序/Taro 项目,强烈建议使用上面的 **OnionRequest**(洋葱模型),它内置了极其强悍的防爆红、无感刷新等特性。
|
|
329
|
-
- 如果您的代码库是从 Web (React/Vue) 跨端迁移过来的,含有大量的 Axios 历史逻辑,可以直接使用本节的 **Axios 适配器**。
|
|
319
|
+
> **注意**:`usePermission` 必须在 `<PermissionProvider>` 内部使用,否则会抛出错误。
|
|
330
320
|
|
|
331
321
|
---
|
|
332
322
|
|
|
333
|
-
##
|
|
334
|
-
|
|
335
|
-
基于 **Alova V3** 构建的终极 React 请求状态机。它是一个抽象工厂,**可以完美接入上述的 OnionRequest 或 Axios**。
|
|
336
|
-
提供极致的 CQRS (读写分离) 体验,告别手写 `useState(loading)` 和 `try...catch`,内置防抖、竞态控制、React 并发模型防撕裂(Tearing),以及极其严苛的 TypeScript 类型约束。
|
|
323
|
+
## useWeappUpdate 自动更新
|
|
337
324
|
|
|
338
|
-
|
|
325
|
+
微信小程序版本自动检测与更新提示的封装 Hook。在 app 根组件中调用一次即可,非微信环境自动跳过。
|
|
339
326
|
|
|
340
|
-
|
|
327
|
+
### 引入
|
|
341
328
|
|
|
342
329
|
```typescript
|
|
343
|
-
import {
|
|
344
|
-
import { request } from './request' // 您的 axios 或 onion 实例
|
|
345
|
-
|
|
346
|
-
export const { useQuery, useMutation, alovaInst } = createRequestHooks(request, {
|
|
347
|
-
// 全局定义后端的标准响应壳字段,引擎会自动进行“启发式剥壳”
|
|
348
|
-
fieldMap: { code: 'code', data: 'data', message: 'message' }
|
|
349
|
-
})
|
|
330
|
+
import { useWeappUpdate } from '@zero-bits/taro-components'
|
|
350
331
|
```
|
|
351
332
|
|
|
352
|
-
###
|
|
353
|
-
|
|
354
|
-
专用于页面初次加载或依赖变化时自动获取数据。支持依赖追踪、内置防抖。
|
|
333
|
+
### 使用示例
|
|
355
334
|
|
|
356
335
|
```tsx
|
|
357
|
-
|
|
358
|
-
import {
|
|
359
|
-
|
|
360
|
-
export default function UserList() {
|
|
361
|
-
const [keyword, setKeyword] = useState('')
|
|
362
|
-
|
|
363
|
-
// 泛型: <返回的数据体类型, 额外响应字段类型>
|
|
364
|
-
const { data: users, loading, extra } = useQuery<User[], { total: number }>(
|
|
365
|
-
() => ({
|
|
366
|
-
url: '/api/users',
|
|
367
|
-
params: { keyword }
|
|
368
|
-
}),
|
|
369
|
-
[keyword], // keyword 改变自动防抖重新请求
|
|
370
|
-
{ debounce: 500 }
|
|
371
|
-
)
|
|
336
|
+
// app.tsx
|
|
337
|
+
import { useWeappUpdate } from '@zero-bits/taro-components'
|
|
372
338
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
</View>
|
|
379
|
-
)
|
|
339
|
+
function App({ children }) {
|
|
340
|
+
// 挂载时自动检测更新,有新版本弹窗提示用户重启
|
|
341
|
+
useWeappUpdate()
|
|
342
|
+
|
|
343
|
+
return <PermissionProvider ...>{children}</PermissionProvider>
|
|
380
344
|
}
|
|
381
345
|
```
|
|
382
346
|
|
|
383
|
-
###
|
|
347
|
+
### 行为说明
|
|
384
348
|
|
|
385
|
-
|
|
349
|
+
- **检测到新版本**:弹出 Modal 提示"新版本已经准备好,是否重启应用?",用户确认后调用 `applyUpdate()` 重启。
|
|
350
|
+
- **下载失败**:弹出 Modal 提示用户删除小程序后重新搜索打开。
|
|
351
|
+
- **非微信环境**(H5 / RN 等):`process.env.TARO_ENV !== 'weapp'` 时自动跳过,无任何副作用。
|
|
386
352
|
|
|
387
|
-
|
|
388
|
-
import { useMutation } from '@/utils/requestHooks'
|
|
389
|
-
|
|
390
|
-
export default function CreateUser() {
|
|
391
|
-
// 泛型: <返回的数据体类型, [send的入参类型]>
|
|
392
|
-
const { send, loading } = useMutation<null, [string, number]>(
|
|
393
|
-
(name, age) => ({
|
|
394
|
-
url: '/api/user/create',
|
|
395
|
-
method: 'POST',
|
|
396
|
-
data: { name, age }
|
|
397
|
-
}),
|
|
398
|
-
{
|
|
399
|
-
onSuccess: (event) => console.log('保存成功', event.data),
|
|
400
|
-
onError: (event) => console.error('报错了', event.error)
|
|
401
|
-
}
|
|
402
|
-
)
|
|
353
|
+
---
|
|
403
354
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
355
|
+
## 完整导出列表
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// 主入口
|
|
359
|
+
import {
|
|
360
|
+
View,
|
|
361
|
+
Text,
|
|
362
|
+
PermissionProvider,
|
|
363
|
+
createPermissionStore,
|
|
364
|
+
usePermission,
|
|
365
|
+
useWeappUpdate
|
|
366
|
+
} from '@zero-bits/taro-components'
|
|
367
|
+
|
|
368
|
+
// 按路径导入(tree-shaking 友好)
|
|
369
|
+
import { View, Text, PermissionProvider, createPermissionStore } from '@zero-bits/taro-components/components'
|
|
370
|
+
import { usePermission, useWeappUpdate } from '@zero-bits/taro-components/hooks'
|
|
371
|
+
|
|
372
|
+
// 样式
|
|
373
|
+
import '@zero-bits/taro-components/index.css'
|
|
413
374
|
```
|
|
375
|
+
|
|
376
|
+
## Peer Dependencies
|
|
377
|
+
|
|
378
|
+
| 包 | 版本要求 |
|
|
379
|
+
|---|---|
|
|
380
|
+
| `react` | `>= 18.0.0` |
|
|
381
|
+
| `react-dom` | `>= 18.0.0` |
|
|
382
|
+
| `@tarojs/components` | `>= 4.2.1` |
|
|
383
|
+
| `@tarojs/taro` | `>= 4.2.1` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zero-bits/taro-components",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Taro components and utilities",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.js",
|
|
@@ -65,4 +65,4 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"classnames": "^2.3.0"
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|