From 25df88d0ab9d7444ed1d162b9efa1259ac2f6f66 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 27 四月 2026 10:13:53 +0800
Subject: [PATCH] 小程序对接finish

---
 yuyueMiniProgram/src/pages/visitRecord/index.vue |  284 +++++++++++++++++++++++++++++---------------------------
 1 files changed, 145 insertions(+), 139 deletions(-)

diff --git a/yuyueMiniProgram/src/pages/visitRecord/index.vue b/yuyueMiniProgram/src/pages/visitRecord/index.vue
index a89df06..784b5b2 100644
--- a/yuyueMiniProgram/src/pages/visitRecord/index.vue
+++ b/yuyueMiniProgram/src/pages/visitRecord/index.vue
@@ -1,5 +1,7 @@
 <script setup lang="ts">
-import { ref } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { fetchVisitRecords } from '@/api/appointment'
+import type { MiniVisitRecordItem } from '@/types/appointment'
 
 definePage({
   name: 'visitRecord',
@@ -9,6 +11,23 @@
   },
 })
 
+const props = withDefaults(defineProps<{
+  title?: string
+  futureOnly?: boolean
+}>(), {
+  title: '',
+  futureOnly: false,
+})
+
+type VisitRecordCard = {
+  id: number
+  name: string
+  totalPeople: number
+  time: string
+  createTime: string
+  orderNo: string
+}
+
 const router = useRouter()
 const sysInfo = uni.getSystemInfoSync()
 const statusBarHeight = sysInfo.statusBarHeight || 0
@@ -16,13 +35,12 @@
 
 const searchKeyword = ref('')
 const dateValue = ref('')
-
 const showDatePicker = ref(false)
-const dateColumns = ref<string[][]>([
-  [], // 年
-  [], // 月
-  [], // 日
-])
+const loading = ref(false)
+const records = ref<VisitRecordCard[]>([])
+const pageTitle = ref(props.title || '来访记录')
+
+const dateColumns = ref<string[][]>([[], [], []])
 const datePickerIndex = ref([0, 0, 0])
 
 function getCurrentDate() {
@@ -33,27 +51,19 @@
   return new Date(year, month, 0).getDate()
 }
 
-function updateDateColumns(selection?: { year?: number; month?: number; day?: number }) {
+function updateDateColumns(selection?: { year?: number, month?: number, day?: number }) {
   const now = getCurrentDate()
   const currentYear = now.getFullYear()
   const currentMonth = now.getMonth() + 1
   const currentDay = now.getDate()
 
   const years = Array.from({ length: 11 }, (_, index) => (currentYear - 5 + index).toString())
-  const selectedYear = selection?.year && years.includes(selection.year.toString())
-    ? selection.year
-    : currentYear
-
+  const selectedYear = selection?.year && years.includes(selection.year.toString()) ? selection.year : currentYear
   const months = Array.from({ length: 12 }, (_, index) => (1 + index).toString())
-  const selectedMonth = selection?.month && months.includes(selection.month.toString())
-    ? selection.month
-    : currentMonth
-
+  const selectedMonth = selection?.month && months.includes(selection.month.toString()) ? selection.month : currentMonth
   const maxDay = getDaysInMonth(selectedYear, selectedMonth)
   const days = Array.from({ length: maxDay }, (_, index) => (1 + index).toString())
-  const selectedDay = selection?.day && days.includes(selection.day.toString())
-    ? selection.day
-    : currentDay
+  const selectedDay = selection?.day && days.includes(selection.day.toString()) ? selection.day : currentDay
 
   dateColumns.value = [years, months, days]
   datePickerIndex.value = [
@@ -62,11 +72,6 @@
     days.findIndex(item => item === selectedDay.toString()),
   ]
 }
-
-function initDateColumns() {
-  updateDateColumns()
-}
-initDateColumns()
 
 function handleDateChange(e: any) {
   const selectedIndex = e.detail.value as number[]
@@ -83,98 +88,135 @@
   const day = dateColumns.value[2][datePickerIndex.value[2]]
   dateValue.value = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
   showDatePicker.value = false
+  void loadRecords()
 }
 
 function openDatePicker() {
   if (dateValue.value) {
     const [year, month, day] = dateValue.value.split('-').map(Number)
     updateDateColumns({ year, month, day })
-  } else {
+  }
+  else {
     updateDateColumns()
   }
   showDatePicker.value = true
 }
 
-const records = ref([
-  {
-    id: 1,
-    name: '张三',
-    company: 'xxxx有限公司',
-    hasCar: true,
-    totalPeople: 3,
-    carPlate: '苏U123456',
-    time: '2023-10-12 00:00至2023-10-13 23:59',
-    reason: '业务洽谈',
-  },
-  {
-    id: 2,
-    name: '张三',
-    company: 'xxxx有限公司',
-    hasCar: false,
-    totalPeople: 3,
-    carPlate: '苏U123456',
-    time: '2023-10-12 00:00至2023-10-13 23:59',
-    reason: '业务洽谈',
-  },
-  {
-    id: 3,
-    name: '张三',
-    company: 'xxxx有限公司',
-    hasCar: false,
-    totalPeople: 3,
-    carPlate: '苏U123456',
-    time: '2023-10-12 00:00至2023-10-13 23:59',
-    reason: '业务洽谈',
-  },
-])
-
 function handleBack() {
   router.back()
 }
 
-function handleSearch() {
-  uni.showToast({ title: '搜索中', icon: 'none' })
+function buildVisitTime(item: MiniVisitRecordItem) {
+  const range = item.visitStartDate && item.visitEndDate && item.visitStartDate !== item.visitEndDate
+    ? `${item.visitStartDate} 至 ${item.visitEndDate}`
+    : item.visitStartDate || item.visitEndDate || '-'
+  const slotMap: Record<string, string> = {
+    ALL_DAY: '全天',
+    AM: '上午',
+    PM: '下午',
+  }
+  const slotText = item.visitTimeMode === 'SINGLE' && item.visitTimeSlot
+    ? ` ${slotMap[item.visitTimeSlot] || item.visitTimeSlot}`
+    : ''
+  return `${range}${slotText}`
 }
 
+function formatDateTime(value?: string | null) {
+  if (!value) {
+    return '-'
+  }
+  return value.replace('T', ' ').slice(0, 16)
+}
+
+function mapRecord(item: MiniVisitRecordItem): VisitRecordCard {
+  return {
+    id: item.orderId,
+    name: item.visitorNames.join('、') || '-',
+    totalPeople: item.visitorNames.length,
+    time: buildVisitTime(item),
+    createTime: item.createTime,
+    orderNo: item.orderNo,
+  }
+}
+
+async function loadRecords() {
+  if (loading.value) {
+    return
+  }
+  loading.value = true
+  try {
+    const result = await fetchVisitRecords({
+      page: 1,
+      size: 50,
+      futureOnly: props.futureOnly,
+      visitorNameKeyword: searchKeyword.value.trim() || undefined,
+      visitDate: dateValue.value || undefined,
+    })
+    records.value = result.records.map(mapRecord)
+  }
+  catch (error) {
+    uni.showToast({
+      title: error instanceof Error ? error.message : '加载来访记录失败',
+      icon: 'none',
+    })
+  }
+  finally {
+    loading.value = false
+  }
+}
+
+function handleSearch() {
+  void loadRecords()
+}
+
+updateDateColumns()
+
+onLoad((options) => {
+  pageTitle.value = options?.title
+    ? decodeURIComponent(options.title)
+    : (props.title || '来访记录')
+})
+
+onShow(() => {
+  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="fixed left-0 top-0 z-0 h-[500rpx] w-full" 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(120)) + '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="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 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">
             <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">
-            来访记录
+            {{ pageTitle }}
           </view>
-          <view class="w-[88rpx]"></view>
+          <view class="w-[88rpx]" />
         </view>
       </view>
 
-      <!-- 搜索栏 -->
-      <view class="px-[28rpx] w-[702rpx] pt-[24rpx] pb-[16rpx]  box-border">
+      <view class="px-[28rpx] w-[702rpx] pt-[24rpx] pb-[16rpx] box-border">
         <view class="bg-white rounded-[16rpx] h-[104rpx] flex items-center px-[24rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full shrink-0 justify-between">
-          <!-- 姓名输入 -->
           <view class="bg-[#F5F5F5] w-[140rpx] px-[40rpx] h-[64rpx] rounded-[44rpx] flex items-center mr-[20rpx]">
-            <input v-model="searchKeyword" type="text" placeholder="姓名关键字" placeholder-class="text-[#B3B3B3] text-[26rpx]"
-              class="w-full text-[26rpx] text-[#333333] h-full" />
+            <input
+              v-model="searchKeyword"
+              type="text"
+              placeholder="搜索来访人姓名"
+              placeholder-class="text-[#B3B3B3] text-[26rpx]"
+              class="w-full text-[26rpx] text-[#333333] h-full"
+              confirm-type="search"
+              @confirm="handleSearch"
+            />
           </view>
 
-          <!-- 日期选择 -->
           <view class="flex items-center mr-[20rpx] bg-[#F5F5F5] w-[220rpx] h-[64rpx] rounded-[44rpx]" @click="openDatePicker">
             <view class="flex items-center">
               <text class="text-[28rpx] pl-[40rpx] mr-[20rpx]" :class="dateValue ? 'text-[#0068FF] font-medium' : 'text-[#333333]'">{{ dateValue || '来访日期' }}</text>
@@ -182,7 +224,6 @@
             </view>
           </view>
 
-          <!-- 查询按钮 -->
           <view class="w-[156rpx] h-[64rpx] rounded-[32rpx] border-[2rpx] border-solid border-[#0068FF] flex items-center justify-center box-border bg-white" @click="handleSearch">
             <wd-icon name="search" size="28rpx" color="#0068FF" class="mr-[10rpx]" />
             <text class="text-[#0068FF] text-[26rpx] ml-[10rpx] font-medium">查询</text>
@@ -191,24 +232,16 @@
       </view>
     </view>
 
-    <!-- 占位元素,用于撑开固定的顶部高度 -->
-    <view :style="{ height: (statusBarHeight + navBarHeight + uni.upx2px(144)) + 'px' }" class="shrink-0"></view>
+    <view :style="{ height: (statusBarHeight + navBarHeight + uni.upx2px(144)) + 'px' }" class="shrink-0" />
 
-    <!-- 内容区 -->
     <view class="relative z-10 px-[24rpx] flex-1 flex flex-col min-h-0 box-border pt-[16rpx]">
       <scroll-view scroll-y class="flex-1 min-h-0" :show-scrollbar="false">
         <view class="pb-[48rpx]">
-          <view v-for="item in records" :key="item.id"
-            class="bg-white rounded-[8rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] mb-[24rpx] px-[40rpx]">
-            
+          <view v-for="item in records" :key="item.id" class="bg-white rounded-[8rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] mb-[24rpx] px-[40rpx]">
             <view class="flex items-center h-[96rpx]">
               <text class="text-[#333333] text-[28rpx] font-bold shrink-0 w-[120rpx] mr-[12rpx]">访客姓名:</text>
               <view class="flex-1 h-full flex items-center border-b1">
                 <text class="text-[#333333] text-[28rpx]">{{ item.name }}</text>
-                <text class="text-[#999999] text-[28rpx] ml-[8rpx]">({{ item.company }})</text>
-                <view v-if="item.hasCar" class="ml-[16rpx] bg-[#E6F0FF] px-[12rpx] py-[4rpx] rounded-[4rpx]">
-                  <text class="text-[#0068FF] text-[22rpx]">车辆</text>
-                </view>
               </view>
             </view>
 
@@ -220,9 +253,9 @@
             </view>
 
             <view class="flex items-center h-[96rpx]">
-              <text class="text-[#333333] text-[28rpx] font-bold shrink-0 w-[120rpx] mr-[12rpx]">访客车辆:</text>
+              <text class="text-[#333333] text-[28rpx] font-bold shrink-0 w-[120rpx] mr-[12rpx]">预约单号:</text>
               <view class="flex-1 h-full flex items-center border-b1">
-                <text class="text-[#616161] text-[28rpx]">{{ item.carPlate }}</text>
+                <text class="text-[#616161] text-[28rpx]">{{ item.orderNo }}</text>
               </view>
             </view>
 
@@ -234,64 +267,49 @@
             </view>
 
             <view class="flex items-center h-[96rpx]">
-              <text class="text-[#333333] text-[28rpx] font-bold shrink-0 w-[120rpx] mr-[12rpx]">拜访事由:</text>
-              <view class="flex-1 h-full flex items-center border-b1">
-                <text class="text-[#616161] text-[28rpx]">{{ item.reason }}</text>
+              <text class="text-[#333333] text-[28rpx] font-bold shrink-0 w-[120rpx] mr-[12rpx]">创建时间:</text>
+              <view class="flex-1 h-full flex items-center">
+                <text class="text-[#616161] text-[28rpx]">{{ formatDateTime(item.createTime) }}</text>
               </view>
             </view>
+          </view>
 
+          <view v-if="loading" class="text-center text-[#999999] text-[24rpx] py-[24rpx]">
+            加载中...
+          </view>
+          <view v-else-if="records.length === 0" class="text-center text-[#999999] text-[24rpx] py-[80rpx]">
+            暂无{{ pageTitle }}
           </view>
         </view>
       </scroll-view>
     </view>
 
-    <!-- 自定义来访日期弹窗 -->
     <wd-popup v-model="showDatePicker" position="bottom" custom-class="date-popup" z-index="100">
       <view class="flex flex-col bg-white rounded-t-[32rpx] overflow-hidden h-[830rpx]">
-        <!-- 弹窗头部 -->
         <view class="relative flex items-center justify-center shrink-0 h-[96rpx] border-b border-[#F5F5F5] border-solid">
-          <view class="absolute left-[32rpx] flex items-center justify-center w-[48rpx] h-[48rpx]"
-            @click="showDatePicker = false">
+          <view class="absolute left-[32rpx] flex items-center justify-center w-[48rpx] h-[48rpx]" @click="showDatePicker = false">
             <wd-icon name="close-outline" size="40rpx" color="#333333" />
           </view>
           <text class="text-[32rpx] text-[#333333] font-medium">选择来访日期</text>
         </view>
 
-        <!-- 弹窗内容区 -->
         <view class="px-[32rpx] flex-1 flex flex-col justify-center relative">
-          <!-- 头部列名 -->
           <view class="flex items-center absolute top-[40rpx] left-[32rpx] right-[32rpx] z-10 text-[24rpx] text-[#333333] font-medium">
             <view class="flex-1 flex justify-center"><text class="bg-[#F5F5F5] rounded-[8rpx] px-[24rpx] py-[8rpx]">年</text></view>
             <view class="flex-1 flex justify-center"><text class="bg-[#F5F5F5] rounded-[8rpx] px-[24rpx] py-[8rpx]">月</text></view>
             <view class="flex-1 flex justify-center"><text class="bg-[#F5F5F5] rounded-[8rpx] px-[24rpx] py-[8rpx]">日</text></view>
           </view>
-          <picker-view
-            class="custom-picker-view date-picker-view mt-[60rpx]"
-            :value="datePickerIndex"
-            indicator-style="height: 48px;"
-            style="width: 100%; height: 192px;"
-            @change="handleDateChange"
-          >
-            <picker-view-column
-              v-for="(col, index) in dateColumns"
-              :key="index"
-            >
-              <view
-                v-for="(item, itemIndex) in col"
-                :key="itemIndex"
-                class="flex items-center justify-center h-[48px]"
-                :class="{ 'wd-picker-view-column__item--active': datePickerIndex[index] === itemIndex, 'wd-picker-view-column__item': true }"
-              >
+          <picker-view class="custom-picker-view date-picker-view mt-[60rpx]" :value="datePickerIndex" indicator-style="height: 48px;" style="width: 100%; height: 192px;" @change="handleDateChange">
+            <picker-view-column v-for="(col, index) in dateColumns" :key="index">
+              <view v-for="(item, itemIndex) in col" :key="itemIndex" class="flex items-center justify-center h-[48px]" :class="{ 'wd-picker-view-column__item--active': datePickerIndex[index] === itemIndex, 'wd-picker-view-column__item': true }">
                 {{ item }}
               </view>
             </picker-view-column>
           </picker-view>
         </view>
 
-        <!-- 弹窗底部按钮 -->
         <view class="px-[32rpx] pb-[48rpx] shrink-0">
-          <wd-button type="primary" size="large" block custom-class="popup-submit-btn"
-            @click="handleDateConfirm">
+          <wd-button type="primary" size="large" block custom-class="popup-submit-btn" @click="handleDateConfirm">
             确认
           </wd-button>
         </view>
@@ -301,11 +319,10 @@
 </template>
 
 <style scoped>
-.border-b1{
+.border-b1 {
   border-bottom: 2rpx solid #F5F5F5;
 }
 
-/* 隐藏滚动条 */
 ::-webkit-scrollbar {
   display: none;
   width: 0 !important;
@@ -314,7 +331,6 @@
   background: transparent;
 }
 
-/* 自定义弹窗样式 */
 :deep(.date-popup) {
   border-top-left-radius: 32rpx;
   border-top-right-radius: 32rpx;
@@ -322,14 +338,13 @@
   background-color: transparent !important;
 }
 
-
 .wd-picker-view-column__item {
-   color: #B3B3B3 !important;
-   font-size: 28rpx !important;
-   background-color: transparent !important;
-   transition: all 0.3s;
-   z-index: 10;
-   position: relative;
+  color: #B3B3B3 !important;
+  font-size: 28rpx !important;
+  background-color: transparent !important;
+  transition: all 0.3s;
+  z-index: 10;
+  position: relative;
 }
 
 :deep(picker-view-column) {
@@ -352,7 +367,6 @@
   z-index: 10;
 }
 
-/* 选择器中间的选中边框线 */
 :deep(.custom-picker-view .wx-picker-view-indicator) {
   border-top: none !important;
   border-bottom: none !important;
@@ -366,13 +380,6 @@
   display: none !important;
 }
 
-.custom-picker-view .wd-picker-view-column__item--active {
-  color: #0068FF !important;
-  font-weight: 500;
-  font-size: 28rpx !important;
-  background-color: #F4F8FF !important;
-}
-
 :deep(.date-picker-view picker-view-column:nth-child(1)),
 :deep(.date-picker-view picker-view-column:nth-child(2)),
 :deep(.date-picker-view picker-view-column:nth-child(3)) {
@@ -382,7 +389,6 @@
   z-index: 10;
 }
 
-/* 调整选项的宽度 */
 :deep(.date-picker-view picker-view-column:nth-child(1) .wd-picker-view-column__item),
 :deep(.date-picker-view picker-view-column:nth-child(2) .wd-picker-view-column__item),
 :deep(.date-picker-view picker-view-column:nth-child(3) .wd-picker-view-column__item) {

--
Gitblit v1.7.1