Selaa lähdekoodia

fix: 移动端接口联调、pc端逻辑完善

lirenjie 6 kuukautta sitten
vanhempi
commit
dbb5c9571a

+ 18 - 2
src/api/personnel-entry/index.js

@@ -1,8 +1,12 @@
 import { postRequest, getRequest } from '/@/lib/axios';
-/* 新增员工 */
-export function addPersonnel(params){
+/* 移动端登记人员信息 */
+export function addPersonnelInfo(params){
     return postRequest('/supports/employee/onboarding/create',params)
 }
+/* 后台登记人员信息 */
+export function addPersonnel(params){
+    return postRequest('/supports/employee/onboarding/createWeb',params)
+}
 /* 获取入职员工信息 */
 export function getPersonnelInfo(id){
     return getRequest(`/supports/employee/onboarding/query/${id}`)
@@ -15,3 +19,15 @@ export function updatePersonnel(params){
 export function getInterviewEvaluation(id){
     return getRequest(`/supports/interview/evaluation/query/${id}`)
 }
+/* 获取人员项目信息 */
+export function getProjectDetail(params){
+    return postRequest('/supports/charge/project/queryPage',params)
+}
+/* 更新负责项目 */
+export function updateProject(params){
+    return postRequest('/supports/charge/project/update',params)
+}
+/* 更新面试评价 */
+export function updateInterview(params){
+    return postRequest()
+}

+ 5 - 1
src/api/support/file-api.js

@@ -11,8 +11,12 @@ export const fileApi = {
   uploadUrl: '/support/file/upload',
   uploadFile: (param, folder) => {
     return postRequest(`/support/file/upload?folder=${folder}`, param);
-  },
 
+  },
+  /* 文件上传不带token */
+  uploadFileIgnoreToken:(param, folder)=>{
+    return postRequest(`/support/employee/file/upload?folder=${folder}`, param)
+  },
   /**
    * 分页查询  @author Gnawzs
    */

+ 44 - 0
src/components/mobile/uploader.vue

@@ -0,0 +1,44 @@
+<template>
+    <van-uploader v-model="fileList" :after-read="afterRead" v-bind="props.componentProps" />
+</template>
+<script setup>
+import { fileApi } from '/src/api/support/file-api';
+import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+const fileList = defineModel('fileList')
+const props = defineProps({
+    componentProps: Object,
+    folder: {
+        type: Number,
+        default: FILE_FOLDER_TYPE_ENUM.COMMON.value,
+    },
+    //是否上传后清空文件列表
+    uploadAterClear: {
+        type: Boolean,
+        default: false,
+    },
+})
+const emit = defineEmits(['change']);
+async function afterRead(options) {
+
+    const formData = new FormData();
+    formData.append('file', options.file);
+    // console.log(formData)
+    let res = await fileApi.uploadFileIgnoreToken(formData, props.folder);
+    // console.log(res)
+
+    let file = res.data;
+    file.url = file.fileUrl;
+    file.name = file.fileName;
+
+    if (props.uploadAterClear) {
+        fileList.value = []
+    }
+    fileList.value = fileList.value.map(item => {
+        if (item.file.name === file.name) {
+            return file;
+        }
+    });
+    emit('change', fileList.value);
+}
+</script>
+<style lang="scss" scoped></style>

+ 10 - 3
src/components/support/file-upload/index.vue

@@ -121,6 +121,14 @@ const files = computed(() => {
     });
     return res;
   }
+  if (props.value && props.value.length > 0) {
+    props.value.forEach((element) => {
+      element.url = element.fileUrl;
+      element.name = element.fileName;
+      res.push(element);
+    });
+    return res;
+  }
   return res;
 });
 // -------------------- 逻辑 --------------------
@@ -146,9 +154,8 @@ const customRequest = async (options) => {
     // console.log(options);
     const formData = new FormData();
     formData.append('file', options.file);
-    // console.log(formData)
+    
     let res = await fileApi.uploadFile(formData, props.folder);
-    // console.log(res)
     let file = res.data;
     file.url = file.fileUrl;
     file.name = file.fileName;
@@ -156,8 +163,8 @@ const customRequest = async (options) => {
       fileList.value = []
     }
     fileList.value.push(file);
-    emit('change', fileList.value);
     emit('update:value',fileList.value)
+    emit('change', fileList.value);
   } catch (e) {
     smartSentry.captureError(e);
   } finally {

+ 102 - 43
src/views/personnel-entry/mobile/components/one-step.vue

@@ -1,5 +1,5 @@
 <template>
-    <van-form @failed="onFailed" @sumbit="onSubmit">
+    <van-form ref="oneStepFormRef">
         <van-cell-group class="cell-group">
             <div class="title">
                 <div class="line"></div>
@@ -15,43 +15,44 @@
                     </van-radio-group>
                 </template>
             </van-field>
-            <van-field v-model="formDataOneStep.name" label="证件号" name="name" placeholder="请输入证件号"
-                :rules="[{ required: true, message: '请输入证件号' }]" required />
-            <van-field v-model="formDataOneStep.name" label="手机号" name="name" placeholder="请输入手机号"
-                :rules="[{ required: true, message: '请输入手机号' }]" required />
-            <van-field v-model="formDataOneStep.name" is-link readonly name="name" label="岗位" placeholder="请选择岗位"
-                @click="showPicker = true" required />
-            <van-popup v-model:show="showPicker" position="bottom">
-                <van-picker :columns="columns" @confirm="onConfirm" @cancel="showPicker = false" />
+            <van-field v-model="formDataOneStep.idCard" label="证件号" name="idCard" placeholder="请输入证件号"
+                :rules="[{ required: true, message: '请输入证件号' },{pattern: /(^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$)|(^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$)/,message: '请输入正确的身份证号'}]" required />
+            <van-field v-model="formDataOneStep.tel" label="手机号" name="tel" placeholder="请输入手机号"
+                :rules="[{ required: true, message: '请输入手机号' },{pattern: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/,message: '请输入正确的手机号'}]" required />
+            <van-field v-model="formDataOneStep.postText" is-link readonly name="postText" label="岗位" :rules="[{ required: true, message: '请选择岗位' }]"
+                placeholder="请选择岗位" @click="showPicker('post')" required />
+            <van-popup v-model:show="show.post" position="bottom">
+                <van-picker :columns-field-names="{ text: 'label', value: 'value' }"
+                    :columns="useBsDict.getDictList('BLINK_ENTRY_POST')" @confirm="onConfirmPost"
+                    @cancel="closePicker('post')" />
             </van-popup>
-            <!-- <van-field name="name" label="文件上传" :rules="[{ required: true, message: '请选择您的性别' }]">
-                    <template #input>
-                        <van-uploader v-model="formData.name" />
-                    </template>
-            </van-field> -->
-            <van-field v-model="formDataOneStep.name" is-link readonly name="name" label="类型" placeholder="请选择招聘类型"
-                @click="showPicker = true" required />
-            <van-popup v-model:show="showPicker" position="bottom">
-                <van-picker :columns="columns" @confirm="onConfirm" @cancel="showPicker = false" />
+            <van-field v-model="formDataOneStep.typeText" is-link readonly name="typeText" label="类型"  :rules="[{ required: true, message: '请选择招聘类型' }]"
+                placeholder="请选择招聘类型" @click="showPicker('type')" required />
+            <van-popup v-model:show="show.type" position="bottom">
+                <van-picker :columns-field-names="{ text: 'label', value: 'value' }"
+                    :columns="useBsDict.getDictList('BLINK_ENTRY_TYPE')" @confirm="onConfirmType"
+                    @cancel="closePicker('type')" />
             </van-popup>
-            <van-field v-model="formDataOneStep.name" label="工作年限" name="name" placeholder="请输入工作年限"
+            <van-field v-model="formDataOneStep.workYear" label="工作年限" name="workYear" placeholder="请输入工作年限" type="number" :formatter="transformData"
                 :rules="[{ required: true, message: '请输入工作年限' }]" required />
-            <van-field v-model="formDataOneStep.name" is-link readonly name="name" label="学历" placeholder="请选择最高学历"
-                @click="showArea = true" required />
-            <van-popup v-model:show="showArea" position="bottom">
-                <van-area :area-list="areaList" @confirm="onConfirm" @cancel="showArea = false" />
+            <van-field v-model="formDataOneStep.degreeText" is-link readonly name="degreeText" label="学历" :rules="[{ required: true, message: '请选择最高学历' }]"
+                placeholder="请选择最高学历" @click="showPicker('degree')" required />
+            <van-popup v-model:show="show.degree" position="bottom">
+                <van-picker :columns-field-names="{ text: 'label', value: 'value' }"
+                    :columns="useBsDict.getDictList('BLINK_ENTRY_DEGREE')" @confirm="onConfirmDegree"
+                    @cancel="closePicker('degree')" />
             </van-popup>
-            <van-field v-model="formDataOneStep.name" label="学校" name="name" placeholder="请输入毕业院校"
+            <van-field v-model="formDataOneStep.graduateSchool" label="学校" name="graduateSchool" placeholder="请输入毕业院校"
                 :rules="[{ required: true, message: '请输入毕业院校' }]" required />
-            <van-field v-model="formDataOneStep.name" label="专业" name="name" placeholder="请输入所学专业"
+            <van-field v-model="formDataOneStep.specialized" label="专业" name="specialized" placeholder="请输入所学专业"
                 :rules="[{ required: true, message: '请输入所学专业' }]" required />
-            <van-field v-model="formDataOneStep.name" is-link readonly name="name" label="联系地址" placeholder="请选择联系地址"
-                @click="showArea = true" required />
-            <van-popup v-model:show="showArea" position="bottom">
-                <van-area :area-list="areaList" @confirm="onConfirm" @cancel="showArea = false" />
+            <van-field v-model="formDataOneStep.provinceCityDistrict" is-link readonly name="provinceCityDistrict"
+                label="联系地址" placeholder="请选择联系地址" @click="showPicker('area')" required />
+            <van-popup v-model:show="show.area" position="bottom">
+                <van-cascader :options="options" @finish="onConfirmArea" @close="closePicker('area')" />
             </van-popup>
-            <van-field v-model="formDataOneStep.name" type="textarea" label="详细地址" name="name" placeholder="请输入详细地址"
-                :rules="[{ required: true, message: '请输入详细地址' }]" required />
+            <van-field v-model="formDataOneStep.address" type="textarea" label="详细地址" name="address"
+                placeholder="请输入详细地址" label-align="top" :rules="[{ required: true, message: '请输入详细地址' }]" required />
         </van-cell-group>
         <div class="gap"></div>
         <van-cell-group class="cell-group">
@@ -59,31 +60,89 @@
                 <div class="line"></div>
                 <div class="title-info">基本信息-紧急联系人</div>
             </div>
-            <van-field v-model="formDataOneStep.name" name="name" label="紧急联系人" placeholder="请输入紧急联系人称呼"
-                @click="showArea = true" required />
-            <van-field v-model="formDataOneStep.name" name="name" label="紧急联系人关系" placeholder="请输入紧急联系人关系"
-                @click="showArea = true" required />
-            <van-field v-model="formDataOneStep.name" name="name" label="联系方式" placeholder="请输入联系人手机号" required />
+            <van-field v-model="formDataOneStep.emergencyContact" name="emergencyContact" label="紧急联系人"
+                :rules="[{ required: true, message: '请输入紧急联系人' }]" placeholder="请输入紧急联系人称呼" label-width="7.8em" required />
+            <van-field v-model="formDataOneStep.contactRelationship" name="contactRelationship" label="紧急联系人关系"
+                :rules="[{ required: true, message: '请输入紧急联系人关系' }]" placeholder="请输入紧急联系人关系" label-width="7.8em" required />
+            <van-field v-model="formDataOneStep.contactTel" name="contactTel" label="联系方式" placeholder="请输入联系人手机号"
+                :rules="[{ required: true, message: '请输入联系方式' },{pattern: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/,message: '请输入正确的手机号'}]" label-width="7.8em" required />
         </van-cell-group>
     </van-form>
 </template>
 <script setup>
 import { ref, reactive } from "vue"
 import useBsDict from '/@/utils/dict.js';
+import { useCascaderAreaData } from '@vant/area-data';
+import { isEmpty } from "lodash";
+const options = useCascaderAreaData();
+const oneStepFormRef =ref(null)
 const formDataOneStep = defineModel('formDataOneStep')
-const showArea = ref(false)
-const showPicker = ref(false)
-const columns = []
-const areaList = []
-function onFailed(errorInfo) {
 
+const show = ref({
+    post: false,
+    type: false,
+    degree: false,
+    area: false
+})
+const showPicker = (type) => {
+    show.value[type] = true;
+};
+const closePicker = (type) => {
+    show.value[type] = false
 }
-function onConfirm() {
 
+function onConfirmPost(selectedValues) {
+    // console.log(selectedValues);
+    formDataOneStep.value.postText = selectedValues.selectedOptions[0].label
+    formDataOneStep.value.post = selectedValues.selectedOptions[0].value
+    closePicker('post')
 }
-function onSubmit(values) {
+function onConfirmType(selectedValues) {
+    // console.log(selectedValues);
+    formDataOneStep.value.typeText = selectedValues.selectedOptions[0].label
+    formDataOneStep.value.type = selectedValues.selectedOptions[0].value
+    closePicker('type')
+}
+function onConfirmDegree(selectedValues) {
+    // console.log(selectedValues);
+    formDataOneStep.value.degreeText = selectedValues.selectedOptions[0].label
+    formDataOneStep.value.degree = selectedValues.selectedOptions[0].value
+    closePicker('degree')
+}
+function onConfirmArea(selectedValues) {
+    // console.log(selectedValues);
+    formDataOneStep.value = Object.assign(formDataOneStep.value, {
+        province: '',
+        provinceName: '',
+        city: '',
+        cityName: '',
+        district: '',
+        districtName: '',
+    });
+    formDataOneStep.value.provinceCityDistrict = selectedValues.selectedOptions.map((option) => option.text).join('/');
+    if (!isEmpty(selectedValues)) {
+        // 地区信息
+        formDataOneStep.value.province = selectedValues.selectedOptions[0].value;
+        formDataOneStep.value.provinceName = selectedValues.selectedOptions[0].text;
 
+        formDataOneStep.value.city = selectedValues.selectedOptions[1].value;
+        formDataOneStep.value.cityName = selectedValues.selectedOptions[1].text;
+        if (selectedValues.selectedOptions[2]) {
+            formDataOneStep.value.district = selectedValues.selectedOptions[2].value;
+            formDataOneStep.value.districtName = selectedValues.selectedOptions[2].text;
+        }
+        closePicker('area')
+    }
 }
+function transformData(val){
+    if (isEmpty(val)) return '0';
+    return String(Number(val)); // "001" → "1"
+}
+async function validate(){
+    await oneStepFormRef.value.validate()
+}
+
+defineExpose({validate})
 </script>
 <style lang="scss" scoped>
 .gap {

+ 26 - 19
src/views/personnel-entry/mobile/components/three-step.vue

@@ -1,32 +1,30 @@
 <template>
-    <van-form @failed="onFailed" @sumbit="onSubmit">
+    <van-form ref="threeStepFormRef">
         <van-cell-group class="cell-group">
             <div class="title">
                 <div class="line"></div>
                 <div class="title-info">证件信息</div>
             </div>
-            <van-field v-model="formDataThreeStep.name" label="身份证" name="name"
-                :rules="[{ required: true, message: '请上传身份证图片' }]" required>
+            <van-field label="身份证" name="uploader" :rules="[{ validator: validateIdCard }]" required>
                 <template #input>
                     <div class="uploader">
-                        <van-uploader />
+                        <uploader v-model:fileList="formDataThreeStep.idCardFront" :componentProps="{ maxCount: 1 }" />
                         人像面
                     </div>
                     <div class="uploader">
-                        <van-uploader />
+                        <uploader v-model:fileList="formDataThreeStep.idCardBack" :componentProps="{ maxCount: 1 }" />
                         国徽面
                     </div>
                 </template>
             </van-field>
-            <van-field v-model="formDataThreeStep.name" label="无犯罪记录证明" name="name"
-                :rules="[{ required: true, message: '请上传无犯罪记录证明图片' }]" required>
+            <van-field label="无犯罪记录证明" name="uploader" :rules="[{ required: true, message: '请上传无犯罪记录证明图片' }]" required>
                 <template #input>
-                    <van-uploader />
+                    <uploader v-model:fileList="formDataThreeStep.noCriminalRecord" :componentProps="{ maxCount: 1 }" />
                 </template>
             </van-field>
-            <van-field v-model="formDataThreeStep.name" label="解聘证书" name="name">
+            <van-field label="解聘证书" name="uploader">
                 <template #input>
-                    <van-uploader />
+                    <uploader v-model:fileList="formDataThreeStep.dismissingCert" :componentProps="{ maxCount: 1 }" />
                 </template>
             </van-field>
         </van-cell-group>
@@ -36,21 +34,19 @@
                 <div class="line"></div>
                 <div class="title-info">学历及其他证书核验</div>
             </div>
-            <van-field v-model="formDataThreeStep.name" label="毕业证书" name="name"
-                :rules="[{ required: true, message: '请上传毕业证书图片' }]" required>
+            <van-field label="毕业证书" name="uploader" :rules="[{ required: true, message: '请上传毕业证书图片' }]" required>
                 <template #input>
-                    <van-uploader />
+                    <uploader v-model:fileList="formDataThreeStep.graduationCert" :componentProps="{ maxCount: 1 }" />
                 </template>
             </van-field>
-            <van-field v-model="formDataThreeStep.name" label="学位证书" name="name"
-                :rules="[{ required: true, message: '请上传学位证书图片' }]" required>
+            <van-field label="学位证书" name="uploader" :rules="[{ required: true, message: '请上传学位证书图片' }]" required>
                 <template #input>
-                    <van-uploader />
+                    <uploader v-model:fileList="formDataThreeStep.degreeCert" :componentProps="{ maxCount: 1 }" />
                 </template>
             </van-field>
-            <van-field v-model="formDataThreeStep.name" label="其他证书" name="name">
+            <van-field label="其他证书" name="uploader">
                 <template #input>
-                    <van-uploader />
+                    <uploader v-model:fileList="formDataThreeStep.otherCert" :componentProps="{ maxCount: 1 }" />
                 </template>
             </van-field>
         </van-cell-group>
@@ -58,9 +54,20 @@
 </template>
 <script setup>
 import { ref, reactive } from "vue"
+import uploader from "/@/components/mobile/uploader.vue";
+import { isEmpty } from "lodash";
 const formDataThreeStep = defineModel('formDataThreeStep')
+const threeStepFormRef = ref(null)
 
-
+function validateIdCard(value, rule) {
+        if (formDataThreeStep.value.idCardFront &&formDataThreeStep.value.idCardBack&& formDataThreeStep.value.idCardFront.length > 0&& formDataThreeStep.value.idCardBack.length > 0) return 
+        if (isEmpty(formDataThreeStep.value.idCardFront) || (formDataThreeStep.value.idCardFront && formDataThreeStep.value.idCardFront.length) === 0) return '身份证人像面不能为空'
+        if (isEmpty(formDataThreeStep.value.idCardBack) || (formDataThreeStep.value.idCardBack && formDataThreeStep.value.idCardBack.length) === 0) return '身份证国徽面不能为空'
+}
+async function validate() {
+    await threeStepFormRef.value.validate()
+}
+defineExpose({ validate })
 </script>
 <style lang="scss" scoped>
 .gap {

+ 51 - 18
src/views/personnel-entry/mobile/components/two-step.vue

@@ -1,44 +1,77 @@
 <template>
-    <van-form @failed="onFailed" @sumbit="onSubmit">
+    <van-form ref="twoStepFormRef">
         <van-cell-group class="cell-group">
             <div class="title">
                 <div class="line"></div>
                 <div class="title-info">项目信息</div>
             </div>
-            <van-field v-model="formDataTwoStep.name" type="textarea" label="个人专长" name="name"
-                placeholder="请围绕管理或技术维度填写,字数不少于120字" :rules="[{ required: true, message: '请输入个人专长' }]" />
-            <van-field label="负责项目情况" class="addBox">
+            <van-field v-model="formDataTwoStep.personalExpertise" type="textarea" label="个人专长" name="personalExpertise"
+                placeholder="请围绕管理或技术维度填写,字数不少于120字" :rules="[{ required: true, message: '请输入个人专长' },{ validator: (val) => val.length >= 120,message: '内容长度不能少于120个字符' }]" required />
+            <van-field label="负责项目情况" class="addBox" name="chargeProjectDTOList" label-width="7em" required>
                 <template #input>
                     <van-button type="primary" size="small" @click="add">新增</van-button>
                 </template>
             </van-field>
-            <div class="box">
-                <van-button class="btn" size="small" type="danger" @click="remove">删除</van-button>
-                <van-field v-model="formDataTwoStep.name" label="姓名" name="name" placeholder="请输入姓名"
-                    :rules="[{ required: true, message: '请输入您的姓名' }]" />
-                <van-field v-model="formDataTwoStep.name" name="calendar" label="项目周期" placeholder="请选择项目开始和结束时间"
+            <div class="box" v-for="(item, index) in formDataTwoStep.chargeProjectDTOList"
+                v-if="formDataTwoStep.chargeProjectDTOList && formDataTwoStep.chargeProjectDTOList.length > 0">
+                <van-button class="btn" size="small" type="danger" @click="remove(index)">删除</van-button>
+                <van-field v-model="formDataTwoStep.chargeProjectDTOList[index].projectName" label="项目名称"
+                    name="projectName" placeholder="请输入项目名称" :rules="[{ required: true, message: '请输入项目名称' }]" />
+                <van-field v-model="formDataTwoStep.chargeProjectDTOList[index].projectTime" name="projectTime"
+                    label="项目周期" placeholder="请选择项目开始和结束时间"
+                    :rules="[{ required: true, message: '请选择项目开始和结束时间' }]"
                     @click="showCalendar = true" />
-                <van-calendar v-model:show="showCalendar" type="range" @confirm="onConfirm" />
-                <van-field v-model="formDataTwoStep.name" label="项目任职" type="textarea" name="name" placeholder="请输入任职描述"
-                    :rules="[{ required: true, message: '请输入您的姓名' }]" label-align="top" />
+                <van-calendar v-model:show="showCalendar" type="range" @confirm="(value) => onConfirm(index, value)" />
+                <van-field v-model="formDataTwoStep.chargeProjectDTOList[index].projectAppointment" label="项目任职"
+                    type="textarea" name="projectAppointment" placeholder="请输入任职描述"
+                    :rules="[{ required: true, message: '请输入任职描述' }]" label-align="top" />
             </div>
         </van-cell-group>
     </van-form>
 </template>
 <script setup>
+import dayjs from "dayjs"
 import { ref, reactive } from "vue"
- 
+const twoStepFormRef = ref(null)
 const formDataTwoStep = defineModel('formDataTwoStep')
 const showCalendar = ref(false)
-function onConfirm() {
 
-}
 function add() {
+    formDataTwoStep.value.chargeProjectDTOList.push(
+        {
+            projectName: undefined,
+            projectTime: undefined,
+            projectStartTime: undefined,
+            projectEndTime: undefined,
+            projectAppointment: undefined
+        }
+    )
+}
 
+function remove(index) {
+    formDataTwoStep.value.chargeProjectDTOList.splice(index, 1)
+}
+
+function onConfirm(index, date) {
+    const startDate = dayjs(date[0]).format('YYYY-MM-DD')
+    const endDate = dayjs(date[1]).format('YYYY-MM-DD')
+    /* 开始时间大于结束时间 */
+    if (date[0] > date[1]) {
+        showFailToast('结束时间必须大于开始时间')
+        return
+    }
+    formDataTwoStep.value.chargeProjectDTOList[index].projectTime = `${startDate} —— ${endDate}`
+    formDataTwoStep.value.chargeProjectDTOList[index].projectStartTime = startDate
+    formDataTwoStep.value.chargeProjectDTOList[index].projectEndTime = endDate
+    showCalendar.value =false
+    // console.log(startDate, endDate);
+    // console.log(index, date);
 }
-function remove(){
 
+async function validate() {
+    await twoStepFormRef.value.validate()
 }
+defineExpose({ validate })
 </script>
 <style lang="scss" scoped>
 .cell-group {
@@ -69,13 +102,13 @@ function remove(){
 
     .box {
         border-radius: 8px;
-        margin: .5rem;
+        margin: .8rem;
         box-shadow: 0px 0px 4px 0px rgba(163, 163, 163, 0.6);
 
         .btn {
             border-radius: 0 8px;
             position: absolute;
-            right: .5rem;
+            right: .8rem;
             z-index: 2;
         }
     }

+ 71 - 13
src/views/personnel-entry/mobile/index.vue

@@ -4,16 +4,17 @@
             <van-step v-for="item in step" :key="item.key">{{ item.title }}</van-step>
         </van-steps>
         <div class="personnel-entry-form">
-            <oneStep v-model:form-data-one-step="formDataOneStep" v-if="active === 0" />
-            <twoStep v-model:form-data-two-step="formDataTwoStep" v-else-if="active === 1" />
-            <threeStep v-model:form-data-three-step="formDataThreeStep" v-else-if="active === 2" />
+            <oneStep v-model:form-data-one-step="formDataOneStep" ref="oneStepRef" v-if="active === 0" />
+            <twoStep v-model:form-data-two-step="formDataTwoStep" ref="twoStepRef" v-else-if="active === 1" />
+            <threeStep v-model:form-data-three-step="formDataThreeStep" ref="threeStepRef" v-else-if="active === 2" />
             <submitStep v-else-if="active === 3" />
         </div>
-        <div class="personnel-entry-option" v-if="active !==3">
+        <div class="personnel-entry-option" v-if="active !== 3">
             <van-button round block type="primary" @click="preStep" v-if="active === 1 || active === 2">
                 上一步
             </van-button>
-            <van-button round block type="primary" @click="nextStep" v-if="active !== 2 || active !== 2">
+            <van-button round block type="primary" native-type="submit" @click="nextStep"
+                v-if="active !== 2 || active !== 2">
                 下一步
             </van-button>
             <van-button round block type="primary" native-type="submit" @click="submit" v-if="active === 2">
@@ -23,24 +24,54 @@
     </div>
 </template>
 <script setup>
-import { ref, reactive } from "vue"
+import { ref } from "vue"
 import oneStep from "./components/one-step.vue";
 import twoStep from "./components/two-step.vue";
 import threeStep from "./components/three-step.vue";
 import submitStep from "./components/submit-step.vue";
+import useBsDict from '/@/utils/dict.js';
+import { addPersonnelInfo } from "/@/api/personnel-entry";
+import { showFailToast, showSuccessToast } from "vant";
+useBsDict.refresh()
 const active = ref(0)
-
+const oneStepRef = ref(null)
+const twoStepRef = ref(null)
+const threeStepRef = ref(null)
 const formDataOneStep = ref(
     {
-        name: null,
-        sex: 1
+        name: undefined,
+        sex: 1,
+        idCard: undefined,
+        tel: undefined,
+        postText: undefined,
+        post: undefined,
+        typeText: undefined,
+        type: undefined,
+        degreeText: undefined,
+        degree: undefined,
+        workYear: 0,
+        graduateSchool: undefined,
+        provinceCityDistrict: undefined,
+        address: undefined,
+        emergencyContact: undefined,
+        contactRelationship: undefined,
+        contactTel: undefined
     }
 )
 const formDataTwoStep = ref({
+    personalExpertise: undefined,
+    chargeProjectDTOList: [
 
+    ]
 })
 const formDataThreeStep = ref({
-
+    idCardFront: undefined,
+    idCardBack: undefined,
+    noCriminalRecord: undefined,
+    dismissingCert: undefined,
+    graduationCert: undefined,
+    degreeCert: undefined,
+    otherCert: undefined
 })
 
 const step = [
@@ -60,11 +91,35 @@ const step = [
 function preStep() {
     active.value -= 1
 }
-function nextStep() {
+async function nextStep() {
+    if (active.value == 0) {
+        await oneStepRef.value.validate()
+    } else if (active.value == 1) {
+        await twoStepRef.value.validate()
+    } 
     active.value += 1
 }
-function submit() {
-    nextStep()
+async function submit() {
+    const params = {
+        ...formDataOneStep.value,
+        ...formDataTwoStep.value,
+        ...formDataThreeStep.value
+    }
+    if (active.value == 2) {
+        await threeStepRef.value.validate()
+    } 
+    params.provinceCityDistrict = undefined
+    await addPersonnelInfo(params).then((res) => {
+        if (!res.ok) {
+            if (!res.ok) {
+                showFailToast(res.msg)
+                return
+            }
+            nextStep()
+            showSuccessToast('提交成功')
+        }
+    })
+
 }
 </script>
 <style lang="scss" scoped>
@@ -72,9 +127,12 @@ function submit() {
     height: 100%;
     width: 100%;
     background-color: #fff;
+    border-radius: 12px 12px 0 0;
+    // margin-top: 12px;
     // padding: .5rem;
 
 
+
     .personnel-entry-form {
         height: calc(100vh - 6rem - 58px);
         overflow: hidden;

+ 6 - 9
src/views/personnel-entry/pc/components/interview-evaluation.vue

@@ -17,7 +17,7 @@
     </div>
 </template>
 <script setup>
-import { ref, computed, inject, onMounted, watch } from "vue"
+import { ref, computed, inject, watch } from "vue"
 import { useUserStore } from "/@/store/modules/system/user";
 import BsForm, { useBsForm } from '/@/components/BsUi/Form/index.js';
 import { formatDate } from "/@/utils/date";
@@ -226,16 +226,13 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
 })
 
 async function validateForm() {
-    console.log(1211111);
-
-    await formRef.value.forEach(item => {
-        item.handlerFormValidator()
-    });
+    if (formRef.value && formRef.value.length > 0) {
+        await formRef.value.forEach(item => {
+            item.handlerFormValidator()
+        });
+    }
 }
 function claerItem(index) {
-    // emit('setClose')
-    console.log(formRef.value);
-
     if (formData.value && formData.value.length === 0 && createInterviewEvaluation.value === true) {
         createInterviewEvaluation.value = !createInterviewEvaluation.value
         formData.value[0] = Object.assign(formData.value[0], formDataInit)

+ 36 - 18
src/views/personnel-entry/pc/components/project-deatil.vue → src/views/personnel-entry/pc/components/project-detail.vue

@@ -21,20 +21,31 @@
 </template>
 
 <script setup lang="jsx">
-import { ref, nextTick, computed, watch, onMounted } from 'vue';
-import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
-import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+import { ref, nextTick, onMounted } from 'vue';
 import dayjs from 'dayjs';
 import { message } from 'ant-design-vue';
+import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+import { getProjectDetail } from '/@/api/personnel-entry';
+import { isEmpty } from 'lodash';
 const formData = defineModel('value')
-const { employeeCode } = defineProps(['employeeCode'])
-onMounted(() => {
-    formData.value = getValue('gridOptions.data')
+const props = defineProps(['employeeCode', 'parentFormRef'])
+const emit = defineEmits(['change'])
+onMounted(async () => {
+    /* 编辑 */
+    if (!isEmpty(props.employeeCode)) {
+        const params = {
+            pageSize: 100,
+            pageNum: 1,
+            employeeCode: props.employeeCode
+        }
+        const res = await getProjectDetail(params)
+        emit('change',res.data.list)
+        formData.value = res.data.list
+        setValue('gridOptions.data', res.data.list)
+    }
 })
-watch(() => formData.value, (value) => {
-    console.log(value);
 
-})
 const {
     tableOptions,
     setTablePropsValue: setValue,
@@ -44,7 +55,6 @@ const {
     fetchTableData,
 } = useBsTable({
     tableOptions: {
-        url: '/supports/charge/project/queryPage',
         toolbarTopConfig: {
             enable: false,
         },
@@ -69,7 +79,7 @@ const {
                     { required: true, message: '项目结束时间不能为空' },
                     {
                         validator({ cellValue, row }) {
-                            console.log(cellValue, row.projectStartTime);
+                            // console.log(cellValue, row.projectStartTime);
                             if (new Date(row.projectStartTime) > new Date(cellValue)) {
                                 return Promise.reject('结束时间必须大于开始时间');
                             }
@@ -158,22 +168,20 @@ const {
                         nextTick(() => {
                             const $table = getGridRef();
                             $table.insertAt(newRow, -1)
-                            // formData.value = getValue('gridOptions.data')
+                            formData.value.push(newRow)
+                            // console.log(formData.value);
                         });
                     },
                 },
             }],
         },
-        tableSearchBeforeBiz() {
-            const searchParams = getValue('searchConfig.data');
-            setValue('searchConfig.data', { ...searchParams, employeeCode: employeeCode });
-        },
     },
 });
 const hasEditStatus = (row) => {
     const $grid = getGridRef()
     if ($grid) {
         return $grid.isEditByRow(row)
+
     }
 }
 async function saveRowEvent(row) {
@@ -185,13 +193,23 @@ async function saveRowEvent(row) {
             message.warning('校验失败')
             throw new Error('校验失败')
         };
-        formData.value.push(row)
-        console.log(formData.value);
+        // console.log(await $grid.getEditRecord(row));
 
+        const { row: newRow, rowIndex, isEdit } = await $grid.getEditRecord(row);
+        if (!isEdit) {
+            formData.value[rowIndex] = newRow
+        } else {
+            formData.value[rowIndex] = Object.assign(formData.value[rowIndex], newRow)
+        }
+
+        if (props.parentFormRef) {
+            props.parentFormRef.ValidatorSingalField('chargeProjectDTOList')
+        }
         $grid.clearEdit()
 
     }
 }
+
 const editRowEvent = (row) => {
     const $grid = getGridRef()
     // console.log($grid, row);

+ 43 - 129
src/views/personnel-entry/pc/index.vue

@@ -15,10 +15,10 @@
     </div>
 </template>
 <script setup lang="jsx">
-import { ref, reactive, provide, onMounted } from "vue"
+import { ref, provide, onMounted } from "vue"
 import BsForm, { useBsForm } from '/@/components/BsUi/Form/index.js';
 import FileUpload from '/@/components/support/file-upload/index.vue'
-import ProjectDeatil from "./components/project-deatil.vue";
+import ProjectDeatil from "./components/project-detail.vue";
 import interviewEvaluation from "./components/interview-evaluation.vue";
 import AreaCascader from '/@/components/framework/area-cascader/index.vue';
 import useBsDict from '/@/utils/dict.js';
@@ -481,14 +481,6 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '1',
                 span: 6,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
                 },
             },
             {
@@ -530,10 +522,8 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 formItemExtraProps: {
                     rules: [
                         {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
+                            min: 120,
+                            message: '个人专长不能小于120个字'
                         },
                     ],
                 },
@@ -544,8 +534,15 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 label: '负责项目情况',
                 component: ProjectDeatil,
                 componentProps: {
-                    placeholder: '请输入负责项目情况',
-                    employeeCode: employeeCode
+                    employeeCode: employeeCode,
+                    parentFormRef: bsFormRef,
+                    onChange: (value) => {
+                        setFVal('formData.chargeProjectDTOList',value)
+                        console.log(getFValue('formData'));
+                        
+                        console.log(value);
+                        
+                    }
                 },
                 field: 'chargeProjectDTOList',
                 sort: '1',
@@ -556,6 +553,8 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     rules: [
                         {
                             validator: (_, value) => {
+                                console.log(value);
+
                                 // return Promise.reject(new Error('报错'));
                                 return Promise.resolve();
                             },
@@ -572,7 +571,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     buttonText: '人像面上传',
                     showUploadList: false,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg',
+                    accept: '.png,.jpg,.jpeg',
                     onChange: (value) => {
                         bsFormRef.value.ValidatorSingalField('idCardFront')
                     }
@@ -583,19 +582,6 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '1',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            transform: (value) => {
-                                console.log(value);
-                            },
-                            asyncValidator: (rule, value) => {
-                                console.log(rule, value);
-                                return Promise.resolve();
-                            },
-                            type: 'array',
-                            trigger: 'change'
-                        },
-                    ],
                 },
             },
             {
@@ -608,7 +594,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     showUploadList: false,
                     uploadAterClear: true,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg'
+                    accept: '.png,.jpg,.jpeg'
                 },
                 field: 'idCardBack',
                 sort: '1',
@@ -616,14 +602,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '1',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
+
                 },
             },
             {
@@ -636,7 +615,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     showUploadList: false,
                     uploadAterClear: true,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg'
+                    accept: '.png,.jpg,.jpeg'
                 },
                 field: 'noCriminalRecord',
                 sort: '1',
@@ -644,14 +623,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '1',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
+
                 },
             },
             {
@@ -664,7 +636,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     showUploadList: false,
                     uploadAterClear: true,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg'
+                    accept: '.png,.jpg,.jpeg'
                 },
                 field: 'dismissingCert',
                 sort: '1',
@@ -672,14 +644,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '0',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
+
                 },
             },
             {
@@ -692,7 +657,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     showUploadList: false,
                     uploadAterClear: true,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg'
+                    accept: '.png,.jpg,.jpeg'
                 },
                 field: 'graduationCert',
                 sort: '1',
@@ -700,14 +665,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '1',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
+
                 },
             },
             {
@@ -720,7 +678,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     showUploadList: false,
                     uploadAterClear: true,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg'
+                    accept: '.png,.jpg,.jpeg'
                 },
                 field: 'degreeCert',
                 sort: '1',
@@ -728,14 +686,6 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '1',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
                 },
             },
             {
@@ -748,7 +698,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     showUploadList: false,
                     uploadAterClear: true,
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg'
+                    accept: '.png,.jpg,.jpeg'
                 },
                 field: 'otherCert',
                 sort: '1',
@@ -756,14 +706,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 required: '0',
                 span: 2,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
+
                 },
             },
             {
@@ -775,7 +718,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     buttonText: '证书上传',
                     listType: 'text',
                     maxUploadSize: 1,
-                    accept: '.png,.jpg,jpeg,.pdf'
+                    accept: '.png,.jpg,.jpeg,.pdf'
                 },
                 field: 'onboardingExamine',
                 sort: '1',
@@ -784,14 +727,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 span: 2,
                 tooltip: "支持png、jpg、pdf等常见报告格式",
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
+
                 },
             },
             {
@@ -806,14 +742,6 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 visible: '1',
                 span: 24,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
                 },
             },
             {
@@ -834,14 +762,6 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 tooltip: '支持png、jpg、pdf等常见报告格式',
                 span: 6,
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
                 },
             },
             {
@@ -853,10 +773,7 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                     buttonText: '点击上传',
                     listType: 'text',
                     maxUploadSize: 3,
-                    accept: '.png,.jpg,jpeg,.pdf,.doc,.ppt,.mp4,.avi',
-                    onChange: (value) => {
-                        console.log(value);
-                    }
+                    accept: '.png,.jpg,.jpeg,.pdf,.doc,.ppt,.mp4,.avi',
                 },
                 field: 'attachmentInformation',
                 sort: '1',
@@ -864,14 +781,6 @@ const { formOptions, setFormPropsValue: setFVal, getFormPropsValue: getFValue }
                 span: 24,
                 tooltip: "附件上传 支持png、jpg、doc、ppt、pdf、mp4、avi等常见报告格式",
                 formItemExtraProps: {
-                    rules: [
-                        {
-                            validator: (_, value) => {
-                                // return Promise.reject(new Error('报错'));
-                                return Promise.resolve();
-                            },
-                        },
-                    ],
                 },
             },
         ],
@@ -888,10 +797,12 @@ function transformData(field, val) {
     formData[field] = String(Number(val)); // "001" → "1"
 }
 async function save() {
+    const params = { ...getFValue('formData') }
     if (isEmpty(id)) {
         /* 新增 */
-        const params = { ...getFValue('formData') }
-        console.log(params);
+        // console.log(params);
+        await bsFormRef.value.handlerFormValidator()
+        await children.value[0].childMethod()
         params.tel = parseInt(params.tel)
         params.contactTel = parseInt(params.contactTel)
         params.idCard = parseInt(params.idCard)
@@ -899,8 +810,6 @@ async function save() {
         params.salaryNumber = parseInt(params.salaryNumber)
         params.provinceCityDistrict = undefined
 
-        await bsFormRef.value.handlerFormValidator()
-        await children.value[0].childMethod()
         await addPersonnel(params).then((res) => {
             if (!res.ok) {
                 message.error(res.msg)
@@ -910,8 +819,17 @@ async function save() {
         })
     } else {
         /* 编辑 */
+        console.log(params);
+
         await bsFormRef.value.handlerFormValidator()
         await children.value[0].childMethod()
+        params.tel = parseInt(params.tel)
+        params.contactTel = parseInt(params.contactTel)
+        params.idCard = parseInt(params.idCard)
+        params.salaries = parseInt(params.salaries)
+        params.salaryNumber = parseInt(params.salaryNumber)
+        params.createUserId = params.createUserId.loginName
+        params.provinceCityDistrict = undefined
         await updatePersonnel(params).then((res) => {
             if (!res.ok) {
                 message.error(res.msg)
@@ -936,8 +854,6 @@ async function reset() {
         })
         await getInterviewEvaluation(id).then((res) => {
             setFVal('formData.evaluationDTOList', [res.data])
-            console.log(getFValue('formData'));
-
         })
     }
 }
@@ -952,8 +868,6 @@ onMounted(async () => {
         })
         await getInterviewEvaluation(id).then((res) => {
             setFVal('formData.evaluationDTOList', [res.data])
-            console.log(getFValue('formData'));
-
         })
     }
 })