同学期、同课程、同教学班的实验项目合并展示,组内连续排列。

管理列表改为数据库筛选、按教学任务服务端分页(10–100 条/页)。
增加开课学院、课程名称/编码、教学班、任课教师、教学任务号筛选。
增加当前页草稿项目的批量选中、批量发布、批量删除;后端会再次校验权限、状态和发布条件。
确认框改为固定居中白色底板与阴影,不再出现只有文字按钮、没有底色的情况。
This commit is contained in:
2026-08-09 19:44:45 +08:00 Unverified
parent 25a1480739
commit 97aa23bc64
12 changed files with 793 additions and 78 deletions
@@ -678,7 +678,16 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
entity.Property(x => x.Name).HasMaxLength(120);
entity.Property(x => x.Description).HasMaxLength(1000);
entity.Property(x => x.Requirements).HasMaxLength(1000);
entity.HasIndex(x => new { x.TeachingTaskId, x.Code }).IsUnique();
// 集中安排会为同一教学任务的每一条实验课表记录生成项目;
// 自行安排仍由控制器保持“教学任务 + 编码”唯一。
entity.HasIndex(x => new
{
x.TeachingTaskId,
x.Code,
x.ScheduleEntryId,
x.ScheduleWeek
})
.IsUnique();
entity.HasIndex(x => new { x.Status, x.StartDate, x.EndDate });
entity.HasOne(x => x.TeachingTask).WithMany()
.HasForeignKey(x => x.TeachingTaskId)
@@ -0,0 +1,19 @@
// <auto-generated />
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql;
[DbContext(typeof(AppDbContext))]
[Migration("20260809112000_AllowAllScheduledExperimentLessons")]
partial class AllowAllScheduledExperimentLessons
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("ProductVersion", "10.0.10");
}
}
@@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql;
public partial class AllowAllScheduledExperimentLessons : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// MySQL 可能将旧唯一索引用于外键支撑。先提供同列的普通索引,
// 再替换业务唯一索引,避免线上迁移因外键依赖而中断。
migrationBuilder.CreateIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code_FkSupport",
table: "ExperimentProjects",
columns: new[] { "TeachingTaskId", "Code" });
migrationBuilder.DropIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code",
table: "ExperimentProjects");
migrationBuilder.CreateIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code_ScheduleEntryId",
table: "ExperimentProjects",
columns: new[] { "TeachingTaskId", "Code", "ScheduleEntryId" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code_ScheduleEntryId",
table: "ExperimentProjects");
migrationBuilder.CreateIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code",
table: "ExperimentProjects",
columns: new[] { "TeachingTaskId", "Code" },
unique: true);
migrationBuilder.DropIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code_FkSupport",
table: "ExperimentProjects");
}
}
@@ -0,0 +1,19 @@
// <auto-generated />
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql;
[DbContext(typeof(AppDbContext))]
[Migration("20260809114000_SplitCentralizedExperimentProjectsByWeek")]
partial class SplitCentralizedExperimentProjectsByWeek
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("ProductVersion", "10.0.10");
}
}
@@ -0,0 +1,78 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql;
public partial class SplitCentralizedExperimentProjectsByWeek : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
ExecuteWhenMissing(
migrationBuilder,
"COLUMNS",
"COLUMN_NAME = 'ScheduleWeek'",
"ALTER TABLE `ExperimentProjects` ADD COLUMN `ScheduleWeek` int NULL");
ExecuteWhenPresent(
migrationBuilder,
"STATISTICS",
"INDEX_NAME = 'IX_ExperimentProjects_TeachingTaskId_Code_ScheduleEntryId'",
"DROP INDEX `IX_ExperimentProjects_TeachingTaskId_Code_ScheduleEntryId` ON `ExperimentProjects`");
ExecuteWhenMissing(
migrationBuilder,
"STATISTICS",
"INDEX_NAME = 'IX_ExpProj_Task_Code_Entry_Week'",
"CREATE UNIQUE INDEX `IX_ExpProj_Task_Code_Entry_Week` ON `ExperimentProjects` (`TeachingTaskId`, `Code`, `ScheduleEntryId`, `ScheduleWeek`)");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_ExpProj_Task_Code_Entry_Week",
table: "ExperimentProjects");
migrationBuilder.CreateIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code_ScheduleEntryId",
table: "ExperimentProjects",
columns: new[] { "TeachingTaskId", "Code", "ScheduleEntryId" },
unique: true);
migrationBuilder.DropColumn(
name: "ScheduleWeek",
table: "ExperimentProjects");
}
private static void ExecuteWhenMissing(
MigrationBuilder migrationBuilder,
string informationSchemaTable,
string condition,
string command)
{
ExecuteConditionally(migrationBuilder, informationSchemaTable, condition, command, "= 0");
}
private static void ExecuteWhenPresent(
MigrationBuilder migrationBuilder,
string informationSchemaTable,
string condition,
string command)
{
ExecuteConditionally(migrationBuilder, informationSchemaTable, condition, command, "> 0");
}
private static void ExecuteConditionally(
MigrationBuilder migrationBuilder,
string informationSchemaTable,
string condition,
string command,
string comparison)
{
migrationBuilder.Sql($"SET @jiaowu_exists = (SELECT COUNT(*) FROM `information_schema`.`{informationSchemaTable}` WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'ExperimentProjects' AND {condition})");
migrationBuilder.Sql($"SET @jiaowu_sql = IF(@jiaowu_exists {comparison}, '{command}', 'SELECT 1')");
migrationBuilder.Sql("PREPARE jiaowu_migration_statement FROM @jiaowu_sql");
migrationBuilder.Sql("EXECUTE jiaowu_migration_statement");
migrationBuilder.Sql("DEALLOCATE PREPARE jiaowu_migration_statement");
}
}
@@ -2432,6 +2432,9 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.Property<Guid?>("ScheduleEntryId")
.HasColumnType("char(36)");
b.Property<int?>("ScheduleWeek")
.HasColumnType("int");
b.Property<DateTime>("StartDate")
.HasColumnType("date");
@@ -2448,7 +2451,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.HasIndex("ScheduleEntryId");
b.HasIndex("TeachingTaskId", "Code")
b.HasIndex("TeachingTaskId", "Code", "ScheduleEntryId", "ScheduleWeek")
.IsUnique();
b.HasIndex("Status", "StartDate", "EndDate");