@gm-mobile/business 1.1.11 → 1.1.13

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 ADDED
@@ -0,0 +1,124 @@
1
+ # @gm-mobile/business
2
+
3
+ ## 简介
4
+
5
+ @gm-mobile/business 是业务组件包,提供了常见的业务场景组件,包括优惠券、商品选择、日期选择和电子签名等功能。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install @gm-mobile/business
11
+ # 或
12
+ yarn add @gm-mobile/business
13
+ ```
14
+
15
+ ## 包含的组件
16
+
17
+ ### 1. Coupon & ReceivedCoupon
18
+ 优惠券组件,支持多种状态的优惠券展示。
19
+
20
+ - **Coupon**: 可领取/可使用的优惠券组件
21
+ - **ReceivedCoupon**: 已领取优惠券组件
22
+ - **适用场景**:营销活动、优惠券管理、订单结算
23
+ - **详细文档**:[Coupon README](src/component/coupon/README.md)
24
+
25
+ ### 2. ProductSelection
26
+ 商品选择组件,支持字母索引和商品搜索。
27
+
28
+ - **适用场景**:商品选择、规格选择、商品列表
29
+ - **主要功能**:
30
+ - 字母索引导航
31
+ - 商品搜索
32
+ - 多选/单选
33
+ - 自定义渲染
34
+ - **详细文档**:[ProductSelection README](src/component/product_selection/README.md)
35
+
36
+ ### 3. TabDateSelect
37
+ 标签日期选择器,支持日期范围选择和服务时间配置。
38
+
39
+ - **适用场景**:配送日期选择、预约时间选择
40
+ - **主要功能**:
41
+ - 日期标签切换
42
+ - 时间段选择
43
+ - 服务时间配置
44
+ - 弹窗展示
45
+ - **详细文档**:[TabDateSelect README](src/component/tab_date_select/README.md)
46
+
47
+ ### 4. Signature
48
+ 电子签名组件,支持手写签名和签名查看。
49
+
50
+ - **适用场景**:电子合同、签收确认、授权签名
51
+ - **主要功能**:
52
+ - 手写签名画布
53
+ - 签名查看模式
54
+ - Base64/Blob 导出
55
+ - 禁用编辑
56
+ - **详细文档**:[Signature README](src/component/signature/README.md)
57
+
58
+ ## 使用示例
59
+
60
+ ```jsx
61
+ import {
62
+ Coupon,
63
+ ReceivedCoupon,
64
+ ProductSelection,
65
+ TabDateSelect,
66
+ Signature
67
+ } from '@gm-mobile/business'
68
+
69
+ // 优惠券组件
70
+ <Coupon
71
+ title="新人专享券"
72
+ subTitle="满100减10"
73
+ status="available"
74
+ onChange={(checked) => console.log(checked)}
75
+ />
76
+
77
+ // 商品选择组件
78
+ <ProductSelection
79
+ data={productList}
80
+ selected={selectedProducts}
81
+ onSelect={handleSelect}
82
+ />
83
+
84
+ // 日期选择器
85
+ <TabDateSelect.render({
86
+ tabs: ['今天', '明天'],
87
+ begin: '2026-03-27',
88
+ end: '2026-03-28',
89
+ onSelect: handleSelect,
90
+ })
91
+
92
+ // 电子签名
93
+ <Signature
94
+ onSave={(base64) => console.log('签名保存:', base64)}
95
+ disabledEdit={false}
96
+ />
97
+ ```
98
+
99
+ ## 开发依赖
100
+
101
+ - **React**: ^16.13.1
102
+ - **@gm-mobile/react**: 依赖基础组件库
103
+
104
+ ## 相关包
105
+
106
+ - [@gm-mobile/react](../react/README.md) - 基础组件库
107
+ - [@gm-mobile/swiper](../swiper/README.md) - 轮播组件库
108
+ - [@gm-mobile/service_time](../service_time/README.md) - 服务时间组件库
109
+
110
+ ## 快速链接
111
+
112
+ - [Coupon 组件文档](src/component/coupon/README.md)
113
+ - [ProductSelection 组件文档](src/component/product_selection/README.md)
114
+ - [TabDateSelect 组件文档](src/component/tab_date_select/README.md)
115
+ - [Signature 组件文档](src/component/signature/README.md)
116
+
117
+ ## 许可证
118
+
119
+ ISC
120
+
121
+ ---
122
+
123
+ **版本**: v1.1.12
124
+ **最后更新**: 2026-03-27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gm-mobile/business",
3
- "version": "1.1.11",
3
+ "version": "1.1.13",
4
4
  "description": "> TODO: description",
5
5
  "author": "liyatang <liyatang@qq.com>",
6
6
  "homepage": "https://github.com/gmfe/business#readme",
@@ -21,9 +21,9 @@
21
21
  "url": "https://github.com/gmfe/business/issues"
22
22
  },
23
23
  "dependencies": {
24
- "@gm-mobile/locales": "^1.1.11",
25
- "@gm-mobile/react": "^1.1.11",
26
- "@gm-mobile/service_time": "^1.1.11"
24
+ "@gm-mobile/locales": "^1.1.13",
25
+ "@gm-mobile/react": "^1.1.13",
26
+ "@gm-mobile/service_time": "^1.1.13"
27
27
  },
28
- "gitHead": "442a85f336ef085e639bb447300e38e8c3feb7df"
28
+ "gitHead": "99f7babda660876d45627b0f0c9378ce655de360"
29
29
  }
@@ -0,0 +1,256 @@
1
+ # Coupon
2
+
3
+ ## 简介
4
+ 优惠券组件 - 用于展示优惠券信息,支持多种状态(正常、过期、已使用、禁用)和交互(领取、使用、勾选),适用于电商、营销等业务场景。
5
+
6
+ ## API
7
+
8
+ ### Coupon Props
9
+
10
+ | 属性 | 说明 | 类型 | 默认值 | 必填 |
11
+ |------|------|------|--------|------|
12
+ | currency | 折扣金额货币符号 | string | - | 是 |
13
+ | discount | 折扣金额 | number | - | 是 |
14
+ | totalInfo | 满减说明,如"满1000元可用" | string | - | 否 |
15
+ | dateInfo | 使用日期说明,可以是字符串或 React 元素 | string \| element | - | 否 |
16
+ | title | 优惠券标题 | string | - | 否 |
17
+ | labels | 优惠券标签数组,如 ['分类券', '新人券'] | array | - | 否 |
18
+ | hasUseInfo | 是否显示使用说明区域 | boolean | false | 否 |
19
+ | useInfo | 使用说明的内容,可以是文本或 React 元素 | element | - | 否 |
20
+ | onUse | 点击"立即使用"按钮的回调函数 | function | - | 否 |
21
+ | disabled | 是否禁用优惠券 | boolean | false | 否 |
22
+ | checked | 优惠券的勾选状态 | boolean | false | 否 |
23
+ | onCheck | 点击优惠券进行勾选的回调函数 | function | - | 否 |
24
+ | isExpired | 优惠券是否已过期 | boolean | false | 否 |
25
+ | isUsed | 优惠券是否已使用 | boolean | false | 否 |
26
+ | onReceived | 点击"立即领取"按钮的回调函数 | function | - | 否 |
27
+ | couponAmount | 可领取的优惠券数量 | number | - | 否 |
28
+ | className | 自定义类名 | string | - | 否 |
29
+ | style | 自定义样式 | object | - | 否 |
30
+
31
+ ### ReceivedCoupon Props
32
+
33
+ ReceivedCoupon 是 Coupon 的简化版本,专门用于优惠券领取场景。
34
+
35
+ | 属性 | 说明 | 类型 | 默认值 | 必填 |
36
+ |------|------|------|--------|------|
37
+ | currency | 折扣金额货币符号 | string | - | 是 |
38
+ | discount | 折扣金额 | string | - | 是 |
39
+ | totalInfo | 满减说明 | string | - | 否 |
40
+ | labels | 优惠券标签数组 | array | - | 否 |
41
+ | couponAmount | 可领取的优惠券数量 | number | - | 否 |
42
+ | isReceived | 是否已领取 | boolean | false | 否 |
43
+ | onReceived | 点击"领取"按钮的回调函数 | function | - | 否 |
44
+ | className | 自定义类名 | string | - | 否 |
45
+ | style | 自定义样式 | object | - | 否 |
46
+
47
+ ## 示例
48
+
49
+ ### 基础用法
50
+
51
+ 最简单的优惠券展示,包含基本的金额、满减信息和日期:
52
+
53
+ ```jsx
54
+ import { Coupon } from '@gm-mobile/react'
55
+
56
+ <Coupon
57
+ currency='¥'
58
+ discount={100}
59
+ totalInfo='满1000元可用'
60
+ dateInfo='2024-01-01~2024-12-31'
61
+ title='新人优惠券'
62
+ />
63
+ ```
64
+
65
+ ### 带标签和勾选的优惠券
66
+
67
+ 支持显示多个标签和勾选功能:
68
+
69
+ ```jsx
70
+ import { Coupon } from '@gm-mobile/react'
71
+ import { observable } from 'mobx'
72
+
73
+ const store = observable({
74
+ checked: false,
75
+ setChecked(checked) {
76
+ this.checked = checked
77
+ },
78
+ })
79
+
80
+ <Coupon
81
+ currency='¥'
82
+ discount={100}
83
+ totalInfo='满1000元可用'
84
+ dateInfo='2024-01-01~2024-12-31'
85
+ title='分类优惠券'
86
+ labels={['分类券', '新人券']}
87
+ checked={store.checked}
88
+ onCheck={() => store.setChecked(!store.checked)}
89
+ />
90
+ ```
91
+
92
+ ### 带使用说明的优惠券
93
+
94
+ 可以展开查看详细的使用说明:
95
+
96
+ ```jsx
97
+ import { Coupon } from '@gm-mobile/react'
98
+
99
+ <Coupon
100
+ currency='¥'
101
+ discount={30}
102
+ totalInfo='满1000元可用'
103
+ dateInfo='2024-01-01~2024-12-31'
104
+ title='分类优惠券'
105
+ labels={['仅限特定商品使用', '商品券']}
106
+ hasUseInfo
107
+ useInfo={
108
+ <div>
109
+ 1. 仅限指定商品使用
110
+ <br />
111
+ 2. 每个订单限用一张
112
+ <br />
113
+ 3. 不可与其他优惠叠加
114
+ </div>
115
+ }
116
+ couponAmount={4}
117
+ onUse={() => console.log('使用优惠券')}
118
+ />
119
+ ```
120
+
121
+ ### 不同状态的优惠券
122
+
123
+ 展示优惠券的不同状态:禁用、过期、已使用:
124
+
125
+ ```jsx
126
+ import { Coupon, Flex } from '@gm-mobile/react'
127
+
128
+ <Flex column>
129
+ {/* 禁用状态 */}
130
+ <Coupon
131
+ currency='¥'
132
+ discount={200}
133
+ totalInfo='满300元可用'
134
+ dateInfo='2024-01-01到期'
135
+ title='禁用优惠券'
136
+ disabled
137
+ />
138
+
139
+ {/* 过期状态 */}
140
+ <Coupon
141
+ currency='¥'
142
+ discount={200}
143
+ totalInfo='满300元可用'
144
+ dateInfo='2024-01-01到期'
145
+ title='过期优惠券'
146
+ isExpired
147
+ />
148
+
149
+ {/* 已使用状态 */}
150
+ <Coupon
151
+ currency='¥'
152
+ discount={200}
153
+ totalInfo='满300元可用'
154
+ dateInfo='2024-01-01到期'
155
+ title='已使用优惠券'
156
+ isUsed
157
+ />
158
+ </Flex>
159
+ ```
160
+
161
+ ### 可领取的优惠券
162
+
163
+ 支持领取功能的优惠券:
164
+
165
+ ```jsx
166
+ import { Coupon } from '@gm-mobile/react'
167
+
168
+ <Coupon
169
+ currency='¥'
170
+ discount={200}
171
+ totalInfo='满300元可用'
172
+ title='可领取优惠券'
173
+ couponAmount={10}
174
+ onReceived={() => console.log('领取优惠券')}
175
+ />
176
+ ```
177
+
178
+ ### ReceivedCoupon 简化版优惠券
179
+
180
+ 用于优惠券领取列表的简化展示:
181
+
182
+ ```jsx
183
+ import { ReceivedCoupon } from '@gm-mobile/react'
184
+
185
+ {/* 未领取状态 */}
186
+ <ReceivedCoupon
187
+ currency='¥'
188
+ discount='10'
189
+ totalInfo='满100元可用'
190
+ couponAmount={1}
191
+ labels={['仅限特定商品使用']}
192
+ onReceived={() => console.log('领取')}
193
+ />
194
+
195
+ {/* 已领取状态 */}
196
+ <ReceivedCoupon
197
+ currency='¥'
198
+ discount='100'
199
+ totalInfo='满200元可用'
200
+ isReceived
201
+ />
202
+ ```
203
+
204
+ ### 自定义日期信息
205
+
206
+ dateInfo 支持传入 React 元素实现复杂的日期展示:
207
+
208
+ ```jsx
209
+ import { Coupon, Flex } from '@gm-mobile/react'
210
+
211
+ <Coupon
212
+ currency='¥'
213
+ discount={30}
214
+ totalInfo='满1000元可用'
215
+ dateInfo={
216
+ <Flex wrap>
217
+ <span>2024-01-01</span>
218
+ <span>~</span>
219
+ <span>2024-12-31</span>
220
+ </Flex>
221
+ }
222
+ title='自定义日期优惠券'
223
+ labels={['仅限特定商品使用', '商品使用']}
224
+ hasUseInfo
225
+ useInfo='详细的使用说明内容'
226
+ onUse={() => console.log('使用')}
227
+ />
228
+ ```
229
+
230
+ ## 注意事项
231
+
232
+ 1. **必填属性**:`currency` 和 `discount` 是必填属性,必须传入这两个参数才能正常显示优惠券。
233
+
234
+ 2. **状态优先级**:当 `disabled`、`isExpired` 或 `isUsed` 任一为 true 时,优惠券会显示为不可用状态,并且"立即使用"/"立即领取"按钮将不可点击。
235
+
236
+ 3. **勾选功能**:如果需要使用勾选功能,必须同时传入 `checked` 和 `onCheck` 属性。`onCheck` 会接收新的勾选状态作为参数。
237
+
238
+ 4. **使用说明**:只有当 `hasUseInfo` 为 true 时,才会显示使用说明区域。点击可以展开/收起 `useInfo` 的内容。
239
+
240
+ 5. **按钮互斥**:`onUse` 和 `onReceived` 不能同时使用,根据业务场景选择一个传入。
241
+
242
+ 6. **labels 数组**:标签数组会自动渲染为多个小标签,建议传入 1-3 个标签,避免过多导致显示混乱。
243
+
244
+ 7. **couponAmount 显示位置**:
245
+ - 在 Coupon 组件中,显示在日期信息下方
246
+ - 在 ReceivedCoupon 组件中,显示在金额右侧的括号中
247
+
248
+ 8. **日期信息格式**:`dateInfo` 支持字符串或 React 元素,可以传入简单的日期字符串,也可以传入复杂的布局元素。
249
+
250
+ 9. **ReceivedCoupon vs Coupon**:
251
+ - Coupon:功能完整,适合优惠券列表、我的优惠券等场景
252
+ - ReceivedCoupon:样式简洁,适合优惠券领取中心、首页优惠券推荐等场景
253
+
254
+ ## 相关组件
255
+
256
+ - [Checkbox](../checkbox/README.md) - Coupon 内部使用的勾选组件
@@ -0,0 +1,208 @@
1
+ # ProductSelection
2
+
3
+ ## 简介
4
+ 商品选择组件 - 一个支持多选的商品列表组件,带字母索引导航、已选商品展示弹窗和底部操作栏,适用于需要从大量商品中选择多个商品的业务场景。
5
+
6
+ ## API
7
+
8
+ ### Props
9
+
10
+ | 属性 | 说明 | 类型 | 默认值 | 必填 |
11
+ |------|------|------|--------|------|
12
+ | data | 商品列表数据,格式为 `[{value, text}]`,value 为唯一标识,text 为显示文本 | `array` | - | 是 |
13
+ | selected | 已选商品的 value 数组 | `array` | `[]` | 否 |
14
+ | onSelect | 选择变化时的回调函数,参数为当前选中的 value 数组 | `function` | - | 是 |
15
+ | renderItem | 自定义列表项渲染函数,接收一个 item 参数,返回 React 节点 | `function` | `(item) => item.text` | 否 |
16
+ | className | 自定义类名 | `string` | - | 否 |
17
+ | style | 自定义样式 | `object` | - | 否 |
18
+
19
+ ### 组件结构说明
20
+
21
+ ProductSelection 由以下三个部分组成:
22
+
23
+ 1. **字母索引列表**(继承自 LetterIndexMultiple)
24
+ - 支持按字母分组快速定位
25
+ - 支持多选操作
26
+ - 可自定义列表项渲染
27
+
28
+ 2. **底部操作栏**
29
+ - 显示已选商品数量(带角标提示)
30
+ - 购物车图标,点击可查看已选商品列表
31
+ - 确定添加按钮,提交最终选择结果
32
+
33
+ 3. **已选商品弹窗**
34
+ - 展示所有已选商品
35
+ - 支持单个删除操作
36
+ - 带遮罩层,点击遮罩关闭
37
+
38
+ ## 示例
39
+
40
+ ### 基础用法
41
+
42
+ 最简单的使用方式,配合状态管理库(如 mobx)管理选中状态:
43
+
44
+ ```jsx
45
+ import React from 'react'
46
+ import { Page } from '@gm-mobile/react'
47
+ import { ProductSelection } from '@gm-mobile/business'
48
+
49
+ const Example = () => {
50
+ const [selected, setSelected] = React.useState([])
51
+
52
+ const data = [
53
+ { value: 'apple', text: '苹果' },
54
+ { value: 'banana', text: '香蕉' },
55
+ { value: 'orange', text: '橙子' },
56
+ { value: 'watermelon', text: '西瓜' },
57
+ ]
58
+
59
+ return (
60
+ <Page>
61
+ <ProductSelection
62
+ data={data}
63
+ selected={selected}
64
+ onSelect={setSelected}
65
+ />
66
+ </Page>
67
+ )
68
+ }
69
+
70
+ export default Example
71
+ ```
72
+
73
+ ### 带默认选中值
74
+
75
+ 初始化时传入已选中的商品:
76
+
77
+ ```jsx
78
+ import React from 'react'
79
+ import { Page } from '@gm-mobile/react'
80
+ import { ProductSelection } from '@gm-mobile/business'
81
+
82
+ const Example = () => {
83
+ // 默认选中苹果和香蕉
84
+ const [selected, setSelected] = React.useState(['apple', 'banana'])
85
+
86
+ const data = [
87
+ { value: 'apple', text: '苹果' },
88
+ { value: 'banana', text: '香蕉' },
89
+ { value: 'orange', text: '橙子' },
90
+ { value: 'watermelon', text: '西瓜' },
91
+ { value: 'grape', text: '葡萄' },
92
+ ]
93
+
94
+ return (
95
+ <Page>
96
+ <ProductSelection
97
+ data={data}
98
+ selected={selected}
99
+ onSelect={setSelected}
100
+ />
101
+ </Page>
102
+ )
103
+ }
104
+
105
+ export default Example
106
+ ```
107
+
108
+ ### 自定义列表项渲染
109
+
110
+ 通过 `renderItem` 属性自定义每个商品的展示样式:
111
+
112
+ ```jsx
113
+ import React from 'react'
114
+ import { Page } from '@gm-mobile/react'
115
+ import { ProductSelection } from '@gm-mobile/business'
116
+
117
+ const Example = () => {
118
+ const [selected, setSelected] = React.useState([])
119
+
120
+ const data = [
121
+ { value: 'apple', text: '苹果', price: '5.00', stock: 100 },
122
+ { value: 'banana', text: '香蕉', price: '3.50', stock: 50 },
123
+ { value: 'orange', text: '橙子', price: '4.00', stock: 80 },
124
+ ]
125
+
126
+ // 自定义渲染,显示价格和库存
127
+ const renderItem = (item) => (
128
+ <div>
129
+ <div>{item.text}</div>
130
+ <div style={{ fontSize: '12px', color: '#999' }}>
131
+ ¥{item.price} | 库存: {item.stock}
132
+ </div>
133
+ </div>
134
+ )
135
+
136
+ return (
137
+ <Page>
138
+ <ProductSelection
139
+ data={data}
140
+ selected={selected}
141
+ onSelect={setSelected}
142
+ renderItem={renderItem}
143
+ />
144
+ </Page>
145
+ )
146
+ }
147
+
148
+ export default Example
149
+ ```
150
+
151
+ ### 提交已选商品
152
+
153
+ 监听 `onSelect` 回调,在点击确定按钮后处理选中的商品:
154
+
155
+ ```jsx
156
+ import React from 'react'
157
+ import { Page, Toast } from '@gm-mobile/react'
158
+ import { ProductSelection } from '@gm-mobile/business'
159
+
160
+ const Example = () => {
161
+ const [selected, setSelected] = React.useState([])
162
+
163
+ const data = [
164
+ { value: 'apple', text: '苹果' },
165
+ { value: 'banana', text: '香蕉' },
166
+ { value: 'orange', text: '橙子' },
167
+ ]
168
+
169
+ // 处理选择确认
170
+ const handleSelect = (selectedList) => {
171
+ setSelected(selectedList)
172
+
173
+ // 可以在这里执行提交逻辑
174
+ if (selectedList.length > 0) {
175
+ console.log('已选商品:', selectedList)
176
+ Toast.success(`已添加 ${selectedList.length} 个商品`)
177
+ }
178
+ }
179
+
180
+ return (
181
+ <Page>
182
+ <ProductSelection
183
+ data={data}
184
+ selected={selected}
185
+ onSelect={handleSelect}
186
+ />
187
+ </Page>
188
+ )
189
+ }
190
+
191
+ export default Example
192
+ ```
193
+
194
+ ## 注意事项
195
+
196
+ 1. **数据格式要求**:`data` 属性必须是对象数组,每个对象必须包含 `value`(唯一标识)和 `text`(显示文本)字段。
197
+
198
+ 2. **状态管理**:建议使用 React hooks(如 `useState`)或状态管理库(如 MobX)来管理 `selected` 状态,确保视图正确更新。
199
+
200
+ 3. **选中值传递**:`selected` 和 `onSelect` 参数中的选中项都是 `value` 数组,不是完整的商品对象。
201
+
202
+ 4. **字母分组**:组件会自动根据 `text` 字段的首字母进行分组和索引,无需手动处理。
203
+
204
+ 5. **底部按钮状态**:当没有选中任何商品时,底部购物车图标和确定按钮处于禁用状态,不可点击。
205
+
206
+ 6. **已选商品弹窗**:点击底部购物车图标可展开已选商品弹窗,在弹窗中可以快速删除已选商品。
207
+
208
+ 7. **样式定制**:可以通过 `className` 和 `style` 属性自定义组件样式,如需深度定制样式,建议使用全局 CSS 覆盖。
@@ -0,0 +1,145 @@
1
+ # Signature
2
+
3
+ ## 简介
4
+ 电子签名组件 - 支持手写签名、查看已签名图片和重新签名,适用于签收、确认等需要电子签名的业务场景。组件会自动在查看模式和编辑模式之间切换。
5
+
6
+ ## API
7
+
8
+ ### Props
9
+
10
+ | 属性 | 说明 | 类型 | 默认值 | 必填 |
11
+ |------|------|------|--------|------|
12
+ | output | 输出类型,支持 base64 字符串或 blob 对象 | oneOf(['base64', 'blob']) | 'base64' | 否 |
13
+ | image | 已有的签名图片 URL,不传则直接进入编辑模式 | string | - | 否 |
14
+ | onSave | 保存签名的回调函数,接收签名数据(base64 字符串或 blob 对象) | function | - | 是 |
15
+ | disabledEdit | 是否禁止修改签名 | boolean | false | 否 |
16
+ | isEdit | 存在 image 时,是否直接进入编辑状态。Edit 场景按钮文案为'确定签收',View 场景按钮文案为'保存' | boolean | false | 否 |
17
+
18
+ ## 示例
19
+
20
+ ### 基础用法 - 查看已有签名
21
+ ```jsx
22
+ import { Signature } from '@gm-mobile/react'
23
+
24
+ function App() {
25
+ const handleSave = (data) => {
26
+ // data 是 base64 字符串或 blob 对象,取决于 output 属性
27
+ console.log('签名数据:', data)
28
+ }
29
+
30
+ return (
31
+ <Signature
32
+ image="https://image.document.guanmai.cn/product_img/1588907508246-058712363353500274.png"
33
+ onSave={handleSave}
34
+ />
35
+ )
36
+ }
37
+ ```
38
+
39
+ ### 直接进入签名模式
40
+ ```jsx
41
+ import { Signature } from '@gm-mobile/react'
42
+
43
+ function App() {
44
+ return (
45
+ <Signature
46
+ isEdit
47
+ output="blob"
48
+ onSave={(blob) => {
49
+ // 处理 blob 格式的签名数据
50
+ console.log('签名 Blob:', blob)
51
+ }}
52
+ />
53
+ )
54
+ }
55
+ ```
56
+
57
+ ### 首次签名(无初始图片)
58
+ ```jsx
59
+ import { Signature } from '@gm-mobile/react'
60
+
61
+ function App() {
62
+ return (
63
+ <Signature
64
+ onSave={(base64) => {
65
+ // 处理 base64 格式的签名数据
66
+ console.log('签名 Base64:', base64)
67
+ }}
68
+ />
69
+ )
70
+ }
71
+ ```
72
+
73
+ ### 禁止修改签名
74
+ ```jsx
75
+ import { Signature } from '@gm-mobile/react'
76
+
77
+ function App() {
78
+ return (
79
+ <Signature
80
+ image="https://example.com/signature.png"
81
+ disabledEdit
82
+ onSave={() => {
83
+ // disabledEdit 为 true 时,不会触发 onSave
84
+ }}
85
+ />
86
+ )
87
+ }
88
+ ```
89
+
90
+ ### 输出为 Blob 对象
91
+ ```jsx
92
+ import { Signature } from '@gm-mobile/react'
93
+
94
+ function App() {
95
+ const handleSave = async (blob) => {
96
+ // 将 blob 转换为文件或上传到服务器
97
+ const file = new File([blob], 'signature.png', { type: 'image/png' })
98
+
99
+ // 示例:使用 FormData 上传
100
+ const formData = new FormData()
101
+ formData.append('signature', file)
102
+
103
+ // await uploadSignature(formData)
104
+ console.log('文件对象:', file)
105
+ }
106
+
107
+ return (
108
+ <Signature
109
+ output="blob"
110
+ image="https://example.com/signature.png"
111
+ onSave={handleSave}
112
+ />
113
+ )
114
+ }
115
+ ```
116
+
117
+ ### 确认签收场景
118
+ ```jsx
119
+ import { Signature } from '@gm-mobile/react'
120
+
121
+ function App() {
122
+ return (
123
+ <Signature
124
+ isEdit
125
+ onSave={(data) => {
126
+ console.log('确认签收,签名数据:', data)
127
+ // 提交签收信息到后端
128
+ }}
129
+ />
130
+ )
131
+ }
132
+ ```
133
+
134
+ ## 注意事项
135
+ - 组件内部会自动管理查看模式和编辑模式的切换,无需手动控制
136
+ - 当 `image` 不传时,会直接进入编辑模式
137
+ - 当 `image` 存在时,默认先显示查看模式,点击"修改签名"按钮进入编辑模式
138
+ - 当 `isEdit=true` 且 `image` 存在时,会直接进入编辑模式,按钮文案为"确定签收"
139
+ - 当 `isEdit=false` 且 `image` 存在时,从查看模式进入编辑模式,按钮文案为"保存"
140
+ - `disabledEdit=true` 时,查看模式下不显示"修改签名"按钮,签名不可修改
141
+ - 编辑模式下,Canvas 会自适应屏幕宽度,高度为屏幕高度减去底部按钮区域高度
142
+ - 在编辑模式下,组件会阻止 iOS 的橡皮筋滚动效果,保证签名体验
143
+ - `output="base64"` 时,`onSave` 接收 base64 字符串(以 `data:image/png;base64,` 开头)
144
+ - `output="blob"` 时,`onSave` 接收 Blob 对象(type 为 'image/png'),适合文件上传场景
145
+ - 确保在 `onSave` 回调中正确处理签名数据(保存到状态、上传服务器等)
@@ -0,0 +1,160 @@
1
+ # TabDateSelect
2
+
3
+ ## 简介
4
+ 标签日期选择器 - 一个带有标签切换功能的日期范围选择组件,支持在不同标签下选择不同的日期范围,常用于按下单日期、按收货日期等场景。
5
+
6
+ ## API
7
+
8
+ ### Props
9
+ | 属性 | 说明 | 类型 | 默认值 | 必填 |
10
+ |------|------|------|--------|------|
11
+ | tabs | 切换 tabs 配置数组,每项包含 text(显示文本)、value(唯一标识)、min(最小可选日期)、max(最大可选日期) | array | - | 是 |
12
+ | selectedTab | 选中的 tab value 值 | any | - | 否 |
13
+ | begin | 开始日期 | object(Date) | - | 否 |
14
+ | end | 结束日期 | object(Date) | - | 否 |
15
+ | onSelect | 确定按钮点击回调,接收参数 { selectedTab, begin, end, serviceTimeId } | function | - | 是 |
16
+ | serviceTimeList | 运营周期列表,用于动态调整可选日期范围,每项包含 value 和 text | array | - | 否 |
17
+
18
+ ### 静态方法
19
+ | 方法名 | 说明 | 参数 | 返回值 |
20
+ |--------|------|------|--------|
21
+ | TabDateSelect.render | 以弹窗形式渲染日期选择器 | { title, tabs, selectedTab, begin, end, serviceTimeList } | Promise,成功时返回 { selectedTab, begin, end, serviceTimeId } |
22
+ | TabDateSelect.hide | 隐藏当前显示的日期选择器弹窗 | 无 | 无 |
23
+
24
+ ## 示例
25
+
26
+ ### 基础用法
27
+ ```jsx
28
+ import { TabDateSelect } from '@gm-mobile/react'
29
+ import moment from 'moment'
30
+
31
+ const tabs = [
32
+ {
33
+ text: '按下单日期',
34
+ value: 1,
35
+ min: moment().subtract(365, 'day').toDate(),
36
+ max: moment().toDate(),
37
+ },
38
+ {
39
+ text: '按收货日期',
40
+ value: 2,
41
+ min: moment().subtract(365, 'day').toDate(),
42
+ max: moment().add(30, 'days').toDate(),
43
+ },
44
+ ]
45
+
46
+ TabDateSelect.render({
47
+ title: '选择日期',
48
+ tabs,
49
+ selectedTab: 1,
50
+ begin: moment().add(-5, 'days').toDate(),
51
+ end: moment().toDate(),
52
+ }).then(({ selectedTab, begin, end }) => {
53
+ console.log('选中标签:', selectedTab)
54
+ console.log('开始日期:', begin)
55
+ console.log('结束日期:', end)
56
+ })
57
+ ```
58
+
59
+ ### 带运营时间的用法
60
+ ```jsx
61
+ import { TabDateSelect } from '@gm-mobile/react'
62
+ import moment from 'moment'
63
+
64
+ const tabs = [
65
+ {
66
+ text: '按下单日期',
67
+ value: 1,
68
+ min: moment().subtract(365, 'day').toDate(),
69
+ max: moment().toDate(),
70
+ selectedServiceTime: {
71
+ text: '全天',
72
+ value: 'all_day',
73
+ },
74
+ },
75
+ {
76
+ text: '按收货日期',
77
+ value: 2,
78
+ min: moment().subtract(365, 'day').toDate(),
79
+ max: moment().add(30, 'days').toDate(),
80
+ selectedServiceTime: {
81
+ text: '上午',
82
+ value: 'morning',
83
+ },
84
+ },
85
+ ]
86
+
87
+ const serviceTimeList = [
88
+ { text: '全天', value: 'all_day' },
89
+ { text: '上午', value: 'morning' },
90
+ { text: '下午', value: 'afternoon' },
91
+ ]
92
+
93
+ TabDateSelect.render({
94
+ title: '选择日期',
95
+ tabs,
96
+ selectedTab: 2,
97
+ serviceTimeList,
98
+ begin: moment().toDate(),
99
+ end: moment().add(7, 'days').toDate(),
100
+ }).then(({ selectedTab, begin, end, serviceTimeId }) => {
101
+ console.log('选中标签:', selectedTab)
102
+ console.log('开始日期:', begin)
103
+ console.log('结束日期:', end)
104
+ console.log('运营时间ID:', serviceTimeId)
105
+ })
106
+ ```
107
+
108
+ ### 直接嵌入页面使用
109
+ ```jsx
110
+ import { TabDateSelect } from '@gm-mobile/react'
111
+ import moment from 'moment'
112
+
113
+ function MyComponent() {
114
+ const [result, setResult] = useState(null)
115
+
116
+ const tabs = [
117
+ {
118
+ text: '按下单日期',
119
+ value: 1,
120
+ min: moment().subtract(365, 'day').toDate(),
121
+ max: moment().toDate(),
122
+ },
123
+ {
124
+ text: '按收货日期',
125
+ value: 2,
126
+ min: moment().subtract(365, 'day').toDate(),
127
+ max: moment().add(30, 'days').toDate(),
128
+ },
129
+ ]
130
+
131
+ return (
132
+ <div>
133
+ <TabDateSelect
134
+ tabs={tabs}
135
+ selectedTab={1}
136
+ begin={moment().add(-5, 'days').toDate()}
137
+ end={moment().toDate()}
138
+ onSelect={({ selectedTab, begin, end }) => {
139
+ setResult({ selectedTab, begin, end })
140
+ }}
141
+ />
142
+ {result && (
143
+ <div>
144
+ <p>选中标签: {result.selectedTab}</p>
145
+ <p>日期范围: {moment(result.begin).format('YYYY-MM-DD')} ~ {moment(result.end).format('YYYY-MM-DD')}</p>
146
+ </div>
147
+ )}
148
+ </div>
149
+ )
150
+ }
151
+ ```
152
+
153
+ ## 注意事项
154
+ - tabs 配置中的 min 和 max 必须是 Date 对象,建议使用 moment.js 创建
155
+ - 当切换 tab 时,日期会自动重置为当前 tab 的 max 日期
156
+ - 如果提供了 serviceTimeList,组件会在 tab 标签下方显示运营时间选择器,切换运营时间时会根据服务时间限制自动调整可选日期范围
157
+ - TabDateSelect.render 返回的是 Promise,成功时 resolve,点击取消或遮罩时 reject
158
+ - begin 和 end 如果不提供,默认会使用当前选中 tab 的 max 日期
159
+ - 组件内部使用了 RangeCalendar 组件,支持单选和范围选择
160
+ - onSelect 回调会在点击确定按钮时触发,返回的 begin 和 end 都是 Date 对象