puzhibing
2026-08-28 e92cd56bb56b111c1afe9922191f9897215ff6b0
yuyueMiniProgram/src/pages/record/index.vue
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { fetchMyAppointmentOrders } from '@/api/appointment'
import type { MiniAppointmentListItem } from '@/types/appointment'
import { normalizeVisitReasonText } from '@/utils/visitReason'
definePage({
  name: 'record',
@@ -15,7 +16,6 @@
  status?: string
}
const router = useRouter()
const sysInfo = uni.getSystemInfoSync()
const statusBarHeight = sysInfo.statusBarHeight || 0
const navBarHeight = 44
@@ -23,11 +23,16 @@
const searchKeyword = ref('')
const activeTab = ref(0)
const loading = ref(false)
const refreshing = ref(false)
const records = ref<MiniAppointmentListItem[]>([])
const page = ref(1)
const size = 10
const total = ref(0)
const finished = ref(false)
const statusCountMap = ref<Record<string, number>>({
  PENDING_VISIT: 0,
  EFFECTIVE: 0,
})
const tabs: RecordTabItem[] = [
  { title: '全部' },
@@ -37,7 +42,22 @@
]
function handleBack() {
  router.back()
  const pages = getCurrentPages()
  if (pages.length > 1) {
    uni.navigateBack({
      delta: 1,
      fail: () => {
        uni.switchTab({ url: '/pages/index/index' })
      },
    })
    return
  }
  uni.switchTab({
    url: '/pages/index/index',
    fail: () => {
      uni.reLaunch({ url: '/pages/index/index' })
    },
  })
}
function statusTheme(item: MiniAppointmentListItem) {
@@ -92,10 +112,31 @@
  }
}
async function loadStatusCounts() {
  try {
    const [pendingResult, effectiveResult] = await Promise.all([
      fetchMyAppointmentOrders({ page: 1, size: 1, status: 'PENDING_VISIT' }),
      fetchMyAppointmentOrders({ page: 1, size: 1, status: 'EFFECTIVE' }),
    ])
    statusCountMap.value = {
      PENDING_VISIT: pendingResult.total,
      EFFECTIVE: effectiveResult.total,
    }
  }
  catch (error) {
    statusCountMap.value = {
      PENDING_VISIT: 0,
      EFFECTIVE: 0,
    }
    console.warn('load appointment status counts failed', error)
  }
}
function reloadRecords() {
  page.value = 1
  finished.value = false
  void loadRecords(true)
  void loadStatusCounts()
}
function handleTabClick(index: number) {
@@ -113,29 +154,62 @@
  })
}
function formatVisitedUserText(item: MiniAppointmentListItem) {
  const name = (item.visitedUserName || '').trim()
  const dept = (item.visitedDeptName || '').trim()
  if (!name) {
    return item.visitedUserDisplay || '-'
  }
  if (!dept) {
    return name
  }
  return `${name}(${dept})`
}
function formatVisitReasonText(item: MiniAppointmentListItem) {
  return normalizeVisitReasonText(item.visitReasonText) || '-'
}
function formatVisitReasonOtherText(item: MiniAppointmentListItem) {
  return (item.visitReasonOther || '').trim() || '-'
}
onShow(() => {
  reloadRecords()
})
onReachBottom(() => {
  if (!finished.value) {
    void loadRecords()
function handlePullRefresh() {
  if (refreshing.value) {
    return
  }
})
  refreshing.value = true
  page.value = 1
  finished.value = false
  void loadRecords(true).finally(() => {
    refreshing.value = false
  })
}
function handleScrollToLower() {
  if (finished.value || loading.value) {
    return
  }
  void loadRecords()
}
</script>
<template>
  <view class="min-h-screen bg-[#F6FBFF] w-full relative box-border flex flex-col">
    <view class="fixed left-0 top-0 z-0 h-[500rpx] w-full" style="background: linear-gradient(180deg, #5398FF 0%, #A9D0FF 50%, #F6FBFF 100%);" />
  <view class="h-screen bg-[#F6FBFF] w-full relative box-border flex flex-col overflow-hidden">
    <view class="fixed left-0 top-0 z-0 h-[500rpx] w-full pointer-events-none" style="background: linear-gradient(180deg, #5398FF 0%, #A9D0FF 50%, #F6FBFF 100%);" />
    <view class="fixed top-0 left-0 w-full z-50 flex flex-col">
      <view class="absolute left-0 top-0 z-[-1] w-full overflow-hidden" :style="{ height: (statusBarHeight + navBarHeight + uni.upx2px(36) + uni.upx2px(288) + uni.upx2px(24)) + 'px' }">
    <view class="fixed top-0 left-0 w-full flex flex-col record-fixed-head">
      <view class="absolute left-0 top-0 z-0 w-full overflow-hidden pointer-events-none" :style="{ height: (statusBarHeight + navBarHeight + uni.upx2px(36) + uni.upx2px(288) + uni.upx2px(24)) + 'px' }">
        <view class="w-full h-[500rpx] absolute top-0 left-0" style="background: linear-gradient(180deg, #5398FF 0%, #A9D0FF 50%, #F6FBFF 100%);" />
      </view>
      <view class="relative w-full shrink-0" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }">
        <view class="flex items-center justify-between w-full absolute left-0" :style="{ top: statusBarHeight + 'px', height: navBarHeight + 'px' }">
          <view class="flex h-full w-[88rpx] items-center justify-center relative z-10" @click="handleBack">
      <view class="relative z-20 w-full shrink-0" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }">
        <view class="flex items-center justify-between w-full absolute left-0 z-20" :style="{ top: statusBarHeight + 'px', height: navBarHeight + 'px' }">
          <view class="record-back-hit flex h-full w-[120rpx] items-center justify-center relative" @tap.stop="handleBack" @click.stop="handleBack">
            <wd-icon name="arrow-left" size="40rpx" color="#ffffff" />
          </view>
          <view class="absolute left-0 w-full text-center text-[34rpx] font-medium text-white z-0">
@@ -145,7 +219,7 @@
        </view>
      </view>
      <view class="px-[24rpx] pt-[36rpx] pb-[24rpx] w-full box-border">
      <view class="relative z-10 px-[24rpx] pt-[36rpx] pb-[24rpx] w-full box-border">
        <view class="bg-white rounded-[16rpx] search-card shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full shrink-0">
          <view class="flex justify-center pt-[32rpx] pb-[16rpx]">
            <view class="w-[622rpx] h-[88rpx] rounded-[44rpx] bg-[#FFFFFF] border-[4rpx] border-solid border-[#D4E6FF] box-border flex items-center px-[32rpx]">
@@ -173,6 +247,14 @@
                <text class="text-[28rpx] transition-colors duration-200" :class="activeTab === index ? 'text-[#0068FF] font-medium' : 'text-[#999999]'">
                  {{ tab.title }}
                </text>
                <view
                  v-if="tab.status && statusCountMap[tab.status] > 0"
                  class="absolute right-[-28rpx] top-[-14rpx] min-w-[32rpx] h-[32rpx] px-[8rpx] rounded-full bg-[#FF4D4F] flex items-center justify-center box-border"
                >
                  <text class="text-white text-[20rpx] leading-none">
                    {{ statusCountMap[tab.status] > 99 ? '99+' : statusCountMap[tab.status] }}
                  </text>
                </view>
              </view>
              <view class="absolute bottom-[16rpx] w-[40rpx] h-[4rpx] rounded-[2rpx] transition-all duration-200" :class="activeTab === index ? 'bg-[#0068FF]' : 'bg-transparent'" />
            </view>
@@ -183,8 +265,17 @@
    <view :style="{ height: (statusBarHeight + navBarHeight + uni.upx2px(36) + uni.upx2px(288) + uni.upx2px(24)) + 'px' }" class="shrink-0" />
    <view class="relative z-10 px-[24rpx] flex-1 flex flex-col min-h-0 box-border">
      <scroll-view scroll-y class="flex-1 min-h-0" :show-scrollbar="false">
    <view class="relative z-10 mt-[20rpx] px-[24rpx] flex-1 flex flex-col min-h-0 box-border">
      <scroll-view
        scroll-y
        class="flex-1 min-h-0"
        :show-scrollbar="false"
        :lower-threshold="120"
        refresher-enabled
        :refresher-triggered="refreshing"
        @refresherrefresh="handlePullRefresh"
        @scrolltolower="handleScrollToLower"
      >
        <view class="pb-[24rpx]">
          <view
            v-for="item in records"
@@ -205,7 +296,7 @@
              <view class="flex flex-col px-[40rpx]">
                <view class="flex items-center h-[96rpx] relative">
                  <text class="text-[#666666] text-[28rpx] w-[140rpx] shrink-0">被访人:</text>
                  <text class="text-[#333333] text-[28rpx] flex-1 truncate">{{ item.visitedUserDisplay }}</text>
                  <text class="text-[#333333] text-[28rpx] flex-1 truncate">{{ formatVisitedUserText(item) }}</text>
                  <view class="absolute bottom-0 left-[140rpx] right-0 h-[2rpx] bg-[#F5F5F5]" />
                </view>
                <view class="flex items-center h-[96rpx] relative">
@@ -220,7 +311,11 @@
                </view>
                <view class="flex items-center h-[96rpx]">
                  <text class="text-[#666666] text-[28rpx] w-[140rpx] shrink-0">拜访事由:</text>
                  <text class="text-[#333333] text-[28rpx] flex-1 truncate">{{ item.visitReasonText }}</text>
                  <text class="text-[#333333] text-[28rpx] flex-1 truncate">{{ formatVisitReasonText(item) }}</text>
                </view>
                <view class="flex items-center min-h-[96rpx]">
                  <text class="text-[#666666] text-[28rpx] w-[140rpx] shrink-0">事由说明:</text>
                  <text class="text-[#333333] text-[28rpx] flex-1 line-clamp-2 break-all">{{ formatVisitReasonOtherText(item) }}</text>
                </view>
              </view>
            </view>
@@ -242,6 +337,14 @@
</template>
<style scoped>
.record-fixed-head {
  z-index: 9999;
}
.record-back-hit {
  z-index: 10001;
}
.search-card {
  width: 702rpx;
  height: 288rpx;