longz
2026-05-28 23fe797f9dded6d7ec9ecbcf760f764374dea8e0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<script setup lang="ts">
import { fetchMiniFieldConfigs } from '@/api/lookup'
import { fetchMyVisitors } from '@/api/visitor'
import { FIELD_CODES } from '@/constants/fieldCodes'
import type { MiniFieldConfigItem } from '@/types/lookup'
import type { MiniVisitorListItem } from '@/types/visitor'
 
definePage({
  name: 'commonVisitors',
  layout: 'default',
  style: {
    navigationStyle: 'custom',
  },
})
 
const router = useRouter()
const choosePersonAssetBase = '/static/choosePerson'
 
const sysInfo = uni.getSystemInfoSync()
const statusBarHeight = sysInfo.statusBarHeight || 0
const navBarHeight = 44
 
const appointmentIcons = {
  male: '/static/appointment/man.png',
  female: '/static/appointment/woman.png',
  company: '/static/appointment/company.png',
}
 
const visitors = ref<MiniVisitorListItem[]>([])
const loading = ref(false)
const fieldConfigs = ref<MiniFieldConfigItem[]>([])
 
const showName = computed(() => isVisible(FIELD_CODES.VISITOR_NAME))
const showGender = computed(() => isVisible(FIELD_CODES.GENDER))
const showIdCard = computed(() => isVisible(FIELD_CODES.ID_CARD))
const showCompany = computed(() => isVisible(FIELD_CODES.COMPANY))
 
function isVisible(code: string) {
  const config = fieldConfigs.value.find(item => item.fieldCode === code)
  return config ? config.visible === 1 : true
}
 
function displayName(item: MiniVisitorListItem) {
  return showName.value ? item.visitorName || '来访人' : '来访人'
}
 
function handleEdit(item: MiniVisitorListItem) {
  uni.navigateTo({
    url: `/pages/addPerson/index?id=${item.id}&uuid=${item.uuid}`,
  })
}
 
function handleBack() {
  router.back()
}
 
function handleAdd() {
  uni.navigateTo({ url: '/pages/addPerson/index' })
}
 
async function loadVisitors() {
  loading.value = true
  try {
    const [configs, list] = await Promise.all([
      fetchMiniFieldConfigs(),
      fetchMyVisitors(),
    ])
    fieldConfigs.value = configs
    visitors.value = list
  }
  catch (error) {
    uni.showToast({
      title: error instanceof Error ? error.message : '加载来访人失败',
      icon: 'none',
    })
  }
  finally {
    loading.value = false
  }
}
 
onShow(() => {
  void loadVisitors()
})
</script>
 
<template>
  <view class="min-h-screen w-full relative box-border bg-[#F6FBFF] overflow-hidden">
    <image
      :src="`${choosePersonAssetBase}/bg@2x.png`"
      class="absolute left-0 top-0 w-full z-0"
      mode="widthFix"
    />
 
    <view class="relative z-50 w-full" :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">
          <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>
 
    <view v-if="!loading && visitors.length === 0" class="absolute top-[268rpx] left-0 w-full z-10 flex flex-col items-center">
      <image :src="`${choosePersonAssetBase}/noData.png`" class="w-[380rpx] h-[380rpx]" mode="aspectFit" />
      <text class="text-[28rpx] text-[#B3B3B3]">
        暂无来访人信息,请先添加~
      </text>
    </view>
 
    <view v-else class="relative z-10 px-[24rpx] pb-[200rpx] mt-[5vh]">
      <view class="w-full flex flex-col gap-[16rpx]">
        <view
          v-for="(item, index) in visitors"
          :key="item.id"
          class="relative flex items-center justify-between px-[24rpx] py-[24rpx] bg-white rounded-[8rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)]"
        >
          <view class="flex items-center w-[160rpx] shrink-0">
            <text class="text-[28rpx] text-[#333333] font-medium truncate">{{ displayName(item) }}</text>
          </view>
 
          <view class="flex flex-col justify-center flex-1 mx-[20rpx] min-w-0">
            <view v-if="showGender || showIdCard" class="flex items-center">
              <image :src="item.gender === '女' ? appointmentIcons.female : appointmentIcons.male" class="w-[32rpx] h-[32rpx] shrink-0" />
              <text class="ml-[20rpx] text-[28rpx] text-[#B3B3B3] truncate">
                {{ showIdCard ? (item.idCardMasked || '-') : '-' }}
              </text>
            </view>
            <view v-if="showCompany" class="flex items-center mt-[8rpx]">
              <image :src="appointmentIcons.company" class="w-[32rpx] h-[32rpx] shrink-0" />
              <text class="ml-[20rpx] text-[28rpx] text-[#B3B3B3] truncate">{{ item.visitorCompany || '-' }}</text>
            </view>
            <text v-if="item.missingFieldCodes?.length" class="mt-[8rpx] text-[22rpx] text-[#F5222D] truncate">
              缺失:{{ item.missingFieldCodes.join('、') }}
            </text>
          </view>
 
          <view class="w-[40rpx] h-[40rpx] shrink-0 flex items-center justify-center" @click.stop="handleEdit(item)">
            <image :src="`${choosePersonAssetBase}/edit.png`" class="w-[33rpx] h-[33rpx]" />
          </view>
        </view>
      </view>
    </view>
 
    <view class="fixed bottom-[48rpx] left-0 w-full px-[32rpx] box-border z-[100]">
      <view class="h-[96rpx] rounded-full bg-[#0068FF] flex items-center justify-center shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)]" @click="handleAdd">
        <text class="text-[32rpx] text-white font-medium">添加来访人</text>
      </view>
    </view>
  </view>
</template>
 
<style scoped>
</style>