完善机组车台功能,并导入机组车台 / 车间班次数据

一、机组车台功能完善
- 车间下拉改为只取「前缫」职责类型的部门(新增接口 /api/jizuchetai/qianxian-deps)
- 补齐校验:车间、机组号、开始车台号必填;结束车台号不得小于开始车台号;同车间同机组不可重复
- 前端页面同步调整:车间改为下拉选择、增加必填提示、表格加 row-key

二、数据导入(新增 ProdSeedData,幂等导入器)
- biz_prod_qs_jzct:机组车台 35 条(其中 11 条已删除)→ 有效 24 条
- biz_prod_qs_cjbc:车间班次 7 条(其中 1 条已删除)→ 有效 6 条
- F9 的部门 Id 通过部门名称映射到本系统部门;已删除的数据不导入;
  已存在且一致的数据不重复写入,缺失的自动补回
This commit is contained in:
yanpeng200212
2026-10-14 09:42:06 +08:00
parent 0e6d4dce53
commit 8c4c464362
9 changed files with 817 additions and 44 deletions
@@ -130,6 +130,11 @@ public class JizuChetaiController : ControllerBase
public async Task<IActionResult> GetPaged([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20, [FromQuery] string keyword = "") public async Task<IActionResult> GetPaged([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20, [FromQuery] string keyword = "")
=> Ok(ApiResult<PageResult<JizuChetaiDto>>.Ok(await _svc.GetPagedAsync(pageIndex, pageSize, keyword))); => Ok(ApiResult<PageResult<JizuChetaiDto>>.Ok(await _svc.GetPagedAsync(pageIndex, pageSize, keyword)));
// 车间下拉(前缫车间)
[HttpGet("qianxian-deps")]
public async Task<IActionResult> GetQianxianDeps()
=> Ok(ApiResult<List<DeptOptionDto>>.Ok(await _svc.GetQianxianDepsAsync()));
[HttpPost] [HttpPost]
public async Task<IActionResult> Create([FromBody] JizuChetaiDto dto) public async Task<IActionResult> Create([FromBody] JizuChetaiDto dto)
{ {
Binary file not shown.
@@ -84,3 +84,10 @@ public class JizuChetaiDto
public int ChetaiStart { get; set; } public int ChetaiStart { get; set; }
public int ChetaiEnd { get; set; } public int ChetaiEnd { get; set; }
} }
// ---- 部门下拉项(机组车台等模块选用车间)----
public class DeptOptionDto
{
public long Id { get; set; }
public string Name { get; set; } = "";
}
@@ -4,6 +4,7 @@ using F9WebAiTest.Application.Dtos.Base;
using F9WebAiTest.Domain.Common; using F9WebAiTest.Domain.Common;
using F9WebAiTest.Domain.Entities; using F9WebAiTest.Domain.Entities;
using F9WebAiTest.Domain.Entities.Base; using F9WebAiTest.Domain.Entities.Base;
using F9WebAiTest.Domain.Enums.SYS.Departments;
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments; using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
namespace F9WebAiTest.Application.Services.Base; namespace F9WebAiTest.Application.Services.Base;
@@ -251,6 +252,9 @@ public interface IJizuChetaiService
Task<JizuChetaiDto> CreateAsync(JizuChetaiDto dto); Task<JizuChetaiDto> CreateAsync(JizuChetaiDto dto);
Task<JizuChetaiDto> UpdateAsync(long id, JizuChetaiDto dto); Task<JizuChetaiDto> UpdateAsync(long id, JizuChetaiDto dto);
Task DeleteAsync(long id); Task DeleteAsync(long id);
/// <summary>车间下拉:只取职责类型为「前缫」的部门</summary>
Task<List<DeptOptionDto>> GetQianxianDepsAsync();
} }
public class JizuChetaiService : IJizuChetaiService public class JizuChetaiService : IJizuChetaiService
@@ -296,6 +300,7 @@ public class JizuChetaiService : IJizuChetaiService
public async Task<JizuChetaiDto> CreateAsync(JizuChetaiDto dto) public async Task<JizuChetaiDto> CreateAsync(JizuChetaiDto dto)
{ {
await ValidateAsync(dto, 0);
var e = new JizuChetai { DepartmentId = dto.DepartmentId, Jizu = dto.Jizu, ChetaiStart = dto.ChetaiStart, ChetaiEnd = dto.ChetaiEnd }; var e = new JizuChetai { DepartmentId = dto.DepartmentId, Jizu = dto.Jizu, ChetaiStart = dto.ChetaiStart, ChetaiEnd = dto.ChetaiEnd };
await _repo.AddAsync(e); await _repo.AddAsync(e);
await _repo.SaveChangesAsync(); await _repo.SaveChangesAsync();
@@ -306,12 +311,36 @@ public class JizuChetaiService : IJizuChetaiService
{ {
var e = await _repo.GetByIdAsync(id); var e = await _repo.GetByIdAsync(id);
if (e == null) throw new ServiceException("记录不存在", 404); if (e == null) throw new ServiceException("记录不存在", 404);
await ValidateAsync(dto, id);
e.DepartmentId = dto.DepartmentId; e.Jizu = dto.Jizu; e.ChetaiStart = dto.ChetaiStart; e.ChetaiEnd = dto.ChetaiEnd; e.DepartmentId = dto.DepartmentId; e.Jizu = dto.Jizu; e.ChetaiStart = dto.ChetaiStart; e.ChetaiEnd = dto.ChetaiEnd;
await _repo.UpdateAsync(e); await _repo.UpdateAsync(e);
await _repo.SaveChangesAsync(); await _repo.SaveChangesAsync();
return await GetDto(e); return await GetDto(e);
} }
/// <summary>校验:车间、机组号、车台范围必填且合理,同一车间的机组不可重复</summary>
private async Task ValidateAsync(JizuChetaiDto dto, long excludeId)
{
if (dto.DepartmentId <= 0) throw new ServiceException("请选择车间");
if (dto.Jizu <= 0) throw new ServiceException("机组号必须大于 0");
if (dto.ChetaiStart <= 0) throw new ServiceException("开始车台号必须大于 0");
if (dto.ChetaiEnd < dto.ChetaiStart) throw new ServiceException("结束车台号不能小于开始车台号");
var all = await _repo.GetAllAsync();
if (all.Any(x => x.Id != excludeId && x.DepartmentId == dto.DepartmentId && x.Jizu == dto.Jizu))
throw new ServiceException("该车间的此机组已存在,请勿重复配置");
}
public async Task<List<DeptOptionDto>> GetQianxianDepsAsync()
{
var depts = await _deptRepo.GetAllAsync();
return depts
.Where(d => d.DutyType == Department_DutyType_Enum.前缫)
.OrderBy(d => d.SortOrder).ThenBy(d => d.Id)
.Select(d => new DeptOptionDto { Id = d.Id, Name = d.Name })
.ToList();
}
public async Task DeleteAsync(long id) public async Task DeleteAsync(long id)
{ {
var e = await _repo.GetByIdAsync(id); var e = await _repo.GetByIdAsync(id);
@@ -68,6 +68,9 @@ public static class DbSeeder
GongyingshangSeedData.Seed(fsql); GongyingshangSeedData.Seed(fsql);
EmployeeSeedData.Seed(fsql); EmployeeSeedData.Seed(fsql);
// 6.655 生产模块基础数据(机组车台 + 车间班次,F9 导出数据幂等导入)
ProdSeedData.Seed(fsql, snowflake);
// 6.66 基础数据菜单(工种管理、账期管理、注册表、原料庄口、物资目录、往来单位) // 6.66 基础数据菜单(工种管理、账期管理、注册表、原料庄口、物资目录、往来单位)
SeedWorkTypeMenus(fsql, snowflake); SeedWorkTypeMenus(fsql, snowflake);
SeedZhangqiMenus(fsql, snowflake); SeedZhangqiMenus(fsql, snowflake);
@@ -0,0 +1,202 @@
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using FreeSql;
using F9WebAiTest.Application.Interfaces.SnowflakeID;
using F9WebAiTest.Domain.Entities.Base;
using F9WebAiTest.Domain.Entities.Prod;
using F9WebAiTest.Domain.Enums.SYS.Employees;
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
namespace F9WebAiTest.Infrastructure.Data;
/// <summary>
/// 生产模块基础数据种子(来源于 F9 系统导出):
/// 1) biz_prod_qs_jzct —— 机组车台
/// 2) biz_prod_qs_cjbc —— 车间班次
/// 幂等:按「车间 + 机组」/「车间 + 班次」匹配,存在则校正,不存在则新增;已删除的数据不导入。
/// </summary>
public static class ProdSeedData
{
private const string JzctResource = "F9WebAiTest.Infrastructure.Data.SeedData.biz_prod_qs_jzct.json";
private const string CjbcResource = "F9WebAiTest.Infrastructure.Data.SeedData.biz_prod_qs_cjbc.json";
private static readonly JsonSerializerOptions JsonOpts = new()
{
PropertyNameCaseInsensitive = true,
NumberHandling = JsonNumberHandling.AllowReadingFromString
};
public static void Seed(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
{
SeedJizuChetai(fsql);
SeedChejianBanci(fsql);
}
// ==================== 机组车台 ====================
private static void SeedJizuChetai(IFreeSql fsql)
{
try
{
var items = Load<JzctItem>(JzctResource);
if (items.Count == 0) return;
var deptMap = BuildDeptMap(fsql);
var existing = fsql.Select<JizuChetai>().ToList()
.GroupBy(x => (x.DepartmentId, x.Jizu))
.ToDictionary(g => g.Key, g => g.First());
int added = 0, updated = 0, skipped = 0;
var unmappedDeps = new HashSet<long>();
foreach (var item in items)
{
if (!string.IsNullOrWhiteSpace(item.DeletedAt)) { skipped++; continue; }
if (!deptMap.TryGetValue(item.DepId, out var deptId))
{
unmappedDeps.Add(item.DepId);
skipped++;
continue;
}
var key = (deptId, item.Jizu);
if (existing.TryGetValue(key, out var e))
{
if (e.ChetaiStart != item.ChetaiStart || e.ChetaiEnd != item.ChetaiEnd)
{
e.ChetaiStart = item.ChetaiStart;
e.ChetaiEnd = item.ChetaiEnd;
fsql.Update<JizuChetai>().SetSource(e).ExecuteAffrows();
updated++;
}
}
else
{
fsql.Insert(new JizuChetai
{
DepartmentId = deptId,
Jizu = item.Jizu,
ChetaiStart = item.ChetaiStart,
ChetaiEnd = item.ChetaiEnd
}).ExecuteAffrows();
added++;
}
}
Console.WriteLine($"[ProdSeed] 机组车台:来源 {items.Count} 条,新增 {added} 条,更新 {updated} 条,跳过(已删除/无法映射){skipped} 条");
if (unmappedDeps.Count > 0)
Console.WriteLine($"[ProdSeed] 提示:以下 F9 部门 Id 未匹配到本系统部门,已跳过 -> {string.Join(", ", unmappedDeps)}");
}
catch (Exception ex)
{
Console.WriteLine($"[ProdSeed] 机组车台导入跳过:{ex.Message}");
}
}
// ==================== 车间班次 ====================
private static void SeedChejianBanci(IFreeSql fsql)
{
try
{
var items = Load<CjbcItem>(CjbcResource);
if (items.Count == 0) return;
var deptMap = BuildDeptMap(fsql);
var existing = fsql.Select<ChejianBanci>().ToList()
.GroupBy(x => (x.DeptId, x.Banci))
.ToDictionary(g => g.Key, g => g.First());
int added = 0, updated = 0, skipped = 0;
var unmappedDeps = new HashSet<long>();
foreach (var item in items)
{
if (!string.IsNullOrWhiteSpace(item.DeletedAt)) { skipped++; continue; }
if (!deptMap.TryGetValue(item.DepId, out var deptId))
{
unmappedDeps.Add(item.DepId);
skipped++;
continue;
}
var banci = (Prod_Banci_Enum)item.Banci;
var key = (deptId, banci);
if (existing.TryGetValue(key, out var e))
{
if (Math.Abs(e.KaoqinDayhours - item.KaoqinDayhours) > 0.001)
{
e.KaoqinDayhours = item.KaoqinDayhours;
fsql.Update<ChejianBanci>().SetSource(e).ExecuteAffrows();
updated++;
}
}
else
{
fsql.Insert(new ChejianBanci
{
DeptId = deptId,
Banci = banci,
KaoqinDayhours = item.KaoqinDayhours
}).ExecuteAffrows();
added++;
}
}
Console.WriteLine($"[ProdSeed] 车间班次:来源 {items.Count} 条,新增 {added} 条,更新 {updated} 条,跳过(已删除/无法映射){skipped} 条");
if (unmappedDeps.Count > 0)
Console.WriteLine($"[ProdSeed] 提示:以下 F9 部门 Id 未匹配到本系统部门,已跳过 -> {string.Join(", ", unmappedDeps)}");
}
catch (Exception ex)
{
Console.WriteLine($"[ProdSeed] 车间班次导入跳过:{ex.Message}");
}
}
// ==================== 公共 ====================
/// <summary>建立 F9 部门 Id → 本系统部门 Id 的映射(以部门名称为桥梁)</summary>
private static Dictionary<long, long> BuildDeptMap(IFreeSql fsql)
{
var f9NameMap = BusinessSeedData.DeptData.ToDictionary(x => x.Id, x => x.Name);
var localNameMap = fsql.Select<SysDept.Department>().ToList()
.GroupBy(d => d.Name)
.ToDictionary(g => g.Key, g => g.First().Id);
var map = new Dictionary<long, long>();
foreach (var (f9Id, name) in f9NameMap)
{
if (localNameMap.TryGetValue(name, out var localId)) map[f9Id] = localId;
}
return map;
}
private static List<T> Load<T>(string resourceName)
{
var asm = Assembly.GetExecutingAssembly();
using var stream = asm.GetManifestResourceStream(resourceName);
if (stream == null) return new List<T>();
return JsonSerializer.Deserialize<List<T>>(stream, JsonOpts) ?? new List<T>();
}
// ---- 数据项 ----
private class JzctItem
{
[JsonPropertyName("id")] public long Id { get; set; }
[JsonPropertyName("deleted_at")] public string? DeletedAt { get; set; }
[JsonPropertyName("dep_id")] public long DepId { get; set; }
[JsonPropertyName("jizu")] public int Jizu { get; set; }
[JsonPropertyName("chetai_start")] public int ChetaiStart { get; set; }
[JsonPropertyName("chetai_end")] public int ChetaiEnd { get; set; }
}
private class CjbcItem
{
[JsonPropertyName("id")] public long Id { get; set; }
[JsonPropertyName("deleted_at")] public string? DeletedAt { get; set; }
[JsonPropertyName("dep_id")] public long DepId { get; set; }
[JsonPropertyName("banci")] public int Banci { get; set; }
[JsonPropertyName("kaoqin_dayhours")] public double KaoqinDayhours { get; set; }
}
}
@@ -0,0 +1,51 @@
[
{
"id": 18269488810037248,
"deleted_at": "",
"dep_id": 16043931070369792,
"banci": 1,
"kaoqin_dayhours": 9
},
{
"id": 18500791564242944,
"deleted_at": "10/12/2025 13:05:07.557",
"dep_id": 16043931070369792,
"banci": 2,
"kaoqin_dayhours": 6
},
{
"id": 20744935502254080,
"deleted_at": "",
"dep_id": 16047387197837312,
"banci": 1,
"kaoqin_dayhours": 9
},
{
"id": 25443995760988160,
"deleted_at": "",
"dep_id": 22308600831545344,
"banci": 1,
"kaoqin_dayhours": 9
},
{
"id": 25444063712907264,
"deleted_at": "",
"dep_id": 22308683820044288,
"banci": 1,
"kaoqin_dayhours": 9
},
{
"id": 25444106268315648,
"deleted_at": "",
"dep_id": 22308683820044288,
"banci": 2,
"kaoqin_dayhours": 9
},
{
"id": 35745732161966080,
"deleted_at": "",
"dep_id": 22308600831545344,
"banci": 2,
"kaoqin_dayhours": 9
}
]
@@ -0,0 +1,422 @@
[
{
"id": 16003469039112192,
"created_by": null,
"created_at": "14/11/2025 11:52:04.367",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "14/11/2025 11:53:55.33",
"dep_id": 101122,
"jizu": 23424,
"chetai_start": 324,
"chetai_end": 2342
},
{
"id": 16047842267238400,
"created_by": null,
"created_at": "14/11/2025 14:48:23.753",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "14/11/2025 15:26:50.25",
"dep_id": 106,
"jizu": 111,
"chetai_start": 111,
"chetai_end": 118
},
{
"id": 16047909204135936,
"created_by": null,
"created_at": "14/11/2025 14:48:39.71",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "14/11/2025 14:48:48.147",
"dep_id": 107,
"jizu": 111,
"chetai_start": 119,
"chetai_end": 125
},
{
"id": 16054811803783168,
"created_by": null,
"created_at": "14/11/2025 15:16:05.42",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "14/11/2025 15:26:47.48",
"dep_id": 107,
"jizu": 100,
"chetai_start": 100,
"chetai_end": 110
},
{
"id": 17090359817408512,
"created_by": null,
"created_at": "17/11/2025 11:50:59.307",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "20/11/2025 17:47:04.083",
"dep_id": 16047387197837312,
"jizu": 7,
"chetai_start": 3,
"chetai_end": 6
},
{
"id": 17124677390438400,
"created_by": null,
"created_at": "17/11/2025 14:07:21.253",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "17/11/2025 15:44:43.493",
"dep_id": 16043931070369792,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 3
},
{
"id": 17149134003769344,
"created_by": null,
"created_at": "17/11/2025 15:44:32.167",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "20/11/2025 17:47:08.097",
"dep_id": 16043931070369792,
"jizu": 100,
"chetai_start": 5,
"chetai_end": 11
},
{
"id": 17895860616564736,
"created_by": null,
"created_at": "19/11/2025 17:11:45.693",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "20/11/2025 17:47:05.973",
"dep_id": 16047387197837312,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 18
},
{
"id": 18267185730293760,
"created_by": null,
"created_at": "20/11/2025 17:47:16.487",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16043931070369792,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 20
},
{
"id": 18267223122513920,
"created_by": null,
"created_at": "20/11/2025 17:47:25.383",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16043931070369792,
"jizu": 2,
"chetai_start": 21,
"chetai_end": 40
},
{
"id": 18267258883149824,
"created_by": null,
"created_at": "20/11/2025 17:47:33.907",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16043931070369792,
"jizu": 3,
"chetai_start": 41,
"chetai_end": 60
},
{
"id": 18267293058338816,
"created_by": null,
"created_at": "20/11/2025 17:47:42.057",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16043931070369792,
"jizu": 4,
"chetai_start": 61,
"chetai_end": 80
},
{
"id": 18267333134913536,
"created_by": null,
"created_at": "20/11/2025 17:47:51.61",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16043931070369792,
"jizu": 5,
"chetai_start": 81,
"chetai_end": 100
},
{
"id": 18267372687200256,
"created_by": null,
"created_at": "20/11/2025 17:48:01.04",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16043931070369792,
"jizu": 6,
"chetai_start": 101,
"chetai_end": 120
},
{
"id": 18267414189838336,
"created_by": null,
"created_at": "20/11/2025 17:48:10.937",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16047387197837312,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 20
},
{
"id": 18267443470274560,
"created_by": null,
"created_at": "20/11/2025 17:48:17.917",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16047387197837312,
"jizu": 2,
"chetai_start": 21,
"chetai_end": 40
},
{
"id": 18267488391270400,
"created_by": null,
"created_at": "20/11/2025 17:48:28.627",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16047387197837312,
"jizu": 3,
"chetai_start": 41,
"chetai_end": 60
},
{
"id": 18267530309144576,
"created_by": null,
"created_at": "20/11/2025 17:48:38.62",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16047387197837312,
"jizu": 4,
"chetai_start": 61,
"chetai_end": 80
},
{
"id": 18267562336849920,
"created_by": null,
"created_at": "20/11/2025 17:48:46.257",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16047387197837312,
"jizu": 5,
"chetai_start": 81,
"chetai_end": 100
},
{
"id": 18267593483751424,
"created_by": null,
"created_at": "20/11/2025 17:48:53.683",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 16047387197837312,
"jizu": 6,
"chetai_start": 101,
"chetai_end": 120
},
{
"id": 18500840679542784,
"created_by": null,
"created_at": "21/11/2025 09:15:44.15",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "24/11/2025 14:30:02.643",
"dep_id": 16043931070369792,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 100
},
{
"id": 20436938137800704,
"created_by": null,
"created_at": "26/11/2025 17:29:05.753",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "10/12/2025 13:06:18.217",
"dep_id": 16047387197837312,
"jizu": 7,
"chetai_start": 121,
"chetai_end": 140
},
{
"id": 25444260044083200,
"created_by": "余华",
"created_at": "10/12/2025 13:06:24.337",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308600831545344,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 20
},
{
"id": 25444289462931456,
"created_by": "余华",
"created_at": "10/12/2025 13:06:31.34",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308600831545344,
"jizu": 2,
"chetai_start": 21,
"chetai_end": 40
},
{
"id": 25444329623392256,
"created_by": "余华",
"created_at": "10/12/2025 13:06:40.917",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308600831545344,
"jizu": 3,
"chetai_start": 41,
"chetai_end": 60
},
{
"id": 25444371641929728,
"created_by": "余华",
"created_at": "10/12/2025 13:06:50.933",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308600831545344,
"jizu": 4,
"chetai_start": 61,
"chetai_end": 80
},
{
"id": 25444402046439424,
"created_by": "余华",
"created_at": "10/12/2025 13:06:58.183",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308600831545344,
"jizu": 5,
"chetai_start": 81,
"chetai_end": 100
},
{
"id": 25444430567706624,
"created_by": "余华",
"created_at": "10/12/2025 13:07:04.983",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308600831545344,
"jizu": 6,
"chetai_start": 101,
"chetai_end": 120
},
{
"id": 25444457453195264,
"created_by": "余华",
"created_at": "10/12/2025 13:07:11.393",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308683820044288,
"jizu": 1,
"chetai_start": 1,
"chetai_end": 20
},
{
"id": 25444480718999552,
"created_by": "余华",
"created_at": "10/12/2025 13:07:16.94",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308683820044288,
"jizu": 2,
"chetai_start": 21,
"chetai_end": 40
},
{
"id": 25444507617071104,
"created_by": "余华",
"created_at": "10/12/2025 13:07:23.353",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308683820044288,
"jizu": 3,
"chetai_start": 41,
"chetai_end": 60
},
{
"id": 25444531084201984,
"created_by": "余华",
"created_at": "10/12/2025 13:07:28.947",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308683820044288,
"jizu": 4,
"chetai_start": 61,
"chetai_end": 80
},
{
"id": 25444554995929088,
"created_by": "余华",
"created_at": "10/12/2025 13:07:34.65",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308683820044288,
"jizu": 5,
"chetai_start": 81,
"chetai_end": 100
},
{
"id": 25444577833914368,
"created_by": "余华",
"created_at": "10/12/2025 13:07:40.093",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": null,
"dep_id": 22308683820044288,
"jizu": 6,
"chetai_start": 101,
"chetai_end": 120
},
{
"id": 37410963145625600,
"created_by": "余华",
"created_at": "12/1/2026 13:37:48.69",
"update_by": null,
"updated_at": "1/1/1970 00:00:00",
"deleted_at": "13/2/2026 13:59:24.617",
"dep_id": 22308683820044288,
"jizu": -1,
"chetai_start": 0,
"chetai_end": 0
}
]
+98 -44
View File
@@ -1,61 +1,103 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import request from '../../../utils/request'; import request from '../../../utils/request';
import { buildDeptTree } from '../../../utils/dept';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
const list = ref([]); const list = ref([]);
const total = ref(0); const total = ref(0);
const loading = ref(false); const loading = ref(false);
const deptTree = ref([]);
const query = reactive({ pageIndex: 1, pageSize: 20, keyword: '' }); const query = reactive({ pageIndex: 1, pageSize: 20, keyword: '' });
// 车间下拉:只取职责类型为「前缫」的部门(后端接口)
const deptOptions = ref([]);
const dialogVisible = ref(false); const dialogVisible = ref(false);
const dialogTitle = ref('新增机组车台'); const dialogTitle = ref('新增机组车台');
const isEdit = ref(false); const isEdit = ref(false);
const editId = ref(null); const editId = ref(null);
const form = reactive({ departmentId: null, jizu: 0, chetaiStart: 0, chetaiEnd: 0 }); const form = reactive({ departmentId: 0, jizu: 0, chetaiStart: 0, chetaiEnd: 0 });
async function load() { async function load() {
loading.value = true; loading.value = true;
try { try {
const res = await request.get('/jizuchetai', { params: query }); const res = await request.get('/jizuchetai', { params: query });
list.value = res.data.items; list.value = res.data.items || [];
total.value = res.data.total; total.value = res.data.total || 0;
} finally { } finally {
loading.value = false; loading.value = false;
} }
} }
async function loadDepts() { async function loadDepts() {
const res = await request.get('/sys/department'); try {
deptTree.value = buildDeptTree(res.data); const res = await request.get('/jizuchetai/qianxian-deps');
deptOptions.value = res.data || [];
} catch (e) {
deptOptions.value = [];
}
}
// 分页组件挂载时会触发一次 current-change:页码未变化时忽略,避免首次打开重复请求
function handlePageChange(p) {
if (p === query.pageIndex) return;
query.pageIndex = p;
load();
}
function handleSearch() {
query.pageIndex = 1;
load();
} }
function openCreate() { function openCreate() {
isEdit.value = false; isEdit.value = false;
dialogTitle.value = '新增机组车台'; dialogTitle.value = '新增机组车台';
Object.assign(form, { departmentId: null, jizu: 0, chetaiStart: 0, chetaiEnd: 0 }); Object.assign(form, {
departmentId: Number(deptOptions.value[0]?.id) || 0,
jizu: 0,
chetaiStart: 0,
chetaiEnd: 0
});
dialogVisible.value = true; dialogVisible.value = true;
} }
function openEdit(row) { function openEdit(row) {
isEdit.value = true; isEdit.value = true;
editId.value = row.id; editId.value = row.id;
dialogTitle.value = '编辑机组车台'; dialogTitle.value = '修改机组车台';
Object.assign(form, { departmentId: row.departmentId, jizu: row.jizu, chetaiStart: row.chetaiStart, chetaiEnd: row.chetaiEnd }); Object.assign(form, {
departmentId: Number(row.departmentId),
jizu: Number(row.jizu),
chetaiStart: Number(row.chetaiStart),
chetaiEnd: Number(row.chetaiEnd)
});
dialogVisible.value = true; dialogVisible.value = true;
} }
async function save() { async function save() {
if (!form.departmentId) { ElMessage.warning('请选择车间'); return; } if (!form.departmentId) {
ElMessage.warning('请选择车间');
return;
}
if (!form.jizu || form.jizu <= 0) {
ElMessage.warning('机组号必须大于 0');
return;
}
if (!form.chetaiStart || form.chetaiStart <= 0) {
ElMessage.warning('开始车台号必须大于 0');
return;
}
if (form.chetaiEnd < form.chetaiStart) {
ElMessage.warning('结束车台号不能小于开始车台号');
return;
}
try { try {
if (isEdit.value) { if (isEdit.value) {
await request.put(`/jizuchetai/${editId.value}`, form); await request.put(`/jizuchetai/${editId.value}`, form);
ElMessage.success('更新成功'); ElMessage.success('修改成功');
} else { } else {
await request.post('/jizuchetai', form); await request.post('/jizuchetai', form);
ElMessage.success('创建成功'); ElMessage.success('新增成功');
} }
dialogVisible.value = false; dialogVisible.value = false;
load(); load();
@@ -63,7 +105,7 @@ async function save() {
} }
async function del(row) { async function del(row) {
await ElMessageBox.confirm(`确定删除机组「${row.jizu}」吗?`, '提示', { type: 'warning' }); await ElMessageBox.confirm(`确定删除「${row.departmentName} · 机组${row.jizu}」吗?`, '提示', { type: 'warning' });
try { try {
await request.delete(`/jizuchetai/${row.id}`); await request.delete(`/jizuchetai/${row.id}`);
ElMessage.success('删除成功'); ElMessage.success('删除成功');
@@ -71,13 +113,10 @@ async function del(row) {
} catch (e) {} } catch (e) {}
} }
function handlePageChange(p) { onMounted(() => {
// 分页组件挂载时会触发一次 current-change,页码未变化时忽略,避免首次打开重复请求 loadDepts();
if (p === query.pageIndex) return;
query.pageIndex = p;
load(); load();
} });
onMounted(() => { load(); loadDepts(); });
</script> </script>
<template> <template>
@@ -90,40 +129,55 @@ onMounted(() => { load(); loadDepts(); });
</template> </template>
<div class="search-bar"> <div class="search-bar">
<el-input v-model="query.keyword" placeholder="车间/机组号" clearable style="width: 200px" @keyup.enter="query.pageIndex=1;load()" /> <el-input
<el-button type="primary" @click="query.pageIndex=1;load()">搜索</el-button> v-model="query.keyword"
placeholder="车间 / 机组号"
clearable
style="width: 220px"
@keyup.enter="handleSearch"
/>
<el-button type="primary" @click="handleSearch">查询</el-button>
</div> </div>
<el-table v-loading="loading" :data="list" border stripe> <el-table v-loading="loading" :data="list" row-key="id" border stripe>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="departmentName" label="车间" min-width="200" /> <el-table-column prop="departmentName" label="车间" min-width="200" />
<el-table-column prop="jizu" label="机组号" width="120" /> <el-table-column prop="jizu" label="机组号" width="120" />
<el-table-column prop="chetaiStart" label="开始车台号" width="120" /> <el-table-column prop="chetaiStart" label="开始车台号" width="130" />
<el-table-column prop="chetaiEnd" label="结束车台号" width="120" /> <el-table-column prop="chetaiEnd" label="结束车台号" width="130" />
<el-table-column label="操作" width="140" fixed="right"> <el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button size="small" type="primary" @click="openEdit(row)">编辑</el-button> <el-button size="small" type="primary" link @click="openEdit(row)">编辑</el-button>
<el-button size="small" type="danger" @click="del(row)">删除</el-button> <el-button size="small" type="danger" link @click="del(row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-pagination class="pagination" :current-page="query.pageIndex" :page-size="query.pageSize" :total="total" layout="total, prev, pager, next" @current-change="handlePageChange" /> <el-pagination
class="pagination"
:current-page="query.pageIndex"
:page-size="query.pageSize"
:total="total"
layout="total, prev, pager, next"
@current-change="handlePageChange"
/>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="480px"> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="520px">
<el-form :model="form" label-width="100px"> <el-form :model="form" label-width="110px">
<el-form-item label="车间"> <el-form-item label="车间" required>
<el-tree-select <el-select v-model="form.departmentId" placeholder="请选择车间" style="width: 100%">
v-model="form.departmentId" <el-option v-for="d in deptOptions" :key="d.id" :label="d.name" :value="Number(d.id)" />
:data="deptTree" </el-select>
:props="{ label: 'name', value: 'id', children: 'children' }" </el-form-item>
check-strictly <el-form-item label="机组号" required>
placeholder="选择车间" <el-input-number v-model="form.jizu" :min="1" :precision="0" style="width: 100%" />
style="width: 100%" </el-form-item>
/> <el-form-item label="开始车台号" required>
<el-input-number v-model="form.chetaiStart" :min="1" :precision="0" style="width: 100%" />
</el-form-item>
<el-form-item label="结束车台号" required>
<el-input-number v-model="form.chetaiEnd" :min="1" :precision="0" style="width: 100%" />
</el-form-item> </el-form-item>
<el-form-item label="机组号"><el-input-number v-model="form.jizu" :min="0" /></el-form-item>
<el-form-item label="开始车台号"><el-input-number v-model="form.chetaiStart" :min="0" /></el-form-item>
<el-form-item label="结束车台号"><el-input-number v-model="form.chetaiEnd" :min="0" /></el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="dialogVisible = false">取消</el-button> <el-button @click="dialogVisible = false">取消</el-button>
@@ -135,6 +189,6 @@ onMounted(() => { load(); loadDepts(); });
<style scoped> <style scoped>
.card-header { display: flex; justify-content: space-between; align-items: center; } .card-header { display: flex; justify-content: space-between; align-items: center; }
.search-bar { display: flex; gap: 12px; margin-bottom: 16px; } .search-bar { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.pagination { margin-top: 16px; justify-content: flex-end; } .pagination { margin-top: 16px; justify-content: flex-end; }
</style> </style>