youzan / vant

A lightweight, customizable Vue UI library for mobile web apps.
https://vant.pro/vant/
MIT License
23.2k stars 9.49k forks source link

[Bug Report] van-time-picker 回显错误 #13084

Open Alkaidcc opened 1 month ago

Alkaidcc commented 1 month ago

重现链接

https://github.com/Alkaidcc/vant-time-picker-issue

Vant 版本

4.9.4

描述一下你遇到的问题。

需求:使用 van-picker-group,van-date-picker,van-time-picker 实现时间选择器,实现所选结束时间大于开始时间。 使用filter做筛选的时候,数据与UI展示不一致

重现步骤

  1. pnpm install
  2. pnpm dev

https://github.com/user-attachments/assets/45b1e564-d7b8-4d6d-9cea-f2307f30f3ee

设备/浏览器

macOS/Chrome

pany-ang commented 1 month ago

这涉及到两个方面的问题:

  1. 修改 v-model 绑定的数据后,UI 并没有同步进行变化(这应该是一个 BUG
  2. 点击确认按钮时,事件回调参数返回的是内部的私有变量,你所给的 demo 中应该在确认按钮事件触发时使用该回调参数才对,而不是使用 v-model 所绑定的变量。因为该组件并非是完全受控组件,这两者的值有可能不一致。(这种不一致现象,可能是一个 BUG

再说的直白点就是:

  1. v-model 修改为 ['00', '00'] 时,此时 UI 上有 ['00', '00'] 选项,却没有被选中(v-model 变了,但 UI 没变)(这应该是一个 BUG
  2. v-model 修改为 ['00', '00'] 时,此时 UI 上没有 ['00', '00'] 选项,UI 被迫选中最接近的一个值,但是 v-model 没有同步更新为该值,依旧是 ['00', '00'](UI 变了,但 v-model 没变)(可能是一个 BUG

这两个问题在下方最小复现代码中均可复现。

Alkaidcc commented 1 month ago

意思是v-model绑定的变量和组件内部的值实际上不同步是吗?现在的问题是选择开始时间后(时间为00时00分)选择结束时间,由于filter,time-picker会把值变成 ['00','01'] 。但是如果我选中后面一天,我希望time-picker重置为 ['00','00'] 。所以我在结束日期变化的时候判断并修改结束时间的值,奇怪的是如果设置为 ['00','00'] 会和UI不同步,但是设置为 undefined UI就正常了。

function onEndDateChange({ selectedOptions }: any) {
  const endDate = selectedOptions.map((option: any) => option.value).join('-')
  if (dayjs(endDate).isAfter(dayjs(formModel.value.leaveStartTime))) {
    // not working
    formModel.value.endTime = ['00', '00']
  }
}
pany-ang commented 1 month ago

意思是v-model绑定的变量和组件内部的值实际上不同步是吗?现在的问题是选择开始时间后(时间为00时00分)选择结束时间,由于filter,time-picker会把值变成 ['00','01'] 。但是如果我选中后面一天,我希望time-picker重置为 ['00','00'] 。所以我在结束日期变化的时候判断并修改结束时间的值,奇怪的是如果设置为 ['00','00'] 会和UI不同步,但是设置为 undefined UI就正常了。

function onEndDateChange({ selectedOptions }: any) {
  const endDate = selectedOptions.map((option: any) => option.value).join('-')
  if (dayjs(endDate).isAfter(dayjs(formModel.value.leaveStartTime))) {
    // not working
    formModel.value.endTime = ['00', '00']
  }
}

是的,这对应的就是我说的第 1 点,应该是一个 BUG。

pany-ang commented 1 month ago

我试着看了一下相关组件,其中 Picker 组件内部有一个 watch:

watch(
      () => props.modelValue,
      (newValues) => {
        if (
          !isSameValue(newValues, selectedValues.value) &&
          !isSameValue(newValues, lastEmittedModelValue)
        ) {
          selectedValues.value = newValues.slice(0);
          lastEmittedModelValue = newValues.slice(0);
        }
      },
      { deep: true },
    );

如果将其中的 !isSameValue(newValues, lastEmittedModelValue) 移除就能解决第 1 点所说的 BUG,但这是在我不熟悉该组件的情况下找到的修复办法,所以移除这行代码可能会引起其他问题。

牵扯的组件和逻辑有点复杂...要是有空我可能会试着再看看,或者等待其他人来修复这个问题

pany-ang commented 1 month ago

你给的 demo 有些冗余,我这里给一个最小复现:

<template>
  <div>
    <van-date-picker v-model="formModel.date" @change="onChange" />
    <van-time-picker
      v-model="formModel.time"
      :filter="filter"
      @confirm="onConfirm"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import VanTimePicker from '../../time-picker';
import VanDatePicker from '../../date-picker';

const formModel = ref({});

function onConfirm(data) {
  console.log('v-model', formModel.value.time);
  console.log('onConfirm', data.selectedValues);
}

function filter(type, options) {
  if (
    type === 'minute' &&
    formModel.value.date?.[1] === '01' &&
    formModel.value.date?.[2] === '01'
  ) {
    return options.filter((option) => Number(option.value) > 0);
  }
  return options;
}

function onChange() {
  formModel.value.time = ['00', '00'];
}
</script>
pany-ang commented 1 month ago

@chenjiahan 可以留意一下这个 issue