增加课程资源 预览

develoop
JayChou 3 months ago
parent 2348b57582
commit 2fec3ce73f
  1. 1
      index.html
  2. 4
      package.json
  3. 104
      pnpm-lock.yaml
  4. 39
      src/Layout/index.vue
  5. 39
      src/api/course.ts
  6. 2
      src/main.ts
  7. 22
      src/utils/filters.ts
  8. 372
      src/views/course/components/courseTree copy.vue
  9. 403
      src/views/course/components/courseTree.vue
  10. 174
      src/views/course/index.vue
  11. 5
      src/views/home/components/Graph.vue
  12. 6
      src/views/home/index.vue

@ -9,6 +9,7 @@
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.5.136/pdf.min.mjs" type="module"></script>
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/3.8.3/dist/g6.min.js"></script>
<!-- <script src="https://unpkg.com/3d-force-graph@1.73.3/dist/3d-force-graph.min.js"></script> -->
</body>

@ -20,6 +20,7 @@
"@antv/g6": "3.8.3",
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.8",
"docx-preview": "^0.3.2",
"echarts": "^5.5.0",
"echarts-liquidfill": "^3.1.0",
"element-plus": "^2.6.2",
@ -30,7 +31,8 @@
"px2rem-loader": "^0.1.9",
"three": "^0.163.0",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
"vue-router": "^4.3.0",
"vue3-pdfjs": "^0.1.6"
},
"devDependencies": {
"@babel/eslint-parser": "^7.24.1",

@ -13,6 +13,9 @@ dependencies:
axios:
specifier: ^1.6.8
version: 1.6.8
docx-preview:
specifier: ^0.3.2
version: 0.3.2
echarts:
specifier: ^5.5.0
version: 5.5.0
@ -46,6 +49,9 @@ dependencies:
vue-router:
specifier: ^4.3.0
version: 4.3.0(vue@3.4.21)
vue3-pdfjs:
specifier: ^0.1.6
version: 0.1.6(typescript@5.2.2)
devDependencies:
'@babel/eslint-parser':
@ -1831,6 +1837,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: false
/cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@ -2205,6 +2215,12 @@ packages:
esutils: 2.0.3
dev: true
/docx-preview@0.3.2:
resolution: {integrity: sha512-YRsyiiejdauCQ2boKNHKjJMiIhOCXs643+NCHnmbCM31e7JWqmPiobtzlmHOnv4i+ft9w+ajPEK1hK7VymyRXQ==}
dependencies:
jszip: 3.10.1
dev: false
/dom-serializer@0.2.2:
resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
dependencies:
@ -2256,6 +2272,11 @@ packages:
domelementtype: 2.3.0
dev: true
/dommatrix@1.0.3:
resolution: {integrity: sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==}
deprecated: dommatrix is no longer maintained. Please use @thednp/dommatrix.
dev: false
/domutils@1.7.0:
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
dependencies:
@ -3282,6 +3303,10 @@ packages:
hasBin: true
dev: true
/immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
dev: false
/immutable@4.3.5:
resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==}
dev: true
@ -3560,7 +3585,6 @@ packages:
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: true
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@ -3652,6 +3676,15 @@ packages:
graceful-fs: 4.2.11
dev: true
/jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
dev: false
/kapsule@1.14.5:
resolution: {integrity: sha512-H0iSpTynUzZw3tgraDmReprpFRmH5oP5GPmaNsurSwLx2H5iCpOMIkp5q+sfhB4Tz/UJd1E1IbEE9Z6ksnJ6RA==}
engines: {node: '>=12'}
@ -3709,6 +3742,12 @@ packages:
resolution: {integrity: sha512-9yowMWA70tKhKdCJDaltY0mNQG4OWo7pWKScnTp9aiSxS7s20ZYlwBRE3335nweOf5qKXVC7sDxJwMPM8/MFZg==}
dev: false
/lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
dependencies:
immediate: 3.0.6
dev: false
/lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: true
@ -4110,6 +4149,10 @@ packages:
p-limit: 3.1.0
dev: true
/pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
dev: false
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@ -4163,6 +4206,18 @@ packages:
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
dev: true
/pdfjs-dist@2.16.105:
resolution: {integrity: sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A==}
peerDependencies:
worker-loader: ^3.0.8
peerDependenciesMeta:
worker-loader:
optional: true
dependencies:
dommatrix: 1.0.3
web-streams-polyfill: 3.3.3
dev: false
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@ -4355,6 +4410,10 @@ packages:
hasBin: true
dev: true
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: false
/proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
@ -4394,6 +4453,18 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
/readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
dev: false
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@ -4538,6 +4609,10 @@ packages:
isarray: 2.0.5
dev: true
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
dev: false
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: true
@ -4637,6 +4712,10 @@ packages:
split-string: 3.1.0
dev: true
/setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
dev: false
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -4797,6 +4876,12 @@ packages:
es-object-atoms: 1.0.0
dev: true
/string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
safe-buffer: 5.1.2
dev: false
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
@ -5363,7 +5448,6 @@ packages:
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: true
/vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
@ -5486,6 +5570,17 @@ packages:
typescript: 5.2.2
dev: true
/vue3-pdfjs@0.1.6(typescript@5.2.2):
resolution: {integrity: sha512-7UaWbsp8wNqB0y/rRlyo5yRb0S+XOkkSpmdUuS267Dhi07Pt4RFEetQ8inrpf/aTFJwGnW0Uc/UE4p376s+Zmw==}
engines: {node: '>=10.0.0'}
dependencies:
pdfjs-dist: 2.16.105
vue: 3.4.21(typescript@5.2.2)
transitivePeerDependencies:
- typescript
- worker-loader
dev: false
/vue@3.4.21(typescript@5.2.2):
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
peerDependencies:
@ -5501,6 +5596,11 @@ packages:
'@vue/shared': 3.4.21
typescript: 5.2.2
/web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
dev: false
/which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies:

@ -43,20 +43,33 @@ const scrollToTop = () => {
// flog.value = true
}
const scrollTop = () => {
var currentPosition = window.pageYOffset
if (currentPosition > 0) {
window.scrollTo(0, currentPosition - 40)
requestAnimationFrame(scrollTop)
} else {
totop.value.style.display = 'none'
num = 800
flog.value = false
show.value = false
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth',
});
setTimeout(() => {
show.value = true
}, 0)
}
show.value = false
},1500);
setTimeout(() => {
show.value = true
flog.value = false
num = 800
},1600);
// var currentPosition = window.pageYOffset
// if (currentPosition > 0) {
// window.scrollTo(0, currentPosition - 40)
// requestAnimationFrame(scrollTop)
// } else {
// totop.value.style.display = 'none'
// num = 800
// flog.value = false
// show.value = false
// setTimeout(() => {
// show.value = true
// }, 0)
// }
}
let num = 800
onMounted(() => {

@ -1,17 +1,44 @@
import request from '@/utils/requset'
import request from '@/utils/requset'
// 获取课程列表
export const getCourseList = (params:object) => {
export const getCourseList = (params: object) => {
return request({
url: '/api/coursesteacher/page',
params,
})
}
// 获取课程图谱
export const getCourseAtlas = (params: object) => {
return request({
url: '/knowNeo/getAllKnowByCourseId',
params,
})
}
// 获取课程简介
// export const getCourseDescription = (params:any) => {
// return request({
// url:''
// })
// }
export const getCourseChapter = (params: any) => {
return request({
url: '/chapter2/chapter',
params,
})
}
// 获取所有课程资源
export const getCourseFiles = (params:any) => {
return request({
url:'/api/coursesteacher/page',
url:'/resource/queryByCourseId',
params
})
}
// 获取课程图谱
export const getCourseAtlas = (params:object) => {
// 获取文件流
export const getFileStreams = (params:any) => {
return request({
url:'/know/all',
url:'/resource/read',
params
})
}

@ -19,12 +19,14 @@ import './permissions'
// 引入仓库
import pinia from '@/store/index'
import '@/utils/rem.js'
import VuePdf from 'vue3-pdfjs'
// 创建vue实例
const app = createApp(App)
// 注册element plus组件库
app.use(ElementPlus, {
locale: zhCn,
})
app.use(VuePdf)
// 注册全局组件
app.use(gloablComponent)
app.use(router)

@ -0,0 +1,22 @@
// 筛选文件类型
export const filterFilesType = (arr:[]) :{} => {
const res:any = {
video:[],
PPT:[],
questions:[],
word:[],
other:[]
}
arr.forEach((item:any) => {
if(item.type === 19){
res.video.push(item)
}else if(item.type === 6){
res.PPT.push(item)
}else if(item.type === 7 || item.type === 5){
res.word.push(item)
}else{
res.other.push(item)
}
})
return res
}

@ -0,0 +1,372 @@
<template>
<div class="fold-info-ui">
<div class="demo-collapse">
<el-collapse class="collapse">
<!-- <button class="my-button" style="position: absolute;left: 0;top: 0;" @click.stop="emits('add')">新增</button> -->
<el-collapse-item v-for="item in foldInfoData" :key="item.id">
<template #title>
<div class="title">
<div class="index" v-if="String(item.numshow).length == 1"> 0{{ item.numshow }} /</div>
<div class="index" v-else>{{ item.numshow }} -</div>
<div class="text">{{ item.name }}</div>
<div class="title-tag-box">
<!-- <div class="my-tag" type="warning">{{ item.totalclasshours }}学时</div> -->
<div class="my-tag">本章资源</div>
</div>
<!-- <button class="my-button" @click.stop="emits('edit', item)" style="margin-right: 10px;">编辑</button>
<my-btn :id="item.id" type="danger" class="is-el-button" style="margin-right: 10px;">删除</my-btn> -->
</div>
</template>
<template #default>
<div class="titile-box-knowledge chapter-knowledge">
<div class="my-tag">知识点1</div>
<div class="my-tag">知识点1</div>
</div>
<div class="main-box">
<div class="left"></div>
<div class="right">
<div class="structure-item">
<div class="structure-item-titile-box" v-for="obj, ind in item.chapterSection" :key="item.id">
<div class="titile-box-titile">
<div class="sequence">{{ ind + 1 }}</div>
<div class="tit-box">
<div class="tit-box-left">{{ obj.numshow }} {{ obj.name }}</div>
<div class="tit-box-right my-tag">资源</div>
<div class="title-tag-box">
<!-- <div class="my-tag" type="warning">{{ obj.totalclasshours }}学时</div> -->
</div>
<!-- <div class="tit-box-edit">
<button class="my-button" @click.stop="emits('edit', obj)"
style="margin-right: 10px;">编辑</button>
<my-btn :id="obj.id" type="danger" class="is-el-button"
style="margin-right: 10px;">删除</my-btn>
</div> -->
</div>
</div>
<div class="titile-box-knowledge">
<div class="my-tag">知识点1</div>
<div class="my-tag">知识点1</div>
</div>
</div>
</div>
</div>
</div>
</template>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const foldInfoData = ref([
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
])
const emits = defineEmits(['add', 'del', 'edit'])
</script>
<style lang="scss" scoped>
::v-deep .collapse {
border-top: none;
.el-collapse-item__header {
border-bottom: none;
}
}
.fold-info-ui {
width: 100%;
height:100%;
// padding-top: 27px;
// padding-left: 61px;
padding: 10px;
background: #fff;
position: relative;
.demo-collapse {
$title-icon-width: 15px;
$title--icon-m-r: 34px;
$title-index-width: 60px;
.title {
display: flex;
height: 22px;
width: 100%;
align-items: center;
.icon {
line-height: 0px;
width: $title-icon-width;
height: $title-icon-width;
margin-right: $title--icon-m-r;
& img {
width: 100%;
height: 100%;
}
}
.index {
width: $title-index-width;
text-align: center;
text-wrap: nowrap;
font-weight: 400;
font-size: 16px;
color: #333333;
// margin-right: 15px;
}
.text {
font-weight: bold;
font-size: 16px;
color: #333333;
}
.title-tag-box {
// margin-left: auto;
// margin-right: 76px;
display: flex;
column-gap: 16px;
}
}
.main-box {
width: 100%;
display: flex;
$line: 1px dashed #D9D9D9;
.left {
width: calc($title-icon-width + $title--icon-m-r + $title-index-width / 2);
border-right: $line;
}
.right {
flex-grow: 1;
.structure-item {
width: 100%;
.structure-item-titile-box {
&>* {
padding-left: 42px;
}
.titile-box-titile {
&:first-child {
margin-top: 20px;
}
position: relative;
margin-bottom: 15px;
&::before {
content: '';
display: block;
position: absolute;
left: 0;
top: 50%;
width: 30px;
border-top: $line;
}
.sequence {
$height: 33px;
width: $height;
height: $height;
position: absolute;
left: 0;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
background: #FFB21E;
text-align: center;
line-height: $height;
font-weight: 400;
font-size: 15px;
color: #000000;
}
.tit-box {
height: 20px;
display: flex;
align-items: center;
gap: 17px;
.tit-box-left {
font-weight: 400;
font-size: 16px;
color: #000000;
}
// .tit-box-right {}
.tit-box-edit {
margin-left: auto;
margin-right: 72px;
}
}
}
}
}
}
}
}
.titile-box-knowledge {
display: flex;
margin-left: 33px;
gap: 38px;
flex-wrap: wrap;
&>* {
border: 1px solid #0052D9;
font-weight: bold;
font-size: 12px;
color: #0052D9;
}
}
.chapter-knowledge {
margin-bottom: 10px;
margin-left: calc(79px - 33px / 2);
}
// ===================
.my-tag {
padding: 2px 8px;
font-weight: 400;
font-size: 12px;
color: #0052D9;
line-height: 20px;
background: #F2F3FF;
cursor: pointer;
border-radius: 3px;
}
* {
white-space: nowrap;
}
[type=warning].my-tag {
color: #E37318;
background: #FFF1E9;
}
}
.my-button {
all: initial;
$color-start: #52A1FF;
$color-end: #4255FF;
padding: 8px 24px;
background: linear-gradient(128deg, $color-start 0%, $color-end 100%);
box-shadow: 0px 4px 7px 0px rgba(0, 82, 255, 0.27);
border-radius: 20px;
font-weight: bold;
font-size: 16px;
color: rgba(255, 255, 255, 0.9);
line-height: 24px;
text-align: center;
cursor: pointer;
&:hover {
background: linear-gradient(128deg, lighten($color-start, 5%) 0%, lighten($color-end, 5%) 100%);
}
&:active {
background: linear-gradient(128deg, darken($color-start, 10%) 0%, darken($color-end, 10%) 100%);
}
&:disabled {
background: linear-gradient(128deg, lighten($color-start, 20%) 0%, lighten($color-end, 20%) 100%);
}
}
.is-el-button {
width: 80px;
height: 40px;
box-shadow: 0px 4px 7px 0px rgba(0, 82, 255, 0.27);
border-radius: 20px;
font-weight: bold;
font-size: 16px !important;
color: rgba(255, 255, 255, 0.9);
line-height: 24px;
text-align: center;
cursor: pointer;
}
</style>

@ -1,372 +1,33 @@
<template>
<div class="fold-info-ui">
<div class="demo-collapse">
<el-collapse class="collapse">
<!-- <button class="my-button" style="position: absolute;left: 0;top: 0;" @click.stop="emits('add')">新增</button> -->
<el-collapse-item v-for="item in foldInfoData" :key="item.id">
<template #title>
<div class="title">
<div class="index" v-if="String(item.numshow).length == 1"> 0{{ item.numshow }} /</div>
<div class="index" v-else>{{ item.numshow }} -</div>
<div class="text">{{ item.name }}</div>
<div class="title-tag-box">
<!-- <div class="my-tag" type="warning">{{ item.totalclasshours }}学时</div> -->
<div class="my-tag">本章资源</div>
</div>
<!-- <button class="my-button" @click.stop="emits('edit', item)" style="margin-right: 10px;">编辑</button>
<my-btn :id="item.id" type="danger" class="is-el-button" style="margin-right: 10px;">删除</my-btn> -->
</div>
</template>
<template #default>
<div class="titile-box-knowledge chapter-knowledge">
<div class="my-tag">知识点1</div>
<div class="my-tag">知识点1</div>
</div>
<div class="main-box">
<div class="left"></div>
<div class="right">
<div class="structure-item">
<div class="structure-item-titile-box" v-for="obj, ind in item.chapterSection" :key="item.id">
<div class="titile-box-titile">
<div class="sequence">{{ ind + 1 }}</div>
<div class="tit-box">
<div class="tit-box-left">{{ obj.numshow }} {{ obj.name }}</div>
<div class="tit-box-right my-tag">资源</div>
<div class="title-tag-box">
<!-- <div class="my-tag" type="warning">{{ obj.totalclasshours }}学时</div> -->
</div>
<!-- <div class="tit-box-edit">
<button class="my-button" @click.stop="emits('edit', obj)"
style="margin-right: 10px;">编辑</button>
<my-btn :id="obj.id" type="danger" class="is-el-button"
style="margin-right: 10px;">删除</my-btn>
</div> -->
</div>
</div>
<div class="titile-box-knowledge">
<div class="my-tag">知识点1</div>
<div class="my-tag">知识点1</div>
</div>
</div>
</div>
</div>
</div>
</template>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const foldInfoData = ref([
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours',
chapterSection: [
{
id: 'id',
name: 'Vue设计与实现',
numshow: 'num',
totalclasshours: 'totalclasshours'
}
]
},
])
const emits = defineEmits(['add', 'del', 'edit'])
</script>
<style lang="scss" scoped>
::v-deep .collapse {
border-top: none;
.el-collapse-item__header {
border-bottom: none;
}
}
.fold-info-ui {
width: 100%;
height:100%;
// padding-top: 27px;
// padding-left: 61px;
padding: 10px;
background: #fff;
position: relative;
.demo-collapse {
$title-icon-width: 15px;
$title--icon-m-r: 34px;
$title-index-width: 60px;
.title {
display: flex;
height: 22px;
width: 100%;
align-items: center;
.icon {
line-height: 0px;
width: $title-icon-width;
height: $title-icon-width;
margin-right: $title--icon-m-r;
& img {
width: 100%;
height: 100%;
}
}
.index {
width: $title-index-width;
text-align: center;
text-wrap: nowrap;
font-weight: 400;
font-size: 16px;
color: #333333;
// margin-right: 15px;
}
.text {
font-weight: bold;
font-size: 16px;
color: #333333;
}
.title-tag-box {
// margin-left: auto;
// margin-right: 76px;
display: flex;
column-gap: 16px;
}
}
.main-box {
width: 100%;
display: flex;
$line: 1px dashed #D9D9D9;
.left {
width: calc($title-icon-width + $title--icon-m-r + $title-index-width / 2);
border-right: $line;
}
.right {
flex-grow: 1;
.structure-item {
width: 100%;
.structure-item-titile-box {
&>* {
padding-left: 42px;
}
.titile-box-titile {
&:first-child {
margin-top: 20px;
}
position: relative;
margin-bottom: 15px;
&::before {
content: '';
display: block;
position: absolute;
left: 0;
top: 50%;
width: 30px;
border-top: $line;
}
.sequence {
$height: 33px;
width: $height;
height: $height;
position: absolute;
left: 0;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
background: #FFB21E;
text-align: center;
line-height: $height;
font-weight: 400;
font-size: 15px;
color: #000000;
}
.tit-box {
height: 20px;
display: flex;
align-items: center;
gap: 17px;
.tit-box-left {
font-weight: 400;
font-size: 16px;
color: #000000;
}
// .tit-box-right {}
.tit-box-edit {
margin-left: auto;
margin-right: 72px;
}
}
}
}
}
}
}
}
.titile-box-knowledge {
display: flex;
margin-left: 33px;
gap: 38px;
flex-wrap: wrap;
&>* {
border: 1px solid #0052D9;
font-weight: bold;
font-size: 12px;
color: #0052D9;
}
}
.chapter-knowledge {
margin-bottom: 10px;
margin-left: calc(79px - 33px / 2);
}
// ===================
.my-tag {
padding: 2px 8px;
font-weight: 400;
font-size: 12px;
color: #0052D9;
line-height: 20px;
background: #F2F3FF;
cursor: pointer;
border-radius: 3px;
}
* {
white-space: nowrap;
}
[type=warning].my-tag {
color: #E37318;
background: #FFF1E9;
}
}
.my-button {
all: initial;
$color-start: #52A1FF;
$color-end: #4255FF;
padding: 8px 24px;
background: linear-gradient(128deg, $color-start 0%, $color-end 100%);
box-shadow: 0px 4px 7px 0px rgba(0, 82, 255, 0.27);
border-radius: 20px;
font-weight: bold;
font-size: 16px;
color: rgba(255, 255, 255, 0.9);
line-height: 24px;
text-align: center;
cursor: pointer;
&:hover {
background: linear-gradient(128deg, lighten($color-start, 5%) 0%, lighten($color-end, 5%) 100%);
}
&:active {
background: linear-gradient(128deg, darken($color-start, 10%) 0%, darken($color-end, 10%) 100%);
}
&:disabled {
background: linear-gradient(128deg, lighten($color-start, 20%) 0%, lighten($color-end, 20%) 100%);
}
}
.is-el-button {
width: 80px;
height: 40px;
box-shadow: 0px 4px 7px 0px rgba(0, 82, 255, 0.27);
border-radius: 20px;
font-weight: bold;
font-size: 16px !important;
color: rgba(255, 255, 255, 0.9);
line-height: 24px;
text-align: center;
cursor: pointer;
}
</style>
<div class="tree-box">
<el-tree
default-expand-all
style="max-width: 600px"
:data="chapterList"
@node-click="handleNodeClick"
:props="defaultProps"
/>
</div>
</template>
<script lang="ts" setup>
const props = defineProps(['chapterList'])
const handleNodeClick = (data: any) => {
console.log(data)
}
console.log(props.chapterList,'props')
const chapterList = props.chapterList
const defaultProps = {
children: 'children',
label: 'name',
}
</script>
<style lang="scss" scoped>
.tree-box {
padding: 10px;
}
:deep(.el-tree-node__content) {
font-size: 16px;
}
</style>

@ -1,19 +1,68 @@
<template>
<div class="view-container">
<div class="banner"></div>
<div class="container">
<div class="container" v-if="loading">
<div class="left">
<div class="gruop">
<Graph :width="1195" :height="800" :index="courseId" :id="courseId" />
<Graph
:width="1195"
:height="800"
:index="courseId"
:id="courseId"
v-show="flag === 1"
/>
<div class="video" v-if="flag === 2">
<video
id="video"
width="100%"
height="100%"
controls
>
<source :src="videoUrl" type="video/mp4" />
您的浏览器不支持视频播放
</video>
</div>
<div class="pdf" v-if="flag === 3">
<!-- <vuePdf /> -->
<VuePdf v-for="page in numOfPages" :key="page" :src="pdfUrl" :page="page" />
</div>
<div v-show="flag != 1" class="back" @click="flag = 1">
<el-icon><Back /></el-icon>
</div>
</div>
<div class="resource">
<el-tabs
v-model="activeName"
class="demo-tabs"
@tab-click="handleClick"
v-loading="isTabsLoading"
>
<el-tab-pane label="视频" name="1">User</el-tab-pane>
<el-tab-pane label="PPT" name="2">Config</el-tab-pane>
<el-tab-pane label="视频" name="1">
<div class="files-box">
<div
class="item"
v-for="item in courseFilesList.video"
:key="item.id"
@click="clickFile(item, 2)"
>
{{ item.name }}
</div>
</div>
</el-tab-pane>
<el-tab-pane label="PPT" name="2">
<div class="files-box">
<div
class="item"
v-for="item in courseFilesList.PPT"
:key="item.id"
@click="clickFile(item, 3)"
>
{{ item.name }}
</div>
</div>
</el-tab-pane>
<el-tab-pane label="试题" name="3">Role</el-tab-pane>
<el-tab-pane label="文档" name="4">Task</el-tab-pane>
<el-tab-pane label="其他" name="5">Task</el-tab-pane>
@ -46,7 +95,7 @@
</div>
</div>
<div class="chapter">
<courseTree />
<courseTree :chapterList="chapterList" />
</div>
</div>
</div>
@ -54,26 +103,79 @@
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ref ,onMounted} from 'vue'
import Graph from '../home/components/Graph.vue'
import courseTree from './components/courseTree.vue'
import { useRoute } from 'vue-router'
import { getCourseAtlas } from '@/api/course'
import { getCourseChapter, getCourseFiles, getFileStreams } from '@/api/course'
import { ElLoading } from 'element-plus'
import { filterFilesType } from '@/utils/filters'
import pdf from 'vue-pdf'
import { VuePdf, createLoadingTask } from 'vue3-pdfjs/esm';
// import vuePdf from './components/vuePdf.vue'
const Route = useRoute()
const courseId: string = Route.query.id as string
console.log(Route.query.id)
const getCourseAtlasEvent = async () => {
const res = await getCourseAtlas({ id: courseId })
const chapterList = ref<any>([])
const loading = ref(false)
const getCourseChapterEvent = async () => {
const loadingInstance: any = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(255, 255, 255, 0.7)',
})
const res = await getCourseChapter({
courseId: '2cd6b5b62c344fd0becff6010cba566e',
})
chapterList.value = res
console.log(res)
loading.value = true
loadingInstance.close()
}
getCourseAtlasEvent()
getCourseChapterEvent()
const activeName = ref<string>('1')
const handleClick = (e: any) => {
console.log(e)
}
const isTabsLoading = ref<boolean>(false)
const courseFilesList = ref<any>({})
//
const getCourseFilesEvent = async () => {
isTabsLoading.value = true
const res: any = await getCourseFiles({
courseId: '2cd6b5b62c344fd0becff6010cba566e',
})
// console.log(filterFilesType(res));
courseFilesList.value = filterFilesType(res)
isTabsLoading.value = false
}
getCourseFilesEvent()
//
const flag = ref<number>(1)
const videoUrl = ref<string>('')
const pdfUrl = ref<string>('http://39.106.16.162:8080/resource/read?filename=20240812%2Fpdf%2F%E9%9D%A2%E8%AF%951-20243912103918.pdf')
const pdfSrc = ref('https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf')
const numOfPages = ref(0)
onMounted(() => {
const loadingTask = createLoadingTask(pdfSrc.value)
loadingTask.promise.then((pdf) => {
numOfPages.value = pdf.numPages
})})
const clickFile = async (file: any, index: number) => {
flag.value = index
if (index === 2) {
console.log(file)
videoUrl.value = file.url
} else if (index === 3) {
const res = await getFileStreams({ filename: file.name })
console.log(res)
}
}
</script>
<style lang="scss" scoped>
@ -89,10 +191,43 @@ const handleClick = (e: any) => {
width: 1195px;
// height: 800px;
.gruop {
position: relative;
overflow: hidden;
width: 100%;
height: 800px;
background-color: #fff;
.video {
width: 100%;
height: 100%;
background-color: #d9d9d9;
#video {
object-fit: cover;
}
}
.pdf {
width: 100%;
height: 100%;
overflow: auto
}
.back {
position: absolute;
top: 30px;
right: 30px;
z-index: 1;
width: 30px;
height: 30px;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 50%;
cursor: pointer;
i {
width: 100%;
height: 100%;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.resource {
width: 100%;
@ -100,6 +235,21 @@ const handleClick = (e: any) => {
margin-top: 20px;
padding: 20px;
background-color: #fff;
.files-box {
.item {
// width: 200px;
// height: 30px;
// background-color: skyblue;
// color: #fff;
line-height: 30px;
border-radius: 5px;
text-decoration: underline;
cursor: pointer;
}
.item:hover {
color: Blue;
}
}
}
}
.right {

@ -52,7 +52,8 @@ const props = defineProps({
// getCourseAtlasEvent()
const dom = ref(null)
onMounted(async () => {
const res: any = await getCourseAtlas({ id: props.id })
const res: any = await getCourseAtlas({ id: '719f91586a64413898253c5b7d046fd8' })
// console.log(res,'res')
const gData:any = {
nodes: [...res.knowList],
links: [...res.linksList],
@ -60,7 +61,7 @@ onMounted(async () => {
gData.links.forEach((link:any) => {
const a = gData.nodes.find((item:any) => item.id === link.source)
const b = gData.nodes.find((item:any) => item.id === link.target)
console.log(a, b)
// console.log(a, b)
!a.neighbors && (a.neighbors = [])
!b.neighbors && (b.neighbors = [])

@ -82,6 +82,8 @@ interface Page {
paNo: number
pageSize: number
username:string
userId: string | number
}
interface item {
name:string,
@ -101,8 +103,8 @@ const Router = useRouter()
const currentPage: Page = reactive({
paNo: 1,
pageSize: 5,
username:'xiao1111'
username:'xiao1111',
userId:3
})
const couresData: CouresData = reactive({
content: [],

Loading…
Cancel
Save