瀏覽代碼

历史回顾 图表 需求变更

任一存 2 年之前
父節點
當前提交
a6a21f3b9c
共有 3 個文件被更改,包括 579 次插入374 次删除
  1. 259 150
      public/chart.html
  2. 259 158
      public/chart4app.html
  3. 61 66
      src/views/History.vue

+ 259 - 150
public/chart.html

@@ -21,125 +21,245 @@
   <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
 
   <script type="text/javascript">
-    const timeList = [
-      '开埠通商',
-      '曲折发展',
-      '步履维艰',
-      '筚路蓝缕',
-      '改革开放',
-      '战略负重',
-      '创新驱动',
-      '追梦未来',
-    ]
-    
     function randomColor() {
       return '#' + Math.floor(
-          (
-            Math.random() * (1 - 0.3) + 0.3
-          ) * 0xffffff
-        ).toString(16)
+        (
+          Math.random() * (1 - 0.3) + 0.3
+        ) * 0xffffff
+      ).toString(16)
     }
-    
-    const dataForRender = {}
+ 
+    const rawInfo = [
+      {
+        name: '开埠通商',
+        color: randomColor(),
+        corpList: [
+          // {
+          //   companyName: "正泰橡皮物品制造厂",
+          //   createTime: "2023-08-03 14:21:45",
+          //   creatorId: null,
+          //   creatorName: "",
+          //   description: "创办正泰橡皮物品制造厂,首创“回力”商标。",
+          //   dirCode: "",
+          //   display: null,
+          //   fileIds: "",
+          //   id: 10,
+          //   name: "刘永康",
+          //   sort: null,
+          //   stage: "开埠通商",
+          //   story: "",
+          //   updateTime: "2023-08-03 14:21:45",
+          // }
+        ],
+        id: 'time-0'
+      },
+      {
+        name: '曲折发展',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-1'
+      },
+      {
+        name: '步履维艰',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-2'
+      },
+      {
+        name: '筚路蓝缕',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-3'
+      },
+      {
+        name: '改革开放',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-4'
+      },
+      {
+        name: '战略负重',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-5'
+      },
+      {
+        name: '创新驱动',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-6'
+      },
+      {
+        name: '追梦未来',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-7'
+      },
+    ]
+   
+    const nodesForRender = [
+      {
+        name: "历史回顾",
+        level: 0,
+        category: null,
+        symbolSize: 100,
+        itemStyle: {
+          color: {
+            type: "radial",
+            x: 0.5,
+            y: 0.5,
+            r: 0.5,
+            colorStops: [
+              {
+                offset: 0,
+                color: "#00c7ef"
+              },
+              {
+                offset: 0.8,
+                color: "#00c7ef"
+              },
+              {
+                offset: 1,
+                color: "rgba(0, 0, 0, 0.3)"
+              }
+            ],
+            global: false
+          },
+          shadowColor: "rgba(255, 255, 255, 0.5)",
+          shadowBlur: 10
+        },
+        label: {
+          position: 'inside',
+          show: true,
+          color: '#fff',
+          fontSize: '18px',
+        }
+      },
+    ]
+    const edgesForRender = []
 
-    let myChart = null
-    
-    var dom = document.getElementById('container');
-    myChart = echarts.init(dom, null, {
-      renderer: 'canvas',
-      useDirtyRect: false
-    });
-    
-    async function showChart(timeIdx) {
-      myChart.clear()
-      
-      const res = await axios({
+    Promise.allSettled(rawInfo.map((timeInfoItem) => {
+      return axios({
         method: 'post',
         url: `https://sit-shgybwg.4dage.com/api/show/history/pageList`,
         headers: {
           "Content-Type": "application/json",
         },
         data: {
-          stage: timeList[timeIdx]
+          stage: timeInfoItem.name
         },
+      }).then((res) => {
+        return res.data.data.records
+      }).then((res) => {
+        timeInfoItem.corpList = res
       })
-      const corpList = res.data.data.records
-
-      dataForRender.nodes = [
-        {
-          name: timeList[timeIdx],
-          id: '-1',
-          symbolSize: 100,
+    })).then((res) => {
+      for (const timeInfoItem of rawInfo) {
+        nodesForRender.push({
+          name: timeInfoItem.name,
+          level: 1,
+          category: timeInfoItem.name,
+          symbolSize: 50,
           itemStyle: {
-            color: randomColor()
+            color: timeInfoItem.color,
+            shadowColor: "rgba(255, 255, 255, 0.5)",
+            shadowBlur: 10
           },
-        },
-      ]
-      dataForRender.edges = []
-
-      for (const iterator of corpList) {
-        const newNode = {
-          name: iterator.name || iterator.companyName,
-          id: iterator.id.toString(),
-          symbolSize: iterator.importance || (100 * Math.random()),
-          itemStyle: {
-            color: randomColor()
+          label: {
+            position: 'right',
+            show: true,
+            color: '#fff',
+            fontSize: '16px',
           }
-        }
-        dataForRender.nodes.push(newNode)
-      }
-      // 除了代表时代的那个节点,其他节点的尺寸归一化
-      let vMax = Number.NEGATIVE_INFINITY
-      let vMin = Number.POSITIVE_INFINITY
-      const vMaxNew = 50
-      const vMinNew = 30
-      for (let index = 1; index < dataForRender.nodes.length; index++) {
-        const size = dataForRender.nodes[index].symbolSize
-        if (size > vMax) {
-          vMax = size
-        }
-        if (size < vMin) {
-          vMin = size
+        })
+        edgesForRender.push({
+          category: null,
+          lineStyle: {
+            normal: {
+              color: timeInfoItem.color,
+            },
+          },
+          source: '历史回顾',
+          target: timeInfoItem.name,
+        })
+        for (const corpItem of timeInfoItem.corpList) {
+          nodesForRender.push({
+            name: corpItem.name,
+            level: 2,
+            idForSend: corpItem.id,
+            category: timeInfoItem.name,
+            symbolSize: 10,
+            itemStyle: {
+              borderColor: timeInfoItem.color,
+              color: 'transparent',
+              shadowColor: "rgba(255, 255, 255, 0.5)",
+              shadowBlur: 10
+            },
+            select: {
+              itemStyle: {
+                borderColor: timeInfoItem.color,
+                color: timeInfoItem.color,
+              },
+            },
+            label: {
+              position: 'right',
+              show: true,
+              color: '#fff',
+              fontSize: '14px',
+            }
+          })
+          edgesForRender.push({
+            category: timeInfoItem.name,
+            lineStyle: {
+              normal: {
+                color: timeInfoItem.color,
+              },
+            },
+            source: timeInfoItem.name,
+            target: corpItem.name,
+          })
         }
       }
-      let scale = null
-      if (vMax === vMin) {
-        scale = 1
-        for (const iterator of dataForRender.nodes.slice(1)) {
-          iterator.symbolSize = vMinNew + (vMaxNew - vMinNew) / 2
-        }
+      console.log(nodesForRender);
+      console.log(edgesForRender);
+      showAll()
+    })
+    
+    let myChart = null
+    var dom = document.getElementById('container');
+    myChart = echarts.init(dom, null, {
+      renderer: 'canvas',
+      useDirtyRect: false
+    });
+    
+    function showAll() {
+      return showChart()
+    }
+
+    function changeTime(timeIdx) {
+      if (Number.isInteger(timeIdx) && timeIdx >= 0) {
+        return showChart(timeIdx)
       } else {
-        scale = (vMaxNew - vMinNew) / (vMax - vMin)
-        for (const iterator of dataForRender.nodes.slice(1)) {
-          iterator.symbolSize = vMinNew + (iterator.symbolSize - vMin) * scale
-        }
+        console.error('[page using echart] changeTime: invalid param!', timeIdx);
       }
-      // 画边
-      for (let i = 0; i < dataForRender.nodes.length; i++) {
-        for (let j = i + 1; j < dataForRender.nodes.length; j++) {
-          const hasCenterNode = (i === 0 || j === 0)
-          if (hasCenterNode) {
-            const newEdge = {
-              source: dataForRender.nodes[i].id,
-              target: dataForRender.nodes[j].id,
-              value: dataForRender.nodes[i].symbolSize * dataForRender.nodes[j].symbolSize * 10, // 值越大,连接的两个节点间斥力越小。
-            }
-            dataForRender.edges.push(newEdge)
-          } else {
-            const newEdge = {
-              source: dataForRender.nodes[i].id,
-              target: dataForRender.nodes[j].id,
-              value: dataForRender.nodes[i].symbolSize * dataForRender.nodes[j].symbolSize * 1, // 值越大,连接的两个节点间斥力越小。
-              lineStyle: {
-                opacity: 0,
-              }
-            }
-            dataForRender.edges.push(newEdge)
-          }
-        }
+    }
+
+    let nodesForRenderTemp = null
+    let edgesForRenderTemp = null
+    function showChart(timeIdx) {
+      if (timeIdx !== undefined) {
+        nodesForRenderTemp = nodesForRender.filter((item) => {
+          return item.category === rawInfo[timeIdx].name
+        })
+        edgesForRenderTemp = edgesForRender.filter((item) => {
+          return item.category === rawInfo[timeIdx].name
+        })
+      } else {
+        nodesForRenderTemp = null
+        edgesForRenderTemp = null
       }
-      // console.log(dataForRender);
-      
+
+      myChart.clear()
       myChart.setOption({
         animationDurationUpdate: 1500,
         animationEasingUpdate: 'quinticInOut',
@@ -147,88 +267,77 @@
           {
             type: 'graph',
             layout: 'force',
-            draggable: false,
             // 力引导布局是模拟弹簧电荷模型在每两个节点之间添加一个斥力,每条边的两个节点之间添加一个引力
             force: {
-              initLayout: 'circular', // 进行力引导布局前的初始化布局,初始化布局会影响到力引导的效果。默认不进行任何布局,使用节点中提供的 x, y 作为节点的位置。如果不存在的话会随机生成一个位置。也可以选择使用环形布局 'circular'。
-              repulsion: 100, // 节点之间的斥力因子。傻逼文档把edgeLength当数组用的用法写到这上边了。
-              gravity: 0.1, // 节点受到的向中心的引力因子。该值越大节点越往中心点靠拢。
-              edgeLength: [50, 400], // 把各个边的两个节点之间的距离归一化到这个范围内。与repulsion共同作用。
+              // initLayout: 'circular', // 进行力引导布局前的初始化布局,初始化布局会影响到力引导的效果。默认不进行任何布局,使用节点中提供的 x, y 作为节点的位置。如果不存在的话会随机生成一个位置。也可以选择使用环形布局 'circular'。
+              // repulsion: 100, // 节点之间的斥力因子。傻逼文档把edgeLength当数组用的用法写到这上边了。
+              repulsion: 300, // 节点之间的斥力因子。傻逼文档把edgeLength当数组用的用法写到这上边了。
+              // gravity: 0.1, // 节点受到的向中心的引力因子。该值越大节点越往中心点靠拢。
+              // edgeLength: [50, 400], // 把各个边的两个节点之间的距离归一化到这个范围内。与repulsion共同作用。
+              edgeLength: 100, // 把各个边的两个节点之间的距离归一化到这个范围内。与repulsion共同作用。
               layoutAnimation: true,
-              friction: 0.1, // 这个参数能减缓节点的移动速度。取值范围 0 到 1。但是仍然是个试验性的参数,参见 #11024。
+              friction: 0.5, // 这个参数能减缓节点的移动速度。取值范围 0 到 1。但是仍然是个试验性的参数,参见 #11024。
             },
-            data: dataForRender.nodes,
+            data: timeIdx === undefined ? nodesForRender : nodesForRenderTemp,
             // 或者叫edges
-            links: dataForRender.edges,
+            links: timeIdx === undefined ? edgesForRender : edgesForRenderTemp,
+            // 单选or多选or不可选
             selectedMode: 'single',
+            // 选中时的图形样式
             select: {
               itemStyle: {
-                shadowBlur: 50,
+                shadowBlur: 30,
                 shadowColor: 'rgba(255, 255, 125, 0.7)',
               },
               label: {
-                position: 'right',
-                show: true,
-                color: '#fff',
-                fontSize: '20px',
                 fontWeight: 'bold',
-              }
+              },
             },
-            // 高亮状态的图形样式
+            // hover时的图形样式
             emphasis: {
               scale: false,
-              label: {
-                position: 'right',
-                show: true,
-                color: '#fff',
-                fontSize: '20px',
-                fontWidth: 'bold',
-              }
             },
+            // hover时只照常显示有联系的那些节点和边,其余的暗色显示。
+            focusNodeAdjacency: true,
             // 图表是否可以移动、缩放
             roam: true,
+            scaleLimit: {
+                min: 0.5, //最小的缩放值
+                max: 3, //最大的缩放值
+            },
+            draggable: true,
             lineStyle: {
-              width: 0.5,
-              curveness: 0.3,
-              opacity: 0.7,
-            }
+              normal: {
+                width: 1.5,
+                curveness: 0,
+                type: "solid"
+              }
+            },
+            edgeSymbol: ["circle", "arrow"],
+            edgeSymbolSize: [4, 8],
           }
         ]
       }, true)
-
-      setTimeout(() => {
-        // 等myChart上注册了select回调后再执行
-        // 一开始自动选中表示时代的那个节点
-        myChart.dispatchAction({
-          type: 'select',
-          seriesIndex: 0,
-          name: timeList[timeIdx],
-        })
-      }, 0);
     }
 
-    showChart(0)
-
     // 用户选中节点后,向父窗口post message
     function onSelect(params) {
-      if (params.dataType === 'node') { // 用户选中节点触发的
-        window.parent.postMessage(`node-selected: ${dataForRender.nodes[params.dataIndexInside].id}`, '*')
-      } else if (params.type === 'select') { // 程序里调用dispatchAction触发的,且type为select
-        window.parent.postMessage(`node-selected: ${dataForRender.nodes[0].id}`, '*')
+      if (params.dataType !== 'node') {
+        return
       }
+      window.parent.postMessage({
+        msg: `node-selected`,
+        nodeLevel: nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].level : nodesForRender[params.dataIndexInside].level,
+        nodeStageName: nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].category : nodesForRender[params.dataIndexInside].category,
+        nodeStageIdx: rawInfo.findIndex((item) => {
+          return item.name === (nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].category : nodesForRender[params.dataIndexInside].category)
+        }),
+        nodeId: nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].idForSend : nodesForRender[params.dataIndexInside].idForSend,
+      }, '*')
     }
     myChart.on('select', onSelect)
 
     window.addEventListener('resize', myChart.resize);
-
-    // 切换时代
-    window.changeTime = function (idx) {
-      if (Number.isInteger(idx) && idx >= 0) {
-        showChart(idx)
-      } else {
-        console.error('[page using echart] changeTime: invalid param!', idx);
-      }
-    }
   </script>
 </body>
 </html>

+ 259 - 158
public/chart4app.html

@@ -29,125 +29,245 @@
   <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
 
   <script type="text/javascript">
-    const timeList = [
-      '开埠通商',
-      '曲折发展',
-      '步履维艰',
-      '筚路蓝缕',
-      '改革开放',
-      '战略负重',
-      '创新驱动',
-      '追梦未来',
-    ]
-
     function randomColor() {
       return '#' + Math.floor(
-          (
-            Math.random() * (1 - 0.3) + 0.3
-          ) * 0xffffff
-        ).toString(16)
+        (
+          Math.random() * (1 - 0.3) + 0.3
+        ) * 0xffffff
+      ).toString(16)
     }
-    
-    const dataForRender = {}
+ 
+    const rawInfo = [
+      {
+        name: '开埠通商',
+        color: randomColor(),
+        corpList: [
+          // {
+          //   companyName: "正泰橡皮物品制造厂",
+          //   createTime: "2023-08-03 14:21:45",
+          //   creatorId: null,
+          //   creatorName: "",
+          //   description: "创办正泰橡皮物品制造厂,首创“回力”商标。",
+          //   dirCode: "",
+          //   display: null,
+          //   fileIds: "",
+          //   id: 10,
+          //   name: "刘永康",
+          //   sort: null,
+          //   stage: "开埠通商",
+          //   story: "",
+          //   updateTime: "2023-08-03 14:21:45",
+          // }
+        ],
+        id: 'time-0'
+      },
+      {
+        name: '曲折发展',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-1'
+      },
+      {
+        name: '步履维艰',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-2'
+      },
+      {
+        name: '筚路蓝缕',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-3'
+      },
+      {
+        name: '改革开放',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-4'
+      },
+      {
+        name: '战略负重',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-5'
+      },
+      {
+        name: '创新驱动',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-6'
+      },
+      {
+        name: '追梦未来',
+        color: randomColor(),
+        corpList: [],
+        id: 'time-7'
+      },
+    ]
+   
+    const nodesForRender = [
+      {
+        name: "历史回顾",
+        level: 0,
+        category: null,
+        symbolSize: 100,
+        itemStyle: {
+          color: {
+            type: "radial",
+            x: 0.5,
+            y: 0.5,
+            r: 0.5,
+            colorStops: [
+              {
+                offset: 0,
+                color: "#00c7ef"
+              },
+              {
+                offset: 0.8,
+                color: "#00c7ef"
+              },
+              {
+                offset: 1,
+                color: "rgba(0, 0, 0, 0.3)"
+              }
+            ],
+            global: false
+          },
+          shadowColor: "rgba(255, 255, 255, 0.5)",
+          shadowBlur: 10
+        },
+        label: {
+          position: 'inside',
+          show: true,
+          color: '#fff',
+          fontSize: '18px',
+        }
+      },
+    ]
+    const edgesForRender = []
 
-    let myChart = null
-    
-    var dom = document.getElementById('container');
-    myChart = echarts.init(dom, null, {
-      renderer: 'canvas',
-      useDirtyRect: false
-    });
-    
-    async function showChart(timeIdx) {
-      myChart.clear()
-      
-      const res = await axios({
+    Promise.allSettled(rawInfo.map((timeInfoItem) => {
+      return axios({
         method: 'post',
         url: `https://sit-shgybwg.4dage.com/api/show/history/pageList`,
         headers: {
           "Content-Type": "application/json",
         },
         data: {
-          stage: timeList[timeIdx]
+          stage: timeInfoItem.name
         },
+      }).then((res) => {
+        return res.data.data.records
+      }).then((res) => {
+        timeInfoItem.corpList = res
       })
-      const corpList = res.data.data.records
-
-      dataForRender.nodes = [
-        {
-          name: timeList[timeIdx],
-          id: '-1',
-          symbolSize: 100,
+    })).then((res) => {
+      for (const timeInfoItem of rawInfo) {
+        nodesForRender.push({
+          name: timeInfoItem.name,
+          level: 1,
+          category: timeInfoItem.name,
+          symbolSize: 50,
           itemStyle: {
-            color: randomColor()
+            color: timeInfoItem.color,
+            shadowColor: "rgba(255, 255, 255, 0.5)",
+            shadowBlur: 10
           },
-        },
-      ]
-      dataForRender.edges = []
-
-      for (const iterator of corpList) {
-        const newNode = {
-          name: iterator.name || iterator.companyName,
-          id: iterator.id.toString(),
-          symbolSize: iterator.importance || (100 * Math.random()),
-          itemStyle: {
-            color: randomColor()
+          label: {
+            position: 'right',
+            show: true,
+            color: '#fff',
+            fontSize: '16px',
           }
-        }
-        dataForRender.nodes.push(newNode)
-      }
-      // 除了代表时代的那个节点,其他节点的尺寸归一化
-      let vMax = Number.NEGATIVE_INFINITY
-      let vMin = Number.POSITIVE_INFINITY
-      const vMaxNew = 50
-      const vMinNew = 30
-      for (let index = 1; index < dataForRender.nodes.length; index++) {
-        const size = dataForRender.nodes[index].symbolSize
-        if (size > vMax) {
-          vMax = size
-        }
-        if (size < vMin) {
-          vMin = size
+        })
+        edgesForRender.push({
+          category: null,
+          lineStyle: {
+            normal: {
+              color: timeInfoItem.color,
+            },
+          },
+          source: '历史回顾',
+          target: timeInfoItem.name,
+        })
+        for (const corpItem of timeInfoItem.corpList) {
+          nodesForRender.push({
+            name: corpItem.name,
+            level: 2,
+            idForSend: corpItem.id,
+            category: timeInfoItem.name,
+            symbolSize: 10,
+            itemStyle: {
+              borderColor: timeInfoItem.color,
+              color: 'transparent',
+              shadowColor: "rgba(255, 255, 255, 0.5)",
+              shadowBlur: 10
+            },
+            select: {
+              itemStyle: {
+                borderColor: timeInfoItem.color,
+                color: timeInfoItem.color,
+              },
+            },
+            label: {
+              position: 'right',
+              show: true,
+              color: '#fff',
+              fontSize: '14px',
+            }
+          })
+          edgesForRender.push({
+            category: timeInfoItem.name,
+            lineStyle: {
+              normal: {
+                color: timeInfoItem.color,
+              },
+            },
+            source: timeInfoItem.name,
+            target: corpItem.name,
+          })
         }
       }
-      let scale = null
-      if (vMax === vMin) {
-        scale = 1
-        for (const iterator of dataForRender.nodes.slice(1)) {
-          iterator.symbolSize = vMinNew + (vMaxNew - vMinNew) / 2
-        }
+      console.log(nodesForRender);
+      console.log(edgesForRender);
+      showAll()
+    })
+    
+    let myChart = null
+    var dom = document.getElementById('container');
+    myChart = echarts.init(dom, null, {
+      renderer: 'canvas',
+      useDirtyRect: false
+    });
+    
+    function showAll() {
+      return showChart()
+    }
+
+    function changeTime(timeIdx) {
+      if (Number.isInteger(timeIdx) && timeIdx >= 0) {
+        return showChart(timeIdx)
       } else {
-        scale = (vMaxNew - vMinNew) / (vMax - vMin)
-        for (const iterator of dataForRender.nodes.slice(1)) {
-          iterator.symbolSize = vMinNew + (iterator.symbolSize - vMin) * scale
-        }
+        console.error('[page using echart] changeTime: invalid param!', timeIdx);
       }
-      // 画边
-      for (let i = 0; i < dataForRender.nodes.length; i++) {
-        for (let j = i + 1; j < dataForRender.nodes.length; j++) {
-          const hasCenterNode = (i === 0 || j === 0)
-          if (hasCenterNode) {
-            const newEdge = {
-              source: dataForRender.nodes[i].id,
-              target: dataForRender.nodes[j].id,
-              value: dataForRender.nodes[i].symbolSize * dataForRender.nodes[j].symbolSize * 10, // 值越大,连接的两个节点间斥力越小。
-            }
-            dataForRender.edges.push(newEdge)
-          } else {
-            const newEdge = {
-              source: dataForRender.nodes[i].id,
-              target: dataForRender.nodes[j].id,
-              value: dataForRender.nodes[i].symbolSize * dataForRender.nodes[j].symbolSize * 1, // 值越大,连接的两个节点间斥力越小。
-              lineStyle: {
-                opacity: 0,
-              }
-            }
-            dataForRender.edges.push(newEdge)
-          }
-        }
+    }
+
+    let nodesForRenderTemp = null
+    let edgesForRenderTemp = null
+    function showChart(timeIdx) {
+      if (timeIdx !== undefined) {
+        nodesForRenderTemp = nodesForRender.filter((item) => {
+          return item.category === rawInfo[timeIdx].name
+        })
+        edgesForRenderTemp = edgesForRender.filter((item) => {
+          return item.category === rawInfo[timeIdx].name
+        })
+      } else {
+        nodesForRenderTemp = null
+        edgesForRenderTemp = null
       }
-      // console.log(dataForRender);
-      
+
+      myChart.clear()
       myChart.setOption({
         animationDurationUpdate: 1500,
         animationEasingUpdate: 'quinticInOut',
@@ -155,96 +275,77 @@
           {
             type: 'graph',
             layout: 'force',
-            draggable: false,
             // 力引导布局是模拟弹簧电荷模型在每两个节点之间添加一个斥力,每条边的两个节点之间添加一个引力
             force: {
-              initLayout: 'circular', // 进行力引导布局前的初始化布局,初始化布局会影响到力引导的效果。默认不进行任何布局,使用节点中提供的 x, y 作为节点的位置。如果不存在的话会随机生成一个位置。也可以选择使用环形布局 'circular'。
-              repulsion: 100, // 节点之间的斥力因子。傻逼文档把edgeLength当数组用的用法写到这上边了。
-              gravity: 0.1, // 节点受到的向中心的引力因子。该值越大节点越往中心点靠拢。
-              edgeLength: [50, 400], // 把各个边的两个节点之间的距离归一化到这个范围内。与repulsion共同作用。
+              // initLayout: 'circular', // 进行力引导布局前的初始化布局,初始化布局会影响到力引导的效果。默认不进行任何布局,使用节点中提供的 x, y 作为节点的位置。如果不存在的话会随机生成一个位置。也可以选择使用环形布局 'circular'。
+              // repulsion: 100, // 节点之间的斥力因子。傻逼文档把edgeLength当数组用的用法写到这上边了。
+              repulsion: 300, // 节点之间的斥力因子。傻逼文档把edgeLength当数组用的用法写到这上边了。
+              // gravity: 0.1, // 节点受到的向中心的引力因子。该值越大节点越往中心点靠拢。
+              // edgeLength: [50, 400], // 把各个边的两个节点之间的距离归一化到这个范围内。与repulsion共同作用。
+              edgeLength: 100, // 把各个边的两个节点之间的距离归一化到这个范围内。与repulsion共同作用。
               layoutAnimation: true,
-              friction: 0.1, // 这个参数能减缓节点的移动速度。取值范围 0 到 1。但是仍然是个试验性的参数,参见 #11024。
+              friction: 0.5, // 这个参数能减缓节点的移动速度。取值范围 0 到 1。但是仍然是个试验性的参数,参见 #11024。
             },
-            data: dataForRender.nodes,
+            data: timeIdx === undefined ? nodesForRender : nodesForRenderTemp,
             // 或者叫edges
-            links: dataForRender.edges,
+            links: timeIdx === undefined ? edgesForRender : edgesForRenderTemp,
+            // 单选or多选or不可选
             selectedMode: 'single',
+            // 选中时的图形样式
             select: {
               itemStyle: {
-                shadowBlur: 50,
+                shadowBlur: 30,
                 shadowColor: 'rgba(255, 255, 125, 0.7)',
               },
               label: {
-                position: 'right',
-                show: true,
-                color: '#fff',
-                fontSize: '20px',
                 fontWeight: 'bold',
-              }
+              },
             },
-            // 高亮状态的图形样式
+            // hover时的图形样式
             emphasis: {
               scale: false,
-              label: {
-                position: 'right',
-                show: true,
-                color: '#fff',
-                fontSize: '20px',
-                fontWidth: 'bold',
-              }
             },
+            // hover时只照常显示有联系的那些节点和边,其余的暗色显示。
+            focusNodeAdjacency: true,
             // 图表是否可以移动、缩放
             roam: true,
+            scaleLimit: {
+                min: 0.5, //最小的缩放值
+                max: 3, //最大的缩放值
+            },
+            draggable: true,
             lineStyle: {
-              width: 0.5,
-              curveness: 0.3,
-              opacity: 0.7,
-            }
+              normal: {
+                width: 1.5,
+                curveness: 0,
+                type: "solid"
+              }
+            },
+            edgeSymbol: ["circle", "arrow"],
+            edgeSymbolSize: [4, 8],
           }
         ]
       }, true)
-
-      setTimeout(() => {
-        // 等myChart上注册了select回调后再执行
-        // 一开始自动选中表示时代的那个节点
-        myChart.dispatchAction({
-          type: 'select',
-          seriesIndex: 0,
-          name: timeList[timeIdx],
-        })
-      }, 0);
     }
 
-    showChart(0)
-
     // 用户选中节点后,向父窗口post message
     function onSelect(params) {
-      if (params.dataType === 'node') { // 用户选中节点触发的
-        if (window.vuplex) {
-          window.vuplex.postMessage({ type: 'select-id', message: dataForRender.nodes[params.dataIndexInside].id });
-        } else {
-          console.error('不存在window.vuplex!');
-        }
-      } else if (params.type === 'select') { // 程序里调用dispatchAction触发的,且type为select
-        if (window.vuplex) {
-          window.vuplex.postMessage({ type: 'select-id', message: dataForRender.nodes[0].id });
-        } else {
-          console.error('不存在window.vuplex!');
-        }
+      if (params.dataType !== 'node') {
+        return
       }
+      window.vuplex.postMessage({
+        msg: `node-selected`,
+        nodeLevel: nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].level : nodesForRender[params.dataIndexInside].level,
+        nodeStageName: nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].category : nodesForRender[params.dataIndexInside].category,
+        nodeStageIdx: rawInfo.findIndex((item) => {
+          return item.name === (nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].category : nodesForRender[params.dataIndexInside].category)
+        }),
+        nodeId: nodesForRenderTemp ? nodesForRenderTemp[params.dataIndexInside].idForSend : nodesForRender[params.dataIndexInside].idForSend,
+      })
     }
     myChart.on('select', onSelect)
 
     window.addEventListener('resize', myChart.resize);
-
-    // 切换时代
-    window.changeTime = function (idx) {
-      if (Number.isInteger(idx) && idx >= 0) {
-        showChart(idx)
-      } else {
-        console.error('[page using echart] changeTime: invalid param!', idx);
-      }
-    }
   </script>
 </body>
 </html>

+ 61 - 66
src/views/History.vue

@@ -7,7 +7,7 @@
     />
 
     <h1>
-      {{ timeNameList[activeTimeIdx] }}
+      {{ Number.isInteger(activeTimeIdx) ? timeNameList[activeTimeIdx] : '历史回顾' }}
     </h1>
     <menu>
       <img
@@ -37,7 +37,7 @@
       </ul>
     </menu>
 
-    <article>
+    <article v-if="articleTitle">
       <h2>{{ articleTitle }}</h2>
       <img
         class="splitter"
@@ -86,14 +86,13 @@ import {
   onBeforeUnmount,
   computed,
   watch,
+  watchEffect,
 } from 'vue'
 import dataRaw from "@/assets/mock/history.json"
 
 export default {
   name: 'HistoryView',
   setup () {
-    const myIframe = ref(null)
-
     const timeNameList = [
       '开埠通商',
       '曲折发展',
@@ -104,95 +103,91 @@ export default {
       '创新驱动',
       '追梦未来',
     ]
-    const corpList = reactive({
-      value: [],
+
+    const activeTimeIdx = ref(null)
+    const activeCorpId = ref('')
+
+    function onIframeMessage(e) {
+      if (e.data.msg !== 'node-selected') {
+        return
+      }
+      console.log(e)
+      if (e.data.nodeLevel === 0) {
+        activeTimeIdx.value = null
+        activeCorpId.value = null
+      } else if (e.data.nodeLevel === 1) {
+        activeTimeIdx.value = e.data.nodeStageIdx
+        activeCorpId.value = null
+      } else if (e.data.nodeLevel === 2) {
+        activeTimeIdx.value = e.data.nodeStageIdx
+        activeCorpId.value = e.data.nodeId
+      }
+    }
+    onMounted(() => {
+      window.addEventListener('message', onIframeMessage)
+    })
+    onBeforeUnmount(() => {
+      window.removeEventListener('message', onIframeMessage)
     })
 
-    const activeTimeIdx = ref(0)
     function onClickLeftArrow() {
       if (activeTimeIdx.value > 0) {
         activeTimeIdx.value--
+        activeCorpId.value = ''
       }
     }
     function onClickRightArrow() {
       if (activeTimeIdx.value < timeNameList.length - 1) {
         activeTimeIdx.value++
+        activeCorpId.value = ''
       }
     }
-    watch(activeTimeIdx, async (newV) => {
-      if (document.getElementById('iframe-echart')?.contentWindow?.changeTime) {
-        document.getElementById('iframe-echart').contentWindow.changeTime(newV)
-      }
-      corpList.value = await api.getHistoryList({ stage: timeNameList[newV] })
-    }, { immediate: true })
 
-    const activeCorpId = ref('')
-    const articleTitle = computed(() => {
-      if (activeCorpId.value === '') {
-        return ''
-      } else if (activeCorpId.value === '-1') {
-        return timeNameList[activeTimeIdx.value]
-      } else {
-        const targetCorp = corpList.value.find((item) => {
-          return item.id.toString() === activeCorpId.value.toString()
-        })
-        return targetCorp?.name || targetCorp?.companyName || ''
-      }
-    })
-    const articleDesc = computed(() => {
-      if (activeCorpId.value === '') {
-        return ''
-      } else if (activeCorpId.value === '-1') {
-        return dataRaw['阶段介绍'][activeTimeIdx.value]['介绍']
+    function onClickTimeItem(idx) {
+      if (activeTimeIdx.value === idx) {
+        activeTimeIdx.value = null
+        activeCorpId.value = null
+        if (document.getElementById('iframe-echart')?.contentWindow?.showAll) {
+          document.getElementById('iframe-echart').contentWindow.showAll()
+        }
       } else {
-        const targetCorp = corpList.value.find((item) => {
-          return item.id.toString() === activeCorpId.value.toString()
-        })
-        return targetCorp?.description || targetCorp?.story || ''
+        activeTimeIdx.value = idx
+        activeCorpId.value = null
+        if (document.getElementById('iframe-echart')?.contentWindow?.changeTime) {
+          document.getElementById('iframe-echart').contentWindow.changeTime(idx)
+        }
       }
-    })
+    }
+
+    const articleTitle = ref('')
+    const articleDesc = ref('')
     const articleBannerUrl = ref('')
-    watch(activeCorpId, async (newV) => {
-      if (newV === '') {
+    watchEffect(async () => {
+      if (Number.isInteger(activeTimeIdx.value) && !Number.isInteger(activeCorpId.value)) {
+        articleTitle.value = timeNameList[activeTimeIdx.value]
+        articleDesc.value = dataRaw['阶段介绍'][activeTimeIdx.value]['介绍']
         articleBannerUrl.value = ''
-      } else if (newV === '-1') {
-        articleBannerUrl.value = ''
-      } else {
-        const corpDetail = await api.getHistoryDetail(newV)
+      } else if (Number.isInteger(activeTimeIdx.value) && Number.isInteger(activeCorpId.value)) {
+        const corpDetail = await api.getHistoryDetail(activeCorpId.value)
+        articleTitle.value = corpDetail.entity?.name || corpDetail.entity?.companyName || ''
+        articleDesc.value = corpDetail.entity?.description || corpDetail.entity?.story || ''
         articleBannerUrl.value = corpDetail?.file[0]?.filePath ? process.env.VUE_APP_API_ORIGIN + corpDetail?.file[0]?.filePath : ''
+      } else {
+        articleTitle.value = ''
+        articleDesc.value = ''
+        articleBannerUrl.value = ''
       }
     })
-    watch(activeTimeIdx, () => {
-      articleBannerUrl.value = ''
-    })
-    function onClickTimeItem(idx) {
-      activeTimeIdx.value = idx
-      activeCorpId.value = ''
-    }
-
-    function onIframeMessage(e) {
-      console.log('iframe message: ', e.data)
-      if (e.data.startsWith && e.data.startsWith('node-selected: ')) {
-        activeCorpId.value = e.data.split('node-selected: ')[1]
-      }
-    }
-    onMounted(() => {
-      window.addEventListener('message', onIframeMessage)
-    })
-    onBeforeUnmount(() => {
-      window.removeEventListener('message', onIframeMessage)
-    })
 
     return {
       timeNameList,
       activeTimeIdx,
-      onClickLeftArrow,
-      onClickRightArrow,
-      activeCorpId,
+      onClickTimeItem,
       articleTitle,
       articleDesc,
       articleBannerUrl,
-      onClickTimeItem,
+      onClickLeftArrow,
+      onClickRightArrow,
     }
   }
 }