瀏覽代碼

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

icssoa 7 月之前
父節點
當前提交
e7b17195dd
共有 49 個文件被更改,包括 677 次插入287 次删除
  1. 二進制
      .cool/icons/remixicon.zip
  2. 0 0
      .cool/remixicon/RemixIcon_Collection_2508121900.remixicon
  3. 95 0
      components/size-set.uvue
  4. 0 0
      icons/remixicon/index.scss
  5. 4 1
      icons/remixicon/index.ts
  6. 0 0
      locale/en.ts
  7. 0 0
      locale/es.ts
  8. 0 0
      locale/zh-cn.ts
  9. 1 1
      package.json
  10. 3 5
      pages/demo/components/item.uvue
  11. 12 2
      pages/demo/data/pagination.uvue
  12. 0 1
      pages/demo/feedback/popup.uvue
  13. 5 0
      pages/demo/form/input-otp.uvue
  14. 1 1
      pages/demo/form/keyboard.uvue
  15. 21 21
      pages/demo/layout/flex.uvue
  16. 3 3
      pages/demo/layout/sticky.uvue
  17. 1 7
      pages/demo/other/qrcode.uvue
  18. 1 1
      pages/demo/status/badge.uvue
  19. 30 10
      pages/index/home.uvue
  20. 9 5
      pages/index/my.uvue
  21. 22 5
      pages/user/login.uvue
  22. 6 1
      uni_modules/cool-ui/components/cl-action-sheet/cl-action-sheet.uvue
  23. 40 22
      uni_modules/cool-ui/components/cl-badge/cl-badge.uvue
  24. 13 2
      uni_modules/cool-ui/components/cl-empty/cl-empty.uvue
  25. 13 6
      uni_modules/cool-ui/components/cl-icon/cl-icon.uvue
  26. 18 19
      uni_modules/cool-ui/components/cl-input-otp/cl-input-otp.uvue
  27. 19 2
      uni_modules/cool-ui/components/cl-input/cl-input.uvue
  28. 18 25
      uni_modules/cool-ui/components/cl-keyboard-car/cl-keyboard-car.uvue
  29. 27 30
      uni_modules/cool-ui/components/cl-keyboard-number/cl-keyboard-number.uvue
  30. 38 36
      uni_modules/cool-ui/components/cl-keyboard-password/cl-keyboard-password.uvue
  31. 7 4
      uni_modules/cool-ui/components/cl-loading/cl-loading.uvue
  32. 6 6
      uni_modules/cool-ui/components/cl-noticebar/cl-noticebar.uvue
  33. 11 7
      uni_modules/cool-ui/components/cl-pagination/cl-pagination.uvue
  34. 5 8
      uni_modules/cool-ui/components/cl-picker-view/cl-picker-view.uvue
  35. 1 1
      uni_modules/cool-ui/components/cl-popup/cl-popup.uvue
  36. 5 8
      uni_modules/cool-ui/components/cl-select-trigger/cl-select-trigger.uvue
  37. 19 2
      uni_modules/cool-ui/components/cl-select/cl-select.uvue
  38. 15 13
      uni_modules/cool-ui/components/cl-tabs/cl-tabs.uvue
  39. 23 2
      uni_modules/cool-ui/components/cl-text/cl-text.uvue
  40. 26 12
      uni_modules/cool-ui/components/cl-textarea/cl-textarea.uvue
  41. 7 5
      uni_modules/cool-ui/components/cl-toast/cl-toast.uvue
  42. 1 1
      uni_modules/cool-ui/components/cl-topbar/cl-topbar.uvue
  43. 6 2
      uni_modules/cool-ui/components/cl-upload/cl-upload.uvue
  44. 6 2
      uni_modules/cool-ui/config.ts
  45. 1 0
      uni_modules/cool-ui/hooks/index.ts
  46. 130 0
      uni_modules/cool-ui/hooks/size.ts
  47. 3 3
      uni_modules/cool-vibrate/utssdk/app-android/index.uts
  48. 2 2
      uni_modules/cool-vibrate/utssdk/mp-weixin/index.uts
  49. 3 3
      uni_modules/cool-vibrate/utssdk/web/index.uts

二進制
.cool/icons/remixicon.zip


文件差異過大導致無法顯示
+ 0 - 0
.cool/remixicon/RemixIcon_Collection_2508121900.remixicon


+ 95 - 0
components/size-set.uvue

@@ -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>

文件差異過大導致無法顯示
+ 0 - 0
icons/remixicon/index.scss


+ 4 - 1
icons/remixicon/index.ts

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

文件差異過大導致無法顯示
+ 0 - 0
locale/en.ts


文件差異過大導致無法顯示
+ 0 - 0
locale/es.ts


文件差異過大導致無法顯示
+ 0 - 0
locale/zh-cn.ts


+ 1 - 1
package.json

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

+ 3 - 5
pages/demo/components/item.uvue

@@ -1,6 +1,8 @@
 <template>
 	<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>
 	</view>
@@ -22,9 +24,5 @@ const props = defineProps({
 <style lang="scss" scoped>
 .demo-item {
 	@apply p-3 rounded-xl bg-white mb-3;
-
-	.label {
-		@apply text-sm text-surface-400 mb-2;
-	}
 }
 </style>

+ 12 - 2
pages/demo/data/pagination.uvue

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

+ 0 - 1
pages/demo/feedback/popup.uvue

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

+ 5 - 0
pages/demo/form/input-otp.uvue

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

+ 1 - 1
pages/demo/form/keyboard.uvue

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

+ 21 - 21
pages/demo/layout/flex.uvue

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

+ 3 - 3
pages/demo/layout/sticky.uvue

@@ -2,7 +2,7 @@
 	<cl-page back-top>
 		<cl-sticky>
 			<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>
 		</cl-sticky>
 
@@ -14,7 +14,7 @@
 
 		<cl-sticky :offset-top="40">
 			<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>
 		</cl-sticky>
 
@@ -26,7 +26,7 @@
 
 		<cl-sticky :offset-top="80">
 			<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>
 		</cl-sticky>
 

+ 1 - 7
pages/demo/other/qrcode.uvue

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

+ 1 - 1
pages/demo/status/badge.uvue

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

+ 30 - 10
pages/index/home.uvue

@@ -16,9 +16,21 @@
 					></cl-image>
 				</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 }}
-				</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
 					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="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">
 					<cl-row :gutter="10">
@@ -60,18 +74,24 @@
 		</view>
 
 		<tabbar></tabbar>
+
+		<!-- 主题设置 -->
 		<locale-set :ref="refs.set('localeSet')"></locale-set>
+
+		<!-- 字体大小设置 -->
+		<size-set :ref="refs.set('sizeSet')"></size-set>
 	</cl-page>
 </template>
 
 <script lang="ts" setup>
 import { isMp, router, useRefs } from "@/cool";
-import LocaleSet from "@/components/locale-set.uvue";
 import { t } from "@/locale";
 import { computed } from "vue";
-import Tabbar from "@/components/tabbar.uvue";
 import { useUi } from "@/uni_modules/cool-ui";
 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 refs = useRefs();
@@ -414,20 +434,20 @@ function toPath(item: Item) {
 function setLocale() {
 	refs.open("localeSet");
 }
+
+function setSize() {
+	refs.open("sizeSet");
+}
 </script>
 
 <style lang="scss" scoped>
 .group {
 	@apply mb-10;
 
-	.title {
-		@apply text-sm mb-2 ml-1 text-surface-500;
-	}
-
 	.list {
 		.item {
 			@apply flex flex-col items-center;
-			@apply rounded-xl bg-white p-2;
+			@apply rounded-xl bg-white px-2;
 			height: 140rpx;
 			margin-bottom: 10rpx;
 			padding-top: 36rpx;

+ 9 - 5
pages/index/my.uvue

@@ -1,6 +1,12 @@
 <template>
 	<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="top-icon dark:!bg-surface-700" @tap="toSet">
 					<cl-icon name="settings-line"></cl-icon>
@@ -30,7 +36,7 @@
 					</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' }">{{
 						user.info.nickName ?? t("未登录")
 					}}</cl-text>
@@ -264,8 +270,6 @@ onReady(() => {
 
 <style lang="scss" scoped>
 .top-icon {
-	@apply flex items-center justify-center rounded-lg bg-white mr-3;
-	height: 50rpx;
-	width: 50rpx;
+	@apply flex items-center justify-center rounded-lg bg-white mr-3 p-2;
 }
 </style>

+ 22 - 5
pages/user/login.uvue

@@ -3,6 +3,7 @@
 		<cl-topbar safe-area-top background-color="transparent"></cl-topbar>
 
 		<view class="px-10">
+			<!-- Logo -->
 			<view class="flex flex-col items-center justify-center py-20">
 				<view class="p-3 bg-primary-500 rounded-2xl">
 					<cl-image
@@ -16,9 +17,13 @@
 				<cl-text :pt="{ className: '!text-xl font-bold mt-3' }">{{ config.name }}</cl-text>
 			</view>
 
+			<!-- 手机号登录 -->
 			<login-phone :form="form" @success="toLogin"></login-phone>
+
+			<!-- 微信登录 -->
 			<login-wx :ref="refs.set('loginWx')" @success="toLogin"></login-wx>
 
+			<!-- 协议 -->
 			<view class="mt-6 flex flex-row flex-wrap items-center justify-center">
 				<cl-checkbox
 					v-model="agree"
@@ -45,17 +50,19 @@
 				</cl-text>
 			</view>
 
+			<!-- 第三方登录 -->
 			<view class="flex flex-row justify-center mt-10 px-10">
 				<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')"
 				>
 					<cl-icon name="wechat-fill" :size="38" color="#00b223"></cl-icon>
 				</view>
 
-				<view
-					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"
-				>
+				<view class="login-item" :class="{ 'is-dark': isDark }">
 					<cl-icon name="apple-fill" :size="38"></cl-icon>
 				</view>
 			</view>
@@ -65,7 +72,7 @@
 
 <script setup lang="ts">
 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 { provide, reactive, ref } from "vue";
 import type { LoginForm } from "./types";
@@ -110,3 +117,13 @@ function isAgree() {
 
 provide("isAgree", isAgree);
 </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>

+ 6 - 1
uni_modules/cool-ui/components/cl-action-sheet/cl-action-sheet.uvue

@@ -15,7 +15,12 @@
 			<slot name="prepend"></slot>
 
 			<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 class="cl-action-sheet__list" :class="[pt.list?.className]">

+ 40 - 22
uni_modules/cool-ui/components/cl-badge/cl-badge.uvue

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

+ 13 - 2
uni_modules/cool-ui/components/cl-empty/cl-empty.uvue

@@ -18,13 +18,24 @@
 			v-if="showIcon"
 		></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>
 </template>
 
 <script lang="ts" setup>
 import { t } from "@/locale";
-import { parsePt, parseRpx } from "@/cool";
+import { isDark, parseClass, parsePt, parseRpx } from "@/cool";
 import { computed } from "vue";
 
 // 组件属性定义

+ 13 - 6
uni_modules/cool-ui/components/cl-icon/cl-icon.uvue

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

+ 18 - 19
uni_modules/cool-ui/components/cl-input-otp/cl-input-otp.uvue

@@ -1,5 +1,13 @@
 <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">
 			<cl-input
 				v-model="value"
@@ -33,16 +41,11 @@
 					pt.item?.className
 				]"
 			>
-				<text
-					class="cl-input-otp__value"
-					:class="[
-						{
-							'is-disabled': disabled,
-							'is-dark': isDark
-						},
-						pt.value?.className
-					]"
-					>{{ item }}</text
+				<cl-text
+					:pt="{
+						className: pt.value?.className
+					}"
+					>{{ item }}</cl-text
 				>
 				<view
 					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 {
 		@apply absolute;
 		@apply bg-primary-500;
@@ -302,5 +297,9 @@ function animateCursor(isIncreasing: boolean) {
 		}
 	}
 	// #endif
+
+	&--disabled {
+		@apply opacity-50;
+	}
 }
 </style>

+ 19 - 2
uni_modules/cool-ui/components/cl-input/cl-input.uvue

@@ -30,8 +30,9 @@
 					'is-disabled': isDisabled,
 					'is-dark': isDark
 				},
-				pt.inner?.className
+				ptClassName
 			]"
+			:style="inputStyle"
 			:value="value"
 			:disabled="readonly ?? isDisabled"
 			:type="type"
@@ -86,7 +87,7 @@ import type { ClInputType, PassThroughProps } from "../../types";
 import { isDark, parseClass, parsePt } from "@/cool";
 import type { ClIconProps } from "../cl-icon/props";
 import { t } from "@/locale";
-import { useForm, useFormItem } from "../../hooks";
+import { useForm, useFormItem, useSize } from "../../hooks";
 
 defineOptions({
 	name: "cl-input"
@@ -225,6 +226,22 @@ type PassThrough = {
 // 解析透传样式
 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>("");
 

+ 18 - 25
uni_modules/cool-ui/components/cl-keyboard-car/cl-keyboard-car.uvue

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

+ 27 - 30
uni_modules/cool-ui/components/cl-keyboard-number/cl-keyboard-number.uvue

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

+ 38 - 36
uni_modules/cool-ui/components/cl-keyboard-password/cl-keyboard-password.uvue

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

+ 7 - 4
uni_modules/cool-ui/components/cl-loading/cl-loading.uvue

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

+ 6 - 6
uni_modules/cool-ui/components/cl-noticebar/cl-noticebar.uvue

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

+ 11 - 7
uni_modules/cool-ui/components/cl-pagination/cl-pagination.uvue

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

+ 5 - 8
uni_modules/cool-ui/components/cl-picker-view/cl-picker-view.uvue

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

+ 1 - 1
uni_modules/cool-ui/components/cl-popup/cl-popup.uvue

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

+ 5 - 8
uni_modules/cool-ui/components/cl-select-trigger/cl-select-trigger.uvue

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

+ 19 - 2
uni_modules/cool-ui/components/cl-select/cl-select.uvue

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

+ 15 - 13
uni_modules/cool-ui/components/cl-tabs/cl-tabs.uvue

@@ -35,19 +35,21 @@
 					@tap="change(index)"
 				>
 					<slot name="item" :item="item" :active="item.isActive">
-						<text
-							class="cl-tabs__item-label"
-							:class="[
-								{
-									'is-active': item.isActive,
-									'is-disabled': item.disabled,
-									'is-dark': isDark,
-									'is-fill': fill
-								},
-								pt.text?.className
-							]"
+						<cl-text
+							:pt="{
+								className: parseClass([
+									'cl-tabs__item-label',
+									{
+										'is-active': item.isActive,
+										'is-disabled': item.disabled,
+										'is-dark': isDark,
+										'is-fill': fill
+									},
+									pt.text?.className
+								])
+							}"
 							:style="getTextStyle(item)"
-							>{{ item.label }}</text
+							>{{ item.label }}</cl-text
 						>
 					</slot>
 				</view>
@@ -73,7 +75,7 @@
 
 <script lang="ts" setup>
 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";
 
 // 定义标签类型

+ 23 - 2
uni_modules/cool-ui/components/cl-text/cl-text.uvue

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

+ 26 - 12
uni_modules/cool-ui/components/cl-textarea/cl-textarea.uvue

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

+ 7 - 5
uni_modules/cool-ui/components/cl-toast/cl-toast.uvue

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

+ 1 - 1
uni_modules/cool-ui/components/cl-topbar/cl-topbar.uvue

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

+ 6 - 2
uni_modules/cool-ui/components/cl-upload/cl-upload.uvue

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

+ 6 - 2
uni_modules/cool-ui/config.ts

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

+ 1 - 0
uni_modules/cool-ui/hooks/index.ts

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

+ 130 - 0
uni_modules/cool-ui/hooks/size.ts

@@ -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);
+}

+ 3 - 3
uni_modules/cool-vibrate/utssdk/app-android/index.uts

@@ -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 {
 		const context = UTSAndroid.getAppContext() as Context;
 		let vb: Vibrator | null = null;
@@ -25,7 +25,7 @@ export function vibrate(duriation: number) {
 			// Android 8.0 (API 26) 及以上使用 VibrationEffect
 			if (Build.VERSION.SDK_INT >= 26) {
 				const effect = VibrationEffect.createOneShot(
-					duriation.toLong(),
+					duration.toLong(),
 					VibrationEffect.DEFAULT_AMPLITUDE
 				);
 				vb.vibrate(effect);

+ 2 - 2
uni_modules/cool-vibrate/utssdk/mp-weixin/index.uts

@@ -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({
 		type: "medium",
 		success() {},

+ 3 - 3
uni_modules/cool-vibrate/utssdk/web/index.uts

@@ -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 {
-		navigator.vibrate(duriation);
+		navigator.vibrate(duration);
 	} catch (error) {
 		console.error("WEB震动失败:", error);
 	}

部分文件因文件數量過多而無法顯示