he wei
2022-09-27 23786b9ef2dc1d9f06dd7d9458e926369aab8c25
UA 整理提交  准备修改标签
10个文件已修改
1个文件已删除
22个文件已添加
3509 ■■■■■ 已修改文件
package-lock.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 129 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/apis.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/demo.css 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/demo_index.html 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/iconfont.css 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/iconfont.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/iconfont.json 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/iconfont.woff 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/iconfont/iconfont.woff2 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/bus.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/offset.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/background.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HelloWorld.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chartContextMenu.vue 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/contextMenu.vue 377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/fileInfo.vue 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/menuList.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/myCharts/BaseChart.vue 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/myCharts/NormalBar.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/myCharts/theme/transparent.js 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabsBar.vue 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/empty.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/xmlResult.vue 534 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/preload.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/setting.js 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/tagsView.js 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -14858,6 +14858,11 @@
      "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
      "dev": true
    },
    "vuex": {
      "version": "3.6.2",
      "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
      "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw=="
    },
    "watchpack": {
      "version": "1.7.5",
      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
package.json
@@ -17,6 +17,7 @@
    "vue": "^2.6.11",
    "vue-router": "3.0.6",
    "axios": "0.18.1",
    "vuex": "^3.4.0",
    "element-ui": "^2.15.6",
    "echarts": "^4.8.0",
    "echarts-liquidfill": "^2.0.6"
src/App.vue
@@ -6,6 +6,7 @@
        <div class="title">文件列表</div>
        <div class="file-list" @contextmenu.prevent="openContextMenu($event)">
          <el-tree
            :class="['station-tree', { empty: !stationData.length }]"
            :data="stationData"
            @node-click="handleNodeClick"
            @node-contextmenu="nodeContextClick"
@@ -14,16 +15,26 @@
      </div>
      <div class="handle" ref="handle"></div>
      <div class="right">
        <div class="" @click="select">选文件</div>
        <div class="">当前选中的文件夹为:{{ path }}</div>
        <router-view />
        <div class="right-inner">
          <!-- 文件标签 -->
          <tabs-bar></tabs-bar>
          <div class="right-content">
            <div class="posIner">
              <keep-alive :include="cachedViews">
                <router-view :key="$route.fullPath" />
              </keep-alive>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- 右键菜单 -->
    <context-menu
      v-model="contextMenu.visible"
      :disabled-list="disabledList"
      :context-data="contextMenu.node"
      :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
      @reload="reload"
    ></context-menu>
  </div>
</template>
@@ -34,6 +45,7 @@
import MenuList from "@/components/menuList";
import ContextMenu from "@/components/contextMenu";
import TabsBar from "@/components/tabsBar";
import { getStation } from "./apis";
export default {
@@ -41,10 +53,10 @@
  components: {
    MenuList,
    ContextMenu,
    TabsBar,
  },
  data() {
    return {
      path: "",
      startX: 0,
      leftW: 0,
      stationData: [],
@@ -52,14 +64,17 @@
        x: 0,
        y: 0,
        visible: false,
        node: {},
      },
      disabledList: [],
    };
  },
  methods: {
    select() {
      window.api.send("open-file-dialog");
  computed: {
    cachedViews() {
      return this.$store.state.tagsView.cachedViews;
    },
  },
  methods: {
    onMouseDown(e) {
      // const el = e.target;
      this.startX = e.clientX;
@@ -83,6 +98,7 @@
      }
      left.style.width = CurBoxLen + "px";
      el.style.left = CurBoxLen + "px";
      this.$bus.$emit("checkScroll");
    },
    getStation() {
      getStation().then((res) => {
@@ -109,8 +125,10 @@
                    label: item1.fileName,
                    url: item1.fileUrl,
                    level: 3,
                    parent: [v.station1, val.station2, item.station3],
                  })),
                  level: 2,
                  parent: [v.station1, val.station2],
                });
              } else {
                children.push(
@@ -118,6 +136,7 @@
                    label: item1.fileName,
                    url: item1.fileUrl,
                    level: 3,
                    parent: [v.station1, val.station2],
                  }))
                );
              }
@@ -126,6 +145,7 @@
              label: val.station2,
              children,
              level: 1,
              parent: [v.station1],
            });
          } else {
            sname2s.push(
@@ -133,6 +153,7 @@
                label: item.fileName,
                url: item.fileUrl,
                level: 3,
                parent: [v.station1],
              }))
            );
          }
@@ -141,16 +162,20 @@
          children: sname2s,
          label: v.station1,
          level: 0,
          parent: [],
        };
      });
      console.log(res);
      return res;
    },
    reload() {
      this.getStation();
    },
    handleNodeClick(obj) {
      console.log(123, obj);
    },
    nodeContextClick($e, obj) {
      console.log(obj);
      // console.log(obj);
      const { clientX, clientY } = $e;
      this.contextMenu.x = clientX;
      let y = clientY;
@@ -162,9 +187,12 @@
      switch (obj.level) {
        case 0:
        case 1:
        case 2:
          // 子站上按鼠标右键,不能“打开文件”、“新建根文件”
          this.disabledList = [0, 11];
          break;
        case 2:
          // 此层级 只能添加文件 不能新建子站
          this.disabledList = [0, 1];
          break;
        case 3:
          // 添加的测试文件上按鼠标右键,可以点击“打开文件”、“重命名”、“删除”
@@ -173,6 +201,7 @@
        default:
          return false;
      }
      this.contextMenu.node = obj;
      this.contextMenu.visible = true;
    },
    // 右键菜单
@@ -193,7 +222,7 @@
      if (classList.contains("file-list")) {
        // 在空白处点击的右键 只能新建
        this.disabledList = [0, 12, 2, 3, 4];
        this.contextMenu.node = {};
        this.contextMenu.visible = true;
      }
    },
@@ -203,14 +232,18 @@
    maskClick() {
      this.closeContextMenu();
    },
    test() {
      setTimeout(() => {
        console.log(this.cachedViews, "cachedViews");
        this.test();
      }, 2000);
    },
  },
  mounted() {
    window.api.receive("selected-directory", (path) => {
      this.path = path.filePaths;
    });
    let handle = this.$refs.handle;
    handle.addEventListener("mousedown", this.onMouseDown);
    this.getStation();
    this.test();
  },
};
</script>
@@ -223,6 +256,9 @@
  padding: 0;
  overflow: hidden;
}
div {
  box-sizing: border-box;
}
#app {
  height: 100%;
  font-family: Avenir, Helvetica, Arial, sans-serif;
@@ -230,7 +266,7 @@
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #000;
  background: #fff;
  background: #076fec;
}
ul,
li {
@@ -245,6 +281,7 @@
  height: 100%;
  position: relative;
  display: flex;
  z-index: 1;
}
.left {
  width: 14em;
@@ -253,27 +290,46 @@
  display: flex;
  flex-direction: column;
  .title {
    background: #999;
    background: #0055bf;
    color: #fff;
    padding: 4px;
  }
  .file-list {
    flex: 1;
    overflow-y: auto;
    text-align: left;
    /deep/ .el-tree {
      color: #333;
    user-select: none;
    :deep(.el-tree.station-tree) {
      background: transparent;
      color: #fff;
      display: inline-block;
      &.empty {
        display: block;
      }
      .el-tree-node__content {
        display: inline-block;
      }
      // .el-tree-node.is-expanded>.el-tree-node__children {
      //   display: inline-block;
      // }
      .el-tree-node:hover,
      .el-tree-node:focus > .el-tree-node__content {
        background-color: rgba(39, 134, 255, 0.4);
        .el-tree-node__label {
          color: #000;
        }
      }
      .el-tree-node__content.el-tree-node__content:hover,
      .el-tree-node__content.el-upload-list__item:hover {
        background-color: rgba(200, 200, 200, 0.4);
        color: #000;
        .el-tree-node__label {
          color: #000;
        }
      }
    }
  }
}
.handle {
  width: 6px;
  z-index: 1;
  position: absolute;
  top: 0;
  bottom: 0;
@@ -287,27 +343,24 @@
.right {
  /* background: rgba(0,0,0,.4); */
  flex: 1;
}
.context-menu {
  position: absolute;
  z-index: 100;
  background: #fff;
  border: 1px #ccc solid;
  border-radius: 4px;
  padding: 4px;
  .main-ul {
  position: relative;
  .right-inner {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    .main-li {
      cursor: pointer;
    .right-content {
      flex: 1;
      border: 1px #ccc solid;
      padding: 4px;
      &:hover {
        background: #f0f0f0;
      }
      & ~ .main-li {
        margin-top: 2px;
      position: relative;
      .posIner {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
      }
    }
  }
src/apis.js
@@ -21,6 +21,17 @@
  })
}
/**
 * 移除台站下文件(单个文件)
 * ?FilePath=1&stationName=1
 */
export const delFileFromStation = (params) => {
  return axios({
    method: "GET",
    url: "stationInfo/delFileFromStation",
    params
  })
}
/**
 * 添加台站
 */
export const addStation = (params) => {
@@ -32,6 +43,7 @@
}
/**
 * 删除台站
 * ?stationName1=null&stationName2=null&stationName3=null
 */
export const deleteStation = (params) => {
  return axios({
@@ -52,6 +64,7 @@
}
/**
 * 解析xml文件
 * filePath
 */
export const getXmlValue = (params) => {
  return axios({
src/assets/iconfont/demo.css
New file
@@ -0,0 +1,539 @@
/* Logo 字体 */
@font-face {
  font-family: "iconfont logo";
  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
  font-family: "iconfont logo";
  font-size: 160px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
  position: relative;
}
.nav-tabs .nav-more {
  position: absolute;
  right: 0;
  bottom: 0;
  height: 42px;
  line-height: 42px;
  color: #666;
}
#tabs {
  border-bottom: 1px solid #eee;
}
#tabs li {
  cursor: pointer;
  width: 100px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  font-size: 16px;
  border-bottom: 2px solid transparent;
  position: relative;
  z-index: 1;
  margin-bottom: -1px;
  color: #666;
}
#tabs .active {
  border-bottom-color: #f00;
  color: #222;
}
.tab-container .content {
  display: none;
}
/* 页面布局 */
.main {
  padding: 30px 100px;
  width: 960px;
  margin: 0 auto;
}
.main .logo {
  color: #333;
  text-align: left;
  margin-bottom: 30px;
  line-height: 1;
  height: 110px;
  margin-top: -50px;
  overflow: hidden;
  *zoom: 1;
}
.main .logo a {
  font-size: 160px;
  color: #333;
}
.helps {
  margin-top: 40px;
}
.helps pre {
  padding: 20px;
  margin: 10px 0;
  border: solid 1px #e7e1cd;
  background-color: #fffdef;
  overflow: auto;
}
.icon_lists {
  width: 100% !important;
  overflow: hidden;
  *zoom: 1;
}
.icon_lists li {
  width: 100px;
  margin-bottom: 10px;
  margin-right: 20px;
  text-align: center;
  list-style: none !important;
  cursor: default;
}
.icon_lists li .code-name {
  line-height: 1.2;
}
.icon_lists .icon {
  display: block;
  height: 100px;
  line-height: 100px;
  font-size: 42px;
  margin: 10px auto;
  color: #333;
  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
  -moz-transition: font-size 0.25s linear, width 0.25s linear;
  transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
  font-size: 100px;
}
.icon_lists .svg-icon {
  /* 通过设置 font-size 来改变图标大小 */
  width: 1em;
  /* 图标和文字相邻时,垂直对齐 */
  vertical-align: -0.15em;
  /* 通过设置 color 来改变 SVG 的颜色/fill */
  fill: currentColor;
  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
      normalize.css 中也包含这行 */
  overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
  color: #666;
}
/* markdown 样式 */
.markdown {
  color: #666;
  font-size: 14px;
  line-height: 1.8;
}
.highlight {
  line-height: 1.5;
}
.markdown img {
  vertical-align: middle;
  max-width: 100%;
}
.markdown h1 {
  color: #404040;
  font-weight: 500;
  line-height: 40px;
  margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
  color: #404040;
  margin: 1.6em 0 0.6em 0;
  font-weight: 500;
  clear: both;
}
.markdown h1 {
  font-size: 28px;
}
.markdown h2 {
  font-size: 22px;
}
.markdown h3 {
  font-size: 16px;
}
.markdown h4 {
  font-size: 14px;
}
.markdown h5 {
  font-size: 12px;
}
.markdown h6 {
  font-size: 12px;
}
.markdown hr {
  height: 1px;
  border: 0;
  background: #e9e9e9;
  margin: 16px 0;
  clear: both;
}
.markdown p {
  margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
  width: 80%;
}
.markdown ul>li {
  list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
  margin-left: 20px;
  padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
  margin: 0.6em 0;
}
.markdown ol>li {
  list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
  margin-left: 20px;
  padding-left: 4px;
}
.markdown code {
  margin: 0 3px;
  padding: 0 5px;
  background: #eee;
  border-radius: 3px;
}
.markdown strong,
.markdown b {
  font-weight: 600;
}
.markdown>table {
  border-collapse: collapse;
  border-spacing: 0px;
  empty-cells: show;
  border: 1px solid #e9e9e9;
  width: 95%;
  margin-bottom: 24px;
}
.markdown>table th {
  white-space: nowrap;
  color: #333;
  font-weight: 600;
}
.markdown>table th,
.markdown>table td {
  border: 1px solid #e9e9e9;
  padding: 8px 16px;
  text-align: left;
}
.markdown>table th {
  background: #F7F7F7;
}
.markdown blockquote {
  font-size: 90%;
  color: #999;
  border-left: 4px solid #e9e9e9;
  padding-left: 0.8em;
  margin: 1em 0;
}
.markdown blockquote p {
  margin: 0;
}
.markdown .anchor {
  opacity: 0;
  transition: opacity 0.3s ease;
  margin-left: 8px;
}
.markdown .waiting {
  color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
  opacity: 1;
  display: inline-block;
}
.markdown>br,
.markdown>p>br {
  clear: both;
}
.hljs {
  display: block;
  background: white;
  padding: 0.5em;
  color: #333333;
  overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
  color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
  color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
  color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
  color: #0086b3;
}
.hljs-section,
.hljs-name {
  color: #63a35c;
}
.hljs-tag {
  color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
  color: #795da3;
}
.hljs-addition {
  color: #55a532;
  background-color: #eaffea;
}
.hljs-deletion {
  color: #bd2c00;
  background-color: #ffecec;
}
.hljs-link {
  text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
 * prism.js default theme for JavaScript, CSS and HTML
 * Based on dabblet (http://dabblet.com)
 * @author Lea Verou
 */
code[class*="language-"],
pre[class*="language-"] {
  color: black;
  background: none;
  text-shadow: 0 1px white;
  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
  text-align: left;
  white-space: pre;
  word-spacing: normal;
  word-break: normal;
  word-wrap: normal;
  line-height: 1.5;
  -moz-tab-size: 4;
  -o-tab-size: 4;
  tab-size: 4;
  -webkit-hyphens: none;
  -moz-hyphens: none;
  -ms-hyphens: none;
  hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
  text-shadow: none;
  background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
  text-shadow: none;
  background: #b3d4fc;
}
@media print {
  code[class*="language-"],
  pre[class*="language-"] {
    text-shadow: none;
  }
}
/* Code blocks */
pre[class*="language-"] {
  padding: 1em;
  margin: .5em 0;
  overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
  background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
  padding: .1em;
  border-radius: .3em;
  white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
  color: slategray;
}
.token.punctuation {
  color: #999;
}
.namespace {
  opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
  color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
  color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
  color: #9a6e3a;
  background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
  color: #07a;
}
.token.function,
.token.class-name {
  color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
  color: #e90;
}
.token.important,
.token.bold {
  font-weight: bold;
}
.token.italic {
  font-style: italic;
}
.token.entity {
  cursor: help;
}
src/assets/iconfont/demo_index.html
New file
@@ -0,0 +1,257 @@
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>iconfont Demo</title>
  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
  <link rel="stylesheet" href="demo.css">
  <link rel="stylesheet" href="iconfont.css">
  <script src="iconfont.js"></script>
  <!-- jQuery -->
  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
  <!-- 代码高亮 -->
  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
  <style>
    .main .logo {
      margin-top: 0;
      height: auto;
    }
    .main .logo a {
      display: flex;
      align-items: center;
    }
    .main .logo .sub-title {
      margin-left: 0.5em;
      font-size: 22px;
      color: #fff;
      background: linear-gradient(-45deg, #3967FF, #B500FE);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
  </style>
</head>
<body>
  <div class="main">
    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
      <img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
    </a></h1>
    <div class="nav-tabs">
      <ul id="tabs" class="dib-box">
        <li class="dib active"><span>Unicode</span></li>
        <li class="dib"><span>Font class</span></li>
        <li class="dib"><span>Symbol</span></li>
      </ul>
      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=3667792" target="_blank" class="nav-more">查看项目</a>
    </div>
    <div class="tab-container">
      <div class="content unicode" style="display: block;">
          <ul class="icon_lists dib-box">
            <li class="dib">
              <span class="icon iconfont">&#xeaf2;</span>
                <div class="name">关闭</div>
                <div class="code-name">&amp;#xeaf2;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe8fa;</span>
                <div class="name">327全屏</div>
                <div class="code-name">&amp;#xe8fa;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe8fb;</span>
                <div class="name">328退出全屏</div>
                <div class="code-name">&amp;#xe8fb;</div>
              </li>
          </ul>
          <div class="article markdown">
          <h2 id="unicode-">Unicode 引用</h2>
          <hr>
          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
          <ul>
            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
            <li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
          </ul>
          <blockquote>
            <p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
          </blockquote>
          <p>Unicode 使用步骤如下:</p>
          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
<pre><code class="language-css"
>@font-face {
  font-family: 'iconfont';
  src: url('iconfont.woff2?t=1663915384901') format('woff2'),
       url('iconfont.woff?t=1663915384901') format('woff'),
       url('iconfont.ttf?t=1663915384901') format('truetype');
}
</code></pre>
          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
<pre><code class="language-css"
>.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</code></pre>
          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre>
<code class="language-html"
>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
</code></pre>
          <blockquote>
            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
          </blockquote>
          </div>
      </div>
      <div class="content font-class">
        <ul class="icon_lists dib-box">
          <li class="dib">
            <span class="icon iconfont icon-guanbi"></span>
            <div class="name">
              关闭
            </div>
            <div class="code-name">.icon-guanbi
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-quanping"></span>
            <div class="name">
              327全屏
            </div>
            <div class="code-name">.icon-quanping
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-tuichuquanping"></span>
            <div class="name">
              328退出全屏
            </div>
            <div class="code-name">.icon-tuichuquanping
            </div>
          </li>
        </ul>
        <div class="article markdown">
        <h2 id="font-class-">font-class 引用</h2>
        <hr>
        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
        <p>与 Unicode 使用方式相比,具有如下特点:</p>
        <ul>
          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
        </ul>
        <p>使用步骤如下:</p>
        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
</code></pre>
        <blockquote>
          <p>"
            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
        </blockquote>
      </div>
      </div>
      <div class="content symbol">
          <ul class="icon_lists dib-box">
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-guanbi"></use>
                </svg>
                <div class="name">关闭</div>
                <div class="code-name">#icon-guanbi</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-quanping"></use>
                </svg>
                <div class="name">327全屏</div>
                <div class="code-name">#icon-quanping</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-tuichuquanping"></use>
                </svg>
                <div class="name">328退出全屏</div>
                <div class="code-name">#icon-tuichuquanping</div>
            </li>
          </ul>
          <div class="article markdown">
          <h2 id="symbol-">Symbol 引用</h2>
          <hr>
          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
          <ul>
            <li>支持多色图标了,不再受单色限制。</li>
            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
          </ul>
          <p>使用步骤如下:</p>
          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
&lt;/style&gt;
</code></pre>
          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</code></pre>
          </div>
      </div>
    </div>
  </div>
  <script>
  $(document).ready(function () {
      $('.tab-container .content:first').show()
      $('#tabs li').click(function (e) {
        var tabContent = $('.tab-container .content')
        var index = $(this).index()
        if ($(this).hasClass('active')) {
          return
        } else {
          $('#tabs li').removeClass('active')
          $(this).addClass('active')
          tabContent.hide().eq(index).fadeIn()
        }
      })
    })
  </script>
</body>
</html>
src/assets/iconfont/iconfont.css
New file
@@ -0,0 +1,27 @@
@font-face {
  font-family: "iconfont"; /* Project id 3667792 */
  src: url('iconfont.woff2?t=1663915384901') format('woff2'),
       url('iconfont.woff?t=1663915384901') format('woff'),
       url('iconfont.ttf?t=1663915384901') format('truetype');
}
.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.icon-guanbi:before {
  content: "\eaf2";
}
.icon-quanping:before {
  content: "\e8fa";
}
.icon-tuichuquanping:before {
  content: "\e8fb";
}
src/assets/iconfont/iconfont.js
New file
@@ -0,0 +1 @@
window._iconfont_svg_string_3667792='<svg><symbol id="icon-guanbi" viewBox="0 0 1024 1024"><path d="M576 512l277.333333 277.333333-64 64-277.333333-277.333333L234.666667 853.333333 170.666667 789.333333l277.333333-277.333333L170.666667 234.666667 234.666667 170.666667l277.333333 277.333333L789.333333 170.666667 853.333333 234.666667 576 512z" fill="#444444" ></path></symbol><symbol id="icon-quanping" viewBox="0 0 1024 1024"><path d="M240.8 196l178.4 178.4-45.6 45.6-177.6-179.2-68 68V128h180.8l-68 68z m133.6 408.8L196 783.2 128 715.2V896h180.8l-68-68 178.4-178.4-44.8-44.8zM715.2 128l68 68-178.4 178.4 45.6 45.6 178.4-178.4 68 68V128H715.2z m-65.6 476.8l-45.6 45.6 178.4 178.4-68 68H896V715.2l-68 68-178.4-178.4z"  ></path></symbol><symbol id="icon-tuichuquanping" viewBox="0 0 1024 1024"><path d="M142.4 96.8l-44.8 44.8 173.6 174.4-68 68H384V203.2l-67.2 67.2zM752.8 316l173.6-174.4-44.8-44.8-174.4 173.6-67.2-67.2V384h180.8zM270.4 707.2l-169.6 170.4 44.8 49.6 170.4-174.4 68 68V640H203.2zM820.8 640H640v180.8l68-68 170.4 174.4 44.8-49.6-169.6-170.4z"  ></path></symbol></svg>',function(n){var t=(t=document.getElementsByTagName("script"))[t.length-1],e=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var i,o,l,d,c,a=function(t,e){e.parentNode.insertBefore(t,e)};if(e&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,e=document.createElement("div");e.innerHTML=n._iconfont_svg_string_3667792,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?a(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),i()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(l=i,d=n.document,c=!1,r(),d.onreadystatechange=function(){"complete"==d.readyState&&(d.onreadystatechange=null,s())})}function s(){c||(c=!0,l())}function r(){try{d.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}s()}}(window);
src/assets/iconfont/iconfont.json
New file
@@ -0,0 +1,30 @@
{
  "id": "3667792",
  "name": "whyc_res",
  "font_family": "iconfont",
  "css_prefix_text": "icon-",
  "description": "",
  "glyphs": [
    {
      "icon_id": "5387522",
      "name": "关闭",
      "font_class": "guanbi",
      "unicode": "eaf2",
      "unicode_decimal": 60146
    },
    {
      "icon_id": "1727563",
      "name": "327全屏",
      "font_class": "quanping",
      "unicode": "e8fa",
      "unicode_decimal": 59642
    },
    {
      "icon_id": "1727566",
      "name": "328退出全屏",
      "font_class": "tuichuquanping",
      "unicode": "e8fb",
      "unicode_decimal": 59643
    }
  ]
}
src/assets/iconfont/iconfont.ttf
Binary files differ
src/assets/iconfont/iconfont.woff
Binary files differ
src/assets/iconfont/iconfont.woff2
Binary files differ
src/assets/js/bus.js
New file
@@ -0,0 +1,3 @@
import Vue from 'vue';
const Bus = new Vue();
export default Bus;
src/assets/js/offset.js
New file
@@ -0,0 +1,12 @@
export default function offset(el) {
  let left = 0, top = 0;
  let elPar = el.offsetParent;
  left += el.offsetLeft;
  top += el.offsetTop;
  while (elPar) {
    left += elPar.offsetLeft;
    top += elPar.offsetTop;
    elPar = elPar.offsetParent;
  }
  return { left, top };
}
src/background.js
@@ -86,13 +86,14 @@
//     }
//   })
// });
ipcMain.on('open-file-dialog', (event) => {
ipcMain.on('open-file-dialog', (event, data) => {
  dialog.showOpenDialog({
    filters: [{ name: 'xml', extensions: ['xml'] }],
    properties: ['openFile']
  }).then((files) => {
    // console.log(files, '0000000')
    if (files) {
      event.sender.send('selected-file', files);
      event.sender.send('selected-file', files, data);
    }
  })
});
src/components/HelloWorld.vue
File was deleted
src/components/chartContextMenu.vue
New file
@@ -0,0 +1,187 @@
<template>
  <div class="chart-context-menu" v-show="visible">
    <!-- mask -->
    <div
      class="mask"
      v-show="visible"
      @click="close"
      @contextmenu="close"
    ></div>
    <div class="contain">
      <ul class="main-ul">
        <li
          class="main-li"
          v-for="(item, idx) in menuList"
          :key="'main_' + idx"
        >
          <div :class="['title']" @click="itemClick(item)">
            {{ item.title }}
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
// import {
//   addStation,
//   addFileInStation,
//   updateStation,
//   deleteStation,
//   delFileFromStation,
//   getXmlValue,
// } from "@/apis";
export default {
  name: "ChartContextMenu",
  model: {
    prop: "visible",
    event: "change",
  },
  props: {
    visible: {
      type: Boolean,
      required: true,
      default: false,
    },
    contextData: {
      type: Object,
      default() {
        return {};
      },
    },
    getParentNode: Function,
  },
  data() {
    const menuList = [
      {
        id: 0,
        title: "显示/限藏数值",
        method: "toggleValue",
      },
      {
        id: 1,
        title: "按高-低排序",
        method: "sortReverse",
      },
      {
        id: 2,
        title: "按低-高排序",
        method: "sort",
      },
    ];
    return {
      menuList,
    };
  },
  components: {},
  watch: {
    visible(n) {
      if (n) {
        if (this.getParentNode) {
          // console.log(this.getParentNode)
          this.getParentNode().appendChild(this.$el);
        }
      }
      //  else {
      //   this.getParentNode().removeChild(this.$el);
      // }
    },
  },
  methods: {
    itemClick(obj) {
      // console.log(obj)
      if (
        obj.method &&
        "function" == typeof this[obj.method] &&
        !obj.disabled
      ) {
        this.close();
        this[obj.method](obj);
      }
    },
    close() {
      this.$emit("change", false);
    },
    toggleValue() {
      let { ref } = this.contextData;
      this.$emit("toggleLabel", ref);
    },
    toObjectList(labels, datas) {
      return labels.map((v, i) => ({ label: v, data: datas[i] }));
    },
    formatData(arr) {
      let xLabel = [];
      let sData = [];
      arr.forEach((v) => {
        xLabel.push(v.label);
        sData.push(v.data);
      });
      return {
        xLabel,
        sData,
      };
    },
    sort() {
      let { xLabel, data, ref } = this.contextData;
      let list = this.toObjectList(xLabel, data);
      list.sort((a, b) => a.data - b.data);
      this.$emit("updateChart", { ref, data: this.formatData(list) });
    },
    sortReverse() {
      let { xLabel, data, ref } = this.contextData;
      let list = this.toObjectList(xLabel, data);
      list.sort((a, b) => b.data - a.data);
      this.$emit("updateChart", { ref, data: this.formatData(list) });
    },
  },
  mounted() {},
};
</script>
<style lang="less" scoped>
.chart-context-menu {
  z-index: 1;
  position: absolute;
  background: #c0c0c0;
  // border: 1px #055AC6 solid;
  border-radius: 4px;
  padding: 4px;
  .contain {
    position: relative;
    background: #c0c0c0;
    z-index: 100;
    .main-ul {
      display: flex;
      flex-direction: column;
      .main-li {
        position: relative;
        cursor: pointer;
        flex: 1;
        border: 1px #fff solid;
        .title {
          padding: 4px;
          color: #333;
          background: #f0f0f0;
          &:hover {
            background: #ccc;
          }
        }
        & ~ .main-li {
          margin-top: 2px;
        }
      }
    }
  }
  .mask {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background: transparent;
    z-index: 99;
  }
}
</style>
src/components/contextMenu.vue
@@ -15,7 +15,7 @@
          :key="'main_' + idx"
        >
          <div
            :class="['title', {disabled: item.disabled}]"
            :class="['title', { disabled: item.disabled }]"
            @click="itemClick(item)"
            @mouseenter="itemHover(item)"
          >
@@ -26,7 +26,7 @@
            v-show="item.visible && item.children && item.children.length"
          >
            <li
              :class="['sub-li', {disabled: subItem.disabled}]"
              :class="['sub-li', { disabled: subItem.disabled }]"
              v-for="(subItem, index) in item.children"
              :key="'sub_' + index"
              @click="itemClick(subItem)"
@@ -37,10 +37,50 @@
        </li>
      </ul>
    </div>
    <!--  -->
    <el-dialog
      :title="dialogTitle"
      :visible.sync="dialogVisible"
      append-to-body
      width="30%"
    >
      <el-input v-model="stationName" placeholder="请输入..."></el-input>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="stationNameOk">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 文件属性 -->
    <el-dialog
      title="属性"
      class="file-info"
      :visible.sync="fileInfoVisible"
      append-to-body
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      width="800px"
    >
      <file-info
        :info="fileData"
        @ok="editOk"
        @cancel="editCancel"
        @quit="quit"
      ></file-info>
    </el-dialog>
  </div>
</template>
<script>
import FileInfo from "@/components/fileInfo";
import {
  addStation,
  addFileInStation,
  updateStation,
  deleteStation,
  delFileFromStation,
  getXmlValue,
} from "@/apis";
export default {
  name: "ContextMenu",
  model: {
@@ -57,6 +97,12 @@
      type: Array,
      default() {
        return [];
      },
    },
    contextData: {
      type: Object,
      default() {
        return {};
      },
    },
  },
@@ -78,13 +124,13 @@
          {
            id: 11,
            title: "新建根文件",
            // method: "openFile",
            method: "addRootStation",
            disabled: false,
          },
          {
            id: 12,
            title: "新建子文件",
            // method: "openFile",
            method: "addSubStation",
            disabled: false,
          },
        ],
@@ -98,11 +144,13 @@
          {
            id: 13,
            title: "指定目录下所有文件",
            method: "selectDir",
            disabled: false,
          },
          {
            id: 14,
            title: "单一文件",
            method: "selectFile",
            disabled: false,
          },
        ],
@@ -110,34 +158,51 @@
      {
        id: 3,
        title: "重命名",
        method: "openFile",
        method: "rename",
        disabled: false,
        visible: false,
      },
      {
        id: 4,
        title: "删除",
        method: "openFile",
        method: "deleteItem",
        disabled: false,
        visible: false,
      },
    ];
    return {
      menuList,
      dialogTitle: "",
      dialogVisible: false,
      stationName: "",
      // 操作类型 'rename', 'add'
      type: "",
      fileInfoVisible: false,
      fileData: {},
      currFile: {
        name: "",
        url: "",
      },
    };
  },
  components: {},
  components: {
    FileInfo,
  },
  watch: {
    visible(n) {
      if(n) {
      if (n) {
        this.initData();
      }
    }
    },
  },
  methods: {
    itemClick(obj) {
      // console.log(obj)
      if (obj.method && "function" == typeof this[obj.method] && !obj.disabled) {
      if (
        obj.method &&
        "function" == typeof this[obj.method] &&
        !obj.disabled
      ) {
        this.close();
        this[obj.method](obj);
      }
@@ -167,16 +232,44 @@
      });
    },
    openFile() {
      // console.log("openfile");
      window.api.send("open-file-dialog");
      let { url } = this.contextData;
      const params = {
        filePath: url,
      };
      getXmlValue(params).then((res) => {
        const { code, data, data2 } = res.data;
        if (code && data) {
          console.log(data2);
          this.fileData = data2.fileParam;
          this.currFile.name = data2.fileName;
          this.currFile.url = data2.fileUrl;
          this.fileInfoVisible = true;
          // this.$router.push({
          //   path: "/result/" + data2.fileName,
          //   query: {
          //     url: data2.fileUrl,
          //   },
          // });
          // this.$bus.$emit("checkScroll");
        } else {
          this.$message.error("文件解析失败");
        }
      });
    },
    initEvents() {
      window.api.receive("selected-file", (path) => {
        console.log(path.filePaths);
        // this.path = path.filePaths;
      window.api.receive("selected-file", (path, data) => {
        if (data && data == "ContextMenu") {
          console.log(path.filePaths, "contextMenu?");
          path = path.filePaths[0];
          this.addFileInStation(path);
        }
      });
      window.api.receive("selected-directory", (path) => {
        console.log(path.filePaths);
      window.api.receive("selected-directory", (path, data) => {
        if (data && data == "ContextMenu") {
          // console.log(path.filePaths);
          path = path.filePaths[0];
          this.addFileInStation(path);
        }
      });
    },
    initData() {
@@ -187,7 +280,209 @@
          item.disabled = this.disabledList.some((val) => val == item.id);
        });
      });
      console.log(this.menuList, this.disabledList, 9090)
      console.log(this.menuList, this.disabledList, 9090);
    },
    addRootStation() {
      this.dialogTitle = "新建根文件名称";
      this.stationName = "";
      this.type = "add";
      this.dialogVisible = true;
    },
    addSubStation() {
      this.dialogTitle = "新建子文件名称";
      this.stationName = "";
      this.type = "add";
      this.dialogVisible = true;
    },
    stationNameOk() {
      if ("" == this.stationName.trim()) {
        this.$message.error("名称不能为空");
        return false;
      }
      if (this.type == "add") {
        this.addStation();
      } else if (this.type == "rename") {
        this.renameOk();
      }
    },
    addStation() {
      const { label, parent, level } = this.contextData;
      let params = {};
      const stationName = this.stationName.trim();
      switch (level) {
        case 0:
          params["stationName1"] = label;
          params["stationName2"] = stationName;
          break;
        case 1:
          params["stationName1"] = parent[0];
          params["stationName2"] = label;
          params["stationName3"] = stationName;
          break;
        default:
          params["stationName1"] = stationName;
      }
      addStation(params).then((res) => {
        const { code, data } = res.data;
        if (code && data) {
          this.dialogVisible = false;
          this.$message.success("添加成功");
          this.reload();
        } else {
          this.$message.error("操作失败");
        }
      });
    },
    selectDir() {
      window.api.send("open-directory-dialog", "ContextMenu");
    },
    selectFile() {
      window.api.send("open-file-dialog", "ContextMenu");
    },
    addFileInStation(path) {
      let { label, parent } = this.contextData;
      parent = parent.map((v) => v);
      parent.push(label);
      const params = {
        FilePath: path,
        stationName: parent.join("-"),
      };
      addFileInStation(params).then((res) => {
        const { code, data, data2 } = res.data;
        if (code && data) {
          console.log(data2);
          this.reload();
        }
      });
    },
    // 删除站点 或都从站点删除文件
    deleteItem() {
      let { label, parent, level } = this.contextData;
      if (level == 3) {
        this.$confirm("此操作将从站点移除该文件, 是否继续?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }).then(() => {
          this.deleteFile();
        });
      } else {
        this.$confirm("此操作将删除该站点, 是否继续?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }).then(() => {
          this.deleteStation();
        });
      }
    },
    deleteStation() {
      let { label, parent, level } = this.contextData;
      let params = {};
      switch (level) {
        case 0:
          params["stationName1"] = label;
          break;
        case 1:
          params["stationName1"] = parent[0];
          params["stationName2"] = label;
          break;
        case 2:
          params["stationName1"] = parent[0];
          params["stationName2"] = parent[1];
          params["stationName3"] = label;
          break;
      }
      deleteStation(params).then((res) => {
        const { code, data, data2 } = res.data;
        if (code && data) {
          console.log(data2);
          this.reload();
        }
      });
    },
    deleteFile() {
      let { label, parent, url } = this.contextData;
      parent = parent.map((v) => v);
      let params = {
        stationName: parent.join("-"),
        FilePath: url,
      };
      delFileFromStation(params).then((res) => {
        const { code, data, data2 } = res.data;
        if (code && data) {
          this.$message.success("操作成功");
          this.reload();
        } else {
          this.$message.error("操作失败");
        }
      });
    },
    // 重命名现只针对台站 (文件是否重命名需要确认需求)
    rename() {
      this.dialogTitle = "修改站点名称";
      this.stationName = this.contextData.label;
      this.type = "rename";
      this.dialogVisible = true;
    },
    renameOk() {
      let { label, parent, level } = this.contextData;
      const stationName = this.stationName.trim();
      let params = {};
      switch (level) {
        case 0:
          params["stationName1"] = label;
          params["updateName"] = stationName;
          break;
        case 1:
          params["stationName1"] = parent[0];
          params["stationName2"] = label;
          params["updateName"] = stationName;
          break;
        case 2:
          params["stationName1"] = parent[0];
          params["stationName2"] = parent[1];
          params["stationName3"] = label;
          params["updateName"] = stationName;
          break;
      }
      updateStation(params).then((res) => {
        const { code, data } = res.data;
        if (code && data) {
          this.dialogVisible = false;
          this.$message.success("操作成功");
          this.reload();
        } else {
          this.$message.error("操作失败");
        }
      });
    },
    reload() {
      this.$emit("reload");
    },
    editOk(data) {
      console.log("ok", data);
      this.fileInfoVisible = false;
      this.toRes();
    },
    editCancel(data) {
      console.log("cancel", data);
      this.fileInfoVisible = false;
      this.toRes();
    },
    toRes() {
      const { name, url } = this.currFile;
      this.$router.push({
        path: "/result/" + name,
        query: {
          url,
        },
      });
      this.$bus.$emit("checkScroll");
    },
    quit() {
      console.log("quit");
      this.fileInfoVisible = false;
    },
  },
@@ -200,13 +495,15 @@
<style lang="less" scoped>
.context-menu {
  z-index: 1;
  position: absolute;
  background: #fff;
  border: 1px #ccc solid;
  background: #c0c0c0;
  // border: 1px #055AC6 solid;
  border-radius: 4px;
  padding: 4px;
  .contain {
    position: relative;
    background: #c0c0c0;
    z-index: 100;
    .main-ul {
      display: flex;
@@ -215,10 +512,14 @@
        position: relative;
        cursor: pointer;
        flex: 1;
        border: 1px #ccc solid;
        padding: 4px;
        &:hover {
        border: 1px #fff solid;
        .title {
          padding: 4px;
          color: #333;
          background: #f0f0f0;
          &:hover {
            background: #ccc;
          }
        }
        & ~ .main-li {
          margin-top: 2px;
@@ -233,11 +534,12 @@
        top: 0;
        transform: translateX(6px);
        min-width: 10em;
        background: #f0f0f0;
        background: #c0c0c0;
        border-radius: 4px;
        padding: 4px;
        .sub-li {
          border-radius: 4px;
          border: 1px #333 solid;
          border: 1px #fff solid;
          background: #d9dce2;
          &:hover {
            background: #169bd5;
@@ -246,8 +548,8 @@
      }
      .disabled.disabled.disabled {
        cursor: not-allowed;
        color: #aaa;
        background: #ccc;
        color: #eee;
        background: #999;
      }
    }
  }
@@ -261,4 +563,25 @@
    z-index: 99;
  }
}
.file-info {
  :deep(.el-dialog__header) {
    position: relative;
    z-index: 0;
    text-align: center;
    padding-bottom: 16px;
    filter: drop-shadow(0 1px 2px #333);
    &::before {
      content: "";
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      right: 0;
      background: #ccc;
      z-index: -1;
      clip-path: polygon(0 0, 100% 0, 100% 60%, 50% 100%, 0 60%);
      background: linear-gradient(#ccc 10%, #fff 50%, #ccc 90%);
    }
  }
}
</style>
src/components/fileInfo.vue
New file
@@ -0,0 +1,183 @@
<template>
  <div class="">
    <el-form
      ref="form"
      :model="info"
      label-position="top"
      label-width="80px"
      size="mini"
    >
      <el-row :gutter="40">
        <el-col :span="8">
          <el-form-item label="区域">
            <el-row :gutter="10">
              <el-col :span="11">
                <el-input
                  v-model="info.battStation"
                  placeholder="请输入站点名称"
                ></el-input>
              </el-col>
              <el-col class="line" :span="2">-</el-col>
              <el-col :span="11">
                <el-input
                  :span="11"
                  v-model="info.battlineName"
                  placeholder="请输入线路名称"
                ></el-input>
              </el-col>
            </el-row>
          </el-form-item>
        </el-col>
        <el-col :span="8">
          <el-form-item label="系统">
            <el-input
              v-model="info.TODO"
              placeholder="请输入系统名称"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="8">
          <el-form-item label="电池组名称">
            <el-input
              v-model="info.battGroupName"
              placeholder="请输入电池组名称"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="电池品牌">
            <el-input
              v-model="info.battBrand"
              placeholder="请输入电池品牌"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="电池型号">
            <el-input
              v-model="info.battModel"
              placeholder="请输入电池型号"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="电池类型(V)">
            <el-input
              v-model="info.battVol"
              placeholder="请输入电池类型"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="电池数量(节)">
            <el-input
              v-model="info.battCount"
              placeholder="请输入电池数量"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="标称容量(Ah)">
            <el-input
              v-model="info.battCap"
              placeholder="请输入标称容量"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="参考内阻(mΩ)">
            <el-input
              v-model="info.battRes"
              placeholder="请输入参考内阻"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="连接条(μΩ)">
            <el-input v-model="info.battGroupName"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="安装时间">
            <el-input
              v-model="info.TODO"
              placeholder="请选择安装时间"
            ></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-form-item label="外观标记">
        <el-radio-group v-model="info.TODO" size="medium">
          <el-radio border class="good" label="良好"></el-radio>
          <el-radio border label="鼓包"></el-radio>
          <el-radio border label="漏液"></el-radio>
          <el-radio border label="开裂"></el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    <div class="footer">
      <el-button type="primary" class="btn" @click="editOk">确认修改</el-button>
      <el-button type="warning" class="btn" @click="editCancel">取消修改</el-button>
      <el-button type="info" class="btn" @click="quit">退出</el-button>
    </div>
  </div>
</template>
<script>
export default {
  name: "FileInfo",
  props: {
    info: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {};
  },
  components: {},
  methods: {
    editOk() {
      this.$emit('ok', this.info);
    },
    editCancel() {
      this.$emit('cancel', this.info);
    },
    quit() {
      this.$emit('quit');
    }
  },
  mounted() {},
};
</script>
<style lang="less" scoped>
:deep(.el-form-item__label) {
  color: #333;
  font-weight: bold;
}
:deep(.el-form--label-top) .el-form-item__label {
  padding-bottom: 0;
}
:deep(.el-radio__input) {
  display: none;
}
:deep(.el-radio--medium).is-checked.is-checked {
  background: #edb20a;
  border-color: #edb20a;
  &.good {
    background: green;
    border-color: green;
  }
  .el-radio__label {
    color: #fff;
  }
}
.footer {
  display: flex;
  justify-content: flex-end;
}
</style>
src/components/menuList.vue
@@ -134,15 +134,20 @@
    },
    openFile() {
      // console.log("openfile");
      window.api.send("open-file-dialog");
      window.api.send("open-file-dialog", 'MenuList');
    },
    closeFile() {
      this.$bus.$emit('closeFile');
    },
    closeAllFiles() {
      this.$bus.$emit('closeAll');
    },
    initEvents() {
      window.api.receive("selected-file", (path) => {
        console.log(path.filePaths);
      window.api.receive("selected-file", (path, data) => {
        if (data && data == 'MenuList') {
          console.log(path.filePaths, 'MenuList');
        }
        // this.path = path.filePaths;
      });
      window.api.receive("selected-directory", (path) => {
        console.log(path.filePaths);
      });
    },
  },
@@ -165,12 +170,12 @@
    .title {
      cursor: pointer;
      border: 1px solid #333;
      background: #fbfbfb;
      background: #0DCED1;
      line-height: 30px;
      border-radius: 4px;
      vertical-align: middle;
      &.active {
        background: #169bd5;
        background: #00f7f9;
      }
      &:hover {
        background: #62cbfa;
@@ -185,11 +190,11 @@
    left: 0;
    top: 100%;
    min-width: 8em;
    background: #f0f0f0;
    background: #c0c0c0;
    padding: 4px;
    .sub-li {
      border-radius: 4px;
      border: 1px #333 solid;
      border: 1px #fff solid;
      background: #d9dce2;
      &:hover {
        background: #169bd5;
src/components/myCharts/BaseChart.vue
New file
@@ -0,0 +1,162 @@
<template>
  <div
    class="e-chart-root"
    :class="{'full-screen': fullScreenFlag}"
    @dblclick="fullScreen">
    <div class="e-chart-container">
      <div class="e-chart" ref="chart"></div>
    </div>
    <div class="export-chart-wrapper">
      <div class="export-chart" ref="exportChart"></div>
    </div>
  </div>
</template>
<script>
// 引入 ECharts 主模块
import ECharts from "echarts";
// 引入自定义主题
import "./theme/transparent";
export default {
  name: "BaseChart",
  chart: "",
  exportChart: "",
  data() {
    return {
      fullScreenFlag: false,
    };
  },
  methods: {
    /**
     * 设置echarts图表的配置项
     * @param option echarts标准的配置项
     */
    setOption(option) {
      let chart = this.$options.chart;
      if (chart) {
        chart.setOption(option);
      }
    },
    /**
     * 全屏
     */
    fullScreen() {
      this.fullScreenFlag = !this.fullScreenFlag;
      this.$nextTick(()=>{
        this.resize();
      });
    },
    /**
     * 导出echarts600*400的图片
     * @returns {Base64} 图片的base64字符串
     */
    getDataURL() {
      let chart = this.$options.chart;
      let exportChart = this.$options.exportChart;
      let base64 = "";
      if(exportChart) {
        let option = chart.getOption();
        // x轴样式
        option.xAxis[0].axisLine.lineStyle = {
          color: "#000"
        };
        option.xAxis[0].axisLabel.textStyle.color = "#000";
        // y轴样式
        option.yAxis[0].axisLine.lineStyle = {
          color: "#000"
        };
        option.yAxis[0].axisLabel.textStyle.color = "#000";
        exportChart.setOption(option);
        base64 = exportChart.getDataURL({
          pixelRatio: 1,
          backgroundColor: '#fff'
        });
      }
      return base64;
    },
    /**
     * 对echarts进行缩放
     */
    resize() {
      let chart = this.$options.chart;
      if (chart) {
        chart.resize();
      }
    },
    /**
     * 销毁echarts实例释放内存
     * @return  {[type]}  [return description]
     */
    dispose() {
      // 销毁chart
      let chart = this.$options.chart;
      this.disposeChart(chart);
      // 销毁chart
      let exportChart = this.$options.exportChart;
      this.disposeChart(exportChart);
    },
    /**
     * 销毁echarts实例释放内存
     * @param chart echarts实例
     */
    disposeChart(chart) {
      if (chart) {
        chart.dispose(); // 销毁实例
        this.$options.chart = "";
      }
    },
    getChart() {
      return this.$options.chart;
    }
  },
  mounted() {
    this.$options.chart = ECharts.init(this.$refs.chart, "transparent");
    this.$options.exportChart = ECharts.init(this.$refs.exportChart, "transparent");
    // 监听windows窗口的缩放,绑定resize事件
    window.addEventListener("resize", this.resize);
  },
  beforeDestroy() {
    // 销毁resize事件
    window.removeEventListener("resize", this.resize);
    this.dispose();
  }
}
</script>
<style scoped>
/* chart wrapper css */
.e-chart-root,
.e-chart-container,
.e-chart {
  height: 100%;
  box-sizing: border-box;
}
.e-chart-root.full-screen .e-chart-container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-size: 100% 100%;
  z-index: 9999;
}
/* export chart wrapper css */
.export-chart-wrapper {
  position: relative;
  width: 0;
  height: 0;
  overflow: hidden;
}
.export-chart {
  position: absolute;
  width: 600px;
  height: 400px;
}
</style>
src/components/myCharts/NormalBar.vue
New file
@@ -0,0 +1,102 @@
<script>
import BaseChart from "@/components/myCharts/BaseChart";
export default {
  name: "NormalBar",
  extends: BaseChart,
  props: {
    name: {
      type: String,
      default: "",
    },
    unit: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      labelVisible: false
    };
  },
  methods: {
    setData(data) {
      let option = this.getOption(data);
      this.setOption(option);
    },
    /**
     * 全屏
     */
    fullScreen() {
      return false;
    },
    toggleLabelVisible() {
      this.labelVisible = !this.labelVisible;
      let opts = this.$options.chart.getOption();
      this.setData(opts);
    },
    getOption(data) {
      let xLabel = data.xLabel || (data.xAxis ? data.xAxis[0].data : []);
      let sData = data.sData || (data.series ? data.series[0].data : []);
      let sName = this.name;
      let yName = this.yName;
      let min = sData.length == 0 ? 0 : null;
      let max = sData.length == 0 ? 0 : null;
      return {
        animation: false,
        tooltip: {},
        grid: {
          top: "28px",
          left: "1%",
          right: "1%",
          bottom: "8px",
          containLabel: true,
        },
        xAxis: {
          data: xLabel,
        },
        yAxis: [
          {
            name: yName,
            type: "value",
            splitLine: {
              show: true,
              lineStyle: {
                color: "#B2FFFF",
                width: 0.5,
                type: "dotted",
              },
            },
            min: min,
            max: max,
          },
        ],
        series: [
          {
            name: sName,
            type: "bar",
            data: sData,
            label: {
              show: this.labelVisible,
              rotate: 30,
              position: 'top'
            },
          },
        ],
      };
    },
  },
  computed: {
    yName() {
      let unit = this.unit;
      return unit ? "Y(" + unit + ")" : "Y";
    },
  },
  mounted() {
    this.setData({
      xLabel: [],
      sData: [],
    });
  },
};
</script>
src/components/myCharts/theme/transparent.js
New file
@@ -0,0 +1,178 @@
/*
* 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.
*/
/*
* 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.
*/
// 引入 ECharts 主模块
import ECharts from "echarts/lib/echarts";
var contrastColor = '#00feff';
var axisCommon = function () {
  return {
    axisLine: {
      lineStyle: {
        color: contrastColor
      }
    },
    axisTick: {
      show: false,
      lineStyle: {
        color: contrastColor
      }
    },
    axisLabel: {
      show: true,
      textStyle: {
        color: contrastColor
      }
    },
    splitLine: {
      show: false,
      lineStyle: {
        type: 'solid',
        color: '#0b388a'
      }
    },
    splitArea: {
      areaStyle: {
        color: contrastColor
      }
    }
  };
};
var colorPalette = ['#15E3F3', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53', '#eedd78', '#73a373', '#73b9bc', '#7289ab', '#91ca8c', '#f49f42'];
var theme = {
  color: colorPalette,
  backgroundColor: '',
  tooltip: {
    axisPointer: {
      lineStyle: {
        color: contrastColor
      },
      crossStyle: {
        color: contrastColor
      },
      label: {
        color: '#000'
      }
    }
  },
  legend: {
    textStyle: {
      color: contrastColor
    }
  },
  textStyle: {
    color: contrastColor
  },
  title: {
    textStyle: {
      color: contrastColor
    }
  },
  toolbox: {
    show: false,
    iconStyle: {
      normal: {
        borderColor: contrastColor
      }
    }
  },
  dataZoom: {
    textStyle: {
      color: contrastColor
    }
  },
  visualMap: {
    textStyle: {
      color: contrastColor
    }
  },
  timeline: {
    lineStyle: {
      color: contrastColor
    },
    itemStyle: {
      normal: {
        color: colorPalette[1]
      }
    },
    label: {
      normal: {
        textStyle: {
          color: contrastColor
        }
      }
    },
    controlStyle: {
      normal: {
        color: contrastColor,
        borderColor: contrastColor
      }
    }
  },
  timeAxis: axisCommon(),
  logAxis: axisCommon(),
  valueAxis: axisCommon(),
  categoryAxis: axisCommon(),
  line: {
    symbol: 'circle'
  },
  graph: {
    color: colorPalette
  },
  gauge: {
    title: {
      textStyle: {
        color: contrastColor
      }
    }
  },
  candlestick: {
    itemStyle: {
      normal: {
        color: '#FD1050',
        color0: '#0CF49B',
        borderColor: '#FD1050',
        borderColor0: '#0CF49B'
      }
    }
  }
};
ECharts.registerTheme('transparent', theme);
src/components/tabsBar.vue
New file
@@ -0,0 +1,342 @@
<template>
  <div class="tabs-contain">
    <div
      class="tab-item active-tab"
      v-if="currentPage && (currentPage != '/empty' || currentPage != '/')"
    >
      <div class="title">
        {{ pageName(currentPage) }}
      </div>
      <i class="el-icon-close icon-close" @click="remove(currentPage)" />
    </div>
    <div class="others">
      <div
        :class="['posA', { 'left-shadow': !isLeft, 'right-shadow': !isEnd }]"
        ref="container"
      >
        <div
          class="inner"
          ref="inner"
          :style="{ transform: 'translateX(' + translateX + 'px)' }"
        >
          <div
            class="tab-item"
            v-for="(page, idx) in list"
            :key="'list_' + idx"
          >
            <div class="title" @click="onTabClick(page)">
              {{ pageName(page) }}
            </div>
            <i class="el-icon-close icon-close" @click="remove(page)" />
          </div>
        </div>
      </div>
    </div>
    <div class="btn-grp" v-if="needScroll">
      <div
        :class="['btn', 'el-icon-caret-left', { disabled: isLeft }]"
        @click="scrollLeft"
      ></div>
      <div
        :class="['btn', 'el-icon-caret-right', { disabled: isEnd }]"
        @click="scrollRight"
      ></div>
    </div>
  </div>
</template>
<script>
export default {
  name: "TabsBar",
  props: {},
  watch: {
    $route(newRoute) {
      this.activePage = newRoute.path;
      // 判断是否需要缓存
      if (this.checkCache(newRoute)) {
        this.addCache(newRoute);
        this.pageList.push(this.createPage(newRoute));
      } else {
        this.$store.dispatch("tagsView/updateVisitedViewName", newRoute);
      }
    },
  },
  computed: {
    list() {
      return this.pageList.filter(
        (v) => v.path != this.activePage && v.path != "/" && v.path != "/empty"
      );
    },
    currentPage() {
      return this.pageList.filter(
        (v) => v.path == this.activePage && v.path != "/" && v.path != "/empty"
      )[0];
    },
    isLeft() {
      return this.translateX == 0;
    },
    isEnd() {
      this.needScroll;
      let containerW = this.$refs.container
        ? this.$refs.container.clientWidth
        : 0;
      let innerW = this.$refs.inner ? this.$refs.inner.clientWidth : 0;
      // console.log(this.translateX, containerW, innerW, '-=-==-=-=-=-');
      return containerW >= innerW + this.translateX;
    },
  },
  data() {
    return {
      pageList: [],
      activePage: "",
      needScroll: false,
      translateX: 0,
    };
  },
  components: {},
  methods: {
    checkCache(route) {
      let { title } = this.createPage(route);
      return this.$store.state.tagsView.cachedViews.indexOf(title) == -1;
    },
    addCache(route) {
      let { title } = this.createPage(route);
      this.$store.dispatch("tagsView/addView", { route, title });
    },
    createPage(route) {
      return {
        keyPath: route.matched[route.matched.length - 1].path,
        fullPath: route.fullPath,
        loading: false,
        path: route.path,
        name: route.name,
        title: route.params && route.params.filename,
        query: route.query,
        params: route.params,
      };
    },
    pageName(page) {
      console.log(page, "pageName");
      return page.title;
    },
    /**
     * 添加监听器
     */
    addListener() {
      // window.addEventListener("page:close", this.closePageListener);
      window.addEventListener("resize", this.checkScroll);
      // window.addEventListener('page:refresh', this.refreshPageListener)
      // window.addEventListener('unload', this.unloadListener)
    },
    /**
     * 移出监听器
     */
    removeListener() {
      // window.removeEventListener("page:close", this.closePageListener);
      window.removeEventListener("resize", this.checkScroll);
      // window.removeEventListener('page:refresh', this.refreshPageListener)
      // window.removeEventListener('unload', this.unloadListener)
    },
    /**
     * 页签关闭事件监听
     * @param event 页签关闭事件
     */
    // closePageListener(event) {
    //   const { closeRoute, nextRoute } = event.detail;
    //   const closePath =
    //     typeof closeRoute === "string" ? closeRoute : closeRoute.path;
    //   const path = closePath && closePath.split("?")[0];
    //   this.remove(path, nextRoute);
    // },
    changePage(key) {
      this.activePage = key;
      const page = this.pageList.find((item) => item.path === key);
      this.$router.push(page.fullPath);
    },
    remove(page) {
      // if (this.pageList.length === 1) {
      //   return this.$message.warning('最后一个了');
      // }
      const { path, title } = page;
      //清除缓存
      this.$store.dispatch("tagsView/delView", title);
      let index = this.pageList.findIndex((item) => item.path === path);
      this.pageList.splice(index, 1);
      if (path === this.activePage) {
        index =
          index >= this.pageList.length ? this.pageList.length - 1 : index;
        this.activePage = this.pageList.length
          ? this.pageList[index].path
          : "/";
        const nextPage = this.pageList.length ? this.pageList[index] : {};
        if ("result" == nextPage.name) {
          this.$router.push({
            path: "/result/" + nextPage.title,
            query: nextPage.query,
          });
        } else {
          this.$router.push(this.activePage);
        }
      }
      this.checkScroll();
    },
    onTabClick(page) {
      const { path, name, query, title } = page;
      this.activePage = path;
      // const page = this.pageList.find((item) => item.path === path);
      if ("result" == name) {
        this.$router.push({
          path: "/result/" + title,
          query,
        });
      } else {
        this.$router.push(this.activePage);
      }
    },
    // onClose(path) {
    //   this.pageList = this.pageList.filter((v) => v.path != path);
    //   this.activePage = this.pageList.length ? this.pageList[0].path : "";
    // },
    checkScroll() {
      this.$nextTick(() => {
        let container = this.$refs.container;
        let inner = this.$refs.inner;
        let containerW = container.clientWidth;
        let innerW = inner.clientWidth;
        this.needScroll = containerW < innerW;
        if (this.translateX < 0 && containerW - this.translateX > innerW) {
          this.translateX = containerW - innerW < 0 ? containerW - innerW : 0;
        }
      });
    },
    scrollLeft() {
      if (this.isLeft) {
        return false;
      }
      let innerW = this.$refs.inner.clientWidth;
      let containerW = this.$refs.container.clientWidth;
      let x = containerW > 600 ? containerW * 0.9 : containerW;
      if (this.translateX + x >= 0) {
        x = -this.translateX;
      }
      this.translateX += x;
    },
    scrollRight() {
      if (this.isEnd) {
        return false;
      }
      let containerW = this.$refs.container.clientWidth;
      let innerW = this.$refs.inner.clientWidth;
      let x = containerW > 600 ? containerW * 0.9 : containerW;
      if (containerW - this.translateX + x > innerW) {
        x = innerW + this.translateX - containerW;
      }
      this.translateX -= x;
    },
  },
  mounted() {
    this.addListener();
    this.$bus.$on("checkScroll", this.checkScroll);
    this.$bus.$on("closeFile", () => {
      if (this.currentPage) {
        this.remove(this.currentPage);
      }
    });
  },
};
</script>
<style lang="less" scoped>
.tabs-contain {
  height: 32px;
  box-shadow: 0 1px 4px -2px #333;
  align-items: center;
  background: #ddd;
  display: flex;
  .tab-item {
    position: relative;
    align-self: flex-end;
    border: 1px solid #1aafea;
    border-radius: 5px 5px 0 0;
    padding: 6px 26px 0 4px;
    cursor: pointer;
    &.active-tab {
      background: #1aafea;
    }
    .icon-close {
      position: absolute;
      width: 14px;
      height: 14px;
      font-size: 10px;
      line-height: 14px;
      text-align: center;
      right: 4px;
      top: 4px;
      border-radius: 50%;
      background: rgba(122, 122, 122, 0.6);
      color: #fff;
      &:hover {
        background: rgba(255, 39, 39, 0.6);
      }
    }
  }
  .others {
    flex: 1;
    align-self: stretch;
    position: relative;
    .posA {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      overflow: hidden;
      display: flex;
      align-items: flex-end;
      &.left-shadow::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        box-shadow: 0 0 1px #333;
      }
      &.right-shadow::after {
        content: "";
        position: absolute;
        right: 0;
        top: 0;
        bottom: 0;
        box-shadow: 0 0 1px #333;
      }
      .inner {
        white-space: nowrap;
        text-align: left;
        transition: transform 300ms;
        .tab-item {
          display: inline-block;
          border: 1px #ccc solid;
          min-width: 160px;
        }
      }
    }
  }
  .btn-grp {
    align-self: center;
    .btn {
      display: inline-block;
      cursor: pointer;
      font-size: 22px;
      &.disabled {
        background: #eee;
        color: #aaa;
        cursor: not-allowed;
      }
    }
  }
}
</style>
src/main.js
@@ -1,16 +1,22 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import './assets/iconfont/iconfont.css';
import 'element-ui/lib/theme-chalk/index.css'
import Bus from './assets/js/bus'
Vue.prototype.$bus = Bus;
Vue.config.productionTip = false
Vue.use(ElementUI, {
  zIndex: 99
  zIndex: 1099
});
new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')
src/pages/empty.vue
New file
@@ -0,0 +1,20 @@
<template>
  <div class=""></div>
</template>
<script>
export default {
  name: "",
  data() {
    return {};
  },
  components: {},
  methods: {},
  mounted() {},
};
</script>
<style scoped>
</style>
src/pages/xmlResult.vue
New file
@@ -0,0 +1,534 @@
<template>
  <!-- <p>params: {{ $route.params }}</p> -->
  <div class="main" ref="main">
    <div class="container" ref="container">
      <div
        class="item"
        v-for="item in windowList"
        :ref="item + 'wrap'"
        :key="'list_' + item"
      >
        <div class="bar">
          <div class="title">{{ titles[item] }}</div>
          <div class="btn-grp">
            <div
              :class="[
                'btn',
                'iconfont',
                {
                  'icon-quanping': !fullScreen,
                  'icon-tuichuquanping': fullScreen,
                },
              ]"
              :title="fullScreen ? '还原' : '全屏'"
              @click="toggleScreen(item)"
            ></div>
            <div
              class="btn iconfont icon-guanbi"
              title="关闭"
              @click="closeItem(item)"
            ></div>
          </div>
        </div>
        <div class="content">
          <normal-bar
            v-if="item != 'tableVisiable'"
            :ref="item"
            @contextmenu.native="(e) => chartContextClick(e, item)"
          ></normal-bar>
          <div class="table-wrap" v-else>
            <el-table
              :data="tableData"
              style="width: 100%"
              :default-sort="{ prop: 'date', order: 'descending' }"
              stripe
              size="small"
              height="100%"
              class="tableCent"
              tooltip-effect="light"
              show-summary
              :summary-method="summaryMethod"
            >
              <el-table-column
                v-for="header in headers"
                :key="header.prop"
                :prop="header.prop"
                :label="header.label"
                :width="header.width"
                :min-width="header.minWidth"
                sortable
                :sort-method="(a, b) => sortMethod(a, b, header.prop)"
                show-overflow-tooltip
                align="center"
              ></el-table-column>
            </el-table>
          </div>
        </div>
      </div>
    </div>
    <!-- 图表右键菜单 -->
    <chart-context-menu
      :getParentNode="getChartContextParentNode"
      v-model="contextMenu.visible"
      :context-data="contextMenu.node"
      :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
      @toggleLabel="toggleLabel"
      @updateChart="updateChart"
    ></chart-context-menu>
  </div>
</template>
<script>
import NormalBar from "@/components/myCharts/NormalBar";
import ChartContextMenu from "@/components/chartContextMenu";
import offset from "@/assets/js/offset";
import { mapGetters } from "vuex";
import { getXmlValue } from "@/apis";
export default {
  name: "",
  data() {
    const titles = {
      resVisiable: "内阻(mΩ)",
      volVisiable: "电压(V)",
      capVisiable: "容量(Ah)",
      chainVisiable: "连接条(uΩ)",
      condVisiable: "电导(S)",
      tableVisiable: "数据表格",
    };
    return {
      fullScreen: false,
      titles,
      list: [],
      fileUrl: this.$route.query.url,
      tableData: [],
      headers: [
        {
          prop: "monNum",
          label: "电池",
          minWidth: 80,
        },
        {
          prop: "bv",
          label: "电压(V)",
          minWidth: 100,
        },
        {
          prop: "br",
          label: "内阻(mΩ)",
          minWidth: 100,
        },
        {
          prop: "cr",
          label: "连接条(mΩ)",
          minWidth: 120,
        },
        {
          prop: "bs",
          label: "电导(S)",
          minWidth: 100,
        },
      ],
      contextMenu: {
        x: 0,
        y: 0,
        visible: false,
        node: {},
      },
      windowList: [],
    };
  },
  components: {
    NormalBar,
    ChartContextMenu,
  },
  computed: {
    ...mapGetters("setting", [
      "volVisiable",
      "resVisiable",
      "capVisiable",
      "chainVisiable",
      "condVisiable",
      "tableVisiable",
    ]),
    // windowList() {
    //   return [
    //     "resVisiable",
    //     "volVisiable",
    //     "capVisiable",
    //     "condVisiable",
    //     "chainVisiable",
    //     "tableVisiable",
    //   ].filter((v) => this[v]);
    // },
  },
  methods: {
    formatData(data) {
      let xLabel = [];
      let resData = [],
        volData = [],
        condData = [],
        capData = [],
        chainData = [],
        tableData = [];
      data.forEach((v) => {
        xLabel.push("#" + v.monNum);
        resData.push(v.br);
        volData.push(v.bv);
        condData.push(v.cr);
        // capData.push() TODO
        chainData.push(v.cr);
        tableData.push({ ...v, monNum: "#" + v.monNum });
      });
      return {
        xLabel,
        resData,
        volData,
        condData,
        capData,
        chainData,
        tableData,
      };
    },
    formatNum(n) {
      return isNaN(n)
        ? ("" + n).indexOf("#") != -1
          ? ("" + n).replace("#", "") * 1
          : 0
        : n;
    },
    sortMethod(a, b, prop) {
      console.log(a, b);
      a = this.formatNum(a[prop]);
      b = this.formatNum(b[prop]);
      return a - b;
    },
    summaryMethod(params) {
      let { columns, data } = params;
      let res = [];
      // console.log(columns, data);
      let len = data.length;
      columns.forEach((v, i) => {
        if (0 == i) {
          res[i] = "均一性";
          return;
        }
        let values = data.map((val) => Number(val[v.property]));
        if (values.some((v) => isNaN(v))) {
          res[i] = "N/A";
        } else {
          // 平均值
          let average = values.reduce((total, v) => total + v, 0) / len;
          // 差值平方和
          let sum = values.reduce(
            (total, v) => total + Math.pow(v - average, 2),
            0
          );
          res[i] = Math.round(Math.sqrt(sum / len) * 10000) / 100 + "%";
        }
      });
      return res;
    },
    toggleLabel(data) {
      this.$refs[data][0].toggleLabelVisible();
    },
    updateChart(data) {
      this.$refs[data.ref][0].setData(data.data);
    },
    getFileInfo() {
      const params = {
        filePath: this.fileUrl,
      };
      getXmlValue(params).then((res) => {
        const { code, data, data2 } = res.data;
        if (code && data) {
          // console.log(data2);
          this.list = data2.battInfoList[0].battDataList;
          this.initChart();
        } else {
          this.$message.error("文件解析失败");
        }
      });
    },
    initChart() {
      let {
        xLabel,
        resData,
        volData,
        condData,
        capData,
        chainData,
        tableData,
      } = this.formatData(this.list);
      this.tableData = tableData;
      const obj = {
        resVisiable: resData,
        volVisiable: volData,
        capVisiable: capData,
        chainVisiable: chainData,
        condVisiable: condData,
      };
      this.windowList.forEach((v) => {
        let chart = this.$refs[v];
        // debugger;
        if (!chart) {
          return;
        }
        chart[0].setData({
          xLabel,
          sData: obj[v],
        });
        chart[0].resize();
      });
    },
    resize() {
      this.windowList.forEach((v) => {
        let chart = this.$refs[v];
        if (!chart) {
          return;
        }
        chart[0].resize();
      });
    },
    chartContextClick($e, item) {
      const { clientX, clientY, target } = $e;
      const container = this.$refs.container;
      let { left, top } = offset(container);
      if (this.fullScreen) {
        left = 0;
        top = 0;
      }
      let y = clientY - top;
      let x = clientX - left;
      let { clientHeight: bodyHeight, clientWidth: bodyWidth } = document.body;
      if (clientY + 105 > bodyHeight) {
        y = bodyHeight - 105 - top;
      }
      if (clientX + 120 > bodyWidth) {
        x = bodyWidth - 120 - left;
      }
      let { xLabel, resData, volData, condData, capData, chainData } =
        this.formatData(this.list);
      const obj = {
        resVisiable: resData,
        volVisiable: volData,
        capVisiable: capData,
        chainVisiable: chainData,
        condVisiable: condData,
      };
      this.contextMenu.x = x;
      this.contextMenu.y = y;
      this.contextMenu.node = { xLabel, data: obj[item], ref: item };
      this.contextMenu.visible = true;
    },
    addListener() {
      document.addEventListener("fullscreenchange", this.fullScreenListener);
      document.addEventListener(
        "webkitfullscreenchange",
        this.fullScreenListener
      );
    },
    removeListener() {
      document.removeEventListener("fullscreenchange", this.fullScreenListener);
      document.removeEventListener(
        "webkitfullscreenchange",
        this.fullScreenListener
      );
    },
    fullScreenListener() {
      // console.log(e);
      this.fullScreen = !this.fullScreen;
      this.resize();
    },
    toggleScreen(item) {
      if (this.fullScreen) {
        this.outFullScreen(item);
      } else {
        this.inFullScreen(item + "wrap");
      }
    },
    inFullScreen(item) {
      const el = this.$refs[item][0];
      if (el.requestFullscreen) {
        el.requestFullscreen();
        return true;
      } else if (el.webkitRequestFullScreen) {
        el.webkitRequestFullScreen();
        return true;
      }
      return false;
    },
    outFullScreen() {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.webkitCancelFullScreen) {
        document.webkitCancelFullScreen();
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
      } else if (document.msExitFullscreen) {
        document.msExitFullscreen();
      }
    },
    getChartContextParentNode() {
      const ref = this.contextMenu.node.ref + "wrap";
      if (!this.fullScreen) {
        return this.$refs.main;
      } else {
        return this.$refs[ref][0];
      }
    },
    closeItem(item) {
      if (this.fullScreen) {
        this.toggleScreen(item);
      }
      if (this.windowList.length <= 1) {
        this.$message.warning("最少保留一个模块");
        return;
      }
      const idx = this.windowList.indexOf(item);
      // console.log(idx)
      this.windowList.splice(idx, 1);
      this.$nextTick(() => {
        this.resize();
      });
    },
    getWindowList() {
      this.windowList = [
        "resVisiable",
        "volVisiable",
        "capVisiable",
        "condVisiable",
        "chainVisiable",
        "tableVisiable",
      ].filter((v) => this[v]);
    },
  },
  mounted() {
    const {
      volVisiable,
      resVisiable,
      capVisiable,
      tableVisiable,
      chainVisiable,
      condVisiable,
    } = this;
    this.getWindowList();
    console.log(
      volVisiable,
      resVisiable,
      capVisiable,
      tableVisiable,
      chainVisiable,
      condVisiable,
      this.windowList,
      this.list
    );
    this.getFileInfo();
    // setTimeout(() => {
    //   // this.initChart();
    // }, 0);
    this.addListener();
    this.$bus.$on("checkScroll", this.resize);
  },
};
</script>
<style lang="less" scoped>
.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-wrap: wrap;
}
.item {
  display: flex;
  flex-direction: column;
  .bar {
    display: flex;
    justify-content: space-between;
    background: #f0f0f0;
    padding-left: 8px;
    padding-right: 8px;
    .btn-grp {
      display: flex;
      align-items: center;
      .btn {
        margin: 0 4px;
        cursor: pointer;
        &:hover {
          background: rgba(200, 200, 200, 0.6);
        }
      }
    }
  }
  .content {
    flex: 1;
    background: #0029a0;
    position: relative;
    .table-wrap {
      position: absolute;
      top: 0;
      right: 0;
      left: 0;
      bottom: 0;
      :deep(.el-table__footer) tr {
        background: #fa8e34;
        td {
          background: transparent;
          color: #fff;
        }
      }
    }
  }
}
.item:only-child {
  width: 100%;
  height: 100%;
}
.item ~ .item {
  margin-left: 4px;
}
.item:first-child:nth-last-child(3) ~ .item:last-child,
.item:first-child:nth-last-child(4) ~ .item:nth-child(3),
.item:first-child:nth-last-child(5) ~ .item:nth-child(4),
.item:first-child:nth-last-child(6) ~ .item:nth-child(4) {
  margin-left: 0;
}
.item:first-child:nth-last-child(2),
.item:first-child:nth-last-child(2) ~ .item {
  width: calc(50% - 2px);
  height: 100%;
}
.item:first-child:nth-last-child(3),
.item:first-child:nth-last-child(3) + .item {
  width: calc(50% - 2px);
  height: 50%;
}
.item:first-child:nth-last-child(3) ~ .item:last-child {
  width: 100%;
  height: 50%;
}
.item:first-child:nth-last-child(4),
.item:first-child:nth-last-child(4) ~ .item {
  width: calc(50% - 2px);
  height: 50%;
}
.item:first-child:nth-last-child(5),
.item:first-child:nth-last-child(5) ~ .item {
  width: calc((100% - 4px * 2) / 3);
  height: 50%;
}
.item:first-child:nth-last-child(5) ~ .item:nth-last-child(2),
.item:first-child:nth-last-child(5) ~ .item:last-child {
  width: calc(50% - 2px);
  height: 50%;
}
.item:first-child:nth-last-child(6),
.item:first-child:nth-last-child(6) ~ .item {
  width: calc((100% - 4px * 2) / 3);
  height: 50%;
}
</style>
src/preload.js
@@ -21,6 +21,7 @@
  receive: (channel, func) => {
    if (validChannels.includes(channel)) {
      ipcRenderer.on(channel, (event, ...args) => {
        // console.log(args, 'args===')
        func(...args)
      });
    }
src/router/routes.js
@@ -1,13 +1,18 @@
export default [{
    path: '/',
    // redirect: '/selectFile'
    redirect: '/empty'
  },
  // 选文件页面
  // {
  //   path: '/selectFile',
  //   name: 'selectFile',
  //   meta: {},
  //   component: () => import('@/views/selectFile')
  // },
  {
    path: '/result/:filename',
    name: 'result',
    meta: {},
    component: () => import('@/pages/xmlResult')
  },
  // 空白页面
  {
    path: '/empty',
    name: 'empty',
    meta: {},
    component: () => import('@/pages/empty')
  },
];
src/store/index.js
New file
@@ -0,0 +1,8 @@
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
const store = new Vuex.Store({modules})
export default store
src/store/modules/index.js
New file
@@ -0,0 +1,4 @@
import setting from './setting'
import tagsView from './tagsView'
export default {setting, tagsView}
src/store/modules/setting.js
New file
@@ -0,0 +1,105 @@
export default {
  namespaced: true,
  state: {
    volVisiable: true,
    resVisiable: true,
    capVisiable: false,
    tableVisiable: true,
    chainVisiable: true,
    condVisiable: false,
  },
  getters: {
    volVisiable: state => {
      if (state.volVisiable == undefined) {
        try {
          const volVisiable = localStorage.getItem('volVisiable')
          state.volVisiable = JSON.parse(volVisiable)
        } catch (e) {
          console.error(e.message)
        }
      }
      return state.volVisiable
    },
    resVisiable: state => {
      if (state.resVisiable == undefined) {
        try {
          const resVisiable = localStorage.getItem('resVisiable')
          state.resVisiable = JSON.parse(resVisiable)
        } catch (e) {
          console.error(e.message)
        }
      }
      return state.resVisiable
    },
    capVisiable: state => {
      if (state.capVisiable == undefined) {
        try {
          const capVisiable = localStorage.getItem('capVisiable')
          state.capVisiable = JSON.parse(capVisiable)
        } catch (e) {
          console.error(e.message)
        }
      }
      return state.capVisiable
    },
    tableVisiable: state => {
      if (state.tableVisiable == undefined) {
        try {
          const tableVisiable = localStorage.getItem('tableVisiable')
          state.tableVisiable = JSON.parse(tableVisiable)
        } catch (e) {
          console.error(e.message)
        }
      }
      return state.tableVisiable
    },
    chainVisiable: state => {
      if (state.chainVisiable == undefined) {
        try {
          const chainVisiable = localStorage.getItem('chainVisiable')
          state.chainVisiable = JSON.parse(chainVisiable)
        } catch (e) {
          console.error(e.message)
        }
      }
      return state.chainVisiable
    },
    condVisiable: state => {
      if (state.condVisiable == undefined) {
        try {
          const condVisiable = localStorage.getItem('condVisiable')
          state.condVisiable = JSON.parse(condVisiable)
        } catch (e) {
          console.error(e.message)
        }
      }
      return state.condVisiable
    },
  },
  mutations: {
    setVolVisiable(state, data) {
      state.volVisiable = data;
      localStorage.setItem('volVisiable', JSON.stringify(data));
    },
    setResVisiable(state, data) {
      state.resVisiable = data;
      localStorage.setItem('resVisiable', JSON.stringify(data));
    },
    setCapVisiable(state, data) {
      state.capVisiable = data;
      localStorage.setItem('capVisiable', JSON.stringify(data));
    },
    setTableVisiable(state, data) {
      state.tableVisiable = data;
      localStorage.setItem('tableVisiable', JSON.stringify(data));
    },
    setChainVisiable(state, data) {
      state.chainVisiable = data;
      localStorage.setItem('chainVisiable', JSON.stringify(data));
    },
    setCondVisiable(state, data) {
      state.condVisiable = data;
      localStorage.setItem('condVisiable', JSON.stringify(data));
    },
  }
}
src/store/modules/tagsView.js
New file
@@ -0,0 +1,170 @@
const state = {
  visitedViews: [],
  cachedViews: []
}
const mutations = {
  ADD_VISITED_VIEW: (state, view) => {
    if (state.visitedViews.some(v => v.path === view.path)) return
    state.visitedViews.push(
      Object.assign({}, view, {
        title: view.meta.title || 'no-name'
      })
    )
  },
  ADD_CACHED_VIEW: (state, key) => {
    if (state.cachedViews.includes(key)) return
    // if (!view.meta.noCache) {
      state.cachedViews.push(key)
    // }
  },
  DEL_VISITED_VIEW: (state, view) => {
    for (const [i, v] of state.visitedViews.entries()) {
      if (v.path === view.path) {
        state.visitedViews.splice(i, 1)
        break
      }
    }
  },
  DEL_CACHED_VIEW: (state, key) => {
    const index = state.cachedViews.indexOf(key)
    index > -1 && state.cachedViews.splice(index, 1)
  },
  DEL_OTHERS_VISITED_VIEWS: (state, view) => {
    state.visitedViews = state.visitedViews.filter(v => {
      return v.meta.affix || v.path === view.path
    })
  },
  DEL_OTHERS_CACHED_VIEWS: (state, view) => {
    const index = state.cachedViews.indexOf(view.name)
    if (index > -1) {
      state.cachedViews = state.cachedViews.slice(index, index + 1)
    } else {
      // if index = -1, there is no cached tags
      state.cachedViews = []
    }
  },
  DEL_ALL_VISITED_VIEWS: state => {
    // keep affix tags
    const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
    state.visitedViews = affixTags
  },
  DEL_ALL_CACHED_VIEWS: state => {
    state.cachedViews = []
  },
  UPDATE_VISITED_VIEW: (state, view) => {
    for (let v of state.visitedViews) {
      if (v.path === view.path) {
        v = Object.assign(v, view)
        break
      }
    }
  },
  UPDATE_VISITED_VIEW_NAME: (state, view) => {
    for (let v of state.visitedViews) {
      if (v.name === view.name) {
        v = Object.assign(v, view)
        break
      }
    }
  }
}
const actions = {
  addView({ dispatch }, view) {
    dispatch('addVisitedView', view.route)
    dispatch('addCachedView', view.title)
  },
  addVisitedView({ commit }, view) {
    commit('ADD_VISITED_VIEW', view)
  },
  addCachedView({ commit }, view) {
    commit('ADD_CACHED_VIEW', view)
  },
  delView({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delVisitedView', view)
      dispatch('delCachedView', view)
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
  },
  delVisitedView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_VISITED_VIEW', view)
      resolve([...state.visitedViews])
    })
  },
  delCachedView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_CACHED_VIEW', view)
      resolve([...state.cachedViews])
    })
  },
  delOthersViews({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delOthersVisitedViews', view)
      dispatch('delOthersCachedViews', view)
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
  },
  delOthersVisitedViews({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_OTHERS_VISITED_VIEWS', view)
      resolve([...state.visitedViews])
    })
  },
  delOthersCachedViews({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_OTHERS_CACHED_VIEWS', view)
      resolve([...state.cachedViews])
    })
  },
  delAllViews({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delAllVisitedViews', view)
      dispatch('delAllCachedViews', view)
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
  },
  delAllVisitedViews({ commit, state }) {
    return new Promise(resolve => {
      commit('DEL_ALL_VISITED_VIEWS')
      resolve([...state.visitedViews])
    })
  },
  delAllCachedViews({ commit, state }) {
    return new Promise(resolve => {
      commit('DEL_ALL_CACHED_VIEWS')
      resolve([...state.cachedViews])
    })
  },
  updateVisitedView({ commit }, view) {
    commit('UPDATE_VISITED_VIEW', view)
  },
  updateVisitedViewName({ commit }, view) {
    commit('UPDATE_VISITED_VIEW_NAME', view)
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}