Skip to content
Closed
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nutui/nutui-react-taro",
"version": "3.0.19-cpp.22",
"version": "3.0.19-cpp.22-7",
"style": "dist/style.css",
"main": "dist/nutui.react.umd.js",
"module": "dist/es/packages/nutui.react.build.js",
Expand Down
33 changes: 32 additions & 1 deletion src/packages/calendar/calendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,20 @@
font-weight: $calendar-day-font-weight;
margin-bottom: 4px;

// 部分 Taro 机型下子节点不会稳定继承父级 color,颜色直接落到最内层文案。
&-day,
&-info-curr {
color: $color-title;
}

&:nth-child(7n + 0),
&:nth-child(7n + 1) {
color: $color-primary;

.nut-calendar-day-day,
.nut-calendar-day-info-curr {
color: $color-primary;
}
}

&-info,
Expand Down Expand Up @@ -170,14 +181,24 @@
border-bottom-right-radius: $calendar-day-active-border-radius;
}

.nut-calendar-day-info {
&.nut-calendar-day-active .nut-calendar-day-info {
color: $color-primary-text;
}

&.nut-calendar-day-active .nut-calendar-day-day,
&.nut-calendar-day-active .nut-calendar-day-info-curr {
color: $color-primary-text;
}
}

&-disabled {
color: $calendar-disable-color !important;

&.nut-calendar-day-disabled .nut-calendar-day-day,
&.nut-calendar-day-disabled .nut-calendar-day-info-curr {
color: $calendar-disable-color;
}

.nut-calendar-day-info-curr {
display: none;
}
Expand All @@ -187,10 +208,20 @@
background-color: $calendar-choose-background-color;
color: $calendar-choose-color;

&.nut-calendar-day-choose .nut-calendar-day-day,
&.nut-calendar-day-choose .nut-calendar-day-info-curr {
color: $calendar-choose-color;
}

&-disabled {
background-color: $calendar-choose-disable-background-color;
color: $calendar-disable-color !important;

&.nut-calendar-day-choose-disabled .nut-calendar-day-day,
&.nut-calendar-day-choose-disabled .nut-calendar-day-info-curr {
color: $calendar-disable-color;
}

.nut-calendar-day-info-curr {
display: none;
}
Expand Down
19 changes: 19 additions & 0 deletions src/packages/calendarcard/calendarcard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
}
}

&-icon {
width: 18px;
height: 18px;
display: block;
}

&-days {
display: flex;
flex-direction: row;
Expand All @@ -49,6 +55,13 @@
margin-bottom: 4px;
text-align: center;

// 部分 Taro 机型下子节点不会稳定继承父级 color,颜色直接落到最内层文案。
&-top,
&-inner,
&-bottom {
color: $color-title;
}

&.header {
cursor: auto;
}
Expand All @@ -66,6 +79,12 @@

&.weekend {
color: $calendar-choose-color;

.nut-calendarcard-day-top,
.nut-calendarcard-day-inner,
.nut-calendarcard-day-bottom {
color: $calendar-choose-color;
}
}

&.active {
Expand Down
52 changes: 20 additions & 32 deletions src/packages/calendarcard/icon.taro.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,32 @@
import React, { FC } from 'react'
import { Image, View } from '@tarojs/components'

let left =
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij48cGF0aCBkPSJNNi42MDUgOS40OWEuNzcxLjc3MSAwIDAgMSAwLS45OGwzLjYtNC4zNzJhLjc3MS43NzEgMCAwIDEgMS4xOS45ODFMOC4yIDlsMy4xOTcgMy44ODFhLjc3MS43NzEgMCAxIDEtMS4xOTEuOThsLTMuNi00LjM3eiIvPjwvc3ZnPg=='

let right =
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij48cGF0aCBkPSJNMTEuMzk2IDkuNDlhLjc3MS43NzEgMCAwIDAgMC0uOThsLTMuNi00LjM3MmEuNzcxLjc3MSAwIDAgMC0xLjE5MS45ODFMOS44IDlsLTMuMTk2IDMuODgxYS43NzEuNzcxIDAgMCAwIDEuMTkuOThsMy42LTQuMzd6Ii8+PC9zdmc+'

let doubleLeft =
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij48cGF0aCBkPSJNMTMuODUzIDQuMDI2YS43NzEuNzcxIDAgMCAxIC4xMiAxLjA4NUwxMC44NjQgOWwzLjExIDMuODg5YS43NzEuNzcxIDAgMSAxLTEuMjA0Ljk2M2wtMy40OTgtNC4zN2EuNzcxLjc3MSAwIDAgMSAwLS45NjRsMy40OTctNC4zNzFhLjc3MS43NzEgMCAwIDEgMS4wODQtLjEyem0tNS4yNDUgMGEuNzcxLjc3MSAwIDAgMSAuMTIgMS4wODVMNS42MTcgOWwzLjExMSAzLjg4OWEuNzcxLjc3MSAwIDAgMS0xLjIwNS45NjNsLTMuNDk3LTQuMzdhLjc3MS43NzEgMCAwIDEgMC0uOTY0bDMuNDk3LTQuMzcxYS43NzEuNzcxIDAgMCAxIDEuMDg1LS4xMnoiLz48L3N2Zz4='

let doubleRight =
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij48cGF0aCBkPSJNNC4xNDcgMTMuOTc0YS43NzEuNzcxIDAgMCAxLS4xMi0xLjA4NUw3LjEzNiA5IDQuMDI4IDUuMTFhLjc3MS43NzEgMCAxIDEgMS4yMDQtLjk2M2wzLjQ5NyA0LjM3MWEuNzcxLjc3MSAwIDAgMSAwIC45NjRsLTMuNDk3IDQuMzcxYS43NzEuNzcxIDAgMCAxLTEuMDg0LjEyem01LjI0NSAwYS43NzEuNzcxIDAgMCAxLS4xMi0xLjA4NUwxMi4zODMgOSA5LjI3MiA1LjExYS43NzEuNzcxIDAgMSAxIDEuMjA1LS45NjNsMy40OTcgNC4zNzFhLjc3MS43NzEgMCAwIDEgMCAuOTY0bC0zLjQ5NyA0LjM3MWEuNzcxLjc3MSAwIDAgMS0xLjA4NS4xMnoiLz48L3N2Zz4='
import { Image } from '@tarojs/components'

interface IconProps {
url: string
}

if (process.env.TARO_ENV === 'jdharmony_cpp') {
left =
'https://img11.360buyimg.com/imagetools/jfs/t1/187031/35/51586/196/6731c464F9b9f8f00/5506e32bf15e29dc.png'
right =
'https://img12.360buyimg.com/imagetools/jfs/t1/181006/25/51824/185/6731c452F9e252322/7493147dd6b4a88d.png'
doubleLeft =
'https://img13.360buyimg.com/imagetools/jfs/t1/221244/29/45876/259/6731c6bdF515df0a2/624e8eee3c8494a2.png'
doubleRight =
'https://img11.360buyimg.com/imagetools/jfs/t1/238382/25/24000/235/6731c6bdF0153286c/4a57e60b6e889af3.png'
}
const left =
'https://storage.360buyimg.com/imgtools/6437a2b149-bb511b40-4942-11f1-b1e1-7158b9aae0a9.svg'
const right =
'https://storage.360buyimg.com/imgtools/8aa060341d-baf78990-4942-11f1-9368-cd56fc5fadc6.svg'
const doubleLeft =
'https://storage.360buyimg.com/imgtools/b5e798f4ed-bbe8c760-4942-11f1-b1e1-7158b9aae0a9.svg'
const doubleRight =
'https://storage.360buyimg.com/imgtools/d6979fa16d-bbb4bf10-4942-11f1-9368-cd56fc5fadc6.svg'
Comment on lines +8 to +15

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

图标 URL 强依赖外部 CDN,在小程序环境中需确认域名白名单并评估可用性风险。

四个图标地址均托管在 storage.360buyimg.com,存在以下问题:

  1. 小程序域名校验:微信/JD/支付宝等小程序要求在后台将 storage.360buyimg.com 加入合法域名(downloadFilerequest 白名单),否则图片将静默加载失败,导致图标区域空白。
  2. 无离线/降级保障:当前实现没有 onError 回调,网络不可用时无任何 fallback 渲染。
  3. CDN 稳定性依赖:原先内联 base64 无此风险,切换为远程 URL 引入了外部可用性依赖。

建议确认以下几点:

  • 小程序 app.jsonnetworkTimeout / 合法域名是否已覆盖该 CDN;
  • 是否需要提供 onError 降级(例如渲染文字箭头 ‹ › ‹‹ ››);
  • 或将 SVG 内容随包分发(require 本地文件)以彻底规避网络依赖。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/packages/calendarcard/icon.taro.tsx` around lines 8 - 15, The four icon
URL constants (left, right, doubleLeft, doubleRight) rely on an external CDN
which can fail in mini-programs; update icon.taro.tsx to remove or mitigate the
network dependency by either (A) replacing these remote URLs with local SVG
assets imported/required into the bundle (use the same constant names
left/right/doubleLeft/doubleRight pointing to local file imports), or (B) add
runtime fallbacks: keep the URLs but ensure the component that renders them
attaches an onError handler to swap to a local inline SVG or simple text arrows
("‹ › ‹‹ ››") when loading fails; choose one approach and apply consistently for
left, right, doubleLeft and doubleRight.


const Icon: FC<IconProps> = ({ url }) => {
const style = {
background: `url('${url}') no-repeat center`,
backgroundSize: '100% 100%',
width: 18,
height: 18,
}
if (process.env.TARO_ENV === 'jdharmony_cpp') {
return <Image src={url} style={style} />
const iconStyle = {
color: 'var(--nutui-color-text)',
}
return <View style={style} />
return (
<Image
className="nut-calendarcard-icon"
svg
src={url}
mode="aspectFit"
style={iconStyle}
/>
)
Comment on lines +18 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

CSS color 属性对 <Image> 渲染的外部 SVG 无效,图标颜色无法跟随主题变化。

const iconStyle = {
  color: 'var(--nutui-color-text)',  // ← 对 <Image> 渲染的 SVG 不生效
}

<Image> 将 SVG 作为位图渲染,color / fill 等 CSS 属性不会透传到 SVG 内部元素,除非 SVG 文件本身使用 fill="currentColor"。若 CDN 上的 SVG 不使用 currentColor,该样式实际上无任何效果,图标颜色将固定为 SVG 文件中硬编码的值,无法响应主题切换。

建议:

  • 确认 CDN 上的四个 SVG 文件是否已将 fill/stroke 设置为 currentColor
  • 若不支持,可考虑将 SVG 以内联方式嵌入(<svg> 标签),或通过 CSS filter 近似颜色变换。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/packages/calendarcard/icon.taro.tsx` around lines 18 - 29, The
Image-based icon rendering in icon.taro.tsx uses iconStyle.color but external
SVGs rendered via the <Image> component won't inherit CSS color unless the SVGs
use fill/stroke="currentColor"; update the four CDN SVGs to set
fill/stroke="currentColor" so they follow var(--nutui-color-text), or change the
rendering in this module (the Image usage that passes url and iconStyle) to
inline SVGs (embed the SVG markup or fetch and inject the SVG into the
component) so CSS can target their fills, or as a fallback apply a CSS filter
colorization approach; locate the Image usage and the iconStyle constant to
implement one of these fixes.

}

export const ArrowLeft: FC = () => <Icon url={left} />
Expand Down
4 changes: 4 additions & 0 deletions src/packages/calendarcard/icons/doubleLeft.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/packages/calendarcard/icons/doubleRight.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/packages/calendarcard/icons/singleLeft.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/packages/calendarcard/icons/singleRight.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 23 additions & 4 deletions src/packages/price/price.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { ComponentDefaults } from '@/utils/typings'
import { useRtl } from '@/packages/configprovider/index.taro'
import { TaroPriceProps, PriceColorEnum } from '@/types'
import { harmony } from '@/utils/taro/platform'
import {
shouldRenderPriceAsRaw,
hasNonPriceChars,
} from '@/utils/should-render-price-raw'

const defaultProps = {
...ComponentDefaults,
Expand All @@ -16,6 +20,7 @@ const defaultProps = {
position: 'before',
size: 'normal',
line: false,
raw: false,
} as TaroPriceProps
export const Price: FunctionComponent<Partial<TaroPriceProps>> = (props) => {
const {
Expand All @@ -27,6 +32,7 @@ export const Price: FunctionComponent<Partial<TaroPriceProps>> = (props) => {
position,
size,
line,
raw,
className,
style,
} = {
Expand All @@ -38,9 +44,20 @@ export const Price: FunctionComponent<Partial<TaroPriceProps>> = (props) => {

const rtl = useRtl()

const isRenderPriceRaw = useMemo(
() =>
(typeof originalPrice === 'string' &&
shouldRenderPriceAsRaw(originalPrice)) ||
(raw &&
typeof originalPrice === 'string' &&
hasNonPriceChars(originalPrice)),
[originalPrice, raw]
)

const price = useMemo(() => {
if (isRenderPriceRaw) return ''
return originalPrice.toString().replace(/[^\d.]/g, '')
}, [originalPrice])
}, [originalPrice, isRenderPriceRaw])

const isCustomPriceColor = useMemo(() => {
const specificPriceColor = Object.values(PriceColorEnum)
Expand Down Expand Up @@ -86,7 +103,7 @@ export const Price: FunctionComponent<Partial<TaroPriceProps>> = (props) => {
}

const formatDecimal = (decimalNum: any) => {
if (Number(decimalNum) === 0) {
if (Number(decimalNum) === 0 && !checkPoint(decimalNum)) {
decimalNum = 0
}

Expand Down Expand Up @@ -164,21 +181,23 @@ export const Price: FunctionComponent<Partial<TaroPriceProps>> = (props) => {
)
}

const renderRawContent = () => <>{originalPrice}</>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

renderRawContent 未应用 priceColorStyle,自定义颜色在 raw 模式下静默失效。

const renderRawContent = () => <>{originalPrice}</>

isCustomPriceColortrue 时(即 color 为非枚举的十六进制/RGB 等自定义值),priceColorStyle 包含 { color: customColor } 内联样式。该样式在 renderInner() 中被应用到各 Text 子节点,但外层容器只传入用户的 style 而非 priceColorStyle。在 raw 模式下,renderRawContent 渲染的内容没有任何颜色样式,导致自定义颜色完全丢失。

🛠️ 建议修复
- const renderRawContent = () => <>{originalPrice}</>
+ const renderRawContent = () => (
+   <Text style={priceColorStyle}>{originalPrice}</Text>
+ )

或将 priceColorStyle 合并至外层容器的 style

  <Text
    className={`${classPrefix} ${classPrefix}-${color} ${className}`}
-   style={style}
+   style={{ ...priceColorStyle, ...style }}
  >
    {isRenderPriceRaw ? renderRawContent() : renderInner()}
  </Text>

(View 分支同理)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/packages/price/price.taro.tsx` at line 184, renderRawContent currently
returns <> {originalPrice} </> without applying priceColorStyle, so custom
colors (isCustomPriceColor) are ignored; fix by merging priceColorStyle into the
outer container's style used by renderRawContent (or into the View/Text wrapper
that renderRawContent returns) so the inline style from priceColorStyle is
applied when rendering originalPrice; locate renderRawContent, priceColorStyle,
renderInner and ensure the same style-merge approach used in renderInner is
replicated for the raw-mode branch (also apply the same change to the View
branch if present).


return (
<>
{harmony() || process.env.TARO_ENV === 'dynamic' ? (
<Text
className={`${classPrefix} ${classPrefix}-${color} ${className}`}
style={style}
>
{renderInner()}
{isRenderPriceRaw ? renderRawContent() : renderInner()}
</Text>
) : (
<View
className={`${classPrefix} ${classPrefix}-${color} ${className}`}
style={style}
>
{renderInner()}
{isRenderPriceRaw ? renderRawContent() : renderInner()}
</View>
)}
</>
Expand Down
1 change: 1 addition & 0 deletions src/types/spec/price/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export interface BasePrice extends BaseProps {
position: string
size: PriceSize
line: boolean
raw: boolean
}
31 changes: 31 additions & 0 deletions src/utils/should-render-price-raw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const RE_NUM = /\d+(?:\.\d+)?/g

// 价格合法字符:数字、小数点、负号、大写货币代码(HK USD EUR 等)、常见货币符号 Unicode 区块(含全角 ¥)
const PRICE_CHARS = /^[\d.\-A-Z¥¥$€£₩₹₽₺₴₪฿\u20A0-\u20CF\uFE69\uFF04\uFFE5]*$/

function hasNoExtractablePrice(s: string) {
const t = s.trim()
if (!t) return true
if (!/\d/.test(t)) return true
if (t.replace(/[^\d.]/g, '') === '') return true
return false
}

export function shouldRenderPriceAsRaw(s: string) {
if (hasNoExtractablePrice(s)) {
return true
}
const t = s.trim()
const matches = Array.from(t.matchAll(RE_NUM))
if (matches.length < 2) return false
const a = matches[0]
const b = matches[1]
const i0 = a.index!
const i1 = b.index!
const between = t.slice(i0 + a[0].length, i1)
return !PRICE_CHARS.test(between)
}

export function hasNonPriceChars(s: string): boolean {
return !PRICE_CHARS.test(s)
}
Loading