“1028审批权限修改v0.1”
This commit is contained in:
1 parent
851ebe86e4
commit
76ee97a90c
21 files changed
+2054
-1370
No files matched your search
@@ -152,11 +152,12 @@ export interface OverseasApplicationForm {
|
||||
approvalStatus?: string; // 工作流审批状态
|
||||
}
|
||||
|
||||
// 审批请求类型
|
||||
// 审批请求类型(支持标准工作流模式)
|
||||
export interface ApprovalRequest {
|
||||
applicationId: number | string;
|
||||
approvalStatus: string;
|
||||
approvalOpinion?: string;
|
||||
taskId?: string; // 🔴 新增:工作流任务ID,支持标准工作流模式
|
||||
}
|
||||
|
||||
// 出国记录查询参数类型
|
||||
@@ -327,15 +328,30 @@ export function listMyOverseasApplication(query: OverseasApplicationQuery) {
|
||||
});
|
||||
}
|
||||
|
||||
// 查询待审批的申请列表
|
||||
export function listPendingOverseasApplication(query: OverseasApplicationQuery) {
|
||||
// 查询当前用户的待审批申请列表(五级审批模式)
|
||||
export function listPendingOverseasApplication() {
|
||||
return request({
|
||||
url: '/rs/overseas/application/pending/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 🚨 标准化改造:删除自定义待审批查询API
|
||||
//
|
||||
// 原因:出国申请工作流应使用标准工作流模式
|
||||
// 用户应通过工作流API pageByTaskWait() 获取待办任务
|
||||
// 而不是调用自定义的业务API
|
||||
//
|
||||
// 删除的API:listPendingOverseasApplication()
|
||||
//
|
||||
// 标准工作流模式:
|
||||
// 1. 导入工作流API:import { pageByTaskWait } from '@/api/workflow/task'
|
||||
// 2. 调用标准API:pageByTaskWait(queryParams)
|
||||
// 3. 获得待办任务列表,包含businessId
|
||||
// 4. 点击办理跳转:/rs/overseas/application/edit?taskId=xxx&applicationId=businessId
|
||||
// ========================================
|
||||
|
||||
// 根据状态查询申请列表
|
||||
export function listOverseasApplicationByStatus(status: string) {
|
||||
return request({
|
||||
@@ -990,3 +1006,27 @@ export function getItineraryChangeHistory(recordId: string | number) {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 动态审批人获取API ====================
|
||||
|
||||
/**
|
||||
* 根据申请人ID获取部门审批人ID
|
||||
* @param applicantId 申请人ID
|
||||
*/
|
||||
export function getDepartmentApproverId(applicantId: string | number): Promise<{ data: number }> {
|
||||
return request({
|
||||
url: `/rs/overseas/application/getDepartmentApproverId/${applicantId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据申请人ID获取人事处审批人列表
|
||||
* @param applicantId 申请人ID
|
||||
*/
|
||||
export function getHrApprovers(applicantId: string | number): Promise<{ data: number[] }> {
|
||||
return request({
|
||||
url: `/rs/overseas/application/getHrApprovers/${applicantId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -78,14 +78,7 @@
|
||||
<el-tag :type="getStatusTagType(scope.row.status)">{{ scope.row.statusText }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工作流状态" align="center" prop="approvalStatusText" min-width="110">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.approvalStatus" :type="getApprovalStatusTagType(scope.row.approvalStatus)">
|
||||
{{ scope.row.approvalStatusText || '-' }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="info">未启动</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 移除工作流状态列 - 避免与申请状态重复,简化用户界面 -->
|
||||
<el-table-column label="申请时间" align="center" prop="applyTime" min-width="160" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" width="240">
|
||||
<template #default="scope">
|
||||
@@ -469,7 +462,9 @@
|
||||
destroy-on-close
|
||||
>
|
||||
<template #default>
|
||||
<!-- 工作流按钮组件 -->
|
||||
<!-- 工作流按钮组件 - 在弹窗模式下隐藏,使用footer按钮 -->
|
||||
<!-- 注释掉,因为弹窗模式下使用footer中的按钮 -->
|
||||
<!--
|
||||
<approvalButton
|
||||
v-if="state.pageType"
|
||||
@submitForm="submitForm"
|
||||
@@ -479,6 +474,7 @@
|
||||
:status="state.applicationForm.approvalStatus"
|
||||
:pageType="state.pageType"
|
||||
/>
|
||||
-->
|
||||
|
||||
<div style="max-height: 65vh; overflow-y: auto; padding-right: 10px;">
|
||||
<el-form ref="applicationFormRef" :model="applicationForm" :rules="applicationRules" label-width="120px">
|
||||
@@ -572,8 +568,8 @@
|
||||
<!-- 第五排:前往国家、出境事由 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="前往国家" prop="destinationCountry">
|
||||
<el-input v-model="applicationForm.destinationCountry" placeholder="请输入前往国家" />
|
||||
<el-form-item label="前往国家或地区" prop="destinationCountry">
|
||||
<el-input v-model="applicationForm.destinationCountry" placeholder="请输入前往国家或地区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
@@ -589,7 +585,7 @@
|
||||
<!-- 探亲时显示的额外字段 -->
|
||||
<el-row :gutter="20" v-if="applicationForm.exitReason === '探亲'">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="探望对象关系" prop="visitRelationship">
|
||||
<el-form-item label="探望对象姓名及与申请者关系" prop="visitRelationship">
|
||||
<el-input
|
||||
v-model="applicationForm.visitRelationship"
|
||||
placeholder="请输入探望对象姓名及与申请者关系"
|
||||
@@ -711,10 +707,10 @@
|
||||
style="width: 60%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="应急联系人及电话" prop="emergencyContact" label-width="160px" style="margin-left: 10%;">
|
||||
<el-form-item label="应急联系人及联系电话" prop="emergencyContact" label-width="160px" style="margin-left: 10%;">
|
||||
<el-input
|
||||
v-model="applicationForm.emergencyContact"
|
||||
placeholder="请输入应急联系人及电话"
|
||||
placeholder="请输入应急联系人及联系电话"
|
||||
style="width: 90%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -1233,9 +1229,11 @@
|
||||
<el-table v-loading="approvalHistoryLoading" :data="approvalHistoryList" border style="width: 100%">
|
||||
<el-table-column label="审批级别" align="center" prop="approvalLevel" width="100">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.approvalLevel === '1'">部门主管</span>
|
||||
<span v-else-if="scope.row.approvalLevel === '2'">分管领导</span>
|
||||
<span v-else-if="scope.row.approvalLevel === '3'">校领导</span>
|
||||
<span v-if="scope.row.approvalLevel === '1'">部门负责人</span>
|
||||
<span v-else-if="scope.row.approvalLevel === '2'">人事处</span>
|
||||
<span v-else-if="scope.row.approvalLevel === '3'">纪委</span>
|
||||
<span v-else-if="scope.row.approvalLevel === '4'">学院分管领导</span>
|
||||
<span v-else-if="scope.row.approvalLevel === '5'">学院党委书记</span>
|
||||
<span v-else>{{ scope.row.approvalLevel }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -1279,7 +1277,7 @@
|
||||
<approvalRecord
|
||||
ref="approvalRecordRef"
|
||||
:businessId="state.applicationForm.applicationId"
|
||||
flowCode="overseas-application"
|
||||
flowCode="dyjs-cgsq"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1307,6 +1305,7 @@ import {
|
||||
validateApplicationPrerequisites,
|
||||
listOverseasAttachments,
|
||||
uploadOverseasAttachment,
|
||||
getDepartmentApproverId,
|
||||
deleteOverseasAttachment,
|
||||
getItineraryChangeHistory,
|
||||
type OverseasApplicationQuery,
|
||||
@@ -1413,7 +1412,7 @@ const state = reactive({
|
||||
department: [{ required: true, message: '现工作部门不能为空', trigger: 'blur' }],
|
||||
professionalTitle: [{ required: true, message: '职称不能为空', trigger: 'blur' }],
|
||||
position: [{ required: true, message: '职务不能为空', trigger: 'blur' }],
|
||||
destinationCountry: [{ required: true, message: '前往国家不能为空', trigger: 'blur' }],
|
||||
destinationCountry: [{ required: true, message: '前往国家或地区不能为空', trigger: 'blur' }],
|
||||
exitReason: [{ required: true, message: '出境事由不能为空', trigger: 'change' }],
|
||||
startDate: [{ required: true, message: '出发日期不能为空', trigger: 'change' }],
|
||||
endDate: [{ required: true, message: '返回日期不能为空', trigger: 'change' }],
|
||||
@@ -1421,7 +1420,7 @@ const state = reactive({
|
||||
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
emergencyContact: [{ required: true, message: '应急联系人及电话不能为空', trigger: 'blur' }]
|
||||
emergencyContact: [{ required: true, message: '应急联系人及联系电话不能为空', trigger: 'blur' }]
|
||||
} as any,
|
||||
|
||||
// 查看详情
|
||||
@@ -1678,13 +1677,12 @@ const resetRecordQuery = () => {
|
||||
|
||||
// 新增登记
|
||||
const handleAddApplication = () => {
|
||||
// 跳转到新增申请页面
|
||||
proxy.$router.push({
|
||||
path: '/rs/overseas/overseas-form',
|
||||
query: {
|
||||
type: 'add'
|
||||
}
|
||||
});
|
||||
// 显示文件通知弹窗
|
||||
state.noticeDialog.visible = true;
|
||||
state.countdown = 5;
|
||||
state.countdownActive = true;
|
||||
state.readCompleted = false;
|
||||
startCountdown();
|
||||
};
|
||||
|
||||
// 开始倒计时
|
||||
@@ -1721,14 +1719,17 @@ const handleCloseNotice = () => {
|
||||
|
||||
// 编辑申请
|
||||
const handleUpdateApplication = async (row: OverseasApplicationVO) => {
|
||||
// 跳转到编辑申请页面
|
||||
proxy.$router.push({
|
||||
path: '/rs/overseas/overseas-form',
|
||||
query: {
|
||||
type: 'edit',
|
||||
id: row.applicationId
|
||||
}
|
||||
});
|
||||
try {
|
||||
// 加载申请详情
|
||||
const response = await getOverseasApplication(row.applicationId);
|
||||
state.applicationForm = response.data;
|
||||
state.applicationDialog.title = '编辑因私出国(境)申请登记报备表';
|
||||
state.pageType = 'edit';
|
||||
state.applicationDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('加载申请详情失败:', error);
|
||||
ElMessage.error('加载申请详情失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 查看申请详情
|
||||
@@ -1791,14 +1792,18 @@ const handleSubmitApplication = (row: OverseasApplicationVO) => {
|
||||
|
||||
// 修改并重新提交申请(用于已拒绝状态)
|
||||
const handleUpdateAndResubmitApplication = async (row: OverseasApplicationVO) => {
|
||||
// 跳转到编辑申请页面,标记为重新提交模式
|
||||
proxy.$router.push({
|
||||
path: '/rs/overseas/overseas-form',
|
||||
query: {
|
||||
type: 'resubmit',
|
||||
id: row.applicationId
|
||||
}
|
||||
});
|
||||
try {
|
||||
// 加载申请详情
|
||||
const response = await getOverseasApplication(row.applicationId);
|
||||
state.applicationForm = response.data;
|
||||
state.applicationDialog.title = '修改并重新提交因私出国(境)申请';
|
||||
state.pageType = 'resubmit';
|
||||
state.isResubmitMode = true; // 标记为重新提交模式
|
||||
state.applicationDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('加载申请详情失败:', error);
|
||||
ElMessage.error('加载申请详情失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 取消申请
|
||||
@@ -1944,11 +1949,13 @@ const getOldApprovalStatusTagType = (status: string): 'success' | 'warning' | 'd
|
||||
*/
|
||||
const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'danger' | 'info' | 'primary' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'danger' | 'info' | 'primary'> = {
|
||||
'draft': 'info', // 草稿 - 灰色
|
||||
'waiting': 'warning', // 待审批 - 橙色
|
||||
'finish': 'success', // 已完成 - 绿色
|
||||
'back': 'danger', // 已退回 - 红色
|
||||
'cancel': 'info', // 已取消 - 灰色
|
||||
'cancel': 'info', // 已撤销 - 灰色
|
||||
'draft': 'info', // 草稿 - 灰色
|
||||
'waiting': 'warning', // 待审核 - 橙色
|
||||
'finish': 'success', // 已完成 - 绿色
|
||||
'invalid': 'danger', // 已作废 - 红色
|
||||
'back': 'danger', // 已退回 - 红色
|
||||
'termination': 'primary' // 已终止 - 蓝色
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
@@ -1981,7 +1988,7 @@ const handleViewProcess = (row: OverseasApplicationVO) => {
|
||||
|
||||
if (approvalRecordRef.value && row.applicationId) {
|
||||
// 使用标准工作流组件查看审批记录
|
||||
approvalRecordRef.value.open(row.applicationId, 'overseas-application');
|
||||
approvalRecordRef.value.open(row.applicationId, 'dyjs-cgsq');
|
||||
} else {
|
||||
ElMessage.warning('无法查看流程信息');
|
||||
}
|
||||
@@ -2022,16 +2029,42 @@ const submitForm = async (type: string) => {
|
||||
|
||||
// 2. 如果是提交审批,使用标准工作流API
|
||||
if (type === 'submit') {
|
||||
// 动态获取审批人信息
|
||||
let departmentApproverId: number | null = null;
|
||||
|
||||
try {
|
||||
// 获取部门审批人ID
|
||||
const deptApproverResponse = await getDepartmentApproverId(state.applicationForm.applicantId || userStore.userId);
|
||||
if (deptApproverResponse && deptApproverResponse.data) {
|
||||
departmentApproverId = deptApproverResponse.data;
|
||||
console.log('获取到部门审批人ID:', departmentApproverId);
|
||||
} else {
|
||||
console.warn('获取部门审批人ID失败');
|
||||
ElMessage.error('获取部门审批人信息失败,请稍后重试');
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('调用获取部门审批人ID接口失败:', error);
|
||||
ElMessage.error('获取部门审批人信息失败,请稍后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 注意:人事处审批通过角色配置,不需要动态获取审批人
|
||||
console.log('人事处审批人通过hr_dept_role角色配置,无需动态获取');
|
||||
|
||||
// 标准工作流启动参数
|
||||
const workflowData = {
|
||||
flowCode: 'overseas-application',
|
||||
flowCode: 'dyjs-cgsq', // 出国申请工作流标识KEY
|
||||
businessId: String(applicationId),
|
||||
variables: {
|
||||
// dyjs-cgsq 使用的流程变量
|
||||
applicantName: state.applicationForm.applicantName,
|
||||
department: state.applicationForm.department,
|
||||
destination: state.applicationForm.destination,
|
||||
duration: state.applicationForm.duration,
|
||||
budget: state.applicationForm.budget
|
||||
budget: state.applicationForm.budget,
|
||||
// 动态审批人变量
|
||||
bmfzrsp: departmentApproverId // 部门负责人审批(对应工作流中的入库主键)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2078,7 +2111,7 @@ const handleApprovalRecord = () => {
|
||||
console.log('📜 [审批记录] 打开审批记录');
|
||||
|
||||
if (approvalRecordRef.value && state.applicationForm.applicationId) {
|
||||
approvalRecordRef.value.open(state.applicationForm.applicationId, 'overseas-application');
|
||||
approvalRecordRef.value.open(state.applicationForm.applicationId, 'dyjs-cgsq');
|
||||
} else {
|
||||
ElMessage.warning('无法查看审批记录');
|
||||
}
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
<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">
|
||||
|
||||
<!-- 待审批 -->
|
||||
<el-tab-pane label="待审批变更" name="pending">
|
||||
<div class="tab-content">
|
||||
@@ -46,8 +69,13 @@
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 待审批列表 -->
|
||||
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleView">
|
||||
<!-- 🔴 标准化改造:显示迁移提示 -->
|
||||
<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-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" />
|
||||
@@ -55,14 +83,10 @@
|
||||
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
||||
<el-table-column label="变更类型" align="center" prop="changeType" min-width="150" />
|
||||
<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="createTime" min-width="160" />
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||
@@ -71,10 +95,22 @@
|
||||
<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="success" icon="Check" @click="handleApprovalPass(scope.row)" v-hasPermi="['rs:itineraryChange:approve']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Check"
|
||||
@click="handleApprovalPass(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:approve']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="拒绝" placement="top">
|
||||
<el-button link type="danger" icon="Close" @click="handleApprovalReject(scope.row)" v-hasPermi="['rs:itineraryChange:approve']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Close"
|
||||
@click="handleApprovalReject(scope.row)"
|
||||
v-hasPermi="['rs:itineraryChange:approve']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -184,14 +220,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>
|
||||
@@ -202,7 +238,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>
|
||||
@@ -230,14 +266,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批意见" prop="opinion">
|
||||
<el-input
|
||||
v-model="approvalForm.opinion"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入审批意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="approvalForm.opinion" type="textarea" :rows="4" placeholder="请输入审批意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -248,16 +277,16 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ItineraryChangeApproval">
|
||||
import { ref, reactive, toRefs, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useRouter } from 'vue-router';
|
||||
import animate from '@/animate';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import {
|
||||
import {
|
||||
listPendingItineraryChange,
|
||||
listItineraryChange,
|
||||
getItineraryChange,
|
||||
@@ -269,6 +298,7 @@ import {
|
||||
|
||||
// ==================== 响应式数据 ====================
|
||||
|
||||
const router = useRouter();
|
||||
const pendingQueryFormRef = ref();
|
||||
const recordQueryFormRef = ref();
|
||||
const approvalFormRef = ref();
|
||||
@@ -276,7 +306,7 @@ const approvalFormRef = ref();
|
||||
const state = reactive({
|
||||
// Tab切换
|
||||
activeTab: 'pending',
|
||||
|
||||
|
||||
// 待审批列表
|
||||
pendingLoading: false,
|
||||
showPendingSearch: true,
|
||||
@@ -288,7 +318,7 @@ const state = reactive({
|
||||
applicantName: '',
|
||||
destination: ''
|
||||
} as ItineraryChangeQuery,
|
||||
|
||||
|
||||
// 审批记录列表
|
||||
recordLoading: false,
|
||||
showRecordSearch: true,
|
||||
@@ -301,7 +331,7 @@ const state = reactive({
|
||||
destination: '',
|
||||
status: ''
|
||||
} as ItineraryChangeQuery,
|
||||
|
||||
|
||||
// 对话框
|
||||
viewDialog: {
|
||||
visible: false
|
||||
@@ -310,7 +340,7 @@ const state = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
},
|
||||
|
||||
|
||||
// 表单数据
|
||||
viewData: null as ItineraryChangeVO | null,
|
||||
approvalForm: {
|
||||
@@ -319,7 +349,7 @@ const state = reactive({
|
||||
opinion: ''
|
||||
} as ItineraryChangeApprovalRequest,
|
||||
approvalLoading: false,
|
||||
|
||||
|
||||
// 表单验证规则
|
||||
approvalRules: {
|
||||
approved: [{ required: true, message: '请选择审批结果', trigger: 'change' }]
|
||||
@@ -358,7 +388,7 @@ onMounted(() => {
|
||||
const handleTabClick = (tab: any) => {
|
||||
const tabName = tab.props?.name || tab.paneName;
|
||||
state.activeTab = tabName;
|
||||
|
||||
|
||||
if (tabName === 'pending') {
|
||||
getPendingList();
|
||||
} else if (tabName === 'record') {
|
||||
@@ -366,18 +396,17 @@ const handleTabClick = (tab: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 查询待审批列表
|
||||
// 🔴 标准化改造:简化待审批列表获取(仅用于兼容模式)
|
||||
const getPendingList = async () => {
|
||||
state.pendingLoading = true;
|
||||
try {
|
||||
const response = await listPendingItineraryChange(state.pendingQueryParams);
|
||||
state.pendingList = response.rows;
|
||||
state.pendingTotal = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取待审批列表失败:', error);
|
||||
} finally {
|
||||
state.pendingLoading = false;
|
||||
}
|
||||
|
||||
// 提示用户使用标准工作流页面
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
|
||||
// 清空数据,不再调用自定义API
|
||||
state.pendingList = [];
|
||||
state.pendingTotal = 0;
|
||||
state.pendingLoading = false;
|
||||
};
|
||||
|
||||
// 查询审批记录列表
|
||||
@@ -400,16 +429,20 @@ const getRecordList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 🔴 标准化改造:导航到标准工作流待办页面
|
||||
const navigateToTaskWaiting = () => {
|
||||
// 跳转到标准工作流待办页面
|
||||
router.push('/workflow/task/taskWaiting');
|
||||
};
|
||||
|
||||
// 搜索查询 - 待审批列表
|
||||
const handlePendingQuery = () => {
|
||||
state.pendingQueryParams.pageNum = 1;
|
||||
getPendingList();
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
};
|
||||
|
||||
// 重置搜索 - 待审批列表
|
||||
const resetPendingQuery = () => {
|
||||
pendingQueryFormRef.value?.resetFields();
|
||||
handlePendingQuery();
|
||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
||||
};
|
||||
|
||||
// 搜索查询 - 审批记录
|
||||
@@ -463,7 +496,7 @@ const submitApproval = () => {
|
||||
await approveItineraryChange(state.approvalForm);
|
||||
ElMessage.success('审批成功');
|
||||
state.approvalDialog.visible = false;
|
||||
|
||||
|
||||
// 刷新两个列表(审批后需要同时更新待审批和审批记录列表)
|
||||
await getPendingList();
|
||||
await getRecordList();
|
||||
@@ -488,11 +521,10 @@ 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' // 已拒绝
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -518,4 +550,3 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
||||
min-height: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<!-- 文件通知阅读弹窗 -->
|
||||
<el-dialog
|
||||
:title="noticeDialog.title"
|
||||
v-model="noticeDialog.visible"
|
||||
width="800px"
|
||||
<el-dialog
|
||||
:title="noticeDialog.title"
|
||||
v-model="noticeDialog.visible"
|
||||
width="800px"
|
||||
top="5vh"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
@@ -12,76 +12,55 @@
|
||||
>
|
||||
<div class="notice-content">
|
||||
<!-- 文件头部 -->
|
||||
<div class="notice-header" style="text-align: center; margin-bottom: 20px;">
|
||||
<h2 style="color: #1890ff; margin: 0;">杭州第一技师学院</h2>
|
||||
<h3 style="color: #666; margin: 10px 0;">关于因私出国(境)申请管理的通知</h3>
|
||||
<div class="notice-header" style="text-align: center; margin-bottom: 20px">
|
||||
<h2 style="color: #1890ff; margin: 0">杭州第一技师学院</h2>
|
||||
<h3 style="color: #666; margin: 10px 0">关于因私出国(境)申请管理的通知</h3>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 文件内容 -->
|
||||
<div class="notice-body" style="line-height: 1.8; font-size: 14px; color: #333;">
|
||||
<div class="notice-body" style="line-height: 1.8; font-size: 14px; color: #333">
|
||||
<p><strong>各部门、各位教职工:</strong></p>
|
||||
|
||||
|
||||
<p>为规范我院教职工因私出国(境)申请管理,根据相关规定,现将有关事项通知如下:</p>
|
||||
|
||||
|
||||
<p><strong>一、申请范围</strong></p>
|
||||
<p>本通知适用于我院在职教职工因私事需要出国(境)的申请管理。</p>
|
||||
|
||||
|
||||
<p><strong>二、申请条件</strong></p>
|
||||
<p>1. 申请人须为我院正式在职教职工;</p>
|
||||
<p>2. 无违法违纪记录;</p>
|
||||
<p>3. 工作表现良好,能够按时完成本职工作。</p>
|
||||
|
||||
|
||||
<p><strong>三、申请流程</strong></p>
|
||||
<p>1. 填写《因私出国(境)申请登记报备表》;</p>
|
||||
<p>2. 提交相关证明材料;</p>
|
||||
<p>3. 按规定程序进行审批;</p>
|
||||
<p>4. 获得批准后方可办理相关手续。</p>
|
||||
|
||||
|
||||
<p><strong>四、注意事项</strong></p>
|
||||
<p>1. 申请人应如实填写申请表,不得隐瞒或虚报;</p>
|
||||
<p>2. 出国(境)期间应遵守当地法律法规;</p>
|
||||
<p>3. 按时回国并及时报告相关情况;</p>
|
||||
<p>4. 如有特殊情况,应及时与学院联系。</p>
|
||||
|
||||
|
||||
<p><strong>五、其他</strong></p>
|
||||
<p>本通知自发布之日起执行,如有疑问请联系人事处。</p>
|
||||
|
||||
<div style="text-align: right; margin-top: 30px;">
|
||||
|
||||
<div style="text-align: right; margin-top: 30px">
|
||||
<p>杭州第一技师学院</p>
|
||||
<p>{{ new Date().getFullYear() }}年{{ new Date().getMonth() + 1 }}月{{ new Date().getDate() }}日</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div class="dialog-footer" style="text-align: center;">
|
||||
<el-button
|
||||
v-if="countdownActive"
|
||||
type="info"
|
||||
disabled
|
||||
size="large"
|
||||
style="min-width: 150px;"
|
||||
>
|
||||
请阅读文件内容 ({{ countdown }}s)
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="readCompleted"
|
||||
type="primary"
|
||||
@click="handleReadComplete"
|
||||
size="large"
|
||||
style="min-width: 150px;"
|
||||
>
|
||||
<div class="dialog-footer" style="text-align: center">
|
||||
<el-button v-if="countdownActive" type="info" disabled size="large" style="min-width: 150px"> 请阅读文件内容 ({{ countdown }}s) </el-button>
|
||||
<el-button v-else-if="readCompleted" type="primary" @click="handleReadComplete" size="large" style="min-width: 150px">
|
||||
阅读完成,进入申请
|
||||
</el-button>
|
||||
<el-button
|
||||
type="default"
|
||||
@click="handleCloseNotice"
|
||||
size="large"
|
||||
style="margin-left: 20px;"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="default" @click="handleCloseNotice" size="large" style="margin-left: 20px"> 取消 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -90,10 +69,28 @@
|
||||
<!-- 顶部按钮区域 -->
|
||||
<div class="top-button-section">
|
||||
<div class="left-buttons">
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit" v-hasPermi="['rs:overseas:add', 'rs:overseas:edit']">
|
||||
{{ isResubmit ? '修改并重新提交' : (isEdit ? '更新' : '提交') }}
|
||||
</el-button>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<!-- ========================================
|
||||
🚨 标准化改造:根据模式显示不同按钮
|
||||
======================================== -->
|
||||
|
||||
<!-- 审批模式按钮 -->
|
||||
<template v-if="isApprovalMode">
|
||||
<el-button type="success" :loading="approvalLoading" @click="handleApprove('1')" v-hasPermi="['rs:overseas:approve']">
|
||||
<el-icon><Check /></el-icon> 通过
|
||||
</el-button>
|
||||
<el-button type="danger" :loading="approvalLoading" @click="handleApprove('2')" v-hasPermi="['rs:overseas:approve']">
|
||||
<el-icon><Close /></el-icon> 拒绝
|
||||
</el-button>
|
||||
<el-button @click="handleBackToWorkflow">返回待办</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 普通编辑模式按钮 -->
|
||||
<template v-else>
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit" v-hasPermi="['rs:overseas:add', 'rs:overseas:edit']">
|
||||
{{ isResubmit ? '修改并重新提交' : isEdit ? '更新' : '提交' }}
|
||||
</el-button>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -101,17 +98,15 @@
|
||||
<el-card shadow="never" class="form-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="section-title">
|
||||
{{ isResubmit ? '修改并重新提交' : (isEdit ? '编辑' : '新增') }}因私出国(境)申请登记报备表
|
||||
</span>
|
||||
<span class="section-title"> {{ isResubmit ? '修改并重新提交' : isEdit ? '编辑' : '新增' }}因私出国(境)申请登记报备表 </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-form
|
||||
ref="applicationFormRef"
|
||||
:model="applicationForm"
|
||||
:rules="applicationRules"
|
||||
label-width="140px"
|
||||
<el-form
|
||||
ref="applicationFormRef"
|
||||
:model="applicationForm"
|
||||
:rules="applicationRules"
|
||||
label-width="140px"
|
||||
class="application-form"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
@@ -202,16 +197,45 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第五排:户籍所在地、移动电话 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="户籍所在地" prop="registeredAddress">
|
||||
<el-input v-model="applicationForm.registeredAddress" placeholder="请输入户籍所在地(填写至县市区)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="移动电话" prop="mobilePhone">
|
||||
<el-input v-model="applicationForm.mobilePhone" placeholder="请输入移动电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第六排:人员类别 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="人员类别" prop="personnelCategory">
|
||||
<el-select v-model="applicationForm.personnelCategory" placeholder="请选择人员类别" style="width: 100%">
|
||||
<el-option label="教学人员" value="教学人员" />
|
||||
<el-option label="管理人员" value="管理人员" />
|
||||
<el-option label="教辅人员" value="教辅人员" />
|
||||
<el-option label="工勤人员" value="工勤人员" />
|
||||
<el-option label="其他人员" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 出国信息 -->
|
||||
<div class="form-section">
|
||||
<div class="section-title">出国信息</div>
|
||||
<!-- 第五排:前往国家、出境事由 -->
|
||||
<!-- 第五排:前往国家或地区、出境事由 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="前往国家" prop="destinationCountry">
|
||||
<el-input v-model="applicationForm.destinationCountry" placeholder="请输入前往国家" />
|
||||
<el-form-item label="前往国家或地区" prop="destinationCountry">
|
||||
<el-input v-model="applicationForm.destinationCountry" placeholder="请输入前往国家或地区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
@@ -227,10 +251,21 @@
|
||||
<!-- 探亲时显示的额外字段 -->
|
||||
<el-row :gutter="20" v-if="applicationForm.exitReason === '探亲'">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="探望对象关系" prop="visitRelationship">
|
||||
<el-input
|
||||
v-model="applicationForm.visitRelationship"
|
||||
placeholder="请输入探望对象姓名及与申请者关系"
|
||||
<el-form-item label="探望对象姓名及与申请者关系" prop="visitRelationship">
|
||||
<el-input v-model="applicationForm.visitRelationship" placeholder="请输入探望对象姓名及与申请者关系" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 备注 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="applicationForm.remarks"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请输入备注信息(可选)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -240,10 +275,10 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="行程安排" prop="itinerary">
|
||||
<el-input
|
||||
v-model="applicationForm.itinerary"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
<el-input
|
||||
v-model="applicationForm.itinerary"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入详细的行程安排(如:访问城市、主要活动、住宿安排等)"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -253,7 +288,7 @@
|
||||
<!-- 出国(境)时间 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="2">
|
||||
<el-form-item label="出国(境)时间" label-width="110px" style="font-weight: 600; white-space: nowrap;">
|
||||
<el-form-item label="出国(境)时间" label-width="110px" style="font-weight: 600; white-space: nowrap">
|
||||
<span></span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -263,7 +298,7 @@
|
||||
v-model="applicationForm.startDate"
|
||||
type="date"
|
||||
placeholder="请选择出发日期"
|
||||
style="width: 100%; min-width: 180px;"
|
||||
style="width: 100%; min-width: 180px"
|
||||
format="YYYY年MM月DD日"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
@@ -275,7 +310,7 @@
|
||||
v-model="applicationForm.endDate"
|
||||
type="date"
|
||||
placeholder="请选择返回日期"
|
||||
style="width: 100%; min-width: 180px;"
|
||||
style="width: 100%; min-width: 180px"
|
||||
format="YYYY年MM月DD日"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
@@ -283,7 +318,7 @@
|
||||
</el-col>
|
||||
<el-col :span="5" :offset="1">
|
||||
<el-form-item label="出国天数" prop="duration">
|
||||
<el-input v-model="applicationForm.duration" placeholder="天数" disabled style="min-width: 120px;">
|
||||
<el-input v-model="applicationForm.duration" placeholder="天数" disabled style="min-width: 120px">
|
||||
<template #append>天</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -291,30 +326,22 @@
|
||||
</el-row>
|
||||
|
||||
<!-- 党籍保留附件上传区域(仅当中共党员且出国天数>30天时显示) -->
|
||||
<div
|
||||
<div
|
||||
v-if="showPartyRetentionUpload"
|
||||
class="party-retention-upload-section"
|
||||
style="margin: 10px 0; padding: 10px; border: 1px dashed #ff4757; border-radius: 4px; background: #fff5f5;">
|
||||
<div style="font-size: 14px; color: #ff4757; margin-bottom: 8px; font-weight: bold;">
|
||||
class="party-retention-upload-section"
|
||||
style="margin: 10px 0; padding: 10px; border: 1px dashed #ff4757; border-radius: 4px; background: #fff5f5"
|
||||
>
|
||||
<div style="font-size: 14px; color: #ff4757; margin-bottom: 8px; font-weight: bold">
|
||||
<el-icon><Warning /></el-icon>
|
||||
党籍保留附件上传(必填)
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #606266; margin-bottom: 8px;">
|
||||
<div style="font-size: 12px; color: #606266; margin-bottom: 8px">
|
||||
您是中共党员且出国天数超过30天,需要先提交党籍保留流程,审批通过后下载并上传盖章附件
|
||||
</div>
|
||||
<!-- 下载PDF空白模板按钮 -->
|
||||
<div style="margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="Download"
|
||||
@click="handleDownloadPartyRetention"
|
||||
>
|
||||
下载党籍保留申请PDF模板
|
||||
</el-button>
|
||||
<span style="margin-left: 10px; font-size: 12px; color: #909399;">
|
||||
* 请先下载空白模板并手工填写,然后上传盖章后的文件
|
||||
</span>
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-button type="primary" size="small" icon="Download" @click="handleDownloadPartyRetention"> 下载党籍保留申请PDF模板 </el-button>
|
||||
<span style="margin-left: 10px; font-size: 12px; color: #909399"> * 请先下载空白模板并手工填写,然后上传盖章后的文件 </span>
|
||||
</div>
|
||||
<div class="attachment-upload-container">
|
||||
<el-upload
|
||||
@@ -332,13 +359,9 @@
|
||||
list-type="text"
|
||||
class="upload-demo"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="Upload">
|
||||
上传党籍保留附件
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" icon="Upload"> 上传党籍保留附件 </el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip" style="font-size: 12px; color: #909399;">
|
||||
支持 PDF、图片、Word 格式,文件大小不超过 10MB
|
||||
</div>
|
||||
<div class="el-upload__tip" style="font-size: 12px; color: #909399">支持 PDF、图片、Word 格式,文件大小不超过 10MB</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
@@ -354,19 +377,17 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 备注 -->
|
||||
<div class="form-section">
|
||||
<!-- 联系方式 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="applicationForm.remarks"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="mobilePhone">
|
||||
<el-input v-model="applicationForm.mobilePhone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="应急联系人及联系电话" prop="emergencyContact">
|
||||
<el-input v-model="applicationForm.emergencyContact" placeholder="请输入应急联系人及联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -381,9 +402,17 @@ import { ref, reactive, computed, onMounted, getCurrentInstance, watch, nextTick
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { Warning, Download, Upload } from '@element-plus/icons-vue';
|
||||
import { Warning, Download, Upload, Check, Close } from '@element-plus/icons-vue';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import request from '@/utils/request';
|
||||
import {
|
||||
addOverseasApplication,
|
||||
updateOverseasApplication,
|
||||
getOverseasApplication,
|
||||
approveOverseasApplication,
|
||||
type OverseasApplicationForm,
|
||||
type ApprovalRequest
|
||||
} from '@/api/rs/overseas';
|
||||
|
||||
// 获取当前实例和路由
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -396,6 +425,16 @@ const formLoading = ref(false);
|
||||
const applicationFormRef = ref<FormInstance>();
|
||||
const partyRetentionUploadRef = ref();
|
||||
|
||||
// ========================================
|
||||
// 🚨 标准化改造:审批相关状态
|
||||
// ========================================
|
||||
const approvalLoading = ref(false);
|
||||
const approvalDialogVisible = ref(false);
|
||||
const approvalForm = reactive({
|
||||
approvalOpinion: '',
|
||||
approvalStatus: '1'
|
||||
});
|
||||
|
||||
// 党籍保留附件相关
|
||||
const partyRetentionFileList = ref([]);
|
||||
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/rs/overseas/application/attachment/upload');
|
||||
@@ -430,12 +469,27 @@ const uploadHeaders = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
// ========================================
|
||||
// 🚨 标准化改造:支持工作流审批模式
|
||||
// ========================================
|
||||
|
||||
// 判断是否为审批模式(通过工作流跳转)
|
||||
const isApprovalMode = computed(() => !!route.query.taskId);
|
||||
|
||||
// 判断是否为编辑模式
|
||||
const isEdit = computed(() => route.query.type === 'edit' || route.query.type === 'resubmit' || !!route.query.id);
|
||||
|
||||
// 判断是否为重新提交模式
|
||||
const isResubmit = computed(() => route.query.type === 'resubmit');
|
||||
|
||||
// 获取工作流任务ID
|
||||
const taskId = computed(() => route.query.taskId as string);
|
||||
|
||||
// 获取申请ID(从路由参数)
|
||||
const applicationId = computed(() => {
|
||||
return route.query.applicationId || route.query.id;
|
||||
});
|
||||
|
||||
// 申请表单数据
|
||||
const applicationForm = reactive({
|
||||
applicationId: '',
|
||||
@@ -448,6 +502,9 @@ const applicationForm = reactive({
|
||||
position: '',
|
||||
documentName: '',
|
||||
documentNumber: '',
|
||||
registeredAddress: '', // ✅ 新增:户籍所在地
|
||||
mobilePhone: '', // ✅ 新增:移动电话
|
||||
personnelCategory: '', // ✅ 新增:人员类别
|
||||
destinationCountry: '',
|
||||
exitReason: '',
|
||||
visitRelationship: '',
|
||||
@@ -471,6 +528,13 @@ const applicationRules: FormRules = {
|
||||
position: [{ required: true, message: '请输入职务', trigger: 'blur' }],
|
||||
documentName: [{ required: true, message: '请输入申领证件名称', trigger: 'blur' }],
|
||||
documentNumber: [{ required: true, message: '请输入证件号码', trigger: 'blur' }],
|
||||
// ✅ 新增必填字段验证
|
||||
registeredAddress: [{ required: true, message: '请输入户籍所在地', trigger: 'blur' }],
|
||||
mobilePhone: [
|
||||
{ required: true, message: '请输入移动电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '移动电话格式不正确', trigger: 'blur' }
|
||||
],
|
||||
personnelCategory: [{ required: true, message: '请选择人员类别', trigger: 'change' }],
|
||||
destinationCountry: [{ required: true, message: '请输入前往国家', trigger: 'blur' }],
|
||||
exitReason: [{ required: true, message: '请选择出境事由', trigger: 'change' }],
|
||||
itinerary: [{ required: true, message: '请输入行程安排', trigger: 'blur' }],
|
||||
@@ -497,19 +561,19 @@ watch([() => applicationForm.startDate, () => applicationForm.endDate], () => {
|
||||
// 计算属性:是否显示党籍保留上传组件
|
||||
const showPartyRetentionUpload = computed(() => {
|
||||
// 首先检查是否为中共党员
|
||||
const isPartyMember = applicationForm.politicalStatus === '1' || applicationForm.politicalStatus === '中共党员';
|
||||
|
||||
const isPartyMember = applicationForm.politicalStatus === '1' || applicationForm.politicalStatus === '中共党员';
|
||||
|
||||
// 如果不是党员,直接返回false
|
||||
if (!isPartyMember) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查出国天数
|
||||
const duration = applicationForm.duration;
|
||||
if (!duration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 出国天数大于30天才显示
|
||||
return Number(duration) > 30;
|
||||
});
|
||||
@@ -529,7 +593,7 @@ watch(
|
||||
const handleDownloadPartyRetention = async () => {
|
||||
try {
|
||||
ElMessage.info('正在生成PDF,请稍候...');
|
||||
|
||||
|
||||
// 使用request工具发送请求,自动包含认证头
|
||||
const response = await request({
|
||||
url: '/rs/overseas/record/downloadPartyRetention',
|
||||
@@ -538,17 +602,16 @@ const handleDownloadPartyRetention = async () => {
|
||||
});
|
||||
|
||||
// 创建blob并触发下载(response已经是blob对象)
|
||||
const blob = response instanceof Blob
|
||||
? new Blob([response], { type: 'application/pdf' })
|
||||
: new Blob([response.data], { type: 'application/pdf' });
|
||||
|
||||
const blob =
|
||||
response instanceof Blob ? new Blob([response], { type: 'application/pdf' }) : new Blob([response.data], { type: 'application/pdf' });
|
||||
|
||||
console.log('📥 生成的Blob大小:', blob.size, 'bytes');
|
||||
console.log('📥 Blob类型:', blob.type);
|
||||
|
||||
|
||||
if (blob.size === 0) {
|
||||
throw new Error('下载的文件为空');
|
||||
}
|
||||
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
@@ -569,9 +632,14 @@ const handleDownloadPartyRetention = async () => {
|
||||
// 党籍保留附件上传前检查
|
||||
const beforePartyRetentionUpload = async (file: any) => {
|
||||
// 检查文件格式
|
||||
const isValidType = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png',
|
||||
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
|
||||
.includes(file.type);
|
||||
const isValidType = [
|
||||
'application/pdf',
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
].includes(file.type);
|
||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||
|
||||
if (!isValidType) {
|
||||
@@ -637,7 +705,7 @@ const handlePartyRetentionRemove = (file: any) => {
|
||||
const startCountdown = () => {
|
||||
countdownActive.value = true;
|
||||
readCompleted.value = false;
|
||||
|
||||
|
||||
const timer = setInterval(() => {
|
||||
if (countdown.value > 0) {
|
||||
countdown.value--;
|
||||
@@ -672,85 +740,276 @@ const handleCloseNotice = () => {
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!applicationFormRef.value) return;
|
||||
|
||||
|
||||
try {
|
||||
await applicationFormRef.value.validate();
|
||||
|
||||
|
||||
// 检查党员是否需要上传党籍保留附件
|
||||
if (showPartyRetentionUpload.value && !applicationForm.partyRetentionAttachment) {
|
||||
ElMessage.error('请上传党籍保留附件');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loading.value = true;
|
||||
|
||||
// 这里调用保存API
|
||||
// 由于我只能修改rs模块内容,暂时模拟提交成功
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const successMessage = isResubmit.value
|
||||
? '修改并重新提交成功!您的申请已重新进入审批流程。'
|
||||
: (isEdit.value ? '更新成功' : '申请提交成功!您的申请已进入审批流程,请耐心等待审批结果。');
|
||||
|
||||
// 准备提交数据 - 修复字段映射关系
|
||||
const submitData: OverseasApplicationForm = {
|
||||
...applicationForm,
|
||||
// ✅ 按照纸质版表格字段直接映射,不做转换
|
||||
// 保持前端字段名与数据库字段名一致
|
||||
destinationCountry: applicationForm.destinationCountry || '', // 前往国家或地区
|
||||
exitReason: applicationForm.exitReason || '', // 出境事由
|
||||
itinerary: applicationForm.itinerary || '', // 行程安排
|
||||
// 以下字段用于兼容后端现有逻辑
|
||||
purpose: applicationForm.exitReason || '', // 兼容字段
|
||||
purposeDetail: applicationForm.itinerary || '', // 兼容字段
|
||||
destination: applicationForm.destinationCountry || '', // 兼容字段
|
||||
// ✅ 使用用户实际填写的字段值,不再使用默认值
|
||||
registeredAddress: applicationForm.registeredAddress || '',
|
||||
mobilePhone: applicationForm.mobilePhone || '',
|
||||
personnelCategory: applicationForm.personnelCategory || '',
|
||||
emergencyContact: applicationForm.emergencyContact || '', // 应急联系人及联系电话
|
||||
remarks: applicationForm.remarks || '', // 备注
|
||||
// 保留的默认值字段
|
||||
budget: 0, // 如果没有预算字段,设置默认值
|
||||
contactPhone: applicationForm.mobilePhone || '暂无', // 使用移动电话作为联系电话
|
||||
duration: applicationForm.duration ? Number(applicationForm.duration) : 0
|
||||
};
|
||||
|
||||
// 新增模式:删除系统自动设置的字段,避免验证错误
|
||||
if (!isEdit.value) {
|
||||
delete submitData.applicantId;
|
||||
// ✅ 保留用户填写的姓名字段,不要删除
|
||||
// delete submitData.applicantName;
|
||||
// ✅ 保留部门字段,后端需要此字段
|
||||
// delete submitData.department;
|
||||
delete submitData.tenantId;
|
||||
delete submitData.applyTime;
|
||||
}
|
||||
|
||||
// 保留办公电话字段的过滤逻辑(可选字段)
|
||||
if (!submitData.officePhone) delete submitData.officePhone;
|
||||
|
||||
let result;
|
||||
if (isEdit.value && applicationForm.applicationId) {
|
||||
// 更新模式
|
||||
result = await updateOverseasApplication(submitData);
|
||||
} else {
|
||||
// 新增模式
|
||||
result = await addOverseasApplication(submitData);
|
||||
// 保存返回的applicationId
|
||||
if (result.data?.applicationId) {
|
||||
applicationForm.applicationId = result.data.applicationId;
|
||||
}
|
||||
}
|
||||
|
||||
const successMessage = isResubmit.value
|
||||
? '修改并重新提交成功!您的申请已重新进入审批流程。'
|
||||
: isEdit.value
|
||||
? '更新成功'
|
||||
: '申请提交成功!您的申请已进入审批流程,请耐心等待审批结果。';
|
||||
ElMessage.success(successMessage);
|
||||
|
||||
|
||||
// 返回上一页
|
||||
handleCancel();
|
||||
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
handleGoBack();
|
||||
} catch (error: any) {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error(error?.message || '提交失败,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 取消操作
|
||||
// 保存申请(用于获取applicationId)
|
||||
const handleSave = async () => {
|
||||
if (!applicationFormRef.value) return;
|
||||
|
||||
await applicationFormRef.value.validate();
|
||||
|
||||
// 如果已有ID,无需重复保存
|
||||
if (applicationForm.applicationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 准备保存数据 - 修复字段映射关系
|
||||
const saveData: OverseasApplicationForm = {
|
||||
...applicationForm,
|
||||
// ✅ 按照纸质版表格字段直接映射,不做转换
|
||||
// 保持前端字段名与数据库字段名一致
|
||||
destinationCountry: applicationForm.destinationCountry || '', // 前往国家或地区
|
||||
exitReason: applicationForm.exitReason || '', // 出境事由
|
||||
itinerary: applicationForm.itinerary || '', // 行程安排
|
||||
// 以下字段用于兼容后端现有逻辑
|
||||
purpose: applicationForm.exitReason || '', // 兼容字段
|
||||
purposeDetail: applicationForm.itinerary || '', // 兼容字段
|
||||
destination: applicationForm.destinationCountry || '', // 兼容字段
|
||||
// ✅ 使用用户实际填写的字段值,不再使用默认值
|
||||
registeredAddress: applicationForm.registeredAddress || '',
|
||||
mobilePhone: applicationForm.mobilePhone || '',
|
||||
personnelCategory: applicationForm.personnelCategory || '',
|
||||
emergencyContact: applicationForm.emergencyContact || '', // 应急联系人及联系电话
|
||||
remarks: applicationForm.remarks || '', // 备注
|
||||
// 保留的默认值字段
|
||||
budget: 0, // 如果没有预算字段,设置默认值
|
||||
contactPhone: applicationForm.mobilePhone || '暂无', // 使用移动电话作为联系电话
|
||||
duration: applicationForm.duration ? Number(applicationForm.duration) : 0
|
||||
};
|
||||
|
||||
// 删除系统自动设置的字段,避免验证错误
|
||||
delete saveData.applicantId;
|
||||
// ✅ 保留用户填写的姓名字段,不要删除
|
||||
// delete saveData.applicantName;
|
||||
delete saveData.department;
|
||||
delete saveData.tenantId;
|
||||
delete saveData.applyTime;
|
||||
|
||||
// 保留办公电话字段的过滤逻辑(可选字段)
|
||||
if (!saveData.officePhone) delete saveData.officePhone;
|
||||
|
||||
try {
|
||||
const result = await addOverseasApplication(saveData);
|
||||
if (result.data?.applicationId) {
|
||||
applicationForm.applicationId = result.data.applicationId;
|
||||
ElMessage.success('申请信息保存成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error);
|
||||
throw new Error('保存申请信息失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 直接返回上一页(不弹确认框)
|
||||
const handleGoBack = () => {
|
||||
// 关闭当前页面,返回上一页
|
||||
proxy.$tab.closePage(route);
|
||||
router.go(-1);
|
||||
};
|
||||
|
||||
// 取消操作(弹出确认框)
|
||||
const handleCancel = () => {
|
||||
ElMessageBox.confirm('是否确认取消操作?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 关闭当前页面,返回上一页
|
||||
proxy.$tab.closePage(route);
|
||||
router.go(-1);
|
||||
}).catch(() => {
|
||||
// 用户取消
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
handleGoBack();
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户取消
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
const initData = async () => {
|
||||
if (isEdit.value && route.query.id) {
|
||||
// 🔴 标准化改造:支持审批模式和编辑模式
|
||||
const targetId = applicationId.value;
|
||||
if ((isEdit.value && route.query.id) || (isApprovalMode.value && targetId)) {
|
||||
formLoading.value = true;
|
||||
try {
|
||||
// 这里应该根据ID获取数据
|
||||
// 由于我只能修改rs模块内容,暂时模拟数据加载
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
// 模拟填充编辑数据
|
||||
Object.assign(applicationForm, {
|
||||
applicationId: route.query.id,
|
||||
applicantName: '张三',
|
||||
gender: '男',
|
||||
// ... 其他字段
|
||||
});
|
||||
|
||||
// 根据ID获取申请数据(支持审批模式和编辑模式)
|
||||
const result = await getOverseasApplication(targetId as string);
|
||||
if (result.data) {
|
||||
// 映射数据到表单 - 修复字段映射关系
|
||||
Object.assign(applicationForm, {
|
||||
applicationId: result.data.applicationId,
|
||||
applicantName: result.data.applicantName || '',
|
||||
gender: result.data.gender || '',
|
||||
birthDate: result.data.birthDate || '',
|
||||
politicalStatus: result.data.politicalStatus || '',
|
||||
department: result.data.department || '',
|
||||
professionalTitle: result.data.professionalTitle || '',
|
||||
position: result.data.position || '',
|
||||
documentName: result.data.documentName || '',
|
||||
documentNumber: result.data.documentNumber || '',
|
||||
// ✅ 新增字段的映射
|
||||
registeredAddress: result.data.registeredAddress || '',
|
||||
mobilePhone: result.data.mobilePhone || '',
|
||||
personnelCategory: result.data.personnelCategory || '',
|
||||
// ✅ 修复字段映射关系
|
||||
destinationCountry: result.data.destination || '', // 后端destination -> 前端destinationCountry
|
||||
exitReason: result.data.purpose || '', // 后端purpose -> 前端exitReason
|
||||
itinerary: result.data.purposeDetail || result.data.itinerary || '', // 后端purposeDetail -> 前端itinerary
|
||||
visitRelationship: result.data.visitRelationship || '',
|
||||
startDate: result.data.startDate || '',
|
||||
endDate: result.data.endDate || '',
|
||||
duration: result.data.duration || '',
|
||||
remarks: result.data.applicantRemark || result.data.remarks || '',
|
||||
partyRetentionAttachment: result.data.partyRetentionFilePath || '',
|
||||
partyRetentionAttachmentId: result.data.partyRetentionFileOssId || ''
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
ElMessage.error('获取数据失败');
|
||||
ElMessage.error('获取申请数据失败');
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ========================================
|
||||
// 🚨 标准化改造:审批相关处理函数
|
||||
// ========================================
|
||||
|
||||
// 处理审批操作
|
||||
const handleApprove = async (approvalStatus: string) => {
|
||||
try {
|
||||
const title = approvalStatus === '1' ? '审批通过' : '审批拒绝';
|
||||
const placeholder = approvalStatus === '1' ? '请填写审批意见(可选)' : '请填写拒绝理由';
|
||||
|
||||
const { value: opinion } = await ElMessageBox.prompt(
|
||||
`确定要${title}此申请吗?`,
|
||||
title,
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPlaceholder: placeholder,
|
||||
inputType: 'textarea',
|
||||
inputValidator: approvalStatus === '2' ? (value) => {
|
||||
return value && value.trim() !== '' || '拒绝时必须填写理由';
|
||||
} : null
|
||||
}
|
||||
);
|
||||
|
||||
approvalLoading.value = true;
|
||||
|
||||
const approvalRequest: ApprovalRequest = {
|
||||
applicationId: applicationId.value as string,
|
||||
approvalStatus,
|
||||
approvalOpinion: opinion || '',
|
||||
taskId: taskId.value
|
||||
};
|
||||
|
||||
await approveOverseasApplication(approvalRequest);
|
||||
|
||||
ElMessage.success(`${title}成功!`);
|
||||
|
||||
// 返回到待办任务页面
|
||||
handleBackToWorkflow();
|
||||
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('审批失败:', error);
|
||||
ElMessage.error(error?.message || '审批失败,请稍后重试');
|
||||
}
|
||||
} finally {
|
||||
approvalLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 返回到工作流待办页面
|
||||
const handleBackToWorkflow = () => {
|
||||
router.push('/workflow/task/taskWaiting');
|
||||
};
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initData();
|
||||
|
||||
// 新增模式下显示文件阅读弹窗
|
||||
if (route.query.type === 'add') {
|
||||
|
||||
// 新增模式下显示文件阅读弹窗(审批模式下不显示)
|
||||
if (route.query.type === 'add' && !isApprovalMode.value) {
|
||||
noticeDialog.visible = true;
|
||||
startCountdown();
|
||||
}
|
||||
@@ -816,7 +1075,7 @@ onMounted(() => {
|
||||
.el-col-8 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
.el-col-12 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
@@ -463,14 +463,23 @@ function handleTabClick(tab) {
|
||||
/** 查询出国审批列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
console.log('临时清空出国审批列表内容');
|
||||
|
||||
// 临时清空列表内容
|
||||
approvalList.value = [];
|
||||
total.value = 0;
|
||||
loading.value = false;
|
||||
|
||||
console.log('出国审批列表已清空');
|
||||
const params = {
|
||||
...queryParams.value,
|
||||
pageNum: queryParams.value.pageNum,
|
||||
pageSize: queryParams.value.pageSize
|
||||
};
|
||||
|
||||
listOverseasApproval(params)
|
||||
.then((response) => {
|
||||
approvalList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
console.log('出国审批列表查询成功,记录数:', total.value);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('查询出国审批列表失败:', error);
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 查询审批记录列表 */
|
||||
|
||||
Reference in new issue
Block a user