chenlei 1 rok temu
rodzic
commit
8b16a60187

Plik diff jest za duży
+ 421 - 0
toolbar/data.json


+ 61 - 0
toolbar/getDataDate.js

@@ -0,0 +1,61 @@
+const fs = require('fs');
+
+const jsonData = [
+  
+];
+
+function parseDate(dateString) {
+  const months = {
+    'January': 0, 'February': 1, 'March': 2, 'April': 3, 'May': 4, 'June': 5,
+    'July': 6, 'August': 7, 'September': 8, 'October': 9, 'November': 10, 'December': 11
+  };
+
+  const regex = /(\b\w+ \d{1,2}(?:-\d{1,2})?,? \d{4}\b)/g;
+  const matches = dateString.match(regex);
+
+  if (matches) {
+    const [start, end] = matches.map(match => {
+      const parts = match.split(' ');
+      const month = months[parts[0]];
+      const day = parseInt(parts[1].replace(',', ''), 10);
+      const year = parseInt(parts[2], 10);
+      return new Date(year, month, day);
+    });
+
+    if (end) {
+      return [formatDate(start), formatDate(end)];
+    } else {
+      return formatDate(start);
+    }
+  }
+
+  return null;
+}
+
+function formatDate(date) {
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, '0');
+  const day = String(date.getDate()).padStart(2, '0');
+  return `${year}-${month}-${day}`;
+}
+
+const processedData = jsonData.map(event => {
+  const { info } = event;
+  const { time } = info;
+  const formattedTime = parseDate(time);
+  const formattedInfo = {
+    ...info,
+    date: formattedTime
+  };
+  return { ...event, info: formattedInfo };
+});
+
+try {
+  fs.unlinkSync('data.json');
+} catch (err) {
+  console.error('data文件不存在', err);
+}
+
+fs.writeFile('data.json', JSON.stringify(processedData, null, 2), (err) => {
+  console.log(err)
+});

+ 15 - 1
web/src/assets/css/base.css

@@ -212,4 +212,18 @@ a:hover {
     -o-filter: grayscale(100%);
     filter: grayscale(100%);
     filter: gray;
-} */
+} */
+
+.limit-line {
+    display: -webkit-box;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    -webkit-line-clamp: 1;
+    -webkit-box-orient: vertical;
+    word-break: break-all;
+    word-wrap: break-word;
+}
+
+.line-2 {
+    -webkit-line-clamp: 2;
+}

BIN
web/src/assets/images/Visit/icon_enter_normal.png


BIN
web/src/assets/images/Visit/m-16.png


+ 6 - 0
web/src/main.js

@@ -3,8 +3,14 @@ import App from './App.vue'
 import router from './router'
 import ElementUI from 'element-ui';
 import 'element-ui/lib/theme-chalk/index.css';
+import lang from 'element-ui/src/locale/lang/en'
+import locale from 'element-ui/src/locale'
+import locale2 from 'element-ui/lib/locale'
 const config = require('./config.js')
 
+locale.use(lang)
+locale2.use(lang)
+
 //引入初始化样式
 import './assets/css/base.css'
 Vue.use(ElementUI);

+ 6 - 0
web/src/router/index.js

@@ -25,6 +25,12 @@ const routes = [
         redirect: { name: 'Visit1' },
         children: [
           {
+            path: '/Layout/Visit/8',
+            name: 'Visit1',
+            component: () => import('../views/Visit/Visit8.vue'),
+            meta: { myName: 'Visit', nameAll: 'Calendar' },
+          },
+          {
             path: '/Layout/Visit/1',
             name: 'Visit1',
             component: () => import('../views/Visit/Visit1.vue'),

+ 308 - 0
web/src/views/Visit/Visit8.less

@@ -0,0 +1,308 @@
+.Visit8 {
+  margin: 0 auto 30px;
+  width: 1300px;
+  font-family: Source Han Serif CN-Regular;
+
+  .dots {
+    display: flex;
+    gap: 8px;
+    margin-top: 10px;
+
+    .dot {
+      width: 5px;
+      height: 5px;
+      border-radius: 5px;
+      background-color: #A9DA8C;
+
+      &.events {
+        background-color: #95D2FF;
+      }
+      &.learn {
+        background-color: #FFCE7E;
+      }
+    }
+  }
+}
+.panel {
+  display: flex;
+  gap: 19px;
+  color: #202020;
+
+  > div {
+    background: white;
+    box-shadow: 0px 10px 32px 0px rgba(146,146,146,0.25);
+    border-radius: 10px;
+    overflow: hidden;
+  }
+  &-lf {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
+    ::v-deep .el-carousel {
+      &__button {
+        width: 9px;
+        height: 9px;
+        opacity: .5;
+        border-radius: 9px;
+      }
+      &__indicator--vertical {
+        position: relative;
+        padding: 8px 15px;
+
+        &.is-active::after {
+          content: '';
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          width: 17px;
+          height: 17px;
+          border-radius: 17px;
+          border: 2px solid #CC1424;
+          transform: translate(-50%, -50%);
+        }
+      }
+    }
+    &__footer {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      padding: 0 22px;
+      font-size: 19px;
+      color: #000;
+
+      div:first-child {
+        display: flex;
+        align-items: center;
+        font-weight: bold;
+
+        p {
+          flex: 1;
+          width: 0;
+          line-height: 24px;
+        }
+        span {
+          margin-left: 16px;
+          padding: 5px;
+          font-size: 12px;
+          color: #D2B986;
+          background: rgba(210,185,134,0.3);
+          border-radius: 4px;
+          white-space: nowrap;
+          border: 1px solid #D2B986;
+        }
+      }
+      p:last-child {
+        margin-top: 4px;
+      }
+    }
+  }
+  &-rg {
+    flex: 0 0 527px;
+    padding: 27px 30px;
+    box-sizing: border-box;
+
+    &__header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 45px 20px;
+
+      i {
+        cursor: pointer;
+        font-size: 20px;
+        color: #000000;
+      }
+      p {
+        font-size: 28px;
+        font-weight: bold;
+
+        span {
+          color: #CC1424;
+        }
+      }
+    }
+    ::v-deep &__date {
+      margin: 0 auto 12px;
+      width: calc(100% - 30px);
+      min-height: 370px;
+
+      th {
+        color: #000;
+      }
+      td {
+        border: 0 !important;
+      }
+      .el-calendar-day {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 55px;
+        color: #000000;
+
+        &:hover {
+          background-color: transparent;
+        }
+      }
+      .is-selected {
+        background-color: transparent;
+
+        .el-calendar-day {
+          color: #CC1424;
+          font-weight: bold;
+        }
+      }
+      .prev div,
+      .next div {
+        display: none;
+      }
+    }
+    &__footer {
+      display: flex;
+      gap: 70px;
+      justify-content: center;
+      padding-top: 20px;
+      border-top: 1px dashed rgba(32, 32, 32, .5);
+
+      p {
+        position: relative;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 50%;
+          left: -20px;
+          width: 10px;
+          height: 10px;
+          border-radius: 10px;
+          transform: translateY(-50%);
+          background-color: #95D2FF;
+        }
+        &:first-child::before {
+          background-color: #A9DA8C;
+        }
+        &:last-child::before {
+          background-color: #FFCE7E;
+        }
+      }
+    }
+  }
+}
+.main {
+  margin-top: 20px;
+  padding: 22px 21px;
+  color: #202020;
+  background: #FFFFFF;
+  border-radius: 10px;
+  box-shadow: 0px 8px 25px 0px rgba(146,146,146,0.25);
+
+  ::v-deep .el-date-editor {
+    width: 233px;
+    height: 45px;
+
+    .el-input__inner {
+      height: 45px;
+      line-height: 45px;
+      border-radius: 4px;
+    }
+  }
+  .el-button {
+    padding: 0 35px;
+    height: 45px;
+
+    &.el-button--primary {
+      border-color: #CC1424;
+      background-color: #CC1424;
+    }
+  }
+  &__header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 22px 24px;
+    border-bottom: 1px dashed rgba(32, 32, 32, .5);
+  }
+  &__list {
+    .visit8-card {
+      display: flex;
+      padding: 28px 23px;
+      height: 320px;
+      box-sizing: border-box;
+      border-bottom: 1px dashed rgba(32, 32, 32, .5);
+      cursor: pointer;
+      
+      &__left {
+        flex: 0 0 185px;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        font-size: 37px;
+        line-height: 48px;
+
+        img {
+          width: 50px;
+          height: 50px;
+        }
+      }
+      &__center {
+        flex : 1;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        padding: 0 26px 0 69px;
+
+        > p {
+          font-size: 37px;
+          font-weight: bold;
+          line-height: 48px;
+        }
+        &__inner {
+          line-height: 18px;
+        }
+      }
+      &__right {
+        flex: 0 0 420px;
+        height: 268px;
+      }
+      &__tag {
+        display: inline-block;
+        padding: 8px 15px;
+        color: #D2B986;
+        background: rgba(210,185,134,0.3);
+        border-radius: 4px;
+        border: 1px solid #D2B986;
+      }
+    }
+  }
+}
+.page {
+  display: flex;
+  justify-content: center;
+  padding-top: 30px;
+  & > span {
+    width: 30px;
+    height: 30px;
+    text-align: center;
+    line-height: 30px;
+    margin-right: 15px;
+    border-radius: 50%;
+    cursor: pointer;
+  }
+  .active {
+    background-color: #bf2323;
+    color: #fff;
+    pointer-events: none;
+  }
+}
+.empty {
+  text-align: center;
+  font-size: 20px;
+  padding-top: 30px;
+  color: #666666;
+}

+ 336 - 0
web/src/views/Visit/Visit8.vue

@@ -0,0 +1,336 @@
+<template>
+  <div class="Visit8">
+    <div class="panel">
+      <div class="panel-lf">
+        <el-carousel ref="carousel" :initial-index="currentBanner" height="440px" direction="vertical" :autoplay="false" @change="handleBanner">
+          <el-carousel-item v-for="item in bannerList" :key="`${item.id}-${selectedDay.getTime()}`">
+            <el-image :src="item.cover" fit="cover" style="cursor: pointer;" @click="handleClick(item)" />
+          </el-carousel-item>
+        </el-carousel>
+
+        <div class="panel-lf__footer">
+          <div><p class="limit-line">{{bannerList[currentBanner].h3}}</p><span>{{getBannerTag(bannerList[currentBanner]._type)}}</span></div>
+          <p>{{bannerList[currentBanner].i || bannerList[currentBanner].info.time}}</p>
+        </div>
+      </div>
+      <Calendar v-model="selectedDay">
+        <template
+          slot="dateCell"
+          slot-scope="{date, data}">
+          {{ data.day.split('-')[2] }}
+          <div class="dots">
+            <!-- <div class="dot" /> -->
+            <div v-if="showDotEvent(data.day)" class="dot events" />
+            <div v-if="showDotLearn(data.day)" class="dot learn" />
+          </div>
+        </template>
+      </Calendar>
+    </div>
+
+    <div class="main">
+      <div class="main__header">
+        from
+        <el-date-picker v-model="startDate"
+          type="date"
+          placeholder="Start Date"
+        />
+        to
+        <el-date-picker v-model="endDate"
+          type="date"
+          placeholder="End Date"
+          :picker-options="{
+            disabledDate: (time) => {
+              return this.startDate ? time.getTime() < this.startDate.getTime() : false;
+            }
+          }"
+        />
+        <el-button :type="currentType === 0 ? 'primary' : 'normal'" @click="handleType(0)">All</el-button>
+        <el-button :type="currentType === 1 ? 'primary' : 'normal'" @click="handleType(1)">Exhibitions</el-button>
+        <el-button :type="currentType === 2 ? 'primary' : 'normal'" @click="handleType(2)">Events</el-button>
+        <el-button :type="currentType === 3 ? 'primary' : 'normal'" @click="handleType(3)">Learn & Engage</el-button>
+      </div>
+
+      <div class="main__list">
+        <div v-for="item in allList[pageSize - 1]" :key="item.id" class="visit8-card" @click="handleClick(item)">
+          <div class="visit8-card__left">
+            <div>
+              <p v-for="txt in getEnDate(formatDate(new Date(Array.isArray(item.date) ? item.date[0] : item.date)))" :key="txt">{{ txt }}</p>
+            </div>
+            <img src="@/assets/images/Visit/icon_enter_normal.png" />
+          </div>
+          <div class="visit8-card__center">
+            <p class="limit-line line-2">{{item.h3}}</p>
+          
+            <div class="visit8-card__center__inner">
+              <p v-if="item.info">{{item.info.time}}</p>
+              <p v-if="item.info">{{item.info.loc}}</p>
+            </div>
+
+            <div class="visit8-card__center__ft">
+              <span v-if="item.tag" class="visit8-card__tag">{{item.tag}}</span>
+            </div>
+          </div>
+          <el-image class="visit8-card__right" :src="item.cover" fit="cover" />
+        </div>
+
+        <div v-if="allList.length === 0" class="empty">no more</div>
+      </div>
+
+      <!-- 分页 -->
+      <div class="page">
+        <span
+          :class="{ active: pageSize === i }"
+          v-for="i in pageNum"
+          :key="i"
+          @click="pageChange(i)"
+          @keydown.enter.passive="pageChange(i)"
+          tabindex="0"
+          aria-label="Link"
+          :aria-description="`page ${i}`"
+        >
+          {{ i }}
+        </span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import { AboutOne, LearnEngage } from '../dataAll';
+  import Calendar from './components/calendar.vue';
+
+  export default {
+    components: {
+      Calendar
+    },
+    data() {
+      return {
+        currentBanner: 0,
+        currentType: 0,
+        selectedDay: new Date(),
+        startDate: new Date(),
+        endDate: null,
+
+        pageNum: 0,
+        pageSize: 1,
+      }
+    },
+    computed: {
+      allList() {
+        let temp = [];
+        let all = this.sortList;
+
+        if (this.startDate) {
+          const currentDate = this.getTimestamp(this.formatDate(this.startDate));
+          const endDate = this.getTimestamp(this.formatDate(this.endDate || new Date()));
+          all = this.sortList.filter(i => {
+            if (Array.isArray(i.date)) {
+              return (i.date[0] <= currentDate && i.date[1] >= currentDate) || (i.date[0] >= currentDate && !this.endDate);
+            }
+            return i.date >= currentDate && (this.endDate ? i.date <= endDate : true)
+          })
+        } else if (this.endDate) {
+          const endDate = this.getTimestamp(this.formatDate(this.endDate || new Date()));
+          all = this.sortList.filter(i => {
+            if (Array.isArray(i.date)) {
+              return i.date[0] <= endDate;
+            }
+            return i.date <= endDate
+          })
+        }
+
+        switch (this.currentType) {
+          case 0:
+            temp = all;
+            break;
+          case 1:
+            temp = [];
+            break;
+          case 2:
+            temp = all.filter(i => i._type === 2);
+            break;
+          case 3:
+            temp = all.filter(i => i._type === 3);
+            break;
+        }
+
+        this.pageNum = Math.ceil(temp.length / 9);
+        let tempArrAll = [];
+        for (let i = 0; i < this.pageNum; i++) {
+          tempArrAll.push(temp.slice(i * 9, (i + 1) * 9));
+        }
+        return tempArrAll;
+      },
+      learnList() {
+        return [...LearnEngage.Students.map(i => ({
+          ...i,
+          date: !Array.isArray(i.info.date) ? this.getTimestamp(i.info.date) : [this.getTimestamp(i.info.date[0]), this.getTimestamp(i.info.date[1])],
+          tag: 'For Students',
+          cover: `/data/LearnEngage/sm/${i.id}.png`,
+          topId: 'Students',
+          _type: 3
+        })), ...LearnEngage.Adults.map(i => ({
+          ...i,
+          date: !Array.isArray(i.info.date) ? this.getTimestamp(i.info.date) : [this.getTimestamp(i.info.date[0]), this.getTimestamp(i.info.date[1])],
+          tag: 'For Adults',
+          topId: 'Adults',
+          cover: `/data/LearnEngage/sm/${i.id}.png`,
+          _type: 3
+        })), ...LearnEngage.Families.map(i => ({
+          ...i,
+          date: !Array.isArray(i.info.date) ? this.getTimestamp(i.info.date) : [this.getTimestamp(i.info.date[0]), this.getTimestamp(i.info.date[1])],
+          tag: 'For Families & Children',
+          topId: 'Families',
+          cover: `/data/LearnEngage/sm/${i.id}.png`,
+          _type: 3
+        }))]
+      },
+      exhibitionList() {
+        return [];
+        // 展览目前都与星期关联,不做展示
+        // return [...dataAll.Exhibitions.Current.map(i => ({
+        //   ...i,
+        //   tag: 'Current Exhibitions'
+        // })), ...dataAll.Exhibitions.Permanent.map(i => ({
+        //   ...i,
+        //   tag: 'Permanent Exhibitions'
+        // })), ...dataAll.Exhibitions.Overseas.map(i => ({
+        //   ...i,
+        //   tag: 'Overseas Exhibitions'
+        // }))]
+      },
+      eventList() {
+        return AboutOne.map(i => {
+          const { date, ...rest } = i
+          return {
+            ...rest,
+            _type: 2,
+            date: typeof date === 'string' ? this.getTimestamp(date) : [this.getTimestamp(date[0]), this.getTimestamp(date[1])]
+          }
+        })
+      },
+      sortList() {
+        const stack = [...this.learnList, ...this.eventList];
+        const currentDate = this.getTimestamp(this.formatDate(this.selectedDay));
+
+        stack.sort((a, b) => {
+          function getEndDate(date) {
+            if (Array.isArray(date)) {
+              return date[1];
+            }
+            return date;
+          }
+
+          const endDateA = getEndDate(a.date);
+          const endDateB = getEndDate(b.date);
+
+          if (endDateA >= currentDate && endDateB >= currentDate) {
+            // 如果两者都是进行中,则按照结束时间早的排在前面
+            return endDateA - endDateB;
+          } else if (endDateA >= currentDate) {
+            // 如果A进行中,B已结束,则A排在前面
+            return -1;
+          } else if (endDateB >= currentDate) {
+            // 如果B进行中,A已结束,则B排在前面
+            return 1;
+          } else {
+            // 如果两者都已结束,则按照结束时间晚的排在前面
+            return endDateB - endDateA;
+          }
+        });
+        return stack;
+      },
+      bannerList() {
+        return this.sortList.slice(0, 5);
+      },
+    },
+    watch: {
+      selectedDay(v) {
+        if (this.$refs.carousel) {
+          this.$refs.carousel.setActiveItem(0);
+          this.currentBanner = 0;
+          this.startDate = v;
+        }
+      },
+      startDate(v) {
+        if (!v || (this.endDate && v.getTime() > this.endDate.getTime())) {
+          this.endDate = null;
+        }
+      }
+    },
+    methods: {
+      handleClick(item) {
+        switch (item._type) {
+          case 2:
+            this.$router.push(`/Layout/EventsInfo/${item.id}`);
+            break;
+          case 3:
+            this.$router.push({
+              name: "LearnEngageInfo",
+              query: { id: item.id, k: item.topId, m: this.pageSize },
+            });
+            break;
+        }
+      },
+      // 切换页码
+      pageChange(val) {
+        this.pageSize = val;
+        window.scrollTo({ top: 900, behavior: "smooth" });
+      },
+      getBannerTag(type) {
+        switch (type) {
+          case 1:
+            return 'Exhibitions';
+          case 2:
+            return 'Events';
+          case 3:
+            return 'Learn & Engage';
+        }
+      },
+      handleType(type) {
+        this.currentType = type;
+      },
+      formatDate(date) {
+        const year = date.getFullYear();
+        const month = (date.getMonth() + 1).toString().padStart(2, '0');
+        const day = date.getDate().toString().padStart(2, '0');
+        return year + '-' + month + '-' + day;
+      },
+      getTimestamp(date) {
+        return new Date(date + ' 00:00:00').getTime();
+      },
+      showDotEvent(date) {
+        const timestamp = new Date(date + ' 00:00:00').getTime();
+        return this.eventList.some(i => {
+          if (typeof i.date === 'number') return i.date === timestamp;
+          return i.date[0] <= timestamp && timestamp <= i.date[1];
+        })
+      },
+      showDotLearn(date) {
+        const timestamp = new Date(date + ' 00:00:00').getTime();
+        return this.learnList.some(i => {
+          if (typeof i.date === 'number') return (i.date) === timestamp;
+          return i.date[0] <= timestamp && timestamp <= i.date[1];
+        })
+      },
+      handleBanner(idx) {
+        this.currentBanner = idx;
+      },
+      getEnDate(d) {
+        const date = new Date(d);
+        const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+        const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+
+        const month = months[date.getMonth()];
+        const day = date.getDate();
+        const dayOfWeek = daysOfWeek[date.getDay()];
+
+        return [`${dayOfWeek},`, month, day];
+      }
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+@import './Visit8.less';
+</style>

+ 121 - 0
web/src/views/Visit/components/calendar.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="panel-rg">
+    <div class="panel-rg__header">
+      <i class="el-icon-arrow-left" @click="selectDate('prev-month')" />
+      <p><span>{{ t('el.datepicker.month' + month) }}</span> {{ date.getFullYear() }}</p>
+      <i class="el-icon-arrow-right" @click="selectDate('next-month')" />
+    </div>
+    <date-table class="panel-rg__date"
+      :date="date"
+      :selected-day="realSelectedDay"
+      :first-day-of-week="1"
+      @pick="pickDay"
+    >
+      <template
+        slot="dateCell"
+        slot-scope="{date, data}">
+        {{ data.day.split('-')[1] }}1
+      </template>
+    </date-table>
+
+    <div class="panel-rg__footer">
+      <p>Exhibitions</p>
+      <p>Events</p>
+      <p>Learn & Engage</p>
+    </div>
+  </div>
+</template>
+
+<script>
+  import fecha from 'element-ui/src/utils/date';
+  import Locale from 'element-ui/src/mixins/locale';
+  import DateTable from 'element-ui/packages/calendar/src/date-table';
+
+  export default {
+    components: {
+      DateTable
+    },
+    mixins: [Locale],
+    props: {
+      value: [Date, String, Number],
+    },
+    provide() {
+      return {
+        elCalendar: this
+      };
+    },
+    data() {
+      return {
+        selectedDay: '',
+        now: new Date()
+      }
+    },
+    computed: {
+      date() {
+        if (!this.value) {
+          if (this.realSelectedDay) {
+            const d = this.selectedDay.split('-');
+            return new Date(d[0], d[1] - 1, d[2]);
+          } else if (this.validatedRange.length) {
+            return this.validatedRange[0][0];
+          }
+          return this.now;
+        } else {
+          return this.toDate(this.value);
+        }
+      },
+      realSelectedDay: {
+        get() {
+          if (!this.value) return this.selectedDay;
+          return this.formatedDate;
+        },
+        set(val) {
+          this.selectedDay = val;
+          const date = new Date(val);
+          this.$emit('input', date);
+        }
+      },
+      prevMonthDatePrefix() {
+        const temp = new Date(this.date.getTime());
+        temp.setDate(0);
+        return fecha.format(temp, 'yyyy-MM');
+      },
+      nextMonthDatePrefix() {
+        const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
+        return fecha.format(temp, 'yyyy-MM');
+      },
+      formatedDate() {
+        return fecha.format(this.date, 'yyyy-MM-dd');
+      },
+      month() {
+        return this.date.getMonth() + 1;
+      }
+    },
+    methods: {
+      pickDay(day) {
+        this.realSelectedDay = day;
+      },
+      toDate(val) {
+        if (!val) {
+          throw new Error('invalid val');
+        }
+        return val instanceof Date ? val : new Date(val);
+      },
+      selectDate(type) {
+        let day = '';
+        if (type === 'prev-month') {
+          day = `${this.prevMonthDatePrefix}-01`;
+        } else if (type === 'next-month') {
+          day = `${this.nextMonthDatePrefix}-01`;
+        }
+
+        if (day === this.formatedDate) return;
+        this.pickDay(day);
+      },
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+@import '../Visit8.less';
+</style>

+ 5 - 0
web/src/views/Visit/index.vue

@@ -93,6 +93,11 @@ export default {
     return {
       topLi: [
         {
+          name: "Calendar",
+          img: "m-16.png",
+          path: "/Layout/Visit/8",
+        },
+        {
           name: "Hours, Direction & Admission",
           img: "m-9.png",
           path: "/Layout/Visit/1",

Plik diff jest za duży
+ 1755 - 1694
web/src/views/dataAll.js


+ 132 - 3
webM/package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.1.0",
       "dependencies": {
         "core-js": "^3.6.5",
+        "element-ui": "^2.15.14",
         "pdfh5": "^1.4.2",
         "vant": "^2.12.47",
         "vue": "^2.6.11",
@@ -3053,6 +3054,14 @@
       "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
       "dev": true
     },
+    "node_modules/async-validator": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz",
+      "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==",
+      "dependencies": {
+        "babel-runtime": "6.x"
+      }
+    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
@@ -3104,6 +3113,11 @@
       "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
       "dev": true
     },
+    "node_modules/babel-helper-vue-jsx-merge-props": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+      "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
+    },
     "node_modules/babel-loader": {
       "version": "8.2.4",
       "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.2.4.tgz",
@@ -3171,6 +3185,27 @@
         "@babel/core": "^7.0.0-0"
       }
     },
+    "node_modules/babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+      "dependencies": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      }
+    },
+    "node_modules/babel-runtime/node_modules/core-js": {
+      "version": "2.6.12",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
+      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+      "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+      "hasInstallScript": true
+    },
+    "node_modules/babel-runtime/node_modules/regenerator-runtime": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+      "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -5172,7 +5207,6 @@
       "version": "1.5.2",
       "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz",
       "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -5647,6 +5681,22 @@
       "integrity": "sha512-ZYfpVLULm67K7CaaGP7DmjyeMY4naxsbTy+syVVxT6QHI1Ww8XbJjmr9fDckrhq44WzCrcC5kH3zGpdusxwwqg==",
       "dev": true
     },
+    "node_modules/element-ui": {
+      "version": "2.15.14",
+      "resolved": "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz",
+      "integrity": "sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==",
+      "dependencies": {
+        "async-validator": "~1.8.1",
+        "babel-helper-vue-jsx-merge-props": "^2.0.0",
+        "deepmerge": "^1.2.0",
+        "normalize-wheel": "^1.0.1",
+        "resize-observer-polyfill": "^1.5.0",
+        "throttle-debounce": "^1.0.1"
+      },
+      "peerDependencies": {
+        "vue": "^2.5.17"
+      }
+    },
     "node_modules/elliptic": {
       "version": "6.5.4",
       "resolved": "https://registry.npmmirror.com/elliptic/-/elliptic-6.5.4.tgz",
@@ -8989,6 +9039,11 @@
         "node": ">=4"
       }
     },
+    "node_modules/normalize-wheel": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
+      "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA=="
+    },
     "node_modules/npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -10925,6 +10980,11 @@
       "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
       "dev": true
     },
+    "node_modules/resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
     "node_modules/resolve": {
       "version": "1.22.0",
       "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.0.tgz",
@@ -12545,6 +12605,14 @@
         "node": ">=4.0.0"
       }
     },
+    "node_modules/throttle-debounce": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz",
+      "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz",
@@ -16783,6 +16851,14 @@
       "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
       "dev": true
     },
+    "async-validator": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz",
+      "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==",
+      "requires": {
+        "babel-runtime": "6.x"
+      }
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
@@ -16822,6 +16898,11 @@
       "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
       "dev": true
     },
+    "babel-helper-vue-jsx-merge-props": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+      "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
+    },
     "babel-loader": {
       "version": "8.2.4",
       "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.2.4.tgz",
@@ -16873,6 +16954,27 @@
         "@babel/helper-define-polyfill-provider": "^0.3.1"
       }
     },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+        },
+        "regenerator-runtime": {
+          "version": "0.11.1",
+          "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+          "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+        }
+      }
+    },
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -18573,8 +18675,7 @@
     "deepmerge": {
       "version": "1.5.2",
       "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz",
-      "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==",
-      "dev": true
+      "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ=="
     },
     "default-gateway": {
       "version": "5.0.5",
@@ -18969,6 +19070,19 @@
       "integrity": "sha512-ZYfpVLULm67K7CaaGP7DmjyeMY4naxsbTy+syVVxT6QHI1Ww8XbJjmr9fDckrhq44WzCrcC5kH3zGpdusxwwqg==",
       "dev": true
     },
+    "element-ui": {
+      "version": "2.15.14",
+      "resolved": "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz",
+      "integrity": "sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==",
+      "requires": {
+        "async-validator": "~1.8.1",
+        "babel-helper-vue-jsx-merge-props": "^2.0.0",
+        "deepmerge": "^1.2.0",
+        "normalize-wheel": "^1.0.1",
+        "resize-observer-polyfill": "^1.5.0",
+        "throttle-debounce": "^1.0.1"
+      }
+    },
     "elliptic": {
       "version": "6.5.4",
       "resolved": "https://registry.npmmirror.com/elliptic/-/elliptic-6.5.4.tgz",
@@ -21707,6 +21821,11 @@
         "sort-keys": "^1.0.0"
       }
     },
+    "normalize-wheel": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
+      "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA=="
+    },
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -23348,6 +23467,11 @@
       "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
       "dev": true
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
     "resolve": {
       "version": "1.22.0",
       "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.0.tgz",
@@ -24728,6 +24852,11 @@
         }
       }
     },
+    "throttle-debounce": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz",
+      "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg=="
+    },
     "through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz",

+ 1 - 0
webM/package.json

@@ -8,6 +8,7 @@
   },
   "dependencies": {
     "core-js": "^3.6.5",
+    "element-ui": "^2.15.14",
     "pdfh5": "^1.4.2",
     "vant": "^2.12.47",
     "vue": "^2.6.11",

+ 15 - 1
webM/src/assets/base.css

@@ -619,4 +619,18 @@ p {
     -o-filter: grayscale(100%);
     filter: grayscale(100%);
     filter: gray;
-} */
+} */
+
+.limit-line {
+    display: -webkit-box;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    -webkit-line-clamp: 1;
+    -webkit-box-orient: vertical;
+    word-break: break-all;
+    word-wrap: break-word;
+}
+
+.line-2 {
+    -webkit-line-clamp: 2;
+}

BIN
webM/src/assets/img/Visit/icon_calender_active.png


BIN
webM/src/assets/img/Visit/icon_enter_normal.png


+ 7 - 1
webM/src/main.js

@@ -1,11 +1,12 @@
 import Vue from 'vue'
 import App from './App.vue'
 import router from './router'
+import 'element-ui/lib/theme-chalk/index.css';
 
 // 引入初始化样式
 import './assets/base.css'
 // 导入vant
-import Vant from 'vant';
+import Vant, { Locale } from 'vant';
 import 'vant/lib/index.css';
 Vue.use(Vant);
 // 图片懒加载
@@ -19,7 +20,12 @@ Vue.use(Lazyload,{
 import VueTouch from 'vue-touch'
 Vue.use(VueTouch, {name: 'v-touch'})
 
+import lang from 'element-ui/src/locale/lang/en'
+import locale from 'element-ui/src/locale'
+import enUS from 'vant/es/locale/lang/en-US'
 
+locale.use(lang)
+Locale.use('en-US', enUS)
 
 Vue.config.productionTip = false
 

+ 2 - 1
webM/src/views/Layout/index.vue

@@ -158,8 +158,9 @@ export default {
         {
           show: "Visit,Reservation",
           name: "Visit",
-          path: "/Layout/Visit/1",
+          path: "/Layout/Visit/8",
           son: [
+            { name: "Calendar", path: "/Layout/Visit/8" },
             { name: "Hours, Direction & Admission", path: "/Layout/Visit/1" },
             { name: "Reservation", path: "/Layout/Visit/2" },
             { name: "Floor Plans", path: "/Layout/Visit/3" },

Plik diff jest za duży
+ 1753 - 1792
webM/src/views/Learn/data.js


+ 215 - 0
webM/src/views/Visit/components/calendar.vue

@@ -0,0 +1,215 @@
+<template>
+  <div class="panel-rg">
+    <div class="panel-rg__header">
+      <i class="el-icon-arrow-left" @click="selectDate('prev-month')" />
+      <p><span>{{ t('el.datepicker.month' + month) }}</span> {{ date.getFullYear() }}</p>
+      <i class="el-icon-arrow-right" @click="selectDate('next-month')" />
+    </div>
+    <date-table class="panel-rg__date"
+      :date="date"
+      :selected-day="realSelectedDay"
+      :first-day-of-week="1"
+      @pick="pickDay"
+    >
+      <template
+        slot="dateCell"
+        slot-scope="{date, data}">
+        {{ data.day.split('-')[1] }}1
+      </template>
+    </date-table>
+
+    <div class="panel-rg__footer">
+      <p>Exhibitions</p>
+      <p>Events</p>
+      <p>Learn & Engage</p>
+    </div>
+  </div>
+</template>
+
+<script>
+  import fecha from 'element-ui/src/utils/date';
+  import Locale from 'element-ui/src/mixins/locale';
+  import DateTable from 'element-ui/packages/calendar/src/date-table';
+
+  export default {
+    components: {
+      DateTable
+    },
+    mixins: [Locale],
+    props: {
+      value: [Date, String, Number],
+    },
+    provide() {
+      return {
+        elCalendar: this
+      };
+    },
+    data() {
+      return {
+        selectedDay: '',
+        now: new Date()
+      }
+    },
+    computed: {
+      date() {
+        if (!this.value) {
+          if (this.realSelectedDay) {
+            const d = this.selectedDay.split('-');
+            return new Date(d[0], d[1] - 1, d[2]);
+          } else if (this.validatedRange.length) {
+            return this.validatedRange[0][0];
+          }
+          return this.now;
+        } else {
+          return this.toDate(this.value);
+        }
+      },
+      realSelectedDay: {
+        get() {
+          if (!this.value) return this.selectedDay;
+          return this.formatedDate;
+        },
+        set(val) {
+          this.selectedDay = val;
+          const date = new Date(val);
+          this.$emit('input', date);
+        }
+      },
+      prevMonthDatePrefix() {
+        const temp = new Date(this.date.getTime());
+        temp.setDate(0);
+        return fecha.format(temp, 'yyyy-MM');
+      },
+      nextMonthDatePrefix() {
+        const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
+        return fecha.format(temp, 'yyyy-MM');
+      },
+      formatedDate() {
+        return fecha.format(this.date, 'yyyy-MM-dd');
+      },
+      month() {
+        return this.date.getMonth() + 1;
+      }
+    },
+    methods: {
+      pickDay(day) {
+        this.realSelectedDay = day;
+      },
+      toDate(val) {
+        if (!val) {
+          throw new Error('invalid val');
+        }
+        return val instanceof Date ? val : new Date(val);
+      },
+      selectDate(type) {
+        let day = '';
+        if (type === 'prev-month') {
+          day = `${this.prevMonthDatePrefix}-01`;
+        } else if (type === 'next-month') {
+          day = `${this.nextMonthDatePrefix}-01`;
+        }
+
+        if (day === this.formatedDate) return;
+        this.pickDay(day);
+      },
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  .panel-rg {
+    margin-top: 18px;
+    padding: 0 14px;
+    box-sizing: border-box;
+
+    &__header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 30px 25px;
+
+      i {
+        cursor: pointer;
+        font-size: 22px;
+        color: #000000;
+      }
+      p {
+        font-size: 28px;
+        font-weight: bold;
+        color: #202020;
+
+        span {
+          color: #C1AA7B;
+        }
+      }
+    }
+    ::v-deep &__date {
+      margin: 0 auto 5px;
+      width: calc(100% - 5px);
+      font-size: 12px;
+      table-layout: fixed;
+
+      th {
+        color: #000;
+        font-weight: 400;
+      }
+      td {
+        border: 0 !important;
+      }
+      .el-calendar-day {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 40px;
+        color: #000000;
+
+        &:hover {
+          background-color: transparent;
+        }
+      }
+      .is-selected {
+        background-color: transparent;
+
+        .el-calendar-day {
+          color: #CC1424;
+          font-weight: bold;
+        }
+      }
+      .prev div,
+      .next div {
+        display: none;
+      }
+    }
+    &__footer {
+      display: flex;
+      justify-content: space-between;
+      padding: 15px 28px 0;
+      font-size: 12px;
+      border-top: 1px dashed rgba(32, 32, 32, .5);
+
+      p {
+        position: relative;
+        padding-left: 12px;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 50%;
+          left: 0;
+          width: 6px;
+          height: 6px;
+          border-radius: 6px;
+          transform: translateY(-50%);
+          background-color: #D2B986;
+        }
+        &:first-child::before {
+          background-color: #BC1C24;
+        }
+        &:last-child::before {
+          background-color: #95D2FF;
+        }
+      }
+    }
+  }
+</style>

+ 547 - 0
webM/src/views/Visit/datePanel.vue

@@ -0,0 +1,547 @@
+<template>
+  <div class="visit8">
+    <div class="banner8">
+      <van-swipe :show-indicators="false" @change="(index) => currentBanner = index">
+        <van-swipe-item v-for="item in bannerList" :key="`${item.id}-${selectedDay.getTime()}`">
+          <van-image
+            width="100%"
+            height="211px"
+            :src="item.cover"
+            style="cursor: pointer;"
+            @click="handleClick(item)"
+          />
+        </van-swipe-item>
+      </van-swipe>
+
+      <div class="banner8__footer">
+        <div><p class="limit-line">{{bannerList[currentBanner].h3}}</p><span>{{getBannerTag(bannerList[currentBanner]._type)}}</span></div>
+        <p>{{bannerList[currentBanner].i || bannerList[currentBanner].info.time}}</p>
+      </div>
+    </div>
+
+    <div class="choose-date">
+      <div :class="['choose-date__input', !startDate && 'placeholder']"
+        @click="showStartDatePicker = true"
+      >{{startDateLabel}}</div>
+      <div :class="['choose-date__input', !endDate && 'placeholder']"
+        @click="showEndDatePicker = true"
+      >{{endDateLabel}}</div>
+      <div class="choose-date__icon">
+        <img src="@/assets/img/Visit/icon_calender_active.png" @click="showDate = !showDate" />
+      </div>
+    </div>
+
+    <Calendar v-show="showDate" v-model="selectedDay">
+      <template
+        slot="dateCell"
+        slot-scope="{date, data}">
+        {{ data.day.split('-')[2] }}
+        <div class="dots">
+          <!-- <div class="dot" /> -->
+          <div v-if="showDotEvent(data.day)" class="dot events" />
+          <div v-if="showDotLearn(data.day)" class="dot learn" />
+        </div>
+      </template>
+    </Calendar>
+
+    <div class="type-container">
+      <div>
+        <div v-for="item in TYPE_LIST" :key="item.value"
+          :class="[currentType === item.value && 'active']"
+          @click="currentType = item.value"
+        >{{item.label}}</div>
+      </div>
+    </div>
+
+    <div class="main__list">
+      <div v-for="item in allList.slice((pageNum - 1) * pageSize, pageNum * pageSize)" :key="item.id" class="visit8-card" @click="handleClick(item)">
+        <div class="visit8-card__top">
+          <span v-for="txt in getEnDate(formatDate(new Date(Array.isArray(item.date) ? item.date[0] : item.date)))" :key="txt">{{ txt }}</span>
+        </div>
+
+        <van-image class="visit8-card__right" :src="item.cover" height="177px" />
+
+        <div class="visit8-card__main">
+          <p class="limit-line line-2">{{item.h3}}</p>
+
+          <div class="visit8-card__main__inner">
+            <p v-if="item.info">·{{item.info.time}}</p>
+            <p v-if="item.info">·{{item.info.loc}}</p>
+          </div>
+
+          <div class="visit8-card__main__bt">
+            <span v-if="item.tag" class="visit8-card__tag">{{item.tag}}</span>
+            <img src="@/assets/img/Visit/icon_enter_normal.png" />
+          </div>
+        </div>
+      </div>
+
+      <div v-if="allList.length > 0" style="text-align: center;">
+        <Pagination
+          background
+          layout="prev, pager, next"
+          :total="allList.length"
+          :page-size="pageSize"
+          :pager-count="5"
+          @current-change="num => pageNum = num"
+        />
+      </div>
+
+      <div v-else class="empty">no more</div>
+    </div>
+
+    <van-calendar v-model="showStartDatePicker" :min-date="minDate" @confirm="handleStartDateConfirm" />
+    <van-calendar v-model="showEndDatePicker" :min-date="startDate" @confirm="handleEndDateConfirm" />
+  </div>
+</template>
+
+<script>
+  import { LearnEngage } from '../Learn/data';
+  import { AboutOne } from '../bottom/Events/data';
+  import { Pagination } from 'element-ui';
+  import Calendar from './components/calendar.vue';
+
+  const TYPE_LIST = [
+    { label: 'All', value: 0 },
+    { label: 'Exhibitions', value: 1 },
+    { label: 'Events', value: 2 },
+    { label: 'Learn & Engage', value: 3 },
+  ]
+
+  export default {
+    components: {
+      Calendar,
+      Pagination
+    },
+    data() {
+      return {
+        TYPE_LIST,
+        currentBanner: 0,
+        currentType: TYPE_LIST[0].value,
+        selectedDay: new Date(),
+        startDate: new Date(),
+        endDate: null,
+        showDate: false,
+
+        showStartDatePicker: false,
+        showEndDatePicker: false,
+        minDate: new Date(2005, 0, 1),
+
+        pageNum: 1,
+        pageSize: 3,
+      }
+    },
+    computed: {
+      startDateLabel() {
+        if (!this.startDate) return 'Start Date';
+        return this.formatDate(this.startDate, true)
+      },
+      endDateLabel() {
+        if (!this.endDate) return 'End Date';
+        return this.formatDate(this.endDate, true)
+      },
+      allList() {
+        let temp = [];
+        let all = this.sortList;
+
+        if (this.startDate) {
+          const currentDate = this.getTimestamp(this.formatDate(this.startDate));
+          const endDate = this.getTimestamp(this.formatDate(this.endDate || new Date()));
+          all = this.sortList.filter(i => {
+            if (Array.isArray(i.date)) {
+              return (i.date[0] <= currentDate && i.date[1] >= currentDate) || (i.date[0] >= currentDate && !this.endDate);
+            }
+            return i.date >= currentDate && (this.endDate ? i.date <= endDate : true)
+          })
+        } else if (this.endDate) {
+          const endDate = this.getTimestamp(this.formatDate(this.endDate || new Date()));
+          all = this.sortList.filter(i => {
+            if (Array.isArray(i.date)) {
+              return i.date[0] <= endDate;
+            }
+            return i.date <= endDate
+          })
+        }
+
+        switch (this.currentType) {
+          case 0:
+            temp = all;
+            break;
+          case 1:
+            temp = [];
+            break;
+          case 2:
+            temp = all.filter(i => i._type === 2);
+            break;
+          case 3:
+            temp = all.filter(i => i._type === 3);
+            break;
+        }
+
+        return temp;
+      },
+      learnList() {
+        return [...LearnEngage.Students.map(i => ({
+          ...i,
+          date: !Array.isArray(i.info.date) ? this.getTimestamp(i.info.date) : [this.getTimestamp(i.info.date[0]), this.getTimestamp(i.info.date[1])],
+          tag: 'For Students',
+          cover: `/data/LearnEngage/sm/${i.id}.png`,
+          topId: 'Students',
+          _type: 3
+        })), ...LearnEngage.Adults.map(i => ({
+          ...i,
+          date: !Array.isArray(i.info.date) ? this.getTimestamp(i.info.date) : [this.getTimestamp(i.info.date[0]), this.getTimestamp(i.info.date[1])],
+          tag: 'For Adults',
+          topId: 'Adults',
+          cover: `/data/LearnEngage/sm/${i.id}.png`,
+          _type: 3
+        })), ...LearnEngage.Families.map(i => ({
+          ...i,
+          date: !Array.isArray(i.info.date) ? this.getTimestamp(i.info.date) : [this.getTimestamp(i.info.date[0]), this.getTimestamp(i.info.date[1])],
+          tag: 'For Families & Children',
+          topId: 'Families',
+          cover: `/data/LearnEngage/sm/${i.id}.png`,
+          _type: 3
+        }))]
+      },
+      exhibitionList() {
+        return [];
+        // 展览目前都与星期关联,不做展示
+        // return [...dataAll.Exhibitions.Current.map(i => ({
+        //   ...i,
+        //   tag: 'Current Exhibitions'
+        // })), ...dataAll.Exhibitions.Permanent.map(i => ({
+        //   ...i,
+        //   tag: 'Permanent Exhibitions'
+        // })), ...dataAll.Exhibitions.Overseas.map(i => ({
+        //   ...i,
+        //   tag: 'Overseas Exhibitions'
+        // }))]
+      },
+      eventList() {
+        return AboutOne.map(i => {
+          const { date, ...rest } = i
+          return {
+            ...rest,
+            _type: 2,
+            date: typeof date === 'string' ? this.getTimestamp(date) : [this.getTimestamp(date[0]), this.getTimestamp(date[1])]
+          }
+        })
+      },
+      sortList() {
+        const stack = [...this.learnList, ...this.eventList];
+        const currentDate = this.getTimestamp(this.formatDate(this.selectedDay));
+
+        stack.sort((a, b) => {
+          function getEndDate(date) {
+            if (Array.isArray(date)) {
+              return date[1];
+            }
+            return date;
+          }
+
+          const endDateA = getEndDate(a.date);
+          const endDateB = getEndDate(b.date);
+
+          if (endDateA >= currentDate && endDateB >= currentDate) {
+            // 如果两者都是进行中,则按照结束时间早的排在前面
+            return endDateA - endDateB;
+          } else if (endDateA >= currentDate) {
+            // 如果A进行中,B已结束,则A排在前面
+            return -1;
+          } else if (endDateB >= currentDate) {
+            // 如果B进行中,A已结束,则B排在前面
+            return 1;
+          } else {
+            // 如果两者都已结束,则按照结束时间晚的排在前面
+            return endDateB - endDateA;
+          }
+        });
+        return stack;
+      },
+      bannerList() {
+        return this.sortList.slice(0, 5);
+      },
+    },
+    watch: {
+      selectedDay(v) {
+        if (this.$refs.carousel) {
+          this.$refs.carousel.setActiveItem(0);
+          this.currentBanner = 0;
+          this.startDate = v;
+        }
+      },
+      startDate(v) {
+        if (!v || (this.endDate && v.getTime() > this.endDate.getTime())) {
+          this.endDate = null;
+        }
+      }
+    },
+    methods: {
+      handleStartDateConfirm(date) {
+        this.startDate = date;
+        this.showStartDatePicker = false;
+      },
+      handleEndDateConfirm(date) {
+        this.endDate = date;
+        this.showEndDatePicker = false;
+      },
+      handleClick(item) {
+        switch (item._type) {
+          case 2:
+            this.$router.push(`/Layout/EventsInfo/${item.id}`);
+            break;
+          case 3:
+            this.$router.push({
+              name: "LearnEngageInfo",
+              query: { id: item.id, k: item.topId, m: this.pageSize },
+            });
+            break;
+        }
+      },
+      // 切换页码
+      pageChange(val) {
+        this.pageSize = val;
+        window.scrollTo({ top: 900, behavior: "smooth" });
+      },
+      getBannerTag(type) {
+        switch (type) {
+          case 1:
+            return 'Exhibitions';
+          case 2:
+            return 'Events';
+          case 3:
+            return 'Learn & Engage';
+        }
+      },
+      handleType(type) {
+        this.currentType = type;
+      },
+      formatDate(date, isLabel) {
+        const year = date.getFullYear();
+        const month = (date.getMonth() + 1).toString().padStart(2, '0');
+        const day = date.getDate().toString().padStart(2, '0');
+        return isLabel ? month + '/' + day + '/' + year : year + '-' + month + '-' + day;
+      },
+      getTimestamp(date) {
+        return new Date(date + ' 00:00:00').getTime();
+      },
+      showDotEvent(date) {
+        const timestamp = new Date(date + ' 00:00:00').getTime();
+        return this.eventList.some(i => {
+          if (typeof i.date === 'number') return i.date === timestamp;
+          return i.date[0] <= timestamp && timestamp <= i.date[1];
+        })
+      },
+      showDotLearn(date) {
+        const timestamp = new Date(date + ' 00:00:00').getTime();
+        return this.learnList.some(i => {
+          if (typeof i.date === 'number') return (i.date) === timestamp;
+          return i.date[0] <= timestamp && timestamp <= i.date[1];
+        })
+      },
+      handleBanner(idx) {
+        this.currentBanner = idx;
+      },
+      getEnDate(d) {
+        const date = new Date(d);
+        const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+        const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+
+        const month = months[date.getMonth()];
+        const day = date.getDate();
+        const dayOfWeek = daysOfWeek[date.getDay()];
+
+        return [`${dayOfWeek} , `, month, day];
+      }
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+.dots {
+  display: flex;
+  gap: 5px;
+  margin-top: 3px;
+  height: 5px;
+
+  .dot {
+    width: 5px;
+    height: 5px;
+    border-radius: 5px;
+    background-color: #BC1C24;
+
+    &.events {
+      background-color: #D2B986;
+    }
+    &.learn {
+      background-color: #95D2FF;
+    }
+  }
+}
+.type-container {
+  padding: 0 11px;
+  margin: 20px 0 5px;
+  overflow-y: auto;
+
+  div {
+    display: flex;
+    gap: 5px;
+    height: 44px;
+    margin-bottom: 10px;
+
+    div {
+      padding: 0 16px;
+      height: 42px;
+      line-height: 42px;
+      font-size: 14px;
+      color: #202020;
+      white-space: nowrap;
+      background: rgba(255,255,255,0.2);
+      border: 1px solid #D2B986;
+      border-radius: 5px;
+      box-sizing: border-box;
+
+      &.active {
+        border: none;
+        color: white;
+        background-color: #BC1C24;
+      }
+    }
+  }
+  }
+.empty {
+  text-align: center;
+  font-size: 16px;
+  padding: 20px 0;
+  color: #666666;
+}
+::v-deep .el-pagination.is-background .el-pager li:not(.disabled).active {
+  background-color: #BC1C24;
+}
+.banner8 {
+  &__footer {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    padding: 7px 15px 10px;
+    font-size: 14px;
+    color: #000;
+    background-color: white;
+
+    div:first-child {
+      display: flex;
+      align-items: center;
+      font-weight: bold;
+      font-size: 16px;
+
+      p {
+        flex: 1;
+        width: 0;
+        line-height: 24px;
+      }
+      span {
+        margin-left: 16px;
+        padding: 5px;
+        font-size: 12px;
+        color: #D2B986;
+        background: rgba(210,185,134,0.3);
+        border-radius: 4px;
+        white-space: nowrap;
+        border: 1px solid #D2B986;
+      }
+    }
+  }
+}
+.main__list {
+  padding: 0 20px;
+}
+.visit8-card {
+  display: flex;
+  flex-direction: column;
+  background-color: white;
+  margin-bottom: 15px;
+  padding: 10px 15px 25px;
+  box-shadow: 0px 3px 6px rgba(68,58,54,0.16);
+
+  &__top {
+    margin-bottom: 10px;
+    color: #D2B986;
+    font-size: 20px;
+    font-weight: bold;
+  }
+  &__main {
+    display: flex;
+    flex-direction: column;
+
+    > p {
+      margin: 10px 0;
+      font-weight: bold;
+      font-size: 16px;
+      line-height: 18px;
+    }
+    &__inner {
+      color: #6A6A6A;
+      font-size: 14px;
+      
+      p {
+        line-height: 18px;
+      }
+    }
+    &__bt {
+      display: flex;
+      align-items: flex-end;
+      justify-content: space-between;
+      margin-top: 15px;
+
+      img {
+        width: 33px;
+        height: 33px;
+      }
+    }
+  }
+  &__tag {
+    height: 30px;
+    line-height: 30px;
+    padding: 0 10px;
+    color: #D2B986;
+    font-size: 12px;
+    background: rgba(210,185,134,0.12);
+    border: 1px solid #D2B986;
+    border-radius: 5px;
+  }
+}
+.choose-date {
+  display: flex;
+  gap: 5px;
+  padding: 18px 10px 0;
+
+  &__input {
+    flex: 1;
+    padding: 0 14px;
+    height: 44px;
+    line-height: 42px;
+    background: rgba(255,255,255,0.2);
+    border: 1px solid #D2B986;
+    border-radius: 5px;
+    font-size: 14px;
+    color: #202020;
+
+    &.placeholder {
+      color: #999;
+    }
+  }
+  &__icon {
+    flex: 0 0 44px;
+    height: 44px;
+    background: #D2B986;
+    border-radius: 5px;
+    box-sizing: border-box;
+
+    img {
+      width: 44px;
+      height: 44px;
+    }
+  }
+}
+</style>

+ 15 - 3
webM/src/views/Visit/index.vue

@@ -18,6 +18,10 @@
           </div>
         </div>
       </div>
+      <!-- 8 -->
+      <div id="Visit8">
+        <DatePanel />
+      </div>
       <!-- 1 -->
       <div id="Visit1">
         <h3>Hours</h3>
@@ -136,13 +140,15 @@
 <script>
 import Card from "@/components/card.vue";
 import { ImagePreview } from "vant";
+import DatePanel from './datePanel.vue';
 export default {
   name: "Visit",
-  components: { Card },
+  components: { Card, DatePanel },
   data() {
     //这里存放数据
     return {
       tabData: [
+        { name: "Calendar", path: "/Layout/Visit/8" },
         { name: "Hours, Direction & Admission", path: "/Layout/Visit/1" },
         { name: "Reservation", path: "/Layout/Visit/2" },
         { name: "Floor Plans", path: "/Layout/Visit/3" },
@@ -290,6 +296,7 @@ export default {
         let Visit4 = document.querySelector("#Visit4");
         let Visit5 = document.querySelector("#Visit5");
         let Visit6 = document.querySelector("#Visit6");
+        let Visit8 = document.querySelector("#Visit8");
         let temp;
         scrollDom.onscroll = () => {
           if (scrollDom.scrollTop > banDom.offsetHeight) {
@@ -326,6 +333,11 @@ export default {
             Visit6.offsetTop + Visit6.offsetHeight - 70
           )
             temp = 5;
+          else if (
+            scrollDom.scrollTop <
+            Visit8.offsetTop + Visit8.offsetHeight - 70
+          )
+            temp = 8;
           else temp = 6;
           this.erjiTop(temp);
         };
@@ -591,8 +603,8 @@ export default {
       z-index: 99;
       position: fixed;
       top: 0;
-      left: 50%;
-      transform: translateX(-50%);
+      left: 0;
+      right: 0;
     }
   }
 }

+ 5 - 0
webM/src/views/bottom/Events/data.js

@@ -12,6 +12,7 @@ export const AboutOne = [
       { name: ' "Museum Night" at Capital Museum of China', path: 1.4 },
     ],
     i: 'May 18, 2021',
+    date: '2021-05-18'
   },
   {
     id: 2,
@@ -24,12 +25,14 @@ export const AboutOne = [
       },
     ],
     i: 'October 27 - October 28, 2019',
+    date: ['2019-10-27', '2019-10-28']
   },
   {
     id: 3,
     h3: "A Bright New Year Opens for Capital Museum",
     txt: [],
     i: 'January 1, 2019',
+    date: '2019-01-01'
   },
   {
     id: 4,
@@ -42,12 +45,14 @@ export const AboutOne = [
       },
     ],
     i: 'August 14, 2018- August 15, 2018',
+    date: ['2018-08-14','2018-08-15']
   },
   {
     id: 5,
     h3: "Exhibition 'Chinese Traditional Custom of the Birthday Celebration for the Senior Citizen' Opens in Mauritius",
     txt: [],
     i: 'February 10, 2018 - March 5, 2018',
+    date: ['2018-02-10','2018-03-05']
   },
 ]
 export const Events = [