Compare commits
No commits in common. 'c4b4d35b42bb0b378c34901362b290005ef95f69' and '85443cbfa7cbbb50766447e54b4f0789f957fc32' have entirely different histories.
c4b4d35b42
...
85443cbfa7
13 changed files with 368 additions and 888 deletions
@ -1,411 +0,0 @@ |
|||||||
<template> |
|
||||||
<div :id="'3d-graph' + props.index"></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 '../../professionalProfile/spritetext' |
|
||||||
// const $router = useRouter() |
|
||||||
// const jsonData = ref(null) |
|
||||||
import { getCourseAtlas ,getCourseDepth,getCourseType} from '@/api/course' |
|
||||||
let Graph = 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, |
|
||||||
}, |
|
||||||
index: { |
|
||||||
type: String, |
|
||||||
required: true, |
|
||||||
}, |
|
||||||
id: { |
|
||||||
type: String || Number, |
|
||||||
required: true, |
|
||||||
}, |
|
||||||
checkList:{ |
|
||||||
|
|
||||||
}, |
|
||||||
radio1:{} |
|
||||||
}) |
|
||||||
watch(() => props.radio1,(newVal:any) => { |
|
||||||
console.log(newVal); |
|
||||||
getCourseDepthEvent(newVal) |
|
||||||
|
|
||||||
} ) |
|
||||||
watch(() => props.checkList,(newVal:any) => { |
|
||||||
console.log(newVal); |
|
||||||
getCourseTypeEvent(newVal) |
|
||||||
|
|
||||||
} ) |
|
||||||
const emits = defineEmits(['clickGraph']) |
|
||||||
|
|
||||||
const data = ref({}) |
|
||||||
const dom = ref(null) |
|
||||||
const getCourseDepthEvent = async(depth:any) => { |
|
||||||
const res = await getCourseDepth({courseId:'719f91586a64413898253c5b7d046fd8',depth}) |
|
||||||
data.value = res.data |
|
||||||
const gData:any = { |
|
||||||
nodes: [... data.value.knowList], |
|
||||||
links: [... data.value.linksList], |
|
||||||
} |
|
||||||
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) |
|
||||||
|
|
||||||
!a.neighbors && (a.neighbors = []) |
|
||||||
!b.neighbors && (b.neighbors = []) |
|
||||||
a.neighbors.push(b) |
|
||||||
b.neighbors.push(a) |
|
||||||
|
|
||||||
!a.links && (a.links = []) |
|
||||||
!b.links && (b.links = []) |
|
||||||
a.links.push(link) |
|
||||||
b.links.push(link) |
|
||||||
}) |
|
||||||
|
|
||||||
const highlightNodes = new Set() |
|
||||||
const highlightLinks = new Set() |
|
||||||
let hoverNode:any = null |
|
||||||
|
|
||||||
Graph = ForceGraph3D({ |
|
||||||
extraRenderers: [new CSS2DRenderer()], |
|
||||||
})(document.getElementById('3d-graph' + props.index) as HTMLElement) |
|
||||||
.graphData(gData) |
|
||||||
// .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) |
|
||||||
.nodeRelSize(7) // 设置节点的相对大小为4 |
|
||||||
.nodeResolution(20) |
|
||||||
.linkDirectionalArrowLength(3) // 设置连接线箭头长度为3 |
|
||||||
.linkDirectionalArrowRelPos(1) // 设置连接线箭头位置为连接线末端 |
|
||||||
.nodeThreeObjectExtend(true) |
|
||||||
.nodeColor((node:any) => |
|
||||||
highlightNodes.has(node) |
|
||||||
? node === hoverNode |
|
||||||
? 'rgb(255,0,0,1)' |
|
||||||
: node.color |
|
||||||
: node.color, |
|
||||||
) |
|
||||||
.linkWidth((link) => (highlightLinks.has(link) ? 4 : 1)) |
|
||||||
.linkDirectionalParticles((link) => (highlightLinks.has(link) ? 4 : 0)) |
|
||||||
.linkDirectionalParticleWidth(4) |
|
||||||
.onNodeClick((node: any) => { |
|
||||||
console.log(node); |
|
||||||
emits('clickGraph',node.id) |
|
||||||
// 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) |
|
||||||
}) |
|
||||||
.onNodeHover((node:any) => { |
|
||||||
// no state change |
|
||||||
if ((!node && !highlightNodes.size) || (node && hoverNode === node)) |
|
||||||
return |
|
||||||
|
|
||||||
highlightNodes.clear() |
|
||||||
highlightLinks.clear() |
|
||||||
if (node && node.neighbors ) { |
|
||||||
highlightNodes.add(node) |
|
||||||
node.neighbors.forEach((neighbor:any) => highlightNodes.add(neighbor)) |
|
||||||
node.links.forEach((link :any) => highlightLinks.add(link)) |
|
||||||
} |
|
||||||
|
|
||||||
hoverNode = node || null |
|
||||||
|
|
||||||
updateHighlight() |
|
||||||
}) |
|
||||||
.onLinkHover((link :any) => { |
|
||||||
highlightNodes.clear() |
|
||||||
highlightLinks.clear() |
|
||||||
|
|
||||||
if (link) { |
|
||||||
highlightLinks.add(link) |
|
||||||
highlightNodes.add(link.source) |
|
||||||
highlightNodes.add(link.target) |
|
||||||
} |
|
||||||
|
|
||||||
updateHighlight() |
|
||||||
}) |
|
||||||
dom.value = document.querySelector('canvas') as any |
|
||||||
|
|
||||||
} |
|
||||||
const getCourseTypeEvent = async(type:any) => { |
|
||||||
const res = await getCourseType({courseId:'719f91586a64413898253c5b7d046fd8',type}) |
|
||||||
console.log(res); |
|
||||||
|
|
||||||
} |
|
||||||
onMounted(async () => { |
|
||||||
const res: any = await getCourseAtlas({ id: '719f91586a64413898253c5b7d046fd8' }) |
|
||||||
data.value = res.data |
|
||||||
// console.log(res,'res') |
|
||||||
const gData:any = { |
|
||||||
nodes: [... data.value.knowList], |
|
||||||
links: [... data.value.linksList], |
|
||||||
} |
|
||||||
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) |
|
||||||
|
|
||||||
!a.neighbors && (a.neighbors = []) |
|
||||||
!b.neighbors && (b.neighbors = []) |
|
||||||
a.neighbors.push(b) |
|
||||||
b.neighbors.push(a) |
|
||||||
|
|
||||||
!a.links && (a.links = []) |
|
||||||
!b.links && (b.links = []) |
|
||||||
a.links.push(link) |
|
||||||
b.links.push(link) |
|
||||||
}) |
|
||||||
|
|
||||||
const highlightNodes = new Set() |
|
||||||
const highlightLinks = new Set() |
|
||||||
let hoverNode:any = null |
|
||||||
|
|
||||||
Graph = ForceGraph3D({ |
|
||||||
extraRenderers: [new CSS2DRenderer()], |
|
||||||
})(document.getElementById('3d-graph' + props.index) as HTMLElement) |
|
||||||
.graphData(gData) |
|
||||||
// .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) |
|
||||||
.nodeRelSize(7) // 设置节点的相对大小为4 |
|
||||||
.nodeResolution(20) |
|
||||||
.linkDirectionalArrowLength(3) // 设置连接线箭头长度为3 |
|
||||||
.linkDirectionalArrowRelPos(1) // 设置连接线箭头位置为连接线末端 |
|
||||||
.nodeThreeObjectExtend(true) |
|
||||||
.nodeColor((node:any) => |
|
||||||
highlightNodes.has(node) |
|
||||||
? node === hoverNode |
|
||||||
? 'rgb(255,0,0,1)' |
|
||||||
: node.color |
|
||||||
: node.color, |
|
||||||
) |
|
||||||
.linkWidth((link) => (highlightLinks.has(link) ? 4 : 1)) |
|
||||||
.linkDirectionalParticles((link) => (highlightLinks.has(link) ? 4 : 0)) |
|
||||||
.linkDirectionalParticleWidth(4) |
|
||||||
.onNodeClick((node: any) => { |
|
||||||
console.log(node); |
|
||||||
emits('clickGraph',node.id) |
|
||||||
// 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) |
|
||||||
}) |
|
||||||
.onNodeHover((node:any) => { |
|
||||||
// no state change |
|
||||||
if ((!node && !highlightNodes.size) || (node && hoverNode === node)) |
|
||||||
return |
|
||||||
|
|
||||||
highlightNodes.clear() |
|
||||||
highlightLinks.clear() |
|
||||||
if (node && node.neighbors ) { |
|
||||||
highlightNodes.add(node) |
|
||||||
node.neighbors.forEach((neighbor:any) => highlightNodes.add(neighbor)) |
|
||||||
node.links.forEach((link :any) => highlightLinks.add(link)) |
|
||||||
} |
|
||||||
|
|
||||||
hoverNode = node || null |
|
||||||
|
|
||||||
updateHighlight() |
|
||||||
}) |
|
||||||
.onLinkHover((link :any) => { |
|
||||||
highlightNodes.clear() |
|
||||||
highlightLinks.clear() |
|
||||||
|
|
||||||
if (link) { |
|
||||||
highlightLinks.add(link) |
|
||||||
highlightNodes.add(link.source) |
|
||||||
highlightNodes.add(link.target) |
|
||||||
} |
|
||||||
|
|
||||||
updateHighlight() |
|
||||||
}) |
|
||||||
dom.value = document.querySelector('canvas') as any |
|
||||||
}) |
|
||||||
const updateHighlight = () => { |
|
||||||
// console.log(1111) |
|
||||||
|
|
||||||
// trigger update of highlighted objects in scene |
|
||||||
Graph.nodeColor(Graph.nodeColor()) |
|
||||||
.linkWidth(Graph.linkWidth()) |
|
||||||
.linkDirectionalParticles(Graph.linkDirectionalParticles()) |
|
||||||
} |
|
||||||
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> |
|
@ -1,14 +1,11 @@ |
|||||||
<template> |
<template> |
||||||
<div> |
<div>111</div> |
||||||
111 |
|
||||||
</div> |
|
||||||
</template> |
</template> |
||||||
|
|
||||||
<script lang='ts' setup> |
<script lang="ts" setup> |
||||||
import { onMounted, reactive, ref, toRefs, watch } from 'vue' |
import {} from 'vue' |
||||||
|
|
||||||
</script> |
</script> |
||||||
|
|
||||||
<style lang='scss' scoped> |
<style lang="scss" scoped> |
||||||
|
|
||||||
</style> |
</style> |
||||||
|
Loading…
Reference in new issue