Browse Source

创建仓库,初次上传项目

Koishi 1 year ago
commit
e802955905
100 changed files with 18356 additions and 0 deletions
  1. 6 0
      .gitignore
  2. 21 0
      LICENSE
  3. 1 0
      README.md
  4. 100 0
      default
  5. 16 0
      index.html
  6. 194 0
      npminstall-debug.log
  7. 5990 0
      package-lock.json
  8. 70 0
      package.json
  9. BIN
      public/favicon.ico
  10. BIN
      public/static/sound/lv4.mp3
  11. BIN
      public/static/sound/lv5.mp3
  12. 13 0
      src/App.vue
  13. 0 0
      src/api/alarm-new.js
  14. 809 0
      src/api/api.js
  15. 96 0
      src/api/axios.js
  16. 11 0
      src/api/config.js
  17. 28 0
      src/assets/css/color-dark.css
  18. 23 0
      src/assets/css/custom.scss
  19. 4 0
      src/assets/css/icon.css
  20. 165 0
      src/assets/css/main.css
  21. 179 0
      src/assets/excel/Blob.js
  22. 144 0
      src/assets/excel/Export2Excel.js
  23. 46 0
      src/assets/font/iconfont.css
  24. BIN
      src/assets/font/iconfont.ttf
  25. BIN
      src/assets/font/iconfont.woff
  26. BIN
      src/assets/font/iconfont.woff2
  27. BIN
      src/assets/img/bg.png
  28. BIN
      src/assets/img/bianjiangA.png
  29. BIN
      src/assets/img/bianjiangB.png
  30. BIN
      src/assets/img/bianpinA.png
  31. BIN
      src/assets/img/bianpinB.png
  32. BIN
      src/assets/img/cefengA.png
  33. BIN
      src/assets/img/cefengB.png
  34. BIN
      src/assets/img/chilunA.png
  35. BIN
      src/assets/img/chilunB.png
  36. BIN
      src/assets/img/default.jpg
  37. BIN
      src/assets/img/fadianA.png
  38. BIN
      src/assets/img/fadianB.png
  39. BIN
      src/assets/img/icon-excel.png
  40. BIN
      src/assets/img/pianhangA.png
  41. BIN
      src/assets/img/pianhangB.png
  42. BIN
      src/assets/img/qitaA.png
  43. BIN
      src/assets/img/qitaB.png
  44. BIN
      src/assets/img/resultA.png
  45. BIN
      src/assets/img/resultB.png
  46. BIN
      src/assets/img/topIntell.png
  47. BIN
      src/assets/img/topIntell1.png
  48. BIN
      src/assets/img/topIntell2.png
  49. BIN
      src/assets/img/topIntell3.png
  50. BIN
      src/assets/img/xunjianA.png
  51. BIN
      src/assets/img/xunjianB.png
  52. BIN
      src/assets/img/yepianA.png
  53. BIN
      src/assets/img/yepianB.png
  54. BIN
      src/assets/img/yeyaA.png
  55. BIN
      src/assets/img/yeyaB.png
  56. BIN
      src/assets/img/yujinga.png
  57. BIN
      src/assets/img/yujingb.png
  58. BIN
      src/assets/img/zhuanjiaA.png
  59. BIN
      src/assets/img/zhuanjiaB.png
  60. BIN
      src/assets/img/zhuzhouA.png
  61. BIN
      src/assets/img/zhuzhouB.png
  62. 67 0
      src/assets/js/dialogDrag.js
  63. 233 0
      src/components/Header.vue
  64. 73 0
      src/components/Sidebar.vue
  65. 173 0
      src/components/Tags.vue
  66. 401 0
      src/components/alarmPopupa/index.vue
  67. 20 0
      src/components/vpro-materials/basic-container/BasicContainer.vue
  68. 3 0
      src/components/vpro-materials/basic-container/index.js
  69. 3 0
      src/components/vpro-materials/vpro-form/index.js
  70. 1004 0
      src/components/vpro-materials/vpro-form/vProForm.vue
  71. 3 0
      src/components/vpro-materials/vpro-table/index.js
  72. 628 0
      src/components/vpro-materials/vpro-table/vProTable.vue
  73. 254 0
      src/components/warningCom/multiple-y-line-chart-normal.vue
  74. 50 0
      src/components/warningCom/panel3.vue
  75. 288 0
      src/components/warningCom/table3.vue
  76. 47 0
      src/composables/common.js
  77. 57 0
      src/main.js
  78. 353 0
      src/pages/Dashboard.vue
  79. 65 0
      src/pages/Home.vue
  80. 211 0
      src/pages/Login.vue
  81. 109 0
      src/pages/alarmConfig/Logs.vue
  82. 545 0
      src/pages/alarmConfig/bj_custom/custom.vue
  83. 818 0
      src/pages/alarmConfig/bj_custom/custom_components.vue
  84. 252 0
      src/pages/alarmConfig/bj_scada/scada.vue
  85. 197 0
      src/pages/alarmConfig/bj_scada/scada_components.vue
  86. 646 0
      src/pages/alarmConfig/bj_windturbine/windturbine.vue
  87. 390 0
      src/pages/alarmConfig/bj_windturbine/windturbine_components.vue
  88. 3 0
      src/pages/alarmConfig/inverter.vue
  89. 3 0
      src/pages/alarmConfig/station.vue
  90. 458 0
      src/pages/baseData/FanDataStatisticChart.vue
  91. 447 0
      src/pages/baseData/FanDataStatisticTable.vue
  92. 353 0
      src/pages/baseData/device.vue
  93. 200 0
      src/pages/baseData/dictionary.vue
  94. 277 0
      src/pages/baseData/fan_components/custom.vue
  95. 665 0
      src/pages/baseData/fan_components/custom_components.vue
  96. 326 0
      src/pages/baseData/fan_components/faultmode.vue
  97. 76 0
      src/pages/baseData/fan_components/message.vue
  98. 345 0
      src/pages/baseData/fan_components/point.vue
  99. 397 0
      src/pages/baseData/fan_components/windturbine.vue
  100. 0 0
      src/pages/baseData/fan_components/windturbine_components.vue

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.DS_Store
+node_modules/
+.vscode
+dist
+dist_electron
+dist.zip

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Ryzen
+
+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.

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# 报警管理系统 - 晋能版

+ 100 - 0
default

@@ -0,0 +1,100 @@
+##
+# You should look at the following URL's in order to grasp a solid understanding
+# of Nginx configuration files in order to fully unleash the power of Nginx.
+# https://www.nginx.com/resources/wiki/start/
+# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
+# https://wiki.debian.org/Nginx/DirectoryStructure
+#
+# In most cases, administrators will remove this file from sites-enabled/ and
+# leave it as reference inside of sites-available where it will continue to be
+# updated by the nginx packaging team.
+#
+# This file will automatically load configuration files provided by other
+# applications, such as Drupal or Wordpress. These applications will be made
+# available underneath a path with that package name, such as /drupal8.
+#
+# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
+##
+
+# Default server configuration
+#
+server {
+	listen 8078 default_server;
+	listen [::]:8078 default_server;
+
+	# SSL configuration
+	#
+	# listen 443 ssl default_server;
+	# listen [::]:443 ssl default_server;
+	#
+	# Note: You should disable gzip for SSL traffic.
+	# See: https://bugs.debian.org/773332
+	#
+	# Read up on ssl_ciphers to ensure a secure configuration.
+	# See: https://bugs.debian.org/765782
+	#
+	# Self signed certs generated by the ssl-cert package
+	# Don't use them in a production server!
+	#
+	# include snippets/snakeoil.conf;
+
+	root /root/dist;
+
+	# Add index.php to the list if you are using PHP
+	index index.html index.htm index.nginx-debian.html;
+
+	server_name _;
+
+	location / {
+		# First attempt to serve request as file, then
+		# as directory, then fall back to displaying a 404.
+		try_files $uri $uri/ =404;
+	}
+	location /sharding {
+		proxy_pass http://10.81.3.154:8075/;
+	}
+	
+	# 代理文档服务
+	location /adapter {
+		proxy_pass http://123.60.219.66:8011/;
+	}
+
+	# pass PHP scripts to FastCGI server
+	#
+	#location ~ \.php$ {
+	#	include snippets/fastcgi-php.conf;
+	#
+	#	# With php-fpm (or other unix sockets):
+	#	fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
+	#	# With php-cgi (or other tcp sockets):
+	#	fastcgi_pass 127.0.0.1:9000;
+	#}
+
+	# deny access to .htaccess files, if Apache's document root
+	# concurs with nginx's one
+	#
+	#location ~ /\.ht {
+	#	deny all;
+	#}
+}
+
+
+# Virtual Host configuration for example.com
+#
+# You can move that to a different file under sites-available/ and symlink that
+# to sites-enabled/ to enable it.
+#
+#server {
+#	listen 80;
+#	listen [::]:80;
+#
+#	server_name example.com;
+#
+#	root /var/www/example.com;
+#	index index.html;
+#
+#	location / {
+#		try_files $uri $uri/ =404; 
+#	}
+#}
+# index.nginx-debian.html

+ 16 - 0
index.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>alarm-config</title>
+</head>
+
+<body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+</body>
+
+</html>

+ 194 - 0
npminstall-debug.log

@@ -0,0 +1,194 @@
+{
+  root: 'E:\\vue-workspace\\intelligentwarning',
+  registry: 'https://registry.npmjs.org',
+  pkgs: [],
+  production: false,
+  cacheStrict: false,
+  cacheDir: 'C:\\Users\\shilin\\.npminstall_tarball',
+  env: {
+    npm_config_registry: 'https://registry.npmjs.org',
+    npm_config_argv: '{"remain":[],"cooked":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\shilin\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmjs.org"],"original":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\shilin\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmjs.org"]}',
+    npm_config_user_agent: 'npminstall/7.9.0 npm/? node/v16.19.1 win32 x64',
+    npm_config_cache: 'C:\\Users\\shilin\\.npminstall_tarball',
+    NODE: 'C:\\Program Files\\nodejs\\node.exe',
+    npm_node_execpath: 'C:\\Program Files\\nodejs\\node.exe',
+    npm_execpath: 'C:\\Users\\shilin\\AppData\\Roaming\\npm\\node_modules\\cnpm\\node_modules\\npminstall\\bin\\install.js',
+    npm_config_userconfig: 'C:\\Users\\shilin\\.cnpmrc',
+    npm_config_disturl: 'https://cdn.npmmirror.com/binaries/node',
+    npm_config_r: 'https://registry.npmjs.org',
+    COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com',
+    NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
+    NVM_NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
+    PHANTOMJS_CDNURL: 'https://cdn.npmmirror.com/binaries/phantomjs',
+    CHROMEDRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/chromedriver',
+    OPERADRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/operadriver',
+    CYPRESS_DOWNLOAD_PATH_TEMPLATE: 'https://cdn.npmmirror.com/binaries/cypress/${version}/${platform}-${arch}/cypress.zip',
+    ELECTRON_MIRROR: 'https://cdn.npmmirror.com/binaries/electron/',
+    ELECTRON_BUILDER_BINARIES_MIRROR: 'https://cdn.npmmirror.com/binaries/electron-builder-binaries/',
+    SASS_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-sass',
+    SWC_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-swc',
+    NWJS_URLBASE: 'https://cdn.npmmirror.com/binaries/nwjs/v',
+    PUPPETEER_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
+    PUPPETEER_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
+    PLAYWRIGHT_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/playwright',
+    SENTRYCLI_CDNURL: 'https://cdn.npmmirror.com/binaries/sentry-cli',
+    SAUCECTL_INSTALL_BINARY_MIRROR: 'https://cdn.npmmirror.com/binaries/saucectl',
+    RE2_DOWNLOAD_MIRROR: 'https://cdn.npmmirror.com/binaries/node-re2',
+    RE2_DOWNLOAD_SKIP_PATH: 'true',
+    PRISMA_ENGINES_MIRROR: 'https://cdn.npmmirror.com/binaries/prisma',
+    npm_config_better_sqlite3_binary_host: 'https://cdn.npmmirror.com/binaries/better-sqlite3',
+    npm_config_keytar_binary_host: 'https://cdn.npmmirror.com/binaries/keytar',
+    npm_config_sharp_binary_host: 'https://cdn.npmmirror.com/binaries/sharp',
+    npm_config_sharp_libvips_binary_host: 'https://cdn.npmmirror.com/binaries/sharp-libvips',
+    npm_config_robotjs_binary_host: 'https://cdn.npmmirror.com/binaries/robotjs',
+    npm_rootpath: 'E:\\vue-workspace\\intelligentwarning',
+    INIT_CWD: 'E:\\vue-workspace\\intelligentwarning'
+  },
+  binaryMirrors: {
+    ENVS: {
+      COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com',
+      NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
+      NVM_NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
+      PHANTOMJS_CDNURL: 'https://cdn.npmmirror.com/binaries/phantomjs',
+      CHROMEDRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/chromedriver',
+      OPERADRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/operadriver',
+      CYPRESS_DOWNLOAD_PATH_TEMPLATE: 'https://cdn.npmmirror.com/binaries/cypress/${version}/${platform}-${arch}/cypress.zip',
+      ELECTRON_MIRROR: 'https://cdn.npmmirror.com/binaries/electron/',
+      ELECTRON_BUILDER_BINARIES_MIRROR: 'https://cdn.npmmirror.com/binaries/electron-builder-binaries/',
+      SASS_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-sass',
+      SWC_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-swc',
+      NWJS_URLBASE: 'https://cdn.npmmirror.com/binaries/nwjs/v',
+      PUPPETEER_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
+      PUPPETEER_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
+      PLAYWRIGHT_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/playwright',
+      SENTRYCLI_CDNURL: 'https://cdn.npmmirror.com/binaries/sentry-cli',
+      SAUCECTL_INSTALL_BINARY_MIRROR: 'https://cdn.npmmirror.com/binaries/saucectl',
+      RE2_DOWNLOAD_MIRROR: 'https://cdn.npmmirror.com/binaries/node-re2',
+      RE2_DOWNLOAD_SKIP_PATH: 'true',
+      PRISMA_ENGINES_MIRROR: 'https://cdn.npmmirror.com/binaries/prisma',
+      npm_config_better_sqlite3_binary_host: 'https://cdn.npmmirror.com/binaries/better-sqlite3',
+      npm_config_keytar_binary_host: 'https://cdn.npmmirror.com/binaries/keytar',
+      npm_config_sharp_binary_host: 'https://cdn.npmmirror.com/binaries/sharp',
+      npm_config_sharp_libvips_binary_host: 'https://cdn.npmmirror.com/binaries/sharp-libvips',
+      npm_config_robotjs_binary_host: 'https://cdn.npmmirror.com/binaries/robotjs'
+    },
+    '@ali/s2': { host: 'https://cdn.npmmirror.com/binaries/looksgood-s2' },
+    sharp: { replaceHostFiles: [Array], replaceHostMap: [Object] },
+    '@tensorflow/tfjs-node': {
+      replaceHostFiles: [Array],
+      replaceHostRegExpMap: [Object],
+      replaceHostMap: [Object]
+    },
+    cypress: {
+      host: 'https://cdn.npmmirror.com/binaries/cypress',
+      newPlatforms: [Object]
+    },
+    'utf-8-validate': {
+      host: 'https://cdn.npmmirror.com/binaries/utf-8-validate/v{version}'
+    },
+    xprofiler: {
+      remote_path: './xprofiler/v{version}/',
+      host: 'https://cdn.npmmirror.com/binaries'
+    },
+    leveldown: { host: 'https://cdn.npmmirror.com/binaries/leveldown/v{version}' },
+    couchbase: { host: 'https://cdn.npmmirror.com/binaries/couchbase/v{version}' },
+    gl: { host: 'https://cdn.npmmirror.com/binaries/gl/v{version}' },
+    sqlite3: {
+      host: 'https://cdn.npmmirror.com/binaries/sqlite3',
+      remote_path: 'v{version}'
+    },
+    '@journeyapps/sqlcipher': { host: 'https://cdn.npmmirror.com/binaries' },
+    grpc: {
+      host: 'https://cdn.npmmirror.com/binaries',
+      remote_path: '{name}/v{version}'
+    },
+    'grpc-tools': { host: 'https://cdn.npmmirror.com/binaries' },
+    wrtc: {
+      host: 'https://cdn.npmmirror.com/binaries',
+      remote_path: '{name}/v{version}'
+    },
+    fsevents: { host: 'https://cdn.npmmirror.com/binaries/fsevents' },
+    nodejieba: { host: 'https://cdn.npmmirror.com/binaries/nodejieba' },
+    canvas: { host: 'https://cdn.npmmirror.com/binaries/canvas' },
+    'skia-canvas': { host: 'https://cdn.npmmirror.com/binaries/skia-canvas' },
+    'flow-bin': {
+      replaceHost: 'https://github.com/facebook/flow/releases/download/v',
+      host: 'https://cdn.npmmirror.com/binaries/flow/v'
+    },
+    'jpegtran-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/jpegtran-bin'
+    },
+    'cwebp-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/cwebp-bin'
+    },
+    'zopflipng-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/zopflipng-bin'
+    },
+    'optipng-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/optipng-bin'
+    },
+    mozjpeg: {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/mozjpeg-bin'
+    },
+    gifsicle: {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/gifsicle-bin'
+    },
+    'pngquant-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/pngquant-bin',
+      replaceHostMap: [Object]
+    },
+    'pngcrush-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/pngcrush-bin'
+    },
+    'jpeg-recompress-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/jpeg-recompress-bin'
+    },
+    'advpng-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/advpng-bin'
+    },
+    'pngout-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/pngout-bin'
+    },
+    'jpegoptim-bin': {
+      replaceHost: [Array],
+      host: 'https://cdn.npmmirror.com/binaries/jpegoptim-bin'
+    },
+    argon2: { host: 'https://cdn.npmmirror.com/binaries/argon2' },
+    'ali-zeromq': { host: 'https://cdn.npmmirror.com/binaries/ali-zeromq' },
+    'ali-usb_ctl': { host: 'https://cdn.npmmirror.com/binaries/ali-usb_ctl' },
+    'gdal-async': { host: 'https://cdn.npmmirror.com/binaries/node-gdal-async' }
+  },
+  forbiddenLicenses: null,
+  flatten: false,
+  proxy: undefined,
+  prune: false,
+  disableFallbackStore: false,
+  workspacesMap: Map(0) {},
+  enableWorkspace: false,
+  workspaceRoot: 'E:\\vue-workspace\\intelligentwarning',
+  isWorkspaceRoot: true,
+  isWorkspacePackage: false,
+  offline: false,
+  strictSSL: true,
+  ignoreScripts: false,
+  foregroundScripts: false,
+  ignoreOptionalDependencies: false,
+  detail: false,
+  forceLinkLatest: false,
+  trace: false,
+  engineStrict: false,
+  registryOnly: false,
+  client: false,
+  autoFixVersion: [Function: autoFixVersion]
+}

File diff suppressed because it is too large
+ 5990 - 0
package-lock.json


+ 70 - 0
package.json

@@ -0,0 +1,70 @@
+{
+  "name": "intelligent-early-warning",
+  "author": "GYEE_ZM",
+  "version": "1.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "serve": "vite preview"
+  },
+  "dependencies": {
+    "@stomp/stompjs": "^6.1.2",
+    "@vue/shared": "^3.2.36",
+    "animate.css": "^4.1.1",
+    "axios": "^0.21.1",
+    "dayjs": "^1.10.7",
+    "default-passive-events": "^2.0.0",
+    "echarts": "^5.2.2",
+    "element-plus": "^1.0.2-beta.71",
+    "file-saver": "^2.0.5",
+    "html2canvas": "^1.0.0-rc.7",
+    "jsencrypt": "^3.2.1",
+    "json-bigint": "^1.0.0",
+    "jspdf": "^2.3.1",
+    "moment": "^2.29.1",
+    "q": "^1.5.1",
+    "screenfull": "^6.0.0",
+    "stompjs": "^2.3.3",
+    "vue": "^3.0.5",
+    "vue-cropperjs": "^5.0.0",
+    "vue-loader-v16": "^16.0.0-beta.5.4",
+    "vue-router": "^4.0.8",
+    "vue3-simple-html2pdf": "^0.0.6",
+    "vuex": "~4.0.0",
+    "xlsx": "^0.17.4"
+  },
+  "devDependencies": {
+    "@typescript-eslint/eslint-plugin": "^5.20.0",
+    "@typescript-eslint/parser": "^5.20.0",
+    "@vitejs/plugin-vue": "^1.2.2",
+    "@vue/compiler-sfc": "^3.0.5",
+    "sass": "^1.34.1",
+    "sass-loader": "^12.0.0",
+    "script-loader": "^0.7.2",
+    "typescript": "^4.6.3",
+    "vite": "^2.3.4",
+    "vite-plugin-style-import": "^0.10.1",
+    "vue-tsc": "^0.2.2",
+    "wait-on": "^6.0.0"
+  },
+  "build": {
+    "appId": "com.my-website.my-app",
+    "productName": "intelligent-early-warning",
+    "copyright": "Copyright © 2021 ${author}",
+    "mac": {
+      "category": "public.app-category.utilities"
+    },
+    "nsis": {
+      "oneClick": false,
+      "allowToChangeInstallationDirectory": true
+    },
+    "files": [
+      "dist/**/*",
+      "electron/**/*"
+    ],
+    "directories": {
+      "buildResources": "assets",
+      "output": "dist_electron"
+    }
+  }
+}

BIN
public/favicon.ico


BIN
public/static/sound/lv4.mp3


BIN
public/static/sound/lv5.mp3


+ 13 - 0
src/App.vue

@@ -0,0 +1,13 @@
+<template>
+  <router-view />
+</template>
+
+<script>
+export default {};
+</script>
+
+<style>
+@import "./assets/css/main.css";
+@import "./assets/css/custom.scss";
+@import "./assets/css/color-dark.css";
+</style>

+ 0 - 0
src/api/alarm-new.js


+ 809 - 0
src/api/api.js

@@ -0,0 +1,809 @@
+import request from "./axios.js";
+// export const baseURL = "http://10.81.3.154:6015/";
+export const baseURL = "http://192.168.1.109:6015/";
+import JSONBIG from "json-bigint";
+
+// 获取场站数据
+export function fetchStationListAll() {
+    return request({
+        baseURL: baseURL,
+        url: `alertrule/querywpllist`,
+        method: "get",
+    });
+}
+
+//获取部件及预警类型列表
+export function fetchRelatePartAndAlarmType() {
+    return request({
+        baseURL: baseURL,
+        url: "alertrule/queryalarmtypellist",
+        method: "get",
+    });
+}
+//获取型号列表
+export const fetchModel = () => {
+    return request({
+        baseURL: baseURL,
+        url: "alertrule/querywpbyymodellist",
+        method: "get",
+    });
+};
+//获取升压站列表
+export const fetchBooststation = () => {
+    return request({
+        baseURL: baseURL,
+        url: "alertrule/querysubllist",
+        method: "get",
+    });
+};
+//查询自定义预警列表
+export function custombj_fetchTableData(params) {
+    return request({
+        baseURL: baseURL,
+        url: "alertrule/page",
+        method: "get",
+        params,
+    });
+}
+
+//根据场站编号,风机型号获取AI/DI测点
+export const fetchPointList = (wpId = "", modelId = "") => {
+    return request({
+        baseURL: baseURL,
+        url: `alertrule/querywtpointlist?wpId=${wpId}&modelId=${modelId}`,
+        method: "get",
+    });
+};
+//根据升压站编号获取AI/DI测点
+export const fetchPointListByBootst = (subId = "") => {
+    return request({
+        baseURL: baseURL,
+        url: `alertrule/querysubpointllist?subId=${subId}`,
+        method: "get",
+    });
+};
+//新增/修改自定义预警
+export const custombj_postSave = (form) => {
+    return request({
+        baseURL: baseURL,
+        url: "alertrule/save",
+        method: "post",
+        data: form,
+    });
+};
+//自定义预警模板下载
+export const custombj_importTemplate = () => {
+    return request({
+        baseURL: baseURL,
+        url: "alertrule/get-import-template",
+        method: "get",
+        responseType: "blob",
+    });
+};
+
+//根据风场id获取所有风机
+export const fetchWindturbineList = (wpId = "") => {
+    return request({
+        baseURL: baseURL,
+        url: `alertrule/querywtlist?wpId=${wpId}`,
+        method: "get",
+    });
+};
+
+//根据structcode获取AI.DI测点
+export const fetchAIDIPointList = (
+    deviceId,
+    structcode,
+    stationId,
+    windturbineId,
+    pageNum,
+    pageSize
+) => {
+    return request({
+        baseURL: baseURL,
+        url: "info/point",
+        method: "get",
+        params: {
+            deviceId,
+            structcode,
+            stationId,
+            windturbineId,
+            pageNum,
+            pageSize,
+        },
+    });
+};
+
+//字典维护列表
+export const get_datadictionary = (pageNum, pageSize, name, category) => {
+    return request({
+        baseURL: baseURL,
+        method: "get",
+        url: `datadictionary/page?pageNum=${pageNum}&pageSize=${pageSize}&name=${name}&category=${category}`,
+    });
+};
+//字典维护 新增 修改
+export const post_saveorupdate = (data) => {
+    return request({
+        baseURL: baseURL,
+        method: "post",
+        url: `datadictionary/save`,
+        data,
+    });
+};
+
+//实时报警列表
+export const alarm_snap_top = (params) => {
+    return request({
+        baseURL: baseURL,
+        method: "get",
+        url: `alarm/history/findAlarmByTypelist`,
+        params: params,
+    });
+};
+//数据查询实时数据
+export const getAdapterLatest = (stationId, AIlist) => {
+    return request({
+        url: `ts/latest?uniformCodes=${AIlist}&thingId=${stationId}&thingType=windturbine`,
+        baseURL: "http://10.81.3.152:8098/",
+        showLoading: {
+            statu: false,
+        },
+        timeout: 1000,
+        transformResponse: [
+            function(data) {
+                const json = JSONBIG({
+                    storeAsString: true,
+                });
+                const res = json.parse(data);
+                return res;
+            },
+        ],
+    });
+};
+//数据查询 历史数据
+export const getAdapterHistory = (
+    stationId,
+    AIpoint,
+    startTs,
+    endTs,
+    baseUrl
+) => {
+    return request({
+        url: `ts/history/raw?uniformCode=${AIpoint}&thingId=${stationId}&thingType=windturbine&startTs=${startTs}&endTs=${endTs}`,
+        baseURL: baseUrl,
+    });
+};
+//数据查询 数据快照
+export const getAdapterHistorysnap = (
+    stationId,
+    AIpoint,
+    startTs,
+    endTs,
+    interval,
+    baseUrl
+) => {
+    return request({
+        url: `ts/history/snap?uniformCode=${AIpoint}&thingId=${stationId}&thingType=windturbine&startTs=${startTs}&endTs=${endTs}&interval=${interval}`,
+        baseURL: baseUrl,
+    });
+};
+
+//查询历史报警记录
+export const alarm_history = (params) => {
+    return request({
+        url: `alarm/history/findAlarmlist`,
+        params: params,
+        baseURL,
+    });
+};
+
+// 风机原始数据统计
+// 列表接口
+export const getAlarmCountList = (params) => {
+    return request({
+        url: `alarm/history/findWtFeatureStat?timeType=${params.timeType}&begin=${params.begin}&end=${params.end}&stationid=${params.stationid}&components=${params.components}&modelId=${params.modelId}&alarmIds=${params.alarmIds}`,
+        baseURL,
+        method: "get",
+    });
+};
+// 获取报警描述列表
+export const GetAlarmId = (params) => {
+    return request({
+        url: `alarmconfiguration/querywtalarmdesclist?components=${params.components}&modelId=${params.modelId}&wpId=${params.wpId}`,
+        baseURL,
+        method: "get",
+    });
+};
+//报警修改日志
+export const fetchruleventLogs = (pagenum, pagesize, ruleName, ruleType) => {
+    return request({
+        url: "ruleupdate/page",
+        method: "get",
+        baseURL,
+        params: {
+            pagenum,
+            pagesize,
+            ruleName,
+            ruleType,
+        },
+    });
+};
+//风机报警列表
+export const windturbinebj_fetchTableData = (query) => {
+    return request({
+        url: "alarmconfiguration/page",
+        method: "get",
+        baseURL,
+        params: query,
+    });
+};
+//获取服务端公钥
+export const getPublickey = (query) => {
+    return request({
+        url: "info/publickey",
+    });
+};
+// login
+export const loginRequest = (params) => {
+    return request({
+        url: "/admin-api/system/auth/login",
+        baseURL: "http://10.81.3.127:48080",
+        data: params,
+        method: "post",
+    });
+};
+export const Login = (params) => {
+    return request({
+        url: "auth/login",
+        baseURL,
+        data: params,
+        method: "post",
+    });
+};
+export function getUserinfo() {
+    return request({
+        baseURL: "http://10.81.3.127:48080",
+        url: "/admin-api/system/auth/get-permission-info",
+        method: "get",
+        headers: {
+            isUser: true,
+        },
+    });
+}
+export function logout(data) {
+    return request({
+        baseURL,
+        url: `auth/logout`,
+        method: "post",
+        data,
+    });
+}
+//register
+export const registerRequest = (params) => {
+    return request.post("user/registers", params);
+};
+// 报警配置缓存清理
+export const getRestart = () => {
+    return request({
+        url: `custom/restart`,
+        baseURL: "/custom/",
+    });
+};
+export const getRestart2 = () => {
+    return request({
+        url: `windturbine/restart`,
+        baseURL: "/windturbine/",
+    });
+};
+export const getRestart3 = () => {
+    return request({
+        url: `scadas/restart`,
+        baseURL: "/scadas/",
+    });
+};
+
+//----------------------------------------homePAge------------------------------------------------
+//根据风场id获取所有风机
+export const getintellList = (query) => {
+    return request({
+        url: "home/early/latest",
+        method: "get",
+        params: {
+            stationId: query,
+        },
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+export const getReportList = (params) => {
+    return request({
+        url: "earlyreport/list",
+        method: "get",
+        params: params,
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+export const getreportReviewList = (query) => {
+    return request({
+        url: "inspectorder/port/list",
+        method: "get",
+        params: {
+            stationId: query,
+        },
+    });
+};
+export const getHomeOrdercount = (query) => {
+    return request({
+        url: "home/ordercount",
+        method: "get",
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+//首页获取机型接口
+export const getHomeAlertrule = (params) => {
+    return request({
+        url: "home/alertrule",
+        method: "get",
+        params: params,
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+export const getHomeDangerHidden = (params) => {
+    return request({
+        url: "home/hidden/danger",
+        method: "get",
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+export const getHomeDurationTotal = (params) => {
+    return request({
+        url: "home/total/duration",
+        method: "get",
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+// 巡检计划
+export const getCheckList = (params) => {
+    return request({
+        url: "inspectorder/order/page",
+        method: "get",
+        params: params,
+    });
+};
+
+// 弹窗数据接口
+export const getDialogData = (params) => {
+    return request.get(
+        `windturbineAlarmCount/query/detail?stationid=${params.stationid}&starttime=${params.starttime}&endtime=${params.endtime}&windturbineid=${params.windturbineid}&parts=${params.parts}`
+    );
+};
+
+// 设备隐患评审报告
+export const postSubmitReport = (params) => {
+    return request.post("inspectorder/update", params);
+};
+
+// ----------------------------------------user------------------------------------------------
+export const getUserList = () => {
+    return request.get(`user/getall`);
+};
+export const editRequest = (params) => {
+    return request.post("user/edituser", params);
+};
+export const deleteUser = (data) => {
+    return request.delete(`user/${data}`);
+};
+// ----------------------------------------user------------------------------------------------
+// ----------------------------------------system------------------------------------------------
+
+// 获取故障弹窗
+export const getjumpwindow = () => {
+    return request.get(`userset/jumpwindow`);
+};
+//  获取语音播报
+export const getfaultread = () => {
+    return request.get(`userset/faultread`);
+};
+// 保存是否故障弹窗
+export const postjumpwindow = (params) => {
+    return request.post(`userset/jumpwindow?isjump=${params}`);
+};
+// 保存是否语音播报
+export const postfaultread = (params) => {
+    return request.post(`userset/faultread?isread=${params}`);
+};
+// ----------------------------------------system------------------------------------------------
+
+//获取所有风场
+export const fetchStationList = () => {
+    return request({
+        url: "info/station",
+        method: "get",
+    });
+};
+// //获取所有风场光伏
+// export const fetchStationListAll = () => {
+//   return request({
+//     url: "info/station/all",
+//     method: "get",
+//   });
+// };
+
+// ----------------------------------------安全生产- 报警记录------------------------------------------------
+
+//导出历史报警记录
+export const new_alarm_history = (params) => {
+    // return request.get(`alarm/history/export?stationid=${params.stationid}&starttime=${params.starttime}&endtime=${params.endtime}&windturbineid=&category1=&category2=&rank=&modelid=&snapid=&messagetype=&keyword=`)
+
+    return request({
+        method: "get",
+        url: `alarm/history/export?stationid=${params.stationid}&starttime=${params.starttime}&endtime=${params.endtime}&windturbineid=&category1=&category2=&rank=&modelid=&snapid=&messagetype=&keyword=`,
+        headers: {
+            "Content-Type": "application/json",
+        },
+        responseType: "blob",
+        showLoading: {
+            text: "拼命下载中...",
+        },
+        timeout: 99999999,
+    });
+};
+
+//查报警
+export const alarm_snap = (params) => {
+    return request({
+        url: `alarm/snap`,
+        params: params,
+    });
+};
+// ----------------------------------------安全生产- 实时报警------------------------------------------------
+
+//实时故障 最高级
+export const alarm_fault_recent = (params) => {
+    return request({
+        url: `fault/recent`,
+        params: params,
+        showLoading: {
+            statu: false,
+        },
+        transformResponse: [
+            function(data) {
+                const json = JSONBIG({
+                    storeAsString: true,
+                });
+                const res = json.parse(data);
+                return res;
+            },
+        ],
+    });
+};
+//实时故障 第一次请求
+export const alarm_fault_top = (params) => {
+    return request({
+        url: `fault/snap/top`,
+        params: params,
+    });
+};
+
+//报警确认
+export const get_fault_confirm = (params) => {
+    return request({
+        url: `fault/confirm`,
+        params: params,
+    });
+};
+
+// ----------------------------------------安全生产- 实时数据查询------------------------------------------------
+
+// get 实时data
+
+// 批量导出
+export const AdapterHistoryExport = (activeAI, activeWT, startTs, endTs) => {
+    return request({
+        url: `ts/history/raw?uniformCode=${activeAI}&thingId=${activeWT}&thingType=windturbine&startTs=${startTs}&endTs=${endTs}`,
+        baseURL: "/adapter/",
+    });
+};
+// ----------------------------------------基础数据- 设备管理------------------------------------------------
+//查询全部设备型号接口
+export const device_list = () => {
+    return request.get(`device/model/list`);
+};
+//获取结构tree
+export function tree(params) {
+    return request({
+        url: "device/structure/tree/windturbine",
+        method: "get",
+        transformResponse: [
+            function(data) {
+                const json = JSONBIG({
+                    storeAsString: true,
+                });
+                const res = json.parse(data);
+                return res;
+            },
+        ],
+    });
+}
+//根据结构获取测点
+export function point(
+    code,
+    pagenum,
+    pagesize,
+    categorydata,
+    keyword,
+    deviceId,
+    model
+) {
+    return request.get(
+        `device/metrics/page?pagenum=${pagenum}&pagesize=${pagesize}&deviceId=${deviceId}&categorydata=${categorydata}&keyword=${keyword}&model=${model}&structureCode=${code}`
+    );
+}
+//测点新增
+export function metrics(params) {
+    return request.post(`device/metrics/single`, params);
+}
+//测点删除
+export function delmetrics(params) {
+    return request.delete(`device/metrics/single/delete/${params}`);
+}
+//测点导入
+
+//tree新增 添加一个设备结构节点
+export function treeAdd(params) {
+    return request.post(`device/structure`, params);
+}
+//tree删除 tree删除一个设备结构节点
+export function treeDel(params) {
+    return request.delete(`device/structure/delete/${params}`);
+}
+
+// 故障模式
+// get All
+export const faultmode_windturbine = () => {
+    return request.get(`device/faultmode/windturbine`);
+};
+// 根据 structurecode get
+export const faultmode_structurecode = (params) => {
+    return request.get(`device/faultmode/windturbine/${params}`);
+};
+// 根据 id delete
+export const faultmode_delete = (params) => {
+    return request.delete(`device/faultmode/delete/${params}`);
+};
+
+// 新增 修改
+export const faultmode_add_edit = (data) => {
+    return request.post(`device/faultmode`, data);
+};
+
+// ---------------------------------------- 报警配置- 自定义预警------------------------------------------------
+
+// 获取机型
+export function getStationinfo(params) {
+    return request({
+        url: `info/stationinfo?stationId=${params}`,
+        method: "get",
+        showLoading: {
+            statu: false,
+        },
+    });
+}
+
+// 获取规则
+export function alertrule2_list() {
+    return request.get(`alertrule2/list/`);
+}
+
+//根据场站编号,风机型号获取DI测点new
+export const getDIPointList = (minModel) => {
+    return request({
+        url: "testingpointDI/point",
+        method: "get",
+        params: {
+            minModel,
+        },
+    });
+};
+//根据场站编号,获取电气DI测点
+export const fetchElectricDIPointList = (stationId) => {
+    return request({
+        url: "info/electrical_point_di",
+        method: "get",
+        params: {
+            stationId: stationId,
+        },
+    });
+};
+//根据电气测点,获取测点信息
+export const fetchElectricDIPointByPointId = (pointId) => {
+    return request({
+        url: "info/electrical_point_di/byId",
+        method: "get",
+        params: {
+            pointId: pointId,
+        },
+    });
+};
+
+//根据风场编号获取电气AI测点
+export const fetch_electrical_point_ai = (stationId) => {
+    return request({
+        url: "info/electrical_point_ai",
+        method: "get",
+        params: {
+            stationId: stationId,
+        },
+    });
+};
+//根据风场编号获取电气DI测点
+export const fetch_electrical_point_di = (stationId) => {
+    return request({
+        url: "info/electrical_point_di",
+        method: "get",
+        params: {
+            stationId: stationId,
+        },
+    });
+};
+//获取报警类型
+export const fetchWarningType = () => {
+    return request({
+        url: "info/warning_type",
+        method: "get",
+    });
+};
+
+//获取报警种类
+export const fetchWarningClassify = () => {
+    return request({
+        url: "info/warning_classify",
+        method: "get",
+    });
+};
+export const custombj_batchImport = (list) => {
+    return request.post("alertrule2/save-batch", list);
+};
+
+//******************************end****************************************//
+
+//******************************升压站报警API************************************//
+
+export const scadabj_fetchTableData = (query) => {
+    return request({
+        url: "scadabj/page",
+        method: "get",
+        params: query,
+        timeout: 20000,
+        transformResponse: [
+            // 处理17位数字精度问题
+            function(data) {
+                const json = JSONBIG({
+                    storeAsString: true,
+                });
+                const res = json.parse(data);
+                return res;
+            },
+        ],
+    });
+};
+export const scadabj_postSave = (objData) => {
+    return request.post("scadabj/save", objData);
+};
+export const scadabj_batchImport = (list) => {
+    return request.post("scadabj/save-batch", list);
+};
+//********************************************end********************************//\
+
+//********************************************报警记录日志********************************//\
+
+//********************************************end********************************//\
+
+//******************************风机报警API************************************//
+
+export const windturbinebj_postSave = (form) => {
+    return request.post("warning2/save", form);
+};
+export const windturbinebj_batchImport = (list) => {
+    return request.post("warning2/save-batch", list);
+};
+
+export const fetchLeaf = () => {
+    return request({
+        url: "warning2/page",
+        method: "get",
+        params: {
+            pagenum: 1,
+            pagesize: 10000,
+            isLeaf: 0,
+        },
+    });
+};
+
+//根据风机型号获取DI测点统一编码相关数据 testingpointdi2
+export const getDIPointByModelId = (query) => {
+    return request({
+        url: "info/testing_point_di",
+        method: "get",
+        params: query,
+    });
+};
+
+//******************************end*******************************************//
+//******************************预警统计*******************************************//
+export const statistics_querymap = (params) => {
+    return request.get(`alarm/count/querymap2`, { params: params });
+};
+export const statistics_querybyname = (params) => {
+    return request.get("alarm/count/querybyname2", { params: params });
+};
+export const statistics_lineandproject = (params) => {
+    return request.get("alarm/count/lineandproject2", { params: params });
+};
+//******************************end*******************************************//
+
+//******************************预警分析*******************************************//
+export const warning_query_new = (params) => {
+    return request.get(`alarm/count/query/new2`, { params: params });
+};
+export const warning_detail = (params) => {
+    return request({
+        url: `analysis/detail`,
+        params: params,
+        timeout: 60000,
+    });
+};
+// export const statistics_lineandproject = params => {
+//     return request.get('alarm/count/lineandproject', { params: params })
+// };
+
+//******************************end*******************************************//
+
+//*****************************旧诊断报告*******************************************//
+export const earlywarnscore_list = (params) => {
+    return request.get(`earlywarnscore/list`, { params: params });
+};
+export const warning_query_new3 = (params) => {
+    return request.get(`alarm/count/query/new3`, { params: params });
+};
+//******************************end*******************************************//
+
+//*****************************新诊断报告*******************************************//
+export const getList = (params) => {
+    return request.get(
+        `earlyreport/list?pagenum=${params.pagenum}&pagesize=${params.pagesize}&stationId=${params.stationId}&keyword=&starttime=&endtime=`
+    );
+};
+
+export const getReportDetail = (id) => {
+    return request({
+        url: `earlyreport/info?id=${id}`,
+        method: "get",
+        showLoading: {
+            statu: false,
+        },
+    });
+};
+
+//******************************end*******************************************//
+
+
+
+export const getAlarmConfig = () => {
+    return request.get(`/alertrule/queryalarmtypellist`);
+};
+export const confirmAlart = (params) => {
+    return request.post("/alarm/history/updateAlarms", params)
+};

+ 96 - 0
src/api/axios.js

@@ -0,0 +1,96 @@
+import axios from "axios";
+import route from "@/router";
+import store from "@/store";
+import { ElMessage, ElMessageBox, ElLoading } from "element-plus";
+import { nextTick } from "vue";
+import config from "./config";
+import JSONBIG from "json-bigint";
+import { getCookie } from "@/utils/auth"; // getToken from cookie
+// 配置新建一个 axios 实例
+var loading = null;
+const service = axios.create({
+    baseURL: "/sharding/",
+    timeout: 20000,
+    headers: { "Content-Type": "application/json" },
+    showLoading: {
+        statu: true,
+        text: "加载中...",
+    },
+});
+
+// 添加请求拦截器
+service.interceptors.request.use(
+    (config) => {
+        if (config.showLoading.statu) {
+            loading = ElLoading.service({
+                lock: true,
+                text: config.showLoading.text,
+                spinner: "el-icon-loading",
+                background: "rgba(0, 0, 0, 0.3)",
+            });
+        }
+        const u = (config.headers || {}).isUser;
+        // 在发送请求之前做些什么 token
+        let token = getCookie("accessToken");
+        let userId = getCookie("userId");
+        if (token) {
+            if (u) {
+                config.headers["Authorization"] = "Bearer " + token;
+            } else {
+                config.headers.common["token"] = token;
+                config.headers.common["userId"] = userId;
+            }
+        }
+        return config;
+    },
+    (error) => {
+        // 对请求错误做些什么
+        return Promise.reject(error);
+    }
+);
+
+// 添加响应拦截器
+service.interceptors.response.use(
+    (response) => {
+        // 对响应数据做点什么
+        nextTick(async() => {
+            loading && (await loading.close());
+        });
+        const res = response.data;
+        if (res.code === 401) {
+            ElMessageBox.alert(
+                "登录状态已过期,您可以继续留在该页面,或者重新登录",
+                "系统提示", {
+                    confirmButtonText: "重新登录",
+                    cancelButtonText: "取消",
+                    type: "warning",
+                    callback: () => {
+                        store.dispatch("user/LogOut").then(() => {
+                            route.push("/");
+                        });
+                    },
+                }
+            );
+        } else {
+            return response.data;
+        }
+    },
+    (error) => {
+        nextTick(async() => {
+            loading && (await loading.close());
+        });
+        // 对响应错误做点什么
+        if (error.message.indexOf("timeout") != -1) {
+            // ElMessage.error('网络超时');
+        } else if (error.message == "Network Error") {
+            // ElMessage.error('网络连接错误');
+        } else {
+            // if (error.response.data) ElMessage.error(error.response.statusText);
+            // else ElMessage.error('接口路径找不到');
+        }
+        return Promise.reject(error);
+    }
+);
+
+// 导出 axios 实例
+export default service;

+ 11 - 0
src/api/config.js

@@ -0,0 +1,11 @@
+const config = {
+    // baseURL: 'http://192.168.2.12:8075', // 本地后端服务实际地址
+    // baseURL: 'http://123.60.219.66:8075', // 宁夏华为云后端服务实际地址
+    // baseURL: 'http://192.168.1.18:8075', // 宁夏ylt后端服务实际地址
+    // baseURL: 'http://192.168.2.205:8075', // 河北后端服务实际地址
+    // baseURL: 'http://10.0.118.75:8075', // 和风后端服务实际地址
+    // baseURL: 'http://15.147.0.228:8075', // 巴音后端服务实际地址
+    baseURL: 'http://10.81.3.154:8075', // 
+}
+
+export default config;

+ 28 - 0
src/assets/css/color-dark.css

@@ -0,0 +1,28 @@
+.header {
+	background-color: #242f42;
+}
+.login-wrap {
+	background: #324157;
+}
+.plugins-tips {
+	background: #eef1f6;
+}
+.plugins-tips a {
+	color: #20a0ff;
+}
+.el-upload--text em {
+	color: #20a0ff;
+}
+.pure-button {
+	background: #20a0ff;
+}
+.tags-li.active {
+	border: 1px solid #409eff;
+	background-color: #409eff;
+}
+.message-title {
+	color: #20a0ff;
+}
+.collapse-btn:hover {
+	background: rgb(40, 52, 70);
+}

+ 23 - 0
src/assets/css/custom.scss

@@ -0,0 +1,23 @@
+.search-input {
+  display: flex;
+  align-items: center;
+  font-size: 14px;
+  color: #000;
+  min-width: 192px;
+  white-space: nowrap;
+  .label {
+    margin-right: 5px;
+  }
+  .el-input {
+    width: 150px;
+    .el-input__inner {
+      width: 150px;
+    }
+  }
+  .el-date-editor.el-input {
+    width: 192px;
+    .el-input__inner {
+      width: 192px;
+    }
+  }
+}

+ 4 - 0
src/assets/css/icon.css

@@ -0,0 +1,4 @@
+[class*=' el-icon-lx'],
+[class^='el-icon-lx'] {
+	font-family: lx-iconfont !important;
+}

+ 165 - 0
src/assets/css/main.css

@@ -0,0 +1,165 @@
+* {
+  margin: 0;
+  padding: 0;
+}
+
+html,
+body,
+#app,
+.wrapper {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+
+body {
+  font-family: "PingFang SC", "Helvetica Neue", Helvetica, "microsoft yahei",
+    arial, STHeiTi, sans-serif;
+}
+
+a {
+  text-decoration: none;
+}
+
+.content-box {
+  position: absolute;
+  left: 250px;
+  right: 0;
+  top: 70px;
+  bottom: 0;
+  /* padding-bottom: 8px; */
+  -webkit-transition: left 0.3s ease-in-out;
+  transition: left 0.3s ease-in-out;
+  background: #f0f0f0;
+}
+
+.content {
+  width: auto;
+  height: calc(100% - 40px);
+  padding: 5px;
+  overflow-y: auto;
+  overflow-x: hidden;
+  box-sizing: border-box;
+}
+
+.content-collapse {
+  left: 65px;
+}
+
+.container {
+  padding: 30px;
+  background: #fff;
+  border: 1px solid #ddd;
+  border-radius: 5px;
+}
+
+.crumbs {
+  margin: 10px 0;
+}
+
+.el-table th {
+  background-color: #f5f7fa !important;
+}
+
+.pagination {
+  margin: 20px 0;
+  text-align: right;
+}
+
+.plugins-tips {
+  padding: 20px 10px;
+  margin-bottom: 20px;
+}
+
+.el-button + .el-tooltip {
+  margin-left: 10px;
+}
+
+.el-table tr:hover {
+  background: #f6faff;
+}
+
+.mgb20 {
+  margin-bottom: 20px;
+}
+
+.move-enter-active,
+.move-leave-active {
+  transition: opacity 0.1s ease;
+}
+
+.move-enter-from,
+.move-leave-to {
+  opacity: 0;
+}
+
+/*BaseForm*/
+
+.form-box {
+  width: 600px;
+}
+
+.form-box .line {
+  text-align: center;
+}
+
+.el-time-panel__content::after,
+.el-time-panel__content::before {
+  margin-top: -7px;
+}
+
+.el-time-spinner__wrapper
+  .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
+  padding-bottom: 0;
+}
+
+/*Upload*/
+
+.pure-button {
+  width: 150px;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+  color: #fff;
+  border-radius: 3px;
+}
+
+.g-core-image-corp-container .info-aside {
+  height: 45px;
+}
+
+/*VueEditor*/
+
+.ql-container {
+  min-height: 400px;
+}
+
+.ql-snow .ql-tooltip {
+  transform: translateX(117.5px) translateY(10px) !important;
+}
+
+.editor-btn {
+  margin-top: 20px;
+}
+
+/*markdown*/
+
+.v-note-wrapper .v-note-panel {
+  min-height: 500px;
+}
+
+/* //滚动条的宽度 */
+
+::-webkit-scrollbar {
+  width: 6px;
+  height: 8px;
+  background-color: #e4e4e4;
+  border-radius: 6px;
+}
+
+/* //滚动条的滑块 */
+
+::-webkit-scrollbar-thumb {
+  background-color: #a9aaad;
+  border-radius: 6px;
+}

+ 179 - 0
src/assets/excel/Blob.js

@@ -0,0 +1,179 @@
+/* eslint-disable */
+/* Blob.js
+ * A Blob implementation.
+ * 2014-05-27
+ *
+ * By Eli Grey, http://eligrey.com
+ * By Devin Samarin, https://github.com/eboyjr
+ * License: X11/MIT
+ *   See LICENSE.md
+ */
+
+/*global self, unescape */
+/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
+ plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
+
+(function (view) {
+    "use strict";
+
+    view.URL = view.URL || view.webkitURL;
+
+    if (view.Blob && view.URL) {
+        try {
+            new Blob;
+            return;
+        } catch (e) {}
+    }
+
+    // Internally we use a BlobBuilder implementation to base Blob off of
+    // in order to support older browsers that only have BlobBuilder
+    var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
+            var
+                get_class = function(object) {
+                    return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
+                }
+                , FakeBlobBuilder = function BlobBuilder() {
+                    this.data = [];
+                }
+                , FakeBlob = function Blob(data, type, encoding) {
+                    this.data = data;
+                    this.size = data.length;
+                    this.type = type;
+                    this.encoding = encoding;
+                }
+                , FBB_proto = FakeBlobBuilder.prototype
+                , FB_proto = FakeBlob.prototype
+                , FileReaderSync = view.FileReaderSync
+                , FileException = function(type) {
+                    this.code = this[this.name = type];
+                }
+                , file_ex_codes = (
+                    "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+                    + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
+                ).split(" ")
+                , file_ex_code = file_ex_codes.length
+                , real_URL = view.URL || view.webkitURL || view
+                , real_create_object_URL = real_URL.createObjectURL
+                , real_revoke_object_URL = real_URL.revokeObjectURL
+                , URL = real_URL
+                , btoa = view.btoa
+                , atob = view.atob
+
+                , ArrayBuffer = view.ArrayBuffer
+                , Uint8Array = view.Uint8Array
+                ;
+            FakeBlob.fake = FB_proto.fake = true;
+            while (file_ex_code--) {
+                FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
+            }
+            if (!real_URL.createObjectURL) {
+                URL = view.URL = {};
+            }
+            URL.createObjectURL = function(blob) {
+                var
+                    type = blob.type
+                    , data_URI_header
+                    ;
+                if (type === null) {
+                    type = "application/octet-stream";
+                }
+                if (blob instanceof FakeBlob) {
+                    data_URI_header = "data:" + type;
+                    if (blob.encoding === "base64") {
+                        return data_URI_header + ";base64," + blob.data;
+                    } else if (blob.encoding === "URI") {
+                        return data_URI_header + "," + decodeURIComponent(blob.data);
+                    } if (btoa) {
+                        return data_URI_header + ";base64," + btoa(blob.data);
+                    } else {
+                        return data_URI_header + "," + encodeURIComponent(blob.data);
+                    }
+                } else if (real_create_object_URL) {
+                    return real_create_object_URL.call(real_URL, blob);
+                }
+            };
+            URL.revokeObjectURL = function(object_URL) {
+                if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
+                    real_revoke_object_URL.call(real_URL, object_URL);
+                }
+            };
+            FBB_proto.append = function(data/*, endings*/) {
+                var bb = this.data;
+                // decode data to a binary string
+                if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
+                    var
+                        str = ""
+                        , buf = new Uint8Array(data)
+                        , i = 0
+                        , buf_len = buf.length
+                        ;
+                    for (; i < buf_len; i++) {
+                        str += String.fromCharCode(buf[i]);
+                    }
+                    bb.push(str);
+                } else if (get_class(data) === "Blob" || get_class(data) === "File") {
+                    if (FileReaderSync) {
+                        var fr = new FileReaderSync;
+                        bb.push(fr.readAsBinaryString(data));
+                    } else {
+                        // async FileReader won't work as BlobBuilder is sync
+                        throw new FileException("NOT_READABLE_ERR");
+                    }
+                } else if (data instanceof FakeBlob) {
+                    if (data.encoding === "base64" && atob) {
+                        bb.push(atob(data.data));
+                    } else if (data.encoding === "URI") {
+                        bb.push(decodeURIComponent(data.data));
+                    } else if (data.encoding === "raw") {
+                        bb.push(data.data);
+                    }
+                } else {
+                    if (typeof data !== "string") {
+                        data += ""; // convert unsupported types to strings
+                    }
+                    // decode UTF-16 to binary string
+                    bb.push(unescape(encodeURIComponent(data)));
+                }
+            };
+            FBB_proto.getBlob = function(type) {
+                if (!arguments.length) {
+                    type = null;
+                }
+                return new FakeBlob(this.data.join(""), type, "raw");
+            };
+            FBB_proto.toString = function() {
+                return "[object BlobBuilder]";
+            };
+            FB_proto.slice = function(start, end, type) {
+                var args = arguments.length;
+                if (args < 3) {
+                    type = null;
+                }
+                return new FakeBlob(
+                    this.data.slice(start, args > 1 ? end : this.data.length)
+                    , type
+                    , this.encoding
+                );
+            };
+            FB_proto.toString = function() {
+                return "[object Blob]";
+            };
+            FB_proto.close = function() {
+                this.size = this.data.length = 0;
+            };
+            return FakeBlobBuilder;
+        }(view));
+
+    view.Blob = function Blob(blobParts, options) {
+        var type = options ? (options.type || "") : "";
+        var builder = new BlobBuilder();
+        if (blobParts) {
+            for (var i = 0, len = blobParts.length; i < len; i++) {
+                builder.append(blobParts[i]);
+            }
+        }
+        return builder.getBlob(type);
+    };
+}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

+ 144 - 0
src/assets/excel/Export2Excel.js

@@ -0,0 +1,144 @@
+/* eslint-disable */
+import('file-saver');
+import('./Blob.js');
+import XLSX from 'xlsx';
+
+function generateArray(table) {
+    var out = [];
+    var rows = table.querySelectorAll('tr');
+    var ranges = [];
+    for (var R = 0; R < rows.length; ++R) {
+        var outRow = [];
+        var row = rows[R];
+        var columns = row.querySelectorAll('td');
+        for (var C = 0; C < columns.length; ++C) {
+            var cell = columns[C];
+            var colspan = cell.getAttribute('colspan');
+            var rowspan = cell.getAttribute('rowspan');
+            var cellValue = cell.innerText;
+            if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
+
+            //Skip ranges
+            ranges.forEach(function (range) {
+                if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
+                    for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
+                }
+            });
+
+            //Handle Row Span
+            if (rowspan || colspan) {
+                rowspan = rowspan || 1;
+                colspan = colspan || 1;
+                ranges.push({ s: { r: R, c: outRow.length }, e: { r: R + rowspan - 1, c: outRow.length + colspan - 1 } });
+            };
+
+            //Handle Value
+            outRow.push(cellValue !== "" ? cellValue : null);
+
+            //Handle Colspan
+            if (colspan)
+                for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
+        }
+        out.push(outRow);
+    }
+    return [out, ranges];
+};
+
+function datenum(v, date1904) {
+    if (date1904) v += 1462;
+    var epoch = Date.parse(v);
+    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
+}
+
+function sheet_from_array_of_arrays(data, opts) {
+    var ws = {};
+    var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
+    for (var R = 0; R != data.length; ++R) {
+        for (var C = 0; C != data[R].length; ++C) {
+            if (range.s.r > R) range.s.r = R;
+            if (range.s.c > C) range.s.c = C;
+            if (range.e.r < R) range.e.r = R;
+            if (range.e.c < C) range.e.c = C;
+            var cell = { v: data[R][C] };
+            if (cell.v == null) continue;
+            var cell_ref = XLSX.utils.encode_cell({ c: C, r: R });
+
+            if (typeof cell.v === 'number') cell.t = 'n';
+            else if (typeof cell.v === 'boolean') cell.t = 'b';
+            else if (cell.v instanceof Date) {
+                cell.t = 'n';
+                cell.z = XLSX.SSF._table[14];
+                cell.v = datenum(cell.v);
+            } else cell.t = 's';
+
+            ws[cell_ref] = cell;
+        }
+    }
+    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
+    return ws;
+}
+
+function Workbook() {
+    if (!(this instanceof Workbook)) return new Workbook();
+    this.SheetNames = [];
+    this.Sheets = {};
+}
+
+function s2ab(s) {
+    var buf = new ArrayBuffer(s.length);
+    var view = new Uint8Array(buf);
+    for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
+    return buf;
+}
+
+export function export_table_to_excel(id) {
+    var theTable = document.getElementById(id);
+    console.log('a')
+    var oo = generateArray(theTable);
+    var ranges = oo[1];
+
+    /* original data */
+    var data = oo[0];
+    var ws_name = "SheetJS";
+    console.log(data);
+
+    var wb = new Workbook(),
+        ws = sheet_from_array_of_arrays(data);
+
+    /* add ranges to worksheet */
+    // ws['!cols'] = ['apple', 'banan'];
+    ws['!merges'] = ranges;
+
+    /* add worksheet to workbook */
+    wb.SheetNames.push(ws_name);
+    wb.Sheets[ws_name] = ws;
+
+    var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
+
+    saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), "test.xlsx")
+}
+
+function formatJson(jsonData) {
+    console.log(jsonData)
+}
+
+export function export_json_to_excel(th, jsonData, defaultTitle) {
+
+    /* original data */
+
+    var data = jsonData;
+    data.unshift(th);
+    var ws_name = "SheetJS";
+
+    var wb = new Workbook(),
+        ws = sheet_from_array_of_arrays(data);
+
+
+    /* add worksheet to workbook */
+    wb.SheetNames.push(ws_name);
+    wb.Sheets[ws_name] = ws;
+
+    var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
+    var title = defaultTitle || '列表'
+    saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), title + ".xlsx")
+}

+ 46 - 0
src/assets/font/iconfont.css

@@ -0,0 +1,46 @@
+@font-face {
+    font-family: "iconfont";
+    /* Project id 2906628 */
+    src: url('iconfont.woff2?t=1640660196056') format('woff2'), url('iconfont.woff?t=1640660196056') format('woff'), url('iconfont.ttf?t=1640660196056') format('truetype');
+}
+
+.iconfont {
+    font-family: "iconfont" !important;
+    font-size: 16px;
+    margin-left: 4px;
+    font-style: normal;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+}
+
+.iconIOTtubiao_huabanfuben:before {
+    content: "\e61a";
+}
+
+.iconzidingyi:before {
+    content: "\e61e";
+}
+
+.iconxitongcaidan:before {
+    content: "\e696";
+}
+
+.iconnibianqi:before {
+    content: "\e72c";
+}
+
+.iconbaojingpeizhi:before {
+    content: "\e67d";
+}
+
+.iconguzhangzhenduan:before {
+    content: "\e601";
+}
+
+.iconfengji:before {
+    content: "\e606";
+}
+
+.iconjichushuju:before {
+    content: "\e7d3";
+}

BIN
src/assets/font/iconfont.ttf


BIN
src/assets/font/iconfont.woff


BIN
src/assets/font/iconfont.woff2


BIN
src/assets/img/bg.png


BIN
src/assets/img/bianjiangA.png


BIN
src/assets/img/bianjiangB.png


BIN
src/assets/img/bianpinA.png


BIN
src/assets/img/bianpinB.png


BIN
src/assets/img/cefengA.png


BIN
src/assets/img/cefengB.png


BIN
src/assets/img/chilunA.png


BIN
src/assets/img/chilunB.png


BIN
src/assets/img/default.jpg


BIN
src/assets/img/fadianA.png


BIN
src/assets/img/fadianB.png


BIN
src/assets/img/icon-excel.png


BIN
src/assets/img/pianhangA.png


BIN
src/assets/img/pianhangB.png


BIN
src/assets/img/qitaA.png


BIN
src/assets/img/qitaB.png


BIN
src/assets/img/resultA.png


BIN
src/assets/img/resultB.png


BIN
src/assets/img/topIntell.png


BIN
src/assets/img/topIntell1.png


BIN
src/assets/img/topIntell2.png


BIN
src/assets/img/topIntell3.png


BIN
src/assets/img/xunjianA.png


BIN
src/assets/img/xunjianB.png


BIN
src/assets/img/yepianA.png


BIN
src/assets/img/yepianB.png


BIN
src/assets/img/yeyaA.png


BIN
src/assets/img/yeyaB.png


BIN
src/assets/img/yujinga.png


BIN
src/assets/img/yujingb.png


BIN
src/assets/img/zhuanjiaA.png


BIN
src/assets/img/zhuanjiaB.png


BIN
src/assets/img/zhuzhouA.png


BIN
src/assets/img/zhuzhouB.png


+ 67 - 0
src/assets/js/dialogDrag.js

@@ -0,0 +1,67 @@
+const dialogDrag = (app) => {
+    app.directive('dialogdrag', {
+        // 渲染完毕
+        mounted(el) {
+            // 可视窗口的宽度
+            const clientWidth = document.documentElement.clientWidth
+                // 可视窗口的高度
+            const clientHeight = document.documentElement.clientHeight
+                // 记录坐标
+            let domset = {
+                x: clientWidth / 4, // 默认width 50%
+                y: clientHeight * 15 / 100 // 根据 15vh 计算
+            }
+
+            // 弹窗的容器
+            const domDrag = el.firstElementChild.firstElementChild
+                // 重新设置上、左距离
+            domDrag.style.marginTop = domset.y + 'px'
+            domDrag.style.marginLeft = domset.x + 'px'
+
+            // 记录拖拽开始的光标坐标,0 表示没有拖拽
+            let start = { x: 0, y: 0 }
+                // 移动中记录偏移量
+            let move = { x: 0, y: 0 }
+
+            // 鼠标按下,开始拖拽
+            document.onmousedown = (e) => {
+                // 判断对话框是否重新打开
+                if (domDrag.style.marginTop === '15vh') {
+                    // 重新打开,设置 domset.y  top
+                    domset.y = clientHeight * 15 / 100
+                }
+                start.x = e.clientX
+                start.y = e.clientY
+                domDrag.style.cursor = 'move' // 改变光标形状
+            }
+
+            // 鼠标移动,实时跟踪
+            document.onmousemove = (e) => {
+                    if (start.x === 0) { // 不是拖拽状态
+                        return
+                    }
+                    move.x = e.clientX - start.x
+                    move.y = e.clientY - start.y
+
+                    // 初始位置 + 拖拽距离
+                    domDrag.style.marginLeft = (domset.x + move.x) + 'px'
+                    domDrag.style.marginTop = (domset.y + move.y) + 'px'
+                }
+                // 鼠标抬起,结束拖拽
+            document.onmouseup = (e) => {
+                move.x = e.clientX - start.x
+                move.y = e.clientY - start.y
+
+                // 记录新坐标,作为下次拖拽的初始位置
+                domset.x += move.x
+                domset.y += move.y
+                domDrag.style.cursor = '' // 恢复光标形状
+                domDrag.style.marginLeft = domset.x + 'px'
+                domDrag.style.marginTop = domset.y + 'px'
+                    // 结束拖拽
+                start.x = 0
+            }
+        }
+    })
+}
+export default dialogDrag

+ 233 - 0
src/components/Header.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="header">
+    <!-- 折叠按钮 -->
+    <div class="collapse-btn" @click="collapseChage">
+      <i v-if="!collapse" class="el-icon-s-fold"></i>
+      <i v-else class="el-icon-s-unfold"></i>
+    </div>
+    <div class="logo">智能报警及预警系统</div>
+    <div class="header-right">
+      <div class="header-user-con">
+        <el-dropdown class="user-name" trigger="click" @command="handleCommand">
+          <span class="el-dropdown-link">
+            {{ username }}
+            <i class="el-icon-caret-bottom"></i>
+          </span>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <!-- <el-dropdown-item command="editpassword"
+                >修改密码</el-dropdown-item
+              > -->
+              <el-dropdown-item command="loginout">退出登录</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+        <i
+          class="el-icon-full-screen"
+          style="margin-left: 30px"
+          @click="toggleFullScreen"
+        ></i>
+      </div>
+    </div>
+  </div>
+  <el-dialog v-model="dialogVisible" width="400px">
+    <el-form
+      :model="ruleForm"
+      :rules="rules"
+      ref="refruleForm"
+      label-width="80px"
+      class="demo-ruleForm"
+    >
+      <el-form-item label="账号" prop="userName">
+        <el-input v-model="ruleForm.userName"></el-input>
+      </el-form-item>
+      <el-form-item label="中文名称" prop="chineseName">
+        <el-input v-model="ruleForm.chineseName"></el-input>
+      </el-form-item>
+      <el-form-item label="新密码" prop="newPassWord">
+        <el-input type="password" v-model="ruleForm.newPassWord"></el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="submitForm(refruleForm)"
+          >确认</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, computed, onMounted, reactive } from "vue";
+import { useRouter } from "vue-router";
+import { editRequest, logout } from "/@/api/api.js";
+import { getCookie } from "@/utils/auth"; // getToken from cookie
+import { ElMessage } from "element-plus";
+import { useStore } from "vuex";
+import { JSEncrypt } from "jsencrypt";
+import BASE from "@tools/basicTool";
+import screenfull from "screenfull";
+const router = useRouter();
+const store = useStore();
+const dialogVisible = ref(false);
+const refruleForm = ref(null);
+const rules = reactive({
+  userName: [{ required: true, message: "请输入账号", trigger: "blur" }],
+  chineseName: [{ required: true, message: "请输入中文名称", trigger: "blur" }],
+  newPassWord: [
+    { required: true, validator: BASE.elCkeck.validatePass, trigger: "blur" },
+  ],
+});
+const ruleForm = reactive({
+  id: sessionStorage.getItem("ms_id"),
+  userName: sessionStorage.getItem("ms_username"),
+  chineseName: sessionStorage.getItem("ms_chinesename"),
+  newPassWord: "",
+});
+
+const username = computed(() => {
+  return store.getters.username;
+});
+
+const collapse = computed(() => store.state.collapse);
+
+// 用户名下拉菜单选择事件
+const handleCommand = (command) => {
+  if (command == "loginout") {
+    logout({
+      token: getCookie("authToken"),
+      userId: getCookie("userId"),
+    }).then((res) => {
+      store.commit("user/REMOVE_TOKEN");
+      router.push("/login");
+    });
+  } else if (command == "editpassword") {
+    // dialogVisible.value = true;
+  }
+};
+// 利用jsencrypt.js进行RSA加密
+const encryptionByPublickey = (formData) => {
+  let key = sessionStorage.getItem("publicKey");
+  var encrypt = new JSEncrypt();
+  encrypt.setPublicKey(key);
+  var result = encrypt.encrypt(JSON.stringify(formData));
+  return result;
+};
+// edit
+const edit = async () => {
+  let requestWrapper = {
+    publicKey: sessionStorage.getItem("publicKey"),
+    token: sessionStorage.getItem("token"),
+    data: encryptionByPublickey(ruleForm),
+  };
+  const res = await editRequest(requestWrapper);
+  dialogVisible.value = false;
+  if (res.success) {
+    ElMessage.success(res.msg);
+  } else {
+    ElMessage.error(res.msg);
+  }
+};
+//表单提交
+const submitForm = (formEl) => {
+  formEl.validate((valid) => {
+    if (valid) {
+      edit();
+    } else {
+      console.log("error submit!!");
+      return false;
+    }
+  });
+};
+// 侧边栏折叠
+const collapseChage = () => {
+  // store.commit("hadndleCollapse", true);
+  store.commit("hadndleCollapse", !collapse.value);
+};
+// 全屏
+const toggleFullScreen = () => {
+  if (screenfull.isEnabled) {
+    screenfull.toggle();
+  }
+};
+onMounted(() => {
+  if (document.body.clientWidth < 1500) {
+    store.commit("hadndleCollapse", true);
+  }
+});
+</script>
+<style scoped>
+.header {
+  position: relative;
+  box-sizing: border-box;
+  width: 100%;
+  height: 70px;
+  font-size: 22px;
+  color: #fff;
+}
+.collapse-btn {
+  float: left;
+  padding: 0 21px;
+  cursor: pointer;
+  line-height: 70px;
+}
+.header .logo {
+  float: left;
+  width: 250px;
+  line-height: 70px;
+}
+.header-right {
+  float: right;
+  padding-right: 30px;
+}
+.header-user-con {
+  display: flex;
+  height: 70px;
+  align-items: center;
+}
+.btn-fullscreen {
+  transform: rotate(45deg);
+  margin-right: 5px;
+  font-size: 24px;
+}
+.btn-bell,
+.btn-fullscreen {
+  position: relative;
+  width: 30px;
+  height: 30px;
+  text-align: center;
+  border-radius: 15px;
+  cursor: pointer;
+}
+.btn-bell-badge {
+  position: absolute;
+  right: 0;
+  top: -2px;
+  width: 8px;
+  height: 8px;
+  border-radius: 4px;
+  background: #f56c6c;
+  color: #fff;
+}
+.btn-bell .el-icon-bell {
+  color: #fff;
+}
+.user-name {
+  margin-left: 10px;
+}
+.user-avator {
+  margin-left: 20px;
+  margin-right: 10px;
+}
+.user-avator img {
+  display: block;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+}
+.el-dropdown-link {
+  color: #fff;
+  cursor: pointer;
+}
+.el-dropdown-menu__item {
+  text-align: center;
+}
+</style>

+ 73 - 0
src/components/Sidebar.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="sidebar">
+    <el-menu
+      class="sidebar-el-menu"
+      :default-active="onRoutes"
+      :collapse="collapse"
+      background-color="#324157"
+      text-color="#bfcbd9"
+      active-text-color="#20a0ff"
+      unique-opened
+      router
+    >
+    
+      <template v-for="item in router.options.routes[1].children">
+        <template v-if="item.children">
+          <el-submenu :index="item.path" :key="item.name"  v-if="item.isshow?.includes(identity) || !item.isshow">
+            <template #title>
+              <i :class="item.icon" style="margin-right:10px"></i>
+              <span>{{ item.meta.title }}</span>
+            </template>
+
+            <template v-for="subItem in item.children" :key="subItem.name">
+              <el-menu-item :index="subItem.path">
+                {{ subItem.meta.title }}
+              </el-menu-item>
+            </template>
+          </el-submenu>
+        </template>
+
+        <template v-else >
+          <el-menu-item :index="item.path" :key="item.path"  v-if="(item.isshow?.includes(identity) || !item.isshow) &&item.path!=='/check'">
+            <i :class="item.icon" style="margin-right:10px"></i>
+            <template #title>{{ item.meta.title }}</template>
+          </el-menu-item>
+        </template>
+        
+      </template>
+    </el-menu>
+  </div>
+</template>
+
+<script setup>
+import { computed, warn } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import { useStore } from "vuex";
+const identity = sessionStorage.getItem("identity")
+
+const route = useRoute();
+const store = useStore();
+const router = useRouter();
+let onRoutes = computed(() => route.path.replace("/", ""));
+let collapse = computed(() => store.state.collapse);
+</script>
+
+<style scoped>
+.sidebar {
+  display: block;
+  position: absolute;
+  left: 0;
+  top: 70px;
+  bottom: 0;
+  overflow-y: scroll;
+}
+.sidebar::-webkit-scrollbar {
+  width: 0;
+}
+.sidebar-el-menu:not(.el-menu--collapse) {
+  width: 250px;
+}
+.sidebar > ul {
+  height: 100%;
+}
+</style>

+ 173 - 0
src/components/Tags.vue

@@ -0,0 +1,173 @@
+<template>
+	<div class="tags" v-if="showTags">
+		<ul>
+			<li
+				class="tags-li"
+				v-for="(item, index) in tagsList"
+				:class="{ active: isActive(item.path) }"
+				:key="index"
+			>
+				<router-link :to="item.path" class="tags-li-title">{{
+					item.title
+				}}</router-link>
+				<span class="tags-li-icon" @click="closeTags(index)">
+					<i class="el-icon-close"></i>
+				</span>
+			</li>
+		</ul>
+		<div class="tags-close-box">
+			<el-dropdown @command="handleTags">
+				<el-button size="mini" type="primary">
+					标签操作
+					<i class="el-icon-arrow-down el-icon--right"></i>
+				</el-button>
+				<template #dropdown>
+					<el-dropdown-menu size="small">
+						<el-dropdown-item command="other">关闭其他</el-dropdown-item>
+						<el-dropdown-item command="all">关闭所有</el-dropdown-item>
+					</el-dropdown-menu>
+				</template>
+			</el-dropdown>
+		</div>
+	</div>
+</template>
+
+<script setup>
+import { computed, onMounted, watch } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useStore } from 'vuex'
+const router = useRouter()
+const route = useRoute()
+const store = useStore()
+
+let tagsList = computed(() => store.state.tagsList)
+let showTags = computed(() => tagsList.value.length > 0)
+
+const isActive = path => {
+	return path === route.fullPath
+}
+
+// 关闭单个标签
+const closeTags = index => {
+	const delItem = tagsList.value[index]
+	store.commit('delTagsItem', { index })
+	const item = tagsList.value[index] ? tagsList.value[index] : tagsList.value[index - 1]
+	if (item) {
+		delItem.path === route.fullPath && router.push(item.path)
+	} else {
+		router.push('/')
+	}
+}
+
+// 关闭全部标签
+const closeAll = () => {
+	store.commit('clearTags')
+	router.push('/')
+}
+
+// 关闭其他标签
+const closeOther = () => {
+	const curItem = tagsList.value.filter(item => item.path === route.fullPath)
+	store.commit('closeTagsOther', curItem)
+}
+
+// 设置标签
+const setTags = route => {
+	const isExist = tagsList.value.some(item => item.path === route.fullPath)
+	if (!isExist) {
+		if (tagsList.value.length >= 8) {
+			store.commit('delTagsItem', { index: 0 })
+		}
+		store.commit('setTagsItem', {
+			name: route.name,
+			title: route.meta.title,
+			path: route.fullPath,
+		})
+	}
+}
+const handleTags = command => {
+	command === 'other' ? closeOther() : closeAll()
+}
+
+onMounted(() => {
+	setTags(route)
+})
+
+watch(
+	() => route.path,
+	(newValue, oldValue) => {
+		setTags(route)
+	}
+)
+</script>
+
+<style>
+.tags {
+	position: relative;
+	height: 40px;
+	overflow: hidden;
+	background: #fff;
+	padding-right: 120px;
+	box-shadow: 0 5px 10px #ddd;
+}
+
+.tags ul {
+	box-sizing: border-box;
+	width: 100%;
+	height: 100%;
+}
+
+.tags-li {
+	float: left;
+	margin: 7px 0 0 7px;
+	border-radius: 3px;
+	font-size: 12px;
+	overflow: hidden;
+	cursor: pointer;
+	height: 23px;
+	line-height: 23px;
+	border: 1px solid #e9eaec;
+	background: #fff;
+	padding: 0 5px 0 12px;
+	vertical-align: middle;
+	color: #666;
+	-webkit-transition: all 0.3s ease-in;
+	-moz-transition: all 0.3s ease-in;
+	transition: all 0.3s ease-in;
+}
+
+.tags-li:not(.active):hover {
+	background: #f8f8f8;
+}
+
+.tags-li.active {
+	color: #fff;
+}
+
+.tags-li-title {
+	float: left;
+	max-width: 80px;
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	margin-right: 5px;
+	color: #666;
+}
+
+.tags-li.active .tags-li-title {
+	color: #fff;
+}
+
+.tags-close-box {
+	position: absolute;
+	right: 3px;
+	top: 0;
+	box-sizing: border-box;
+	padding-top: 7px;
+	text-align: center;
+	width: 110px;
+	height: 23px;
+	background: #fff;
+	z-index: 10;
+}
+</style>

+ 401 - 0
src/components/alarmPopupa/index.vue

@@ -0,0 +1,401 @@
+<template>
+  <div class="alarmBox" :class="alarmList?.length ? 'notEmpty' : ''">
+    <div
+      class="alarmItem animate__animated"
+      :class="item.class"
+      v-for="(item, index) in alarmList"
+      :key="index"
+    >
+      <div class="alarmTitle">{{ item.alarmName }}</div>
+      <div class="alarmContent">
+        <div class="contentItem">报警描述: {{ item.description }}</div>
+        <div class="contentItem">报警时间: {{ item.tsName }}</div>
+      </div>
+      <div class="btntBox" :class="`lv${item.lv}BdColor`">
+        <div class="btnItem" :class="`lv${item.lv}BdColor`">
+          <el-button
+            class="comfirmAll"
+            size="small"
+            type="text"
+            @click="comfirm(item)"
+            >确认本条</el-button
+          >
+        </div>
+        <div class="btnItem">
+          <el-button
+            class="comfirmAll"
+            size="small"
+            type="text"
+            @click="comfirmAll"
+            >全部确认</el-button
+          >
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { confirmAlart } from "@api/api.js";
+export default {
+  data() {
+    return {
+      alarmList: [],
+      seriousWarning: false,
+      audioElement: null,
+      ws: null,
+      timeConnect: 0,
+      limitConnect: 5,
+    };
+  },
+
+  created() {
+    this.webSocketInit(
+      `ws://10.81.3.154:6014/websocket/${this.$store.state.user.authToken}`
+    );
+  },
+
+  unmounted() {
+    this.ws?.close();
+  },
+
+  methods: {
+    getAlarmName(alarmItem) {
+      let alarmName = "";
+      if (alarmItem.deviceType === "booststation") {
+        alarmName += "升压站";
+      } else if (alarmItem.deviceType === "inverter") {
+        alarmName += "逆变器";
+      } else if (alarmItem.deviceType === "windturbine") {
+        alarmName += "风机";
+      }
+
+      if (alarmItem.alarmType === "custom") {
+        alarmName += "自定义";
+      }
+      return `${alarmName}报警`;
+    },
+
+    comfirm(item) {
+      this.$confirm("您确定要执行此操作吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        item.class = `animate__bounceOutRight lv${item.lv}`;
+        item.confirm = true;
+        setTimeout(() => {
+          item.class = `animate__bounceOutRight hidden lv${item.lv}`;
+          confirmAlart([item]).then((res) => {
+            if (res.code === 200) {
+              this.BASE.showMsg({
+                type: "success",
+                msg: "确认成功",
+              });
+              this.$store.commit("removeWarning", item);
+              this.playAudioEffect();
+            }
+          });
+        }, 500);
+      });
+    },
+
+    comfirmAll() {
+      this.$confirm("您确定要执行此操作吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        for (let i = 0; i < this.alarmList.length; i++) {
+          if (!this.alarmList[i].comfirm) {
+            this.alarmList[
+              i
+            ].class = `animate__bounceOutRight lv${this.alarmList[i].lv}`;
+            this.alarmList[i].confirm = true;
+            setTimeout(() => {
+              this.alarmList[
+                i
+              ].class = `animate__bounceOutRight hidden lv${this.alarmList[i].lv}`;
+            }, 500);
+          }
+        }
+
+        confirmAlart(this.alarmList).then((res) => {
+          if (res.code === 200) {
+            this.BASE.showMsg({
+              type: "success",
+              msg: "全部确认成功",
+            });
+            this.$store.commit("emptyWarning");
+            this.playAudioEffect();
+          }
+        });
+        this.playAudioEffect();
+      });
+    },
+
+    getRandomNumber(min, max) {
+      return Math.floor(Math.random() * (max - min + 1)) + min;
+    },
+
+    playAudioEffect() {
+      const fiveLvWarning = this.alarmList.some((ele) => {
+        return ele.lv === 5 && !ele.confirm;
+      });
+
+      const fourLvWarning = this.alarmList.some((ele) => {
+        return ele.lv === 4 && !ele.confirm;
+      });
+
+      if (fiveLvWarning && !this.seriousWarning) {
+        this.seriousWarning = true;
+        this.audioElement = new Audio();
+        this.audioElement.src = "./static/sound/lv5.mp3";
+        this.audioElement.loop = true;
+        this.audioElement?.play();
+      } else if (fourLvWarning && !this.seriousWarning) {
+        this.audioElement = new Audio();
+        this.audioElement.src = "./static/sound/lv4.mp3";
+        this.audioElement.addEventListener("ended", () => {
+          this.audioElement.removeEventListener(
+            "ended",
+            this.stopPlayAudioEffect
+          );
+        });
+        this.audioElement?.play();
+      } else {
+        if (!this.seriousWarning) {
+          this.stopPlayAudioEffect();
+        }
+      }
+    },
+
+    stopPlayAudioEffect() {
+      this.seriousWarning = false;
+      if (this.audioElement) {
+        this.audioElement.pause();
+        this.audioElement.currentTime = 0;
+        this.audioElement.loop = false;
+      }
+      this.audioElement = null;
+    },
+
+    webSocketInit(serveIP) {
+      if ("WebSocket" in window) {
+        this.ws = new WebSocket(serveIP);
+        this.ws.onmessage = (res) => {
+          let alarmItem = JSON.parse(res.data);
+          if (alarmItem) {
+            const alarmOption = {
+              lv: alarmItem.rank,
+              id: alarmItem.id,
+              class: `animate__bounceInRight lv${alarmItem.rank}`,
+              confirm: alarmItem.isClose,
+              alarmType: alarmItem.alarmType,
+              alarmName: this.getAlarmName(alarmItem),
+              description: alarmItem.description,
+              deviceType: alarmItem.deviceType,
+              oval: alarmItem.oval,
+              triggerType: alarmItem.triggerType,
+              ts: alarmItem.ts,
+              tsName: new Date(alarmItem.ts).formatDate("yyyy-MM-dd hh:mm:ss"),
+            };
+            this.alarmList.push(alarmOption);
+            this.$store.commit("setWarning", alarmOption);
+            this.alarmList.sort((a, b) => {
+              return b.lv - a.lv;
+            });
+            this.playAudioEffect();
+          }
+        };
+
+        this.ws.onclose = () => {
+          this.ws = null;
+        };
+
+        this.ws.onopen = () => {
+          this.timeConnect = 0;
+          console.log("WebSocket 服务已建立");
+        };
+
+        this.ws.onerror = () => {
+          this.reconnect(serveIP);
+        };
+      } else {
+        this.BASE.showMsg({
+          msg: "当前浏览器不支持 WebSocket ,请更换浏览器后重试",
+        });
+      }
+    },
+
+    reconnect(serveIP) {
+      if (this.timeConnect < this.limitConnect) {
+        console.log(`webSocket 连接失败,第 ${++this.timeConnect} 次重连`);
+        setTimeout(() => {
+          this.webSocketInit(serveIP);
+        }, 2000);
+      } else {
+        console.log("webSocket 连接已超时");
+        this.BASE.showMsg({
+          showClose: true,
+          duration: 0,
+          msg: `webSocket 连接超时,实时报警获取失败`,
+        });
+        this.ws?.close();
+      }
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.alarmBox {
+  width: 200px;
+  height: 100%;
+  padding: 0 12px 0 30px;
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  z-index: 5000;
+  display: flex;
+  flex-direction: column-reverse;
+  align-items: center;
+  font-size: 12px;
+  overflow-y: scroll;
+  pointer-events: none;
+  transition: 0.2s;
+
+  .alarmItem {
+    width: 100%;
+    box-sizing: border-box;
+    border-radius: 8px;
+    border: 1px solid #ebeef5;
+    background: #1890ff;
+    margin-bottom: 12px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    color: #fff;
+    pointer-events: auto;
+    cursor: pointer;
+    .alarmTitle {
+      display: flex;
+      justify-content: flex-start;
+      align-content: center;
+      width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      padding: 0 8px;
+      margin-top: 8px;
+    }
+
+    .alarmContent {
+      width: calc(100% - 16px);
+      display: flex;
+      justify-content: flex-start;
+      align-items: flex-start;
+      flex-wrap: wrap;
+      margin-top: 4px;
+      padding: 0 8px;
+
+      .contentItem {
+        width: 100%;
+        display: flex;
+        justify-content: flex-start;
+        align-items: center;
+        margin-bottom: 2px;
+        word-wrap: break-word;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+    }
+
+    .btntBox {
+      display: flex;
+      width: 100%;
+      justify-content: center;
+      align-items: center;
+      margin-top: 4px;
+      border-top: 1px solid red;
+
+      .btnItem {
+        width: 50%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        padding: 4px 0;
+
+        .el-button {
+          padding: 0;
+          width: 45%;
+          height: 20px;
+          min-height: 20px;
+        }
+
+        .confirmCurrent {
+          color: #67c23a;
+        }
+        .confirmAll {
+          color: #e6a23c;
+        }
+
+        &:first-child {
+          border-right: 1px solid red;
+        }
+      }
+    }
+    &.lv5 {
+      background: #fef0f0;
+      border: 1px solid #f56c6c;
+      color: #f56c6c;
+    }
+
+    &.lv4 {
+      background: #f0f9eb;
+      border: 1px solid #e6a23c;
+      color: #e6a23c;
+    }
+
+    &.lv1,
+    &.lv2,
+    &.lv3 {
+      background: #fdf6ec;
+      border: 1px solid #67c23a;
+      color: #67c23a;
+    }
+
+    .lv1BdColor,
+    .lv2BdColor,
+    .lv3BdColor {
+      border-color: #67c23a !important;
+    }
+
+    .lv4BdColor {
+      border-color: #e6a23c !important;
+    }
+
+    .lv5BdColor {
+      border-color: #f56c6c !important;
+    }
+
+    &.hidden {
+      height: 0;
+      padding: 0;
+      margin-bottom: 0;
+      border: 0;
+      transition: 0.2s;
+      overflow: hidden;
+    }
+  }
+
+  &::-webkit-scrollbar {
+    width: 0; /* 隐藏Webkit浏览器的滚动条宽度 */
+    height: 0; /* 隐藏Webkit浏览器的滚动条高度 */
+  }
+
+  &.notEmpty:hover {
+    background: rgba(0, 0, 0, 0.12);
+    box-shadow: 0 0 12px rgba(0, 0, 0, 0.12);
+    transition: 0.2s;
+  }
+}
+</style>

+ 20 - 0
src/components/vpro-materials/basic-container/BasicContainer.vue

@@ -0,0 +1,20 @@
+<template>
+  <div class="basic-container">
+    <slot></slot>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'basic-container',
+}
+</script>
+
+<style>
+  .basic-container {
+    padding: 20px;
+    margin-bottom: 20px;
+    background-color: #fff;
+    border-radius: 6px;
+  }
+</style>

+ 3 - 0
src/components/vpro-materials/basic-container/index.js

@@ -0,0 +1,3 @@
+import BasicContainer from './BasicContainer.vue';
+
+export default BasicContainer;

+ 3 - 0
src/components/vpro-materials/vpro-form/index.js

@@ -0,0 +1,3 @@
+import vProForm from './vProForm.vue'
+
+export default vProForm

File diff suppressed because it is too large
+ 1004 - 0
src/components/vpro-materials/vpro-form/vProForm.vue


+ 3 - 0
src/components/vpro-materials/vpro-table/index.js

@@ -0,0 +1,3 @@
+import vProTable from './vProTable.vue'
+
+export default vProTable

+ 628 - 0
src/components/vpro-materials/vpro-table/vProTable.vue

@@ -0,0 +1,628 @@
+<template>
+  <!-- <el-main> -->
+  <div>
+    <el-table
+      ref="YxTable"
+      border
+      :row-key="getRowKey"
+      :data="data"
+      :span-method="spanMethod"
+      :highlight-current-row="highlightCcurrentRow"
+      :row-class-name="rowClassName"
+      :default-expand-all="defaultExpandAll"
+      :tree-props="treeProps"
+      :filter-method="filterMethod"
+      :expand-row-keys="expandRowKeys"
+      :reserve-selection="reserveSelection"
+      @current-change="changeRow"
+      @selection-change="selectionChange"
+      :height="height"
+    >
+      <el-table-column v-if="select" type="selection"></el-table-column>
+      <template v-for="(item, index) in columns">
+        <template v-if="!item.type || item.type == 'string'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :prop="item.dataIndex"
+            :label="item.title"
+            :width="item.width"
+            :sortable="item.sorter || false"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+            :formatter="item.customRender ? item.customRender : null"
+          >
+          </el-table-column>
+        </template>
+        <template v-if="item.type == 'map'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :prop="item.dataIndex"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+          >
+            <template v-slot:default="scope">
+              <div :class="item.class">{{ formatterMap(item, scope.row) }}</div>
+            </template>
+          </el-table-column>
+        </template>
+        <template v-if="item.type == 'newMap'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :prop="item.dataIndex"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+          >
+            <template v-slot:default="scope">
+              <div :class="item.class">
+                {{ formatterNewMap(item, scope.row) }}
+              </div>
+            </template>
+          </el-table-column>
+        </template>
+        <!-- 时间类型 -->
+        <template v-if="item.type == 'data'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :prop="item.dataIndex"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :header-align="item.headerAlign"
+            :render-header="item.renderHeader"
+            :formatter="formatterData"
+          >
+            <template v-slot:default="scope">
+              <div :class="item.class">
+                {{ formatterData(item, scope.row) }}
+              </div>
+            </template>
+          </el-table-column>
+        </template>
+        <template v-if="item.type == 'date'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :prop="item.dataIndex"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+            :formatter="formatterData"
+          >
+            <template v-slot:default="scope">
+              <div :class="item.class">
+                {{ formatterData(item, scope.row) }}
+              </div>
+            </template>
+          </el-table-column>
+        </template>
+        <!-- 金额 -->
+        <template v-if="item.type == 'amount'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :prop="item.dataIndex"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+            :formatter="formatterData"
+          >
+            <template v-slot:default="scope">
+              <div :class="item.class">
+                ¥{{ getNumberFormat(scope.row, item) }}
+              </div>
+            </template>
+          </el-table-column>
+        </template>
+        <template v-else-if="item.type == 'image'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+          >
+            <template v-slot:default="scope">
+              <el-image
+                :class="item.class || 'c_image'"
+                fit="cover"
+                :src="scope.row[item.dataIndex]"
+                :preview-src-list="[scope.row[item.dataIndex]]"
+              >
+                <template v-slot:error class="image-slot">
+                  <i class="el-icon-picture-outline"></i>
+                </template>
+              </el-image>
+            </template>
+          </el-table-column>
+        </template>
+        <template v-else-if="item.type == 'action'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+          >
+            <template v-slot:default="data">
+              <template v-for="(action, actionIndex) in item.actions" :key="actionIndex">
+                <el-button
+                  v-show="
+                    !getAtionFormatter(action, actionIndex, data).hide &&
+                    action.copyFormatter
+                  "
+                  type="text"
+                  size="small"
+                  @click="onCopyClick(action, data)"
+                >
+                  {{ getAtionFormatter(action, actionIndex, data).title }}
+                </el-button>
+                <el-button
+                  v-show="
+                    !getAtionFormatter(action, actionIndex, data).hide &&
+                    !getAtionFormatter(action, actionIndex, data)
+                      .popconfirm
+                  "
+                  :type="
+                    getAtionFormatter(action, actionIndex, data).type
+                      ? getAtionFormatter(action, actionIndex, data).type
+                      : 'text'
+                  "
+                  size="small"
+                  @click="itemClick($event, action, data)"
+                  >{{ getAtionFormatter(action, actionIndex, data).title }}
+                </el-button>
+                <!-- <el-popconfirm
+                  class="popconfirm-btn"
+                  v-show="
+                    !getAtionFormatter(action, actionIndex, data).hide &&
+                    getAtionFormatter(action, actionIndex, data).popconfirm
+                  "
+                  :confirmButtonText="
+                    getAtionFormatter(action, actionIndex, data)
+                      .confirmButtonText
+                  "
+                  :confirmButtonType="
+                    getAtionFormatter(action, actionIndex, data)
+                      .confirmButtonType
+                  "
+                  :cancelButtonText="
+                    getAtionFormatter(action, actionIndex, data)
+                      .cancelButtonText
+                  "
+                  :cancelButtonType="
+                    getAtionFormatter(action, actionIndex, data)
+                      .cancelButtonType
+                  "
+                  :icon="getAtionFormatter(action, actionIndex, data).icon"
+                  :iconColor="
+                    getAtionFormatter(action, actionIndex, data).iconColor
+                  "
+                  :hideIcon="
+                    getAtionFormatter(action, actionIndex, data).hideIcon
+                  "
+                  :title="
+                    getAtionFormatter(action, actionIndex, data)
+                      .popconfirmTitle
+                  "
+                  @confirm="itemClick($event, data, 'confirm')"
+                  @cancel="itemClick($event, data, 'cancel')"
+                >
+                  <template v-slot:reference>
+                    <el-button
+                      :type="
+                        getAtionFormatter(action, actionIndex, data).type
+                          ? getAtionFormatter(action, actionIndex, data).type
+                          : 'text'
+                      "
+                      size="small"
+                      >{{
+                        getAtionFormatter(action, actionIndex, data).title
+                      }}</el-button
+                    >
+                  </template>
+                </el-popconfirm> -->
+              </template>
+            </template>
+          </el-table-column>
+        </template>
+        <template v-else-if="item.type == 'custom'">
+          <el-table-column
+            :key="index"
+            v-if="!item.hide"
+            :fixed="item.fixed"
+            :label="item.title"
+            :width="item.width"
+            :align="item.align || 'center'"
+            :render-header="item.renderHeader"
+            :header-align="item.headerAlign"
+          >
+            <template v-slot:default="scope">
+              <slot
+                :name="item.dataIndex"
+                :row="scope.row"
+                :index="scope.$index"
+              ></slot>
+            </template>
+          </el-table-column>
+        </template>
+      </template>
+    </el-table>
+    <template v-if="pagination && pagination.count">
+      <el-pagination
+        background
+        @size-change="sizeChange"
+        @current-change="currentChange"
+        :current-page="pagination.currentPage"
+        :page-sizes="getPageSizes(pagination.pageSizes)"
+        :page-size="getpageSize(pagination)"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="pagination.count"
+      ></el-pagination>
+    </template>
+  </div>
+  <!-- </el-main> -->
+</template>
+
+<script>
+/**
+ * @Description: 使用例子:
+ * <div>
+		<vpro-table :select="true"
+      :columns="tableColumns"
+      :selectionChange="selectionChange"
+      :pagination="page"
+      :data="tableData"></vpro-table>
+	</div>
+ * @property {boolean} select 是否显示勾选列
+ * @property {array} items 组件使用数据的配置
+ * @property {object} data 组件渲染所使用的数据
+ * @property {Function} selectionChange 勾选回调
+ * @property {array} selectedRowKeys 默认选中列
+ * @property {object} pagination 分页对象
+ *
+ */
+import moment from "moment";
+export default {
+  name: "vpro-table",
+  props: {
+    rowKey: {
+      type: String,
+      default: "",
+    },
+    // 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
+    reserveSelection: {
+      type: Boolean,
+      default: false,
+    },
+    // 是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效
+    defaultExpandAll: {
+      type: Boolean,
+      default: false,
+    },
+    // 渲染嵌套数据的配置选项
+    treeProps: {
+      type: Object,
+      default: () => {},
+    },
+    // 数据过滤使用的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 true 就会显示。
+    filterMethod: {
+      type: Function,
+      default: () => {},
+    },
+    // 行的 className
+    rowClassName: {
+      type: Function,
+      default: () => {},
+    },
+    select: {
+      type: Boolean,
+      default: false,
+    },
+    highlightCcurrentRow: {
+      type: Boolean,
+      default: false,
+    },
+    spanMethod: {
+      type: Function,
+      default: () => {},
+    },
+    // 可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
+    expandRowKeys: {
+      type: Array,
+      default: () => [],
+    },
+    data: {
+      type: Array,
+      default: () => [],
+    },
+    columns: {
+      type: Array,
+      default: () => [],
+    },
+    selectionChange: {
+      type: Function,
+      default: () => {},
+    },
+    changeRow: {
+      type: Function,
+      default: () => {},
+    },
+    selectedRowKeys: {
+      type: Array,
+      default: () => [],
+    },
+    pagination: {
+      type: Object,
+      default: () => {},
+    },
+    currentInfo: {
+      type: Object,
+      default: () => {},
+    },
+    height: {
+      type: [String, Number, Boolean],
+			default: false,
+    },
+  },
+  data() {
+    return {
+      currentRow: {},
+    };
+  },
+  created() {
+    this.toggleSelectedRowKeys();
+  },
+  watch: {
+    currentInfo() {
+      this.$refs.YxTable.setCurrentRow(this.currentInfo);
+      this.currentRow = this.currentInfo;
+    },
+    selectedRowKeys() {
+      this.toggleSelectedRowKeys();
+    },
+  },
+  methods: {
+    getRowKey() {
+			return this.rowKey
+		},
+    /**
+     *  金额处理方法
+     *
+     * @number 要格式化的数字
+     * @decimals 保留几位小数
+     * @dec_point 小数点符号
+     * @thousands_sep 千分位符号
+     * @roundtag 舍入参数,默认 "ceil" 向上取,"floor"向下取,"round" 四舍五入
+     *
+     *  @return 返回视图标题
+     */
+    numberFormat(number, decimals, dec_point, thousands_sep, roundtag) {
+      /*
+       * 参数说明:
+       * number:要格式化的数字
+       * decimals:保留几位小数
+       * dec_point:小数点符号
+       * thousands_sep:千分位符号
+       * roundtag:舍入参数,默认 "ceil" 向上取,"floor"向下取,"round" 四舍五入
+       * */
+      number = (number + "").replace(/[^0-9+-Ee.]/g, "");
+      roundtag = roundtag || "floor"; //"ceil","floor","round"
+      let n = !isFinite(+number) ? 0 : +number,
+        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
+        sep = typeof thousands_sep === "undefined" ? "," : thousands_sep,
+        dec = typeof dec_point === "undefined" ? "." : dec_point,
+        s = "",
+        toFixedFix = function (ns, precs) {
+          let k = Math.pow(10, prec);
+
+          return (
+            "" +
+            parseFloat(
+              Math[roundtag](parseFloat((ns * k).toFixed(precs * 2))).toFixed(
+                precs * 2
+              )
+            ) /
+              k
+          );
+        };
+      s = (prec ? toFixedFix(n, prec) : "" + Math.round(n)).split(".");
+      let re = /(-?\d+)(\d{3})/;
+      while (re.test(s[0])) {
+        s[0] = s[0].replace(re, "$1" + sep + "$2");
+      }
+
+      if ((s[1] || "").length < prec) {
+        s[1] = s[1] || "";
+        s[1] += new Array(prec - s[1].length + 1).join("0");
+      }
+      return s.join(dec);
+    },
+    getNumberFormat(row, item) {
+      let {
+        decimals = 2,
+        decPoint = ".",
+        thousandsSep = ",",
+        roundtag = "floor",
+      } = item;
+      return this.numberFormat(
+        row[item.dataIndex],
+        decimals,
+        decPoint,
+        thousandsSep,
+        roundtag
+      );
+    },
+    getCopyFormatter(action, row) {
+      if (action.copyFormatter) {
+        return action.copyFormatter(row);
+      }
+    },
+    onCopyClick(action, row) {
+      /** 复制粘贴点击事件 **/
+      let copyStr = this.getCopyFormatter(action, row);
+      if (!copyStr) {
+        this.$message.error("复制失败");
+      }
+      if (action.click) {
+        action.click(row);
+      }
+    },
+    onCopyError(event, action, row) {
+      /** 复制粘贴-失败 **/
+      // console.log('复制失败', event)
+      this.$message.error("复制失败");
+      if (action.copyError) {
+        action.copyError(event, row);
+      }
+    },
+    onCopySuccess(event, action, row) {
+      /** 复制粘贴-成功 **/
+      // console.log('复制成功', event)
+      this.$message.success("复制成功");
+      if (action.copySuccess) {
+        action.copySuccess(event, row);
+      }
+    },
+    formatterNewMap(item, row) {
+      const map = new Map(item.options);
+      let key = row[item.dataIndex];
+      if (Number.isInteger(key)) {
+        key = key.toString();
+			}
+			if (!map.get(key)) {
+				return '-'
+			}
+      return map.get(key);
+    },
+    formatterMap(item, row) {
+      for (let key in item.options) {
+        if (item.options[key] == row[item.dataIndex]) {
+          return key;
+        }
+      }
+      return item.options[row[item.dataIndex]];
+    },
+    formatterData(item, row) {
+      let format = item.format ? item.format : "YYYY-MM-DD HH:mm:ss";
+      return row[item.dataIndex]
+        ? moment(row[item.dataIndex]).format(format)
+        : "-";
+    },
+    toggleSelectedRowKeys() {
+      this.$nextTick(() => {
+        if (this.selectedRowKeys && this.selectedRowKeys.length) {
+          this.selectedRowKeys.forEach((row) => {
+            this.$refs.YxTable.toggleRowSelection(row);
+          });
+        }
+      });
+    },
+    itemClick(e, action, scope, Events) {
+      let { row, $index } = scope;
+      if (action.click && !action.popconfirm) {
+        action.click(row, e, $index);
+      } else if (action.popconfirm && action[Events]) {
+        action.popconfirm && action[Events](row, e, $index);
+      }
+    },
+    getAtionFormatter(action, actionIndex, row) {
+      if (action.customRender) {
+        let newAction = action.customRender(Object.assign({}, action), row);
+        if (newAction) {
+          if (!newAction.hide) {
+            action.hide = false;
+          } else {
+            action.hide = true;
+          }
+        }
+        return newAction;
+      }
+      return action;
+    },
+    getpageSize(data) {
+      /** 获取没也长度 **/
+      let sizeArr = this.getPageSizes(data.pageSizes);
+      if (data.length) {
+        let isHas = false;
+        sizeArr.map((item) => {
+          if (data.length === item) {
+            isHas = true;
+          }
+        });
+        if (isHas) {
+          return data.length;
+        }
+      }
+      return sizeArr[0];
+    },
+    getPageSizes(data) {
+      /** 页码类型 **/
+      let sizes = [10, 30, 50, 100];
+      if (
+        data &&
+        Object.prototype.toString.call(data) === "[object Array]" &&
+        data.length > 0
+      ) {
+        return data;
+      } else {
+        return sizes;
+      }
+    },
+    sizeChange(data) {
+      /** 页码长度变化回调 **/
+      this.pagination.length = data;
+      if (this.pagination.tableChange) {
+        this.pagination.tableChange();
+      }
+    },
+    currentChange(data) {
+      /** 选中页码下标变化回调 **/
+      this.pagination.currentPage = data;
+      if (this.pagination.tableChange) {
+        this.pagination.tableChange();
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.el-pagination {
+  padding: 22px 10px;
+  text-align: center;
+}
+.popconfirm-btn{
+  margin-left: 10px;
+}
+.c_image{
+  width: 120px;
+  height: auto;
+}
+</style>

+ 254 - 0
src/components/warningCom/multiple-y-line-chart-normal.vue

@@ -0,0 +1,254 @@
+<template>
+  <div class="chart" :id="id"></div>
+</template>
+
+<script>
+import util from "/@/tools/util.js";
+import partten from "/@/tools/partten.js";
+import * as echarts from "echarts";
+import dayjs from "dayjs";
+export default {
+  name: "multiple-y-line-chart-normal",
+  componentName: "multiple-y-line-chart-normal",
+  props: {
+    width: {
+      type: String,
+      default: "100%",
+    },
+    height: {
+      type: String,
+      default: "13.889vh",
+    },
+    // 数据
+    list: {
+      type: Array,
+      default: () => [],
+    },
+    showLegend: {
+      type: Boolean,
+      default: false,
+    },
+    // 轴
+    yAxises: {
+      type: Array,
+      default: () => [
+        {
+          name: "机舱震动x方向",
+          min: -0.01,
+          max: 0,
+          unit: "",
+          position: "left",
+        },
+        {
+          name: "机舱震动y方向",
+          min: -0.01,
+          max: 0,
+          unit: "",
+          position: "right",
+        },
+        {
+          name: "机舱震动最大偏移值",
+          min: 0,
+          max: 1,
+          unit: "",
+          position: "left",
+        },
+        {
+          name: "风速1",
+          min: 0,
+          max: 10,
+          unit: "",
+          position: "right",
+        },
+        {
+          name: "风速2",
+          min: 0,
+          max: 10,
+          unit: "",
+          position: "left",
+        },
+      ],
+    },
+  },
+  data() {
+    return {
+      id: "",
+      chart: null,
+      color: ["#323E6F", "#1DA0D7", "#05BB4C", "#EDB32F", "#DB5520"],
+    };
+  },
+  computed: {
+    legend() {
+      return this.list.map((t) => {
+        return t.name;
+      });
+    },
+    xdata() {
+       return this.list[1]?.data.map((t) => {
+          return dayjs(t.ts).format("HH:mm:ss");
+        });
+    },
+    yAxis() {
+      let result = [];
+      let p = { left: 0, right: 0 };
+      this.yAxises.forEach((item, index) => {
+        result.push({
+          type: "value",
+          name: item.name,
+          nameLocation: p[item.position] % 2 == 0 ? "end" : "start",
+          nameGap: 10,
+          min: item.min,
+          max: item.max,
+          position: index % 2 == 0 ? "left" : "right",
+          offset: p[item.position] * 50,
+          axisLabel: {
+            formatter: "{value}",
+            fontSize: 12,
+          },
+          //分格线
+          splitLine: {
+            lineStyle: {
+              color: partten.getColor("gray"),
+              type: "dashed",
+            },
+          },
+          axisLine: {
+            show: true,
+          },
+          axisTick: {
+            show: true,
+          },
+        });
+        p[item.position]++;
+      });
+      return result;
+    },
+    series() {
+      let result = [];
+      this.list.forEach((value, index) => {
+        result.push({
+          name: value.name,
+          type: "line",
+          smooth: true,
+          zlevel: index,
+          lineStyle: {
+            normal: {
+              color: this.color[index],
+              width: 1,
+            },
+          },
+          showSymbol: false,
+          yAxisIndex: value.yAxisIndex,
+          data: value.data.map((t) => {
+            return t.doubleValue.toFixed(2)
+          }),
+        });
+      });
+
+      return result;
+    },
+  },
+  methods: {
+    resize() {},
+    initChart() {
+      const chart = echarts.init(this.$el);
+
+      let option = this.option();
+      chart.clear();
+
+      chart.setOption(option);
+      this.resize = function () {
+        chart.resize();
+      };
+
+      window.addEventListener("resize", this.resize);
+    },
+    option: function () {
+      return {
+        color: this.color,
+        tooltip: {
+          trigger: "axis",
+          backgroundColor: "rgba(0,0,0,0.4)",
+          borderColor: partten.getColor("gray"),
+          textStyle: {
+            color: "#fff",
+            fontSize: 12,
+          },
+        },
+        legend: {
+          show: this.showLegend,
+          data: this.legend,
+          top: "top",
+          icon: "circle",
+          itemWidth: 6,
+          inactiveColor: partten.getColor("gray"),
+          textStyle: {
+            color: partten.getColor("grayl"),
+            fontSize: 12,
+          },
+        },
+        grid: {
+          top: 27,
+          left: 16,
+          right: 16,
+          bottom: 0,
+          containLabel: true,
+        },
+        xAxis: [
+          {
+            type: "category",
+            boundaryGap: false,
+            axisLabel: {
+              interval:
+                Number((this.xdata?.length / 8).toFixed(0)) > 2
+                  ? Number((this.xdata.length / 8).toFixed(0))
+                  : 0,
+              showMinLabel: true,
+              showMaxLabel: true,
+              formatter: "{value}",
+              fontSize: 12,
+              textStyle: {
+                color: partten.getColor("gray"),
+              },
+            },
+            data: this.xdata,
+          },
+        ],
+        yAxis: this.yAxis,
+        series: this.series,
+      };
+    },
+    reload: function () {
+      const chart = echarts.getInstanceByDom(this.$el);
+      chart.clear();
+      let option = this.option();
+      chart.setOption(option);
+    },
+  },
+  created() {
+    this.id = "pie-chart-" + util.newGUID();
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.$el.style.width = this.width;
+      this.$el.style.height = this.height;
+      this.initChart();
+    });
+  },
+  updated() {
+    this.$nextTick(() => {
+      this.initChart();
+    });
+  },
+  unmounted() {
+    window.removeEventListener("resize", this.resize);
+  },
+};
+</script>
+<style lang="scss" scoped>
+.chart {
+  width: 100%;
+  height: 100%;
+  display: inline-block;
+}
+</style>

+ 50 - 0
src/components/warningCom/panel3.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="com-panel-3">
+    <span class="dot top-left"></span>
+    <span class="dot bottom-left"></span>
+    <span class="dot top-rignt"></span>
+    <span class="dot bottom-right"></span>
+    <slot></slot>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "com-panel-3",
+  componentName: "com-panel-3",
+};
+</script>
+<style lang="scss" scoped>
+.com-panel-3 {
+  position: relative;
+  background: rgba(255, 255, 255, 0.1);
+  padding: 1.4815vh;
+  display: inline-block;
+  .dot {
+    position: absolute;
+    width: 0.370vh;
+    height: 0.370vh;
+    background: #fff;
+
+    &.top-left {
+      top: 0.556vh;
+      left: 0.556vh;
+    }
+
+    &.bottom-left {
+      bottom: 0.556vh;
+      left: 0.556vh;
+    }
+
+    &.top-rignt {
+      top: 0.556vh;
+      right: 0.556vh;
+    }
+
+    &.bottom-right {
+      bottom: 0.556vh;
+      right: 0.556vh;
+    }
+  }
+}
+</style>

+ 288 - 0
src/components/warningCom/table3.vue

@@ -0,0 +1,288 @@
+<template>
+  <el-table
+    class="custom-table"
+    stripe
+    :data="data.data"
+    :height="height"
+    style="width: 100%"
+    @cell-click="onClick"
+    v-if="data && data.data"
+  >
+    <el-table-column
+      v-for="col in data.column"
+      :key="col"
+      :prop="col.field"
+      :label="col.name"
+      :width="col.width"
+      :min-width="col.minWidth"
+      :sortable="col.sortable"
+      :sort-orders="sortOrder"
+      :sort-by="col.field + '.time'"
+      :show-overflow-tooltip="!col.slot"
+      :fixed="col.fixed"
+      :resizable="col.resizable"
+    >
+      <template v-if="col.slot == true" #default="item">
+        <slot
+          :name="col.field"
+          :column="col"
+          :row="item.row"
+          :all="item"
+          :data="item.row[col.field]"
+        ></slot>
+      </template>
+    </el-table-column>
+  </el-table>
+  <el-pagination
+    class="mg-t-8"
+    v-if="pageable"
+    @current-change="handleCurrentChange"
+    :current-page="currentPage"
+    v-model:page-size="selfPageSize"
+    :total="data.total"
+    v-bind="elPaggingProps"
+  >
+  </el-pagination>
+</template>
+
+<script>
+export default {
+  // 名称
+  name: "ComTable",
+  // 使用组件
+  components: {},
+  // 传入参数
+  props: {
+    /**
+             * {
+                    column: [{
+                        name: "风机名称",
+                        field: "name",
+                        width:'', // 宽度
+                        click:function(){} // 点击事件
+                        sortable:fasle,
+                        slot:false,
+                        fixed:false,
+                        align:'center',
+                        resizable :false,
+                    }],
+                    total:200
+                }
+             */
+    data: Object,
+    height: {
+      type: String,
+      default: "",
+    },
+    pageSize: {
+      type: Number,
+      default: 0,
+    },
+    elPaggingProps: {
+      type: Object,
+      default: () => {
+        return {
+          layout: "total, sizes, prev, pager, next, jumper",
+          // "page-sizes": [100, 200, 300, 400],
+        };
+      },
+    },
+    sortOrder: {
+      type: Array,
+      default: () => {
+        return ["descending", "ascending", null];
+      },
+    },
+  },
+  // 自定义事件
+  emits: {
+    // 分页事件
+    onPagging: null,
+  },
+  // 数据
+  data() {
+    return {
+      currentPage: 1,
+    };
+  },
+  computed: {
+    tableData() {
+      let that = this;
+      if (this.sortCol == "") {
+        return this.data.data;
+      } else {
+        let data = this.data.data;
+
+        data.sort((a, b) => {
+          let rev = 1;
+          if (that.sortType == "ASC") rev = 1;
+          else if (that.sortType == "DESC") rev = -1;
+
+          if (a[that.sortCol] > b[that.sortCol]) return rev * 1;
+          if (a[that.sortCol] < b[that.sortCol]) return rev * -1;
+          return 0;
+        });
+        return data;
+      }
+    },
+    pageable() {
+      return this.pageSize != 0;
+    },
+    pages() {
+      if (this.pageable) return parseInt(this.data.total / this.pageSize) + 1;
+      else return 0;
+    },
+    startRow() {
+      if (this.pageable) return (this.currentPage - 1) * this.pageSize;
+      else return 0;
+    },
+    endRow() {
+      if (this.pageable) return this.currentPage * this.pageSize;
+      else return this.data.data.length;
+    },
+    // sortOrder: {
+    //   type: Array,
+    //   default: () => {
+    //     return ["descending", "ascending", null];
+    //   },
+    // },
+  },
+  // 函数
+  methods: {
+    onClick(row, column, cell, event) {
+      // if (column.rawColumnKey.click) column.rawColumnKey.click(event, row);
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.$emit("onPagging", {
+        pageIndex: this.currentPage,
+        pageSize: this.pageSize,
+        start: this.startRow,
+        end: this.endRow,
+      });
+    },
+  },
+  // 生命周期钩子
+  beforeCreate() {
+    // 创建前
+  },
+  created() {
+    // 创建后
+    this.selfPageSize = this.pageSize;
+  },
+  beforeMount() {
+    // 渲染前
+  },
+  mounted() {
+    // 渲染后
+  },
+  beforeUpdate() {},
+  updated() {},
+};
+</script>
+
+<style lang="scss" scoped>
+$titleGray: #9ca5a8;
+$rowGray: #606769;
+$darkBack: #536268;
+$green:#606769;
+$gray:#606769;
+$gray-l:#606769;
+.com-table {
+  width: 100%;
+  border-collapse: collapse;
+
+  thead {
+    tr {
+      display: table;
+      table-layout: fixed;
+      width: 100%;
+
+      th {
+        background-color: fade($darkBack, 20%);
+        height: 30px;
+        line-height: 30px;
+        color: $titleGray;
+        font-weight: 400;
+        font-size: 16px;
+        position: sticky;
+        top: 0;
+        cursor: pointer;
+
+        &.light,
+        &.always-light {
+          color: #606769;
+        }
+      }
+    }
+  }
+
+  tbody {
+    display: block;
+
+    tr {
+      display: table;
+      table-layout: fixed;
+      width: 100%;
+
+      &:nth-child(2n) {
+        background-color: fade($rowGray, 20%);
+      }
+
+      td {
+        padding: 0.556vh 0;
+        color: $rowGray;
+        text-align: center;
+        font-size: 16px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+
+        &.light,
+        &.always-light {
+          color: $green !important;
+        }
+
+        &.num {
+          font-family: "Bicubik";
+          font-weight: 400;
+        }
+      }
+    }
+  }
+
+  .el-pagination {
+    color: $gray;
+    .el-pagination__total {
+      color: $gray;
+    }
+
+    button {
+      &.btn-next,
+      &.btn-prev {
+        background: center center no-repeat fade($gray, 20);
+        color: $gray-l;
+      }
+      &:disabled {
+        color: $gray-l;
+        background-color: fade($gray, 20);
+        cursor: not-allowed;
+      }
+    }
+
+    .el-pager li {
+      color: $gray-l;
+      background: fade($gray, 20);
+      &.active {
+        color: $green;
+      }
+    }
+
+    .el-input__inner {
+      color: $gray-l;
+      background: fade($gray, 20);
+      border: 1px solid fade($gray, 20);
+    }
+  }
+}
+</style>

+ 47 - 0
src/composables/common.js

@@ -0,0 +1,47 @@
+import { computed, ref } from "vue";
+import { ElMessage } from "element-plus";
+import { getjumpwindow, getfaultread, postjumpwindow, postfaultread, getAlarmConfig } from "/@/api/api.js";
+import { async } from "q";
+
+const system = ref({
+    enableWarn: false,
+    enableSpeak: false
+})
+
+export const common = () => {
+    const identity = sessionStorage.getItem('identity')
+    const username = sessionStorage.getItem('ms_username')
+    const notEdit = computed(() => identity == 'user');
+    const setenableWarn = async(warn, speak) => {
+        system.value = {
+            enableWarn: warn,
+            enableSpeak: speak
+        }
+        const res1 = await postjumpwindow(warn)
+        const res2 = await postfaultread(speak)
+        ElMessage.success("保存成功!");
+    };
+    const getenableWarn = async() => {
+        const res = await getAlarmConfig();
+        const enableWarn = res.data.sd.find(ele => {
+            return ele.nemCode === "jumpwindow";
+        });
+        const enableSpeak = res.data.sd.find(ele => {
+            return ele.nemCode === "faultread";
+        });
+        system.value.enableWarn = (!!enableWarn.enable) || true;
+        system.value.enableSpeak = (!!enableSpeak.enable) || true;
+
+        // const res1 = await getjumpwindow()
+        // system.value.enableWarn = res1.data
+        // const res2 = await getfaultread()
+        // system.value.enableSpeak = res2.data
+    };
+    return {
+        notEdit,
+        username,
+        system,
+        setenableWarn,
+        getenableWarn,
+    };
+};

+ 57 - 0
src/main.js

@@ -0,0 +1,57 @@
+import { createApp } from "vue";
+import App from "./App.vue";
+import router from "./router";
+import store from "./store";
+import ElementPlus from "element-plus";
+import "element-plus/lib/theme-chalk/index.css";
+import "./assets/css/icon.css";
+import "./assets/font/iconfont.css";
+import "default-passive-events";
+import dialogDrag from "/@/assets/js/dialogDrag.js"; // 地址就是dialogDrag.js在位置
+
+import "animate.css";
+
+import "./permission";
+// import Antd from 'ant-design-vue';
+// import 'ant-design-vue/dist/antd.css';
+
+import basicTool from "@tools/basicTool";
+
+/**
+ * 对 Date 的扩展,将 Date 转化为指定格式的字符串
+ * @param {String} fmt 传入一个字符串,根据所传字符串的格式返回转换后特定格式的日期。
+ * 调用姿势:new Date().formatDate("yyyy-MM-dd hh:mm:ss");
+ */
+Date.prototype.formatDate = function(fmt) {
+    let o = {
+        "M+": this.getMonth() + 1, //月份 
+        "d+": this.getDate(), //日 
+        "h+": this.getHours(), //小时 
+        "m+": this.getMinutes(), //分 
+        "s+": this.getSeconds(), //秒 
+        "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
+        "S": this.getMilliseconds() //毫秒 
+    };
+    if (/(y+)/.test(fmt)) {
+        fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+    }
+    for (let k in o) {
+        if (new RegExp("(" + k + ")").test(fmt)) {
+            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+        }
+
+    }
+    return fmt;
+}
+
+const app = createApp(App);
+
+app.use(router)
+    .use(store)
+    .use(dialogDrag)
+    .use(ElementPlus, { size: "small" })
+    // .use(Antd)
+
+app.config.globalProperties.BASE = basicTool; //全局注册
+
+app.mount("#app");

+ 353 - 0
src/pages/Dashboard.vue

@@ -0,0 +1,353 @@
+<template>
+  <div class="main">
+    <div class="dChart" style="height: 500px; width: 50%" :id="eChart"></div>
+    <div class="dChart" style="height: 500px; width: 50%" :id="eChart1"></div>
+  </div>
+  <div class="dChart" style="height: 500px; width: 100%" :id="eChart2"></div>
+</template>
+
+<script setup>
+import { ref, onMounted, reactive, computed, onUnmounted, warn } from "vue";
+import * as echarts from "echarts";
+onMounted(() => {
+  initchat();
+  initchat1();
+  initchat2();
+});
+onUnmounted(() => {
+  console.warn("onUnmounted,页面销毁定时任务");
+  state.echarts?.clear(); //销毁
+});
+
+const state = reactive({
+  echarts: null,
+});
+const eChart = ref("eChart" + Date.now() + Math.random());
+const eChart1 = ref("eChart1" + Date.now() + Math.random());
+const eChart2 = ref("eChart2" + Date.now() + Math.random());
+
+const initchat = async () => {
+  let angle = 0; //角度,用来做简单的动画效果的
+  let value = 78; //图上角度数据
+  let option = {
+    backgroundColor: "#0D2753",
+    title: {
+      text: "{a|" + value + "}{c|%}",
+      x: "center",
+      y: "center",
+      textStyle: {
+        rich: {
+          a: {
+            fontSize: 45,
+            color: "#ffffff",
+            fontWeight: "bold",
+          },
+          c: {
+            fontSize: 45,
+            color: "#ffffff",
+            fontWeight: "normal",
+          },
+        },
+      },
+    },
+    series: [
+      //内环
+      {
+        name: "",
+        type: "custom",
+        coordinateSystem: "none",
+        renderItem: function (params, api) {
+          return {
+            type: "arc",
+            shape: {
+              cx: api.getWidth() / 2,
+              cy: api.getHeight() / 2,
+              r: (Math.min(api.getWidth(), api.getHeight()) / 2.3) * 0.65,
+              startAngle: ((0 + -angle) * Math.PI) / 180,
+              endAngle: ((360 + -angle) * Math.PI) / 180,
+            },
+            style: {
+              stroke: "#0CD3DB",
+              fill: "transparent",
+              lineWidth: 0.5,
+            },
+            silent: true,
+          };
+        },
+        data: [0],
+      },
+      //外环
+      {
+        name: "",
+        type: "pie",
+        radius: ["85%", "70%"],
+        silent: true,
+        clockwise: true,
+        startAngle: 90,
+        z: 0,
+        zlevel: 0,
+        label: {
+          normal: {
+            position: "center",
+          },
+        },
+        data: [
+          {
+            value: value,
+            name: "",
+            itemStyle: {
+              normal: {
+                //外环发光
+                borderWidth: 0.5,
+                shadowBlur: 20,
+                borderColor: "#4bf3f9",
+                shadowColor: "#9bfeff",
+                color: {
+                  // 圆环的颜色
+                  colorStops: [
+                    {
+                      offset: 0,
+                      color: "#4bf3f9", // 0% 处的颜色
+                    },
+                    {
+                      offset: 1,
+                      color: "#4bf3f9", // 100% 处的颜色
+                    },
+                  ],
+                },
+              },
+            },
+          },
+          {
+            value: 100 - value,
+            name: "",
+            label: {
+              normal: {
+                show: false,
+              },
+            },
+            itemStyle: {
+              normal: {
+                color: "#173164",
+              },
+            },
+          },
+        ],
+      },
+    ],
+  };
+
+  let chat = echarts.init(document.getElementById(eChart.value));
+  state.echarts = await chat.setOption(option);
+};
+
+const initchat1 = async () => {
+  let dataX = [
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+    "XX街道",
+  ]; //名称
+  let dataY = [
+    20, 50, 15, 35, 50, 30, 40, 50, 60, 20, 50, 15, 35, 50, 30, 40, 50, 60,
+  ]; //数据
+  let zoomShow = false;
+  if (dataY.length > 14) {
+    zoomShow = true;
+  } else {
+    zoomShow = false;
+  }
+  var option = {
+    backgroundColor: "#0D2753",
+    tooltip: {
+      trigger: "axis",
+      axisPointer: {
+        type: "shadow",
+      },
+    },
+    grid: {
+      top: "10%",
+      right: "5%",
+      left: "5%",
+      bottom: "10%",
+    },
+    xAxis: [
+      {
+        type: "category",
+        data: dataX,
+        axisLine: {
+          lineStyle: {
+            color: "rgba(66, 192, 255, .3)",
+          },
+        },
+        axisLabel: {
+          interval: 0,
+          margin: 10,
+          color: "#05D5FF",
+          textStyle: {
+            fontSize: 11,
+          },
+          rotate: "45",
+        },
+        axisTick: {
+          //刻度
+          show: false,
+        },
+      },
+    ],
+    yAxis: [
+      {
+        axisLabel: {
+          padding: [3, 0, 0, 0],
+          formatter: "{value}",
+          color: "rgba(95, 187, 235, 1)",
+          textStyle: {
+            fontSize: 11,
+          },
+        },
+        axisTick: {
+          show: true,
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(66, 192, 255, .3)",
+          },
+        },
+        splitLine: {
+          lineStyle: {
+            color: "rgba(255,255,255,0)",
+          },
+        },
+      },
+    ],
+    dataZoom: [
+      //滚动条
+      {
+        show: zoomShow,
+        type: "slider",
+        realtime: true,
+        startValue: 0,
+        endValue: 14,
+        xAxisIndex: [0],
+        bottom: "10",
+        left: "30",
+        height: 10,
+        borderColor: "rgba(0,0,0,0)",
+        textStyle: {
+          color: "#05D5FF",
+        },
+      },
+    ],
+    series: [
+      {
+        type: "bar",
+        data: dataY,
+        barWidth: "10",
+        itemStyle: {
+          normal: {
+            color: new echarts.graphic.LinearGradient(
+              0,
+              0,
+              0,
+              1,
+              [
+                {
+                  offset: 0,
+                  color: "rgba(5, 213, 255, 1)", // 0% 处的颜色
+                },
+                {
+                  offset: 0.98,
+                  color: "rgba(5, 213, 255, 0)", // 100% 处的颜色
+                },
+              ],
+              false
+            ),
+            shadowColor: "rgba(5, 213, 255, 1)",
+            shadowBlur: 4,
+          },
+        },
+        label: {
+          normal: {
+            show: true,
+            lineHeight: 10,
+            formatter: "{c}",
+            position: "top",
+            textStyle: {
+              color: "#fff",
+              fontSize: 10,
+            },
+          },
+        },
+      },
+    ],
+  };
+
+  let chat = echarts.init(document.getElementById(eChart1.value));
+  await chat.setOption(option);
+};
+const initchat2 = async () => {
+  var res = [];
+  let option = {
+    xAxis: {
+      type: "category",
+      boundaryGap: true,
+      data: (function () {
+        var len = 50;
+        while (len--) {
+          res[len] = len;
+        }
+        return res;
+      })(),
+    },
+
+    yAxis: {
+      type: "value",
+      scale: true,
+      name: "",
+      max: 2,
+      min: 0,
+      boundaryGap: [0.2, 0.2],
+    },
+
+    series: {
+      name: "",
+      type: "line",
+      data: (function () {
+        var res = [];
+        var len = 0;
+        while (len < 10) {
+          res.push(Math.random());
+          len++;
+        }
+        return res;
+      })(),
+    },
+  };
+  let chat = echarts.init(document.getElementById(eChart2.value));
+  setInterval(function () {
+    var data0 = option.series.data;
+    data0.push(Math.random());
+    chat.setOption(option);
+  }, 1000);
+};
+</script>
+
+<style lang="scss" scoped>
+.main {
+  display: flex;
+}
+</style>

+ 65 - 0
src/pages/Home.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="about">
+    <el-config-provider :locale="zhCn">
+      <v-header />
+      <v-sidebar />
+      <div class="content-box" :class="{ 'content-collapse': collapse }">
+        <v-tags></v-tags>
+        <div class="content">
+          <router-view />
+        </div>
+      </div>
+    </el-config-provider>
+    <AlarmPopupa />
+  </div>
+</template>
+<script setup>
+import { ElConfigProvider } from "element-plus";
+import zhCn from "element-plus/lib/locale/lang/zh-cn";
+
+import { computed, onBeforeMount } from "vue";
+import { useStore } from "vuex";
+import vHeader from "../components/Header.vue";
+import vSidebar from "../components/Sidebar.vue";
+import vTags from "../components/Tags.vue";
+import AlarmPopupa from "../components/alarmPopupa/index.vue";
+
+import { fetchStationListAll, fetchBooststation } from "/@/api/api.js";
+const store = useStore();
+const collapse = computed(() => store.state.collapse);
+onBeforeMount(async () => {
+  await getStationListAll();
+  await getBooststation();
+});
+// //get 场站
+// const getStationList = async () => {
+// //   const resold = await fetchStationList();
+
+//   const resnew = await getStationinfoAll();
+// //   console.log("resold>>>>>>>>qweqwe", resold);
+//   let arr = [];
+//   resnew.data.forEach((item) => {
+//     let ddd = {
+
+//     id :item.id,
+//     name: item.name,
+//     model:item.model,
+//     modelList : item.modelList,
+//     equipmentmodel: null,
+// };
+//     arr.push(ddd);
+//   });
+
+//   // console.log("resnew>>>>>>>>123", resnew);
+//   // console.log("arr>>>>>>>>456", arr);
+//   store.dispatch("actionsStationList", arr);
+// };
+const getStationListAll = async () => {
+  const { data } = await fetchStationListAll();
+  store.dispatch("actionsStationListAll", data);
+};
+const getBooststation = async () => {
+  const { data } = await fetchBooststation();
+  store.dispatch("actionsBooststationList", data);
+};
+</script>

+ 211 - 0
src/pages/Login.vue

@@ -0,0 +1,211 @@
+<template>
+  <div class="login-wrap">
+    <!-- <img
+      class="login-wrap-icon"
+      src="../assets/img/login-wrap-icon.png"
+      draggable="false"
+    /> -->
+    <div class="login-content">
+      <!-- <div class="ms-title">
+        <div style="margin-bottom: 20px">智能预警系统</div>
+        <div style="padding-bottom: 40px; font-size: 30px">
+          Intelligent Early Warning
+        </div>
+      </div> -->
+      <div class="ms-title">
+        <div style="margin-bottom: 20px">智能报警及预警系统</div>
+        <div style="padding-bottom: 40px; font-size: 24px">
+          <!-- Equipment Hidden Hazard Awareness System -->
+          Intelligent Alarms and Early Warnings System
+        </div>
+      </div>
+      <div class="ms-login">
+        <div class="ms-login-title">欢迎登录</div>
+        <el-form
+          :model="userInfo"
+          :rules="rules"
+          ref="login"
+          label-width="0px"
+          class="ms-content"
+        >
+          <el-form-item prop="username" style="margin-bottom: 30px">
+            <el-input
+              v-model="userInfo.username"
+              size="large"
+              placeholder="请输入用户名"
+            >
+              <template #prepend>
+                <el-button icon="el-icon-user"></el-button>
+              </template>
+            </el-input>
+          </el-form-item>
+          <el-form-item prop="password" style="margin-bottom: 30px">
+            <el-input
+              type="password"
+              placeholder="请输入密码"
+              size="large"
+              v-model="userInfo.password"
+              @keyup.enter="submitForm()"
+            >
+              <template #prepend>
+                <el-button icon="el-icon-lock"></el-button>
+              </template>
+            </el-input>
+          </el-form-item>
+          <div class="login-btn">
+            <el-button type="primary" size="large" @click="submitForm()"
+              >登录</el-button
+            >
+          </div>
+          <!-- <p class="login-tips">初始账号密码为admin</p> -->
+        </el-form>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import { ElMessage } from "element-plus";
+import { JSEncrypt } from "jsencrypt";
+import { loginRequest, getPublickey, Login, getUserinfo } from "/@/api/api.js";
+const router = useRouter();
+const login = ref(null);
+const store = useStore();
+const userInfo = reactive({
+  username: "",
+  password: "",
+});
+
+const rules = {
+  username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
+  password: [{ required: true, message: "请输入密码", trigger: "blur" }],
+};
+
+onMounted(async () => {
+  await getkey();
+  store.commit("clearTags");
+  //使用
+  const locationHeBei = await GetQueryString("u");
+  console.warn(locationHeBei);
+  if (locationHeBei) {
+    userInfo.username = "admin";
+    userInfo.password = "admin";
+    loginFun();
+  }
+});
+// 截取URL字段属性
+const GetQueryString = (name) => {
+  var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+  var r = window.location.search.substr(1).match(reg);
+  if (r != null) return unescape(r[2]);
+  return null;
+};
+// 获取公钥
+const getkey = async () => {
+  const key = await getPublickey();
+  sessionStorage.setItem("publicKey", key);
+};
+// 利用jsencrypt.js进行RSA加密
+const encryptionByPublickey = (formData) => {
+  let key = sessionStorage.getItem("publicKey");
+  var encrypt = new JSEncrypt();
+  encrypt.setPublicKey(key);
+
+  var result = encrypt.encrypt(JSON.stringify(formData));
+  return result;
+};
+
+const toUtf8 = (str) => {
+  return new TextEncoder("utf8").encode(str);
+};
+
+const submitForm = () => {
+  login.value.validate((valid) => {
+    if (valid) {
+      loginFun();
+    } else {
+      ElMessage.error("请输入账号和密码");
+      return false;
+    }
+  });
+};
+const loginFun = async () => {
+  //   let requestWrapper = {
+  //     publicKey: sessionStorage.getItem("publicKey"),
+  //     data: encryptionByPublickey(userInfo),
+  //   };
+  const { data: res, msg, code } = await loginRequest({ ...userInfo });
+  if (code == 0) {
+    // sessionStorage.setItem("ms_username", res.data.userName);
+    // sessionStorage.setItem("ms_chinesename", res.data.chineseName);
+    store.commit("user/SET_TOKEN", res);
+    // sessionStorage.setItem("ms_id", res.userId);
+    // sessionStorage.setItem("token", res.token);
+    Login({ userId: res.userId, token: res.accessToken }).then((res) => {
+      if (res.code == 200) {
+        ElMessage.success("登录成功");
+      }
+    });
+    sessionStorage.setItem("identity", res.identity || "admin");
+  } else {
+    ElMessage.error(msg);
+  }
+  router.push("/");
+};
+</script>
+
+<style lang="scss" scoped>
+.login-wrap {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background-image: url("../assets/img/bg.png");
+  background-size: 100%;
+  .login-content {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    .ms-title {
+      flex: 1;
+      margin-left: 20vw;
+      font-size: 50px;
+      color: #dadada;
+      padding: 10px;
+      padding-left: 20px;
+      border-left: 4px solid #fff;
+      align-self: flex-start;
+      margin-top: 36vh;
+    }
+    .ms-login {
+      margin-right: 22vw;
+      width: 20vw;
+      border-radius: 20px;
+      background-color: #1a1a1aa1;
+      overflow: hidden;
+      .ms-login-title {
+        font-size: 24px;
+        color: #fff;
+        padding: 10px;
+        width: 100%;
+        text-align: center;
+        border-bottom: 2px solid rgb(31, 31, 31);
+      }
+    }
+    .ms-content {
+      padding: 30px 30px;
+    }
+    .login-btn {
+      text-align: center;
+    }
+    .login-btn button {
+      width: 100%;
+      height: 36px;
+      margin-bottom: 20px;
+    }
+  }
+}
+</style>

+ 109 - 0
src/pages/alarmConfig/Logs.vue

@@ -0,0 +1,109 @@
+<template>
+  <div>
+    <div class="container">
+      <div class="sleBox">
+        <el-select v-model="value" style="margin-right:10px"  clearable placeholder="类型">
+          <el-option
+            v-for="item in options"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+            :disabled="item.disabled"
+          />
+        </el-select>
+        <el-input v-model="input" clearable placeholder="名称检索" style="width:200px;margin-right:10px"/>
+        <el-button type="primary" @click="search">确定</el-button>
+      </div>
+      <el-table :data="tableData" height="700" stripe style="width: 100%">
+        <el-table-column prop="ruleName" label="规则名称"> </el-table-column>
+        <el-table-column prop="ruleType" label="规则类型"> </el-table-column>
+        <el-table-column prop="stationName" label="场站名称"> </el-table-column>
+        <el-table-column prop="model" label="风机型号"> </el-table-column>
+        <el-table-column label="更改内容" width="600">
+          <template #default="scope">
+            <div v-for="item in scope.row.infoList" :key="item">
+              {{ item.fieldName }} :更改前: {{ item.beforValue }}
+              <el-divider direction="vertical" /> 更改后:
+              {{ item.afterValue }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="updateTime" label="操作时间"> </el-table-column>
+        <el-table-column prop="updateType" label="操作类型"> </el-table-column>
+        <el-table-column prop="updateUser" label="操作人"> </el-table-column>
+      </el-table>
+      <div class="pagination">
+        <el-pagination
+          background
+          layout="total, prev, pager, next"
+          hide-on-single-page
+          :current-page="query.page"
+          :page-size="query.limit"
+          :total="query.pageTotal"
+          @current-change="handlePageChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, reactive, nextTick, watch, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { fetchruleventLogs } from "/@/api/api.js";
+import dayjs from "dayjs";
+onMounted(() => {
+  logsList();
+});
+const query = reactive({
+  page: 1,
+  limit: 15,
+  pageTotal: null,
+});
+let tableData = ref(null);
+let input = ref("");
+let value = ref("");
+let options = ref([
+  {
+    label: "自定义报警",
+    value: "自定义报警",
+  },
+  {
+    label: "风机报警",
+    value: "风机报警",
+  },
+  {
+    label: "升压站报警",
+    value: "升压站报警",
+  },
+]);
+//
+const search = () =>{
+  logsList()
+}
+const logsList = async () => {
+  const { data } = await fetchruleventLogs(query.page, query.limit,input.value,value.value);
+  console.warn(data);
+  query.pageTotal = data.total;
+  tableData.value = data.records;
+};
+// 分页导航
+const handlePageChange = (val) => {
+  query.page = val;
+  logsList();
+};
+// 时间格式化
+const createdtime = (val) => {
+  return dayjs(val).format("YYYY-MM-DD:HH:mm:ss");
+};
+</script>
+<style lang="scss" scoped>
+.container {
+  padding: 20px;
+  box-sizing: border-box;
+}
+.sleBox {
+  margin-bottom: 10px;
+  display: flex;
+}
+</style>

+ 545 - 0
src/pages/alarmConfig/bj_custom/custom.vue

@@ -0,0 +1,545 @@
+<template>
+  <el-card>
+    <el-row type="flex" justify="space-between">
+      <div class="handle-box" style="margin-bottom: 10px">
+        <el-button
+          type="primary"
+          icon="el-icon-lx-add"
+          class="handle-edit mr10"
+          @click="handleInsert"
+        >
+          新增记录
+        </el-button>
+        <el-button
+          type="primary"
+          icon="el-icon-lx-down"
+          class="handle-edit mr10"
+          @click="export2Excel"
+        >
+          批量导出</el-button
+        >
+        <el-button
+          type="primary"
+          icon="el-icon-lx-down"
+          class="handle-edit mr10"
+          @click="outExe"
+        >
+          模板下载</el-button
+        >
+        <el-upload
+          class="handle-edit mr10"
+          style="display: inline"
+          :action="baseURL + 'alertrule/import'"
+          :show-file-list="false"
+          :on-success="handleSuccess"
+          :on-progress="handleProgress"
+          :on-error="handleError"
+        >
+          <el-button
+            type="primary"
+            icon="el-icon-lx-top"
+            @click="exportShow = true"
+          >
+            批量导入</el-button
+          >
+        </el-upload>
+      </div>
+      <div style="display: flex; flex-direction: row; margin-bottom: 10px">
+        <el-select
+          v-if="!isStation"
+          v-model="query.category"
+          class="mr10"
+          style="width: 150px"
+          popper-class="select"
+          @change="categorychanged"
+        >
+          <el-option key="1" label="风机报警" value="windturbine"></el-option>
+          <el-option
+            key="2"
+            label="升压站报警"
+            value="booststation"
+          ></el-option>
+        </el-select>
+        <el-select
+          v-model="query.wpId"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          :placeholder="
+            query.category == 'windturbine' ? '全部场站' : '全部升压站'
+          "
+          popper-class="select"
+          @change="changeStation"
+        >
+          <el-option
+            v-for="item in stationList"
+            :key="item.id"
+            :value="item.id"
+            :label="item.aname"
+          ></el-option>
+        </el-select>
+        <el-select
+          v-if="query.category == 'windturbine'"
+          v-model="query.relatedparts"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          placeholder="所属部件"
+        >
+          <el-option
+            v-for="item in fetchList"
+            :key="item.id"
+            :label="item.name"
+            :value="item.nemCode"
+          >
+          </el-option>
+        </el-select>
+        <el-select
+          v-model="query.enabled"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          placeholder="是否可用"
+        >
+          <el-option
+            v-for="item in state.isEnabled"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          >
+          </el-option>
+        </el-select>
+        <el-select
+          v-if="query.category == 'windturbine'"
+          v-model="query.modelId"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          placeholder="全部机型"
+          popper-class="select"
+        >
+          <el-option
+            v-for="item in modelList"
+            :key="item.id"
+            :value="item.id"
+            :label="item.aname"
+          ></el-option>
+        </el-select>
+        <el-select
+          v-model="query.rank"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          placeholder="全部级别"
+          popper-class="select"
+        >
+          <el-option
+            v-for="item in state.rankList"
+            :key="item.id"
+            :value="item.id"
+            :label="item.name"
+          ></el-option>
+        </el-select>
+        <el-input
+          placeholder="请输入名称"
+          v-model="query.name"
+          style="width: 150px"
+          class="mr10"
+          clearable
+        ></el-input>
+        <el-button icon="el-icon-search" type="primary" @click="getData"
+          >搜索</el-button
+        >
+      </div>
+    </el-row>
+    <el-table
+      :data="state.tableData"
+      border
+      class="table"
+      header-cell-name="table-header"
+      :highlight-current-row="true"
+      height="calc(100% - 42px - 45px)"
+    >
+      <el-table-column
+        v-for="item in query.category == 'windturbine'
+          ? state.tableHeader
+          : state.tableHeader1"
+        :key="item.code"
+        :label="item.title"
+        align="center"
+        :prop="item.code"
+        :minWidth="item.width ? item.width : 60"
+        show-overflow-tooltip
+      >
+        <template #default="scope">
+          <span v-if="item.code == 'rank'">
+            {{ rankConvert(scope.row.rank) }}
+          </span>
+          <span v-else-if="item.code == 'category'">
+            {{ categoryConvert(scope.row.category) }}
+          </span>
+          <span v-else-if="item.code == 'enable'">
+            {{ enabledConvert(scope.row.enable) }}
+          </span>
+          <span v-else>
+            {{ scope.row[item.code] }}
+          </span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="操作" align="center" width="100">
+        <template #default="scope">
+          <el-button
+            type="primary"
+            plain
+            round
+            size="mini"
+            icon="el-icon-lx-edit"
+            :disabled="notEdit"
+            @click="handleEditClick(scope.row)"
+            >编辑</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination
+        background
+        layout="total, prev, pager, next"
+        hide-on-single-page
+        :current-page="query.pagenum"
+        :page-size="query.pagesize"
+        :total="total"
+        @current-change="handlePageChange"
+      ></el-pagination>
+    </div>
+    <customcomponents
+      @close="dialogclose"
+      :isVisible="state.visible"
+      :form="state.form"
+      :rankList="state.rankList"
+      :modelListAll="state.modelListAll"
+      :fetchListAll="state.fetchListAll"
+    />
+  </el-card>
+</template>
+<script setup>
+import {
+  custombj_fetchTableData,
+  getStationinfo,
+  fetchRelatePartAndAlarmType,
+  fetchModel,
+  fetchBooststation,
+  custombj_importTemplate,
+  baseURL,
+} from "/@/api/api.js";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+import {
+  ref,
+  onMounted,
+  provide,
+  computed,
+  reactive,
+  watch,
+  nextTick,
+} from "vue";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import { ElMessageBox, ElMessage } from "element-plus";
+import customcomponents from "./custom_components.vue";
+import { common } from "/@/composables/common";
+const { notEdit } = common();
+const token = { token: sessionStorage.getItem("token") };
+const store = useStore();
+const router = useRouter();
+const isStation = computed(() => store.getters.isStation);
+const query = reactive({
+  pagenum: 1,
+  pagesize: 4,
+  name: "",
+  wpId: "",
+  rank: "",
+  modelId: "",
+  category: "windturbine",
+  enabled: "",
+  relatedparts: "",
+});
+const state = reactive({
+  tableData: [],
+  fetchListAll: [], //部件及预警类型
+  modelListAll: {}, //型号所有列表
+  isEnabled: [
+    {
+      id: false,
+      name: "否",
+    },
+    {
+      id: true,
+      name: "是",
+    },
+  ],
+  rankList: [
+    {
+      id: 1,
+      name: "低",
+    },
+    {
+      id: 2,
+      name: "中低",
+    },
+    {
+      id: 3,
+      name: "中",
+    },
+    {
+      id: 4,
+      name: "中高",
+    },
+    {
+      id: 5,
+      name: "高",
+    },
+  ],
+  visible: false,
+  form: {},
+  tableHeader: [
+    { title: "编码", code: "id" },
+    { title: "场站", code: "stationName", width: "100" },
+    { title: "机型", code: "modelId" },
+    { title: "规则名称", code: "name", width: "150" },
+    { title: "表达式", code: "expression", width: "150" },
+    { title: "描述", code: "description", width: "150" },
+    { title: "所属部件", code: "relatedPartsName" },
+    { title: "级别", code: "rank" },
+    { title: "类型", code: "category" },
+    { title: "是否启用", code: "enable" },
+  ],
+  tableHeader1: [
+    { title: "编码", code: "id" },
+    { title: "升压站", code: "stationName" },
+    { title: "规则名称", code: "name" },
+    { title: "表达式", code: "expression" },
+    { title: "描述", code: "description" },
+    { title: "级别", code: "rank" },
+    { title: "类型", code: "category" },
+    { title: "是否启用", code: "enable" },
+  ],
+});
+//型号列表
+const modelList = computed(() => {
+  if (query.wpId == "") {
+    return [];
+  } else {
+    return state.modelListAll[query.wpId];
+  }
+});
+//部件列表
+const fetchList = computed(() => {
+  if (query.wpId == "") {
+    return [];
+  } else {
+    if (query.wpId.includes("FDC")) {
+      return state.fetchListAll?.fjbj;
+    } else {
+      return state.fetchListAll?.gfbj;
+    }
+  }
+});
+
+// 场站列表/升压站列表
+const stationList = computed(() => {
+  if (query.category == "windturbine") {
+    return store.state.stationListAll;
+  } else {
+    return store.state.booststationList;
+  }
+});
+watch(
+  () => stationList,
+  (val, old) => {
+    val.value.length &&
+      nextTick(async () => {
+        query.wpId = val.value[0]?.id;
+        await getData();
+      });
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+let total = ref(0);
+onMounted(() => {
+  getfetchRelatePart();
+  getequipmentmodel_list();
+  //   getData();
+});
+const dialogclose = () => {
+  state.visible = false;
+  state.form = {};
+  getData();
+};
+const getData = async () => {
+  const { data } = await custombj_fetchTableData(query);
+  state.tableData = data.records;
+  total.value = data.total;
+};
+//所属部件
+const getfetchRelatePart = async () => {
+  const { data } = await fetchRelatePartAndAlarmType();
+  state.fetchListAll = data;
+  //   if (router && router.currentRoute.value.query.name) {
+  //     let queryName = router.currentRoute.value.query.name;
+  //     query.relatedparts = queryName;
+  //   }
+  //   getData();
+};
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await fetchModel();
+  state.modelListAll = data;
+};
+const handleEditClick = (row) => {
+  let obj = Object.assign({}, row);
+  state.form = obj;
+  state.visible = true;
+};
+const handleInsert = () => {
+  state.form = {
+    category: "windturbine",
+    enable: true,
+    stationId: "",
+    expression: "",
+  };
+  state.visible = true;
+};
+
+//changeStation
+const changeStation = async () => {
+  query.modelId = "";
+  query.relatedparts = "";
+};
+//categorychanged
+const categorychanged = async () => {
+  clean();
+  getData();
+};
+
+// 分页导航
+const handlePageChange = (val) => {
+  query.pagenum = val;
+  getData();
+};
+
+// 批量导出
+const export2Excel = async () => {
+  let tableHeader = [];
+  let tableKey = [];
+  const { data } = await custombj_fetchTableData({
+    pagenum: 1,
+    pagesize: total.value,
+    ...query,
+  });
+  ElMessage.success(`导出成功!`);
+  if (query.category == "windturbine") {
+    tableHeader = state.tableHeader.map((item) => item.title);
+    tableKey = state.tableHeader.map((item) => item.code);
+  } else if (query.category == "booststation") {
+    tableHeader = state.tableHeader1.map((item) => item.title);
+    tableKey = state.tableHeader1.map((item) => item.code);
+  }
+  outExportExcel(
+    tableHeader,
+    tableKey,
+    data.records,
+    "自定义预警配置导出excel"
+  );
+};
+// 模板下载
+const outExe = () => {
+  custombj_importTemplate().then((response) => {
+    const link = document.createElement("a");
+    const blob = new Blob([response], {
+      type: "application/vnd.ms-excel",
+    });
+    link.style.display = "none";
+    link.href = URL.createObjectURL(blob);
+    link.download = "自定义报警模板.xlsx";
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  });
+};
+// 批量导入
+const handleSuccess = (response) => {
+  if (response.code == "200") {
+    ElMessage.success("导入成功!");
+    getData();
+  } else {
+    ElMessage.error(response.msg);
+  }
+};
+// 批量导入中
+const handleProgress = (response) => {};
+// 批量导入失败
+const handleError = (response) => {
+  ElMessage.success("导入失败!");
+};
+// 清空字段
+const clean = () => {
+  query.modelId = "";
+  query.relatedparts = "";
+  query.wpId = "";
+  query.enabled = "";
+  query.rank = "";
+  query.name = "";
+  query.pagenum = 1;
+  state.tableData = [];
+  total.value = 0;
+};
+
+const rankConvert = (val) => {
+  if (val == 1) {
+    return "低";
+  } else if (val == 2) {
+    return "中低";
+  } else if (val == 3) {
+    return "中";
+  } else if (val == 4) {
+    return "中高";
+  } else if (val == 5) {
+    return "高";
+  }
+};
+// 类型
+const categoryConvert = (val) => {
+  if (val === "windturbine") {
+    return "风机";
+  } else if (val === "booststation") {
+    return "升压站";
+  }
+};
+// 状态
+const enabledConvert = (val) => {
+  if (val === false) {
+    return "停用";
+  } else if (val === true) {
+    return "启用";
+  }
+};
+</script>
+<style scoped lang="scss">
+:deep.el-card {
+  height: 100%;
+  .el-card__body {
+    height: calc(100% - 40px);
+  }
+}
+.mr10 {
+  margin-right: 10px;
+}
+
+.el-button + .el-button {
+  margin-left: 10px;
+}
+</style>

+ 818 - 0
src/pages/alarmConfig/bj_custom/custom_components.vue

@@ -0,0 +1,818 @@
+<template>
+  <el-dialog
+    v-model="isShow"
+    width="1100px"
+    :before-close="handleClose"
+    destroy-on-close
+  >
+    <el-form
+      ref="ruleFormRef"
+      :model="form"
+      :rules="rules"
+      label-position="top"
+      label-width="200px"
+    >
+      <el-row type="flex" justify="space-between" align="middle" :gutter="10">
+        <el-col :span="15">
+          <el-form-item prop="name">
+            <el-tag>规则名称</el-tag>
+            <el-input v-model="form.name" />
+          </el-form-item>
+        </el-col>
+        <el-col
+          :span="9"
+          style="
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+          "
+        >
+          <el-form-item prop="category">
+            <el-tag>报警类别</el-tag>
+            <el-select
+              v-model="form.category"
+              class="select-mini"
+              @change="categorychanged"
+            >
+              <el-option
+                key="1"
+                label="风机报警"
+                value="windturbine"
+              ></el-option>
+              <el-option
+                key="2"
+                label="升压站报警"
+                value="booststation"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="rank">
+            <el-tag>报警级别</el-tag>
+            <el-select v-model="form.rank" class="select-mini">
+              <el-option
+                v-for="item in rankList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="enable">
+            <el-tag>是否启用</el-tag>
+            <el-switch
+              v-model="form.enable"
+              :active-value="true"
+              :inactive-value="false"
+              active-color="#13ce66"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="10">
+        <el-col :span="6">
+          <el-form-item prop="station">
+            <el-tag>{{
+              form.category == "windturbine" ? "风场" : "升压站"
+            }}</el-tag>
+            <el-select
+              v-model="form.stationId"
+              style="width: 100%"
+              @change="stationChange"
+            >
+              <el-option
+                v-for="item in stationList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="modelId" v-if="form.category == 'windturbine'">
+            <el-tag>风机型号</el-tag>
+            <el-select
+              v-model="form.modelId"
+              style="width: 100%"
+              @change="modelIdChange"
+            >
+              <el-option
+                v-for="item in state.modelList"
+                :key="item"
+                :value="item.id"
+                :label="item.aname"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            prop="relatedParts"
+            v-if="form.category == 'windturbine'"
+          >
+            <el-tag>所属部件</el-tag>
+            <el-select v-model="form.relatedParts" style="width: 100%">
+              <el-option
+                v-for="item in state.relatedPartsList"
+                :key="item.nemCode"
+                :label="item.name"
+                :value="item.nemCode"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="fault">
+            <el-tag>预警类型</el-tag>
+            <el-select v-model="form.range" style="width: 100%">
+              <el-option
+                v-for="i in faultList"
+                :key="i.nemCode"
+                :value="i.nemCode"
+                :label="i.name"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="9">
+          <el-form-item prop="expression">
+            <el-tag>表达式</el-tag>
+            <el-input
+              type="textarea"
+              rows="14"
+              v-model="form.expression"
+              id="expressionInput"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="9">
+          <el-tabs type="border-card">
+            <el-tab-pane label="AI测点">
+              <el-input v-model="state.AIPointSearch"> </el-input>
+              <el-table
+                size="mini"
+                fit
+                :show-header="false"
+                :highlight-current-row="true"
+                :stripe="false"
+                :border="false"
+                height="240"
+                :data="filterAIList"
+                @row-dblclick="rowDbclick"
+              >
+                <el-table-column prop="uniformCode" />
+                <el-table-column prop="name" />
+              </el-table>
+            </el-tab-pane>
+            <el-tab-pane label="DI测点">
+              <el-input v-model="state.DIPointSearch"> </el-input>
+              <el-table
+                size="mini"
+                fit
+                :show-header="false"
+                :highlight-current-row="true"
+                :stripe="false"
+                :border="false"
+                height="240"
+                :data="filterDIList"
+                @row-dblclick="rowDbclick"
+              >
+                <el-table-column prop="uniformCode" />
+                <el-table-column prop="name" />
+              </el-table>
+            </el-tab-pane>
+            <el-tab-pane label="函数">
+              <el-table
+                size="mini"
+                fit
+                :show-header="false"
+                :highlight-current-row="true"
+                :stripe="false"
+                :border="false"
+                :height="270"
+                :data="func"
+                @row-dblclick="tabFuncRowClickHandle"
+              >
+                <el-table-column min-width="60%">
+                  <template #default="scope">
+                    <el-popover trigger="hover" placement="bottom">
+                      <p>描述:{{ scope.row.describe }}</p>
+                      <p>参数:{{ scope.row.param }}</p>
+                      <template #reference>
+                        <span size="medium" transition="fade-in-linear">{{
+                          scope.row.lab
+                        }}</span>
+                      </template>
+                    </el-popover>
+                  </template>
+                </el-table-column>
+                <el-table-column min-width="40%">
+                  <template #default="scope">
+                    <el-popover trigger="hover" placement="bottom">
+                      <p>描述:{{ scope.row.describe }}</p>
+                      <p>参数:{{ scope.row.param }}</p>
+                      <template #reference>
+                        <span size="medium" transition="fade-in-linear">{{
+                          scope.row.name
+                        }}</span>
+                      </template>
+                    </el-popover>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-tab-pane>
+            <el-tab-pane label="运算符">
+              <el-button-group>
+                <el-button
+                  v-for="item in operator"
+                  :key="item"
+                  plain
+                  type="primary"
+                  @click="elInputSplit(item)"
+                  style="width: 60px; margin: 7px 12px; height: 40px"
+                >
+                  {{ item }}
+                </el-button>
+              </el-button-group>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+      <el-row :gutter="24">
+        <el-col :span="24">
+          <el-form-item prop="description">
+            <el-tag>规则描述</el-tag>
+            <el-input
+              type="textarea"
+              rows="4"
+              v-model="form.description"
+              id="descriptionInput"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="closeDialog">取 消</el-button>
+        <el-button type="primary" @click="submitForm(ruleFormRef)"
+          >确 定</el-button
+        >
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, onMounted, reactive, computed, watch, nextTick } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import {
+  custombj_postSave,
+  fetch_electrical_point_ai,
+  fetch_electrical_point_di,
+  getStationinfo,
+  fetchPointList,
+  fetchPointListByBootst,
+} from "/@/api/api.js";
+import { useStore } from "vuex";
+const store = useStore();
+const emits = defineEmits(["close"]);
+const props = defineProps({
+  //是否显示
+  isVisible: {
+    type: Boolean,
+    defaule: false,
+  },
+  //表单
+  form: {
+    type: Object,
+  },
+  //型号接口数据
+  modelListAll: {
+    type: Object,
+  },
+  //所属部件及预警类型
+  fetchListAll: {
+    type: Array,
+  },
+  //报警级别
+  rankList: {
+    type: Array,
+  },
+});
+watch(
+  () => props.isVisible,
+  (val, old) => {
+    nextTick(() => {
+      isShow.value = val;
+    });
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => props.form,
+  (val, old) => {
+    nextTick(() => {
+      form.value = val;
+      if (val.id && val.id != "") {
+        if (val?.stationId.includes("FDC") || val?.stationId.includes("GDC")) {
+          getfetchPointList();
+        } else {
+          getfetchPointListByBootst();
+        }
+      }
+    });
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => props.form?.stationId,
+  (val, old) => {
+    if (val && val != "") {
+      nextTick(async () => {
+        state.modelList = props.modelListAll[val];
+        if (val.includes("FDC")) {
+          state.relatedPartsList = props.fetchListAll?.fjbj;
+        } else if (val.includes("GDC")) {
+          state.relatedPartsList = props.fetchListAll?.gfbj;
+        } else {
+          state.relatedPartsList = [];
+        }
+      });
+    } else {
+      state.relatedPartsList = [];
+      state.modelList = [];
+    }
+  },
+  {
+    deep: true,
+  }
+);
+
+const isShow = ref(false);
+
+const form = ref({
+  id: "",
+  category: "windturbine", //报警类别
+  description: "", //描述
+  name: "", //规则名称
+  enable: true, //是否启用
+  expression: "", //表达式
+  range: "", //预警类型
+  rank: "", //报警级别
+  relatedParts: "", //所属部件
+  stationId: "", //场站id
+  deviceId: "",
+  electricalId: "",
+  lineId: "",
+  modelId: "",
+  projectId: "",
+  tag: "",
+  uniformCode: "",
+});
+// 场站列表/升压站列表
+const stationList = computed(() => {
+  if (form.value.category == "windturbine") {
+    return store.state.stationListAll;
+  } else {
+    return store.state.booststationList;
+  }
+});
+const toEmits = () => {
+  emits("close"); // 向父组件传递数据
+};
+const state = reactive({
+  modelList: [], //型号列表
+  relatedPartsList: [], //部件列表
+  AIPointList: [],
+  DIPointList: [],
+  AIPointSearch: "",
+  DIPointSearch: "",
+});
+const operator = [
+  "+",
+  "-",
+  "*",
+  "/",
+  "(",
+  ")",
+  ">",
+  ">=",
+  "<",
+  "<=",
+  "==",
+  "!=",
+  "&&",
+  "||",
+  "!",
+  "%",
+  "true",
+  "false",
+  ".",
+];
+const func = [
+  {
+    lab: "MR",
+    name: "移动极差",
+    param: "测点名,时间(秒)",
+    describe: "是指两个或多个连续样本值中最大值与最小值之差",
+    scene: "测点的移动极差超限报警",
+  },
+  {
+    lab: "MAR",
+    name: "均值极差",
+    param: "测点名,时间(秒)",
+    describe: "",
+    scene: "测点的均值极差计算",
+  },
+  {
+    lab: "RiseExceed",
+    name: "上升趋势",
+    param: "测点名,时间(秒),阈值",
+    describe: "取测点在给定的时间范围内数据上升的量是否超过阈值",
+    scene: "测点值的上升速度过快等",
+  },
+  {
+    lab: "Sustain",
+    name: "持续时间",
+    param: "表达式,时间(秒)",
+    describe:
+      "判定状态(表达式成立)持续的时间是否超过给定的时间判断状态持续的时间",
+    scene: "",
+  },
+  {
+    lab: "LastUpdateTime",
+    name: "最近数据时间",
+    param: "测点名",
+    describe: "",
+    scene: "判定离线,状态持续时间等",
+  },
+  {
+    lab: "abs",
+    name: "取绝对值",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "acos",
+    name: "反余弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "asin",
+    name: "反正弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "atan",
+    name: "反正切",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "atan2",
+    name: "xy坐标转为极坐标",
+    param: "x,y",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "ceiling",
+    name: "向上取整",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "cos",
+    name: "余弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "cosh",
+    name: "双曲线余弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "exp",
+    name: "欧拉数 e 的 double 次幂的值",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "floor",
+    name: "向下取整",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "log",
+    name: "自然对数",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "log10",
+    name: "底数为 10 的对数",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "max",
+    name: "比较最大值",
+    param: "double a, double b",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "min",
+    name: "比较最小值",
+    param: "double a, double b",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "pow",
+    name: "返回第一个参数的第二个参数次幂的值",
+    param: "double a, double b",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "round",
+    name: "返回最接近参数的 long,或int",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sign",
+    name: "负数返回-1.0,整数返回1.0,0返回0.0",
+    param: "float f/double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sin",
+    name: "三角正弦值",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sinh",
+    name: "双曲线正弦",
+    param: "double x",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sqrt",
+    name: "正平方根",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "tan",
+    name: "正切",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "tanh",
+    name: "双曲线正切",
+    param: "double x",
+    describe: "",
+    scene: "",
+  },
+  { lab: "PI", name: "圆周率", param: "", describe: "", scene: "" },
+  { lab: "E", name: "自然对数", param: "", describe: "", scene: "" },
+];
+const faultList = computed(() => props.fetchListAll.yj); //预警类型列表
+const ruleFormRef = ref(null);
+const rules = reactive({
+  name: [{ required: true, message: "请输入规则名称", trigger: "blur" }],
+  category: [{ required: true, message: "请选择报警类别", trigger: "change" }],
+  rank: [{ required: true, message: "请选择报警级别", trigger: "change" }],
+  stationId: [{ required: true, message: "请选择风场", trigger: "change" }],
+  expression: [
+    { required: true, message: "表达式不能为空", trigger: ["change"] },
+  ],
+});
+
+//categorychanged
+const categorychanged = async (val) => {
+  form.value.stationId = "";
+  form.modelId = "";
+  form.value.expression = "";
+  form.relatedParts = "";
+  state.AIPointList = [];
+  state.DIPointList = [];
+};
+//stationChange
+const stationChange = async (val) => {
+  if (form.value.category == "windturbine") {
+    form.value.modelId = "";
+    state.modelList = props.modelListAll[val];
+  } else {
+    getfetchPointListByBootst();
+  }
+};
+//modelIdChange
+const modelIdChange = async () => {
+  getfetchPointList();
+};
+// 根据场站和型号查询测点数据
+const getfetchPointList = async () => {
+  const { data } = await fetchPointList(
+    form.value.stationId,
+    form.value.modelId
+  );
+  if (Object.keys(data).length) {
+    state.AIPointList = data.ai.sort(function (a, b) {
+      return a.uniformCode - b.uniformCode;
+    });
+    state.DIPointList = data.di.sort(function (a, b) {
+      return a.uniformCode - b.uniformCode;
+    });
+  }
+};
+// 根据升压站查询测点数据
+const getfetchPointListByBootst = async () => {
+  const { data } = await fetchPointListByBootst(form.value.stationId);
+  if (Object.keys(data).length) {
+    state.AIPointList = data.ai.sort(function (a, b) {
+      return a.uniformCode - b.uniformCode;
+    });
+    state.DIPointList = data.di.sort(function (a, b) {
+      return a.uniformCode - b.uniformCode;
+    });
+  }
+};
+//筛选AI测点
+const filterAIList = computed(() =>
+  state.AIPointList?.filter(
+    (data) =>
+      !state.AIPointSearch ||
+      data.uniformCode.includes(state.AIPointSearch) ||
+      data.name.includes(state.AIPointSearch)
+  )
+);
+//筛选DI测点
+const filterDIList = computed(() =>
+  state.DIPointList?.filter(
+    (data) =>
+      !state.DIPointSearch ||
+      data.uniformCode.includes(state.DIPointSearch) ||
+      data.name.includes(state.DIPointSearch)
+  )
+);
+
+// 函数点击事件
+const tabFuncRowClickHandle = (row) => {
+  let elInput = document.getElementById("expressionInput");
+  let startPos = elInput.selectionStart; //第0个字符到选中的字符
+  let endPos = elInput.selectionEnd; //选中字符到末尾字符
+  if (startPos === undefined || endPos === undefined) return;
+  let txt = elInput.value;
+  let func;
+  if (
+    row.lab === "MR" ||
+    row.lab === "MAR" ||
+    row.lab === "RiseExceed" ||
+    row.lab === "Sustain" ||
+    row.lab === "LastUpdateTime"
+  ) {
+    func = row.lab + "()";
+  } else if (row.lab === "PI" || row.lab === "E") {
+    func = "Math." + row.lab;
+  } else {
+    func = "Math." + row.lab + "()";
+  }
+  // 将插值添加到选中光标位置
+  let result = txt.substring(0, startPos) + func + txt.substring(endPos);
+  elInput.value = result;
+  // 重新定义光标位置
+  elInput.focus();
+  if (row.lab === "PI" || row.lab === "E") {
+    elInput.selectionStart = startPos + func.length;
+    elInput.selectionEnd = startPos + func.length;
+  } else {
+    elInput.selectionStart = startPos + func.length - 1;
+    elInput.selectionEnd = startPos + func.length - 1;
+  }
+  form.value.expression = result; // 赋值给表单中的的字段
+};
+//rowDbclick
+const rowDbclick = (row) => {
+  elInputSplit(row);
+};
+// 表达式字符串拼接
+const elInputSplit = async (val) => {
+  let elInput = document.getElementById("expressionInput");
+  let startPos = elInput.selectionStart;
+  let endPos = elInput.selectionEnd;
+  if (startPos === undefined || endPos === undefined) return;
+  let txt = elInput.value;
+  let txtSplit = val.uniformCode || val;
+  let result = txt.substring(0, startPos) + txtSplit + txt.substring(endPos);
+  elInput.value = result;
+  elInput.focus();
+  elInput.selectionStart = startPos + txtSplit.length;
+  elInput.selectionEnd = startPos + txtSplit.length;
+  form.value.expression = result;
+};
+
+//提交
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      save();
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+//保存
+const save = async () => {
+  const res = await custombj_postSave(form.value);
+  console.warn(res);
+  if (res.code != 200) {
+    ElMessage.error(res.msg);
+  } else {
+    ElMessage.success(`保存成功!`);
+    closeDialog();
+  }
+};
+//reset
+const resetForm = (formEl) => {
+  formEl.resetFields();
+};
+//confirm关闭
+const handleClose = () => {
+  ElMessageBox.confirm("确认关闭?")
+    .then(() => {
+      closeDialog();
+    })
+    .catch(() => {
+      // catch error
+    });
+};
+//关闭触发事件
+const closeDialog = () => {
+  resetForm(ruleFormRef.value);
+  form.value = {};
+  state.AIPointList = [];
+  state.DIPointList = [];
+  toEmits();
+};
+</script>
+<style lang="scss" scoped>
+.col-box {
+  display: flex;
+  flex-direction: column;
+}
+
+.select-mini {
+  width: 120px;
+}
+
+.el-tabs__content {
+  padding: 0 !important;
+}
+
+.el-tabs--border-card {
+  -webkit-box-shadow: none;
+  box-shadow: none;
+}
+
+.border {
+  border: solid red 1px;
+}
+
+.el-table--mini td {
+  padding: 3px 0;
+}
+
+.el-button-group .el-button--primary {
+  border: none;
+}
+
+.el-form--label-top .el-form-item__label {
+  padding: 0;
+}
+</style>

+ 252 - 0
src/pages/alarmConfig/bj_scada/scada.vue

@@ -0,0 +1,252 @@
+<template>
+  <el-card>
+    <div class="row-box">
+      <div class="handle-box">
+        <el-button type="primary" class="mr10" @click="handleInsert">新增记录</el-button>
+        <el-button type="primary" class="mr10" @click="export2Excel">批量导出</el-button>
+        <el-button type="primary" class="mr10" @click="outExe">模板下载</el-button>
+        <el-upload class="handle-edit mr10" style=" display: inline;" action="/sharding/scadabj/import"
+          :headers="token" :show-file-list="false" :on-success="handleSuccess" :on-progress="handleProgress"
+          :on-error="handleError">
+          <el-button type="primary" icon="el-icon-lx-top" @click="exportShow = true">
+            批量导入</el-button>
+        </el-upload>
+      </div>
+      <div class="handle-box">
+        <el-select v-if="!isStation" v-model="query.stationId" placeholder="场站" class="handle-select mr10"
+          style="width: 150px" clearable>
+          <el-option v-for="item in stationListAll" :key="item.id" :value="item.id" :label="item.name"></el-option>
+        </el-select>
+        <el-input v-model="query.description" placeholder="描述" class="handle-input mr10" clearable></el-input>
+        <el-input v-model="query.pointKey" placeholder="测点" class="handle-input mr10" clearable></el-input>
+        <el-select v-model="query.enable" placeholder="是否可用" class="handle-select mr10" clearable>
+          <el-option key="1" label="启用" value="1" />
+          <el-option key="2" label="停用" value="0" />
+        </el-select>
+        <el-button type="primary" icon="el-icon-search" @click="getData">搜索</el-button>
+      </div>
+    </div>
+    <el-table :data="state.tableData" border class="table" header-cell-name="table-header"
+      :highlight-current-row="true">
+      <el-table-column prop="pointKey" label="测点" align="center" min-width="200px" />
+      <el-table-column prop="description" label="描述" align="left" min-width="120px">
+      </el-table-column>
+      <el-table-column label="报警类型" align="center" min-width="50">
+        <template #default="scope">{{
+            category1Convert(scope.row.category1)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="场站" prop="stationName" align="center" />
+      <el-table-column label="报警级别" align="center" min-width="40">
+        <template #default="scope">{{
+            rankConvert(scope.row.rank)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="报警逻辑" align="center" min-width="70">
+        <template #default="scope">{{
+            alarmTypeConvert(scope.row.alarmType)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="是否启用" align="center" min-width="50" prop="enabled">
+        <template #default="scope">
+          <span>{{ enabledConvert(scope.row.enabled) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="100">
+        <template #default="scope">
+          <el-button type="primary" plain round size="mini" icon="el-icon-lx-edit"
+            :disabled="notEdit || scope.row.range == 1" @click="handleEditClick(scope.row)">编辑</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination background layout="total, prev, pager, next" hide-on-single-page :current-page="query.pagenum"
+        :page-size="query.pagesize" :total="total" @current-change="handlePageChange"></el-pagination>
+    </div>
+    <scadacomponents @close="dialogclose" :isVisible="state.visible" :form="state.form" />
+  </el-card>
+</template>
+<script setup>
+import { scadabj_fetchTableData } from "/@/api/api.js";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+import { ref, onMounted, provide, computed, reactive, watch } from "vue";
+import { useStore } from "vuex";
+import { ElMessage } from "element-plus";
+import scadacomponents from "./scada_components.vue";
+import { common } from "/@/composables/common";
+const { notEdit } = common();
+const token = { 'token': sessionStorage.getItem('token') }
+const store = useStore();
+const stationListAll = computed(() => store.state.stationListAll);
+const isStation = computed(() => store.getters.isStation);
+const state = reactive({
+  tableData: [],
+  visible: false,
+  form: {},
+});
+const query = reactive({
+  pagenum: 1,
+  pagesize: 15,
+  stationId: "",
+  description: "",
+  pointKey: "",
+  enable: "",
+});
+let total = ref(0);
+onMounted(() => {
+  getData();
+});
+const dialogclose = () => {
+  state.visible = false
+  getData()
+}
+const getData = async () => {
+  const res = await scadabj_fetchTableData(query);
+  console.warn(res);
+  state.tableData = res.records;
+  total.value = res.total;
+};
+const handleEditClick = (row) => {
+  console.warn(row);
+  let obj = Object.assign({},row)
+  state.form = obj;
+  state.visible = true;
+};
+const handleInsert = () => {
+  state.visible = true;
+};
+
+
+// 分页导航
+const handlePageChange = (val) => {
+  query.pagenum = val;
+  getData();
+};
+const tableHeader = [
+  "id",
+  "场站(必填)",
+  "测点(必填)",
+  "描述",
+  "报警类型(必填)",
+  "类别2",
+  "类别3",
+  "类别4",
+  "报警级别(必填——1:低;2:中低;3:中;4:中高;5:高)",
+  "报警逻辑(必填——0:【0触发,1解除】;1:【1触发,0解除】;2:【变化触发】)",
+  "是否可用(必填——1:启用;0:停用)",
+  "设备编号",
+];
+
+const tableKey = [
+  "id",
+  "stationId",
+  "pointKey",
+  "description",
+  "category1",
+  "category2",
+  "category3",
+  "category4",
+  "rank",
+  "alarmType",
+  "enabled",
+  "deviceId",
+];
+// 批量导出
+const export2Excel = async () => {
+  let { stationId, description, pointKey, enable } = query;
+  const res = await scadabj_fetchTableData({
+    pagenum: 1,
+    pagesize: 999999,
+    stationId,
+    description,
+    pointKey,
+    enable,
+  });
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, res.records, "升压站报警导出excel");
+};
+// 模板下载
+const outExe = () => {
+  const data = [];
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, data, "升压站报警模板");
+};
+// 批量导入
+const handleSuccess = (response, file, fileList) => {
+  ElMessage.success("导入成功!");
+  getData();
+};
+const handleProgress = (response, file, fileList) => { };
+const handleError = (response, file, fileList) => {
+  ElMessage.success("导入失败!");
+};
+const rankobj = {
+  '1': '低',
+  '2': '中低',
+  '3': '中',
+  '4': '中高',
+  '5': '高',
+}
+const rankConvert = (val) => {
+  return rankobj[val]
+};
+
+// 状态
+const enabledConvert = (val) => {
+  return val == 0 ? "停用" : "启用"
+};
+const category1Convert = (val) => {
+  return val == "SYZ" ? "升压站" : "光伏"
+};
+const alarmTypeConvert = (alarmType) => {
+  if (alarmType === 0) {
+    return "0触发,1解除";
+  } else if (alarmType === 1) {
+    return "1触发,0解除";
+  } else if (alarmType === 2) {
+    return "变化触发";
+  }
+};
+
+
+</script>
+<style scoped>
+.handle-box {
+  margin-bottom: 10px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 200px;
+  display: inline-block;
+}
+
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+
+.red {
+  color: #ff0000;
+}
+
+.mr10 {
+  margin-right: 10px;
+}
+
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+
+.row-box {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+</style>

+ 197 - 0
src/pages/alarmConfig/bj_scada/scada_components.vue

@@ -0,0 +1,197 @@
+<template>
+  <el-dialog v-model="isShow" width="500px" :before-close="handleClose">
+    <el-form ref="ruleFormRef" :model="form" :rules="rules" label-position="right" label-width="80px">
+      <el-form-item label="场站" prop="stationId">
+        <el-select v-model="form.stationId" placeholder="场站" class="el-component" @change="stationChange">
+          <el-option v-for="item in stationListAll" :key="item.id" :value="item.id" :label="item.name"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="报警类型" prop="category1">
+        <el-select v-model="form.category1" placeholder="报警类型" class="el-component">
+          <el-option key="1" label="升压站" value="SYZ"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="测点名称" prop="pointKey">
+        <!-- <el-select-v2 v-model="form.pointKey" filterable :options="pointList" placeholder="Please select"
+          style="width: 380px" /> -->
+        <el-select v-model="form.pointKey" class="m-2" placeholder="Select" size="large"  style="width: 380px">
+          <el-option v-for="item in pointList" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+
+      </el-form-item>
+      <el-form-item label="描述" prop="description">
+        <el-input v-model="form.description" type="textarea" rows="3" />
+      </el-form-item>
+
+      <el-form-item label="报警级别" prop="rank">
+        <el-select v-model="form.rank" placeholder="报警级别" class="el-component">
+          <el-option key="1" label="低" value="1" />
+          <el-option key="2" label="中低" value="2" />
+          <el-option key="3" label="中" value="3" />
+          <el-option key="4" label="中高" value="4" />
+          <el-option key="5" label="高" value="5" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="报警逻辑" prop="alarmType">
+        <el-select v-model="form.alarmType" placeholder="报警逻辑" class="el-component">
+          <el-option key="1" label="0触发,1解除" :value="0" />
+          <el-option key="2" label="1触发,0解除" :value="1" />
+          <el-option key="3" label="变化触发" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="是否启用" prop="enabled">
+        <el-select v-model="form.enabled" placeholder="是否可用" class="el-component">
+          <el-option key="1" label="启用" :value="1" />
+          <el-option key="2" label="停用" :value="0" />
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="closeDialog">取 消</el-button>
+        <el-button type="primary" @click="submitForm(ruleFormRef)">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, onMounted, reactive, computed, watch, nextTick } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import {
+  fetchElectricDIPointList,
+  scadabj_postSave
+} from "/@/api/api.js";
+import { useStore } from "vuex";
+const store = useStore();
+const stationListAll = computed(() => store.state.stationListAll);
+watch(
+  () => props.isVisible,
+  (val, old) => {
+    isShow.value = val
+  },
+  {
+    deep: true,
+  },
+);
+watch(
+  () => props.form?.id,
+  (val, old) => {
+    if (val != "") {
+      nextTick(async () => {
+        stationChange()
+      });
+    }
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => props.form,
+  (val, old) => {
+    nextTick(() => {
+      form.value = val
+    });
+  },
+  {
+    deep: true,
+  }
+)
+const isShow=ref(false);
+const form = ref({
+  id: "",
+  stationId: "",
+  pointKey: "",
+  description: "",
+  category1: "SYZ",
+  rank: "",
+  alarmType: "",
+  enabled: "",
+  userName: "",
+})
+const emits = defineEmits(["close"]);
+const props = defineProps({
+  isVisible: {
+    type: Boolean,
+    defaule: false,
+  },
+  form: {
+    type: Object,
+  },
+});
+const toEmits = () => {
+  emits("close"); // 向父组件传递数据
+};
+const pointList = ref([])
+const ruleFormRef = ref(null);
+const rules = reactive({
+  stationId: [
+    { required: true, message: "请选择场站", trigger: "change" },
+  ],
+  pointKey: [{ required: true, message: "请输入测点", trigger: "blur" }],
+  description: [
+    { required: true, message: "请输入描述", trigger: "blur" },
+  ],
+  category1: [
+    { required: true, message: "请选择报警类型", trigger: "change" },
+  ],
+  rank: [{ required: true, message: "请选择级别", trigger: "change" }],
+  alarmType: [
+    { required: true, message: "请选择报警逻辑", trigger: "change" },
+  ],
+  enabled: [
+    { required: true, message: "请选择是否可用", trigger: "change" },
+  ],
+});
+const stationChange = async () => {
+  const res = await fetchElectricDIPointList(form.value?.stationId);
+  res.forEach(e => {
+    e['value'] = e.id
+    e['label'] = e.name
+  })
+  pointList.value = res
+};
+
+//保存
+const save = async () => {
+  const res = await scadabj_postSave(form.value);
+  console.warn(res);
+  ElMessage.success(`保存成功!`);
+  closeDialog()
+};
+//提交
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      save()
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+//reset
+const resetForm = (formEl) => {
+  formEl.resetFields();
+};
+//confirm关闭
+const handleClose = () => {
+  ElMessageBox.confirm("确认关闭?")
+    .then(() => {
+      closeDialog();
+    })
+    .catch(() => {
+      // catch error
+    });
+};
+//关闭触发事件
+const closeDialog = () => {
+  resetForm(ruleFormRef.value);
+  toEmits();
+};
+</script>
+<style lang="scss" scoped>
+.el-component {
+  width: 100%;
+}
+</style>

+ 646 - 0
src/pages/alarmConfig/bj_windturbine/windturbine.vue

@@ -0,0 +1,646 @@
+<template>
+  <el-card>
+    <el-row type="flex" justify="space-between">
+      <div class="handle-box" style="margin-bottom: 10px">
+        <el-button
+          type="primary"
+          icon="el-icon-lx-add"
+          class="handle-edit mr10"
+          @click="handleInsert"
+        >
+          新增记录
+        </el-button>
+        <el-button
+          type="primary"
+          icon="el-icon-lx-down"
+          class="handle-edit mr10"
+          @click="export2Excel"
+        >
+          批量导出</el-button
+        >
+        <el-button
+          type="primary"
+          icon="el-icon-lx-down"
+          class="handle-edit mr10"
+          @click="outExe"
+        >
+          模板下载</el-button
+        >
+        <el-upload
+          class="handle-edit mr10"
+          style="display: inline"
+          action="/sharding/warning2/import"
+          :headers="token"
+          :show-file-list="false"
+          :on-success="handleSuccess"
+          :on-progress="handleProgress"
+          :on-error="handleError"
+        >
+          <el-button
+            type="primary"
+            icon="el-icon-lx-top"
+            @click="exportShow = true"
+          >
+            批量导入</el-button
+          >
+        </el-upload>
+      </div>
+      <div style="display: flex; flex-direction: row; margin-bottom: 10px">
+        <el-select
+          v-if="!isStation"
+          v-model="query.alarmType"
+          class="mr10"
+          style="width: 150px"
+          popper-class="select"
+          @change="categorychanged"
+        >
+          <el-option key="1" label="设备报警" value="windturbine"></el-option>
+          <el-option
+            key="2"
+            label="升压站报警"
+            value="booststation"
+          ></el-option>
+        </el-select>
+        <el-select
+          v-model="query.wpId"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          :placeholder="
+            query.alarmType == 'windturbine' ? '全部场站' : '全部升压站'
+          "
+          popper-class="select"
+          @change="changeStation"
+        >
+          <el-option
+            v-for="item in stationList"
+            :key="item.id"
+            :value="item.id"
+            :label="item.aname"
+          ></el-option>
+        </el-select>
+        <el-select
+          v-if="query.alarmType == 'windturbine'"
+          v-model="query.modelId"
+          clearable
+          class="mr10"
+          style="width: 150px"
+          placeholder="全部机型"
+          popper-class="select"
+        >
+          <el-option
+            v-for="item in modelList"
+            :key="item.id"
+            :value="item.id"
+            :label="item.aname"
+          ></el-option>
+        </el-select>
+        <el-input
+          placeholder="请输入名称"
+          v-model="query.name"
+          style="width: 150px"
+          class="mr10"
+          clearable
+        ></el-input>
+        <el-button icon="el-icon-search" type="primary" @click="getData"
+          >搜索</el-button
+        >
+      </div>
+    </el-row>
+    <el-table :data="state.tableData" :highlight-current-row="true" border>
+      <el-table-column
+        v-for="item in query.alarmType == 'windturbine'
+          ? state.tableHeader
+          : state.tableHeader1"
+        :key="item.code"
+        :label="item.title"
+        align="center"
+        :prop="item.code"
+        show-overflow-tooltip
+      >
+        <template #default="scope">
+          <span v-if="item.code == 'rank'">
+            {{ rankConvert(scope.row.rank) }}
+          </span>
+          <span v-else-if="item.code == 'alarmType'">
+            {{ alarmTypeConvert(scope.row.alarmType) }}
+          </span>
+          <span v-else-if="item.code == 'deviceType'">
+            {{ alarmTypeConvert(scope.row.deviceType) }}
+          </span>
+          <span v-else-if="item.code == 'enable'">
+            {{ enabledConvert(scope.row.enable) }}
+          </span>
+          <span v-else-if="item.code == 'characteristic'">
+            {{ charactConvert(scope.row.characteristic) }}
+          </span>
+          <span v-else>
+            {{ scope.row[item.code] }}
+          </span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="序列号" align="center" prop="id" />
+      <el-table-column label="统一编码" align="center" prop="uniformCode">
+        <template #default="scope">{{
+          // ednaValueConvert(scope.row.ednaValue)
+          scope.row.uniformCode
+        }}</template>
+      </el-table-column>
+      <el-table-column
+        label="名称"
+        align="left"
+        prop="chineseText"
+        width="180"
+      />
+      <el-table-column label="停机类型" align="center" prop="characteristic" />
+      <el-table-column label="设备型号" align="center" prop="modelId" />
+      <el-table-column label="报警类型" align="center" prop="warningTypeId" />
+      <el-table-column label="故障编码" align="center" prop="faultCode" />
+      <el-table-column label="报警级别" align="center" prop="levelId">
+        <template #default="scope">{{
+          levelIdConvert(scope.row.levelId)
+        }}</template>
+      </el-table-column>
+      <el-table-column
+        label="报警分类"
+        align="center"
+        prop="warningClassIfyId"
+        min-width="50"
+      >
+        <template #default="scope">{{
+          warningClassIfyIdConvert(scope.row.warningClassIfyId)
+        }}</template>
+      </el-table-column>
+      <el-table-column
+        label="是否可以复位"
+        align="center"
+        prop="isreset"
+        min-width="50"
+      >
+        <template #default="scope">{{
+          isresetConvert(scope.row.isreset)
+        }}</template>
+      </el-table-column>
+      <el-table-column
+        label="是否启用"
+        align="center"
+        prop="display"
+        min-width="50"
+      >
+        <template #default="scope">{{
+          scope.row.enabled == 1 ? "是" : "否"
+        }}</template>
+      </el-table-column>
+      <el-table-column
+        label="关联部件"
+        align="center"
+        prop="relatedParts"
+        min-width="50"
+      >
+        <template #default="scope">
+          <span>{{ relatePartConvert(scope.row.relatedParts) }}</span>
+        </template>
+      </el-table-column> -->
+      <el-table-column label="操作" align="center" width="100">
+        <template #default="scope">
+          <el-button
+            type="primary"
+            plain
+            round
+            size="mini"
+            icon="el-icon-lx-edit"
+            @click="handleEditClick(scope.row)"
+          >
+            编辑</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination
+        background
+        layout="total, prev, pager, next"
+        hide-on-single-page
+        :current-page="query.pagenum"
+        :page-size="query.pagesize"
+        :total="total"
+        @current-change="handlePageChange"
+      ></el-pagination>
+    </div>
+    <windturbinecomponents
+      @close="dialogclose"
+      :isVisible="state.visible"
+      :form="state.form"
+    />
+  </el-card>
+</template>
+<script setup>
+import {
+  windturbinebj_fetchTableData,
+  getStationinfo,
+  fetchRelatePartAndAlarmType,
+  fetchModel,
+} from "/@/api/api.js";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+import {
+  ref,
+  onMounted,
+  provide,
+  computed,
+  reactive,
+  watch,
+  nextTick,
+} from "vue";
+import { useStore } from "vuex";
+import { ElMessageBox, ElMessage } from "element-plus";
+import windturbinecomponents from "./windturbine_components.vue";
+const token = { token: sessionStorage.getItem("token") };
+const store = useStore();
+onMounted(() => {
+  getequipmentmodel_list();
+});
+const query = reactive({
+  pagenum: 1,
+  pagesize: 10,
+  wpId: "",
+  modelId: "",
+  name: "",
+  alarmType: "windturbine",
+});
+const state = reactive({
+  tableData: [],
+  modelList: [],
+  modelListAll: {}, //型号所有列表
+  visible: false,
+  form: {},
+  tableHeader: [
+    { title: "编码", code: "id" },
+    { title: "场站", code: "stationId", width: "100" },
+    { title: "设备", code: "deviceId", width: "100" },
+    { title: "机型", code: "modelId" },
+    { title: "部件", code: "components" },
+    { title: "报警描述", code: "description", width: "150" },
+    { title: "设备部件", code: "components" },
+    { title: "级别", code: "rank" },
+    { title: "特性", code: "characteristic" },
+    { title: "设备类型", code: "deviceType" },
+    { title: "报警类型", code: "alarmType" },
+    { title: "报警类别", code: "triggerType" },
+    { title: "是否启用", code: "enable" },
+    { title: "是否可复位", code: "resetTable" },
+  ],
+  tableHeader1: [
+    { title: "编码", code: "id" },
+    { title: "升压站", code: "stationName" },
+    { title: "规则名称", code: "name" },
+    { title: "表达式", code: "expression" },
+    { title: "描述", code: "description" },
+    { title: "级别", code: "rank" },
+    { title: "类型", code: "alarmType" },
+    { title: "是否启用", code: "enable" },
+  ],
+});
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await fetchModel();
+  state.modelListAll = data;
+};
+// 场站列表/升压站列表
+const stationList = computed(() => {
+  if (query.alarmType == "windturbine") {
+    return store.state.stationListAll;
+  } else {
+    return store.state.booststationList;
+  }
+});
+watch(
+  () => stationList,
+  (val, old) => {
+    val.value.length &&
+      nextTick(async () => {
+        query.wpId = val.value[0]?.id;
+        await getData();
+      });
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+//型号列表
+const modelList = computed(() => {
+  if (query.wpId == "") {
+    return [];
+  } else {
+    return state.modelListAll[query.wpId];
+  }
+});
+const isStation = computed(() => store.getters.isStation);
+
+let total = ref(0);
+const getData = async () => {
+  const { data: res } = await windturbinebj_fetchTableData(query);
+  state.tableData = res.records;
+  total.value = res.total;
+};
+//changeStation
+const changeStation = async () => {
+  query.modelId = "";
+  getData();
+};
+const dialogclose = () => {
+  state.visible = false;
+  getData();
+};
+
+const handleInsert = () => {
+  state.visible = true;
+};
+const handleEditClick = (row) => {
+  let obj = Object.assign({}, row);
+  obj && (obj.manufacturerCode = "");
+  state.form = obj;
+  state.visible = true;
+};
+
+// 分页导航
+const handlePageChange = (val) => {
+  query.pagenum = val;
+  getData();
+};
+
+const tableHeader = [
+  "id",
+  "报警级别(ZC_BJ:运转正常;YJ_BJ:运转状态超出标准范围;GZ_BJ:非正常停止运转;WH_BJ:停机维护;XD_BJ:限电/计划停机)",
+  "制造商(HFYG_CS:合肥阳光电源有限公司;AMS_CS:艾默生;NRJD_CS:国电南瑞吉电新能源;MGSK_CS:美国赛康;LHDL_CS:联合动力;SYHC_CS:沈阳华创)",
+  "名称",
+  "上一级节点",
+  "是否有子节点",
+  "序列号",
+  "类别 :正常停机,正常启动,快速停机,紧急停机",
+  "设备型号",
+  "全部默认为1",
+  "统一编码",
+  "是否展示(0:是;1:否)",
+  "报警分类(bj:变桨;bpq:变频器;clx:齿轮箱;fdj:发电机;hh:滑环;jcjr:机舱加热;lq:冷却;ph:偏航;rh:润滑;yy:液压;zz:主轴;other:其它;kz:控制;yl:叶轮;jc:机舱;xb:箱变;dw:电网;td:塔底;fsy:风速仪)",
+  "消缺规定时间",
+  "是否可以复位(0:是;1:否)",
+  "报警类型编号",
+  "故障编码",
+  "关联部件",
+];
+
+const tableKey = [
+  "id",
+  "levelId",
+  "manufacturerCode",
+  "chineseText",
+  "parentId",
+  "isleaf",
+  "sequenceNumber",
+  "characteristic",
+  "modelId",
+  "codeName",
+  "ednaValue",
+  "display",
+  "warningClassIfyId",
+  "standardTime",
+  "isreset",
+  "warningTypeId",
+  "faultCode",
+  "relatedParts",
+];
+// 批量导出
+const export2Excel = async () => {
+  let { name, wpId, modelId } = query;
+  const res = await windturbinebj_fetchTableData({
+    pagenum: 1,
+    pagesize: 999999,
+    name,
+    wpId,
+    modelId,
+  });
+
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, res.records, "设备报警配置导出excel");
+};
+// 模板下载
+const outExe = () => {
+  const data = [];
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, data, "设备报警模板");
+};
+// 批量导入
+const handleSuccess = (response, file, fileList) => {
+  ElMessage.success("导入成功!");
+  getData();
+};
+const handleProgress = (response, file, fileList) => {};
+const handleError = (response, file, fileList) => {
+  ElMessage.success("导入失败!");
+};
+//级别
+const rankConvert = (val) => {
+  if (val == 1) {
+    return "低";
+  } else if (val == 2) {
+    return "中低";
+  } else if (val == 3) {
+    return "中";
+  } else if (val == 4) {
+    return "中高";
+  } else if (val == 5) {
+    return "高";
+  }
+};
+// 类型
+const alarmTypeConvert = (val) => {
+  if (val === "windturbine") {
+    return "设备";
+  } else if (val === "booststation") {
+    return "升压站";
+  }
+};
+// 状态
+const enabledConvert = (val) => {
+  if (val === false) {
+    return "停用";
+  } else if (val === true) {
+    return "启用";
+  }
+};
+
+// 特征
+const charactConvert = (val) => {
+  switch (val) {
+    case "ZC_BJ":
+      return "运转正常";
+      break;
+    case "YJ_BJ":
+      return "运转状态超出标准范围";
+      break;
+    case "GZ_BJ":
+      return "非正常停止运转";
+      break;
+    case "WH_BJ":
+      return "停机维护";
+      break;
+    case "XD_BJ":
+      return "限电/计划停机";
+      break;
+    default:
+      return;
+  }
+};
+// 报警分类
+const warningClassIfyIdConvert = (val) => {
+  switch (val) {
+    case "bj":
+      return "变桨";
+      break;
+    case "bpq":
+      return "变频器";
+      break;
+    case "clx":
+      return "齿轮箱";
+      break;
+    case "fdj":
+      return "发电机";
+      break;
+    case "hh":
+      return "滑环";
+      break;
+    case "jcjr":
+      return "机舱加热";
+      break;
+    case "lq":
+      return "冷却";
+      break;
+    case "ph":
+      return "偏航";
+      break;
+    case "rh":
+      return "润滑";
+      break;
+    case "yy":
+      return "液压";
+      break;
+    case "zz":
+      return "主轴";
+      break;
+    case "other":
+      return "其它";
+      break;
+    case "kz":
+      return "控制";
+      break;
+    case "yl":
+      return "叶轮";
+      break;
+    case "jc":
+      return "机舱";
+      break;
+    case "xb":
+      return "箱变";
+      break;
+    case "dw":
+      return "电网";
+      break;
+    case "td":
+      return "塔底";
+      break;
+    case "fsy":
+      return "风速仪";
+      break;
+  }
+};
+// 是否可以复位
+const isresetConvert = (val) => {
+  switch (val) {
+    case 0:
+      return "是";
+      break;
+    case 1:
+      return "否";
+      break;
+  }
+};
+
+// 统一编码
+const ednaValueConvert = (val) => {
+  if (val === 0) {
+    return "";
+  } else if (val < 100 && val > 0) {
+    return "DI0" + val;
+  } else if (val >= 100) {
+    return "DI" + val;
+  }
+};
+// 关联部件
+const relatePartConvert = (val) => {
+  switch (val) {
+    case "YP":
+      return "叶片";
+      break;
+    case "LG":
+      return "轮毂";
+      break;
+    case "TZ":
+      return "塔架";
+      break;
+    case "JC":
+      return "机舱";
+      break;
+    case "KZXT":
+      return "控制系统";
+      break;
+    case "BJXT":
+      return "变桨系统";
+      break;
+    case "PHXT":
+      return "偏航系统";
+      break;
+    case "CLX":
+      return "齿轮箱";
+      break;
+    case "FDJ":
+      return "发电机";
+      return;
+    case "BPQ":
+      return "变频器";
+      return;
+    case "YYXT":
+      return "液压系统";
+      break;
+    case "FZXT":
+      return "辅助系统";
+      break;
+    case "CFXT":
+      return "测风系统";
+      break;
+    case "DWXT":
+      return "电网系统";
+      break;
+    case "TDG":
+      return "塔底柜";
+      break;
+    case "CDL":
+      return "传动链";
+      break;
+    case "QT":
+      return "其他";
+      break;
+    default:
+      break;
+  }
+};
+</script>
+<style scoped>
+.mr10 {
+  margin-right: 10px;
+}
+
+.el-button + .el-button {
+  margin-left: 10px;
+}
+</style>

+ 390 - 0
src/pages/alarmConfig/bj_windturbine/windturbine_components.vue

@@ -0,0 +1,390 @@
+<template>
+  <el-dialog v-model="isShow" width="800px" :before-close="handleClose">
+    <el-form
+      ref="ruleFormRef"
+      :model="form"
+      :rules="rules"
+      label-position="right"
+      label-width="100px"
+    >
+      <el-row :gutter="50">
+        <el-col :span="12">
+          <el-form-item label="序列号" prop="id">
+            <el-input v-model="form.id" disabled />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="制造商" prop="manufacturerCode">
+            <el-select class="width-100" v-model="form.manufacturerCode">
+              <el-option
+                v-for="item in state.creator"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="50">
+        <el-col :span="12">
+          <el-form-item label="风机型号" prop="modelId">
+            <el-select
+              class="width-100"
+              v-model="form.modelId"
+              @change="modelChange"
+            >
+              <el-option
+                v-for="item in state.modelList"
+                :key="item"
+                :value="item"
+                :label="item"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="风机测点" prop="uniformCode">
+            <el-select
+              v-model="form.uniformCode"
+              class="width-100"
+              filterable
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in state.pointList"
+                :key="item.id"
+                :label="item.uniformCode + '    ' + item.name"
+                :value="item.uniformCode"
+              >
+                <span style="float: left">{{ item.name }}</span>
+                <span style="float: right; color: #8492a6; font-size: 13px">{{
+                  item.uniformCode
+                }}</span>
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="50">
+        <el-col :span="12">
+          <el-form-item label="停机类型" prop="characteristic">
+            <el-select class="width-100" v-model="form.characteristic">
+              <el-option
+                v-for="item in state.CHARACTERISTIC"
+                :key="item"
+                :label="item"
+                :value="item"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="故障编码" prop="faultCode">
+            <el-input v-model="form.faultCode" :disabled="isEdit" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="50">
+        <el-col :span="12">
+          <el-form-item label="描述" prop="chineseText">
+            <el-input v-model="form.chineseText" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="报警级别" prop="levelId">
+            <el-select class="width-100" v-model="form.levelId">
+              <el-option
+                v-for="item in state.LEVELID"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="50">
+        <el-col :span="12">
+          <el-form-item label="报警分类" prop="warningClassIfyId">
+            <el-select class="width-100" v-model="form.warningClassIfyId">
+              <el-option
+                v-for="item in state.warningClassifyList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <!-- <el-col :span="12">
+          <el-form-item label="报警类型" prop="warningTypeId">
+            <el-select class="width-100" v-model="form.warningTypeId">
+              <el-option v-for="item in warningTypeList" :key="item.id" :value="item.id" :label="item.name" />
+            </el-select>
+          </el-form-item>
+        </el-col> -->
+        <el-col :span="12">
+          <el-form-item label="是否启用" prop="enabled">
+            <el-radio-group v-model="form.enabled">
+              <el-radio-button label="1">是</el-radio-button>
+              <el-radio-button label="0">否</el-radio-button>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="50">
+        <el-col :span="12">
+          <el-form-item label="关联部件" prop="relatedParts">
+            <el-select class="width-100" v-model="form.relatedParts">
+              <el-option
+                v-for="item in state.relatePartList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="是否可以复位" prop="isreset">
+            <el-radio-group v-model="form.isreset">
+              <el-radio-button label="0">是</el-radio-button>
+              <el-radio-button label="1">否</el-radio-button>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="50">
+        <el-col :span="12"></el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="closeDialog">取 消</el-button>
+        <el-button type="primary" @click="submitForm(ruleFormRef)"
+          >确 定</el-button
+        >
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, onMounted, reactive, computed, watch, nextTick } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import {
+  //   fetchRelatePart,
+  windturbinebj_postSave,
+  fetchRelatePartAndAlarmType,
+  getStationinfo,
+  fetchWarningClassify,
+} from "/@/api/api.js";
+import { useStore } from "vuex";
+const store = useStore();
+const stationList = computed(() => store.state.stationList);
+onMounted(() => {
+  getfetchRelatePart();
+  getdatadictionaryList();
+  getfetchWarningClassify();
+});
+watch(
+  () => props.isVisible,
+  (val, old) => {
+    isShow.value = val;
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => props.form?.id,
+  (val, old) => {
+    if (val != "") {
+      nextTick(async () => {
+        modelChange();
+      });
+    }
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => props.form,
+  (val, old) => {
+    nextTick(() => {
+      form.value = val;
+    });
+  },
+  {
+    deep: true,
+  }
+);
+const isShow = ref(false);
+const form = ref({
+  id: "",
+  levelId: "",
+  manufacturerCode: "",
+  chineseText: "",
+  sequenceNumber: "",
+  characteristic: "",
+  modelId: "",
+  codeName: "1",
+  uniformCode: "",
+  enabled: "0",
+  warningClassIfyId: "",
+  standardTime: 2,
+  isreset: "1",
+  warningTypeId: "",
+  faultCode: "",
+  relatedParts: "",
+  userName: "",
+});
+const emits = defineEmits(["close"]);
+const props = defineProps({
+  isVisible: {
+    type: Boolean,
+    defaule: false,
+  },
+  form: {
+    type: Object,
+  },
+});
+const toEmits = () => {
+  emits("close"); // 向父组件传递数据
+};
+const state = reactive({
+  modelList: [],
+  pointList: [],
+  fetchListAll: [],
+//   relatePartList: [],
+  warningClassifyList: [],
+  creator: [
+    {
+      id: "HFYG_CS",
+      name: "合肥阳光电源有限公司",
+      country: "中国",
+      place: "合肥",
+    },
+    { id: "AMS_CS", name: "艾默生", country: "", place: "" },
+    {
+      id: "NRJD_CS",
+      name: "国电南瑞吉电新能源",
+      country: "中国",
+      place: "",
+    },
+    { id: "MGSK_CS", name: "美国赛康", country: "美国", place: "" },
+    { id: "LHDL_CS", name: "联合动力", country: "中国", place: "保定" },
+    { id: "SYHC_CS", name: "沈阳华创", country: "中国", place: "沈阳" },
+  ],
+  LEVELID: [
+    { id: "ZC_BJ", name: "运转正常" },
+    { id: "YJ_BJ", name: "运转状态超出标准范围" },
+    { id: "GZ_BJ", name: "非正常停止运转" },
+    { id: "WH_BJ", name: "停机维护" },
+    { id: "XD_BJ", name: "限电/计划停机" },
+  ],
+  CHARACTERISTIC: ["正常停机", "正常启动", "快速停机", "紧急停机"],
+});
+const ruleFormRef = ref(null);
+const rules = reactive({
+  manufacturerCode: {
+    required: true,
+    message: "请选择制造商",
+    trigger: "change",
+  },
+  uniformCode: {
+    required: true,
+    message: "请输入统一编码",
+    trigger: "trigger",
+  },
+  characteristic: {
+    required: true,
+    message: "请选择类别",
+    trigger: "trigger",
+  },
+  modelId: {
+    required: true,
+    message: "请选择风机型号",
+    trigger: "change",
+  },
+});
+//部件列表
+const relatePartList = computed(() => {
+//   if (query.wpId == "") {
+//     return [];
+//   } else {
+//     if (query.wpId.includes("FDC")) {
+//       return state.fetchListAll?.fjbj;
+//     } else {
+//       return state.fetchListAll?.gfbj;
+//     }
+//   }
+});
+const modelChange = async () => {
+  const { data } = await getDIPointList(form.value?.modelId);
+  state.pointList = data;
+};
+//机型
+const getdatadictionaryList = async () => {
+  const { data } = await getStationinfo("");
+  let arr = [];
+  data.forEach((e) => {
+    arr.push(...e.pointModelList);
+  });
+  state.modelList = [...new Set(arr)];
+};
+//所属部件
+const getfetchRelatePart = async () => {
+  const res = await fetchRelatePartAndAlarmType();
+  state.relatePartList = res;
+};
+const getfetchWarningClassify = async () => {
+  const res = await fetchWarningClassify();
+  state.warningClassifyList = res;
+};
+
+//保存
+const save = async () => {
+  const res = await windturbinebj_postSave(form.value);
+  console.warn(res);
+  ElMessage.success(`保存成功!`);
+  closeDialog();
+};
+//提交
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      save();
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+//reset
+const resetForm = (formEl) => {
+  if (!formEl) return;
+  formEl.resetFields();
+};
+//confirm关闭
+const handleClose = () => {
+  ElMessageBox.confirm("确认关闭?")
+    .then(() => {
+      closeDialog();
+    })
+    .catch(() => {
+      // catch error
+    });
+};
+//关闭触发事件
+const closeDialog = () => {
+  resetForm(ruleFormRef.value);
+  toEmits();
+};
+</script>
+<style scoped>
+.width-100 {
+  width: 100%;
+}
+</style>

+ 3 - 0
src/pages/alarmConfig/inverter.vue

@@ -0,0 +1,3 @@
+<template>
+    <div class="cl"></div>
+</template>

+ 3 - 0
src/pages/alarmConfig/station.vue

@@ -0,0 +1,3 @@
+<template>
+    <div class="cl"></div>
+</template>

+ 458 - 0
src/pages/baseData/FanDataStatisticChart.vue

@@ -0,0 +1,458 @@
+<template>
+  <!-- 风机原始数据统计表页面 -->
+  <div class="AllPage">
+    <!-- 页面头部 multiple多选属性 -->
+    <el-card>
+      <el-space>
+        <div class="search-input">
+          <span class="lable">场站:</span>
+          <el-select v-model="state.changZhan" placeholder="全部场站">
+            <el-option
+              v-for="item in changZhanArray"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">型号:</span>
+          <el-select v-model="state.modelId" placeholder="全部型号">
+            <el-option
+              v-for="item in modelList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">部件:</span>
+          <el-select v-model="state.components" placeholder="全部部件">
+            <el-option
+              v-for="item in componentList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.nemCode"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">开始日期:</span>
+          <el-date-picker
+            v-model="state.starttime"
+            type="datetime"
+            placeholder="选择日期"
+            popper-class="date-select"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          >
+          </el-date-picker>
+        </div>
+        <div class="search-input">
+          <span class="lable">结束日期:</span>
+          <el-date-picker
+            v-model="state.endtime"
+            type="datetime"
+            placeholder="选择日期"
+            popper-class="date-select"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          >
+          </el-date-picker>
+        </div>
+        <el-button type="primary" @click="getTableList">查询</el-button>
+      </el-space>
+    </el-card>
+
+    <!-- 页面下部---统计表 -->
+    <div class="table_all">
+      <el-table
+        :data="state.tableData"
+        border
+        :cell-class-name="tableCell"
+        :header-row-class-name="tableRowClassName"
+        height="100%"
+        width="100%"
+        @cell-click="handle"
+      >
+        <el-table-column prop="wtId" fixed label="风机编号" align="center" />
+        <el-table-column
+          :label="item.label"
+          v-for="(item, index) in state.tHeard"
+          :key="index"
+          header-align="center"
+          show-overflow-tooltip
+        >
+          <template #default="{ row }">
+            <div class="bar">
+              <div
+                class="bar-percent"
+                :style="{
+                  width: (row?.count / (row?.count + row?.time)) * 100 + 'px',
+                }"
+              ></div>
+              <span class="value">{{ row?.count }} 次数</span>
+            </div>
+            <div class="bar">
+              <div
+                class="bar-percent"
+                :style="{
+                  width: (row?.time / (row?.count + row?.time)) * 100 + 'px',
+                }"
+              ></div>
+              <span class="value">{{ row?.time }} 分钟</span>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <el-dialog
+      v-model="dialogVisible"
+      width="80%"
+      :before-close="dialogClose"
+      custom-class="currentPdfDialogStyle"
+    >
+      <el-table
+        :data="DataDetail"
+        border
+        :cell-class-name="tableCell"
+        :header-row-class-name="tableRowClassName"
+        height="600px"
+        @row-click="handle"
+      >
+        <el-table-column prop="windturbineId" label="风机编号" align="center" />
+        <el-table-column prop="alarmName" label="报警名称" align="center" />
+        <el-table-column prop="alarmDate" label="时间" align="center" />
+        <el-table-column prop="type" label="类型(触发/解除)" align="center">
+          <template #default="scope">
+            <!-- <span v-if="scope.row.type === 1">触发</span>
+            <span v-if="scope.row.type === 0">解除</span> -->
+            {{ scope.row.type === 1 ? "触发" : "解除" }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+    <!-- <el-pagination
+      small
+      background
+      layout="total, prev, pager, next"
+      :current-page="pageParam.pagenum"
+      :page-size="pageParam.pagesize"
+      :total="pageParam.total"
+      @current-change="changePage"
+    ></el-pagination> -->
+  </div>
+</template>
+
+<script setup>
+import { useStore } from "vuex";
+// import BASE from "@tools/basicTool.js";
+import {
+  ref,
+  onMounted,
+  watch,
+  reactive,
+  computed,
+  onUnmounted,
+  nextTick,
+} from "vue";
+import {
+  getAlarmCountList,
+  fetchModel,
+  fetchRelatePartAndAlarmType,
+} from "/@/api/api.js";
+import dayjs from "dayjs";
+onMounted(() => {
+  getequipmentmodel_list();
+  getfetchRelatePart();
+  getTableList();
+});
+const store = useStore();
+const changZhanArray = computed(() => store.state.stationListAll);
+const state = reactive({
+  tableData: [],
+  tHeard: [
+    // { title: "风机编号", code: "windturbineCode" },
+    // { title: "时间", code: "time" },
+    // { title: "报警信息", code: "alertText" },
+    // { title: "部件", code: "relatePartsText" },
+  ],
+  changZhan: "",
+  components: "", //部件
+  modelId: "", //型号
+  modelListAll: {},
+  fetchListAll: {},
+  starttime: dayjs().add(-1, "day").format("YYYY-MM-DD HH:mm:ss"),
+  endtime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+  dialogVisible: false,
+});
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await fetchModel();
+  state.modelListAll = data;
+};
+//所属部件
+const getfetchRelatePart = async () => {
+  const { data } = await fetchRelatePartAndAlarmType();
+  state.fetchListAll = data;
+};
+//型号列表
+const modelList = computed(() => {
+  if (state.changZhan == "") {
+    return [];
+  } else {
+    return state.modelListAll[state.changZhan];
+  }
+});
+//部件列表
+const componentList = computed(() => {
+  if (state.changZhan == "") {
+    return [];
+  } else {
+    if (state.changZhan.includes("FDC")) {
+      return state.fetchListAll?.fjbj;
+    } else {
+      return state.fetchListAll?.gfbj;
+    }
+  }
+});
+// 获取列表数据 调用接口
+function getTableList() {
+  getAlarmCountList({
+    stationid: state.changZhan || "",
+    begin: state.starttime,
+    end: state.endtime,
+    alarmType: "windturbine",
+    components: state.components,
+    modelId: state.modelId,
+  }).then((res) => {
+    if (res.length) {
+      let tableData = [];
+      let tHeard = [];
+      let data = res;
+      data.forEach((pEle) => {
+        for (let wtId in pEle) {
+          let wtItem = data.find((tableItem) => {
+            return wtId === tableItem.windturbineId;
+          });
+          !wtItem && (wtItem = { wtId });
+          console.log(wtItem);
+          pEle[wtId].forEach((cEle) => {
+            // console.log(cEle);
+            let someRes = tHeard.some((findEle) => {
+              return findEle.label == cEle.alertText;
+            });
+
+            if (!someRes) {
+              tHeard.push({
+                label: cEle.alertText,
+              });
+            }
+            wtItem["count"] = cEle.count + 20;
+            wtItem["time"] = cEle.time + 523;
+          });
+          tableData.push(wtItem);
+        }
+      });
+      state.tHeard = tHeard;
+      state.tableData = tableData;
+    }
+  });
+}
+// export default {
+//   data() {
+//     return {
+//       tableData: [],
+//       tHeard: [],
+//       changZhan: "",
+//     //   changZhanArray: [],
+//       starttime: dayjs(new Date(new Date() - 1 * 24 * 60 * 60 * 1000)).format(
+//         "YYYY-MM-DD"
+//       ),
+//       endtime: dayjs(new Date()).format("YYYY-MM-DD"),
+
+//       // pageParam: {
+//       //   pagenum: 1,
+//       //   pagesize: 10,
+//       //   total: 0,
+//       // },
+//       dialogVisible: false,
+//     };
+//   },
+
+//   created() {
+//     this.getTableList();
+//   },
+
+//   mounted() {
+//     this.$nextTick(() => {
+//       this.getChangZhan();
+//     });
+//   },
+
+//   methods: {
+//     //获取场站
+//     getChangZhan() {
+//       //   getStationinfoAll().then((res) => {
+//       //     this.changZhan = res.data[0].id;
+//       //     this.changZhanArray = res.data;
+//       //     // console.log('changzhang>>>>>>>>>>>',res)
+//       //     this.getTableList();
+//       //   });
+//     },
+//     // 查询按钮
+//     Select() {
+//       this.getTableList();
+//     },
+//     // 对时间进行包装
+//     getTime(date) {
+//       var y = date.getFullYear();
+//       var m = date.getMonth() + 1;
+//       m = m < 10 ? "0" + m : m;
+//       var d = date.getDate();
+//       d = d < 10 ? "0" + d : d;
+//       return y + "-" + m + "-" + d + " ";
+//     },
+
+//     // 获取列表数据 调用接口
+//     getTableList() {
+//       getAlarmCountList({
+//         stationid: this.changZhan || "",
+//         starttime: dayjs(this.starttime).format("YYYY-MM-DD"),
+//         endtime: dayjs(this.endtime).format("YYYY-MM-DD"),
+//       }).then((res) => {
+//         console.log("列表数据》》》》》》》", res);
+//         if (res && res.status === 20000) {
+//           let tHeard = [];
+//           let tableData = [];
+
+//           this.tableData = res.data;
+
+//           res.data.forEach((pEle) => {
+//             for (let wtId in pEle) {
+//               let wtItem = tableData.find((tableItem) => {
+//                 return wtId === tableItem.wtId;
+//               });
+//               !wtItem && (wtItem = { wtId });
+//               pEle[wtId].forEach((cEle) => {
+//                 let someRes = tHeard.some((findEle) => {
+//                   return findEle.parts === cEle.parts;
+//                 });
+
+//                 if (!someRes) {
+//                   tHeard.push({
+//                     parts: cEle.parts,
+//                     label: cEle.partsName,
+//                     align: "center",
+//                     prop: `${cEle.parts}_Value`,
+//                   });
+//                 }
+
+//                 wtItem[`${cEle.parts}_Value`] = wtItem[`${cEle.parts}_Value`]
+//                   ? wtItem[`${cEle.parts}_Value`] + cEle.count
+//                   : cEle.count;
+//               });
+//               tableData.push(wtItem);
+//             }
+//           });
+
+//           this.tHeard = tHeard;
+//           this.tableData = tableData;
+//         }
+//       });
+//     },
+
+//     // 单元格点击事件
+//     handle(row, column, event, cell) {
+//       let parts =
+//         this.tHeard.find((ele) => {
+//           return ele.label === column.label;
+//         })?.parts || "";
+//       getDialogData({
+//         stationid: this.changZhan || "",
+//         starttime: dayjs(this.starttime).format("YYYY-MM-DD"),
+//         endtime: dayjs(this.endtime).format("YYYY-MM-DD"),
+//         windturbineid: row.wtId,
+//         parts,
+//       }).then((res) => {
+//         if (res && res.status === 20000) {
+//           console.log("弹窗数据》》》》》》》", res);
+
+//           if (res.data.length) {
+//             if (column.property !== "wtId") {
+//               this.DataDetail = res.data;
+//               let tableArr = [];
+//               res.data.forEach((currentItem) => {
+//                 // if (currentItem.type === 1) {
+//                 //   currentItem.type = '触发'
+//                 // }
+//                 // else if (currentItem.type === 0) {
+//                 //   currentItem.type = '解除'
+//                 // }
+//                 tableArr.push(currentItem);
+//               });
+
+//               this.dialogVisible = true;
+//             }
+//           } else {
+//             BASE.showMsg({
+//               msg: "所选风机暂无数据",
+//             });
+//           }
+//         }
+//       });
+//     },
+
+//     // changePage(val) {
+//     //   this.pageParam.pagenum = val;
+//     //   this.getTableList();
+//     // },
+//   },
+// };
+</script>
+
+<style lang="scss" scoped>
+.AllPage {
+  height: 100%;
+  .Head {
+    display: flex;
+    width: 100%;
+    height: 5%;
+    margin: 5px 0;
+  }
+  .bar {
+    display: flex;
+    align-items: center;
+    height: 16px;
+    margin: 8px 0;
+
+    .bar-percent {
+      height: 100%;
+      background: #6198ff;
+      margin-right: 8px;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.table_all {
+  height: calc(100% - 74px - 30px);
+  padding: 10px;
+  margin-top: 10px;
+  background: #fff;
+  .el-table {
+    .el-table__row {
+      td {
+        cursor: pointer;
+
+        &:first-child {
+          cursor: auto;
+        }
+      }
+    }
+  }
+}
+</style>

+ 447 - 0
src/pages/baseData/FanDataStatisticTable.vue

@@ -0,0 +1,447 @@
+<template>
+  <!-- 风机原始数据统计表页面 -->
+  <div class="AllPage">
+    <!-- 页面头部 multiple多选属性 -->
+    <el-card>
+      <el-space>
+        <div class="search-input">
+          <span class="lable">场站:</span>
+          <el-select
+            v-model="state.changZhan"
+            @change="changeChangzhan"
+            placeholder="全部场站"
+          >
+            <el-option
+              v-for="item in changZhanArray"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">型号:</span>
+          <el-select
+            v-model="state.modelId"
+            @change="changeModel"
+            placeholder="全部型号"
+          >
+            <el-option
+              v-for="item in modelList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">部件:</span>
+          <el-select
+            v-model="state.components"
+            multiple
+            collapse-tags
+            placeholder="全部部件"
+            @change="changeComponents"
+          >
+            <el-option
+              v-for="item in componentList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.nemCode"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">报警描述:</span>
+          <el-select
+            v-model="state.alarmIds"
+            multiple
+            collapse-tags
+            placeholder="全部描述"
+          >
+            <el-option
+              v-for="item in state.alarmIdList"
+              :key="item.id"
+              :label="item.description"
+              :value="item.alarmId"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="search-input">
+          <span class="lable">开始日期:</span>
+          <el-date-picker
+            v-model="state.starttime"
+            type="datetime"
+            placeholder="选择日期"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          >
+          </el-date-picker>
+        </div>
+        <div class="search-input">
+          <span class="lable">结束日期:</span>
+          <el-date-picker
+            v-model="state.endtime"
+            type="datetime"
+            placeholder="选择日期"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          >
+          </el-date-picker>
+        </div>
+        <el-button type="primary" @click="getTableList">查询</el-button>
+      </el-space>
+    </el-card>
+
+    <!-- 页面下部---统计表 -->
+    <div class="table_all">
+      <el-table
+        :data="state.tableData"
+        border
+        :cell-class-name="tableCell"
+        :header-row-class-name="tableRowClassName"
+        @cell-click="handle"
+        height="100%"
+      >
+        <el-table-column
+          prop="wtname"
+          label="风机编号"
+          align="center"
+          width="80"
+        />
+        <el-table-column
+          :label="item.label"
+          v-for="(item, index) in state.tHeard"
+          :key="index"
+          header-align="center"
+          show-overflow-tooltip
+        >
+          <template #default="{ row }">
+            <div class="bar">
+              <div
+                class="bar-percent"
+                :style="{
+                  width:
+                    (row[`${item.code}_count`] /
+                      (row[`${item.code}_count`] + row[`${item.code}_time`])) *
+                      100 +
+                    'px',
+                }"
+              ></div>
+              <span class="value">{{ row[`${item.code}_count`] }} 次数</span>
+            </div>
+            <div class="bar">
+              <div
+                class="bar-percent"
+                :style="{
+                  width:
+                    (row[`${item.code}_time`] /
+                      (row[`${item.code}_count`] + row[`${item.code}_time`])) *
+                      100 +
+                    'px',
+                }"
+              ></div>
+              <span class="value">{{ row[`${item.code}_time`] }} 分钟</span>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <el-dialog
+      v-model="dialogVisible"
+      width="80%"
+      :before-close="dialogClose"
+      custom-class="currentPdfDialogStyle"
+    >
+      <el-table
+        :data="DataDetail"
+        border
+        :cell-class-name="tableCell"
+        :header-row-class-name="tableRowClassName"
+        height="600px"
+        @row-click="handle"
+      >
+        <el-table-column prop="windturbineId" label="风机编号" align="center" />
+        <el-table-column prop="alarmName" label="报警名称" align="center" />
+        <el-table-column prop="alarmDate" label="时间" align="center" />
+        <el-table-column prop="type" label="类型(触发/解除)" align="center">
+          <template #default="scope">
+            <!-- <span v-if="scope.row.type === 1">触发</span>
+            <span v-if="scope.row.type === 0">解除</span> -->
+            {{ scope.row.type === 1 ? "触发" : "解除" }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+    <!-- <el-pagination
+      small
+      background
+      layout="total, prev, pager, next"
+      :current-page="pageParam.pagenum"
+      :page-size="pageParam.pagesize"
+      :total="pageParam.total"
+      @current-change="changePage"
+    ></el-pagination> -->
+  </div>
+</template>
+
+<script setup>
+import { useStore } from "vuex";
+// import BASE from "@tools/basicTool.js";
+import {
+  ref,
+  onMounted,
+  watch,
+  reactive,
+  computed,
+  onUnmounted,
+  nextTick,
+} from "vue";
+import {
+  getAlarmCountList,
+  fetchModel,
+  fetchRelatePartAndAlarmType,
+  GetAlarmId,
+} from "/@/api/api.js";
+import dayjs from "dayjs";
+onMounted(() => {
+  getequipmentmodel_list();
+  getfetchRelatePart();
+  //   getTableList();
+});
+
+const store = useStore();
+const changZhanArray = computed(() => store.state.stationListAll);
+const state = reactive({
+  tableData: [],
+  tHeard: [],
+  changZhan: "",
+  components: "", //部件
+  modelId: "", //型号
+  alarmIds: "",
+  alarmIdList: [],
+  modelListAll: {},
+  fetchListAll: {},
+  starttime: dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss"),
+  endtime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+  dialogVisible: false,
+});
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await fetchModel();
+  state.modelListAll = data;
+};
+//所属部件
+const getfetchRelatePart = async () => {
+  const { data } = await fetchRelatePartAndAlarmType();
+  state.fetchListAll = data;
+};
+//型号列表
+const modelList = computed(() => {
+  if (state.changZhan == "") {
+    return [];
+  } else {
+    return state.modelListAll[state.changZhan];
+  }
+});
+//部件列表
+const componentList = computed(() => {
+  if (state.changZhan == "") {
+    return [];
+  } else {
+    if (state.changZhan.includes("FDC")) {
+      return state.fetchListAll?.fjbj;
+    } else {
+      return state.fetchListAll?.gfbj;
+    }
+  }
+});
+watch(
+  () => changZhanArray,
+  (val, old) => {
+    val.value[0] &&
+      nextTick(async () => {
+        state.changZhan = changZhanArray.value[0]?.id;
+      });
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+watch(
+  () => [modelList, componentList],
+  (val) => {
+    let arr = val.map((item) => item.value);
+    if (arr[0] && arr[0].length && arr[1] && arr[1].length) {
+      state.modelId = [arr[0][0]?.nemCode];
+      state.components = [arr[1][0]?.nemCode];
+      getAlarmId();
+    }
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+function changeModel(val) {
+  state.modelId = val;
+  getAlarmId();
+}
+function changeComponents(val) {
+  state.components = val;
+  getAlarmId();
+}
+function changeChangzhan(val) {
+  state.changZhan = val;
+  getAlarmId();
+}
+function getAlarmId() {
+  GetAlarmId({
+    components: state.components,
+    modelId: state.modelId,
+    wpId: state.changZhan,
+  }).then(({ data }) => {
+    state.alarmIdList = data;
+    state.alarmIds =
+      data.length <= 5
+        ? data.map((item) => item.alarmId)
+        : data.slice(0, 5).map((item) => item.alarmId);
+    getTableList();
+  });
+}
+// 获取列表数据 调用接口
+function getTableList() {
+  getAlarmCountList({
+    stationid: state.changZhan || "",
+    begin: state.starttime,
+    end: state.endtime,
+    timeType: "s",
+    components: state.components,
+    modelId: state.modelId,
+    alarmIds: state.alarmIds,
+  }).then((res) => {
+    if (res.length) {
+      let tableData = [];
+      let tHeard = [];
+      let data = res;
+      data.forEach((pEle) => {
+        for (let wtId in pEle) {
+          let wtItem = data.find((tableItem) => {
+            return wtId === tableItem.windturbineId;
+          });
+          !wtItem && (wtItem = { wtId });
+          pEle[wtId].forEach((cEle) => {
+            let someRes = tHeard.some((findEle) => {
+              return findEle.label == cEle.alertText;
+            });
+
+            if (!someRes) {
+              tHeard.push({
+                label: cEle.alertText,
+                code: cEle.alarmid,
+              });
+            }
+            wtItem[`${cEle.alarmid}_count`] = cEle.count;
+            wtItem[`${cEle.alarmid}_time`] = cEle.time;
+            wtItem["wtname"] = cEle.windturbineCode;
+          });
+          tableData.push(wtItem);
+        }
+      });
+      state.tHeard = tHeard;
+      state.tableData = tableData;
+    } else {
+      state.tHeard = [];
+      state.tableData = [];
+    }
+  });
+}
+
+//     // 单元格点击事件
+//     handle(row, column, event, cell) {
+//       let parts =
+//         this.tHeard.find((ele) => {
+//           return ele.label === column.label;
+//         })?.parts || "";
+//       getDialogData({
+//         stationid: this.changZhan || "",
+//         starttime: dayjs(this.starttime).format("YYYY-MM-DD"),
+//         endtime: dayjs(this.endtime).format("YYYY-MM-DD"),
+//         windturbineid: row.wtId,
+//         parts,
+//       }).then((res) => {
+//         if (res && res.status === 20000) {
+//           console.log("弹窗数据》》》》》》》", res);
+
+//           if (res.data.length) {
+//             if (column.property !== "wtId") {
+//               this.DataDetail = res.data;
+//               let tableArr = [];
+//               res.data.forEach((currentItem) => {
+//                 // if (currentItem.type === 1) {
+//                 //   currentItem.type = '触发'
+//                 // }
+//                 // else if (currentItem.type === 0) {
+//                 //   currentItem.type = '解除'
+//                 // }
+//                 tableArr.push(currentItem);
+//               });
+
+//               this.dialogVisible = true;
+//             }
+//           } else {
+//             BASE.showMsg({
+//               msg: "所选风机暂无数据",
+//             });
+//           }
+//         }
+//       });
+//     },
+</script>
+
+<style lang="scss" scoped>
+.AllPage {
+  height: 100%;
+  .Head {
+    display: flex;
+    width: 100%;
+    height: 5%;
+    margin: 5px 0;
+  }
+  .bar {
+    display: flex;
+    align-items: center;
+    height: 16px;
+    margin: 8px 0;
+
+    .bar-percent {
+      height: 100%;
+      background: #6198ff;
+      margin-right: 8px;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.table_all {
+  height: calc(100% - 74px - 30px);
+  padding: 10px;
+  margin-top: 10px;
+  background: #fff;
+  .el-table {
+    .el-table__row {
+      td {
+        cursor: pointer;
+
+        &:first-child {
+          cursor: auto;
+        }
+      }
+    }
+  }
+}
+</style>

+ 353 - 0
src/pages/baseData/device.vue

@@ -0,0 +1,353 @@
+<template>
+  <div class="main">
+    <el-card class="box-card-tree">
+      <el-button size="mini" type="primary" @click="handleTreeAdd()"
+        >新增</el-button
+      >
+      <el-button
+        size="mini"
+        type="primary"
+        @click="handleTreeDel()"
+        :disabled="!state.isDel"
+        >删除</el-button
+      >
+      <el-tree
+      style="margin-top:10px"
+        :data="state.treedata"
+        :props="state.defaultProps"
+        node-key="id"
+        highlight-current
+        :default-expanded-keys="[23]" :current-node-key="23" :expand-on-click-node="false"
+        @node-click="handleNodeClick"
+      />
+    </el-card>
+    <el-card class="box-card" style="overflow-y: auto; max-height: 90vh">
+      <el-tabs type="card">
+        <!-- <el-tab-pane label="传感点">
+          <point />
+        </el-tab-pane> -->
+        <el-tab-pane label="遥测点">
+          <ycpoint />
+        </el-tab-pane>
+        <el-tab-pane label="遥信点">
+          <yxpoint />
+        </el-tab-pane>
+         <el-tab-pane label="设备报警">
+          <windturbine />
+        </el-tab-pane>
+        <el-tab-pane label="故障预警">
+          <custom />
+        </el-tab-pane>
+        <el-tab-pane label="故障原因">
+          <faultmode />
+        </el-tab-pane>
+        <el-tab-pane label="基本信息">
+          <message />
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+
+    <!-- tree add 弹出框 -->
+    <el-dialog v-model="treeVisible" width="40%">
+      <el-form :model="state.treeAddFormData" label-width="80px">
+        <el-row :gutter="24">
+          <el-col :span="12">
+            <el-form-item label="结构编码">
+              <el-input
+                v-model="state.treeAddFormData.code"
+              ></el-input> </el-form-item
+          ></el-col>
+          <el-col :span="12">
+            <el-form-item label="上级节点" label-width="100px">
+              <el-cascader
+                v-model="state.treeAddFormData.parentcode"
+                :options="state.cascaderdata"
+                :props="{ checkStrictly: true, emitPath: false }"
+                :show-all-levels="false"
+                clearable
+              /> </el-form-item
+          ></el-col>
+        </el-row>
+        <el-row :gutter="24">
+          <el-col :span="12">
+            <el-form-item label="结构名称">
+              <el-input
+                v-model="state.treeAddFormData.name"
+              ></el-input> </el-form-item
+          ></el-col>
+          <el-col :span="12">
+            <el-form-item label="结构英文名称" label-width="100px">
+              <el-input
+                v-model="state.treeAddFormData.enname"
+              ></el-input> </el-form-item
+          ></el-col>
+        </el-row>
+        <el-row :gutter="24">
+          <el-col :span="12"
+            ><el-form-item label="序号">
+              <el-input
+                v-model="state.treeAddFormData.orderno"
+              ></el-input> </el-form-item
+          ></el-col>
+          <el-col :span="4">
+            <el-form-item label="是否有效">
+              <el-switch
+                v-model="state.treeAddFormData.enabled"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8">
+            <el-form-item label="设备ID">
+              <el-input
+                v-model="state.treeAddFormData.deviceid"
+                disabled
+              ></el-input> </el-form-item
+          ></el-col>
+        </el-row>
+        <el-form-item label="描述">
+          <el-input
+            v-model="state.treeAddFormData.description"
+            :rows="4"
+            clearable
+            type="textarea"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="treeVisible = false">取 消</el-button>
+          <el-button type="primary" @click="saveTreeAdd">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import { ref, onMounted, reactive, provide, computed ,nextTick} from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import faultmode from "./fan_components/faultmode.vue";
+import message from "./fan_components/message.vue";
+import ycpoint from "./fan_components/ycpoint.vue";
+import yxpoint from "./fan_components/yxpoint.vue";
+import custom from "./fan_components/custom.vue";
+import windturbine from "./fan_components/windturbine.vue";
+import { tree, treeDel, treeAdd } from "/@/api/api.js";
+let treeVisible = ref(false);
+let editFormData = ref({});
+onMounted(async () => {
+  await getData();
+  nextTick(() => {
+    handleNodeClick(state.cascaderdata[0])
+  })
+
+});
+let state = reactive({
+  isDel:false, //是否能被删除
+  treedata: [],
+  cascaderdata: [],
+  defaultProps: {
+    children: "children",
+    label: (a) => {
+      return a.node.name;
+    },
+  },
+  textarea: "",
+  addFormData: {
+    categorydata: "AI",
+    categoryres1: null,
+    categoryres2: null,
+    categoryres3: null,
+    categoryres4: null,
+    categoryres5: null,
+    categorysci: null,
+    categorysys: null,
+    description: null,
+    deviceid: "windturbine",
+    enname: null,
+    id: null,
+    metriccode: "",
+    name: "",
+    structurecode: "101010205",
+    unitname: null,
+    unitnamecn: null,
+    deviceModelMetrics: [],
+  },
+  treeAddFormData: {
+    code: "",
+    parentcode: "",
+    deviceid: "windturbine",
+    name: "",
+    enname: "",
+    description: "",
+    orderno: "",
+    enabled: true,
+  },
+  options: [
+    {
+      value: "AI",
+      label: "AI",
+    },
+    {
+      value: "DI",
+      label: "DI",
+    },
+    {
+      value: "CI",
+      label: "CI",
+    },
+  ],
+  desc: {
+    code: "",
+    parentcode: "",
+    deviceid: "",
+    name: "",
+    enname: "",
+    description: "",
+    orderno: "",
+    enabled: true,
+  },
+  treeDelVal: "",
+  deviceModel: [],
+  deviceModelSel: "",
+  treeId:'',
+});
+
+// 父子传参
+provide(
+  "cascaderdata",
+  computed(() => state.cascaderdata)
+);
+provide(
+  "desc",
+  computed(() => state.desc)
+);
+provide(
+  "treeId",
+  computed(() => state.treeId)
+);
+
+// tree click
+const handleNodeClick = (data) => {
+  console.log(data);
+  state.isDel = data.children?false:true;
+  state.treeId = data.id;
+  state.desc = data.node;
+  state.addFormData.structurecode = data.node.code;
+};
+
+// tree add
+const handleTreeAdd = () => {
+  treeVisible.value = true;
+};
+// tree Del
+const handleTreeDel = () => {
+  console.warn(state.desc);
+  if (!state.isDel) {
+    ElMessage.error(`不能删除该节点!!`);
+    return;
+  }
+  ElMessageBox.confirm(`确定要删除 ${state.desc.name} 吗?`, "提示", {
+    type: "warning",
+  })
+    .then(async () => {
+      const res = await treeDel(state.desc.id);
+      console.warn(res);
+      if (res == 1) {
+        ElMessage.success(`删除成功!`);
+        getData();
+      } else {
+        ElMessage.warning(`删除失败!`);
+      }
+    })
+    .catch(() => {});
+};
+// delTreeBtn
+const delTreeBtn = async () => {
+  console.warn(state.treeDelVal);
+  const res = await treeDel(state.treeDelVal);
+  console.warn(res);
+  if (res == 1) {
+    ElMessage.success(`删除成功!`);
+    getData();
+  } else {
+    ElMessage.warning(`删除失败!`);
+  }
+  state.treeDelVal = "";
+};
+//保存tree add
+const saveTreeAdd = async () => {
+  const res = await treeAdd(state.treeAddFormData);
+  console.warn(res);
+  if (res.status == 20000) {
+    ElMessage.success(`操作成功!`);
+    treeVisible.value = false;
+    getData();
+  } else {
+    ElMessage.warning(res.msg);
+  }
+};
+
+// getData
+const getData = async () => {
+  const res = await tree();
+  console.warn(res);
+  state.treedata = res.children.reverse();
+  state.cascaderdata = cascaderforEach(res);
+  console.warn(state.cascaderdata);
+};
+
+// 递归解析基础数据
+const cascaderforEach = (val) => {
+  if (val.children != null) {
+    val.children.forEach((v) => {
+      v["value"] = v.node.code;
+      v["label"] = v.node.name;
+      v["id"] = v.node.id;
+      cascaderforEach(v);
+    });
+    return val.children;
+  }
+};
+</script>
+<style lang="scss" scoped>
+.main {
+  display: flex;
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  justify-content: space-between;
+  .box-card-tree {
+    width: 20%;
+    margin-right: 1%;
+    overflow: auto;
+  }
+  .box-card {
+    flex: 1;
+    .desc {
+      display: flex;
+      margin-bottom: 30px;
+      span {
+        width: 100px;
+      }
+    }
+  }
+}
+</style>
+<style lang="scss" >
+.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
+  background-color: #bbdbff !important;
+}
+.deviceModelClass {
+  .el-table__header tr,
+  .el-table__header th {
+    padding: 0;
+    height: 40px;
+  }
+  .el-table__body tr,
+  .el-table__body td {
+    padding: 0;
+    height: 40px;
+  }
+}
+</style>

+ 200 - 0
src/pages/baseData/dictionary.vue

@@ -0,0 +1,200 @@
+<template>
+  <el-card class="box-card">
+    <div class="devicemenu">
+      <el-space wrap :size="15">
+        <el-button type="primary" @click="handleEdit(state.addFormData)"
+          >新增</el-button
+        >
+        <el-input
+          v-model="state.searchCategory"
+          placeholder="字典类别"
+          style="width: 180px"
+          size="small"
+        />
+        <el-input
+          v-model="state.searchName"
+          placeholder="字典名称"
+          style="width: 180px"
+          size="small"
+        />
+        <el-button type="primary" @click="search">查询</el-button>
+      </el-space>
+    </div>
+    <el-table
+      :data="state.TableData"
+      stripe
+      style="width: 100%"
+      height="calc(100% - 42px)"
+    >
+      <!-- <el-table-column type="index" width="50" /> -->
+      <el-table-column
+        prop="name"
+        label="字典名称"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="category"
+        label="字典分类"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="code"
+        label="字典编码"
+        align="center"
+      ></el-table-column>
+      <!-- <el-table-column prop="orderno" label="顺序号" align="center"></el-table-column> -->
+      <el-table-column prop="enabled" label="是否可用" align="center">
+        <template #default="scope">
+          {{ scope.row.enabled == 1 ? "是" : "否" }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="scope">
+          <el-button
+            type="text"
+            icon="el-icon-edit"
+            @click="handleEdit(scope.row)"
+            >编辑</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination
+        background
+        layout="total, prev, pager, next"
+        hide-on-single-page
+        :current-page="query.page"
+        :page-size="query.limit"
+        :total="pageTotal"
+        @current-change="handlePageChange"
+      ></el-pagination>
+    </div>
+  </el-card>
+  <!-- dialog编辑弹出框 -->
+  <el-dialog v-model="editVisible" width="40%">
+    <el-form :model="editFormData" label-width="80px">
+      <el-form-item label="字典名称">
+        <el-input v-model="editFormData.name"></el-input>
+      </el-form-item>
+      <el-row :gutter="24">
+        <el-col :span="12">
+          <el-form-item label="字典分类">
+            <el-input v-model="editFormData.category"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="字典编码">
+            <el-input v-model="editFormData.code"></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="24">
+        <el-col :span="12">
+          <el-form-item label="顺序号">
+            <el-input v-model="editFormData.orderno"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="是否可用">
+            <el-switch
+              v-model="editFormData.enabled"
+              class="ml-2"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+              :active-value="1"
+              :inactive-value="0"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="editVisible = false">取 消</el-button>
+        <el-button type="primary" @click="saveEdit">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, onMounted, reactive } from "vue";
+import { ElMessage } from "element-plus";
+import { get_datadictionary, post_saveorupdate } from "/@/api/api.js";
+onMounted(() => {
+  getTableData();
+});
+let editFormData = ref({});
+let editVisible = ref(false); //传感点
+
+let state = reactive({
+  TableData: "",
+  searchName: "",
+  searchCategory: "",
+  addFormData: {
+    category: "",
+    code: "",
+    name: "",
+    orderno: 1,
+    enabled: 1,
+  },
+});
+const query = reactive({
+  page: 1,
+  limit: 10,
+});
+let pageTotal = ref(0);
+const search = async () => {
+  getTableData();
+};
+// getTableData
+const getTableData = async () => {
+  const { data } = await get_datadictionary(
+    query.page,
+    query.limit,
+    state.searchName,
+    state.searchCategory
+  );
+  //   console.warn(data);
+  pageTotal.value = data.total;
+  state.TableData = data.records;
+};
+
+// 编辑
+const handleEdit = (row) => {
+  editFormData.value = row;
+  editVisible.value = true;
+};
+//add
+const postSaveorupdate = async () => {
+  const res = await post_saveorupdate(editFormData.value);
+  if (res.code == 200) {
+    ElMessage.success(`操作成功!`);
+    editVisible.value = false;
+  } else {
+    ElMessage.success(res.msg);
+  }
+  getTableData();
+};
+// 保存编辑
+const saveEdit = async () => {
+  postSaveorupdate();
+};
+
+// 分页导航
+const handlePageChange = (val) => {
+  query.page = val;
+  getTableData();
+};
+</script>
+<style lang="scss" scoped>
+.devicemenu {
+  margin-bottom: 10px;
+}
+.el-card :deep {
+  height: 100%;
+  .el-card__body {
+    height: calc(100% - 40px);
+  }
+}
+</style>

+ 277 - 0
src/pages/baseData/fan_components/custom.vue

@@ -0,0 +1,277 @@
+<template>
+    <el-row type="flex" justify="space-between">
+      <div class="handle-box" style="margin-bottom: 10px">
+        <el-button type="primary" icon="el-icon-lx-add" class="handle-edit mr10" @click="handleInsert">
+          新增记录
+        </el-button>
+        <el-button type="primary" icon="el-icon-lx-down" class="handle-edit mr10" @click="export2Excel">
+          批量导出</el-button>
+        <el-button type="primary" icon="el-icon-lx-down" class="handle-edit mr10" @click="outExe">
+          模板下载</el-button>
+        <el-upload class="handle-edit mr10" style=" display: inline;" action="/sharding/alertrule2/import"
+          :headers="token" :show-file-list="false" :on-success="handleSuccess" :on-progress="handleProgress"
+          :on-error="handleError">
+          <el-button type="primary" icon="el-icon-lx-top" @click="exportShow = true">
+            批量导入</el-button>
+        </el-upload>
+      </div>
+      <div style="display: flex; flex-direction: row; margin-bottom: 10px">
+        <el-select v-if="!isStation" v-model="query.category" class="mr10" style="width: 150px" popper-class="select">
+          <el-option key="1" label="风机" value="1"></el-option>
+          <el-option key="2" label="电气" value="2"></el-option>
+        </el-select>
+        <el-select v-if="!isStation" v-model="query.station" clearable class="mr10" style="width: 150px"
+          placeholder="全部场站" popper-class="select" @change="changeStation()">
+          <el-option v-for="item in stationList" :key="item.id" :value="item.id" :label="item.name"></el-option>
+        </el-select>
+        <el-select v-model="query.modelId" clearable class="mr10" style="width: 150px" placeholder="全部机型"
+          popper-class="select">
+          <el-option v-for="item in state.modelList" :key="item" :value="item" :label="item"></el-option>
+        </el-select>
+        <el-select v-model="query.rank" clearable class="mr10" style="width: 150px" placeholder="全部级别"
+          popper-class="select">
+          <el-option v-for="item in state.rankList" :key="item.id" :value="item.id" :label="item.name"></el-option>
+        </el-select>
+        <el-input placeholder="请输入名称" v-model="query.name" style="width: 150px" class="mr10" clearable></el-input>
+        <el-button icon="el-icon-search" type="primary" @click="getData">搜索</el-button>
+      </div>
+    </el-row>
+    <el-table :data="state.tableData" border class="table" header-cell-name="table-header"
+      :highlight-current-row="true">
+      <el-table-column label="编码" align="center" prop="id" min-width="50" />
+      <el-table-column label="场站" align="center" prop="windPowerStation.name" min-width="50" />
+      <el-table-column label="机型" align="center" prop="modelId" min-width="50" />
+      <el-table-column label="名称" align="left" prop="name" min-width="50" />
+      <el-table-column label="表达式" align="left">
+        <template #default="scope">
+          {{ scope.row.expressionShow }}
+        </template>
+      </el-table-column>
+      <el-table-column label="级别" align="center" prop="rank" min-width="20">
+        <template #default="scope">{{ rankConvert(scope.row.rank) }}</template>
+      </el-table-column>
+      <el-table-column label="类型" align="center" prop="category" min-width="20">
+        <template #default="scope">{{
+            categoryConvert(scope.row.category)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="描述" align="left">
+        <template #default="scope">
+          {{ scope.row.descriptionShow }}
+        </template>
+      </el-table-column>
+      <el-table-column label="是否启用" align="center" prop="enabled" min-width="20">
+        <template #default="scope">
+          {{ enabledConvert(scope.row.enabled) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="所属部件" align="center" prop="relatedParts" min-width="30">
+        <template #default="scope">
+          <span>{{ scope.row.datadictionary?.name }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="100">
+        <template #default="scope">
+          <el-button type="primary" plain round size="mini" icon="el-icon-lx-edit"
+            :disabled="notEdit || scope.row.range == 1" @click="handleEditClick(scope.row)">编辑</el-button>
+          <!-- <el-button type="primary" plain round size="mini" icon="el-icon-search" @click="handleViewClick(scope.$index, scope.row)">查看</el-button> -->
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination background layout="total, prev, pager, next" hide-on-single-page :current-page="query.pagenum"
+        :page-size="query.pagesize" :total="total" @current-change="handlePageChange"></el-pagination>
+    </div>
+    <customcomponents @close="dialogclose" :isVisible="state.visible" :form="state.form" />
+</template>
+<script setup>
+import { custombj_fetchTableData, getStationinfo } from "/@/api/api.js";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+import { ref, onMounted, provide, computed, reactive, watch } from "vue";
+import { useStore } from "vuex";
+import { ElMessageBox, ElMessage } from "element-plus";
+import customcomponents from "./custom_components.vue";
+import { common } from "/@/composables/common";
+const { notEdit } = common();
+const token = { 'token': sessionStorage.getItem('token') }
+const store = useStore();
+const stationList = computed(() => store.state.stationList);
+const isStation = computed(() => store.getters.isStation);
+const state = reactive({
+  tableData: [],
+  modelList: [],
+  rankList: [
+    {
+      id: 1,
+      name: "低",
+    },
+    {
+      id: 2,
+      name: "中低",
+    },
+    {
+      id: 3,
+      name: "中",
+    },
+    {
+      id: 4,
+      name: "中高",
+    },
+    {
+      id: 5,
+      name: "高",
+    },
+  ],
+  visible: false,
+  form: {},
+});
+const query = reactive({
+  pagenum: 1,
+  pagesize: 10,
+  name: "",
+  station: "",
+  rank: "",
+  modelId: "",
+  category: "1",
+});
+let total = ref(0);
+onMounted(() => {
+  getData();
+  getequipmentmodel_list()
+});
+const dialogclose = () => {
+  state.visible = false
+  getData()
+}
+const getData = async () => {
+  const res = await custombj_fetchTableData(query);
+  state.tableData = res.records;
+  total.value = res.total;
+};
+const handleEditClick = (row) => {
+  let obj = Object.assign({}, row)
+  state.form = obj;
+  state.visible = true;
+};
+const handleInsert = () => {
+  state.visible = true;
+};
+
+//changeStation
+const changeStation = async () => {
+  query.modelId = "";
+  await getequipmentmodel_list();
+};
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await getStationinfo(query.station);
+  if (query.station == '') {
+    let arr = []
+    data.forEach(e => {
+      arr.push(...e.modelList)
+    });
+    state.modelList = [...new Set(arr)]
+  } else {
+    state.modelList = data[0].modelList;
+  }
+};
+// 分页导航
+const handlePageChange = (val) => {
+  query.pagenum = val;
+  getData();
+};
+const tableHeader = [
+  "id",
+  "规则名称",
+  "报警类别(1:风气;2:电气)",
+  "报警级别(1:低;2:中低;3:中;4:中高;5:高)",
+  "是否启用(0:停用;1:启用)",
+  "风场",
+  "风机类型",
+  "所属部件",
+  "表达式",
+  "规则描述",
+];
+
+const tableKey = [
+  "id",
+  "name",
+  "category",
+  "rank",
+  "enabled",
+  "station",
+  "modelId",
+  "relatedParts",
+  "expressionShow",
+  "descriptionShow",
+];
+// 批量导出
+const export2Excel = async () => {
+  let { name, station, rank, modelId, category } = query;
+  const res = await custombj_fetchTableData({
+    pagenum: 1,
+    pagesize: 999999,
+    name,
+    station,
+    rank,
+    modelId,
+    category,
+  });
+
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, res.records, "自定义预警配置导出excel");
+};
+// 模板下载
+const outExe = () => {
+  const data = [];
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, data, "自定义报警模板");
+};
+// 批量导入
+const handleSuccess = (response, file, fileList) => {
+  ElMessage.success("导入成功!");
+  getData();
+};
+const handleProgress = (response, file, fileList) => { };
+const handleError = (response, file, fileList) => {
+  ElMessage.success("导入失败!");
+};
+const rankConvert = (val) => {
+  if (val === "1") {
+    return "低";
+  } else if (val === "2") {
+    return "中低";
+  } else if (val === "3") {
+    return "中";
+  } else if (val === "4") {
+    return "中高";
+  } else if (val === "5") {
+    return "高";
+  }
+};
+// 类型
+const categoryConvert = (val) => {
+  if (val === "1") {
+    return "风机";
+  } else if (val === "2") {
+    return "电气";
+  }
+};
+// 状态
+const enabledConvert = (val) => {
+  if (val === 0) {
+    return "停用";
+  } else if (val === 1) {
+    return "启用";
+  }
+};
+</script>
+<style scoped>
+.mr10 {
+  margin-right: 10px;
+}
+
+.el-button+.el-button {
+  margin-left: 10px;
+}
+</style>

+ 665 - 0
src/pages/baseData/fan_components/custom_components.vue

@@ -0,0 +1,665 @@
+<template>
+  <el-dialog v-model="isShow" width="1100px" :before-close="handleClose">
+    <el-form ref="ruleFormRef" :model="form" :rules="rules" label-position="top" label-width="200px">
+      <el-row type="flex" justify="space-between" align="middle" :gutter="10">
+        <el-col :span="15">
+          <el-form-item prop="name">
+            <el-tag>规则名称</el-tag>
+            <el-input v-model="form.name" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="9" style="
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+          ">
+          <el-form-item prop="category">
+            <el-tag>报警类别</el-tag>
+            <el-select v-model="form.category" class="select-mini">
+              <el-option key="1" label="风机" value="1" />
+              <el-option key="2" label="电气" value="2" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="rank">
+            <el-tag>报警级别</el-tag>
+            <el-select v-model="form.rank" class="select-mini">
+              <el-option key="1" label="低" value="1" />
+              <el-option key="2" label="中低" value="2" />
+              <el-option key="3" label="中" value="3" />
+              <el-option key="4" label="中高" value="4" />
+              <el-option key="5" label="高" value="5" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="enabled">
+            <el-tag>是否启用</el-tag>
+            <el-switch v-model="form.enabled" :active-value="1" :inactive-value="0" active-color="#13ce66" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="10">
+        <el-col :span="6">
+          <el-form-item prop="station">
+            <el-tag>风场</el-tag>
+            <el-select v-model="form.station" style="width: 100%" @change="stationChange">
+              <el-option v-for="item in stationList" :key="item.id" :value="item.id" :label="item.name" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="modelId">
+            <el-tag>风机型号</el-tag>
+            <el-select v-model="form.modelId" style="width: 100%" @change="modelIdChange">
+              <el-option v-for="item in state.modelList" :key="item" :value="item" :label="item" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="relatedParts">
+            <el-tag>所属部件</el-tag>
+            <el-select v-model="form.relatedParts" style="width: 100%">
+              <el-option v-for="i in state.relatePartList" :key="i.partCode" :value="i.partCode" :label="i.name" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="fault">
+            <el-tag>预警类型</el-tag>
+            <el-select v-model="form.project" style="width: 100%">
+              <el-option v-for="i in faultList" :key="i.value" :value="i.value" :label="i.name" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="9">
+          <el-form-item prop="expressionShow">
+            <el-tag>表达式</el-tag>
+            <el-input type="textarea" rows="14" v-model="form.expressionShow" :value="form.expressionShow"
+              id="expressionInput" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="9">
+          <el-tabs type="border-card">
+            <el-tab-pane label="AI测点">
+              <el-input v-model="state.AIPointSearch"> </el-input>
+              <el-table size="mini" fit :show-header="false" :highlight-current-row="true" :stripe="false"
+                :border="false" height="240" :data="filterAIList" @row-dblclick="rowDbclick">
+                <el-table-column prop="uniformCode" />
+                <el-table-column prop="name" />
+              </el-table>
+            </el-tab-pane>
+            <el-tab-pane label="DI测点">
+              <el-input v-model="state.DIPointSearch"> </el-input>
+              <el-table size="mini" fit :show-header="false" :highlight-current-row="true" :stripe="false"
+                :border="false" height="240" :data="filterDIList" @row-dblclick="rowDbclick">
+                <el-table-column prop="uniformCode" />
+                <el-table-column prop="name" />
+              </el-table>
+            </el-tab-pane>
+            <el-tab-pane label="函数">
+              <el-table size="mini" fit :show-header="false" :highlight-current-row="true" :stripe="false"
+                :border="false" :height="270" :data="func" @row-dblclick="tabFuncRowClickHandle">
+                <el-table-column min-width="60%">
+                  <template #default="scope">
+                    <el-popover trigger="hover" placement="bottom">
+                      <p>描述:{{ scope.row.describe }}</p>
+                      <p>参数:{{ scope.row.param }}</p>
+                      <template #reference>
+                        <span size="medium" transition="fade-in-linear">{{
+                            scope.row.lab
+                        }}</span>
+                      </template>
+                    </el-popover>
+                  </template>
+                </el-table-column>
+                <el-table-column min-width="40%">
+                  <template #default="scope">
+                    <el-popover trigger="hover" placement="bottom">
+                      <p>描述:{{ scope.row.describe }}</p>
+                      <p>参数:{{ scope.row.param }}</p>
+                      <template #reference>
+                        <span size="medium" transition="fade-in-linear">{{
+                            scope.row.name
+                        }}</span>
+                      </template>
+                    </el-popover>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-tab-pane>
+            <el-tab-pane label="运算符">
+              <el-button-group>
+                <el-button v-for="item in operator" :key="item" plain type="primary" @click="elInputSplit(item)"
+                  style="width: 60px; margin: 7px 12px; height: 40px">
+                  {{ item }}
+                </el-button>
+              </el-button-group>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+      <el-row :gutter="24">
+        <el-col :span="24">
+          <el-form-item prop="descriptionShow">
+            <el-tag>规则描述</el-tag>
+            <el-input type="textarea" rows="4" v-model="form.descriptionShow" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="closeDialog">取 消</el-button>
+        <el-button type="primary" @click="submitForm(ruleFormRef)">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, onMounted, reactive, computed, watch, nextTick } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import {
+//   fetchAIPointList,
+//   fetchDIPointList,
+//   fetchRelatePart,
+  custombj_postSave,
+  fetch_electrical_point_ai,
+  fetch_electrical_point_di,
+  getStationinfo,
+} from "/@/api/api.js";
+import { useStore } from "vuex";
+const store = useStore();
+const stationList = computed(() => store.state.stationList);
+onMounted(() => {
+  getfetchRelatePart();
+});
+watch(
+  () => props.isVisible,
+  (val, old) => {
+    isShow.value = val
+  },
+  {
+    deep: true,
+  },
+);
+watch(
+  () => props.form?.id,
+  (val, old) => {
+    if (val != "") {
+      nextTick(async () => {
+        getfetchAIPointList();
+        getfetchDIPointList();
+      });
+    }
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => props.form,
+  (val, old) => {
+    nextTick(() => {
+      form.value = val
+    });
+  },
+  {
+    deep: true,
+  }
+)
+const isShow=ref(false);
+const form = ref({
+  id: "",
+  name: "",
+  descriptionShow: "",
+  expressionShow: "",
+  tag: "",
+  rank: "",
+  enabled: "1", // 1可用-0禁用
+  modelId: "",
+  ednaValue: "",
+  category: "",
+  range: 0,
+  station: "",
+  windturbine: "",
+  line: "",
+  project: "",
+  electrical: "",
+  taskstart: "",
+  relatedParts: "",
+  userName: "",
+})
+const emits = defineEmits(["close"]);
+const props = defineProps({
+  isVisible: {
+    type: Boolean,
+    defaule: false,
+  },
+  form: {
+    type: Object,
+  },
+});
+const toEmits = () => {
+  emits("close"); // 向父组件传递数据
+};
+const state = reactive({
+  relatePartList: [],
+  modelList: [],
+  AIPointList: [],
+  DIPointList: [],
+  AIPointSearch: "",
+  DIPointSearch: "",
+});
+const operator = [
+  "+",
+  "-",
+  "*",
+  "/",
+  "(",
+  ")",
+  ">",
+  ">=",
+  "<",
+  "<=",
+  "==",
+  "!=",
+  "&&",
+  "||",
+  "!",
+  "%",
+  "true",
+  "false",
+  ".",
+];
+const func = [
+  {
+    lab: "MR",
+    name: "移动极差",
+    param: "测点名,时间(秒)",
+    describe: "是指两个或多个连续样本值中最大值与最小值之差",
+    scene: "测点的移动极差超限报警",
+  },
+  {
+    lab: "MAR",
+    name: "均值极差",
+    param: "测点名,时间(秒)",
+    describe: "",
+    scene: "测点的均值极差计算",
+  },
+  {
+    lab: "RiseExceed",
+    name: "上升趋势",
+    param: "测点名,时间(秒),阈值",
+    describe: "取测点在给定的时间范围内数据上升的量是否超过阈值",
+    scene: "测点值的上升速度过快等",
+  },
+  {
+    lab: "Sustain",
+    name: "持续时间",
+    param: "表达式,时间(秒)",
+    describe:
+      "判定状态(表达式成立)持续的时间是否超过给定的时间判断状态持续的时间",
+    scene: "",
+  },
+  {
+    lab: "LastUpdateTime",
+    name: "最近数据时间",
+    param: "测点名",
+    describe: "",
+    scene: "判定离线,状态持续时间等",
+  },
+  {
+    lab: "abs",
+    name: "取绝对值",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "acos",
+    name: "反余弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "asin",
+    name: "反正弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "atan",
+    name: "反正切",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "atan2",
+    name: "xy坐标转为极坐标",
+    param: "x,y",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "ceiling",
+    name: "向上取整",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "cos",
+    name: "余弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "cosh",
+    name: "双曲线余弦",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "exp",
+    name: "欧拉数 e 的 double 次幂的值",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "floor",
+    name: "向下取整",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "log",
+    name: "自然对数",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "log10",
+    name: "底数为 10 的对数",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "max",
+    name: "比较最大值",
+    param: "double a, double b",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "min",
+    name: "比较最小值",
+    param: "double a, double b",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "pow",
+    name: "返回第一个参数的第二个参数次幂的值",
+    param: "double a, double b",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "round",
+    name: "返回最接近参数的 long,或int",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sign",
+    name: "负数返回-1.0,整数返回1.0,0返回0.0",
+    param: "float f/double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sin",
+    name: "三角正弦值",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sinh",
+    name: "双曲线正弦",
+    param: "double x",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "sqrt",
+    name: "正平方根",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "tan",
+    name: "正切",
+    param: "double a",
+    describe: "",
+    scene: "",
+  },
+  {
+    lab: "tanh",
+    name: "双曲线正切",
+    param: "double x",
+    describe: "",
+    scene: "",
+  },
+  { lab: "PI", name: "圆周率", param: "", describe: "", scene: "" },
+  { lab: "E", name: "自然对数", param: "", describe: "", scene: "" },
+];
+const faultList = [
+  {
+    value: "sensors",
+    name: "传感器异常",
+  },
+  {
+    value: "parts",
+    name: "零部件隐患",
+  },
+  {
+    value: "control",
+    name: "控制参数优化",
+  },
+  {
+    value: "performance",
+    name: "性能下降",
+  },
+];
+const ruleFormRef = ref(null);
+const rules = reactive({
+  name: [{ required: true, message: "请输入规则名称", trigger: "blur" }],
+  category: [{ required: true, message: "请选择报警类别", trigger: "change" }],
+  rank: [{ required: true, message: "请选择报警级别", trigger: "change" }],
+  station: [{ required: true, message: "请选择风场", trigger: "change" }],
+  expressionShow: [
+    { required: true, message: "表达式不能为空", trigger: "change" },
+  ],
+});
+
+//stationChange
+const stationChange = async () => {
+  form.value.modelId = "";
+  getequipmentmodel_list();
+};
+//modelIdChange
+const modelIdChange = async () => {
+  getfetchAIPointList();
+  getfetchDIPointList();
+};
+//机型
+const getequipmentmodel_list = async () => {
+  const { data } = await getStationinfo(form.value.station);
+  state.modelList = data[0].modelList;
+};
+//所属部件
+const getfetchRelatePart = async () => {
+  const res = await fetchRelatePart();
+  state.relatePartList = res;
+};
+// 查询风场AI、DI测点
+const getfetchAIPointList = async () => {
+//   const res = await fetchAIPointList(form.value.station, form.value.modelId);
+//   state.AIPointList = res.sort(function (a, b) {
+//     return a.uniformCode - b.uniformCode;
+//   });
+};
+const filterAIList = computed(() =>
+  state.AIPointList?.filter(
+    (data) =>
+      !state.AIPointSearch ||
+      data.uniformCode.includes(state.AIPointSearch) ||
+      data.name.includes(state.AIPointSearch)
+  )
+);
+const filterDIList = computed(() =>
+  state.DIPointList?.filter(
+    (data) =>
+      !state.DIPointSearch ||
+      data.uniformCode.includes(state.DIPointSearch) ||
+      data.name.includes(state.DIPointSearch)
+  )
+);
+const getfetchDIPointList = async () => {
+  const res = await fetchDIPointList(form.value.station, form.value.modelId);
+  state.DIPointList = res.sort(function (a, b) {
+    return a.uniformCode - b.uniformCode;
+  });
+};
+// 函数点击事件
+const tabFuncRowClickHandle = (row) => {
+  let elInput = document.getElementById("expressionInput");
+  let startPos = elInput.selectionStart; //第0个字符到选中的字符
+  let endPos = elInput.selectionEnd; //选中字符到末尾字符
+  if (startPos === undefined || endPos === undefined) return;
+  let txt = elInput.value;
+  let func;
+  if (
+    row.lab === "MR" ||
+    row.lab === "MAR" ||
+    row.lab === "RiseExceed" ||
+    row.lab === "Sustain" ||
+    row.lab === "LastUpdateTime"
+  ) {
+    func = row.lab + "()";
+  } else if (row.lab === "PI" || row.lab === "E") {
+    func = "Math." + row.lab;
+  } else {
+    func = "Math." + row.lab + "()";
+  }
+  // 将插值添加到选中光标位置
+  let result = txt.substring(0, startPos) + func + txt.substring(endPos);
+  elInput.value = result;
+  // 重新定义光标位置
+  elInput.focus();
+  if (row.lab === "PI" || row.lab === "E") {
+    elInput.selectionStart = startPos + func.length;
+    elInput.selectionEnd = startPos + func.length;
+  } else {
+    elInput.selectionStart = startPos + func.length - 1;
+    elInput.selectionEnd = startPos + func.length - 1;
+  }
+  form.value.expressionShow = result; // 赋值给表单中的的字段
+};
+//rowDbclick
+const rowDbclick = (row) => {
+  elInputSplit(row.uniformCode);
+};
+// 表达式字符串拼接
+const elInputSplit = async (val) => {
+  let elInput = document.getElementById("expressionInput");
+  let startPos = elInput.selectionStart;
+  let endPos = elInput.selectionEnd;
+  if (startPos === undefined || endPos === undefined) return;
+  let txt = elInput.value;
+  let txtSplit = val;
+  let result = txt.substring(0, startPos) + txtSplit + txt.substring(endPos);
+  elInput.value = result;
+  elInput.focus();
+  elInput.selectionStart = startPos + txtSplit.length;
+  elInput.selectionEnd = startPos + txtSplit.length;
+};
+//保存
+const save = async () => {
+  const res = await custombj_postSave(form.value);
+  console.warn(res);
+  ElMessage.success(`保存成功!`);
+  closeDialog()
+};
+//提交
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      save()
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+//reset
+const resetForm = (formEl) => {
+  formEl.resetFields();
+};
+//confirm关闭
+const handleClose = () => {
+  ElMessageBox.confirm("确认关闭?")
+    .then(() => {
+      closeDialog();
+    })
+    .catch(() => {
+      // catch error
+    });
+};
+//关闭触发事件
+const closeDialog = () => {
+  resetForm(ruleFormRef.value);
+  state.AIPointList = []
+  state.DIPointList = []
+  toEmits();
+};
+</script>
+<style lang="scss" scoped>
+.col-box {
+  display: flex;
+  flex-direction: column;
+}
+
+.select-mini {
+  width: 120px;
+}
+
+.el-tabs__content {
+  padding: 0 !important;
+}
+
+.el-tabs--border-card {
+  -webkit-box-shadow: none;
+  box-shadow: none;
+}
+
+.border {
+  border: solid red 1px;
+}
+
+.el-table--mini td {
+  padding: 3px 0;
+}
+
+.el-button-group .el-button--primary {
+  border: none;
+}
+
+.el-form--label-top .el-form-item__label {
+  padding: 0;
+}
+</style>

+ 326 - 0
src/pages/baseData/fan_components/faultmode.vue

@@ -0,0 +1,326 @@
+<template>
+  <div>
+    <el-button type="primary" @click="handleEdit(state.addFormData)"
+      >新增</el-button
+    >
+    <el-upload
+      style="display: inline-block; margin: 0 10px 10px 10px"
+      class="upload-demo"
+      action="/sharding/device/faultmode/input"
+      :show-file-list="false"
+      :on-success="handleSuccess"
+      :on-progress="handleProgress"
+      :on-error="handleError"
+    >
+      <el-button type="primary" :disabled="state.faultmodeTableData.length == 0"
+        >导入</el-button
+      >
+    </el-upload>
+    <el-button
+      type="primary"
+      @click="export2Excel"
+      :disabled="state.faultmodeTableData.length == 0"
+      >导出</el-button
+    >
+
+    <el-table
+      :data="state.faultmodeTableData"
+      stripe
+      style="width: 100%"
+      height="700"
+    >
+      <el-table-column type="index" width="50" />
+      <el-table-column prop="name" label="名称"></el-table-column>
+      <el-table-column prop="code" label="故障编码"></el-table-column>
+      <el-table-column label="频度" width="50">
+        <template #default="scope">
+          {{ rank(scope.row.occurence) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="严酷度" width="70">
+        <template #default="scope">
+          {{ rank(scope.row.severity) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="难检度" width="70">
+        <template #default="scope">
+          {{ rank(scope.row.detection) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="cause" label="原因"></el-table-column>
+      <el-table-column prop="effects" label="导致结果"></el-table-column>
+      <el-table-column prop="measure" label="处理方法"></el-table-column>
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="scope">
+          <el-button
+            type="text"
+            icon="el-icon-edit"
+            @click="handleEdit(scope.row)"
+            >编辑</el-button
+          >
+          <el-button
+            type="text"
+            icon="el-icon-delete"
+            style="color: red"
+            @click="handleDelete(scope.row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 传感点编辑弹出框 -->
+    <el-dialog v-model="editVisible" width="40%">
+      <el-form :model="editFormData" label-width="80px">
+        <el-form-item label="故障编码">
+          <el-input v-model="editFormData.code"></el-input>
+        </el-form-item>
+        <el-form-item label="名称">
+          <el-input v-model="editFormData.name"></el-input>
+        </el-form-item>
+        <el-form-item label="导致结果">
+          <el-input
+            v-model="editFormData.effects"
+            :rows="2"
+            clearable
+            type="textarea"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="原因">
+          <el-input
+            v-model="editFormData.cause"
+            :rows="2"
+            clearable
+            type="textarea"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="处理方法">
+          <el-input
+            v-model="editFormData.measure"
+            :rows="3"
+            clearable
+            type="textarea"
+          ></el-input>
+        </el-form-item>
+        <el-row :gutter="24">
+          <el-col :span="12">
+            <el-form-item label="严酷度">
+              <el-select v-model="editFormData.severity">
+                <el-option
+                  v-for="item in selectOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="频度">
+              <el-select v-model="editFormData.occurence">
+                <el-option
+                  v-for="item in selectOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option
+              ></el-select> </el-form-item
+          ></el-col>
+        </el-row>
+        <el-row :gutter="24">
+          <el-col :span="12"
+            ><el-form-item label="难检度">
+              <el-select v-model="editFormData.detection">
+                <el-option
+                  v-for="item in selectOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option
+              ></el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="结构">
+              <el-cascader
+                v-model="editFormData.structurecode"
+                :options="cascaderdata"
+                :props="{ checkStrictly: true, emitPath: false }"
+                clearable
+              /> </el-form-item
+          ></el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="editVisible = false">取 消</el-button>
+          <el-button type="primary" @click="saveEdit">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup>
+import { ref, onMounted, reactive, inject, watch } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+import {
+  faultmode_windturbine,
+  faultmode_structurecode,
+  faultmode_delete,
+  faultmode_add_edit,
+} from "/@/api/api.js";
+import config from "/@/api/config.js";
+let cascaderdata = inject("cascaderdata");
+let desc = inject("desc");
+let treeId = inject("treeId");
+watch(
+  () => treeId,
+  (val, old) => {
+    getfaultmode(desc.value.code);
+  },
+  {
+    deep: true,
+  }
+);
+let editVisible = ref(false);
+let editFormData = ref({});
+onMounted(() => {
+  getfaultmode(desc.value.code);
+});
+const selectV = {
+  1: "I",
+  2: "II",
+  3: "III",
+  4: "IV",
+  5: "V",
+};
+const selectOptions = [
+  {
+    label: "I",
+    value: 1,
+  },
+  {
+    label: "II",
+    value: 2,
+  },
+  {
+    label: "III",
+    value: 3,
+  },
+  {
+    label: "IV",
+    value: 4,
+  },
+  {
+    label: "V",
+    value: 5,
+  },
+];
+const rank = (v) => {
+  return selectV[v] || "";
+};
+let state = reactive({
+  faultmodeTableData: [],
+  addFormData: {
+    code: "",
+    name: "",
+    effects: "",
+    cause: "",
+    measure: "",
+    severity: null,
+    occurence: null,
+    detection: null,
+    deviceid: "windturbine",
+    structurecode: "",
+    models: null,
+  },
+});
+// 新增 编辑
+const handleEdit = (row) => {
+  editFormData.value = row;
+  editVisible.value = true;
+};
+
+// 测点 删除
+const handleDelete = (row) => {
+  // 二次确认删除
+  ElMessageBox.confirm("确定要删除吗?", "提示", {
+    type: "warning",
+  })
+    .then(async () => {
+      console.warn(row);
+      const res = await faultmode_delete(row.id);
+      console.warn(res);
+      if (res == 1) {
+        ElMessage.success(`删除成功!`);
+      } else {
+        ElMessage.success("删除失败");
+      }
+      getfaultmode(desc.value.code);
+    })
+    .catch(() => {});
+};
+// 保存编辑
+const saveEdit = async () => {
+  const res = await faultmode_add_edit(editFormData.value);
+  console.warn(res);
+  if (res.status == 20000) {
+    ElMessage.success(`操作成功!`);
+    editVisible.value = false;
+    getfaultmode(desc.value.code);
+  } else {
+    ElMessage.success(res.msg);
+  }
+};
+// getData
+const getfaultmode = async (code) => {
+  const res = await faultmode_structurecode(code);
+  state.faultmodeTableData = res;
+  console.warn(res);
+};
+
+// 批量导入
+const handleSuccess = (response, file, fileList) => {
+  ElMessage.success(response.msg);
+  getfaultmode(desc.value.code);
+};
+const handleProgress = (response, file, fileList) => {};
+const handleError = (response, file, fileList) => {
+  ElMessage.success("导入失败!");
+};
+// 批量导出
+const export2Excel = () => {
+  const tableData = state.faultmodeTableData;
+  // loading.close();
+  ElMessage.success(`导出成功!`);
+  const tableHeader = [
+    "上级结构编码",
+    "名称",
+    "故障编码",
+    "频度",
+    "严酷度",
+    "难检度",
+    "原因",
+    "导致结果",
+    "处理方法",
+  ];
+  const tableKey = [
+    "structurecode",
+    "name",
+    "code",
+    "occurence",
+    "severity",
+    "detection",
+    "cause",
+    "effects",
+    "measure",
+  ];
+  outExportExcel(tableHeader, tableKey, tableData, "故障模式导出excel");
+};
+
+</script>
+<style lang="scss" scoped>
+</style>

+ 76 - 0
src/pages/baseData/fan_components/message.vue

@@ -0,0 +1,76 @@
+<template>
+  <div style="width: 50%">
+    <el-form :model="desc" label-width="100px">
+      <el-form-item label="结构编码">
+        <el-input v-model="desc.code"></el-input>
+      </el-form-item>
+      <el-form-item label="上级节点">
+        <el-cascader
+          v-model="desc.parentcode"
+          :options="cascaderdata"
+          :props="{ checkStrictly: true, emitPath: false }"
+          :show-all-levels="false"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="设备ID">
+        <el-input v-model="desc.deviceid" disabled></el-input>
+      </el-form-item>
+      <el-form-item label="结构名称">
+        <el-input v-model="desc.name"></el-input>
+      </el-form-item>
+      <el-form-item label="结构英文名称">
+        <el-input v-model="desc.enname"></el-input>
+      </el-form-item>
+       <el-form-item label="英文缩写">
+        <el-input v-model="desc.sortname"></el-input>
+      </el-form-item>
+      <el-form-item label="描述">
+        <el-input
+          v-model="desc.description"
+          :rows="4"
+          clearable
+          type="textarea"
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="序号">
+        <el-input v-model="desc.orderno"></el-input>
+      </el-form-item>
+      <el-form-item label="是否有效">
+        <el-switch
+          v-model="desc.enabled"
+          active-color="#13ce66"
+          inactive-color="#ff4949"
+        />
+      </el-form-item>
+    </el-form>
+    <el-button
+      style="float: right"
+      type="primary"
+      @click="savedesc"
+      :disabled="desc.code == ''"
+      >保存</el-button
+    >
+  </div>
+</template>
+<script setup>
+import { ref, onMounted,  inject } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import {
+ treeAdd
+} from "/@/api/api.js";
+let desc = inject("desc");
+let cascaderdata = inject("cascaderdata");
+const savedesc = async () => {
+  const res = await treeAdd(desc.value);
+  if (res.status == 20000) {
+    ElMessage.success(`操作成功!`);
+  } else {
+    ElMessage.success(res.msg);
+  }
+};
+
+
+</script>
+<style lang="scss" scoped>
+</style>

+ 345 - 0
src/pages/baseData/fan_components/point.vue

@@ -0,0 +1,345 @@
+<template>
+  <div class="devicemenu">
+    <el-space wrap :size="10">
+      <el-button type="primary" @click="handleEdit(state.addFormData)" :disabled="desc.code == ''">新增</el-button>
+      <el-input v-model="state.SearchVal" placeholder="筛选" style="width: 180px" size="small" />
+      <el-select v-model="state.selectModel" class="mr10" style="width: 180px"  placeholder="全部" >
+     <el-option v-for="item in state.modelList" :key="item" :value="item" :label="item"></el-option>
+    </el-select>
+    <el-select v-model="state.selectPoint" class="mr10" style="width: 100px"  placeholder="全部" >
+      <el-option key="1" label="全部" value=""></el-option>
+      <el-option key="2" label="遥测" value="AI"></el-option>
+      <el-option key="3" label="遥调" value="MI"></el-option>
+      <el-option key="4" label="计算" value="CI"></el-option>
+    </el-select>
+      <el-button type="primary" @click="search">查询</el-button>
+      <el-upload class="upload-demo" :disabled="desc.code == ''" action="/sharding/device/input" :show-file-list="false"
+        :on-success="handleSuccess" :on-progress="handleProgress" :on-error="handleError">
+        <el-button :disabled="desc.code == ''" type="primary">导入</el-button>
+      </el-upload>
+      <el-button type="primary" @click="export2Excel" :disabled="desc.code == ''">导出</el-button>
+    </el-space>
+  </div>
+  <el-table :data="state.pointTableData" stripe style="width: 100%" height="65vh">
+    <el-table-column type="index" width="50" />
+    <el-table-column prop="metriccode" label="测点编码"></el-table-column>
+    <el-table-column label="名称">
+      <template #default="scope">
+        <el-popover placement="right" :width="260" trigger="click">
+          <template #reference>
+            <el-button type="text" size="small">{{ scope.row.name }}</el-button>
+          </template>
+          <el-table :data="scope.row.deviceModelMetrics" max-height="400px">
+            <el-table-column property="devicemodel" label="机型" align="center"></el-table-column>
+            <el-table-column property="uniformcode" label="统一编码" align="center"></el-table-column>
+            <el-table-column property="multiplier" label="倍率" align="center"></el-table-column>
+          </el-table>
+        </el-popover>
+      </template>
+    </el-table-column>
+
+    <el-table-column prop="unitname" label="单位"></el-table-column>
+    <el-table-column prop="categorydata" label="测点类型"></el-table-column>
+    <el-table-column prop="categorysci" label="物理类型"></el-table-column>
+    <el-table-column prop="categorysci" label="数据类型"></el-table-column>
+    <el-table-column prop="description" label="描述"></el-table-column>
+    <el-table-column label="操作" width="180" align="center">
+      <template #default="scope">
+        <el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
+        <el-button type="text" icon="el-icon-delete" style="color: red" @click="handleDelete(scope.row)">删除</el-button>
+      </template>
+    </el-table-column>
+  </el-table>
+  <div class="pagination">
+    <el-pagination background layout="total, prev, pager, next" hide-on-single-page :current-page="query.page"
+      :page-size="query.limit" :total="pageTotal" @current-change="handlePageChange"></el-pagination>
+  </div>
+  <!-- 传感点编辑弹出框 -->
+  <el-dialog v-model="editVisible" width="40%">
+    <el-form :model="editFormData" label-width="80px">
+      <el-form-item label="编码">
+        <el-input v-model="editFormData.metriccode"></el-input>
+      </el-form-item>
+      <el-row :gutter="24">
+        <el-col :span="12">
+          <el-form-item label="名称">
+            <el-input v-model="editFormData.name"></el-input>
+          </el-form-item>
+        </el-col>
+
+        <el-col :span="12">
+          <el-form-item label="英文名称">
+            <el-input v-model="editFormData.enname"></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="24">
+        <el-col :span="12">
+          <el-form-item label="标准单位">
+            <el-input v-model="editFormData.unitname"></el-input>
+          </el-form-item>
+        </el-col>
+
+        <el-col :span="12">
+          <el-form-item label="单位名称">
+            <el-input v-model="editFormData.unitnamecn"></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-form-item label="描述">
+        <el-input v-model="editFormData.description" :rows="3" type="textarea" clearable></el-input>
+      </el-form-item>
+      <el-row :gutter="24">
+        <el-col :span="12">
+          <el-form-item label="数据类型">
+            <el-select v-model="editFormData.categorydata" placeholder="Select">
+              <el-option v-for="item in state.categorydataoptions" :key="item" :label="item" :value="item">
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="结构">
+            <el-cascader v-model="editFormData.structurecode" :options="cascaderdata"
+              :props="{ checkStrictly: true, emitPath: false }" clearable />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="24">
+        <el-col :span="12">
+          <el-form-item label="物理类型">
+            <el-input v-model="editFormData.categorysci"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="系统分类">
+            <el-input v-model="editFormData.categorysys"></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-form-item label="适配机型" class="deviceModelClass">
+        <el-select v-model="state.deviceModelSel" style="margin-right: 20px" placeholder="新增机型" size="mini">
+          <el-option v-for="item in state.deviceModel" :key="item.code" :label="item.code" :value="item.code">
+          </el-option>
+        </el-select>
+        <el-button v-show="state.deviceModelSel != ''" size="mini" type="primary" @click="deviceModelAdd()">新增机型
+        </el-button>
+        <el-table :data="editFormData.deviceModelMetrics" style="width: 100%;margin-top: 10px;" max-height="200px">
+          <el-table-column prop="devicemodel" label="机型" />
+          <el-table-column label="倍率">
+            <template #default="scope">
+              <el-input v-model="scope.row.multiplier" size="mini" />
+            </template>
+          </el-table-column>
+          <el-table-column label="统一编码">
+            <template #default="scope">
+              <el-input v-model="scope.row.uniformcode" size="mini" />
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="editVisible = false">取 消</el-button>
+        <el-button type="primary" @click="saveEdit">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script setup>
+import { ref, onMounted, inject, reactive, watch } from "vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import { point, metrics, delmetrics, device_list,getStationinfo } from "/@/api/api.js";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+onMounted(() => {
+  getPoint(desc.value);
+  getDeviceModel();
+  getequipmentmodel_list()
+});
+let editFormData = ref({});
+let editVisible = ref(false); //传感点
+let desc = inject("desc");
+let treeId = inject("treeId");
+let cascaderdata = inject("cascaderdata");
+watch(
+  () => treeId,
+  (newV, old) => {
+    state.addFormData.structurecode = desc.value.code;
+    getPoint(desc.value);
+  },
+  {
+    deep: true,
+  }
+);
+let state = reactive({
+  pointTableData: "",
+  SearchVal: "",
+  selectPoint: "",
+  selectModel: "",
+  modelList: [],
+  categorydataoptions: ['AI', 'DI', 'CI'],
+  addFormData: {
+    categorydata: "AI",
+    categoryres1: null,
+    categoryres2: null,
+    categoryres3: null,
+    categoryres4: null,
+    categoryres5: null,
+    categorysci: null,
+    categorysys: null,
+    description: null,
+    deviceid: "windturbine",
+    enname: null,
+    id: null,
+    metriccode: "",
+    name: "",
+    structurecode: "101010205",
+    unitname: null,
+    unitnamecn: null,
+    deviceModelMetrics: [],
+  },
+});
+const query = reactive({
+  page: 1,
+  limit: 10,
+});
+let pageTotal = ref(0);
+const search = async () => {
+  getPoint(desc.value)
+}
+// getPoint
+const getPoint = async (node) => {
+  const res = await point(node.code, query.page, query.limit, state.selectPoint, state.SearchVal, 'windturbine', state.selectModel);
+  console.warn(res);
+  pageTotal.value = res.total;
+  state.pointTableData = res.records?.sort((a, b) => {
+    return a.id - b.id;
+  });
+};
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await getStationinfo('');
+    let arr = []
+    data.forEach(e => {
+      arr.push(...e.modelList)
+    });
+    state.modelList = [...new Set(arr)]
+};
+// 测点 编辑
+const handleEdit = (row) => {
+  console.warn(row);
+  if (!row.deviceModelMetrics) {
+    row.deviceModelMetrics = [];
+  }
+  editFormData.value = row;
+  editVisible.value = true;
+};
+//测点 add
+const postmetrics = async () => {
+  console.warn(editFormData.value);
+  const res = await metrics(editFormData.value);
+  console.warn(res);
+  if (res.status == 20000) {
+    ElMessage.success(`操作成功!`);
+    editVisible.value = false;
+  } else {
+    ElMessage.success(res.msg);
+  }
+  getPoint(desc.value);
+};
+// 保存编辑
+const saveEdit = async () => {
+  postmetrics();
+};
+// 测点 删除
+const handleDelete = (row) => {
+  // 二次确认删除
+  ElMessageBox.confirm("确定要删除吗?", "提示", {
+    type: "warning",
+  })
+    .then(async () => {
+      console.warn(row);
+      const res = await delmetrics(row.id);
+      console.warn(res);
+      if (res == 1) {
+        ElMessage.success(`删除成功!`);
+      } else {
+        ElMessage.success("删除失败");
+      }
+      getPoint(desc.value);
+    })
+    .catch(() => { });
+};
+
+//deviceModelAdd 新增机型
+const deviceModelAdd = async () => {
+  if (state.deviceModelSel == "") return;
+  console.warn();
+  let obj = {
+    deviceid: editFormData.value.deviceid,
+    devicemodel: state.deviceModelSel,
+    id: null,
+    metriccode: editFormData.value.metriccode,
+    multiplier: 0,
+    uniformcode: "",
+  };
+  editFormData.value.deviceModelMetrics.push(obj);
+};
+//getDeviceModel
+const getDeviceModel = async () => {
+  const res = await device_list();
+  state.deviceModel = res;
+  console.warn(res);
+};
+
+// 分页导航
+const handlePageChange = (val) => {
+  query.page = val;
+  getPoint(desc.value);
+};
+
+// 批量导入
+const handleSuccess = (response, file, fileList) => {
+  console.warn(response, file, fileList);
+  ElMessage.success("导入成功!");
+  getPoint(desc.value);
+};
+const handleProgress = (response, file, fileList) => { };
+const handleError = (response, file, fileList) => {
+  ElMessage.success("导入失败!");
+};
+
+// 批量导出
+const export2Excel = async () => {
+  const tableData = await point(desc.value.code, 1, 99999999, state.selectPoint, state.SearchVal, 'windturbine', state.selectModel);
+  ElMessage.success(`导出成功!`);
+  const tableHeader = [
+    "上级结构编码",
+    "测点编码",
+    "名称",
+    "英文名称",
+    "单位",
+    "测点类型",
+    "物理类型",
+    "描述",
+  ];
+  const tableKey = [
+    "structurecode",
+    "metriccode",
+    "name",
+    "enname",
+    "unitname",
+    "categorydata",
+    "categorysci",
+    "description",
+  ];
+  outExportExcel(tableHeader, tableKey, tableData.records, "测点导出excel");
+};
+</script>
+<style lang="scss" scoped>
+.devicemenu {
+  margin-bottom: 10px;
+}
+</style>

+ 397 - 0
src/pages/baseData/fan_components/windturbine.vue

@@ -0,0 +1,397 @@
+<template>
+    <el-row type="flex" justify="space-between">
+      <div class="handle-box" style="margin-bottom: 10px">
+        <el-button type="primary" icon="el-icon-lx-add" class="handle-edit mr10" @click="handleInsert">
+          新增记录
+        </el-button>
+        <el-button type="primary" icon="el-icon-lx-down" class="handle-edit mr10" @click="export2Excel">
+          批量导出</el-button>
+        <el-button type="primary" icon="el-icon-lx-down" class="handle-edit mr10" @click="outExe">
+          模板下载</el-button>
+        <el-upload class="handle-edit mr10" style=" display: inline;" action="/sharding/warning2/import"
+          :headers="token" :show-file-list="false" :on-success="handleSuccess" :on-progress="handleProgress"
+          :on-error="handleError">
+          <el-button type="primary" icon="el-icon-lx-top" @click="exportShow = true">
+            批量导入</el-button>
+        </el-upload>
+      </div>
+      <div style="display: flex; flex-direction: row; margin-bottom: 10px">
+        <el-select v-if="!isStation" v-model="query.stationId" clearable class="mr10" style="width: 150px"
+          placeholder="全部场站" popper-class="select" @change="changeStation()">
+          <el-option v-for="item in stationList" :key="item.id" :value="item.id" :label="item.name"></el-option>
+        </el-select>
+        <el-select v-model="query.modelId" clearable class="mr10" style="width: 150px" placeholder="全部机型"
+          popper-class="select">
+          <el-option v-for="item in state.modelList" :key="item" :value="item" :label="item"></el-option>
+        </el-select>
+
+        <el-input placeholder="请输入名称" v-model="query.name" style="width: 150px" class="mr10" clearable></el-input>
+        <el-button icon="el-icon-search" type="primary" @click="getData">搜索</el-button>
+      </div>
+    </el-row>
+    <el-table :data="state.tableData" :highlight-current-row="true" border>
+      <el-table-column label="序列号" align="center" prop="id" />
+      <el-table-column label="统一编码" align="center" prop="uniformCode">
+        <template #default="scope">{{
+            // ednaValueConvert(scope.row.ednaValue)
+            scope.row.uniformCode
+        }}</template>
+      </el-table-column>
+      <el-table-column label="名称" align="left" prop="chineseText" width="180" />
+      <el-table-column label="停机类型" align="center" prop="characteristic" />
+      <el-table-column label="风机型号" align="center" prop="modelId" />
+      <el-table-column label="报警类型" align="center" prop="warningTypeId" />
+      <el-table-column label="故障编码" align="center" prop="faultCode" />
+      <el-table-column label="报警级别" align="center" prop="levelId">
+        <template #default="scope">{{
+            levelIdConvert(scope.row.levelId)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="报警分类" align="center" prop="warningClassIfyId" min-width="50">
+        <template #default="scope">{{
+            warningClassIfyIdConvert(scope.row.warningClassIfyId)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="是否可以复位" align="center" prop="isreset" min-width="50">
+        <template #default="scope">{{
+            isresetConvert(scope.row.isreset)
+        }}</template>
+      </el-table-column>
+      <el-table-column label="是否启用" align="center" prop="display" min-width="50">
+        <template #default="scope">{{
+            scope.row.enabled == 1?'是':'否'
+        }}</template>
+      </el-table-column>
+      <el-table-column label="关联部件" align="center" prop="relatedParts" min-width="50">
+        <template #default="scope">
+          <span>{{ relatePartConvert(scope.row.relatedParts) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="100">
+        <template #default="scope">
+          <el-button type="primary" plain round size="mini" icon="el-icon-lx-edit" @click="handleEditClick(scope.row)">
+            编辑</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination background layout="total, prev, pager, next" hide-on-single-page :current-page="query.pagenum"
+        :page-size="query.pagesize" :total="total" @current-change="handlePageChange"></el-pagination>
+    </div>
+    <windturbinecomponents @close="dialogclose" :isVisible="state.visible" :form="state.form" />
+</template>
+<script setup>
+import { windturbinebj_fetchTableData, getStationinfo } from "/@/api/api.js";
+import { outExportExcel } from "/@/utils/exportExcel"; //引入文件
+import { ref, onMounted, provide, computed, reactive, watch } from "vue";
+import { useStore } from "vuex";
+import { ElMessageBox, ElMessage } from "element-plus";
+import windturbinecomponents from "./windturbine_components.vue";
+const token = { 'token': sessionStorage.getItem('token') }
+const store = useStore();
+const stationList = computed(() => store.state.stationList);
+const isStation = computed(() => store.getters.isStation);
+const state = reactive({
+  tableData: [],
+  modelList: [],
+  visible: false,
+  form: {},
+});
+const query = reactive({
+  pagenum: 1,
+  pagesize: 10,
+  stationId: "",
+  modelId: "",
+  name: "",
+});
+let total = ref(0);
+onMounted(() => {
+  getData();
+  getequipmentmodel_list()
+});
+const dialogclose = () => {
+  state.visible = false
+  getData()
+}
+const getData = async () => {
+  const res = await windturbinebj_fetchTableData(query);
+  state.tableData = res.records;
+  total.value = res.total;
+};
+const handleInsert = () => {
+  state.visible = true;
+};
+const handleEditClick = (row) => {
+  console.warn(row);
+  let obj = Object.assign({}, row)
+  state.form = obj;
+  state.visible = true;
+};
+//changeStation
+const changeStation = async () => {
+  query.modelId = "";
+  await getequipmentmodel_list();
+};
+// 机型
+const getequipmentmodel_list = async () => {
+  const { data } = await getStationinfo(query.stationId);
+  if (query.stationId == '') {
+    let arr = []
+    data.forEach(e => {
+      arr.push(...e.pointModelList)
+    });
+    state.modelList = [...new Set(arr)]
+  } else {
+    state.modelList = data[0].pointModelList;
+  }
+};
+// 分页导航
+const handlePageChange = (val) => {
+  query.pagenum = val;
+  getData();
+};
+
+const tableHeader = [
+  "id",
+  "报警级别(ZC_BJ:运转正常;YJ_BJ:运转状态超出标准范围;GZ_BJ:非正常停止运转;WH_BJ:停机维护;XD_BJ:限电/计划停机)",
+  "制造商(HFYG_CS:合肥阳光电源有限公司;AMS_CS:艾默生;NRJD_CS:国电南瑞吉电新能源;MGSK_CS:美国赛康;LHDL_CS:联合动力;SYHC_CS:沈阳华创)",
+  "名称",
+  "上一级节点",
+  "是否有子节点",
+  "序列号",
+  "类别 :正常停机,正常启动,快速停机,紧急停机",
+  "风机型号",
+  "全部默认为1",
+  "统一编码",
+  "是否展示(0:是;1:否)",
+  "报警分类(bj:变桨;bpq:变频器;clx:齿轮箱;fdj:发电机;hh:滑环;jcjr:机舱加热;lq:冷却;ph:偏航;rh:润滑;yy:液压;zz:主轴;other:其它;kz:控制;yl:叶轮;jc:机舱;xb:箱变;dw:电网;td:塔底;fsy:风速仪)",
+  "消缺规定时间",
+  "是否可以复位(0:是;1:否)",
+  "报警类型编号",
+  "故障编码",
+  "关联部件",
+];
+
+const tableKey = [
+  "id",
+  "levelId",
+  "manufacturerCode",
+  "chineseText",
+  "parentId",
+  "isleaf",
+  "sequenceNumber",
+  "characteristic",
+  "modelId",
+  "codeName",
+  "ednaValue",
+  "display",
+  "warningClassIfyId",
+  "standardTime",
+  "isreset",
+  "warningTypeId",
+  "faultCode",
+  "relatedParts",
+];
+// 批量导出
+const export2Excel = async () => {
+  let { name, stationId, modelId } = query;
+  const res = await windturbinebj_fetchTableData({
+    pagenum: 1,
+    pagesize: 999999,
+    name,
+    stationId,
+    modelId,
+  });
+
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, res.records, "风机报警配置导出excel");
+};
+// 模板下载
+const outExe = () => {
+  const data = [];
+  ElMessage.success(`导出成功!`);
+  outExportExcel(tableHeader, tableKey, data, "风机报警模板");
+};
+// 批量导入
+const handleSuccess = (response, file, fileList) => {
+  ElMessage.success("导入成功!");
+  getData();
+};
+const handleProgress = (response, file, fileList) => { };
+const handleError = (response, file, fileList) => {
+  ElMessage.success("导入失败!");
+};
+// 报警级别
+const levelIdConvert = (val) => {
+  switch (val) {
+    case "ZC_BJ":
+      return "运转正常";
+      break;
+    case "YJ_BJ":
+      return "运转状态超出标准范围";
+      break;
+    case "GZ_BJ":
+      return "非正常停止运转";
+      break;
+    case "WH_BJ":
+      return "停机维护";
+      break;
+    case "XD_BJ":
+      return "限电/计划停机";
+      break;
+    default:
+      return;
+  }
+};
+// 报警分类
+const warningClassIfyIdConvert = (val) => {
+  switch (val) {
+    case "bj":
+      return "变桨";
+      break;
+    case "bpq":
+      return "变频器";
+      break;
+    case "clx":
+      return "齿轮箱";
+      break;
+    case "fdj":
+      return "发电机";
+      break;
+    case "hh":
+      return "滑环";
+      break;
+    case "jcjr":
+      return "机舱加热";
+      break;
+    case "lq":
+      return "冷却";
+      break;
+    case "ph":
+      return "偏航";
+      break;
+    case "rh":
+      return "润滑";
+      break;
+    case "yy":
+      return "液压";
+      break;
+    case "zz":
+      return "主轴";
+      break;
+    case "other":
+      return "其它";
+      break;
+    case "kz":
+      return "控制";
+      break;
+    case "yl":
+      return "叶轮";
+      break;
+    case "jc":
+      return "机舱";
+      break;
+    case "xb":
+      return "箱变";
+      break;
+    case "dw":
+      return "电网";
+      break;
+    case "td":
+      return "塔底";
+      break;
+    case "fsy":
+      return "风速仪";
+      break;
+  }
+};
+// 是否可以复位
+const isresetConvert = (val) => {
+  switch (val) {
+    case 0:
+      return "是";
+      break;
+    case 1:
+      return "否";
+      break;
+  }
+};
+
+// 统一编码
+const ednaValueConvert = (val) => {
+  if (val === 0) {
+    return "";
+  } else if (val < 100 && val > 0) {
+    return "DI0" + val;
+  } else if (val >= 100) {
+    return "DI" + val;
+  }
+};
+// 关联部件
+const relatePartConvert = (val) => {
+  switch (val) {
+    case "YP":
+      return "叶片";
+      break;
+    case "LG":
+      return "轮毂";
+      break;
+    case "TZ":
+      return "塔架";
+      break;
+    case "JC":
+      return "机舱";
+      break;
+    case "KZXT":
+      return "控制系统";
+      break;
+    case "BJXT":
+      return "变桨系统";
+      break;
+    case "PHXT":
+      return "偏航系统";
+      break;
+    case "CLX":
+      return "齿轮箱";
+      break;
+    case "FDJ":
+      return "发电机";
+      return;
+    case "BPQ":
+      return "变频器";
+      return;
+    case "YYXT":
+      return "液压系统";
+      break;
+    case "FZXT":
+      return "辅助系统";
+      break;
+    case "CFXT":
+      return "测风系统";
+      break;
+    case "DWXT":
+      return "电网系统";
+      break;
+    case "TDG":
+      return "塔底柜";
+      break;
+    case "CDL":
+      return "传动链";
+      break;
+    case "QT":
+      return "其他";
+      break;
+    default:
+      break;
+  }
+};
+</script>
+<style scoped>
+.mr10 {
+  margin-right: 10px;
+}
+
+.el-button+.el-button {
+  margin-left: 10px;
+}
+</style>

+ 0 - 0
src/pages/baseData/fan_components/windturbine_components.vue


Some files were not shown because too many files changed in this diff