Skip to content

LimeForm 表单

  • 用以收集、校验和提交数据,一般由输入框、单选框、复选框、选择器等控件组成
  • 插件依赖lime-style,lime-shared不喜勿下

文档

form

安装

插件市场入口 导入

代码演示

基础使用

在表单中,每个 l-form-item 组件 代表一个表单项,使用 l-form-itemrules 属性定义校验规则。

html
<l-form ref="formRef" :data="formData">
	<l-form-item name="username" label="用户名" :rules="[{ required: true, message: '请填写用户名' }]">
		<l-input :value="formData['username']" @change="($event: string) => {formData['username'] = $event}" placeholder="用户名" :bordered="false"></l-input>
	</l-form-item>
	<l-form-item name="password" label="密码" :rules="[{ required: true, message: '请填写密码' }]">
		<l-input :value="formData['password']" @change="($event: string) => {formData['password'] = $event}" type="password" placeholder="密码" :bordered="false"></l-input>
	</l-form-item>

	<view style="padding: 16px;">
		<l-button block type="primary" form-type="submit" @click="onSubmit">提交</l-button>
	</view>
</l-form>
js
export default {
	data() {
		return {
			formData: {
				username: '',
				password: '',
			}
		}
	},
	methods: {
		onSubmit() {
			(this.$refs['formRef'] as LFormComponentPublicInstance).submit(null).then(res => {
				// 返回错误信息
				console.log('res', res)
			})
		}
	}
}

校验规则

通过 rules 定义表单校验规则,所有可用字段见下方表格。rules 可以加给 from 也可以加给 form-item, form-item 会覆盖 from

html
<l-form ref="formRef" :data="formData">
	<!-- 布尔类型校验 -->
	<l-form-item name="boolean" label="布尔类型" :rules="[{ boolean: true, message: '必须接受协议' }]">
		<l-checkbox :checked="formData['boolean']"
			@change="(val: boolean) => {formData['boolean'] = val}">同意协议</l-checkbox>
	</l-form-item>

	<!-- 日期格式校验(短横线分隔) -->
	<l-form-item name="date" label="日期" :rules="[{ date: { delimiters: ['-'], format: 'YYYY-MM-DD' }, message: '日期格式应为YYYY-MM-DD' }]">
		<l-input :bordered="false" :value="formData['date']" @change="(val: string) => {formData['date'] = val}"
			placeholder="2000-01-01" />
	</l-form-item>

	<!-- 邮箱格式校验 -->
	<l-form-item name="email" label="邮箱" :rules="[{ email: true, message: '请输入有效邮箱' }]">
		<l-input :bordered="false" :value="formData['email']"
			@change="(val: string) => {formData['email'] = val}" placeholder="name@example.com" />
	</l-form-item>

	<!-- 身份证校验 -->
	<l-form-item name="idcard" label="身份证" :rules="[{ idcard: true, message: '请输入有效身份证号' }]">
		<l-input :bordered="false" :value="formData['idcard']"
			@change="(val: string) => {formData['idcard'] = val}" placeholder="18位身份证号" />
	</l-form-item>

	<!-- 固定长度校验 -->
	<l-form-item name="len" label="固定长度" :rules="[{ len: 5, message: '必须5个字符' }]">
		<l-input :bordered="false" :value="formData['len']" @change="(val: string) => { formData['len'] = val }"
			placeholder="输入5个字符" />
	</l-form-item>

	<!-- 最大值校验(数字) -->
	<l-form-item name="maxNumber" label="最大数值" :rules="[{ max: 100, message: '不能超过100' }]">
		<l-input :bordered="false" type="number" :value="formData['maxNumber']"
			@change="(val: any) =>{ formData['maxNumber'] = val }" placeholder="<=100" />
	</l-form-item>

	<!-- 最小值校验(文本长度) -->
	<l-form-item name="minText" label="最小长度" :rules="[{ min: 3, message: '至少3个字符' }]">
		<l-input :bordered="false" :value="formData['minText']"
			@change="(val: string) =>{ formData['minText'] = val }" placeholder="至少3个字" />
	</l-form-item>

	<!-- 数字类型校验 -->
	<l-form-item name="number" label="数字" :rules="[{ number: true, message: '请输入数字' }]">
		<l-input :bordered="false" type="number" :value="formData['number']"
			@change="(val: any) => {formData['number'] = val}" />
	</l-form-item>

	<!-- 正则校验 -->
	<l-form-item name="pattern" label="正则" :rules="[{ pattern: /\d{6}/, message: '请输入6位数字' }]">
		<l-input :bordered="false" :value="formData['pattern']"
			@change="(val: string) =>{ formData['pattern'] = val }" placeholder="6位数字" />
	</l-form-item>

	<!-- 必填校验 -->
	<l-form-item name="required" label="必填项" :rules="[{ required: true, message: '此项必填' }]">
		<l-input :bordered="false" :value="formData['required']"
			@change="(val: string) => {formData['required'] = val }" />
	</l-form-item>

	<!-- 手机号校验 -->
	<l-form-item name="telnumber" label="手机号" :rules="[{ telnumber: true, message: '请输入有效手机号' }]">
		<l-input :bordered="false" :value="formData['telnumber']"
			@change="(val: number) => {formData['telnumber'] = val}" placeholder="11位手机号" />
	</l-form-item>

	<!-- URL校验 -->
	<l-form-item name="url" label="网址" :rules="[{ url: { protocols: ['http','https'] }, message: '请输入有效URL' }]">
		<l-input :bordered="false" :value="formData['url']" @change="(val: string) => {formData['url'] = val}"
			placeholder="https://example.com" />
	</l-form-item>

	<!-- 自定义校验 -->
	<l-form-item name="custom" label="自定义" :rules="customRules">
		<l-input :bordered="false" :value="formData['custom']"
			@change="(val: string) => {formData['custom'] = val}" @blur="" />
	</l-form-item>

	<view style="padding: 16px;">
		<l-button block type="primary" @click="onSubmit">提交</l-button>
	</view>
</l-form>
js
import type { FormRule } from '@/uni_modules/lime-form';

const formData = reactive<UTSJSONObject>({
	boolean: false,
	date: '',
	email: '',
	enum: '',
	idcard: '',
	len: '',
	maxNumber: '',
	minText: '',
	number: '',
	pattern: '',
	required: '',
	telnumber: '',
	url: '',
	custom: ''
})

const formRef = ref<LFormComponentPublicInstance | null>(null)
const customRules = [{
	validator: (v : any) : Promise<boolean> => {
		return new Promise((resolve) => {
			uni.showToast({
				title: '异步校验中'
			})
			setTimeout(() => {
				resolve(`${v}`.includes('limeui'))
			}, 1000)

		})
	},
	message: '必须包含limeui',
	trigger: 'blur'
} as FormRule]

const onSubmit = () => {
	if(formRef.value == null) return
	formRef.value!.validate(null).then(res => {
		console.log('验证结果:', res);
	});
}

方向与对齐

html
<view class="sub-box">
	<text class="sub-title" style="padding-top:0">排列方向</text>
	<l-radio-group v-model="direction" direction="horizontal">
		<l-radio value="vertical">vertical</l-radio>
		<l-radio style="margin-left: 10px;" value="horizontal">horizontal</l-radio>
	</l-radio-group>

	<text class="sub-title">水平对齐</text>
	<l-radio-group v-model="labelAlign" direction="horizontal">
		<l-radio value="start">start</l-radio>
		<l-radio style="margin-left: 10px;" value="center">center</l-radio>
		<l-radio style="margin-left: 10px;" value="end">end</l-radio>
	</l-radio-group>

	<text class="sub-title">垂直对齐</text>
	<l-radio-group v-model="labelValign" direction="horizontal">
		<l-radio value="start">start</l-radio>
		<l-radio style="margin-left: 10px;" value="center">center</l-radio>
		<l-radio style="margin-left: 10px;" value="end">end</l-radio>
	</l-radio-group>

	<text class="sub-title">星号位置</text>
	<l-radio-group v-model="starPosition" direction="horizontal">
		<l-radio value="left">left</l-radio>
		<l-radio style="margin-left: 10px;" value="right">right</l-radio>
	</l-radio-group>
	<text class="sub-title">禁用只读</text>
	<view style="flex-direction: row;">
		<l-checkbox v-model="disabled">禁用</l-checkbox>
		<l-checkbox v-model="readonly" style="margin-left: 10px;">只读</l-checkbox>
	</view>
</view>
<l-form ref="formRef" :rules="rules" :layout="direction" :labelAlign="labelAlign" :labelValign="labelValign"
	:asteriskPosition="starPosition" :disabled="disabled" :readonly="readonly" :data="formData">
	<l-form-item label="用户名" name="name" help="输入用户名">
		<l-input :value="formData['name']" :bordered="false" placeholder="请输入内容"
			@change="($event: string) => {formData['name'] = $event}" />
	</l-form-item>

	<l-form-item label="密码" name="password">
		<l-input :value="formData['password']" :bordered="false" type="password" :clearable="false"
			placeholder="请输入内容" @change="($event: string) => {formData['password'] = $event}">
			<template #suffix-icon>
				<view @click="browseChange">
					<l-icon size="24px" v-if="browseOff" name="browse-off"></l-icon>
					<l-icon size="24px" v-if="!browseOff" name="browse"></l-icon>
				</view>
			</template>
		</l-input>
	</l-form-item>
	<l-form-item label="性别" name="gender">
		<l-radio-group :value="formData.gender" @change="($event: string) => {formData['gender'] = $event}"
			style="justify-content: space-between;" :bordered="false">
			<l-radio name="radio" value="man" label="男" />
			<l-radio name="radio" value="women" label="女" />
			<l-radio name="radio" value="secret" label="保密" />
		</l-radio-group>
	</l-form-item>
	<l-form-item arrow label="生日" name="birth">
		<l-input :value="formData.birth" readonly :bordered="false" placeholder="请输入内容" @tap="visible = true">
		</l-input>
		<l-popup v-model="visible" position="bottom">
			<l-date-time-picker :value="formData.birth" cancel-btn="取消" confirm-btn="确实" title="选择日期"
				start="2015-5-5" format="YYYY-MM-DD" @change="onChange" @pick="onPick" @confirm="onConfirm"
				@cancel="onCancel" />
		</l-popup>
	</l-form-item>
	<l-form-item arrow label="籍贯" name="place">
		<l-input readonly :value="formData.place" :bordered="false" placeholder="请输入内容"
			@tap="showCascader"></l-input>
		<l-cascader v-model:visible="visibleCascader" :value="address" title="选择地址" :options="options"
			@change="onChangeCascader" />
	</l-form-item>
	<l-form-item label="年限" name="age">
		<l-stepper :value="formData.age" theme="filled" @change="onChangeStepper" />
	</l-form-item>
	<l-form-item label="自我评价" name="description">
		<l-rate :value="formData.description" @change="($event: number) => {formData['description'] = $event}"
			variant="filled" allow-half :gap="rateGap" />
	</l-form-item>
	<l-form-item label="个人简介" name="resume">
		<l-textarea :value="formData.resume" class="textarea" indicator :bordered="false" :maxlength="50"
			placeholder="请输入个人简介"></l-textarea>
	</l-form-item>
	<l-form-item label="上传照片" name="photo">
		<l-upload :default-files="formData.photo" multiple :max="8" :action="action" @fail="onFail"
			@success="onSuccess" @remove="onRemove">
		</l-upload>
	</l-form-item>
	<l-form-item label="兴趣爱好" name="hobbies">
		<l-checkbox-group :value="formData.hobbies" @change="($event: string[]) => {formData['hobbies'] = $event}"
			style="justify-content: space-between;">
			<l-checkbox value="reading">阅读</l-checkbox>
			<l-checkbox value="sports">运动</l-checkbox>
			<l-checkbox value="music">音乐</l-checkbox>
		</l-checkbox-group>
	</l-form-item>

	<l-form-item label="满意度" name="satisfaction">
		<l-slider :value="formData.satisfaction" :min="0" :max="100" :step="10" show-value
			@change="($event: number) => {formData['satisfaction'] = $event}" />
	</l-form-item>

	<l-form-item label="接收通知" name="notifications">
		<l-switch :value="formData.notifications"
			@change="($event: boolean) => {formData['notifications'] = $event}" active-text="开启"
			inactive-text="关闭" />
	</l-form-item>
	<view class="button-group">
		<l-button type="primary" style="flex:1" form-type="submit" @click="onSubmit">提交</l-button>
		<l-button type="default" style="flex:1;margin-left:10px" form-type="reset" @click="onReset">重置</l-button>
	</view>
</l-form>
js
import type { FormRule } from '@/uni_modules/lime-form'

// upload
const onFail = (e : any) => {
	console.log('---onFail', e);
};


const onSuccess = (e : UTSJSONObject) => {
	console.log('====onSuccess', e);
};
const onRemove = (e : UTSJSONObject) => {
	console.log('====onRemove', e);
};

const action = 'https://service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo';
const files = ref([
	{
		url: 'https://picsum.photos/200/300?random=12',
		name: 'uploaded1.png',
		type: 'image',
	},
	{
		url: 'https://picsum.photos/200/300?random=10',
		name: 'uploaded2.png',
		type: 'image',
	},
]);

const formData = reactive<UTSJSONObject>({
	name: '',
	password: '',
	gender: '',
	birth: '',
	place: '',
	age: 3,
	description: 2,
	resume: '',
	photo: files,
	hobbies: [] as string[],
	satisfaction: 50,
	notifications: false,
});


const browseOff = ref(false)

const browseChange = () => {
	browseOff.value = !browseOff.value
}

const visible = ref(false)
const onChange = (value : string) => {
	console.log('change: ', value);
};

const onPick = (value : string) => {
	console.log('pick: ', value);
};

const onCancel = () => {
	console.log('cancel');
	visible.value = false;
};

const onConfirm = (value : string) => {
	console.log('confirm: ', value);
	formData.birth = value;
	visible.value = false;
};


// 级联选择器
const data = {
	areaList: [
		{
			label: '北京市',
			value: '110000',
			children: [
				{
					value: '110100',
					label: '北京市',
					children: [
						{ value: '110101', label: '东城区' },
						{ value: '110102', label: '西城区' },
						{ value: '110105', label: '朝阳区' },
						{ value: '110106', label: '丰台区' },
						{ value: '110107', label: '石景山区' },
						{ value: '110108', label: '海淀区' },
						{ value: '110109', label: '门头沟区' },
						{ value: '110111', label: '房山区' },
						{ value: '110112', label: '通州区' },
						{ value: '110113', label: '顺义区' },
						{ value: '110114', label: '昌平区' },
						{ value: '110115', label: '大兴区' },
						{ value: '110116', label: '怀柔区' },
						{ value: '110117', label: '平谷区' },
						{ value: '110118', label: '密云区' },
						{ value: '110119', label: '延庆区' },
					],
				},
			],
		},
		{
			label: '天津市',
			value: '120000',
			children: [
				{
					value: '120100',
					label: '天津市',
					children: [
						{ value: '120101', label: '和平区' },
						{ value: '120102', label: '河东区' },
						{ value: '120103', label: '河西区' },
						{ value: '120104', label: '南开区' },
						{ value: '120105', label: '河北区' },
						{ value: '120106', label: '红桥区' },
						{ value: '120110', label: '东丽区' },
						{ value: '120111', label: '西青区' },
						{ value: '120112', label: '津南区' },
						{ value: '120113', label: '北辰区' },
						{ value: '120114', label: '武清区' },
						{ value: '120115', label: '宝坻区' },
						{ value: '120116', label: '滨海新区' },
						{ value: '120117', label: '宁河区' },
						{ value: '120118', label: '静海区' },
						{ value: '120119', label: '蓟州区' },
					],
				},
			],
		},
	],
};
const options = data.areaList;
const address = ref('120119');
const visibleCascader = ref(false);

const onChangeCascader = (value : string, options : UTSJSONObject[]) => {
	formData.place = options?.map((item : UTSJSONObject) => `${item['label'] ?? ''}`).join('/');
	visibleCascader.value = false;
};

const showCascader = () => {
	visibleCascader.value = true;
};

// 步进器
const onChangeStepper = ($event : number) => {
	formData.age = $event;
};

// rate
const rateGap = 8;

// form
const formRef = ref<LFormComponentPublicInstance|null>(null)
const onReset = () => {
	console.log('===onReset');
	formRef.value?.reset?.(null)
};

const onSubmit = (e : any) => {
	formRef.value?.submit?.(null)
	console.log('===onSubmit', e);
};

const rules = {
	name: [{ required: true, validator: (val : any) => `${val}`.length == 8, message: '只能输入8个字符英文' }] as FormRule[],
	password: [{ validator: (val : any) => `${val}`.length > 6, message: '长度大于6个字符' }] as FormRule[],
	gender: [{ validator: (val : any) => val != '', message: '不能为空' }] as FormRule[],
	birth: [{ validator: (val : any) => val != '', message: '不能为空' }] as FormRule[],
	place: [{ validator: (val : any) => val != '', message: '不能为空' }] as FormRule[],
	description: [{ validator: (val : any) => (val as number) > 3, message: '分数过低会影响整体评价' }] as FormRule[],
	resume: [{ validator: (val : any) => val != '', message: '不能为空' }] as FormRule[],
	hobbies: [{
		validator: (val : any) => (val as string[]).length > 0,
		message: '请至少选择一项兴趣爱好'
	}] as FormRule[],
	satisfaction: [{
		validator: (val : any) => (val as number) >= 60,
		message: '满意度需达到60%以上'
	}] as FormRule[],
	notifications: [{
		required: true,
		validator: (val : any) => val == true,
		message: '需要开启通知接收'
	}] as FormRule[],

};

查看示例

  • 导入后直接使用这个标签查看演示效果
html
<!-- // 代码位于 uni_modules/lime-form/compoents/lime-form -->
<lime-form />

插件标签

  • 默认 l-form 为 component
  • 默认 lime-form 为 demo

API

Form Props 属性说明

属性名类型默认值必填说明
contentAlign'left' | 'right''left'表单内容对齐方式
dataObject{}表单数据对象
disabledboolean-是否禁用整个表单
readonlyboolean-是否只读该表单内的所有组件
labelWidthstring | number'86px'标签宽度
requiredMarkboolean-是否显示必填符号(*)
resetType'empty' | 'initial''empty'重置表单方式:empty-置空,initial-恢复初始值
rulesObject-表单字段校验规则集
scrollToFirstErrorbooleantrue是否自动滚动到第一个校验不通过的字段
showErrorMessagebooleantrue是否显示错误提示信息
layout'horizontal' | 'vertical''horizontal'表单布局方式
labelAlign'start' | 'center' | 'end''start'标签水平对齐方式
labelValign'start' | 'center' | 'end''start'标签垂直对齐方式
asteriskPosition'left' | 'right''left'必填星号位置
errorMessageFormErrorMessage-自定义错误提示文案配置对象
scrollIntoViewOptionsObject-滚动到错误项的配置参数
scrollDurationnumber-滚动动画持续时间(单位:ms)

Form Expose 组件暴露方法

通过 ref 可调用的表单控制方法:

方法名说明参数返回值
validate执行表单验证(带UI反馈)(params?: FormValidateParams)Promise<UTSJSONObject | boolean>
submit手动触发表单提交(params?: FormValidateParams)Promise<UTSJSONObject>
reset手动重置表单数据(params?: FormResetParams)void
clearValidate清除验证状态(fields?: string[])void
setValidateMessage设置自定义验证消息(validateMessages: FormValidateMessage)void
validateOnly静默验证(无UI反馈)(params?: FormValidateParams)Promise<UTSJSONObject | boolean>

参数类型说明:

js
// 验证参数类型
type FormValidateParams = {
  fields?: string[]    // 指定校验字段名数组
  trigger?: 'all' | 'change' | 'blur' // 校验触发方式
  showErrorMessage?: boolean // 是否显示错误信息
}

// 重置参数类型
type FormResetParams = {
  type?: 'empty' | 'initial'  // 重置方式:empty-置空,initial-恢复初始值
  fields?: string[]    // 指定重置字段名数组
}

// 自定义验证消息
type FormItemValidateMessage = {
	type: 'warning' | 'error';
	message: string;
}

// 验证消息类型
type FormValidateMessage = UTSJSONObject & {
  [fieldName: string]: FormItemValidateMessage[] // 字段名对应验证消息数组
}

// 验证指定字段
const formRef = ref<LFormComponentPublicInstance|null>(null)
formRef.value.validate({ 
  fields: ['username', 'email'],
  trigger: 'blur'
})

// 重置密码字段为初始值
formRef.value.reset({
  type: 'initial',
  fields: ['password']
})

// 设置自定义错误消息
formRef.value.setValidateMessage({
  username: [{ message: '该用户名已被注册', type: 'error' }]
})

Form Emits 事件声明

事件名说明回调参数参数类型说明
reset表单重置时触发(e: UniFormResetEvent | null)原生表单重置事件对象
submit表单提交时触发{ validateResult: Result, firstError: string }
  • validateResult: 整体验证结果(true 或错误对象)
  • firstError: 首个错误信息文本
validate任一表单项验证后触发{ validateResult: Result }validateResult: 当前验证结果(布尔或错误对象)
js
// 通用验证结果类型
type Result = UTSJSONObject | boolean

// 验证结果详情示例
type ValidateResult = {
  username: ValidateResultType[]
  password: ValidateResultType[]
}

// 单个验证结果类型
type ValidateResultType = {
  result: boolean
  message: string
  type?: 'error' | 'warning' | 'success'
  rule?: RuleType  // 规则类型(如required/max/min等)
  value?: any
}

FormRule 验证规则配置

属性类型默认值必填说明
requiredboolean-是否必填项,显示红色星号标记
messagestring-校验失败时的提示信息
trigger'change' | 'blur''change'校验触发时机
type'error' | 'warning''error'校验失败提示类型
whitespaceboolean-是否必须包含非空格内容
enumstring[]-枚举值校验(值必须在数组中)
lennumber-严格长度校验(中文=2字符)
minnumber-最小长度/数值校验
maxnumber-最大长度/数值校验
patternRegExp-正则表达式校验
validatorfunction-自定义校验函数
numberboolean-必须为数字类型
booleanboolean-必须为布尔类型
emailboolean | object-邮箱格式校验
urlboolean | object-URL格式校验
idcardboolean-身份证格式校验
telnumberboolean-手机号格式校验
dateboolean | object-日期格式校验

FormItem Props 属性说明

属性名类型默认值必填说明
arrowbooleanfalse是否显示右侧箭头
contentAlign'left' | 'right'-表单内容对齐方式(优先级高于 Form)
helpstring-表单项说明内容
labelstring''字段标签文本
labelWidthstring | number-标签宽度(优先级高于 Form)
namestring-表单字段唯一标识
requiredMarkbooleannull是否显示必填符号(优先级高于 Form)
rulesArray<FormRule|UTSJSONObject>-表单项校验规则集合
showErrorMessagebooleannull是否显示错误提示(优先级高于 Form)
layout"horizontal" | "vertical"-布局方式(优先级高于 Form)
labelAlign"start" | "center" | "end"-标签水平对齐方式
labelValign"start" | "center" | "end"-标签垂直对齐方式
borderedbooleantrue是否显示下边框

FormItem Expose 暴露方法说明

通过 ref 可调用的表单项控制方法:

方法名说明参数返回值
blur触发失焦验证-void
resetHandler重置验证状态-void
resetField重置字段值(type?: 'initial' | 'empty')void
validate执行字段验证(trigger: 'change'|'blur'|'all', showError?: boolean)Promise<FormItemValidateResult>
validateOnly静默验证(trigger: 'change'|'blur'|'all')Promise<FormItemValidateResult>
scrollToField滚动到当前字段-Promise<void>
setValidateMessage设置自定义验证消息(messages: FormItemValidateMessage[])void

源代码

组件源码