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/review/index.vue |  665 ++++++++++++++++++++++++-------------------------------
 1 files changed, 291 insertions(+), 374 deletions(-)

diff --git a/yuyueMiniProgram/src/pages/review/index.vue b/yuyueMiniProgram/src/pages/review/index.vue
index 9853c40..141c155 100644
--- a/yuyueMiniProgram/src/pages/review/index.vue
+++ b/yuyueMiniProgram/src/pages/review/index.vue
@@ -1,5 +1,6 @@
 <script setup lang="ts">
-import { ref } from 'vue'
+import { cancelMyAppointment, deleteMyAppointment, fetchMyAppointmentDetail } from '@/api/appointment'
+import type { ApprovalFlowNodeItem, MiniMyAppointmentDetailItem } from '@/types/appointment'
 
 definePage({
   name: 'review',
@@ -16,51 +17,12 @@
 
 const showCancelModal = ref(false)
 const showDeleteModal = ref(false)
-const appointmentStatus = ref('reviewing') // 'reviewing', 'pending_effect', 'effective', 'cancelled', 'invalid', 'rejected'
-
-// 接收页面参数设置初始状态
-import { onLoad } from '@dcloudio/uni-app'
-onLoad((options) => {
-  if (options && options.status) {
-    appointmentStatus.value = options.status
-  }
-})
-
-function handleBack() {
-  router.back()
-}
-
-function handleCancel() {
-  showCancelModal.value = true
-}
-
-function handleDelete() {
-  showDeleteModal.value = true
-}
-
-function confirmCancel() {
-  showCancelModal.value = false
-  uni.showToast({ title: '已取消预约', icon: 'success' })
-  setTimeout(() => {
-    router.back()
-  }, 1000)
-}
-
-function confirmDelete() {
-  showDeleteModal.value = false
-  uni.showToast({ title: '已删除记录', icon: 'success' })
-  setTimeout(() => {
-    router.back()
-  }, 1000)
-}
-
-function handleRebook() {
-  uni.navigateTo({ url: '/pages/appointment/index' })
-}
-
-function handleViewVisitorCode() {
-  uni.navigateTo({ url: '/pages/visitorCode/index' })
-}
+const detail = ref<MiniMyAppointmentDetailItem | null>(null)
+const loading = ref(false)
+const canceling = ref(false)
+const deleting = ref(false)
+const orderId = ref<number>()
+const orderUuid = ref('')
 
 const appointmentIcons = {
   male: '/static/appointment/man.png',
@@ -68,342 +30,323 @@
   company: '/static/appointment/company.png',
 }
 
-const visitors = [
-  {
-    id: 1,
-    name: '张三',
-    gender: 'male',
-    phone: '5120**********258',
-    company: '成都xxxxxx有限公司',
-    plate: '苏U123456'
-  },
-  {
-    id: 2,
-    name: '张三',
-    gender: 'female',
-    phone: '5120**********258',
-    company: '成都xxxxxx有限公司'
-  },
-  {
-    id: 3,
-    name: '张三',
-    gender: 'female',
-    phone: '5120**********258',
-    company: '成都xxxxxx有限公司'
+const appointmentStatus = computed(() => detail.value?.displayStatus || 'PENDING_VISIT')
+const invalidLike = computed(() => appointmentStatus.value === 'INVALID')
+const headerBg = computed(() => invalidLike.value ? '/static/review/bg-grey.png' : '/static/review/bg-t.png')
+const titleText = computed(() => detail.value?.statusText || '预约记录')
+const showVisitorCodeEntry = computed(() => appointmentStatus.value === 'PENDING_VISIT' || appointmentStatus.value === 'EFFECTIVE')
+
+function handleBack() {
+  router.back()
+}
+
+function handleCancel() {
+  if (!detail.value?.cancelable) {
+    return
   }
-]
+  showCancelModal.value = true
+}
+
+async function confirmCancel() {
+  if (!orderId.value || !orderUuid.value || canceling.value) {
+    return
+  }
+  canceling.value = true
+  try {
+    await cancelMyAppointment(orderId.value, orderUuid.value)
+    showCancelModal.value = false
+    uni.showToast({ title: '已取消预约', icon: 'success' })
+    await loadDetail()
+  }
+  catch (error) {
+    uni.showToast({
+      title: error instanceof Error ? error.message : '取消预约失败',
+      icon: 'none',
+    })
+  }
+  finally {
+    canceling.value = false
+  }
+}
+
+function handleRebook() {
+  if (!detail.value) {
+    return
+  }
+  uni.navigateTo({
+    url: `/pages/appointment/index?rebookOrderId=${detail.value.orderId}&rebookOrderUuid=${encodeURIComponent(detail.value.orderUuid)}`,
+  })
+}
+
+function handleDelete() {
+  if (!detail.value?.deletable) {
+    return
+  }
+  showDeleteModal.value = true
+}
+
+async function confirmDelete() {
+  if (!orderId.value || !orderUuid.value || deleting.value) {
+    return
+  }
+  deleting.value = true
+  try {
+    await deleteMyAppointment(orderId.value, orderUuid.value)
+    showDeleteModal.value = false
+    uni.showToast({ title: '已删除记录', icon: 'success' })
+    setTimeout(() => {
+      uni.navigateBack()
+    }, 500)
+  }
+  catch (error) {
+    uni.showToast({
+      title: error instanceof Error ? error.message : '删除记录失败',
+      icon: 'none',
+    })
+  }
+  finally {
+    deleting.value = false
+  }
+}
+
+function handleViewVisitorCode() {
+  uni.switchTab({ url: '/pages/index/index' })
+}
+
+function visitorGenderIcon(gender?: string | null) {
+  return gender === 'FEMALE' ? appointmentIcons.female : appointmentIcons.male
+}
+
+function flowStatusText(status?: string | null) {
+  switch (status) {
+    case 'APPROVED':
+      return '已通过'
+    case 'REJECTED':
+      return '已驳回'
+    case 'CANCELLED':
+      return '已取消'
+    case 'SKIPPED':
+      return '已跳过'
+    case 'PENDING':
+      return '审批中'
+    default:
+      return '待处理'
+  }
+}
+
+function flowStatusClass(status?: string | null) {
+  switch (status) {
+    case 'APPROVED':
+      return 'text-[#52C41A]'
+    case 'REJECTED':
+      return 'text-[#FF4D4F]'
+    case 'CANCELLED':
+      return 'text-[#999999]'
+    case 'PENDING':
+      return 'text-[#0068FF]'
+    default:
+      return 'text-[#999999]'
+  }
+}
+
+function flowUsers(node: ApprovalFlowNodeItem) {
+  return node.groups.flatMap(group => group.users.map(user => ({
+    id: user.taskId,
+    name: user.userName || group.groupName || '审批人',
+    status: user.taskStatus,
+    time: user.actionTime,
+  })))
+}
+
+async function loadDetail() {
+  if (!orderId.value || !orderUuid.value) {
+    uni.showToast({ title: '缺少预约参数', icon: 'none' })
+    return
+  }
+  loading.value = true
+  try {
+    detail.value = await fetchMyAppointmentDetail(orderId.value, orderUuid.value)
+  }
+  catch (error) {
+    uni.showToast({
+      title: error instanceof Error ? error.message : '加载预约详情失败',
+      icon: 'none',
+    })
+  }
+  finally {
+    loading.value = false
+  }
+}
+
+onLoad((options) => {
+  orderId.value = options?.orderId ? Number(options.orderId) : undefined
+  orderUuid.value = options?.orderUuid || ''
+  void loadDetail()
+})
 </script>
 
 <template>
   <view class="min-h-screen bg-[#F6FBFF] w-full relative box-border flex flex-col pb-[160rpx]">
-    
-    <!-- 顶部背景图 (被覆盖层) -->
     <view class="absolute left-0 top-0 z-0 h-[480rpx] w-full overflow-hidden">
-      <image :src="['cancelled', 'invalid', 'rejected'].includes(appointmentStatus) ? '/static/review/bg-grey.png' : '/static/review/bg-t.png'" class="w-full h-full" mode="aspectFill" />
+      <image :src="headerBg" class="w-full h-full" mode="aspectFill" />
     </view>
 
-    <!-- 整体内容背景图 (通过 z-index 层级置于顶部背景之上,利用背景图自身弧度产生遮盖效果) -->
     <view class="absolute left-0 top-[410rpx] w-full z-0 pointer-events-none" style="height: calc(100% - 380rpx);">
       <image src="/static/review/contentbg.png" class="w-full h-full" mode="scaleToFill" />
     </view>
 
-    <!-- 自定义导航栏 -->
     <view class="relative z-50 w-full shrink-0" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }">
-      <!-- 固定在顶部的导航栏 -->
       <view class="fixed left-0 top-0 w-full z-50 overflow-hidden" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }">
-        <!-- 截取顶部背景图作为 navBar 背景,防止滚动时内容透出 -->
-        <image :src="['cancelled', 'invalid', 'rejected'].includes(appointmentStatus) ? '/static/review/bg-grey.png' : '/static/review/bg-t.png'" class="absolute left-0 top-0 w-full z-0" mode="widthFix" />
-        
-        <view class="flex items-center justify-between w-full absolute left-0 z-10"
-          :style="{ top: statusBarHeight + 'px', height: navBarHeight + 'px' }">
+        <image :src="headerBg" class="absolute left-0 top-0 w-full z-0" mode="widthFix" />
+        <view class="flex items-center justify-between w-full absolute left-0 z-10" :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">
             预约记录
           </view>
-          <view class="w-[88rpx]"></view>
+          <view class="w-[88rpx]" />
         </view>
       </view>
     </view>
 
-    <!-- 内容区 -->
     <view class="relative z-10 px-[24rpx] flex-1">
-      
-      <!-- 审批状态区 -->·
-      <view class="px-[24rpx] mb-[108rpx]" v-if="['reviewing', 'rejected'].includes(appointmentStatus)">
-        <view class="flex items-center mb-[10rpx]">
-          <image v-if="appointmentStatus === 'reviewing'" src="/static/review/chapter.png" class="w-[40rpx] h-[40rpx] mr-[8rpx]" mode="aspectFit" />
-          <image v-else-if="appointmentStatus === 'rejected'" src="/static/review/Pending.png" class="w-[40rpx] h-[40rpx] mr-[8rpx]" mode="aspectFit" />
-          <text class="text-[32rpx] text-white font-bold">审批中...</text>
-        </view>
-        
-        <!-- 分割线 -->
-        <view class="w-full h-[2rpx] bg-white/20 mb-[24rpx]"></view>
+      <view v-if="loading" class="px-[24rpx] pt-[120rpx] text-center text-white text-[28rpx]">
+        加载中...
+      </view>
 
-        <!-- 进度条模拟 -->
-        <view class="flex items-start justify-between relative px-[16rpx]">
+      <template v-else-if="detail">
+        <view class="px-[24rpx] mb-[32rpx]" :class="showVisitorCodeEntry ? '' : 'mt-[76rpx]'">
+          <view class="flex items-center mb-[20rpx]">
+            <image
+              :src="appointmentStatus === 'EFFECTIVE' ? '/static/review/success.png' : invalidLike ? '/static/review/cancel.png' : '/static/review/Pending.png'"
+              class="w-[40rpx] h-[40rpx] mr-[8rpx]"
+              mode="aspectFit"
+            />
+            <text class="text-[32rpx] text-white font-bold">{{ titleText }}</text>
+          </view>
 
-          <!-- 步骤1 -->
-          <view class="flex flex-col items-center relative z-10 w-[60rpx]">
-            <view class="w-[40rpx] h-[40rpx] rounded-full bg-white flex items-center justify-center mb-[24rpx] relative">
-              <!-- 波纹动画层 -->
-              <template v-if="['reviewing', 'rejected'].includes(appointmentStatus)">
-                <view class="ripple-wave absolute w-full h-full rounded-full bg-white"></view>
-                <view class="ripple-wave ripple-delay absolute w-full h-full rounded-full bg-white"></view>
-              </template>
-              <text class="text-[#0068FF] text-[24rpx] font-bold z-10">1</text>
+          <view class="w-full h-[2rpx] bg-white/30 mb-[24rpx]" />
+
+          <view v-if="showVisitorCodeEntry" class="flex items-center mb-[24rpx]">
+            <view class="bg-[#52C41A] w-full rounded-full h-[60rpx] px-[18rpx] flex items-center justify-center">
+              <text class="text-white text-[26rpx] font-medium">{{ detail.visitTimeText }}</text>
             </view>
-            <text class="text-white text-[20rpx] mb-[4rpx] whitespace-nowrap">经办人</text>
-            <text class="text-white/80 text-[18rpx] whitespace-nowrap">2-3 15:05</text>
           </view>
 
-          <!-- 箭头 1 -->
-          <view class="flex-1 flex justify-center pt-[0rpx]">
-            <image src="/static/review/arrow.png" class="w-[64rpx] h-[40rpx]" mode="aspectFit" />
+          <view v-if="showVisitorCodeEntry" class="flex justify-center mb-[48rpx]">
+            <view class="border-[2rpx] border-solid border-white/60 rounded-full w-[280rpx] h-[64rpx] flex items-center justify-center bg-white/20 transition-opacity active:opacity-70" @click="handleViewVisitorCode">
+              <wd-icon name="view" size="32rpx" color="#ffffff" class="mr-[12rpx]" />
+              <text class="text-white text-[28rpx]">查看访客码</text>
+            </view>
+          </view>
+        </view>
+
+        <view class="flex items-end mb-[26rpx] px-[8rpx]">
+          <text class="text-[32rpx] text-[#000000] font-[500]">拜访信息</text>
+          <text class="text-[24rpx] text-[#676767] font-[350] ml-[16rpx] mb-[4rpx]">您要见的人</text>
+        </view>
+
+        <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[48rpx] px-[40rpx] py-[12rpx] box-border">
+          <view class="flex flex-col">
+            <view class="flex justify-between items-center h-[96rpx] border-b1">
+              <text class="text-[#000000] text-[28rpx] font-normal">被访人姓名</text>
+              <text class="text-[#666666] text-[28rpx] font-[350]">{{ detail.visitedUserDisplay }}</text>
+            </view>
+            <view class="flex justify-between items-center h-[96rpx] border-b1">
+              <text class="text-[#000000] text-[28rpx] font-normal">拜访事由</text>
+              <text class="text-[#666666] text-[28rpx] font-[350]">{{ detail.visitReasonText }}</text>
+            </view>
+            <view class="flex justify-between items-center h-[96rpx]">
+              <text class="text-[#000000] text-[28rpx] font-normal">拜访时间</text>
+              <text class="text-[#666666] text-[28rpx] font-[350] text-right ml-[24rpx]">{{ detail.visitTimeText }}</text>
+            </view>
+          </view>
+        </view>
+
+        <view class="flex items-end mb-[26rpx] px-[8rpx]">
+          <text class="text-[32rpx] text-[#000000] font-[500]">来访人信息</text>
+          <text class="text-[24rpx] text-[#676767] font-[350] ml-[16rpx] mb-[4rpx]">选择您及同行人员</text>
+        </view>
+
+        <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[24rpx] box-border pb-[24rpx]">
+          <view class="flex justify-between items-center px-[40rpx] py-[28rpx] border-b1">
+            <text class="text-[#000000] text-[28rpx] font-normal">进场类型</text>
+            <text class="text-[#616161] text-[28rpx] font-[350]">{{ detail.visitTypeText }}</text>
           </view>
 
-          <!-- 步骤2 -->
-          <view class="flex flex-col items-center relative z-10 w-[60rpx]">
-            <view 
-              class="w-[40rpx] h-[40rpx] rounded-full flex items-center justify-center mb-[24rpx] relative"
-              :class="appointmentStatus === 'rejected' ? 'bg-[#FF4D4F]' : 'bg-[#3B72FF]'"
+          <view class="px-[32rpx] py-[16rpx]">
+            <view
+              v-for="(visitor, index) in detail.visitors"
+              :key="visitor.visitorId"
+              class="flex items-center py-[24rpx] border-b2 mb-[16rpx]"
+              :class="index === detail.visitors.length - 1 ? 'last-visitor' : ''"
             >
-              <template v-if="['reviewing', 'rejected'].includes(appointmentStatus)">
-                <!-- 波纹动画层 (蓝底/红底白边) -->
-                <view class="ripple-wave absolute w-full h-full rounded-full border-[4rpx] border-solid border-white box-border"></view>
-                <view class="ripple-wave ripple-delay absolute w-full h-full rounded-full border-[4rpx] border-solid border-white box-border"></view>
-                <!-- 内圈白边 -->
-                <view class="absolute w-[40rpx] h-[40rpx] rounded-full border-[2rpx] border-solid border-white top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 box-border z-0"></view>
-                <!-- 外圈半透明白边(固定光晕) -->
-                <view class="absolute w-[52rpx] h-[52rpx] rounded-full border-[4rpx] border-solid border-white/40 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 box-border z-0"></view>
-              </template>
-              <template v-if="appointmentStatus === 'rejected'">
-                <!-- 驳回红叉 -->
-                <wd-icon name="close" size="24rpx" color="#ffffff" class="z-10" />
-              </template>
-              <template v-else>
-                <text class="text-white text-[24rpx] font-bold z-10">2</text>
-              </template>
-            </view>
-            <text class="text-white text-[20rpx] font-medium whitespace-nowrap">部门负责人</text>
-            <text v-if="appointmentStatus === 'rejected'" class="text-white/80 text-[18rpx] whitespace-nowrap">2-3 15:05</text>
-          </view>
-
-          <!-- 箭头 2 -->
-          <view class="flex-1 flex justify-center pt-[0rpx]">
-            <image src="/static/review/arrow.png" class="w-[64rpx] h-[40rpx] opacity-40" mode="aspectFit" />
-          </view>
-
-          <!-- 步骤3 -->
-          <view class="flex flex-col items-center relative z-10 w-[60rpx]">
-            <view class="w-[40rpx] h-[40rpx] rounded-full bg-white flex items-center justify-center mb-[24rpx]">
-              <text class="text-[#989898] text-[24rpx] font-bold">3</text>
-            </view>
-            <text class="text-white text-[20rpx] whitespace-nowrap">属地单位领导</text>
-          </view>
-
-          <!-- 箭头 3 -->
-          <view class="flex-1 flex justify-center pt-[0rpx]">
-            <image src="/static/review/arrow.png" class="w-[64rpx] h-[40rpx] opacity-40" mode="aspectFit" />
-          </view>
-
-          <!-- 步骤4 -->
-          <view class="flex flex-col items-center relative z-10 w-[60rpx]">
-            <view class="w-[40rpx] h-[40rpx] rounded-full bg-white flex items-center justify-center mb-[24rpx]">
-              <text class="text-[#989898] text-[24rpx] font-bold">4</text>
-            </view>
-            <text class="text-white text-[20rpx] whitespace-nowrap">访客主管部门</text>
-          </view>
-
-          <!-- 箭头 4 -->
-          <view class="flex-1 flex justify-center pt-[0rpx]">
-            <image src="/static/review/arrow.png" class="w-[64rpx] h-[40rpx] opacity-40" mode="aspectFit" />
-          </view>
-
-          <!-- 步骤5 -->
-          <view class="flex flex-col items-center relative z-10 w-[60rpx]">
-            <view class="w-[40rpx] h-[40rpx] rounded-full bg-white flex items-center justify-center mb-[24rpx]">
-              <text class="text-[#989898] text-[24rpx] font-bold">5</text>
-            </view>
-            <text class="text-white text-[20rpx] whitespace-nowrap">主管部门领导</text>
-          </view>
-        </view>
-        
-        <!-- 驳回提示信息 -->
-        <view v-if="appointmentStatus === 'rejected'" class="mt-[16rpx] ml-[-16rpx] w-[702rpx] bg-[rgba(255,230,189,0.6)] border-[2rpx] border-solid border-[#E73232] rounded-full h-[64rpx] flex items-center justify-center box-border relative -left-[8rpx]">
-          <wd-icon name="info-circle" size="28rpx" color="#F5222D" class="mr-[12rpx]" />
-          <text class="text-[#F5222D] text-[26rpx]">部门领导该日外出,请另外预约</text>
-        </view>
-      </view>
-
-      <!-- 待生效状态区 -->
-      <view class="px-[24rpx] mb-[32rpx]" v-if="appointmentStatus === 'pending_effect'">
-        <view class="flex items-center mb-[20rpx]">
-          <image src="/static/review/Pending.png" class="w-[40rpx] h-[40rpx] mr-[8rpx]" mode="aspectFit" />
-          <text class="text-[32rpx] text-white font-bold">待生效...</text>
-        </view>
-        
-        <!-- 分割线 -->
-        <view class="w-full h-[2rpx] bg-white/30 mb-[24rpx]"></view>
-
-        <!-- 有效期 -->
-        <view class="flex items-center mb-[24rpx]">
-          <view class="bg-[#52C41A] w-full rounded-full h-[60rpx] px-[18rpx] flex items-center justify-center">
-            <text class="text-white text-[28rpx] font-medium">有效期: 2023-10-13 10:00 至 2023-10-13 18:00</text>
-          </view>
-        </view>
-
-        <!-- 查看访客码按钮 -->
-        <view class="flex justify-center mb-[85rpx]">
-          <view class="border-[2rpx] border-solid border-white/60 rounded-full w-[280rpx] h-[64rpx] flex items-center justify-center bg-white/20 transition-opacity active:opacity-70" @click="handleViewVisitorCode">
-            <wd-icon name="view" size="32rpx" color="#ffffff" class="mr-[12rpx]" />
-            <text class="text-white text-[28rpx]">查看访客码</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 生效中状态区 -->
-      <view class="px-[24rpx] mb-[32rpx]" v-if="appointmentStatus === 'effective'">
-        <view class="flex items-center mb-[20rpx]">
-          <image src="/static/review/success.png" class="w-[40rpx] h-[40rpx] mr-[8rpx]" mode="aspectFit" />
-          <text class="text-[32rpx] text-white font-bold">生效中</text>
-        </view>
-        
-        <!-- 分割线 -->
-        <view class="w-full h-[2rpx] bg-white/30 mb-[24rpx]"></view>
-
-        <!-- 有效期 -->
-        <view class="flex items-center mb-[24rpx]">
-          <view class="bg-[#52C41A] w-full rounded-full h-[60rpx] px-[18rpx] flex items-center justify-center">
-            <text class="text-white text-[28rpx] font-medium">有效期: 2023-10-13 10:00 至 2023-10-13 18:00</text>
-          </view>
-        </view>
-
-        <!-- 查看访客码按钮 -->
-        <view class="flex justify-center mb-[85rpx]">
-          <view class="border-[2rpx] border-solid border-white/60 rounded-full w-[280rpx] h-[64rpx] flex items-center justify-center bg-white/20 transition-opacity active:opacity-70" @click="handleViewVisitorCode">
-            <wd-icon name="view" size="32rpx" color="#ffffff" class="mr-[12rpx]" />
-            <text class="text-white text-[28rpx]">查看访客码</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 已失效/已取消 状态区 -->
-      <view class="px-[24rpx] mb-[148rpx] mt-[76rpx] flex flex-col items-center" v-if="['cancelled', 'invalid'].includes(appointmentStatus)">
-        <image src="/static/review/cancel.png" class="w-[40rpx] h-[40rpx] mb-[16rpx]" mode="aspectFit" />
-        <text class="text-[32rpx] text-white font-bold">{{ appointmentStatus === 'cancelled' ? '已取消' : '已失效' }}</text>
-      </view>
-
-      <!-- 拜访信息标题 -->
-      <view class="flex items-end mb-[26rpx] px-[8rpx]">
-        <text class="text-[32rpx] text-[#000000] font-[500]">拜访信息</text>
-        <text class="text-[24rpx] text-[#676767] font-[350] ml-[16rpx] mb-[4rpx]">您要见的人</text>
-      </view>
-
-      <!-- 拜访信息卡片 -->
-      <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[48rpx] px-[40rpx] py-[12rpx] box-border">
-        <view class="flex flex-col">
-          <view class="flex justify-between items-center h-[96rpx] border-b1">
-            <text class="text-[#000000] text-[28rpx] font-normal">被访人姓名</text>
-            <text class="text-[#666666] text-[28rpx] font-[350]">员工A(管理部)</text>
-          </view>
-          <view class="flex justify-between items-center h-[96rpx] border-b1">
-            <text class="text-[#000000] text-[28rpx] font-normal">拜访事由</text>
-            <text class="text-[#666666] text-[28rpx] font-[350]">xx区域/xx区域</text>
-          </view>
-          <view class="flex justify-between items-center h-[96rpx]">
-            <text class="text-[#000000] text-[28rpx] font-normal">拜访时间</text>
-            <text class="text-[#666666] text-[28rpx] font-[350]">2023-10-12 00:00 -2023-10-13 23:59</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 来访人信息标题 -->
-      <view class="flex items-end mb-[26rpx] px-[8rpx]">
-        <text class="text-[32rpx] text-[#000000] font-[500]">来访人信息</text>
-        <text class="text-[24rpx] text-[#676767] font-[350] ml-[16rpx] mb-[4rpx]">选择您及同行人员</text>
-      </view>
-
-      <!-- 来访人信息卡片 -->
-      <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[24rpx] box-border pb-[24rpx]">
-        <view class="flex justify-between items-center px-[40rpx] py-[28rpx] border-b1">
-          <text class="text-[#000000] text-[28rpx] font-normal">进场类型</text>
-          <text class="text-[#616161] text-[28rpx] font-[350]">人员+车辆</text>
-        </view>
-        
-        <view class="px-[32rpx] py-[16rpx]">
-          <view 
-            v-for="(visitor, index) in visitors" 
-            :key="visitor.id"
-            class="flex items-center py-[24rpx] border-b2 mb-[16rpx]"
-            :class="index !== visitors.length - 1 ? '' : ''"
-          >
-            <text class="text-[#333333] text-[30rpx] font-medium w-[100rpx] pl-[20rpx] shrink-0">{{ visitor.name }}</text>
-            <view class="flex flex-col gap-[12rpx] flex-1 min-w-0 pl-[36rpx]">
-              <view class="flex items-center text-[#B3B3B3] text-[26rpx]">
-                <image :src="visitor.gender === 'male' ? appointmentIcons.male : appointmentIcons.female" class="w-[32rpx] h-[32rpx] mr-[12rpx] shrink-0" mode="aspectFit" />
-                <text>{{ visitor.phone }}</text>
-              </view>
-              <view class="flex items-center text-[#B3B3B3] text-[26rpx]">
-                <image :src="appointmentIcons.company" class="w-[32rpx] h-[32rpx] mr-[12rpx] shrink-0" mode="aspectFit" />
-                <text class="truncate">{{ visitor.company }}</text>
-              </view>
-              <view v-if="visitor.plate" class="flex items-center text-[#B3B3B3] text-[26rpx]">
-                <view class="w-[28rpx] h-[20rpx] border-[2rpx] border-solid border-[#0068FF] rounded-[4rpx] flex items-center justify-center mr-[12rpx]">
-                  <text class="text-[#0068FF] text-[12rpx] transform scale-75">NO.</text>
+              <text class="text-[#333333] text-[30rpx] font-medium w-[100rpx] pl-[20rpx] shrink-0">{{ visitor.name }}</text>
+              <view class="flex flex-col gap-[12rpx] flex-1 min-w-0 pl-[36rpx]">
+                <view class="flex items-center text-[#B3B3B3] text-[26rpx]">
+                  <image :src="visitorGenderIcon(visitor.gender)" class="w-[32rpx] h-[32rpx] mr-[12rpx] shrink-0" mode="aspectFit" />
+                  <text>{{ visitor.mobileMasked || '-' }}</text>
                 </view>
-                <text>{{ visitor.plate }}</text>
+                <view class="flex items-center text-[#B3B3B3] text-[26rpx]">
+                  <image :src="appointmentIcons.company" class="w-[32rpx] h-[32rpx] mr-[12rpx] shrink-0" mode="aspectFit" />
+                  <text class="truncate">{{ visitor.companyName || '-' }}</text>
+                </view>
+                <view v-if="visitor.plateNo" class="flex items-center text-[#B3B3B3] text-[26rpx]">
+                  <view class="w-[28rpx] h-[20rpx] border-[2rpx] border-solid border-[#0068FF] rounded-[4rpx] flex items-center justify-center mr-[12rpx]">
+                    <text class="text-[#0068FF] text-[12rpx] transform scale-75">NO.</text>
+                  </view>
+                  <text>{{ visitor.plateNo }}</text>
+                </view>
               </view>
             </view>
           </view>
         </view>
-      </view>
 
-      <!-- 提交时间卡片 -->
-      <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[32rpx] px-[40rpx] py-[12rpx] box-border">
-        <view class="flex flex-col">
-          <view class="flex justify-between items-center h-[96rpx]" :class="(appointmentStatus !== 'reviewing' && appointmentStatus !== 'invalid') ? 'border-b1' : ''">
-            <text class="text-[#000000] text-[28rpx] font-normal">提交时间</text>
-            <text class="text-[#666666] text-[28rpx] font-[350]">2023-10-13 12:00</text>
-          </view>
-          <view v-if="['pending_effect', 'effective'].includes(appointmentStatus)" class="flex justify-between items-center h-[96rpx]">
-            <text class="text-[#000000] text-[28rpx] font-normal">审核通过时间</text>
-            <text class="text-[#666666] text-[28rpx] font-[350]">2023-10-13 12:00</text>
-          </view>
-          <view v-if="appointmentStatus === 'cancelled'" class="flex justify-between items-center h-[96rpx]">
-            <text class="text-[#000000] text-[28rpx] font-normal">取消时间</text>
-            <text class="text-[#666666] text-[28rpx] font-[350]">2023-10-13 12:00</text>
+        <view class="flex items-end mb-[26rpx] px-[8rpx]">
+          <text class="text-[32rpx] text-[#000000] font-[500]">审批情况</text>
+        </view>
+
+        <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[32rpx] px-[40rpx] py-[24rpx] box-border">
+          <view v-for="node in detail.flowTree" :key="node.nodeCode" class="py-[20rpx]" :class="node !== detail.flowTree[detail.flowTree.length - 1] ? 'border-b1' : ''">
+            <view class="flex items-center justify-between mb-[12rpx]">
+              <text class="text-[#000000] text-[28rpx] font-medium">{{ node.nodeName }}</text>
+              <text class="text-[24rpx]" :class="flowStatusClass(node.nodeStatus)">{{ flowStatusText(node.nodeStatus) }}</text>
+            </view>
+            <view v-for="user in flowUsers(node)" :key="user.id" class="flex items-center justify-between py-[8rpx]">
+              <text class="text-[#666666] text-[24rpx]">{{ user.name }}</text>
+              <text class="text-[#999999] text-[22rpx]">{{ user.time ? user.time.replace('T', ' ').slice(0, 16) : flowStatusText(user.status) }}</text>
+            </view>
           </view>
         </view>
-      </view>
+
+        <view class="bg-white rounded-[16rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] w-full mb-[32rpx] px-[40rpx] py-[12rpx] box-border">
+          <view class="flex justify-between items-center h-[96rpx]">
+            <text class="text-[#000000] text-[28rpx] font-normal">提交时间</text>
+            <text class="text-[#666666] text-[28rpx] font-[350]">{{ detail.createTime.replace('T', ' ').slice(0, 16) }}</text>
+          </view>
+        </view>
+      </template>
     </view>
 
-    <!-- 底部操作按钮 -->
-    <view class="fixed bottom-0 left-0 w-[750rpx] h-[108rpx] bg-[#FFFFFF] rounded-t-[20rpx] box-content pb-[env(safe-area-inset-bottom)] z-[100] shadow-[inset_0_2rpx_0_0_#E1E1E1] flex justify-end items-center gap-[32rpx]" >
-      <view 
-        v-if="appointmentStatus === 'reviewing'"
+    <view class="fixed bottom-0 left-0 w-[750rpx] h-[108rpx] bg-[#FFFFFF] rounded-t-[20rpx] box-content pb-[env(safe-area-inset-bottom)] z-[100] shadow-[inset_0_2rpx_0_0_#E1E1E1] flex justify-end items-center gap-[32rpx]">
+      <view
+        v-if="detail?.cancelable"
         class="w-[240rpx] h-[76rpx] rounded-full flex items-center justify-center bg-[#F5F5F5] transition-opacity active:opacity-70"
         @click="handleCancel"
       >
         <image src="/static/review/reduce.png" class="w-[40rpx] h-[40rpx] mr-[8rpx]" mode="aspectFit" />
         <text class="text-[30rpx] text-[#999999] font-medium">取消预约</text>
       </view>
-      <view 
-        v-if="appointmentStatus === 'rejected'"
-        class="w-[240rpx] h-[76rpx] rounded-full flex items-center justify-center bg-white border-[2rpx] border-solid border-[#0068FF] transition-opacity active:opacity-70 box-border"
-      >
-        <text class="text-[30rpx] text-[#0068FF] font-medium">重新提交</text>
-      </view>
-      <view 
-        v-if="['cancelled', 'invalid'].includes(appointmentStatus)"
+      <view
+        v-else-if="detail?.deletable"
         class="w-[240rpx] h-[76rpx] rounded-full flex items-center justify-center bg-[#F5F5F5] transition-opacity active:opacity-70"
         @click="handleDelete"
       >
-        <image src="/static/appointment/delete.png" class="w-[40rpx] h-[40rpx] mr-[8rpx]" mode="aspectFit" />
-        <text class="text-[30rpx] text-[#999999] font-medium">删除</text>
+        <wd-icon name="delete-thin" size="32rpx" color="#999999" class="mr-[8rpx]" />
+        <text class="text-[30rpx] text-[#999999] font-medium">删除记录</text>
       </view>
-      <view 
+      <view
         class="w-[240rpx] h-[76rpx] mr-[40rpx] rounded-full flex items-center justify-center bg-[#0068FF] shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)] transition-opacity active:opacity-70"
         @click="handleRebook"
       >
@@ -412,49 +355,36 @@
       </view>
     </view>
 
-    <!-- 取消预约弹窗 -->
     <wd-popup v-model="showCancelModal" custom-style="border-radius: 32rpx; background: transparent;" :safe-area-inset-bottom="false">
       <view class="w-[600rpx] bg-white rounded-[32rpx] flex flex-col items-center pt-[48rpx] pb-[48rpx] px-[40rpx] box-border relative overflow-hidden" style="background: linear-gradient(180deg, #EBF4FF 0%, #FFFFFF 40%);">
         <text class="text-[34rpx] text-[#333333] font-bold mb-[32rpx]">提示</text>
         <image src="/static/review/cancelIcon.png" class="w-[202rpx] h-[202rpx] mb-[32rpx]" mode="aspectFit" />
         <text class="text-[30rpx] text-[#333333] mb-[64rpx]">是否取消本次预约</text>
-        
+
         <view class="flex w-full justify-center gap-[32rpx]">
-          <view 
-            class="w-[212rpx] h-[80rpx] rounded-full bg-[#F5F5F5] flex items-center justify-center transition-opacity active:opacity-70"
-            @click="showCancelModal = false"
-          >
+          <view class="w-[212rpx] h-[80rpx] rounded-full bg-[#F5F5F5] flex items-center justify-center transition-opacity active:opacity-70" @click="showCancelModal = false">
             <text class="text-[#999999] text-[30rpx]">再想想</text>
           </view>
-          <view 
-            class="w-[212rpx] h-[80rpx] rounded-full bg-[#0068FF] flex items-center justify-center transition-opacity active:opacity-70"
-            @click="confirmCancel"
-          >
-            <text class="text-white text-[30rpx]">确认取消</text>
+          <view class="w-[212rpx] h-[80rpx] rounded-full bg-[#0068FF] flex items-center justify-center transition-opacity active:opacity-70" @click="confirmCancel">
+            <text class="text-white text-[30rpx]">{{ canceling ? '取消中...' : '确认取消' }}</text>
           </view>
         </view>
       </view>
     </wd-popup>
 
-    <!-- 删除记录弹窗 -->
     <wd-popup v-model="showDeleteModal" custom-style="border-radius: 32rpx; background: transparent;" :safe-area-inset-bottom="false">
       <view class="w-[600rpx] bg-white rounded-[32rpx] flex flex-col items-center pt-[48rpx] pb-[48rpx] px-[40rpx] box-border relative overflow-hidden" style="background: linear-gradient(180deg, #EBF4FF 0%, #FFFFFF 40%);">
         <text class="text-[34rpx] text-[#333333] font-bold mb-[32rpx]">提示</text>
-        <image src="/static/review/cancelIcon.png" class="w-[202rpx] h-[202rpx] mb-[32rpx]" mode="aspectFit" />
-        <text class="text-[30rpx] text-[#333333] mb-[64rpx]">是否有确认删除此记录?</text>
-        
+        <wd-icon name="delete-thin" size="120rpx" color="#999999" class="mb-[32rpx]" />
+        <text class="text-[30rpx] text-[#333333] mb-[24rpx]">删除后将不再出现在我的预约记录中</text>
+        <text class="text-[24rpx] text-[#999999] mb-[64rpx]">仅当前账号隐藏,不影响后台查看</text>
+
         <view class="flex w-full justify-center gap-[32rpx]">
-          <view 
-            class="w-[212rpx] h-[80rpx] rounded-full bg-[#F5F5F5] flex items-center justify-center transition-opacity active:opacity-70"
-            @click="showDeleteModal = false"
-          >
-            <text class="text-[#999999] text-[30rpx]">再想想</text>
+          <view class="w-[212rpx] h-[80rpx] rounded-full bg-[#F5F5F5] flex items-center justify-center transition-opacity active:opacity-70" @click="showDeleteModal = false">
+            <text class="text-[#999999] text-[30rpx]">取消</text>
           </view>
-          <view 
-            class="w-[212rpx] h-[80rpx] rounded-full bg-[#0068FF] flex items-center justify-center transition-opacity active:opacity-70"
-            @click="confirmDelete"
-          >
-            <text class="text-white text-[30rpx]">确认删除</text>
+          <view class="w-[212rpx] h-[80rpx] rounded-full bg-[#0068FF] flex items-center justify-center transition-opacity active:opacity-70" @click="confirmDelete">
+            <text class="text-white text-[30rpx]">{{ deleting ? '删除中...' : '确认删除' }}</text>
           </view>
         </view>
       </view>
@@ -463,34 +393,21 @@
 </template>
 
 <style scoped lang="scss">
-.border-b1{
+.border-b1 {
   border-bottom: 2rpx solid #F5F5F5;
 }
 
-.border-b2{
+.border-b2 {
   border-bottom: 2rpx dashed #F5F5F5;
 
-  &:last-child{
+  &:last-child {
     border-bottom: none;
     margin-bottom: 0;
   }
 }
-@keyframes ripple {
-  0% {
-    transform: scale(1);
-    opacity: 0.6;
-  }
-  100% {
-    transform: scale(1.6);
-    opacity: 0;
-  }
-}
 
-.ripple-wave {
-  animation: ripple 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
+.last-visitor {
+  border-bottom: none;
+  margin-bottom: 0;
 }
-
-.ripple-delay {
-  animation-delay: 1s;
-}
-</style>
\ No newline at end of file
+</style>

--
Gitblit v1.7.1