timepick.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <view :show="show && !disabled" mode="bottom" :round="round">
  3. <view class="time-content">
  4. <view class="header-top">
  5. <slot name="header">
  6. <view class="cancel" @click="cancel">{{cancelText}}</view>
  7. <view class="title">{{title}}</view>
  8. <view class="confirm" @click="confirm">{{confirmText}}</view>
  9. </slot>
  10. </view>
  11. <view class="time-wrapper">
  12. <picker-view
  13. :value="pickerValue"
  14. indicator-class="picker-box"
  15. :indicator-style="indicatorStyle"
  16. @change="bindChange"
  17. >
  18. <picker-view-column v-for="(item,index) in innerColumns" :key="index">
  19. <view class="item" v-for="(item1,index1) in item.values" :key="index1">
  20. {{item1}}{{item.label}}
  21. </view>
  22. </picker-view-column>
  23. </picker-view>
  24. </view>
  25. </view>
  26. </view>
  27. </template>
  28. <script>
  29. export default {
  30. name: 'GDatepicker',
  31. /**
  32. * 插槽
  33. * header 自定义头部样式
  34. */
  35. props:{
  36. value:{
  37. type: [String,Number],
  38. default:''
  39. },
  40. show: {
  41. type:[Boolean,String],
  42. default:false
  43. },
  44. cancelText: {
  45. type:String,
  46. default:'取消'
  47. },
  48. confirmText: {
  49. type:String,
  50. default:'确定'
  51. },
  52. title: {
  53. type:String,
  54. default:'时间选择'
  55. },
  56. /**
  57. * 是否格式化时间
  58. * format 格式同 uView timeFormat API,如:"yyyy-mm-dd"
  59. * 格式可以自由搭配: 年为"yyyy",月为"mm",日为"dd",时为"hh",分为"MM",秒为"ss",
  60. * 注意分为 大写M
  61. */
  62. format: {
  63. type:String,
  64. default:''
  65. },
  66. // 是否转换成json格式数据 如:2020-01-01T19:19:20.253Z
  67. formatToJson: {
  68. type: Boolean,
  69. default: false
  70. },
  71. /**
  72. * 时间模式(选择器可选时间格式)(如果mode为date,则format不应包含时分秒格式)
  73. * datetime 全时间:年月日时分秒
  74. * date 只包含年月日
  75. */
  76. mode: {
  77. type: String,
  78. default: 'datetime'
  79. },
  80. // 可选的最大时间 时间戳毫秒 默认后十年
  81. maxDate: {
  82. type: [String, Number],
  83. default: new Date(new Date().getFullYear() + 10, 0, 1).getTime(),
  84. },
  85. // 可选的最小时间 时间戳毫秒 默认前十年
  86. minDate: {
  87. type: [String, Number],
  88. default: new Date(new Date().getFullYear() - 10, 0, 1).getTime(),
  89. },
  90. minMonth: {
  91. type: [String, Number],
  92. default: 1
  93. },
  94. maxMonth: {
  95. type: [String, Number],
  96. default: 12
  97. },
  98. minDay: {
  99. type: [String, Number],
  100. default: 1
  101. },
  102. maxDay: {
  103. type: [String, Number],
  104. default: 31
  105. },
  106. // 弹窗圆角值
  107. round: {
  108. type: [String, Number],
  109. default:0
  110. },
  111. disabled:{
  112. type:Boolean,
  113. default:false
  114. }
  115. },
  116. model: {
  117. prop: 'value',
  118. event: 'input'
  119. },
  120. data() {
  121. return {
  122. year: 2022,
  123. month: 0,
  124. day: 0,
  125. hour: 0,
  126. minute: 0,
  127. second: 0,
  128. pickerValue: [], // picker-view绑定值
  129. innerColumns: [],
  130. indicatorStyle: `color: red;!important`
  131. }
  132. },
  133. watch:{
  134. show(val) {
  135. if(val) {
  136. this.initData()
  137. if(this.value) {
  138. this.setData(this.value)
  139. } else {
  140. this.setData()
  141. }
  142. }
  143. }
  144. },
  145. // mounted() {
  146. // this.initData()
  147. // },
  148. methods: {
  149. initData() {
  150. const startYear = new Date(Number(this.minDate)).getFullYear()
  151. const endYear = new Date(Number(this.maxDate)).getFullYear()
  152. let result = [
  153. {
  154. type: 'year',
  155. label: '年',
  156. range: [startYear, endYear]
  157. },
  158. {
  159. type: 'month',
  160. label: '月',
  161. range: [this.minMonth, this.maxMonth]
  162. },
  163. {
  164. type: 'day',
  165. label: '日',
  166. range: [this.minDay, this.maxDay]
  167. },
  168. {
  169. type: 'hour',
  170. label: '时',
  171. range: [0,23]
  172. },
  173. {
  174. type: 'minute',
  175. label: '分',
  176. range: [0, 59]
  177. },
  178. {
  179. type: 'secode',
  180. label: '秒',
  181. range: [0, 59]
  182. }
  183. ]
  184. this.innerColumns = result.map(({ type, label, range }) => {
  185. const num = range[1] - range[0] + 1;
  186. let values = this.getColumnTimes(num, (index) => {
  187. let value = range[0] + index
  188. value = type === 'year' ? `${value}` : this.padZero(value)
  189. return value
  190. })
  191. return { type, label, values }
  192. })
  193. if(this.mode === "date") {
  194. this.innerColumns.splice(3,3)
  195. }
  196. },
  197. bindChange (e) {
  198. const val = e.detail.value;
  199. const date = new Date();
  200. this.year = this.getCurrCol(0)[val[0]] || date.getFullYear()
  201. this.month = this.getCurrCol(1)[val[1]] ||date.getMonth() + 1;
  202. this.day = this.getCurrCol(2)[val[2]] || date.getDate()
  203. this.hour = this.getCurrCol(3)[val[3]] || date.getHours();
  204. this.minute = this.getCurrCol(4)[val[4]] || date.getMinutes();
  205. this.second = this.getCurrCol(5)[val[5]] || date.getSeconds();
  206. /* // change事件 需要启用取消注释
  207. const currentTime = new Date(this.year,this.month - 1,this.day,this.hour,this.minute,this.second).getTime()
  208. if(this.format) {
  209. this.$emit('change',uni.$u.timeFormat(currentTime,this.format))
  210. } else {
  211. this.$emit('change',currentTime)
  212. } */
  213. },
  214. getCurrCol(index) {
  215. if(!this.innerColumns[index]) {
  216. return []
  217. }
  218. return this.innerColumns[index].values
  219. },
  220. cancel() {
  221. this.$emit('cancel')
  222. this.$emit('update:show',false)
  223. },
  224. confirm() {
  225. const str = `${this.year}/${this.month}/${this.day} ${this.hour}:${this.minute}:${this.second}`
  226. const time = new Date(str).getTime()
  227. this.$emit('confirm',this.formatDate(time))
  228. this.$emit('input', this.formatDate(time));
  229. },
  230. // set data
  231. setData(value) {
  232. let date;
  233. if(value) {
  234. const n = isNaN(value) ? value : Number(value)
  235. date = new Date(n)
  236. } else {
  237. date = new Date()
  238. }
  239. const startYear = new Date(Number(this.minDate)).getFullYear()
  240. this.year = date.getFullYear()
  241. // 绑定时间小于当前时间列表的开始时间则取第一项
  242. const yearColumnIndex = this.year > startYear ? this.year - startYear : 0
  243. this.day = date.getDate()
  244. this.month = date.getMonth() + 1
  245. this.hour = date.getHours()
  246. this.minute = date.getMinutes()
  247. this.second = date.getSeconds()
  248. // pickerValue数组中的项依次表示 picker-view 内的 picker-view-column 选择的第几项
  249. this.pickerValue = [yearColumnIndex,this.month - 1,this.day - 1,this.hour,this.minute,this.second]
  250. this.$nextTick(() => {
  251. if(this.mode === "date") {
  252. this.pickerValue.splice(3,3)
  253. }
  254. })
  255. },
  256. formatDate(time) {
  257. if(this.format) {
  258. return uni.$u.timeFormat(time,this.format)
  259. } else if(this.formatToJson) {
  260. return new Date(time).toJSON()
  261. } else {
  262. return time
  263. }
  264. },
  265. // 设置列数据
  266. getColumnTimes(n, iteratee) {
  267. let index = -1
  268. const result = Array(n < 0 ? 0 : n)
  269. while (++index < n) {
  270. result[index] = iteratee(index)
  271. }
  272. return result
  273. },
  274. // 字符串转换
  275. padZero(num, targetLength = 2) {
  276. let str = `${num}`
  277. while (str.length < targetLength) {
  278. str = `0${str}`
  279. }
  280. return str
  281. }
  282. }
  283. }
  284. </script>
  285. <style lang="scss" scoped>
  286. picker-view {
  287. width: 100%;
  288. height: 100%;
  289. }
  290. .time-content {
  291. height: 280px;
  292. .header-top {
  293. height: 44px;
  294. line-height: 44px;
  295. display: flex;
  296. padding: 0 16px;
  297. font-size: 15px;
  298. border-bottom: 1px solid #EEEEEE;
  299. overflow: hidden;
  300. .cancel {
  301. color: #999999;
  302. }
  303. .title {
  304. flex: 1;
  305. text-align: center;
  306. color: #666666;
  307. }
  308. .confirm {
  309. color: #27B57D;
  310. }
  311. }
  312. .time-wrapper {
  313. height: calc(100% - 44px);
  314. box-sizing: border-box;
  315. text-align: center;
  316. .picker-box {
  317. height: 44px;
  318. font-size: 18px!important;
  319. font-weight: 800!important;
  320. color: #333333!important;
  321. }
  322. .item {
  323. line-height: 44px;
  324. text-align: center;
  325. }
  326. }
  327. }
  328. </style>