laiguoran 6 gadi atpakaļ
revīzija
81d8b4fd53
72 mainītis faili ar 21486 papildinājumiem un 0 dzēšanām
  1. 39 0
      .babelrc
  2. 3 0
      .eslintignore
  3. 26 0
      .eslintrc.js
  4. 12 0
      .gitignore
  5. 43 0
      .travis.yml
  6. 28 0
      README.md
  7. 32 0
      appveyor.yml
  8. BIN
      build/icons/256x256.png
  9. BIN
      build/icons/icon.icns
  10. BIN
      build/icons/icon.ico
  11. BIN
      build/icons/smartcost.ico
  12. BIN
      data/fileInfo.dll
  13. 1 0
      data/sc_software.json
  14. BIN
      data/zhlData.dll
  15. 2857 0
      package-lock.json
  16. 132 0
      package.json
  17. 55 0
      src/database/index.js
  18. 22 0
      src/index.ejs
  19. 27 0
      src/main/index.dev.js
  20. 104 0
      src/main/index.js
  21. 138 0
      src/main/main-process/downloads.js
  22. 120 0
      src/main/main-process/file-select.js
  23. 212 0
      src/main/main-process/updateInstall.js
  24. 60 0
      src/main/main-process/usb-ffi.js
  25. 64 0
      src/main/software-update.js
  26. 178 0
      src/renderer/App.vue
  27. 0 0
      src/renderer/assets/.gitkeep
  28. BIN
      src/renderer/assets/font-awesome/font/fa-brands-400.eot
  29. 1104 0
      src/renderer/assets/font-awesome/font/fa-brands-400.svg
  30. BIN
      src/renderer/assets/font-awesome/font/fa-brands-400.ttf
  31. BIN
      src/renderer/assets/font-awesome/font/fa-brands-400.woff
  32. BIN
      src/renderer/assets/font-awesome/font/fa-brands-400.woff2
  33. BIN
      src/renderer/assets/font-awesome/font/fa-regular-400.eot
  34. 372 0
      src/renderer/assets/font-awesome/font/fa-regular-400.svg
  35. BIN
      src/renderer/assets/font-awesome/font/fa-regular-400.ttf
  36. BIN
      src/renderer/assets/font-awesome/font/fa-regular-400.woff
  37. BIN
      src/renderer/assets/font-awesome/font/fa-regular-400.woff2
  38. BIN
      src/renderer/assets/font-awesome/font/fa-solid-900.eot
  39. 1896 0
      src/renderer/assets/font-awesome/font/fa-solid-900.svg
  40. BIN
      src/renderer/assets/font-awesome/font/fa-solid-900.ttf
  41. BIN
      src/renderer/assets/font-awesome/font/fa-solid-900.woff
  42. BIN
      src/renderer/assets/font-awesome/font/fa-solid-900.woff2
  43. 3203 0
      src/renderer/assets/font-awesome/fontawesome-all.css
  44. BIN
      src/renderer/assets/img/bg.jpg
  45. BIN
      src/renderer/assets/img/bg1.jpg
  46. BIN
      src/renderer/assets/img/bg2.jpg
  47. BIN
      src/renderer/assets/img/bodyBg.jpg
  48. BIN
      src/renderer/assets/logo.png
  49. 143 0
      src/renderer/components/StartUpPage.vue
  50. 237 0
      src/renderer/components/StartUpPage/DownloadHeader.vue
  51. 64 0
      src/renderer/components/StartUpPage/FirstOpen.vue
  52. 133 0
      src/renderer/components/StartUpPage/SoftwareDetail.vue
  53. 114 0
      src/renderer/components/StartUpPage/SoftwareList.vue
  54. 242 0
      src/renderer/components/StartUpPage/SoftwareStartup.vue
  55. 153 0
      src/renderer/components/StartUpPage/SoftwareStartupDetail.vue
  56. 66 0
      src/renderer/components/StartUpPage/SoftwareUpdate.vue
  57. 32 0
      src/renderer/components/StartUpPage/UsbHeader.vue
  58. 26 0
      src/renderer/components/mixin.js
  59. 27 0
      src/renderer/main.js
  60. 47 0
      src/renderer/router/index.js
  61. 11 0
      src/renderer/store/index.js
  62. 25 0
      src/renderer/store/modules/Counter.js
  63. 14 0
      src/renderer/store/modules/index.js
  64. 0 0
      static/.gitkeep
  65. 11 0
      test/.eslintrc
  66. 18 0
      test/e2e/index.js
  67. 13 0
      test/e2e/specs/Launch.spec.js
  68. 23 0
      test/e2e/utils.js
  69. 13 0
      test/unit/index.js
  70. 62 0
      test/unit/karma.conf.js
  71. 13 0
      test/unit/specs/LandingPage.spec.js
  72. 9271 0
      yarn.lock

+ 39 - 0
.babelrc

@@ -0,0 +1,39 @@
+{
+  "comments": false,
+  "env": {
+    "test": {
+      "presets": [
+        ["env", {
+          "targets": { "node": 7 }
+        }],
+        "stage-0"
+      ],
+      "plugins": ["istanbul"]
+    },
+    "main": {
+      "presets": [
+        ["env", {
+          "targets": { "node": 7 }
+        }],
+        "stage-0"
+      ]
+    },
+    "renderer": {
+      "presets": [
+        ["env", {
+          "modules": false
+        }],
+        "stage-0"
+      ]
+    },
+    "web": {
+      "presets": [
+        ["env", {
+          "modules": false
+        }],
+        "stage-0"
+      ]
+    }
+  },
+  "plugins": ["transform-runtime"]
+}

+ 3 - 0
.eslintignore

@@ -0,0 +1,3 @@
+test/unit/coverage/**
+test/unit/*.js
+test/e2e/*.js

+ 26 - 0
.eslintrc.js

@@ -0,0 +1,26 @@
+module.exports = {
+  root: true,
+  parser: 'babel-eslint',
+  parserOptions: {
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true
+  },
+  extends: 'standard',
+  globals: {
+    __static: true
+  },
+  plugins: [
+    'html'
+  ],
+  'rules': {
+    // allow paren-less arrow functions
+    'arrow-parens': 0,
+    // allow async-await
+    'generator-star-spacing': 0,
+    // allow debugger during development
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
+  }
+}

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+.DS_Store
+.electron-vue/
+.idea/
+dist/
+build/*
+!build/icons
+coverage
+node_modules/
+npm-debug.log
+npm-debug.log.*
+thumbs.db
+!.gitkeep

+ 43 - 0
.travis.yml

@@ -0,0 +1,43 @@
+# Commented sections below can be used to run tests on the CI server
+# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
+osx_image: xcode8.3
+sudo: required
+dist: trusty
+language: c
+matrix:
+  include:
+  - os: osx
+  - os: linux
+    env: CC=clang CXX=clang++ npm_config_clang=1
+    compiler: clang
+cache:
+  directories:
+  - node_modules
+  - "$HOME/.electron"
+  - "$HOME/.cache"
+addons:
+  apt:
+    packages:
+    - libgnome-keyring-dev
+    - icnsutils
+    #- xvfb
+before_install:
+- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
+  "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
+  | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
+- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
+install:
+#- export DISPLAY=':99.0'
+#- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
+- nvm install 7
+- curl -o- -L https://yarnpkg.com/install.sh | bash
+- source ~/.bashrc
+- npm install -g xvfb-maybe
+- yarn
+script:
+#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
+#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
+- yarn run build
+branches:
+  only:
+  - master

+ 28 - 0
README.md

@@ -0,0 +1,28 @@
+# startup
+
+> 启动器
+
+#### Build Setup
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:9080
+npm run dev
+
+# build electron application for production
+npm run build
+
+# run unit & end-to-end tests
+npm test
+
+
+# lint all JS/Vue component files in `src/`
+npm run lint
+
+```
+
+---
+
+This project was generated with [electron-vue](https://github.com/SimulatedGREG/electron-vue) using [vue-cli](https://github.com/vuejs/vue-cli). Documentation about the original structure can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/index.html).

+ 32 - 0
appveyor.yml

@@ -0,0 +1,32 @@
+# Commented sections below can be used to run tests on the CI server
+# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
+version: 0.1.{build}
+
+branches:
+  only:
+    - master
+
+image: Visual Studio 2017
+platform:
+  - x64
+
+cache:
+  - node_modules
+  - '%APPDATA%\npm-cache'
+  - '%USERPROFILE%\.electron'
+  - '%USERPROFILE%\AppData\Local\Yarn\cache'
+
+init:
+  - git config --global core.autocrlf input
+
+install:
+  - ps: Install-Product node 8 x64
+  - git reset --hard HEAD
+  - yarn
+  - node --version
+
+build_script:
+  #- yarn test
+  - yarn build
+
+test: off

BIN
build/icons/256x256.png


BIN
build/icons/icon.icns


BIN
build/icons/icon.ico


BIN
build/icons/smartcost.ico


BIN
data/fileInfo.dll


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
data/sc_software.json


BIN
data/zhlData.dll


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2857 - 0
package-lock.json


+ 132 - 0
package.json

@@ -0,0 +1,132 @@
+{
+  "name": "startup",
+  "version": "0.1.1",
+  "author": "珠海纵横软件有限公司",
+  "description": "启动器",
+  "license": null,
+  "main": "./dist/electron/main.js",
+  "scripts": {
+    "build": "node .electron-vue/build.js && electron-builder",
+    "build:dir": "node .electron-vue/build.js && electron-builder --dir",
+    "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
+    "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
+    "dev": "node .electron-vue/dev-runner.js",
+    "e2e": "npm run pack && mocha test/e2e",
+    "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
+    "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
+    "pack": "npm run pack:main && npm run pack:renderer",
+    "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
+    "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
+    "test": "npm run unit && npm run e2e",
+    "unit": "karma start test/unit/karma.conf.js",
+    "postinstall": "npm run lint:fix",
+    "rebuild-ffi": "cd ./node_modules/ffi/ && node-gyp rebuild --target=2.0.2 --arch=ia32 --target_arch=ia32 --dist-url=https://atom.io/download/electron && cd ../ref && node-gyp rebuild --target=2.0.2 --arch=ia32 --target_arch=ia32 --dist-url=https://atom.io/download/electron "
+  },
+  "build": {
+    "productName": "纵横启动器",
+    "appId": "org.smartcost.startup",
+    "directories": {
+      "output": "build"
+    },
+    "files": [
+      "dist/electron/**/*"
+    ],
+    "publish": [
+      {
+        "provider": "generic",
+        "url": "http://d2.smartcost.com.cn/startup/"
+      }
+    ],
+    "win": {
+      "icon": "build/icons/smartcost.ico",
+      "target": [
+        "nsis"
+      ],
+      "artifactName": "${productName}_setup_${version}.${ext}"
+    },
+    "extraResources": [
+      {
+        "from": "data",
+        "to": "../data"
+      }
+    ]
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "electron-dl": "^1.12.0",
+    "electron-updater": "^2.21.10",
+    "element-ui": "^2.4.1",
+    "ffi": "^2.2.0",
+    "fs-extra": "^6.0.1",
+    "glob": "^7.1.2",
+    "lodash-id": "^0.14.0",
+    "lowdb": "^1.0.0",
+    "regedit": "^3.0.0",
+    "usb": "^1.3.2",
+    "vue": "^2.3.3",
+    "vue-electron": "^1.0.6",
+    "vue-router": "^2.5.3",
+    "vuex": "^2.3.1"
+  },
+  "devDependencies": {
+    "babel-core": "^6.25.0",
+    "babel-eslint": "^7.2.3",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-istanbul": "^4.1.1",
+    "babel-plugin-transform-runtime": "^6.23.0",
+    "babel-preset-env": "^1.6.0",
+    "babel-preset-stage-0": "^6.24.1",
+    "babel-register": "^6.24.1",
+    "babili-webpack-plugin": "^0.1.2",
+    "cfonts": "^1.1.3",
+    "chai": "^4.0.0",
+    "chalk": "^2.1.0",
+    "copy-webpack-plugin": "^4.0.1",
+    "cross-env": "^5.0.5",
+    "css-loader": "^0.28.4",
+    "del": "^3.0.0",
+    "devtron": "^1.4.0",
+    "electron": "^2.0.2",
+    "electron-builder": "^20.15.1",
+    "electron-debug": "^1.4.0",
+    "electron-devtools-installer": "^2.2.0",
+    "electron-rebuild": "^1.7.3",
+    "eslint": "^4.4.1",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^1.9.0",
+    "eslint-plugin-html": "^3.1.1",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-node": "^5.1.1",
+    "eslint-plugin-promise": "^3.5.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "extract-text-webpack-plugin": "^3.0.0",
+    "file-loader": "^0.11.2",
+    "html-webpack-plugin": "^2.30.1",
+    "inject-loader": "^3.0.0",
+    "karma": "^1.3.0",
+    "karma-chai": "^0.1.0",
+    "karma-coverage": "^1.1.1",
+    "karma-electron": "^5.1.1",
+    "karma-mocha": "^1.2.0",
+    "karma-sourcemap-loader": "^0.3.7",
+    "karma-spec-reporter": "^0.0.31",
+    "karma-webpack": "^2.0.1",
+    "mocha": "^3.0.2",
+    "multispinner": "^0.2.1",
+    "node-loader": "^0.6.0",
+    "node-sass": "^4.9.0",
+    "require-dir": "^0.3.0",
+    "spectron": "^3.7.1",
+    "style-loader": "^0.18.2",
+    "url-loader": "^0.5.9",
+    "vue-html-loader": "^1.2.4",
+    "vue-loader": "^13.0.5",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.4.2",
+    "webpack": "^3.5.2",
+    "webpack-dev-server": "^2.7.1",
+    "webpack-hot-middleware": "^2.18.2",
+    "webpack-merge": "^4.1.0"
+  }
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 55 - 0
src/database/index.js


+ 22 - 0
src/index.ejs

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>startup</title>
+    <% if (htmlWebpackPlugin.options.nodeModules) { %>
+      <!-- Add `node_modules/` to global paths so `require` works properly in development -->
+      <script>
+        require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
+      </script>
+    <% } %>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- Set `__static` path to static files in production -->
+    <script>
+      if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
+    </script>
+
+    <!-- webpack builds are automatically injected -->
+  </body>
+</html>

+ 27 - 0
src/main/index.dev.js

@@ -0,0 +1,27 @@
+/**
+ * This file is used specifically and only for development. It installs
+ * `electron-debug` & `vue-devtools`. There shouldn't be any need to
+ *  modify this file, but it can be used to extend your development
+ *  environment.
+ */
+
+/* eslint-disable */
+
+// Set environment for development
+process.env.NODE_ENV = 'development'
+
+// Install `electron-debug` with `devtron`
+require('electron-debug')({ showDevTools: true })
+
+// Install `vue-devtools`
+require('electron').app.on('ready', () => {
+  let installExtension = require('electron-devtools-installer')
+  installExtension.default(installExtension.VUEJS_DEVTOOLS)
+    .then(() => {})
+    .catch(err => {
+      console.log('Unable to install `vue-devtools`: \n', err)
+    })
+})
+
+// Require `main` process to boot app
+require('./index')

+ 104 - 0
src/main/index.js

@@ -0,0 +1,104 @@
+'use strict'
+
+import pkg from '../../package.json'
+import usbffi from './main-process/usb-ffi'
+import downloads from './main-process/downloads'
+import updateInstall from './main-process/updateInstall'
+import fileselect from './main-process/file-select'
+import db from '../database/index'
+const path = require('path')
+// const glob = require('glob')
+const electron = require('electron')
+const app = electron.app
+const BrowserWindow = electron.BrowserWindow
+const Menu = electron.Menu
+const autoUpdater = require('./software-update')
+
+/**
+ * Set `__static` path to static files in production
+ * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
+ */
+if (process.env.NODE_ENV !== 'development') {
+  global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
+}
+
+let mainWindow
+let winURL = process.env.NODE_ENV === 'development'
+  ? `http://localhost:9080`
+  : `file://${__dirname}/index.html`
+
+let firsturl = db.read().get('sc_hadInstall.first').value()
+winURL += firsturl ? '/#/firstopen' : '/#' + db.read().get('sc_hadInstall.url').value()
+
+function initialize () {
+  function createWindow () {
+    Menu.setApplicationMenu(null)
+    /**
+     * Initial window options
+     */
+    const windowOptions = {
+      // width: 900,
+      width: 1450,
+      height: 564,
+      resizable: false,
+      show: true,
+      frame: false,
+      fullscreenable: false,
+      center: true,
+      transparent: true,
+      titleBarStyle: 'hidden',
+      backgroundColor: '#fff',
+      webPreferences: {
+        backgroundThrottling: false
+      }
+    }
+
+    mainWindow = new BrowserWindow(windowOptions)
+    mainWindow.loadURL(winURL)
+
+    mainWindow.on('closed', () => {
+      mainWindow = null
+    })
+  }
+
+  app.on('ready', function () {
+    createWindow()
+    loadJS(mainWindow)
+    autoUpdater.initialize(mainWindow)
+  })
+
+  app.on('window-all-closed', () => {
+    if (process.platform !== 'darwin') {
+      app.quit()
+    }
+  })
+
+  app.on('activate', () => {
+    if (mainWindow === null) {
+      createWindow()
+    }
+  })
+
+  function loadJS (win) {
+    // 无法使用require调用含异步的方法,只能采用import方式,原因不明,坑!
+    // require('./main-process/usb-ffi').initialize(win)
+    usbffi(win)
+    downloads(win)
+    updateInstall(win)
+    fileselect(win)
+    // require('./main-process/downloads').initialize(win)
+    // let files = glob.sync(path.join(__dirname, './main-process/*.js'))
+    // if (files !== []) {
+    //   files.forEach(function (file) {
+    //     require(file)
+    //   })
+    // }
+    // require('./main-process/download')
+  }
+}
+
+initialize()
+
+if (process.platform === 'win32') {
+  app.setAppUserModelId(pkg.build.appId)
+}

+ 138 - 0
src/main/main-process/downloads.js

@@ -0,0 +1,138 @@
+'use strict'
+
+/**
+ *
+ *
+ * @author EllisRan.
+ * @date 2018/6/22
+ * @version
+ */
+import db from '../../database/index'
+const path = require('path')
+const electron = require('electron')
+const ipcMain = electron.ipcMain
+const folderpath = 'data/software'
+const download = require('electron-dl').download
+// const BrowserWindow = electron.BrowserWindow
+const app = electron.app
+const fs = require('fs-extra')
+const __appPath = app.getPath('userData')
+
+const downloads = function (win) {
+  let item
+  ipcMain.on('cancel', (eve, id) => {
+    // cancelStatus = true
+    item.cancel()
+  })
+  ipcMain.on('pause', (eve, id) => {
+    // pauseStatus = true
+    item.pause()
+  })
+  ipcMain.on('resume', (eve, id) => {
+    // resumeStatus = true
+    // item = db.get('sc_download.item').value()
+    // console.log(db.read().get('sc_download.item').value())
+    if (!item) {
+      // item = db.read().get('sc_download.item').value()
+      console.log(item.canResume())
+      console.log(item)
+    }
+    item.resume()
+  })
+
+  ipcMain.on('download', (event, args) => {
+    let info = db.read().get('sc_download').getById(args.id).value()
+    let downloadpath = info.downloadpath
+    let options = {
+      saveAs: false,
+      directory: path.join(__appPath, folderpath),
+      filename: info.savepath,
+      errorTitle: '下载失败',
+      onStarted: function (items) {
+        item = items // 必须抽离出来控制item,否则二次使用时会报错
+      },
+      onProgress: function (status) {
+        win.webContents.send('downloadtips', {percent: Math.ceil(status * 100), id: args.id, index: args.index})
+      },
+      onCancel: function () {
+        console.log('cancel download')
+        try {
+          info.start = true
+          info.pause = false
+          info.resume = false
+          db.read().get('sc_download').getById(args.id).assign(info).write()
+          fs.removeSync(path.join(__appPath, folderpath, info.savepath))
+        } catch (err) {
+          console.log(err)
+        }
+      }
+    }
+    download(win, downloadpath, options)
+      .then(
+        dl => win.webContents.send('downloadsuccess', {id: args.id, savepath: path.join(__appPath, folderpath, info.savepath), index: args.index})
+      )
+      .catch(console.error)
+  })
+}
+
+export default downloads
+
+// exports.initialize = function (win) {
+//   ipcMain.on('download', (event, args) => {
+//     let downloadpath = args
+//     win.webContents.downloadURL(downloadpath)
+//     let $event = event
+//     win.webContents.session.on('will-download', (event, item, webContents) => {
+//       const totalBytes = item.getTotalBytes()
+//       const filename = item.getFilename()
+//       item.setSavePath(path.join(folderpath, `${item.getFilename()}`))
+//       item.on('updated', (event, state) => {
+//         if (state === 'interrupted') {
+//           $event.sender.send('downloadtips', 'Download is interrupted but can be resumed')
+//         } else if (state === 'progressing') {
+//           if (item.isPaused()) {
+//             $event.sender.send('downloadtips', filename + ' 下载暂停')
+//           } else {
+//             console.log(item)
+//             $event.sender.send('downloadtips', Math.ceil(item.getReceivedBytes() / totalBytes * 100))
+//           }
+//         }
+//       })
+//       item.once('done', (event, state) => {
+//         if (state === 'completed') {
+//           $event.sender.send('downloadtips', filename + ' 下载完成 共:' + conver(totalBytes))
+//           $event.sender.send('notification-test', filename + ' 下载完成')
+//         } else {
+//           $event.sender.send('downloadtips', filename + ` 下载失败: ${state}`)
+//           $event.sender.send('notification-test', filename + ` 下载失败: ${state}`)
+//         }
+//       })
+//     })
+//   })
+// }
+//
+// /**
+//  *
+//  * @param 计算内存大小
+//  * @return {string}
+//  */
+// function conver (limit) {
+//   var size = ''
+//   if (limit < 0.1 * 1024) { // 如果小于0.1KB转化成B
+//     size = limit.toFixed(2) + 'B'
+//   } else if (limit < 0.1 * 1024 * 1024) { // 如果小于0.1MB转化成KB
+//     size = (limit / 1024).toFixed(2) + 'KB'
+//   } else if (limit < 0.1 * 1024 * 1024 * 1024) { // 如果小于0.1GB转化成MB
+//     size = (limit / (1024 * 1024)).toFixed(2) + 'MB'
+//   } else { // 其他转化成GB
+//     size = (limit / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
+//   }
+//
+//   var sizestr = size + ''
+//   var len = sizestr.indexOf('.')
+//   var dec = sizestr.substr(len + 1, 2)
+//   if (dec === '00') { // 当小数点后为00时 去掉小数部分
+//     return sizestr.substring(0, len) + sizestr.substr(len + 3, 2)
+//   }
+//   return sizestr
+// }

+ 120 - 0
src/main/main-process/file-select.js

@@ -0,0 +1,120 @@
+'use strict'
+
+/**
+ * 手动添加纵横软件文件夹
+ *
+ * @author EllisRan.
+ * @date 2018/6/20
+ * @version
+ */
+import db from '../../database/index'
+const path = require('path')
+const ffi = require('ffi')
+const fs = require('fs')
+const electron = require('electron')
+const ipcMain = electron.ipcMain
+const dialog = electron.dialog
+
+const fileselect = function (win) {
+  ipcMain.on('file-select', function (event) {
+    dialog.showOpenDialog({
+      properties: ['openFile', 'openDirectory']
+    }, async function (files) {
+      if (files) {
+        let data = await checkDirectory(files[0])
+        if (data.length === 0) {
+          event.sender.send('failedDirectory', { msg: '该文件夹不存在纵横软件' })
+        } else {
+          let addnum = await checkExeName(files[0], data)
+          if (addnum !== 0) {
+            event.sender.send('successUpdate', { id: '1', num: addnum, delnum: 0 })
+          } else {
+            event.sender.send('failedDirectory', { msg: '该文件夹中的纵横软件已存在启动器中' })
+          }
+        }
+      } else {
+        event.sender.send('failedDirectory', { msg: '' })
+      }
+    })
+  })
+}
+
+/**
+ * 检查文件夹并判断是否包含纵横软件(一个文件可能存在2个纵横软件的)
+ * @param files
+ * @return {Promise.<Array>}
+ */
+async function checkDirectory (files) {
+  let data = []
+  let dirname = await fs.readdirSync(files)
+  dirname.forEach(async function (item, index) {
+    if (path.extname(item) === '.exe' && existSoftwareName(item)) {
+      let pathstr = path.join('data/fileInfo.dll')
+      let libm = ffi.Library(pathstr, {
+        'GetFileInfo': ['string', ['string']]
+      })
+      let fileinfo = JSON.parse(await libm.GetFileInfo(path.join(files, item)))
+      fileinfo.exeName = item
+      data.push(fileinfo)
+    }
+  })
+  return data
+}
+
+/**
+ * 判断数据库是否已存在该文件信息,不存在则添加到数据库中
+ * @param files
+ * @return {Promise.<Array>}
+ */
+async function checkExeName (Directory, files) {
+  let index = Directory.lastIndexOf('\\')
+  let filename = Directory.substring(index + 1, Directory.length)
+  let addnum = 0
+  for (let i = 0; i < files.length; i++) {
+    let exeinfo = await db.read().get('sc_exeData').find({ path: Directory, exeName: files[i].exeName, fileVersion: files[i].FileVersion }).value()
+    if (!exeinfo) {
+      let exeData = {
+        path: Directory,
+        name: filename,
+        regeditName: '',
+        fileName: filename,
+        simpleName: '',
+        versionName: '',
+        fileVersion: files[i].FileVersion,
+        productName: files[i].ProductName,
+        productVersion: files[i].ProductVersion,
+        exeName: files[i].exeName,
+        pid: '1',
+        keyNumber: '',
+        addtime: Date.parse(new Date()) / 1000,
+        isshow: true,
+        auto: false
+      }
+      await db.read().get('sc_exeData').insert(exeData).write()
+      await db.read().get('sc_productData').updateById(1, { isshow: true }).write()
+      await db.read().set('sc_hadInstall.url', '/softwarestartup/1').write()
+      ++addnum
+    }
+  }
+  return addnum
+}
+
+/**
+ * 判断文件名是否包含纵横软件exe的部分名称
+ * @param item
+ * @return {boolean}
+ */
+function existSoftwareName (item) {
+  // 纵横软件exe所带包含的文件名
+  const smartcostSoftwareHeader = ['SmartCost', 'Measure', 'DrawingBuilder', 'BillsEditor']
+  let flag = false
+  for (let i = 0; i < smartcostSoftwareHeader.length; i++) {
+    if (item.indexOf(smartcostSoftwareHeader[i]) !== -1) {
+      flag = true
+      break
+    }
+  }
+  return flag
+}
+
+export default fileselect

+ 212 - 0
src/main/main-process/updateInstall.js

@@ -0,0 +1,212 @@
+'use strict'
+
+/**
+ * 读取注册表获取纵横软件
+ *
+ * @author EllisRan.
+ * @date 2018/6/20
+ * @version
+ */
+import db from '../../database/index'
+const fs = require('fs')
+const fse = require('fs-extra')
+const path = require('path')
+const ffi = require('ffi')
+const electron = require('electron')
+const ipcMain = electron.ipcMain
+const regeditPath64 = 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
+const regeditPath32 = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
+const regeditPath = isOSWin64() ? regeditPath64 : regeditPath32
+const regedit = require('regedit')
+let globalNUM = 0
+
+const updateInstall = function (win) {
+  ipcMain.on('updateInstall', function () {
+    regedit.list(regeditPath).on('data', async function (result) {
+      try {
+        // let flag = false
+        // 每次自动获取更新软件内容先让数据库isshow都为false,然后再执行getExeDataList方法里把isshow包含的变true,
+        // 最后再检测把还是false的删除,完成已删除软件功能(手动添加的exe信息除外)
+        await productAndExeDataIsshow()
+        let softwarelist = result.data.keys
+        let promiseArr = []
+        for (let i in softwarelist) {
+          if (softwarelist[i].indexOf('SmartCost_') !== -1) {
+            promiseArr.push(getExeDataList(softwarelist[i]))
+          }
+        }
+        await Promise.all(promiseArr)
+          .then(async function (result) {
+            let delnum = await delProductAndExeDataAsync()
+            await db.read().set('sc_hadInstall.first', false).write()
+            let exeInfo = await db.read().get('sc_exeData').last().value()
+            let gourl = exeInfo.pid === undefined ? 1 : exeInfo.pid
+            await db.read().set('sc_hadInstall.url', '/softwarestartup/' + gourl).write()
+            if (promiseArr !== undefined && promiseArr.length !== 0) {
+              win.webContents.send('successUpdate', { id: gourl, num: globalNUM, delnum: delnum })
+              globalNUM = 0
+            } else {
+              win.webContents.send('failedUpdate', { id: '1' })
+            }
+          })
+          .catch(function () {
+            win.webContents.send('failedUpdate', { id: '1' })
+          })
+      } catch (err) {
+        console.log(err)
+      }
+    })
+  })
+
+  /**
+   *  make all data isshow: true=>false
+   */
+  async function productAndExeDataIsshow () {
+    await db.read().get('sc_productData').updateWhere({ isshow: true }, { isshow: false }).write()
+    await db.read().get('sc_exeData').updateWhere({ auto: true, isshow: true }, { isshow: false }).write()
+  }
+
+  /**
+   *  delete data by isshow=false
+   *   @return {int}
+   */
+  async function delProductAndExeDataAsync () {
+    // 这里必须静止1s等Promise.all里的getExeDataList方法所有获取执行完才可以获取正确的num,原因不明......
+    await sleep(1000)
+    let num = await db.read().get('sc_exeData').filter({ auto: true, isshow: false }).size().value()
+    // 先让默认的“未分类” isshow:true,免于被删除,当不存在手动添加的软件或自动检测软件的时候,暂时隐藏“未分类”,
+    // 当启动器不存在任何软件时,默认显示“未分类”
+    let manualnum = db.read().get('sc_exeData').filter({ auto: false }).size().value()
+    let flag = db.read().get('sc_productData').getById('1').value().isshow
+    if (!flag) {
+      db.read().get('sc_productData').updateById('1', { isshow: true }).write()
+    }
+    db.read().get('sc_productData').removeWhere({ isshow: false }).write()
+    let allnum = db.read().get('sc_exeData').size().value()
+    if (manualnum === 0 && !flag && allnum !== 0) {
+      db.read().get('sc_productData').updateById('1', { isshow: false }).write()
+    }
+    db.read().get('sc_exeData').removeWhere({ auto: true, isshow: false }).write()
+    return num
+  }
+
+  const sleep = (timeout = 2000) => new Promise(resolve => {
+    setTimeout(resolve, timeout)
+  })
+
+  /**
+   * 判断文件名是否包含纵横软件exe的部分名称
+   * @param item
+   * @return {boolean}
+   */
+  function existSoftwareName (item) {
+    // 纵横软件exe所带包含的文件名
+    const smartcostSoftwareHeader = ['SmartCost', 'Measure', 'DrawingBuilder', 'BillsEditor']
+    let flag = false
+    for (let i = 0; i < smartcostSoftwareHeader.length; i++) {
+      if (item.indexOf(smartcostSoftwareHeader[i]) !== -1) {
+        flag = true
+        break
+      }
+    }
+    return flag
+  }
+
+  /**
+   * node 循环和异步解决方案
+   * @param Info
+   */
+  function getExeDataList (Info) {
+    return new Promise(resolve => {
+      // setTimeout(function () {
+      regedit.list(regeditPath + '\\' + Info).on('data', async function (result) {
+        let values = result.data.values
+        let simpleVersion = values['DisplayVersion'] === undefined || values['DisplayVersion'] === null ? '' : values['DisplayVersion'].value.split('_')
+        let exeData = {
+          path: values['Inno Setup: App Path'].value,
+          name: values['DisplayName'].value,
+          regeditName: Info,
+          fileName: values['Inno Setup: Icon Group'].value,
+          simpleName: simpleVersion === '' ? '' : simpleVersion[0],
+          versionName: simpleVersion === '' ? '' : simpleVersion[1]
+        }
+        let dirname = await fs.readdirSync(exeData.path)
+        dirname.forEach(async function (item, index) {
+          if (path.extname(item) === '.exe' && existSoftwareName(item)) {
+            let pathstr = path.join('data/fileInfo.dll')
+            let libm = ffi.Library(pathstr, {
+              'GetFileInfo': ['string', ['string']]
+            })
+            let fileinfo = JSON.parse(await libm.GetFileInfo(path.join(exeData.path, item)))
+            exeData.fileVersion = fileinfo.FileVersion
+            exeData.productName = fileinfo.ProductName
+            exeData.productVersion = fileinfo.ProductVersion
+            exeData.exeName = item
+            let dbExeData = await db.read().get('sc_exeData').find({ exeName: exeData.exeName, path: exeData.path, fileVersion: exeData.fileVersion }).value()
+            if (dbExeData === undefined || dbExeData === null) {
+              // 先判断是否存在该产品数据库(同时关联到sc_sofeware.json文件数据),再加入安装包数据库
+              if (exeData.simpleName !== '') {
+                let softwarejson = path.join('data/sc_software.json')
+                let productInfo = await db.read().get('sc_productData').find({ title: exeData.simpleName }).value()
+                if (productInfo === undefined || productInfo === null) {
+                  let productlist = await fse.readJsonSync(softwarejson).sc_product
+                  for (let i in productlist) {
+                    if (productlist[i].title === exeData.simpleName) {
+                      let productInfo2 = {
+                        readid: productlist[i].product_id,
+                        title: productlist[i].title,
+                        productName: productlist[i].productName,
+                        src: productlist[i].src,
+                        addtime: Date.parse(new Date()) / 1000,
+                        isshow: true
+                      }
+                      let insertproudct = await db.read().get('sc_productData').insert(productInfo2).write()
+                      exeData.pid = insertproudct.id
+                      productInfo = insertproudct
+                      break
+                    }
+                  }
+                } else {
+                  exeData.pid = productInfo.id
+                  await db.read().get('sc_productData').updateById(productInfo.id, { isshow: true }).write()
+                }
+                // 获取json文件中的锁号,更新到exe当中
+                let downlist = await fse.readJsonSync(softwarejson).sc_down
+                for (let j in downlist) {
+                  if (productInfo.readid === downlist[j].product_id && exeData.versionName === downlist[j].title) {
+                    exeData.keyNumber = downlist[j].key_number
+                    break
+                  }
+                }
+              } else {
+                exeData.pid = '1'
+                exeData.keyNumber = ''
+                await db.read().get('sc_productData').updateById('1', { isshow: true }).write()
+              }
+              exeData.addtime = Date.parse(new Date()) / 1000
+              exeData.isshow = true
+              exeData.auto = true
+              ++globalNUM
+              await db.read().get('sc_exeData').insert(exeData).write()
+            } else {
+              await db.read().get('sc_productData').updateById(dbExeData.pid, { isshow: true }).write()
+              await db.read().get('sc_exeData').updateById(dbExeData.id, { isshow: true }).write()
+            }
+          }
+        })
+        resolve()
+      })
+      // }, 1000)
+    })
+  }
+}
+
+/**
+ * 判断系统位数
+ * @return {boolean}
+ */
+function isOSWin64 () {
+  return process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
+}
+
+export default updateInstall

+ 60 - 0
src/main/main-process/usb-ffi.js

@@ -0,0 +1,60 @@
+'use strict'
+
+/**
+ * 软件锁检测
+ *
+ * @author EllisRan.
+ * @date 2018/6/20
+ * @version
+ */
+const path = require('path')
+const ffi = require('ffi')
+const electron = require('electron')
+const Notification = electron.Notification
+const ipcMain = electron.ipcMain
+const usb = require('usb')
+
+const usbffi = function (win) {
+  ipcMain.on('testUsb', function () {
+    testUsb(win.webContents)
+  })
+  usb.on('attach', function (device) {
+    testUsb(win.webContents)
+  })
+  usb.on('detach', function (device) {
+    win.webContents.send('usbOut')
+    const notification = new Notification({
+      title: '软件锁',
+      body: '你的软件锁已拔出'
+    })
+    notification.show()
+  })
+}
+
+async function testUsb (webContents) {
+  try {
+    let pathstr = path.join('data/zhlData.dll')
+    let libm = ffi.Library(pathstr, {
+      'GetProductIDList': ['string', []]
+    })
+    console.log(await libm.GetProductIDList())
+    let alllist = await libm.GetProductIDList()
+    if (alllist !== '') {
+      // let productlist = alllist.split('|')
+      // for (let i in productlist) {
+      //   let productNum = productlist[i].split(':')[0]
+      //   let productVerList = productlist[i].split(':')[1].split(';')
+      // }
+    }
+    webContents.send('usbIn')
+    const notification = new Notification({
+      title: '软件锁',
+      body: '启动器检测到软件锁'
+    })
+    notification.show()
+  } catch (err) {
+    console.error(err)
+  }
+}
+
+export default usbffi

+ 64 - 0
src/main/software-update.js

@@ -0,0 +1,64 @@
+'use strict'
+
+/**
+ *
+ *
+ * @author EllisRan.
+ * @date 2018/6/4
+ * @version
+ */
+const electron = require('electron')
+const ipcMain = electron.ipcMain
+const autoUpdater = require('electron-updater').autoUpdater
+const uploadUrl = 'http://d2.smartcost.com.cn/startup/'
+
+exports.initialize = function (win) {
+  // let message = {
+  //   error: '检查更新出错 1',
+  //   checking: '正在检查更新…… 2',
+  //   updateAva: '检测到新版本,正在下载…… 3',
+  //   updateNotAva: '现在使用的就是最新版本,不用更新 4'
+  // }
+  autoUpdater.setFeedURL(uploadUrl)
+  autoUpdater.autoDownload = false
+  autoUpdater.on('error', function () {
+    // sendUpdateMessage(win, message.error)
+  })
+  autoUpdater.on('checking-for-update', function () {
+    // sendUpdateMessage(win, message.checking)
+  })
+  autoUpdater.on('update-available', function (info) {
+    sendUpdateMessage(win, 2)
+  })
+  autoUpdater.on('update-not-available', function (info) {
+    // sendUpdateMessage(win, message.updateNotAva)
+  })
+
+  // 更新下载进度事件
+  autoUpdater.on('download-progress', function (progressObj) {
+    win.webContents.send('downloadProgress', progressObj)
+    win.setProgressBar(progressObj.percent / 100)
+    console.log(progressObj)
+  })
+  autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
+    ipcMain.on('isUpdateNow', (e, arg) => {
+      // some code here to handle event
+      autoUpdater.quitAndInstall()
+    })
+
+    win.webContents.send('isUpdateNow')
+  })
+
+  ipcMain.on('checkForUpdate', () => {
+    // 执行自动更新检查
+    autoUpdater.checkForUpdates()
+  })
+}
+ipcMain.on('downloadUpdate', () => {
+  autoUpdater.downloadUpdate()
+})
+
+// 通过main进程发送事件给renderer进程,提示更新信息
+function sendUpdateMessage (win, status = 0) {
+  win.webContents.send('msgBox', status)
+}

+ 178 - 0
src/renderer/App.vue

@@ -0,0 +1,178 @@
+<template>
+  <div id="app">
+    <router-view></router-view>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'startup'
+  }
+</script>
+
+<style>
+  /* CSS */
+  @import "assets/font-awesome/fontawesome-all.css";
+
+  #app {
+    overflow: hidden;
+  }
+  body {
+    margin: 0;
+    font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
+    font-size: 1rem;
+    font-weight: 400;
+    line-height: 1.5;
+    color: #212529;
+    text-align: left;
+    background-color: #fff;
+  }
+  .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
+    margin-bottom: .5rem;
+    font-family: inherit;
+    font-weight: 500;
+    line-height: 1.2;
+    color: inherit;
+  }
+  .h4, h4 {
+    margin-top: 0;
+    font-size: 1.5rem;
+  }
+
+  .pl-0, .px-0 {
+    padding-left: 0!important;
+  }
+  .pr-0, .px-0 {
+    padding-right: 0!important;
+  }
+  .shadow-sm {
+    box-shadow: 0 .125rem .25rem rgba(0,0,0,.075)!important;
+  }
+  .rounded {
+    border-radius: .25rem!important;
+  }
+  .border {
+    border: 1px solid #dee2e6!important;
+  }
+  .pt-1, .py-1 {
+    padding-top: .25rem!important;
+  }
+  .pt-4, .py-4 {
+    padding-top: 1.5rem!important;
+  }
+  .pb-2, .py-2 {
+    padding-bottom: .5rem!important;
+  }
+  .pl-2, .py-2 {
+    padding-left: .5rem!important;
+  }
+  .pt-2, .py-2 {
+    padding-top: .5rem!important;
+  }
+  .mt-1, .my-1 {
+    margin-top: .25rem!important;
+  }
+  .mt-2, .my-2 {
+    margin-top: .5rem!important;
+  }
+  .ml-2, .mx-2 {
+    margin-left: .5rem!important;
+  }
+  .ml-3, .mx-3 {
+    margin-left: 1rem!important;
+  }
+  .mt-4, .my-4 {
+    margin-top: 1.5rem!important;
+  }
+  .pl-3, .px-3 {
+    padding-left: 1rem!important;
+  }
+  .pt-3, .py-3 {
+    padding-top: 1rem!important;
+  }
+  .p-2 {
+    padding: .5rem!important;
+  }
+  .p-3 {
+    padding: 1rem!important;
+  }
+  .shadow-sm {
+    box-shadow: 0 .125rem .25rem rgba(0,0,0,.075)!important;
+  }
+  .border-bottom {
+    border-bottom: 1px solid #dee2e6!important;
+  }
+  .media {
+    display: -ms-flexbox;
+    display: flex;
+    -ms-flex-align: start;
+    align-items: flex-start;
+  }
+  .mr-2, .mx-2 {
+    margin-right: .5rem!important;
+  }
+  .mt-1, .my-1 {
+    margin-top: .25rem!important;
+  }
+  .h5, h5 {
+    margin-top: 0;
+    font-size: 1.25rem;
+  }
+
+  .fa, .fas {
+    font-family: 'Font Awesome 5 Free';
+    font-weight: 900;
+  }
+  .fa, .fas, .far, .fal, .fab {
+    -moz-osx-font-smoothing: grayscale;
+    -webkit-font-smoothing: antialiased;
+    display: inline-block;
+    font-style: normal;
+    font-variant: normal;
+    text-rendering: auto;
+    line-height: 1;
+    cursor: pointer;
+  }
+  .fa-th-large:before {
+    content: "\f009";
+  }
+
+  .float-right {
+    float: right!important;
+  }
+  .mr-3, .mx-3 {
+    margin-right: 1rem!important;
+  }
+  .mt-1, .my-1 {
+    margin-top: .25rem!important;
+  }
+  .float-left {
+    float: left!important;
+  }
+  .el-dropdown {
+    color: #212529;
+    font-size: 1rem;
+  }
+  ::-webkit-scrollbar {
+    width: 5px;
+    height: 5px;
+  }
+  ::-webkit-scrollbar-thumb {
+    background-color: #e0e0e0;
+  }
+  ::-webkit-scrollbar-track {
+    background-color: #fff;
+  }
+
+  .btn-blue {
+    color: #fff !important;
+    background-color: #2daebf !important;
+    border-color: #2daebf !important;
+  }
+  a {
+    color: #007bff;
+    text-decoration: none;
+    background-color: transparent;
+    -webkit-text-decoration-skip: objects;
+  }
+</style>

+ 0 - 0
src/renderer/assets/.gitkeep


BIN
src/renderer/assets/font-awesome/font/fa-brands-400.eot


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1104 - 0
src/renderer/assets/font-awesome/font/fa-brands-400.svg


BIN
src/renderer/assets/font-awesome/font/fa-brands-400.ttf


BIN
src/renderer/assets/font-awesome/font/fa-brands-400.woff


BIN
src/renderer/assets/font-awesome/font/fa-brands-400.woff2


BIN
src/renderer/assets/font-awesome/font/fa-regular-400.eot


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 372 - 0
src/renderer/assets/font-awesome/font/fa-regular-400.svg


BIN
src/renderer/assets/font-awesome/font/fa-regular-400.ttf


BIN
src/renderer/assets/font-awesome/font/fa-regular-400.woff


BIN
src/renderer/assets/font-awesome/font/fa-regular-400.woff2


BIN
src/renderer/assets/font-awesome/font/fa-solid-900.eot


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1896 - 0
src/renderer/assets/font-awesome/font/fa-solid-900.svg


BIN
src/renderer/assets/font-awesome/font/fa-solid-900.ttf


BIN
src/renderer/assets/font-awesome/font/fa-solid-900.woff


BIN
src/renderer/assets/font-awesome/font/fa-solid-900.woff2


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3203 - 0
src/renderer/assets/font-awesome/fontawesome-all.css


BIN
src/renderer/assets/img/bg.jpg


BIN
src/renderer/assets/img/bg1.jpg


BIN
src/renderer/assets/img/bg2.jpg


BIN
src/renderer/assets/img/bodyBg.jpg


BIN
src/renderer/assets/logo.png


+ 143 - 0
src/renderer/components/StartUpPage.vue

@@ -0,0 +1,143 @@
+<template>
+  <el-container class="container container-width border rounded pl-0 pr-0">
+    <el-header class="media p-3 header border-bottom shadow-sm">
+      <div class="media-body">
+        <div class="float-right webkit-drag">
+          <usb-header></usb-header>
+          <download-header ref="download"></download-header>
+          <div class="float-left mr-3"><i class="fas fa-minus" @click="minimizeWindow"></i></div>
+          <div class="float-right"><i class="fas fa-times" @click="closeWindow"></i></div>
+        </div>
+        <h5><a class="mr-2 webkit-drag" @click="goURL"><i class="fas fa-th-large"></i></a>纵横启动器</h5>
+        <!--<h5 class="webkit-drag"><router-link to="/firstopen">open</router-link>&nbsp;<router-link to="/softwarelist">softwarelist</router-link></h5>-->
+      </div>
+    </el-header>
+    <el-main>
+      <router-view v-on:softwareDownload="relationDownload"></router-view>
+      <!--<first-open></first-open>-->
+    </el-main>
+    <software-update></software-update>
+  </el-container>
+</template>
+
+<script>
+  import mixin from './mixin'
+  import SoftwareUpdate from './StartUpPage/SoftwareUpdate'
+  import UsbHeader from './StartUpPage/UsbHeader'
+  import DownloadHeader from './StartUpPage/DownloadHeader'
+  const electron = require('electron').remote
+  const BrowserWindow = electron.BrowserWindow
+
+  export default {
+    name: 'element-page',
+    mixins: [mixin],
+    components: { SoftwareUpdate, UsbHeader, DownloadHeader },
+    methods: {
+      minimizeWindow () {
+        const window = BrowserWindow.getFocusedWindow()
+        window.minimize()
+      },
+      closeWindow () {
+        const window = BrowserWindow.getFocusedWindow()
+        window.close()
+      },
+      goURL () {
+        let first = this.$db.read().get('sc_hadInstall.first').value()
+        let url = first ? { path: '/firstopen' } : { path: this.$db.read().get('sc_hadInstall.url').value() }
+        this.$router.push(url)
+      },
+      relationDownload (name, downloadpath, size) {
+        this.$refs.download.adddownload(name, downloadpath, size)
+      }
+    }
+  }
+</script>
+<style>
+  .container {
+    width: 100%;
+    padding-right: 15px;
+    padding-left: 15px;
+    margin-right: auto;
+    margin-left: auto;
+  }
+
+  .container-width {
+    position: relative;
+    width: 900px;
+    height: 564px;
+    position: relative;
+    overflow: hidden;
+    background: url(../assets/img/bg.jpg);
+  }
+
+  .header{
+    background-color: rgba(255,101,1,0.8);
+    color: #fff;
+    -webkit-app-region: drag
+  }
+  .header i {
+    color: #fff;
+  }
+  .webkit-drag {
+    -webkit-app-region: no-drag
+  }
+
+  .media-body {
+    -ms-flex: 1;
+    flex: 1;
+  }
+
+  .border {
+    border-color: rgba(255,101,1,0.8) !important;
+  }
+
+  .btn-block {
+    display: block;
+    width: 100%;
+  }
+  .el-button--primary:focus, .el-button--primary:hover {
+    background: #0fa2bf !important;
+    border-color: #0fa2bf !important;
+    color: #fff !important;
+  }
+  .el-button--medium {
+    padding: .375rem .75rem;
+    font-size: 1rem;
+    line-height: 1.5;
+    border-radius: .25rem;
+  }
+  .el-main {
+    padding: 0!important;
+  }
+  .el-loading-mask {
+    position: fixed;
+  }
+  .el-tabs__item.is-active {
+    color: #495057!important;
+    /*border-bottom: 1px solid transparent!important;*/
+    /*border-top: 1px solid #dee2e6!important;*/
+    /*border-left: 1px solid #dee2e6!important;*/
+    /*border-right: 1px solid #dee2e6!important;*/
+    border-top-left-radius: .25rem;
+    border-top-right-radius: .25rem;
+  }
+  .el-tabs__item {
+    border: 1px solid transparent;
+    border-left: none!important;
+    font-size: 1rem!important;
+    color: #007bff!important;
+    font-weight: 500!important;
+
+  }
+  .el-tabs__nav-wrap {
+    padding-left: 1rem!important;
+  }
+  .el-tabs--card>.el-tabs__header .el-tabs__nav{
+    border: none!important;
+  }
+  .el-tabs--card>.el-tabs__header .el-tabs__item.is-active{
+    border-top-color: #dee2e6;
+    border-left: 1px solid #dee2e6!important;
+    border-right-color: #dee2e6;
+  }
+</style>

+ 237 - 0
src/renderer/components/StartUpPage/DownloadHeader.vue

@@ -0,0 +1,237 @@
+<template>
+  <el-dropdown class="download-header float-left mr-3" ref="downloadmenu" trigger="click" :hide-on-click=false>
+    <span class="el-dropdown-link">
+      <i class="fas fa-download"></i><span class="badge badge-pill badge-info ml-2" v-show="downloadNum !== 0">{{ downloadNum }}</span>
+    </span>
+    <!--<el-button @click="testbtn">test</el-button>-->
+    <el-dropdown-menu slot="dropdown" class="downloadlist">
+      <div v-for="(item, index) in downloads" v-show="!item.delete" :key="item.id" class="downloaditem">
+        <div class="pb-2" v-if="item.isfinish === false">
+          <span class="float-right">
+            <span v-if="item.show">
+              <i v-show="item.pause" class="far fa-pause-circle" @click="operationDownload('pause', item.id, index)"></i>
+              <i v-show="item.resume" class="far fa-play-circle" @click="operationDownload('resume', item.id, index)"></i>
+              <i v-show="item.start" class="far fa-play-circle" @click="operationDownload('start', item.id, index)"></i>
+            </span>
+            &nbsp;&nbsp;{{ item.size }}
+          </span>
+          {{ item.name }}
+        </div>
+        <div class="pb-2" v-else>
+          <span class="float-right">
+            <i class="fas fa-times text-danger" @click="deleteDownload(item.id, index)"></i>
+          </span>{{ item.name }}
+        </div>
+        <div class="pb-2" v-if="item.isfinish === false">
+          <el-progress :text-inside="true" :stroke-width="18" :percentage="item.percent" status="success"></el-progress>
+        </div>
+        <el-button v-else @click="installbtn(item.id)" type="primary" size="medium" class="btn-sm btn-block btn-blue">安装软件</el-button>
+      </div>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  const lodashId = require('lodash-id')
+  // const path = require('path')
+  // const fs = require('fs-extra')
+  export default {
+    mixins: [mixin],
+    data () {
+      return {
+        downloadNum: '',
+        downloads: ''
+      }
+    },
+    created () {
+      this.$electron.ipcRenderer.on('downloadtips', (event, msg) => {
+        let percent = msg.percent
+        let info = this.$db.read().get('sc_download').getById(msg.id).value()
+        info.percent = percent
+        console.log(info)
+        this.downloads.splice(msg.index, 1, info)
+      })
+      this.$db._.mixin(lodashId)
+      this.downloads = this.$db.read().get('sc_download').value()
+      this.downloadNum = this.$db.read().get('sc_download').filter({delete: false}).size().value()
+
+      this.$electron.ipcRenderer.on('downloadsuccess', (event, msg) => {
+        let info = this.$db.read().get('sc_download').getById(msg.id).value()
+        info.isfinish = true
+        info.show = false
+        info.pause = false
+        info.savepath = msg.savepath
+        this.$db.read().get('sc_download').getById(msg.id).assign(info).write()
+        // 通知下载完成
+        let notification = {
+          title: '下载器',
+          body: info.name + '下载完成'
+        }
+        let myNotification = new window.Notification(notification.title, notification)
+        this.downloads.splice(msg.index, 1, info)
+        // 下载完成当前后 获取下一个info并启动它,没有则停止
+        let info2 = this.$db.read().get('sc_download').find({isfinish: false}).value()
+        if (info2 !== undefined) {
+          info2.show = true
+          this.$db.read().get('sc_download').getById(info2.id).assign(info2).write()
+          let index = this.downloads.length - this.$db.read().get('sc_download').filter({isfinish: false}).size().value()
+          console.log(index)
+          this.downloads.splice(index, 1, info2)
+          this.operationDownload('start', info2.id, index)
+        }
+        myNotification.onclick = () => {
+          return true
+        }
+      })
+    },
+    beforeDestroy () {
+    },
+    methods: {
+      checkOnline () {
+        if (!navigator.onLine) {
+          this.$message.error('当前网络不可用,无法下载')
+          return false
+        } else {
+          return true
+        }
+      },
+      operationDownload (status, id, index) {
+        if (this.checkOnline()) {
+          let info = this.$db.read().get('sc_download').getById(id).value()
+          if (status === 'start') {
+            this.$electron.ipcRenderer.send('download', {id: id, index: index})
+            info.start = false
+            info.resume = false
+            info.pause = true
+          } else if (status === 'pause') {
+            this.$electron.ipcRenderer.send(status, id)
+            info.pause = false
+            info.resume = true
+            info.start = false
+          } else if (status === 'resume') {
+            this.$electron.ipcRenderer.send(status, id)
+            info.pause = true
+            info.resume = false
+            info.start = false
+          }
+          this.downloads.splice(index, 1, info)
+          this.$db.read().get('sc_download').getById(id).assign(info).write()
+        }
+      },
+      deleteDownload (id, index) {
+        try {
+          let info = this.$db.read().get('sc_download').getById(id).value()
+          info.delete = true
+          // this.$db.read().get('sc_download').removeById(id).write()
+          // this.downloads.splice(index, 1)
+          this.$db.read().get('sc_download').getById(id).assign(info).write()
+          this.downloads.splice(index, 1, info)
+          this.downloadNum = this.$db.read().get('sc_download').filter({delete: false}).size().value()
+          // fs.removeSync(info.savepath)
+        } catch (err) {
+          console.log(err)
+        }
+      },
+      adddownload (name, downloadpath, size) {
+        if (this.checkOnline()) {
+          this.$refs.downloadmenu.show()
+          let downloadlist = {
+            'name': name,
+            'downloadpath': downloadpath,
+            'size': size,
+            'isfinish': false,
+            'percent': 0,
+            'savepath': downloadpath.substring(downloadpath.lastIndexOf('/') + 1),
+            'addtime': Date.parse(new Date()) / 1000,
+            'show': false,
+            'pause': false,
+            'resume': false,
+            'start': true,
+            'delete': false
+          }
+          let info = this.$db.read().get('sc_download').insert(downloadlist).write()
+          this.downloads.push(downloadlist)
+          this.downloadNum = this.$db.read().get('sc_download').filter({delete: false}).size().value()
+          // 如果downloads中没有未下载的info,就让新增的info进入下载过程
+          let info2 = this.$db.read().get('sc_download').find({isfinish: false}).value()
+          if (info2 !== undefined && info.id === info2.id) {
+            info.show = true
+            this.$db.read().get('sc_download').getById(info.id).assign(info).write()
+            this.operationDownload('start', info.id, this.downloads.length - 1)
+          }
+        }
+      },
+      testbtn () {
+        console.log(`新增了 1 个纵横软件`)
+        this.$message({
+          message: `新增了 1 个纵横软件`,
+          onClass: () => {
+            console.log('close')
+            // this.message.info(`移除了 1 个纵横软件`)
+          }
+        })
+      },
+      installbtn (id) {
+        let info = this.$db.read().get('sc_download').getById(id).value()
+        this.$electron.shell.openItem(info.savepath)
+      }
+    }
+  }
+</script>
+
+<style>
+  .downloadlist {
+    min-width: 400px!important;
+    max-height: 450px!important;
+    padding: 10px 15px!important;
+    overflow-y: auto!important;
+  }
+
+  .downloaditem {
+    border: 1px solid #dee2e6;
+    background: #fefaf9;
+    border-radius: 7px;
+    padding: 10px;
+    margin: 15px 0;
+  }
+
+  .text-danger {
+    color: #dc3545!important;
+  }
+  .el-progress.is-success .el-progress-bar__inner {
+    background-color: #28a745 !important;
+  }
+  .el-progress-bar__inner,.el-progress-bar__outer {
+    border-radius: .25rem !important;
+  }
+  .btn-sm {
+    padding: .25rem .5rem;
+    font-size: .875rem;
+    line-height: 1.5;
+    border-radius: .2rem;
+  }
+  .badge-info {
+    color: #fff;
+    background-color: #17a2b8;
+  }
+  .badge-pill {
+    padding-right: .6em;
+    padding-left: .6em;
+    border-radius: 10rem;
+  }
+  .badge {
+    display: inline-block;
+    padding-top: .25em;
+    padding-bottom: .25em;
+    font-size: 75%;
+    font-weight: 700;
+    line-height: 1;
+    text-align: center;
+    white-space: nowrap;
+    vertical-align: baseline;
+  }
+  .download-header {
+     cursor: pointer;
+  }
+</style>

+ 64 - 0
src/renderer/components/StartUpPage/FirstOpen.vue

@@ -0,0 +1,64 @@
+<template>
+  <div class="first-open software-wrap p-3" v-loading="loading" element-loading-text="正在获取本地数据中,请稍候...">
+    <div style="margin-top: 80px;">
+      <el-row type="flex" :gutter="30">
+        <el-col :span="6"></el-col>
+        <el-col :span="6">
+          <el-button type="primary" size="medium" class="btn btn-primary btn-blue btn-block" @click="openlist('software-list')">下载软件</el-button>
+        </el-col>
+        <el-col :span="6">
+          <el-button type="primary" size="medium" class="btn btn-primary btn-blue btn-block" @click="updateInstalledSoftware">添加已安装软件</el-button>
+        </el-col>
+        <el-col :span="6"></el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  export default {
+    mixins: [mixin],
+    data: () => ({
+      loading: false
+    }),
+    created () {
+      let self = this
+      this.$electron.ipcRenderer.once('successUpdate', (event, msg) => {
+        self.loading = false
+        let id = msg.id
+        self.$router.push({
+          name: 'software-startup-detail',
+          params: { productid: id }
+        })
+      })
+
+      this.$electron.ipcRenderer.on('failedUpdate', (event, msg) => {
+        self.loading = false
+        let id = msg.id
+        self.$router.push({
+          name: 'software-startup-detail',
+          params: { productid: id }
+        })
+      })
+    },
+    methods: {
+      openlist (index) {
+        this.$router.push({
+          name: index
+        })
+      },
+      updateInstalledSoftware () {
+        this.loading = true
+        this.$electron.ipcRenderer.send('updateInstall')
+        // this.$router.push({
+        // name: 'software-startup'
+        // })
+      }
+    }
+  }
+</script>
+
+<style>
+
+</style>

+ 133 - 0
src/renderer/components/StartUpPage/SoftwareDetail.vue

@@ -0,0 +1,133 @@
+<template>
+  <div class="software-detail" v-loading="loading">
+    <div class="pt-3 pl-3">
+      <div class="mt-2 pt-1 mr-2 float-left"><a class="software-detail-a" @click="$router.back(-1)"><h4><i class="fas fa-angle-left"></i></h4></a></div>
+      <div class="media">
+        <img class="mr-3" :src="items.src" width="50" height="50" :alt="items.title">
+        <div class="media-body">
+          <h5 class="mt-0 mb-1">{{ items.title }}</h5>
+          {{ items.productName }}
+        </div>
+      </div>
+    </div>
+    <div class="pt-3">
+      <el-tabs v-model="activeName" type="card">
+        <el-tab-pane class="pt-3 pl-3 software-content" name="first">
+          <span slot="label">下载软件</span>
+          <div class="details" v-for="down in downlist">
+            <h5 class="w-25 d-inline-block">{{ down.title }}</h5>
+            <div class="w-50 d-inline-block"><span v-if="down.key_number !== ''">锁号:{{ down.key_number }}</span></div>
+            <div class="float-right">
+              <el-button type="primary" @click="downloadSoftware(down.fulltitle, down.down_url, down.size)" class="btn btn-primary btn-blue btn-sm d-inline-block">下载</el-button>
+            </div>
+          </div>
+        </el-tab-pane>
+        <el-tab-pane label="软件详情" class="pt-3 pl-3 software-content" name="second">
+          <p><strong><span style="color:#e36c09">产品介绍:</span></strong></p>
+          <p>纵横公路造价软件2008年通过交通部测评,数据准确,品质保证。具有“模板克隆、定位查询、企业定额、清单调价、参数化设计、实时计算等功能。纵横公路软件以简洁的界面、个性化的资源、高效的算法和完善的管理让您的造价管理工作得心应手。</p>
+          <p><strong><span style="color:#e36c09">使用说明:</span></strong></p>
+          <p><a href="http://d2.smartcost.com.cn/doc/gongchengzaojiaguanlishouce.rar" target="blank">《纵横公路工程造价管理系统》简易操作手册</a></p><p><a href="http://d2.smartcost.com.cn/doc/zhaotoubiao.rar" target="blank">纵横公路造价软件招投标版功能演示</a></p>
+          <p><a href="http://d2.smartcost.com.cn/doc/gaiyusuan.rar" target="blank">纵横公路造价软件概预算版功能演示</a></p><p><a href="http://d2.smartcost.com.cn/doc/wangluoban.zip" target="blank">纵横工程造价软件免费网络版注册教程</a></p>
+          <p><a href="http://d2.smartcost.com.cn/doc/badianshiyonggongneng.zip" target="blank">纵横公路造价软件设计院客户8点实用功能</a></p>
+          <p><strong><span style="color:#e36c09">操作指引:</span></strong></p>
+          <p><img title="公路造价操作流程图.png" src="http://smartcost.com.cn/global/upload/img/20151028/14459972402207.png"></p>
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  const fs = require('fs-extra')
+  const path = require('path')
+  export default {
+    mixins: [mixin],
+    data () {
+      return {
+        activeName: 'first',
+        loading: false,
+        items: '',
+        downlist: ''
+      }
+    },
+    created () {
+      this.fetchData()
+    },
+    methods: {
+      fetchData () {
+        this.loading = true
+        let softwarejson = path.join('data/sc_software.json')
+        let softwarelist = fs.readJsonSync(softwarejson).sc_product
+        let pid = this.$route.params.pid
+        let items = ''
+        let loadings = true
+        softwarelist.forEach(function (value) {
+          if (value.product_id === pid) {
+            items = value
+            loadings = false
+            return false
+          }
+        })
+        this.items = items
+        let downlist = fs.readJsonSync(softwarejson).sc_down
+        let newdownlist = []
+        downlist.forEach(function (v) {
+          if (v.product_id === pid) {
+            newdownlist.push(v)
+          }
+        })
+        this.downlist = newdownlist
+        this.loading = loadings
+      },
+      downloadSoftware (name, downloadpath, size) {
+        // 先判断是否已在下载列表中,再加入列表中
+        let downloaditem = this.$db.read().get('sc_download').find({name: name, delete: false}).value()
+        if (!downloaditem) {
+          this.$emit('softwareDownload', name, downloadpath, size)
+        } else {
+          this.$message({
+            showClose: true,
+            message: name + '已存在下载列中',
+            iconClass: '',
+            type: 'warning'
+          })
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .software-detail {
+    width: 100%;
+    height: 497px;
+    overflow-y: auto;
+  }
+  .software-detail-a {
+    color: #007bff;
+    text-decoration: none;
+    background-color: transparent;
+  }
+  .details {
+    padding: 15px 0;
+    border-bottom: 1px dotted #ccc;
+  }
+  .details:hover{
+    border-bottom:1px solid #ff6501;
+  }
+  .w-25 {
+    width: 25%!important;
+  }
+  .w-50 {
+    width: 50%!important;
+  }
+  .software-content {
+    height: 337px;
+    overflow-y: auto;
+    padding-right: 15px;
+  }
+  .d-inline-block {
+    display: inline-block!important;
+  }
+</style>

+ 114 - 0
src/renderer/components/StartUpPage/SoftwareList.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="software-list pt-4 pb-2" v-loading="loading" element-loading-text="数据同步中">
+    <el-row type="flex" justify="center"  v-for="software in softwarelist" :key="software.product_id">
+      <el-col :span="22" class="software-item">
+        <router-link :to="{ name: 'software-detail', params: { pid: software.product_id }}">
+          <div class="media">
+            <img class="mr-3" :src="software.src" width="40" height="40" :alt="software.title">
+            <div class="media-body">
+              <div class="software-body float-left">
+                <h5 class="software-title mb-1">{{ software.title }}</h5>
+                <div>{{ software.productName }}</div>
+              </div>
+              <div class="float-left">
+                版本号:{{ software.version }}
+                <br>
+                更新时间:{{ software.updateTime }}
+              </div>
+              <div class="float-right">
+                <el-button type="primary" class="btn btn-primary btn-blue btn-sm mt-2">详情</el-button>
+              </div>
+            </div>
+          </div>
+        </router-link>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  const fs = require('fs-extra')
+  const path = require('path')
+  export default {
+    mixins: [mixin],
+    data () {
+      return {
+        softwarelist: '',
+        loading: false
+      }
+    },
+    created () {
+      let softwarejson = path.join('data/sc_software.json')
+      if (this.checkOnline()) {
+        let version = fs.readJsonSync(softwarejson).startup_version
+        let self = this
+        this.$http.get('http://smartcost.com.cn/startup/sc_version.json')
+          .then(function (response) {
+            if (version === response.data.startup_version) {
+              self.softwarelist = fs.readJsonSync(softwarejson).sc_product
+            } else {
+              self.loading = true
+              self.$http.get('http://smartcost.com.cn/startup/sc_software.json')
+                .then(function (response2) {
+                  fs.writeJsonSync(softwarejson, response2.data)
+                  self.softwarelist = response2.data.sc_product
+                  self.loading = false
+                })
+                .catch(function (error2) {
+                  self.loading = false
+                  throw error2
+                })
+            }
+          })
+          .catch(function (error) {
+            self.$message.error(error)
+          })
+      } else {
+        this.softwarelist = fs.readJsonSync(softwarejson).sc_product
+      }
+    },
+    methods: {
+      checkOnline () {
+        if (!navigator.onLine) {
+          return false
+        } else {
+          return true
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .software-list{
+    overflow-y: auto!important;
+  }
+  .software-item {
+    float: left;
+    background: #fefaf9;
+    border: 1px solid #ddd;
+    border-radius: 7px;
+    padding: 15px;
+    margin-top: 5px;
+    margin-bottom: 15px;
+  }
+  .software-item:hover{
+    background:#fdf5f2;
+    border:1px solid #ff6501;
+  }
+  .software-item a {
+    color: #333;
+  }
+  .software-item a:hover{
+    text-decoration: none;
+  }
+  .software-title {
+    font-size: 16px;
+    font-weight: bold;
+  }
+  .software-body {
+    width: 50%;
+  }
+
+</style>

+ 242 - 0
src/renderer/components/StartUpPage/SoftwareStartup.vue

@@ -0,0 +1,242 @@
+<template>
+  <div class="software-startup software-wrap" v-loading="loading" element-loading-text="正在获取本地数据中,请稍候...">
+    <div class="software-left border-right border-top">
+      <div class="local-software">
+        <div class="software media p-3" v-for="product in productlist" :key="product.id" :class="[products.id === product.id ? 'active' : '']">
+          <img class="mr-2" :src="product.src" width="25" height="25" :alt="product.title">
+          <div class="media-body">
+            <div class="mt-0 mb-1"><router-link :to="{ name: 'software-startup-detail', params: { productid: product.id } }">{{ product.title }}</router-link></div>
+          </div>
+        </div>
+      </div>
+      <div class="software-left-bottom border-top">
+        <div class="d-flex bd-highlight">
+          <div class="p-2 flex-fill bd-highlight">
+            <el-button style="width:100%;" type="primary" class="btn btn-primary btn-blue btn-sm" @click="openlist('software-list')">下载软件</el-button>
+          </div>
+          <div class="p-2 flex-fill bd-highlight">
+            <el-dropdown placement="top" trigger="click" style="width:100%;" @command="clickbtn">
+              <el-button type="primary" style="width:100%;" class="btn btn-primary btn-blue btn-sm">
+                添加软件<i class="el-icon-arrow-up el-icon--right"></i>
+              </el-button>
+              <el-dropdown-menu class="command-menu" slot="dropdown">
+                <el-dropdown-item class="command-item" command="auto">自动获取</el-dropdown-item>
+                <el-dropdown-item class="command-item" command="manual">手动添加</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </div>
+        </div>
+      </div>
+    </div>
+    <software-startup-detail ref="startupDetail"></software-startup-detail>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  import SoftwareStartupDetail from './SoftwareStartupDetail'
+  export default {
+    mixins: [mixin],
+    components: { SoftwareStartupDetail },
+    data () {
+      return {
+        activeName: 'first',
+        productlist: '',
+        products: '',
+        loading: false
+      }
+    },
+    created () {
+      this.fetchData()
+      let self = this
+      this.$electron.ipcRenderer.on('successUpdate', (event, msg) => {
+        self.loading = false
+        let num = msg.num
+        let delnum = msg.delnum
+        if (num !== 0 && delnum !== 0) {
+          self.$message({
+            message: `新增了 ${num} 个纵横软件`,
+            type: 'success'
+          })
+          setTimeout(function () {
+            self.$message({
+              message: `移除了 ${delnum} 个纵横软件`,
+              type: 'error'
+            })
+          }, 2000)
+        } else {
+          if (delnum !== 0) {
+            self.$message({
+              message: `移除了 ${delnum} 个纵横软件`,
+              type: 'error'
+            })
+          }
+
+          if (num !== 0) {
+            self.$message({
+              message: `新增了 ${num} 个纵横软件`,
+              type: 'success'
+            })
+          }
+          if (num === 0 && delnum === 0) {
+            self.$message.info('没有更新获取到新的纵横软件')
+          }
+        }
+        let id = msg.id
+        // 如果不跳转出本产品,则让downlist 每次都执行更新一遍
+        if (self.$route.params.productid === id) {
+          self.fetchData()
+          self.$refs.startupDetail.fetchData()
+        } else {
+          self.$router.push({
+            name: 'software-startup-detail',
+            params: { productid: id }
+          })
+        }
+      })
+
+      this.$electron.ipcRenderer.on('failedUpdate', (event, msg) => {
+        self.loading = false
+        self.$message.info('你的电脑还没安装过纵横软件')
+        let id = msg.id
+        if (self.$route.params.productid === id) {
+          self.fetchData()
+          self.$refs.startupDetail.fetchData()
+        } else {
+          self.$router.push({
+            name: 'software-startup-detail',
+            params: { productid: id }
+          })
+        }
+      })
+
+      this.$electron.ipcRenderer.on('failedDirectory', (event, result) => {
+        self.loading = false
+        if (result.msg !== '') {
+          self.$message({
+            message: result.msg,
+            type: 'error'
+          })
+        }
+      })
+    },
+    watch: {
+      // 如果路由有变化,会再次执行该方法
+      '$route': 'fetchData'
+    },
+    methods: {
+      fetchData () {
+        let pid = this.$route.params.productid === undefined ? this.$db.read().get('sc_productData').last().value().id : this.$route.params.productid
+        this.products = this.$db.read().get('sc_productData').getById(pid).value()
+        this.productlist = this.$db.read().get('sc_productData').filter({ isshow: true }).orderBy('addtime', 'desc').value()
+      },
+      openlist (index) {
+        this.$router.push({
+          name: index
+        })
+      },
+      clickbtn (command) {
+        this.loading = true
+        if (command === 'auto') {
+          this.$electron.ipcRenderer.send('updateInstall')
+        } else {
+          this.$electron.ipcRenderer.send('file-select')
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .software-left {
+    position: absolute;
+    width: 250px;
+    top: 59px;
+    bottom: 0;
+    left: 0;
+  }
+  .border-right {
+    border-right: 1px solid #dee2e6!important;
+  }
+  .border-top {
+    border-top: 1px solid #dee2e6!important;
+  }
+  .local-software {
+    position: absolute;
+    width: 100%;
+    top: 0;
+    left: 0;
+    bottom: 50px;
+    /* background: #f9f9f9; */
+    background-color: rgba(254,250,249,0.3);
+    overflow-y: auto;
+  }
+  .software-left-bottom {
+    background-color: rgba(251,231,216,0.5);
+    z-index: 999;
+    position: absolute;
+    width: 100%;
+    bottom: 0;
+    left: 0;
+  }
+  .local-install {
+    position: absolute;
+    top: 80px;
+    right: 0;
+    bottom: 0;
+    width: 648px;
+    overflow-y: auto;
+  }
+  .software.active {
+    background: #fdf5f2;
+    border-left: 3px solid #ff6501;
+  }
+  .software.active a {
+    color: #333;
+  }
+  .software a {
+    text-decoration: none;
+  }
+  .d-flex {
+    display: -ms-flexbox!important;
+    display: flex!important;
+  }
+  .flex-fill {
+    -ms-flex: 1 1 auto!important;
+    flex: 1 1 auto!important;
+  }
+  .software-content {
+    height: 337px;
+    overflow-y: auto;
+    padding-right: 15px;
+  }
+  .details {
+    padding: 15px 0;
+    border-bottom: 1px dotted #ccc;
+  }
+  .details:hover{
+    border-bottom:1px solid #ff6501;
+  }
+  .w-25 {
+    width: 25%!important;
+  }
+  .w-50 {
+    width: 50%!important;
+  }
+  .d-inline-block {
+    display: inline-block!important;
+  }
+  .command-menu {
+    padding: .5rem 0 ;
+    color: #212529;
+    border-radius: .25rem;
+    margin: .125rem 0 0;
+    font-size: 1rem;
+  }
+  .command-item {
+    font-size: 1rem;
+  }
+  .el-popper[x-placement^=top] {
+    margin-bottom: 5px!important;
+  }
+</style>

+ 153 - 0
src/renderer/components/StartUpPage/SoftwareStartupDetail.vue

@@ -0,0 +1,153 @@
+<template>
+  <div class="software-startup-detail">
+    <div class="media pl-3">
+      <img class="mr-3" :src="products.src" width="50" height="50" :alt="products.title">
+      <div class="media-body">
+        <h5 class="mt-0 mb-1">{{ products.title }}</h5>
+        {{ products.productName }}&nbsp;
+      </div>
+    </div>
+    <div class="mt-4">
+      <el-tabs v-model="activeName" type="card">
+        <el-tab-pane class="pl-3 software-content" name="first">
+          <span slot="label">启动软件</span>
+          <div class="details" v-for="exe in exelist">
+            <h5 class="w-25 d-inline-block" v-if="exe.versionName !== ''">{{ exe.versionName }}</h5>
+            <h5 v-else>{{ exe.fileName }}</h5>
+            <div class="w-50 d-inline-block" v-if="exe.keyNumber !== ''">锁号:{{ exe.keyNumber }}</div>
+            <div class="w-50 d-inline-block"><b>{{ exe.fileVersion }}</b></div>
+            <div class="float-right">
+              <el-button type="primary" class="btn btn-primary btn-blue btn-sm d-inline-block" @click="openExebtn(exe.id)">启动软件</el-button>
+            </div>
+            <div>{{ exe.path }}</div>
+          </div>
+        </el-tab-pane>
+        <el-tab-pane label="软件详情" class="pt-3 pl-3 software-content" name="second">
+          <p><strong><span style="color:#e36c09">产品介绍:</span></strong></p>
+          <p>纵横公路造价软件2008年通过交通部测评,数据准确,品质保证。具有“模板克隆、定位查询、企业定额、清单调价、参数化设计、实时计算等功能。纵横公路软件以简洁的界面、个性化的资源、高效的算法和完善的管理让您的造价管理工作得心应手。</p>
+          <p><strong><span style="color:#e36c09">使用说明:</span></strong></p>
+          <p><a href="http://d2.smartcost.com.cn/doc/gongchengzaojiaguanlishouce.rar" target="blank">《纵横公路工程造价管理系统》简易操作手册</a></p><p><a href="http://d2.smartcost.com.cn/doc/zhaotoubiao.rar" target="blank">纵横公路造价软件招投标版功能演示</a></p>
+          <p><a href="http://d2.smartcost.com.cn/doc/gaiyusuan.rar" target="blank">纵横公路造价软件概预算版功能演示</a></p><p><a href="http://d2.smartcost.com.cn/doc/wangluoban.zip" target="blank">纵横工程造价软件免费网络版注册教程</a></p>
+          <p><a href="http://d2.smartcost.com.cn/doc/badianshiyonggongneng.zip" target="blank">纵横公路造价软件设计院客户8点实用功能</a></p>
+          <p><strong><span style="color:#e36c09">操作指引:</span></strong></p>
+          <p><img title="公路造价操作流程图.png" src="http://smartcost.com.cn/global/upload/img/20151028/14459972402207.png"></p>
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  const path = require('path')
+  export default {
+    mixins: [mixin],
+    data () {
+      return {
+        activeName: 'first',
+        products: '',
+        exelist: ''
+      }
+    },
+    created () {
+      this.fetchData()
+    },
+    watch: {
+      // 如果路由有变化,会再次执行该方法
+      '$route': 'fetchData'
+    },
+    methods: {
+      fetchData () {
+        let productid = this.$route.params.productid === undefined ? this.$db.read().get('sc_productData').last().value().id : this.$route.params.productid
+        this.$db.read().set('sc_hadInstall.url', '/softwarestartup/' + productid).write()
+        this.products = this.$db.read().get('sc_productData').getById(productid).value()
+        this.exelist = this.$db.read().get('sc_exeData').filter({ pid: productid }).orderBy('addtime', 'desc').value()
+      },
+      openExebtn (id) {
+        let info = this.$db.read().get('sc_exeData').getById(id).value()
+        this.$electron.shell.openItem(path.join(info.path, info.exeName))
+      }
+    }
+  }
+</script>
+
+<style>
+  .software-left {
+    position: absolute;
+    width: 250px;
+    top: 59px;
+    bottom: 0;
+    left: 0;
+  }
+  .border-right {
+    border-right: 1px solid #dee2e6!important;
+  }
+  .border-top {
+    border-top: 1px solid #dee2e6!important;
+  }
+  .local-software {
+    position: absolute;
+    width: 100%;
+    top: 0;
+    left: 0;
+    bottom: 50px;
+    /* background: #f9f9f9; */
+    background-color: rgba(254,250,249,0.3);
+    overflow-y: auto;
+  }
+  .software-left-bottom {
+    background-color: rgba(251,231,216,0.5);
+    z-index: 999;
+    position: absolute;
+    width: 100%;
+    bottom: 0;
+    left: 0;
+  }
+  .software-startup-detail {
+    position: absolute;
+    top: 80px;
+    right: 0;
+    bottom: 0;
+    width: 648px;
+    overflow-y: auto;
+  }
+  .software.active {
+    background: #fdf5f2;
+    border-left: 3px solid #ff6501;
+  }
+  .software.active a {
+    color: #333;
+  }
+  .software a {
+    text-decoration: none;
+  }
+  .d-flex {
+    display: -ms-flexbox!important;
+    display: flex!important;
+  }
+  .flex-fill {
+    -ms-flex: 1 1 auto!important;
+    flex: 1 1 auto!important;
+  }
+  .software-content {
+    height: 337px;
+    overflow-y: auto;
+    padding-right: 15px;
+  }
+  .details {
+    padding: 15px 0;
+    border-bottom: 1px dotted #ccc;
+  }
+  .details:hover{
+    border-bottom:1px solid #ff6501;
+  }
+  .w-25 {
+    width: 25%!important;
+  }
+  .w-50 {
+    width: 50%!important;
+  }
+  .d-inline-block {
+    display: inline-block!important;
+  }
+</style>

+ 66 - 0
src/renderer/components/StartUpPage/SoftwareUpdate.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="software-update">
+    <el-dialog
+            title="正在下载新版本,请稍候......"
+            :visible.sync="centerDialogVisible"
+            width="50%"
+            :show-close=false
+            :close-on-click-modal=false
+            :close-on-press-escape=false
+            >
+      <el-progress :percentage="uploadPercent"></el-progress>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  export default {
+    mixins: [mixin],
+    created () {
+      this.$electron.ipcRenderer.send('checkForUpdate')
+
+      this.$electron.ipcRenderer.on('msgBox', (event, status) => {
+        if (status === 2) {
+          this.openUpdateDialog()
+        }
+      })
+
+      this.$electron.ipcRenderer.on('isUpdateNow', function (event, index) {
+        event.sender.send('isUpdateNow')
+      })
+    },
+    data () {
+      return {
+        uploadPercent: 0,
+        centerDialogVisible: false
+      }
+    },
+    methods: {
+      openUpdateDialog () {
+        this.$confirm('更新内容:1:添加XXX功能<br>' +
+          '2:修改XXXbug<br>' +
+          '3:提高了响应速度', '新版本更新提示', {
+          confirmButtonText: '马上更新',
+          cancelButtonText: '稍后再说',
+          center: true,
+          showClose: false,
+          closeOnClickModal: false,
+          dangerouslyUseHTMLString: true
+        }).then(() => {
+          setTimeout(() => {
+            this.ProcessDialog()
+          }, 200)
+        }).catch(() => {
+        })
+      },
+      ProcessDialog () {
+        this.$electron.ipcRenderer.send('downloadUpdate')
+        this.centerDialogVisible = true
+        this.$electron.ipcRenderer.on('downloadProgress', (event, progressObj) => {
+          this.uploadPercent = parseInt(progressObj.percent)
+        })
+      }
+    }
+  }
+</script>

+ 32 - 0
src/renderer/components/StartUpPage/UsbHeader.vue

@@ -0,0 +1,32 @@
+<template>
+  <div v-show="usbshow" class="usb-header float-left mr-3">
+    <div class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+      <i class="fab fa-usb"></i>
+    </div>
+  </div>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  export default {
+    mixins: [mixin],
+    data: () => ({
+      usbshow: false
+    }),
+    created () {
+      //  this.$electron.ipcRenderer.send('testUsb')
+      this.$electron.ipcRenderer.on('usbIn', (event) => {
+        this.usbshow = true
+      })
+      this.$electron.ipcRenderer.on('usbOut', (event) => {
+        this.usbshow = false
+      })
+    }
+  }
+  const app = require('electron').remote.app
+  console.log(app.getPath('userData'))
+</script>
+
+<style>
+
+</style>

+ 26 - 0
src/renderer/components/mixin.js

@@ -0,0 +1,26 @@
+export default {
+  mounted () {
+    this.disableDragEvent()
+  },
+  methods: {
+    disableDragEvent () {
+      window.addEventListener('dragenter', this.disableDrag, false)
+      window.addEventListener('dragover', this.disableDrag)
+      window.addEventListener('drop', this.disableDrag)
+    },
+    disableDrag (e) {
+      // const dropzone = document.getElementById('upload-area') // 这个是可拖拽的上传区
+      // if (dropzone === null || !dropzone.contains(e.target)) {
+      e.preventDefault()
+      return false
+      //   e.dataTransfer.effectAllowed = 'none'
+      //   e.dataTransfer.dropEffect = 'none'
+      // }
+    }
+  },
+  beforeDestroy () {
+    window.removeEventListener('dragenter', this.disableDrag, false)
+    window.removeEventListener('dragover', this.disableDrag)
+    window.removeEventListener('drop', this.disableDrag)
+  }
+}

+ 27 - 0
src/renderer/main.js

@@ -0,0 +1,27 @@
+import Vue from 'vue'
+import axios from 'axios'
+
+import App from './App'
+import router from './router'
+import store from './store'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import db from '../database'
+
+if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
+
+Vue.http = Vue.prototype.$http = axios
+
+Vue.config.productionTip = false
+
+Vue.use(ElementUI)
+
+Vue.prototype.$db = db
+
+/* eslint-disable no-new */
+new Vue({
+  components: { App },
+  router,
+  store,
+  template: '<App/>'
+}).$mount('#app')

+ 47 - 0
src/renderer/router/index.js

@@ -0,0 +1,47 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+
+Vue.use(Router)
+
+export default new Router({
+  routes: [
+    {
+      path: '/',
+      name: 'startup-page',
+      component: require('@/components/StartUpPage').default,
+      children: [
+        {
+          path: 'firstopen',
+          component: require('@/components/StartUpPage/FirstOpen').default,
+          name: 'first-open'
+        },
+        {
+          path: 'softwarelist',
+          component: require('@/components/StartUpPage/SoftwareList').default,
+          name: 'software-list'
+        },
+        {
+          path: 'softwaredetail/:pid',
+          component: require('@/components/StartUpPage/SoftwareDetail').default,
+          name: 'software-detail'
+        },
+        {
+          path: 'softwarestartup',
+          component: require('@/components/StartUpPage/SoftwareStartup').default,
+          name: 'software-startup',
+          children: [
+            {
+              path: ':productid',
+              component: require('@/components/StartUpPage/SoftwareStartupDetail').default,
+              name: 'software-startup-detail'
+            }
+          ]
+        }
+      ]
+    },
+    {
+      path: '*',
+      redirect: '/'
+    }
+  ]
+})

+ 11 - 0
src/renderer/store/index.js

@@ -0,0 +1,11 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+import modules from './modules'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  modules,
+  strict: process.env.NODE_ENV !== 'production'
+})

+ 25 - 0
src/renderer/store/modules/Counter.js

@@ -0,0 +1,25 @@
+const state = {
+  main: 0
+}
+
+const mutations = {
+  DECREMENT_MAIN_COUNTER (state) {
+    state.main--
+  },
+  INCREMENT_MAIN_COUNTER (state) {
+    state.main++
+  }
+}
+
+const actions = {
+  someAsyncTask ({ commit }) {
+    // do something async
+    commit('INCREMENT_MAIN_COUNTER')
+  }
+}
+
+export default {
+  state,
+  mutations,
+  actions
+}

+ 14 - 0
src/renderer/store/modules/index.js

@@ -0,0 +1,14 @@
+/**
+ * The file enables `@/store/index.js` to import all vuex modules
+ * in a one-shot manner. There should not be any reason to edit this file.
+ */
+
+const files = require.context('.', false, /\.js$/)
+const modules = {}
+
+files.keys().forEach(key => {
+  if (key === './index.js') return
+  modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
+})
+
+export default modules

+ 0 - 0
static/.gitkeep


+ 11 - 0
test/.eslintrc

@@ -0,0 +1,11 @@
+{
+  "env": {
+    "mocha": true
+  },
+  "globals": {
+    "assert": true,
+    "expect": true,
+    "should": true,
+    "__static": true
+  }
+}

+ 18 - 0
test/e2e/index.js

@@ -0,0 +1,18 @@
+'use strict'
+
+// Set BABEL_ENV to use proper env config
+process.env.BABEL_ENV = 'test'
+
+// Enable use of ES6+ on required files
+require('babel-register')({
+  ignore: /node_modules/
+})
+
+// Attach Chai APIs to global scope
+const { expect, should, assert } = require('chai')
+global.expect = expect
+global.should = should
+global.assert = assert
+
+// Require all JS files in `./specs` for Mocha to consume
+require('require-dir')('./specs')

+ 13 - 0
test/e2e/specs/Launch.spec.js

@@ -0,0 +1,13 @@
+import utils from '../utils'
+
+describe('Launch', function () {
+  beforeEach(utils.beforeEach)
+  afterEach(utils.afterEach)
+
+  it('shows the proper application title', function () {
+    return this.app.client.getTitle()
+      .then(title => {
+        expect(title).to.equal('startup')
+      })
+  })
+})

+ 23 - 0
test/e2e/utils.js

@@ -0,0 +1,23 @@
+import electron from 'electron'
+import { Application } from 'spectron'
+
+export default {
+  afterEach () {
+    this.timeout(10000)
+
+    if (this.app && this.app.isRunning()) {
+      return this.app.stop()
+    }
+  },
+  beforeEach () {
+    this.timeout(10000)
+    this.app = new Application({
+      path: electron,
+      args: ['dist/electron/main.js'],
+      startTimeout: 10000,
+      waitTimeout: 10000
+    })
+
+    return this.app.start()
+  }
+}

+ 13 - 0
test/unit/index.js

@@ -0,0 +1,13 @@
+import Vue from 'vue'
+Vue.config.devtools = false
+Vue.config.productionTip = false
+
+// require all test files (files that ends with .spec.js)
+const testsContext = require.context('./specs', true, /\.spec$/)
+testsContext.keys().forEach(testsContext)
+
+// require all src files except main.js for coverage.
+// you can also change this to match only the subset of files that
+// you want coverage for.
+const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/)
+srcContext.keys().forEach(srcContext)

+ 62 - 0
test/unit/karma.conf.js

@@ -0,0 +1,62 @@
+'use strict'
+
+const path = require('path')
+const merge = require('webpack-merge')
+const webpack = require('webpack')
+
+const baseConfig = require('../../.electron-vue/webpack.renderer.config')
+const projectRoot = path.resolve(__dirname, '../../src/renderer')
+
+// Set BABEL_ENV to use proper preset config
+process.env.BABEL_ENV = 'test'
+
+let webpackConfig = merge(baseConfig, {
+  devtool: '#inline-source-map',
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env.NODE_ENV': '"testing"'
+    })
+  ]
+})
+
+// don't treat dependencies as externals
+delete webpackConfig.entry
+delete webpackConfig.externals
+delete webpackConfig.output.libraryTarget
+
+// apply vue option to apply isparta-loader on js
+webpackConfig.module.rules
+  .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader'
+
+module.exports = config => {
+  config.set({
+    browsers: ['visibleElectron'],
+    client: {
+      useIframe: false
+    },
+    coverageReporter: {
+      dir: './coverage',
+      reporters: [
+        { type: 'lcov', subdir: '.' },
+        { type: 'text-summary' }
+      ]
+    },
+    customLaunchers: {
+      'visibleElectron': {
+        base: 'Electron',
+        flags: ['--show']
+      }
+    },
+    frameworks: ['mocha', 'chai'],
+    files: ['./index.js'],
+    preprocessors: {
+      './index.js': ['webpack', 'sourcemap']
+    },
+    reporters: ['spec', 'coverage'],
+    singleRun: true,
+    webpack: webpackConfig,
+    webpackMiddleware: {
+      noInfo: true
+    }
+  })
+}

+ 13 - 0
test/unit/specs/LandingPage.spec.js

@@ -0,0 +1,13 @@
+import Vue from 'vue'
+import LandingPage from '@/components/LandingPage'
+
+describe('LandingPage.vue', () => {
+  it('should render correct contents', () => {
+    const vm = new Vue({
+      el: document.createElement('div'),
+      render: h => h(LandingPage)
+    }).$mount()
+
+    expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!')
+  })
+})

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 9271 - 0
yarn.lock