|
|
@@ -321,60 +321,59 @@
|
|
|
:rules="formRules"
|
|
|
v-loading="formLoading"
|
|
|
label-width="auto"
|
|
|
+ require-asterisk
|
|
|
>
|
|
|
<el-row :gutter="20">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="部门" prop="deptId">
|
|
|
- <el-tree-select
|
|
|
- clearable
|
|
|
- v-model="formData.deptId"
|
|
|
- :data="deptList2"
|
|
|
- :props="defaultProps"
|
|
|
- :check-strictly="false"
|
|
|
- node-key="id"
|
|
|
- filterable
|
|
|
- placeholder="请选择所在部门"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
-
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="专家姓名" prop="expertName">
|
|
|
- <el-input v-model="formData.expertName" placeholder="请输入专家姓名" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="专家序列" prop="expertRank">
|
|
|
- <el-select
|
|
|
- multiple
|
|
|
- v-model="formData.expertRank"
|
|
|
- placeholder="请选择专家序列"
|
|
|
- clearable
|
|
|
+ <el-form-item label="序列" prop="rankEchelonMappings">
|
|
|
+ <div class="rank-echelons">
|
|
|
+ <div
|
|
|
+ v-for="(mapping, index) in formData.rankEchelonMappings"
|
|
|
+ :key="index"
|
|
|
+ class="rank-echelon-row"
|
|
|
>
|
|
|
- <el-option
|
|
|
- v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_RANK)"
|
|
|
- :key="dict.value"
|
|
|
- :label="dict.label"
|
|
|
- :value="Number(dict.value)"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="专家梯队" prop="echelon">
|
|
|
- <el-select v-model="formData.echelon" placeholder="请选择专家梯队" clearable>
|
|
|
- <el-option
|
|
|
- v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_ECHELON)"
|
|
|
- :key="dict.value"
|
|
|
- :label="dict.label"
|
|
|
- :value="dict.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
+ <el-select
|
|
|
+ style="width: 335px"
|
|
|
+ v-model="mapping.expertRank"
|
|
|
+ placeholder="请选择专家序列"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_RANK)"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="dict.label"
|
|
|
+ :value="Number(dict.value)"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <span class="rank-echelon-arrow" style="padding-left: 20px">选择对应梯队</span>
|
|
|
+ <el-select
|
|
|
+ v-model="mapping.echelon"
|
|
|
+ placeholder="请选择专家梯队"
|
|
|
+ clearable
|
|
|
+ style="width: 325px"
|
|
|
+ class="rank-echelon-select"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_ECHELON)"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="dict.label"
|
|
|
+ :value="dict.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <el-button
|
|
|
+ v-if="formData.rankEchelonMappings.length > 1"
|
|
|
+ type="danger"
|
|
|
+ link
|
|
|
+ class="rank-echelon-remove"
|
|
|
+ @click="removeRankEchelonMapping(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" link @click="addRankEchelonMapping">
|
|
|
+ <Icon icon="ep:plus" class="mr-5px" />添加序列
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
</el-row>
|
|
|
|
|
|
<el-row :gutter="20">
|
|
|
@@ -598,6 +597,7 @@ const defaultFormData = () => ({
|
|
|
expertName: '',
|
|
|
achievement: '',
|
|
|
echelon: '',
|
|
|
+ rankEchelonMappings: [{ expertRank: undefined, echelon: '' }],
|
|
|
education: '',
|
|
|
specialDirection: [],
|
|
|
softwareSkills: [],
|
|
|
@@ -641,7 +641,20 @@ const currentExpertDetail = ref<any>(null)
|
|
|
const formRules = {
|
|
|
expertName: [{ required: true, message: '请输入专家名称', trigger: 'blur' }],
|
|
|
// achievement: [{ required: true, message: '请输入成果', trigger: 'blur' }],
|
|
|
- echelon: [{ required: true, message: '请选择专家层级', trigger: 'blur' }],
|
|
|
+ rankEchelonMappings: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ validator: (
|
|
|
+ _rule: any,
|
|
|
+ value: Array<{ expertRank?: number; echelon?: string }>,
|
|
|
+ callback: any
|
|
|
+ ) => {
|
|
|
+ const valid = value?.length > 0 && value.every((item) => item.expertRank && item.echelon)
|
|
|
+ callback(valid ? undefined : new Error('请完整选择序列和梯队'))
|
|
|
+ },
|
|
|
+ trigger: 'change'
|
|
|
+ }
|
|
|
+ ],
|
|
|
education: [{ required: true, message: '请选择专家学历', trigger: 'blur' }],
|
|
|
specialDirection: [{ required: true, message: '请选择专业方向', trigger: 'blur' }],
|
|
|
// softwareSkills: [{ required: true, message: '请选择专业软件技能', trigger: 'blur' }],
|
|
|
@@ -649,11 +662,10 @@ const formRules = {
|
|
|
// school: [{ required: true, message: '请输入毕业院校', trigger: 'blur' }],
|
|
|
// birthday: [{ required: true, message: '请选择出生日期', trigger: 'blur' }],
|
|
|
// avatar: [{ required: true, message: '请上传头像', trigger: 'blur' }],
|
|
|
- deptId: [{ required: true, message: '请选择部门', trigger: 'blur' }],
|
|
|
+ deptId: [{ required: true, message: '请选择部门', trigger: 'blur' }]
|
|
|
// formerPost: [{ required: true, message: '请输入历任岗位', trigger: 'blur' }],
|
|
|
// jobTitle: [{ required: true, message: '请输入岗位', trigger: 'blur' }],
|
|
|
// professionalTitle: [{ required: true, message: '请输入职称', trigger: 'blur' }],
|
|
|
- expertRank: [{ required: true, message: '请选择专家序列', trigger: 'blur' }]
|
|
|
}
|
|
|
|
|
|
// 重置表单
|
|
|
@@ -676,6 +688,32 @@ const fillTeamSelection = (teams: any[] = []) => {
|
|
|
tempSelectedTeamMap.value = Object.fromEntries(teams.map((team) => [team.id, team]))
|
|
|
}
|
|
|
|
|
|
+const addRankEchelonMapping = () => {
|
|
|
+ formData.value.rankEchelonMappings.push({ expertRank: undefined, echelon: '' })
|
|
|
+}
|
|
|
+
|
|
|
+const removeRankEchelonMapping = (index: number) => {
|
|
|
+ if (formData.value.rankEchelonMappings.length <= 1) return
|
|
|
+ formData.value.rankEchelonMappings.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+const getRankEchelonMappings = (detail: any) => {
|
|
|
+ if (detail?.rankEchelonMappings?.length) {
|
|
|
+ return detail.rankEchelonMappings.map((item: any) => ({
|
|
|
+ expertRank: Number(item.expertRank),
|
|
|
+ echelon: item.echelon || ''
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ const expertRanks = detail?.expertRank || []
|
|
|
+ return expertRanks.length
|
|
|
+ ? expertRanks.map((expertRank: number) => ({
|
|
|
+ expertRank: Number(expertRank),
|
|
|
+ echelon: detail?.echelon || ''
|
|
|
+ }))
|
|
|
+ : [{ expertRank: undefined, echelon: detail?.echelon || '' }]
|
|
|
+}
|
|
|
+
|
|
|
const buildFormDataFromExpertDetail = (detail: any) => {
|
|
|
const teams = detail?.teams || []
|
|
|
fillTeamSelection(teams)
|
|
|
@@ -684,6 +722,7 @@ const buildFormDataFromExpertDetail = (detail: any) => {
|
|
|
expertName: detail?.expertName || '',
|
|
|
achievement: detail?.achievement || '',
|
|
|
echelon: detail?.echelon || '',
|
|
|
+ rankEchelonMappings: getRankEchelonMappings(detail),
|
|
|
education: detail?.education || '',
|
|
|
specialDirection: detail?.specialDirection || [],
|
|
|
softwareSkills: detail?.softwareSkills || [],
|
|
|
@@ -1199,8 +1238,14 @@ const submitExpertForm = async () => {
|
|
|
|
|
|
submitLoading.value = true
|
|
|
try {
|
|
|
+ const rankEchelonMappings = formData.value.rankEchelonMappings.filter(
|
|
|
+ (item) => item.expertRank && item.echelon
|
|
|
+ )
|
|
|
const payload = {
|
|
|
...formData.value,
|
|
|
+ expertRank: rankEchelonMappings.map((item) => item.expertRank),
|
|
|
+ echelon: rankEchelonMappings[0]?.echelon || '',
|
|
|
+ rankEchelonMappings,
|
|
|
avatar:
|
|
|
typeof formData.value.avatar === 'string'
|
|
|
? formData.value.avatar
|
|
|
@@ -1278,6 +1323,36 @@ onMounted(async () => {
|
|
|
flex: 1;
|
|
|
}
|
|
|
|
|
|
+.rank-echelons {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 10px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.rank-echelon-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.rank-echelon-select {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.rank-echelon-arrow {
|
|
|
+ flex: 0 0 auto;
|
|
|
+ color: #7a8a89;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+
|
|
|
+.rank-echelon-remove {
|
|
|
+ flex: 0 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
.team-picker-table-wrap {
|
|
|
margin-top: 8px;
|
|
|
// margin-bottom: 12px;
|