Skip to content

fix(swiper): 修改初始值时不生效问题 #3293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions packages/nutui-taro-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,23 @@
"@nutui/touch-emulator": "^1.0.0",
"@nutui/lottie-miniprogram": "^1.0.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@tarojs/components": "4.0.12",
"@tarojs/helper": "4.0.12",
"@tarojs/plugin-framework-react": "4.0.12",
"@tarojs/plugin-html": "4.0.12",
"@tarojs/plugin-platform-alipay": "4.0.12",
"@tarojs/plugin-platform-h5": "4.0.12",
"@tarojs/plugin-platform-jd": "4.0.12",
"@tarojs/plugin-platform-qq": "4.0.12",
"@tarojs/plugin-platform-swan": "4.0.12",
"@tarojs/plugin-platform-tt": "4.0.12",
"@tarojs/plugin-platform-weapp": "4.0.12",
"@tarojs/react": "4.0.12",
"@tarojs/router": "4.0.12",
"@tarojs/runtime": "4.0.12",
"@tarojs/shared": "4.0.12",
"@tarojs/taro": "4.0.12",
"@tarojs/taro-h5": "4.0.12",
"@tarojs/components": "4.1.3",
"@tarojs/helper": "4.1.3",
"@tarojs/plugin-framework-react": "4.1.3",
"@tarojs/plugin-html": "4.1.3",
"@tarojs/plugin-platform-alipay": "4.1.3",
"@tarojs/plugin-platform-h5": "4.1.3",
"@tarojs/plugin-platform-jd": "4.1.3",
"@tarojs/plugin-platform-qq": "4.1.3",
"@tarojs/plugin-platform-swan": "4.1.3",
"@tarojs/plugin-platform-tt": "4.1.3",
"@tarojs/plugin-platform-weapp": "4.1.3",
"@tarojs/react": "4.1.3",
"@tarojs/router": "4.1.3",
"@tarojs/runtime": "4.1.3",
"@tarojs/shared": "4.1.3",
"@tarojs/taro": "4.1.3",
"@tarojs/taro-h5": "4.1.3",
"babel-plugin-import": "^1.13.8",
"lodash.isequal": "^4.5.0",
"lodash.kebabcase": "^4.1.1",
Expand All @@ -92,10 +92,10 @@
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@tarojs/cli": "4.0.12",
"@tarojs/plugin-platform-harmony-ets": "4.0.12",
"@tarojs/vite-runner": "4.0.12",
"@tarojs/webpack5-runner": "4.0.12",
"@tarojs/cli": "4.1.3",
"@tarojs/plugin-platform-harmony-ets": "4.1.3",
"@tarojs/vite-runner": "4.1.3",
"@tarojs/webpack5-runner": "4.1.3",
"@types/react": "^18.2.57",
"@types/react-dom": "^18.2.19",
"@types/react-router-dom": "^5.3.3",
Expand All @@ -105,9 +105,9 @@
"@types/webpack-env": "^1.18.4",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"babel-preset-taro": "4.0.12",
"babel-preset-taro": "4.1.3",
"eslint": "^8.56.0",
"eslint-config-taro": "4.0.12",
"eslint-config-taro": "4.1.3",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
Expand Down
6,106 changes: 3,112 additions & 2,994 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion src/packages/imagepreview/demos/taro/demo2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const Demo2 = () => {
))}
</Cell>
<ImagePreview
autoPlay
images={images}
visible={showPreview}
defaultValue={init}
Expand Down
13 changes: 8 additions & 5 deletions src/packages/imagepreview/imagepreview.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
PreviewVideoOption,
TaroImagePreviewProps,
} from '@/types'
import { mergeProps } from '@/utils'

interface Store {
scale: number
Expand All @@ -46,7 +47,7 @@ const defaultProps = {
showMenuByLongpress: false,
onChange: () => {},
onClose: () => {},
} as TaroImagePreviewProps
} as unknown as TaroImagePreviewProps
export const ImagePreview: FunctionComponent<Partial<TaroImagePreviewProps>> = (
props
) => {
Expand All @@ -62,13 +63,15 @@ export const ImagePreview: FunctionComponent<Partial<TaroImagePreviewProps>> = (
pagination,
indicator,
autoPlay,
loop,
closeOnContentClick,
closeIcon,
closeIconPosition,
showMenuByLongpress,
onClose,
onChange,
} = { ...defaultProps, ...props }
...rest
} = mergeProps(defaultProps, props)
const classPrefix = 'nut-imagepreview'
const ref = useRef<HTMLDivElement | null>(null)
const [innerNo, setInnerNo] = usePropsValue<number>({
Expand Down Expand Up @@ -223,17 +226,17 @@ export const ImagePreview: FunctionComponent<Partial<TaroImagePreviewProps>> = (
onTouchCancel={onTouchEnd}
>
<Swiper
autoPlay={autoPlay}
autoplay={autoPlay}
loop={loop}
direction="horizontal"
className={`${classPrefix}-swiper`}
loop
height="100%"
style={{
display: showPop ? 'block' : 'none',
'--nutui-indicator-color': indicatorColor,
width: '100%',
height: '100%',
}}
direction="horizontal"
onChange={(e) => slideChangeEnd(e.detail.current)}
defaultValue={innerNo && (innerNo > maxNo ? maxNo - 1 : innerNo - 1)}
indicator={indicator}
Expand Down
12 changes: 6 additions & 6 deletions src/packages/imagepreview/imagepreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
PreviewVideoOption,
WebImagePreviewProps,
} from '@/types'
import { mergeProps } from '@/utils'

interface Store {
scale: number
Expand All @@ -42,7 +43,7 @@ const defaultProps = {
closeIconPosition: 'top-right',
onChange: () => {},
onClose: () => {},
} as WebImagePreviewProps
} as unknown as WebImagePreviewProps
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

类型断言可能隐藏类型问题

使用 as unknown as WebImagePreviewProps 双重类型断言会绕过 TypeScript 的类型检查。建议检查 defaultPropsWebImagePreviewProps 接口的实际类型不匹配问题,并修复根本原因而不是使用类型断言。


🏁 Script executed:

#!/bin/bash
# 描述:检查 defaultProps 与 WebImagePreviewProps 的类型兼容性问题
# 预期:找到具体的类型不匹配问题

# 查找 WebImagePreviewProps 的定义
rg -A 10 "interface WebImagePreviewProps" src/types/

# 查找 defaultProps 中可能存在类型不匹配的属性
ast-grep --pattern 'const defaultProps = {
  $$$
}'

Length of output: 10141


🏁 Script executed:

#!/bin/bash
# 查看 imagepreview.tsx 中 defaultProps 定义及类型断言
echo "---- defaultProps in imagepreview.tsx ----"
rg -n -C3 "defaultProps" src/packages/imagepreview/imagepreview.tsx || true

echo "---- 类型断言位置 ----"
rg -n -C3 "as unknown as WebImagePreviewProps" src/packages/imagepreview/imagepreview.tsx || true

# 查看 WebImagePreviewProps 与 props 定义
echo "---- WebImagePreviewProps 定义 (h5.ts) ----"
rg -n -A5 "interface WebImagePreviewProps" src/types/spec/imagepreview/h5.ts || true

echo "---- props 定义 (spec 文件夹) ----"
rg -n -A5 "interface props" src/types/spec/imagepreview || true

Length of output: 1277


🏁 Script executed:

#!/bin/bash
# 列出 spec/imagepreview 目录,确认文件结构
echo "---- src/types/spec/imagepreview 目录 ----"
ls -R src/types/spec/imagepreview

# 打印 h5.ts 前 30 行,查看 props 来源
echo "---- h5.ts 文件头部 ----"
sed -n '1,30p' src/types/spec/imagepreview/h5.ts

# 全局搜索 interface props 定义,定位 props 接口所在文件
echo "---- 全局查找 interface props ----"
rg -n "interface props" -g "*.ts" .

Length of output: 622


强化类型定义:移除双重断言 as unknown as WebImagePreviewProps

双重断言会完全绕过编译时检查,建议改为让类型系统正确推导或校验:

  • defaultProps 上显式声明类型
    const defaultProps: Partial<WebImagePreviewProps> = {
      ...ComponentDefaults,
      images: [],
      videos: [],
    }
  • 修改或重载 mergeProps 签名,使其返回准确类型
    function mergeProps<T>(defaults: Partial<T>, props: T): T {  }
    // 调用时
    const merged = mergeProps<WebImagePreviewProps>(defaultProps, props)
  • 最终结果直接满足 WebImagePreviewProps,无需任何 as unknown as

待修改位置:

  • src/packages/imagepreview/imagepreview.tsx:46
🤖 Prompt for AI Agents
In src/packages/imagepreview/imagepreview.tsx at line 46, remove the double
assertion 'as unknown as WebImagePreviewProps' which bypasses type checking.
Instead, explicitly type the defaultProps as Partial<WebImagePreviewProps> and
adjust the mergeProps function signature to return the correct type, such as
mergeProps<T>(defaults: Partial<T>, props: T): T. Then call mergeProps with the
WebImagePreviewProps type parameter so the merged result naturally matches
WebImagePreviewProps without needing any type assertions.

export const ImagePreview: FunctionComponent<Partial<WebImagePreviewProps>> = (
props
) => {
Expand All @@ -63,7 +64,8 @@ export const ImagePreview: FunctionComponent<Partial<WebImagePreviewProps>> = (
closeIconPosition,
onClose,
onChange,
} = { ...defaultProps, ...props }
...rest
} = mergeProps(defaultProps, props)
const classPrefix = 'nut-imagepreview'
const ref = useRef<HTMLDivElement | null>(null)
const [innerNo, setInnerNo] = usePropsValue<number>({
Expand Down Expand Up @@ -218,17 +220,15 @@ export const ImagePreview: FunctionComponent<Partial<WebImagePreviewProps>> = (
>
{showPop && (
<Swiper
autoPlay={!!duration}
duration={duration}
autoplay={!!duration}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

需要验证 Swiper 组件的 autoplay 属性要求

属性名称从 autoPlay 规范化为 autoplay 是好的,但值从 duration 改为 !!duration 可能会影响功能:

  1. 现在只传递布尔值指示是否启用自动播放
  2. duration 变量(第208行计算)不再传递给 Swiper 组件
  3. 如果 Swiper 组件需要具体的播放间隔时间,这个变更可能导致功能丢失

Also applies to: 231-231


🏁 Script executed:

#!/bin/bash
# 描述:检查 Swiper 组件对 autoplay 和 duration 属性的要求
# 预期:确认 Swiper 组件的正确 API 用法

# 查找 Swiper 组件的类型定义和实现
rg -A 5 -B 5 "autoplay" src/packages/swiper/
rg -A 5 -B 5 "duration" src/packages/swiper/

# 查找 Swiper 组件的 props 接口定义
ast-grep --pattern 'interface $_ {
  $$$
  autoplay$$$
  $$$
}'

Length of output: 21029


确认 Swiper autoplay 参数类型及默认行为

经验证,Swiper 组件的 autoplay 支持数字/字符串来指定轮播间隔(0 表示不自动轮播),也可传布尔值仅作开关,但布尔值不会被当作间隔使用——此时会退回到默认的 duration = 3000ms。当前将 autoplay={!!duration} 改为布尔值后,会丢失自定义的 duration 值,始终以 3000ms 间隔轮播。

请在以下位置恢复数值传参以保持原有功能:

• 文件:src/packages/imagepreview/imagepreview.tsx
行 223:将

-   autoplay={!!duration}
+   autoplay={duration}           // 传入数值,或改为 autoplay={duration || 0}

• 如有移除对 Swiper 的 duration 属性传递,也需补回:

  <Swiper
     
    autoplay={duration}
+     duration={duration}
    {...rest}
  />
🤖 Prompt for AI Agents
In src/packages/imagepreview/imagepreview.tsx at line 223, the Swiper
component's autoplay prop is incorrectly set as a boolean using !!duration,
which causes it to lose the custom duration value and fallback to the default
3000ms interval. To fix this, change autoplay to pass the numeric duration value
directly (autoplay={duration}) and ensure the duration prop is also passed to
Swiper if it was removed, so the custom interval is preserved.

className={`${classPrefix}-swiper`}
loop
style={{
'--nutui-indicator-color': indicatorColor,
}}
direction="horizontal"
onChange={(page) => slideChangeEnd(page)}
defaultValue={innerNo > maxNo ? maxNo - 1 : innerNo - 1}
indicator={indicator}
{...rest}
>
{[
...videos.map((item) => ({ type: 'video', data: item })),
Expand Down
4 changes: 1 addition & 3 deletions src/packages/swiper/index.taro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { TaroSwiperProps } from '@/types'

export type { TaroSwiperProps as SwiperProps } from '@/types'
type CompoundedComponent = React.ForwardRefExoticComponent<
Partial<TaroSwiperProps> &
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> &
React.RefAttributes<any>
Partial<TaroSwiperProps> & React.RefAttributes<any>
> & {
Item: typeof SwiperItem
}
Expand Down
17 changes: 16 additions & 1 deletion src/packages/swiper/swiper.taro.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { Children, useImperativeHandle, useMemo, useState } from 'react'
import React, {
Children,
useImperativeHandle,
useMemo,
useState,
useEffect,
} from 'react'
import {
Swiper as TaroSwiper,
SwiperItem as TSwiperItem,
Expand Down Expand Up @@ -47,6 +53,7 @@ export const Swiper = React.forwardRef(
...defaultProps,
...props,
}

const [innerValue, setInnerValue] = useState(current || defaultValue)
const childrenCount = useMemo(() => {
let c = 0
Expand All @@ -55,6 +62,11 @@ export const Swiper = React.forwardRef(
})
return c
}, [children])

useEffect(() => {
setInnerValue(current || defaultValue)
}, [defaultValue, current])

const renderIndicator = () => {
if (React.isValidElement(indicator)) return indicator
if (indicator || indicatorDots) {
Expand All @@ -76,11 +88,13 @@ export const Swiper = React.forwardRef(
}
return null
}

const handleOnChange: CommonEventFunction<
TSwiperProps.onChangeEventDetail
> = (value) => {
setInnerValue(value.detail.current)
}

useImperativeHandle(ref, () => ({
to: (value: number) => {
setInnerValue(value)
Expand All @@ -104,6 +118,7 @@ export const Swiper = React.forwardRef(
}
},
}))

return (
<View
className={classNames(classPrefix, className)}
Expand Down
4 changes: 3 additions & 1 deletion src/types/spec/imagepreview/h5.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { BaseImagePreview } from './base'
import { WebSwiperProps } from '@/types'

export interface WebImagePreviewProps extends BaseImagePreview {}
type props = BaseImagePreview & Omit<WebSwiperProps, 'autoPlay' | 'indicator'>
export interface WebImagePreviewProps extends Omit<props, 'autoplay'> {}
6 changes: 4 additions & 2 deletions src/types/spec/imagepreview/taro.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { BaseImagePreview } from './base'
import { TaroSwiperProps } from '@/types'

export interface TaroImagePreviewProps
extends Omit<BaseImagePreview, 'autoPlay'> {
type props = Omit<BaseImagePreview, 'autoPlay'> &
Omit<TaroSwiperProps, 'autoPlay' | 'autoplay' | 'indicator'>
export interface TaroImagePreviewProps extends props {
autoPlay: boolean
showMenuByLongpress: boolean
}
Loading