feat(position): 岗位课程分配增加全选功能和checkbox样式优化
Some checks failed
continuous-integration/drone/push Build is failing

- 添加表格多选功能,支持全选/取消全选
- 增加批量设为必修/选修操作
- 优化 checkbox 样式(渐变色、圆角、动画)
- 新增批量操作栏UI
This commit is contained in:
yuliang_guo
2026-01-28 13:10:47 +08:00
parent f52c8fde10
commit 98fc8b8eea

View File

@@ -338,40 +338,76 @@
<div class="course-section"> <div class="course-section">
<div class="section-header"> <div class="section-header">
<h4>可用课程列表</h4> <h4>可用课程列表</h4>
<el-input <div class="header-actions">
v-model="courseSearchText" <el-input
placeholder="搜索课程名称" v-model="courseSearchText"
style="width: 200px" placeholder="搜索课程名称"
clearable style="width: 200px"
> clearable
<template #prefix> >
<el-icon><Search /></el-icon> <template #prefix>
</template> <el-icon><Search /></el-icon>
</el-input> </template>
</el-input>
</div>
</div> </div>
<el-table :data="filteredAllCourses" style="width: 100%" max-height="300">
<el-table-column prop="name" label="课程名称" min-width="200" /> <!-- 批量操作栏 -->
<el-table-column prop="category" label="分类" width="120"> <div class="batch-actions" v-if="selectedCourses.length > 0">
<span class="selected-count">已选择 {{ selectedCourses.length }} 门课程</span>
<el-button type="danger" size="small" @click="batchAssignCourse('required')">
<el-icon><Star /></el-icon>
批量设为必修
</el-button>
<el-button type="warning" size="small" @click="batchAssignCourse('optional')">
<el-icon><CollectionTag /></el-icon>
批量设为选修
</el-button>
<el-button size="small" @click="clearSelection">
取消选择
</el-button>
</div>
<el-table
ref="courseTableRef"
:data="filteredAllCourses"
style="width: 100%"
max-height="300"
@selection-change="handleSelectionChange"
class="course-table"
>
<el-table-column type="selection" width="55">
<template #header>
<el-checkbox
v-model="isAllSelected"
:indeterminate="isIndeterminate"
@change="handleSelectAll"
class="custom-checkbox"
/>
</template>
</el-table-column>
<el-table-column prop="name" label="课程名称" min-width="180" />
<el-table-column prop="category" label="分类" width="100">
<template #default="scope"> <template #default="scope">
<el-tag size="small">{{ scope.row.category }}</el-tag> <el-tag size="small">{{ scope.row.category }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="duration" label="学时" width="80" /> <el-table-column prop="duration" label="学时" width="70" />
<el-table-column prop="difficulty" label="难度" width="100"> <el-table-column prop="difficulty" label="难度" width="80">
<template #default="scope"> <template #default="scope">
<el-tag :type="getDifficultyTagType(scope.row.difficulty)" size="small"> <el-tag :type="getDifficultyTagType(scope.row.difficulty)" size="small">
{{ getDifficultyText(scope.row.difficulty) }} {{ getDifficultyText(scope.row.difficulty) }}
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="当前状态" width="120"> <el-table-column label="当前状态" width="100">
<template #default="scope"> <template #default="scope">
<el-tag v-if="isCourseRequired(scope.row.id)" type="danger" size="small">必修</el-tag> <el-tag v-if="isCourseRequired(scope.row.id)" type="danger" size="small">必修</el-tag>
<el-tag v-else-if="isCourseOptional(scope.row.id)" type="warning" size="small">选修</el-tag> <el-tag v-else-if="isCourseOptional(scope.row.id)" type="warning" size="small">选修</el-tag>
<span v-else class="text-muted">未分配</span> <span v-else class="text-muted">未分配</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="200"> <el-table-column label="操作" width="180">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-if="!isCourseRequired(scope.row.id)" v-if="!isCourseRequired(scope.row.id)"
@@ -452,6 +488,12 @@ const requiredCourses = ref<any[]>([])
const optionalCourses = ref<any[]>([]) const optionalCourses = ref<any[]>([])
const allCourses = ref<any[]>([]) const allCourses = ref<any[]>([])
// 全选相关
const courseTableRef = ref<any>(null)
const selectedCourses = ref<any[]>([])
const isAllSelected = ref(false)
const isIndeterminate = ref(false)
// 岗位表单 // 岗位表单
const positionForm = reactive({ const positionForm = reactive({
id: '', id: '',
@@ -941,6 +983,67 @@ const filteredAllCourses = computed(() => {
) )
}) })
/**
* 处理表格选择变化
*/
const handleSelectionChange = (selection: any[]) => {
selectedCourses.value = selection
const total = filteredAllCourses.value.length
isAllSelected.value = selection.length === total && total > 0
isIndeterminate.value = selection.length > 0 && selection.length < total
}
/**
* 全选/取消全选
*/
const handleSelectAll = (val: boolean) => {
if (courseTableRef.value) {
if (val) {
filteredAllCourses.value.forEach(row => {
courseTableRef.value.toggleRowSelection(row, true)
})
} else {
courseTableRef.value.clearSelection()
}
}
}
/**
* 清除选择
*/
const clearSelection = () => {
if (courseTableRef.value) {
courseTableRef.value.clearSelection()
}
selectedCourses.value = []
isAllSelected.value = false
isIndeterminate.value = false
}
/**
* 批量分配课程
*/
const batchAssignCourse = async (type: 'required' | 'optional') => {
if (!currentPosition.value || selectedCourses.value.length === 0) return
try {
const courseIds = selectedCourses.value.map(c => c.id)
const response = await request.post(`/api/v1/admin/positions/${currentPosition.value.id}/courses`, {
course_ids: courseIds,
course_type: type
})
if (response && response.code === 200) {
await loadPositionCourses(currentPosition.value.id)
clearSelection()
ElMessage.success(`已将 ${courseIds.length} 门课程设为${type === 'required' ? '必修' : '选修'}`)
}
} catch (error) {
console.error('批量分配课程失败:', error)
ElMessage.error('批量分配课程失败')
}
}
/** /**
* 检查课程是否为必修 * 检查课程是否为必修
*/ */
@@ -1326,11 +1429,120 @@ onMounted(() => {
color: #333; color: #333;
margin: 0; margin: 0;
} }
.header-actions {
display: flex;
gap: 12px;
align-items: center;
}
}
// 批量操作栏样式
.batch-actions {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
margin-bottom: 12px;
background: linear-gradient(135deg, #e8f4fd 0%, #f0f7ff 100%);
border-radius: 8px;
border: 1px solid #d4e8fc;
.selected-count {
font-size: 14px;
font-weight: 500;
color: #409eff;
margin-right: 8px;
}
.el-button {
&.el-button--danger {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
border: none;
&:hover {
background: linear-gradient(135deg, #ff5252 0%, #d63031 100%);
}
}
&.el-button--warning {
background: linear-gradient(135deg, #ffa726 0%, #ff9800 100%);
border: none;
&:hover {
background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
}
}
}
} }
.text-muted { .text-muted {
color: #909399; color: #909399;
} }
// 课程表格样式优化
.course-table {
// Checkbox 样式优化
:deep(.el-checkbox) {
.el-checkbox__inner {
width: 18px;
height: 18px;
border-radius: 4px;
border: 2px solid #dcdfe6;
transition: all 0.2s ease;
&::after {
width: 4px;
height: 8px;
left: 5px;
top: 1px;
border-width: 2px;
}
}
&:hover .el-checkbox__inner {
border-color: #409eff;
}
&.is-checked .el-checkbox__inner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: #667eea;
}
&.is-indeterminate .el-checkbox__inner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: #667eea;
&::before {
height: 3px;
top: 6px;
left: 3px;
right: 3px;
}
}
}
// 表格行选中效果
:deep(.el-table__row) {
&.current-row,
&:hover {
background-color: #f5f7fa;
}
&.selection-row {
background-color: #ecf5ff;
}
}
// 表头样式
:deep(.el-table__header-wrapper) {
th {
background-color: #fafafa;
font-weight: 600;
color: #333;
}
}
}
} }
} }