人才上报.

This commit is contained in:
18631081161 2026-01-17 01:24:44 +08:00
parent 70b0c938ab
commit 2495d39e9e
6 changed files with 331 additions and 28 deletions

View File

@ -704,7 +704,8 @@ public class PersonnelController : BaseApiController
// 检查权限:只有提交单位或上级单位可以删除
var unitId = GetCurrentUnitId();
if (unitId == null)
var userLevel = GetCurrentUnitLevel();
if (unitId == null || userLevel == null)
{
return Unauthorized();
}
@ -722,10 +723,19 @@ public class PersonnelController : BaseApiController
}
}
// 只能删除待审批状态的人员
if (personnel.Status != PersonnelStatus.Pending)
// 待审批状态的人员可以直接删除
// 已批准状态的人员,只有审批单位或更高级别单位可以删除
if (personnel.Status == PersonnelStatus.Approved)
{
return BadRequest(new { message = "只能删除待审批状态的人员记录" });
// 检查当前用户单位层级是否足够高(数值越小层级越高)
var personnelLevelValue = personnel.ApprovedLevel.HasValue ? (int)personnel.ApprovedLevel.Value : 4;
var userLevelValue = (int)userLevel.Value;
// 用户层级必须高于或等于人员等级才能删除
if (userLevelValue > personnelLevelValue)
{
return BadRequest(new { message = "您的权限不足以删除该等级的人员" });
}
}
try
@ -787,4 +797,52 @@ public class PersonnelController : BaseApiController
}
}
/// <summary>
/// 拒绝向上申报请求
/// </summary>
[HttpPost("{id}/reject-upgrade")]
[Authorize(Policy = "BattalionLevel")] // 营级及以上权限
public async Task<IActionResult> RejectUpgrade(int id, [FromBody] RejectUpgradeRequest request)
{
var unitId = GetCurrentUnitId();
if (unitId == null)
{
return Unauthorized();
}
try
{
var personnel = await _personnelService.RejectUpgradeAsync(id, unitId.Value, request.Comments);
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
{
return BadRequest(new { message = ex.Message });
}
}
/// <summary>
/// 师部直接升级团级人才为师级
/// </summary>
[HttpPost("{id}/direct-upgrade")]
[Authorize(Policy = "DivisionLevel")] // 师级权限
public async Task<IActionResult> DirectUpgrade(int id)
{
var unitId = GetCurrentUnitId();
if (unitId == null)
{
return Unauthorized();
}
try
{
var personnel = await _personnelService.DirectUpgradeAsync(id, unitId.Value);
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
{
return BadRequest(new { message = ex.Message });
}
}
}

View File

@ -122,6 +122,16 @@ public class RejectPersonnelRequest
public string? Reason { get; set; }
}
/// <summary>
/// 拒绝向上申报请求DTO
/// </summary>
public class RejectUpgradeRequest
{
[Required(ErrorMessage = "拒绝原因不能为空")]
[StringLength(500, ErrorMessage = "拒绝原因长度不能超过500个字符")]
public string Comments { get; set; } = string.Empty;
}
/// <summary>
/// 人员响应DTO
/// </summary>

View File

@ -778,4 +778,91 @@ public class PersonnelService : IPersonnelService
return personnel;
}
/// <summary>
/// 拒绝向上申报请求
/// </summary>
public async Task<Personnel> RejectUpgradeAsync(int personnelId, int rejectedByUnitId, string comments)
{
var personnel = await _context.Personnel
.Include(p => p.SubmittedByUnit)
.Include(p => p.ApprovedByUnit)
.Include(p => p.PendingUpgradeByUnit)
.FirstOrDefaultAsync(p => p.Id == personnelId);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
if (!personnel.PendingUpgradeByUnitId.HasValue)
throw new ArgumentException("该人员没有待处理的向上申报请求");
var rejectedByUnit = await _context.OrganizationalUnits.FindAsync(rejectedByUnitId);
if (rejectedByUnit == null)
throw new ArgumentException("审批单位不存在");
var previousLevel = personnel.ApprovedLevel;
// 清除待申报标记,保持原有等级不变
personnel.PendingUpgradeByUnitId = null;
await _context.SaveChangesAsync();
// 记录审批历史
var userId = await GetUserIdByUnitAsync(rejectedByUnitId);
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Rejected,
PersonnelStatus.Approved, PersonnelStatus.Approved, previousLevel, previousLevel,
userId, rejectedByUnitId, $"向上申报被拒绝:{comments}");
_logger.LogInformation("人员 {PersonnelId} 向上申报已被单位 {UnitId} 拒绝,原因:{Comments}",
personnelId, rejectedByUnitId, comments);
return personnel;
}
/// <summary>
/// 师部直接升级团级人才为师级
/// </summary>
public async Task<Personnel> DirectUpgradeAsync(int personnelId, int divisionUnitId)
{
var personnel = await _context.Personnel
.Include(p => p.SubmittedByUnit)
.Include(p => p.ApprovedByUnit)
.FirstOrDefaultAsync(p => p.Id == personnelId);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
if (personnel.Status != PersonnelStatus.Approved)
throw new ArgumentException("只有已审批的人员才能升级");
if (personnel.ApprovedLevel != PersonnelLevel.Regiment)
throw new ArgumentException("只能升级团级人才");
var divisionUnit = await _context.OrganizationalUnits.FindAsync(divisionUnitId);
if (divisionUnit == null)
throw new ArgumentException("单位不存在");
if (divisionUnit.Level != OrganizationalLevel.Division)
throw new ArgumentException("只有师部可以直接升级团级人才");
var previousLevel = personnel.ApprovedLevel;
// 直接升级为师级
personnel.ApprovedLevel = PersonnelLevel.Division;
personnel.ApprovedByUnitId = divisionUnitId;
personnel.ApprovedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
// 记录审批历史
var userId = await GetUserIdByUnitAsync(divisionUnitId);
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.LevelUpgraded,
PersonnelStatus.Approved, PersonnelStatus.Approved, previousLevel, PersonnelLevel.Division,
userId, divisionUnitId, "师部直接升级团级人才为师级");
_logger.LogInformation("人员 {PersonnelId} 已被师部 {UnitId} 直接升级为师级",
personnelId, divisionUnitId);
return personnel;
}
}

View File

@ -85,6 +85,16 @@ public interface IPersonnelService
/// 审批向上申报请求
/// </summary>
Task<Personnel> ApproveUpgradeAsync(int personnelId, int approvedByUnitId);
/// <summary>
/// 拒绝向上申报请求
/// </summary>
Task<Personnel> RejectUpgradeAsync(int personnelId, int rejectedByUnitId, string comments);
/// <summary>
/// 师部直接升级团级人才为师级
/// </summary>
Task<Personnel> DirectUpgradeAsync(int personnelId, int divisionUnitId);
}
/// <summary>

View File

@ -67,5 +67,19 @@ export const personnelApi = {
async approveUpgrade(personnelId: number): Promise<Personnel> {
const response = await apiClient.post<Personnel>(`/personnel/${personnelId}/approve-upgrade`)
return response.data
},
// 拒绝向上申报请求
async rejectUpgrade(personnelId: number, comments: string): Promise<Personnel> {
const response = await apiClient.post<Personnel>(`/personnel/${personnelId}/reject-upgrade`, {
comments
})
return response.data
},
// 师部直接升级团级人才为师级
async directUpgrade(personnelId: number): Promise<Personnel> {
const response = await apiClient.post<Personnel>(`/personnel/${personnelId}/direct-upgrade`)
return response.data
}
}

View File

@ -65,10 +65,10 @@
<el-table-column prop="submittedByUnitName" label="所属单位" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="120" align="center">
<template #default="{ row }">
<el-tag v-if="row.pendingUpgradeByUnitId" type="primary" effect="dark" size="small">
<el-tag v-if="row.pendingUpgradeByUnitId" :type="canApproveUpgrade(row) ? 'warning' : 'primary'" effect="dark" size="small">
{{ canApproveUpgrade(row) ? '待审批' : '待上级审批' }}
</el-tag>
<el-tag v-else :type="getStatusTagType(row.status)" effect="dark" size="small">
<el-tag v-else :type="getDisplayStatusType(row)" effect="dark" size="small">
{{ getDisplayStatus(row) }}
</el-tag>
</template>
@ -104,6 +104,11 @@
<el-icon><Top /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip v-if="row.status === 'Approved' && authStore.canApprove && canDirectUpgrade(row)" content="直接升级" placement="top">
<el-button type="success" link size="small" @click="handleDirectUpgrade(row)">
<el-icon><Top /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip v-if="row.status === 'Approved' && row.pendingUpgradeByUnitId && authStore.canApprove && canApproveUpgrade(row)" content="审批向上申报" placement="top">
<el-button type="success" link size="small" @click="handleApproveUpgrade(row)">
<el-icon><Check /></el-icon>
@ -114,7 +119,7 @@
<el-icon><Edit /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip v-if="row.status === 'Pending'" content="删除" placement="top">
<el-tooltip v-if="canDelete(row)" content="删除" placement="top">
<el-button type="danger" link size="small" @click="handleDelete(row)">
<el-icon><Delete /></el-icon>
</el-button>
@ -161,6 +166,36 @@
</el-button>
</template>
</el-dialog>
<!-- Upgrade Approval Dialog -->
<el-dialog v-model="showUpgradeApprovalDialog" title="审批向上申报" width="500px" :close-on-click-modal="false">
<el-form :model="upgradeApprovalForm" label-width="100px">
<el-descriptions :column="2" border class="approval-info">
<el-descriptions-item label="姓名">{{ selectedPerson?.name }}</el-descriptions-item>
<el-descriptions-item label="职位">{{ selectedPerson?.position }}</el-descriptions-item>
<el-descriptions-item label="当前等级">{{ upgradeInfo.currentLevel }}</el-descriptions-item>
<el-descriptions-item label="升级后等级">{{ upgradeInfo.newLevel }}</el-descriptions-item>
</el-descriptions>
<el-alert
type="info"
:closable="false"
style="margin: 16px 0"
:title="`批准后,该人才将从${upgradeInfo.currentLevel}升级为${upgradeInfo.newLevel}`"
/>
<el-form-item label="审批意见" style="margin-top: 20px">
<el-input v-model="upgradeApprovalForm.comments" type="textarea" rows="3" placeholder="请输入审批意见(拒绝时必填)" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showUpgradeApprovalDialog = false">取消</el-button>
<el-button type="danger" :loading="approvingUpgrade" @click="submitUpgradeApproval(false)">
<el-icon><Close /></el-icon>
</el-button>
<el-button type="success" :loading="approvingUpgrade" @click="submitUpgradeApproval(true)">
<el-icon><Check /></el-icon>
</el-button>
</template>
</el-dialog>
</div>
</template>
@ -182,8 +217,11 @@ const loading = ref(false)
const statusFilter = ref('')
const searchKeyword = ref('')
const showApprovalDialog = ref(false)
const showUpgradeApprovalDialog = ref(false)
const approving = ref(false)
const approvingUpgrade = ref(false)
const selectedPerson = ref<Personnel | null>(null)
const upgradeInfo = ref<{ currentLevel: string; newLevel: string }>({ currentLevel: '', newLevel: '' })
const pagination = reactive({
pageNumber: 1,
@ -196,6 +234,10 @@ const approvalForm = reactive({
level: 'Company'
})
const upgradeApprovalForm = reactive({
comments: ''
})
function getStatusName(status: PersonnelStatus): string {
switch (status) {
case PersonnelStatus.Pending: return '待审批'
@ -218,6 +260,19 @@ function getDisplayStatus(person: Personnel): string {
return getStatusName(person.status)
}
//
function getDisplayStatusType(person: Personnel): string {
if (person.status === PersonnelStatus.Pending) {
//
if (canApprovePersonnel(person)) {
return 'warning' // -
} else {
return 'primary' // -
}
}
return getStatusTagType(person.status)
}
function getStatusTagType(status: PersonnelStatus): string {
switch (status) {
case PersonnelStatus.Pending: return 'warning'
@ -296,20 +351,27 @@ function canUpgrade(person: Personnel): boolean {
// 2.
// 3.
// 4.
//
// 使""""
if (!authStore.user || !person.approvedLevel || !person.approvedByUnitId) return false
if (person.approvedLevel === PersonnelLevel.Division) return false
if (person.pendingUpgradeByUnitId) return false //
//
if (person.approvedByUnitId === authStore.user.organizationalUnitId) return true
//
// 使
if (authStore.organizationalLevelNum === 1 && person.approvedLevel === PersonnelLevel.Regiment) {
return true
return false
}
return false
//
return person.approvedByUnitId === authStore.user.organizationalUnitId
}
//
function canDirectUpgrade(person: Personnel): boolean {
if (!authStore.user || !person.approvedLevel) return false
if (person.pendingUpgradeByUnitId) return false //
//
return authStore.organizationalLevelNum === 1 && person.approvedLevel === PersonnelLevel.Regiment
}
//
@ -349,6 +411,32 @@ function canApprovePersonnel(person: Personnel): boolean {
return true
}
//
function canDelete(person: Personnel): boolean {
if (!authStore.user) return false
const userLevelNum = authStore.organizationalLevelNum
//
if (person.status === PersonnelStatus.Pending) {
return true
}
//
if (person.status === PersonnelStatus.Approved && person.approvedLevel) {
const personnelLevelNum = {
'Division': 1,
'Regiment': 2,
'Battalion': 3,
'Company': 4
}[person.approvedLevel] || 4
return userLevelNum <= personnelLevelNum
}
return false
}
function handleApprove(person: Personnel) {
selectedPerson.value = person
approvalForm.comments = ''
@ -380,35 +468,71 @@ async function handleRequestUpgrade(person: Personnel) {
}
}
//
async function handleApproveUpgrade(person: Personnel) {
//
async function handleDirectUpgrade(person: Personnel) {
try {
const currentLevel = getLevelName(person.approvedLevel!)
//
const levelOrder = ['Company', 'Battalion', 'Regiment', 'Division']
const currentIndex = levelOrder.indexOf(person.approvedLevel || 'Company')
const newLevel = currentIndex < levelOrder.length - 1 ? getLevelName(levelOrder[currentIndex + 1] as PersonnelLevel) : currentLevel
await ElMessageBox.confirm(
`确定要批准 "${person.name}" 的向上申报请求吗?\n批准后该人才将从${currentLevel}升级为${newLevel}`,
'确认审批向上申报',
`确定要将 "${person.name}"(团级人才)直接升级为师级人才吗?`,
'确认直接升级',
{
type: 'warning',
confirmButtonText: '批准',
confirmButtonText: '确认升级',
cancelButtonText: '取消'
}
)
await personnelApi.approveUpgrade(person.id)
ElMessage.success('向上申报已批准,人才等级已升级')
await personnelApi.directUpgrade(person.id)
ElMessage.success('已成功升级为师级人才')
await loadPersonnel()
} catch (error: any) {
if (error !== 'cancel') {
ElMessage.error(error.response?.data?.message || '审批失败')
ElMessage.error(error.response?.data?.message || '升级失败')
}
}
}
//
async function handleApproveUpgrade(person: Personnel) {
const currentLevel = getLevelName(person.approvedLevel!)
//
const levelOrder = ['Company', 'Battalion', 'Regiment', 'Division']
const currentIndex = levelOrder.indexOf(person.approvedLevel || 'Company')
const newLevel = currentIndex < levelOrder.length - 1 ? getLevelName(levelOrder[currentIndex + 1] as PersonnelLevel) : currentLevel
selectedPerson.value = person
upgradeInfo.value = { currentLevel, newLevel }
upgradeApprovalForm.comments = ''
showUpgradeApprovalDialog.value = true
}
//
async function submitUpgradeApproval(approved: boolean) {
if (!selectedPerson.value) return
//
if (!approved && !upgradeApprovalForm.comments.trim()) {
ElMessage.warning('请填写拒绝原因')
return
}
approvingUpgrade.value = true
try {
if (approved) {
await personnelApi.approveUpgrade(selectedPerson.value.id)
ElMessage.success('向上申报已批准,人才等级已升级')
} else {
await personnelApi.rejectUpgrade(selectedPerson.value.id, upgradeApprovalForm.comments)
ElMessage.success('向上申报已拒绝')
}
showUpgradeApprovalDialog.value = false
await loadPersonnel()
} catch (error: any) {
ElMessage.error(error.response?.data?.message || '审批失败')
} finally {
approvingUpgrade.value = false
}
}
async function submitApproval(approved: boolean) {
if (!selectedPerson.value) return