|
@@ -1,4 +1,6 @@
|
|
|
import React, { useEffect, useRef, useCallback, useState } from 'react'
|
|
import React, { useEffect, useRef, useCallback, useState } from 'react'
|
|
|
|
|
+import { useSelector } from 'react-redux'
|
|
|
|
|
+import { RootState } from '@/store'
|
|
|
import styles from './index.module.scss'
|
|
import styles from './index.module.scss'
|
|
|
import Chart from './components/Chart'
|
|
import Chart from './components/Chart'
|
|
|
import Panel from './components/Panel'
|
|
import Panel from './components/Panel'
|
|
@@ -29,25 +31,27 @@ interface NodeStyleConfig {
|
|
|
lineColor: string
|
|
lineColor: string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const IS_PC = window.innerWidth > 1024
|
|
|
|
|
+
|
|
|
const getNodeStyleByDepth = (depth: number): NodeStyleConfig => {
|
|
const getNodeStyleByDepth = (depth: number): NodeStyleConfig => {
|
|
|
if (depth === 3) {
|
|
if (depth === 3) {
|
|
|
return {
|
|
return {
|
|
|
fontColor: '#FF9807',
|
|
fontColor: '#FF9807',
|
|
|
- fontSize: 16,
|
|
|
|
|
|
|
+ fontSize: IS_PC ? 8 : 16,
|
|
|
fontWeight: 'normal',
|
|
fontWeight: 'normal',
|
|
|
lineColor: '#FF9807'
|
|
lineColor: '#FF9807'
|
|
|
}
|
|
}
|
|
|
} else if (depth >= 4) {
|
|
} else if (depth >= 4) {
|
|
|
return {
|
|
return {
|
|
|
fontColor: '#D1C9B2',
|
|
fontColor: '#D1C9B2',
|
|
|
- fontSize: 14,
|
|
|
|
|
|
|
+ fontSize: IS_PC ? 6 : 14,
|
|
|
fontWeight: 'normal',
|
|
fontWeight: 'normal',
|
|
|
lineColor: '#D1C9B2'
|
|
lineColor: '#D1C9B2'
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
return {
|
|
return {
|
|
|
fontColor: '#FFE9B6',
|
|
fontColor: '#FFE9B6',
|
|
|
- fontSize: 16,
|
|
|
|
|
|
|
+ fontSize: IS_PC ? 8 : 16,
|
|
|
fontWeight: 'bold',
|
|
fontWeight: 'bold',
|
|
|
lineColor: '#FFE9B6'
|
|
lineColor: '#FFE9B6'
|
|
|
}
|
|
}
|
|
@@ -101,7 +105,7 @@ const createNodeConfig = (options: CreateNodeConfigOptions) => {
|
|
|
opacity
|
|
opacity
|
|
|
},
|
|
},
|
|
|
label: {
|
|
label: {
|
|
|
- show: true,
|
|
|
|
|
|
|
+ show: opacity === 1,
|
|
|
formatter: node.name,
|
|
formatter: node.name,
|
|
|
fontSize: style.fontSize,
|
|
fontSize: style.fontSize,
|
|
|
fontWeight: style.fontWeight,
|
|
fontWeight: style.fontWeight,
|
|
@@ -139,7 +143,6 @@ const loadImage = (src: string): Promise<HTMLImageElement> => {
|
|
|
const SCALE_MIN = 0.1
|
|
const SCALE_MIN = 0.1
|
|
|
const SCALE_MAX = 1.2
|
|
const SCALE_MAX = 1.2
|
|
|
const DEFAULT_ZOOM = 0.5
|
|
const DEFAULT_ZOOM = 0.5
|
|
|
-const IS_PC = window.innerWidth > 1024
|
|
|
|
|
|
|
|
|
|
// Force 布局配置
|
|
// Force 布局配置
|
|
|
const FORCE_LAYOUT_CONFIG = {
|
|
const FORCE_LAYOUT_CONFIG = {
|
|
@@ -204,6 +207,8 @@ function A9knowlege() {
|
|
|
const [knowlegeData, setKnowlegeData] = useState<any[]>([])
|
|
const [knowlegeData, setKnowlegeData] = useState<any[]>([])
|
|
|
const [dataLoading, setDataLoading] = useState(true)
|
|
const [dataLoading, setDataLoading] = useState(true)
|
|
|
const currentId = useRef<string | null>(null)
|
|
const currentId = useRef<string | null>(null)
|
|
|
|
|
+ // 获取布局缩放比例
|
|
|
|
|
+ const layoutStyle = useSelector((state: RootState) => state.A0Layout.style)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (!window.location.href.includes('?l=look')) {
|
|
if (!window.location.href.includes('?l=look')) {
|
|
@@ -214,7 +219,7 @@ function A9knowlege() {
|
|
|
const echartRef = useRef<HTMLDivElement | null>(null)
|
|
const echartRef = useRef<HTMLDivElement | null>(null)
|
|
|
const chartInstance = useRef<any>(null)
|
|
const chartInstance = useRef<any>(null)
|
|
|
const graphRef = useRef<any>(null)
|
|
const graphRef = useRef<any>(null)
|
|
|
- const scaleLineRef = useRef<HTMLDivElement | null>(null)
|
|
|
|
|
|
|
+ // const scaleLineRef = useRef<HTMLDivElement | null>(null)
|
|
|
const [detail, setDetail] = useState<any>(null)
|
|
const [detail, setDetail] = useState<any>(null)
|
|
|
const [detailLoading, setDetailLoading] = useState(false)
|
|
const [detailLoading, setDetailLoading] = useState(false)
|
|
|
// 缩放百分比
|
|
// 缩放百分比
|
|
@@ -299,6 +304,30 @@ function A9knowlege() {
|
|
|
return { nodes, links, categories }
|
|
return { nodes, links, categories }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 获取缩放比例的辅助函数
|
|
|
|
|
+ const getScaleRatio = useCallback(() => {
|
|
|
|
|
+ if (layoutStyle?.sizeW && layoutStyle?.sizeH) {
|
|
|
|
|
+ // 使用 Redux store 中的缩放比例
|
|
|
|
|
+ return Math.min(layoutStyle.sizeW, layoutStyle.sizeH)
|
|
|
|
|
+ }
|
|
|
|
|
+ // 如果没有,尝试从 DOM 元素计算
|
|
|
|
|
+ const rootElement = document.querySelector('#root')
|
|
|
|
|
+ if (rootElement) {
|
|
|
|
|
+ const transform = window.getComputedStyle(rootElement).transform
|
|
|
|
|
+ if (transform && transform !== 'none') {
|
|
|
|
|
+ const matrix = transform.match(/matrix\(([^)]+)\)/)
|
|
|
|
|
+ if (matrix) {
|
|
|
|
|
+ const values = matrix[1].split(',').map(parseFloat)
|
|
|
|
|
+ // matrix(a, b, c, d, e, f) 中 a 和 d 是缩放值
|
|
|
|
|
+ const scaleX = values[0] || 1
|
|
|
|
|
+ const scaleY = values[3] || 1
|
|
|
|
|
+ return Math.min(scaleX, scaleY)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return 1
|
|
|
|
|
+ }, [layoutStyle])
|
|
|
|
|
+
|
|
|
const initChart = useCallback(async () => {
|
|
const initChart = useCallback(async () => {
|
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
|
const echarts = (window as any).echarts
|
|
const echarts = (window as any).echarts
|
|
@@ -312,9 +341,13 @@ function A9knowlege() {
|
|
|
chartInstance.current = null
|
|
chartInstance.current = null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 计算实际的 devicePixelRatio,考虑 CSS transform 缩放
|
|
|
|
|
+ const scaleRatio = getScaleRatio()
|
|
|
|
|
+
|
|
|
const myChart = echarts.init(echartRef.current, null, {
|
|
const myChart = echarts.init(echartRef.current, null, {
|
|
|
renderer: 'canvas',
|
|
renderer: 'canvas',
|
|
|
- useDirtyRect: false
|
|
|
|
|
|
|
+ useDirtyRect: false,
|
|
|
|
|
+ devicePixelRatio: IS_PC ? scaleRatio : window.devicePixelRatio
|
|
|
})
|
|
})
|
|
|
chartInstance.current = myChart
|
|
chartInstance.current = myChart
|
|
|
|
|
|
|
@@ -339,7 +372,7 @@ function A9knowlege() {
|
|
|
}
|
|
}
|
|
|
}),
|
|
}),
|
|
|
categories: graph.categories,
|
|
categories: graph.categories,
|
|
|
- roam: IS_PC ? true : 'move',
|
|
|
|
|
|
|
+ roam: true,
|
|
|
zoom: DEFAULT_ZOOM,
|
|
zoom: DEFAULT_ZOOM,
|
|
|
scaleLimit: {
|
|
scaleLimit: {
|
|
|
min: SCALE_MIN,
|
|
min: SCALE_MIN,
|
|
@@ -546,18 +579,22 @@ function A9knowlege() {
|
|
|
resetHighlight() // 恢复所有节点和连线的高亮状态
|
|
resetHighlight() // 恢复所有节点和连线的高亮状态
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
- if (!IS_PC) {
|
|
|
|
|
- myChart.on('graphroam', function (params: any) {
|
|
|
|
|
- if (typeof params?.zoom === 'number') {
|
|
|
|
|
- const p = (params.zoom - SCALE_MIN) / (SCALE_MAX - SCALE_MIN)
|
|
|
|
|
- setZoomPercent(Math.max(0, Math.min(1, p)))
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ // if (!IS_PC) {
|
|
|
|
|
+ // myChart.on('graphroam', function (params: any) {
|
|
|
|
|
+ // if (typeof params?.zoom === 'number') {
|
|
|
|
|
+ // const p = (params.zoom - SCALE_MIN) / (SCALE_MAX - SCALE_MIN)
|
|
|
|
|
+ // setZoomPercent(Math.max(0, Math.min(1, p)))
|
|
|
|
|
+ // }
|
|
|
|
|
+ // })
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ const resizeHandler = () => {
|
|
|
|
|
+ // 当窗口大小改变时,重新调整图表尺寸
|
|
|
|
|
+ // devicePixelRatio 在初始化时已设置,resize 时会自动适应
|
|
|
|
|
+ myChart.resize()
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- const resizeHandler = () => myChart.resize()
|
|
|
|
|
window.addEventListener('resize', resizeHandler)
|
|
window.addEventListener('resize', resizeHandler)
|
|
|
- }, [knowlegeData, dataLoading])
|
|
|
|
|
|
|
+ }, [knowlegeData, dataLoading, getScaleRatio])
|
|
|
|
|
|
|
|
const handleClosePanel = () => {
|
|
const handleClosePanel = () => {
|
|
|
setDetail(null)
|
|
setDetail(null)
|
|
@@ -611,12 +648,12 @@ function A9knowlege() {
|
|
|
)
|
|
)
|
|
|
}, [zoomPercent])
|
|
}, [zoomPercent])
|
|
|
|
|
|
|
|
- const handleZoomStep = (deltaPercent: number) => {
|
|
|
|
|
- setZoomPercent(prev => {
|
|
|
|
|
- const next = prev + deltaPercent
|
|
|
|
|
- return Math.max(0, Math.min(1, next))
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // const handleZoomStep = (deltaPercent: number) => {
|
|
|
|
|
+ // setZoomPercent(prev => {
|
|
|
|
|
+ // const next = prev + deltaPercent
|
|
|
|
|
+ // return Math.max(0, Math.min(1, next))
|
|
|
|
|
+ // })
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div className={styles.A9knowlege}>
|
|
<div className={styles.A9knowlege}>
|
|
@@ -626,7 +663,7 @@ function A9knowlege() {
|
|
|
<div id='echart-container' ref={echartRef} />
|
|
<div id='echart-container' ref={echartRef} />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {!IS_PC && (
|
|
|
|
|
|
|
+ {/* {!IS_PC && (
|
|
|
<div className={styles.scaleControl}>
|
|
<div className={styles.scaleControl}>
|
|
|
<div className={styles.scaleControlItem} onClick={() => handleZoomStep(-0.1)}>
|
|
<div className={styles.scaleControlItem} onClick={() => handleZoomStep(-0.1)}>
|
|
|
<img src={require('./images/icon_zoomin.png')} alt='' />
|
|
<img src={require('./images/icon_zoomin.png')} alt='' />
|
|
@@ -643,7 +680,7 @@ function A9knowlege() {
|
|
|
<div className={styles.scaleControlItemText}>放大</div>
|
|
<div className={styles.scaleControlItemText}>放大</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- )}
|
|
|
|
|
|
|
+ )} */}
|
|
|
|
|
|
|
|
<div className={`${styles.sidebar} ${!sidebarVisible ? styles.sidebarHidden : ''}`}>
|
|
<div className={`${styles.sidebar} ${!sidebarVisible ? styles.sidebarHidden : ''}`}>
|
|
|
{detailLoading ? (
|
|
{detailLoading ? (
|