uview-ultra 4.5.53 → 4.5.71

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.
@@ -25,13 +25,18 @@ $up-zoom-scale: scale(0.95);
25
25
  opacity: 0
26
26
  }
27
27
 
28
- .up-fade-zoom-enter,
28
+ .up-fade-enter-to {
29
+ opacity: 1;
30
+ }.up-fade-zoom-enter,
29
31
  .up-fade-zoom-leave-to {
30
32
  transform: $up-zoom-scale;
31
33
  opacity: 0;
32
34
  }
33
35
 
34
- .up-fade-zoom-enter-active,
36
+ .up-fade-zoom-enter-to {
37
+ transform: scale(1);
38
+ opacity: 1;
39
+ }.up-fade-zoom-enter-active,
35
40
  .up-fade-zoom-leave-active {
36
41
  transition-property: transform, opacity;
37
42
  }
@@ -53,25 +58,37 @@ $up-zoom-scale: scale(0.95);
53
58
  opacity: 0
54
59
  }
55
60
 
56
- .up-fade-down-enter,
61
+ .up-fade-up-enter-to {
62
+ transform: translateY(0);
63
+ opacity: 1;
64
+ }.up-fade-down-enter,
57
65
  .up-fade-down-leave-to {
58
66
  transform: translateY(-100%);
59
67
  opacity: 0
60
68
  }
61
69
 
62
- .up-fade-left-enter,
70
+ .up-fade-down-enter-to {
71
+ transform: translateY(0);
72
+ opacity: 1;
73
+ }.up-fade-left-enter,
63
74
  .up-fade-left-leave-to {
64
75
  transform: translateX(-100%);
65
76
  opacity: 0
66
77
  }
67
78
 
68
- .up-fade-right-enter,
79
+ .up-fade-left-enter-to {
80
+ transform: translateX(0);
81
+ opacity: 1;
82
+ }.up-fade-right-enter,
69
83
  .up-fade-right-leave-to {
70
84
  transform: translateX(100%);
71
85
  opacity: 0
72
86
  }
73
87
 
74
- .up-slide-down-enter-active,
88
+ .up-fade-right-enter-to {
89
+ transform: translateX(0);
90
+ opacity: 1;
91
+ }.up-slide-down-enter-active,
75
92
  .up-slide-down-leave-active,
76
93
  .up-slide-left-enter-active,
77
94
  .up-slide-left-leave-active,
@@ -87,22 +104,34 @@ $up-zoom-scale: scale(0.95);
87
104
  transform: translateY(100%)
88
105
  }
89
106
 
90
- .up-slide-down-enter,
107
+ .up-slide-up-enter-to {
108
+ transform: translateY(0);
109
+ opacity: 1;
110
+ }.up-slide-down-enter,
91
111
  .up-slide-down-leave-to {
92
112
  transform: translateY(-100%)
93
113
  }
94
114
 
95
- .up-slide-left-enter,
115
+ .up-slide-down-enter-to {
116
+ transform: translateY(0);
117
+ opacity: 1;
118
+ }.up-slide-left-enter,
96
119
  .up-slide-left-leave-to {
97
120
  transform: translateX(-100%)
98
121
  }
99
122
 
100
- .up-slide-right-enter,
123
+ .up-slide-left-enter-to {
124
+ transform: translateX(0);
125
+ opacity: 1;
126
+ }.up-slide-right-enter,
101
127
  .up-slide-right-leave-to {
102
128
  transform: translateX(100%)
103
129
  }
104
130
 
105
- .up-zoom-enter-active,
131
+ .up-slide-right-enter-to {
132
+ transform: translateX(0);
133
+ opacity: 1;
134
+ }.up-zoom-enter-active,
106
135
  .up-zoom-leave-active {
107
136
  transition-property: transform
108
137
  }
@@ -95,7 +95,8 @@ export const themeState = {
95
95
  vars: {}
96
96
  }
97
97
 
98
- const THEME_MODE_STORAGE_KEY = 'u-theme-mode'
98
+ const THEME_MODE_STORAGE_KEY = 'up-theme-mode'
99
+ const LEGACY_THEME_MODE_STORAGE_KEY = 'u-theme-mode'
99
100
  const THEME_MODE_SYSTEM = 'system'
100
101
  const THEME_MODE_MANUAL = ['light', 'dark']
101
102
  const LIGHT_THEME_TOKEN_FIELD_MAP = Object.freeze({
@@ -229,7 +230,16 @@ function getExplicitRuntimeColorValue(token, runtimeColorMap = {}) {
229
230
  function readThemePreferenceFromStorage() {
230
231
  try {
231
232
  if (typeof uni !== 'undefined' && typeof uni.getStorageSync === 'function') {
232
- const mode = uni.getStorageSync(THEME_MODE_STORAGE_KEY)
233
+ let mode = uni.getStorageSync(THEME_MODE_STORAGE_KEY)
234
+ // 旧 `u-theme-mode` 迁移(fork 时代 key)。
235
+ if (typeof mode !== 'string' || mode.length === 0) {
236
+ const legacy = uni.getStorageSync(LEGACY_THEME_MODE_STORAGE_KEY)
237
+ if (typeof legacy === 'string' && legacy.length > 0) {
238
+ mode = legacy
239
+ uni.setStorageSync(THEME_MODE_STORAGE_KEY, legacy)
240
+ uni.removeStorageSync(LEGACY_THEME_MODE_STORAGE_KEY)
241
+ }
242
+ }
233
243
  return normalizeThemePreference(mode)
234
244
  }
235
245
  } catch (e) {}
@@ -92,7 +92,11 @@ const EXTRA_LIGHT_VARS: UTSJSONObject = {
92
92
  // 于是没有底色 —— 而 App 端也不支持 `backdrop-filter`,两层一没,滚动时顶栏看起来
93
93
  // 跟全透明没区别(这正是「开了 frosted 却没效果」的原因)。
94
94
  '--up-navbar-glass-bg': 'rgba(255, 255, 255, 0.75)',
95
- '--up-navbar-glass-border': 'rgba(0, 0, 0, 0.08)'
95
+ '--up-navbar-glass-border': 'rgba(0, 0, 0, 0.08)',
96
+ // 底栏激活项的实心块底色:默认 #1c1c1f(原硬编码值)。
97
+ // 与主色无关——激活块要衬托彩色填充图标,跟主色联动反而会撞色。
98
+ // iOS 27 风格:浅灰选中块(应用方 2026-10-30 指定)
99
+ '--up-tabbar-active-bg': '#e9e9eb'
96
100
  }
97
101
 
98
102
  const EXTRA_DARK_VARS: UTSJSONObject = {
@@ -107,7 +111,10 @@ const EXTRA_DARK_VARS: UTSJSONObject = {
107
111
  '--up-calendar-month-mark-color': 'rgba(255, 255, 255, 0.04)',
108
112
  // 暗色下不透明度要更高:暗底上叠亮内容,72% 会糊成一片读不清。
109
113
  '--up-navbar-glass-bg': 'rgba(28, 28, 30, 0.78)',
110
- '--up-navbar-glass-border': 'rgba(255, 255, 255, 0.12)'
114
+ '--up-navbar-glass-border': 'rgba(255, 255, 255, 0.12)',
115
+ // 深色下保持 #3a3a3c:纯黑会融进深色底栏,激活块消失。
116
+ // 深色:比底栏底(#1c1c1e 系)微亮一档的选中块
117
+ '--up-tabbar-active-bg': '#2c2c2e'
111
118
  }
112
119
 
113
120
  // 主题色字段 -> CSS 变量 token(不含 --up-/--u- 前缀)
@@ -141,7 +148,9 @@ const FIELD_TOKENS: UTSJSONObject = {
141
148
  'disabledColor': 'disabled-color'
142
149
  }
143
150
 
144
- const THEME_MODE_STORAGE_KEY = 'u-theme-mode'
151
+ const THEME_MODE_STORAGE_KEY = 'up-theme-mode'
152
+ /** 旧存储 key(fork 时代 `u-` 前缀)。首次读取时迁移到 `up-theme-mode` 后即可弃用。 */
153
+ const LEGACY_THEME_MODE_STORAGE_KEY = 'u-theme-mode'
145
154
 
146
155
  // ---------------- 响应式状态 ----------------
147
156
 
@@ -178,11 +187,23 @@ function pickColor(colors: UTSJSONObject, field: string, fallback: string): stri
178
187
  return fallback
179
188
  }
180
189
 
181
- /** 读取 config.uts color 中显式配置的 token(支持 up-/u- 双前缀 key) */
182
- function configColorOverride(token: string): string {
190
+ /**
191
+ * 读取 config.uts color 中显式配置的 token(支持 up-/u- 双前缀 key)。
192
+ *
193
+ * 支持**按主题模式**覆盖:`up-primary-light` / `up-primary-dark` 后缀键优先于单值键
194
+ * `up-primary`——应用要「浅色主色换黑系、深色保持原主色」这类需求时不用写 JS。
195
+ */
196
+ function configColorOverride(token: string, isDark: boolean): string {
183
197
  const colorMap = config['color']
184
198
  if (!(colorMap instanceof UTSJSONObject)) return ''
185
199
  const conf = colorMap as UTSJSONObject
200
+ const modeSuffix = isDark ? 'dark' : 'light'
201
+ const modeUpKey = `up-${token}-${modeSuffix}`
202
+ const modeUKey = `u-${token}-${modeSuffix}`
203
+ const modeUpValue = conf[modeUpKey]
204
+ if (typeof modeUpValue === 'string' && (modeUpValue as string).length > 0) return modeUpValue as string
205
+ const modeUValue = conf[modeUKey]
206
+ if (typeof modeUValue === 'string' && (modeUValue as string).length > 0) return modeUValue as string
186
207
  const upKey = `up-${token}`
187
208
  const uKey = `u-${token}`
188
209
  const upValue = conf[upKey]
@@ -224,7 +245,9 @@ export function getThemeVars(mode: string | null = null): UTSJSONObject {
224
245
  UTSJSONObject.keys(FIELD_TOKENS).forEach((field: string) => {
225
246
  const token = FIELD_TOKENS[field] as string
226
247
  const fallback = pickColor(base, field, isDark ? '#1f1f1f' : '#303133')
227
- const override = configColorOverride(token)
248
+ // 优先级:运行时品牌色(设置页切换)> config 覆盖(编译期/模式后缀)> 主题表默认。
249
+ const runtimePrimary = token == "primary" ? upThemePrimaryColor.value : ""
250
+ const override = runtimePrimary.length > 0 ? runtimePrimary : configColorOverride(token, isDark)
228
251
  const value = override.length > 0 ? override : fallback
229
252
  vars[`--up-${token}`] = value
230
253
  vars[`--u-${token}`] = value
@@ -306,6 +329,43 @@ export function upThemeVar(varName: string, fallbackColor: string = ''): string
306
329
  return fallbackColor
307
330
  }
308
331
 
332
+ /**
333
+ * 读取主题色并**附加透明度**,返回 rgba() 字符串。
334
+ *
335
+ * 为什么需要它:`--up-primary` 是十六进制串,CSS 里没法直接给它加 alpha —— 业务侧
336
+ * 想要「主色 12% 的浅底」(图标底衬、选中态背景、发光阴影)时只能硬编码一个
337
+ * rgba 蓝,主色一换就不同步。这里在脚本侧把当前主色解析成 rgba,随主题与运行时
338
+ * 品牌色一起变化。
339
+ *
340
+ * 注意 `--up-primary-light` 不是这个用途:那是主题表里**独立的浅色档**(浅色
341
+ * #ecf5ff / 深色 #10243a),不随主色计算,不能当「主色 + alpha」用。
342
+ *
343
+ * @param varName 变量名(如 `--up-primary`)
344
+ * @param alpha 0-1 的透明度
345
+ * @param fallbackColor 变量取不到时的兜底十六进制色
346
+ */
347
+ export function upThemeColorAlpha(varName: string, alpha: number, fallbackColor: string = '#2979ff'): string {
348
+ const base = upThemeVar(varName, fallbackColor)
349
+ const rgb = parseHexColor(base)
350
+ if (rgb == null) return base
351
+ const clamped = alpha < 0 ? 0 : (alpha > 1 ? 1 : alpha)
352
+ const a = Math.round(clamped * 1000) / 1000
353
+ return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${a})`
354
+ }
355
+
356
+ /** `#RGB` / `#RRGGBB` → [r, g, b];其它格式(已是 rgba 等)返回 null。 */
357
+ function parseHexColor(value: string): number[] | null {
358
+ const match = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.exec(value.trim())
359
+ if (match == null) return null
360
+ let body = match[1]
361
+ if (body.length == 3) {
362
+ body = `${body[0]}${body[0]}${body[1]}${body[1]}${body[2]}${body[2]}`
363
+ }
364
+ const parsed = parseInt(body, 16)
365
+ if (isNaN(parsed)) return null
366
+ return [(parsed >> 16) & 0xff, (parsed >> 8) & 0xff, parsed & 0xff]
367
+ }
368
+
309
369
  /** 当前是否暗黑(响应式)。 */
310
370
  export function upThemeIsDark(): boolean {
311
371
  return upThemeMode.value === 'dark'
@@ -383,6 +443,55 @@ function applyTheme(mode: string): void {
383
443
  } catch (e) {}
384
444
  }
385
445
 
446
+ // ---------------- 运行时主色(品牌色)切换 ----------------
447
+
448
+ const THEME_PRIMARY_STORAGE_KEY = "up-theme-primary"
449
+ /** 旧 key:4.5.66 首版误用了 `u-` 前缀,读取时迁移。 */
450
+ const LEGACY_THEME_PRIMARY_STORAGE_KEY = "u-theme-primary"
451
+
452
+ /** 当前生效的主色(运行时覆盖值;空串表示未覆盖、走 config/默认)。 */
453
+ export const upThemePrimaryColor = ref<string>("")
454
+
455
+ /**
456
+ * 设置运行时主色并立即生效 + 持久化。传空串清除覆盖、回到 config 默认。
457
+ *
458
+ * 与 `config.color['up-primary-light']` 的区别:config 是**编译期**配置
459
+ * (发布不同品牌包时改它),这里是**运行时**用户可切换的品牌色(设置页取色器)。
460
+ * 二者优先级:运行时值 > config 覆盖 > 主题表默认。
461
+ *
462
+ * 只覆盖主色单值(浅/深共用)——浅色用黑系、深色保持默认蓝这类按模式区分的
463
+ * 场景仍走 config 的 `up-primary-light/-dark` 后缀键。
464
+ */
465
+ export function setThemePrimaryColor(color: string): void {
466
+ const normalized = normalizeHexColor(color)
467
+ upThemePrimaryColor.value = normalized
468
+ try {
469
+ if (normalized.length > 0) {
470
+ uni.setStorageSync(THEME_PRIMARY_STORAGE_KEY, normalized)
471
+ } else {
472
+ uni.removeStorageSync(THEME_PRIMARY_STORAGE_KEY)
473
+ }
474
+ } catch (e) {}
475
+ applyTheme(upThemeMode.value)
476
+ }
477
+
478
+ /** 读取当前运行时主色覆盖值(未覆盖返回空串)。 */
479
+ export function getThemePrimaryColor(): string {
480
+ return upThemePrimaryColor.value
481
+ }
482
+
483
+ function normalizeHexColor(value: string): string {
484
+ const color = value.trim()
485
+ if (color.length == 0) return ""
486
+ const match = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.exec(color)
487
+ if (match == null) return ""
488
+ const body = match[1]
489
+ if (body.length == 3) {
490
+ return `#${body[0]}${body[0]}${body[1]}${body[1]}${body[2]}${body[2]}`.toLowerCase()
491
+ }
492
+ return `#${body}`.toLowerCase()
493
+ }
494
+
386
495
  /** 设置并持久化主题偏好('light' | 'dark')。 */
387
496
  export function setTheme(mode: string): void {
388
497
  const preference = normalizeThemeMode(mode)
@@ -412,9 +521,35 @@ export function setThemePreference(mode: string): void {
412
521
  * 在 App.uvue onLaunch 或 main.uts 中调用一次。
413
522
  */
414
523
  export function initThemeSystem(): void {
524
+ // 运行时主色覆盖值(设置页取色器写入)——必须在首次 getThemeVars 前加载,
525
+ // 否则首屏会用默认色、切主题时才跳变。
526
+ try {
527
+ let storedPrimary = uni.getStorageSync(THEME_PRIMARY_STORAGE_KEY)
528
+ // 旧 `u-theme-primary` 迁移:读到了就搬到新 key 并清掉旧的。
529
+ if (typeof storedPrimary !== "string" || (storedPrimary as string).length == 0) {
530
+ const legacyPrimary = uni.getStorageSync(LEGACY_THEME_PRIMARY_STORAGE_KEY)
531
+ if (typeof legacyPrimary === "string" && (legacyPrimary as string).length > 0) {
532
+ storedPrimary = legacyPrimary
533
+ uni.setStorageSync(THEME_PRIMARY_STORAGE_KEY, legacyPrimary as string)
534
+ uni.removeStorageSync(LEGACY_THEME_PRIMARY_STORAGE_KEY)
535
+ }
536
+ }
537
+ if (typeof storedPrimary === "string" && (storedPrimary as string).length > 0) {
538
+ upThemePrimaryColor.value = normalizeHexColor(storedPrimary as string)
539
+ }
540
+ } catch (e) {}
415
541
  let stored = 'system'
416
542
  try {
417
- const value = uni.getStorageSync(THEME_MODE_STORAGE_KEY)
543
+ let value = uni.getStorageSync(THEME_MODE_STORAGE_KEY)
544
+ // 旧 `u-theme-mode` 迁移(fork 时代的 key)。
545
+ if (typeof value !== 'string' || (value as string).length == 0) {
546
+ const legacyMode = uni.getStorageSync(LEGACY_THEME_MODE_STORAGE_KEY)
547
+ if (typeof legacyMode === 'string' && (legacyMode as string).length > 0) {
548
+ value = legacyMode
549
+ uni.setStorageSync(THEME_MODE_STORAGE_KEY, legacyMode as string)
550
+ uni.removeStorageSync(LEGACY_THEME_MODE_STORAGE_KEY)
551
+ }
552
+ }
418
553
  if (typeof value === 'string') stored = normalizeThemePreference(value as string)
419
554
  } catch (e) {}
420
555
  upThemePreference.value = stored
@@ -425,10 +560,19 @@ export function initThemeSystem(): void {
425
560
  }
426
561
  if (!hasRegisterThemeListener) {
427
562
  hasRegisterThemeListener = true
563
+ /*
564
+ * `uni.onThemeChange` 只有 web / 小程序端有,uni-app x 的 App 端没有这个 API ——
565
+ * 直接调用会让 App 启动即抛 `TypeError: uni.onThemeChange is not a function`。
566
+ *
567
+ * App 端不注册监听:系统主题在每次启动时由 `getSystemTheme()` 重新读一次,
568
+ * 运行期切系统深浅色需要用户重新进设置页触发(或下次冷启动生效)。
569
+ */
570
+ // #ifdef WEB
428
571
  uni.onThemeChange((res) => {
429
572
  if (upThemePreference.value === 'system') {
430
573
  applyTheme(res.theme)
431
574
  }
432
575
  })
576
+ // #endif
433
577
  }
434
578
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "uview-ultra",
3
3
  "name": "uview-ultra",
4
4
  "displayName": "零云®UviewUltra v4重磅发布,大满贯全面兼容uniapp-x、uniapp鸿蒙组件库。",
5
- "version": "4.5.53",
5
+ "version": "4.5.71",
6
6
  "description": "uview-ultra全面兼容uni-app/uni-app-x/鸿蒙/nvue,全面的组件和便捷的工具会让您信手拈来,如鱼得水",
7
7
  "keywords": [
8
8
  "uview",