|
@@ -0,0 +1,276 @@
|
|
|
+<script setup>
|
|
|
+import { reactive, toRefs, ref, watch, onMounted } from 'vue';
|
|
|
+import moment from 'moment';
|
|
|
+import _ from 'lodash';
|
|
|
+
|
|
|
+const emit = defineEmits(['dateChange']);
|
|
|
+
|
|
|
+const calendarRef = ref(null);
|
|
|
+
|
|
|
+const weekDays = ref([
|
|
|
+ {
|
|
|
+ label: '一',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '二',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '三',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '四',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '五',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '六',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '日',
|
|
|
+ },
|
|
|
+]);
|
|
|
+
|
|
|
+/* 当前 */
|
|
|
+const current = ref({
|
|
|
+ currentDate: moment().format('YYYY-MM-DD'), //今日
|
|
|
+ isTouch: false, //控制滑动动画
|
|
|
+ touchStartX: 0, //判断滑动位置
|
|
|
+});
|
|
|
+
|
|
|
+const state = reactive({
|
|
|
+ weekFirstDay: null, //周第一天
|
|
|
+ weekDateList: null, //最近三周日期数组
|
|
|
+ selectDate: current.value.currentDate, //当前选中的日期
|
|
|
+ moveIndex: 0, //滑动的位置
|
|
|
+ touch: { x: 0 },
|
|
|
+ weekIndex: 1, //周日历选中的index
|
|
|
+});
|
|
|
+
|
|
|
+/* 获取日期数组 */
|
|
|
+const getRecentWeek = (day) => {
|
|
|
+ const currenWeekStart = moment(day).startOf('isoweek'); //本周一
|
|
|
+ const prevWeekStart = moment(day).subtract(1, 'week').startOf('isoWeek'); //上周一
|
|
|
+ const nextWeekStart = moment(day).add(1, 'week').startOf('isoWeek'); //下周一
|
|
|
+
|
|
|
+ const weekeList = [
|
|
|
+ new Array(7)
|
|
|
+ .fill('')
|
|
|
+ .map((_, index) =>
|
|
|
+ moment(prevWeekStart).add(index, 'day').format('YYYY-MM-DD')
|
|
|
+ ),
|
|
|
+ new Array(7)
|
|
|
+ .fill('')
|
|
|
+ .map((_, index) =>
|
|
|
+ moment(currenWeekStart).add(index, 'day').format('YYYY-MM-DD')
|
|
|
+ ),
|
|
|
+ new Array(7)
|
|
|
+ .fill('')
|
|
|
+ .map((_, index) =>
|
|
|
+ moment(nextWeekStart).add(index, 'day').format('YYYY-MM-DD')
|
|
|
+ ),
|
|
|
+ ];
|
|
|
+
|
|
|
+ // console.log(weekeList);
|
|
|
+
|
|
|
+ state.weekDateList = weekeList;
|
|
|
+ state.weekFirstDay = currenWeekStart;
|
|
|
+};
|
|
|
+getRecentWeek(current.value.currentDate);
|
|
|
+
|
|
|
+/* 设置周日历选中的位置 */
|
|
|
+const setWeekIndex = (selectDate, list) => {
|
|
|
+ const currentWeekDateList = list[1];
|
|
|
+ let weekIndex = 0;
|
|
|
+ currentWeekDateList.forEach((item, index) => {
|
|
|
+ if (item === selectDate) weekIndex = index;
|
|
|
+ });
|
|
|
+
|
|
|
+ state.weekIndex = weekIndex;
|
|
|
+};
|
|
|
+
|
|
|
+setWeekIndex(current.value.currentDate,state.weekDateList);
|
|
|
+
|
|
|
+/* 选择天 */
|
|
|
+const selectDateHandle = (date,index) => {
|
|
|
+
|
|
|
+ state.weekIndex = index;
|
|
|
+ state.selectDate = date;
|
|
|
+ state.touch = { x: 0 };
|
|
|
+};
|
|
|
+
|
|
|
+/* 滑动开始 */
|
|
|
+const touchStartHandle = (e) => {
|
|
|
+ // e.preventDefault();
|
|
|
+
|
|
|
+ const { clientX } = e.touches[0];
|
|
|
+ current.value.touchStartX = clientX;
|
|
|
+ current.value.isTouch = true;
|
|
|
+}
|
|
|
+
|
|
|
+/* 滑动中 */
|
|
|
+const touchMoveHandle = _.throttle((e) => {
|
|
|
+ // e.preventDefault();
|
|
|
+ const { clientX } = e.touches[0];
|
|
|
+ const moveX = clientX - current.value.touchStartX;
|
|
|
+ const calendarWidth = calendarRef.value.offsetWidth;
|
|
|
+ // console.log(current.value.touchStartX,clientX)
|
|
|
+
|
|
|
+ state.touch = { x: moveX / calendarWidth };
|
|
|
+},50)
|
|
|
+
|
|
|
+//滑动结束
|
|
|
+const touchEndHandle = (e) => {
|
|
|
+
|
|
|
+ const touchX = state.touch.x;
|
|
|
+ const newTranslateIndex = touchX > 0 ? state.moveIndex + 1 : state.moveIndex - 1;
|
|
|
+
|
|
|
+ if( Math.abs(touchX) > 0 ) {
|
|
|
+ const nextWeekFirstDay = touchX > 0
|
|
|
+ ? moment(state.selectDate)
|
|
|
+ .subtract(1, 'week')
|
|
|
+ .startOf('isoWeek')
|
|
|
+ .format('YYYY-MM-DD')
|
|
|
+ : moment(state.selectDate)
|
|
|
+ .add(1, 'week')
|
|
|
+ .startOf('isoWeek')
|
|
|
+ .format('YYYY-MM-DD');
|
|
|
+ getRecentWeek(nextWeekFirstDay);
|
|
|
+
|
|
|
+ state.moveIndex = newTranslateIndex;
|
|
|
+
|
|
|
+ //默认选中
|
|
|
+ const currentWeekDays = state.weekDateList[1];
|
|
|
+ const selectWeekDay = currentWeekDays[state.weekIndex]
|
|
|
+ state.selectDate = selectWeekDay;
|
|
|
+ state.touch = { x: 0 }
|
|
|
+
|
|
|
+ current.value.isTouch = false;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/* 选中日期 更新日历 */
|
|
|
+watch(
|
|
|
+ () => state.selectDate,
|
|
|
+ (newval) => {
|
|
|
+ emit('dateChange', newval);
|
|
|
+ },
|
|
|
+ {
|
|
|
+ deep: true,
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+const { weekDateList, selectDate, moveIndex, touch } =
|
|
|
+ toRefs(state);
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <ul class="weekend-ul">
|
|
|
+ <li v-for="item in weekDays">
|
|
|
+ <span>{{ item.label }}</span>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="swiper-wrapper"
|
|
|
+ ref="calendarRef"
|
|
|
+ @touchstart.stop.prevent="touchStartHandle"
|
|
|
+ @touchmove.stop.prevent="touchMoveHandle"
|
|
|
+ @touchend.prevent="touchEndHandle"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="swiper-move"
|
|
|
+ :style="`transform:translateX(${-moveIndex * 100}%)`"
|
|
|
+ >
|
|
|
+ <ul
|
|
|
+ class="days-ul"
|
|
|
+ v-for="(week, index) in weekDateList"
|
|
|
+ :key="index"
|
|
|
+ :style="`transform: translateX(${
|
|
|
+ (index - 1 + moveIndex + (current.isTouch ? touch.x : 0)) * 100
|
|
|
+ }%)`"
|
|
|
+ >
|
|
|
+ <li
|
|
|
+ v-for="(day, day_index) in week"
|
|
|
+ :key="`${(index, day_index)}`"
|
|
|
+ :class="{ act: selectDate === day }"
|
|
|
+ @touchstart.stop="selectDateHandle(day,day_index)"
|
|
|
+ >
|
|
|
+ {{ moment().format('YYYY-MM-DD') === day ? '今' : moment(day).format('DD') }}
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.weekend-ul {
|
|
|
+ display: flex;
|
|
|
+ li {
|
|
|
+ width: calc(100% / 7);
|
|
|
+ text-align: center;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.swiper-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ height: 80px;
|
|
|
+ transition: 0.15s;
|
|
|
+ overflow: hidden;
|
|
|
+ .swiper-move {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ .days-ul {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ padding: 20px 0 0;
|
|
|
+ flex-shrink: 0;
|
|
|
+ transition: all 0.5s;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 11;
|
|
|
+ li {
|
|
|
+ color: #333;
|
|
|
+ height: 40px;
|
|
|
+ line-height: 40px;
|
|
|
+ width: calc(100% / 7);
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px 0;
|
|
|
+ margin: 0 28px;
|
|
|
+ &.act {
|
|
|
+ background: #39a9ed;
|
|
|
+ border-radius: 4px;
|
|
|
+ /* border-radius: 50%; */
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.swiper-cont {
|
|
|
+ .swiper-item {
|
|
|
+ .days-ul {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ padding: 20px 0;
|
|
|
+ li {
|
|
|
+ color: #333;
|
|
|
+ font-weight: bold;
|
|
|
+ padding: 18px 10px;
|
|
|
+ &.act {
|
|
|
+ background: #39a9ed;
|
|
|
+ border-radius: 4px;
|
|
|
+ /* border-radius: 50%; */
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|