vant

lishihuan大约 7 分钟

vant

1.

vant使用过程中的记录

1.van-tab一直默认选中第一个tab

1.1 通过 v-model:active实现选中, 官网说明支持 number | string,但是使用出现数字无法实现默认选中,需要讲绑定的字段转为字符串

<van-tabs v-model:active="activeMonth" @change="changeMonth">
    <van-tab v-for="item in DateList[activeYearIndex].months" :title="item + '月'" :name="item"></van-tab>
</van-tabs>

不加 name 属性,则是通过下标

1.2.van-tab 数据更新后,赋值后又被改回默认选择第一个tab,因为没有初始化好,可以通过$nextTick

this.activeMonthIndex=activeMonthIndex;// 不在这里赋值,会导致数据赋值滞后
var this_=this;
this.$nextTick(() => {
	this_.activeMonthIndex=activeMonthIndex;// 需要重新赋值,否则会被赋值为 0 默认加载第一个tab
})

2. vant 下拉刷新数据加载2次

原因是在于下拉刷新的时候触发了上拉加载,所以执行了两次

解决方法是:先将list组价的finished=true数据加载完了在判断该值应该是true还是false,这样可以避免在下拉刷新的时候触发上拉加载。

image-20220906142801786
image-20220906142801786

3.DatetimePicker 时间选择

初始化时间插件,如果出现没有默认选中,可能是 时间格式 :new Date(xxxxx)

this.currentDate=new Date('2022-06-16');

van-tabs 嵌入的内容如果是自定义表单,则不能使用循环

van-tabs 下面的这种写法是错误的

在使用 this.$refs.listRef 就会发现 会出现多个

<van-tabs v-model:active="active"  sticky :offset-top="offsetTop" class="page-tabs" ><!--swipeable-->
    <van-tab v-for="item in tabs" :title="item.name" :name="item.listType">
        <act-task-list procDefKey="zj_user_flow" dataType="task" v-if="listType == '1'" />
        <act-task-list procDefKey="zj_user_flow" dataType="hisTask" v-if="listType == '2'" />
        <apply-list v-if="listType == '3'" />
    </van-tab>
</van-tabs>
  • 正确
<van-tabs v-model:active="listType"  sticky :offset-top="offsetTop" class="page-tabs" @change="changeTab"><!--swipeable-->
    <van-tab title="待处理" name="1"  >
        <act-task-list key="zj_user_flow-task" procDefKey="zj_user_flow" dataType="task" ref="listRef" v-if="listType=='1'"/>
    </van-tab>
    <van-tab title="已处理" name="2" >
        <act-task-list key="zj_user_flow-hisTask" procDefKey="zj_user_flow" dataType="hisTask" ref="listRef"  v-if="listType=='2'" />
    </van-tab>
    <van-tab title="全部" name="3"  >
        <apply-list ref="listRef" v-if="listType=='3'" />
    </van-tab>
</van-tabs>

表单验证

表单验证

this.$refs.xxx.validate().then(()=>{
	// 验证通过
}).catch(()=>{
	//验证失败
})

如果想单独校验某个输入框,则需要指定name 属性否则找到不到

 <van-field v-model="form.unitName" name="unitName"
:rules="rules.unitName" label="监督单位" placeholder="请选择监督单位"/>


// 必填校验
rules: {
   unitName: [{ required: true, message: "请选择监督单位", trigger: "blur" }],
  applyRemarks: [{ required: true, message: "请输入", trigger: "blur" }],
},
this.$refs.xxx.validate('unitName').then(()=>{
	// 验证通过
}).catch(()=>{
	//验证失败
})

自定义表单校验

将表单校验提示信息放到

export default {
    data() {
        return { };
    },
    methods: {
        /**
         * 自定义 表单校验
         *      1. van-field 标签需要使用name 属性
         *      2. 需要指定ref :命名- rield+'Ref'
         *      3. 可以使用 @blur focus update:model-value
         */
        setFieldPlaceholder(prop) {
            const field = this.$refs[prop + 'Ref'];
            if (field) {  // 如果存在字段,则设置占位文本
                this.$refs.form.validate(prop).catch(error => {
                    if (error) {
                        const message = error.message || '';
                        field.$el.querySelector('.van-field__value').classList.add('error')
                        field.$el.querySelector('.van-field__body .van-field__control').setAttribute('placeholder', message);
                    } else {
                        field.$el.querySelector('.van-field__value').classList.remove('error');
                        field.$el.querySelector('.van-field__body .van-field__control').setAttribute('placeholder', field.placeholder)
                    }
                })
            }
        },
        /**
         * 表单校验 ---submit时
         */
        showValidationErrors(){
            Object.keys(this.rules).forEach(key => {
                this.setFieldPlaceholder(key);
            });
        }
    }
};
<van-field v-model="form.unitName" name="unitName" @blur="setFieldPlaceholder('unitName')"  ref="unitNameRef"
                               required  :rules="rules.unitName" label="监督单位"
                               placeholder="请选择监督单位"  />
this.$refs.form.validate().then((res) => {
    // 校验成功

}).catch(error => {
    // 表单校验失败
    this.showValidationErrors(); // 迁移到 混入mixin里面
})

身份证校验

data () {
            let validateIdNumber = (idNumber) => {
                debugger
                if (!idNumber) {
                    return false;
                }

                if (idNumber.length === 15) {
                    // 一代身份证号码格式:
                    //      地址码(6位) + 生日(6位) + 顺序码(3位)
                    if (!/^[1-9]\d{5}\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}$/.test(idNumber)) {
                        return false;
                    }
                    // 校验出生日期
                    const year = parseInt(idNumber.substr(6, 2)) + 1900;
                    const month = parseInt(idNumber.substr(8, 2)) - 1;
                    const day = parseInt(idNumber.substr(10, 2));
                    const date = new Date(year, month, day);
                    if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
                        return false;
                    }
                } else if (idNumber.length === 18) {
                    // 二代身份证号码格式:
                    //      地址码(6位) + 生日(8位) + 顺序码(3位) + 校验码(1位)
                    if (!/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(idNumber)) {
                        return false;
                    }
                    // 校验出生日期
                    const year = parseInt(idNumber.substr(6, 4));
                    const month = parseInt(idNumber.substr(10, 2)) - 1;
                    const day = parseInt(idNumber.substr(12, 2));
                    const date = new Date(year, month, day);
                    if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
                        return false;
                    }
                    // 校验校验码:参考 GB 11643-1999 标准
                    const checkCodes = '10X98765432';
                    let sum = 0;
                    for (let i = 0; i < 17; i++) {
                        sum += parseInt(idNumber[i]) * Math.pow(2, 17 - i);
                    }
                    const checkCode = checkCodes.charAt(sum % 11);
                    if (checkCode !== idNumber.charAt(17).toUpperCase()) {
                        return false;
                    }
                } else {
                    // 非法长度
                    return false;
                }

                // 校验地区码
                const regionCodes = [
                    '11', '12', '13', '14', '15', '21', '22', '23', '31', '32', '33', '34', '35', '36', '37', '41',
                    '42', '43', '44', '45', '46', '50', '51', '52', '53', '54', '61', '62', '63', '64', '65', '71',
                    '81', '82', '91'
                ];
                const regionCode = idNumber.substr(0, 2);
                if (!regionCodes.includes(regionCode)) {
                    return false;
                }

                // 校验顺序码
                const seqCode = idNumber.substr(-4, 3);
                if (!/^\d{3}$/.test(seqCode)) {
                    return false;
                }

                return true;
            }
            return {
                isDisabled: false,
                // 人员状态status(user_status1:正常;2:失效,例如离职等;3:待审核;4:审核未通过)字典
                statusOptions: [],
                sexOptions: [], // 性别 0-男 1-女 2 未知
                moduleName: 'user_files',
                // 表单参数
                form: {},
                testingCompanyList: [],// 检测公司 下拉选数据
                rules: {
                    name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
                    identity: [
                        { required: true, message: '请输入身份证号码', trigger: 'blur' },
                        {
                            pattern: /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/,
                            message: '请输入正确的证件号'
                        },
                        {
                            validator: (value, rule) => validateIdNumber(value),
                            message: '请输入正确的身份证号码'
                        }
                    ],
                },
            }
        },

列表+侧滑菜单

https://blog.csdn.net/m0_49529959/article/details/125087941open in new window

输入框限定数字限定

 <van-field v-model="form.flBlqNum" label="避雷器" placeholder="请输入防雷避雷器数量(个)" @keyup="formatValidationNumber($event, 'flBlqNum')"/>
formatValidationNumber(event, fieldKey) {
    const obj = event.target;
    obj.value = obj.value.toString().match(/^\d{0,8}(?:\.\d{0,4})?/);
    this.form[fieldKey] = obj.value;
},

整数或小数

<van-field v-model="form.pileLength" label="桩长/m" placeholder="请填写桩长"
                                               @keyup="formatInportData($event.target.value,'10,2','pileLength',form)"/>
/**
 * 针对 input 对数字格式化 -- 限定只能输入整数和指定格式的小数
 * 分为 小数/整数
 * @param value : 需要校验的数字
 * @param format:指定格式 小数或者整数  "8,4"|| "8"
 * @param field:需要跟新的自动
 * @param Obj:实体对象 例如表单对象--form
 */
export const formatInportData = (value,format,field,Obj) =>{
    const formatArr = format.split(",");
    let regx2 = '';
    if (formatArr.length == 1) { // 说明当前校验的是整数
        // value = value.replace(/[^\d]/g,'');
        regx2=new RegExp(`^\\d{0,${formatArr[0]}}`);
    } else { // 小数
        regx2=new RegExp(`^\\d{0,${formatArr[0]}}(?:\\.\\d{0,${formatArr[1]}})?`);
        // value = value.toString().match(/^\d{0,8}(?:\.\d{0,4})?/);
    }
    value = value.toString().match(regx2);
    value = value[0];
    Obj[field] =  value;
    // 如果 数据不刷新,使用 set方法,目前使用没遇到问题
    // Vue.set([Obj],field,value)// 不能直接使用 this.$set
}
/**
 * 限定输入的是数字
 * @param value
 * @param maxLenght
 * @param field:需要跟新的自动
 * @param Obj:实体对象 例如表单对象--form
 */
export const formatInportNum = (value, maxLength, field, Obj) => {
    let regx = new RegExp(`^(\\-|\\+)?\\d{0,}(?:\\.\\d{0,})?`);//new RegExp(`^(\\-|\\+)?\\d+(\\.\\d+)?$`);
    value = value.toString().match(regx);
    value = value[0];
    // 控制输入的最多位数
    if (maxLength && value.length > maxLength) {
        value = value.slice(0, maxLength);
    }

    Obj[field] = value;
}

输入框支持输入也可以 弹窗选择

类似于 el-autocomplete 功能

image-20230614100001731
image-20230614100001731
<van-field v-model="form.pileNum" maxlength="64" required :rules="rules.pileNum" label="桩号" 
	@update:model-value="pileNumChange" placeholder="请填写桩号">
    <template #right-icon>
    	<van-icon name="arrow" @click="showSelectPopup('pileNumSelectRef', 'pileNum')" class="custom-van-field-icon"/>
    </template>
</van-field>
// 选择桩号
pileNumSelect (item) {
    this.form.pileId = item.id
    this.form.pileNum = item.pileNum
},

// 监听 输入变化,进行 桩基id清除,防止数据污染
pileNumChange () {
   console.log('change ')
    this.form.pileId = ''
},

自定义弹窗多选组件

MultiSelectPopup.vue

<multi-select-popup title="检测人员选择" :defaultOptions="personList" v-model="form.jcUserIds"
                            :show-picker="showPicker"
                            @selectObject="selectJcUser" @update:showPicker="showPicker = $event"/>

调用 @click="showPicker = true"

1686714690982
1686714690982

表单

image-20231226151928899
image-20231226151928899

文本左右对其

  • 设置 表单公共属性 input-align="right"
<van-form input-align="right" ref="form" v-model="form">

</van-form>
  • 设置单个field组件 input-align="right"
<van-field v-model="form.mName" label="主体工作" readonly required is-link input-align="right"/>

vant search 右侧的关闭icon 无法点击

.van-field__body {
  position: relative;

  .van-icon-clear {
    z-index: 999;
    position: absolute;
    right: 0;
  }
}

复选框

需要支持点击文本实现选中

image-20240621104825856
image-20240621104825856
<van-checkbox-group v-model="ids" v-if="multiMode">
    <van-cell-group inset>
        <van-cell v-for="(item, index) in computedOptions" clickable :key="item"
                  :title="item[label]" @click.stop="toggle(item.id)">
            <template #right-icon>
<van-checkbox :name="item[valueKey]"  shape icon-size="15px" @click.stop  :ref="'checkboxRefs'+item.id" />
            </template>
        </van-cell>
    </van-cell-group>
</van-checkbox-group>
toggle(id) {
    this.$refs[`checkboxRefs${id}`][0].toggle();
},