Selaa lähdekoodia

维修预约接口1

冯诚 3 vuotta sitten
vanhempi
commit
bb355f15ab

+ 4 - 1
src/App.vue

@@ -51,11 +51,12 @@
 </template>
 
 <script setup lang="ts">
-import { computed, watch } from 'vue'
+import { computed, watch, onMounted } from 'vue'
 import { useRoute } from 'vue-router'
 import { state } from './store'
 import NavBar from './components/nav-bar/index.vue'
 import PageFooter from './components/footer/index.vue'
+import getLocation from './utils/getLocation'
 
 const route = useRoute()
 const navBarIgnore = ['/login', '/register']
@@ -65,6 +66,8 @@ watch(
   () => route.path,
   () => (state.bgWhite = false)
 )
+
+onMounted(getLocation)
 </script>
 
 <style lang="scss">

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 2 - 0
src/assets/loading.svg


+ 103 - 0
src/components/infinite-list/index.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="infinite-list">
+    <div class="infinite-list__container">
+      <slot />
+    </div>
+    <div ref="indicator" class="infinite-list__indicator">
+      <i v-if="loading" class="loading" />
+      <span v-else-if="!hasMore" class="nomore">THE END</span>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watchEffect } from 'vue'
+
+const props = withDefaults(
+  defineProps<{
+    loading: boolean
+    hasMore: boolean
+    threshold?: number
+    scrollTarget?: string
+  }>(),
+  {
+    threshold: 150,
+  }
+)
+const emit = defineEmits<{
+  (e: 'loadmore'): void
+}>()
+
+const indicator = ref<HTMLElement>()
+
+watchEffect(onInvalidate => {
+  if (!indicator.value) return
+  const io = new IntersectionObserver(
+    ([entry]) => {
+      if (entry.isIntersecting && !props.loading && props.hasMore) {
+        emit('loadmore')
+      }
+    },
+    {
+      root: props.scrollTarget
+        ? document.querySelector(props.scrollTarget)
+        : null,
+      rootMargin: `0px 0px ${props.threshold}px 0px`,
+    }
+  )
+  io.observe(indicator.value)
+
+  onInvalidate(() => io.disconnect())
+})
+</script>
+
+<style lang="scss">
+.infinite-list {
+  overflow: auto;
+
+  &__indicator {
+    margin: 20px 0;
+    height: 48px;
+    line-height: 48px;
+    text-align: center;
+    font-size: 24px;
+    color: #999;
+  }
+
+  .loading {
+    @include icon('@img/loading.svg', 48px);
+    display: inline-block;
+    vertical-align: top;
+    animation: rotate 1s ease-in-out infinite;
+  }
+
+  .nomore {
+    position: relative;
+    &:after,
+    &:before {
+      content: '';
+      position: absolute;
+      top: 50%;
+      transform: scale(0.5) translate(0, -50%);
+      height: 1px;
+      width: 40px;
+      background-color: #e5e5e5;
+    }
+    &:before {
+      left: -50px;
+    }
+    &:after {
+      right: -50px;
+    }
+  }
+
+  @keyframes rotate {
+    0% {
+      transform: rotate(0deg);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+}
+</style>

+ 57 - 29
src/pages/appointment/StepOne.vue

@@ -17,44 +17,72 @@
       </div>
       <div class="shop-wrap">
         <div class="tip">Nearby shops</div>
-        <ul class="shop-list">
-          <li class="shop-item border-bottom" @click="state.step++">
-            <div class="shop-name">
-              <span>PTC Browns Plains Kiosk</span>
-              <span>3.5KM</span>
-            </div>
-            <div class="shop-address">
-              Browns Plains Grand Plaza Shopping Centre, Shop K007 27-49 Browns
-              Plains Road, Browns Plains, QLD, 4118 (07) 3059 1014
-            </div>
-            <div class="shop-mark">
-              <i class="icon"></i>Available for appointment today
-            </div>
-          </li>
-          <li class="shop-item">
-            <div class="shop-name">
-              <span>PTC Browns Plains Kiosk</span>
-              <span>3.5KM</span>
-            </div>
-            <div class="shop-address">
-              Browns Plains Grand Plaza Shopping Centre, Shop K007 27-49 Browns
-              Plains Road, Browns Plains, QLD, 4118 (07) 3059 1014
-            </div>
-            <div class="shop-mark danger">
-              <i class="icon"></i>Available for appointment today
-            </div>
-          </li>
-        </ul>
+        <InfiniteList
+          class="shop-list-wrap"
+          :loading="loading"
+          :has-more="hasMore"
+          scroll-target=".shop-list-wrap"
+          @loadmore="fetchData"
+        >
+          <ul class="shop-list">
+            <li
+              v-for="(item, index) of list"
+              :key="index"
+              class="shop-item border-bottom"
+              @click="state.step++"
+            >
+              <div class="shop-name">
+                <span>{{ item.name }}</span>
+                <span>3.5KM</span>
+              </div>
+              <div class="shop-address">
+                {{ item.shop_detail }},{{ item.address }}
+              </div>
+              <div class="shop-mark" :class="{ danger: !item.can_appointment }">
+                <i class="icon"></i
+                >{{
+                  item.can_appointment
+                    ? item.can_appointment_day
+                    : 'No appointments available for the next 7 days'
+                }}
+              </div>
+            </li>
+          </ul>
+        </InfiniteList>
       </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
+import { ref, reactive } from 'vue'
 import { state } from './store'
+import { getShopList } from '@/service/repair'
+import getLocation from '@/utils/getLocation'
+import InfiniteList from '@/components/infinite-list/index.vue'
 
 const showSuggestions = ref(false)
+const loading = ref(false)
+const hasMore = ref(true)
+const list = ref<any[]>()
+let pageNo = 1
+let coords: any = null
+
+async function fetchData() {
+  loading.value = true
+  try {
+    if (!list.value) {
+      list.value = []
+      const res = await getLocation({ timeout: 2000 })
+      coords = res?.coords
+    }
+    const { results, pageBean } = await getShopList({})
+    list.value.push(...results)
+    pageNo++
+    hasMore.value = list.value.length === pageBean.totalCount
+  } catch {}
+  loading.value = false
+}
 
 function autocomplete(e: any) {
   showSuggestions.value = e.target.value.length > 0

+ 34 - 0
src/pages/appointment/index.scss

@@ -57,6 +57,14 @@
   }
 
   .shop {
+    &-list-wrap {
+      height: 1000px;
+      width: calc(100% + 32px);
+      padding-right: 32px;
+      &:only-child {
+        margin-top: 16px;
+      }
+    }
     &-item {
       padding: 48px 0;
 
@@ -347,4 +355,30 @@
     margin-left: 0;
     margin-right: 0;
   }
+
+  @include media-breakpoint-down(md) {
+    &.step0 {
+      display: flex;
+      flex-direction: column;
+      height: calc(100vh - var(--nav-bar-height) - 48px);
+      .ptc-block {
+        flex: 1;
+        overflow: hidden;
+      }
+      .ptc-inner-md {
+        display: flex;
+        flex-direction: column;
+        height: 100%;
+      }
+      .shop-wrap {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        overflow: hidden;
+      }
+      .infinite-list {
+        flex: 1;
+      }
+    }
+  }
 }

+ 1 - 1
src/pages/appointment/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="p-appointment">
+  <div class="p-appointment" :class="{ step0: state.step === 0 }">
     <component :is="Component" />
   </div>
 </template>

+ 8 - 0
src/service/order.ts

@@ -24,3 +24,11 @@ export function applyPost(data: any) {
 export function getPostInfo(member_right_id: string) {
   return request.get('/order/post/info', { params: { member_right_id } })
 }
+
+/**
+ * 我的礼品卡
+ * @param status 状态:0未使用;1已使用;2已失效
+ */
+export function getGiftCardList(status: 0 | 1 | 2) {
+  return request.get('/gift-card/list', { params: { status } })
+}

+ 24 - 0
src/service/repair.ts

@@ -13,6 +13,30 @@ export function getShopPeriods(params: any) {
   })
 }
 
+/** 获取上次维修提交的电话号码 */
 export function getRepairPhone() {
   return request.get<string>('/repair/phone')
 }
+
+/** 我的维修记录 */
+export function getRepairList() {
+  return request.get('/repair/list')
+}
+
+/** 创建维修申请 */
+export function applyRepair(data: any) {
+  return request.post('/repair/apply', data)
+}
+
+/** 修改维修申请 */
+export function rescheduleRepair(data: any) {
+  return request.post('/repair/reschedule', data)
+}
+
+export function cancelRepair(id: number) {
+  return request.post('/repair/cancel', { id })
+}
+
+export function deleteRepair(id: number) {
+  return request.post('/repair/delete', { id })
+}

+ 1 - 0
src/service/request.ts

@@ -17,6 +17,7 @@ interface ResponseData<T = any> {
   success: boolean
   results: T
   message: string
+  [k: string]: any
 }
 
 const _axios = axios.create({

+ 0 - 4
src/style/normalize.scss

@@ -119,7 +119,3 @@ i {
 ::-webkit-input-placeholder {
   color: $placeholder-color;
 }
-
-::-webkit-scrollbar {
-  display: none;
-}

+ 25 - 0
src/utils/getLocation.ts

@@ -0,0 +1,25 @@
+export default function getLocation(
+  options?: PositionOptions
+): Promise<GeolocationPosition | null> {
+  return new Promise(resolve => {
+    if (!navigator.geolocation) return resolve(null)
+    const {
+      enableHighAccuracy = true,
+      timeout = 10 * 1000,
+      maximumAge = 5 * 60 * 1000,
+    } = options || {}
+
+    navigator.geolocation.getCurrentPosition(
+      resolve,
+      err => {
+        console.error(err)
+        resolve(null)
+      },
+      {
+        enableHighAccuracy,
+        timeout,
+        maximumAge,
+      }
+    )
+  })
+}