|
@@ -132,23 +132,26 @@
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="岗位性质"><el-select v-model="form.employmentType" filterable
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="岗位性质" prop="employmentType"><el-select
|
|
|
|
|
+ v-model="form.employmentType" filterable
|
|
|
clearable class="full-width" :placeholder="form.positionId ? '请选择岗位性质' : '请先选择岗位'"
|
|
clearable class="full-width" :placeholder="form.positionId ? '请选择岗位性质' : '请先选择岗位'"
|
|
|
@change="syncEmploymentTypeName">
|
|
@change="syncEmploymentTypeName">
|
|
|
<el-option v-for="item in employmentOptions" :key="item.key || item.value" :label="item.label"
|
|
<el-option v-for="item in employmentOptions" :key="item.key || item.value" :label="item.label"
|
|
|
:value="item.value" />
|
|
:value="item.value" />
|
|
|
</el-select></el-form-item></el-col>
|
|
</el-select></el-form-item></el-col>
|
|
|
- <el-col :xs="24" :sm="12"><el-form-item label="岗位层级"><el-select v-model="form.staffingLevel"
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12"><el-form-item label="岗位层级" prop="staffingLevel"><el-select
|
|
|
|
|
+ v-model="form.staffingLevel"
|
|
|
class="full-width" :disabled="!staffingLevelOptions.length"
|
|
class="full-width" :disabled="!staffingLevelOptions.length"
|
|
|
:placeholder="form.positionId ? '请选择岗位层级' : '请先选择岗位'" @change="handleStaffingLevelChange">
|
|
:placeholder="form.positionId ? '请选择岗位层级' : '请先选择岗位'" @change="handleStaffingLevelChange">
|
|
|
<el-option v-for="item in staffingLevelOptions" :key="item" :label="item"
|
|
<el-option v-for="item in staffingLevelOptions" :key="item" :label="item"
|
|
|
:value="item" /></el-select></el-form-item></el-col>
|
|
:value="item" /></el-select></el-form-item></el-col>
|
|
|
<el-col :xs="24" :sm="12">
|
|
<el-col :xs="24" :sm="12">
|
|
|
<el-form-item label="直属上级">
|
|
<el-form-item label="直属上级">
|
|
|
- <el-select v-model="form.managerId" filterable clearable class="full-width"
|
|
|
|
|
- :loading="managerLoading" :disabled="!hasDept" :placeholder="hasDept ? '请选择直属上级' : '请先选择隶属部门'"
|
|
|
|
|
- @change="handleManagerChange">
|
|
|
|
|
- <el-option v-for="item in managerOptions" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
|
|
|
|
+ <el-select v-model="form.managerId" filterable remote clearable class="full-width"
|
|
|
|
|
+ :loading="managerLoading" :remote-method="searchManagers" placeholder="输入姓名/工号搜索全员"
|
|
|
|
|
+ @change="handleManagerChange" @focus="ensureManagerOptions">
|
|
|
|
|
+ <el-option v-for="item in managerOptions" :key="item.id" :label="managerOptionLabel(item)"
|
|
|
|
|
+ :value="item.id" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
@@ -639,7 +642,7 @@ import {
|
|
|
getDepartmentPositionType,
|
|
getDepartmentPositionType,
|
|
|
getDepartmentPositionLevels,
|
|
getDepartmentPositionLevels,
|
|
|
} from '@/api/hr';
|
|
} from '@/api/hr';
|
|
|
-import { listOrganizations, getFactoryAreaPage, getUserPage, EMPLOYEE_STATUS, EMPLOYEE_STATUS_CODE, CERT_TYPE } from '@/api/organization';
|
|
|
|
|
|
|
+import { listOrganizations, getFactoryAreaPage, searchUsersByKeyword, EMPLOYEE_STATUS, EMPLOYEE_STATUS_CODE, CERT_TYPE } from '@/api/organization';
|
|
|
import { getDictByCode } from '@/api/sys';
|
|
import { getDictByCode } from '@/api/sys';
|
|
|
import { loadEmployeeStatusOptions } from '@/utils/employee-status-dict';
|
|
import { loadEmployeeStatusOptions } from '@/utils/employee-status-dict';
|
|
|
/* 性别:接口口径 1 男 / 2 女(下拉 value 即接口码);没有值就留空,不提交该字段 */
|
|
/* 性别:接口口径 1 男 / 2 女(下拉 value 即接口码);没有值就留空,不提交该字段 */
|
|
@@ -651,6 +654,8 @@ import { positionAllowanceUnitOptions } from '@/enum/dict.js';
|
|
|
import { getCertLevelOptions, isCertLevelDisabled, NO_LEVEL_CERT_TYPES, NO_LEVEL_VALUE } from '@/enum/certLevel';
|
|
import { getCertLevelOptions, isCertLevelDisabled, NO_LEVEL_CERT_TYPES, NO_LEVEL_VALUE } from '@/enum/certLevel';
|
|
|
/* 所属机构默认值取登录缓存(loginChangeGroupVOList 第一条) */
|
|
/* 所属机构默认值取登录缓存(loginChangeGroupVOList 第一条) */
|
|
|
import { getCurrentOrgId, getOrgOptions } from '@/utils/current-org';
|
|
import { getCurrentOrgId, getOrgOptions } from '@/utils/current-org';
|
|
|
|
|
+/* 明确报错:岗位性质字典失败时提示,不静默留空 */
|
|
|
|
|
+import { showRequestError } from '@/utils/message';
|
|
|
|
|
|
|
|
const emptyContract = () => ({ category: '', name: '', signDate: '', startDate: '', endDate: '', files: [], status: '生效', remark: '' });
|
|
const emptyContract = () => ({ category: '', name: '', signDate: '', startDate: '', endDate: '', files: [], status: '生效', remark: '' });
|
|
|
const emptyForm = () => ({
|
|
const emptyForm = () => ({
|
|
@@ -854,6 +859,38 @@ export default {
|
|
|
},
|
|
},
|
|
|
trigger: 'change',
|
|
trigger: 'change',
|
|
|
}],
|
|
}],
|
|
|
|
|
+ /* 岗位性质(form.employmentType,提交字典 id):必填 */
|
|
|
|
|
+ employmentType: [{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
|
|
+ if (this._skipFormValidate) return callback();
|
|
|
|
|
+ if (value == null || value === '') {
|
|
|
|
|
+ callback(new Error('请选择岗位性质'));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ callback();
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: 'change',
|
|
|
|
|
+ }],
|
|
|
|
|
+ /* 岗位层级(form.staffingLevel):必填。岗位未配置层级时下拉无候选,给出可操作的提示 */
|
|
|
|
|
+ staffingLevel: [{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
|
|
+ if (this._skipFormValidate) return callback();
|
|
|
|
|
+ if (value == null || value === '') {
|
|
|
|
|
+ let message = '请选择岗位层级';
|
|
|
|
|
+ if (!this.staffingLevelOptions.length) {
|
|
|
|
|
+ message = this.form.positionId
|
|
|
|
|
+ ? '当前岗位未配置岗位层级,请在岗位档案中维护后再保存'
|
|
|
|
|
+ : '请先选择岗位';
|
|
|
|
|
+ }
|
|
|
|
|
+ callback(new Error(message));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ callback();
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: 'change',
|
|
|
|
|
+ }],
|
|
|
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }, { validator: validatePhone, trigger: 'blur' }],
|
|
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }, { validator: validatePhone, trigger: 'blur' }],
|
|
|
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }],
|
|
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }],
|
|
|
idCard: [{ validator: validateIdCard, trigger: ['blur', 'change'] }],
|
|
idCard: [{ validator: validateIdCard, trigger: ['blur', 'change'] }],
|
|
@@ -956,7 +993,8 @@ export default {
|
|
|
return Boolean(this.form.positionSequenceId);
|
|
return Boolean(this.form.positionSequenceId);
|
|
|
},
|
|
},
|
|
|
requiredProgress() {
|
|
requiredProgress() {
|
|
|
- const keys = ['name', 'employeeNo', 'gender', 'groupId', 'status', 'hireDate', 'positionId', 'phone'];
|
|
|
|
|
|
|
+ // 与 form rules 一一对应(含隶属部门 / 岗位序列 / 岗位性质 / 岗位层级),保证「必填 x/y」口径准确
|
|
|
|
|
+ const keys = ['name', 'employeeNo', 'gender', 'groupId', 'deptIds', 'status', 'hireDate', 'positionSequenceId', 'positionId', 'employmentType', 'staffingLevel', 'phone'];
|
|
|
return { base: `${keys.filter((key) => { const value = this.form[key]; return Array.isArray(value) ? value.length : Boolean(value); }).length}/${keys.length}` };
|
|
return { base: `${keys.filter((key) => { const value = this.form[key]; return Array.isArray(value) ? value.length : Boolean(value); }).length}/${keys.length}` };
|
|
|
},
|
|
},
|
|
|
sectionProgress() {
|
|
sectionProgress() {
|
|
@@ -1175,11 +1213,13 @@ export default {
|
|
|
// 部门岗位接口已给出层级下拉时,不再用岗位自带层级覆盖
|
|
// 部门岗位接口已给出层级下拉时,不再用岗位自带层级覆盖
|
|
|
if (!this.staffingLevelOptions.length) this.syncStaffingLevelsFromPosition();
|
|
if (!this.staffingLevelOptions.length) this.syncStaffingLevelsFromPosition();
|
|
|
});
|
|
});
|
|
|
|
|
+ /* 直属上级与部门/机构解耦(全员搜索):表单填充后预拉候选,
|
|
|
|
|
+ 确保编辑时已有直属上级能立刻回显「名字(工号)」而不是裸 id */
|
|
|
|
|
+ this.searchManagers('');
|
|
|
this.loadOrganizations().then(() => {
|
|
this.loadOrganizations().then(() => {
|
|
|
/* 新增员工默认带出所属机构(登录机构列表第一个) */
|
|
/* 新增员工默认带出所属机构(登录机构列表第一个) */
|
|
|
this.applyDefaultLegalEntity();
|
|
this.applyDefaultLegalEntity();
|
|
|
this.normalizeDeptAgainstOptions();
|
|
this.normalizeDeptAgainstOptions();
|
|
|
- this.loadManagerOptions({ keepSelection: true });
|
|
|
|
|
});
|
|
});
|
|
|
/* 所属工厂与机构、部门互不关联:并行加载,不等机构 */
|
|
/* 所属工厂与机构、部门互不关联:并行加载,不等机构 */
|
|
|
this.loadFactories().then(() => this.syncFactoryDisplay());
|
|
this.loadFactories().then(() => this.syncFactoryDisplay());
|
|
@@ -1739,7 +1779,7 @@ export default {
|
|
|
.filter(Boolean)
|
|
.filter(Boolean)
|
|
|
.join('、');
|
|
.join('、');
|
|
|
if (resetDependents) {
|
|
if (resetDependents) {
|
|
|
- this.loadManagerOptions({ keepSelection: false });
|
|
|
|
|
|
|
+ /* 直属上级不再随部门联动,这里只重置岗位 */
|
|
|
this.resetPositionsByDepartment();
|
|
this.resetPositionsByDepartment();
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
@@ -1776,17 +1816,15 @@ export default {
|
|
|
input.setAttribute('placeholder', want);
|
|
input.setAttribute('placeholder', want);
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- /* 所属机构切换:只重置依赖机构的部门 / 岗位 / 汇报对象级联。
|
|
|
|
|
- 所属工厂与机构、部门互不关联(工厂列表是独立的平铺数据),这里保持已选工厂不动 */
|
|
|
|
|
|
|
+ /* 所属机构切换:只重置依赖机构的部门 / 岗位级联。
|
|
|
|
|
+ 所属工厂与机构、部门互不关联(工厂列表是独立的平铺数据),这里保持已选工厂不动;
|
|
|
|
|
+ 直属上级已改为全员搜索,与机构/部门解耦,同样保持已选直属上级不动 */
|
|
|
handleLegalEntityChange(id) {
|
|
handleLegalEntityChange(id) {
|
|
|
const selected = this.legalEntities.find((item) => String(item.id) === String(id));
|
|
const selected = this.legalEntities.find((item) => String(item.id) === String(id));
|
|
|
this.form.legalEntity = selected?.name || '';
|
|
this.form.legalEntity = selected?.name || '';
|
|
|
this.form.company = selected?.name || this.form.company;
|
|
this.form.company = selected?.name || this.form.company;
|
|
|
this.deptCascaderKey += 1;
|
|
this.deptCascaderKey += 1;
|
|
|
this.applyDeptIds([]);
|
|
this.applyDeptIds([]);
|
|
|
- this.form.managerId = null;
|
|
|
|
|
- this.form.manager = '';
|
|
|
|
|
- this.managerOptions = [];
|
|
|
|
|
this.resetPositionsByDepartment();
|
|
this.resetPositionsByDepartment();
|
|
|
},
|
|
},
|
|
|
normalizeDeptAgainstOptions() {
|
|
normalizeDeptAgainstOptions() {
|
|
@@ -1796,6 +1834,12 @@ export default {
|
|
|
const valid = ids.filter((id) => this.findDeptNode(id));
|
|
const valid = ids.filter((id) => this.findDeptNode(id));
|
|
|
this.applyDeptIds(valid.length ? valid : ids);
|
|
this.applyDeptIds(valid.length ? valid : ids);
|
|
|
},
|
|
},
|
|
|
|
|
+ /** 直属上级下拉选项文案:名字(工号),无工号时只显示名字 */
|
|
|
|
|
+ managerOptionLabel(item = {}) {
|
|
|
|
|
+ const name = item.name || '未命名人员';
|
|
|
|
|
+ const jobNumber = item.jobNumber || item.employeeNo || '';
|
|
|
|
|
+ return jobNumber ? `${name}(${jobNumber})` : name;
|
|
|
|
|
+ },
|
|
|
handleManagerChange(id) {
|
|
handleManagerChange(id) {
|
|
|
const matched = this.managerOptions.find((item) => String(item.id) === String(id));
|
|
const matched = this.managerOptions.find((item) => String(item.id) === String(id));
|
|
|
this.form.managerId = id != null && id !== '' ? String(id) : null;
|
|
this.form.managerId = id != null && id !== '' ? String(id) : null;
|
|
@@ -1808,27 +1852,23 @@ export default {
|
|
|
if (exists) return;
|
|
if (exists) return;
|
|
|
this.managerOptions.unshift({
|
|
this.managerOptions.unshift({
|
|
|
id: String(id),
|
|
id: String(id),
|
|
|
- name: this.form.manager || '未命名人员'
|
|
|
|
|
|
|
+ name: this.form.manager || '未命名人员',
|
|
|
|
|
+ jobNumber: ''
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
- async loadManagerOptions({ keepSelection = false } = {}) {
|
|
|
|
|
- const deptId = this.form.deptId;
|
|
|
|
|
- if (!keepSelection) {
|
|
|
|
|
- this.form.managerId = null;
|
|
|
|
|
- this.form.manager = '';
|
|
|
|
|
- }
|
|
|
|
|
- if (!deptId) {
|
|
|
|
|
- this.managerOptions = [];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const requestKey = String(deptId);
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 直属上级:远程搜索全员(姓名/工号/手机号,searchUsersByKeyword 内部并行检索),
|
|
|
|
|
+ * 不再按隶属部门过滤——新员工尚未落部门或部门下无人时也能直接选人;
|
|
|
|
|
+ * 登录人组织范围由 getUserPage 自身权限控制,isQueryLZ=0 不带离职员工。
|
|
|
|
|
+ */
|
|
|
|
|
+ async searchManagers(keyword) {
|
|
|
|
|
+ const requestKey = String(keyword || '');
|
|
|
this._managerRequestKey = requestKey;
|
|
this._managerRequestKey = requestKey;
|
|
|
this.managerLoading = true;
|
|
this.managerLoading = true;
|
|
|
try {
|
|
try {
|
|
|
- const data = await getUserPage({
|
|
|
|
|
|
|
+ const data = await searchUsersByKeyword(keyword || '', {
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
- size: 99999,
|
|
|
|
|
- groupId: deptId,
|
|
|
|
|
|
|
+ size: 30,
|
|
|
isQueryLZ: 0
|
|
isQueryLZ: 0
|
|
|
});
|
|
});
|
|
|
if (this._managerRequestKey !== requestKey) return;
|
|
if (this._managerRequestKey !== requestKey) return;
|
|
@@ -1836,12 +1876,10 @@ export default {
|
|
|
this.managerOptions = (data?.list || [])
|
|
this.managerOptions = (data?.list || [])
|
|
|
.map((item) => ({
|
|
.map((item) => ({
|
|
|
id: item.id == null ? item.id : String(item.id),
|
|
id: item.id == null ? item.id : String(item.id),
|
|
|
- name: item.name || ''
|
|
|
|
|
|
|
+ name: item.name || '',
|
|
|
|
|
+ jobNumber: item.jobNumber || item.employeeNo || ''
|
|
|
}))
|
|
}))
|
|
|
.filter((item) => item.id != null && item.id !== '' && item.name && String(item.id) !== selfId);
|
|
.filter((item) => item.id != null && item.id !== '' && item.name && String(item.id) !== selfId);
|
|
|
- if (this.form.managerId != null && this.form.managerId !== '') {
|
|
|
|
|
- this.form.managerId = String(this.form.managerId);
|
|
|
|
|
- }
|
|
|
|
|
this.mergeCurrentManagerOption();
|
|
this.mergeCurrentManagerOption();
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
if (this._managerRequestKey !== requestKey) return;
|
|
if (this._managerRequestKey !== requestKey) return;
|
|
@@ -1853,6 +1891,12 @@ export default {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ /** 首次点开下拉时候选还是空的,补拉一次默认列表 */
|
|
|
|
|
+ ensureManagerOptions() {
|
|
|
|
|
+ if (!this.managerOptions.length && !this.managerLoading) {
|
|
|
|
|
+ this.searchManagers('');
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
async loadOrganizations() {
|
|
async loadOrganizations() {
|
|
|
try {
|
|
try {
|
|
|
const list = await listOrganizations();
|
|
const list = await listOrganizations();
|
|
@@ -2209,7 +2253,9 @@ export default {
|
|
|
this.syncEmploymentTypeName(this.form.employmentType);
|
|
this.syncEmploymentTypeName(this.form.employmentType);
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ // 岗位性质已改为必填:字典取不到就明确报错,不要静默留空导致下拉无选项、用户不知道原因
|
|
|
this.employmentOptions = [];
|
|
this.employmentOptions = [];
|
|
|
|
|
+ showRequestError(error, '岗位性质字典加载失败,请刷新重试');
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
/** 接口返回可能是数组 / {list|records|rows|data},统一取数组(与办理入职一致) */
|
|
/** 接口返回可能是数组 / {list|records|rows|data},统一取数组(与办理入职一致) */
|