Browse Source

添加一些文件

任一存 2 năm trước cách đây
mục cha
commit
979dba5c53
4 tập tin đã thay đổi với 290 bổ sung7 xóa
  1. 108 0
      src/components/BackTop.vue
  2. 85 0
      src/utils/index.js
  3. 96 6
      src/views/Serve/index.vue
  4. 1 1
      vue.config.js

+ 108 - 0
src/components/BackTop.vue

@@ -0,0 +1,108 @@
+<template>
+  <div class="back-top" @click="onClickBackTop" v-show="isShowBackTopBtn">
+    <slot>
+      <div class="back-top__default">回到顶部</div>
+    </slot>
+  </div>
+</template>
+
+<script>
+// const TWEEN = require('@tweenjs/tween.js')
+
+export default({
+  props: {
+    targetId: {
+      type: String,
+      required: true,
+    },
+    triggerDistance: {
+      type: Number,
+      default: 200,
+    }
+  },
+  data() {
+    return {
+      target: null,
+      isShowBackTopBtn: false,
+      isBackingTop: false,
+    }
+  },
+  methods: {
+    onClickBackTop() {
+      if (this.isBackingTop) {
+        return
+      }
+      this.isBackingTop = true
+
+      // const tweenTarget = {
+      //   scrollTop: this.target.scrollTop
+      // }
+      // new TWEEN.Tween(tweenTarget)
+      //   .to({scrollTop: 0}, 800)
+      //   .easing(TWEEN.Easing.Quartic.Out)
+      //   .onUpdate(() => {
+      //     this.target.scrollTop = tweenTarget.scrollTop
+      //   })
+      //   .onComplete(() => {
+      //     this.isBackingTop = false
+      //   })
+      //   .start()
+      
+      // const animate = (time) => {
+      //   if (this.isBackingTop) {
+      //     requestAnimationFrame(animate)
+      //     TWEEN.update(time)
+      //   }
+      // }
+      // requestAnimationFrame(animate)
+
+      // 不想引入tween.js的话,可以用这段简单的匀速滚动代码
+      const startTime = Date.now()
+      const totalScroll = this.target.scrollTop
+      const fn = () => {
+        if (this.target.scrollTop === 0) {
+          this.isBackingTop = false
+          return
+        } 
+
+        const nowTime = Date.now()
+        const assumeScrollTop = totalScroll - (nowTime - startTime) * totalScroll / 500
+        this.target.scrollTop = assumeScrollTop > 0 ? assumeScrollTop : 0
+        requestAnimationFrame(fn)
+      }
+      requestAnimationFrame(fn)
+    },
+    onTargetScroll: globalUtils.debounce(function(e) {
+      if (this.isBackingTop) {
+        return
+      }
+      if (e.target.scrollTop >= this.triggerDistance) {
+        this.isShowBackTopBtn = true
+      } else {
+        this.isShowBackTopBtn = false
+      }
+    }),
+  },
+  mounted() {
+    this.target = document.getElementById(this.targetId)
+    if (this.target) {
+      this.target.addEventListener('scroll', this.onTargetScroll, {
+        passive: true,
+      })
+    }
+  },
+  unmounted() {
+    if (this.target) {
+      this.target.removeEventListener('scroll', this.onTargetScroll, {
+        passive: true,
+      })
+    }
+  }
+})
+</script>
+
+<style scoped lang="less">
+.back-top__default {
+  cursor: pointer;
+}
+</style>

+ 85 - 0
src/utils/index.js

@@ -0,0 +1,85 @@
+/**
+ * 返回一个自带消抖效果的函数,下文用fnDebounced表示。
+ *
+ * fn: 需要被消抖的函数
+ * delay: 消抖时长
+ * isImmediateCall: 是否在一组操作中的第一次调用时立即执行fn
+ * isRememberLastCall:是否在一组中最后一次调用后等delay时长再执行fn
+ * 
+ * 如果isRememberLastCall为false,意味着fn不会被延迟执行,所以fnDebounced执行时,要么在内部调用fn,同步返回fn返回值;要么内部决定本次不调用fn,同步返回null。
+ * 如果isRememberLastCall为true,意味着fn可能被延迟执行,所以fnDebounced会返回一个Promise,在fn被调用时用其返回值resolve该Promise,或者在fn的延时调用计划被取消时用null reject该Promise。
+ */
+export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLastCall = true) {
+  console.assert(isImmediateCall || isRememberLastCall, 'isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!')
+  let timer = null
+  let retPromiseLastTimeRejector = null
+  // 上次调用的时刻
+  let lastCallTime = 0
+
+  if (isImmediateCall && !isRememberLastCall) {
+    return function (...args) {
+      let ret = null
+      const currentTime = Date.now()
+      if (currentTime - lastCallTime >= delay) {
+        ret = fn.apply(this, args)
+      }
+      lastCallTime = currentTime
+      return ret
+    }
+  } else if (!isImmediateCall && isRememberLastCall) {
+    return function (...args) {
+      if (timer) {
+        clearTimeout(timer)
+        timer = null
+      }
+      if (retPromiseLastTimeRejector) {
+        retPromiseLastTimeRejector(null)
+        retPromiseLastTimeRejector = null
+      }
+      const ret = new Promise((resolve, reject) => {
+        retPromiseLastTimeRejector = reject
+        timer = setTimeout(() => {
+          timer = null
+          retPromiseLastTimeRejector = null
+          resolve(fn.apply(this, args))
+        }, delay)
+      })
+      return ret
+    }
+  } else if (isImmediateCall && isRememberLastCall) {
+    return function (...args) {
+      const currentTime = Date.now()
+      if (currentTime - lastCallTime >= delay) { // 一组操作中的第一次
+        const res = fn.apply(this, args) 
+        lastCallTime = currentTime
+        return Promise.resolve(res)
+      } else { // 一组中的后续调用
+        if (timer) { // 在此之前存在中间调用
+          lastCallTime = currentTime
+          clearTimeout(timer)
+          timer = null
+        }
+        if (retPromiseLastTimeRejector) {
+          retPromiseLastTimeRejector(null)
+          retPromiseLastTimeRejector = null
+        }
+        const ret = new Promise((resolve, reject) => {
+          retPromiseLastTimeRejector = reject
+          timer = setTimeout(() => {
+            lastCallTime = 0
+            timer = null
+            retPromiseLastTimeRejector = null
+            resolve(fn.apply(this, args))
+          }, delay)
+        })
+        return ret
+      }
+    }
+  } else {
+    console.error('不应该执行到这里!')
+  }
+}
+
+export default {
+  debounce,
+}

+ 96 - 6
src/views/Serve/index.vue

@@ -1,15 +1,102 @@
 <template>
-<div class='Serve'>享·服务</div>
+  <div class="service" id="service-first-page">
+    <div class="banner">
+      <div class="search-wrap">
+        <img class="icon" src="" alt="" draggable="false">
+        <input
+          v-model.trim="searchKeyword"
+          maxlength="30"
+          type="text"
+          placeholder="输入您感兴趣的内容"
+          @keydown.enter="onClickSearch"
+        >
+        <button
+          class="search"
+          @click="onClickSearch"
+        >
+          搜索
+        </button>
+      </div>
+    </div>
+    
+    <menu>
+      <router-link to="">
+        <img src="" alt="" draggable="false">
+        推荐路线
+      </router-link>
+      <router-link to="">
+        <img src="" alt="" draggable="false">
+        必玩景点
+      </router-link>
+      <router-link to="">
+        <img src="" alt="" draggable="false">
+        美食
+      </router-link>
+      <router-link to="">
+        <img src="" alt="" draggable="false">
+        出行
+      </router-link>
+      <router-link to="">
+        <img src="" alt="" draggable="false">
+        酒店住宿
+      </router-link>
+    </menu>
+
+    <div class="travel-route-1">
+      <router-link>一日游推荐路线</router-link>
+    </div>
+
+    <div class="travel-route-2">
+      <h3>推荐路线</h3>
+      <router-link>更多  》</router-link>
+      <!-- todo -->
+    </div>
+
+    <div class="travel-plague">
+      <h3>出游防疫</h3>
+      <router-link>更多  》</router-link>
+      <!-- todo tab -->
+      <div class="swiper">
+        <!-- todo -->
+      </div>
+      <!-- todo 服务电话 -->
+    </div>
+
+    <div class="help-center">
+      <h3>帮助中心</h3>
+      <router-link>更多  》</router-link>
+      <!-- todo tab -->
+      <ul class="questions">
+        <li v-for="n in 10" :key="n">
+          <h4>芜湖全员核酸检测点最新安排出炉</h4>
+          <span class="name">佚名用户</span>
+          <span class="time">2022-10-10</span>
+        </li>
+      </ul>
+      <ul class="phones">
+        <li v-for="n in 10" :key="n">
+          <h4>芜湖全员核酸检测点最新安排出炉</h4>
+          <span class="name">佚名用户</span>
+          <span class="time">2022-10-10</span>
+        </li>
+      </ul>
+    </div>
+
+    <BackTop class="back-top" :targetId="'service-first-page'"></BackTop>
+  </div>
 </template>
 
 <script>
+import BackTop from "@/components/BackTop.vue";
 
 export default {
-components: {},
+components: {
+  BackTop
+},
 data() {
-return {
-
-};
+  return {
+    searchKeyword: '',
+  };
 },
 computed: {},
 watch: {},
@@ -31,6 +118,9 @@ destroyed() {}, //生命周期 - 销毁完成
 activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
 }
 </script>
-<style lang='less' scoped>
 
+<style lang='less' scoped>
+.service {
+  background-color: #f0f0f0;
+}
 </style>

+ 1 - 1
vue.config.js

@@ -18,7 +18,7 @@ module.exports = {
         globalMapGetters: ['vuex', 'mapGetters'],
         // globalConfig: ['/src/config.js', 'default'],
         // globalApi: ['/src/api.js', 'default'],
-        // globalUtils: ['/src/utils.js', 'default'],
+        globalUtils: ['/src/utils/index.js', 'default'],
       }),
     ],
   },