index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <template>
  2. <view class="calendar" v-if="visible">
  3. <view class="calendar-mask" @click.stop='maskEvent'></view>
  4. <view class="calendar-container" @click.stop='handleClickOutSide'>
  5. <view class="calendar-header" :style="{backgroundColor: headerBgColor}">
  6. <view class="txt solar" :style="{color: (isLunar ? defaultColor : activeColor)}" @click="changeCalendar(0)">
  7. {{solarText}}
  8. </view>
  9. <view class="txt lunar" :style="{color: (!isLunar ? defaultColor : activeColor)}" @click="changeCalendar(1)">
  10. {{lunarText}}
  11. </view>
  12. <view class="txt confirm" :style="{color: confirmColor}" @click="changeCalendar(2)">
  13. {{confirmText}}
  14. </view>
  15. </view>
  16. <view class="calendar-body">
  17. <picker-view @change="timeChange" data-index='0' data-id='year' :value='[multiIndex[0]]' indicator-style="height: 40px;"
  18. class='calendar-body-wrap' v-if="level > 0 ">
  19. <picker-view-column>
  20. <view v-for="item in yearData" :key='item' style="line-height: 40px;text-align:center;">{{item}}</view>
  21. </picker-view-column>
  22. </picker-view>
  23. <picker-view @change="timeChange" data-index='1' data-id='month' :value='[multiIndex[1]]' indicator-style="height: 40px;"
  24. class='calendar-body-wrap' v-if="level> 1">
  25. <picker-view-column>
  26. <view v-for="item in monthData" :key='item' style="line-height: 40px;text-align:center;">{{item}}</view>
  27. </picker-view-column>
  28. </picker-view>
  29. <picker-view @change="timeChange" data-index='2' data-id='day' :value='[multiIndex[2]]' indicator-style="height: 40px;"
  30. class='calendar-body-wrap' v-if="level > 2 ">
  31. <picker-view-column>
  32. <view v-for="item in dayData" :key='item' style="line-height: 40px;text-align:center;">{{item}}</view>
  33. </picker-view-column>
  34. </picker-view>
  35. <picker-view @change="timeChange" data-index='3' data-id='hour' :value='[multiIndex[3]]' indicator-style="height: 40px;"
  36. class='calendar-body-wrap' v-if="level > 3 && (!isLunar || isLunar && !isShowChinaTime)">
  37. <picker-view-column>
  38. <view v-for="item in hours" :key='item' style="line-height: 40px;text-align:center;">{{item}}时</view>
  39. </picker-view-column>
  40. </picker-view>
  41. <picker-view @change="timeChange" data-index='4' data-id='minus' :value='[multiIndex[4]]' indicator-style="height: 40px;"
  42. class='calendar-body-wrap' v-if="level > 4 && (!isLunar || isLunar && !isShowChinaTime)">
  43. <picker-view-column>
  44. <view v-for="item in minutes" :key='item' style="line-height: 40px;text-align:center;">{{item}}分</view>
  45. </picker-view-column>
  46. </picker-view>
  47. <picker-view @change="timeChange" data-index='5' data-id='second' :value='[multiIndex[5]]' indicator-style="height: 40px;"
  48. class='calendar-body-wrap' v-if="level > 5 && (!isLunar || isLunar && !isShowChinaTime)">
  49. <picker-view-column>
  50. <view v-for="item in seconds" :key='item' style="line-height: 40px;text-align:center;">{{item}}秒</view>
  51. </picker-view-column>
  52. </picker-view>
  53. <picker-view @change="timeChange" data-index='6' data-id='china' :value='[multiIndex[6]]' indicator-style="height: 40px;"
  54. class='calendar-body-wrap' v-if="isLunar && isShowChinaTime">
  55. <picker-view-column>
  56. <view v-for="item in chinaHours" :key='item.startTime' style="line-height: 40px;text-align:center;">{{item.name}}</view>
  57. </picker-view-column>
  58. </picker-view>
  59. </view>
  60. </view>
  61. </view>
  62. </template>
  63. <script>
  64. import clickoutside from '../directives/clickoutside.js';
  65. import {
  66. calendar
  67. } from '../libs/calendar.js'
  68. import {
  69. getYears,
  70. getMonths,
  71. getDays,
  72. getHours,
  73. getMinutes,
  74. getSeconds,
  75. getDateTime,
  76. formateTime,
  77. addZero,
  78. createArray,
  79. getDateStringTime,
  80. chinaHours,
  81. getChinaTime
  82. } from '../utils/util.js';
  83. export default {
  84. name: 'CalendarPicker',
  85. props: {
  86. level: {
  87. type: String,
  88. default: '5'
  89. },
  90. isShowChinaTime: {
  91. type: Boolean,
  92. default: false
  93. },
  94. currentTime: {
  95. type: String,
  96. default: ''
  97. },
  98. startYear: {
  99. type: Number,
  100. default: 1900
  101. },
  102. endYear: {
  103. type: Number,
  104. default: 2100
  105. },
  106. isLunar: {
  107. type: Boolean,
  108. default: false
  109. },
  110. headerBgColor: {
  111. type: String,
  112. default: '#e9e9e9'
  113. },
  114. solarText: {
  115. type: String,
  116. default: '阳历'
  117. },
  118. lunarText: {
  119. type: String,
  120. default: '阴历'
  121. },
  122. activeColor: {
  123. type: String,
  124. default: '#912222'
  125. },
  126. defaultColor: {
  127. type: String,
  128. default: '#000000'
  129. },
  130. confirmText: {
  131. type: String,
  132. default: '确定'
  133. },
  134. confirmColor: {
  135. type: String,
  136. default: '#912222'
  137. }
  138. },
  139. computed:{
  140. yearData(){
  141. return this.isLunar ? this.ganZhiYears : this.years;
  142. },
  143. monthData(){
  144. return this.isLunar ? this.ganZhiMonths : this.months;
  145. },
  146. dayData(){
  147. return this.isLunar ? this.ganZhiDays : this.days;
  148. }
  149. },
  150. watch: {
  151. currentTime: {
  152. handler(value) {
  153. if (value != '') {
  154. this.years = getYears(this.startYear, this.endYear);
  155. this.months = getMonths();
  156. this.hours = getHours();
  157. this.minutes = getMinutes();
  158. this.seconds = getSeconds();
  159. const selectTime = value.split(' ')[1];
  160. const timeArray = selectTime.split(':');
  161. const dateArray = value.split(' ')[0].split('-');
  162. this.days = getDays(dateArray[0], dateArray[1]);
  163. const dateTimeArray = dateArray.concat(timeArray);
  164. this.$nextTick(() => {
  165. this.selectYear = dateArray[0];
  166. this.selectMonth = dateArray[1];
  167. this.selectDay = dateArray[2];
  168. const names = ['year', 'month', 'day', 'hour', 'minute', 'second', 'china'];
  169. let multiIndex = [];
  170. names.forEach((name, i) => {
  171. if (name == 'china') {
  172. multiIndex.push(0);
  173. } else {
  174. const index = this[name + 's'].findIndex(item => item == dateTimeArray[i]);
  175. multiIndex.push(index);
  176. }
  177. this.multiIndex = multiIndex;
  178. })
  179. if (this.isLunar) {
  180. if (this.isShowChinaTime) {
  181. const time =
  182. `${this.hours[this.multiIndex[3]]}:${this.minutes[this.multiIndex[4]]}:${this.seconds[this.multiIndex[5]]}`;
  183. const {
  184. name
  185. } = getChinaTime(time);
  186. const index = this.chinaHours.findIndex(item => item.name == name);
  187. this.multiIndex.splice(this.multiIndex.length - 1, 1, index)
  188. }
  189. this.getLunarDate()
  190. }
  191. })
  192. }
  193. },
  194. immediate: true
  195. }
  196. },
  197. data() {
  198. return {
  199. chinaHours,
  200. multiIndex: [],
  201. selectYear: '',
  202. selectMonth: '',
  203. selectDay: '',
  204. selectTime: '',
  205. isLeap: false,
  206. visible: false,
  207. years: [],
  208. months: [],
  209. days: [],
  210. hours: [],
  211. minutes: [],
  212. seconds: [],
  213. ganZhiYears: [],
  214. ganZhiMonths: [],
  215. ganZhiDays: []
  216. }
  217. },
  218. methods: {
  219. handleClickOutSide() {
  220. return;
  221. },
  222. maskEvent() {
  223. this.hide()
  224. },
  225. show() {
  226. this.visible = true;
  227. },
  228. hide() {
  229. this.visible = false;
  230. },
  231. getLunarDate() {
  232. const lunarData = calendar.solar2lunar(this.selectYear, this.selectMonth, this.selectDay);
  233. console.log('lunarData:', lunarData);
  234. const {
  235. lYear,
  236. lMonth,
  237. lDay,
  238. isLeap,
  239. IMonthCn,
  240. IDayCn
  241. } = lunarData;
  242. this.selectYear = lYear;
  243. this.selectMonth = lMonth;
  244. this.selectDay = lDay;
  245. this.isLeap = isLeap;
  246. this.ganZhiYears = this.years.map(year => `${year}${calendar.getGanZhiYear(year)}`);
  247. const yearIndex = this.ganZhiYears.findIndex(item => item.indexOf(lYear) > -1);
  248. this.multiIndex.splice(0, 1, yearIndex);
  249. const leapMonth = calendar.leapMonth(lYear);
  250. console.log('leapMonth:', leapMonth)
  251. this.ganZhiMonths = this.months.map(month => calendar.toChinaMonth(month));
  252. let monthCount;
  253. if (leapMonth > 0) { //所选的年有闰年
  254. this.ganZhiMonths.splice(leapMonth, 0, `闰${calendar.toChinaMonth(leapMonth)}`);
  255. monthCount = calendar.leapDays(lYear);
  256. } else {
  257. monthCount = calendar.monthDays(lYear, lMonth)
  258. }
  259. const monthIndex = this.ganZhiMonths.findIndex(item => item == IMonthCn);
  260. this.multiIndex.splice(1, 1, monthIndex);
  261. const ganZhiDays = [];
  262. for (let i = 1; i <= monthCount; i++) {
  263. ganZhiDays.push(calendar.toChinaDay(i));
  264. }
  265. this.ganZhiDays = ganZhiDays;
  266. const dayIndex = this.ganZhiDays.findIndex(item => item == IDayCn);
  267. this.multiIndex.splice(2, 1, dayIndex);
  268. if (this.isShowChinaTime) {
  269. const time =
  270. `${this.hours[this.multiIndex[3]]}:${this.minutes[this.multiIndex[4]]}:${this.seconds[this.multiIndex[5]]}`;
  271. const {
  272. name
  273. } = getChinaTime(time);
  274. const index = this.chinaHours.findIndex(item => item.name == name);
  275. this.multiIndex.splice(this.multiIndex.length - 1, 1, index)
  276. }
  277. },
  278. getSolarDate() {
  279. const solarData = calendar.lunar2solar(this.selectYear, this.selectMonth, this.selectDay, this.isLeap);
  280. console.log('solarData:', solarData);
  281. const {
  282. cYear,
  283. cMonth,
  284. cDay,
  285. lYear,
  286. lMonth,
  287. lDay,
  288. isLeap,
  289. IMonthCn,
  290. IDayCn
  291. } = solarData;
  292. this.selectYear = cYear;
  293. this.selectMonth = cMonth;
  294. this.selectDay = cDay;
  295. this.isLeap = isLeap;
  296. const yearIndex = this.years.findIndex(item => item == cYear);
  297. const monthIndex = this.months.findIndex(item => item == cMonth);
  298. const dayIndex = this.days.findIndex(item => item == cDay);
  299. this.multiIndex.splice(0, 1, yearIndex);
  300. this.multiIndex.splice(1, 1, monthIndex);
  301. this.multiIndex.splice(2, 1, dayIndex);
  302. },
  303. timeChange(e) {
  304. const {
  305. id,
  306. index
  307. } = e.target.dataset;
  308. console.log('++++++',id,index)
  309. const scrollIndex = e.detail.value[0];
  310. this.multiIndex.splice(index, 1, scrollIndex);
  311. const names = ['Year', 'Month', 'Day'];
  312. if (!this.isLunar) {
  313. if (index == 1) {
  314. this.selectMonth = addZero(scrollIndex + 1) + '';
  315. this.days = getDays(this.selectYear, this.selectMonth);
  316. if (this.multiIndex[index + 1] + 1 > this.days.length) {
  317. this.multiIndex.splice(index + 1, 1, this.days.length - 1);
  318. this.selectDay = addZero(this.days.length);
  319. }
  320. } else if (index == 0) {
  321. this.selectYear = this.years[scrollIndex];
  322. } else {
  323. this['select' + names[index]] = addZero(scrollIndex + 1) + '';
  324. }
  325. this.selectTime = `${addZero(this.multiIndex[3]+1)}:${addZero(this.multiIndex[4]+1)}:${addZero(this.multiIndex[5]+1)}`;
  326. this.$nextTick(() => {
  327. this.selectDate = `${this.selectYear}-${this.selectMonth}-${this.selectDay} ${this.selectTime}`;
  328. console.log('selectDate', this.selectDate)
  329. })
  330. } else {
  331. if (index == 0) {
  332. this.selectYear = Number(this.ganZhiYears[this.multiIndex[0]].slice(0, 4));
  333. const leapMonth = calendar.leapMonth(this.selectYear);
  334. console.log('leapMonth:', leapMonth)
  335. this.ganZhiMonths = this.months.map(month => calendar.toChinaMonth(month));
  336. let monthCount, monthIndex;
  337. if (leapMonth > 0) { //所选的年有闰年
  338. this.ganZhiMonths.splice(leapMonth, 0, `闰${calendar.toChinaMonth(leapMonth)}`);
  339. }
  340. if (this.multiIndex[1] + 1 > this.ganZhiMonths.length) {
  341. this.multiIndex.splice(1, 1, this.ganZhiMonths.length - 1);
  342. }
  343. const month = this.ganZhiMonths[this.multiIndex[1]];
  344. monthIndex = this.ganZhiMonths.findIndex(item => item == month);
  345. if (leapMonth > 0) {
  346. console.log('======', )
  347. if (monthIndex == leapMonth) {
  348. this.selectMonth = monthIndex;
  349. this.isLeap = true;
  350. monthCount = calendar.leapDays(this.selectYear);
  351. } else {
  352. if (monthIndex > leapMonth) {
  353. this.selectMonth = monthIndex
  354. } else {
  355. this.selectMonth = monthIndex + 1;
  356. }
  357. this.isLeap = false;
  358. monthCount = calendar.monthDays(this.selectYear, this.selectMonth)
  359. }
  360. } else {
  361. this.selectMonth = monthIndex + 1;
  362. this.isLeap = false;
  363. monthCount = calendar.monthDays(this.selectYear, this.selectMonth)
  364. }
  365. const ganZhiDays = [];
  366. for (let i = 1; i <= monthCount; i++) {
  367. ganZhiDays.push(calendar.toChinaDay(i));
  368. }
  369. this.ganZhiDays = ganZhiDays;
  370. if (this.multiIndex[2] + 1 > this.ganZhiDays.length) {
  371. this.multiIndex.splice(2, 1, this.ganZhiDays.length - 1);
  372. }
  373. } else if (index == 1) {
  374. const leapMonth = calendar.leapMonth(this.selectYear);
  375. console.log('leapMonth:', leapMonth)
  376. let monthCount, monthIndex;
  377. const month = this.ganZhiMonths[this.multiIndex[1]];
  378. monthIndex = this.ganZhiMonths.findIndex(item => item == month);
  379. if (leapMonth > 0) {
  380. if (monthIndex == leapMonth) {
  381. this.selectMonth = monthIndex;
  382. this.isLeap = true;
  383. monthCount = calendar.leapDays(this.selectYear);
  384. } else {
  385. if (monthIndex > leapMonth) {
  386. this.selectMonth = monthIndex
  387. } else {
  388. this.selectMonth = monthIndex + 1;
  389. }
  390. this.isLeap = false;
  391. monthCount = calendar.monthDays(this.selectYear, this.selectMonth)
  392. }
  393. } else {
  394. this.selectMonth = monthIndex + 1;
  395. this.isLeap = false;
  396. monthCount = calendar.monthDays(this.selectYear, this.selectMonth)
  397. }
  398. const ganZhiDays = [];
  399. for (let i = 1; i <= monthCount; i++) {
  400. ganZhiDays.push(calendar.toChinaDay(i));
  401. }
  402. this.ganZhiDays = ganZhiDays;
  403. if (this.multiIndex[2] + 1 > this.ganZhiDays.length) {
  404. this.multiIndex.splice(2, 1, this.ganZhiDays.length - 1);
  405. }
  406. } else if (index == 2) {
  407. this.selectDay = this.multiIndex[2] + 1;
  408. }else if(index == 6){
  409. const { startTime } = this.chinaHours[scrollIndex];
  410. this.selectTime = startTime;
  411. }
  412. }
  413. },
  414. changeCalendar(index) {
  415. if (index == 0) { //阳历
  416. this.$emit('changecalendar', false);
  417. this.getSolarDate();
  418. } else if (index == 1) { //阴历
  419. this.$emit('changecalendar', true);
  420. this.getLunarDate()
  421. } else if (index == 2) { //确定
  422. let selectDate;
  423. if (this.isLunar) {
  424. selectDate = calendar.lunar2solar(this.selectYear, this.selectMonth, this.selectDay, this.isLeap);
  425. } else {
  426. selectDate = calendar.solar2lunar(this.selectYear, this.selectMonth, this.selectDay);
  427. }
  428. this.hide();
  429. selectDate['date'] = getDateStringTime(selectDate['date']);
  430. selectDate['lunarDate'] = getDateStringTime(selectDate['lunarDate']);
  431. let time;
  432. if(this.isShowChinaTime){
  433. const { startTime,name } = this.chinaHours[this.multiIndex[6]];
  434. selectDate['chinaTime'] = name;
  435. time = startTime;
  436. }else{
  437. time =
  438. `${this.hours[this.multiIndex[3]]}:${this.minutes[this.multiIndex[4]]}:${this.seconds[this.multiIndex[5]]}`;
  439. selectDate['chinaTime'] = getChinaTime(time) && getChinaTime(time)['name'];
  440. }
  441. this.$emit('confirm', { ...selectDate,
  442. time
  443. })
  444. }
  445. }
  446. },
  447. mounted() {
  448. }
  449. }
  450. </script>
  451. <style lang="scss" scoped>
  452. $zIndex: 1000;
  453. .calendar {
  454. .calendar-mask {
  455. position: fixed;
  456. top: 0;
  457. left: 0;
  458. right: 0;
  459. bottom: 0;
  460. background-color: rgba(0, 0, 0, .5);
  461. z-index: $zIndex;
  462. }
  463. .calendar-container {
  464. position: absolute;
  465. bottom: 0;
  466. left: 0;
  467. right: 0;
  468. height: 500rpx;
  469. background-color: #fff;
  470. z-index: $zIndex+1;
  471. display: flex;
  472. flex-direction: column;
  473. .calendar-header {
  474. display: flex;
  475. justify-content: space-between;
  476. align-items: center;
  477. height: 80rpx;
  478. box-sizing: border-box;
  479. padding: 0 32rpx;
  480. .txt {
  481. flex: 1;
  482. font-size: 32rpx;
  483. &.solar {
  484. text-align: left;
  485. }
  486. &.lunar {
  487. text-align: center;
  488. }
  489. &.confirm {
  490. text-align: right;
  491. cursor: pointer;
  492. }
  493. }
  494. }
  495. .calendar-body {
  496. flex: 1;
  497. display: flex;
  498. .calendar-body-wrap {
  499. flex: 1;
  500. font-size: 26rpx;
  501. }
  502. }
  503. }
  504. }
  505. </style>