bug fix and add dockerfile (#9)

diff --git a/README.md b/README.md
index b208008..ba9b960 100644
--- a/README.md
+++ b/README.md
@@ -20,17 +20,21 @@
 -->
 
 # iotdb-tsfile-viewer
+<!--
 [![Main Mac and Linux](https://github.com/apache/iotdb/actions/workflows/main-unix.yml/badge.svg)](https://github.com/apache/iotdb/actions/workflows/main-unix.yml)
 [![Main Win](https://github.com/apache/iotdb/actions/workflows/main-win.yml/badge.svg)](https://github.com/apache/iotdb/actions/workflows/main-win.yml)
+-->
 # Outline
 - [Introduction](#Introduction)
 - [Quick Start](#quick-start)
     - [Prerequisites](#Prerequisites)
     - [Compile](#Compile)
 - [User Guide](#user-guide)
+- [Build Docker Image](#build-docker-image)
 - [Maintainers](#Maintainers)
 - [Contributing](#Contributing)
 - [Contributors](#Contributors)
+- [FAQ Summary](#faq-summary)
 # Introduction
 tsfile-viewer is a tool to view TSFILE. Currently, we support bit granularity parsing of TsFile and provide visual display.  
 we have three modules in the project
@@ -103,6 +107,14 @@
   web:
      baseDirectory: C:\Users\Administrator\Desktop\
 ```
+The system can load up to 5 tsfile files by default, you can modify this value through application.yml  
+```
+tsfile-viewer-web\src\main\resources\application.yml
+
+tsviewer:
+  web:
+     containerSize: 5
+```
 
 # User Guide
 
@@ -150,6 +162,16 @@
 Data Search function:  
 ![image](/imgs/datasearch.png) 
 
+# Build Docker Image
+There is a dockerfile in tsfile-viewer-web, through which you can easily build a docker image.  
+After you have successfully executed the 'mvn clean install' command,enter the tsfile-viewer-web project and execute the following command  
+```
+docker build -t iotdb-tsfile-viewer:0.13.2-SNAPSHOT .
+docker run --volume=D:\tsfile:/tsfile -p 8080:8080 -d iotdb-tsfile-viewer:0.13.2-SNAPSHOT
+```
+
+docker.yml is the corresponding configuration file in docker image.
+If you want to modify the folder path of tsfile, you need to modify docker.yml, dockerfile and the --volume parameter of the dock run command.
 
 # Maintainers
 
@@ -157,3 +179,7 @@
 Feel free to dive in! Open an issue or submit PRs.
 # Contributors
 This project exists thanks to all the people who contribute.
+
+# FAQ Summary
+- 1 After the project is cloned, use mvn clean install to report a spotless exception; usually found on windows, the reason is that when git clones the project on windows, LF will be converted to CRLF, in this case, execute the mvn spotless:apply command to solve the problem
+- 2 Some front-end components cannot be downloaded, enter the tsfile-viewer-web-frontend project, enter the node folder, and execute the command ".\yarn\dist\bin\yarn set registry http://registry.npm.taobao.org/" switch to Taobao mirror
diff --git a/tsfile-viewer-web-frontend/config/config.js b/tsfile-viewer-web-frontend/config/config.js
index 1d49193..4608063 100644
--- a/tsfile-viewer-web-frontend/config/config.js
+++ b/tsfile-viewer-web-frontend/config/config.js
@@ -49,6 +49,10 @@
   // umi routes: https://umijs.org/docs/routing
   routes: [
     {
+      path: '/',
+      component: 'Welcome'
+    },
+    {
       path: '/tsfile-tool/v2/',
       name: 'tsfile-tool',
       icon: 'FileOutlined', 
diff --git a/tsfile-viewer-web-frontend/pom.xml b/tsfile-viewer-web-frontend/pom.xml
index 34d9b9e..c3b5f63 100644
--- a/tsfile-viewer-web-frontend/pom.xml
+++ b/tsfile-viewer-web-frontend/pom.xml
@@ -57,6 +57,11 @@
 						<phase>generate-resources</phase>
 					</execution>
 					<execution>
+						<id></id>
+						<goals></goals>
+						<phase></phase>
+					</execution>
+					<execution>
 						<id>yarn install</id>
 						<goals>
 							<goal>yarn</goal>
diff --git a/tsfile-viewer-web-frontend/src/app.jsx b/tsfile-viewer-web-frontend/src/app.jsx
index 8800132..d48ac05 100644
--- a/tsfile-viewer-web-frontend/src/app.jsx
+++ b/tsfile-viewer-web-frontend/src/app.jsx
@@ -16,10 +16,10 @@
  */
 import { PageLoading } from '@ant-design/pro-layout';
 import { message } from 'antd';
-import { history,SelectLang } from 'umi';
+import { history, SelectLang } from 'umi';
 import Footer from '@/components/Footer';
 import { forwardRef } from 'react';
-const fowardPath = '/tsfile-tool/v2/';
+const fowardPath = '/';
 /** 获取用户信息比较慢的时候会展示一个 loading */
 
 export const initialStateConfig = {
@@ -67,10 +67,10 @@
 export const request = {
   errorHandler: (error) => {
     const { response } = error;
-    if(response.status == '404'){
+    if (response.status == '404') {
       history.push("/exception/404")
     }
-    if(response.status == '500' || response.status == '502' || response.status == '503' || response.status == '504'){
+    if (response.status == '500' || response.status == '502' || response.status == '503' || response.status == '504') {
       history.push("/exception/500")
     }
     throw error;
@@ -85,15 +85,15 @@
     onPageChange: () => {
       const { location } = history; // 如果没有登录,重定向到 login
       let pathname = location.pathname.endsWith('/') ?
-       location.pathname.substring(0, location.pathname.length - 1):
-       location.pathname;
-      if(pathname == ''){
+        location.pathname.substring(0, location.pathname.length - 1) :
+        location.pathname;
+      if (pathname == '') {
         history.push(fowardPath);
       }
     },
     menuHeaderRender: undefined,
     itemRender: (route, params, routes) => {
-      if(routes.indexOf(route) == route.length -1){
+      if (routes.indexOf(route) == route.length - 1) {
         return <span>{route.breadcrumbName}</span>;
       }
       return <span>{route.breadcrumbName}</span>;
diff --git a/tsfile-viewer-web-frontend/src/locales/en-US.js b/tsfile-viewer-web-frontend/src/locales/en-US.js
index ff117bd..6e5e636 100644
--- a/tsfile-viewer-web-frontend/src/locales/en-US.js
+++ b/tsfile-viewer-web-frontend/src/locales/en-US.js
@@ -26,6 +26,17 @@
   'app.preview.down.block': 'Download this page to your local project',
   'app.welcome.link.fetch-blocks': 'Get all block',
   'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
+  'pages.welcome.message':'Welcome to tsfile viewer',
+  'pages.welcome.alertMessage':'The front-end project build depends on antd and antdpro',
+  'pages.welcome.antd':'Antd URL',
+  'pages.welcome.antdpro':'Antd pro URL',
+  'pages.welcome.git':'The git address of this project',
+  'overview.explanation': 'explanation',
+  'overview.magicNumber': 'magic number',
+  'overview.version': 'version',
+  'overview.endExplanation': 'The character TSFILE at the end of the file, marking the end of the file,',
+  'overview.structureDescription': 'Structure Description',
+  'overview.details': 'Details',
   ...menu,
   ...pwa,
   ...tsviewer,
diff --git a/tsfile-viewer-web-frontend/src/locales/en-US/menu.js b/tsfile-viewer-web-frontend/src/locales/en-US/menu.js
index c7a9093..447196c 100644
--- a/tsfile-viewer-web-frontend/src/locales/en-US/menu.js
+++ b/tsfile-viewer-web-frontend/src/locales/en-US/menu.js
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 export default {
-  'menu.platform.file-preview': 'File Preview',
+  'menu.tsfile-tool': 'Tsfile viewer',
   'menu.exception.403': '403',
   'menu.exception.404': '404',
   'menu.exception.500': '500',
diff --git a/tsfile-viewer-web-frontend/src/locales/zh-CN.js b/tsfile-viewer-web-frontend/src/locales/zh-CN.js
index 3c18f01..6e4fcea 100644
--- a/tsfile-viewer-web-frontend/src/locales/zh-CN.js
+++ b/tsfile-viewer-web-frontend/src/locales/zh-CN.js
@@ -26,6 +26,17 @@
   'app.preview.down.block': '下载此页面到本地项目',
   'app.welcome.link.fetch-blocks': '获取全部区块',
   'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
+  'pages.welcome.message':'欢迎使用tsfile viewer',
+  'pages.welcome.alertMessage':'前端项目构建依赖于antd和antdpro。',
+  'pages.welcome.antd':'antd 网址',
+  'pages.welcome.antdpro':'antd pro 网址',
+  'pages.welcome.git':'本项目的git地址',
+  'overview.explanation': '说明',
+  'overview.magicNumber': '魔数',
+  'overview.version': '版本',
+  'overview.endExplanation': '文件末尾的字符TSFILE,标记文件结束,',
+  'overview.structureDescription': '结构说明',
+  'overview.details': '内容详情',
   ...menu,
   ...pwa,
   ...tsviewer,
diff --git a/tsfile-viewer-web-frontend/src/pages/Welcome.jsx b/tsfile-viewer-web-frontend/src/pages/Welcome.jsx
index 5cfad7c..ad61577 100644
--- a/tsfile-viewer-web-frontend/src/pages/Welcome.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/Welcome.jsx
@@ -31,12 +31,25 @@
 export default () => {
   const intl = useIntl();
   return (
-    <PageContainer>
+    <PageContainer
+      fixedHeader
+      header={{
+        title: (
+          <div style={{ whiteSpace: "pre-wrap" }}>
+            {intl.formatMessage({
+              id: 'pages.welcome.message',
+              defaultMessage: 'Welcome to Tsfile viewer',
+            })}
+          </div>
+        ),
+        ghost: true,
+      }}
+    >
       <Card>
         <Alert
           message={intl.formatMessage({
             id: 'pages.welcome.alertMessage',
-            defaultMessage: 'Faster and stronger heavy-duty components have been released.',
+            defaultMessage: 'The front-end project build depends on antd and antdpro',
           })}
           type="success"
           showIcon
@@ -47,32 +60,33 @@
           }}
         />
         <Typography.Text strong>
-          <FormattedMessage id="pages.welcome.advancedComponent" defaultMessage="Advanced Form" />{' '}
-          <a
-            href="https://procomponents.ant.design/components/table"
-            rel="noopener noreferrer"
-            target="__blank"
-          >
-            <FormattedMessage id="pages.welcome.link" defaultMessage="Welcome" />
-          </a>
+          <FormattedMessage id="pages.welcome.antd" defaultMessage={intl.formatMessage({
+            id: 'pages.welcome.antd',
+            defaultMessage: 'antd URL',
+          })} />{' '}
         </Typography.Text>
-        <CodePreview>yarn add @ant-design/pro-table</CodePreview>
+        <CodePreview>https://ant.design/index-cn</CodePreview>
+
+        <Typography.Text strong>
+          <FormattedMessage id="pages.welcome.antdpro" defaultMessage={intl.formatMessage({
+            id: 'pages.welcome.antdpro',
+            defaultMessage: ' antd pro URL',
+          })} />{' '}
+        </Typography.Text>
+        <CodePreview>https://pro.ant.design/zh-CN/</CodePreview>
+
         <Typography.Text
           strong
           style={{
             marginBottom: 12,
           }}
         >
-          <FormattedMessage id="pages.welcome.advancedLayout" defaultMessage="Advanced layout" />{' '}
-          <a
-            href="https://procomponents.ant.design/components/layout"
-            rel="noopener noreferrer"
-            target="__blank"
-          >
-            <FormattedMessage id="pages.welcome.link" defaultMessage="Welcome" />
-          </a>
+          <FormattedMessage id="pages.welcome.git" defaultMessage={intl.formatMessage({
+            id: 'pages.welcome.git',
+            defaultMessage: 'The git address of this project',
+          })} />{' '}
         </Typography.Text>
-        <CodePreview>yarn add @ant-design/pro-layout</CodePreview>
+        <CodePreview>https://github.com/apache/iotdb-tsfile-viewer</CodePreview>
       </Card>
     </PageContainer>
   );
diff --git a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/fileSelect/fileSelect.jsx b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/fileSelect/fileSelect.jsx
index 18f5eb1..e6c7dfa 100644
--- a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/fileSelect/fileSelect.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/fileSelect/fileSelect.jsx
@@ -82,6 +82,7 @@
             filters: [
                 { text: intl.formatMessage({ id: 'tsviewer.fileSelect.loaded', }), value: "LOADED" },
             ],
+            filteredValue: tableFilters == undefined ? null : tableFilters.status || null,
             render(text, record, index) {
                 if (record.status == 'EXCLUDED') {
                     return
@@ -155,6 +156,7 @@
             setBreadData(arr.map((item, key) => {
                 return <><div style={{ cursor: "pointer", background: "#f7e0e0" }}><Breadcrumb.Item key={key} onClick={() => { openDrictory(generateBreadArray(arr, key)) }}>{item}</Breadcrumb.Item></div><Breadcrumb.Separator>{'>'}</Breadcrumb.Separator></>;
             }))
+            setTableFilters();
         } else {
             notification.error({
                 message: res.message,
@@ -275,6 +277,7 @@
     }
 
     const handelTableChanged = async (pagenation, filters) => {
+        console.log(filters)
         setTableFilters(filters)
         setIsSameFolder(false)
     }
diff --git a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreChunkGroup.jsx b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreChunkGroup.jsx
index 6dd90f1..3139b0f 100644
--- a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreChunkGroup.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreChunkGroup.jsx
@@ -17,13 +17,14 @@
 import React, { useState, useEffect } from "react";
 import { Card, Layout, Button, Drawer, Tree, Input, PageHeader, notification, Pagination, Table, Tooltip } from 'antd';
 import styles from '../style.less'
-import { GroupOutlined, RetweetOutlined, CopyOutlined, QuestionCircleOutlined } from '@ant-design/icons';
+import { GroupOutlined, RetweetOutlined, MinusSquareOutlined, PlusSquareOutlined, CopyOutlined, QuestionCircleOutlined } from '@ant-design/icons';
 import {
     getChunkGroupsListUsingPOST, getChunkGroupInfoUsingPOST, getChunkListUsingPOST, getPageListUsingPOST
     , getPageInfoUsingPOST
 } from '@/services/swagger1/tsfileViewerController'
 import moment from 'moment';
 import { useIntl } from 'umi';
+import { Tree as TreeArborist } from "react-arborist";
 
 const { Header, Footer, Sider, Content } = Layout;
 const { Search } = Input;
@@ -42,6 +43,9 @@
     const [columnsLength, setColumnsLength] = useState();
     const [pageData, setPageData] = useState()
     const intl = useIntl();
+    const [structureMapLoading, setStructureMapLoading] = useState();
+    const [treeHeight, setTreeHeight] = useState()
+    const [randomFlag, setRandomFlag] = useState()
 
     var pageDataCache;
 
@@ -70,17 +74,24 @@
         if (details == undefined) {
             return
         }
+        setStructureMapLoading(true)
         let res = await getChunkListUsingPOST({ offset: details.offset, filePath: filePath, offsetType: 'CG' })
         if (res.code == 0) {
-            setTreeData(Object.values(res.data).map((chunkInfo) => {
+            setRandomFlag(moment(new Date()).valueOf())
+            let tree = Object.values(res.data).map((node) => {
                 let tree = {};
-                tree['title'] = chunkInfo.measurementId;
-                tree['key'] = chunkInfo.offset;
-                tree['icon'] = <GroupOutlined />
+                tree['name'] = node.measurementId;
+                tree['id'] = node.measurementId + '-' + node.offset + randomFlag;
+                // tree['icon'] = <RightOutlined />
                 tree['isLeaf'] = false;
+                tree['position'] = node.offset;
                 return tree;
-            }))
+            })
+            setTreeData(tree)
             setopenChunk(true);
+            var div = document.getElementById('tree-div');
+            setTreeHeight(div.offsetHeight)
+            setStructureMapLoading(false);
         } else {
             notification.error({
                 message: res.message,
@@ -89,50 +100,6 @@
 
     };
 
-    const updateTreeData = (list, key, children) => {
-        return list.map((node) => {
-            if (node.key === key) {
-                return {
-                    ...node,
-                    children,
-                };
-            }
-            // 这个应该是用来加载子节点的子节点的,应该需要修改children对象
-            // if (node.children) {
-            //     return {
-            //         ...node,
-            //         children: updateTreeData(node.children, key, children),
-            //     };
-            // }
-            return node;
-        });
-    }
-
-
-    const onLoadData = async ({ key, children }) => {
-        let res = await getPageListUsingPOST({ offset: key, filePath: filePath })
-
-        if (res.code == 0) {
-            let pages = Object.values(res.data).map((pageInfo) => {
-                let tree = {};
-                tree['title'] = pageInfo.pageNo;
-                tree['key'] = pageInfo.offset;
-                tree['icon'] = <CopyOutlined />
-                tree['isLeaf'] = true;
-                tree['pageInfo'] = pageInfo;
-                return tree;
-            })
-            setTreeData(origin =>
-                updateTreeData(origin, key, pages),
-            );
-        } else {
-            notification.error({
-                message: res.message,
-            });
-        }
-
-    }
-
     const onCloseChunk = () => {
         setopenChunk(false);
     };
@@ -145,55 +112,57 @@
         setopenPage(false);
     };
 
-    const onSelect = async (selectedKeys, info) => {
-        if (info.node.isLeaf == true) {
-            let param = info.node.pageInfo;
-            param['chunkGroupOffset'] = details.offset;
-            param['filePath'] = filePath;
-            let res = await getPageInfoUsingPOST(param)
-            if (res.code == 0) {
-                //pagedata信息
-                let cols = [{
-                    title: 'No',
-                    fixed: 'left',
-                    width: '100px',
-                    // render: (text, record, index) => `${index + 1}`,  //每一页都从1开始
-                    render: (text, record, index) => {
-                        return index + 1
-                    }
+    const onSelect = async (info) => {
+        if (!info.isLeaf) {
+            return
+        }
+        let param = info.pageInfo;
+        param['chunkGroupOffset'] = details.offset;
+        param['filePath'] = filePath;
+        let res = await getPageInfoUsingPOST(param)
+        if (res.code == 0) {
+            //pagedata信息
+            let cols = [{
+                title: 'No',
+                fixed: 'left',
+                width: '100px',
+                // render: (text, record, index) => `${index + 1}`,  //每一页都从1开始
+                render: (text, record, index) => {
+                    return index + 1
+                }
 
-                }]
-                cols.push(...Object.values(res.data.title).map((titleName, key) => {
-                    if (titleName == 'timestamp') {
-                        return {
-                            title: (
+            }]
+            cols.push(...Object.values(res.data.title).map((titleName, key) => {
+                if (titleName == 'timestamp') {
+                    return {
+                        title: (
+                            <>
+                                {titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
+                                <RetweetOutlined
+                                    onClick={() => {
+                                        pageDataCache = Object.values(pageDataCache).map((item) => {
+                                            if ((item[0] + "").indexOf("-") > -1) {
+                                                item[0] = moment(item[0], 'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
+                                            } else {
+                                                item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
+                                            }
+                                            return item
+                                        })
+                                        setPageData(pageDataCache)
+                                    }}
+                                />
+                            </>),
+                        dataIndex: titleName,
+                        key: titleName,
+                        fixed: 'left',
+                        width: '250px',
+                        render: (text, record, index) => {
+                            return (
                                 <>
-                                    {titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
-                                    <RetweetOutlined
-                                        onClick={() => {
-                                            pageDataCache = Object.values(pageDataCache).map((item)=>{
-                                                if((item[0]+"").indexOf("-") > -1){
-                                                    item[0] = moment(item[0],'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
-                                                } else {
-                                                    item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                                                }
-                                                return item
-                                            })
-                                            setPageData(pageDataCache)
-                                        }}
-                                    />
-                                </>),
-                            dataIndex: titleName,
-                            key: titleName,
-                            fixed: 'left',
-                            width: '250px',
-                            render: (text, record, index) => {
-                                return (
-                                    <>
-                                        <span id={index}>
-                                            {record[key]}
-                                        </span>
-                                        {/* <span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
+                                    <span id={index}>
+                                        {record[key]}
+                                    </span>
+                                    {/* <span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
                                         <RetweetOutlined
                                             onClick={(e) => {
                                                 if (document.getElementById(index).innerText.indexOf("-") > -1) {
@@ -202,33 +171,32 @@
                                                     document.getElementById(index).innerText = moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
                                                 }
                                             }} /> */}
-                                    </>
-                                )
+                                </>
+                            )
 
-                                // return moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                            }
-                        }
-                    } else {
-                        return {
-                            title: titleName,
-                            dataIndex: titleName,
-                            key: titleName,
-                            render: (text, record, index) => {
-                                return record[key]
-                            }
+                            // return moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
                         }
                     }
-                }))
-                setColumnsLength(cols.length)
-                setColumns(cols)
-                setPageData(res.data.values);
-                pageDataCache = res.data.values;
-                showPage()
-            } else {
-                notification.error({
-                    message: res.message,
-                });
-            }
+                } else {
+                    return {
+                        title: titleName,
+                        dataIndex: titleName,
+                        key: titleName,
+                        render: (text, record, index) => {
+                            return record[key]
+                        }
+                    }
+                }
+            }))
+            setColumnsLength(cols.length)
+            setColumns(cols)
+            setPageData(res.data.values);
+            pageDataCache = res.data.values;
+            showPage()
+        } else {
+            notification.error({
+                message: res.message,
+            });
         }
     };
 
@@ -293,6 +261,61 @@
         cardSelect = id;
     }
 
+    const updateTreeData = (list, id, children) => {
+        return list.map((node) => {
+            if (node.id === id) {
+                return {
+                    ...node,
+                    children,
+                };
+            }
+            //这个应该是用来加载子节点的子节点的,应该需要修改children对象
+            if (node.children) {
+                return {
+                    ...node,
+                    children: updateTreeData(node.children, id, children),
+                };
+            }
+            return node;
+        });
+    }
+
+    const onLoadTreeData = async (expanded, node) => {
+        if (expanded && node.children == undefined) {
+            let res = await getPageListUsingPOST({ offset: node.position, filePath: filePath })
+            if (res.code == 0) {
+                let newTree = Object.values(res.data).map((child) => {
+                    let tree = {};
+                    tree['name'] = child.pageNo;
+                    tree['id'] = child.pageNo + '-' + child.offset + randomFlag;
+                    tree['isLeaf'] = true;
+                    tree['position'] = child.offset;
+                    tree['pageInfo'] = child;
+                    return tree;
+                })
+                let data = treeData;
+                let pa = updateTreeData(data, node.id, newTree);
+                setTreeData(pa)
+            } else {
+                notification.error({
+                    message: res.message,
+                });
+            }
+        }
+    }
+
+    function Node({ node, style, dragHandle, tree }) {
+        if (!node.isOpen && !node.data.isLeaf && node.data.children == undefined && node.isSelected) {
+            onLoadTreeData(!node.isOpen, node.data)
+        }
+        /* This node instance can do many things. See the API reference. */
+        return (
+            <div style={{ ...style, overflow: "hidden", width: "155vh", textOverflow: "ellipsis", whiteSpace: "nowrap" }} ref={dragHandle} onClick={() => (onSelect(node.data))}>
+                {node.data.isLeaf ? "" : node.isOpen ? <MinusSquareOutlined onClick={() => (node.toggle())} /> : <PlusSquareOutlined onClick={() => (node.toggle())} />} <span style={{ background: node.isSelected && node.data.isLeaf ? "#FFDFD4" : "white" }}>{node.data.isLeaf ? <CopyOutlined /> : <GroupOutlined />}{node.data.name}</span>
+            </div>
+        );
+    }
+
     useEffect(() => {
         generateChunkGroupCards(1, pageSize);
     }, [])
@@ -308,7 +331,7 @@
                                     placeholder="deviceName"
                                     allowClear
                                     onChange={(e) => { setDeviceNameLike(e.target.value) }}
-                                    onSearch={() => { generateChunkGroupCards(1, pageSize) }}
+                                    onSearch={() => { setCardList([]),generateChunkGroupCards(1, pageSize) }}
                                     style={{
                                         width: 500,
                                     }}
@@ -330,7 +353,7 @@
                         <PageHeader
                             style={{ background: "#f2f2f2" }}
                             extra={(
-                                <Button type="primary" onClick={() => showChunk(details)}>{intl.formatMessage({ id: 'tsviewer.more.structureMap', })}</Button>
+                                <Button type="primary" loading={structureMapLoading} onClick={() => showChunk(details)}>{intl.formatMessage({ id: 'tsviewer.more.structureMap', })}</Button>
                             )}>
                         </PageHeader>
                         <Details></Details>
@@ -356,14 +379,18 @@
                 onClose={onCloseChunk}
                 open={openChunk}
             >
-                <div style={{ height: "80vh", overflow: "auto" }}>
-                    <Tree
-                        showLine={{ showLeafIcon: false }}
-                        showIcon={true}
-                        onSelect={onSelect}
-                        loadData={onLoadData}
-                        treeData={treeData}
-                    />
+                <div id="tree-div" style={{ height: "80vh", background: "white", margin: '4px 0px 0px 0px' }}>
+                    <TreeArborist
+                        openByDefault={false}
+                        disableDrag={false}
+                        width={"100%"}
+                        height={treeHeight}
+                        // paddingBottom={200}
+                        //height={400}
+                        data={treeData}
+                    >
+                        {Node}
+                    </TreeArborist>
                 </div>
 
                 <Drawer
@@ -385,10 +412,10 @@
                     open={openPage}
                 >
                     <Table columns={columns} dataSource={pageData} scroll={{ x: 150 * columnsLength, y: "80vh" }}
-                        rowKey={(record)=>{
+                        rowKey={(record) => {
                             return record[0];
                         }}
-                        pagination={{ pageSize: 100, showQuickJumper: true, position: ["bottomCenter"] }}
+                        pagination={{ defaultPageSize: 100, showQuickJumper: true, position: ["bottomCenter"] }}
                         bordered />
                 </Drawer>
             </Drawer>
diff --git a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreIndexOfTimeseriesIndex.jsx b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreIndexOfTimeseriesIndex.jsx
index e76b266..4d10eea 100644
--- a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreIndexOfTimeseriesIndex.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreIndexOfTimeseriesIndex.jsx
@@ -45,6 +45,7 @@
     const [randomFlag, setRandomFlag] = useState()
     const [indexTreeHeight, setIndexTreeHeight] = useState()
     const { fileName, filePath, cardList, setCardList } = props;
+    const [treeHeight, setTreeHeight] = useState()
     const intl = useIntl();
 
     var pageDataCache;
@@ -64,16 +65,21 @@
         }
         let res = await getChunkListUsingPOST({ offset: node.position, filePath: filePath, offsetType: 'TS_INDEX', beginDate: beginDate, endDate: endDate })
         if (res.code == 0) {
-            setChunkTreeData(Object.values(res.data).map((chunkInfo) => {
+            setRandomFlag(moment(new Date()).valueOf())
+            let tree = Object.values(res.data).map((node) => {
                 let tree = {};
-                tree['title'] = chunkInfo.measurementId;
-                tree['key'] = chunkInfo.offset;
-                tree['icon'] = <GroupOutlined />
-                tree['timeseriesIndexOffset'] = node.position;
+                tree['name'] = node.measurementId;
+                tree['id'] = node.measurementId + '-' + node.offset + randomFlag;
+                // tree['icon'] = <RightOutlined />
                 tree['isLeaf'] = false;
+                tree['position'] = node.offset;
+                tree['timeseriesIndexOffset'] = node.position;
                 return tree;
-            }))
+            })
+            setChunkTreeData(tree)
             setopenChunk(true);
+            var div = document.getElementById('tree-div');
+            setTreeHeight(div.offsetHeight)
         } else {
             notification.error({
                 message: res.message,
@@ -81,93 +87,83 @@
         }
     };
 
-    const onChunkSelect = async (selectedKeys, info) => {
-        if (info.node.isLeaf == true) {
-            let param = info.node.pageInfo;
-            param['timeseriesIndexOffset'] = info.node.timeseriesIndexOffset;
-            param['chunkOffset'] = info.node.chunkOffset;
-            param['filePath'] = filePath;
-            param['beginDate'] = beginDate;
-            param['endDate'] = endDate;
-            let res = await getPageInfoThroughTimeseriesIndexOffsetUsingPOST(param)
-            if (res.code == 0) {
-                //pagedata信息
-                let cols = [{
-                    title: 'No',
-                    fixed: 'left',
-                    width: '100px',
-                    // render: (text, record, index) => `${index + 1}`,  //每一页都从1开始
-                    render: (text, record, index) => {
-                        return index + 1
-                    }
+    const onChunkSelect = async (info) => {
+        if (!info.isLeaf) {
+            return
+        }
+        let param = info.pageInfo;
+        param['timeseriesIndexOffset'] = info.timeseriesIndexOffset;
+        param['chunkOffset'] = info.chunkOffset;
+        param['filePath'] = filePath;
+        param['beginDate'] = beginDate;
+        param['endDate'] = endDate;
+        let res = await getPageInfoThroughTimeseriesIndexOffsetUsingPOST(param)
+        if (res.code == 0) {
+            //pagedata信息
+            let cols = [{
+                title: 'No',
+                fixed: 'left',
+                width: '100px',
+                // render: (text, record, index) => `${index + 1}`,  //每一页都从1开始
+                render: (text, record, index) => {
+                    return index + 1
+                }
 
-                }]
-                cols.push(...Object.values(res.data.title).map((titleName, key) => {
-                    if (titleName == 'timestamp') {
-                        return {
-                            title: (
+            }]
+            cols.push(...Object.values(res.data.title).map((titleName, key) => {
+                if (titleName == 'timestamp') {
+                    return {
+                        title: (
+                            <>
+                                {titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
+                                <RetweetOutlined
+                                    onClick={() => {
+                                        pageDataCache = Object.values(pageDataCache).map((item) => {
+                                            if ((item[0] + "").indexOf("-") > -1) {
+                                                item[0] = moment(item[0], 'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
+                                            } else {
+                                                item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
+                                            }
+                                            return item
+                                        })
+                                        setPageData(pageDataCache)
+                                    }}
+                                />
+                            </>),
+                        dataIndex: titleName,
+                        key: titleName,
+                        fixed: 'left',
+                        width: '250px',
+                        render: (text, record, index) => {
+                            return (
                                 <>
-                                    {titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
-                                    <RetweetOutlined
-                                        onClick={() => {
-                                            pageDataCache = Object.values(pageDataCache).map((item)=>{
-                                                if((item[0]+"").indexOf("-") > -1){
-                                                    item[0] = moment(item[0],'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
-                                                } else {
-                                                    item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                                                }
-                                                return item
-                                            })
-                                            setPageData(pageDataCache)
-                                        }}
-                                    />
-                                </>),
-                            dataIndex: titleName,
-                            key: titleName,
-                            fixed: 'left',
-                            width: '250px',
-                            render: (text, record, index) => {
-                                return (
-                                    <>
-                                        <span id={index}>
-                                            {record[key]}
-                                        </span>
-                                        {/* <span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
-                                        <RetweetOutlined
-                                            onClick={(e) => {
-                                                if (document.getElementById(index).innerText.indexOf("-") > -1) {
-                                                    document.getElementById(index).innerText = record[key]
-                                                } else {
-                                                    document.getElementById(index).innerText = moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                                                }
-                                            }} /> */}
-                                    </>
-                                )
-
-                                // return moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                            }
-                        }
-                    } else {
-                        return {
-                            title: titleName,
-                            dataIndex: titleName,
-                            key: titleName,
-                            render: (text, record, index) => {
-                                return record[key]
-                            }
+                                    <span id={index}>
+                                        {record[key]}
+                                    </span>
+                                </>
+                            )
                         }
                     }
-                }))
-                setColumnsLength(cols.length)
-                setColumns(cols)
-                setPageData(res.data.values);
-                pageDataCache = res.data.values;
-                showPage()
-            } else {
-                notification.error({
-                    message: res.message,
-                });
-            }
+                } else {
+                    return {
+                        title: titleName,
+                        dataIndex: titleName,
+                        key: titleName,
+                        render: (text, record, index) => {
+                            return record[key]
+                        }
+                    }
+                }
+            }))
+            setColumnsLength(cols.length)
+            setColumns(cols)
+            setPageData(res.data.values);
+            pageDataCache = res.data.values;
+            showPage()
+        } else {
+            notification.error({
+                message: res.message,
+            });
         }
     };
 
@@ -183,7 +179,7 @@
         setopenPage(false);
     };
 
-    const updateIndexTreeData = (list, id, children) => {
+    const updateTreeData = (list, id, children) => {
         return list.map((node) => {
             if (node.id === id) {
                 return {
@@ -195,56 +191,37 @@
             if (node.children) {
                 return {
                     ...node,
-                    children: updateIndexTreeData(node.children, id, children),
+                    children: updateTreeData(node.children, id, children),
                 };
             }
             return node;
         });
     }
 
-    const updateTreeData = (list, key, children) => {
-        return list.map((node) => {
-            if (node.key === key) {
-                return {
-                    ...node,
-                    children,
-                };
+    const onLoadTreeData = async (expanded, node) => {
+        if (expanded && node.children == undefined) {
+            let res = await getPageListUsingPOST({ offset: node.position, filePath: filePath, beginDate: beginDateCache, endDate: endDateCache })
+            if (res.code == 0) {
+                let newTree = Object.values(res.data).map((child) => {
+                    let tree = {};
+                    tree['name'] = child.pageNo;
+                    tree['id'] = child.pageNo + '-' + child.offset + randomFlag;
+                    tree['isLeaf'] = true;
+                    tree['position'] = child.offset;
+                    tree['chunkOffset'] = node.position;
+                    tree['timeseriesIndexOffset'] = node.timeseriesIndexOffset;
+                    tree['pageInfo'] = child;
+                    return tree;
+                })
+                let data = chunkTreeData;
+                let pa = updateTreeData(data, node.id, newTree);
+                setChunkTreeData(pa)
+            } else {
+                notification.error({
+                    message: res.message,
+                });
             }
-            //这个应该是用来加载子节点的子节点的,应该需要修改children对象
-            if (node.children) {
-                return {
-                    ...node,
-                    children: updateTreeData(node.children, key, children),
-                };
-            }
-            return node;
-        });
-    }
-
-
-    const onLoadChunkTreeData = async ({ key, children, timeseriesIndexOffset }) => {
-        let res = await getPageListUsingPOST({ offset: key, filePath: filePath, beginDate: beginDateCache, endDate: endDateCache })
-        if (res.code == 0) {
-            let pages = Object.values(res.data).map((pageInfo) => {
-                let tree = {};
-                tree['title'] = pageInfo.pageNo;
-                tree['key'] = pageInfo.offset;
-                tree['icon'] = <CopyOutlined />
-                tree['isLeaf'] = true;
-                tree['chunkOffset'] = key;
-                tree['pageInfo'] = pageInfo;
-                tree['timeseriesIndexOffset'] = timeseriesIndexOffset
-                return tree;
-            })
-            setChunkTreeData(origin =>
-                updateTreeData(origin, key, pages),
-            );
-        } else {
-            notification.error({
-                message: res.message,
-            });
         }
-
     }
 
     const onLoadIndexTreeData = async (expanded, node) => {
@@ -260,13 +237,8 @@
                     tree['position'] = child.position;
                     return tree;
                 })
-                // not work
-                // setIndexTreeData(origin =>
-                //     updateTreeData(origin, node.id, newTree),
-                // );
                 let data = indexTreeData;
-                let pa = updateIndexTreeData(data, node.id, newTree);
-                console.log(pa)
+                let pa = updateTreeData(data, node.id, newTree);
                 setIndexTreeData(pa)
             } else {
                 notification.error({
@@ -304,7 +276,19 @@
         /* This node instance can do many things. See the API reference. */
         return (
             <div style={{ ...style, overflow: "hidden", width: "155vh", textOverflow: "ellipsis", whiteSpace: "nowrap" }} ref={dragHandle} onClick={() => (showChunk(node.data))}>
-                {node.data.isLeaf ? "" : node.isOpen ? <MinusSquareOutlined onClick={() => (node.toggle())} /> : <PlusSquareOutlined onClick={() => (node.toggle())} />} <span style={{ background: node.isSelected ? "#FFDFD4" : "white" }}>{node.data.isLeaf ? <RightOutlined /> : ""}{node.data.name}</span>
+                {node.data.isLeaf ? "" : node.isOpen ? <MinusSquareOutlined onClick={() => (node.toggle())} /> : <PlusSquareOutlined onClick={() => (node.toggle())} />} <span style={{ background: node.isSelected && node.data.isLeaf ? "#FFDFD4" : "white" }}>{node.data.isLeaf ? <RightOutlined /> : ""}{node.data.name}</span>
+            </div>
+        );
+    }
+
+    function Node1({ node, style, dragHandle, tree }) {
+        if (!node.isOpen && !node.data.isLeaf && node.data.children == undefined && node.isSelected) {
+            onLoadTreeData(!node.isOpen, node.data)
+        }
+        /* This node instance can do many things. See the API reference. */
+        return (
+            <div style={{ ...style, overflow: "hidden", width: "155vh", textOverflow: "ellipsis", whiteSpace: "nowrap" }} ref={dragHandle} onClick={() => (onChunkSelect(node.data))}>
+                {node.data.isLeaf ? "" : node.isOpen ? <MinusSquareOutlined onClick={() => (node.toggle())} /> : <PlusSquareOutlined onClick={() => (node.toggle())} />} <span style={{ background: node.isSelected && node.data.isLeaf ? "#FFDFD4" : "white" }}>{node.data.isLeaf ? <CopyOutlined /> : <GroupOutlined />}{node.data.name}</span>
             </div>
         );
     }
@@ -440,16 +424,18 @@
                 onClose={onCloseChunk}
                 open={openChunk}
             >
-                {/* style={{ height: "80vh", overflow: "auto" }} */}
-                <div >
-                    <Tree
-                        height={'78vh'}
-                        showLine={{ showLeafIcon: false }}
-                        showIcon={true}
-                        onSelect={onChunkSelect}
-                        loadData={onLoadChunkTreeData}
-                        treeData={chunkTreeData}
-                    />
+                <div id="tree-div" style={{ height: "80vh", background: "white", margin: '4px 0px 0px 0px' }}>
+                    <TreeArborist
+                        openByDefault={false}
+                        disableDrag={false}
+                        width={"100%"}
+                        height={treeHeight}
+                        // paddingBottom={200}
+                        //height={400}
+                        data={chunkTreeData}
+                    >
+                        {Node1}
+                    </TreeArborist>
                 </div>
 
                 <Drawer
diff --git a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreTimeseriesIndex.jsx b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreTimeseriesIndex.jsx
index e145a7f..1c3c1ee 100644
--- a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreTimeseriesIndex.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/moreTimeseriesIndex.jsx
@@ -17,13 +17,14 @@
 import React, { useState, useEffect } from "react";
 import { Card, Layout, Button, Drawer, Tree, Input, Tooltip, DatePicker, Pagination, notification, Table, PageHeader } from 'antd';
 import styles from '../style.less'
-import { GroupOutlined, CopyOutlined, RetweetOutlined, QuestionCircleOutlined } from '@ant-design/icons';
+import { GroupOutlined, CopyOutlined, RetweetOutlined, MinusSquareOutlined, PlusSquareOutlined, QuestionCircleOutlined } from '@ant-design/icons';
 import {
     getTimeseriesIndexListUsingPOST, getChunkListUsingPOST, getPageListUsingPOST
     , getPageInfoThroughTimeseriesIndexOffsetUsingPOST, getTimeseriesIndexInfoUsingPOST
 } from '@/services/swagger1/tsfileViewerController'
 import moment from 'moment';
 import { useIntl } from 'umi';
+import { Tree as TreeArborist } from "react-arborist";
 
 const { Sider, Content } = Layout;
 const { Search } = Input;
@@ -50,6 +51,9 @@
     const [deviceNameLike, setDeviceNameLike] = useState()
     const { fileName, filePath, cardList, setCardList } = props;
     const intl = useIntl();
+    const [structureMapLoading, setStructureMapLoading] = useState();
+    const [treeHeight, setTreeHeight] = useState()
+    const [randomFlag, setRandomFlag] = useState()
 
     var pageDataCache;
 
@@ -78,17 +82,24 @@
         if (details == undefined) {
             return
         }
+        setStructureMapLoading(true)
         let res = await getChunkListUsingPOST({ offset: details.offset, filePath: filePath, offsetType: 'TS_INDEX', beginDate: beginDateCache, endDate: endDateCache })
         if (res.code == 0) {
-            setTreeData(Object.values(res.data).map((chunkInfo) => {
+            setRandomFlag(moment(new Date()).valueOf())
+            let tree = Object.values(res.data).map((node) => {
                 let tree = {};
-                tree['title'] = chunkInfo.measurementId;
-                tree['key'] = chunkInfo.offset;
-                tree['icon'] = <GroupOutlined />
+                tree['name'] = node.measurementId;
+                tree['id'] = node.measurementId + '-' + node.offset + randomFlag;
+                // tree['icon'] = <RightOutlined />
                 tree['isLeaf'] = false;
+                tree['position'] = node.offset;
                 return tree;
-            }))
+            })
+            setTreeData(tree)
             setopenChunk(true);
+            var div = document.getElementById('tree-div');
+            setTreeHeight(div.offsetHeight)
+            setStructureMapLoading(false)
         } else {
             notification.error({
                 message: res.message,
@@ -96,49 +107,60 @@
         }
     };
 
-    const updateTreeData = (list, key, children) => {
+    const updateTreeData = (list, id, children) => {
         return list.map((node) => {
-            if (node.key === key) {
+            if (node.id === id) {
                 return {
                     ...node,
                     children,
                 };
             }
-            // 这个应该是用来加载子节点的子节点的,应该需要修改children对象
-            // if (node.children) {
-            //     return {
-            //         ...node,
-            //         children: updateTreeData(node.children, key, children),
-            //     };
-            // }
+            //这个应该是用来加载子节点的子节点的,应该需要修改children对象
+            if (node.children) {
+                return {
+                    ...node,
+                    children: updateTreeData(node.children, id, children),
+                };
+            }
             return node;
         });
     }
 
-
-    const onLoadData = async ({ key, children }) => {
-        let res = await getPageListUsingPOST({ offset: key, filePath: filePath, beginDate: beginDateCache, endDate: endDateCache })
-
-        if (res.code == 0) {
-            let pages = Object.values(res.data).map((pageInfo) => {
-                let tree = {};
-                tree['title'] = pageInfo.pageNo;
-                tree['key'] = pageInfo.offset;
-                tree['icon'] = <CopyOutlined />
-                tree['isLeaf'] = true;
-                tree['chunkOffset'] = key;
-                tree['pageInfo'] = pageInfo;
-                return tree;
-            })
-            setTreeData(origin =>
-                updateTreeData(origin, key, pages),
-            );
-        } else {
-            notification.error({
-                message: res.message,
-            });
+    const onLoadTreeData = async (expanded, node) => {
+        if (expanded && node.children == undefined) {
+            let res = await getPageListUsingPOST({ offset: node.position, filePath: filePath, beginDate: beginDateCache, endDate: endDateCache })
+            if (res.code == 0) {
+                let newTree = Object.values(res.data).map((child) => {
+                    let tree = {};
+                    tree['name'] = child.pageNo;
+                    tree['id'] = child.pageNo + '-' + child.offset + randomFlag;
+                    tree['isLeaf'] = true;
+                    tree['position'] = child.offset;
+                    tree['chunkOffset'] = node.position;
+                    tree['pageInfo'] = child;
+                    return tree;
+                })
+                let data = treeData;
+                let pa = updateTreeData(data, node.id, newTree);
+                setTreeData(pa)
+            } else {
+                notification.error({
+                    message: res.message,
+                });
+            }
         }
+    }
 
+    function Node({ node, style, dragHandle, tree }) {
+        if (!node.isOpen && !node.data.isLeaf && node.data.children == undefined && node.isSelected) {
+            onLoadTreeData(!node.isOpen, node.data)
+        }
+        /* This node instance can do many things. See the API reference. */
+        return (
+            <div style={{ ...style, overflow: "hidden", width: "155vh", textOverflow: "ellipsis", whiteSpace: "nowrap" }} ref={dragHandle} onClick={() => (onSelect(node.data))}>
+                {node.data.isLeaf ? "" : node.isOpen ? <MinusSquareOutlined onClick={() => (node.toggle())} /> : <PlusSquareOutlined onClick={() => (node.toggle())} />} <span style={{ background: node.isSelected && node.data.isLeaf ? "#FFDFD4" : "white" }}>{node.data.isLeaf ? <CopyOutlined /> : <GroupOutlined />}{node.data.name}</span>
+            </div>
+        );
     }
 
     const onCloseChunk = () => {
@@ -153,92 +175,82 @@
         setopenPage(false);
     };
 
-    const onSelect = async (selectedKeys, info) => {
-        if (info.node.isLeaf == true) {
-            let param = info.node.pageInfo;
-            param['timeseriesIndexOffset'] = details.offset;
-            param['chunkOffset'] = info.node.chunkOffset;
-            param['filePath'] = filePath;
-            param['beginDate'] = beginDate;
-            param['endDate'] = endDate;
-            let res = await getPageInfoThroughTimeseriesIndexOffsetUsingPOST(param)
-            if (res.code == 0) {
-                //pagedata信息
-                let cols = [{
-                    title: 'No',
-                    fixed: 'left',
-                    width: '100px',
-                    // render: (text, record, index) => `${index + 1}`,  //每一页都从1开始
-                    render: (text, record, index) => {
-                        return index + 1
-                    }
-                }]
-                cols.push(...Object.values(res.data.title).map((titleName, key) => {
-                    if (titleName == 'timestamp') {
-                        return {
-                            title: (
+    const onSelect = async (info) => {
+        if (!info.isLeaf) {
+            return
+        }
+        let param = info.pageInfo;
+        param['timeseriesIndexOffset'] = details.offset;
+        param['chunkOffset'] = info.chunkOffset;
+        param['filePath'] = filePath;
+        param['beginDate'] = beginDate;
+        param['endDate'] = endDate;
+        let res = await getPageInfoThroughTimeseriesIndexOffsetUsingPOST(param)
+        if (res.code == 0) {
+            //pagedata信息
+            let cols = [{
+                title: 'No',
+                fixed: 'left',
+                width: '100px',
+                // render: (text, record, index) => `${index + 1}`,  //每一页都从1开始
+                render: (text, record, index) => {
+                    return index + 1
+                }
+            }]
+            cols.push(...Object.values(res.data.title).map((titleName, key) => {
+                if (titleName == 'timestamp') {
+                    return {
+                        title: (
+                            <>
+                                {titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
+                                <RetweetOutlined
+                                    onClick={() => {
+                                        pageDataCache = Object.values(pageDataCache).map((item) => {
+                                            if ((item[0] + "").indexOf("-") > -1) {
+                                                item[0] = moment(item[0], 'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
+                                            } else {
+                                                item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
+                                            }
+                                            return item
+                                        })
+                                        setPageData(pageDataCache)
+                                    }}
+                                />
+                            </>),
+                        dataIndex: titleName,
+                        key: titleName,
+                        fixed: 'left',
+                        width: '250px',
+                        render: (text, record, index) => {
+                            return (
                                 <>
-                                    {titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
-                                    <RetweetOutlined
-                                        onClick={() => {
-                                            pageDataCache = Object.values(pageDataCache).map((item)=>{
-                                                if((item[0]+"").indexOf("-") > -1){
-                                                    item[0] = moment(item[0],'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
-                                                } else {
-                                                    item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                                                }
-                                                return item
-                                            })
-                                            setPageData(pageDataCache)
-                                        }}
-                                    />
-                                </>),
-                            dataIndex: titleName,
-                            key: titleName,
-                            fixed: 'left',
-                            width: '250px',
-                            render: (text, record, index) => {
-                                return (
-                                    <>
-                                        <span id={index}>
-                                            {record[key]}
-                                        </span>
-                                        {/* <span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
-                                        <RetweetOutlined
-                                            onClick={(e) => {
-                                                if (document.getElementById(index).innerText.indexOf("-") > -1) {
-                                                    document.getElementById(index).innerText = record[key]
-                                                } else {
-                                                    document.getElementById(index).innerText = moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                                                }
-                                            }} /> */}
-                                    </>
-                                )
-
-                                // return moment(Number(record[key])).format('YYYY-MM-DD HH:mm:ss.SSS')
-                            }
-                        }
-                    } else {
-                        return {
-                            title: titleName,
-                            dataIndex: titleName,
-                            key: titleName,
-                            render: (text, record, index) => {
-                                return record[key]
-                            }
+                                    <span id={index}>
+                                        {record[key]}
+                                    </span>
+                                </>
+                            )
                         }
                     }
-                }))
-                setColumnsLength(cols.length)
-                setColumns(cols)
-                setPageData(res.data.values);
-                pageDataCache = res.data.values;
-                showPage()
-            } else {
-                notification.error({
-                    message: res.message,
-                });
-            }
+                } else {
+                    return {
+                        title: titleName,
+                        dataIndex: titleName,
+                        key: titleName,
+                        render: (text, record, index) => {
+                            return record[key]
+                        }
+                    }
+                }
+            }))
+            setColumnsLength(cols.length)
+            setColumns(cols)
+            setPageData(res.data.values);
+            pageDataCache = res.data.values;
+            showPage()
+        } else {
+            notification.error({
+                message: res.message,
+            });
         }
     };
 
@@ -352,7 +364,7 @@
                                         onChange={(e) => {
                                             setDeviceNameLike(e.target.value)
                                         }}
-                                        onSearch={() => generateTimeSeriesCards(1, pageSize)}
+                                        onSearch={() => {setCardList([]),generateTimeSeriesCards(1, pageSize)}}
                                         style={{
                                             width: 200,
                                         }}
@@ -401,14 +413,18 @@
                 onClose={onCloseChunk}
                 open={openChunk}
             >
-                <div style={{ height: "80vh", overflow: "auto" }}>
-                    <Tree
-                        showLine={{ showLeafIcon: false }}
-                        showIcon={true}
-                        onSelect={onSelect}
-                        loadData={onLoadData}
-                        treeData={treeData}
-                    />
+                <div id="tree-div" style={{ height: "80vh", background: "white", margin: '4px 0px 0px 0px' }}>
+                    <TreeArborist
+                        openByDefault={false}
+                        disableDrag={false}
+                        width={"100%"}
+                        height={treeHeight}
+                        // paddingBottom={200}
+                        //height={400}
+                        data={treeData}
+                    >
+                        {Node}
+                    </TreeArborist>
                 </div>
 
                 <Drawer
diff --git a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/overview.jsx b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/overview.jsx
index b3e8aa3..7373981 100644
--- a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/overview.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/overview/overview.jsx
@@ -26,45 +26,44 @@
 const Tsfile = (props) => {
 
     const { fileName, baseInfo } = props;
-    const intl = useIntl();
 
     const doMessageShow = (msg, offset) => {
         props.showStructureContext()
         props.doChange(msg, offset)
     }
-    const getMessage = (wrap)=>{
+    const getMessage = (wrap) => {
         let message;
         let messageShow;
-        if(wrap == undefined){
+        if (wrap == undefined) {
             message = "";
             messageShow = "";
-        }else{
-            message = <div className={styles.hcenter}>{wrap.deviceName }<br/>{"[" + wrap.offset + "]"}</div>;
-            messageShow = wrap.deviceName +"\n[" + wrap.offset + "]";
+        } else {
+            message = <div className={styles.hcenter}>{wrap.deviceName}<br />{"[" + wrap.offset + "]"}</div>;
+            messageShow = wrap.deviceName + "\n[" + wrap.offset + "]";
         }
         return (
-            <Tooltip placement="bottomLeft" title={<span style={{"whiteSpace":"pre-line"}}>{messageShow}</span>}>
+            <Tooltip placement="bottomLeft" title={<span style={{ "whiteSpace": "pre-line" }}>{messageShow}</span>}>
                 <h3 className={styles.hcenter}>{message}</h3>
             </Tooltip>
         )
     }
-    const getMessageIndex = (wrap)=>{
+    const getMessageIndex = (wrap) => {
         let message;
         let messageShow;
-        if(wrap == undefined){
+        if (wrap == undefined) {
             message = "";
             messageShow = "";
-        }else{
-            if(!wrap.aligned){
-                message = <div className={styles.hcenter}>{wrap.deviceId }<br/>{"[" + wrap.measurementId + "]"}<br/>{"[" + wrap.offset + "]"}</div>;
-                messageShow = wrap.deviceId +"\n[" + wrap.measurementId + "]\n[" + wrap.offset + "]";
-            }else{
-                message = <div className={styles.hcenter}>{wrap.deviceId }<br/>{"[" + wrap.offset + "]"}</div>;
-                messageShow = wrap.deviceId +"\n[" + wrap.offset + "]";
+        } else {
+            if (!wrap.aligned) {
+                message = <div className={styles.hcenter}>{wrap.deviceId}<br />{"[" + wrap.measurementId + "]"}<br />{"[" + wrap.offset + "]"}</div>;
+                messageShow = wrap.deviceId + "\n[" + wrap.measurementId + "]\n[" + wrap.offset + "]";
+            } else {
+                message = <div className={styles.hcenter}>{wrap.deviceId}<br />{"[" + wrap.offset + "]"}</div>;
+                messageShow = wrap.deviceId + "\n[" + wrap.offset + "]";
             }
         }
         return (
-            <Tooltip placement="bottomLeft" title={<span style={{"whiteSpace":"pre-line"}}>{message}</span>}>
+            <Tooltip placement="bottomLeft" title={<span style={{ "whiteSpace": "pre-line" }}>{message}</span>}>
                 <h3 className={styles.hcenter}>{message}</h3>
             </Tooltip>
         )
@@ -132,7 +131,7 @@
             <div className={styles.notoplinerow}>
                 <Row align="middle" justify="center" style={{ height: "45px" }}>
                     <Col span={22} style={{ height: "40px" }}>
-                        <div className={styles.shortStyle} onClick={() => doMessageShow("TsfileMetaDataSize")}><h3 className={styles.hcenter}>TsfileMetaDataSize{"[" + baseInfo.metadataSize + "]"}</h3></div>
+                        <div className={styles.shortStyle} onClick={() => doMessageShow("TsfileMetaDataSize")}><h3 className={styles.hcenter}>TsfileMetaDataSize{"[" + baseInfo.metadataSize + " bytes]"}</h3></div>
                     </Col>
                 </Row>
             </div>
@@ -148,6 +147,7 @@
 }
 
 const ImageMessage = (props) => {
+    const intl = useIntl();
     const { value, offset, showStructureContext, filePath } = props;
     const [version, setVersion] = useState();
     const [tsfileMetaDataSize, setTsfileMetaDataSize] = useState();
@@ -177,10 +177,10 @@
     const showImage = (key, offset) => {
         if (key == "TSFILE") {
             getVersion();
-            let message = '说明:\n' +
-                'TSFILE 魔数 offset=0 size=6\n' +
-                'VERSION:' + version + ' 版本 offset=6 size=1\n' +
-                '文件末尾 TSFILE 标记结束 offset= 文件长度-6 size=6';
+            let message = intl.formatMessage({ id: 'overview.explanation', }) + ':\n' +
+                'TSFILE, ' + intl.formatMessage({ id: 'overview.magicNumber', }) + ' offset=0 size=6\n' +
+                'VERSION:' + version + ', ' + intl.formatMessage({ id: 'overview.version', }) + ' offset=6 size=1\n' +
+                intl.formatMessage({ id: 'overview.endExplanation', }) + ' offset= file.length-6 size=6';
             return (<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>{message}</pre>);
         }
 
@@ -302,7 +302,7 @@
     const [indexTimeseriesIndexBrief, setIndexTimeseriesIndexBrief] = useState()
     // 结构CGH/CH/PH点击所对应的内容
     const [structureContext, setStructureContext] = useState()
-
+    const intl = useIntl();
     const { fileName, filePath, baseInfo } = props;
 
     const doChange = (structureName, offset) => {
@@ -372,43 +372,43 @@
     const showStructureContext = (level1Flag, level2Flag) => {
         if (level1Flag == 'ChunkGroup') {
             if (level2Flag == 'CGH') {
-                let info = "结构说明:\n"
+                let info = intl.formatMessage({ id: 'overview.structureDescription', }) + ":\n"
                     + "\t CGH = ChunkGroupHeader \n"
                     + "\t CGD = ChunkGroupData \n"
                     + "\t CGD = n * Chunk \n"
                     + "\t ChunkGroup = CGH +CGD \n"
-                    + "内容详情: \n"
+                    + intl.formatMessage({ id: 'overview.details', }) + ": \n"
                 setStructureContext(<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>{info}{JSON.stringify(chunkGroupBrief.cgh, null, '\t')}</pre>)
             }
             if (level2Flag == 'CH') {
-                let info = "结构说明:\n"
+                let info = intl.formatMessage({ id: 'overview.structureDescription', }) + ":\n"
                     + "\t CH = ChunkHeader \n"
                     + "\t CD = ChunkData \n"
                     + "\t CD = n * Page \n"
                     + "\t Chunk = CH +CD \n"
-                    + "内容详情: \n"
+                    + intl.formatMessage({ id: 'overview.details', }) + ": \n"
                 setStructureContext(<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>{info}{JSON.stringify(chunkGroupBrief.ch, null, '\t')}</pre>)
             }
             if (level2Flag == 'PH') {
-                let info = "结构说明:\n"
+                let info = intl.formatMessage({ id: 'overview.structureDescription', }) + ":\n"
                     + "\t PH = PageHeader \n"
                     + "\t PD = PageData \n"
                     + "\t Page = PH + PD \n"
-                    + "内容详情: \n"
+                    + intl.formatMessage({ id: 'overview.details', }) + ": \n"
                 setStructureContext(<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>{info}{JSON.stringify(chunkGroupBrief.ph, null, '\t')}</pre>)
             }
         } else if (level1Flag == 'TimeseriesIndex') {
             if (level2Flag == 'TM') {
-                let info = "结构说明:\n"
+                let info = intl.formatMessage({ id: 'overview.structureDescription', }) + ":\n"
                     + "\t TM = TimeseriesMetadata \n"
                     + "\t TimeseriesIndex = TM + n*CM \n"
-                    + "内容详情: \n"
+                    + intl.formatMessage({ id: 'overview.details', }) + ": \n"
                 setStructureContext(<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>{info}{JSON.stringify(timeseriesIndexBrief.tm, null, '\t')}</pre>)
             }
             if (level2Flag == 'CM') {
-                let info = "结构说明:\n"
+                let info = intl.formatMessage({ id: 'overview.structureDescription', }) + ":\n"
                     + "\t CM = ChunkMetadata \n"
-                    + "内容详情: \n"
+                    + intl.formatMessage({ id: 'overview.details', }) + ": \n"
                 setStructureContext(<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>{info}{JSON.stringify(timeseriesIndexBrief.cm, null, '\t')}</pre>)
             }
         } else if (level1Flag == 'IndexOfTimeseriesIndex') {
diff --git a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/searchData/searchPageData.jsx b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/searchData/searchPageData.jsx
index b96fe60..0f382d4 100644
--- a/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/searchData/searchPageData.jsx
+++ b/tsfile-viewer-web-frontend/src/pages/tsfile-tool/v2/searchData/searchPageData.jsx
@@ -38,26 +38,25 @@
     const intl = useIntl();
 
 
-    const doQuery = async () => {
-
+    const doQuery = async (measurementValue) => {
 
         if (device == undefined || device == null || device == '') {
             notification.info({ message: "device " + intl.formatMessage({ id: 'tsviewer.more.notNull', }) })
             return;
         }
 
-        if (measurement == undefined || measurement == null || measurement == '') {
+        if (measurementValue == undefined || measurementValue == null || measurementValue == '') {
             notification.info({ message: "measurement " + intl.formatMessage({ id: 'tsviewer.more.notNull', }) })
             return;
         }
 
         if (beginDate == undefined || beginDate == null || beginDate == '') {
-            notification.info({ message: "beginDate " } + intl.formatMessage({ id: 'tsviewer.more.notNull', }))
+            notification.info({ message: "beginDate " + intl.formatMessage({ id: 'tsviewer.more.notNull', }) })
             return;
         }
 
         if (endDate == undefined || endDate == null || endDate == '') {
-            notification.info({ message: "device " + intl.formatMessage({ id: 'tsviewer.more.notNull', }) })
+            notification.info({ message: "endDate " + intl.formatMessage({ id: 'tsviewer.more.notNull', }) })
             return;
         }
 
@@ -134,7 +133,7 @@
                             onChange={(e) => {
                                 setMeasureMent(e.target.value)
                             }}
-                            onSearch={() => doQuery()}
+                            onSearch={(value) => doQuery(value)}
                             style={{
                                 width: 200,
                             }}
diff --git a/tsfile-viewer-web/Dockerfile b/tsfile-viewer-web/Dockerfile
new file mode 100644
index 0000000..c71d36e
--- /dev/null
+++ b/tsfile-viewer-web/Dockerfile
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+FROM openjdk:8
+COPY target/iotdb-tsfile-viewer-web-0.13.2-SNAPSHOT.jar app.jar
+COPY docker.yml application.yml
+EXPOSE 8080
+RUN bash -c 'touch /app.jar'
+RUN bash -c 'touch /application.yml'
+VOLUME /tsfile
+ENTRYPOINT ["java","-jar","/app.jar","--spring.config.location=application.yml"]
\ No newline at end of file
diff --git a/tsfile-viewer-web/docker.yml b/tsfile-viewer-web/docker.yml
new file mode 100644
index 0000000..f6b194e
--- /dev/null
+++ b/tsfile-viewer-web/docker.yml
@@ -0,0 +1,49 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+server:
+  port: 8080
+  servlet: 
+     session: 
+        cookie:
+           http-only: false
+spring:
+  servlet:
+    multipart:
+      max-file-size: 300MB
+      max-request-size: 300MB
+  main:
+    allow-bean-definition-overriding: true
+  messages:
+    basename: messages/message
+    encoding: utf-8
+management:
+  endpoint:
+    shutdown:
+      enabled: true
+  endpoints:
+    web:
+      exposure:
+        include: '*'
+swagger:
+  enable: true
+tsviewer:
+  web:
+     baseDirectory: /tsfile
+     containerSize: 5
\ No newline at end of file
diff --git a/tsfile-viewer-web/src/main/java/org/apache/iotdb/ui/config/TsfileViewerContainer.java b/tsfile-viewer-web/src/main/java/org/apache/iotdb/ui/config/TsfileViewerContainer.java
index 8c1af53..78477f0 100644
--- a/tsfile-viewer-web/src/main/java/org/apache/iotdb/ui/config/TsfileViewerContainer.java
+++ b/tsfile-viewer-web/src/main/java/org/apache/iotdb/ui/config/TsfileViewerContainer.java
@@ -21,6 +21,7 @@
 import org.apache.iotdb.tool.core.service.TsFileAnalyserV13;
 import org.apache.iotdb.ui.exception.TsfileViewerException;
 
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -34,7 +35,10 @@
 @Configuration
 public class TsfileViewerContainer {
 
-  private Map<String, TsFileAnalyserV13> container = new ConcurrentHashMap(5);
+  @Value("${tsviewer.web.containerSize}")
+  private int containerSize;
+
+  private Map<String, TsFileAnalyserV13> container = new ConcurrentHashMap(containerSize);
 
   /**
    * 向容器中添加 parser
@@ -46,7 +50,7 @@
   public void addTsfileParser(String key, TsFileAnalyserV13 tsFileAnalyserV13)
       throws TsfileViewerException {
     synchronized (container) {
-      if (container.size() == 5) {
+      if (container.size() == containerSize) {
         throw new TsfileViewerException(TsfileViewerException.CONTAINER_SIZE_REACHED_MAXIMUM, "");
       }
     }
@@ -91,7 +95,7 @@
    * @return
    */
   public boolean hasReachedMaximum() {
-    if (container.size() >= 5) {
+    if (container.size() >= containerSize) {
       return true;
     }
     return false;
diff --git a/tsfile-viewer-web/src/main/resources/application.yml b/tsfile-viewer-web/src/main/resources/application.yml
index 9b3ca16..80fc78f 100644
--- a/tsfile-viewer-web/src/main/resources/application.yml
+++ b/tsfile-viewer-web/src/main/resources/application.yml
@@ -45,4 +45,5 @@
   enable: true
 tsviewer:
   web:
-     baseDirectory: C:\Users\Administrator\Desktop\
\ No newline at end of file
+     baseDirectory: C:\Users\lilong\Desktop\
+     containerSize: 5
\ No newline at end of file