表单项添加红色边框的错误提示
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) != "";
|
||||
};
|
||||
}
|
||||
|
||||
export const useForm = (): FormValidate => {
|
||||
return new FormValidate();
|
||||
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 = (): Form => {
|
||||
return new Form();
|
||||
};
|
||||
|
||||
export const useFormItem = (): FormItem => {
|
||||
return new FormItem();
|
||||
};
|
||||
|
||||
6
uni_modules/cool-ui/types/component.d.ts
vendored
6
uni_modules/cool-ui/types/component.d.ts
vendored
@@ -189,8 +189,6 @@ declare type ClFormComponentPublicInstance = {
|
||||
};
|
||||
|
||||
declare type ClFormItemComponentPublicInstance = {
|
||||
validate: () => Promise<boolean>;
|
||||
clearValidate: () => void;
|
||||
hasError: boolean;
|
||||
currentError: string;
|
||||
prop: string;
|
||||
isError: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user