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.
- package/changelog.md +1650 -1480
- package/components/up-button/up-button.uvue +9 -8
- package/components/up-button/up-button.vue +4 -4
- package/components/up-cell/props.js +6 -0
- package/components/up-cell/up-cell.uvue +15 -2
- package/components/up-cell/up-cell.vue +9 -2
- package/components/up-cell-group/up-cell-group.uvue +7 -0
- package/components/up-checkbox/up-checkbox.uvue +4 -3
- package/components/up-navbar/up-navbar.uvue +43 -4
- package/components/up-navbar/up-navbar.vue +14 -1
- package/components/up-popup/props.js +10 -0
- package/components/up-popup/up-popup.uvue +89 -1
- package/components/up-popup/up-popup.vue +69 -1
- package/components/up-radio/up-radio.uvue +6 -4
- package/components/up-radio-group/radioGroup.js +1 -1
- package/components/up-radio-group/radioGroup.uts +1 -1
- package/components/up-switch/switch.uts +2 -1
- package/components/up-tag/up-tag.uvue +1 -1
- package/components/up-tag/up-tag.vue +1 -1
- package/components/up-transition/up-transition.uvue +10 -0
- package/components/up-transition/up-transition.vue +7 -0
- package/components/up-transition/vue.ani-style.scss +39 -10
- package/libs/theme/theme.js +12 -2
- package/libs/theme/theme.uts +151 -7
- package/package.json +1 -1
|
@@ -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:
|
|
406
|
-
$up-button-square-border-top-left-radius:
|
|
407
|
-
$up-button-square-border-bottom-left-radius:
|
|
408
|
-
$up-button-square-border-bottom-right-radius:
|
|
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:
|
|
319
|
-
$up-button-square-border-top-left-radius:
|
|
320
|
-
$up-button-square-border-bottom-left-radius:
|
|
321
|
-
$up-button-square-border-bottom-right-radius:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
489
|
-
|
|
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 {
|
|
@@ -15,7 +15,8 @@ export default {
|
|
|
15
15
|
loading: false,
|
|
16
16
|
disabled: false,
|
|
17
17
|
size: 25,
|
|
18
|
-
|
|
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,
|
|
@@ -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
|
|