wuzhen update
This commit is contained in:
1 parent
edaf8dbacf
commit
48da578546
86 files changed
+2101
-1849
No files matched your search
+6
@@ -1,6 +1,7 @@
|
||||
package org.dromara.cw.mapper;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectAuditor;
|
||||
import org.dromara.cw.domain.UserWithDeptInfo;
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
@@ -25,4 +26,9 @@ public interface CwProjectAuditorMapper extends BaseMapperPlus<CwProjectAuditor,
|
||||
*/
|
||||
List<CwProjectAuditorVo> selectAuditorStatistics();
|
||||
|
||||
/**
|
||||
* 查询完整的用户信息(包含部门信息)用于批量添加
|
||||
*/
|
||||
List<UserWithDeptInfo> selectUsersWithDeptInfo(@Param("userIds") List<Long> userIds);
|
||||
|
||||
}
|
||||
+4
-3
@@ -14,6 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.cw.domain.bo.CwProjectAuditorBo;
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.cw.domain.CwProjectAuditor;
|
||||
import org.dromara.cw.domain.UserWithDeptInfo;
|
||||
import org.dromara.cw.mapper.CwProjectAuditorMapper;
|
||||
import org.dromara.cw.service.ICwProjectAuditorService;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
@@ -150,8 +151,8 @@ public class CwProjectAuditorServiceImpl implements ICwProjectAuditorService {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. 获取用户信息
|
||||
List<SysUserVo> users = sysUserService.selectUserByIds(userIds, null);
|
||||
// 1. 获取完整的用户信息(包含部门信息)
|
||||
List<UserWithDeptInfo> users = baseMapper.selectUsersWithDeptInfo(userIds);
|
||||
if (users.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -163,7 +164,7 @@ public class CwProjectAuditorServiceImpl implements ICwProjectAuditorService {
|
||||
|
||||
// 3. 构建新增审核人列表
|
||||
List<CwProjectAuditor> auditors = new ArrayList<>();
|
||||
for (SysUserVo user : users) {
|
||||
for (UserWithDeptInfo user : users) {
|
||||
if (!existingUserIds.contains(user.getUserId())) {
|
||||
CwProjectAuditor auditor = new CwProjectAuditor();
|
||||
auditor.setUserId(user.getUserId());
|
||||
|
||||
@@ -111,4 +111,25 @@
|
||||
and deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 查询完整的用户信息(包含部门信息)用于批量添加 -->
|
||||
<select id="selectUsersWithDeptInfo" resultType="org.dromara.cw.domain.UserWithDeptInfo">
|
||||
select
|
||||
u.user_id as userId,
|
||||
u.user_name as userName,
|
||||
u.nick_name as nickName,
|
||||
u.dept_id as deptId,
|
||||
d.dept_name as deptName,
|
||||
u.phonenumber as phonenumber,
|
||||
u.email as email,
|
||||
u.status as status
|
||||
from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
where u.status = '0'
|
||||
and u.user_id in
|
||||
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
|
||||
#{userId}
|
||||
</foreach>
|
||||
order by u.dept_id, u.user_name
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -1,6 +1,12 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { AssessmentApprovalQuery, AssessmentApprovalVO, AssessmentApprovalDetailVO, AssessmentApprovalSubmitBO, EvaluationLevelStatsVO } from './types';
|
||||
import {
|
||||
AssessmentApprovalQuery,
|
||||
AssessmentApprovalVO,
|
||||
AssessmentApprovalDetailVO,
|
||||
AssessmentApprovalSubmitBO,
|
||||
EvaluationLevelStatsVO
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 查询班主任考核审核列表
|
||||
|
||||
@@ -224,11 +224,12 @@ export interface AssessmentApprovalSpecialWorkSubmitVO {
|
||||
* 评价等级统计数据
|
||||
*/
|
||||
export interface EvaluationLevelStatsVO {
|
||||
totalTeachers: number; // 总班主任数量
|
||||
totalTeachers: number; // 总班主任数量
|
||||
scoreRankings: ScoreRankingVO[]; // 分数排名列表
|
||||
evaluationLevelRules: { // 评价等级规则
|
||||
aLevelMaxPercentage: number; // A等最大占比(如20%)
|
||||
passScore: number; // 及格分数(如60分)
|
||||
evaluationLevelRules: {
|
||||
// 评价等级规则
|
||||
aLevelMaxPercentage: number; // A等最大占比(如20%)
|
||||
passScore: number; // 及格分数(如60分)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -236,8 +237,8 @@ export interface EvaluationLevelStatsVO {
|
||||
* 分数排名VO
|
||||
*/
|
||||
export interface ScoreRankingVO {
|
||||
score: number; // 考核得分
|
||||
ranking: number; // 排名(从1开始)
|
||||
percentage: number; // 百分比排名(前X%)
|
||||
score: number; // 考核得分
|
||||
ranking: number; // 排名(从1开始)
|
||||
percentage: number; // 百分比排名(前X%)
|
||||
evaluatedUserId?: string | number; // 被评价用户ID(可选,用于标识当前用户)
|
||||
}
|
||||
@@ -61,6 +61,7 @@ export interface CwApprovalRecordVO {
|
||||
*/
|
||||
export interface CwCustomApprovalRequest {
|
||||
applicationId: string | number;
|
||||
applicationNo: string;
|
||||
auditorIds: Array<string | number>;
|
||||
processName: string;
|
||||
submitterId: string | number;
|
||||
|
||||
@@ -63,6 +63,22 @@ export interface BatchAddRequest {
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加审核人详细请求对象(包含完整用户信息)
|
||||
*/
|
||||
export interface BatchAddDetailRequest {
|
||||
users: Array<{
|
||||
userId: string | number;
|
||||
userName: string;
|
||||
nickName: string;
|
||||
deptId: string | number;
|
||||
deptName: string;
|
||||
phoneNumber: string; // 注意:这里使用驼峰命名,确保与数据库字段映射
|
||||
email: string;
|
||||
}>;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态更新请求对象
|
||||
*/
|
||||
@@ -149,6 +165,20 @@ export function batchAddProjectAuditor(data: BatchAddRequest): AxiosPromise<void
|
||||
});
|
||||
}
|
||||
|
||||
// 注释掉:后端暂不支持带完整用户信息的批量添加接口
|
||||
// 使用 batchAddProjectAuditor 接口,后端会自动查询用户完整信息
|
||||
/**
|
||||
* 批量添加审核人(带完整用户信息)- 已弃用
|
||||
* 改用 batchAddProjectAuditor,后端会自动获取用户完整信息
|
||||
*/
|
||||
// export function batchAddProjectAuditorWithDetails(data: BatchAddDetailRequest): AxiosPromise<void> {
|
||||
// return request({
|
||||
// url: '/cw/projectAuditor/batchAddWithDetails',
|
||||
// method: 'post',
|
||||
// data: data
|
||||
// });
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取可用审核人列表
|
||||
*/
|
||||
|
||||
@@ -59,4 +59,4 @@ export function getTeamsByActivityId(activityId: string | number) {
|
||||
url: '/stgl/activityteaminfo/activity/' + activityId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -238,4 +238,4 @@ export interface StglActivityTeamInfoVo {
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
}
|
||||
@@ -61,4 +61,4 @@ export function exportStglClass(query: StglClassQuery) {
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -299,4 +299,4 @@ export interface StglClassDetailVO {
|
||||
* 年级
|
||||
*/
|
||||
grade: string;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
import request from '@/utils/request';
|
||||
import { XskhAssessmentApplicationApprovalQuery, XskhAssessmentApplicationApprovalBo, XskhAssessmentApplicationApprovalVo, AvailableScheduleOption } from './types';
|
||||
import {
|
||||
XskhAssessmentApplicationApprovalQuery,
|
||||
XskhAssessmentApplicationApprovalBo,
|
||||
XskhAssessmentApplicationApprovalVo,
|
||||
AvailableScheduleOption
|
||||
} from './types';
|
||||
|
||||
// 查询学生考核申请列表
|
||||
export function listAssessmentApplication(query: XskhAssessmentApplicationApprovalQuery) {
|
||||
@@ -91,4 +96,4 @@ export function getIndicatorsByScheduleId(scheduleId: string | number) {
|
||||
url: `/assessment/application/indicators/${scheduleId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -71,4 +71,4 @@ export function getAvailableAssessmentSchedules() {
|
||||
url: '/assessment/approval/available',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -138,4 +138,4 @@ export interface RewardTypeOption {
|
||||
typeCode: string;
|
||||
/** 类型名称 */
|
||||
typeName: string;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { TableDataInfo, R } from '@/api/types';
|
||||
import { XskhAssessmentIndicatorTypeVo, XskhAssessmentIndicatorTypeBo, XskhAssessmentIndicatorTypeQuery, XskhAssessmentIndicatorTypeForm } from './types';
|
||||
import {
|
||||
XskhAssessmentIndicatorTypeVo,
|
||||
XskhAssessmentIndicatorTypeBo,
|
||||
XskhAssessmentIndicatorTypeQuery,
|
||||
XskhAssessmentIndicatorTypeForm
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 查询学生考核指标类型列表
|
||||
@@ -75,4 +80,4 @@ export function getEnabledAssessmentIndicatorTypes(): AxiosPromise<R<XskhAssessm
|
||||
url: '/assessment/indicatorType/enabled',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -86,4 +86,4 @@ export interface XskhAssessmentIndicatorTypeForm extends BaseEntity {
|
||||
status?: string;
|
||||
/** 排序号 */
|
||||
sortOrder?: number;
|
||||
}
|
||||
}
|
||||
@@ -75,4 +75,4 @@ export function getEnabledAssessmentPlans(): AxiosPromise<R<XskhAssessmentPlanVo
|
||||
url: '/assessment/plan/enabled',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -140,4 +140,4 @@ export interface AssessmentIndicatorOption {
|
||||
indicatorDescription: string;
|
||||
/** 最高分值 */
|
||||
maxScore: number;
|
||||
}
|
||||
}
|
||||
@@ -124,4 +124,4 @@ export function getEnabledAssessmentPlans(): AxiosPromise<R<any[]>> {
|
||||
url: '/assessment/plan/enabled',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -142,4 +142,4 @@ export interface AssessmentPlanOption {
|
||||
planName: string;
|
||||
/** 方案说明 */
|
||||
planDescription: string;
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@
|
||||
<el-row :gutter="2" align="middle">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="姓名" :prop="`advisors.${index}.name`">
|
||||
<TeacherSelector
|
||||
:model-value="advisor.teacher"
|
||||
placeholder="选择指导老师"
|
||||
<TeacherSelector
|
||||
:model-value="advisor.teacher"
|
||||
placeholder="选择指导老师"
|
||||
@update:model-value="(teacher) => handleTeacherChange(advisor, teacher)"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -119,7 +119,7 @@ const removeAdvisor = (index: number) => {
|
||||
// 监听teacher选择变化,自动填充相关信息
|
||||
const handleTeacherChange = (advisor: AdvisorVO, teacher: TeacherVO | null) => {
|
||||
console.log('handleTeacherChange called with teacher:', teacher);
|
||||
|
||||
|
||||
if (teacher) {
|
||||
// 自动填充老师信息
|
||||
advisor.name = teacher.nickName || '';
|
||||
@@ -127,7 +127,7 @@ const handleTeacherChange = (advisor: AdvisorVO, teacher: TeacherVO | null) => {
|
||||
advisor.dept = teacher.deptName || '';
|
||||
advisor.email = teacher.email || '暂无邮箱';
|
||||
advisor.userId = teacher.userId?.toString() || '';
|
||||
|
||||
|
||||
console.log('Updated advisor:', {
|
||||
name: advisor.name,
|
||||
phone: advisor.phone,
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
value-key="uniqueKey"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="option in options" :key="option.uniqueKey || `${option.relatedType}-${option.relatedClubId}`" :label="formatOptionLabel(option)" :value="option">
|
||||
<el-option
|
||||
v-for="option in options"
|
||||
:key="option.uniqueKey || `${option.relatedType}-${option.relatedClubId}`"
|
||||
:label="formatOptionLabel(option)"
|
||||
:value="option"
|
||||
>
|
||||
<div class="option-content">
|
||||
<span class="option-name">{{ option.relatedClubName }}</span>
|
||||
<span class="option-type">{{ getTypeLabel(option.relatedType) }}</span>
|
||||
@@ -98,9 +103,13 @@ const selectedOption = ref<ClubOption | null>(null);
|
||||
|
||||
// 监听 modelValue 变化
|
||||
import { watch } from 'vue';
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
console.log('ClubSelector - modelValue changed:', newValue);
|
||||
}, { immediate: true });
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
console.log('ClubSelector - modelValue changed:', newValue);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 格式化选项标签
|
||||
const formatOptionLabel = (option: ClubOption): string => {
|
||||
@@ -172,7 +181,7 @@ const loadOptions = async () => {
|
||||
uniqueKey: `${item.relatedType}-${item.relatedClubId}`
|
||||
}));
|
||||
options.value = [...clubOptions.value, ...volunteerOptions.value];
|
||||
|
||||
|
||||
console.log('ClubSelector - 加载选项数据完成:', {
|
||||
clubCount: clubOptions.value.length,
|
||||
volunteerCount: volunteerOptions.value.length,
|
||||
|
||||
@@ -267,11 +267,11 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自我总体评价">
|
||||
<el-input
|
||||
:value="monthlyScoreForm.selfOverallComment || '暂无自我总体评价'"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
readonly
|
||||
<el-input
|
||||
:value="monthlyScoreForm.selfOverallComment || '暂无自我总体评价'"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
readonly
|
||||
placeholder="暂无自我总体评价内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -457,7 +457,7 @@ const loadApprovalDetail = async () => {
|
||||
if (approvalDetail.value?.routineWorkIndicators && Array.isArray(approvalDetail.value.routineWorkIndicators)) {
|
||||
routineWorkScores.value = approvalDetail.value.routineWorkIndicators.map((item) => {
|
||||
const mappedItem = {
|
||||
...item,
|
||||
...item,
|
||||
// 确保数值字段不为空
|
||||
selfScore: Number(item.selfScore) || 0,
|
||||
superiorScore: Number(item.superiorScore) || Number(item.selfScore) || 0, // 默认使用自评分数
|
||||
@@ -517,7 +517,7 @@ const loadApprovalDetail = async () => {
|
||||
if (approvalDetail.value?.specialWorkIndicators && Array.isArray(approvalDetail.value.specialWorkIndicators)) {
|
||||
specialWorkScores.value = approvalDetail.value.specialWorkIndicators.map((item) => {
|
||||
const mappedItem = {
|
||||
...item,
|
||||
...item,
|
||||
// 自评分数字段处理 - 兼容不同字段名,确保数值类型
|
||||
score: Number(item.score || item.selfScore) || 0,
|
||||
selfScore: Number(item.selfScore || item.score) || 0,
|
||||
@@ -557,18 +557,17 @@ const loadApprovalDetail = async () => {
|
||||
approvalForm.auditComment = '';
|
||||
|
||||
console.log('=== 审批详情数据加载完成 ===');
|
||||
|
||||
|
||||
// 验证数据完整性
|
||||
if (routineWorkScores.value.length === 0) {
|
||||
console.warn('⚠️ 警告:班主任常规工作数据为空,可能存在数据问题');
|
||||
proxy?.$modal.msgWarning('班主任常规工作数据为空,请检查数据是否正确');
|
||||
}
|
||||
|
||||
|
||||
if (specialWorkScores.value.length === 0) {
|
||||
console.warn('⚠️ 警告:特色工作数据为空,可能存在数据问题');
|
||||
proxy?.$modal.msgWarning('特色工作数据为空,请检查数据是否正确');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载审批详情失败:', error);
|
||||
proxy?.$modal.msgError('加载审批详情失败');
|
||||
|
||||
@@ -241,15 +241,13 @@
|
||||
</template>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="评价等级">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<dict-tag :options="getEvaluationLevelOptions()" :value="calculateEvaluationLevel()" />
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<div>
|
||||
<div>{{ evaluationStats ? '基于占比规则的精准评级' : '基于分数阈值的简化评级' }}</div>
|
||||
<div v-if="isDictDataMissing" style="color: #f56c6c; margin-top: 4px;">
|
||||
⚠️ 字典数据缺失,使用默认数据
|
||||
</div>
|
||||
<div v-if="isDictDataMissing" style="color: #f56c6c; margin-top: 4px">⚠️ 字典数据缺失,使用默认数据</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-icon :color="evaluationStats ? '#67c23a' : '#e6a23c'" size="16">
|
||||
@@ -259,9 +257,7 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="考核得分">
|
||||
{{ calculateMonthlyScore().toFixed(2) }}分
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="考核得分"> {{ calculateMonthlyScore().toFixed(2) }}分 </el-descriptions-item>
|
||||
<el-descriptions-item label="审批时间">
|
||||
{{ parseTime(currentRow?.createTime, '{y}-{m}-{d} {h}:{i}:{s}') || '-' }}
|
||||
</el-descriptions-item>
|
||||
@@ -310,9 +306,10 @@ const getEvaluationLevelOptions = (): DictDataOption[] => {
|
||||
console.log('✅ 使用数据库字典数据:', bmzr_evaluation_level.value);
|
||||
return bmzr_evaluation_level.value;
|
||||
}
|
||||
|
||||
|
||||
// 字典数据未加载或为空时的处理
|
||||
if (visible.value) { // 只在弹窗打开时输出警告
|
||||
if (visible.value) {
|
||||
// 只在弹窗打开时输出警告
|
||||
console.warn('⚠️ 字典数据为空,使用备用数据,当前状态:', {
|
||||
弹窗是否打开: visible.value,
|
||||
字典对象存在: !!bmzr_evaluation_level.value,
|
||||
@@ -321,7 +318,7 @@ const getEvaluationLevelOptions = (): DictDataOption[] => {
|
||||
字典数据内容: bmzr_evaluation_level.value
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return fallbackEvaluationLevelOptions.value;
|
||||
};
|
||||
|
||||
@@ -363,10 +360,10 @@ const dialogTitle = computed(() => {
|
||||
const isDictDataMissing = computed(() => {
|
||||
// 如果还没有尝试加载字典数据,不显示警告
|
||||
if (!visible.value) return false;
|
||||
|
||||
|
||||
// 如果字典数据已经加载成功,不显示警告
|
||||
if (isDictLoaded.value) return false;
|
||||
|
||||
|
||||
// 只有在弹窗打开且字典数据为空时才显示警告
|
||||
return !bmzr_evaluation_level.value || bmzr_evaluation_level.value.length === 0;
|
||||
});
|
||||
@@ -434,7 +431,7 @@ const calculateEvaluationLevel = () => {
|
||||
console.log('💯 考核得分:', score);
|
||||
console.log('📈 是否有统计数据:', !!evaluationStats.value);
|
||||
console.log('👤 当前用户ID:', currentRow.value?.evaluatedUserId);
|
||||
|
||||
|
||||
if (evaluationStats.value) {
|
||||
console.log('📊 统计数据详情:', {
|
||||
总教师数: evaluationStats.value.totalTeachers,
|
||||
@@ -443,15 +440,17 @@ const calculateEvaluationLevel = () => {
|
||||
及格分数: evaluationStats.value.evaluationLevelRules?.passScore
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let level = '';
|
||||
let calculationMethod = '';
|
||||
|
||||
|
||||
// 如果有统计数据且用户ID存在,使用严格的占比规则
|
||||
if (evaluationStats.value &&
|
||||
evaluationStats.value.totalTeachers > 0 &&
|
||||
evaluationStats.value.scoreRankings.length > 0 &&
|
||||
currentRow.value?.evaluatedUserId) {
|
||||
if (
|
||||
evaluationStats.value &&
|
||||
evaluationStats.value.totalTeachers > 0 &&
|
||||
evaluationStats.value.scoreRankings.length > 0 &&
|
||||
currentRow.value?.evaluatedUserId
|
||||
) {
|
||||
level = calculateEvaluationLevelByStats(score, currentRow.value.evaluatedUserId);
|
||||
calculationMethod = '📈 严格占比规则评级';
|
||||
} else {
|
||||
@@ -464,17 +463,17 @@ const calculateEvaluationLevel = () => {
|
||||
level = 'B'; // B等(合格):60-85分
|
||||
}
|
||||
calculationMethod = '🔢 简化分数评级';
|
||||
|
||||
|
||||
// 输出使用简化评级的原因
|
||||
const reasons = [];
|
||||
if (!evaluationStats.value) reasons.push('统计数据为空');
|
||||
if (evaluationStats.value?.totalTeachers === 0) reasons.push('总教师数为0');
|
||||
if (evaluationStats.value?.scoreRankings?.length === 0) reasons.push('排名数据为空');
|
||||
if (!currentRow.value?.evaluatedUserId) reasons.push('用户ID缺失');
|
||||
|
||||
|
||||
console.warn('⚠️ 使用简化评级的原因:', reasons.join('、'));
|
||||
}
|
||||
|
||||
|
||||
console.log('🎯 计算方法:', calculationMethod);
|
||||
console.log('📋 最终评价等级:', level);
|
||||
console.log('=== 📊 评价等级计算调试结束 ===');
|
||||
@@ -486,32 +485,30 @@ const calculateEvaluationLevelByStats = (score: number, evaluatedUserId: string
|
||||
const stats = evaluationStats.value;
|
||||
if (!stats) {
|
||||
console.warn('统计数据不可用,使用简单评级');
|
||||
return score < 60 ? 'C' : (score >= 85 ? 'A' : 'B');
|
||||
return score < 60 ? 'C' : score >= 85 ? 'A' : 'B';
|
||||
}
|
||||
|
||||
|
||||
const { totalTeachers, scoreRankings, evaluationLevelRules } = stats;
|
||||
const { aLevelMaxPercentage, passScore } = evaluationLevelRules;
|
||||
|
||||
|
||||
// C等:不及格
|
||||
if (score < passScore) {
|
||||
return 'C';
|
||||
}
|
||||
|
||||
|
||||
// 查找当前用户的排名
|
||||
const currentRanking = scoreRankings.find(item =>
|
||||
item.evaluatedUserId === evaluatedUserId || item.score === score
|
||||
);
|
||||
|
||||
const currentRanking = scoreRankings.find((item) => item.evaluatedUserId === evaluatedUserId || item.score === score);
|
||||
|
||||
if (!currentRanking) {
|
||||
console.warn('找不到当前用户的排名信息,使用简单评级');
|
||||
return score < 60 ? 'C' : (score >= 85 ? 'A' : 'B');
|
||||
return score < 60 ? 'C' : score >= 85 ? 'A' : 'B';
|
||||
}
|
||||
|
||||
|
||||
// A等:及格且排名在前20%内
|
||||
if (score >= passScore && currentRanking.percentage <= aLevelMaxPercentage) {
|
||||
return 'A';
|
||||
}
|
||||
|
||||
|
||||
// B等:其余及格的
|
||||
return 'B';
|
||||
};
|
||||
@@ -525,28 +522,28 @@ const open = async (row: AssessmentApprovalVO) => {
|
||||
|
||||
currentRow.value = row;
|
||||
visible.value = true;
|
||||
|
||||
|
||||
// 检查字典数据加载状态
|
||||
console.log('🔍 打开弹窗时字典数据状态:', {
|
||||
字典对象存在: !!bmzr_evaluation_level.value,
|
||||
字典数据长度: bmzr_evaluation_level.value?.length || 0,
|
||||
字典数据内容: bmzr_evaluation_level.value
|
||||
});
|
||||
|
||||
|
||||
// 等待字典数据加载完成(最多等待2秒)
|
||||
let waitCount = 0;
|
||||
while ((!bmzr_evaluation_level.value || bmzr_evaluation_level.value.length === 0) && waitCount < 20) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
waitCount++;
|
||||
}
|
||||
|
||||
|
||||
if (bmzr_evaluation_level.value && bmzr_evaluation_level.value.length > 0) {
|
||||
console.log('✅ 字典数据加载完成:', bmzr_evaluation_level.value);
|
||||
isDictLoaded.value = true;
|
||||
} else {
|
||||
console.warn('⚠️ 字典数据加载超时,使用备用数据');
|
||||
}
|
||||
|
||||
|
||||
await loadViewDetail();
|
||||
};
|
||||
|
||||
@@ -577,11 +574,15 @@ const loadViewDetail = async () => {
|
||||
if (statsRes.status === 'fulfilled') {
|
||||
evaluationStats.value = statsRes.value.data;
|
||||
console.log('✅ 评价等级统计数据加载成功:', evaluationStats.value);
|
||||
|
||||
|
||||
// 检查统计数据的完整性
|
||||
if (!evaluationStats.value.totalTeachers || evaluationStats.value.scoreRankings.length === 0) {
|
||||
console.warn('⚠️ 统计数据不完整,总教师数:', evaluationStats.value.totalTeachers,
|
||||
'排名数据条数:', evaluationStats.value.scoreRankings.length);
|
||||
console.warn(
|
||||
'⚠️ 统计数据不完整,总教师数:',
|
||||
evaluationStats.value.totalTeachers,
|
||||
'排名数据条数:',
|
||||
evaluationStats.value.scoreRankings.length
|
||||
);
|
||||
proxy?.$modal.msgWarning('统计数据不完整,可能影响等级评定准确性');
|
||||
}
|
||||
} else {
|
||||
@@ -696,18 +697,17 @@ const loadViewDetail = async () => {
|
||||
approvalData.auditComment = '';
|
||||
|
||||
console.log('=== 查看详情数据加载完成 ===');
|
||||
|
||||
|
||||
// 验证数据完整性
|
||||
if (routineWorkScores.value.length === 0) {
|
||||
console.warn('⚠️ 警告:班主任常规工作数据为空,可能存在数据问题');
|
||||
proxy?.$modal.msgWarning('班主任常规工作数据为空,请检查数据是否正确');
|
||||
}
|
||||
|
||||
|
||||
if (specialWorkScores.value.length === 0) {
|
||||
console.warn('⚠️ 警告:特色工作数据为空,可能存在数据问题');
|
||||
proxy?.$modal.msgWarning('特色工作数据为空,请检查数据是否正确');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载查看详情失败:', error);
|
||||
proxy?.$modal.msgError('加载查看详情失败');
|
||||
@@ -724,8 +724,8 @@ const handleClose = () => {
|
||||
routineWorkScores.value = [];
|
||||
specialWorkScores.value = [];
|
||||
evaluationStats.value = undefined;
|
||||
isDictLoaded.value = false; // 重置字典加载状态
|
||||
|
||||
isDictLoaded.value = false; // 重置字典加载状态
|
||||
|
||||
Object.assign(managementEffectivenessData, {
|
||||
classDeduction: 0,
|
||||
classDeductionHalf: 0,
|
||||
@@ -802,7 +802,7 @@ defineExpose({
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
.el-table__row {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,22 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="指标类型编码" prop="indicatorTypeCode">
|
||||
<el-input v-model="queryParams.indicatorTypeCode" placeholder="请输入指标类型编码(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
<el-input
|
||||
v-model="queryParams.indicatorTypeCode"
|
||||
placeholder="请输入指标类型编码(支持模糊搜索)"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="指标类型名称" prop="indicatorTypeName">
|
||||
<el-input v-model="queryParams.indicatorTypeName" placeholder="请输入指标类型名称(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
<el-input
|
||||
v-model="queryParams.indicatorTypeName"
|
||||
placeholder="请输入指标类型名称(支持模糊搜索)"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
@@ -194,7 +204,7 @@ const handleQuery = () => {
|
||||
pageNum: queryParams.value.pageNum,
|
||||
pageSize: queryParams.value.pageSize
|
||||
});
|
||||
|
||||
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
@@ -202,24 +212,26 @@ const handleQuery = () => {
|
||||
/** 查询班主任考核指标类型列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
|
||||
|
||||
// 构建查询参数,正确处理空字符串和undefined/null值
|
||||
const queryData = {
|
||||
pageNum: queryParams.value.pageNum,
|
||||
pageSize: queryParams.value.pageSize,
|
||||
// 修复:正确处理空字符串,只过滤undefined和null
|
||||
...(queryParams.value.indicatorTypeCode !== undefined && queryParams.value.indicatorTypeCode !== null && { indicatorTypeCode: queryParams.value.indicatorTypeCode }),
|
||||
...(queryParams.value.indicatorTypeName !== undefined && queryParams.value.indicatorTypeName !== null && { indicatorTypeName: queryParams.value.indicatorTypeName }),
|
||||
...(queryParams.value.indicatorTypeCode !== undefined &&
|
||||
queryParams.value.indicatorTypeCode !== null && { indicatorTypeCode: queryParams.value.indicatorTypeCode }),
|
||||
...(queryParams.value.indicatorTypeName !== undefined &&
|
||||
queryParams.value.indicatorTypeName !== null && { indicatorTypeName: queryParams.value.indicatorTypeName }),
|
||||
...(queryParams.value.status !== undefined && queryParams.value.status !== null && { status: queryParams.value.status })
|
||||
};
|
||||
|
||||
|
||||
console.log('📋 发送查询请求,参数:', queryData);
|
||||
console.log('🔍 原始查询参数:', {
|
||||
indicatorTypeCode: `"${queryParams.value.indicatorTypeCode}"`,
|
||||
indicatorTypeName: `"${queryParams.value.indicatorTypeName}"`,
|
||||
status: `"${queryParams.value.status}"`
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
const res = await listAssessmentIndicatorType(queryData);
|
||||
console.log('📊 查询结果:', {
|
||||
@@ -227,7 +239,7 @@ const getList = async () => {
|
||||
dataCount: res.rows?.length,
|
||||
firstItem: res.rows?.[0]
|
||||
});
|
||||
|
||||
|
||||
assessmentIndicatorTypeList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
@@ -253,23 +265,23 @@ const reset = () => {
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
console.log('🔄 重置查询参数');
|
||||
|
||||
|
||||
// 重置表单字段
|
||||
queryFormRef.value?.resetFields();
|
||||
|
||||
|
||||
// 手动重置查询参数对象,确保清空所有搜索条件
|
||||
queryParams.value.indicatorTypeCode = undefined;
|
||||
queryParams.value.indicatorTypeName = undefined;
|
||||
queryParams.value.status = undefined;
|
||||
queryParams.value.pageNum = 1;
|
||||
|
||||
|
||||
console.log('🔄 重置后的查询参数:', {
|
||||
indicatorTypeCode: queryParams.value.indicatorTypeCode,
|
||||
indicatorTypeName: queryParams.value.indicatorTypeName,
|
||||
status: queryParams.value.status,
|
||||
pageNum: queryParams.value.pageNum
|
||||
});
|
||||
|
||||
|
||||
// 重新查询
|
||||
getList();
|
||||
};
|
||||
@@ -349,7 +361,7 @@ onMounted(() => {
|
||||
pageNum: queryParams.value.pageNum,
|
||||
pageSize: queryParams.value.pageSize
|
||||
});
|
||||
|
||||
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -454,7 +454,7 @@ const submitForm = () => {
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: AssessmentPlanVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
|
||||
|
||||
let confirmMessage = '';
|
||||
if (row) {
|
||||
const semesterText = row.semester === '1' ? '上学期' : '下学期';
|
||||
@@ -462,7 +462,7 @@ const handleDelete = async (row?: AssessmentPlanVO) => {
|
||||
} else {
|
||||
confirmMessage = `是否确认删除选中的${ids.value.length}条考核计划?`;
|
||||
}
|
||||
|
||||
|
||||
await proxy?.$modal.confirm(confirmMessage).then(async () => {
|
||||
await delAssessmentPlan(_ids).then(() => {
|
||||
getList();
|
||||
|
||||
@@ -382,14 +382,14 @@ const submitForm = () => {
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: BmzrAssessmentSchemeVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
|
||||
|
||||
let confirmMessage = '';
|
||||
if (row) {
|
||||
confirmMessage = `是否确认删除考核方案"${row.schemeName}"(${row.academicYear}学年第${row.semester}学期)?`;
|
||||
} else {
|
||||
confirmMessage = `是否确认删除选中的${ids.value.length}条考核方案?`;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await proxy?.$modal.confirm(confirmMessage);
|
||||
await delBmzrAssessmentScheme(_ids);
|
||||
|
||||
@@ -95,14 +95,14 @@
|
||||
<!-- 未提交状态、草稿状态或审核不通过状态:显示提交考核按钮 -->
|
||||
<template v-if="!scope.row.submitted || scope.row.auditStatus === '9' || scope.row.auditStatus === '2'">
|
||||
<el-tooltip content="提交考核" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
:disabled="loading || !isDataReady"
|
||||
@click="handleSubmitAssessment(scope.row)"
|
||||
v-hasPermi="['bzrkh:teacherAssessment:submit']"
|
||||
></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
:disabled="loading || !isDataReady"
|
||||
@click="handleSubmitAssessment(scope.row)"
|
||||
v-hasPermi="['bzrkh:teacherAssessment:submit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<!-- 待审核状态:显示查看考核和撤销按钮 -->
|
||||
@@ -155,7 +155,7 @@
|
||||
|
||||
<!-- 考核提交弹窗 -->
|
||||
<SubmitDialog ref="submitDialogRef" :plan-id="currentPlanId" :class-id="currentClassId" @success="handleSubmitSuccess" />
|
||||
|
||||
|
||||
<!-- 考核查看弹窗 -->
|
||||
<ViewDialog ref="viewDialogRef" :plan-id="currentPlanId" :class-id="currentClassId" />
|
||||
</div>
|
||||
@@ -241,13 +241,13 @@ const getTeacherClasses = async () => {
|
||||
queryParams.value.planName = undefined; // 确保搜索条件被重置
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.classId = activeTab.value;
|
||||
|
||||
|
||||
console.log('🔄 初始化完成:', {
|
||||
activeTab: activeTab.value,
|
||||
classId: queryParams.value.classId,
|
||||
planName: queryParams.value.planName
|
||||
});
|
||||
|
||||
|
||||
// 等待activeTab设置完成后再获取列表
|
||||
await nextTick();
|
||||
await getList();
|
||||
@@ -270,9 +270,9 @@ const getList = async () => {
|
||||
teacherId: userStore.userId, // 确保包含teacherId
|
||||
planStatus: '1' // 确保包含planStatus
|
||||
};
|
||||
|
||||
|
||||
console.log('📋 查询参数:', queryData);
|
||||
|
||||
|
||||
const res = await listTeacherAssessmentPlans(queryData);
|
||||
console.log('📊 查询结果:', {
|
||||
total: res.total,
|
||||
@@ -280,7 +280,7 @@ const getList = async () => {
|
||||
firstItemStatus: res.rows?.[0]?.auditStatus,
|
||||
firstItemSubmitted: res.rows?.[0]?.submitted
|
||||
});
|
||||
|
||||
|
||||
assessmentPlanList.value = res.rows || [];
|
||||
total.value = res.total || 0;
|
||||
} catch (error) {
|
||||
@@ -298,18 +298,18 @@ const handleTabChange = async (tabName: string) => {
|
||||
activeTab: activeTab.value,
|
||||
oldPlanName: queryParams.value.planName
|
||||
});
|
||||
|
||||
|
||||
// 重置分页和搜索条件,设置新的班级ID
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.planName = undefined; // 重置搜索条件,避免标签页间共享
|
||||
queryParams.value.classId = tabName;
|
||||
|
||||
|
||||
console.log('🔄 参数设置完成:', {
|
||||
classId: queryParams.value.classId,
|
||||
activeTab: activeTab.value,
|
||||
planName: queryParams.value.planName
|
||||
});
|
||||
|
||||
|
||||
// 等待参数设置完成后再查询
|
||||
await nextTick();
|
||||
getList();
|
||||
@@ -332,20 +332,20 @@ const handleQuery = () => {
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
console.log('🔄 开始重置查询参数');
|
||||
|
||||
|
||||
try {
|
||||
// 保存当前班级ID和计划状态(这些不应该被重置)
|
||||
const currentClassId = queryParams.value.classId;
|
||||
const currentPlanStatus = queryParams.value.planStatus;
|
||||
|
||||
|
||||
// 手动重置查询参数到初始状态
|
||||
queryParams.value.planName = undefined;
|
||||
queryParams.value.pageNum = 1;
|
||||
|
||||
|
||||
// 恢复不应该被重置的参数
|
||||
queryParams.value.classId = currentClassId;
|
||||
queryParams.value.planStatus = currentPlanStatus;
|
||||
|
||||
|
||||
console.log('🔄 重置后的查询参数:', {
|
||||
planName: queryParams.value.planName,
|
||||
pageNum: queryParams.value.pageNum,
|
||||
@@ -353,7 +353,7 @@ const resetQuery = () => {
|
||||
planStatus: queryParams.value.planStatus,
|
||||
teacherId: userStore.userId
|
||||
});
|
||||
|
||||
|
||||
// 等待DOM更新完成后再刷新数据
|
||||
nextTick(() => {
|
||||
console.log('🔄 开始强制刷新数据');
|
||||
@@ -367,8 +367,6 @@ const resetQuery = () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 排序触发事件 */
|
||||
const sortChange = (column: any) => {
|
||||
const { prop, order } = column;
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
<span>特色工作(加20分)</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="specialWorkForm" class="special-work-form">
|
||||
<el-form :model="specialWorkForm" class="special-work-form">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="及时并妥善处理好学生违纪事件" label-width="240px" class="no-wrap-label">
|
||||
@@ -203,7 +203,15 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自评意见" label-width="80px">
|
||||
<el-input v-model="specialWorkForm.comment1" type="textarea" :rows="1" placeholder="请输入自评意见" maxlength="200" show-word-limit class="special-comment-input" />
|
||||
<el-input
|
||||
v-model="specialWorkForm.comment1"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入自评意见"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
class="special-comment-input"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -224,7 +232,15 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自评意见" label-width="80px">
|
||||
<el-input v-model="specialWorkForm.comment2" type="textarea" :rows="1" placeholder="请输入自评意见" maxlength="200" show-word-limit class="special-comment-input" />
|
||||
<el-input
|
||||
v-model="specialWorkForm.comment2"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入自评意见"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
class="special-comment-input"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -245,7 +261,15 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自评意见" label-width="80px">
|
||||
<el-input v-model="specialWorkForm.comment3" type="textarea" :rows="1" placeholder="请输入自评意见" maxlength="200" show-word-limit class="special-comment-input" />
|
||||
<el-input
|
||||
v-model="specialWorkForm.comment3"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入自评意见"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
class="special-comment-input"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -266,15 +290,23 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自评意见" label-width="80px">
|
||||
<el-input v-model="specialWorkForm.comment4" type="textarea" :rows="1" placeholder="请输入自评意见" maxlength="200" show-word-limit class="special-comment-input" />
|
||||
<el-input
|
||||
v-model="specialWorkForm.comment4"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入自评意见"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
class="special-comment-input"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="总分值" label-width="240px">
|
||||
<el-input :value="specialWorkForm.totalScore?.toFixed(2) || '0.00'" readonly style="width: 150px; text-align: center" />
|
||||
</el-form-item>
|
||||
<el-input :value="specialWorkForm.totalScore?.toFixed(2) || '0.00'" readonly style="width: 150px; text-align: center" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
@@ -316,7 +348,9 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="saveLoading" type="primary" @click="handleSubmit">保存</el-button>
|
||||
<el-button :loading="submitLoading" type="success" @click="handleSubmitForApproval" v-hasPermi="['bzrkh:teacherAssessment:submit']">提交</el-button>
|
||||
<el-button :loading="submitLoading" type="success" @click="handleSubmitForApproval" v-hasPermi="['bzrkh:teacherAssessment:submit']"
|
||||
>提交</el-button
|
||||
>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -525,7 +559,7 @@ const calculateMonthlyScore = () => {
|
||||
|
||||
// 检查是否有实际评分数据(避免空白状态显示50分)
|
||||
const hasAnyScore = routineWorkTotal > 0 || deductionHalf > 0 || specialWorkTotal > 0;
|
||||
|
||||
|
||||
if (!hasAnyScore || totalCoefficient === 0) {
|
||||
return 0; // 没有评分数据时显示0
|
||||
}
|
||||
@@ -534,7 +568,7 @@ const calculateMonthlyScore = () => {
|
||||
const classDeductionMaxScore = 50;
|
||||
|
||||
// 修正后的考核得分公式 = 总系数(K)* ((班主任常规工作自评分之和 + 班级扣分项满分值) - 按班级扣分50%计的分值 + 特色工作总分值)
|
||||
return totalCoefficient * ((routineWorkTotal + classDeductionMaxScore) - deductionHalf + specialWorkTotal);
|
||||
return totalCoefficient * (routineWorkTotal + classDeductionMaxScore - deductionHalf + specialWorkTotal);
|
||||
};
|
||||
|
||||
/** 打开弹窗 */
|
||||
@@ -935,7 +969,7 @@ defineExpose({
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/* 覆盖small尺寸的字体大小,与特色工作输入框保持一致 */
|
||||
.el-input-number--small.score-input .el-input-number__input {
|
||||
font-size: 14px !important;
|
||||
|
||||
@@ -55,9 +55,7 @@
|
||||
</template>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="班级总人数">{{ getCurrentClassDetail()?.totalStudentCount || 0 }}人</el-descriptions-item>
|
||||
<el-descriptions-item label="人数系数">{{
|
||||
formatNumber(getCurrentCoefficient()?.studentCountCoefficient, 3)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="人数系数">{{ formatNumber(getCurrentCoefficient()?.studentCountCoefficient, 3) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
@@ -70,9 +68,7 @@
|
||||
</template>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="住校生人数">{{ getCurrentClassDetail()?.boardingStudentCount || 0 }}人</el-descriptions-item>
|
||||
<el-descriptions-item label="住校生系数">{{
|
||||
formatNumber(getCurrentCoefficient()?.boardingCoefficient, 3)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="住校生系数">{{ formatNumber(getCurrentCoefficient()?.boardingCoefficient, 3) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
@@ -165,10 +161,10 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上级评分">
|
||||
<el-input
|
||||
:value="approvalManagementEffectivenessData.superiorScore?.toFixed(2) || '-'"
|
||||
readonly
|
||||
style="width: 100%; text-align: center"
|
||||
<el-input
|
||||
:value="approvalManagementEffectivenessData.superiorScore?.toFixed(2) || '-'"
|
||||
readonly
|
||||
style="width: 100%; text-align: center"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -176,12 +172,12 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自评意见">
|
||||
<el-input :value="approvalManagementEffectivenessData.selfComment" readonly style="height: 24px;" />
|
||||
<el-input :value="approvalManagementEffectivenessData.selfComment" readonly style="height: 24px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="审批意见">
|
||||
<el-input :value="approvalManagementEffectivenessData.superiorComment" readonly style="height: 24px;" />
|
||||
<el-input :value="approvalManagementEffectivenessData.superiorComment" readonly style="height: 24px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -280,17 +276,17 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="考核得分">
|
||||
<el-input :value="calculateMonthlyScore().toFixed(2)" readonly style="width: 100%; text-align: center; height: 24px;" />
|
||||
<el-input :value="calculateMonthlyScore().toFixed(2)" readonly style="width: 100%; text-align: center; height: 24px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="自我总体评价">
|
||||
<el-input :value="approvalMonthlyScoreData.selfOverallComment" readonly style="height: 24px;" />
|
||||
<el-input :value="approvalMonthlyScoreData.selfOverallComment" readonly style="height: 24px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上级总体评价">
|
||||
<el-input :value="approvalMonthlyScoreData.superiorOverallComment" readonly style="height: 24px;" />
|
||||
<el-input :value="approvalMonthlyScoreData.superiorOverallComment" readonly style="height: 24px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -307,15 +303,13 @@
|
||||
</template>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="评价等级">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<dict-tag :options="getEvaluationLevelOptions()" :value="calculateEvaluationLevel()" />
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<div>
|
||||
<div>{{ evaluationStats ? '基于占比规则的精准评级' : '基于分数阈值的简化评级' }}</div>
|
||||
<div v-if="isDictDataMissing" style="color: #f56c6c; margin-top: 4px;">
|
||||
⚠️ 字典数据缺失,使用默认数据
|
||||
</div>
|
||||
<div v-if="isDictDataMissing" style="color: #f56c6c; margin-top: 4px">⚠️ 字典数据缺失,使用默认数据</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-icon :color="evaluationStats ? '#67c23a' : '#e6a23c'" size="16">
|
||||
@@ -325,16 +319,14 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="考核得分">
|
||||
{{ calculateMonthlyScore().toFixed(2) }}分
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="考核得分"> {{ calculateMonthlyScore().toFixed(2) }}分 </el-descriptions-item>
|
||||
<el-descriptions-item label="审批时间">
|
||||
{{ parseTime(approvalDetail?.submitTime, '{y}-{m}-{d} {h}:{i}:{s}') || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form label-width="100px" style="margin-top: 20px" v-if="approvalMonthlyScoreData.superiorOverallComment">
|
||||
<el-form-item label="审批意见">
|
||||
<el-input :value="approvalMonthlyScoreData.superiorOverallComment" readonly style="height: 24px;" />
|
||||
<el-input :value="approvalMonthlyScoreData.superiorOverallComment" readonly style="height: 24px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
@@ -434,11 +426,7 @@
|
||||
import { InfoFilled, WarningFilled } from '@element-plus/icons-vue';
|
||||
import { getAssessmentSubmitDetail, getClassStudentRecords } from '@/api/bzrkh/teacherAssessment';
|
||||
import { getAssessmentApprovalDetail, getEvaluationLevelStats } from '@/api/bzrkh/assessmentApproval';
|
||||
import {
|
||||
AssessmentDetailVO,
|
||||
AssessmentIndicatorVO,
|
||||
ClassStudentRecordsVO
|
||||
} from '@/api/bzrkh/teacherAssessment/types';
|
||||
import { AssessmentDetailVO, AssessmentIndicatorVO, ClassStudentRecordsVO } from '@/api/bzrkh/teacherAssessment/types';
|
||||
import {
|
||||
AssessmentApprovalDetailVO,
|
||||
AssessmentApprovalIndicatorVO,
|
||||
@@ -471,9 +459,10 @@ const getEvaluationLevelOptions = (): DictDataOption[] => {
|
||||
console.log('✅ 使用数据库字典数据:', bmzr_evaluation_level.value);
|
||||
return bmzr_evaluation_level.value;
|
||||
}
|
||||
|
||||
|
||||
// 字典数据未加载或为空时的处理
|
||||
if (visible.value) { // 只在弹窗打开时输出警告
|
||||
if (visible.value) {
|
||||
// 只在弹窗打开时输出警告
|
||||
console.warn('⚠️ 字典数据为空,使用备用数据,当前状态:', {
|
||||
弹窗是否打开: visible.value,
|
||||
字典对象存在: !!bmzr_evaluation_level.value,
|
||||
@@ -482,7 +471,7 @@ const getEvaluationLevelOptions = (): DictDataOption[] => {
|
||||
字典数据内容: bmzr_evaluation_level.value
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return fallbackEvaluationLevelOptions.value;
|
||||
};
|
||||
|
||||
@@ -511,10 +500,10 @@ const isDictLoaded = ref(false);
|
||||
const isDictDataMissing = computed(() => {
|
||||
// 如果还没有尝试加载字典数据,不显示警告
|
||||
if (!visible.value) return false;
|
||||
|
||||
|
||||
// 如果字典数据已经加载成功,不显示警告
|
||||
if (isDictLoaded.value) return false;
|
||||
|
||||
|
||||
// 只有在弹窗打开且字典数据为空时才显示警告
|
||||
return !bmzr_evaluation_level.value || bmzr_evaluation_level.value.length === 0;
|
||||
});
|
||||
@@ -556,16 +545,16 @@ const specialWorkForm = reactive({
|
||||
totalScore: 0
|
||||
});
|
||||
|
||||
// 考核得分表单(只读)
|
||||
const monthlyScoreForm = reactive({
|
||||
overallComment: ''
|
||||
});
|
||||
// 考核得分表单(只读)
|
||||
const monthlyScoreForm = reactive({
|
||||
overallComment: ''
|
||||
});
|
||||
|
||||
// 审批模式下的考核得分数据
|
||||
const approvalMonthlyScoreData = reactive({
|
||||
selfOverallComment: '',
|
||||
superiorOverallComment: ''
|
||||
});
|
||||
// 审批模式下的考核得分数据
|
||||
const approvalMonthlyScoreData = reactive({
|
||||
selfOverallComment: '',
|
||||
superiorOverallComment: ''
|
||||
});
|
||||
|
||||
// 学生违纪记录和奖励记录弹窗相关
|
||||
const studentRecordsVisible = ref(false);
|
||||
@@ -666,7 +655,7 @@ const calculateMonthlyScore = () => {
|
||||
|
||||
// 检查是否有实际评分数据(避免空白状态显示50分)
|
||||
const hasAnyScore = routineWorkTotal > 0 || deductionHalf > 0 || specialWorkTotal > 0;
|
||||
|
||||
|
||||
if (!hasAnyScore || totalCoefficient === 0) {
|
||||
return 0; // 没有评分数据时显示0
|
||||
}
|
||||
@@ -675,7 +664,7 @@ const calculateMonthlyScore = () => {
|
||||
const classDeductionMaxScore = 50;
|
||||
|
||||
// 修正后的考核得分公式 = 总系数(K)* ((班主任常规工作自评分之和 + 班级扣分项满分值) - 按班级扣分50%计的分值 + 特色工作总分值)
|
||||
return totalCoefficient * ((routineWorkTotal + classDeductionMaxScore) - deductionHalf + specialWorkTotal);
|
||||
return totalCoefficient * (routineWorkTotal + classDeductionMaxScore - deductionHalf + specialWorkTotal);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -685,7 +674,7 @@ const calculateEvaluationLevel = () => {
|
||||
console.log('=== 📊 评价等级计算调试 ===');
|
||||
console.log('💯 考核得分:', score);
|
||||
console.log('📈 是否有统计数据:', !!evaluationStats.value);
|
||||
|
||||
|
||||
if (evaluationStats.value) {
|
||||
console.log('📊 统计数据详情:', {
|
||||
总教师数: evaluationStats.value.totalTeachers,
|
||||
@@ -694,14 +683,12 @@ const calculateEvaluationLevel = () => {
|
||||
及格分数: evaluationStats.value.evaluationLevelRules?.passScore
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let level = '';
|
||||
let calculationMethod = '';
|
||||
|
||||
|
||||
// 如果有统计数据,使用严格的占比规则
|
||||
if (evaluationStats.value &&
|
||||
evaluationStats.value.totalTeachers > 0 &&
|
||||
evaluationStats.value.scoreRankings.length > 0) {
|
||||
if (evaluationStats.value && evaluationStats.value.totalTeachers > 0 && evaluationStats.value.scoreRankings.length > 0) {
|
||||
level = calculateEvaluationLevelByStats(score);
|
||||
calculationMethod = '📈 严格占比规则评级';
|
||||
} else {
|
||||
@@ -714,16 +701,16 @@ const calculateEvaluationLevel = () => {
|
||||
level = 'B'; // B等(合格):60-85分
|
||||
}
|
||||
calculationMethod = '🔢 简化分数评级';
|
||||
|
||||
|
||||
// 输出使用简化评级的原因
|
||||
const reasons = [];
|
||||
if (!evaluationStats.value) reasons.push('统计数据为空');
|
||||
if (evaluationStats.value?.totalTeachers === 0) reasons.push('总教师数为0');
|
||||
if (evaluationStats.value?.scoreRankings?.length === 0) reasons.push('排名数据为空');
|
||||
|
||||
|
||||
console.warn('⚠️ 使用简化评级的原因:', reasons.join('、'));
|
||||
}
|
||||
|
||||
|
||||
console.log('🎯 计算方法:', calculationMethod);
|
||||
console.log('📋 最终评价等级:', level);
|
||||
console.log('=== 📊 评价等级计算调试结束 ===');
|
||||
@@ -735,30 +722,30 @@ const calculateEvaluationLevelByStats = (score: number) => {
|
||||
const stats = evaluationStats.value;
|
||||
if (!stats) {
|
||||
console.warn('统计数据不可用,使用简单评级');
|
||||
return score < 60 ? 'C' : (score >= 85 ? 'A' : 'B');
|
||||
return score < 60 ? 'C' : score >= 85 ? 'A' : 'B';
|
||||
}
|
||||
|
||||
|
||||
const { totalTeachers, scoreRankings, evaluationLevelRules } = stats;
|
||||
const { aLevelMaxPercentage, passScore } = evaluationLevelRules;
|
||||
|
||||
|
||||
// C等:不及格
|
||||
if (score < passScore) {
|
||||
return 'C';
|
||||
}
|
||||
|
||||
|
||||
// 查找当前分数的排名
|
||||
const currentRanking = scoreRankings.find(item => item.score === score);
|
||||
|
||||
const currentRanking = scoreRankings.find((item) => item.score === score);
|
||||
|
||||
if (!currentRanking) {
|
||||
console.warn('找不到当前分数的排名信息,使用简单评级');
|
||||
return score < 60 ? 'C' : (score >= 85 ? 'A' : 'B');
|
||||
return score < 60 ? 'C' : score >= 85 ? 'A' : 'B';
|
||||
}
|
||||
|
||||
|
||||
// A等:及格且排名在前20%内
|
||||
if (score >= passScore && currentRanking.percentage <= aLevelMaxPercentage) {
|
||||
return 'A';
|
||||
}
|
||||
|
||||
|
||||
// B等:其余及格的
|
||||
return 'B';
|
||||
};
|
||||
@@ -783,7 +770,7 @@ const open = async (auditStatus?: string) => {
|
||||
routineWorkScores.value = [];
|
||||
approvalRoutineWorkScores.value = [];
|
||||
approvalSpecialWorkScores.value = [];
|
||||
|
||||
|
||||
Object.assign(managementEffectivenessForm, {
|
||||
classDeduction: 0,
|
||||
classDeductionHalf: 0,
|
||||
@@ -816,7 +803,7 @@ const open = async (auditStatus?: string) => {
|
||||
});
|
||||
|
||||
visible.value = true;
|
||||
|
||||
|
||||
// 检查字典数据加载状态
|
||||
console.log('🔍 打开弹窗时字典数据状态:', {
|
||||
字典对象存在: !!bmzr_evaluation_level.value,
|
||||
@@ -824,21 +811,21 @@ const open = async (auditStatus?: string) => {
|
||||
字典加载状态: isDictLoaded.value,
|
||||
字典数据内容: bmzr_evaluation_level.value
|
||||
});
|
||||
|
||||
|
||||
// 等待字典数据加载完成(最多等待2秒)
|
||||
let waitCount = 0;
|
||||
while ((!bmzr_evaluation_level.value || bmzr_evaluation_level.value.length === 0) && waitCount < 20) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
waitCount++;
|
||||
}
|
||||
|
||||
|
||||
if (bmzr_evaluation_level.value && bmzr_evaluation_level.value.length > 0) {
|
||||
console.log('✅ 字典数据加载完成:', bmzr_evaluation_level.value);
|
||||
isDictLoaded.value = true;
|
||||
} else {
|
||||
console.warn('⚠️ 字典数据加载超时,使用备用数据');
|
||||
}
|
||||
|
||||
|
||||
// 根据显示模式加载不同的数据
|
||||
if (viewMode.value === 'approval') {
|
||||
await loadApprovalDetail();
|
||||
@@ -854,7 +841,7 @@ const loadApprovalDetail = async () => {
|
||||
// 从userStore获取当前用户ID作为被评价用户ID
|
||||
const userStore = useUserStore();
|
||||
const evaluatedUserId = userStore.userId;
|
||||
|
||||
|
||||
if (!evaluatedUserId) {
|
||||
proxy?.$modal.msgError('获取用户信息失败');
|
||||
return;
|
||||
@@ -925,7 +912,6 @@ const loadApprovalDetail = async () => {
|
||||
}
|
||||
|
||||
console.log('=== 审批详情数据加载完成 ===');
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载审批详情失败:', error);
|
||||
proxy?.$modal.msgError('加载审批详情失败,请检查网络连接或刷新页面重试');
|
||||
@@ -981,7 +967,7 @@ const loadAssessmentDetail = async () => {
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 计算特色工作总分
|
||||
specialWorkForm.totalScore =
|
||||
(specialWorkForm.score1 || 0) + (specialWorkForm.score2 || 0) + (specialWorkForm.score3 || 0) + (specialWorkForm.score4 || 0);
|
||||
@@ -1033,8 +1019,8 @@ const handleClose = () => {
|
||||
approvalRoutineWorkScores.value = [];
|
||||
approvalSpecialWorkScores.value = [];
|
||||
evaluationStats.value = undefined;
|
||||
isDictLoaded.value = false; // 重置字典加载状态
|
||||
|
||||
isDictLoaded.value = false; // 重置字典加载状态
|
||||
|
||||
Object.assign(managementEffectivenessForm, {
|
||||
classDeduction: 0,
|
||||
classDeductionHalf: 0,
|
||||
@@ -1128,13 +1114,13 @@ defineExpose({
|
||||
.total-score {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #409EFF;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.monthly-score {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #67C23A;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
@@ -1183,7 +1169,7 @@ defineExpose({
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
.el-table__row {
|
||||
height: auto;
|
||||
}
|
||||
@@ -1280,4 +1266,4 @@ defineExpose({
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -33,6 +33,7 @@ web/src/api/cw/
|
||||
## 🚀 核心功能
|
||||
|
||||
### 1. 项目审批人配置管理 (`projectAuditor/`)
|
||||
|
||||
- **审批人池管理**: 从系统用户中选择并配置审批人员
|
||||
- **批量添加**: 通过部门树选择用户,支持批量添加审批人
|
||||
- **跨部门选择**: 支持在不同部门间选择用户,选择状态会自动保持
|
||||
@@ -40,23 +41,27 @@ web/src/api/cw/
|
||||
- **权限控制**: 基于RBAC的细粒度权限控制
|
||||
|
||||
### 2. 项目审批流程管理 (`projectApproval/`)
|
||||
|
||||
- **流程监控**: 查看所有审批流程状态和进度(待实现)
|
||||
- **流程详情**: 查看审批记录和时间轴展示(待实现)
|
||||
- **流程终止**: 管理员可终止进行中的审批流程(待实现)
|
||||
|
||||
### 3. 我的待审批 (`projectApproval/pending.vue`)
|
||||
|
||||
- **待审批列表**: 显示当前用户需要审批的项目
|
||||
- **项目详情**: 查看项目完整信息和审批历史
|
||||
- **审批操作**: 支持通过/拒绝,必填审批意见
|
||||
- **批量审批**: 支持批量通过或拒绝多个项目(待实现)
|
||||
|
||||
### 4. 审批流程选择组件 (`components/ApprovalProcessSelector.vue`)
|
||||
|
||||
- **自定义流程配置**: 支持用户从审批人池中选择审核人
|
||||
- **可视化流程预览**: 直观展示审批顺序和流程走向
|
||||
- **审核人管理**: 支持添加、删除、排序审核人
|
||||
- **流程验证**: 验证审批流程的合理性和完整性
|
||||
|
||||
### 5. 项目申报集成 (`application/form.vue` - 已修改)
|
||||
|
||||
- **流程选择对话框**: 在提交时弹出审批流程选择界面
|
||||
- **数据保存**: 先保存申报数据,再配置审批流程
|
||||
- **流程启动**: 自动启动配置的审批流程
|
||||
@@ -65,6 +70,7 @@ web/src/api/cw/
|
||||
## 📋 实现特色
|
||||
|
||||
### 🔧 技术特色
|
||||
|
||||
1. **Vue 3.5.13 Composition API**: 使用最新的组合式API,代码结构清晰
|
||||
2. **TypeScript支持**: 完整的类型定义,提供良好的开发体验
|
||||
3. **Element Plus UI**: 统一的UI组件库,符合后勤管理模块设计规范
|
||||
@@ -72,6 +78,7 @@ web/src/api/cw/
|
||||
5. **组件化开发**: 高度复用的组件设计,易于维护和扩展
|
||||
|
||||
### 🎨 用户体验特色
|
||||
|
||||
1. **跨部门选择**: 审批人选择支持在多个部门间切换,保持选择状态
|
||||
2. **可视化流程**: 审批流程以图形化方式展示,直观易懂
|
||||
3. **实时验证**: 表单验证和流程验证,及时提供反馈
|
||||
@@ -79,6 +86,7 @@ web/src/api/cw/
|
||||
5. **状态恢复**: 切换部门后自动恢复之前的选择状态
|
||||
|
||||
### 🚀 性能特色
|
||||
|
||||
1. **按需加载**: 组件和API按需加载,优化性能
|
||||
2. **智能缓存**: 合理的数据缓存策略,减少重复请求
|
||||
3. **批量操作**: 支持批量添加、删除等操作,提升效率
|
||||
@@ -87,16 +95,19 @@ web/src/api/cw/
|
||||
## 🔄 核心流程
|
||||
|
||||
### 审批人配置流程
|
||||
|
||||
```
|
||||
管理员登录 → 进入审批人配置 → 选择部门 → 批量选择用户 → 确认添加 → 配置完成
|
||||
```
|
||||
|
||||
### 项目申报审批流程
|
||||
|
||||
```
|
||||
填写申报信息 → 点击提交 → 选择审批流程 → 配置审核人 → 确认提交 → 流程启动
|
||||
```
|
||||
|
||||
### 审批执行流程
|
||||
|
||||
```
|
||||
审核人收到通知 → 查看待审批列表 → 查看项目详情 → 执行审批操作 → 填写意见 → 提交审批
|
||||
```
|
||||
@@ -104,16 +115,19 @@ web/src/api/cw/
|
||||
## 🛠️ 后端支持
|
||||
|
||||
### 数据库设计
|
||||
|
||||
1. **cw_project_auditor**: 项目审核人配置表
|
||||
2. **cw_project_approval_process**: 项目审批流程表
|
||||
3. **cw_approval_record**: 审批记录表(已扩展支持自定义流程)
|
||||
|
||||
### API接口
|
||||
|
||||
1. **审核人配置**: 16个标准API接口(CRUD + 业务功能 + 辅助功能)
|
||||
2. **审批流程**: 10个核心API接口(流程管理 + 审批执行)
|
||||
3. **权限控制**: 基于Sa-Token的权限管理
|
||||
|
||||
### 数据安全
|
||||
|
||||
1. **多租户隔离**: 严格的数据隔离机制
|
||||
2. **权限控制**: 细粒度的功能权限控制
|
||||
3. **操作日志**: 完整的操作记录和审计
|
||||
@@ -121,18 +135,21 @@ web/src/api/cw/
|
||||
## 📊 技术规范
|
||||
|
||||
### 前端规范
|
||||
|
||||
- **组件命名**: PascalCase,清晰的语义化命名
|
||||
- **文件组织**: 按功能模块组织,层次分明
|
||||
- **样式管理**: Scoped CSS,避免样式冲突
|
||||
- **状态管理**: Reactive响应式状态,简洁高效
|
||||
|
||||
### 后端规范
|
||||
|
||||
- **接口设计**: RESTful风格,统一响应格式
|
||||
- **数据验证**: Bean Validation参数校验
|
||||
- **异常处理**: 统一的异常处理机制
|
||||
- **事务管理**: 合理的事务边界控制
|
||||
|
||||
### 代码质量
|
||||
|
||||
- **TypeScript**: 完整的类型检查
|
||||
- **ESLint**: 代码风格检查
|
||||
- **Prettier**: 代码格式化
|
||||
@@ -141,18 +158,21 @@ web/src/api/cw/
|
||||
## 🎯 后续优化
|
||||
|
||||
### 功能扩展
|
||||
|
||||
1. **审批流程管理页面**: 完整的流程监控和管理界面
|
||||
2. **批量审批功能**: 支持批量处理多个项目
|
||||
3. **审批历史查询**: 完整的审批记录查询功能
|
||||
4. **移动端支持**: 基于uni-app的移动端审批应用
|
||||
|
||||
### 性能优化
|
||||
|
||||
1. **虚拟滚动**: 大数据量列表的性能优化
|
||||
2. **缓存策略**: 更智能的数据缓存机制
|
||||
3. **懒加载**: 按需加载组件和数据
|
||||
4. **CDN集成**: 静态资源CDN加速
|
||||
|
||||
### 用户体验
|
||||
|
||||
1. **快捷操作**: 键盘快捷键支持
|
||||
2. **拖拽排序**: 审核人拖拽排序功能
|
||||
3. **消息通知**: 实时消息推送
|
||||
@@ -161,18 +181,21 @@ web/src/api/cw/
|
||||
## 🚀 部署说明
|
||||
|
||||
### 开发环境
|
||||
|
||||
1. 确保Node.js 20.15.x+,npm 10.7.x+
|
||||
2. 安装依赖:`npm install`
|
||||
3. 启动开发服务器:`npm run dev`
|
||||
4. 访问:http://localhost
|
||||
|
||||
### 生产环境
|
||||
|
||||
1. 构建项目:`npm run build`
|
||||
2. 部署到Nginx或其他Web服务器
|
||||
3. 配置反向代理到后端API
|
||||
4. 确保数据库脚本已执行
|
||||
|
||||
### 数据库部署
|
||||
|
||||
1. 执行数据库脚本:`docs/数据库修复脚本.sql`
|
||||
2. 配置权限菜单和角色权限
|
||||
3. 创建测试用户和审核人数据
|
||||
@@ -187,4 +210,4 @@ web/src/api/cw/
|
||||
|
||||
**📝 维护日期**: 2025-01-03
|
||||
**📁 文件**: 完整的前后端实现 + 数据库脚本 + API文档
|
||||
**🔄 状态**: 核心功能完成,待进一步测试和优化
|
||||
**🔄 状态**: 核心功能完成,待进一步测试和优化
|
||||
@@ -1158,7 +1158,7 @@ const confirmApprovalSubmit = async () => {
|
||||
// 构建审批流程数据
|
||||
const approvalData = {
|
||||
applicationId: form.id,
|
||||
applicationNo: form.applicationNo, // 新增:申报编号
|
||||
applicationNo: form.applicationNo, // 新增:申报编号
|
||||
auditorIds: processConfig.auditorIds,
|
||||
processName: processConfig.processName || `${form.projectName}审批流程`,
|
||||
submitterId: userStore.userId,
|
||||
|
||||
@@ -122,10 +122,14 @@
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['cw:application:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['cw:application:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['cw:application:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="handleSubmit" :disabled="multiple" v-hasPermi="['cw:application:submit']">提交审批</el-button>
|
||||
<el-button type="info" plain icon="Upload" @click="handleSubmit" :disabled="multiple" v-hasPermi="['cw:application:submit']"
|
||||
>提交审批</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cw:application:export']">导出</el-button>
|
||||
@@ -135,7 +139,15 @@
|
||||
</template>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table v-loading="loading" border :data="applicationList" @selection-change="handleSelectionChange" style="width: 100%" :scroll-x="true" class="scroll-table">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
border
|
||||
:data="applicationList"
|
||||
@selection-change="handleSelectionChange"
|
||||
style="width: 100%"
|
||||
:scroll-x="true"
|
||||
class="scroll-table"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||
<el-table-column label="申报编号" align="center" prop="applicationNo" min-width="140" />
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" min-width="180" show-overflow-tooltip />
|
||||
@@ -169,31 +181,13 @@
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['cw:application:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="提交审批" placement="top" v-if="['draft', 'returned'].includes(scope.row.approvalStatus)">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="Upload"
|
||||
@click="handleSubmit(scope.row)"
|
||||
v-hasPermi="['cw:application:submit']"
|
||||
/>
|
||||
<el-button link type="warning" icon="Upload" @click="handleSubmit(scope.row)" v-hasPermi="['cw:application:submit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="撤回" placement="top" v-if="scope.row.approvalStatus === 'submitted'">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="RefreshLeft"
|
||||
@click="handleWithdraw(scope.row)"
|
||||
v-hasPermi="['cw:application:withdraw']"
|
||||
/>
|
||||
<el-button link type="info" icon="RefreshLeft" @click="handleWithdraw(scope.row)" v-hasPermi="['cw:application:withdraw']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="['draft', 'cancelled', 'returned'].includes(scope.row.approvalStatus)">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['cw:application:remove']"
|
||||
/>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['cw:application:remove']" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
@@ -234,6 +228,20 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批流程选择对话框 -->
|
||||
<el-dialog title="选择审批流程" v-model="approvalProcessDialog.visible" width="800px" append-to-body :close-on-click-modal="false">
|
||||
<ApprovalProcessSelector
|
||||
ref="approvalProcessSelectorRef"
|
||||
v-model="approvalProcessDialog.selectedAuditors"
|
||||
/>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelSubmitApproval">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmSubmitApproval">确认提交</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -247,8 +255,11 @@ import {
|
||||
submitApplication,
|
||||
getApplicationStatistics
|
||||
} from '@/api/cw/projectApplication';
|
||||
import { startCustomApprovalProcess, type CwCustomApprovalRequest } from '@/api/cw/projectApproval';
|
||||
import { getInfo } from '@/api/login';
|
||||
import ApplicationForm from './form.vue';
|
||||
import ApplicationSummary from './summary.vue';
|
||||
import ApprovalProcessSelector from '../projectApproval/components/ApprovalProcessSelector.vue';
|
||||
import Pagination from '@/components/Pagination/index.vue';
|
||||
|
||||
export default {
|
||||
@@ -256,6 +267,7 @@ export default {
|
||||
components: {
|
||||
ApplicationForm,
|
||||
ApplicationSummary,
|
||||
ApprovalProcessSelector,
|
||||
Pagination
|
||||
},
|
||||
data() {
|
||||
@@ -300,7 +312,14 @@ export default {
|
||||
totalCount: 0,
|
||||
approvedCount: 0,
|
||||
pendingCount: 0,
|
||||
draftCount: 0
|
||||
draftCount: 0,
|
||||
// 审批流程选择对话框
|
||||
approvalProcessDialog: {
|
||||
visible: false,
|
||||
applicationId: null,
|
||||
applicationNo: null,
|
||||
selectedAuditors: []
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -476,21 +495,92 @@ export default {
|
||||
},
|
||||
/** 提交审批 */
|
||||
handleSubmit(row) {
|
||||
const applications = row?.applicationNo
|
||||
? [row.applicationNo]
|
||||
: this.applicationList.filter((item) => this.ids.includes(item.id)).map((item) => item.applicationNo);
|
||||
if (row) {
|
||||
// 单个提交
|
||||
this.approvalProcessDialog.applicationId = row.id;
|
||||
this.approvalProcessDialog.applicationNo = row.applicationNo;
|
||||
this.approvalProcessDialog.visible = true;
|
||||
this.approvalProcessDialog.selectedAuditors = [];
|
||||
} else {
|
||||
// 批量提交 - 暂不支持
|
||||
this.$modal.msgWarning('批量提交审批功能暂未开放,请单个提交');
|
||||
}
|
||||
},
|
||||
/** 确认提交审批 */
|
||||
async confirmSubmitApproval() {
|
||||
try {
|
||||
const processSelector = this.$refs.approvalProcessSelectorRef;
|
||||
if (!processSelector) {
|
||||
this.$modal.msgError('审批流程选择组件未加载');
|
||||
return;
|
||||
}
|
||||
|
||||
this.$modal
|
||||
.confirm('是否确认提交申报编号为"' + applications.join('", "') + '"的数据项?')
|
||||
.then(() => {
|
||||
return submitApplication(row?.id || this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.getList();
|
||||
this.getStatistics(); // 提交后更新统计
|
||||
this.$modal.msgSuccess('提交成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
// 验证审批流程
|
||||
const validation = processSelector.validateProcess();
|
||||
if (!validation.valid) {
|
||||
this.$modal.msgError(validation.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
let userInfo = null;
|
||||
try {
|
||||
const userResponse = await getInfo();
|
||||
if (userResponse.code === 200 && userResponse.data && userResponse.data.user) {
|
||||
userInfo = userResponse.data.user;
|
||||
}
|
||||
} catch (userError) {
|
||||
console.error('获取用户信息失败:', userError);
|
||||
this.$modal.msgError('获取用户信息失败,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userInfo || !userInfo.userId) {
|
||||
this.$modal.msgError('用户信息不完整,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取审批流程配置
|
||||
const processConfig = processSelector.getProcessConfig();
|
||||
|
||||
// 构建请求数据
|
||||
const approvalData: CwCustomApprovalRequest = {
|
||||
applicationId: this.approvalProcessDialog.applicationId,
|
||||
applicationNo: this.approvalProcessDialog.applicationNo,
|
||||
processName: processConfig.processName,
|
||||
auditorIds: processConfig.auditorIds,
|
||||
submitterId: userInfo.userId,
|
||||
submitterName: userInfo.nickName || userInfo.userName
|
||||
};
|
||||
|
||||
console.log('启动审批流程数据:', approvalData);
|
||||
|
||||
// 启动审批流程
|
||||
const result = await startCustomApprovalProcess(approvalData);
|
||||
console.log('启动审批流程结果:', result);
|
||||
|
||||
// 关闭对话框
|
||||
this.approvalProcessDialog.visible = false;
|
||||
this.approvalProcessDialog.applicationId = null;
|
||||
this.approvalProcessDialog.applicationNo = null;
|
||||
this.approvalProcessDialog.selectedAuditors = [];
|
||||
|
||||
// 刷新列表和统计
|
||||
this.getList();
|
||||
this.getStatistics();
|
||||
|
||||
this.$modal.msgSuccess('审批流程启动成功');
|
||||
} catch (error) {
|
||||
console.error('启动审批流程失败:', error);
|
||||
this.$modal.msgError('启动审批流程失败:' + (error.message || '未知错误'));
|
||||
}
|
||||
},
|
||||
/** 取消提交审批 */
|
||||
cancelSubmitApproval() {
|
||||
this.approvalProcessDialog.visible = false;
|
||||
this.approvalProcessDialog.applicationId = null;
|
||||
this.approvalProcessDialog.applicationNo = null;
|
||||
this.approvalProcessDialog.selectedAuditors = [];
|
||||
},
|
||||
/** 撤回申请 */
|
||||
handleWithdraw(row) {
|
||||
@@ -642,7 +732,9 @@ export default {
|
||||
/* 卡片样式 */
|
||||
:deep(.el-card) {
|
||||
border: 1px solid #e4e7ed;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
|
||||
box-shadow:
|
||||
0 2px 4px rgba(0, 0, 0, 0.12),
|
||||
0 0 6px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 表格卡片头部样式 */
|
||||
|
||||
@@ -98,7 +98,15 @@
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @update:page="queryParams.pageNum = $event" @update:limit="queryParams.pageSize = $event" @pagination="getList" />
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page="queryParams.pageNum"
|
||||
:limit="queryParams.pageSize"
|
||||
@update:page="queryParams.pageNum = $event"
|
||||
@update:limit="queryParams.pageSize = $event"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
@@ -377,7 +385,9 @@ export default {
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
|
||||
box-shadow:
|
||||
0 2px 4px rgba(0, 0, 0, 0.12),
|
||||
0 0 6px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
|
||||
@@ -157,8 +157,8 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="审批决定" prop="approvalResult" required>
|
||||
<el-radio-group v-model="approvalForm.approvalResult">
|
||||
<el-radio label="approve">通过</el-radio>
|
||||
<el-radio label="reject">退回申请人</el-radio>
|
||||
<el-radio value="approve">通过</el-radio>
|
||||
<el-radio value="reject">退回申请人</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批意见" prop="approvalComment">
|
||||
|
||||
@@ -7,23 +7,27 @@
|
||||
## 主要功能
|
||||
|
||||
### 1. 审批流程管理
|
||||
|
||||
- 查询审批流程列表(支持分页)
|
||||
- 按申报编号、流程状态、提交人等条件筛选
|
||||
- 显示审批进度和流程状态
|
||||
- 支持终止正在进行的审批流程
|
||||
|
||||
### 2. 待我审批
|
||||
|
||||
- 查看分配给当前用户的待审批任务
|
||||
- 执行审批操作(通过/打回)
|
||||
- 填写审批意见
|
||||
- 查看申报详情和审批记录
|
||||
|
||||
### 3. 我的已审批
|
||||
|
||||
- 查看当前用户已完成的审批记录
|
||||
- 显示审批动作和意见
|
||||
- 统计审批效率和处理时长
|
||||
|
||||
### 4. 审批记录跟踪
|
||||
|
||||
- 完整的审批时间线展示
|
||||
- 每个审批节点的详细信息
|
||||
- 审批意见和处理结果记录
|
||||
@@ -39,21 +43,25 @@ web/src/views/cw/projectApproval/
|
||||
## 功能特性
|
||||
|
||||
### 1. 多标签页设计
|
||||
|
||||
- **审批流程管理**: 查看所有审批流程的整体情况
|
||||
- **待我审批**: 专注于当前用户需要处理的审批任务
|
||||
- **我的已审批**: 查看历史审批记录和统计
|
||||
|
||||
### 2. 统计卡片
|
||||
|
||||
- 总流程数、等待开始、进行中、已完成等状态统计
|
||||
- 平均处理时长、待我审批数量等关键指标
|
||||
- 本月完成数量、已终止流程等趋势数据
|
||||
|
||||
### 3. 审批操作
|
||||
|
||||
- 支持批量审批和单个审批
|
||||
- 必填审批意见,确保审批质量
|
||||
- 实时更新审批状态和流程进度
|
||||
|
||||
### 4. 流程可视化
|
||||
|
||||
- 进度条显示审批进度
|
||||
- 时间线展示审批历史
|
||||
- 状态标签清晰标识各阶段
|
||||
@@ -61,6 +69,7 @@ web/src/views/cw/projectApproval/
|
||||
## API接口
|
||||
|
||||
使用的主要API接口:
|
||||
|
||||
- `listProjectApproval` - 查询审批流程列表
|
||||
- `listPendingApprovals` - 查询待我审批列表
|
||||
- `listProcessedApprovals` - 查询我的已审批列表
|
||||
@@ -72,6 +81,7 @@ web/src/views/cw/projectApproval/
|
||||
## 权限控制
|
||||
|
||||
页面功能对应的权限标识:
|
||||
|
||||
- `cw:projectApproval:list` - 查询权限
|
||||
- `cw:projectApproval:add` - 启动流程权限
|
||||
- `cw:projectApproval:approve` - 审批权限
|
||||
@@ -81,18 +91,21 @@ web/src/views/cw/projectApproval/
|
||||
## 状态说明
|
||||
|
||||
### 流程状态
|
||||
|
||||
- `waiting` - 等待开始
|
||||
- `running` - 进行中
|
||||
- `running` - 进行中
|
||||
- `completed` - 已完成
|
||||
- `terminated` - 已终止
|
||||
|
||||
### 节点状态
|
||||
|
||||
- `waiting` - 等待审批
|
||||
- `approved` - 已通过
|
||||
- `rejected` - 已拒绝
|
||||
- `pending` - 待处理
|
||||
|
||||
### 审批动作
|
||||
|
||||
- `approve` - 审批通过
|
||||
- `reject` - 打回申请
|
||||
|
||||
@@ -114,6 +127,7 @@ web/src/views/cw/projectApproval/
|
||||
## 使用说明
|
||||
|
||||
### 审批人员操作流程
|
||||
|
||||
1. 登录系统,进入项目审批管理页面
|
||||
2. 点击"待我审批"标签页查看待处理任务
|
||||
3. 点击具体记录查看申报详情
|
||||
@@ -121,6 +135,7 @@ web/src/views/cw/projectApproval/
|
||||
5. 提交审批结果,系统自动推进流程
|
||||
|
||||
### 管理员操作流程
|
||||
|
||||
1. 在"审批流程管理"标签页查看所有流程状态
|
||||
2. 可以终止异常或不需要的审批流程
|
||||
3. 通过统计卡片监控审批效率和积压情况
|
||||
@@ -131,4 +146,4 @@ web/src/views/cw/projectApproval/
|
||||
1. 审批意见为必填项,且至少5个字符
|
||||
2. 审批操作不可撤销,请谨慎操作
|
||||
3. 终止流程会影响项目申报状态,需谨慎使用
|
||||
4. 导出功能需要相应权限,请联系管理员配置
|
||||
4. 导出功能需要相应权限,请联系管理员配置
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="approval-process-selector">
|
||||
<el-form-item label="审批流程" required>
|
||||
<el-radio-group v-model="processType" @change="handleProcessTypeChange">
|
||||
<el-radio label="custom">自定义审批流程</el-radio>
|
||||
<el-radio value="custom">自定义审批流程</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@@ -307,11 +307,11 @@
|
||||
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="120px">
|
||||
<el-form-item label="审批操作" prop="approveAction" required>
|
||||
<el-radio-group v-model="approvalForm.approveAction">
|
||||
<el-radio label="approve">
|
||||
<el-radio value="approve">
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
审批通过
|
||||
</el-radio>
|
||||
<el-radio label="reject">
|
||||
<el-radio value="reject">
|
||||
<el-icon><CircleClose /></el-icon>
|
||||
打回申请
|
||||
</el-radio>
|
||||
|
||||
@@ -21,11 +21,7 @@
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-card shadow="never" class="table-card">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="pendingList"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table v-loading="loading" :data="pendingList" @row-dblclick="handleView">
|
||||
<el-table-column label="序号" type="index" width="80" align="center" />
|
||||
<el-table-column label="申报编号" prop="applicationNo" width="160" />
|
||||
<el-table-column label="项目名称" prop="projectName" show-overflow-tooltip />
|
||||
@@ -64,13 +60,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 审批操作对话框 -->
|
||||
<el-dialog
|
||||
title="审批操作"
|
||||
v-model="approvalDialog.visible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-dialog title="审批操作" v-model="approvalDialog.visible" width="600px" append-to-body destroy-on-close>
|
||||
<div class="approval-content">
|
||||
<!-- 项目信息 -->
|
||||
<div class="project-info">
|
||||
@@ -87,25 +77,18 @@
|
||||
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
|
||||
<el-form-item label="审批操作" prop="action">
|
||||
<el-radio-group v-model="approvalForm.action">
|
||||
<el-radio label="approve">
|
||||
<el-radio value="approve">
|
||||
<el-icon><Check /></el-icon>
|
||||
审批通过
|
||||
</el-radio>
|
||||
<el-radio label="reject">
|
||||
<el-radio value="reject">
|
||||
<el-icon><Close /></el-icon>
|
||||
审批拒绝
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批意见" prop="opinion">
|
||||
<el-input
|
||||
v-model="approvalForm.opinion"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入审批意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="approvalForm.opinion" type="textarea" :rows="4" placeholder="请输入审批意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -113,27 +96,15 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="approvalDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitApproval" :loading="approvalDialog.submitting">
|
||||
确认审批
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitApproval" :loading="approvalDialog.submitting"> 确认审批 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情查看对话框 -->
|
||||
<el-dialog
|
||||
title="项目申报详情"
|
||||
v-model="detailDialog.visible"
|
||||
width="1200px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
top="20px"
|
||||
>
|
||||
<el-dialog title="项目申报详情" v-model="detailDialog.visible" width="1200px" append-to-body destroy-on-close top="20px">
|
||||
<div v-loading="detailDialog.loading">
|
||||
<ApplicationSummary
|
||||
v-if="detailDialog.visible && currentApplicationId"
|
||||
:applicationId="currentApplicationId"
|
||||
/>
|
||||
<ApplicationSummary v-if="detailDialog.visible && currentApplicationId" :applicationId="currentApplicationId" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
@@ -283,7 +254,6 @@ const submitApproval = async () => {
|
||||
|
||||
// 重新加载列表
|
||||
getList();
|
||||
|
||||
} catch (error) {
|
||||
ElMessage.error('审批操作失败');
|
||||
} finally {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
# 项目审批人批量添加功能Bug修复报告
|
||||
|
||||
## 🐛 问题描述
|
||||
|
||||
用户反馈:批量添加审批人时,选择用户后点击提交,页面提示添加成功,但后台数据库中没有新增数据。
|
||||
|
||||
## 🔍 问题分析
|
||||
|
||||
### 根本原因
|
||||
|
||||
1. **前端使用模拟API函数**:`index.vue` 中第354-378行定义了模拟的API函数,而不是真实的API调用
|
||||
2. **缺少正确的API导入**:没有导入真实的 `batchAddProjectAuditor` 等API函数
|
||||
3. **数据格式不匹配**:前端传递的数据格式与后端期望格式存在差异
|
||||
|
||||
### 问题代码
|
||||
|
||||
```javascript
|
||||
// 错误:使用模拟函数
|
||||
const batchAddProjectAuditor = (data: any) => {
|
||||
@@ -21,18 +24,20 @@ const batchAddProjectAuditor = (data: any) => {
|
||||
## 🔧 修复方案
|
||||
|
||||
### 1. 导入真实API函数
|
||||
|
||||
```javascript
|
||||
import {
|
||||
listProjectAuditor,
|
||||
getProjectAuditor,
|
||||
updateProjectAuditor,
|
||||
delProjectAuditor,
|
||||
batchAddProjectAuditor
|
||||
import {
|
||||
listProjectAuditor,
|
||||
getProjectAuditor,
|
||||
updateProjectAuditor,
|
||||
delProjectAuditor,
|
||||
batchAddProjectAuditor
|
||||
} from '@/api/cw/projectAuditor'
|
||||
import type { CwProjectAuditorVO, CwProjectAuditorForm, CwProjectAuditorQuery, CwProjectAuditorBatchBo } from '@/api/cw/types'
|
||||
```
|
||||
|
||||
### 2. 修复数据类型定义
|
||||
|
||||
```typescript
|
||||
// 修正数据类型
|
||||
const auditorList = ref<CwProjectAuditorVO[]>([])
|
||||
@@ -43,6 +48,7 @@ const editDialog = reactive({
|
||||
```
|
||||
|
||||
### 3. 修复批量添加数据格式
|
||||
|
||||
```javascript
|
||||
const confirmBatchAdd = async () => {
|
||||
const userIds = batchDialog.selectedUsers.map((user) => user.userId as number)
|
||||
@@ -50,23 +56,25 @@ const confirmBatchAdd = async () => {
|
||||
userIds: userIds,
|
||||
remark: '批量添加'
|
||||
}
|
||||
|
||||
|
||||
const response = await batchAddProjectAuditor(data)
|
||||
// ... 处理响应
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 修复后端数据解析
|
||||
|
||||
```javascript
|
||||
const response = await listProjectAuditor(queryParams.value)
|
||||
const response = await listProjectAuditor(queryParams.value);
|
||||
// 后端返回的是TableDataInfo格式
|
||||
auditorList.value = response.data?.rows || []
|
||||
total.value = response.data?.total || 0
|
||||
auditorList.value = response.data?.rows || [];
|
||||
total.value = response.data?.total || 0;
|
||||
```
|
||||
|
||||
## ✅ 修复内容清单
|
||||
|
||||
### 前端修复
|
||||
|
||||
- [x] 移除模拟API函数,导入真实API
|
||||
- [x] 修复TypeScript类型定义
|
||||
- [x] 修复批量添加数据格式
|
||||
@@ -75,6 +83,7 @@ total.value = response.data?.total || 0
|
||||
- [x] 修复按钮事件类型问题
|
||||
|
||||
### 后端验证
|
||||
|
||||
- [x] 确认后端API接口正常工作
|
||||
- [x] 确认数据库表结构正确
|
||||
- [x] 确认权限控制配置
|
||||
@@ -82,13 +91,16 @@ total.value = response.data?.total || 0
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试用例
|
||||
|
||||
1. **批量添加功能测试**
|
||||
|
||||
- 选择部门
|
||||
- 选择多个用户
|
||||
- 点击确认添加
|
||||
- 验证数据库是否成功插入数据
|
||||
|
||||
2. **查询列表功能测试**
|
||||
|
||||
- 验证API调用正常
|
||||
- 验证数据格式解析正确
|
||||
- 验证分页功能
|
||||
@@ -99,11 +111,13 @@ total.value = response.data?.total || 0
|
||||
- 测试权限不足情况
|
||||
|
||||
### 测试脚本
|
||||
|
||||
提供了 `test-api.js` 文件用于功能验证测试。
|
||||
|
||||
## 📋 技术规范遵循
|
||||
|
||||
### 符合项目规范
|
||||
|
||||
- ✅ **前端开发规范**:符合Vue 3.5.13 + TypeScript + Element Plus架构
|
||||
- ✅ **API接口规范**:使用统一的request工具和错误处理
|
||||
- ✅ **UI设计规范**:继承后勤管理模块的样式风格和颜色主题
|
||||
@@ -111,6 +125,7 @@ total.value = response.data?.total || 0
|
||||
- ✅ **权限控制**:使用@SaCheckPermission注解控制接口权限
|
||||
|
||||
### 代码质量
|
||||
|
||||
- ✅ **类型安全**:完整的TypeScript类型定义
|
||||
- ✅ **错误处理**:完善的异常捕获和用户提示
|
||||
- ✅ **用户体验**:优化的加载状态和反馈信息
|
||||
@@ -119,12 +134,14 @@ total.value = response.data?.total || 0
|
||||
## 🚀 部署说明
|
||||
|
||||
### 验证步骤
|
||||
|
||||
1. 确保后端服务正常运行
|
||||
2. 检查数据库表 `cw_project_auditor` 是否存在
|
||||
3. 验证用户权限配置 `cw:projectAuditor:add`
|
||||
4. 测试前端页面功能
|
||||
|
||||
### 预期结果
|
||||
|
||||
- 批量添加功能正常工作
|
||||
- 数据成功写入数据库
|
||||
- 页面显示准确的成功/失败提示
|
||||
@@ -132,16 +149,17 @@ total.value = response.data?.total || 0
|
||||
|
||||
## 📝 修复日志
|
||||
|
||||
| 时间 | 修复内容 | 影响范围 |
|
||||
|------|----------|----------|
|
||||
| 2025-01-03 | 修复API调用问题 | 批量添加功能 |
|
||||
| 2025-01-03 | 修复数据格式问题 | 前后端数据传递 |
|
||||
| 时间 | 修复内容 | 影响范围 |
|
||||
| ---------- | ---------------- | ------------------ |
|
||||
| 2025-01-03 | 修复API调用问题 | 批量添加功能 |
|
||||
| 2025-01-03 | 修复数据格式问题 | 前后端数据传递 |
|
||||
| 2025-01-03 | 修复类型定义问题 | TypeScript类型安全 |
|
||||
| 2025-01-03 | 修复响应解析问题 | 列表查询功能 |
|
||||
| 2025-01-03 | 修复响应解析问题 | 列表查询功能 |
|
||||
|
||||
## 🎯 总结
|
||||
|
||||
本次修复解决了批量添加审批人功能的根本问题,确保:
|
||||
|
||||
1. **功能正常**:批量添加能够真实地向后端发送请求并保存数据
|
||||
2. **类型安全**:完整的TypeScript类型定义和检查
|
||||
3. **用户体验**:准确的成功/失败提示和数据刷新
|
||||
|
||||
@@ -7,18 +7,21 @@
|
||||
## 主要功能
|
||||
|
||||
### 1. 审批人列表管理
|
||||
|
||||
- 查询审批人列表(支持分页)
|
||||
- 按用户姓名、部门、状态等条件筛选
|
||||
- 显示审批人基本信息(姓名、部门、联系方式等)
|
||||
- 支持审批人状态管理(正常/停用)
|
||||
|
||||
### 2. 批量添加审批人
|
||||
|
||||
- 通过部门树选择部门
|
||||
- 展示部门下的可用用户列表
|
||||
- 支持多选用户批量添加为审批人
|
||||
- 自动过滤已存在的审批人
|
||||
|
||||
### 3. 审批人信息维护
|
||||
|
||||
- 修改审批人联系信息
|
||||
- 更新审批人状态
|
||||
- 添加备注信息
|
||||
@@ -35,6 +38,7 @@ web/src/views/cw/projectAuditor/
|
||||
## API接口
|
||||
|
||||
使用的主要API接口:
|
||||
|
||||
- `listProjectAuditor` - 查询审批人列表
|
||||
- `getProjectAuditor` - 获取审批人详情
|
||||
- `updateProjectAuditor` - 更新审批人信息
|
||||
@@ -45,6 +49,7 @@ web/src/views/cw/projectAuditor/
|
||||
## 权限控制
|
||||
|
||||
页面功能对应的权限标识:
|
||||
|
||||
- `cw:projectAuditor:query` - 查询权限
|
||||
- `cw:projectAuditor:add` - 新增权限
|
||||
- `cw:projectAuditor:edit` - 编辑权限
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>测试审核人列表API</span>
|
||||
<el-button @click="testAuditorList" type="primary" style="float: right;">测试API</el-button>
|
||||
<el-button @click="testAuditorList" type="primary" style="float: right">测试API</el-button>
|
||||
</template>
|
||||
|
||||
<div v-if="loading">加载中...</div>
|
||||
@@ -32,10 +32,10 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-top: 20px;">
|
||||
<el-card style="margin-top: 20px">
|
||||
<template #header>
|
||||
<span>测试部门数据API</span>
|
||||
<el-button @click="testDeptList" type="success" style="float: right;">测试部门API</el-button>
|
||||
<el-button @click="testDeptList" type="success" style="float: right">测试部门API</el-button>
|
||||
</template>
|
||||
|
||||
<div v-if="deptLoading">加载中...</div>
|
||||
@@ -101,7 +101,6 @@ const testAuditorList = async () => {
|
||||
}
|
||||
|
||||
console.log('处理后的审核人列表:', auditorList.value);
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试审核人API失败:', error);
|
||||
} finally {
|
||||
@@ -126,7 +125,6 @@ const testDeptList = async () => {
|
||||
|
||||
deptList.value = Array.isArray(deptData) ? deptData : [];
|
||||
console.log('处理后的部门列表:', deptList.value);
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试部门API失败:', error);
|
||||
} finally {
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
@@ -106,7 +105,7 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExport" v-hasPermi="['cw:projectAuditor:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :show-search="showSearch" @update:show-search="(val) => showSearch = val" @queryTable="getList"></right-toolbar>
|
||||
<right-toolbar :show-search="showSearch" @update:show-search="(val) => (showSearch = val)" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
@@ -166,10 +165,22 @@
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['cw:projectAuditor:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.status === '1'" content="停用" placement="top">
|
||||
<el-button link type="warning" icon="CircleClose" @click="handleDisable(scope.row)" v-hasPermi="['cw:projectAuditor:edit']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="CircleClose"
|
||||
@click="handleDisable(scope.row)"
|
||||
v-hasPermi="['cw:projectAuditor:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-else content="启用" placement="top">
|
||||
<el-button link type="success" icon="CircleCheck" @click="handleEnable(scope.row)" v-hasPermi="['cw:projectAuditor:edit']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="CircleCheck"
|
||||
@click="handleEnable(scope.row)"
|
||||
v-hasPermi="['cw:projectAuditor:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -181,15 +192,15 @@
|
||||
:total="total"
|
||||
:page="queryParams.pageNum"
|
||||
:limit="queryParams.pageSize"
|
||||
@update:page="(val) => queryParams.pageNum = val"
|
||||
@update:limit="(val) => queryParams.pageSize = val"
|
||||
@update:page="(val) => (queryParams.pageNum = val)"
|
||||
@update:limit="(val) => (queryParams.pageSize = val)"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 批量添加审核人对话框 -->
|
||||
<el-dialog title="批量添加审核人" v-model="batchAddDialog.visible" width="80%" append-to-body>
|
||||
<el-dialog title="批量添加审核人" v-model="batchAddDialog.visible" width="1200px" append-to-body>
|
||||
<div class="batch-add-container">
|
||||
<!-- 左侧部门选择 -->
|
||||
<div class="dept-select-section">
|
||||
@@ -221,24 +232,42 @@
|
||||
<el-card shadow="never" class="user-table-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>部门用户列表</span>
|
||||
<el-tag v-if="currentDeptName" type="info" size="small">{{ currentDeptName }}</el-tag>
|
||||
<div class="header-left">
|
||||
<span>部门用户列表</span>
|
||||
<el-tag v-if="currentDeptName" type="info" size="small">{{ currentDeptName }}</el-tag>
|
||||
<span v-if="deptUserList.length > 0" class="user-count">({{ deptUserList.length }}人)</span>
|
||||
</div>
|
||||
<div class="header-right" v-if="deptUserList.length > 0">
|
||||
<el-input v-model="userSearchText" placeholder="搜索用户" size="small" style="width: 180px" clearable @input="handleUserSearch">
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
v-loading="userLoading"
|
||||
:data="deptUserList"
|
||||
:data="filteredUserList"
|
||||
@selection-change="handleUserSelectionChange"
|
||||
max-height="400"
|
||||
style="width: 100%"
|
||||
table-layout="fixed"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="姓名" prop="nickName" width="100" />
|
||||
<el-table-column label="用户名" prop="userName" width="120" />
|
||||
<el-table-column label="部门" prop="deptName" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="手机号" prop="phonenumber" width="130" />
|
||||
<el-table-column label="邮箱" prop="email" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="姓名" prop="nickName" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="用户名" prop="userName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="部门" prop="deptName" width="160" show-overflow-tooltip />
|
||||
<el-table-column label="手机号" prop="phonenumber" width="130" show-overflow-tooltip />
|
||||
<el-table-column label="邮箱" prop="email" width="200" show-overflow-tooltip />
|
||||
<el-table-column label="状态" prop="status" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '1' ? 'success' : 'danger'" size="small">
|
||||
{{ row.status === '1' ? '正常' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
@@ -261,7 +290,7 @@
|
||||
@close="removeSelectedUser(user)"
|
||||
class="selected-user-tag"
|
||||
>
|
||||
{{ user.nickName }}({{ user.userName }})
|
||||
{{ user.nickName }}({{ user.userName }}) - {{ user.deptName || '未知部门' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -271,12 +300,7 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="batchAddDialog.visible = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="confirmBatchAdd"
|
||||
:disabled="batchAddDialog.selectedUsers.length === 0"
|
||||
:loading="batchAddLoading"
|
||||
>
|
||||
<el-button type="primary" @click="confirmBatchAdd" :disabled="batchAddDialog.selectedUsers.length === 0" :loading="batchAddLoading">
|
||||
确定添加 ({{ batchAddDialog.selectedUsers.length }})
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -329,8 +353,8 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="editDialog.form.status">
|
||||
<el-radio label="1">正常</el-radio>
|
||||
<el-radio label="0">停用</el-radio>
|
||||
<el-radio value="1">正常</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -339,14 +363,7 @@
|
||||
<el-input v-model="editDialog.form.email" placeholder="请输入邮箱地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注说明" prop="remark">
|
||||
<el-input
|
||||
v-model="editDialog.form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注说明"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="editDialog.form.remark" type="textarea" :rows="3" placeholder="请输入备注说明" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -360,7 +377,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CwProjectAuditor">
|
||||
import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue';
|
||||
import { ref, reactive, onMounted, nextTick, getCurrentInstance, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox, ElTable, ElTree, FormInstance } from 'element-plus';
|
||||
import {
|
||||
listProjectAuditor,
|
||||
@@ -369,18 +386,20 @@ import {
|
||||
updateProjectAuditor,
|
||||
delProjectAuditor,
|
||||
batchAddProjectAuditor,
|
||||
// batchAddProjectAuditorWithDetails, // 后端暂不支持此接口
|
||||
listMyConfigAuditors,
|
||||
listUserByDeptId,
|
||||
enableAuditors,
|
||||
disableAuditors,
|
||||
exportProjectAuditor,
|
||||
listUserByDeptId,
|
||||
type CwProjectAuditorVO,
|
||||
type CwProjectAuditorForm,
|
||||
type CwProjectAuditorQuery,
|
||||
type BatchAddRequest,
|
||||
type BatchAddDetailRequest,
|
||||
type DeptUserVO
|
||||
} from '@/api/cw/projectAuditor';
|
||||
import { listDept } from '@/api/system/dept/index';
|
||||
import { deptTreeSelect } from '@/api/system/user/index';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
@@ -417,7 +436,6 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
|
||||
|
||||
// 部门数据
|
||||
const deptOptions = ref([]);
|
||||
const deptTreeData = ref([]);
|
||||
@@ -433,6 +451,20 @@ const batchAddLoading = ref(false);
|
||||
const userLoading = ref(false);
|
||||
const deptUserList = ref<DeptUserVO[]>([]);
|
||||
const currentDeptName = ref('');
|
||||
const userSearchText = ref('');
|
||||
const filteredUserList = computed(() => {
|
||||
if (!userSearchText.value) {
|
||||
return deptUserList.value;
|
||||
}
|
||||
const searchText = userSearchText.value.toLowerCase();
|
||||
return deptUserList.value.filter(
|
||||
(user) =>
|
||||
user.userName?.toLowerCase().includes(searchText) ||
|
||||
user.nickName?.toLowerCase().includes(searchText) ||
|
||||
user.phonenumber?.includes(searchText) ||
|
||||
user.email?.toLowerCase().includes(searchText)
|
||||
);
|
||||
});
|
||||
|
||||
// 详情对话框
|
||||
const detailDialog = reactive({
|
||||
@@ -450,15 +482,9 @@ const editLoading = ref(false);
|
||||
|
||||
// 编辑表单验证规则
|
||||
const editRules = {
|
||||
phoneNumber: [
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ type: 'email' as const, message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择状态', trigger: 'change' }
|
||||
]
|
||||
phoneNumber: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }],
|
||||
email: [{ type: 'email' as const, message: '请输入正确的邮箱地址', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 查询审核人列表
|
||||
@@ -500,7 +526,6 @@ const getList = async () => {
|
||||
|
||||
console.log('处理后的审核人列表:', auditorList.value);
|
||||
console.log('总数:', total.value);
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取审核人列表失败:', error);
|
||||
auditorList.value = [];
|
||||
@@ -510,7 +535,6 @@ const getList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
@@ -526,7 +550,7 @@ const resetQuery = () => {
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: CwProjectAuditorVO[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
@@ -542,7 +566,7 @@ const handleUpdate = (row?: CwProjectAuditorVO) => {
|
||||
if (row) {
|
||||
editDialog.form = { ...row };
|
||||
} else if (ids.value.length === 1) {
|
||||
const selectedRow = auditorList.value.find(item => item.id === ids.value[0]);
|
||||
const selectedRow = auditorList.value.find((item) => item.id === ids.value[0]);
|
||||
if (selectedRow) {
|
||||
editDialog.form = { ...selectedRow };
|
||||
}
|
||||
@@ -573,19 +597,13 @@ const submitEdit = async () => {
|
||||
// 删除审核人
|
||||
const handleDelete = (row?: CwProjectAuditorVO) => {
|
||||
const deleteIds = row ? [row.id] : ids.value;
|
||||
const names = row ? [row.userName] : auditorList.value
|
||||
.filter((item) => deleteIds.includes(item.id))
|
||||
.map((item) => item.userName);
|
||||
const names = row ? [row.userName] : auditorList.value.filter((item) => deleteIds.includes(item.id)).map((item) => item.userName);
|
||||
|
||||
ElMessageBox.confirm(
|
||||
`是否确认删除审核人"${names.join(', ')}"?`,
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(async () => {
|
||||
ElMessageBox.confirm(`是否确认删除审核人"${names.join(', ')}"?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await delProjectAuditor(deleteIds);
|
||||
ElMessage.success('删除成功');
|
||||
@@ -649,44 +667,28 @@ const handleExport = () => {
|
||||
const handleBatchAdd = () => {
|
||||
batchAddDialog.visible = true;
|
||||
batchAddDialog.selectedUsers = [];
|
||||
userSearchText.value = '';
|
||||
currentDeptName.value = '';
|
||||
deptUserList.value = [];
|
||||
loadDeptTree();
|
||||
};
|
||||
|
||||
// 加载部门树数据
|
||||
const loadDeptTree = async () => {
|
||||
try {
|
||||
const response = await listDept();
|
||||
const response = await deptTreeSelect();
|
||||
console.log('部门选项响应:', response);
|
||||
|
||||
let deptData;
|
||||
if (response.data) {
|
||||
deptData = response.data;
|
||||
} else {
|
||||
deptData = response;
|
||||
}
|
||||
const deptData = response.data;
|
||||
|
||||
// 验证部门数据结构
|
||||
if (Array.isArray(deptData)) {
|
||||
// 检查数据格式并打印前几个节点用于调试
|
||||
// deptTreeSelect 返回的已经是正确的 DeptTreeVO[] 格式
|
||||
// 包含 id、label、children 字段,直接使用即可
|
||||
console.log('部门数据示例:', deptData.slice(0, 3));
|
||||
|
||||
// 转换为树形结构,适配 el-tree-select 组件
|
||||
const treeData = proxy?.handleTree(deptData, 'deptId', 'parentId', 'children', '0') || [];
|
||||
|
||||
// 转换数据格式以适配 el-tree-select
|
||||
const formatTreeData = (nodes: any[]): any[] => {
|
||||
return nodes.map(node => ({
|
||||
id: node.deptId,
|
||||
label: node.deptName,
|
||||
value: node.deptId,
|
||||
children: node.children ? formatTreeData(node.children) : []
|
||||
}));
|
||||
};
|
||||
|
||||
const formattedData = formatTreeData(treeData);
|
||||
|
||||
deptOptions.value = formattedData;
|
||||
deptTreeData.value = formattedData;
|
||||
deptOptions.value = deptData;
|
||||
deptTreeData.value = deptData;
|
||||
} else {
|
||||
console.warn('部门数据格式错误,期望数组但收到:', typeof deptData);
|
||||
deptOptions.value = [];
|
||||
@@ -720,61 +722,70 @@ const handleDeptClick = async (data: any) => {
|
||||
console.log('部门树节点点击:', data);
|
||||
console.log('节点ID类型:', typeof data?.id, '值:', data?.id);
|
||||
|
||||
// 验证deptId是否为有效的数字
|
||||
if (data?.id && !isNaN(Number(data.id)) && data.id !== 'treeselect') {
|
||||
try {
|
||||
userLoading.value = true;
|
||||
currentDeptName.value = data.label || data.deptName;
|
||||
// 验证deptId是否为有效的数字,排除特殊值
|
||||
const nodeId = data?.id;
|
||||
if (!nodeId || nodeId === 'treeselect' || nodeId === '') {
|
||||
console.warn('无效的部门节点ID:', nodeId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保传递数字类型的ID
|
||||
const deptId = Number(data.id);
|
||||
console.log('调用API,部门ID:', deptId);
|
||||
try {
|
||||
userLoading.value = true;
|
||||
currentDeptName.value = data.label || data.deptName;
|
||||
|
||||
const response = await listUserByDeptId(deptId);
|
||||
console.log('部门用户数据响应:', response);
|
||||
// 保持字符串类型的ID,避免大整数精度丢失
|
||||
const deptId = String(nodeId);
|
||||
console.log('调用API,部门ID:', deptId, '类型:', typeof deptId);
|
||||
|
||||
let userData;
|
||||
if (response.data) {
|
||||
userData = response.data;
|
||||
} else {
|
||||
userData = response;
|
||||
}
|
||||
const response = await listUserByDeptId(deptId);
|
||||
console.log('部门用户数据响应:', response);
|
||||
|
||||
deptUserList.value = Array.isArray(userData) ? userData : [];
|
||||
console.log('处理后的用户数据:', deptUserList.value);
|
||||
|
||||
// 切换部门后,恢复之前选中的用户状态
|
||||
nextTick(() => {
|
||||
if (userTableRef.value) {
|
||||
// 清空选择
|
||||
userTableRef.value.clearSelection();
|
||||
// 恢复已选择的用户
|
||||
deptUserList.value.forEach(user => {
|
||||
const isSelected = batchAddDialog.selectedUsers.some(selected => selected.userId === user.userId);
|
||||
if (isSelected) {
|
||||
userTableRef.value?.toggleRowSelection(user, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取部门用户失败:', error);
|
||||
ElMessage.error('获取部门用户失败,请重试');
|
||||
deptUserList.value = [];
|
||||
} finally {
|
||||
userLoading.value = false;
|
||||
// 后端返回格式: { code: 200, data: [...], msg: "操作成功" }
|
||||
let userData = [];
|
||||
if (response && response.data) {
|
||||
userData = response.data;
|
||||
} else if (Array.isArray(response)) {
|
||||
userData = response;
|
||||
}
|
||||
} else {
|
||||
console.warn('无效的部门节点:', data);
|
||||
ElMessage.warning('请选择有效的部门节点');
|
||||
|
||||
// 处理用户数据,添加缺失的状态字段和部门信息
|
||||
deptUserList.value = Array.isArray(userData) ? userData.map((user: any) => ({
|
||||
...user,
|
||||
status: user.status || '0', // 默认为正常状态
|
||||
deptId: user.deptId || nodeId, // 确保有部门ID
|
||||
deptName: user.deptName || currentDeptName.value // 确保有部门名称
|
||||
})) : [];
|
||||
console.log('处理后的用户数据:', deptUserList.value);
|
||||
console.log('用户数据数量:', deptUserList.value.length);
|
||||
|
||||
// 切换部门后,恢复之前选中的用户状态
|
||||
nextTick(() => {
|
||||
if (userTableRef.value) {
|
||||
// 清空选择
|
||||
userTableRef.value.clearSelection();
|
||||
// 恢复已选择的用户
|
||||
deptUserList.value.forEach((user) => {
|
||||
const isSelected = batchAddDialog.selectedUsers.some((selected) => selected.userId === user.userId);
|
||||
if (isSelected) {
|
||||
userTableRef.value?.toggleRowSelection(user, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取部门用户失败:', error);
|
||||
ElMessage.error('获取部门用户失败,请重试');
|
||||
deptUserList.value = [];
|
||||
} finally {
|
||||
userLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 用户选择变化
|
||||
const handleUserSelectionChange = (selection: DeptUserVO[]) => {
|
||||
// 移除当前部门中未选中的用户
|
||||
batchAddDialog.selectedUsers = batchAddDialog.selectedUsers.filter(user =>
|
||||
!deptUserList.value.some(deptUser => deptUser.userId === user.userId)
|
||||
batchAddDialog.selectedUsers = batchAddDialog.selectedUsers.filter(
|
||||
(user) => !deptUserList.value.some((deptUser) => deptUser.userId === user.userId)
|
||||
);
|
||||
|
||||
// 添加新选中的用户
|
||||
@@ -782,7 +793,7 @@ const handleUserSelectionChange = (selection: DeptUserVO[]) => {
|
||||
|
||||
// 去重
|
||||
const userMap = new Map();
|
||||
batchAddDialog.selectedUsers.forEach(user => {
|
||||
batchAddDialog.selectedUsers.forEach((user) => {
|
||||
userMap.set(user.userId, user);
|
||||
});
|
||||
batchAddDialog.selectedUsers = Array.from(userMap.values());
|
||||
@@ -790,14 +801,14 @@ const handleUserSelectionChange = (selection: DeptUserVO[]) => {
|
||||
|
||||
// 移除已选择的用户
|
||||
const removeSelectedUser = (user: DeptUserVO) => {
|
||||
const index = batchAddDialog.selectedUsers.findIndex(item => item.userId === user.userId);
|
||||
const index = batchAddDialog.selectedUsers.findIndex((item) => item.userId === user.userId);
|
||||
if (index > -1) {
|
||||
batchAddDialog.selectedUsers.splice(index, 1);
|
||||
}
|
||||
|
||||
// 如果该用户在当前显示的部门用户列表中,取消选中状态
|
||||
if (userTableRef.value && deptUserList.value.some(deptUser => deptUser.userId === user.userId)) {
|
||||
const tableUser = deptUserList.value.find(deptUser => deptUser.userId === user.userId);
|
||||
if (userTableRef.value && deptUserList.value.some((deptUser) => deptUser.userId === user.userId)) {
|
||||
const tableUser = deptUserList.value.find((deptUser) => deptUser.userId === user.userId);
|
||||
if (tableUser) {
|
||||
userTableRef.value.toggleRowSelection(tableUser, false);
|
||||
}
|
||||
@@ -812,6 +823,22 @@ const clearSelectedUsers = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 用户搜索处理
|
||||
const handleUserSearch = () => {
|
||||
// 搜索时重新应用选择状态
|
||||
nextTick(() => {
|
||||
if (userTableRef.value) {
|
||||
userTableRef.value.clearSelection();
|
||||
filteredUserList.value.forEach((user) => {
|
||||
const isSelected = batchAddDialog.selectedUsers.some((selected) => selected.userId === user.userId);
|
||||
if (isSelected) {
|
||||
userTableRef.value?.toggleRowSelection(user, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 确认批量添加
|
||||
const confirmBatchAdd = async () => {
|
||||
if (batchAddDialog.selectedUsers.length === 0) {
|
||||
@@ -821,19 +848,38 @@ const confirmBatchAdd = async () => {
|
||||
|
||||
batchAddLoading.value = true;
|
||||
try {
|
||||
const userIds = batchAddDialog.selectedUsers.map(user => user.userId);
|
||||
// 验证用户数据完整性
|
||||
const invalidUsers = batchAddDialog.selectedUsers.filter(user =>
|
||||
!user.userId || !user.userName || !user.deptId || !user.deptName
|
||||
);
|
||||
|
||||
if (invalidUsers.length > 0) {
|
||||
ElMessage.warning(`发现 ${invalidUsers.length} 个用户信息不完整,请重新选择`);
|
||||
console.warn('用户信息不完整:', invalidUsers);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用标准批量添加API(后端会自动获取用户完整信息)
|
||||
const userIds = batchAddDialog.selectedUsers.map((user) => user.userId);
|
||||
const request: BatchAddRequest = {
|
||||
userIds,
|
||||
remark: '批量添加的审核人'
|
||||
};
|
||||
|
||||
console.log('批量添加请求数据:', request);
|
||||
console.log('选中的用户详情:', batchAddDialog.selectedUsers);
|
||||
|
||||
await batchAddProjectAuditor(request);
|
||||
|
||||
ElMessage.success(`成功添加 ${batchAddDialog.selectedUsers.length} 个审核人`);
|
||||
batchAddDialog.visible = false;
|
||||
batchAddDialog.selectedUsers = [];
|
||||
userSearchText.value = '';
|
||||
getList();
|
||||
|
||||
} catch (error) {
|
||||
console.error('批量添加审核人失败:', error);
|
||||
ElMessage.error('批量添加审核人失败,请检查用户信息是否完整');
|
||||
} finally {
|
||||
batchAddLoading.value = false;
|
||||
}
|
||||
@@ -882,7 +928,6 @@ onMounted(() => {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
|
||||
/* 表格样式 */
|
||||
.scroll-table {
|
||||
:deep(.el-table__header-wrapper) {
|
||||
@@ -901,6 +946,8 @@ onMounted(() => {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
height: 500px;
|
||||
max-width: 1160px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dept-select-section {
|
||||
@@ -909,7 +956,8 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.user-select-section {
|
||||
flex: 1;
|
||||
width: 840px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
@@ -923,12 +971,33 @@ onMounted(() => {
|
||||
|
||||
.user-table-card {
|
||||
flex: 1;
|
||||
|
||||
.el-table {
|
||||
width: 100% !important;
|
||||
table-layout: fixed !important;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.user-count {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-users-list {
|
||||
|
||||
@@ -17,13 +17,7 @@
|
||||
|
||||
<div class="test-section">
|
||||
<h3>3. 部门选择测试</h3>
|
||||
<el-tree-select
|
||||
v-model="selectedDept"
|
||||
:data="deptOptions"
|
||||
placeholder="选择部门"
|
||||
clearable
|
||||
style="width: 300px"
|
||||
/>
|
||||
<el-tree-select v-model="selectedDept" :data="deptOptions" placeholder="选择部门" clearable style="width: 300px" />
|
||||
<p>选中部门: {{ selectedDept }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,14 +36,22 @@ const apiResult = ref('');
|
||||
|
||||
// 模拟部门数据
|
||||
const mockDeptOptions = [
|
||||
{ value: '1', label: '总公司', children: [
|
||||
{ value: '2', label: '财务部' },
|
||||
{ value: '3', label: '人事部' }
|
||||
]},
|
||||
{ value: '4', label: '分公司', children: [
|
||||
{ value: '5', label: '技术部' },
|
||||
{ value: '6', label: '市场部' }
|
||||
]}
|
||||
{
|
||||
value: '1',
|
||||
label: '总公司',
|
||||
children: [
|
||||
{ value: '2', label: '财务部' },
|
||||
{ value: '3', label: '人事部' }
|
||||
]
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: '分公司',
|
||||
children: [
|
||||
{ value: '5', label: '技术部' },
|
||||
{ value: '6', label: '市场部' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 模拟API响应数据
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
<!-- 操作按钮行 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -167,10 +167,14 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:povertyApplication:add']">新增申请</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['jcbt:povertyApplication:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['jcbt:povertyApplication:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['jcbt:povertyApplication:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['jcbt:povertyApplication:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:povertyApplication:export']">导出</el-button>
|
||||
|
||||
@@ -164,7 +164,14 @@
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleBatchReject" v-hasPermi="['jcbt:povertyApplication:reject']">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Close"
|
||||
:disabled="multiple"
|
||||
@click="handleBatchReject"
|
||||
v-hasPermi="['jcbt:povertyApplication:reject']"
|
||||
>
|
||||
批量驳回
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
@@ -44,12 +44,7 @@
|
||||
<el-col :span="6">
|
||||
<el-form-item label="奖学金类型" prop="scholarshipTypeId">
|
||||
<el-select v-model="queryParams.scholarshipTypeId" placeholder="请选择奖学金类型" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="type in scholarshipTypeList"
|
||||
:key="type.id"
|
||||
:label="type.typeName"
|
||||
:value="type.id"
|
||||
/>
|
||||
<el-option v-for="type in scholarshipTypeList" :key="type.id" :label="type.typeName" :value="type.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -68,9 +63,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8" >
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -89,14 +84,9 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipApplication:add']">班主任批量申请</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['jcbt:scholarshipApplication:edit']"
|
||||
>修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipApplication:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
@@ -106,7 +96,8 @@
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['jcbt:scholarshipApplication:remove']"
|
||||
>删除</el-button>
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -153,33 +153,17 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipBatch:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['jcbt:scholarshipBatch:edit']"
|
||||
>修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipBatch:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['jcbt:scholarshipBatch:remove']"
|
||||
>删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['jcbt:scholarshipBatch:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['jcbt:scholarshipBatch:export']"
|
||||
>导出</el-button>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:scholarshipBatch:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -254,22 +238,10 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="View"
|
||||
@click="handleView(scope.row)"
|
||||
v-hasPermi="['jcbt:scholarshipBatch:query']"
|
||||
></el-button>
|
||||
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['jcbt:scholarshipBatch:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['jcbt:scholarshipBatch:edit']"
|
||||
></el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['jcbt:scholarshipBatch:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="状态变更" placement="top">
|
||||
<el-button
|
||||
@@ -772,7 +744,7 @@ async function handleUpdate(row?: ScholarshipBatchVO) {
|
||||
// 关键修复:不调用reset(),直接赋值数据,确保ID不会被重置为undefined
|
||||
form.value = {
|
||||
...res.data,
|
||||
id: String(_id) // 🔧 修复:确保ID为字符串格式,避免精度丢失
|
||||
id: String(_id) // 🔧 修复:确保ID为字符串格式,避免精度丢失
|
||||
};
|
||||
|
||||
// 确保scholarshipTypeIds正确赋值
|
||||
@@ -828,7 +800,7 @@ function submitForm() {
|
||||
// 不再使用parseInt(),直接使用字符串格式的ID
|
||||
const updateData = {
|
||||
...form.value,
|
||||
id: String(form.value.id) // 确保ID始终为字符串格式
|
||||
id: String(form.value.id) // 确保ID始终为字符串格式
|
||||
};
|
||||
console.log('发送到后端的修改数据:', JSON.stringify(updateData, null, 2));
|
||||
await updateScholarshipBatch(updateData);
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -85,45 +85,23 @@
|
||||
<el-row ref="toolbarRef" :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipQuota:add']">新增</el-button>
|
||||
</el-col>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipQuota:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['jcbt:scholarshipQuota:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:scholarshipQuota:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="Operation"
|
||||
@click="handleBatchAllocate"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:add']"
|
||||
>批量分配</el-button>
|
||||
</el-col>
|
||||
<el-button type="info" plain icon="Operation" @click="handleBatchAllocate" v-hasPermi="['jcbt:scholarshipQuota:add']">批量分配</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -178,18 +156,45 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:edit']" :disabled="scope.row.isLocked === 1"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:remove']" :disabled="scope.row.isLocked === 1"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="锁定/解锁" placement="top">
|
||||
<el-button link :type="scope.row.isLocked === 1 ? 'warning' : 'info'" :icon="scope.row.isLocked === 1 ? 'Unlock' : 'Lock'" @click="handleLock(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="调整名额" placement="top">
|
||||
<el-button link type="success" icon="Switch" @click="handleAdjust(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:edit']" :disabled="scope.row.isLocked === 1"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:edit']"
|
||||
:disabled="scope.row.isLocked === 1"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:remove']"
|
||||
:disabled="scope.row.isLocked === 1"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="锁定/解锁" placement="top">
|
||||
<el-button
|
||||
link
|
||||
:type="scope.row.isLocked === 1 ? 'warning' : 'info'"
|
||||
:icon="scope.row.isLocked === 1 ? 'Unlock' : 'Lock'"
|
||||
@click="handleLock(scope.row)"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="调整名额" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Switch"
|
||||
@click="handleAdjust(scope.row)"
|
||||
v-hasPermi="['jcbt:scholarshipQuota:edit']"
|
||||
:disabled="scope.row.isLocked === 1"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -977,13 +982,13 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
}
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
/* 表格容器 - 支持横向滚动 */
|
||||
.table-container {
|
||||
@@ -1013,29 +1018,29 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* Webkit 浏览器的横向滚动条样式 */
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
|
||||
height: 12px !important; /* 增加滚动条高度 */
|
||||
width: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
|
||||
background: #f0f0f0 !important;
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
|
||||
background: #409eff !important; /* 使用主题色,更明显 */
|
||||
border-radius: 6px !important;
|
||||
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
|
||||
min-height: 20px !important; /* 确保最小高度 */
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
|
||||
background: #337ecc !important; /* 悬停时更深的蓝色 */
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
|
||||
background: #2b6cb0 !important; /* 点击时的颜色 */
|
||||
}
|
||||
|
||||
@@ -1065,13 +1070,13 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* 分页容器样式 */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/* 移除按钮栏底部边框 */
|
||||
.button-card :deep(.el-card__header) {
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
<!-- 操作按钮行 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
|
||||
@@ -69,9 +69,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8" >
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -88,36 +88,20 @@
|
||||
<el-row ref="toolbarRef" :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipType:add']">新增</el-button>
|
||||
</el-col>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['jcbt:scholarshipType:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipType:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['jcbt:scholarshipType:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['jcbt:scholarshipType:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['jcbt:scholarshipType:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:scholarshipType:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -180,15 +164,15 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['jcbt:scholarshipType:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['jcbt:scholarshipType:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['jcbt:scholarshipType:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -646,19 +630,21 @@ const getExclusiveRelationText = (scholarshipTypeId: string | number): string =>
|
||||
}
|
||||
|
||||
// 异步获取并缓存
|
||||
getExclusiveRelationDisplay(scholarshipTypeId).then(response => {
|
||||
exclusiveRelationCache.value.set(scholarshipTypeId, response.data);
|
||||
}).catch(error => {
|
||||
console.warn('获取互斥关系显示文本失败:', error);
|
||||
exclusiveRelationCache.value.set(scholarshipTypeId, '互斥(无)');
|
||||
});
|
||||
getExclusiveRelationDisplay(scholarshipTypeId)
|
||||
.then((response) => {
|
||||
exclusiveRelationCache.value.set(scholarshipTypeId, response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('获取互斥关系显示文本失败:', error);
|
||||
exclusiveRelationCache.value.set(scholarshipTypeId, '互斥(无)');
|
||||
});
|
||||
|
||||
return '加载中...';
|
||||
};
|
||||
|
||||
/** 预加载互斥关系显示文本 */
|
||||
const preloadExclusiveRelations = async () => {
|
||||
const customTypes = scholarshipTypeList.value.filter(item => item.compatibilityType === 'CUSTOM');
|
||||
const customTypes = scholarshipTypeList.value.filter((item) => item.compatibilityType === 'CUSTOM');
|
||||
|
||||
for (const type of customTypes) {
|
||||
if (type.id && !exclusiveRelationCache.value.has(type.id)) {
|
||||
@@ -967,13 +953,13 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
}
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
/* 表格容器 - 支持横向滚动 */
|
||||
.table-container {
|
||||
@@ -1003,29 +989,29 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* Webkit 浏览器的横向滚动条样式 */
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
|
||||
height: 12px !important; /* 增加滚动条高度 */
|
||||
width: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
|
||||
background: #f0f0f0 !important;
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
|
||||
background: #409eff !important; /* 使用主题色,更明显 */
|
||||
border-radius: 6px !important;
|
||||
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
|
||||
min-height: 20px !important; /* 确保最小高度 */
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
|
||||
background: #337ecc !important; /* 悬停时更深的蓝色 */
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
|
||||
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
|
||||
background: #2b6cb0 !important; /* 点击时的颜色 */
|
||||
}
|
||||
|
||||
@@ -1055,13 +1041,13 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* 分页容器样式 */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/* 移除按钮栏底部边框 */
|
||||
.button-card :deep(.el-card__header) {
|
||||
@@ -1074,23 +1060,23 @@ onMounted(() => {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
/* 对话框样式 */
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
/* 对话框样式 */
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dialog-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.dialog-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
line-height: 1.4;
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,9 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExportApplications" v-hasPermi="['rs:application:export']" style="display: none">导出</el-button>
|
||||
<el-button icon="Download" @click="handleExportApplications" v-hasPermi="['rs:application:export']" style="display: none"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Printer" @click="handlePrintApplications" style="display: none">打印</el-button>
|
||||
@@ -424,8 +426,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="application-table mt-4">
|
||||
<el-table v-loading="applicationLoading" :data="applicationList" border size="small" class="compact-table" :row-style="{ height: '32px' }" :cell-style="{ padding: '2px 0' }">
|
||||
<div class="application-table mt-4">
|
||||
<el-table
|
||||
v-loading="applicationLoading"
|
||||
:data="applicationList"
|
||||
border
|
||||
size="small"
|
||||
class="compact-table"
|
||||
:row-style="{ height: '32px' }"
|
||||
:cell-style="{ padding: '2px 0' }"
|
||||
>
|
||||
<el-table-column label="应聘者" align="center" prop="applicantName" width="110" />
|
||||
<el-table-column label="申请状态" align="center" prop="status" width="110">
|
||||
<template #default="scope">
|
||||
@@ -1996,7 +2006,7 @@ onMounted(() => {
|
||||
/* 紧凑表格样式 */
|
||||
.compact-table {
|
||||
font-size: 13px;
|
||||
|
||||
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
padding: 4px 0;
|
||||
@@ -2004,14 +2014,14 @@ onMounted(() => {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-table__body) {
|
||||
td {
|
||||
padding: 4px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:deep(.cell) {
|
||||
padding: 0 4px;
|
||||
line-height: 1.3;
|
||||
|
||||
@@ -69,9 +69,9 @@
|
||||
</template>
|
||||
|
||||
<!-- 待审批申请表格 -->
|
||||
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleViewApplication" v-hasPermi="['rs:overseas:list']">
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleViewApplication" v-hasPermi="['rs:overseas:list']">
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="出国目的" align="center" prop="purposeText" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="目的地" align="center" prop="destination" width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="出国时间" align="center" min-width="220" :show-overflow-tooltip="true">
|
||||
@@ -198,11 +198,11 @@
|
||||
</template>
|
||||
|
||||
<!-- 审批记录表格 -->
|
||||
<el-table v-loading="recordLoading" border :data="recordList" @row-dblclick="handleViewRecord" v-hasPermi="['rs:overseas:list']">
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table v-loading="recordLoading" border :data="recordList" @row-dblclick="handleViewRecord" v-hasPermi="['rs:overseas:list']">
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="出国目的" align="center" prop="purposeText" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="目的地" align="center" prop="destination" width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="出国时间" align="center" min-width="220" :show-overflow-tooltip="true">
|
||||
<template #default="scope"> {{ formatDate(scope.row.startDate) }} 至 {{ formatDate(scope.row.endDate) }} </template>
|
||||
</el-table-column>
|
||||
@@ -634,7 +634,6 @@ const resetRecordQuery = () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 查看申请详情
|
||||
const handleViewApplication = async (row: OverseasApplicationVO) => {
|
||||
try {
|
||||
@@ -821,8 +820,6 @@ const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'dang
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -55,7 +54,9 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAddApplication" v-hasPermi="['rs:overseas:add']">新增申请</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExportApplication" v-hasPermi="['rs:overseas:export']" style="display: none">导出</el-button>
|
||||
<el-button icon="Download" @click="handleExportApplication" v-hasPermi="['rs:overseas:export']" style="display: none"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showApplicationSearch" @queryTable="getApplicationList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -316,12 +317,13 @@
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="state.submitLoading"
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="state.submitLoading"
|
||||
@click="submitApplicationForm"
|
||||
v-hasPermi="state.applicationForm.applicationId ? ['rs:overseas:edit'] : ['rs:overseas:add', 'rs:overseas:submit']"
|
||||
>确 定</el-button>
|
||||
>确 定</el-button
|
||||
>
|
||||
<el-button @click="cancelApplicationForm">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -543,8 +545,6 @@ const state = reactive({
|
||||
status: '0'
|
||||
},
|
||||
|
||||
|
||||
|
||||
// 字典数据
|
||||
statusOptions: [
|
||||
{ label: '待审核', value: '0' },
|
||||
@@ -723,8 +723,6 @@ const resetRecordQuery = () => {
|
||||
getRecordList();
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 新增申请
|
||||
const handleAddApplication = () => {
|
||||
resetApplicationForm();
|
||||
|
||||
@@ -64,7 +64,9 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['rs:overseas:export']" style="display: none">导出</el-button>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['rs:overseas:export']" style="display: none"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -534,7 +536,7 @@ function resetQuery() {
|
||||
} catch (error) {
|
||||
console.error('重置查询条件时发生错误:', error);
|
||||
// 即使出错也要重新查询
|
||||
handleQuery();
|
||||
handleQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +567,7 @@ function resetRecordQuery() {
|
||||
} catch (error) {
|
||||
console.error('重置查询条件时发生错误:', error);
|
||||
// 即使出错也要重新查询
|
||||
handleRecordQuery();
|
||||
handleRecordQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,14 +665,14 @@ function submitForm() {
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const _approvalIds = row.approvalId || ids.value;
|
||||
|
||||
|
||||
let confirmMessage = '';
|
||||
if (row) {
|
||||
confirmMessage = `是否确认删除申请人"${row.applicantName}"前往"${row.destination}"的审批记录?`;
|
||||
} else {
|
||||
confirmMessage = `是否确认删除选中的${ids.value.length}条审批记录?`;
|
||||
}
|
||||
|
||||
|
||||
proxy.$modal
|
||||
.confirm(confirmMessage)
|
||||
.then(function () {
|
||||
|
||||
@@ -420,13 +420,13 @@ onMounted(() => {
|
||||
.el-breadcrumb {
|
||||
margin: 0 auto;
|
||||
padding: 0 30px;
|
||||
|
||||
|
||||
:deep(.el-breadcrumb__item) {
|
||||
.el-breadcrumb__inner {
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
&:last-child .el-breadcrumb__inner {
|
||||
color: #2d5a3d;
|
||||
font-weight: 500;
|
||||
@@ -488,10 +488,10 @@ onMounted(() => {
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
border: none;
|
||||
|
||||
|
||||
// 默认状态 - 普通导航项
|
||||
background: #004e48;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: #006157;
|
||||
}
|
||||
@@ -501,7 +501,7 @@ onMounted(() => {
|
||||
background: #003a37;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
|
||||
// 创建右箭头效果
|
||||
&::after {
|
||||
content: '';
|
||||
@@ -516,7 +516,7 @@ onMounted(() => {
|
||||
border-bottom: 15px solid transparent;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
|
||||
// 箭头阴影效果
|
||||
&::before {
|
||||
content: '';
|
||||
@@ -539,7 +539,7 @@ onMounted(() => {
|
||||
color: #ffffff;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
span {
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.5px;
|
||||
@@ -558,18 +558,18 @@ onMounted(() => {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e8e8e8;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
|
||||
:deep(.el-card__header) {
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -596,19 +596,19 @@ onMounted(() => {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.view-options {
|
||||
:deep(.el-radio-group) {
|
||||
.el-radio-button__inner {
|
||||
background: #fff;
|
||||
border-color: #2d5a3d;
|
||||
color: #2d5a3d;
|
||||
|
||||
|
||||
&:hover {
|
||||
color: #2d5a3d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.el-radio-button__original-radio:checked + .el-radio-button__inner {
|
||||
background: #2d5a3d;
|
||||
border-color: #2d5a3d;
|
||||
@@ -622,21 +622,21 @@ onMounted(() => {
|
||||
:deep(.el-table) {
|
||||
.el-table__header {
|
||||
background: #fafafa;
|
||||
|
||||
|
||||
th {
|
||||
background: #fafafa !important;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.el-table__row {
|
||||
&:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.job-title-link {
|
||||
font-weight: 500;
|
||||
color: #2d5a3d;
|
||||
@@ -672,12 +672,12 @@ onMounted(() => {
|
||||
.el-button {
|
||||
background: #2d5a3d;
|
||||
border-color: #2d5a3d;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: #4a7c59;
|
||||
border-color: #4a7c59;
|
||||
}
|
||||
|
||||
|
||||
&:disabled {
|
||||
background: #ccc;
|
||||
border-color: #ccc;
|
||||
@@ -692,11 +692,11 @@ onMounted(() => {
|
||||
// 对话框样式优化
|
||||
:deep(.el-dialog) {
|
||||
border-radius: 8px;
|
||||
|
||||
|
||||
.el-dialog__header {
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
|
||||
.el-dialog__title {
|
||||
color: #2d5a3d;
|
||||
font-weight: 600;
|
||||
@@ -723,7 +723,7 @@ onMounted(() => {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.el-tag {
|
||||
&.el-tag--success {
|
||||
background: #e8f5e8;
|
||||
@@ -771,11 +771,11 @@ onMounted(() => {
|
||||
|
||||
.el-button {
|
||||
margin-left: 10px;
|
||||
|
||||
|
||||
&.el-button--primary {
|
||||
background: #2d5a3d;
|
||||
border-color: #2d5a3d;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: #4a7c59;
|
||||
border-color: #4a7c59;
|
||||
|
||||
+269
-262
File diff suppressed because it is too large.
Load diff
@@ -57,7 +57,13 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExportTrainingApplication" v-hasPermi="['rs:trainingapproval:export']" style="display: none">导出</el-button>
|
||||
<el-button
|
||||
icon="Download"
|
||||
@click="handleExportTrainingApplication"
|
||||
v-hasPermi="['rs:trainingapproval:export']"
|
||||
style="display: none"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showApplicationSearch" @queryTable="getApplicationList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -120,7 +126,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 审批记录选项卡 -->
|
||||
<el-tab-pane label="审批记录" name="approvalRecord" v-hasPermi="['rs:trainingapproval:query']">
|
||||
<el-tab-pane label="审批记录" name="approvalRecord" v-hasPermi="['rs:trainingapproval:query']">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
|
||||
<div v-show="showApprovalRecordSearch" class="mb-[10px]">
|
||||
@@ -167,7 +173,9 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExportApprovalRecord" v-hasPermi="['rs:trainingapproval:export']" style="display: none">导出</el-button>
|
||||
<el-button icon="Download" @click="handleExportApprovalRecord" v-hasPermi="['rs:trainingapproval:export']" style="display: none"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showApprovalRecordSearch" @queryTable="getApprovalRecordList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -704,12 +712,12 @@ const getApplicationList = async () => {
|
||||
// 排除已通过状态的数据,只显示待审核、已拒绝、已取消等状态
|
||||
excludeStatus: '1' // 1表示已通过状态
|
||||
};
|
||||
|
||||
|
||||
const response = await listTrainingApplication(queryParams);
|
||||
|
||||
|
||||
// 前端二次过滤,确保不显示已通过的数据
|
||||
const filteredApplications = response.rows.filter(app => app.status !== '1');
|
||||
|
||||
const filteredApplications = response.rows.filter((app) => app.status !== '1');
|
||||
|
||||
applicationList.value = filteredApplications;
|
||||
applicationTotal.value = filteredApplications.length;
|
||||
} catch (error) {
|
||||
|
||||
@@ -38,9 +38,15 @@
|
||||
<!-- 主办方信息区域 -->
|
||||
<el-divider content-position="left">主办方信息</el-divider>
|
||||
<el-descriptions :column="3" border class="organizer-info-section custom-width-organizer">
|
||||
<el-descriptions-item label="主办方名称" :label-style="{ width: '13%' }" :content-style="{ width: '35%' }">{{ activity.organizerName || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '15%' }">{{ activity.organizerContact || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '17%' }">{{ activity.organizerPhone || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="主办方名称" :label-style="{ width: '13%' }" :content-style="{ width: '35%' }">{{
|
||||
activity.organizerName || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '15%' }">{{
|
||||
activity.organizerContact || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '17%' }">{{
|
||||
activity.organizerPhone || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 承办方信息区域 -->
|
||||
@@ -48,9 +54,15 @@
|
||||
<div v-if="coOrganizerList.length > 0">
|
||||
<div v-for="(organizer, index) in coOrganizerList" :key="index" class="co-organizer-item custom-width-co-organizer">
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="承办方名称" :label-style="{ width: '12%' }" :content-style="{ width: '36%' }">{{ organizer.name || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '16%' }">{{ organizer.contact || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '16%' }">{{ organizer.phone || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="承办方名称" :label-style="{ width: '12%' }" :content-style="{ width: '36%' }">{{
|
||||
organizer.name || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '16%' }">{{
|
||||
organizer.contact || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '16%' }">{{
|
||||
organizer.phone || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,10 +73,18 @@
|
||||
<div v-if="instructorList.length > 0">
|
||||
<div v-for="(instructor, index) in instructorList" :key="index" class="instructor-item custom-width-instructor">
|
||||
<el-descriptions :column="4" border size="small">
|
||||
<el-descriptions-item label="姓名" :label-style="{ width: '8%' }" :content-style="{ width: '15%' }">{{ instructor.name || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="部门" :label-style="{ width: '8%' }" :content-style="{ width: '17%' }">{{ instructor.dept || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电话" :label-style="{ width: '8%' }" :content-style="{ width: '15%' }">{{ instructor.phone || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱" :label-style="{ width: '8%' }" :content-style="{ width: '17%' }">{{ instructor.email || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名" :label-style="{ width: '8%' }" :content-style="{ width: '15%' }">{{
|
||||
instructor.name || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="部门" :label-style="{ width: '8%' }" :content-style="{ width: '17%' }">{{
|
||||
instructor.dept || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="电话" :label-style="{ width: '8%' }" :content-style="{ width: '15%' }">{{
|
||||
instructor.phone || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱" :label-style="{ width: '8%' }" :content-style="{ width: '17%' }">{{
|
||||
instructor.email || '暂无'
|
||||
}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,11 +223,16 @@ const props = defineProps<Props>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, stgl_activity_range, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'stgl_activity_range', 'sys_yes_no')
|
||||
proxy?.useDict(
|
||||
'stgl_activity_type',
|
||||
'stgl_activity_status',
|
||||
'stgl_registration_type',
|
||||
'stgl_registration_status',
|
||||
'stgl_activity_range',
|
||||
'sys_yes_no'
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 当前激活的Tab页签
|
||||
const activeTab = ref('basic');
|
||||
|
||||
@@ -585,25 +610,25 @@ onMounted(() => {
|
||||
.el-descriptions__header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
.el-descriptions__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
|
||||
.el-descriptions__body {
|
||||
.el-descriptions__table {
|
||||
.el-descriptions__cell {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
|
||||
.el-descriptions__label {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
|
||||
.el-descriptions__content {
|
||||
color: #303133;
|
||||
}
|
||||
@@ -624,7 +649,7 @@ onMounted(() => {
|
||||
.activity-detail-view {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-descriptions) {
|
||||
.el-descriptions__body {
|
||||
.el-descriptions__table {
|
||||
@@ -634,7 +659,7 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.co-organizer-item,
|
||||
.instructor-item {
|
||||
padding: 12px;
|
||||
|
||||
@@ -3,24 +3,15 @@
|
||||
<el-tabs v-model="activeTab" type="border-card" @tab-change="handleTabChange">
|
||||
<!-- 已报名标签页 -->
|
||||
<el-tab-pane label="已报名" name="registered">
|
||||
<RegisteredMembersTab
|
||||
:activity-id="activityId"
|
||||
:activity-name="activityName"
|
||||
ref="registeredTabRef"
|
||||
/>
|
||||
<RegisteredMembersTab :activity-id="activityId" :activity-name="activityName" ref="registeredTabRef" />
|
||||
</el-tab-pane>
|
||||
|
||||
|
||||
<!-- 待审核标签页 -->
|
||||
<el-tab-pane label="待审核" name="pending">
|
||||
<PendingMembersTab
|
||||
:activity-id="activityId"
|
||||
:activity-name="activityName"
|
||||
ref="pendingTabRef"
|
||||
@refresh-other-tabs="handleRefreshOtherTabs"
|
||||
/>
|
||||
<PendingMembersTab :activity-id="activityId" :activity-name="activityName" ref="pendingTabRef" @refresh-other-tabs="handleRefreshOtherTabs" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">关 闭</el-button>
|
||||
@@ -57,18 +48,21 @@ const handleVisibleChange = (value: boolean) => {
|
||||
};
|
||||
|
||||
// 监听弹窗显示状态,当弹窗打开时刷新数据
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
// 弹窗打开时刷新当前标签页数据
|
||||
nextTick(() => {
|
||||
if (activeTab.value === 'registered' && registeredTabRef.value) {
|
||||
registeredTabRef.value.getList();
|
||||
} else if (activeTab.value === 'pending' && pendingTabRef.value) {
|
||||
pendingTabRef.value.getList();
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
// 弹窗打开时刷新当前标签页数据
|
||||
nextTick(() => {
|
||||
if (activeTab.value === 'registered' && registeredTabRef.value) {
|
||||
registeredTabRef.value.getList();
|
||||
} else if (activeTab.value === 'pending' && pendingTabRef.value) {
|
||||
pendingTabRef.value.getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// 标签页切换时刷新数据
|
||||
const handleTabChange = (tabName: string) => {
|
||||
@@ -127,4 +121,4 @@ defineExpose({
|
||||
:deep(.el-tabs__content) {
|
||||
padding: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<div v-else>
|
||||
<!-- 移除统计信息部分 -->
|
||||
|
||||
|
||||
<el-table :data="ratings" height="600" border stripe>
|
||||
<!-- 基本信息 - 减少宽度 -->
|
||||
<el-table-column label="评分人信息" width="160" fixed="left">
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 所属信息 - 减少宽度 -->
|
||||
<el-table-column label="所属信息" width="140">
|
||||
<template #default="scope">
|
||||
@@ -117,19 +117,23 @@ const averageRating = computed(() => {
|
||||
// 计算最高评分
|
||||
const maxRating = computed(() => {
|
||||
if (ratings.value.length === 0) return 0;
|
||||
return Math.max(...ratings.value.map((rating) => {
|
||||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||||
return isNaN(score) ? 0 : score;
|
||||
}));
|
||||
return Math.max(
|
||||
...ratings.value.map((rating) => {
|
||||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||||
return isNaN(score) ? 0 : score;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 计算最低评分
|
||||
const minRating = computed(() => {
|
||||
if (ratings.value.length === 0) return 0;
|
||||
return Math.min(...ratings.value.map((rating) => {
|
||||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||||
return isNaN(score) ? 0 : score;
|
||||
}));
|
||||
return Math.min(
|
||||
...ratings.value.map((rating) => {
|
||||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||||
return isNaN(score) ? 0 : score;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 简化的日期时间格式化
|
||||
@@ -171,12 +175,16 @@ const loadRatings = async () => {
|
||||
};
|
||||
|
||||
// 监听 activityId 变化,自动重新加载评分数据
|
||||
watch(() => props.activityId, (newId, oldId) => {
|
||||
console.log('活动ID发生变化:', oldId, '->', newId);
|
||||
if (newId && newId !== oldId) {
|
||||
loadRatings();
|
||||
}
|
||||
}, { immediate: true }); // immediate: true 确保组件挂载时也会执行一次
|
||||
watch(
|
||||
() => props.activityId,
|
||||
(newId, oldId) => {
|
||||
console.log('活动ID发生变化:', oldId, '->', newId);
|
||||
if (newId && newId !== oldId) {
|
||||
loadRatings();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
); // immediate: true 确保组件挂载时也会执行一次
|
||||
|
||||
// 移除 onMounted,因为 watch 的 immediate: true 已经覆盖了这个功能
|
||||
</script>
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="always" style="padding: 8px 16px;">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0;">
|
||||
<el-card shadow="always" style="padding: 8px 16px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
|
||||
<el-row :gutter="16" align="middle">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0;">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0;">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0;">
|
||||
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.phone" placeholder="请输入联系方式" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -94,13 +94,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="签到时间" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.latestSignInTime"
|
||||
link
|
||||
type="primary"
|
||||
@click="showSignRecords(scope.row)"
|
||||
class="time-button"
|
||||
>
|
||||
<el-button v-if="scope.row.latestSignInTime" link type="primary" @click="showSignRecords(scope.row)" class="time-button">
|
||||
{{ formatSignTime(scope.row.latestSignInTime) }}
|
||||
</el-button>
|
||||
<span v-else class="text-gray-400">未签到</span>
|
||||
@@ -108,13 +102,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="签退时间" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.latestSignOutTime"
|
||||
link
|
||||
type="primary"
|
||||
@click="showSignRecords(scope.row)"
|
||||
class="time-button"
|
||||
>
|
||||
<el-button v-if="scope.row.latestSignOutTime" link type="primary" @click="showSignRecords(scope.row)" class="time-button">
|
||||
{{ formatSignTime(scope.row.latestSignOutTime) }}
|
||||
</el-button>
|
||||
<span v-else class="text-gray-400">未签退</span>
|
||||
@@ -309,7 +297,7 @@
|
||||
是否更新已经存在的报名数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
@@ -493,15 +481,15 @@ const getList = async () => {
|
||||
try {
|
||||
// 构建查询参数,查询已审核通过的报名记录
|
||||
const queryData = { ...queryParams.value };
|
||||
|
||||
|
||||
// 如果状态字段为空或未选择,则查询已通过(approvalStatus=1)的报名记录
|
||||
if (!queryData.approvalStatus) {
|
||||
queryData.approvalStatus = 1;
|
||||
}
|
||||
|
||||
|
||||
console.log('ActivityRegistrationView: getList 开始查询,参数:', queryData);
|
||||
console.log('ActivityRegistrationView: props.activityId:', props.activityId);
|
||||
|
||||
|
||||
const res = await listActivityRegister(queryData);
|
||||
console.log('ActivityRegistrationView: 查询结果完整对象:', res);
|
||||
console.log('ActivityRegistrationView: res 的类型:', typeof res);
|
||||
@@ -510,7 +498,7 @@ const getList = async () => {
|
||||
console.log('ActivityRegistrationView: res.rows 的类型:', typeof res.rows);
|
||||
console.log('ActivityRegistrationView: res.total:', res.total);
|
||||
console.log('ActivityRegistrationView: res 的所有属性:', Object.keys(res || {}));
|
||||
|
||||
|
||||
// 检查 res.rows 是否存在且为数组
|
||||
if (!res || !res.rows) {
|
||||
console.error('ActivityRegistrationView: API返回数据异常 - res.rows 不存在');
|
||||
@@ -520,7 +508,7 @@ const getList = async () => {
|
||||
total.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!Array.isArray(res.rows)) {
|
||||
console.error('ActivityRegistrationView: API返回数据异常 - res.rows 不是数组');
|
||||
console.error('ActivityRegistrationView: res.rows 实际值:', res.rows);
|
||||
@@ -530,9 +518,9 @@ const getList = async () => {
|
||||
total.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
console.log('ActivityRegistrationView: res.rows 数组长度:', res.rows.length);
|
||||
|
||||
|
||||
// 为每个成员添加签到信息
|
||||
const membersWithSignInfo = await Promise.all(
|
||||
res.rows.map(async (member) => {
|
||||
@@ -546,17 +534,17 @@ const getList = async () => {
|
||||
studentNo: member.studentNo
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (signRes.code === 200 && signRes.data && signRes.data.length > 0) {
|
||||
// 获取最新的签到和签退时间
|
||||
const latestSignIn = signRes.data
|
||||
.filter((record: any) => record.signInTime)
|
||||
.sort((a: any, b: any) => new Date(b.signInTime).getTime() - new Date(a.signInTime).getTime())[0];
|
||||
|
||||
|
||||
const latestSignOut = signRes.data
|
||||
.filter((record: any) => record.signOutTime)
|
||||
.sort((a: any, b: any) => new Date(b.signOutTime).getTime() - new Date(a.signOutTime).getTime())[0];
|
||||
|
||||
|
||||
return {
|
||||
...member,
|
||||
latestSignInTime: latestSignIn?.signInTime,
|
||||
@@ -566,7 +554,7 @@ const getList = async () => {
|
||||
} catch (error) {
|
||||
console.warn('获取签到信息失败:', error);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
...member,
|
||||
latestSignInTime: undefined,
|
||||
@@ -574,7 +562,7 @@ const getList = async () => {
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
memberList.value = membersWithSignInfo;
|
||||
total.value = res.total;
|
||||
} catch (error: any) {
|
||||
@@ -586,7 +574,7 @@ const getList = async () => {
|
||||
request: error?.request,
|
||||
config: error?.config
|
||||
});
|
||||
|
||||
|
||||
// 检查是否是网络错误
|
||||
if (error?.code === 'NETWORK_ERROR' || error?.code === 'ECONNABORTED') {
|
||||
proxy?.$modal.msgError('网络连接失败,请检查网络状态');
|
||||
@@ -603,7 +591,7 @@ const getList = async () => {
|
||||
// 其他错误(如代码逻辑错误)
|
||||
proxy?.$modal.msgError(`查询成员列表失败: ${error?.message || '未知错误'}`);
|
||||
}
|
||||
|
||||
|
||||
// 确保在错误情况下清空数据
|
||||
memberList.value = [];
|
||||
total.value = 0;
|
||||
@@ -617,7 +605,7 @@ const showSignRecords = async (member: ExtendedActivityRegisterVO) => {
|
||||
signRecordsDialog.visible = true;
|
||||
signRecordsDialog.title = `${member.studentName}(${member.studentNo})的签到记录`;
|
||||
signRecordsLoading.value = true;
|
||||
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/stgl/activityAttendance/getAllByActivityAndUser',
|
||||
@@ -627,7 +615,7 @@ const showSignRecords = async (member: ExtendedActivityRegisterVO) => {
|
||||
studentNo: member.studentNo
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (res.code === 200) {
|
||||
signRecords.value = res.data || [];
|
||||
} else {
|
||||
@@ -708,7 +696,7 @@ const handleUpdate = async (row?: ExtendedActivityRegisterVO) => {
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getActivityRegister(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
|
||||
|
||||
// 设置学生选择器数据
|
||||
if (res.data.studentName) {
|
||||
form.value.selectedStudent = {
|
||||
@@ -722,7 +710,7 @@ const handleUpdate = async (row?: ExtendedActivityRegisterVO) => {
|
||||
className: res.data.className || ''
|
||||
} as StudentVO;
|
||||
}
|
||||
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改活动报名';
|
||||
};
|
||||
@@ -732,14 +720,14 @@ const submitForm = () => {
|
||||
memberFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
|
||||
|
||||
// 创建提交数据,排除selectedStudent字段
|
||||
const { selectedStudent, ...submitData } = form.value;
|
||||
|
||||
|
||||
// 确保包含必要字段
|
||||
submitData.activityId = props.activityId;
|
||||
submitData.approvalStatus = 1; // 新增的报名默认为已通过状态
|
||||
|
||||
|
||||
try {
|
||||
if (form.value.id) {
|
||||
await updateActivityRegister(submitData as ActivityRegisterForm);
|
||||
@@ -762,14 +750,14 @@ const submitForm = () => {
|
||||
const handleDelete = async (row?: ExtendedActivityRegisterVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除选中的活动报名记录?');
|
||||
|
||||
|
||||
try {
|
||||
if (Array.isArray(_ids)) {
|
||||
await delActivityRegister(_ids.join(','));
|
||||
} else {
|
||||
await delActivityRegister(_ids);
|
||||
}
|
||||
|
||||
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
@@ -911,4 +899,4 @@ onMounted(() => {
|
||||
.text-gray-400 {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -3,21 +3,21 @@
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="always" style="padding: 8px 16px;">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0;">
|
||||
<el-card shadow="always" style="padding: 8px 16px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
|
||||
<el-row :gutter="16" align="middle">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0;">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0;">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -33,10 +33,14 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Check" :disabled="single" @click="handleApprove()" v-hasPermi="['stgl:activityRegister:approve']">批量通过</el-button>
|
||||
<el-button type="success" plain icon="Check" :disabled="single" @click="handleApprove()" v-hasPermi="['stgl:activityRegister:approve']"
|
||||
>批量通过</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Close" :disabled="single" @click="handleReject()" v-hasPermi="['stgl:activityRegister:approve']">批量拒绝</el-button>
|
||||
<el-button type="danger" plain icon="Close" :disabled="single" @click="handleReject()" v-hasPermi="['stgl:activityRegister:approve']"
|
||||
>批量拒绝</el-button
|
||||
>
|
||||
</el-col>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
@@ -86,9 +90,15 @@
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['stgl:activityRegister:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="通过" placement="top">
|
||||
<el-button link type="success" icon="Check" @click="handleApprove(scope.row)" v-hasPermi="['stgl:activityRegister:approve']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="拒绝" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Check"
|
||||
@click="handleApprove(scope.row)"
|
||||
v-hasPermi="['stgl:activityRegister:approve']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="拒绝" placement="top">
|
||||
<el-button link type="danger" icon="Close" @click="handleReject(scope.row)" v-hasPermi="['stgl:activityRegister:approve']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
@@ -123,7 +133,7 @@
|
||||
<el-divider content-position="left">
|
||||
<span class="text-lg font-medium">团队信息</span>
|
||||
</el-divider>
|
||||
|
||||
|
||||
<el-descriptions :column="2" border class="mb-4">
|
||||
<el-descriptions-item label="团队名称">{{ teamInfo.teamName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="团队人数">{{ teamInfo.teamSize || 0 }}人</el-descriptions-item>
|
||||
@@ -160,7 +170,7 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelView">关 闭</el-button>
|
||||
@@ -178,9 +188,9 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核意见" prop="approvalRemark">
|
||||
<el-input
|
||||
v-model="approvalForm.approvalRemark"
|
||||
type="textarea"
|
||||
<el-input
|
||||
v-model="approvalForm.approvalRemark"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:placeholder="approvalForm.approvalStatus === 1 ? '请输入通过意见(可选)' : '请输入拒绝原因(必填)'"
|
||||
/>
|
||||
@@ -299,9 +309,9 @@ const approvalForm = reactive({
|
||||
const approvalRules = reactive({
|
||||
approvalStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
|
||||
approvalRemark: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入拒绝原因',
|
||||
{
|
||||
required: true,
|
||||
message: '请输入拒绝原因',
|
||||
trigger: 'blur',
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (approvalForm.approvalStatus === 2 && !value) {
|
||||
@@ -332,15 +342,15 @@ const { queryParams } = toRefs(data);
|
||||
/** 查询成员列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
|
||||
|
||||
try {
|
||||
const queryData = { ...queryParams.value };
|
||||
|
||||
|
||||
// 确保查询待审核状态(approvalStatus='0')
|
||||
queryData.approvalStatus = '0';
|
||||
|
||||
|
||||
const res = await listActivityRegister(queryData);
|
||||
|
||||
|
||||
memberList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
@@ -385,7 +395,7 @@ const handleApprove = (row?: ExtendedStglActivityRegistrationVo) => {
|
||||
proxy?.$modal.msgError('请选择要审核的记录');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
approvalForm.ids = _ids;
|
||||
approvalForm.approvalStatus = 1;
|
||||
approvalForm.approvalRemark = '';
|
||||
@@ -400,7 +410,7 @@ const handleReject = (row?: ExtendedStglActivityRegistrationVo) => {
|
||||
proxy?.$modal.msgError('请选择要审核的记录');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
approvalForm.ids = _ids;
|
||||
approvalForm.approvalStatus = 2;
|
||||
approvalForm.approvalRemark = '';
|
||||
@@ -413,29 +423,21 @@ const submitApproval = () => {
|
||||
approvalFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
|
||||
|
||||
try {
|
||||
if (approvalForm.ids.length === 1) {
|
||||
// 单个审核
|
||||
await approveActivityRegister(
|
||||
approvalForm.ids[0],
|
||||
approvalForm.approvalStatus,
|
||||
approvalForm.approvalRemark
|
||||
);
|
||||
await approveActivityRegister(approvalForm.ids[0], approvalForm.approvalStatus, approvalForm.approvalRemark);
|
||||
} else {
|
||||
// 批量审核
|
||||
await batchApproveActivityRegister(
|
||||
approvalForm.ids,
|
||||
approvalForm.approvalStatus,
|
||||
approvalForm.approvalRemark
|
||||
);
|
||||
await batchApproveActivityRegister(approvalForm.ids, approvalForm.approvalStatus, approvalForm.approvalRemark);
|
||||
}
|
||||
|
||||
|
||||
const actionText = approvalForm.approvalStatus === 1 ? '通过' : '拒绝';
|
||||
proxy?.$modal.msgSuccess(`批量${actionText}成功`);
|
||||
approvalDialog.visible = false;
|
||||
await getList();
|
||||
|
||||
|
||||
// 通知父组件刷新其他标签页
|
||||
notifyRefreshOtherTabs();
|
||||
} catch (error) {
|
||||
@@ -457,14 +459,14 @@ const cancelApproval = () => {
|
||||
const handleDelete = async (row?: ExtendedStglActivityRegistrationVo) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除选中的活动报名记录?');
|
||||
|
||||
|
||||
try {
|
||||
if (Array.isArray(_ids)) {
|
||||
await delActivityRegister(_ids.join(','));
|
||||
} else {
|
||||
await delActivityRegister(_ids);
|
||||
}
|
||||
|
||||
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
@@ -478,7 +480,7 @@ const handleView = async (row: ExtendedStglActivityRegistrationVo) => {
|
||||
Object.assign(viewForm, res.data);
|
||||
viewDialog.visible = true;
|
||||
viewDialog.title = '查看活动报名详情';
|
||||
|
||||
|
||||
// 如果是团队报名,获取团队信息
|
||||
if (Number(viewForm.registrationType) === 2 && viewForm.teamId) {
|
||||
await loadTeamInfo(viewForm.teamId);
|
||||
@@ -494,39 +496,37 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loadingTeamInfo.value = true;
|
||||
|
||||
|
||||
try {
|
||||
// 尝试调用团队信息API获取完整信息
|
||||
try {
|
||||
const response = await getTeamsByActivityId(viewForm.activityId);
|
||||
console.log('PendingMembersTab: 团队信息API响应:', response);
|
||||
|
||||
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
// 从团队列表中查找当前团队
|
||||
const teamData = response.data.find((team: any) => {
|
||||
return team.id == teamId || team.id.toString() === teamId || team.id === parseInt(teamId as string);
|
||||
});
|
||||
|
||||
|
||||
console.log('PendingMembersTab: 找到的团队数据:', teamData);
|
||||
|
||||
|
||||
if (teamData) {
|
||||
// 优先从teamMembers中查找队长信息
|
||||
let leaderPhone = '暂无联系方式';
|
||||
let leaderDepartment = '暂无院系信息';
|
||||
let leaderMajor = '';
|
||||
let leaderClassName = '';
|
||||
|
||||
|
||||
try {
|
||||
if (teamData.teamMembers) {
|
||||
const membersData = JSON.parse(teamData.teamMembers);
|
||||
if (Array.isArray(membersData)) {
|
||||
// 查找队长信息(isLeader: 1 或者 userId 匹配 leaderUserId)
|
||||
const leaderMember = membersData.find((member: any) =>
|
||||
member.isLeader === 1 || member.userId === teamData.leaderUserId
|
||||
);
|
||||
|
||||
const leaderMember = membersData.find((member: any) => member.isLeader === 1 || member.userId === teamData.leaderUserId);
|
||||
|
||||
if (leaderMember) {
|
||||
leaderPhone = leaderMember.phone || '暂无联系方式';
|
||||
leaderDepartment = leaderMember.department || '暂无院系信息';
|
||||
@@ -539,7 +539,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
} catch (parseError) {
|
||||
console.error('PendingMembersTab: 解析团队成员JSON失败:', parseError);
|
||||
}
|
||||
|
||||
|
||||
// 如果从teamMembers中没有找到队长信息,尝试通过leaderUserId获取
|
||||
if (leaderPhone === '暂无联系方式' || leaderDepartment === '暂无院系信息') {
|
||||
try {
|
||||
@@ -547,7 +547,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
console.log('PendingMembersTab: 通过leaderUserId获取队长详细信息:', teamData.leaderUserId);
|
||||
const studentResponse = await getStudent(teamData.leaderUserId);
|
||||
console.log('PendingMembersTab: getStudent API响应:', studentResponse);
|
||||
|
||||
|
||||
if (studentResponse.data) {
|
||||
const studentData = studentResponse.data;
|
||||
console.log('PendingMembersTab: 学生数据详情:', studentData);
|
||||
@@ -557,7 +557,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
majorName: studentData.majorName,
|
||||
className: studentData.className
|
||||
});
|
||||
|
||||
|
||||
// 只有在teamMembers中没有找到时才使用API获取的信息
|
||||
if (leaderPhone === '暂无联系方式') {
|
||||
leaderPhone = studentData.phone || '暂无联系方式';
|
||||
@@ -580,7 +580,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
console.log('PendingMembersTab: 获取队长详细信息失败:', studentError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果仍然没有获取到队长信息,尝试从报名详情中获取
|
||||
if (leaderPhone === '暂无联系方式' && viewForm.phone) {
|
||||
leaderPhone = viewForm.phone;
|
||||
@@ -590,17 +590,15 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
leaderDepartment = viewForm.department;
|
||||
console.log('PendingMembersTab: 使用报名详情中的院系信息:', leaderDepartment);
|
||||
}
|
||||
|
||||
|
||||
// 最后的备用方案:如果还是没有获取到,尝试从teamMembers中查找匹配leaderUserId的成员
|
||||
if ((leaderPhone === '暂无联系方式' || leaderDepartment === '暂无院系信息') && teamData.teamMembers) {
|
||||
try {
|
||||
const membersData = JSON.parse(teamData.teamMembers);
|
||||
if (Array.isArray(membersData)) {
|
||||
// 查找与leaderUserId匹配的成员
|
||||
const leaderMember = membersData.find((member: any) =>
|
||||
member.userId === teamData.leaderUserId
|
||||
);
|
||||
|
||||
const leaderMember = membersData.find((member: any) => member.userId === teamData.leaderUserId);
|
||||
|
||||
if (leaderMember) {
|
||||
if (leaderPhone === '暂无联系方式') {
|
||||
leaderPhone = leaderMember.phone || '暂无联系方式';
|
||||
@@ -621,7 +619,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
console.error('PendingMembersTab: 备用方案解析团队成员JSON失败:', parseError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新团队信息
|
||||
teamInfo.value = {
|
||||
id: teamData.id,
|
||||
@@ -636,9 +634,9 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
description: teamData.description || '',
|
||||
teamMembers: teamData.teamMembers || '[]'
|
||||
};
|
||||
|
||||
|
||||
console.log('PendingMembersTab: 最终设置的团队信息:', teamInfo.value);
|
||||
|
||||
|
||||
// 解析团队成员信息
|
||||
if (teamData.teamMembers) {
|
||||
try {
|
||||
@@ -674,13 +672,13 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.log('PendingMembersTab: 团队信息API调用失败,使用报名详情中的信息:', apiError);
|
||||
|
||||
|
||||
// 如果API调用失败,使用报名详情中的信息
|
||||
if (viewForm.teamName) {
|
||||
// 尝试获取队长的详细信息
|
||||
let leaderPhone = '暂无联系方式';
|
||||
let leaderDepartment = '暂无院系信息';
|
||||
|
||||
|
||||
try {
|
||||
if (viewForm.userId) {
|
||||
const studentResponse = await getStudent(viewForm.userId);
|
||||
@@ -701,7 +699,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
leaderDepartment = viewForm.department;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 构造团队信息对象,优先使用获取到的详细信息
|
||||
teamInfo.value = {
|
||||
id: viewForm.teamId,
|
||||
@@ -716,10 +714,10 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
description: viewForm.remark || '暂无团队简介',
|
||||
teamMembers: '[]' // JSON格式的字符串
|
||||
};
|
||||
|
||||
|
||||
// 团队成员信息暂时为空,因为报名详情中没有这个字段
|
||||
teamMembers.value = [] as TeamMemberInfo[];
|
||||
|
||||
|
||||
console.log('PendingMembersTab: 使用报名详情构造的团队信息:', teamInfo.value);
|
||||
} else {
|
||||
teamInfo.value = null;
|
||||
@@ -744,8 +742,6 @@ const cancelView = () => {
|
||||
teamMembers.value = [] as TeamMemberInfo[];
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
getList
|
||||
@@ -753,7 +749,7 @@ defineExpose({
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
refreshOtherTabs: []
|
||||
refreshOtherTabs: [];
|
||||
}>();
|
||||
|
||||
// 通知父组件刷新其他标签页
|
||||
@@ -803,4 +799,4 @@ onMounted(() => {
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -3,21 +3,21 @@
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="always" style="padding: 8px 16px;">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0;">
|
||||
<el-card shadow="always" style="padding: 8px 16px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
|
||||
<el-row :gutter="16" align="middle">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0;">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0;">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -36,10 +36,14 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activityRegister:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:activityRegister:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:activityRegister:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:activityRegister:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:activityRegister:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Download" @click="handleExport" v-hasPermi="['stgl:activityRegister:export']">导出数据</el-button>
|
||||
@@ -98,7 +102,7 @@
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['stgl:activityRegister:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
|
||||
<!-- 审核通过状态显示成绩录入按钮 -->
|
||||
<template v-if="Number(scope.row.approvalStatus) === 1">
|
||||
<el-tooltip content="录入成绩" placement="top">
|
||||
@@ -111,7 +115,7 @@
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['stgl:activityRegister:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
@@ -216,7 +220,7 @@
|
||||
<el-divider content-position="left">
|
||||
<span class="text-lg font-medium">团队信息</span>
|
||||
</el-divider>
|
||||
|
||||
|
||||
<el-descriptions :column="2" border class="mb-4">
|
||||
<el-descriptions-item label="团队名称">{{ teamInfo.teamName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="团队人数">{{ teamInfo.teamSize || 0 }}人</el-descriptions-item>
|
||||
@@ -253,7 +257,7 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelView">关 闭</el-button>
|
||||
@@ -306,7 +310,7 @@
|
||||
是否更新已经存在的报名数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
@@ -508,18 +512,22 @@ const data = reactive<PageData<ExtendedActivityRegisterForm, StglActivityRegistr
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
// 监听activityId变化,同步更新查询参数
|
||||
watch(() => props.activityId, (newActivityId) => {
|
||||
if (newActivityId) {
|
||||
console.log('RegisteredMembersTab: activityId发生变化,从', queryParams.value.activityId, '更新为', newActivityId);
|
||||
queryParams.value.activityId = newActivityId;
|
||||
// 重置分页到第一页
|
||||
queryParams.value.pageNum = 1;
|
||||
// 如果组件已经挂载,则立即重新查询
|
||||
if (memberList.value.length > 0 || loading.value === false) {
|
||||
getList();
|
||||
watch(
|
||||
() => props.activityId,
|
||||
(newActivityId) => {
|
||||
if (newActivityId) {
|
||||
console.log('RegisteredMembersTab: activityId发生变化,从', queryParams.value.activityId, '更新为', newActivityId);
|
||||
queryParams.value.activityId = newActivityId;
|
||||
// 重置分页到第一页
|
||||
queryParams.value.pageNum = 1;
|
||||
// 如果组件已经挂载,则立即重新查询
|
||||
if (memberList.value.length > 0 || loading.value === false) {
|
||||
getList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 上传相关
|
||||
const uploadRef = ref<UploadInstance>();
|
||||
@@ -544,23 +552,23 @@ const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 构建查询参数,确保使用最新的activityId
|
||||
const queryData = {
|
||||
const queryData = {
|
||||
...queryParams.value,
|
||||
activityId: props.activityId // 强制使用最新的props值
|
||||
activityId: props.activityId // 强制使用最新的props值
|
||||
};
|
||||
|
||||
|
||||
// 如果状态字段为空或未选择,则查询已通过(approvalStatus=1)的报名记录
|
||||
if (!queryData.approvalStatus) {
|
||||
queryData.approvalStatus = '1';
|
||||
}
|
||||
|
||||
|
||||
console.log('RegisteredMembersTab: getList 开始查询,参数:', queryData);
|
||||
console.log('RegisteredMembersTab: props.activityId:', props.activityId);
|
||||
console.log('RegisteredMembersTab: queryParams.activityId:', queryParams.value.activityId);
|
||||
|
||||
|
||||
const res = await listActivityRegister(queryData);
|
||||
console.log('RegisteredMembersTab: 查询结果:', res);
|
||||
|
||||
|
||||
memberList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
@@ -579,9 +587,9 @@ const cancel = () => {
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = {
|
||||
...initFormData,
|
||||
activityId: props.activityId // 确保使用最新的activityId
|
||||
form.value = {
|
||||
...initFormData,
|
||||
activityId: props.activityId // 确保使用最新的activityId
|
||||
};
|
||||
memberFormRef.value?.resetFields();
|
||||
};
|
||||
@@ -626,7 +634,7 @@ const handleUpdate = async (row?: ExtendedStglActivityRegistrationVo) => {
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getActivityRegister(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
|
||||
|
||||
// 设置学生选择器数据
|
||||
if (res.data.studentName) {
|
||||
// 使用扩展类型来访问额外字段
|
||||
@@ -642,7 +650,7 @@ const handleUpdate = async (row?: ExtendedStglActivityRegistrationVo) => {
|
||||
className: extendedData.className || ''
|
||||
} as StudentVO;
|
||||
}
|
||||
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改活动报名';
|
||||
};
|
||||
@@ -652,14 +660,14 @@ const submitForm = () => {
|
||||
memberFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
|
||||
|
||||
// 创建提交数据,排除selectedStudent字段
|
||||
const { selectedStudent, ...submitData } = form.value;
|
||||
|
||||
|
||||
// 确保包含必要字段
|
||||
submitData.activityId = props.activityId; // 使用最新的activityId
|
||||
submitData.activityId = props.activityId; // 使用最新的activityId
|
||||
submitData.approvalStatus = 1; // 新增的报名默认为已通过状态
|
||||
|
||||
|
||||
try {
|
||||
if (form.value.id) {
|
||||
await updateActivityRegister(submitData as StglActivityRegistrationForm);
|
||||
@@ -682,14 +690,14 @@ const submitForm = () => {
|
||||
const handleDelete = async (row?: ExtendedStglActivityRegistrationVo) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除选中的活动报名记录?');
|
||||
|
||||
|
||||
try {
|
||||
if (Array.isArray(_ids)) {
|
||||
await delActivityRegister(_ids.join(','));
|
||||
} else {
|
||||
await delActivityRegister(_ids);
|
||||
}
|
||||
|
||||
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
@@ -703,7 +711,7 @@ const handleView = async (row: ExtendedStglActivityRegistrationVo) => {
|
||||
Object.assign(viewForm, res.data);
|
||||
viewDialog.visible = true;
|
||||
viewDialog.title = '查看活动报名详情';
|
||||
|
||||
|
||||
// 如果是团队报名,获取团队信息
|
||||
if (Number(viewForm.registrationType) === 2 && viewForm.teamId) {
|
||||
await loadTeamInfo(viewForm.teamId);
|
||||
@@ -719,39 +727,37 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loadingTeamInfo.value = true;
|
||||
|
||||
|
||||
try {
|
||||
// 尝试调用团队信息API获取完整信息
|
||||
try {
|
||||
const response = await getTeamsByActivityId(viewForm.activityId);
|
||||
console.log('RegisteredMembersTab: 团队信息API响应:', response);
|
||||
|
||||
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
// 从团队列表中查找当前团队
|
||||
const teamData = response.data.find((team: any) => {
|
||||
return team.id == teamId || team.id.toString() === teamId || team.id === parseInt(teamId as string);
|
||||
});
|
||||
|
||||
|
||||
console.log('RegisteredMembersTab: 找到的团队数据:', teamData);
|
||||
|
||||
|
||||
if (teamData) {
|
||||
// 优先从teamMembers中查找队长信息
|
||||
let leaderPhone = '暂无联系方式';
|
||||
let leaderDepartment = '暂无院系信息';
|
||||
let leaderMajor = '';
|
||||
let leaderClassName = '';
|
||||
|
||||
|
||||
try {
|
||||
if (teamData.teamMembers) {
|
||||
const membersData = JSON.parse(teamData.teamMembers);
|
||||
if (Array.isArray(membersData)) {
|
||||
// 查找队长信息(isLeader: 1 或者 userId 匹配 leaderUserId)
|
||||
const leaderMember = membersData.find((member: any) =>
|
||||
member.isLeader === 1 || member.userId === teamData.leaderUserId
|
||||
);
|
||||
|
||||
const leaderMember = membersData.find((member: any) => member.isLeader === 1 || member.userId === teamData.leaderUserId);
|
||||
|
||||
if (leaderMember) {
|
||||
leaderPhone = leaderMember.phone || '暂无联系方式';
|
||||
leaderDepartment = leaderMember.department || '暂无院系信息';
|
||||
@@ -764,7 +770,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
} catch (parseError) {
|
||||
console.error('RegisteredMembersTab: 解析团队成员JSON失败:', parseError);
|
||||
}
|
||||
|
||||
|
||||
// 如果从teamMembers中没有找到队长信息,尝试通过leaderUserId获取
|
||||
if (leaderPhone === '暂无联系方式' || leaderDepartment === '暂无院系信息') {
|
||||
try {
|
||||
@@ -772,7 +778,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
console.log('RegisteredMembersTab: 通过leaderUserId获取队长详细信息:', teamData.leaderUserId);
|
||||
const studentResponse = await getStudent(teamData.leaderUserId);
|
||||
console.log('RegisteredMembersTab: getStudent API响应:', studentResponse);
|
||||
|
||||
|
||||
if (studentResponse.data) {
|
||||
const studentData = studentResponse.data;
|
||||
console.log('RegisteredMembersTab: 学生数据详情:', studentData);
|
||||
@@ -782,7 +788,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
majorName: studentData.majorName,
|
||||
className: studentData.className
|
||||
});
|
||||
|
||||
|
||||
// 只有在teamMembers中没有找到时才使用API获取的信息
|
||||
if (leaderPhone === '暂无联系方式') {
|
||||
leaderPhone = studentData.phone || '暂无联系方式';
|
||||
@@ -805,7 +811,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
console.log('RegisteredMembersTab: 获取队长详细信息失败:', studentError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果仍然没有获取到队长信息,尝试从报名详情中获取
|
||||
if (leaderPhone === '暂无联系方式' && viewForm.phone) {
|
||||
leaderPhone = viewForm.phone;
|
||||
@@ -815,17 +821,15 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
leaderDepartment = viewForm.department;
|
||||
console.log('RegisteredMembersTab: 使用报名详情中的院系信息:', leaderDepartment);
|
||||
}
|
||||
|
||||
|
||||
// 最后的备用方案:如果还是没有获取到,尝试从teamMembers中查找匹配leaderUserId的成员
|
||||
if ((leaderPhone === '暂无联系方式' || leaderDepartment === '暂无院系信息') && teamData.teamMembers) {
|
||||
try {
|
||||
const membersData = JSON.parse(teamData.teamMembers);
|
||||
if (Array.isArray(membersData)) {
|
||||
// 查找与leaderUserId匹配的成员
|
||||
const leaderMember = membersData.find((member: any) =>
|
||||
member.userId === teamData.leaderUserId
|
||||
);
|
||||
|
||||
const leaderMember = membersData.find((member: any) => member.userId === teamData.leaderUserId);
|
||||
|
||||
if (leaderMember) {
|
||||
if (leaderPhone === '暂无联系方式') {
|
||||
leaderPhone = leaderMember.phone || '暂无联系方式';
|
||||
@@ -846,7 +850,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
console.error('RegisteredMembersTab: 备用方案解析团队成员JSON失败:', parseError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新团队信息
|
||||
teamInfo.value = {
|
||||
id: teamData.id,
|
||||
@@ -861,9 +865,9 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
description: teamData.description || '',
|
||||
teamMembers: teamData.teamMembers || '[]'
|
||||
};
|
||||
|
||||
|
||||
console.log('RegisteredMembersTab: 最终设置的团队信息:', teamInfo.value);
|
||||
|
||||
|
||||
// 解析团队成员信息
|
||||
if (teamData.teamMembers) {
|
||||
try {
|
||||
@@ -899,13 +903,13 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.log('RegisteredMembersTab: 团队信息API调用失败,使用报名详情中的信息:', apiError);
|
||||
|
||||
|
||||
// 如果API调用失败,使用报名详情中的信息
|
||||
if (viewForm.teamName) {
|
||||
// 尝试获取队长的详细信息
|
||||
let leaderPhone = '暂无联系方式';
|
||||
let leaderDepartment = '暂无院系信息';
|
||||
|
||||
|
||||
try {
|
||||
if (viewForm.userId) {
|
||||
const studentResponse = await getStudent(viewForm.userId);
|
||||
@@ -926,7 +930,7 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
leaderDepartment = viewForm.department;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 构造团队信息对象,优先使用获取到的详细信息
|
||||
teamInfo.value = {
|
||||
id: viewForm.teamId,
|
||||
@@ -941,10 +945,10 @@ const loadTeamInfo = async (teamId: string | number) => {
|
||||
description: viewForm.remark || '暂无团队简介',
|
||||
teamMembers: '[]' // JSON格式的字符串
|
||||
};
|
||||
|
||||
|
||||
// 团队成员信息暂时为空,因为报名详情中没有这个字段
|
||||
teamMembers.value = [] as TeamMemberInfo[];
|
||||
|
||||
|
||||
console.log('RegisteredMembersTab: 使用报名详情构造的团队信息:', teamInfo.value);
|
||||
} else {
|
||||
teamInfo.value = null;
|
||||
@@ -1124,4 +1128,4 @@ onMounted(() => {
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -147,7 +147,7 @@
|
||||
<!-- 查看活动详细信息对话框 -->
|
||||
<el-dialog :title="viewDialog.title" v-model="viewDialog.visible" width="1200px" append-to-body>
|
||||
<ActivityDetailView :activity="viewForm" />
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleRegister(viewForm)" v-if="canRegister(viewForm)">立即报名</el-button>
|
||||
@@ -484,7 +484,7 @@ const checkUserRegistrationStatus = async () => {
|
||||
console.warn('活动数据不完整,跳过报名状态检查:', activity);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const registered = await checkRegistration(activity.id as number);
|
||||
userRegistrationStatus.value[activity.id as number] = registered.data;
|
||||
} catch (error) {
|
||||
@@ -591,7 +591,7 @@ const handleRegistrationTypeChange = async (value: number) => {
|
||||
} else if (value === 2) {
|
||||
// 团队报名,自动填充当前用户信息作为团队负责人
|
||||
await loadUserInfoToForm();
|
||||
|
||||
|
||||
// 设置团队负责人为学生对象(用于StudentSelector组件)
|
||||
// 使用类型断言避免类型错误,因为我们只需要这些字段用于显示
|
||||
registerForm.presidentStudent = {
|
||||
@@ -747,14 +747,14 @@ const submitRegister = () => {
|
||||
// 个人报名时不发送任何团队相关字段
|
||||
|
||||
await registerActivity(submitData);
|
||||
|
||||
|
||||
// 根据审核设置显示不同的成功消息
|
||||
if (selectedActivity.value.needApproval === 0) {
|
||||
proxy?.$modal.msgSuccess('报名成功!您的报名已自动通过审核');
|
||||
} else {
|
||||
proxy?.$modal.msgSuccess('报名成功,请等待审核');
|
||||
}
|
||||
|
||||
|
||||
registerDialog.visible = false;
|
||||
|
||||
// 更新报名状态
|
||||
|
||||
@@ -84,18 +84,41 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activity:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="!single && selectedActivity && ['0', '7', '6'].includes(getOriginalActivityStatus(selectedActivity)) && hasActivityOperationPermission(selectedActivity)">
|
||||
<el-button icon="Edit" @click="handleUpdate()" v-hasPermi="['stgl:activity:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '0' && hasActivityOperationPermission(selectedActivity)">
|
||||
<el-button type="success" icon="Upload" @click="handleSubmit()" v-hasPermi="['stgl:activity:edit']">提交</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '2' && hasActivityOperationPermission(selectedActivity)">
|
||||
<el-button type="warning" icon="Check" @click="handleApprove()" v-hasPermi="['stgl:activity:approve']">审批</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="!single && selectedActivity && ['3', '4'].includes(selectedActivity.activityStatus) && hasActivityOperationPermission(selectedActivity)">
|
||||
<el-button type="danger" icon="Close" @click="handleCancel()" v-hasPermi="['stgl:activity:edit']">取消活动</el-button>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="1.5"
|
||||
v-if="
|
||||
!single &&
|
||||
selectedActivity &&
|
||||
['0', '7', '6'].includes(getOriginalActivityStatus(selectedActivity)) &&
|
||||
hasActivityOperationPermission(selectedActivity)
|
||||
"
|
||||
>
|
||||
<el-button icon="Edit" @click="handleUpdate()" v-hasPermi="['stgl:activity:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="1.5"
|
||||
v-if="
|
||||
!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '0' && hasActivityOperationPermission(selectedActivity)
|
||||
"
|
||||
>
|
||||
<el-button type="success" icon="Upload" @click="handleSubmit()" v-hasPermi="['stgl:activity:edit']">提交</el-button>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="1.5"
|
||||
v-if="
|
||||
!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '2' && hasActivityOperationPermission(selectedActivity)
|
||||
"
|
||||
>
|
||||
<el-button type="warning" icon="Check" @click="handleApprove()" v-hasPermi="['stgl:activity:approve']">审批</el-button>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="1.5"
|
||||
v-if="
|
||||
!single && selectedActivity && ['3', '4'].includes(selectedActivity.activityStatus) && hasActivityOperationPermission(selectedActivity)
|
||||
"
|
||||
>
|
||||
<el-button type="danger" icon="Close" @click="handleCancel()" v-hasPermi="['stgl:activity:edit']">取消活动</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="CopyDocument" :disabled="single" @click="handleCopy()" v-hasPermi="['stgl:activity:add']">复制</el-button>
|
||||
</el-col>
|
||||
@@ -105,7 +128,9 @@
|
||||
:disabled="multiple || !['0', '7'].includes(getOriginalActivityStatus(selectedActivity) || '')"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['stgl:activity:remove']"
|
||||
:title="selectedActivity && !['0', '7'].includes(getOriginalActivityStatus(selectedActivity)) ? '只有草稿和已驳回状态的活动可以删除' : ''"
|
||||
:title="
|
||||
selectedActivity && !['0', '7'].includes(getOriginalActivityStatus(selectedActivity)) ? '只有草稿和已驳回状态的活动可以删除' : ''
|
||||
"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
@@ -209,7 +234,11 @@
|
||||
v-hasPermi="['stgl:activity:query']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top" v-if="['0', '7', '6'].includes(getOriginalActivityStatus(scope.row)) && hasActivityOperationPermission(scope.row)">
|
||||
<el-tooltip
|
||||
content="修改"
|
||||
placement="top"
|
||||
v-if="['0', '7', '6'].includes(getOriginalActivityStatus(scope.row)) && hasActivityOperationPermission(scope.row)"
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@@ -220,7 +249,11 @@
|
||||
v-hasPermi="['stgl:activity:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="提交" placement="top" v-if="getOriginalActivityStatus(scope.row) === '0' && hasActivityOperationPermission(scope.row)">
|
||||
<el-tooltip
|
||||
content="提交"
|
||||
placement="top"
|
||||
v-if="getOriginalActivityStatus(scope.row) === '0' && hasActivityOperationPermission(scope.row)"
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
@@ -231,7 +264,11 @@
|
||||
v-hasPermi="['stgl:activity:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="审批" placement="top" v-if="getOriginalActivityStatus(scope.row) === '2' && hasActivityOperationPermission(scope.row)">
|
||||
<el-tooltip
|
||||
content="审批"
|
||||
placement="top"
|
||||
v-if="getOriginalActivityStatus(scope.row) === '2' && hasActivityOperationPermission(scope.row)"
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
@@ -242,7 +279,11 @@
|
||||
v-hasPermi="['stgl:activity:approve']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="取消活动" placement="top" v-if="['3', '4'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)">
|
||||
<el-tooltip
|
||||
content="取消活动"
|
||||
placement="top"
|
||||
v-if="['3', '4'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)"
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@@ -286,7 +327,11 @@
|
||||
v-hasPermi="['stgl:activity:query']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="['0', '7'].includes(getOriginalActivityStatus(scope.row)) && hasActivityOperationPermission(scope.row)">
|
||||
<el-tooltip
|
||||
content="删除"
|
||||
placement="top"
|
||||
v-if="['0', '7'].includes(getOriginalActivityStatus(scope.row)) && hasActivityOperationPermission(scope.row)"
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@@ -409,9 +454,9 @@
|
||||
<div class="section-divider">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
</div>
|
||||
<AdvisorManager
|
||||
<AdvisorManager
|
||||
:key="`advisor-manager-${form.id || 'new'}-${formKey}`"
|
||||
v-model="form.advisors"
|
||||
v-model="form.advisors"
|
||||
@change="handleAdvisorsChange"
|
||||
:initial-advisors="form.advisors"
|
||||
/>
|
||||
@@ -495,10 +540,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
|
||||
|
||||
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -565,7 +606,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 成员审核弹窗 -->
|
||||
<ActivityMembersDialog
|
||||
<ActivityMembersDialog
|
||||
v-model:visible="membersDialog.visible"
|
||||
:activity-id="selectedActivity?.id"
|
||||
:activity-name="selectedActivity?.activityName"
|
||||
@@ -585,14 +626,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="驳回原因" prop="rejectReason" v-if="approveForm.approvalResult === 'reject'">
|
||||
<el-input
|
||||
v-model="approveForm.rejectReason"
|
||||
type="textarea"
|
||||
placeholder="请输入驳回原因"
|
||||
:rows="3"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="approveForm.rejectReason" type="textarea" placeholder="请输入驳回原因" :rows="3" :maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -636,7 +670,14 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, stgl_activity_range, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'stgl_activity_range', 'sys_yes_no')
|
||||
proxy?.useDict(
|
||||
'stgl_activity_type',
|
||||
'stgl_activity_status',
|
||||
'stgl_registration_type',
|
||||
'stgl_registration_status',
|
||||
'stgl_activity_range',
|
||||
'sys_yes_no'
|
||||
)
|
||||
);
|
||||
|
||||
const activityList = ref<StglActivityVO[]>([]);
|
||||
@@ -790,8 +831,6 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
const viewForm = ref<StglActivityVO>({} as StglActivityVO);
|
||||
|
||||
|
||||
|
||||
/** 初始化URL参数 */
|
||||
const initUrlParams = () => {
|
||||
const { relatedClubId, relatedClubName, relatedTeamId, relatedTeamName, from } = route.query;
|
||||
@@ -897,27 +936,27 @@ const reset = () => {
|
||||
form.value.advisors = [];
|
||||
form.value.instructorList = [];
|
||||
form.value.instructors = '[]';
|
||||
|
||||
|
||||
// 重置关联社团禁用状态
|
||||
isRelatedClubDisabled.value = false;
|
||||
|
||||
|
||||
// 重置审批表单
|
||||
approveForm.id = undefined;
|
||||
approveForm.activityName = '';
|
||||
approveForm.approvalResult = 'approve';
|
||||
approveForm.rejectReason = '';
|
||||
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
formKey.value++;
|
||||
|
||||
|
||||
activeTab.value = 'basic';
|
||||
|
||||
|
||||
console.log('表单重置后的状态:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors
|
||||
});
|
||||
|
||||
|
||||
// 延迟重置表单字段,避免递归更新
|
||||
setTimeout(() => {
|
||||
activityFormRef.value?.resetFields();
|
||||
@@ -950,37 +989,37 @@ const handleAdd = () => {
|
||||
// 先重置表单
|
||||
form.value = { ...initFormData };
|
||||
activeTab.value = 'basic';
|
||||
|
||||
|
||||
// 确保指导老师字段被正确初始化
|
||||
form.value.advisors = [];
|
||||
form.value.instructorList = [];
|
||||
form.value.instructors = '[]';
|
||||
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
formKey.value++;
|
||||
|
||||
|
||||
console.log('新增活动 - 初始化表单数据:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors,
|
||||
formKey: formKey.value
|
||||
});
|
||||
|
||||
|
||||
// 如果有查询条件中的关联组织(社团或志愿者队伍),自动填充关联信息
|
||||
if (queryParams.value.relatedClub) {
|
||||
form.value.relatedClub = queryParams.value.relatedClub;
|
||||
form.value.relatedClubId = queryParams.value.relatedClub.relatedClubId;
|
||||
form.value.relatedType = queryParams.value.relatedClub.relatedType;
|
||||
|
||||
|
||||
// 设置关联社团字段为禁用状态(不允许修改)
|
||||
isRelatedClubDisabled.value = true;
|
||||
|
||||
|
||||
console.log('新增活动 - 自动填充关联组织信息:', {
|
||||
relatedType: queryParams.value.relatedClub.relatedType,
|
||||
relatedClubId: queryParams.value.relatedClub.relatedClubId,
|
||||
relatedClubName: queryParams.value.relatedClub.relatedClubName
|
||||
});
|
||||
|
||||
|
||||
// 确保表单数据正确设置
|
||||
console.log('新增活动 - 表单数据设置后:', {
|
||||
formRelatedClub: form.value.relatedClub,
|
||||
@@ -994,7 +1033,7 @@ const handleAdd = () => {
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加活动信息';
|
||||
|
||||
|
||||
// 确保对话框完全打开后再检查表单状态
|
||||
nextTick(() => {
|
||||
console.log('新增活动 - 对话框打开后的表单状态:', {
|
||||
@@ -1036,7 +1075,7 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
relatedType: res.data.relatedType,
|
||||
uniqueKey: `${res.data.relatedType}-${res.data.relatedClubId}`
|
||||
};
|
||||
|
||||
|
||||
// 修改时,如果是从URL参数进入的,则禁用关联社团字段
|
||||
if (queryParams.value.relatedClub && queryParams.value.relatedClub.relatedClubId === res.data.relatedClubId) {
|
||||
isRelatedClubDisabled.value = true;
|
||||
@@ -1074,7 +1113,7 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
try {
|
||||
const instructorsData = JSON.parse(res.data.instructors);
|
||||
console.log('解析的指导老师数据:', instructorsData);
|
||||
|
||||
|
||||
// 确保数据格式正确
|
||||
if (Array.isArray(instructorsData)) {
|
||||
(res.data as any).advisors = instructorsData.map((advisor: any) => ({
|
||||
@@ -1098,12 +1137,12 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
}
|
||||
|
||||
Object.assign(form.value, res.data);
|
||||
|
||||
|
||||
// 确保数据类型正确
|
||||
if (form.value.registrationType !== undefined) {
|
||||
form.value.registrationType = String(form.value.registrationType);
|
||||
}
|
||||
|
||||
|
||||
// 确保指导老师数据正确设置
|
||||
console.log('修改活动 - 设置表单数据后的状态:', {
|
||||
advisors: form.value.advisors,
|
||||
@@ -1111,10 +1150,10 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
instructors: form.value.instructors,
|
||||
registrationType: form.value.registrationType
|
||||
});
|
||||
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
formKey.value++;
|
||||
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改活动信息';
|
||||
};
|
||||
@@ -1122,7 +1161,7 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
/** 显示验证错误提示 */
|
||||
const showValidationErrors = (invalidFields: any) => {
|
||||
if (!invalidFields) return;
|
||||
|
||||
|
||||
const errorMessages: string[] = [];
|
||||
const tabMapping = {
|
||||
// 基本信息标签页字段
|
||||
@@ -1135,20 +1174,23 @@ const showValidationErrors = (invalidFields: any) => {
|
||||
'registrationType': { tab: 'registration', label: '报名方式' },
|
||||
'activityRange': { tab: 'registration', label: '报名范围' }
|
||||
};
|
||||
|
||||
|
||||
let needSwitchTab = '';
|
||||
let currentTabErrors: string[] = [];
|
||||
let otherTabErrors: string[] = [];
|
||||
|
||||
|
||||
// 分类错误信息
|
||||
Object.keys(invalidFields).forEach(fieldName => {
|
||||
Object.keys(invalidFields).forEach((fieldName) => {
|
||||
const fieldInfo = tabMapping[fieldName];
|
||||
if (fieldInfo) {
|
||||
const errorMsg = invalidFields[fieldName][0]?.message || '验证失败';
|
||||
// 去掉重复的"不能为空"等文字,只保留核心提示
|
||||
const cleanMessage = errorMsg.replace(/不能为空$/, '').replace(/^请选择/, '').replace(/^请输入/, '');
|
||||
const cleanMessage = errorMsg
|
||||
.replace(/不能为空$/, '')
|
||||
.replace(/^请选择/, '')
|
||||
.replace(/^请输入/, '');
|
||||
const errorMessage = cleanMessage ? `${fieldInfo.label}:${cleanMessage}` : `${fieldInfo.label}为必填项`;
|
||||
|
||||
|
||||
if (fieldInfo.tab === activeTab.value) {
|
||||
currentTabErrors.push(errorMessage);
|
||||
} else {
|
||||
@@ -1159,17 +1201,17 @@ const showValidationErrors = (invalidFields: any) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 构建提示消息
|
||||
if (currentTabErrors.length > 0) {
|
||||
errorMessages.push(`【当前页面】${currentTabErrors.join(';')}`);
|
||||
}
|
||||
|
||||
|
||||
if (otherTabErrors.length > 0) {
|
||||
const tabName = needSwitchTab === 'registration' ? '报名设置' : '基本信息';
|
||||
errorMessages.push(`【${tabName}页面】${otherTabErrors.join(';')}`);
|
||||
}
|
||||
|
||||
|
||||
if (errorMessages.length > 0) {
|
||||
// 使用Notification在浏览器顶部显示提示
|
||||
ElNotification({
|
||||
@@ -1180,11 +1222,11 @@ const showValidationErrors = (invalidFields: any) => {
|
||||
position: 'top-right',
|
||||
showClose: true
|
||||
});
|
||||
|
||||
|
||||
// 如果其他标签页有错误,自动切换到第一个有错误的标签页
|
||||
if (needSwitchTab && needSwitchTab !== activeTab.value) {
|
||||
activeTab.value = needSwitchTab;
|
||||
|
||||
|
||||
// 切换标签页后延迟一下再次验证,确保用户能看到错误字段
|
||||
nextTick(() => {
|
||||
activityFormRef.value?.validate(() => {});
|
||||
@@ -1201,7 +1243,7 @@ const submitForm = () => {
|
||||
|
||||
// 准备提交的数据
|
||||
const submitData = { ...form.value } as any;
|
||||
|
||||
|
||||
console.log('提交前 - 原始表单数据:', {
|
||||
advisors: submitData.advisors,
|
||||
instructorList: submitData.instructorList,
|
||||
@@ -1234,7 +1276,7 @@ const submitForm = () => {
|
||||
if (submitData.coOrganizerList && submitData.coOrganizerList.length > 0) {
|
||||
submitData.coOrganizers = JSON.stringify(submitData.coOrganizerList);
|
||||
} else {
|
||||
submitData.coOrganizers = '[]'; // 修复:使用空JSON数组而不是undefined
|
||||
submitData.coOrganizers = '[]'; // 修复:使用空JSON数组而不是undefined
|
||||
}
|
||||
|
||||
// 处理指导老师数据
|
||||
@@ -1245,24 +1287,24 @@ const submitForm = () => {
|
||||
submitData.advisors = [];
|
||||
}
|
||||
|
||||
try {
|
||||
if (form.value.id) {
|
||||
await updateStglActivity(submitData);
|
||||
} else {
|
||||
await addStglActivity(submitData);
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
|
||||
// 提交成功后重置表单
|
||||
reset();
|
||||
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
try {
|
||||
if (form.value.id) {
|
||||
await updateStglActivity(submitData);
|
||||
} else {
|
||||
await addStglActivity(submitData);
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
|
||||
// 提交成功后重置表单
|
||||
reset();
|
||||
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
} else {
|
||||
// 验证失败,显示必填项提示
|
||||
showValidationErrors(invalidFields);
|
||||
@@ -1459,7 +1501,7 @@ const handleRelatedClubChange = (club: ClubOption | null) => {
|
||||
form.value.relatedClub = club;
|
||||
form.value.relatedClubId = club?.relatedClubId;
|
||||
form.value.relatedType = club?.relatedType; // 修复:同时设置关联类型
|
||||
|
||||
|
||||
console.log('活动表单 - 更新后的表单数据:', {
|
||||
relatedClub: form.value.relatedClub,
|
||||
relatedClubId: form.value.relatedClubId,
|
||||
@@ -1473,8 +1515,6 @@ const handleQueryRelatedClubChange = (club: ClubOption | null) => {
|
||||
queryParams.value.relatedClubId = club?.relatedClubId;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 承办方变化处理 */
|
||||
const handleCoOrganizersChange = (coOrganizers: CoOrganizerInfo[]) => {
|
||||
form.value.coOrganizerList = coOrganizers;
|
||||
@@ -1525,14 +1565,14 @@ const handleSubmit = async (row?: StglActivityVO) => {
|
||||
|
||||
try {
|
||||
await proxy?.$modal.confirm(`是否确认提交活动"${activity.activityName}"?提交后将进入审批流程。`);
|
||||
|
||||
|
||||
// 调用后端接口更新活动状态为2(待审批)
|
||||
// 传递完整的活动数据,避免后端验证失败
|
||||
await updateStglActivity({
|
||||
...activity,
|
||||
activityStatus: '2'
|
||||
});
|
||||
|
||||
|
||||
proxy?.$modal.msgSuccess('提交成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
@@ -1564,14 +1604,14 @@ const handleCancel = async (row?: StglActivityVO) => {
|
||||
|
||||
try {
|
||||
await proxy?.$modal.confirm(`是否确认取消活动"${activity.activityName}"?取消后将无法恢复。`);
|
||||
|
||||
|
||||
// 调用后端接口更新活动状态为6(已取消)
|
||||
// 传递完整的活动数据,避免后端验证失败
|
||||
await updateStglActivity({
|
||||
...activity,
|
||||
activityStatus: '6'
|
||||
});
|
||||
|
||||
|
||||
proxy?.$modal.msgSuccess('取消活动成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
@@ -1588,7 +1628,7 @@ const confirmApprove = async () => {
|
||||
|
||||
approveLoading.value = true;
|
||||
try {
|
||||
const activity = activityList.value.find(item => item.id === approveForm.id);
|
||||
const activity = activityList.value.find((item) => item.id === approveForm.id);
|
||||
if (!activity) {
|
||||
proxy?.$modal.msgError('活动信息不存在');
|
||||
return;
|
||||
@@ -1677,7 +1717,7 @@ const hasInstructorPermission = computed(() => {
|
||||
// 检查当前用户是否为指定活动的指导老师
|
||||
const isCurrentUserAdvisor = (activity: StglActivityVO): boolean => {
|
||||
if (!activity.instructors) return false;
|
||||
|
||||
|
||||
try {
|
||||
const advisors = JSON.parse(activity.instructors);
|
||||
if (Array.isArray(advisors)) {
|
||||
@@ -1690,7 +1730,7 @@ const isCurrentUserAdvisor = (activity: StglActivityVO): boolean => {
|
||||
} catch (error) {
|
||||
console.warn('解析活动指导老师数据失败:', error);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1700,22 +1740,22 @@ const hasActivityOperationPermission = (activity: StglActivityVO): boolean => {
|
||||
if (proxy?.$auth.hasRole('admin') || proxy?.$auth.hasRole('common')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 指导老师只能操作自己指导的活动
|
||||
if (proxy?.$auth.hasRole('stgl_instructor')) {
|
||||
return isCurrentUserAdvisor(activity);
|
||||
}
|
||||
|
||||
|
||||
// 社长只能操作自己创建的活动
|
||||
if (proxy?.$auth.hasRole('stgl_president')) {
|
||||
return activity.createBy === userStore.userId;
|
||||
}
|
||||
|
||||
|
||||
// 志愿者队长只能操作自己创建的活动
|
||||
if (proxy?.$auth.hasRole('stgl_volunteer_captain')) {
|
||||
return activity.createBy === userStore.userId;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1844,6 +1884,4 @@ onMounted(() => {
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -43,7 +43,6 @@
|
||||
新增
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:clubmember:edit']"
|
||||
@@ -421,7 +420,7 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
const isAddButtonDisabled = computed(() => {
|
||||
// 使用本地成员数量,确保实时更新
|
||||
const currentCount = memberList.value.length;
|
||||
|
||||
|
||||
if (!props.memberLimit || props.memberLimit <= 0) {
|
||||
return false; // 没有设置上限,允许新增
|
||||
}
|
||||
@@ -431,7 +430,7 @@ const isAddButtonDisabled = computed(() => {
|
||||
// 计算属性:新增按钮的提示信息
|
||||
const getAddButtonTooltip = computed(() => {
|
||||
const currentCount = memberList.value.length;
|
||||
|
||||
|
||||
if (!props.memberLimit || props.memberLimit <= 0) {
|
||||
return '该社团未设置成员上限';
|
||||
}
|
||||
@@ -441,7 +440,7 @@ const getAddButtonTooltip = computed(() => {
|
||||
// 计算属性:成员数量标签的类型
|
||||
const getMemberCountTagType = computed(() => {
|
||||
const currentCount = memberList.value.length;
|
||||
|
||||
|
||||
if (!props.memberLimit || props.memberLimit <= 0) {
|
||||
return 'info'; // 无上限时显示信息标签
|
||||
}
|
||||
@@ -812,7 +811,7 @@ defineExpose({
|
||||
|
||||
// 定义事件,用于通知父组件数据变化
|
||||
const emit = defineEmits<{
|
||||
memberCountChanged: [newCount: number]
|
||||
memberCountChanged: [newCount: number];
|
||||
}>();
|
||||
|
||||
// 通知父组件成员数量变化
|
||||
@@ -838,6 +837,4 @@ const notifyMemberCountChange = () => {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -3,27 +3,22 @@
|
||||
<el-tabs v-model="activeTab" type="border-card" :key="clubId" @tab-change="handleTabChange">
|
||||
<!-- 已加入标签页 -->
|
||||
<el-tab-pane label="已加入" name="joined">
|
||||
<JoinedMembersTab
|
||||
<JoinedMembersTab
|
||||
ref="joinedTabRef"
|
||||
:club-id="clubId"
|
||||
:club-name="clubName"
|
||||
:club-id="clubId"
|
||||
:club-name="clubName"
|
||||
:member-limit="memberLimit"
|
||||
:current-member-count="currentMemberCount"
|
||||
:key="`joined-${clubId}`"
|
||||
:key="`joined-${clubId}`"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
|
||||
<!-- 待审核标签页 -->
|
||||
<el-tab-pane label="待审核" name="pending">
|
||||
<PendingMembersTab
|
||||
ref="pendingTabRef"
|
||||
:club-id="clubId"
|
||||
:club-name="clubName"
|
||||
:key="`pending-${clubId}`"
|
||||
/>
|
||||
<PendingMembersTab ref="pendingTabRef" :club-id="clubId" :club-name="clubName" :key="`pending-${clubId}`" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">关 闭</el-button>
|
||||
@@ -58,27 +53,33 @@ const joinedTabRef = ref();
|
||||
const pendingTabRef = ref();
|
||||
|
||||
// 监听visible变化
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
activeTab.value = 'joined';
|
||||
// 当弹窗打开时,触发当前标签页的数据加载
|
||||
nextTick(() => {
|
||||
if (activeTab.value === 'joined' && joinedTabRef.value) {
|
||||
joinedTabRef.value.getList();
|
||||
} else if (activeTab.value === 'pending' && pendingTabRef.value) {
|
||||
pendingTabRef.value.getList();
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
activeTab.value = 'joined';
|
||||
// 当弹窗打开时,触发当前标签页的数据加载
|
||||
nextTick(() => {
|
||||
if (activeTab.value === 'joined' && joinedTabRef.value) {
|
||||
joinedTabRef.value.getList();
|
||||
} else if (activeTab.value === 'pending' && pendingTabRef.value) {
|
||||
pendingTabRef.value.getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// 监听clubId变化,重置标签页
|
||||
watch(() => props.clubId, (newClubId) => {
|
||||
console.log('MemberManagementDialog: clubId 变化:', newClubId);
|
||||
if (newClubId) {
|
||||
activeTab.value = 'joined';
|
||||
watch(
|
||||
() => props.clubId,
|
||||
(newClubId) => {
|
||||
console.log('MemberManagementDialog: clubId 变化:', newClubId);
|
||||
if (newClubId) {
|
||||
activeTab.value = 'joined';
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:visible', false);
|
||||
@@ -91,7 +92,7 @@ const handleVisibleChange = (value: boolean) => {
|
||||
/** 标签页切换处理 */
|
||||
const handleTabChange = (tabName: string) => {
|
||||
console.log('标签页切换到:', tabName);
|
||||
|
||||
|
||||
// 根据当前激活的标签页,调用对应组件的刷新方法
|
||||
if (tabName === 'joined' && joinedTabRef.value) {
|
||||
// 调用已加入成员Tab的刷新方法
|
||||
@@ -111,4 +112,4 @@ const handleTabChange = (tabName: string) => {
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -3,26 +3,26 @@
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="always" style="padding: 8px 16px;">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0;">
|
||||
<el-card shadow="always" style="padding: 8px 16px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
|
||||
<el-row :gutter="16" align="middle">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0;">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0;">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="联系方式" prop="params.phone" style="margin-bottom: 0;">
|
||||
<el-form-item label="联系方式" prop="params.phone" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.params.phone" placeholder="请输入联系方式" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -38,13 +38,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="Check"
|
||||
:disabled="multiple"
|
||||
@click="handleBatchApprove()"
|
||||
v-hasPermi="['stgl:clubmember:approve']"
|
||||
<el-button type="info" plain icon="Check" :disabled="multiple" @click="handleBatchApprove()" v-hasPermi="['stgl:clubmember:approve']"
|
||||
>批量审核</el-button
|
||||
>
|
||||
</el-col>
|
||||
@@ -280,9 +274,9 @@ const getList = async () => {
|
||||
try {
|
||||
// 构建查询参数,支持多状态查询
|
||||
const queryData = { ...queryParams.value };
|
||||
|
||||
|
||||
console.log('PendingMembersTab: getList 开始查询,参数:', queryData);
|
||||
|
||||
|
||||
// 待审核页面:查询待审核(status=0)和已驳回(status=3)的成员
|
||||
// 字典配置:0-待审核,1-审核中,2-已加入,3-已驳回,4-已退出
|
||||
if (!queryData.status) {
|
||||
@@ -290,19 +284,19 @@ const getList = async () => {
|
||||
queryData.params = { ...queryData.params, statusList: ['0', '3'] } as any;
|
||||
delete queryData.status; // 删除单个status字段
|
||||
}
|
||||
|
||||
|
||||
// 确保phone参数正确传递到params中
|
||||
if (queryData.params.phone) {
|
||||
// 保持phone参数在params中,后端会从params中读取
|
||||
console.log('PendingMembersTab: phone参数:', queryData.params.phone);
|
||||
}
|
||||
|
||||
|
||||
console.log('PendingMembersTab: 最终查询参数:', queryData);
|
||||
console.log('PendingMembersTab: phone参数值:', queryData.params?.phone);
|
||||
|
||||
|
||||
const res = await listStglMember(queryData);
|
||||
console.log('PendingMembersTab: 查询结果:', res);
|
||||
|
||||
|
||||
memberList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
@@ -363,25 +357,25 @@ const validateMemberData = (member: StglMemberVO, operation: string): boolean =>
|
||||
console.error(`${operation}失败 - 用户ID缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!member.relationId && !props.clubId) {
|
||||
proxy?.$modal.msgError(`${operation}失败:关联ID(社团ID)不能为空`);
|
||||
console.error(`${operation}失败 - 关联ID缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!member.memberType) {
|
||||
proxy?.$modal.msgError(`${operation}失败:成员类型不能为空`);
|
||||
console.error(`${operation}失败 - 成员类型缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!member.studentNo || !member.studentName) {
|
||||
proxy?.$modal.msgError(`${operation}失败:学号和姓名不能为空`);
|
||||
console.error(`${operation}失败 - 基础信息缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -391,7 +385,7 @@ const handleApprove = (row: StglMemberVO) => {
|
||||
if (!validateMemberData(row, '审核')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
approvalForm.id = row.id;
|
||||
approvalForm.approvalStatus = undefined;
|
||||
approvalForm.approvalRemark = '';
|
||||
@@ -408,11 +402,11 @@ const handleBatchApprove = () => {
|
||||
|
||||
// 验证所有选中成员的数据完整性
|
||||
const invalidMembers = memberList.value
|
||||
.filter(member => ids.value.includes(member.id))
|
||||
.filter(member => !validateMemberData(member, '批量审核'));
|
||||
|
||||
.filter((member) => ids.value.includes(member.id))
|
||||
.filter((member) => !validateMemberData(member, '批量审核'));
|
||||
|
||||
if (invalidMembers.length > 0) {
|
||||
const invalidNames = invalidMembers.map(m => m.studentName || '未知成员').join('、');
|
||||
const invalidNames = invalidMembers.map((m) => m.studentName || '未知成员').join('、');
|
||||
proxy.$modal.msgError(`以下成员数据不完整,无法审核:${invalidNames}`);
|
||||
return;
|
||||
}
|
||||
@@ -432,41 +426,43 @@ const submitApproval = () => {
|
||||
if (valid) {
|
||||
if (approvalForm.id) {
|
||||
// 单个审核 - 再次验证数据完整性
|
||||
const member = memberList.value.find(m => m.id === approvalForm.id);
|
||||
const member = memberList.value.find((m) => m.id === approvalForm.id);
|
||||
if (member && !validateMemberData(member, '审核')) {
|
||||
return;
|
||||
}
|
||||
|
||||
approveMember(approvalForm.id, approvalForm.approvalStatus === 2).then(() => {
|
||||
proxy.$modal.msgSuccess('审核成功');
|
||||
approvalOpen.value = false;
|
||||
getList();
|
||||
}).catch((error) => {
|
||||
console.error('审核失败:', error);
|
||||
proxy.$modal.msgError('审核失败,请检查数据完整性');
|
||||
});
|
||||
|
||||
approveMember(approvalForm.id, approvalForm.approvalStatus === 2)
|
||||
.then(() => {
|
||||
proxy.$modal.msgSuccess('审核成功');
|
||||
approvalOpen.value = false;
|
||||
getList();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('审核失败:', error);
|
||||
proxy.$modal.msgError('审核失败,请检查数据完整性');
|
||||
});
|
||||
} else {
|
||||
// 批量审核 - 再次验证数据完整性
|
||||
const selectedMembers = memberList.value.filter(m => ids.value.includes(m.id));
|
||||
const invalidMembers = selectedMembers.filter(member => !validateMemberData(member, '批量审核'));
|
||||
|
||||
const selectedMembers = memberList.value.filter((m) => ids.value.includes(m.id));
|
||||
const invalidMembers = selectedMembers.filter((member) => !validateMemberData(member, '批量审核'));
|
||||
|
||||
if (invalidMembers.length > 0) {
|
||||
const invalidNames = invalidMembers.map(m => m.studentName || '未知成员').join('、');
|
||||
const invalidNames = invalidMembers.map((m) => m.studentName || '未知成员').join('、');
|
||||
proxy.$modal.msgError(`以下成员数据不完整,无法审核:${invalidNames}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const promises = ids.value.map(id =>
|
||||
approveMember(id, approvalForm.approvalStatus === 2)
|
||||
);
|
||||
Promise.all(promises).then(() => {
|
||||
proxy.$modal.msgSuccess('批量审核成功');
|
||||
approvalOpen.value = false;
|
||||
getList();
|
||||
}).catch((error) => {
|
||||
console.error('批量审核失败:', error);
|
||||
proxy.$modal.msgError('批量审核失败,请检查数据完整性');
|
||||
});
|
||||
|
||||
const promises = ids.value.map((id) => approveMember(id, approvalForm.approvalStatus === 2));
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
proxy.$modal.msgSuccess('批量审核成功');
|
||||
approvalOpen.value = false;
|
||||
getList();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('批量审核失败:', error);
|
||||
proxy.$modal.msgError('批量审核失败,请检查数据完整性');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -519,4 +515,4 @@ defineExpose({
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -26,8 +26,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item>
|
||||
@@ -238,7 +236,14 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成员规模上限" prop="memberLimit" required>
|
||||
<el-input-number v-model="form.memberLimit" :min="1" :max="9999" placeholder="请输入成员规模上限" style="width: 100%" :disabled="!hasInstructorPermission" />
|
||||
<el-input-number
|
||||
v-model="form.memberLimit"
|
||||
:min="1"
|
||||
:max="9999"
|
||||
placeholder="请输入成员规模上限"
|
||||
style="width: 100%"
|
||||
:disabled="!hasInstructorPermission"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -307,8 +312,6 @@
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
@@ -439,8 +442,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="position-group">
|
||||
<div class="position-title">指导老师:</div>
|
||||
@@ -623,12 +624,12 @@
|
||||
<script setup name="StglClub" lang="ts">
|
||||
/**
|
||||
* 社团信息管理
|
||||
*
|
||||
*
|
||||
* 权限控制说明:
|
||||
* - stgl_president角色:可以操作自己担任社长的社团(无论是否为创建者)
|
||||
* - stgl_instructor角色:指导老师,有部分字段的只读权限
|
||||
* - 管理员角色:可以操作所有社团
|
||||
*
|
||||
*
|
||||
* 业务逻辑:
|
||||
* 管理员创建社团后选择社长,被选中的社长(stgl_president角色)
|
||||
* 需要能够看到和操作自己担任社长的社团,不再受限于创建者身份
|
||||
@@ -663,8 +664,6 @@ const hasInstructorPermission = computed(() => {
|
||||
return proxy?.$auth.hasRoleOr(['stgl_instructor', 'stgl_stgl_admin', 'superadmin']);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 社长权限控制 - 检查用户是否为各社团的社长(无论是否为创建者)
|
||||
// 用于控制转让按钮显示和社长相关操作权限
|
||||
const presidentRoles = ref<Record<string | number, boolean>>({});
|
||||
@@ -974,8 +973,6 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
vicePresidentPhone: res.data.vicePresidentPhone
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
// 🔥 优先从JSON字段读取指导老师数据
|
||||
if (res.data.advisorsJson) {
|
||||
@@ -1040,8 +1037,6 @@ const submitForm = () => {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 🔥 新增:同时保存JSON字段和兼容性字段
|
||||
submitData.advisorsJson = JSON.stringify(submitData.advisors || []);
|
||||
|
||||
@@ -1109,8 +1104,6 @@ const handleView = async (row: StglClubVO) => {
|
||||
(viewData as any).sighOpen = String(viewData.sighOpen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 🔥 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
console.log('handleView: 指导老师原始数据:', {
|
||||
advisorsJson: viewData.advisorsJson,
|
||||
@@ -1263,8 +1256,6 @@ const handlePresidentChange = (student: StudentVO | null) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 防止递归更新的锁
|
||||
const syncLock = ref(false);
|
||||
|
||||
@@ -1486,9 +1477,9 @@ const handleMemberCountChanged = (newCount: number) => {
|
||||
if (currentClub.value) {
|
||||
// 更新当前社团的成员数量
|
||||
currentClub.value.memberCount = newCount;
|
||||
|
||||
|
||||
// 同时更新列表中的对应社团数据
|
||||
const clubIndex = clubList.value.findIndex(club => club.id === currentClub.value?.id);
|
||||
const clubIndex = clubList.value.findIndex((club) => club.id === currentClub.value?.id);
|
||||
if (clubIndex !== -1) {
|
||||
clubList.value[clubIndex].memberCount = newCount;
|
||||
console.log('handleMemberCountChanged: 已更新列表中的成员数量:', clubList.value[clubIndex]);
|
||||
@@ -1552,8 +1543,6 @@ onMounted(() => {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.member-count {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
@@ -1706,8 +1695,6 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 人员信息部分样式 */
|
||||
.personnel-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
@@ -12,19 +12,9 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="活动类型" prop="activityType">
|
||||
<el-select
|
||||
v-model="queryParams.activityType"
|
||||
placeholder="请选择活动类型"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-select v-model="queryParams.activityType" placeholder="请选择活动类型" clearable style="width: 100%">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option
|
||||
v-for="dict in stgl_activity_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
<el-option v-for="dict in stgl_activity_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -44,7 +34,7 @@
|
||||
</el-form-item>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item>
|
||||
@@ -106,7 +96,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="报名状态" align="center" prop="myRegistrationStatus" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
<el-tag
|
||||
:type="scope.row.myRegistrationStatus === '1' ? 'success' : scope.row.myRegistrationStatus === '2' ? 'danger' : 'warning'"
|
||||
size="small"
|
||||
>
|
||||
@@ -217,7 +207,9 @@
|
||||
<span class="rating-text">{{ detailForm.myRatingScore }}</span>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="评分时间" v-if="detailForm.myRatingTime">{{ parseTime(detailForm.myRatingTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="评分时间" v-if="detailForm.myRatingTime">{{
|
||||
parseTime(detailForm.myRatingTime, '{y}-{m}-{d} {h}:{i}')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="活动平均评分" v-if="detailForm.averageRating">
|
||||
<div class="rating-cell">
|
||||
<el-rate :model-value="detailForm.averageRating" disabled size="small" />
|
||||
@@ -305,10 +297,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="身份" align="center" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="row.isLeader === 1 ? 'success' : 'info'"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="row.isLeader === 1 ? 'success' : 'info'" size="small">
|
||||
{{ row.isLeader === 1 ? '队长' : '队员' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -321,14 +310,12 @@
|
||||
</div>
|
||||
<div v-else class="no-team-info">
|
||||
<el-empty description="未找到队伍信息" :image-size="60">
|
||||
<el-button type="primary" size="small" @click="getTeamInfo(detailForm.id)">
|
||||
重新获取
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" @click="getTeamInfo(detailForm.id)"> 重新获取 </el-button>
|
||||
</el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="detailOpen = false">关 闭</el-button>
|
||||
@@ -382,10 +369,10 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 签到对话框 -->
|
||||
<el-dialog
|
||||
title="活动签到"
|
||||
v-model="attendanceOpen"
|
||||
width="560px"
|
||||
<el-dialog
|
||||
title="活动签到"
|
||||
v-model="attendanceOpen"
|
||||
width="560px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="true"
|
||||
@@ -393,25 +380,11 @@
|
||||
>
|
||||
<div class="attendance-content">
|
||||
<div class="attendance-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="signInLoading"
|
||||
:disabled="!canSignIn"
|
||||
@click="handleSignIn"
|
||||
class="action-btn"
|
||||
>
|
||||
<el-button type="primary" size="large" :loading="signInLoading" :disabled="!canSignIn" @click="handleSignIn" class="action-btn">
|
||||
{{ signInButtonText }}
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
type="warning"
|
||||
size="large"
|
||||
:loading="signOutLoading"
|
||||
:disabled="!canSignOut"
|
||||
@click="handleSignOut"
|
||||
class="action-btn"
|
||||
>
|
||||
|
||||
<el-button type="warning" size="large" :loading="signOutLoading" :disabled="!canSignOut" @click="handleSignOut" class="action-btn">
|
||||
签 退
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -420,24 +393,10 @@
|
||||
<div class="attendance-records" v-if="attendanceRecords.length > 0">
|
||||
<div class="records-header">
|
||||
<span class="records-title">签到记录</span>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="refreshRecords"
|
||||
:loading="recordsLoading"
|
||||
>
|
||||
刷新
|
||||
</el-button>
|
||||
<el-button type="text" size="small" @click="refreshRecords" :loading="recordsLoading"> 刷新 </el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="attendanceRecords"
|
||||
stripe
|
||||
size="small"
|
||||
class="records-table"
|
||||
v-loading="recordsLoading"
|
||||
:max-height="getTableMaxHeight()"
|
||||
>
|
||||
|
||||
<el-table :data="attendanceRecords" stripe size="small" class="records-table" v-loading="recordsLoading" :max-height="getTableMaxHeight()">
|
||||
<el-table-column prop="signInTime" label="签到时间" width="200">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.signInTime) }}
|
||||
@@ -451,10 +410,7 @@
|
||||
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="row.signOutTime ? 'success' : 'warning'"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="row.signOutTime ? 'success' : 'warning'" size="small">
|
||||
{{ row.signOutTime ? '已完成' : '进行中' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -549,23 +505,23 @@ const getList = async () => {
|
||||
const params = Object.fromEntries(
|
||||
Object.entries(queryParams.value).filter(([_, value]) => value !== '' && value !== undefined && value !== null)
|
||||
);
|
||||
|
||||
|
||||
// 直接使用getMyActivities接口,后端已经处理了所有情况(包括用户作为队员参与的活动)
|
||||
const res = await getMyActivities(params);
|
||||
const activities = res.data || [];
|
||||
|
||||
|
||||
// 按报名时间排序
|
||||
activities.sort((a, b) => {
|
||||
const timeA = a.myRegistrationTime ? new Date(a.myRegistrationTime).getTime() : 0;
|
||||
const timeB = b.myRegistrationTime ? new Date(b.myRegistrationTime).getTime() : 0;
|
||||
return timeB - timeA; // 降序排列
|
||||
});
|
||||
|
||||
|
||||
activityList.value = activities;
|
||||
|
||||
|
||||
console.log('我的活动列表数据:', {
|
||||
totalCount: activities.length,
|
||||
activities: activities.map(activity => ({
|
||||
activities: activities.map((activity) => ({
|
||||
id: activity.id,
|
||||
activityName: activity.activityName,
|
||||
registrationType: activity.registrationType,
|
||||
@@ -580,8 +536,6 @@ const getList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
@@ -597,7 +551,7 @@ const resetQuery = () => {
|
||||
const handleView = async (row: MyActivityVO) => {
|
||||
detailForm.value = { ...row };
|
||||
detailOpen.value = true;
|
||||
|
||||
|
||||
// 如果是团队报名,获取队伍信息
|
||||
if (row.registrationType === '2') {
|
||||
await getTeamInfo(row.id);
|
||||
@@ -609,7 +563,7 @@ const handleView = async (row: MyActivityVO) => {
|
||||
/** 获取队伍信息 */
|
||||
const getTeamInfo = async (activityId: string | number) => {
|
||||
if (!activityId) return;
|
||||
|
||||
|
||||
teamInfoLoading.value = true;
|
||||
try {
|
||||
const res = await getTeamsByActivityId(activityId);
|
||||
@@ -621,7 +575,7 @@ const getTeamInfo = async (activityId: string | number) => {
|
||||
teamInfo.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const currentUserTeam = res.data.find((team: StglActivityTeamInfoVo) => {
|
||||
// 检查是否为队长
|
||||
if (team.leaderStudentNo === userInfo.userName) {
|
||||
@@ -639,9 +593,9 @@ const getTeamInfo = async (activityId: string | number) => {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
teamInfo.value = currentUserTeam || null;
|
||||
|
||||
|
||||
if (currentUserTeam) {
|
||||
console.log('获取到队伍信息:', {
|
||||
teamName: currentUserTeam.teamName,
|
||||
@@ -681,14 +635,12 @@ const parseTeamMembers = (teamMembersStr: string): any[] => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 活动评分按钮操作 */
|
||||
const handleRate = async (row: MyActivityVO) => {
|
||||
try {
|
||||
// 获取当前用户详细信息
|
||||
const userInfo = await getCurrentUserDetailInfo();
|
||||
|
||||
|
||||
ratingForm.value = {
|
||||
activityId: row.id,
|
||||
activityName: row.activityName,
|
||||
@@ -699,7 +651,7 @@ const handleRate = async (row: MyActivityVO) => {
|
||||
experienceScore: 0,
|
||||
ratingComment: ''
|
||||
};
|
||||
|
||||
|
||||
console.log('评分表单数据:', ratingForm.value);
|
||||
ratingOpen.value = true;
|
||||
} catch (error) {
|
||||
@@ -711,7 +663,7 @@ const handleRate = async (row: MyActivityVO) => {
|
||||
/** 提交评分 */
|
||||
const submitRating = async () => {
|
||||
if (!ratingFormRef.value) return;
|
||||
|
||||
|
||||
await ratingFormRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
ratingLoading.value = true;
|
||||
@@ -821,8 +773,8 @@ const attendanceHistory = ref<any[]>([]);
|
||||
const currentAttendance = ref<any>(null);
|
||||
|
||||
// 签到记录相关数据
|
||||
const attendanceRecords = ref<any[]>([])
|
||||
const recordsLoading = ref(false)
|
||||
const attendanceRecords = ref<any[]>([]);
|
||||
const recordsLoading = ref(false);
|
||||
|
||||
// 计算属性 - 简化状态判断
|
||||
const canSignIn = computed(() => {
|
||||
@@ -830,37 +782,35 @@ const canSignIn = computed(() => {
|
||||
if (!currentAttendance.value) {
|
||||
return true; // 没有记录时可以签到
|
||||
}
|
||||
|
||||
|
||||
// 如果最新记录已签到已签退,则可以重新签到
|
||||
if (currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 如果最新记录已签到未签退,则不能签到
|
||||
if (currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 其他情况允许签到
|
||||
return true;
|
||||
});
|
||||
|
||||
const canSignOut = computed(() => {
|
||||
// 如果有记录且已签到未签退,则可以签退
|
||||
return currentAttendance.value &&
|
||||
currentAttendance.value.signInStatus === 1 &&
|
||||
currentAttendance.value.signOutStatus === 0;
|
||||
return currentAttendance.value && currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 0;
|
||||
});
|
||||
|
||||
const signInButtonText = computed(() => {
|
||||
if (!currentAttendance.value) {
|
||||
return '签到';
|
||||
}
|
||||
|
||||
|
||||
if (currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 0) {
|
||||
return '已签到';
|
||||
}
|
||||
|
||||
|
||||
return '签到';
|
||||
});
|
||||
|
||||
@@ -868,7 +818,7 @@ const signInButtonText = computed(() => {
|
||||
const handleAttendance = async (row: MyActivityVO) => {
|
||||
try {
|
||||
const userInfo = await getCurrentUserDetailInfo();
|
||||
|
||||
|
||||
// 填充活动信息
|
||||
attendanceForm.value = {
|
||||
activityId: row.id,
|
||||
@@ -929,13 +879,13 @@ const handleSignIn = async () => {
|
||||
};
|
||||
|
||||
await createSignInRecord(signInData);
|
||||
|
||||
|
||||
// 签到成功消息
|
||||
proxy?.$modal.msgSuccess('签到成功');
|
||||
|
||||
// 刷新签到状态和历史记录
|
||||
await refreshAttendanceData();
|
||||
|
||||
|
||||
// 强制更新计算属性
|
||||
nextTick(() => {
|
||||
console.log('签到后状态更新:', {
|
||||
@@ -980,7 +930,7 @@ const handleSignOut = async () => {
|
||||
|
||||
// 刷新签到状态和历史记录
|
||||
await refreshAttendanceData();
|
||||
|
||||
|
||||
// 强制更新计算属性
|
||||
nextTick(() => {
|
||||
console.log('签退后状态更新:', {
|
||||
@@ -1003,19 +953,17 @@ const refreshAttendanceData = async () => {
|
||||
try {
|
||||
// 获取最新的签到记录
|
||||
const recordRes = await getAllAttendanceRecords(attendanceForm.value.activityId, attendanceForm.value.studentNo);
|
||||
|
||||
|
||||
if (recordRes.data && recordRes.data.length > 0) {
|
||||
// 按创建时间排序,取最新的一条作为当前记录
|
||||
const sortedRecords = recordRes.data.sort((a: any, b: any) =>
|
||||
new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
|
||||
);
|
||||
|
||||
const sortedRecords = recordRes.data.sort((a: any, b: any) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime());
|
||||
|
||||
currentAttendance.value = sortedRecords[0];
|
||||
attendanceHistory.value = sortedRecords;
|
||||
|
||||
|
||||
// 同时更新签到记录表格数据
|
||||
attendanceRecords.value = sortedRecords;
|
||||
|
||||
|
||||
console.log('获取到签到记录:', {
|
||||
total: sortedRecords.length,
|
||||
current: currentAttendance.value,
|
||||
@@ -1030,7 +978,7 @@ const refreshAttendanceData = async () => {
|
||||
|
||||
// 强制触发计算属性重新计算
|
||||
await nextTick();
|
||||
|
||||
|
||||
console.log('刷新签到数据完成:', {
|
||||
history: attendanceHistory.value,
|
||||
current: currentAttendance.value,
|
||||
@@ -1050,7 +998,7 @@ const refreshAttendanceData = async () => {
|
||||
/** 刷新签到记录 */
|
||||
const refreshRecords = async () => {
|
||||
if (!attendanceForm.value.activityId || !attendanceForm.value.studentNo) return;
|
||||
|
||||
|
||||
recordsLoading.value = true;
|
||||
try {
|
||||
await refreshAttendanceData();
|
||||
@@ -1093,14 +1041,14 @@ const getTableMaxHeight = () => {
|
||||
const calculateDuration = (signInTime: string, signOutTime: string): string => {
|
||||
if (!signInTime) return '-';
|
||||
if (!signOutTime) return '进行中';
|
||||
|
||||
|
||||
const start = new Date(signInTime);
|
||||
const end = new Date(signOutTime);
|
||||
const diffMs = end.getTime() - start.getTime();
|
||||
|
||||
|
||||
const hours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}小时${minutes}分钟`;
|
||||
} else {
|
||||
@@ -1111,9 +1059,9 @@ const calculateDuration = (signInTime: string, signOutTime: string): string => {
|
||||
/** 提取指导老师姓名 */
|
||||
const getInstructorNames = (instructors: string | any[]): string => {
|
||||
if (!instructors) return '暂无';
|
||||
|
||||
|
||||
let instructorArray: any[] = [];
|
||||
|
||||
|
||||
// 如果是字符串,尝试解析JSON
|
||||
if (typeof instructors === 'string') {
|
||||
try {
|
||||
@@ -1127,12 +1075,12 @@ const getInstructorNames = (instructors: string | any[]): string => {
|
||||
} else {
|
||||
return '暂无';
|
||||
}
|
||||
|
||||
|
||||
if (!instructorArray || instructorArray.length === 0) return '暂无';
|
||||
|
||||
|
||||
return instructorArray
|
||||
.map(instructor => instructor.name || instructor.nickName || '未知')
|
||||
.filter(name => name && name !== '未知')
|
||||
.map((instructor) => instructor.name || instructor.nickName || '未知')
|
||||
.filter((name) => name && name !== '未知')
|
||||
.join('、');
|
||||
};
|
||||
|
||||
@@ -1423,4 +1371,4 @@ onMounted(() => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -17,16 +17,9 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable style="width: 100%" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -228,8 +221,6 @@ const getRegistrationTypeTag = (type: string) => {
|
||||
return tagMap[type as keyof typeof tagMap] || ('info' as const);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 获取当前状态标签样式 */
|
||||
const getCurrentStatusTag = (status: string) => {
|
||||
const tagMap = {
|
||||
|
||||
@@ -202,9 +202,7 @@ import {
|
||||
import { StglClassQuery, StglClassVO, StglClassDetailVO, ClubDetailVO, VolunteerTeamDetailVO, ActivityDetailVO } from '@/api/stgl/stclass/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_user_sex, stgl_club_type, stgl_activity_type } = toRefs<any>(
|
||||
proxy?.useDict('sys_user_sex', 'stgl_club_type', 'stgl_activity_type')
|
||||
);
|
||||
const { sys_user_sex, stgl_club_type, stgl_activity_type } = toRefs<any>(proxy?.useDict('sys_user_sex', 'stgl_club_type', 'stgl_activity_type'));
|
||||
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
@@ -308,7 +306,7 @@ const handleClassChange = () => {
|
||||
|
||||
/** 查看按钮操作 */
|
||||
const handleView = (row?: StglClassVO) => {
|
||||
const _row = row || studentList.value.find(item => item.id === ids.value[0]);
|
||||
const _row = row || studentList.value.find((item) => item.id === ids.value[0]);
|
||||
if (!_row) {
|
||||
proxy?.$modal.msgError('请选择要查看的学生');
|
||||
return;
|
||||
@@ -364,11 +362,7 @@ const handleViewActivities = async (row: StglClassVO) => {
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'stgl/stclass/export',
|
||||
queryParams.value,
|
||||
`班级学生参与情况_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
proxy?.download('stgl/stclass/export', queryParams.value, `班级学生参与情况_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
@@ -452,4 +446,4 @@ onMounted(() => {
|
||||
.activity-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -3,26 +3,26 @@
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="always" style="padding: 8px 16px;">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0;">
|
||||
<el-card shadow="always" style="padding: 8px 16px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
|
||||
<el-row :gutter="16" align="middle">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0;">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0;">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0;">
|
||||
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.phone" placeholder="请输入联系方式" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -39,16 +39,27 @@
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-tooltip :content="getAddButtonTooltip" placement="top" :disabled="!isAddButtonDisabled">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" :disabled="isAddButtonDisabled" v-hasPermi="['stgl:volunteermember:add']">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
:disabled="isAddButtonDisabled"
|
||||
v-hasPermi="['stgl:volunteermember:add']"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:volunteermember:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:volunteermember:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:volunteermember:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:volunteermember:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown class="mt-[1px]">
|
||||
@@ -259,7 +270,7 @@
|
||||
是否更新已经存在的志愿者成员数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
@@ -308,7 +319,9 @@ interface ExtendedVolunteerMemberForm extends VolunteerMemberForm {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_user_sex, sys_normal_disable, stgl_member_status } = toRefs<any>(proxy?.useDict('sys_user_sex', 'sys_normal_disable', 'stgl_member_status'));
|
||||
const { sys_user_sex, sys_normal_disable, stgl_member_status } = toRefs<any>(
|
||||
proxy?.useDict('sys_user_sex', 'sys_normal_disable', 'stgl_member_status')
|
||||
);
|
||||
|
||||
const memberList = ref<VolunteerMemberVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
@@ -401,7 +414,7 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
const isAddButtonDisabled = computed(() => {
|
||||
// 使用本地成员数量,确保实时更新
|
||||
const currentCount = memberList.value.length;
|
||||
|
||||
|
||||
if (!props.memberLimit || props.memberLimit <= 0) {
|
||||
return false; // 没有设置上限,允许新增
|
||||
}
|
||||
@@ -411,7 +424,7 @@ const isAddButtonDisabled = computed(() => {
|
||||
// 计算属性:新增按钮的提示信息
|
||||
const getAddButtonTooltip = computed(() => {
|
||||
const currentCount = memberList.value.length;
|
||||
|
||||
|
||||
if (!props.memberLimit || props.memberLimit <= 0) {
|
||||
return '该团队未设置成员上限';
|
||||
}
|
||||
@@ -442,30 +455,30 @@ const getList = async () => {
|
||||
try {
|
||||
// 构建查询参数,支持多状态查询
|
||||
const queryData = { ...queryParams.value };
|
||||
|
||||
|
||||
console.log('JoinedMembersTab: getList 开始查询,参数:', queryData);
|
||||
|
||||
|
||||
// 构建params对象,包含所有需要在params中的查询条件
|
||||
queryData.params = { ...queryData.params } as any;
|
||||
|
||||
|
||||
// 将phone等查询条件放入params中(后端期望params.phone)
|
||||
if (queryData.phone) {
|
||||
(queryData.params as any).phone = queryData.phone;
|
||||
delete queryData.phone; // 删除顶级phone字段
|
||||
}
|
||||
|
||||
|
||||
// 如果状态字段为空或未选择,则查询已加入(status=2)和已退出(status=4)的成员
|
||||
if (!queryData.status) {
|
||||
// 使用params传递多状态查询,传递字符串格式
|
||||
(queryData.params as any).statusList = '2,4';
|
||||
delete queryData.status; // 删除单个status字段
|
||||
}
|
||||
|
||||
|
||||
console.log('JoinedMembersTab: 最终查询参数:', queryData);
|
||||
|
||||
|
||||
const res = await listVolunteerMember(queryData);
|
||||
console.log('JoinedMembersTab: 查询结果:', res);
|
||||
|
||||
|
||||
memberList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
@@ -520,7 +533,7 @@ const handleSelectionChange = (selection: VolunteerMemberVO[]) => {
|
||||
const handleAdd = () => {
|
||||
// 检查人员上限 - 使用本地成员数量,确保实时更新
|
||||
const currentCount = memberList.value.length;
|
||||
|
||||
|
||||
if (props.memberLimit && props.memberLimit > 0) {
|
||||
if (currentCount >= props.memberLimit) {
|
||||
proxy?.$modal.msgError(`该团队成员已满,当前成员数量:${currentCount},成员上限:${props.memberLimit},无法再新增成员`);
|
||||
@@ -569,20 +582,20 @@ const submitForm = () => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buttonLoading.value = true;
|
||||
|
||||
|
||||
// 创建提交数据,排除selectedStudent字段,确保包含userId和memberType
|
||||
const { selectedStudent, ...submitData } = form.value;
|
||||
|
||||
|
||||
// 确保memberType为2(志愿者成员)
|
||||
submitData.memberType = 2;
|
||||
|
||||
|
||||
// 确保status为2(已加入状态)
|
||||
if (!submitData.status) {
|
||||
submitData.status = '2';
|
||||
}
|
||||
|
||||
|
||||
if (form.value.id) {
|
||||
await updateVolunteerMember(submitData as VolunteerMemberForm).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
@@ -601,14 +614,14 @@ const submitForm = () => {
|
||||
const handleDelete = async (row?: VolunteerMemberVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除选中的志愿者成员?');
|
||||
|
||||
|
||||
// 处理单个删除或批量删除
|
||||
if (Array.isArray(_ids)) {
|
||||
await delVolunteerMember(_ids.join(','));
|
||||
} else {
|
||||
await delVolunteerMember(_ids);
|
||||
}
|
||||
|
||||
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
// 通知父组件成员数量变化
|
||||
@@ -693,7 +706,7 @@ const handleFileSuccess = (response: any) => {
|
||||
getList();
|
||||
// 通知父组件成员数量变化
|
||||
notifyMemberCountChange();
|
||||
|
||||
|
||||
// 检查导入后是否超过成员上限
|
||||
setTimeout(() => {
|
||||
const currentCount = memberList.value.length;
|
||||
@@ -711,7 +724,7 @@ defineExpose({
|
||||
|
||||
// 定义事件,用于通知父组件数据变化
|
||||
const emit = defineEmits<{
|
||||
memberCountChanged: [newCount: number]
|
||||
memberCountChanged: [newCount: number];
|
||||
}>();
|
||||
|
||||
// 通知父组件成员数量变化
|
||||
@@ -741,4 +754,4 @@ onMounted(() => {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -3,28 +3,23 @@
|
||||
<el-tabs v-model="activeTab" type="border-card" :key="teamId" @tab-change="handleTabChange">
|
||||
<!-- 已加入标签页 -->
|
||||
<el-tab-pane label="已加入" name="joined">
|
||||
<JoinedMembersTab
|
||||
<JoinedMembersTab
|
||||
ref="joinedTabRef"
|
||||
:team-id="teamId"
|
||||
:team-name="teamName"
|
||||
:team-id="teamId"
|
||||
:team-name="teamName"
|
||||
:member-limit="memberLimit"
|
||||
:current-member-count="currentMemberCount"
|
||||
:key="`joined-${teamId}`"
|
||||
:key="`joined-${teamId}`"
|
||||
@member-count-changed="handleMemberCountChanged"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
|
||||
<!-- 待审核标签页 -->
|
||||
<el-tab-pane label="待审核" name="pending">
|
||||
<PendingMembersTab
|
||||
ref="pendingTabRef"
|
||||
:team-id="teamId"
|
||||
:team-name="teamName"
|
||||
:key="`pending-${teamId}`"
|
||||
/>
|
||||
<PendingMembersTab ref="pendingTabRef" :team-id="teamId" :team-name="teamName" :key="`pending-${teamId}`" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">关 闭</el-button>
|
||||
@@ -59,27 +54,33 @@ const joinedTabRef = ref();
|
||||
const pendingTabRef = ref();
|
||||
|
||||
// 监听visible变化
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
activeTab.value = 'joined';
|
||||
// 当弹窗打开时,触发当前标签页的数据加载
|
||||
nextTick(() => {
|
||||
if (activeTab.value === 'joined' && joinedTabRef.value) {
|
||||
joinedTabRef.value.getList();
|
||||
} else if (activeTab.value === 'pending' && pendingTabRef.value) {
|
||||
pendingTabRef.value.getList();
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
activeTab.value = 'joined';
|
||||
// 当弹窗打开时,触发当前标签页的数据加载
|
||||
nextTick(() => {
|
||||
if (activeTab.value === 'joined' && joinedTabRef.value) {
|
||||
joinedTabRef.value.getList();
|
||||
} else if (activeTab.value === 'pending' && pendingTabRef.value) {
|
||||
pendingTabRef.value.getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// 监听teamId变化,重置标签页
|
||||
watch(() => props.teamId, (newTeamId) => {
|
||||
console.log('MemberManagementDialog: teamId 变化:', newTeamId);
|
||||
if (newTeamId) {
|
||||
activeTab.value = 'joined';
|
||||
watch(
|
||||
() => props.teamId,
|
||||
(newTeamId) => {
|
||||
console.log('MemberManagementDialog: teamId 变化:', newTeamId);
|
||||
if (newTeamId) {
|
||||
activeTab.value = 'joined';
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:visible', false);
|
||||
@@ -98,7 +99,7 @@ const handleMemberCountChanged = (newCount: number) => {
|
||||
/** 标签页切换处理 */
|
||||
const handleTabChange = (tabName: string) => {
|
||||
console.log('标签页切换到:', tabName);
|
||||
|
||||
|
||||
// 根据当前激活的标签页,调用对应组件的刷新方法
|
||||
if (tabName === 'joined' && joinedTabRef.value) {
|
||||
// 调用已加入成员Tab的刷新方法
|
||||
@@ -118,4 +119,4 @@ const handleTabChange = (tabName: string) => {
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -3,26 +3,26 @@
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="always" style="padding: 8px 16px;">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0;">
|
||||
<el-card shadow="always" style="padding: 8px 16px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
|
||||
<el-row :gutter="16" align="middle">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0;">
|
||||
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0;">
|
||||
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0;">
|
||||
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0">
|
||||
<el-input v-model="queryParams.phone" placeholder="请输入联系方式" clearable size="small" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -38,10 +38,14 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Select" :disabled="multiple" @click="handleBatchApprove" v-hasPermi="['stgl:volunteermember:edit']">批量通过</el-button>
|
||||
<el-button type="success" plain icon="Select" :disabled="multiple" @click="handleBatchApprove" v-hasPermi="['stgl:volunteermember:edit']"
|
||||
>批量通过</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleBatchReject" v-hasPermi="['stgl:volunteermember:edit']">批量拒绝</el-button>
|
||||
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleBatchReject" v-hasPermi="['stgl:volunteermember:edit']"
|
||||
>批量拒绝</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -130,12 +134,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, onMounted, reactive, ref, toRefs } from 'vue';
|
||||
import {
|
||||
listVolunteerMember,
|
||||
getVolunteerMember,
|
||||
updateVolunteerMember,
|
||||
approveVolunteerMember
|
||||
} from '@/api/stgl/volunteermember';
|
||||
import { listVolunteerMember, getVolunteerMember, updateVolunteerMember, approveVolunteerMember } from '@/api/stgl/volunteermember';
|
||||
import { VolunteerMemberVO, VolunteerMemberQuery } from '@/api/stgl/volunteermember/types';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
|
||||
@@ -147,7 +146,9 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_user_sex, sys_normal_disable, stgl_member_status } = toRefs<any>(proxy?.useDict('sys_user_sex', 'sys_normal_disable', 'stgl_member_status'));
|
||||
const { sys_user_sex, sys_normal_disable, stgl_member_status } = toRefs<any>(
|
||||
proxy?.useDict('sys_user_sex', 'sys_normal_disable', 'stgl_member_status')
|
||||
);
|
||||
|
||||
const memberList = ref<VolunteerMemberVO[]>([]);
|
||||
const loading = ref(true);
|
||||
@@ -204,19 +205,19 @@ const validateMemberData = (member: VolunteerMemberVO, operation: string): boole
|
||||
console.error(`${operation}失败 - 用户ID缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!member.relationId && !props.teamId) {
|
||||
proxy?.$modal.msgError(`${operation}失败:关联ID(志愿者队伍ID)不能为空`);
|
||||
console.error(`${operation}失败 - 关联ID缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!member.studentNo || !member.studentName) {
|
||||
proxy?.$modal.msgError(`${operation}失败:学号和姓名不能为空`);
|
||||
console.error(`${operation}失败 - 基础信息缺失:`, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -226,31 +227,31 @@ const getList = async () => {
|
||||
try {
|
||||
// 构建查询参数,支持多状态查询
|
||||
const queryData = { ...queryParams.value };
|
||||
|
||||
|
||||
console.log('PendingMembersTab: getList 开始查询,参数:', queryData);
|
||||
|
||||
|
||||
// 构建params对象,包含所有需要在params中的查询条件
|
||||
queryData.params = { ...queryData.params } as any;
|
||||
|
||||
|
||||
// 将phone等查询条件放入params中(后端期望params.phone)
|
||||
if (queryData.phone) {
|
||||
(queryData.params as any).phone = queryData.phone;
|
||||
console.log('PendingMembersTab: phone参数放入params:', queryData.phone);
|
||||
delete queryData.phone; // 删除顶级phone字段
|
||||
}
|
||||
|
||||
|
||||
// 如果状态字段为空或未选择,则查询待审核(status=0)和已驳回(status=3)的成员
|
||||
if (!queryData.status) {
|
||||
// 使用params传递多状态查询,保留现有params内容,传递字符串格式
|
||||
(queryData.params as any).statusList = '0,3';
|
||||
delete queryData.status; // 删除单个status字段
|
||||
}
|
||||
|
||||
|
||||
console.log('PendingMembersTab: 最终查询参数:', queryData);
|
||||
|
||||
|
||||
const res = await listVolunteerMember(queryData);
|
||||
console.log('PendingMembersTab: 查询结果:', res);
|
||||
|
||||
|
||||
// 防御性编程:检查响应数据结构
|
||||
if (!res || !res.rows) {
|
||||
console.warn('PendingMembersTab: API返回数据格式异常:', res);
|
||||
@@ -259,7 +260,7 @@ const getList = async () => {
|
||||
} else {
|
||||
memberList.value = res.rows;
|
||||
total.value = res.total;
|
||||
|
||||
|
||||
// 调试:检查数据完整性
|
||||
if (memberList.value.length > 0) {
|
||||
console.log('PendingMembersTab: 第一条成员数据:', memberList.value[0]);
|
||||
@@ -322,9 +323,9 @@ const handleApprove = async (row: VolunteerMemberVO) => {
|
||||
if (!validateMemberData(row, '审核通过')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await proxy?.$modal.confirm(`确认通过"${row.studentName}"的入队申请?`);
|
||||
|
||||
|
||||
await approveVolunteerMember(row.id, true);
|
||||
proxy?.$modal.msgSuccess('审核通过成功');
|
||||
await getList();
|
||||
@@ -336,9 +337,9 @@ const handleReject = async (row: VolunteerMemberVO) => {
|
||||
if (!validateMemberData(row, '审核拒绝')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await proxy?.$modal.confirm(`确认拒绝"${row.studentName}"的入队申请?`);
|
||||
|
||||
|
||||
await approveVolunteerMember(row.id, false);
|
||||
proxy?.$modal.msgSuccess('审核拒绝成功');
|
||||
await getList();
|
||||
@@ -350,25 +351,25 @@ const handleBatchApprove = async () => {
|
||||
proxy?.$modal.msgWarning('请选择要审核的成员');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await proxy?.$modal.confirm(`确认批量通过所选成员的入队申请?`);
|
||||
|
||||
const promises = ids.value.map(id => {
|
||||
|
||||
const promises = ids.value.map((id) => {
|
||||
// 根据ID找到对应的成员记录,进行数据验证
|
||||
const member = memberList.value.find(m => m.id === id);
|
||||
const member = memberList.value.find((m) => m.id === id);
|
||||
if (!member) {
|
||||
proxy?.$modal.msgError(`未找到ID为${id}的成员记录`);
|
||||
return Promise.reject(new Error(`未找到ID为${id}的成员记录`));
|
||||
}
|
||||
|
||||
|
||||
// 验证数据完整性
|
||||
if (!validateMemberData(member, '批量审核通过')) {
|
||||
return Promise.reject(new Error(`成员${member.studentName}数据不完整`));
|
||||
}
|
||||
|
||||
|
||||
return approveVolunteerMember(id, true);
|
||||
});
|
||||
|
||||
|
||||
await Promise.all(promises);
|
||||
proxy?.$modal.msgSuccess('批量审核通过成功');
|
||||
await getList();
|
||||
@@ -380,25 +381,25 @@ const handleBatchReject = async () => {
|
||||
proxy?.$modal.msgWarning('请选择要审核的成员');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await proxy?.$modal.confirm(`确认批量拒绝所选成员的入队申请?`);
|
||||
|
||||
const promises = ids.value.map(id => {
|
||||
|
||||
const promises = ids.value.map((id) => {
|
||||
// 根据ID找到对应的成员记录,进行数据验证
|
||||
const member = memberList.value.find(m => m.id === id);
|
||||
const member = memberList.value.find((m) => m.id === id);
|
||||
if (!member) {
|
||||
proxy?.$modal.msgError(`未找到ID为${id}的成员记录`);
|
||||
return Promise.reject(new Error(`未找到ID为${id}的成员记录`));
|
||||
}
|
||||
|
||||
|
||||
// 验证数据完整性
|
||||
if (!validateMemberData(member, '批量审核拒绝')) {
|
||||
return Promise.reject(new Error(`成员${member.studentName}数据不完整`));
|
||||
}
|
||||
|
||||
|
||||
return approveVolunteerMember(id, false);
|
||||
});
|
||||
|
||||
|
||||
await Promise.all(promises);
|
||||
proxy?.$modal.msgSuccess('批量审核拒绝成功');
|
||||
await getList();
|
||||
@@ -486,4 +487,4 @@ onMounted(() => {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1199,7 +1199,7 @@ const handleActivityManagement = (row) => {
|
||||
relatedTeamName: row.teamName,
|
||||
from: 'team'
|
||||
});
|
||||
|
||||
|
||||
router.push({
|
||||
path: '/stgl/activity/info',
|
||||
query: {
|
||||
|
||||
@@ -4,4 +4,4 @@ export type {
|
||||
XskhAssessmentIndicatorTypeBo,
|
||||
XskhAssessmentIndicatorTypeQuery,
|
||||
XskhAssessmentIndicatorTypeForm
|
||||
} from '@/api/xskh/assessmentIndicatorType/types';
|
||||
} from '@/api/xskh/assessmentIndicatorType/types';
|
||||
@@ -78,7 +78,7 @@
|
||||
<!-- 操作按钮行 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -97,16 +97,22 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:enrollment:info:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:enrollment:info:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:enrollment:info:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:enrollment:info:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:enrollment:info:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:enrollment:info:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchAudit()" v-hasPermi="['zsjy:enrollment:info:edit']">批量审核</el-button>
|
||||
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchAudit()" v-hasPermi="['zsjy:enrollment:info:edit']"
|
||||
>批量审核</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -1606,7 +1612,7 @@ const handleBatchUpdate = async () => {
|
||||
return;
|
||||
}
|
||||
// 如果只选择了一条记录,调用单个修改方法
|
||||
const row = enrollmentInfoList.value.find(item => item.id === ids.value[0]);
|
||||
const row = enrollmentInfoList.value.find((item) => item.id === ids.value[0]);
|
||||
if (row) {
|
||||
handleUpdate(row);
|
||||
}
|
||||
|
||||
@@ -474,13 +474,7 @@
|
||||
|
||||
<script setup name="GraduateInfo" lang="ts">
|
||||
import { ref, reactive, getCurrentInstance, onMounted } from 'vue';
|
||||
import {
|
||||
listGraduateInfo,
|
||||
getGraduateInfo,
|
||||
delGraduateInfo,
|
||||
exportGraduateInfo,
|
||||
getOverallStatistics
|
||||
} from '@/api/zsjy/graduateInfo';
|
||||
import { listGraduateInfo, getGraduateInfo, delGraduateInfo, exportGraduateInfo, getOverallStatistics } from '@/api/zsjy/graduateInfo';
|
||||
import { GraduateInfoVO, GraduateInfoQuery, GraduateStatisticsVO } from '@/api/zsjy/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<!-- 第三行:搜索按钮 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -186,20 +186,10 @@
|
||||
<el-table-column label="操作" width="160" align="center" class-name="small-padding fixed-width" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="View"
|
||||
@click="handleView(scope.row)"
|
||||
></el-button>
|
||||
<el-button link type="info" icon="View" @click="handleView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看日历" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Calendar"
|
||||
@click="handleCalendar(scope.row)"
|
||||
></el-button>
|
||||
<el-button link type="success" icon="Calendar" @click="handleCalendar(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<!-- 搜索按钮行 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -170,31 +170,13 @@
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="View"
|
||||
@click="handleView(scope.row)"
|
||||
v-hasPermi="['zsjy:internship:query']"
|
||||
></el-button>
|
||||
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['zsjy:internship:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增实习信息" placement="top" v-if="!scope.row.internshipId">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Plus"
|
||||
@click="handleAdd(scope.row)"
|
||||
v-hasPermi="['zsjy:internship:add']"
|
||||
></el-button>
|
||||
<el-button link type="success" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['zsjy:internship:add']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑实习信息" placement="top" v-if="scope.row.internshipId">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleEdit(scope.row)"
|
||||
v-hasPermi="['zsjy:internship:edit']"
|
||||
></el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)" v-hasPermi="['zsjy:internship:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -56,33 +56,17 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipmonthly:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['zsjy:internshipmonthly:edit']"
|
||||
>修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:internshipmonthly:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['zsjy:internshipmonthly:remove']"
|
||||
>删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:internshipmonthly:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['zsjy:internshipmonthly:export']"
|
||||
>导出</el-button>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipmonthly:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -144,16 +128,27 @@
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipmonthly:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="提交" placement="top" v-if="scope.row.reviewStatus === 3">
|
||||
<el-button link type="success" icon="Check" @click="handleSubmit(scope.row)" v-hasPermi="['zsjy:internshipmonthly:submit']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Check"
|
||||
@click="handleSubmit(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:submit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.reviewStatus === 3">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipmonthly:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
|
||||
<!-- 分页 -->
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<!-- 第三行:搜索按钮 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item style="margin-bottom: 0;">
|
||||
<el-form-item style="margin-bottom: 0">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -71,33 +71,17 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipVisit:add']">新增走访</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleBatchUpdate"
|
||||
v-hasPermi="['zsjy:internshipVisit:edit']"
|
||||
>修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleBatchUpdate" v-hasPermi="['zsjy:internshipVisit:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleBatchDelete"
|
||||
v-hasPermi="['zsjy:internshipVisit:remove']"
|
||||
>删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleBatchDelete" v-hasPermi="['zsjy:internshipVisit:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['zsjy:internshipVisit:export']"
|
||||
>导出</el-button>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipVisit:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -335,8 +319,6 @@ const viewDialogVisible = ref(false);
|
||||
const formDialogVisible = ref(false);
|
||||
const currentRecord = ref(null);
|
||||
|
||||
|
||||
|
||||
// 字典数据
|
||||
const visitStatusOptions = ref([]);
|
||||
const studentOptions = ref([]);
|
||||
@@ -449,8 +431,6 @@ function getList() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.pageNum = 1;
|
||||
@@ -540,7 +520,7 @@ function handleBatchUpdate() {
|
||||
if (ids.value.length === 1) {
|
||||
// 单个修改,直接调用修改方法
|
||||
const visitId = ids.value[0];
|
||||
const row = visitList.value.find(item => item.visitId === visitId);
|
||||
const row = visitList.value.find((item) => item.visitId === visitId);
|
||||
if (row) {
|
||||
handleUpdate(row);
|
||||
}
|
||||
@@ -782,8 +762,6 @@ onMounted(() => {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
|
||||
@@ -84,10 +84,14 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:position:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:position:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:position:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:position:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:position:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:position:export']">导出</el-button>
|
||||
@@ -260,7 +264,15 @@
|
||||
</template>
|
||||
|
||||
<script setup name="JobPosition" lang="ts">
|
||||
import { listJobPosition, getJobPosition, delJobPosition, addJobPosition, updateJobPosition, exportJobPosition, listEnterpriseOptions } from '@/api/zsjy/jobPosition';
|
||||
import {
|
||||
listJobPosition,
|
||||
getJobPosition,
|
||||
delJobPosition,
|
||||
addJobPosition,
|
||||
updateJobPosition,
|
||||
exportJobPosition,
|
||||
listEnterpriseOptions
|
||||
} from '@/api/zsjy/jobPosition';
|
||||
import { JobPositionVO, JobPositionForm, JobPositionQuery } from '@/api/zsjy/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@@ -63,7 +63,9 @@
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['zsjy:poster:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['zsjy:poster:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['zsjy:poster:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:poster:export']">导出</el-button>
|
||||
@@ -105,8 +107,22 @@
|
||||
:preview="true"
|
||||
style="cursor: pointer"
|
||||
/>
|
||||
<div v-else style="width: 80px; height: 80px; background: #fafafa; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #ccc; border: 1px dashed #d9d9d9; border-radius: 4px;">
|
||||
<div style="text-align: center;">
|
||||
<div
|
||||
v-else
|
||||
style="
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: #fafafa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 4px;
|
||||
"
|
||||
>
|
||||
<div style="text-align: center">
|
||||
<el-icon><Picture /></el-icon>
|
||||
<div>未上传</div>
|
||||
</div>
|
||||
@@ -185,13 +201,7 @@
|
||||
<div v-if="previewOpen && previewData.posterPath" class="poster-preview-container">
|
||||
<!-- 海报图片预览 -->
|
||||
<div class="poster-image-section">
|
||||
<image-preview
|
||||
:src="previewData.posterPath"
|
||||
:width="400"
|
||||
:height="600"
|
||||
:preview="true"
|
||||
style="margin: 0 auto;"
|
||||
/>
|
||||
<image-preview :src="previewData.posterPath" :width="400" :height="600" :preview="true" style="margin: 0 auto" />
|
||||
</div>
|
||||
|
||||
<!-- 海报详情信息 -->
|
||||
|
||||
Reference in new issue
Block a user