课程章节

dev
significative 6 months ago
parent fdef8ce4d4
commit 1a5a34d289
  1. 2
      mock/user.ts
  2. 2
      package.json
  3. 229
      pnpm-lock.yaml
  4. 267
      public/data.json
  5. 29
      src/api/courseChaptersApi.ts
  6. BIN
      src/assets/images/minus-circle-filled.png
  7. BIN
      src/assets/images/minus-circle-filled@2x.png
  8. 13
      src/router/routers.ts
  9. 153
      src/views/course/components/KnowledgeGraph.vue
  10. 275
      src/views/course/components/KnowledgeGraphUi/foldInfoUi.vue
  11. 5
      src/views/course/components/KnowledgeGraphUi/index.ts
  12. 195
      src/views/course/components/KnowledgeGraphUi/lookResourceUi.vue
  13. 96
      src/views/course/components/KnowledgeGraphUi/objectiveUi.vue
  14. 116
      src/views/course/components/KnowledgeGraphUi/theoryUi.vue
  15. 50
      src/views/course/components/atlasUi.vue
  16. 142
      src/views/course/courseChapters.vue
  17. 14
      src/views/course/knowledgeAtlas.vue
  18. 468
      src/views/course/spritetext.js

@ -10,7 +10,7 @@ function createUserList() {
desc: '平台管理员',
roles: ['平台管理员'],
buttons: ['cuser.detail'],
routes: ['Home', 'Course', 'Student', 'Group', 'Message','BasicCourseInformation','CourseObjectives','CourseChapters','KnowledgePoints','CurriculumMap'], //老师权限
routes: ['Home', 'Course', 'Student', 'Group', 'Message','BasicCourseInformation','CourseObjectives','CourseChapters','KnowledgePoints','CurriculumMap','knowledgeAtlas'], //老师权限
token: 'Admin Token',
},
{

@ -17,12 +17,14 @@
"preinstall": "node ./scripts/preinstall.js"
},
"dependencies": {
"3d-force-graph": "^1.73.3",
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.9.0",
"axios": "^1.6.8",
"element-plus": "^2.6.0",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"three": "^0.163.0",
"vue": "^3.4.19",
"vue-router": "^4.3.0"
},

@ -1,6 +1,13 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
3d-force-graph:
specifier: ^1.73.3
version: 1.73.3
'@element-plus/icons-vue':
specifier: ^2.3.1
version: 2.3.1(vue@3.4.19)
@ -19,6 +26,9 @@ dependencies:
pinia:
specifier: ^2.1.7
version: 2.1.7(typescript@5.2.2)(vue@3.4.19)
three:
specifier: ^0.163.0
version: 0.163.0
vue:
specifier: ^3.4.19
version: 3.4.19(typescript@5.2.2)
@ -123,6 +133,17 @@ devDependencies:
packages:
/3d-force-graph@1.73.3:
resolution: {integrity: sha512-azb65Lwn2yr/fJ4+qrxjmstVxogjzwJIZL/fdboCKBg6ph/FLW+xdvYFEBZW92XxBn1C8yRKS3d2VkVT3BzLSw==}
engines: {node: '>=12'}
dependencies:
accessor-fn: 1.5.0
kapsule: 1.14.5
three: 0.163.0
three-forcegraph: 1.41.14(three@0.163.0)
three-render-objects: 1.29.4(three@0.163.0)
dev: false
/@aashutoshrathi/word-wrap@1.2.6:
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
@ -302,6 +323,13 @@ packages:
dependencies:
'@babel/types': 7.24.0
/@babel/runtime@7.24.6:
resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.1
dev: false
/@babel/template@7.24.0:
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
engines: {node: '>=6.9.0'}
@ -844,6 +872,10 @@ packages:
engines: {node: '>=10.13.0'}
dev: true
/@tweenjs/tween.js@23.1.2:
resolution: {integrity: sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==}
dev: false
/@types/estree@1.0.5:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true
@ -1219,6 +1251,11 @@ packages:
- vue
dev: false
/accessor-fn@1.5.0:
resolution: {integrity: sha512-dml7D96DY/K5lt4Ra2jMnpL9Bhw5HEGws4p1OAIxFFj9Utd/RxNfEO3T3f0QIWFNwQU7gNxH9snUfqF/zNkP/w==}
engines: {node: '>=12'}
dev: false
/acorn-jsx@5.3.2(acorn@8.11.3):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@ -1791,6 +1828,104 @@ packages:
/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
/d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
dependencies:
internmap: 2.0.3
dev: false
/d3-binarytree@1.0.2:
resolution: {integrity: sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==}
dev: false
/d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
dev: false
/d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
dev: false
/d3-force-3d@3.0.5:
resolution: {integrity: sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg==}
engines: {node: '>=12'}
dependencies:
d3-binarytree: 1.0.2
d3-dispatch: 3.0.1
d3-octree: 1.0.2
d3-quadtree: 3.0.1
d3-timer: 3.0.1
dev: false
/d3-format@3.1.0:
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
engines: {node: '>=12'}
dev: false
/d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
dependencies:
d3-color: 3.1.0
dev: false
/d3-octree@1.0.2:
resolution: {integrity: sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==}
dev: false
/d3-quadtree@3.0.1:
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
engines: {node: '>=12'}
dev: false
/d3-scale-chromatic@3.1.0:
resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
engines: {node: '>=12'}
dependencies:
d3-color: 3.1.0
d3-interpolate: 3.0.1
dev: false
/d3-scale@4.0.2:
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.4
d3-format: 3.1.0
d3-interpolate: 3.0.1
d3-time: 3.1.0
d3-time-format: 4.1.0
dev: false
/d3-time-format@4.1.0:
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
engines: {node: '>=12'}
dependencies:
d3-time: 3.1.0
dev: false
/d3-time@3.1.0:
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.4
dev: false
/d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
dev: false
/data-joint@1.3.1:
resolution: {integrity: sha512-tMK0m4OVGqiA3zkn8JmO6YAqD8UwJqIAx4AAwFl1SKTtKAqcXePuT+n2aayiX9uITtlN3DFtKKTOxJRUc2+HvQ==}
engines: {node: '>=12'}
dependencies:
index-array-by: 1.4.1
dev: false
/dayjs@1.11.10:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
dev: false
@ -2967,6 +3102,11 @@ packages:
engines: {node: '>=0.8.19'}
dev: true
/index-array-by@1.4.1:
resolution: {integrity: sha512-Zu6THdrxQdyTuT2uA5FjUoBEsFHPzHcPIj18FszN6yXKHxSfGcR4TPLabfuT//E25q1Igyx9xta2WMvD/x9P/g==}
engines: {node: '>=12'}
dev: false
/inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
dependencies:
@ -2991,6 +3131,11 @@ packages:
side-channel: 1.0.6
dev: true
/internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
dev: false
/is-accessor-descriptor@1.0.1:
resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==}
engines: {node: '>= 0.10'}
@ -3288,6 +3433,13 @@ packages:
graceful-fs: 4.2.11
dev: true
/kapsule@1.14.5:
resolution: {integrity: sha512-H0iSpTynUzZw3tgraDmReprpFRmH5oP5GPmaNsurSwLx2H5iCpOMIkp5q+sfhB4Tz/UJd1E1IbEE9Z6ksnJ6RA==}
engines: {node: '>=12'}
dependencies:
lodash-es: 4.17.21
dev: false
/keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
dependencies:
@ -3563,6 +3715,32 @@ packages:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: true
/ngraph.events@1.2.2:
resolution: {integrity: sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ==}
dev: false
/ngraph.forcelayout@3.3.1:
resolution: {integrity: sha512-MKBuEh1wujyQHFTW57y5vd/uuEOK0XfXYxm3lC7kktjJLRdt/KEKEknyOlc6tjXflqBKEuYBBcu7Ax5VY+S6aw==}
dependencies:
ngraph.events: 1.2.2
ngraph.merge: 1.0.0
ngraph.random: 1.1.0
dev: false
/ngraph.graph@20.0.1:
resolution: {integrity: sha512-VFsQ+EMkT+7lcJO1QP8Ik3w64WbHJl27Q53EO9hiFU9CRyxJ8HfcXtfWz/U8okuoYKDctbciL6pX3vG5dt1rYA==}
dependencies:
ngraph.events: 1.2.2
dev: false
/ngraph.merge@1.0.0:
resolution: {integrity: sha512-5J8YjGITUJeapsomtTALYsw7rFveYkM+lBj3QiYZ79EymQcuri65Nw3knQtFxQBU1r5iOaVRXrSwMENUPK62Vg==}
dev: false
/ngraph.random@1.1.0:
resolution: {integrity: sha512-h25UdUN/g8U7y29TzQtRm/GvGr70lK37yQPvPKXXuVfs7gCm82WipYFZcksQfeKumtOemAzBIcT7lzzyK/edLw==}
dev: false
/node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: true
@ -3789,6 +3967,13 @@ packages:
vue-demi: 0.14.7(vue@3.4.19)
dev: false
/polished@4.3.1:
resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==}
engines: {node: '>=10'}
dependencies:
'@babel/runtime': 7.24.6
dev: false
/posix-character-classes@0.1.1:
resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
engines: {node: '>=0.10.0'}
@ -3980,6 +4165,10 @@ packages:
picomatch: 2.3.1
dev: true
/regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
dev: false
/regex-not@1.0.2:
resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==}
engines: {node: '>=0.10.0'}
@ -4683,6 +4872,46 @@ packages:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
/three-forcegraph@1.41.14(three@0.163.0):
resolution: {integrity: sha512-W/cZElLXO0l6ffdMmDakh4bUGSYuSv/YxInOHMN9KAQgDwJ8904SOBh8qkTnGu7UPsi0mAsrUgkfViW8heloTA==}
engines: {node: '>=12'}
peerDependencies:
three: '>=0.118.3'
dependencies:
accessor-fn: 1.5.0
d3-array: 3.2.4
d3-force-3d: 3.0.5
d3-scale: 4.0.2
d3-scale-chromatic: 3.1.0
data-joint: 1.3.1
kapsule: 1.14.5
ngraph.forcelayout: 3.3.1
ngraph.graph: 20.0.1
three: 0.163.0
tinycolor2: 1.6.0
dev: false
/three-render-objects@1.29.4(three@0.163.0):
resolution: {integrity: sha512-E6YwTN5zNsaMjo/5rosgnK44b1aq//3YJGJ5BxG9t7+euRm7ZAmNX3NIqFkoDhKtFC5WLoOxZjyNoq8Uc49gaA==}
engines: {node: '>=12'}
peerDependencies:
three: '*'
dependencies:
'@tweenjs/tween.js': 23.1.2
accessor-fn: 1.5.0
kapsule: 1.14.5
polished: 4.3.1
three: 0.163.0
dev: false
/three@0.163.0:
resolution: {integrity: sha512-HlMgCb2TF/dTLRtknBnjUTsR8FsDqBY43itYop2+Zg822I+Kd0Ua2vs8CvfBVefXkBdNDrLMoRTGCIIpfCuDew==}
dev: false
/tinycolor2@1.6.0:
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
dev: false
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}

@ -0,0 +1,267 @@
{
"nodes": [
{ "id": "node1", "label": "计算机", "color": "#4682B4", "classID": 0 },
{
"id": "node2",
"label": "前端",
"color": "rgba(254, 241, 0, 1)",
"classID": 1
},
{
"id": "node3",
"label": "js",
"color": "rgba(239, 242, 18, 1)",
"classID": 2
},
{
"id": "node4",
"label": "html",
"color": "rgba(230, 234, 10, 1)",
"classID": 3
},
{
"id": "node5",
"label": "css",
"color": "rgba(244, 231, 0, 1)",
"classID": 4
},
{
"id": "node6",
"label": "less",
"color": "rgba(15, 245, 57, 1)",
"classID": 5
},
{
"id": "node7",
"label": "scss",
"color": "rgba(133, 255, 11, 1)",
"classID": 6
},
{
"id": "node8",
"label": "VUE",
"color": "rgba(42, 255, 0, 1)",
"classID": 7
},
{
"id": "node9",
"label": "React",
"color": "rgba(76, 73, 245, 1)",
"classID": 8
},
{ "id": "node10", "label": "模块化", "color": "#4682B4", "classID": 9 },
{ "id": "node11", "label": "webpack", "color": "#4682B4", "classID": 10 },
{ "id": "node12", "label": "vite", "color": "#4682B4", "classID": 11 },
{
"id": "node13",
"label": "uniapp",
"color": "rgba(77, 255, 0, 1)",
"classID": 12
},
{
"id": "node14",
"label": "element",
"color": "rgba(33, 162, 255, 1)",
"classID": 13
},
{
"id": "node15",
"label": "web3",
"color": "rgba(255, 0, 251, 1)",
"classID": 14
},
{
"id": "node16",
"label": "webGl",
"color": "rgba(208, 0, 249, 1)",
"classID": 15
},
{
"id": "node17",
"label": "three",
"color": "rgba(225, 0, 255, 1)",
"classID": 16
},
{
"id": "node18",
"label": "后端",
"color": "rgba(0, 229, 255, 1)",
"classID": 17
},
{
"id": "node19",
"label": "java",
"color": "rgba(237, 229, 85, 1)",
"classID": 18
},
{
"id": "node20",
"label": "PHP",
"color": "rgba(195, 206, 215, 1)",
"classID": 19
},
{
"id": "node21",
"label": "Go",
"color": "rgba(255, 0, 0, 1)",
"classID": 20
},
{
"id": "node22",
"label": "Python",
"color": "rgba(109, 238, 180, 1)",
"classID": 21
},
{ "id": "node23", "label": "MySQL", "color": "#4682B4", "classID": 22 },
{
"id": "node24",
"label": "人工智能",
"color": "rgba(180, 5, 255, 1)",
"classID": 23
},
{
"id": "node25",
"label": "python",
"color": "rgba(255, 8, 8, 1)",
"classID": 24
},
{
"id": "node26",
"label": "AI模型",
"color": "rgba(10, 138, 244, 1)",
"classID": 25
},
{
"id": "node27",
"label": "Spring Framework",
"color": "rgba(242, 238, 14, 1)",
"classID": 26
},
{
"id": "node28",
"label": "Hibernate",
"color": "rgba(242, 238, 14, 1)",
"classID": 27
},
{
"id": "node29",
"label": "Spring MVC",
"color": "rgba(242, 238, 14, 1)",
"classID": 28
},
{
"id": "node30",
"label": "Gin",
"color": "rgba(255, 0, 0, 1)",
"classID": 29
},
{
"id": "node31",
"label": "Echo",
"color": "rgba(255, 0, 0, 1)",
"classID": 30
},
{
"id": "node32",
"label": "Beego",
"color": "rgba(255, 8, 0, 1)",
"classID": 31
},
{
"id": "node33",
"label": "Laravel",
"color": "rgba(200, 209, 217, 1)",
"classID": 32
},
{
"id": "node34",
"label": "Symfony",
"color": "rgba(182, 194, 204, 1)",
"classID": 33
},
{
"id": "node35",
"label": "CodeIgniter",
"color": "rgba(188, 197, 204, 1)",
"classID": 34
},
{
"id": "node36",
"label": "Django",
"color": "rgba(36, 245, 144, 1)",
"classID": 35
},
{
"id": "node37",
"label": "Flask",
"color": "rgba(41, 244, 176, 1)",
"classID": 36
},
{
"id": "node38",
"label": "FastAPI",
"color": "rgba(58, 244, 142, 1)",
"classID": 37
}
],
"links": [
{ "source": "node2", "target": "node3", "label": "", "classID": 0 },
{ "source": "node2", "target": "node5", "label": "", "classID": 1 },
{ "source": "node2", "target": "node4", "label": "", "classID": 2 },
{ "source": "node1", "target": "node2", "label": "前端方向", "classID": 3 },
{ "source": "node5", "target": "node6", "label": "", "classID": 4 },
{ "source": "node5", "target": "node7", "label": "", "classID": 5 },
{ "source": "node3", "target": "node8", "label": "", "classID": 6 },
{ "source": "node3", "target": "node9", "label": "", "classID": 7 },
{ "source": "node3", "target": "node10", "label": "", "classID": 8 },
{ "source": "node10", "target": "node11", "label": "", "classID": 9 },
{ "source": "node10", "target": "node12", "label": "", "classID": 10 },
{ "source": "node11", "target": "node9", "label": "", "classID": 11 },
{ "source": "node11", "target": "node8", "label": "", "classID": 12 },
{ "source": "node12", "target": "node8", "label": "", "classID": 13 },
{ "source": "node8", "target": "node13", "label": "", "classID": 14 },
{ "source": "node8", "target": "node14", "label": "", "classID": 15 },
{ "source": "node11", "target": "node13", "label": "", "classID": 16 },
{ "source": "node12", "target": "node13", "label": "", "classID": 17 },
{ "source": "node2", "target": "node15", "label": "", "classID": 18 },
{ "source": "node15", "target": "node16", "label": "", "classID": 19 },
{ "source": "node16", "target": "node17", "label": "", "classID": 20 },
{ "source": "node1", "target": "node1", "label": "", "classID": 21 },
{
"source": "node1",
"target": "node18",
"label": "后端方向",
"classID": 22
},
{ "source": "node18", "target": "node21", "label": "", "classID": 23 },
{ "source": "node18", "target": "node20", "label": "", "classID": 24 },
{ "source": "node18", "target": "node19", "label": "", "classID": 25 },
{ "source": "node18", "target": "node22", "label": "", "classID": 26 },
{ "source": "node22", "target": "node23", "label": "", "classID": 27 },
{ "source": "node19", "target": "node23", "label": "", "classID": 28 },
{ "source": "node20", "target": "node23", "label": "", "classID": 29 },
{ "source": "node21", "target": "node23", "label": "", "classID": 30 },
{
"source": "node1",
"target": "node24",
"label": "人工智能方向",
"classID": 31
},
{ "source": "node24", "target": "node25", "label": "", "classID": 32 },
{ "source": "node24", "target": "node26", "label": "", "classID": 33 },
{ "source": "node12", "target": "node9", "label": "", "classID": 34 },
{ "source": "node19", "target": "node27", "label": "", "classID": 35 },
{ "source": "node19", "target": "node28", "label": "", "classID": 36 },
{ "source": "node19", "target": "node29", "label": "", "classID": 37 },
{ "source": "node21", "target": "node30", "label": "", "classID": 38 },
{ "source": "node21", "target": "node31", "label": "", "classID": 39 },
{ "source": "node21", "target": "node32", "label": "", "classID": 40 },
{ "source": "node20", "target": "node33", "label": "", "classID": 41 },
{ "source": "node20", "target": "node34", "label": "", "classID": 42 },
{ "source": "node20", "target": "node35", "label": "", "classID": 43 },
{ "source": "node22", "target": "node36", "label": "", "classID": 44 },
{ "source": "node22", "target": "node37", "label": "", "classID": 45 },
{ "source": "node22", "target": "node38", "label": "", "classID": 46 }
]
}

@ -0,0 +1,29 @@
/**
*
* @returns
*/
export function getLearnInfoApi() {
return new Promise((resolve) => {
const data = [
{ name: '总学时', value: '36学时' },
{ name: '已分配学时', value: '20学时' },
{ name: '总学分', value: '2.0分' },
{ name: '章节数', value: '10章' },
{ name: '未分配学时', value: '' },
{ name: '知识点总数', value: '33个' },
]
setTimeout(() => resolve(data), 500 * Math.random())
})
}
/**
*
* @returns
*/
export function getOrogramObjectiveApi() {
return new Promise((resolve) => {
const data = {
con: '内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容',
}
setTimeout(() => resolve(data), 500 * Math.random())
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

@ -22,6 +22,16 @@ export const constantRoute: any = [
},
],
},
{
path: '/knowledgeAtlas',
component: () => import('@/views/course/knowledgeAtlas.vue'),
name: 'knowledgeAtlas',
meta: {
title: '知识图谱',
hidden: true,
icon: 'Notebook',
},
},
{
path: '/curriculumCenter',
redirect: '/curriculumCenter/basicCourseInformation',
@ -258,7 +268,8 @@ export const constantRoute: any = [
},
{
path: '/portal/LearningPathRecommendations',
component: () => import('@/views/portal/LearningPathRecommendations.vue'),
component: () =>
import('@/views/portal/LearningPathRecommendations.vue'),
name: 'LearningPathRecommendations',
meta: {
title: '学习路径推荐',

@ -0,0 +1,153 @@
<template>
<div id="3d-graph"></div>
</template>
<script lang="ts" setup>
// import { useRouter } from 'vue-router'
import { onMounted, ref,reactive,watch } from 'vue'
import ForceGraph3D from '3d-force-graph'
//@ts-ignore
import { CSS2DRenderer, CSS2DObject,} from 'three/examples/jsm/renderers/CSS2DRenderer.js'
//@ts-ignore
import SpriteText from '../spritetext.js'
// const $router = useRouter()
// const jsonData = ref(null)
let Graph:any = reactive({})
const props = defineProps({
width:{
type:Number,
default:window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
},
height:{
type:Number,
default:window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
}
})
watch(() => props.width, ne => Graph.width(ne))
watch(() => props.height, ne => Graph.height(ne))
const dom = ref(null)
onMounted(() => {
Graph = ForceGraph3D({
extraRenderers: [new CSS2DRenderer()],
})(document.getElementById('3d-graph') as HTMLElement)
.jsonUrl('../../../public/data.json')
// .nodeAutoColorBy('group')
.nodeThreeObject((node: any) => {
const nodeEl = document.createElement('div')
nodeEl.textContent = node.label
nodeEl.style.color = '#333333'
nodeEl.style.borderRadius = '50%'
// console.log(node, 111, Graph.graphData().nodes)
return new CSS2DObject(nodeEl)
})
.linkLabel((link:any) => link.label) //
.linkWidth(0.8)
.linkHoverPrecision(0.5) //
.linkColor(() => '#dd92fd') // 线
.backgroundColor('#f5f6fd')
.width(props.width)
.height(props.height)
.linkThreeObjectExtend(true)
.nodeColor((node: any) => {
return node.color
})
.nodeRelSize(7) // 4
.nodeResolution(20)
.linkDirectionalArrowLength(3) // 线3
.linkDirectionalArrowRelPos(1) // 线线
.nodeThreeObjectExtend(true)
.onNodeClick((node: any) => {
// Aim at node from outside it
//
const targetDistance = 200 //
//
const distRatio = 1 + targetDistance / Math.hypot(node.x, node.y, node.z)
const newPos = {
x: node.x * distRatio,
y: node.y * distRatio,
z: node.z * distRatio,
}
//
if (node.x === 0 && node.y === 0 && node.z === 0) {
newPos.z = targetDistance // z
}
//
//@ts-ignore
Graph.cameraPosition(
newPos, //
node, //
3000, //
)
//
//@ts-ignore
const graphData = Graph.graphData()
// 线线
graphData.links.forEach((link: any) => {
// console.log(link);
if (link.source.id === node.id || link.target.id === node.id) {
setLabel()
// 线
// link.color = '#FF0000' // 线
//@ts-ignore
Graph.linkColor((item: any): any => {
if (item.source.id === node.id || item.target.id === node.id) {
return 'red'
} else {
return '#dd92fd'
}
})
} else {
// Graph.linkColor(() => '#a4c7fe') // 线
}
})
//
//@ts-ignore
Graph.graphData(graphData)
})
dom.value = document.querySelector('canvas') as any
})
const setLabel = () => {
//@ts-ignore
Graph.linkThreeObject((link:any) => {
// extend link with text sprite
const sprite = new SpriteText(`${link.label}`);
sprite.color = '#ccc';
sprite.textHeight = 1.5;
return sprite;
})
//@ts-ignore
Graph.linkPositionUpdate((sprite, { start, end }) => {
//@ts-ignore
const middlePos = Object.assign(...['x', 'y', 'z'].map(c => ({
//@ts-ignore
[c]: start[c] + (end[c] - start[c]) / 2 // calc middle point
})));
// Position sprite
Object.assign(sprite.position, middlePos);
});
//@ts-ignore
Graph.d3Force('charge').strength(-120);
}
// const goToEditAtlas = () => {
// console.log(jsonData.value)
// $router.push({ name: 'EditAtlas', params: { id: 123 } })
// }
</script>
<style lang='scss' scoped>
</style>

@ -0,0 +1,275 @@
<template>
<div class="fold-info-ui">
<div class="demo-collapse">
<el-collapse class="collapse" v-model="activeNames" @change="handleChange">
<el-collapse-item v-for="item in 3" :key="item" :name="item">
<template #title>
<div class="title">
<div class="icon">
<img src="@/assets/images/minus-circle-filled.png" alt="">
</div>
<div class="index">01 /</div>
<div class="text">基本概念</div>
<div class="title-tag-box">
<div class="my-tag" type="warning">32学时</div>
<div class="my-tag">本章资源</div>
</div>
</div>
</template>
<template #default>
<div class="main-box">
<div class="left"></div>
<div class="right">
<div class="structure-item">
<div class="structure-item-titile-box" v-for="item in 3" :key="item">
<div class="titile-box-titile">
<div class="sequence">5</div>
<div class="tit-box">
<div class="tit-box-left">1.1 基本概念1</div>
<div class="tit-box-right my-tag">资源</div>
<div class="tit-box-edit">
<button class="my-button">编辑</button>
</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 props = defineProps(['foldInfoData'])
console.log(props.foldInfoData, '可折叠基本概念接口数据');
const activeNames = ref([1])
const handleChange = (val: string[]) => {
console.log(val)
}
</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;
background: #fff;
.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: 32px;
color: #333333;
margin-right: 15px;
}
.text {
font-weight: bold;
font-size: 24px;
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: 20px;
color: #000000;
}
// .tit-box-right {}
.tit-box-edit {
margin-left: auto;
margin-right: 72px;
}
}
}
.titile-box-knowledge {
display: flex;
margin-left: 33px;
&>* {
margin-left: 38px;
border: 1px solid #0052D9;
font-weight: bold;
font-size: 12px;
color: #0052D9;
&:first-child {
margin-left: 0;
}
}
}
}
}
}
}
}
// ===================
.my-tag {
padding: 2px 8px;
font-weight: 400;
font-size: 12px;
color: #0052D9;
line-height: 20px;
background: #F2F3FF;
cursor: pointer;
border-radius: 3px;
}
[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%);
}
}
}
</style>

@ -0,0 +1,5 @@
export { default as theoryUi } from './theoryUi.vue'
export { default as objectiveUi } from './objectiveUi.vue'
export { default as atlasUi } from '../atlasUi.vue'
export { default as lookResourceUi } from './lookResourceUi.vue'
export { default as foldInfoUi } from './foldInfoUi.vue'

@ -0,0 +1,195 @@
<template>
<div class="look-resource-ui" ref="atlas">
<div class="head">
<div class="log"></div>
<div class="text">查看资源</div>
</div>
<div class="con" v-for="item in 5" :key="item">
<div class="chapter-box">
<div class="left">第一章</div>
<div class="right">第二小节</div>
</div>
<div class="detailedness-chapter-box">
<div class="left">
<div class="vertical-dotted-line"></div>
</div>
<div class="right">
<div class="top">知识点</div>
<div class="center">知识点详情知识点内容</div>
<div class="bottom">
<div class="img-box"></div>
<div class="info-box">
<div class="text-name">知识点清单.pdf</div>
<div class="text-size">15M</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
const props = defineProps(['lookResource'])
console.log(props.lookResource, '查看资源接口数据');
</script>
<style lang="scss">
.look-resource-ui {
$padding-left: 18px;
width: 100%;
height: 100%;
background-color: #fff;
padding: 24px 22px 0 $padding-left;
display: flex;
flex-direction: column;
.head {
$height: 30px;
display: flex;
margin-bottom: 32px;
.log {
width: $height;
height: $height;
background: #4782FF;
border-radius: 4px;
margin-right: 12px;
}
.text {
font-weight: bold;
font-size: 20px;
color: #333333;
line-height: $height;
}
}
.con {
&>* {
display: flex;
}
$width: 51px;
.chapter-box {
$line-height: 22px;
margin-bottom: 12px;
.left {
min-width: $width;
height: 22px;
margin-right: 16px;
font-weight: 400;
font-size: 16px;
color: #333333;
line-height: $line-height;
}
.right {
line-height: $line-height;
font-weight: 400;
font-size: 14px;
color: #9A9A9A;
}
}
.detailedness-chapter-box {
.left {
width: $width;
.vertical-dotted-line {
height: 168px;
margin-left: calc(53px - $padding-left);
border-left: 1px dotted #D9D9D9;
}
}
.right {
display: flex;
flex-direction: column;
&>* {
margin-bottom: 15px;
}
&>*:last-child {
margin-bottom: 0;
}
.top {
$height: 17px;
width: 53px;
height: $height;
line-height: $height;
text-align: center;
background: #5EE183;
border-radius: 2px;
font-weight: 400;
font-size: 12px;
color: #FFFFFF;
}
.center {
$height: 22px;
height: $height;
line-height: $height;
font-weight: 400;
font-size: 16px;
color: #333333;
}
.bottom {
width: 243px;
height: 84px;
background: rgba(255, 255, 255, 0.5);
border-radius: 4px;
border: 1px solid #D9D9D9;
cursor: pointer;
transition: .3s;
padding: 17px 0 17px 20px;
display: flex;
&:hover {
border: 1px solid #6997fa;
}
.img-box {
width: 42px;
height: 50px;
background: #4D4D4D;
margin-right: 23px;
}
.info-box {
display: flex;
flex-direction: column;
$height: 22px;
&>* {
height: $height;
line-height: $height;
}
.text-name {
font-weight: 400;
font-size: 12px;
color: #333333;
margin-bottom: 6px;
}
.text-size {
font-weight: 400;
font-size: 12px;
color: #9A9A9A;
}
}
}
}
}
}
}
</style>

@ -0,0 +1,96 @@
<template>
<div class="objective-ui">
<div class="container">
<div class="title">课程目标</div>
<button class="more btn">了解更多</button>
<div class="content">
{{ orogramObjective.con }}
</div>
</div>
</div>
</template>
<script lang="ts" setup>
defineProps(['orogramObjective'])
</script>
<style lang="scss">
.objective-ui {
$border-width: 3px;
$line-height: 17px;
width: 100%;
height: 100%;
background: #FFFFFF;
border: $border-width solid #EDEDED;
padding: calc(16px - $border-width) 0 $line-height 66px;
button {
all: initial;
}
.btn {
$background-color: #F2F3FF;
width: 64px;
height: 18px;
line-height: 18px;
text-align: center;
background-color: $background-color;
border-radius: 45px;
font-weight: 400;
font-size: 12px;
color: #0052D9;
cursor: pointer;
&:hover {
background: darken($background-color, 5%);
}
&:active {
background: darken($background-color, 10%);
}
&:disabled {
background: lighten($background-color, 20%);
}
}
.container {
$row-height: 23px;
height: 100%;
width: 443px;
display: grid;
grid-template-columns: repeat(2, auto);
grid-template-rows: $row-height 1fr;
row-gap: 25px;
.title {
font-weight: bold;
font-size: 20px;
color: #333333;
line-height: $row-height;
text-align: left;
}
.more {
justify-self: end;
}
.content {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
grid-column: 1/3;
font-weight: 400;
font-size: 14px;
color: #999999;
line-height: $line-height;
text-align: left;
}
}
}
</style>

@ -0,0 +1,116 @@
<template>
<div class="theory-ui">
<div class="container">
<div class="top">
<div class="title">计算机导论</div>
<button class="my-button">导入</button>
<button class="my-button">编辑</button>
</div>
<div class="bottom">
<div class="text-container" v-for="item, index in props.learnInfo" :key="index">
<div class="text-name">{{ addUnit(item.name, ':') }}</div>
<div class="text-value">{{ item.value }}</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
function addUnit(viewStr: string, unit: string): string {
if (viewStr) return `${viewStr}${unit}`
else return ''
}
const props = defineProps(['learnInfo'])
</script>
<style lang="scss">
.theory-ui {
width: 100%;
height: 100%;
display: flex;
background-color: #fff;
.container {
$border-width: 3px;
flex-grow: 1;
border: $border-width solid #EDEDED;
padding-top: calc(10px - $border-width);
padding-left: 61px;
display: flex;
flex-direction: column;
.top {
display: flex;
margin-top: 30px;
margin-bottom: 46px;
.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%);
}
}
&>*:first-child {
margin-right: 82px;
}
&>*:last-child {
margin-left: 20px;
}
.title {
width: 208px;
height: 34px;
font-size: 36px;
color: #5577FF;
}
}
.bottom {
display: grid;
grid-template-columns: repeat(2, 1fr);
row-gap: 15px;
.text-container {
font-weight: 400;
font-size: 15px;
color: #333333;
width: 100%;
height: 20px;
display: flex;
.text-name {
width: 93px;
}
}
}
}
}
</style>

@ -0,0 +1,50 @@
<template>
<div class="atlas-ui" ref="atlas">
<professionalProfile v-bind="atlasAttribute" />
<div class="on-off" @click="()=>$emit('hadRouter')">
<el-icon>
<Right />
</el-icon>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue'
import professionalProfile from './KnowledgeGraph.vue'
const $emit = defineEmits(['hadRouter'])
const atlas = ref(null)
const atlasAttribute = reactive({ width: 0, height: 0 })
onMounted(() => {
const el: HTMLElement = atlas.value!
const width = +getComputedStyle(el).width.slice(0, -2)
const height = +getComputedStyle(el).height.slice(0, -2)
atlasAttribute.width = width
atlasAttribute.height = height
})
</script>
<style lang="scss">
.atlas-ui {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
&:hover .on-off {
transform: translate(0%);
padding-right: 10px;
}
.on-off {
position: absolute;
top: 0;
right: 0;
cursor: pointer;
padding-top: 5px;
transform: translate(100%);
transition: all .3s;
}
}
</style>

@ -1,14 +1,150 @@
<template>
<div>
课程章节
<div class="course-chapters">
<div class="flex-layout">
<div class="flex-left">
<div class="left" ref="atlasCon">
<!-- 知识图谱 -->
<atlas-ui @hadRouter="handle" />
</div>
<div class="right-top radius-10 vertical-line">
<!-- 计算机导论 -->
<theory-ui :learnInfo="learnInfo" />
</div>
<div class="right-bottom radius-10 vertical-line">
<!-- 课程目标 -->
<objective-ui :orogramObjective="orogramObjective" />
</div>
<div class="bottom">
<!-- 可折叠基本概念 -->
<fold-info-ui :foldInfoData="foldInfoData"/>
</div>
</div>
<div class="flex_right">
<div class="top-container">
<img src="" alt="">
</div>
<div class="bottom-container radius-10">
<!-- 查看资源 -->
<look-resource-ui :lookResource="lookResource"/>
</div>
</div>
</div>
</div>
</template>
<script lang='ts' setup>
import { } from 'vue'
import { getLearnInfoApi, getOrogramObjectiveApi } from '@/api/courseChaptersApi'
import { theoryUi, objectiveUi, atlasUi, lookResourceUi,foldInfoUi } from './components/KnowledgeGraphUi/index'
import { reactive } from 'vue'
import { useRouter } from 'vue-router'
const $router = useRouter()
// atlas-ui
const handle = () => $router.push('/knowledgeAtlas')
// theory-ui
const learnInfo: object[] = reactive([])
getLearnInfoApi().then(res => learnInfo.push(...res as object[]))
// objective-ui
const orogramObjective: any = reactive({})
getOrogramObjectiveApi().then(res => Object.assign(orogramObjective, res))
// look-resource-ui
const lookResource:any = reactive({})
// fold-info-ui
const foldInfoData:any = reactive({})
</script>
<style lang='scss' scoped>
.course-chapters {
width: 100%;
height: 100%;
position: relative;
background: #F2F7FB;
&,
* {
box-sizing: border-box;
}
$right-box-width: 555px;
.radius-10 {
border-radius: 10px;
overflow: hidden;
}
.vertical-line {
position: relative;
&::before {
content: '';
display: block;
position: absolute;
height: 100%;
width: 12px;
background-color: #2147FB;
}
}
.flex-layout {
display: flex;
justify-content: space-between;
align-items: flex-start;
.flex-left {
display: grid;
grid-template-columns: repeat(2, auto);
width: 1243px;
.left {
grid-row: 1/3;
width: 664px;
height: 424px;
}
.right-top {
justify-self: end;
width: $right-box-width;
height: 243px;
}
.right-bottom {
justify-self: end;
align-self: end;
width: $right-box-width;
height: 168px;
}
.bottom {
margin-top: 15px;
grid-column: 1/3;
width: 100%;
min-height: 521px;
}
}
.flex_right {
width: 355px;
.top-container {
height: 231px;
margin-bottom: 23px;
img{
width: 100%;
height: 100%;
}
}
.bottom-container {
min-height: 945px;
}
}
}
}
</style>

@ -0,0 +1,14 @@
<template>
<div class="knowledge-atlas">
<atlas-ui @hadRouter="handle" />
</div>
</template>
<script lang='ts' setup>
import { atlasUi } from './components/KnowledgeGraphUi/index'
import { useRouter } from 'vue-router';
const $router = useRouter()
const handle = () => $router.push('/curriculumCenter/courseChapters')
</script>
<style lang='scss' scoped></style>

@ -0,0 +1,468 @@
import { LinearFilter, Sprite, SpriteMaterial, SRGBColorSpace, Texture } from 'three';
function _callSuper(t, o, e) {
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
}
function _isNativeReflectConstruct() {
try {
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
} catch (t) {}
return (_isNativeReflectConstruct = function () {
return !!t;
})();
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) return;
f = !1;
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : String(i);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
Object.defineProperty(subClass, "prototype", {
writable: false
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
} else if (call !== void 0) {
throw new TypeError("Derived constructors may only return object or undefined");
}
return _assertThisInitialized(self);
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var three = typeof window !== 'undefined' && window.THREE ? window.THREE // Prefer consumption from global THREE, if exists
: {
LinearFilter: LinearFilter,
Sprite: Sprite,
SpriteMaterial: SpriteMaterial,
SRGBColorSpace: SRGBColorSpace,
Texture: Texture
};
var _default = /*#__PURE__*/function (_three$Sprite) {
_inherits(_default, _three$Sprite);
function _default() {
var _this;
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var textHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
var color = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'rgba(255, 255, 255, 1)';
_classCallCheck(this, _default);
_this = _callSuper(this, _default, [new three.SpriteMaterial()]);
_this._text = "".concat(text);
_this._textHeight = textHeight;
_this._color = color;
_this._backgroundColor = false; // no background color
_this._padding = 0;
_this._borderWidth = 0;
_this._borderRadius = 0;
_this._borderColor = 'white';
_this._strokeWidth = 0;
_this._strokeColor = 'white';
_this._fontFace = 'system-ui';
_this._fontSize = 90; // defines text resolution
_this._fontWeight = 'normal';
_this._canvas = document.createElement('canvas');
_this._genCanvas();
return _this;
}
_createClass(_default, [{
key: "text",
get: function get() {
return this._text;
},
set: function set(text) {
this._text = text;
this._genCanvas();
}
}, {
key: "textHeight",
get: function get() {
return this._textHeight;
},
set: function set(textHeight) {
this._textHeight = textHeight;
this._genCanvas();
}
}, {
key: "color",
get: function get() {
return this._color;
},
set: function set(color) {
this._color = color;
this._genCanvas();
}
}, {
key: "backgroundColor",
get: function get() {
return this._backgroundColor;
},
set: function set(color) {
this._backgroundColor = color;
this._genCanvas();
}
}, {
key: "padding",
get: function get() {
return this._padding;
},
set: function set(padding) {
this._padding = padding;
this._genCanvas();
}
}, {
key: "borderWidth",
get: function get() {
return this._borderWidth;
},
set: function set(borderWidth) {
this._borderWidth = borderWidth;
this._genCanvas();
}
}, {
key: "borderRadius",
get: function get() {
return this._borderRadius;
},
set: function set(borderRadius) {
this._borderRadius = borderRadius;
this._genCanvas();
}
}, {
key: "borderColor",
get: function get() {
return this._borderColor;
},
set: function set(borderColor) {
this._borderColor = borderColor;
this._genCanvas();
}
}, {
key: "fontFace",
get: function get() {
return this._fontFace;
},
set: function set(fontFace) {
this._fontFace = fontFace;
this._genCanvas();
}
}, {
key: "fontSize",
get: function get() {
return this._fontSize;
},
set: function set(fontSize) {
this._fontSize = fontSize;
this._genCanvas();
}
}, {
key: "fontWeight",
get: function get() {
return this._fontWeight;
},
set: function set(fontWeight) {
this._fontWeight = fontWeight;
this._genCanvas();
}
}, {
key: "strokeWidth",
get: function get() {
return this._strokeWidth;
},
set: function set(strokeWidth) {
this._strokeWidth = strokeWidth;
this._genCanvas();
}
}, {
key: "strokeColor",
get: function get() {
return this._strokeColor;
},
set: function set(strokeColor) {
this._strokeColor = strokeColor;
this._genCanvas();
}
}, {
key: "_genCanvas",
value: function _genCanvas() {
var _this2 = this;
var canvas = this._canvas;
var ctx = canvas.getContext('2d');
var border = Array.isArray(this.borderWidth) ? this.borderWidth : [this.borderWidth, this.borderWidth]; // x,y border
var relBorder = border.map(function (b) {
return b * _this2.fontSize * 0.1;
}); // border in canvas units
var borderRadius = Array.isArray(this.borderRadius) ? this.borderRadius : [this.borderRadius, this.borderRadius, this.borderRadius, this.borderRadius]; // tl tr br bl corners
var relBorderRadius = borderRadius.map(function (b) {
return b * _this2.fontSize * 0.1;
}); // border radius in canvas units
var padding = Array.isArray(this.padding) ? this.padding : [this.padding, this.padding]; // x,y padding
var relPadding = padding.map(function (p) {
return p * _this2.fontSize * 0.1;
}); // padding in canvas units
var lines = this.text.split('\n');
var font = "".concat(this.fontWeight, " ").concat(this.fontSize, "px ").concat(this.fontFace);
ctx.font = font; // measure canvas with appropriate font
var innerWidth = Math.max.apply(Math, _toConsumableArray(lines.map(function (line) {
return ctx.measureText(line).width;
})));
var innerHeight = this.fontSize * lines.length;
canvas.width = innerWidth + relBorder[0] * 2 + relPadding[0] * 2;
canvas.height = innerHeight + relBorder[1] * 2 + relPadding[1] * 2;
// paint border
if (this.borderWidth) {
ctx.strokeStyle = this.borderColor;
if (relBorder[0]) {
// left + right borders
var hb = relBorder[0] / 2;
ctx.lineWidth = relBorder[0];
ctx.beginPath();
ctx.moveTo(hb, relBorderRadius[0]);
ctx.lineTo(hb, canvas.height - relBorderRadius[3]);
ctx.moveTo(canvas.width - hb, relBorderRadius[1]);
ctx.lineTo(canvas.width - hb, canvas.height - relBorderRadius[2]);
ctx.stroke();
}
if (relBorder[1]) {
// top + bottom borders
var _hb = relBorder[1] / 2;
ctx.lineWidth = relBorder[1];
ctx.beginPath();
ctx.moveTo(Math.max(relBorder[0], relBorderRadius[0]), _hb);
ctx.lineTo(canvas.width - Math.max(relBorder[0], relBorderRadius[1]), _hb);
ctx.moveTo(Math.max(relBorder[0], relBorderRadius[3]), canvas.height - _hb);
ctx.lineTo(canvas.width - Math.max(relBorder[0], relBorderRadius[2]), canvas.height - _hb);
ctx.stroke();
}
if (this.borderRadius) {
// strike rounded corners
var cornerWidth = Math.max.apply(Math, _toConsumableArray(relBorder));
var _hb2 = cornerWidth / 2;
ctx.lineWidth = cornerWidth;
ctx.beginPath();
[!!relBorderRadius[0] && [relBorderRadius[0], _hb2, _hb2, relBorderRadius[0]], !!relBorderRadius[1] && [canvas.width - relBorderRadius[1], canvas.width - _hb2, _hb2, relBorderRadius[1]], !!relBorderRadius[2] && [canvas.width - relBorderRadius[2], canvas.width - _hb2, canvas.height - _hb2, canvas.height - relBorderRadius[2]], !!relBorderRadius[3] && [relBorderRadius[3], _hb2, canvas.height - _hb2, canvas.height - relBorderRadius[3]]].filter(function (d) {
return d;
}).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 4),
x0 = _ref2[0],
x1 = _ref2[1],
y0 = _ref2[2],
y1 = _ref2[3];
ctx.moveTo(x0, y0);
ctx.quadraticCurveTo(x1, y0, x1, y1);
});
ctx.stroke();
}
}
// paint background
if (this.backgroundColor) {
ctx.fillStyle = this.backgroundColor;
if (!this.borderRadius) {
ctx.fillRect(relBorder[0], relBorder[1], canvas.width - relBorder[0] * 2, canvas.height - relBorder[1] * 2);
} else {
// fill with rounded corners
ctx.beginPath();
ctx.moveTo(relBorder[0], relBorderRadius[0]);
[[relBorder[0], relBorderRadius[0], canvas.width - relBorderRadius[1], relBorder[1], relBorder[1], relBorder[1]],
// t
[canvas.width - relBorder[0], canvas.width - relBorder[0], canvas.width - relBorder[0], relBorder[1], relBorderRadius[1], canvas.height - relBorderRadius[2]],
// r
[canvas.width - relBorder[0], canvas.width - relBorderRadius[2], relBorderRadius[3], canvas.height - relBorder[1], canvas.height - relBorder[1], canvas.height - relBorder[1]],
// b
[relBorder[0], relBorder[0], relBorder[0], canvas.height - relBorder[1], canvas.height - relBorderRadius[3], relBorderRadius[0]] // t
].forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 6),
x0 = _ref4[0],
x1 = _ref4[1],
x2 = _ref4[2],
y0 = _ref4[3],
y1 = _ref4[4],
y2 = _ref4[5];
ctx.quadraticCurveTo(x0, y0, x1, y1);
ctx.lineTo(x2, y2);
});
ctx.closePath();
ctx.fill();
}
}
ctx.translate.apply(ctx, _toConsumableArray(relBorder));
ctx.translate.apply(ctx, _toConsumableArray(relPadding));
// paint text
ctx.font = font; // Set font again after canvas is resized, as context properties are reset
ctx.fillStyle = this.color;
ctx.textBaseline = 'bottom';
var drawTextStroke = this.strokeWidth > 0;
if (drawTextStroke) {
ctx.lineWidth = this.strokeWidth * this.fontSize / 10;
ctx.strokeStyle = this.strokeColor;
}
lines.forEach(function (line, index) {
var lineX = (innerWidth - ctx.measureText(line).width) / 2;
var lineY = (index + 1) * _this2.fontSize;
drawTextStroke && ctx.strokeText(line, lineX, lineY);
ctx.fillText(line, lineX, lineY);
});
// Inject canvas into sprite
if (this.material.map) this.material.map.dispose(); // gc previous texture
var texture = this.material.map = new three.Texture(canvas);
texture.minFilter = three.LinearFilter;
texture.colorSpace = three.SRGBColorSpace;
texture.needsUpdate = true;
var yScale = this.textHeight * lines.length + border[1] * 2 + padding[1] * 2;
this.scale.set(yScale * canvas.width / canvas.height, yScale, 0);
}
}, {
key: "clone",
value: function clone() {
return new this.constructor(this.text, this.textHeight, this.color).copy(this);
}
}, {
key: "copy",
value: function copy(source) {
three.Sprite.prototype.copy.call(this, source);
this.color = source.color;
this.backgroundColor = source.backgroundColor;
this.padding = source.padding;
this.borderWidth = source.borderWidth;
this.borderColor = source.borderColor;
this.fontFace = source.fontFace;
this.fontSize = source.fontSize;
this.fontWeight = source.fontWeight;
this.strokeWidth = source.strokeWidth;
this.strokeColor = source.strokeColor;
return this;
}
}]);
return _default;
}(three.Sprite);
export { _default as default };
Loading…
Cancel
Save