瀏覽代碼

Merge branch 'roadshow_dev'

Karsa 3 年之前
父節點
當前提交
3d9d403e02

+ 3 - 1
.env.development

@@ -5,4 +5,6 @@ VITE_APP_CYGX_BASEAPIURL="http://8.136.199.33:8500/api"
 
 VITE_APP_HZYB_BASEAPIURL="http://8.136.199.33:8612/api"
 
-VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+
+VITE_APP_SSBG_BASEAPIURL="http://8.136.199.33:8607/h5adminapi"

+ 3 - 1
.env.product

@@ -5,4 +5,6 @@ VITE_APP_CYGX_BASEAPIURL="https://cygx.hzinsights.com/api"
 
 VITE_APP_HZYB_BASEAPIURL="https://yanbao.hzinsights.com/api"
 
-VITE_APP_HZSL_BASEAPIURL="https://openapi.hzinsights.com/api"
+VITE_APP_HZSL_BASEAPIURL="https://openapi.hzinsights.com/api"
+
+VITE_APP_SSBG_BASEAPIURL="https://ficc.hzinsights.com/h5adminapi"

+ 3 - 1
.env.test

@@ -5,4 +5,6 @@ VITE_APP_CYGX_BASEAPIURL="http://8.136.199.33:8500/api"
 
 VITE_APP_HZYB_BASEAPIURL="http://8.136.199.33:8612/api"
 
-VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+
+VITE_APP_SSBG_BASEAPIURL="https://xcxh5test.hzinsights.com/h5adminapi"

+ 1 - 0
index.html

@@ -10,5 +10,6 @@
   <body>
     <div id="app"></div>
     <script type="module" src="/src/main.js"></script>
+    <script type="text/javascript" src="https://gitee.com/dcloud/uni-app/raw/master/dist/uni.webview.1.5.2.js"></script>
   </body>
 </html>

+ 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",

+ 25 - 0
src/api/ssbg/api.js

@@ -0,0 +1,25 @@
+import {get,post} from './http';
+
+
+/**
+ * 研究员列表
+ * @param {*} params 
+ * @returns 
+ */
+export const researcherList = params=>{
+	return get('/roadshow/researcher/list',params)
+}
+
+/**
+ * 研究员日历日程 StartDate  EndDate ResearcherId
+ */
+export const researchEvents = params => {
+	return get('/roadshow/researcher/calendar/detail', params)
+}
+
+/**
+ * 我的日历日程 StartDate  EndDate
+ */
+export const myEvents = params => {
+	return get('/roadshow/my/calendar/detail', params)
+}

+ 63 - 0
src/api/ssbg/http.js

@@ -0,0 +1,63 @@
+"use strict";
+import axios from "axios";
+import { Toast } from "vant";
+
+// Full config:  https://github.com/axios/axios#request-config
+// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
+// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
+// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
+
+// 请求数
+let LOADINGCOUNT = 0;
+let LOADING;
+let config = {
+  baseURL: import.meta.env.VITE_APP_SSBG_BASEAPIURL,
+  timeout: 60 * 1000, // Timeout
+  // withCredentials: true, // Check cross-site Access-Control
+};
+
+const _axios = axios.create(config);
+
+_axios.interceptors.request.use(
+  function (config) {
+    // Do something before request is sent
+    config.headers.Authorization = localStorage.getItem('ssbg-token') || '';
+    
+    return config;
+  },
+  function (error) {
+    // Do something with request error
+    return Promise.reject(error);
+  }
+);
+
+// Add a response interceptor
+_axios.interceptors.response.use(
+  function (response) {
+    // Do something with response data
+    
+    return response.data;
+  },
+  function (error) {
+    // Do something with response error
+    return Promise.reject(error);
+  }
+);
+
+/**
+ * 导出get请求方法
+ * @url 请求地址
+ * @params get请求参数
+ */
+export const get = (url, params) => {
+  return _axios.get(url, { params });
+};
+
+/**
+ * 导出post请求方法
+ * @url 请求地址
+ * @params post请求参数
+ */
+export const post = (url, params) => {
+  return _axios.post(url, params);
+};

二進制
src/assets/ssbg/calendar_ico.png


二進制
src/assets/ssbg/choose_ico.png


二進制
src/assets/ssbg/tab_icon1.png


二進制
src/assets/ssbg/tab_icon2.png


二進制
src/assets/ssbg/tab_icon3.png


二進制
src/assets/ssbg/tab_icon4.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;
 }

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

@@ -0,0 +1,275 @@
+<script setup>
+import { ref, onMounted,watch, computed } from 'vue';
+import { useRoute } from 'vue-router';
+import { Sticky,Popup } from 'vant';
+
+import '@fullcalendar/core/vdom'; // solve problem with Vite
+import FullCalendar from '@fullcalendar/vue3';
+import timeGridPlugin from '@fullcalendar/timegrid';
+import interactionPlugin from '@fullcalendar/interaction';
+
+import moment from 'moment';
+import 'moment/dist/locale/zh-cn';
+moment.locale('zh-cn');
+import swiperCalendar from './swiperCalendar.vue';
+
+/* props */
+const props = defineProps({
+  eventList: {
+    type: Array,
+    required: true
+  }
+})
+
+const emits = defineEmits(['dateChange','cellClick']);
+
+const route = useRoute();
+const swiperCalendarRef = ref(null);
+
+const isSeller = computed(()=> ['ficc_seller', 'rai_seller', 'ficc_group', 'rai_group'].includes(localStorage.getItem('ssbg-role')))
+
+
+let isShowPopup = ref(false);//日程弹窗
+const selectEventInfo = ref(null)//日程信息
+
+/* 点击表格日期 */
+const handleDateClick = ({date}) => {
+  emits('cellClick',date)
+};
+
+/* 点击日程 */
+const handleEventClick = (_) => {
+  let id = _.event.id; // 获取当前点击日程的ID
+
+  let event_item = props.eventList.find(item => item.id === Number(id));
+
+  //标题拼接
+  let title = setDynamicTitle(event_item);
+
+  selectEventInfo.value = {...event_item,title};
+  isShowPopup.value = true;
+};
+
+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,
+  events: [], //日程表
+  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)
+  emits('dateChange',date)
+  // document.title = date.substr(0,7);
+}
+
+/* 切换日历和周为本日 */
+const toogeCurrentWeek = () => {
+  swiperCalendarRef.value.getRecentWeek(moment().format('YYYY-MM-DD'))
+  swiperCalendarRef.value.selectDateHandle(moment().format('YYYY-MM-DD'),1)
+}
+
+// 处理数据结构
+const getReservationList = () => {
+  const arr = props.eventList.map((item) => {
+    // let title = (this.isSeller && item.ActivityType==='路演')
+    // ? `${item.CompanyName}${item.CompanyStatus ? '('+ item.CompanyStatus + ')' : ''}` : (this.isSeller && item.ActivityType==='公开会议')
+    // ? item.Theme : setDynamicTitle(item);
+    let title = item.ActivityType==='路演'
+    ? `${item.CompanyName}${item.CompanyStatus ? '('+ item.CompanyStatus + ')' : ''}` : item.ActivityType==='公开会议'
+    ? item.Theme : setDynamicTitle(item);
+
+    return {
+      start: item.StartDate+'T'+item.StartTime ,
+      end: item.EndDate+'T'+item.EndTime,
+      event_id: item.RsCalendarId || item.RsMattersId,
+      id: item.id,
+      title,
+    };
+  });
+  calendarOptions.value.events = arr
+}
+
+// 拼接标题 type 内部会议 公开会议 路演 报告电话会 事项
+const setDynamicTitle = ({ActivityType,RsMattersId,MatterContent,RoadshowType,RoadshowPlatform,Province,City,ActivityCategory,Source,Title}) => {
+
+  //第三方添加的日历活动
+  if(Source === 1) return Title;
+
+  switch(ActivityType || RsMattersId) {
+    case '内部会议': return ActivityType;
+    case '公开会议': return `${RoadshowType}${ActivityType}(${RoadshowType==='线上' ? RoadshowPlatform : Province+City})`;
+    case '路演': return `${RoadshowType}${ActivityType}(${RoadshowType==='线上' ? RoadshowPlatform : Province+City})`;
+    case '报告电话会': return `${ActivityCategory}电话会`;
+    case RsMattersId: return MatterContent;
+  }
+}
+
+watch(
+  () => props.eventList,
+  (newval) => {
+    getReservationList();
+  }
+)
+
+onMounted(() => {
+  calendarApi.value = FullCalendarRef.value.getApi();
+  datechangeHandle(moment().format('YYYY-MM-DD'))
+});
+
+defineExpose({ toogeCurrentWeek,calendarApi });
+</script>
+
+<template>
+  <Sticky>
+    <div class="header">
+      <swiperCalendar @dateChange="datechangeHandle" ref="swiperCalendarRef"/>
+    </div>
+  </Sticky>
+  <FullCalendar :options="calendarOptions" ref="FullCalendarRef">
+    <template #eventContent="arg">
+      <div class="popper-content">
+        <!-- <p>{{ arg.timeText }}</p> -->
+        <p>{{ arg.event.title }}</p>
+      </div>
+    </template>
+  </FullCalendar>
+
+<!-- 弹窗层 -->
+  <Popup
+    v-model:show="isShowPopup"
+    closeable
+  >
+    <div class="dialog-cont">
+      <p>
+
+        <template v-if="selectEventInfo.StartDate === selectEventInfo.EndDate">
+					{{
+            `${moment(selectEventInfo.StartDate).format("MM.DD")}
+            (${moment(selectEventInfo.StartDate).format('ddd')}) 
+            ${selectEventInfo.StartTime&&selectEventInfo.StartTime.substr(0,5)} - 
+            ${selectEventInfo.EndTime&&selectEventInfo.EndTime.substr(0,5)}`
+          }}
+				</template>
+        <template v-else>
+          {{
+            `${moment(selectEventInfo.StartDate).format("MM.DD")}
+            (${moment(selectEventInfo.StartDate).format('ddd')}) 
+            ${selectEventInfo.StartTime&&selectEventInfo.StartTime.substr(0,5)} - 
+            ${moment(selectEventInfo.EndDate).format("MM.DD")}
+            (${moment(selectEventInfo.EndDate).format('ddd')}) 
+            ${selectEventInfo.EndTime&&selectEventInfo.EndTime.substr(0,5)}`
+          }}
+        </template>
+          
+        </p>
+        <p v-if="isSeller">{{ selectEventInfo.ResearcherName }}</p>
+        <p>{{ selectEventInfo.title }}</p>
+        <p v-if="selectEventInfo.CompanyName" >{{ `${selectEventInfo.CompanyName}${selectEventInfo.CompanyStatus ? '('+ selectEventInfo.CompanyStatus + ')' : ''}` }}</p>
+        <p v-if="selectEventInfo.Theme">{{ selectEventInfo.Theme }}</p>
+        <p v-if="selectEventInfo.CooperationName">{{ selectEventInfo.CooperationName }}</p>
+        <p v-if="['公开会议','内部会议','路演'].includes(selectEventInfo.ActivityType)">发起人:{{selectEventInfo.SysUserRealName}}</p>
+    </div>
+  </Popup>
+</template>
+
+<style lang="scss" scoped>
+
+.header {
+  background: #fff;
+  padding-bottom: 20px;
+  box-shadow: 0px 3px 6px rgba(172, 172, 172, 0.16);
+  .tab-cont {
+		padding: 30px;
+		background: #fff;
+		box-shadow: 0px 3px 12px rgba(175, 175, 175, 0.16);
+		border-bottom: 6px solid #f6f6f6;
+    margin-bottom: 20px;
+		.tab-ul {
+			display: flex;
+			align-items: center;
+			li {
+				margin-right: 50px;
+				color: #666;
+				text-align: center;
+				.item-img {
+					width: 78px;
+					height: 78px;
+					display:block;
+					margin: 0 auto 10px;
+				}
+			}
+		}
+  }  
+}
+.fc {
+  margin: 0 0 160px;
+  ::v-deep(.fc-timegrid-slot) {
+    height: 120px;
+  }
+
+  ::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;
+
+  }
+}
+
+.dialog-cont {
+  width: 550px;
+  padding: 40px;
+  p {
+    color: #333;
+  }
+}
+</style>

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

@@ -0,0 +1,296 @@
+<script setup>
+import { reactive, toRefs, ref, watch, onMounted } from 'vue';
+import moment from 'moment';
+import 'moment/dist/locale/zh-cn';
+moment.locale('zh-cn');
+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) => {
+  const currentWeekDateList = state.weekDateList[1];
+  let weekIndex = 0;
+  currentWeekDateList.forEach((item, index) => {
+    if (item === selectDate) weekIndex = index;
+  });
+
+  state.weekIndex = weekIndex;
+};
+
+setWeekIndex(current.value.currentDate);
+
+/* 选择天 */
+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,
+  }
+);
+
+defineExpose({
+  getRecentWeek,
+  selectDateHandle
+})
+
+const { weekDateList, selectDate, moveIndex, touch } = toRefs(state);
+</script>
+
+<template>
+  <h3 class="current-date">{{ moment(selectDate).format('YYYY年MM月') }}</h3>
+  <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,
+            today: moment().format('YYYY-MM-DD') === 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">
+.current-date {
+  text-align: center;
+  margin-bottom: 10px;
+}
+.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 24px;
+        &.today {
+          background: #b4cef5;
+          border-radius: 50%;
+          color: #fff;
+        }
+        &.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>

+ 56 - 0
src/views/ssbg/roadshow/common-calendar.js

@@ -0,0 +1,56 @@
+
+
+/* 根据研究员id找父类index */
+export const findParentByid = (id,arr) => {
+	let obj = arr.find(item => item.ResearcherList.some( _ => _.AdminId === id));
+	// console.log(obj)
+	return arr.findIndex(_ => _.GroupName === obj.GroupName);
+}
+
+//  研究员
+export const RESEARCHLIST = ['ficc_researcher', 'researcher', 'rai_researcher','ficc_admin', 'rai_admin',];
+
+// 销售/组长
+const SELLERLIST = ['ficc_seller', 'rai_seller', 'ficc_group', 'rai_group','seller'];
+
+// admin
+const ADMINLIST = ['admin'];
+
+/* 根据角色获取我的日历顶部tab */
+export const getTabsByRole = (role) => {
+	return RESEARCHLIST.includes(role) 
+		? [
+			{
+				label: '活动审批',
+				key:1
+			},
+			{
+				label: '内部会议',
+				key:3
+			},
+			{
+				label: '事项',
+				key:5
+			},
+		] : SELLERLIST.includes(role)
+		? [
+			{
+				label: '活动申请',
+				key:2
+			},
+			{
+				label: '内部会议',
+				key:3
+			},
+		] : ADMINLIST.includes(role)
+		? [
+			{
+				label: '内部会议',
+				key:3
+			},
+			{
+				label: '报告电话会',
+				key:4
+			},
+		] : [];
+}

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

@@ -0,0 +1,191 @@
+
+<script setup>
+import { ref,onMounted } from 'vue';
+import { Icon } from 'vant';
+import { useRoute } from 'vue-router';
+import moment from 'moment';
+import 'moment/dist/locale/zh-cn';
+moment.locale('zh-cn');
+
+import { getTabsByRole } from './common-calendar';
+import { isWxprogram } from '../utils';
+import Calendar from '../components/calendar.vue';
+import { myEvents } from '@/api/ssbg/api';
+
+const route = useRoute();
+localStorage.setItem('ssbg-token',route.query.token ||  '5e532bc5fa7281aebaa6d89960b54c4ab22f1ab18e969258c634a7940fdb0922');
+localStorage.setItem('ssbg-role',route.query.role ||  'researcher');
+
+const calendarRef = ref(null);//日历ref
+const actionsList = ref([
+  {
+    label: '今天',
+    key: 'weeknow',
+    show: true,
+  },
+  {
+    label: '添加事项',
+    key: 'add_matter',
+		icon: 'add-o',
+    show:  ['ficc_researcher', 'researcher', 'rai_researcher','ficc_admin', 'rai_admin'].includes(localStorage.getItem('ssbg-role'))
+  },
+]);//底部固定数据
+const eventList = ref([]);//日程信息
+const Tabs = ref(getTabsByRole(localStorage.getItem('ssbg-role')));//顶部tab
+const showCalendar = ref(true);
+
+onMounted(() => {
+  showCalendar.value = localStorage.getItem('ssbg-role') === 'admin' ? false : true;
+})
+
+/* 获取日历日程列表 */
+const getEventList = async () => {
+  if(localStorage.getItem('ssbg-role') === 'admin') return;
+
+  const { currentStart } = calendarRef.value.calendarApi.view;
+
+  const { code,data } = await myEvents({
+    StartDate: moment(currentStart).format('YYYY-MM-DD'),
+    EndDate: moment(currentStart).format('YYYY-MM-DD')
+  })
+
+  if(code !== 200) return
+
+  eventList.value = [
+    ...(data.CalendarList || []),
+    ...(data.RsMattersList || []),
+  ].map((item, index) => ( { ...item, id: index }));
+};
+
+/* 底部操作 */
+const dealActionHandler = (type) => {
+  const deal_methods_handles = {
+    weeknow: toogeCurrentWeek,
+    add_matter: addMatterHandle
+  };
+  deal_methods_handles[type]();
+}
+
+const addMatterHandle = () => {
+  wx.miniProgram.navigateTo({
+    url: '/pages-roadshow/addMatter/index',
+  });
+}
+
+/* 切换为今天 */
+const toogeCurrentWeek = () => {
+  calendarRef.value.toogeCurrentWeek();
+};
+
+/* 进入活动列表 */
+const switchTabHandle = (key) => {
+  wx.miniProgram.navigateTo({
+    url: `/pages-approve/activity/list?type=${key}`,
+  });
+}
+
+</script>
+<!--  -->
+<template>
+	<div class="mycalendar-container">
+    <div class="tab-cont" v-if="route.path === '/ssbg/roadshow/mine'">
+      <ul class="tab-ul">
+        <li v-for="tab in Tabs" :key="tab.key" @click="switchTabHandle(tab.label)">
+          <img class="item-img" src="@/assets/ssbg/tab_icon1.png" v-if="[1,2].includes(tab.key)"/>
+          <img class="item-img" src="@/assets/ssbg/tab_icon2.png" v-if="tab.key === 3"/>
+          <img class="item-img" src="@/assets/ssbg/tab_icon3.png" v-if="tab.key === 5"/>
+          <img class="item-img" src="@/assets/ssbg/tab_icon4.png" v-if="tab.key === 4"/>
+          {{ tab.label }}
+        </li>
+      </ul>
+    </div>
+
+		<Calendar
+    ref="calendarRef"
+    :eventList="eventList"
+    @dateChange="getEventList"
+    v-if="showCalendar"
+    />
+
+    <div class="fix-action" v-if="isWxprogram() && showCalendar">
+      <ul class="action-ul">
+        <li
+          v-for="item in actionsList"
+          key="item.key"
+          @click="dealActionHandler(item.key)"
+          v-show="item.show"
+        >
+          <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'"
+          />
+          {{ item.label }}
+        </li>
+      </ul>
+    </div>
+	</div>
+</template>
+<style scoped lang="scss">
+.mycalendar-container {
+  .tab-cont {
+		padding: 30px;
+		background: #fff;
+		box-shadow: 0px 3px 12px rgba(175, 175, 175, 0.16);
+		border-bottom: 6px solid #f6f6f6;
+    margin-bottom: 20px;
+		.tab-ul {
+			display: flex;
+			align-items: center;
+			li {
+				margin-right: 50px;
+				color: #666;
+				text-align: center;
+				.item-img {
+					width: 78px;
+					height: 78px;
+					display:block;
+					margin: 0 auto 10px;
+				}
+			}
+		}
+  } 
+	.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);
+    padding-top: 20px;
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+    .action-ul {
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: space-around;
+      color: #3385ff;
+      li:hover {
+        opacity: 0.8;
+      }
+      .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>

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

@@ -0,0 +1,287 @@
+<script setup>
+import { ref, reactive,toRefs,onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { Icon, Toast,TreeSelect,Popup } from 'vant';
+import moment from 'moment';
+import 'moment/dist/locale/zh-cn';
+moment.locale('zh-cn');
+
+import { researcherList,researchEvents } from '@/api/ssbg/api';
+import { isWxprogram } from '../utils';
+import { findParentByid } from './common-calendar';
+import Calendar from '../components/calendar.vue';
+
+
+const route = useRoute();
+localStorage.setItem('ssbg-token',route.query.token ||  '5e532bc5fa7281aebaa6d89960b54c4ab22f1ab18e969258c634a7940fdb0922');
+
+const actionsList = ref([
+  { label: '添加活动', key: 'add', icon: 'add-o' },
+  {
+    label: '今天',
+    key: 'weeknow',
+    img: import("@/assets/ssbg/calendar_ico.png"),
+  },
+  {
+    label: '选择研究员',
+    key: 'choose',
+    img: import("@/assets/ssbg/choose_ico.png"),
+  },
+]);
+
+const calendarRef = ref(null);
+
+const state = reactive({
+  selectResearcher: '', // 选中的研究员
+  eventList: [],//日程信息
+});
+
+/* 获取日历日程列表 */
+const getEventList = async () => {
+  if(!state.selectResearcher) return
+
+  const { currentStart } = calendarRef.value.calendarApi.view;
+
+  const { code,data } = await researchEvents({
+    StartDate: moment(currentStart).format('YYYY-MM-DD'),
+    EndDate: moment(currentStart).format('YYYY-MM-DD'),
+    ResearcherId: Number(state.selectResearcher)
+  })
+
+  if(code !== 200) return
+
+  state.eventList = [
+    ...(data.CalendarList || []),
+    ...(data.RsMattersList || []),
+  ].map((item, index) => ( { ...item, id: index }));
+};
+
+/* 点击日历表格添加活动 */
+const cellClickHandle = (date) => {
+
+  if (!state.selectResearcher) return Toast.fail('请先选择研究员');
+
+  const endDate = new Date(date.getTime() + 1000 * 60 * 60).getTime();
+  const defaultResearcher = [
+    {
+      researcherId: state.selectResearcher,
+      startTime: date.getTime(),
+      endTime: endDate,
+    }
+  ]
+
+  wx.miniProgram.navigateTo({
+    url: `/pages-roadshow/addActivity/byCell?defaultOpt=${JSON.stringify(defaultResearcher)}`,
+  });
+};
+
+/* 底部操作 */
+const dealActionHandler = (type) => {
+  const deal_methods_handles = {
+    add: addActivityHandleByBtn,
+    weeknow: toogeCurrentWeek,
+    choose: slectResearcherHandle,
+  };
+  deal_methods_handles[type]();
+};
+
+/* 点击按钮添加活动 */
+const addActivityHandleByBtn = () => {
+  wx.miniProgram.navigateTo({
+    url: '/pages-roadshow/addActivity/index',
+  });
+};
+
+/* 切换为今天 */
+const toogeCurrentWeek = () => {
+  calendarRef.value.toogeCurrentWeek();
+};
+
+/* ==========选择研究员picker============ */
+const researcherArr = ref([]);
+const isResearcherPicker = ref(false);
+
+const rs_picker = ref({
+  firstindex: 0,
+  id: ''
+})
+
+/* 获取研究员列表 */
+const getResearcherList = async() => {
+  const { code,data } = await researcherList();
+  if(code !== 200) return;
+  researcherArr.value = data.map(group => ({
+    ...group,
+    text: group.GroupName,
+    children: group.ResearcherList ? group.ResearcherList.map(child => ({
+      ...child,
+      text: child.RealName,
+      id: child.AdminId
+    })) : []
+  }));
+}
+onMounted(() => {
+  getResearcherList();
+})
+
+/* 选择研究员 */
+const slectResearcherHandle = () => {
+  // console.log('slect');
+  if(state.selectResearcher) {
+    rs_picker.value = {
+      firstindex: findParentByid(state.selectResearcher,researcherArr.value),
+      id: state.selectResearcher
+    }
+  }
+  isResearcherPicker.value = true;
+};
+
+/* 点击第一项索引 */
+const clickFirstHandle = (index) => {
+  rs_picker.value.firstindex = index;
+}
+/* 点击选择研究员 */
+const clickItemHandle = ({AdminId}) => {
+  rs_picker.value.id  = AdminId;
+}
+/* 取消 */
+const cancelResearcher = () => {
+
+  isResearcherPicker.value = false;
+}
+/* 确认 */
+const confirmResearcher = () => {
+  state.selectResearcher = rs_picker.value.id;
+			
+	isResearcherPicker.value = false;
+  getEventList();
+}
+
+const { eventList } = toRefs(state);
+
+document.title = '研究员日历';
+</script>
+
+<!--  -->
+<template>
+  <div class="rs-container">
+    <Calendar 
+      ref="calendarRef"
+      :eventList="eventList"
+      @dateChange="getEventList" 
+      @cellClick="cellClickHandle" 
+    />
+    <!-- v-if="isWxprogram()" -->
+    <div class="fix-action" >
+      <ul class="action-ul">
+        <li
+          v-for="item in actionsList"
+          key="item.key"
+          @click="dealActionHandler(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>
+    
+    
+
+    <!-- 选择研究员picker -->
+    <Popup v-model:show="isResearcherPicker" position="bottom">
+			<view class="select-rs-header">
+				<text class="cancel" @click="cancelResearcher">取消</text>
+				<text class="ensure" @click="confirmResearcher">确认</text>
+			</view>
+			<TreeSelect 
+				:items="researcherArr" 
+				:main-active-index="rs_picker.firstindex" 
+				:active-id="rs_picker.id"
+				@click-nav="clickFirstHandle" 
+				@click-item="clickItemHandle" 
+			/>
+		</Popup>
+  </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);
+    padding-top: 20px;
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+    .action-ul {
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: space-around;
+      color: #3385ff;
+      li:hover {
+        opacity: 0.8;
+      }
+      .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;
+      }
+    }
+  }
+
+  .select-rs-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 30px 20px;
+
+    .cancel {
+      color: #969799;
+    }
+
+    .ensure {
+      color: #576b95;
+    }
+  }
+
+  .van-tree-select {
+		::v-deep(.van-sidebar-item--select:before) {
+			background-color: #1989fa;
+		}
+
+		::v-deep(.van-tree-select__item--active) {
+			color: #1989fa;
+		}
+
+    ::v-deep(.van-tree-select__selected) {
+      position: absolute !important;
+    }
+
+	}
+}
+</style>

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

@@ -0,0 +1,13 @@
+import moment from "moment";
+
+//格式化日期函数
+export const formtDate = (date, v) => {
+  return moment(date).format(v)
+}
+
+/* 是否在小程序内 */
+export const isWxprogram = () => {
+  const userAgent = navigator.userAgent;
+
+  return /miniProgram/i.test(userAgent) && /micromessenger/i.test(userAgent);
+}