|
|
@@ -117,12 +117,20 @@
|
|
|
!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '0' && hasActivityOperationPermission(selectedActivity)
|
|
|
"
|
|
|
>
|
|
|
- <el-button type="success" icon="Upload" @click="handleSubmit()" v-hasPermi="['stgl:activity:edit']">提交</el-button>
|
|
|
+ <el-button type="success" icon="Upload" @click="handleSubmitApproval()" v-hasPermi="['stgl:activity:edit']">提交审批</el-button>
|
|
|
</el-col>
|
|
|
<el-col
|
|
|
:span="1.5"
|
|
|
v-if="
|
|
|
- !single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '2' && hasActivityOperationPermission(selectedActivity)
|
|
|
+ !single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '8' && hasActivityOperationPermission(selectedActivity)
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-button type="info" icon="RefreshLeft" @click="handleWithdrawApproval()" v-hasPermi="['stgl:activity:edit']">撤回审批</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col
|
|
|
+ :span="1.5"
|
|
|
+ v-if="
|
|
|
+ !single && selectedActivity && ['8', '9'].includes(getOriginalActivityStatus(selectedActivity) || '') && canApproveActivity(selectedActivity)
|
|
|
"
|
|
|
>
|
|
|
<el-button type="warning" icon="Check" @click="handleApprove()" v-hasPermi="['stgl:activity:approve']">审批</el-button>
|
|
|
@@ -283,7 +291,7 @@
|
|
|
></el-button>
|
|
|
</el-tooltip>
|
|
|
<el-tooltip
|
|
|
- content="提交"
|
|
|
+ content="提交审批"
|
|
|
placement="top"
|
|
|
v-if="getOriginalActivityStatus(scope.row) === '0' && hasActivityOperationPermission(scope.row)"
|
|
|
>
|
|
|
@@ -293,14 +301,29 @@
|
|
|
icon="Upload"
|
|
|
size="default"
|
|
|
style="font-size: 16px; margin-right: 8px"
|
|
|
- @click="handleSubmit(scope.row)"
|
|
|
+ @click="handleSubmitApproval(scope.row)"
|
|
|
+ v-hasPermi="['stgl:activity:edit']"
|
|
|
+ ></el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip
|
|
|
+ content="撤回审批"
|
|
|
+ placement="top"
|
|
|
+ v-if="getOriginalActivityStatus(scope.row) === '8' && hasActivityOperationPermission(scope.row)"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="info"
|
|
|
+ icon="RefreshLeft"
|
|
|
+ size="default"
|
|
|
+ style="font-size: 16px; margin-right: 8px"
|
|
|
+ @click="handleWithdrawApproval(scope.row)"
|
|
|
v-hasPermi="['stgl:activity:edit']"
|
|
|
></el-button>
|
|
|
</el-tooltip>
|
|
|
<el-tooltip
|
|
|
content="审批"
|
|
|
placement="top"
|
|
|
- v-if="getOriginalActivityStatus(scope.row) === '2' && hasActivityOperationPermission(scope.row)"
|
|
|
+ v-if="['8', '9'].includes(getOriginalActivityStatus(scope.row) || '') && canApproveActivity(scope.row)"
|
|
|
>
|
|
|
<el-button
|
|
|
link
|
|
|
@@ -661,29 +684,8 @@
|
|
|
ref="membersDialogRef"
|
|
|
/>
|
|
|
|
|
|
- <!-- 审批对话框 -->
|
|
|
- <el-dialog v-model="approveDialog.visible" title="审批活动" width="500px" append-to-body>
|
|
|
- <el-form ref="approveFormRef" :model="approveForm" label-width="100px">
|
|
|
- <el-form-item label="活动名称">
|
|
|
- <el-input v-model="approveForm.activityName" readonly />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="审批结果" prop="approvalResult">
|
|
|
- <el-radio-group v-model="approveForm.approvalResult">
|
|
|
- <el-radio label="approve">通过</el-radio>
|
|
|
- <el-radio label="reject">驳回</el-radio>
|
|
|
- </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-form-item>
|
|
|
- </el-form>
|
|
|
- <template #footer>
|
|
|
- <div class="dialog-footer">
|
|
|
- <el-button @click="approveDialog.visible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="confirmApprove" :loading="approveLoading">确认审批</el-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </el-dialog>
|
|
|
+ <!-- 新的审批对话框组件 -->
|
|
|
+ <ApprovalDialog ref="approvalDialogRef" @success="handleApprovalSuccess" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -712,9 +714,11 @@ import ActivityRatingView from './components/ActivityRatingView.vue';
|
|
|
import ActivityDetailView from './components/ActivityDetailView.vue';
|
|
|
import ActivityMembersDialog from './components/ActivityMembersDialog.vue';
|
|
|
import ActivityRegistrationView from './components/ActivityRegistrationView.vue'; // Added import
|
|
|
+import ApprovalDialog from './components/ApprovalDialog.vue';
|
|
|
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
import { checkClubActivityPermission, type ClubActivityPermissionResult } from '@/api/stgl/clubPermission';
|
|
|
+import { getPendingActivityAudits } from '@/api/stgl/activity/audit';
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const route = useRoute();
|
|
|
@@ -752,8 +756,8 @@ const queryFormRef = ref<ElFormInstance>();
|
|
|
const activityFormRef = ref<ElFormInstance>();
|
|
|
const copyFormRef = ref<ElFormInstance>();
|
|
|
const summaryFormRef = ref<ElFormInstance>();
|
|
|
-const approveFormRef = ref<ElFormInstance>();
|
|
|
const membersDialogRef = ref();
|
|
|
+const approvalDialogRef = ref<InstanceType<typeof ApprovalDialog>>();
|
|
|
|
|
|
const dialog = reactive<DialogOption>({
|
|
|
visible: false,
|
|
|
@@ -780,23 +784,11 @@ const membersDialog = reactive<DialogOption>({
|
|
|
title: ''
|
|
|
});
|
|
|
|
|
|
-// 审批对话框
|
|
|
-const approveDialog = reactive<DialogOption>({
|
|
|
- visible: false,
|
|
|
- title: ''
|
|
|
-});
|
|
|
-
|
|
|
-// 审批表单
|
|
|
-const approveForm = reactive({
|
|
|
- id: undefined as string | number | undefined,
|
|
|
- activityName: '',
|
|
|
- approvalResult: 'approve' as 'approve' | 'reject',
|
|
|
- rejectReason: ''
|
|
|
-});
|
|
|
+// 待审批活动缓存(用于权限判断)
|
|
|
+const pendingAuditsCache = ref<Map<number, any>>(new Map());
|
|
|
|
|
|
const copyLoading = ref(false);
|
|
|
const summaryLoading = ref(false);
|
|
|
-const approveLoading = ref(false);
|
|
|
|
|
|
// 关联社团禁用状态控制
|
|
|
const isRelatedClubDisabled = ref(false);
|
|
|
@@ -1005,12 +997,6 @@ const reset = () => {
|
|
|
// 重置关联社团禁用状态
|
|
|
isRelatedClubDisabled.value = false;
|
|
|
|
|
|
- // 重置审批表单
|
|
|
- approveForm.id = undefined;
|
|
|
- approveForm.activityName = '';
|
|
|
- approveForm.approvalResult = 'approve';
|
|
|
- approveForm.rejectReason = '';
|
|
|
-
|
|
|
// 更新formKey以强制重新渲染组件
|
|
|
formKey.value++;
|
|
|
|
|
|
@@ -1689,6 +1675,12 @@ const handleRelatedDeptsChange = (depts: DeptOption[] | null) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+/** 查询表单 - 关联部门变化处理 */
|
|
|
+const handleQueryRelatedDeptChange = (dept: DeptOption | null) => {
|
|
|
+ queryParams.value.relatedDept = dept;
|
|
|
+ queryParams.value.relatedDeptId = dept?.deptId;
|
|
|
+};
|
|
|
+
|
|
|
/** 关联社团变化处理 */
|
|
|
const handleRelatedClubChange = (club: ClubOption | null) => {
|
|
|
form.value.relatedClub = club;
|
|
|
@@ -1776,99 +1768,159 @@ const handleSubmit = async (row?: StglActivityVO) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-/** 审批按钮操作 */
|
|
|
-const handleApprove = async (row?: StglActivityVO) => {
|
|
|
+/** 提交活动审批 */
|
|
|
+const handleSubmitApproval = async (row?: StglActivityVO) => {
|
|
|
const activity = row || selectedActivity.value;
|
|
|
if (!activity) return;
|
|
|
|
|
|
- // 显示审批对话框
|
|
|
- approveDialog.visible = true;
|
|
|
- approveDialog.title = `审批活动 - ${activity.activityName}`;
|
|
|
- approveForm.id = activity.id;
|
|
|
- approveForm.activityName = activity.activityName || '';
|
|
|
- approveForm.approvalResult = 'approve';
|
|
|
- approveForm.rejectReason = '';
|
|
|
+ // 调试信息:打印关联社团ID
|
|
|
+ console.log('🔍 提交审批 - 活动信息:', {
|
|
|
+ activityId: activity.id,
|
|
|
+ activityName: activity.activityName,
|
|
|
+ relatedClubId: activity.relatedClubId,
|
|
|
+ relatedClubIdType: typeof activity.relatedClubId,
|
|
|
+ relatedClubName: activity.relatedClubName
|
|
|
+ });
|
|
|
+
|
|
|
+ // 前置检查:必须关联社团
|
|
|
+ if (!activity.relatedClubId) {
|
|
|
+ proxy?.$modal.msgError('该活动未关联社团,无法提交审批。请先编辑活动并选择关联社团。');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ await proxy?.$modal.confirm(`是否确认提交活动"${activity.activityName}"进行审批?`);
|
|
|
+
|
|
|
+ // 发送必填字段 + 业务必需字段(满足后端验证和业务逻辑)
|
|
|
+ // 注意:relatedClubId 保持字符串类型,避免雪花ID精度丢失
|
|
|
+ // 后端 Bo 已修改为接受 String 类型,在 Service 层转换为 Long
|
|
|
+ const requestData = {
|
|
|
+ id: activity.id,
|
|
|
+ activityName: activity.activityName,
|
|
|
+ activityType: activity.activityType,
|
|
|
+ startTime: activity.startTime,
|
|
|
+ endTime: activity.endTime,
|
|
|
+ relatedClubId: String(activity.relatedClubId), // 保持字符串类型
|
|
|
+ activityStatus: '8' // 8-已提交审批
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('📤 发送审批请求:', requestData);
|
|
|
+
|
|
|
+ await updateStglActivity(requestData);
|
|
|
+
|
|
|
+ proxy?.$modal.msgSuccess('提交审批成功');
|
|
|
+ await getList();
|
|
|
+ // 重新加载待审批权限
|
|
|
+ await loadPendingAudits();
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('❌ 提交审批失败:', error);
|
|
|
+ proxy?.$modal.msgError('提交审批失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-/** 取消活动按钮操作 */
|
|
|
-const handleCancel = async (row?: StglActivityVO) => {
|
|
|
+/** 撤回活动审批 */
|
|
|
+const handleWithdrawApproval = async (row?: StglActivityVO) => {
|
|
|
const activity = row || selectedActivity.value;
|
|
|
if (!activity) return;
|
|
|
|
|
|
try {
|
|
|
- await proxy?.$modal.confirm(`是否确认取消活动"${activity.activityName}"?取消后将无法恢复。`);
|
|
|
-
|
|
|
- // 调用后端接口更新活动状态为6(已取消)
|
|
|
- // 传递完整的活动数据,避免后端验证失败
|
|
|
+ await proxy?.$modal.confirm(`是否确认撤回活动"${activity.activityName}"的审批?`);
|
|
|
+
|
|
|
+ // 发送必填字段(满足后端EditGroup验证)
|
|
|
await updateStglActivity({
|
|
|
- ...activity,
|
|
|
- activityStatus: '6'
|
|
|
+ id: activity.id,
|
|
|
+ activityName: activity.activityName,
|
|
|
+ activityType: activity.activityType,
|
|
|
+ startTime: activity.startTime,
|
|
|
+ endTime: activity.endTime,
|
|
|
+ activityStatus: '0' // 0-草稿
|
|
|
});
|
|
|
-
|
|
|
- proxy?.$modal.msgSuccess('取消活动成功');
|
|
|
+
|
|
|
+ proxy?.$modal.msgSuccess('撤回审批成功');
|
|
|
await getList();
|
|
|
+ // 重新加载待审批权限
|
|
|
+ await loadPendingAudits();
|
|
|
} catch (error) {
|
|
|
if (error !== 'cancel') {
|
|
|
- console.error('取消活动失败:', error);
|
|
|
- proxy?.$modal.msgError('取消活动失败');
|
|
|
+ console.error('撤回审批失败:', error);
|
|
|
+ proxy?.$modal.msgError('撤回审批失败');
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-/** 确认审批 */
|
|
|
-const confirmApprove = async () => {
|
|
|
- if (!approveFormRef.value) return;
|
|
|
+/** 审批活动 */
|
|
|
+const handleApprove = async (row?: StglActivityVO) => {
|
|
|
+ const activity = row || selectedActivity.value;
|
|
|
+ if (!activity) return;
|
|
|
|
|
|
- approveLoading.value = true;
|
|
|
- try {
|
|
|
- const activity = activityList.value.find((item) => item.id === approveForm.id);
|
|
|
- if (!activity) {
|
|
|
- proxy?.$modal.msgError('活动信息不存在');
|
|
|
- return;
|
|
|
- }
|
|
|
+ // 从缓存中获取审批记录
|
|
|
+ const auditRecord = pendingAuditsCache.value.get(Number(activity.id));
|
|
|
+ if (!auditRecord) {
|
|
|
+ proxy?.$modal.msgWarning('无法获取审批记录信息');
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- let newStatus: string;
|
|
|
- let successMessage: string;
|
|
|
+ // 打开新的审批对话框
|
|
|
+ approvalDialogRef.value?.open(auditRecord.id, activity.id);
|
|
|
+};
|
|
|
|
|
|
- if (approveForm.approvalResult === 'approve') {
|
|
|
- // 审批通过,根据时间比较确定状态
|
|
|
- const now = new Date();
|
|
|
- const startTime = new Date(activity.startTime);
|
|
|
- const endTime = new Date(activity.endTime);
|
|
|
+/** 审批成功回调 */
|
|
|
+const handleApprovalSuccess = async () => {
|
|
|
+ await getList();
|
|
|
+ // 重新加载待审批权限
|
|
|
+ await loadPendingAudits();
|
|
|
+};
|
|
|
|
|
|
- if (now < startTime) {
|
|
|
- newStatus = '3'; // 已通过(活动还未开始)
|
|
|
- } else if (now >= startTime && now <= endTime) {
|
|
|
- newStatus = '4'; // 进行中(活动正在进行)
|
|
|
- } else {
|
|
|
- newStatus = '5'; // 已结束(活动已结束)
|
|
|
- }
|
|
|
- successMessage = '审批通过';
|
|
|
- } else {
|
|
|
- // 审批驳回
|
|
|
- if (!approveForm.rejectReason.trim()) {
|
|
|
- proxy?.$modal.msgWarning('请输入驳回原因');
|
|
|
- return;
|
|
|
- }
|
|
|
- newStatus = '7'; // 已驳回
|
|
|
- successMessage = '审批驳回';
|
|
|
- }
|
|
|
+/** 判断是否可以审批活动 */
|
|
|
+const canApproveActivity = (activity: StglActivityVO | null): boolean => {
|
|
|
+ if (!activity) return false;
|
|
|
+ return pendingAuditsCache.value.has(Number(activity.id));
|
|
|
+};
|
|
|
+
|
|
|
+/** 加载待审批权限 */
|
|
|
+const loadPendingAudits = async () => {
|
|
|
+ try {
|
|
|
+ const userId = userStore.userId;
|
|
|
+ const res = await getPendingActivityAudits(userId);
|
|
|
+ const audits = res.data || [];
|
|
|
+
|
|
|
+ // 构建待审批缓存
|
|
|
+ const cache = new Map<number, any>();
|
|
|
+ audits.forEach((audit: any) => {
|
|
|
+ cache.set(Number(audit.activityId), audit);
|
|
|
+ });
|
|
|
+ pendingAuditsCache.value = cache;
|
|
|
+
|
|
|
+ console.log('待审批活动缓存:', pendingAuditsCache.value);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载待审批权限失败:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
- // 调用后端接口更新活动状态
|
|
|
+/** 取消活动按钮操作 */
|
|
|
+const handleCancel = async (row?: StglActivityVO) => {
|
|
|
+ const activity = row || selectedActivity.value;
|
|
|
+ if (!activity) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await proxy?.$modal.confirm(`是否确认取消活动"${activity.activityName}"?取消后将无法恢复。`);
|
|
|
+
|
|
|
+ // 调用后端接口更新活动状态为6(已取消)
|
|
|
// 传递完整的活动数据,避免后端验证失败
|
|
|
await updateStglActivity({
|
|
|
...activity,
|
|
|
- activityStatus: newStatus
|
|
|
+ activityStatus: '6'
|
|
|
});
|
|
|
|
|
|
- proxy?.$modal.msgSuccess(successMessage);
|
|
|
- approveDialog.visible = false;
|
|
|
+ proxy?.$modal.msgSuccess('取消活动成功');
|
|
|
await getList();
|
|
|
} catch (error) {
|
|
|
- console.error('审批失败:', error);
|
|
|
- proxy?.$modal.msgError('审批失败');
|
|
|
- } finally {
|
|
|
- approveLoading.value = false;
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('取消活动失败:', error);
|
|
|
+ proxy?.$modal.msgError('取消活动失败');
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -1989,6 +2041,28 @@ const getOriginalActivityStatus = (activity: StglActivityVO | null): string => {
|
|
|
return activity.originalActivityStatus || activity.activityStatus || '';
|
|
|
};
|
|
|
|
|
|
+// 监听对话框打开,在新增模式下自动设置报名开始时间为当前时间
|
|
|
+watch(
|
|
|
+ () => dialog.visible,
|
|
|
+ (isVisible, wasVisible) => {
|
|
|
+ // 当对话框从关闭变为打开,且是新增模式(无ID)时,自动设置报名开始时间
|
|
|
+ if (isVisible && !wasVisible && !form.value.id) {
|
|
|
+ nextTick(() => {
|
|
|
+ // 获取当前时间并格式化为 YYYY-MM-DD HH:mm:ss
|
|
|
+ const now = new Date();
|
|
|
+ const year = now.getFullYear();
|
|
|
+ const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
|
+ const day = String(now.getDate()).padStart(2, '0');
|
|
|
+ const hours = String(now.getHours()).padStart(2, '0');
|
|
|
+ const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
|
+ const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
|
+
|
|
|
+ form.value.registrationStart = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
// 监听活动开始时间变化,自动更新报名结束时间
|
|
|
watch(
|
|
|
() => form.value.startTime,
|
|
|
@@ -2019,9 +2093,11 @@ watch(
|
|
|
onMounted(() => {
|
|
|
initUrlParams();
|
|
|
getList();
|
|
|
+ // 加载待审批权限
|
|
|
+ loadPendingAudits();
|
|
|
// 如果有关联社团,检查权限
|
|
|
if (queryParams.value.relatedClub?.relatedClubId && queryParams.value.relatedClub?.relatedType === 1) {
|
|
|
- checkClubPermission(Number(queryParams.value.relatedClub.relatedClubId));
|
|
|
+ checkClubPermission(queryParams.value.relatedClub.relatedClubId);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -2030,7 +2106,7 @@ watch(
|
|
|
() => queryParams.value.relatedClub,
|
|
|
(newRelatedClub) => {
|
|
|
if (newRelatedClub?.relatedClubId && newRelatedClub.relatedType === 1) {
|
|
|
- checkClubPermission(Number(newRelatedClub.relatedClubId));
|
|
|
+ checkClubPermission(newRelatedClub.relatedClubId);
|
|
|
} else {
|
|
|
clubPermissionInfo.value = null;
|
|
|
}
|