Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

671 rader
24 KiB

  1. <template>
  2. <view class="u-datetime-picker">
  3. <view v-if="hasInput" class="u-datetime-picker__has-input"
  4. @click="onShowByClickInput"
  5. >
  6. <slot name="trigger" :value="inputValue">
  7. <up-input
  8. :readonly="!!showByClickInput"
  9. v-model="inputValue"
  10. v-bind="inputPropsInner"
  11. ></up-input>
  12. <cover-view class="input-cover">
  13. </cover-view>
  14. </slot>
  15. </view>
  16. <u-picker
  17. ref="picker"
  18. :show="pageInline || show || (hasInput && showByClickInput)"
  19. :popupMode="popupMode"
  20. :closeOnClickOverlay="closeOnClickOverlay"
  21. :columns="columns"
  22. :title="title"
  23. :itemHeight="itemHeight"
  24. :showToolbar="showToolbar"
  25. :visibleItemCount="visibleItemCount"
  26. :defaultIndex="innerDefaultIndex"
  27. :cancelText="cancelText"
  28. :confirmText="confirmText"
  29. :cancelColor="cancelColor"
  30. :confirmColor="confirmColor"
  31. :toolbarRightSlot="toolbarRightSlot"
  32. :pageInline="pageInline"
  33. :maskClass="maskClass"
  34. :maskStyle="resolvedMaskStyle"
  35. @close="close"
  36. @cancel="cancel"
  37. @confirm="confirm"
  38. @change="change"
  39. >
  40. <template #toolbar-right>
  41. <slot name="toolbar-right">
  42. </slot>
  43. </template>
  44. <template #toolbar-bottom>
  45. <slot name="toolbar-bottom">
  46. </slot>
  47. </template>
  48. </u-picker>
  49. </view>
  50. </template>
  51. <script>
  52. function times(n, iteratee) {
  53. let index = -1
  54. const result = Array(n < 0 ? 0 : n)
  55. while (++index < n) {
  56. result[index] = iteratee(index)
  57. }
  58. return result
  59. }
  60. import { props } from './props';
  61. import { mpMixin } from '../../libs/mixin/mpMixin';
  62. import { mixin } from '../../libs/mixin/mixin';
  63. import dayjs from './dayjs.esm.min.js';
  64. import { range, error, padZero } from '../../libs/function/index';
  65. import test from '../../libs/function/test';
  66. /**
  67. * DatetimePicker 时间日期选择器
  68. * @description 此选择器用于时间日期
  69. * @tutorial https://uview-plus.jiangruyi.com/components/datetimePicker.html
  70. * @property {Boolean} show 用于控制选择器的弹出与收起 ( 默认 false )
  71. * @property {Boolean} showToolbar 是否显示顶部的操作栏 ( 默认 true )
  72. * @property {String | Number} modelValue 绑定值
  73. * @property {String} title 顶部标题
  74. * @property {String} mode 展示格式 mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择,mode=datehour为日期小时选择,mode=timesecond为时分秒选择,mode=datetimesecond为日期时分秒选择 ( 默认 ‘datetime )
  75. * @property {Number} maxDate 可选的最大时间 默认值为后10年
  76. * @property {Number} minDate 可选的最小时间 默认值为前10年
  77. * @property {Number} minHour 可选的最小小时,仅mode=time/timesecond有效 ( 默认 0 )
  78. * @property {Number} maxHour 可选的最大小时,仅mode=time/timesecond有效 ( 默认 23 )
  79. * @property {Number} minMinute 可选的最小分钟,仅mode=time/timesecond有效 ( 默认 0 )
  80. * @property {Number} maxMinute 可选的最大分钟,仅mode=time/timesecond有效 ( 默认 59 )
  81. * @property {Number} minSecond 可选的最小秒,仅mode=timesecond有效 ( 默认 0 )
  82. * @property {Number} maxSecond 可选的最大秒,仅mode=timesecond有效 ( 默认 59 )
  83. * @property {Function} filter 选项过滤函数
  84. * @property {Function} formatter 选项格式化函数
  85. * @property {Boolean} loading 是否显示加载中状态 ( 默认 false )
  86. * @property {String | Number} itemHeight 各列中,单个选项的高度 ( 默认 44 )
  87. * @property {String} cancelText 取消按钮的文字 ( 默认 '取消' )
  88. * @property {String} confirmText 确认按钮的文字 ( 默认 '确认' )
  89. * @property {String} cancelColor 取消按钮的颜色 ( 默认 '#909193' )
  90. * @property {String} confirmColor 确认按钮的颜色 ( 默认 '#3c9cff' )
  91. * @property {String | Number} visibleItemCount 每列中可见选项的数量 ( 默认 5 )
  92. * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器 ( 默认 false )
  93. * @property {Array} defaultIndex 各列的默认索引
  94. * @event {Function} close 关闭选择器时触发
  95. * @event {Function} confirm 点击确定按钮,返回当前选择的值
  96. * @event {Function} change 当选择值变化时触发
  97. * @event {Function} cancel 点击取消按钮
  98. * @example <u-datetime-picker :show="show" :value="value1" mode="datetime" ></u-datetime-picker>
  99. */
  100. export default {
  101. name: 'up-datetime-picker',
  102. mixins: [mpMixin, mixin, props],
  103. data() {
  104. return {
  105. // 原来的日期选择器不方便,这里增加一个hasInput选项支持类似element的自带输入框的功能。
  106. inputValue: '', // 表单显示值
  107. showByClickInput: false, // 是否在hasInput模式下显示日期选择弹唱
  108. columns: [],
  109. innerDefaultIndex: [],
  110. innerFormatter: (type, value) => value
  111. }
  112. },
  113. watch: {
  114. show(newValue, oldValue) {
  115. if (!newValue && this.hasInput) {
  116. this.showByClickInput = false
  117. }
  118. if (newValue) {
  119. // #ifdef VUE3
  120. this.innerValue = this.correctValue(this.modelValue)
  121. // #endif
  122. // #ifdef VUE2
  123. this.innerValue = this.correctValue(this.value)
  124. // #endif
  125. this.updateColumnValue(this.innerValue)
  126. }
  127. },
  128. // #ifdef VUE3
  129. modelValue(newValue) {
  130. this.init()
  131. // this.getInputValue(newValue)
  132. },
  133. // #endif
  134. // #ifdef VUE2
  135. value(newValue) {
  136. this.init()
  137. // this.getInputValue(newValue)
  138. },
  139. // #endif
  140. propsChange() {
  141. this.init()
  142. }
  143. },
  144. computed: {
  145. // 如果以下这些变量发生了变化,意味着需要重新初始化各列的值
  146. propsChange() {
  147. return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.minSecond, this.maxSecond, this.filter, this.modelValue]
  148. },
  149. resolvedMaskStyle() {
  150. if (this.upHasProp('maskStyle')) {
  151. return this.maskStyle || ''
  152. }
  153. if (this.upThemeIsDark) {
  154. const topBottomStrong = 'rgba(28, 28, 30, 0.95)'
  155. const topBottomWeak = 'rgba(28, 28, 30, 0.6)'
  156. return `background-image: linear-gradient(180deg, ${topBottomStrong}, ${topBottomWeak}), linear-gradient(0deg, ${topBottomStrong}, ${topBottomWeak});`
  157. }
  158. return ''
  159. },
  160. // input的props
  161. inputPropsInner() {
  162. return {
  163. border: this.inputBorder,
  164. placeholder: this.placeholder,
  165. disabled: this.disabled,
  166. disabledColor: this.disabledColor,
  167. ...this.inputProps
  168. }
  169. }
  170. },
  171. mounted() {
  172. this.init()
  173. // pageInline模式下picker-view常驻显示,在原生端(Android/HarmonyOS)需要
  174. // 等待原生picker-view组件完成初始化后再重新设置选中值,否则滚动位置可能不生效
  175. if (this.pageInline) {
  176. setTimeout(() => {
  177. this.updateIndexs(this.innerValue)
  178. }, 200)
  179. }
  180. },
  181. // #ifdef VUE3
  182. emits: ['close', 'cancel', 'confirm', 'change', 'update:modelValue'],
  183. // #endif
  184. methods: {
  185. toInt(value, fallback = 0) {
  186. const num = parseInt(value, 10)
  187. return Number.isFinite(num) ? num : fallback
  188. },
  189. // 按列安全读取 picker 值,避免快速滚动时出现越界/空值
  190. safeColumnValue(values, columnIndex, optionIndex, fallback = '') {
  191. const column = Array.isArray(values[columnIndex]) ? values[columnIndex] : []
  192. if (!column.length) return fallback
  193. let index = Number(optionIndex)
  194. if (!Number.isFinite(index)) index = 0
  195. index = range(0, column.length - 1, index)
  196. const value = column[index]
  197. return value == null ? fallback : value
  198. },
  199. getInputValue(newValue) {
  200. if (newValue == '' || !newValue || newValue == undefined) {
  201. this.inputValue = ''
  202. return
  203. }
  204. if (this.mode === 'time' || this.mode === 'timesecond') {
  205. this.inputValue = newValue
  206. } else {
  207. if (this.format) {
  208. this.inputValue = dayjs(newValue).format(this.format)
  209. } else {
  210. let format = ''
  211. switch (this.mode) {
  212. case 'date':
  213. format = 'YYYY-MM-DD'
  214. break;
  215. case 'year-month':
  216. format = 'YYYY-MM'
  217. break;
  218. case 'datehour':
  219. format = 'YYYY-MM-DD HH'
  220. break;
  221. case 'datetime':
  222. format = 'YYYY-MM-DD HH:mm'
  223. break;
  224. case 'datetimesecond':
  225. format = 'YYYY-MM-DD HH:mm:ss'
  226. break;
  227. case 'time':
  228. format = 'HH:mm'
  229. break;
  230. case 'timesecond':
  231. format = 'HH:mm:ss'
  232. break;
  233. default:
  234. break;
  235. }
  236. this.inputValue = dayjs(newValue).format(format)
  237. }
  238. }
  239. },
  240. init() {
  241. // #ifdef VUE3
  242. this.innerValue = this.correctValue(this.modelValue)
  243. // #endif
  244. // #ifdef VUE2
  245. this.innerValue = this.correctValue(this.value)
  246. // #endif
  247. this.updateColumnValue(this.innerValue)
  248. // 初始化hasInput展示
  249. this.getInputValue(this.innerValue)
  250. },
  251. // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
  252. setFormatter(e) {
  253. this.innerFormatter = e
  254. },
  255. // 关闭选择器
  256. close() {
  257. if (this.hasInput) {
  258. this.showByClickInput = false
  259. }
  260. if (this.closeOnClickOverlay) {
  261. if (this.hasInput) {
  262. this.showByClickInput = false
  263. }
  264. this.$emit('close')
  265. }
  266. },
  267. // 点击工具栏的取消按钮
  268. cancel() {
  269. if (this.hasInput) {
  270. this.showByClickInput = false
  271. }
  272. this.$emit('cancel')
  273. },
  274. // 点击工具栏的确定按钮
  275. confirm() {
  276. // #ifdef VUE3
  277. this.$emit('update:modelValue', this.innerValue)
  278. // #endif
  279. // #ifdef VUE2
  280. this.$emit('input', this.innerValue)
  281. // #endif
  282. if (this.hasInput) {
  283. this.getInputValue(this.innerValue)
  284. this.showByClickInput = false
  285. }
  286. this.$emit('confirm', {
  287. value: this.innerValue,
  288. mode: this.mode
  289. })
  290. },
  291. //用正则截取输出值,当出现多组数字时,抛出错误
  292. intercept(e,type){
  293. if (e === undefined || e === null) {
  294. return type ? '0000' : '00'
  295. }
  296. let judge = String(e).match(/\d+/g)
  297. if (!judge || judge.length === 0) {
  298. return type ? '0000' : '00'
  299. }
  300. //判断是否掺杂数字
  301. if(judge.length>1){
  302. error("请勿在过滤或格式化函数时添加数字")
  303. return 0
  304. }else if(type&&judge[0].length==4){//判断是否是年份
  305. return judge[0]
  306. }else if(judge[0].length>2){
  307. error("请勿在过滤或格式化函数时添加数字")
  308. return 0
  309. }else{
  310. return judge[0]
  311. }
  312. },
  313. // 列发生变化时触发
  314. change(e) {
  315. const { indexs, values } = e
  316. const safeValues = Array.isArray(values) ? values : []
  317. let selectValue = ''
  318. if (this.mode === 'time' || this.mode === 'timesecond') {
  319. // 根据value各列索引,从各列数组中,取出当前时间的选中值
  320. const hourText = this.safeColumnValue(safeValues, 0, indexs[0], padZero(this.minHour))
  321. const minuteText = this.safeColumnValue(safeValues, 1, indexs[1], padZero(this.minMinute))
  322. let hour = this.toInt(this.intercept(hourText), this.minHour)
  323. let minute = this.toInt(this.intercept(minuteText), this.minMinute)
  324. let second = this.minSecond
  325. hour = range(this.minHour, this.maxHour, hour)
  326. minute = range(this.minMinute, this.maxMinute, minute)
  327. selectValue = `${padZero(hour)}:${padZero(minute)}`
  328. if (this.mode === 'timesecond') {
  329. const secondText = this.safeColumnValue(safeValues, 2, indexs[2], padZero(this.minSecond))
  330. second = range(this.minSecond, this.maxSecond, this.toInt(this.intercept(secondText), this.minSecond))
  331. selectValue = `${selectValue}:${padZero(second)}`
  332. }
  333. } else {
  334. const validCurrent = dayjs(this.innerValue).isValid() ? dayjs(this.innerValue) : dayjs(this.minDate)
  335. const currentYear = validCurrent.year()
  336. const currentMonth = validCurrent.month() + 1
  337. const currentDate = validCurrent.date()
  338. const currentHour = validCurrent.hour()
  339. const currentMinute = validCurrent.minute()
  340. const currentSecond = validCurrent.second()
  341. const yearText = this.safeColumnValue(safeValues, 0, indexs[0], `${currentYear}`)
  342. const monthText = this.safeColumnValue(safeValues, 1, indexs[1], padZero(currentMonth))
  343. // 将选择的值转为数值,比如'03'转为数值的3,'2019'转为数值的2019
  344. let year = this.toInt(this.intercept(yearText, 'year'), currentYear)
  345. let month = this.toInt(this.intercept(monthText), currentMonth)
  346. let hour = 0, minute = 0, second = 0
  347. month = range(1, 12, month)
  348. // 此月份的最大天数
  349. const maxDate = dayjs(`${year}-${month}`).daysInMonth()
  350. const dayText = this.safeColumnValue(safeValues, 2, indexs[2], padZero(Math.min(currentDate, maxDate)))
  351. let date = this.toInt(this.intercept(dayText), Math.min(currentDate, maxDate))
  352. // year-month模式下,date不会出现在列中,设置为1,为了符合后边需要减1的需求
  353. if (this.mode === 'year-month') {
  354. date = 1
  355. }
  356. // 不允许超过maxDate值
  357. date = range(1, maxDate, date)
  358. if (this.mode === 'datehour' || this.mode === 'datetime' || this.mode === 'datetimesecond') {
  359. const hourText = this.safeColumnValue(safeValues, 3, indexs[3], padZero(currentHour))
  360. hour = range(0, 23, this.toInt(this.intercept(hourText), currentHour))
  361. }
  362. if (this.mode === 'datetime' || this.mode === 'datetimesecond') {
  363. const minuteText = this.safeColumnValue(safeValues, 4, indexs[4], padZero(currentMinute))
  364. minute = range(0, 59, this.toInt(this.intercept(minuteText), currentMinute))
  365. }
  366. if (this.mode === 'datetimesecond') {
  367. const secondText = this.safeColumnValue(safeValues, 5, indexs[5], padZero(currentSecond))
  368. second = range(0, 59, this.toInt(this.intercept(secondText), currentSecond))
  369. }
  370. // 转为时间模式
  371. selectValue = Number(new Date(year, month - 1, date, hour, minute, second))
  372. if (!Number.isFinite(selectValue)) {
  373. selectValue = this.correctValue(this.innerValue)
  374. }
  375. }
  376. // 取出准确的合法值,防止超越边界的情况
  377. selectValue = this.correctValue(selectValue)
  378. this.innerValue = selectValue
  379. this.updateColumnValue(selectValue)
  380. // 发出change时间,value为当前选中的时间戳
  381. this.$emit('change', {
  382. value: selectValue,
  383. // #ifndef MP-WEIXIN
  384. // 微信小程序不能传递this实例,会因为循环引用而报错
  385. // picker: this.$refs.picker,
  386. // #endif
  387. mode: this.mode
  388. })
  389. },
  390. // 更新各列的值,进行补0、格式化等操作
  391. updateColumnValue(value) {
  392. this.innerValue = value
  393. this.updateColumns()
  394. // 延迟执行,等待u-picker组件列数据更新完后再设置选中值索引
  395. // pageInline模式下picker-view常驻显示,需额外等待原生组件滚动到位
  396. this.$nextTick(() => {
  397. setTimeout(() => {
  398. this.updateIndexs(value)
  399. }, this.pageInline ? 100 : 0)
  400. })
  401. },
  402. // 更新索引
  403. updateIndexs(value) {
  404. let values = []
  405. const formatter = this.formatter || this.innerFormatter
  406. if (this.mode === 'time' || this.mode === 'timesecond') {
  407. // 将time模式的时间用:分隔成数组
  408. const timeArr = value.split(':')
  409. // 使用formatter格式化方法进行管道处理
  410. values = [formatter('hour', timeArr[0]), formatter('minute', timeArr[1])]
  411. if (this.mode === 'timesecond') {
  412. values.push(formatter('second', timeArr[2]))
  413. }
  414. } else {
  415. values = [
  416. formatter('year', `${dayjs(value).year()}`),
  417. // 月份补0
  418. formatter('month', padZero(dayjs(value).month() + 1))
  419. ]
  420. if (this.mode === 'date' || this.mode === 'datehour' || this.mode === 'datetime' || this.mode === 'datetimesecond') {
  421. // date模式,需要添加天列
  422. values.push(formatter('day', padZero(dayjs(value).date())))
  423. }
  424. if (this.mode === 'datehour' || this.mode === 'datetime' || this.mode === 'datetimesecond') {
  425. values.push(formatter('hour', padZero(dayjs(value).hour())))
  426. }
  427. if (this.mode === 'datetime' || this.mode === 'datetimesecond') {
  428. // 数组的push方法,可以写入多个参数
  429. values.push(formatter('minute', padZero(dayjs(value).minute())))
  430. }
  431. if (this.mode === 'datetimesecond') {
  432. values.push(formatter('second', padZero(dayjs(value).second())))
  433. }
  434. }
  435. // 根据当前各列的所有值,从各列默认值中找到默认值在各列中的索引
  436. const indexs = this.columns.map((column, index) => {
  437. // 通过取大值,可以保证不会出现找不到索引的-1情况
  438. return Math.max(0, column.findIndex(item => item === values[index]))
  439. })
  440. this.innerDefaultIndex = indexs
  441. },
  442. // 更新各列的值
  443. updateColumns() {
  444. const formatter = this.formatter || this.innerFormatter
  445. // 获取各列的值,并且map后,对各列的具体值进行补0操作
  446. const results = this.getOriginColumns().map((column) => column.values.map((value) => formatter(column.type, value)))
  447. this.columns = results
  448. },
  449. getOriginColumns() {
  450. // 生成各列的值
  451. const results = this.getRanges().map(({ type, range }) => {
  452. let values = times(range[1] - range[0] + 1, (index) => {
  453. let value = range[0] + index
  454. value = type === 'year' ? `${value}` : padZero(value)
  455. return value
  456. })
  457. // 进行过滤
  458. if (this.filter) {
  459. values = this.filter(type, values)
  460. if (!values || (values && values.length == 0)) {
  461. // uni.showToast({
  462. // title: '日期filter结果不能为空',
  463. // icon: 'error',
  464. // mask: true
  465. // })
  466. console.log('日期filter结果不能为空')
  467. }
  468. }
  469. return { type, values }
  470. })
  471. return results
  472. },
  473. // 通过最大值和最小值生成数组
  474. generateArray(start, end) {
  475. return Array.from(new Array(end + 1).keys()).slice(start)
  476. },
  477. // 得出合法的时间
  478. correctValue(value) {
  479. const isDateMode = !['time', 'timesecond'].includes(this.mode)
  480. // if (isDateMode && !test.date(value)) {
  481. if (isDateMode && !dayjs.unix(value).isValid()) {
  482. // 如果是日期类型,但是又没有设置合法的当前时间的话,使用最小时间为当前时间
  483. value = this.minDate
  484. } else if (!isDateMode && !value) {
  485. // 如果是时间类型,而又没有默认值的话,就用最小时间
  486. value = this.mode === 'timesecond'
  487. ? `${padZero(this.minHour)}:${padZero(this.minMinute)}:${padZero(this.minSecond)}`
  488. : `${padZero(this.minHour)}:${padZero(this.minMinute)}`
  489. }
  490. // 时间类型
  491. if (!isDateMode) {
  492. if (String(value).indexOf(':') === -1) return error('时间错误,请传递如12:24的格式')
  493. const timeArr = String(value).split(':')
  494. let hour = timeArr[0]
  495. let minute = timeArr[1]
  496. let second = timeArr[2]
  497. // 对时间补零,同时控制在最小值和最大值之间
  498. const hourNum = Number(hour)
  499. const minuteNum = Number(minute)
  500. hour = padZero(range(this.minHour, this.maxHour, Number.isNaN(hourNum) ? this.minHour : hourNum))
  501. minute = padZero(range(this.minMinute, this.maxMinute, Number.isNaN(minuteNum) ? this.minMinute : minuteNum))
  502. if (this.mode === 'timesecond') {
  503. const secondNum = Number(second)
  504. second = padZero(range(this.minSecond, this.maxSecond, Number.isNaN(secondNum) ? this.minSecond : secondNum))
  505. return `${hour}:${minute}:${second}`
  506. }
  507. return `${hour}:${minute}`
  508. } else {
  509. // 如果是日期格式,控制在最小日期和最大日期之间
  510. value = dayjs(value).isBefore(dayjs(this.minDate)) ? this.minDate : value
  511. value = dayjs(value).isAfter(dayjs(this.maxDate)) ? this.maxDate : value
  512. return value
  513. }
  514. },
  515. // 获取每列的最大和最小值
  516. getRanges() {
  517. if (this.mode === 'time' || this.mode === 'timesecond') {
  518. const timeColumns = [
  519. {
  520. type: 'hour',
  521. range: [this.minHour, this.maxHour],
  522. },
  523. {
  524. type: 'minute',
  525. range: [this.minMinute, this.maxMinute],
  526. }
  527. ]
  528. if (this.mode === 'timesecond') {
  529. timeColumns.push({
  530. type: 'second',
  531. range: [this.minSecond, this.maxSecond],
  532. })
  533. }
  534. return timeColumns
  535. }
  536. const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSecond } = this.getBoundary('max', this.innerValue);
  537. const { minYear, minDate, minMonth, minHour, minMinute, minSecond } = this.getBoundary('min', this.innerValue);
  538. const result = [
  539. {
  540. type: 'year',
  541. range: [minYear, maxYear],
  542. },
  543. {
  544. type: 'month',
  545. range: [minMonth, maxMonth],
  546. },
  547. {
  548. type: 'day',
  549. range: [minDate, maxDate],
  550. },
  551. {
  552. type: 'hour',
  553. range: [minHour, maxHour],
  554. },
  555. {
  556. type: 'minute',
  557. range: [minMinute, maxMinute],
  558. },
  559. {
  560. type: 'second',
  561. range: [minSecond, maxSecond],
  562. },
  563. ];
  564. // 兜底:防止边界计算异常导致日列出现空范围(快速滚动场景)
  565. if (result[2] && result[2].type === 'day') {
  566. const start = Number(result[2].range[0])
  567. const end = Number(result[2].range[1])
  568. if (!Number.isFinite(start) || !Number.isFinite(end) || start > end) {
  569. const fallbackDays = dayjs(this.innerValue).isValid() ? dayjs(this.innerValue).daysInMonth() : 31
  570. result[2].range = [1, fallbackDays]
  571. }
  572. }
  573. if (this.mode === 'date')
  574. result.splice(3, 3);
  575. if (this.mode === 'datehour')
  576. result.splice(4, 2);
  577. if (this.mode === 'datetime')
  578. result.splice(5, 1);
  579. if (this.mode === 'year-month')
  580. result.splice(2, 4);
  581. return result;
  582. },
  583. // 根据minDate、maxDate、minHour、maxHour等边界值,判断各列的开始和结束边界值
  584. getBoundary(type, innerValue) {
  585. let value = new Date(innerValue)
  586. if(isNaN(value.getTime())){
  587. value = new Date()
  588. }
  589. const boundary = new Date(this[`${type}Date`])
  590. const year = dayjs(boundary).year()
  591. let month = 1
  592. let date = 1
  593. let hour = 0
  594. let minute = 0
  595. let second = 0
  596. if (type === 'max') {
  597. month = 12
  598. // 月份的天数
  599. date = dayjs(value).daysInMonth()
  600. hour = 23
  601. minute = 59
  602. second = 59
  603. }
  604. // 获取边界值,逻辑是:当年达到了边界值(最大或最小年),就检查月允许的最大和最小值,以此类推
  605. if (dayjs(value).year() === year) {
  606. month = dayjs(boundary).month() + 1
  607. if (dayjs(value).month() + 1 === month) {
  608. date = dayjs(boundary).date()
  609. if (dayjs(value).date() === date) {
  610. hour = dayjs(boundary).hour()
  611. if (dayjs(value).hour() === hour) {
  612. minute = dayjs(boundary).minute()
  613. if (dayjs(value).minute() === minute) {
  614. second = dayjs(boundary).second()
  615. }
  616. }
  617. }
  618. }
  619. }
  620. return {
  621. [`${type}Year`]: year,
  622. [`${type}Month`]: month,
  623. [`${type}Date`]: date,
  624. [`${type}Hour`]: hour,
  625. [`${type}Minute`]: minute,
  626. [`${type}Second`]: second
  627. }
  628. },
  629. onShowByClickInput(){
  630. if(!this.disabled){
  631. this.showByClickInput = !this.showByClickInput
  632. }
  633. }
  634. }
  635. }
  636. </script>
  637. <style lang="scss" scoped>
  638. .u-datetime-picker {
  639. flex: 1;
  640. &__has-input {
  641. position: relative;
  642. display: flex;
  643. flex-direction: column;
  644. justify-content: center;
  645. /* #ifndef APP-NVUE */
  646. width: 100%;
  647. /* #endif */
  648. .input-cover {
  649. opacity: 0;
  650. position: absolute;
  651. top: 0;
  652. bottom: 0;
  653. left:0;
  654. right:0;
  655. display: flex;
  656. flex-direction: column;
  657. justify-content: center;
  658. border-radius: 4px;
  659. border: 1px solid var(--up-border-color, #eee);
  660. padding: 0 10px;
  661. }
  662. }
  663. }
  664. </style>