合并分支:合并课程两步式向导与教学资源关联管理能力
This commit is contained in:
commit
9030d797f4
22 files changed
+1178
-291
No files matched your search
@@ -35,6 +35,23 @@ export function updateCourse(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 绑定课程教学资源
|
||||
export function bindCourseResource(cId, data) {
|
||||
return request({
|
||||
url: '/train/course/' + cId + '/resources',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 解绑课程教学资源(不删除资源本身)
|
||||
export function unbindCourseResource(cId, resourceId) {
|
||||
return request({
|
||||
url: '/train/course/' + cId + '/resources/' + resourceId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除课程信息
|
||||
export function delCourse(cId) {
|
||||
return request({
|
||||
|
||||
@@ -2,34 +2,14 @@
|
||||
<div class="app-container">
|
||||
<el-form v-show="showSearch" ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="68px">
|
||||
<el-form-item label="课程编码" prop="cCode">
|
||||
<el-input
|
||||
v-model="queryParams.cCode"
|
||||
placeholder="请输入课程编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.cCode" placeholder="请输入课程编码" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程名称" prop="cName">
|
||||
<el-input
|
||||
v-model="queryParams.cName"
|
||||
placeholder="请输入课程名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.cName" placeholder="请输入课程名称" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程类型" prop="cType">
|
||||
<el-select
|
||||
v-model="queryParams.cType"
|
||||
placeholder="请选择课程类型"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.train_course_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
<el-select v-model="queryParams.cType" placeholder="请选择课程类型" clearable>
|
||||
<el-option v-for="dict in dict.type.train_course_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
@@ -46,25 +26,10 @@
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
v-hasPermi="['train:course:add']"
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
<el-button v-hasPermi="['train:course:add']" type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
v-hasPermi="['train:course:edit']"
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
>修改</el-button>
|
||||
<el-button v-hasPermi="['train:course:edit']" type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :show-search.sync="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
@@ -79,26 +44,14 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学时" align="center" prop="cDuration" width="80" />
|
||||
<!-- <el-table-column label="教学目标" align="center" prop="teachingObjective" min-width="150" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="课程内容" align="center" prop="courseContent" min-width="160" :show-overflow-tooltip="true" />-->
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status === 1 ? '正常' : '停用' }}
|
||||
</el-tag>
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">{{ scope.row.status === 1 ? '正常' : '停用' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="备注" align="center" prop="remark" min-width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />-->
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-hasPermi="['train:course:edit']"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>修改</el-button>
|
||||
<el-button v-hasPermi="['train:course:edit']" size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 1"
|
||||
v-hasPermi="['train:course:edit']"
|
||||
@@ -129,119 +82,207 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改课程信息对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
|
||||
<el-form-item label="课程编码" prop="cCode">
|
||||
<el-input v-model="form.cCode" placeholder="请输入课程统一业务编码 (如 KC-2026-001)" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程名称" prop="cName">
|
||||
<el-input v-model="form.cName" placeholder="请输入课程名称" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程类型" prop="cType">
|
||||
<el-select v-model="form.cType" placeholder="请选择课程类型" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in dict.type.train_course_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="课程学时" prop="cDuration">
|
||||
<el-input-number v-model="form.cDuration" :min="1" :max="9999" placeholder="请输入课程学时" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="教学目标" prop="teachingObjective">
|
||||
<el-input v-model="form.teachingObjective" type="textarea" :rows="3" placeholder="请输入教学目标" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程内容" prop="courseContent">
|
||||
<el-input v-model="form.courseContent" type="textarea" :rows="4" placeholder="请输入课程内容/摘要" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程资源" prop="resourceId">
|
||||
<el-select
|
||||
v-model="resourceIdList"
|
||||
multiple
|
||||
filterable
|
||||
collapse-tags
|
||||
placeholder="请选择课程资源 (可多选)"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in resourceOptions"
|
||||
:key="item.resourceId"
|
||||
:label="item.resourceName"
|
||||
:value="String(item.resourceId)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio :label="1">正常</el-radio>
|
||||
<el-radio :label="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" maxlength="500" />
|
||||
</el-form-item>
|
||||
<el-dialog :title="title" :visible.sync="open" width="820px" append-to-body @closed="reset">
|
||||
<el-steps :active="activeStep" finish-status="success" simple class="course-steps">
|
||||
<el-step title="课程基本信息" icon="el-icon-edit-outline" />
|
||||
<el-step title="教学资源" icon="el-icon-folder-opened" />
|
||||
</el-steps>
|
||||
|
||||
<el-form v-show="activeStep === 0" ref="form" :model="form" :rules="rules" label-width="90px" class="course-form">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程编码" prop="cCode">
|
||||
<el-input v-model="form.cCode" placeholder="如 KC-2026-001" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程名称" prop="cName">
|
||||
<el-input v-model="form.cName" placeholder="请输入课程名称" maxlength="100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程类型" prop="cType">
|
||||
<el-select v-model="form.cType" placeholder="请选择课程类型" clearable style="width: 100%">
|
||||
<el-option v-for="dict in dict.type.train_course_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程学时" prop="cDuration">
|
||||
<el-input-number v-model="form.cDuration" :min="1" :max="9999" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="教学目标" prop="teachingObjective">
|
||||
<el-input v-model="form.teachingObjective" type="textarea" :rows="3" placeholder="请输入教学目标" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="课程内容" prop="courseContent">
|
||||
<el-input v-model="form.courseContent" type="textarea" :rows="4" placeholder="请输入课程内容/摘要" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio :label="1">正常</el-radio>
|
||||
<el-radio :label="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" maxlength="500" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div v-show="activeStep === 1" class="resource-step">
|
||||
<el-alert title="课程基本信息已保存,可关联资源库中的资料,或上传新资源后自动关联。" type="success" :closable="false" show-icon />
|
||||
|
||||
<el-card shadow="never" class="resource-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span>已关联资源</span>
|
||||
<el-tag size="small">{{ boundResources.length }} 项</el-tag>
|
||||
</div>
|
||||
<el-table :data="boundResources" empty-text="暂未关联教学资源" size="small">
|
||||
<el-table-column label="资源名称" prop="resourceName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="类型" prop="resourceType" width="110" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.train_resource_type" :value="scope.row.resourceType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件/地址" prop="resourceUrl" min-width="220" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-link :href="resourceLink(scope.row)" target="_blank" type="primary" :underline="false">{{ resourceLabel(scope.row) }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="handleUnbind(scope.row)">解除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="resource-card">
|
||||
<div slot="header">从资源库关联</div>
|
||||
<div class="resource-bind-row">
|
||||
<el-select v-model="resourceToBind" filterable clearable placeholder="搜索并选择已有教学资源" style="flex: 1">
|
||||
<el-option
|
||||
v-for="item in availableResourceOptions"
|
||||
:key="item.resourceId"
|
||||
:label="item.resourceName"
|
||||
:value="item.resourceId"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" :disabled="!resourceToBind" :loading="resourceActionLoading" @click="handleBindExisting">关联资源</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card v-hasPermi="['train:resource:edit']" shadow="never" class="resource-card">
|
||||
<div slot="header">上传并关联新资源</div>
|
||||
<el-form ref="resourceForm" :model="resourceForm" :rules="resourceRules" label-width="90px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资源名称" prop="resourceName">
|
||||
<el-input v-model="resourceForm.resourceName" maxlength="150" placeholder="请输入资源名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资源类型" prop="resourceType">
|
||||
<el-select v-model="resourceForm.resourceType" placeholder="请选择资源类型" style="width: 100%">
|
||||
<el-option v-for="item in resourceTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item v-if="isExternalLink" label="外部链接" prop="resourceUrl">
|
||||
<el-input v-model="resourceForm.resourceUrl" maxlength="500" placeholder="请输入以 http(s):// 开头的地址" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="资源文件" prop="resourceUrl">
|
||||
<file-upload v-model="resourceForm.resourceUrl" :limit="1" :file-size="100" :file-type="fileTypes" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="资源描述" prop="description">
|
||||
<el-input v-model="resourceForm.description" type="textarea" :rows="2" maxlength="500" placeholder="请输入资源描述/使用说明" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="resource-submit">
|
||||
<el-button type="primary" icon="el-icon-upload" :loading="resourceActionLoading" @click="handleCreateAndBind">保存资源并关联</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<template v-if="activeStep === 0">
|
||||
<el-button type="primary" :loading="savingCourse" @click="saveCourseAndNext">保存并下一步</el-button>
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button @click="activeStep = 0">上一步</el-button>
|
||||
<el-button type="primary" @click="finishWizard">完成</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCourse, getCourse, addCourse, updateCourse, delCourse } from '@/api/train/course'
|
||||
import { listResource, getResource } from '@/api/train/resource'
|
||||
import {
|
||||
listCourse,
|
||||
getCourse,
|
||||
addCourse,
|
||||
updateCourse,
|
||||
delCourse,
|
||||
bindCourseResource,
|
||||
unbindCourseResource
|
||||
} from '@/api/train/course'
|
||||
import { listResource, addResource } from '@/api/train/resource'
|
||||
|
||||
export default {
|
||||
name: 'Course',
|
||||
dicts: ['train_course_type'],
|
||||
dicts: ['train_course_type', 'train_resource_type'],
|
||||
data() {
|
||||
const validateDuration = (rule, value, callback) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
const num = Number(value)
|
||||
if (!Number.isInteger(num) || num <= 0) {
|
||||
callback(new Error('课程学时必须为正整数'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
const num = Number(value)
|
||||
if (value !== undefined && value !== null && value !== '' && (!Number.isInteger(num) || num <= 0)) {
|
||||
callback(new Error('课程学时必须为正整数'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateResourceUrl = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('资源文件或外部链接不能为空'))
|
||||
} else if (Number(this.resourceForm.resourceType) === 5 && !/^https?:\/\//i.test(value)) {
|
||||
callback(new Error('外部链接必须以 http:// 或 https:// 开头'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 课程信息表格数据
|
||||
courseList: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 课程资源下拉备选项
|
||||
activeStep: 0,
|
||||
savingCourse: false,
|
||||
resourceActionLoading: false,
|
||||
resourceOptions: [],
|
||||
// 已选课程资源ID集合 (对应 form.resourceId 逗号分隔字符串)
|
||||
resourceIdList: [],
|
||||
// 查询参数
|
||||
boundResources: [],
|
||||
resourceToBind: null,
|
||||
fileTypes: ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'txt', 'zip', 'rar', '7z', 'mp4', 'avi', 'mov', 'wmv', 'mp3', 'png', 'jpg', 'jpeg', 'gif', 'glb', 'gltf', 'obj', 'fbx'],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@@ -250,9 +291,8 @@ export default {
|
||||
cType: null,
|
||||
status: null
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
resourceForm: {},
|
||||
rules: {
|
||||
cCode: [
|
||||
{ required: true, message: '课程编码不能为空', trigger: 'blur' },
|
||||
@@ -262,71 +302,76 @@ export default {
|
||||
{ required: true, message: '课程名称不能为空', trigger: 'blur' },
|
||||
{ max: 100, message: '课程名称长度不能超过100个字符', trigger: 'blur' }
|
||||
],
|
||||
cDuration: [
|
||||
{ validator: validateDuration, trigger: 'blur' }
|
||||
]
|
||||
cDuration: [{ validator: validateDuration, trigger: 'blur' }]
|
||||
},
|
||||
resourceRules: {
|
||||
resourceName: [{ required: true, message: '资源名称不能为空', trigger: 'blur' }],
|
||||
resourceType: [{ required: true, message: '资源类型不能为空', trigger: 'change' }],
|
||||
resourceUrl: [{ validator: validateResourceUrl, trigger: ['blur', 'change'] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
resourceTypeOptions() {
|
||||
return (this.dict.type.train_resource_type || []).map(item => ({
|
||||
label: item.label,
|
||||
value: /^\d+$/.test(String(item.value)) ? Number(item.value) : item.value
|
||||
}))
|
||||
},
|
||||
isExternalLink() {
|
||||
return Number(this.resourceForm.resourceType) === 5
|
||||
},
|
||||
availableResourceOptions() {
|
||||
const boundIds = this.boundResources.map(item => String(item.resourceId))
|
||||
return this.resourceOptions.filter(item => boundIds.indexOf(String(item.resourceId)) === -1)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'resourceForm.resourceType'(newVal, oldVal) {
|
||||
if (oldVal !== undefined && newVal !== oldVal && this.open) {
|
||||
this.$set(this.resourceForm, 'resourceUrl', null)
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.resourceForm) {
|
||||
this.$refs.resourceForm.clearValidate('resourceUrl')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getResourceOptions()
|
||||
},
|
||||
methods: {
|
||||
/** 查询课程资源下拉备选项 (不做状态过滤,保证已选的停用资源也能正常回显) */
|
||||
getResourceOptions() {
|
||||
return listResource({ pageNum: 1, pageSize: 1000 }).then(response => {
|
||||
this.resourceOptions = response.rows || []
|
||||
return this.resourceOptions
|
||||
}).catch(() => {
|
||||
this.resourceOptions = this.resourceOptions || []
|
||||
return this.resourceOptions
|
||||
})
|
||||
},
|
||||
/** 课程资源ID字符串 -> 多选数组 (统一转成字符串,避免类型不一致导致无法勾选) */
|
||||
parseResourceIds(value) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return []
|
||||
}
|
||||
return String(value).split(',').map(item => String(item).trim()).filter(item => item !== '')
|
||||
},
|
||||
/** 保证已选资源在下拉选项中存在;element-ui 多选值若无对应 option 会不显示,故必须补齐后再回显 */
|
||||
ensureResourceOptions(ids) {
|
||||
const existingIds = this.resourceOptions.map(item => String(item.resourceId).trim())
|
||||
const missingIds = ids.filter(id => existingIds.indexOf(id) === -1)
|
||||
if (missingIds.length === 0) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.all(missingIds.map(id => {
|
||||
return getResource(id).then(response => {
|
||||
if (response.data) {
|
||||
this.resourceOptions.push(response.data)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}))
|
||||
},
|
||||
/** 备选项就绪后再回显已选资源 */
|
||||
prepareResourceOptions(ids) {
|
||||
const load = this.resourceOptions.length > 0 ? Promise.resolve() : this.getResourceOptions()
|
||||
return Promise.resolve(load).then(() => this.ensureResourceOptions(ids))
|
||||
},
|
||||
/** 查询课程信息列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listCourse(this.queryParams).then(response => {
|
||||
this.courseList = response.rows
|
||||
//console.log(response.rows);
|
||||
this.total = response.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 取消按钮
|
||||
getResourceOptions() {
|
||||
return listResource({ pageNum: 1, pageSize: 1000 }).then(response => {
|
||||
this.resourceOptions = response.rows || []
|
||||
})
|
||||
},
|
||||
loadCourseResources() {
|
||||
if (!this.form.cId) {
|
||||
this.boundResources = []
|
||||
return Promise.resolve()
|
||||
}
|
||||
return getCourse(this.form.cId).then(response => {
|
||||
this.boundResources = response.data.resources || []
|
||||
})
|
||||
},
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.activeStep = 0
|
||||
this.savingCourse = false
|
||||
this.resourceActionLoading = false
|
||||
this.form = {
|
||||
cId: null,
|
||||
cCode: null,
|
||||
@@ -335,81 +380,125 @@ export default {
|
||||
cDuration: null,
|
||||
teachingObjective: null,
|
||||
courseContent: null,
|
||||
resourceId: null,
|
||||
status: 1,
|
||||
remark: null
|
||||
}
|
||||
this.resourceIdList = []
|
||||
this.resetResourceForm()
|
||||
this.resourceOptions = []
|
||||
this.boundResources = []
|
||||
this.resourceToBind = null
|
||||
this.resetForm('form')
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
resetResourceForm() {
|
||||
this.resourceForm = {
|
||||
resourceId: null,
|
||||
resourceName: null,
|
||||
resourceType: 1,
|
||||
resourceUrl: null,
|
||||
description: null,
|
||||
status: 1,
|
||||
remark: null
|
||||
}
|
||||
this.resetForm('resourceForm')
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.cId)
|
||||
this.single = selection.length !== 1
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.prepareResourceOptions([]).then(() => {
|
||||
this.open = true
|
||||
this.title = '添加课程'
|
||||
})
|
||||
this.title = '添加课程'
|
||||
this.open = true
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const cId = row.cId || this.ids
|
||||
const cId = (row && row.cId) || this.ids[0]
|
||||
getCourse(cId).then(response => {
|
||||
//console.log(response.data);
|
||||
this.form = response.data
|
||||
if (this.form.status === undefined || this.form.status === null) {
|
||||
this.form.status = 1
|
||||
}
|
||||
const selectedIds = this.parseResourceIds(this.form.resourceId)
|
||||
// 必须先保证这些ID在下拉备选项中存在,否则多选不会被勾选/显示
|
||||
return this.prepareResourceOptions(selectedIds).then(() => {
|
||||
//this.resourceIdList = selectedIds
|
||||
//console.log("selectedIds===============");
|
||||
//console.log(selectedIds);
|
||||
this.$set(this,"resourceIdList",selectedIds);
|
||||
this.open = true
|
||||
this.title = '修改课程'
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
// 多选资源ID以逗号分隔写入 resource_id 字段
|
||||
this.form.resourceId = this.resourceIdList.length > 0 ? this.resourceIdList.join(',') : null
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.cId != null) {
|
||||
updateCourse(this.form).then(response => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
} else {
|
||||
addCourse(this.form).then(response => {
|
||||
this.$modal.msgSuccess('新增成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
}
|
||||
this.form = { ...response.data }
|
||||
this.form.status = this.form.status == null ? 1 : Number(this.form.status)
|
||||
this.boundResources = response.data.resources || []
|
||||
this.title = '修改课程'
|
||||
this.open = true
|
||||
})
|
||||
},
|
||||
/** 状态切换(停用/启用) */
|
||||
saveCourseAndNext() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
this.savingCourse = true
|
||||
const saving = this.form.cId ? updateCourse(this.form) : addCourse(this.form)
|
||||
saving.then(response => {
|
||||
if (!this.form.cId) {
|
||||
this.form = { ...this.form, ...response.data }
|
||||
}
|
||||
return Promise.all([this.getResourceOptions(), this.loadCourseResources()])
|
||||
}).then(() => {
|
||||
this.activeStep = 1
|
||||
this.$modal.msgSuccess('课程基本信息已保存')
|
||||
this.getList()
|
||||
}).finally(() => {
|
||||
this.savingCourse = false
|
||||
})
|
||||
})
|
||||
},
|
||||
handleBindExisting() {
|
||||
if (!this.resourceToBind) {
|
||||
return
|
||||
}
|
||||
this.resourceActionLoading = true
|
||||
bindCourseResource(this.form.cId, {
|
||||
resourceId: this.resourceToBind,
|
||||
sortNo: this.boundResources.length
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('资源关联成功')
|
||||
this.resourceToBind = null
|
||||
return this.loadCourseResources()
|
||||
}).finally(() => {
|
||||
this.resourceActionLoading = false
|
||||
})
|
||||
},
|
||||
handleCreateAndBind() {
|
||||
this.$refs.resourceForm.validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
this.resourceActionLoading = true
|
||||
addResource(this.resourceForm).then(response => {
|
||||
const resource = response.data
|
||||
return bindCourseResource(this.form.cId, {
|
||||
resourceId: resource.resourceId,
|
||||
sortNo: this.boundResources.length
|
||||
})
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('资源已上传并关联')
|
||||
this.resetResourceForm()
|
||||
return Promise.all([this.getResourceOptions(), this.loadCourseResources()])
|
||||
}).finally(() => {
|
||||
this.resourceActionLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
handleUnbind(resource) {
|
||||
this.$modal.confirm(`确定解除资源《${resource.resourceName}》与当前课程的关联吗?资源本身不会被删除。`).then(() => {
|
||||
return unbindCourseResource(this.form.cId, resource.resourceId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已解除关联')
|
||||
return this.loadCourseResources()
|
||||
}).catch(() => {})
|
||||
},
|
||||
finishWizard() {
|
||||
this.open = false
|
||||
this.getList()
|
||||
},
|
||||
handleStatusChange(row, newStatus) {
|
||||
const actionText = newStatus === 1 ? '启用' : '停用'
|
||||
const promptText = newStatus === 0
|
||||
@@ -422,7 +511,6 @@ export default {
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm(`确定删除课程《${row.cName}》吗?`).then(() => {
|
||||
return delCourse(row.cId)
|
||||
@@ -430,7 +518,60 @@ export default {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
},
|
||||
resourceLink(resource) {
|
||||
if (!resource || !resource.resourceUrl) {
|
||||
return ''
|
||||
}
|
||||
if (/^https?:\/\//i.test(resource.resourceUrl)) {
|
||||
return resource.resourceUrl
|
||||
}
|
||||
return process.env.VUE_APP_BASE_API + resource.resourceUrl
|
||||
},
|
||||
resourceLabel(resource) {
|
||||
if (!resource || !resource.resourceUrl) {
|
||||
return ''
|
||||
}
|
||||
if (Number(resource.resourceType) === 5) {
|
||||
return resource.resourceUrl
|
||||
}
|
||||
return resource.resourceUrl.substring(resource.resourceUrl.lastIndexOf('/') + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.course-steps {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.course-form {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.resource-step {
|
||||
max-height: 62vh;
|
||||
overflow-y: auto;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.resource-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.resource-bind-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.resource-submit {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user