he wei
2022-09-20 a9750e068b4cd62caf05f33ed9f83dd052de845b
UA 主菜单和右键菜单
5个文件已修改
9个文件已添加
4479 ■■■■■ 已修改文件
.gitignore 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 3354 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/apis.js 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/axios.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/background.js 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/contextMenu.vue 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/menuList.vue 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/preload.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -21,3 +21,6 @@
*.njsproj
*.sln
*.sw?
#Electron-builder output
/dist_electron
package-lock.json
Diff too large
package.json
@@ -5,19 +5,34 @@
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
    "lint": "vue-cli-service lint",
    "electron:build": "vue-cli-service electron:build",
    "electron:serve": "vue-cli-service electron:serve",
    "postinstall": "electron-builder install-app-deps",
    "postuninstall": "electron-builder install-app-deps"
  },
  "main": "background.js",
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11"
    "vue": "^2.6.11",
    "vue-router": "3.0.6",
    "axios": "0.18.1",
    "element-ui": "^2.15.6",
    "echarts": "^4.8.0",
    "echarts-liquidfill": "^2.0.6"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.13",
    "@vue/cli-plugin-eslint": "~4.5.13",
    "@vue/cli-service": "~4.5.13",
    "babel-eslint": "^10.1.0",
    "electron": "^13.0.0",
    "electron-devtools-installer": "^3.1.0",
    "eslint": "^6.7.2",
    "less": "^3.12.2",
    "less-loader": "^6.2.0",
    "eslint-plugin-vue": "^6.2.2",
    "vue-cli-plugin-electron-builder": "~2.1.1",
    "vue-template-compiler": "^2.6.11"
  },
  "eslintConfig": {
@@ -32,7 +47,11 @@
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
    "rules": {
      "no-unused-vars": "off",
      "vue/no-unused-components": "off",
      "no-debugger": "off"
    }
  },
  "browserslist": [
    "> 1%",
src/App.vue
@@ -1,28 +1,324 @@
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
    <div class="main">
      <div class="left" ref="left">
        <menu-list></menu-list>
        <div class="title">文件列表</div>
        <div class="file-list" @contextmenu.prevent="openContextMenu($event)">
          <el-tree
            :data="stationData"
            @node-click="handleNodeClick"
            @node-contextmenu="nodeContextClick"
          ></el-tree>
        </div>
      </div>
      <div class="handle" ref="handle"></div>
      <div class="right">
        <div class="" @click="select">选文件</div>
        <div class="">当前选中的文件夹为:{{ path }}</div>
        <router-view />
      </div>
    </div>
    <!-- 右键菜单 -->
    <context-menu
      v-model="contextMenu.visible"
      :disabled-list="disabledList"
      :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
    ></context-menu>
  </div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
const minWidth = 200,
  maxWidth = 600;
import MenuList from "@/components/menuList";
import ContextMenu from "@/components/contextMenu";
import { getStation } from "./apis";
export default {
  name: 'App',
  name: "App",
  components: {
    HelloWorld
    MenuList,
    ContextMenu,
  },
  data() {
    return {
      path: "",
      startX: 0,
      leftW: 0,
      stationData: [],
      contextMenu: {
        x: 0,
        y: 0,
        visible: false,
      },
      disabledList: [],
    };
  },
  methods: {
    select() {
      window.api.send("open-file-dialog");
    },
    onMouseDown(e) {
      // const el = e.target;
      this.startX = e.clientX;
      this.leftW = this.$refs.left.clientWidth;
      document.addEventListener("mousemove", this.onMouseMove);
      document.addEventListener("mouseup", this.onMouseUp);
    },
    onMouseUp() {
      // this.$refs.handle.style.background = 'transparent';
      // document.removeEventListener('mousedown', this.onMouseDown);
      document.removeEventListener("mousemove", this.onMouseMove);
    },
    onMouseMove(e) {
      const el = this.$refs.handle;
      const left = this.$refs.left;
      const endX = e.clientX;
      const moveLen = endX - this.startX;
      const CurBoxLen = this.leftW + moveLen;
      if (CurBoxLen <= minWidth || CurBoxLen >= maxWidth) {
        return;
  }
      left.style.width = CurBoxLen + "px";
      el.style.left = CurBoxLen + "px";
    },
    getStation() {
      getStation().then((res) => {
        const { code, data, data2 } = res.data;
        let list = [];
        if (code && data) {
          console.log(1, data2);
          list = this.formatData(data2);
}
        this.stationData = list;
      });
    },
    formatData(data) {
      let res = data.map((v) => {
        let sname2s = [];
        v.sname2s.forEach((val) => {
          if (val.station2 != "-") {
            let children = [];
            val.sname3s.forEach((item) => {
              if (item.station3 != "-") {
                children.push({
                  label: item.station3,
                  children: item.fileNames.map((item1) => ({
                    label: item1.fileName,
                    url: item1.fileUrl,
                    level: 3,
                  })),
                  level: 2,
                });
              } else {
                children.push(
                  ...item.fileNames.map((item1) => ({
                    label: item1.fileName,
                    url: item1.fileUrl,
                    level: 3,
                  }))
                );
              }
            });
            sname2s.push({
              label: val.station2,
              children,
              level: 1,
            });
          } else {
            sname2s.push(
              ...val.sname3s[0].fileNames.map((item) => ({
                label: item.fileName,
                url: item.fileUrl,
                level: 3,
              }))
            );
          }
        });
        return {
          children: sname2s,
          label: v.station1,
          level: 0,
        };
      });
      console.log(res);
      return res;
    },
    handleNodeClick(obj) {
      console.log(123, obj);
    },
    nodeContextClick($e, obj) {
      console.log(obj);
      const { clientX, clientY } = $e;
      this.contextMenu.x = clientX;
      let y = clientY;
      let bodyHeight = document.body.clientHeight;
      if (y + 180 > bodyHeight) {
        y = bodyHeight - 180;
      }
      this.contextMenu.y = y;
      switch (obj.level) {
        case 0:
        case 1:
        case 2:
          // 子站上按鼠标右键,不能“打开文件”、“新建根文件”
          this.disabledList = [0, 11];
          break;
        case 3:
          // 添加的测试文件上按鼠标右键,可以点击“打开文件”、“重命名”、“删除”
          this.disabledList = [1, 2];
          break;
        default:
          return false;
      }
      this.contextMenu.visible = true;
    },
    // 右键菜单
    openContextMenu($e) {
      // console.log($e, "context");
      const {
        clientX,
        clientY,
        target: { classList },
      } = $e;
      this.contextMenu.x = clientX;
      let y = clientY;
      let bodyHeight = document.body.clientHeight;
      if (y + 180 > bodyHeight) {
        y = bodyHeight - 180;
      }
      this.contextMenu.y = y;
      if (classList.contains("file-list")) {
        // 在空白处点击的右键 只能新建
        this.disabledList = [0, 12, 2, 3, 4];
        this.contextMenu.visible = true;
      }
    },
    closeContextMenu() {
      this.contextMenu.visible = false;
    },
    maskClick() {
      this.closeContextMenu();
    },
  },
  mounted() {
    window.api.receive("selected-directory", (path) => {
      this.path = path.filePaths;
    });
    let handle = this.$refs.handle;
    handle.addEventListener("mousedown", this.onMouseDown);
    this.getStation();
  },
};
</script>
<style>
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
#app {
  height: 100%;
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
  color: #000;
  background: #fff;
}
ul,
li {
  list-style: none;
  margin: 0;
  padding: 0;
  user-select: none;
}
</style>
<style lang="less" scoped>
.main {
  height: 100%;
  position: relative;
  display: flex;
}
.left {
  width: 14em;
  box-shadow: 1px 0 2px -1px #000;
  /* background: rgba(200, 200, 200, .8); */
  display: flex;
  flex-direction: column;
  .title {
    background: #999;
    padding: 4px;
  }
  .file-list {
    flex: 1;
    overflow-y: auto;
    text-align: left;
    /deep/ .el-tree {
      color: #333;
      display: inline-block;
      .el-tree-node__content {
        display: inline-block;
      }
      // .el-tree-node.is-expanded>.el-tree-node__children {
      //   display: inline-block;
      // }
    }
  }
}
.handle {
  width: 6px;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 14em;
  background: transparent;
  cursor: col-resize;
}
.handle:hover {
  background: rgba(200, 200, 200, 0.6);
}
.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 {
    display: flex;
    flex-direction: column;
    .main-li {
      cursor: pointer;
      flex: 1;
      border: 1px #ccc solid;
      padding: 4px;
      &:hover {
        background: #f0f0f0;
      }
      & ~ .main-li {
        margin-top: 2px;
      }
    }
  }
}
.trans-mask {
  position: fixed;
  z-index: 99;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: transparent;
}
</style>
src/apis.js
New file
@@ -0,0 +1,62 @@
import axios from "@/assets/js/axios";
/**
 * 查询台站列表
 */
export const getStation = () => {
  return axios({
    method: "GET",
    url: "station/getStation"
  })
}
/**
 * 台站下添加文件
 */
export const addFileInStation = (params) => {
  return axios({
    method: "GET",
    url: "stationInfo/addFileInStation",
    params
  })
}
/**
 * 添加台站
 */
export const addStation = (params) => {
  return axios({
    method: "GET",
    url: "stationInfo/addStation",
    params
  })
}
/**
 * 删除台站
 */
export const deleteStation = (params) => {
  return axios({
    method: "GET",
    url: "stationInfo/deleteStation",
    params
  })
}
/**
 * 编辑台站
 */
export const updateStation = (params) => {
  return axios({
    method: "GET",
    url: "stationInfo/updateStation",
    params
  })
}
/**
 * 解析xml文件
 */
export const getXmlValue = (params) => {
  return axios({
    method: "GET",
    url: "fileInfo/getXmlValue",
    params
  })
}
src/assets/js/axios.js
New file
@@ -0,0 +1,15 @@
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:8093/res/';
axios.defaults.withCredentials = true;  // 保持请求头
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
}, function (error) {
    return Promise.reject(error);
});
export default axios;
src/background.js
New file
@@ -0,0 +1,122 @@
'use strict'
import { app, protocol, BrowserWindow, ipcMain, ipcRenderer, dialog } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
import path from 'path';
import child_process from 'child_process';
const isDevelopment = process.env.NODE_ENV !== 'production'
// const path = require('path');
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow() {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      // nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      // contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
      nodeIntegration: false,
      webSecurity: true,
      allowEval: false,
      allowRunningInsecureContent: false,
      contextIsolation: true,
      enableRemoteModule: false,
      preload: path.join(__dirname, 'preload.js')
    }
  })
  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  // if (isDevelopment && !process.env.IS_TEST) {
  //   // Install Vue Devtools
  //   try {
  //     await installExtension(VUEJS_DEVTOOLS)
  //   } catch (e) {
  //     console.error('Vue Devtools failed to install:', e.toString())
  //   }
  // }
  createWindow()
})
// ipcMain 事件处理
// ipcMain.on('open-file-dialog', (event) => {
//   dialog.showOpenDialog({
//     properties: ['openFile', 'openDirectory']
//   }, (files) => {
//     // child_process.spawn('cmd.exe', ['/c', 'echo '+JSON.stringify(files)+' success >> 112233.txt'])
//     if(files) {
//       event.sender.send('selected-directory', files);
//     }
//   })
// });
ipcMain.on('open-file-dialog', (event) => {
  dialog.showOpenDialog({
    filters: [{ name: 'xml', extensions: ['xml'] }],
    properties: ['openFile']
  }).then((files) => {
    if (files) {
      event.sender.send('selected-file', files);
    }
  })
});
ipcMain.on('open-directory-dialog', (event) => {
  dialog.showOpenDialog({
    properties: ['openDirectory']
  }).then((files) => {
    if (files) {
      event.sender.send('selected-directory', files);
    }
  })
});
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}
src/components/contextMenu.vue
New file
@@ -0,0 +1,264 @@
<template>
  <div class="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', {disabled: item.disabled}]"
            @click="itemClick(item)"
            @mouseenter="itemHover(item)"
          >
            {{ item.title }}
          </div>
          <ul
            class="sub-ul"
            v-show="item.visible && item.children && item.children.length"
          >
            <li
              :class="['sub-li', {disabled: subItem.disabled}]"
              v-for="(subItem, index) in item.children"
              :key="'sub_' + index"
              @click="itemClick(subItem)"
            >
              {{ subItem.title }}
            </li>
          </ul>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
export default {
  name: "ContextMenu",
  model: {
    prop: "visible",
    event: "change",
  },
  props: {
    visible: {
      type: Boolean,
      required: true,
      default: false,
    },
    disabledList: {
      type: Array,
      default() {
        return [];
      },
    },
  },
  data() {
    const menuList = [
      {
        id: 0,
        title: "打开文件",
        method: "openFile",
        disabled: false,
        visible: false,
      },
      {
        id: 1,
        title: "新建",
        disabled: false,
        visible: false,
        children: [
          {
            id: 11,
            title: "新建根文件",
            // method: "openFile",
            disabled: false,
          },
          {
            id: 12,
            title: "新建子文件",
            // method: "openFile",
            disabled: false,
          },
        ],
      },
      {
        id: 2,
        title: "添加",
        disabled: false,
        visible: false,
        children: [
          {
            id: 13,
            title: "指定目录下所有文件",
            disabled: false,
          },
          {
            id: 14,
            title: "单一文件",
            disabled: false,
          },
        ],
      },
      {
        id: 3,
        title: "重命名",
        method: "openFile",
        disabled: false,
        visible: false,
      },
      {
        id: 4,
        title: "删除",
        method: "openFile",
        disabled: false,
        visible: false,
      },
    ];
    return {
      menuList,
    };
  },
  components: {},
  watch: {
    visible(n) {
      if(n) {
        this.initData();
      }
    }
  },
  methods: {
    itemClick(obj) {
      // console.log(obj)
      if (obj.method && "function" == typeof this[obj.method] && !obj.disabled) {
        this.close();
        this[obj.method](obj);
      }
    },
    close() {
      this.invisibleSubMenu();
      this.$emit("change", false);
    },
    showChildren(obj) {
      // this.menuList.forEach((v) => {
      //   v.visible = false;
      // });
      obj.visible = true;
    },
    invisibleSubMenu() {
      this.menuList.forEach((v) => {
        v.visible = false;
      });
    },
    itemHover(obj) {
      if (obj.disabled) {
        return false;
      }
      this.invisibleSubMenu();
      this.$nextTick(() => {
        obj.visible = true;
      });
    },
    openFile() {
      // console.log("openfile");
      window.api.send("open-file-dialog");
    },
    initEvents() {
      window.api.receive("selected-file", (path) => {
        console.log(path.filePaths);
        // this.path = path.filePaths;
      });
      window.api.receive("selected-directory", (path) => {
        console.log(path.filePaths);
      });
    },
    initData() {
      this.menuList.forEach((v) => {
        v.visible = false;
        v.disabled = this.disabledList.some((val) => val == v.id);
        (v.children || []).forEach((item) => {
          item.disabled = this.disabledList.some((val) => val == item.id);
        });
      });
      console.log(this.menuList, this.disabledList, 9090)
    },
  },
  mounted() {
    this.initData();
    this.initEvents();
  },
};
</script>
<style lang="less" scoped>
.context-menu {
  position: absolute;
  background: #fff;
  border: 1px #ccc solid;
  border-radius: 4px;
  padding: 4px;
  .contain {
    position: relative;
    z-index: 100;
    .main-ul {
      display: flex;
      flex-direction: column;
      .main-li {
        position: relative;
        cursor: pointer;
        flex: 1;
        border: 1px #ccc solid;
        padding: 4px;
        &:hover {
          background: #f0f0f0;
        }
        & ~ .main-li {
          margin-top: 2px;
        }
      }
      .sub-ul {
        // display: none;
        display: flex;
        flex-direction: column;
        position: absolute;
        left: 100%;
        top: 0;
        transform: translateX(6px);
        min-width: 10em;
        background: #f0f0f0;
        padding: 4px;
        .sub-li {
          border-radius: 4px;
          border: 1px #333 solid;
          background: #d9dce2;
          &:hover {
            background: #169bd5;
          }
        }
      }
      .disabled.disabled.disabled {
        cursor: not-allowed;
        color: #aaa;
        background: #ccc;
      }
    }
  }
  .mask {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background: transparent;
    z-index: 99;
  }
}
</style>
src/components/menuList.vue
New file
@@ -0,0 +1,209 @@
<template>
  <div class="menu">
    <ul class="main-ul">
      <li class="main-li" v-for="(item, idx) in menuList" :key="'main_' + idx">
        <div
          :class="['title', { active: item.active }]"
          @click="clickMain(item)"
        >
          {{ item.title }}
        </div>
        <ul class="sub-ul" v-show="item.visible">
          <li
            class="sub-li"
            v-for="(subItem, index) in item.children"
            :key="'sub_' + index"
            @click="clickSub(subItem)"
          >
            {{ subItem.title }}
          </li>
        </ul>
      </li>
    </ul>
    <!-- mask -->
    <div class="mask" v-show="maskVisible" @click="closeMenu"></div>
  </div>
</template>
<script>
export default {
  name: "MenuList",
  data() {
    const menuList = [
      {
        title: "文件",
        visible: false,
        children: [
          {
            title: "打开文件",
            method: "openFile",
          },
          {
            title: "关闭文件",
            method: "closeFile",
          },
          {
            title: "关闭所有文件",
            method: "closeAllFiles",
          },
        ],
      },
      {
        title: "分析",
        visible: false,
        children: [
          {
            title: "单数据分析",
          },
          {
            title: "对比分析",
          },
          {
            title: "分级评价",
          },
          {
            title: "生成报告",
          },
        ],
      },
      {
        title: "配置",
        visible: false,
        children: [
          {
            title: "窗口配置",
          },
          {
            title: "数据管理",
          },
          {
            title: "语言设置",
          },
        ],
      },
      {
        title: "help",
        visible: false,
        children: [
          {
            title: "说明书",
          },
          {
            title: "操作视频",
          },
          {
            title: "升级",
          },
          {
            title: "联系支持",
          },
          {
            title: "其它",
          },
        ],
      },
    ];
    return {
      menuList,
      maskVisible: false,
    };
  },
  components: {},
  methods: {
    clickMain(obj) {
      // console.log(obj)
      this.closeMenu();
      obj.visible = true;
      obj.active = true;
      this.maskVisible = true;
    },
    clickSub(obj) {
      // console.log(obj)
      if (obj.method && "function" == typeof this[obj.method]) {
        this.closeMenu();
        this[obj.method]();
      }
    },
    closeMenu() {
      this.menuList.forEach((v) => {
        v.visible = false;
        v.active = false;
      });
      this.maskVisible = false;
    },
    openFile() {
      // console.log("openfile");
      window.api.send("open-file-dialog");
    },
    initEvents() {
      window.api.receive("selected-file", (path) => {
        console.log(path.filePaths);
        // this.path = path.filePaths;
      });
      window.api.receive("selected-directory", (path) => {
        console.log(path.filePaths);
      });
    },
  },
  mounted() {
    this.initEvents();
  },
};
</script>
<style lang="less" scoped>
.main-ul {
  position: relative;
  z-index: 1000;
  display: flex;
  align-items: stretch;
  .main-li {
    flex: 1;
    position: relative;
    .title {
      cursor: pointer;
      border: 1px solid #333;
      background: #fbfbfb;
      line-height: 30px;
      border-radius: 4px;
      vertical-align: middle;
      &.active {
        background: #169bd5;
      }
      &:hover {
        background: #62cbfa;
      }
    }
  }
  .sub-ul {
    // display: none;
    display: flex;
    flex-direction: column;
    position: absolute;
    left: 0;
    top: 100%;
    min-width: 8em;
    background: #f0f0f0;
    padding: 4px;
    .sub-li {
      border-radius: 4px;
      border: 1px #333 solid;
      background: #d9dce2;
      &:hover {
        background: #169bd5;
      }
    }
  }
}
.mask {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: transparent;
  z-index: 999;
}
</style>
src/main.js
@@ -1,8 +1,16 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
Vue.use(ElementUI, {
  zIndex: 99
});
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
src/preload.js
New file
@@ -0,0 +1,28 @@
import {
  contextBridge,
  ipcRenderer
} from 'electron';
const validChannels = [
  'renderer-ready',
  'main-window-ready',
  'open-file-dialog',
  'open-directory-dialog',
  'selected-directory',
  'selected-file'
];
contextBridge.exposeInMainWorld('api', {
  send: (channel, data) => {
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, data);
    }
  },
  receive: (channel, func) => {
    if (validChannels.includes(channel)) {
      ipcRenderer.on(channel, (event, ...args) => {
        func(...args)
      });
    }
  }
});
src/router/index.js
New file
@@ -0,0 +1,18 @@
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router);
const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({
    y: 0
  }),
  routes
})
const router = createRouter()
export default router;
src/router/routes.js
New file
@@ -0,0 +1,13 @@
export default [{
    path: '/',
    // redirect: '/selectFile'
  },
  // 选文件页面
  // {
  //   path: '/selectFile',
  //   name: 'selectFile',
  //   meta: {},
  //   component: () => import('@/views/selectFile')
  // },
];
vue.config.js
New file
@@ -0,0 +1,48 @@
const path = require('path');
function resolve(dir) {
  return path.join(__dirname, dir);
}
module.exports = {
  publicPath: './',
  devServer: {
    // can be overwritten by process.env.HOST
    host: '0.0.0.0',
    port: 8081
  },
  pluginOptions:{
    electronBuilder:{
      preload:'src/preload.js',
      builderOptions: {
        extraResources: [
          {
            from: "./public/runtime",
            to: "./runtime"
          },
          {
            from: "./public/app_x64.exe",
            to: "./app_x64.exe"
          }
        ],
        files: [
          '**/*',
          '!**/runtime',
          '!**/app_x64.exe'
        ],
      }
    }
  },
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src'))
      .set('src', resolve('src'))
      .set('common', resolve('src/common'))
      .set('components', resolve('src/components'));
    config.plugin('html').tap((args) => {
      args[0].title = '内阻测试分析软件V1.1.1';
      return args;
    })
  }
};