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/choosePerson/index.vue | 235 +++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 156 insertions(+), 79 deletions(-)
diff --git a/yuyueMiniProgram/src/pages/choosePerson/index.vue b/yuyueMiniProgram/src/pages/choosePerson/index.vue
index 5b95aad..09dcacc 100644
--- a/yuyueMiniProgram/src/pages/choosePerson/index.vue
+++ b/yuyueMiniProgram/src/pages/choosePerson/index.vue
@@ -1,5 +1,9 @@
<script setup lang="ts">
-import { ref } from 'vue'
+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: 'choosePerson',
@@ -8,6 +12,8 @@
navigationStyle: 'custom',
},
})
+
+type PageMode = 'manage' | 'pick' | 'driver'
const router = useRouter()
const choosePersonAssetBase = '/static/choosePerson'
@@ -22,43 +28,82 @@
company: '/static/appointment/company.png',
}
-interface Visitor {
- id: number
- name: string
- phone: string
- company: string
- gender: 'male' | 'female'
+const mode = ref<PageMode>('manage')
+const visitors = ref<MiniVisitorListItem[]>([])
+const loading = ref(false)
+const fieldConfigs = ref<MiniFieldConfigItem[]>([])
+const selectedId = ref<number | null>(null)
+const selectedIds = ref<number[]>([])
+const vehicleIndex = ref<number>(-1)
+
+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
}
-// 模拟有数据状态
-const visitors = ref<Visitor[]>([
- { id: 1, name: '张三', phone: '5120********258', company: '成都xxxxxx有限公司', gender: 'male' },
- { id: 2, name: '张三', phone: '5120********258', company: '成都xxxxxx有限公司', gender: 'female' },
- { id: 3, name: '张三', phone: '5120********258', company: '成都xxxxxx有限公司', gender: 'female' },
-])
+function displayName(item: MiniVisitorListItem) {
+ return showName.value ? item.visitorName || '来访人' : '来访人'
+}
-const selectedId = ref<number>(1)
+function normalizeGender(gender?: string | null) {
+ return gender === '女' ? 'female' : 'male'
+}
function handleSelect(id: number) {
+ if (mode.value === 'pick') {
+ if (selectedIds.value.includes(id)) {
+ selectedIds.value = selectedIds.value.filter(item => item !== id)
+ return
+ }
+ selectedIds.value = [...selectedIds.value, id]
+ return
+ }
selectedId.value = id
}
-function handleEdit(id: number) {
- // 跳转到编辑页面
- uni.showToast({ title: '编辑功能开发中', icon: 'none' })
+function handleEdit(item: MiniVisitorListItem) {
+ uni.navigateTo({
+ url: `/pages/addPerson/index?id=${item.id}&uuid=${item.uuid}`,
+ })
+}
+
+function buildPayload(items: MiniVisitorListItem[]) {
+ return items.map(item => ({
+ id: item.id,
+ uuid: item.uuid,
+ name: item.visitorName,
+ phone: '',
+ company: item.visitorCompany || '',
+ gender: normalizeGender(item.gender),
+ idCardMasked: item.idCardMasked || '',
+ }))
}
function handleConfirm() {
- if (visitors.value.length > 0) {
- const selectedVisitor = visitors.value.find(v => v.id === selectedId.value)
- if (selectedVisitor) {
- uni.$emit('acceptVisitor', selectedVisitor)
- }
- uni.showToast({ title: '选择成功', icon: 'success' })
- setTimeout(() => {
- router.back()
- }, 1000)
+ if (mode.value === 'manage') {
+ router.back()
+ return
}
+
+ const picked = mode.value === 'pick'
+ ? visitors.value.filter(item => selectedIds.value.includes(item.id))
+ : visitors.value.filter(item => item.id === selectedId.value)
+
+ uni.$emit('selectVisitors', {
+ mode: mode.value,
+ vehicleIndex: vehicleIndex.value,
+ visitors: buildPayload(picked),
+ })
+
+ uni.showToast({ title: '选择成功', icon: 'success' })
+ setTimeout(() => {
+ router.back()
+ }, 300)
}
function handleBack() {
@@ -68,65 +113,98 @@
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
+
+ if (mode.value === 'pick') {
+ selectedIds.value = selectedIds.value.filter(id => list.some(item => item.id === id))
+ }
+ else {
+ selectedId.value = list.find(item => item.id === selectedId.value)?.id ?? list[0]?.id ?? null
+ }
+ }
+ catch (error) {
+ uni.showToast({
+ title: error instanceof Error ? error.message : '加载来访人失败',
+ icon: 'none',
+ })
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+onLoad((options) => {
+ const pageMode = options?.mode as PageMode | undefined
+ if (pageMode === 'pick' || pageMode === 'driver' || pageMode === 'manage') {
+ mode.value = pageMode
+ }
+
+ vehicleIndex.value = options?.vehicleIndex ? Number(options.vehicleIndex) : -1
+
+ if (mode.value === 'pick' && options?.selectedIds) {
+ selectedIds.value = options.selectedIds
+ .split(',')
+ .map(item => Number(item))
+ .filter(item => !Number.isNaN(item))
+ }
+
+ if (mode.value === 'driver' && options?.selectedId) {
+ const id = Number(options.selectedId)
+ selectedId.value = Number.isNaN(id) ? null : id
+ }
+})
+
+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`"
+ <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 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 class="w-[88rpx]" />
</view>
</view>
- <!-- 内容区 -->
- <view v-if="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"
- />
- <!-- 提示文案 -->
+ <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
- class="mt-[94rpx] w-[486rpx] h-[108rpx] rounded-full bg-[#0068FF] flex items-center justify-center shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)]"
- @click="handleAdd"
- >
+ <view class="mt-[94rpx] w-[486rpx] h-[108rpx] rounded-full bg-[#0068FF] flex items-center justify-center shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)]" @click="handleAdd">
<image :src="`${choosePersonAssetBase}/add.png`" class="w-[40rpx] h-[40rpx]" />
<text class="ml-[12rpx] text-[32rpx] text-white font-medium">立即添加</text>
</view>
- <!-- 返回按钮 -->
- <view
- class="mt-[48rpx] w-[486rpx] h-[108rpx] rounded-full border-[2rpx] border-solid border-[#0068FF] bg-white flex items-center justify-center"
- @click="handleBack"
- >
+ <view class="mt-[48rpx] w-[486rpx] h-[108rpx] rounded-full border-[2rpx] border-solid border-[#0068FF] bg-white flex items-center justify-center" @click="handleBack">
<text class="text-[32rpx] text-[#0068FF] font-medium">返回</text>
</view>
</view>
- <!-- 有数据时的列表展示 -->
- <view v-else class="relative z-10 px-[24rpx] pb-[200rpx]" >
- <!-- 顶部添加按钮 -->
+ <view v-else class="relative z-10 px-[24rpx] pb-[200rpx]">
<view class="w-full bg-white/50 rounded-[8rpx] h-[144rpx] flex items-center justify-center shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] mb-[24rpx]" @click="handleAdd">
<view class="flex items-center justify-center w-[252rpx] h-[64rpx] rounded-full border-[2rpx] border-solid border-[#0068FF] bg-transparent">
<wd-icon name="add-circle" size="28rpx" color="#1677FF" />
@@ -134,53 +212,52 @@
</view>
</view>
- <!-- 人员卡片列表 -->
<view class="w-full bg-white rounded-[8rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] overflow-hidden">
- <view
- v-for="(item, index) in visitors"
+ <view
+ v-for="(item, index) in visitors"
:key="item.id"
class="relative flex items-center justify-between px-[24rpx] py-[24rpx]"
@click="handleSelect(item.id)"
>
- <!-- 底部分隔线 (最后一个元素不显示) -->
- <view v-if="index < visitors.length - 1" class="absolute bottom-0 right-0 w-[574rpx] h-[2rpx] bg-[#F5F5F5]"></view>
+ <view v-if="index < visitors.length - 1" class="absolute bottom-0 right-0 w-[574rpx] h-[2rpx] bg-[#F5F5F5]" />
- <!-- 左侧:单选按钮和名字 -->
<view class="flex items-center w-[160rpx] shrink-0">
- <!-- 自定义单选框 -->
- <view class="w-[32rpx] h-[32rpx] rounded-full border-[2rpx] border-solid flex items-center justify-center transition-colors duration-200 shrink-0"
- :class="selectedId === item.id ? 'border-[#0068FF]' : 'border-[#CFCFCF]'">
- <view v-if="selectedId === item.id" class="w-[16rpx] h-[16rpx] rounded-full bg-[#0068FF]"></view>
+ <view
+ class="w-[32rpx] h-[32rpx] rounded-full border-[2rpx] border-solid flex items-center justify-center transition-colors duration-200 shrink-0"
+ :class="mode === 'pick'
+ ? (selectedIds.includes(item.id) ? 'border-[#0068FF]' : 'border-[#CFCFCF]')
+ : (selectedId === item.id ? 'border-[#0068FF]' : 'border-[#CFCFCF]')"
+ >
+ <view v-if="mode === 'pick' ? selectedIds.includes(item.id) : selectedId === item.id" class="w-[16rpx] h-[16rpx] rounded-full bg-[#0068FF]" />
</view>
- <text class="ml-[16rpx] text-[28rpx] text-[#333333] font-medium truncate">{{ item.name }}</text>
+ <text class="ml-[16rpx] 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 class="flex items-center">
- <image :src="item.gender === 'female' ? appointmentIcons.female : appointmentIcons.male" class="w-[32rpx] h-[32rpx] shrink-0" />
- <text class="ml-[20rpx] text-[28rpx] text-[#B3B3B3] truncate">{{ item.phone }}</text>
+ <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">
+ {{ [showGender ? item.gender : '', showIdCard ? item.idCardMasked || '-' : ''].filter(Boolean).join(' ') }}
+ </text>
</view>
- <view class="flex items-center mt-[8rpx]">
+ <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.company }}</text>
+ <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.id)">
+ <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 v-if="visitors.length > 0" 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="handleConfirm"
- >
+ <view class="h-[96rpx] rounded-full bg-[#0068FF] flex items-center justify-center shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)]" @click="handleConfirm">
<text class="text-[32rpx] text-white font-medium">确认</text>
</view>
</view>
--
Gitblit v1.7.1