liyongli 1 miesiąc temu
rodzic
commit
db2515073d

+ 19 - 0
src/api/2025.js

@@ -60,4 +60,23 @@ export function getUserInfo(data) {
         errorToast: '当前访问人数过多,请重试。',
         data
     }); 
+}
+
+/**
+ * 上传照片
+ */
+export function uploadImage(data) {
+    const oriData = new FormData();
+    oriData.append('file', data.file);
+    return ajax({
+        url: 'dev/file/uploadDynamicReturnUrl',
+        method: 'POST',
+        header: {
+            'page-language': localStorage.getItem('langSelect') || 'zh'
+        },
+        isFormData: true,
+        urlType: 'Url2025',
+        errorToast: '当前访问人数过多,请重试。',
+        data: oriData
+    }); 
 }

+ 7 - 4
src/utils/request.js

@@ -38,12 +38,15 @@ export default function (ori) {
     xhttp.open(method, url, true);
     const headers = Object.keys(ori.header);
     for (let i = 0; i < headers.length; i++) {
-      const key = headers[i];
-      if (key === 'contentType') continue;
-      console.log(key, ori.header[key]); // 输出每个键名和对应的值
+      let key = headers[i];
+      if (key === 'contentType') {
+        key = 'Content-Type';
+        ori.header[key] = ori.header['contentType']
+      }
       xhttp.setRequestHeader(key, ori.header[key]);
     }
-    if (ori.header.contentType === 'multipart/form-data') {
+    console.log(ori);
+    if (ori.isFormData) {
       xhttp.send(ori.data);
     } else {
       xhttp.setRequestHeader("Content-type", 'application/json');

+ 4 - 51
src/view/fenghui/form.vue

@@ -297,10 +297,8 @@
 </template>
 <script setup>
 import { defineEmits, defineProps, reactive, ref, computed, defineExpose } from 'vue';
-import { showLoadingToast, showToast, showDialog } from 'vant';
+import { showToast, showDialog } from 'vant';
 import { saveData, uploadImage } from '@/api/2025.js';
-import config from '@/config/index.js';
-import axios from 'axios';
 import lang from './lang.js';
 const props = defineProps(['langType','verificationCode', 'subData']);
 
@@ -362,61 +360,16 @@ const beforeDelete = (index) => {
 const filesize = () => showToast(lang[props.langType].fileSize);
 const beforeRead = (file) => {
     if (!file) showToast(lang[props.langType].photoContent);
-    const oriData = new FormData();
-    oriData.append('file', file.file);
-
-    uploadImage({
-        file: file.file, // 图片文件对象,必填
-    }).then(res => {
-        loading.close();
-        if (res.status !== 200 || res.data.code !== 0) {
-            showToast(res.msg || lang[props.langType].upFileError);
-            formData.photoUrl = ''
-            return
-        }
-        showToast(lang[props.langType].upFileSusscess);
-        formData.photoUrl = res.data.data
-        photo.value = [
-            {
-                url: res.data.data,
-                file: file.file,
-            }
-        ]
-    }).catch((res) => {
-        loading.close();
-        showToast(res.msg || lang[props.langType].upFileError);
-    })
-
-    const loading = showLoadingToast({
-        message: lang[props.langType].upFile,
-        forbidClick: true,
-        loadingType: 'spinner',
-        duration: 0
-    })
-    axios({
-        method: 'post',
-        url: config.base.Url2025 + 'dev/file/uploadDynamicReturnUrl',
-        headers: {
-            'Content-Type': 'multipart/form-data',
-        },
-        data: oriData,
-    }).then(res => {
-        loading.close();
-        if (res.status !== 200 || res.data.code !== 0) {
-            showToast(res.msg || lang[props.langType].upFileError);
-            formData.photoUrl = ''
-            return
-        }
+    uploadImage(file).then(res => {
         showToast(lang[props.langType].upFileSusscess);
-        formData.photoUrl = res.data.data
+        formData.photoUrl = res
         photo.value = [
             {
-                url: res.data.data,
+                url: res,
                 file: file.file,
             }
         ]
     }).catch((res) => {
-        loading.close();
         showToast(res.msg || lang[props.langType].upFileError);
     })
     return false;

+ 5 - 29
src/view/fenghui/form1.vue

@@ -279,10 +279,8 @@
 </template>
 <script setup>
 import { defineEmits, defineProps, reactive, ref, computed, defineExpose } from 'vue';
-import { showLoadingToast, showToast, showDialog } from 'vant';
-import { saveData } from '@/api/2025.js';
-import config from '@/config/index.js';
-import axios from 'axios';
+import { showToast, showDialog } from 'vant';
+import { saveData, uploadImage } from '@/api/2025.js';
 import lang from './lang.js';
 const props = defineProps(['langType','verificationCode', 'subData']);
 
@@ -345,38 +343,16 @@ const beforeDelete = (index) => {
 const filesize = () => showToast(lang[props.langType].fileSize);
 const beforeRead = (file) => {
     if (!file) showToast(lang[props.langType].photoContent);
-    const oriData = new FormData();
-    oriData.append('file', file.file);
-    const loading = showLoadingToast({
-        message: lang[props.langType].upFile,
-        forbidClick: true,
-        loadingType: 'spinner',
-        duration: 0
-    })
-    axios({
-        method: 'post',
-        url: config.base.Url2025 + 'dev/file/uploadDynamicReturnUrl',
-        headers: {
-            'Content-Type': 'multipart/form-data',
-        },
-        data: oriData,
-    }).then(res => {
-        loading.close();
-        if (res.status !== 200 || res.data.code !== 0) {
-            showToast(res.msg || lang[props.langType].upFileError);
-            formData.photoUrl = ''
-            return
-        }
+    uploadImage(file).then(res => {
         showToast(lang[props.langType].upFileSusscess);
-        formData.photoUrl = res.data.data
+        formData.photoUrl = res
         photo.value = [
             {
-                url: res.data.data,
+                url: res,
                 file: file.file,
             }
         ]
     }).catch((res) => {
-        loading.close();
         showToast(res.msg || lang[props.langType].upFileError);
     })
     return false;

+ 464 - 0
src/view/fenghui/form1Recurrence.vue

@@ -0,0 +1,464 @@
+<!-- 中国嘉宾 -->
+<template>
+    <van-nav-bar :title="lang[langType].signUpZH" left-arrow @click-left="onClickLeft" />
+    <div class="form">
+        <div>
+            <span style="font-size: 16px;margin-left: 10px;" v-text="lang[langType].signUpZH"></span>
+        </div>
+        <van-form ref="form" @submit="onSubmit" label-width="8em">
+            <!-- 姓名 -->
+            <van-field input-align="right"
+                :rules="[{ required: formData, trigger: 'onBlur', message: lang[langType].nameContent }]" required
+                v-model="formData.name" :placeholder="lang[langType].nameContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].name"></span>
+                </template>
+            </van-field>
+
+            <!-- 省市 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].provinceCityContent }]"
+                v-model="formData.provinceCity" required :placeholder="lang[langType].provinceCityContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].provinceCity"></span>
+                </template>
+            </van-field>
+
+            <!-- 性别 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].genderContent }]"
+                v-model="formData.gender" required :placeholder="lang[langType].genderContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].gender"></span>
+                </template>
+            </van-field>
+
+            <!-- 生日 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].birthdayContent }]" clickable
+                right-icon="calendar-o" readonly @click="showBirthday = true" v-model="formData.birthday" required
+                :placeholder="lang[langType].birthdayContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].birthday"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showBirthday" destroy-on-close round position="bottom">
+                <van-date-picker :confirm-button-text="lang[langType].confirm"
+                    :cancel-button-text="lang[langType].cancel" @confirm="packerConfirm"
+                    :title="lang[langType].birthday" @cancel="showBirthday = false" :min-date="new Date(1970, 0, 1)"
+                    :max-date="new Date()" />
+            </van-popup>
+
+            <!-- 照片 -->
+            <van-field input-align="right" :placeholder="lang[langType].photoContent" required
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].photoContent }]"
+                v-model="formData.photoUrl">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].photo"></span>
+                </template>
+                <template #input>
+                    <van-uploader :preview-full-image="false" accept="image/*" v-model="photo"
+                        :before-delete="beforeDelete" :max-count="1" :max-size="524288000" @oversize="filesize"
+                        :after-read="beforeRead" result-type="file">
+                    </van-uploader>
+                    <p style="font-size: 12px;" v-text="lang[langType].photoSubContent"> </p>
+                </template>
+            </van-field>
+
+            <!-- 手机号 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].contactContent }]"
+                v-model="formData.contact" type="number" required :placeholder="lang[langType].contactContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].contact"></span>
+                </template>
+            </van-field>
+
+            <!-- 邮箱 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].emailContent }]" required
+                v-model="formData.email" :placeholder="lang[langType].emailContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].email"></span>
+                </template>
+            </van-field>
+
+            <!-- 紧急联系人 -->
+            <van-field input-align="right" v-model="formData.emergencyContact"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].emergencyContactContent }]"
+                required :placeholder="lang[langType].emergencyContactContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].emergencyContact"></span>
+                </template>
+            </van-field>
+
+            <!-- 紧急联系人电话 -->
+            <van-field input-align="right" v-model="formData.emergencyContactPhone" required
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].emergencyContactPhoneContent }]"
+                :placeholder="lang[langType].emergencyContactPhoneContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''"
+                        v-html="lang[langType].emergencyContactPhone"></span>
+                </template>
+            </van-field>
+
+            <!-- 证件类型 -->
+            <van-field input-align="right" v-model="formData.cardTypeName" clickable
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].conferenceTypeContent }]"
+                right-icon="calendar-o" readonly :placeholder="lang[langType].conferenceTypeContent"
+                @click="showCardType = true" required>
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].conferenceType"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showCardType" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :model-value="formData.cardType" :columns="cardTypeList" @cancel="showCardType = false"
+                    @confirm="onConfirm" />
+            </van-popup>
+
+            <!-- 证件 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].passportContent }]"
+                v-model="formData.passport" required :placeholder="lang[langType].passportContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].passport"></span>
+                </template>
+            </van-field>
+
+            <!-- 饮食要求 -->
+            <van-field input-align="right" v-model="formData.catering" rows="3" autosize
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].cateringContent }]"
+                type="textarea" required :placeholder="lang[langType].cateringContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].catering"></span>
+                </template>
+            </van-field>
+
+            <div style="background-color: #F7F7F7;height: 10px;"></div>
+
+            <!-- 工作单位 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].workUnit }]" required
+                v-model="formData.workUnit" :placeholder="lang[langType].companyContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].company"></span>
+                </template>
+            </van-field>
+
+            <!-- 单位类型 -->
+            <van-field input-align="right" v-model="formData.unitTypeName" clickable
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].unitTypeContent }]"
+                right-icon="calendar-o" readonly :placeholder="lang[langType].unitTypeContent"
+                @click="showUnitType = true" required>
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].unitType"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showUnitType" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :model-value="formData.unitType" :columns="unitTypeList" @cancel="showUnitType = false"
+                    @confirm="onUnitTypeConfirm" />
+            </van-popup>
+
+            <!-- 职务 -->
+            <van-field input-align="right" v-model="formData.career"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].careerContent }]" required
+                :placeholder="lang[langType].careerContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].career"></span>
+                </template>
+            </van-field>
+
+            <!-- 传真 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].faxContent }]"
+                v-model="formData.fax" required :placeholder="lang[langType].faxContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].fax"></span>
+                </template>
+            </van-field>
+
+            <!-- 办公地址 -->
+            <van-field input-align="right" v-model="formData.officeAddress" rows="3" autosize required
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].officeAddressContent }]"
+                type="textarea" :placeholder="lang[langType].officeAddressContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].officeAddress"></span>
+                </template>
+            </van-field>
+
+            <div style="background-color: #F7F7F7;height: 10px;"></div>
+            <!-- 接受采访意向  -->
+            <van-field input-align="right" v-model="formData.interview" required
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].interviewContent }]"
+                :placeholder="lang[langType].interviewContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].interview"></span>
+                </template>
+                <template #input>
+                    <van-radio-group style="justify-content: flex-end;width: 100%;" v-model="formData.interview"
+                        direction="horizontal">
+                        <van-radio :name="lang[langType].confirm">{{ lang[langType].confirm }}</van-radio>
+                        <van-radio :name="lang[langType].cancel">{{ lang[langType].cancel }}</van-radio>
+                    </van-radio-group>
+                </template>
+            </van-field>
+
+            <div style="background-color: #F7F7F7;height: 10px;"></div>
+
+            <!--  抵达西安交通车次 -->
+            <van-field input-align="right" v-model="formData.reachTrainNumber"
+                :placeholder="lang[langType].reachTrainNumberContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].reachTrainNumber"></span>
+                </template>
+            </van-field>
+
+            <!--  抵达西安时间 -->
+            <van-field input-align="right" v-model="formData.reachTime" clickable right-icon="calendar-o" readonly
+                @click="showReachTime = true" :placeholder="lang[langType].reachTimeContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].reachTime"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showReachTime" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :title="lang[langType].reachTime" :columns="dateTimeColumns" @confirm="reachTimeConfirm"
+                    @cancel="showReachTime = false" />
+            </van-popup>
+
+            <!--  离开车次 -->
+            <van-field input-align="right" v-model="formData.leavingTrainNumber"
+                :placeholder="lang[langType].leavingTrainNumberContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].leavingTrainNumber"></span>
+                </template>
+            </van-field>
+
+            <!--  离开时间 -->
+            <van-field input-align="right" v-model="formData.leavingTime" clickable right-icon="calendar-o" readonly
+                @click="showLeavingTime = true" :placeholder="lang[langType].leavingTimeContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].leavingTime"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showLeavingTime" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :title="lang[langType].leavingTime" :columns="dateTimeColumns" @confirm="leavingTimeConfirm"
+                    @cancel="showLeavingTime = false" />
+            </van-popup>
+
+            <!--  离店时间 -->
+            <van-field input-align="right" v-model="formData.checkOutTime" clickable right-icon="calendar-o" readonly
+                @click="showCheckOutTime = true" :placeholder="lang[langType].checkOutTimeContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].checkOutTime"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showCheckOutTime" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :title="lang[langType].checkOutTime" :columns="dateTimeColumns" @confirm="checkOutTimeConfirm"
+                    @cancel="showCheckOutTime = false" />
+            </van-popup>
+
+            <van-field input-align="right" v-model="formData.message" rows="3" autosize type="textarea"
+                :placeholder="lang[langType].ortherNotesContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].ortherNotes"></span>
+                </template>
+            </van-field>
+            <br />
+            <van-button
+                style="background: linear-gradient(270deg, #1D19BC 0%, #1C4CD5 100%);margin: 0 auto;width: calc(100% - 40px);"
+                block type="primary" native-type="submit">
+                {{ lang[langType].submit }}
+            </van-button>
+        </van-form>
+    </div>
+</template>
+<script setup>
+import { defineEmits, defineProps, reactive, ref, computed, defineExpose } from 'vue';
+import { showToast, showDialog } from 'vant';
+import { saveData, uploadImage } from '@/api/2025.js';
+import lang from './lang.js';
+const props = defineProps(['langType','verificationCode', 'subData']);
+
+const emits = defineEmits(['closeComponent']);
+
+const formData = reactive({})
+const cardTypeList = computed(() => lang[props.langType].cardTypeList)
+const unitTypeList = computed(() => lang[props.langType].foreignCountryUnitType)
+const showBirthday = ref(false)
+const showReachTime = ref(false)
+const showUnitType = ref(false)
+const showLeavingTime = ref(false)
+const showCardType = ref(false)
+const showCheckOutTime = ref(false)
+const form = ref(null);
+const photo = ref([]);
+
+const reset = () => {
+    form.value.resetFields();
+}
+
+const onClickLeft = () => {
+    emits('closeComponent');
+};
+
+const createTextDate = values => values.slice(0, 3).join('-') + ' ' + values.slice(3).join(':');
+const packerConfirm = (values) => {
+    showBirthday.value = false;
+    formData.birthday = createTextDate(values.selectedValues);
+}
+const leavingTimeConfirm = ({ selectedValues }) => {
+    showLeavingTime.value = false;
+    formData.leavingTime = createTextDate(selectedValues);
+}
+const onUnitTypeConfirm = (values) => {
+    showUnitType.value = false;
+    formData.unitType = values.selectedValues;
+    formData.unitTypeName = values.selectedOptions[0].text;
+}
+const reachTimeConfirm = ({ selectedValues }) => {
+    showReachTime.value = false;
+    formData.reachTime = createTextDate(selectedValues);
+}
+const checkOutTimeConfirm = ({ selectedValues }) => {
+    showCheckOutTime.value = false;
+    formData.checkOutTime = createTextDate(selectedValues);
+}
+const onConfirm = (values) => {
+    showCardType.value = false;
+    formData.cardType = values.selectedValues;
+    formData.cardTypeName = values.selectedOptions[0].text;
+}
+
+// 上传
+const beforeDelete = (index) => {
+    photo.value.splice(index, 1);
+    formData.photoUrl = '';
+    return false;
+}
+const filesize = () => showToast(lang[props.langType].fileSize);
+const beforeRead = (file) => {
+    if (!file) showToast(lang[props.langType].photoContent);
+    uploadImage(file).then(res => {
+        showToast(lang[props.langType].upFileSusscess);
+        formData.photoUrl = res
+        photo.value = [
+            {
+                url: res,
+                file: file.file,
+            }
+        ]
+    }).catch((res) => {
+        showToast(res.msg || lang[props.langType].upFileError);
+    })
+    return false;
+};
+
+
+let arrivalTime = []
+const dateTimeColumns = computed(() => {
+    const now = new Date();
+    const dateList = lang[props.langType].dateList;
+    const years = [{ text: dateList[0], value: '', disabled: true }, ...Array.from({ length: Number(now.getFullYear()) + 1 - 1970 }, (_, i) => {
+        const t = 1970 + i + ''
+        return {
+            text: t,
+            value: t
+        }
+    }).reverse()];
+    const months = [{ text: dateList[1], value: '', disabled: true }, ...Array.from({ length: 12 }, (_, i) => {
+        const m = (i + 1).toString().padStart(2, '0')
+        return {
+            text: m,
+            value: m
+        }
+    })];
+
+    // 动态计算当前选中年月对应的天数
+    const daysInMonth = new Date(
+        parseInt(arrivalTime[0] || '0'),
+        parseInt(arrivalTime[1] || '0'),
+        0
+    ).getDate();
+    const days = [{ text: dateList[2], value: '', disabled: true }, ...Array.from({ length: daysInMonth }, (_, i) => {
+        const d = (i + 1).toString().padStart(2, '0')
+        return {
+            text: d,
+            value: d
+        }
+    })];
+
+    const hours = [{ text: dateList[3], value: '', disabled: true }, ...Array.from({ length: 24 }, (_, i) => {
+        const h = i.toString().padStart(2, '0')
+        return {
+            text: h,
+            value: h
+        }
+    })];
+    const minutes = [{ text: dateList[4], value: '', disabled: true }, ...Array.from({ length: 60 }, (_, i) => {
+        const m = i.toString().padStart(2, '0')
+        return {
+            text: m,
+            value: m
+        }
+    })];
+    return [years, months, days, hours, minutes];
+});
+
+
+let loading = false
+const onSubmit = () => {
+    if (loading) return
+    loading = true
+    saveData({
+        code: props.verificationCode, // 邀请码
+        name: formData.name, // 姓名
+        provinceCity: formData.provinceCity, // 省市区
+        gender: formData.gender, // 性别
+        birthDate: formData.birthday, // 出生日期
+        photo: formData.photoUrl, // 照片
+        phone: formData.contact, // 手机号
+        email: formData.email, // 邮箱
+        emergencyContact: formData.emergencyContact, // 紧急联系人
+        emergencyContactPhone: formData.emergencyContactPhone, // 紧急联系人电话
+        cardTypeName: formData.cardTypeName, // 证件类型
+        idCard: formData.passport, // 证件号
+        dietaryRequirements: formData.catering, // 饮食要求
+        workUnit: formData.workUnit, // 工作单位
+        unitType: formData.unitTypeName, // 单位类型
+        position: formData.career, // 职务
+        fax: formData.fax, // fax
+        interviewIntention: formData.interview, // 接受采访意向
+        officeAddress: formData.officeAddress, // 办公地址
+        arrivalNumberXian: formData.reachTrainNumber, // 抵达西安交通车次
+        arrivalTimeXian: formData.reachTime, // 抵达西安时间
+        departureNumber: formData.leavingTrainNumber, // 离开车次
+        departureTime: formData.leavingTime, // 离开时间
+        checkOutTime: formData.checkOutTime, // 离店时间
+        remarks: formData.message, // 备注
+        userType: '国内嘉宾'
+    }).then((res) => {
+        if (!isNaN(res.code) && res.code !== 0) {
+            showToast(res.msg || lang[props.langType].networkError);
+            return;
+        }
+        // 清空表单内容 from
+        // Object.keys(from).forEach((key) => {
+        //     from[key] = '';
+        // });
+        loading = false
+        showDialog({
+            message: lang[props.langType].registration,
+        })
+    }).catch((res) => {
+        loading = false
+        showToast(res.msg || lang[props.langType].networkError);
+    });
+}
+
+defineExpose({ reset })
+</script>

+ 7 - 39
src/view/fenghui/form2.vue

@@ -83,10 +83,8 @@
 </template>
 <script setup>
 import { defineEmits, defineProps, reactive, ref, computed, defineExpose } from 'vue';
-import { showLoadingToast, showToast, showDialog } from 'vant';
-import { saveData } from '@/api/2025.js';
-import config from '@/config/index.js';
-import axios from 'axios';
+import { showToast, showDialog } from 'vant';
+import { saveData, uploadImage } from '@/api/2025.js';
 import lang from './lang.js';
 const props = defineProps(['langType','verificationCode', 'subData']);
 
@@ -121,47 +119,17 @@ const beforeDelete = (index) => {
 const filesize = () => showToast(lang[props.langType].fileSize);
 const beforeRead = (file) => {
     if (!file) showToast(lang[props.langType].photoContent);
-    const oriData = new FormData();
-    oriData.append('file', file.file);
-
-    const loading = showLoadingToast({
-        message: lang[props.langType].upFile,
-        forbidClick: true,
-        loadingType: 'spinner',
-        duration: 0
-    })
-    axios({
-        method: 'post',
-        url: config.base.Url2025 + 'dev/file/uploadDynamicReturnUrl',
-        headers: {
-            'Content-Type': 'multipart/form-data',
-        },
-        data: oriData,
-    }).then(res => {
-        loading.close();
-        if (res.status !== 200 || res.data.code !== 0) {
-            const messages = res.data
-            let message = lang[props.langType].upFileError;
-            if (messages.cnMessage && props.langType === 'zh') {
-                message = messages.cnMessage;
-            } else if (messages.enMessage && props.langType === 'en') {
-                message = messages.enMessage;
-            }
-            showToast(message);
-            formData.photoUrl = ''
-            return
-        }
+    uploadImage(file).then(res => {
         showToast(lang[props.langType].upFileSusscess);
-        formData.photoUrl = res.data.data
+        formData.photoUrl = res
         photo.value = [
             {
-                url: res.data.data,
+                url: res,
                 file: file.file,
             }
         ]
-    }).catch(() => {
-        loading.close();
-        showToast(lang[props.langType].upFileError);
+    }).catch((res) => {
+        showToast(res.msg || lang[props.langType].upFileError);
     })
     return false;
 };

+ 170 - 0
src/view/fenghui/form2Recurrence.vue

@@ -0,0 +1,170 @@
+<!-- 工作人员 -->
+<template>
+    <van-nav-bar :title="lang[langType].signUpWK" left-arrow @click-left="onClickLeft" />
+    <div class="form">
+        <div>
+            <span style="font-size: 16px;margin-left: 10px;" v-text="lang[langType].signUpWK"></span>
+        </div>
+        <van-form ref="form" @submit="onSubmit" label-width="8em">
+            <!-- 姓名 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].nameContent }]" required
+                v-model="formData.name" :placeholder="lang[langType].nameContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].name"></span>
+                </template>
+            </van-field>
+
+            <!-- 手机号 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].contactContent }]"
+                v-model="formData.contact" type="number" required :placeholder="lang[langType].contactContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].contact"></span>
+                </template>
+            </van-field>
+
+            <!-- 照片 -->
+            <van-field input-align="right" :placeholder="lang[langType].photoContent" required
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].photoContent }]"
+                v-model="formData.photoUrl">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].photo"></span>
+                </template>
+                <template #input>
+                    <van-uploader :preview-full-image="false" accept="image/*" v-model="photo"
+                        :before-delete="beforeDelete" :max-count="1" :max-size="524288000" @oversize="filesize"
+                        :after-read="beforeRead" result-type="file">
+                    </van-uploader>
+                    <p style="font-size: 12px;" v-text="lang[langType].photoSubContent"> </p>
+                </template>
+            </van-field>
+
+            <!-- 证件 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].IDcardNumberContent }]"
+                v-model="formData.passport" required :placeholder="lang[langType].IDcardNumberContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].IDcardNumber"></span>
+                </template>
+            </van-field>
+
+            <!-- 单位类型 -->
+            <van-field input-align="right" v-model="formData.unitTypeName" clickable
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].workGroupTextContent }]"
+                right-icon="calendar-o" readonly :placeholder="lang[langType].workGroupTextContent"
+                @click="showUnitType = true" required>
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].workGroupText"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showUnitType" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :model-value="formData.unitType" :columns="unitTypeList" @cancel="showUnitType = false"
+                    @confirm="onUnitTypeConfirm" />
+            </van-popup>
+
+            <!-- 工作单位 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].companyB }]" required
+                v-model="formData.workUnit" :placeholder="lang[langType].companyBContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].companyB"></span>
+                </template>
+            </van-field>
+            <br />
+            <van-button
+                style="background: linear-gradient(270deg, #1D19BC 0%, #1C4CD5 100%);margin: 0 auto;width: calc(100% - 40px);"
+                block type="primary" native-type="submit">
+                {{ lang[langType].submit }}
+            </van-button>
+        </van-form>
+    </div>
+</template>
+<script setup>
+import { defineEmits, defineProps, reactive, ref, computed, defineExpose } from 'vue';
+import { showToast, showDialog } from 'vant';
+import { saveData, uploadImage } from '@/api/2025.js';
+import lang from './lang.js';
+const props = defineProps(['langType','verificationCode', 'subData']);
+
+const emits = defineEmits(['closeComponent']);
+
+const formData = reactive({})
+const unitTypeList = computed(() => lang[props.langType].workGroup)
+const showUnitType = ref(false)
+const form = ref(null); // 引入子组件的实例,用于调用子组件的方法
+const photo = ref([]);
+
+const reset = () => {
+    form.value.resetFields();
+}
+
+const onClickLeft = () => {
+    emits('closeComponent');
+};
+
+const onUnitTypeConfirm = (values) => {
+    showUnitType.value = false;
+    formData.unitType = values.selectedOptions[0].text;
+    formData.unitTypeName = values.selectedOptions[0].text;
+}
+
+// 上传
+const beforeDelete = (index) => {
+    photo.value.splice(index, 1);
+    formData.photoUrl = '';
+    return false;
+}
+const filesize = () => showToast(lang[props.langType].fileSize);
+const beforeRead = (file) => {
+    if (!file) showToast(lang[props.langType].photoContent);
+    uploadImage(file).then(res => {
+        showToast(lang[props.langType].upFileSusscess);
+        formData.photoUrl = res
+        photo.value = [
+            {
+                url: res,
+                file: file.file,
+            }
+        ]
+    }).catch((res) => {
+        showToast(res.msg || lang[props.langType].upFileError);
+    })
+    return false;
+};
+
+let loading = false
+const onSubmit = () => {
+    if (loading) return
+    loading = true
+    saveData({
+        name: formData.name, // 姓名
+        code: props.verificationCode, // 邀请码
+        photo: formData.photoUrl, // 照片
+        phone: formData.contact, // 手机号
+        idCard: formData.passport, // 证件号
+        workUnit: formData.workUnit, // 工作单位
+        workGroup: formData.unitType, // 单位类型
+        userType: '工作人员'
+    }).then((res) => {
+        if (!isNaN(res.code) && res.code !== 0) {
+            showToast(res.message || lang[props.langType].networkError);
+            return;
+        }
+        // 清空表单内容 from
+        // Object.keys(from).forEach((key) => {
+        //     from[key] = '';
+        // });
+        loading = false
+        showDialog({
+            message: lang[props.langType].registration,
+        })
+    }).catch((res) => {
+        loading = false
+        showToast(res.message || lang[props.langType].networkError);
+    });
+}
+defineExpose({ reset })
+</script>
+<style lang="scss"></style>

+ 482 - 0
src/view/fenghui/formRecurrence.vue

@@ -0,0 +1,482 @@
+<!-- 外国嘉宾 -->
+<template>
+    <van-nav-bar :title="lang[langType].signUp" left-arrow @click-left="onClickLeft" />
+    <div class="form">
+        <div>
+            <span style="font-size: 16px;margin-left: 10px;" v-text="lang[langType].signUp"></span>
+        </div>
+        <van-form ref="form" @submit="onSubmit" label-width="8em">
+            <!-- 国家/地区 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].countryRegionContent }]" required
+                v-model="formData.countryRegion" :placeholder="lang[langType].countryRegionContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].countryRegion"></span>
+                </template>
+            </van-field>
+
+            <!-- 姓 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].lastNameContent }]" required
+                v-model="formData.lastName" :placeholder="lang[langType].lastNameContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].lastName"></span>
+                </template>
+            </van-field>
+
+            <!-- 名 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].firstNameContent }]" required
+                v-model="formData.firstName" :placeholder="lang[langType].firstNameContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].firstName"></span>
+                </template>
+            </van-field>
+
+            <!-- 性别 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].genderContent }]"
+                v-model="formData.gender" required :placeholder="lang[langType].genderContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].gender"></span>
+                </template>
+            </van-field>
+
+            <!-- 生日 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].birthdayContent }]" clickable
+                right-icon="calendar-o" readonly @click="showBirthday = true" v-model="formData.birthday" required
+                :placeholder="lang[langType].birthdayContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].birthday"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showBirthday" destroy-on-close round position="bottom">
+                <van-date-picker :confirm-button-text="lang[langType].confirm"
+                    :cancel-button-text="lang[langType].cancel" @confirm="packerConfirm"
+                    :title="lang[langType].birthday" @cancel="showBirthday = false" :min-date="new Date(1970, 0, 1)"
+                    :max-date="new Date()" />
+            </van-popup>
+
+            <!-- 照片 -->
+            <van-field input-align="right" :placeholder="lang[langType].photoContent" required
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].photoContent }]"
+                v-model="formData.photoUrl">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].photo"></span>
+                </template>
+                <template #input>
+                    <van-uploader :preview-full-image="false" accept="image/*" v-model="photo"
+                        :before-delete="beforeDelete" :max-count="1" :max-size="524288000" @oversize="filesize"
+                        :after-read="beforeRead" result-type="file">
+                    </van-uploader>
+                    <p style="font-size: 12px;" v-text="lang[langType].photoSubContent"> </p>
+                </template>
+            </van-field>
+
+            <!-- 手机号 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].contactContent }]"
+                v-model="formData.contact" type="number" required :placeholder="lang[langType].contactContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].contact"></span>
+                </template>
+            </van-field>
+
+            <!-- 邮箱 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].emailContent }]" required
+                v-model="formData.email" :placeholder="lang[langType].emailContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].email"></span>
+                </template>
+            </van-field>
+
+            <!-- 紧急联系人 -->
+            <van-field input-align="right" v-model="formData.emergencyContact"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].emergencyContactContent }]"
+                required :placeholder="lang[langType].emergencyContactContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].emergencyContact"></span>
+                </template>
+            </van-field>
+
+            <!-- 紧急联系人电话 -->
+            <van-field input-align="right" v-model="formData.emergencyContactPhone" required
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].emergencyContactPhoneContent }]"
+                :placeholder="lang[langType].emergencyContactPhoneContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''"
+                        v-html="lang[langType].emergencyContactPhone"></span>
+                </template>
+            </van-field>
+
+            <!-- 证件类型 -->
+            <van-field input-align="right" v-model="formData.cardTypeName" clickable
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].conferenceTypeContent }]"
+                right-icon="calendar-o" readonly :placeholder="lang[langType].conferenceTypeContent"
+                @click="showCardType = true" required>
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].conferenceType"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showCardType" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :model-value="formData.cardType" :columns="cardTypeList" @cancel="showCardType = false"
+                    @confirm="onConfirm" />
+            </van-popup>
+
+            <!-- 证件 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].passportContent }]"
+                v-model="formData.passport" required :placeholder="lang[langType].passportContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].passport"></span>
+                </template>
+            </van-field>
+
+            <!-- 宗教 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].beliefContent }]"
+                v-model="formData.belief" required :placeholder="lang[langType].beliefContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].belief"></span>
+                </template>
+            </van-field>
+
+            <!-- 饮食要求 -->
+            <van-field input-align="right" v-model="formData.catering" rows="3" autosize
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].cateringContent }]"
+                type="textarea" required :placeholder="lang[langType].cateringContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].catering"></span>
+                </template>
+            </van-field>
+
+            <div style="background-color: #F7F7F7;height: 10px;"></div>
+
+            <!-- 工作单位 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].workUnit }]" required
+                v-model="formData.workUnit" :placeholder="lang[langType].companyContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].company"></span>
+                </template>
+            </van-field>
+
+            <!-- 单位类型 -->
+            <van-field input-align="right" v-model="formData.unitTypeName" clickable
+                :rules="[{ required: true, trigger: 'onChange', message: lang[langType].unitTypeContent }]"
+                right-icon="calendar-o" readonly :placeholder="lang[langType].unitTypeContent"
+                @click="showUnitType = true" required>
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].unitType"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showUnitType" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :model-value="formData.unitType" :columns="unitTypeList" @cancel="showUnitType = false"
+                    @confirm="onUnitTypeConfirm" />
+            </van-popup>
+
+            <!-- 职务 -->
+            <van-field input-align="right" v-model="formData.career"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].careerContent }]" required
+                :placeholder="lang[langType].careerContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].career"></span>
+                </template>
+            </van-field>
+
+            <!-- 传真 -->
+            <van-field input-align="right"
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].faxContent }]"
+                v-model="formData.fax" required :placeholder="lang[langType].faxContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].fax"></span>
+                </template>
+            </van-field>
+
+            <!-- 办公地址 -->
+            <van-field input-align="right" v-model="formData.officeAddress" rows="3" autosize required
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].officeAddressContent }]"
+                type="textarea" :placeholder="lang[langType].officeAddressContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].officeAddress"></span>
+                </template>
+            </van-field>
+
+            <div style="background-color: #F7F7F7;height: 10px;"></div>
+            <!-- 接受采访意向  -->
+            <van-field input-align="right" v-model="formData.interview" required
+                :rules="[{ required: true, trigger: 'onBlur', message: lang[langType].interviewContent }]"
+                :placeholder="lang[langType].interviewContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].interview"></span>
+                </template>
+                <template #input>
+                    <van-radio-group style="justify-content: flex-end;width: 100%;" v-model="formData.interview"
+                        direction="horizontal">
+                        <van-radio :name="lang[langType].confirm">{{ lang[langType].confirm }}</van-radio>
+                        <van-radio :name="lang[langType].cancel">{{ lang[langType].cancel }}</van-radio>
+                    </van-radio-group>
+                </template>
+            </van-field>
+
+            <div style="background-color: #F7F7F7;height: 10px;"></div>
+
+            <!--  抵达西安交通车次 -->
+            <van-field input-align="right" v-model="formData.reachTrainNumber"
+                :placeholder="lang[langType].reachTrainNumberContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].reachTrainNumber"></span>
+                </template>
+            </van-field>
+
+            <!--  抵达西安时间 -->
+            <van-field input-align="right" v-model="formData.reachTime" clickable right-icon="calendar-o" readonly
+                @click="showReachTime = true" :placeholder="lang[langType].reachTimeContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].reachTime"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showReachTime" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :title="lang[langType].reachTime" :columns="dateTimeColumns" @confirm="reachTimeConfirm"
+                    @cancel="showReachTime = false" />
+            </van-popup>
+
+            <!--  离开车次 -->
+            <van-field input-align="right" v-model="formData.leavingTrainNumber"
+                :placeholder="lang[langType].leavingTrainNumberContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].leavingTrainNumber"></span>
+                </template>
+            </van-field>
+
+            <!--  离开时间 -->
+            <van-field input-align="right" v-model="formData.leavingTime" clickable right-icon="calendar-o" readonly
+                @click="showLeavingTime = true" :placeholder="lang[langType].leavingTimeContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].leavingTime"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showLeavingTime" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :title="lang[langType].leavingTime" :columns="dateTimeColumns" @confirm="leavingTimeConfirm"
+                    @cancel="showLeavingTime = false" />
+            </van-popup>
+
+            <!--  离店时间 -->
+            <van-field input-align="right" v-model="formData.checkOutTime" clickable right-icon="calendar-o" readonly
+                @click="showCheckOutTime = true" :placeholder="lang[langType].checkOutTimeContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].checkOutTime"></span>
+                </template>
+            </van-field>
+            <van-popup v-model:show="showCheckOutTime" destroy-on-close round position="bottom">
+                <van-picker :confirm-button-text="lang[langType].confirm" :cancel-button-text="lang[langType].cancel"
+                    :title="lang[langType].checkOutTime" :columns="dateTimeColumns" @confirm="checkOutTimeConfirm"
+                    @cancel="showCheckOutTime = false" />
+            </van-popup>
+
+            <van-field input-align="right" v-model="formData.message" rows="3" autosize type="textarea"
+                :placeholder="lang[langType].ortherNotesContent">
+                <template #label>
+                    <span :class="langType === 'zh' ? 'label' : ''" v-html="lang[langType].ortherNotes"></span>
+                </template>
+            </van-field>
+            <br />
+            <van-button
+                style="background: linear-gradient(270deg, #1D19BC 0%, #1C4CD5 100%);margin: 0 auto;width: calc(100% - 40px);"
+                block type="primary" native-type="submit">
+                {{ lang[langType].submit }}
+            </van-button>
+        </van-form>
+    </div>
+</template>
+<script setup>
+import { defineEmits, defineProps, reactive, ref, computed, defineExpose } from 'vue';
+import { showToast, showDialog } from 'vant';
+import { saveData, uploadImage } from '@/api/2025.js';
+import lang from './lang.js';
+const props = defineProps(['langType','verificationCode', 'subData']);
+
+const emits = defineEmits(['closeComponent']);
+
+const formData = reactive({})
+const cardTypeList = computed(() => lang[props.langType].cardTypeList)
+const unitTypeList = computed(() => lang[props.langType].foreignCountryUnitType)
+const form = ref(null);
+const showBirthday = ref(false)
+const showReachTime = ref(false)
+const showLeavingTime = ref(false)
+const showCardType = ref(false)
+const showCheckOutTime = ref(false)
+const showUnitType = ref(false)
+const photo = ref([]);
+
+const onClickLeft = () => {
+    emits('closeComponent');
+};
+
+const reset = () => {
+    form.value.resetValidation();
+}
+const createTextDate = values => values.slice(0, 3).join('-') + ' ' + values.slice(3).join(':');
+const packerConfirm = (values) => {
+    showBirthday.value = false;
+    formData.birthday = createTextDate(values.selectedValues);
+}
+const leavingTimeConfirm = ({ selectedValues }) => {
+    showLeavingTime.value = false;
+    formData.leavingTime = createTextDate(selectedValues);
+}
+const reachTimeConfirm = ({ selectedValues }) => {
+    showReachTime.value = false;
+    formData.reachTime = createTextDate(selectedValues);
+}
+const checkOutTimeConfirm = ({ selectedValues }) => {
+    showCheckOutTime.value = false;
+    formData.checkOutTime = createTextDate(selectedValues);
+}
+const onUnitTypeConfirm = (values) => {
+    showUnitType.value = false;
+    formData.unitType = values.selectedOptions[0].text;
+    formData.unitTypeName = values.selectedOptions[0].text;
+}
+const onConfirm = (values) => {
+    showCardType.value = false;
+    formData.cardType = values.selectedOptions[0].text;
+    formData.cardTypeName = values.selectedOptions[0].text;
+}
+
+// 上传
+const beforeDelete = (index) => {
+    photo.value.splice(index, 1);
+    formData.photoUrl = '';
+    return false;
+}
+const filesize = () => showToast(lang[props.langType].fileSize);
+const beforeRead = (file) => {
+    if (!file) showToast(lang[props.langType].photoContent);
+    uploadImage(file).then(res => {
+        showToast(lang[props.langType].upFileSusscess);
+        formData.photoUrl = res
+        photo.value = [
+            {
+                url: res,
+                file: file.file,
+            }
+        ]
+    }).catch((res) => {
+        showToast(res.msg || lang[props.langType].upFileError);
+    })
+    return false;
+};
+
+
+let arrivalTime = []
+const dateTimeColumns = computed(() => {
+    const dateList = lang[props.langType].dateList;
+    const years = [{ text: dateList[0], value: '', disabled: true }, ...Array.from({ length: 10 }, (_, i) => {
+        const t = 2020 + i + ''
+        return {
+            text: t,
+            value: t
+        }
+    }).reverse()];
+    const months = [{ text: dateList[1], value: '', disabled: true }, ...Array.from({ length: 12 }, (_, i) => {
+        const m = (i + 1).toString().padStart(2, '0')
+        return {
+            text: m,
+            value: m
+        }
+    })];
+
+    // 动态计算当前选中年月对应的天数
+    const daysInMonth = new Date(
+        parseInt(arrivalTime[0] || '0'),
+        parseInt(arrivalTime[1] || '0'),
+        0
+    ).getDate();
+    const days = [{ text: dateList[2], value: '', disabled: true }, ...Array.from({ length: daysInMonth }, (_, i) => {
+        const d = (i + 1).toString().padStart(2, '0')
+        return {
+            text: d,
+            value: d
+        }
+    })];
+
+    const hours = [{ text: dateList[3], value: '', disabled: true }, ...Array.from({ length: 24 }, (_, i) => {
+        const h = i.toString().padStart(2, '0')
+        return {
+            text: h,
+            value: h
+        }
+    })];
+    const minutes = [{ text: dateList[4], value: '', disabled: true }, ...Array.from({ length: 60 }, (_, i) => {
+        const m = i.toString().padStart(2, '0')
+        return {
+            text: m,
+            value: m
+        }
+    })];
+    return [years, months, days, hours, minutes];
+});
+
+
+let loading = false
+const onSubmit = () => {
+    if (loading) return
+    loading = true
+    saveData({
+        code: props.verificationCode, // 邀请码
+        lastName: formData.lastName, // 姓
+        firstName: formData.firstName, // 名
+        gender: formData.gender, // 性别
+        birthDate: formData.birthday, // 出生日期
+        photo: formData.photoUrl, // 照片
+        phone: formData.contact, // 手机号
+        countryRegion: formData.countryRegion, // 国家/地区
+        email: formData.email, // 邮箱
+        religion: formData.belief, // 宗教
+        emergencyContact: formData.emergencyContact, // 紧急联系人
+        emergencyContactPhone: formData.emergencyContactPhone, // 紧急联系人电话
+        cardTypeName: formData.cardTypeName, // 证件类型
+        idCard: formData.passport, // 证件号
+        dietaryRequirements: formData.catering, // 饮食要求
+        workUnit: formData.workUnit, // 工作单位
+        unitType: formData.unitType[0], // 单位类型
+        position: formData.career, // 职务
+        fax: formData.fax, // fax
+        interviewIntention: formData.interview, // 接受采访意向
+        officeAddress: formData.officeAddress, // 办公地址
+        arrivalNumberXian: formData.reachTrainNumber, // 抵达西安交通车次
+        arrivalTimeXian: formData.reachTime, // 抵达西安时间
+        departureNumber: formData.leavingTrainNumber, // 离开车次
+        departureTime: formData.leavingTime, // 离开时间
+        checkOutTime: formData.checkOutTime, // 离店时间
+        remarks: formData.message, // 备注
+        userType: '国外嘉宾'
+    }).then((res) => {
+        if (!isNaN(res.code) && res.code !== 0) {
+            showToast(res.msg || lang[props.langType].networkError);
+            return;
+        }
+        // 清空表单内容 from
+        // Object.keys(from).forEach((key) => {
+        //     from[key] = '';
+        // });
+        loading = false
+        showDialog({
+            message: lang[props.langType].registration,
+        })
+    }).catch((res) => {
+        loading = false
+        showToast(res.msg || lang[props.langType].networkError);
+    });
+}
+defineExpose({ reset })
+</script>
+<style lang="scss"></style>

+ 16 - 5
src/view/fenghui/index.vue

@@ -12,11 +12,18 @@
         </div>
         <Introduction v-if="!componentId" :langType="langSelect" @saveData="saveData" />
         <!-- 动态组件 -->
-        <Fore25 ref="fore25" v-if="componentId === 'fore25'" :subData="subData" :verificationCode="verificationCode" :langType="langSelect"
+        <Fore25 ref="fore25" v-if="componentId === 'fore25'" :verificationCode="verificationCode" :langType="langSelect"
             @closeComponent="closeComponent" />
-        <Chin25 ref="chin25" v-if="componentId === 'chin25'" :subData="subData" :verificationCode="verificationCode" :langType="langSelect"
+        <Chin25 ref="chin25" v-if="componentId === 'chin25'" :verificationCode="verificationCode" :langType="langSelect"
             @closeComponent="closeComponent" />
-        <Work25 ref="work25" v-if="componentId === 'work25'" :subData="subData" :verificationCode="verificationCode" :langType="langSelect"
+        <Work25 ref="work25" v-if="componentId === 'work25'" :verificationCode="verificationCode" :langType="langSelect"
+            @closeComponent="closeComponent" />
+
+        <Fore25-recurrence ref="fore25" v-if="componentId === 'fore25'" :subData="subData" :verificationCode="verificationCode" :langType="langSelect"
+            @closeComponent="closeComponent" />
+        <Chin25-recurrence ref="chin25" v-if="componentId === 'chin25'" :subData="subData" :verificationCode="verificationCode" :langType="langSelect"
+            @closeComponent="closeComponent" />
+        <Work25-recurrence ref="work25" v-if="componentId === 'work25'" :subData="subData" :verificationCode="verificationCode" :langType="langSelect"
             @closeComponent="closeComponent" />
     </div>
 </template>
@@ -27,6 +34,9 @@ import Introduction from './introduction.vue';
 import Fore25 from './form.vue';
 import Chin25 from './form1.vue';
 import Work25 from './form2.vue';
+import Fore25Recurrence from './formRecurrence.vue';
+import Chin25Recurrence from './form1Recurrence.vue';
+import Work25Recurrence from './form2Recurrence.vue';
 import lang from './lang';
 const langSelect = ref(localStorage.getItem('langSelect') || 'zh');
 const componentId = ref('');
@@ -54,8 +64,9 @@ const saveData = (code, verification, data) => {
         const userType = data.userType;
         const keysList = Object.keys(lang[langSelect.value].distinguishedGuest); // 获取对象的所有键名组成的数组
         for (let ii = 0; ii < keysList.length; ii++) {
-            const keysList = keysList[ii];
-            lang[langSelect.value].distinguishedGuest === userType ? componentId.value = keysList : '';
+            const keys = keysList[ii];
+            const value = lang[langSelect.value].distinguishedGuest[keys];  // 获取对象的所有键名组成的数组
+            value === userType ? componentId.value = keys + 'Recurrence' : '';
         }
     }
 }

+ 15 - 12
src/view/fenghui/introduction.vue

@@ -234,20 +234,12 @@ const checkVerificationCode = () => {
         userType
     }).then((res) => {
         if (!isNaN(res.code) && res.code !== 0) {
-            const messages = res.data
-            let message = lang[props.langType].upFileError;
-            if (messages.cnMessage && props.langType === 'zh') {
-                message = messages.cnMessage;
-            } else if (messages.enMessage && props.langType === 'en') {
-                message = messages.enMessage;
-            }
-            showToast(message);
-            showToast(lang[props.langType].verificationCodeError);
+            showToast(res.msg || lang[props.langType].verificationCodeError);
             return;
         }
         emits('saveData', code, verificationCode.value.toLowerCase());
     }).catch((res) => {
-        showToast(res.msg || lang[props.langType].networkError);
+        showToast(res.msg || lang[props.langType].verificationCodeError);
     })
 }
 
@@ -256,8 +248,19 @@ const saveLogin = () => {
         "code": form.verificationCode,
         "idCard": form.password
     }).then((res) => {
-        emits('saveData', undefined, undefined, res);
-
+        if (!isNaN(res.code) && res.code !== 0) {
+            showToast(res.msg || lang[props.langType].networkError);
+            return;
+        }
+        
+        const userType = res.userType;
+        const keysList = Object.keys(lang[props.langType].distinguishedGuest); // 获取对象的所有键名组成的数组
+        for (let ii = 0; ii < keysList.length; ii++) {
+            const keys = keysList[ii];
+            const value = lang[props.langType].distinguishedGuest[keys];  // 获取对象的所有键名组成的数组
+            value === userType ? code = keys + 'Recurrence' : '';
+        }
+        emits('saveData', code, form.verificationCode.toLowerCase(), res);
     }).catch((res) => {
         showToast(res.msg || lang[props.langType].networkError);
     })