DatePicker.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <template>
  2. <div class="date-picker-container">
  3. <!-- 使用van-field作为输入框,优化移动端点击体验 -->
  4. <van-field
  5. v-model="displayDate"
  6. :label="label"
  7. readonly
  8. :required="required"
  9. clickable
  10. :placeholder="placeholder"
  11. @click="showPicker = true"
  12. :error-message="error"
  13. >
  14. <template #right-icon>
  15. <van-icon name="calendar-o" class="calendar-icon" />
  16. </template>
  17. </van-field>
  18. <!-- 日期选择弹窗 -->
  19. <van-popup
  20. v-model="showPicker"
  21. position="bottom"
  22. round
  23. >
  24. <van-datetime-picker
  25. v-model="currentDate"
  26. type="date"
  27. :min-date="minDate"
  28. :max-date="maxDate"
  29. :formatter="formatter"
  30. @confirm="onConfirm"
  31. @cancel="showPicker = false"
  32. />
  33. </van-popup>
  34. </div>
  35. </template>
  36. <script>
  37. export default {
  38. props: {
  39. value: {
  40. type: [String, Date],
  41. default: ''
  42. },
  43. label: String,
  44. required: {
  45. type: Boolean,
  46. default: false
  47. },
  48. placeholder: {
  49. type: String,
  50. default: '请选择日期'
  51. },
  52. // 新增日期格式配置
  53. dateFormat: {
  54. type: String,
  55. default: 'yyyy-MM-dd'
  56. },
  57. error: {
  58. type: String,
  59. default: ''
  60. }
  61. },
  62. data() {
  63. return {
  64. showPicker: false,
  65. currentDate: this.value ? new Date(this.value) : new Date(),
  66. minDate: new Date(1990, 0, 1),
  67. maxDate: new Date()
  68. }
  69. },
  70. computed: {
  71. // 显示格式化后的日期
  72. displayDate: {
  73. get() {
  74. return this.formatDate(this.currentDate)
  75. },
  76. set() {} // 避免直接修改警告
  77. }
  78. },
  79. watch: {
  80. value: {
  81. handler(newVal) {
  82. if (newVal) {
  83. this.currentDate = this.parseDate(newVal)
  84. }
  85. },
  86. immediate: true
  87. }
  88. },
  89. methods: {
  90. // 日期确认处理
  91. onConfirm(value) {
  92. this.currentDate = value
  93. this.$emit('input', this.formatDate(value))
  94. this.showPicker = false
  95. },
  96. // 日期格式化方法
  97. formatDate(date) {
  98. if (!(date instanceof Date)) return ''
  99. const pad = n => n.toString().padStart(2, '0')
  100. return this.dateFormat
  101. .replace('yyyy', date.getFullYear())
  102. .replace('MM', pad(date.getMonth() + 1))
  103. .replace('dd', pad(date.getDate()))
  104. },
  105. // 字符串转Date对象
  106. parseDate(dateStr) {
  107. if (dateStr instanceof Date) return dateStr
  108. return new Date(dateStr.replace(/-/g, '/'))
  109. },
  110. // 自定义日期显示格式
  111. formatter(type, value) {
  112. if (type === 'year') return `${value}年`
  113. if (type === 'month') return `${value}月`
  114. if (type === 'day') return `${value}日`
  115. return value
  116. }
  117. }
  118. }
  119. </script>
  120. <style scoped>
  121. .date-picker-container {
  122. display: flex;
  123. align-items: center;
  124. gap: 12px;
  125. width: 100%;
  126. padding: 12px 0;
  127. box-sizing: border-box;
  128. }
  129. /* 移动端适配 */
  130. @media (max-width: 768px) {
  131. .date-picker-container {
  132. flex-direction: column;
  133. align-items: flex-start;
  134. }
  135. .custom-label {
  136. width: 100% !important;
  137. margin-bottom: 8px;
  138. }
  139. }
  140. /* Vant组件样式覆盖 */
  141. .calendar-icon {
  142. color: #1989fa;
  143. font-size: 18px;
  144. }
  145. /* 必填星号样式 */
  146. .required-star {
  147. color: #ff4444;
  148. margin-left: 4px;
  149. }
  150. /* 标签样式优化 */
  151. .custom-label {
  152. flex: 0 0 auto;
  153. font-size: 14px;
  154. color: #333;
  155. min-width: 6em;
  156. }
  157. </style>