提交代码
This commit is contained in:
1 parent
4e34c7eb36
commit
375a2c8df7
54 files changed
+4631
-4352
No files matched your search
@@ -70,6 +70,7 @@ export interface OverseasRegistrationForm {
|
||||
officePhone?: string; // 办公电话
|
||||
mobilePhone?: string; // 移动电话
|
||||
remarks?: string; // 备注
|
||||
status?: string; // 登记状态:0-草稿,1-待审核,2-已通过,3-已拒绝
|
||||
}
|
||||
|
||||
// 登记历史查询参数类型
|
||||
@@ -142,6 +143,17 @@ export function getOverseasRegistrationList(query: OverseasRegistrationQuery) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的出国登记列表(只查询当前登录人创建的记录)
|
||||
*/
|
||||
export function getMyOverseasRegistrationList(query: OverseasRegistrationQuery) {
|
||||
return request({
|
||||
url: '/rs/overseas/registration/my/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询出国登记详细
|
||||
*/
|
||||
@@ -433,4 +445,4 @@ export function batchRejectOverseasRegistration(registrationIds: Array<number |
|
||||
...data
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export interface OverseasApplicationVO {
|
||||
partyRetentionFileName?: string; // 党籍保留附件文件名
|
||||
partyRetentionUploadTime?: string; // 党籍保留附件上传时间
|
||||
partyRetentionUploadStatus?: string; // 党籍保留附件上传状态
|
||||
partyRetentionFileOssId?: number; // 党籍保留附件OSS ID
|
||||
partyRetentionFileOssId?: string; // 党籍保留附件OSS ID(支持多个ID用逗号分隔)
|
||||
purpose: string;
|
||||
purposeDetail: string;
|
||||
destination: string;
|
||||
@@ -137,6 +137,7 @@ export interface OverseasApplicationForm {
|
||||
partyRetentionFileName?: string; // 党籍保留附件文件名
|
||||
partyRetentionUploadTime?: string; // 党籍保留附件上传时间
|
||||
partyRetentionUploadStatus?: string; // 党籍保留附件上传状态:0-未上传,1-已上传
|
||||
partyRetentionFileOssId?: string; // 党籍保留附件OSS ID(支持多个ID用逗号分隔)
|
||||
purpose: string;
|
||||
purposeDetail: string;
|
||||
destination: string;
|
||||
@@ -341,6 +342,15 @@ export function listPendingOverseasApplication() {
|
||||
});
|
||||
}
|
||||
|
||||
// 查询当前用户的待审批申请分页列表(根据角色过滤,支持分页和搜索)
|
||||
export function listPendingOverseasApplicationPage(query: OverseasApplicationQuery) {
|
||||
return request({
|
||||
url: '/rs/overseas/application/pending/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 🚨 标准化改造:删除自定义待审批查询API
|
||||
//
|
||||
@@ -805,6 +815,7 @@ export interface ItineraryChangeQuery {
|
||||
applicantName?: string;
|
||||
destination?: string;
|
||||
status?: string;
|
||||
applicantId?: number;
|
||||
params?: Record<string, any>;
|
||||
}
|
||||
|
||||
@@ -842,8 +853,8 @@ export interface ItineraryChangeVO {
|
||||
status: string;
|
||||
statusText: string;
|
||||
approvalOpinion?: string;
|
||||
approvalBy?: number;
|
||||
approvalByName?: string;
|
||||
approverId?: number;
|
||||
approverName?: string;
|
||||
approvalTime?: string;
|
||||
createTime: string;
|
||||
updateTime?: string;
|
||||
|
||||
@@ -34,7 +34,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
const [err] = await tos(useUserStore().getInfo());
|
||||
if (err) {
|
||||
await useUserStore().logout();
|
||||
ElMessage.error(err);
|
||||
// ElMessage.error(err.message || '获取用户信息失败');
|
||||
next({ path: '/' });
|
||||
} else {
|
||||
isRelogin.show = false;
|
||||
|
||||
@@ -204,8 +204,14 @@ service.interceptors.response.use(
|
||||
err.response = res;
|
||||
return Promise.reject(err);
|
||||
} else if (code !== HttpStatus.SUCCESS) {
|
||||
ElNotification.error({ title: msg });
|
||||
const err = new Error(msg) as any;
|
||||
// 确保错误消息不为空,避免只显示图标没有文字
|
||||
const errorMsg = msg || errorCode['default'] || '操作失败,请稍后重试';
|
||||
ElNotification.error({
|
||||
title: '错误提示',
|
||||
message: errorMsg,
|
||||
duration: 5000
|
||||
});
|
||||
const err = new Error(errorMsg) as any;
|
||||
err.response = res;
|
||||
return Promise.reject(err);
|
||||
} else {
|
||||
|
||||
+22
-6
@@ -192,13 +192,29 @@ const getLoginData = () => {
|
||||
* 获取租户列表
|
||||
*/
|
||||
const initTenantList = async () => {
|
||||
const { data } = await getTenantList(false);
|
||||
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
|
||||
if (tenantEnabled.value) {
|
||||
tenantList.value = data.voList;
|
||||
if (tenantList.value != null && tenantList.value.length !== 0) {
|
||||
loginForm.value.tenantId = tenantList.value[0].tenantId;
|
||||
try {
|
||||
const [err, res] = await to(getTenantList(false));
|
||||
if (err) {
|
||||
// 静默处理租户列表获取失败,不影响登录功能
|
||||
console.warn('获取租户列表失败:', err);
|
||||
// 设置默认值,确保登录功能正常
|
||||
tenantEnabled.value = true;
|
||||
return;
|
||||
}
|
||||
if (res && res.data) {
|
||||
const { data } = res;
|
||||
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
|
||||
if (tenantEnabled.value) {
|
||||
tenantList.value = data.voList;
|
||||
if (tenantList.value != null && tenantList.value.length !== 0) {
|
||||
loginForm.value.tenantId = tenantList.value[0].tenantId;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 捕获其他可能的错误,静默处理
|
||||
console.warn('初始化租户列表时发生错误:', error);
|
||||
tenantEnabled.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1142
-573
File diff suppressed because it is too large.
Load diff
@@ -1,30 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<!-- 🔴 标准化改造:提示用户使用标准工作流页面 -->
|
||||
<el-alert
|
||||
title="功能迁移通知"
|
||||
type="info"
|
||||
description="出国行程变更审批功能已标准化,请使用系统统一的工作流页面查看和处理任务"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="mb-4"
|
||||
>
|
||||
<template #default>
|
||||
<div class="mt-2">
|
||||
<p><strong>推荐操作:</strong></p>
|
||||
<ul class="ml-4">
|
||||
<li>访问 <strong>工作流 > 我的任务 > 待办任务</strong> 页面</li>
|
||||
<li>在统一的待办列表中查看"出国行程变更审批"任务</li>
|
||||
<li>使用工作流的筛选功能按业务类型查看</li>
|
||||
</ul>
|
||||
<div class="mt-3">
|
||||
<el-button type="primary" @click="navigateToTaskWaiting"> 前往待办任务页面 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<!-- 审批管理选项卡 -->
|
||||
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
||||
<!-- 待审批 -->
|
||||
@@ -69,13 +45,8 @@
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 🔴 标准化改造:显示迁移提示 -->
|
||||
<el-empty description="请使用标准工作流待办页面查看审批任务" :image-size="120">
|
||||
<el-button type="primary" @click="navigateToTaskWaiting">前往待办页面</el-button>
|
||||
</el-empty>
|
||||
|
||||
<!-- 保留原表格结构用于兼容(隐藏显示) -->
|
||||
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleView" style="display: none">
|
||||
<!-- 待审批变更列表 -->
|
||||
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleView">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
||||
<el-table-column label="部门" align="center" prop="department" min-width="120" />
|
||||
@@ -190,7 +161,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批时间" align="center" prop="approvalTime" min-width="160" />
|
||||
<el-table-column label="审批人" align="center" prop="approvalByName" min-width="100" />
|
||||
<el-table-column label="审批人" align="center" prop="approverName" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="80" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
@@ -243,7 +214,7 @@
|
||||
<el-tag :type="getStatusTagType(viewData.status)">{{ viewData.statusText }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">{{ viewData.createTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审批人" v-if="viewData.approvalByName">{{ viewData.approvalByName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审批人" v-if="viewData.approverName">{{ viewData.approverName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审批时间" v-if="viewData.approvalTime">{{ viewData.approvalTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审批意见" :span="2" v-if="viewData.approvalOpinion">{{ viewData.approvalOpinion }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@@ -267,16 +238,10 @@
|
||||
<el-form-item label="审批意见" prop="opinion">
|
||||
<el-input v-model="approvalForm.opinion" type="textarea" :rows="4" placeholder="请输入审批意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<!-- 手写签字 -->
|
||||
<el-form-item label="审批签字">
|
||||
<SignaturePad
|
||||
v-model="approvalForm.signature"
|
||||
:width="450"
|
||||
:height="120"
|
||||
:auto-save="true"
|
||||
:show-save-button="false"
|
||||
/>
|
||||
<SignaturePad v-model="approvalForm.signature" :width="450" :height="120" :auto-save="true" :show-save-button="false" />
|
||||
<div class="signature-tip">请在上方签字板中手写签名</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -413,17 +378,24 @@ const handleTabClick = (tab: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 🔴 标准化改造:简化待审批列表获取(仅用于兼容模式)
|
||||
// 获取待审批变更列表(status=0)
|
||||
const getPendingList = async () => {
|
||||
state.pendingLoading = true;
|
||||
|
||||
// 提示用户使用标准工作流页面
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
|
||||
// 清空数据,不再调用自定义API
|
||||
state.pendingList = [];
|
||||
state.pendingTotal = 0;
|
||||
state.pendingLoading = false;
|
||||
try {
|
||||
// 查询待审核状态的变更申请(status='0')
|
||||
const params = { ...state.pendingQueryParams };
|
||||
params.status = '0'; // 只查询待审核的
|
||||
const response = await listPendingItineraryChange(params);
|
||||
state.pendingList = response.rows || [];
|
||||
state.pendingTotal = response.total || 0;
|
||||
} catch (error) {
|
||||
console.error('获取待审批列表失败:', error);
|
||||
ElMessage.error('获取待审批列表失败');
|
||||
state.pendingList = [];
|
||||
state.pendingTotal = 0;
|
||||
} finally {
|
||||
state.pendingLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 查询审批记录列表
|
||||
@@ -446,20 +418,22 @@ const getRecordList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 🔴 标准化改造:导航到标准工作流待办页面
|
||||
const navigateToTaskWaiting = () => {
|
||||
// 跳转到标准工作流待办页面
|
||||
router.push('/workflow/task/taskWaiting');
|
||||
};
|
||||
|
||||
// 搜索查询 - 待审批列表
|
||||
const handlePendingQuery = () => {
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
state.pendingQueryParams.pageNum = 1;
|
||||
getPendingList();
|
||||
};
|
||||
|
||||
// 重置搜索 - 待审批列表
|
||||
const resetPendingQuery = () => {
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
pendingQueryFormRef.value?.resetFields();
|
||||
Object.assign(state.pendingQueryParams, {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicantName: '',
|
||||
destination: ''
|
||||
});
|
||||
getPendingList();
|
||||
};
|
||||
|
||||
// 搜索查询 - 审批记录
|
||||
@@ -574,4 +548,7 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
}
|
||||
:global(.app-container) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,6 @@
|
||||
<el-card class="box-card">
|
||||
<!-- Tab切换:变更申请 / 变更记录 -->
|
||||
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
||||
|
||||
<!-- Tab 1: 变更申请(待审核) -->
|
||||
<el-tab-pane label="变更申请(待审核)" name="all">
|
||||
<div class="tab-content">
|
||||
@@ -23,15 +22,6 @@
|
||||
<el-input v-model="queryParams.destination" placeholder="请输入目的地国家" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="变更状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择变更状态" clearable style="width: 100%">
|
||||
<el-option label="待审核" value="0" />
|
||||
<el-option label="已通过" value="1" />
|
||||
<el-option label="已拒绝" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -52,9 +42,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['rs:itineraryChange:add']">
|
||||
新增变更申请
|
||||
</el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['rs:itineraryChange:add']"> 新增变更申请 </el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -62,45 +50,65 @@
|
||||
|
||||
<!-- 变更行程列表 -->
|
||||
<el-table v-loading="loading" border :data="changeList" @row-dblclick="handleView">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
||||
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
||||
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
||||
<el-table-column label="原出国时间" align="center" min-width="200">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="新出国时间" align="center" min-width="200">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)">{{ scope.row.statusText }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请时间" align="center" prop="createTime" min-width="160" />
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
||||
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
||||
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
||||
<el-table-column label="原出国时间" align="center" min-width="200">
|
||||
<template #default="scope"> {{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }} </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="新出国时间" align="center" min-width="200">
|
||||
<template #default="scope"> {{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }} </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)">{{ scope.row.statusText }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请时间" align="center" prop="createTime" min-width="160" />
|
||||
<el-table-column label="操作" align="center" width="250" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="变更历史" placement="top">
|
||||
<el-button link type="info" icon="List" @click="handleViewHistory(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="List"
|
||||
@click="handleViewHistory(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:query']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 待审核状态:显示撤回按钮 -->
|
||||
<el-tooltip content="撤回申请" placement="top" v-if="scope.row.status === '0'">
|
||||
<el-button link type="warning" icon="RefreshLeft" @click="handleCancel(scope.row)" v-hasPermi="['rs:itineraryChange:cancel']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="RefreshLeft"
|
||||
@click="handleCancel(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:cancel']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 已拒绝状态:显示重新提交按钮 -->
|
||||
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2'">
|
||||
<el-button link type="success" icon="RefreshRight" @click="handleResubmit(scope.row)" v-hasPermi="['rs:itineraryChange:edit']"></el-button>
|
||||
<!-- 已拒绝或已撤回状态:显示重新提交按钮 -->
|
||||
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2' || scope.row.status === '3'">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="RefreshRight"
|
||||
@click="handleResubmit(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 删除按钮 -->
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.status !== '1'">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['rs:itineraryChange:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -131,15 +139,6 @@
|
||||
<el-input v-model="myQueryParams.destination" placeholder="请输入目的地国家" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="变更状态" prop="status">
|
||||
<el-select v-model="myQueryParams.status" placeholder="请选择变更状态" clearable style="width: 100%">
|
||||
<el-option label="待审核" value="0" />
|
||||
<el-option label="已通过" value="1" />
|
||||
<el-option label="已拒绝" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -169,14 +168,10 @@
|
||||
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
||||
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
||||
<el-table-column label="原出国时间" align="center" min-width="200">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }}
|
||||
</template>
|
||||
<template #default="scope"> {{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }} </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="新出国时间" align="center" min-width="200">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }}
|
||||
</template>
|
||||
<template #default="scope"> {{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }} </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
||||
<template #default="scope">
|
||||
@@ -190,19 +185,43 @@
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="变更历史" placement="top">
|
||||
<el-button link type="info" icon="List" @click="handleViewHistory(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="List"
|
||||
@click="handleViewHistory(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:query']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 待审核状态:显示撤回按钮 -->
|
||||
<el-tooltip content="撤回申请" placement="top" v-if="scope.row.status === '0'">
|
||||
<el-button link type="warning" icon="RefreshLeft" @click="handleCancel(scope.row)" v-hasPermi="['rs:itineraryChange:cancel']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="RefreshLeft"
|
||||
@click="handleCancel(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:cancel']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 已拒绝状态:显示重新提交按钮 -->
|
||||
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2'">
|
||||
<el-button link type="success" icon="RefreshRight" @click="handleResubmit(scope.row)" v-hasPermi="['rs:itineraryChange:edit']"></el-button>
|
||||
<!-- 已拒绝或已撤回状态:显示重新提交按钮 -->
|
||||
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2' || scope.row.status === '3'">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="RefreshRight"
|
||||
@click="handleResubmit(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 删除按钮 -->
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.status !== '1'">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['rs:itineraryChange:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -223,24 +242,23 @@
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body :close-on-click-modal="false" destroy-on-close>
|
||||
<div style="max-height: 70vh; overflow-y: auto; padding-right: 10px;">
|
||||
<div style="max-height: 70vh; overflow-y: auto; padding-right: 10px">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
|
||||
|
||||
<!-- 选择原出国申请 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="选择原出国申请" prop="applicationId">
|
||||
<el-select
|
||||
v-model="form.applicationId"
|
||||
placeholder="请选择已审批通过的出国申请"
|
||||
<el-select
|
||||
v-model="form.applicationId"
|
||||
placeholder="请选择已审批通过的出国申请"
|
||||
style="width: 100%"
|
||||
@change="handleApplicationChange"
|
||||
:disabled="!!form.changeId"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="application in availableApplicationList"
|
||||
:key="application.applicationId"
|
||||
<el-option
|
||||
v-for="application in availableApplicationList"
|
||||
:key="application.applicationId"
|
||||
:label="`${application.applicantName} - ${application.destination} (${formatDate(application.startDate)}至${formatDate(application.endDate)})`"
|
||||
:value="application.applicationId"
|
||||
/>
|
||||
@@ -336,13 +354,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="行程安排">
|
||||
<el-input
|
||||
v-model="form.originalItinerary"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
disabled
|
||||
placeholder="原行程安排"
|
||||
/>
|
||||
<el-input v-model="form.originalItinerary" type="textarea" :rows="3" disabled placeholder="原行程安排" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -384,14 +396,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="行程安排" prop="newItinerary">
|
||||
<el-input
|
||||
v-model="form.newItinerary"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入实际行程安排"
|
||||
maxlength="1000"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="form.newItinerary" type="textarea" :rows="3" placeholder="请输入实际行程安排" maxlength="1000" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -401,10 +406,10 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="changeReason">
|
||||
<el-input
|
||||
v-model="form.changeReason"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
<el-input
|
||||
v-model="form.changeReason"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请详细说明行程变更的原因(相关证明材料可附后)"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
@@ -418,21 +423,13 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请填写有关认识和态度"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<div style="margin-top: 12px; color: #303133; font-size: 14px; font-weight: bold;">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请填写有关认识和态度" maxlength="200" show-word-limit />
|
||||
<div style="margin-top: 12px; color: #303133; font-size: 14px; font-weight: bold">
|
||||
我承诺,以上情况说明完全属实。如有不实行为,愿意接受组织处理。
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
@@ -450,14 +447,14 @@
|
||||
<!-- 基本信息 -->
|
||||
<el-descriptions-item label="申请人">{{ viewData.applicantName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="部门">{{ viewData.department || '-' }}</el-descriptions-item>
|
||||
|
||||
|
||||
<!-- 原申请信息 -->
|
||||
<el-descriptions-item label="原目的地">{{ viewData.originalDestination || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="原出境事由">{{ viewData.originalExitReason || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="原出发日期">{{ formatDate(viewData.originalStartDate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="原返回日期">{{ formatDate(viewData.originalEndDate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="原出国天数" :span="2">{{ viewData.originalDuration || 0 }}天</el-descriptions-item>
|
||||
|
||||
|
||||
<!-- 变更信息 -->
|
||||
<el-descriptions-item label="变更类型" :span="2">{{ viewData.changeType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="新目的地" v-if="viewData.newDestination">{{ viewData.newDestination }}</el-descriptions-item>
|
||||
@@ -468,7 +465,7 @@
|
||||
<el-descriptions-item label="新行程安排" :span="2" v-if="viewData.newItinerary">{{ viewData.newItinerary }}</el-descriptions-item>
|
||||
<el-descriptions-item label="变更原因" :span="2">{{ viewData.changeReason || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2" v-if="viewData.remark">{{ viewData.remark }}</el-descriptions-item>
|
||||
|
||||
|
||||
<!-- 审批信息 -->
|
||||
<el-descriptions-item label="变更状态">
|
||||
<el-tag :type="getStatusTagType(viewData.status)">{{ viewData.statusText }}</el-tag>
|
||||
@@ -513,7 +510,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -522,7 +518,8 @@ import { ref, reactive, toRefs, onMounted, computed, watch } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import animate from '@/animate';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import {
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import {
|
||||
listItineraryChange,
|
||||
listMyItineraryChange,
|
||||
getItineraryChange,
|
||||
@@ -535,6 +532,7 @@ import {
|
||||
getOverseasApplication,
|
||||
getItineraryChangeHistory,
|
||||
getOverseasRecordByApplication,
|
||||
listOverseasApplication,
|
||||
type ItineraryChangeQuery,
|
||||
type ItineraryChangeVO,
|
||||
type ItineraryChangeForm,
|
||||
@@ -553,7 +551,7 @@ const formRef = ref();
|
||||
const state = reactive({
|
||||
// Tab切换
|
||||
activeTab: 'all',
|
||||
|
||||
|
||||
// 全部变更列表
|
||||
loading: false,
|
||||
showSearch: true,
|
||||
@@ -565,9 +563,10 @@ const state = reactive({
|
||||
pageSize: 10,
|
||||
applicantName: '',
|
||||
destination: '',
|
||||
status: ''
|
||||
status: '',
|
||||
applicantId: undefined as number | undefined
|
||||
} as ItineraryChangeQuery,
|
||||
|
||||
|
||||
// 我的变更列表
|
||||
myLoading: false,
|
||||
showMySearch: true,
|
||||
@@ -577,9 +576,10 @@ const state = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
destination: '',
|
||||
status: ''
|
||||
status: '',
|
||||
applicantId: undefined as number | undefined
|
||||
} as ItineraryChangeQuery,
|
||||
|
||||
|
||||
// 对话框
|
||||
dialog: {
|
||||
visible: false,
|
||||
@@ -591,21 +591,21 @@ const state = reactive({
|
||||
historyDialog: {
|
||||
visible: false
|
||||
},
|
||||
|
||||
|
||||
// 表单数据
|
||||
form: {} as ItineraryChangeForm,
|
||||
viewData: null as ItineraryChangeVO | null,
|
||||
|
||||
|
||||
// 变更历史
|
||||
historyLoading: false,
|
||||
historyList: [] as ItineraryChangeVO[],
|
||||
|
||||
|
||||
// 可用的出国申请列表(已审批通过)
|
||||
availableApplicationList: [] as OverseasApplicationVO[],
|
||||
|
||||
|
||||
// 变更类型(多选)
|
||||
changeTypes: [] as string[],
|
||||
|
||||
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }],
|
||||
@@ -690,7 +690,7 @@ onMounted(() => {
|
||||
const handleTabClick = (tab: any) => {
|
||||
const tabName = tab.props?.name || tab.paneName;
|
||||
state.activeTab = tabName;
|
||||
|
||||
|
||||
if (tabName === 'all') {
|
||||
getList();
|
||||
} else if (tabName === 'my') {
|
||||
@@ -698,18 +698,36 @@ const handleTabClick = (tab: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 查询变更申请列表(只显示待审核的数据)
|
||||
// 查询变更申请列表(只显示当前登录人的待审核和已撤回的数据)
|
||||
const getList = async () => {
|
||||
state.loading = true;
|
||||
try {
|
||||
// 如果用户没有手动选择状态,默认只显示待审核的申请
|
||||
const params = { ...state.queryParams };
|
||||
if (!params.status) {
|
||||
params.status = '0'; // 默认只显示待审核状态
|
||||
}
|
||||
const userStore = useUserStore();
|
||||
// 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 10000, // 设置一个较大的值,获取所有数据
|
||||
applicantId: Number(userStore.userId),
|
||||
applicantName: state.queryParams.applicantName,
|
||||
destination: state.queryParams.destination
|
||||
};
|
||||
|
||||
const response = await listItineraryChange(params);
|
||||
state.changeList = response.rows;
|
||||
state.total = response.total;
|
||||
|
||||
// 前端过滤:只显示状态为 0(待审核)和 3(已撤回)的数据
|
||||
const filteredRows = response.rows.filter((item: ItineraryChangeVO) => {
|
||||
return item.status === '0' || item.status === '3';
|
||||
});
|
||||
|
||||
// 前端分页
|
||||
const pageNum = state.queryParams.pageNum;
|
||||
const pageSize = state.queryParams.pageSize;
|
||||
const total = filteredRows.length;
|
||||
const start = (pageNum - 1) * pageSize;
|
||||
const end = Math.min(start + pageSize, total);
|
||||
|
||||
state.changeList = filteredRows.slice(start, end);
|
||||
state.total = total;
|
||||
} catch (error) {
|
||||
console.error('获取变更行程列表失败:', error);
|
||||
} finally {
|
||||
@@ -717,13 +735,35 @@ const getList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 查询我的变更记录列表(只显示已通过的变更申请)
|
||||
// 查询我的变更记录列表(只显示当前登录人的已通过和已拒绝的数据)
|
||||
const getMyList = async () => {
|
||||
state.myLoading = true;
|
||||
try {
|
||||
const response = await listMyItineraryChange(state.myQueryParams);
|
||||
state.myChangeList = response.rows;
|
||||
state.myTotal = response.total;
|
||||
const userStore = useUserStore();
|
||||
// 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 10000, // 设置一个较大的值,获取所有数据
|
||||
applicantId: Number(userStore.userId),
|
||||
destination: state.myQueryParams.destination
|
||||
};
|
||||
|
||||
const response = await listItineraryChange(params);
|
||||
|
||||
// 前端过滤:只显示状态为 1(已通过)和 2(已拒绝)的数据
|
||||
const filteredRows = response.rows.filter((item: ItineraryChangeVO) => {
|
||||
return item.status === '1' || item.status === '2';
|
||||
});
|
||||
|
||||
// 前端分页
|
||||
const pageNum = state.myQueryParams.pageNum;
|
||||
const pageSize = state.myQueryParams.pageSize;
|
||||
const total = filteredRows.length;
|
||||
const start = (pageNum - 1) * pageSize;
|
||||
const end = Math.min(start + pageSize, total);
|
||||
|
||||
state.myChangeList = filteredRows.slice(start, end);
|
||||
state.myTotal = total;
|
||||
} catch (error) {
|
||||
console.error('获取我的变更记录失败:', error);
|
||||
} finally {
|
||||
@@ -731,13 +771,25 @@ const getMyList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取已审批通过的出国申请列表
|
||||
// 获取已审批通过的出国申请列表(只显示当前登录人的)
|
||||
const getApprovedApplicationList = async () => {
|
||||
try {
|
||||
const response = await getApprovedOverseasApplications();
|
||||
state.availableApplicationList = response.data;
|
||||
const userStore = useUserStore();
|
||||
// 使用listOverseasApplication查询当前登录人的已通过申请
|
||||
const response = await listOverseasApplication({
|
||||
pageNum: 1,
|
||||
pageSize: 1000, // 设置一个较大的值,获取所有数据
|
||||
applicantId: Number(userStore.userId), // 只查询当前登录人的申请
|
||||
status: '1' // 只查询已通过的状态
|
||||
});
|
||||
|
||||
// 确保只返回已通过审批的申请
|
||||
state.availableApplicationList = response.rows.filter((item: OverseasApplicationVO) => {
|
||||
return item.status === '1';
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取已审批通过的出国申请列表失败:', error);
|
||||
ElMessage.error('获取已审批通过的出国申请列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -798,30 +850,30 @@ const handleApplicationChange = async (applicationId: number) => {
|
||||
// 1. 获取出国申请详情
|
||||
const response = await getOverseasApplication(applicationId);
|
||||
const selectedApplication = response.data;
|
||||
|
||||
|
||||
// 2. 根据申请ID获取出国记录(包含recordId)
|
||||
const recordResponse = await getOverseasRecordByApplication(applicationId);
|
||||
const recordData = recordResponse.data;
|
||||
|
||||
|
||||
if (selectedApplication && recordData) {
|
||||
// 设置recordId(关键字段,必填)
|
||||
state.form.recordId = recordData.recordId;
|
||||
|
||||
|
||||
// 设置申请人ID(关键字段,必填)
|
||||
state.form.applicantId = selectedApplication.applicantId;
|
||||
|
||||
|
||||
// 填充本人基本情况
|
||||
state.form.applicantName = selectedApplication.applicantName;
|
||||
state.form.gender = selectedApplication.gender;
|
||||
state.form.birthDate = selectedApplication.birthDate;
|
||||
// 政治面貌:将字典值转换为文本标签
|
||||
state.form.politicalStatus = getPoliticalStatusLabel(selectedApplication.politicalStatus);
|
||||
state.form.politicalStatus = selectedApplication.politicalStatus ? getPoliticalStatusLabel(selectedApplication.politicalStatus) : '';
|
||||
state.form.professionalTitle = selectedApplication.professionalTitle;
|
||||
state.form.position = selectedApplication.position;
|
||||
state.form.documentName = selectedApplication.documentName;
|
||||
state.form.documentNumber = selectedApplication.documentNumber;
|
||||
state.form.department = selectedApplication.department;
|
||||
|
||||
|
||||
// 填充申请行程情况
|
||||
// 前往国家:优先使用destinationCountry,其次使用destination
|
||||
state.form.originalDestination = selectedApplication.destinationCountry || selectedApplication.destination;
|
||||
@@ -833,7 +885,7 @@ const handleApplicationChange = async (applicationId: number) => {
|
||||
state.form.originalStartDate = selectedApplication.startDate;
|
||||
state.form.originalEndDate = selectedApplication.endDate;
|
||||
state.form.originalDuration = selectedApplication.duration;
|
||||
|
||||
|
||||
// 默认将原信息复制到新信息
|
||||
state.form.newDestination = selectedApplication.destinationCountry || selectedApplication.destination;
|
||||
state.form.newDestinationCity = selectedApplication.destinationCity;
|
||||
@@ -867,22 +919,24 @@ const handleCancel = (row: ItineraryChangeVO) => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPlaceholder: '请输入撤回原因'
|
||||
}).then(async ({ value }) => {
|
||||
try {
|
||||
await cancelItineraryChange(row.changeId, value);
|
||||
ElMessage.success('撤回成功');
|
||||
|
||||
// 根据当前Tab刷新列表
|
||||
if (state.activeTab === 'all') {
|
||||
getList();
|
||||
} else if (state.activeTab === 'my') {
|
||||
getMyList();
|
||||
})
|
||||
.then(async ({ value }) => {
|
||||
try {
|
||||
await cancelItineraryChange(row.changeId, value);
|
||||
ElMessage.success('撤回成功');
|
||||
|
||||
// 根据当前Tab刷新列表
|
||||
if (state.activeTab === 'all') {
|
||||
getList();
|
||||
} else if (state.activeTab === 'my') {
|
||||
getMyList();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('撤回失败:', error);
|
||||
ElMessage.error('撤回失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('撤回失败:', error);
|
||||
ElMessage.error('撤回失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 重新提交
|
||||
@@ -890,12 +944,12 @@ const handleResubmit = async (row: ItineraryChangeVO) => {
|
||||
try {
|
||||
const response = await getItineraryChange(row.changeId);
|
||||
Object.assign(state.form, response.data);
|
||||
|
||||
|
||||
// 解析变更类型
|
||||
if (response.data.changeType) {
|
||||
state.changeTypes = response.data.changeType.split(',');
|
||||
}
|
||||
|
||||
|
||||
state.dialog.title = '重新提交行程变更申请';
|
||||
state.dialog.visible = true;
|
||||
} catch (error) {
|
||||
@@ -910,30 +964,32 @@ const handleDelete = (row: ItineraryChangeVO) => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await delItineraryChange(row.changeId);
|
||||
ElMessage.success('删除成功');
|
||||
|
||||
// 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页)
|
||||
if (state.activeTab === 'all') {
|
||||
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
||||
if (state.changeList.length === 1 && state.queryParams.pageNum > 1) {
|
||||
state.queryParams.pageNum--;
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await delItineraryChange(row.changeId);
|
||||
ElMessage.success('删除成功');
|
||||
|
||||
// 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页)
|
||||
if (state.activeTab === 'all') {
|
||||
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
||||
if (state.changeList.length === 1 && state.queryParams.pageNum > 1) {
|
||||
state.queryParams.pageNum--;
|
||||
}
|
||||
getList();
|
||||
} else if (state.activeTab === 'my') {
|
||||
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
||||
if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) {
|
||||
state.myQueryParams.pageNum--;
|
||||
}
|
||||
getMyList();
|
||||
}
|
||||
getList();
|
||||
} else if (state.activeTab === 'my') {
|
||||
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
||||
if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) {
|
||||
state.myQueryParams.pageNum--;
|
||||
}
|
||||
getMyList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 查看变更历史
|
||||
@@ -963,15 +1019,14 @@ const submitForm = () => {
|
||||
if (state.form.newExitReason !== state.form.originalExitReason) {
|
||||
changeTypeList.push('出境事由变更');
|
||||
}
|
||||
if (state.form.newStartDate !== state.form.originalStartDate ||
|
||||
state.form.newEndDate !== state.form.originalEndDate) {
|
||||
if (state.form.newStartDate !== state.form.originalStartDate || state.form.newEndDate !== state.form.originalEndDate) {
|
||||
changeTypeList.push('时间变更');
|
||||
}
|
||||
if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) {
|
||||
changeTypeList.push('行程变更');
|
||||
}
|
||||
state.form.changeType = changeTypeList.length > 0 ? changeTypeList.join(',') : '其他变更';
|
||||
|
||||
|
||||
state.submitLoading = true;
|
||||
try {
|
||||
if (state.form.changeId) {
|
||||
@@ -982,7 +1037,7 @@ const submitForm = () => {
|
||||
ElMessage.success('提交成功!变更申请已进入审批流程。');
|
||||
}
|
||||
state.dialog.visible = false;
|
||||
|
||||
|
||||
// 根据当前Tab刷新列表(重置到第1页)
|
||||
if (state.activeTab === 'all') {
|
||||
state.queryParams.pageNum = 1;
|
||||
@@ -1056,11 +1111,11 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
||||
const typeMap: Record<string, 'primary' | 'success' | 'info' | 'warning' | 'danger'> = {
|
||||
'0': 'warning', // 待审核
|
||||
'1': 'success', // 已通过
|
||||
'2': 'danger' // 已拒绝
|
||||
'2': 'danger', // 已拒绝
|
||||
'3': 'info' // 已撤回
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -1081,6 +1136,8 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
||||
.table-card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:global(.app-container) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -49,24 +49,24 @@
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Refresh" @click="handleRefreshPending">刷新</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<div class="info-text">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
<span>这里显示所有待审批申请</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Refresh" @click="handleRefreshPending">刷新</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<div class="info-text">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
<span>这里显示所有待审批申请</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table v-loading="pendingLoading" :data="pendingList" border>
|
||||
<!-- 表格 -->
|
||||
<el-table v-loading="pendingLoading" :data="pendingList" border>
|
||||
<el-table-column label="申请人" prop="applicantName" min-width="100" align="center" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" prop="department" min-width="140" align="center" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="前往国家" prop="destinationCountry" min-width="110" align="center" :show-overflow-tooltip="true" />
|
||||
@@ -350,8 +350,16 @@
|
||||
<template #footer>
|
||||
<div style="display: flex; justify-content: space-between; width: 100%">
|
||||
<div>
|
||||
<!-- 只有审批完成的申请才显示下载PDF按钮 -->
|
||||
<el-button v-if="currentDetail && currentDetail.status === '1'" type="primary" icon="Download" @click="handleDownloadPdf" v-hasPermi="['rs:overseas:query']">下载审批表PDF</el-button>
|
||||
<!-- 只有审批完成的申请才显示下载Word按钮 -->
|
||||
<el-button
|
||||
v-if="currentDetail && currentDetail.status === '1'"
|
||||
type="primary"
|
||||
icon="Download"
|
||||
@click="handleDownloadApprovalWord"
|
||||
v-hasPermi="['rs:overseas:query']"
|
||||
>
|
||||
下载审批表Word
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
@@ -362,14 +370,15 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OverseasApprovalMonitor">
|
||||
<script setup lang="ts" name="OverseasApprovalMonitor">
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { Clock, Search, Refresh, View, EditPen, InfoFilled, Download } from '@element-plus/icons-vue';
|
||||
|
||||
// API请求
|
||||
import { getPendingList as apiGetPendingList, getCompletedList as apiGetCompletedList, getDetail, downloadApprovalPdf } from '@/api/rs/overseas';
|
||||
import { getPendingList as apiGetPendingList, getCompletedList as apiGetCompletedList, getDetail } from '@/api/rs/overseas';
|
||||
|
||||
// 导入变更历史API接口
|
||||
import { getItineraryChangeHistory } from '@/api/rs/overseas';
|
||||
@@ -405,6 +414,8 @@ const completedQueryParams = reactive({
|
||||
department: undefined,
|
||||
destination: undefined,
|
||||
applyTimeRange: undefined,
|
||||
applyTimeStart: undefined as string | undefined,
|
||||
applyTimeEnd: undefined as string | undefined,
|
||||
status: '1'
|
||||
});
|
||||
|
||||
@@ -691,48 +702,180 @@ const getChangeStatusTagType = (status) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载审批表PDF
|
||||
* 下载审批表Word
|
||||
*/
|
||||
const handleDownloadPdf = async () => {
|
||||
if (!currentDetail.value || !currentDetail.value.applicationId) {
|
||||
const handleDownloadApprovalWord = async () => {
|
||||
const detail = currentDetail.value;
|
||||
if (!detail || !detail.applicationId) {
|
||||
ElMessage.error('无法获取申请ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ElMessage.info('正在生成PDF,请稍候...');
|
||||
|
||||
// 调用API获取blob数据
|
||||
const response = await downloadApprovalPdf(currentDetail.value.applicationId);
|
||||
|
||||
// 创建带MIME类型的Blob对象
|
||||
const blob = response instanceof Blob
|
||||
? new Blob([response], { type: 'application/pdf' })
|
||||
: new Blob([response.data], { type: 'application/pdf' });
|
||||
|
||||
// 创建下载链接
|
||||
ElMessage.info('正在生成审批表,请稍候...');
|
||||
|
||||
const templateData = buildApprovalTemplateData(detail);
|
||||
const [Docxtemplater, PizZip, ImageModule] = await Promise.all([
|
||||
import('docxtemplater'),
|
||||
import('pizzip'),
|
||||
import('docxtemplater-image-module-free')
|
||||
]);
|
||||
const templateResponse = await fetch('/chuguojingshenpibiao.docx', { cache: 'no-store' });
|
||||
|
||||
if (!templateResponse.ok) {
|
||||
throw new Error('模板文件加载失败,请联系管理员');
|
||||
}
|
||||
|
||||
const arrayBuffer = await templateResponse.arrayBuffer();
|
||||
const zip = new PizZip.default(arrayBuffer);
|
||||
const imageOpts = {
|
||||
centered: false,
|
||||
getImage: (base64: string) => {
|
||||
if (!base64) {
|
||||
return null;
|
||||
}
|
||||
const pure = base64.split(',')[1] || base64;
|
||||
const bytes = Uint8Array.from(atob(pure), (char) => char.charCodeAt(0));
|
||||
return bytes.buffer;
|
||||
},
|
||||
getSize: () => [60, 20]
|
||||
};
|
||||
|
||||
const doc = new Docxtemplater.default(zip, {
|
||||
modules: [new ImageModule.default(imageOpts)],
|
||||
paragraphLoop: true,
|
||||
linebreaks: true,
|
||||
delimiters: { start: '{{', end: '}}' },
|
||||
parser: (tag: string) => {
|
||||
return {
|
||||
get: (scope: Record<string, any>) => {
|
||||
const cleanTag = tag.trim().replace(/^%+/, '');
|
||||
return scope[cleanTag];
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
doc.render(templateData);
|
||||
|
||||
const blob = doc.getZip().generate({
|
||||
type: 'blob',
|
||||
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
});
|
||||
|
||||
const fileName = `出国申请审批表-${detail.applicantName || dayjs().format('YYYYMMDDHHmmss')}.docx`;
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
|
||||
// 设置下载文件名(使用申请人姓名和时间戳)
|
||||
const fileName = `出国申请审批表_${currentDetail.value.applicantName}_${currentDetail.value.applicationId}.pdf`;
|
||||
link.download = fileName;
|
||||
|
||||
// 触发下载
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// 清理资源
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
ElMessage.success('PDF下载成功');
|
||||
} catch (error) {
|
||||
console.error('下载PDF失败', error);
|
||||
ElMessage.error('下载PDF失败,请稍后重试');
|
||||
|
||||
ElMessage.success('审批表下载成功');
|
||||
} catch (error: any) {
|
||||
console.error('下载审批表失败', error);
|
||||
ElMessage.error(error?.message || '下载审批表失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建审批表模板数据
|
||||
*/
|
||||
const buildApprovalTemplateData = (detail) => {
|
||||
const startParts = getDateParts(detail.startDate);
|
||||
const endParts = getDateParts(detail.endDate);
|
||||
const applyParts = getDateParts(detail.applyTime || detail.createTime);
|
||||
const durationDays = detail.duration || calculateDuration(detail.startDate, detail.endDate);
|
||||
const templateData: Record<string, any> = {
|
||||
applicantName: detail.applicantName || '',
|
||||
genderText: detail.genderText || '',
|
||||
birthYear: getDateParts(detail.birthDate).year,
|
||||
birthMonth: getDateParts(detail.birthDate).month,
|
||||
birthDay: getDateParts(detail.birthDate).day,
|
||||
politicalStatusText: detail.politicalStatusText || '-',
|
||||
department: detail.department || '-',
|
||||
professionalTitle: detail.professionalTitle || '-',
|
||||
position: detail.position || '-',
|
||||
documentName: detail.documentName || '-',
|
||||
documentNumber: detail.documentNumber || '-',
|
||||
destinationCountry: detail.destinationCountry || detail.destination || '-',
|
||||
isTourismChecked: detail.exitReason === '旅游' ? '√' : '',
|
||||
isVisitChecked: detail.exitReason === '探亲' ? '√' : '',
|
||||
visitRelationship: detail.visitRelationship || '',
|
||||
remarks: detail.remarks || '',
|
||||
startYear: startParts.year,
|
||||
startMonth: startParts.month,
|
||||
startDay: startParts.day,
|
||||
endYear: endParts.year,
|
||||
endMonth: endParts.month,
|
||||
endDay: endParts.day,
|
||||
durationDays: durationDays || '',
|
||||
applicantSignature: detail.signature || '',
|
||||
mobilePhone: detail.mobilePhone || detail.contactPhone || '',
|
||||
emergencyContact: detail.emergencyContact || '',
|
||||
applicantSignYear: applyParts.year,
|
||||
applicantSignMonth: applyParts.month,
|
||||
applicantSignDay: applyParts.day
|
||||
};
|
||||
|
||||
fillApprovalNodeData(templateData, detail.approvalHistory || []);
|
||||
return templateData;
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算出国天数
|
||||
*/
|
||||
const calculateDuration = (startDate, endDate) => {
|
||||
if (!startDate || !endDate) return '';
|
||||
const start = dayjs(startDate);
|
||||
const end = dayjs(endDate);
|
||||
if (!start.isValid() || !end.isValid()) return '';
|
||||
return Math.max(end.diff(start, 'day') + 1, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取日期拆分
|
||||
*/
|
||||
const getDateParts = (date) => {
|
||||
if (!date) {
|
||||
return { year: '', month: '', day: '' };
|
||||
}
|
||||
const parsed = dayjs(date);
|
||||
if (!parsed.isValid()) {
|
||||
return { year: '', month: '', day: '' };
|
||||
}
|
||||
return {
|
||||
year: parsed.format('YYYY'),
|
||||
month: parsed.format('MM'),
|
||||
day: parsed.format('DD')
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 填充审批节点数据
|
||||
*/
|
||||
const fillApprovalNodeData = (templateData, approvalHistory) => {
|
||||
const approvalNodes = [
|
||||
{ level: '1', prefix: 'deptLeader' },
|
||||
{ level: '2', prefix: 'hr' },
|
||||
{ level: '3', prefix: 'discipline' },
|
||||
{ level: '4', prefix: 'viceDean' },
|
||||
{ level: '5', prefix: 'secretary' }
|
||||
];
|
||||
|
||||
approvalNodes.forEach((node) => {
|
||||
const record = approvalHistory.find((item) => item.approvalLevel === node.level);
|
||||
const dateParts = getDateParts(record?.approvalTime);
|
||||
templateData[`${node.prefix}Signature`] = record?.approvalSignature || record?.signature || '';
|
||||
templateData[`${node.prefix}Opinion`] = record?.approvalOpinion || '';
|
||||
templateData[`${node.prefix}Year`] = dateParts.year;
|
||||
templateData[`${node.prefix}Month`] = dateParts.month;
|
||||
templateData[`${node.prefix}Day`] = dateParts.day;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
# 出国申请进度查询功能修改总结 - 快速参考
|
||||
|
||||
## ✅ 修改完成
|
||||
|
||||
**修改时间**:2025-10-29
|
||||
**修改范围**:仅修改monitor相关文件,未影响其他模块
|
||||
|
||||
---
|
||||
|
||||
## 📋 核心修改
|
||||
|
||||
### 1. 功能变化
|
||||
|
||||
#### 修改前
|
||||
- ❌ 显示工作流迁移提示
|
||||
- ❌ 无实际查询功能
|
||||
- ❌ 表格被隐藏
|
||||
|
||||
#### 修改后
|
||||
- ✅ 显示当前用户提交的待审批申请
|
||||
- ✅ 显示当前用户提交的已完成审批申请(抄送)
|
||||
- ✅ 支持搜索和查看审批历史
|
||||
|
||||
---
|
||||
|
||||
## 🎯 核心逻辑
|
||||
|
||||
### 数据过滤
|
||||
```java
|
||||
// 后端Service层添加过滤条件
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
wrapper.eq("applicant_id", currentUserId); // 🔴 只查询当前用户提交的申请
|
||||
```
|
||||
|
||||
### 功能说明
|
||||
- **待审批中**:用户提交申请后,在这里查看审批进度
|
||||
- **审批完成**:审批流程完成后,抄送一份到这里
|
||||
|
||||
---
|
||||
|
||||
## 📄 修改文件
|
||||
|
||||
| 文件 | 类型 | 修改内容 |
|
||||
|------|------|---------|
|
||||
| `RsOverseasApprovalMonitorServiceImpl.java` | 后端 | 添加当前用户过滤 |
|
||||
| `RsOverseasApprovalMonitorController.java` | 后端 | 恢复pending接口 |
|
||||
| `monitor/index.vue` | 前端 | 删除提示,恢复表格 |
|
||||
|
||||
---
|
||||
|
||||
## 🧪 快速测试
|
||||
|
||||
### 测试步骤
|
||||
1. 登录系统
|
||||
2. 访问"出国申请进度查询"
|
||||
3. 查看"待审批中"和"审批完成"选项卡
|
||||
|
||||
### 预期结果
|
||||
- ✅ 只显示当前用户提交的申请
|
||||
- ✅ 不显示其他用户的申请
|
||||
- ✅ 可以查看审批历史
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署命令
|
||||
|
||||
```bash
|
||||
# 后端
|
||||
cd backend
|
||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
||||
./stop-main.sh && ./start-main.sh
|
||||
|
||||
# 前端(如需要)
|
||||
cd web
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 详细文档
|
||||
|
||||
完整修改说明请参考:`出国申请进度查询功能修改说明.md`
|
||||
|
||||
---
|
||||
|
||||
**状态**:✅ 已完成
|
||||
**版本**:v1.0
|
||||
|
||||
@@ -1,365 +0,0 @@
|
||||
# 出国申请进度查询功能优化说明
|
||||
|
||||
**日期**: 2025-01-29
|
||||
**模块**: 出国申请进度查询 (`/rs/overseas/monitor`)
|
||||
|
||||
## 📋 功能需求
|
||||
|
||||
### 1. 变更历史记录条件显示
|
||||
- **需求**: 变更历史记录字段只在有变更记录时显示,没有变更记录时不显示该区域
|
||||
- **实现位置**: `web/src/views/rs/overseas/monitor/index.vue`
|
||||
|
||||
### 2. PDF下载功能
|
||||
- **需求**: 在审批完成选项卡的详情弹窗中新增PDF下载按钮
|
||||
- **PDF模板**: 杭州第一技师学院登记报备人员因私事出国(境)审批表
|
||||
- **显示条件**: 仅当申请状态为"已通过"(status='1')时显示下载按钮
|
||||
|
||||
## 🎯 实现内容
|
||||
|
||||
### 一、前端修改
|
||||
|
||||
#### 1. 变更历史记录条件显示
|
||||
**文件**: `web/src/views/rs/overseas/monitor/index.vue`
|
||||
|
||||
**修改内容**:
|
||||
- 原条件: `v-if="currentDetail.recordId"`
|
||||
- 新条件: `v-if="currentDetail.recordId && changeHistoryList.length > 0"`
|
||||
- 移除空状态提示组件 `<el-empty>`
|
||||
|
||||
**代码位置**: 第287行
|
||||
|
||||
```vue
|
||||
<!-- 变更历史记录(仅已完成且有变更记录时显示) -->
|
||||
<div v-if="currentDetail.recordId && changeHistoryList.length > 0" style="margin-top: 20px">
|
||||
<el-divider content-position="left">
|
||||
<span style="font-size: 16px; font-weight: bold; color: #409eff">
|
||||
<el-icon style="vertical-align: middle; margin-right: 5px"><EditPen /></el-icon>
|
||||
变更历史记录
|
||||
</span>
|
||||
</el-divider>
|
||||
|
||||
<!-- 变更历史列表 -->
|
||||
<div v-loading="changeHistoryLoading">
|
||||
<el-table :data="changeHistoryList" border style="width: 100%" max-height="400px">
|
||||
<!-- 表格列定义 -->
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 2. PDF下载按钮
|
||||
**文件**: `web/src/views/rs/overseas/monitor/index.vue`
|
||||
|
||||
**新增内容**:
|
||||
1. **导入图标**: 添加 `Download` 图标
|
||||
2. **导入API**: 添加 `downloadApprovalPdf` 方法
|
||||
3. **弹窗footer布局**: 改为左右布局,左侧显示下载按钮,右侧显示关闭按钮
|
||||
4. **下载方法**: 添加 `handleDownloadPdf` 方法
|
||||
|
||||
**代码位置**: 第350-360行
|
||||
|
||||
```vue
|
||||
<template #footer>
|
||||
<div style="display: flex; justify-content: space-between; width: 100%">
|
||||
<div>
|
||||
<!-- 只有审批完成的申请才显示下载PDF按钮 -->
|
||||
<el-button
|
||||
v-if="currentDetail && currentDetail.status === '1'"
|
||||
type="primary"
|
||||
icon="Download"
|
||||
@click="handleDownloadPdf"
|
||||
v-hasPermi="['rs:overseas:query']">
|
||||
下载审批表PDF
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
**JavaScript方法** (第693-710行):
|
||||
```javascript
|
||||
/**
|
||||
* 下载审批表PDF
|
||||
*/
|
||||
const handleDownloadPdf = async () => {
|
||||
if (!currentDetail.value || !currentDetail.value.applicationId) {
|
||||
ElMessage.error('无法获取申请ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ElMessage.info('正在生成PDF,请稍候...');
|
||||
await downloadApprovalPdf(currentDetail.value.applicationId);
|
||||
ElMessage.success('PDF下载成功');
|
||||
} catch (error) {
|
||||
console.error('下载PDF失败', error);
|
||||
ElMessage.error('下载PDF失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### 3. API接口定义
|
||||
**文件**: `web/src/api/rs/overseas.ts`
|
||||
|
||||
**新增内容**: Monitor相关API接口(第1036-1085行)
|
||||
|
||||
```typescript
|
||||
// ==================== 出国申请进度查询(监控)相关API ====================
|
||||
|
||||
// 审批监控查询参数类型
|
||||
export interface OverseasMonitorQuery {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
applicantName?: string;
|
||||
department?: string;
|
||||
destination?: string;
|
||||
approvalLevel?: string;
|
||||
currentApprover?: string;
|
||||
status?: string;
|
||||
applyTimeStart?: string;
|
||||
applyTimeEnd?: string;
|
||||
}
|
||||
|
||||
// 查询待审批列表(监控)
|
||||
export function getPendingList(query: OverseasMonitorQuery) {
|
||||
return request({
|
||||
url: '/rs/overseas/monitor/pending/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询已完成审批列表(监控)
|
||||
export function getCompletedList(query: OverseasMonitorQuery) {
|
||||
return request({
|
||||
url: '/rs/overseas/monitor/completed/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询申请详情(监控)
|
||||
export function getDetail(applicationId: string | number) {
|
||||
return request({
|
||||
url: `/rs/overseas/monitor/detail/${applicationId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 下载审批表PDF
|
||||
export function downloadApprovalPdf(applicationId: string | number) {
|
||||
return request({
|
||||
url: `/rs/overseas/monitor/downloadApprovalPdf/${applicationId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 二、后端修改
|
||||
|
||||
#### 1. Service接口
|
||||
**文件**: `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApprovalMonitorService.java`
|
||||
|
||||
**新增方法**:
|
||||
```java
|
||||
/**
|
||||
* 下载出国申请审批表PDF
|
||||
*
|
||||
* @param applicationId 申请ID
|
||||
* @param response HTTP响应对象
|
||||
*/
|
||||
void downloadApprovalPdf(Long applicationId, HttpServletResponse response);
|
||||
```
|
||||
|
||||
#### 2. Controller接口
|
||||
**文件**: `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApprovalMonitorController.java`
|
||||
|
||||
**新增接口** (第119-135行):
|
||||
```java
|
||||
/**
|
||||
* 下载出国申请审批表PDF
|
||||
* <p>
|
||||
* 生成并下载"杭州第一技师学院登记报备人员因私事出国(境)审批表"PDF文件
|
||||
*
|
||||
* @param applicationId 申请ID
|
||||
* @param response HTTP响应对象
|
||||
*/
|
||||
@SaCheckPermission("rs:overseas:query")
|
||||
@GetMapping("/downloadApprovalPdf/{applicationId}")
|
||||
public void downloadApprovalPdf(
|
||||
@PathVariable @NotNull(message = "申请ID不能为空") Long applicationId,
|
||||
HttpServletResponse response) {
|
||||
log.info("下载出国申请审批表PDF,申请ID:{}", applicationId);
|
||||
monitorService.downloadApprovalPdf(applicationId, response);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Service实现
|
||||
**文件**: `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalMonitorServiceImpl.java`
|
||||
|
||||
**新增依赖导入**:
|
||||
```java
|
||||
import com.itextpdf.text.*;
|
||||
import com.itextpdf.text.pdf.*;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
```
|
||||
|
||||
**新增方法** (第521-808行):
|
||||
|
||||
1. **`downloadApprovalPdf`**: 主方法,负责查询数据和设置响应头
|
||||
2. **`generateApprovalPdf`**: PDF生成核心方法
|
||||
3. **`addCell`**: 添加表格单元格辅助方法
|
||||
4. **`addApprovalRow`**: 添加审批意见行辅助方法
|
||||
5. **`getApprovalOpinion`**: 获取指定级别审批意见
|
||||
6. **`formatDate`**: 格式化日期(yyyy-MM-dd)
|
||||
7. **`formatBirthDate`**: 格式化出生日期(yyyy年MM月)
|
||||
|
||||
**PDF文档结构**:
|
||||
1. 标题:
|
||||
- "杭州第一技师学院"
|
||||
- "登记报备人员因私事出国(境)审批表"
|
||||
|
||||
2. 个人信息表格(8列):
|
||||
- 姓名、性别、出生年月、政治面貌
|
||||
- 现工作部门(教学系)、职称、职务
|
||||
- 申领证件名称、证件号码
|
||||
- 前往国家或地区、出境事由(旅游/探亲)
|
||||
- 探望对象姓名及与申请者关系
|
||||
- 出国时间、备注
|
||||
- 承诺声明、联系电话、应急联系人
|
||||
|
||||
3. 审批意见表格(2列):
|
||||
- 部门(教学系)意见
|
||||
- 人事(组织)处审核意见
|
||||
- 纪委意见
|
||||
- 学院分管领导意见
|
||||
- 学院党委书记意见
|
||||
|
||||
4. 备注说明:
|
||||
- 因私出境时间说明
|
||||
- 证件号码填写说明
|
||||
|
||||
## 🔧 技术实现细节
|
||||
|
||||
### PDF生成技术
|
||||
- **库**: iText 5.5.13.3
|
||||
- **中文字体**: STSong-Light (UniGB-UCS2-H)
|
||||
- **页面尺寸**: A4
|
||||
- **页边距**: 40pt (上下左右)
|
||||
|
||||
### 字体大小
|
||||
- **标题字体**: 16pt, 加粗
|
||||
- **副标题字体**: 14pt, 加粗
|
||||
- **正文字体**: 10pt, 常规
|
||||
- **小字体**: 9pt, 常规
|
||||
|
||||
### 表格设计
|
||||
- **个人信息表**: 8列,列宽比例 [8:12:6:10:10:12:16:6]
|
||||
- **审批意见表**: 2列,列宽比例 [20:80]
|
||||
- **单元格最小高度**: 25-50pt
|
||||
- **表头背景**: 浅灰色 (BaseColor.LIGHT_GRAY)
|
||||
|
||||
### 数据处理
|
||||
- **性别翻译**: 0→男, 1→女
|
||||
- **政治面貌翻译**: 支持多种编码格式(01-13, 1-13, 英文代码)
|
||||
- **出境事由**: 旅游/探亲,用√标记
|
||||
- **日期格式化**: yyyy-MM-dd / yyyy年MM月
|
||||
- **审批意见**: 优先显示审批意见,无意见时通过状态显示"同意"
|
||||
|
||||
## 📝 权限要求
|
||||
|
||||
- **查看权限**: `rs:overseas:query`
|
||||
- **列表权限**: `rs:overseas:list`
|
||||
|
||||
## 🎨 UI/UX优化
|
||||
|
||||
1. **按钮位置**: 弹窗footer左右布局,下载按钮在左侧,关闭按钮在右侧
|
||||
2. **按钮样式**: 主色调按钮(type="primary")+ 下载图标
|
||||
3. **显示条件**: 仅审批完成状态显示下载按钮
|
||||
4. **用户提示**:
|
||||
- 开始下载:"正在生成PDF,请稍候..."
|
||||
- 下载成功:"PDF下载成功"
|
||||
- 下载失败:"下载PDF失败,请稍后重试"
|
||||
|
||||
## ✅ 测试要点
|
||||
|
||||
### 前端测试
|
||||
1. ✅ 有变更记录时,变更历史区域正常显示
|
||||
2. ✅ 无变更记录时,变更历史区域不显示
|
||||
3. ✅ 审批完成状态显示下载PDF按钮
|
||||
4. ✅ 待审批状态不显示下载PDF按钮
|
||||
5. ✅ 点击下载按钮能触发PDF下载
|
||||
6. ✅ 下载过程中显示加载提示
|
||||
|
||||
### 后端测试
|
||||
1. ✅ PDF文件名包含申请人姓名和时间戳
|
||||
2. ✅ PDF包含完整的个人信息
|
||||
3. ✅ PDF包含所有审批历史记录
|
||||
4. ✅ 中文字体正确显示
|
||||
5. ✅ 表格格式符合模板要求
|
||||
6. ✅ 审批意见正确填充
|
||||
7. ✅ 日期格式化正确
|
||||
|
||||
## 📊 文件清单
|
||||
|
||||
### 前端文件
|
||||
- ✅ `web/src/views/rs/overseas/monitor/index.vue` (修改)
|
||||
- ✅ `web/src/api/rs/overseas.ts` (修改,新增monitor相关API)
|
||||
|
||||
### 后端文件
|
||||
- ✅ `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApprovalMonitorService.java` (修改)
|
||||
- ✅ `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApprovalMonitorController.java` (修改)
|
||||
- ✅ `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalMonitorServiceImpl.java` (修改)
|
||||
|
||||
### 文档文件
|
||||
- ✅ `web/src/views/rs/overseas/monitor/出国申请进度查询功能优化说明.md` (新增)
|
||||
|
||||
## 🚀 部署说明
|
||||
|
||||
### 前端部署
|
||||
```bash
|
||||
cd web
|
||||
npm run build
|
||||
# 部署dist目录到Nginx
|
||||
```
|
||||
|
||||
### 后端部署
|
||||
```bash
|
||||
cd backend
|
||||
mvn clean package -DskipTests
|
||||
# 重启ruoyi-admin服务
|
||||
```
|
||||
|
||||
### 依赖检查
|
||||
后端已包含iText PDF生成库依赖:
|
||||
- `com.itextpdf:itextpdf:5.5.13.3`
|
||||
- `com.itextpdf:itext-asian:5.2.0` (中文字体支持)
|
||||
|
||||
## 📖 相关文档
|
||||
|
||||
- **项目规范**: `.cursor/cursorrules.md`
|
||||
- **开发文档**: `docs/开发文档/API/`
|
||||
- **出国申请API**: `docs/开发文档/API/出国申请管理API.md`
|
||||
|
||||
## 🔗 相关模块
|
||||
|
||||
- **出国申请管理**: `/rs/overseas/application`
|
||||
- **出国记录管理**: `/rs/overseas/record`
|
||||
- **行程变更管理**: `/rs/itinerary/change`
|
||||
|
||||
---
|
||||
|
||||
**实施人**: Cursor AI
|
||||
**审核状态**: ✅ 已完成
|
||||
**更新日期**: 2025-01-29
|
||||
|
||||
@@ -1,394 +0,0 @@
|
||||
# 出国申请进度查询功能修改说明
|
||||
|
||||
## 📋 修改概述
|
||||
|
||||
**修改时间**:2025-10-29
|
||||
**修改目的**:按照审批流程要求,修改出国申请进度查询功能,实现申请人查看自己提交的申请进度
|
||||
|
||||
---
|
||||
|
||||
## 🎯 功能需求
|
||||
|
||||
### 1. 待审批选项卡
|
||||
- **功能**:用户提交申请后,在这里查看自己的待审批记录
|
||||
- **显示内容**:
|
||||
- 当前用户提交的所有待审批申请
|
||||
- 当前审批环节和审批人
|
||||
- 审批历史记录
|
||||
|
||||
### 2. 审批完成选项卡
|
||||
- **功能**:审批完成后,抄送一份到这里
|
||||
- **显示内容**:
|
||||
- 当前用户提交的所有已完成审批申请
|
||||
- 完整的审批历史记录
|
||||
- 关联的出国记录信息
|
||||
|
||||
---
|
||||
|
||||
## 📝 修改内容
|
||||
|
||||
### 1. 后端Service修改
|
||||
|
||||
**文件**:`backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalMonitorServiceImpl.java`
|
||||
|
||||
#### 修改点1:待审批列表查询
|
||||
```java
|
||||
/**
|
||||
* 查询审批中的申请列表(待审核状态)
|
||||
* 🔴 只查询当前用户提交的待审批申请
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<RsOverseasApprovalMonitorVo> queryPendingList(RsOverseasApprovalMonitorQuery query) {
|
||||
// 🔴 获取当前登录用户ID
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
|
||||
// 构建查询条件
|
||||
QueryWrapper<RsOverseasApplication> wrapper = buildQueryWrapper(query);
|
||||
wrapper.eq("status", "0"); // 只查询待审核状态
|
||||
wrapper.eq("applicant_id", currentUserId); // 🔴 只查询当前用户提交的申请
|
||||
|
||||
// ... 分页查询和数据处理
|
||||
}
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ✅ 添加当前用户ID过滤条件
|
||||
- ✅ 只查询申请人ID = 当前用户ID的记录
|
||||
- ✅ 添加日志记录便于排查问题
|
||||
|
||||
#### 修改点2:已完成列表查询
|
||||
```java
|
||||
/**
|
||||
* 查询已完成审批的申请列表(已通过状态)
|
||||
* 🔴 只查询当前用户提交的已完成审批申请(抄送功能)
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<RsOverseasApprovalMonitorVo> queryCompletedList(RsOverseasApprovalMonitorQuery query) {
|
||||
// 🔴 获取当前登录用户ID
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
|
||||
// 构建查询条件
|
||||
QueryWrapper<RsOverseasApplication> wrapper = buildQueryWrapper(query);
|
||||
wrapper.eq("status", "1"); // 只查询已通过状态
|
||||
wrapper.eq("applicant_id", currentUserId); // 🔴 只查询当前用户提交的申请
|
||||
|
||||
// ... 分页查询和数据处理
|
||||
}
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ✅ 实现抄送功能:审批完成后,申请人可以在这里查看
|
||||
- ✅ 添加当前用户ID过滤条件
|
||||
- ✅ 只显示当前用户提交且已完成审批的申请
|
||||
|
||||
---
|
||||
|
||||
### 2. 后端Controller修改
|
||||
|
||||
**文件**:`backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApprovalMonitorController.java`
|
||||
|
||||
#### 修改点:恢复pending接口功能
|
||||
```java
|
||||
/**
|
||||
* 查询待审批的申请列表(出国申请进度查询)
|
||||
* 🔴 功能说明:查询当前用户提交的待审批申请
|
||||
*/
|
||||
@SaCheckPermission("rs:overseas:list")
|
||||
@GetMapping("/pending/list")
|
||||
public TableDataInfo<RsOverseasApprovalMonitorVo> pendingList(RsOverseasApprovalMonitorQuery query) {
|
||||
log.info("查询当前用户的待审批申请列表");
|
||||
query.setStatus("0");
|
||||
return monitorService.queryPendingList(query);
|
||||
}
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ❌ 删除了工作流迁移提示
|
||||
- ✅ 恢复正常的查询功能
|
||||
- ✅ 返回实际数据而不是错误提示
|
||||
|
||||
---
|
||||
|
||||
### 3. 前端页面修改
|
||||
|
||||
**文件**:`web/src/views/rs/overseas/monitor/index.vue`
|
||||
|
||||
#### 修改点1:删除工作流迁移提示
|
||||
```vue
|
||||
<!-- 🔴 删除的内容 -->
|
||||
<el-alert
|
||||
title="功能迁移通知"
|
||||
description="出国申请监控功能已标准化..."
|
||||
...
|
||||
>
|
||||
<el-button @click="navigateToTaskWaiting">前往待办任务页面</el-button>
|
||||
</el-alert>
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ❌ 删除整个Alert提示组件
|
||||
- ❌ 删除跳转到工作流页面的按钮
|
||||
|
||||
#### 修改点2:恢复待审批表格显示
|
||||
```vue
|
||||
<!-- 表格 -->
|
||||
<el-table v-loading="pendingLoading" :data="pendingList" border>
|
||||
<!-- 之前是 style="display: none" 隐藏的,现在恢复显示 -->
|
||||
<el-table-column label="申请人" prop="applicantName" ... />
|
||||
<el-table-column label="所属部门" prop="department" ... />
|
||||
<!-- ... 其他列 -->
|
||||
</el-table>
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ✅ 恢复表格正常显示
|
||||
- ✅ 删除 `style="display: none"`
|
||||
- ✅ 显示待审批申请数据
|
||||
|
||||
#### 修改点3:恢复查询功能
|
||||
```javascript
|
||||
// 🔴 修改前:显示提示信息
|
||||
const getPendingList = async () => {
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
pendingList.value = [];
|
||||
};
|
||||
|
||||
// ✅ 修改后:正常查询数据
|
||||
const getPendingList = async () => {
|
||||
pendingLoading.value = true;
|
||||
try {
|
||||
const response = await apiGetPendingList(pendingQueryParams);
|
||||
if (response.code === 200) {
|
||||
pendingList.value = response.rows || [];
|
||||
pendingTotal.value = response.total || 0;
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取数据失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取待审批列表失败', error);
|
||||
ElMessage.error('获取数据失败,请稍后重试');
|
||||
} finally {
|
||||
pendingLoading.value = false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ✅ 调用真实API接口
|
||||
- ✅ 处理loading状态
|
||||
- ✅ 显示查询结果或错误提示
|
||||
|
||||
#### 修改点4:恢复查询和重置按钮功能
|
||||
```javascript
|
||||
// 查询按钮
|
||||
const handlePendingQuery = () => {
|
||||
pendingQueryParams.pageNum = 1;
|
||||
getPendingList();
|
||||
};
|
||||
|
||||
// 重置按钮
|
||||
const resetPendingQuery = () => {
|
||||
pendingQueryParams.pageNum = 1;
|
||||
pendingQueryParams.applicantName = undefined;
|
||||
pendingQueryParams.department = undefined;
|
||||
pendingQueryParams.destination = undefined;
|
||||
pendingQueryParams.approvalLevel = undefined;
|
||||
getPendingList();
|
||||
};
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ✅ 恢复正常的查询功能
|
||||
- ✅ 恢复正常的重置功能
|
||||
- ❌ 删除工作流跳转提示
|
||||
|
||||
#### 修改点5:添加功能说明
|
||||
```vue
|
||||
<el-col :span="1.5">
|
||||
<div class="info-text">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
<span>这里显示您提交的待审批申请</span>
|
||||
</div>
|
||||
</el-col>
|
||||
```
|
||||
|
||||
**修改说明**:
|
||||
- ✅ 在操作按钮区域添加功能说明
|
||||
- ✅ 明确告知用户这里只显示自己提交的申请
|
||||
- ✅ 添加对应的CSS样式
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI变化
|
||||
|
||||
### 修改前
|
||||
```
|
||||
[Alert提示框:功能迁移通知]
|
||||
[前往待办任务页面按钮]
|
||||
[Empty: 请使用标准工作流待办页面]
|
||||
```
|
||||
|
||||
### 修改后
|
||||
```
|
||||
[搜索条件]
|
||||
[刷新按钮] [ℹ️ 这里显示您提交的待审批申请]
|
||||
[数据表格:显示待审批申请列表]
|
||||
[分页组件]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 数据过滤逻辑
|
||||
|
||||
### 待审批选项卡
|
||||
```sql
|
||||
SELECT * FROM rs_overseas_application
|
||||
WHERE status = '0' -- 待审核状态
|
||||
AND applicant_id = {当前用户ID} -- 🔴 只查询当前用户提交的
|
||||
AND deleted = '0'
|
||||
AND tenant_id = {租户ID}
|
||||
ORDER BY apply_time DESC
|
||||
```
|
||||
|
||||
### 审批完成选项卡
|
||||
```sql
|
||||
SELECT * FROM rs_overseas_application
|
||||
WHERE status = '1' -- 已通过状态
|
||||
AND applicant_id = {当前用户ID} -- 🔴 只查询当前用户提交的
|
||||
AND deleted = '0'
|
||||
AND tenant_id = {租户ID}
|
||||
ORDER BY apply_time DESC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试场景1:待审批选项卡
|
||||
|
||||
**测试步骤**:
|
||||
1. 使用用户A登录系统
|
||||
2. 提交一个出国申请
|
||||
3. 访问"出国申请进度查询"菜单
|
||||
4. 切换到"待审批中"选项卡
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 能看到刚才提交的申请记录
|
||||
- ✅ 显示当前审批环节和审批人
|
||||
- ✅ 点击"查看详情"能查看审批历史
|
||||
|
||||
**测试步骤2**:
|
||||
1. 使用用户B登录系统
|
||||
2. 访问"出国申请进度查询"菜单
|
||||
3. 切换到"待审批中"选项卡
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 只能看到用户B提交的申请
|
||||
- ❌ 看不到用户A提交的申请
|
||||
|
||||
### 测试场景2:审批完成选项卡
|
||||
|
||||
**测试步骤**:
|
||||
1. 使用用户A登录系统
|
||||
2. 提交一个出国申请
|
||||
3. 完成全部审批流程
|
||||
4. 访问"出国申请进度查询"菜单
|
||||
5. 切换到"审批完成"选项卡
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 能看到刚才审批完成的申请(抄送)
|
||||
- ✅ 显示完整的审批历史记录
|
||||
- ✅ 能查看关联的出国记录信息
|
||||
|
||||
### 测试场景3:搜索功能
|
||||
|
||||
**测试步骤**:
|
||||
1. 在"待审批中"选项卡输入搜索条件
|
||||
2. 点击"搜索"按钮
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 能正常搜索并返回结果
|
||||
- ✅ 结果仍然只包含当前用户提交的申请
|
||||
|
||||
---
|
||||
|
||||
## 📊 修改文件清单
|
||||
|
||||
| 序号 | 文件类型 | 文件路径 | 修改内容 | 状态 |
|
||||
|------|---------|---------|---------|------|
|
||||
| 1 | 后端Service | `RsOverseasApprovalMonitorServiceImpl.java` | 添加当前用户过滤条件 | ✅ |
|
||||
| 2 | 后端Controller | `RsOverseasApprovalMonitorController.java` | 恢复pending接口功能 | ✅ |
|
||||
| 3 | 前端页面 | `monitor/index.vue` | 删除工作流提示,恢复表格显示 | ✅ |
|
||||
| 4 | 前端脚本 | `monitor/index.vue` | 恢复查询、重置功能 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 权限要求
|
||||
- 用户需要有 `rs:overseas:list` 权限才能访问此菜单
|
||||
- 用户需要有 `rs:overseas:query` 权限才能查看详情
|
||||
|
||||
### 2. 数据范围
|
||||
- **待审批选项卡**:只显示当前用户提交的待审核申请
|
||||
- **审批完成选项卡**:只显示当前用户提交的已完成申请
|
||||
- 不会显示其他用户提交的申请
|
||||
|
||||
### 3. 与其他模块的关系
|
||||
- **出国申请**:用户在这里提交申请
|
||||
- **出国审批**:审批人在这里审批申请
|
||||
- **出国记录**:审批完成后,申请人在这里查看出国记录
|
||||
- **出国申请进度查询**:申请人在这里查看申请进度(当前模块)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署说明
|
||||
|
||||
### 后端部署
|
||||
```bash
|
||||
cd backend
|
||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
||||
./stop-main.sh
|
||||
./start-main.sh
|
||||
```
|
||||
|
||||
### 前端部署
|
||||
```bash
|
||||
cd web
|
||||
npm run build
|
||||
# 将dist目录部署到nginx
|
||||
```
|
||||
|
||||
### 验证步骤
|
||||
1. 清除浏览器缓存
|
||||
2. 重新登录系统
|
||||
3. 访问"出国申请进度查询"菜单
|
||||
4. 确认功能正常
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验收标准
|
||||
|
||||
- [ ] 待审批选项卡只显示当前用户提交的待审批申请
|
||||
- [ ] 审批完成选项卡只显示当前用户提交的已完成审批申请
|
||||
- [ ] 搜索功能正常工作
|
||||
- [ ] 重置功能正常工作
|
||||
- [ ] 查看详情能显示审批历史
|
||||
- [ ] 不会显示其他用户的申请
|
||||
- [ ] UI显示正常,无工作流迁移提示
|
||||
|
||||
---
|
||||
|
||||
## 📞 问题反馈
|
||||
|
||||
如有问题,请检查:
|
||||
1. 后端日志:`backend/logs/sys-info.log`
|
||||
2. 浏览器控制台:F12查看网络请求和错误
|
||||
3. 确认用户权限配置正确
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v1.0
|
||||
**最后更新**:2025-10-29
|
||||
**修改人员**:Cursor AI
|
||||
|
||||
@@ -1,411 +0,0 @@
|
||||
# 出国申请审批环节命名标准分析报告
|
||||
|
||||
## 📋 问题发现
|
||||
|
||||
在分析代码时,发现系统中**存在两套不同的审批级别命名标准**:
|
||||
|
||||
1. **出国申请进度查询模块**:使用**3级审批**命名
|
||||
2. **出国申请审批模块**:使用**5级审批**命名
|
||||
|
||||
这导致**命名不一致**的问题。
|
||||
|
||||
---
|
||||
|
||||
## 🔍 详细分析
|
||||
|
||||
### 一、出国申请进度查询模块(monitor)
|
||||
|
||||
**文件位置**:`RsOverseasApprovalMonitorServiceImpl.java`
|
||||
|
||||
**当前定义**(第390-404行):
|
||||
```java
|
||||
private String getApprovalLevelText(String level) {
|
||||
if (level == null) {
|
||||
return "未开始";
|
||||
}
|
||||
switch (level) {
|
||||
case "1":
|
||||
return "部门主管审批";
|
||||
case "2":
|
||||
return "分管领导审批";
|
||||
case "3":
|
||||
return "校领导审批";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- ❌ 只定义了3个级别
|
||||
- ❌ Level 4 和 Level 5 会显示为"未知"
|
||||
- ❌ 命名与实际审批流程不符
|
||||
|
||||
---
|
||||
|
||||
### 二、出国申请审批模块(application)
|
||||
|
||||
**文件位置**:`RsOverseasApplicationServiceImpl.java`
|
||||
|
||||
**当前定义**(第1269-1283行):
|
||||
```java
|
||||
private String getApprovalLevelDescription(String approvalLevel) {
|
||||
switch (approvalLevel) {
|
||||
case "1":
|
||||
return "待部门负责人审批";
|
||||
case "2":
|
||||
return "待人事处审批";
|
||||
case "3":
|
||||
return "待纪委审批";
|
||||
case "4":
|
||||
return "待学院分管领导审批";
|
||||
case "5":
|
||||
return "待学院党委书记审批";
|
||||
default:
|
||||
return "待审批";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**流转逻辑**(第683-698行):
|
||||
```java
|
||||
/**
|
||||
* 获取下一级审批级别
|
||||
* 五级审批制度:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
|
||||
*/
|
||||
private String getNextApprovalLevel(String currentLevel) {
|
||||
switch (currentLevel) {
|
||||
case "1":
|
||||
return "2"; // 部门负责人 → 人事处
|
||||
case "2":
|
||||
return "3"; // 人事处 → 纪委
|
||||
case "3":
|
||||
return "4"; // 纪委 → 学院分管领导
|
||||
case "4":
|
||||
return "5"; // 学院分管领导 → 学院党委书记
|
||||
case "5":
|
||||
return null; // 学院党委书记 → 审批完成
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 标准审批级别定义
|
||||
|
||||
### 完整的五级审批制度
|
||||
|
||||
| 审批级别 | 数据库值 | 权限标识 | 审批环节名称 | 当前审批人显示 | 说明 |
|
||||
|---------|---------|---------|------------|--------------|------|
|
||||
| **Level 1** | `"1"` | `rs:overseas:approve1` | **部门负责人审批** | 待部门负责人审批 | 只能看到本部门的申请 |
|
||||
| **Level 2** | `"2"` | `rs:overseas:approve2` | **人事处审批** | 待人事处审批 | 可以看到所有部门的申请 |
|
||||
| **Level 3** | `"3"` | `rs:overseas:approve3` | **纪委审批** | 待纪委审批 | 可以看到所有部门的申请 |
|
||||
| **Level 4** | `"4"` | `rs:overseas:approve4` | **学院分管领导审批** | 待学院分管领导审批 | 可以看到所有部门的申请 |
|
||||
| **Level 5** | `"5"` | `rs:overseas:approve5` | **学院党委书记审批** | 待学院党委书记审批 | 可以看到所有部门的申请 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 审批流程完整流转
|
||||
|
||||
### 流程图
|
||||
```
|
||||
申请提交
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ Level 1: 部门负责人审批 │
|
||||
│ - 审批人:各部门负责人 │
|
||||
│ - 权限:rs:overseas:approve1 │
|
||||
│ - 范围:仅本部门申请 │
|
||||
└─────────────────────────────────┘
|
||||
↓ (审批通过)
|
||||
┌─────────────────────────────────┐
|
||||
│ Level 2: 人事处审批 │
|
||||
│ - 审批人:人事处工作人员 │
|
||||
│ - 权限:rs:overseas:approve2 │
|
||||
│ - 范围:所有部门申请 │
|
||||
└─────────────────────────────────┘
|
||||
↓ (审批通过)
|
||||
┌─────────────────────────────────┐
|
||||
│ Level 3: 纪委审批 │
|
||||
│ - 审批人:纪委工作人员 │
|
||||
│ - 权限:rs:overseas:approve3 │
|
||||
│ - 范围:所有部门申请 │
|
||||
└─────────────────────────────────┘
|
||||
↓ (审批通过)
|
||||
┌─────────────────────────────────┐
|
||||
│ Level 4: 学院分管领导审批 │
|
||||
│ - 审批人:学院分管领导 │
|
||||
│ - 权限:rs:overseas:approve4 │
|
||||
│ - 范围:所有部门申请 │
|
||||
└─────────────────────────────────┘
|
||||
↓ (审批通过)
|
||||
┌─────────────────────────────────┐
|
||||
│ Level 5: 学院党委书记审批 │
|
||||
│ - 审批人:学院党委书记 │
|
||||
│ - 权限:rs:overseas:approve5 │
|
||||
│ - 范围:所有部门申请 │
|
||||
└─────────────────────────────────┘
|
||||
↓ (审批通过)
|
||||
申请完成 → 生成出国记录
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 各字段的具体命名标准
|
||||
|
||||
### 1. **当前审批环节**(approval_level字段)
|
||||
|
||||
**数据库字段**:`approval_level VARCHAR(10)`
|
||||
|
||||
**标准命名**(用于显示):
|
||||
```java
|
||||
switch (approvalLevel) {
|
||||
case "1": return "部门负责人审批";
|
||||
case "2": return "人事处审批";
|
||||
case "3": return "纪委审批";
|
||||
case "4": return "学院分管领导审批";
|
||||
case "5": return "学院党委书记审批";
|
||||
default: return "未知";
|
||||
}
|
||||
```
|
||||
|
||||
### 2. **当前审批人**(current_approver字段)
|
||||
|
||||
**数据库字段**:`current_approver VARCHAR(100)`
|
||||
|
||||
**标准命名格式**:
|
||||
```java
|
||||
switch (approvalLevel) {
|
||||
case "1": return "待部门负责人审批";
|
||||
case "2": return "待人事处审批";
|
||||
case "3": return "待纪委审批";
|
||||
case "4": return "待学院分管领导审批";
|
||||
case "5": return "待学院党委书记审批";
|
||||
default: return "待审批";
|
||||
}
|
||||
```
|
||||
|
||||
**特殊情况**:
|
||||
- 如果是部门负责人级别,可能显示:`"待指定部门负责人"` 或 `"待{具体审批人姓名}审批"`
|
||||
- 其他级别通常显示:`"待{审批环节}审批"`
|
||||
|
||||
### 3. **审批进度**(用于进度查询)
|
||||
|
||||
**显示格式**:`"已审批级别数/总级别数"`
|
||||
|
||||
**示例**:
|
||||
- `"0/5"` - 未开始审批
|
||||
- `"1/5"` - 部门负责人已审批
|
||||
- `"2/5"` - 人事处已审批
|
||||
- `"3/5"` - 纪委已审批
|
||||
- `"4/5"` - 学院分管领导已审批
|
||||
- `"5/5"` - 全部审批完成
|
||||
|
||||
---
|
||||
|
||||
## 🔧 数据库定义
|
||||
|
||||
### rs_overseas_application表字段
|
||||
|
||||
```sql
|
||||
-- 审批级别字段
|
||||
`approval_level` varchar(10) DEFAULT '1'
|
||||
COMMENT '当前审批级别:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记'
|
||||
|
||||
-- 当前审批人字段
|
||||
`current_approver` varchar(100) DEFAULT NULL
|
||||
COMMENT '当前审批人姓名或环节描述'
|
||||
|
||||
-- 当前审批人ID字段(冗余字段,仅供参考)
|
||||
`current_approver_id` bigint DEFAULT NULL
|
||||
COMMENT '当前审批人ID(冗余字段)'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 前端显示示例
|
||||
|
||||
### 待审批列表显示
|
||||
|
||||
| 申请人 | 所属部门 | 当前审批环节 | 当前审批人 | 审批进度 |
|
||||
|--------|---------|-------------|-----------|---------|
|
||||
| 张三 | 教务处 | 部门负责人审批 | 待部门负责人审批 | 0% (0/5) |
|
||||
| 李四 | 人事处 | 人事处审批 | 待人事处审批 | 20% (1/5) |
|
||||
| 王五 | 学工处 | 纪委审批 | 待纪委审批 | 40% (2/5) |
|
||||
| 赵六 | 后勤处 | 学院分管领导审批 | 待学院分管领导审批 | 60% (3/5) |
|
||||
| 钱七 | 财务处 | 学院党委书记审批 | 待学院党委书记审批 | 80% (4/5) |
|
||||
|
||||
### 审批历史记录显示
|
||||
|
||||
```
|
||||
📋 审批历史
|
||||
|
||||
✅ 第1级:部门负责人审批
|
||||
审批人:张主任
|
||||
审批时间:2025-10-27 10:30:25
|
||||
审批结果:通过
|
||||
审批意见:同意出国交流
|
||||
|
||||
✅ 第2级:人事处审批
|
||||
审批人:李处长
|
||||
审批时间:2025-10-27 14:20:15
|
||||
审批结果:通过
|
||||
审批意见:人事档案无问题,同意
|
||||
|
||||
⏳ 第3级:纪委审批
|
||||
状态:待审批
|
||||
|
||||
⏸️ 第4级:学院分管领导审批
|
||||
状态:未到审批阶段
|
||||
|
||||
⏸️ 第5级:学院党委书记审批
|
||||
状态:未到审批阶段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 当前存在的问题
|
||||
|
||||
### 问题1:命名不统一
|
||||
|
||||
**问题描述**:
|
||||
- `RsOverseasApprovalMonitorServiceImpl.java` 中只定义了3个级别
|
||||
- 与实际的5级审批流程不符
|
||||
|
||||
**影响范围**:
|
||||
- 出国申请进度查询菜单的"当前审批环节"显示
|
||||
- Level 4 和 Level 5 会显示为"未知"
|
||||
|
||||
**解决方案**:
|
||||
需要修改 `getApprovalLevelText()` 方法,添加完整的5级定义
|
||||
|
||||
### 问题2:总审批级别数设置错误
|
||||
|
||||
**问题位置**:`RsOverseasApprovalMonitorServiceImpl.java` 第345-346行
|
||||
|
||||
```java
|
||||
// 总审批级别数(假设最多3级:部门主管、分管领导、校领导)
|
||||
vo.setTotalLevels(3);
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 实际是5级审批,但这里设置为3级
|
||||
- 导致审批进度显示错误(如:2/3 而不是 2/5)
|
||||
|
||||
**影响**:
|
||||
- 审批进度百分比计算错误
|
||||
- 进度条显示不准确
|
||||
|
||||
---
|
||||
|
||||
## ✅ 建议的标准命名
|
||||
|
||||
### 统一的命名标准
|
||||
|
||||
| 级别 | 简称 | 完整名称 | 待审批显示 | 已审批显示 |
|
||||
|------|------|---------|-----------|-----------|
|
||||
| 1 | 部门负责人 | 部门负责人审批 | 待部门负责人审批 | 部门负责人已审批 |
|
||||
| 2 | 人事处 | 人事处审批 | 待人事处审批 | 人事处已审批 |
|
||||
| 3 | 纪委 | 纪委审批 | 待纪委审批 | 纪委已审批 |
|
||||
| 4 | 学院分管领导 | 学院分管领导审批 | 待学院分管领导审批 | 学院分管领导已审批 |
|
||||
| 5 | 学院党委书记 | 学院党委书记审批 | 待学院党委书记审批 | 学院党委书记已审批 |
|
||||
|
||||
### 代码实现建议
|
||||
|
||||
```java
|
||||
/**
|
||||
* 获取审批级别完整名称(用于"当前审批环节"列)
|
||||
*/
|
||||
private String getApprovalLevelText(String level) {
|
||||
if (level == null) {
|
||||
return "未开始";
|
||||
}
|
||||
switch (level) {
|
||||
case "1":
|
||||
return "部门负责人审批";
|
||||
case "2":
|
||||
return "人事处审批";
|
||||
case "3":
|
||||
return "纪委审批";
|
||||
case "4":
|
||||
return "学院分管领导审批";
|
||||
case "5":
|
||||
return "学院党委书记审批";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前审批人显示文本(用于"当前审批人"列)
|
||||
*/
|
||||
private String getCurrentApproverText(String level) {
|
||||
if (level == null) {
|
||||
return "未指定";
|
||||
}
|
||||
switch (level) {
|
||||
case "1":
|
||||
return "待部门负责人审批";
|
||||
case "2":
|
||||
return "待人事处审批";
|
||||
case "3":
|
||||
return "待纪委审批";
|
||||
case "4":
|
||||
return "待学院分管领导审批";
|
||||
case "5":
|
||||
return "待学院党委书记审批";
|
||||
default:
|
||||
return "待审批";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 总结
|
||||
|
||||
### 当前审批级别定义
|
||||
|
||||
**完整的五级审批体系**:
|
||||
|
||||
1. **Level 1 - 部门负责人审批**
|
||||
- 权限:`rs:overseas:approve1`
|
||||
- 范围:仅本部门申请
|
||||
- 显示:部门负责人审批
|
||||
|
||||
2. **Level 2 - 人事处审批**
|
||||
- 权限:`rs:overseas:approve2`
|
||||
- 范围:所有部门申请
|
||||
- 显示:人事处审批
|
||||
|
||||
3. **Level 3 - 纪委审批**
|
||||
- 权限:`rs:overseas:approve3`
|
||||
- 范围:所有部门申请
|
||||
- 显示:纪委审批
|
||||
|
||||
4. **Level 4 - 学院分管领导审批**
|
||||
- 权限:`rs:overseas:approve4`
|
||||
- 范围:所有部门申请
|
||||
- 显示:学院分管领导审批
|
||||
|
||||
5. **Level 5 - 学院党委书记审批**
|
||||
- 权限:`rs:overseas:approve5`
|
||||
- 范围:所有部门申请
|
||||
- 显示:学院党委书记审批
|
||||
|
||||
### 关键注意事项
|
||||
|
||||
- ✅ 系统使用**5级审批制度**
|
||||
- ✅ 只有**部门负责人(Level 1)**需要部门过滤
|
||||
- ✅ **Level 2-5** 可以看到所有部门的申请
|
||||
- ⚠️ 出国申请进度查询模块存在命名不一致问题
|
||||
- ⚠️ 总审批级别数应该是5而不是3
|
||||
|
||||
---
|
||||
|
||||
**分析完成时间**:2025-10-29
|
||||
**分析人员**:Cursor AI
|
||||
**文档版本**:v1.0
|
||||
|
||||
@@ -1,286 +0,0 @@
|
||||
# 出国申请进度查询 - 审批级别修复说明
|
||||
|
||||
## 🔧 修复时间
|
||||
2025-10-29
|
||||
|
||||
## 🎯 修复目标
|
||||
将出国申请进度查询模块从3级审批改为完整的5级审批,与实际审批流程保持一致。
|
||||
|
||||
---
|
||||
|
||||
## 📝 修复内容
|
||||
|
||||
### 修复1:更新审批级别文本定义
|
||||
|
||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
||||
**方法**:`getApprovalLevelText(String level)`
|
||||
|
||||
#### 修复前
|
||||
```java
|
||||
switch (level) {
|
||||
case "1":
|
||||
return "部门主管审批";
|
||||
case "2":
|
||||
return "分管领导审批";
|
||||
case "3":
|
||||
return "校领导审批";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- ❌ Level 4 和 Level 5 显示为"未知"
|
||||
- ❌ 命名与实际流程不符
|
||||
|
||||
#### 修复后
|
||||
```java
|
||||
/**
|
||||
* 获取审批级别文本
|
||||
* 五级审批制度:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
|
||||
*/
|
||||
switch (level) {
|
||||
case "1":
|
||||
return "部门负责人审批";
|
||||
case "2":
|
||||
return "人事处审批";
|
||||
case "3":
|
||||
return "纪委审批";
|
||||
case "4":
|
||||
return "学院分管领导审批";
|
||||
case "5":
|
||||
return "学院党委书记审批";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
```
|
||||
|
||||
**改进**:
|
||||
- ✅ 支持完整的5级审批
|
||||
- ✅ 命名与实际流程一致
|
||||
- ✅ 添加了详细的注释说明
|
||||
|
||||
---
|
||||
|
||||
### 修复2:更新总审批级别数
|
||||
|
||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
||||
**方法**:`fillApprovalProgress()`
|
||||
|
||||
#### 修复前
|
||||
```java
|
||||
// 总审批级别数(假设最多3级:部门主管、分管领导、校领导)
|
||||
vo.setTotalLevels(3);
|
||||
|
||||
// 未开始审批
|
||||
vo.setApprovedLevels(0);
|
||||
vo.setTotalLevels(3);
|
||||
vo.setApprovalProgress("0/3");
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- ❌ 总级别数设置为3
|
||||
- ❌ 审批进度显示错误(如:2/3 而不是 2/5)
|
||||
- ❌ 进度百分比计算错误
|
||||
|
||||
#### 修复后
|
||||
```java
|
||||
// 总审批级别数(五级审批制度:部门负责人、人事处、纪委、学院分管领导、学院党委书记)
|
||||
vo.setTotalLevels(5);
|
||||
|
||||
// 未开始审批
|
||||
vo.setApprovedLevels(0);
|
||||
vo.setTotalLevels(5);
|
||||
vo.setApprovalProgress("0/5");
|
||||
```
|
||||
|
||||
**改进**:
|
||||
- ✅ 总级别数正确设置为5
|
||||
- ✅ 审批进度显示正确
|
||||
- ✅ 进度百分比计算准确
|
||||
|
||||
---
|
||||
|
||||
### 修复3:更新统计信息定义
|
||||
|
||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
||||
**方法**:`getApprovalStatistics()`
|
||||
|
||||
#### 修复前
|
||||
```java
|
||||
// 2. 各审批级别的待审批数量
|
||||
Map<String, Long> pendingByLevel = new HashMap<>();
|
||||
pendingByLevel.put("level1", countByStatusAndLevel("0", "1", tenantId)); // 部门主管
|
||||
pendingByLevel.put("level2", countByStatusAndLevel("0", "2", tenantId)); // 分管领导
|
||||
pendingByLevel.put("level3", countByStatusAndLevel("0", "3", tenantId)); // 校领导
|
||||
statistics.put("pendingByLevel", pendingByLevel);
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- ❌ 只统计3个级别
|
||||
- ❌ Level 4 和 Level 5 的统计缺失
|
||||
|
||||
#### 修复后
|
||||
```java
|
||||
// 2. 各审批级别的待审批数量(五级审批制度)
|
||||
Map<String, Long> pendingByLevel = new HashMap<>();
|
||||
pendingByLevel.put("level1", countByStatusAndLevel("0", "1", tenantId)); // 部门负责人
|
||||
pendingByLevel.put("level2", countByStatusAndLevel("0", "2", tenantId)); // 人事处
|
||||
pendingByLevel.put("level3", countByStatusAndLevel("0", "3", tenantId)); // 纪委
|
||||
pendingByLevel.put("level4", countByStatusAndLevel("0", "4", tenantId)); // 学院分管领导
|
||||
pendingByLevel.put("level5", countByStatusAndLevel("0", "5", tenantId)); // 学院党委书记
|
||||
statistics.put("pendingByLevel", pendingByLevel);
|
||||
```
|
||||
|
||||
**改进**:
|
||||
- ✅ 统计所有5个级别
|
||||
- ✅ 统计数据完整准确
|
||||
- ✅ 命名规范一致
|
||||
|
||||
---
|
||||
|
||||
## 📊 修复效果对比
|
||||
|
||||
### 1. 当前审批环节显示
|
||||
|
||||
| 审批级别 | 修复前 | 修复后 | 状态 |
|
||||
|---------|--------|--------|------|
|
||||
| Level 1 | 部门主管审批 | 部门负责人审批 | ✅ 已修正 |
|
||||
| Level 2 | 分管领导审批 | 人事处审批 | ✅ 已修正 |
|
||||
| Level 3 | 校领导审批 | 纪委审批 | ✅ 已修正 |
|
||||
| Level 4 | **未知** ❌ | 学院分管领导审批 | ✅ 已修复 |
|
||||
| Level 5 | **未知** ❌ | 学院党委书记审批 | ✅ 已修复 |
|
||||
|
||||
### 2. 审批进度显示
|
||||
|
||||
| 已审批级别 | 修复前 | 修复后 | 进度百分比(修复前) | 进度百分比(修复后) |
|
||||
|-----------|--------|--------|-------------------|-------------------|
|
||||
| 0级 | 0/3 | 0/5 | 0% | 0% |
|
||||
| 1级 | 1/3 | 1/5 | 33% ❌ | 20% ✅ |
|
||||
| 2级 | 2/3 | 2/5 | 67% ❌ | 40% ✅ |
|
||||
| 3级 | 3/3 | 3/5 | 100% ❌ | 60% ✅ |
|
||||
| 4级 | - | 4/5 | - | 80% ✅ |
|
||||
| 5级 | - | 5/5 | - | 100% ✅ |
|
||||
|
||||
### 3. 统计信息
|
||||
|
||||
| 统计项 | 修复前 | 修复后 |
|
||||
|--------|--------|--------|
|
||||
| 统计级别数 | 3个 | 5个 ✅ |
|
||||
| Level 4统计 | ❌ 缺失 | ✅ 包含 |
|
||||
| Level 5统计 | ❌ 缺失 | ✅ 包含 |
|
||||
|
||||
---
|
||||
|
||||
## 🎨 前端显示效果
|
||||
|
||||
### 待审批列表示例
|
||||
|
||||
#### 修复前
|
||||
| 申请人 | 当前审批环节 | 审批进度 |
|
||||
|--------|-------------|---------|
|
||||
| 张三 | 部门主管审批 | 0/3 (0%) |
|
||||
| 李四 | 分管领导审批 | 1/3 (33%) |
|
||||
| 王五 | 校领导审批 | 2/3 (67%) |
|
||||
| 赵六 | **未知** ❌ | 3/3 (100%) ❌ |
|
||||
| 钱七 | **未知** ❌ | - |
|
||||
|
||||
#### 修复后
|
||||
| 申请人 | 当前审批环节 | 审批进度 |
|
||||
|--------|-------------|---------|
|
||||
| 张三 | 部门负责人审批 | 0/5 (0%) |
|
||||
| 李四 | 人事处审批 | 1/5 (20%) |
|
||||
| 王五 | 纪委审批 | 2/5 (40%) |
|
||||
| 赵六 | 学院分管领导审批 ✅ | 3/5 (60%) ✅ |
|
||||
| 钱七 | 学院党委书记审批 ✅ | 4/5 (80%) ✅ |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 修复验证
|
||||
|
||||
### 验证步骤
|
||||
|
||||
1. **重新编译后端**
|
||||
```bash
|
||||
cd backend
|
||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
||||
```
|
||||
|
||||
2. **重启后端服务**
|
||||
```bash
|
||||
./stop-main.sh
|
||||
./start-main.sh
|
||||
```
|
||||
|
||||
3. **登录系统测试**
|
||||
- 访问"出国申请进度查询"菜单
|
||||
- 查看待审批中和审批完成选项卡
|
||||
- 验证以下内容:
|
||||
|
||||
#### 验证项1:当前审批环节显示
|
||||
- [ ] Level 1 显示为"部门负责人审批"
|
||||
- [ ] Level 2 显示为"人事处审批"
|
||||
- [ ] Level 3 显示为"纪委审批"
|
||||
- [ ] Level 4 显示为"学院分管领导审批"(不再是"未知")
|
||||
- [ ] Level 5 显示为"学院党委书记审批"(不再是"未知")
|
||||
|
||||
#### 验证项2:审批进度显示
|
||||
- [ ] 未审批显示:0/5 (0%)
|
||||
- [ ] 部门负责人已审批:1/5 (20%)
|
||||
- [ ] 人事处已审批:2/5 (40%)
|
||||
- [ ] 纪委已审批:3/5 (60%)
|
||||
- [ ] 学院分管领导已审批:4/5 (80%)
|
||||
- [ ] 学院党委书记已审批:5/5 (100%)
|
||||
|
||||
#### 验证项3:统计信息
|
||||
- [ ] 统计信息包含所有5个级别
|
||||
- [ ] Level 4 和 Level 5 的统计数据正常显示
|
||||
|
||||
---
|
||||
|
||||
## 📋 修改文件清单
|
||||
|
||||
| 序号 | 文件 | 修改内容 | 行数 | 状态 |
|
||||
|------|------|---------|------|------|
|
||||
| 1 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新审批级别文本定义 | 387-404 | ✅ |
|
||||
| 2 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新总审批级别数(已审批) | 345-346 | ✅ |
|
||||
| 3 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新总审批级别数(未审批) | 358-360 | ✅ |
|
||||
| 4 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新统计信息定义 | 152-157 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 修复总结
|
||||
|
||||
### 修复前的问题
|
||||
- ❌ 只支持3级审批,Level 4和5显示"未知"
|
||||
- ❌ 总审批级别数错误(3而不是5)
|
||||
- ❌ 审批进度百分比计算错误
|
||||
- ❌ 统计信息不完整
|
||||
|
||||
### 修复后的改进
|
||||
- ✅ 完整支持5级审批流程
|
||||
- ✅ 所有审批级别正确显示
|
||||
- ✅ 审批进度计算准确
|
||||
- ✅ 统计信息完整
|
||||
- ✅ 与实际业务流程一致
|
||||
|
||||
### 影响范围
|
||||
- ✅ 仅修改出国申请进度查询模块
|
||||
- ✅ 不影响其他模块
|
||||
- ✅ 向后兼容
|
||||
|
||||
---
|
||||
|
||||
## 📞 相关文档
|
||||
|
||||
- 详细分析报告:`审批环节命名标准分析报告.md`
|
||||
- 功能修改说明:`出国申请进度查询功能修改说明.md`
|
||||
- 权限修改说明:`权限修改说明.md`
|
||||
|
||||
---
|
||||
|
||||
**修复状态**:✅ 已完成
|
||||
**需要操作**:重新编译和重启后端服务
|
||||
**版本**:v1.2
|
||||
**最后更新**:2025-10-29
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
# 出国申请进度查询权限修改说明
|
||||
|
||||
## 📋 修改时间
|
||||
2025-10-29
|
||||
|
||||
## 🎯 修改目的
|
||||
将"出国申请进度查询"从"只能查看自己提交的申请"改为"拥有权限的用户可以查看所有申请"
|
||||
|
||||
---
|
||||
|
||||
## 📝 修改内容
|
||||
|
||||
### 1. 后端Service层修改
|
||||
|
||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
||||
|
||||
#### 修改前
|
||||
```java
|
||||
// 只查询当前用户提交的申请
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
wrapper.eq("applicant_id", currentUserId);
|
||||
```
|
||||
|
||||
#### 修改后
|
||||
```java
|
||||
// 不再限制申请人,拥有权限的用户可以看到所有数据
|
||||
// 已删除 applicant_id 过滤条件
|
||||
```
|
||||
|
||||
**影响方法**:
|
||||
- ✅ `queryPendingList()` - 待审批列表
|
||||
- ✅ `queryCompletedList()` - 已完成列表
|
||||
|
||||
---
|
||||
|
||||
### 2. 后端Controller层修改
|
||||
|
||||
**文件**:`RsOverseasApprovalMonitorController.java`
|
||||
|
||||
#### 修改点
|
||||
- 更新注释说明:从"当前用户提交的"改为"所有"
|
||||
- 更新日志输出:从"当前用户的"改为"所有"
|
||||
|
||||
---
|
||||
|
||||
### 3. 前端页面修改
|
||||
|
||||
**文件**:`monitor/index.vue`
|
||||
|
||||
#### 修改点
|
||||
- 提示文字:从"这里显示您提交的"改为"这里显示所有"
|
||||
|
||||
---
|
||||
|
||||
## 🔍 权限控制
|
||||
|
||||
### 访问控制
|
||||
- **权限标识**:`rs:overseas:list`
|
||||
- **控制级别**:拥有此权限的用户可以查看所有数据
|
||||
- **数据范围**:不再限制申请人,显示所有待审批和已完成的申请
|
||||
|
||||
### 查询逻辑
|
||||
```sql
|
||||
-- 待审批列表
|
||||
SELECT * FROM rs_overseas_application
|
||||
WHERE status = '0' -- 待审核状态
|
||||
AND deleted = '0'
|
||||
AND tenant_id = {租户ID}
|
||||
ORDER BY apply_time DESC
|
||||
|
||||
-- 已完成列表
|
||||
SELECT * FROM rs_overseas_application
|
||||
WHERE status = '1' -- 已通过状态
|
||||
AND deleted = '0'
|
||||
AND tenant_id = {租户ID}
|
||||
ORDER BY apply_time DESC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 修改对比
|
||||
|
||||
| 项目 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| 数据范围 | 当前用户提交的申请 | 所有申请 |
|
||||
| 权限要求 | rs:overseas:list | rs:overseas:list(不变) |
|
||||
| 查询条件 | applicant_id = 当前用户ID | 无申请人限制 |
|
||||
| 用户体验 | 只能看到自己的 | 可以看到所有 |
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试场景1:查看待审批列表
|
||||
|
||||
**测试步骤**:
|
||||
1. 用户A登录系统(拥有 rs:overseas:list 权限)
|
||||
2. 访问"出国申请进度查询"菜单
|
||||
3. 切换到"待审批中"选项卡
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 能看到所有用户提交的待审批申请
|
||||
- ✅ 包括用户A自己提交的和其他用户提交的
|
||||
|
||||
### 测试场景2:查看已完成列表
|
||||
|
||||
**测试步骤**:
|
||||
1. 用户A登录系统(拥有 rs:overseas:list 权限)
|
||||
2. 访问"出国申请进度查询"菜单
|
||||
3. 切换到"审批完成"选项卡
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 能看到所有已完成审批的申请
|
||||
- ✅ 不限制申请人
|
||||
|
||||
### 测试场景3:无权限用户
|
||||
|
||||
**测试步骤**:
|
||||
1. 用户B登录系统(没有 rs:overseas:list 权限)
|
||||
2. 尝试访问"出国申请进度查询"菜单
|
||||
|
||||
**预期结果**:
|
||||
- ❌ 无法访问该菜单(权限控制在菜单级别)
|
||||
|
||||
---
|
||||
|
||||
## 📊 修改文件清单
|
||||
|
||||
| 序号 | 文件类型 | 文件路径 | 修改内容 | 状态 |
|
||||
|------|---------|---------|---------|------|
|
||||
| 1 | 后端Service | `RsOverseasApprovalMonitorServiceImpl.java` | 删除申请人ID过滤 | ✅ |
|
||||
| 2 | 后端Controller | `RsOverseasApprovalMonitorController.java` | 更新注释和日志 | ✅ |
|
||||
| 3 | 前端页面 | `monitor/index.vue` | 更新提示文字 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 权限配置
|
||||
- 确保只有需要查看所有数据的用户才有 `rs:overseas:list` 权限
|
||||
- 如果需要区分不同权限级别,建议创建新的权限标识
|
||||
|
||||
### 2. 数据安全
|
||||
- 所有查询仍然受租户ID限制
|
||||
- 不会跨租户显示数据
|
||||
|
||||
### 3. 性能考虑
|
||||
- 查询所有数据时,建议使用搜索条件过滤
|
||||
- 分页显示可以减轻服务器压力
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署说明
|
||||
|
||||
### 后端部署
|
||||
```bash
|
||||
cd backend
|
||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
||||
./stop-main.sh
|
||||
./start-main.sh
|
||||
```
|
||||
|
||||
### 前端(如已运行无需重启)
|
||||
```bash
|
||||
# 前端代码修改后会自动热更新
|
||||
# 如需打包部署:
|
||||
cd web
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 相关文档
|
||||
|
||||
- 详细修改说明:`出国申请进度查询功能修改说明.md`
|
||||
- 快速参考:`修改总结-快速参考.md`
|
||||
|
||||
---
|
||||
|
||||
**修改状态**:✅ 已完成
|
||||
**生效方式**:需要重启后端服务
|
||||
**版本**:v1.1
|
||||
|
||||
Reference in new issue
Block a user