备课管理

dev_202412
yujialong 4 months ago
parent f538b92d38
commit 78a7ecd845
  1. 5
      src/api/index.js
  2. 1
      src/assets/img/empty.svg
  3. 2
      src/layouts/home/index.vue
  4. 2
      src/layouts/sidebar/index.vue
  5. 1001
      src/pages/lesson/content/index.vue
  6. 426
      src/pages/lesson/content/source.vue
  7. 1161
      src/pages/lesson/detail/index.vue
  8. 307
      src/pages/lesson/list/index.vue
  9. 2
      src/pages/workbench/list/index.vue
  10. 35
      src/router/modules/lesson.js

@ -180,7 +180,6 @@ export default {
detailsOfCompetitionStage: `competition/competition/management/detailsOfCompetitionStage`,
entryInformation: `competition/competition/team/entryInformation`,
getCustomerOrder: `nakadai/nakadai/valueModuleManagement/getCustomerOrder`,
curriculumList: `nakadai/nakadai/curriculum/schoolCourse`,
queryCustomer: `nakadai/nakadai/customer/queryCustomer`,
getSchoolsByProvince: `nakadai/nakadai/school/getSchoolsByProvince`,
getRedisCacheCompetition: `competition/competition/management/getRedisCache`,
@ -518,6 +517,10 @@ export default {
submitTheExamPaperForPractice: `exam/exam/paper/submitTheExamPaperForPractice`,
examPaperRecordCache: `exam/exam/paper/examPaperRecordCache`,
curriculumList: `nakadai/nakadai/curriculum/curriculumList`,
createCurriculum: `nakadai/nakadai/curriculum/createCurriculum`,
modifyCourse: `nakadai/nakadai/curriculum/modifyCourse`,
// 教师评语
addComment: `evaluation/cevaluation/comment/addComment`,
queryComment: `evaluation/evaluation/ccomment/queryComment`,

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

@ -121,7 +121,7 @@ export default {
handler (val) {
this.path = val
//
this.showSidebar = ['/assessment/list', '/achievement/list', '/evaluation/list', '/course/list', '/review/list', '/information/list', '/project/list', '/resourse/list', '/exam/list', '/review', '/theoreticalCourse', '/resourse'].includes(val)
this.showSidebar = ['/assessment/list', '/achievement/list', '/evaluation/list', '/course/list', '/review/list', '/information/list', '/project/list', '/resourse/list', '/exam/list', '/review/list', '/lesson/list', '/resourse/list'].includes(val)
},
immediate: true
}

@ -72,7 +72,7 @@ export default {
title: '理论考试系统'
},
{
index: '/exam1/list',
index: '/lesson/list',
title: '备课管理'
},
],

File diff suppressed because it is too large Load Diff

@ -0,0 +1,426 @@
<template>
<el-drawer title="添加系统资源" :visible.sync="sourceVisible" size="1200px" :close-on-click-modal="false"
custom-class="source-dia" @closed="closeDia">
<div class="overflow">
<div class="left">
<el-input style="width: 300px" placeholder="请输入资源名称" prefix-icon="el-icon-search" v-model="keyword"
clearable></el-input>
<div class="tabs mgb20">
<a class="item" v-for="(item, i) in tabs" :key="i" :class="{ active: i == active }" @click="tabChange(i)">{{
item
}}</a>
</div>
<div class="course">
<div v-for="(course, i) in course" :key="i" class="item">
<div class="line">
<i :class="`el-icon-caret-right arrow ${course.shrink ? 'active' : ''}`"
@click="course.shrink = !course.shrink"></i>
<el-checkbox class="check" v-model="course.check" @change="checkCourse(course)"></el-checkbox>
<img v-if="course.coverUrl" class="cover" :src="course.coverUrl" alt="">
<span class="course-name">{{ course.curriculumName }}</span>
</div>
<div v-if="course.shrink" class="chapters">
<!-- 章节 -->
<div v-for="(chapter, j) in course.chapters" :key="j" class="">
<div class="line">
<i :class="`el-icon-caret-right arrow ${chapter.shrink ? 'active' : ''}`"
@click="chapter.shrink = !chapter.shrink"></i>
<el-checkbox class="check" v-model="chapter.check" @change="checkChapter(chapter, course)">{{
chapter.name
}}</el-checkbox>
</div>
<!-- 小节 -->
<div v-if="chapter.shrink" class="sections">
<div v-for="(section, k) in chapter.subsections" :key="k" class="line">
<img v-if="section.fileType === 'pptx'" src="@/assets/img/exts/ppt.png" alt="">
<img v-else-if="section.fileType === 'mp4'" src="@/assets/img/exts/video.png" alt="">
<img v-else-if="section.fileType === 'doc' || section.fileType === 'docx'"
src="@/assets/img/exts/word.png" alt="">
<img v-else-if="section.fileType === 'xlsx' || section.fileType === 'xls'"
src="@/assets/img/exts/excel.png" alt="">
<img v-else-if="section.fileType === 'txt'" src="@/assets/img/exts/txt.png" alt="">
<img v-else-if="section.fileType === 'pdf'" src="@/assets/img/exts/pdf.png" alt="">
<img v-else src="@/assets/img/exts/pic.png" alt="">
<el-checkbox class="check" v-model="section.check" @change="checkSection(section, chapter)">{{
section.name
}}</el-checkbox>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right">
<template v-if="checked.length">
<div class="flex-between m-b-10">
<p class="total">已选资源{{ checked.length }}</p>
<el-button type="text" @click="batchDelChecked">批量移除</el-button>
</div>
<el-input placeholder="请输入资源名称" prefix-icon="el-icon-search" v-model="checkedKeyword" clearable></el-input>
<div :class="['lines']">
<template v-for="(item, i) in checked">
<div v-if="item.name.includes(checkedKeyword)" :key="i" class="line">
<div class="check-left">
<el-checkbox v-model="item.check"></el-checkbox>
<span class="serial">{{ i + 1 }}</span>
<el-tooltip effect="dark" :content="item.name" placement="top-start">
<p class="checked-name ellipsis">{{ item.name }}</p>
</el-tooltip>
</div>
<i class="el-icon-delete action-icon" @click="delChecked(item)"></i>
</div>
</template>
</div>
</template>
<div v-else class="empty">
<img class="icon" src="@/assets/img/empty.svg" alt="">
<p>暂无数据</p>
</div>
</div>
</div>
<div class="btns">
<el-button @click="sourceVisible = false">取消</el-button>
<el-button type="primary" :loading="submiting" @click="submit">确定</el-button>
</div>
</el-drawer>
</template>
<script>
import Setting from '@/setting'
import Util from '@/libs/util'
import _ from 'lodash'
export default {
props: ['visible'],
data () {
return {
sourceVisible: false,
active: 'tab1',
tabs: {
tab1: '教学课程',
tab2: '精品课程',
tab3: '文件素材',
},
keyword: '',
searchTimer: null,
checkedKeyword: '',
allSections: [],
course: [],
checked: [],
submiting: false,
loaded: 0,
};
},
watch: {
'keyword': function (val) {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(this.getCourse, 500)
},
visible () {
this.sourceVisible = this.visible
this.visible && this.init()
}
},
mounted () {
},
methods: {
//
init () {
this.getCourse()
},
//
async getCourse () {
const { list } = await this.$post(`${this.api[this.active === 'tab1' ? 'curriculumTree' : 'boutiqueCurriculumTree']}?name=${this.keyword}`)
if (list.length) {
const all = []
list.forEach(e => {
e.shrink = false
e.check = false
e.chapters.forEach(n => {
n.shrink = false
n.check = false
all.push(...n.subsections)
n.subsections.forEach(m => {
m.check = false
})
})
})
list[0].shrink = true
list[0].chapters[0].shrink = true
this.allSections = all
}
this.course = list
},
initData () {
this.page = 1;
this.getData();
},
tabChange (index) {
this.active = index
this.getCourse()
},
//
checkCourse (row) {
const { check } = row
const { checked } = this
row.chapters.forEach(e => {
e.check = check
e.subsections.forEach(n => {
n.check = check
const cur = checked.findIndex(m => m.id === n.id)
if (check) {
if (cur === -1) {
const section = _.cloneDeep(n)
section.check = false
checked.push(section)
}
} else {
cur !== -1 && checked.splice(cur, 1)
}
})
})
},
//
checkChapter (row, course) {
const { check } = row
const { checked } = this
row.subsections.forEach(n => {
n.check = check
const cur = checked.findIndex(m => m.id === n.id)
if (check) {
if (cur === -1) {
const section = _.cloneDeep(n)
section.check = false
checked.push(section)
}
} else {
cur !== -1 && checked.splice(cur, 1)
}
})
course.check = course.chapters.every(e => e.check)
},
//
async checkSection (row, chapter) {
const { check } = row
const { checked } = this
const cur = checked.findIndex(m => m.id === row.id)
if (check) {
if (cur === -1) {
const section = _.cloneDeep(row)
section.check = false
this.checked.push(section)
}
} else {
cur !== -1 && this.checked.splice(cur, 1)
}
chapter.check = chapter.subsections.every(e => e.check)
},
//
async batchDelChecked (val) {
try {
const checked = this.checked.filter(e => e.check)
if (checked.length) {
checked.map(e => {
const cur = this.allSections.find(n => n.id === e.id)
if (cur) {
cur.check = false
}
})
this.checked = this.checked.filter(e => !e.check)
} else {
Util.warningMsg('请选择数据')
}
} catch (e) { }
},
//
async delChecked (item) {
try {
const cur = this.allSections.find(e => e.id === item.id)
if (cur) cur.check = false
this.checked.splice(this.checked.findIndex(e => e.id === item.id), 1)
} catch (e) { }
},
//
async submit () {
try {
if (this.submiting) return false
this.submiting = true
const { checked } = this
const { chapterId, id } = this.$parent
const result = checked.map(e => {
return {
chapterId,
cid: id,
resourceId: e.id,
type: e.cid ? 0 : 1,
}
})
const res = await this.$post(this.api.combinationResource, result)
this.sourceVisible = false
this.$parent.getData()
this.submiting = false
} catch (e) {
this.submiting = false
}
},
//
closeDia () {
this.$emit('update:visible', false)
}
}
};
</script>
<style lang="scss" scoped>
/deep/.source-dia {
.el-drawer__header {
padding-bottom: 20px;
margin-bottom: 0;
border-bottom: 1px solid #eee;
}
.overflow {
display: flex;
}
.btns {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 14px 0;
text-align: center;
background-color: #fff;
box-shadow: 4px -2px 6px 0px rgba(198, 198, 198, 0.3500);
}
.empty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
font-size: 14px;
text-align: center;
color: #a3a3a3;
}
.left {
width: 700px;
padding: 0 20px;
padding: 15px;
border-right: 1px solid #eee;
box-sizing: border-box;
.course {
height: calc(100vh - 263px);
overflow: auto;
.item {
padding: 10px;
margin-bottom: 10px;
background-color: #f9f9f9;
}
}
.line {
display: flex;
align-items: center;
}
.arrow {
font-size: 16px;
color: #9f9f9f;
cursor: pointer;
transition: .3s;
&.active {
transform: rotate(90deg);
}
}
.check {
margin: 0 10px;
}
.cover {
width: 100px;
max-height: 80px;
margin-right: 15px;
}
.course-name {
font-size: 14px;
color: #333;
}
.chapters {
padding-left: 21px;
margin-top: 10px;
.line {
margin-bottom: 10px;
}
}
.chapter-name {
font-size: 13px;
color: #ccc;
}
.sections {
padding-left: 26px;
}
}
.right {
flex: 1;
padding: 15px;
.lines {
height: calc(100vh - 228px);
padding-right: 10px;
margin-top: 10px;
overflow: auto;
}
.line {
display: flex;
padding: 5px 0;
color: #333;
}
.serial {
width: 32px;
margin: 0 12px;
text-align: center;
white-space: nowrap;
}
.check-left {
display: inline-flex;
align-items: center;
}
.checked-name {
width: 360px;
margin-right: 20px;
}
.action-icon {
font-size: 14px;
cursor: pointer;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,307 @@
<template>
<div>
<el-card shadow="hover" class="m-b-20">
<div>
<div class="flex-center m-b-20">
<p class="hr_tag"></p>
<span>筛选</span>
</div>
<div>
<el-form label-width="80px">
<el-col :span="4">
<el-form-item label="学科类别">
<el-select v-model="form.categoryId" clearable @change="getProfessionalClass()" @clear="clearClass()">
<el-option v-for="(item, index) in subjectList" :key="index" :label="item.disciplineName"
:value="item.disciplineId"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="专业类">
<el-select v-model="form.professionalCategoryId" clearable :disabled="form.categoryId ? false : true"
@change="getProfessional()" @clear="clearProfess()">
<el-option v-for="(item, index) in ProfessionalClassList" :key="index"
:label="item.professionalClassName" :value="item.professionalClassId"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="专业">
<el-select v-model="form.professionalId" clearable
:disabled="form.professionalCategoryId ? false : true" @change="getData()">
<el-option v-for="(item, index) in ProfessionalList" :key="index" :label="item.professionalName"
:value="item.professionalId"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="课程类别">
<el-select v-model="form.curriculumType" clearable @change="getData()">
<el-option label="理论课程" :value="0"></el-option>
<el-option label="实训课程" :value="1"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item>
<el-input placeholder="请输入课程名称" prefix-icon="el-icon-search" v-model="form.curriculumName" clearable
@keyup.enter.native="onSearch"></el-input>
</el-form-item>
</el-col>
</el-form>
</div>
</div>
</el-card>
<el-card shadow="hover" class="card">
<div class="flex-between m-b-20">
<div class="flex-center">
<p class="hr_tag"></p>
<span>课程列表</span>
</div>
<div>
<el-button type="primary" round @click="addcourse" v-auth>新建课程</el-button>
<el-button type="primary" round @click="delAllSelection" v-auth>批量删除</el-button>
</div>
</div>
<el-table v-loading="loading" :data="courseData" class="table" ref="table" header-align="center"
@selection-change="handleSelectionChange" :row-key="getRowKeys">
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="70" label="序号" align="center"></el-table-column>
<el-table-column prop="curriculumName" label="课程名称" align="center"></el-table-column>
<el-table-column prop="curriculumType" label="课程类别" width="90" align="center">
<template slot-scope="scope">
<span class="ellipsis">{{ courseTypeStatus[scope.row.curriculumType] }}</span>
</template>
</el-table-column>
<el-table-column label="配置的系统" align="center">
<template slot-scope="scope">
<span class="ellipsis">{{ scope.row.sysName }}</span>
</template>
</el-table-column>
<el-table-column prop="expectedCourse" label="预计课时" width="90" align="center"></el-table-column>
<el-table-column prop="orderVolume" label="上架范围" width="90" align="center"></el-table-column>
<el-table-column prop="userName" label="创建人" width="120" align="center"></el-table-column>
<el-table-column label="上架/下架" width="90" align="center">
<template slot-scope="scope">
<el-switch v-model="scope.row.isShelves" :active-value="1" :inactive-value="0"
@change="changeSwitch($event, scope.row)" v-auth="'/curriculum:上下架'">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center">
<template slot-scope="scope">
<el-button type="text" @click="edit(scope.row)" v-auth>编辑</el-button>
<el-button type="text" @click="config(scope.row)" v-auth>内容设置</el-button>
<el-button type="text" @click="handleDelete(scope.row)" v-auth>删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background @current-change="handleCurrentChange" :current-page="page"
layout="total, prev, pager, next" :total="totals"></el-pagination>
</div>
</el-card>
</div>
</template>
<script>
import qs from 'qs'
export default {
data () {
return {
courseTypeStatus: {
0: "理论课程",
1: "实训课程"
},
courseData: [],
form: {
categoryId: +this.$route.query.categoryId || '',
professionalCategoryId: +this.$route.query.professionalCategoryId || '',
professionalId: +this.$route.query.professionalId || '',
curriculumType: this.$route.query.curriculumType ? +this.$route.query.curriculumType : '',
curriculumName: this.$route.query.curriculumName || ''
},
page: +this.$route.query.page || 1,
pageSize: 10,
totals: 0,
subjectList: [], //
ProfessionalClassList: [], //
ProfessionalList: [], //
multipleSelection: [],
loading: false,
searchTimer: null
};
},
watch: {
"form.curriculumName": function (val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.initData();
}, 500);
}
},
mounted () {
this.getSubject();
this.getData();
},
methods: {
getRowKeys (row) {
return row.cid;
},
//
getData () {
this.$post(this.api.curriculumList, {
...this.form,
pageNum: this.page,
pageSize: this.pageSize,
supplierId: ''
}).then(res => {
this.courseData = res.page.records;
this.totals = res.page.total
this.loading = false
}).catch(err => { })
},
initData () {
this.page = 1
this.getData()
},
//
async getSubject () {
const { list } = await this.$get(this.api.courseDiscipline)
this.subjectList = list
this.form.professionalCategoryId && this.getProfessionalClassData()
},
//
clearClass () {
this.form.professionalCategoryId = ''
this.form.professionalId = ''
},
//
getProfessionalClass () {
this.clearClass();
this.getProfessionalClassData();
this.page = 1;
this.getData();
},
async getProfessionalClassData () {
const { list } = await this.$get(this.api.courseProfessionalClass, {
disciplineId: this.form.categoryId
})
this.ProfessionalClassList = list
this.form.professionalId && this.getProfessionalData()
},
//
clearProfess () {
this.form.professionalId = ''
},
//
getProfessional () {
this.clearProfess();
this.getProfessionalData();
this.page = 1;
this.getData();
},
async getProfessionalData () {
const { list } = await this.$get(this.api.courseProfessional, {
professionalClassId: this.form.professionalCategoryId
})
this.ProfessionalList = list
},
// url
setReferrer () {
// this.$store.commit('setReferrer', `${this.$route.path}?${qs.stringify(this.form)}&page=${this.page}`)
},
//
addcourse () {
this.setReferrer()
this.$router.push('detail')
},
//
edit (row) {
this.setReferrer()
this.$router.push(`detail?cid=${row.cid}`);
},
//
config (row) {
this.setReferrer()
this.$router.push(`content?cid=${row.cid}&name=${row.curriculumName}`);
},
//
handleDelete (row) {
this.$post(`${this.api.deleteCoursePrompt}?cids=${row.cid}`).then(({ status }) => {
if (status === 200) {
this.$confirm("确定要删除吗?", "提示", {
type: "warning"
}).then(() => {
this.$post(`${this.api.delCourse}?cids=${row.cid}`).then(res => {
this.getData();
this.$message.success("删除成功");
}).catch(err => { })
}).catch(() => {
});
}
}).catch(err => { })
},
//
handleSelectionChange (val) {
this.multipleSelection = val;
},
//
delAllSelection () {
if (this.multipleSelection.length) {
let cids = []
this.multipleSelection.forEach(i => {
cids.push('cids=' + i.cid)
});
this.$post(`${this.api.deleteCoursePrompt}?${cids.join('&')}`).then(({ status }) => {
if (status === 200) {
this.$confirm("确定要删除吗?", "提示", {
type: "warning"
}).then(() => {
let ids = this.multipleSelection.map(i => i.cid);
this.$post(`${this.api.delCourse}?cids=${ids.toString()}`).then(res => {
if (ids.length == this.courseData.length) {
if (this.page > 1) {
this.page = this.page - 1
}
}
this.getData();
this.$message.success("删除成功");
this.$refs.table.clearSelection()
}).catch(err => { })
}).catch(() => { })
}
}).catch(err => { })
} else {
this.$message.warning("请先选择课程 !");
}
},
//
handleCurrentChange (val) {
this.page = val;
this.$router.push(`/curriculum?page=${val}`)
this.getData();
},
//
onSearch () {
this.page = 1;
this.getData();
},
//
changeSwitch (value, row) {
this.$post(`${this.api.isShelves}?cid=${row.cid}&isShelves=${value}`).then((res) => {
this.getData();
this.$message.success("修改上下架状态成功!");
}).catch((res) => {
});
}
}
};
</script>
<style scoped>
.card {
min-height: calc(100vh - 300px);
}
</style>

@ -45,7 +45,7 @@
<img src="@/assets/img/workbench/9.png" alt="">
<p class="name">理论考试系统</p>
</div>
<div class="app" @click="to('/data')">
<div class="app" @click="to('/lesson')">
<img src="@/assets/img/workbench/10.png" alt="">
<p class="name">备课管理</p>
</div>

@ -0,0 +1,35 @@
import BasicLayout from "@/layouts/home";
const meta = {};
const pre = "lesson-";
export default {
path: "/lesson",
name: "lesson",
redirect: {
name: `${pre}list`
},
meta,
component: BasicLayout,
children: [
{
name: `${pre}list`,
path: `list`,
component: () => import("@/pages/lesson/list"),
meta: { title: "备课管理" }
},
{
name: `${pre}detail`,
path: `detail`,
component: () => import("@/pages/lesson/detail"),
meta: { title: "新增课程" }
},
{
name: `${pre}content`,
path: `content`,
component: () => import("@/pages/lesson/content"),
meta: { title: "内容设置" }
}
]
};
Loading…
Cancel
Save