辅导员管理-添加业绩考核个人填报详情和加分项、就业指导工作API

- 新增kpiFillingDetail函数用于获取业绩考核个人填报详情
- 新增加分项相关API:kpiFillingBonusPointsAdd、kpiFillingBonusPointsUpdate、
  kpiFillingBonusPointsDetail
- 新增就业指导工作相关API:kpiFillingGraduationGuidanceAdd、
  kpiFillingGraduationGuidanceUpdate、kpiFillingGraduationGuidanceDetail
- 添加TODO注释标记待后端API完成的功能

fix(pages): 解决部门名称存储问题

- 启用被注释掉的部门名称存储功能
- 确保deptName正确存入本地缓存

feat(performance): 支持毕业班和非毕业班不同考核标准

- 为考勤管理组件添加classType参数支持
- 为负面清单组件添加classType参数支持
- 为专业工作组件重构标签显示逻辑,支持根据classType动态显示
- 为奖励绩效加班组件添加classType参数支持
- 为学生突发事件组件添加classType参数支持
- 为学生管理组件添加毕业班/非毕业班差异化显示逻辑

refactor(performance): 优化业绩评估页面结构

- 添加班级类型选择按钮(毕业班/非毕业班)
- 在填报时间弹窗中集成班级类型选择功能
- 更新数据加载逻辑以支持classType参数
- 修正各种评分计算中的数值类型转换问题
```
This commit is contained in:
2026-03-13 15:14:29 +08:00
parent e1b2a84030
commit ecc0d00a4f
13 changed files with 788 additions and 154 deletions

View File

@@ -77,37 +77,42 @@
+
</view>
<uni-popup @change="popupChange" ref="popupTime" class="popup-time" background-color="#ffffff">
<view class="popup-content">
<view class="title">
填报时间
<view class="popup-content">
<view class="title">
填报时间
</view>
<view class="class-type-btns">
<button :class="['type-btn', classType === 'graduate' ? 'active' : '']" @tap="selectClassType('graduate')">毕业班</button>
<button :class="['type-btn', classType === 'ungraduate' ? 'active' : '']" @tap="selectClassType('ungraduate')">非毕业班</button>
</view>
<view class="type-tip">必选</view>
<form @submit="topersonalReporting">
<view class="form-item">
<label>填报年份</label>
<picker v-if="years.length>0" range-key="dictValue" :value="yearIndex" :range="years"
@change="yearChange" name="year">
<view class="uni-input">
<text class="val">{{years[yearIndex].dictValue}}</text>
<uni-icons type="calendar" size="25" color="#202020"></uni-icons>
</view>
</picker>
</view>
<view class="form-item">
<label>选择月份</label>
<picker v-if="months.length>0" name="month" range-key="dictValue" @change="monthChange"
:value="monthIndex" :range="months">
<view class="uni-input">
<text class="val">{{months[monthIndex].dictValue}}</text>
<uni-icons type="calendar" size="25" color="#202020"></uni-icons>
</view>
</picker>
</view>
<view class="btns">
<button type="default" @tap="onCancel">取消</button>
<button form-type="submit" type="primary">确定</button>
</view>
</form>
</view>
<form @submit="topersonalReporting">
<view class="form-item">
<label>填报年份</label>
<picker v-if="years.length>0" range-key="dictValue" :value="yearIndex" :range="years"
@change="yearChange" name="year">
<view class="uni-input">
<text class="val">{{years[yearIndex].dictValue}}</text>
<uni-icons type="calendar" size="25" color="#202020"></uni-icons>
</view>
</picker>
</view>
<view class="form-item">
<label>选择月份</label>
<picker v-if="months.length>0" name="month" range-key="dictValue" @change="monthChange"
:value="monthIndex" :range="months">
<view class="uni-input">
<text class="val">{{months[monthIndex].dictValue}}</text>
<uni-icons type="calendar" size="25" color="#202020"></uni-icons>
</view>
</picker>
</view>
<view class="btns">
<button type="default" @tap="onCancel">取消</button>
<button form-type="submit" type="primary">确定</button>
</view>
</form>
</view>
</uni-popup>
<uni-popup ref="popupAuditStatus" class="popup-audit " background-color="#ffffff">
<view class="popup-content">
@@ -146,6 +151,7 @@
export default {
data() {
return {
classType: "graduate", // graduate-毕业班, ungraduate-非毕业班
topLoading: true,
loading: false,
type: 'center',
@@ -250,7 +256,8 @@
month: item.fillingMonth,
reportingId: item.id,
commitStatus: item.commitStatus,
roleAudit: item.roleAudit
roleAudit: item.roleAudit,
classType: item.classType || 'ungraduate'
});
if (item.auditStatus == 2) {
let refuseTxt = "";
@@ -292,30 +299,51 @@
// 学生管理分数
row.kpiFillingStuMgtList.forEach(element => {
if (element.hasOwnProperty('id')) {
scoring += element.cadreScoring
scoring += element.classScoring
scoring += element.stuActivityScoring
scoring += element.stuTalkScoring
scoring += element.visitDormitoryScoring
scoring += Number(element.cadreScoring) || 0
scoring += Number(element.classScoring) || 0
scoring += Number(element.stuActivityScoring) || 0
scoring += Number(element.stuTalkScoring) || 0
scoring += Number(element.visitDormitoryScoring) || 0
scoring += Number(element.noticeScoring) || 0
scoring += Number(element.edgScoring) || 0
}
});
// 业务工作分数
row.kpiFillingBusinessWorksList.forEach(element => {
if (element.hasOwnProperty('id')) {
scoring += element.stuLeaveMaterialsScoring
scoring += element.stuFillingMaterialsScoring
scoring += element.stuBasicDataScoring
scoring += element.stuDisciplinaryViolationScoring
scoring += element.handleEventsScoring
scoring += Number(element.stuLeaveMaterialsScoring) || 0
scoring += Number(element.stuFillingMaterialsScoring) || 0
scoring += Number(element.stuBasicDataScoring) || 0
scoring += Number(element.stuDisciplinaryViolationScoring) || 0
scoring += Number(element.handleEventsScoring) || 0
scoring += Number(element.otherTaskScoring) || 0
}
});
// 考勤管理分数
row.kpiFillingAMgtList.forEach(element => {
if (element.hasOwnProperty('id')) {
scoring += element.dutyWorkScoring
scoring += element.conferenceScoring
scoring += Number(element.dutyWorkScoring) || 0
scoring += Number(element.conferenceScoring) || 0
}
});
// 加分项分数
if (row.kpiFillingBonusPointsList) {
row.kpiFillingBonusPointsList.forEach(element => {
if (element.hasOwnProperty('id')) {
scoring += Number(element.bonusScoring) || 0
}
});
}
// 就业指导工作分数
if (row.kpiFillingGraduationGuidanceList) {
row.kpiFillingGraduationGuidanceList.forEach(element => {
if (element.hasOwnProperty('id')) {
scoring += Number(element.gradFormAuditScoring) || 0
scoring += Number(element.stuCareerConsultScoring) || 0
scoring += Number(element.gradFormGuidanceScoring) || 0
}
});
}
return scoring
},
scrolltolower() {
@@ -391,7 +419,17 @@
addRecordsPopup() {
this.$refs.popupTime.open(this.type);
},
selectClassType(type) {
this.classType = type;
},
async topersonalReporting(e) {
if (!this.classType) {
uni.showToast({
title: '请选择班级类型',
icon: 'none'
});
return;
}
this.$refs.popupTime.close();
let year = this.years[e.detail.value.year + 1].dictValue;
let month = this.months[e.detail.value.month].dictValue;
@@ -399,6 +437,7 @@
let formData = {
fillingYear: year,
fillingMonth: month,
classType: this.classType,
}
let res = await kpiFillingAdd(formData);
if (res.code == 200) {
@@ -407,7 +446,8 @@
month,
commitStatus: 0,
reportingId: res.data.id,
completionStatus: 0
completionStatus: 0,
classType: this.classType,
});
uni.navigateTo({
url: `/pages/instructor/performance-appraisal/performance-evaluation/personal-reporting?${searchParams.toString()}`
@@ -416,12 +456,14 @@
},
toPersonalReportingPage(params) {
console.log('toPersonalReportingPage params:', params);
const searchParams = new URLSearchParams({
year: params.fillingYear,
month: params.fillingMonth,
reportingId: params.id,
commitStatus: params.commitStatus,
completionStatus: params.completionStatus
completionStatus: params.completionStatus,
classType: params.classType || 'ungraduate'
});
uni.navigateTo({
@@ -702,7 +744,7 @@
.popup-content {
width: 600rpx;
height: 600rpx;
height: 680rpx;
padding: 40rpx 50rpx 40rpx;
.title {
@@ -748,6 +790,35 @@
}
}
.class-type-btns {
display: flex;
justify-content: center;
margin: 30rpx 0;
.type-btn {
flex: 1;
margin: 0 20rpx;
height: 70rpx;
line-height: 70rpx;
background-color: #f0f0f0;
color: #666;
border-radius: 10rpx;
font-size: 28rpx;
&.active {
background-color: #1890FF;
color: #fff;
}
}
}
.type-tip {
text-align: center;
color: #ff4d4f;
font-size: 24rpx;
margin-bottom: 20rpx;
}
.btns {
display: flex;
margin-top: 50rpx;

View File

@@ -6,18 +6,20 @@
:key="index">{{item}}</text>
</view>
</scroll-view>
<view class="form">
<StudentManagement :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" ref="studentManagement" v-if="tabIndex==0" />
<ProfessionalWork :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" ref="ProfessionalWork" v-if="tabIndex==1" />
<AttendanceManagement :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" ref="AttendanceManagement"
v-if="tabIndex==2" />
<NegativeList :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" ref="NegativeList" v-if="tabIndex==3" />
<OverWork :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" ref="OverWork" v-if="tabIndex==4" />
<StudentEmergencies :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" ref="StudentEmergencies" v-if="tabIndex==5" />
<view class="form" v-if="!loading">
<StudentManagement :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="studentManagement" v-if="tabIndex==0" :key="classType" />
<GraduationGuidance :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="GraduationGuidance" v-if="classType === 'graduate' && tabIndex==1" :key="classType" />
<ProfessionalWork :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="ProfessionalWork" v-if="(classType === 'graduate' && tabIndex==2) || (classType !== 'graduate' && tabIndex==1)" :key="classType" />
<AttendanceManagement :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="AttendanceManagement"
v-if="(classType === 'graduate' && tabIndex==3) || (classType !== 'graduate' && tabIndex==2)" :key="classType" />
<BonusPoints :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="BonusPoints" v-if="(classType === 'graduate' && tabIndex==4) || (classType !== 'graduate' && tabIndex==3)" :key="classType" />
<NegativeList :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="NegativeList" v-if="(classType === 'graduate' && tabIndex==5) || (classType !== 'graduate' && tabIndex==4)" :key="classType" />
<OverWork :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="OverWork" v-if="(classType === 'graduate' && tabIndex==6) || (classType !== 'graduate' && tabIndex==5)" :key="classType" />
<StudentEmergencies :commitStatus="commitStatus" :queryDetailParams="queryDetailParams" :classType="classType" ref="StudentEmergencies" v-if="(classType === 'graduate' && tabIndex==7) || (classType !== 'graduate' && tabIndex==6)" :key="classType" />
<view class="btns" v-if="commitStatus==0">
<button type="primary" class="prev-page" v-if="tabIndex!=0" @click="onPrevPage">上一页</button>
<button type="primary" class="next-page" @click="onNextPage" v-if="tabIndex!=5">保存</button>
<button type="primary" class="next-page" @click="onSubmit" v-if="tabIndex==5">提交</button>
<button type="primary" class="next-page" @click="onNextPage" v-if="(classType === 'graduate' && tabIndex!=7) || (classType !== 'graduate' && tabIndex!=6)">保存</button>
<button type="primary" class="next-page" @click="onSubmit" v-if="(classType === 'graduate' && tabIndex==7) || (classType !== 'graduate' && tabIndex==6)">提交</button>
</view>
</view>
</view>
@@ -26,6 +28,7 @@
<script>
import {
kpiFillingUpdate,
kpiFillingDetail,
teacherKpiFillingMgtAdd,
teacherKpiFillingMgtUpdate,
teacherKpiFillingMgtDetail,
@@ -34,7 +37,11 @@
kpiFillingAMgtAdd,
kpiFillingAMgtUpdate,
kpiFillingNegativeListAdd,
kpiFillingNegativeListUpdate
kpiFillingNegativeListUpdate,
kpiFillingBonusPointsAdd,
kpiFillingBonusPointsUpdate,
kpiFillingGraduationGuidanceAdd,
kpiFillingGraduationGuidanceUpdate
} from "@/api/instructor/superintendent.js"
import StudentManagement from "../components/student-management.vue";
import ProfessionalWork from "../components/professional-work.vue";
@@ -42,6 +49,8 @@
import NegativeList from "../components/negative-list.vue";
import OverWork from "../components/reward-performance-overwork.vue";
import StudentEmergencies from "../components/student-emergencies.vue";
import BonusPoints from "../components/bonuspoints.vue"
import GraduationGuidance from "../components/graduation-guidance.vue"
export default {
components: {
StudentManagement,
@@ -50,15 +59,18 @@
NegativeList,
OverWork,
StudentEmergencies,
BonusPoints,
GraduationGuidance,
reportingId: ""
},
data() {
return {
tabs: ["学生管理", "业务管理", "考勤管理", "负面清单", "超工作量奖励绩效", "处理学生突发事件"],
tabIndex: 0,
fillingYear: "",
fillingMonth: "",
queryDetailParams: {
fillingYear: "",
fillingMonth: "",
classType: "ungraduate",
loading: true,
queryDetailParams: {
fdyName: uni.getStorageSync("stuName"),
fillingMonth: "",
fillingYear: ""
@@ -76,30 +88,49 @@
updateFunc: teacherKpiFillingMgtUpdate,
},
1: {
ref: 'GraduationGuidance',
addFunc: kpiFillingGraduationGuidanceAdd,
updateFunc: kpiFillingGraduationGuidanceUpdate,
},
2: {
ref: 'ProfessionalWork',
addFunc: kpiFillingBusinessWorkAdd,
updateFunc: kpiFillingBusinessWorkUpdate,
},
2: {
3: {
ref: 'AttendanceManagement',
addFunc: kpiFillingAMgtAdd,
updateFunc: kpiFillingAMgtUpdate,
},
3: {
4: {
ref: 'BonusPoints',
addFunc: kpiFillingBonusPointsAdd,
updateFunc: kpiFillingBonusPointsUpdate,
},
5: {
ref: 'NegativeList',
addFunc: kpiFillingNegativeListAdd,
updateFunc: kpiFillingNegativeListUpdate,
},
4: {
6: {
ref: 'OverWork',
},
5: {
7: {
ref: 'StudentEmergencies',
}
}
}
},
computed: {
tabs() {
if (this.classType === 'graduate') {
return ["学生管理", "就业指导工作", "业务管理", "考勤管理", "加分项", "负面清单", "超工作量奖励绩效", "处理学生突发事件"];
}
return ["学生管理", "业务管理", "考勤管理", "加分项", "负面清单", "超工作量奖励绩效", "处理学生突发事件"];
}
},
onLoad(option) {
console.log('personal-reporting onLoad option:', option);
this.fillingYear = option.year;
this.fillingMonth = option.month;
this.reportingId = option.reportingId;
@@ -108,8 +139,30 @@
this.commitStatus = option.commitStatus;
this.completionStatus = option.completionStatus;
this.roleAudit = option.roleAudit;
// 始终从后端获取classType
if (this.reportingId) {
this.getClassType();
} else if (option.classType) {
this.classType = option.classType;
}
this.loading = false;
},
methods: {
async getClassType() {
try {
console.log('calling kpiFillingDetail with id:', this.reportingId);
const res = await kpiFillingDetail(this.reportingId);
console.log('kpiFillingDetail response:', res);
if (res.data) {
this.classType = res.data.classType || 'ungraduate';
console.log('classType from backend:', this.classType);
}
} catch(e) {
console.error('getClassType error:', e);
}
},
tabChange(index) {
this.tabIndex = index;
},
@@ -137,24 +190,50 @@
addFunc,
updateFunc
} = this.tabConfigs[this.tabIndex];
// 检查组件是否存在
if (!this.$refs[ref]) {
this.tabIndex++;
return;
}
// 获取表单数据
let formData = this.$refs[ref].getFormData();
formData = {
...formData,
departmentName: uni.getStorageSync("deptName"),
departmentName: uni.getStorageSync("deptName") || "测试学院",
fdyName: uni.getStorageSync("stuName"),
fillingYear: this.fillingYear,
fillingMonth: this.fillingMonth,
classType: this.classType,
};
// 根据班级类型排除不需要填写的字段
let excludeKeys = ['id', 'departmentName', 'fdyName', 'fillingYear', 'fillingMonth', 'classType'];
// 学生管理(tabIndex 0) - 毕业班需要排除部分字段
if (this.tabIndex === 0 && this.classType === 'graduate') {
excludeKeys = [...excludeKeys, 'visitDormitoryScoring', 'cadreScoring', 'stuActivityScoring', 'edgScoring', 'noticeScoring'];
}
// 负面清单(tabIndex 4/5)、考勤管理(tabIndex 2/3)、加分项(tabIndex 3/4)、就业指导(tabIndex 1)等不需要验证空值
if (this.tabIndex === 1 || this.tabIndex === 2 || this.tabIndex === 3 || this.tabIndex === 4 || this.tabIndex === 5) {
excludeKeys = [...excludeKeys,
'moralityAndBehavior', 'speech', 'complaint', 'offense', 'emergency', 'punishment', 'practiceFraud',
'dutyWorkScoring', 'conferenceScoring', 'bonusType', 'bonusScoring',
'gradFormAuditScoring', 'stuCareerConsultScoring', 'gradFormGuidanceScoring'];
}
console.log('formData:', formData);
console.log('excludeKeys:', excludeKeys);
console.log('hasEmptyValues result:', this.hasEmptyValues(formData, excludeKeys));
// 检查formData中是否有空值
if (this.hasEmptyValues(formData, ['id'])) {
if (this.hasEmptyValues(formData, excludeKeys)) {
console.log('表单有空值,阻止保存');
uni.showToast({
title: "请填写完整内容!",
icon: 'none' // 根据需要设置图标
});
return; // 停止执行后续操作
}
if (this.tabIndex !== 4 && this.tabIndex !== 5) {
console.log('准备保存tabIndex:', this.tabIndex, 'formData:', formData);
// 只跳过超工作量(tabIndex 6)和处理学生突发事件(tabIndex 7)
if (this.tabIndex !== 6 && this.tabIndex !== 7) {
if (formData.id || formData.id === 0) {
let res = await updateFunc(formData);
if (res.code == 200) {