xusl преди 1 година
ревизия
a9475a493b
променени са 100 файла, в които са добавени 11319 реда и са изтрити 0 реда
  1. 20 0
      .gitignore
  2. 3 0
      README.md
  3. 14 0
      ipfcst/ipfcst-console/.editorconfig
  4. 14 0
      ipfcst/ipfcst-console/.env.development
  5. 6 0
      ipfcst/ipfcst-console/.env.production
  6. 8 0
      ipfcst/ipfcst-console/.env.staging
  7. 198 0
      ipfcst/ipfcst-console/.eslintrc.js
  8. 17 0
      ipfcst/ipfcst-console/.gitignore
  9. 5 0
      ipfcst/ipfcst-console/.travis.yml
  10. 21 0
      ipfcst/ipfcst-console/LICENSE
  11. 98 0
      ipfcst/ipfcst-console/README-zh.md
  12. 91 0
      ipfcst/ipfcst-console/README.md
  13. 5 0
      ipfcst/ipfcst-console/babel.config.js
  14. 35 0
      ipfcst/ipfcst-console/build/index.js
  15. 24 0
      ipfcst/ipfcst-console/jest.config.js
  16. 9 0
      ipfcst/ipfcst-console/jsconfig.json
  17. 75 0
      ipfcst/ipfcst-console/package.json
  18. 564 0
      ipfcst/ipfcst-console/pom.xml
  19. 8 0
      ipfcst/ipfcst-console/postcss.config.js
  20. BIN
      ipfcst/ipfcst-console/public/favicon.ico
  21. 17 0
      ipfcst/ipfcst-console/public/index.html
  22. 11 0
      ipfcst/ipfcst-console/src/main/frontend/App.vue
  23. 24 0
      ipfcst/ipfcst-console/src/main/frontend/api/user.js
  24. BIN
      ipfcst/ipfcst-console/src/main/frontend/assets/404_images/404.png
  25. BIN
      ipfcst/ipfcst-console/src/main/frontend/assets/404_images/404_cloud.png
  26. BIN
      ipfcst/ipfcst-console/src/main/frontend/assets/img1.jpg
  27. BIN
      ipfcst/ipfcst-console/src/main/frontend/assets/login-null.jpg
  28. BIN
      ipfcst/ipfcst-console/src/main/frontend/assets/login.png
  29. 105 0
      ipfcst/ipfcst-console/src/main/frontend/assets/particles.json
  30. 78 0
      ipfcst/ipfcst-console/src/main/frontend/components/Breadcrumb/index.vue
  31. 44 0
      ipfcst/ipfcst-console/src/main/frontend/components/Hamburger/index.vue
  32. 62 0
      ipfcst/ipfcst-console/src/main/frontend/components/SvgIcon/index.vue
  33. 9 0
      ipfcst/ipfcst-console/src/main/frontend/icons/index.js
  34. 0 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/dashboard.svg
  35. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/example.svg
  36. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/eye-open.svg
  37. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/eye.svg
  38. 0 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/form.svg
  39. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/link.svg
  40. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/nested.svg
  41. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/password.svg
  42. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/system.svg
  43. 2 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/table.svg
  44. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/tree.svg
  45. 1 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svg/user.svg
  46. 22 0
      ipfcst/ipfcst-console/src/main/frontend/icons/svgo.yml
  47. 40 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/AppMain.vue
  48. 159 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Navbar.vue
  49. 26 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/FixiOSBug.js
  50. 29 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/Item.vue
  51. 36 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/Link.vue
  52. 82 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/Logo.vue
  53. 95 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/SidebarItem.vue
  54. 266 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/index.vue
  55. 3 0
      ipfcst/ipfcst-console/src/main/frontend/layout/components/index.js
  56. 93 0
      ipfcst/ipfcst-console/src/main/frontend/layout/index.vue
  57. 45 0
      ipfcst/ipfcst-console/src/main/frontend/layout/mixin/ResizeHandler.js
  58. 215 0
      ipfcst/ipfcst-console/src/main/frontend/main.js
  59. 62 0
      ipfcst/ipfcst-console/src/main/frontend/permission.js
  60. 99 0
      ipfcst/ipfcst-console/src/main/frontend/router/index.js
  61. 185 0
      ipfcst/ipfcst-console/src/main/frontend/router/modules/console.js
  62. 66 0
      ipfcst/ipfcst-console/src/main/frontend/router/modules/dataexchange.js
  63. 46 0
      ipfcst/ipfcst-console/src/main/frontend/router/modules/system.js
  64. 46 0
      ipfcst/ipfcst-console/src/main/frontend/router/modules/uploadFile.js
  65. 16 0
      ipfcst/ipfcst-console/src/main/frontend/settings.js
  66. 8 0
      ipfcst/ipfcst-console/src/main/frontend/store/getters.js
  67. 19 0
      ipfcst/ipfcst-console/src/main/frontend/store/index.js
  68. 48 0
      ipfcst/ipfcst-console/src/main/frontend/store/modules/app.js
  69. 31 0
      ipfcst/ipfcst-console/src/main/frontend/store/modules/settings.js
  70. 116 0
      ipfcst/ipfcst-console/src/main/frontend/store/modules/user.js
  71. 54 0
      ipfcst/ipfcst-console/src/main/frontend/styles/element-ui.scss
  72. 65 0
      ipfcst/ipfcst-console/src/main/frontend/styles/index.scss
  73. 28 0
      ipfcst/ipfcst-console/src/main/frontend/styles/mixin.scss
  74. 209 0
      ipfcst/ipfcst-console/src/main/frontend/styles/sidebar.scss
  75. 48 0
      ipfcst/ipfcst-console/src/main/frontend/styles/transition.scss
  76. 25 0
      ipfcst/ipfcst-console/src/main/frontend/styles/variables.scss
  77. 15 0
      ipfcst/ipfcst-console/src/main/frontend/utils/auth.js
  78. 13 0
      ipfcst/ipfcst-console/src/main/frontend/utils/commonFuc.js
  79. 10 0
      ipfcst/ipfcst-console/src/main/frontend/utils/get-page-title.js
  80. 107 0
      ipfcst/ipfcst-console/src/main/frontend/utils/index.js
  81. 126 0
      ipfcst/ipfcst-console/src/main/frontend/utils/request.js
  82. 20 0
      ipfcst/ipfcst-console/src/main/frontend/utils/validate.js
  83. 228 0
      ipfcst/ipfcst-console/src/main/frontend/views/404.vue
  84. 113 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/FileAnalysisRecord/index.vue
  85. 103 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/QREnvironmentData/index.vue
  86. 343 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/agc_avcInfo/index.vue
  87. 332 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/boosterStationInfo/index.vue
  88. 309 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/communicationEquipment/index.vue
  89. 361 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/dataLimitation/index.vue
  90. 274 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/dataLimitationLog/index.vue
  91. 511 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/electricField/index.vue
  92. 391 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/holidayCalendar/index.vue
  93. 569 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/inverterInfo/index.vue
  94. 1031 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/manualInterventionData/index.vue
  95. 536 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/manualInterventionQuery/index.vue
  96. 545 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/overHaulPlan/index.vue
  97. 545 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/overHaulPlanOne/index.vue
  98. 196 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/powerPlanSD/index.vue
  99. 457 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/pvModuleModel/index.vue
  100. 326 0
      ipfcst/ipfcst-console/src/main/frontend/views/console/subsidiaryEquipmentInfo/index.vue

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+package-lock.json
+tests/**/coverage/
+
+# Editor directories and files
+/*.iml
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+/src/main/resources/static/
+/.idea/
+**/target/
+**/.iml

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+# ipfcst-znzx
+
+中能智新定制版本

+ 14 - 0
ipfcst/ipfcst-console/.editorconfig

@@ -0,0 +1,14 @@
+# http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 14 - 0
ipfcst/ipfcst-console/.env.development

@@ -0,0 +1,14 @@
+# just a flag
+ENV = 'development'
+
+# base api
+VUE_APP_BASE_API = '/dev-api'
+
+# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
+# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
+# It only does one thing by converting all import() to require().
+# This configuration can significantly increase the speed of hot updates,
+# when you have a large number of pages.
+# Detail:  https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
+
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 6 - 0
ipfcst/ipfcst-console/.env.production

@@ -0,0 +1,6 @@
+# just a flag
+ENV = 'production'
+
+# base api
+VUE_APP_BASE_API = '/'
+

+ 8 - 0
ipfcst/ipfcst-console/.env.staging

@@ -0,0 +1,8 @@
+NODE_ENV = production
+
+# just a flag
+ENV = 'staging'
+
+# base api
+VUE_APP_BASE_API = '/stage-api'
+

+ 198 - 0
ipfcst/ipfcst-console/.eslintrc.js

@@ -0,0 +1,198 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint',
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true,
+  },
+  extends: ['plugin:vue/recommended', 'eslint:recommended'],
+
+  // add your custom rules here
+  //it is base on https://github.com/vuejs/eslint-config-vue
+  rules: {
+    "vue/max-attributes-per-line": [2, {
+      "singleline": 10,
+      "multiline": {
+        "max": 1,
+        "allowFirstLine": false
+      }
+    }],
+    "vue/singleline-html-element-content-newline": "off",
+    "vue/multiline-html-element-content-newline":"off",
+    "vue/name-property-casing": ["error", "PascalCase"],
+    "vue/no-v-html": "off",
+    'accessor-pairs': 2,
+    'arrow-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'block-spacing': [2, 'always'],
+    'brace-style': [2, '1tbs', {
+      'allowSingleLine': true
+    }],
+    'camelcase': [0, {
+      'properties': 'always'
+    }],
+    'comma-dangle': [2, 'never'],
+    'comma-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'comma-style': [2, 'last'],
+    'constructor-super': 2,
+    'curly': [2, 'multi-line'],
+    'dot-location': [2, 'property'],
+    'eol-last': 2,
+    'eqeqeq': ["error", "always", {"null": "ignore"}],
+    'generator-star-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'handle-callback-err': [2, '^(err|error)$'],
+    'indent': [2, 2, {
+      'SwitchCase': 1
+    }],
+    'jsx-quotes': [2, 'prefer-single'],
+    'key-spacing': [2, {
+      'beforeColon': false,
+      'afterColon': true
+    }],
+    'keyword-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'new-cap': [2, {
+      'newIsCap': true,
+      'capIsNew': false
+    }],
+    'new-parens': 2,
+    'no-array-constructor': 2,
+    'no-caller': 2,
+    'no-console': 'off',
+    'no-class-assign': 2,
+    'no-cond-assign': 2,
+    'no-const-assign': 2,
+    'no-control-regex': 0,
+    'no-delete-var': 2,
+    'no-dupe-args': 2,
+    'no-dupe-class-members': 2,
+    'no-dupe-keys': 2,
+    'no-duplicate-case': 2,
+    'no-empty-character-class': 2,
+    'no-empty-pattern': 2,
+    'no-eval': 2,
+    'no-ex-assign': 2,
+    'no-extend-native': 2,
+    'no-extra-bind': 2,
+    'no-extra-boolean-cast': 2,
+    'no-extra-parens': [2, 'functions'],
+    'no-fallthrough': 2,
+    'no-floating-decimal': 2,
+    'no-func-assign': 2,
+    'no-implied-eval': 2,
+    'no-inner-declarations': [2, 'functions'],
+    'no-invalid-regexp': 2,
+    'no-irregular-whitespace': 2,
+    'no-iterator': 2,
+    'no-label-var': 2,
+    'no-labels': [2, {
+      'allowLoop': false,
+      'allowSwitch': false
+    }],
+    'no-lone-blocks': 2,
+    'no-mixed-spaces-and-tabs': 2,
+    'no-multi-spaces': 2,
+    'no-multi-str': 2,
+    'no-multiple-empty-lines': [2, {
+      'max': 1
+    }],
+    'no-native-reassign': 2,
+    'no-negated-in-lhs': 2,
+    'no-new-object': 2,
+    'no-new-require': 2,
+    'no-new-symbol': 2,
+    'no-new-wrappers': 2,
+    'no-obj-calls': 2,
+    'no-octal': 2,
+    'no-octal-escape': 2,
+    'no-path-concat': 2,
+    'no-proto': 2,
+    'no-redeclare': 2,
+    'no-regex-spaces': 2,
+    'no-return-assign': [2, 'except-parens'],
+    'no-self-assign': 2,
+    'no-self-compare': 2,
+    'no-sequences': 2,
+    'no-shadow-restricted-names': 2,
+    'no-spaced-func': 2,
+    'no-sparse-arrays': 2,
+    'no-this-before-super': 2,
+    'no-throw-literal': 2,
+    'no-trailing-spaces': 2,
+    'no-undef': 2,
+    'no-undef-init': 2,
+    'no-unexpected-multiline': 2,
+    'no-unmodified-loop-condition': 2,
+    'no-unneeded-ternary': [2, {
+      'defaultAssignment': false
+    }],
+    'no-unreachable': 2,
+    'no-unsafe-finally': 2,
+    'no-unused-vars': [2, {
+      'vars': 'all',
+      'args': 'none'
+    }],
+    'no-useless-call': 2,
+    'no-useless-computed-key': 2,
+    'no-useless-constructor': 2,
+    'no-useless-escape': 0,
+    'no-whitespace-before-property': 2,
+    'no-with': 2,
+    'one-var': [2, {
+      'initialized': 'never'
+    }],
+    'operator-linebreak': [2, 'after', {
+      'overrides': {
+        '?': 'before',
+        ':': 'before'
+      }
+    }],
+    'padded-blocks': [2, 'never'],
+    'quotes': [2, 'single', {
+      'avoidEscape': true,
+      'allowTemplateLiterals': true
+    }],
+    'semi': [2, 'never'],
+    'semi-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'space-before-blocks': [2, 'always'],
+    'space-before-function-paren': [2, 'never'],
+    'space-in-parens': [2, 'never'],
+    'space-infix-ops': 2,
+    'space-unary-ops': [2, {
+      'words': true,
+      'nonwords': false
+    }],
+    'spaced-comment': [2, 'always', {
+      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
+    }],
+    'template-curly-spacing': [2, 'never'],
+    'use-isnan': 2,
+    'valid-typeof': 2,
+    'wrap-iife': [2, 'any'],
+    'yield-star-spacing': [2, 'both'],
+    'yoda': [2, 'never'],
+    'prefer-const': 2,
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+    'object-curly-spacing': [2, 'always', {
+      objectsInObjects: false
+    }],
+    'array-bracket-spacing': [2, 'never']
+  }
+}

+ 17 - 0
ipfcst/ipfcst-console/.gitignore

@@ -0,0 +1,17 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+package-lock.json
+tests/**/coverage/
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+/src/main/resources/static/

+ 5 - 0
ipfcst/ipfcst-console/.travis.yml

@@ -0,0 +1,5 @@
+language: node_js
+node_js: 10
+script: npm run test
+notifications:
+  email: false

+ 21 - 0
ipfcst/ipfcst-console/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017-present PanJiaChen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 98 - 0
ipfcst/ipfcst-console/README-zh.md

@@ -0,0 +1,98 @@
+# vue-admin-template
+
+> 这是一个极简的 vue admin 管理后台。它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
+
+[线上地址](http://panjiachen.github.io/vue-admin-template)
+
+[国内访问](https://panjiachen.gitee.io/vue-admin-template)
+
+目前版本为 `v4.0+` 基于 `vue-cli` 进行构建,若你想使用旧版本,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-admin-template/tree/tag/3.11.0),它不依赖 `vue-cli`。
+
+## Extra
+
+如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
+
+## 相关项目
+
+- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
+
+- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
+
+- [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
+
+- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
+
+写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
+
+- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
+- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
+- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
+- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
+- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
+
+## Build Setup
+
+```bash
+# 克隆项目
+git clone https://github.com/PanJiaChen/vue-admin-template.git
+
+# 进入项目目录
+cd vue-admin-template
+
+# 安装依赖
+npm install
+
+# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
+npm install --registry=https://registry.npm.taobao.org
+
+# 启动服务
+npm run dev
+```
+
+浏览器访问 [http://localhost:9528](http://localhost:9528)
+
+## 发布
+
+```bash
+# 构建测试环境
+npm run build:stage
+
+# 构建生产环境
+npm run build:prod
+```
+
+## 其它
+
+```bash
+# 预览发布环境效果
+npm run preview
+
+# 预览发布环境效果 + 静态资源分析
+npm run preview -- --report
+
+# 代码格式检查
+npm run lint
+
+# 代码格式检查并自动修复
+npm run lint -- --fix
+```
+
+更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
+
+## Demo
+
+![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
+
+## Browsers support
+
+Modern browsers and Internet Explorer 10+.
+
+| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
+| --------- | --------- | --------- | --------- |
+| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
+
+## License
+
+[MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
+
+Copyright (c) 2017-present PanJiaChen

+ 91 - 0
ipfcst/ipfcst-console/README.md

@@ -0,0 +1,91 @@
+# vue-admin-template
+
+English | [简体中文](./README-zh.md)
+
+> A minimal vue admin template with Element UI & axios & iconfont & permission control & lint
+
+**Live demo:** http://panjiachen.github.io/vue-admin-template
+
+
+**The current version is `v4.0+` build on `vue-cli`. If you want to use the old version , you can switch branch to [tag/3.11.0](https://github.com/PanJiaChen/vue-admin-template/tree/tag/3.11.0), it does not rely on `vue-cli`**
+
+## Build Setup
+
+
+```bash
+# clone the project
+git clone https://github.com/PanJiaChen/vue-admin-template.git
+
+# enter the project directory
+cd vue-admin-template
+
+# install dependency
+npm install
+
+# develop
+npm run dev
+```
+
+This will automatically open http://localhost:9528
+
+## Build
+
+```bash
+# build for test environment
+npm run build:stage
+
+# build for production environment
+npm run build:prod
+```
+
+## Advanced
+
+```bash
+# preview the release environment effect
+npm run preview
+
+# preview the release environment effect + static resource analysis
+npm run preview -- --report
+
+# code format check
+npm run lint
+
+# code format check and auto fix
+npm run lint -- --fix
+```
+
+Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
+
+## Demo
+
+![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
+
+## Extra
+
+If you want router permission && generate menu by user roles , you can use this branch [permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
+
+For `typescript` version, you can use [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
+
+## Related Project
+
+- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
+
+- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
+
+- [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
+
+- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
+
+## Browsers support
+
+Modern browsers and Internet Explorer 10+.
+
+| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
+| --------- | --------- | --------- | --------- |
+| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
+
+## License
+
+[MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
+
+Copyright (c) 2017-present PanJiaChen

+ 5 - 0
ipfcst/ipfcst-console/babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/app'
+  ]
+}

+ 35 - 0
ipfcst/ipfcst-console/build/index.js

@@ -0,0 +1,35 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const config = require('../vue.config.js')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+  const report = rawArgv.includes('--report')
+
+  run(`vue-cli-service build ${args}`)
+
+  const port = 9526
+  const publicPath = config.publicPath
+
+  var connect = require('connect')
+  var serveStatic = require('serve-static')
+  const app = connect()
+
+  app.use(
+    publicPath,
+    serveStatic('./dist', {
+      index: ['index.html', '/']
+    })
+  )
+
+  app.listen(port, function () {
+    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))
+    if (report) {
+      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))
+    }
+
+  })
+} else {
+  run(`vue-cli-service build ${args}`)
+}

+ 24 - 0
ipfcst/ipfcst-console/jest.config.js

@@ -0,0 +1,24 @@
+module.exports = {
+  moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
+  transform: {
+    '^.+\\.vue$': 'vue-jest',
+    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
+      'jest-transform-stub',
+    '^.+\\.jsx?$': 'babel-jest'
+  },
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1'
+  },
+  snapshotSerializers: ['jest-serializer-vue'],
+  testMatch: [
+    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
+  ],
+  collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
+  coverageDirectory: '<rootDir>/tests/unit/coverage',
+  // 'collectCoverage': true,
+  'coverageReporters': [
+    'lcov',
+    'text-summary'
+  ],
+  testURL: 'http://localhost/'
+}

+ 9 - 0
ipfcst/ipfcst-console/jsconfig.json

@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+    "baseUrl": "./",
+    "paths": {
+        "@/*": ["src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

+ 75 - 0
ipfcst/ipfcst-console/package.json

@@ -0,0 +1,75 @@
+{
+  "name": "vue-admin-template",
+  "version": "4.2.1",
+  "description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
+  "author": "Pan <panfree23@gmail.com>",
+  "license": "MIT",
+  "private": true,
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "build:prod": "vue-cli-service build",
+    "build:stage": "vue-cli-service build --mode staging",
+    "preview": "node build/index.js --preview",
+    "lint": "eslint --ext .js,.vue src",
+    "test:unit": "jest --clearCache && vue-cli-service test:unit",
+    "test:ci": "npm run lint && npm run test:unit",
+    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
+  },
+  "dependencies": {
+    "axios": "0.18.1",
+    "echarts": "^4.9.0",
+    "element-ui": "2.13.0",
+    "font-awesome": "^4.7.0",
+    "js-cookie": "2.2.0",
+    "moment": "^2.24.0",
+    "node-sass": "^6.0.1",
+    "normalize.css": "7.0.0",
+    "nprogress": "0.2.0",
+    "particles.js": "^2.0.0",
+    "path-to-regexp": "2.4.0",
+    "qrcodejs2": "0.0.2",
+    "sortablejs": "^1.10.2",
+    "vue": "2.6.10",
+    "vue-router": "3.0.6",
+    "vuedraggable": "^2.23.2",
+    "vuex": "3.1.0",
+    "vxe-table": "^2.9.18",
+    "xe-utils": "^2.7.5"
+  },
+  "devDependencies": {
+    "@babel/core": "7.0.0",
+    "@babel/register": "7.0.0",
+    "@vue/cli-plugin-babel": "3.6.0",
+    "@vue/cli-plugin-eslint": "^3.9.1",
+    "@vue/cli-plugin-unit-jest": "3.6.3",
+    "@vue/cli-service": "3.6.0",
+    "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "^9.5.1",
+    "babel-core": "7.0.0-bridge.0",
+    "babel-eslint": "10.0.1",
+    "babel-jest": "23.6.0",
+    "chalk": "2.4.2",
+    "connect": "3.6.6",
+    "eslint": "5.15.3",
+    "eslint-plugin-vue": "5.2.2",
+    "html-webpack-plugin": "3.2.0",
+    "mockjs": "1.0.1-beta3",
+    "pulldown": "^1.1.0",
+    "runjs": "^4.3.2",
+    "sass-loader": "^10.2.0",
+    "script-ext-html-webpack-plugin": "2.1.3",
+    "script-loader": "0.7.2",
+    "serve-static": "^1.13.2",
+    "svg-sprite-loader": "4.1.3",
+    "svgo": "1.2.2",
+    "vue-template-compiler": "2.6.10"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

+ 564 - 0
ipfcst/ipfcst-console/pom.xml

@@ -0,0 +1,564 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.jiayue.ipfcst</groupId>
+  <artifactId>ipfcst-console</artifactId>
+
+  <version>3.3.1.znzx</version>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <java.version>1.8</java.version>
+    <ipfcst.version>3.0</ipfcst.version>
+    <aviator.version>5.0.0</aviator.version>
+    <fastjson.version>1.2.60</fastjson.version>
+    <commons-dbcp2.version>2.5.0</commons-dbcp2.version>
+    <jasypt-boot.version>2.1.1</jasypt-boot.version>
+    <reflections.version>0.9.10</reflections.version>
+    <rxtx.version>18.12.07</rxtx.version>
+    <modbus4j.version>3.0.5</modbus4j.version>
+    <commons.fileupload.version>1.3</commons.fileupload.version>
+    <commons.net.version>3.3</commons.net.version>
+    <velocity.version>1.7</velocity.version>
+    <netty.version>4.1.22.Final</netty.version>
+    <jodatime.version>2.9.9</jodatime.version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>com.jiayue.ipfcst</groupId>
+        <artifactId>ipfcst</artifactId>
+        <version>3.0.0</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <!--配置文件处理器-->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-configuration-processor</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <!--jasypt配置文件加解密-->
+    <dependency>
+      <groupId>com.github.ulisesbocchio</groupId>
+      <artifactId>jasypt-spring-boot-starter</artifactId>
+      <version>${jasypt-boot.version}</version>
+    </dependency>
+    <!--Lombok-->
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!--测试依赖-->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>cn.hutool</groupId>
+      <artifactId>hutool-all</artifactId>
+      <version>5.3.5</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-mock</artifactId>
+      <version>2.0.8</version>
+    </dependency>
+    <dependency>
+      <groupId>com.jiayue.ipfcst</groupId>
+      <artifactId>ipfcst-common-data</artifactId>
+      <version>3.0.0</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.jiayue.ipfcst</groupId>
+      <artifactId>ipfcst-common-security</artifactId>
+      <version>3.0.0</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-dbcp2</artifactId>
+      <version>${commons-dbcp2.version}</version>
+    </dependency>
+    <!-- https://mvnrepository.com/artifact/com.googlecode.aviator/aviator -->
+    <dependency>
+      <groupId>com.googlecode.aviator</groupId>
+      <artifactId>aviator</artifactId>
+      <version>${aviator.version}</version>
+    </dependency>
+    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
+    <dependency>
+      <groupId>com.alibaba</groupId>
+      <artifactId>fastjson</artifactId>
+      <version>${fastjson.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.reflections</groupId>
+      <artifactId>reflections</artifactId>
+      <version>${reflections.version}</version>
+      <exclusions>
+        <exclusion>
+          <artifactId>javassist</artifactId>
+          <groupId>org.javassist</groupId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>net.sf.ehcache</groupId>
+      <artifactId>ehcache</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-websocket</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-undertow</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-fileupload</groupId>
+      <artifactId>commons-fileupload</artifactId>
+      <version>${commons.fileupload.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+      <version>${velocity.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-all</artifactId>
+      <version>${netty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>joda-time</groupId>
+      <artifactId>joda-time</artifactId>
+      <version>${jodatime.version}</version>
+    </dependency>
+    <!--ftp上传下载依赖包-->
+    <dependency>
+      <groupId>commons-net</groupId>
+      <artifactId>commons-net</artifactId>
+      <version>${commons.net.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-aop</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>net.sourceforge.jexcelapi</groupId>
+      <artifactId>jxl</artifactId>
+      <version>2.6.10</version>
+    </dependency>
+
+    <!-- SSH安全连接所使用的类库 -->
+    <dependency>
+      <groupId>com.jcraft</groupId>
+      <artifactId>jsch</artifactId>
+      <version>0.1.55</version>
+      <scope>compile</scope>
+      <optional>true</optional>
+    </dependency>
+    <!--hutool的poi包 https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
+    <dependency>
+      <groupId>org.apache.poi</groupId>
+      <artifactId>poi-ooxml</artifactId>
+      <version>3.17</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.zxing</groupId>
+      <artifactId>core</artifactId>
+      <version>3.3.3</version>
+    </dependency>
+
+
+    <!--
+    <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-csv</artifactId>
+        <version>1.7</version>
+    </dependency>
+    -->
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-compress</artifactId>
+      <version>1.5</version>
+    </dependency>
+
+
+
+  </dependencies>
+
+  <profiles>    <!--考虑到window 和linux环境 npm命令格式的问题,使用maven的profile实现动态指定命令-->
+    <profile>
+      <id>window</id>
+      <properties>
+        <npm>npm.cmd</npm>
+      </properties>
+
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+
+    </profile>
+    <profile>
+      <id>linux</id>
+      <properties>
+        <npm>npm</npm>
+      </properties>
+    </profile>
+  </profiles>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <excludes>
+          <exclude>*.keystore</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>false</filtering>
+        <includes>
+          <include>*.keystore</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.7.0</version>
+        <configuration>
+          <source>${java.version}</source>
+          <target>${java.version}</target>
+          <encoding>${project.build.sourceEncoding}</encoding>
+          <!--<compilerArgs>-->
+          <!--&lt;!&ndash; 过期的方法的警告&ndash;&gt;-->
+          <!--<arg>-Xlint:deprecation</arg>-->
+          <!--</compilerArgs>-->
+          <compilerArguments>
+            <!-- 是否输出所有的编译信息(包括类的加载等)-->
+            <!--<verbose />-->
+            <!-- 解决maven命令编译报错,因为rt.jar 和jce.jar在jre的lib下面,不在jdk的lib下面,
+            导致maven找不到(java7以后会出现这个问题),将这2个jar包拷贝到jdk的lib下面估计也好使-->
+            <bootclasspath>${java.home}\lib\rt.jar;${java.home}\lib\jce.jar</bootclasspath>
+          </compilerArguments>
+        </configuration>
+      </plugin>
+      <!--跳过test类-->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.21.0</version>
+        <configuration>
+          <skipTests>true</skipTests>
+        </configuration>
+      </plugin>
+      <!-- 插件maven-clean-plugin,用于在编译前,清除之前编译的文件、文件夹等,避免残留之前的内容 -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-clean-plugin</artifactId>
+        <version>3.1.0</version>
+        <configuration>
+          <filesets>
+            <fileset>
+              <!-- 前端资源目录,即:存放前端包目录-->
+              <directory>src/main/resources/static</directory>
+            </fileset>
+            <fileset>
+              <!-- Vue项目打包自动生成的dist目录 -->
+              <directory>${basedir}/dist</directory>
+            </fileset>
+          </filesets>
+        </configuration>
+      </plugin>
+
+      <!--////////////////////////////////////////////////////小包配置开始//////////////////////////////////////-->
+      <!--拷贝依赖jar文件到ipfcst-lib目录下-->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>2.10</version>
+        <executions>
+          <execution>
+            <id>copy-dependencies</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <excludeArtifactIds>ipfcst-common-data,ipfcst-common-security,ipfcst-common-core,protocol-iec104,protocol-modbus,protocol-cdt,protocol-core,protocol-all</excludeArtifactIds>
+              <outputDirectory>../syjy/ipfcstV3/ipfcst-lib</outputDirectory>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!--////////////////////////////////////////////////////小包配置结束//////////////////////////////////////-->
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.6.0</version>
+        <executions>
+          <execution>
+            <id>exec-npm-install</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+            <configuration>
+              <executable>${npm}</executable>
+              <arguments>
+                <argument>install</argument>
+              </arguments>
+              <workingDirectory>${basedir}/src/main/frontend</workingDirectory>
+            </configuration>
+          </execution>
+          <execution>
+            <id>exec-npm-run-build</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+            <configuration>
+              <executable>${npm}</executable>
+              <arguments>
+                <argument>run</argument>
+                <argument>build:prod</argument>
+              </arguments>
+              <workingDirectory>${basedir}/src/main/frontend</workingDirectory>
+            </configuration>
+          </execution>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>java</goal>
+            </goals>
+            <configuration>
+              <mainClass>com.jiayue.ipfcst.console.util.CreateConsoleSh</mainClass>
+              <arguments>
+                <argument>${project.version}</argument>
+              </arguments>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>3.1.0</version>
+        <configuration>
+          <nonFilteredFileExtensions>
+            <nonFilteredFileExtension>woff</nonFilteredFileExtension>
+            <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
+            <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
+            <nonFilteredFileExtension>eot</nonFilteredFileExtension>
+            <nonFilteredFileExtension>swf</nonFilteredFileExtension>
+            <nonFilteredFileExtension>ico</nonFilteredFileExtension>
+            <nonFilteredFileExtension>png</nonFilteredFileExtension>
+          </nonFilteredFileExtensions>
+          <includeEmptyDirs>true</includeEmptyDirs>
+        </configuration>
+        <executions>
+          <execution>
+            <id>copy config</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <resources>
+                <resource>
+                  <directory>src/main/resources</directory>
+                  <includes>
+                    <include>application.yml</include>
+                    <include>vms/**</include>
+                  </includes>
+                </resource>
+              </resources>
+              <outputDirectory>../syjy/ipfcstV3/produce/console/${project.version}</outputDirectory>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy static</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <!-- 复制前端打包文件到这里 -->
+              <outputDirectory>src/main/resources/static</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <!-- 从前端打包的目录dist进行指定文件、文件夹内容的复制-->
+                  <directory>${basedir}/dist</directory>
+                  <includes>
+                    <!-- 具体根据实际前端代码、及目录结构进行配置-->
+                    <include>assets/</include>
+                    <include>favicon.ico</include>
+                    <include>index.html</include>
+                  </includes>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy-start-console.sh</id>
+            <!-- here the phase you need -->
+            <phase>compile</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>../syjy/ipfcstV3/bin</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>target/classes</directory>
+                  <includes>
+                    <include>start-console.sh</include>
+                  </includes>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.6</version>
+        <configuration>
+
+
+          <excludes>
+            <exclude>application.yml</exclude>
+            <exclude>logback-console.xml</exclude>
+          </excludes>
+          <!--////////////////////////////////////////////////////小包配置开始//////////////////////////////////////-->
+          <archive>
+            <manifest>
+              <addClasspath>true</addClasspath>
+              <classpathPrefix>/home/syjy/ipfcstV3/ipfcst-lib/</classpathPrefix>
+              <mainClass>com.jiayue.ipfcst.ConsoleApplication</mainClass>
+            </manifest>
+            <!-- 用maven在MANIFEST.MF资料中的Class-Path中增加当前目录(.)  -->
+            <manifestEntries>
+              <Class-Path>/home/syjy/ipfcstV3/ipfcst-lib</Class-Path>
+            </manifestEntries>
+          </archive>
+          <!--////////////////////////////////////////////////////小包配置结束//////////////////////////////////////-->
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <version>2.1.7.RELEASE</version>
+        <configuration>
+          <outputDirectory>../syjy/ipfcstV3/produce/console/${project.version}</outputDirectory>
+          <!--////////////////////////////////////////////////////小包配置开始//////////////////////////////////////-->
+          <includes>
+            <include>
+              <groupId>nothing</groupId>
+              <artifactId>nothing</artifactId>
+            </include>
+            <include>
+              <groupId>com.jiayue.ipfcst</groupId>
+              <artifactId>ipfcst-common-data</artifactId>
+            </include>
+            <include>
+              <groupId>com.jiayue.ipfcst</groupId>
+              <artifactId>ipfcst-common-security</artifactId>
+            </include>
+            <include>
+              <groupId>com.jiayue.ipfcst</groupId>
+              <artifactId>ipfcst-common-core</artifactId>
+            </include>
+            <include>
+              <groupId>wei.yigulu</groupId>
+              <artifactId>protocol-iec104</artifactId>
+            </include>
+            <include>
+              <groupId>wei.yigulu</groupId>
+              <artifactId>protocol-modbus</artifactId>
+            </include>
+            <include>
+              <groupId>wei.yigulu</groupId>
+              <artifactId>protocol-cdt</artifactId>
+            </include>
+            <include>
+              <groupId>wei.yigulu</groupId>
+              <artifactId>protocol-core</artifactId>
+            </include>
+            <include>
+              <groupId>wei.yigulu</groupId>
+              <artifactId>protocol-all</artifactId>
+            </include>
+
+          </includes>
+          <!--////////////////////////////////////////////////////小包配置结束//////////////////////////////////////-->
+          <!--控制是否启用支持Linux下service方式运行-->
+          <executable>false</executable>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>build-info</goal>
+              <goal>repackage</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.2.1</version>
+        <configuration>
+          <!-- 定义压缩包存放位置 -->
+          <outputDirectory>../syjy/ipfcstV3/produce/console/${project.version}</outputDirectory>
+          <!-- 定义压缩包文件名 -->
+          <finalName>${project.artifactId}-${project.version}</finalName>
+          <appendAssemblyId>false</appendAssemblyId>
+          <descriptors>
+            <descriptor>src/main/resources/assembly_base.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <id>make-assembly</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

+ 8 - 0
ipfcst/ipfcst-console/postcss.config.js

@@ -0,0 +1,8 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  'plugins': {
+    // to edit target browsers: use "browserslist" field in package.json
+    'autoprefixer': {}
+  }
+}

BIN
ipfcst/ipfcst-console/public/favicon.ico


+ 17 - 0
ipfcst/ipfcst-console/public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= webpackConfig.name %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 11 - 0
ipfcst/ipfcst-console/src/main/frontend/App.vue

@@ -0,0 +1,11 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'App'
+}
+</script>

+ 24 - 0
ipfcst/ipfcst-console/src/main/frontend/api/user.js

@@ -0,0 +1,24 @@
+import request from '@/utils/request'
+
+export function login(data) {
+  return request({
+    url: '/vue-admin-template/user/login',
+    method: 'post',
+    data
+  })
+}
+
+export function getInfo(token) {
+  return request({
+    url: '/vue-admin-template/user/info',
+    method: 'get',
+    params: { token }
+  })
+}
+
+export function logout() {
+  return request({
+    url: '/vue-admin-template/user/logout',
+    method: 'post'
+  })
+}

BIN
ipfcst/ipfcst-console/src/main/frontend/assets/404_images/404.png


BIN
ipfcst/ipfcst-console/src/main/frontend/assets/404_images/404_cloud.png


BIN
ipfcst/ipfcst-console/src/main/frontend/assets/img1.jpg


BIN
ipfcst/ipfcst-console/src/main/frontend/assets/login-null.jpg


BIN
ipfcst/ipfcst-console/src/main/frontend/assets/login.png


+ 105 - 0
ipfcst/ipfcst-console/src/main/frontend/assets/particles.json

@@ -0,0 +1,105 @@
+{
+  "particles": {
+    "number": {
+      "value": 60,
+      "density": {
+        "enable": true,
+        "value_area": 800
+      }
+    },
+    "color": {
+      "value": ["#344d7b","#7A378B","#551A8B"]
+    },
+    "shape": {
+      "type": "circle",
+      "stroke": {
+        "width": 3,
+        "color": "#fff"
+      },
+      "polygon": {
+        "nb_sides": 5
+      }
+    },
+    "opacity": {
+      "value": 1,
+      "random": false,
+      "anim": {
+        "enable": false,
+        "speed": 1,
+        "opacity_min": 0.1,
+        "sync": false
+      }
+    },
+    "size": {
+      "value": 3,
+      "random": true,
+      "anim": {
+        "enable": false,
+        "speed": 40,
+        "size_min": 0.1,
+        "sync": false
+      }
+    },
+    "line_linked": {
+      "enable": true,
+      "distance": 150,
+      "color": "#4381bd",
+      "opacity": 0.6,
+      "width": 2
+    },
+    "move": {
+      "enable": true,
+      "speed": 4,
+      "direction": "none",
+      "random": false,
+      "straight": false,
+      "out_mode": "out",
+      "bounce": false,
+      "attract": {
+        "enable": false,
+        "rotateX": 100,
+        "rotateY": 1200
+      }
+    }
+  },
+  "interactivity": {
+    "detect_on": "Window",
+    "events": {
+      "onhover": {
+        "enable": true,
+        "mode": "grab"
+      },
+      "onclick": {
+        "enable": true,
+        "mode": "push"
+      },
+      "resize": true
+    },
+    "modes": {
+      "grab": {
+        "distance": 140,
+        "line_linked": {
+          "opacity": 1
+        }
+      },
+      "bubble": {
+        "distance": 400,
+        "size": 40,
+        "duration": 2,
+        "opacity": 8,
+        "speed": 3
+      },
+      "repulse": {
+        "distance": 200,
+        "duration": 0.4
+      },
+      "push": {
+        "particles_nb": 4
+      },
+      "remove": {
+        "particles_nb": 2
+      }
+    }
+  },
+  "retina_detect": true
+}

+ 78 - 0
ipfcst/ipfcst-console/src/main/frontend/components/Breadcrumb/index.vue

@@ -0,0 +1,78 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb" separator="/">
+    <transition-group name="breadcrumb">
+      <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
+        <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
+        <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+import pathToRegexp from 'path-to-regexp'
+
+export default {
+  data() {
+    return {
+      levelList: null
+    }
+  },
+  watch: {
+    $route() {
+      this.getBreadcrumb()
+    }
+  },
+  created() {
+    this.getBreadcrumb()
+  },
+  methods: {
+    getBreadcrumb() {
+      // only show routes with meta.title
+      let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
+      const first = matched[0]
+
+      if (!this.isDashboard(first)) {
+        matched = [{ path: '/dashboard', meta: { title: '首页' }}].concat(matched)
+      }
+
+      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
+    },
+    isDashboard(route) {
+      const name = route && route.name
+      if (!name) {
+        return false
+      }
+      return name.trim().toLocaleLowerCase() === '首页'.toLocaleLowerCase()
+    },
+    pathCompile(path) {
+      // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
+      const { params } = this.$route
+      var toPath = pathToRegexp.compile(path)
+      return toPath(params)
+    },
+    handleLink(item) {
+      const { redirect, path } = item
+      if (redirect) {
+        this.$router.push(redirect)
+        return
+      }
+      this.$router.push(this.pathCompile(path))
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 50px;
+  margin-left: 8px;
+
+  .no-redirect {
+    color: #97a8be;
+    cursor: text;
+  }
+}
+</style>

+ 44 - 0
ipfcst/ipfcst-console/src/main/frontend/components/Hamburger/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div style="padding: 0 15px;" @click="toggleClick">
+    <svg
+      :class="{'is-active':isActive}"
+      class="hamburger"
+      viewBox="0 0 1024 1024"
+      xmlns="http://www.w3.org/2000/svg"
+      width="64"
+      height="64"
+    >
+      <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
+    </svg>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    toggleClick() {
+      this.$emit('toggleClick')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.hamburger {
+  display: inline-block;
+  vertical-align: middle;
+  width: 20px;
+  height: 20px;
+}
+
+.hamburger.is-active {
+  transform: rotate(180deg);
+}
+</style>

+ 62 - 0
ipfcst/ipfcst-console/src/main/frontend/components/SvgIcon/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :href="iconName" />
+  </svg>
+</template>
+
+<script>
+// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
+import { isExternal } from '@/utils/validate'
+
+export default {
+  name: 'SvgIcon',
+  props: {
+    iconClass: {
+      type: String,
+      required: true
+    },
+    className: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    isExternal() {
+      return isExternal(this.iconClass)
+    },
+    iconName() {
+      return `#icon-${this.iconClass}`
+    },
+    svgClass() {
+      if (this.className) {
+        return 'svg-icon ' + this.className
+      } else {
+        return 'svg-icon'
+      }
+    },
+    styleExternalIcon() {
+      return {
+        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
+        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.svg-icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+
+.svg-external-icon {
+  background-color: currentColor;
+  mask-size: cover!important;
+  display: inline-block;
+}
+</style>

+ 9 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/index.js

@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import SvgIcon from '@/components/SvgIcon'// svg component
+
+// register globally
+Vue.component('svg-icon', SvgIcon)
+
+const req = require.context('./svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+requireAll(req)

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/dashboard.svg


+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/example.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/eye-open.svg

@@ -0,0 +1 @@
+<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/eye.svg

@@ -0,0 +1 @@
+<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/form.svg


+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/link.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/nested.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/password.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/system.svg

@@ -0,0 +1 @@
+<svg t="1586766453729" class="icon" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2666" width="200" height="200"><path d="M1080.096 434.5c-4.216-23.731-26.924-47.945-50.596-53.185l-17.648-4.096a175.94 175.94 0 0 1-101.613-80.832 177.807 177.807 0 0 1-18.732-129.802l5.541-16.685c7.108-23.13-2.108-54.992-20.6-70.833 0 0-16.624-14.095-63.244-41.2-46.8-26.984-66.858-34.513-66.858-34.513-22.768-8.373-54.632-0.362-71.256 17.407l-12.287 13.251a173.47 173.47 0 0 1-120.466 48.066 174.133 174.133 0 0 1-121.008-48.487l-11.745-12.83C393.14 2.992 361.096-4.899 338.268 3.354c0 0-20.359 7.529-67.1 34.513-46.8 27.346-63.244 41.44-63.244 41.44-18.431 15.661-27.647 47.223-20.54 70.593l5.06 16.866a178.048 178.048 0 0 1-18.672 129.62A174.916 174.916 0 0 1 71.496 377.46l-17.045 3.855c-23.31 5.421-46.26 29.334-50.596 53.186 0 0-3.855 21.382-3.855 75.712s3.855 75.713 3.855 75.713C8.07 609.9 30.779 633.872 54.45 639.112l16.624 3.855A174.254 174.254 0 0 1 173.47 724.28c23.31 40.838 28.911 87.338 18.732 129.802l-4.818 16.444c-7.108 23.129 2.108 54.992 20.6 70.833 0 0 16.623 14.095 63.244 41.2 46.8 27.105 66.918 34.513 66.918 34.513 22.708 8.373 54.632 0.362 71.256-17.407l11.625-12.589a175.097 175.097 0 0 1 242.257 0.12l11.624 12.65c16.384 17.708 48.428 25.599 71.256 17.347 0 0 20.359-7.53 67.16-34.514 46.74-27.105 63.124-41.2 63.124-41.2 18.491-15.6 27.707-47.463 20.6-70.833l-5.06-17.106A176.723 176.723 0 0 1 910.66 724.4a176.06 176.06 0 0 1 102.396-81.314l16.684-3.855c23.31-5.42 46.26-29.333 50.596-53.185 0 0 3.855-21.383 3.855-75.713-0.241-54.33-4.096-75.833-4.096-75.833z m-537.82 293.335c-119.26 0-216.175-97.336-216.175-217.622a216.658 216.658 0 0 1 216.236-217.32c119.2 0 216.115 97.276 216.115 217.561-0.24 120.045-96.974 217.32-216.175 217.32z" p-id="2667" fill="#e6e6e6"></path></svg>

+ 2 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/table.svg

@@ -0,0 +1,2 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg">
+<path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/tree.svg

@@ -0,0 +1 @@
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg>

+ 1 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svg/user.svg

@@ -0,0 +1 @@
+<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>

+ 22 - 0
ipfcst/ipfcst-console/src/main/frontend/icons/svgo.yml

@@ -0,0 +1,22 @@
+# replace default config
+
+# multipass: true
+# full: true
+
+plugins:
+
+  # - name
+  #
+  # or:
+  # - name: false
+  # - name: true
+  #
+  # or:
+  # - name:
+  #     param1: 1
+  #     param2: 2
+
+- removeAttrs:
+    attrs:
+      - 'fill'
+      - 'fill-rule'

+ 40 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/AppMain.vue

@@ -0,0 +1,40 @@
+<template>
+  <section class="app-main">
+    <transition name="fade-transform" mode="out-in">
+      <router-view :key="key" />
+    </transition>
+  </section>
+</template>
+
+<script>
+export default {
+  name: 'AppMain',
+  computed: {
+    key() {
+      return this.$route.path
+    }
+  }
+}
+</script>
+
+<style scoped>
+.app-main {
+  /*50 = navbar  */
+  min-height: calc(100vh - 50px);
+  width: 100%;
+  position: relative;
+  overflow: hidden;
+}
+.fixed-header+.app-main {
+  padding-top: 50px;
+}
+</style>
+
+<style lang="scss">
+// fix css style bug in open el-dialog
+.el-popup-parent--hidden {
+  .fixed-header {
+    padding-right: 15px;
+  }
+}
+</style>

+ 159 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Navbar.vue

@@ -0,0 +1,159 @@
+<template>
+  <div class="navbar">
+    <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
+
+    <breadcrumb class="breadcrumb-container" />
+
+    <div class="right-menu">
+      <el-dropdown class="avatar-container" trigger="click">
+        <div class="avatar-wrapper">
+          <img src="@/assets/img1.jpg" class="user-avatar">
+          <i class="el-icon-caret-bottom" />
+        </div>
+        <el-dropdown-menu slot="dropdown" class="user-dropdown">
+          <!--<router-link to="/">-->
+            <el-dropdown-item @click.native='jumpReport'>
+              统计查询
+            </el-dropdown-item>
+
+          <el-dropdown-item divided @click.native="logout">
+            <span style="display:block;">退出系统</span>
+          </el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {mapGetters} from 'vuex'
+  import Breadcrumb from '@/components/Breadcrumb'
+  import Hamburger from '@/components/Hamburger'
+  import {getBrowserToken} from '@/utils/commonFuc' // get token from cookie
+  export default {
+  components: {
+    Breadcrumb,
+    Hamburger
+  },
+  computed: {
+    ...mapGetters([
+      'sidebar',
+      'avatar'
+    ])
+  },
+  methods: {
+    toggleSideBar() {
+      this.$store.dispatch('app/toggleSideBar')
+    },
+    async logout() {
+      const tok = getBrowserToken();
+      document.cookie = 'token='+tok+';expires=' + new Date(0).toUTCString()
+      //注销返回自己的登录页
+      // this.$router.push(`/login?redirect=${this.$route.fullPath}`)
+      sessionStorage.clear()
+      var href = window.location.href
+      window.location.href='https://'+href.split("//")[1].split(":")[0]+':9000'
+    },
+    // 查询
+    jumpReport(){
+      //页面url通常为http://localhost:8090/XXXX/images/1_15.png
+      var href = window.location.href
+      this.$axios.get('/sysParameter/sysParameterKey/REPORT_THEME').then(res => {
+        if(res.data.sysValue === '0'){
+          //蓝色
+          window.location.href='https://'+href.split("//")[1].split(":")[0]+':9000'
+          // window.location.href='http://localhost:9528'
+        }else{
+          //白色
+          window.location.href='https://'+href.split("//")[1].split(":")[0]+':9000/#/dashboard'
+          // window.location.href='http://localhost:9528/#/dashboard'
+        }
+      })
+    },
+    jumpMonitor(){
+      //页面url通常为http://localhost:8090/XXXX/images/1_15.png
+      var href = window.location.href
+      window.open('https://'+href.split("//")[1].split(":")[0]+':8088')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.navbar {
+  height: 50px;
+  overflow: hidden;
+  position: relative;
+  background: #fff;
+  box-shadow: 0 1px 4px rgba(0,21,41,.08);
+
+  .hamburger-container {
+    line-height: 46px;
+    height: 100%;
+    float: left;
+    cursor: pointer;
+    transition: background .3s;
+    -webkit-tap-highlight-color:transparent;
+
+    &:hover {
+      background: rgba(0, 0, 0, .025)
+    }
+  }
+
+  .breadcrumb-container {
+    float: left;
+  }
+
+  .right-menu {
+    float: right;
+    height: 100%;
+    line-height: 50px;
+
+    &:focus {
+      outline: none;
+    }
+
+    .right-menu-item {
+      display: inline-block;
+      padding: 0 8px;
+      height: 100%;
+      font-size: 18px;
+      color: #5a5e66;
+      vertical-align: text-bottom;
+
+      &.hover-effect {
+        cursor: pointer;
+        transition: background .3s;
+
+        &:hover {
+          background: rgba(0, 0, 0, .025)
+        }
+      }
+    }
+
+    .avatar-container {
+      margin-right: 30px;
+
+      .avatar-wrapper {
+        margin-top: 5px;
+        position: relative;
+
+        .user-avatar {
+          cursor: pointer;
+          width: 40px;
+          height: 40px;
+          border-radius: 10px;
+        }
+
+        .el-icon-caret-bottom {
+          cursor: pointer;
+          position: absolute;
+          right: -20px;
+          top: 25px;
+          font-size: 12px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 26 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/FixiOSBug.js

@@ -0,0 +1,26 @@
+export default {
+  computed: {
+    device() {
+      return this.$store.state.app.device
+    }
+  },
+  mounted() {
+    // In order to fix the click on menu on the ios device will trigger the mouseleave bug
+    // https://github.com/PanJiaChen/vue-element-admin/issues/1135
+    this.fixBugIniOS()
+  },
+  methods: {
+    fixBugIniOS() {
+      const $subMenu = this.$refs.subMenu
+      if ($subMenu) {
+        const handleMouseleave = $subMenu.handleMouseleave
+        $subMenu.handleMouseleave = (e) => {
+          if (this.device === 'mobile') {
+            return
+          }
+          handleMouseleave(e)
+        }
+      }
+    }
+  }
+}

+ 29 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/Item.vue

@@ -0,0 +1,29 @@
+<script>
+export default {
+  name: 'MenuItem',
+  functional: true,
+  props: {
+    icon: {
+      type: String,
+      default: ''
+    },
+    title: {
+      type: String,
+      default: ''
+    }
+  },
+  render(h, context) {
+    const { icon, title } = context.props
+    const vnodes = []
+
+    if (icon) {
+      vnodes.push(<svg-icon icon-class={icon}/>)
+    }
+
+    if (title) {
+      vnodes.push(<span slot='title'>{(title)}</span>)
+    }
+    return vnodes
+  }
+}
+</script>

+ 36 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/Link.vue

@@ -0,0 +1,36 @@
+
+<template>
+  <!-- eslint-disable vue/require-component-is -->
+  <component v-bind="linkProps(to)">
+    <slot />
+  </component>
+</template>
+
+<script>
+import { isExternal } from '@/utils/validate'
+
+export default {
+  props: {
+    to: {
+      type: String,
+      required: true
+    }
+  },
+  methods: {
+    linkProps(url) {
+      if (isExternal(url)) {
+        return {
+          is: 'a',
+          href: url,
+          target: '_blank',
+          rel: 'noopener'
+        }
+      }
+      return {
+        is: 'router-link',
+        to: url
+      }
+    }
+  }
+}
+</script>

+ 82 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/Logo.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="sidebar-logo-container" :class="{'collapse':collapse}">
+    <transition name="sidebarLogoFade">
+      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
+        <img v-if="logo" :src="logo" class="sidebar-logo">
+        <h1 v-else class="sidebar-title">{{ title }} </h1>
+      </router-link>
+      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
+        <img v-if="logo" :src="logo" class="sidebar-logo">
+        <h1 class="sidebar-title">{{ title }} </h1>
+      </router-link>
+    </transition>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'SidebarLogo',
+  props: {
+    collapse: {
+      type: Boolean,
+      required: true
+    }
+  },
+  data() {
+    return {
+      title: 'Vue Admin Template',
+      logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebarLogoFade-enter-active {
+  transition: opacity 1.5s;
+}
+
+.sidebarLogoFade-enter,
+.sidebarLogoFade-leave-to {
+  opacity: 0;
+}
+
+.sidebar-logo-container {
+  position: relative;
+  width: 100%;
+  height: 50px;
+  line-height: 50px;
+  background: #2b2f3a;
+  text-align: center;
+  overflow: hidden;
+
+  & .sidebar-logo-link {
+    height: 100%;
+    width: 100%;
+
+    & .sidebar-logo {
+      width: 32px;
+      height: 32px;
+      vertical-align: middle;
+      margin-right: 12px;
+    }
+
+    & .sidebar-title {
+      display: inline-block;
+      margin: 0;
+      color: #fff;
+      font-weight: 600;
+      line-height: 50px;
+      font-size: 14px;
+      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
+      vertical-align: middle;
+    }
+  }
+
+  &.collapse {
+    .sidebar-logo {
+      margin-right: 0px;
+    }
+  }
+}
+</style>

+ 95 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/SidebarItem.vue

@@ -0,0 +1,95 @@
+<template>
+  <div v-if="!item.hidden">
+    <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
+      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
+        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
+          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
+        </el-menu-item>
+      </app-link>
+    </template>
+
+    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
+      <template slot="title">
+        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
+      </template>
+      <sidebar-item
+        v-for="child in item.children"
+        :key="child.path"
+        :is-nest="true"
+        :item="child"
+        :base-path="resolvePath(child.path)"
+        class="nest-menu"
+      />
+    </el-submenu>
+  </div>
+</template>
+
+<script>
+import path from 'path'
+import { isExternal } from '@/utils/validate'
+import Item from './Item'
+import AppLink from './Link'
+import FixiOSBug from './FixiOSBug'
+
+export default {
+  name: 'SidebarItem',
+  components: { Item, AppLink },
+  mixins: [FixiOSBug],
+  props: {
+    // route object
+    item: {
+      type: Object,
+      required: true
+    },
+    isNest: {
+      type: Boolean,
+      default: false
+    },
+    basePath: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
+    // TODO: refactor with render function
+    this.onlyOneChild = null
+    return {}
+  },
+  methods: {
+    hasOneShowingChild(children = [], parent) {
+      const showingChildren = children.filter(item => {
+        if (item.hidden) {
+          return false
+        } else {
+          // Temp set(will be used if only has one showing child)
+          this.onlyOneChild = item
+          return true
+        }
+      })
+
+      // When there is only one child router, the child router is displayed by default
+      if (showingChildren.length === 1) {
+        return true
+      }
+
+      // Show parent if there are no child router to display
+      if (showingChildren.length === 0) {
+        this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
+        return true
+      }
+
+      return false
+    },
+    resolvePath(routePath) {
+      if (isExternal(routePath)) {
+        return routePath
+      }
+      if (isExternal(this.basePath)) {
+        return this.basePath
+      }
+      return path.resolve(this.basePath, routePath)
+    }
+  }
+}
+</script>

+ 266 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/Sidebar/index.vue

@@ -0,0 +1,266 @@
+<template>
+  <div class="elmenu" :class="{'has-logo':showLogo}">
+    <logo v-if="showLogo" :collapse="isCollapse"/>
+    <el-scrollbar wrap-class="scrollbar-wrapper">
+
+      <el-menu
+        :default-active="activeMenu"
+        :collapse="isCollapse"
+        :background-color="variables.menuBg"
+        :text-color="variables.menuText"
+        :unique-opened="false"
+        :active-text-color="variables.menuActiveText"
+        :collapse-transition="false"
+        mode="vertical"
+      >
+        <sidebar-item v-for="route in allRoute" :key="route.path" :item="route" :base-path="route.path"/>
+      </el-menu>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script>
+  import {mapGetters} from 'vuex'
+  import Logo from './Logo'
+  import SidebarItem from './SidebarItem'
+  import variables from '@/styles/variables.scss'
+  export default {
+    components: {SidebarItem, Logo},
+    data() {
+      return {
+        electricField: {},
+        allRoute: {},
+        fileUploadNodeShowSysValue: '1',
+        menuList: []
+      }
+    },
+    created() {
+      this.getParameterBySysKey()
+    },
+    destoryed() {
+      this.allRoute = {}
+    },
+    mounted(){
+      this.getCookie()
+    },
+    methods: {
+      getCookie() {
+        const strCookie = document.cookie
+        var arrCookie=strCookie.split(";");
+        var user;
+        //遍历cookie数组,处理每个cookie对
+        for(var i=0;i<arrCookie.length;i++){
+          var arr=arrCookie[i].split("=");
+          //找到名称为user的cookie,并返回它的值
+          if("user"==arr[0].trim()){
+            user=arr[1];
+            sessionStorage.setItem('user', user)
+            this.getMenu()
+            break;
+          }
+        }
+      },
+      getParameterBySysKey() {
+        this.$axios.get('/sysParameter/sysParameterKey/FILE_UPLOAD_NODE_SHOW').then(res => {
+          this.fileUploadNodeShowSysValue = res.data.sysValue
+          this.routes()
+        })
+      }
+      ,
+      getMenu() {
+        this.$axios.get('/sysMenu/getMenufindByName/' + sessionStorage.getItem('user') + '/9001').then(res => {
+          console.log(res.data)
+          this.menuList = res.data
+        }).catch(e => {
+          this.$message.error('查询菜单异常:' + e)
+        })
+      }
+      ,
+      routes() {
+        var routesResult = []
+        var routes = this.$router.options.routes
+        const user = sessionStorage.getItem('user')
+        this.$axios.get('/electricField/').then((res) => {
+          this.electricField = res.data
+          var sign = 'windPower'
+          if (this.electricField.electricFieldTypeEnum == 'E2') {
+            sign = 'photovoltaic'
+          }
+          for (var i = 0; i < routes.length; i++) {
+
+            if (routes[i].children != undefined) {
+
+              var routesR = []
+              var routeChildren = routes[i].children
+              for (var p = 0; p < routeChildren.length; p++) {
+
+                // if (routeChildren[p].sign != sign) {
+                //   if (routeChildren[p].path != 'systemuser') {
+                //     if (routeChildren[p].path == 'powerPlanSD') {
+                //       if (this.electricField.provinceEnum == 'E37') {
+                //         routesR.push(routeChildren[p])
+                //       }
+                //     } else {
+                //       if (routeChildren[p].path == 'feedbackFile') {
+                //         if (this.electricField.provinceEnum == 'E46' || this.electricField.provinceEnum == 'E52') {
+                //           routesR.push(routeChildren[p])
+                //         }
+                //       } else {
+                //         if (routeChildren[p].meta.title == '上传上报文件') {
+                //           if (this.fileUploadNodeShowSysValue == '1') {
+                //             routesR.push(routeChildren[p])
+                //           }
+                //         } else {
+                //           routesR.push(routeChildren[p])
+                //         }
+                //       }
+                //     }
+                //   }
+                //
+                //   if (routeChildren[p].sign == 'admin') {
+                //     routesR.push(routeChildren[p])
+                //   }
+                //
+                //
+                // }
+                routesR.push(routeChildren[p])
+              }
+
+              if (routesR.length > 0) {
+                routes[i].children = routesR
+                routesResult.push(routes[i])
+              }
+            } else {
+
+              // if (routes[i].sign != 'photovoltaic') {
+                routesResult.push(routes[i])
+              // }
+
+            }
+          }
+
+          if (this.menuList.length === 0) {
+            this.allRoute = routesResult
+          } else {
+            this.allRoute = this.filterMenu(routesResult)
+          }
+          // this.allRoute = routesResult
+          // console.log(this.allRoute)
+
+        }).catch((error) => {
+          this.$message.error('获取场站类型动态路由失败' + error)
+          this.allRoute = routes
+        })
+
+      }
+      ,
+      filterMenu(router) {
+        var routerList = []
+        routerList.push(router[0])
+        routerList.push(router[1])
+        for (var i = 0; i < this.menuList.length; i++) {
+          for (var j = 0; j < router.length; j++) {
+            if (this.menuList[i].itemPath == router[j].path && this.menuList[i].children[0].title == router[j].children[0].name) {
+              routerList.push(router[j])
+            }
+
+            if (this.menuList[i].itemPath == 'ftpSftpTools' && router[j].children != undefined && router[j].children[0].path == 'ftpSftpTools') {
+              routerList.push(router[j])
+            }
+            if (this.menuList[i].itemPath == router[j].name && router[j].name != undefined) {
+              if (router[j].children != undefined && router[j].children.length > 0 && this.menuList[i].children.length > 0) {
+                router[j].children = this.filterChildrenMenu(router[j].children, this.menuList[i].children)
+                routerList.push(router[j])
+              } else {
+                routerList.push(router[j])
+              }
+            }
+          }
+        }
+        return routerList
+      }
+      ,
+      filterChildrenMenu(router, menuList) {
+        var childrenList = []
+        for (var i = 0; i < router.length; i++) {
+          for (var j = 0; j < menuList.length; j++) {
+            if (router[i].path == menuList[j].itemPath) {
+              childrenList.push(router[i])
+            }
+          }
+        }
+        return childrenList;
+      }
+      ,
+    },
+
+    computed: {
+      ...
+        mapGetters([
+          'sidebar'
+        ]),
+      /*routes() {
+        var routes = this.$router.options.routes
+        this.$axios.get('/electricField/').then((res) => {
+          this.electricField = res.data
+
+          if(this.electricField.electricFieldTypeEnum == 'E1'){
+              for(var i = 0;i<routes.length;i++){
+                if(routes[i].path == '/realTimeQuery'){
+                  routes[i].children.splice(1,1)
+                }
+              }
+          }
+
+          if(this.electricField.electricFieldTypeEnum == 'E2'){
+            for(var i = 0;i<routes.length;i++){
+              if(routes[i].path == '/realTimeQuery'){
+                console.log(routes[i])
+                routes[i].children.splice(1,1)
+                console.log(routes[i])
+              }
+
+            }
+          }
+        }).catch((error) => {
+          this.$message.error('查询实时预测短期echarts出错' + error)
+
+        })
+        return routes
+      },*/
+      activeMenu() {
+        const route = this.$route
+        const {meta, path} = route
+        // if set path, the sidebar will highlight the path you set
+        if (meta.activeMenu) {
+          return meta.activeMenu
+        }
+        return path
+      }
+      ,
+      showLogo() {
+        return this.$store.state.settings.sidebarLogo
+      }
+      ,
+      variables() {
+        return variables
+      }
+      ,
+      isCollapse() {
+        if (!this.sidebar.opened) {
+          this.variables.menuBg = 'rgb(0,0,0)'
+        } else {
+          this.variables.menuBg = 'rgba(48, 65, 86,0)'
+        }
+        return !this.sidebar.opened
+      }
+    }
+  }
+</script>
+<style scoped>
+  /*.elmenu {*/
+  /*  background: url('../../img/pageBg.png');*/
+  /*  border: 1px solid #ffffff;*/
+  /*  height: 100%;*/
+  /*}*/
+</style>

+ 3 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/components/index.js

@@ -0,0 +1,3 @@
+export { default as Navbar } from './Navbar'
+export { default as Sidebar } from './Sidebar'
+export { default as AppMain } from './AppMain'

+ 93 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div :class="classObj" class="app-wrapper">
+    <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
+    <sidebar class="sidebar-container" />
+    <div class="main-container">
+      <div :class="{'fixed-header':fixedHeader}">
+        <navbar />
+      </div>
+      <app-main />
+    </div>
+  </div>
+</template>
+
+<script>
+import { Navbar, Sidebar, AppMain } from './components'
+import ResizeMixin from './mixin/ResizeHandler'
+
+export default {
+  name: 'Layout',
+  components: {
+    Navbar,
+    Sidebar,
+    AppMain
+  },
+  mixins: [ResizeMixin],
+  computed: {
+    sidebar() {
+      return this.$store.state.app.sidebar
+    },
+    device() {
+      return this.$store.state.app.device
+    },
+    fixedHeader() {
+      return this.$store.state.settings.fixedHeader
+    },
+    classObj() {
+      return {
+        hideSidebar: !this.sidebar.opened,
+        openSidebar: this.sidebar.opened,
+        withoutAnimation: this.sidebar.withoutAnimation,
+        mobile: this.device === 'mobile'
+      }
+    }
+  },
+  methods: {
+    handleClickOutside() {
+      this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  @import "~@/styles/mixin.scss";
+  @import "~@/styles/variables.scss";
+
+  .app-wrapper {
+    @include clearfix;
+    position: relative;
+    height: 100%;
+    width: 100%;
+    &.mobile.openSidebar{
+      position: fixed;
+      top: 0;
+    }
+  }
+  .drawer-bg {
+    background: #000;
+    opacity: 0.3;
+    width: 100%;
+    top: 0;
+    height: 100%;
+    position: absolute;
+    z-index: 999;
+  }
+
+  .fixed-header {
+    position: fixed;
+    top: 0;
+    right: 0;
+    z-index: 9;
+    width: calc(100% - #{$sideBarWidth});
+    transition: width 0.28s;
+  }
+
+  .hideSidebar .fixed-header {
+    width: calc(100% - 54px)
+  }
+
+  .mobile .fixed-header {
+    width: 100%;
+  }
+</style>

+ 45 - 0
ipfcst/ipfcst-console/src/main/frontend/layout/mixin/ResizeHandler.js

@@ -0,0 +1,45 @@
+import store from '@/store'
+
+const { body } = document
+const WIDTH = 992 // refer to Bootstrap's responsive design
+
+export default {
+  watch: {
+    $route(route) {
+      if (this.device === 'mobile' && this.sidebar.opened) {
+        store.dispatch('app/closeSideBar', { withoutAnimation: false })
+      }
+    }
+  },
+  beforeMount() {
+    window.addEventListener('resize', this.$_resizeHandler)
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.$_resizeHandler)
+  },
+  mounted() {
+    const isMobile = this.$_isMobile()
+    if (isMobile) {
+      store.dispatch('app/toggleDevice', 'mobile')
+      store.dispatch('app/closeSideBar', { withoutAnimation: true })
+    }
+  },
+  methods: {
+    // use $_ for mixins properties
+    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+    $_isMobile() {
+      const rect = body.getBoundingClientRect()
+      return rect.width - 1 < WIDTH
+    },
+    $_resizeHandler() {
+      if (!document.hidden) {
+        const isMobile = this.$_isMobile()
+        store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
+
+        if (isMobile) {
+          store.dispatch('app/closeSideBar', { withoutAnimation: true })
+        }
+      }
+    }
+  }
+}

+ 215 - 0
ipfcst/ipfcst-console/src/main/frontend/main.js

@@ -0,0 +1,215 @@
+import Vue from 'vue'
+
+import 'normalize.css/normalize.css' // A modern alternative to CSS resets
+import ElementUI, {Message, MessageBox} from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n
+import 'font-awesome/scss/font-awesome.scss'
+import '@/styles/index.scss' // global css
+import moment from 'moment'
+
+import App from './App'
+import store from './store'
+import router, {resetRouter} from './router'
+
+import echarts from 'echarts'
+import '@/icons' // icon
+import '@/permission' // permission control
+import axios from 'axios'
+import 'xe-utils'
+import VXETable from 'vxe-table'
+import 'vxe-table/lib/index.css'
+
+import {removeToken} from './utils/auth'
+import {getBrowserToken} from './utils/commonFuc' // get token from cookie
+Vue.prototype.$moment = moment
+Vue.prototype.$echarts = echarts
+Vue.use(VXETable)
+/**
+ * If you don't want to use mock-server
+ * you want to use MockJs for mock api
+ * you can execute: mockXHR()
+ *
+ * Currently MockJs will be used in the production environment,
+ * please remove it before going online ! ! !
+ */
+/* if (process.env.NODE_ENV === 'production') {
+  const { mockXHR } = require('../mock')
+  mockXHR()
+}*/
+
+// set ElementUI lang to EN
+Vue.use(ElementUI, {locale})
+// 如果想要中文版 element-ui,按如下方式声明
+// Vue.use(ElementUI)
+Vue.prototype.$fpath = require('path')
+Vue.config.productionTip = false
+Vue.prototype.$axios = axios.create({
+    baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
+    // withCredentials: true, // send cookies when cross-domain requests
+    timeout: 1000 * 60 * 10 // request timeout
+})
+VXETable.setup({
+  validArgs: 'obsolete' // 将自定义校验参数还原为 Function(rule, cellValue, callback)
+})
+
+Vue.prototype.$axios.interceptors.request.use(
+    config => {
+        // do something before request is sent
+        /*    if (store.getters.token) {
+          // let each request carry token
+          // ['X-Token'] is a custom headers key
+          // please modify it according to the actual situation
+          config.headers['Authorization'] = getToken()
+        }*/
+
+        if (getBrowserToken()) { // 判断是否存在token,如果存在的话,则每个http header都加上token
+            config.headers['Authorization'] = getBrowserToken()
+            sessionStorage.setItem('user', getBrowserUser())
+        }
+        return config
+    },
+    error => {
+        // do something with request error
+        console.log(error) // for debug
+        return Promise.reject(error)
+    }
+)
+
+
+function getBrowserUser() {
+    var user = "";
+    var ca = document.cookie.split(';');
+    for (var i = 0; i < ca.length; i++) {
+        var c = ca[i].trim();
+        if (c.indexOf("user=") == 0){
+            user = c.substring("user=".length, c.length);
+        }
+    }
+    return user
+}
+
+// response interceptor
+Vue.prototype.$axios.interceptors.response.use(
+    /**
+     * If you want to get http information such as headers or status
+     * Please return  response => response
+     */
+
+    /**
+     * Determine the request status by custom code
+     * Here is just an example
+     * You can also judge the status by HTTP Status Code
+     */
+    response => {
+        // 处理下载文件
+        if (response.headers && response.headers['content-type'] && (response.headers['content-type'].indexOf('application/x-msdownload') != -1)) {
+            // 创建一个blob对象,file的一种
+            const blob = new Blob([response.data], {type: response.headers['content-type']})
+            const fileName = decodeURI(response.headers['content-disposition'].split('=')[1])
+            if (window.navigator.msSaveOrOpenBlob) {
+                // 兼容IE10
+                navigator.msSaveBlob(blob, fileName)
+            } else {
+                // 非IE下载
+                const elink = document.createElement('a')
+                elink.download = fileName
+                elink.style.display = 'none'
+                elink.href = URL.createObjectURL(blob)
+                document.body.appendChild(elink)
+                elink.click()
+                URL.revokeObjectURL(elink.href) // 释放URL 对象
+                document.body.removeChild(elink)
+            }
+
+            response.data = ''
+            response.headers['content-type'] = 'text/json'
+            return response
+        } else {
+            const res = response.data
+            // if the custom code is not 20000, it is judged as an error.
+            //console.log(res.code)
+            if (res.code !== 0) {
+                Message({
+                    message: res.message || 'Error',
+                    type: 'error',
+                    duration: 5 * 1000
+                })
+
+                // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
+                if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
+                    // to re-login
+                    MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
+                        confirmButtonText: 'Re-Login',
+                        cancelButtonText: 'Cancel',
+                        type: 'warning'
+                    }).then(() => {
+                        store.dispatch('user/resetToken').then(() => {
+                            location.reload()
+                        })
+                    })
+                }
+                return Promise.reject(new Error(res.message || 'Error'))
+            } else {
+                return res
+            }
+        }
+    },
+    error => {
+        if (error.response) {
+            switch (error.response.status) {
+                case 401:
+                    console.log('用户验证失败!')
+                    // 返回 401 清除token信息并跳转到登录页面
+                    removeToken()
+                    resetRouter()
+                    Message({
+                        message: error.response.data.data,
+                        type: 'error',
+                        duration: 5 * 1000
+                    })
+                    break
+                case 403:
+                    console.log('登录超时!')
+                    // 返回 401 清除token信息并跳转到登录页面
+                    removeToken()
+                    resetRouter()
+                    router.push('/login')
+                    Message({
+                        message: '登录超时',
+                        type: 'error',
+                        duration: 5 * 1000
+                    })
+                    break
+                case 500:
+                    Message({
+                        message: '服务器关闭了!请联系相关工作人员',
+                        type: 'error',
+                        duration: 5 * 1000
+                    })
+                    removeToken()
+                    resetRouter()
+                    router.push('/login')
+                    break
+                case 504:
+                    console.log('服务器关闭了!')
+                    resetRouter()
+                    break
+            }
+        }
+        /*    console.log('err' + error) // for debug
+        Message({
+          message: error.message,
+          type: 'error',
+          duration: 5 * 1000
+        })*/
+        return Promise.reject(error)
+    }
+)
+
+new Vue({
+    el: '#app',
+    router,
+    store,
+    render: h => h(App)
+})

+ 62 - 0
ipfcst/ipfcst-console/src/main/frontend/permission.js

@@ -0,0 +1,62 @@
+import router from './router'
+/*import store from './store'
+import { Message } from 'element-ui'*/
+import NProgress from 'nprogress' // progress bar
+import 'nprogress/nprogress.css' // progress bar style
+import { getBrowserToken } from './utils/commonFuc' // get token from cookie
+import getPageTitle from '@/utils/get-page-title'
+
+NProgress.configure({ showSpinner: false }) // NProgress Configuration
+
+const whiteList = ['/login'] // no redirect whitelist
+
+router.beforeEach(async(to, from, next) => {
+  // start progress bar
+  NProgress.start()
+
+  // set page title
+  document.title = getPageTitle(to.meta.title)
+
+  if (getBrowserToken()) {
+    if (to.path === '/login') {
+      // if is logged in, redirect to the home page
+      next({ path: '/' })
+      NProgress.done()
+    } else {
+      next()
+    /*  const hasGetUserInfo = store.getters.name
+      if (hasGetUserInfo) {
+        next()
+      } else {
+        try {
+          // get user info
+          await store.dispatch('user/getInfo')
+
+          next()
+        } catch (error) {
+          // remove token and go to login page to re-login
+          await store.dispatch('user/resetToken')
+          Message.error(error || 'Has Error')
+          next(`/login?redirect=${to.path}`)
+          NProgress.done()
+        }
+      }*/
+    }
+  } else {
+    /* has no token*/
+
+    if (whiteList.indexOf(to.path) !== -1) {
+      // in the free login whitelist, go directly
+      next()
+    } else {
+      // other pages that do not have permission to access are redirected to the login page.
+      next(`/login?redirect=${to.path}`)
+      NProgress.done()
+    }
+  }
+})
+
+router.afterEach(() => {
+  // finish progress bar
+  NProgress.done()
+})

+ 99 - 0
ipfcst/ipfcst-console/src/main/frontend/router/index.js

@@ -0,0 +1,99 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+
+Vue.use(Router)
+
+/* Layout */
+import Layout from '@/layout'
+
+import consoleRouter from './modules/console'
+import systemRouter from './modules/system'
+import dataExchangeRouter from "./modules/dataexchange"
+import uploadRouter from './modules/uploadFile';
+
+/**
+ * Note: sub-menu only appear when route children.length >= 1
+ * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
+ *
+ * hidden: true                   if set true, item will not show in the sidebar(default is false)
+ * alwaysShow: true               if set true, will always show the root menu
+ *                                if not set alwaysShow, when item has more than one children route,
+ *                                it will becomes nested mode, otherwise not show the root menu
+ * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
+ * name:'router-name'             the name is used by <keep-alive> (must set!!!)
+ * meta : {
+    roles: ['admin','editor']    control the page roles (you can set multiple roles)
+    title: 'title'               the name show in sidebar and breadcrumb (recommend set)
+    icon: 'svg-name'             the icon show in the sidebar
+    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
+    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
+  }
+ */
+
+/**
+ * constantRoutes
+ * a base page that does not have permission requirements
+ * all roles can be accessed
+ */
+export const constantRoutes = [
+  {
+    path: '/login',
+    component: () => import('@/views/login/index'),
+    hidden: true,
+    sign: 'currency'
+  },
+
+  {
+    path: '/404',
+    component: () => import('@/views/404'),
+    hidden: true,
+    sign: 'currency'
+  },
+  {
+    path: '/',
+    component: Layout,
+    redirect: '/dashboard',
+    children: [{
+      path: 'dashboard',
+      name: '首页',
+      component: () => import('@/views/dashboard/index'),
+      meta: { title: '首页', icon: 'dashboard' },
+      sign: 'currency'
+    }]
+  },
+  consoleRouter,
+  systemRouter,
+  dataExchangeRouter,
+  uploadRouter,
+  {
+    path: '/',
+    component: Layout,
+    redirect: '/ftpSftpTools',
+    children: [{
+      path: 'ftpSftpTools',
+      name: 'FTP/SFTP工具',
+      component: () => import('@/views/ftpSftpTools/index'),
+      meta: { title: 'FTP/SFTP工具', icon: 'nested' },
+      sign: 'currency'
+    }],
+  },
+  // 404 page must be placed at the end !!!
+  { path: '*', redirect: '/404', hidden: true,
+    sign: 'currency' }
+]
+
+const createRouter = () => new Router({
+  // mode: 'history', // require service support
+  scrollBehavior: () => ({ y: 0 }),
+  routes: constantRoutes
+})
+
+const router = createRouter()
+
+// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
+export function resetRouter() {
+  const newRouter = createRouter()
+  router.matcher = newRouter.matcher // reset router
+}
+
+export default router

+ 185 - 0
ipfcst/ipfcst-console/src/main/frontend/router/modules/console.js

@@ -0,0 +1,185 @@
+/** When your routing table is too long, you can split it into small modules**/
+
+import Layout from '@/layout'
+
+const consoleRouter = {
+  path: '/console',
+  component: Layout,
+  redirect: '/console/electricFieldInfo',
+  name: 'console',
+  meta: {
+    title: '控制管理',
+    icon: 'table'
+  },
+  children: [
+    {
+      path: 'electricField',
+      component: () => import('@/views/console/electricField/index'),
+      name: 'electricField',
+      meta: { title: '场站信息', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'pvModuleModel',
+      component: () => import('@/views/console/pvModuleModel/index'),
+      name: 'pvModuleModel',
+      meta: { title: '光伏组件', noCache: true },
+      sign: 'photovoltaic'
+    },
+    {
+      path: 'inverterInfo',
+      component: () => import('@/views/console/inverterInfo/index'),
+      name: 'inverterInfo',
+      meta: { title: '逆变器信息', noCache: true },
+      sign: 'photovoltaic'
+    },
+    {
+      path: 'holidayCalendar',
+      component: () => import('@/views/console/holidayCalendar/index'),
+      name: 'holidayCalendar',
+      meta: { title: '假期日历', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'weatherStationInfo',
+      component: () => import('@/views/console/weatherStationInfo/index'),
+      name: 'WeatherStationInfo',
+      meta: { title: '气象站信息', noCache: true },
+      sign: 'photovoltaic'
+    },
+    {
+      path: 'anemometerTowerInfo',
+      component: () => import('@/views/console/windTowerInfo/index'),
+      name: 'AnemometerTowerInfo',
+      meta: { title: '测风塔信息', noCache: true },
+      sign: 'windPower'
+    },
+    {
+      path: 'agc_avcInfo',
+      component: () => import('@/views/console/agc_avcInfo/index'),
+      name: 'agc_avcInfo',
+      meta: { title: 'AGC_AVC信息', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'subsidiaryEquipmentInfo',
+      component: () => import('@/views/console/subsidiaryEquipmentInfo/index'),
+      name: 'subsidiaryEquipmentInfo',
+      meta: { title: '附属设备信息', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'boosterStationInfo',
+      component: () => import('@/views/console/boosterStationInfo/index'),
+      name: 'boosterStationInfo',
+      meta: { title: '升压站信息', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'windTurbineidInfo',
+      component: () => import('@/views/console/windTurbineInfo/index'),
+      name: 'WindTurbineInfo',
+      meta: { title: '风机信息', noCache: true },
+      sign: 'windPower'
+    },
+    {
+      path: 'overHaulPlan',
+      component: () => import('@/views/console/overHaulPlan/index'),
+      name: 'OverHaulPlan',
+      meta: { title: '光伏检修计划', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'overHaulPlanOne',
+      component: () => import('@/views/console/overHaulPlanOne/index'),
+      name: 'OverHaulPlanOne',
+      meta: { title: '风电检修计划', noCache: true },
+      sign: 'currency'
+    },
+    // {
+    //   path: 'powerPlanSD',
+    //   component: () => import('@/views/console/powerPlanSD/index'),
+    //   name: 'powerPlanSD',
+    //   meta: { title: '山东发电计划', noCache: true },
+    //   sign: 'currency'
+    // },
+    // {
+    //   path: 'consoleCenter',
+    //     component: () => import('@/views/console/consoleCenter/index'),
+    //   name: 'ConsoleCenter',
+    //   meta: {title: '数据控制', noCache: true}
+    // },
+    {
+      path: 'sysParameter',
+      component: () => import('@/views/console/sysParameter/index'),
+      name: 'SysParameter',
+      meta: { title: '系统参数', noCache: true },
+      sign: 'currency'
+    },
+    {
+      path: 'sysAlarm',
+      component: () => import('@/views/console/sysAlarm/index'),
+      name: 'SysAlarm',
+      meta: { title: '系统告警', noCache: true },
+      sign: 'currency'
+    },
+    // {
+    //   path: 'manualInterventionData',
+    //   component: () => import('@/views/console/manualInterventionData/index'),
+    //   name: 'ManualInterventionData',
+    //   meta: { title: '人工干预设置', noCache: true },
+    //   sign: 'currency'
+    // },
+    // {
+    //   path: 'manualInterventionQuery',
+    //   component: () => import('@/views/console/manualInterventionQuery/index'),
+    //   name: 'ManualInterventionQuery',
+    //   meta: { title: '人工干预数据查询', noCache: true },
+    //   sign: 'currency'
+    // },
+    // {
+    //   path: 'dataLimitation',
+    //   component: () => import('@/views/console/dataLimitation/index'),
+    //   name: 'DataLimitation',
+    //   meta: { title: '上报数据限制', noCache: true },
+    //   sign: 'currency'
+    // },
+    // {
+    //   path: 'dataLimitationLog',
+    //   component: () => import('@/views/console/dataLimitationLog/index'),
+    //   name: 'DataLimitationLog',
+    //   meta: { title: '上报数据操作记录', noCache: true },
+    //   sign: 'currency'
+    // },
+    // {
+    //   path: 'communicationEquipment',
+    //   component: () => import('@/views/console/communicationEquipment/index'),
+    //   name: 'CommunicationEquipment',
+    //   meta: { title: '通信设备', noCache: true },
+    //   sign: 'currency'
+    // },
+    {
+      path: 'fileAnalysisRecord',
+      component: () => import('@/views/console/FileAnalysisRecord/index'),
+      name: 'FileAnalysisRecord',
+      meta: { title: '文件解析记录', noCache: true },
+      sign: 'currency'
+    },
+    // {
+    //   path: 'QREnvironmentData',
+    //   component: () => import('@/views/console/QREnvironmentData/index'),
+    //   name: 'QREnvironmentData',
+    //   meta: { title: '二维码数据回传', noCache: true },
+    //   sign: 'currency'
+    // }
+    // {
+    //   path: 'uploadFileChannel',
+    //     component: () => import('@/views/console/UploadFileChannel/index'),
+    //   name: 'UploadFileChannel',
+    //   meta: {title: '通道配置', noCache: true}
+    // }
+
+  ]
+}
+
+export default consoleRouter

+ 66 - 0
ipfcst/ipfcst-console/src/main/frontend/router/modules/dataexchange.js

@@ -0,0 +1,66 @@
+import Layout from '@/layout'
+
+const dataExchangeRouter = {
+  path: '/dataExchange',
+  component: Layout,
+  redirect: 'noRedirect',
+  name: 'DataExchange',
+  meta: {
+    title: '数据交互中心',
+    icon: 'tree'
+  },
+  children: [
+    {
+      path: 'equipmentAttribute',
+      component: () => import('@/views/dataexchange/equipmentAttribute'),
+      name: 'equipmentAttribute',
+      meta: {title: '设备状态属性', noCache: true},
+      sign: 'currency'
+    },
+    {
+      path: 'gatherdatapoint',
+      component: () => import('@/views/dataexchange/gatherdatapoint'),
+      name: 'gatherdatapoint',
+      meta: {title: '通道采集点表', noCache: true},
+      sign: 'currency'
+    },
+    {
+      path: 'senderdatapoint',
+      component: () => import('@/views/dataexchange/senderdatapoint'),
+      name: 'senderdatapoint',
+      meta: {title: '通道转发点表', noCache: true},
+      sign: 'currency'
+    },
+    {
+      path: 'template',
+      component: () => import('@/views/dataexchange/template'),
+      name: 'template',
+      meta: {title: '模版信息', noCache: true},
+      sign: 'currency'
+    },
+    {
+      path: 'templatedatapoint',
+      component: () => import('@/views/dataexchange/templatedatapoint'),
+      name: 'templatedatapoint',
+      meta: {title: '模版点表', noCache: true},
+      hidden:true,
+      sign: 'currency'
+    },
+    {
+      path: 'tunnelinfo',
+      component: () => import('@/views/dataexchange/tunnelinfo'),
+      name: 'tunnelinfo',
+      meta: {title: '通道信息', noCache: true},
+      sign: 'currency'
+    },
+    {
+      path: 'sendLog',
+      component: () => import('@/views/dataexchange/sendLog'),
+      name: 'sendLog',
+      meta: {title: '回传日志', noCache: true},
+      sign: 'currency'
+    }
+  ]
+}
+
+export default dataExchangeRouter

+ 46 - 0
ipfcst/ipfcst-console/src/main/frontend/router/modules/system.js

@@ -0,0 +1,46 @@
+/** When your routing table is too long, you can split it into small modules**/
+
+import Layout from '@/layout'
+
+const systemRouter = {
+    path: '/system',
+    component: Layout,
+    redirect: '/system/quartz',
+    name: 'system',
+    meta: {
+        title: '系统管理',
+        icon: 'system'
+    },
+    children: [
+        // {
+        //     path: 'jobClass',
+        //     component: () => import('@/views/quartz/JobClass/index'),
+        //     name: 'jobClass',
+        //     meta: {title: '定时任务初始化', noCache: true},
+        //     sign: 'currency'
+        // },
+        {
+            path: 'quartz',
+            component: () => import('@/views/quartz/index'),
+            name: 'quartz',
+            meta: {title: '定时任务', noCache: true},
+            sign: 'currency'
+        },
+        {
+            path: 'systemfile',
+            component: () => import('@/views/systemfile/LogFile'),
+            name: 'systemfile',
+            meta: {title: '系统文件', noCache: true},
+            sign: 'currency'
+        },
+        {
+            path: 'systemuser',
+            component: () => import('@/views/console/sysUser/index'),
+            name: 'systemuser',
+            meta: {title: '用户管理', noCache: true},
+            sign: 'admin'
+        }
+    ]
+}
+
+export default systemRouter

+ 46 - 0
ipfcst/ipfcst-console/src/main/frontend/router/modules/uploadFile.js

@@ -0,0 +1,46 @@
+/** When your routing table is too long, you can split it into small modules**/
+
+import Layout from '@/layout'
+
+const uploadRouter = {
+  path: '/uploadFile',
+  component: Layout,
+  redirect: '/uploadFile/uploadFileChannel',
+  name: 'uploadFile',
+  meta: {
+    title: '文件上报',
+    icon: 'form'
+  },
+  children: [
+    {
+      path: 'uploadFileChannel',
+      component: () => import('@/views/uploadFile/UploadFileChannel'),
+      name: 'UploadFileChannel',
+      meta: {title: '上报通道配置', noCache: true},
+      sign: 'currency'
+    },
+    {
+      path: 'queryUploadFile',
+      component: () => import('@/views/uploadFile/queryUploadFile'),
+      name: 'queryUploadFile',
+      meta: { title: '文件上报查询', noCache: true },
+      sign: 'currency'
+    },
+    // {
+    //   path: 'uploadFile',
+    //   component: () => import('@/views/uploadFile/uploadFile'),
+    //   name: 'uploadFile',
+    //   meta: { title: '上传上报文件', noCache: true },
+    //   sign: 'currency'
+    // },
+    // {
+    //   path: 'feedbackFile',
+    //   component: () => import('@/views/uploadFile/feedbackFile'),
+    //   name: 'feedbackFile',
+    //   meta: { title: '反馈文件日志查询', noCache: true },
+    //   sign: 'feedback'
+    // }
+  ]
+}
+
+export default uploadRouter

+ 16 - 0
ipfcst/ipfcst-console/src/main/frontend/settings.js

@@ -0,0 +1,16 @@
+module.exports = {
+
+  title: '功率预测系统-管理控制台V3.0',
+
+  /**
+   * @type {boolean} true | false
+   * @description Whether fix the header
+   */
+  fixedHeader: false,
+
+  /**
+   * @type {boolean} true | false
+   * @description Whether show the logo in sidebar
+   */
+  sidebarLogo: false
+}

+ 8 - 0
ipfcst/ipfcst-console/src/main/frontend/store/getters.js

@@ -0,0 +1,8 @@
+const getters = {
+  sidebar: state => state.app.sidebar,
+  device: state => state.app.device,
+  token: state => state.user.token,
+  avatar: state => state.user.avatar,
+  name: state => state.user.name
+}
+export default getters

+ 19 - 0
ipfcst/ipfcst-console/src/main/frontend/store/index.js

@@ -0,0 +1,19 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import getters from './getters'
+import app from './modules/app'
+import settings from './modules/settings'
+import user from './modules/user'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+  modules: {
+    app,
+    settings,
+    user
+  },
+  getters
+})
+
+export default store

+ 48 - 0
ipfcst/ipfcst-console/src/main/frontend/store/modules/app.js

@@ -0,0 +1,48 @@
+import Cookies from 'js-cookie'
+
+const state = {
+  sidebar: {
+    opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
+    withoutAnimation: false
+  },
+  device: 'desktop'
+}
+
+const mutations = {
+  TOGGLE_SIDEBAR: state => {
+    state.sidebar.opened = !state.sidebar.opened
+    state.sidebar.withoutAnimation = false
+    if (state.sidebar.opened) {
+      Cookies.set('sidebarStatus', 1)
+    } else {
+      Cookies.set('sidebarStatus', 0)
+    }
+  },
+  CLOSE_SIDEBAR: (state, withoutAnimation) => {
+    Cookies.set('sidebarStatus', 0)
+    state.sidebar.opened = false
+    state.sidebar.withoutAnimation = withoutAnimation
+  },
+  TOGGLE_DEVICE: (state, device) => {
+    state.device = device
+  }
+}
+
+const actions = {
+  toggleSideBar({ commit }) {
+    commit('TOGGLE_SIDEBAR')
+  },
+  closeSideBar({ commit }, { withoutAnimation }) {
+    commit('CLOSE_SIDEBAR', withoutAnimation)
+  },
+  toggleDevice({ commit }, device) {
+    commit('TOGGLE_DEVICE', device)
+  }
+}
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+}

+ 31 - 0
ipfcst/ipfcst-console/src/main/frontend/store/modules/settings.js

@@ -0,0 +1,31 @@
+import defaultSettings from '@/settings'
+
+const { showSettings, fixedHeader, sidebarLogo } = defaultSettings
+
+const state = {
+  showSettings: showSettings,
+  fixedHeader: fixedHeader,
+  sidebarLogo: sidebarLogo
+}
+
+const mutations = {
+  CHANGE_SETTING: (state, { key, value }) => {
+    if (state.hasOwnProperty(key)) {
+      state[key] = value
+    }
+  }
+}
+
+const actions = {
+  changeSetting({ commit }, data) {
+    commit('CHANGE_SETTING', data)
+  }
+}
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+}
+

+ 116 - 0
ipfcst/ipfcst-console/src/main/frontend/store/modules/user.js

@@ -0,0 +1,116 @@
+import { login, logout, getInfo } from '@/api/user'
+import { getToken, setToken, removeToken } from '@/utils/auth'
+import { resetRouter } from '@/router'
+import request from '@/utils/request'
+const getDefaultState = () => {
+  return {
+    token: getToken(),
+    name: '',
+    avatar: ''
+  }
+}
+
+const state = getDefaultState()
+
+const mutations = {
+  RESET_STATE: (state) => {
+    Object.assign(state, getDefaultState())
+  },
+  SET_TOKEN: (state, token) => {
+    state.token = token
+  },
+  SET_NAME: (state, name) => {
+    state.name = name
+  },
+  SET_AVATAR: (state, avatar) => {
+    state.avatar = avatar
+  }
+}
+
+const actions = {
+  // user login
+  login: function({ commit }, userInfo) {
+    /* const { username, password } = userInfo*/
+    /*    this.$axios.post('/user/login', {
+      username: username,
+      password: password
+    }).then((res) => {
+      const { data } = res
+      commit('SET_TOKEN', data.token)
+    }).catch((error) => {
+      this.$message.error('登录后台出错' + error)
+    })*/
+
+    return new Promise((resolve, reject) => {
+      request({
+        url: '/user/login',
+        method: 'post',
+        params: userInfo
+      }).then(res => {
+        const { data } = res
+        commit('SET_TOKEN', data)
+        setToken(data)
+        resolve()
+      }).catch((error) => {
+        reject(error)
+      })
+
+      /*     login({ username: username.trim(), password: password }).then(response => {
+          const { data } = response
+          commit('SET_TOKEN', data.token)
+          setToken(data.token)
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })*/
+    })
+  },
+
+  // get user info
+  getInfo({ commit, state }) {
+    return new Promise((resolve, reject) => {
+      getInfo(state.token).then(response => {
+        const { data } = response
+
+        if (!data) {
+          reject('Verification failed, please Login again.')
+        }
+
+        const { name, avatar } = data
+
+        commit('SET_NAME', name)
+        commit('SET_AVATAR', avatar)
+        resolve(data)
+      }).catch(error => {
+        reject(error)
+      })
+    })
+  },
+
+  // user logout
+  logout({ commit, state }) {
+    return new Promise((resolve, reject) => {
+      removeToken() // must remove  token  first
+      resetRouter()
+      commit('RESET_STATE')
+      resolve()
+    })
+  },
+
+  // remove token
+  resetToken({ commit }) {
+    return new Promise(resolve => {
+      removeToken() // must remove  token  first
+      commit('RESET_STATE')
+      resolve()
+    })
+  }
+}
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+}
+

+ 54 - 0
ipfcst/ipfcst-console/src/main/frontend/styles/element-ui.scss

@@ -0,0 +1,54 @@
+// cover some element-ui styles
+
+.el-breadcrumb__inner,
+.el-breadcrumb__inner a {
+  font-weight: 400 !important;
+}
+
+.el-upload {
+  input[type="file"] {
+    display: none !important;
+  }
+}
+
+.el-upload__input {
+  display: none;
+}
+
+
+// to fixed https://github.com/ElemeFE/element/issues/2461
+.el-dialog {
+  transform: none;
+  left: 0;
+  position: relative;
+  margin: 0 auto;
+}
+
+// refine element ui upload
+.upload-container {
+  .el-upload {
+    width: 100%;
+
+    .el-upload-dragger {
+      width: 100%;
+      height: 200px;
+    }
+  }
+}
+
+// dropdown
+.el-dropdown-menu {
+  a {
+    display: block
+  }
+}
+
+// to fix el-date-picker css style
+.el-range-separator {
+  box-sizing: content-box;
+}
+.el-cascader-menu__wrap{
+height: 100%;
+}
+
+

+ 65 - 0
ipfcst/ipfcst-console/src/main/frontend/styles/index.scss

@@ -0,0 +1,65 @@
+@import './variables.scss';
+@import './mixin.scss';
+@import './transition.scss';
+@import './element-ui.scss';
+@import './sidebar.scss';
+
+body {
+  height: 100%;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  text-rendering: optimizeLegibility;
+  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+}
+
+label {
+  font-weight: 700;
+}
+
+html {
+  height: 100%;
+  box-sizing: border-box;
+}
+
+#app {
+  height: 100%;
+}
+
+*,
+*:before,
+*:after {
+  box-sizing: inherit;
+}
+
+a:focus,
+a:active {
+  outline: none;
+}
+
+a,
+a:focus,
+a:hover {
+  cursor: pointer;
+  color: inherit;
+  text-decoration: none;
+}
+
+div:focus {
+  outline: none;
+}
+
+.clearfix {
+  &:after {
+    visibility: hidden;
+    display: block;
+    font-size: 0;
+    content: " ";
+    clear: both;
+    height: 0;
+  }
+}
+
+// main-container global css
+.app-container {
+  padding: 20px;
+}

+ 28 - 0
ipfcst/ipfcst-console/src/main/frontend/styles/mixin.scss

@@ -0,0 +1,28 @@
+@mixin clearfix {
+  &:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+}
+
+@mixin scrollBar {
+  &::-webkit-scrollbar-track-piece {
+    background: #d3dce6;
+  }
+
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background: #99a9bf;
+    border-radius: 20px;
+  }
+}
+
+@mixin relative {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}

+ 209 - 0
ipfcst/ipfcst-console/src/main/frontend/styles/sidebar.scss

@@ -0,0 +1,209 @@
+#app {
+
+  .main-container {
+    min-height: 100%;
+    transition: margin-left .28s;
+    margin-left: $sideBarWidth;
+    position: relative;
+  }
+
+  .sidebar-container {
+    transition: width 0.28s;
+    width: $sideBarWidth !important;
+    background-color: $menuBg;
+    height: 100%;
+    position: fixed;
+    font-size: 0px;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1001;
+    overflow: hidden;
+
+    // reset element-ui css
+    .horizontal-collapse-transition {
+      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
+    }
+
+    .scrollbar-wrapper {
+      overflow-x: hidden !important;
+    }
+
+    .el-scrollbar__bar.is-vertical {
+      right: 0px;
+    }
+
+    .el-scrollbar {
+      height: 100%;
+    }
+
+    &.has-logo {
+      .el-scrollbar {
+        height: calc(100% - 50px);
+      }
+    }
+
+    .is-horizontal {
+      display: none;
+    }
+
+    a {
+      display: inline-block;
+      width: 100%;
+      overflow: hidden;
+    }
+
+    .svg-icon {
+      margin-right: 16px;
+    }
+
+    .el-menu {
+      border: none;
+      height: 100%;
+      width: 100% !important;
+    }
+
+    // menu hover
+    .submenu-title-noDropdown,
+    .el-submenu__title {
+      &:hover {
+        background-color: $menuHover !important;
+      }
+    }
+
+    .is-active>.el-submenu__title {
+      color: $subMenuActiveText !important;
+    }
+
+    & .nest-menu .el-submenu>.el-submenu__title,
+    & .el-submenu .el-menu-item {
+      min-width: $sideBarWidth !important;
+      background-color: $subMenuBg !important;
+
+      &:hover {
+        background-color: $subMenuHover !important;
+      }
+    }
+  }
+
+  .hideSidebar {
+    .sidebar-container {
+      width: 54px !important;
+    }
+
+    .main-container {
+      margin-left: 54px;
+    }
+
+    .submenu-title-noDropdown {
+      padding: 0 !important;
+      position: relative;
+
+      .el-tooltip {
+        padding: 0 !important;
+
+        .svg-icon {
+          margin-left: 20px;
+        }
+      }
+    }
+
+    .el-submenu {
+      overflow: hidden;
+
+      &>.el-submenu__title {
+        padding: 0 !important;
+
+        .svg-icon {
+          margin-left: 20px;
+        }
+
+        .el-submenu__icon-arrow {
+          display: none;
+        }
+      }
+    }
+
+    .el-menu--collapse {
+      .el-submenu {
+        &>.el-submenu__title {
+          &>span {
+            height: 0;
+            width: 0;
+            overflow: hidden;
+            visibility: hidden;
+            display: inline-block;
+          }
+        }
+      }
+    }
+  }
+
+  .el-menu--collapse .el-menu .el-submenu {
+    min-width: $sideBarWidth !important;
+  }
+
+  // mobile responsive
+  .mobile {
+    .main-container {
+      margin-left: 0px;
+    }
+
+    .sidebar-container {
+      transition: transform .28s;
+      width: $sideBarWidth !important;
+    }
+
+    &.hideSidebar {
+      .sidebar-container {
+        pointer-events: none;
+        transition-duration: 0.3s;
+        transform: translate3d(-$sideBarWidth, 0, 0);
+      }
+    }
+  }
+
+  .withoutAnimation {
+
+    .main-container,
+    .sidebar-container {
+      transition: none;
+    }
+  }
+}
+
+// when menu collapsed
+.el-menu--vertical {
+  &>.el-menu {
+    .svg-icon {
+      margin-right: 16px;
+    }
+  }
+
+  .nest-menu .el-submenu>.el-submenu__title,
+  .el-menu-item {
+    &:hover {
+      // you can use $subMenuHover
+      background-color: $menuHover !important;
+    }
+  }
+
+  // the scroll bar appears when the subMenu is too long
+  >.el-menu--popup {
+    max-height: 100vh;
+    overflow-y: auto;
+
+    &::-webkit-scrollbar-track-piece {
+      background: #d3dce6;
+    }
+
+    &::-webkit-scrollbar {
+      width: 6px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: #99a9bf;
+      border-radius: 20px;
+    }
+  }
+}

+ 48 - 0
ipfcst/ipfcst-console/src/main/frontend/styles/transition.scss

@@ -0,0 +1,48 @@
+// global transition css
+
+/* fade */
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.28s;
+}
+
+.fade-enter,
+.fade-leave-active {
+  opacity: 0;
+}
+
+/* fade-transform */
+.fade-transform-leave-active,
+.fade-transform-enter-active {
+  transition: all .5s;
+}
+
+.fade-transform-enter {
+  opacity: 0;
+  transform: translateX(-30px);
+}
+
+.fade-transform-leave-to {
+  opacity: 0;
+  transform: translateX(30px);
+}
+
+/* breadcrumb transition */
+.breadcrumb-enter-active,
+.breadcrumb-leave-active {
+  transition: all .5s;
+}
+
+.breadcrumb-enter,
+.breadcrumb-leave-active {
+  opacity: 0;
+  transform: translateX(20px);
+}
+
+.breadcrumb-move {
+  transition: all .5s;
+}
+
+.breadcrumb-leave-active {
+  position: absolute;
+}

+ 25 - 0
ipfcst/ipfcst-console/src/main/frontend/styles/variables.scss

@@ -0,0 +1,25 @@
+// sidebar
+$menuText:#bfcbd9;
+$menuActiveText:#409EFF;
+$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
+
+$menuBg:#304156;
+$menuHover:#263445;
+
+$subMenuBg:#1f2d3d;
+$subMenuHover:#001528;
+
+$sideBarWidth: 210px;
+
+// the :export directive is the magic sauce for webpack
+// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
+:export {
+  menuText: $menuText;
+  menuActiveText: $menuActiveText;
+  subMenuActiveText: $subMenuActiveText;
+  menuBg: $menuBg;
+  menuHover: $menuHover;
+  subMenuBg: $subMenuBg;
+  subMenuHover: $subMenuHover;
+  sideBarWidth: $sideBarWidth;
+}

+ 15 - 0
ipfcst/ipfcst-console/src/main/frontend/utils/auth.js

@@ -0,0 +1,15 @@
+import Cookies from 'js-cookie'
+
+const TokenKey = 'vue_admin_template_token'
+
+export function getToken() {
+  return Cookies.get(TokenKey)
+}
+
+export function setToken(token) {
+  return Cookies.set(TokenKey, token)
+}
+
+export function removeToken() {
+  return Cookies.remove(TokenKey)
+}

+ 13 - 0
ipfcst/ipfcst-console/src/main/frontend/utils/commonFuc.js

@@ -0,0 +1,13 @@
+export function getBrowserToken() {
+  var token = "";
+  var ca = document.cookie.split(';');
+  for (var i = 0; i < ca.length; i++) {
+    var c = ca[i].trim();
+    if (c.indexOf("token=") == 0){
+      token = c.substring("token=".length, c.length);
+    }
+  }
+  return token
+}
+
+

+ 10 - 0
ipfcst/ipfcst-console/src/main/frontend/utils/get-page-title.js

@@ -0,0 +1,10 @@
+import defaultSettings from '@/settings'
+
+const title = defaultSettings.title || 'Vue Admin Template'
+
+export default function getPageTitle(pageTitle) {
+  if (pageTitle) {
+    return `${pageTitle} - ${title}`
+  }
+  return `${title}`
+}

+ 107 - 0
ipfcst/ipfcst-console/src/main/frontend/utils/index.js

@@ -0,0 +1,107 @@
+/**
+ * Created by PanJiaChen on 16/11/18.
+ */
+
+/**
+ * Parse the time to string
+ * @param {(Object|string|number)} time
+ * @param {string} cFormat
+ * @returns {string | null}
+ */
+export function parseTime(time, cFormat) {
+  if (arguments.length === 0) {
+    return null
+  }
+  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+      time = parseInt(time)
+    }
+    if ((typeof time === 'number') && (time.toString().length === 10)) {
+      time = time * 1000
+    }
+    date = new Date(time)
+  }
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  }
+  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
+    const value = formatObj[key]
+    // Note: getDay() returns 0 on Sunday
+    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
+    return value.toString().padStart(2, '0')
+  })
+  return time_str
+}
+
+/**
+ * @param {number} time
+ * @param {string} option
+ * @returns {string}
+ */
+export function formatTime(time, option) {
+  if (('' + time).length === 10) {
+    time = parseInt(time) * 1000
+  } else {
+    time = +time
+  }
+  const d = new Date(time)
+  const now = Date.now()
+
+  const diff = (now - d) / 1000
+
+  if (diff < 30) {
+    return '刚刚'
+  } else if (diff < 3600) {
+    // less 1 hour
+    return Math.ceil(diff / 60) + '分钟前'
+  } else if (diff < 3600 * 24) {
+    return Math.ceil(diff / 3600) + '小时前'
+  } else if (diff < 3600 * 24 * 2) {
+    return '1天前'
+  }
+  if (option) {
+    return parseTime(time, option)
+  } else {
+    return (
+      d.getMonth() +
+      1 +
+      '月' +
+      d.getDate() +
+      '日' +
+      d.getHours() +
+      '时' +
+      d.getMinutes() +
+      '分'
+    )
+  }
+}
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export function param2Obj(url) {
+  const search = url.split('?')[1]
+  if (!search) {
+    return {}
+  }
+  return JSON.parse(
+    '{"' +
+      decodeURIComponent(search)
+        .replace(/"/g, '\\"')
+        .replace(/&/g, '","')
+        .replace(/=/g, '":"')
+        .replace(/\+/g, ' ') +
+      '"}'
+  )
+}

+ 126 - 0
ipfcst/ipfcst-console/src/main/frontend/utils/request.js

@@ -0,0 +1,126 @@
+import axios from 'axios'
+import { MessageBox, Message } from 'element-ui'
+import store from '@/store'
+import { getToken, removeToken } from '@/utils/auth'
+import router, { resetRouter } from '@/router'
+
+// create an axios instance
+const service = axios.create({
+  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
+  // withCredentials: true, // send cookies when cross-domain requests
+  timeout: 1000 * 60 * 10 // request timeout
+})
+
+// request interceptor
+service.interceptors.request.use(
+  config => {
+    // do something before request is sent
+    if (store.getters.token) {
+      // let each request carry token
+      // ['X-Token'] is a custom headers key
+      // please modify it according to the actual situation
+      config.headers['Authorization'] = getToken()
+    }
+    return config
+  },
+  error => {
+    // do something with request error
+    console.log(error) // for debug
+    return Promise.reject(error)
+  }
+)
+
+// response interceptor
+service.interceptors.response.use(
+  /**
+   * If you want to get http information such as headers or status
+   * Please return  response => response
+  */
+
+  /**
+   * Determine the request status by custom code
+   * Here is just an example
+   * You can also judge the status by HTTP Status Code
+   */
+  response => {
+    const res = response.data
+    // if the custom code is not 20000, it is judged as an error.
+    if (res.code !== 0) {
+      Message({
+        message: res.message || 'Error',
+        type: 'error',
+        duration: 5 * 1000
+      })
+
+      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
+      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
+        // to re-login
+        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
+          confirmButtonText: 'Re-Login',
+          cancelButtonText: 'Cancel',
+          type: 'warning'
+        }).then(() => {
+          store.dispatch('user/resetToken').then(() => {
+            location.reload()
+          })
+        })
+      }
+      return Promise.reject(new Error(res.message || 'Error'))
+    } else {
+      return res
+    }
+  },
+  error => {
+    if (error.response) {
+      switch (error.response.status) {
+        case 401:
+          console.log('用户验证失败!')
+          // 返回 401 清除token信息并跳转到登录页面
+          removeToken()
+          resetRouter()
+          router.push('/login')
+          Message({
+            message: error.response.data.data,
+            type: 'error',
+            duration: 5 * 1000
+          })
+          break
+        case 403:
+          console.log('登录超时!')
+          // 返回 401 清除token信息并跳转到登录页面
+          removeToken()
+          resetRouter()
+          router.push('/login')
+          Message({
+            message: '登录超时',
+            type: 'error',
+            duration: 5 * 1000
+          })
+          break
+        case 500:
+          Message({
+            message: '服务器关闭了!请联系相关工作人员',
+            type: 'error',
+            duration: 5 * 1000
+          })
+          removeToken()
+          resetRouter()
+          router.push('/login')
+          break
+        case 504:
+          console.log('服务器关闭了!')
+          resetRouter()
+          break
+      }
+    }
+    /*    console.log('err' + error) // for debug
+    Message({
+      message: error.message,
+      type: 'error',
+      duration: 5 * 1000
+    })*/
+    return Promise.reject(error)
+  }
+)
+
+export default service

+ 20 - 0
ipfcst/ipfcst-console/src/main/frontend/utils/validate.js

@@ -0,0 +1,20 @@
+/**
+ * Created by PanJiaChen on 16/11/18.
+ */
+
+/**
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export function isExternal(path) {
+  return /^(https?:|mailto:|tel:)/.test(path)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validUsername(str) {
+  const valid_map = ['admin', 'editor']
+  return valid_map.indexOf(str.trim()) >= 0
+}

+ 228 - 0
ipfcst/ipfcst-console/src/main/frontend/views/404.vue

@@ -0,0 +1,228 @@
+<template>
+  <div class="wscn-http404-container">
+    <div class="wscn-http404">
+      <div class="pic-404">
+        <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
+        <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
+        <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
+        <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
+      </div>
+      <div class="bullshit">
+        <div class="bullshit__oops">OOPS!</div>
+        <div class="bullshit__info">All rights reserved
+          <a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>
+        </div>
+        <div class="bullshit__headline">{{ message }}</div>
+        <div class="bullshit__info">Please check that the URL you entered is correct, or click the button below to return to the homepage.</div>
+        <a href="" class="bullshit__return-home">Back to home</a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+
+export default {
+  name: 'Page404',
+  computed: {
+    message() {
+      return 'The webmaster said that you can not enter this page...'
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.wscn-http404-container{
+  transform: translate(-50%,-50%);
+  position: absolute;
+  top: 40%;
+  left: 50%;
+}
+.wscn-http404 {
+  position: relative;
+  width: 1200px;
+  padding: 0 50px;
+  overflow: hidden;
+  .pic-404 {
+    position: relative;
+    float: left;
+    width: 600px;
+    overflow: hidden;
+    &__parent {
+      width: 100%;
+    }
+    &__child {
+      position: absolute;
+      &.left {
+        width: 80px;
+        top: 17px;
+        left: 220px;
+        opacity: 0;
+        animation-name: cloudLeft;
+        animation-duration: 2s;
+        animation-timing-function: linear;
+        animation-fill-mode: forwards;
+        animation-delay: 1s;
+      }
+      &.mid {
+        width: 46px;
+        top: 10px;
+        left: 420px;
+        opacity: 0;
+        animation-name: cloudMid;
+        animation-duration: 2s;
+        animation-timing-function: linear;
+        animation-fill-mode: forwards;
+        animation-delay: 1.2s;
+      }
+      &.right {
+        width: 62px;
+        top: 100px;
+        left: 500px;
+        opacity: 0;
+        animation-name: cloudRight;
+        animation-duration: 2s;
+        animation-timing-function: linear;
+        animation-fill-mode: forwards;
+        animation-delay: 1s;
+      }
+      @keyframes cloudLeft {
+        0% {
+          top: 17px;
+          left: 220px;
+          opacity: 0;
+        }
+        20% {
+          top: 33px;
+          left: 188px;
+          opacity: 1;
+        }
+        80% {
+          top: 81px;
+          left: 92px;
+          opacity: 1;
+        }
+        100% {
+          top: 97px;
+          left: 60px;
+          opacity: 0;
+        }
+      }
+      @keyframes cloudMid {
+        0% {
+          top: 10px;
+          left: 420px;
+          opacity: 0;
+        }
+        20% {
+          top: 40px;
+          left: 360px;
+          opacity: 1;
+        }
+        70% {
+          top: 130px;
+          left: 180px;
+          opacity: 1;
+        }
+        100% {
+          top: 160px;
+          left: 120px;
+          opacity: 0;
+        }
+      }
+      @keyframes cloudRight {
+        0% {
+          top: 100px;
+          left: 500px;
+          opacity: 0;
+        }
+        20% {
+          top: 120px;
+          left: 460px;
+          opacity: 1;
+        }
+        80% {
+          top: 180px;
+          left: 340px;
+          opacity: 1;
+        }
+        100% {
+          top: 200px;
+          left: 300px;
+          opacity: 0;
+        }
+      }
+    }
+  }
+  .bullshit {
+    position: relative;
+    float: left;
+    width: 300px;
+    padding: 30px 0;
+    overflow: hidden;
+    &__oops {
+      font-size: 32px;
+      font-weight: bold;
+      line-height: 40px;
+      color: #1482f0;
+      opacity: 0;
+      margin-bottom: 20px;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-fill-mode: forwards;
+    }
+    &__headline {
+      font-size: 20px;
+      line-height: 24px;
+      color: #222;
+      font-weight: bold;
+      opacity: 0;
+      margin-bottom: 10px;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-delay: 0.1s;
+      animation-fill-mode: forwards;
+    }
+    &__info {
+      font-size: 13px;
+      line-height: 21px;
+      color: grey;
+      opacity: 0;
+      margin-bottom: 30px;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-delay: 0.2s;
+      animation-fill-mode: forwards;
+    }
+    &__return-home {
+      display: block;
+      float: left;
+      width: 110px;
+      height: 36px;
+      background: #1482f0;
+      border-radius: 100px;
+      text-align: center;
+      color: #ffffff;
+      opacity: 0;
+      font-size: 14px;
+      line-height: 36px;
+      cursor: pointer;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-delay: 0.3s;
+      animation-fill-mode: forwards;
+    }
+    @keyframes slideUp {
+      0% {
+        transform: translateY(60px);
+        opacity: 0;
+      }
+      100% {
+        transform: translateY(0);
+        opacity: 1;
+      }
+    }
+  }
+}
+</style>

+ 113 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/FileAnalysisRecord/index.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="app-container">
+      <el-card class="box-carde">
+        <div slot="header" class="clearfix">
+          <span>文件解析记录</span>
+
+        </div>
+    <el-container>
+      <el-header style="padding: 0px;display:flex;justify-content:space-between;align-items: center">
+        <div style="display: inline">
+          <el-date-picker
+            v-model="dateTime"
+            type="date"
+            placeholder="选择日期"/>
+          <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-search" @click="getData">搜索</el-button>
+        </div>
+      </el-header>
+
+      <el-tabs type="card">
+
+        <vxe-table
+          align="center"
+          :loading="loading"
+          ref="xTable"
+          auto-resize
+          highlight-current-row
+          border
+          resizable
+          show-overflow
+          :data="tableData"
+          v-show="showTable"
+          :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+        >
+        <vxe-table-column title="文件解析记录">
+<!--          <vxe-table-column field="createTime" title="时间" :formatter="timestampToTimess"></vxe-table-column>-->
+          <vxe-table-column field="fileTitle" title="文件名称"></vxe-table-column>
+          <vxe-table-column field="fileType" title="文件类型"></vxe-table-column>
+          <vxe-table-column field="fileDescription" title="文件描述"></vxe-table-column>
+          <vxe-table-column field="filePath" title="存放路径"></vxe-table-column>
+          <vxe-table-column field="fileStatus" title="解析状态">
+            <template v-slot="{ row }">
+              <el-button type="success" circle v-if="row.fileStatus==1" icon="el-icon-check"></el-button>
+              <el-button type="danger" circle v-if="row.fileStatus!=1" icon="el-icon-close"></el-button>
+            </template>
+          </vxe-table-column>
+        </vxe-table-column>
+        </vxe-table>
+      </el-tabs>
+    </el-container>
+    </el-card>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      // 日期选择器
+      dateTime:  new Date(new Date().toLocaleDateString()),
+      tableData:[],
+      totalCount: -1,
+      currentPage: 1,
+      pageSize:25,
+      tableLoading: false,
+      showTable:true,
+      loading:false
+    }
+  },
+  created: function() {
+    this.getData()
+
+  },
+  methods: {
+    getData(){
+      this.$axios.get("/FileAnalysisRecord/"+this.dateTime.getTime()).then(res=>{
+        this.tableData = res.data
+      })
+    },
+    timestampToTimess(time) {
+      const date = new Date(time)// 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return Y + M + D + H + m
+    },
+  }
+}
+</script>
+<style>
+.el-dialog__body {
+  padding-top: 0px;
+  padding-bottom: 0px;
+}
+
+.slide-fade-enter-active {
+  transition: all .8s ease;
+}
+
+.slide-fade-leave-active {
+  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
+}
+
+.slide-fade-enter, .slide-fade-leave-to {
+  transform: translateX(10px);
+  opacity: 0;
+}
+
+#MyUltraShortTermChart {
+  width: 100%;
+  height: 400px;
+}
+</style>

+ 103 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/QREnvironmentData/index.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-carde">
+      <div slot="header" class="clearfix">
+        <span>实际功率&环境数据二维码扫描回传</span>
+      </div>
+      <div>
+        <div class="block">
+          <span class="demonstration">日期选择:</span>
+          <el-date-picker
+            v-model="startTime"
+            align="right"
+            type="date"
+            placeholder="选择日期"
+            :picker-options="pickerOptions">
+          </el-date-picker>
+          <el-button type="primary" size="small" style="round-clip: 10px" @click="getQR">查询</el-button>
+        </div>
+        <div style="text-align: center">
+          <div style="font-size: 24px;font-weight: bold;color: #cf2b2b;display: inline-block;">实际功率&环境数据二维码</div>
+          <div style="font-size: 12px;font-weight: bold;color: #cf2b2b;display: inline-block;">{{ timestampToTime(startTime) }} <===> {{ timestampToTime(endTime) }}</div>
+          <div>
+            <el-image
+              :src="urlReal"
+              :fit="fit"
+              :preview-src-list="srcList"
+            />
+          </div>
+        </div>
+
+      </div>
+    </el-card>
+  </div>
+
+</template>
+<script>
+export default {
+  data() {
+    return {
+      pickerOptions: {
+        disabledDate(time) {
+          return time.getTime() > Date.now();
+        },
+        shortcuts: [{
+          text: '今天',
+          onClick(picker) {
+            picker.$emit('pick', new Date());
+          }
+        }, {
+          text: '昨天',
+          onClick(picker) {
+            const date = new Date();
+            date.setTime(date.getTime() - 3600 * 1000 * 24);
+            picker.$emit('pick', date);
+          }
+        }, {
+          text: '一周前',
+          onClick(picker) {
+            const date = new Date();
+            date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', date);
+          }
+        }]
+      },
+      fit: 'fill',
+      urlReal: '',
+      startTime: new Date(new Date(new Date().toLocaleDateString()).getTime()),
+      endTime: new Date(new Date(new Date().toLocaleDateString()).getTime()+ 86399999),
+      srcList:[]
+    }
+  },
+  created() {
+    this.getQR()
+  },
+  methods: {
+    getQR() {
+      var href = window.location.href
+      var time = this.startTime.getTime()+ 86399999
+      this.urlReal = 'https://' + href.split('//')[1].split(':')[0] + ':9001/qrCode/realPower/'+this.startTime.getTime()+'/'+time
+      this.srcList = ['https://' + href.split('//')[1].split(':')[0] + ':9001/qrCode/realPower/'+this.startTime.getTime()+'/'+time]
+    },
+    timestampToTime(time) {
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      const H = date.getHours()
+      const MIN = date.getMinutes()
+      const S = date.getSeconds()
+      return Y + M + D + ' ' + H + ':' + MIN + ':' + S
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-container {}
+
+.pagination {
+  margin: 20px 0;
+  text-align: right;
+}
+</style>

+ 343 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/agc_avcInfo/index.vue

@@ -0,0 +1,343 @@
+<template>
+  <div class="app-container">
+    <div>
+      <el-card>
+        <div slot="header" class="clearfix">
+          <b><span>AGC_AVC信息</span></b>
+          <el-button
+            :loading="loadButton"
+            style="float: right;padding:3px 10px 3px 3px;"
+            type="text"
+            @click="exportDataEvent"
+          >导出数据
+          </el-button>
+        </div>
+        <el-button type="primary" size="small" style="round-clip: 10px" :loading="loadButton" @click="insertEvent">添加</el-button>
+        <div style="padding-top: 10px">
+          <vxe-table
+            v-show="showTable"
+            ref="xTable"
+            export-config
+            :keep-source="true"
+            align="center"
+            :loading="loading"
+            highlight-current-row
+            auto-resize
+            border
+            resizable
+            max-height="600px"
+            :edit-rules="rules"
+            :data="tableData"
+            :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+          >
+            <vxe-table-column title="AGC_AVC信息" align="center">
+              <vxe-table-column
+                field="name"
+                title="名称"
+                width="8%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="modelNumber"
+                title="型号"
+                width="8%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="manufacturer"
+                title="生产商"
+                width="8%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                width="150px"
+                field="installationTime"
+                title="安装时间"
+                align="center"
+                :edit-render="{props: {type: 'default'}}"
+              >
+                <template v-slot:edit="{ row }">
+                  <el-date-picker
+                    v-model="row.installationTime"
+                    size="mini"
+                    style="width: 100%"
+                    type="date"
+                    placeholder="选择日期:"
+                  />
+                </template>
+                <template v-slot="{ row }">{{ timestampToTime(row.installationTime) }}</template>
+              </vxe-table-column>
+              <vxe-table-column
+                field="report"
+                title="是否上报"
+                width="8%"
+                align="center"
+                :edit-render="{name: '$select', options: trueOrFalse}"
+              />
+              <vxe-table-column
+                field="interval"
+                title="入库间隔(单位s)"
+                width="8%"
+                align="center"
+                :edit-render="{name: '$select', options: intervals}"
+              />
+              <vxe-table-column title="操作" fixed="right" width="10%" align="center">
+                <template v-slot="{ row }">
+                  <template v-if="$refs.xTable.isActiveByRow(row)">
+                    <el-button
+                      type="success"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium"
+                      icon="el-icon-edit"
+                      @click="editSave(row)"
+                    >保存
+                    </el-button>
+                    <el-button
+                      class="cancel-btn"
+                      icon="el-icon-refresh"
+                      type="warning"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium"
+                      @click="cancelRowEvent(row)"
+                    >取消
+                    </el-button>
+                  </template>
+                  <template v-else>
+                    <el-button
+                      :loading="loadButton"
+                      type="primary"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium "
+                      icon="el-icon-edit"
+                      @click="editRowEvent(row)"
+                    >编辑
+                    </el-button>
+                    <el-button
+                      :loading="loadButton"
+                      type="danger"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium "
+                      icon="el-icon-delete"
+                      @click="deleteRowEvent(row)"
+                    >删除
+                    </el-button>
+                  </template>
+                </template>
+              </vxe-table-column>
+
+            </vxe-table-column>
+          </vxe-table>
+          <vxe-pager
+            v-show="!loadButton&&showTable"
+            perfect
+            :current-page.sync="currentPage"
+            :page-size.sync="pageSize"
+            :total="total"
+            :page-sizes="[10,50,100]"
+            :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+          />
+        </div>
+      </el-card>
+    </div>
+    <!-- 删除提示框 -->
+    <el-dialog :visible.sync="delVisible" title="提示" width="300px" center>
+      <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="deleteCancel()">取 消</el-button>
+        <el-button type="primary" @click="deleteInfo()">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Index',
+  data() {
+    const checkName = (rule, value, callback) => {
+      var s6 = this.tableData
+      if (value == null || value === '') {
+        callback(new Error('请填写逆变器名称'))
+      }
+      for (let i = 0; i < s6.length; i++) {
+        if (this.mid == '' || this.mid == undefined) {
+          // 新增
+          if ((value == s6[i].name)) {
+            callback(new Error('名称不能重复'))
+          }
+        } else {
+          // 修改
+          if (this.mid != s6[i].id) {
+            if ((value == s6[i].name)) {
+              callback(new Error('名称不能重复'))
+            }
+          }
+        }
+      }
+      callback()
+    }
+    return {
+      mid: '',
+      rowId: '',
+      loadButton: false,
+      loading: false,
+      showTable: true,
+      delVisible: false,
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      tableData: [],
+      trueOrFalse: [{ label: '是', value: true }, { label: '否', value: false }],
+      intervals: [{ key: 0, value: 0, label: '不入库' },{ key: 60, value: 60, label: '一分钟' }, { key: 900, value: 900, label: '十五分钟' }],
+      // 是否为编辑
+      isEdit: false,
+      // 表单验证规则
+      rules: {
+        name: [
+          { required: true, validator: checkName }
+        ],
+        modelNumber: [
+          { required: true, message: 'AGC_AVC型号不能为空', trigger: 'blur' }
+        ],
+        manufacturer: [
+          { required: true, message: '制造商名称不能为空' }
+        ],
+        installationTime: [
+          { required: true, message: '请选择日期' }
+        ],
+        report: [
+          { required: true, message: '是否上报不能为空', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  mounted() {
+    this.getInfo()
+  },
+  methods: {
+    getInfo() {
+      this.$axios.get('/agcAvcInfo').then(res => {
+        console.log(res.data)
+        this.tableData = res.data
+        this.total = res.data.length
+        this.loading = false
+      }).catch((error) => {
+        this.$message.error('获取AGC_AVC信息出错' + error)
+      })
+    },
+    editSave(row) {
+      this.$refs.xTable.validate(this.$refs.xTable.getCurrentRecord(),valid => {
+        if (valid) {
+          this.$refs.xTable.clearActived().then(() => {
+            if (this.isEdit) {
+              console.log(row)
+              row.equipmentType = row.equipmentType.code
+              // 编辑保存
+              this.$axios.put('/agcAvcInfo/', row).then(res => {
+                this.$message({
+                  message: '修改成功',
+                  type: 'success'
+                })
+                this.loadButton = false
+                this.getInfo()
+              }).catch((error) => {
+                this.$refs.xTable.setActiveRow(row)
+                this.$message.error('修改AGC_AVC出错' + error)
+              })
+            } else {
+              // 新增保存
+              this.$axios.post('/agcAvcInfo/', row).then(res => {
+                this.$message({
+                  message: '保存成功',
+                  type: 'success'
+                })
+                this.loadButton = false
+                this.getInfo()
+              }).catch((error) => {
+                this.$refs.xTable.setActiveRow(row)
+                this.$message.error('保存AGC_AVC出错' + error)
+              })
+            }
+          })
+        } else {
+          this.$XModal.message({status: 'error', message: '校验不通过!'})
+        }
+      })
+    },
+    editRowEvent(row) {
+      this.mid = row.id
+      this.isEdit = true
+      this.loadButton = true
+      this.$refs.xTable.setActiveRow(row)
+    },
+    insertEvent(row) {
+      this.mid = ''
+      this.showTable = true
+      this.piadd = false
+      this.isEdit = false
+      this.loadButton = true
+      this.$refs.xTable.insert({ interval: 60 }).then(({ row }) => this.$refs.xTable.setActiveRow(row))
+    },
+    exportDataEvent() {
+      this.$axios.get('/agcAvcInfo/').then(res => {
+        const data = res.data
+        this.$refs.xTable.exportData({
+          filename: 'AGC_AVC信息',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data
+        })
+      })
+    },
+    cancelRowEvent(row) {
+      const xTable = this.$refs.xTable
+      xTable.clearActived().then(() => {
+        // 还原行数据
+        if (this.isEdit) {
+          //编辑
+          xTable.revertData(row)
+        } else {
+          //新增
+          xTable.remove(row)
+        }
+        this.loadButton = false;
+      })
+    },
+    // 删除场站信息
+    deleteRowEvent(row) {
+      this.rowId = row.id
+      this.delVisible = true
+    },
+    deleteCancel() {
+      this.delVisible = false
+    },
+    deleteInfo() {
+      this.$axios.delete('/agcAvcInfo/' + this.rowId).then(res => {
+        this.$message({
+          message: '删除成功',
+          type: 'success'
+        })
+        this.delVisible = false
+        this.getInfo()
+      }).catch((error) => {
+        this.$message.error('删除AGC_AVC信息出错' + error)
+      })
+    },
+    timestampToTime(time) {
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      return Y + M + D
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 332 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/boosterStationInfo/index.vue

@@ -0,0 +1,332 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-carde">
+      <div slot="header" class="clearfix">
+        <span>升压站信息</span>
+        <el-button
+          style="float: right;padding:3px 10px 3px 3px;"
+          type="text"
+          :loading="btnLonding"
+          @click="exportDataEvent"
+        >导出数据
+        </el-button>
+      </div>
+      <el-button
+        type="primary"
+        size="small"
+        style="round-clip: 10px"
+        :loading="btnLonding"
+        @click="insertEvent"
+      >新增</el-button>
+      <div style="padding-top: 10px">
+        <vxe-table
+          ref="xTable"
+          v-show="showTable"
+          :keep-source="true"
+          align="center"
+          :loading="loading"
+          highlight-current-row
+          auto-resize
+          border
+          resizable
+          show-overflow
+          :edit-rules="rules"
+          :data="tableData"
+          :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+        >
+          <vxe-table-column title="升压站信息">
+            <vxe-table-column
+              field="name"
+              title="名称"
+              width="13%"
+              :edit-render="{name: '$input', attrs: {type: 'text'}}"
+            />
+            <vxe-table-column
+              field="manufacturer"
+              title="生产商"
+              width="13%"
+              :edit-render="{name: '$input', attrs: {type: 'text'}}"
+            />
+            <vxe-table-column
+              field="modelNumber"
+              title="型号"
+              width="13%"
+              :edit-render="{name: '$input', attrs: {type: 'text'}}"
+            />
+            <vxe-table-column
+              min-width="60px"
+              width="15%"
+              field="installationTime"
+              title="安装时间"
+              :edit-render="{props: {type: 'default'}}"
+            >
+              <template v-slot:edit="{ row }">
+                <el-date-picker
+                  v-model="row.installationTime"
+                  size="mini"
+                  style="width: 100%"
+                  type="date"
+                  placeholder="选择日期:"
+                />
+              </template>
+              <template v-slot="{ row }">{{ timestampToTime(row.installationTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column
+              field="report"
+              title="是否上报"
+              width="8%"
+              :edit-render="{name: '$select', options: options}"
+            />
+            <vxe-table-column
+              field="interval"
+              title="入库间隔(单位s)"
+              width="10%"
+              align="center"
+              :edit-render="{name: '$select', options: intervals}"
+            />
+            <vxe-table-column min-width="30px" width="15%" title="操作">
+              <template v-slot="{ row }">
+                <template v-if="$refs.xTable.isActiveByRow(row)">
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="saveRowEvent(row)"
+                  >保存
+                  </el-button>
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="cancelRowEvent(row)"
+                  >取消
+                  </el-button>
+                </template>
+                <template v-else>
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    :loading="btnLonding"
+                    @click="editRowEvent(row)"
+                  >编辑
+                  </el-button>
+                  <el-button
+                    type="danger"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-delete"
+                    :loading="btnLonding"
+                    @click="deleteRowEvent(row)"
+                  >删除
+                  </el-button>
+                </template>
+              </template>
+            </vxe-table-column>
+          </vxe-table-column>
+        </vxe-table>
+        <vxe-pager
+          v-show="!btnLonding&&showTable"
+          perfect
+          :current-page.sync="currentPage"
+          :page-size.sync="pageSize"
+          :total="total"
+          :page-sizes="[10,50,100]"
+          :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+        />
+      </div>
+      <!-- 删除提示框 -->
+      <el-dialog :visible.sync="delVisible" title="提示" width="300px" center>
+        <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+        <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="deleteCancel()">取 消</el-button>
+        <el-button type="primary" @click="deleteInfo()">确 定</el-button>
+      </span>
+      </el-dialog>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Index',
+  data() {
+    const checkName = (rule, value, callback) => {
+      var s6 = this.tableData
+      if (value == null || value === '') {
+        callback(new Error('请填写逆变器名称'))
+      }
+      for (let i = 0; i < s6.length; i++) {
+        if (this.mid == '' || this.mid == undefined) {
+          // 新增
+          if ((value == s6[i].name)) {
+            callback(new Error('名称不能重复'))
+          }
+        } else {
+          // 修改
+          if (this.mid != s6[i].id) {
+            if ((value == s6[i].name)) {
+              callback(new Error('名称不能重复'))
+            }
+          }
+        }
+      }
+      callback()
+    }
+    return {
+      mid: '',
+      rowId: '',
+      btnLonding: false,
+      loading: false,
+      showTable: true,
+      delVisible: false,
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      tableData: [],
+      options: [{ label: '是', value: true }, { label: '否', value: false }],
+      intervals: [{ key: 0, value: 0, label: '不入库' },{ key: 60, value: 60, label: '一分钟' }, { key: 900, value: 900, label: '十五分钟' }],
+      // 是否为编辑
+      isEdit: false,
+      // 表单验证规则
+      rules: {
+        name: [
+          { required: true, validator: checkName }
+        ],
+        modelNumber: [
+          { required: true, message: '升压站型号不能为空', trigger: 'blur' }
+        ],
+        manufacturer: [
+          { required: true, message: '制造商名称不能为空' }
+        ],
+        installationTime: [
+          { required: true, message: '请选择日期' }
+        ],
+        report: [
+          { required: true, message: '是否上报不能为空', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  mounted() {
+    this.getInfo()
+  },
+  methods: {
+    getInfo() {
+      this.$axios.get('/boosterStationInfo/').then(res => {
+        this.tableData = res.data
+      })
+    },
+    saveRowEvent(row) {
+      this.$refs.xTable.validate(this.$refs.xTable.getCurrentRecord(),valid => {
+        if (valid) {
+          this.$refs.xTable.clearActived().then(() => {
+            if (this.isEdit) {
+              console.log(row)
+              row.equipmentType = row.equipmentType.code
+              // 编辑保存
+              this.$axios.put('/boosterStationInfo/', row).then(res => {
+                this.$message({
+                  message: '修改成功',
+                  type: 'success'
+                })
+                this.btnLonding = false
+                this.getInfo()
+              }).catch((error) => {
+                this.$refs.xTable.setActiveRow(row)
+                this.$message.error('修改升压站出错' + error)
+              })
+            } else {
+              // 新增保存
+              this.$axios.post('/boosterStationInfo/', row).then(res => {
+                this.$message({
+                  message: '保存成功',
+                  type: 'success'
+                })
+                this.btnLonding = false
+                this.getInfo()
+              }).catch((error) => {
+                this.$refs.xTable.setActiveRow(row)
+                this.$message.error('保存升压站出错' + error)
+              })
+            }
+          })
+        } else {
+          this.$XModal.message({status: 'error', message: '校验不通过!'})
+        }
+      })
+    },
+    editRowEvent(row) {
+      this.mid = row.id
+      this.isEdit = true
+      this.btnLonding = true
+      this.$refs.xTable.setActiveRow(row)
+    },
+    insertEvent(row) {
+      this.mid = ''
+      this.showTable = true
+      this.piadd = false
+      this.isEdit = false
+      this.btnLonding = true
+      this.$refs.xTable.insert({ interval: 60 }).then(({ row }) => this.$refs.xTable.setActiveRow(row))
+    },
+    exportDataEvent() {
+      this.$axios.get('/boosterStationInfo/').then(res => {
+        const data = res.data
+        this.$refs.xTable.exportData({
+          filename: '升压站信息',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data
+        })
+      })
+    },
+    cancelRowEvent(row) {
+      const xTable = this.$refs.xTable
+      xTable.clearActived().then(() => {
+        // 还原行数据
+        if (this.isEdit) {
+          //编辑
+          xTable.revertData(row)
+        } else {
+          //新增
+          xTable.remove(row)
+        }
+        this.loadButton = false;
+      })
+    },
+    // 删除场站信息
+    deleteRowEvent(row) {
+      this.rowId = row.id
+      this.delVisible = true
+    },
+    deleteCancel() {
+      this.delVisible = false
+    },
+    deleteInfo() {
+      this.$axios.delete('/boosterStationInfo/' + this.rowId).then(res => {
+        this.$message({
+          message: '删除成功',
+          type: 'success'
+        })
+        this.delVisible = false
+        this.getInfo()
+      }).catch((error) => {
+        this.$message.error('删除升压站信息出错' + error)
+      })
+    },
+    timestampToTime(time) {
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      return Y + M + D
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 309 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/communicationEquipment/index.vue

@@ -0,0 +1,309 @@
+<template>
+  <div class="app-container">
+    <el-card>
+      <div slot="header" class="clearfix">
+        <b><span>通信设备</span></b>
+      </div>
+      <div style="padding-bottom: 1%">
+        <el-button type="primary" size="small" @click="insertCommEq">添加</el-button>
+        <el-button type="primary" size="small" @click="exportDataEvent">导出</el-button>
+      </div>
+      <vxe-table
+        ref="xTable"
+        :loading="loading"
+        border
+        highlight-current-row
+        keep-source
+        auto-resize
+        resizable
+        :data="tableData"
+      >
+        <vxe-table-column title="通信设备基本信息" align="center">
+          <vxe-table-column field="name" title="通信设备" align="center" width="12%" />
+          <vxe-table-column field="netCardName" title="网卡名称" align="center" />
+          <vxe-table-column field="netCardIp" title="网卡IP地址" align="center" />
+          <vxe-table-column field="netmask" title="子网掩码" align="center" />
+          <vxe-table-column field="gateway" title="网关" align="center" />
+          <vxe-table-column field="port" title="端口" align="center" />
+          <vxe-table-column field="systemName" title="功率预测系统" align="center" width="12%"/>
+          <vxe-table-column field="systemNetCardName" title="系统网卡名称" align="center" />
+          <vxe-table-column field="systemNetCardIp" title="系统网卡IP地址" align="center" />
+          <vxe-table-column field="systemNetmask" title="系统子网掩码" align="center" />
+          <vxe-table-column field="systemGateway" title="系统网关" align="center" />
+          <vxe-table-column field="systemPort" title="系统端口" align="center" />
+          <vxe-table-column title="操作" width="10%" align="center">
+            <template v-slot="{ row }">
+              <el-button
+                type="primary"
+                style="padding: 3px 4px 3px 4px;margin: 2px;"
+                size="medium "
+                icon="el-icon-edit"
+                @click="editRowEvent(row)"
+              >编辑
+              </el-button>
+              <el-button
+                type="danger"
+                style="padding: 3px 4px 3px 4px;margin: 2px;"
+                size="medium "
+                icon="el-icon-edit"
+                @click="deleteCommEq(row)"
+              >删除
+              </el-button>
+            </template>
+          </vxe-table-column>
+        </vxe-table-column>
+      </vxe-table>
+      <vxe-modal v-model="showEdit" :title="selectRow ? '编辑&保存' : '新增&保存'" width="800" min-width="600" min-height="400" resize @close="closeModel">
+        <vxe-form
+          ref="form"
+          class="my-form2"
+          title-align="right"
+          title-width="35%"
+          :data="formData"
+          :rules="formRules"
+          @submit="saveForm"
+        >
+          <vxe-form-item title="通信设备" field="name" span="12" >
+            <vxe-select v-model="formData.name" placeholder="请输入名称" @change="fanChange">
+              <vxe-option v-for="list in nameList" :value="list.value" :label="list.label" />
+            </vxe-select>
+          </vxe-form-item>
+          <vxe-form-item title="网卡名称" field="netCardName" span="12">
+            <vxe-input v-model="formData.netCardName" placeholder="请输入网卡名称" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="网卡IP地址" field="netCardIp" span="12">
+            <vxe-input v-model="formData.netCardIp" placeholder="请输入网卡IP地址" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="子网掩码" field="netmask" span="12">
+            <vxe-input v-model="formData.netmask" placeholder="请输入子网掩码" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="网关" field="gateway" span="12">
+            <vxe-input v-model="formData.gateway" placeholder="请输入网关" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="端口" field="port" span="12">
+            <vxe-input v-model="formData.port" placeholder="请输入端口" clearable />
+          </vxe-form-item>
+
+          <vxe-form-item title="通信设备" field="systemName" span="12" v-show="fanShow">
+            <vxe-select v-model="formData.systemName" placeholder="请输入名称">
+              <vxe-option v-for="list in systemNameList" :value="list.value" :label="list.label" />
+            </vxe-select>
+          </vxe-form-item>
+          <vxe-form-item title="通信设备Ip" field="systemNetCardIp" span="12" v-show="fanShow">
+            <vxe-input v-model="formData.systemNetCardIp" placeholder="请输入网卡IP地址" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="功率预测系统" field="systemName" span="12" title-width="40%" v-show="sysShow">
+            <vxe-select v-model="formData.systemName" placeholder="请输入名称">
+              <vxe-option v-for="list in systemNameList" :value="list.value" :label="list.label" />
+            </vxe-select>
+          </vxe-form-item>
+          <vxe-form-item title="预测系统网卡名称" field="systemNetCardName" span="12" v-show="sysShow">
+            <vxe-input v-model="formData.systemNetCardName" placeholder="请输入网卡名称" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="预测系统网卡IP地址" field="systemNetCardIp" span="12" title-width="40%" v-show="sysShow">
+            <vxe-input v-model="formData.systemNetCardIp" placeholder="请输入网卡IP地址" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="预测系统子网掩码" field="systemNetmask" span="12" v-show="sysShow">
+            <vxe-input v-model="formData.systemNetmask" placeholder="请输入子网掩码" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="预测系统网关" field="systemGateway" span="12" title-width="40%" v-show="sysShow">
+            <vxe-input v-model="formData.systemGateway" placeholder="请输入网关" clearable />
+          </vxe-form-item>
+          <vxe-form-item title="预测系统端口" field="systemPort" span="12" v-show="sysShow">
+            <vxe-input v-model="formData.systemPort" placeholder="请输入端口" clearable />
+          </vxe-form-item>
+          <vxe-form-item align="center" span="24">
+            <vxe-button status="primary" type="submit">保存</vxe-button>
+            <vxe-button type="reset">重置</vxe-button>
+          </vxe-form-item>
+
+        </vxe-form>
+      </vxe-modal>
+
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Index',
+  data() {
+    return {
+      loading: false,
+      showEdit: false,
+      selectRow: false,
+      fanShow: false,
+      sysShow: true,
+      saveFlag: 1, // 保存 1, 修改 2
+      modelId: '',
+      tableData: [],
+      nameList: [
+        { value: '前台PC机', label: '前台PC机' },
+        { value: '势态感知', label: '势态感知' },
+        { value: '对时系统', label: '对时系统' },
+        { value: '反向隔离内网真实IP', label: '反向隔离内网真实IP' },
+        { value: '反向隔离外网真实IP', label: '反向隔离外网真实IP' },
+        { value: '网络安全监测', label: '网络安全监测' },
+        { value: '入侵系统监测', label: '入侵系统监测' },
+        { value: '环境监测仪串口接入', label: '环境监测仪串口接入' },
+        { value: '测风塔串口接入', label: '测风塔串口接入' },
+        { value: '测风塔网口接入', label: '测风塔网口接入' },
+        { value: '外网Internet IP', label: '外网Internet IP' }
+      ],
+      systemNameList: [
+        { value: '外网服务器', label: '外网服务器' },
+        { value: '内网服务器', label: '内网服务器' }
+      ],
+      formData: {
+        name: '',
+        netCardName: '',
+        netCardIp: '',
+        netmask: '',
+        gateway: '',
+        port: '',
+        systemName: '',
+        systemNetCardName: '',
+        systemNetCardIp: '',
+        systemNetmask: '',
+        systemGateway: '',
+        systemPort: ''
+      },
+      formRules: {
+        name: [{ required: true, message: '不能为空' }],
+        netCardIp: [{
+          pattern: /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))/,
+          message: '请填写正确的ip'
+        }],
+        netmask: [{
+          pattern: /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))/,
+          message: '请填写正确的子网掩码'
+        }],
+        gateway: [{
+          pattern: /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))/,
+          message: '请填写正确的网关'
+        }],
+        port: [{
+          pattern: /^[0-9]*$/,
+          message: '请填写正确的端口'
+        }],
+        systemName: [{ required: true, message: '不能为空' }],
+        systemNetCardIp: [{
+          pattern: /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))/,
+          message: '请填写正确的ip'
+        }],
+        systemNetmask: [{
+          pattern: /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))/,
+          message: '请填写正确的子网掩码'
+        }],
+        systemGateway: [{
+          pattern: /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))/,
+          message: '请填写正确的网关'
+        }],
+        systemPort: [{
+          pattern: /^[0-9]*$/,
+          message: '请填写正确的端口'
+        }]
+      }
+    }
+  },
+  mounted() {
+    this.getCommEq()
+  },
+  created() {
+
+  },
+  methods: {
+    getCommEq() {
+      this.$axios.get('/communicationEquipment/').then((res) => {
+        this.tableData = res.data
+      })
+    },
+    editRowEvent(row) {
+      // console.log(row)
+      this.saveFlag = 2
+      this.selectRow = true
+      this.showEdit = true
+      this.modelId = row.id
+      this.formData = {
+        id: '',
+        name: row.name,
+        netCardName: row.netCardName,
+        netCardIp: row.netCardIp,
+        netmask: row.netmask,
+        gateway: row.gateway,
+        port: row.port,
+        systemName: row.systemName,
+        systemNetCardName: row.systemNetCardName,
+        systemNetCardIp: row.systemNetCardIp,
+        systemNetmask: row.systemNetmask,
+        systemGateway: row.systemGateway,
+        systemPort: row.systemPort
+      }
+    },
+    insertCommEq() {
+      this.saveFlag = 1
+      this.showEdit = true
+      this.sysShow = true
+      this.fanShow = false
+    },
+    saveForm() {
+      if (this.saveFlag === 1) {
+        this.$axios.post('/communicationEquipment/', this.formData).then((res) => {
+          this.closeModel()
+          this.getCommEq()
+        })
+      } else {
+        this.formData.id = this.modelId
+        this.$axios.put('/communicationEquipment/', this.formData).then((res) => {
+          this.closeModel()
+          this.getCommEq()
+        })
+      }
+    },
+    deleteCommEq(row) {
+      this.$XModal.confirm('您确定要删除该数据?').then(type => {
+        if (type === 'confirm') {
+          this.$axios.delete('/communicationEquipment/' + row.id).then((res) => {
+            this.getCommEq()
+          })
+        }
+      })
+    },
+    closeModel() {
+      this.$refs.form.reset()
+      this.selectRow = false
+      this.showEdit = false
+    },
+    fanChange(val) {
+      this.sysShow = false
+      this.fanShow = true
+      if(val.value == '反向隔离内网真实IP'){
+        this.systemNameList = [{value:'反向隔离内网虚拟IP',label:'反向隔离内网虚拟IP'}]
+        this.formData.systemName = this.systemNameList[0].value
+      }else if(val.value == '反向隔离外网真实IP') {
+        this.systemNameList = [{value:'反向隔离虚拟真实IP',label:'反向隔离外网虚拟IP'}]
+        this.formData.systemName = this.systemNameList[0].value
+      }else{
+        this.sysShow = true
+        this.fanShow = false
+        this.systemNameList = [
+          { value: '外网服务器', label: '外网服务器' },
+          { value: '内网服务器', label: '内网服务器' }
+        ]
+        this.formData.systemName = ''
+      }
+    },
+    exportDataEvent() {
+      this.$axios.post('/communicationEquipment/export', {}, {
+        fileName: '',
+        responseType: 'blob'// 用于解决中文乱码
+      }).then((response) => {
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 361 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/dataLimitation/index.vue

@@ -0,0 +1,361 @@
+<template>
+  <div class="app-container">
+      <el-card class="box-carde">
+        <div slot="header" class="clearfix">
+          <span>上报数据限制设置</span>
+
+        </div>
+    <el-container>
+      <el-header style="padding: 0px;display:flex;justify-content:space-between;align-items: center">
+        <div style="display: inline">
+          <el-date-picker
+            v-model="dateTime"
+            type="date"
+            placeholder="选择日期"/>
+          <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-search" @click="searchEmp">搜索
+          </el-button>
+          <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-plus" @click="saveManual('form')">保存
+          </el-button>
+        </div>
+      </el-header>
+
+      <el-tabs v-model="activeName" type="card">
+
+        <div>
+          <el-table
+            v-loading="tableLoading"
+            :data="form.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+            height="550px"
+            border
+            stripe
+            size="mini"
+            style="width: 100%">
+            <el-table-column
+              prop="time1"
+              header-align="center"
+              align="center"
+              label="时间">
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMin1"
+              header-align="center"
+              align="center"
+              label="下限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMin1" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMax1"
+              header-align="center"
+              align="center"
+              label="上限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMax1" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="time2"
+              align="center"
+              header-align="center"
+              label="时间"/>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMin2"
+              align="center"
+              header-align="center"
+              label="下限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMin2" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMax2"
+              align="center"
+              header-align="center"
+              label="上限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMax2" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="time3"
+              align="center"
+              header-align="center"
+              label="时间"/>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMin3"
+              align="center"
+              header-align="center"
+              label="下限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMin3" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMax3"
+              align="center"
+              header-align="center"
+              label="上限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMax3" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="time4"
+              align="center"
+              header-align="center"
+              label="时间"/>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMin4"
+              align="center"
+              header-align="center"
+              label="下限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMin4" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :show-overflow-tooltip="true"
+              prop="dataMax4"
+              align="center"
+              header-align="center"
+              label="上限值">
+              <template slot-scope="scope">
+                <vxe-input type="number" v-model="scope.row.dataMax4" size="small"  style="width:100%" min="0" :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </el-tabs>
+    </el-container>
+    </el-card>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      ef: '',
+      // 日期选择器
+      dateTime:  new Date(new Date().toLocaleDateString()),
+      // 场站信息存储对象
+      elTableData: [],
+      efiId: '',
+      emps: [],
+      dataList: [],
+      keywords: '',
+      jType: '',
+      dialogTitle: '',
+      multipleSelection: [],
+      downLoading: false,
+      depTextColor: '#c0c4cc',
+      totalCount: -1,
+      currentPage: 1,
+      pageSize:25,
+      dialogVisible: false,
+      tableLoading: false,
+      fileParsing: [],
+      activeName: 'first',
+      ultraValueData: [],
+      shortValueData: [],
+      realPowerValueData: [],
+      capacityUnit: '',
+      capacity: '',
+      shortValueDate: [],
+      form:[{
+        time1:'',
+        dataMax1:'',
+        dataMin1:'',
+        time2:'',
+        dataMax2:'',
+        dataMin2:'',
+        time3:'',
+        dataMax3:'',
+        dataMin3:'',
+        time4:'',
+        dataMax4:'',
+        dataMin4:'',
+        lastModifyTime:''
+      }],
+
+    }
+  },
+  created: function() {
+    this.getEl()
+
+  },
+
+  methods: {
+    getEl(){
+      this.$axios.get("/electricField/").then(res => {
+        this.capacity = res.data.capacity
+        this.queryManualIntervention()
+        // console.log("获取场站信息成功")
+      })
+    },
+    timestampToTimess(time) {
+      const date = new Date(time)// 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return Y + M + D + H + m
+    },
+    timestampToTimes(time) {
+      const a = parseInt(time)
+      const date = new Date(a) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return Y + M + D
+    },
+    // 根据类型获取class
+    queryManualIntervention() {
+      this.tableLoading =true
+      const a=[];
+      const b=[];
+      const c=[];
+      const d=[];
+      for(var i=0; i<=23;i++){
+        if(i<10){
+          a.push(this.timestampToTimes(Math.round(this.dateTime))+"0"+i+":00")
+          b.push(this.timestampToTimes(Math.round(this.dateTime))+"0"+i+":15")
+          c.push(this.timestampToTimes(Math.round(this.dateTime))+"0"+i+":30")
+          d.push(this.timestampToTimes(Math.round(this.dateTime))+"0"+i+":45")
+        }else{
+          a.push(this.timestampToTimes(Math.round(this.dateTime))+i+":00")
+          b.push(this.timestampToTimes(Math.round(this.dateTime))+i+":15")
+          c.push(this.timestampToTimes(Math.round(this.dateTime))+i+":30")
+          d.push(this.timestampToTimes(Math.round(this.dateTime))+i+":45")
+        }
+      }
+      for(var i=0; i<a.length;i++){
+        this.form[i]={
+          time1:a[i],
+          dataMax1:"",
+          dataMin1:"",
+          time2:b[i],
+          dataMax2:"",
+          dataMin2:"",
+          time3:c[i],
+          dataMax3:"",
+          dataMin3:"",
+          time4:d[i],
+          dataMax4:"",
+          dataMin4:"",
+        }
+      }
+      const startTime = Math.round(this.dateTime)
+      const endTime = Math.round(this.dateTime) + 60 * 60 * 24 * 1000 - 1
+      this.$axios.get('/dataLimitationController/queryDataLimitationServicea/'+startTime+'/'+endTime).then((res) => {
+        for(var i=0;i<res.data.length;i++){
+          for(var j=0;j<this.form.length;j++){
+            if(this.timestampToTimess(res.data[i].limitTime) == this.form[j].time1){
+              this.form[j].dataMax1 = res.data[i].upperLimit
+              this.form[j].dataMin1 = res.data[i].lowerLimit
+            }
+            if(this.timestampToTimess(res.data[i].limitTime) == this.form[j].time2){
+              this.form[j].dataMax2 = res.data[i].upperLimit
+              this.form[j].dataMin2 = res.data[i].lowerLimit
+            }
+            if(this.timestampToTimess(res.data[i].limitTime) == this.form[j].time3){
+              this.form[j].dataMax3 = res.data[i].upperLimit
+              this.form[j].dataMin3 = res.data[i].lowerLimit
+            }
+            if(this.timestampToTimess(res.data[i].limitTime) == this.form[j].time4){
+              this.form[j].dataMax4 = res.data[i].upperLimit
+              this.form[j].dataMin4 = res.data[i].lowerLimit
+            }
+          }
+        }
+        this.totalCount = this.form.length
+        this.tableLoading = false
+      }).catch((error) => {
+        this.$message.error('查询人工干预数据出错' + error)
+      })
+    },
+
+    saveManual(formName){
+      this.tableLoading = true
+      this.form[0].lastModifyTime = Math.round(this.dateTime)
+      this.$axios.put('/dataLimitationController/saveDataLimitation', this.form).then((res) => {
+        this.$message.success(`保存成功`)
+        this.queryManualIntervention()
+        this.tableLoading = false
+      }).catch((error) => {
+        this.$message.error('保存数据限制出错' + error)
+      })
+    },
+    // 时间格式化
+    dateFormat(row, column) {
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD HH:mm:ss')// 使用moment插件进行日期格式化
+    },
+    timestampToTime(time) {
+      const a = parseInt(time)
+      const date = new Date(a) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return H + m
+    },
+
+    searchEmp() {
+      this.queryManualIntervention()
+    },
+    currentChange(currentChange) {
+      this.currentPage = currentChange
+      this.queryManualIntervention()
+    },
+    handleSizeChange(handleSizeChange) {
+      this.pageSize = handleSizeChange
+      this.queryManualIntervention()
+    },
+    changeTime(row, column) {
+      return this.timestampToTime(row.time1)
+    },
+  }
+}
+</script>
+<style>
+.el-dialog__body {
+  padding-top: 0px;
+  padding-bottom: 0px;
+}
+
+.slide-fade-enter-active {
+  transition: all .8s ease;
+}
+
+.slide-fade-leave-active {
+  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
+}
+
+.slide-fade-enter, .slide-fade-leave-to {
+  transform: translateX(10px);
+  opacity: 0;
+}
+
+#MyUltraShortTermChart {
+  width: 100%;
+  height: 400px;
+}
+</style>

+ 274 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/dataLimitationLog/index.vue

@@ -0,0 +1,274 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-carde">
+      <div slot="header" class="clearfix">
+        <span>上报数据限制记录</span>
+      </div>
+      <el-container>
+        <el-header style="padding: 0px;display:flex;justify-content:space-between;align-items: center">
+          <div style="display: inline">
+            <el-date-picker
+              v-model="dateTime"
+              type="daterange"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"/>
+            <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-search" @click="searchEmp">搜索
+            </el-button>
+          </div>
+        </el-header>
+            <div>
+              <el-table
+                v-loading="tableLoading"
+                :data="emps.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+                height="500px"
+                border
+                stripe
+                size="mini"
+                style="width: 100%">
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  :formatter="dateFormat"
+                  prop="operationalTime"
+                  align="center"
+                  header-align="center"
+                  label="操作时间"/>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  prop="limitTime"
+                  :formatter="dateFormats"
+                  align="center"
+                  header-align="center"
+                  label="干预数据日期"/>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  :formatter="operationalFormat"
+                  prop="operationalStatus"
+                  align="center"
+                  header-align="center"
+                  label="操作类型"/>
+                <el-table-column
+                  header-align="center"
+                  align="center"
+                  label="操作">
+                  <template slot-scope="scope">
+                    <el-button
+                      style="padding: 3px 4px 3px 4px;margin: 2px"
+                      size="mini"
+                      icon="el-icon-document"
+                      @click="showErrorInfo(scope.row)">查看内容
+                    </el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div style="display: flex;justify-content: space-between;margin: 2px;float: right">
+                <el-pagination
+                  :current-page="currentPage"
+                  :page-size="pageSize"
+                  :page-sizes="[10, 25, 50, 100]"
+                  :total="totalCount"
+                  background
+                  layout="total, sizes, prev, pager, next, jumper"
+                  @size-change="handleSizeChange"
+                  @current-change="currentChange"/>
+              </div>
+            </div>
+        <el-form
+          ref="addEmpForm"
+          :model="emp"
+          style="margin: 0px;padding: 0px;"
+          label-width="100px">
+          <el-dialog
+            :close-on-click-modal="false"
+            :visible.sync="dialogVisibleErrorInfo"
+            title="具体信息"
+            style="padding: 0px;"
+            label-width="1px"
+            width="1000px">
+            <el-input
+              v-model="emp.operationalContext"
+              :rows="20"
+              type="textarea"
+              style="width: 100%" />
+            <span
+              slot="footer"
+              class="dialog-footer">
+            <el-button
+              size="mini"
+              @click="cancelEidtEI">关 闭</el-button>
+          </span>
+          </el-dialog>
+        </el-form>
+      </el-container>
+    </el-card>
+  </div>
+</template>
+<script>
+export default {
+
+
+  data() {
+    return {
+      ef: '',
+      // 日期选择器
+      dateTime: [new Date(new Date().toLocaleDateString()).getTime() , new Date(new Date().toLocaleDateString()).getTime() + 60 * 60 * 24 * 3 * 1000],
+      // 场站信息存储对象
+      elTableData: [],
+      efiId: '',
+      emps: [],
+      keywords: '',
+      jType: '',
+      dialogTitle: '',
+      dialogVisibleErrorInfo: false,
+      multipleSelection: [],
+      depTextColor: '#c0c4cc',
+      totalCount: -1,
+      currentPage: 1,
+      pageSize: 10,
+      dialogVisible: false,
+      tableLoading: false,
+      fileParsing: [],
+      activeName: 'first',
+      forecastAndManualInterventionData: [],
+      forecastValueCorrectAfterData: [],
+      forecastTimeData: [],
+      capacityUnit: '',
+      capacity:'',
+      emp:{
+        operationalContext:''
+      }
+    }
+  },
+  mounted: function() {
+    this.fileType=0;
+    this.getAllClass()
+  },
+
+  methods: {
+    cancelEidtEI () {
+      this.dialogVisibleErrorInfo = false
+    },
+    showErrorInfo (row) {
+      this.emp.operationalContext = row.operationalContext
+      this.dialogVisibleErrorInfo = true
+    },
+    // // 获取后台所有场站信息
+    // getELData() {
+    //   this.$axios.post('/electricFieldInfo/findAllElectricFieldInfo').then((res) => {
+    //     this.elTableData = res.data
+    //     this.efiId = res.data[0].id
+    //     // this.capacityUnit=res.data[0].capacityUnit
+    //     // this.type = 'NWP'
+    //     this.getAllClass()
+    //   })
+    // },
+    // 表格所属场站,ID转换场站名称
+    formatStation(row, column) {
+      let belongTo = '未知的所属场站'
+      for (let i = 0; i < this.elTableData.length; i++) {
+        if (row.efiId === this.elTableData[i].id) {
+          belongTo = this.elTableData[i].name
+        }
+      }
+      return belongTo
+    },
+    // 根据类型获取class
+    getAllClass() {
+      if (this.dateTime == '' || this.dateTime == null) {
+        alert('请选择时间')
+        return
+      }
+      this.$axios.get('/electricField/').then((res) => {
+        this.capacityUnit = 'mw'
+        this.capacity=res.data.capacity
+      })
+      const formData = new FormData()
+      const startTime = Math.round(this.dateTime[0])
+      const endTime = Math.round(this.dateTime[1]) + 60 * 60 * 24 * 1000 - 1
+      this.$axios.get('/dataLimitationLogController/queryDataLimitationLog/'+ startTime +'/' + endTime ).then((res) => {
+        this.emps = res.data
+        this.totalCount = this.emps.length
+      })
+
+    },
+    operationalFormat(row, column) {
+      let belongTo = '未知的文件类型场站'
+      for (let i = 0; i < this.emps.length; i++) {
+        if (row.operationalStatus =="0") {
+          belongTo ="新增"
+        }
+        if (row.operationalStatus =="1") {
+          belongTo ="修改"
+        }
+        if (row.operationalStatus =="2") {
+          belongTo ="删除"
+        }
+      }
+      return belongTo
+    },
+    // 时间格式化
+    dateFormat(row, column) {
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD HH:mm:ss')// 使用moment插件进行日期格式化
+    },
+    timestampToTime(time) {
+      const a = parseInt(time)
+      const date = new Date(a) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()) + ':'
+      const s = (date.getSeconds() < 10 ? '0' + (date.getSeconds()) : date.getSeconds())
+      return Y + M + D + H + m + s
+    },
+    // 时间格式化
+    dateFormats(row, column) {
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD')// 使用moment插件进行日期格式化
+    },
+    searchEmp() {
+      this.getAllClass()
+    },
+    currentChange(currentChange) {
+      this.currentPage = currentChange
+      this.getAllClass()
+    },
+    handleSizeChange(handleSizeChange) {
+      this.pageSize = handleSizeChange
+      this.getAllClass()
+    },
+    // 自定义排序
+  }
+}
+</script>
+<style>
+.el-dialog__body {
+  padding-top: 0px;
+  padding-bottom: 0px;
+}
+
+.slide-fade-enter-active {
+  transition: all .8s ease;
+}
+
+.slide-fade-leave-active {
+  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
+}
+
+.slide-fade-enter, .slide-fade-leave-to {
+  transform: translateX(10px);
+  opacity: 0;
+}
+
+#MyShortTermRealCharts {
+  width: 100%;
+  height: 500px;
+}
+</style>

+ 511 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/electricField/index.vue

@@ -0,0 +1,511 @@
+<template>
+  <div class="app-container">
+
+    <el-card>
+      <div slot="header" class="clearfix">
+        <b><span>场站信息</span></b>
+        <el-button
+          v-loading="tableLoading"
+          style="float: right;padding:3px 10px 3px 3px;"
+          type="text"
+          @click="exportDataEvent"
+        >导出数据
+        </el-button>
+      </div>
+
+      <div class="filter-container">
+        <el-button
+          class="filter-item"
+          type="primary"
+          icon="el-icon-edit"
+          style="margin-bottom: 10px;"
+          @click="downloadFile"
+        >
+          导出配置文件
+        </el-button>
+      </div>
+      <div>
+        <el-card style="width: 80%;margin-left: 10%">
+          <div style="height: 30px" />
+          <el-form ref="form" v-loading="tableLoading" :model="form" :rules="rules">
+
+            <el-row style="gutter:30;text-align: left" class="row-bg el-row-two" justify="space-between">
+              <el-col :span="12">
+                <el-form-item label="场站编号:" prop="stationCode" label-width="180px">
+                  <el-input v-model="form.stationCode" placeholder="必填项" maxlength="32" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="上报省调:" label-width="180px" prop="provinceEnum">
+                  <el-select
+                    v-model="form.provinceEnum"
+                    placeholder="请选择"
+                    filterable
+                    style="width: 100%"
+                  >
+                    <el-option
+                      v-for="item in this.provinceTypeList"
+                      :key="item.key"
+                      :label="item.label"
+                      :value="item.value"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row style="gutter:30" class="row-bg el-row-two" justify="space-between">
+
+              <el-col :span="12">
+                <el-form-item label="场站标识:" prop="sign" label-width="180px">
+                  <el-input v-model="form.sign" placeholder="必填项" maxlength="32" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="场站名称:" prop="name" label-width="180px">
+                  <el-input v-model="form.name" placeholder="必填项" maxlength="85" />
+                </el-form-item>
+              </el-col>
+
+            </el-row>
+
+            <el-row style="gutter:30" class="row-bg el-row-two" justify="space-between">
+              <el-col :span="12">
+                <el-form-item
+                  label="别名(上网变电站名称):"
+                  prop="netSubstationName"
+                  label-width="180px"
+                  style="min-width: 110px"
+                >
+                  <el-input v-model="form.netSubstationName" placeholder="必填项" maxlength="32" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item
+                  label="总装机容量(MW):"
+                  prop="capacity"
+                  label-width="180px"
+                  style="min-width: 110px"
+                >
+                  <el-input v-model="form.capacity" placeholder="必填项" maxlength="85" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <el-row style="gutter:30" class="row-bg el-row-two" justify="space-between">
+              <el-col :span="12">
+                <el-form-item
+                  label="光伏装机容量(MW):"
+                  prop="backupE"
+                  label-width="180px"
+                  style="min-width: 110px"
+                >
+                  <el-input v-model="form.backupE" placeholder="必填项" maxlength="85" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item
+                  label="风电装机容量(MW):"
+                  prop="backupF"
+                  label-width="180px"
+                  style="min-width: 110px"
+                >
+                  <el-input v-model="form.backupF" placeholder="必填项" maxlength="85" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <el-row style="gutter:30" class="row-bg el-row-two" justify="space-between">
+              <el-col :span="12">
+                <el-form-item label="场站类型:" label-width="180px" prop="electricFieldTypeEnum" style="display:none">
+                  <el-select
+                    v-model="form.electricFieldTypeEnum"
+                    placeholder="请选择"
+                    filterable
+                    style="width: 100%"
+                  >
+                    <el-option
+                      v-for="item in this.electricTypeList"
+                      :key="item.key"
+                      :label="item.label"
+                      :value="item.value"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="场站状态111:" prop="stationStatusEnum" label-width="180px" style="display:none">
+                  <el-input v-model="form.stationStatusEnum.message" placeholder="运维中" maxlength="32" readonly />
+                </el-form-item>
+              </el-col>
+
+            </el-row>
+
+            <el-row style="gutter:30" class="row-bg" justify="space-between">
+              <el-col :span="12">
+                <el-form-item label="入库间隔(单位s):" prop="interval" label-width="180px">
+                  <el-select
+                    v-model="form.interval"
+                    placeholder="请选择"
+                    filterable
+                    style="width: 100%"
+                  >
+                    <el-option
+                      v-for="item in this.intervals"
+                      :key="item.key"
+                      :label="item.label"
+                      :value="item.value"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="并网设备数:" prop="gridCE" label-width="180px">
+                  <el-input v-model="form.gridCE" placeholder="必填项" maxlength="10" />
+                </el-form-item>
+              </el-col>
+
+            </el-row>
+            <el-row style="gutter:30" class="row-bg" justify="space-between">
+
+              <el-col :span="12">
+                <el-form-item label="场站经度:" prop="longitude" label-width="180px">
+                  <el-input v-model="form.longitude" placeholder="必填项" maxlength="10" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="场站纬度:" prop="latitude" label-width="180px">
+                  <el-input v-model="form.latitude" placeholder="必填项" maxlength="10" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <el-row style="gutter:30" class="row-bg" justify="space-between">
+
+              <el-col :span="12">
+                <el-form-item label="场站海拔:" prop="altitude" label-width="180px">
+                  <el-input v-model="form.altitude" placeholder="必填项" maxlength="10" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="场站所属公司:" prop="company" label-width="180px">
+                  <el-input v-model="form.company" placeholder="必填项" maxlength="100" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <el-row style="gutter:30" class="row-bg" justify="space-between">
+
+              <el-col :span="12">
+                <el-form-item label="场站位置:" prop="location" label-width="180px">
+                  <el-input v-model="form.location" placeholder="必填项" maxlength="100" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="场站面积:" prop="area" label-width="180px">
+                  <el-input v-model="form.area" placeholder="必填项" maxlength="10" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row style="gutter:30" class="row-bg" justify="space-between">
+
+              <el-col :span="12">
+                <el-form-item label="所属地调(文本):" prop="backupA" label-width="180px">
+                  <el-input v-model="form.backupA" placeholder="选填项" maxlength="20" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="合同结束日期:" prop="backupC" label-width="180px" style="display:none">
+                  <!--<el-date-picker-->
+                    <!--style="width: 100%" -->
+                    <!--v-model="form.contractEndTime"-->
+                    <!--:clearable="false"-->
+                    <!--:editable="false"-->
+                    <!--type="datetime"-->
+                    <!--value-format="timestamp"-->
+                    <!--placeholder="选择合同结束日期">-->
+                  <!--</el-date-picker>-->
+                  <el-date-picker
+                    :readonly="true"
+                    style="width: 100%"
+                    v-model="form.backupC"
+                    type="date"
+                    placeholder="选择合同结束日期">
+                  </el-date-picker>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+          <div style="text-align: center">
+            <span slot="footer" class="dialog-footer">
+              <el-button
+                v-loading="tableLoading"
+                type="primary"
+                style="width: 15%"
+                @click="saveElectricField('form')"
+              >保 存</el-button>
+              <!--                    <span style="width: 200px"></span>-->
+              <!--                  <el-button v-loading="tableLoading" @click="deleteRowEvent(form)">删 除</el-button>-->
+              <!--                    <el-button v-loading="tableLoading" @click="resetForm('form')">重 置</el-button>-->
+            </span>
+          </div>
+          <div style="height: 30px" />
+        </el-card>
+        <div style="height: 20px" />
+      </div>
+    </el-card>
+
+  </div>
+
+</template>
+
+<script>
+
+export default {
+
+  data() {
+    return {
+      addVisible: false,
+      loading: true,
+      tableLoading: false,
+      tableData: {},
+      datas: {},
+      showCreateButton: false,
+      provinceTypeList: [],
+      electricTypeList: [],
+      form: { stationStatusEnum: {
+        code: 'E1',
+        message: '未运维'
+      }},
+      electricFieldStatrsEnumList: [],
+      intervals: [{ key: 0, value: 0, label: '不入库' },{ key: 60, value: 60, label: '一分钟' }, { key: 900, value: 900, label: '十五分钟' }],
+      // 表单验证规则
+      rules: {
+        stationCode: [
+          { required: true, message: '请填写场站编号' }
+          // {message: '输入过长', max: 8}
+        ],
+        sign: [
+          { required: true, message: '请填写标识名称' }
+          // {message: '输入过长', max: 15}
+        ],
+        name: [
+          { required: true, message: '请填写场站名称' }
+          // {message: '输入过长', max: 50}
+        ],
+        backupE: [
+          { required: true, message: '请正确填写光伏装机容量' },
+          { pattern: /^\d+(\.\d{0,2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 10}
+        ],
+        backupF: [
+          { required: true, message: '请正确填写风电装机容量' },
+          { pattern: /^\d+(\.\d{0,2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 10}
+        ],
+        capacity: [
+          { required: true, message: '请正确填写装机容量' },
+          { pattern: /^\d+(\.\d{0,2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 10}
+        ],
+        gridCE: [
+          { required: true, message: '请正确填写并网设备数' },
+          { pattern: /^[0-9]*[1-9][0-9]*$/, message: '只能输入正整数数字' }
+          // {message: '输入过长', max: 5}
+        ],
+        longitude: [
+          { required: true, message: '请正确填写场站经度' },
+          {pattern: /^\d+(\.\d{1,6})?$/, message: '只能输入正数数字或带小数点6位以内的数字'},
+          {pattern: /^-?((0|1?[0-8]?[0-9]?)(([.][0-9]{1,10})?)|180(([.][0]{1,10})?))$/, message: '请输入正确的经度'}
+          // {message: '输入过长', max: 10}
+        ],
+        latitude: [
+          { required: true, message: '请正确填写场站纬度' },
+          {pattern: /^\d+(\.\d{1,6})?$/, message: '只能输入正数数字或带小数点6位以内的数字'},
+          {pattern: /^-?((0|[1-8]?[0-9]?)(([.][0-9]{1,10})?)|90(([.][0]{1,10})?))$/, message: '请输入正确的纬度'}
+          // {message: '输入过长', max: 10}
+        ],
+        provinceEnum: [
+          { required: true, message: '请选择上报省调类型' }
+        ],
+        netSubstationName: [
+          { required: true, message: '请填写别名' }
+          // {message: '输入过长', max: 50}
+        ],
+        electricFieldTypeEnum: [
+          { required: true, message: '请填写场站类型' }
+        ],
+        interval: [
+          { required: true, message: '请填写入库时间' }],
+        altitude: [
+          { required: true, message: '请填写场站海拔' },
+          { pattern: /^\d+(\.\d{2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 50}
+        ],
+        company: [
+          { required: true, message: '请填写场站所属公司' }
+          // {message: '输入过长', max: 50}
+        ],
+        location: [
+          { required: true, message: '请填写场站位置' }
+          // {message: '输入过长', max: 50}
+        ],
+        area: [
+          { required: true, message: '请填写场站面积' },
+          { pattern: /^\d+(\.\d{2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 50}
+        ]
+      }
+    }
+  },
+  created() {
+    this.getData()
+  },
+  mounted() {
+  },
+  methods: {
+    getData() {
+      Promise.all([this.getEl(), this.getProvince(), this.getElType(), this.getElectricFieldStatrsEnum()]).then((results) => {
+        this.provinceTypeList = results[1]
+        this.electricTypeList = results[2]
+        // window.console.log(this.provinceTypeList)
+        this.electricFieldStatrsEnumList = results[3]
+        if (results[0] == null) {
+          this.showCreateButton = true
+          this.form = {
+            stationStatusEnum: {
+              code: 'E1',
+              message: '未运维'
+            }
+          }
+          this.tableData = {
+            stationStatusEnum: {
+              code: 'E1',
+              message: '未运维'
+            }
+          }
+        } else {
+          this.form = results[0]
+          this.tableData = results[0]
+          console.log(this.tableData)
+        }
+
+        this.loading = false
+      })
+    },
+    getElType() {
+      const a = this.$axios
+      return new Promise(function(resolve, reject) {
+        a.get('/electricField/getElType').then(res => {
+          resolve(res.data)
+          // console.log("获取场站类型枚举信息成功")
+        }).catch((error) => {
+          this.$message.error('获取场站类型枚举信息出错' + error)
+        })
+      })
+    },
+    getProvince() {
+      const a = this.$axios
+      return new Promise(function(resolve, reject) {
+        a.get('/electricField/getProvince').then(res => {
+          resolve(res.data)
+          // console.log("获取上报类型枚举信息成功")
+        }).catch((error) => {
+          this.$message.error('获取上报类型枚举信息出错' + error)
+        })
+      })
+    },
+    getEl() {
+      const a = this.$axios
+      return new Promise(function(resolve, reject) {
+        a.get('/electricField/').then(res => {
+          resolve(res.data)
+          // console.log("获取场站信息成功")
+        }).catch((error) => {
+          this.$message.error('获取场站信息出错' + error)
+        })
+      })
+    },
+    getElectricFieldStatrsEnum() {
+      const a = this.$axios
+      return new Promise(function(resolve, reject) {
+        a.get('/electricField/getElectricFieldStatrsEnum').then(res => {
+          resolve(res.data)
+          // console.log("获取场站状态信息成功")
+        }).catch((error) => {
+          this.$message.error('获取场站状态信息出错' + error)
+        })
+      })
+    },
+    downloadFile() {
+      this.loading = true
+      this.$axios.post('/electricField/dataDownload', {
+        responseType: 'blob'// 用于解决中文乱码
+      }).then((response) => {
+        this.loading = false
+      }).catch((error) => {
+        console.log(error)
+        this.loading = false
+      })
+    },
+    saveElectricField(formName) {
+      // window.console.log(this.form)
+      this.form.stationStatusEnum = 'E1'
+      const a = this.$axios
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.tableLoading = true
+          a.post('/electricField/', this.form).then(res => {
+            this.$message({
+              message: '保存成功',
+              type: 'success'
+            })
+            this.addVisible = false
+            this.tableLoading = false
+            this.getData()
+            this.showCreateButton = false
+          }).catch((error) => {
+            this.$message.error('添加场站信息出错' + error)
+            this.tableLoading = false
+          })
+        } else {
+          this.$message.error('添加信息未通过验证,请查看提示')
+          return false
+        }
+      })
+    },
+    // 删除场站信息
+    deleteRowEvent(row) {
+      this.$XModal.confirm('删除不可恢复!!!确认删除?').then(type => {
+        if (type === 'confirm') {
+          this.$axios.delete('/electricField/' + row.stationCode).then(res => {
+            this.$message({
+              message: '删除成功',
+              type: 'success'
+            })
+            this.getData()
+          }).catch((error) => {
+            this.$message.error('删除场站信息出错' + error)
+          })
+        }
+      })
+    },
+    // 重置表单
+    resetForm(formName) {
+      this.$refs[formName].resetFields()
+      this.form = this.tableData
+    },
+    exportDataEvent() {
+      this.loading = true
+      this.$axios.post('/electricField/export', {}, {
+        responseType: 'blob'// 用于解决中文乱码
+      }).then((response) => {
+        this.loading = false
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 391 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/holidayCalendar/index.vue

@@ -0,0 +1,391 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-carde">
+      <div slot="header" class="clearfix">
+        <span>假期日历</span>
+        <el-button style="float: right;padding:3px 10px 3px 3px;" type="text" @click="exportDataEvent">导出数据
+        </el-button>
+      </div>
+      <el-button type="primary" size="small" @click="insertEvent" style="round-clip: 10px"
+                 :loading="btnLonding">新增
+      </el-button><!--v-loading="false"-->
+      <div style="padding-top: 10px">
+        <vxe-table
+          :keep-source="true"
+          align="center"
+          :loading="loading"
+          ref="xTable"
+          auto-resize
+          border
+          resizable
+          highlight-current-row
+          show-overflow
+          :edit-rules="rules"
+          :data="tableData"
+          v-show="showTable"
+          :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+        >
+          <vxe-table-column title="假期日历信息">
+            <vxe-table-column field="name" title="假期名称"
+                              :edit-render="{name:'$input',attrs:{type:'text',size:'3'}}"></vxe-table-column>
+            <vxe-table-column field="holidayTypeEnum" title="假期类型"
+                              :edit-render="{name: '$select', options: options}"></vxe-table-column>
+            <vxe-table-column field="timeStartEnd" width="350px" title="开始时间-结束时间"
+                              :edit-render="{props: {type: 'default'}}">
+              <template v-slot:edit="{ row }">
+                <el-date-picker
+                  style="width: 100%"
+                  v-model="timeStartEnd"
+                  type="daterange"
+                  value-format="timestamp"
+                  range-separator="至"
+                  start-placeholder="开始日期"
+                  end-placeholder="结束日期"
+                  :picker-options="pickerOptions1"/>
+              </template>
+              <template v-slot="{ row }">{{ timestampToTime(row.startTime) }} 至 {{timestampToTime(row.endTime) }}
+              </template>
+            </vxe-table-column>
+            <vxe-table-column field="days" title="天数"></vxe-table-column>
+            <vxe-table-column width="15%" title="操作" align="center">
+              <template v-slot="{ row }">
+                <template v-if="$refs.xTable.isActiveByRow(row)">
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px;"
+                    type="success"
+                    size="medium"
+                    icon="el-icon-edit"
+                    :loading="saveLoding"
+                    @click="saveCheck(row)">保存
+                  </el-button>
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    class="cancel-btn"
+                    icon="el-icon-refresh"
+                    type="warning"
+                    @click="cancelRowEvent(row)">取消
+                  </el-button>
+                </template>
+                <template v-else>
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="editRowEvent(row)"
+                    :loading="btnLonding">编辑
+                  </el-button>
+                  <el-button
+                    type="danger"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-delete"
+                    @click="removeEvent(row)"
+                    :loading=btnLonding>删除
+                  </el-button>
+                </template>
+              </template>
+            </vxe-table-column>
+          </vxe-table-column>
+        </vxe-table>
+        <vxe-pager
+          perfect
+          :current-page.sync="currentPage"
+          :page-size.sync="pageSize"
+          :total="total"
+          :page-sizes=[10,50,100]
+          :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+          @page-change="handlePageChange"
+          v-show="!btnLonding&&showTable"
+        >
+        </vxe-pager>
+      </div>
+
+    </el-card>
+  </div>
+</template>
+<script>
+
+  export default {
+    data() {
+      return {
+        checkSign: false,
+        checkTableDate: [],
+        holidayType: '',
+        timeStartEnd: [],
+        loading: false,
+        saveLoding: false,
+        showTable: true,
+        btnLonding: false,
+        tableData: [],
+        currentPage: 1,
+        pageSize: 10,
+        total: 0,
+        options: [
+          {
+            value: "E1",
+            label: "假期"
+          },
+          {
+            value: "E2",
+            label: "特殊工作日"
+          }
+        ],
+        //表单验证规则
+        rules: {
+          name: [
+            {required: true, message: '假期名称不能为空'},
+            {min: 1, max: 9, message: '假期名称长度在 3 到 9 个字符'}
+          ],
+          holidayTypeEnum: [
+            {required: true, message: '请选择假期类型'}
+          ]
+        },
+        //当前日期之前日期不可选
+        pickerOptions1: {
+          disabledDate(time) {
+            // return time.getTime() < Date.now() - 8.64e7;
+            return time.getTime() <= Date.now();
+          }
+        }
+      }
+    },
+    created() {
+      this.getAll()
+    },
+    methods: {
+      checkDate(v1, v2, v3, v4) {
+        let result = false
+        const item = ''
+        // 修改时所需便利集合
+        const v5 = []
+        // 所有假期存放集合
+
+        // 在修改时,把当前在所有信息集合里要修改的信息去掉(如果不去掉会影响校验)
+        for (let i = 0; i < v2.length; i++) {
+          if (v2[i].id == v4.id) {
+            continue
+          }
+          v5.push(v2[i])
+        }
+        // 在编辑假期信息时 当前时间已存在于所有假期信息中,为防止当前时间被校验,在所有假期信息集合中删除该记录。
+        // 编辑修改
+        if (v3) {
+          // 假期修改
+          for (let i = 0; i < v5.length; i++) {
+            if (v1[0] >= v5[i].startTime && v1[0] <= v5[i].endTime) {
+              result = true
+              break
+            }
+            if (v1[1] >= v5[i].startTime && v1[1] <= v5[i].endTime) {
+              result = true
+              break
+            }
+            if (v1[0] <= v5[i].startTime && v1[1] >= v5[i].endTime) {
+              result = true
+              break
+            }
+            if (v1[0] >= v5[i].startTime && v1[1] <= v5[i].endTime) {
+              result = true
+              break
+            }
+          }
+        } else {
+          for (let i = 0; i < v2.length; i++) {
+            if (v1[0] >= v2[i].startTime && v1[0] <= v2[i].endTime) {
+              result = true
+              break
+            }
+            if (v1[1] >= v2[i].startTime && v1[1] <= v2[i].endTime) {
+              result = true
+              break
+            }
+            if (v1[0] <= v2[i].startTime && v1[1] >= v2[i].endTime) {
+              result = true
+              break
+            }
+            if (v1[0] >= v2[i].startTime && v1[1] <= v2[i].endTime) {
+              result = true
+              break
+            }
+          }
+        }
+        return result
+      },
+      getAll() {
+        this.loading = true
+        this.saveLoding = false
+        this.btnLonding = false
+        this.$axios.get("/holidayCalendar/" + this.currentPage + '/' + this.pageSize).then(response => {
+          if (response.data.content == "") {
+            this.showTable = false
+          } else {
+            this.showTable = true
+          }
+          this.tableData = response.data.content
+          this.total = response.data.totalElements
+          this.loading = false
+        })
+        this.$axios.get("/holidayCalendar/").then(response => {
+          this.checkTableDate = response.data
+        })
+      },
+      //编辑框改变
+      editRowEvent(row) {
+        this.checkSign = true
+        this.timeStartEnd = [row.startTime, row.endTime]
+        this.btnLonding = true
+        this.$refs.xTable.setActiveRow(row)
+      },
+      //数据校验
+      saveCheck(row) {
+        this.$refs.xTable.validate(row, callback => {
+          if (this.timeStartEnd == null) {
+            // callback(new Error('开始结束日期不能为空'))
+            this.$XModal.message({status: 'error', message: '开始结束日期不能为空!'})
+          } else {
+            // 判断开始时间是否是大于当前日期
+            if (row.holidayTypeEnum == 'E2') {
+              const startTime = new Date(this.timeStartEnd[0])
+              const endTime = new Date(this.timeStartEnd[1])
+              // 判断开始时间和结束时间是否是周六周日,切是相邻的周六周日
+              //if ((startTime.getDay() == 6 || startTime.getDay() == 0||endTime.getDay() == 6 || endTime.getDay() == 0) && (this.timeStartEnd[1] - this.timeStartEnd[0] <= 1000 * 60 * 60 * 24 * 2)) {
+              if ((startTime.getDay() < 1 || startTime.getDay() > 5) && (endTime.getDay() < 1 || endTime.getDay() > 5) && (this.timeStartEnd[1] - this.timeStartEnd[0] <= 1000 * 60 * 60 * 24 * 2)) {
+                if (this.checkDate(this.timeStartEnd, this.checkTableDate, this.checkSign, row)) {
+                  this.$XModal.message({status: 'error', message: '当前假日已存在于已有的假期中!'})
+                } else {
+                  callback:this.saveRowEvent(row)
+                }
+              } else {
+
+                this.$XModal.message({status: 'error', message: '特殊工作日必须为周六周日且开始日期和结束日期相邻!'})
+              }
+            } else {
+              if (this.checkDate(this.timeStartEnd, this.checkTableDate, this.checkSign, row)) {
+                this.$XModal.message({status: 'error', message: '当前假日已存在于已有的假期中!'})
+              } else {
+                callback:this.saveRowEvent(row)
+              }
+            }
+          }
+        })
+      },
+      //保存数据操作
+      saveRowEvent(row) {
+        row.startTime = this.timeStartEnd[0]
+        row.endTime = this.timeStartEnd[1]
+        row.days = (row.endTime -row.startTime) / 86400000 + 1
+        this.saveLoding = true
+
+        this.$refs.xTable.validate(valid => {
+          if (valid) {
+            if (row.id == undefined) {
+
+              this.$axios.post("/holidayCalendar/", row).then(response => {
+                this.btnLonding = false
+                this.saveLoding = false
+                this.getAll()
+                this.$XModal.message({status: 'warning', message: response.message})
+
+              })
+            } else {
+              this.$axios.put("/holidayCalendar/", row).then(response => {
+                this.saveLoding = false
+                this.getAll()
+                this.$XModal.message({status: 'warning', message: response.message})
+              })
+            }
+          } else {
+            this.$XModal.message({status: 'error', message: '校验不通过!'})
+            this.saveLoding = false
+          }
+        })
+      },
+      //取消编辑或添加操作
+      cancelRowEvent(row) {
+        this.saveLoding = false
+        this.btnLonding = false
+        const xTable = this.$refs.xTable
+        if (row.id == undefined) {
+          this.$refs.xTable.remove(row)
+        } else {
+          xTable.clearActived().then(() => {
+            // 还原行数据
+            xTable.revertData(row)
+          })
+        }
+      },
+      //删除数据
+      removeEvent(row) {
+        this.$XModal.confirm('您确定要删除该数据?').then(type => {
+          if (type === 'confirm') {
+            this.$axios.delete("/holidayCalendar/", {data: row}).then(response => {
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.getAll()
+            })
+          }
+        })
+      },
+      //时间戳日期转换器
+      timestampToTime(time) {
+        const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+        const Y = date.getFullYear() + '-'
+        const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+        const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+        return Y + M + D
+      },
+      //新增编辑框
+      insertEvent() {
+        this.timeStartEnd = []
+        this.showTable = true
+        this.btnLonding = true
+        this.checkSign = false
+        this.$refs.xTable.insert()
+          .then(({row}) => this.$refs.xTable.setActiveRow(row))
+      },
+      //分页触发事件
+      handlePageChange({currentPage, pageSize}) {
+        this.currentPage = currentPage
+        this.pageSize = pageSize
+        this.getAll()
+      },
+      //导出
+      exportDataEvent() {
+        this.loading = true
+        this.$axios.get('/holidayCalendar/').then(res => {
+          const data = res.data.content
+          this.$refs.xTable.exportData({
+            filename: '假期日历信息',
+            type: 'csv',
+            isHeader: true,
+            isFooter: true,
+            data
+          })
+          this.loading = false
+          // this.loading = false
+        }).catch(e => {
+          this.loading = false
+
+        })
+      },
+      //导入
+      // importDataEvent() {
+      //   this.$refs.xTable.importData({types: ['csv']})
+      // }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .app-container {
+    /*left: 0;*/
+    /*width: 100%;*/
+    /*min-height: 100%;*/
+    /*height: auto;*/
+    /*background-image: linear-gradient(25deg, #05362d, #145d44, #24875d, #35b477)*/
+  }
+
+  .pagination {
+    margin: 20px 0;
+    text-align: right;
+  }
+</style>

+ 569 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/inverterInfo/index.vue

@@ -0,0 +1,569 @@
+<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
+
+
+  <div class="app-container">
+
+    <div>
+      <el-card>
+        <div slot="header" class="clearfix">
+          <b><span>逆变器信息</span></b>
+          <el-button :loading="loadButton" style="float: right;padding:3px 10px 3px 3px;" type="text"
+                     @click="exportDataEvent">导出数据
+          </el-button>
+        </div>
+        <el-button type="primary" @click="insertEvent" size="small" style="round-clip: 10px" :loading="loadButton">添加</el-button>
+        <el-button
+          size="small"
+          :loading="loadButton"
+          type="primary"
+          style="round-clip: 10px"
+          @click="piInsertEvent">
+          批量添加
+        </el-button>
+        <el-button
+          size="small"
+          :loading="loadButton"
+          type="danger"
+          style="round-clip: 10px"
+          @click="getSelectionEvent">
+          批量删除
+        </el-button>
+        <div style="padding-top: 10px">
+          <vxe-table
+            ref="xTable"
+            border
+            export-config
+            keep-source
+            auto-resize
+            resizable
+            highlight-current-row
+            :loading="loading"
+            :edit-rules="rules"
+            :data="tableData"
+            v-show="showTable"
+            :edit-config="{trigger: 'manual', mode: 'row',autoClear:false,showStatus: true,icon:'none'}">
+            <vxe-table-column title="逆变器信息" align="center">
+              <vxe-table-column type="checkbox" width="3%"></vxe-table-column>
+              <vxe-table-column field="name" title="名称" width="8%" align="center"
+                                :edit-render="{name: '$input', attrs: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="modelNumber" title="型号" width="8%" align="center"
+                                :edit-render="{name: '$input', attrs: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="manufacturer" title="生产商" width="8%" align="center"
+                                :edit-render="{name: '$input', attrs: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column width="150px" field="installationTime" title="安装时间" align="center"
+                                :edit-render="{props: {type: 'default'}}">
+                <template v-slot:edit="{ row }">
+                  <el-date-picker
+                    size="mini"
+                    v-model="row.installationTime"
+                    style="width: 100%"
+                    type="date"
+                    placeholder="选择日期:"
+                  />
+                </template>
+                <template v-slot="{ row }">{{ timestampToTime(row.installationTime) }}</template>
+              </vxe-table-column>
+              <vxe-table-column field="report" title="是否上报" width="8%" align="center"
+                                :edit-render="{name: '$select', options: trueOrFalse}"></vxe-table-column>
+              <vxe-table-column field="capacity" title="额定功率" width="8%" align="center"
+                                :edit-render="{name: '$input', attrs: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="efficiency" title="效率" width="8%" align="center"
+                                :edit-render="{name: '$input', attrs: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="box" title="箱式变压器" width="8%" align="center"
+                                :edit-render="{name: '$input', props: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="collectorCircuit" title="集电线路" width="8%" align="center"
+                                :edit-render="{name: '$input', props: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="batteryModel" title="光组型号" width="8%" align="center"
+                                :edit-render="{name: '$select', options: pvInfoMode}"></vxe-table-column>
+              <vxe-table-column field="batteryNumber" title="光组个数" width="8%" align="center"
+                                :edit-render="{name: '$input', props: {type: 'number'}}"></vxe-table-column>
+              <vxe-table-column field="groupSeries" title="是否组串" width="6%" align="center"
+                                :edit-render="{name: '$select', options: trueOrFalse}"></vxe-table-column>
+              <vxe-table-column field="sample" title="是否样板" width="6%" align="center"
+                                :edit-render="{name: '$select', options: trueOrFalse}"></vxe-table-column>
+              <vxe-table-column field="backupA" width="10%" title="样板机编号"
+                :edit-render="{name: '$input',attrs:{type:'text'}}"/>
+              <vxe-table-column field="backupB" title="经度" width="8%" align="center"
+                                :edit-render="{name: '$input', props: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="backupC" title="纬度" width="8%" align="center"
+                                :edit-render="{name: '$input', props: {type: 'text'}}"></vxe-table-column>
+              <vxe-table-column field="interval" title="入库间隔(单位s)" width="8%" align="center"
+                                :edit-render="{name: '$select', options: intervals}"></vxe-table-column>
+              <vxe-table-column title="操作" fixed="right" width="10%" align="center">
+                <template v-slot="{ row }">
+                  <template v-if="$refs.xTable.isActiveByRow(row)">
+                    <el-button
+                      type="success"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium"
+                      icon="el-icon-edit"
+                      @click="editSave(row)">保存
+                    </el-button>
+                    <el-button
+                      class="cancel-btn"
+                      icon="el-icon-refresh"
+                      type="warning"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium"
+                      @click="cancelRowEvent(row)">取消
+                    </el-button>
+                  </template>
+                  <template v-else>
+                    <el-button
+                      :loading="loadButton"
+                      type="primary"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium "
+                      icon="el-icon-edit"
+                      @click="editRowEvent(row)">编辑
+                    </el-button>
+                    <el-button
+                      :loading="loadButton"
+                      type="danger"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium "
+                      icon="el-icon-delete"
+                      @click="deleteRowEvent(row)">删除
+                    </el-button>
+                  </template>
+                </template>
+              </vxe-table-column>
+
+            </vxe-table-column>
+          </vxe-table>
+          <vxe-pager
+            perfect
+            :current-page.sync="currentPage"
+            :page-size.sync="pageSize"
+            :total="total"
+            :page-sizes=[10,50,100]
+            :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+            @page-change="handlePageChange"
+            v-show="!loadButton&&showTable"
+          >
+          </vxe-pager>
+        </div>
+      </el-card>
+    </div>
+    <!-- 删除提示框 -->
+    <el-dialog :visible.sync="delVisible" title="提示" width="300px" center>
+      <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+      <span slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="deleteCancel()">取 消</el-button>
+                <el-button type="primary" @click="deleteInfo()">确 定</el-button>
+                </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import request from '@/utils/request'
+
+  export default {
+    data() {
+      const checkName = (rule, value, callback) => {
+        var s6 = this.tableData
+        if (value == null || value === '') {
+          callback(new Error('请填写逆变器名称'))
+        }
+        for (let i = 0; i < s6.length; i++) {
+          if (this.id == '' || this.id == undefined) {
+            // 新增
+            if ((value == s6[i].name)) {
+              callback(new Error('名称不能重复'))
+            }
+          } else {
+            // 修改
+            if (this.id != s6[i].id) {
+              if ((value == s6[i].name)) {
+                callback(new Error('名称不能重复'))
+              }
+            }
+          }
+        }
+        callback()
+      }
+      return {
+        id:'',
+        currentPage: 1,
+        pageSize: 10,
+        total: 0,
+        isSample: '',
+        piadd: '',
+        loadButton: false,
+        loading: false,
+        rowId: "",
+        options: [{value: true, label: '样板机'},
+          {value: null, label: '所有'}],
+        option: "",
+        tableData: [],
+        showTable: true,
+        batchAddData: [],
+        delVisible: false,
+        //是否为编辑
+        isEdit: false,
+        pvInfo: [],
+        pvInfoMode: [],
+        pvRotationMode: [],
+        trueOrFalse: [{label: '是', value: true},
+          {label: '否', value: false}],
+        intervals:[{ key: 0, value: 0, label: '不入库' },{key:60,value:60,label:'一分钟'},{key:900,value:900,label:'十五分钟'}],
+        // 表单验证规则
+        rules: {
+          name: [
+            {required: true,  validator:checkName}
+          ],
+          modelNumber: [
+            {required: true, message: '逆变器型号不能为空', trigger: 'blur'}
+          ],
+          capacity: [
+            {required: true, message: '额定容量不能为空', trigger: 'blur'},
+            {pattern: /^\d+(\.\d{1,2})?$/, message: '只能输入正数数字或带小数点2位以内的数字'},
+          ],
+          efficiency: [
+            {required: true, message: '逆变器效率不能为空', trigger: 'blur'},
+            {pattern: /^\d+(\.\d{1,2})?$/, message: '只能输入正数数字或带小数点2位以内的数字'},
+          ],
+          backupB: [
+            { required: true, message: '请正确填写经度' },
+            {pattern: /^\d+(\.\d{1,4})?$/, message: '只能输入正数数字或带小数点4位以内的数字'}
+            // {message: '输入过长', max: 10}
+          ],
+          backupC: [
+            { required: true, message: '请正确填写纬度' },
+            {pattern: /^\d+(\.\d{1,4})?$/, message: '只能输入正数数字或带小数点4位以内的数字'}
+            // {message: '输入过长', max: 10}
+          ],
+          box: [
+            {required: true, message: '箱变不能为空', trigger: 'blur'},
+          ],
+          collectorCircuit: [
+            {required: true, message: '集电线不能为空', trigger: 'blur'},
+          ],
+          batteryModel: [
+            {required: true, message: '光组型号不能为空', trigger: 'blur'},
+          ],
+          batteryNumber: [
+            {required: true, message: '光组个数不能为空', trigger: 'blur'}
+          ],
+          groupSeries: [
+            {required: true, message: '是否组串式逆变器不能为空', trigger: 'change'}
+          ],
+          sample: [
+            {required: true, message: '是否样板机不能为空', trigger: 'change'}
+          ],
+          manufacturer: [
+            {required: true, message: '制造商名称不能为空'},
+          ],
+          installationTime: [
+            {required: true, message: '请选择日期'}
+          ],
+          report: [
+            {required: true, message: '是否上报不能为空', trigger: 'change'}
+          ]
+        }
+      }
+    },
+
+    created() {
+      this.getData(this.currentPage, this.pageSize)
+    },
+    activated() {
+      console.log("2激活activated钩子函数");
+    },
+    deactivated() {
+      console.log("2激活deactivated钩子函数");
+    },
+    methods: {
+      getData(currentPage, pageSize) {
+        this.loading = true
+        let url = currentPage + '/' + pageSize
+        this.$axios.get('/inverterInfo/' + url).then(res => {
+          if (res.data.content == "") {
+            this.showTable = false
+
+          } else {
+            this.showTable = true
+          }
+
+          this.tableData = res.data.content
+          this.total = res.data.totalElements
+          this.loading = false
+          // console.log(res.data)
+          this.getPvData()
+        }).catch((error) => {
+          this.$message.error('获取逆变器信息出错' + error)
+        })
+      },
+      handlePageChange({currentPage, pageSize}) {
+        this.currentPage = currentPage
+        this.pageSize = pageSize
+        this.getData(this.currentPage, this.pageSize)
+      },
+      getPvData() {
+        this.$axios.get('/pvModuleModel/').then(res => {
+          this.pvInfo = res.data
+          this.pvInfoMode = []
+          for (let i = 0; i < this.pvInfo.length; i++) {
+            const pInfo = {label: this.pvInfo[i].model, value: this.pvInfo[i].id}
+            this.pvInfoMode.push(pInfo)
+          }
+          console.log("获取所有光伏组件信息成功")
+        }).catch((error) => {
+          this.$message.error('获取逆变器信息出错' + error)
+        })
+      },
+      timestampToTime(time) {
+        const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+        const Y = date.getFullYear() + '-'
+        const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+        const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+        return Y + M + D
+      },
+      getSelectionEvent() {
+        let selectRecords = this.$refs.xTable.getCheckboxRecords()
+        if(selectRecords==""){
+          this.$message.error('请选择要删除的对象')
+          return
+        }
+        this.rowId = ''
+        for (let i = 0; i < selectRecords.length; i++)
+          this.rowId += (selectRecords[i].id + ",")
+        // console.log(this.rowId)
+        this.delVisible = true
+      },
+      piInsertEvent(row) {
+        this.piadd = true;
+        this.showTable = true
+        this.isEdit = false;
+        this.loadButton = true;
+        this.$refs.xTable.insert({interval: 60}).then(({row}) => this.$refs.xTable.setActiveRow(row))
+      },
+      insertEvent(row) {
+        this.id = ''
+        this.showTable = true
+        this.piadd = false;
+        this.isEdit = false;
+        this.loadButton = true;
+        this.$refs.xTable.insert({interval: 60}).then(({row}) => this.$refs.xTable.setActiveRow(row))
+      },
+      async insertBatchEvent(row) {
+        /*const { row: newRow } = await this.$refs.bTable.insert()*/
+        // alert(1111)
+        // 插入一条数据并触发校验
+        let {row: newRow} = await this.$refs.bTable.insertAt(row)
+        await this.$refs.bTable.setActiveRow(newRow)
+        // alert(2222)
+        /*          const errMap = await this.$refs.bTable.validate(newRow).catch(errMap => errMap)
+                  if (errMap) {
+
+                  }*/
+      },
+      async fullValidEvent() {
+        const errMap = await this.$refs.bTable.fullValidate().catch(errMap => errMap)
+        if (errMap) {
+          let msgList = []
+          Object.values(errMap).forEach(errList => {
+            errList.forEach(params => {
+              let {rowIndex, column, rules} = params
+              rules.forEach(rule => {
+                msgList.push(`第 ${rowIndex} 行 ${column.title} 校验错误:${rule.message}`)
+              })
+            })
+          })
+          this.$XModal.message({
+            status: 'error',
+            message: () => {
+              return [
+                < div
+            class
+              = "red"
+              style = "max-height: 400px;overflow: auto;" >
+                {
+                  msgList.map(msg => < div > {msg} < /div>)
+                }
+                < /div>
+            ]
+            }
+          })
+        } else {
+          this.$XModal.message({status: 'success', message: '校验成功!'})
+        }
+      },
+      saveBatchEvent() {
+        const {insertRecords, removeRecords, updateRecords} = this.$refs.bTable.getRecordset()
+        this.$XModal.alert(`insertRecords=${insertRecords.length} removeRecords=${removeRecords.length} updateRecords=${updateRecords.length}`)
+      },
+      editRowEvent(row) {
+        this.id = row.id
+        for (let i = 0; i < this.pvInfoMode.length; i++) {
+          if (row.batteryModel == this.pvInfoMode[i].value) {
+            row.batteryModel = this.pvInfoMode[i].value
+          }
+        }
+        this.isEdit = true
+        this.loadButton = true
+        this.$refs.xTable.setActiveRow(row)
+      },
+      //设计有判断是否批量添加,批量添加弹窗表明添加条数,然后批量生成保存
+      editSave(row) {
+        this.$refs.xTable.validate(this.$refs.xTable.getCurrentRecord(),valid => {
+
+          if (valid) {
+            if (this.piadd) {
+              const inviter = row
+              this.$prompt('请输入开始编号(例:1-0,其中0为开始值)', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                inputType: 'number',
+              }).then(({value}) => {
+                const startValue = value
+                if (startValue >= 0 && startValue != null) {
+                  this.$prompt('请输入添加数目', '提示', {
+                    confirmButtonText: '确定',
+                    cancelButtonText: '取消',
+                    inputType: 'number',
+                  }).then(({value}) => {
+                    const endValue = value
+                    if (endValue >= 0) {
+                      this.$axios.post('/inverterInfo/saveAll/' + startValue + '/' + endValue, row).then(res => {
+                        this.$message({
+                          message: '保存成功',
+                          type: 'success'
+                        })
+                        this.loadButton = false
+                        this.piadd = false
+                        this.getData(this.currentPage, this.pageSize)
+                      }).catch((error) => {
+                        this.$refs.xTable.setActiveRow(row)
+                        this.$message.error('保存逆变器出错' + error)
+                      })
+                    } else {
+                      alert("数量必须是正整数")
+                    }
+                  })
+                } else {
+                  alert("开始值必须是正整数并且不能为空")
+                }
+              })
+            } else {
+              this.$refs.xTable.clearActived().then(() => {
+                if (this.isEdit) {
+                  row.equipmentType = row.equipmentType.code
+                  //编辑保存
+                  this.$axios.put('/inverterInfo/', row).then(res => {
+                    this.$message({
+                      message: '修改成功',
+                      type: 'success'
+                    })
+                    this.loadButton = false;
+                    this.getData(this.currentPage, this.pageSize)
+                  }).catch((error) => {
+                    this.$refs.xTable.setActiveRow(row)
+                    this.$message.error('修改逆变器出错' + error)
+                  })
+                } else {
+                  //新增保存
+                  this.$axios.post('/inverterInfo/', row).then(res => {
+                    this.$message({
+                      message: '保存成功',
+                      type: 'success'
+                    })
+                    this.loadButton = false;
+                    this.getData(this.currentPage, this.pageSize)
+                  }).catch((error) => {
+                    this.$refs.xTable.setActiveRow(row)
+                    this.$message.error('保存逆变器出错' + error)
+                  })
+                }
+              })
+            }
+          } else {
+            this.$XModal.message({status: 'error', message: '校验不通过!'})
+          }
+        })
+      },
+      cancelRowEvent(row) {
+        const xTable = this.$refs.xTable
+        xTable.clearActived().then(() => {
+          // 还原行数据
+          if (this.isEdit) {
+            //编辑
+            xTable.revertData(row)
+          } else {
+            //新增
+            xTable.remove(row)
+          }
+          this.loadButton = false;
+        })
+      },
+      // 删除场站信息
+      deleteRowEvent(row) {
+        this.rowId = row.id
+        this.delVisible = true
+      },
+      deleteCancel() {
+        this.delVisible = false
+      },
+      deleteInfo() {
+        this.$axios.delete('/inverterInfo/' + '' + this.rowId).then(res => {
+          this.$message({
+            message: '删除成功',
+            type: 'success'
+          })
+          this.delVisible = false
+          this.getData(this.currentPage, this.pageSize)
+        }).catch((error) => {
+          this.$message.error('删除逆变器信息出错' + error)
+        })
+      },
+      exportDataEvent() {
+        // this.loading = true
+        this.$axios.get('/inverterInfo/').then(res => {
+          const data = res.data.content
+          this.$refs.xTable.exportData({
+            filename: '逆变器信息',
+            type: 'csv',
+            isHeader: true,
+            isFooter: true,
+            data
+          })
+          // this.loading = false
+        }).catch(e => {
+          // this.loading = false
+        })
+      },
+      /*dataUpload(item) {
+
+      },*/
+      importDataEvent(item) {
+        this.loadButton = true
+        const formData = new FormData()
+        formData.append('file', item.file)
+        // console.log('上传文件', item.file)
+        this.$axios.post('/console/importPvModuleModelCsv/', formData).then(res => {
+          this.$message({
+            message: '导入光伏组件信息成功',
+            type: 'success'
+          })
+        }).catch((error) => {
+          this.$message({
+            message: '导入光伏组件信息成功',
+            type: 'success'
+          })
+        })
+        this.loadButton = false
+        /*  this.$refs.xTable.importData({ types: ['csv'] })*/
+      },
+
+
+    }
+  }
+</script>
+
+<style scoped>
+
+  .my_table_insert .vxe-body--row.is--new {
+    background-color: #f1fdf1;
+  }
+</style>

+ 1031 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/manualInterventionData/index.vue

@@ -0,0 +1,1031 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-carde">
+      <div slot="header" class="clearfix">
+        <span>人工干预设置</span>
+      </div>
+      <el-container>
+        <el-header style="padding: 0px;display:flex;justify-content:space-between;align-items: center">
+          <div style="display: inline">
+            <el-date-picker
+              v-if="activeName=='first'"
+              v-model="dateTime"
+              type="date"
+              placeholder="选择日期"/>
+            <el-date-picker
+              v-else-if="activeName=='second'"
+              v-model="dateTime"
+              type="date"
+              placeholder="选择日期"/>
+            <el-date-picker
+              v-if="activeName=='third'"
+              v-model="dateTime2"
+              type="daterange"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"/>
+            <el-date-picker
+              v-if="activeName=='fourth'"
+              v-model="dateTime2"
+              type="daterange"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"/>
+            <el-select v-model="fileType" :filterable="true" placeholder="请选择文件类型" prop="fileType">
+              <el-option
+                v-for="item in fileTypeData"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"/>
+            </el-select>
+            <el-button v-if="activeName=='first'" type="primary" size="mini" style="margin-left: 5px"
+                       icon="el-icon-search" @click="searchEmp">搜索
+            </el-button>
+            <el-button v-if="activeName=='second'" type="primary" size="mini" style="margin-left: 5px"
+                       icon="el-icon-search" @click="searchEmp">搜索
+            </el-button>
+            <el-button v-if="activeName=='third'" type="primary" size="mini" style="margin-left: 5px"
+                       icon="el-icon-search" @click="searchEmp2">搜索
+            </el-button>
+            <el-button v-if="activeName=='fourth'" type="primary" size="mini" style="margin-left: 5px"
+                       icon="el-icon-search" @click="searchEmp3">搜索
+            </el-button>
+            <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-plus"
+                       v-if="activeName=='first'" @click="saveManual('form')">保存
+            </el-button>
+            <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-refresh"
+                       v-if="activeName=='first'" @click="refresh">更新首页短期数据
+            </el-button>
+            <el-button type="primary" size="mini" v-if="activeName=='second'" @click="insertEvent"
+                       style="margin-left: 5px"
+                       :loading="btnLonding">新增
+            </el-button>
+            <el-button type="primary" size="mini" @click="saveNumber" style="margin-left: 5px"
+                       :loading="btnLonding">负偏差(点击保存,范围0-0.1)->
+            </el-button>
+            <el-input-number type="number" size="mini" style="margin-left: 5px;width: 200px" :max="0.1" :min="0"
+                             :step="0.01" :precision="2" placeholder="负偏差" v-model="numberValue"></el-input-number>
+          </div>
+        </el-header>
+
+        <el-tabs v-model="activeName" type="card">
+          <el-tab-pane label="点位设置" name="first">
+
+            <div class="echartsDiv">
+
+              <div id="MyShortTermRealCharts"/>
+
+            </div>
+            <div ref="testInput">
+              <el-table
+                v-loading="tableLoading"
+                :data="form.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+                height="550px"
+                border
+                stripe
+                size="mini"
+                style="width: 20%">
+                <el-table-column
+                  prop="time1"
+                  header-align="center"
+                  align="center"
+                  label="时间">
+                </el-table-column>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  prop="data1"
+                  header-align="center"
+                  align="center"
+                  label="预测值">
+                  <template slot-scope="scope">
+                    <vxe-input type="number" v-model="scope.row.data1" size="small" style="width:100%" min="0"
+                               :max="capacity" placeholder="超过装机容量默认最大值"></vxe-input>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div style="display: flex;justify-content: space-between;margin: 2px;float: right;width: 15%">
+                总条数:{{ this.totalCount }}
+              </div>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane label="系数设置" name="second">
+            <div style="padding-top: 10px">
+              <vxe-table
+                highlight-hover-row
+                :keep-source="true"
+                align="center"
+                :loading="loading"
+                v-loading="false"
+                ref="xTable"
+                auto-resize
+                highlight-current-row
+                border
+                resizable
+                show-overflow
+                :data="tableData"
+                :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+              >
+                <vxe-table-column width="25%" field="interveneStartTime" title="开始时间"
+                                  :edit-render="{name:'$input',attrs:{type:'text'}}">
+                  <template v-slot:edit="{ row }">
+                    <el-time-select
+                      style="width: 100%"
+                      v-model="row.interveneStartTime"
+                      :picker-options="{
+                        start: '00:00',
+                        step: '00:15',
+                        end: '23:45'
+                      }"
+                      placeholder="选择开始时间"
+                    />
+                  </template>
+                  <template v-slot="{ row }">{{ timestampToTimeRatio(row.interveneStartTime) }}
+                  </template>
+                </vxe-table-column>
+                <vxe-table-column width="25%" field="interveneEndTime" title="结束时间"
+                                  :edit-render="{name:'$input',attrs:{type:'text'}}">
+                  <template v-slot:edit="{ row }">
+                    <el-time-select
+                      style="width: 100%"
+                      v-model="row.interveneEndTime"
+                      :picker-options="{
+                        start: '00:00',
+                        step: '00:15',
+                        end: '23:45'
+                      }"
+                      placeholder="选择结束时间"/>
+                  </template>
+                  <template v-slot="{ row }">{{
+                      timestampToTimeRatio(row.interveneEndTime)
+                    }}
+                  </template>
+                </vxe-table-column>
+                <vxe-table-column field="ratio" width="25%" title="系数"
+                                  :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+                <vxe-table-column fixed="right" width="25%" title="操作">
+                  <template v-slot="{ row }">
+                    <template v-if="$refs.xTable.isActiveByRow(row)">
+                      <el-button
+                        style="padding: 3px 4px 3px 4px;margin: 2px"
+                        size="mini"
+                        icon="el-icon-edit"
+                        :loading="saveLoding"
+                        @click="saveRatioData(row)">保存
+                      </el-button>
+                      <el-button
+                        style="padding: 3px 4px 3px 4px;margin: 2px"
+                        size="mini"
+                        icon="el-icon-edit"
+                        @click="cancelRowEvent(row)">取消
+                      </el-button>
+                    </template>
+                    <template v-else>
+                      <el-button
+                        style="padding: 3px 4px 3px 4px;margin: 2px"
+                        size="mini"
+                        icon="el-icon-edit"
+                        @click="editRowEvent(row)"
+                        :loading="btnLonding">编辑
+                      </el-button>
+                      <el-button
+                        type="danger"
+                        style="padding: 3px 4px 3px 4px;margin: 2px"
+                        size="mini"
+                        icon="el-icon-delete"
+                        @click="removeEvent(row)"
+                        :loading=btnLonding>删除
+                      </el-button>
+                    </template>
+                  </template>
+                </vxe-table-column>
+              </vxe-table>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane label="人工干预记录列表" name="third">
+            <div>
+              <el-table
+                v-loading="tableLoading"
+                :data="emps.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+                height="500px"
+                border
+                stripe
+                size="mini"
+                style="width: 100%">
+                <el-table-column
+                  sortable="custom"
+                  :formatter="fileTypeFormat"
+                  prop="fileType"
+                  align="center"
+                  header-align="center"
+                  label="文件类型"/>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  :formatter="dateFormat"
+                  prop="operationalTime"
+                  align="center"
+                  header-align="center"
+                  label="操作时间"/>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  prop="interveneTime"
+                  :formatter="dateFormats"
+                  align="center"
+                  header-align="center"
+                  label="干预数据日期"/>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  :formatter="operationalFormat"
+                  prop="operationalStatus"
+                  align="center"
+                  header-align="center"
+                  label="操作类型"/>
+                <el-table-column
+                  header-align="center"
+                  align="center"
+                  label="操作">
+                  <template slot-scope="scope">
+                    <el-button
+                      style="padding: 3px 4px 3px 4px;margin: 2px"
+                      size="mini"
+                      icon="el-icon-document"
+                      @click="showErrorInfo(scope.row)">查看内容
+                    </el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div style="display: flex;justify-content: space-between;margin: 2px;float: right">
+                <el-pagination
+                  :current-page="currentPage"
+                  :page-size="pageSize"
+                  :page-sizes="[10, 25, 50, 100]"
+                  :total="totalCount2"
+                  background
+                  layout="total, sizes, prev, pager, next, jumper"
+                  @size-change="handleSizeChange"
+                  @current-change="currentChange"/>
+              </div>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane v-if="this.eltype=='E33'" label="负偏差修改记录" name="fourth">
+            <div>
+              <el-table
+                v-loading="tableLoading"
+                :data="fpcTable.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+                height="500px"
+                border
+                stripe
+                size="mini"
+                style="width: 100%">
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  :formatter="dateFormat"
+                  prop="operationalTime"
+                  align="center"
+                  header-align="center"
+                  label="操作时间"/>
+                <el-table-column
+                  :show-overflow-tooltip="true"
+                  prop="operationalContext"
+                  align="center"
+                  header-align="center"
+                  label="操作内容"/>
+              </el-table>
+              <div style="display: flex;justify-content: space-between;margin: 2px;float: right">
+                <el-pagination
+                  :current-page="currentPage"
+                  :page-size="pageSize"
+                  :page-sizes="[10, 25, 50, 100]"
+                  :total="totalCount3"
+                  background
+                  layout="total, sizes, prev, pager, next, jumper"
+                  @size-change="handleSizeChange"
+                  @current-change="currentChange"/>
+              </div>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+        <el-form
+          ref="addEmpForm"
+          :model="emp"
+          style="margin: 0px;padding: 0px;"
+          label-width="100px">
+          <el-dialog
+            :close-on-click-modal="false"
+            :visible.sync="dialogVisibleErrorInfo"
+            title="具体信息"
+            style="padding: 0px;"
+            label-width="1px"
+            width="1000px">
+            <el-input
+              v-model="emp.operationalContext"
+              :rows="20"
+              type="textarea"
+              style="width: 100%"/>
+            <span
+              slot="footer"
+              class="dialog-footer">
+            <el-button
+              size="mini"
+              @click="cancelEidtEI">关 闭</el-button>
+          </span>
+          </el-dialog>
+        </el-form>
+      </el-container>
+    </el-card>
+  </div>
+</template>
+<script>
+import echarts from 'echarts'
+
+export default {
+  data() {
+    return {
+      numberValue: '',
+      saveLoding: false,
+      showTable: true,
+      btnLonding: false,
+      ef: '',
+      // 日期选择器
+      dateTime: new Date(new Date().toLocaleDateString()),
+      dateTime2: [new Date(new Date().toLocaleDateString()).getTime(), new Date(new Date().toLocaleDateString()).getTime() + 60 * 60 * 24 * 3 * 1000],
+
+      // 场站信息存储对象
+      elTableData: [],
+      efiId: '',
+      emps: [],
+      fpcTable: [],
+      dataList: [],
+      keywords: '',
+      jType: '',
+      dialogTitle: '',
+      dialogVisibleErrorInfo: false,
+      loading: false,
+      multipleSelection: [],
+      downLoading: false,
+      depTextColor: '#c0c4cc',
+      totalCount: -1,
+      totalCount2: -1,
+      totalCount3: -1,
+      currentPage: 1,
+      pageSize: 100,
+      dialogVisible: false,
+      tableLoading: false,
+      fileParsing: [],
+      activeName: 'first',
+      ultraValueData: [],
+      shortValueData: [],
+      realPowerValueData: [],
+      capacityUnit: '',
+      capacity: '',
+      shortValueDate: [],
+      fileType: '',
+      fileTypeData: [
+        {value: 0, label: '短期'},
+        {value: 1, label: '超短期'}
+      ],
+      form: [{
+        time1: '',
+        data1: '',
+        fileType: '',
+        lastModifyTime: '',
+      }],
+      radioStartTime: '',
+      radioEndTime: ',',
+      tableData: [],
+      isEdit: false,
+      eltype: '',
+      sysid: null,
+      myChart: {},
+      symbolSize: 20,
+      chartsData: [],
+      emp: {
+        operationalContext: ''
+      },
+      option: {
+        title: {
+          text: '人工干预数据对比',
+          left: '3%', // left 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比,也可以是 'left', 'center', 'right',如果 left 的值为'left', 'center', 'right',组件会根据相应的位置自动对齐。
+          top: '10px', // left 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比,也可以是 'left', 'center', 'right',如果 left 的值为'left', 'center', 'right',组件会根据相应的位置自动对齐。
+          right: 'auto', // right 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比。
+          bottom: 'auto'// bottom 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比。
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { // 会出现横竖2个方向的指示器
+            type: 'cross'
+          },
+
+        },
+        // 图例名
+        legend: {
+          top: '2%',
+          data: ['预测数据', '干预后预测数据']
+        },
+        grid: {
+          top: '70px',
+          left: '5%', // 图表距边框的距离
+          right: '5%',
+          bottom: '20%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category',
+          axisLine: {onZero: false}
+        },
+        yAxis: [{
+          max: 30,
+          min: 0,
+          type: 'value',
+          axisLine: {onZero: false}
+        }],
+
+        // 工具框,可以选择
+        toolbox: {
+          show: true,
+          feature: {
+            mark: {show: true},
+            dataView: {show: true, readOnly: false},
+            magicType: {show: true, type: ['line', 'bar']},
+            restore: {show: true},
+            saveAsImage: {show: true}
+          }
+        },
+        dataZoom: [
+          {
+            type: 'slider',
+            xAxisIndex: 0,
+            filterMode: 'none'
+          },
+          {
+            type: 'inside',
+            xAxisIndex: 0,
+            filterMode: 'none'
+          }
+        ],
+        series: [
+          {
+            name: '干预后预测数据',
+            id: 'a',
+            type: 'line',
+            smooth: true,
+            color: ['red'],
+            symbolSize: 10,
+            data: [],
+            max: 30
+          },
+          {
+            name: '预测数据',
+            id: 'b',
+            type: 'line',
+            smooth: true,
+            color: ['orange'],
+            symbolSize: 10,
+            data: []
+          }
+        ]
+      }
+
+    }
+
+
+  },
+  created: function () {
+    this.fileType = 0;
+    this.getEl()
+    this.draData()
+    this.getRecordList()
+  },
+  mounted: function () {
+
+    this.getRecordList()
+    this.getRecordList2()
+  },
+  methods: {
+
+    cancelEidtEI() {
+      this.dialogVisibleErrorInfo = false
+    },
+    operationalFormat(row, column) {
+      let belongTo = '未知的文件类型场站'
+      for (let i = 0; i < this.emps.length; i++) {
+        if (row.operationalStatus == "0") {
+          belongTo = "点位新增"
+        }
+        if (row.operationalStatus == "1") {
+          belongTo = "点位修改"
+        }
+        if (row.operationalStatus == "2") {
+          belongTo = "点位删除"
+        }
+        if (row.operationalStatus == "3") {
+          belongTo = "系数"
+        }
+        if (row.operationalStatus == "4") {
+          belongTo = "系数删除"
+        }
+      }
+      return belongTo
+    },
+    dateFormats(row, column) {
+      let that = this;
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD')// 使用moment插件进行日期格式化
+    },
+    fileTypeFormat(row, column) {
+      let belongTo = '未知的文件类型场站'
+      for (let i = 0; i < this.emps.length; i++) {
+        if (row.fileType == "0") {
+          belongTo = "短期"
+        }
+        if (row.fileType == "1") {
+          belongTo = "超短期"
+        }
+      }
+      return belongTo
+    },
+    showErrorInfo(row) {
+      this.emp.operationalContext = row.operationalContext
+      this.dialogVisibleErrorInfo = true
+    },
+    draData() {
+
+      this.option.series[0].data = this.chartsData2
+      this.option.series[1].data = this.chartsData
+      this.option.yAxis.max = this.capacity
+
+      // 基于准备好的dom,初始化echarts实例
+      this.myChart = echarts.init(document.getElementById('MyShortTermRealCharts'))
+      var data = this.chartsData2
+      var myChart = this.myChart
+      let symbolSize = this.symbolSize;
+      myChart.setOption(this.option)
+      var form1 = this.form
+      setTimeout(function () {
+// Add shadow circles (which is not visible) to enable drag.
+        myChart.setOption({
+          graphic: data.map(function (item, dataIndex) {
+            return {
+              type: 'circle',
+              position: myChart.convertToPixel('grid', item),
+              shape: {
+                cx: 0,
+                cy: 0,
+                r: 10
+              },
+              invisible: true,
+              draggable: true,
+              ondrag: function (dx, dy,) {
+
+                onPointDragging(dataIndex, [item[0], dx.offsetY]);
+              },
+              onmousemove: function () {
+                showTooltip(dataIndex);
+              },
+              onmouseout: function () {
+                hideTooltip(dataIndex);
+              },
+              z: 100
+            };
+          })
+        });
+      }, 0);
+      window.addEventListener('resize', updatePosition);
+      myChart.on('dataZoom', updatePosition);
+
+      function updatePosition() {
+        myChart.setOption({
+          graphic: data.map(function (item, dataIndex) {
+            return {
+              position: myChart.convertToPixel('grid', item)
+            };
+          })
+        });
+      }
+
+      function showTooltip(dataIndex) {
+        myChart.dispatchAction({
+          type: 'showTip',
+          seriesIndex: 0,
+          dataIndex: dataIndex
+        });
+      }
+
+      function hideTooltip(dataIndex) {
+        myChart.dispatchAction({
+          type: 'hideTip'
+        });
+      }
+
+      function onPointDragging(dataIndex, pos,) {
+
+
+        data[dataIndex][1] = myChart.convertFromPixel('grid', pos)[1].toFixed(2);
+
+        if (myChart.convertFromPixel('grid', pos)[1].toFixed(2) > 30) {
+          data[dataIndex][1] = 30
+        }
+        if (myChart.convertFromPixel('grid', pos)[1].toFixed(2) < 0) {
+          data[dataIndex][1] = 0
+        }
+        form1[dataIndex].data1 = data[dataIndex][1]
+        // Update data
+        myChart.setOption({
+
+          series: [
+            {
+              id: 'a',
+              data: data,
+            }
+          ]
+        });
+
+        updatePosition()
+      }
+      this.form = form1
+      updatePosition()
+    },
+
+    saveNumber() {
+      this.btnLonding = true
+      if (this.numberValue > 0.1 || this.numberValue < 0) {
+        alert("无法保存,注意负偏差范围")
+      } else {
+        var row = {
+          id: this.sysid,
+          sysKey: "negative_deviation",
+          sysValue: this.numberValue,
+          describe: "浙江负偏差数字(注意只能填数字)"
+        }
+
+        this.$axios.post('/sysParameter/noSaveValidate', row).then(response => {
+          this.btnLonding = false
+          this.$message.success('设置成功!')
+          var rowLog = {
+            fileType: "2",
+            operationalTime: new Date().getTime(),
+            operationalContext: "修改负偏差为:" + this.numberValue,
+            operationalStatus: 5,
+            interveneTime: null
+          }
+          this.$axios.put('/manualInterventionLogController/saveForecastPowerShortTerm', rowLog).then(res => {
+
+          }).catch((error) => {
+            this.$emit('sendLoading', this.loading)
+            this.$message.error('记录保存出错' + error)
+          })
+
+        }).catch((error) => {
+          this.$emit('sendLoading', this.loading)
+          this.$message.error('设置出错' + error)
+        })
+      }
+      this.btnLonding = false
+    },
+    //取消编辑或添加操作
+    cancelRowEvent(row) {
+      this.saveLoding = false
+      this.btnLonding = false
+      const xTable = this.$refs.xTable
+      xTable.clearActived().then(() => {
+        // 还原行数据
+        if (!this.isEdit) {
+          //编辑
+          xTable.revertData(row)
+        } else {
+          //新增
+          xTable.remove(row)
+        }
+        this.isEdit = false
+      })
+    },
+    //编辑框改变
+    editRowEvent(row) {
+      this.startTimeShow = false
+      if (row.interveneStartTime < new Date().getTime()) {
+        this.startTimeShow = true
+      }
+      this.checkSign = true
+      this.btnLonding = true
+      row.interveneStartTime = this.timestampToTimeRatio(row.interveneStartTime)
+      row.interveneEndTime = this.timestampToTimeRatio(row.interveneEndTime)
+      this.$refs.xTable.setActiveRow(row)
+    },
+    //删除数据
+    removeEvent(row) {
+      this.$XModal.confirm('您确定要删除该数据?').then(type => {
+        if (type === 'confirm') {
+          this.$axios.delete("/manualInterventionRatioController/delete/" + row.id).then(response => {
+            this.$message.success(`删除成功`)
+            this.queryManualInterventionRatio()
+          })
+        }
+      })
+    },
+    //新增编辑框
+    insertEvent() {
+      this.isEdit = true
+      this.startTimeShow = false
+      this.showTable = true
+      this.btnLonding = true
+      this.checkSign = false
+      this.$refs.xTable.insert()
+        .then(({row}) => this.$refs.xTable.setActiveRow(row))
+    },
+    getEl() {
+      this.$axios.get("/electricField/").then(res => {
+
+        this.capacity = res.data.capacity
+        this.queryManualIntervention()
+        this.queryManualInterventionRatio()
+        this.querySysParameter()
+        this.getInterventionData()
+      })
+    },
+    //获取干预对比数据
+    getInterventionData() {
+      const startTime = Math.round(this.dateTime)
+      const endTime = Math.round(this.dateTime) + 60 * 60 * 24 * 1000 - 1
+//预测的
+      this.$axios.get('/manualInterventionController/getForecastPowerShortTermForDrawing/' + startTime + '/' + endTime + '/' + this.fileType).then((res) => {
+
+        this.chartsData = res.data
+        //人工干预之后的数据
+        this.$axios.get('/manualInterventionLogController/queryForecastAndManualInterventionData/' + startTime + '/' + endTime + '/' + this.fileType).then((res) => {
+          this.chartsData2 = res.data
+          this.draData()
+        })
+      })
+    },
+    querySysParameter() {
+      this.$axios.get('/sysParameter/sysParameterKey/negative_deviation').then((res) => {
+        if (res.data != null) {
+          this.numberValue = res.data.sysValue
+          this.sysid = res.data.id
+        }
+      })
+    },
+    refreshShortTermHisData() {
+      this.$XModal.confirm('您确定要更新首页短期数据?').then(type => {
+        if (type === 'confirm') {
+          this.$axios.get('/manualInterventionController/refreshSt',).then(res => {
+            if (res.code == 0) {
+              this.$message.success("更新成功!")
+            }
+          })
+        }
+      })
+    },
+    timestampToTimess(time) {
+      const date = new Date(time)// 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return Y + M + D + H + m
+    },
+    timestampToTimeRatio(time) {
+      const date = new Date(time)// 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return H + m
+    },
+    // 根据类型获取class
+    queryManualIntervention() {
+      this.tableLoading = true
+      const a = [];
+      for (var i = 0; i <= 23; i++) {
+        if (i < 10) {
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + "0" + i + ":00")
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + "0" + i + ":15")
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + "0" + i + ":30")
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + "0" + i + ":45")
+        } else {
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + i + ":00")
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + i + ":15")
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + i + ":30")
+          a.push(this.timestampToTimes(Math.round(this.dateTime)) + i + ":45")
+        }
+      }
+      for (var i = 0; i < a.length; i++) {
+        this.form[i] = {
+          time1: a[i],
+          data1: ""
+        }
+      }
+      const startTime = Math.round(this.dateTime)
+      const endTime = Math.round(this.dateTime) + 60 * 60 * 24 * 1000 - 1
+      this.$axios.get('/manualInterventionController/queryManualInterventionData/' + startTime + '/' + endTime + '/' + this.fileType).then((res) => {
+        for (var i = 0; i < res.data.length; i++) {
+          for (var j = 0; j < this.form.length; j++) {
+            if (this.timestampToTimess(res.data[i].interveneTime) == this.form[j].time1) {
+              this.form[j].data1 = res.data[i].interveneData
+            }
+          }
+        }
+
+        this.totalCount = this.form.length
+        this.tableLoading = false
+      }).catch((error) => {
+        this.$message.error('查询人工干预数据出错' + error)
+      })
+    },
+
+    saveManual(formName) {
+      this.tableLoading = true
+      this.form[0].fileType = this.fileType
+      this.form[0].lastModifyTime = Math.round(this.dateTime)
+      this.$axios.put('/manualInterventionController/saveManualIntervention', this.form).then((res) => {
+        this.$message.success(`保存成功`)
+        this.queryManualIntervention()
+        this.tableLoading = false
+        this.getEl()
+        this.draData()
+      }).catch((error) => {
+        this.$message.error('保存人工干预数据出错' + error)
+      })
+
+    },
+    time_to_sec(time) {
+
+      if (time !== null) {
+        var s = "";
+        var hour = time.split(":")[0];
+        var min = time.split(":")[1];
+        var sec = time.split(":")[2];
+        s = Number(hour * 3600) + Number(min * 60) + Number(sec);
+        return s;
+      }
+    },
+    queryManualInterventionRatio() {
+      this.loading = true
+      this.saveLoding = false
+      const startTime = Math.round(this.dateTime)
+      const endTime = Math.round(this.dateTime) + 60 * 60 * 24 * 1000 - 1
+
+      this.$axios.get('/manualInterventionRatioController/queryManualInterventionRatioData/' + startTime + '/' + endTime + '/' + this.fileType).then((res) => {
+        this.tableData = res.data
+        this.loading = false
+
+      })
+    },
+    saveRatioData(row){
+      this.saveLoding = true
+      if(row.ratio == null || row.interveneStartTime ==null ||row.interveneEndTime == null||row.interveneStartTime >= row.interveneEndTime){
+        this.saveLoding = false
+        return this.$XModal.message({status: 'error', message: '校验不通过!'})
+      }
+      const formData = new FormData()
+      formData.append('ratio', row.ratio)
+      formData.append('interveneStartTime', this.dateTime.getTime()+this.time_to_sec(row.interveneStartTime+":00")*1000)
+      formData.append('interveneEndTime',this.dateTime.getTime()+this.time_to_sec(row.interveneEndTime+":00")*1000)
+      formData.append('interveneType', this.fileType)
+      row.interveneStartTime = this.dateTime.getTime()+this.time_to_sec(row.interveneStartTime+":00")*1000
+      row.interveneEndTime = this.dateTime.getTime()+this.time_to_sec(row.interveneEndTime+":00")*1000
+      row.interveneType = this.fileType
+      this.$refs.xTable.validate(valid => {
+        if (valid) {
+          this.$refs.xTable.clearActived().then(() => {
+            this.loading = true
+            this.$axios.post("/manualInterventionRatioController/saveManualInterventionRatio", row).then(response => {
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+              this.queryManualInterventionRatio()
+            }).catch((error) => {
+              this.$message.error('保存失败,请检查是否设置过点位')
+            })
+          this.btnLonding = false
+            this.loading = false
+            this.queryManualInterventionRatio()
+          })
+
+        } else {
+          this.$XModal.message({status: 'error', message: '校验不通过!'})
+          this.saveLoding = false
+          this.loading = false
+        }
+
+      })
+    },
+    // 时间格式化
+    dateFormat(row, column) {
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD HH:mm:ss')// 使用moment插件进行日期格式化
+    },
+    timestampToTime(time) {
+      const a = parseInt(time)
+      const date = new Date(a) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return H + m
+    },
+    timestampToTimes(time) {
+      const a = parseInt(time)
+      const date = new Date(a) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
+      return Y + M + D
+    },
+    searchEmp() {
+      this.queryManualIntervention()
+      this.queryManualInterventionRatio()
+      this.getInterventionData()
+    },
+    searchEmp2() {
+      this.getRecordList()
+    },
+    searchEmp3() {
+      this.getRecordList2()
+    },
+    getRecordList() {
+      if (this.dateTime2 == '' || this.dateTime2 == null) {
+        alert('请选择时间')
+        return
+      }
+      this.$axios.get('/electricField/').then((res) => {
+        this.capacityUnit = 'mw'
+        this.eltype = res.data.provinceEnum
+        this.capacity = res.data.capacity
+      })
+      const formData = new FormData()
+      const startTime2 = Math.round(this.dateTime2[0])
+      const endTime2 = Math.round(this.dateTime2[1]) + 60 * 60 * 24 * 1000 - 1
+      this.$axios.get('/manualInterventionLogController/queryManualInterventionLogData/' + startTime2 + '/' + endTime2 + '/' + this.fileType).then((res) => {
+        this.emps = res.data
+        this.totalCount2 = this.emps.length
+      })
+    },
+    getRecordList2() {
+      if (this.dateTime2 == '' || this.dateTime2 == null) {
+        alert('请选择时间')
+        return
+      }
+      this.$axios.get('/manualInterventionLogController/getByFileType/2').then((res) => {
+        this.fpcTable = res.data
+        this.totalCount3 = this.fpcTable.length
+      })
+
+    },
+
+
+    refresh() {
+      this.refreshShortTermHisData()
+    },
+    currentChange(currentChange) {
+      this.currentPage = currentChange
+      this.queryManualIntervention()
+    },
+    handleSizeChange(handleSizeChange) {
+      this.pageSize = handleSizeChange
+      this.queryManualIntervention()
+    },
+    changeTime(row, column) {
+      return this.timestampToTime(row.time1)
+    },
+  }
+
+}
+</script>
+<style>
+
+.echartsDiv {
+  float: left;
+  width: 80%;
+  height: 400px;
+}
+
+.el-dialog__body {
+  padding-top: 0px;
+  padding-bottom: 0px;
+}
+
+.slide-fade-enter-active {
+  transition: all .8s ease;
+}
+
+.slide-fade-leave-active {
+  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
+}
+
+.slide-fade-enter, .slide-fade-leave-to {
+  transform: translateX(10px);
+  opacity: 0;
+}
+
+#MyShortTermRealCharts {
+  width: 100%;
+  height: 580px;
+}
+</style>

+ 536 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/manualInterventionQuery/index.vue

@@ -0,0 +1,536 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-carde">
+      <div slot="header" class="clearfix">
+        <span>人工干预记录</span>
+      </div>
+    <el-container>
+      <el-header style="padding: 0px;display:flex;justify-content:space-between;align-items: center">
+        <div style="display: inline">
+          <el-date-picker
+            v-model="dateTime"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"/>
+
+          <el-select v-model="fileType" :filterable="true" placeholder="请选择文件类型" prop="fileType">
+            <el-option
+              v-for="item in fileTypeData"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"/>
+          </el-select>
+          <el-button type="primary" size="mini" style="margin-left: 5px" icon="el-icon-search" @click="searchEmp">搜索
+          </el-button>
+        </div>
+      </el-header>
+
+      <el-tabs v-model="activeName" type="card">
+        <el-tab-pane label="人工干预数据图表对比" name="first">
+          <el-main style="padding-left: 0px;padding-top: 0px">
+            <div class="container">
+              <div id="MyShortTermRealCharts"/>
+            </div>
+          </el-main>
+        </el-tab-pane>
+
+        <el-tab-pane label="人工干预记录列表" name="second">
+          <div>
+            <el-table
+              v-loading="tableLoading"
+              :data="emps.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+              height="500px"
+              border
+              stripe
+              size="mini"
+              style="width: 100%">
+              <el-table-column
+                sortable="custom"
+                :formatter="fileTypeFormat"
+                prop="fileType"
+                align="center"
+                header-align="center"
+                label="文件类型"/>
+              <el-table-column
+                :show-overflow-tooltip="true"
+                :formatter="dateFormat"
+                prop="operationalTime"
+                align="center"
+                header-align="center"
+                label="操作时间"/>
+              <el-table-column
+                :show-overflow-tooltip="true"
+                prop="interveneTime"
+                :formatter="dateFormats"
+                align="center"
+                header-align="center"
+                label="干预数据日期"/>
+              <el-table-column
+                :show-overflow-tooltip="true"
+                :formatter="operationalFormat"
+                prop="operationalStatus"
+                align="center"
+                header-align="center"
+                label="操作类型"/>
+              <el-table-column
+                header-align="center"
+                align="center"
+                label="操作">
+                <template slot-scope="scope">
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-document"
+                    @click="showErrorInfo(scope.row)">查看内容
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div style="display: flex;justify-content: space-between;margin: 2px;float: right">
+              <el-pagination
+                :current-page="currentPage"
+                :page-size="pageSize"
+                :page-sizes="[10, 25, 50, 100]"
+                :total="totalCount"
+                background
+                layout="total, sizes, prev, pager, next, jumper"
+                @size-change="handleSizeChange"
+                @current-change="currentChange"/>
+            </div>
+          </div>
+        </el-tab-pane>
+        <el-tab-pane v-if="this.eltype=='E33'" label="负偏差修改记录" name="third">
+          <div>
+            <el-table
+              v-loading="tableLoading"
+              :data="fpcTable.slice((currentPage-1)*pageSize,currentPage*pageSize)"
+              height="500px"
+              border
+              stripe
+              size="mini"
+              style="width: 100%">
+              <el-table-column
+                :show-overflow-tooltip="true"
+                :formatter="dateFormat"
+                prop="operationalTime"
+                align="center"
+                header-align="center"
+                label="操作时间"/>
+              <el-table-column
+                :show-overflow-tooltip="true"
+                prop="operationalContext"
+                align="center"
+                header-align="center"
+                label="操作内容"/>
+            </el-table>
+            <div style="display: flex;justify-content: space-between;margin: 2px;float: right">
+              <el-pagination
+                :current-page="currentPage"
+                :page-size="pageSize"
+                :page-sizes="[10, 25, 50, 100]"
+                :total="totalCount"
+                background
+                layout="total, sizes, prev, pager, next, jumper"
+                @size-change="handleSizeChange"
+                @current-change="currentChange"/>
+            </div>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+      <el-form
+        ref="addEmpForm"
+        :model="emp"
+        style="margin: 0px;padding: 0px;"
+        label-width="100px">
+        <el-dialog
+          :close-on-click-modal="false"
+          :visible.sync="dialogVisibleErrorInfo"
+          title="具体信息"
+          style="padding: 0px;"
+          label-width="1px"
+          width="1000px">
+          <el-input
+            v-model="emp.operationalContext"
+            :rows="20"
+            type="textarea"
+            style="width: 100%" />
+          <span
+            slot="footer"
+            class="dialog-footer">
+            <el-button
+              size="mini"
+              @click="cancelEidtEI">关 闭</el-button>
+          </span>
+        </el-dialog>
+      </el-form>
+    </el-container>
+    </el-card>
+  </div>
+</template>
+<script>
+export default {
+
+
+  data() {
+    return {
+      ef: '',
+      // 日期选择器
+      dateTime: [new Date(new Date().toLocaleDateString()).getTime() , new Date(new Date().toLocaleDateString()).getTime() + 60 * 60 * 24 * 3 * 1000],
+      // 场站信息存储对象
+      elTableData: [],
+      efiId: '',
+      emps: [],
+      fileType:'',
+      fileTypeData:[
+        { value: 0, label: '短期' },
+        { value: 1, label: '超短期' }
+      ],
+      keywords: '',
+      jType: '',
+      dialogTitle: '',
+      dialogVisibleErrorInfo: false,
+      multipleSelection: [],
+      depTextColor: '#c0c4cc',
+      totalCount: -1,
+      currentPage: 1,
+      pageSize: 10,
+      dialogVisible: false,
+      tableLoading: false,
+      fileParsing: [],
+      activeName: 'first',
+      forecastAndManualInterventionData: [],
+      forecastValueCorrectAfterData: [],
+      forecastTimeData: [],
+      capacityUnit: '',
+      capacity:'',
+      emp:{
+        operationalContext:''
+      },
+      eltype:'',
+      fpcTable:[]
+    }
+  },
+  mounted: function() {
+    this.fileType=0;
+    this.getAllClass()
+  },
+
+  methods: {
+    cancelEidtEI () {
+      this.dialogVisibleErrorInfo = false
+    },
+    showErrorInfo (row) {
+      this.emp.operationalContext = row.operationalContext
+      this.dialogVisibleErrorInfo = true
+    },
+    // // 获取后台所有场站信息
+    // getELData() {
+    //   this.$axios.post('/electricFieldInfo/findAllElectricFieldInfo').then((res) => {
+    //     this.elTableData = res.data
+    //     this.efiId = res.data[0].id
+    //     // this.capacityUnit=res.data[0].capacityUnit
+    //     // this.type = 'NWP'
+    //     this.getAllClass()
+    //   })
+    // },
+    // 表格所属场站,ID转换场站名称
+    formatStation(row, column) {
+      let belongTo = '未知的所属场站'
+      for (let i = 0; i < this.elTableData.length; i++) {
+        if (row.efiId === this.elTableData[i].id) {
+          belongTo = this.elTableData[i].name
+        }
+      }
+      return belongTo
+    },
+    // 根据类型获取class
+    getAllClass() {
+      if (this.dateTime == '' || this.dateTime == null) {
+        alert('请选择时间')
+        return
+      }
+      this.$axios.get('/electricField/').then((res) => {
+        this.capacityUnit = 'mw'
+        this.eltype = res.data.provinceEnum
+        this.capacity=res.data.capacity
+      })
+      const formData = new FormData()
+      const startTime = Math.round(this.dateTime[0])
+      const endTime = Math.round(this.dateTime[1]) + 60 * 60 * 24 * 1000 - 1
+      this.$axios.get('/manualInterventionLogController/getByFileType/2' ).then((res) => {
+        this.fpcTable = res.data
+        this.totalCount = this.fpcTable.length
+      })
+      this.$axios.get('/manualInterventionLogController/queryManualInterventionLogData/'+ startTime +'/' + endTime +'/'+ this.fileType ).then((res) => {
+        this.emps = res.data
+        this.totalCount = this.emps.length
+      })
+      const s = Math.round(this.dateTime[1])+ 60 * 60 * 24  * 1000 -1000
+      this.$axios.get('/manualInterventionLogController/getForecastPowerShortTermDrawing/' + Math.round(this.dateTime[0]) + '/' + s).then(resp => {
+        var data = resp.data
+        // 可用预测功率(修正后)
+        for (let k = 0; k < data.list.length; k++) {
+          this.forecastValueCorrectAfterData.push(data.list[k].fpValue)
+        }
+        for (let i = 0; i < data.list.length; i++) {
+          this.forecastTimeData.push(this.timestampToTime(data.list[i].forecastTime))
+        }
+        const startTime1 = Math.round(this.dateTime[0])
+        const endTime1 = Math.round(this.dateTime[1]) + 60 * 60 * 24 * 1000 - 1
+        this.$axios.get('/manualInterventionLogController/queryForecastAndManualInterventionData/'+startTime1+'/'+endTime1+'/'+this.fileType).then(resp => {
+          for (let i = 0; i < resp.data.length; i++) {
+            if(resp.data[i].intervention!=null){
+              this.forecastAndManualInterventionData.push(resp.data[i].intervention)
+            }
+
+          }
+          this.drawLine(this.forecastValueCorrectAfterData, this.forecastAndManualInterventionData, this.forecastTimeData, this.capacityUnit,this.capacity)
+          this.forecastValueCorrectAfterData = []
+          this.forecastAndManualInterventionData=[]
+          this.forecastTimeData = []
+          this.capacity=''
+        })
+
+
+
+      })
+    },
+    drawLine(forecastValueCorrectAfterData, forecastAndManualInterventionData, forecastTimeData, capacityUnit,capacity) {
+      var echarts = require('echarts')
+      // 基于准备好的dom,初始化echarts实例
+      const MyShortTermRealCharts = echarts.init(document.getElementById('MyShortTermRealCharts'))
+      // 绘制图表
+      MyShortTermRealCharts.setOption({
+        // 标题
+        title: {
+          text: '人工干预数据对比',
+          left: '3%', // left 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比,也可以是 'left', 'center', 'right',如果 left 的值为'left', 'center', 'right',组件会根据相应的位置自动对齐。
+          top: '10px', // left 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比,也可以是 'left', 'center', 'right',如果 left 的值为'left', 'center', 'right',组件会根据相应的位置自动对齐。
+          right: 'auto', // right 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比。
+          bottom: 'auto'// bottom 的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的百分比。
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { // 会出现横竖2个方向的指示器
+            type: 'cross'
+          }
+        },
+        // 图例名
+        legend: {
+          top: '2%',
+          data: ['预测数据', '干预后预测数据']
+        },
+        grid: {
+          top: '70px',
+          left: '5%', // 图表距边框的距离
+          right: '5%',
+          bottom: '20%',
+          containLabel: true
+        },
+        // 工具框,可以选择
+        toolbox: {
+          show: true,
+          feature: {
+            mark: { show: true },
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        dataZoom: [{
+          show: true,
+          realtime: true,
+          start: 0,
+          end: 100,
+        },{
+          type: 'inside'
+        }],
+        // x轴信息样式
+
+        xAxis: {
+          type: 'category',
+          boundaryGap: false,
+          axisLine: { onZero: false },
+          data: forecastTimeData,
+          // 坐标轴颜色
+          axisLine: {
+            lineStyle: {
+              color: 'gray'
+            }
+          },
+          // x轴文字旋转
+          axisLabel: {
+            rotate: 0,
+            // interval: 'auto',
+            show: true, // true为显示字体 false则不显示
+            textStyle: {// 字体颜色
+              color: 'gray'
+            }
+          }
+        },
+        yAxis: [
+          {
+            type: 'value',
+            min:0,
+            max:capacity,
+            axisPointer: {
+              type: 'line'
+            },
+            position: 'left',
+            splitLine: {// 网格线
+              show: true
+            },
+            axisTick: {// y轴刻度线
+              show: false
+            },
+            axisLabel: {// y轴线
+              formatter: '{value} ' + capacityUnit
+            },
+            axisLine: {// y轴线
+              show: false
+            }
+          }
+        ],
+        series: [
+          {
+            name: '预测数据',
+            type: 'line',
+            symbol: 'circle',
+            showSymbol: false,
+            symbolSize: 4,
+            data: forecastValueCorrectAfterData,
+            color: ['orange'],
+            smooth: true,
+            itemStyle: {
+              normal: {
+                lineStyle: {
+                  width: 2,
+                  type: 'solid' // 'dotted'虚线 'solid'实线
+                }
+              }
+            }
+          },
+          {
+            name: '干预后预测数据',
+            type: 'line',
+            color: ['red'],
+            symbol: 'circle',
+            showSymbol: false,
+            smooth: true,
+            symbolSize: 4,
+            data: forecastAndManualInterventionData,
+            itemStyle: {
+              normal: {
+                lineStyle: {
+                  width: 2,
+                  type: 'solid' // 'dotted'虚线 'solid'实线
+                }
+              }
+            }
+          }
+        ]
+      })
+      window.onresize = MyShortTermRealCharts.resize// 图表自适应浏览器大小
+    },
+    // 时间格式化
+    fileTypeFormat(row, column) {
+      let belongTo = '未知的文件类型场站'
+      for (let i = 0; i < this.emps.length; i++) {
+        if (row.fileType =="0") {
+          belongTo ="短期"
+        }
+        if (row.fileType =="1") {
+          belongTo ="超短期"
+        }
+      }
+      return belongTo
+    },
+    operationalFormat(row, column) {
+      let belongTo = '未知的文件类型场站'
+      for (let i = 0; i < this.emps.length; i++) {
+        if (row.operationalStatus =="0") {
+          belongTo ="点位新增"
+        }
+        if (row.operationalStatus =="1") {
+          belongTo ="点位修改"
+        }
+        if (row.operationalStatus =="2") {
+          belongTo ="点位删除"
+        }
+        if (row.operationalStatus =="3") {
+          belongTo ="系数"
+        }
+        if (row.operationalStatus =="4") {
+          belongTo ="系数删除"
+        }
+      }
+      return belongTo
+    },
+    // 时间格式化
+    dateFormat(row, column) {
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD HH:mm:ss')// 使用moment插件进行日期格式化
+    },
+    timestampToTime(time) {
+      const a = parseInt(time)
+      const date = new Date(a) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+      const H = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()) + ':'
+      const m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()) + ':'
+      const s = (date.getSeconds() < 10 ? '0' + (date.getSeconds()) : date.getSeconds())
+      return Y + M + D + H + m + s
+    },
+    // 时间格式化
+    dateFormats(row, column) {
+      const date = row[column.property]
+      if (date === undefined) {
+        return ''
+      }
+      return this.$moment(date).format('YYYY-MM-DD')// 使用moment插件进行日期格式化
+    },
+    searchEmp() {
+      this.getAllClass()
+    },
+    currentChange(currentChange) {
+      this.currentPage = currentChange
+      this.getAllClass()
+    },
+    handleSizeChange(handleSizeChange) {
+      this.pageSize = handleSizeChange
+      this.getAllClass()
+    },
+    // 自定义排序
+  }
+}
+</script>
+<style>
+.el-dialog__body {
+  padding-top: 0px;
+  padding-bottom: 0px;
+}
+
+.slide-fade-enter-active {
+  transition: all .8s ease;
+}
+
+.slide-fade-leave-active {
+  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
+}
+
+.slide-fade-enter, .slide-fade-leave-to {
+  transform: translateX(10px);
+  opacity: 0;
+}
+
+#MyShortTermRealCharts {
+  width: 100%;
+  height: 500px;
+}
+</style>

+ 545 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/overHaulPlan/index.vue

@@ -0,0 +1,545 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>检修计划</span>
+<!--        <el-button style="float: right;padding:3px 10px 3px 3px;" type="text" @click="qrShow">二维码扫描上传-->
+<!--        </el-button>-->
+        <el-button style="float: right;padding:3px 10px 3px 3px;" type="text" @click="exportDataEvent">导出数据
+        </el-button>
+      </div>
+      <el-button type="primary" size="small" @click="insertEvent" style="round-clip: 10px"
+                 :loading="btnLonding">新增
+      </el-button>
+      <div>
+        <el-dialog
+          :close-on-click-modal="false"
+          :visible.sync="dialogVisible"
+          title="请扫描以下二维码"
+          style="padding: 0px;"
+          label-width="1px"
+          width="500px">
+          <!--            <el-image-->
+          <!--              src="http://www.baidu.com"-->
+          <!--              fit="fill"-->
+          <!--            />-->
+          <div id="qrCode" ref="qrCodeDiv" name="qrCodeDiv" align="center"></div>
+
+          <span
+            slot="footer"
+            class="dialog-footer">
+            <el-button
+              size="mini"
+              @click="cancel">关 闭</el-button>
+          </span>
+        </el-dialog>
+      </div>
+      <div style="padding-top: 10px">
+        <vxe-table
+          highlight-hover-row
+          :keep-source="true"
+          align="center"
+          :loading="loading"
+          v-loading="false"
+          ref="xTable"
+          auto-resize
+          highlight-current-row
+          border
+          resizable
+          show-overflow
+          :edit-rules="rules"
+          :data="tableData"
+          v-show="showTable"
+          :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+        >
+          <vxe-table-column title="检修计划信息">
+            <vxe-table-column field="name" title="检修名称"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+            <vxe-table-column field="describe" title="描述"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+            <vxe-table-column width="14%" field="startTime" title="开始时间"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}">
+              <template v-slot:edit="{ row }">
+                <el-date-picker
+                  style="width: 100%"
+                  v-model="row.startTime"
+                  type="datetime"
+                  value-format="timestamp"
+                  placeholder="选择开始时间"
+                  :readonly=startTimeShow
+                  :picker-options="pickerOptions1"/>
+              </template>
+              <template v-slot="{ row }">{{ timestampToTime(row.startTime) }}
+              </template>
+            </vxe-table-column>
+            <vxe-table-column width="14%" field="endTime" title="结束时间"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}">
+              <template v-slot:edit="{ row }">
+                <el-date-picker
+                  style="width: 100%"
+                  v-model="row.endTime"
+                  type="datetime"
+                  value-format="timestamp"
+                  placeholder="选择结束时间"
+                  :picker-options="pickerOptions1"/>
+              </template>
+              <template v-slot="{ row }">{{
+                  timestampToTime(row.endTime)
+                }}
+              </template>
+            </vxe-table-column>
+            <!--            <vxe-table-column width="15%" field="finalEntryTime" title="生效时间"-->
+            <!--                              :edit-render="{}">-->
+            <!--              <template v-slot="{ row }">{{ timestampToTime(row.finalEntryTime) }}</template>-->
+            <!--            </vxe-table-column>-->
+            <vxe-table-column field="overhaulCapactity" title="检修容量(Mw)"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+            <vxe-table-column field="status" title="计划状态">
+              <template v-slot="{ row }">{{ row.status == 2 ? '已停止' : '执行中' }}</template>
+            </vxe-table-column>
+            <vxe-table-column width="10%" field="createTime" title="创建时间">
+              <template v-slot="{ row }">{{ timestampToTime1(row.createTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column width="10%" field="lastModifyTime" title="最后修改时间">
+              <template v-slot="{ row }">{{ timestampToTime1(row.lastModifyTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column width="10%" field="mcTime" title="手动停止时间">
+              <template v-slot="{ row }">{{ timestampToTime1(row.mcTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column fixed="right" width="150" title="操作">
+              <template v-slot="{ row }">
+                <template v-if="$refs.xTable.isActiveByRow(row)">
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    :loading="saveLoding"
+                    @click="saveCheck(row)">保存
+                  </el-button>
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="cancelRowEvent(row)">取消
+                  </el-button>
+                </template>
+                <template v-else>
+                  <el-button
+                    v-show="row.status==1"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="editRowEvent(row)"
+                    :loading="btnLonding">编辑
+                  </el-button>
+                  <el-button
+                    v-show="row.status==1&&row.finalEntryTime>now"
+                    type="danger"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-delete"
+                    @click="removeEvent(row)"
+                    :loading=btnLonding>删除
+                  </el-button>
+                  <el-button
+                    v-show="row.finalEntryTime<now&&row.id!=undefined&&row.status==1"
+                    type="success"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-check"
+                    :loading="saveLoding"
+                    title="手动停止"
+                    @click="fulSaveRowEvent(row)">停止
+                  </el-button>
+                </template>
+              </template>
+            </vxe-table-column>
+          </vxe-table-column>
+        </vxe-table>
+        <vxe-pager
+          perfect
+          :current-page.sync="currentPage"
+          :page-size.sync="pageSize"
+          :total="total"
+          :page-sizes=[10,50,100]
+          :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+          @page-change="handlePageChange"
+          v-show="showTable"
+        ><!--v-show="!btnLonding"-->
+        </vxe-pager>
+      </div>
+
+
+    </el-card>
+  </div>
+</template>
+<script>
+import request from '@/utils/request'
+import QRCode from 'qrcodejs2';
+export default {
+  data() {
+    return {
+      now: new Date().getTime(),
+      checkSign: '',
+      checkTableDate: [],
+      holidayType: '',
+      loading: false,
+      dialogVisible: false,
+      generateOverHaulPlanCodeContentData:'',
+      saveLoding: false,
+      showTable: true,
+      btnLonding: false,
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      elCapacity: 0,
+      startTimeShow:false,
+      //表单验证规则
+      rules: {
+        name: [
+          {required: true, message: ' 检修名称不能为空'},
+          {min: 1, max: 9, message: '称长度在 1 到 9 个字符'}
+        ],
+        describe: [
+          {required: true, message: '请填写检修计划描述,原因等'},
+          {min: 1, max: 9, message: '名称长度在 1 到 9 个字符'}
+        ],
+        startTime: [
+          {required: true, message: '开始时间不能为空'}
+        ],
+        overhaulCapactity: [
+          { required: true, message: '不能为空' },
+          { pattern: /^\d+(\.\d{2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 50}
+
+        ]
+      },
+      //当前日期之前日期不可选
+      pickerOptions1: {
+        disabledDate(time) {
+          return time.getTime() < Date.now() - 8.64e7;
+          // return time.getTime() <= Date.now();
+        }
+      }
+    }
+  },
+  created() {
+    this.getEl()
+  },
+  methods: {
+    getEl() {
+      this.$axios.get("/electricField/").then(response => {
+        this.elCapacity = response.data.backupE
+        this.getAll()
+      })
+    },
+    getAll() {
+      this.now = (new Date()).getTime()
+      this.loading = true
+      this.saveLoding = false
+      this.$axios.get("/overHaulPlan/" + this.currentPage + '/' + this.pageSize).then(response => {
+        if (response.data.content == "") {
+          this.showTable = false
+
+        } else {
+          this.showTable = true
+        }
+        this.tableData = response.data.content
+        this.total = response.data.count
+
+        if (response.data.totalElements != 0) {
+
+          this.total = response.data.totalElements
+        }
+        this.loading = false
+
+      })
+      this.generateOverHaulPlanCodeContent()
+    },
+    //编辑框改变
+    editRowEvent(row) {
+      this.startTimeShow = false
+      if(row.startTime < new Date().getTime()){
+        this.startTimeShow = true
+      }
+      this.checkSign = true
+      this.btnLonding = true
+      this.$refs.xTable.setActiveRow(row)
+    },
+    checkDate(v1, v2, v3, v4) {
+      let result = false
+      const item = ''
+      // 修改时所需便利集合
+      const v5 = []
+      // 所有假期存放集合
+
+      // 在修改时,把当前在所有信息集合里要修改的信息去掉(如果不去掉会影响校验)
+      for (let i = 0; i < v2.length; i++) {
+        if (v2[i].id == v4.id||v2[i].status == 2) {
+          continue
+        }
+        v5.push(v2[i])
+      }
+      // 编辑修改
+      if (v3) {
+        // 假期修改
+        for (let i = 0; i < v5.length; i++) {
+          if (v1[0] >= v5[i].startTime && v1[0] <= v5[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[1] >= v5[i].startTime && v1[1] <= v5[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] <= v5[i].startTime && v1[1] >= v5[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] >= v5[i].startTime && v1[1] <= v5[i].endTime) {
+            result = true
+            break
+          }
+        }
+      } else {
+        for (let i = 0; i < v2.length; i++) {
+          if (v1[0] >= v2[i].startTime && v1[0] <= v2[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[1] >= v2[i].startTime && v1[1] <= v2[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] <= v2[i].startTime && v1[1] >= v2[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] >= v2[i].startTime && v1[1] <= v2[i].endTime) {
+            result = true
+            break
+          }
+        }
+      }
+      return result
+    },
+    //数据校验
+    saveCheck(row) {
+
+      this.$refs.xTable.validate(row, callback => {
+        if(row.startTime<new Date().getTime()&&!this.startTimeShow){
+          this.$XModal.message({status: 'error', message: '开始时间不能小于当前时间!'})
+        }else if(row.overhaulCapactity>this.elCapacity){
+          this.$XModal.message({status: 'error', message: '检修容量不能大于装机容量!'})
+        } else if (row.startTime == null || row.endTime == null) {
+          // callback(new Error('开始结束日期不能为空'))
+          this.$XModal.message({status: 'error', message: '开始结束日期不能为空!'})
+        } else {
+          var timeStartEnd = [row.startTime,row.endTime]
+          if (this.checkDate(timeStartEnd, this.tableData, this.checkSign, row)) {
+            this.$XModal.message({status: 'error', message: '该时间段已有检修计划!'})
+          } else {
+            callback:this.saveRowEvent(row)
+          }
+        }
+      })
+    },
+    //保存数据操作
+    saveRowEvent(row) {
+      this.saveLoding = true
+      this.$refs.xTable.validate(valid => {
+        if (valid) {
+          if (row.id == undefined) {
+            row.status = '1'
+            this.$axios.post("/overHaulPlan/", row).then(response => {
+              this.getAll()
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+            })
+          } else {
+            this.$axios.put("/overHaulPlan/", row).then(response => {
+              this.getAll()
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+
+            })
+          }
+          this.btnLonding = false
+        } else {
+          this.$XModal.message({status: 'error', message: '校验不通过!'})
+          this.saveLoding = false
+        }
+
+      })
+    },
+    //停止数据操作
+    fulSaveRowEvent(row) {
+      this.$XModal.confirm('您点击了手动停止,是否确认停止?').then(type => {
+        this.saveLoding = true
+        this.$refs.xTable.validate(valid => {
+          if (valid) {
+            row.status = '2'
+            this.$axios.put("/overHaulPlan/", row).then(response => {
+              this.getAll()
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+            })
+          } else {
+            this.$XModal.message({status: 'error', message: '校验不通过!'})
+            this.saveLoding = false
+          }
+          this.btnLonding = false
+        })
+      })
+    },
+    //取消编辑或添加操作
+    cancelRowEvent(row) {
+      this.saveLoding = false
+      this.btnLonding = false
+      const xTable = this.$refs.xTable
+      if (row.id == undefined) {
+        this.$refs.xTable.remove(row)
+      } else {
+        xTable.clearActived().then(() => {
+          // 还原行数据
+          xTable.revertData(row)
+        })
+      }
+    },
+    //删除数据
+    removeEvent(row) {
+      this.$XModal.confirm('您确定要删除该数据?').then(type => {
+        if (type === 'confirm') {
+          this.$axios.delete("/overHaulPlan/", {data: row}).then(response => {
+            this.$XModal.message({status: 'warning', message: response.message})
+            this.getAll()
+          })
+        }
+      })
+    },
+    //时间戳日期转换器
+    timestampToTime(time) {
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      const h = " " + (date.getHours() < 10 ? '0' : '') + date.getHours() + ':'
+      const m = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':'
+      const s = (date.getSeconds() < 10 ? '0' : '') + date.getSeconds()
+      return Y + M + D + h + m + s
+    },
+    timestampToTime1(time) {
+      if (time == undefined) {
+        return '空'
+      }
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      const h = " " + (date.getHours() < 10 ? '0' : '') + date.getHours() + ':'
+      const m = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':'
+      const s = (date.getSeconds() < 10 ? '0' : '') + date.getSeconds()
+      return Y + M + D + h + m + s
+    },
+    //新增编辑框
+    insertEvent() {
+      this.startTimeShow = false
+      this.showTable = true
+      this.btnLonding = true
+      this.checkSign = false
+      this.timeStartEnd = []
+      this.$refs.xTable.insert()
+        .then(({row}) => this.$refs.xTable.setActiveRow(row))
+    },
+    //分页出发事件
+    handlePageChange({currentPage, pageSize}) {
+      this.currentPage = currentPage
+      this.pageSize = pageSize
+      this.getAll()
+    },
+    /*handleSizeChange(size) {
+        this.pageSize = size
+        this.getAll()
+    },
+    handleCurrentChange(currentPage) {
+        this.currentPage = currentPage
+        this.getAll()
+    },*/
+    //导出
+    qrShow(){
+      this.dialogVisible = true
+      this.$nextTick(() => {
+        var divs = document.getElementsByName('qrCodeDiv')
+        for(var j = 0;j<divs.length;j++){
+          divs[j].innerHTML = ""
+        }
+        new QRCode(this.$refs.qrCodeDiv, {
+          text: this.generateOverHaulPlanCodeContentData[0],
+          width: 300,
+          height: 300,
+          colorDark: "#333333", //二维码颜色
+          colorLight: "#ffffff", //二维码背景色
+          correctLevel: QRCode.CorrectLevel.L//容错率,L/M/H
+        })
+      })
+
+    },
+    generateOverHaulPlanCodeContent(){
+      this.$axios.get("/overHaulPlan/generateOverHaulPlanCodeContent").then(response => {
+        console.log(response.data)
+        this.generateOverHaulPlanCodeContentData =  response.data
+      })
+
+    },
+    cancel(){
+
+      this.dialogVisible = false
+    },
+    exportDataEvent() {
+      this.loading = true
+      this.$axios.get('/overHaulPlan/').then(res => {
+        const data = res.data
+
+        for(let i = 0;i<data.length;i++){
+          data[i].startTime = this.timestampToTime1(data[i].startTime)
+          data[i].endTime = this.timestampToTime1(data[i].endTime)
+          data[i].finalEntryTime = this.timestampToTime1(data[i].finalEntryTime)
+          data[i].createTime = this.timestampToTime1(data[i].createTime)
+          data[i].mcTime = this.timestampToTime1(data[i].mcTime)
+        }
+
+        this.$refs.xTable.exportData({
+          filename: '检修计划信息',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data
+        })
+        this.loading = false
+      }).catch(e => {
+      })
+    },
+    //导入
+    importDataEvent() {
+      this.$refs.xTable.importData({types: ['csv']})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  /*left: 0;*/
+  /*width: 100%;*/
+  /*min-height: 100%;*/
+  /*height: auto;*/
+  /*background-image: linear-gradient(25deg, #05362d, #145d44, #24875d, #35b477)*/
+
+}
+
+.pagination {
+  margin: 20px 0;
+  text-align: right;
+}
+</style>

+ 545 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/overHaulPlanOne/index.vue

@@ -0,0 +1,545 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>检修计划</span>
+<!--        <el-button style="float: right;padding:3px 10px 3px 3px;" type="text" @click="qrShow">二维码扫描上传-->
+<!--        </el-button>-->
+        <el-button style="float: right;padding:3px 10px 3px 3px;" type="text" @click="exportDataEvent">导出数据
+        </el-button>
+      </div>
+      <el-button type="primary" size="small" @click="insertEvent" style="round-clip: 10px"
+                 :loading="btnLonding">新增
+      </el-button>
+      <div>
+        <el-dialog
+          :close-on-click-modal="false"
+          :visible.sync="dialogVisible"
+          title="请扫描以下二维码"
+          style="padding: 0px;"
+          label-width="1px"
+          width="500px">
+          <!--            <el-image-->
+          <!--              src="http://www.baidu.com"-->
+          <!--              fit="fill"-->
+          <!--            />-->
+          <div id="qrCode" ref="qrCodeDiv" name="qrCodeDiv" align="center"></div>
+
+          <span
+            slot="footer"
+            class="dialog-footer">
+            <el-button
+              size="mini"
+              @click="cancel">关 闭</el-button>
+          </span>
+        </el-dialog>
+      </div>
+      <div style="padding-top: 10px">
+        <vxe-table
+          highlight-hover-row
+          :keep-source="true"
+          align="center"
+          :loading="loading"
+          v-loading="false"
+          ref="xTable"
+          auto-resize
+          highlight-current-row
+          border
+          resizable
+          show-overflow
+          :edit-rules="rules"
+          :data="tableData"
+          v-show="showTable"
+          :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+        >
+          <vxe-table-column title="检修计划信息">
+            <vxe-table-column field="name" title="检修名称"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+            <vxe-table-column field="describe" title="描述"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+            <vxe-table-column width="14%" field="startTime" title="开始时间"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}">
+              <template v-slot:edit="{ row }">
+                <el-date-picker
+                  style="width: 100%"
+                  v-model="row.startTime"
+                  type="datetime"
+                  value-format="timestamp"
+                  placeholder="选择开始时间"
+                  :readonly=startTimeShow
+                  :picker-options="pickerOptions1"/>
+              </template>
+              <template v-slot="{ row }">{{ timestampToTime(row.startTime) }}
+              </template>
+            </vxe-table-column>
+            <vxe-table-column width="14%" field="endTime" title="结束时间"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}">
+              <template v-slot:edit="{ row }">
+                <el-date-picker
+                  style="width: 100%"
+                  v-model="row.endTime"
+                  type="datetime"
+                  value-format="timestamp"
+                  placeholder="选择结束时间"
+                  :picker-options="pickerOptions1"/>
+              </template>
+              <template v-slot="{ row }">{{
+                  timestampToTime(row.endTime)
+                }}
+              </template>
+            </vxe-table-column>
+            <!--            <vxe-table-column width="15%" field="finalEntryTime" title="生效时间"-->
+            <!--                              :edit-render="{}">-->
+            <!--              <template v-slot="{ row }">{{ timestampToTime(row.finalEntryTime) }}</template>-->
+            <!--            </vxe-table-column>-->
+            <vxe-table-column field="overhaulCapactity" title="检修容量(Mw)"
+                              :edit-render="{name:'$input',attrs:{type:'text'}}"></vxe-table-column>
+            <vxe-table-column field="status" title="计划状态">
+              <template v-slot="{ row }">{{ row.status == 2 ? '已停止' : '执行中' }}</template>
+            </vxe-table-column>
+            <vxe-table-column width="10%" field="createTime" title="创建时间">
+              <template v-slot="{ row }">{{ timestampToTime1(row.createTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column width="10%" field="lastModifyTime" title="最后修改时间">
+              <template v-slot="{ row }">{{ timestampToTime1(row.lastModifyTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column width="10%" field="mcTime" title="手动停止时间">
+              <template v-slot="{ row }">{{ timestampToTime1(row.mcTime) }}</template>
+            </vxe-table-column>
+            <vxe-table-column fixed="right" width="150" title="操作">
+              <template v-slot="{ row }">
+                <template v-if="$refs.xTable.isActiveByRow(row)">
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    :loading="saveLoding"
+                    @click="saveCheck(row)">保存
+                  </el-button>
+                  <el-button
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="cancelRowEvent(row)">取消
+                  </el-button>
+                </template>
+                <template v-else>
+                  <el-button
+                    v-show="row.status==1"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-edit"
+                    @click="editRowEvent(row)"
+                    :loading="btnLonding">编辑
+                  </el-button>
+                  <el-button
+                    v-show="row.status==1&&row.finalEntryTime>now"
+                    type="danger"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-delete"
+                    @click="removeEvent(row)"
+                    :loading=btnLonding>删除
+                  </el-button>
+                  <el-button
+                    v-show="row.finalEntryTime<now&&row.id!=undefined&&row.status==1"
+                    type="success"
+                    style="padding: 3px 4px 3px 4px;margin: 2px"
+                    size="mini"
+                    icon="el-icon-check"
+                    :loading="saveLoding"
+                    title="手动停止"
+                    @click="fulSaveRowEvent(row)">停止
+                  </el-button>
+                </template>
+              </template>
+            </vxe-table-column>
+          </vxe-table-column>
+        </vxe-table>
+        <vxe-pager
+          perfect
+          :current-page.sync="currentPage"
+          :page-size.sync="pageSize"
+          :total="total"
+          :page-sizes=[10,50,100]
+          :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+          @page-change="handlePageChange"
+          v-show="showTable"
+        ><!--v-show="!btnLonding"-->
+        </vxe-pager>
+      </div>
+
+
+    </el-card>
+  </div>
+</template>
+<script>
+import request from '@/utils/request'
+import QRCode from 'qrcodejs2';
+export default {
+  data() {
+    return {
+      now: new Date().getTime(),
+      checkSign: '',
+      checkTableDate: [],
+      holidayType: '',
+      loading: false,
+      dialogVisible: false,
+      generateOverHaulPlanCodeContentData:'',
+      saveLoding: false,
+      showTable: true,
+      btnLonding: false,
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      elCapacity: 0,
+      startTimeShow:false,
+      //表单验证规则
+      rules: {
+        name: [
+          {required: true, message: ' 检修名称不能为空'},
+          {min: 1, max: 9, message: '称长度在 1 到 9 个字符'}
+        ],
+        describe: [
+          {required: true, message: '请填写检修计划描述,原因等'},
+          {min: 1, max: 9, message: '名称长度在 1 到 9 个字符'}
+        ],
+        startTime: [
+          {required: true, message: '开始时间不能为空'}
+        ],
+        overhaulCapactity: [
+          { required: true, message: '不能为空' },
+          { pattern: /^\d+(\.\d{2})?$/, message: '只能输入正数数字或带两位小数的数字' }
+          // {message: '输入过长', max: 50}
+
+        ]
+      },
+      //当前日期之前日期不可选
+      pickerOptions1: {
+        disabledDate(time) {
+          return time.getTime() < Date.now() - 8.64e7;
+          // return time.getTime() <= Date.now();
+        }
+      }
+    }
+  },
+  created() {
+    this.getEl()
+  },
+  methods: {
+    getEl() {
+      this.$axios.get("/electricField/").then(response => {
+        this.elCapacity = response.data.backupF
+        this.getAll()
+      })
+    },
+    getAll() {
+      this.now = (new Date()).getTime()
+      this.loading = true
+      this.saveLoding = false
+      this.$axios.get("/overHaulPlanOne/" + this.currentPage + '/' + this.pageSize).then(response => {
+        if (response.data.content == "") {
+          this.showTable = false
+
+        } else {
+          this.showTable = true
+        }
+        this.tableData = response.data.content
+        this.total = response.data.count
+
+        if (response.data.totalElements != 0) {
+
+          this.total = response.data.totalElements
+        }
+        this.loading = false
+
+      })
+      this.generateOverHaulPlanCodeContent()
+    },
+    //编辑框改变
+    editRowEvent(row) {
+      this.startTimeShow = false
+      if(row.startTime < new Date().getTime()){
+        this.startTimeShow = true
+      }
+      this.checkSign = true
+      this.btnLonding = true
+      this.$refs.xTable.setActiveRow(row)
+    },
+    checkDate(v1, v2, v3, v4) {
+      let result = false
+      const item = ''
+      // 修改时所需便利集合
+      const v5 = []
+      // 所有假期存放集合
+
+      // 在修改时,把当前在所有信息集合里要修改的信息去掉(如果不去掉会影响校验)
+      for (let i = 0; i < v2.length; i++) {
+        if (v2[i].id == v4.id||v2[i].status == 2) {
+          continue
+        }
+        v5.push(v2[i])
+      }
+      // 编辑修改
+      if (v3) {
+        // 假期修改
+        for (let i = 0; i < v5.length; i++) {
+          if (v1[0] >= v5[i].startTime && v1[0] <= v5[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[1] >= v5[i].startTime && v1[1] <= v5[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] <= v5[i].startTime && v1[1] >= v5[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] >= v5[i].startTime && v1[1] <= v5[i].endTime) {
+            result = true
+            break
+          }
+        }
+      } else {
+        for (let i = 0; i < v2.length; i++) {
+          if (v1[0] >= v2[i].startTime && v1[0] <= v2[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[1] >= v2[i].startTime && v1[1] <= v2[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] <= v2[i].startTime && v1[1] >= v2[i].endTime) {
+            result = true
+            break
+          }
+          if (v1[0] >= v2[i].startTime && v1[1] <= v2[i].endTime) {
+            result = true
+            break
+          }
+        }
+      }
+      return result
+    },
+    //数据校验
+    saveCheck(row) {
+
+      this.$refs.xTable.validate(row, callback => {
+        if(row.startTime<new Date().getTime()&&!this.startTimeShow){
+          this.$XModal.message({status: 'error', message: '开始时间不能小于当前时间!'})
+        }else if(row.overhaulCapactity>this.elCapacity){
+          this.$XModal.message({status: 'error', message: '检修容量不能大于装机容量!'})
+        } else if (row.startTime == null || row.endTime == null) {
+          // callback(new Error('开始结束日期不能为空'))
+          this.$XModal.message({status: 'error', message: '开始结束日期不能为空!'})
+        } else {
+          var timeStartEnd = [row.startTime,row.endTime]
+          if (this.checkDate(timeStartEnd, this.tableData, this.checkSign, row)) {
+            this.$XModal.message({status: 'error', message: '该时间段已有检修计划!'})
+          } else {
+            callback:this.saveRowEvent(row)
+          }
+        }
+      })
+    },
+    //保存数据操作
+    saveRowEvent(row) {
+      this.saveLoding = true
+      this.$refs.xTable.validate(valid => {
+        if (valid) {
+          if (row.id == undefined) {
+            row.status = '1'
+            this.$axios.post("/overHaulPlanOne/", row).then(response => {
+              this.getAll()
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+            })
+          } else {
+            this.$axios.put("/overHaulPlanOne/", row).then(response => {
+              this.getAll()
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+
+            })
+          }
+          this.btnLonding = false
+        } else {
+          this.$XModal.message({status: 'error', message: '校验不通过!'})
+          this.saveLoding = false
+        }
+
+      })
+    },
+    //停止数据操作
+    fulSaveRowEvent(row) {
+      this.$XModal.confirm('您点击了手动停止,是否确认停止?').then(type => {
+        this.saveLoding = true
+        this.$refs.xTable.validate(valid => {
+          if (valid) {
+            row.status = '2'
+            this.$axios.put("/overHaulPlanOne/", row).then(response => {
+              this.getAll()
+              this.$XModal.message({status: 'warning', message: response.message})
+              this.saveLoding = false
+            })
+          } else {
+            this.$XModal.message({status: 'error', message: '校验不通过!'})
+            this.saveLoding = false
+          }
+          this.btnLonding = false
+        })
+      })
+    },
+    //取消编辑或添加操作
+    cancelRowEvent(row) {
+      this.saveLoding = false
+      this.btnLonding = false
+      const xTable = this.$refs.xTable
+      if (row.id == undefined) {
+        this.$refs.xTable.remove(row)
+      } else {
+        xTable.clearActived().then(() => {
+          // 还原行数据
+          xTable.revertData(row)
+        })
+      }
+    },
+    //删除数据
+    removeEvent(row) {
+      this.$XModal.confirm('您确定要删除该数据?').then(type => {
+        if (type === 'confirm') {
+          this.$axios.delete("/overHaulPlanOne/", {data: row}).then(response => {
+            this.$XModal.message({status: 'warning', message: response.message})
+            this.getAll()
+          })
+        }
+      })
+    },
+    //时间戳日期转换器
+    timestampToTime(time) {
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      const h = " " + (date.getHours() < 10 ? '0' : '') + date.getHours() + ':'
+      const m = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':'
+      const s = (date.getSeconds() < 10 ? '0' : '') + date.getSeconds()
+      return Y + M + D + h + m + s
+    },
+    timestampToTime1(time) {
+      if (time == undefined) {
+        return '空'
+      }
+      const date = new Date(time) // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+      const Y = date.getFullYear() + '-'
+      const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+      const D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()
+      const h = " " + (date.getHours() < 10 ? '0' : '') + date.getHours() + ':'
+      const m = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':'
+      const s = (date.getSeconds() < 10 ? '0' : '') + date.getSeconds()
+      return Y + M + D + h + m + s
+    },
+    //新增编辑框
+    insertEvent() {
+      this.startTimeShow = false
+      this.showTable = true
+      this.btnLonding = true
+      this.checkSign = false
+      this.timeStartEnd = []
+      this.$refs.xTable.insert()
+        .then(({row}) => this.$refs.xTable.setActiveRow(row))
+    },
+    //分页出发事件
+    handlePageChange({currentPage, pageSize}) {
+      this.currentPage = currentPage
+      this.pageSize = pageSize
+      this.getAll()
+    },
+    /*handleSizeChange(size) {
+        this.pageSize = size
+        this.getAll()
+    },
+    handleCurrentChange(currentPage) {
+        this.currentPage = currentPage
+        this.getAll()
+    },*/
+    //导出
+    qrShow(){
+      this.dialogVisible = true
+      this.$nextTick(() => {
+        var divs = document.getElementsByName('qrCodeDiv')
+        for(var j = 0;j<divs.length;j++){
+          divs[j].innerHTML = ""
+        }
+        new QRCode(this.$refs.qrCodeDiv, {
+          text: this.generateOverHaulPlanCodeContentData[0],
+          width: 300,
+          height: 300,
+          colorDark: "#333333", //二维码颜色
+          colorLight: "#ffffff", //二维码背景色
+          correctLevel: QRCode.CorrectLevel.L//容错率,L/M/H
+        })
+      })
+
+    },
+    generateOverHaulPlanCodeContent(){
+      this.$axios.get("/overHaulPlanOne/generateOverHaulPlanCodeContent").then(response => {
+        console.log(response.data)
+        this.generateOverHaulPlanCodeContentData =  response.data
+      })
+
+    },
+    cancel(){
+
+      this.dialogVisible = false
+    },
+    exportDataEvent() {
+      this.loading = true
+      this.$axios.get('/overHaulPlanOne/').then(res => {
+        const data = res.data
+
+        for(let i = 0;i<data.length;i++){
+          data[i].startTime = this.timestampToTime1(data[i].startTime)
+          data[i].endTime = this.timestampToTime1(data[i].endTime)
+          data[i].finalEntryTime = this.timestampToTime1(data[i].finalEntryTime)
+          data[i].createTime = this.timestampToTime1(data[i].createTime)
+          data[i].mcTime = this.timestampToTime1(data[i].mcTime)
+        }
+
+        this.$refs.xTable.exportData({
+          filename: '检修计划信息',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data
+        })
+        this.loading = false
+      }).catch(e => {
+      })
+    },
+    //导入
+    importDataEvent() {
+      this.$refs.xTable.importData({types: ['csv']})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  /*left: 0;*/
+  /*width: 100%;*/
+  /*min-height: 100%;*/
+  /*height: auto;*/
+  /*background-image: linear-gradient(25deg, #05362d, #145d44, #24875d, #35b477)*/
+
+}
+
+.pagination {
+  margin: 20px 0;
+  text-align: right;
+}
+</style>

+ 196 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/powerPlanSD/index.vue

@@ -0,0 +1,196 @@
+<template>
+    <div class="app-container">
+        <el-card class="box-carde">
+            <div slot="header" class="clearfix">
+                <span>山东发电计划</span>
+            </div>
+
+            <el-header style="padding: 0px;display:flex;justify-content:space-between;align-items: center">
+                <div style="display: inline;">
+
+                    <el-date-picker
+                            v-model="dateTime"
+                            size="mini"
+                            type="datetimerange"
+                            range-separator="至"
+                            start-placeholder="开始日期"
+                            end-placeholder="结束日期"/>
+                    <el-button type="primary" size="mini" style="margin-left: 5px"
+                               @click="searchEmp">查询数据
+                    </el-button>
+                </div>
+            </el-header>
+            <div style="padding-top: 10px">
+                <el-tabs v-model="activeName" type="card">
+                    <el-tab-pane label="图表" name="first">
+                        <el-main style="padding-left: 0px;padding-top: 0px">
+                            <div class="container">
+                                <div id="forcastdataChart" ref="forcastdataChart"/>
+                            </div>
+                        </el-main>
+                    </el-tab-pane>
+
+                    <el-tab-pane label="列表" name="second">
+                        <vxe-table
+                                highlight-current-row
+                                :keep-source="true"
+                                align="center"
+                                export-config
+                                :loading="loading"
+                                ref="xTable"
+                                auto-resize
+                                border
+                                resizable
+                                show-overflow
+                                :data="tableData"
+                                height="500px"
+                                max-height="800px"
+                        >
+
+                            <vxe-table-column field="backupA" sortable title="时间"/>
+                            <vxe-table-column field="planValue" sortable title="值"/>
+
+                            <template v-slot:empty>
+                        <span style="color: red;">
+                          <p>没有更多数据了!</p>
+                        </span>
+                            </template>
+                        </vxe-table>
+                    </el-tab-pane>
+                </el-tabs>
+            </div>
+
+        </el-card>
+    </div>
+</template>
+<script>
+    export default {
+        data() {
+            return {
+                dateTime: [new Date(new Date().toLocaleDateString()).getTime(), new Date(new Date().toLocaleDateString()).getTime() + 60 * 60 * 24 * 1000],
+                filterName: '',
+                tableData: [],
+                stationData: [],
+                loading: false,
+                activeName: 'first',
+                fdataVals: [],
+                fdataMVals: [],
+                realpowers: [],
+                datatimes: [],
+                xzxs: '1',
+                yxts: '1',
+            }
+        },
+        mounted: function () {
+        },
+        methods: {
+            searchEmp() {
+                this.$axios.get('/pwoerPlanSD/' + Math.round(this.dateTime[0]) + '/' +
+                    Math.round(this.dateTime[1])).then((res) => {
+                    console.log(res.data)
+                    this.tableData = res.data
+                    this.fdataVals = []
+                    this.datatimes = []
+                    for (var i = 0; i < this.tableData.length; i++) {
+                        this.fdataVals.push(this.tableData[i].planValue)
+                        this.datatimes.push(this.tableData[i].planTime)
+                    }
+                    this.drawLine()
+                    this.loading = false
+                }).catch((error) => {
+                    this.$message.error('获取预测数据出错' + error)
+                })
+            },
+            updateEmp() {
+                if (this.stationName === '') {
+                    this.$message({
+                        message: '请选择场站!',
+                        type: 'warning'
+                    });
+                    return
+                }
+                this.loading = true
+                this.$axios.get('/forcastdata/' + this.stationName + '/' + Math.round(this.dateTime[0]) + '/' +
+                    Math.round(this.dateTime[1]) + '/' + this.xzxs + '/' + this.yxts
+                ).then((res) => {
+                    if (res.code === 0) {
+                        this.searchEmp()
+                    }
+                }).catch((error) => {
+                    this.loading = false
+                    this.$message.error('更新预测数据出错' + error)
+                })
+            },
+            createEmp() {
+                if (this.stationName === '') {
+                    this.$message({
+                        message: '请选择场站!',
+                        type: 'warning'
+                    });
+                    return
+                }
+                this.loading = true
+                this.$axios.get('/forcastdata/' + this.stationName).then((res) => {
+                    this.loading = false
+                    this.$message.info('文件生成成功!')
+                }).catch((error) => {
+                    this.$message.error('获取系统信息出错' + error)
+                })
+            },
+            drawLine() {
+                let elementById = document.getElementById('forcastdataChart')
+                let echarts = this.$echarts.init(elementById)
+                let option = {
+                    title: {
+                        text: '发电计划'
+                    },
+                    tooltip: {
+                        trigger: 'axis'
+                    },
+                    legend: {
+                        data: ['发电计划']
+                    },
+                    grid: {
+                        left: '3%',
+                        right: '4%',
+                        bottom: '3%',
+                        containLabel: true
+                    },
+                    toolbox: {
+                        feature: {
+                            saveAsImage: {}
+                        }
+                    },
+                    xAxis: {
+                        type: 'category',
+                        boundaryGap: false,
+                        data: this.datatimes
+                    },
+                    yAxis: {
+                        type: 'value'
+                    },
+                    series: [
+                        {
+                            name: '发电计划',
+                            type: 'line',
+                            data: this.fdataVals
+                        }
+                    ]
+                }
+                echarts.setOption(option, true)
+                window.onresize = echarts.resize// 图表自适应浏览器大小
+            },
+        }
+    }
+</script>
+<style>
+    .keyword-lighten {
+        color: #000;
+        background-color: #FFFF00;
+    }
+
+    #forcastdataChart {
+        width: 100%;
+        height: 400px;
+    }
+</style>

+ 457 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/pvModuleModel/index.vue

@@ -0,0 +1,457 @@
+<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
+
+  <div class="app-container">
+    <div>
+      <el-card>
+        <div slot="header" class="clearfix">
+          <b><span>光伏组件信息</span></b>
+
+          <el-button :loading="loadButton" style="float: right;padding:3px 10px 3px 3px;" type="text" @click="exportDataEvent">导出数据</el-button>
+          <!--  <el-upload
+                            :http-request="importDataEvent"
+                            style="float: right;padding:3px 3px 3px 16px;"
+                            action="/console/importPvModuleModelCsv/"
+                            accept=".xls">
+                        <el-button :loading="loadButton"   type="text">导入数据</el-button>
+                    </el-upload>-->
+        </div>
+
+        <div class="filter-container">
+          <el-button type="primary" size="small" style="round-clip: 10px" @click="insertEvent" :loading="loadButton">添加</el-button>
+        </div>
+        <div style="padding-top: 10px">
+          <vxe-table
+            ref="xTable"
+            border
+            export-config
+            keep-source
+            auto-resize
+            resizable
+            :edit-rules="rules"
+            :loading="loading"
+            :data="tableData"
+            v-show="showTable"
+            show-overflow
+            highlight-current-row
+            :edit-config="{trigger: 'manual', mode: 'row',autoClear:false,showStatus: true,icon:'none'}"
+          >
+            <vxe-table-column title="光伏组件信息" align="center">
+              <vxe-table-column
+                field="model"
+                title="组件型号"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="singleCap"
+                title="组件容量"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="maximumExcitationVoltage"
+                title="最大工作电压(V)"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="maximumPowerCurrent"
+                title="最大工作电流(A)"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="openCircuitVoltage"
+                title="开路电压(V)"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="shortCircuitCurrent"
+                title="短路电流(A)"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="efficiencyOfWork"
+                title="工作效率"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="rotationMode"
+                title="旋转方式"
+                width="8%"
+                align="center"
+                :edit-render="{name: '$select', options: pvRotationMode}"
+              />
+              <vxe-table-column
+                field="gradient"
+                title="安装倾斜度"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="singleArea"
+                title="组件面积(㎡)"
+                width="7%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                      field="backupC"
+                      title="电池板个数"
+                      width="7%"
+                      align="center"
+                      :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+
+              <vxe-table-column title="操作" width="15%" align="center">
+                <template v-slot="{ row }">
+                  <template v-if="$refs.xTable.isActiveByRow(row)">
+                    <el-button
+                      type="success"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="mini"
+                      icon="el-icon-edit"
+                      @click="editSave(row)"
+                    >保存
+                    </el-button>
+                    <el-button
+                      class="cancel-btn"
+                      icon="el-icon-refresh"
+                      type="warning"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="mini"
+                      @click="cancelRowEvent(row)"
+                    >取消
+                    </el-button>
+                  </template>
+                  <template v-else>
+                    <el-button
+                      :loading="loadButton"
+                      type="primary"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="mini "
+                      icon="el-icon-edit"
+                      @click="editRowEvent(row)"
+                    >编辑
+                    </el-button>
+                    <el-button
+                      :loading="loadButton"
+                      type="danger"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="mini "
+                      icon="el-icon-delete"
+                      @click="deleteRowEvent(row)"
+                    >删除
+                    </el-button>
+                  </template>
+                </template>
+              </vxe-table-column>
+            </vxe-table-column>
+          </vxe-table>
+
+          <vxe-pager
+            perfect
+            :current-page.sync="currentPage"
+            :page-size.sync="pageSize"
+            :total="total"
+            :page-sizes=[10,50,100]
+            :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+            @page-change="handlePageChange"
+            v-show="!loadButton&&showTable"
+          />
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 删除提示框 -->
+    <el-dialog :visible.sync="delVisible" title="提示" width="300px" center>
+      <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="deleteCancel()">取 消</el-button>
+        <el-button type="primary" @click="deleteInfo()">确 定</el-button>
+      </span>
+    </el-dialog>
+
+  </div>
+
+</template>
+
+<script>
+
+  export default {
+
+    data() {
+      const checkName = (rule, value, callback) => {
+        var s6 = this.tableData
+        if (value == null || value === '') {
+          callback(new Error('请填写光伏组件型号'))
+        }
+        for (let i = 0; i < s6.length; i++) {
+          if (this.id == '' || this.id == undefined) {
+            // 新增
+            if ((value == s6[i].model)) {
+              callback(new Error('型号不能重复'))
+            }
+          } else {
+            // 修改
+            if (this.id != s6[i].id) {
+              if ((value == s6[i].model)) {
+                callback(new Error('型号不能重复'))
+              }
+            }
+          }
+        }
+        callback()
+      }
+      return {
+        id:'',
+        total:0,
+        pageSize: 10,
+        currentPage: 1,
+        loadButton: false,
+        rowId: '',
+        showTable: true,
+        tableData: [],
+        delVisible: false,
+        loading: false,
+        // 是否为编辑
+        isEdit: false,
+        pvRotationMode: [],
+        // 表单验证规则
+        rules: {
+          model: [
+            { required: true, validator:checkName},
+            { message: '输入过长', max: 15 }
+          ],
+          singleCap: [
+            { required: true, message: '请填写组件容量' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          maximumExcitationVoltage: [
+            { required: true, message: '请填写最大工作电压' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          maximumPowerCurrent: [
+            { required: true, message: '请填写最大工作电流' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          openCircuitVoltage: [
+            { required: true, message: '请填写开路电压' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          shortCircuitCurrent: [
+            { required: true, message: '请填写短路电流' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          efficiencyOfWork: [
+            { required: true, message: '请填写工作效率' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          gradient: [
+            { required: true, message: '请填写倾斜角度' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 10 }
+          ],
+          rotationMode: [
+            { required: true, message: '请填写旋转模式' }
+          ],
+          singleArea: [
+            { required: true, message: '请填写单片面积' },
+            {pattern: /^\d+(\.\d+)?$/, message: '只能输入正数数字或带小数点的数字'},
+            { message: '输入过长', max: 15 }
+          ]
+
+
+        }
+      }
+    },
+    created() {
+      this.getData(this.currentPage, this.pageSize)
+    },
+
+    methods: {
+      getData(currentPage, pageSize) {
+        this.loading = true
+        this.$axios.get('/pvModuleModel/' + currentPage + '/' + pageSize,).then((res) => {
+          this.tableData = res.data.content
+          if(res.data.content==''){
+            this.showTable = false
+          }else{
+            this.showTable = true
+          }
+          this.total = res.data.count
+          // console.log('获取光伏组件信息成功')
+          this.getPvRotationMode()
+          this.loading = false
+        }).catch((error) => {
+          this.$message.error('获取光伏组件信息出错' + error)
+        })
+      },
+      getPvRotationMode() {
+        this.$axios.get('/getPvRotationMode/').then((res) => {
+          this.pvRotationMode = res.data
+          // console.log('获取光伏组件旋转枚举信息成功')
+        }).catch((error) => {
+          this.$message.error('获取光伏组件旋转枚举信息出错' + error)
+        })
+      },
+      handlePageChange({ currentPage, pageSize }) {
+        this.currentPage = currentPage
+        this.pageSize = pageSize
+        this.getData(this.currentPage, this.pageSize)
+      },
+      insertEvent(row) {
+        this.id = ''
+        this.showTable = true
+        this.isEdit = false
+        this.loadButton = true
+        this.$refs.xTable.insert()
+          .then(({ row }) => this.$refs.xTable.setActiveRow(row))
+      },
+      editRowEvent(row) {
+        this.id = row.id
+        this.isEdit = true
+        this.loadButton = true
+        this.$refs.xTable.setActiveRow(row)
+      },
+      editSave(row) {
+        this.$refs.xTable.validate(this.$refs.xTable.getCurrentRecord(),valid => {
+          if (valid) {
+            this.$refs.xTable.clearActived().then(() => {
+              if (this.isEdit) {
+                // 编辑保存
+                this.$axios.put('/pvModuleModel/',row).then(res => {
+                  this.$message({
+                    message: '修改成功',
+                    type: 'success'
+                  })
+                  this.loadButton = false
+                  this.getData(this.currentPage, this.pageSize)
+                }).catch((error) => {
+                  this.$refs.xTable.setActiveRow(row)
+                  this.$message.error('修改光伏组件出错' + error)
+                })
+              } else {
+                // 新增保存
+
+                this.$axios.post('/pvModuleModel/',row).then(res => {
+                  this.$message({
+                    message: '保存成功',
+                    type: 'success'
+                  })
+                  this.loadButton = false
+                  this.getData(this.currentPage, this.pageSize)
+                }).catch((error) => {
+                  this.$refs.xTable.setActiveRow(row)
+                  this.$message.error('保存光伏组件出错' + error)
+                })
+              }
+            })
+          } else {
+            this.$XModal.message({ status: 'error', message: '校验不通过!' })
+          }
+        })
+      },
+      cancelRowEvent(row) {
+        const xTable = this.$refs.xTable
+        xTable.clearActived().then(() => {
+          // 还原行数据
+          if (this.isEdit) {
+            // 编辑
+            xTable.revertData(row)
+          } else {
+            // 新增
+            xTable.remove(row)
+          }
+          this.loadButton = false
+        })
+      },
+      // 删除场站信息
+      deleteRowEvent(row) {
+        this.rowId = row.id
+        this.delVisible = true
+      },
+      deleteCancel() {
+        this.delVisible = false
+      },
+      deleteInfo() {
+        this.$axios.delete('/pvModuleModel/'+this.rowId).then(res => {
+          this.$message({
+            message: '删除成功',
+            type: 'success'
+          })
+          this.delVisible = false
+          this.getData(this.currentPage, this.pageSize)
+        }).catch((error) => {
+          this.$message.error('删除光伏组件信息出错' + error)
+        })
+      },
+
+      exportDataEvent() {
+        this.loading = true
+        this.$axios.get('/pvModuleModel/').then(res => {
+          const data = res.data.content
+          this.$refs.xTable.exportData({
+            filename: '光伏组件信息',
+            type: 'csv',
+            isHeader: true,
+            isFooter: true,
+            data
+          })
+          this.loading = false
+        }).catch(e => {
+          this.loading = false
+        })
+      },
+      dataUpload(item) {
+
+      },
+      importDataEvent(item) {
+        this.loadButton = true
+        const formData = new FormData()
+        formData.append('file', item.file)
+        console.log('上传文件', item.file)
+        this.$axios.post('/pvModuleModel/').then(res => {
+          this.$message({
+            message: '导入光伏组件信息成功',
+            type: 'success'
+          })
+        }).catch((error) => {
+          this.$message({
+            message: '导入光伏组件信息成功',
+            type: 'success'
+          })
+        })
+
+        this.loadButton = false
+
+        /*  this.$refs.xTable.importData({ types: ['csv'] })*/
+      }
+
+    }
+  }
+</script>
+
+<style scoped>
+
+  .my_table_insert .vxe-body--row.is--new {
+    background-color: #f1fdf1;
+  }
+</style>

+ 326 - 0
ipfcst/ipfcst-console/src/main/frontend/views/console/subsidiaryEquipmentInfo/index.vue

@@ -0,0 +1,326 @@
+<template>
+  <div class="app-container">
+    <div>
+      <el-card>
+        <div slot="header" class="clearfix">
+          <b><span>附属设备信息</span></b>
+          <el-button
+            :loading="loadButton"
+            style="float: right;padding:3px 10px 3px 3px;"
+            type="text"
+            @click="exportDataEvent"
+          >导出数据
+          </el-button>
+        </div>
+        <el-button type="primary" size="small" style="round-clip: 10px" :loading="loadButton" @click="insertEvent">添加
+        </el-button>
+        <div style="padding-top: 10px">
+          <vxe-table
+            v-show="showTable"
+            ref="xTable"
+            export-config
+            :keep-source="true"
+            align="center"
+            :loading="loading"
+            highlight-current-row
+            auto-resize
+            border
+            resizable
+            max-height="600px"
+            :edit-rules="rules"
+            :data="tableData"
+            :edit-config="{trigger: 'manual', mode: 'row',autoClear: false,icon:'none'}"
+          >
+            <vxe-table-column title="附属设备信息" align="center">
+              <vxe-table-column
+                field="name"
+                title="名称"
+                width="25%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="equipmentType.code"
+                title="设备类型"
+                width="20%"
+                :edit-render="{name: '$select', options: equipmentTypeEnum}"
+                :filters="this.equipmentTypeEnum"
+                :filter-multiple="false"
+                :formatter="formatterEquipmentType"
+              />
+              <vxe-table-column
+                field="code"
+                title="编号"
+                width="15%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column
+                field="backupA"
+                title="备注字段"
+                width="15%"
+                align="center"
+                :edit-render="{name: '$input', attrs: {type: 'text'}}"
+              />
+              <vxe-table-column title="操作" fixed="right" width="10%" align="center">
+                <template v-slot="{ row }">
+                  <template v-if="$refs.xTable.isActiveByRow(row)">
+                    <el-button
+                      type="success"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium"
+                      icon="el-icon-edit"
+                      @click="editSave(row)"
+                    >保存
+                    </el-button>
+                    <el-button
+                      class="cancel-btn"
+                      icon="el-icon-refresh"
+                      type="warning"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium"
+                      @click="cancelRowEvent(row)"
+                    >取消
+                    </el-button>
+                  </template>
+                  <template v-else>
+                    <el-button
+                      :loading="loadButton"
+                      type="primary"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium "
+                      icon="el-icon-edit"
+                      @click="editRowEvent(row)"
+                    >编辑
+                    </el-button>
+                    <el-button
+                      :loading="loadButton"
+                      type="danger"
+                      style="padding: 3px 4px 3px 4px;margin: 2px;"
+                      size="medium "
+                      icon="el-icon-delete"
+                      @click="deleteRowEvent(row)"
+                    >删除
+                    </el-button>
+                  </template>
+                </template>
+              </vxe-table-column>
+
+            </vxe-table-column>
+          </vxe-table>
+          <vxe-pager
+            v-show="!loadButton&&showTable"
+            perfect
+            :current-page.sync="currentPage"
+            :page-size.sync="pageSize"
+            :total="total"
+            :page-sizes="[10,50,100]"
+            :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+          />
+        </div>
+      </el-card>
+    </div>
+    <!-- 删除提示框 -->
+    <el-dialog :visible.sync="delVisible" title="提示" width="300px" center>
+      <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="deleteCancel()">取 消</el-button>
+        <el-button type="primary" @click="deleteInfo()">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Index',
+  data() {
+    const checkName = (rule, value, callback) => {
+      var s6 = this.tableData
+      if (value == null || value === '') {
+        callback(new Error('请填写设备名称'))
+      }
+      for (let i = 0; i < s6.length; i++) {
+        if (this.mid === '' || this.mid === undefined) {
+          // 新增
+          if ((value === s6[i].name)) {
+            callback(new Error('名称不能重复'))
+          }
+        } else {
+          // 修改
+          if (this.mid !== s6[i].id) {
+            if ((value === s6[i].name)) {
+              callback(new Error('名称不能重复'))
+            }
+          }
+        }
+      }
+      callback()
+    }
+    return {
+      mid: '',
+      rowId: '',
+      loadButton: false,
+      loading: false,
+      showTable: true,
+      delVisible: false,
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      equipmentTypeEnum: [],
+      tableData: [],
+      intervals: [{ key: 0, value: 0, label: '不入库' }, { key: 60, value: 60, label: '一分钟' }, {
+        key: 900,
+        value: 900,
+        label: '十五分钟'
+      }],
+      // 是否为编辑
+      isEdit: false,
+      // 表单验证规则
+      rules: {
+        name: [
+          { required: true, validator: checkName }
+        ],
+        equipmentType: [
+          { required: true, message: '附属设备型号不能为空', trigger: 'blur' }
+        ],
+        code: [
+          { required: true, message: '编号不能为空' }
+        ]
+      }
+    }
+  },
+  mounted() {
+    this.getSubsidiaryEquipmentTypeEnum()
+    this.getInfo()
+
+  },
+  methods: {
+    getInfo() {
+      this.$axios.get('/subsidiaryEquipmentInfo').then(res => {
+        this.tableData = res.data
+        this.total = res.data.length
+        this.loading = false
+      }).catch((error) => {
+        this.$message.error('获取附属设备信息出错' + error)
+      })
+    },
+    formatterEquipmentType({ cellValue }) {
+      const item = this.equipmentTypeEnum.find(item => item.value === cellValue)
+      return item ? item.label : ''
+    },
+    editSave(row) {
+      this.$refs.xTable.validate(this.$refs.xTable.getCurrentRecord(), valid => {
+        if (valid) {
+          this.$refs.xTable.clearActived().then(() => {
+            if (this.isEdit) {
+              row.equipmentType = row.equipmentType.code
+              // 编辑保存
+              this.$axios.put('/subsidiaryEquipmentInfo/', row).then(res => {
+                this.$message({
+                  message: '修改成功',
+                  type: 'success'
+                })
+                this.loadButton = false
+                this.getInfo()
+              }).catch((error) => {
+                this.$refs.xTable.setActiveRow(row)
+                this.$message.error('修改附属设备出错' + error)
+              })
+            } else {
+              // 新增保存
+              row.equipmentType = row.equipmentType.code
+              this.$axios.post('/subsidiaryEquipmentInfo/', row).then(res => {
+                this.$message({
+                  message: '保存成功',
+                  type: 'success'
+                })
+                this.loadButton = false
+                this.getInfo()
+              }).catch((error) => {
+                this.$refs.xTable.setActiveRow(row)
+                this.$message.error('保存附属设备出错' + error)
+              })
+            }
+          })
+        } else {
+          this.$XModal.message({ status: 'error', message: '校验不通过!' })
+        }
+      })
+    },
+    getSubsidiaryEquipmentTypeEnum() {
+      this.$axios.get('dataExchange/getSubsidiaryEquipmentTypeEnum').then(res => {
+        this.equipmentTypeEnum = []
+        // eslint-disable-next-line no-unused-vars,no-empty
+        for (const i in res.data) {
+          this.equipmentTypeEnum.push({ 'label': res.data[i].label, 'value': res.data[i].code })
+        }
+      })
+    },
+    editRowEvent(row) {
+      this.mid = row.id
+      this.isEdit = true
+      this.loadButton = true
+      this.$refs.xTable.setActiveRow(row)
+    },
+    insertEvent(row) {
+      this.mid = ''
+      this.showTable = true
+      this.piadd = false
+      this.isEdit = false
+      this.loadButton = true
+      this.$refs.xTable.insert({ interval: 0 }).then(({ row }) => this.$refs.xTable.setActiveRow(row))
+    },
+    exportDataEvent() {
+      this.$axios.get('/subsidiaryEquipmentInfo/').then(res => {
+        const data = res.data
+        this.$refs.xTable.exportData({
+          filename: '附属设备信息',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data
+        })
+      })
+    },
+    cancelRowEvent(row) {
+      const xTable = this.$refs.xTable
+      xTable.clearActived().then(() => {
+        // 还原行数据
+        if (this.isEdit) {
+          // 编辑
+          xTable.revertData(row)
+        } else {
+          // 新增
+          xTable.remove(row)
+        }
+        this.loadButton = false
+      })
+    },
+    // 删除场站信息
+    deleteRowEvent(row) {
+      this.rowId = row.id
+      this.delVisible = true
+    },
+    deleteCancel() {
+      this.delVisible = false
+    },
+    deleteInfo() {
+      this.$axios.delete('/subsidiaryEquipmentInfo/' + this.rowId).then(res => {
+        this.$message({
+          message: '删除成功',
+          type: 'success'
+        })
+        this.delVisible = false
+        this.getInfo()
+      }).catch((error) => {
+        this.$message.error('删除附属设备信息出错' + error)
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

Някои файлове не бяха показани, защото твърде много файлове са промени