mx-datepicker.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. <template>
  2. <view v-if="isShow" class="picker">
  3. <u-mask :show="isShow" @click="onCancel"></u-mask>
  4. <!-- 日期选择器 -->
  5. <view v-if="type!='time'" class="picker-modal">
  6. <view class="picker-modal-header">
  7. <view class="picker-icon picker-icon-zuozuo" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetYear('-1')"></view>
  8. <view class="picker-icon picker-icon-zuo" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetMonth('-1')"></view>
  9. <text class="picker-modal-header-title">{{title}}</text>
  10. <view class="picker-icon picker-icon-you" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetMonth('+1')"></view>
  11. <view class="picker-icon picker-icon-youyou" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetYear('+1')"></view>
  12. </view>
  13. <swiper class="picker-modal-body" :circular="true" :duration="200" :skip-hidden-item-layout="true" :current="calendarIndex" @change="onSwiperChange">
  14. <swiper-item class="picker-calendar" v-for="(calendar,calendarIndex2) in calendars" :key="calendarIndex2">
  15. <view class="picker-calendar-view" v-for="(week,index) in weeks" :key="index - 7">
  16. <view class="picker-calendar-view-item">{{week}}</view>
  17. </view>
  18. <view class="picker-calendar-view" v-for="(date,dateIndex) in calendar" :key="dateIndex" @click="onSelectDate(date)">
  19. <!-- 背景样式 -->
  20. <view v-show="date.bgStyle.type" :class="'picker-calendar-view-'+date.bgStyle.type" :style="{background: date.bgStyle.background}"></view>
  21. <!-- 正常和选中样式 -->
  22. <view class="picker-calendar-view-item" :style="{opacity: date.statusStyle.opacity, color: date.statusStyle.color, background: date.statusStyle.background}">
  23. <text>{{date.title}}</text>
  24. </view>
  25. <!-- 小圆点样式 -->
  26. <view class="picker-calendar-view-dot" :style="{opacity: date.dotStyle.opacity, background: date.dotStyle.background}"></view>
  27. <!-- 信息样式 -->
  28. <view v-show="date.tips" class="picker-calendar-view-tips">{{date.tips}}</view>
  29. </view>
  30. </swiper-item>
  31. </swiper>
  32. <view class="picker-modal-footer">
  33. <view class="picker-modal-footer-info">
  34. <block v-if="isMultiSelect">
  35. <view class="picker-display">
  36. <text>{{beginText}}日期</text>
  37. <text class="picker-display-text">{{BeginTitle}}</text>
  38. <view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
  39. :style="{color}" @click="onShowTimePicker('begin')">{{BeginTimeTitle}}</view>
  40. </view>
  41. <view class="picker-display">
  42. <text>{{endText}}日期</text>
  43. <text class="picker-display-text">{{EndTitle}}</text>
  44. <view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
  45. :style="{color}" @click="onShowTimePicker('end')">{{EndTimeTitle}}</view>
  46. </view>
  47. </block>
  48. <block v-else>
  49. <view class="picker-display">
  50. <text>当前选择</text>
  51. <text class="picker-display-text">{{BeginTitle}}</text>
  52. <view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
  53. :style="{color}" @click="onShowTimePicker('begin')">{{BeginTimeTitle}}</view>
  54. </view>
  55. </block>
  56. </view>
  57. <view class="picker-modal-footer-btn">
  58. <view class="picker-btn" :hover-stay-time="100" hover-class="picker-btn-active" @click="onCancel">取消</view>
  59. <view class="picker-btn" :style="{color}" :hover-stay-time="100" hover-class="picker-btn-active" @click="onConfirm">确定</view>
  60. </view>
  61. </view>
  62. </view>
  63. <!-- 时间选择器 -->
  64. <view v-if="showTimePicker" class="picker">
  65. <view class="picker-modal picker-time">
  66. <view class="picker-modal-header">
  67. <text class="picker-modal-header-title">选择日期</text>
  68. </view>
  69. <picker-view class="picker-modal-time" indicator-class="picker-modal-time-item" :value="timeValue" @change="onTimeChange">
  70. <picker-view-column>
  71. <view v-for="(v,i) in 24" :key="i">{{i<10?'0'+i:i}}时</view>
  72. </picker-view-column>
  73. <picker-view-column>
  74. <view v-for="(v,i) in 60" :key="i">{{i<10?'0'+i:i}}分</view>
  75. </picker-view-column>
  76. <picker-view-column v-if="showSeconds">
  77. <view v-for="(v,i) in 60" :key="i">{{i<10?'0'+i:i}}秒</view>
  78. </picker-view-column>
  79. </picker-view>
  80. <view class="picker-modal-footer">
  81. <view class="picker-modal-footer-info">
  82. <view class="picker-display">
  83. <text>当前选择</text>
  84. <text class="picker-display-text">{{PickerTimeTitle}}</text>
  85. </view>
  86. </view>
  87. <view class="picker-modal-footer-btn">
  88. <view class="picker-btn" :hover-stay-time="100" hover-class="picker-btn-active" @click="onCancelTime">取消</view>
  89. <view class="picker-btn" :style="{color}" :hover-stay-time="100" hover-class="picker-btn-active" @click="onConfirmTime">确定</view>
  90. </view>
  91. </view>
  92. </view>
  93. </view>
  94. </view>
  95. </template>
  96. <script>
  97. /**
  98. * 工具函数库
  99. */
  100. const DateTools = {
  101. /**
  102. * 获取公历节日
  103. * @param date Date对象
  104. */
  105. getHoliday(date) {
  106. let holidays = {
  107. '0101': '元旦',
  108. '0214': '情人',
  109. '0308': '妇女',
  110. '0312': '植树',
  111. '0401': '愚人',
  112. '0501': '劳动',
  113. '0504': '青年',
  114. '0601': '儿童',
  115. '0701': '建党',
  116. '0801': '建军',
  117. '0903': '抗日',
  118. '0910': '教师',
  119. '1001': '国庆',
  120. '1031': '万圣',
  121. '1224': '平安',
  122. '1225': '圣诞'
  123. };
  124. let value = this.format(date, 'mmdd');
  125. if (holidays[value]) return holidays[value];
  126. return false;
  127. },
  128. /**
  129. * 解析标准日期格式
  130. * @param s 日期字符串
  131. * @return 返回Date对象
  132. */
  133. parse: s => new Date(s.replace(/(年|月|-)/g, '/').replace(/(日)/g, '')),
  134. /**
  135. * 比较日期是否为同一天
  136. * @param a Date对象
  137. * @param b Date对象
  138. * @return Boolean
  139. */
  140. isSameDay: (a, b) => a.getMonth() == b.getMonth() && a.getFullYear() == b.getFullYear() && a.getDate() == b.getDate(),
  141. /**
  142. * 格式化Date对象
  143. * @param d 日期对象
  144. * @param f 格式字符串
  145. * @return 返回格式化后的字符串
  146. */
  147. format(d, f) {
  148. var o = {
  149. "m+": d.getMonth() + 1,
  150. "d+": d.getDate(),
  151. "h+": d.getHours(),
  152. "i+": d.getMinutes(),
  153. "s+": d.getSeconds(),
  154. "q+": Math.floor((d.getMonth() + 3) / 3),
  155. };
  156. if (/(y+)/.test(f))
  157. f = f.replace(RegExp.$1, (d.getFullYear() + "").substr(4 - RegExp.$1.length));
  158. for (var k in o)
  159. if (new RegExp("(" + k + ")").test(f))
  160. f = f.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  161. return f;
  162. },
  163. /**
  164. * 用于format格式化后的反解析
  165. * @param s 日期字符串
  166. * @param f 格式字符串
  167. * @return 返回Date对象
  168. */
  169. inverse(s, f) {
  170. var o = {
  171. "y": '',
  172. "m": '',
  173. "d": '',
  174. "h": '',
  175. "i": '',
  176. "s": '',
  177. };
  178. let d = new Date();
  179. if (s.length != f.length) return d;
  180. for (let i in f)
  181. if (o[f[i]] != undefined) o[f[i]] += s[i];
  182. if (o.y) d.setFullYear(o.y.length < 4 ? (d.getFullYear() + '').substr(0, 4 - o.y.length) + o.y : o.y);
  183. o.m && d.setMonth(o.m - 1, 1);
  184. o.d && d.setDate(o.d - 0);
  185. o.h && d.setHours(o.h - 0);
  186. o.i && d.setMinutes(o.i - 0);
  187. o.s && d.setSeconds(o.s - 0);
  188. return d;
  189. },
  190. /**
  191. * 获取日历数组(42天)
  192. * @param date 日期对象或日期字符串
  193. * @param proc 处理日历(和forEach类似),传递一个数组中的item
  194. * @return Array
  195. */
  196. getCalendar(date, proc) {
  197. let it = new Date(date),
  198. calendars = [];
  199. it.setDate(1);
  200. it.setDate(it.getDate() - ((it.getDay() == 0 ? 7 : it.getDay()) - 1)); //偏移量
  201. for (let i = 0; i < 42; i++) {
  202. let tmp = {
  203. dateObj: new Date(it),
  204. title: it.getDate(),
  205. isOtherMonth: it.getMonth() < date.getMonth() || it.getMonth() > date.getMonth()
  206. };
  207. calendars.push(Object.assign(tmp, proc ? proc(tmp) : {}));
  208. it.setDate(it.getDate() + 1);
  209. }
  210. return calendars;
  211. },
  212. /**
  213. * 获取日期到指定的月份1号(不改变原来的date对象)
  214. * @param d Date对象
  215. * @param v 指定的月份
  216. * @return Date对象
  217. */
  218. getDateToMonth(d, v) {
  219. let n = new Date(d);
  220. n.setMonth(v, 1);
  221. return n;
  222. },
  223. /**
  224. * 把时间数组转为时间字符串
  225. * @param t Array[时,分,秒]
  226. * @param showSecinds 是否显示秒
  227. * @return 字符串 时:分[:秒]
  228. */
  229. formatTimeArray(t, s) {
  230. let r = [...t];
  231. if (!s) r.length = 2;
  232. r.forEach((v, k) => r[k] = ('0' + v).slice(-2));
  233. return r.join(':');
  234. }
  235. };
  236. export default {
  237. props: {
  238. //颜色
  239. color: {
  240. type: String,
  241. default: '#409eff'
  242. },
  243. //是否显示秒 针对type为datetime或time时生效
  244. showSeconds: {
  245. type: Boolean,
  246. default: false
  247. },
  248. //初始的值
  249. value: [String, Array],
  250. //类型date time datetime range rangetime
  251. type: {
  252. type: String,
  253. default: 'range'
  254. },
  255. //是否显示
  256. show: {
  257. type: Boolean,
  258. default: false
  259. },
  260. //初始格式
  261. format: {
  262. type: String,
  263. default: ''
  264. },
  265. //显示公历节日
  266. showHoliday: {
  267. type: Boolean,
  268. default: true
  269. },
  270. //显示提示
  271. showTips: {
  272. type: Boolean,
  273. default: false
  274. },
  275. //开始文案 针对type为范围选择时生效
  276. beginText: {
  277. type: String,
  278. default: '开始'
  279. },
  280. //结束文案 针对type为范围选择时生效
  281. endText: {
  282. type: String,
  283. default: '结束'
  284. }
  285. },
  286. data() {
  287. return {
  288. isShow: false, //是否显示
  289. isMultiSelect: false, //是否为多选
  290. isContainTime: false, //是否包含时间
  291. date: {}, //当前日期对象
  292. weeks: ["一", "二", "三", "四", "五", "六", "日"],
  293. title: '初始化', //标题
  294. calendars: [[],[],[]], //日历数组
  295. calendarIndex: 1, //当前日历索引
  296. checkeds: [], //选中的日期对象集合
  297. showTimePicker: false, //是否显示时间选择器
  298. timeValue: [0, 0, 0], //时间选择器的值
  299. timeType: 'begin', //当前时间选择的类型
  300. beginTime: [0, 0, 0], //当前所选的开始时间值
  301. endTime: [0, 0, 0], //当前所选的结束时间值
  302. };
  303. },
  304. methods: {
  305. //设置值
  306. setValue(value) {
  307. this.date = new Date();
  308. this.checkeds = [];
  309. this.isMultiSelect = this.type.indexOf('range') >= 0;
  310. this.isContainTime = this.type.indexOf('time') >= 0;
  311. //将字符串解析为Date对象
  312. let parseDateStr = (str) => (this.format ? DateTools.inverse(str, this.format) : DateTools.parse(str));
  313. if (value) {
  314. if (this.isMultiSelect) {
  315. Array.isArray(value) && value.forEach((dateStr, index) => {
  316. let date = parseDateStr(dateStr);
  317. let time = [date.getHours(), date.getMinutes(), date.getSeconds()];
  318. if (index == 0) this.beginTime = time;
  319. else this.endTime = time;
  320. this.checkeds.push(date);
  321. });
  322. } else {
  323. if (this.type == 'time') {
  324. let date = parseDateStr('2019/1/1 ' + value);
  325. this.beginTime = [date.getHours(), date.getMinutes(), date.getSeconds()];
  326. this.onShowTimePicker('begin');
  327. } else {
  328. this.checkeds.push(parseDateStr(value));
  329. if (this.isContainTime) this.beginTime = [
  330. this.checkeds[0].getHours(),
  331. this.checkeds[0].getMinutes(),
  332. this.checkeds[0].getSeconds()
  333. ];
  334. }
  335. }
  336. if (this.checkeds.length) this.date = new Date(this.checkeds[0]);
  337. } else {
  338. if (this.isContainTime) {
  339. this.beginTime = [this.date.getHours(), this.date.getMinutes(), this.date.getSeconds()];
  340. if (this.isMultiSelect) this.endTime = [...this.beginTime];
  341. }
  342. this.checkeds.push(new Date(this.date));
  343. }
  344. if (this.type != 'time') this.refreshCalendars(true);
  345. else this.onShowTimePicker('begin');
  346. },
  347. //改变年份
  348. onSetYear(value) {
  349. this.date.setFullYear(this.date.getFullYear() + parseInt(value));
  350. this.refreshCalendars(true);
  351. },
  352. //改变月份
  353. onSetMonth(value) {
  354. this.date.setMonth(this.date.getMonth() + parseInt(value));
  355. this.refreshCalendars(true);
  356. },
  357. //时间选择变更
  358. onTimeChange(e) {
  359. this.timeValue = e.detail.value;
  360. },
  361. //设置时间选择器的显示状态
  362. onShowTimePicker(type) {
  363. this.showTimePicker = true;
  364. this.timeType = type;
  365. this.timeValue = type == 'begin' ? [...this.beginTime] : [...this.endTime];
  366. },
  367. //处理日历
  368. procCalendar(item) {
  369. //定义初始样式
  370. item.statusStyle = {
  371. opacity: 1,
  372. color: item.isOtherMonth ? '#ddd' : '#000',
  373. background: 'transparent'
  374. };
  375. item.bgStyle = {
  376. type: '',
  377. background: 'transparent'
  378. };
  379. item.dotStyle = {
  380. opacity: 1,
  381. background: 'transparent'
  382. };
  383. item.tips = "";
  384. //标记今天的日期
  385. if (DateTools.isSameDay(new Date(), item.dateObj)) {
  386. item.statusStyle.color = this.color;
  387. if (item.isOtherMonth) item.statusStyle.opacity = 0.3;
  388. }
  389. //标记选中项
  390. this.checkeds.forEach(date => {
  391. if (DateTools.isSameDay(date, item.dateObj)) {
  392. item.statusStyle.background = this.color;
  393. item.statusStyle.color = '#fff';
  394. item.statusStyle.opacity = 1;
  395. if (this.isMultiSelect && this.showTips) item.tips = this.beginText;
  396. }
  397. });
  398. //节假日或今日的日期标点
  399. if (item.statusStyle.background != this.color) {
  400. let holiday = this.showHoliday ? DateTools.getHoliday(item.dateObj) : false;
  401. if (holiday || DateTools.isSameDay(new Date(), item.dateObj)) {
  402. item.title = holiday || item.title;
  403. item.dotStyle.background = this.color;
  404. if (item.isOtherMonth) item.dotStyle.opacity = 0.2;
  405. }
  406. } else {
  407. item.title = item.dateObj.getDate();
  408. }
  409. //有两个日期
  410. if (this.checkeds.length == 2) {
  411. if (DateTools.isSameDay(this.checkeds[0], item.dateObj)) { //开始日期
  412. item.bgStyle.type = 'bgbegin';
  413. }
  414. if (DateTools.isSameDay(this.checkeds[1], item.dateObj)) { //结束日期
  415. if (this.isMultiSelect && this.showTips) item.tips = item.bgStyle.type ? this.beginText + ' / ' + this.endText : this.endText;
  416. if (!item.bgStyle.type) { //开始日期不等于结束日期
  417. item.bgStyle.type = 'bgend';
  418. } else {
  419. item.bgStyle.type = '';
  420. }
  421. }
  422. if (!item.bgStyle.type && (+item.dateObj > +this.checkeds[0] && +item.dateObj < +this.checkeds[1])) { //中间的日期
  423. item.bgStyle.type = 'bg';
  424. item.statusStyle.color = this.color;
  425. }
  426. if (item.bgStyle.type) {
  427. item.bgStyle.background = this.color;
  428. item.dotStyle.opacity = 1;
  429. item.statusStyle.opacity = 1;
  430. }
  431. }
  432. },
  433. //刷新日历
  434. refreshCalendars(refresh = false) {
  435. let date = new Date(this.date);
  436. let before = DateTools.getDateToMonth(date, date.getMonth() - 1);
  437. let after = DateTools.getDateToMonth(date, date.getMonth() + 1);
  438. if (this.calendarIndex == 0) {
  439. if(refresh) this.calendars.splice(0, 1, DateTools.getCalendar(date, this.procCalendar));
  440. this.calendars.splice(1, 1, DateTools.getCalendar(after, this.procCalendar));
  441. this.calendars.splice(2, 1, DateTools.getCalendar(before, this.procCalendar));
  442. } else if (this.calendarIndex == 1) {
  443. this.calendars.splice(0, 1, DateTools.getCalendar(before, this.procCalendar));
  444. if(refresh) this.calendars.splice(1, 1, DateTools.getCalendar(date, this.procCalendar));
  445. this.calendars.splice(2, 1, DateTools.getCalendar(after, this.procCalendar));
  446. } else if (this.calendarIndex == 2) {
  447. this.calendars.splice(0, 1, DateTools.getCalendar(after, this.procCalendar));
  448. this.calendars.splice(1, 1, DateTools.getCalendar(before, this.procCalendar));
  449. if(refresh) this.calendars.splice(2, 1, DateTools.getCalendar(date, this.procCalendar));
  450. }
  451. this.title = DateTools.format(this.date, 'yyyy年mm月');
  452. },
  453. //滑块切换
  454. onSwiperChange(e) {
  455. this.calendarIndex = e.detail.current;
  456. let calendar = this.calendars[this.calendarIndex];
  457. this.date = new Date(calendar[22].dateObj); //取中间一天,保证是当前的月份
  458. this.refreshCalendars();
  459. },
  460. //选中日期
  461. onSelectDate(date) {
  462. if (~this.type.indexOf('range') && this.checkeds.length == 2) this.checkeds = [];
  463. else if (!(~this.type.indexOf('range')) && this.checkeds.length) this.checkeds = [];
  464. this.checkeds.push(new Date(date.dateObj));
  465. this.checkeds.sort((a, b) => a - b); //从小到大排序
  466. this.calendars.forEach(calendar => {
  467. calendar.forEach(this.procCalendar); //重新处理
  468. });
  469. },
  470. //时间选择取消
  471. onCancelTime() {
  472. this.showTimePicker = false;
  473. this.type == 'time' && this.onCancel();
  474. },
  475. //时间选择确定
  476. onConfirmTime() {
  477. if (this.timeType == 'begin') this.beginTime = this.timeValue;
  478. else this.endTime = this.timeValue;
  479. this.showTimePicker = false;
  480. this.type == 'time' && this.onConfirm();
  481. },
  482. //取消
  483. onCancel() {
  484. this.$emit('cancel', false);
  485. },
  486. //确定
  487. onConfirm() {
  488. let result = {
  489. value: null,
  490. date: null
  491. };
  492. //定义默认格式
  493. let defaultFormat = {
  494. 'date': 'yyyy/mm/dd',
  495. 'time': 'hh:ii' + (this.showSeconds ? ':ss' : ''),
  496. 'datetime': ''
  497. };
  498. defaultFormat['datetime'] = defaultFormat.date + ' ' + defaultFormat.time;
  499. let fillTime = (date, timeArr) => {
  500. date.setHours(timeArr[0], timeArr[1]);
  501. if (this.showSeconds) date.setSeconds(timeArr[2]);
  502. };
  503. if (this.type == 'time') {
  504. let date = new Date();
  505. fillTime(date, this.beginTime);
  506. result.value = DateTools.format(date, this.format ? this.format : defaultFormat.time);
  507. result.date = date;
  508. } else {
  509. if (this.isMultiSelect) {
  510. let values = [],
  511. dates = [];
  512. if (this.checkeds.length < 2) return uni.showToast({
  513. icon: 'none',
  514. title: '请选择两个日期'
  515. });
  516. this.checkeds.forEach((date, index) => {
  517. let newDate = new Date(date);
  518. if (this.isContainTime) {
  519. let time = [this.beginTime, this.endTime];
  520. fillTime(newDate, time[index]);
  521. }
  522. values.push(DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
  523. 'datetime' : 'date']));
  524. dates.push(newDate);
  525. });
  526. result.value = values;
  527. result.date = dates;
  528. } else {
  529. let newDate = new Date(this.checkeds[0]);
  530. if (this.isContainTime) {
  531. newDate.setHours(this.beginTime[0], this.beginTime[1]);
  532. if (this.showSeconds) newDate.setSeconds(this.beginTime[2]);
  533. }
  534. result.value = DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
  535. 'datetime' : 'date']);
  536. result.date = newDate;
  537. }
  538. }
  539. this.$emit('confirm', result);
  540. }
  541. },
  542. computed: {
  543. BeginTitle() {
  544. let value = '未选择';
  545. if (this.checkeds.length) value = DateTools.format(this.checkeds[0], 'yy/mm/dd');
  546. return value;
  547. },
  548. EndTitle() {
  549. let value = '未选择';
  550. if (this.checkeds.length == 2) value = DateTools.format(this.checkeds[1], 'yy/mm/dd');
  551. return value;
  552. },
  553. PickerTimeTitle() {
  554. return DateTools.formatTimeArray(this.timeValue, this.showSeconds);
  555. },
  556. BeginTimeTitle() {
  557. return this.BeginTitle != '未选择' ? DateTools.formatTimeArray(this.beginTime, this.showSeconds) : '';
  558. },
  559. EndTimeTitle() {
  560. return this.EndTitle != '未选择' ? DateTools.formatTimeArray(this.endTime, this.showSeconds) : '';
  561. }
  562. },
  563. watch: {
  564. show(newValue, oldValue) {
  565. newValue && this.setValue(this.value);
  566. this.isShow = newValue;
  567. },
  568. value(newValue, oldValue) {
  569. setTimeout(()=>{
  570. this.setValue(newValue);
  571. }, 0);
  572. }
  573. }
  574. }
  575. </script>
  576. <style lang="scss" scoped>
  577. $z-index: 100;
  578. $cell-spacing: 20upx;
  579. $calendar-size: 630upx;
  580. $calendar-item-size: 90upx;
  581. .picker {
  582. position: fixed;
  583. z-index: $z-index;
  584. background: rgba(255, 255, 255, 0);
  585. left: 0;
  586. top: 0;
  587. width: 100%;
  588. height: 100%;
  589. font-size: 28upx;
  590. &-btn {
  591. padding: $cell-spacing*0.5 $cell-spacing;
  592. border-radius: 12upx;
  593. color: #666;
  594. &-active {
  595. background: rgba(0, 0, 0, .1);
  596. }
  597. }
  598. &-display {
  599. color: #666;
  600. &-text {
  601. color: #000;
  602. margin: 0 $cell-spacing*0.5;
  603. }
  604. &-link {
  605. display: inline-block;
  606. &-active {
  607. background: rgba(0, 0, 0, .1);
  608. }
  609. }
  610. }
  611. &-time {
  612. width: $calendar-size - 80upx !important;
  613. left: ((750upx - $calendar-size) / 2 + 40upx) !important;
  614. }
  615. &-modal {
  616. background: #fff;
  617. position: absolute;
  618. top: 50%;
  619. left: (750upx - $calendar-size) / 2;
  620. width: $calendar-size;
  621. transform: translateY(-50%);
  622. box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.1);
  623. border-radius: 12upx;
  624. z-index: 999999;
  625. &-header {
  626. text-align: center;
  627. line-height: 80upx;
  628. font-size: 32upx;
  629. &-title {
  630. display: inline-block;
  631. width: 40%;
  632. }
  633. .picker-icon {
  634. display: inline-block;
  635. line-height: 50upx;
  636. width: 50upx;
  637. height: 50upx;
  638. border-radius: 50upx;
  639. text-align: center;
  640. margin: 10upx;
  641. background: #fff;
  642. font-size: 36upx;
  643. &-active {
  644. background: rgba(0, 0, 0, .1);
  645. }
  646. }
  647. }
  648. &-body {
  649. width: $calendar-size !important;
  650. height: $calendar-size !important;
  651. position: relative;
  652. }
  653. &-time {
  654. width: 100%;
  655. height: 180upx;
  656. text-align: center;
  657. line-height: 60upx;
  658. }
  659. &-footer {
  660. display: flex;
  661. justify-content: space-between;
  662. align-items: center;
  663. padding: $cell-spacing;
  664. &-info {
  665. flex-grow: 1;
  666. }
  667. &-btn {
  668. flex-shrink: 0;
  669. display: flex;
  670. }
  671. }
  672. }
  673. &-calendar {
  674. position: absolute;
  675. left: 0;
  676. top: 0;
  677. width: 100%;
  678. height: 100%;
  679. display: flex;
  680. align-items: center;
  681. flex-wrap: wrap;
  682. &-view {
  683. position: relative;
  684. width: $calendar-item-size;
  685. height: $calendar-item-size;
  686. text-align: center;
  687. &-bgbegin,
  688. &-bg,
  689. &-bgend,
  690. &-item,
  691. &-dot,
  692. &-tips {
  693. position: absolute;
  694. transition: .2s;
  695. }
  696. &-bgbegin,
  697. &-bg,
  698. &-bgend {
  699. opacity: .15;
  700. height: 80%;
  701. }
  702. &-bg {
  703. left: 0;
  704. top: 10%;
  705. width: 100%;
  706. }
  707. &-bgbegin {
  708. border-radius: $calendar-item-size 0 0 $calendar-item-size;
  709. top: 10%;
  710. left: 10%;
  711. width: 90%;
  712. }
  713. &-bgend {
  714. border-radius: 0 $calendar-item-size $calendar-item-size 0;
  715. top: 10%;
  716. left: 0%;
  717. width: 90%;
  718. }
  719. &-item {
  720. left: 5%;
  721. top: 5%;
  722. width: 90%;
  723. height: 90%;
  724. border-radius: $calendar-item-size;
  725. display: flex;
  726. align-items: center;
  727. justify-content: center;
  728. }
  729. &-dot {
  730. right: 10%;
  731. top: 10%;
  732. width: 12upx;
  733. height: 12upx;
  734. border-radius: 12upx;
  735. }
  736. &-tips {
  737. bottom: 100%;
  738. left: 50%;
  739. transform: translateX(-50%);
  740. background: #4E4B46;
  741. color: #fff;
  742. border-radius: 12upx;
  743. padding: 10upx 20upx;
  744. font-size: 24upx;
  745. width: max-content;
  746. margin-bottom: 5px;
  747. pointer-events: none;
  748. &:after {
  749. content: "";
  750. position: absolute;
  751. top: 100%;
  752. left: 50%;
  753. transform: translateX(-50%);
  754. width: 0;
  755. height: 0;
  756. border-style: solid;
  757. border-width: 5px 5px 0 5px;
  758. border-color: #4E4B46 transparent transparent transparent;
  759. }
  760. }
  761. }
  762. }
  763. }
  764. @font-face {
  765. font-family: "mxdatepickericon";
  766. src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMYAAsAAAAACBgAAALMAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDIgqDRIJiATYCJAMUCwwABCAFhG0HSRvfBsg+QCa3noNAyAQ9w6GDvbwpNp2vloCyn8bD/x+y+/5qDhtj+T4eRVEcbsCoKMFASzCgLdDkmqYDwgxkWQ6YH5L/YnppOlLEjlnter43YRjU7M6vJ3iGADVAgJn5kqjv/wEii23T86UsAQT+04fV+o97VTMx4PPZt4DlorLXwIQiGMA5uhaVrBWqGHfQXcTEiE+PE+g2SUlxWlLVBHwUYFMgrgwSB3wstTKSGzqF1nOyiGeeOtNjV4An/vvxR58PSc3AzrMViyDvPo/7dVEUzn5GROfIWAcU4rLXfMFdhte56y4We9gGNEVIezkBOOaQXUrbTf/hJVkhGpDdCw7dSOEzByMEn3kIic98hMxnAfeFPKWCbjRcA148/HxhCEkaA94eGWFaGolsblpaWz8/Po2WVuNHh1fmBpZHIpqal9fOjizhTteY+RZ9rv02I/pq0W6QVH3pSncBz3m55r9ZIPycHfmenvxe4uyutIgfT5u4bgkDusl9gcF0rnfnz+b2NpSaQWBFeu8GIL1xQj5AH/6FAsEr/50F28e/gA9ny6KjLrxIp0TE+UucmQOl5AFNLXkzZufWamWHYEI39PEP2If97CMdm51N6DSmIekwAVmneXTBr0PVYx+aTgfQbU3p+R4jKHdRurBq0oEw6AKSfm+QDbpGF/w3VOP+oBnMHbqdx409FjP4RRHHkAj5IWgQiBUjHfMTuQ1Icpg5avI4sQVRu8EHdWptM1aKrIjuscfeL+kZwxBTYoElztOQ2UygjRIjEphaZsyWodHgvm9SC8QC/JygEA6DiCDeEMhAQFhhOpvxa/18A0TiYMahIy0L2hYIZWeYH9JR085Al4qts1re5St2/SR6DINBGEVYQCWOETHDMAHZ+pcZIQJGTV4RtMmg8UbhuWL1+VLLA2RFHYC71kiRo0SNpjwQh8pj2EFU3oTNmS1WqgIA') format('woff2');
  767. }
  768. .picker-icon {
  769. font-family: "mxdatepickericon" !important;
  770. }
  771. .picker-icon-you:before {
  772. content: "\e63e";
  773. }
  774. .picker-icon-zuo:before {
  775. content: "\e640";
  776. }
  777. .picker-icon-zuozuo:before {
  778. content: "\e641";
  779. }
  780. .picker-icon-youyou:before {
  781. content: "\e642";
  782. }
  783. </style>