浏览代码

添加 useForm 事件

icssoa 7 月之前
父节点
当前提交
da573d0d35

+ 1 - 1
package.json

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

+ 5 - 4
pages/demo/form/upload.uvue

@@ -2,11 +2,11 @@
 	<cl-page>
 		<view class="p-3">
 			<demo-item :label="t('基础用法')">
-				<cl-upload v-model="form.upload1"></cl-upload>
+				<cl-upload v-model="form.upload1" test></cl-upload>
 			</demo-item>
 
 			<demo-item :label="t('禁用')">
-				<cl-upload v-model="form.upload1" disabled></cl-upload>
+				<cl-upload v-model="form.upload1" disabled test></cl-upload>
 			</demo-item>
 
 			<demo-item :label="t('自定义图标、文字、大小')">
@@ -16,15 +16,16 @@
 					:text="t('上传证件照')"
 					:width="300"
 					:height="200"
+					test
 				></cl-upload>
 			</demo-item>
 
 			<demo-item :label="t('多选')">
-				<cl-upload multiple v-model="form.upload2"></cl-upload>
+				<cl-upload multiple v-model="form.upload2" test></cl-upload>
 			</demo-item>
 
 			<demo-item :label="t('限制 3 个')">
-				<cl-upload multiple :limit="3" v-model="form.upload3"></cl-upload>
+				<cl-upload multiple :limit="3" v-model="form.upload3" test></cl-upload>
 			</demo-item>
 		</view>
 	</cl-page>

+ 2 - 5
pages/demo/other/sign.uvue

@@ -25,7 +25,7 @@
 import { t } from "@/locale";
 import { ref } from "vue";
 
-const height = ref(0);
+const height = ref(200);
 const width = ref(0);
 
 const isFullscreen = ref(false);
@@ -49,9 +49,6 @@ function onFullscreenChange() {
 }
 
 onReady(() => {
-	const { windowWidth } = uni.getWindowInfo();
-
-	height.value = 200;
-	width.value = windowWidth;
+	width.value = uni.getWindowInfo().windowWidth;
 });
 </script>

+ 0 - 4
uni_modules/cool-ui/components/cl-cascader/cl-cascader.uvue

@@ -358,10 +358,6 @@ const visible = ref(false);
  * 检查禁用状态,如果未禁用则显示弹窗
  */
 function open() {
-	if (props.disabled) {
-		return;
-	}
-
 	visible.value = true;
 }
 

+ 8 - 3
uni_modules/cool-ui/components/cl-checkbox/cl-checkbox.uvue

@@ -3,7 +3,7 @@
 		class="cl-checkbox"
 		:class="[
 			{
-				'cl-checkbox--disabled': disabled,
+				'cl-checkbox--disabled': isDisabled,
 				'cl-checkbox--checked': isChecked
 			},
 			pt.className
@@ -47,6 +47,7 @@ import { computed, useSlots, type PropType } from "vue";
 import type { PassThroughProps } from "../../types";
 import { get, parseClass, parsePt, pull } from "@/cool";
 import type { ClIconProps } from "../cl-icon/props";
+import { useForm } from "../../hooks";
 
 defineOptions({
 	name: "cl-checkbox"
@@ -99,6 +100,10 @@ const props = defineProps({
 const emit = defineEmits(["update:modelValue", "change"]);
 
 const slots = useSlots();
+const { disabled } = useForm();
+
+// 是否禁用
+const isDisabled = computed(() => props.disabled || disabled.value);
 
 // 透传样式类型定义
 type PassThrough = {
@@ -142,7 +147,7 @@ const iconName = computed(() => {
  * 在非禁用状态下切换选中状态
  */
 function onTap() {
-	if (!props.disabled) {
+	if (!isDisabled.value) {
 		let val = props.modelValue;
 
 		if (Array.isArray(val)) {
@@ -166,7 +171,7 @@ function onTap() {
 	@apply flex flex-row items-center;
 
 	&--disabled {
-		@apply opacity-70;
+		@apply opacity-50;
 	}
 }
 </style>

+ 10 - 3
uni_modules/cool-ui/components/cl-radio/cl-radio.uvue

@@ -3,7 +3,7 @@
 		class="cl-radio"
 		:class="[
 			{
-				'cl-radio--disabled': disabled,
+				'cl-radio--disabled': isDisabled,
 				'cl-radio--checked': isChecked
 			},
 			pt.className
@@ -47,6 +47,7 @@ import { computed, useSlots } from "vue";
 import type { PassThroughProps } from "../../types";
 import { get, parseClass, parsePt } from "@/cool";
 import type { ClIconProps } from "../cl-icon/props";
+import { useForm } from "../../hooks";
 
 defineOptions({
 	name: "cl-radio"
@@ -108,6 +109,12 @@ type PassThrough = {
 // 解析透传样式配置
 const pt = computed(() => parsePt<PassThrough>(props.pt));
 
+// cl-form 上下文
+const { disabled } = useForm();
+
+// 是否禁用
+const isDisabled = computed(() => props.disabled || disabled.value);
+
 // 是否为选中状态
 const isChecked = computed(() => props.modelValue == props.value);
 
@@ -130,7 +137,7 @@ const iconName = computed(() => {
  * 在非禁用状态下切换选中状态
  */
 function onTap() {
-	if (!props.disabled) {
+	if (!isDisabled.value) {
 		emit("update:modelValue", props.value);
 		emit("change", props.value);
 	}
@@ -142,7 +149,7 @@ function onTap() {
 	@apply flex flex-row items-center;
 
 	&--disabled {
-		@apply opacity-70;
+		@apply opacity-50;
 	}
 }
 </style>

+ 14 - 4
uni_modules/cool-ui/components/cl-rate/cl-rate.uvue

@@ -1,5 +1,5 @@
 <template>
-	<view class="cl-rate" :class="[pt.className]">
+	<view class="cl-rate" :class="[{ 'cl-rate--disabled': isDisabled }, pt.className]">
 		<view
 			v-for="(item, index) in max"
 			:key="index"
@@ -48,6 +48,7 @@ import { computed } from "vue";
 import { parseClass, parsePt } from "@/cool";
 import type { PassThroughProps } from "../../types";
 import type { ClIconProps } from "../cl-icon/props";
+import { useForm } from "../../hooks";
 
 defineOptions({
 	name: "cl-rate"
@@ -126,6 +127,12 @@ type PassThrough = {
 // 解析透传样式
 const pt = computed(() => parsePt<PassThrough>(props.pt));
 
+// cl-form 上下文
+const { disabled } = useForm();
+
+// 是否禁用
+const isDisabled = computed(() => props.disabled || disabled.value);
+
 // 获取图标激活宽度
 function getIconActiveWidth(item: number) {
 	// 如果评分值大于等于当前项,返回null表示完全填充
@@ -144,7 +151,7 @@ function getIconActiveWidth(item: number) {
 
 // 点击事件处理
 function onTap(index: number) {
-	if (props.disabled) {
+	if (isDisabled.value) {
 		return;
 	}
 
@@ -176,11 +183,14 @@ function onTap(index: number) {
 .cl-rate {
 	@apply flex flex-row items-center;
 
+	&--disabled {
+		@apply opacity-50;
+	}
+
 	&__item {
-		@apply flex items-center justify-center relative duration-200;
+		@apply flex items-center justify-center relative duration-200 overflow-hidden;
 		transition-property: color;
 		margin-right: 6rpx;
-		overflow: hidden;
 	}
 }
 </style>

+ 8 - 4
uni_modules/cool-ui/components/cl-select-date/cl-select-date.uvue

@@ -51,7 +51,7 @@
 						@tap="setRange(0)"
 					>
 						<cl-text
-							v-if="values[0] != ''"
+							v-if="values.length > 0 && values[0] != ''"
 							:pt="{
 								className: 'text-center'
 							}"
@@ -78,7 +78,7 @@
 						@tap="setRange(1)"
 					>
 						<cl-text
-							v-if="values[1] != ''"
+							v-if="values.length > 1 && values[1] != ''"
 							:pt="{
 								className: 'text-center'
 							}"
@@ -393,10 +393,14 @@ const list = computed(() => {
 	).toArray();
 	// 解析结束日期为年月日时分秒数组
 	const [endYear, endMonth, endDate, endHour, endMinute, endSecond] = dayUts(props.end).toArray();
-	// 获取当前选中的年月日时分秒值
-	const [year, month, date, hour, minute] = value.value;
 	// 初始化年月日时分秒六个选项数组
 	const arr = [[], [], [], [], [], []] as ClSelectOption[][];
+	// 边界处理,如果value为空,返回空数组
+	if (isEmpty(value.value)) {
+		return arr;
+	}
+	// 获取当前选中的年月日时分秒值
+	const [year, month, date, hour, minute] = value.value;
 	// 判断是否为闰年
 	const isLeapYear = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
 	// 根据月份和是否闰年获取当月天数

+ 24 - 14
uni_modules/cool-ui/components/cl-slider/cl-slider.uvue

@@ -3,7 +3,7 @@
 		class="cl-slider"
 		:class="[
 			{
-				'cl-slider--disabled': disabled
+				'cl-slider--disabled': isDisabled
 			},
 			pt.className
 		]"
@@ -62,14 +62,16 @@
 			></view>
 		</view>
 
-		<cl-text
-			v-if="showValue"
-			:pt="{
-				className: parseClass(['text-center w-[100rpx]', pt.value?.className])
-			}"
-		>
-			{{ displayValue }}
-		</cl-text>
+		<slot name="value" :value="displayValue">
+			<cl-text
+				v-if="showValue"
+				:pt="{
+					className: parseClass(['text-center w-[100rpx]', pt.value?.className])
+				}"
+			>
+				{{ displayValue }}
+			</cl-text>
+		</slot>
 	</view>
 </template>
 
@@ -77,6 +79,7 @@
 import { computed, getCurrentInstance, nextTick, onMounted, ref, watch, type PropType } from "vue";
 import { parseClass, parsePt, rpx2px } from "@/cool";
 import type { PassThroughProps } from "../../types";
+import { useForm } from "../../hooks";
 
 defineOptions({
 	name: "cl-slider"
@@ -157,6 +160,12 @@ type PassThrough = {
 // 计算样式穿透对象
 const pt = computed(() => parsePt<PassThrough>(props.pt));
 
+// cl-form 上下文
+const { disabled } = useForm();
+
+// 是否禁用
+const isDisabled = computed(() => props.disabled || disabled.value);
+
 // 当前滑块的值,单值模式
 const value = ref<number>(props.modelValue);
 
@@ -356,7 +365,7 @@ function updateValue(newValue: number | number[]) {
 
 // 触摸开始事件:获取轨道信息并初始化滑块位置
 async function onTouchStart(e: TouchEvent) {
-	if (props.disabled) return;
+	if (isDisabled.value) return;
 
 	// 先获取轨道的位置和尺寸信息,这是后续计算的基础
 	await getTrackInfo();
@@ -381,7 +390,7 @@ async function onTouchStart(e: TouchEvent) {
 
 // 触摸移动事件:实时更新滑块位置
 function onTouchMove(e: TouchEvent) {
-	if (props.disabled) return;
+	if (isDisabled.value) return;
 
 	const clientX = e.touches[0].clientX;
 	const calculatedValue = calculateValue(clientX);
@@ -399,7 +408,7 @@ function onTouchMove(e: TouchEvent) {
 
 // 触摸结束事件:完成拖动,触发最终的change事件
 function onTouchEnd() {
-	if (props.disabled) return;
+	if (isDisabled.value) return;
 
 	// 触发change事件,表示用户完成了一次完整的拖动操作
 	if (props.range) {
@@ -500,8 +509,7 @@ onMounted(() => {
 	@apply flex flex-row items-center w-full overflow-visible;
 
 	&--disabled {
-		opacity: 0.6;
-		pointer-events: none;
+		@apply opacity-50;
 	}
 
 	&__inner {
@@ -528,6 +536,8 @@ onMounted(() => {
 		transform: translateY(-50%);
 		pointer-events: none;
 		z-index: 1;
+		border-width: 4rpx;
+		box-shadow: 0 0 2rpx 2rpx rgba(100, 100, 100, 0.1);
 
 		&--min {
 			z-index: 2;

+ 9 - 2
uni_modules/cool-ui/components/cl-switch/cl-switch.uvue

@@ -3,7 +3,7 @@
 		class="cl-switch"
 		:class="[
 			{
-				'cl-switch--disabled': disabled,
+				'cl-switch--disabled': isDisabled,
 				'cl-switch--checked': isChecked
 			},
 
@@ -53,6 +53,7 @@ import { computed, ref, watch } from "vue";
 import { isDark, parseClass, parsePt } from "@/cool";
 import type { PassThroughProps } from "../../types";
 import { vibrate } from "@/uni_modules/cool-vibrate";
+import { useForm } from "../../hooks";
 
 defineOptions({
 	name: "cl-switch"
@@ -107,6 +108,12 @@ type PassThrough = {
 // 解析透传样式配置
 const pt = computed(() => parsePt<PassThrough>(props.pt));
 
+// cl-form 上下文
+const { disabled } = useForm();
+
+// 是否禁用
+const isDisabled = computed(() => props.disabled || disabled.value);
+
 // 绑定值
 const value = ref(props.modelValue);
 
@@ -147,7 +154,7 @@ const rect = computed<Rect>(() => {
  * 在非禁用且非加载状态下切换开关状态
  */
 function onTap() {
-	if (!props.disabled && !props.loading) {
+	if (!isDisabled.value && !props.loading) {
 		// 切换开关状态
 		const val = !value.value;
 		value.value = val;

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

@@ -5,7 +5,8 @@
 		:class="[
 			isDark ? 'text-surface-50' : 'text-surface-700',
 			{
-				'truncate w-full': ellipsis
+				'truncate w-full': ellipsis,
+				'cl-text--pre-wrap': preWrap
 			},
 			{
 				'!text-primary-500': color == 'primary',
@@ -35,7 +36,8 @@
 		:class="[
 			isDark ? 'text-surface-50' : 'text-surface-700',
 			{
-				'truncate w-full': ellipsis
+				'truncate w-full': ellipsis,
+				'cl-text--pre-wrap': preWrap
 			},
 			{
 				'!text-primary-500': color == 'primary',
@@ -140,6 +142,11 @@ const props = defineProps({
 	decode: {
 		type: Boolean,
 		default: false
+	},
+	// 是否保留单词
+	preWrap: {
+		type: Boolean,
+		default: false
 	}
 });
 
@@ -268,5 +275,11 @@ const content = computed(() => {
 <style lang="scss" scoped>
 .cl-text {
 	@apply text-md;
+
+	&--pre-wrap {
+		// #ifdef H5
+		white-space: pre-wrap;
+		// #endif
+	}
 }
 </style>

+ 12 - 0
uni_modules/cool-ui/components/cl-upload/cl-upload.uvue

@@ -145,6 +145,11 @@ const props = defineProps({
 	disabled: {
 		type: Boolean,
 		default: false
+	},
+	// 演示用,本地预览
+	test: {
+		type: Boolean,
+		default: false
 	}
 });
 
@@ -330,6 +335,13 @@ function choose(index: number) {
 					// 添加到列表并获取唯一ID
 					const uid = append(file.path);
 
+					// 测试用,本地预览
+					if (props.test) {
+						update(uid, { url: file.path, progress: 100 });
+						emit("success", file.path, uid);
+						return;
+					}
+
 					// 开始上传文件
 					uploadFile(file, {
 						// 上传进度回调