Просмотр исходного кода

表单项添加红色边框的错误提示

icssoa 7 месяцев назад
Родитель
Сommit
a5fde75b8a

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

@@ -27,7 +27,7 @@
 			</view>
 		</view>
 
-		<view class="cl-form-item__error" v-if="hasError && showMessage">
+		<view class="cl-form-item__error" v-if="isError && showMessage">
 			<slot name="error" :error="errorText">
 				<cl-text
 					color="error"
@@ -121,7 +121,7 @@ const errorText = computed<string>(() => {
 });
 
 // 是否有错误
-const hasError = computed<boolean>(() => {
+const isError = computed<boolean>(() => {
 	return errorText.value != "";
 });
 
@@ -194,6 +194,10 @@ onMounted(() => {
 onUnmounted(() => {
 	removeField(props.prop);
 });
+
+defineExpose({
+	prop: props.prop
+});
 </script>
 
 <style lang="scss" scoped>

+ 7 - 3
uni_modules/cool-ui/components/cl-form/cl-form.uvue

@@ -15,7 +15,7 @@
 
 <script setup lang="ts">
 import { computed, nextTick, ref, watch, type PropType } from "vue";
-import { isEmpty, parsePt, parseToObject } from "@/cool";
+import { isEmpty, isString, parsePt, parseToObject } from "@/cool";
 import type { ClFormLabelPosition, ClFormRule, ClFormValidateError } from "../../types";
 import { $t, t } from "@/locale";
 
@@ -167,13 +167,17 @@ function getRule(prop: string): ClFormRule[] {
 function validateRule(value: any | null, rule: ClFormRule): null | string {
 	// 必填验证
 	if (rule.required == true) {
-		if (value == null || value == "" || (Array.isArray(value) && value.length == 0)) {
+		if (
+			value == null ||
+			(value == "" && isString(value)) ||
+			(Array.isArray(value) && value.length == 0)
+		) {
 			return rule.message ?? t("此字段为必填项");
 		}
 	}
 
 	// 如果值为空且不是必填,直接通过
-	if ((value == null || value == "") && rule.required != true) {
+	if ((value == null || (value == "" && isString(value))) && rule.required != true) {
 		return null;
 	}
 

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

@@ -7,7 +7,8 @@
 				'is-dark': isDark,
 				'cl-input--border': border,
 				'cl-input--focus': isFocus,
-				'cl-input--disabled': isDisabled
+				'cl-input--disabled': isDisabled,
+				'cl-input--error': isError
 			}
 		]"
 		@tap="onTap"
@@ -85,7 +86,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 } from "../../hooks";
+import { useForm, useFormItem } from "../../hooks";
 
 defineOptions({
 	name: "cl-input"
@@ -205,6 +206,9 @@ const emit = defineEmits([
 // cl-form 上下文
 const { disabled } = useForm();
 
+// cl-form-item 上下文
+const { isError } = useFormItem();
+
 // 是否禁用
 const isDisabled = computed(() => {
 	return disabled.value || props.disabled;
@@ -365,6 +369,10 @@ defineExpose({
 		}
 	}
 
+	&--error {
+		@apply border-red-500;
+	}
+
 	&.is-dark {
 		@apply bg-surface-800;
 

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

@@ -5,7 +5,8 @@
 			{
 				'is-dark': isDark,
 				'cl-select-trigger--disabled': isDisabled,
-				'cl-select-trigger--focus': focus
+				'cl-select-trigger--focus': focus,
+				'cl-select-trigger--error': isError
 			},
 			pt.className
 		]"
@@ -62,7 +63,7 @@ import type { ClIconProps } from "../cl-icon/props";
 import { isDark, parseClass, parsePt } from "@/cool";
 import { t } from "@/locale";
 import type { PassThroughProps } from "../../types";
-import { useForm } from "../../hooks";
+import { useForm, useFormItem } from "../../hooks";
 
 defineOptions({
 	name: "cl-select-trigger"
@@ -104,8 +105,12 @@ const props = defineProps({
 
 const emit = defineEmits(["open", "clear"]);
 
+// cl-form 上下文
 const { disabled } = useForm();
 
+// cl-form-item 上下文
+const { isError } = useFormItem();
+
 // 是否禁用
 const isDisabled = computed(() => {
 	return disabled.value || props.disabled;
@@ -172,6 +177,10 @@ function open() {
 		}
 	}
 
+	&--error {
+		@apply border-red-500;
+	}
+
 	&.is-dark {
 		@apply border-surface-700 bg-surface-800;
 

+ 10 - 2
uni_modules/cool-ui/components/cl-textarea/cl-textarea.uvue

@@ -7,7 +7,8 @@
 				'is-dark': isDark,
 				'cl-textarea--border': border,
 				'cl-textarea--focus': isFocus,
-				'cl-textarea--disabled': isDisabled
+				'cl-textarea--disabled': isDisabled,
+				'cl-textarea--error': isError
 			}
 		]"
 		@tap="onTap"
@@ -63,7 +64,7 @@ import { parsePt, parseRpx } from "@/cool";
 import type { PassThroughProps } from "../../types";
 import { isDark } from "@/cool";
 import { t } from "@/locale";
-import { useForm } from "../../hooks";
+import { useForm, useFormItem } from "../../hooks";
 
 defineOptions({
 	name: "cl-textarea"
@@ -225,6 +226,9 @@ const emit = defineEmits([
 // cl-form 上下文
 const { disabled } = useForm();
 
+// cl-form-item 上下文
+const { isError } = useFormItem();
+
 // 是否禁用
 const isDisabled = computed(() => {
 	return disabled.value || props.disabled;
@@ -360,6 +364,10 @@ defineExpose({
 		@apply bg-surface-100 opacity-70;
 	}
 
+	&--error {
+		@apply border-red-500;
+	}
+
 	&.is-dark {
 		@apply bg-surface-800;
 

+ 30 - 3
uni_modules/cool-ui/hooks/form.ts

@@ -2,7 +2,7 @@ import { computed, ref, type ComputedRef } from "vue";
 import type { ClFormRule, ClFormValidateError } from "../types";
 import { useParent } from "@/cool";
 
-export class FormValidate {
+export class Form {
 	public formRef = ref<ClFormComponentPublicInstance | null>(null);
 	public disabled: ComputedRef<boolean>;
 
@@ -85,8 +85,35 @@ export class FormValidate {
 	validate = (callback: (valid: boolean, errors: ClFormValidateError[]) => void): void => {
 		this.formRef.value!.validate(callback);
 	};
+
+	// 检查字段是否存在错误
+	isError = (prop: string): boolean => {
+		return this.formRef.value!.getError(prop) != "";
+	};
+}
+
+class FormItem {
+	public isError: ComputedRef<boolean>;
+
+	constructor() {
+		const { isError } = new Form();
+		const ClFormItem = useParent<ClFormItemComponentPublicInstance>("cl-form-item");
+
+		// 监听表单字段是否验证错误
+		this.isError = computed<boolean>(() => {
+			if (ClFormItem == null) {
+				return false;
+			}
+
+			return isError(ClFormItem.prop);
+		});
+	}
 }
 
-export const useForm = (): FormValidate => {
-	return new FormValidate();
+export const useForm = (): Form => {
+	return new Form();
+};
+
+export const useFormItem = (): FormItem => {
+	return new FormItem();
 };

+ 2 - 4
uni_modules/cool-ui/types/component.d.ts

@@ -189,8 +189,6 @@ declare type ClFormComponentPublicInstance = {
 };
 
 declare type ClFormItemComponentPublicInstance = {
-	validate: () => Promise<boolean>;
-	clearValidate: () => void;
-	hasError: boolean;
-	currentError: string;
+	prop: string;
+	isError: boolean;
 };