From e45e74e5ae207f7642be9a88ed99b593de89fb3b Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期三, 24 六月 2026 16:54:46 +0800
Subject: [PATCH] 1.0
---
yuyueMiniProgram/src/pages/commonVisitors/index.vue | 148 +++++++++++++++++++++++++++++++++++++++----------
1 files changed, 117 insertions(+), 31 deletions(-)
diff --git a/yuyueMiniProgram/src/pages/commonVisitors/index.vue b/yuyueMiniProgram/src/pages/commonVisitors/index.vue
index 0f01bf9..ce8c5f1 100644
--- a/yuyueMiniProgram/src/pages/commonVisitors/index.vue
+++ b/yuyueMiniProgram/src/pages/commonVisitors/index.vue
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { fetchMiniFieldConfigs } from '@/api/lookup'
-import { fetchMyVisitors, setDefaultVisitor } from '@/api/visitor'
+import { deleteVisitor, fetchMyVisitors, setDefaultVisitor } from '@/api/visitor'
import { FIELD_CODES, formatVisitorFieldLabels } from '@/constants/fieldCodes'
import type { MiniFieldConfigItem } from '@/types/lookup'
import type { MiniVisitorListItem } from '@/types/visitor'
@@ -29,7 +29,13 @@
const visitors = ref<MiniVisitorListItem[]>([])
const loading = ref(false)
const settingDefaultId = ref<number | null>(null)
+const deletingId = ref<number | null>(null)
+const openSwipeId = ref<number | null>(null)
const fieldConfigs = ref<MiniFieldConfigItem[]>([])
+
+let touchStartX = 0
+let touchStartY = 0
+let touchingId: number | null = null
const showName = computed(() => isVisible(FIELD_CODES.VISITOR_NAME))
const showGender = computed(() => isVisible(FIELD_CODES.GENDER))
@@ -50,6 +56,7 @@
}
function handleEdit(item: MiniVisitorListItem) {
+ openSwipeId.value = null
uni.navigateTo({
url: `/pages/addPerson/index?id=${item.id}&uuid=${item.uuid}`,
})
@@ -67,6 +74,7 @@
if (item.isDefault || settingDefaultId.value) {
return
}
+ openSwipeId.value = null
settingDefaultId.value = item.id
try {
await setDefaultVisitor(item.id, item.uuid)
@@ -81,6 +89,73 @@
}
finally {
settingDefaultId.value = null
+ }
+}
+
+function handleTouchStart(item: MiniVisitorListItem, event: any) {
+ const touch = event.touches?.[0]
+ if (!touch) {
+ return
+ }
+ touchingId = item.id
+ touchStartX = touch.clientX
+ touchStartY = touch.clientY
+}
+
+function handleTouchEnd(item: MiniVisitorListItem, event: any) {
+ if (touchingId !== item.id) {
+ return
+ }
+ const touch = event.changedTouches?.[0]
+ touchingId = null
+ if (!touch) {
+ return
+ }
+ const deltaX = touch.clientX - touchStartX
+ const deltaY = touch.clientY - touchStartY
+ if (Math.abs(deltaX) <= Math.abs(deltaY) + 10) {
+ return
+ }
+ if (deltaX < -40) {
+ openSwipeId.value = item.id
+ } else if (deltaX > 20) {
+ openSwipeId.value = null
+ }
+}
+
+function confirmDelete(item: MiniVisitorListItem) {
+ if (deletingId.value) {
+ return
+ }
+ uni.showModal({
+ title: '删除确认',
+ content: `确认删除来访人「${displayName(item)}」?`,
+ confirmText: '删除',
+ confirmColor: '#FF4D4F',
+ success: (res) => {
+ if (res.confirm) {
+ void handleDelete(item)
+ }
+ },
+ })
+}
+
+async function handleDelete(item: MiniVisitorListItem) {
+ deletingId.value = item.id
+ try {
+ await deleteVisitor(item.id, item.uuid)
+ openSwipeId.value = null
+ uni.showToast({ title: '已删除', icon: 'none' })
+ await loadVisitors()
+ }
+ catch (error) {
+ uni.showToast({
+ title: error instanceof Error ? error.message : '删除来访人失败',
+ icon: 'none',
+ })
+ }
+ finally {
+ deletingId.value = null
}
}
@@ -140,42 +215,53 @@
<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"
+ v-for="item in visitors"
:key="item.id"
- class="relative flex items-center justify-between px-[24rpx] pt-[24rpx] pb-[64rpx] bg-white rounded-[8rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)]"
+ class="relative overflow-hidden rounded-[8rpx] bg-[#FF4D4F]"
>
- <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">
- 缺失:{{ formatMissingFields(item.missingFieldCodes) }}
- </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 class="absolute right-0 top-0 bottom-0 w-[132rpx] bg-[#FF4D4F] flex items-center justify-center z-0" @click.stop="confirmDelete(item)">
+ <text class="text-[28rpx] text-white">{{ deletingId === item.id ? '删除中' : '删除' }}</text>
</view>
<view
- class="absolute right-[24rpx] bottom-[18rpx] h-[36rpx] px-[18rpx] rounded-full flex items-center justify-center"
- :class="item.isDefault ? 'bg-[#E6F0FF]' : 'bg-[#F5F7FA]'"
- @click.stop="handleSetDefault(item)"
+ class="relative z-10 flex items-center justify-between px-[24rpx] pt-[24rpx] pb-[64rpx] bg-white rounded-[8rpx] shadow-[0_4rpx_8rpx_rgba(6,47,108,0.1608)] transition-transform duration-200"
+ :style="{ transform: openSwipeId === item.id ? 'translateX(-132rpx)' : 'translateX(0)' }"
+ @touchstart="handleTouchStart(item, $event)"
+ @touchend="handleTouchEnd(item, $event)"
>
- <text class="text-[22rpx]" :class="item.isDefault ? 'text-[#0068FF]' : 'text-[#999999]'">
- {{ item.isDefault ? '默认来访人' : (settingDefaultId === item.id ? '设置中...' : '设为默认') }}
- </text>
+ <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">
+ 缺失:{{ formatMissingFields(item.missingFieldCodes) }}
+ </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
+ class="absolute right-[24rpx] bottom-[18rpx] h-[36rpx] px-[18rpx] rounded-full flex items-center justify-center"
+ :class="item.isDefault ? 'bg-[#E6F0FF]' : 'bg-[#F5F7FA]'"
+ @click.stop="handleSetDefault(item)"
+ >
+ <text class="text-[22rpx]" :class="item.isDefault ? 'text-[#0068FF]' : 'text-[#999999]'">
+ {{ item.isDefault ? '默认来访人' : (settingDefaultId === item.id ? '设置中...' : '设为默认') }}
+ </text>
+ </view>
</view>
</view>
</view>
--
Gitblit v1.7.1