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.
@@ -72,6 +72,7 @@ import { computed } from 'vue'
72
72
  import { addStyle, addUnit, getPx, bem } from '../../libs/function/index'
73
73
  import { throttle } from '../../libs/function/throttle'
74
74
  import config from '../../libs/config/config'
75
+ import { upThemeVar } from '../../libs/theme/theme.uts'
75
76
  const props = defineProps({
76
77
  // 自定义样式
77
78
  customStyle: {
@@ -255,7 +256,7 @@ const loadingColor = computed(() => {
255
256
  // 如果有设置color值,则用color值,否则使用type主题颜色
256
257
  return props.color != ''
257
258
  ? props.color
258
- : config.getString(`color.up-${props.type}`) as string;
259
+ : upThemeVar(`--up-${props.type}`, config.getString(`color.up-${props.type}`) as string);
259
260
  }
260
261
  if (props.type == "info") {
261
262
  return "#c9c9c9";
@@ -270,7 +271,7 @@ const textColor = computed((): string => {
270
271
  if (props.color != '') return props.color
271
272
  if (props.type == 'info') return '#000000'
272
273
  if (props.type == 'warning') return config.getString('color.up-error') ?? '#fa3534'
273
- const color = config.getString(`color.up-${props.type}`) ?? ''
274
+ const color = upThemeVar(`--up-${props.type}`, config.getString(`color.up-${props.type}`) ?? '')
274
275
  return color == '' ? '#ffffff' : color
275
276
  })
276
277
 
@@ -402,10 +403,10 @@ $up-button-circle-border-top-right-radius: 100px !default;
402
403
  $up-button-circle-border-top-left-radius: 100px !default;
403
404
  $up-button-circle-border-bottom-left-radius: 100px !default;
404
405
  $up-button-circle-border-bottom-right-radius: 100px !default;
405
- $up-button-square-border-top-right-radius: 3px !default;
406
- $up-button-square-border-top-left-radius: 3px !default;
407
- $up-button-square-border-bottom-left-radius: 3px !default;
408
- $up-button-square-border-bottom-right-radius: 3px !default;
406
+ $up-button-square-border-top-right-radius: 10px !default;
407
+ $up-button-square-border-top-left-radius: 10px !default;
408
+ $up-button-square-border-bottom-left-radius: 10px !default;
409
+ $up-button-square-border-bottom-right-radius: 10px !default;
409
410
  $up-button-icon-min-width: 4px !default;
410
411
  $up-button-plain-background-color: transparent !default;
411
412
  $up-button-hairline-border-width: 0.5px !default;
@@ -500,8 +501,8 @@ $up-button-plain-up-button-warning-color:$up-error !default;;
500
501
  /* #ifndef APP */
501
502
  color: $up-button-primary-color;
502
503
  /* #endif */
503
- background-color: $up-button-primary-background-color !important;
504
- border-color: $up-button-primary-border-color !important;
504
+ background-color: var(--up-primary, $up-button-primary-background-color) !important;
505
+ border-color: var(--up-primary, $up-button-primary-border-color) !important;
505
506
  border-width: $up-button-primary-border-width !important;
506
507
  border-style: $up-button-primary-border-style !important;
507
508
  }
@@ -315,10 +315,10 @@ $up-button-circle-border-top-right-radius: 100px !default;
315
315
  $up-button-circle-border-top-left-radius: 100px !default;
316
316
  $up-button-circle-border-bottom-left-radius: 100px !default;
317
317
  $up-button-circle-border-bottom-right-radius: 100px !default;
318
- $up-button-square-border-top-right-radius: 3px !default;
319
- $up-button-square-border-top-left-radius: 3px !default;
320
- $up-button-square-border-bottom-left-radius: 3px !default;
321
- $up-button-square-border-bottom-right-radius: 3px !default;
318
+ $up-button-square-border-top-right-radius: 10px !default;
319
+ $up-button-square-border-top-left-radius: 10px !default;
320
+ $up-button-square-border-bottom-left-radius: 10px !default;
321
+ $up-button-square-border-bottom-right-radius: 10px !default;
322
322
  $up-button-icon-min-width: 1em !default;
323
323
  $up-button-plain-background-color: transparent !default;
324
324
  $up-button-hairline-border-width: 0.5px !default;
@@ -33,6 +33,12 @@ export const propsCell = defineMixin({
33
33
  type: Boolean,
34
34
  default: () => defProps.cell.border
35
35
  },
36
+ // 分隔线左侧偏移(px 或带单位字符串)。iOS 标准是线条不顶到图标,
37
+ // 而是从图标右侧的内容起始处开始;传 0 或不传保持通栏。
38
+ borderInsetLeft: {
39
+ type: [String, Number],
40
+ default: 0
41
+ },
36
42
  // 内容是否垂直居中(主要是针对右侧的value部分)
37
43
  center: {
38
44
  type: Boolean,
@@ -51,14 +51,21 @@
51
51
  </slot>
52
52
  </view>
53
53
  </view>
54
- <up-line v-if="border"></up-line>
54
+ <!--
55
+ iOS 标准分隔线:默认通栏;borderInsetLeft 传值后线条从该偏移处开始,
56
+ 对齐「图标右侧内容」的起始位置(= 左 padding + 图标宽 + 图标右距)。
57
+ -->
58
+ <view v-if="border" class="up-cell__line-wrap"
59
+ :style="{ paddingLeft: addUnit(borderInsetLeft) }">
60
+ <up-line></up-line>
61
+ </view>
55
62
  </view>
56
63
  </template>
57
64
 
58
65
  <script setup lang="ts">
59
66
  import { computed } from 'vue'
60
67
  import defProps from './cell'
61
- import { addStyle } from '../../libs/function/index'
68
+ import { addStyle, addUnit } from '../../libs/function/index'
62
69
  import { empty as _testEmpty } from '../../libs/function/test'
63
70
  // props
64
71
  const props = defineProps({
@@ -139,6 +146,12 @@
139
146
  type: Boolean,
140
147
  default: defProps.getBoolean('cell.border')
141
148
  },
149
+ // 分隔线左侧偏移(px 或带单位字符串)。iOS 标准是线条不顶到图标,
150
+ // 而是从图标右侧的内容起始处开始;传 0 或不传保持通栏。
151
+ borderInsetLeft: {
152
+ type: [String, Number],
153
+ default: 0
154
+ },
142
155
  disabled: {
143
156
  type: Boolean,
144
157
  default: defProps.getBoolean('cell.disabled')
@@ -44,7 +44,14 @@
44
44
  </slot>
45
45
  </view>
46
46
  </view>
47
- <up-line v-if="border"></up-line>
47
+ <!--
48
+ iOS 标准分隔线:默认通栏;borderInsetLeft 传值后线条从该偏移处开始,
49
+ 对齐「图标右侧内容」的起始位置(= 左 padding + 图标宽 + 图标右距)。
50
+ -->
51
+ <view v-if="border" class="up-cell__line-wrap"
52
+ :style="{ paddingLeft: addUnit(borderInsetLeft) }">
53
+ <up-line></up-line>
54
+ </view>
48
55
  </view>
49
56
  </template>
50
57
 
@@ -52,7 +59,7 @@
52
59
  import { computed, inject } from 'vue'
53
60
  import { propsCell } from './props.js'
54
61
  import { commonProps, useUltraUI } from '../../libs/composable/useUltraUI.js'
55
- import { addStyle } from '../../libs/function/index.js'
62
+ import { addStyle, addUnit } from '../../libs/function/index.js'
56
63
  import test from '../../libs/function/test.js'
57
64
  /**
58
65
  * cell 单元格
@@ -51,7 +51,14 @@
51
51
  $up-cell-group-title-line-height: 16px !default;
52
52
  $up-cell-group-title-color: $up-main-color !default;
53
53
 
54
+ /*
55
+ * 默认 iOS grouped-inset 卡片形制:大圆角 + 裁剪。
56
+ * 分隔线/子 cell 的直角背景被裁进圆角里;不需要圆角的场合用 customStyle
57
+ * 传 borderRadius: 0 覆盖(内联样式优先级高于此处类规则)。
58
+ */
54
59
  .up-cell-group {
60
+ border-radius: 12px;
61
+ overflow: hidden;
55
62
 
56
63
  &__title {
57
64
  padding: $up-cell-group-title-padding;
@@ -38,6 +38,7 @@
38
38
  import { array as testArray } from '../../libs/function/test'
39
39
  import { addStyle, addUnit, deepMerge, formValidate, error } from '../../libs/function/index'
40
40
  import defProps from './checkbox.uts'
41
+ import { upThemeVar } from '../../libs/theme/theme.uts'
41
42
  import { useUltraUI } from '../../libs/composable/useUltraUI'
42
43
  const { parent, parentData, getParentData } = useUltraUI()
43
44
  const instance = getCurrentInstance()!.proxy!
@@ -193,7 +194,7 @@
193
194
  ? props.activeColor
194
195
  : (parentData.value['activeColor'] != null
195
196
  ? parentData.value['activeColor'].toString()
196
- : '#2979ff');
197
+ : upThemeVar('--up-primary', '#2979ff'));
197
198
  })
198
199
 
199
200
  // 组件选未中激活时的颜色
@@ -503,8 +504,8 @@
503
504
 
504
505
  &--checked {
505
506
  color: $up-checkbox-icon-wrap-checked-color;
506
- background-color: $up-checkbox-icon-wrap-checked-background-color;
507
- border-color: $up-checkbox-icon-wrap-checked-border-color;
507
+ background-color: var(--up-primary, $up-checkbox-icon-wrap-checked-background-color);
508
+ border-color: var(--up-primary, $up-checkbox-icon-wrap-checked-border-color);
508
509
  }
509
510
 
510
511
  &--disabled {
@@ -29,7 +29,7 @@
29
29
  <view
30
30
  v-if="showGlass"
31
31
  class="up-navbar__glass"
32
- :class="[frostedGlass ? 'up-navbar__glass--frosted' : '']"
32
+ :class="[frostedGlass ? 'up-navbar__glass--frosted' : '', frostedBorder ? 'up-navbar__glass--border' : '']"
33
33
  :style="glassStyle"
34
34
  ></view>
35
35
  <!-- 状态栏占位:高度可被 statusBarInsetTop 覆盖(iOS standalone 传 0) -->
@@ -109,7 +109,7 @@
109
109
  * @property {Boolean} safeAreaInsetTop 是否开启顶部安全区适配 (默认 true )
110
110
  * @property {Boolean} placeholder 固定在顶部时,是否生成一个等高元素,以防止塌陷 (默认 false )
111
111
  * @property {Boolean} fixed 导航栏是否固定在顶部 (默认 false )
112
- * @property {Boolean} border 导航栏底部是否显示下边框 (默认 false )
112
+ * @property {Boolean} border 导航栏底部是否显示下边框(磨砂层的分隔线也服从它)(默认 false )
113
113
  * @property {String} leftIcon 左边返回图标的名称,只能为uView自带的图标 (默认 'arrow-left' )
114
114
  * @property {String} leftText 左边的提示文字
115
115
  * @property {String} rightText 右边的提示文字
@@ -253,6 +253,30 @@
253
253
  const emit = defineEmits(['leftClick', 'rightClick'])
254
254
  const systemStatusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight ?? 0)
255
255
 
256
+ // #ifdef H5 || WEB
257
+ // web 端 `statusBarHeight` 恒 0(浏览器视口不含系统状态栏),但 iOS Safari
258
+ // (viewport-fit=cover,地址栏收起)的页面视口顶部包含状态栏区域——真实占位
259
+ // 高度是 CSS `env(safe-area-inset-top)` 的像素值。探针元素读一次实际值:
260
+ // iPhone Safari ≈ 59,桌面 / Android 浏览器 env 为 0、行为不变。App 端原生
261
+ // statusBarHeight 是准的,不进这段。
262
+ try {
263
+ const doc: any = (globalThis as any).document
264
+ const probe = doc.createElement('div')
265
+ probe.setAttribute(
266
+ 'style',
267
+ 'position:absolute;visibility:hidden;pointer-events:none;height:env(safe-area-inset-top);padding:0;border:0;'
268
+ )
269
+ doc.body.appendChild(probe)
270
+ const measured = probe.getBoundingClientRect().height
271
+ doc.body.removeChild(probe)
272
+ if (typeof measured === 'number' && measured > 0) {
273
+ systemStatusBarHeight.value = measured
274
+ }
275
+ } catch (e) {
276
+ // 探测失败保持 0:与既有行为一致
277
+ }
278
+ // #endif
279
+
256
280
  /**
257
281
  * 实际使用的状态栏占位高度。
258
282
  *
@@ -361,6 +385,17 @@
361
385
  return props.frosted && !isIosMode.value
362
386
  })
363
387
 
388
+ /**
389
+ * 磨砂层要不要画底部分隔线。
390
+ *
391
+ * 与 `border` 同一语义 —— `border` 的描述就是「导航栏底部是否显示下边框」,
392
+ * 磨砂层那条线就是下边框,必须服从它。早先这条线写死在磨砂层样式里,
393
+ * 调用方明明传了 `:border="false"`,滚动时照样会冒出一条,等于这个开关半失效。
394
+ */
395
+ const frostedBorder = computed((): boolean => {
396
+ return frostedGlass.value && props.border
397
+ })
398
+
364
399
  // iOS 模式:磨砂在前半段走完,为居中标题的出现铺好不透明底。
365
400
  // frosted 模式:按滚动距离从 0 淡入到 1(「半透明」由底色负责,不是靠 opacity)。
366
401
  const glassOpacity = computed((): number => {
@@ -541,14 +576,18 @@
541
576
  }
542
577
 
543
578
  // 滚动磨砂(非 iOS 模式):底色是半透明的卡片色,配合 backdrop-filter 才是
544
- // 「高仿 iOS」的材质 —— 不透明底会把背后的模糊完全盖掉。底部细线在内容压到
545
- // 导航栏下时给出分隔,随磨砂层一起淡入。
579
+ // 「高仿 iOS」的材质 —— 不透明底会把背后的模糊完全盖掉。
546
580
  //
547
581
  // 底色走**主题变量**(`theme.uts` 里算好的 rgba),不写 `color-mix`:
548
582
  // uvue App 端不支持 `color-mix`,整条声明会被丢掉;而 App 端也不支持
549
583
  // `backdrop-filter` —— 两层同时失效,滚动时顶栏就跟全透明一样,磨砂等于没开。
550
584
  &__glass--frosted {
551
585
  background-color: var(--up-navbar-glass-bg, rgba(255, 255, 255, 0.75));
586
+ }
587
+
588
+ // 底部分隔线单独一个类,**只在 `border` 为真时**才挂上来。
589
+ // 早先它写死在 `--frosted` 里,页面传 `:border="false"` 也照样出线。
590
+ &__glass--border {
552
591
  border-bottom: 1px solid var(--up-navbar-glass-border, rgba(0, 0, 0, 0.08));
553
592
  }
554
593
 
@@ -171,9 +171,22 @@ const isIosMode = computed(() => props.mode === 'ios')
171
171
  // 有效大标题行高:title 为空时不渲染大标题行,行高塌陷为 0
172
172
  const largeTitleHeight = computed(() => (props.title ? LARGE_TITLE_HEIGHT : 0))
173
173
 
174
+ // web 端 `statusBarHeight` 恒 0,但 iOS Safari(viewport-fit=cover,地址栏收起)的
175
+ // 页面视口顶部包含状态栏区域——真实高度是 env(safe-area-inset-top)。探针读一次。
176
+ let envSafeAreaTop = 0
177
+ // #ifdef H5
178
+ try {
179
+ const probe = document.createElement('div')
180
+ probe.setAttribute('style', 'position:absolute;visibility:hidden;height:env(safe-area-inset-top);padding:0;border:0;')
181
+ document.body.appendChild(probe)
182
+ envSafeAreaTop = probe.getBoundingClientRect().height
183
+ document.body.removeChild(probe)
184
+ } catch (e) {}
185
+ // #endif
186
+
174
187
  // in-flow 层顶部让位块高度,为固定层腾出空间
175
188
  const flowSpacerHeight = computed(() => {
176
- const statusBarHeight = props.safeAreaInsetTop ? sys().statusBarHeight : 0
189
+ const statusBarHeight = props.safeAreaInsetTop ? (sys().statusBarHeight || envSafeAreaTop) : 0
177
190
  return addUnit(getPx(props.height) + statusBarHeight, 'px')
178
191
  })
179
192
 
@@ -81,6 +81,16 @@ export const props = defineMixin({
81
81
  pageInline: {
82
82
  type: Boolean,
83
83
  default: () => defProps.popup.pageInline
84
+ },
85
+ // bottom 弹层是否支持拖动下滑关闭
86
+ dragClose: {
87
+ type: Boolean,
88
+ default: false
89
+ },
90
+ // 拖动下滑关闭的位移阈值,单位px
91
+ dragCloseThreshold: {
92
+ type: [String, Number],
93
+ default: 80
84
94
  }
85
95
  }
86
96
  })
@@ -20,7 +20,11 @@
20
20
  >
21
21
  <view
22
22
  class="up-popup__content"
23
- :style="[contentStyle]"
23
+ :style="[contentStyle, dragStyle]"
24
+ @touchstart="onDragTouchStart"
25
+ @touchmove="onDragTouchMove"
26
+ @touchend="onDragTouchEnd"
27
+ @touchcancel="onDragTouchEnd"
24
28
  @tap.stop="stopContentTap"
25
29
  >
26
30
  <up-status-bar v-if="safeAreaInsetTop"></up-status-bar>
@@ -69,6 +73,8 @@
69
73
  * @property {String} closeIconPos 自定义关闭图标位置(默认 'top-right' )
70
74
  * @property {String | Number} round 圆角值(默认 0)
71
75
  * @property {Boolean} zoom 当mode=center时 是否开启缩放(默认 true )
76
+ * @property {Boolean} dragClose bottom 弹层是否支持拖动下滑关闭(默认 false;配合弹层顶部的拖拽手柄使用)
77
+ * @property {String | Number} dragCloseThreshold 拖动下滑关闭的位移阈值,单位px(默认 80)
72
78
  * @property {Object} customStyle 组件的样式,对象形式
73
79
  * @event {Function} open 弹出层打开(进场动画结束后)
74
80
  * @event {Function} close 弹出层收起(关闭动作发生时,离场动画开始前;外部直接将show置为false时也会触发)
@@ -161,6 +167,16 @@
161
167
  type: Boolean,
162
168
  default: defProps.getBoolean('popup.pageInline')
163
169
  },
170
+ // bottom 弹层是否支持拖动下滑关闭
171
+ dragClose: {
172
+ type: Boolean,
173
+ default: false
174
+ },
175
+ // 拖动下滑关闭的位移阈值,单位px
176
+ dragCloseThreshold: {
177
+ type: [String, Number],
178
+ default: 80
179
+ },
164
180
  customStyle: {
165
181
  type: [Object, String],
166
182
  default: () => ({})
@@ -289,6 +305,78 @@
289
305
  return ''
290
306
  })
291
307
 
308
+ /*
309
+ * 拖动下滑关闭(dragClose)。
310
+ *
311
+ * 仅 bottom 弹层生效:手指按下后向下拖,content 跟手位移;松手时超过阈值
312
+ * 走既有 close 流程(update:show=false + close 事件),否则带回弹动画归位。
313
+ * 向上拖不拦截,弹层内部滚动不受影响。
314
+ */
315
+ const dragOffset = ref(0)
316
+ const dragActive = ref(false)
317
+ let dragStartY = 0
318
+ let dragTracking = false
319
+ let dragReleaseTimer = -1
320
+
321
+ const dragStyle = computed((): UTSJSONObject => {
322
+ if (!dragActive.value || dragOffset.value <= 0) {
323
+ return {} as UTSJSONObject
324
+ }
325
+ const style = {
326
+ transform: `translateY(${dragOffset.value}px)`,
327
+ } as UTSJSONObject
328
+ // 跟手阶段不做过渡;松手回弹时加过渡(dragActive 在回弹结束后复位)
329
+ style["transition"] = dragTracking ? "none" : "transform 0.25s ease-out"
330
+ return style
331
+ })
332
+
333
+ const dragCloseEnabled = computed((): boolean => {
334
+ return props.dragClose && props.mode === "bottom" && !props.pageInline
335
+ })
336
+
337
+ const onDragTouchStart = (e: TouchEvent): void => {
338
+ if (!dragCloseEnabled.value) return
339
+ if (e.touches.length == 0) return
340
+ dragStartY = e.touches[0].clientY
341
+ dragTracking = true
342
+ }
343
+
344
+ const onDragTouchMove = (e: TouchEvent): void => {
345
+ if (!dragTracking) return
346
+ if (e.touches.length == 0) return
347
+ const delta = e.touches[0].clientY - dragStartY
348
+ if (delta <= 0) {
349
+ // 向上拖:不进入拖动态,交给内部滚动
350
+ if (!dragActive.value) return
351
+ dragOffset.value = 0
352
+ return
353
+ }
354
+ dragActive.value = true
355
+ dragOffset.value = delta
356
+ }
357
+
358
+ const onDragTouchEnd = (_e: UniEvent): void => {
359
+ if (!dragTracking) return
360
+ dragTracking = false
361
+ const offset = dragOffset.value
362
+ const thresholdValue = parseInt(props.dragCloseThreshold.toString())
363
+ const threshold = isNaN(thresholdValue) ? 80 : thresholdValue
364
+ if (offset >= threshold) {
365
+ dragActive.value = false
366
+ dragOffset.value = 0
367
+ close(_e)
368
+ return
369
+ }
370
+ // 回弹:dragStyle 保留到过渡结束后再卸下,否则回弹动画会被瞬时移除样式截断
371
+ if (dragReleaseTimer >= 0) {
372
+ clearTimeout(dragReleaseTimer)
373
+ }
374
+ dragReleaseTimer = setTimeout(() => {
375
+ dragActive.value = false
376
+ dragOffset.value = 0
377
+ }, 260)
378
+ }
379
+
292
380
  const stopContentTap = (e: UniEvent): void => {
293
381
  e.stopPropagation()
294
382
  }
@@ -20,7 +20,11 @@
20
20
  >
21
21
  <view
22
22
  class="up-popup__content"
23
- :style="[contentStyle]"
23
+ :style="[contentStyle, dragStyle]"
24
+ @touchstart="onDragTouchStart"
25
+ @touchmove="onDragTouchMove"
26
+ @touchend="onDragTouchEnd"
27
+ @touchcancel="onDragTouchEnd"
24
28
  @tap.stop="noop"
25
29
  >
26
30
  <up-status-bar v-if="safeAreaInsetTop"></up-status-bar>
@@ -92,6 +96,70 @@
92
96
  const emit = defineEmits(['open', 'close', 'closed', 'click', 'update:show'])
93
97
  const instance = getCurrentInstance()
94
98
  const { noop } = useUltraUI(props)
99
+
100
+ /*
101
+ * 拖动下滑关闭(dragClose)。
102
+ *
103
+ * 仅 bottom 弹层生效:手指按下后向下拖,content 跟手位移;松手时超过阈值
104
+ * 走既有 close 流程(update:show=false + close 事件),否则带回弹动画归位。
105
+ * 向上拖不拦截,弹层内部滚动不受影响。
106
+ */
107
+ const dragOffset = ref(0)
108
+ const dragActive = ref(false)
109
+ let dragStartY = 0
110
+ let dragTracking = false
111
+ let dragReleaseTimer = -1
112
+
113
+ const dragStyle = computed(() => {
114
+ if (!dragActive.value || dragOffset.value <= 0) return {}
115
+ return {
116
+ transform: `translateY(${dragOffset.value}px)`,
117
+ transition: dragTracking ? 'none' : 'transform 0.25s ease-out',
118
+ }
119
+ })
120
+
121
+ const dragCloseEnabled = computed(() => props.dragClose && props.mode === 'bottom' && !props.pageInline)
122
+
123
+ const onDragTouchStart = (e) => {
124
+ if (!dragCloseEnabled.value) return
125
+ if (!e.touches || e.touches.length === 0) return
126
+ dragStartY = e.touches[0].clientY
127
+ dragTracking = true
128
+ }
129
+
130
+ const onDragTouchMove = (e) => {
131
+ if (!dragTracking) return
132
+ if (!e.touches || e.touches.length === 0) return
133
+ const delta = e.touches[0].clientY - dragStartY
134
+ if (delta <= 0) {
135
+ // 向上拖:不进入拖动态,交给内部滚动
136
+ if (!dragActive.value) return
137
+ dragOffset.value = 0
138
+ return
139
+ }
140
+ dragActive.value = true
141
+ dragOffset.value = delta
142
+ }
143
+
144
+ const onDragTouchEnd = (_e) => {
145
+ if (!dragTracking) return
146
+ dragTracking = false
147
+ const offset = dragOffset.value
148
+ const thresholdValue = parseInt(props.dragCloseThreshold)
149
+ const threshold = isNaN(thresholdValue) ? 80 : thresholdValue
150
+ if (offset >= threshold) {
151
+ dragActive.value = false
152
+ dragOffset.value = 0
153
+ close(_e)
154
+ return
155
+ }
156
+ // 回弹:dragStyle 保留到过渡结束后再卸下,否则回弹动画会被瞬时移除样式截断
157
+ if (dragReleaseTimer >= 0) clearTimeout(dragReleaseTimer)
158
+ dragReleaseTimer = setTimeout(() => {
159
+ dragActive.value = false
160
+ dragOffset.value = 0
161
+ }, 260)
162
+ }
95
163
  const overlayDuration = ref(props.duration + 50)
96
164
  // close事件是否已由组件内部的关闭动作发出,避免show变化时重复发出
97
165
  const closeEmitted = ref(false)
@@ -39,7 +39,8 @@
39
39
 
40
40
  <script setup lang="uts">
41
41
  import { computed, getCurrentInstance, nextTick, onMounted, ref } from 'vue';
42
- import defProps from './radio.uts';
42
+ import defProps from './radio.uts'
43
+ import { upThemeVar } from '../../libs/theme/theme.uts';
43
44
  import { addUnit, addStyle, os, deepMerge, formValidate, error } from '../../libs/function/index.uts';
44
45
  import { useUltraUI } from '../../libs/composable/useUltraUI.uts';
45
46
  /**
@@ -213,7 +214,7 @@
213
214
  const elActiveColor = computed((): string => {
214
215
  return props.activeColor as string != ''
215
216
  ? props.activeColor as string
216
- : (parentData.value['activeColor'] as string != '' ? parentData.value['activeColor'] as string : '#2979ff')
217
+ : (parentData.value['activeColor'] as string != '' ? parentData.value['activeColor'] as string : upThemeVar('--up-primary', '#2979ff'))
217
218
  })
218
219
 
219
220
  // 组件选未中激活时的颜色
@@ -485,8 +486,9 @@
485
486
 
486
487
  &--checked {
487
488
  color: $up-radio-checked-color;
488
- background-color: $up-radio-checked-background-color;
489
- border-color: $up-radio-checked-border-color;
489
+ /* 兜底色接主题变量:activeColor 未显式传时(radio-group 默认)随主色走 */
490
+ background-color: var(--up-primary, $up-radio-checked-background-color);
491
+ border-color: var(--up-primary, $up-radio-checked-border-color);
490
492
  }
491
493
 
492
494
  &--disabled {
@@ -13,7 +13,7 @@ export default {
13
13
  value: '',
14
14
  disabled: false,
15
15
  shape: 'circle',
16
- activeColor: '#2979ff',
16
+ activeColor: 'var(--up-primary, #2979ff)',
17
17
  inactiveColor: '#c8c9cc',
18
18
  name: '',
19
19
  size: 18,
@@ -13,7 +13,7 @@ export default {
13
13
  value: '',
14
14
  disabled: false,
15
15
  shape: 'circle',
16
- activeColor: '#2979ff',
16
+ activeColor: 'var(--up-primary, #2979ff)',
17
17
  inactiveColor: '#c8c9cc',
18
18
  name: '',
19
19
  size: 18,
@@ -15,7 +15,8 @@ export default {
15
15
  loading: false,
16
16
  disabled: false,
17
17
  size: 25,
18
- activeColor: '#2979ff',
18
+ // 与 switch.js 对齐:走主题变量(uvue 端 background 支持 var(),样式隔离 2.0 下变量可继承)
19
+ activeColor: 'var(--up-switch-active-color, var(--up-primary, #2979ff))',
19
20
  inactiveColor: '#ffffff',
20
21
  activeValue: true,
21
22
  inactiveValue: false,
@@ -275,7 +275,7 @@
275
275
  }
276
276
 
277
277
  &--square {
278
- border-radius: 3px;
278
+ border-radius: 8px;
279
279
  }
280
280
 
281
281
  &__icon {
@@ -183,7 +183,7 @@
183
183
  }
184
184
 
185
185
  &--square {
186
- border-radius: 3px;
186
+ border-radius: 8px;
187
187
  }
188
188
 
189
189
  &__icon {
@@ -142,6 +142,16 @@ const vueEnter = async (): Promise<void> => {
142
142
  emit('afterEnter')
143
143
  // 赋予组件enter-to类名
144
144
  classes.value = classNames['enter-to']!!.toString()
145
+ // 动画结束后清理 enter-active / enter-to。
146
+ // 不清理的话 transition-property 会常驻:进场的 CSS 过渡一旦被页面上任何一次
147
+ // 重渲染打断,computed transform 就冻结在起点(bottom 弹层 = translateY(100%),
148
+ // 整个弹层停在屏幕外),且之后再没有任何样式变化能触发新过渡把它拉回来。
149
+ // 清掉 active 类后 transition-property 消失,冻结值直接跳到最终态(归位)。
150
+ setTimeout(() => {
151
+ if (status.value === 'enter') {
152
+ classes.value = ''
153
+ }
154
+ }, parseInt(props.duration != null ? props.duration.toString() : "0") + 50)
145
155
  }
146
156
 
147
157
  // 动画离场处理
@@ -134,6 +134,13 @@ async function vueEnter() {
134
134
  emit('afterEnter')
135
135
  // 赋予组件enter-to类名
136
136
  classes.value = classNames['enter-to']
137
+ // 动画结束后清理 enter-active / enter-to(避免过渡被打断后 computed transform
138
+ // 冻结在起点,bottom 弹层停在屏幕外)
139
+ setTimeout(() => {
140
+ if (status.value === 'enter') {
141
+ classes.value = ''
142
+ }
143
+ }, parseInt(String(props.duration)) + 50)
137
144
  }
138
145
  }
139
146