فهرست منبع

Merge branch 'master' of http://192.168.0.115:3000/lanxin/Chengzhebei

lanxin 1 روز پیش
والد
کامیت
da5b5be5cd
33فایلهای تغییر یافته به همراه4259 افزوده شده و 1561 حذف شده
  1. 2872 380
      package-lock.json
  2. 2 0
      package.json
  3. 20 0
      public/knowlege/asset-manifest.json
  4. BIN
      public/knowlege/favicon.ico
  5. 1 0
      public/knowlege/index.html
  6. 2 0
      public/knowlege/static/css/912.a53e8d10.chunk.css
  7. 1 0
      public/knowlege/static/css/912.a53e8d10.chunk.css.map
  8. 2 0
      public/knowlege/static/css/main.592aab85.css
  9. 1 0
      public/knowlege/static/css/main.592aab85.css.map
  10. 3 0
      public/knowlege/static/js/258.d9c4f188.chunk.js
  11. 17 0
      public/knowlege/static/js/258.d9c4f188.chunk.js.LICENSE.txt
  12. 1 0
      public/knowlege/static/js/258.d9c4f188.chunk.js.map
  13. 2 0
      public/knowlege/static/js/912.3d7cf3e1.chunk.js
  14. 1 0
      public/knowlege/static/js/912.3d7cf3e1.chunk.js.map
  15. 3 0
      public/knowlege/static/js/main.f6c967f0.js
  16. 78 0
      public/knowlege/static/js/main.f6c967f0.js.LICENSE.txt
  17. 1 0
      public/knowlege/static/js/main.f6c967f0.js.map
  18. BIN
      public/knowlege/static/media/bg_jiaohu-min.a9768d99e466f68ab764.jpg
  19. 18 9
      src/App.tsx
  20. 7 6
      src/pages/A9knowlege/components/Chart/index.module.scss
  21. 43 40
      src/pages/A9knowlege/components/Chart/index.tsx
  22. 1 0
      src/pages/A9knowlege/components/ChartInput/index.module.scss
  23. 1 0
      src/pages/A9knowlege/components/ChartInput/index.tsx
  24. 1 0
      src/pages/A9knowlege/components/Panel/index.module.scss
  25. 1 0
      src/pages/A9knowlege/components/Panel2/index.module.scss
  26. BIN
      src/pages/A9knowlege/images/btn_close_red.png
  27. BIN
      src/pages/A9knowlege/images/frame_01_01.png
  28. BIN
      src/pages/A9knowlege/images/frame_01_03.png
  29. BIN
      src/pages/A9knowlege/images/icon_flower_red.png
  30. BIN
      src/pages/A9knowlege/images/line_04-min.png
  31. 14 19
      src/pages/A9knowlege/index.module.scss
  32. 115 626
      src/pages/A9knowlege/index.tsx
  33. 1051 481
      yarn.lock

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2872 - 380
package-lock.json


+ 2 - 0
package.json

@@ -9,6 +9,7 @@
     "@testing-library/jest-dom": "^5.16.5",
     "@testing-library/react": "^13.4.0",
     "@testing-library/user-event": "^13.5.0",
+    "@types/d3": "^7.4.3",
     "@types/jest": "^27.5.2",
     "@types/node": "^16.18.3",
     "@types/react": "^18.0.24",
@@ -18,6 +19,7 @@
     "axios": "^1.13.2",
     "classnames": "^2.5.1",
     "crypto-js": "^4.2.0",
+    "d3": "^7.9.0",
     "pdfh5": "^3.0.0",
     "pdfjs-dist": "3.11.174",
     "react": "^18.2.0",

+ 20 - 0
public/knowlege/asset-manifest.json

@@ -0,0 +1,20 @@
+{
+  "files": {
+    "main.css": "./static/css/main.592aab85.css",
+    "main.js": "./static/js/main.f6c967f0.js",
+    "static/css/912.a53e8d10.chunk.css": "./static/css/912.a53e8d10.chunk.css",
+    "static/js/912.3d7cf3e1.chunk.js": "./static/js/912.3d7cf3e1.chunk.js",
+    "static/js/258.d9c4f188.chunk.js": "./static/js/258.d9c4f188.chunk.js",
+    "static/media/bg_jiaohu-min.jpg": "./static/media/bg_jiaohu-min.a9768d99e466f68ab764.jpg",
+    "index.html": "./index.html",
+    "main.592aab85.css.map": "./static/css/main.592aab85.css.map",
+    "main.f6c967f0.js.map": "./static/js/main.f6c967f0.js.map",
+    "912.a53e8d10.chunk.css.map": "./static/css/912.a53e8d10.chunk.css.map",
+    "912.3d7cf3e1.chunk.js.map": "./static/js/912.3d7cf3e1.chunk.js.map",
+    "258.d9c4f188.chunk.js.map": "./static/js/258.d9c4f188.chunk.js.map"
+  },
+  "entrypoints": [
+    "static/css/main.592aab85.css",
+    "static/js/main.f6c967f0.js"
+  ]
+}

BIN
public/knowlege/favicon.ico


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/knowlege/index.html


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/knowlege/static/css/912.a53e8d10.chunk.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/knowlege/static/css/912.a53e8d10.chunk.css.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/knowlege/static/css/main.592aab85.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/knowlege/static/css/main.592aab85.css.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3 - 0
public/knowlege/static/js/258.d9c4f188.chunk.js


+ 17 - 0
public/knowlege/static/js/258.d9c4f188.chunk.js.LICENSE.txt

@@ -0,0 +1,17 @@
+/*!
+	Copyright (c) 2018 Jed Watson.
+	Licensed under the MIT License (MIT), see
+	http://jedwatson.github.io/classnames
+*/
+
+/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
+
+/**
+ * @license React
+ * use-sync-external-store-shim.production.js
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/knowlege/static/js/258.d9c4f188.chunk.js.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/knowlege/static/js/912.3d7cf3e1.chunk.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/knowlege/static/js/912.3d7cf3e1.chunk.js.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3 - 0
public/knowlege/static/js/main.f6c967f0.js


+ 78 - 0
public/knowlege/static/js/main.f6c967f0.js.LICENSE.txt

@@ -0,0 +1,78 @@
+/**
+ * @license React
+ * react-dom.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * react-jsx-runtime.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * react.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * scheduler.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * use-sync-external-store-shim.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * use-sync-external-store-shim/with-selector.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/** @license React v16.13.1
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/knowlege/static/js/main.f6c967f0.js.map


BIN
public/knowlege/static/media/bg_jiaohu-min.a9768d99e466f68ab764.jpg


+ 18 - 9
src/App.tsx

@@ -256,10 +256,6 @@ export default function App() {
     if (envFlag) {
       window.addEventListener('keyup', e => {
         if (e.code === 'Enter') {
-
-
-
-
           callIframeFu(
             'refreshAppSettings',
             JSON.stringify({
@@ -1054,14 +1050,27 @@ export default function App() {
         <Image
           preview={{
             closeIcon: (
-              <img src={require(`@/assets/img/${myLangue === 'EN' ? 'closeWithTxt_E' : 'closeWithTxt'}.png`)} draggable='false' alt='' />
+              <img
+                src={require(`@/assets/img/${
+                  myLangue === 'EN' ? 'closeWithTxt_E' : 'closeWithTxt'
+                }.png`)}
+                draggable='false'
+                alt=''
+              />
             ),
             imageRender: originalNode => (
-              < div className='previewImage'  >
-                <TouchContainer className='Ori' zoom={lookBigImg.zoom}>{originalNode}</TouchContainer>
+              <div className='previewImage'>
+                <TouchContainer className='Ori' zoom={lookBigImg.zoom}>
+                  {originalNode}
+                </TouchContainer>
                 <div className='ImgFromTxt'>{lookBigImg.fromTxt}</div>
-                {lookBigImg.url.includes('tapian') || lookBigImg.url.includes('xiantu') ? <QuanwenBtns isEn={myLangue === 'EN'} name={lookBigImg.url.includes('tapian') ? 'tapian' : 'xiantu'} /> : null}
-              </div >
+                {lookBigImg.url.includes('tapian') || lookBigImg.url.includes('xiantu') ? (
+                  <QuanwenBtns
+                    isEn={myLangue === 'EN'}
+                    name={lookBigImg.url.includes('tapian') ? 'tapian' : 'xiantu'}
+                  />
+                ) : null}
+              </div>
             ),
             visible: lookBigImg.show,
             src: lookBigImg.url,

+ 7 - 6
src/pages/A9knowlege/components/Chart/index.module.scss

@@ -112,6 +112,7 @@
     height: 22px;
     background: url('../../images/icon_flower_red.png') no-repeat center / contain;
     transform: translateY(-50%);
+    cursor: pointer;
   }
   input {
     width: 100%;
@@ -134,8 +135,8 @@
   margin: 5px 10px;
   color: #7c4b36;
   max-width: 230px;
-  height: 21px;
-  line-height: 21px;
+  height: 20px;
+  line-height: 20px;
   font-size: 12px;
   cursor: pointer;
   transition: opacity 0.2s;
@@ -159,18 +160,18 @@
     position: absolute;
     top: 0;
     left: -9px;
-    bottom: 0;
     width: 9px;
-    background: url('../../images/frame_01_01.png') no-repeat center / contain;
+    height: 21px;
+    background: url('../../images/frame_01_01.png') no-repeat center / 100% 100%;
   }
   &::after {
     content: '';
     position: absolute;
     top: 0;
     right: -9px;
-    bottom: 0;
     width: 9px;
-    background: url('../../images/frame_01_03.png') no-repeat center / contain;
+    height: 21px;
+    background: url('../../images/frame_01_03.png') no-repeat center / 100% 100%;
   }
 }
 

+ 43 - 40
src/pages/A9knowlege/components/Chart/index.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useRef, useEffect } from 'react'
+import { createPortal } from 'react-dom'
 import axios from 'axios'
 import styles from './index.module.scss'
 import { decrypt } from '@/utils/encrypt'
@@ -34,8 +35,6 @@ function Chart() {
   const [currentAnswer, setCurrentAnswer] = useState('')
   const messagesEndRef = useRef<HTMLDivElement>(null)
   const inputRef = useRef<HTMLInputElement>(null)
-  const savedTransformRef = useRef<string>('')
-  const savedLeftRef = useRef<string>('')
 
   useEffect(() => {
     messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
@@ -165,36 +164,36 @@ function Chart() {
     setShowQuickQuestions(true)
   }
 
-  const handleRotateRoot = (normal: boolean) => {
-    const rootElement = document.querySelector('#root') as HTMLElement
-    if (!rootElement) return
-
-    if (normal) {
-      savedTransformRef.current = rootElement.style.transform || ''
-      savedLeftRef.current = rootElement.style.left || ''
-
-      const currentTransform = rootElement.style.transform
-      if (currentTransform && currentTransform.includes('rotate(90deg)')) {
-        rootElement.style.transform = ''
-        rootElement.style.left = '0'
-      }
-    } else {
-      rootElement.style.transform = savedTransformRef.current || ''
-      rootElement.style.left = savedLeftRef.current || ''
-    }
-  }
+  // const handleRotateRoot = (normal: boolean) => {
+  //   const rootElement = document.querySelector('#root') as HTMLElement
+  //   if (!rootElement) return
+
+  //   if (normal) {
+  //     savedTransformRef.current = rootElement.style.transform || ''
+  //     savedLeftRef.current = rootElement.style.left || ''
+
+  //     const currentTransform = rootElement.style.transform
+  //     if (currentTransform && currentTransform.includes('rotate(90deg)')) {
+  //       rootElement.style.transform = ''
+  //       rootElement.style.left = '0'
+  //     }
+  //   } else {
+  //     rootElement.style.transform = savedTransformRef.current || ''
+  //     rootElement.style.left = savedLeftRef.current || ''
+  //   }
+  // }
 
   const handleInputClick = () => {
     if (isHH) {
       return
     }
 
-    handleRotateRoot(true)
+    // handleRotateRoot(true)
     setShowChartInput(true)
   }
 
   const handleCloseChartInput = () => {
-    handleRotateRoot(false)
+    // handleRotateRoot(false)
     setShowChartInput(false)
   }
 
@@ -237,7 +236,7 @@ function Chart() {
                       className={styles.helpImprove}
                       onClick={() => {
                         const question = findQuestion()
-                        handleRotateRoot(true)
+                        // handleRotateRoot(true)
                         setCurrentQuestion(question)
                         setCurrentAnswer(msg.content)
                         setShowHelpImprove(true)
@@ -280,23 +279,27 @@ function Chart() {
         </div>
       </div>
 
-      {showChartInput && (
-        <ChartInput
-          onClose={handleCloseChartInput}
-          onSend={handleSendMessage}
-          isLoading={isLoading}
-        />
-      )}
-      {showHelpImprove && (
-        <HelpImprove
-          question={currentQuestion}
-          answer={currentAnswer}
-          onClose={() => {
-            handleRotateRoot(false)
-            setShowHelpImprove(false)
-          }}
-        />
-      )}
+      {showChartInput &&
+        createPortal(
+          <ChartInput
+            onClose={handleCloseChartInput}
+            onSend={handleSendMessage}
+            isLoading={isLoading}
+          />,
+          document.body
+        )}
+      {showHelpImprove &&
+        createPortal(
+          <HelpImprove
+            question={currentQuestion}
+            answer={currentAnswer}
+            onClose={() => {
+              // handleRotateRoot(false)
+              setShowHelpImprove(false)
+            }}
+          />,
+          document.body
+        )}
     </div>
   )
 }

+ 1 - 0
src/pages/A9knowlege/components/ChartInput/index.module.scss

@@ -47,4 +47,5 @@
   width: 100%;
   transition: opacity 0.3s;
   user-select: none;
+  cursor: pointer;
 }

+ 1 - 0
src/pages/A9knowlege/components/ChartInput/index.tsx

@@ -46,6 +46,7 @@ function ChartInput({ onClose, onSend, isLoading = false }: ChartInputProps) {
       <Zback clickFu={onClose} />
 
       <textarea
+        name='textarea'
         ref={textareaRef}
         className={styles.textarea}
         placeholder='请输入您想了解的内容...'

+ 1 - 0
src/pages/A9knowlege/components/Panel/index.module.scss

@@ -51,4 +51,5 @@
   bottom: 28px;
   width: 61px;
   height: 25px;
+  cursor: pointer;
 }

+ 1 - 0
src/pages/A9knowlege/components/Panel2/index.module.scss

@@ -47,4 +47,5 @@
   bottom: 28px;
   width: 61px;
   height: 25px;
+  cursor: pointer;
 }

BIN
src/pages/A9knowlege/images/btn_close_red.png


BIN
src/pages/A9knowlege/images/frame_01_01.png


BIN
src/pages/A9knowlege/images/frame_01_03.png


BIN
src/pages/A9knowlege/images/icon_flower_red.png


BIN
src/pages/A9knowlege/images/line_04-min.png


+ 14 - 19
src/pages/A9knowlege/index.module.scss

@@ -1,31 +1,25 @@
+.iframe {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  border: none;
+  z-index: 1;
+}
+
 .A9knowlege {
   position: relative;
   z-index: 2;
-  display: flex;
   width: 100%;
   height: 100%;
   overflow: hidden;
-  background: url('./images/bg_jiaohu-min.jpg') no-repeat center / cover;
-
-  // :global {
-
-  // }
-}
-
-.main {
-  flex: 1;
-  position: relative;
-  z-index: 1;
-
-  > * {
-    width: 100%;
-    height: 100%;
-    border: none;
-  }
 }
 
 .sidebar {
-  position: relative;
+  position: absolute;
+  top: 0;
+  right: 0;
   width: 298px;
   height: 100%;
   background: url('./images/img_pop_02-min.png') no-repeat center / cover;
@@ -47,6 +41,7 @@
   transform: translateY(-50%);
   transition: left 0.3s ease, width 0.3s ease;
   background: url('./images/icon_right@2x-min.png') no-repeat center / cover;
+  cursor: pointer;
 
   &.hide {
     left: -67px;

+ 115 - 626
src/pages/A9knowlege/index.tsx

@@ -1,670 +1,159 @@
-import React, { useEffect, useRef, useCallback, useState } from 'react'
+import React, { useRef, useState, useEffect } from 'react'
+import http from '@/utils/axios'
 import styles from './index.module.scss'
+import { echPageBackFu } from '@/components/MenuSider/data'
+import Zback from '@/components/Zback'
 import Chart from './components/Chart'
 import Panel from './components/Panel'
 import Panel2 from './components/Panel2'
 import ModelPanel from './components/ModelPanel'
-import { echPageBackFu } from '@/components/MenuSider/data'
-import Zback from '@/components/Zback'
-import http from '@/utils/axios'
-
-import level1 from './images/level_01-min.png'
-import level2 from './images/level_02-min.png'
-import level3 from './images/level_03-min.png'
-import level4 from './images/level_04-min.png'
-import level5 from './images/level_05-min.png'
-import level6 from './images/level_06-min.png'
-import { baseUrl } from '@/utils/http'
 import { DotLoading } from 'antd-mobile'
 
-const levelImages = [level1, level1, level1, level2, level3, level4, level5, level6]
-const getLevelImage = (depth: number) => levelImages[Math.min(depth, 5)]
-const getLevelSize = (depth: number) => (depth <= 2 ? 60 : 40)
-
-// 节点样式配置
-interface NodeStyleConfig {
-  fontColor: string
-  fontSize: number
-  fontWeight: 'bold' | 'normal'
-  lineColor: string
-}
-
-const getNodeStyleByDepth = (depth: number): NodeStyleConfig => {
-  if (depth === 3) {
-    return {
-      fontColor: '#FF9807',
-      fontSize: 16,
-      fontWeight: 'normal',
-      lineColor: '#FF9807'
-    }
-  } else if (depth >= 4) {
-    return {
-      fontColor: '#D1C9B2',
-      fontSize: 14,
-      fontWeight: 'normal',
-      lineColor: '#D1C9B2'
-    }
-  }
-  return {
-    fontColor: '#FFE9B6',
-    fontSize: 16,
-    fontWeight: 'bold',
-    lineColor: '#FFE9B6'
-  }
-}
-
-// 从节点 category 中提取 depth
-const getDepthFromCategory = (category: string): number => {
-  const match = (category || '').match(/level(\d+)/)
-  return match ? Number(match[1]) : 0
-}
-
-// 获取图表中所有节点的位置信息
-const getNodePositions = (chartInstance: any): Map<string, { x?: number; y?: number }> => {
-  const nodePositionMap = new Map<string, { x?: number; y?: number }>()
-  if (!chartInstance) return nodePositionMap
-
-  const currentOption = chartInstance.getOption()
-  const currentSeries = currentOption.series?.[0]
-  const currentNodes = currentSeries?.data || []
-
-  currentNodes.forEach((node: any) => {
-    if (node.id && (node.x !== undefined || node.y !== undefined)) {
-      nodePositionMap.set(node.id, { x: node.x, y: node.y })
-    }
-  })
-
-  return nodePositionMap
-}
-
-// 创建节点配置
-interface CreateNodeConfigOptions {
-  node: any
-  position?: { x?: number; y?: number }
-  opacity?: number
-  fixed?: boolean
-}
-
-const createNodeConfig = (options: CreateNodeConfigOptions) => {
-  const { node, position = {}, opacity = 1, fixed = true } = options
-  const depth = getDepthFromCategory(node.category || '')
-  const style = getNodeStyleByDepth(depth)
-
-  return {
-    ...node,
-    ...position,
-    fixed,
-    symbol: node.symbol,
-    symbolSize: node.symbolSize,
-    itemStyle: {
-      color: style.fontColor,
-      opacity
-    },
-    label: {
-      show: true,
-      formatter: node.name,
-      fontSize: style.fontSize,
-      fontWeight: style.fontWeight,
-      color: style.fontColor,
-      position: 'bottom',
-      distance: 6,
-      align: 'center',
-      textShadowColor: 'rgba(0, 0, 0, 0.5)',
-      textShadowBlur: 4,
-      textShadowOffsetX: 0,
-      textShadowOffsetY: 0
-    }
-  }
-}
-
-const transformData = (data: any[]): any[] => {
-  return data.map(item => ({
-    ...item,
-    label: item.name,
-    children: item.children ? transformData(item.children) : undefined
-  }))
-}
-
-const loadImage = (src: string): Promise<HTMLImageElement> => {
-  return new Promise((resolve, reject) => {
-    const img = new Image()
-    img.crossOrigin = 'anonymous'
-    img.onload = () => resolve(img)
-    img.onerror = () => reject(new Error(`Failed to load: ${src}`))
-    img.src = src
-  })
-}
-
-// 缩放区间和默认值(与 ECharts 的 scaleLimit / zoom 保持一致)
-const SCALE_MIN = 0.1
-const SCALE_MAX = 1.2
-const DEFAULT_ZOOM = 0.5
-const IS_PC = window.innerWidth > 1024
-
-// Force 布局配置
-const FORCE_LAYOUT_CONFIG = {
-  repulsion: 1500,
-  edgeLength: [80, 80],
-  gravity: 0.05,
-  layoutAnimation: false
-}
-
-// 禁用布局的配置(用于保持节点位置)
-const DISABLED_FORCE_CONFIG = {
-  initLayout: null,
-  friction: 0,
-  repulsion: 0,
-  gravity: 0
-}
-
-const createCombinedSymbol = async (
-  levelImageSrc: string,
-  thumbSrc: string,
-  symbolSize: number
-): Promise<string> => {
-  try {
-    const canvas = document.createElement('canvas')
-    canvas.width = symbolSize
-    canvas.height = symbolSize
-    const ctx = canvas.getContext('2d')!
-
-    const levelImg = await loadImage(levelImageSrc)
-    ctx.drawImage(levelImg, 0, 0, symbolSize, symbolSize)
-
-    const thumbImg = await loadImage(thumbSrc)
-    const thumbSize = symbolSize === 40 ? 30 : 48
-    const centerX = symbolSize / 2 - (symbolSize === 60 ? 1 : 0)
-    const centerY = symbolSize / 2 - (symbolSize === 60 ? 1 : 0)
-    const imgW = thumbImg.width
-    const imgH = thumbImg.height
-    const scale = Math.max(thumbSize / imgW, thumbSize / imgH)
-    const drawW = imgW * scale
-    const drawH = imgH * scale
-    const drawX = centerX - drawW / 2
-    const drawY = centerY - drawH / 2
-
-    ctx.save()
-    ctx.beginPath()
-    ctx.arc(centerX, centerY, thumbSize / 2, 0, Math.PI * 2)
-    ctx.clip()
-    ctx.drawImage(thumbImg, drawX, drawY, drawW, drawH)
-    ctx.restore()
-
-    return `image://${canvas.toDataURL()}`
-  } catch (e) {
-    return `image://${levelImageSrc}`
-  }
-}
-
 function A9knowlege() {
-  // 是否显示返回按钮
-  const [backShow, setBackShow] = useState(false)
   // 控制 sidebar 显示
   const [sidebarVisible, setSidebarVisible] = useState(true)
-  const [knowlegeData, setKnowlegeData] = useState<any[]>([])
-  const [dataLoading, setDataLoading] = useState(true)
-  const currentId = useRef<string | null>(null)
-
-  useEffect(() => {
-    if (!window.location.href.includes('?l=look')) {
-      setBackShow(true)
-    }
-  }, [])
-
-  const echartRef = useRef<HTMLDivElement | null>(null)
-  const chartInstance = useRef<any>(null)
-  const graphRef = useRef<any>(null)
-  const scaleLineRef = useRef<HTMLDivElement | null>(null)
   const [detail, setDetail] = useState<any>(null)
   const [detailLoading, setDetailLoading] = useState(false)
-  // 缩放百分比
-  const [zoomPercent, setZoomPercent] = useState(
-    (DEFAULT_ZOOM - SCALE_MIN) / (SCALE_MAX - SCALE_MIN)
-  )
-
-  useEffect(() => {
-    const fetchKnowlegeData = async () => {
-      try {
-        setDataLoading(true)
-        const response = await http.get('/show/dict/getTree')
-        if (response.code === 0 && response.data) {
-          const transformedData = transformData(Array.isArray(response.data) ? response.data : [])
-          setKnowlegeData(transformedData)
-        } else {
-          setKnowlegeData([])
-        }
-      } catch (error) {
-        console.error('获取知识图谱数据失败:', error)
-        setKnowlegeData([])
-      } finally {
-        setDataLoading(false)
-      }
-    }
-    fetchKnowlegeData()
-  }, [])
-
-  const buildGraphFromKnowlege = async (data: any[]) => {
-    const nodes: any[] = []
-    const links: any[] = []
-    let idCounter = 0
-    const genId = (label: string) =>
-      `${idCounter++}-${String(label || 'node').replace(/\s+/g, '_')}`
-
-    const traverse = (item: any, parentId: string | null, depth: number) => {
-      const id = genId(item.label)
-      const category = `level${depth}`
-      const symbolSize = getLevelSize(depth)
-      const symbol = `image://${getLevelImage(depth)}`
-
-      nodes.push({
-        id,
-        name: item.label,
-        category,
-        symbolSize,
-        symbol,
-        raw: item,
-        depth,
-        thumb: item.thumb ? baseUrl + item.thumb : null
-      })
-
-      if (parentId) {
-        links.push({ source: parentId, target: id, targetDepth: depth })
-      }
-
-      if (Array.isArray(item.children)) {
-        item.children.forEach((child: any) => traverse(child, id, depth + 1))
-      }
-    }
-
-    data.forEach((root: any) => traverse(root, null, 0))
-
-    // 对于有 thumb 的节点,异步生成合成 symbol
-    const thumbPromises = nodes
-      .filter(n => n.thumb)
-      .map(async n => {
-        const levelImageSrc = getLevelImage(n.depth)
-        n.symbol = await createCombinedSymbol(levelImageSrc, n.thumb, n.symbolSize)
-      })
-    await Promise.all(thumbPromises)
-
-    const maxDepth = nodes.reduce((m, n) => {
-      const d = getDepthFromCategory(n.category || '')
-      return Math.max(m, d)
-    }, 0)
-
-    const categories = Array.from({ length: maxDepth + 1 }, (_, i) => ({
-      name: `level${i}`
-    }))
-
-    return { nodes, links, categories }
-  }
+  const [animationCompleted, setAnimationCompleted] = useState(true)
+  const currentId = useRef<string | null>(null)
+  const iframeRef = useRef<HTMLIFrameElement>(null)
 
-  const initChart = useCallback(async () => {
+  const handleClosePanel = () => {
     // @ts-ignore
-    const echarts = (window as any).echarts
-    const data = knowlegeData || []
-    if (!echarts || !echartRef.current || dataLoading || data.length === 0) return
-
-    if (chartInstance.current) {
-      try {
-        chartInstance.current.dispose()
-      } catch (e) {}
-      chartInstance.current = null
-    }
-
-    const myChart = echarts.init(echartRef.current, null, {
-      renderer: 'canvas',
-      useDirtyRect: false
-    })
-    chartInstance.current = myChart
-
-    const graph = await buildGraphFromKnowlege(data)
-    graphRef.current = graph
-
-    const option = {
-      tooltip: { show: false },
-      series: [
-        {
-          name: 'KnowledgeGraph',
-          type: 'graph',
-          layout: 'force',
-          data: graph.nodes.map((n: any) => createNodeConfig({ node: n, fixed: false })),
-          links: graph.links.map((link: any) => {
-            const style = getNodeStyleByDepth(link.targetDepth)
-            return {
-              ...link,
-              lineStyle: {
-                color: style.lineColor
-              }
-            }
-          }),
-          categories: graph.categories,
-          roam: IS_PC ? true : 'move',
-          zoom: DEFAULT_ZOOM,
-          scaleLimit: {
-            min: SCALE_MIN,
-            max: SCALE_MAX
-          },
-          label: { position: 'bottom', distance: 6, align: 'center' },
-          // emphasis: {
-          //   focus: 'adjacency',
-          //   lineStyle: {
-          //     width: 3,
-          //     opacity: 1
-          //   }
-          // },
-          // force 布局参数:调大 repulsion 让斥力更明显
-          // 降低 gravity 避免被拉回中心过紧
-          force: FORCE_LAYOUT_CONFIG,
-          // 连线为直线:curveness 设为 0
-          lineStyle: { curveness: 0 },
-          edgeLabel: { show: false },
-          edgeSymbol: ['none', 'none']
-        }
-      ],
-      animationDurationUpdate: 0
-    }
-
-    myChart.setOption(option, true)
-
-    // 初始化滑块位置
-    setZoomPercent((DEFAULT_ZOOM - SCALE_MIN) / (SCALE_MAX - SCALE_MIN))
-
-    // 更新图表节点和连线配置的通用函数
-    const updateChartNodesAndLinks = (
-      nodes: any[],
-      links: any[],
-      opacityMap?: Map<string, number>
-    ) => {
-      const nodePositionMap = getNodePositions(myChart)
-      const updatedNodes = nodes.map((n: any) => {
-        const position = nodePositionMap.get(n.id) || {}
-        const opacity = opacityMap?.get(n.id) ?? 1
-        return createNodeConfig({
-          node: n,
-          position,
-          opacity
-        })
-      })
-
-      const updatedLinks = links.map((link: any) => {
-        const linkKey = `${link.source}-${link.target}`
-        const reverseKey = `${link.target}-${link.source}`
-        // 如果没有提供 opacityMap,默认透明度为 1(重置状态)
-        // 如果提供了 opacityMap,检查是否有关联的连线
-        const opacity = opacityMap
-          ? opacityMap.get(linkKey) ?? opacityMap.get(reverseKey) ?? 0.1
-          : 1
-        return {
-          ...link,
-          lineStyle: {
-            ...link.lineStyle,
-            opacity
-          }
-        }
-      })
-
-      myChart.setOption(
-        {
-          series: [
-            {
-              data: updatedNodes,
-              links: updatedLinks,
-              force: DISABLED_FORCE_CONFIG
-            }
-          ]
-        },
-        { notMerge: false }
-      )
-    }
-
-    // 恢复所有节点和连线的高亮状态(取消高亮)
-    const resetHighlight = () => {
-      updateChartNodesAndLinks(graph.nodes, graph.links)
-    }
+    iframeRef.current?.contentWindow?.blurNode()
+    setDetail(null)
+    currentId.current = null
+  }
 
-    myChart.on('click', function (params: any) {
-      if (!params || !params.data) return
+  const handleTransitionEnd = () => {
+    setAnimationCompleted(true)
+  }
 
-      if (params.dataType === 'edge') {
-        // 关系线
-        const edge = params.data
-        const sourceIndex = graph.nodes.findIndex((n: any) => n.id === edge.source)
-        const targetIndex = graph.nodes.findIndex((n: any) => n.id === edge.target)
+  const toggleSidebar = () => {
+    setAnimationCompleted(false)
+    setSidebarVisible(!sidebarVisible)
+  }
 
-        if (sourceIndex !== -1 && targetIndex !== -1) {
-          // 高亮源节点
+  useEffect(() => {
+    const handleMessage = (event: MessageEvent) => {
+      if (event.data && event.data.id) {
+        const id = event.data.id
+        if (currentId.current !== id) {
+          fetchNodeDetail(id)
         }
       } else {
-        // 节点
-        const node = params.data
-        const targetId = params.data.id
-        const related = new Set([targetId])
-
-        // 创建节点和连线的透明度映射
-        const opacityMap = new Map<string, number>()
-
-        // 标记关联元素并设置透明度映射
-        graph.links.forEach((link: any) => {
-          if (link.source === targetId || link.target === targetId) {
-            related.add(link.source)
-            related.add(link.target)
-            // 设置关联连线的透明度
-            const linkKey = `${link.source}-${link.target}`
-            const reverseKey = `${link.target}-${link.source}`
-            opacityMap.set(linkKey, 1)
-            opacityMap.set(reverseKey, 1)
-          }
-        })
-
-        // 设置节点透明度
-        graph.nodes.forEach((n: any) => {
-          opacityMap.set(n.id, related.has(n.id) ? 1 : 0.1)
-        })
-
-        // 更新图表节点和连线
-        updateChartNodesAndLinks(graph.nodes, graph.links, opacityMap)
-
-        if (node && node.raw && node.raw.id) {
-          // 如果点击的节点是已选中的节点,则取消选中状态
-          if (currentId.current === node.raw.id) {
-            currentId.current = null
-            setDetail(null)
-            resetHighlight() // 恢复所有节点和连线的高亮状态
-            return
-          }
-
-          // 高亮选中的节点
-
-          const fetchDetail = async () => {
-            try {
-              currentId.current = node.raw.id
-              setDetailLoading(true)
-              const response = await http.get(`/show/dict/detail/${node.raw.id}`)
-
-              if (response.code !== 0 || !response.data) {
-                setDetail(null)
-                return
-              }
-
-              const { label, name, rtf } = response.data
-              let detailData: any = { name }
-
-              // 存在 label 显示 Panel
-              if (label) {
-                try {
-                  const parsedLabel = JSON.parse(label.replace(/\\\\"/g, '\\"'))
-                  detailData = {
-                    ...detailData,
-                    label: name,
-                    content: Array.isArray(parsedLabel) ? parsedLabel : [],
-                    type: 'label'
-                  }
-                  setDetail(detailData)
-                  setSidebarVisible(true)
-                  return
-                } catch (e) {
-                  console.error('解析 label 失败:', e)
-                }
-              }
-
-              // 存在 rtf 显示 Panel2
-              if (rtf) {
-                try {
-                  const parsedRtf = JSON.parse(rtf)
-                  const content = parsedRtf?.txtArr?.[0]?.txt || ''
-                  detailData = {
-                    ...detailData,
-                    label: name,
-                    content: content,
-                    type: 'rtf'
-                  }
-                  setDetail(detailData)
-                  setSidebarVisible(true)
-                  return
-                } catch (e) {
-                  console.error('解析 rtf 失败:', e)
-                }
-              }
-
-              setDetail(null)
-            } catch (error) {
-              console.error('获取详情失败:', error)
-              setDetail(null)
-            } finally {
-              setDetailLoading(false)
-            }
-          }
-          fetchDetail()
-        }
-      }
-    })
-    myChart.getZr().on('click', function (params: any) {
-      if (!params.target) {
-        // 点击空白处,取消选中状态和高亮
+        setDetail(null)
         currentId.current = null
-        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)))
-        }
-      })
     }
 
-    const resizeHandler = () => myChart.resize()
-    window.addEventListener('resize', resizeHandler)
-  }, [knowlegeData, dataLoading])
+    window.addEventListener('message', handleMessage)
 
-  const handleClosePanel = () => {
-    setDetail(null)
-    currentId.current = null
-  }
-
-  useEffect(() => {
-    if (!dataLoading && knowlegeData.length > 0) {
-      initChart()
-    }
     return () => {
-      if (chartInstance.current) {
-        try {
-          const h = (chartInstance.current as any)._resizeHandler
-          if (h) window.removeEventListener('resize', h)
-          chartInstance.current.dispose()
-        } catch (e) {}
-        chartInstance.current = null
-      }
+      window.removeEventListener('message', handleMessage)
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [knowlegeData, dataLoading, initChart])
+  }, [])
 
-  useEffect(() => {
-    const timer = setTimeout(() => {
-      if (chartInstance.current) {
-        chartInstance.current.resize()
+  // 获取节点详情
+  const fetchNodeDetail = async (id: string) => {
+    try {
+      currentId.current = id
+      setDetailLoading(true)
+      const response = await http.get(`/show/dict/detail/${id}`)
+
+      if (response.code !== 0 || !response.data) {
+        setDetail(null)
+        return
       }
-    }, 300)
-    return () => clearTimeout(timer)
-  }, [sidebarVisible])
 
-  const percentToZoom = (percent: number) => {
-    const p = Math.max(0, Math.min(1, percent))
-    return SCALE_MIN + p * (SCALE_MAX - SCALE_MIN)
-  }
+      const { label, name, rtf } = response.data
+      let detailData: any = { name }
 
-  // 外部控制:当 zoomPercent 改变时,同步到 ECharts
-  useEffect(() => {
-    if (!chartInstance.current) return
-    const zoom = percentToZoom(zoomPercent)
-    chartInstance.current.setOption(
-      {
-        series: [
-          {
-            zoom
+      // 存在 label 显示 Panel
+      if (label) {
+        try {
+          const parsedLabel = JSON.parse(label.replace(/\\\\"/g, '\\"'))
+          detailData = {
+            ...detailData,
+            label: name,
+            content: Array.isArray(parsedLabel) ? parsedLabel : [],
+            type: 'label'
+          }
+          setDetail(detailData)
+          setSidebarVisible(true)
+          return
+        } catch (e) {
+          console.error('解析 label 失败:', e)
+        }
+      }
+
+      // 存在 rtf 显示 Panel2
+      if (rtf) {
+        try {
+          const parsedRtf = JSON.parse(rtf)
+          const content = parsedRtf?.txtArr?.[0]?.txt || ''
+          detailData = {
+            ...detailData,
+            label: name,
+            content: content,
+            type: 'rtf'
           }
-        ]
-      },
-      false
-    )
-  }, [zoomPercent])
+          setDetail(detailData)
+          setSidebarVisible(true)
+          return
+        } catch (e) {
+          console.error('解析 rtf 失败:', e)
+        }
+      }
 
-  const handleZoomStep = (deltaPercent: number) => {
-    setZoomPercent(prev => {
-      const next = prev + deltaPercent
-      return Math.max(0, Math.min(1, next))
-    })
+      setDetail(null)
+    } catch (error) {
+      console.error('获取详情失败:', error)
+      setDetail(null)
+    } finally {
+      setDetailLoading(false)
+    }
   }
 
   return (
     <div className={styles.A9knowlege}>
-      {backShow ? <Zback clickFu={() => echPageBackFu()} /> : null}
-
-      <div className={styles.main}>
-        <div id='echart-container' ref={echartRef} />
-      </div>
-
-      {!IS_PC && (
-        <div className={styles.scaleControl}>
-          <div className={styles.scaleControlItem} onClick={() => handleZoomStep(-0.1)}>
-            <img src={require('./images/icon_zoomin.png')} alt='' />
-            <div className={styles.scaleControlItemText}>缩小</div>
-          </div>
-          <div className={styles.scaleControlItemLine} ref={scaleLineRef}>
-            <div
-              className={styles.scaleControlItemLineInner}
-              style={{ left: `${zoomPercent * 100}%` }}
-            />
-          </div>
-          <div className={styles.scaleControlItem} onClick={() => handleZoomStep(0.1)}>
-            <img src={require('./images/icon_zoomax.png')} alt='' />
-            <div className={styles.scaleControlItemText}>放大</div>
-          </div>
-        </div>
-      )}
-
-      <div className={`${styles.sidebar} ${!sidebarVisible ? styles.sidebarHidden : ''}`}>
-        {detailLoading ? (
-          <div className={styles.loading}>
-            <DotLoading color='#7c4b36' />
-          </div>
-        ) : !detail ? (
-          <Chart />
-        ) : detail.type === 'label' ? (
-          <Panel detail={detail} onClose={handleClosePanel} />
-        ) : detail.type === 'rtf' ? (
-          <Panel2 detail={detail} onClose={handleClosePanel} />
-        ) : detail.type === 5 ? (
-          <ModelPanel detail={detail} onClose={handleClosePanel} />
-        ) : (
-          <Panel2 detail={detail} onClose={handleClosePanel} />
+      <Zback clickFu={() => echPageBackFu()} />
+
+      <iframe
+        ref={iframeRef}
+        className={styles.iframe}
+        src='knowlege/index.html'
+        title='knowlege'
+      />
+
+      <div
+        className={`${styles.sidebar} ${!sidebarVisible ? styles.sidebarHidden : ''}`}
+        onTransitionEnd={handleTransitionEnd}
+      >
+        {animationCompleted && sidebarVisible && (
+          <>
+            {detailLoading ? (
+              <div className={styles.loading}>
+                <DotLoading color='#7c4b36' />
+              </div>
+            ) : !detail ? (
+              <Chart />
+            ) : detail.type === 'label' ? (
+              <Panel detail={detail} onClose={handleClosePanel} />
+            ) : detail.type === 'rtf' ? (
+              <Panel2 detail={detail} onClose={handleClosePanel} />
+            ) : detail.type === 5 ? (
+              <ModelPanel detail={detail} onClose={handleClosePanel} />
+            ) : (
+              <Panel2 detail={detail} onClose={handleClosePanel} />
+            )}
+          </>
         )}
 
         <div
           className={`${styles.sidebarHideBtn} ${!sidebarVisible ? styles.hide : ''}`}
-          onClick={() => setSidebarVisible(!sidebarVisible)}
+          onClick={toggleSidebar}
         ></div>
       </div>
     </div>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1051 - 481
yarn.lock