Browse Source

滚动日历组件封装;
研究员日历布局

Karsa 3 years ago
parent
commit
46a6dc62b3

+ 5 - 0
package.json

@@ -8,9 +8,14 @@
     "serve": "vite preview --port 3001"
   },
   "dependencies": {
+    "@fullcalendar/core": "^5.10.1",
+    "@fullcalendar/interaction": "^5.10.1",
+    "@fullcalendar/timegrid": "^5.10.1",
+    "@fullcalendar/vue3": "^5.10.1",
     "@vant/touch-emulator": "^1.3.2",
     "axios": "^0.24.0",
     "highcharts": "^9.3.2",
+    "lodash": "^4.17.21",
     "moment": "^2.29.1",
     "normalize.css": "^8.0.1",
     "vant": "^3.3.4",

BIN
src/assets/ssbg/calendar_ico.png


BIN
src/assets/ssbg/choose_ico.png


+ 1 - 0
src/main.js

@@ -5,4 +5,5 @@ import 'normalize.css'
 import './style/common.scss'
 import '@vant/touch-emulator';//vant 
 
+
 createApp(App).use(router).mount('#app')

+ 4 - 0
src/router/index.js

@@ -2,10 +2,14 @@ import { createRouter, createWebHistory } from 'vue-router'
 import {hzybRoutes} from './hzyb/index'// 弘则研报小程序路由
 import {cygxRoutes} from './cygx/index'// 查研观向小程序路由
 import {hzslRoutes} from './hzsl/index'// 弘则思路小程序路由
+import { ssbgRoutes } from './ssbg';//随手办公
+
+
 const routes=[
     ...hzybRoutes,
     ...cygxRoutes,
     ...hzslRoutes,
+    ...ssbgRoutes,
     //404
     {
         path: "/:pathMatch(.*)",

+ 21 - 0
src/router/ssbg/index.js

@@ -0,0 +1,21 @@
+/* 随手办公 */
+
+export const ssbgRoutes = [
+	{
+		path: '/ssbg/roadshow',
+		name:'ssbgRoadShow',
+		component: () => import("@/App.vue"),
+		children: [
+			{
+				path: 'mine',
+				name: 'myCalendar',
+				component: () => import("@/views/ssbg/roadshow/myCalendar.vue")
+			},
+			{
+				path: 'researcher',
+				name: 'researcherCalendar',
+				component: () => import("@/views/ssbg/roadshow/rsCalendar.vue")
+			}
+		]
+	},
+]

+ 5 - 0
src/style/common.scss

@@ -1,6 +1,7 @@
 #app{
     font-size: 28px;
 }
+* { margin: 0px; padding:0}
 div{
     box-sizing: border-box;
 }
@@ -17,4 +18,8 @@ a {
 }
 input{
     -webkit-appearance: none;
+}
+
+li {
+    list-style: none;
 }

+ 149 - 0
src/views/ssbg/components/calendar.vue

@@ -0,0 +1,149 @@
+<script setup>
+import { ref, onMounted } from 'vue';
+import '@fullcalendar/core/vdom'; // solve problem with Vite
+import FullCalendar from '@fullcalendar/vue3';
+import timeGridPlugin from '@fullcalendar/timegrid';
+import interactionPlugin from '@fullcalendar/interaction';
+
+import swiperCalendar from './swiperCalendar.vue';
+// import swiperCalendar from './swiper.vue';
+
+const emit = defineEmits(['dateChange'])
+
+const handleDateClick = () => {
+};
+const handleEventClick = () => {};
+const handleEvents = () => {};
+const handleDateSelect = () => {};
+
+
+let calendarOptions = ref({
+  height: 'auto',
+  headerToolbar: false, // 顶部工具栏
+  allDaySlot: false, // 全天日程(不需要)
+  locale: 'zh-cn', // 语言
+  nowIndicator: true,
+  plugins: [
+    timeGridPlugin,
+    interactionPlugin, // needed for dateClick
+  ],
+  initialView: 'timeGridDay', //默认显示格式
+  editable: true,
+  selectable: true,
+  selectMirror: true,
+  dayMaxEvents: true,
+  weekends: true,
+  dateClick: handleDateClick,
+  eventClick: handleEventClick,
+  eventsSet: handleEvents,
+  select: handleDateSelect,
+  events: [
+    {
+      start: '2022-03-07T09:30:00',
+      end: '2022-03-07T10:30:00',
+      event_id: '10',
+      id: '12',
+      title: '454545',
+    }
+  ], //日程表
+  eventTimeFormat: {
+    // 格式化日程显示时间,如'14:30'
+    hour: '2-digit',
+    minute: '2-digit',
+    hour12: false,
+  },
+  slotMinTime: '08:00:00', //每天最早8点
+  dayHeaderFormat: {
+    weekday: 'short',
+    // year: 'numeric',
+    month: 'numeric',
+    day: 'numeric',
+    omitCommas: true,
+  }, //顶部显示格式
+  slotLabelFormat: {
+    //格式化左侧显示时间
+    hour12: false, // 14:00  true = 2pm
+    hour: '2-digit', //'06:xx'
+    minute: '2-digit', // 'xx:10'
+  },
+  slotDuration: '01:00:00',//一小时分割
+  eventColor: '#D5E6FF', // 默认日程背景色
+  eventTextColor: '#333', // 默认日程文本颜色
+  weekNumberCalculation: "ISO"
+  // eventClassNames: [ 'myclassname', 'otherclassname' ],  // 自定义日程类名
+}); //配置
+
+let calendarApi = ref(null); //日历api
+let FullCalendarRef = ref(null); //ref
+
+
+/* 选中日期改变 更新日历 */
+const datechangeHandle = (date) => {
+
+  calendarApi.value.gotoDate(date)
+  emit('dateChange',date)
+}
+
+
+onMounted(() => {
+  calendarApi.value = FullCalendarRef.value.getApi();
+
+  // calendarApi.value.prev()
+});
+</script>
+
+<template>
+  <div class="header">
+    <swiperCalendar @dateChange="datechangeHandle"/>
+
+  </div>
+  <FullCalendar :options="calendarOptions" ref="FullCalendarRef">
+    <template #eventContent="arg">
+      <div class="popper-content">
+        <!-- <p>{{ arg.timeText }}</p> -->
+        <p>{{ arg.event.title }}</p>
+      </div>
+    </template>
+  </FullCalendar>
+</template>
+
+<style lang="scss" scoped>
+
+.header {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 99;
+  background: #fff;
+  padding-top: 20px;
+  padding: 20px 0;
+  box-shadow: 0px 3px 6px rgba(172, 172, 172, 0.16);
+}
+.fc {
+  margin: 160px 0;
+  ::v-deep(.fc-timegrid-slot) {
+    height: 80px;
+  }
+
+  ::v-deep(.fc-timegrid-slot-label) {
+    border-right: none;
+  }
+  ::v-deep(.fc-timegrid-slot) {
+    border-left: none;
+  }
+  ::v-deep(.fc-timegrid-event ) {
+    border-left: 8px solid #3385FF !important;
+    /* border-left-color:#3385FF !important; */
+    /* &::after {
+      height: 100%;
+      width: 6px;
+      position: absolute;
+      top: 0;
+      content: '';
+      background-color: #3385FF;
+    } */
+
+  }
+}
+</style>

+ 276 - 0
src/views/ssbg/components/swiperCalendar.vue

@@ -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>

+ 9 - 0
src/views/ssbg/roadshow/myCalendar.vue

@@ -0,0 +1,9 @@
+<!--  -->
+<template>
+	<div>4848484</div>
+</template>
+
+<script lang='ts'>
+</script>
+<style scoped>
+</style>

+ 89 - 0
src/views/ssbg/roadshow/rsCalendar.vue

@@ -0,0 +1,89 @@
+<script setup>
+import { ref } from 'vue';
+import { Icon } from 'vant';
+import Calendar from '../components/calendar.vue';
+
+const actionsList = ref([
+  { label: '添加活动', key: 'add', icon: 'add-o' },
+  {
+    label: '本周',
+    key: 'weeknow',
+    img: '../../../assets/ssbg/calendar_ico.png',
+  },
+  {
+    label: '选择研究员',
+    key: 'choose',
+    img: '../../../assets/ssbg/choose_ico.png',
+  },
+]);
+
+/* 获取日历日程列表 */
+const getEventList = (date) => {
+  console.log(date, '日程');
+};
+
+
+
+</script>
+
+<!--  -->
+<template>
+  <div class="rs-container">
+    <Calendar @dateChange="getEventList" />
+
+    <div class="fix-action">
+      <ul class="action-ul">
+        <li v-for="item in actionsList" key="item.key">
+          <Icon :name="item.icon" class="item-icon" v-if="item.icon" />
+          <img
+						class="item-img"
+            src="@/assets/ssbg/calendar_ico.png"
+            alt=""
+            v-else-if="item.key === 'weeknow'"
+          />
+          <img
+						class="item-img"
+            src="@/assets/ssbg/choose_ico.png"
+            alt=""
+            v-else-if="item.key === 'choose'"
+          />
+          {{ item.label }}
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.rs-container {
+  .fix-action {
+    height: 160px;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 99;
+    background-color: #fff;
+    box-shadow: 0px -3px 6px rgba(172, 172, 172, 0.16);
+    .action-ul {
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: space-around;
+      color: #3385ff;
+      .item-icon {
+        font-size: 52px;
+        display: block;
+        text-align: center;
+        margin: 0 auto 10px;
+      }
+			.item-img {
+				display: block;
+				width: 46px;
+				height: 46px;
+				margin: 0 auto 10px;
+			}
+    }
+  }
+}
+</style>

+ 6 - 0
src/views/ssbg/utils/index.js

@@ -0,0 +1,6 @@
+import moment from "moment";
+
+//格式化日期函数
+export const formtDate = (date, v) => {
+  return moment(date).format(v)
+}