Просмотр исходного кода

对时间选择器组件进行性能优化。并解决问题 -- 在使用过程中,发现在小时 或 分钟还在滚动时,立马点击确认会出现取件时间还是原来的那个时间。
为了解决这个问题,我进行了以下优化:
1. 开启实时更新 (immediate-change):给 picker-view 添加了 immediate-change="true" 属性,使其在滚动时实时触发 change 事件,确保能捕获到最新的选择。

2. 防抖处理 (debounce):为了防止实时更新导致频繁的计算(如重新生成日期/小时数组)引起性能问题,我在 handleChange 中引入了防抖逻辑(100ms 延迟)。

3. 强制同步确认:在点击“确认”按钮时,检查是否有正在等待执行的变更逻辑。如果有,立即取消等待并强制执行更新逻辑,确保使用的是用户当前滚动到的最新值,而不是旧值。

4. 优化级联逻辑:改进了日期、小时切换时的重置逻辑,从“强制归零”改为“越界修正”,保留用户的选择,提升连续滚动的体验。

shizhongqi 7 месяцев назад
Родитель
Сommit
1dcf9a60cd

+ 49 - 12
ghsApp/src/admin/delivery/components/TimePicker.vue

@@ -8,7 +8,7 @@
         </view>
       </view>
 
-      <picker-view v-if="visible" class="picker-view" :indicator-style="indicatorStyle" :value="pickerValue" @change="handleChange">
+      <picker-view v-if="visible" class="picker-view" :indicator-style="indicatorStyle" :value="pickerValue" @change="handleChange" :immediate-change="true">
         <picker-view-column>
           <view class="picker-item" v-for="(item, index) in days" :key="index">{{ item.label }}</view>
         </picker-view-column>
@@ -38,6 +38,8 @@ export default {
     return {
       visible: false, // 控制picker-view的渲染
       pickerValue: [0, 0, 0],
+      tempPickerValue: [0, 0, 0], // 临时存储滚动过程中的值
+      debounceTimer: null,
       days: [],
       hours: [],
       minutes: [],
@@ -73,6 +75,7 @@ export default {
         this.hours = this.generateHours(tomorrow, false);
         this.minutes = this.generateMinutes(tomorrow, false, this.hours[0].value);
         this.pickerValue = [1, 0, 0]; // Select "Tomorrow", first hour, first minute
+        this.tempPickerValue = [1, 0, 0];
         return;
       }
 
@@ -85,6 +88,7 @@ export default {
       const initialMinuteIndex = 0;
       
       this.pickerValue = [0, initialHourIndex, initialMinuteIndex];
+      this.tempPickerValue = [0, initialHourIndex, initialMinuteIndex];
     },
 
     generateDays() {
@@ -135,7 +139,20 @@ export default {
     },
 
     handleChange(e) {
-      const [dayIndex, hourIndex, minuteIndex] = e.detail.value;
+      const val = e.detail.value;
+      this.tempPickerValue = val;
+      
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+      }
+      
+      this.debounceTimer = setTimeout(() => {
+        this.updatePickers(val);
+      }, 100);
+    },
+
+    updatePickers(val, isSync = false) {
+      const [dayIndex, hourIndex, minuteIndex] = val;
       const oldPickerValue = this.pickerValue;
       
       const now = dayjs();
@@ -150,26 +167,46 @@ export default {
       // Day changed
       if (oldPickerValue[0] !== dayIndex) {
         newHours = this.generateHours(now, isToday);
-        finalHourIndex = 0;
+        // 修正越界,尽量保持用户的选择,而不是强制归零
+        if (finalHourIndex >= newHours.length) {
+            finalHourIndex = 0;
+        }
       }
       
-      // Hour changed (or day changed)
-      if (oldPickerValue[0] !== dayIndex || oldPickerValue[1] !== hourIndex) {
-        const selectedHour = newHours[finalHourIndex].value;
-        newMinutes = this.generateMinutes(now, isToday, selectedHour);
-        finalMinuteIndex = 0;
+      // Calculate selected hour based on current indices
+      const selectedHour = newHours[finalHourIndex] ? newHours[finalHourIndex].value : -1;
+
+      // Always regenerate minutes to ensure correctness (e.g. today/tomorrow switch, or hour switch)
+      // Optimization: we could check if generation parameters actually changed, but it's fast enough.
+      newMinutes = this.generateMinutes(now, isToday, selectedHour);
+      
+      // 修正越界
+      if (finalMinuteIndex >= newMinutes.length) {
+          finalMinuteIndex = 0;
       }
       
       this.hours = newHours;
       this.minutes = newMinutes;
+      this.debounceTimer = null;
 
-      // Use setTimeout to avoid visual glitch on picker reset
-      setTimeout(() => {
-        this.pickerValue = [dayIndex, finalHourIndex, finalMinuteIndex];
-      }, 0);
+      const newValue = [dayIndex, finalHourIndex, finalMinuteIndex];
+      
+      if (isSync) {
+        this.pickerValue = newValue;
+      } else {
+        // Use setTimeout to avoid visual glitch on picker reset
+        setTimeout(() => {
+            this.pickerValue = newValue;
+        }, 0);
+      }
     },
 
     handleConfirm() {
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+        this.updatePickers(this.tempPickerValue, true);
+      }
+      
       const [dayIndex, hourIndex, minuteIndex] = this.pickerValue;
       const selectedDay = this.days[dayIndex];
       const selectedHour = this.hours[hourIndex];

+ 49 - 12
hdApp/src/admin/delivery/components/TimePicker.vue

@@ -8,7 +8,7 @@
         </view>
       </view>
 
-      <picker-view v-if="visible" class="picker-view" :indicator-style="indicatorStyle" :value="pickerValue" @change="handleChange">
+      <picker-view v-if="visible" class="picker-view" :indicator-style="indicatorStyle" :value="pickerValue" @change="handleChange" :immediate-change="true">
         <picker-view-column>
           <view class="picker-item" v-for="(item, index) in days" :key="index">{{ item.label }}</view>
         </picker-view-column>
@@ -38,6 +38,8 @@ export default {
     return {
       visible: false, // 控制picker-view的渲染
       pickerValue: [0, 0, 0],
+      tempPickerValue: [0, 0, 0], // 临时存储滚动过程中的值
+      debounceTimer: null,
       days: [],
       hours: [],
       minutes: [],
@@ -73,6 +75,7 @@ export default {
         this.hours = this.generateHours(tomorrow, false);
         this.minutes = this.generateMinutes(tomorrow, false, this.hours[0].value);
         this.pickerValue = [1, 0, 0]; // Select "Tomorrow", first hour, first minute
+        this.tempPickerValue = [1, 0, 0];
         return;
       }
 
@@ -85,6 +88,7 @@ export default {
       const initialMinuteIndex = 0;
       
       this.pickerValue = [0, initialHourIndex, initialMinuteIndex];
+      this.tempPickerValue = [0, initialHourIndex, initialMinuteIndex];
     },
 
     generateDays() {
@@ -135,7 +139,20 @@ export default {
     },
 
     handleChange(e) {
-      const [dayIndex, hourIndex, minuteIndex] = e.detail.value;
+      const val = e.detail.value;
+      this.tempPickerValue = val;
+      
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+      }
+      
+      this.debounceTimer = setTimeout(() => {
+        this.updatePickers(val);
+      }, 100);
+    },
+
+    updatePickers(val, isSync = false) {
+      const [dayIndex, hourIndex, minuteIndex] = val;
       const oldPickerValue = this.pickerValue;
       
       const now = dayjs();
@@ -150,26 +167,46 @@ export default {
       // Day changed
       if (oldPickerValue[0] !== dayIndex) {
         newHours = this.generateHours(now, isToday);
-        finalHourIndex = 0;
+        // 修正越界,尽量保持用户的选择,而不是强制归零
+        if (finalHourIndex >= newHours.length) {
+            finalHourIndex = 0;
+        }
       }
       
-      // Hour changed (or day changed)
-      if (oldPickerValue[0] !== dayIndex || oldPickerValue[1] !== hourIndex) {
-        const selectedHour = newHours[finalHourIndex].value;
-        newMinutes = this.generateMinutes(now, isToday, selectedHour);
-        finalMinuteIndex = 0;
+      // Calculate selected hour based on current indices
+      const selectedHour = newHours[finalHourIndex] ? newHours[finalHourIndex].value : -1;
+
+      // Always regenerate minutes to ensure correctness (e.g. today/tomorrow switch, or hour switch)
+      // Optimization: we could check if generation parameters actually changed, but it's fast enough.
+      newMinutes = this.generateMinutes(now, isToday, selectedHour);
+      
+      // 修正越界
+      if (finalMinuteIndex >= newMinutes.length) {
+          finalMinuteIndex = 0;
       }
       
       this.hours = newHours;
       this.minutes = newMinutes;
+      this.debounceTimer = null;
 
-      // Use setTimeout to avoid visual glitch on picker reset
-      setTimeout(() => {
-        this.pickerValue = [dayIndex, finalHourIndex, finalMinuteIndex];
-      }, 0);
+      const newValue = [dayIndex, finalHourIndex, finalMinuteIndex];
+      
+      if (isSync) {
+        this.pickerValue = newValue;
+      } else {
+        // Use setTimeout to avoid visual glitch on picker reset
+        setTimeout(() => {
+            this.pickerValue = newValue;
+        }, 0);
+      }
     },
 
     handleConfirm() {
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+        this.updatePickers(this.tempPickerValue, true);
+      }
+      
       const [dayIndex, hourIndex, minuteIndex] = this.pickerValue;
       const selectedDay = this.days[dayIndex];
       const selectedHour = this.hours[hourIndex];