生产模块拆分为前缫/后缫;修复菜单卡顿与重复请求;支持 IIS 单站点部署

1. 生产模块按工序拆分为两个一级菜单
   - 生产-前缫(落丝记录)、生产-后缫(纤度检验、黑板检验)
   - 旧的「生产管理」(/prod) 目录及子菜单由种子自动清理,页面组件与权限标识保持不变
   - 消息中心跳转路径、首页快捷入口路径同步更新

2. 职工管理独立为一级菜单(原挂在「基础数据」目录下),并清理历史子菜单

3. 修复侧边栏菜单展开卡顿(基础数据等子项较多的目录尤其明显)
   - 去掉 Element Plus 的 height 过渡动画(会逐帧重排全部子项)
   - 收起时改用 height:0 + visibility:hidden 而非 display:none,子项提前完成布局
   - 菜单区独立滚动并预留滚动条宽度(scrollbar-gutter),消除展开时的横向抖动
   - 菜单项增加 contain 布局隔离,淡入动画改为容器级

4. 修复页面首次打开重复请求
   - 分页组件挂载时会触发一次 current-change,页码未变化时不再重复加载(18 个页面)
   - 职工管理去掉 onMounted 与默认选中部门的重复加载
   - 工种管理、原料庄口的注册表下拉由按 key 多次请求改为一次全量拉取

5. 后端增加前端静态托管与 SPA 路由回退,支持 IIS 单站点部署(前后端同源)
6. 新增部署脚本 deploy-iis.ps1(首次部署)与 update-iis.ps1(更新发布)
This commit is contained in:
yanpeng200212
2026-10-12 14:09:59 +08:00
parent 2ffb83830c
commit c3e2665257
47 changed files with 59270 additions and 109 deletions
@@ -0,0 +1,275 @@
using FreeSql.DataAnnotations;
using F9WebAiTest.Domain.Enums.SYS.Employees;
using F9WebAiTest.Domain.Events.SYS.Employees;
namespace F9WebAiTest.Domain.Entities.SYS.Employees;
/// <summary>
/// 职工档案:部门/工种/岗位 + 基本信息 + 合同社保 + 教育经历 + 生产信息(班次/工作组/顺序号)。
/// 转正、离职、调岗、重新入职等通过行为方法变更,并单独记录变更/调岗历史。
/// </summary>
[Table(Name = "sys_employee")]
public class Employee : AggregateRoot
{
/// <summary>所属部门 Id</summary>
[Column(Name = "dep_id")] public long DepId { get; private set; }
/// <summary>工种 Id</summary>
[Column(Name = "worktype_id")] public long WorktypeId { get; private set; }
/// <summary>在职状态</summary>
[Column(Name = "work_status")] public Employee_WorkStatus_Enum WorkStatus { get; private set; }
/// <summary>工号</summary>
[Column(Name = "code")] public string Code { get; private set; }
/// <summary>姓名</summary>
[Column(Name = "name")] public string Name { get; private set; }
/// <summary>岗位</summary>
[Column(Name = "gangwei")] public string Gangwei { get; private set; }
/// <summary>电话</summary>
[Column(Name = "phone")] public string Phone { get; private set; }
/// <summary>身份证号</summary>
[Column(Name = "idcard")] public string IdCard { get; private set; }
/// <summary>出生日期</summary>
[Column(Name = "birth_date")] public DateTime? BirthDate { get; private set; }
/// <summary>住址</summary>
[Column(Name = "address")] public string Address { get; private set; }
/// <summary>性别</summary>
[Column(Name = "gender")] public string Gender { get; private set; }
/// <summary>民族</summary>
[Column(Name = "minzu")] public string Minzu { get; private set; }
/// <summary>婚姻状态</summary>
[Column(Name = "hunyin_status")] public string HunyinStatus { get; private set; }
/// <summary>户籍</summary>
[Column(Name = "huji")] public string Huji { get; private set; }
/// <summary>入职日期</summary>
[Column(Name = "ruzhi_date")] public DateTime? RuzhiDate { get; private set; }
/// <summary>离职日期</summary>
[Column(Name = "lizhi_date")] public DateTime? LizhiDate { get; private set; }
/// <summary>转正日期</summary>
[Column(Name = "zhuanzheng_date")] public DateTime? ZhuanzhengDate { get; private set; }
/// <summary>是否已转正</summary>
[Column(Name = "is_zhuanzheng")] public bool IsZhuanzheng { get; private set; }
/// <summary>紧急联系人</summary>
[Column(Name = "jinji_lianxiren")] public string JinjiLianxiren { get; private set; }
/// <summary>紧急联系人电话</summary>
[Column(Name = "jinji_lianxiren_phone")] public string JinjiLianxirenPhone { get; private set; }
/// <summary>合同类型</summary>
[Column(Name = "hetong_type")] public string HetongType { get; private set; }
/// <summary>劳动合同开始日期</summary>
[Column(Name = "hetong_start_date")] public DateTime? HetongStartDate { get; private set; }
/// <summary>劳动合同结束日期</summary>
[Column(Name = "hetong_end_date")] public DateTime? HetongEndDate { get; private set; }
/// <summary>退休日期</summary>
[Column(Name = "tuixiu_date")] public DateTime? TuixiuDate { get; private set; }
/// <summary>社保参保日期</summary>
[Column(Name = "shebao_date")] public DateTime? ShebaoDate { get; private set; }
/// <summary>社保参保情况说明</summary>
[Column(Name = "shebao_desc")] public string ShebaoDesc { get; private set; }
/// <summary>银行卡号</summary>
[Column(Name = "bankcard_code")] public string BankcardCode { get; private set; }
/// <summary>毕业院校</summary>
[Column(Name = "biye_yuanxiao")] public string BiyeYuanxiao { get; private set; }
/// <summary>专业</summary>
[Column(Name = "zhuanye")] public string Zhuanye { get; private set; }
/// <summary>学历</summary>
[Column(Name = "xueli")] public string Xueli { get; private set; }
/// <summary>班次(甲/乙)</summary>
[Column(Name = "banci")] public Prod_Banci_Enum? Banci { get; private set; }
/// <summary>工作组</summary>
[Column(Name = "work_group")] public string WorkGroup { get; private set; }
/// <summary>顺序号</summary>
[Column(Name = "sort")] public int Sort { get; private set; }
/// <summary>姓名拼音(简拼)</summary>
[Column(Name = "name_pinyin")] public string NamePinyin { get; private set; }
/// <summary>备注</summary>
[Column(Name = "memo")] public string Memo { get; private set; }
/// <summary>离职原因</summary>
[Column(Name = "lizhi_yuanyin")] public string LizhiYuanyin { get; private set; }
public Employee() { }
public Employee(long id, long depId, long worktypeId, Employee_WorkStatus_Enum workStatus, string code, string name,
string gangwei, string phone, string idCard, DateTime? birthDate, string address, string gender, string minzu,
string hunyinStatus, string huji, DateTime? ruzhiDate, DateTime? lizhiDate, DateTime? zhuanzhengDate,
bool isZhuanzheng, string jinjiLianxiren, string jinjiLianxirenPhone, string hetongType,
DateTime? hetongStartDate, DateTime? hetongEndDate, DateTime? tuixiuDate, DateTime? shebaoDate, string shebaoDesc,
string bankcardCode, string biyeYuanxiao, string zhuanye, string xueli, Prod_Banci_Enum? banci, string workGroup,
int sort, string namePinyin, string memo, string lizhiYuanyin)
{
Id = id;
DepId = depId;
WorktypeId = worktypeId;
WorkStatus = workStatus;
Code = code;
Name = name;
Gangwei = gangwei;
Phone = phone;
IdCard = idCard;
BirthDate = birthDate;
Address = address;
Gender = gender;
Minzu = minzu;
HunyinStatus = hunyinStatus;
Huji = huji;
RuzhiDate = ruzhiDate;
LizhiDate = lizhiDate;
ZhuanzhengDate = zhuanzhengDate;
IsZhuanzheng = isZhuanzheng;
JinjiLianxiren = jinjiLianxiren;
JinjiLianxirenPhone = jinjiLianxirenPhone;
HetongType = hetongType;
HetongStartDate = hetongStartDate;
HetongEndDate = hetongEndDate;
TuixiuDate = tuixiuDate;
ShebaoDate = shebaoDate;
ShebaoDesc = shebaoDesc;
BankcardCode = bankcardCode;
BiyeYuanxiao = biyeYuanxiao;
Zhuanye = zhuanye;
Xueli = xueli;
Banci = banci;
WorkGroup = workGroup;
Sort = sort;
NamePinyin = namePinyin;
Memo = memo;
LizhiYuanyin = lizhiYuanyin;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>修改基本信息(部门/工种/班次通过调岗变更)</summary>
public void Update(string code, string name, string gangwei, string phone, string idCard, DateTime? birthDate,
string address, string gender, string minzu, string hunyinStatus, string huji, DateTime? ruzhiDate,
string jinjiLianxiren, string jinjiLianxirenPhone, string hetongType, DateTime? hetongStartDate,
DateTime? hetongEndDate, DateTime? tuixiuDate, DateTime? shebaoDate, string shebaoDesc, string bankcardCode,
string biyeYuanxiao, string zhuanye, string xueli, string namePinyin, string memo)
{
Code = code;
Name = name;
Gangwei = gangwei;
Phone = phone;
IdCard = idCard;
BirthDate = birthDate;
Address = address;
Gender = gender;
Minzu = minzu;
HunyinStatus = hunyinStatus;
Huji = huji;
RuzhiDate = ruzhiDate;
JinjiLianxiren = jinjiLianxiren;
JinjiLianxirenPhone = jinjiLianxirenPhone;
HetongType = hetongType;
HetongStartDate = hetongStartDate;
HetongEndDate = hetongEndDate;
TuixiuDate = tuixiuDate;
ShebaoDate = shebaoDate;
ShebaoDesc = shebaoDesc;
BankcardCode = bankcardCode;
BiyeYuanxiao = biyeYuanxiao;
Zhuanye = zhuanye;
Xueli = xueli;
NamePinyin = namePinyin;
Memo = memo;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>转正</summary>
public void Zhuanzheng(DateTime date)
{
IsZhuanzheng = true;
ZhuanzhengDate = date.Date;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>取消转正</summary>
public void ZhuanzhengCancel()
{
IsZhuanzheng = false;
ZhuanzhengDate = null;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>离职</summary>
public void Lizhi(DateTime date, string yuanyin)
{
WorkStatus = Employee_WorkStatus_Enum.离职;
LizhiDate = date.Date;
LizhiYuanyin = yuanyin ?? string.Empty;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>取消离职</summary>
public void LizhiCancel()
{
WorkStatus = Employee_WorkStatus_Enum.在职;
LizhiDate = null;
LizhiYuanyin = string.Empty;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>调岗</summary>
public void Tiaogang(long depId, long worktypeId, string gangwei, Prod_Banci_Enum? banci)
{
DepId = depId;
WorktypeId = worktypeId;
if (!string.IsNullOrWhiteSpace(gangwei)) Gangwei = gangwei;
if (banci.HasValue) Banci = banci;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>重新入职(离职人员按新部门/工种入职)</summary>
public void RenewRuzhi(DateTime date, long depId, long worktypeId, string gangwei, Prod_Banci_Enum? banci)
{
WorkStatus = Employee_WorkStatus_Enum.在职;
DepId = depId;
WorktypeId = worktypeId;
RuzhiDate = date.Date;
LizhiDate = null;
LizhiYuanyin = string.Empty;
if (!string.IsNullOrWhiteSpace(gangwei)) Gangwei = gangwei;
if (banci.HasValue) Banci = banci;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
/// <summary>修改生产信息(工作组 / 顺序号)</summary>
public void UpdateSc(string workGroup, int sort)
{
WorkGroup = workGroup ?? string.Empty;
Sort = sort;
AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
public void Deleted() => AddDomainEvent(new Employee_ChangedDomainEvent(Id));
}
@@ -0,0 +1,36 @@
using FreeSql.DataAnnotations;
using F9WebAiTest.Domain.Enums.SYS.Employees;
namespace F9WebAiTest.Domain.Entities.SYS.Employees;
/// <summary>职工变更记录(入职/调岗/转正/离职/再入职)</summary>
[Table(Name = "sys_employee_change_record")]
public class EmployeeChangeRecord : BusinessEntity
{
[Column(Name = "employee_id")] public long EmployeeId { get; private set; }
/// <summary>变更日期</summary>
[Column(Name = "date")] public DateTime Date { get; private set; }
/// <summary>变更类型</summary>
[Column(Name = "change_status")] public Employee_ChangeStatus_Enum ChangeStatus { get; private set; }
/// <summary>变更说明(部门、岗位等)</summary>
[Column(Name = "change_shuoming")] public string ChangeShuoming { get; private set; }
/// <summary>变更原因</summary>
[Column(Name = "change_yuanyin")] public string ChangeYuanyin { get; private set; }
public EmployeeChangeRecord() { }
public EmployeeChangeRecord(long id, long employeeId, DateTime date, Employee_ChangeStatus_Enum changeStatus,
string changeShuoming, string changeYuanyin)
{
Id = id;
EmployeeId = employeeId;
Date = date;
ChangeStatus = changeStatus;
ChangeShuoming = changeShuoming;
ChangeYuanyin = changeYuanyin;
}
}
@@ -0,0 +1,35 @@
using FreeSql.DataAnnotations;
namespace F9WebAiTest.Domain.Entities.SYS.Employees;
/// <summary>职工调岗记录(记录调岗前后的部门与工种)</summary>
[Table(Name = "sys_employee_tiaogang_record")]
public class EmployeeTiaogangRecord : BusinessEntity
{
[Column(Name = "employee_id")] public long EmployeeId { get; private set; }
[Column(Name = "old_depid")] public long OldDepId { get; private set; }
[Column(Name = "old_worktypeid")] public long OldWorktypeId { get; private set; }
[Column(Name = "new_depid")] public long NewDepId { get; private set; }
[Column(Name = "new_worktypeid")] public long NewWorktypeId { get; private set; }
/// <summary>调岗日期</summary>
[Column(Name = "tiaogang_date")] public DateTime TiaogangDate { get; private set; }
public EmployeeTiaogangRecord() { }
public EmployeeTiaogangRecord(long id, long employeeId, long oldDepId, long oldWorktypeId, long newDepId,
long newWorktypeId, DateTime tiaogangDate)
{
Id = id;
EmployeeId = employeeId;
OldDepId = oldDepId;
OldWorktypeId = oldWorktypeId;
NewDepId = newDepId;
NewWorktypeId = newWorktypeId;
TiaogangDate = tiaogangDate;
}
}
@@ -0,0 +1,25 @@
namespace F9WebAiTest.Domain.Enums.SYS.Employees;
/// <summary>职工在职状态(与 F9 的 Employee_WorkStatus_Enum 一致)</summary>
public enum Employee_WorkStatus_Enum
{
在职 = 0,
离职 = 1
}
/// <summary>生产班次(与 F9 的 Prod_Banci_Enum 一致)</summary>
public enum Prod_Banci_Enum
{
甲 = 1,
乙 = 2
}
/// <summary>职工变更记录类型(与 F9 的 Employee_ChangeStatus_Enum 一致)</summary>
public enum Employee_ChangeStatus_Enum
{
新入职 = 1,
调岗 = 2,
离职 = 3,
再入职 = 4,
转正 = 5
}
@@ -0,0 +1,5 @@
using F9WebAiTest.Domain.Entities;
namespace F9WebAiTest.Domain.Events.SYS.Employees;
public record Employee_ChangedDomainEvent(long employeeId) : DomainEvent;
@@ -0,0 +1,12 @@
using F9WebAiTest.Domain.Entities.SYS.Employees;
namespace F9WebAiTest.Domain.Interfaces.SYS.Employees;
/// <summary>职工写仓储(聚合根仓储)</summary>
public interface IEmployeeWriteRepository : IDInterface
{
Task<Employee> GetByIdAsync(long id, CancellationToken cancellationToken);
Task<bool> AddAsync(Employee entity);
Task<bool> UpdateAsync(Employee entity);
Task<bool> SoftDeleteAsync(long id);
}