添加全局字号,动态调整大小

This commit is contained in:
icssoa
2025-08-13 15:21:16 +08:00
parent b04cfb0066
commit e7b17195dd
49 changed files with 682 additions and 292 deletions

Binary file not shown.

95
components/size-set.uvue Normal file
View File

@@ -0,0 +1,95 @@
<template>
<cl-select
ref="selectRef"
v-model="size"
:title="t('全局字号')"
:options="list"
:show-trigger="false"
@changing="onChanging"
>
<template #prepend>
<view class="px-3 absolute top-0 left-0 z-10">
<cl-text
:style="{
fontSize: px2rpx(14) * size + 'rpx'
}"
>{{ t("这是一段示例文字,用于预览不同字号的效果。") }}</cl-text
>
</view>
</template>
</cl-select>
</template>
<script setup lang="ts">
import { px2rpx } from "@/cool";
import { t } from "@/locale";
import { type ClSelectOption } from "@/uni_modules/cool-ui";
import { config } from "@/uni_modules/cool-ui/config";
import { ref } from "vue";
defineOptions({
name: "size-set"
});
const selectRef = ref<ClSelectComponentPublicInstance | null>(null);
// 语言列表
const list = [
{
label: "0.9",
value: 0.9
},
{
label: t("默认 1.0"),
value: 1
},
{
label: "1.1",
value: 1.1
},
{
label: "1.2",
value: 1.2
},
{
label: "1.3",
value: 1.3
},
{
label: "1.4",
value: 1.4
}
] as ClSelectOption[];
// 当前语言
const size = ref(1);
// 是否可见
const visible = ref(false);
// 打开
function open() {
visible.value = true;
size.value = config.fontSize ?? 1;
selectRef.value!.open((value) => {
config.fontSize = value == 1 ? null : (value as number);
});
}
// 关闭
function close() {
visible.value = false;
}
// 切换
function onChanging(value: number) {
size.value = value;
}
defineExpose({
visible,
open,
close
});
</script>

File diff suppressed because one or more lines are too long

View File

@@ -711,5 +711,8 @@ export const remixicon = {
"skip-left-line": "f363", "skip-left-line": "f363",
"text-snippet": "f46e", "text-snippet": "f46e",
"input-method-line": "ee60", "input-method-line": "ee60",
"input-method-fill": "ee5f" "input-method-fill": "ee5f",
"font-size": "ed8d",
"font-size-2": "ed8c",
"font-color": "ed8b"
}; };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{ {
"name": "cool-unix", "name": "cool-unix",
"version": "8.0.7", "version": "8.0.8",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js", "build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js",

View File

@@ -1,6 +1,8 @@
<template> <template>
<view class="demo-item dark:!bg-surface-800"> <view class="demo-item dark:!bg-surface-800">
<text class="label" v-if="label != ''">{{ label }}</text> <cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2' }" v-if="label != ''">{{
label
}}</cl-text>
<slot></slot> <slot></slot>
</view> </view>
@@ -22,9 +24,5 @@ const props = defineProps({
<style lang="scss" scoped> <style lang="scss" scoped>
.demo-item { .demo-item {
@apply p-3 rounded-xl bg-white mb-3; @apply p-3 rounded-xl bg-white mb-3;
.label {
@apply text-sm text-surface-400 mb-2;
}
} }
</style> </style>

View File

@@ -36,11 +36,21 @@
}" }"
> >
<template #prev> <template #prev>
<cl-text class="!text-sm">{{ t("上一页") }}</cl-text> <cl-text
:pt="{
className: '!text-sm'
}"
>{{ t("上一页") }}</cl-text
>
</template> </template>
<template #next> <template #next>
<cl-text class="!text-sm">{{ t("下一页") }}</cl-text> <cl-text
:pt="{
className: '!text-sm'
}"
>{{ t("下一页") }}</cl-text
>
</template> </template>
</cl-pagination> </cl-pagination>
</demo-item> </demo-item>

View File

@@ -10,7 +10,6 @@
v-model="direction" v-model="direction"
:list="directionList" :list="directionList"
show-slider show-slider
fill
:height="66" :height="66"
></cl-tabs> ></cl-tabs>
</view> </view>

View File

@@ -25,6 +25,11 @@
className: parseClass({ className: parseClass({
'!text-sky-700': isCustom '!text-sky-700': isCustom
}) })
},
cursor: {
className: parseClass({
'!bg-sky-700': isCustom
})
} }
}" }"
@done="toDone" @done="toDone"

View File

@@ -54,7 +54,7 @@
className: 'justify-between' className: 'justify-between'
}, },
item: { item: {
className: '!h-8 !w-8' className: '!h-9 !w-9'
}, },
cursor: { cursor: {
className: '!h-3' className: '!h-3'

View File

@@ -10,17 +10,17 @@
<cl-row :gutter="12"> <cl-row :gutter="12">
<cl-col :span="8"> <cl-col :span="8">
<view class="item"> <view class="item">
<text class="text">1</text> <cl-text :pt="{ className: 'text' }">1</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="8"> <cl-col :span="8">
<view class="item"> <view class="item">
<text class="text">2</text> <cl-text :pt="{ className: 'text' }">2</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="8"> <cl-col :span="8">
<view class="item"> <view class="item">
<text class="text">3</text> <cl-text :pt="{ className: 'text' }">3</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
@@ -28,12 +28,12 @@
<cl-row :gutter="12" :pt="{ className: 'mt-3' }"> <cl-row :gutter="12" :pt="{ className: 'mt-3' }">
<cl-col :span="12"> <cl-col :span="12">
<view class="item"> <view class="item">
<text class="text">1</text> <cl-text :pt="{ className: 'text' }">1</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="12"> <cl-col :span="12">
<view class="item"> <view class="item">
<text class="text">2</text> <cl-text :pt="{ className: 'text' }">2</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
@@ -41,22 +41,22 @@
<cl-row :gutter="12" :pt="{ className: 'mt-3' }"> <cl-row :gutter="12" :pt="{ className: 'mt-3' }">
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">1</text> <cl-text :pt="{ className: 'text' }">1</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">2</text> <cl-text :pt="{ className: 'text' }">2</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">3</text> <cl-text :pt="{ className: 'text' }">3</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">4</text> <cl-text :pt="{ className: 'text' }">4</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
@@ -66,17 +66,17 @@
<cl-row :gutter="12"> <cl-row :gutter="12">
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">1</text> <cl-text :pt="{ className: 'text' }">1</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6" :offset="6"> <cl-col :span="6" :offset="6">
<view class="item active"> <view class="item active">
<text class="text">2</text> <cl-text :pt="{ className: 'text' }">2</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">3</text> <cl-text :pt="{ className: 'text' }">3</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
@@ -86,42 +86,42 @@
<cl-row :gutter="12"> <cl-row :gutter="12">
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">1</text> <cl-text :pt="{ className: 'text' }">1</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">2</text> <cl-text :pt="{ className: 'text' }">2</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6" :push="6"> <cl-col :span="6" :push="6">
<view class="item active"> <view class="item active">
<text class="text">3</text> <cl-text :pt="{ className: 'text' }">3</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
</demo-item> </demo-item>
<demo-item :label="t('左移动')"> <demo-item :label="t('左移动')">
<cl-row :gutter="12" :pt="{ className: 'mt-3' }"> <cl-row :gutter="12">
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">1</text> <cl-text :pt="{ className: 'text' }">1</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">2</text> <cl-text :pt="{ className: 'text' }">2</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6"> <cl-col :span="6">
<view class="item"> <view class="item">
<text class="text">3</text> <cl-text :pt="{ className: 'text' }">3</cl-text>
</view> </view>
</cl-col> </cl-col>
<cl-col :span="6" :pull="6"> <cl-col :span="6" :pull="6">
<view class="item active"> <view class="item active">
<text class="text">4</text> <cl-text :pt="{ className: 'text' }">4</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>
@@ -131,7 +131,7 @@
<cl-row :gutter="12"> <cl-row :gutter="12">
<cl-col :span="4" v-for="item in 20" :key="item"> <cl-col :span="4" v-for="item in 20" :key="item">
<view class="item mb-2"> <view class="item mb-2">
<text class="text">{{ item }}</text> <cl-text :pt="{ className: 'text' }">{{ item }}</cl-text>
</view> </view>
</cl-col> </cl-col>
</cl-row> </cl-row>

View File

@@ -2,7 +2,7 @@
<cl-page back-top> <cl-page back-top>
<cl-sticky> <cl-sticky>
<view class="bg-primary-500 p-3 h-[40px] flex flex-row items-center"> <view class="bg-primary-500 p-3 h-[40px] flex flex-row items-center">
<text class="text-white">Header</text> <cl-text color="white">Header - 1</cl-text>
</view> </view>
</cl-sticky> </cl-sticky>
@@ -14,7 +14,7 @@
<cl-sticky :offset-top="40"> <cl-sticky :offset-top="40">
<view class="bg-red-500 p-3 h-[40px] flex flex-row items-center"> <view class="bg-red-500 p-3 h-[40px] flex flex-row items-center">
<text class="text-white">Header - 2</text> <cl-text color="white">Header - 2</cl-text>
</view> </view>
</cl-sticky> </cl-sticky>
@@ -26,7 +26,7 @@
<cl-sticky :offset-top="80"> <cl-sticky :offset-top="80">
<view class="bg-purple-500 p-3 h-[40px] flex flex-row items-center"> <view class="bg-purple-500 p-3 h-[40px] flex flex-row items-center">
<text class="text-white">Header - 3</text> <cl-text color="white">Header - 3</cl-text>
</view> </view>
</cl-sticky> </cl-sticky>

View File

@@ -37,13 +37,7 @@
</cl-list-item> </cl-list-item>
<view class="p-2"> <view class="p-2">
<cl-tabs <cl-tabs v-model="mode" :height="66" :list="modeList" show-slider></cl-tabs>
v-model="mode"
:height="66"
:list="modeList"
show-slider
fill
></cl-tabs>
</view> </view>
</cl-list> </cl-list>
</demo-item> </demo-item>

View File

@@ -14,7 +14,7 @@
</demo-item> </demo-item>
<demo-item :label="t('结合按钮')"> <demo-item :label="t('结合按钮')">
<view class="flex flex-row pt-2"> <view class="flex flex-row overflow-visible">
<cl-button> <cl-button>
{{ t("购买") }} {{ t("购买") }}
<template #content> <template #content>

View File

@@ -16,9 +16,21 @@
></cl-image> ></cl-image>
</view> </view>
<text class="text-xl text-primary-500 dark:!text-white mr-auto ml-2 flex-1"> <cl-text
color="primary"
:pt="{
className: '!text-xl mr-auto ml-2 flex-1'
}"
>
{{ config.name }} {{ config.name }}
</text> </cl-text>
<view
class="bg-surface-500 h-8 w-8 rounded-full flex flex-row items-center justify-center mr-3"
@tap="setSize"
>
<cl-icon name="font-size" color="white"></cl-icon>
</view>
<view <view
class="bg-primary-500 h-8 w-8 rounded-full flex flex-row items-center justify-center" class="bg-primary-500 h-8 w-8 rounded-full flex flex-row items-center justify-center"
@@ -34,7 +46,9 @@
<view class="p-3"> <view class="p-3">
<view class="group" v-for="item in data" :key="item.label"> <view class="group" v-for="item in data" :key="item.label">
<text class="title dark:!text-surface-50">{{ item.label }}</text> <cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-1' }">{{
item.label
}}</cl-text>
<view class="list"> <view class="list">
<cl-row :gutter="10"> <cl-row :gutter="10">
@@ -60,18 +74,24 @@
</view> </view>
<tabbar></tabbar> <tabbar></tabbar>
<!-- 主题设置 -->
<locale-set :ref="refs.set('localeSet')"></locale-set> <locale-set :ref="refs.set('localeSet')"></locale-set>
<!-- 字体大小设置 -->
<size-set :ref="refs.set('sizeSet')"></size-set>
</cl-page> </cl-page>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { isMp, router, useRefs } from "@/cool"; import { isMp, router, useRefs } from "@/cool";
import LocaleSet from "@/components/locale-set.uvue";
import { t } from "@/locale"; import { t } from "@/locale";
import { computed } from "vue"; import { computed } from "vue";
import Tabbar from "@/components/tabbar.uvue";
import { useUi } from "@/uni_modules/cool-ui"; import { useUi } from "@/uni_modules/cool-ui";
import { config } from "@/config"; import { config } from "@/config";
import LocaleSet from "@/components/locale-set.uvue";
import SizeSet from "@/components/size-set.uvue";
import Tabbar from "@/components/tabbar.uvue";
const ui = useUi(); const ui = useUi();
const refs = useRefs(); const refs = useRefs();
@@ -414,20 +434,20 @@ function toPath(item: Item) {
function setLocale() { function setLocale() {
refs.open("localeSet"); refs.open("localeSet");
} }
function setSize() {
refs.open("sizeSet");
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.group { .group {
@apply mb-10; @apply mb-10;
.title {
@apply text-sm mb-2 ml-1 text-surface-500;
}
.list { .list {
.item { .item {
@apply flex flex-col items-center; @apply flex flex-col items-center;
@apply rounded-xl bg-white p-2; @apply rounded-xl bg-white px-2;
height: 140rpx; height: 140rpx;
margin-bottom: 10rpx; margin-bottom: 10rpx;
padding-top: 36rpx; padding-top: 36rpx;

View File

@@ -1,6 +1,12 @@
<template> <template>
<cl-page> <cl-page>
<cl-topbar fixed :show-back="false" safe-area-top background-color="transparent"> <cl-topbar
fixed
:height="100"
:show-back="false"
safe-area-top
background-color="transparent"
>
<view class="flex flex-row items-center w-full flex-1 px-3"> <view class="flex flex-row items-center w-full flex-1 px-3">
<view class="top-icon dark:!bg-surface-700" @tap="toSet"> <view class="top-icon dark:!bg-surface-700" @tap="toSet">
<cl-icon name="settings-line"></cl-icon> <cl-icon name="settings-line"></cl-icon>
@@ -30,7 +36,7 @@
</view> </view>
</view> </view>
<view class="flex-1 flex flex-col justify-center items-center" @tap="toEdit"> <view class="flex-1 flex flex-col justify-center items-center w-full" @tap="toEdit">
<cl-text :pt="{ className: '!text-xl mt-5 mb-1 font-bold' }">{{ <cl-text :pt="{ className: '!text-xl mt-5 mb-1 font-bold' }">{{
user.info.nickName ?? t("未登录") user.info.nickName ?? t("未登录")
}}</cl-text> }}</cl-text>
@@ -264,8 +270,6 @@ onReady(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.top-icon { .top-icon {
@apply flex items-center justify-center rounded-lg bg-white mr-3; @apply flex items-center justify-center rounded-lg bg-white mr-3 p-2;
height: 50rpx;
width: 50rpx;
} }
</style> </style>

View File

@@ -3,6 +3,7 @@
<cl-topbar safe-area-top background-color="transparent"></cl-topbar> <cl-topbar safe-area-top background-color="transparent"></cl-topbar>
<view class="px-10"> <view class="px-10">
<!-- Logo -->
<view class="flex flex-col items-center justify-center py-20"> <view class="flex flex-col items-center justify-center py-20">
<view class="p-3 bg-primary-500 rounded-2xl"> <view class="p-3 bg-primary-500 rounded-2xl">
<cl-image <cl-image
@@ -16,9 +17,13 @@
<cl-text :pt="{ className: '!text-xl font-bold mt-3' }">{{ config.name }}</cl-text> <cl-text :pt="{ className: '!text-xl font-bold mt-3' }">{{ config.name }}</cl-text>
</view> </view>
<!-- 手机号登录 -->
<login-phone :form="form" @success="toLogin"></login-phone> <login-phone :form="form" @success="toLogin"></login-phone>
<!-- 微信登录 -->
<login-wx :ref="refs.set('loginWx')" @success="toLogin"></login-wx> <login-wx :ref="refs.set('loginWx')" @success="toLogin"></login-wx>
<!-- 协议 -->
<view class="mt-6 flex flex-row flex-wrap items-center justify-center"> <view class="mt-6 flex flex-row flex-wrap items-center justify-center">
<cl-checkbox <cl-checkbox
v-model="agree" v-model="agree"
@@ -45,17 +50,19 @@
</cl-text> </cl-text>
</view> </view>
<!-- 第三方登录 -->
<view class="flex flex-row justify-center mt-10 px-10"> <view class="flex flex-row justify-center mt-10 px-10">
<view <view
class="mx-5flex items-center justify-center h-10 w-10 rounded-full bg-white dark:bg-surface-700 border border-solid border-surface-100 dark:border-surface-600" class="login-item"
:class="{
'is-dark': isDark
}"
@tap="refs.callMethod('loginWx', 'login')" @tap="refs.callMethod('loginWx', 'login')"
> >
<cl-icon name="wechat-fill" :size="38" color="#00b223"></cl-icon> <cl-icon name="wechat-fill" :size="38" color="#00b223"></cl-icon>
</view> </view>
<view <view class="login-item" :class="{ 'is-dark': isDark }">
class="mx-5 flex items-center justify-center h-10 w-10 rounded-full bg-white dark:bg-surface-700 border border-solid border-surface-100 dark:border-surface-600"
>
<cl-icon name="apple-fill" :size="38"></cl-icon> <cl-icon name="apple-fill" :size="38"></cl-icon>
</view> </view>
</view> </view>
@@ -65,7 +72,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { config } from "@/config"; import { config } from "@/config";
import { parse, router, useRefs, useStore, type Token } from "@/cool"; import { isDark, parse, router, useRefs, useStore, type Token } from "@/cool";
import { t } from "@/locale"; import { t } from "@/locale";
import { provide, reactive, ref } from "vue"; import { provide, reactive, ref } from "vue";
import type { LoginForm } from "./types"; import type { LoginForm } from "./types";
@@ -110,3 +117,13 @@ function isAgree() {
provide("isAgree", isAgree); provide("isAgree", isAgree);
</script> </script>
<style lang="scss" scoped>
.login-item {
@apply mx-2 p-2 flex items-center justify-center rounded-full bg-white border border-solid border-surface-100;
&.is-dark {
@apply border-surface-600 bg-surface-700;
}
}
</style>

View File

@@ -15,7 +15,12 @@
<slot name="prepend"></slot> <slot name="prepend"></slot>
<view class="cl-action-sheet__description" v-if="config.description != ''"> <view class="cl-action-sheet__description" v-if="config.description != ''">
<text class="text-surface-400 text-md">{{ config.description }}</text> <cl-text
:pt="{
className: '!text-surface-400 !text-md text-center'
}"
>{{ config.description }}</cl-text
>
</view> </view>
<view class="cl-action-sheet__list" :class="[pt.list?.className]"> <view class="cl-action-sheet__list" :class="[pt.list?.className]">

View File

@@ -13,10 +13,16 @@
}, },
pt.className pt.className
]" ]"
:style="badgeStyle"
> >
<text class="cl-badge__text" v-if="!dot"> <cl-text
:pt="{
className: parseClass(['cl-badge__text', pt.text?.className])
}"
v-if="!dot"
>
{{ value }} {{ value }}
</text> </cl-text>
<slot></slot> <slot></slot>
</view> </view>
@@ -25,8 +31,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue"; import { computed } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
import type { Type } from "../../types"; import type { PassThroughProps, Type } from "../../types";
import { parsePt } from "@/cool"; import { parseClass, parsePt } from "@/cool";
import { useSize } from "../../hooks";
defineOptions({ defineOptions({
name: "cl-badge" name: "cl-badge"
@@ -55,11 +62,39 @@ const props = defineProps({
} }
}); });
const { getRpx } = useSize();
type PassThrough = { type PassThrough = {
className?: string; className?: string;
text?: PassThroughProps;
}; };
const pt = computed(() => parsePt<PassThrough>(props.pt)); const pt = computed(() => parsePt<PassThrough>(props.pt));
const badgeStyle = computed(() => {
const style = {};
if (props.dot) {
style["height"] = getRpx(10);
style["width"] = getRpx(10);
style["minWidth"] = getRpx(10);
style["padding"] = 0;
} else {
style["height"] = getRpx(30);
style["minWidth"] = getRpx(30);
style["padding"] = `0 ${getRpx(6)}`;
}
if (props.position) {
style["transform"] = "translate(50%, -50%)";
if (props.dot) {
style["transform"] = `translate(-${getRpx(5)}, ${getRpx(5)})`;
}
}
return style;
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -67,29 +102,12 @@ const pt = computed(() => parsePt<PassThrough>(props.pt));
@apply flex flex-row items-center justify-center; @apply flex flex-row items-center justify-center;
@apply rounded-full; @apply rounded-full;
height: 30rpx;
min-width: 30rpx;
padding: 0 6rpx;
&__text { &__text {
@apply text-white; @apply text-white text-sm;
font-size: 18rpx;
}
&--dot {
height: 10rpx;
width: 10rpx;
min-width: 10rpx;
padding: 0;
} }
&--position { &--position {
@apply absolute z-10 right-0 top-0; @apply absolute z-10 right-0 top-0;
transform: translate(50%, -50%);
&.cl-badge--dot {
transform: translate(-5rpx, 5rpx);
}
} }
} }
</style> </style>

View File

@@ -18,13 +18,24 @@
v-if="showIcon" v-if="showIcon"
></image> ></image>
<text class="cl-empty__text dark:!text-surface-100" v-if="text">{{ text }}</text> <cl-text
:pt="{
className: parseClass([
'cl-empty__text',
{
'!text-surface-100': isDark
}
])
}"
v-if="text"
>{{ text }}</cl-text
>
</view> </view>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { t } from "@/locale"; import { t } from "@/locale";
import { parsePt, parseRpx } from "@/cool"; import { isDark, parseClass, parsePt, parseRpx } from "@/cool";
import { computed } from "vue"; import { computed } from "vue";
// 组件属性定义 // 组件属性定义

View File

@@ -24,8 +24,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, type PropType } from "vue"; import { computed, type PropType } from "vue";
import { forInObject, get, has, parseRpx, parsePt, useCache, isDark } from "@/cool"; import { forInObject, get, has, parsePt, useCache, isDark } from "@/cool";
import { icons } from "@/icons"; import { icons } from "@/icons";
import { useSize } from "../../hooks";
defineOptions({ defineOptions({
name: "cl-icon" name: "cl-icon"
@@ -68,6 +69,9 @@ const props = defineProps({
// 缓存 // 缓存
const { cache } = useCache(() => [props.color]); const { cache } = useCache(() => [props.color]);
// 字号
const { getRpx } = useSize();
// 透传样式类型定义 // 透传样式类型定义
type PassThrough = { type PassThrough = {
className?: string; className?: string;
@@ -126,11 +130,14 @@ const iconStyle = computed(() => {
style["color"] = props.color; style["color"] = props.color;
} }
style["fontFamily"] = icon.value.font; if (icon.value.font != "") {
style["fontSize"] = parseRpx(props.size!); style["fontFamily"] = icon.value.font;
style["height"] = parseRpx(props.height ?? props.size!); }
style["width"] = parseRpx(props.width ?? props.size!);
style["lineHeight"] = parseRpx(props.size!); style["fontSize"] = getRpx(props.size!);
style["height"] = getRpx(props.height ?? props.size!);
style["width"] = getRpx(props.width ?? props.size!);
style["lineHeight"] = getRpx(props.size!);
return style; return style;
}); });

View File

@@ -1,5 +1,13 @@
<template> <template>
<view class="cl-input-otp" :class="[pt.className]"> <view
class="cl-input-otp"
:class="[
{
'cl-input-otp--disabled': disabled
},
pt.className
]"
>
<view class="cl-input-otp__inner"> <view class="cl-input-otp__inner">
<cl-input <cl-input
v-model="value" v-model="value"
@@ -33,16 +41,11 @@
pt.item?.className pt.item?.className
]" ]"
> >
<text <cl-text
class="cl-input-otp__value" :pt="{
:class="[ className: pt.value?.className
{ }"
'is-disabled': disabled, >{{ item }}</cl-text
'is-dark': isDark
},
pt.value?.className
]"
>{{ item }}</text
> >
<view <view
class="cl-input-otp__cursor" class="cl-input-otp__cursor"
@@ -268,14 +271,6 @@ function animateCursor(isIncreasing: boolean) {
} }
} }
&__value {
@apply text-surface-700 text-md;
&.is-dark {
@apply text-white;
}
}
&__cursor { &__cursor {
@apply absolute; @apply absolute;
@apply bg-primary-500; @apply bg-primary-500;
@@ -302,5 +297,9 @@ function animateCursor(isIncreasing: boolean) {
} }
} }
// #endif // #endif
&--disabled {
@apply opacity-50;
}
} }
</style> </style>

View File

@@ -30,8 +30,9 @@
'is-disabled': isDisabled, 'is-disabled': isDisabled,
'is-dark': isDark 'is-dark': isDark
}, },
pt.inner?.className ptClassName
]" ]"
:style="inputStyle"
:value="value" :value="value"
:disabled="readonly ?? isDisabled" :disabled="readonly ?? isDisabled"
:type="type" :type="type"
@@ -86,7 +87,7 @@ import type { ClInputType, PassThroughProps } from "../../types";
import { isDark, parseClass, parsePt } from "@/cool"; import { isDark, parseClass, parsePt } from "@/cool";
import type { ClIconProps } from "../cl-icon/props"; import type { ClIconProps } from "../cl-icon/props";
import { t } from "@/locale"; import { t } from "@/locale";
import { useForm, useFormItem } from "../../hooks"; import { useForm, useFormItem, useSize } from "../../hooks";
defineOptions({ defineOptions({
name: "cl-input" name: "cl-input"
@@ -225,6 +226,22 @@ type PassThrough = {
// 解析透传样式 // 解析透传样式
const pt = computed(() => parsePt<PassThrough>(props.pt)); const pt = computed(() => parsePt<PassThrough>(props.pt));
// 字号
const { ptClassName, getSize } = useSize(() => pt.value.inner?.className ?? "");
// 输入框样式
const inputStyle = computed(() => {
const style = {};
// 字号
const fontSize = getSize(null);
if (fontSize != null) {
style["fontSize"] = fontSize;
}
return style;
});
// 绑定值 // 绑定值
const value = ref<string>(""); const value = ref<string>("");

View File

@@ -22,10 +22,21 @@
class="cl-keyboard-car__value" class="cl-keyboard-car__value"
:class="[pt.value?.className]" :class="[pt.value?.className]"
> >
<text class="cl-keyboard-car__value-text dark:!text-white" v-if="value != ''">{{ <cl-text
valueText v-if="value != ''"
}}</text> :pt="{
<text class="cl-keyboard-car__value-placeholder" v-else>{{ placeholder }}</text> className: '!text-2xl'
}"
>{{ valueText }}</cl-text
>
<cl-text
v-else
:pt="{
className: '!text-md !text-surface-400'
}"
>{{ placeholder }}</cl-text
>
</view> </view>
</slot> </slot>
@@ -70,9 +81,7 @@
color="white" color="white"
></cl-icon> ></cl-icon>
<text v-else class="cl-keyboard-car__item-text dark:!text-white">{{ <cl-text v-else>{{ item }}</cl-text>
item
}}</text>
</slot> </slot>
</view> </view>
</view> </view>
@@ -289,16 +298,8 @@ defineExpose({
&__value { &__value {
@apply flex flex-row items-center justify-center; @apply flex flex-row items-center justify-center;
height: 60rpx; height: 80rpx;
margin-bottom: 28rpx; margin-bottom: 20rpx;
&-text {
@apply text-2xl text-surface-700;
}
&-placeholder {
@apply text-md text-surface-400;
}
} }
&__list { &__list {
@@ -327,10 +328,6 @@ defineExpose({
@apply bg-surface-800; @apply bg-surface-800;
} }
&-text {
@apply text-lg;
}
&.is-fill { &.is-fill {
flex: 1; flex: 1;
} }
@@ -347,10 +344,6 @@ defineExpose({
&.is-keycode-confirm { &.is-keycode-confirm {
@apply bg-primary-500; @apply bg-primary-500;
flex: 1; flex: 1;
.cl-keyboard-car__item-text {
@apply text-white;
}
} }
&.is-empty { &.is-empty {

View File

@@ -22,14 +22,21 @@
class="cl-keyboard-number__value" class="cl-keyboard-number__value"
:class="[pt.value?.className]" :class="[pt.value?.className]"
> >
<text <cl-text
class="cl-keyboard-number__value-text dark:!text-white"
v-if="value != ''" v-if="value != ''"
>{{ value }}</text :pt="{
className: '!text-2xl'
}"
>{{ value }}</cl-text
>
<cl-text
v-else
:pt="{
className: '!text-md !text-surface-400'
}"
>{{ placeholder }}</cl-text
> >
<text class="cl-keyboard-number__value-placeholder" v-else>{{
placeholder
}}</text>
</view> </view>
</slot> </slot>
@@ -61,15 +68,21 @@
v-else-if="item == 'confirm'" v-else-if="item == 'confirm'"
class="cl-keyboard-number__item-confirm" class="cl-keyboard-number__item-confirm"
> >
<text class="cl-keyboard-number__item-text">{{ <cl-text
confirmText color="white"
}}</text> :pt="{
className: '!text-lg'
}"
>{{ confirmText }}</cl-text
>
</view> </view>
<text <cl-text
v-else v-else
class="cl-keyboard-number__item-text dark:!text-white" :pt="{
>{{ item }}</text className: '!text-lg'
}"
>{{ item }}</cl-text
> >
</slot> </slot>
</view> </view>
@@ -346,16 +359,8 @@ defineExpose({
&__value { &__value {
@apply flex flex-row items-center justify-center; @apply flex flex-row items-center justify-center;
height: 60rpx; height: 80rpx;
margin-bottom: 28rpx; margin-bottom: 20rpx;
&-text {
@apply text-2xl text-surface-700;
}
&-placeholder {
@apply text-md text-surface-400;
}
} }
&__list { &__list {
@@ -372,10 +377,6 @@ defineExpose({
@apply bg-surface-800; @apply bg-surface-800;
} }
&-text {
@apply text-lg;
}
&.is-keycode-delete { &.is-keycode-delete {
@apply bg-surface-200; @apply bg-surface-200;
@@ -386,10 +387,6 @@ defineExpose({
&.is-keycode-confirm { &.is-keycode-confirm {
@apply bg-transparent relative; @apply bg-transparent relative;
.cl-keyboard-number__item-text {
@apply text-white;
}
} }
&-confirm { &-confirm {

View File

@@ -22,14 +22,21 @@
class="cl-keyboard-password__value" class="cl-keyboard-password__value"
:class="[pt.value?.className]" :class="[pt.value?.className]"
> >
<text <cl-text
class="cl-keyboard-password__value-text dark:!text-white"
v-if="value != ''" v-if="value != ''"
>{{ valueText }}</text :pt="{
className: '!text-2xl'
}"
>{{ valueText }}</cl-text
>
<cl-text
v-else
:pt="{
className: '!text-md !text-surface-400'
}"
>{{ placeholder }}</cl-text
> >
<text class="cl-keyboard-password__value-placeholder" v-else>{{
placeholder
}}</text>
</view> </view>
</slot> </slot>
@@ -66,22 +73,29 @@
:size="36" :size="36"
></cl-icon> ></cl-icon>
<text <cl-text
v-else-if="item == 'confirm'" v-else-if="item == 'confirm'"
class="cl-keyboard-password__item-text" color="white"
>{{ confirmText }}</text :pt="{
className: '!text-lg'
}"
>{{ confirmText }}</cl-text
> >
<text <cl-text
v-else-if="item == 'letter'" v-else-if="item == 'letter'"
class="cl-keyboard-password__item-text dark:!text-white" :pt="{
>ABC</text className: '!text-lg'
}"
>ABC</cl-text
> >
<text <cl-text
v-else-if="item == 'number'" v-else-if="item == 'number'"
class="cl-keyboard-password__item-text dark:!text-white" :pt="{
>123</text className: '!text-lg'
}"
>123</cl-text
> >
<template v-else-if="item == 'caps'"> <template v-else-if="item == 'caps'">
@@ -97,9 +111,13 @@
></cl-badge> ></cl-badge>
</template> </template>
<text v-else class="cl-keyboard-password__item-text dark:!text-white">{{ <cl-text
item v-else
}}</text> :pt="{
className: '!text-lg'
}"
>{{ item }}</cl-text
>
</slot> </slot>
</view> </view>
</view> </view>
@@ -364,16 +382,8 @@ defineExpose({
&__value { &__value {
@apply flex flex-row items-center justify-center; @apply flex flex-row items-center justify-center;
height: 60rpx; height: 80rpx;
margin-bottom: 28rpx; margin-bottom: 20rpx;
&-text {
@apply text-2xl text-surface-700;
}
&-placeholder {
@apply text-md text-surface-400;
}
} }
&__list { &__list {
@@ -403,10 +413,6 @@ defineExpose({
@apply bg-surface-800; @apply bg-surface-800;
} }
&-text {
@apply text-lg bg-transparent;
}
&.is-keycode-number, &.is-keycode-number,
&.is-keycode-letter { &.is-keycode-letter {
width: 150rpx; width: 150rpx;
@@ -434,10 +440,6 @@ defineExpose({
@apply bg-primary-500; @apply bg-primary-500;
width: 150rpx !important; width: 150rpx !important;
flex: none; flex: none;
.cl-keyboard-password__item-text {
@apply text-white;
}
} }
&.is-empty { &.is-empty {

View File

@@ -21,8 +21,9 @@
// #ifdef APP // #ifdef APP
transform: `rotate(${rotate}deg)`, transform: `rotate(${rotate}deg)`,
// #endif // #endif
height: parseRpx(size!), height: getRpx(size!),
width: parseRpx(size!), width: getRpx(size!),
borderWidth: getRpx(2),
borderTopColor: color, borderTopColor: color,
borderRightColor: 'transparent', borderRightColor: 'transparent',
borderBottomColor: color, borderBottomColor: color,
@@ -35,8 +36,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue"; import { computed, onMounted, ref, watch } from "vue";
import { isDark, parsePt, parseRpx } from "@/cool"; import { isDark, parsePt } from "@/cool";
import type { ClIconProps } from "../cl-icon/props"; import type { ClIconProps } from "../cl-icon/props";
import { useSize } from "../../hooks";
defineOptions({ defineOptions({
name: "cl-loading" name: "cl-loading"
@@ -66,6 +68,8 @@ const props = defineProps({
} }
}); });
const { getRpx } = useSize();
// 透传样式类型定义 // 透传样式类型定义
type PassThrough = { type PassThrough = {
className?: string; className?: string;
@@ -114,7 +118,6 @@ onMounted(() => {
.cl-loading { .cl-loading {
@apply flex flex-row items-center justify-center rounded-full; @apply flex flex-row items-center justify-center rounded-full;
@apply border-surface-700 border-solid; @apply border-surface-700 border-solid;
border-width: 2rpx;
&--dark { &--dark {
border-color: white !important; border-color: white !important;

View File

@@ -16,10 +16,11 @@
}" }"
> >
<slot name="text" :item="item"> <slot name="text" :item="item">
<text <cl-text
class="cl-noticebar__text dark:!text-white" :pt="{
:class="[pt.text?.className]" className: parseClass(['cl-noticebar__text', pt.text?.className])
>{{ item }}</text }"
>{{ item }}</cl-text
> >
</slot> </slot>
</view> </view>
@@ -37,7 +38,7 @@ import {
type PropType, type PropType,
watch watch
} from "vue"; } from "vue";
import { parseRpx, parsePt } from "@/cool"; import { parseRpx, parsePt, parseClass } from "@/cool";
import type { PassThroughProps } from "../../types"; import type { PassThroughProps } from "../../types";
defineOptions({ defineOptions({
@@ -248,7 +249,6 @@ onUnmounted(() => {
} }
&__text { &__text {
@apply text-md text-surface-700;
white-space: nowrap; white-space: nowrap;
} }
} }

View File

@@ -30,13 +30,17 @@
]" ]"
@tap="toPage(item)" @tap="toPage(item)"
> >
<text <cl-text
class="cl-pagination__item-text" :pt="{
:class="{ className: parseClass([
'is-active': item == value, 'cl-pagination__item-text',
'is-dark': isDark {
'is-active': item == value,
'is-dark': isDark
}
])
}" }"
>{{ item }}</text >{{ item }}</cl-text
> >
</view> </view>
@@ -61,7 +65,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { PassThroughProps } from "../../types"; import type { PassThroughProps } from "../../types";
import { isDark, parsePt } from "@/cool"; import { isDark, parseClass, parsePt } from "@/cool";
import { computed, ref, watch } from "vue"; import { computed, ref, watch } from "vue";
defineOptions({ defineOptions({

View File

@@ -1,11 +1,13 @@
<template> <template>
<view class="cl-picker-view"> <view class="cl-picker-view">
<view class="cl-picker-view__header" v-if="headers.length > 0"> <view class="cl-picker-view__header" v-if="headers.length > 0">
<text <cl-text
class="cl-picker-view__header-item dark:!text-white" :pt="{
className: 'flex-1 !text-sm text-center'
}"
v-for="(label, index) in headers" v-for="(label, index) in headers"
:key="index" :key="index"
>{{ label }}</text >{{ label }}</cl-text
> >
</view> </view>
@@ -168,11 +170,6 @@ function onChange(e: UniPickerViewChangeEvent) {
&__header { &__header {
@apply flex flex-row items-center py-4; @apply flex flex-row items-center py-4;
&-item {
@apply text-center text-sm text-surface-700;
flex: 1;
}
} }
&__item { &__item {

View File

@@ -252,7 +252,7 @@ const paddingBottom = computed(() => {
let h = 0; let h = 0;
if (props.direction == "bottom") { if (props.direction == "bottom") {
h += hasCustomTabBar() ? getTabBarHeight() : getSafeAreaHeight("bottom"); h += getSafeAreaHeight("bottom");
} }
return h + "px"; return h + "px";

View File

@@ -28,13 +28,14 @@
{{ text }} {{ text }}
</cl-text> </cl-text>
<text <cl-text
class="cl-select-trigger__placeholder" :pt="{
:class="[pt.placeholder?.className]" className: parseClass(['!text-surface-400', pt.placeholder?.className])
}"
v-else v-else
> >
{{ placeholder }} {{ placeholder }}
</text> </cl-text>
</view> </view>
<view v-if="showText && !isDisabled" class="cl-select-trigger__icon" @tap.stop="clear"> <view v-if="showText && !isDisabled" class="cl-select-trigger__icon" @tap.stop="clear">
@@ -152,10 +153,6 @@ function open() {
height: 66rpx; height: 66rpx;
padding: 0 20rpx; padding: 0 20rpx;
&__placeholder {
@apply text-surface-400 text-md;
}
&__content { &__content {
flex: 1; flex: 1;
} }

View File

@@ -26,6 +26,8 @@
}" }"
> >
<view class="cl-select-popup" @touchmove.stop> <view class="cl-select-popup" @touchmove.stop>
<slot name="prepend"></slot>
<view class="cl-select-popup__picker"> <view class="cl-select-popup__picker">
<cl-picker-view <cl-picker-view
:value="indexes" :value="indexes"
@@ -34,6 +36,8 @@
></cl-picker-view> ></cl-picker-view>
</view> </view>
<slot name="append"></slot>
<view class="cl-select-popup__op"> <view class="cl-select-popup__op">
<cl-button <cl-button
v-if="showCancel" v-if="showCancel"
@@ -73,6 +77,11 @@ defineOptions({
name: "cl-select" name: "cl-select"
}); });
defineSlots<{
prepend(): any;
append(): any;
}>();
// 值类型 // 值类型
type Value = string[] | number[] | number | string | null; type Value = string[] | number[] | number | string | null;
@@ -146,7 +155,7 @@ const props = defineProps({
}); });
// 定义事件 // 定义事件
const emit = defineEmits(["update:modelValue", "change"]); const emit = defineEmits(["update:modelValue", "change", "changing"]);
// 弹出层引用 // 弹出层引用
const popupRef = ref<ClPopupComponentPublicInstance | null>(null); const popupRef = ref<ClPopupComponentPublicInstance | null>(null);
@@ -230,6 +239,11 @@ const text = computed(() => {
.join(props.splitor); .join(props.splitor);
}); });
// 获取当前选中值
function getValue() {
return props.columnCount == 1 ? value.value[0] : value.value;
}
// 选择器值改变事件 // 选择器值改变事件
function onChange(a: number[]) { function onChange(a: number[]) {
// 复制当前组件内部维护的索引数组 // 复制当前组件内部维护的索引数组
@@ -258,6 +272,9 @@ function onChange(a: number[]) {
indexes.value = b; indexes.value = b;
// 根据最新的索引数组,更新选中的值数组 // 根据最新的索引数组,更新选中的值数组
value.value = b.map((e, i) => columns.value[i][e].value); value.value = b.map((e, i) => columns.value[i][e].value);
// 触发changing事件
emit("changing", getValue());
} }
// 选择器显示状态 // 选择器显示状态
@@ -291,7 +308,7 @@ function clear() {
// 确认选择 // 确认选择
function confirm() { function confirm() {
// 根据列数返回单个值或数组 // 根据列数返回单个值或数组
const val = props.columnCount == 1 ? value.value[0] : value.value; const val = getValue();
// 触发更新事件 // 触发更新事件
emit("update:modelValue", val); emit("update:modelValue", val);

View File

@@ -35,19 +35,21 @@
@tap="change(index)" @tap="change(index)"
> >
<slot name="item" :item="item" :active="item.isActive"> <slot name="item" :item="item" :active="item.isActive">
<text <cl-text
class="cl-tabs__item-label" :pt="{
:class="[ className: parseClass([
{ 'cl-tabs__item-label',
'is-active': item.isActive, {
'is-disabled': item.disabled, 'is-active': item.isActive,
'is-dark': isDark, 'is-disabled': item.disabled,
'is-fill': fill 'is-dark': isDark,
}, 'is-fill': fill
pt.text?.className },
]" pt.text?.className
])
}"
:style="getTextStyle(item)" :style="getTextStyle(item)"
>{{ item.label }}</text >{{ item.label }}</cl-text
> >
</slot> </slot>
</view> </view>
@@ -73,7 +75,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { type PropType, computed, getCurrentInstance, nextTick, onMounted, ref, watch } from "vue"; import { type PropType, computed, getCurrentInstance, nextTick, onMounted, ref, watch } from "vue";
import { isDark, isEmpty, isHarmony, isNull, parsePt, parseRpx, rpx2px } from "@/cool"; import { isDark, isEmpty, isHarmony, isNull, parseClass, parsePt, parseRpx, rpx2px } from "@/cool";
import type { ClTabsItem, PassThroughProps } from "../../types"; import type { ClTabsItem, PassThroughProps } from "../../types";
// 定义标签类型 // 定义标签类型

View File

@@ -18,7 +18,7 @@
'!text-surface-50': color == 'light', '!text-surface-50': color == 'light',
'!text-surface-400': color == 'disabled' '!text-surface-400': color == 'disabled'
}, },
pt.className ptClassName
]" ]"
:style="textStyle" :style="textStyle"
:selectable="selectable" :selectable="selectable"
@@ -49,7 +49,7 @@
'!text-surface-50': color == 'light', '!text-surface-50': color == 'light',
'!text-surface-400': color == 'disabled' '!text-surface-400': color == 'disabled'
}, },
pt.className ptClassName
]" ]"
:style="textStyle" :style="textStyle"
:selectable="selectable" :selectable="selectable"
@@ -66,6 +66,7 @@
import { computed, type PropType } from "vue"; import { computed, type PropType } from "vue";
import { isDark, parsePt, useCache } from "@/cool"; import { isDark, parsePt, useCache } from "@/cool";
import type { ClTextType } from "../../types"; import type { ClTextType } from "../../types";
import { useSize } from "../../hooks";
defineOptions({ defineOptions({
name: "cl-text" name: "cl-text"
@@ -88,6 +89,11 @@ const props = defineProps({
type: String, type: String,
default: "" default: ""
}, },
// 字体大小
size: {
type: [Number, String] as PropType<number | string | null>,
default: null
},
// 文本类型 // 文本类型
type: { type: {
type: String as PropType<ClTextType>, type: String as PropType<ClTextType>,
@@ -161,6 +167,9 @@ type PassThrough = {
// 解析透传样式 // 解析透传样式
const pt = computed(() => parsePt<PassThrough>(props.pt)); const pt = computed(() => parsePt<PassThrough>(props.pt));
// 文本大小
const { getSize, getLineHeight, ptClassName } = useSize(() => pt.value.className ?? "");
// 文本样式 // 文本样式
const textStyle = computed(() => { const textStyle = computed(() => {
const style = {}; const style = {};
@@ -169,6 +178,18 @@ const textStyle = computed(() => {
style["color"] = props.color; style["color"] = props.color;
} }
// 字号
const fontSize = getSize(props.size);
if (fontSize != null) {
style["fontSize"] = fontSize;
}
// 行高
const lineHeight = getLineHeight();
if (lineHeight != null) {
style["lineHeight"] = lineHeight;
}
return style; return style;
}); });

View File

@@ -20,11 +20,9 @@
'is-disabled': isDisabled, 'is-disabled': isDisabled,
'is-dark': isDark 'is-dark': isDark
}, },
pt.inner?.className ptClassName
]" ]"
:style="{ :style="textareaStyle"
height: parseRpx(height)
}"
:value="value" :value="value"
:name="name" :name="name"
:disabled="readonly ?? isDisabled" :disabled="readonly ?? isDisabled"
@@ -48,12 +46,14 @@
@input="onInput" @input="onInput"
@linechange="onLineChange" @linechange="onLineChange"
@blur="onBlur" @blur="onBlur"
@keyboardheightchange="onKeyboardheightchange"
@focus="onFocus" @focus="onFocus"
@keyboardheightchange="onKeyboardheightchange"
/> />
<text class="cl-textarea__count" v-if="showWordLimit" <cl-text
>{{ value.length }}/{{ maxlength }}</text :pt="{ className: 'absolute right-2 bottom-2 !text-xs !text-surface-400' }"
v-if="showWordLimit"
>{{ value.length }} / {{ maxlength }}</cl-text
> >
</view> </view>
</template> </template>
@@ -64,7 +64,7 @@ import { parsePt, parseRpx } from "@/cool";
import type { PassThroughProps } from "../../types"; import type { PassThroughProps } from "../../types";
import { isDark } from "@/cool"; import { isDark } from "@/cool";
import { t } from "@/locale"; import { t } from "@/locale";
import { useForm, useFormItem } from "../../hooks"; import { useForm, useFormItem, useSize } from "../../hooks";
defineOptions({ defineOptions({
name: "cl-textarea" name: "cl-textarea"
@@ -243,6 +243,24 @@ type PassThrough = {
// 解析透传样式 // 解析透传样式
const pt = computed(() => parsePt<PassThrough>(props.pt)); const pt = computed(() => parsePt<PassThrough>(props.pt));
// 字号
const { ptClassName, getSize } = useSize(() => pt.value.inner?.className ?? "");
// 文本框样式
const textareaStyle = computed(() => {
const style = {
height: parseRpx(props.height)
};
// 字号
const fontSize = getSize(null);
if (fontSize != null) {
style["fontSize"] = fontSize;
}
return style;
});
// 绑定值 // 绑定值
const value = ref(props.modelValue); const value = ref(props.modelValue);
@@ -348,10 +366,6 @@ defineExpose({
padding-left: 20rpx; padding-left: 20rpx;
} }
&__count {
@apply text-surface-400 text-sm absolute right-2 bottom-2;
}
&--border { &--border {
@apply border border-solid border-surface-200; @apply border border-solid border-surface-200;
} }

View File

@@ -37,7 +37,13 @@
></cl-icon> ></cl-icon>
</view> </view>
<text class="cl-toast__text">{{ item.message }}</text> <cl-text
color="white"
:pt="{
className: 'text-center'
}"
>{{ item.message }}</cl-text
>
</view> </view>
</view> </view>
</cl-popup> </cl-popup>
@@ -157,10 +163,6 @@ defineExpose({
max-width: 600rpx; max-width: 600rpx;
opacity: 0; opacity: 0;
&__text {
@apply text-md text-center font-bold text-white;
}
&.is-open { &.is-open {
opacity: 1; opacity: 1;
} }

View File

@@ -31,7 +31,7 @@
<cl-text <cl-text
:color="color" :color="color"
:pt="{ :pt="{
className: parseClass(['!text-[16px]', pt.title?.className]) className: parseClass(['!text-md', pt.title?.className])
}" }"
> >
{{ title }} {{ title }}

View File

@@ -43,7 +43,7 @@
<view <view
v-if="isAdd" v-if="isAdd"
class="cl-upload" class="cl-upload is-add"
:class="[ :class="[
{ {
'is-dark': isDark, 'is-dark': isDark,
@@ -109,7 +109,7 @@ const props = defineProps({
// 上传按钮显示的文本 // 上传按钮显示的文本
text: { text: {
type: String, type: String,
default: () => t("上传/拍摄") default: () => t("上传 / 拍摄")
}, },
// 图片压缩方式original原图compressed压缩图 // 图片压缩方式original原图compressed压缩图
sizeType: { sizeType: {
@@ -421,6 +421,10 @@ watch(
@apply opacity-50; @apply opacity-50;
} }
&.is-add {
@apply p-1;
}
&__image { &__image {
@apply w-full h-full absolute top-0 left-0; @apply w-full h-full absolute top-0 left-0;
transition-property: opacity; transition-property: opacity;

View File

@@ -1,11 +1,15 @@
import { reactive } from "vue";
type Config = { type Config = {
fontSize: number | null;
zIndex: number; zIndex: number;
startDate: string; startDate: string;
endDate: string; endDate: string;
}; };
export const config: Config = { export const config = reactive<Config>({
fontSize: null,
zIndex: 600, zIndex: 600,
startDate: "2000-01-01 00:00:00", startDate: "2000-01-01 00:00:00",
endDate: "2050-12-31 23:59:59" endDate: "2050-12-31 23:59:59"
}; });

View File

@@ -1,4 +1,5 @@
export * from "./component"; export * from "./component";
export * from "./form"; export * from "./form";
export * from "./page"; export * from "./page";
export * from "./size";
export * from "./ui"; export * from "./ui";

View File

@@ -0,0 +1,130 @@
import { computed, type ComputedRef } from "vue";
import { config } from "../config";
import { px2rpx } from "@/cool";
/**
* 字号管理类
* 用于处理文本大小的缩放和样式
*/
class Size {
// 预设的字号类名
public names = [
"text-xs",
"text-sm",
"text-md",
"text-lg",
"text-xl",
"text-2xl",
"text-3xl",
"text-4xl",
"text-5xl",
"text-6xl",
"text-7xl",
"text-8xl",
"text-9xl"
];
// 对应的字号大小(px)
public sizes = [10, 12, 14, 16, 18, 22, 28, 34, 46, 58, 70, 94, 126];
// 对应的行高(px)
public lineHeights = [14, 18, 22, 26, 26, 1, 1, 1, 1, 1, 1, 1, 1];
// 原始类名
public className: ComputedRef<string> = computed(() => "");
// 计算后的类名
public ptClassName: ComputedRef<string>;
constructor(cb: (() => string) | null) {
this.className = computed(cb ?? (() => ""));
// 根据全局字号配置动态计算类名
this.ptClassName = computed(() => {
if (config.fontSize == null) {
return this.className.value;
}
const name = this.names[this.getIndex()];
return this.className.value.replace(`-important-${name}`, "").replace(name, "");
});
}
/**
* 获取全局字号缩放比例
*/
getScale = () => {
return config.fontSize ?? 1;
};
/**
* 根据缩放比例计算rpx值
* @param val - 需要转换的值
*/
getRpx = (val: number | string) => {
const scale = this.getScale();
if (typeof val == "number") {
return val * scale + "rpx";
} else {
const num = parseFloat(val);
const unit = val.replace(`${num}`, "");
return num * scale + unit;
}
};
/**
* 获取当前字号在预设中的索引
*/
getIndex = () => {
let index = this.names.findIndex((name) => {
if (this.className.value.includes(name)) {
return true;
}
return false;
});
// 默认使用 text-md (14px)
if (index < 0) {
index = 2;
}
return index;
};
/**
* 获取最终的字号大小
* @param size - 指定字号大小,为空则使用预设值
*/
getSize = (size: number | string | null): null | string => {
// 未设置全局字号时返回null
if (config.fontSize == null) {
return null;
}
return this.getRpx(size ?? px2rpx(this.sizes[this.getIndex()]));
};
/**
* 获取当前行高
*/
getLineHeight = (): null | string => {
// 未设置全局字号时返回null
if (config.fontSize == null) {
return null;
}
const lineHeight = this.lineHeights[this.getIndex()];
return lineHeight == 1 ? `1` : this.getRpx(px2rpx(lineHeight));
};
}
/**
* 字号管理Hook
* @param className - 类名
*/
export function useSize(cb: (() => string) | null = null): Size {
return new Size(cb);
}

View File

@@ -6,9 +6,9 @@ import Build from "android.os.Build";
/** /**
* 震动 * 震动
* @param {number} duriation 震动时间单位ms * @param {number} duration 震动时间单位ms
*/ */
export function vibrate(duriation: number) { export function vibrate(duration: number) {
try { try {
const context = UTSAndroid.getAppContext() as Context; const context = UTSAndroid.getAppContext() as Context;
let vb: Vibrator | null = null; let vb: Vibrator | null = null;
@@ -25,7 +25,7 @@ export function vibrate(duriation: number) {
// Android 8.0 (API 26) 及以上使用 VibrationEffect // Android 8.0 (API 26) 及以上使用 VibrationEffect
if (Build.VERSION.SDK_INT >= 26) { if (Build.VERSION.SDK_INT >= 26) {
const effect = VibrationEffect.createOneShot( const effect = VibrationEffect.createOneShot(
duriation.toLong(), duration.toLong(),
VibrationEffect.DEFAULT_AMPLITUDE VibrationEffect.DEFAULT_AMPLITUDE
); );
vb.vibrate(effect); vb.vibrate(effect);

View File

@@ -1,8 +1,8 @@
/** /**
* 震动 * 震动
* @param {number} duriation 震动时间单位ms,ios微信失效 * @param {number} duration 震动时间单位ms,ios微信失效
*/ */
export function vibrate(duriation: number) { export function vibrate(duration: number) {
wx.vibrateShort({ wx.vibrateShort({
type: "medium", type: "medium",
success() {}, success() {},

View File

@@ -1,10 +1,10 @@
/** /**
* 震动 * 震动
* @param {number} duriation 震动时间单位ms,ios微信失效 * @param {number} duration 震动时间单位ms,ios微信失效
*/ */
export function vibrate(duriation: number) { export function vibrate(duration: number) {
try { try {
navigator.vibrate(duriation); navigator.vibrate(duration);
} catch (error) { } catch (error) {
console.error("WEB震动失败:", error); console.error("WEB震动失败:", error);
} }