|
|
@@ -13,8 +13,12 @@
|
|
|
<div class="dialog-intro">
|
|
|
<div class="intro-icon"><i class="el-icon-collection-tag"></i></div>
|
|
|
<div>
|
|
|
- <strong>{{ isUpdate ? '维护岗位说明书与联动规则' : '创建标准化岗位' }}</strong>
|
|
|
- <p>岗位档案将承载定岗、定薪、定责、技能资质与晋升路径,保存后同步到人力资源岗位主档。</p>
|
|
|
+ <strong>{{
|
|
|
+ isUpdate ? "维护岗位说明书与联动规则" : "创建标准化岗位"
|
|
|
+ }}</strong>
|
|
|
+ <p>
|
|
|
+ 岗位档案将承载定岗、定薪、定责、技能资质与晋升路径,保存后同步到人力资源岗位主档。
|
|
|
+ </p>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -24,45 +28,101 @@
|
|
|
<el-row :gutter="18">
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="岗位编码" prop="code">
|
|
|
- <el-input v-model.trim="form.code" maxlength="32" placeholder="自动生成或手动输入">
|
|
|
+ <el-input
|
|
|
+ v-model.trim="form.code"
|
|
|
+ maxlength="32"
|
|
|
+ placeholder="自动生成或手动输入"
|
|
|
+ >
|
|
|
<i slot="prefix" class="el-icon-cpu"></i>
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="岗位名称" prop="name">
|
|
|
- <el-input v-model.trim="form.name" maxlength="64" placeholder="如:数控车床操作工">
|
|
|
+ <el-input
|
|
|
+ v-model.trim="form.name"
|
|
|
+ maxlength="64"
|
|
|
+ placeholder="如:数控车床操作工"
|
|
|
+ >
|
|
|
<i slot="prefix" class="el-icon-suitcase"></i>
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="所属法人" prop="legalId">
|
|
|
- <el-select v-model="form.legalId" class="full-width" placeholder="请选择法人" @change="handleLegalChange">
|
|
|
- <el-option v-for="item in legalEntities" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
+ <el-select
|
|
|
+ v-model="form.legalId"
|
|
|
+ class="full-width"
|
|
|
+ placeholder="请选择法人"
|
|
|
+ @change="handleLegalChange"
|
|
|
+ >
|
|
|
+ <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" :md="8">
|
|
|
<el-form-item label="所属部门" prop="deptId">
|
|
|
- <el-select v-model="form.deptId" filterable class="full-width" placeholder="请选择部门">
|
|
|
- <el-option v-for="item in departmentOptions" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
+ <el-select
|
|
|
+ v-model="form.deptId"
|
|
|
+ filterable
|
|
|
+ class="full-width"
|
|
|
+ placeholder="请选择部门"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in departmentOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="岗位序列" prop="seq">
|
|
|
- <el-radio-group v-model="form.seq" class="seq-radio" @change="handleSequenceChange">
|
|
|
- <el-radio-button v-for="item in sequenceOptions" :key="item.id" :label="item.id">{{ item.label.replace('序列', '') }}</el-radio-button>
|
|
|
+ <el-radio-group
|
|
|
+ v-model="form.seq"
|
|
|
+ class="seq-radio"
|
|
|
+ @change="handleSequenceChange"
|
|
|
+ >
|
|
|
+ <el-radio-button
|
|
|
+ v-for="item in sequenceOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.id"
|
|
|
+ >{{ item.label.replace("序列", "") }}</el-radio-button
|
|
|
+ >
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="岗位层级" prop="level">
|
|
|
- <el-select v-if="configuredLevelOptions.length" v-model="form.level" class="full-width" placeholder="请选择岗位层级" @change="refreshCode">
|
|
|
- <el-option v-for="item in configuredLevelOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ <el-select
|
|
|
+ v-if="configuredLevelOptions.length"
|
|
|
+ v-model="form.level"
|
|
|
+ class="full-width"
|
|
|
+ placeholder="请选择岗位层级"
|
|
|
+ @change="handleLevelChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in configuredLevelOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
</el-select>
|
|
|
- <el-input-number v-else v-model="form.level" :min="1" :max="20" controls-position="right" class="full-width" @change="refreshCode" />
|
|
|
+ <el-input-number
|
|
|
+ v-else
|
|
|
+ v-model="form.level"
|
|
|
+ :min="1"
|
|
|
+ :max="20"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width"
|
|
|
+ @change="handleLevelChange"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
@@ -77,12 +137,25 @@
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="岗位编制" prop="staffing">
|
|
|
- <el-input-number v-model="form.staffing" :min="0" :max="999" controls-position="right" class="full-width" />
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.staffing"
|
|
|
+ :min="0"
|
|
|
+ :max="999"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="在岗人数">
|
|
|
- <el-input-number v-model="form.actual" :min="0" :max="999" controls-position="right" class="full-width" disabled />
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.actual"
|
|
|
+ :min="0"
|
|
|
+ :max="999"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width"
|
|
|
+ disabled
|
|
|
+ />
|
|
|
<div class="field-hint">由员工入职、调岗和离职记录实时统计</div>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
@@ -97,7 +170,13 @@
|
|
|
</el-col>
|
|
|
<el-col v-if="form.status === 3" :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="计划撤销日期">
|
|
|
- <el-date-picker v-model="form.revokeDate" type="date" value-format="yyyy-MM-dd" :picker-options="revokePickerOptions" class="full-width" />
|
|
|
+ <el-date-picker
|
|
|
+ v-model="form.revokeDate"
|
|
|
+ type="date"
|
|
|
+ value-format="yyyy-MM-dd"
|
|
|
+ :picker-options="revokePickerOptions"
|
|
|
+ class="full-width"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
@@ -107,50 +186,272 @@
|
|
|
<div class="duty-layout">
|
|
|
<section class="form-section salary-section">
|
|
|
<div class="section-heading">
|
|
|
- <div><i class="el-icon-coin"></i><span><strong>定薪标准</strong><small>设置岗位薪酬区间及绩效联动规则</small></span></div>
|
|
|
+ <div>
|
|
|
+ <i class="el-icon-coin"></i
|
|
|
+ ><span
|
|
|
+ ><strong>定薪标准</strong
|
|
|
+ ><small>设置岗位薪酬区间及绩效联动规则</small></span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
<span class="salary-summary">{{ salarySummary }}</span>
|
|
|
</div>
|
|
|
<el-row :gutter="16">
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="薪资下限" prop="salaryMin"><el-input-number v-model="form.salaryMin" :min="0" :step="500" controls-position="right" class="full-width" @change="syncSalaryMid" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="薪资中位值"><el-input-number v-model="form.salaryMid" :min="0" :step="500" controls-position="right" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="薪资上限" prop="salaryMax"><el-input-number v-model="form.salaryMax" :min="0" :step="500" controls-position="right" class="full-width" @change="syncSalaryMid" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="薪资模板"><el-select v-model="form.salaryTemplateId" filterable allow-create default-first-option class="full-width" placeholder="关联薪资模板名称或编码"><el-option v-for="item in availableSalaryTemplates" :key="item.id" :label="item.name" :value="item.name" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="绩效方案"><el-select v-model="form.perfSchemeId" filterable allow-create default-first-option class="full-width" placeholder="关联绩效方案名称或编码"><el-option v-for="item in performanceSchemes" :key="item.id" :label="item.name" :value="item.name" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="绩效系数基准"><el-input-number v-model="form.perfBaseCoef" :min="0" :max="3" :step="0.05" controls-position="right" class="full-width" /></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="考勤规则组"><el-select v-model="form.attendRuleGroup" class="full-width" placeholder="关联考勤规则"><el-option label="标准白班规则" value="1" /><el-option label="生产倒班规则" value="2" /><el-option label="弹性工时规则" value="3" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="工作班制"><el-select v-model="form.workShift" class="full-width"><el-option label="白班" value="白班" /><el-option label="两班倒" value="两班倒" /><el-option label="三班倒" value="三班倒" /><el-option label="弹性" value="弹性" /><el-option label="跟班制" value="跟班制" /></el-select></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="薪资下限" prop="salaryMin"
|
|
|
+ ><el-input-number
|
|
|
+ v-model="form.salaryMin"
|
|
|
+ :min="0"
|
|
|
+ :step="500"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width"
|
|
|
+ @change="syncSalaryMid" /></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="薪资中位值"
|
|
|
+ ><el-input-number
|
|
|
+ v-model="form.salaryMid"
|
|
|
+ :min="0"
|
|
|
+ :step="500"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width" /></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="薪资上限" prop="salaryMax"
|
|
|
+ ><el-input-number
|
|
|
+ v-model="form.salaryMax"
|
|
|
+ :min="0"
|
|
|
+ :step="500"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width"
|
|
|
+ @change="syncSalaryMid" /></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"
|
|
|
+ ><el-form-item label="薪资模板"
|
|
|
+ ><el-select
|
|
|
+ v-model="form.salaryTemplateId"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ class="full-width"
|
|
|
+ placeholder="关联薪资模板名称或编码"
|
|
|
+ ><el-option
|
|
|
+ v-for="item in availableSalaryTemplates"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.name" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"
|
|
|
+ ><el-form-item label="绩效方案"
|
|
|
+ ><el-select
|
|
|
+ v-model="form.perfSchemeId"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ class="full-width"
|
|
|
+ placeholder="关联绩效方案名称或编码"
|
|
|
+ ><el-option
|
|
|
+ v-for="item in performanceSchemes"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.name" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="绩效系数基准"
|
|
|
+ ><el-input-number
|
|
|
+ v-model="form.perfBaseCoef"
|
|
|
+ :min="0"
|
|
|
+ :max="3"
|
|
|
+ :step="0.05"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width" /></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="考勤规则组"
|
|
|
+ ><el-select
|
|
|
+ v-model="form.attendRuleGroup"
|
|
|
+ class="full-width"
|
|
|
+ placeholder="关联考勤规则"
|
|
|
+ ><el-option label="标准白班规则" value="1" /><el-option
|
|
|
+ label="生产倒班规则"
|
|
|
+ value="2" /><el-option
|
|
|
+ label="弹性工时规则"
|
|
|
+ value="3" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="工作班制"
|
|
|
+ ><el-select v-model="form.workShift" class="full-width"
|
|
|
+ ><el-option label="白班" value="白班" /><el-option
|
|
|
+ label="两班倒"
|
|
|
+ value="两班倒" /><el-option
|
|
|
+ label="三班倒"
|
|
|
+ value="三班倒" /><el-option
|
|
|
+ label="弹性"
|
|
|
+ value="弹性" /><el-option
|
|
|
+ label="跟班制"
|
|
|
+ value="跟班制" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
</el-row>
|
|
|
</section>
|
|
|
|
|
|
<section class="form-section qualification-section">
|
|
|
- <div class="section-heading"><div><i class="el-icon-user"></i><span><strong>任职条件</strong><small>明确岗位基本资格、工作关系与生效时间</small></span></div></div>
|
|
|
+ <div class="section-heading">
|
|
|
+ <div>
|
|
|
+ <i class="el-icon-user"></i
|
|
|
+ ><span
|
|
|
+ ><strong>任职条件</strong
|
|
|
+ ><small>明确岗位基本资格、工作关系与生效时间</small></span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<el-row :gutter="16" class="qualification-basics">
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="学历要求"><el-select v-model="form.eduReq" class="full-width"><el-option label="初中及以上" value="初中及以上" /><el-option label="中专及以上" value="中专及以上" /><el-option label="大专及以上" value="大专及以上" /><el-option label="本科及以上" value="本科及以上" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="8"><el-form-item label="性别要求"><el-select v-model="form.genderReq" 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="8"><el-form-item label="经验要求(年)"><el-input-number v-model="form.expReq" :min="0" :max="30" controls-position="right" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="学历要求"
|
|
|
+ ><el-select v-model="form.eduReq" class="full-width"
|
|
|
+ ><el-option
|
|
|
+ label="初中及以上"
|
|
|
+ value="初中及以上" /><el-option
|
|
|
+ label="中专及以上"
|
|
|
+ value="中专及以上" /><el-option
|
|
|
+ label="大专及以上"
|
|
|
+ value="大专及以上" /><el-option
|
|
|
+ label="本科及以上"
|
|
|
+ value="本科及以上" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="8"
|
|
|
+ ><el-form-item label="性别要求"
|
|
|
+ ><el-select v-model="form.genderReq" 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="8"
|
|
|
+ ><el-form-item label="经验要求(年)"
|
|
|
+ ><el-input-number
|
|
|
+ v-model="form.expReq"
|
|
|
+ :min="0"
|
|
|
+ :max="30"
|
|
|
+ controls-position="right"
|
|
|
+ class="full-width" /></el-form-item
|
|
|
+ ></el-col>
|
|
|
</el-row>
|
|
|
<el-row :gutter="16" class="qualification-timing">
|
|
|
- <el-col :xs="24" :sm="12"><div class="age-panel"><el-form-item label="年龄范围" class="age-field"><div class="age-value"><i class="el-icon-time"></i>{{ form.ageRange[0] }} - {{ form.ageRange[1] }} 岁</div><el-slider v-model="form.ageRange" range :min="18" :max="65" /><div class="age-scale"><span>18 岁</span><span>65 岁</span></div></el-form-item></div></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="说明书生效日期" class="date-panel"><el-date-picker v-model="form.effectDate" value-format="yyyy-MM-dd" type="date" class="full-width" /></el-form-item></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"
|
|
|
+ ><div class="age-panel">
|
|
|
+ <el-form-item label="年龄范围" class="age-field"
|
|
|
+ ><div class="age-value">
|
|
|
+ <i class="el-icon-time"></i>{{ form.ageRange[0] }} -
|
|
|
+ {{ form.ageRange[1] }} 岁
|
|
|
+ </div>
|
|
|
+ <el-slider
|
|
|
+ v-model="form.ageRange"
|
|
|
+ range
|
|
|
+ :min="18"
|
|
|
+ :max="65"
|
|
|
+ />
|
|
|
+ <div class="age-scale">
|
|
|
+ <span>18 岁</span><span>65 岁</span>
|
|
|
+ </div></el-form-item
|
|
|
+ >
|
|
|
+ </div></el-col
|
|
|
+ >
|
|
|
+ <el-col :xs="24" :sm="12"
|
|
|
+ ><el-form-item label="说明书生效日期" class="date-panel"
|
|
|
+ ><el-date-picker
|
|
|
+ v-model="form.effectDate"
|
|
|
+ value-format="yyyy-MM-dd"
|
|
|
+ type="date"
|
|
|
+ class="full-width" /></el-form-item
|
|
|
+ ></el-col>
|
|
|
</el-row>
|
|
|
<el-row :gutter="16" class="qualification-relations">
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="汇报对象"><el-select v-model="form.reportToId" filterable clearable class="full-width" placeholder="请选择上级岗位"><el-option v-for="item in positionOptions" :key="item.id" :label="item.name + ' · ' + item.code" :value="item.id" /></el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="协作部门"><el-select v-model="form.cooperationDepartmentId" filterable clearable class="full-width" placeholder="请选择协作部门"><el-option v-for="item in departments" :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-select
|
|
|
+ v-model="form.reportToId"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ class="full-width"
|
|
|
+ placeholder="请选择上级岗位"
|
|
|
+ ><el-option
|
|
|
+ v-for="item in positionOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name + ' · ' + item.code"
|
|
|
+ :value="item.id" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :xs="24" :sm="12"
|
|
|
+ ><el-form-item label="协作部门"
|
|
|
+ ><el-select
|
|
|
+ v-model="form.cooperationDepartmentId"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ class="full-width"
|
|
|
+ placeholder="请选择协作部门"
|
|
|
+ ><el-option
|
|
|
+ v-for="item in departments"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id" /></el-select></el-form-item
|
|
|
+ ></el-col>
|
|
|
</el-row>
|
|
|
- <el-form-item label="工作环境" class="work-environment-field"><el-input v-model.trim="form.workEnv" placeholder="如:车间、办公室、高温或粉尘环境等"><i slot="prefix" class="el-icon-office-building"></i></el-input></el-form-item>
|
|
|
+ <el-form-item label="工作环境" class="work-environment-field"
|
|
|
+ ><el-input
|
|
|
+ v-model.trim="form.workEnv"
|
|
|
+ placeholder="如:车间、办公室、高温或粉尘环境等"
|
|
|
+ ><i
|
|
|
+ slot="prefix"
|
|
|
+ class="el-icon-office-building"
|
|
|
+ ></i></el-input
|
|
|
+ ></el-form-item>
|
|
|
</section>
|
|
|
|
|
|
<section class="form-section responsibility-section">
|
|
|
- <div class="section-heading"><div><i class="el-icon-document-checked"></i><span><strong>岗位职责与权限</strong><small>职责建议逐行填写 5-10 条,权限支持输入后回车新增</small></span></div><span class="duty-counter">已填写 {{ dutyCount }} 条</span></div>
|
|
|
- <el-form-item label="岗位职责"><el-input v-model="dutyText" type="textarea" :rows="6" placeholder="1. 负责…… 2. 落实…… 3. 协同……" /></el-form-item>
|
|
|
- <el-form-item label="工作权限"><el-select v-model="form.authorities" multiple filterable allow-create default-first-option class="full-width" placeholder="选择权限,或输入后按回车新增"><el-option v-for="item in authorityOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
|
|
+ <div class="section-heading">
|
|
|
+ <div>
|
|
|
+ <i class="el-icon-document-checked"></i
|
|
|
+ ><span
|
|
|
+ ><strong>岗位职责与权限</strong
|
|
|
+ ><small
|
|
|
+ >职责建议逐行填写 5-10 条,权限支持输入后回车新增</small
|
|
|
+ ></span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <span class="duty-counter">已填写 {{ dutyCount }} 条</span>
|
|
|
+ </div>
|
|
|
+ <el-form-item label="岗位职责"
|
|
|
+ ><el-input
|
|
|
+ v-model="dutyText"
|
|
|
+ type="textarea"
|
|
|
+ :rows="6"
|
|
|
+ placeholder="1. 负责…… 2. 落实…… 3. 协同……"
|
|
|
+ /></el-form-item>
|
|
|
+ <el-form-item label="工作权限"
|
|
|
+ ><el-select
|
|
|
+ v-model="form.authorities"
|
|
|
+ multiple
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ class="full-width"
|
|
|
+ placeholder="选择权限,或输入后按回车新增"
|
|
|
+ ><el-option
|
|
|
+ v-for="item in authorityOptions"
|
|
|
+ :key="item"
|
|
|
+ :label="item"
|
|
|
+ :value="item" /></el-select
|
|
|
+ ></el-form-item>
|
|
|
</section>
|
|
|
</div>
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="技能资质" name="skill">
|
|
|
<div class="form-subtitle">必备技能</div>
|
|
|
- <div v-for="(skill, index) in form.skills" :key="index" class="dynamic-row">
|
|
|
+ <div
|
|
|
+ v-for="(skill, index) in form.skills"
|
|
|
+ :key="index"
|
|
|
+ class="dynamic-row"
|
|
|
+ >
|
|
|
<el-input v-model.trim="skill.name" placeholder="技能名称" />
|
|
|
<el-select v-model="skill.level" placeholder="等级">
|
|
|
<el-option label="初级" value="初级" />
|
|
|
@@ -162,23 +463,45 @@
|
|
|
<el-switch v-model="skill.required" active-text="必备" />
|
|
|
<el-button icon="el-icon-delete" @click="removeSkill(index)" />
|
|
|
</div>
|
|
|
- <el-button class="add-line" icon="el-icon-plus" @click="addSkill">新增技能</el-button>
|
|
|
+ <el-button class="add-line" icon="el-icon-plus" @click="addSkill"
|
|
|
+ >新增技能</el-button
|
|
|
+ >
|
|
|
|
|
|
<div class="form-subtitle">资质证书</div>
|
|
|
- <div v-for="(cert, index) in form.certificates" :key="cert.name + index" class="dynamic-row cert-row">
|
|
|
+ <div
|
|
|
+ v-for="(cert, index) in form.certificates"
|
|
|
+ :key="cert.name + index"
|
|
|
+ class="dynamic-row cert-row"
|
|
|
+ >
|
|
|
<el-input v-model.trim="cert.name" placeholder="证书名称" />
|
|
|
<el-select v-model="cert.category" placeholder="类别">
|
|
|
<el-option label="准入类" value="准入类" />
|
|
|
<el-option label="评价类" value="评价类" />
|
|
|
<el-option label="管理类" value="管理类" />
|
|
|
</el-select>
|
|
|
- <el-input-number v-model="cert.validMonth" :min="0" :max="120" controls-position="right" placeholder="有效期" />
|
|
|
+ <el-input-number
|
|
|
+ v-model="cert.validMonth"
|
|
|
+ :min="0"
|
|
|
+ :max="120"
|
|
|
+ controls-position="right"
|
|
|
+ placeholder="有效期"
|
|
|
+ />
|
|
|
<el-button icon="el-icon-delete" @click="removeCert(index)" />
|
|
|
</div>
|
|
|
- <el-button class="add-line" icon="el-icon-plus" @click="addCert">新增证书</el-button>
|
|
|
+ <el-button class="add-line" icon="el-icon-plus" @click="addCert"
|
|
|
+ >新增证书</el-button
|
|
|
+ >
|
|
|
|
|
|
<div class="form-subtitle">推荐培训</div>
|
|
|
- <el-select v-model="form.trainings" multiple filterable allow-create default-first-option class="full-width" placeholder="选择或录入关联培训课程">
|
|
|
+ <el-select
|
|
|
+ v-model="form.trainings"
|
|
|
+ multiple
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ class="full-width"
|
|
|
+ placeholder="选择或录入关联培训课程"
|
|
|
+ >
|
|
|
<el-option label="岗位安全上岗培训" value="岗位安全上岗培训" />
|
|
|
<el-option label="质量意识与问题分析" value="质量意识与问题分析" />
|
|
|
<el-option label="班组管理训练营" value="班组管理训练营" />
|
|
|
@@ -186,325 +509,575 @@
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="晋升路径" name="promote">
|
|
|
- <el-row :gutter="18">
|
|
|
- <el-col :xs="24" :md="8">
|
|
|
- <el-form-item label="向上晋升岗位">
|
|
|
- <el-select v-model="form.promote.up" multiple filterable allow-create class="full-width">
|
|
|
- <el-option v-for="item in positionOptions" :key="item.id" :label="item.name + '(' + item.seq + item.level + ')'" :value="item.name" :disabled="item.level <= form.level" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :xs="24" :md="8">
|
|
|
- <el-form-item label="向下降级岗位">
|
|
|
- <el-select v-model="form.promote.down" multiple filterable allow-create class="full-width">
|
|
|
- <el-option v-for="item in positionOptions" :key="item.id" :label="item.name + '(' + item.seq + item.level + ')'" :value="item.name" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :xs="24" :md="8">
|
|
|
- <el-form-item label="平级轮岗岗位">
|
|
|
- <el-select v-model="form.promote.lateral" multiple filterable allow-create class="full-width">
|
|
|
- <el-option v-for="item in positionOptions" :key="item.id" :label="item.name + '(' + item.seq + item.level + ')'" :value="item.name" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="24">
|
|
|
- <el-form-item label="晋升条件">
|
|
|
- <el-select v-model="form.promote.conditions" multiple filterable allow-create default-first-option class="full-width" placeholder="输入条件后回车,如:绩效≥B">
|
|
|
- <el-option v-for="item in conditionOptions" :key="item" :label="item" :value="item" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+ <div class="form-section qualification-section promote-section">
|
|
|
+ <div class="section-heading">
|
|
|
+ <div>
|
|
|
+ <i class="el-icon-s-opportunity"></i>
|
|
|
+ <div>
|
|
|
+ <strong>晋升路径</strong>
|
|
|
+ <small
|
|
|
+ >可配置多条路径,每条包含向上晋升、向下降级、平级轮岗岗位</small
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ size="small"
|
|
|
+ icon="el-icon-plus"
|
|
|
+ @click="addPromotePath"
|
|
|
+ >
|
|
|
+ 新增路径
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-for="(path, index) in form.promote.paths"
|
|
|
+ :key="index"
|
|
|
+ class="promote-path-card"
|
|
|
+ >
|
|
|
+ <div class="promote-path-head">
|
|
|
+ <span class="promote-path-index">路径 {{ index + 1 }}</span>
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ plain
|
|
|
+ size="mini"
|
|
|
+ icon="el-icon-delete"
|
|
|
+ :disabled="form.promote.paths.length <= 1"
|
|
|
+ @click="removePromotePath(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="18">
|
|
|
+ <el-col :xs="24" :md="8">
|
|
|
+ <el-form-item label="向上晋升岗位">
|
|
|
+ <el-select
|
|
|
+ v-model="path.up"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ class="full-width"
|
|
|
+ placeholder="选择或录入晋升岗位"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in positionOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name + '(' + item.seq + item.level + ')'"
|
|
|
+ :value="item.name"
|
|
|
+ :disabled="item.level <= form.level"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :md="8">
|
|
|
+ <el-form-item label="向下降级岗位">
|
|
|
+ <el-select
|
|
|
+ v-model="path.down"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ class="full-width"
|
|
|
+ placeholder="选择或录入降级岗位"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in positionOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name + '(' + item.seq + item.level + ')'"
|
|
|
+ :value="item.name"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :md="8">
|
|
|
+ <el-form-item label="平级轮岗岗位">
|
|
|
+ <el-select
|
|
|
+ v-model="path.lateral"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ class="full-width"
|
|
|
+ placeholder="选择或录入轮岗岗位"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in positionOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name + '(' + item.seq + item.level + ')'"
|
|
|
+ :value="item.name"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form-item label="晋升条件" class="promote-conditions">
|
|
|
+ <el-select
|
|
|
+ v-model="form.promote.conditions"
|
|
|
+ multiple
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ class="full-width"
|
|
|
+ placeholder="公共条件,输入条件后回车,如:绩效≥B"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in conditionOptions"
|
|
|
+ :key="item"
|
|
|
+ :label="item"
|
|
|
+ :value="item"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</el-form>
|
|
|
|
|
|
<template v-slot:footer>
|
|
|
<el-button @click="updateVisible(false)">取消</el-button>
|
|
|
- <el-button icon="el-icon-document-checked" @click="previewRules">校验规则</el-button>
|
|
|
+ <el-button icon="el-icon-document-checked" @click="previewRules"
|
|
|
+ >校验规则</el-button
|
|
|
+ >
|
|
|
<el-button type="primary" :loading="saving" @click="save">
|
|
|
- {{ saving ? '正在保存' : '保存岗位' }}
|
|
|
+ {{ saving ? "正在保存" : "保存岗位" }}
|
|
|
</el-button>
|
|
|
</template>
|
|
|
</ele-modal>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
- import { performanceSchemes, salaryTemplates } from '../mock';
|
|
|
+import { performanceSchemes, salaryTemplates } from "../mock";
|
|
|
|
|
|
- const emptyForm = () => ({
|
|
|
- id: null,
|
|
|
- code: '',
|
|
|
- name: '',
|
|
|
- legalId: 1,
|
|
|
- deptId: null,
|
|
|
- seq: 'O',
|
|
|
- level: 1,
|
|
|
- type: 3,
|
|
|
- staffing: 0,
|
|
|
- actual: 0,
|
|
|
- salaryMin: 0,
|
|
|
- salaryMid: 0,
|
|
|
- salaryMax: 0,
|
|
|
- salaryTemplateId: 1,
|
|
|
- perfSchemeId: 1,
|
|
|
- perfBaseCoef: 1,
|
|
|
- status: 1,
|
|
|
- revokeDate: '',
|
|
|
- attendRuleGroup: '1',
|
|
|
- workShift: '白班',
|
|
|
- reportTo: '',
|
|
|
- reportToId: null,
|
|
|
- cooperateWith: '',
|
|
|
- cooperationDepartmentId: null,
|
|
|
- workEnv: '',
|
|
|
- ageRange: [18, 60],
|
|
|
- genderReq: '不限',
|
|
|
- eduReq: '中专及以上',
|
|
|
- expReq: 0,
|
|
|
- effectDate: new Date().toISOString().slice(0, 10),
|
|
|
- version: 1,
|
|
|
- duties: [],
|
|
|
- authorities: [],
|
|
|
- skills: [],
|
|
|
- certificates: [],
|
|
|
- trainings: [],
|
|
|
- promote: { up: [], down: [], lateral: [], conditions: [] },
|
|
|
- employees: [],
|
|
|
- history: [],
|
|
|
- updatedAt: ''
|
|
|
- });
|
|
|
+const emptyForm = () => ({
|
|
|
+ id: null,
|
|
|
+ code: "",
|
|
|
+ name: "",
|
|
|
+ legalId: 1,
|
|
|
+ deptId: null,
|
|
|
+ seq: "O",
|
|
|
+ level: 1,
|
|
|
+ positionName: "",
|
|
|
+ positionSequence: "",
|
|
|
+ positionLevel: "",
|
|
|
+ type: 3,
|
|
|
+ staffing: 0,
|
|
|
+ actual: 0,
|
|
|
+ salaryMin: 0,
|
|
|
+ salaryMid: 0,
|
|
|
+ salaryMax: 0,
|
|
|
+ salaryTemplateId: 1,
|
|
|
+ perfSchemeId: 1,
|
|
|
+ perfBaseCoef: 1,
|
|
|
+ status: 1,
|
|
|
+ revokeDate: "",
|
|
|
+ attendRuleGroup: "1",
|
|
|
+ workShift: "白班",
|
|
|
+ reportTo: "",
|
|
|
+ reportToId: null,
|
|
|
+ cooperateWith: "",
|
|
|
+ cooperationDepartmentId: null,
|
|
|
+ workEnv: "",
|
|
|
+ ageRange: [18, 60],
|
|
|
+ genderReq: "不限",
|
|
|
+ eduReq: "中专及以上",
|
|
|
+ expReq: 0,
|
|
|
+ effectDate: new Date().toISOString().slice(0, 10),
|
|
|
+ version: 1,
|
|
|
+ duties: [],
|
|
|
+ authorities: [],
|
|
|
+ skills: [],
|
|
|
+ certificates: [],
|
|
|
+ trainings: [],
|
|
|
+ promote: { paths: [{ up: "", down: "", lateral: "" }], conditions: [] },
|
|
|
+ employees: [],
|
|
|
+ history: [],
|
|
|
+ updatedAt: "",
|
|
|
+});
|
|
|
|
|
|
- export default {
|
|
|
- name: 'PositionEditDialog',
|
|
|
- props: {
|
|
|
- visible: Boolean,
|
|
|
- data: Object,
|
|
|
- nextIndex: {
|
|
|
- type: Number,
|
|
|
- default: 1
|
|
|
- },
|
|
|
- positionList: {
|
|
|
- type: Array,
|
|
|
- default: () => []
|
|
|
- },
|
|
|
- legalEntities: {
|
|
|
- type: Array,
|
|
|
- default: () => []
|
|
|
+export default {
|
|
|
+ name: "PositionEditDialog",
|
|
|
+ props: {
|
|
|
+ visible: Boolean,
|
|
|
+ data: Object,
|
|
|
+ nextIndex: {
|
|
|
+ type: Number,
|
|
|
+ default: 1,
|
|
|
+ },
|
|
|
+ positionList: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ legalEntities: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ departments: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ sequenceOptions: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ activeTab: "base",
|
|
|
+ form: emptyForm(),
|
|
|
+ dutyText: "",
|
|
|
+ saving: false,
|
|
|
+ salaryTemplates,
|
|
|
+ performanceSchemes,
|
|
|
+ authorityOptions: [
|
|
|
+ "生产报工",
|
|
|
+ "设备点检",
|
|
|
+ "绩效初评",
|
|
|
+ "班组排班",
|
|
|
+ "体系文件维护",
|
|
|
+ "维修工单派发",
|
|
|
+ ],
|
|
|
+ conditionOptions: [
|
|
|
+ "在岗满 24 月",
|
|
|
+ "绩效≥B",
|
|
|
+ "技能达标",
|
|
|
+ "资质齐全",
|
|
|
+ "培训完成率 100%",
|
|
|
+ "直接上级推荐",
|
|
|
+ ],
|
|
|
+ revokePickerOptions: {
|
|
|
+ disabledDate: (date) =>
|
|
|
+ date.getTime() < new Date().setHours(0, 0, 0, 0),
|
|
|
},
|
|
|
- departments: {
|
|
|
- type: Array,
|
|
|
- default: () => []
|
|
|
+ rules: {
|
|
|
+ code: [{ required: true, message: "请输入岗位编码", trigger: "blur" }],
|
|
|
+ name: [{ required: true, message: "请输入岗位名称", trigger: "blur" }],
|
|
|
+ legalId: [{ required: true, message: "请选择法人", trigger: "change" }],
|
|
|
+ deptId: [{ required: true, message: "请选择部门", trigger: "change" }],
|
|
|
+ seq: [{ required: true, message: "请选择岗位序列", trigger: "change" }],
|
|
|
+ level: [
|
|
|
+ { required: true, message: "请输入岗位层级", trigger: "change" },
|
|
|
+ ],
|
|
|
+ staffing: [
|
|
|
+ { required: true, message: "请输入编制人数", trigger: "change" },
|
|
|
+ ],
|
|
|
+ salaryMin: [
|
|
|
+ { required: true, message: "请输入薪资下限", trigger: "change" },
|
|
|
+ ],
|
|
|
+ salaryMax: [
|
|
|
+ { required: true, message: "请输入薪资上限", trigger: "change" },
|
|
|
+ ],
|
|
|
},
|
|
|
- sequenceOptions: {
|
|
|
- type: Array,
|
|
|
- default: () => []
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ isUpdate() {
|
|
|
+ return Boolean(this.data?.id);
|
|
|
+ },
|
|
|
+ departmentOptions() {
|
|
|
+ return this.departments.filter(
|
|
|
+ (item) => item.legalId === this.form.legalId,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ positionOptions() {
|
|
|
+ return this.positionList.filter(
|
|
|
+ (item) => item.id !== this.form.id && item.status < 4,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ availableSalaryTemplates() {
|
|
|
+ return this.salaryTemplates.filter((item) =>
|
|
|
+ item.legalIds.includes(this.form.legalId),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ configuredLevelOptions() {
|
|
|
+ const sequence = this.sequenceOptions.find(
|
|
|
+ (item) => item.id === this.form.seq,
|
|
|
+ );
|
|
|
+ return (sequence?.children || [])
|
|
|
+ .map((item) => ({
|
|
|
+ // 优先取后端 positionLevel,缺失时按原逻辑从 id/label 提取数字
|
|
|
+ value:
|
|
|
+ item.value ??
|
|
|
+ Number(String(item.id || item.label).match(/\d+/)?.[0] || 0),
|
|
|
+ label: item.label,
|
|
|
+ }))
|
|
|
+ .filter((item) => item.label);
|
|
|
+ },
|
|
|
+ salarySummary() {
|
|
|
+ const min = Number(this.form.salaryMin || 0).toLocaleString("zh-CN");
|
|
|
+ const max = Number(this.form.salaryMax || 0).toLocaleString("zh-CN");
|
|
|
+ return `¥${min} - ¥${max} / 月`;
|
|
|
+ },
|
|
|
+ dutyCount() {
|
|
|
+ return this.dutyText
|
|
|
+ .split("\n")
|
|
|
+ .map((item) => item.trim())
|
|
|
+ .filter(Boolean).length;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ visible(value) {
|
|
|
+ if (value) {
|
|
|
+ this.initialize();
|
|
|
}
|
|
|
},
|
|
|
- data() {
|
|
|
- return {
|
|
|
- activeTab: 'base',
|
|
|
- form: emptyForm(),
|
|
|
- dutyText: '',
|
|
|
- saving: false,
|
|
|
- salaryTemplates,
|
|
|
- performanceSchemes,
|
|
|
- authorityOptions: ['生产报工', '设备点检', '绩效初评', '班组排班', '体系文件维护', '维修工单派发'],
|
|
|
- conditionOptions: ['在岗满 24 月', '绩效≥B', '技能达标', '资质齐全', '培训完成率 100%', '直接上级推荐'],
|
|
|
- revokePickerOptions: { disabledDate: (date) => date.getTime() < new Date().setHours(0, 0, 0, 0) },
|
|
|
- rules: {
|
|
|
- code: [{ required: true, message: '请输入岗位编码', trigger: 'blur' }],
|
|
|
- name: [{ required: true, message: '请输入岗位名称', trigger: 'blur' }],
|
|
|
- legalId: [{ required: true, message: '请选择法人', trigger: 'change' }],
|
|
|
- deptId: [{ required: true, message: '请选择部门', trigger: 'change' }],
|
|
|
- seq: [{ required: true, message: '请选择岗位序列', trigger: 'change' }],
|
|
|
- level: [{ required: true, message: '请输入岗位层级', trigger: 'change' }],
|
|
|
- staffing: [{ required: true, message: '请输入编制人数', trigger: 'change' }],
|
|
|
- salaryMin: [{ required: true, message: '请输入薪资下限', trigger: 'change' }],
|
|
|
- salaryMax: [{ required: true, message: '请输入薪资上限', trigger: 'change' }]
|
|
|
- }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ initialize() {
|
|
|
+ const source = this.data
|
|
|
+ ? JSON.parse(JSON.stringify(this.data))
|
|
|
+ : emptyForm();
|
|
|
+ this.form = {
|
|
|
+ ...emptyForm(),
|
|
|
+ ...source,
|
|
|
+ promote: this.normalizePromote(source.promote),
|
|
|
};
|
|
|
+ if (!this.isUpdate) {
|
|
|
+ this.form.legalId = this.legalEntities[0]?.id || null;
|
|
|
+ this.form.seq = this.sequenceOptions[0]?.id || this.form.seq;
|
|
|
+ this.form.level = this.configuredLevelOptions[0]?.value || 1;
|
|
|
+ this.syncSequenceFields();
|
|
|
+ this.refreshCode();
|
|
|
+ this.form.deptId = this.departmentOptions[0]?.id || null;
|
|
|
+ this.form.salaryTemplateId =
|
|
|
+ this.availableSalaryTemplates[0]?.name || "";
|
|
|
+ }
|
|
|
+ this.syncSequenceFields();
|
|
|
+ this.dutyText = (this.form.duties || []).join("\n");
|
|
|
+ this.activeTab = "base";
|
|
|
},
|
|
|
- computed: {
|
|
|
- isUpdate() {
|
|
|
- return Boolean(this.data?.id);
|
|
|
- },
|
|
|
- departmentOptions() {
|
|
|
- return this.departments.filter((item) => item.legalId === this.form.legalId);
|
|
|
- },
|
|
|
- positionOptions() {
|
|
|
- return this.positionList.filter((item) => item.id !== this.form.id && item.status < 4);
|
|
|
- },
|
|
|
- availableSalaryTemplates() {
|
|
|
- return this.salaryTemplates.filter((item) => item.legalIds.includes(this.form.legalId));
|
|
|
- },
|
|
|
- configuredLevelOptions() {
|
|
|
- const sequence = this.sequenceOptions.find((item) => item.id === this.form.seq);
|
|
|
- return (sequence?.children || []).map((item) => ({
|
|
|
- value: Number(String(item.id || item.label).match(/\d+/)?.[0] || 0),
|
|
|
- label: item.label,
|
|
|
- })).filter((item) => item.value > 0);
|
|
|
- },
|
|
|
- salarySummary() {
|
|
|
- const min = Number(this.form.salaryMin || 0).toLocaleString('zh-CN');
|
|
|
- const max = Number(this.form.salaryMax || 0).toLocaleString('zh-CN');
|
|
|
- return `¥${min} - ¥${max} / 月`;
|
|
|
- },
|
|
|
- dutyCount() {
|
|
|
- return this.dutyText
|
|
|
- .split('\n')
|
|
|
- .map((item) => item.trim())
|
|
|
- .filter(Boolean).length;
|
|
|
+ normalizePromote(source) {
|
|
|
+ const promote = { ...emptyForm().promote, ...(source || {}) };
|
|
|
+ const hasPath = (list) =>
|
|
|
+ Array.isArray(list) &&
|
|
|
+ list.some((path) => path?.up || path?.down || path?.lateral);
|
|
|
+ if (!Array.isArray(promote.paths) || !hasPath(promote.paths)) {
|
|
|
+ // 兼容旧数据结构(up/down/lateral 数组):迁移为首条路径,最少保留一条
|
|
|
+ promote.paths =
|
|
|
+ promote.up?.length || promote.down?.length || promote.lateral?.length
|
|
|
+ ? [
|
|
|
+ {
|
|
|
+ up: promote.up?.[0] || "",
|
|
|
+ down: promote.down?.[0] || "",
|
|
|
+ lateral: promote.lateral?.[0] || "",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ : [{ up: "", down: "", lateral: "" }];
|
|
|
}
|
|
|
+ return promote;
|
|
|
},
|
|
|
- watch: {
|
|
|
- visible(value) {
|
|
|
- if (value) {
|
|
|
- this.initialize();
|
|
|
- }
|
|
|
+ addPromotePath() {
|
|
|
+ this.form.promote.paths.push({ up: "", down: "", lateral: "" });
|
|
|
+ },
|
|
|
+ removePromotePath(index) {
|
|
|
+ if (this.form.promote.paths.length <= 1) return;
|
|
|
+ this.form.promote.paths.splice(index, 1);
|
|
|
+ },
|
|
|
+ handleLegalChange() {
|
|
|
+ this.form.deptId = this.departmentOptions[0]?.id || null;
|
|
|
+ if (
|
|
|
+ !this.availableSalaryTemplates.some(
|
|
|
+ (item) => item.id === this.form.salaryTemplateId,
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ this.form.salaryTemplateId =
|
|
|
+ this.availableSalaryTemplates[0]?.name || "";
|
|
|
}
|
|
|
+ this.refreshCode();
|
|
|
},
|
|
|
- methods: {
|
|
|
- initialize() {
|
|
|
- const source = this.data ? JSON.parse(JSON.stringify(this.data)) : emptyForm();
|
|
|
- this.form = {
|
|
|
- ...emptyForm(),
|
|
|
- ...source,
|
|
|
- promote: { ...emptyForm().promote, ...(source.promote || {}) }
|
|
|
- };
|
|
|
- if (!this.isUpdate) {
|
|
|
- this.form.legalId = this.legalEntities[0]?.id || null;
|
|
|
- this.form.seq = this.sequenceOptions[0]?.id || this.form.seq;
|
|
|
- this.form.level = this.configuredLevelOptions[0]?.value || 1;
|
|
|
- this.refreshCode();
|
|
|
- this.form.deptId = this.departmentOptions[0]?.id || null;
|
|
|
- this.form.salaryTemplateId = this.availableSalaryTemplates[0]?.name || '';
|
|
|
- }
|
|
|
- this.dutyText = (this.form.duties || []).join('\n');
|
|
|
- this.activeTab = 'base';
|
|
|
- },
|
|
|
- handleLegalChange() {
|
|
|
- this.form.deptId = this.departmentOptions[0]?.id || null;
|
|
|
- if (!this.availableSalaryTemplates.some((item) => item.id === this.form.salaryTemplateId)) {
|
|
|
- this.form.salaryTemplateId = this.availableSalaryTemplates[0]?.name || '';
|
|
|
- }
|
|
|
- this.refreshCode();
|
|
|
- },
|
|
|
- handleSequenceChange() {
|
|
|
- if (this.configuredLevelOptions.length) {
|
|
|
- this.form.level = this.configuredLevelOptions[0].value;
|
|
|
- }
|
|
|
- this.refreshCode();
|
|
|
- },
|
|
|
- refreshCode() {
|
|
|
- if (this.isUpdate) {
|
|
|
+ handleSequenceChange() {
|
|
|
+ if (this.configuredLevelOptions.length) {
|
|
|
+ this.form.level = this.configuredLevelOptions[0].value;
|
|
|
+ }
|
|
|
+ this.syncSequenceFields();
|
|
|
+ this.refreshCode();
|
|
|
+ },
|
|
|
+ handleLevelChange() {
|
|
|
+ this.syncSequenceFields();
|
|
|
+ this.refreshCode();
|
|
|
+ },
|
|
|
+ syncSequenceFields() {
|
|
|
+ // positionSequence 取选中序列的 label
|
|
|
+ // positionLevel 取选中层级的 label
|
|
|
+ // sequenceId 取选中层级的 value(用于 departmentPosition.sequenceId)
|
|
|
+ const sequence = this.sequenceOptions.find(
|
|
|
+ (item) => item.id === this.form.seq,
|
|
|
+ );
|
|
|
+ const level = (sequence?.children || []).find(
|
|
|
+ (item) => String(item.value) === String(this.form.level),
|
|
|
+ );
|
|
|
+ if (sequence?.label) this.form.positionSequence = sequence.label;
|
|
|
+ if (level?.label) this.form.positionLevel = level.label;
|
|
|
+ if (level?.value !== undefined && level?.value !== null) {
|
|
|
+ this.form.sequenceId = level.value;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ refreshCode() {
|
|
|
+ if (this.isUpdate) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const legal = this.legalEntities.find(
|
|
|
+ (item) => item.id === this.form.legalId,
|
|
|
+ );
|
|
|
+ const serial = String(this.nextIndex || 1).padStart(4, "0");
|
|
|
+ // 层级部分用 positionLevel(层级 label,如 "pq"),不再用层级的长 id
|
|
|
+ this.form.code = `${this.form.seq}${
|
|
|
+ this.form.positionLevel || this.form.level
|
|
|
+ }-${
|
|
|
+ legal?.code || legal?.groupCode || "ZY"
|
|
|
+ }-${serial}`;
|
|
|
+ },
|
|
|
+ syncSalaryMid() {
|
|
|
+ if (this.form.salaryMax >= this.form.salaryMin) {
|
|
|
+ this.form.salaryMid = Math.round(
|
|
|
+ (Number(this.form.salaryMax) + Number(this.form.salaryMin)) / 2,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addSkill() {
|
|
|
+ this.form.skills.push({
|
|
|
+ name: "",
|
|
|
+ level: "中级",
|
|
|
+ required: true,
|
|
|
+ matchRate: 0,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ removeSkill(index) {
|
|
|
+ this.form.skills.splice(index, 1);
|
|
|
+ },
|
|
|
+ addCert() {
|
|
|
+ this.form.certificates.push({
|
|
|
+ name: "",
|
|
|
+ category: "准入类",
|
|
|
+ validMonth: 36,
|
|
|
+ alertDays: 30,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ removeCert(index) {
|
|
|
+ this.form.certificates.splice(index, 1);
|
|
|
+ },
|
|
|
+ previewRules() {
|
|
|
+ const warnings = this.collectRuleErrors();
|
|
|
+ if (this.form.actual > this.form.staffing) {
|
|
|
+ warnings.push("在岗人数超过编制,将触发超编预警");
|
|
|
+ }
|
|
|
+ if (!warnings.length) {
|
|
|
+ this.$message.success("规则校验通过,可参与编制、薪资、绩效和培训联动");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$alert(warnings.join(";"), "规则校验提示", { type: "warning" });
|
|
|
+ },
|
|
|
+ save() {
|
|
|
+ this.$refs.form.validate((valid) => {
|
|
|
+ if (!valid || this.saving) {
|
|
|
return;
|
|
|
}
|
|
|
- const legal = this.legalEntities.find((item) => item.id === this.form.legalId);
|
|
|
- const serial = String(this.nextIndex || 1).padStart(4, '0');
|
|
|
- this.form.code = `${this.form.seq}${this.form.level}-${legal?.code || legal?.groupCode || 'ZY'}-${serial}`;
|
|
|
- },
|
|
|
- syncSalaryMid() {
|
|
|
- if (this.form.salaryMax >= this.form.salaryMin) {
|
|
|
- this.form.salaryMid = Math.round((Number(this.form.salaryMax) + Number(this.form.salaryMin)) / 2);
|
|
|
- }
|
|
|
- },
|
|
|
- addSkill() {
|
|
|
- this.form.skills.push({ name: '', level: '中级', required: true, matchRate: 0 });
|
|
|
- },
|
|
|
- removeSkill(index) {
|
|
|
- this.form.skills.splice(index, 1);
|
|
|
- },
|
|
|
- addCert() {
|
|
|
- this.form.certificates.push({ name: '', category: '准入类', validMonth: 36, alertDays: 30 });
|
|
|
- },
|
|
|
- removeCert(index) {
|
|
|
- this.form.certificates.splice(index, 1);
|
|
|
- },
|
|
|
- previewRules() {
|
|
|
- const warnings = this.collectRuleErrors();
|
|
|
- if (this.form.actual > this.form.staffing) {
|
|
|
- warnings.push('在岗人数超过编制,将触发超编预警');
|
|
|
- }
|
|
|
- if (!warnings.length) {
|
|
|
- this.$message.success('规则校验通过,可参与编制、薪资、绩效和培训联动');
|
|
|
+ const errors = this.collectRuleErrors();
|
|
|
+ if (errors.length) {
|
|
|
+ this.activeTab = this.resolveErrorTab(errors[0]);
|
|
|
+ this.$alert(errors.join(";"), "岗位规则校验未通过", {
|
|
|
+ type: "error",
|
|
|
+ });
|
|
|
return;
|
|
|
}
|
|
|
- this.$alert(warnings.join(';'), '规则校验提示', { type: 'warning' });
|
|
|
- },
|
|
|
- save() {
|
|
|
- this.$refs.form.validate((valid) => {
|
|
|
- if (!valid || this.saving) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const errors = this.collectRuleErrors();
|
|
|
- if (errors.length) {
|
|
|
- this.activeTab = this.resolveErrorTab(errors[0]);
|
|
|
- this.$alert(errors.join(';'), '岗位规则校验未通过', { type: 'error' });
|
|
|
- return;
|
|
|
- }
|
|
|
- this.saving = true;
|
|
|
- const payload = {
|
|
|
- ...this.form,
|
|
|
- version: this.isUpdate ? Number(this.form.version || 1) + 1 : 1,
|
|
|
- duties: this.dutyText
|
|
|
- .split('\n')
|
|
|
- .map((item) => item.trim())
|
|
|
- .filter(Boolean),
|
|
|
- updatedAt: new Date().toISOString().slice(0, 10),
|
|
|
- history: [
|
|
|
- {
|
|
|
- time: new Date().toLocaleString('zh-CN', { hour12: false }),
|
|
|
- title: this.isUpdate ? '编辑岗位档案' : '新增岗位档案',
|
|
|
- content: '岗位档案已提交至人力资源服务。'
|
|
|
- },
|
|
|
- ...(this.form.history || [])
|
|
|
- ]
|
|
|
- };
|
|
|
- this.$emit('save', payload);
|
|
|
- this.saving = false;
|
|
|
- });
|
|
|
- },
|
|
|
- reset() {
|
|
|
- this.form = emptyForm();
|
|
|
- this.dutyText = '';
|
|
|
- this.$refs.form?.clearValidate();
|
|
|
- },
|
|
|
- updateVisible(value) {
|
|
|
- this.$emit('update:visible', value);
|
|
|
- },
|
|
|
- collectRuleErrors() {
|
|
|
- const errors = [];
|
|
|
- const duplicate = this.positionList.some((item) => item.code === this.form.code && item.id !== this.form.id);
|
|
|
- const department = this.departments.find((item) => item.id === this.form.deptId);
|
|
|
- const template = this.salaryTemplates.find((item) => item.name === this.form.salaryTemplateId || item.id === this.form.salaryTemplateId);
|
|
|
- const duties = this.dutyText.split('\n').map((item) => item.trim()).filter(Boolean);
|
|
|
- const skills = this.form.skills.filter((item) => item.name.trim());
|
|
|
- const targets = this.form.promote.up.map((name) => this.positionList.find((item) => item.name === name)).filter(Boolean);
|
|
|
- if (duplicate) errors.push('岗位编码已存在');
|
|
|
- if (!department || department.legalId !== this.form.legalId) errors.push('所属部门必须存在且属于当前法人');
|
|
|
- if (this.form.level < 1) errors.push('岗位层级必须大于 0');
|
|
|
- if (this.configuredLevelOptions.length && !this.configuredLevelOptions.some((item) => item.value === this.form.level)) errors.push('岗位层级必须来自当前序列的启用层级');
|
|
|
- if (this.form.salaryMax < this.form.salaryMin) errors.push('薪资上限不能小于下限');
|
|
|
- if (template && !template.legalIds.includes(this.form.legalId)) errors.push('薪资模板与所属法人不匹配');
|
|
|
- else if (template && (this.form.salaryMin < template.range[0] || this.form.salaryMax > template.range[1])) errors.push(`薪资区间须位于模板 ${template.range[0]}-${template.range[1]} 元范围内`);
|
|
|
- if (this.form.status === 3 && !this.form.revokeDate) errors.push('待撤销岗位必须填写计划撤销日期');
|
|
|
- if (!duties.length) errors.push('岗位职责不能为空');
|
|
|
- if (!(this.form.authorities || []).length) errors.push('岗位权限不能为空');
|
|
|
- if (this.form.skills.some((item) => !item.name.trim())) errors.push('技能名称不能为空');
|
|
|
- if (this.form.certificates.some((item) => !item.name.trim())) errors.push('证书名称不能为空');
|
|
|
- if (targets.some((item) => item.level <= this.form.level)) errors.push('向上晋升目标岗位层级必须高于当前岗位');
|
|
|
- return errors;
|
|
|
- },
|
|
|
- resolveErrorTab(message) {
|
|
|
- if (/编码|部门|层级/.test(message)) return 'base';
|
|
|
- if (/薪资|职责/.test(message)) return 'duty';
|
|
|
- if (/技能|证书/.test(message)) return 'skill';
|
|
|
- return 'promote';
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
+ this.saving = true;
|
|
|
+ const payload = {
|
|
|
+ ...this.form,
|
|
|
+ version: this.isUpdate ? Number(this.form.version || 1) + 1 : 1,
|
|
|
+ duties: this.dutyText
|
|
|
+ .split("\n")
|
|
|
+ .map((item) => item.trim())
|
|
|
+ .filter(Boolean),
|
|
|
+ updatedAt: new Date().toISOString().slice(0, 10),
|
|
|
+ history: [
|
|
|
+ {
|
|
|
+ time: new Date().toLocaleString("zh-CN", { hour12: false }),
|
|
|
+ title: this.isUpdate ? "编辑岗位档案" : "新增岗位档案",
|
|
|
+ content: "岗位档案已提交至人力资源服务。",
|
|
|
+ },
|
|
|
+ ...(this.form.history || []),
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ this.$emit("save", payload);
|
|
|
+ this.saving = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ reset() {
|
|
|
+ this.form = emptyForm();
|
|
|
+ this.dutyText = "";
|
|
|
+ this.$refs.form?.clearValidate();
|
|
|
+ },
|
|
|
+ updateVisible(value) {
|
|
|
+ this.$emit("update:visible", value);
|
|
|
+ },
|
|
|
+ collectRuleErrors() {
|
|
|
+ const errors = [];
|
|
|
+ const duplicate = this.positionList.some(
|
|
|
+ (item) => item.code === this.form.code && item.id !== this.form.id,
|
|
|
+ );
|
|
|
+ const department = this.departments.find(
|
|
|
+ (item) => item.id === this.form.deptId,
|
|
|
+ );
|
|
|
+ const template = this.salaryTemplates.find(
|
|
|
+ (item) =>
|
|
|
+ item.name === this.form.salaryTemplateId ||
|
|
|
+ item.id === this.form.salaryTemplateId,
|
|
|
+ );
|
|
|
+ const duties = this.dutyText
|
|
|
+ .split("\n")
|
|
|
+ .map((item) => item.trim())
|
|
|
+ .filter(Boolean);
|
|
|
+ const skills = this.form.skills.filter((item) => item.name.trim());
|
|
|
+ const targets = (this.form.promote.paths || [])
|
|
|
+ .map((path) =>
|
|
|
+ this.positionList.find((item) => item.name === path.up),
|
|
|
+ )
|
|
|
+ .filter(Boolean);
|
|
|
+ if (duplicate) errors.push("岗位编码已存在");
|
|
|
+ if (!department || department.legalId !== this.form.legalId)
|
|
|
+ errors.push("所属部门必须存在且属于当前法人");
|
|
|
+ if (this.form.level < 1) errors.push("岗位层级必须大于 0");
|
|
|
+ if (
|
|
|
+ this.configuredLevelOptions.length &&
|
|
|
+ !this.configuredLevelOptions.some(
|
|
|
+ (item) => item.value === this.form.level,
|
|
|
+ )
|
|
|
+ )
|
|
|
+ errors.push("岗位层级必须来自当前序列的启用层级");
|
|
|
+ if (this.form.salaryMax < this.form.salaryMin)
|
|
|
+ errors.push("薪资上限不能小于下限");
|
|
|
+ if (template && !template.legalIds.includes(this.form.legalId))
|
|
|
+ errors.push("薪资模板与所属法人不匹配");
|
|
|
+ else if (
|
|
|
+ template &&
|
|
|
+ (this.form.salaryMin < template.range[0] ||
|
|
|
+ this.form.salaryMax > template.range[1])
|
|
|
+ )
|
|
|
+ errors.push(
|
|
|
+ `薪资区间须位于模板 ${template.range[0]}-${template.range[1]} 元范围内`,
|
|
|
+ );
|
|
|
+ if (this.form.status === 3 && !this.form.revokeDate)
|
|
|
+ errors.push("待撤销岗位必须填写计划撤销日期");
|
|
|
+ if (!duties.length) errors.push("岗位职责不能为空");
|
|
|
+ if (!(this.form.authorities || []).length)
|
|
|
+ errors.push("岗位权限不能为空");
|
|
|
+ if (this.form.skills.some((item) => !item.name.trim()))
|
|
|
+ errors.push("技能名称不能为空");
|
|
|
+ if (this.form.certificates.some((item) => !item.name.trim()))
|
|
|
+ errors.push("证书名称不能为空");
|
|
|
+ if (targets.some((item) => item.level <= this.form.level))
|
|
|
+ errors.push("向上晋升目标岗位层级必须高于当前岗位");
|
|
|
+ return errors;
|
|
|
+ },
|
|
|
+ resolveErrorTab(message) {
|
|
|
+ if (/编码|部门|层级/.test(message)) return "base";
|
|
|
+ if (/薪资|职责/.test(message)) return "duty";
|
|
|
+ if (/技能|证书/.test(message)) return "skill";
|
|
|
+ return "promote";
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
-<style src="../../../styles/views/positionManagement/components/PositionEditDialog.scss" lang="scss" scoped></style>
|
|
|
+<style
|
|
|
+ src="../../../styles/views/positionManagement/components/PositionEditDialog.scss"
|
|
|
+ lang="scss"
|
|
|
+ scoped
|
|
|
+></style>
|