uni-calendar.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. <template>
  2. <view class="uni-calendar">
  3. <view v-if="!insert && show" class="uni-calendar__mask" :class="{ 'uni-calendar--mask-show': aniMaskShow }" @click="clean"></view>
  4. <view v-if="insert || show" class="uni-calendar__content" :class="{ 'uni-calendar--fixed': !insert, 'uni-calendar--ani-show': aniMaskShow }" @touchstart="touchStart" @touchend="touchEnd">
  5. <view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
  6. <view class="uni-calendar__header-btn-box" @click="close">
  7. <text class="uni-calendar__header-text uni-calendar--fixed-width">{{ cancelText }}</text>
  8. </view>
  9. <view class="uni-calendar__header-btn-box" @click="confirm">
  10. <text class="uni-calendar__header-text uni-calendar--fixed-width">{{ okText }}</text>
  11. </view>
  12. </view>
  13. <view class="uni-calendar__header">
  14. <!-- <view class="uni-calendar__header-btn-box" @click.stop="pre">
  15. <view class="uni-calendar__header-btn uni-calendar--left"></view>
  16. </view> -->
  17. <picker mode="date" :value="date" fields="month" @change="bindDateChange">
  18. <text class="uni-calendar__header-text">{{ (nowDate.year || "") + " 年 " + (nowDate.month || "") + " 月" }}</text>
  19. </picker>
  20. <!-- <view class="uni-calendar__header-btn-box" @click.stop="next">
  21. <view class="uni-calendar__header-btn uni-calendar--right"></view>
  22. </view> -->
  23. <!-- <text class="uni-calendar__backtoday" @click="backtoday">{{todayText}}</text> -->
  24. <view style="margin: auto"></view>
  25. <view style="display: flex">
  26. <view style="margin: 0px 10px">
  27. <span style="margin-right: 5px">巡检</span>
  28. <span style="color: #00cdac">{{ JSON.stringify(currentDateList) === "{}" ? 0 : currentDateList.patrolledCount }}</span>
  29. </view>
  30. <view class="margin-left-xs">
  31. <span style="margin-right: 5px">漏检</span>
  32. <span style="color: #f07d28">{{ JSON.stringify(currentDateList) === "{}" ? 0 : currentDateList.undetectedCount }}</span>
  33. </view>
  34. </view>
  35. </view>
  36. <view class="uni-calendar__box">
  37. <view v-if="showMonth" class="uni-calendar__box-bg">
  38. <text class="uni-calendar__box-bg-text">{{ nowDate.month }}</text>
  39. </view>
  40. <view class="uni-calendar__weeks">
  41. <view class="uni-calendar__weeks-day">
  42. <text class="uni-calendar__weeks-day-text">{{ SUNText }}</text>
  43. </view>
  44. <view class="uni-calendar__weeks-day">
  45. <text class="uni-calendar__weeks-day-text">{{ monText }}</text>
  46. </view>
  47. <view class="uni-calendar__weeks-day">
  48. <text class="uni-calendar__weeks-day-text">{{ TUEText }}</text>
  49. </view>
  50. <view class="uni-calendar__weeks-day">
  51. <text class="uni-calendar__weeks-day-text">{{ WEDText }}</text>
  52. </view>
  53. <view class="uni-calendar__weeks-day">
  54. <text class="uni-calendar__weeks-day-text">{{ THUText }}</text>
  55. </view>
  56. <view class="uni-calendar__weeks-day">
  57. <text class="uni-calendar__weeks-day-text">{{ FRIText }}</text>
  58. </view>
  59. <view class="uni-calendar__weeks-day">
  60. <text class="uni-calendar__weeks-day-text">{{ SATText }}</text>
  61. </view>
  62. </view>
  63. <view class="uni-calendar__weeks" v-for="(item, weekIndex) in weeks" :key="weekIndex">
  64. <view class="uni-calendar__weeks-item" v-for="(weeks, weeksIndex) in item" :key="weeksIndex">
  65. <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
  66. </view>
  67. </view>
  68. </view>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. import Calendar from "./util.js";
  74. import calendarItem from "./uni-calendar-item.vue";
  75. import { initVueI18n } from "@dcloudio/uni-i18n";
  76. import messages from "./i18n/index.js";
  77. const { t } = initVueI18n(messages);
  78. /**
  79. * Calendar 日历
  80. * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
  81. * @tutorial https://ext.dcloud.net.cn/plugin?id=56
  82. * @property {String} date 自定义当前时间,默认为今天
  83. * @property {Boolean} lunar 显示农历
  84. * @property {String} startDate 日期选择范围-开始日期
  85. * @property {String} endDate 日期选择范围-结束日期
  86. * @property {Boolean} range 范围选择
  87. * @property {Boolean} insert = [true|false] 插入模式,默认为false
  88. * @value true 弹窗模式
  89. * @value false 插入模式
  90. * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
  91. * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
  92. * @property {Boolean} showMonth 是否选择月份为背景
  93. * @event {Function} change 日期改变,`insert :ture` 时生效
  94. * @event {Function} confirm 确认选择`insert :false` 时生效
  95. * @event {Function} monthSwitch 切换月份时触发
  96. * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
  97. */
  98. let touchStartX = 0; // 触屏起始点x
  99. let touchStartY = 0; // 触屏起始点y
  100. export default {
  101. components: {
  102. calendarItem,
  103. },
  104. emits: ["close", "confirm", "change", "monthSwitch"],
  105. props: {
  106. date: {
  107. type: String,
  108. default: "",
  109. },
  110. selected: {
  111. type: Array,
  112. default() {
  113. return [];
  114. },
  115. },
  116. lunar: {
  117. type: Boolean,
  118. default: false,
  119. },
  120. startDate: {
  121. type: String,
  122. default: "",
  123. },
  124. endDate: {
  125. type: String,
  126. default: "",
  127. },
  128. range: {
  129. type: Boolean,
  130. default: false,
  131. },
  132. insert: {
  133. type: Boolean,
  134. default: true,
  135. },
  136. showMonth: {
  137. type: Boolean,
  138. default: true,
  139. },
  140. clearDate: {
  141. type: Boolean,
  142. default: true,
  143. },
  144. currentDate: {
  145. type: Object,
  146. default: {},
  147. },
  148. },
  149. data() {
  150. return {
  151. show: false,
  152. weeks: [],
  153. calendar: {},
  154. nowDate: "",
  155. aniMaskShow: false,
  156. currentDateList: {},
  157. };
  158. },
  159. computed: {
  160. /**
  161. * for i18n
  162. */
  163. okText() {
  164. return t("uni-calender.ok");
  165. },
  166. cancelText() {
  167. return t("uni-calender.cancel");
  168. },
  169. todayText() {
  170. return t("uni-calender.today");
  171. },
  172. monText() {
  173. return t("uni-calender.MON");
  174. },
  175. TUEText() {
  176. return t("uni-calender.TUE");
  177. },
  178. WEDText() {
  179. return t("uni-calender.WED");
  180. },
  181. THUText() {
  182. return t("uni-calender.THU");
  183. },
  184. FRIText() {
  185. return t("uni-calender.FRI");
  186. },
  187. SATText() {
  188. return t("uni-calender.SAT");
  189. },
  190. SUNText() {
  191. return t("uni-calender.SUN");
  192. },
  193. },
  194. watch: {
  195. date(newVal) {
  196. // this.cale.setDate(newVal)
  197. this.init(newVal);
  198. },
  199. startDate(val) {
  200. this.cale.resetSatrtDate(val);
  201. this.cale.setDate(this.nowDate.fullDate);
  202. this.weeks = this.cale.weeks;
  203. },
  204. endDate(val) {
  205. this.cale.resetEndDate(val);
  206. this.cale.setDate(this.nowDate.fullDate);
  207. this.weeks = this.cale.weeks;
  208. },
  209. selected(newVal) {
  210. this.cale.setSelectInfo(this.nowDate.fullDate, newVal);
  211. this.weeks = this.cale.weeks;
  212. },
  213. currentDate(obj) {
  214. this.currentDateList = obj;
  215. },
  216. },
  217. created() {
  218. // 获取日历方法实例
  219. this.cale = new Calendar({
  220. // date: new Date(),
  221. selected: this.selected,
  222. startDate: this.startDate,
  223. endDate: this.endDate,
  224. range: this.range,
  225. });
  226. // 选中某一天
  227. // this.cale.setDate(this.date)
  228. this.init(this.date);
  229. // this.setDay
  230. },
  231. methods: {
  232. // 取消穿透
  233. clean() {},
  234. bindDateChange(e) {
  235. const value = e.detail.value + "-1";
  236. console.log(this.cale.getDate(value));
  237. this.setDate(value);
  238. },
  239. /**
  240. * 初始化日期显示
  241. * @param {Object} date
  242. */
  243. init(date) {
  244. this.cale.setDate(date);
  245. this.weeks = this.cale.weeks;
  246. this.nowDate = this.calendar = this.cale.getInfo(date);
  247. },
  248. /**
  249. * 打开日历弹窗
  250. */
  251. open() {
  252. // 弹窗模式并且清理数据
  253. if (this.clearDate && !this.insert) {
  254. this.cale.cleanMultipleStatus();
  255. // this.cale.setDate(this.date)
  256. this.init(this.date);
  257. }
  258. this.show = true;
  259. this.$nextTick(() => {
  260. setTimeout(() => {
  261. this.aniMaskShow = true;
  262. }, 50);
  263. });
  264. },
  265. /**
  266. * 关闭日历弹窗
  267. */
  268. close() {
  269. this.aniMaskShow = false;
  270. this.$nextTick(() => {
  271. setTimeout(() => {
  272. this.show = false;
  273. this.$emit("close");
  274. }, 300);
  275. });
  276. },
  277. /**
  278. * 确认按钮
  279. */
  280. confirm() {
  281. this.setEmit("confirm");
  282. this.close();
  283. },
  284. /**
  285. * 变化触发
  286. */
  287. change() {
  288. if (!this.insert) return;
  289. this.setEmit("change");
  290. },
  291. /**
  292. * 选择月份触发
  293. */
  294. monthSwitch() {
  295. let { year, month } = this.nowDate;
  296. this.$emit("monthSwitch", {
  297. year,
  298. month: Number(month),
  299. });
  300. },
  301. /**
  302. * 派发事件
  303. * @param {Object} name
  304. */
  305. setEmit(name) {
  306. let { year, month, date, fullDate, lunar, extraInfo } = this.calendar;
  307. this.$emit(name, {
  308. range: this.cale.multipleStatus,
  309. year,
  310. month,
  311. date,
  312. fulldate: fullDate,
  313. lunar,
  314. extraInfo: extraInfo || {},
  315. });
  316. },
  317. /**
  318. * 选择天触发
  319. * @param {Object} weeks
  320. */
  321. choiceDate(weeks) {
  322. if (weeks.disable) return;
  323. this.calendar = weeks;
  324. // 设置多选
  325. this.cale.setMultiple(this.calendar.fullDate);
  326. this.weeks = this.cale.weeks;
  327. this.change();
  328. },
  329. /**
  330. * 回到今天
  331. */
  332. backtoday() {
  333. console.log(this.cale.getDate(new Date()).fullDate);
  334. let date = this.cale.getDate(new Date()).fullDate;
  335. // this.cale.setDate(date)
  336. this.init(date);
  337. this.change();
  338. },
  339. /**
  340. * 上个月
  341. */
  342. pre() {
  343. const preDate = this.cale.getDate(this.nowDate.fullDate, -1, "month").fullDate;
  344. this.setDate(preDate);
  345. this.monthSwitch();
  346. },
  347. /**
  348. * 下个月
  349. */
  350. next() {
  351. const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, "month").fullDate;
  352. this.setDate(nextDate);
  353. this.monthSwitch();
  354. },
  355. /**
  356. * 设置日期
  357. * @param {Object} date
  358. */
  359. setDate(date) {
  360. this.cale.setDate(date);
  361. this.weeks = this.cale.weeks;
  362. this.nowDate = this.cale.getInfo(date);
  363. },
  364. /**
  365. * @触摸开始
  366. **/
  367. touchStart(e) {
  368. console.log("触摸开始");
  369. touchStartX = e.touches[0].clientX;
  370. touchStartY = e.touches[0].clientY;
  371. },
  372. /**
  373. * @触摸结束
  374. **/
  375. touchEnd(e) {
  376. console.log("触摸结束");
  377. let deltaX = e.changedTouches[0].clientX - touchStartX;
  378. let deltaY = e.changedTouches[0].clientY - touchStartY;
  379. if (Math.abs(deltaX) > 50 && Math.abs(deltaX) > Math.abs(deltaY)) {
  380. if (deltaX >= 0) {
  381. console.log("左滑");
  382. this.pre();
  383. // calendar.value.selectDate("prev-month");
  384. } else {
  385. console.log("右滑");
  386. this.next();
  387. // calendar.value.selectDate("next-month");
  388. }
  389. } else if (Math.abs(deltaY) > 50 && Math.abs(deltaX) < Math.abs(deltaY)) {
  390. if (deltaY < 0) {
  391. console.log("上滑");
  392. } else {
  393. console.log("下滑");
  394. }
  395. } else {
  396. console.log("可能是误触!");
  397. }
  398. },
  399. },
  400. };
  401. </script>
  402. <style lang="scss" scoped>
  403. $uni-bg-color-mask: rgba(
  404. $color: #000000,
  405. $alpha: 0.4,
  406. );
  407. $uni-border-color: #ededed;
  408. $uni-text-color: #333;
  409. $uni-bg-color-hover: #f1f1f1;
  410. $uni-font-size-base: 14px;
  411. $uni-text-color-placeholder: #808080;
  412. $uni-color-subtitle: #555555;
  413. $uni-text-color-grey: #999;
  414. .uni-calendar {
  415. /* #ifndef APP-NVUE */
  416. display: flex;
  417. /* #endif */
  418. flex-direction: column;
  419. }
  420. .uni-calendar__mask {
  421. position: fixed;
  422. bottom: 0;
  423. top: 0;
  424. left: 0;
  425. right: 0;
  426. background-color: $uni-bg-color-mask;
  427. transition-property: opacity;
  428. transition-duration: 0.3s;
  429. opacity: 0;
  430. /* #ifndef APP-NVUE */
  431. z-index: 99;
  432. /* #endif */
  433. }
  434. .uni-calendar--mask-show {
  435. opacity: 1;
  436. }
  437. .uni-calendar--fixed {
  438. position: fixed;
  439. /* #ifdef APP-NVUE */
  440. bottom: 0;
  441. /* #endif */
  442. left: 0;
  443. right: 0;
  444. transition-property: transform;
  445. transition-duration: 0.3s;
  446. transform: translateY(460px);
  447. /* #ifndef APP-NVUE */
  448. bottom: calc(var(--window-bottom));
  449. z-index: 99;
  450. /* #endif */
  451. }
  452. .uni-calendar--ani-show {
  453. transform: translateY(0);
  454. }
  455. .uni-calendar__content {
  456. background-color: #fff;
  457. }
  458. .uni-calendar__header {
  459. position: relative;
  460. /* #ifndef APP-NVUE */
  461. display: flex;
  462. /* #endif */
  463. flex-direction: row;
  464. // justify-content: center;
  465. align-items: center;
  466. height: 50px;
  467. padding: 0 15px;
  468. border-bottom-color: $uni-border-color;
  469. border-bottom-style: solid;
  470. border-bottom-width: 1px;
  471. }
  472. .uni-calendar--fixed-top {
  473. /* #ifndef APP-NVUE */
  474. display: flex;
  475. /* #endif */
  476. flex-direction: row;
  477. justify-content: space-between;
  478. border-top-color: $uni-border-color;
  479. border-top-style: solid;
  480. border-top-width: 1px;
  481. }
  482. .uni-calendar--fixed-width {
  483. width: 50px;
  484. // padding: 0 15px;
  485. }
  486. .uni-calendar__backtoday {
  487. position: absolute;
  488. right: 0;
  489. top: 25rpx;
  490. padding: 0 5px;
  491. padding-left: 10px;
  492. height: 25px;
  493. line-height: 25px;
  494. font-size: 12px;
  495. border-top-left-radius: 25px;
  496. border-bottom-left-radius: 25px;
  497. color: $uni-text-color;
  498. background-color: $uni-bg-color-hover;
  499. }
  500. .uni-calendar__header-text {
  501. text-align: center;
  502. width: 100px;
  503. font-size: $uni-font-size-base;
  504. color: $uni-text-color;
  505. }
  506. .uni-calendar__header-btn-box {
  507. /* #ifndef APP-NVUE */
  508. display: flex;
  509. /* #endif */
  510. flex-direction: row;
  511. align-items: center;
  512. justify-content: center;
  513. width: 50px;
  514. height: 50px;
  515. }
  516. .uni-calendar__header-btn {
  517. width: 10px;
  518. height: 10px;
  519. border-left-color: $uni-text-color-placeholder;
  520. border-left-style: solid;
  521. border-left-width: 2px;
  522. border-top-color: $uni-color-subtitle;
  523. border-top-style: solid;
  524. border-top-width: 2px;
  525. }
  526. .uni-calendar--left {
  527. transform: rotate(-45deg);
  528. }
  529. .uni-calendar--right {
  530. transform: rotate(135deg);
  531. }
  532. .uni-calendar__weeks {
  533. position: relative;
  534. /* #ifndef APP-NVUE */
  535. display: flex;
  536. /* #endif */
  537. flex-direction: row;
  538. }
  539. .uni-calendar__weeks-item {
  540. flex: 1;
  541. }
  542. .uni-calendar__weeks-day {
  543. flex: 1;
  544. /* #ifndef APP-NVUE */
  545. display: flex;
  546. /* #endif */
  547. flex-direction: column;
  548. justify-content: center;
  549. align-items: center;
  550. height: 45px;
  551. border-bottom-color: #f5f5f5;
  552. border-bottom-style: solid;
  553. border-bottom-width: 1px;
  554. }
  555. .uni-calendar__weeks-day-text {
  556. font-size: 14px;
  557. }
  558. .uni-calendar__box {
  559. position: relative;
  560. }
  561. .uni-calendar__box-bg {
  562. /* #ifndef APP-NVUE */
  563. display: flex;
  564. /* #endif */
  565. justify-content: center;
  566. align-items: center;
  567. position: absolute;
  568. top: 0;
  569. left: 0;
  570. right: 0;
  571. bottom: 0;
  572. }
  573. .uni-calendar__box-bg-text {
  574. font-size: 200px;
  575. font-weight: bold;
  576. color: $uni-text-color-grey;
  577. opacity: 0.1;
  578. text-align: center;
  579. /* #ifndef APP-NVUE */
  580. line-height: 1;
  581. /* #endif */
  582. }
  583. </style>