【UniApp 日期選擇器實現與樣式優化實踐】

UniApp 日期選擇器實現與樣式優化實踐

發布時間:2025/6/26

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

前言

在移動端應用開發中,日期選擇器是一個常見且重要的交互組件。本文將分享我們在 UniApp 項目中實現自定義日期選擇器的經驗,特別是在樣式優化過程中遇到的問題及解決方案。通過這個案例,希望能為大家在 UniApp 組件開發中提供一些參考。

需求分析

在我們的業務場景中,需要一個支持年、月、日三種維度的日期選擇器,具有以下特點:

  1. 多維度選擇:支持年、月、日三種維度的切換
  2. 自定義樣式:符合設計規范的 UI 樣式
  3. 良好交互:滑動流暢,選中項明顯
  4. 默認值設置:支持設置默認日期和默認維度

基于以上需求,我們決定基于 UniApp 的 picker-view 組件進行二次開發,實現一個自定義的日期選擇器組件。

基礎實現

組件結構

<template><view class="date-picker-drawer"><!-- 遮罩層 --><view v-if="visible" class="drawer-mask" @click="handleClose"></view><!-- 抽屜內容 --><view class="drawer-content" :class="{ show: visible }"><!-- 頭部 --><view class="drawer-header"><view class="placeholder-btn"></view><view class="header-title">時間維度</view><view class="close-btn" @click="handleClose">×</view></view><!-- 標簽頁 --><view class="tab-container"><viewv-for="(tab, index) in tabs":key="tab.value"class="tab-item":class="{ active: currentTab === tab.value }"@click="switchTab(tab.value)">{{ tab.label }}</view></view><!-- 當前選中日期顯示 --><view class="current-date"><text class="date-text">{{ formatCurrentDate }}</text></view><!-- 日期選擇器 --><view class="picker-container"><picker-viewclass="picker-view":value="pickerValue"@change="handlePickerChange"mask-class="picker-mask"><!-- 年份列 --><picker-view-column><view v-for="year in yearList" :key="year" class="picker-item">{{ year }}年</view></picker-view-column><!-- 月份列 --><picker-view-column v-if="currentTab !== 'year'"><view v-for="month in monthList" :key="month" class="picker-item">{{ month }}月</view></picker-view-column><!-- 日期列 --><picker-view-column v-if="currentTab === 'day'"><view v-for="day in dayList" :key="day" class="picker-item">{{ day }}日</view></picker-view-column></picker-view></view><!-- 確定按鈕 --><view class="confirm-btn" @click="handleConfirm">確定</view></view></view>
</template>

核心邏輯

  1. 數據初始化
// Props 和 Emits
const props = withDefaults(defineProps<Props>(), {defaultDate: () => new Date(),defaultTab: 'year',minYear: () => new Date().getFullYear() - 3,maxYear: () => new Date().getFullYear() + 3
});// 響應式數據
const currentTab = ref<'day' | 'month' | 'year'>(props.defaultTab);
const selectedDate = ref(new Date(props.defaultDate));
const pickerValue = ref([0, 0, 0]);
  1. 動態計算年月日列表
// 年份列表
const yearList = computed(() => {const years = [];const minYear = Math.min(props.minYear, props.maxYear);const maxYear = Math.max(props.minYear, props.maxYear);for (let i = minYear; i <= maxYear; i++) {years.push(i);}return years;
});// 月份列表
const monthList = computed(() => {const months = [];for (let i = 1; i <= 12; i++) {months.push(i);}return months;
});// 日期列表
const dayList = computed(() => {const yearIndex = Math.min(Math.max(0, pickerValue.value[0]), yearList.value.length - 1);const monthIndex = Math.min(Math.max(0, pickerValue.value[1]), monthList.value.length - 1);const year = yearList.value[yearIndex] || new Date().getFullYear();const month = monthList.value[monthIndex] || 1;// 計算該月的天數const daysInMonth = new Date(year, month, 0).getDate();const days = [];for (let i = 1; i <= daysInMonth; i++) {days.push(i);}return days;
});
  1. 選擇器值初始化
const initPickerValue = () => {const year = selectedDate.value.getFullYear();const month = selectedDate.value.getMonth() + 1;const day = selectedDate.value.getDate();// 確保年份在可選范圍內const safeYear = Math.max(props.minYear, Math.min(props.maxYear, year));// 查找年份在列表中的索引const yearIndex = yearList.value.findIndex((y) => y === safeYear);// 月份和日期索引const monthIndex = month - 1;const dayIndex = day - 1;// 確保索引有效const validYearIndex = yearIndex >= 0 ? yearIndex : 0;const validMonthIndex = monthIndex >= 0 && monthIndex < 12 ? monthIndex : 0;const validDayIndex = dayIndex >= 0 && dayIndex < dayList.value.length ? dayIndex : 0;pickerValue.value = [validYearIndex, validMonthIndex, validDayIndex];
};
  1. 處理選擇器變化
const handlePickerChange = (e: any) => {const values = e.detail.value;// 設置標志位,表示用戶正在操作isUserChanging.value = true;// 確保索引有效const validValues = [Math.min(Math.max(0, values[0]), yearList.value.length - 1),Math.min(Math.max(0, values[1] || 0), monthList.value.length - 1),Math.min(Math.max(0, values[2] || 0), dayList.value.length - 1)];pickerValue.value = validValues;// 獲取實際選中的值const yearIndex = validValues[0];const year = yearList.value[yearIndex];let month = 1;let day = 1;if (currentTab.value !== 'year' && validValues[1] !== undefined) {const monthIndex = validValues[1];month = monthList.value[monthIndex];}if (currentTab.value === 'day' && validValues[2] !== undefined) {const dayIndex = validValues[2];day = dayList.value[dayIndex] || 1;}// 更新selectedDateselectedDate.value = new Date(year, month - 1, day);// 延遲重置標志位,避免觸發watchsetTimeout(() => {isUserChanging.value = false;}, 50);
};

樣式優化過程

在實現基本功能后,我們遇到了一系列樣式和交互問題,主要圍繞 picker-view 組件的自定義樣式。

問題一:選中項與指示器不對齊

問題描述

在初始實現中,我們發現選中項與指示器(高亮區域)不對齊,導致視覺上的混亂。用戶不清楚實際選中的是哪一項。

原因分析

  1. picker-item 的高度與 uni-picker-view-indicator 的高度不一致
  2. 文本在 picker-item 中的垂直對齊問題

解決方案

/* 選中項樣式 */
.uni-picker-view-indicator {height: 52px;box-sizing: border-box;border-top: 1px solid rgba(0, 0, 0, 0.1);border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}.picker-item {height: 52px;line-height: 52px;display: flex;align-items: center;justify-content: center;font-size: 32px;color: rgba(0, 0, 0, 0.6);font-family: 'PingFang SC', sans-serif;font-weight: 400;padding: 0;margin: 0;
}/* 選中項文字樣式 */
.uni-picker-view-indicator .picker-item {color: rgba(0, 0, 0, 0.9);font-weight: 500;
}

關鍵點是確保 picker-item 的高度與 uni-picker-view-indicator 的高度一致,并使用 line-height、align-items 和 justify-content 確保文本垂直居中。

問題二:最后一項選不到

問題描述

在某些情況下,列表的最后一項無法滾動到選中位置,導致用戶無法選擇某些值。

原因分析

  1. picker-view 的內部實現中,滾動計算與項目高度和容器高度相關
  2. 當 picker-item 高度與 uni-picker-view-indicator 不一致時,會導致滾動計算錯誤

解決方案

  1. 增加 picker-container 的高度,確保有足夠的滾動空間:
.picker-container {height: 280px;margin-bottom: 30px;
}
  1. 確保 picker-item 與 uni-picker-view-indicator 高度一致:
.uni-picker-view-indicator {height: 52px;/* 其他樣式 */
}.picker-item {height: 52px;line-height: 52px;/* 其他樣式 */
}

問題三:自定義樣式被覆蓋

問題描述

在開發過程中,我們發現一些自定義樣式被 UniApp 內部樣式覆蓋,特別是 indicator 的樣式。

原因分析

  1. UniApp 的 picker-view 組件有內置樣式,可能會覆蓋自定義樣式
  2. 某些樣式屬性被硬編碼在組件內部,難以通過外部 CSS 覆蓋

解決方案

  1. 使用 mask-class 屬性自定義遮罩層樣式:
<picker-viewclass="picker-view":value="pickerValue"@change="handlePickerChange"mask-class="picker-mask"
><!-- 內容 -->
</picker-view>
.picker-mask {background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.6)),linear-gradient(to top, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.6));background-position: top, bottom;background-size: 100% 88px;background-repeat: no-repeat;
}
  1. 避免使用 indicatorStyle 屬性,而是通過 CSS 類選擇器控制樣式:
.uni-picker-view-indicator {height: 52px;box-sizing: border-box;border-top: 1px solid rgba(0, 0, 0, 0.1);border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}.uni-picker-view-indicator::before,
.uni-picker-view-indicator::after {height: 0px;
}

關鍵技術點與經驗總結

1. 避免使用內聯樣式

在早期實現中,我們嘗試使用 picker-view 的 indicatorStyle 屬性設置樣式:

<picker-view :indicator-style="indicatorStyle"><!-- 內容 -->
</picker-view>
const indicatorStyle = 'height: 48px; background-color: rgba(0, 0, 0, 0.05);';

這種方式導致了多種問題:

  • 樣式難以維護和擴展
  • 與其他 CSS 規則可能沖突
  • 無法使用更復雜的 CSS 選擇器

改進后,我們完全通過 CSS 類控制樣式,提高了代碼可維護性。

2. 同步高度設置的重要性

在日期選擇器中,確保以下元素高度一致至關重要:

  • uni-picker-view-indicator(選中指示器)
  • picker-item(選項項)

這不僅影響視覺效果,還會影響滾動計算和選中邏輯。我們通過反復測試確定了 52px 是最佳高度。

3. 處理循環依賴問題

在開發過程中,我們遇到了一個棘手的問題:當選擇器值變化時,會觸發 selectedDate 的更新,而 selectedDate 的更新又會觸發 pickerValue 的重新計算,形成循環依賴。

解決方案是添加一個標志位,區分用戶操作和程序自動更新:

// 添加標志位
const isUserChanging = ref(false);// 處理選擇器變化
const handlePickerChange = (e: any) => {// 設置標志位,表示用戶正在操作isUserChanging.value = true;// 處理邏輯...// 延遲重置標志位setTimeout(() => {isUserChanging.value = false;}, 50);
};// 監聽selectedDate變化
watch(selectedDate, (newDate) => {// 如果是用戶操作導致的變化,不需要重新初始化if (!isUserChanging.value) {// 重新初始化pickerValueinitPickerValue();}
});

4. 容器高度與可滾動性

picker-view 的可滾動范圍與容器高度相關。如果容器高度不足,可能導致某些項無法滾動到選中位置。我們通過增加 picker-container 的高度解決了這個問題:

.picker-container {height: 280px;margin-bottom: 30px;
}

最終效果與性能優化

經過多次調整和優化,我們的日期選擇器組件實現了以下效果:

  1. 視覺一致性:選中項與指示器完美對齊
  2. 交互流暢:滾動平滑,所有項都可以選中
  3. 樣式美觀:符合設計規范,選中項樣式明顯
  4. 性能良好:避免了不必要的重新渲染

性能優化方面,我們采取了以下措施:

  1. 使用 computed 屬性計算年月日列表,避免重復計算
  2. 添加 isUserChanging 標志位,減少不必要的更新
  3. 使用 setTimeout 延遲執行某些操作,確保 DOM 更新完成
  4. 優化 CSS 選擇器,減少樣式計算復雜度

兼容性考慮

在不同平臺上,UniApp 的 picker-view 組件可能有不同的表現。我們針對主要平臺進行了測試和優化:

  1. iOS

    • 滾動慣性較強,需要調整選項間距
    • 文本渲染更精細,字體大小需要微調
  2. Android

    • 滾動阻尼不同,可能需要調整滾動參數
    • 不同廠商的 Android 系統可能有不同表現
  3. 小程序

    • 微信小程序中 picker-view 的實現與原生略有不同
    • 需要額外測試確保樣式一致

總結與展望

通過這次日期選擇器組件的開發,我們積累了豐富的 UniApp 自定義組件開發經驗,特別是在處理原生組件樣式自定義方面。核心經驗包括:

  1. 避免使用內聯樣式,優先使用 CSS 類控制樣式
  2. 確保相關元素的高度一致,特別是在滾動選擇器中
  3. 處理好數據流向,避免循環依賴
  4. 考慮不同平臺的兼容性問題

未來,我們計劃進一步優化這個組件:

  1. 支持更多的日期格式和范圍限制
  2. 添加農歷日期支持
  3. 優化動畫效果和過渡
  4. 提高跨平臺兼容性

希望本文對大家在 UniApp 開發中實現自定義日期選擇器有所幫助。如有任何問題或建議,歡迎在評論區留言討論。


發布時間:2025/6/26

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/88700.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/88700.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/88700.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

推薦系統的視頻特征-視頻關鍵幀特征提取與向量生成

&#x1f4cc; 總體流程概覽 視頻文件 (.mp4)↓ 關鍵幀抽取&#xff08;FFmpeg / SceneDetect&#xff09;↓ 幀圖像&#xff08;.jpg&#xff09;↓ 圖像模型提取特征&#xff08;CLIP / CNN / ViT&#xff09;↓ 多幀聚合成視頻向量&#xff08;均值池化等&#xff09;↓ 向…

Apache SeaTunnel Flink引擎執行流程源碼分析

目錄 1. 任務啟動入口 2. 任務執行命令類:FlinkTaskExecuteCommand 3. FlinkExecution的創建與初始化 3.1 核心組件初始化 3.2 關鍵對象說明 4. 任務執行:FlinkExecution.execute() 5. Source處理流程 5.1 插件初始化 5.2 數據流生成 6. Transform處理流程 6.1 插…

Vue 3 + Element Plus 實現「動態表單組件」詳解教程

? Vue 3 Element Plus 實現「動態表單組件」詳解教程 &#x1f4cc; 適用場景&#xff1a;表單字段根據配置動態生成&#xff0c;支持校驗、提交、自定義組件、復雜布局等。 &#x1f9e9; 技術棧&#xff1a;Vue 3 TypeScript Element Plus &#x1f527; 核心特性&#x…

本地部署開源時間跟蹤工具 Kimai 并實現外部訪問( Windows 版本)

Kimai 是一款開源的時間跟蹤工具&#xff0c;它易于使用&#xff0c;并提供了強大的報告功能&#xff0c;在個人和團隊記錄工作時間、項目時間和活動時間等之后可以幫助用戶了解他們是如何花費時間的&#xff0c;從而提高生產力和效率。本文將詳細介紹如何在 Windows 系統本地部…

系統分析師案例知識點

目錄 1 必做題1.1 狀態機圖1.2 活動圖1.3 統一軟件開發過程RUP 2 需求分析2.1 數據流圖DFD2.2 ER圖2.3 狀態轉換圖STD2.4 數據字典2.5 流程圖2.6 需求評審2.7 設計類2.8 FAST分析2.9 常見的關系類 3 嵌入式3.1 容器技術3.2 虛擬機技術3.3 虛擬機和容器的不同點 4 數據庫4.1 NoS…

多相機人臉掃描設備如何助力高效打造數字教育孿生體?

在教育數字化轉型浪潮中&#xff0c;數字孿生體作為現實教育場景的虛擬映射&#xff0c;正成為智慧教育發展的關鍵技術支點。傳統教育模式面臨師資資源分布不均、個性化教學難以覆蓋、跨時空教學場景受限等痛點&#xff0c;而數字孿生體通過構建高仿真虛擬教育主體&#xff08;…

用 EXCEL/WPS 實現聚類分析:賦能智能客服場景的最佳實踐

聚類分析作為無監督學習的核心技術&#xff0c;能在客服數據中發現隱藏的用戶群體或問題模式。盡管 Excel/WPS 并非專業統計軟件&#xff0c;但巧妙利用其內置功能&#xff0c;也能實現基礎的聚類分析&#xff0c;為中小型客服團隊提供快速洞察。以下介紹具體方法及智能客服場景…

基于定制開發開源AI智能名片S2B2C商城小程序源碼的H5游戲開發模式創新研究

摘要 本文以定制開發開源AI智能名片S2B2C商城小程序源碼為技術底座&#xff0c;探討其在H5游戲開發中的創新應用。通過分析原生開發與第三方工具兩種傳統開發模式的局限性&#xff0c;提出將AI智能名片的多模態內容生成能力、S2B2C商城的生態協同機制與H5游戲開發深度融合的解…

vue3+ELInput無法輸入的問題

vue3ElInput無法輸入的問題 開篇 寫業務的時候發現&#xff0c;因為想偷懶嘛&#xff0c;直接就在想在外部去定義一個變量&#xff0c;然后寫個彈窗里&#xff08;tsx&#xff09;的el-input&#xff0c;而不是又去寫個vue頁面&#xff0c;但發現就輸入不了了&#xff0c;而且…

SQL Server:如何檢測和修復 FILESTREAM 數據庫損壞?

SQL Server 中的 FILESTREAM 功能可以將二進制大型對象 &#xff08;BLOB&#xff09; 存儲到文件系統上&#xff0c;而不是將它們存儲在數據庫中。但是&#xff0c;默認情況下不啟用此功能。用戶需要使用 SQL Server Management Studio &#xff08;SSMS&#xff09; 和 SQL S…

FORCE 開發者論壇 | 火山引擎發布多款 Agent 開發工具

資料來源&#xff1a;火山引擎-開發者社區 6 月 12 日&#xff0c;2025 火山引擎 FORCE 原動力大會開發者論壇成功舉辦。大會聚焦 Agent 開發新范式&#xff0c;升級發布了 PromptPilot、MCP Servers、TRAE、扣子開發平臺等產品&#xff0c;以及多款開源項目&#xff0c;構建起…

【Qt-windows】如何使用perfmon 具體分析windows serverR2的Qt程序CPU問題

可以使用 Windows 自帶的 PerfMon&#xff08;Performance Monitor&#xff09; 工具對運行在 Windows Server R2 上的 Qt 程序進行詳細的性能分析&#xff0c;尤其是 CPU 使用情況。以下是具體的操作步驟和建議&#xff1a; 一、打開 PerfMon 工具 按下 Win R 打開運行窗口。…

【軟考高級系統架構論文】論NoSQL數據庫技術及其應用

論文真題 隨著互聯網web2.0網站的興起,傳統關系數據庫在應對web2.0 網站,特別是超大規模和高并發的web2.0純動態 SNS 網站上已經顯得力不從心,暴露了很多難以克服的問題,而非關系型的數據庫則由于其本身的特點得到了非常迅速的發展。 NoSQL(Not only SQL )的產生就是為了解…

bash的配置文件,source

一.按生效范圍分類 二.按shell登錄的方式分類 這里的執行順序存疑,因為會互相調用,不需要記憶 source執行腳本 source不創建子進程,bash創建子進程 普通腳本:用bash 配置文件腳本:用source 三.按功能分類

30道C語言高頻題整理(附答案背誦版)

1.請描述一下C語言的基本數據類型有哪些&#xff1f; C語言提供了一系列的基本數據類型&#xff0c;它們是構建更復雜數據結構的基礎。這些基本數據類型主要包括&#xff1a; 整型&#xff08;Integer Types&#xff09;&#xff1a;用于存儲整數值。根據存儲大小和符號性&…

使用Tailwind CSS和i18n的react實踐

首先在 src 下設置 i18n.js 文件 // src/i18n.js import i18n from i18next; import { initReactI18next } from react-i18next;import en from ./locales/en/public; import zh from ./locales/zh/public;i18n.use(initReactI18next) .init({resources: {en: { translation:…

生信自學路線|R語言的數據變量類型與對應運算

R 是一種動態類型語言&#xff0c;使用靈活&#xff0c;變量無需預先聲明類型。掌握 R 的數據類型和變量機制&#xff0c;是后續進行數據處理和建模分析的基礎。本章節主要介紹 R 語言中的常量、變量、基本數據類型及常用數據結構&#xff0c;并結合示例進行說明。 文章目錄 一…

UI前端大數據處理優化策略:提升數據處理速度與準確性

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩! 在數字化浪潮下&#xff0c;前端面臨的數據規模正呈指數級增長 ——IDC 預測&#xff0c;2025…

技術調研:時序數據庫(二)

除了 InfluxDB、TDengine 和 TimescaleDB&#xff0c;還有其他多個主流的開源時序數據庫&#xff0c;各自針對不同場景優化。以下是補充的時序數據庫選型清單&#xff0c;涵蓋其核心特性、適用場景及局限性&#xff1a; 1. 監控與運維場景 (1) Prometheus 核心優勢&#xff1…

【C++/C】十進制數轉為十六進制時,如何區分正負? 負數補碼高位是1,那么一個很大的正數,高位也會出現1,會和負數搞混嗎?

文章目錄 1 十進制數轉為十六進制時&#xff0c;如何區分正負&#xff1f;1.1 正數處理1.2 負數處理?1.3 關鍵點? 2 負數補碼高位是1&#xff0c;那么一個很大的正數&#xff0c;高位也會出現1&#xff0c;會和負數搞混嗎&#xff1f;2.1 符號位明確區分?2.2 補碼的數值范圍…