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/addPerson/index.vue | 238 ++++++++++++++++++++++++++++++++++++++---------------------
1 files changed, 153 insertions(+), 85 deletions(-)
diff --git a/yuyueMiniProgram/src/pages/addPerson/index.vue b/yuyueMiniProgram/src/pages/addPerson/index.vue
index 5ba6cbe..d4e792b 100644
--- a/yuyueMiniProgram/src/pages/addPerson/index.vue
+++ b/yuyueMiniProgram/src/pages/addPerson/index.vue
@@ -1,27 +1,37 @@
<script setup lang="ts">
-import { reactive } from 'vue'
+import { createVisitor, fetchVisitorDetail, updateVisitor } from '@/api/visitor'
+import { fetchMiniFieldConfigs } from '@/api/lookup'
+import { FIELD_CODES } from '@/constants/fieldCodes'
+import type { MiniFieldConfigItem } from '@/types/lookup'
+import type { MiniVisitorSaveRequest } from '@/types/visitor'
definePage({
name: 'addPerson',
layout: 'default',
style: {
- navigationStyle: 'custom'
+ navigationStyle: 'custom',
},
})
const router = useRouter()
-// 状态栏高度
const sysInfo = uni.getSystemInfoSync()
const statusBarHeight = sysInfo.statusBarHeight || 0
const navBarHeight = 44
+
+const pageMode = ref<'add' | 'edit'>('add')
+const visitorId = ref<number>()
+const visitorUuid = ref('')
+const loading = ref(false)
+const saving = ref(false)
+const fieldConfigs = ref<MiniFieldConfigItem[]>([])
const formData = reactive({
name: '',
phone: '',
company: '',
idCard: '',
- gender: 'male',
+ gender: '男' as '男' | '女',
})
const errors = reactive({
@@ -31,35 +41,61 @@
idCard: '',
})
-function validate() {
- let isValid = true
+const pageTitle = computed(() => pageMode.value === 'edit' ? '编辑来访人' : '新增来访人')
+
+function findConfig(code: string) {
+ return fieldConfigs.value.find(item => item.fieldCode === code)
+}
+
+function isVisible(code: string) {
+ const config = findConfig(code)
+ return config ? config.visible === 1 : true
+}
+
+function isRequired(code: string) {
+ const config = findConfig(code)
+ return config ? config.requiredFlag === 1 : false
+}
+
+function resetErrors() {
errors.name = ''
errors.phone = ''
errors.company = ''
errors.idCard = ''
+}
- if (!formData.name) {
+function validate() {
+ let isValid = true
+ resetErrors()
+
+ if (isVisible(FIELD_CODES.VISITOR_NAME) && isRequired(FIELD_CODES.VISITOR_NAME) && !formData.name.trim()) {
errors.name = '请输入来访人姓名'
isValid = false
}
-
- if (!formData.phone) {
+ else if (formData.name && formData.name.trim().length > 30) {
+ errors.name = '来访人姓名不能超过30个字'
+ isValid = false
+ }
+
+ if (isVisible(FIELD_CODES.MOBILE) && isRequired(FIELD_CODES.MOBILE) && !formData.phone.trim()) {
errors.phone = '请输入来访人电话'
isValid = false
- } else if (!/^1[3-9]\d{9}$/.test(formData.phone)) {
+ }
+ else if (formData.phone && !/^\d{11}$/.test(formData.phone.trim())) {
errors.phone = '请确保电话号码录入正确!'
isValid = false
}
-
- if (!formData.company) {
+
+ if (isVisible(FIELD_CODES.COMPANY) && isRequired(FIELD_CODES.COMPANY) && !formData.company.trim()) {
errors.company = '请输入来访人单位'
isValid = false
}
-
- if (!formData.idCard) {
+
+ if (isVisible(FIELD_CODES.ID_CARD) && isRequired(FIELD_CODES.ID_CARD) && !formData.idCard.trim()) {
errors.idCard = '请输入身份证号码'
isValid = false
- } else if (!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(formData.idCard)) {
+ }
+ else if (formData.idCard && !/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(formData.idCard.trim())) {
errors.idCard = '请确保身份证号码录入正确!'
isValid = false
}
@@ -67,145 +103,177 @@
return isValid
}
-function handleSave() {
- if (validate()) {
+function buildPayload(): MiniVisitorSaveRequest {
+ return {
+ visitorName: formData.name.trim(),
+ visitorMobile: formData.phone.trim(),
+ visitorCompany: formData.company.trim(),
+ idCard: formData.idCard.trim(),
+ gender: formData.gender,
+ }
+}
+
+async function loadFieldConfigs() {
+ fieldConfigs.value = await fetchMiniFieldConfigs()
+}
+
+async function loadDetail() {
+ if (pageMode.value !== 'edit' || !visitorId.value || !visitorUuid.value) {
+ return
+ }
+ const detail = await fetchVisitorDetail(visitorId.value, visitorUuid.value)
+ formData.name = detail.visitorName || ''
+ formData.phone = detail.visitorMobile || ''
+ formData.company = detail.visitorCompany || ''
+ formData.idCard = detail.idCard || ''
+ formData.gender = detail.gender === '女' ? '女' : '男'
+}
+
+async function initPage() {
+ loading.value = true
+ try {
+ await loadFieldConfigs()
+ await loadDetail()
+ }
+ catch (error) {
+ uni.showToast({
+ title: error instanceof Error ? error.message : '加载来访人信息失败',
+ icon: 'none',
+ })
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+async function handleSave() {
+ if (saving.value || !validate()) {
+ return
+ }
+ saving.value = true
+ try {
+ if (pageMode.value === 'edit' && visitorId.value && visitorUuid.value) {
+ await updateVisitor(visitorId.value, visitorUuid.value, buildPayload())
+ }
+ else {
+ await createVisitor(buildPayload())
+ }
uni.showToast({ title: '保存成功', icon: 'success' })
setTimeout(() => {
router.back()
- }, 1500)
+ }, 300)
+ }
+ catch (error) {
+ uni.showToast({
+ title: error instanceof Error ? error.message : '保存来访人失败',
+ icon: 'none',
+ })
+ }
+ finally {
+ saving.value = false
}
}
+
+onLoad((options) => {
+ visitorId.value = options?.id ? Number(options.id) : undefined
+ visitorUuid.value = options?.uuid || ''
+ pageMode.value = visitorId.value && visitorUuid.value ? 'edit' : 'add'
+ void initPage()
+})
</script>
<template>
<view class="min-h-screen bg-[#F6FBFF] box-border relative pb-[200rpx]">
-
- <!-- 自定义导航栏 -->
<view class="relative z-50 w-full bg-[#F6FBFF]" :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="router.back()">
<wd-icon name="arrow-left" size="40rpx" color="#333333" />
</view>
<view class="absolute left-0 w-full text-center text-[34rpx] font-medium text-[#333333] z-0">
- 选择来访人
+ {{ pageTitle }}
</view>
- <view class="w-[88rpx]"></view>
+ <view class="w-[88rpx]" />
</view>
</view>
- <!-- 表单区域 -->
- <view class="bg-white px-[64rpx] py-[48rpx]">
-
- <!-- 来访人姓名 -->
- <view class="py-[28rpx] flex items-start justify-between border-b1">
+ <view v-if="loading" class="px-[64rpx] py-[80rpx] text-center text-[28rpx] text-[#999999]">
+ 加载中...
+ </view>
+
+ <view v-else class="bg-white px-[64rpx] py-[48rpx]">
+ <view v-if="isVisible(FIELD_CODES.VISITOR_NAME)" class="py-[28rpx] flex items-start justify-between border-b1">
<view class="flex flex-col shrink-0 mr-[32rpx]">
<view class="flex items-center h-[40rpx]">
<text class="text-[28rpx] text-[#000000]">来访人姓名</text>
- <text class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
+ <text v-if="isRequired(FIELD_CODES.VISITOR_NAME)" class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
</view>
<text v-if="errors.name" class="text-[20rpx] text-[#F5222D] mt-[8rpx]">{{ errors.name }}</text>
</view>
- <input
- v-model="formData.name"
- placeholder="请输入来访人姓名"
- placeholder-class="text-[#B3B3B3] text-[28rpx]"
- class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]"
- />
+ <input v-model="formData.name" placeholder="请输入来访人姓名" placeholder-class="text-[#B3B3B3] text-[28rpx]" class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]" />
</view>
- <!-- 来访人电话 -->
- <view class=" py-[28rpx] flex items-start justify-between border-b1">
+ <view v-if="isVisible(FIELD_CODES.MOBILE)" class="py-[28rpx] flex items-start justify-between border-b1">
<view class="flex flex-col shrink-0 mr-[32rpx]">
<view class="flex items-center h-[40rpx]">
<text class="text-[28rpx] text-[#000000]">来访人电话</text>
- <text class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
+ <text v-if="isRequired(FIELD_CODES.MOBILE)" class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
</view>
<text v-if="errors.phone" class="text-[20rpx] text-[#F5222D] mt-[8rpx]">{{ errors.phone }}</text>
</view>
- <input
- v-model="formData.phone"
- type="number"
- maxlength="11"
- placeholder="请输入来访人电话"
- placeholder-class="text-[#B3B3B3] text-[28rpx]"
- class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]"
- />
+ <input v-model="formData.phone" type="number" maxlength="11" placeholder="请输入来访人电话" placeholder-class="text-[#B3B3B3] text-[28rpx]" class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]" />
</view>
- <!-- 来访人单位 -->
- <view class=" py-[28rpx] flex items-start justify-between border-b1">
+ <view v-if="isVisible(FIELD_CODES.COMPANY)" class="py-[28rpx] flex items-start justify-between border-b1">
<view class="flex flex-col shrink-0 mr-[32rpx]">
<view class="flex items-center h-[40rpx]">
<text class="text-[28rpx] text-[#000000]">来访人单位</text>
- <text class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
+ <text v-if="isRequired(FIELD_CODES.COMPANY)" class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
</view>
<text v-if="errors.company" class="text-[20rpx] text-[#F5222D] mt-[8rpx]">{{ errors.company }}</text>
</view>
- <input
- v-model="formData.company"
- placeholder="请输入来访人单位"
- placeholder-class="text-[#B3B3B3] text-[28rpx]"
- class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]"
- />
+ <input v-model="formData.company" placeholder="请输入来访人单位" placeholder-class="text-[#B3B3B3] text-[28rpx]" class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]" />
</view>
- <!-- 身份证号码 -->
- <view class=" py-[28rpx] flex items-start justify-between border-b1">
+ <view v-if="isVisible(FIELD_CODES.ID_CARD)" class="py-[28rpx] flex items-start justify-between border-b1">
<view class="flex flex-col shrink-0 mr-[32rpx]">
<view class="flex items-center h-[40rpx]">
<text class="text-[28rpx] text-[#000000]">身份证号码</text>
- <text class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
+ <text v-if="isRequired(FIELD_CODES.ID_CARD)" class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
</view>
<text v-if="errors.idCard" class="text-[20rpx] text-[#F5222D] mt-[8rpx]">{{ errors.idCard }}</text>
</view>
- <input
- v-model="formData.idCard"
- type="idcard"
- placeholder="请输入身份证号码"
- placeholder-class="text-[#B3B3B3] text-[28rpx]"
- class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]"
- />
+ <input v-model="formData.idCard" type="idcard" placeholder="请输入身份证号码" placeholder-class="text-[#B3B3B3] text-[28rpx]" class="text-[28rpx] text-right text-[#333333] flex-1 h-[40rpx] min-h-[40rpx]" />
</view>
- <!-- 性别 -->
- <view class="py-[28rpx] flex items-start justify-between">
+ <view v-if="isVisible(FIELD_CODES.GENDER)" class="py-[28rpx] flex items-start justify-between">
<view class="flex flex-col shrink-0 mr-[32rpx]">
<view class="flex items-center h-[40rpx]">
<text class="text-[28rpx] text-[#000000]">性别</text>
- <text class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
+ <text v-if="isRequired(FIELD_CODES.GENDER)" class="text-[28rpx] text-[#F5222D] ml-[4rpx]">*</text>
</view>
</view>
<view class="flex items-center space-x-[48rpx] h-[40rpx]">
- <view class="flex items-center" @click="formData.gender = 'male'">
- <view class="w-[32rpx] h-[32rpx] rounded-full border-[2rpx] border-solid flex items-center justify-center transition-colors duration-200"
- :class="formData.gender === 'male' ? 'border-[#0068FF]' : 'border-[#CFCFCF]'">
- <view v-if="formData.gender === 'male'" class="w-[24rp x] h-[24rpx] rounded-full bg-[#0068FF]"></view>
+ <view class="flex items-center" @click="formData.gender = '男'">
+ <view class="w-[32rpx] h-[32rpx] rounded-full border-[2rpx] border-solid flex items-center justify-center transition-colors duration-200" :class="formData.gender === '男' ? 'border-[#0068FF]' : 'border-[#CFCFCF]'">
+ <view v-if="formData.gender === '男'" class="w-[24rpx] h-[24rpx] rounded-full bg-[#0068FF]" />
</view>
<text class="ml-[12rpx] text-[28rpx] text-[#333333]">男</text>
</view>
- <view class="flex items-center" @click="formData.gender = 'female'">
- <view class="w-[32rpx] h-[32rpx] rounded-full border-[2rpx] border-solid flex items-center justify-center transition-colors duration-200"
- :class="formData.gender === 'female' ? 'border-[#0068FF]' : 'border-[#CFCFCF]'">
- <view v-if="formData.gender === 'female'" class="w-[24rpx] h-[24rpx] rounded-full bg-[#0068FF]"></view>
+ <view class="flex items-center" @click="formData.gender = '女'">
+ <view class="w-[32rpx] h-[32rpx] rounded-full border-[2rpx] border-solid flex items-center justify-center transition-colors duration-200" :class="formData.gender === '女' ? 'border-[#0068FF]' : 'border-[#CFCFCF]'">
+ <view v-if="formData.gender === '女'" class="w-[24rpx] h-[24rpx] rounded-full bg-[#0068FF]" />
</view>
<text class="ml-[12rpx] text-[28rpx] text-[#333333]">女</text>
</view>
</view>
</view>
-
</view>
- <!-- 保存按钮 -->
<view class="fixed bottom-[94rpx] left-0 w-full px-[32rpx] box-border z-[10]">
- <view
- class="h-[96rpx] rounded-full bg-[#0068FF] flex items-center justify-center shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)]"
- @click="handleSave"
- >
- <text class="text-[32rpx] text-white font-medium">保存</text>
+ <view class="h-[96rpx] rounded-full bg-[#0068FF] flex items-center justify-center shadow-[0_8rpx_24rpx_rgba(0,104,255,0.2)]" @click="handleSave">
+ <text class="text-[32rpx] text-white font-medium">{{ saving ? '保存中...' : '保存' }}</text>
</view>
</view>
-
</view>
</template>
--
Gitblit v1.7.1