|
|
@@ -32,102 +32,83 @@
|
|
|
</span>
|
|
|
|
|
|
<div v-if="section.name === 'base'" class="section-content">
|
|
|
- <header class="section-title"><div><h3>基本身份信息</h3><p>员工身份、户籍及个人资料</p></div><span>必填 {{ requiredProgress.base }}</span></header>
|
|
|
- <section class="profile-section">
|
|
|
- <div class="avatar-editor">
|
|
|
- <el-upload action="#" accept="image/*" :show-file-list="false" :before-upload="handleAvatarUpload">
|
|
|
- <div class="avatar-preview" :style="avatarStyle">
|
|
|
- <img v-if="form.avatar" :src="form.avatar" alt="员工照片" />
|
|
|
- <span v-else>{{ nameInitial }}</span>
|
|
|
- <div class="avatar-mask"><i class="el-icon-camera"></i></div>
|
|
|
- </div>
|
|
|
- </el-upload>
|
|
|
- <strong>员工照片</strong><small>JPG / PNG,2MB 以内</small>
|
|
|
- </div>
|
|
|
- <el-row :gutter="16" class="profile-fields">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="员工姓名" prop="name"><el-input v-model.trim="form.name" maxlength="20" placeholder="请输入员工姓名" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="员工编号" prop="employeeNo"><el-input v-model.trim="form.employeeNo" maxlength="20" placeholder="请输入员工编号"><el-button slot="append" :disabled="isEdit" @click="generateEmployeeNo">生成</el-button></el-input></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="性别" prop="gender"><el-radio-group v-model="form.gender" class="gender-group"><el-radio-button label="男"><i class="el-icon-male"></i> 男</el-radio-button><el-radio-button label="女"><i class="el-icon-female"></i> 女</el-radio-button></el-radio-group></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="出生日期" prop="birthDate"><el-date-picker v-model="form.birthDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择出生日期" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="身份证号" prop="idCard"><el-input v-model.trim="form.idCard" maxlength="18" placeholder="请输入身份证号" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="民族"><el-select v-model="form.ethnic" filterable class="full-width"><el-option v-for="item in ethnicOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="政治面貌"><el-select v-model="form.politicalStatus" class="full-width"><el-option v-for="item in politicalOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="婚姻状况"><el-select v-model="form.maritalStatus" class="full-width"><el-option v-for="item in maritalOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="籍贯"><el-input v-model.trim="form.nativePlace" maxlength="30" placeholder="省 / 市" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="户口性质"><el-select v-model="form.householdType" clearable class="full-width" placeholder="请选择"><el-option label="城镇" value="城镇" /><el-option label="农村" value="农村" /><el-option label="其他" value="其他" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="健康状况"><el-select v-model="form.healthStatus" class="full-width"><el-option label="优" value="优" /><el-option label="良" value="良" /><el-option label="差" value="差" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="个人证件有效期"><el-date-picker v-model="form.idCardExpiry" value-format="yyyy-MM-dd" type="date" placeholder="请选择有效期" class="full-width" /></el-form-item></el-col>
|
|
|
- </el-row>
|
|
|
- </section>
|
|
|
- </div>
|
|
|
+ <header class="section-title"><div><h3>基本信息</h3><p>员工身份、组织任职与联系方式,字段与档案模型一一对应</p></div><span>必填 {{ requiredProgress.base }}</span></header>
|
|
|
|
|
|
- <div v-else-if="section.name === 'employment'" class="section-content">
|
|
|
- <header class="section-title"><div><h3>组织与任职信息</h3><p>法人、工厂、部门、岗位及员工状态</p></div><span>必填 {{ requiredProgress.employment }}</span></header>
|
|
|
+ <div class="subsection-title"><strong>身份信息</strong></div>
|
|
|
<el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="所属组织" prop="deptId"><el-cascader v-model="form.deptId" :options="organizations" :props="organizationProps" filterable clearable class="full-width" placeholder="请选择公司或部门" @change="handleOrganizationChange" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="所属法人"><el-input v-model.trim="form.legalEntity" maxlength="40" placeholder="请输入所属法人" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="所属工厂"><el-input v-model.trim="form.factory" maxlength="40" placeholder="请输入或选择工厂" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="直属上级"><el-input v-model.trim="form.manager" maxlength="20" placeholder="请输入直属上级" prefix-icon="el-icon-user" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="岗位" prop="position"><el-input v-model.trim="form.position" maxlength="30" placeholder="请输入岗位名称" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="员工性质"><el-select v-model="form.positionType" clearable class="full-width" placeholder="请选择"><el-option v-for="item in ['正式','临时','兼职','编制']" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="职级" prop="level"><el-select v-model="form.level" filterable allow-create class="full-width" placeholder="请选择或输入"><el-option v-for="item in levelOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="编制级别"><el-select v-model="form.staffingLevel" clearable class="full-width" placeholder="请选择"><el-option v-for="item in staffingLevelOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="职责级别"><el-select v-model="form.dutyLevel" clearable class="full-width" placeholder="请选择"><el-option v-for="item in ['初级','中级','高级']" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="用工类型" prop="employmentType"><el-select v-model="form.employmentType" class="full-width"><el-option v-for="item in employmentOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="员工状态" prop="status"><el-select v-model="form.status" class="full-width"><el-option label="在职" value="active" /><el-option label="试用期" value="probation" /><el-option label="已离职" value="inactive" /><el-option label="停用" value="disabled" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="入职日期" prop="hireDate"><el-date-picker v-model="form.hireDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择入职日期" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="转正日期"><el-date-picker v-model="form.regularDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择转正日期" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="系统账号"><el-input v-model.trim="form.account" maxlength="30" placeholder="保存后可开通"><template slot="prepend">@</template></el-input></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="账号状态"><div class="enable-field"><el-switch v-model="form.enabled" active-color="#18a875" /><span>{{ form.enabled ? '启用' : '停用' }}</span></div></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="姓名" prop="name"><el-input v-model.trim="form.name" maxlength="20" placeholder="请输入员工真实姓名" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="性别" prop="gender"><el-select v-model="form.gender" class="full-width"><el-option v-for="item in genderOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="出生日期"><el-date-picker v-model="form.birthDate" value-format="yyyy-MM-dd" type="date" placeholder="可由身份证自动解析" class="full-width" @change="syncAge" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="年龄"><el-input :value="form.age || ''" readonly placeholder="自动计算" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="身份证"><el-input v-model.trim="form.idCard" maxlength="18" placeholder="18位身份证,自动回填出生日期" @blur="parseIdCard" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="民族"><el-select v-model="form.ethnic" filterable allow-create default-first-option class="full-width"><el-option v-for="item in ethnicOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="政治面貌"><el-input v-model.trim="form.politicalStatus" maxlength="20" placeholder="党员、预备党员、群众等" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="婚姻状态"><el-select v-model="form.maritalStatus" class="full-width"><el-option v-for="item in maritalOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="籍贯"><el-input v-model.trim="form.nativePlace" maxlength="30" placeholder="省 / 市" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="最高学历"><el-select v-model="form.education" class="full-width"><el-option v-for="item in educationOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="毕业院校"><el-input v-model.trim="form.school" maxlength="40" placeholder="请输入毕业院校" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="户籍地址"><el-input v-model.trim="form.registeredAddress" maxlength="100" placeholder="请输入户籍地址" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="现居住地址"><el-input v-model.trim="form.currentAddress" maxlength="100" placeholder="请输入现居住地址" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="工作地址"><el-input v-model.trim="form.workAddress" maxlength="60" placeholder="实际办公地点" /></el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <div class="subsection-title"><strong>组织与任职</strong></div>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="工号" prop="employeeNo"><el-input v-model.trim="form.employeeNo" maxlength="20" placeholder="集团全局唯一"><el-button slot="append" :disabled="isEdit" @click="generateEmployeeNo">生成</el-button></el-input></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="用户账号"><el-input v-model.trim="form.account" maxlength="30" placeholder="系统登录账号,支持自动生成、手动修改"><template slot="prepend">@</template></el-input></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="所属工厂"><el-select v-model="form.factory" filterable allow-create default-first-option clearable class="full-width" placeholder="请选择或输入工厂"><el-option v-for="item in factoryOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="所属机构" prop="groupId"><el-select v-model="form.groupId" filterable class="full-width" placeholder="请选择法人组织" @change="handleLegalEntityChange"><el-option v-for="item in legalEntities" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="隶属部门"><el-cascader v-model="form.deptId" :options="deptOptions" :props="organizationProps" filterable clearable class="full-width" placeholder="受所属机构过滤" @change="handleOrganizationChange" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="员工状态" prop="status"><el-select v-model="form.status" class="full-width"><el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="入职时间" prop="hireDate"><el-date-picker v-model="form.hireDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择入职日期" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="转正日期"><el-date-picker v-model="form.regularDate" value-format="yyyy-MM-dd" type="date" placeholder="转正生效日期" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="岗位" prop="position"><el-select v-model="form.positionId" filterable clearable class="full-width" placeholder="请选择岗位" @change="handlePositionChange"><el-option v-for="item in positionOptions" :key="item.id" :label="item.positionName + (item.positionSequence ? ' · ' + item.positionSequence : '')" :value="item.id" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="直属上级"><el-input v-model.trim="form.manager" maxlength="20" placeholder="选择系统内员工" prefix-icon="el-icon-user" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="用工形式"><el-select v-model="form.employmentType" class="full-width"><el-option v-for="item in employmentOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="编制级别"><el-select v-model="form.staffingLevel" class="full-width"><el-option v-for="item in staffingLevelOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="职责级别"><el-select v-model="form.dutyLevel" class="full-width"><el-option v-for="item in dutyLevelOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="关键工序"><el-input v-model.trim="form.keyProcess" maxlength="30" placeholder="标记特种工序岗位人员" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="是否启用"><div class="enable-field"><el-switch v-model="form.enabled" active-color="#18a875" /><span>{{ form.enabled ? '启用' : '停用' }}</span></div></el-form-item></el-col>
|
|
|
</el-row>
|
|
|
- </div>
|
|
|
|
|
|
- <div v-else-if="section.name === 'contact'" class="section-content">
|
|
|
- <header class="section-title"><div><h3>联系、地址与家庭</h3><p>日常联络、紧急联系人和家庭成员</p></div><span>必填 {{ requiredProgress.contact }}</span></header>
|
|
|
<div class="subsection-title"><strong>联系方式</strong></div>
|
|
|
<el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="手机号码" prop="phone"><el-input v-model.trim="form.phone" maxlength="11" placeholder="请输入 11 位手机号码" prefix-icon="el-icon-mobile-phone" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="企业邮箱" prop="email"><el-input v-model.trim="form.email" maxlength="50" placeholder="name@company.com" prefix-icon="el-icon-message" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="手机号" prop="phone"><el-input v-model.trim="form.phone" maxlength="11" placeholder="请输入 11 位手机号码" prefix-icon="el-icon-mobile-phone" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="邮箱"><el-input v-model.trim="form.email" maxlength="50" placeholder="name@company.com" prefix-icon="el-icon-message" /></el-form-item></el-col>
|
|
|
<el-col :xs="24" :sm="12"><el-form-item label="微信号"><el-input v-model.trim="form.wechat" maxlength="30" placeholder="请输入微信号" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="工作地点"><el-input v-model.trim="form.workAddress" maxlength="60" placeholder="请输入工作地点" /></el-form-item></el-col>
|
|
|
- <el-col :span="24"><el-form-item label="现居住地址"><el-input v-model.trim="form.currentAddress" maxlength="100" placeholder="请输入现居住地址" /></el-form-item></el-col>
|
|
|
- <el-col :span="24"><el-form-item label="户籍地址"><el-input v-model.trim="form.registeredAddress" maxlength="100" placeholder="请输入户籍地址" /></el-form-item></el-col>
|
|
|
<el-col :xs="24" :sm="12"><el-form-item label="紧急联系人"><el-input v-model.trim="form.emergencyContact" maxlength="20" placeholder="请输入联系人姓名" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="紧急联系电话" prop="emergencyPhone"><el-input v-model.trim="form.emergencyPhone" maxlength="11" placeholder="请输入联系电话" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="紧急联系电话"><el-input v-model.trim="form.emergencyPhone" maxlength="11" placeholder="请输入联系电话" /></el-form-item></el-col>
|
|
|
</el-row>
|
|
|
- <div class="subsection-title"><strong>家庭成员</strong><el-button type="text" icon="el-icon-plus" @click="addFamilyMember">添加成员</el-button></div>
|
|
|
- <div v-if="form.familyMembers.length" class="repeat-list compact-list">
|
|
|
- <div v-for="(item, index) in form.familyMembers" :key="`family-${index}`" class="repeat-row family-row">
|
|
|
- <el-input v-model.trim="item.name" maxlength="20" placeholder="姓名" />
|
|
|
- <el-select v-model="item.relation" placeholder="关系"><el-option v-for="option in ['配偶','父亲','母亲','子女','其他']" :key="option" :label="option" :value="option" /></el-select>
|
|
|
- <el-input v-model.trim="item.phone" maxlength="11" placeholder="联系电话" />
|
|
|
- <el-input v-model.trim="item.workUnit" maxlength="40" placeholder="工作单位" />
|
|
|
- <el-tooltip content="删除" placement="top"><el-button class="remove-button" icon="el-icon-delete" @click="removeItem('familyMembers', index)"></el-button></el-tooltip>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div v-else class="inline-empty">暂无家庭成员记录</div>
|
|
|
</div>
|
|
|
|
|
|
<div v-else-if="section.name === 'experience'" class="section-content">
|
|
|
<header class="section-title"><div><h3>教育与工作履历</h3><p>最高教育经历和入职前工作经历</p></div><span>{{ form.previousJobs.length }} 段履历</span></header>
|
|
|
- <div class="subsection-title"><strong>教育经历</strong></div>
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="最高学历"><el-select v-model="form.education" class="full-width"><el-option v-for="item in educationOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="学位"><el-select v-model="form.degree" clearable class="full-width" placeholder="请选择"><el-option v-for="item in ['无','学士','硕士','博士']" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="毕业日期"><el-date-picker v-model="form.graduationDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="毕业院校"><el-input v-model.trim="form.school" maxlength="40" placeholder="请输入毕业院校" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="所学专业"><el-input v-model.trim="form.major" maxlength="40" placeholder="请输入所学专业" /></el-form-item></el-col>
|
|
|
- </el-row>
|
|
|
+ <div class="subsection-title"><strong>教育经历</strong><el-button type="text" icon="el-icon-plus" @click="addEducation">添加教育</el-button></div>
|
|
|
+ <div v-if="form.educations.length" class="repeat-list">
|
|
|
+ <div v-for="(item, index) in form.educations" :key="`edu-${index}`" class="repeat-block">
|
|
|
+ <div class="repeat-block-heading"><strong>教育经历 {{ index + 1 }}</strong><el-button type="text" icon="el-icon-delete" @click="removeItem('educations', index)">删除</el-button></div>
|
|
|
+ <el-row :gutter="14">
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="最高学历"><el-select v-model="item.education" class="full-width"><el-option v-for="opt in educationOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="学位"><el-select v-model="item.degree" clearable class="full-width" placeholder="请选择"><el-option v-for="opt in ['无','学士','硕士','博士']" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="毕业日期"><el-date-picker v-model="item.graduationDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="毕业院校"><el-input v-model.trim="item.school" maxlength="40" placeholder="请输入毕业院校" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="所学专业"><el-input v-model.trim="item.major" maxlength="40" placeholder="请输入所学专业" /></el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="inline-empty">暂无教育经历</div>
|
|
|
<div class="subsection-title"><strong>工作履历</strong><el-button type="text" icon="el-icon-plus" @click="addPreviousJob">添加履历</el-button></div>
|
|
|
<div v-if="form.previousJobs.length" class="repeat-list">
|
|
|
<div v-for="(item, index) in form.previousJobs" :key="`job-${index}`" class="repeat-block">
|
|
|
<div class="repeat-block-heading"><strong>工作经历 {{ index + 1 }}</strong><el-button type="text" icon="el-icon-delete" @click="removeItem('previousJobs', index)">删除</el-button></div>
|
|
|
<el-row :gutter="14">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="起止日期"><el-date-picker v-model="item.dateRange" type="daterange" value-format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期" range-separator="至" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="前公司名称"><el-input v-model.trim="item.company" maxlength="50" placeholder="请输入公司名称" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="部门"><el-input v-model.trim="item.department" maxlength="30" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="岗位"><el-input v-model.trim="item.position" maxlength="30" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="离职原因"><el-input v-model.trim="item.leaveReason" maxlength="40" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="4"><el-form-item label="序号"><el-input :value="index + 1" readonly /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="5"><el-form-item label="工作开始日期" required><el-date-picker v-model="item.startDate" value-format="yyyy-MM-dd" type="date" placeholder="开始日期" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="5"><el-form-item label="工作结束日期" required><el-date-picker v-model="item.endDate" value-format="yyyy-MM-dd" type="date" placeholder="结束日期" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="5"><el-form-item label="前公司名称" required><el-input v-model.trim="item.company" maxlength="50" placeholder="请输入公司名称" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="5"><el-form-item label="前公司岗位" required><el-input v-model.trim="item.position" maxlength="30" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="6"><el-form-item label="前公司部门"><el-input v-model.trim="item.department" maxlength="30" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="6"><el-form-item label="离职原因"><el-input v-model.trim="item.leaveReason" maxlength="40" /></el-form-item></el-col>
|
|
|
<el-col :span="24"><el-form-item label="工作描述"><el-input v-model.trim="item.description" type="textarea" :rows="2" maxlength="300" show-word-limit /></el-form-item></el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
|
@@ -136,54 +117,112 @@
|
|
|
</div>
|
|
|
|
|
|
<div v-else-if="section.name === 'contract'" class="section-content">
|
|
|
- <header class="section-title"><div><h3>合同与薪酬银行</h3><p>劳动合同、薪资卡及发薪信息</p></div><span>{{ contractState }}</span></header>
|
|
|
+ <header class="section-title"><div><h3>合同与薪酬银行</h3><p>劳动合同、工资卡信息</p></div><span>{{ contractState }}</span></header>
|
|
|
<div class="subsection-title"><strong>劳动合同</strong></div>
|
|
|
<el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="合同编号"><el-input v-model.trim="form.contract.number" maxlength="30" placeholder="请输入合同编号" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="合同类型"><el-select v-model="form.contract.type" clearable class="full-width" placeholder="请选择"><el-option v-for="item in ['固定期限','无固定期限','以完成一定工作任务为期限','实习协议','劳务协议']" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="签署方式"><el-select v-model="form.contract.signType" clearable class="full-width" placeholder="请选择"><el-option label="线上签署" value="线上签署" /><el-option label="线下签署" value="线下签署" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="试用期(月)"><el-input-number v-model="form.contract.probationMonths" :min="0" :max="6" controls-position="right" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="合同开始日期"><el-date-picker v-model="form.contract.startDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="合同结束日期"><el-date-picker v-model="form.contract.endDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
- </el-row>
|
|
|
- <div class="subsection-title"><strong>薪酬与银行</strong></div>
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="工资开户行"><el-input v-model.trim="form.bankName" maxlength="50" placeholder="请输入银行及支行" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="银行卡号"><el-input v-model.trim="form.bankAccount" maxlength="24" placeholder="请输入工资卡号" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="开户姓名"><el-input v-model.trim="form.bankAccountName" maxlength="20" placeholder="默认与员工姓名一致" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="薪资核算类型"><el-select v-model="form.payrollType" clearable class="full-width" placeholder="请选择"><el-option label="月薪制" value="月薪制" /><el-option label="计时制" value="计时制" /><el-option label="计件制" value="计件制" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="分类"><el-select v-model="form.contract.category" clearable class="full-width" placeholder="请选择"><el-option v-for="item in contractTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="合同名称" required><el-input v-model.trim="form.contract.name" maxlength="60" placeholder="如:2026年度劳动合同" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="签订时间" required><el-date-picker v-model="form.contract.signDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="开始时间" required><el-date-picker v-model="form.contract.startDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="结束时间"><el-date-picker v-model="form.contract.endDate" value-format="yyyy-MM-dd" type="date" placeholder="无固定期限可留空" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="合同附件">
|
|
|
+ <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple :on-change="handleContractFile"><el-button size="small" icon="el-icon-upload2">上传附件</el-button></el-upload>
|
|
|
+ <span v-if="form.contract.files.length" class="file-list inline-file-list"><span v-for="(file, i) in form.contract.files" :key="i"><i class="el-icon-document"></i>{{ file }}<button type="button" @click="form.contract.files.splice(i, 1)"><i class="el-icon-close"></i></button></span></span>
|
|
|
+ </el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="生效状态" required><el-select v-model="form.contract.status" class="full-width"><el-option v-for="item in ['生效','已到期','已解除','已终止']" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :span="24"><el-form-item label="备注"><el-input v-model.trim="form.contract.remark" type="textarea" :rows="2" maxlength="300" placeholder="续签、变更、解除相关备注" /></el-form-item></el-col>
|
|
|
</el-row>
|
|
|
+ <div class="subsection-title"><strong>薪酬银行</strong><el-button type="text" icon="el-icon-plus" @click="addBankAccount">添加银行</el-button></div>
|
|
|
+ <div v-if="form.bankAccounts.length" class="repeat-list compact-list">
|
|
|
+ <div v-for="(item, index) in form.bankAccounts" :key="`bank-${index}`" class="repeat-row bank-row">
|
|
|
+ <el-input v-model.trim="item.bankName" maxlength="50" placeholder="开户行" />
|
|
|
+ <el-input v-model.trim="item.bankAccount" maxlength="24" placeholder="银行卡号" />
|
|
|
+ <el-input v-model.trim="item.bankAccountName" maxlength="20" placeholder="姓名(必填)" />
|
|
|
+ <el-tooltip content="删除" placement="top"><el-button class="remove-button" icon="el-icon-delete" @click="removeItem('bankAccounts', index)"></el-button></el-tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="inline-empty">暂无银行账户</div>
|
|
|
</div>
|
|
|
|
|
|
<div v-else-if="section.name === 'social'" class="section-content">
|
|
|
- <header class="section-title"><div><h3>社保、资质与附件</h3><p>参保信息、工种、技能证照和电子材料</p></div><span>{{ form.files.length }} 份附件</span></header>
|
|
|
- <div class="subsection-title"><strong>社保公积金</strong></div>
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="社保类型"><el-select v-model="form.socialSecurityType" clearable class="full-width" placeholder="请选择"><el-option v-for="item in socialSecurityOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="参保城市"><el-input v-model.trim="form.insuredCity" maxlength="30" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="参保日期"><el-date-picker v-model="form.insuredDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="停保日期"><el-date-picker v-model="form.socialSecurityStopDate" value-format="yyyy-MM-dd" type="date" placeholder="未停保" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="社保基数"><el-input-number v-model="form.socialSecurityBase" :min="0" :precision="2" :controls="false" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="公积金账号"><el-input v-model.trim="form.housingFundAccount" maxlength="30" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="公积金基数"><el-input-number v-model="form.housingFundBase" :min="0" :precision="2" :controls="false" class="full-width" /></el-form-item></el-col>
|
|
|
- </el-row>
|
|
|
- <div class="subsection-title"><strong>工种与能力</strong></div>
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="工种"><el-input v-model.trim="form.workType" maxlength="30" placeholder="如:电工、焊工、装配工" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="特种作业人员"><div class="enable-field"><el-switch v-model="form.specialOperator" active-color="#18a875" /><span>{{ form.specialOperator ? '是' : '否' }}</span></div></el-form-item></el-col>
|
|
|
- <el-col :span="24"><el-form-item label="技能标签"><el-select v-model="form.skills" multiple filterable allow-create default-first-option class="full-width" placeholder="输入技能后按回车"><el-option v-for="item in skillOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-col>
|
|
|
- </el-row>
|
|
|
+ <header class="section-title"><div><h3>社保、资质与技能</h3><p>社保公积金、工种、证照与技能列表</p></div><span>{{ form.socialSecurityRecords.length }} 条社保记录</span></header>
|
|
|
+
|
|
|
+ <div class="subsection-title"><strong>社保公积金</strong><el-button type="text" icon="el-icon-plus" @click="addSocialSecurity">添加记录</el-button></div>
|
|
|
+ <div v-if="form.socialSecurityRecords.length" class="repeat-list">
|
|
|
+ <div v-for="(item, index) in form.socialSecurityRecords" :key="`ss-${index}`" class="repeat-block">
|
|
|
+ <div class="repeat-block-heading"><strong>社保记录 {{ index + 1 }}</strong><el-button type="text" icon="el-icon-delete" @click="removeItem('socialSecurityRecords', index)">删除</el-button></div>
|
|
|
+ <el-row :gutter="14">
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="社保类型"><el-select v-model="item.type" clearable class="full-width"><el-option v-for="opt in socialSecurityOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="基数"><el-input-number v-model="item.base" :min="0" :precision="2" :controls="false" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="参保日期"><el-date-picker v-model="item.insuredDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="停保日期"><el-date-picker v-model="item.stopDate" value-format="yyyy-MM-dd" type="date" placeholder="在职可为空" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"><el-form-item label="停缴记录"><el-select v-model="item.stopReason" clearable class="full-width" placeholder="请选择"><el-option v-for="opt in ['离职','调转','其他']" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="inline-empty">暂无社保记录</div>
|
|
|
+
|
|
|
+ <div class="subsection-title"><strong>工种</strong><el-button type="text" icon="el-icon-plus" @click="addWorkType">添加工种</el-button></div>
|
|
|
+ <div v-if="form.workTypes.length" class="repeat-list">
|
|
|
+ <div v-for="(item, index) in form.workTypes" :key="`wt-${index}`" class="repeat-block">
|
|
|
+ <div class="repeat-block-heading"><strong>工种 {{ index + 1 }}</strong><el-button type="text" icon="el-icon-delete" @click="removeItem('workTypes', index)">删除</el-button></div>
|
|
|
+ <el-row :gutter="14">
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="类型"><el-select v-model="item.type" clearable class="full-width"><el-option v-for="opt in workTypeCategoryOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="编码"><el-input v-model.trim="item.code" maxlength="20" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="名称"><el-input v-model.trim="item.name" maxlength="30" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="等级"><el-select v-model="item.level" clearable class="full-width"><el-option v-for="opt in workTypeLevelOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="标准工时费"><el-input-number v-model="item.hourlyWage" :min="0" :precision="2" :controls="false" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="工时费单位"><el-select v-model="item.wageUnit" clearable class="full-width"><el-option v-for="opt in ['元/小时','元/天','元/件']" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="健康状况"><el-select v-model="item.healthStatus" clearable class="full-width"><el-option v-for="opt in ['优','良','一般','差']" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="是否特种作业人员"><el-select v-model="item.specialOperator" class="full-width"><el-option label="是" value="是" /><el-option label="否" value="否" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :span="24"><el-form-item label="证件照">
|
|
|
+ <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple :on-change="(f) => handleListFile(f, item, 'photo')"><el-button size="small" icon="el-icon-upload2">上传附件</el-button></el-upload>
|
|
|
+ <span v-if="item.photo && item.photo.length" class="file-list inline-file-list"><span v-for="(file, i) in item.photo" :key="i"><i class="el-icon-document"></i>{{ file }}<button type="button" @click="item.photo.splice(i, 1)"><i class="el-icon-close"></i></button></span></span>
|
|
|
+ </el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="inline-empty">暂无工种记录</div>
|
|
|
+
|
|
|
<div class="subsection-title"><strong>证照列表</strong><el-button type="text" icon="el-icon-plus" @click="addCertificate">添加证照</el-button></div>
|
|
|
- <div v-if="form.certificates.length" class="repeat-list compact-list">
|
|
|
- <div v-for="(item, index) in form.certificates" :key="`cert-${index}`" class="repeat-row certificate-row">
|
|
|
- <el-input v-model.trim="item.name" maxlength="40" placeholder="证照名称" />
|
|
|
- <el-input v-model.trim="item.number" maxlength="30" placeholder="证照编号" />
|
|
|
- <el-date-picker v-model="item.issueDate" value-format="yyyy-MM-dd" type="date" placeholder="取得日期" />
|
|
|
- <el-date-picker v-model="item.expiryDate" value-format="yyyy-MM-dd" type="date" placeholder="到期日期" />
|
|
|
- <el-tooltip content="删除" placement="top"><el-button class="remove-button" icon="el-icon-delete" @click="removeItem('certificates', index)"></el-button></el-tooltip>
|
|
|
+ <div v-if="form.certificates.length" class="repeat-list">
|
|
|
+ <div v-for="(item, index) in form.certificates" :key="`cert-${index}`" class="repeat-block">
|
|
|
+ <div class="repeat-block-heading"><strong>证照 {{ index + 1 }}</strong><el-button type="text" icon="el-icon-delete" @click="removeItem('certificates', index)">删除</el-button></div>
|
|
|
+ <el-row :gutter="14">
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="证照类型" required><el-select v-model="item.type" class="full-width"><el-option v-for="opt in certificateTypeOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="证照编号" required><el-input v-model.trim="item.number" maxlength="30" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="等级"><el-select v-model="item.level" clearable class="full-width"><el-option v-for="opt in certificateLevelOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="状态" required><el-select v-model="item.status" class="full-width"><el-option label="有效" value="有效" /><el-option label="失效" value="失效" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="有效期开始时间" required><el-date-picker v-model="item.startDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="有效期结束时间" required><el-date-picker v-model="item.endDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="颁布日期"><el-date-picker v-model="item.issueDate" value-format="yyyy-MM-dd" type="date" placeholder="请选择" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="证照附件">
|
|
|
+ <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple :on-change="(f) => handleListFile(f, item, 'attachment')"><el-button size="small" icon="el-icon-upload2">上传</el-button></el-upload>
|
|
|
+ </el-form-item></el-col>
|
|
|
+ <el-col :span="24"><el-form-item label="备注"><el-input v-model.trim="item.remark" maxlength="200" placeholder="补充说明信息" /></el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else class="inline-empty">暂无证照记录</div>
|
|
|
+
|
|
|
+ <div class="subsection-title"><strong>技能列表</strong><el-button type="text" icon="el-icon-plus" @click="addSkill">添加技能</el-button></div>
|
|
|
+ <div v-if="form.skills.length" class="repeat-list">
|
|
|
+ <div v-for="(item, index) in form.skills" :key="`skill-${index}`" class="repeat-block">
|
|
|
+ <div class="repeat-block-heading"><strong>技能 {{ index + 1 }}</strong><el-button type="text" icon="el-icon-delete" @click="removeItem('skills', index)">删除</el-button></div>
|
|
|
+ <el-row :gutter="14">
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="分类"><el-select v-model="item.category" clearable class="full-width"><el-option v-for="opt in skillCategoryOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="技能名称" required><el-input v-model.trim="item.name" maxlength="30" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="技能等级"><el-select v-model="item.level" clearable class="full-width"><el-option v-for="opt in skillLevelOptions" :key="opt" :label="opt" :value="opt" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="12" :sm="6"><el-form-item label="技能经验(年限)"><el-input-number v-model="item.years" :min="0" :precision="1" :controls="false" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="证照附件">
|
|
|
+ <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple :on-change="(f) => handleListFile(f, item, 'attachment')"><el-button size="small" icon="el-icon-upload2">上传</el-button></el-upload>
|
|
|
+ </el-form-item></el-col>
|
|
|
+ <el-col :span="24"><el-form-item label="备注"><el-input v-model.trim="item.remark" type="textarea" :rows="2" maxlength="200" placeholder="培训经历、考核情况" /></el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="inline-empty">暂无技能记录</div>
|
|
|
+
|
|
|
<div class="subsection-title attachment-title"><strong>档案附件</strong><el-upload action="#" :auto-upload="false" :show-file-list="false" multiple :on-change="handleFileChange"><el-button type="text" icon="el-icon-upload2">上传附件</el-button></el-upload></div>
|
|
|
<div v-if="form.files.length" class="file-list">
|
|
|
<span v-for="(file, index) in form.files" :key="`${file}-${index}`"><i class="el-icon-document"></i><strong>{{ file }}</strong><button type="button" aria-label="删除附件" @click="removeItem('files', index)"><i class="el-icon-close"></i></button></span>
|
|
|
@@ -192,7 +231,7 @@
|
|
|
</div>
|
|
|
|
|
|
<div v-else class="section-content record-section-content">
|
|
|
- <employee-archive-records :archive="form" />
|
|
|
+ <employee-archive-records ref="records" :archive="form" />
|
|
|
</div>
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
@@ -207,18 +246,27 @@
|
|
|
|
|
|
<script>
|
|
|
import EmployeeArchiveRecords from './EmployeeArchiveRecords.vue';
|
|
|
+import { getPositionPage } from '@/api/hr';
|
|
|
+import { listOrganizations } from '@/api/organization';
|
|
|
+import { buildOrganizationTree, flattenOrganizationTree } from '@/utils/organization-tree';
|
|
|
|
|
|
-const emptyContract = () => ({ number: '', type: '', signType: '', startDate: '', endDate: '', probationMonths: 3, status: '待签署' });
|
|
|
+const emptyContract = () => ({ category: '', name: '', signDate: '', startDate: '', endDate: '', files: [], status: '生效', remark: '' });
|
|
|
const emptyForm = () => ({
|
|
|
id: null, avatar: '', name: '', employeeNo: '', gender: '男', birthDate: '', age: null, idCard: '', idCardExpiry: '', ethnic: '汉族',
|
|
|
maritalStatus: '未婚', politicalStatus: '群众', nativePlace: '', householdType: '', healthStatus: '优',
|
|
|
- company: '中盈产业集团有限公司', legalEntity: '中盈产业集团有限公司', factory: '', deptId: '', deptName: '', position: '', positionType: '编制', level: '', staffingLevel: '一级', dutyLevel: '初级', manager: '',
|
|
|
- employmentType: '全职', status: 'probation', hireDate: '', regularDate: '', account: '', enabled: true,
|
|
|
- phone: '', email: '', wechat: '', workAddress: '长沙产业园 A 区', currentAddress: '', registeredAddress: '', emergencyContact: '', emergencyPhone: '', familyMembers: [],
|
|
|
- education: '本科', degree: '学士', school: '', major: '', graduationDate: '', previousJobs: [],
|
|
|
- contract: emptyContract(), bankName: '', bankAccount: '', bankAccountName: '', payrollType: '月薪制',
|
|
|
- socialSecurityType: '五险一金', insuredCity: '长沙', insuredDate: '', socialSecurityStopDate: '', socialSecurityBase: 0, housingFundAccount: '', housingFundBase: 0,
|
|
|
- workType: '', specialOperator: false, skills: [], certificates: [], files: [], remarks: '',
|
|
|
+ company: '中盈产业集团有限公司', legalEntity: '', groupId: null, factory: '', deptId: '', deptName: '', position: '', positionId: null, positionType: '编制', level: '', staffingLevel: '一级', dutyLevel: '初级', manager: '',
|
|
|
+ employmentType: '编制', status: '全职', hireDate: '', regularDate: '', account: '', enabled: true,
|
|
|
+ keyProcess: '',
|
|
|
+ phone: '', email: '', wechat: '', workAddress: '', currentAddress: '', registeredAddress: '', emergencyContact: '', emergencyPhone: '', familyMembers: [],
|
|
|
+ education: '本科', school: '',
|
|
|
+ educations: [],
|
|
|
+ previousJobs: [],
|
|
|
+ contract: emptyContract(), bankAccounts: [],
|
|
|
+ socialSecurityRecords: [],
|
|
|
+ workTypes: [],
|
|
|
+ certificates: [],
|
|
|
+ skills: [],
|
|
|
+ files: [], remarks: '',
|
|
|
attendanceRecords: [], performanceRecords: [], trainingRecords: [], rewardPunishmentRecords: [], awards: [], positionTransfers: [], contractChanges: [],
|
|
|
leaveInfo: { enabled: false, position: '', department: '', leaveDate: '', leaveType: '', leaveReason: '', serviceYears: '', handoverStatus: '' }
|
|
|
});
|
|
|
@@ -247,34 +295,45 @@ export default {
|
|
|
else callback();
|
|
|
};
|
|
|
return {
|
|
|
- activeTab: 'base', saving: false, form: emptyForm(),
|
|
|
+ activeTab: 'base', saving: false, form: emptyForm(), organizationTree: [],
|
|
|
sections: [
|
|
|
- { name: 'base', label: '基本身份', subtitle: '个人与户籍', icon: 'el-icon-user' },
|
|
|
- { name: 'employment', label: '任职信息', subtitle: '组织与岗位', icon: 'el-icon-office-building' },
|
|
|
- { name: 'contact', label: '联系与家庭', subtitle: '地址与联系人', icon: 'el-icon-phone-outline' },
|
|
|
+ { name: 'base', label: '基本信息', subtitle: '身份/任职/联系', icon: 'el-icon-user' },
|
|
|
{ name: 'experience', label: '教育与履历', subtitle: '学习与工作经历', icon: 'el-icon-notebook-2' },
|
|
|
{ name: 'contract', label: '合同与薪酬', subtitle: '合同与银行', icon: 'el-icon-document-checked' },
|
|
|
{ name: 'social', label: '社保与资质', subtitle: '证照与附件', icon: 'el-icon-medal' },
|
|
|
{ name: 'records', label: '业务记录', subtitle: '考勤绩效与异动', icon: 'el-icon-tickets' }
|
|
|
],
|
|
|
+ // 岗位设定列表(岗位下拉数据源)
|
|
|
+ positionOptions: [],
|
|
|
ethnicOptions: ['汉族', '土家族', '苗族', '侗族', '回族', '瑶族', '其他'],
|
|
|
- educationOptions: ['高中/中专', '大专', '本科', '硕士', '博士'],
|
|
|
- politicalOptions: ['群众', '共青团员', '中共党员', '民主党派', '其他'], maritalOptions: ['未婚', '已婚', '离异', '丧偶', '其他'],
|
|
|
- employmentOptions: ['全职', '兼职', '实习', '试用', '离职'], levelOptions: ['P1', 'P2', 'P3', 'P4', 'P5', 'M1', 'M2', 'M3', 'M4'],
|
|
|
- staffingLevelOptions: ['一级', '二级', '三级', '四级', '五级', '六级', '七级', '八级', '九级', '十级'],
|
|
|
+ genderOptions: ['男', '女', '未知'],
|
|
|
+ statusOptions: ['全职', '兼职', '实习', '劳务', '离职'],
|
|
|
+ maritalOptions: ['未婚', '已婚', '离异', '丧偶'],
|
|
|
+ educationOptions: ['小学', '初中', '高中', '大专', '本科', '硕士', '博士'],
|
|
|
+ employmentOptions: ['编制', '外包', '劳务派遣', '临时用工'],
|
|
|
+ staffingLevelOptions: ['一级', '二级', '三级'],
|
|
|
+ dutyLevelOptions: ['初级', '中级', '高级管理'],
|
|
|
+ factoryOptions: ['常德工厂', '长沙工厂', '湘潭工厂', '东莞工厂'],
|
|
|
socialSecurityOptions: ['四险', '四险一金', '五险', '五险一金', '六险一金'],
|
|
|
- skillOptions: ['Office 办公', '项目管理', '机械设计', '质量管理', '设备维护', '安全生产'],
|
|
|
+ contractTypeOptions: ['固定期限', '无固定期限', '以完成一定工作任务为期限', '劳务协议', '实习协议'],
|
|
|
+ workTypeCategoryOptions: ['维修', '装配', '检验', '机加', '焊接', '其他'],
|
|
|
+ workTypeLevelOptions: ['初级', '中级', '高级', '技师', '高级技师'],
|
|
|
+ certificateTypeOptions: ['身份证', '护照', '特种作业证', '职称证', '职业资格证', '其他'],
|
|
|
+ certificateLevelOptions: ['初级', '中级', '高级'],
|
|
|
+ skillCategoryOptions: ['专业技能', '办公技能', '语言技能', '特种操作技能', '其他'],
|
|
|
+ skillLevelOptions: ['了解', '熟练', '精通', '初级', '中级', '高级'],
|
|
|
organizationProps: { value: 'id', label: 'name', children: 'children', emitPath: false, checkStrictly: true },
|
|
|
rules: {
|
|
|
name: [{ required: true, message: '请输入员工姓名', trigger: 'blur' }],
|
|
|
- employeeNo: [{ required: true, message: '请输入员工编号', trigger: 'blur' }, { pattern: /^[A-Za-z0-9_-]{2,20}$/, message: '仅支持字母、数字、下划线和短横线', trigger: 'blur' }, { validator: validateNumber, trigger: 'blur' }],
|
|
|
- gender: [{ required: true, message: '请选择性别', trigger: 'change' }], birthDate: [{ required: true, message: '请选择出生日期', trigger: 'change' }],
|
|
|
- idCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }, { validator: validateIdCard, trigger: 'blur' }],
|
|
|
- deptId: [{ required: true, message: '请选择所属组织', trigger: 'change' }], position: [{ required: true, message: '请输入岗位名称', trigger: 'blur' }],
|
|
|
- level: [{ required: true, message: '请选择职级', trigger: 'change' }], employmentType: [{ required: true, message: '请选择用工类型', trigger: 'change' }],
|
|
|
- status: [{ required: true, message: '请选择员工状态', trigger: 'change' }], hireDate: [{ required: true, message: '请选择入职日期', trigger: 'change' }],
|
|
|
- phone: [{ required: true, message: '请输入手机号码', trigger: 'blur' }, { validator: validatePhone, trigger: 'blur' }],
|
|
|
- email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }], emergencyPhone: [{ validator: validatePhone, trigger: 'blur' }]
|
|
|
+ employeeNo: [{ required: true, message: '请输入工号', trigger: 'blur' }, { pattern: /^[A-Za-z0-9_-]{2,20}$/, message: '仅支持字母、数字、下划线和短横线', trigger: 'blur' }, { validator: validateNumber, trigger: 'blur' }],
|
|
|
+ gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
|
|
+ groupId: [{ required: true, message: '请选择所属机构', trigger: 'change' }],
|
|
|
+ status: [{ required: true, message: '请选择员工状态', trigger: 'change' }],
|
|
|
+ hireDate: [{ required: true, message: '请选择入职时间', trigger: 'change' }],
|
|
|
+ position: [{ required: true, message: '请选择岗位', trigger: 'change' }],
|
|
|
+ phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }, { validator: validatePhone, trigger: 'blur' }],
|
|
|
+ email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }],
|
|
|
+ idCard: [{ validator: validateIdCard, trigger: 'blur' }]
|
|
|
}
|
|
|
};
|
|
|
},
|
|
|
@@ -284,25 +343,46 @@ export default {
|
|
|
nameInitial() { return (this.form.name || '新').slice(-1); },
|
|
|
avatarStyle() { const tones = ['#1768e5', '#13a471', '#7c5ce7', '#d08716', '#d25567']; const code = [...(this.form.name || '新')].reduce((sum, char) => sum + char.charCodeAt(0), 0); return { '--avatar-color': tones[code % tones.length] }; },
|
|
|
currentSectionLabel() { return this.sections.find((item) => item.name === this.activeTab)?.label || ''; },
|
|
|
+ // 所属机构:法人组织(集团/公司),来源真实机构接口 getGroupList
|
|
|
+ legalEntities() {
|
|
|
+ const tree = this.organizationTree.length ? this.organizationTree : this.organizations;
|
|
|
+ const isLegalEntity = (item) => ['10', '20', 'group', 'company'].includes(String(item.type));
|
|
|
+ return flattenOrganizationTree(tree || []).filter(isLegalEntity);
|
|
|
+ },
|
|
|
+ // 隶属部门:受所属机构过滤的子树
|
|
|
+ deptOptions() {
|
|
|
+ const tree = this.organizationTree.length ? this.organizationTree : this.organizations;
|
|
|
+ if (!this.form.groupId) return tree || [];
|
|
|
+ const find = (nodes) => {
|
|
|
+ for (const node of nodes) {
|
|
|
+ if (String(node.id) === String(this.form.groupId)) return node;
|
|
|
+ const matched = find(node.children || []);
|
|
|
+ if (matched) return matched;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+ const selected = find(tree || []);
|
|
|
+ return selected ? [selected] : (tree || []);
|
|
|
+ },
|
|
|
requiredProgress() {
|
|
|
- const progress = (keys) => `${keys.filter((key) => this.form[key]).length}/${keys.length}`;
|
|
|
- return { base: progress(['name', 'employeeNo', 'gender', 'birthDate', 'idCard']), employment: progress(['deptId', 'position', 'level', 'employmentType', 'status', 'hireDate']), contact: progress(['phone']) };
|
|
|
+ const keys = ['name', 'employeeNo', 'gender', 'groupId', 'status', 'hireDate', 'position', 'phone'];
|
|
|
+ return { base: `${keys.filter((key) => this.form[key]).length}/${keys.length}` };
|
|
|
},
|
|
|
sectionProgress() {
|
|
|
const count = (keys) => `${keys.filter((key) => { const value = this.form[key]; return Array.isArray(value) ? value.length : Boolean(value); }).length}/${keys.length}`;
|
|
|
return {
|
|
|
- base: count(['name', 'employeeNo', 'gender', 'birthDate', 'idCard', 'ethnic', 'nativePlace', 'householdType']), employment: count(['deptId', 'position', 'level', 'manager', 'hireDate', 'employmentType']),
|
|
|
- contact: count(['phone', 'email', 'currentAddress', 'emergencyContact']), experience: count(['education', 'degree', 'school', 'major', 'previousJobs']),
|
|
|
+ base: count(['name', 'employeeNo', 'groupId', 'factory', 'deptId', 'status', 'hireDate', 'position', 'employmentType', 'staffingLevel', 'dutyLevel', 'gender', 'birthDate', 'idCard', 'ethnic', 'politicalStatus', 'maritalStatus', 'nativePlace', 'education', 'school', 'phone', 'email', 'wechat', 'currentAddress', 'registeredAddress', 'workAddress', 'emergencyContact', 'emergencyPhone', 'keyProcess']),
|
|
|
+ experience: count(['educations', 'previousJobs']),
|
|
|
contract: `${[this.form.contract.number, this.form.contract.type, this.form.bankName, this.form.bankAccount].filter(Boolean).length}/4`,
|
|
|
- social: count(['socialSecurityType', 'insuredDate', 'workType', 'skills', 'certificates', 'files']),
|
|
|
+ social: count(['socialSecurityRecords', 'workTypes', 'certificates', 'skills', 'files']),
|
|
|
records: `${['attendanceRecords', 'performanceRecords', 'trainingRecords', 'rewardPunishmentRecords', 'awards', 'positionTransfers', 'contractChanges'].reduce((sum, key) => sum + this.form[key].length, 0) + (this.form.leaveInfo.enabled ? 1 : 0)}`
|
|
|
};
|
|
|
},
|
|
|
completionRate() {
|
|
|
- const values = [this.form.name, this.form.employeeNo, this.form.birthDate, this.form.idCard, this.form.deptId, this.form.position, this.form.level, this.form.hireDate, this.form.phone, this.form.email, this.form.currentAddress, this.form.education, this.form.school, this.form.contract.number, this.form.bankAccount, this.form.insuredDate, this.form.skills.length, this.form.files.length];
|
|
|
+ const values = [this.form.name, this.form.employeeNo, this.form.groupId, this.form.position, this.form.status, this.form.hireDate, this.form.phone, this.form.gender, this.form.email, this.form.birthDate, this.form.education, this.form.school, this.form.currentAddress, this.form.contract.number, this.form.bankAccount, this.form.insuredDate, this.form.skills.length, this.form.files.length];
|
|
|
return Math.round(values.filter(Boolean).length / values.length * 100);
|
|
|
},
|
|
|
- contractState() { return this.form.contract.number ? (this.form.contract.endDate ? '合同资料已填写' : '待补结束日期') : '待录入合同'; }
|
|
|
+ contractState() { return this.form.contract.name ? (this.form.contract.endDate ? '合同资料已填写' : '待补结束日期') : '待录入合同'; }
|
|
|
},
|
|
|
watch: { visible(value) { if (value) this.loadForm(); } },
|
|
|
methods: {
|
|
|
@@ -310,65 +390,205 @@ export default {
|
|
|
this.activeTab = 'base';
|
|
|
const source = this.employee ? JSON.parse(JSON.stringify(this.employee)) : {};
|
|
|
const empty = emptyForm();
|
|
|
+ const statusMap = { active: '全职', probation: '全职', inactive: '离职', disabled: '离职' };
|
|
|
this.form = {
|
|
|
...empty, ...source,
|
|
|
+ groupId: source.groupId ?? source.legalEntityId ?? null,
|
|
|
+ status: statusMap[source.status] || source.status || '全职',
|
|
|
contract: { ...emptyContract(), ...(source.contract || {}) },
|
|
|
leaveInfo: { ...empty.leaveInfo, ...(source.leaveInfo || {}) },
|
|
|
- familyMembers: source.familyMembers || [], previousJobs: source.previousJobs || [], skills: source.skills || [], files: source.files || [],
|
|
|
- certificates: (source.certificates || []).map((item) => ({ ...item, issueDate: item.issueDate || item.date || '', expiryDate: item.expiryDate || '' })),
|
|
|
+ familyMembers: source.familyMembers || [], files: source.files || [],
|
|
|
+ // 教育经历:兼容旧的单条 education/degree/school 字段
|
|
|
+ educations: source.educations?.length
|
|
|
+ ? source.educations
|
|
|
+ : (source.education
|
|
|
+ ? [{ education: source.education || '本科', degree: source.degree || '', graduationDate: source.graduationDate || '', school: source.school || '', major: source.major || '' }]
|
|
|
+ : []),
|
|
|
+ // 薪酬银行:兼容旧的单条 bankName/bankAccount 字段
|
|
|
+ bankAccounts: source.bankAccounts?.length
|
|
|
+ ? source.bankAccounts
|
|
|
+ : (source.bankName || source.bankAccount
|
|
|
+ ? [{ bankName: source.bankName || '', bankAccount: source.bankAccount || '', bankAccountName: source.bankAccountName || '' }]
|
|
|
+ : []),
|
|
|
+ // 工作履历:兼容旧的 dateRange
|
|
|
+ previousJobs: (source.previousJobs || []).map((item) => ({
|
|
|
+ ...item,
|
|
|
+ startDate: item.startDate || item.dateRange?.[0] || '',
|
|
|
+ endDate: item.endDate || item.dateRange?.[1] || '',
|
|
|
+ })),
|
|
|
+ // 技能:兼容旧字符串数组
|
|
|
+ skills: (source.skills || []).map((item) => (typeof item === 'string' ? { name: item, category: '', level: '', years: 0, attachment: [], remark: '' } : { ...item, attachment: item.attachment || [] })),
|
|
|
+ certificates: (source.certificates || []).map((item) => ({ ...item, attachment: item.attachment || [] })),
|
|
|
+ socialSecurityRecords: source.socialSecurityRecords || [],
|
|
|
+ workTypes: (source.workTypes || []).map((item) => ({ ...item, photo: item.photo || [] })),
|
|
|
attendanceRecords: source.attendanceRecords || [], performanceRecords: source.performanceRecords || [], trainingRecords: source.trainingRecords || [],
|
|
|
rewardPunishmentRecords: source.rewardPunishmentRecords || [], awards: source.awards || [], positionTransfers: source.positionTransfers || [], contractChanges: source.contractChanges || []
|
|
|
};
|
|
|
+ this.loadPositions();
|
|
|
+ this.loadOrganizations();
|
|
|
+ this.syncAge();
|
|
|
if (!this.employee) this.generateEmployeeNo();
|
|
|
this.$nextTick(() => this.$refs.form?.clearValidate());
|
|
|
},
|
|
|
resetForm() { this.form = emptyForm(); this.activeTab = 'base'; this.saving = false; },
|
|
|
generateEmployeeNo() { const maxNo = this.existingNumbers.reduce((max, item) => Math.max(max, Number(String(item).match(/\d+/)?.[0] || 0)), 0); this.form.employeeNo = `ZY${String(maxNo + 1).padStart(4, '0')}`; this.$nextTick(() => this.$refs.form?.clearValidate('employeeNo')); },
|
|
|
handleOrganizationChange(id) {
|
|
|
- const find = (nodes) => { for (const node of nodes) { if (node.id === id) return node; const result = find(node.children || []); if (result) return result; } return null; };
|
|
|
- const selected = find(this.organizations); if (selected) { this.form.deptName = selected.name; if (selected.type === 'company' || selected.type === 'group') { this.form.company = selected.name; this.form.legalEntity = selected.name; } }
|
|
|
+ const find = (nodes) => { for (const node of nodes) { if (String(node.id) === String(id)) return node; const result = find(node.children || []); if (result) return result; } return null; };
|
|
|
+ const tree = this.organizationTree.length ? this.organizationTree : this.organizations;
|
|
|
+ const selected = find(tree || []);
|
|
|
+ if (selected) {
|
|
|
+ this.form.deptName = selected.name;
|
|
|
+ if (['10', '20', 'group', 'company'].includes(String(selected.type))) {
|
|
|
+ this.form.company = selected.name;
|
|
|
+ this.form.legalEntity = selected.name;
|
|
|
+ this.form.groupId = selected.id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleLegalEntityChange(id) {
|
|
|
+ const selected = this.legalEntities.find((item) => String(item.id) === String(id));
|
|
|
+ this.form.legalEntity = selected?.name || '';
|
|
|
+ this.form.company = selected?.name || this.form.company;
|
|
|
+ // 所属机构变化,部门归属随之变化,清空已选部门
|
|
|
+ this.form.deptId = '';
|
|
|
+ this.form.deptName = '';
|
|
|
+ },
|
|
|
+ // 加载真实机构数据(/main/group/getGroupList),用于所属机构与隶属部门
|
|
|
+ async loadOrganizations() {
|
|
|
+ try {
|
|
|
+ const list = await listOrganizations();
|
|
|
+ this.organizationTree = buildOrganizationTree(Array.isArray(list) ? list : []);
|
|
|
+ } catch (error) {
|
|
|
+ this.organizationTree = [];
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handlePositionChange(id) {
|
|
|
+ const selected = this.positionOptions.find((item) => String(item.id) === String(id));
|
|
|
+ this.form.position = selected?.positionName || '';
|
|
|
},
|
|
|
handleAvatarUpload(file) { if (file.size > 2 * 1024 * 1024) { this.$message.warning('员工照片不能超过 2MB'); return false; } const reader = new FileReader(); reader.onload = (event) => { this.form.avatar = event.target.result; }; reader.readAsDataURL(file); return false; },
|
|
|
handleFileChange(file) { if (file.size > 10 * 1024 * 1024) { this.$message.warning('单个附件不能超过 10MB'); return; } if (!this.form.files.includes(file.name)) this.form.files.push(file.name); else this.$message.info('该附件已添加'); },
|
|
|
- addFamilyMember() { this.form.familyMembers.push({ name: '', relation: '', phone: '', workUnit: '' }); },
|
|
|
- addPreviousJob() { this.form.previousJobs.push({ dateRange: [], company: '', department: '', position: '', leaveReason: '', description: '' }); },
|
|
|
- addCertificate() { this.form.certificates.push({ name: '', number: '', issueDate: '', expiryDate: '', status: '有效' }); },
|
|
|
+ addEducation() { this.form.educations.push({ education: '本科', degree: '学士', graduationDate: '', school: '', major: '' }); },
|
|
|
+ addBankAccount() { this.form.bankAccounts.push({ bankName: '', bankAccount: '', bankAccountName: '' }); },
|
|
|
+ addPreviousJob() { this.form.previousJobs.push({ startDate: '', endDate: '', company: '', position: '', department: '', leaveReason: '', description: '' }); },
|
|
|
+ addSocialSecurity() { this.form.socialSecurityRecords.push({ type: '五险一金', base: 0, insuredDate: '', stopDate: '', stopReason: '' }); },
|
|
|
+ addWorkType() { this.form.workTypes.push({ type: '', code: '', name: '', level: '', hourlyWage: 0, wageUnit: '元/小时', healthStatus: '优', specialOperator: '否', photo: [] }); },
|
|
|
+ addCertificate() { this.form.certificates.push({ type: '', number: '', level: '', status: '有效', startDate: '', endDate: '', issueDate: '', attachment: [], remark: '' }); },
|
|
|
+ addSkill() { this.form.skills.push({ category: '', name: '', level: '', years: 0, attachment: [], remark: '' }); },
|
|
|
removeItem(key, index) { this.form[key].splice(index, 1); },
|
|
|
+ // 列表行内附件上传(把文件名存入对应字段)
|
|
|
+ handleListFile(file, item, key) {
|
|
|
+ if (!Array.isArray(item[key])) this.$set(item, key, []);
|
|
|
+ if (file.name && !item[key].includes(file.name)) item[key].push(file.name);
|
|
|
+ },
|
|
|
+ // 合同附件上传
|
|
|
+ handleContractFile(file) {
|
|
|
+ if (file.name && !this.form.contract.files.includes(file.name)) this.form.contract.files.push(file.name);
|
|
|
+ },
|
|
|
calculateAge(date) { if (!date) return null; const birthday = new Date(date); const today = new Date(); let age = today.getFullYear() - birthday.getFullYear(); if (today.getMonth() < birthday.getMonth() || (today.getMonth() === birthday.getMonth() && today.getDate() < birthday.getDate())) age -= 1; return age; },
|
|
|
- firstInvalidTab(fields) { const names = Object.keys(fields || {}); if (names.some((name) => ['name', 'employeeNo', 'gender', 'birthDate', 'idCard'].includes(name))) return 'base'; if (names.some((name) => ['deptId', 'position', 'level', 'employmentType', 'status', 'hireDate'].includes(name))) return 'employment'; return 'contact'; },
|
|
|
- validateDateLogic() {
|
|
|
- const checks = [
|
|
|
- [this.form.birthDate, this.form.hireDate, '入职日期不能早于出生日期', 'employment'],
|
|
|
- [this.form.hireDate, this.form.regularDate, '转正日期不能早于入职日期', 'employment'],
|
|
|
- [this.form.contract.startDate, this.form.contract.endDate, '合同结束日期不能早于开始日期', 'contract'],
|
|
|
- [this.form.insuredDate, this.form.socialSecurityStopDate, '停保日期不能早于参保日期', 'social']
|
|
|
- ];
|
|
|
- const invalid = checks.find(([start, end]) => start && end && start > end);
|
|
|
- if (!invalid) return true;
|
|
|
- this.activeTab = invalid[3];
|
|
|
- this.$message.warning(invalid[2]);
|
|
|
- return false;
|
|
|
+ // 出生日期变化 → 自动计算年龄(只读)
|
|
|
+ syncAge() { this.form.age = this.calculateAge(this.form.birthDate); },
|
|
|
+ // 身份证号 → 自动回填出生日期与年龄
|
|
|
+ parseIdCard() {
|
|
|
+ const value = String(this.form.idCard || '').trim();
|
|
|
+ if (!/^\d{17}[\dXx]$/.test(value)) return;
|
|
|
+ const birth = `${value.slice(6, 10)}-${value.slice(10, 12)}-${value.slice(12, 14)}`;
|
|
|
+ if (!this.form.birthDate) this.form.birthDate = birth;
|
|
|
+ this.form.age = this.calculateAge(birth);
|
|
|
+ },
|
|
|
+ // 岗位设定列表(岗位下拉数据源)
|
|
|
+ async loadPositions() {
|
|
|
+ try {
|
|
|
+ const data = await getPositionPage({ pageNum: 1, size: 1000, status: 1 });
|
|
|
+ this.positionOptions = (data?.list || []).filter((item) => item.positionName);
|
|
|
+ } catch (error) {
|
|
|
+ this.positionOptions = [];
|
|
|
+ }
|
|
|
},
|
|
|
- submit() {
|
|
|
- this.$refs.form.validate((valid, fields) => {
|
|
|
- if (!valid) { this.activeTab = this.firstInvalidTab(fields); this.$message.warning('请完善标记的必填信息'); return; }
|
|
|
- if (!this.validateDateLogic()) return;
|
|
|
- this.saving = true;
|
|
|
- const today = new Date().toISOString().slice(0, 10);
|
|
|
- const certificates = this.form.certificates.filter((item) => item.name).map((item) => ({ ...item, date: item.issueDate, status: item.expiryDate && item.expiryDate < new Date().toISOString().slice(0, 10) ? '已到期' : '有效' }));
|
|
|
- const contractStatus = !this.form.contract.number ? '待签署' : this.form.contract.endDate && this.form.contract.endDate < today ? '已到期' : '生效中';
|
|
|
- const contract = { ...this.form.contract, status: contractStatus };
|
|
|
- const leaveEnabled = this.form.leaveInfo.enabled && Boolean(this.form.leaveInfo.leaveDate);
|
|
|
- const awards = this.form.awards.filter((item) => item.name).map((item) => ({ ...item, level: item.level || '公司级' }));
|
|
|
- const payload = {
|
|
|
- ...this.form, certificates, contract, awards,
|
|
|
- status: leaveEnabled ? 'inactive' : this.form.status,
|
|
|
- employmentType: leaveEnabled ? '离职' : this.form.employmentType,
|
|
|
- age: this.calculateAge(this.form.birthDate),
|
|
|
- bankAccountName: this.form.bankAccountName || this.form.name
|
|
|
- };
|
|
|
- setTimeout(() => { this.$emit('save', payload); this.saving = false; this.dialogVisible = false; }, 350);
|
|
|
+ // 保存前整体校验:基础必填 + 各分区必填 + 日期逻辑 + 记录分区,校验不过跳转到对应 tab
|
|
|
+ async validateSections() {
|
|
|
+ // 1. 基础 el-form(base tab 必填)
|
|
|
+ const formValid = await new Promise((resolve) => {
|
|
|
+ this.$refs.form.validate((valid) => resolve(valid));
|
|
|
});
|
|
|
+ if (!formValid) {
|
|
|
+ this.activeTab = 'base';
|
|
|
+ this.$message.warning('请完善基本信息中标记的必填项');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 2. 各分区重复列表必填/日期逻辑
|
|
|
+ const sectionInvalid = this.checkRepeatLists();
|
|
|
+ if (sectionInvalid) {
|
|
|
+ this.activeTab = sectionInvalid.tab;
|
|
|
+ this.$message.warning(sectionInvalid.message);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 3. 记录分区(考勤/绩效/培训/奖惩)
|
|
|
+ const recordsResult = this.$refs.records?.validateRecords?.();
|
|
|
+ if (recordsResult && !recordsResult.valid) {
|
|
|
+ this.activeTab = 'records';
|
|
|
+ this.$message.warning(recordsResult.message);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ // 校验各分区重复列表必填与日期逻辑,返回首个不满足项 { tab, message } 或 null
|
|
|
+ checkRepeatLists() {
|
|
|
+ const gt = (start, end) => start && end && start > end;
|
|
|
+ // 日期逻辑(基本信息)
|
|
|
+ if (gt(this.form.birthDate, this.form.hireDate)) return { tab: 'base', message: '入职日期不能早于出生日期' };
|
|
|
+ if (gt(this.form.hireDate, this.form.regularDate)) return { tab: 'base', message: '转正日期不能早于入职日期' };
|
|
|
+ // 工作履历
|
|
|
+ for (const item of this.form.previousJobs || []) {
|
|
|
+ if (!item.startDate || !item.endDate || !item.company || !item.position) {
|
|
|
+ return { tab: 'experience', message: '工作履历的必填项未完善(工作开始/结束日期、前公司名称、前公司岗位)' };
|
|
|
+ }
|
|
|
+ if (gt(item.startDate, item.endDate)) return { tab: 'experience', message: '工作结束日期必须晚于开始日期' };
|
|
|
+ }
|
|
|
+ // 劳动合同:结束≥开始
|
|
|
+ if (gt(this.form.contract.startDate, this.form.contract.endDate)) return { tab: 'contract', message: '合同结束日期不能早于开始日期' };
|
|
|
+ // 薪酬银行:姓名必填
|
|
|
+ for (const item of this.form.bankAccounts || []) {
|
|
|
+ if (!item.bankAccountName) return { tab: 'contract', message: '薪酬银行的姓名(开户人)必填' };
|
|
|
+ }
|
|
|
+ // 社保记录:停保≥参保
|
|
|
+ for (const item of this.form.socialSecurityRecords || []) {
|
|
|
+ if (gt(item.insuredDate, item.stopDate)) return { tab: 'social', message: '社保停保日期不能早于参保日期' };
|
|
|
+ }
|
|
|
+ // 证照:类型/编号/状态/有效期必填,有效期结束≥开始
|
|
|
+ for (const item of this.form.certificates || []) {
|
|
|
+ if (!item.type || !item.number || !item.status || !item.startDate || !item.endDate) {
|
|
|
+ return { tab: 'social', message: '证照的必填项未完善(证照类型、证照编号、状态、有效期起止)' };
|
|
|
+ }
|
|
|
+ if (gt(item.startDate, item.endDate)) return { tab: 'social', message: '证照有效期结束时间不能早于开始时间' };
|
|
|
+ }
|
|
|
+ // 技能:技能名称必填
|
|
|
+ for (const item of this.form.skills || []) {
|
|
|
+ if (!item.name) return { tab: 'social', message: '技能名称必填' };
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+ async submit() {
|
|
|
+ const ok = await this.validateSections();
|
|
|
+ if (!ok) return;
|
|
|
+ this.saving = true;
|
|
|
+ const today = new Date().toISOString().slice(0, 10);
|
|
|
+ // 证照:过滤未选类型的行,有效期已过自动置为失效
|
|
|
+ const certificates = this.form.certificates.filter((item) => item.type).map((item) => ({ ...item, status: item.endDate && item.endDate < today ? '失效' : (item.status || '有效') }));
|
|
|
+ const contract = { ...this.form.contract };
|
|
|
+ const leaveEnabled = this.form.leaveInfo.enabled && Boolean(this.form.leaveInfo.leaveDate);
|
|
|
+ const awards = this.form.awards.filter((item) => item.name).map((item) => ({ ...item, level: item.level || '公司级' }));
|
|
|
+ // 银行开户姓名默认与员工姓名一致
|
|
|
+ const bankAccounts = (this.form.bankAccounts || []).map((item, i) => ({
|
|
|
+ ...item,
|
|
|
+ bankAccountName: item.bankAccountName || (i === 0 ? this.form.name : ''),
|
|
|
+ }));
|
|
|
+ const payload = {
|
|
|
+ ...this.form, certificates, contract, awards, bankAccounts,
|
|
|
+ status: leaveEnabled ? '离职' : this.form.status,
|
|
|
+ age: this.calculateAge(this.form.birthDate)
|
|
|
+ };
|
|
|
+ setTimeout(() => { this.$emit('save', payload); this.saving = false; this.dialogVisible = false; }, 350);
|
|
|
}
|
|
|
}
|
|
|
};
|