生产模块拆分为前缫/后缫;修复菜单卡顿与重复请求;支持 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:
@@ -60,12 +60,13 @@ public static class DbSeeder
|
||||
// 6.6 F9 风格 RBAC 菜单(用户/角色/菜单/部门管理,写入新 sys_rbac_menus 表)
|
||||
SeedSysRbacMenus(fsql, snowflake);
|
||||
|
||||
// 6.65 基础数据(工种 + 注册表配置 + 原料庄口 + 物资 + 往来单位,F9 导出数据幂等导入)
|
||||
// 6.65 基础数据(工种 + 注册表配置 + 原料庄口 + 物资 + 往来单位 + 职工,F9 导出数据幂等导入)
|
||||
WorkTypeSeedData.Seed(fsql, snowflake);
|
||||
RegistrySeedData.Seed(fsql);
|
||||
YuanliaoZhuangkouSeedData.Seed(fsql);
|
||||
WuziSeedData.Seed(fsql);
|
||||
GongyingshangSeedData.Seed(fsql);
|
||||
EmployeeSeedData.Seed(fsql);
|
||||
|
||||
// 6.66 基础数据菜单(工种管理、账期管理、注册表、原料庄口、物资目录、往来单位)
|
||||
SeedWorkTypeMenus(fsql, snowflake);
|
||||
@@ -75,6 +76,12 @@ public static class DbSeeder
|
||||
SeedWuziMenus(fsql, snowflake);
|
||||
SeedGongyingshangMenus(fsql, snowflake);
|
||||
|
||||
// 6.67 职工管理(一级菜单,与"基础数据"平级)
|
||||
SeedEmployeeTopMenu(fsql, snowflake);
|
||||
|
||||
// 6.68 生产模块(按工序拆分:生产-前缫 / 生产-后缫)
|
||||
SeedProdMenus(fsql, snowflake);
|
||||
|
||||
// 6.7 为菜单创建权限单元(Permission),并修正旧菜单权限关联(统一权限模型)
|
||||
FixMenuPermissions(fsql, snowflake);
|
||||
|
||||
@@ -175,7 +182,7 @@ public static class DbSeeder
|
||||
{
|
||||
Code = "TeyeFound", Name = "发现特野", Description = "纤度检验发现特野时自动推送提醒",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 1,
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/xdjy"
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/houxian/xdjy"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
@@ -186,7 +193,7 @@ public static class DbSeeder
|
||||
{
|
||||
Code = "TdcFound", Name = "发现特大糙", Description = "黑板检验发现特大糙时自动推送提醒",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 2,
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/hbjy"
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/houxian/hbjy"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
@@ -572,6 +579,203 @@ public static class DbSeeder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 职工管理菜单:独立的一级菜单(原作为"基础数据"的子菜单)。
|
||||
/// 会清理历史遗留的 /base/employee 子菜单,并规范一级菜单排序,避免同一页面出现两个入口。
|
||||
/// </summary>
|
||||
private static void SeedEmployeeTopMenu(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
try
|
||||
{
|
||||
const string menuPath = "/employee";
|
||||
const string menuIcon = "Avatar";
|
||||
|
||||
// 1) 清理历史:曾是"基础数据"下的子菜单
|
||||
var legacy = fsql.Select<SysMenu>().Where(m => m.Path == "/base/employee").First();
|
||||
if (legacy != null)
|
||||
{
|
||||
fsql.Ado.ExecuteNonQuery($"DELETE FROM sys_rbac_role_permissions WHERE permission_id = {legacy.Id}");
|
||||
fsql.Ado.ExecuteNonQuery($"DELETE FROM sys_rbac_menus WHERE id = {legacy.Id}");
|
||||
Console.WriteLine("[DbSeeder] 已清理历史子菜单 /base/employee");
|
||||
}
|
||||
|
||||
// 2) 创建/校正一级菜单
|
||||
var menu = fsql.Select<SysMenu>().Where(m => m.Path == menuPath).First();
|
||||
if (menu == null)
|
||||
{
|
||||
menu = new SysMenu(snowflake.CreateId(), "职工管理", menuIcon, MenuType_Enum.页面, null, null, 0, 4, 0);
|
||||
menu.SetWebInfo(menuPath, "base/employee/index", "base:employee:list");
|
||||
fsql.Insert(menu).ExecuteAffrows();
|
||||
}
|
||||
else if (!menu.IsActive || menu.IconSvg != menuIcon || menu.ParentId != 0)
|
||||
{
|
||||
fsql.Update<SysMenu>()
|
||||
.Set(x => x.IconSvg, menuIcon)
|
||||
.Set(x => x.IsActive, true)
|
||||
.Set(x => x.ParentId, 0)
|
||||
.Set(x => x.Level, 1)
|
||||
.Where(x => x.Id == menu.Id)
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 3) 规范一级菜单顺序:考勤管理 / 基础数据 / 职工管理 / 生产管理 / 消息中心 / 系统管理
|
||||
foreach (var (path, sort) in new (string Path, int Sort)[]
|
||||
{
|
||||
("/attendance", 2), ("/base", 3), ("/employee", 4),
|
||||
("/prod", 5), ("/notification", 6), ("/sys", 7)
|
||||
})
|
||||
{
|
||||
fsql.Update<SysMenu>()
|
||||
.Set(x => x.SortOrder, sort)
|
||||
.Where(x => x.ParentId == 0 && x.Path == path && x.SortOrder != sort)
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 4) admin 角色授权(后续 FixMenuPermissions 会把 permission_id 修正为权限单元 Id)
|
||||
var adminRole = fsql.Select<SysRole>().Where(r => r.Code == "admin").First();
|
||||
if (adminRole != null)
|
||||
{
|
||||
var any = fsql.Select<SysRolePermission>()
|
||||
.Where(x => x.RoleId == adminRole.Id && x.PermissionId == menu.Id)
|
||||
.First();
|
||||
if (any == null)
|
||||
{
|
||||
fsql.Insert(new SysRolePermission(snowflake.CreateId(), adminRole.Id, menu.Id)).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("[DbSeeder] 职工管理一级菜单已就绪");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] 职工管理菜单种子跳过:{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生产模块菜单:按缫丝工序拆分为「生产-前缫」(落丝记录)与「生产-后缫」(纤度检验、黑板检验)两个一级菜单。
|
||||
/// 会清理旧的「生产管理」(/prod) 目录及其子菜单;页面组件与权限标识保持不变。
|
||||
/// </summary>
|
||||
private static void SeedProdMenus(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1) 清理旧「生产管理」结构(精确匹配历史路径)
|
||||
foreach (var legacyPath in new[] { "/prod", "/prod/lsjl", "/prod/xdjy", "/prod/hbjy" })
|
||||
{
|
||||
var legacy = fsql.Select<SysMenu>().Where(m => m.Path == legacyPath).First();
|
||||
if (legacy == null) continue;
|
||||
fsql.Ado.ExecuteNonQuery($"DELETE FROM sys_rbac_role_permissions WHERE permission_id = {legacy.Id}");
|
||||
fsql.Ado.ExecuteNonQuery($"DELETE FROM sys_rbac_menus WHERE id = {legacy.Id}");
|
||||
Console.WriteLine($"[DbSeeder] 已清理旧生产菜单 {legacyPath}");
|
||||
}
|
||||
|
||||
// 2) 两个一级目录 + 各自页面菜单
|
||||
EnsureTopMenuWithChildren(fsql, snowflake, "/prod/qianxian", "生产-前缫", "Cpu", 5,
|
||||
new (string Name, string Path, string Component, string Permission, string Icon, int Sort)[]
|
||||
{
|
||||
("落丝记录", "/prod/qianxian/lsjl", "prod/lsjl/index", "prod:lsjl:list", "Document", 1)
|
||||
});
|
||||
|
||||
EnsureTopMenuWithChildren(fsql, snowflake, "/prod/houxian", "生产-后缫", "DataLine", 6,
|
||||
new (string Name, string Path, string Component, string Permission, string Icon, int Sort)[]
|
||||
{
|
||||
("纤度检验", "/prod/houxian/xdjy", "prod/xdjy/index", "prod:xdjy:list", "EditPen", 1),
|
||||
("黑板检验", "/prod/houxian/hbjy", "prod/hbjy/index", "prod:hbjy:list", "Notebook", 2)
|
||||
});
|
||||
|
||||
// 3) 规范一级菜单顺序
|
||||
foreach (var (path, sort) in new (string Path, int Sort)[]
|
||||
{
|
||||
("/attendance", 2), ("/base", 3), ("/employee", 4),
|
||||
("/prod/qianxian", 5), ("/prod/houxian", 6), ("/notification", 7), ("/sys", 8)
|
||||
})
|
||||
{
|
||||
fsql.Update<SysMenu>()
|
||||
.Set(x => x.SortOrder, sort)
|
||||
.Where(x => x.ParentId == 0 && x.Path == path && x.SortOrder != sort)
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 4) 消息跳转路径同步到新路由
|
||||
fsql.Ado.ExecuteNonQuery("UPDATE NotificationTypes SET RouteTemplate = '/prod/houxian/xdjy' WHERE Code = 'TeyeFound'");
|
||||
fsql.Ado.ExecuteNonQuery("UPDATE NotificationTypes SET RouteTemplate = '/prod/houxian/hbjy' WHERE Code = 'TdcFound'");
|
||||
|
||||
Console.WriteLine("[DbSeeder] 生产模块菜单已就绪(生产-前缫 / 生产-后缫)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] 生产菜单种子跳过:{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保一个一级目录菜单及其下属页面菜单存在(含父级、图标、启用状态校正),并给 admin 角色授权。
|
||||
/// 注意:图标名必须是 Element Plus 图标组件名。
|
||||
/// </summary>
|
||||
private static void EnsureTopMenuWithChildren(IFreeSql fsql, ISnowflakeIdGenerator snowflake,
|
||||
string dirPath, string dirName, string dirIcon, int dirSort,
|
||||
(string Name, string Path, string Component, string Permission, string Icon, int Sort)[] children)
|
||||
{
|
||||
var dir = fsql.Select<SysMenu>().Where(m => m.Path == dirPath).First();
|
||||
if (dir == null)
|
||||
{
|
||||
dir = new SysMenu(snowflake.CreateId(), dirName, dirIcon, MenuType_Enum.分组, null, null, 0, dirSort, 0);
|
||||
dir.SetWebInfo(dirPath, "", "");
|
||||
fsql.Insert(dir).ExecuteAffrows();
|
||||
}
|
||||
else if (!dir.IsActive || dir.IconSvg != dirIcon || dir.Name != dirName || dir.ParentId != 0)
|
||||
{
|
||||
fsql.Update<SysMenu>()
|
||||
.Set(x => x.Name, dirName)
|
||||
.Set(x => x.IconSvg, dirIcon)
|
||||
.Set(x => x.IsActive, true)
|
||||
.Set(x => x.ParentId, 0)
|
||||
.Set(x => x.Level, 1)
|
||||
.Where(x => x.Id == dir.Id)
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
|
||||
var adminRoleId = fsql.Select<SysRole>().Where(r => r.Code == "admin").First()?.Id ?? 0;
|
||||
EnsureMenuPermission(fsql, snowflake, adminRoleId, dir.Id);
|
||||
|
||||
foreach (var (name, path, component, permission, icon, sort) in children)
|
||||
{
|
||||
var child = fsql.Select<SysMenu>().Where(m => m.Path == path).First();
|
||||
if (child == null)
|
||||
{
|
||||
child = new SysMenu(snowflake.CreateId(), name, icon, MenuType_Enum.页面, null, null, dir.Id, sort, 1);
|
||||
child.SetWebInfo(path, component, permission);
|
||||
fsql.Insert(child).ExecuteAffrows();
|
||||
}
|
||||
else if (!child.IsActive || child.IconSvg != icon || child.ParentId != dir.Id)
|
||||
{
|
||||
fsql.Update<SysMenu>()
|
||||
.Set(x => x.IconSvg, icon)
|
||||
.Set(x => x.IsActive, true)
|
||||
.Set(x => x.ParentId, dir.Id)
|
||||
.Set(x => x.Level, 2)
|
||||
.Where(x => x.Id == child.Id)
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
|
||||
EnsureMenuPermission(fsql, snowflake, adminRoleId, child.Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>确保指定菜单已授权给角色(后续 FixMenuPermissions 会把 permission_id 修正为权限单元 Id)</summary>
|
||||
private static void EnsureMenuPermission(IFreeSql fsql, ISnowflakeIdGenerator snowflake, long roleId, long menuId)
|
||||
{
|
||||
if (roleId <= 0 || menuId <= 0) return;
|
||||
var any = fsql.Select<SysRolePermission>()
|
||||
.Where(x => x.RoleId == roleId && x.PermissionId == menuId)
|
||||
.First();
|
||||
if (any == null)
|
||||
{
|
||||
fsql.Insert(new SysRolePermission(snowflake.CreateId(), roleId, menuId)).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保"基础数据"目录下的菜单存在(含启用状态与图标校正),并给 admin 角色授权。
|
||||
/// 注意:图标名必须是 Element Plus 图标组件名,前端按图标名渲染菜单图标。
|
||||
|
||||
Reference in New Issue
Block a user