新建时可选“自动生成顺序号”:例如 数据库实验 01、02…
或选“逐行粘贴名称”:每行一个名称,按实际生成顺序对应项目。 草稿项目支持勾选后“批量改名”,可直接粘贴多行名称。 服务端会校验名称数量、长度和草稿状态,避免错位或修改已发布项目。
This commit is contained in:
@@ -574,6 +574,27 @@ public sealed class ExperimentsController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var names = NormalizeBatchProjectNames(request.Names);
|
||||||
|
if (request.GenerateSequentialNames)
|
||||||
|
{
|
||||||
|
if (names is not null)
|
||||||
|
return ValidationProblem("自动生成顺序号时无需填写逐行项目名称。");
|
||||||
|
var width = Math.Max(2, projects.Count.ToString().Length);
|
||||||
|
if (request.Name.Trim().Length + width + 1 > 120)
|
||||||
|
return ValidationProblem("项目名称前缀过长,无法追加顺序号。");
|
||||||
|
for (var index = 0; index < projects.Count; index++)
|
||||||
|
projects[index].Name = $"{request.Name.Trim()} {(index + 1).ToString($"D{width}")}";
|
||||||
|
}
|
||||||
|
else if (names is not null)
|
||||||
|
{
|
||||||
|
if (names.Any(name => string.IsNullOrWhiteSpace(name) || name.Length > 120))
|
||||||
|
return ValidationProblem("每个实验项目名称均不能为空,且不能超过 120 个字符。");
|
||||||
|
if (names.Count != projects.Count)
|
||||||
|
return ValidationProblem($"已填写 {names.Count} 个项目名称,但本次将生成 {projects.Count} 个实验项目;请确保每行对应一个项目。");
|
||||||
|
for (var index = 0; index < projects.Count; index++)
|
||||||
|
projects[index].Name = names[index];
|
||||||
|
}
|
||||||
|
|
||||||
db.ExperimentProjects.AddRange(projects);
|
db.ExperimentProjects.AddRange(projects);
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
await db.SaveChangesAsync(cancellationToken);
|
||||||
return Created(string.Empty, new
|
return Created(string.Empty, new
|
||||||
@@ -645,6 +666,34 @@ public sealed class ExperimentsController(
|
|||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPut("batch/names")]
|
||||||
|
[Authorize(Roles = Managers)]
|
||||||
|
public async Task<ActionResult> UpdateProjectNames(
|
||||||
|
ExperimentProjectBatchNamesRequest request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var ids = ValidateBulkProjectIds(request.ProjectIds);
|
||||||
|
if (ids is null) return ValidationProblem("请选择 1 至 100 个实验项目。");
|
||||||
|
|
||||||
|
var names = NormalizeBatchProjectNames(request.Names);
|
||||||
|
if (names is null || names.Count != ids.Count || names.Any(name => string.IsNullOrWhiteSpace(name) || name.Length > 120))
|
||||||
|
return ValidationProblem("请为每个已选实验项目填写一个名称,每行一个且数量必须一致。");
|
||||||
|
|
||||||
|
var projects = await ScopedProjects()
|
||||||
|
.Where(x => ids.Contains(x.Id))
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
if (projects.Count != ids.Count) return NotFound();
|
||||||
|
if (projects.Any(x => x.Status != ExperimentProjectStatus.Draft))
|
||||||
|
return ConflictProblem("批量修改名称只能包含草稿实验项目。");
|
||||||
|
|
||||||
|
var projectsById = projects.ToDictionary(x => x.Id);
|
||||||
|
for (var index = 0; index < ids.Count; index++)
|
||||||
|
projectsById[ids[index]].Name = names[index];
|
||||||
|
|
||||||
|
await db.SaveChangesAsync(cancellationToken);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
[HttpDelete("batch")]
|
[HttpDelete("batch")]
|
||||||
[Authorize(Roles = Managers)]
|
[Authorize(Roles = Managers)]
|
||||||
public async Task<ActionResult> DeleteProjects(
|
public async Task<ActionResult> DeleteProjects(
|
||||||
@@ -1494,6 +1543,12 @@ public sealed class ExperimentsController(
|
|||||||
private static string? Normalize(string? value) =>
|
private static string? Normalize(string? value) =>
|
||||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||||
|
|
||||||
|
private static List<string>? NormalizeBatchProjectNames(IReadOnlyList<string>? names)
|
||||||
|
{
|
||||||
|
if (names is null || names.Count == 0) return null;
|
||||||
|
return names.Select(name => name.Trim()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private ActionResult ConflictProblem(string detail) =>
|
private ActionResult ConflictProblem(string detail) =>
|
||||||
Conflict(new ProblemDetails
|
Conflict(new ProblemDetails
|
||||||
{
|
{
|
||||||
@@ -1522,7 +1577,9 @@ public sealed record ExperimentProjectBatchRequest(
|
|||||||
[MaxLength(1000)] string? Description,
|
[MaxLength(1000)] string? Description,
|
||||||
[MaxLength(1000)] string? Requirements,
|
[MaxLength(1000)] string? Requirements,
|
||||||
DateOnly StartDate,
|
DateOnly StartDate,
|
||||||
DateOnly EndDate)
|
DateOnly EndDate,
|
||||||
|
IReadOnlyList<string>? Names = null,
|
||||||
|
bool GenerateSequentialNames = false)
|
||||||
{
|
{
|
||||||
public ExperimentProjectRequest ForTeachingTask(Guid teachingTaskId) =>
|
public ExperimentProjectRequest ForTeachingTask(Guid teachingTaskId) =>
|
||||||
new(
|
new(
|
||||||
@@ -1539,6 +1596,10 @@ public sealed record ExperimentProjectBatchRequest(
|
|||||||
public sealed record ExperimentProjectBulkRequest(
|
public sealed record ExperimentProjectBulkRequest(
|
||||||
[Required] IReadOnlyList<Guid> ProjectIds);
|
[Required] IReadOnlyList<Guid> ProjectIds);
|
||||||
|
|
||||||
|
public sealed record ExperimentProjectBatchNamesRequest(
|
||||||
|
[Required] IReadOnlyList<Guid> ProjectIds,
|
||||||
|
[Required] IReadOnlyList<string> Names);
|
||||||
|
|
||||||
public sealed record ExperimentSessionRequest(
|
public sealed record ExperimentSessionRequest(
|
||||||
Guid ClassroomId,
|
Guid ClassroomId,
|
||||||
DateOnly SessionDate,
|
DateOnly SessionDate,
|
||||||
|
|||||||
@@ -37,6 +37,59 @@ public sealed class ExperimentsControllerTests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BatchProjects_AssignsPastedNamesAndSequentialNamesInGenerationOrder()
|
||||||
|
{
|
||||||
|
await using var fixture = await ExperimentFixture.CreateAsync();
|
||||||
|
var controller = fixture.Controller(fixture.ManagerScope);
|
||||||
|
|
||||||
|
var pasted = fixture.BatchProjectRequest(ExperimentArrangementMode.SelfScheduled) with
|
||||||
|
{
|
||||||
|
Names = ["实验一", "实验二"]
|
||||||
|
};
|
||||||
|
Assert.IsType<CreatedResult>(await controller.CreateProjects(pasted, CancellationToken.None));
|
||||||
|
Assert.Equal(["实验一", "实验二"], await fixture.Db.ExperimentProjects
|
||||||
|
.OrderBy(x => x.TeachingTask!.TaskNumber)
|
||||||
|
.Select(x => x.Name)
|
||||||
|
.ToListAsync());
|
||||||
|
|
||||||
|
fixture.Db.ExperimentProjects.RemoveRange(fixture.Db.ExperimentProjects);
|
||||||
|
await fixture.Db.SaveChangesAsync();
|
||||||
|
var sequential = fixture.BatchProjectRequest(ExperimentArrangementMode.SelfScheduled) with
|
||||||
|
{
|
||||||
|
Name = "数据库实验",
|
||||||
|
GenerateSequentialNames = true
|
||||||
|
};
|
||||||
|
Assert.IsType<CreatedResult>(await controller.CreateProjects(sequential, CancellationToken.None));
|
||||||
|
Assert.Equal(["数据库实验 01", "数据库实验 02"], await fixture.Db.ExperimentProjects
|
||||||
|
.OrderBy(x => x.TeachingTask!.TaskNumber)
|
||||||
|
.Select(x => x.Name)
|
||||||
|
.ToListAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BatchProjectNames_UpdatesOnlySelectedDraftProjectsInRequestOrder()
|
||||||
|
{
|
||||||
|
await using var fixture = await ExperimentFixture.CreateAsync();
|
||||||
|
var controller = fixture.Controller(fixture.ManagerScope);
|
||||||
|
await controller.CreateProjects(
|
||||||
|
fixture.BatchProjectRequest(ExperimentArrangementMode.SelfScheduled),
|
||||||
|
CancellationToken.None);
|
||||||
|
var ids = await fixture.Db.ExperimentProjects
|
||||||
|
.OrderByDescending(x => x.TeachingTask!.TaskNumber)
|
||||||
|
.Select(x => x.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var result = await controller.UpdateProjectNames(
|
||||||
|
new ExperimentProjectBatchNamesRequest(ids, ["第二个", "第一个"]),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<NoContentResult>(result);
|
||||||
|
var projectsById = await fixture.Db.ExperimentProjects.ToDictionaryAsync(x => x.Id, x => x.Name);
|
||||||
|
Assert.Equal("第二个", projectsById[ids[0]]);
|
||||||
|
Assert.Equal("第一个", projectsById[ids[1]]);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CentralizedBatchProjects_CreatesOneProjectForEveryScheduledExperimentLesson()
|
public async Task CentralizedBatchProjects_CreatesOneProjectForEveryScheduledExperimentLesson()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,12 +42,18 @@ const projectForm = reactive({
|
|||||||
scheduleEntryId: '',
|
scheduleEntryId: '',
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
projectNames: '',
|
||||||
|
nameMode: 'Sequence',
|
||||||
arrangementMode: 'Centralized',
|
arrangementMode: 'Centralized',
|
||||||
description: '',
|
description: '',
|
||||||
requirements: '',
|
requirements: '',
|
||||||
dates: [] as string[],
|
dates: [] as string[],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const batchRenameDialog = ref(false)
|
||||||
|
const batchRenameProjectIds = ref<string[]>([])
|
||||||
|
const batchRenameNames = ref('')
|
||||||
|
|
||||||
const sessionDialog = ref(false)
|
const sessionDialog = ref(false)
|
||||||
const selectedProject = ref<any>(null)
|
const selectedProject = ref<any>(null)
|
||||||
const sessionForm = reactive({
|
const sessionForm = reactive({
|
||||||
@@ -119,6 +125,9 @@ const draftSelectedProjectIds = computed(() =>
|
|||||||
selectedProjectIds.value.filter((id) =>
|
selectedProjectIds.value.filter((id) =>
|
||||||
projects.value.some((project) => project.id === id && project.status === 'Draft')),
|
projects.value.some((project) => project.id === id && project.status === 'Draft')),
|
||||||
)
|
)
|
||||||
|
const batchRenameProjects = computed(() =>
|
||||||
|
projects.value.filter((project) => batchRenameProjectIds.value.includes(project.id)),
|
||||||
|
)
|
||||||
const allPageDraftSelected = computed(() => {
|
const allPageDraftSelected = computed(() => {
|
||||||
const ids = projects.value.filter((project) => project.status === 'Draft').map((project) => project.id)
|
const ids = projects.value.filter((project) => project.status === 'Draft').map((project) => project.id)
|
||||||
return ids.length > 0 && ids.every((id) => selectedProjectIds.value.includes(id))
|
return ids.length > 0 && ids.every((id) => selectedProjectIds.value.includes(id))
|
||||||
@@ -154,6 +163,8 @@ function resetProjectForm() {
|
|||||||
scheduleEntryId: '',
|
scheduleEntryId: '',
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
projectNames: '',
|
||||||
|
nameMode: 'Sequence',
|
||||||
arrangementMode: 'Centralized',
|
arrangementMode: 'Centralized',
|
||||||
description: '',
|
description: '',
|
||||||
requirements: '',
|
requirements: '',
|
||||||
@@ -191,6 +202,8 @@ function openEditProject(project: any) {
|
|||||||
scheduleEntryId: project.scheduleEntryId ?? '',
|
scheduleEntryId: project.scheduleEntryId ?? '',
|
||||||
code: project.code,
|
code: project.code,
|
||||||
name: project.name,
|
name: project.name,
|
||||||
|
projectNames: '',
|
||||||
|
nameMode: 'Sequence',
|
||||||
arrangementMode: project.arrangementMode,
|
arrangementMode: project.arrangementMode,
|
||||||
description: project.description ?? '',
|
description: project.description ?? '',
|
||||||
requirements: project.requirements ?? '',
|
requirements: project.requirements ?? '',
|
||||||
@@ -199,11 +212,21 @@ function openEditProject(project: any) {
|
|||||||
projectDialog.value = true
|
projectDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function projectNameLines(value: string) {
|
||||||
|
return value.split(/\r?\n/).map((name) => name.trim()).filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
async function saveProject() {
|
async function saveProject() {
|
||||||
|
const names = projectNameLines(projectForm.projectNames)
|
||||||
|
const hasValidName = editingProjectId.value
|
||||||
|
? !!projectForm.name.trim()
|
||||||
|
: projectForm.nameMode === 'InputNames'
|
||||||
|
? names.length > 0
|
||||||
|
: !!projectForm.name.trim()
|
||||||
if (!projectForm.teachingTaskIds.length ||
|
if (!projectForm.teachingTaskIds.length ||
|
||||||
(editingProjectId.value && projectForm.arrangementMode === 'Centralized' && !projectForm.scheduleEntryId) ||
|
(editingProjectId.value && projectForm.arrangementMode === 'Centralized' && !projectForm.scheduleEntryId) ||
|
||||||
!projectForm.code.trim()
|
!projectForm.code.trim()
|
||||||
|| !projectForm.name.trim() || projectForm.dates.length !== 2) {
|
|| !hasValidName || projectForm.dates.length !== 2) {
|
||||||
ElMessage.warning(projectForm.arrangementMode === 'Centralized' && editingProjectId.value
|
ElMessage.warning(projectForm.arrangementMode === 'Centralized' && editingProjectId.value
|
||||||
? '请选择课表实验课,并填写项目编码、名称和开放日期'
|
? '请选择课表实验课,并填写项目编码、名称和开放日期'
|
||||||
: '请填写教学任务、项目编码、名称和开放日期')
|
: '请填写教学任务、项目编码、名称和开放日期')
|
||||||
@@ -213,7 +236,7 @@ async function saveProject() {
|
|||||||
teachingTaskId: projectForm.teachingTaskIds[0],
|
teachingTaskId: projectForm.teachingTaskIds[0],
|
||||||
scheduleEntryId: projectForm.scheduleEntryId || null,
|
scheduleEntryId: projectForm.scheduleEntryId || null,
|
||||||
code: projectForm.code,
|
code: projectForm.code,
|
||||||
name: projectForm.name,
|
name: projectForm.nameMode === 'InputNames' && !editingProjectId.value ? names[0] : projectForm.name,
|
||||||
arrangementMode: projectForm.arrangementMode,
|
arrangementMode: projectForm.arrangementMode,
|
||||||
description: projectForm.description || null,
|
description: projectForm.description || null,
|
||||||
requirements: projectForm.requirements || null,
|
requirements: projectForm.requirements || null,
|
||||||
@@ -229,6 +252,8 @@ async function saveProject() {
|
|||||||
...payload,
|
...payload,
|
||||||
teachingTaskId: undefined,
|
teachingTaskId: undefined,
|
||||||
teachingTaskIds: projectForm.teachingTaskIds,
|
teachingTaskIds: projectForm.teachingTaskIds,
|
||||||
|
names: projectForm.nameMode === 'InputNames' ? names : undefined,
|
||||||
|
generateSequentialNames: projectForm.nameMode === 'Sequence',
|
||||||
})
|
})
|
||||||
ElMessage.success(projectForm.arrangementMode === 'Centralized'
|
ElMessage.success(projectForm.arrangementMode === 'Centralized'
|
||||||
? `已按 ${projectForm.teachingTaskIds.length} 个教学班的全部实验课生成项目`
|
? `已按 ${projectForm.teachingTaskIds.length} 个教学班的全部实验课生成项目`
|
||||||
@@ -395,6 +420,30 @@ async function deleteProject(project: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openBatchRename() {
|
||||||
|
batchRenameProjectIds.value = projects.value
|
||||||
|
.filter((project) => draftSelectedProjectIds.value.includes(project.id))
|
||||||
|
.map((project) => project.id)
|
||||||
|
batchRenameNames.value = batchRenameProjects.value.map((project) => project.name).join('\n')
|
||||||
|
batchRenameDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveBatchRename() {
|
||||||
|
const names = projectNameLines(batchRenameNames.value)
|
||||||
|
if (names.length !== batchRenameProjectIds.value.length) {
|
||||||
|
ElMessage.warning(`已选 ${batchRenameProjectIds.value.length} 个项目,请粘贴 ${batchRenameProjectIds.value.length} 行项目名称`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await http.put('/experiments/batch/names', { projectIds: batchRenameProjectIds.value, names })
|
||||||
|
ElMessage.success('实验项目名称已批量更新')
|
||||||
|
batchRenameDialog.value = false
|
||||||
|
await load()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(apiErrorMessage(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function togglePageDraftSelection(value: unknown) {
|
function togglePageDraftSelection(value: unknown) {
|
||||||
const ids = projects.value.filter((project) => project.status === 'Draft').map((project) => project.id)
|
const ids = projects.value.filter((project) => project.status === 'Draft').map((project) => project.id)
|
||||||
selectedProjectIds.value = Boolean(value)
|
selectedProjectIds.value = Boolean(value)
|
||||||
@@ -715,6 +764,7 @@ onMounted(async () => {
|
|||||||
选中当前页全部草稿
|
选中当前页全部草稿
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<span>已选 {{ draftSelectedProjectIds.length }} 个草稿项目</span>
|
<span>已选 {{ draftSelectedProjectIds.length }} 个草稿项目</span>
|
||||||
|
<el-button size="small" :disabled="!draftSelectedProjectIds.length" @click="openBatchRename">批量改名</el-button>
|
||||||
<el-button size="small" type="primary" :disabled="!draftSelectedProjectIds.length" @click="publishSelectedProjects">批量发布</el-button>
|
<el-button size="small" type="primary" :disabled="!draftSelectedProjectIds.length" @click="publishSelectedProjects">批量发布</el-button>
|
||||||
<el-button size="small" type="danger" plain :disabled="!draftSelectedProjectIds.length" @click="deleteSelectedProjects">批量删除</el-button>
|
<el-button size="small" type="danger" plain :disabled="!draftSelectedProjectIds.length" @click="deleteSelectedProjects">批量删除</el-button>
|
||||||
</section>
|
</section>
|
||||||
@@ -979,10 +1029,31 @@ onMounted(async () => {
|
|||||||
<el-form-item label="项目编码" required>
|
<el-form-item label="项目编码" required>
|
||||||
<el-input v-model="projectForm.code" maxlength="40" placeholder="如 LAB-01" />
|
<el-input v-model="projectForm.code" maxlength="40" placeholder="如 LAB-01" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="项目名称" required>
|
<el-form-item v-if="editingProjectId" label="项目名称" required>
|
||||||
<el-input v-model="projectForm.name" maxlength="120" placeholder="如 数据库事务与并发实验" />
|
<el-input v-model="projectForm.name" maxlength="120" placeholder="如 数据库事务与并发实验" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="!editingProjectId">
|
||||||
|
<el-form-item label="项目命名方式" required>
|
||||||
|
<el-radio-group v-model="projectForm.nameMode">
|
||||||
|
<el-radio-button value="Sequence">自动生成顺序号</el-radio-button>
|
||||||
|
<el-radio-button value="InputNames">逐行粘贴名称</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="projectForm.nameMode === 'Sequence'" label="项目名称前缀" required>
|
||||||
|
<el-input v-model="projectForm.name" maxlength="116" placeholder="如 数据库实验;将生成“数据库实验 01、数据库实验 02”" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-else label="实验项目名称" required>
|
||||||
|
<el-input
|
||||||
|
v-model="projectForm.projectNames"
|
||||||
|
type="textarea"
|
||||||
|
:rows="6"
|
||||||
|
maxlength="120000"
|
||||||
|
placeholder="每行一个名称,可直接从 Excel 粘贴;系统按实际生成顺序逐行对应"
|
||||||
|
/>
|
||||||
|
<small class="form-help">已输入 {{ projectNameLines(projectForm.projectNames).length }} 行有效名称。</small>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
@@ -1043,6 +1114,23 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="batchRenameDialog" title="批量修改实验项目名称" width="680px">
|
||||||
|
<el-alert
|
||||||
|
:title="`已选择 ${batchRenameProjects.length} 个草稿项目。请按下方显示顺序,每行填写一个名称。`"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
/>
|
||||||
|
<ol class="batch-rename-preview">
|
||||||
|
<li v-for="project in batchRenameProjects" :key="project.id">{{ project.code }} · {{ project.name }}</li>
|
||||||
|
</ol>
|
||||||
|
<el-input v-model="batchRenameNames" type="textarea" :rows="Math.min(12, Math.max(5, batchRenameProjects.length))" maxlength="12000" placeholder="每行一个实验项目名称" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="batchRenameDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveBatchRename">保存名称</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="sessionDialog"
|
v-model="sessionDialog"
|
||||||
:title="`安排场次 · ${selectedProject?.name ?? ''}`"
|
:title="`安排场次 · ${selectedProject?.name ?? ''}`"
|
||||||
@@ -1253,6 +1341,7 @@ onMounted(async () => {
|
|||||||
.result-note { margin-left: auto; color: var(--muted); font-size: 12px; }
|
.result-note { margin-left: auto; color: var(--muted); font-size: 12px; }
|
||||||
.experiment-batch-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; min-height: 46px; margin: -6px 0 16px; padding: 8px 12px; border: 1px solid #cddce3; background: #f7fafb; }
|
.experiment-batch-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; min-height: 46px; margin: -6px 0 16px; padding: 8px 12px; border: 1px solid #cddce3; background: #f7fafb; }
|
||||||
.experiment-batch-bar > span { margin-right: auto; color: #5c7481; font-size: 12px; }
|
.experiment-batch-bar > span { margin-right: auto; color: #5c7481; font-size: 12px; }
|
||||||
|
.batch-rename-preview { max-height: 180px; margin: 14px 0; overflow: auto; padding: 10px 12px 10px 32px; border: 1px solid #dce5e9; background: #f8fbfc; color: #526b78; font-size: 12px; line-height: 1.8; }
|
||||||
.experiment-pagination { justify-content: flex-end; margin-top: 4px; }
|
.experiment-pagination { justify-content: flex-end; margin-top: 4px; }
|
||||||
.project-list { display: grid; gap: 14px; min-height: 160px; }
|
.project-list { display: grid; gap: 14px; min-height: 160px; }
|
||||||
.project-group { overflow: hidden; border: 1px solid #cddce3; background: #f6fafb; }
|
.project-group { overflow: hidden; border: 1px solid #cddce3; background: #f6fafb; }
|
||||||
|
|||||||
Reference in New Issue
Block a user