更新记录

0.0.1(2025-05-16)

  • init

平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 4.64 app-vue app-uvue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序 鸿蒙元服务
× × × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× ×

lime-form 表单

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

文档

form

安装

插件市场导入

代码演示

基础使用

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

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

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

方向与对齐

<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="">重置</l-button>
    </view>
</l-form>
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  = () => {
    console.log('===');
    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[],

};

查看示例

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

插件标签

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

API

Form Props 属性说明

属性名 类型 默认值 必填 说明
contentAlign 'left' | 'right' 'left' 表单内容对齐方式
data Object {} 表单数据对象
disabled boolean - 是否禁用整个表单
readonly boolean - 是否只读该表单内的所有组件
labelWidth string | number '86px' 标签宽度
requiredMark boolean - 是否显示必填符号(*)
resetType 'empty' | 'initial' 'empty' 重置表单方式:empty-置空,initial-恢复初始值
rules Object - 表单字段校验规则集
scrollToFirstError boolean true 是否自动滚动到第一个校验不通过的字段
showErrorMessage boolean true 是否显示错误提示信息
layout 'horizontal' | 'vertical' 'horizontal' 表单布局方式
labelAlign 'start' | 'center' | 'end' 'start' 标签水平对齐方式
labelValign 'start' | 'center' | 'end' 'start' 标签垂直对齐方式
asteriskPosition 'left' | 'right' 'left' 必填星号位置
errorMessage FormErrorMessage - 自定义错误提示文案配置对象
scrollIntoViewOptions Object - 滚动到错误项的配置参数
scrollDuration number - 滚动动画持续时间(单位: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>

参数类型说明:

// 验证参数类型
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: 当前验证结果(布尔或错误对象)
// 通用验证结果类型
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 验证规则配置

属性 类型 默认值 必填 说明
required boolean - 是否必填项,显示红色星号标记
message string - 校验失败时的提示信息
trigger 'change' | 'blur' 'change' 校验触发时机
type 'error' | 'warning' 'error' 校验失败提示类型
whitespace boolean - 是否必须包含非空格内容
enum string[] - 枚举值校验(值必须在数组中)
len number - 严格长度校验(中文=2字符)
min number - 最小长度/数值校验
max number - 最大长度/数值校验
pattern RegExp - 正则表达式校验
validator function - 自定义校验函数
number boolean - 必须为数字类型
boolean boolean - 必须为布尔类型
email boolean | object - 邮箱格式校验
url boolean | object - URL格式校验
idcard boolean - 身份证格式校验
telnumber boolean - 手机号格式校验
date boolean | object - 日期格式校验

FormItem Props 属性说明

属性名 类型 默认值 必填 说明
arrow boolean false 是否显示右侧箭头
contentAlign 'left' | 'right' - 表单内容对齐方式(优先级高于 Form)
help string - 表单项说明内容
label string '' 字段标签文本
labelWidth string | number - 标签宽度(优先级高于 Form)
name string - 表单字段唯一标识
requiredMark boolean null 是否显示必填符号(优先级高于 Form)
rules Array<FormRule|UTSJSONObject> - 表单项校验规则集合
showErrorMessage boolean null 是否显示错误提示(优先级高于 Form)
layout "horizontal" | "vertical" - 布局方式(优先级高于 Form)
labelAlign "start" | "center" | "end" - 标签水平对齐方式
labelValign "start" | "center" | "end" - 标签垂直对齐方式
bordered boolean true 是否显示下边框

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

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问