33 changed files with 10924 additions and 2052 deletions
@ -1,582 +0,0 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace X1.Infrastructure.Migrations |
|||
{ |
|||
[DbContext(typeof(AppDbContext))] |
|||
[Migration("20250705165102_InitialCreate")] |
|||
partial class InitialCreate |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "8.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("角色ID,主键"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("角色描述"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("角色名称"); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化角色名称(大写)"); |
|||
|
|||
b.Property<DateTime>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Roles_Name"); |
|||
|
|||
b.HasIndex("NormalizedName") |
|||
.IsUnique() |
|||
.HasDatabaseName("RoleNameIndex"); |
|||
|
|||
b.ToTable("Roles", null, t => |
|||
{ |
|||
t.HasComment("角色表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("用户ID,主键"); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.HasColumnType("integer") |
|||
.HasComment("登录失败次数"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Email") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("电子邮箱"); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("邮箱是否已验证"); |
|||
|
|||
b.Property<bool>("IsActive") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(true) |
|||
.HasComment("用户状态(true: 启用, false: 禁用)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(false) |
|||
.HasComment("是否已删除"); |
|||
|
|||
b.Property<DateTime?>("LastLoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("最后登录时间"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用账户锁定"); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("账户锁定结束时间"); |
|||
|
|||
b.Property<DateTime?>("ModifiedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("修改时间"); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化电子邮箱(大写)"); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化账号(大写)"); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnType("text") |
|||
.HasComment("密码哈希值"); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.IsRequired() |
|||
.HasColumnType("text") |
|||
.HasComment("电话号码"); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("电话号码是否已验证"); |
|||
|
|||
b.Property<string>("RealName") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("用户名"); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.HasColumnType("text") |
|||
.HasComment("安全戳,用于并发控制"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用双因素认证"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("账号"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail") |
|||
.HasDatabaseName("EmailIndex"); |
|||
|
|||
b.HasIndex("NormalizedUserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("UserNameIndex"); |
|||
|
|||
b.HasIndex("PhoneNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_PhoneNumber"); |
|||
|
|||
b.HasIndex("UserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_UserName"); |
|||
|
|||
b.ToTable("Users", null, t => |
|||
{ |
|||
t.HasComment("用户表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("设备ID"); |
|||
|
|||
b.Property<int>("AgentPort") |
|||
.HasColumnType("integer") |
|||
.HasComment("Agent端口"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("CreatedBy") |
|||
.IsRequired() |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Description") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备描述"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(45) |
|||
.HasColumnType("character varying(45)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("设备名称"); |
|||
|
|||
b.Property<string>("ProtocolVersionId") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("协议版本ID"); |
|||
|
|||
b.Property<string>("SerialNumber") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("序列号"); |
|||
|
|||
b.Property<DateTime?>("UpdatedAt") |
|||
.IsRequired() |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.Property<string>("UpdatedBy") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("ProtocolVersionId") |
|||
.HasDatabaseName("IX_CellularDevices_ProtocolVersionId"); |
|||
|
|||
b.HasIndex("SerialNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_CellularDevices_SerialNumber"); |
|||
|
|||
b.ToTable("CellularDevices", null, t => |
|||
{ |
|||
t.HasComment("蜂窝设备表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Device.ProtocolVersion", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("版本ID"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("CreatedBy") |
|||
.IsRequired() |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("版本描述"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsEnabled") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsForceUpdate") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<string>("MinimumSupportedVersion") |
|||
.HasMaxLength(20) |
|||
.HasColumnType("character varying(20)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("ProtocolType") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime?>("ReleaseDate") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<DateTime?>("UpdatedAt") |
|||
.IsRequired() |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.Property<string>("UpdatedBy") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Version") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("版本号"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Version") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_ProtocolVersions_Version"); |
|||
|
|||
b.ToTable("ProtocolVersions", null, t => |
|||
{ |
|||
t.HasComment("协议版本表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("日志ID"); |
|||
|
|||
b.Property<string>("Browser") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("浏览器信息"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("FailureReason") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("失败原因"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("登录IP"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsSuccess") |
|||
.HasColumnType("boolean") |
|||
.HasComment("登录状态(成功/失败)"); |
|||
|
|||
b.Property<string>("Location") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("登录位置"); |
|||
|
|||
b.Property<string>("LoginSource") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("LoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("登录时间"); |
|||
|
|||
b.Property<string>("LoginType") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("OperatingSystem") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("操作系统信息"); |
|||
|
|||
b.Property<string>("SessionId") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)"); |
|||
|
|||
b.Property<DateTime?>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("UserAgent") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备信息"); |
|||
|
|||
b.Property<string>("UserId") |
|||
.IsRequired() |
|||
.HasMaxLength(450) |
|||
.HasColumnType("character varying(450)") |
|||
.HasComment("用户ID"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("IpAddress") |
|||
.HasDatabaseName("IX_LoginLogs_IpAddress"); |
|||
|
|||
b.HasIndex("LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_LoginTime"); |
|||
|
|||
b.HasIndex("UserId") |
|||
.HasDatabaseName("IX_LoginLogs_UserId"); |
|||
|
|||
b.HasIndex("UserId", "LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); |
|||
|
|||
b.ToTable("LoginLogs", null, t => |
|||
{ |
|||
t.HasComment("用户登录日志表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Code") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("Type") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Permissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("PermissionId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.HasKey("RoleId", "PermissionId"); |
|||
|
|||
b.HasIndex("PermissionId"); |
|||
|
|||
b.ToTable("RolePermissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.Property<string>("UserId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("UserRoles", null, t => |
|||
{ |
|||
t.HasComment("用户角色关系表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Device.ProtocolVersion", "ProtocolVersion") |
|||
.WithMany() |
|||
.HasForeignKey("ProtocolVersionId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("ProtocolVersion"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", null) |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") |
|||
.WithMany("RolePermissions") |
|||
.HasForeignKey("PermissionId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Permission"); |
|||
|
|||
b.Navigation("Role"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Role"); |
|||
|
|||
b.Navigation("User"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Navigation("RolePermissions"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
@ -1,582 +0,0 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace X1.Infrastructure.Migrations |
|||
{ |
|||
[DbContext(typeof(AppDbContext))] |
|||
[Migration("20250705173130_InitProtocolVersionAndDevice")] |
|||
partial class InitProtocolVersionAndDevice |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "8.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("角色ID,主键"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("角色描述"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("角色名称"); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化角色名称(大写)"); |
|||
|
|||
b.Property<DateTime>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Roles_Name"); |
|||
|
|||
b.HasIndex("NormalizedName") |
|||
.IsUnique() |
|||
.HasDatabaseName("RoleNameIndex"); |
|||
|
|||
b.ToTable("Roles", null, t => |
|||
{ |
|||
t.HasComment("角色表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("用户ID,主键"); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.HasColumnType("integer") |
|||
.HasComment("登录失败次数"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Email") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("电子邮箱"); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("邮箱是否已验证"); |
|||
|
|||
b.Property<bool>("IsActive") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(true) |
|||
.HasComment("用户状态(true: 启用, false: 禁用)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(false) |
|||
.HasComment("是否已删除"); |
|||
|
|||
b.Property<DateTime?>("LastLoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("最后登录时间"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用账户锁定"); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("账户锁定结束时间"); |
|||
|
|||
b.Property<DateTime?>("ModifiedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("修改时间"); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化电子邮箱(大写)"); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化账号(大写)"); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnType("text") |
|||
.HasComment("密码哈希值"); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.IsRequired() |
|||
.HasColumnType("text") |
|||
.HasComment("电话号码"); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("电话号码是否已验证"); |
|||
|
|||
b.Property<string>("RealName") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("用户名"); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.HasColumnType("text") |
|||
.HasComment("安全戳,用于并发控制"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用双因素认证"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("账号"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail") |
|||
.HasDatabaseName("EmailIndex"); |
|||
|
|||
b.HasIndex("NormalizedUserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("UserNameIndex"); |
|||
|
|||
b.HasIndex("PhoneNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_PhoneNumber"); |
|||
|
|||
b.HasIndex("UserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_UserName"); |
|||
|
|||
b.ToTable("Users", null, t => |
|||
{ |
|||
t.HasComment("用户表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("设备ID"); |
|||
|
|||
b.Property<int>("AgentPort") |
|||
.HasColumnType("integer") |
|||
.HasComment("Agent端口"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("CreatedBy") |
|||
.IsRequired() |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Description") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备描述"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(45) |
|||
.HasColumnType("character varying(45)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("设备名称"); |
|||
|
|||
b.Property<string>("ProtocolVersionId") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("协议版本ID"); |
|||
|
|||
b.Property<string>("SerialNumber") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("序列号"); |
|||
|
|||
b.Property<DateTime?>("UpdatedAt") |
|||
.IsRequired() |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.Property<string>("UpdatedBy") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("ProtocolVersionId") |
|||
.HasDatabaseName("IX_CellularDevices_ProtocolVersionId"); |
|||
|
|||
b.HasIndex("SerialNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_CellularDevices_SerialNumber"); |
|||
|
|||
b.ToTable("CellularDevices", null, t => |
|||
{ |
|||
t.HasComment("蜂窝设备表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Device.ProtocolVersion", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("版本ID"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("CreatedBy") |
|||
.IsRequired() |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("版本描述"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用"); |
|||
|
|||
b.Property<bool>("IsForceUpdate") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否强制更新"); |
|||
|
|||
b.Property<string>("MinimumSupportedVersion") |
|||
.HasMaxLength(20) |
|||
.HasColumnType("character varying(20)") |
|||
.HasComment("最低支持版本"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("版本名称"); |
|||
|
|||
b.Property<DateTime?>("ReleaseDate") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("发布日期"); |
|||
|
|||
b.Property<DateTime?>("UpdatedAt") |
|||
.IsRequired() |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.Property<string>("UpdatedBy") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Version") |
|||
.IsRequired() |
|||
.HasMaxLength(20) |
|||
.HasColumnType("character varying(20)") |
|||
.HasComment("版本号"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Version") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_ProtocolVersions_Version"); |
|||
|
|||
b.ToTable("ProtocolVersions", null, t => |
|||
{ |
|||
t.HasComment("协议版本表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("日志ID"); |
|||
|
|||
b.Property<string>("Browser") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("浏览器信息"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("FailureReason") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("失败原因"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("登录IP"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.HasColumnType("boolean"); |
|||
|
|||
b.Property<bool>("IsSuccess") |
|||
.HasColumnType("boolean") |
|||
.HasComment("登录状态(成功/失败)"); |
|||
|
|||
b.Property<string>("Location") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("登录位置"); |
|||
|
|||
b.Property<string>("LoginSource") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("LoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("登录时间"); |
|||
|
|||
b.Property<string>("LoginType") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("OperatingSystem") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("操作系统信息"); |
|||
|
|||
b.Property<string>("SessionId") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)"); |
|||
|
|||
b.Property<DateTime?>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("UserAgent") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备信息"); |
|||
|
|||
b.Property<string>("UserId") |
|||
.IsRequired() |
|||
.HasMaxLength(450) |
|||
.HasColumnType("character varying(450)") |
|||
.HasComment("用户ID"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("IpAddress") |
|||
.HasDatabaseName("IX_LoginLogs_IpAddress"); |
|||
|
|||
b.HasIndex("LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_LoginTime"); |
|||
|
|||
b.HasIndex("UserId") |
|||
.HasDatabaseName("IX_LoginLogs_UserId"); |
|||
|
|||
b.HasIndex("UserId", "LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); |
|||
|
|||
b.ToTable("LoginLogs", null, t => |
|||
{ |
|||
t.HasComment("用户登录日志表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Code") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("Type") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Permissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("PermissionId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.HasKey("RoleId", "PermissionId"); |
|||
|
|||
b.HasIndex("PermissionId"); |
|||
|
|||
b.ToTable("RolePermissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.Property<string>("UserId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("UserRoles", null, t => |
|||
{ |
|||
t.HasComment("用户角色关系表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Device.ProtocolVersion", "ProtocolVersion") |
|||
.WithMany() |
|||
.HasForeignKey("ProtocolVersionId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("ProtocolVersion"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", null) |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") |
|||
.WithMany("RolePermissions") |
|||
.HasForeignKey("PermissionId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Permission"); |
|||
|
|||
b.Navigation("Role"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Role"); |
|||
|
|||
b.Navigation("User"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Navigation("RolePermissions"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
@ -1,183 +0,0 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace X1.Infrastructure.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class InitProtocolVersionAndDevice : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropColumn( |
|||
name: "ProtocolType", |
|||
table: "ProtocolVersions"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Version", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(20)", |
|||
maxLength: 20, |
|||
nullable: false, |
|||
comment: "版本号", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(50)", |
|||
oldMaxLength: 50, |
|||
oldComment: "版本号"); |
|||
|
|||
migrationBuilder.AlterColumn<DateTime>( |
|||
name: "ReleaseDate", |
|||
table: "ProtocolVersions", |
|||
type: "timestamp with time zone", |
|||
nullable: true, |
|||
comment: "发布日期", |
|||
oldClrType: typeof(DateTime), |
|||
oldType: "timestamp with time zone", |
|||
oldNullable: true); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Name", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
comment: "版本名称", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(50)", |
|||
oldMaxLength: 50); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "MinimumSupportedVersion", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(20)", |
|||
maxLength: 20, |
|||
nullable: true, |
|||
comment: "最低支持版本", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(20)", |
|||
oldMaxLength: 20, |
|||
oldNullable: true); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsForceUpdate", |
|||
table: "ProtocolVersions", |
|||
type: "boolean", |
|||
nullable: false, |
|||
comment: "是否强制更新", |
|||
oldClrType: typeof(bool), |
|||
oldType: "boolean"); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsEnabled", |
|||
table: "ProtocolVersions", |
|||
type: "boolean", |
|||
nullable: false, |
|||
comment: "是否启用", |
|||
oldClrType: typeof(bool), |
|||
oldType: "boolean"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Description", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(500)", |
|||
maxLength: 500, |
|||
nullable: true, |
|||
comment: "版本描述", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(200)", |
|||
oldMaxLength: 200, |
|||
oldNullable: true, |
|||
oldComment: "版本描述"); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Version", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
comment: "版本号", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(20)", |
|||
oldMaxLength: 20, |
|||
oldComment: "版本号"); |
|||
|
|||
migrationBuilder.AlterColumn<DateTime>( |
|||
name: "ReleaseDate", |
|||
table: "ProtocolVersions", |
|||
type: "timestamp with time zone", |
|||
nullable: true, |
|||
oldClrType: typeof(DateTime), |
|||
oldType: "timestamp with time zone", |
|||
oldNullable: true, |
|||
oldComment: "发布日期"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Name", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(50)", |
|||
oldMaxLength: 50, |
|||
oldComment: "版本名称"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "MinimumSupportedVersion", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(20)", |
|||
maxLength: 20, |
|||
nullable: true, |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(20)", |
|||
oldMaxLength: 20, |
|||
oldNullable: true, |
|||
oldComment: "最低支持版本"); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsForceUpdate", |
|||
table: "ProtocolVersions", |
|||
type: "boolean", |
|||
nullable: false, |
|||
oldClrType: typeof(bool), |
|||
oldType: "boolean", |
|||
oldComment: "是否强制更新"); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsEnabled", |
|||
table: "ProtocolVersions", |
|||
type: "boolean", |
|||
nullable: false, |
|||
oldClrType: typeof(bool), |
|||
oldType: "boolean", |
|||
oldComment: "是否启用"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Description", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(200)", |
|||
maxLength: 200, |
|||
nullable: true, |
|||
comment: "版本描述", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(500)", |
|||
oldMaxLength: 500, |
|||
oldNullable: true, |
|||
oldComment: "版本描述"); |
|||
|
|||
migrationBuilder.AddColumn<string>( |
|||
name: "ProtocolType", |
|||
table: "ProtocolVersions", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
defaultValue: ""); |
|||
} |
|||
} |
|||
} |
@ -1,64 +0,0 @@ |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace X1.Infrastructure.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class UpdateProtocolVersionAndCellularDevice : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropColumn( |
|||
name: "IsForceUpdate", |
|||
table: "ProtocolVersions"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "IpAddress", |
|||
table: "CellularDevices", |
|||
type: "character varying(45)", |
|||
maxLength: 45, |
|||
nullable: false, |
|||
comment: "IP地址", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(45)", |
|||
oldMaxLength: 45); |
|||
|
|||
migrationBuilder.AddColumn<bool>( |
|||
name: "IsRunning", |
|||
table: "CellularDevices", |
|||
type: "boolean", |
|||
nullable: false, |
|||
defaultValue: false, |
|||
comment: "设备状态(启动/未启动)"); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropColumn( |
|||
name: "IsRunning", |
|||
table: "CellularDevices"); |
|||
|
|||
migrationBuilder.AddColumn<bool>( |
|||
name: "IsForceUpdate", |
|||
table: "ProtocolVersions", |
|||
type: "boolean", |
|||
nullable: false, |
|||
defaultValue: false, |
|||
comment: "是否强制更新"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "IpAddress", |
|||
table: "CellularDevices", |
|||
type: "character varying(45)", |
|||
maxLength: 45, |
|||
nullable: false, |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(45)", |
|||
oldMaxLength: 45, |
|||
oldComment: "IP地址"); |
|||
} |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,209 @@ |
|||
import React, { useState, useRef, useEffect } from 'react'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { MagnifyingGlassIcon, Cross2Icon } from '@radix-ui/react-icons'; |
|||
|
|||
interface ConfigContentEditorProps { |
|||
value: string; |
|||
onChange: (value: string) => void; |
|||
placeholder?: string; |
|||
rows?: number; |
|||
disabled?: boolean; |
|||
required?: boolean; |
|||
label?: string; |
|||
className?: string; |
|||
} |
|||
|
|||
export default function ConfigContentEditor({ |
|||
value, |
|||
onChange, |
|||
placeholder = "请输入配置内容", |
|||
rows = 8, |
|||
disabled = false, |
|||
required = false, |
|||
label = "配置内容", |
|||
className = "" |
|||
}: ConfigContentEditorProps) { |
|||
const [searchTerm, setSearchTerm] = useState(''); |
|||
const [highlightedContent, setHighlightedContent] = useState(''); |
|||
const [isSearchVisible, setIsSearchVisible] = useState(false); |
|||
const textareaRef = useRef<HTMLTextAreaElement>(null); |
|||
|
|||
// 高亮搜索内容
|
|||
useEffect(() => { |
|||
if (!searchTerm.trim()) { |
|||
setHighlightedContent(value); |
|||
return; |
|||
} |
|||
|
|||
const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); |
|||
const highlighted = value.replace(regex, '<mark class="bg-yellow-200 text-black">$1</mark>'); |
|||
setHighlightedContent(highlighted); |
|||
}, [value, searchTerm]); |
|||
|
|||
// 搜索下一个匹配项
|
|||
const findNext = () => { |
|||
if (!searchTerm.trim() || !textareaRef.current) return; |
|||
|
|||
const textarea = textareaRef.current; |
|||
const text = textarea.value; |
|||
const currentPos = textarea.selectionStart; |
|||
const searchIndex = text.toLowerCase().indexOf(searchTerm.toLowerCase(), currentPos); |
|||
|
|||
if (searchIndex !== -1) { |
|||
textarea.setSelectionRange(searchIndex, searchIndex + searchTerm.length); |
|||
textarea.focus(); |
|||
} else { |
|||
// 如果从当前位置没找到,从头开始搜索
|
|||
const firstIndex = text.toLowerCase().indexOf(searchTerm.toLowerCase()); |
|||
if (firstIndex !== -1) { |
|||
textarea.setSelectionRange(firstIndex, firstIndex + searchTerm.length); |
|||
textarea.focus(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// 搜索上一个匹配项
|
|||
const findPrevious = () => { |
|||
if (!searchTerm.trim() || !textareaRef.current) return; |
|||
|
|||
const textarea = textareaRef.current; |
|||
const text = textarea.value; |
|||
const currentPos = textarea.selectionStart; |
|||
|
|||
// 从当前位置向前搜索
|
|||
const beforeText = text.substring(0, currentPos); |
|||
const lastIndex = beforeText.toLowerCase().lastIndexOf(searchTerm.toLowerCase()); |
|||
|
|||
if (lastIndex !== -1) { |
|||
textarea.setSelectionRange(lastIndex, lastIndex + searchTerm.length); |
|||
textarea.focus(); |
|||
} else { |
|||
// 如果从当前位置没找到,从末尾开始搜索
|
|||
const lastIndexFromEnd = text.toLowerCase().lastIndexOf(searchTerm.toLowerCase()); |
|||
if (lastIndexFromEnd !== -1) { |
|||
textarea.setSelectionRange(lastIndexFromEnd, lastIndexFromEnd + searchTerm.length); |
|||
textarea.focus(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// 计算匹配数量
|
|||
const getMatchCount = () => { |
|||
if (!searchTerm.trim()) return 0; |
|||
const regex = new RegExp(searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); |
|||
const matches = value.match(regex); |
|||
return matches ? matches.length : 0; |
|||
}; |
|||
|
|||
const matchCount = getMatchCount(); |
|||
|
|||
return ( |
|||
<div className={`flex flex-col h-full ${className}`}> |
|||
<div className="flex items-center justify-between mb-2"> |
|||
<Label htmlFor="configContent">{label}</Label> |
|||
<div className="flex items-center gap-1"> |
|||
{/* 搜索按钮 */} |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={() => setIsSearchVisible(!isSearchVisible)} |
|||
disabled={disabled} |
|||
title="搜索内容" |
|||
> |
|||
<MagnifyingGlassIcon className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* 搜索栏 */} |
|||
{isSearchVisible && ( |
|||
<div className="flex items-center gap-2 p-2 bg-gray-50 rounded border mb-2"> |
|||
<div className="relative flex-1"> |
|||
<MagnifyingGlassIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" /> |
|||
<Input |
|||
placeholder="搜索内容..." |
|||
value={searchTerm} |
|||
onChange={(e) => setSearchTerm(e.target.value)} |
|||
className="pl-10" |
|||
autoFocus |
|||
/> |
|||
</div> |
|||
{searchTerm && ( |
|||
<span className="text-sm text-gray-500"> |
|||
{matchCount} 个匹配 |
|||
</span> |
|||
)} |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={findPrevious} |
|||
disabled={!searchTerm.trim()} |
|||
title="上一个" |
|||
> |
|||
↑ |
|||
</Button> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={findNext} |
|||
disabled={!searchTerm.trim()} |
|||
title="下一个" |
|||
> |
|||
↓ |
|||
</Button> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={() => { |
|||
setSearchTerm(''); |
|||
setIsSearchVisible(false); |
|||
}} |
|||
title="关闭搜索" |
|||
> |
|||
<Cross2Icon className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
)} |
|||
|
|||
{/* 编辑器区域 - 占满剩余空间 */} |
|||
<div className="relative flex-1 min-h-0"> |
|||
<Textarea |
|||
ref={textareaRef} |
|||
value={value} |
|||
onChange={(e) => onChange(e.target.value)} |
|||
placeholder={placeholder} |
|||
required={required} |
|||
disabled={disabled} |
|||
className="font-mono text-sm h-full resize-none" |
|||
style={{ height: '100%' }} |
|||
/> |
|||
|
|||
{/* 高亮显示层(仅用于显示,不可编辑) */} |
|||
{searchTerm.trim() && ( |
|||
<div |
|||
className="absolute inset-0 pointer-events-none font-mono text-sm p-3 overflow-auto whitespace-pre-wrap" |
|||
style={{ |
|||
backgroundColor: 'transparent', |
|||
color: 'transparent', |
|||
caretColor: 'transparent', |
|||
border: '1px solid transparent', |
|||
borderRadius: 'inherit' |
|||
}} |
|||
dangerouslySetInnerHTML={{ __html: highlightedContent }} |
|||
/> |
|||
)} |
|||
</div> |
|||
|
|||
<p className="text-xs text-gray-500 mt-2"> |
|||
支持任意格式的配置内容,支持搜索高亮 |
|||
</p> |
|||
</div> |
|||
); |
|||
} |
@ -0,0 +1,78 @@ |
|||
import * as React from "react" |
|||
import { Dialog, DialogContent } from "@/components/ui/dialog" |
|||
import { cn } from "@/lib/utils" |
|||
|
|||
interface DrawerProps { |
|||
open: boolean |
|||
onOpenChange: (open: boolean) => void |
|||
children: React.ReactNode |
|||
className?: string |
|||
} |
|||
|
|||
export function Drawer({ open, onOpenChange, children, className }: DrawerProps) { |
|||
return ( |
|||
<Dialog open={open} onOpenChange={onOpenChange}> |
|||
<DialogContent |
|||
className={cn( |
|||
"fixed right-0 top-0 h-full w-[600px] max-w-[90vw] border-l bg-background p-0 shadow-lg", |
|||
"data-[state=open]:animate-in data-[state=closed]:animate-out", |
|||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right", |
|||
"duration-300", |
|||
"inset-auto left-auto", |
|||
className |
|||
)} |
|||
style={{ |
|||
position: 'fixed', |
|||
right: 0, |
|||
top: 0, |
|||
height: '100vh', |
|||
width: '600px', |
|||
maxWidth: '90vw', |
|||
transform: open ? 'translateX(0)' : 'translateX(100%)', |
|||
transition: 'transform 0.3s ease-in-out', |
|||
}} |
|||
> |
|||
{children} |
|||
</DialogContent> |
|||
</Dialog> |
|||
) |
|||
} |
|||
|
|||
interface DrawerHeaderProps { |
|||
children: React.ReactNode |
|||
className?: string |
|||
} |
|||
|
|||
export function DrawerHeader({ children, className }: DrawerHeaderProps) { |
|||
return ( |
|||
<div className={cn("flex h-16 items-center border-b px-6", className)}> |
|||
{children} |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
interface DrawerContentProps { |
|||
children: React.ReactNode |
|||
className?: string |
|||
} |
|||
|
|||
export function DrawerContent({ children, className }: DrawerContentProps) { |
|||
return ( |
|||
<div className={cn("flex-1 p-6 overflow-y-auto", className)}> |
|||
{children} |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
interface DrawerFooterProps { |
|||
children: React.ReactNode |
|||
className?: string |
|||
} |
|||
|
|||
export function DrawerFooter({ children, className }: DrawerFooterProps) { |
|||
return ( |
|||
<div className={cn("flex items-center justify-end gap-2 border-t p-6", className)}> |
|||
{children} |
|||
</div> |
|||
) |
|||
} |
@ -0,0 +1,165 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { CoreNetworkConfig, CreateCoreNetworkConfigRequest, UpdateCoreNetworkConfigRequest } from '@/services/coreNetworkConfigService'; |
|||
import { Drawer, DrawerHeader, DrawerContent, DrawerFooter } from '@/components/ui/drawer'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import ConfigContentEditor from '@/components/ui/ConfigContentEditor'; |
|||
import { X } from 'lucide-react'; |
|||
|
|||
interface CoreNetworkConfigDrawerProps { |
|||
open: boolean; |
|||
onOpenChange: (open: boolean) => void; |
|||
onSubmit: (data: CreateCoreNetworkConfigRequest | UpdateCoreNetworkConfigRequest) => void; |
|||
initialData?: Partial<CoreNetworkConfig>; |
|||
isEdit?: boolean; |
|||
isSubmitting?: boolean; |
|||
} |
|||
|
|||
export default function CoreNetworkConfigDrawer({ |
|||
open, |
|||
onOpenChange, |
|||
onSubmit, |
|||
initialData, |
|||
isEdit = false, |
|||
isSubmitting = false |
|||
}: CoreNetworkConfigDrawerProps) { |
|||
const [formData, setFormData] = useState<CreateCoreNetworkConfigRequest>({ |
|||
name: '', |
|||
configContent: '', |
|||
description: '', |
|||
isDisabled: false |
|||
}); |
|||
|
|||
// 当初始数据变化时更新表单数据
|
|||
useEffect(() => { |
|||
if (initialData) { |
|||
setFormData({ |
|||
name: initialData.name || '', |
|||
configContent: initialData.configContent || '', |
|||
description: initialData.description || '', |
|||
isDisabled: initialData.isDisabled ?? false |
|||
}); |
|||
} else { |
|||
setFormData({ |
|||
name: '', |
|||
configContent: '', |
|||
description: '', |
|||
isDisabled: false |
|||
}); |
|||
} |
|||
}, [initialData]); |
|||
|
|||
const handleInputChange = (field: keyof CreateCoreNetworkConfigRequest, value: string | boolean) => { |
|||
setFormData(prev => ({ |
|||
...prev, |
|||
[field]: value |
|||
})); |
|||
}; |
|||
|
|||
const handleSubmit = (e: React.FormEvent) => { |
|||
e.preventDefault(); |
|||
if (isSubmitting) return; |
|||
|
|||
if (isEdit && initialData?.coreNetworkConfigId) { |
|||
const updateData: UpdateCoreNetworkConfigRequest = { |
|||
coreNetworkConfigId: initialData.coreNetworkConfigId, |
|||
name: formData.name, |
|||
configContent: formData.configContent, |
|||
description: formData.description, |
|||
isDisabled: formData.isDisabled |
|||
}; |
|||
onSubmit(updateData); |
|||
} else { |
|||
onSubmit(formData); |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<Drawer open={open} onOpenChange={onOpenChange}> |
|||
<div className="flex flex-col h-full"> |
|||
<DrawerHeader> |
|||
<div className="flex items-center justify-between w-full"> |
|||
<h2 className="text-lg font-semibold"> |
|||
{isEdit ? '编辑核心网络配置' : '创建核心网络配置'} |
|||
</h2> |
|||
<Button |
|||
variant="ghost" |
|||
size="sm" |
|||
onClick={() => onOpenChange(false)} |
|||
className="h-8 w-8 p-0" |
|||
> |
|||
<X className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
</DrawerHeader> |
|||
|
|||
<form onSubmit={handleSubmit} className="flex flex-col flex-1"> |
|||
<DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto"> |
|||
{/* 配置名称 */} |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="name" className="text-sm font-medium"> |
|||
配置名称 <span className="text-red-500">*</span> |
|||
</Label> |
|||
<Input |
|||
id="name" |
|||
value={formData.name} |
|||
onChange={(e) => handleInputChange('name', e.target.value)} |
|||
placeholder="请输入配置名称" |
|||
disabled={isSubmitting} |
|||
required |
|||
/> |
|||
</div> |
|||
|
|||
{/* 描述 */} |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="description" className="text-sm font-medium"> |
|||
描述 |
|||
</Label> |
|||
<Textarea |
|||
id="description" |
|||
value={formData.description} |
|||
onChange={(e) => handleInputChange('description', e.target.value)} |
|||
placeholder="请输入配置描述(可选)" |
|||
disabled={isSubmitting} |
|||
rows={3} |
|||
/> |
|||
</div> |
|||
|
|||
{/* 配置内容 - 占满剩余空间 */} |
|||
<div className="flex flex-col flex-1 min-h-0"> |
|||
<ConfigContentEditor |
|||
value={formData.configContent} |
|||
onChange={(value) => handleInputChange('configContent', value)} |
|||
placeholder="请输入配置内容" |
|||
required |
|||
disabled={isSubmitting} |
|||
label="配置内容 *" |
|||
className="flex-1" |
|||
/> |
|||
</div> |
|||
</DrawerContent> |
|||
|
|||
<DrawerFooter> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
onClick={() => onOpenChange(false)} |
|||
disabled={isSubmitting} |
|||
> |
|||
取消 |
|||
</Button> |
|||
<Button |
|||
type="submit" |
|||
disabled={isSubmitting} |
|||
className="bg-primary text-primary-foreground hover:bg-primary/90" |
|||
> |
|||
{isSubmitting ? '保存中...' : (isEdit ? '更新' : '创建')} |
|||
</Button> |
|||
</DrawerFooter> |
|||
</form> |
|||
</div> |
|||
</Drawer> |
|||
); |
|||
} |
@ -1,90 +0,0 @@ |
|||
import React from 'react'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import { Checkbox } from '@/components/ui/checkbox'; |
|||
import { CreateCoreNetworkConfigRequest, UpdateCoreNetworkConfigRequest, CoreNetworkConfig } from '@/services/coreNetworkConfigService'; |
|||
|
|||
interface CoreNetworkConfigFormProps { |
|||
onSubmit: (data: CreateCoreNetworkConfigRequest | UpdateCoreNetworkConfigRequest) => void; |
|||
initialData?: Partial<CoreNetworkConfig>; |
|||
isEdit?: boolean; |
|||
isSubmitting?: boolean; |
|||
} |
|||
|
|||
export default function CoreNetworkConfigForm({ onSubmit, initialData, isEdit = false, isSubmitting = false }: CoreNetworkConfigFormProps) { |
|||
const [formData, setFormData] = React.useState<CreateCoreNetworkConfigRequest>({ |
|||
name: initialData?.name || '', |
|||
configContent: initialData?.configContent || '', |
|||
description: initialData?.description || '', |
|||
isDisabled: initialData?.isDisabled ?? false |
|||
}); |
|||
|
|||
const handleSubmit = (e: React.FormEvent) => { |
|||
e.preventDefault(); |
|||
if (isSubmitting) return; // 防止重复提交
|
|||
onSubmit(formData); |
|||
}; |
|||
|
|||
return ( |
|||
<form onSubmit={handleSubmit} className="space-y-4"> |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="name">配置名称</Label> |
|||
<Input |
|||
id="name" |
|||
value={formData.name} |
|||
onChange={e => setFormData({ ...formData, name: e.target.value })} |
|||
placeholder="请输入配置名称" |
|||
required |
|||
disabled={isSubmitting} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="space-y-2"> |
|||
<Label htmlFor="configContent">配置内容 (JSON格式)</Label> |
|||
<Textarea |
|||
id="configContent" |
|||
value={formData.configContent} |
|||
onChange={e => setFormData({ ...formData, configContent: e.target.value })} |
|||
placeholder="请输入JSON格式的配置内容" |
|||
rows={8} |
|||
required |
|||
disabled={isSubmitting} |
|||
className="font-mono text-sm" |
|||
/> |
|||
<p className="text-xs text-gray-500"> |
|||
请输入有效的JSON格式配置内容 |
|||
</p> |
|||
</div> |
|||
|
|||
<div className="space-y-2"> |
|||
<Label htmlFor="description">描述</Label> |
|||
<Textarea |
|||
id="description" |
|||
value={formData.description} |
|||
onChange={e => setFormData({ ...formData, description: e.target.value })} |
|||
placeholder="请输入配置描述" |
|||
rows={3} |
|||
disabled={isSubmitting} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="flex items-center space-x-2"> |
|||
<Checkbox |
|||
id="isDisabled" |
|||
checked={formData.isDisabled} |
|||
onCheckedChange={(checked) => |
|||
setFormData({ ...formData, isDisabled: checked as boolean }) |
|||
} |
|||
disabled={isSubmitting} |
|||
/> |
|||
<Label htmlFor="isDisabled">禁用此配置</Label> |
|||
</div> |
|||
|
|||
<Button type="submit" className="w-full" disabled={isSubmitting}> |
|||
{isSubmitting ? '提交中...' : (isEdit ? '更新核心网络配置' : '创建核心网络配置')} |
|||
</Button> |
|||
</form> |
|||
); |
|||
} |
@ -0,0 +1,165 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { IMSConfiguration, CreateIMSConfigurationRequest, UpdateIMSConfigurationRequest } from '@/services/imsConfigurationService'; |
|||
import { Drawer, DrawerHeader, DrawerContent, DrawerFooter } from '@/components/ui/drawer'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import ConfigContentEditor from '@/components/ui/ConfigContentEditor'; |
|||
import { X } from 'lucide-react'; |
|||
|
|||
interface IMSConfigurationDrawerProps { |
|||
open: boolean; |
|||
onOpenChange: (open: boolean) => void; |
|||
onSubmit: (data: CreateIMSConfigurationRequest | UpdateIMSConfigurationRequest) => void; |
|||
initialData?: Partial<IMSConfiguration>; |
|||
isEdit?: boolean; |
|||
isSubmitting?: boolean; |
|||
} |
|||
|
|||
export default function IMSConfigurationDrawer({ |
|||
open, |
|||
onOpenChange, |
|||
onSubmit, |
|||
initialData, |
|||
isEdit = false, |
|||
isSubmitting = false |
|||
}: IMSConfigurationDrawerProps) { |
|||
const [formData, setFormData] = useState<CreateIMSConfigurationRequest>({ |
|||
name: '', |
|||
configContent: '', |
|||
description: '', |
|||
isDisabled: false |
|||
}); |
|||
|
|||
// 当初始数据变化时更新表单数据
|
|||
useEffect(() => { |
|||
if (initialData) { |
|||
setFormData({ |
|||
name: initialData.name || '', |
|||
configContent: initialData.configContent || '', |
|||
description: initialData.description || '', |
|||
isDisabled: initialData.isDisabled ?? false |
|||
}); |
|||
} else { |
|||
setFormData({ |
|||
name: '', |
|||
configContent: '', |
|||
description: '', |
|||
isDisabled: false |
|||
}); |
|||
} |
|||
}, [initialData]); |
|||
|
|||
const handleInputChange = (field: keyof CreateIMSConfigurationRequest, value: string | boolean) => { |
|||
setFormData(prev => ({ |
|||
...prev, |
|||
[field]: value |
|||
})); |
|||
}; |
|||
|
|||
const handleSubmit = (e: React.FormEvent) => { |
|||
e.preventDefault(); |
|||
if (isSubmitting) return; |
|||
|
|||
if (isEdit && initialData?.imS_ConfigurationId) { |
|||
const updateData: UpdateIMSConfigurationRequest = { |
|||
imS_ConfigurationId: initialData.imS_ConfigurationId, |
|||
name: formData.name, |
|||
configContent: formData.configContent, |
|||
description: formData.description, |
|||
isDisabled: formData.isDisabled |
|||
}; |
|||
onSubmit(updateData); |
|||
} else { |
|||
onSubmit(formData); |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<Drawer open={open} onOpenChange={onOpenChange}> |
|||
<div className="flex flex-col h-full"> |
|||
<DrawerHeader> |
|||
<div className="flex items-center justify-between w-full"> |
|||
<h2 className="text-lg font-semibold"> |
|||
{isEdit ? '编辑IMS配置' : '创建IMS配置'} |
|||
</h2> |
|||
<Button |
|||
variant="ghost" |
|||
size="sm" |
|||
onClick={() => onOpenChange(false)} |
|||
className="h-8 w-8 p-0" |
|||
> |
|||
<X className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
</DrawerHeader> |
|||
|
|||
<form onSubmit={handleSubmit} className="flex flex-col flex-1"> |
|||
<DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto"> |
|||
{/* 配置名称 */} |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="name" className="text-sm font-medium"> |
|||
配置名称 <span className="text-red-500">*</span> |
|||
</Label> |
|||
<Input |
|||
id="name" |
|||
value={formData.name} |
|||
onChange={(e) => handleInputChange('name', e.target.value)} |
|||
placeholder="请输入配置名称" |
|||
disabled={isSubmitting} |
|||
required |
|||
/> |
|||
</div> |
|||
|
|||
{/* 描述 */} |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="description" className="text-sm font-medium"> |
|||
描述 |
|||
</Label> |
|||
<Textarea |
|||
id="description" |
|||
value={formData.description} |
|||
onChange={(e) => handleInputChange('description', e.target.value)} |
|||
placeholder="请输入配置描述(可选)" |
|||
disabled={isSubmitting} |
|||
rows={3} |
|||
/> |
|||
</div> |
|||
|
|||
{/* 配置内容 - 占满剩余空间 */} |
|||
<div className="flex flex-col flex-1 min-h-0"> |
|||
<ConfigContentEditor |
|||
value={formData.configContent} |
|||
onChange={(value) => handleInputChange('configContent', value)} |
|||
placeholder="请输入配置内容" |
|||
required |
|||
disabled={isSubmitting} |
|||
label="配置内容 *" |
|||
className="flex-1" |
|||
/> |
|||
</div> |
|||
</DrawerContent> |
|||
|
|||
<DrawerFooter> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
onClick={() => onOpenChange(false)} |
|||
disabled={isSubmitting} |
|||
> |
|||
取消 |
|||
</Button> |
|||
<Button |
|||
type="submit" |
|||
disabled={isSubmitting} |
|||
className="bg-primary text-primary-foreground hover:bg-primary/90" |
|||
> |
|||
{isSubmitting ? '保存中...' : (isEdit ? '更新' : '创建')} |
|||
</Button> |
|||
</DrawerFooter> |
|||
</form> |
|||
</div> |
|||
</Drawer> |
|||
); |
|||
} |
@ -1,90 +0,0 @@ |
|||
import React from 'react'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import { Checkbox } from '@/components/ui/checkbox'; |
|||
import { CreateIMSConfigurationRequest, UpdateIMSConfigurationRequest, IMSConfiguration } from '@/services/imsConfigurationService'; |
|||
|
|||
interface IMSConfigurationFormProps { |
|||
onSubmit: (data: CreateIMSConfigurationRequest | UpdateIMSConfigurationRequest) => void; |
|||
initialData?: Partial<IMSConfiguration>; |
|||
isEdit?: boolean; |
|||
isSubmitting?: boolean; |
|||
} |
|||
|
|||
export default function IMSConfigurationForm({ onSubmit, initialData, isEdit = false, isSubmitting = false }: IMSConfigurationFormProps) { |
|||
const [formData, setFormData] = React.useState<CreateIMSConfigurationRequest>({ |
|||
name: initialData?.name || '', |
|||
configContent: initialData?.configContent || '', |
|||
description: initialData?.description || '', |
|||
isDisabled: initialData?.isDisabled ?? false |
|||
}); |
|||
|
|||
const handleSubmit = (e: React.FormEvent) => { |
|||
e.preventDefault(); |
|||
if (isSubmitting) return; // 防止重复提交
|
|||
onSubmit(formData); |
|||
}; |
|||
|
|||
return ( |
|||
<form onSubmit={handleSubmit} className="space-y-4"> |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="name">配置名称</Label> |
|||
<Input |
|||
id="name" |
|||
value={formData.name} |
|||
onChange={e => setFormData({ ...formData, name: e.target.value })} |
|||
placeholder="请输入配置名称" |
|||
required |
|||
disabled={isSubmitting} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="space-y-2"> |
|||
<Label htmlFor="configContent">配置内容 (JSON格式)</Label> |
|||
<Textarea |
|||
id="configContent" |
|||
value={formData.configContent} |
|||
onChange={e => setFormData({ ...formData, configContent: e.target.value })} |
|||
placeholder="请输入JSON格式的配置内容" |
|||
rows={8} |
|||
required |
|||
disabled={isSubmitting} |
|||
className="font-mono text-sm" |
|||
/> |
|||
<p className="text-xs text-gray-500"> |
|||
请输入有效的JSON格式配置内容 |
|||
</p> |
|||
</div> |
|||
|
|||
<div className="space-y-2"> |
|||
<Label htmlFor="description">描述</Label> |
|||
<Textarea |
|||
id="description" |
|||
value={formData.description} |
|||
onChange={e => setFormData({ ...formData, description: e.target.value })} |
|||
placeholder="请输入配置描述" |
|||
rows={3} |
|||
disabled={isSubmitting} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="flex items-center space-x-2"> |
|||
<Checkbox |
|||
id="isDisabled" |
|||
checked={formData.isDisabled} |
|||
onCheckedChange={(checked) => |
|||
setFormData({ ...formData, isDisabled: checked as boolean }) |
|||
} |
|||
disabled={isSubmitting} |
|||
/> |
|||
<Label htmlFor="isDisabled">禁用此配置</Label> |
|||
</div> |
|||
|
|||
<Button type="submit" className="w-full" disabled={isSubmitting}> |
|||
{isSubmitting ? '提交中...' : (isEdit ? '更新IMS配置' : '创建IMS配置')} |
|||
</Button> |
|||
</form> |
|||
); |
|||
} |
@ -0,0 +1,170 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { RANConfiguration, CreateRANConfigurationRequest, UpdateRANConfigurationRequest } from '@/services/ranConfigurationService'; |
|||
import { Drawer, DrawerHeader, DrawerContent, DrawerFooter } from '@/components/ui/drawer'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
|
|||
import ConfigContentEditor from '@/components/ui/ConfigContentEditor'; |
|||
import { X } from 'lucide-react'; |
|||
|
|||
interface RANConfigurationDrawerProps { |
|||
open: boolean; |
|||
onOpenChange: (open: boolean) => void; |
|||
onSubmit: (data: CreateRANConfigurationRequest | UpdateRANConfigurationRequest) => Promise<void>; |
|||
initialData?: RANConfiguration; |
|||
isEdit?: boolean; |
|||
isSubmitting?: boolean; |
|||
} |
|||
|
|||
export default function RANConfigurationDrawer({ |
|||
open, |
|||
onOpenChange, |
|||
onSubmit, |
|||
initialData, |
|||
isEdit = false, |
|||
isSubmitting = false |
|||
}: RANConfigurationDrawerProps) { |
|||
const [formData, setFormData] = useState<CreateRANConfigurationRequest>({ |
|||
name: '', |
|||
configContent: '', |
|||
description: '', |
|||
isDisabled: false |
|||
}); |
|||
|
|||
// 当抽屉打开时,初始化表单数据
|
|||
useEffect(() => { |
|||
if (open) { |
|||
if (initialData && isEdit) { |
|||
setFormData({ |
|||
name: initialData.name || '', |
|||
configContent: initialData.configContent || '', |
|||
description: initialData.description || '', |
|||
isDisabled: initialData.isDisabled || false |
|||
}); |
|||
} else { |
|||
// 重置表单
|
|||
setFormData({ |
|||
name: '', |
|||
configContent: '', |
|||
description: '', |
|||
isDisabled: false |
|||
}); |
|||
} |
|||
} |
|||
}, [open, initialData, isEdit]); |
|||
|
|||
const handleSubmit = async (e: React.FormEvent) => { |
|||
e.preventDefault(); |
|||
if (isSubmitting) return; |
|||
|
|||
// 基本验证
|
|||
if (!formData.name.trim()) { |
|||
alert('请输入配置名称'); |
|||
return; |
|||
} |
|||
if (!formData.configContent.trim()) { |
|||
alert('请输入配置内容'); |
|||
return; |
|||
} |
|||
|
|||
await onSubmit(formData); |
|||
}; |
|||
|
|||
const handleInputChange = (field: keyof CreateRANConfigurationRequest, value: string | boolean) => { |
|||
setFormData(prev => ({ |
|||
...prev, |
|||
[field]: value |
|||
})); |
|||
}; |
|||
|
|||
return ( |
|||
<Drawer open={open} onOpenChange={onOpenChange}> |
|||
<div className="flex flex-col h-full"> |
|||
<DrawerHeader> |
|||
<div className="flex items-center justify-between w-full"> |
|||
<h2 className="text-lg font-semibold"> |
|||
{isEdit ? '编辑RAN配置' : '创建RAN配置'} |
|||
</h2> |
|||
<Button |
|||
variant="ghost" |
|||
size="sm" |
|||
onClick={() => onOpenChange(false)} |
|||
className="h-8 w-8 p-0" |
|||
> |
|||
<X className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
</DrawerHeader> |
|||
|
|||
<form onSubmit={handleSubmit} className="flex flex-col flex-1"> |
|||
<DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto"> |
|||
{/* 配置名称 */} |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="name" className="text-sm font-medium"> |
|||
配置名称 <span className="text-red-500">*</span> |
|||
</Label> |
|||
<Input |
|||
id="name" |
|||
value={formData.name} |
|||
onChange={(e) => handleInputChange('name', e.target.value)} |
|||
placeholder="请输入配置名称" |
|||
disabled={isSubmitting} |
|||
required |
|||
/> |
|||
</div> |
|||
|
|||
{/* 描述 */} |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="description" className="text-sm font-medium"> |
|||
描述 |
|||
</Label> |
|||
<Textarea |
|||
id="description" |
|||
value={formData.description} |
|||
onChange={(e) => handleInputChange('description', e.target.value)} |
|||
placeholder="请输入配置描述(可选)" |
|||
disabled={isSubmitting} |
|||
rows={3} |
|||
/> |
|||
</div> |
|||
|
|||
|
|||
|
|||
{/* 配置内容 - 占满剩余空间 */} |
|||
<div className="flex flex-col flex-1 min-h-0"> |
|||
<ConfigContentEditor |
|||
value={formData.configContent} |
|||
onChange={(value) => handleInputChange('configContent', value)} |
|||
placeholder="请输入配置内容" |
|||
required |
|||
disabled={isSubmitting} |
|||
label="配置内容 *" |
|||
className="flex-1" |
|||
/> |
|||
</div> |
|||
</DrawerContent> |
|||
|
|||
<DrawerFooter> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
onClick={() => onOpenChange(false)} |
|||
disabled={isSubmitting} |
|||
> |
|||
取消 |
|||
</Button> |
|||
<Button |
|||
type="submit" |
|||
disabled={isSubmitting} |
|||
className="bg-primary text-primary-foreground hover:bg-primary/90" |
|||
> |
|||
{isSubmitting ? '保存中...' : (isEdit ? '更新' : '创建')} |
|||
</Button> |
|||
</DrawerFooter> |
|||
</form> |
|||
</div> |
|||
</Drawer> |
|||
); |
|||
} |
@ -1,90 +0,0 @@ |
|||
import React from 'react'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import { Checkbox } from '@/components/ui/checkbox'; |
|||
import { CreateRANConfigurationRequest, UpdateRANConfigurationRequest, RANConfiguration } from '@/services/ranConfigurationService'; |
|||
|
|||
interface RANConfigurationFormProps { |
|||
onSubmit: (data: CreateRANConfigurationRequest | UpdateRANConfigurationRequest) => void; |
|||
initialData?: Partial<RANConfiguration>; |
|||
isEdit?: boolean; |
|||
isSubmitting?: boolean; |
|||
} |
|||
|
|||
export default function RANConfigurationForm({ onSubmit, initialData, isEdit = false, isSubmitting = false }: RANConfigurationFormProps) { |
|||
const [formData, setFormData] = React.useState<CreateRANConfigurationRequest>({ |
|||
name: initialData?.name || '', |
|||
configContent: initialData?.configContent || '', |
|||
description: initialData?.description || '', |
|||
isDisabled: initialData?.isDisabled ?? false |
|||
}); |
|||
|
|||
const handleSubmit = (e: React.FormEvent) => { |
|||
e.preventDefault(); |
|||
if (isSubmitting) return; // 防止重复提交
|
|||
onSubmit(formData); |
|||
}; |
|||
|
|||
return ( |
|||
<form onSubmit={handleSubmit} className="space-y-4"> |
|||
<div className="space-y-2"> |
|||
<Label htmlFor="name">配置名称</Label> |
|||
<Input |
|||
id="name" |
|||
value={formData.name} |
|||
onChange={e => setFormData({ ...formData, name: e.target.value })} |
|||
placeholder="请输入配置名称" |
|||
required |
|||
disabled={isSubmitting} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="space-y-2"> |
|||
<Label htmlFor="configContent">配置内容 (JSON格式)</Label> |
|||
<Textarea |
|||
id="configContent" |
|||
value={formData.configContent} |
|||
onChange={e => setFormData({ ...formData, configContent: e.target.value })} |
|||
placeholder="请输入JSON格式的配置内容" |
|||
rows={8} |
|||
required |
|||
disabled={isSubmitting} |
|||
className="font-mono text-sm" |
|||
/> |
|||
<p className="text-xs text-gray-500"> |
|||
请输入有效的JSON格式配置内容 |
|||
</p> |
|||
</div> |
|||
|
|||
<div className="space-y-2"> |
|||
<Label htmlFor="description">描述</Label> |
|||
<Textarea |
|||
id="description" |
|||
value={formData.description} |
|||
onChange={e => setFormData({ ...formData, description: e.target.value })} |
|||
placeholder="请输入配置描述" |
|||
rows={3} |
|||
disabled={isSubmitting} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="flex items-center space-x-2"> |
|||
<Checkbox |
|||
id="isDisabled" |
|||
checked={formData.isDisabled} |
|||
onCheckedChange={(checked) => |
|||
setFormData({ ...formData, isDisabled: checked as boolean }) |
|||
} |
|||
disabled={isSubmitting} |
|||
/> |
|||
<Label htmlFor="isDisabled">禁用此配置</Label> |
|||
</div> |
|||
|
|||
<Button type="submit" className="w-full" disabled={isSubmitting}> |
|||
{isSubmitting ? '提交中...' : (isEdit ? '更新RAN配置' : '创建RAN配置')} |
|||
</Button> |
|||
</form> |
|||
); |
|||
} |
Loading…
Reference in new issue