|
|
@@ -1,7 +1,7 @@
|
|
|
<!-- 机构编辑弹窗 -->
|
|
|
<template>
|
|
|
<ele-modal
|
|
|
- width="880px"
|
|
|
+ width="1080px"
|
|
|
:visible="visible"
|
|
|
:close-on-click-modal="false"
|
|
|
custom-class="ele-dialog-form"
|
|
|
@@ -241,6 +241,27 @@
|
|
|
class="dept-position-table"
|
|
|
>
|
|
|
<el-table-column type="index" label="序号" width="80" align="center" />
|
|
|
+ <el-table-column label="岗位序列" min-width="140" align="center">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-if="!row.id"
|
|
|
+ v-model="row.positionSequenceId"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="onDeptPositionSequenceChange(row)"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in positionSequenceList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.sequence"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <span v-else>{{ row.positionSequence }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="岗位名称" min-width="160" align="center">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-select
|
|
|
@@ -250,12 +271,13 @@
|
|
|
clearable
|
|
|
placeholder="请选择"
|
|
|
style="width: 100%"
|
|
|
+ :disabled="!row.positionSequenceId"
|
|
|
@change="onDeptPositionNameChange(row)"
|
|
|
>
|
|
|
<el-option
|
|
|
- v-for="item in positionList"
|
|
|
+ v-for="item in getPositionsBySequence(row.positionSequenceId)"
|
|
|
:key="item.id"
|
|
|
- :label="item.positionName"
|
|
|
+ :label="formatPositionLabel(item)"
|
|
|
:value="item.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
@@ -284,6 +306,27 @@
|
|
|
<span v-else>{{ row.positionLevel }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <el-table-column label="岗位性质" min-width="140" align="center">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-if="!row.id"
|
|
|
+ v-model="row.positionType"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="onDeptPositionTypeChange(row)"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in positionNatureOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <span v-else>{{ row.positionTypeName }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="编制人数" min-width="120" align="center">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-input-number
|
|
|
@@ -465,7 +508,9 @@
|
|
|
saveDepartmentPositionBatch,
|
|
|
deleteDepartmentPosition,
|
|
|
getHrPositionPage,
|
|
|
- getHrPositionById
|
|
|
+ getHrPositionById,
|
|
|
+ getPositionSequenceList,
|
|
|
+ getPositionNatureDict
|
|
|
} from '@/api/system/organization';
|
|
|
import { basicAreaPageAPI } from '@/api/regionalManage';
|
|
|
import { cityDataLabel } from 'ele-admin/packages/utils/regions';
|
|
|
@@ -544,7 +589,9 @@
|
|
|
// 是否是修改
|
|
|
isUpdate: false,
|
|
|
departmentPositionList: [],
|
|
|
- positionList: []
|
|
|
+ positionList: [],
|
|
|
+ positionSequenceList: [],
|
|
|
+ positionNatureOptions: []
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -615,6 +662,10 @@
|
|
|
id: null,
|
|
|
positionId: '',
|
|
|
positionName: '',
|
|
|
+ positionSequenceId: '',
|
|
|
+ positionSequence: '',
|
|
|
+ positionType: '',
|
|
|
+ positionTypeName: '',
|
|
|
positionLevel: '',
|
|
|
positionLevelId: '',
|
|
|
establishmentCount: undefined,
|
|
|
@@ -656,6 +707,10 @@
|
|
|
deptId: this.form.id,
|
|
|
positionId: row.positionId,
|
|
|
positionName: row.positionName,
|
|
|
+ positionSequenceId: row.positionSequenceId,
|
|
|
+ positionSequence: row.positionSequence,
|
|
|
+ positionType: row.positionType,
|
|
|
+ positionTypeName: row.positionTypeName,
|
|
|
positionLevelId: row.positionLevelId,
|
|
|
positionLevel: row.positionLevel,
|
|
|
establishmentCount: Number(row.establishmentCount)
|
|
|
@@ -703,35 +758,157 @@
|
|
|
} catch (e) {}
|
|
|
},
|
|
|
async ensurePositionOptions() {
|
|
|
- if (this.positionList.length) {
|
|
|
- return;
|
|
|
+ // 防重:并发调用复用同一 Promise,避免接口被请求两次
|
|
|
+ if (this._ensurePositionPromise) {
|
|
|
+ return this._ensurePositionPromise;
|
|
|
}
|
|
|
- const res = await getHrPositionPage({
|
|
|
- pageNum: 1,
|
|
|
- size: 9999,
|
|
|
- status: 1
|
|
|
- });
|
|
|
- this.positionList = (res && res.list) || [];
|
|
|
+ this._ensurePositionPromise = (async () => {
|
|
|
+ try {
|
|
|
+ if (!this.positionList.length) {
|
|
|
+ const res = await getHrPositionPage({
|
|
|
+ pageNum: 1,
|
|
|
+ size: 9999,
|
|
|
+ status: 1
|
|
|
+ });
|
|
|
+ this.positionList = (res && res.list) || [];
|
|
|
+ }
|
|
|
+ if (!this.positionSequenceList.length) {
|
|
|
+ try {
|
|
|
+ const res = await getPositionSequenceList();
|
|
|
+ this.positionSequenceList = (Array.isArray(res)
|
|
|
+ ? res
|
|
|
+ : []
|
|
|
+ ).filter((item) => Number(item.status) === 1);
|
|
|
+ } catch (e) {
|
|
|
+ this.positionSequenceList = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 加载岗位性质字典(无静态兜底)
|
|
|
+ if (!this.positionNatureOptions.length) {
|
|
|
+ try {
|
|
|
+ const res = await getPositionNatureDict();
|
|
|
+ this.positionNatureOptions = Array.isArray(res) ? res : [];
|
|
|
+ } catch (e) {
|
|
|
+ this.positionNatureOptions = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this._ensurePositionPromise = null;
|
|
|
+ }
|
|
|
+ })();
|
|
|
+ return this._ensurePositionPromise;
|
|
|
},
|
|
|
async onDeptPositionNameChange(row) {
|
|
|
row.positionLevel = '';
|
|
|
row.positionLevelId = '';
|
|
|
row.levelOptions = [];
|
|
|
+ row.positionSequenceId = '';
|
|
|
+ row.positionSequence = '';
|
|
|
+ row.positionType = '';
|
|
|
+ row.positionTypeName = '';
|
|
|
const position = this.positionList.find(
|
|
|
(item) => item.id === row.positionId
|
|
|
);
|
|
|
row.positionName = position ? position.positionName : '';
|
|
|
+ // 自动带出岗位序列 id+名称
|
|
|
+ if (position && (position.positionSequenceId || position.sequenceId)) {
|
|
|
+ row.positionSequenceId = position.positionSequenceId || position.sequenceId;
|
|
|
+ row.positionSequence =
|
|
|
+ position.positionSequence || position.sequenceName || '';
|
|
|
+ }
|
|
|
if (!row.positionId) {
|
|
|
return;
|
|
|
}
|
|
|
const detail = await getHrPositionById(row.positionId);
|
|
|
+ // levelConfigs 项层级id可能是 positionLevelId 或 id,层级名可能是 positionLevel 或 level
|
|
|
const configs = (detail && detail.levelConfigs) || [];
|
|
|
row.levelOptions = configs
|
|
|
- .filter((item) => item.positionLevelId)
|
|
|
+ .filter(
|
|
|
+ (item) =>
|
|
|
+ (item.positionLevelId ?? item.id) &&
|
|
|
+ (item.positionLevel || item.level)
|
|
|
+ )
|
|
|
.map((item) => ({
|
|
|
- positionLevelId: item.positionLevelId,
|
|
|
- positionLevel: item.positionLevel
|
|
|
+ positionLevelId: item.positionLevelId ?? item.id,
|
|
|
+ positionLevel: item.positionLevel || item.level || ''
|
|
|
}));
|
|
|
+ // 详情接口的岗位序列覆盖列表值
|
|
|
+ if (detail && (detail.positionSequenceId || detail.sequenceId)) {
|
|
|
+ row.positionSequenceId = detail.positionSequenceId || detail.sequenceId;
|
|
|
+ row.positionSequence =
|
|
|
+ detail.positionSequence || detail.sequenceName || '';
|
|
|
+ }
|
|
|
+ // 详情/列表的岗位性质可能是 id 或名称,通过字典反查统一为 id+名称
|
|
|
+ const typeRaw =
|
|
|
+ (detail && detail.positionType) ||
|
|
|
+ (position && position.positionType) ||
|
|
|
+ '';
|
|
|
+ if (typeRaw) {
|
|
|
+ if (!this.positionNatureOptions.length) {
|
|
|
+ await this.ensurePositionOptions();
|
|
|
+ }
|
|
|
+ const matched = this.positionNatureOptions.find(
|
|
|
+ (item) =>
|
|
|
+ String(item.id) === String(typeRaw) || item.name === typeRaw
|
|
|
+ );
|
|
|
+ if (matched) {
|
|
|
+ row.positionType = matched.id;
|
|
|
+ row.positionTypeName = matched.name;
|
|
|
+ } else {
|
|
|
+ row.positionType = typeRaw;
|
|
|
+ row.positionTypeName =
|
|
|
+ (detail && detail.positionTypeName) ||
|
|
|
+ (position && position.positionTypeName) ||
|
|
|
+ '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onDeptPositionSequenceChange(row) {
|
|
|
+ // 选中序列时同步名称
|
|
|
+ const seq = this.positionSequenceList.find(
|
|
|
+ (item) => item.id === row.positionSequenceId
|
|
|
+ );
|
|
|
+ row.positionSequence = seq ? seq.sequence : '';
|
|
|
+ // 切换序列时清空岗位名称、层级、性质
|
|
|
+ row.positionId = '';
|
|
|
+ row.positionName = '';
|
|
|
+ row.positionLevel = '';
|
|
|
+ row.positionLevelId = '';
|
|
|
+ row.positionType = '';
|
|
|
+ row.positionTypeName = '';
|
|
|
+ row.levelOptions = [];
|
|
|
+ },
|
|
|
+ onDeptPositionTypeChange(row) {
|
|
|
+ // 选中岗位性质时同步名称
|
|
|
+ const type = this.positionNatureOptions.find(
|
|
|
+ (item) => item.id === row.positionType
|
|
|
+ );
|
|
|
+ row.positionTypeName = type ? type.name : '';
|
|
|
+ },
|
|
|
+ // 按岗位序列过滤岗位名称选项(未选序列时返回全部)
|
|
|
+ getPositionsBySequence(sequenceId) {
|
|
|
+ if (!sequenceId) return this.positionList;
|
|
|
+ return this.positionList.filter(
|
|
|
+ (item) =>
|
|
|
+ String(item.positionSequenceId ?? item.sequenceId ?? '') ===
|
|
|
+ String(sequenceId)
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 岗位名称拼接岗位性质:测试人员(全职)
|
|
|
+ formatPositionLabel(item) {
|
|
|
+ if (!item) return '';
|
|
|
+ let type = item.positionTypeName || '';
|
|
|
+ if (!type && item.positionType) {
|
|
|
+ const matched = this.positionNatureOptions.find(
|
|
|
+ (m) =>
|
|
|
+ String(m.id) === String(item.positionType) ||
|
|
|
+ m.name === item.positionType
|
|
|
+ );
|
|
|
+ type = matched ? matched.name : '';
|
|
|
+ }
|
|
|
+ return type
|
|
|
+ ? `${item.positionName}(${type})`
|
|
|
+ : item.positionName;
|
|
|
},
|
|
|
onDeptPositionLevelChange(row) {
|
|
|
const level = (row.levelOptions || []).find(
|
|
|
@@ -744,20 +921,40 @@
|
|
|
this.departmentPositionList = [];
|
|
|
return;
|
|
|
}
|
|
|
- await this.ensurePositionOptions();
|
|
|
- const res = await getDepartmentPositionPage({
|
|
|
- deptId: this.form.id,
|
|
|
- pageNum: 1,
|
|
|
- size: 9999
|
|
|
- });
|
|
|
- this.departmentPositionList = ((res && res.list) || []).map((item) => ({
|
|
|
- ...item,
|
|
|
- levelOptions: []
|
|
|
- }));
|
|
|
+ // 防重:visible 与 form.type watch 会并发触发,复用同一请求
|
|
|
+ const deptId = this.form.id;
|
|
|
+ if (
|
|
|
+ this._loadDeptPromise &&
|
|
|
+ this._loadDeptId === deptId
|
|
|
+ ) {
|
|
|
+ return this._loadDeptPromise;
|
|
|
+ }
|
|
|
+ this._loadDeptId = deptId;
|
|
|
+ this._loadDeptPromise = (async () => {
|
|
|
+ try {
|
|
|
+ await this.ensurePositionOptions();
|
|
|
+ const res = await getDepartmentPositionPage({
|
|
|
+ deptId,
|
|
|
+ pageNum: 1,
|
|
|
+ size: 9999
|
|
|
+ });
|
|
|
+ this.departmentPositionList = ((res && res.list) || []).map(
|
|
|
+ (item) => ({
|
|
|
+ ...item,
|
|
|
+ levelOptions: []
|
|
|
+ })
|
|
|
+ );
|
|
|
+ } finally {
|
|
|
+ this._loadDeptPromise = null;
|
|
|
+ }
|
|
|
+ })();
|
|
|
+ return this._loadDeptPromise;
|
|
|
},
|
|
|
resetDepartmentPosition() {
|
|
|
this.departmentPositionList = [];
|
|
|
this.positionList = [];
|
|
|
+ this.positionSequenceList = [];
|
|
|
+ this.positionNatureOptions = [];
|
|
|
},
|
|
|
formValidate() {
|
|
|
return new Promise((resolve, reject) => {
|