bug修改

This commit is contained in:
18631081161 2026-01-20 14:47:25 +08:00
parent 599fa05ae6
commit 0cd0fce8c2
5 changed files with 122 additions and 15 deletions

View File

@ -1177,14 +1177,53 @@ public class PersonnelController : BaseApiController
cell.Style.Border.OutsideBorder = ClosedXML.Excel.XLBorderStyleValues.Thin; cell.Style.Border.OutsideBorder = ClosedXML.Excel.XLBorderStyleValues.Thin;
} }
// 设置照片列宽度
worksheet.Column(3).Width = 15;
// 填充数据从第4行开始 // 填充数据从第4行开始
int row = 4; int row = 4;
int seq = 1; int seq = 1;
var webRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
foreach (var p in personnelList) foreach (var p in personnelList)
{ {
worksheet.Cell(row, 1).Value = seq++; worksheet.Cell(row, 1).Value = seq++;
worksheet.Cell(row, 2).Value = p.Name; worksheet.Cell(row, 2).Value = p.Name;
worksheet.Cell(row, 3).Value = ""; // 照片列留空
// 处理照片 - 2寸照片比例 35mm x 49mm (约 3:4)
worksheet.Row(row).Height = 70; // 设置行高
if (!string.IsNullOrEmpty(p.PhotoPath))
{
try
{
// PhotoPath 格式如 "/uploads/xxx.jpg",需要转换为实际文件路径
var photoRelativePath = p.PhotoPath.TrimStart('/').Replace("/", Path.DirectorySeparatorChar.ToString());
var photoFullPath = Path.Combine(webRootPath, photoRelativePath);
if (System.IO.File.Exists(photoFullPath))
{
var picture = worksheet.AddPicture(photoFullPath);
// 先移动到目标单元格
var targetCell = worksheet.Cell(row, 3);
picture.MoveTo(targetCell, 18, 3); // 偏移实现居中
// 2寸照片比例宽35mm 高49mm按比例缩放 (38 x 53 像素)
picture.WithSize(38, 53);
}
else
{
worksheet.Cell(row, 3).Value = "图片不存在";
}
}
catch
{
worksheet.Cell(row, 3).Value = "图片加载失败";
}
}
else
{
worksheet.Cell(row, 3).Value = "";
}
worksheet.Cell(row, 4).Value = p.Unit ?? p.SubmittedByUnit?.Name ?? ""; worksheet.Cell(row, 4).Value = p.Unit ?? p.SubmittedByUnit?.Name ?? "";
worksheet.Cell(row, 5).Value = p.Position; worksheet.Cell(row, 5).Value = p.Position;
worksheet.Cell(row, 6).Value = p.Rank; worksheet.Cell(row, 6).Value = p.Rank;
@ -1381,6 +1420,19 @@ public class PersonnelController : BaseApiController
continue; continue;
} }
// 检查士兵证号是否重复
var idNumber = GetNullIfEmpty(row.Cell(7).GetString());
if (!string.IsNullOrEmpty(idNumber))
{
var existingPersonnel = await _personnelService.GetByIdNumberAsync(idNumber);
if (existingPersonnel != null)
{
failCount++;
importResults.Add(new { row = row.RowNumber(), success = false, message = $"士兵证号 {idNumber} 已存在" });
continue;
}
}
var birthDate = GetNullIfEmpty(row.Cell(13).GetString()); var birthDate = GetNullIfEmpty(row.Cell(13).GetString());
var age = CalculateAge(birthDate); var age = CalculateAge(birthDate);
@ -1389,7 +1441,7 @@ public class PersonnelController : BaseApiController
Name = name, Name = name,
Gender = "男", // 默认性别,模板中没有性别列 Gender = "男", // 默认性别,模板中没有性别列
Age = age, Age = age,
IdNumber = GetNullIfEmpty(row.Cell(7).GetString()) ?? "", // 士兵证号 IdNumber = idNumber ?? "", // 士兵证号
Unit = unit, Unit = unit,
Position = position, Position = position,
Rank = rank, Rank = rank,

View File

@ -40,6 +40,13 @@ public class PersonnelService : IPersonnelService
.FirstOrDefaultAsync(p => p.Id == id); .FirstOrDefaultAsync(p => p.Id == id);
} }
public async Task<Personnel?> GetByIdNumberAsync(string idNumber)
{
if (string.IsNullOrEmpty(idNumber)) return null;
return await _context.Personnel
.FirstOrDefaultAsync(p => p.IdNumber == idNumber);
}
public async Task<IEnumerable<Personnel>> GetByUnitAsync(int unitId, bool includeSubordinates = false) public async Task<IEnumerable<Personnel>> GetByUnitAsync(int unitId, bool includeSubordinates = false)
{ {
if (includeSubordinates) if (includeSubordinates)
@ -111,12 +118,21 @@ public class PersonnelService : IPersonnelService
// 例如:营级单位提交的人才,团级审批后应为"营级人才" // 例如:营级单位提交的人才,团级审批后应为"营级人才"
actualLevel = (PersonnelLevel)(int)personnel.SubmittedByUnit!.Level; actualLevel = (PersonnelLevel)(int)personnel.SubmittedByUnit!.Level;
// 师部(最高级别)可以审批自己提交的人才,此时人才等级为师级
if (approvedByUnit.Level == OrganizationalLevel.Division &&
personnel.SubmittedByUnitId == approvedByUnitId)
{
actualLevel = PersonnelLevel.Division;
}
else
{
// 验证审批单位层级必须高于提交单位层级(数值越小层级越高) // 验证审批单位层级必须高于提交单位层级(数值越小层级越高)
var unitLevelValue = (int)approvedByUnit.Level; var unitLevelValue = (int)approvedByUnit.Level;
var submittedUnitLevelValue = (int)personnel.SubmittedByUnit!.Level; var submittedUnitLevelValue = (int)personnel.SubmittedByUnit!.Level;
if (unitLevelValue >= submittedUnitLevelValue) if (unitLevelValue >= submittedUnitLevelValue)
throw new ArgumentException("审批单位层级必须高于提交单位层级"); throw new ArgumentException("审批单位层级必须高于提交单位层级");
} }
}
else if (previousStatus == PersonnelStatus.Approved) else if (previousStatus == PersonnelStatus.Approved)
{ {
// 向上申报:人员等级升级为上报单位(即之前的审批单位)的层级 // 向上申报:人员等级升级为上报单位(即之前的审批单位)的层级
@ -584,7 +600,11 @@ public class PersonnelService : IPersonnelService
if (personnel.Status == PersonnelStatus.Rejected) if (personnel.Status == PersonnelStatus.Rejected)
return false; return false;
// 本单位不能审批自己提交的人员,必须由上级单位审批 // 师部(最高级别)可以审批所有人员,包括自己提交的
if (user.OrganizationalUnit!.Level == OrganizationalLevel.Division)
return true;
// 非师部:本单位不能审批自己提交的人员,必须由上级单位审批
if (user.OrganizationalUnitId == personnel.SubmittedByUnitId) if (user.OrganizationalUnitId == personnel.SubmittedByUnitId)
return false; return false;

View File

@ -9,6 +9,7 @@ namespace MilitaryTrainingManagement.Services.Interfaces;
public interface IPersonnelService public interface IPersonnelService
{ {
Task<Personnel?> GetByIdAsync(int id); Task<Personnel?> GetByIdAsync(int id);
Task<Personnel?> GetByIdNumberAsync(string idNumber);
Task<IEnumerable<Personnel>> GetByUnitAsync(int unitId, bool includeSubordinates = false); Task<IEnumerable<Personnel>> GetByUnitAsync(int unitId, bool includeSubordinates = false);
Task<Personnel> CreateAsync(Personnel personnel); Task<Personnel> CreateAsync(Personnel personnel);
Task<Personnel> UpdateAsync(Personnel personnel); Task<Personnel> UpdateAsync(Personnel personnel);

View File

@ -102,6 +102,9 @@ function getPieOption(regiment: RegimentAllocationStats) {
const consumed = regiment.totalConsumed const consumed = regiment.totalConsumed
const remaining = regiment.totalQuota - consumed const remaining = regiment.totalQuota - consumed
// 0
const hasData = regiment.totalQuota > 0
return { return {
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
@ -130,10 +133,14 @@ function getPieOption(regiment: RegimentAllocationStats) {
labelLine: { labelLine: {
show: false show: false
}, },
data: [ data: hasData
? [
{ value: consumed, name: '已消耗', itemStyle: { color: '#67C23A' } }, { value: consumed, name: '已消耗', itemStyle: { color: '#67C23A' } },
{ value: remaining > 0 ? remaining : 0, name: '剩余', itemStyle: { color: '#E4E7ED' } } { value: remaining > 0 ? remaining : 0, name: '剩余', itemStyle: { color: '#E4E7ED' } }
] ]
: [
{ value: 1, name: '无配额', itemStyle: { color: '#E4E7ED' } }
]
} }
] ]
} }

View File

@ -669,11 +669,27 @@ async function handleFileChange(event: Event) {
} }
if (result.failCount > 0) { if (result.failCount > 0) {
ElMessage.warning(`${result.failCount}条记录导入失败`) //
console.log('导入失败详情:', result.results.filter(r => !r.success)) const failedResults = result.results.filter((r: any) => !r.success)
const failMessages = failedResults.slice(0, 3).map((r: any) => `${r.row}行: ${r.message}`).join('\n')
const moreCount = failedResults.length > 3 ? `\n...还有${failedResults.length - 3}条失败` : ''
ElMessageBox.alert(
`${result.failCount}条记录导入失败:\n\n${failMessages}${moreCount}`,
'导入失败详情',
{
type: 'warning',
confirmButtonText: '知道了'
}
)
console.log('导入失败详情:', failedResults)
} }
} catch (error: any) { } catch (error: any) {
ElMessage.error(error.response?.data?.message || '导入失败') //
const errorMsg = error.response?.data?.message || '导入失败'
//
const chineseMsg = translateErrorMessage(errorMsg)
ElMessage.error(chineseMsg)
} finally { } finally {
importing.value = false importing.value = false
// //
@ -681,6 +697,17 @@ async function handleFileChange(event: Event) {
} }
} }
//
function translateErrorMessage(msg: string): string {
if (msg.includes('duplicate key') || msg.includes('unique index') || msg.includes('IX_Personnel_IdNumber')) {
return '导入失败:存在重复的士兵证号'
}
if (msg.includes('entity changes')) {
return '导入失败:数据保存出错,请检查数据格式'
}
return msg
}
function handleView(person: Personnel) { function handleView(person: Personnel) {
router.push(`/personnel/${person.id}`) router.push(`/personnel/${person.id}`)
} }