@yorkjs/hive-ui 0.1.4 → 0.1.6
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 +1 -1
- package/src/components/Alert/index.module.styl +105 -0
- package/src/components/Alert/index.tsx +268 -0
- package/src/components/Card/index.module.styl +15 -0
- package/src/components/Card/index.tsx +44 -0
- package/src/components/Cell/index.module.styl +1 -0
- package/src/components/DisplayCard/index.module.styl +4 -4
- package/src/components/RemoteImage/index.tsx +60 -0
- package/src/index.ts +3 -0
- package/src/util/env.ts +2 -0
- package/src/util/function.ts +97 -0
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
@import '../../styles/mixin.styl'
|
|
2
|
+
@import '../../styles/variable.styl'
|
|
3
|
+
|
|
4
|
+
.alert-container
|
|
5
|
+
padding 6PX $WING_BLANK
|
|
6
|
+
position relative
|
|
7
|
+
box-sizing border-box
|
|
8
|
+
width 100%
|
|
9
|
+
display flex
|
|
10
|
+
flex-direction column
|
|
11
|
+
overflow hidden
|
|
12
|
+
|
|
13
|
+
&.is-pressable
|
|
14
|
+
&:active
|
|
15
|
+
opacity 0.7
|
|
16
|
+
|
|
17
|
+
&.show-animation
|
|
18
|
+
animation alertFadeIn 0.3s ease-out forwards
|
|
19
|
+
|
|
20
|
+
&.is-leaving
|
|
21
|
+
animation alertFadeOut 0.25s ease-in forwards
|
|
22
|
+
|
|
23
|
+
.alert-content
|
|
24
|
+
display flex
|
|
25
|
+
flex-direction row
|
|
26
|
+
|
|
27
|
+
.icon-wrapper
|
|
28
|
+
margin-right 8PX
|
|
29
|
+
flex-shrink 0
|
|
30
|
+
display flex
|
|
31
|
+
align-items center
|
|
32
|
+
justify-content center
|
|
33
|
+
|
|
34
|
+
.text-container
|
|
35
|
+
flex 1
|
|
36
|
+
display flex
|
|
37
|
+
flex-direction column
|
|
38
|
+
overflow hidden
|
|
39
|
+
|
|
40
|
+
.message-text
|
|
41
|
+
getPlatformFontSize(13)
|
|
42
|
+
line-height 20PX
|
|
43
|
+
word-break break-word
|
|
44
|
+
white-space pre-line
|
|
45
|
+
|
|
46
|
+
.scroll-text
|
|
47
|
+
getPlatformFontSize(13)
|
|
48
|
+
line-height 20PX
|
|
49
|
+
white-space nowrap
|
|
50
|
+
padding-right 60PX
|
|
51
|
+
|
|
52
|
+
.scroll-wrapper
|
|
53
|
+
overflow hidden
|
|
54
|
+
width 100%
|
|
55
|
+
|
|
56
|
+
.scroll-track
|
|
57
|
+
display flex
|
|
58
|
+
width max-content
|
|
59
|
+
align-items center
|
|
60
|
+
animation alertScroll 18s linear infinite
|
|
61
|
+
will-change transform
|
|
62
|
+
|
|
63
|
+
.description-wrapper
|
|
64
|
+
margin-top 4PX
|
|
65
|
+
|
|
66
|
+
.description-text
|
|
67
|
+
getPlatformFontSize(12)
|
|
68
|
+
line-height 18PX
|
|
69
|
+
|
|
70
|
+
.action-container
|
|
71
|
+
margin-left 8PX
|
|
72
|
+
display flex
|
|
73
|
+
flex-direction row
|
|
74
|
+
align-items center
|
|
75
|
+
|
|
76
|
+
.close-btn-wrapper
|
|
77
|
+
display flex
|
|
78
|
+
align-items center
|
|
79
|
+
justify-content center
|
|
80
|
+
|
|
81
|
+
.close-text
|
|
82
|
+
getPlatformFontSize(12)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@keyframes alertFadeIn
|
|
86
|
+
0%
|
|
87
|
+
opacity 0
|
|
88
|
+
transform translateY(-20PX)
|
|
89
|
+
100%
|
|
90
|
+
opacity 1
|
|
91
|
+
transform translateY(0)
|
|
92
|
+
|
|
93
|
+
@keyframes alertFadeOut
|
|
94
|
+
0%
|
|
95
|
+
opacity 1
|
|
96
|
+
transform translateY(0)
|
|
97
|
+
100%
|
|
98
|
+
opacity 0
|
|
99
|
+
transform translateY(-20PX)
|
|
100
|
+
|
|
101
|
+
@keyframes alertScroll
|
|
102
|
+
0%
|
|
103
|
+
transform translate3d(0,0,0)
|
|
104
|
+
100%
|
|
105
|
+
transform translate3d(-50%,0,0)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import React, { useState, useMemo, useEffect, useCallback, memo } from 'react'
|
|
2
|
+
import { View, Text } from '@tarojs/components'
|
|
3
|
+
|
|
4
|
+
import { useTheme } from '../../config'
|
|
5
|
+
import { formatClassNames } from '../../util/function'
|
|
6
|
+
|
|
7
|
+
import Icon from '../Icon'
|
|
8
|
+
|
|
9
|
+
import styles from './index.module.styl'
|
|
10
|
+
|
|
11
|
+
export type AlertType = 'success' | 'error' | 'primary' | 'warning'
|
|
12
|
+
|
|
13
|
+
export interface AlertProps {
|
|
14
|
+
message: React.ReactNode
|
|
15
|
+
description?: React.ReactNode
|
|
16
|
+
type?: AlertType
|
|
17
|
+
showIcon?: boolean
|
|
18
|
+
icon?: React.ReactNode
|
|
19
|
+
iconName?: string
|
|
20
|
+
closable?: boolean
|
|
21
|
+
closeText?: React.ReactNode
|
|
22
|
+
onClose?: () => void
|
|
23
|
+
showAnimation?: boolean
|
|
24
|
+
duration?: number
|
|
25
|
+
onChange?: () => void
|
|
26
|
+
showArrow?: boolean
|
|
27
|
+
borderRadius?: number
|
|
28
|
+
className?: string
|
|
29
|
+
messageClassName?: string
|
|
30
|
+
iconClassName?: string
|
|
31
|
+
|
|
32
|
+
scroll?: boolean
|
|
33
|
+
scrollSpeed?: number
|
|
34
|
+
|
|
35
|
+
clickToClose?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const Alert: React.FC<AlertProps> = ({
|
|
39
|
+
message,
|
|
40
|
+
description,
|
|
41
|
+
type = 'primary',
|
|
42
|
+
showIcon = true,
|
|
43
|
+
icon,
|
|
44
|
+
iconName,
|
|
45
|
+
closable = false,
|
|
46
|
+
closeText,
|
|
47
|
+
onClose,
|
|
48
|
+
showAnimation = false,
|
|
49
|
+
duration = 0,
|
|
50
|
+
onChange,
|
|
51
|
+
showArrow = false,
|
|
52
|
+
borderRadius = 0,
|
|
53
|
+
className,
|
|
54
|
+
messageClassName,
|
|
55
|
+
iconClassName,
|
|
56
|
+
scroll = false,
|
|
57
|
+
scrollSpeed = 17,
|
|
58
|
+
clickToClose = false
|
|
59
|
+
}) => {
|
|
60
|
+
|
|
61
|
+
const { themeSelect } = useTheme()
|
|
62
|
+
|
|
63
|
+
const [visible, setVisible] = useState(true)
|
|
64
|
+
const [isLeaving, setIsLeaving] = useState(false)
|
|
65
|
+
|
|
66
|
+
const config = useMemo(() => {
|
|
67
|
+
|
|
68
|
+
const mapping = {
|
|
69
|
+
warning: {
|
|
70
|
+
icon: 'exclamation-circle-fill',
|
|
71
|
+
color: '#FA8C16',
|
|
72
|
+
bg: themeSelect('#FFF7E6', 'rgba(255,153,0,0.25)')
|
|
73
|
+
},
|
|
74
|
+
success: {
|
|
75
|
+
icon: 'check-circle-fill',
|
|
76
|
+
color: '#03BE02',
|
|
77
|
+
bg: themeSelect('#E9FFE9', 'rgba(3, 190, 2, 0.30)')
|
|
78
|
+
},
|
|
79
|
+
error: {
|
|
80
|
+
icon: 'close-circle-fill',
|
|
81
|
+
color: '#FF3141',
|
|
82
|
+
bg: themeSelect('#FFE5E7', 'rgba(255, 49, 65, 0.3)')
|
|
83
|
+
},
|
|
84
|
+
primary: {
|
|
85
|
+
icon: 'volume-up',
|
|
86
|
+
color: 'var(--primary)',
|
|
87
|
+
bg: themeSelect('#FFF7E6', 'rgba(255, 143, 31, 0.3)')
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return mapping[type] || mapping.primary
|
|
92
|
+
}, [type])
|
|
93
|
+
|
|
94
|
+
const handleClose = useCallback(() => {
|
|
95
|
+
if (showAnimation) {
|
|
96
|
+
setIsLeaving(true)
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
setVisible(false)
|
|
99
|
+
onClose?.()
|
|
100
|
+
}, 250)
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
setVisible(false)
|
|
104
|
+
onClose?.()
|
|
105
|
+
}
|
|
106
|
+
}, [showAnimation, onClose])
|
|
107
|
+
|
|
108
|
+
const handleContainerClick = () => {
|
|
109
|
+
if (clickToClose) {
|
|
110
|
+
handleClose()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onChange?.()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (visible && duration > 0) {
|
|
118
|
+
const timer = setTimeout(handleClose, duration)
|
|
119
|
+
return () => clearTimeout(timer)
|
|
120
|
+
}
|
|
121
|
+
}, [visible, duration, handleClose])
|
|
122
|
+
|
|
123
|
+
if (!visible) return null
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<View
|
|
127
|
+
className={formatClassNames(styles['alert-container'], className, {
|
|
128
|
+
[styles['is-pressable']]: !!onChange,
|
|
129
|
+
[styles['show-animation']]: showAnimation,
|
|
130
|
+
[styles['is-leaving']]: isLeaving
|
|
131
|
+
})}
|
|
132
|
+
style={{
|
|
133
|
+
backgroundColor: config.bg,
|
|
134
|
+
borderRadius: borderRadius ? `${borderRadius}PX` : '0'
|
|
135
|
+
}}
|
|
136
|
+
onClick={handleContainerClick}
|
|
137
|
+
>
|
|
138
|
+
|
|
139
|
+
<View className={styles['alert-content']}>
|
|
140
|
+
|
|
141
|
+
{
|
|
142
|
+
showIcon
|
|
143
|
+
? (
|
|
144
|
+
<View className={formatClassNames(styles['icon-wrapper'], iconClassName)}>
|
|
145
|
+
{
|
|
146
|
+
icon
|
|
147
|
+
? icon
|
|
148
|
+
: (
|
|
149
|
+
<Icon
|
|
150
|
+
name={iconName || config.icon}
|
|
151
|
+
color={config.color}
|
|
152
|
+
size={16}
|
|
153
|
+
/>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
</View>
|
|
157
|
+
)
|
|
158
|
+
: undefined
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
<View className={styles['text-container']}>
|
|
162
|
+
|
|
163
|
+
{typeof message === 'string' ? (
|
|
164
|
+
|
|
165
|
+
scroll ? (
|
|
166
|
+
|
|
167
|
+
<View className={styles['scroll-wrapper']}>
|
|
168
|
+
|
|
169
|
+
<View
|
|
170
|
+
className={styles['scroll-track']}
|
|
171
|
+
style={{ animationDuration: `${scrollSpeed}s` }}
|
|
172
|
+
>
|
|
173
|
+
|
|
174
|
+
<Text
|
|
175
|
+
className={formatClassNames(styles['scroll-text'], messageClassName)}
|
|
176
|
+
style={{ color: config.color }}
|
|
177
|
+
>
|
|
178
|
+
{message}
|
|
179
|
+
</Text>
|
|
180
|
+
|
|
181
|
+
<Text
|
|
182
|
+
className={formatClassNames(styles['scroll-text'], messageClassName)}
|
|
183
|
+
style={{ color: config.color }}
|
|
184
|
+
>
|
|
185
|
+
{message}
|
|
186
|
+
</Text>
|
|
187
|
+
|
|
188
|
+
</View>
|
|
189
|
+
|
|
190
|
+
</View>
|
|
191
|
+
|
|
192
|
+
) : (
|
|
193
|
+
|
|
194
|
+
<Text
|
|
195
|
+
className={formatClassNames(styles['message-text'], messageClassName)}
|
|
196
|
+
style={{ color: config.color }}
|
|
197
|
+
>
|
|
198
|
+
{message}
|
|
199
|
+
</Text>
|
|
200
|
+
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
) : message}
|
|
204
|
+
|
|
205
|
+
{description && (
|
|
206
|
+
<View className={styles['description-wrapper']}>
|
|
207
|
+
{
|
|
208
|
+
typeof description === 'string'
|
|
209
|
+
? (
|
|
210
|
+
<Text
|
|
211
|
+
className={styles['description-text']}
|
|
212
|
+
style={{ color: 'var(--content)' }}
|
|
213
|
+
>
|
|
214
|
+
{description}
|
|
215
|
+
</Text>
|
|
216
|
+
)
|
|
217
|
+
: description
|
|
218
|
+
}
|
|
219
|
+
</View>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
</View>
|
|
223
|
+
|
|
224
|
+
<View className={styles['action-container']}>
|
|
225
|
+
|
|
226
|
+
{closable && (
|
|
227
|
+
<View
|
|
228
|
+
className={styles['close-btn-wrapper']}
|
|
229
|
+
onClick={(e) => {
|
|
230
|
+
e.stopPropagation()
|
|
231
|
+
handleClose()
|
|
232
|
+
}}
|
|
233
|
+
>
|
|
234
|
+
{
|
|
235
|
+
closeText
|
|
236
|
+
? (
|
|
237
|
+
<Text className={styles['close-text']} style={{ color: config.color }}>
|
|
238
|
+
{closeText}
|
|
239
|
+
</Text>
|
|
240
|
+
)
|
|
241
|
+
: (
|
|
242
|
+
<Icon
|
|
243
|
+
name="close"
|
|
244
|
+
color={config.color}
|
|
245
|
+
size={14}
|
|
246
|
+
/>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
</View>
|
|
250
|
+
)}
|
|
251
|
+
|
|
252
|
+
{(showArrow || (!showArrow && onChange)) && !closable && (
|
|
253
|
+
<Icon
|
|
254
|
+
name="right"
|
|
255
|
+
color={config.color}
|
|
256
|
+
size={12}
|
|
257
|
+
/>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
</View>
|
|
261
|
+
|
|
262
|
+
</View>
|
|
263
|
+
|
|
264
|
+
</View>
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export default memo(Alert)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@import '../../styles/mixin.styl'
|
|
2
|
+
@import '../../styles/variable.styl'
|
|
3
|
+
|
|
4
|
+
.card
|
|
5
|
+
border-radius 10PX
|
|
6
|
+
overflow hidden
|
|
7
|
+
background-color var(--containerCard)
|
|
8
|
+
margin-left $WING_BLANK
|
|
9
|
+
margin-right $WING_BLANK
|
|
10
|
+
&.show-top-gutter
|
|
11
|
+
margin-top $GUTTER_BLANK
|
|
12
|
+
&.show-bottom-gutter
|
|
13
|
+
margin-bottom $GUTTER_BLANK
|
|
14
|
+
&.show-top-blank
|
|
15
|
+
margin-top $CARD_BLANK
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View } from '@tarojs/components'
|
|
3
|
+
import { formatClassNames } from '../../util/function'
|
|
4
|
+
|
|
5
|
+
import styles from './index.module.styl'
|
|
6
|
+
|
|
7
|
+
export interface CardProps {
|
|
8
|
+
className?: string
|
|
9
|
+
showTopGutter?: boolean
|
|
10
|
+
showBottomGutter?: boolean
|
|
11
|
+
showTopBlank?: boolean
|
|
12
|
+
children?: React.ReactNode
|
|
13
|
+
onClick?: () => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const CellCard: React.FC<CardProps> = ({
|
|
17
|
+
showTopGutter = false,
|
|
18
|
+
showBottomGutter = false,
|
|
19
|
+
showTopBlank = false,
|
|
20
|
+
onClick,
|
|
21
|
+
className,
|
|
22
|
+
children
|
|
23
|
+
}) => {
|
|
24
|
+
const combinedClass = formatClassNames(
|
|
25
|
+
styles['card'],
|
|
26
|
+
{
|
|
27
|
+
[styles['show-top-gutter']]: showTopGutter,
|
|
28
|
+
[styles['show-bottom-gutter']]: showBottomGutter,
|
|
29
|
+
[styles['show-top-blank']]: showTopBlank,
|
|
30
|
+
},
|
|
31
|
+
className
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View
|
|
36
|
+
className={combinedClass}
|
|
37
|
+
onClick={onClick}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</View>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default CellCard
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
@import '../../styles/mixin.styl'
|
|
2
2
|
@import '../../styles/variable.styl'
|
|
3
3
|
|
|
4
|
-
$
|
|
5
|
-
$CELL_PADDING_VERTICAL = 15PX
|
|
4
|
+
$PADDING = 15PX
|
|
6
5
|
$LABEL_MARGIN_RIGHT = 20PX
|
|
7
6
|
|
|
8
7
|
.display-card
|
|
9
|
-
padding $
|
|
8
|
+
padding $PADDING
|
|
10
9
|
background-color var(--containerCard)
|
|
11
|
-
margin
|
|
10
|
+
margin-left $WING_BLANK
|
|
11
|
+
margin-right $WING_BLANK
|
|
12
12
|
border-radius 10PX
|
|
13
13
|
&.show-top-gutter
|
|
14
14
|
margin-top $GUTTER_BLANK
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { Image, ImageProps } from '@tarojs/components'
|
|
3
|
+
|
|
4
|
+
import getResponsiveImage from '../../util/function'
|
|
5
|
+
|
|
6
|
+
export interface IRemoteImageProps extends Omit<ImageProps, 'src'> {
|
|
7
|
+
/** 图片地址 */
|
|
8
|
+
url: string
|
|
9
|
+
/** 期望显示宽度 */
|
|
10
|
+
width?: number
|
|
11
|
+
/** 期望显示高度 */
|
|
12
|
+
height?: number
|
|
13
|
+
/** 是否不进行裁剪(等比缩放) */
|
|
14
|
+
noCrop?: boolean
|
|
15
|
+
/** 图片质量 1-100 */
|
|
16
|
+
quality?: string | number
|
|
17
|
+
/** 自定义样式类 */
|
|
18
|
+
className?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const RemoteImage: React.FC<IRemoteImageProps> = (props) => {
|
|
22
|
+
const {
|
|
23
|
+
url,
|
|
24
|
+
width = 50,
|
|
25
|
+
height = 50,
|
|
26
|
+
noCrop = false,
|
|
27
|
+
quality,
|
|
28
|
+
mode = 'aspectFill',
|
|
29
|
+
className,
|
|
30
|
+
...restProps
|
|
31
|
+
} = props
|
|
32
|
+
|
|
33
|
+
const responsiveSrc = useMemo(() => {
|
|
34
|
+
return getResponsiveImage({
|
|
35
|
+
url,
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
38
|
+
noCrop,
|
|
39
|
+
quality,
|
|
40
|
+
})
|
|
41
|
+
}, [url, width, height, noCrop, quality])
|
|
42
|
+
console.log("🚀 ~ RemoteImage ~ responsiveSrc:", responsiveSrc)
|
|
43
|
+
|
|
44
|
+
const containerStyle: React.CSSProperties = {
|
|
45
|
+
width: width ? `${width}px` : 'auto',
|
|
46
|
+
height: height ? `${height}px` : 'auto',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Image
|
|
51
|
+
className={className}
|
|
52
|
+
src={responsiveSrc}
|
|
53
|
+
mode={mode}
|
|
54
|
+
style={containerStyle}
|
|
55
|
+
{...restProps}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default React.memo(RemoteImage)
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,9 @@ export { default as Checkbox } from './components/Checkbox'
|
|
|
7
7
|
export { default as Tag } from './components/Tag'
|
|
8
8
|
export { default as FieldCard } from './components/FieldCard'
|
|
9
9
|
export { default as DisplayCard } from './components/DisplayCard'
|
|
10
|
+
export { default as Card } from './components/Card'
|
|
11
|
+
export { default as RemoteImage } from './components/RemoteImage'
|
|
12
|
+
export { default as Alert } from './components/Alert'
|
|
10
13
|
|
|
11
14
|
export { default as FlowItem } from './item/FlowItem'
|
|
12
15
|
export { default as ProductItem, setProductItemConfig } from './item/ProductItem'
|
package/src/util/env.ts
CHANGED
package/src/util/function.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { pixelRatio } from "./env"
|
|
2
|
+
|
|
1
3
|
interface ClassRecord {
|
|
2
4
|
[key: string]: boolean | string | number
|
|
3
5
|
}
|
|
@@ -53,4 +55,99 @@ export function isEmpty(value: any): value is EmptyValue {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
return isEmpty
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function formatAssetUrl(url: string, prefix?: string) {
|
|
61
|
+
if (!prefix) {
|
|
62
|
+
prefix = 'https'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!url) {
|
|
66
|
+
return ''
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (url.indexOf('//') === 0) {
|
|
70
|
+
url = prefix + ':' + url
|
|
71
|
+
}
|
|
72
|
+
else if (url.indexOf('http://img.finstao.com') === 0) {
|
|
73
|
+
url = url.replace(/^http/, 'https')
|
|
74
|
+
}
|
|
75
|
+
return url
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function cleanQuery(url: string) {
|
|
79
|
+
if (url && url.split) {
|
|
80
|
+
let terms = url.split('?')
|
|
81
|
+
return terms.length === 2 ? terms[0] : url
|
|
82
|
+
}
|
|
83
|
+
return url
|
|
84
|
+
}
|
|
85
|
+
function isResponsiveImage(uri: string) {
|
|
86
|
+
return typeof uri === 'string'
|
|
87
|
+
&& (uri.indexOf('clouddn') > 0 || uri.indexOf('img.finstao.com') > 0)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const imagePixelRatio = Math.min(2, pixelRatio || 2)
|
|
91
|
+
|
|
92
|
+
export interface IWatermarkOptions {
|
|
93
|
+
text?: string
|
|
94
|
+
gravity?: string
|
|
95
|
+
dissolve?: number
|
|
96
|
+
[key: string]: any
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface IGetResponsiveImageOptions {
|
|
100
|
+
url?: string
|
|
101
|
+
width?: number
|
|
102
|
+
height?: number
|
|
103
|
+
noCrop?: boolean
|
|
104
|
+
quality?: string | number
|
|
105
|
+
supportWebp?: boolean
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 获取响应式图片地址
|
|
110
|
+
*/
|
|
111
|
+
export default function getResponsiveImage(options: IGetResponsiveImageOptions): string {
|
|
112
|
+
let {
|
|
113
|
+
url,
|
|
114
|
+
width,
|
|
115
|
+
height,
|
|
116
|
+
noCrop,
|
|
117
|
+
quality,
|
|
118
|
+
} = options
|
|
119
|
+
|
|
120
|
+
if (!url) return ''
|
|
121
|
+
|
|
122
|
+
// 如果不是可缩放类型的图片链接(如 svg/base64),原样返回
|
|
123
|
+
if (!isResponsiveImage(url)) {
|
|
124
|
+
return url
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const suffix: string[] = []
|
|
128
|
+
|
|
129
|
+
if (typeof width === 'number') {
|
|
130
|
+
const w = Math.floor(width * imagePixelRatio)
|
|
131
|
+
suffix.push('w', w.toString())
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof height === 'number') {
|
|
135
|
+
const h = Math.floor(height * imagePixelRatio)
|
|
136
|
+
suffix.push('h', h.toString())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (quality) {
|
|
140
|
+
suffix.push('q', quality.toString())
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const hasImageV2 = suffix.length > 0
|
|
144
|
+
let finalUrl = cleanQuery(url)
|
|
145
|
+
|
|
146
|
+
if (hasImageV2) {
|
|
147
|
+
const mode = noCrop ? 2 : 1
|
|
148
|
+
finalUrl += `?imageView2/${mode}/${suffix.join('/')}`
|
|
149
|
+
}
|
|
150
|
+
console.log("🚀 ~ getResponsiveImage ~ finalUrl:", finalUrl)
|
|
151
|
+
|
|
152
|
+
return formatAssetUrl(finalUrl)
|
|
56
153
|
}
|