@yorkjs/hive-ui 0.0.1 → 0.0.3
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/package.json +5 -1
- package/src/components/Cell/Cell.tsx +153 -0
- package/src/components/Cell/CellArrow.tsx +25 -0
- package/src/components/Cell/CellCard.tsx +44 -0
- package/src/components/Cell/CellDescription.tsx +28 -0
- package/src/components/Cell/CellGroup.tsx +46 -0
- package/src/components/Cell/CellTitle.tsx +71 -0
- package/src/components/Cell/CellValue.tsx +50 -0
- package/src/components/Cell/config.ts +13 -0
- package/src/components/Cell/index.module.styl +145 -0
- package/src/components/Cell/index.tsx +24 -0
- package/src/components/Checkbox/index.module.styl +68 -0
- package/src/components/Checkbox/index.tsx +118 -0
- package/src/components/Icon/index.tsx +51 -0
- package/src/components/NumberKeyboard/index.module.styl +20 -7
- package/src/components/NumberKeyboard/index.tsx +30 -8
- package/src/components/Tag/index.module.styl +29 -0
- package/src/components/Tag/index.tsx +117 -0
- package/src/constant/mall.ts +22 -0
- package/src/index.ts +6 -1
- package/src/item/ProductCard/index.module.styl +251 -0
- package/src/item/ProductCard/index.tsx +300 -0
- package/src/util/env.ts +17 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
@import '../../styles/mixin.styl'
|
|
2
|
+
|
|
3
|
+
// 颜色与字号变量对照表 (基于 RN getStyles)
|
|
4
|
+
$F_TITLE = 15PX // RN 15
|
|
5
|
+
$F_PRICE_VAL = 14PX // RN 14
|
|
6
|
+
$F_PRICE_SYM = 10PX // RN 10
|
|
7
|
+
$F_INFO = 12PX // RN 12 (库存/销量/条码)
|
|
8
|
+
$F_TAG_TYPE = 11PX // RN 11 (商品类型实物/虚拟/服务)
|
|
9
|
+
$F_TAG_NORMAL = 10PX // RN 10 (活动标签/配送方式)
|
|
10
|
+
$F_SPEC = 11PX // RN 11
|
|
11
|
+
$F_CUSTOM = 10PX // RN 10 (做法描述)
|
|
12
|
+
$F_BUY_COUNT = 14PX // RN 14
|
|
13
|
+
|
|
14
|
+
.item-container
|
|
15
|
+
position relative
|
|
16
|
+
display flex
|
|
17
|
+
flex-direction column
|
|
18
|
+
padding 15PX 10PX // 对齐 RN: paddingVertical: 15, paddingHorizontal: 10
|
|
19
|
+
background-color var(--containerCard)
|
|
20
|
+
box-sizing border-box
|
|
21
|
+
|
|
22
|
+
.card-main-body
|
|
23
|
+
display flex
|
|
24
|
+
flex-direction row
|
|
25
|
+
align-items flex-start
|
|
26
|
+
width 100%
|
|
27
|
+
|
|
28
|
+
.checkbox-container
|
|
29
|
+
height 72PX // 对齐图片高度 72
|
|
30
|
+
margin-right 10PX
|
|
31
|
+
display flex
|
|
32
|
+
justify-content center
|
|
33
|
+
align-items center
|
|
34
|
+
flex-shrink 0
|
|
35
|
+
|
|
36
|
+
.image-container
|
|
37
|
+
position relative
|
|
38
|
+
margin-right 10PX
|
|
39
|
+
flex-shrink 0
|
|
40
|
+
width 72PX
|
|
41
|
+
height 72PX
|
|
42
|
+
|
|
43
|
+
.product-image
|
|
44
|
+
width 72PX
|
|
45
|
+
height 72PX
|
|
46
|
+
border-radius 10PX // 对齐 RN: IMAGE_RADIUS = 10
|
|
47
|
+
background-color var(--containerInner)
|
|
48
|
+
|
|
49
|
+
.status-overlay
|
|
50
|
+
position absolute
|
|
51
|
+
top 0
|
|
52
|
+
left 0
|
|
53
|
+
bottom 0
|
|
54
|
+
right 0
|
|
55
|
+
border-radius 10PX
|
|
56
|
+
background-color rgba(0, 0, 0, 0.3)
|
|
57
|
+
display flex
|
|
58
|
+
justify-content center
|
|
59
|
+
align-items center
|
|
60
|
+
z-index 2
|
|
61
|
+
|
|
62
|
+
.status-text
|
|
63
|
+
font-size 12PX // 对齐 RN: 12
|
|
64
|
+
color #FFF
|
|
65
|
+
line-height 1
|
|
66
|
+
|
|
67
|
+
.content-container
|
|
68
|
+
flex 1
|
|
69
|
+
display flex
|
|
70
|
+
flex-direction column
|
|
71
|
+
justify-content space-between
|
|
72
|
+
min-height 72PX
|
|
73
|
+
min-width 0 // 关键:确保内部 Flex 截断能正常工作
|
|
74
|
+
|
|
75
|
+
.content-body
|
|
76
|
+
flex 1
|
|
77
|
+
display flex
|
|
78
|
+
flex-direction column
|
|
79
|
+
|
|
80
|
+
.title-row
|
|
81
|
+
display flex
|
|
82
|
+
flex-direction row
|
|
83
|
+
align-items center
|
|
84
|
+
margin-bottom 2PX
|
|
85
|
+
|
|
86
|
+
.product-tag-container
|
|
87
|
+
display flex
|
|
88
|
+
margin-right 8PX
|
|
89
|
+
flex-shrink 0
|
|
90
|
+
line-height 1
|
|
91
|
+
|
|
92
|
+
.product-tag
|
|
93
|
+
height 15PX
|
|
94
|
+
line-height 15PX
|
|
95
|
+
padding 1PX 3PX
|
|
96
|
+
border-radius 3PX
|
|
97
|
+
font-size 9PX
|
|
98
|
+
|
|
99
|
+
.title
|
|
100
|
+
flex 1
|
|
101
|
+
font-size $F_TITLE
|
|
102
|
+
color var(--textTitle)
|
|
103
|
+
line-height 1.4
|
|
104
|
+
font-weight bold
|
|
105
|
+
lineClamp(1)
|
|
106
|
+
|
|
107
|
+
.second-row
|
|
108
|
+
margin-top 2PX
|
|
109
|
+
width 100%
|
|
110
|
+
white-space nowrap
|
|
111
|
+
.second-row-inner
|
|
112
|
+
display flex
|
|
113
|
+
flex-direction row
|
|
114
|
+
align-items center
|
|
115
|
+
|
|
116
|
+
.info-line
|
|
117
|
+
width 1PX
|
|
118
|
+
height 10PX
|
|
119
|
+
background-color var(--textContent)
|
|
120
|
+
margin 0 5PX
|
|
121
|
+
transform scaleX(0.5)
|
|
122
|
+
|
|
123
|
+
.info-text
|
|
124
|
+
font-size $F_INFO
|
|
125
|
+
color var(--textTitle)
|
|
126
|
+
|
|
127
|
+
.primary-text
|
|
128
|
+
font-size $F_INFO
|
|
129
|
+
color var(--primary)
|
|
130
|
+
|
|
131
|
+
.stock-and-sale
|
|
132
|
+
display flex
|
|
133
|
+
flex-direction row
|
|
134
|
+
align-items center
|
|
135
|
+
.stock-box
|
|
136
|
+
display flex
|
|
137
|
+
flex-direction row
|
|
138
|
+
margin-right 5PX
|
|
139
|
+
.stock-text
|
|
140
|
+
font-size $F_INFO
|
|
141
|
+
color var(--primary)
|
|
142
|
+
margin-left 5PX
|
|
143
|
+
|
|
144
|
+
.tag-row
|
|
145
|
+
display flex
|
|
146
|
+
flex-direction row
|
|
147
|
+
align-items center
|
|
148
|
+
flex-wrap wrap
|
|
149
|
+
gap 5PX
|
|
150
|
+
margin-top 5PX
|
|
151
|
+
|
|
152
|
+
.delivery-tag
|
|
153
|
+
padding 1PX 5PX
|
|
154
|
+
border-radius 3PX
|
|
155
|
+
font-size $F_TAG_NORMAL
|
|
156
|
+
border 1PX solid transparent
|
|
157
|
+
|
|
158
|
+
.activity-tag
|
|
159
|
+
display flex
|
|
160
|
+
flex-direction row
|
|
161
|
+
align-items center
|
|
162
|
+
border 1PX solid #F21902 // 对齐 RN: borderColor: '#F21902'
|
|
163
|
+
border-radius 3PX
|
|
164
|
+
overflow hidden
|
|
165
|
+
|
|
166
|
+
.activity-tag-badge
|
|
167
|
+
background-color #F21902
|
|
168
|
+
padding 1PX 4PX // 对齐 RN: paddingHorizontal: 4, paddingVertical: 1
|
|
169
|
+
display flex
|
|
170
|
+
align-items center
|
|
171
|
+
justify-content center
|
|
172
|
+
|
|
173
|
+
.activity-tag-badge-text
|
|
174
|
+
font-size $F_TAG_NORMAL
|
|
175
|
+
color #FFF
|
|
176
|
+
line-height 1
|
|
177
|
+
|
|
178
|
+
.activity-tag-content
|
|
179
|
+
padding 1PX 5PX // 对齐 RN: paddingHorizontal: 5, paddingVertical: 1
|
|
180
|
+
|
|
181
|
+
.activity-tag-text
|
|
182
|
+
font-size $F_TAG_NORMAL
|
|
183
|
+
color #F21902
|
|
184
|
+
line-height 1
|
|
185
|
+
|
|
186
|
+
.spec-text
|
|
187
|
+
font-size $F_SPEC
|
|
188
|
+
color var(--textContent)
|
|
189
|
+
line-height 15PX // 对齐 RN: lineHeight: 15
|
|
190
|
+
margin-top 5PX // 对齐 RN: marginTop: 5
|
|
191
|
+
lineClamp(1)
|
|
192
|
+
|
|
193
|
+
.customization-text
|
|
194
|
+
font-size $F_CUSTOM
|
|
195
|
+
color var(--textMuted)
|
|
196
|
+
line-height 15PX // 对齐 RN: lineHeight: 15
|
|
197
|
+
margin-top 5PX // 对齐 RN: marginTop: 5
|
|
198
|
+
lineClamp(3)
|
|
199
|
+
|
|
200
|
+
.content-bottom
|
|
201
|
+
display flex
|
|
202
|
+
flex-direction row
|
|
203
|
+
align-items center
|
|
204
|
+
justify-content space-between
|
|
205
|
+
margin-top 10PX // 对齐 RN: marginTop: 10
|
|
206
|
+
|
|
207
|
+
.price-container
|
|
208
|
+
display flex
|
|
209
|
+
flex-direction row
|
|
210
|
+
align-items center
|
|
211
|
+
|
|
212
|
+
.price-row
|
|
213
|
+
display flex
|
|
214
|
+
flex-direction row
|
|
215
|
+
align-items baseline // 确保符号和数值底部对齐
|
|
216
|
+
|
|
217
|
+
.price-symbol
|
|
218
|
+
font-size $F_PRICE_SYM
|
|
219
|
+
color var(--textMoney)
|
|
220
|
+
font-weight bold // 对齐 RN: FONT_WEIGHT_BOLD
|
|
221
|
+
margin-right 1PX
|
|
222
|
+
|
|
223
|
+
.price-value
|
|
224
|
+
font-size $F_PRICE_VAL
|
|
225
|
+
color var(--textMoney)
|
|
226
|
+
font-weight bold // 对齐 RN: FONT_WEIGHT_BOLD
|
|
227
|
+
|
|
228
|
+
.original-price
|
|
229
|
+
font-size 11PX // 对齐 RN: 11
|
|
230
|
+
color var(--textMuted)
|
|
231
|
+
text-decoration line-through
|
|
232
|
+
margin-left 5PX // 对齐 RN: marginLeft: 5
|
|
233
|
+
|
|
234
|
+
.buy-count-value
|
|
235
|
+
font-size $F_BUY_COUNT
|
|
236
|
+
color var(--textContent)
|
|
237
|
+
margin-left 10PX // 对齐 RN: marginLeft: 10
|
|
238
|
+
|
|
239
|
+
.delete-container
|
|
240
|
+
position absolute
|
|
241
|
+
top 16PX // 对齐 RN: top: 16
|
|
242
|
+
right 11PX // 对齐 RN: right: 11
|
|
243
|
+
padding 5PX // 对齐 RN: pressHitSlop
|
|
244
|
+
z-index 10
|
|
245
|
+
|
|
246
|
+
.delete-icon
|
|
247
|
+
color var(--textContent)
|
|
248
|
+
|
|
249
|
+
.card-footer
|
|
250
|
+
width 100%
|
|
251
|
+
margin-top 12PX
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { View, Text, ScrollView, Image, ITouchEvent } from '@tarojs/components'
|
|
3
|
+
|
|
4
|
+
import { formatClassNames, isEmpty } from '../../util/function'
|
|
5
|
+
|
|
6
|
+
import Tag from '../../components/Tag'
|
|
7
|
+
import Icon from '../../components/Icon'
|
|
8
|
+
import Checkbox from '../../components/Checkbox'
|
|
9
|
+
|
|
10
|
+
import styles from './index.module.styl'
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
MALL_PRODUCT_TYPE_REAL,
|
|
14
|
+
MALL_PRODUCT_TYPE_VIRTUAL,
|
|
15
|
+
MALL_PRODUCT_TYPE_SERVICE,
|
|
16
|
+
MALL_PRODUCT_DELIVERY_TYPE_EXPRESS,
|
|
17
|
+
MALL_PRODUCT_DELIVERY_TYPE_SELF,
|
|
18
|
+
MALL_PRODUCT_DELIVERY_TYPE_SHOP,
|
|
19
|
+
MALL_PRODUCT_DELIVERY_TYPE_DINE,
|
|
20
|
+
PROMOTION_ACTIVITY_TYPE_FLASH,
|
|
21
|
+
PROMOTION_ACTIVITY_TYPE_BRAND,
|
|
22
|
+
PROMOTION_ACTIVITY_TYPE_HOT,
|
|
23
|
+
PROMOTION_ACTIVITY_TYPE_PRESENT,
|
|
24
|
+
} from '../../constant/mall'
|
|
25
|
+
|
|
26
|
+
export interface IProductCardLabels {
|
|
27
|
+
product_type?: { real: string; virtual: string; service: string }
|
|
28
|
+
delivery_type?: { dine: string; self_pickup: string; delivery: string }
|
|
29
|
+
activity_type?: { flash: string; brand: string; hot: string; present: string }
|
|
30
|
+
present_badge?: string
|
|
31
|
+
stock_label?: string
|
|
32
|
+
sale_label?: string
|
|
33
|
+
multiple_spec?: string
|
|
34
|
+
barcode_label?: string
|
|
35
|
+
spec_label?: string
|
|
36
|
+
sold_out?: string
|
|
37
|
+
offline?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 商品卡片组件属性 */
|
|
41
|
+
export interface IProductCardProps {
|
|
42
|
+
/** 外部样式类 */
|
|
43
|
+
className?: string
|
|
44
|
+
/** 商品图片对象 */
|
|
45
|
+
image: { url: string }
|
|
46
|
+
/** 是否显示售罄标记 */
|
|
47
|
+
showSoldOut?: boolean
|
|
48
|
+
/** 是否显示已下架标记 */
|
|
49
|
+
showOffline?: boolean
|
|
50
|
+
/** 商品标题 */
|
|
51
|
+
title: string
|
|
52
|
+
/** 标题最大显示行数 (默认1) */
|
|
53
|
+
titleMaxLines?: number
|
|
54
|
+
/** 商品类型:实物/虚拟/服务 */
|
|
55
|
+
productType?: number
|
|
56
|
+
/** 是否显示“多规格”标签 */
|
|
57
|
+
showMultipleSpec?: boolean
|
|
58
|
+
/** 库存数量文案 */
|
|
59
|
+
stockCount?: string | number
|
|
60
|
+
/** 销量文案 */
|
|
61
|
+
saleCount?: string | number
|
|
62
|
+
/** 商品条码 */
|
|
63
|
+
barcode?: string
|
|
64
|
+
/** 规格描述文案 (如: 红色, 大号) */
|
|
65
|
+
spec?: string
|
|
66
|
+
/** 规格描述最大显示行数 (默认1) */
|
|
67
|
+
specMaxLines?: number
|
|
68
|
+
/** 活动类型:秒杀/品牌/热点/满赠 */
|
|
69
|
+
activityType?: number
|
|
70
|
+
/** 配送方式集合:堂食/自提/外送 */
|
|
71
|
+
deliveryTypes?: number[]
|
|
72
|
+
/** 做法/定制化信息描述 */
|
|
73
|
+
customization?: string
|
|
74
|
+
/** 做法描述最大显示行数 (默认3) */
|
|
75
|
+
customizationMaxLines?: number
|
|
76
|
+
/** 售价 */
|
|
77
|
+
salePrice: string | number
|
|
78
|
+
/** 原价 (划线价) */
|
|
79
|
+
originalPrice?: string | number
|
|
80
|
+
/** 已购数量 (显示为 xN) */
|
|
81
|
+
buyCount?: number
|
|
82
|
+
/** 是否显示左侧勾选框 */
|
|
83
|
+
showCheckbox?: boolean
|
|
84
|
+
/** 勾选框是否选中 */
|
|
85
|
+
checkboxChecked?: boolean
|
|
86
|
+
/** 勾选框是否禁用 */
|
|
87
|
+
checkboxDisabled?: boolean
|
|
88
|
+
/** 点击勾选框或卡片触发 (当 checkbox 可用时) */
|
|
89
|
+
onCheckboxClick?: () => void
|
|
90
|
+
/** 是否显示右上角删除按钮 */
|
|
91
|
+
showDelete?: boolean
|
|
92
|
+
/** 点击删除按钮回调 */
|
|
93
|
+
onDelete?: () => void
|
|
94
|
+
/** 右下角自定义操作区域 (与 buyCount 互斥) */
|
|
95
|
+
action?: React.ReactNode
|
|
96
|
+
/** 国际化标签注入 */
|
|
97
|
+
labels?: IProductCardLabels
|
|
98
|
+
// 卡片点击事件
|
|
99
|
+
onClick?: () => void
|
|
100
|
+
children?: React.ReactNode
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const DEFAULT_LABELS: IProductCardLabels = {
|
|
104
|
+
product_type: { real: '实物', virtual: '虚拟', service: '服务' },
|
|
105
|
+
delivery_type: { dine: '堂食', self_pickup: '到店自取', delivery: '送货上门' },
|
|
106
|
+
activity_type: { flash: '秒杀活动', brand: '品牌活动', hot: '热点活动', present: '买赠活动' },
|
|
107
|
+
present_badge: '赠',
|
|
108
|
+
stock_label: '库存',
|
|
109
|
+
sale_label: '销量',
|
|
110
|
+
multiple_spec: '多规格',
|
|
111
|
+
barcode_label: '条码',
|
|
112
|
+
spec_label: '规格',
|
|
113
|
+
sold_out: '售罄',
|
|
114
|
+
offline: '已下架'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const ProductCard: React.FC<IProductCardProps> = (props) => {
|
|
118
|
+
const {
|
|
119
|
+
image,
|
|
120
|
+
title,
|
|
121
|
+
titleMaxLines = 1,
|
|
122
|
+
productType,
|
|
123
|
+
showMultipleSpec,
|
|
124
|
+
stockCount,
|
|
125
|
+
saleCount,
|
|
126
|
+
barcode,
|
|
127
|
+
spec,
|
|
128
|
+
specMaxLines = 1,
|
|
129
|
+
activityType,
|
|
130
|
+
deliveryTypes = [],
|
|
131
|
+
customization,
|
|
132
|
+
customizationMaxLines = 3,
|
|
133
|
+
salePrice,
|
|
134
|
+
originalPrice,
|
|
135
|
+
buyCount,
|
|
136
|
+
showSoldOut = false,
|
|
137
|
+
showOffline = false,
|
|
138
|
+
showCheckbox = false,
|
|
139
|
+
checkboxChecked = false,
|
|
140
|
+
checkboxDisabled = false,
|
|
141
|
+
onCheckboxClick,
|
|
142
|
+
showDelete,
|
|
143
|
+
onDelete,
|
|
144
|
+
action,
|
|
145
|
+
className,
|
|
146
|
+
labels: customLabels,
|
|
147
|
+
onClick,
|
|
148
|
+
children
|
|
149
|
+
} = props
|
|
150
|
+
|
|
151
|
+
const handleCardClick = (e: ITouchEvent) => {
|
|
152
|
+
if (showCheckbox) {
|
|
153
|
+
if (!checkboxDisabled) {
|
|
154
|
+
onCheckboxClick?.()
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
onClick?.()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const labels = useMemo(() => ({
|
|
163
|
+
...DEFAULT_LABELS,
|
|
164
|
+
...customLabels,
|
|
165
|
+
product_type: { ...DEFAULT_LABELS.product_type, ...customLabels?.product_type },
|
|
166
|
+
delivery_type: { ...DEFAULT_LABELS.delivery_type, ...customLabels?.delivery_type },
|
|
167
|
+
activity_type: { ...DEFAULT_LABELS.activity_type, ...customLabels?.activity_type }
|
|
168
|
+
}), [customLabels])
|
|
169
|
+
|
|
170
|
+
const productTypeTag = useMemo(() => {
|
|
171
|
+
if (productType === undefined) return null
|
|
172
|
+
const colorMap: Record<number, { color: string; text: string | undefined }> = {
|
|
173
|
+
[MALL_PRODUCT_TYPE_REAL]: { color: 'var(--primary)', text: labels.product_type?.real },
|
|
174
|
+
[MALL_PRODUCT_TYPE_VIRTUAL]: { color: '#E4B700', text: labels.product_type?.virtual },
|
|
175
|
+
[MALL_PRODUCT_TYPE_SERVICE]: { color: '#03BE02', text: labels.product_type?.service },
|
|
176
|
+
}
|
|
177
|
+
const item = colorMap[productType]
|
|
178
|
+
if (!item) return null
|
|
179
|
+
return (
|
|
180
|
+
<View className={styles['product-tag-container']}>
|
|
181
|
+
<Tag
|
|
182
|
+
type='light-warning'
|
|
183
|
+
text={item.text}
|
|
184
|
+
className={styles['product-tag']}
|
|
185
|
+
style={{ backgroundColor: item.color, color: '#fff' }}
|
|
186
|
+
/>
|
|
187
|
+
</View>
|
|
188
|
+
)
|
|
189
|
+
}, [productType, labels])
|
|
190
|
+
|
|
191
|
+
const deliveryTags = useMemo(() => {
|
|
192
|
+
if (isEmpty(deliveryTypes)) return null
|
|
193
|
+
return deliveryTypes.map(type => {
|
|
194
|
+
let tagProps: any = null
|
|
195
|
+
if (type === MALL_PRODUCT_DELIVERY_TYPE_DINE) {
|
|
196
|
+
tagProps = { text: labels.delivery_type?.dine, color: '#2db7f5', bg: 'rgba(145, 213, 255, 0.16)' }
|
|
197
|
+
} else if (type === MALL_PRODUCT_DELIVERY_TYPE_SELF) {
|
|
198
|
+
tagProps = { text: labels.delivery_type?.self_pickup, color: '#FF3D15', bg: 'rgba(255,61,21,0.16)' }
|
|
199
|
+
} else if (type === MALL_PRODUCT_DELIVERY_TYPE_EXPRESS || type === MALL_PRODUCT_DELIVERY_TYPE_SHOP) {
|
|
200
|
+
tagProps = { text: labels.delivery_type?.delivery, color: '#FF8901', bg: 'rgba(255,137,1,0.16)' }
|
|
201
|
+
}
|
|
202
|
+
if (!tagProps) return null
|
|
203
|
+
return (
|
|
204
|
+
<Tag
|
|
205
|
+
key={type}
|
|
206
|
+
text={tagProps.text}
|
|
207
|
+
style={{ color: tagProps.color, backgroundColor: tagProps.bg, borderColor: tagProps.color }}
|
|
208
|
+
className={styles['delivery-tag']}
|
|
209
|
+
/>
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
}, [deliveryTypes, labels])
|
|
213
|
+
|
|
214
|
+
const stockAndSaleElem = useMemo(() => {
|
|
215
|
+
if (!stockCount && !saleCount) return null
|
|
216
|
+
return (
|
|
217
|
+
<View className={styles['stock-and-sale']}>
|
|
218
|
+
{stockCount && (
|
|
219
|
+
<View className={styles['stock-box']}>
|
|
220
|
+
<Text className={styles['info-text']}>{labels.stock_label}</Text>
|
|
221
|
+
<Text className={styles['stock-text']}>{stockCount}</Text>
|
|
222
|
+
</View>
|
|
223
|
+
)}
|
|
224
|
+
{saleCount && (
|
|
225
|
+
<Text className={styles['info-text']}>{labels.sale_label} {saleCount}</Text>
|
|
226
|
+
)}
|
|
227
|
+
</View>
|
|
228
|
+
)
|
|
229
|
+
}, [stockCount, saleCount, labels])
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<View className={formatClassNames(styles['item-container'], className)} onClick={handleCardClick}>
|
|
233
|
+
<View className={styles['card-main-body']}>
|
|
234
|
+
{showCheckbox && (
|
|
235
|
+
<View className={styles['checkbox-container']}>
|
|
236
|
+
<Checkbox checked={checkboxChecked} disabled={checkboxDisabled} readOnly />
|
|
237
|
+
</View>
|
|
238
|
+
)}
|
|
239
|
+
<View className={styles['image-container']}>
|
|
240
|
+
<Image src={image?.url} className={styles['product-image']} mode='aspectFill' />
|
|
241
|
+
{(showSoldOut || showOffline) && (
|
|
242
|
+
<View className={styles['status-overlay']}>
|
|
243
|
+
<Text className={styles['status-text']}>{showSoldOut ? labels.sold_out : labels.offline}</Text>
|
|
244
|
+
</View>
|
|
245
|
+
)}
|
|
246
|
+
</View>
|
|
247
|
+
<View className={styles['content-container']}>
|
|
248
|
+
<View className={styles['content-body']}>
|
|
249
|
+
<View className={styles['title-row']} style={{ marginRight: showDelete ? '30PX' : '0' }}>
|
|
250
|
+
{productTypeTag}
|
|
251
|
+
<Text className={styles['title']} style={{ WebkitLineClamp: titleMaxLines }}>{title}</Text>
|
|
252
|
+
</View>
|
|
253
|
+
{(showMultipleSpec || stockCount || saleCount || barcode) && (
|
|
254
|
+
<ScrollView scrollX className={styles['second-row']} showScrollbar={false}>
|
|
255
|
+
<View className={styles['second-row-inner']}>
|
|
256
|
+
{showMultipleSpec && <Text className={styles['primary-text']}>{labels.multiple_spec}</Text>}
|
|
257
|
+
{showMultipleSpec && stockAndSaleElem && <View className={styles['info-line']} />}
|
|
258
|
+
{stockAndSaleElem}
|
|
259
|
+
{((showMultipleSpec || stockAndSaleElem) && barcode) && <View className={styles['info-line']} />}
|
|
260
|
+
{barcode && <Text className={styles['info-text']}>{labels.barcode_label} {barcode}</Text>}
|
|
261
|
+
</View>
|
|
262
|
+
</ScrollView>
|
|
263
|
+
)}
|
|
264
|
+
{(activityType || deliveryTags) && (
|
|
265
|
+
<View className={styles['tag-row']}>
|
|
266
|
+
{activityType && labels.activity_type && (
|
|
267
|
+
<View className={styles['activity-tag']}>
|
|
268
|
+
{activityType === PROMOTION_ACTIVITY_TYPE_PRESENT && <View className={styles['activity-tag-badge']}><Text className={styles['activity-tag-badge-text']}>{labels.present_badge}</Text></View>}
|
|
269
|
+
<View className={styles['activity-tag-content']}>
|
|
270
|
+
<Text className={styles['activity-tag-text']}>
|
|
271
|
+
{activityType === PROMOTION_ACTIVITY_TYPE_FLASH && labels.activity_type.flash}
|
|
272
|
+
{activityType === PROMOTION_ACTIVITY_TYPE_BRAND && labels.activity_type.brand}
|
|
273
|
+
{activityType === PROMOTION_ACTIVITY_TYPE_HOT && labels.activity_type.hot}
|
|
274
|
+
{activityType === PROMOTION_ACTIVITY_TYPE_PRESENT && labels.activity_type.present}
|
|
275
|
+
</Text>
|
|
276
|
+
</View>
|
|
277
|
+
</View>
|
|
278
|
+
)}
|
|
279
|
+
{deliveryTags}
|
|
280
|
+
</View>
|
|
281
|
+
)}
|
|
282
|
+
{spec && <Text className={styles['spec-text']} style={{ WebkitLineClamp: specMaxLines }}>{labels.spec_label}:{spec}</Text>}
|
|
283
|
+
{customization && <Text className={styles['customization-text']} style={{ WebkitLineClamp: customizationMaxLines }}>{customization}</Text>}
|
|
284
|
+
</View>
|
|
285
|
+
<View className={styles['content-bottom']}>
|
|
286
|
+
<View className={styles['price-container']}>
|
|
287
|
+
<View className={styles['price-row']}><Text className={styles['price-symbol']}>¥</Text><Text className={styles['price-value']}>{salePrice}</Text></View>
|
|
288
|
+
{originalPrice && <Text className={styles['original-price']}>¥{originalPrice}</Text>}
|
|
289
|
+
</View>
|
|
290
|
+
<View className={styles['action-area']}>{buyCount ? <Text className={styles['buy-count-value']}>x{buyCount}</Text> : action}</View>
|
|
291
|
+
</View>
|
|
292
|
+
</View>
|
|
293
|
+
</View>
|
|
294
|
+
{children && <View className={styles['card-footer']} onClick={e => e.stopPropagation()}>{children}</View>}
|
|
295
|
+
{showDelete && <View className={styles['delete-container']} onClick={(e: ITouchEvent) => { e.stopPropagation(); onDelete?.(); }}><Icon name='trash' size={13} className={styles['delete-icon']} /></View>}
|
|
296
|
+
</View>
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export default React.memo(ProductCard)
|
package/src/util/env.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Taro from "@tarojs/taro"
|
|
2
|
+
|
|
3
|
+
// 支付宝小程序
|
|
4
|
+
export const isAlipayApp = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY
|
|
5
|
+
// 微信小程序
|
|
6
|
+
export const isWeApp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
|
|
7
|
+
// web
|
|
8
|
+
export const isWeb = Taro.getEnv() === Taro.ENV_TYPE.WEB
|
|
9
|
+
|
|
10
|
+
const userAgent = navigator && navigator.userAgent
|
|
11
|
+
export const isIos = /iphone|ipad/i.test(userAgent)
|
|
12
|
+
export const isAndroid = /android/i.test(userAgent)
|
|
13
|
+
|
|
14
|
+
const promoterAppMatch = userAgent.match(/promoter_app\/([\d.]+)/i)
|
|
15
|
+
|
|
16
|
+
export const isPromoterApp = promoterAppMatch && (isIos || isAndroid)
|
|
17
|
+
export const isApp = isPromoterApp
|