You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
601 lines
16 KiB
601 lines
16 KiB
<template> |
|
<el-drawer title="添加系统资源" :visible.sync="sourceVisible" size="1200px" :close-on-click-modal="false" |
|
custom-class="source-dia" @closed="closeDia"> |
|
<div class="overflow" v-loading="loading"> |
|
<div class="left"> |
|
<div class="tabs m-b-20"> |
|
<a class="item" v-for="(item, i) in tabs" :key="i" :class="{ active: i == active }" @click="tabChange(i)">{{ |
|
item |
|
}}</a> |
|
</div> |
|
|
|
<template v-if="active !== 'tab3'"> |
|
<el-input class="m-b-10" style="width: 300px" placeholder="请输入资源名称" prefix-icon="el-icon-search" |
|
v-model="keyword" clearable></el-input> |
|
|
|
<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 || course.courseName }}</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"> |
|
<el-checkbox class="check" v-model="section.check" @change="checkSection(section, chapter)"> |
|
<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=""> |
|
{{ section.name }}</el-checkbox> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</template> |
|
|
|
<!-- 文件素材 --> |
|
<div v-else class="materials"> |
|
<div class="types"> |
|
<div class="flex-center m-b-10"> |
|
<p class="addhr_tag"></p> |
|
<span>资源类型</span> |
|
</div> |
|
<ul class="lines"> |
|
<li v-for="(item, i) in sourceType" :key="i" :class="['line', { active: curType === item.name }]" |
|
@click="checkType(item)">{{ item.name }}</li> |
|
</ul> |
|
</div> |
|
|
|
<div class="sources"> |
|
<div class="flex-center m-b-10"> |
|
<p class="addhr_tag"></p> |
|
<span>资源列表</span> |
|
</div> |
|
<el-input class="m-b-10" placeholder="请输入资源名称" prefix-icon="el-icon-search" v-model="keyword" |
|
clearable></el-input> |
|
<ul class="lines"> |
|
<el-checkbox v-if="sources.length" v-model="checkAll" label="全选" @change="checkAllChange"></el-checkbox> |
|
<li v-for="(item, i) in sources" :key="i" class="line"> |
|
<el-checkbox v-model="item.check" :label="item.resourceName" @change="sourceChange(item)"></el-checkbox> |
|
</li> |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="right"> |
|
<div v-if="$parent.curSection.id" class="m-b-20"> |
|
<p class="total m-b-10">原资源(共1个)</p> |
|
<div v-html="$parent.curSection.name"></div> |
|
</div> |
|
<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 || 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="none"> |
|
<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' |
|
import SourceConst from '@/const/source' |
|
export default { |
|
props: ['visible'], |
|
data () { |
|
return { |
|
sourceVisible: false, |
|
active: 'tab1', |
|
tabs: { |
|
tab1: '教学课程', |
|
tab2: '精品课程', |
|
tab3: '文件素材', |
|
}, |
|
sourceType: SourceConst.types, |
|
curType: '', |
|
sources: [], |
|
checkAll: false, |
|
keyword: '', |
|
searchTimer: null, |
|
checkedKeyword: '', |
|
|
|
allSections: [], |
|
course: [], |
|
|
|
checked: [], |
|
submiting: false, |
|
loading: false, |
|
}; |
|
}, |
|
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() |
|
this.checked = [] |
|
this.sourceType.forEach(e => { |
|
e.check = false |
|
}) |
|
}, |
|
// 课程列表 |
|
async getCourse () { |
|
try { |
|
this.loading = true |
|
this.checkAll = false |
|
const { checked } = this |
|
if (this.active === 'tab3') { |
|
const { page } = await this.$post(this.api.resourceLibrary, { |
|
pageNum: 1, |
|
pageSize: 10000, |
|
platformId: Setting.platformId, |
|
type: 2, |
|
keyword: this.keyword, |
|
displayFileType: this.curType ? [this.curType] : [], |
|
}) |
|
const list = page.records |
|
list.forEach(e => { |
|
const cur = checked.find(m => m.resourceName && m.id === e.id) |
|
e.name = e.resourceName |
|
e.check = !!cur |
|
}) |
|
this.sources = list |
|
} else { |
|
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 => { |
|
const cur = checked.find(j => m.cid === j.cid && m.id === j.id) |
|
m.check = !!cur |
|
}) |
|
}) |
|
}) |
|
|
|
list.forEach(e => { |
|
e.shrink = false |
|
e.check = false |
|
e.chapters.forEach(n => { |
|
n.shrink = false |
|
n.check = n.subsections.every(m => m.check) |
|
}) |
|
}) |
|
|
|
list.forEach(e => { |
|
e.shrink = false |
|
e.check = e.chapters.every(m => m.check) |
|
}) |
|
list[0].shrink = true |
|
list[0].chapters[0].shrink = true |
|
this.allSections = all |
|
} |
|
this.course = list |
|
} |
|
} finally { |
|
this.loading = false |
|
} |
|
}, |
|
initData () { |
|
this.page = 1 |
|
this.getData() |
|
}, |
|
tabChange (index) { |
|
this.keyword = '' |
|
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) |
|
}, |
|
// 资源全选回调 |
|
checkAllChange (val) { |
|
this.sources.map(e => { |
|
e.check = val |
|
this.sourceChange(e) |
|
}) |
|
}, |
|
// 资源类型选择回调 |
|
checkType ({ name }) { |
|
this.curType = name |
|
this.getCourse() |
|
}, |
|
// 资源列表选择回调 |
|
sourceChange (row) { |
|
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) |
|
} |
|
}, |
|
|
|
// 批量移除 |
|
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 |
|
const { checked } = this |
|
if (checked.length) { |
|
this.submiting = true |
|
const { chapterId, id } = this.$parent |
|
const result = checked.map(e => { |
|
return { |
|
chapterId, |
|
cid: id, |
|
resourceId: e.id, |
|
type: e.cid ? 0 : e.resourceName ? 2 : 1, |
|
} |
|
}) |
|
|
|
const old = this.$parent.curSection // 需要更换的资源 |
|
if (old.id) { |
|
// 更换资源 |
|
await this.$post(this.api.replaceResource, { |
|
chapterId: old.chapterId, |
|
cid: +id, |
|
subsectionId: old.id, |
|
newResource: result |
|
}) |
|
|
|
} else { |
|
// 批量新增资源 |
|
await this.$post(this.api.combinationResource, result) |
|
} |
|
|
|
this.sourceVisible = false |
|
this.$parent.switchTypeVisible = false |
|
this.$parent.getData() |
|
this.submiting = false |
|
} else { |
|
Util.warningMsg('请选择资源') |
|
} |
|
} 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); |
|
} |
|
|
|
|
|
.none { |
|
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 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; |
|
|
|
img { |
|
vertical-align: middle; |
|
} |
|
} |
|
|
|
.cover { |
|
width: 100px; |
|
max-height: 80px; |
|
margin-right: 15px; |
|
border-radius: 6px; |
|
} |
|
|
|
.course-name { |
|
font-size: 14px; |
|
color: #333; |
|
} |
|
|
|
.chapters { |
|
padding-left: 26px; |
|
margin-top: 10px; |
|
|
|
.line { |
|
margin-bottom: 10px; |
|
} |
|
} |
|
|
|
.chapter-name { |
|
font-size: 13px; |
|
color: #ccc; |
|
} |
|
|
|
.sections { |
|
padding-left: 43px; |
|
} |
|
} |
|
|
|
.right { |
|
flex: 1; |
|
padding: 15px; |
|
|
|
.total { |
|
font-size: 15px; |
|
color: #333; |
|
font-weight: 600; |
|
} |
|
|
|
.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; |
|
} |
|
} |
|
|
|
.materials { |
|
display: flex; |
|
gap: 20px; |
|
} |
|
|
|
.types { |
|
width: 250px; |
|
height: calc(100vh - 223px); |
|
padding: 15px; |
|
background-color: #f9f9f9; |
|
box-sizing: border-box; |
|
overflow: auto; |
|
|
|
.line { |
|
margin-bottom: 10px; |
|
cursor: pointer; |
|
|
|
&.active { |
|
font-weight: 600; |
|
color: #062c87; |
|
} |
|
} |
|
} |
|
|
|
.sources { |
|
flex: 1; |
|
padding: 10px; |
|
|
|
.line { |
|
margin-top: 8px; |
|
} |
|
} |
|
} |
|
</style> |