新建时可选“自动生成顺序号”:例如 数据库实验 01、02…
或选“逐行粘贴名称”:每行一个名称,按实际生成顺序对应项目。 草稿项目支持勾选后“批量改名”,可直接粘贴多行名称。 服务端会校验名称数量、长度和草稿状态,避免错位或修改已发布项目。
This commit is contained in:
@@ -42,12 +42,18 @@ const projectForm = reactive({
|
||||
scheduleEntryId: '',
|
||||
code: '',
|
||||
name: '',
|
||||
projectNames: '',
|
||||
nameMode: 'Sequence',
|
||||
arrangementMode: 'Centralized',
|
||||
description: '',
|
||||
requirements: '',
|
||||
dates: [] as string[],
|
||||
})
|
||||
|
||||
const batchRenameDialog = ref(false)
|
||||
const batchRenameProjectIds = ref<string[]>([])
|
||||
const batchRenameNames = ref('')
|
||||
|
||||
const sessionDialog = ref(false)
|
||||
const selectedProject = ref<any>(null)
|
||||
const sessionForm = reactive({
|
||||
@@ -119,6 +125,9 @@ const draftSelectedProjectIds = computed(() =>
|
||||
selectedProjectIds.value.filter((id) =>
|
||||
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 ids = projects.value.filter((project) => project.status === 'Draft').map((project) => project.id)
|
||||
return ids.length > 0 && ids.every((id) => selectedProjectIds.value.includes(id))
|
||||
@@ -154,6 +163,8 @@ function resetProjectForm() {
|
||||
scheduleEntryId: '',
|
||||
code: '',
|
||||
name: '',
|
||||
projectNames: '',
|
||||
nameMode: 'Sequence',
|
||||
arrangementMode: 'Centralized',
|
||||
description: '',
|
||||
requirements: '',
|
||||
@@ -191,6 +202,8 @@ function openEditProject(project: any) {
|
||||
scheduleEntryId: project.scheduleEntryId ?? '',
|
||||
code: project.code,
|
||||
name: project.name,
|
||||
projectNames: '',
|
||||
nameMode: 'Sequence',
|
||||
arrangementMode: project.arrangementMode,
|
||||
description: project.description ?? '',
|
||||
requirements: project.requirements ?? '',
|
||||
@@ -199,11 +212,21 @@ function openEditProject(project: any) {
|
||||
projectDialog.value = true
|
||||
}
|
||||
|
||||
function projectNameLines(value: string) {
|
||||
return value.split(/\r?\n/).map((name) => name.trim()).filter(Boolean)
|
||||
}
|
||||
|
||||
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 ||
|
||||
(editingProjectId.value && projectForm.arrangementMode === 'Centralized' && !projectForm.scheduleEntryId) ||
|
||||
!projectForm.code.trim()
|
||||
|| !projectForm.name.trim() || projectForm.dates.length !== 2) {
|
||||
|| !hasValidName || projectForm.dates.length !== 2) {
|
||||
ElMessage.warning(projectForm.arrangementMode === 'Centralized' && editingProjectId.value
|
||||
? '请选择课表实验课,并填写项目编码、名称和开放日期'
|
||||
: '请填写教学任务、项目编码、名称和开放日期')
|
||||
@@ -213,7 +236,7 @@ async function saveProject() {
|
||||
teachingTaskId: projectForm.teachingTaskIds[0],
|
||||
scheduleEntryId: projectForm.scheduleEntryId || null,
|
||||
code: projectForm.code,
|
||||
name: projectForm.name,
|
||||
name: projectForm.nameMode === 'InputNames' && !editingProjectId.value ? names[0] : projectForm.name,
|
||||
arrangementMode: projectForm.arrangementMode,
|
||||
description: projectForm.description || null,
|
||||
requirements: projectForm.requirements || null,
|
||||
@@ -229,6 +252,8 @@ async function saveProject() {
|
||||
...payload,
|
||||
teachingTaskId: undefined,
|
||||
teachingTaskIds: projectForm.teachingTaskIds,
|
||||
names: projectForm.nameMode === 'InputNames' ? names : undefined,
|
||||
generateSequentialNames: projectForm.nameMode === 'Sequence',
|
||||
})
|
||||
ElMessage.success(projectForm.arrangementMode === 'Centralized'
|
||||
? `已按 ${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) {
|
||||
const ids = projects.value.filter((project) => project.status === 'Draft').map((project) => project.id)
|
||||
selectedProjectIds.value = Boolean(value)
|
||||
@@ -715,6 +764,7 @@ onMounted(async () => {
|
||||
选中当前页全部草稿
|
||||
</el-checkbox>
|
||||
<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="danger" plain :disabled="!draftSelectedProjectIds.length" @click="deleteSelectedProjects">批量删除</el-button>
|
||||
</section>
|
||||
@@ -979,10 +1029,31 @@ onMounted(async () => {
|
||||
<el-form-item label="项目编码" required>
|
||||
<el-input v-model="projectForm.code" maxlength="40" placeholder="如 LAB-01" />
|
||||
</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-form-item>
|
||||
</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 class="form-section">
|
||||
@@ -1043,6 +1114,23 @@ onMounted(async () => {
|
||||
</template>
|
||||
</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
|
||||
v-model="sessionDialog"
|
||||
:title="`安排场次 · ${selectedProject?.name ?? ''}`"
|
||||
@@ -1253,6 +1341,7 @@ onMounted(async () => {
|
||||
.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 > 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; }
|
||||
.project-list { display: grid; gap: 14px; min-height: 160px; }
|
||||
.project-group { overflow: hidden; border: 1px solid #cddce3; background: #f6fafb; }
|
||||
|
||||
Reference in New Issue
Block a user