@mpxjs/webpack-plugin 2.10.15 → 2.10.16

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.
@@ -0,0 +1,444 @@
1
+ /**
2
+ * ✔ min 最小值
3
+ * ✔ max 最大值
4
+ * ✔ step 步长
5
+ * ✔ disabled 是否禁用
6
+ * ✔ value 当前取值
7
+ * ✔ color 背景条的颜色(已废弃,使用 backgroundColor)
8
+ * ✔ selected-color 已选择的颜色(已废弃,使用 activeColor)
9
+ * ✔ activeColor 已选择的颜色
10
+ * ✔ backgroundColor 背景条的颜色
11
+ * ✔ block-size 滑块的大小
12
+ * ✔ block-color 滑块的颜色
13
+ * ✘ show-value 是否显示当前 value
14
+ * ✔ bindchange 完成一次拖动后触发的事件
15
+ * ✔ bindchanging 拖动过程中触发的事件
16
+ */
17
+ import {
18
+ JSX,
19
+ useRef,
20
+ forwardRef,
21
+ useEffect,
22
+ useState,
23
+ createElement,
24
+ ForwardedRef,
25
+ useContext,
26
+ useMemo
27
+ } from 'react'
28
+ import {
29
+ View,
30
+ ViewStyle
31
+ } from 'react-native'
32
+ import { GestureDetector, Gesture, GestureStateChangeEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'
33
+ import Animated, { useSharedValue, useAnimatedStyle, runOnJS } from 'react-native-reanimated'
34
+ import { warn } from '@mpxjs/utils'
35
+
36
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
37
+ import useNodesRef, { HandlerRef } from './useNodesRef'
38
+ import { useLayout, useTransformStyle, extendObject, useRunOnJSCallback } from './utils'
39
+ import Portal from './mpx-portal'
40
+ import { FormContext, FormFieldValue } from './context'
41
+
42
+ export interface SliderProps {
43
+ min?: number
44
+ max?: number
45
+ step?: number
46
+ disabled?: boolean
47
+ value?: number
48
+ color?: string
49
+ 'selected-color'?: string
50
+ activeColor?: string
51
+ backgroundColor?: string
52
+ 'block-size'?: number
53
+ 'block-color'?: string
54
+ name?: string
55
+ bindchange?: (event: any) => void
56
+ catchchange?: (event: any) => void
57
+ bindchanging?: (event: any) => void
58
+ catchchanging?: (event: any) => void
59
+ style?: ViewStyle & Record<string, any>
60
+ 'enable-offset'?: boolean
61
+ 'enable-var'?: boolean
62
+ 'external-var-context'?: Record<string, any>
63
+ 'parent-font-size'?: number
64
+ 'parent-width'?: number
65
+ 'parent-height'?: number
66
+ }
67
+
68
+ const Slider = forwardRef<
69
+ HandlerRef<View, SliderProps>,
70
+ SliderProps
71
+ >((props: SliderProps, ref: ForwardedRef<HandlerRef<View, SliderProps>>): JSX.Element => {
72
+ const {
73
+ min: rawMin = 0,
74
+ max: rawMax = 100,
75
+ step: rawStep = 1,
76
+ disabled = false,
77
+ value: rawValue,
78
+ color,
79
+ 'selected-color': selectedColor,
80
+ activeColor = selectedColor || color || '#1aad19',
81
+ backgroundColor = color || '#e9e9e9',
82
+ 'block-size': rawBlockSize = 28,
83
+ 'block-color': blockColor = '#ffffff',
84
+ name,
85
+ style = {},
86
+ 'enable-var': enableVar,
87
+ 'external-var-context': externalVarContext,
88
+ 'parent-font-size': parentFontSize,
89
+ 'parent-width': parentWidth,
90
+ 'parent-height': parentHeight
91
+ } = props
92
+
93
+ // 确保数值类型正确
94
+ const min = typeof rawMin === 'string' ? parseFloat(rawMin) : rawMin
95
+ const max = typeof rawMax === 'string' ? parseFloat(rawMax) : rawMax
96
+ const step = typeof rawStep === 'string' ? parseFloat(rawStep) : rawStep
97
+ const value = rawValue !== undefined ? (typeof rawValue === 'string' ? parseFloat(rawValue) : rawValue) : undefined
98
+ const blockSize = typeof rawBlockSize === 'string' ? parseFloat(rawBlockSize) : rawBlockSize
99
+
100
+ // 如果没有提供 value,则使用 min 作为默认值
101
+ const defaultValue = value !== undefined ? value : min
102
+ const nodeRef = useRef(null)
103
+ const trackRef = useRef(null)
104
+ const [currentValue, setCurrentValue] = useState(defaultValue)
105
+ const [trackWidth, setTrackWidth] = useState(0)
106
+
107
+ const thumbPosition = useSharedValue(0)
108
+ const isDragging = useSharedValue(false)
109
+ const startDragPosition = useSharedValue(0) // 记录拖拽开始时的位置
110
+ const startDragValue = useSharedValue(0) // 记录拖拽开始时的值
111
+
112
+ let formValuesMap: Map<string, FormFieldValue> | undefined
113
+
114
+ const propsRef = useRef(props)
115
+ propsRef.current = props
116
+
117
+ const formContext = useContext(FormContext)
118
+
119
+ if (formContext) {
120
+ formValuesMap = formContext.formValuesMap
121
+ }
122
+
123
+ const {
124
+ normalStyle,
125
+ hasSelfPercent,
126
+ setWidth,
127
+ setHeight,
128
+ hasPositionFixed
129
+ } = useTransformStyle(style, {
130
+ enableVar,
131
+ externalVarContext,
132
+ parentFontSize,
133
+ parentWidth,
134
+ parentHeight
135
+ })
136
+
137
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({
138
+ props,
139
+ hasSelfPercent,
140
+ setWidth,
141
+ setHeight,
142
+ nodeRef
143
+ })
144
+
145
+ useNodesRef(props, ref, nodeRef, {
146
+ style: normalStyle
147
+ })
148
+
149
+ // 使用 useRunOnJSCallback 处理手势回调
150
+ const runOnJSCallbackRef = useRef({
151
+ triggerChangeEvent: (newValue: number) => {
152
+ setCurrentValue(newValue)
153
+ const currentProps = propsRef.current
154
+ const changeHandler = currentProps.bindchange || currentProps.catchchange
155
+ if (changeHandler) {
156
+ changeHandler(getCustomEvent('change', {}, { layoutRef, detail: { value: newValue } }, currentProps))
157
+ }
158
+ },
159
+ triggerChangingEvent: (newValue: number) => {
160
+ const currentProps = propsRef.current
161
+ const changingHandler = currentProps.bindchanging || currentProps.catchchanging
162
+ if (changingHandler) {
163
+ changingHandler(getCustomEvent('changing', {}, { layoutRef, detail: { value: newValue } }, currentProps))
164
+ }
165
+ }
166
+ })
167
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
168
+
169
+ // 限制步长,确保 step 大于 0,并且可被 (max - min) 整除
170
+ const validateStep = (step: number, min: number, max: number): number => {
171
+ if (step <= 0) return 1
172
+ if ((max - min) % step !== 0) {
173
+ warn(`Step ${step} is not a divisor of range ${max - min}`)
174
+ }
175
+ return step
176
+ }
177
+
178
+ const validStep = validateStep(step, min, max)
179
+
180
+ // 将值约束在 min-max 范围内,并按步长对齐
181
+ const constrainValue = (val: number, minVal: number = min, maxVal: number = max, stepVal: number = validStep): number => {
182
+ const constrained = Math.max(minVal, Math.min(maxVal, val))
183
+ const steps = Math.round((constrained - minVal) / stepVal)
184
+ return minVal + steps * stepVal
185
+ }
186
+
187
+ // 计算滑块位置
188
+ const getThumbPosition = (val: number, trackW: number = trackWidth, minVal: number = min, maxVal: number = max): number => {
189
+ if (trackW === 0) return 0
190
+ const percentage = (val - minVal) / (maxVal - minVal)
191
+ const position = percentage * trackW
192
+ return position
193
+ }
194
+
195
+ // 手势处理
196
+ const panGesture = useMemo(() => {
197
+ const getThumbPositionWorklet = (val: number, trackW: number, minVal: number, maxVal: number): number => {
198
+ 'worklet'
199
+ if (trackW === 0) return 0
200
+ const percentage = (val - minVal) / (maxVal - minVal)
201
+ return percentage * trackW
202
+ }
203
+
204
+ const constrainValueWorklet = (val: number, minVal: number, maxVal: number, stepVal: number): number => {
205
+ 'worklet'
206
+ const constrained = Math.max(minVal, Math.min(maxVal, val))
207
+ const steps = Math.round((constrained - minVal) / stepVal)
208
+ return minVal + steps * stepVal
209
+ }
210
+
211
+ return Gesture.Pan()
212
+ .enabled(!disabled) // 通过手势启用状态控制是否可拖拽
213
+ .onBegin(() => {
214
+ 'worklet'
215
+ if (trackWidth === 0) return
216
+ isDragging.value = true
217
+ // 记录拖拽开始时的位置 - 使用当前的动画位置
218
+ startDragPosition.value = thumbPosition.value
219
+ // 根据当前位置反推值
220
+ const percentage = thumbPosition.value / trackWidth
221
+ const currentVal = min + percentage * (max - min)
222
+ startDragValue.value = constrainValueWorklet(currentVal, min, max, validStep)
223
+ })
224
+ .onUpdate((event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
225
+ 'worklet'
226
+ if (trackWidth === 0) return
227
+
228
+ // 基于拖拽开始位置计算新位置
229
+ const newX = startDragPosition.value + event.translationX
230
+ const clampedX = Math.max(0, Math.min(trackWidth, newX))
231
+
232
+ // 计算新值
233
+ const percentage = clampedX / trackWidth
234
+ const rawValue = min + percentage * (max - min)
235
+ const newValue = constrainValueWorklet(rawValue, min, max, validStep)
236
+
237
+ // 更新滑块位置 - 使用约束后的值对应的位置
238
+ const constrainedPosition = getThumbPositionWorklet(newValue, trackWidth, min, max)
239
+ thumbPosition.value = constrainedPosition
240
+
241
+ // 只触发 changing 事件,不更新 currentValue(避免干扰拖拽)
242
+ runOnJS(runOnJSCallback)('triggerChangingEvent', newValue)
243
+ })
244
+ .onEnd((event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
245
+ 'worklet'
246
+ isDragging.value = false
247
+
248
+ // 基于拖拽开始位置计算最终位置
249
+ const newX = startDragPosition.value + event.translationX
250
+ const clampedX = Math.max(0, Math.min(trackWidth, newX))
251
+ const percentage = clampedX / trackWidth
252
+ const rawValue = min + percentage * (max - min)
253
+ const finalValue = constrainValueWorklet(rawValue, min, max, validStep)
254
+
255
+ // 确保滑块位置与最终值匹配
256
+ const finalPosition = getThumbPositionWorklet(finalValue, trackWidth, min, max)
257
+ thumbPosition.value = finalPosition
258
+
259
+ // 更新 currentValue 并触发 change 事件
260
+ runOnJS(runOnJSCallback)('triggerChangeEvent', finalValue)
261
+ })
262
+ }, [disabled, trackWidth, min, max, validStep, runOnJSCallback])
263
+
264
+ // 当 value 属性变化时更新位置
265
+ useEffect(() => {
266
+ const newValue = constrainValue(defaultValue)
267
+ setCurrentValue(newValue)
268
+ // 同时更新动画位置
269
+ thumbPosition.value = getThumbPosition(newValue)
270
+ }, [defaultValue, min, max, validStep])
271
+
272
+ // 当 trackWidth 变化时更新滑块位置
273
+ useEffect(() => {
274
+ // 只在非拖拽状态下更新位置
275
+ if (!isDragging.value) {
276
+ thumbPosition.value = getThumbPosition(currentValue)
277
+ }
278
+ }, [trackWidth, currentValue])
279
+
280
+ // 动画样式
281
+ const animatedThumbStyle = useAnimatedStyle(() => {
282
+ const blockSizeNum = Math.max(12, Math.min(28, blockSize))
283
+ const trackHeight = 4
284
+ return {
285
+ position: 'absolute',
286
+ top: -((blockSizeNum - trackHeight) / 2),
287
+ left: Math.max(0, Math.min(trackWidth - blockSizeNum, thumbPosition.value - (blockSizeNum / 2))),
288
+ width: blockSizeNum,
289
+ height: blockSizeNum,
290
+ justifyContent: 'center',
291
+ alignItems: 'center'
292
+ }
293
+ })
294
+
295
+ // 轨道布局回调
296
+ const onTrackLayout = (event: any) => {
297
+ const { width } = event.nativeEvent.layout
298
+ setTrackWidth(width)
299
+ }
300
+
301
+ // 表单相关处理
302
+ const resetValue = () => {
303
+ const currentProps = propsRef.current
304
+ const currentValue = currentProps.value !== undefined ? currentProps.value : currentProps.min || 0
305
+ const parsedValue = typeof currentValue === 'string' ? parseFloat(currentValue) : currentValue
306
+ const currentMin = typeof currentProps.min === 'string' ? parseFloat(currentProps.min) : (currentProps.min || 0)
307
+ const currentMax = typeof currentProps.max === 'string' ? parseFloat(currentProps.max) : (currentProps.max || 100)
308
+ const currentStep = typeof currentProps.step === 'string' ? parseFloat(currentProps.step) : (currentProps.step || 1)
309
+ const resetVal = parsedValue !== undefined ? parsedValue : currentMin
310
+ const validatedStep = validateStep(currentStep, currentMin, currentMax)
311
+ const constrainedVal = constrainValue(resetVal, currentMin, currentMax, validatedStep)
312
+ setCurrentValue(constrainedVal)
313
+ thumbPosition.value = getThumbPosition(constrainedVal, trackWidth, currentMin, currentMax)
314
+ }
315
+
316
+ const getValue = () => {
317
+ return currentValue
318
+ }
319
+
320
+ if (formValuesMap) {
321
+ if (!name) {
322
+ warn('If a form component is used, the name attribute is required.')
323
+ } else {
324
+ formValuesMap.set(name, { getValue, resetValue })
325
+ }
326
+ }
327
+
328
+ useEffect(() => {
329
+ return () => {
330
+ if (formValuesMap && name) {
331
+ formValuesMap.delete(name)
332
+ }
333
+ }
334
+ }, [])
335
+
336
+ // 样式定义
337
+ const blockSizeNum = Math.max(12, Math.min(28, blockSize))
338
+ const trackHeight = 4
339
+
340
+ const containerStyle: ViewStyle = extendObject({} as ViewStyle, {
341
+ flexDirection: 'row' as const,
342
+ alignItems: 'center' as const,
343
+ minHeight: Math.max(blockSizeNum + 8, 40),
344
+ paddingHorizontal: 14 // 固定内边距,不受 block-size 影响
345
+ }, normalStyle, layoutStyle)
346
+
347
+ const trackStyle: ViewStyle = {
348
+ flex: 1,
349
+ height: trackHeight,
350
+ backgroundColor,
351
+ borderRadius: trackHeight / 2,
352
+ position: 'relative'
353
+ }
354
+
355
+ // 动画进度条样式
356
+ const animatedProgressStyle = useAnimatedStyle(() => {
357
+ return {
358
+ height: '100%',
359
+ backgroundColor: activeColor,
360
+ borderRadius: trackHeight / 2,
361
+ width: Math.max(0, thumbPosition.value)
362
+ }
363
+ })
364
+
365
+ const thumbStyle: ViewStyle = {
366
+ width: blockSizeNum,
367
+ height: blockSizeNum,
368
+ backgroundColor: blockColor,
369
+ borderRadius: blockSizeNum / 2,
370
+ shadowColor: '#000',
371
+ shadowOffset: { width: 0, height: 2 },
372
+ shadowOpacity: 0.2,
373
+ shadowRadius: 4,
374
+ elevation: 4
375
+ }
376
+
377
+ const innerProps = useInnerProps(
378
+ extendObject({}, props, layoutProps, {
379
+ ref: nodeRef
380
+ }),
381
+ [
382
+ 'min',
383
+ 'max',
384
+ 'step',
385
+ 'disabled',
386
+ 'value',
387
+ 'color',
388
+ 'selected-color',
389
+ 'activeColor',
390
+ 'backgroundColor',
391
+ 'block-size',
392
+ 'block-color',
393
+ 'bindchange',
394
+ 'catchchange',
395
+ 'bindchanging',
396
+ 'catchchanging'
397
+ ],
398
+ { layoutRef }
399
+ )
400
+
401
+ const sliderContent = createElement(
402
+ View,
403
+ extendObject({}, innerProps, { style: containerStyle }),
404
+ // 轨道容器
405
+ createElement(
406
+ View,
407
+ {
408
+ style: trackStyle,
409
+ onLayout: onTrackLayout,
410
+ ref: trackRef
411
+ },
412
+ // 进度条 - 使用动画样式
413
+ createElement(Animated.View, {
414
+ style: animatedProgressStyle
415
+ }),
416
+ // 滑块容器
417
+ createElement(
418
+ GestureDetector,
419
+ {
420
+ gesture: panGesture
421
+ },
422
+ createElement(
423
+ Animated.View,
424
+ {
425
+ style: [animatedThumbStyle]
426
+ },
427
+ // 滑块
428
+ createElement(View, {
429
+ style: thumbStyle
430
+ })
431
+ )
432
+ )
433
+ )
434
+ )
435
+
436
+ if (hasPositionFixed) {
437
+ return createElement(Portal, null, sliderContent)
438
+ }
439
+
440
+ return sliderContent
441
+ })
442
+
443
+ Slider.displayName = 'MpxSlider'
444
+ export default Slider
@@ -287,16 +287,13 @@ function backgroundSize (imageProps: ImageProps, preImageInfo: PreImageInfo, ima
287
287
  } else { // 数值类型 ImageStyle
288
288
  // 数值类型设置为 stretch
289
289
  imageProps.resizeMode = 'stretch'
290
- if (type === 'linear') {
291
- const dimensionWidth = calcPercent(width as NumberVal, layoutWidth) || 0
292
- const dimensionHeight = calcPercent(height as NumberVal, layoutHeight) || 0
293
- // ios 上 linear 组件只要重新触发渲染,在渲染过程中 width 或者 height 被设置为 0,即使后面再更新为正常宽高,也会渲染不出来
294
- if (dimensionWidth && dimensionHeight) {
295
- dimensions = {
296
- width: dimensionWidth,
297
- height: dimensionHeight
298
- } as { width: NumberVal, height: NumberVal }
299
- }
290
+ if (type === 'linear' && (!layoutWidth || !layoutHeight)) {
291
+ // ios linear 组件只要重新触发渲染,在渲染过程中外层容器 width 或者 height 被设置为 0,通过设置 % 的方式会渲染不出来,即使后面再更新为正常宽高也渲染不出来
292
+ // 所以 hack 手动先将 linear 宽高也设置为 0,后面再更新为正确的数值或 %。
293
+ dimensions = {
294
+ width: 0,
295
+ height: 0
296
+ } as { width: NumberVal, height: NumberVal }
300
297
  } else {
301
298
  dimensions = {
302
299
  width: isPercent(width) ? width : +width,
@@ -56,7 +56,7 @@
56
56
  if (val !== -1) this.setSelectionRange(undefined, val)
57
57
  },
58
58
  immediate: true
59
- }
59
+ },
60
60
  },
61
61
  render (createElement) {
62
62
  const mergeBefore = {
@@ -4,10 +4,12 @@
4
4
  import { processSize } from '../../utils'
5
5
  import BScroll from '@better-scroll/core'
6
6
  import PullDown from '@better-scroll/pull-down'
7
+ import MouseWheel from '@better-scroll/mouse-wheel'
7
8
  import throttle from 'lodash/throttle'
8
9
  import debounce from 'lodash/debounce'
9
10
 
10
11
  BScroll.use(PullDown)
12
+ BScroll.use(MouseWheel)
11
13
 
12
14
  let mutationObserver = null
13
15
  let resizeObserver = null
@@ -222,7 +224,11 @@
222
224
  bounce: false,
223
225
  stopPropagation: true,
224
226
  bindToWrapper: true,
225
- eventPassthrough: (this.scrollX && 'vertical') || (this.scrollY && 'horizontal') || ''
227
+ eventPassthrough: (this.scrollX && 'vertical') || (this.scrollY && 'horizontal') || '',
228
+ mouseWheel: {
229
+ speed: 20,
230
+ easeTime: 300
231
+ }
226
232
  }
227
233
  if (this.refresherEnabled) {
228
234
  originBsOptions.bounce = true
@@ -148,6 +148,11 @@
148
148
  },
149
149
  controls: function (show) {
150
150
  this.$emit('controlstoggle', inheritEvent('controlstoggle', {}, { show }))
151
+ },
152
+ objectFit (val) {
153
+ if (this._player && this._player.video) {
154
+ this._player.video.style.objectFit = val
155
+ }
151
156
  }
152
157
  },
153
158
  mounted () {
@@ -189,6 +194,9 @@
189
194
  if (this.initialTime) {
190
195
  this._player.seek(this.initialTime)
191
196
  }
197
+ if (this.objectFit) {
198
+ this._player.video.style.objectFit = this.objectFit
199
+ }
192
200
  },
193
201
  initStyle () {
194
202
 
@@ -239,7 +247,10 @@
239
247
 
240
248
  this._player.on('progress', (e) => {
241
249
  const eNode = e.target
242
- const buffered = (eNode?.buffered?.end(0)) / (eNode?.duration)
250
+ let buffered = 0
251
+ if (eNode?.buffered && eNode.buffered.length > 0) {
252
+ buffered = (eNode.buffered.end(0)) / (eNode?.duration)
253
+ }
243
254
  this.$emit('progress', inheritEvent('progress', e, { buffered: buffered * 100 }))
244
255
  })
245
256
 
@@ -108,7 +108,9 @@ registered in parent context!`)
108
108
  export function getComponent (component, extendOptions) {
109
109
  component = component.__esModule ? component.default : component
110
110
  // eslint-disable-next-line
111
- if (extendOptions) extend(component, extendOptions)
111
+ if (extendOptions && !component.__mpxExtended) {
112
+ extend(component, extendOptions, { __mpxExtended: true })
113
+ }
112
114
  return component
113
115
  }
114
116
 
@@ -5,7 +5,9 @@ import { extend } from './utils'
5
5
  export function getComponent (component, extendOptions) {
6
6
  component = component.__esModule ? component.default : component
7
7
  // eslint-disable-next-line
8
- if (extendOptions) Object.assign(component, extendOptions)
8
+ if (extendOptions && !component.__mpxExtended) {
9
+ extend(component, extendOptions, { __mpxExtended: true })
10
+ }
9
11
  return component
10
12
  }
11
13
 
@@ -15,7 +17,7 @@ export function getAsyncSuspense (commonProps) {
15
17
  result = memo(forwardRef(function (props, ref) {
16
18
  return createElement(AsyncSuspense,
17
19
  extend({}, commonProps, {
18
- innerProps: Object.assign({}, props, { ref })
20
+ innerProps: extend({}, props, { ref })
19
21
  })
20
22
  )
21
23
  }))
@@ -176,11 +176,11 @@ const i18nWxsPath = normalize.lib('runtime/i18n.wxs')
176
176
  const i18nWxsLoaderPath = normalize.lib('wxs/i18n-loader.js')
177
177
  // 添加~前缀避免wxs绝对路径在存在projectRoot时被拼接为错误路径
178
178
  const i18nWxsRequest = '~' + i18nWxsLoaderPath + '!' + i18nWxsPath
179
- const i18nModuleName = '_i'
179
+ const i18nModuleName = '_i_'
180
180
  const stringifyWxsPath = '~' + normalize.lib('runtime/stringify.wxs')
181
- const stringifyModuleName = '_s'
181
+ const stringifyModuleName = '_s_'
182
182
  const optionalChainWxsPath = '~' + normalize.lib('runtime/oc.wxs')
183
- const optionalChainWxsName = '_oc' // 改成_oc解决web下_o重名问题
183
+ const optionalChainWxsName = '_oc_' // 改成_oc解决web下_o重名问题
184
184
 
185
185
  const tagRES = /(\{\{(?:.|\n|\r)+?\}\})(?!})/
186
186
  const tagRE = /\{\{((?:.|\n|\r)+?)\}\}(?!})/
@@ -1383,7 +1383,11 @@ function processEvent (el, options) {
1383
1383
  const targetConfigs = isCapture ? eventConfigMap[type].captureConfigs : eventConfigMap[type].configs
1384
1384
  targetConfigs.push(Object.assign({ name }, parsedFunc))
1385
1385
  if (modifiers.indexOf('proxy') > -1 || options.forceProxyEvent) {
1386
- eventConfigMap[type].proxy = true
1386
+ if (isCapture) {
1387
+ eventConfigMap[type].captureProxy = true
1388
+ } else {
1389
+ eventConfigMap[type].proxy = true
1390
+ }
1387
1391
  }
1388
1392
  }
1389
1393
  }
@@ -1423,10 +1427,10 @@ function processEvent (el, options) {
1423
1427
  }
1424
1428
 
1425
1429
  for (const type in eventConfigMap) {
1426
- const { configs = [], captureConfigs = [], proxy } = eventConfigMap[type]
1430
+ const { configs = [], captureConfigs = [], proxy, captureProxy } = eventConfigMap[type]
1427
1431
 
1428
1432
  let needBubblingBind = isNeedBind(configs, proxy)
1429
- let needCaptureBind = isNeedBind(captureConfigs, proxy)
1433
+ let needCaptureBind = isNeedBind(captureConfigs, captureProxy)
1430
1434
 
1431
1435
  const escapedType = dash2hump(type)
1432
1436
  // 排除特殊情况
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 链式合并方法的工具函数
3
+ *
4
+ * 在多条件分支下使用 Object.assign 会导致同名方法被覆盖,
5
+ * 这个函数通过创建组合函数来确保所有方法都能按顺序执行。
6
+ *
7
+ * @param {Object} target - 目标 visitor 对象
8
+ * @param {Object} source - 要链式分配的 visitor 方法对象
9
+ *
10
+ * @example
11
+ * const visitor = {}
12
+ *
13
+ * // 第一次合并
14
+ * chainAssign(visitor, {
15
+ * CallExpression(path) {
16
+ * console.log('第一个处理器')
17
+ * }
18
+ * })
19
+ *
20
+ * // 第二次合并 - 不会覆盖,而是组合执行
21
+ * chainAssign(visitor, {
22
+ * CallExpression(path) {
23
+ * console.log('第二个处理器')
24
+ * }
25
+ * })
26
+ *
27
+ * // 执行时会依次输出:
28
+ * // 第一个处理器
29
+ * // 第二个处理器
30
+ */
31
+ module.exports = function chainAssign (target, source) {
32
+ for (const [key, value] of Object.entries(source)) {
33
+ if (target[key]) {
34
+ // 如果已存在同名方法,创建组合函数依次执行
35
+ const originalMethod = target[key]
36
+ target[key] = function (path) {
37
+ originalMethod.call(this, path)
38
+ // 只有当节点没有停止遍历或被移除时才继续执行
39
+ if (!path.removed && !path.shouldStop) {
40
+ value.call(this, path)
41
+ }
42
+ }
43
+ } else {
44
+ target[key] = value
45
+ }
46
+ }
47
+ }