兼容 ios 样式

This commit is contained in:
icssoa
2025-09-03 19:03:39 +08:00
parent 0f2eea7b03
commit 7791c47483
18 changed files with 168 additions and 139 deletions

View File

@@ -30,7 +30,7 @@
v-for="(item, index) in config.list" v-for="(item, index) in config.list"
:key="index" :key="index"
:hover-class="`${isDark ? '!bg-surface-900' : '!bg-surface-50'}`" :hover-class="`${isDark ? '!bg-surface-900' : '!bg-surface-50'}`"
:hover-stay-time="100" :hover-stay-time="250"
@tap="onItemTap(item)" @tap="onItemTap(item)"
> >
<slot name="item" :item="item"> <slot name="item" :item="item">

View File

@@ -17,7 +17,7 @@
> >
<cl-text <cl-text
:pt="{ :pt="{
className: parseClass(['cl-badge__text !text-white !text-sm', pt.text?.className]) className: parseClass(['cl-badge__text !text-white !text-xs', pt.text?.className])
}" }"
v-if="!dot" v-if="!dot"
> >

View File

@@ -74,8 +74,7 @@ const padding = computed(() => (parent == null ? "0" : parseRpx(parent.gutter /
@use "sass:math"; @use "sass:math";
.cl-col { .cl-col {
@apply w-full; @apply w-full overflow-visible;
overflow: visible;
} }
@for $i from 1 through 24 { @for $i from 1 through 24 {

View File

@@ -1,6 +1,9 @@
<template> <template>
<view <view
class="cl-float-view" class="cl-float-view"
:class="{
'no-dragging': !position.isDragging
}"
:style="viewStyle" :style="viewStyle"
@touchstart="onTouchStart" @touchstart="onTouchStart"
@touchmove.stop.prevent="onTouchMove" @touchmove.stop.prevent="onTouchMove"
@@ -126,11 +129,6 @@ const viewStyle = computed(() => {
// 设置高度 // 设置高度
style["height"] = `${props.size}px`; style["height"] = `${props.size}px`;
// 非拖拽状态下添加过渡动画,使移动更平滑
if (!position.isDragging) {
style["transition-duration"] = "300ms";
}
return style; return style;
}); });
@@ -279,6 +277,11 @@ function onTouchEnd() {
<style lang="scss" scoped> <style lang="scss" scoped>
.cl-float-view { .cl-float-view {
@apply fixed; @apply fixed transition-none;
&.no-dragging {
@apply duration-300;
transition-property: left, bottom;
}
} }
</style> </style>

View File

@@ -18,7 +18,7 @@
pt.op?.minus?.className pt.op?.minus?.className
]" ]"
hover-class="!bg-surface-200" hover-class="!bg-surface-200"
:hover-stay-time="50" :hover-stay-time="250"
:style="{ :style="{
height: parseRpx(size!), height: parseRpx(size!),
width: parseRpx(size!) width: parseRpx(size!)
@@ -66,7 +66,7 @@
pt.op?.plus?.className pt.op?.plus?.className
]" ]"
hover-class="!bg-primary-600" hover-class="!bg-primary-600"
:hover-stay-time="50" :hover-stay-time="250"
:style="{ :style="{
height: parseRpx(size!), height: parseRpx(size!),
width: parseRpx(size!) width: parseRpx(size!)

View File

@@ -64,7 +64,7 @@
marginRight: index == row.length - 1 ? '0' : '10rpx' marginRight: index == row.length - 1 ? '0' : '10rpx'
}" }"
hover-class="opacity-50" hover-class="opacity-50"
:hover-stay-time="50" :hover-stay-time="250"
@touchstart.stop="onCommand(item)" @touchstart.stop="onCommand(item)"
> >
<slot name="item" :item="item"> <slot name="item" :item="item">

View File

@@ -42,49 +42,89 @@
<view class="cl-keyboard-number__list"> <view class="cl-keyboard-number__list">
<cl-row :gutter="10"> <cl-row :gutter="10">
<cl-col :span="6" v-for="item in list" :key="item"> <cl-col :span="18">
<view <cl-row :gutter="10">
class="cl-keyboard-number__item" <cl-col :span="8" v-for="item in list" :key="item">
:class="[ <view
`is-keycode-${item}`, class="cl-keyboard-number__item"
{ :class="[
'is-dark': isDark, `is-keycode-${item}`,
'is-empty': item == '' {
}, 'is-dark': isDark,
pt.item?.className 'is-empty': item == ''
]" },
hover-class="opacity-50" pt.item?.className
:hover-stay-time="50" ]"
@touchstart.stop="onCommand(item)" hover-class="opacity-50"
> :hover-stay-time="250"
<slot name="item" :item="item"> @touchstart.stop="onCommand(item)"
>
<slot name="item" :item="item">
<cl-icon
v-if="item == 'delete'"
name="delete-back-2-line"
:size="36"
></cl-icon>
<view
v-else-if="item == 'confirm'"
class="cl-keyboard-number__item-confirm"
>
<cl-text
color="white"
:pt="{
className: '!text-lg'
}"
>{{ confirmText }}</cl-text
>
</view>
<view v-else-if="item == '_confirm'"></view>
<cl-text
v-else
:pt="{
className: '!text-lg'
}"
>{{ item }}</cl-text
>
</slot>
</view>
</cl-col>
</cl-row>
</cl-col>
<cl-col :span="6">
<view class="cl-keyboard-number__op">
<view
v-for="item in opList"
:key="item"
class="cl-keyboard-number__item"
:class="[
{
'is-dark': isDark
},
`is-keycode-${item}`
]"
hover-class="opacity-50"
:hover-stay-time="250"
@touchstart.stop="onCommand(item)"
>
<cl-icon <cl-icon
v-if="item == 'delete'" v-if="item == 'delete'"
name="delete-back-2-line" name="delete-back-2-line"
:size="36" :size="36"
></cl-icon> ></cl-icon>
<view
v-else-if="item == 'confirm'"
class="cl-keyboard-number__item-confirm"
>
<cl-text
color="white"
:pt="{
className: '!text-lg'
}"
>{{ confirmText }}</cl-text
>
</view>
<cl-text <cl-text
v-else v-if="item == 'confirm'"
color="white"
:pt="{ :pt="{
className: '!text-lg' className: '!text-lg'
}" }"
>{{ item }}</cl-text >{{ confirmText }}</cl-text
> >
</slot> </view>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
@@ -99,7 +139,7 @@ import type { PassThroughProps } from "../../types";
import type { ClPopupProps } from "../cl-popup/props"; import type { ClPopupProps } from "../cl-popup/props";
import { ref, computed, watch, type PropType } from "vue"; import { ref, computed, watch, type PropType } from "vue";
import { $t, t } from "@/locale"; import { $t, t } from "@/locale";
import { isDark, parseClass, parsePt } from "@/cool"; import { isAppIOS, isDark, parseClass, parsePt } from "@/cool";
import { vibrate } from "@/uni_modules/cool-vibrate"; import { vibrate } from "@/uni_modules/cool-vibrate";
defineOptions({ defineOptions({
@@ -193,38 +233,26 @@ const maxlength = computed(() => {
// 数字键盘的按键列表包含数字、删除、00和小数点 // 数字键盘的按键列表包含数字、删除、00和小数点
const list = computed(() => { const list = computed(() => {
const arr = [ const arr = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "00", "0", ""];
"1",
"2",
"3",
"delete",
"4",
"5",
"6",
"",
"7",
"8",
"9",
"",
"00",
"0",
"",
"confirm"
];
// 数字键盘显示为小数点 "." // 数字键盘显示为小数点 "."
if (props.type == "digit") { if (props.type == "digit") {
arr[14] = "."; arr[11] = ".";
} }
// 身份证键盘显示为 "X" // 身份证键盘显示为 "X"
if (props.type == "idcard") { if (props.type == "idcard") {
arr[14] = "X"; arr[11] = "X";
} }
return arr; return arr;
}); });
// 操作按钮列表
const opList = computed(() => {
return ["delete", "confirm"];
});
// 打开键盘弹窗 // 打开键盘弹窗
function open() { function open() {
visible.value = true; visible.value = true;
@@ -238,10 +266,12 @@ function close() {
// 处理键盘按键点击事件 // 处理键盘按键点击事件
function onCommand(key: string) { function onCommand(key: string) {
// 震动 // 震动
vibrate(1); try {
vibrate(1);
} catch (error) {}
// 确认按钮逻辑 // 确认按钮逻辑
if (key == "confirm") { if (key == "confirm" || key == "_confirm") {
if (value.value == "") { if (value.value == "") {
ui.showToast({ ui.showToast({
message: t("请输入内容") message: t("请输入内容")
@@ -364,14 +394,17 @@ defineExpose({
} }
&__list { &__list {
@apply relative; @apply relative overflow-visible;
}
&__op {
@apply flex flex-col h-full;
} }
&__item { &__item {
@apply flex items-center justify-center rounded-xl bg-white; @apply flex items-center justify-center rounded-xl bg-white overflow-visible;
height: 100rpx; height: 100rpx;
margin-top: 10rpx; margin-top: 10rpx;
overflow: visible;
&.is-dark { &.is-dark {
@apply bg-surface-800; @apply bg-surface-800;
@@ -386,14 +419,8 @@ defineExpose({
} }
&.is-keycode-confirm { &.is-keycode-confirm {
@apply bg-transparent relative;
}
&-confirm {
@apply flex flex-col items-center justify-center; @apply flex flex-col items-center justify-center;
@apply bg-primary-500 absolute w-full rounded-xl; @apply bg-primary-500 rounded-xl flex-1;
height: 320rpx;
top: -220rpx;
} }
&.is-empty { &.is-empty {

View File

@@ -63,7 +63,7 @@
marginRight: index == row.length - 1 ? '0' : '10rpx' marginRight: index == row.length - 1 ? '0' : '10rpx'
}" }"
hover-class="opacity-50" hover-class="opacity-50"
:hover-stay-time="50" :hover-stay-time="250"
@touchstart.stop="onCommand(item)" @touchstart.stop="onCommand(item)"
> >
<slot name="item" :item="item"> <slot name="item" :item="item">

View File

@@ -109,7 +109,7 @@ import {
useSlots, useSlots,
type PropType type PropType
} from "vue"; } from "vue";
import { isDark, isHarmony, parseClass, parsePt } from "@/cool"; import { isAppIOS, isDark, isHarmony, parseClass, parsePt } from "@/cool";
import type { Justify, PassThroughProps } from "../../types"; import type { Justify, PassThroughProps } from "../../types";
import type { ClIconProps } from "../cl-icon/props"; import type { ClIconProps } from "../cl-icon/props";
@@ -388,7 +388,7 @@ onMounted(() => {
() => { () => {
initSwipe(); initSwipe();
}, },
isHarmony() ? 50 : 0 isHarmony() || isAppIOS() ? 50 : 0
); );
}); });

View File

@@ -56,7 +56,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { forInObject, isDark, isEqual, isNull, parseClass, rpx2px } from "@/cool"; import { forInObject, isAppIOS, isDark, isEqual, isNull, parseClass, rpx2px } from "@/cool";
import type { ClSelectOption } from "../../types"; import type { ClSelectOption } from "../../types";
import { parseRpx } from "@/cool"; import { parseRpx } from "@/cool";
import { computed } from "vue"; import { computed } from "vue";
@@ -107,6 +107,10 @@ const headers = computed(() => {
// 遮罩层样式 // 遮罩层样式
const maskStyle = computed(() => { const maskStyle = computed(() => {
if (isDark.value) { if (isDark.value) {
if(isAppIOS()) {
return `background-color: rgba(0, 0, 0, 0);`
}
return `background-image: linear-gradient( return `background-image: linear-gradient(
180deg, 180deg,
rgba(0, 0, 0, 0), rgba(0, 0, 0, 0),

View File

@@ -24,7 +24,7 @@ import {
} from "vue"; } from "vue";
import { drawQrcode, type QrcodeOptions } from "./draw"; import { drawQrcode, type QrcodeOptions } from "./draw";
import { canvasToPng, getPx, isHarmony, uuid } from "@/cool"; import { canvasToPng, getPx, isAppIOS, isHarmony, uuid } from "@/cool";
import type { ClQrcodeMode } from "../../types"; import type { ClQrcodeMode } from "../../types";
defineOptions({ defineOptions({
@@ -168,7 +168,7 @@ onMounted(() => {
() => { () => {
drawer(); drawer();
}, },
isHarmony() ? 50 : 0 isHarmony() || isAppIOS() ? 50 : 0
); );
}); });

View File

@@ -53,6 +53,6 @@ defineExpose({
<style lang="scss" scoped> <style lang="scss" scoped>
.cl-row { .cl-row {
@apply flex flex-row flex-wrap relative; @apply flex flex-row flex-wrap relative overflow-visible;
} }
</style> </style>

View File

@@ -1,9 +1,9 @@
<template> <template>
<view <view
class="cl-safe-area" class="cl-safe-area"
:class="[{ 'is-dark': isDark }, pt.className]" :class="[pt.className]"
:style="{ :style="{
height: height + 'px' height
}" }"
> >
<slot></slot> <slot></slot>
@@ -11,7 +11,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getSafeAreaHeight, isDark, parsePt } from "@/cool"; import { getSafeAreaHeight, parsePt } from "@/cool";
import { computed, type PropType } from "vue"; import { computed, type PropType } from "vue";
defineOptions({ defineOptions({
@@ -26,6 +26,10 @@ const props = defineProps({
type: { type: {
type: String as PropType<"top" | "bottom">, type: String as PropType<"top" | "bottom">,
default: "top" default: "top"
},
transparent: {
type: Boolean,
default: false
} }
}); });
@@ -40,13 +44,3 @@ const height = computed(() => {
return getSafeAreaHeight(props.type) + "px"; return getSafeAreaHeight(props.type) + "px";
}); });
</script> </script>
<style lang="scss" scoped>
.cl-safe-area {
@apply bg-white;
&.is-dark {
@apply bg-surface-900;
}
}
</style>

View File

@@ -317,14 +317,15 @@ function initCanvas() {
} }
onMounted(() => { onMounted(() => {
initCanvas();
watch( watch(
computed(() => [props.width, props.height]), computed(() => [props.width, props.height]),
() => { () => {
nextTick(() => { nextTick(() => {
initCanvas(); initCanvas();
}); });
},
{
immediate: true
} }
); );
}); });

View File

@@ -45,7 +45,8 @@
:class="[ :class="[
{ {
'cl-slide-verify__progress--success': isSuccess, 'cl-slide-verify__progress--success': isSuccess,
'cl-slide-verify__progress--fail': isFail 'cl-slide-verify__progress--fail': isFail,
'no-dragging': !isDragging
}, },
pt.progress?.className pt.progress?.className
]" ]"
@@ -60,7 +61,8 @@
'cl-slide-verify__slider--active': isDragging, 'cl-slide-verify__slider--active': isDragging,
'cl-slide-verify__slider--success': isSuccess, 'cl-slide-verify__slider--success': isSuccess,
'cl-slide-verify__slider--fail': isFail, 'cl-slide-verify__slider--fail': isFail,
'cl-slide-verify__slider--dark': isDark 'cl-slide-verify__slider--dark': isDark,
'no-dragging': !isDragging
}, },
pt.slider?.className pt.slider?.className
]" ]"
@@ -268,10 +270,6 @@ const progressStyle = computed(() => {
width += props.size / 2; width += props.size / 2;
} }
style["width"] = width + "px"; // 设置宽度 style["width"] = width + "px"; // 设置宽度
if (!isDragging.value) {
// 非拖动时添加过渡动画
style["transition-duration"] = "300ms";
}
return style; // 返回样式对象 return style; // 返回样式对象
}); });
@@ -282,10 +280,6 @@ const sliderStyle = computed(() => {
height: props.size + "px", // 滑块高度 height: props.size + "px", // 滑块高度
width: props.size + "px" // 滑块宽度 width: props.size + "px" // 滑块宽度
}; };
if (!isDragging.value) {
// 非拖动时添加过渡动画
style["transition-duration"] = "300ms";
}
return style; // 返回样式对象 return style; // 返回样式对象
}); });
@@ -423,6 +417,11 @@ defineExpose({
.cl-slide-verify { .cl-slide-verify {
@apply relative rounded-lg w-full flex flex-col items-center justify-center; @apply relative rounded-lg w-full flex flex-col items-center justify-center;
.no-dragging {
@apply duration-300;
transition-property: left;
}
&__track { &__track {
@apply relative w-full h-full; @apply relative w-full h-full;
@apply bg-surface-100 rounded-lg; @apply bg-surface-100 rounded-lg;
@@ -445,7 +444,7 @@ defineExpose({
} }
&__progress { &__progress {
@apply absolute left-0 top-0 h-full; @apply absolute left-0 top-0 h-full transition-none;
@apply bg-primary-100; @apply bg-primary-100;
&--success { &--success {
@@ -458,7 +457,7 @@ defineExpose({
} }
&__slider { &__slider {
@apply absolute top-1/2 left-0 z-20; @apply absolute top-1/2 left-0 z-20 transition-none;
@apply bg-white rounded-lg; @apply bg-white rounded-lg;
@apply flex items-center justify-center; @apply flex items-center justify-center;
@apply border border-surface-200; @apply border border-surface-200;

View File

@@ -26,30 +26,30 @@
:class="[pt.progress?.className]" :class="[pt.progress?.className]"
:style="progressStyle" :style="progressStyle"
></view> ></view>
<!-- 单滑块模式 -->
<view
v-if="!range"
class="cl-slider__thumb"
:class="[pt.thumb?.className]"
:style="singleThumbStyle"
></view>
<!-- 双滑块模式 -->
<template v-if="range">
<view
class="cl-slider__thumb cl-slider__thumb--min"
:class="[pt.thumb?.className]"
:style="minThumbStyle"
></view>
<view
class="cl-slider__thumb cl-slider__thumb--max"
:class="[pt.thumb?.className]"
:style="maxThumbStyle"
></view>
</template>
</view> </view>
<!-- 单滑块模式 -->
<view
v-if="!range"
class="cl-slider__thumb"
:class="[pt.thumb?.className]"
:style="singleThumbStyle"
></view>
<!-- 双滑块模式 -->
<template v-if="range">
<view
class="cl-slider__thumb cl-slider__thumb--min"
:class="[pt.thumb?.className]"
:style="minThumbStyle"
></view>
<view
class="cl-slider__thumb cl-slider__thumb--max"
:class="[pt.thumb?.className]"
:style="maxThumbStyle"
></view>
</template>
<view <view
class="cl-slider__picker" class="cl-slider__picker"
:style="{ :style="{
@@ -531,9 +531,8 @@ onMounted(() => {
} }
&__thumb { &__thumb {
@apply absolute top-1/2 rounded-full border border-solid border-white; @apply absolute rounded-full border border-solid border-white;
@apply bg-primary-500; @apply bg-primary-500;
transform: translateY(-50%);
pointer-events: none; pointer-events: none;
z-index: 1; z-index: 1;
border-width: 4rpx; border-width: 4rpx;

View File

@@ -50,7 +50,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from "vue"; import { computed, ref, watch } from "vue";
import { isDark, parseClass, parsePt, rpx2px } from "@/cool"; import { isAppIOS, isDark, parseClass, parsePt, rpx2px } from "@/cool";
import type { PassThroughProps } from "../../types"; import type { PassThroughProps } from "../../types";
import { vibrate } from "@/uni_modules/cool-vibrate"; import { vibrate } from "@/uni_modules/cool-vibrate";
import { useForm } from "../../hooks"; import { useForm } from "../../hooks";
@@ -145,7 +145,7 @@ const rect = computed<Rect>(() => {
width: width + "rpx", width: width + "rpx",
size: size + "rpx", size: size + "rpx",
left: left + "rpx", left: left + "rpx",
translateX: rpx2px(translateX) + "px" translateX: isAppIOS() ? rpx2px(translateX) + "px" : `${translateX}rpx`
}; };
}); });

View File

@@ -6,7 +6,10 @@
{ {
'is-expand': hover, 'is-expand': hover,
'is-dark': isDark, 'is-dark': isDark,
'is-checked': item.isChecked == true && ClTree?.checkable == true, 'is-checked':
item.isChecked == true &&
ClTree?.checkable == true &&
ClTree?.multiple == false,
'is-half-checked': item.isHalfChecked, 'is-half-checked': item.isHalfChecked,
'is-disabled': item.disabled, 'is-disabled': item.disabled,
'is-multiple': ClTree?.multiple 'is-multiple': ClTree?.multiple