zhulizhen 6 years ago
commit
12f21788fd
100 changed files with 49136 additions and 0 deletions
  1. 18 0
      .babelrc
  2. 9 0
      .editorconfig
  3. 5 0
      .eslintignore
  4. 29 0
      .eslintrc.js
  5. 17 0
      .gitignore
  6. 10 0
      .postcssrc.js
  7. 32 0
      README.md
  8. 41 0
      build/build.js
  9. 54 0
      build/check-versions.js
  10. 101 0
      build/utils.js
  11. 22 0
      build/vue-loader.conf.js
  12. 96 0
      build/webpack.base.conf.js
  13. 95 0
      build/webpack.dev.conf.js
  14. 149 0
      build/webpack.prod.conf.js
  15. 7 0
      config/dev.env.js
  16. 99 0
      config/index.js
  17. 4 0
      config/prod.env.js
  18. 7 0
      config/test.env.js
  19. 14 0
      index.html
  20. 39001 0
      package-lock.json
  21. 97 0
      package.json
  22. 21 0
      src/App.vue
  23. 6 0
      src/assets/css/public.css
  24. 65 0
      src/assets/css/reset.css
  25. 143 0
      src/components/Head.vue
  26. 58 0
      src/components/Home.vue
  27. 206 0
      src/components/Login.vue
  28. 42 0
      src/components/NoTableData.vue
  29. 19 0
      src/main.js
  30. 7 0
      src/modules/energyConsumption/index.js
  31. 75 0
      src/modules/energyConsumption/pages/asideMenu/index.vue
  32. 73 0
      src/modules/energyConsumption/pages/home/index.vue
  33. 43 0
      src/modules/energyConsumption/pages/main/index.vue
  34. 16 0
      src/modules/energyConsumption/router/index.js
  35. 15 0
      src/modules/energyConsumption/store/index.js
  36. 73 0
      src/modules/environment/component/reference.vue
  37. 7 0
      src/modules/environment/index.js
  38. 205 0
      src/modules/environment/pages/analys/index.vue
  39. 71 0
      src/modules/environment/pages/asideMenu/index.vue
  40. 184 0
      src/modules/environment/pages/heal/RoomReport.vue
  41. 200 0
      src/modules/environment/pages/heal/RoomTable.vue
  42. 357 0
      src/modules/environment/pages/heal/index.vue
  43. 67 0
      src/modules/environment/pages/home/index.vue
  44. 45 0
      src/modules/environment/pages/main/hoverDiv.vue
  45. 547 0
      src/modules/environment/pages/main/index.vue
  46. 107 0
      src/modules/environment/pages/main/temperatureBar.vue
  47. 89 0
      src/modules/environment/pages/main/temperatureTable.vue
  48. 42 0
      src/modules/environment/pages/mass/index.vue
  49. 42 0
      src/modules/environment/pages/plane/index.vue
  50. 38 0
      src/modules/environment/router/index.js
  51. 15 0
      src/modules/environment/store/index.js
  52. 7 0
      src/modules/facilitiesAndStaff/index.js
  53. 89 0
      src/modules/facilitiesAndStaff/pages/asideMenu/index.vue
  54. 53 0
      src/modules/facilitiesAndStaff/pages/equipmentView/Card.vue
  55. 42 0
      src/modules/facilitiesAndStaff/pages/equipmentView/Detail.vue
  56. 578 0
      src/modules/facilitiesAndStaff/pages/equipmentView/index.vue
  57. 66 0
      src/modules/facilitiesAndStaff/pages/home/index.vue
  58. 757 0
      src/modules/facilitiesAndStaff/pages/orderInfoView/index.vue
  59. 39 0
      src/modules/facilitiesAndStaff/pages/peopleView/GiveMark.vue
  60. 199 0
      src/modules/facilitiesAndStaff/pages/peopleView/OnlinePeople.vue
  61. 289 0
      src/modules/facilitiesAndStaff/pages/peopleView/OnlinePeopleDetail.vue
  62. 332 0
      src/modules/facilitiesAndStaff/pages/peopleView/Worker.vue
  63. 156 0
      src/modules/facilitiesAndStaff/pages/peopleView/index.vue
  64. 34 0
      src/modules/facilitiesAndStaff/router/index.js
  65. 14 0
      src/modules/facilitiesAndStaff/store/index.js
  66. 7 0
      src/modules/firstPage/index.js
  67. 124 0
      src/modules/firstPage/pages/asideMenu/index.vue
  68. 62 0
      src/modules/firstPage/pages/home/index.vue
  69. 174 0
      src/modules/firstPage/pages/main/Energy.vue
  70. 171 0
      src/modules/firstPage/pages/main/SgyHealthyIndex.vue
  71. 119 0
      src/modules/firstPage/pages/main/SgyMarquee.vue
  72. 181 0
      src/modules/firstPage/pages/main/Staff.vue
  73. 84 0
      src/modules/firstPage/pages/main/baseInfo.vue
  74. 140 0
      src/modules/firstPage/pages/main/enviromentAndSafe.vue
  75. 159 0
      src/modules/firstPage/pages/main/equipment.vue
  76. 69 0
      src/modules/firstPage/pages/main/index.vue
  77. 203 0
      src/modules/firstPage/pages/main/newActive.vue
  78. 13 0
      src/modules/firstPage/router/index.js
  79. 15 0
      src/modules/firstPage/store/index.js
  80. 7 0
      src/modules/performance/index.js
  81. 71 0
      src/modules/performance/pages/asideMenu/index.vue
  82. 66 0
      src/modules/performance/pages/home/index.vue
  83. 46 0
      src/modules/performance/pages/kpi/index.vue
  84. 42 0
      src/modules/performance/pages/mark/index.vue
  85. 42 0
      src/modules/performance/pages/suggest/index.vue
  86. 34 0
      src/modules/performance/router/index.js
  87. 15 0
      src/modules/performance/store/index.js
  88. 7 0
      src/modules/safety/index.js
  89. 77 0
      src/modules/safety/pages/asideMenu/index.vue
  90. 186 0
      src/modules/safety/pages/detail/AlarmReport.vue
  91. 183 0
      src/modules/safety/pages/detail/EqWarn.vue
  92. 323 0
      src/modules/safety/pages/detail/index.vue
  93. 180 0
      src/modules/safety/pages/history/index.vue
  94. 62 0
      src/modules/safety/pages/home/index.vue
  95. 42 0
      src/modules/safety/pages/statistics/index.vue
  96. 283 0
      src/modules/safety/pages/summary/detect.vue
  97. 457 0
      src/modules/safety/pages/summary/index.vue
  98. 41 0
      src/modules/safety/router/index.js
  99. 11 0
      src/modules/safety/store/index.js
  100. 0 0
      src/modules/test/transition.vue

+ 18 - 0
.babelrc

@@ -0,0 +1,18 @@
+{
+  "presets": [
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }],
+    "stage-2"
+  ],
+  "plugins": ["transform-vue-jsx", "transform-runtime"],
+  "env": {
+    "test": {
+      "presets": ["env", "stage-2"],
+      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
+    }
+  }
+}

+ 9 - 0
.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 5 - 0
.eslintignore

@@ -0,0 +1,5 @@
+/build/
+/config/
+/dist/
+/*.js
+/test/unit/coverage/

+ 29 - 0
.eslintrc.js

@@ -0,0 +1,29 @@
+// https://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  env: {
+    browser: true,
+  },
+  extends: [
+    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
+    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
+    'plugin:vue/essential', 
+    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
+    'standard'
+  ],
+  // required to lint *.vue files
+  plugins: [
+    'vue'
+  ],
+  // add your custom rules here
+  rules: {
+    // allow async-await
+    'generator-star-spacing': 'off',
+    // allow debugger during development
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
+  }
+}

+ 17 - 0
.gitignore

@@ -0,0 +1,17 @@
+.DS_Store
+node_modules/
+/dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+/test/unit/coverage/
+/test/e2e/reports/
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln

+ 10 - 0
.postcssrc.js

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

+ 32 - 0
README.md

@@ -0,0 +1,32 @@
+# wanke-project
+
+>基于vue.js 2+ 的项目
+
+## Build Setup
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+
+# run unit tests
+npm run unit
+
+# run e2e tests
+npm run e2e
+
+# run all tests
+npm test
+```
+
+1. 使用vue + vueRouter + vuex + axios;
+2. 模块化目录结构,耦合度低。
+3. 

+ 41 - 0
build/build.js

@@ -0,0 +1,41 @@
+'use strict'
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf')
+
+const spinner = ora('building for production...')
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+})

+ 54 - 0
build/check-versions.js

@@ -0,0 +1,54 @@
+'use strict'
+const chalk = require('chalk')
+const semver = require('semver')
+const packageConfig = require('../package.json')
+const shell = require('shelljs')
+
+function exec (cmd) {
+  return require('child_process').execSync(cmd).toString().trim()
+}
+
+const versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  }
+]
+
+if (shell.which('npm')) {
+  versionRequirements.push({
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  })
+}
+
+module.exports = function () {
+  const warnings = []
+
+  for (let i = 0; i < versionRequirements.length; i++) {
+    const mod = versionRequirements[i]
+
+    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
+      warnings.push(mod.name + ': ' +
+        chalk.red(mod.currentVersion) + ' should be ' +
+        chalk.green(mod.versionRequirement)
+      )
+    }
+  }
+
+  if (warnings.length) {
+    console.log('')
+    console.log(chalk.yellow('To use this template, you must update following to modules:'))
+    console.log()
+
+    for (let i = 0; i < warnings.length; i++) {
+      const warning = warnings[i]
+      console.log('  ' + warning)
+    }
+
+    console.log()
+    process.exit(1)
+  }
+}

+ 101 - 0
build/utils.js

@@ -0,0 +1,101 @@
+'use strict'
+const path = require('path')
+const config = require('../config')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const packageConfig = require('../package.json')
+
+exports.assetsPath = function (_path) {
+  const assetsSubDirectory = process.env.NODE_ENV === 'production'
+    ? config.build.assetsSubDirectory
+    : config.dev.assetsSubDirectory
+
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function (options) {
+  options = options || {}
+
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  const postcssLoader = {
+    loader: 'postcss-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loader, loaderOptions) {
+    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
+
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract({
+        use: loaders,
+        fallback: 'vue-style-loader'
+      })
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  const output = []
+  const loaders = exports.cssLoaders(options)
+
+  for (const extension in loaders) {
+    const loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+
+  return output
+}
+
+exports.createNotifierCallback = () => {
+  const notifier = require('node-notifier')
+
+  return (severity, errors) => {
+    if (severity !== 'error') return
+
+    const error = errors[0]
+    const filename = error.file && error.file.split('!').pop()
+
+    notifier.notify({
+      title: packageConfig.name,
+      message: severity + ': ' + error.name,
+      subtitle: filename || '',
+      icon: path.join(__dirname, 'logo.png')
+    })
+  }
+}

+ 22 - 0
build/vue-loader.conf.js

@@ -0,0 +1,22 @@
+'use strict'
+const utils = require('./utils')
+const config = require('../config')
+const isProduction = process.env.NODE_ENV === 'production'
+const sourceMapEnabled = isProduction
+  ? config.build.productionSourceMap
+  : config.dev.cssSourceMap
+
+module.exports = {
+  loaders: utils.cssLoaders({
+    sourceMap: sourceMapEnabled,
+    extract: isProduction
+  }),
+  cssSourceMap: sourceMapEnabled,
+  cacheBusting: config.dev.cacheBusting,
+  transformToRequire: {
+    video: ['src', 'poster'],
+    source: 'src',
+    img: 'src',
+    image: 'xlink:href'
+  }
+}

+ 96 - 0
build/webpack.base.conf.js

@@ -0,0 +1,96 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const config = require('../config')
+const vueLoaderConfig = require('./vue-loader.conf')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const createLintingRule = () => ({
+  test: /\.(js|vue)$/,
+  loader: 'eslint-loader',
+  enforce: 'pre',
+  include: [resolve('src'), resolve('test')],
+  options: {
+    formatter: require('eslint-friendly-formatter'),
+    emitWarning: !config.dev.showEslintErrorsInOverlay
+  }
+})
+
+module.exports = {
+  context: path.resolve(__dirname, '../'),
+  entry: {
+    app: ['babel-polyfill', 'event-source-polyfill','./src/main.js']
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath: process.env.NODE_ENV === 'production'
+      ? config.build.assetsPublicPath
+      : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src'),
+    }
+  },
+  module: {
+    rules: [
+      ...(config.dev.useEslint ? [createLintingRule()] : []),
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.less$/,
+        loader: "style-loader!css-loader!less-loader",
+      }
+    ]
+  },
+  node: {
+    // prevent webpack from injecting useless setImmediate polyfill because Vue
+    // source contains it (although only uses it if it's native).
+    setImmediate: false,
+    // prevent webpack from injecting mocks to Node native modules
+    // that does not make sense for the client
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  }
+}

+ 95 - 0
build/webpack.dev.conf.js

@@ -0,0 +1,95 @@
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const path = require('path')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+
+const HOST = process.env.HOST
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: {
+      rewrites: [
+        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
+      ],
+    },
+    hot: true,
+    contentBase: false, // since we use CopyWebpackPlugin.
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env')
+    }),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.dev.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+        compilationSuccessInfo: {
+          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+        },
+        onErrors: config.dev.notifyOnErrors
+        ? utils.createNotifierCallback()
+        : undefined
+      }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 149 - 0
build/webpack.prod.conf.js

@@ -0,0 +1,149 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
+
+const env = process.env.NODE_ENV === 'testing'
+  ? require('../config/test.env')
+  : require('../config/prod.env')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true,
+      usePostCSS: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
+      },
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: true,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: process.env.NODE_ENV === 'testing'
+        ? 'index.html'
+        : config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vendor modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks (module) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 7 - 0
config/dev.env.js

@@ -0,0 +1,7 @@
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+  NODE_ENV: '"development"'
+})

+ 99 - 0
config/index.js

@@ -0,0 +1,99 @@
+'use strict'
+// Template version: 1.3.1
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+
+module.exports = {
+  dev: {
+
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {
+      '/api': {
+          target:"http://service.sagacloud.cn:28888",//设置你调用的接口域名和端口号 别忘了加http
+          //changeOrigin: true,
+          pathRewrite: {
+              '^/api': '/' // 这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/user/add',直接写‘/api/user/add’即可
+          }
+      },
+      '/equipment': {
+        target:"http://172.17.134.225:8888",//设备设施代理
+        changeOrigin: true,
+        pathRewrite: {
+          '^/equipment': '/' // 这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/user/add',直接写‘/api/user/add’即可
+        }
+      },
+      '/enviroment':{
+        target:"http://service.sagacloud.cn:28888",
+        changeOrigin:true,
+        pathRewrite: {
+          '^/enviroment': '/' // 这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/user/add',直接写‘/api/user/add’即可
+        }
+      }
+  },
+
+    // Various Dev Server settings
+    host: 'localhost', // can be overwritten by process.env.HOST
+    disableHostCheck: true,
+    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+    autoOpenBrowser: false,
+    errorOverlay: true,
+    notifyOnErrors: true,
+    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+    // Use Eslint Loader?
+    // If true, your code will be linted during bundling and
+    // linting errors and warnings will be shown in the console.
+    useEslint: false,
+    // If true, eslint errors and warnings will also be shown in the error overlay
+    // in the browser.
+    showEslintErrorsInOverlay: false,
+
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: 'cheap-module-eval-source-map',
+
+    // If you have problems debugging vue-files in devtools,
+    // set this to false - it *may* help
+    // https://vue-loader.vuejs.org/en/options.html#cachebusting
+    cacheBusting: true,
+
+    cssSourceMap: true
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: true,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: '#source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  }
+}

+ 4 - 0
config/prod.env.js

@@ -0,0 +1,4 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"'
+}

+ 7 - 0
config/test.env.js

@@ -0,0 +1,7 @@
+'use strict'
+const merge = require('webpack-merge')
+const devEnv = require('./dev.env')
+
+module.exports = merge(devEnv, {
+  NODE_ENV: '"testing"'
+})

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <title>wanke-project</title>
+    <link rel="stylesheet" type="text/css" href="/static/font/iconfont.css">
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

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


+ 97 - 0
package.json

@@ -0,0 +1,97 @@
+{
+  "name": "wanke-project",
+  "version": "1.0.0",
+  "description": "A Vue.js project",
+  "author": "zlz",
+  "private": true,
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "start": "npm run dev",
+    "unit": "jest --config test/unit/jest.conf.js --coverage",
+    "e2e": "node test/e2e/runner.js",
+    "test": "npm run unit && npm run e2e",
+    "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
+    "build": "node build/build.js"
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "echarts": "^4.2.0-rc.1",
+    "iview": "^3.1.1-rc.1",
+    "jquery": "^3.3.1",
+    "svg-sprite-loader": "^4.1.2",
+    "vue": "^2.5.2",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.0.1"
+  },
+  "devDependencies": {
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-eslint": "^8.2.1",
+    "babel-helper-vue-jsx-merge-props": "^2.0.3",
+    "babel-jest": "^21.0.2",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-dynamic-import-node": "^1.2.0",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.5.0",
+    "babel-polyfill": "^6.26.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "babel-register": "^6.22.0",
+    "chalk": "^2.0.1",
+    "chromedriver": "^2.27.2",
+    "copy-webpack-plugin": "^4.0.1",
+    "cross-spawn": "^5.0.1",
+    "css-loader": "^0.28.0",
+    "eslint": "^4.15.0",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^1.7.1",
+    "eslint-plugin-import": "^2.14.0",
+    "eslint-plugin-node": "^5.2.0",
+    "eslint-plugin-promise": "^3.4.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "eslint-plugin-vue": "^4.0.0",
+    "event-source-polyfill": "^1.0.4",
+    "extract-text-webpack-plugin": "^3.0.0",
+    "file-loader": "^1.1.4",
+    "friendly-errors-webpack-plugin": "^1.6.1",
+    "html-webpack-plugin": "^2.30.1",
+    "jest": "^22.0.4",
+    "jest-serializer-vue": "^0.3.0",
+    "less": "^3.8.1",
+    "less-loader": "^4.1.0",
+    "nightwatch": "^0.9.12",
+    "node-notifier": "^5.1.2",
+    "optimize-css-assets-webpack-plugin": "^3.2.0",
+    "ora": "^1.2.0",
+    "portfinder": "^1.0.13",
+    "postcss-import": "^11.0.0",
+    "postcss-loader": "^2.0.8",
+    "postcss-url": "^7.2.1",
+    "rimraf": "^2.6.0",
+    "selenium-server": "^3.0.1",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "uglifyjs-webpack-plugin": "^1.1.1",
+    "url-loader": "^0.5.8",
+    "vue-jest": "^1.0.2",
+    "vue-loader": "^13.3.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.5.2",
+    "webpack": "^3.6.0",
+    "webpack-bundle-analyzer": "^2.9.0",
+    "webpack-dev-server": "^2.9.1",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 6.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}

+ 21 - 0
src/App.vue

@@ -0,0 +1,21 @@
+<template>
+  <div id="app">
+    <keep-alive>
+      <router-view v-if="!$route.meta.keep_alive"/>
+    </keep-alive>
+  </div>
+</template>
+
+<script>
+
+
+export default {
+  name: 'App'
+}
+</script>
+<style lang="less">
+#app {
+  position: relative;
+  margin: 0 auto;
+}
+</style>

+ 6 - 0
src/assets/css/public.css

@@ -0,0 +1,6 @@
+.remark-icon{
+    width:6px;
+    height: 20px;
+    display: inline-block;
+    background:#4A9EF9; 
+}

+ 65 - 0
src/assets/css/reset.css

@@ -0,0 +1,65 @@
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font: inherit;
+  vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+  display: block;
+}
+body {
+  background: #FFFFFF;
+  line-height: 1;
+  font-size: 16px;
+  /*使用简书的那一套字体
+    简书优先选择了lucida家族的系列字体作为英文字体,该系列字体在Mac和Win上都是预装的,并且有着不俗的表现;
+    中文字体方面将冬青黑体作为最优先使用的字体,同样考虑了Linux系统。
+  */
+  font-family: "Microsoft YaHei", "lucida grande", "lucida sans unicode", lucida, helvetica, "Hiragino Sans GB", "WenQuanYi Micro Hei", sans-serif;
+  -webkit-font-smoothing: subpixel-antialiased;
+  -moz-osx-font-smoothing: auto;
+}
+ol, ul {
+  list-style: none;
+}
+blockquote, q {
+  quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+  content: '';
+  content: none;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+a {
+  text-decoration: none;
+  color: black;
+}
+html,body {
+  height: 100%;
+}
+/*消除window、mac系统差异,windows border-width:2px mac: border-width:1px*/
+button {
+  border-width: 0;
+}
+* {
+  box-sizing: border-box;
+}

+ 143 - 0
src/components/Head.vue

@@ -0,0 +1,143 @@
+<template>
+  <div class="my-head">
+    <img class="my-img" src="/static/img/img1.png">
+    <div class="vanke">
+          <img class="off-img" src="/static/img/1.png">
+      </div>
+    <div class="box">
+      <div class="left">
+      <ul>
+        <li><router-link to="/home/">首页</router-link></li>
+        <li><router-link to="/staff/">设施与人员</router-link></li>
+        <li><router-link to="/environment/">环境</router-link></li>
+        <li><router-link to="/safety/">设备安全</router-link></li>
+        <li><a class="hover-active" :href="`${iSagy}`" target="_blank">能耗</a></li>
+        <li><router-link to="/performance/">项目绩效管理</router-link></li>
+      </ul>
+       <div class="right">
+      <ul>
+        <li><i class="iconfont icon-search"></i></li>
+        <li><i class="iconfont icon-dangqianbaojing"></i></li>
+        <li><i class="iconfont icon-message"></i></li>
+        <li><i class="iconfont icon-more"></i></li>
+        <li>测试人员<Icon type="md-arrow-dropdown" /></li>
+      </ul>
+    </div>
+    </div>
+   
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios';
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+      iSagy:""
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+    this.getSubSystemUrl();
+  },
+
+  methods: {
+    getSubSystemUrl(){
+     axios.get(`/equipment/info-mng-backend/config/getSubSystemUrl?projectId=${this.$store.state.projectId}`).then(res=>{
+       let data = res.data.data;
+       this.iSagy = data.iSagy;
+     })
+   }
+   
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.my-head{
+  display:flex;
+  align-items: center;
+  border-bottom: 1px solid #ccc;
+  border-left: 1px solid #ccc;
+  border-right: 1px solid #ccc;
+  box-shadow: 0 1px 2px 0 rgba(11,13,17,0.05);
+  width: 100%;
+  overflow: hidden;
+  font-size: 20px;
+  .my-img{
+    width:78px;
+    height:100%;
+  }
+  .vanke{
+      display: inline-block;
+      height:100%;
+      color:#fff;
+      font-size: 33.4px;
+      padding:5px 0 5px 30px; 
+      background: #374045;
+      .off-img{
+        width:98px;
+        height:100%;
+      }
+    }
+  .box{
+    background: #374045;
+    width: 100%;
+    height:100%;
+    padding-left: 40px;
+    .left{
+    ul {
+      display:inline-block;
+      li{
+        display: inline-block;
+        font-weight: bold;
+        color: #fff;
+        //font-size: 14px;
+        cursor: pointer;
+        a{
+          display:inline-block;
+          padding: 14px 10px;
+          color: #fff;
+         // font-size: 14px;
+          margin: 0 12.5px;
+        }
+        .router-link-active{
+          font-weight: bolder;
+          background: #333;
+        }
+      }
+    }
+  }
+  .right{
+     display:inline-block;
+     float: right;
+     padding-right: 50px;
+    ul{
+      li{
+        display:inline-block;
+        padding: 14px 10px;
+        //font-size: 14px;
+        color:#fff;
+        margin: 0 12.5px;
+      }
+    }
+  }
+  }
+}
+</style>

+ 58 - 0
src/components/Home.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="global-home">
+      <my-head class="my-head"></my-head>
+      <div class="content">
+        <router-view>
+        </router-view>
+      </div>
+  </div>
+</template>
+
+<script>
+import MyHead from './Head.vue'
+export default {
+  components: {
+    MyHead
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.global-home {
+  font-size: 15px;
+  font-family: "Microsoft YaHei";
+  .my-head {
+    height: 50px;
+    line-height: 20px;
+    position: fixed;
+    z-index: 999;
+    top: 0;
+  }
+
+}
+</style>

+ 206 - 0
src/components/Login.vue

@@ -0,0 +1,206 @@
+<template>
+  <div class="my-login"> 
+      <div class="bg"></div>
+        <div class="drag">
+            <img class="login-img" src="/static/img/loginbg.jpg" alt="">
+            <div class="my-form">
+                <p class="login-title">欢迎登录</p>
+                <div class="login-info">
+                    <input class="username" type="text" v-model="username" autocomplete="new-username" placeholder="请输入用户名">
+                    <i class="iconfont icon-zhanghao mark"></i>
+                    <i class="iconfont icon-close close"></i>
+                    </div>
+                <div class="login-info">
+                    <input class="password" type="password" v-model="password" autocomplete="new-password" placeholder="请输入密码">
+                    <i class="iconfont icon-mima mark"></i>
+                    <i class="iconfont icon-close close"></i>
+                    </div>
+                <p class="forget-password">忘记密码 <span class="msg">{{msg}}</span></p>
+                <button class="login-btn" @click="handleLogin">登陆</button>
+                <div class="login-bottom">@2018 Persagy.All right reserved.版权所有</div>
+            </div>
+        </div>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+         username:"",
+         password:"",
+         msg:""
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+      handleLogin(){
+          
+          if(this.username==''){
+              this.msg='用户名或密码错误';
+          }
+          if(this.password==''){
+              this.msg='用户名或密码错误';
+          }
+          if(this.username!='' && this.password!=''){
+              this.$router.push({path:"/home"})
+          }
+      }
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.my-login{
+    width: 100%;
+    height:100%;
+    min-width: 1140px;
+    position:fixed;
+    .bg{
+        width:100%;
+        height:100%;
+        background: url("/static/img/bg.jpg") no-repeat;
+        padding:1px;
+        box-sizing:border-box;
+        z-index:1;
+        background-size:cover;
+        filter: blur(15px)
+    }
+    .drag{
+        margin:220px auto;
+        width:1120px;
+        height:640px;
+        background: #fff;
+        display: flex;
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        margin-left: -560px;
+        margin-top:-320px;
+        .login-img{
+            width: 688px;
+            height: 100%;
+        }
+        .my-form{
+            display: inline-block;
+            width: 432px;
+            text-align: center;
+            .login-title{
+                font-weight: bold;
+                font-size: 32px;
+                color: #3B4254;
+                letter-spacing: 1.71px;
+                padding:132px 0 98px 0;
+            }
+            .login-info{
+                width: 320px;
+                height: 22px;
+                margin:0 0  24px 58px;
+                position: relative;
+                >input{
+                    width: 100%;
+                    border: none;
+                    //font-size: 16px;
+                    outline: none;
+                    border-bottom:1px solid #DBDDE4;
+                    padding:5px 20px;
+                    &:-webkit-autofill {
+                        -webkit-box-shadow: inset 0 0 0px 9999px white;
+                    }
+                    &:focus{
+                        border-bottom-color: #4A9EF9;
+                    }
+                    &:focus ~ i.mark{
+                       color: #4A9EF9;
+                    }
+                    &:focus ~ i.close{
+                       opacity: 1;
+                    }
+                }
+                .mark {
+                    position: absolute;
+                    top: 3px;
+                    left: 0;
+                }
+                .close {
+                    position: absolute;
+                    top:0;
+                    right: 0;
+                    opacity: 0;
+                    cursor: pointer;
+                    &:hover {
+                        color: #000;
+                    }
+                }
+                .my-img{
+                    width: 20px;
+                    height: 20px;
+                    margin-right: 22px;
+                }
+                .my-close{
+                    width:20px;
+                    height: 20px;
+                    margin-left: 50px;
+                }
+            }
+            .forget-password{
+                float: left;
+                //margin:0 0 0 52px; 
+                padding-left: 52px;
+                //font-size: 14px;
+                color: #4A9EF9;
+                letter-spacing: 0;
+                display: flex;
+                .msg{
+                    padding-left: 100px;
+                    color: #FF7B7B;
+                    letter-spacing: 0;
+                }
+            }
+            .login-btn{
+                width: 320px;
+                line-height: 44px;
+                text-align: center;
+                background: #4A9EF9;
+                box-shadow: 0 2px 4px 0 rgba(39,110,227,0.20);
+                border-radius: 3px;
+                font-size: 18px;
+                color: #FFFFFF;
+                letter-spacing: 0.75px;
+                font-weight: bold;
+                margin-top:16px;
+            }
+            .login-bottom{
+                margin-top:142px;
+                opacity: 0.8;
+                font-family: 'ArialMT';
+                color: #B5B9C3;
+                letter-spacing: 0.2px;
+            }
+        }
+        
+    }
+    
+    
+}
+</style>

+ 42 - 0
src/components/NoTableData.vue

@@ -0,0 +1,42 @@
+<template>
+  <div>
+      
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 19 - 0
src/main.js

@@ -0,0 +1,19 @@
+
+import './assets/css/reset.css'
+import 'jquery'
+import Vue from 'vue'
+import App from './App'
+import router from './router'
+import store from './store'
+import iView from 'iview'
+import 'iview/dist/styles/iview.css'
+Vue.use(iView)
+Vue.config.productionTip = false
+/* eslint-disable no-new */
+new Vue({
+  el: '#app',
+  router,
+  store,
+  components: { App },
+  template: '<App/>'
+})

+ 7 - 0
src/modules/energyConsumption/index.js

@@ -0,0 +1,7 @@
+import store from './store'
+import router from './router'
+
+export {
+    store,
+    router
+}

+ 75 - 0
src/modules/energyConsumption/pages/asideMenu/index.vue

@@ -0,0 +1,75 @@
+<template>
+    <div class="aside-menu">
+      <ul>
+        <li v-for="(item, index) in menuList" :key="index">
+          <router-link  :to="item.routerLink">
+            <img src="item.iconSrc" alt="icon">
+            <span>{{item.text}}</span>
+          </router-link>
+        </li>
+      </ul>
+    </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+      menuList: [
+        {
+          iconSrc: 'https://',
+          text: '能耗首页',
+          routerLink: 'main'
+        },
+      ]
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.aside-menu {
+  ul {
+    > li {
+      &:hover {
+        background: #ccc;
+      }
+      a {
+        display: inline-block;
+        white-space: nowrap;
+        padding: 10px 20px;
+        width: 100%;
+        span {
+          padding-left: 20px;
+         }
+      }
+      .router-link-active {
+        background: #ccc;
+      }
+    }
+  }
+}
+</style>

+ 73 - 0
src/modules/energyConsumption/pages/home/index.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="e-home">
+    <!-- <aside-mune class="aside"></aside-mune>
+    <div class="e-content">
+      <router-view>
+      </router-view>
+    </div> -->
+  </div>
+</template>
+
+<script>
+
+import AsideMune from '../asideMenu'
+
+export default {
+  components: {
+    AsideMune
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.e-home {
+  .aside {
+    position: fixed;
+    z-index: 999;
+    top: 60px;
+    left: 0;
+    bottom: 0;
+    width: 80px;
+    border-right: 1px solid #ebecf0;
+    transition: all 0.3s ease-in-out;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+  .e-content {
+    margin-left: 80px;
+    transition: all 0.3s ease-in-out;
+    background: #ccc;
+  }
+  &>.aside:hover {
+    width: 300px;
+  }
+  &>.aside:hover + .e-content {
+    margin-left: 300px;
+  }
+}
+</style>

+ 43 - 0
src/modules/energyConsumption/pages/main/index.vue

@@ -0,0 +1,43 @@
+<template>
+  <div>能耗杀杀杀</div>
+</template>
+
+<script>
+
+
+
+export default {
+  components: {
+    
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 16 - 0
src/modules/energyConsumption/router/index.js

@@ -0,0 +1,16 @@
+const Home = () => import(/* webpackChunkName: 'energyConsumption' */ '../pages/home')
+const Main = () => import(/* webpackChunkName: 'energyConsumption' */ '../pages/main')
+
+export default [
+    {
+        path: 'energyConsumption',
+        component: Home,
+        redirect: 'energyConsumption/main',
+        children:[
+            {
+                path:"main",
+                component: Main
+            }
+        ]
+    }
+]

+ 15 - 0
src/modules/energyConsumption/store/index.js

@@ -0,0 +1,15 @@
+/**
+ * 能耗
+ */
+export default {
+    namespaced: true,
+    state: {
+        name: '能耗'
+    },
+    mutations: {
+        
+    },
+    actions: {
+
+    },
+}

+ 73 - 0
src/modules/environment/component/reference.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="legend">
+    <div > 
+      <i class="line" :style="`width: ${width}; background: linear-gradient(${colors});`"></i>
+      <span class="unit" v-html="unitText"></span> 
+    </div>
+    <ul>
+      <li v-for="(item, index) in referList" :key="index" :style="`left: ${space * index}px`">{{item.value}}</li>
+    </ul>       
+  </div> 
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {
+    width: {
+      type: String,
+      default: "300px"
+    },
+    unitText: {
+      type: String,
+      default: ""
+    },
+    referList: {
+      type: Array,
+      required: true
+    }
+  },
+  data() {
+    return {};
+  },
+
+  computed: {
+    space() {
+      return `${parseFloat(this.width) / (this.referList.length - 1)}`;
+    },
+    colors() {
+      const str = this.referList.map(i => i.color).toString();
+      return `to right,${str}`;
+    }
+  },
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" style="text/less">
+.legend {
+  > div {
+    display: flex;
+    align-items: center;
+    .line {
+      display: inline-block;
+      height: 10px;
+      border-radius: 3px;
+      margin-left: 7px;
+    }
+    .unit {
+      padding-left: 3px;
+    }
+  }
+  ul {
+    position: relative;
+    margin-top: 3px;
+    li {
+      position: absolute;
+    }
+  }
+}
+</style>

+ 7 - 0
src/modules/environment/index.js

@@ -0,0 +1,7 @@
+import store from './store'
+import router from './router'
+
+export {
+    store,
+    router
+}

+ 205 - 0
src/modules/environment/pages/analys/index.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="ev-analysis">
+    <p class="evPage" ><img src="/static/img/backIcon.jpg" alt="" @click="back"> 环境首页&nbsp;&nbsp;>&nbsp;&nbsp;<span class="spaceName">{{floorname}}</span></p>
+     <!-- 上层 -->
+    <div class="ev-top">
+      <div class="average-index">
+          <sgy-healthy-index width="65px" :colors="colors" :innerValue="innerValue"
+              :outerValue="outerValue" :innerTdb="innerTdb" :innerRH="innerRH" :innerPM="innerPM" :innerC="innerC"></sgy-healthy-index>
+      </div>
+      <div class="point-space">
+        <span class="remark-icon"></span><p class="my-title">重点关注空间</p>
+        <div class="rooms">
+          <div class="point-div" v-for="(item,index) in rooms" :key="index">
+            <p style="padding:20px 0 10px 20px">{{item.name}}</p>
+            <p style="padding:0 0 10px 20px;color: #3B4254;">健康指数: {{item.index}}</p>
+            <div style="width:196px;padding-left:20px;word-wrap:break-word;word-break:break-all;">建议:{{item.suggest}}</div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 下层 -->
+     <div class="ev-bottom">
+        <span class="remark-icon"></span><p class="my-title">各区域对比</p>
+        <i-Table stripe  size="large" :columns="columns1" :data="data1" ></i-Table>
+     </div> 
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import sgyHealthyIndex from "@/modules/firstPage/pages/main/SgyHealthyIndex.vue";
+export default {
+  components: {
+    sgyHealthyIndex
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      innerValue:55,
+      outerValue:70,
+      routerParams: "",
+      rooms: [
+        { name: "会议室", index: "差", suggest: "通风换气,控制人员密度" },
+        { name: "会议室", index: "差", suggest: "通风换气,控制人员密度" },
+        { name: "会议室", index: "差", suggest: "通风换气,控制人员密度" },
+        { name: "会议室", index: "差", suggest: "通风换气,控制人员密度" },
+        { name: "会议室", index: "差", suggest: "通风换气,控制人员密度" },
+        { name: "会议室", index: "差", suggest: "通风换气,控制人员密度" }
+      ],
+      columns1: [
+        {
+          title: "区域",
+          key: "name"
+        },
+        {
+          title: "温度",
+          key: "Tdb"
+        },
+        {
+          title: "湿度",
+          key: "RH"
+        },
+        {
+          title: "空气质量",
+          key: "PM25"
+        },
+        {
+          title: "人流量/h",
+          key: "ExsitPeopleNum"
+        }
+      ],
+      data1: [],
+      colors: ["#F78686", "#FABA53", "#73DA99", "#4A9EF9"],
+      floorname: "",
+      innerTdb: 0,
+      innerC: 0,
+      innerRH: 0,
+      innerPM: 0
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getParams();
+  },
+
+  methods: {
+    back() {
+      this.$router.history.go(-1);
+    },
+    getParams() {
+      let routerParams = location.search.split("=")[1];
+      axios
+        .get(
+          `/enviroment/info-mng-backend/env/floorDetails?projectId=${this.$store.state.projectId}&floorId=${routerParams}`
+        )
+        .then(res => {
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.floorname = data.floorname;
+            let indoorAverage = data.indoorAverage;
+            this.innerTdb = indoorAverage.Tdb;
+            this.innerC = indoorAverage.CO2;
+            this.innerRH = indoorAverage.RH;
+            this.innerPM = indoorAverage.PM25;
+            this.data1 = data.spaces;
+            this.data1.forEach(el=>{
+              el.Tdb = el.Tdb.toFixed(1);
+              el.RH = el.RH.toFixed(1)
+            })
+          }
+        });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.ev-analysis {
+  background: #fff;
+  padding: 20px;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  //font-size: 14px;
+  .evPage {
+    border-bottom: 1px solid #dbdde4;
+    height: 60px;
+    line-height: 60px;
+    display: flex;
+    > img {
+      height: 30px;
+      margin-top: 14px;
+      margin-right: 8px;
+      cursor: pointer;
+    }
+    .spaceName {
+      color: #3b4254;
+      font-weight: bold;
+    }
+  }
+  .ev-top {
+    margin-top: 20px;
+    display: flex;
+    .average-index {
+      width: 27%;
+      min-width: 357px;
+      border: 1px solid #dbdde4;
+      border-radius: 3px;
+    }
+    .point-space {
+      width: 73%;
+      border: 1px solid #dbdde4;
+      border-radius: 3px;
+      margin-left: 10px;
+      .my-title {
+        height: 40px;
+        font-size:18px;
+        font-weight: bold;
+        display: inline-block;
+      }
+      .remark-icon {
+        padding: 7px 3px;
+        display: inline-block;
+        background: #4a9ef9;
+        margin: 20px 10px 0 20px;
+      }
+      .rooms {
+        overflow-x: scroll;
+        white-space: nowrap;
+        .point-div {
+          width: 260px;
+          height: 148px;
+          box-shadow: 0 1px 5px 0 rgba(59, 66, 84, 0.1);
+          border-radius: 3px;
+          margin: 10px 0 10px 18px;
+          display: inline-block;
+        }
+      }
+    }
+  }
+  .ev-bottom {
+    margin-top: 10px;
+    padding: 0 20px;
+    border: 1px solid #dbdde4;
+    border-radius: 3px;
+    .my-title {
+      height: 40px;
+      font-size:18px;
+      font-weight: bold;
+      display: inline-block;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+  }
+}
+</style>

+ 71 - 0
src/modules/environment/pages/asideMenu/index.vue

@@ -0,0 +1,71 @@
+<template>
+    <div class="aside-menu">
+      <ul>
+        <li>
+          <router-link to="main">
+           <i class="iconfont icon-huanjing"></i>
+            <span>环境首页</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link to="plane">
+            <i class="iconfont icon-jiancedian"></i>
+            <span>环境测点平面布置</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link to="mass">
+            <i class="iconfont icon-add"></i>
+            <span>环境质量分析</span>
+          </router-link>
+        </li>
+      </ul>
+    </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.aside-menu {
+  ul {
+    > li {
+      &:hover {
+        background: #eee;
+      }
+      a {
+        display: inline-block;
+        white-space: nowrap;
+        padding: 10px 30px;
+        width: 100%;
+        color: #b6bccb;
+        span {
+          padding-left: 37px;
+          color: black;
+          font-size: 15px;
+        }
+      }
+      .router-link-active {
+        color: #57a4f7;
+      }
+    }
+  }
+}
+</style>

+ 184 - 0
src/modules/environment/pages/heal/RoomReport.vue

@@ -0,0 +1,184 @@
+<template>
+  <div class="room-report">
+     <p class="alarm-description">变压器超温 <span>未处理</span> <Icon type="md-close" class="close-btn"  @click="close"/></p>
+     <div class="alarm-detailInfo">
+        <span class="remark-icon"></span><p class="my-title">报警详细信息</p>
+        <p class="detail-info"><span class="info-left">报警位置:</span>配电间 变配电低压开关柜0001号</p>
+        <p class="detail-info"><span class="info-left">报警开始时间:</span>2018.10.20 12:33</p>
+        <p class="detail-info" style=" padding-bottom: 20px;"><span class="info-left">报警持续时间:</span>40分钟</p>
+        <div class="alarm-value">
+          <div class="value">
+            <span>报警值(℃)</span>
+            <p>80</p>
+          </div>
+          <div class="value">
+            <span>当前值(℃)</span>
+            <p>80</p>
+          </div>
+          <div class="value">
+            <span>门限值(℃)</span>
+            <p>80</p>
+          </div>
+        </div>
+        <p class="unit">单位:℃</p>
+        <div id="alarmLine"></div>
+     </div>
+     <div class="alarm-video">
+        <span class="remark-icon"></span><p class="my-title">报警时间内视频监控记录</p>
+        <div class="video"></div>
+     </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+var echarts = require("echarts")
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: ['maskWidth'],
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+    this.getLine();
+  },
+
+  methods: {
+    //关闭弹窗
+    close(){
+       this.$emit("closeMask")
+    },
+    //折线图
+     getLine(){
+       var alarmLine = echarts.init(document.getElementById("alarmLine"));
+       let timeValue = ['00:00','03:00','06:00','09:00','12:00','15:00','18:00','21:00'];
+       let alarmData = [40,46,64,80,90,60,50,43]
+       alarmLine.setOption({
+         xAxis: {
+              type: 'category',
+              data: timeValue
+          },
+          yAxis: {
+              type: 'value',
+          },
+          series: [{
+              data: alarmData,
+              type: 'line',
+              color: "#4A9EF9"
+          }]
+       })
+     }
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.room-report{
+  .alarm-description{
+    height: 56px;
+    line-height: 56px;
+    padding-left: 24px;
+    font-size: 16px;
+    color: #3B4254;
+    border-bottom: 1px solid #DBDDE4;
+    >span{
+      color: #F78686;
+      padding: 3px 7px;
+      background: rgba(247,134,134,0.10);
+      border: 1px solid #F78686;
+      border-radius: 3px;
+      margin-left: 16px;
+    }
+    .close-btn{
+      float: right;
+      margin-right: 10px;
+      margin-top: 17px;
+      cursor: pointer;
+    }
+  }
+  .alarm-detailInfo{
+    .my-title {
+      height: 40px;
+      font-size:18px;
+      font-weight: bold;
+      display: inline-block;
+      color: #3B4254;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+    .detail-info {
+      line-height: 40px;
+      margin-left: 20px;
+      .info-left{
+        text-align: right;
+        display: inline-block;
+        margin-right: 6px;
+      }
+    }
+    .alarm-value {
+      display: flex;
+      justify-content: space-around;
+      padding: 30px 0;
+      border-top: 1px dashed #DBDDE4;
+      .value{
+        >p{
+          font-size: 32px;
+          color:#F78686;
+        }
+      }
+    }
+    #alarmLine{
+      position: relative;
+      margin-left: 30px;
+      width: 340px;
+      height: 340px;
+    }
+    .unit{
+      position: absolute;
+      left: 36px;
+      top: 391px;
+    }
+  }
+  .alarm-video{
+    .my-title {
+      height: 40px;
+      font-size:18px;
+      font-weight: bold;
+      display: inline-block;
+      color: #3B4254;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+    .video{
+      width: 350px;
+      height: 226px;
+      background: #F6F8FB;
+      border-radius: 3px;
+      margin: 0 0 20px 20px;
+    }
+  }
+}
+
+</style>

+ 200 - 0
src/modules/environment/pages/heal/RoomTable.vue

@@ -0,0 +1,200 @@
+<template>
+  <div class="room-table">
+   <div class="only-box">
+        <div class="alarm-top">
+            <span @click="getAlarmData(0)" :class="{active: activeIndex== 0}">当前报警</span>
+            <span @click="getAlarmData(1)" :class="{active: activeIndex== 1}">尚未处理的报警</span>
+            <span @click="getAlarmData(2)" :class="{active: activeIndex== 2}">近20条报警</span>
+        </div>
+         <p class="only-room">
+            <Checkbox v-model="single" @on-change="changeEvent">只看空间本身的报警</Checkbox>
+        </p>
+   </div>
+     <i-Table stripe  :columns="columns1" height="590" :data="roomDate" size="large" @on-row-click="changeDetail" :no-data-text="noData"></i-Table>
+     <div class="mask">
+        <div class="mask-bg" @click="closeMask"></div>
+        <div class="mask-lay" :style="`width:${maskWidth}`">
+            <room-report :maskWidth="maskWidth" @closeMask="closeMask"></room-report>
+        </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios';
+import roomReport from './RoomReport'
+export default {
+  components: {
+     roomReport
+  },
+
+  mixins: [],
+
+  props:['roomDate','roomLeftName'],
+
+  data () {
+    return {
+      activeIndex:0,
+      maskWidth:"0px",
+      id:"",
+      single:false,
+      noData:
+        '<div style="padding-top:204px;height:868px;"><i class="iconfont icon-kongzhuangtaiicon" style="font-size:98px;"></i><p>此设备没有尚未处理的报警</p></div>',
+       columns1: [
+        {
+          title: "报警等级",
+          key: "level",
+          render:(h,params)=>{
+           const row = params.row;
+           console.log(row)
+           const backgroundColor = row.level == 'S'? 'error' : row.level == 'A'?'warning':'primary'
+           return h('Button',{
+             props:{
+               type:backgroundColor
+             }
+           },row.level)
+          }
+        },
+        {
+          title: "报警开始时间",
+          key: "ctime",
+        },
+        {
+          title: "报警持续时间",
+          key: "durationTime",
+        },
+        {
+          title: "报警详情",
+          key: "description",
+          width:400,
+          className:"description-td",
+        },
+        {
+          title: "状态",
+          key: "state",
+        }
+      ],
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+  },
+
+  methods: {
+      //复选框改变后,把变量提交给父组件
+      changeEvent(val){
+          this.$emit("spAlarm",this.single);
+          console.log(this.roomLeftName)
+          this.$parent.getCurrentSpaceAlarm(this.roomLeftName.spObj)
+      },
+      //关闭弹窗
+      closeMask(){
+        document.querySelector(".mask-bg").style.display = "none";
+        document.querySelector("body").style.overflow = "auto";
+        this.maskWidth = "0px"
+      },
+      //弹窗展示
+      changeDetail(item,index){
+        this.id = item.id;
+        document.querySelector(".mask-bg").style.display = "block";
+        document.querySelector("body").style.overflow = "hidden";
+        this.maskWidth = "400px"
+      },
+      //尚未处理报警点击事件
+      getAlarmData(index){
+          this.activeIndex = index;
+      }
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+.room-table{
+    .only-box{
+        display: flex;
+        justify-content: space-around;
+        .alarm-top{
+            margin-bottom: 40px;
+            display: flex;
+            justify-content:center;
+            span{
+                text-align: center;
+                padding: 6px 20px;
+                color: #62697B;
+                cursor: pointer;
+                border: 1px solid #CACACA;
+                border-right: none;
+                &:last-child {
+                    border-right: 1px solid #CACACA;
+                    border-top-right-radius: 3px;
+                    border-bottom-right-radius: 3px;
+                }
+                &:first-child {
+                    border-top-left-radius: 3px;
+                    border-bottom-left-radius: 3px;
+                }
+                &:hover, &.active{
+                    background: #57A4F7;
+                    color:#fff;
+                }
+            }
+        }
+    .only-room{
+        display: inline-block;
+    }
+    }
+    .cell-class-name>.ivu-table-cell>span{
+        display: inline-block;
+        width: 24px;
+        height: 24px;
+        color: #fff;
+        border-radius: 50%;
+    }
+    .mask{
+        .mask-bg{
+            position: fixed;
+            top: 0;
+            bottom:0;
+            left: 0;
+            right: 0;
+            z-index: 999;
+            display: none;
+        }
+        .mask-lay{
+            position: fixed;
+            top: 50px;
+            bottom:0;
+            right: 0;
+            z-index: 999;
+            background: #fff;
+            transition: all 0.5s ease-in-out;
+            overflow-y: auto;
+            box-shadow: 0 7px 15px 0 rgba(0,0,0,0.20);
+        }
+    }
+}
+</style>
+<style lang="less">
+.room-table{
+ .ivu-table-row td:first-of-type .ivu-table-cell > button {
+    display: inline-block;
+    width: 30px;
+    height: 30px;
+    border-radius: 50%;
+    color: #fff;
+    font-size: 16px;
+    line-height:16px;
+  }
+  .ivu-table-row td:first-of-type .ivu-table-cell>button>span{
+    margin-left:-4px;
+  }
+}
+</style>
+
+
+

+ 357 - 0
src/modules/environment/pages/heal/index.vue

@@ -0,0 +1,357 @@
+<template>
+  <div class="ev-heal">
+     <div class="sa-left">
+       <div class="sa-left-top">
+             <div class="room-img">
+               <img src="/static/img/backIcon.jpg" alt="" @click="back"><span style="padding-left:32px;">环境首页</span>
+                ><strong>{{name}}</strong>
+             </div>
+            <div class="input-stat">
+              <i-Input search size="large" v-model="keyword" placeholder="请输入关键字" @on-change="getEvRoom()"/>
+            </div> 
+            <div>
+              <label for="position">所属建筑:</label>
+              <i-Select id="position" v-model="building" size="large" style="width:200px" @on-change="getEvRoom()">
+                  <Option v-for="item in buildList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+              </i-Select>
+            </div>
+             <br/>
+             <div>
+                <label for="major">所属楼层:</label>
+                <i-Select id="major" v-model="floor" size="large" style="width:200px" @on-change="getEvRoom()">
+                    <Option v-for="item in floorList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+                </i-Select>
+             </div>
+             <br/><br/>
+        </div>
+        <div class="sa-left-bottom">
+          <p><strong>总共<span> {{roomList.length}} </span>个符合条件的设备空间:</strong></p>
+          <ul>
+            <li  v-for="(item,index) in roomList" :key="index" @click="showDetail(item,index)" :class="{activeStyle:index+1 == changeStyle}">{{item.spObjName}}</li>
+          </ul>
+        </div>
+     </div>
+     <div class="sa-right">
+       <div class="sa-right-top">
+         <p class="eq-info" v-if="flag"><strong>{{roomLeftName.spObjName}}</strong></p>
+         <p class="eq-info" v-else><strong>{{roomDetail.RoomLocalName }}</strong></p>        
+          <div class="sa-box">
+            <div class="warn-num">
+               <p>当前空间报警</p>
+              <span style="padding-left:20px;">{{roomDetail.spAlarmCount}}</span>  
+            </div>
+            <div class="warn-num">
+               <p>当前空间严重报警(S级/A级)</p>
+               <span style="padding-left:50px;">{{roomDetail.spAlarmSACount}}</span>
+            </div>
+            <div class="warn-num">
+               <p>空间内包含 {{roomDetail.spEqCount}} 台设备</p>
+               <i>报警设备</i><span style="padding:0 8px;">{{roomDetail.spAlarmEqCount}}</span> 
+               <i style="margin-left:50px">严重报警</i><span style="padding:0 8px;">{{roomDetail.spAlarmEqSACount}}</span>
+            </div>
+          </div>
+       </div>
+       <div class="sa-right-bottom">
+         <room-table :roomDate="roomDate" :roomLeftName="roomLeftName" @spAlarm="spAlarm"></room-table>
+       </div>
+     </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import roomTable from './RoomTable'
+export default {
+  components: {
+      roomTable
+  },
+
+  mixins: [],
+
+  props: {},
+  data() {
+    return {
+      floorList: [],
+      buildList: [],
+      model2: "",
+      keyword: "",
+      building: "",
+      floor: "",
+      roomList:[],
+      roomLeftName:{},
+      changeStyle: 0,
+      roomDetail: {},
+      roomDate:[],
+      flag:false,
+      checkVal:false,
+      name:""
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getRooomInfo();
+    this.getFloorList();
+    this.getbuildList();
+    this.getEvRoom();
+  },
+
+  methods: {
+    //接收子组件传过来的值
+    spAlarm(val){
+      this.checkVal = val;
+    },
+    //获取空间报警数据
+    getCurrentSpaceAlarm(roomId){
+      let checkVal = this.checkVal;
+      axios.get(`/equipment/info-mng-backend/env/getCurrentSpaceAlarm?projectId=${this.$store.state.projectId}&spId=${roomId}&isOnlySpAlarm=${checkVal}`).then(res=>{
+        let data = res.data.data;
+        this.roomDate = data.list;
+        data.list.forEach(el=>{
+          el.ctime = this.formatTime(el.ctime);
+          el.durationTime = this.toHourMinute(el.durationTime)
+        })
+      })
+    },
+    //空间报警信息
+    getSpaceDetail(roomId){
+      axios.get(`/equipment/info-mng-backend/env/getSpaceDetail?projectId=${this.$store.state.projectId}&spId=${roomId}`).then(res=>{
+        this.roomDetail = res.data.data;
+      })
+    },
+    //首次空间报警信息数据
+    getRooomInfo() {
+      console.log(location.search,this.$route.query.name)
+      this.name = this.$route.query.name;
+      let routerParams = location.search.split("=")[1];
+      console.log(routerParams)
+      this.getSpaceDetail(routerParams);
+      this.getCurrentSpaceAlarm(routerParams)
+    },
+    //回退
+    back() {
+      this.$router.push('/environment/main')
+    },
+    //所属楼层
+    getFloorList() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/getFloorList?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          let list = data.list;
+          list.forEach(element => {
+            element.label = element.value = element.floorName;
+          });
+          this.floorList = list;
+        });
+    },
+    //所属建筑
+    getbuildList() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/env/getBuildingList?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          let list = data.list;
+          list.forEach(element => {
+            element.label = element.value = element.infos.BuildLocalName;
+          });
+          this.buildList = list;
+        });
+    },
+     //开始时间格式调整
+     formatTime(str) {
+      return (
+        str.slice(0, 4) +
+        "." +
+        str.slice(4, 6) +
+        "." +
+        str.slice(6, 8) +
+        " " +
+        str.slice(8, 10) +
+        ":" +
+        str.slice(10, 12)
+      );
+    },
+    //持续时间格式化
+    toHourMinute(minutes){
+      return ((Math.floor(minutes/60)>=24?Math.floor(minutes/60/24)+'天'+
+      (Math.floor(minutes/60%24)):Math.floor(minutes/60)) + "小时" + (minutes%60) + "分" );
+    },
+    //设备空间按条件查找
+    getEvRoom() {
+      let buildId = "";
+      let floorId = "";
+      let keyword = this.keyword;
+      this.buildList.forEach(el => {
+        if ((el.BuildLocalName = this.building)) {
+          buildId = el.id;
+        }
+      });
+      this.floorList.forEach(el => {
+        if ((el.floorName = this.floor)) {
+          floorId = el.floorId;
+        }
+      });
+      axios
+        .get(
+          `/equipment/info-mng-backend/env/getSpaceInfo?projectId=${this.$store.state.projectId}&floorId=${floorId}&buildingId=${buildId}&keyword=${keyword}`
+        )
+        .then(res => {
+          let data = res.data
+          if(data.result=='success'){
+             this.roomList = data.data.list
+          }
+        });
+    },
+    //左侧点击展示右侧数据事件
+    showDetail(item, index) {
+      this.flag = true;
+      this.roomLeftName = item;
+      this.changeStyle = index+1;
+      this.getSpaceDetail(item.spObj);
+      this.getCurrentSpaceAlarm(item.spObj)
+    },
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.ev-heal {
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  display: flex;
+  .sa-left {
+    width: 20%;
+    padding: 20px 10px;
+    margin-right: 10px;
+    background: #fff;
+    border-radius: 3px;
+    min-width: 304px;
+    .sa-left-top {
+      border-bottom: 1px dashed #dbdde4;
+      text-align: center;
+      .room-img {
+        text-align: left;
+        margin-left: 7px;
+        position: relative;
+        > img {
+          position: absolute;
+          top: -4px;
+        }
+      }
+      .input-stat {
+        width: 270px;
+        display: inline-block;
+        margin: 30px 0;
+      }
+    }
+    .sa-left-bottom {
+      text-indent: 2em;
+      > p {
+        padding: 10px 0;
+        color: #8c94a9;
+        > span {
+          font-size: 20px;
+        }
+      }
+      ul {
+        height: 600px;
+        overflow-y: auto;
+        li {
+          padding: 10px 0;
+          cursor: pointer;
+          text-indent: 2em;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+        &:not(:last-child) {
+          border-bottom: 1px solid #eee;
+        }
+        .activeStyle {
+          color: #57a4f7;
+        }
+      }
+    }
+  }
+  .sa-right {
+    width: 79.4%;
+    display: flex;
+    flex-direction: column;
+    .sa-right-top {
+      padding: 20px;
+      background: #fff;
+      border-radius: 3px;
+      margin-bottom: 10px;
+      .eq-info {
+        strong {
+          font-size: 24px;
+        }
+        .eq-major {
+          padding: 0 37px;
+        }
+      }
+      .sa-box {
+        display: flex;
+        justify-content: space-around;
+        margin: 20px;
+        .warn-num {
+          text-align: left;
+          > span {
+            font-size: 40px;
+          }
+          .word-alarm {
+            max-width: 390px;
+            word-break: break-all;
+            //  >span{
+            //    display: inline-block;
+            //    width: 70px;
+            //    height: 32px;
+            //    text-align: right;
+            //  }
+          }
+        }
+      }
+    }
+    .sa-right-bottom {
+      padding: 20px;
+      background: #fff;
+      border-radius: 3px;
+      flex: 1;
+    }
+  }
+}
+</style>
+<style  lang="less">
+.ev-heal{
+  .ivu-tabs-bar {
+    border: none;
+  }
+  .ivu-table-large{
+    font-size:16px;
+  }
+  .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    border: none;
+    background: #fff;
+  }
+  .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab:not(:last-child) {
+    border-right: 1px solid #ccc;
+  }
+  .ivu-table-row td:first-of-type .ivu-table-cell>span{
+      display: inline-block;
+      width: 24px;
+      height: 24px;
+      border-radius: 50%;
+      color: #fff;
+      background:  #F78686;
+      text-align: center;
+      line-height: 24px;
+  }
+ 
+}
+</style>
+

+ 67 - 0
src/modules/environment/pages/home/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="fas-home">
+    <aside-mune class="aside"></aside-mune>
+    <div class="content">
+      <router-view>
+      </router-view>
+    </div>
+  </div>
+</template>
+
+<script>
+import AsideMune from "../asideMenu";
+
+export default {
+  components: {
+    AsideMune
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.fas-home {
+  .aside {
+    position: fixed;
+    z-index: 999;
+    top: 50px;
+    left: 0;
+    bottom: 0;
+    width: 80px;
+    border-right: 1px solid #ebecf0;
+    transition: all 0.3s ease-in-out;
+    overflow-x: hidden;
+    overflow-y: auto;
+    background: #fff;
+  }
+  .content {
+    margin-left: 80px;
+    transition: all 0.3s ease-in-out;
+    background: #ebecf0;
+    padding: 70px 20px 20px 20px;
+    //font-size: 14px;
+    min-width: 1300px;
+  }
+  & > .aside:hover {
+    width: 260px;
+    background: #fff;
+  }
+  // &>.aside:hover + .content {
+  //   margin-left: 260px;
+  //   padding-left: 20px;
+  // }
+}
+</style>

+ 45 - 0
src/modules/environment/pages/main/hoverDiv.vue

@@ -0,0 +1,45 @@
+<template>
+ <div class="hover-div">
+    <p>空间名称:<span >{{item2.RoomLocalName}}</span></p>
+  <p>{{btnName}}:<span>{{item2.data?item2.data.toFixed(2):""}}</span></p>
+ </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: ["item2", "btnName"],
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.hover-div {
+  background:rgba(0, 0, 0, .6);
+  width: 164px;
+  height: 64px;
+  border: 0 solid #d9d9d9;
+  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12);
+  border-radius: 3px;
+  color: #fff;
+  display: none;
+  position: absolute;
+  font-size: 13px;
+  z-index: 999;
+  >p {
+   padding: 4px 0 0 10px;
+  }
+}
+</style>

+ 547 - 0
src/modules/environment/pages/main/index.vue

@@ -0,0 +1,547 @@
+<template>
+  <div class="ev-index">
+     <!-- 顶部按钮 -->
+      <div >
+        <div class="en-top">
+        <Button   v-for="(list,index) in btn"  @click="change(index)" :class="{red:index == changeRed}" :key="index">{{list.a}}</Button>
+      </div>
+      <!-- 图 -->
+      <div class="en-img" >
+        <div class="en-top">
+          <p class="door-ev">
+            室内平均{{btnName}} <span class="indoor-ev">{{indoorAverage?indoorAverage:"--"}}</span> 
+            <span class="outdoor-ev">室外平均{{btnName}} </span><span class="indoor-ev">{{outdoor?outdoor:"--"}}</span> 
+          </p>
+          <div>
+            <p class="ev-standard" v-if="activeType=='Tdb'">营业区域温度范围 24℃~26℃</p>
+          <!--温度冷标尺 -->
+            <div class="ev-scale" v-if="activeType=='Tdb' && seasonType=='coolPeriod'">
+              <reference :refer-list="referenceObj[activeType].coldList" :unitText="referenceObj[activeType].unitText"></reference>
+            </div>
+            <!--温度热标尺 -->
+            <div class="ev-scale" v-if="activeType=='Tdb' && seasonType=='heatPeriod'">
+              <reference :refer-list="referenceObj[activeType].list" :unitText="referenceObj[activeType].unitText"></reference>
+            </div>
+            <!-- co2 pm 湿度等标尺 -->
+            <div class="ev-scale" v-if="activeType!='Tdb'">
+              <reference :refer-list="referenceObj[activeType].list" :unitText="referenceObj[activeType].unitText"></reference>
+            </div>
+          </div>
+        </div>
+        <!-- 温度热列表 -->
+         <div class="equipment-view" v-if="activeType=='Tdb' && seasonType=='heatPeriod'">
+          <div class="floor" v-for="(item1, index1) in myData" :key="index1">
+            <p>
+                <span @click="showFloor(item1.id)" class="floor-num">{{item1.FloorLocalName=="地下一层"?"地下一层设备房":item1.FloorLocalName}}</span>
+              平均{{btnName}}&nbsp;&nbsp;&nbsp;&nbsp;{{item1.average?item1.average:""}}</p>
+            <ul>
+              <!-- 展示 -->
+              <template>             
+                <li v-for="(item2, index2) in item1.spaces" :key="index2" @click="showRoom(item2)"
+                  :style="`background: ${valueToColor(item2.data, referenceObj[activeType].list)}`">
+                  <span>{{item2.data}}</span>
+                  <hover-div :item2="item2" :btnName="btnName"></hover-div>
+                </li>
+              </template>
+            </ul>
+          </div> 
+        </div>
+        <!-- 温度冷列表 -->
+        <div class="equipment-view" v-if="activeType=='Tdb' && seasonType=='coolPeriod'">
+          <div class="floor" v-for="(item1, index1) in myData" :key="index1">
+            <p>
+                <span @click="showFloor(item1.id)" class="floor-num">{{item1.FloorLocalName=="地下一层"?"地下一层设备房":item1.FloorLocalName}}</span>
+              平均{{btnName}}&nbsp;&nbsp;&nbsp;&nbsp;{{item1.average?item1.average:""}}</p>
+            <ul>
+              <!-- 展示 -->
+              <template>             
+                <li v-for="(item2, index2) in item1.spaces" :key="index2" @click="showRoom(item2)"
+                  :style="`background: ${valueToColor(item2.data, referenceObj[activeType].coldList)}`">
+                  <span>{{item2.data.toFixed(1)}}</span>
+                  <hover-div :item2="item2" :btnName="btnName"></hover-div>
+                </li>
+              </template>
+            </ul>
+          </div> 
+        </div>
+
+        <!-- co2 pm 湿度的数据颜色渲染 -->
+        <div class="equipment-view" v-if="activeType!='Tdb'">
+          <div class="floor" v-for="(item1, index1) in myData" :key="index1">
+            <p>
+                <span @click="showFloor(item1.id)" class="floor-num">{{item1.FloorLocalName=="地下一层"?"地下一层设备房":item1.FloorLocalName}}</span>
+              平均{{btnName}}&nbsp;&nbsp;&nbsp;&nbsp;{{item1.average?item1.average:""}}</p>
+            <ul>
+              <!-- 浮层展示 -->
+              <template>             
+                <li v-for="(item2, index2) in item1.spaces" :key="index2" @click="showRoom(item2)"
+                  :style="`background: ${valueToColor(item2.data, referenceObj[activeType].list)}`">
+                  <span>{{item2.data.toFixed(2)}}</span>
+                  <hover-div :item2="item2" :btnName="btnName"></hover-div>
+                </li>
+              </template>
+            </ul>
+          </div> 
+        </div>
+        
+      </div>
+      <!-- 动画 -->
+       <!-- <div v-if="myData.length==0" style="padding-top:40%">
+        <Spin fix>
+            <Icon type="ios-loading" size=40 class="demo-spin-icon-load"></Icon>
+            <div>Loading</div>
+        </Spin>
+      </div>    -->
+      <!-- 底部的两个图 -->
+      <div class="en-table" v-if="activeType=='Tdb'&&tableData.length>0">
+        <temperature-bar :stat="stat"></temperature-bar>
+        <temperature-table :tableData="tableData"></temperature-table>
+      </div>
+     </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import hoverDiv from "./HoverDiv.vue";
+import reference from "../../component/reference.vue";
+import temperatureBar from "./TemperatureBar.vue";
+import temperatureTable from "./TemperatureTable.vue";
+import valueToColor from "@/utils/vauleToColor.js";
+export default {
+  components: {
+    reference,
+    hoverDiv,
+    temperatureBar,
+    temperatureTable
+  },
+  mixins: [],
+  props: [],
+  data() {
+    return {
+      valueToColor: valueToColor,
+      activeType: "Tdb",
+      btn: [
+        { a: "健康指数", type: "" },
+        { a: "温度", type: "Tdb" },
+        { a: "湿度", type: "RH" },
+        { a: "CO2", type: "CO2" },
+        { a: "PM2.5", type: "PM2.5" },
+        { a: "甲醛", type: "HCHO" },
+        { a: "情绪", type: "" },
+        { a: "照度", type: "" },
+        { a: "人流量", type: "" }
+      ],
+      btnName: "温度",
+      changeRed: 1,
+      myData: [],
+      stat:{},
+      tableData:[],
+      seasonType:"heatPeriod",
+      referenceObj: {
+        //温度
+        Tdb: {
+          unitText: "℃",
+          // 热列表
+          list: [
+            {
+              value: 20,
+              color: "#7D40CC"
+            },
+            {
+              value: 22,
+              color: "#465CCC"
+            },
+            {
+              value: 24,
+              color: "#328E2A"
+            },
+            {
+              value: 26,
+              color: "#62C85E"
+            },
+            {
+              value: 28,
+              color: "#ECF911"
+            },
+            {
+              value: 30,
+              color: "#FFB711"
+            },
+            {
+              value: 32,
+              color: "#FF3E13"
+            }
+          ],
+          //冷列表
+          coldList: [
+            {
+              value: 20,
+              color: "#7D40CC"
+            },
+            {
+              value: 22,
+              color: "#465CCC"
+            },
+            {
+              value: 24,
+              color: "#328E2A"
+            },
+            {
+              value: 26,
+              color: "#62C85E"
+            },
+            {
+              value: 28,
+              color: "#ECF911"
+            },
+            {
+              value: 30,
+              color: "#FFB711"
+            },
+            {
+              value: 32,
+              color: "#FF3E13"
+            }
+          ],
+          //中间列表
+          list2: [
+            {
+              value: -10,
+              color: "#3103BC"
+            },
+            {
+              value: 0,
+              color: "#36c397"
+            },
+            {
+              value: 10,
+              color: "#3305ca"
+            },
+            {
+              value: 20,
+              color: "#A87482"
+            },
+            {
+              value: 25,
+              color: "#8CD94B"
+            },
+            {
+              value: 30,
+              color: "#F0A500"
+            },
+            {
+              value: 40,
+              color: "#F01D80"
+            }
+          ],
+          //地下一层列表
+          undergroundList: [
+             {
+              value: 15,
+              color: "#7D40CC"
+            },
+            {
+              value: 20,
+              color: "#465CCC"
+            },
+            {
+              value: 25,
+              color: "#328E2A"
+            },
+            {
+              value: 30,
+              color: "#62C85E"
+            },
+            {
+              value: 35,
+              color: "#ECF911"
+            },
+            {
+              value: 40,
+              color: "#FFB711"
+            },
+            {
+              value: 45,
+              color: "#FF3E13"
+            }
+          ]
+        },
+        //湿度
+        RH: {
+          unitText: "%",
+          list: [
+            {
+              value: 0,
+              color: "#4ED3FF"
+            },
+            {
+              value: 35,
+              color: "#3FFF06"
+            },
+            {
+              value: 45,
+              color: "#33F09F"
+            },
+            {
+              value: 55,
+              color: "#FFFD8F"
+            },
+            {
+              value: 65,
+              color: "#FFDA1D"
+            },
+            {
+              value: 95,
+              color: "#FF900B"
+            },
+            {
+              value: 100,
+              color: "#FF5704"
+            }
+          ]
+        },
+        //co2
+        CO2: {
+          unitText: "ppm",
+          list: [
+            {
+              value: 0,
+              color: "#48A15D"
+            },
+            {
+              value: 450,
+              color: "#5EC153"
+            },
+            {
+              value: 700,
+              color: "#5EC153"
+            },
+            {
+              value: 1000,
+              color: "#FFFE4B"
+            },
+            {
+              value: 2500,
+              color: "#E5A136"
+            },
+            {
+              value: 5000,
+              color: "#E66039"
+            }
+          ]
+        },
+        //pm 
+        'PM2.5': {
+          unitText: "",
+          list: [
+            {
+              value: 0,
+              color: "#009966"
+            },
+            {
+              value: 50,
+              color: "#FFDE33"
+            },
+            {
+              value: 100,
+              color: "#FF9A32"
+            },
+            {
+              value: 150,
+              color: "#CD0032"
+            },
+            {
+              value: 200,
+              color: "#66009A"
+            },
+            {
+              value: 300,
+              color: "#7E0023"
+            }
+          ]
+        },
+        'HCHO': {
+          unitText: "mg/m³",
+          list: [
+            {
+              value: 0,
+              color: "#009966"
+            },
+            {
+              value: 0.05,
+              color: "#FFDE33"
+            },
+            {
+              value: 0.08,
+              color: "#FF9A32"
+            },
+            {
+              value: 0.5,
+              color: "#CD0032"
+            },
+            {
+              value: 1,
+              color: "#66009A"
+            },
+            {
+              value: 2,
+              color: "#CD0032"
+            }
+          ]
+        }
+      },
+      indoorAverage: "",
+      outdoor: ""
+    };
+  },
+  computed: {},
+  mounted() {
+    this.init();
+  },
+  methods: {
+    //获取数据
+    init() {
+      this.myData = [];
+      axios
+        .get(
+          `/enviroment/info-mng-backend/env/indexByType?projectId=${this.$store.state.projectId}&type=${
+            this.activeType
+          }&${Math.random()}`).then(res => {
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.indoorAverage = data.indoorAverage?data.indoorAverage:"";
+            this.outdoor = data.outdoor?data.outdoor:"";
+            this.seasonType = data.seasonType;
+            this.myData = data.floors;
+            this.stat = res.data.stat?res.data.stat:{};
+            this.tableData = this.stat.floors?this.stat.floors:[];
+          }
+        });
+    },
+    //点击的按钮名称
+    change(index) {
+      this.changeRed = index;
+      this.btnName = this.btn[index].a;
+      this.activeType = this.btn[index].type;
+      this.init();
+    },
+    // 传参数 第几层
+    showFloor(item1) {
+      this.$router.push({ path: "analys", query: { floorId: item1 } });
+    },
+    //传参数 哪个空间
+    showRoom(item2){
+       this.$router.replace({ path: "heal", query: { objId: item2.id,name:item2.RoomLocalName} });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.ev-index {
+  background: #fff;
+  padding: 20px;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  .en-top {
+    text-align: center;
+    margin-top:20px;
+    button {
+      width: 100px;
+      line-height: 30px;
+      font-size:15px;
+      text-align: center;
+      display: inline-block;
+    }
+    .red {
+      background: #57a4f7;
+      color: #fff;
+    }
+  }
+  .en-img {
+    .en-top {
+      display: flex;
+      justify-content: space-between;
+      margin-top: 40px;
+      .door-ev {
+        height: 42px;
+        line-height: 42px;
+        background: #f4f5f8;
+        border-radius: 1px;
+        padding: 0 20px;
+        .indoor-ev {
+          color: #57a4f7;
+          padding-left: 14px;
+        }
+        .outdoor-ev {
+          padding-left: 40px;
+        }
+      }
+      .ev-standard{
+         padding-left:10px;
+         margin-right: 300px;
+      }
+      .ev-scale {
+        margin-right: 100px;
+      }
+    }
+    .under-stander{
+      float: right;
+      margin-right: 200px;
+    }
+    .equipment-view {
+      .floor {
+        margin: 40px 0 14px 0;
+        p {
+          margin-bottom: 30px;
+        }
+        .floor-num {
+          padding: 6px 19px;
+          font-weight: bold;
+          margin-right: 30px;
+          color: #4a9ef9;
+          text-align: center;
+          background: #fff;
+          cursor: pointer;
+          border: 1px solid #4a9ef9;
+          border-radius: 1px;
+        }
+        ul {
+          li {
+            display: inline-block;
+            width: 164px;
+            height: 64px;
+            position: relative;
+            margin-right: 4px;
+            border-radius: 2px;
+            &:hover .hover-div {
+              display: block;
+            }
+            span {
+              position: absolute;
+              width: 56px;
+              height: 28px;
+              line-height: 28px;
+              text-align: center;
+              display: inline-block;
+              background: #fff;
+              right: 10px;
+              bottom: 10px;
+              border-radius: 1.5px;
+              opacity: 0.4;
+            }
+          }
+        }
+      }
+    }
+  }
+  .en-table {
+    display: flex;
+    margin-top: 60px;
+    .en-good {
+      padding-right: 20px;
+    }
+  }
+}
+</style>

+ 107 - 0
src/modules/environment/pages/main/temperatureBar.vue

@@ -0,0 +1,107 @@
+<template>
+    <div id="temperatureBar">
+        <span class="remark-icon"></span><p class="my-title">温度统计</p>
+        <div id="barCharts"> </div>
+    </div>
+</template>
+
+<script>
+var echarts = require("echarts");
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: ['stat'],
+
+  data() {
+    return {
+      arr:[],
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.dealData();
+  },
+
+  methods: {
+    dealData(){
+      this.arr = [];
+      this.arr.push(this.stat.to20Sum,this.stat.to24Sum,this.stat.to26Sum,this.stat.to28Sum,
+      this.stat.from28Sum);
+       this.barchart();
+    },
+    barchart() {
+      var temperatureBar = echarts.init(document.getElementById("barCharts"));
+      temperatureBar.setOption({
+        color: ["#3398DB"],
+        tooltip: {
+          trigger: "axis",
+          axisPointer: {
+            // 坐标轴指示器,坐标轴触发有效
+            type: "shadow" // 默认为直线,可选为:'line' | 'shadow'
+          }
+        },
+        grid: {
+          left: "3%",
+          right: "4%",
+          bottom: "3%",
+          containLabel: true
+        },
+        xAxis: [
+          {
+            type: "category",
+            data: ["20℃以下", "20℃~24℃", "24℃~26℃", "26℃~28℃", "28℃以上"],
+        
+            axisLabel : {    boundaryGap: true,
+			                show:true,
+			                interval: 0
+			            }
+          }
+        ],
+        yAxis: [
+          {
+            type: "value"
+          }
+        ],
+        series: [
+          {
+            name: "温度统计",
+            type: "bar",
+            barWidth: "40%",
+            data: this.arr
+          }
+        ]
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+#temperatureBar {
+  width:35%;
+  margin-right: 10px;
+  border: 1px solid #dbdde4;
+  padding: 0 10px;
+  border-radius: 3px;
+  #barCharts {
+    width: 95%;
+    height: 270px;
+  }
+  .my-title {
+    height: 40px;
+    font-size:18px;
+    font-weight: bold;
+    display: inline-block;
+  }
+  .remark-icon {
+    padding: 7px 3px;
+    display: inline-block;
+    background: #4a9ef9;
+    margin: 20px 10px 0 20px;
+  }
+}
+</style>

+ 89 - 0
src/modules/environment/pages/main/temperatureTable.vue

@@ -0,0 +1,89 @@
+<template>
+  <div class="temperatureTable">
+      <span class="remark-icon"></span><p class="my-title">温度统计表</p>
+      <i-Table stripe  size="large" :columns="columns1" :data="tableData"></i-Table>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: ['tableData'],
+
+  data() {
+    return {
+      columns1: [
+        {
+          title: "楼层",
+          key: "FloorLocalName"
+        },
+        {
+          title: "测量点个数",
+          key: "count"
+        },
+        {
+          title: "20℃以下的个数",
+          key: "to20"
+        },
+        {
+          title: "20℃~24℃的个数",
+          key: "to24"
+        },
+        {
+          title: "24℃~26℃的个数",
+          key: "to26"
+        },
+        {
+          title: "26℃~28℃的个数",
+          key: "to28",
+          className: "table-info-column"
+        },
+        {
+          title: "28℃以上的个数 ",
+          key: "from28"
+        }
+      ],
+      data1: []
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+
+  },
+
+  methods: {
+   
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.my-title {
+  height: 40px;
+  font-size:18px;
+  font-weight: bold;
+  display: inline-block;
+}
+.remark-icon {
+  padding: 7px 3px;
+  display: inline-block;
+  background: #4a9ef9;
+  margin: 20px 10px 0 20px;
+}
+.temperatureTable {
+  width: 65%;
+  border: 1px solid #dbdde4;
+  border-radius: 3px;
+  padding: 0 20px;
+}
+</style>
+<style lang="less">
+.ivu-table-wrapper {
+  border: none;
+}
+</style>

+ 42 - 0
src/modules/environment/pages/mass/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="ev-mass">
+mass
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 42 - 0
src/modules/environment/pages/plane/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="ev-plane">
+      plane
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 38 - 0
src/modules/environment/router/index.js

@@ -0,0 +1,38 @@
+const Home = () => import(/* webpackChunkName: 'environment' */ '../pages/home')
+const Main = () => import(/* webpackChunkName: 'environment' */ '../pages/main')
+const Analys = () => import(/* webpackChunkName: 'environment' */ '../pages/analys')
+const Heal = () => import(/* webpackChunkName: 'environment' */ '../pages/heal')
+const Plane = () => import(/* webpackChunkName: 'environment' */ '../pages/plane')
+const Mass = () => import(/* webpackChunkName: 'environment' */ '../pages/mass')
+export default [
+  {
+    path: 'environment',
+    component: Home,
+    redirect: 'environment/main',
+    children: [
+      {
+        path: 'main',
+        component: Main,
+        meta: {
+          keepAlive: true
+        }
+      },
+      {
+        path: 'analys',
+        component: Analys
+      },
+      {
+        path: 'heal',
+        component: Heal
+      },
+      {
+        path: 'plane',
+        component: Plane
+      },
+      {
+        path: 'mass',
+        component: Mass
+      }
+    ]
+  }
+]

+ 15 - 0
src/modules/environment/store/index.js

@@ -0,0 +1,15 @@
+/**
+ * 环境
+ */
+export default {
+    namespaced: true,
+    state: {
+        name: '环境'
+    },
+    mutations: {
+        
+    },
+    actions: {
+
+    },
+}

+ 7 - 0
src/modules/facilitiesAndStaff/index.js

@@ -0,0 +1,7 @@
+import store from './store'
+import router from './router'
+
+export {
+    store,
+    router
+}

+ 89 - 0
src/modules/facilitiesAndStaff/pages/asideMenu/index.vue

@@ -0,0 +1,89 @@
+<template>
+    <div class="aside-menu">
+      <ul>
+        <li>
+          <router-link  to="equipmentView">
+           <i class="iconfont icon-zonglan"></i>
+            <span style="padding-left:52px;">设备总览</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="peopleView">
+            <i class="iconfont icon-people"></i>
+            <span style="padding-left:52px;">人员总览</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="orderInfoView">
+             <i class="iconfont icon-gongdan1"></i>
+            <span style="padding-left:52px;">工单信息总览</span>
+          </router-link>
+        </li>
+        <li v-if="meos">
+          <a :href="`${meos}`" target="_blank">
+           <i class="iconfont icon-meos"></i>
+            <span>设备设施管理系统</span>
+          </a>
+        </li>
+      </ul>
+    </div>
+</template>
+
+<script>
+import axios from 'axios'
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      meos:""
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getSubSystemUrl();
+  },
+
+  methods: {
+    getSubSystemUrl(){
+     axios.get(`/equipment/info-mng-backend/config/getSubSystemUrl?projectId=${this.$store.state.projectId}`).then(res=>{
+       let data = res.data.data;
+       this.meos = data.meos;
+     })
+   }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.aside-menu {
+  ul {
+    > li {
+      &:hover {
+        background: #eee;
+      }
+      a {
+        display: inline-block;
+        white-space: nowrap;
+        padding: 10px 30px;
+        width: 100%;
+        color: #b6bccb;
+        span {
+          padding-left: 37px;
+          color: black;
+          font-size: 15px;
+        }
+      }
+      .router-link-active {
+        color: #57a4f7;
+      }
+    }
+  }
+}
+</style>

+ 53 - 0
src/modules/facilitiesAndStaff/pages/equipmentView/Card.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="card">
+      <div class="evc-type" v-for="(item,index) in groupByCate" :key="index" v-if="groupByCate">
+        <p style="height:39px;">{{item.name}}</p>
+        <p v-if="isShow">数量:{{item.intactCount}} <span style="padding-left:30px;">总数:{{item.count}}</span> </p>
+        <p v-else>数量: {{item.count}} </p>
+      </div>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: ["isShow", "groupByCate"],
+
+  data() {
+    return {
+    
+    };
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.card {
+  height: 345px;
+  width: 100%;
+  background: #f4f5f8;
+  padding-top: 18px;
+  overflow-y: scroll;
+  .evc-type {
+    width: 210px;
+    height: 90px;
+    background: #ffffff;
+    border: 0 solid #dbdde4;
+    border-radius: 1px;
+    margin: 0 20px 20px 20px;
+    display: inline-block;
+    p {
+      padding: 20px 0 0 20px;
+    }
+  }
+}
+</style>

+ 42 - 0
src/modules/facilitiesAndStaff/pages/equipmentView/Detail.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="fs-detail">
+   detail
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 578 - 0
src/modules/facilitiesAndStaff/pages/equipmentView/index.vue

@@ -0,0 +1,578 @@
+<template>
+    <div class="pandect">
+        <div>
+            <p class="device">当前设备总体运行状态&nbsp;&nbsp;&nbsp;
+                <span class="states" v-if="eqIntactRate>=99">优</span>
+                <span class="states" v-if="99>eqIntactRate>=98">良好</span>
+                <span class="states" v-if="98>eqIntactRate>=95">中</span>
+                <span class="states" v-if="eqIntactRate<95">差</span>
+            </p>
+            <!-- <div v-if="groupByCate.length==0 && sum==0">
+                <Spin fix>
+                    <Icon type="ios-loading" size=40 class="demo-spin-icon-load"></Icon>
+                    <div>Loading</div>
+                </Spin>
+            </div>  -->
+            <Tabs type="card"   @on-click="getData">
+                <TabPane :label="`设备设施总数 ${sum}`">
+                    <card :isShow="false" :groupByCate='groupByCate'></card>
+                </TabPane>
+                <TabPane :label="`设备完好率 ${eqIntactRate}%`"> 
+                    <card :isShow="true" :groupByCate='groupByCate'></card>
+                </TabPane>
+                <TabPane :label="`保修及时率 ${maintTimelinessRate}%`"> 
+                    <card :isShow="true"></card>
+                </TabPane>
+                <TabPane :label="`超期服役率 ${overdueServiceRate}%`"> 
+                    <card :isShow="true"></card>
+                </TabPane>
+                <TabPane :label="`运行偏差率 ${operatingDeviationRate}%`"> 
+                    <card :isShow="true"></card>
+                </TabPane>
+            </Tabs>
+        </div>
+        <div class="my-bottom">
+            <div class="pie">
+                <span class="remark-icon"></span><p class="my-title">设备专业分类</p>
+                <div id="pieImg" style="height:279px;width:368px" ref="mayPieCharts"></div>
+                <table class="description">
+                <tr class="normal" v-for="(item, index) in groupByMajor" :key="index">
+                    <td><i :style="`background-color: ${item.itemStyle.color}`"></i>{{item.name}}</td>
+                    <td>{{((item.value / groupByMajorTotalValue)*100).toFixed(2)}}%</td>
+                </tr>
+                </table>
+            </div>
+            <div class="my-table">
+                <span class="remark-icon"></span><p class="my-title">近12个月已维修过的设备数量</p>
+                <div id="barImg" style="height:340px;width:100%" ref="mayBarCharts"></div>
+                <div class="legend">
+                    <span class="good"><i style="background:#4A9EF9"></i> 维修中的</span>
+                </div>
+            </div>
+        </div>
+        <div class="attention">
+            <span class="remark-icon"></span><p class="my-title">重点关注设备</p>
+            <i-Table stripe :columns="columns1" :data="data1" size="large"></i-Table>
+        </div>
+        <div class="mask-bg" @click="closeLay"></div>
+       <div class="mask-lay" :style="`width: ${maskWidth}`">
+         <detail></detail>
+       </div>
+    </div>
+</template>
+
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+import card from "./Card.vue";
+import detail from './Detail.vue'
+export default {
+  components: {
+    card,
+    detail
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      isShow: true,
+      sum: 0,
+      eqIntactRate: 100,
+      maintTimelinessRate: 80,
+      operatingDeviationRate: 80,
+      overdueServiceRate: 80,
+      rateMajor: [],
+      maskWidth:"0px",
+      columns1: [
+        {
+          title: "设备本地编号",
+          key: "name"
+        },
+        {
+          title: "设备本地名称",
+          key: "position"
+        },
+        {
+          title: "安装位置",
+          key: "craft"
+        },
+        {
+          title: "运行状态",
+          key: "performance"
+        },
+        {
+          title: "近30天维修次数",
+          key: "wage",
+          sortable: true
+        },
+        {
+          title: "未关闭的工单",
+          key: "mark",
+          sortable: true
+        },
+        {
+          title: "报警数量 ",
+          key: "ontime",
+          sortable: true
+        },
+        {
+          title: "操作",
+          key: "operation",
+          className: "table-btn-column",
+          render: (h, params) => {
+            return h(
+              "Button",
+              {
+                props: {
+                  type: "text",
+                  size: "large"
+                },
+                on: {
+                  click: () => {
+                    document.querySelector("body").style.overflow = "hidden";
+                    document.querySelector('.mask-bg').style.display = 'block';
+                    this.maskWidth = "450px;"
+                  }
+                }
+              },                                                                                                             "查看详情"
+            );
+          }
+        }
+      ],
+      data1: [
+        {
+          name: "GCWK-8-JKZF",
+          position: "2#风冷热泵",
+          craft: "8号楼-JC-制冷机房",
+          performance: "开启",
+          wage: 0,
+          mark: 0,
+          ontime: 0
+        },
+        {
+          name: "GCWK-8-JKZF",
+          position: "1#离心泵",
+          craft: "8号楼-JC-制冷机房",
+          performance: "开启",
+          wage: 0,
+          mark: 0,
+          ontime: 0
+        },
+        {
+          name: "GCWK-8-JKZF",
+          position: "7#给水加压泵",
+          craft: "8号楼-B1F-生活水泵房",
+          performance: "开启",
+          wage: 0,
+          mark: 0,
+          ontime: 1
+        },
+        {
+          name: "GCWK-8-GPS",
+          position: "1#紫外线消防器",
+          craft: "8号楼-B1F-生活水泵房",
+          performance: "开启",
+          wage: 0,
+          mark: 0,
+          ontime: 0
+        },
+        {
+          name: "GCWK-8-B1-RCC",
+          position: "挡杆",
+          craft: "8号楼-车库出口",
+          performance: "开启",
+          wage: 0,
+          mark: 1,
+          ontime: 0
+        }
+      ],
+      groupByCate: [],
+      groupByMajor: [],
+      groupByMajorTotalValue: 100,
+      colors: {
+        SE: "#4A9EF9", // 强电
+        WE: "#6ECAF7", // 弱电【key值待修改】
+        AC: "#73DA99", // 空调
+        WS: "#F3E171", // 给排水
+        FF: "#FABA53", // 消防
+        SP: "#EF8DCC", // 安防【key值待修改】
+        OT: "#63ADE2" ,// 综合专业(接口里叫其他专业)
+        ST:"#DEFB92",
+        ID:"#E2F516",
+        DW:"#BFEA2B"
+      }
+    };
+  },
+
+  computed: {},
+  mounted() {
+    this.getTotal();
+    this.getEqTotal();
+    this.drawBar();
+  },
+
+  methods: {
+    //弹窗关闭事件
+    closeLay(){
+       document.querySelector("body").style.overflow = "auto";
+       document.querySelector('.mask-bg').style.display = 'none';
+       this.maskWidth = "0px;"
+    },
+    //每个卡片头的点击事件
+    getData(name){
+      if(name==0){
+        this.getEqTotal();
+      }else if(name==1){
+        this.getRate();
+      }
+    },
+    //近12个月已维修过的设备数量图
+    drawBar() {
+      let mayBarCharts = echarts.init(document.getElementById("barImg"));
+      var dataAxis = ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"];
+      var data = [8, 10, 5, 6, 8, 12, 10, 5, 7, 9, 9, 8];
+      var yMax = 14;
+      var dataShadow = [];
+
+      for (var i = 0; i < data.length; i++) {
+        dataShadow.push(yMax);
+      }
+
+      mayBarCharts.setOption({
+        color: ['#3398DB'],
+        tooltip : {
+            trigger: 'axis',
+            axisPointer : {            // 坐标轴指示器,坐标轴触发有效
+                type : 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
+            }
+        },
+        grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis : [
+            {
+                type : 'category',
+                data : dataAxis,
+                axisTick: {
+                    alignWithLabel: true
+                }
+            }
+        ],
+        yAxis : [
+            {
+                type : 'value'
+            }
+        ],
+        series : [
+            {
+                name:'维修中',
+                type:'bar',
+                barWidth: '60%',
+                data:data
+            }
+        ]
+      });
+       
+      // Enable data zoom when user click bar.
+      var zoomSize = 6;
+    },
+    //设备专业分类图
+    drawPie() {
+      let mayPieCharts = echarts.init(document.getElementById("pieImg"));
+      mayPieCharts.setOption({
+        tooltip: {
+          trigger: "item",
+          formatter: "{a} <br/>{b} : {c} ({d}%)"
+        },
+
+        series: [
+          {
+            name: "",
+            type: "pie",
+            radius: "65%",
+            center: ["50%", "50%"],
+            selectedMode: "",
+            data: this.groupByMajor,
+            itemStyle: {
+              emphasis: {
+                shadowBlur: 10,
+                shadowOffsetX: 0,
+                shadowColor: "rgba(0, 0, 0, 0.5)"
+              },
+              normal: {
+                label: {
+                  show: false //隐藏标示文字
+                },
+                labelLine: {
+                  show: false //隐藏标示线
+                }
+              }
+            }
+          }
+        ]
+      });
+      setTimeout(() => {
+         window.onresize = function () {
+          mayPieCharts.resize()
+        }
+      }, 10)
+    },
+    //设备专业分类数据
+    getEqTotal() {
+       this.groupByCate = [];
+      axios
+        .get(`/equipment/info-mng-backend/eq/getAll?projectId=${this.$store.state.projectId}`)
+        .then(res => {
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.groupByCate = data.groupByCate;
+            this.groupByMajor = this.formatMajor(data.groupByMajor);
+            this.groupByMajorTotalValue = this.calculationMajorTotalValue(
+              data.groupByMajor
+            );
+            this.drawPie();
+          }
+        });
+    },
+    //设备设施总数,完好率
+    getTotal() {
+      axios
+        .get(`/equipment/info-mng-backend/saas/getEqStatistics?projectId=${this.$store.state.projectId}`)
+        .then(res => {       
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.sum = data.eqCount;
+            this.overdueServiceRate = data.overdueServiceRate;
+            this.operatingDeviationRate = data.operatingDeviationRate;
+            this.maintTimelinessRate = data.maintTimelinessRate;
+            this.eqIntactRate = data.eqIntactRate;
+          }
+        });
+    },
+    getRate() {
+      this.groupByCate = [];
+      axios
+        .get(`/equipment/info-mng-backend/saas/getEqIntactRate?projectId=${this.$store.state.projectId}`)
+        .then(res => {
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.groupByCate = data.groupByMajor;
+          }
+        });
+    },
+    formatMajor(groupByMajor) {
+      return groupByMajor.map(item => {
+        return {
+          name: item.name,
+          value: item.count,
+          itemStyle: {
+            color: this.colors[item.major]
+          }
+        };
+      });
+    },
+    calculationMajorTotalValue(groupByMajor) {
+      let total = 0;
+      groupByMajor.forEach(i => {
+        total += parseFloat(i.count);
+      });
+      return total;
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.pandect {
+  background: #fff;
+  padding: 20px;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  .device {
+    color: #3b4254;
+    padding-bottom: 30px;
+    font-size: 18px;
+    font-weight: bold;
+    .states {
+      font-weight: bold;
+      font-size: 30px;
+      color: #73da99;
+    }
+  }
+  .my-bottom {
+    display: flex;
+    margin: 64px 0 20px 0;
+    .pie {
+      width: 452px;
+      margin-right: 20px;
+      border: 1px solid #dbdde4;
+      border-radius: 1px;
+      position: relative;
+      .my-title {
+        height: 40px;
+        font-size: 18px;
+        font-weight: bold;
+        display: inline-block;
+      }
+      #pieImg {
+        top: -40px;
+        margin: 0 auto;
+      }
+      .description {
+        margin-left: 20px;
+        margin-top: -60px;
+        display: flex;
+        flex-wrap: wrap;
+        tr {
+          width: 50%;
+          padding: 3px 0;
+        }
+        i {
+          width: 8px;
+          height: 8px;
+          display: inline-block;
+          background: #000;
+          border-radius: 50%;
+          margin-right: 10px;
+        }
+        td {
+          height: 18px;
+          color: #62697b;
+          &:nth-child(2n) {
+            padding-left: 5px;
+          }
+        }
+      }
+      .remark-icon {
+        padding: 7px 3px;
+        display: inline-block;
+        background: #4a9ef9;
+        margin: 20px 10px 0 20px;
+      }
+    }
+    .my-table {
+      width: 1288px;
+      border: 1px solid #dbdde4;
+      border-radius: 1px;
+      position: relative;
+      .my-title {
+        height: 40px;
+        font-size: 18px;
+        font-weight: bold;
+        display: inline-block;
+      }
+      .remark-icon {
+        padding: 7px 3px;
+        display: inline-block;
+        background: #4a9ef9;
+        margin: 20px 10px 0 20px;
+      }
+      .legend {
+        position: absolute;
+        top: 59px;
+        right: 115px;
+        i {
+          width: 10px;
+          height: 10px;
+          display: inline-block;
+          background: #000;
+          margin-right: 8px;
+        }
+        .good {
+          padding-right: 20px;
+        }
+      }
+    }
+  }
+  .attention {
+    width: 100%;
+    border: 1px solid #dbdde4;
+    border-radius: 1px;
+    padding: 0 20px;
+    .my-title {
+      height: 40px;
+      display: inline-block;
+      font-size: 18px;
+      font-weight: bold;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 0;
+    }
+  }
+  .mask-bg{
+    position: fixed;
+    top: 0;
+    bottom:0;
+    right: 0;
+    left: 0;
+    z-index: 9999;
+    display: none ;
+    opacity: 0.4;
+  }
+  .mask-lay{
+    position: fixed;
+    top: 50px;
+    bottom:0;
+    right: 0;
+    z-index: 99999;
+    transition: all 0.5s ease-in-out;
+    background:#fff;
+  }
+}
+</style>
+<style lang="less">
+.pandect {
+  .ivu-tabs-bar {
+    margin-bottom: 0;
+  }
+  .ivu-tabs-nav-container{
+    font-size:16px;
+  }
+ .ivu-table-large {
+    font-size:16px;
+  }
+  .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    background: #fff;
+  }
+  .ivu-tabs .ivu-tabs-tabpane {
+    border: 1px solid #dbdde4;
+    border-top: none;
+  }
+  .ivu-tabs.ivu-tabs-card>.ivu-tabs-bar .ivu-tabs-tab,.ivu-tabs .ivu-tabs-content-animated{
+   transform:none;
+   transition:none;
+  }
+  .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab-active {
+    background: #f4f5f8;
+  }
+  .ivu-table .ivu-table-tbody .table-info-column {
+    color: #faba53;
+  }
+  .ivu-table-wrapper {
+    border: none;
+  }
+  .ivu-table:before {
+    height: 0;
+  }
+  .ivu-table:after {
+    width: 0;
+  }
+  .ivu-table td,
+  .ivu-table th {
+    border: none;
+  }
+  .ivu-table .ivu-table-tbody .ivu-btn-text {
+    color: #57a4f7;
+  }
+}
+</style>
+

+ 66 - 0
src/modules/facilitiesAndStaff/pages/home/index.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="fas-home">
+    <aside-mune class="aside"></aside-mune>
+    <div class="content">
+      <router-view>
+      </router-view>
+    </div>
+  </div>
+</template>
+
+<script>
+import AsideMune from "../asideMenu";
+
+export default {
+  components: {
+    AsideMune
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.fas-home {
+  .aside {
+    position: fixed;
+    z-index: 999;
+    top: 50px;
+    left: 0;
+    bottom: 0;
+    width: 80px;
+    border-right: 1px solid #ebecf0;
+    transition: all 0.3s ease-in-out;
+    overflow-x: hidden;
+    overflow-y: auto;
+    background: #fff;
+  }
+  .content {
+    margin-left: 80px;
+    padding: 70px 20px 20px 20px;
+    transition: all 0.3s ease-in-out;
+    background: #ebecf0;
+    min-width: 1100px;
+  }
+  & > .aside:hover {
+    width: 260px;
+    background: #fff;
+  }
+  // &>.aside:hover + .content {
+  //   margin-left: 260px;
+  //   padding-left:20px;
+  // }
+}
+</style>

+ 757 - 0
src/modules/facilitiesAndStaff/pages/orderInfoView/index.vue

@@ -0,0 +1,757 @@
+<template>
+  <div class="info-sumary">
+      <p class="accumulation">月累计工单数量 <span class="my-size">1200</span> 个   <span class="ratio">工单按时完成率 </span><span class="my-size">95</span>%</p>
+      <div class="picture">
+        <span class="remark-icon"></span><p class="my-title">本月5类工单数量</p>
+         <i-Select v-model="model1" style="width:140px;float:right;margin-right:40px;margin-top:20px">
+            <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+        </i-Select>
+        <div id="bill-charts">
+           
+        </div>
+        <div class="legend">
+           <div style="background:#4A9EF9"></div>运行类工单&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+           <div style="background:#6ECAF7"></div>巡检类工单&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+           <div style="background:#73DA99"></div>维保类工单&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+           <div style="background:#F3E171"></div>维修类工单&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+           <div style="background:#FABA53"></div>综合工单&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+           <div style="background:#DFE7F0"></div>上月工单总数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        </div>
+      </div>
+      <div class="my-bottom">
+        <div class="pie">
+           <span class="remark-icon"></span><p class="my-title">工单数量</p>
+           <div class="tt-echarts">
+              <div id="tt_bill"> </div>
+              <div class="tt-legend">
+                <div><div class="square"></div>运行类工单<span class="num">500</span><span class="rat">40%</span></div>
+                <div><div class="square" style="border:1px solid #6ECAF7"></div>巡检类工单<span class="num">400</span><span class="rat">38%</span></div>
+                <div><div class="square" style="border:1px solid #73DA99"></div>维保类工单<span class="num">100</span><span class="rat">30%</span></div>
+                <div><div class="square" style="border:1px solid #F3E171"></div>维修类工单<span class="num">100</span><span class="rat">22%</span></div>
+                <div><div class="square" style="border:1px solid #FABA53"></div>综合类工单<span class="num">100</span><span class="rat">18%</span></div>
+              </div>
+           </div>
+        </div>
+        <div class="my-table">
+          <span class="remark-icon"></span><p class="my-title">已完成工单数量</p>
+          <div class="tt-v">
+            <i-Table stripe  :columns="columns1" :data="data1" size="large"></i-Table>
+          </div>  
+        </div>
+      </div>
+  </div>
+</template>
+ 
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+export default {
+  components: {},
+ 
+  mixins: [],
+ 
+  props: {},
+ 
+  data() {
+    return {
+      columns1: [
+        {
+          title: "时间",
+          key: "name"
+        },
+        {
+          title: "合计",
+          key: "position"
+        },
+        {
+          title: "运行",
+          key: "craft"
+        },
+        {
+          title: "巡检",
+          key: "performance"
+        },
+        {
+          title: "维保",
+          key: "wage"
+        },
+        {
+          title: "维修",
+          key: "mark",
+          className: "table-info-column"
+        },
+        {
+          title: "综合",
+          key: "ontime"
+        }
+      ],
+      data1: [
+        {
+          name: "上月",
+          position: 1600,
+          craft: 400,
+          performance: 300,
+          wage: 300,
+          mark: 300,
+          ontime: 300
+        },
+        {
+          name: "本月",
+          position: 1200,
+          craft: 300,
+          performance: 312,
+          wage: 240,
+          mark: 300,
+          ontime: 300
+        },
+        {
+          name: "上周",
+          position: 400,
+          craft: 100,
+          performance: 200,
+          wage: 300,
+          mark: 300,
+          ontime: 300
+        },
+        {
+          name: "本周",
+          position: 300,
+          craft: 80,
+          performance: 80,
+          wage: 300,
+          mark: 300,
+          ontime: 300
+        },
+        {
+          name: "昨日",
+          position: 40,
+          craft: 10,
+          performance: 8,
+          wage: 300,
+          mark: 300,
+          ontime: 300
+        },
+        {
+          name: "今日",
+          position: 45,
+          craft: 12,
+          performance: 13,
+          wage: 300,
+          mark: 300,
+          ontime: 300
+        }
+      ],
+      cityList: [
+        {
+          value: "一月",
+          label: "一月"
+        },
+        {
+          value: "二月",
+          label: "二月"
+        },
+        {
+          value: "三月",
+          label: "三月"
+        },
+        {
+          value: "四月",
+          label: "四月"
+        },
+        {
+          value: "五月",
+          label: "五月"
+        },
+        {
+          value: "六月",
+          label: "六月"
+        },
+        {
+          value: "七月",
+          label: "七月"
+        },
+        {
+          value: "八月",
+          label: "八月"
+        },
+        {
+          value: "九月",
+          label: "九月"
+        },
+        {
+          value: "十月",
+          label: "十月"
+        },
+        {
+          value: "十一月",
+          label: "十一月"
+        },
+        {
+          value: "十二月",
+          label: "十二月"
+        }
+      ],
+      model1: "一月"
+    };
+  },
+ 
+  computed: {},
+ 
+  mounted() {
+    this.initCharts();
+    this.draw();
+  },
+ 
+  methods: {
+    initCharts() {
+      var bill = echarts.init(document.getElementById("bill-charts"));
+      bill.setOption({
+        tooltip: {
+          trigger: "axis",
+          axisPointer: {
+            // 坐标轴指示器,坐标轴触发有效
+            type: "shadow" // 默认为直线,可选为:'line' | 'shadow'
+          }
+        },
+        grid: {
+          left: "3%",
+          right: "4%",
+          bottom: "3%",
+          containLabel: true
+        },
+        xAxis: [
+          {
+            type: "category",
+            data: [
+              "1",
+              "2",
+              "3",
+              "4",
+              "5",
+              "6",
+              "7",
+              "8",
+              "9",
+              "10",
+              "11",
+              "12",
+              "13",
+              "14",
+              "15",
+              "16",
+              "17",
+              "18",
+              "19",
+              "20",
+              "21",
+              "22",
+              "23",
+              "24",
+              "25",
+              "26",
+              "27",
+              "28",
+              "29",
+              "30",
+              "31"
+            ]
+          }
+        ],
+        yAxis: [
+          {
+            type: "value"
+          }
+        ],
+        series: [
+          {
+            name: "运行类工单",
+            type: "bar",
+            barWidth: 8,
+            itemStyle: {
+              color: "#4A9EF9"
+            },
+            stack: "搜索引擎",
+            data: [
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              132
+            ]
+          },
+          {
+            name: "巡检类工单",
+            type: "bar",
+            itemStyle: {
+              color: "#6ECAF7"
+            },
+            stack: "搜索引擎",
+            data: [
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              100
+            ]
+          },
+          {
+            name: "维保类工单",
+            type: "bar",
+            itemStyle: {
+              color: "#73DA99"
+            },
+            stack: "搜索引擎",
+            data: [
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              132
+            ]
+          },
+          {
+            name: "维修类工单",
+            type: "bar",
+            itemStyle: {
+              color: "#F3E171"
+            },
+            stack: "搜索引擎",
+            data: [
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              100
+            ]
+          },
+          {
+            name: "综合类工单",
+            type: "bar",
+            itemStyle: {
+              color: "#FABA53"
+            },
+            stack: "搜索引擎",
+            data: [
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              120,
+              100,
+              110,
+              114,
+              104,
+              107,
+              100,
+              118,
+              113,
+              100,
+              100
+            ]
+          },
+          {
+            name: "上月工单总数",
+            type: "bar",
+            itemStyle: {
+              color: "#DFE7F0"
+            },
+            data: [
+              450,
+              460,
+              500,
+              520,
+              570,
+              490,
+              440,
+              459,
+              478,
+              492,
+              450,
+              460,
+              500,
+              520,
+              570,
+              490,
+              440,
+              459,
+              478,
+              492,
+              450,
+              460,
+              500,
+              520,
+              570,
+              490,
+              440,
+              459,
+              478,
+              492,
+              450
+            ]
+          }
+        ]
+        
+      });
+      setTimeout(() => {
+         window.onresize = function () {
+          bill.resize()
+        }
+      }, 10)
+    },
+    draw() {
+      let myChart = echarts.init(document.getElementById("tt_bill"));
+ 
+      var scaleData = [
+        {
+          name: "运行类工单",
+          value: 500
+        },
+        {
+          name: "巡检类工单",
+          value: 400
+        },
+        {
+          name: "维保类工单",
+          value: 100
+        },
+        {
+          name: "维修类工单",
+          value: 100
+        },
+        {
+          name: "综合工单",
+          value: 100
+        }
+      ];
+ 
+      var placeHolderStyle = {
+        normal: {
+          label: {
+            show: false
+          },
+          labelLine: {
+            show: false
+          },
+          color: "rgba(0, 0, 0, 0)",
+          borderColor: "rgba(0, 0, 0, 0)",
+          borderWidth: 0
+        }
+      };
+      var data = [];
+ 
+      var color = ["#73DA99", "#FABA53", "#4A9EF9", "#6ECAF7", "#F3E171"];
+      for (var i = 0; i < scaleData.length; i++) {
+        data.push(
+          {
+            value: scaleData[i].value,
+            name: scaleData[i].name,
+            itemStyle: {
+              normal: {
+                borderWidth: 15,
+                borderColor: color[i]
+              }
+            }
+          },
+          {
+            value: 5,
+            name: "",
+            itemStyle: placeHolderStyle
+          }
+        );
+      }
+ 
+      var seriesObj = [
+        {
+          name: "",
+          type: "pie",
+          clockWise: false,
+          radius: [70, 70],
+          hoverAnimation: false,
+          itemStyle: {
+            normal: {
+              label: {
+                show: true,
+                position: "center",
+                color: "#3B4254",
+                fontSize: 18,
+                formatter: function(params) {
+                  return "\n\n\n\n\n工单总数\n\n 1200";
+                }
+              }
+            }
+          },
+          data: data
+        }
+      ];
+ 
+      myChart.setOption({
+        tooltip: {
+          show: false
+        },
+        legend: {
+          show: false
+        },
+        toolbox: {
+          show: true
+        },
+        series: seriesObj
+      });
+    }
+  }
+};
+</script>
+ 
+<style scoped lang="less" >
+.info-sumary {
+  padding: 20px;
+  background: #fff;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  .accumulation {
+    line-height: 40px;
+    .my-size {
+      font-size: 20px;
+      color: #4a9ef9;
+      font-weight: bold;
+    }
+    .ratio {
+      padding-left: 50px;
+    }
+  }
+  .picture {
+    margin: 20px 0;
+    border: 1px solid #dbdde4;
+    border-radius: 3px;
+ 
+    position: relative;
+    .my-title {
+      height: 40px;
+      font-size: 16px;
+      display: inline-block;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+    #bill-charts {
+      width: 100%;
+      height: 390px;
+    }
+    .legend {
+      position: absolute;
+      right: 58px;
+      top: 60px;
+      > div {
+        display: inline-block;
+        width: 10px;
+        height: 10px;
+        margin-right: 5px;
+      }
+    }
+  }
+  .my-bottom {
+    display: flex;
+    .pie {
+      width: 25.7%;
+      min-width: 400px;
+      padding-right: 2%;
+      border: 1px solid #dbdde4;
+      .tt-echarts {
+        #tt_bill {
+          width: 200px;
+          height: 200px;
+          margin-left: 90px;
+        }
+        .tt-legend {
+          margin-left: 100px;
+          > div {
+            height: 30px;
+          }
+          .square {
+            width: 8px;
+            height: 8px;
+            background: #fff;
+            border: 1px solid #4a9ef9;
+            border-radius: 50px;
+            display: inline-block;
+            margin-right: 10px;
+          }
+          .num {
+            margin-left: 15px;
+          }
+          .rat {
+            margin-left: 15px;
+          }
+        }
+      }
+      .my-title {
+        height: 40px;
+        font-size: 16px;
+        display: inline-block;
+      }
+      .remark-icon {
+        padding: 7px 3px;
+        display: inline-block;
+        background: #4a9ef9;
+        margin: 20px 10px 0 20px;
+      }
+    }
+    .my-table {
+      width: 75.2%;
+      border: 1px solid #dbdde4;
+      margin-left: 20px;
+      .my-title {
+        height: 40px;
+        font-size: 16px;
+        display: inline-block;
+      }
+      .remark-icon {
+        padding: 7px 3px;
+        display: inline-block;
+        background: #4a9ef9;
+        margin: 20px 10px 0 20px;
+      }
+      .tt-v {
+        padding: 0 20px 20px 20px;
+      }
+    }
+  }
+}
+</style>
+<style lang="less">
+.ivu-table-wrapper {
+  border: none;
+}
+.ivu-table:before {
+  height: 0;
+}
+.ivu-table:after {
+  width: 0;
+}
+.ivu-table td,
+.ivu-table th {
+  border: none;
+}
+</style>

+ 39 - 0
src/modules/facilitiesAndStaff/pages/peopleView/GiveMark.vue

@@ -0,0 +1,39 @@
+<template>
+  <div>
+      <span v-for="(item,index) in star" :key="index">☆</span>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      star: 4,
+      score: 3.3
+    };
+  },
+
+  computed: {
+    star() {
+      let result = [];
+      let newScore = this.score % 1 !== 0;
+      if (newScore == true) {
+        this.score = Math.floor(this.score) + 1;
+      }
+    }
+  },
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+</style>

+ 199 - 0
src/modules/facilitiesAndStaff/pages/peopleView/OnlinePeople.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="peoples">
+    <div class="career">
+        
+        <i-Select id="career" v-model="career" size="large" style="width:140px;height:36px" @on-change="initTable(career)">
+            <Option v-for="item in cityList" :value="item.name" :key="item.name">{{ item.label }}</Option>
+        </i-Select>
+        <i-Input v-model="keyword" size="large" placeholder="输入关键字搜索" style="width:auto" @on-change="initTable()">
+            <Icon type="ios-search" slot="suffix" @click="initTable()" style="color:#2d8cf0;cursor: pointer;"/>
+        </i-Input>
+    </div>
+    <p style="padding-bottom:14px">近30天数据统计</p>
+    <i-Table stripe  :columns="columns1" :data="data1" size="large"></i-Table>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+
+export default {
+  components: {},
+
+  mixins: [],
+
+  data() {
+    return {
+      keyword: "",
+      columns1: [
+        {
+          title: "姓名",
+          key: "name",
+        },
+        {
+          title: "职位",
+          key: "positionName",
+        },
+        {
+          title: "专业",
+          key: "specialtyName",
+          width:300
+        },
+        {
+          title: "绩效指标",
+          key: "performance",
+        },
+        {
+          title: "本月日平均工时",
+          key: "averageWorkHours",
+        },
+        {
+          title: "工作评分",
+          key: "averageWorkScore",
+          render: (h, params) => {
+            return h("Rate", {
+              props: {
+                value: parseFloat(params.row.averageWorkScore),
+                allowHalf: true,
+                disabled: true
+              },
+              style: {}
+            });
+          }
+        },
+        {
+          title: "按时完成率 ",
+          key: "ontimeFinishRate",
+        },
+        {
+          title: "当前在岗与否",
+          key: "onWorkingStatus",
+        },
+        {
+          title: "操作",
+          key: "operation",
+          className: "table-btn-column",
+          render: (h, params) => {
+            return h(
+              "Button",
+              {
+                props: {
+                  type: "text",
+                  size: "large"
+                },
+                on: {
+                  click: () => {
+                    this.$emit("showPeopleDetail", params.row.personId);
+                  }
+                }
+              },
+              "查看详情"
+            );
+          }
+        }
+      ],
+      cityList: [],
+      career: "",
+      majorMap: [
+        { 给排水: "WS" },
+        { 弱电: "WE" },
+        { 安防: "SP" },
+        { 强电: "SE" },
+        { 消防: "FF" },
+        { ccc: "CC" },
+        { 空调: "AC" },
+        { 其他: "OT" }
+      ],
+      timeList: [
+        {
+          value: "近30天",
+          label: "近30天"
+        },
+        {
+          value: "近7天",
+          label: "近7天"
+        }
+      ],
+      times: "近30天",
+      data1: [],
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getMajor();
+    this.initTable(this.career, this.keyword);
+  },
+
+  methods: {
+    //获取专业
+    getMajor() {
+      axios.get(`/equipment/info-mng-backend/saas/queryGeneralDictByKey`).then(res => {
+        if (res.data.result == "success") {
+          let data = res.data.data;
+          data.forEach(element => {
+            element.label = element.name;
+          });
+          this.cityList = data;
+        }
+      });
+    },
+    //表格数据查找事件
+    initTable(career) {
+      let majorValue = "";
+      let keywordValue = " ";
+      for (let i of this.cityList) {
+       if(i.label==career){
+         majorValue=i.code;
+       }
+      }
+      axios
+        .get(
+          `/equipment/info-mng-backend/saas/queryPersonList?projectId=${this.$store.state.projectId}&specialtyCode=${majorValue}&keyword=${
+            this.keyword
+          }`
+        )
+        .then(res => {
+          this.data1 = res.data.data;
+          this.$emit("showData", this.data1);
+        });
+    },
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.peoples {
+  background: #fff;
+  .career {
+    margin: 22px 0 20px 0;
+  }
+}
+</style>
+<style lang="less">
+.peoples {
+  .ivu-tabs-nav-container{
+    font-size:16px;
+  }
+  .ivu-table .ivu-table-tbody .table-info-column {
+    color: #faba53;
+  }
+  .ivu-table-large {
+    font-size:16px;
+  }
+  .ivu-table .ivu-table-tbody .ivu-btn-text {
+    color: #57a4f7;
+  }
+  .ivu-table-wrapper {
+    border: none;
+  }
+  .ivu-select {
+    padding-right: 10px;
+  }
+  .ivu-rate-star {
+    margin-right: -4px;
+  }
+}
+</style>

+ 289 - 0
src/modules/facilitiesAndStaff/pages/peopleView/OnlinePeopleDetail.vue

@@ -0,0 +1,289 @@
+<template>
+  <div class="online-people-detail" :style="`width: ${maskWidth}`">
+     <p class="particular">人员详情 —— <span>{{everyPeople.name}}</span><Icon type="md-close" class="close-btn"  @click="close"/></p> 
+     <span class="remark-icon"></span><p class="my-title">基本信息</p>
+     <div class="base-info">
+       <img class="base-img" :src="everyPeople.headPortrait?`http://58.251.20.250:7777/image-service/common/file_get?systemId=dataPlatform&key=${everyPeople.headPortrait}`:`/static/img/noimg.png`" alt="员工头像">
+       <div class="each-grade">
+         <p class="people-grade">个人绩效</p>
+         <p class="grade">{{everyPeople.performance}}</p>
+       </div>
+     </div>
+     <div class="repair-order">
+       <p>本月工单总量: {{orderStat.currentMonthWoConut}}个 <span class="completion"> 完成率:{{orderStat.woFinishRate}}</span></p>
+       <div class="tt-echarts">
+        <div id="detail_bill" style=" width: 200px;height: 200px;"> </div>
+        <div class="tt-legend">
+          <div v-for="(item1,index1) in orderStat.woTypeCountInfo" :key="index1">
+            <div class="square" :style="`border:1px solid ${borderColor[item1.typeName]}`"></div>{{item1.typeName}}<span class="num">{{item1.woCount}}</span>
+            <span class="rat">{{item1.rate}}</span>
+          </div>
+        </div>
+      </div>
+     </div>
+    <span class="remark-icon"></span><p class="my-title">个人详情</p>
+    <table>
+      <tr>
+        <td>姓名:</td>
+        <td>{{everyPeople.name}}</td>
+      </tr>
+      <tr>
+        <td>职位:</td>
+        <td>{{everyPeople.positionName}}</td>
+      </tr>
+      <tr>
+        <td>专业:</td>
+        <td style="width:200px">{{everyPeople.specialtyName}}</td>
+      </tr>
+      <tr>
+        <td>手机号:</td>
+        <td>{{everyPeople.phoneNum}}</td>
+      </tr>
+      <tr>
+        <td>是否在岗:</td>
+        <td>{{everyPeople.onWorkingStatus}}</td>
+      </tr>
+      <tr> 
+        <td>工作评分:</td>
+        <td><Rate allowHalf disabled v-model="everyPeople.averageWorkScore"/></td>
+      </tr>
+      <tr>
+        <td>工单总数:</td>
+        <td>{{orderStat.currentMonthWoConut}}</td>
+      </tr>
+    </table>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+export default {
+  components: {},
+
+  mixins: [],
+  props: {
+    maskWidth: {
+      type: String,
+      default: "0px"
+    },
+    everyPeople: {},
+    orderStat:{},
+  },
+
+  data() {
+    return {
+      borderColor: {
+        "维保类工单": "#73DA99",
+        "巡检类工单": "#4A9EF9",
+        "运行类工单": "#6ECAF7",
+        "维修类工单": "#FABA53"
+      }
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.draw();
+  },
+
+  methods: {
+    getData() {},
+    close() {
+      this.$emit("closePeopleDetail");
+    },
+    draw() {
+      let myChart = echarts.init(document.getElementById("detail_bill"));
+      var scaleData = this.orderStat.woTypeCountInfo || [];
+      var placeHolderStyle = {
+        normal: {
+          label: {
+            show: false
+          },
+          labelLine: {
+            show: false
+          },
+          color: "rgba(0, 0, 0, 0)",
+          borderColor: "rgba(0, 0, 0, 0)",
+          borderWidth: 0
+        }
+      };
+      var data = [];
+
+      var color = ["#73DA99", "#FABA53", "#4A9EF9", "#6ECAF7", "#F3E171"];
+      for (var i = 0; i < scaleData.length; i++) {
+        data.push(
+          {
+            value: scaleData[i].woConut,
+            name: scaleData[i].typeName,
+            itemStyle: {
+              normal: {
+                borderWidth: 15,
+                borderColor: color[i]
+              }
+            }
+          },
+          {
+            value: 5,
+            name: "",
+            itemStyle: placeHolderStyle
+          }
+        );
+      }
+
+      var seriesObj = [
+        {
+          name: "",
+          type: "pie",
+          clockWise: false,
+          radius: [50, 50],
+          hoverAnimation: false,
+          itemStyle: {
+            normal: {
+              label: {
+                show: true,
+                position: "center",
+                color: "#3B4254",
+                fontSize: 0,
+                formatter: function(params) {
+                  return "\n\n\n\n\n\n\n";
+                }
+              }
+            }
+          },
+          data: data
+        }
+      ];
+
+      myChart.setOption({
+        tooltip: {
+          show: false
+        },
+        legend: {
+          show: false
+        },
+        toolbox: {
+          show: true
+        },
+        series: seriesObj
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.online-people-detail {
+  position: fixed;
+  z-index: 999;
+  top: 50px;
+  right: 0;
+  bottom: 0;
+  background: #ffffff;
+  box-shadow: 0 7px 15px 0 rgba(0, 0, 0, 0.2);
+  transition: all 0.5s ease-in-out;
+  overflow-y: scroll;
+  .particular {
+    height: 56px;
+    line-height: 56px;
+    border-bottom: 1px solid #dbdde4;
+    //font-size: 16px;
+    color: #3b4254;
+    padding-left: 24px;
+      .close-btn {
+      float: right;
+      padding: 10px;
+      padding: 18px;
+      cursor: pointer;
+    }
+  }
+  .my-title {
+    height: 40px;
+    font-size:18px;
+    font-weight: bold;
+    display: inline-block;
+  }
+  .remark-icon {
+    padding: 7px 3px;
+    display: inline-block;
+    background: #4a9ef9;
+    margin: 20px 10px 0 20px;
+  }
+  .base-info {
+    margin-left: 30px;
+    box-shadow: 0 1px 5px 0 rgba(59, 66, 84, 0.12);
+    border-radius: 2px;
+    width: 140px;
+    height: 198px;
+    > img {
+      width: 100%;
+      height: 140px;
+    }
+    .each-grade {
+      text-align: center;
+      .people-grade {
+       // font-size: 14px;
+        color: #3b4254;
+      }
+      .grade {
+        font-size: 20px;
+        color: #57a4f7;
+      }
+    }
+  }
+  .repair-order {
+    margin: 30px 0 30px 30px;
+    .completion {
+      margin-left: 40px;
+    }
+  }
+  .tt-echarts {
+    display: flex;
+    #detail_bill {
+      margin-top: 30px;
+    }
+    .tt-legend {
+      margin-top: 61px;
+      > div {
+        height: 30px;
+      }
+      .square {
+        width: 8px;
+        height: 8px;
+        background: #fff;
+        //border: 1px solid #4a9ef9;
+        border-radius: 50px;
+        display: inline-block;
+        margin-right: 10px;
+      }
+      .num {
+        margin-left: 15px;
+      }
+      .rat {
+        margin-left: 15px;
+      }
+    }
+  }
+  table {
+    td {
+      width: 98px;
+      height: 30px;
+    }
+    td:first-child {
+      text-align: right;
+    }
+    td:nth-child(2) {
+      text-align: left;
+      padding-left: 20px;
+    }
+  }
+}
+</style>
+<style lang="less">
+.online-people-detail {
+  .ivu-rate-star {
+    margin-right: -1px;
+  }
+}
+</style>

+ 332 - 0
src/modules/facilitiesAndStaff/pages/peopleView/Worker.vue

@@ -0,0 +1,332 @@
+<template>
+  <div class="worker">
+      <div class="wk-info" v-for="(item,index) in statffInfo" :key="index">
+          <div v-for="(item1,index1) in item" :key="index1">
+            <div class="wk-w">
+                <img class="wk-img" :src="item1.headPortrait?`http://58.251.20.250:7777/image-service/common/file_get?systemId=dataPlatform&key=${item1.headPortrait}`:`/static/img/noimg.png`" alt="人员图片">
+                <div class="wk-state">{{item1.personStatus}}</div>
+            </div>
+            <p>姓名:{{item1.name}}</p>
+            <p>电话:{{item1.phoneNum}}</p>
+            <p class="wk-break">专业:{{item1.specialtyName}}</p>
+          </div>
+      </div>
+      <div class="wk-charts">
+           <span class="remark-icon"></span><p class="my-title">近48小时出勤情况</p>
+           <div id="mySimple" style="width:100%;height:338px"></div>
+           <p class="legend"><img src="/static/img/icon1.jpg" alt=""><span>计划出勤人数</span> <img src="/static/img/icon2.jpg" alt=""><span>实际出勤人数</span> </p>
+      </div>
+  </div>
+</template>
+
+<script>
+var echarts = require("echarts");
+import axios from "axios";
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      statffInfo: []
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getPersonInfo();
+    this.drawSimple();
+  },
+
+  methods: {
+    drawSimple() {
+      let mySimpleChart = echarts.init(document.getElementById("mySimple"));
+      var hours = new Date().getHours();
+      var data = [
+        3,
+        4,
+        3,
+        3,
+        5,
+        4,
+        4,
+        3,
+        4,
+        3,
+        3,
+        5,
+        4,
+        4,
+        3,
+        3,
+        4,
+        3,
+        3,
+        5,
+        4,
+        4,
+        3,
+        4,
+        3,
+        3,
+        5,
+        4,
+        4,
+        3,
+        5,
+        3,
+        4,
+        5,
+        4,
+        5,
+        3,
+        4,
+        5,
+        3,
+        4,
+        5,
+        4,
+        5,
+        4,
+        5,
+        3,
+        4
+      ];
+      var data1 = [
+        5,
+        5,
+        5,
+        5,
+        5,
+        5,
+        4,
+        3,
+        4,
+        5,
+        5,
+        5,
+        4,
+        4,
+        5,
+        5,
+        4,
+        5,
+        5,
+        5,
+        4,
+        4,
+        5,
+        4,
+        5,
+        5,
+        5,
+        4,
+        4,
+        5,
+        5,
+        5,
+        4,
+        5,
+        4,
+        5,
+        5,
+        4,
+        5,
+        5,
+        4,
+        5,
+        5,
+        5,
+        5,
+        5,
+        5,
+        5
+      ];
+      var realData = data.splice(data.length - (24 - hours) + 1);
+      mySimpleChart.setOption({
+        xAxis: {
+          type: "category",
+          axisLabel: {
+            interval: 2
+          },
+          boundaryGap: false,
+          data: [
+            "昨天00:00",
+            "01:00",
+            "02:00",
+            "03:00",
+            "04:00",
+            "05:00",
+            "06:00",
+            "07:00",
+            "08:00",
+            "09:00",
+            "10:00",
+            "11:00",
+            "12:00",
+            "13:00",
+            "14:00",
+            "15:00",
+            "16:00",
+            "17:00",
+            "18:00",
+            "19:00",
+            "20:00",
+            "21:00",
+            "22:00",
+            "23:00",
+            "今天00:00",
+            "01:00",
+            "02:00",
+            "03:00",
+            "04:00",
+            "05:00",
+            "06:00",
+            "07:00",
+            "08:00",
+            "09:00",
+            "10:00",
+            "11:00",
+            "12:00",
+            "13:00",
+            "14:00",
+            "15:00",
+            "16:00",
+            "17:00",
+            "18:00",
+            "19:00",
+            "20:00",
+            "21:00",
+            "22:00",
+            "23:00"
+          ]
+        },
+        yAxis: {
+          type: "value"
+        },
+        tooltip: {
+          trigger: "axis",
+          backgroundColor: "#fff",
+          borderColor: "#D9D9D9",
+          textStyle: {
+            color: " #8C94A9",
+            fontSize: "12px",
+            fontWeight: "bold"
+          },
+          formatter: function(params, ticket, callback) {
+            var res = params[0].name;
+            var name = "";
+            for (var i = 0, l = params.length; i < l; i++) {
+              if (i == 0) {
+                name = "计划出勤人数";
+              } else {
+                name = "实际出勤人数";
+              }
+              res += "<br>" + name + " : " + params[i].value + "人";
+            }
+           
+            return res;
+          }
+        },
+        series: [
+          {
+            data: data1,
+            type: "line",
+            lineStyle: {
+              color: "#4A9EF9"
+            },
+            showSymbol: false
+          },
+          {
+            data: data,
+            type: "line",
+            lineStyle: {
+              color: "#FABA53"
+            },
+            showSymbol: false
+          }
+        ]
+      });
+    },
+    getPersonInfo() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/saas/queryPlanAttendancePersonList?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          if (res.data.result == "success") {
+            this.statffInfo.push(res.data.data);
+          }
+        });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.worker {
+  .ev-png {
+    width: 100%;
+  }
+  .wk-info {
+    display: inline-block;
+    padding: 20px;
+    > div {
+      display: inline-block;
+      padding: 20px;
+      .wk-w {
+        box-shadow: 0 1px 5px 0 rgba(59, 66, 84, 0.12);
+        border-radius: 2px;
+        width: 140px;
+        margin-bottom: 10px;
+        .wk-img {
+          width: 140px;
+          height: 140px;
+        }
+        .wk-state {
+          height: 20px;
+          text-align: center;
+        }
+      }
+      > p {
+        line-height: 22px;
+      }
+      .wk-break{
+        width: 155px;
+        word-wrap: break-word; 
+        word-break: normal;
+      }
+    }
+  }
+  .wk-charts {
+    margin-top: 40px;
+    border: 1px solid #dbdde4;
+    border-radius: 1px;
+    position: relative;
+    .ev-jpg {
+      width: 100%;
+    }
+    .my-title {
+      height: 40px;
+      font-size:18px;
+      font-weight: bold;
+      display: inline-block;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+    .legend {
+      position: absolute;
+      top: 48px;
+      right: 150px;
+    }
+  }
+}
+</style>

+ 156 - 0
src/modules/facilitiesAndStaff/pages/peopleView/index.vue

@@ -0,0 +1,156 @@
+<template>
+  <div class="people-view">
+      <Tabs value="name1">
+        <TabPane :label="`人员总数 ${personTotal}人`"  name="name1">
+          <online-people @showPeopleDetail="showPeopleDetail" @showData="showData"></online-people>
+        </TabPane>
+        <TabPane :label="`今日计划出勤人数:${planAttendance}人  今日实际出勤人数:${actualAttendance}人`" name="name2">
+           <worker></worker>
+        </TabPane>    
+    </Tabs>
+    <div class="mask-layer" @click="closeAnimation"></div>
+    <div v-if="Object.keys(orderStat).length>0">
+      <online-people-detail :maskWidth="maskWidth" @closePeopleDetail="closePeopleDetail" :everyPeople="everyPeople" :orderStat="orderStat"></online-people-detail>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import onlinePeople from "./OnlinePeople.vue";
+import worker from "./Worker.vue";
+import OnlinePeopleDetail from "./OnlinePeopleDetail.vue";
+export default {
+  components: {
+    onlinePeople,
+    worker,
+    OnlinePeopleDetail
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      maskWidth: "0px",
+      everyPeople: {},
+      personTotal: 20,
+      planAttendance: 20,
+      actualAttendance: 20,
+      detailData: [],
+      orderStat:{},
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getNumber();
+  },
+
+  methods: {
+    //关闭弹窗
+    closeAnimation(){
+       document.querySelector("body").style.overflow = "auto";
+      document.querySelector('.mask-layer').style.display = 'none';
+      this.maskWidth = "0px";
+    },
+    //计划人数
+    getNumber() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/saas/queryPersonStatisticCount?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.personTotal = data.personTotal;
+            this.planAttendance = data.planAttendance;
+            this.actualAttendance = data.actualAttendance;
+          }
+        });
+    },
+    //弹窗数据
+    showPeopleDetail(index) {
+      document.querySelector("body").style.overflow = "hidden";
+      document.querySelector('.mask-layer').style.display = 'block'
+      this.maskWidth = "458px";
+      axios
+        .get(
+          `/equipment/info-mng-backend/saas/queryPersonDetailById?projectId=${this.$store.state.projectId}&personId=${index}`
+        )
+        .then(res => {
+          if (res.data.result == "success") {
+            this.everyPeople = res.data.data;
+          }
+        });
+        this.queryPersonDetailWoStatById(index);
+    },
+    queryPersonDetailWoStatById(personId){
+      axios
+        .get(
+          `/equipment/info-mng-backend/saas/queryPersonDetailWoStatById?projectId=${this.$store.state.projectId}&personId=${personId}`
+        )
+        .then(res => {
+          if (res.data.result == "success") {
+            this.orderStat = res.data.data
+          }
+        });
+    },
+    //关闭弹窗
+    closePeopleDetail() {
+      document.querySelector("body").style.overflow = "auto";
+      document.querySelector('.mask-layer').style.display = 'none'
+      this.maskWidth = "0px";
+    },
+    //查看详情事件
+    showData(msg) {
+      this.detailData = msg;
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.people-view {
+  padding: 20px;
+  background: #fff;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  .mask-layer{
+    position: fixed;
+    top: 0;
+		left: 0;
+		right: 0;
+    bottom: 0;
+    z-index: 999;
+    display: none;
+  }
+}
+</style>
+<style lang="less">
+.people-view {
+  .ivu-tabs .nav-text {
+    padding-bottom: 20px;
+    color: #3b4254;
+  }
+  .ivu-table-wrapper {
+    border: none;
+  }
+  .ivu-tabs-nav-container{
+    font-size:16px;
+  }
+  .ivu-table:before {
+    height: 0;
+  }
+  .ivu-table:after {
+    width: 0;
+  }
+  .ivu-table td,
+  .ivu-table th {
+    border: none;
+  }
+}
+</style>
+

+ 34 - 0
src/modules/facilitiesAndStaff/router/index.js

@@ -0,0 +1,34 @@
+
+// 此方式会将所以文件打包到一个js里,没能实现按需加载
+// import Home from '../pages/home'
+// import EquipmentView from '../pages/equipmentView'
+// import PeopleView from '../pages/peopleView'
+// import OrderInfoView from '../pages/orderInfoView'
+
+// 按需加载,这里将facilitiesAndStaff目录里的内容打包到一个js里。
+const Home = () => import(/* webpackChunkName: 'facilitiesAndStaff' */ '../pages/home')
+const EquipmentView = () => import(/* webpackChunkName: 'facilitiesAndStaff' */ '../pages/equipmentView')
+const PeopleView = () => import(/* webpackChunkName: 'facilitiesAndStaff' */ '../pages/peopleView')
+const OrderInfoView = () => import(/* webpackChunkName: 'facilitiesAndStaff' */ '../pages/orderInfoView')
+
+export default [
+    {
+        path: 'staff',
+        component: Home,
+        redirect: 'staff/equipmentView',
+        children: [
+            {
+                path: 'equipmentView',
+                component: EquipmentView
+            },
+            {
+                path: 'peopleView',
+                component: PeopleView
+            },
+            {
+                path: 'orderInfoView',
+                component: OrderInfoView
+            }
+        ]
+    }
+]

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

@@ -0,0 +1,14 @@
+/**
+ * 设施与人员
+ */
+export default {
+  namespaced: true,
+  state: {
+    name: '设施与人员'
+  },
+  mutations: {
+
+  },
+  actions: {
+  }
+}

+ 7 - 0
src/modules/firstPage/index.js

@@ -0,0 +1,7 @@
+import store from './store'
+import router from './router'
+
+export {
+    store,
+    router
+}

+ 124 - 0
src/modules/firstPage/pages/asideMenu/index.vue

@@ -0,0 +1,124 @@
+<template>
+    <div class="aside-menu">
+      <ul>
+         <li>
+          <router-link to="">
+           <i class="iconfont icon-home" style="padding-left:7px;"></i>
+            <span style="padding-left:72px;">首页</span>
+          </router-link>
+        </li>
+        <li v-if="meos">
+          <a  :href="`${meos}`" target="_blank">
+            <i class="iconfont icon-meos"></i>
+            <span style="padding-left:50px;">设备设施管理系统</span>
+          </a>
+        </li>
+        <li v-if="Wellzoom">
+           <a  :href="`${Wellzoom}`" target="_blank">
+            <i class="iconfont icon-huanjinglogo" style="font-size:14px;"></i>
+            <span>环境管理系统</span>
+          </a>
+        </li>
+        <li v-if="Cpeco">
+           <a  :href="`${Cpeco}`" target="_blank">
+           <i class="iconfont icon-lengzhanlogo"></i>
+            <span style="padding-left:44px;">冷站群控系统</span>
+          </a>
+        </li>
+        <li v-if="Mindin">
+          <a  :href="`${Mindin}`" target="_blank">
+            <i class="iconfont icon-peidianlogo"></i>
+            <span style="padding-left:41px;">配电监控系统</span>
+          </a>
+        </li>
+        <li v-if="iSagy">
+          <a :href="`${iSagy}`" target="_blank">
+           <i class="iconfont icon-nengyuanlogo"></i>
+            <span>能源管理系统</span>
+          </a>
+        </li>
+        <li v-if="Finein">
+          <a :href="`${Finein}`">
+            <i class="iconfont icon-zulinlogo"></i>
+            <span style="padding-left:40px;">租户能源管理</span>
+          </a>
+        </li>
+        <li>
+          <a >
+            <i class="iconfont icon-add" style="padding-left:7px;"></i>
+            <span></span>
+          </a>
+        </li>
+      </ul>
+    </div>
+</template>
+
+<script>
+import axios from 'axios'
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      Cpeco:"",
+      Finein:"",
+      Mindin:"",
+      Wellzoom:"",
+      iSagy:"",
+      meos:""
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getSubSystemUrl();
+  },
+
+  methods: {
+   getSubSystemUrl(){
+     axios.get(`/equipment/info-mng-backend/config/getSubSystemUrl?projectId=${this.$store.state.projectId}`).then(res=>{
+       console.log(res)
+       let data = res.data.data;
+       this.Cpeco = data.Cpeco;
+       this.Finein = data.Finein;
+       this.Mindin = data.Mindin;
+       this.Wellzoom = data.Wellzoom;
+       this.iSagy = data.iSagy;
+       this.meos = data.meos;
+     })
+   }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.aside-menu {
+  ul {
+    > li {
+      &:hover {
+        background: #eee;
+      }
+      a {
+        display: inline-block;
+        white-space: nowrap;
+        padding: 10px 23px;
+        width: 100%;
+        color: #b6bccb;
+        span {
+          padding-left: 37px;
+          color: black;
+          font-size: 15px;
+        }
+      }
+      .router-link-active {
+        color: #57a4f7;
+      }
+    }
+  }
+}
+</style>

+ 62 - 0
src/modules/firstPage/pages/home/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="fp-home">
+    <aside-mune class="aside"></aside-mune>
+    <div class="fp-content">
+      <router-view>
+      </router-view>
+    </div>
+  </div>
+</template>
+
+<script>
+import AsideMune from "../asideMenu";
+
+export default {
+  components: {
+    AsideMune
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.fp-home {
+  .aside {
+    position: fixed;
+    z-index: 999;
+    top: 50px;
+    left: 0;
+    bottom: 0;
+    width: 80px;
+    border-right: 1px solid #ebecf0;
+    transition: all 0.3s ease-in-out;
+    overflow-x: hidden;
+    overflow-y: auto;
+    background: #fff;
+  }
+  .fp-content {
+    margin-left: 80px;
+    padding: 70px 20px 20px 20px;
+    transition: all 0.3s ease-in-out;
+    background: #eeeff3;
+    min-width: 1344px;
+  }
+  & > .aside:hover {
+    width: 300px;
+    background: #fff;
+  }
+}
+</style>

+ 174 - 0
src/modules/firstPage/pages/main/Energy.vue

@@ -0,0 +1,174 @@
+<template>
+  <div class="energy">
+      <p class="info">能耗</p>
+      <p class="my-time">截至{{month}}月{{day}}日</p>
+      <div id="pie">
+        <!-- cx,cy: 圆心点坐标(viewBox 440,则对应220)
+          r: 园半径
+          stroke-width:线条宽度
+          stroke: 颜色
+          stroke-dasharray:length1 length2  length1最长设圆周长,length2尽可能大
+            transform-origin: 改变原点位置
+            stroke-linecap="round": 圆边
+        -->
+        <svg width="270" height="270" viewBox="0 0 440 440">
+          <circle cx="220" cy="220" r="170" stroke-width="15" stroke-linecap="round" stroke="#FBF3E7" fill="none" stroke-dasharray="900 1069" style="
+            transform: rotate(118deg);
+            transform-origin: 220px 220px;
+        "></circle>
+            
+            <circle cx="220" cy="220" r="170" stroke-width="15" stroke-linecap="round" stroke="#FABA53" fill="none" :stroke-dasharray="`${(consume.actualConsumption / 100) * 900} 1069`" style="
+            transform: rotate(118deg);
+            transform-origin: 220px 220px;
+        "></circle>
+            
+          <circle cx="220" cy="220" r="120" stroke-width="15" stroke-linecap="round" stroke="#eaf0f8" fill="none" stroke-dasharray="630 1069" style="
+            transform: rotate(118deg);
+            transform-origin: 220px 220px;
+        "></circle>
+            
+            <circle cx="220" cy="220" r="120" stroke-width="15" stroke-linecap="round" stroke="#4A9EF9" fill="none" :stroke-dasharray="`${(consume.planConsumption / 100) * 630} 1069`" style="
+            transform: rotate(118deg);
+            transform-origin: 220px 220px;
+        "></circle>
+            
+        </svg>
+        <div class="budget">
+          <p style="color: #62697B;">年度能耗预算</p>
+          <p style="font-family: ArialMT;font-size: 32px;color: #3B4254;">{{consume.annualBudget}}</p>
+          <p style="color: #62697B;">万kWh</p>
+        </div>
+      </div>
+      <table class="description">
+        <tr class="normal">
+          <td><i></i></td>
+          <td>计划消耗</td>
+          <td>{{consume.planConsumption}}%</td>
+        </tr>
+        <tr class="repair">
+          <td><i></i></td>
+          <td>实际已消耗</td>
+          <td>{{consume.actualConsumption}}%</td>
+        </tr>
+      </table>
+  </div>
+</template>
+
+<script>
+var echarts = require("echarts");
+import axios from "axios";
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      consume: {
+        annualBudget:0,
+        planConsumption:0,
+        actualConsumption:0,
+      },
+      month: new Date().getMonth() + 1,
+      day: new Date().getDate()
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.initData();
+  },
+
+  methods: {
+    //能耗
+    initData() {
+      axios.get(`/api/info-mng-backend/isagy/index?projectId=${this.$store.state.projectId}`).then(({ data }) => {
+        if (data.result === "success") {
+          //  this.planConsumption = (data.data.planConsumption / data.data.annualBudget) * 630
+          this.consume = data.data;
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.energy {
+  border: 1px solid #ebecf0;
+  background: #fff;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 3px;
+  //font-size: 14px;
+  .info {
+    height: 50px;
+    line-height: 50px;
+    background: #f7f9fc;
+    border: 0 solid #f2f2f2;
+    padding-left: 20px;
+    font-weight: bolder;
+    font-size: 18px;
+    color: #3b4254;
+    border-top-left-radius: 3px;
+    border-top-right-radius: 3px;
+  }
+  .my-time {
+    padding-left: 20px;
+    padding-top: 20px;
+  }
+  #pie {
+    margin: 0 auto;
+    margin: 50px 0 36px 0;
+    text-align: center;
+    position: relative;
+    .my-circle {
+      padding-top: 116px;
+      width: 192px;
+      height: 230px;
+    }
+  }
+  .budget {
+    position: absolute;
+    top: 100px;
+    left: 38%;
+  }
+  table.description {
+    margin: 0 auto;
+    margin-bottom: 30px;
+    tr {
+      td {
+        padding: 10px 15px;
+        i {
+          display: inline-block;
+          width: 16px;
+          height: 10px;
+          border: 2px solid#4A9EF9;
+          background: #4a9ef9;
+          border-radius: 6px;
+        }
+      }
+      &:nth-child(2) {
+        td {
+          i {
+            border-color: #faba53;
+            background: #faba53;
+          }
+        }
+      }
+    }
+  }
+}
+</style>
+<style lang="less">
+.enery {
+  #main {
+    .ivu-chart-circle {
+      padding-left: 88px;
+    }
+  }
+}
+</style>
+

+ 171 - 0
src/modules/firstPage/pages/main/SgyHealthyIndex.vue

@@ -0,0 +1,171 @@
+<template>
+ <div class="enviroment">
+    <p class="heal-index"> 平均健康指数</p>
+    <div class="healthy-index">
+
+      <span>差</span>&nbsp;&nbsp;
+
+      <div class="healthy-mark">
+
+        <p class="inner" :style="`border-top-color: ${pointer(innerValue).color};transform: translateX(${pointer(innerValue).left})`">
+          <span>室内</span>
+        </p>
+
+        <div class="mid">
+          <ul class="rectangle">
+            <li v-for="item in colors" :key="item" :style="`width:${width};background-color:${item}`"></li>
+          </ul>
+        </div>
+
+        <p class="outer" :style="`border-bottom-color: ${pointer(outerValue).color};transform: translateX(${pointer(outerValue).left})`">
+          <span>室外</span>
+        </p>
+
+      </div>
+
+      &nbsp;<span>优</span>
+    </div>
+    <p class="outside-tp tips">室内环境</p>
+    <div class="outside-tp temp"><span>温度:{{innerTdb==null?"--":innerTdb.toFixed(1)}} ℃</span><span >CO2:{{innerC==null?"--":innerC.toFixed(1)}} ppm</span></div>
+    <div class="outside-tp"><span>湿度:{{innerRH==null?"--":innerRH.toFixed(1)}} %</span>
+    <!-- <span>PM2.5:{{innerPM==null?"--":innerPM.toFixed(1)}}</span> -->
+    </div>
+ </div>
+</template>
+
+<script>
+var axios = require("axios");
+export default {
+  components: {},
+  mixins: [],
+  props: {
+    width: {
+      type: String,
+      default: "66px"
+    },
+    colors: {
+      type: Array,
+      default: function() {
+        return ["#f58788", "#f8b95c", "#77d99b", "#4fa0f6"];
+      }
+    },
+    innerValue: {
+      type: Number,
+      default: 40
+    },
+    outerValue: {
+      type: Number,
+      default: 50
+    },
+    innerC: 0,
+    innerRH: 0,
+    innerPM: 0,
+    innerTdb: 0
+  },
+  data() {
+    return {
+      minValue: 0,
+      maxValue: 100
+    };
+  },
+  computed: {},
+  mounted() {
+    //this.getData();
+  },
+  methods: {
+    // 返回指针对象
+    pointer(currentValue) {
+      const itemWidth = parseFloat(this.width);
+      // 占比
+      const per =
+        (currentValue - this.minValue) / (this.maxValue - this.minValue);
+      const left = per * (itemWidth * this.colors.length);
+      const index = this.index(currentValue);
+      return {
+        left: `${left + 4 * index}px`,
+        color: this.colors[index]
+      };
+    },
+    // 位于第几个
+    index(currentValue) {
+      let index =
+        Math.ceil((parseFloat(currentValue) / 100) * this.colors.length) - 1;
+      index = index === -1 ? 0 : index;
+      return index;
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.heal-index {
+  padding-left: 20px;
+  padding-top: 20px;
+  color: #3b4254;
+}
+.outside-tp {
+  margin-left: 20px;
+  color: #62697b;
+  span:first-child {
+    display: inline-block;
+    width: 137px;
+  }
+}
+.outside-tp.tips {
+  color: #3b4254;
+}
+.outside-tp.temp {
+  padding: 16px 0 10px 0;
+}
+.healthy-index {
+  display: flex;
+  align-items: center;
+  padding: 43px 0;
+  padding-left: 30px;
+  .healthy-mark {
+    position: relative;
+    .inner,
+    .outer {
+      position: absolute;
+      left: -3px; // 抵消三角形的边距
+      top: -8px;
+      width: 0;
+      height: 0;
+      border-left: 3px solid transparent;
+      border-right: 3px solid transparent;
+      border-top: 5px solid; /*--三角形的高--*/
+      span {
+        position: absolute;
+        display: inline-block;
+        top: -24px;
+        left: -12px;
+        width: 100px;
+      }
+    }
+    .outer {
+      top: 15px;
+      border-top: none;
+      border-bottom: 5px solid; /*--三角形的高--*/
+      span {
+        position: absolute;
+        display: inline-block;
+        top: 6px;
+        left: -12px;
+        width: 100px;
+      }
+    }
+    .mid {
+      ul.rectangle {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        li {
+          margin-right: 4px;
+          height: 12px;
+          border-radius: 1px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 119 - 0
src/modules/firstPage/pages/main/SgyMarquee.vue

@@ -0,0 +1,119 @@
+<template>
+  <div class="sgy-marquee" :data-guid="guid">
+    <p :class="myClass">
+      <template v-if="$slots.default">
+        <i class="real-text"><slot></slot></i>
+        <i class="text" :style="`visibility: ${isMarquee ? 'visible': 'hidden'}`">
+          {{this.$slots.default[0].text}}
+        </i>
+      </template>
+      <template v-else>
+        <i v-html="html"></i><i class="text" :style="`visibility: ${isMarquee ? 'visible': 'hidden'}`" v-html="html"></i>
+      </template>
+    </p>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {
+    hover: {
+      type: Boolean,
+      default: false
+    },
+    html: {
+      type: String,
+      default: ""
+    }
+  },
+  data() {
+    return {
+      guid: "",
+      textWidth: 0,
+      offsetWidth: 0
+    };
+  },
+  computed: {
+    isMarquee() {
+      return (this.textWidth - 80) / 2 > this.offsetWidth;
+    },
+    myClass() {
+      if (this.isMarquee) {
+        return this.hover ? "is-hover-marquee" : "is-marquee";
+      } else {
+        return "";
+      }
+    }
+  },
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      // 避免样式覆盖
+      this.guid = this.guidFunc();
+      this.$nextTick(() => {
+        this.textWidth = document.querySelector(
+          `.sgy-marquee[data-guid="${this.guid}"]`
+        ).scrollWidth;
+        this.offsetWidth = document.querySelector(
+          `.sgy-marquee[data-guid="${this.guid}"]`
+        ).offsetWidth;
+        this.initCss();
+      });
+    },
+    guidFunc() {
+      return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(
+        c
+      ) {
+        let r = (Math.random() * 16) | 0;
+        let v = c === "x" ? r : (r & 0x3) | 0x8;
+        return v.toString(16);
+      });
+    },
+    initCss() {
+      let cssText = `
+      .sgy-marquee[data-guid="${this.guid}"] .is-marquee {
+        animation: marquee-${this.guid} ${this.textWidth /
+        200}s linear infinite;
+      }
+      .sgy-marquee[data-guid="${this.guid}"] .is-hover-marquee:hover {
+        animation: marquee-${this.guid} ${this.textWidth /
+        200}s linear infinite;
+      }
+      @keyframes marquee-${this.guid} {
+        0% {
+          transform: translateX(0px);
+        }
+        100% {
+          transform: translateX(${-this.textWidth / 2 - 40}px);
+        }
+      }`;
+      this.insertCss(cssText);
+    },
+    insertCss(cssText) {
+      let head = document.head || document.getElementsByTagName("head")[0];
+      let style = document.createElement("style");
+      style.type = "text/css";
+      let textNode = document.createTextNode(cssText);
+      style.appendChild(textNode);
+      head.appendChild(style);
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" style="text/less">
+.sgy-marquee {
+  width: 100%;
+  overflow: hidden;
+  white-space: nowrap;
+  > p {
+    display: inline-block;
+  }
+  .text {
+    padding-left: 80px;
+  }
+}
+</style>

+ 181 - 0
src/modules/firstPage/pages/main/Staff.vue

@@ -0,0 +1,181 @@
+<template>
+  <div class="staff">
+      <div class="bell">
+          <p class="info">工单数量</p>
+          <div class="clearify">
+            <i-Col span="12" style="text-align:center">
+              <img class="mg-png" src="/static/img/popularity.png">
+              <p class="num">{{cumulativeCount}}</p>
+              <p class="total">月累计工单数量(个)</p>
+            </i-Col>
+            <i-Col span="12" style="text-align:center">
+              <img class="mg-png" src="/static/img/bill.png">
+              <p class="num">{{completionCount}}</p>
+              <p class="total">月完成工单数量(个)</p>
+            </i-Col>
+          </div>
+      </div>
+
+      <div class="popularity">
+          <p class="info">人员</p>
+          <div class="clearify">
+            <i-Col span="12" style="text-align:center">
+              <img class="mg-png" src="/static/img/plan-user.png">
+              <p class="num">{{planOnJobCount}}</p>
+              <p class="total">计划在线人数(人)</p>
+            </i-Col>
+            <i-Col span="12" style="text-align:center">
+              <img class="mg-png" src="/static/img/real-user.png">
+              <p class="num">{{actualOnJobCount}}</p>
+              <p class="total">实际在线人数(人)</p>
+            </i-Col>
+          </div>
+      </div>
+  </div>
+</template>
+
+<script>
+var echarts = require("echarts");
+import axios from "axios";
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      planOnJobCount: "",
+      actualOnJobCount: "",
+      completionCount: "1083",
+      cumulativeCount: "1200"
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getCount();
+    this.getData();
+  },
+
+  methods: {
+    //人员
+    getCount() {
+      axios
+        .get(`/enviroment/info-mng-backend/saas/getOnJobCount?projectId=${this.$store.state.projectId}`)
+        .then(res => {
+          let data = res.data.data;
+          this.planOnJobCount = data.planOnJobCount;
+          this.actualOnJobCount = data.actualOnJobCount;
+        });
+    },
+    //工单数量
+    getData() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/saas/queryMonthlyWorkOrderStatistics?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.cumulativeCount = data.cumulativeCount;
+          this.completionCount = data.completionCount;
+        });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.staff {
+  //font-size: 14px;
+  display: flex;
+  flex-direction: column;
+  .bell {
+    border: 1px solid #ebecf0;
+    margin-bottom: 10px;
+    background: #fff;
+    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+   // height: 53.6%;
+    .info {
+      line-height: 50px;
+      background: #f7f9fc;
+      border: 0 solid #f2f2f2;
+      padding-left: 20px;
+      font-size: 18px;
+      font-weight: bolder;
+      color: #3b4254;
+      border-top-left-radius: 3px;
+      border-top-right-radius: 3px;
+    }
+    .mg-png {
+      padding-top: 81px;
+    }
+    .num {
+      font-family: 'ArialMT';
+      font-size: 32px;
+      color: #3b4254;
+      height: 48px;
+    }
+    .total {
+      color: #8c94a9;
+      padding-bottom:62px;
+    }
+  }
+  .popularity {
+    border: 1px solid #ebecf0;
+    background: #fff;
+    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+   // height: 44.5%;
+    display: flex;
+    flex-direction: column;
+    .info {
+      height: 50px;
+      line-height: 50px;
+      background: #f7f9fc;
+      border: 0 solid #f2f2f2;
+      padding-left: 20px;
+      font-weight: bolder;
+      font-size: 18px;
+      color: #3b4254;
+      border-top-left-radius: 3px;
+      border-top-right-radius: 3px;
+    }
+    .mg-png {
+      padding-top: 45px;
+    }
+    .num {
+      font-family: 'ArialMT';
+      font-size: 32px;
+      color: #3b4254;
+      height: 48px;
+    }
+    .total {
+      color: #8c94a9;
+      padding-bottom:62px;
+    }
+  }
+  .clearify::before {
+    content: "";
+    display: block;
+  }
+}
+</style>
+<style lang="less">
+.staff {
+  .bill {
+    .ivu-col {
+      display: inline-block;
+    }
+  }
+  .popularity {
+    .ivu-col {
+      height: 80%;
+    }
+  }
+}
+</style>
+

+ 84 - 0
src/modules/firstPage/pages/main/baseInfo.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="base-info">
+      <p class="info">基本信息</p>
+      <div class="abstract">
+          <img class="info-img" src="/static/img/INFO.jpg">
+          <p class="my-text">{{infoText}}</p>
+      </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+import { mapMutations } from 'vuex'
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      picture:"Pj1101010001_huiyunying.jpg",
+      infoText:"",
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.init();
+  },
+
+  methods: {
+    //全局projextId的映射
+    ...mapMutations(['getProId']),
+    //基本信息
+    init(){
+      axios.get(`/equipment/info-mng-backend/config/getPresentation?projectId=${this.$store.state.projectId}`).then(res=>{
+        let data = res.data.data;
+        this.infoText = data.presentation;
+        this.picture = data.picture;
+      })
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.base-info {
+  width: 32.8%;
+  border: 1px solid#ebecf0;
+  background: #fff;
+  margin-right: 10px;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 3px;
+  min-width: 528px;
+  .info {
+    line-height: 50px;
+    background: #f7f9fc;
+    border: 0 solid #f2f2f2;
+    padding-left: 20px;
+    font-weight: bolder;
+    font-size: 18px;
+    color: #3b4254;
+    border-top-left-radius: 3px;
+    border-top-right-radius: 3px;
+  }
+  .abstract {
+    padding: 20px;
+    display: flex;
+    .info-img {
+      min-width: 188px;
+      height: 232px;
+    }
+    .my-text {
+      display: inline-block;
+      padding-left: 20px;
+      //font-size: 14px;
+      text-indent: 2em;
+    }
+  }
+}
+</style>

+ 140 - 0
src/modules/firstPage/pages/main/enviromentAndSafe.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class="en-safe">
+      <div class="envir">
+          <p class="info">环境</p>
+           <div>
+             <sgy-healthy-index width="65px" :colors="colors" :innerValue="innerValue"
+              :outerValue="outerValue" :innerTdb="innerTdb" :innerRH="innerRH" :innerPM="innerPM" :innerC="innerC"></sgy-healthy-index>
+           </div>
+      </div>
+      <!-- 安全 -->
+      <div class="safe">
+          <p class="info">安全</p>
+          <p class="heal-index"> 安全综合评分</p>
+          <div class="my-echarts">
+              <i-Circle :percent="grade">
+                <span class="demo-Circle-inner" style="font-size:32px">{{grade}}<i style="font-size: 14px;color: #8C94A9;">分</i> </span>
+             </i-Circle>
+          </div>
+      </div>
+
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+import sgyHealthyIndex from "./SgyHealthyIndex.vue";
+export default {
+  components: {
+    sgyHealthyIndex
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      colors: ["#F78686", "#FABA53", "#73DA99", "#4A9EF9"],
+      innerTdb: 0,
+      innerRH: 0,
+      innerPM: 0,
+      innerC: 0,
+      innerValue:55,
+      outerValue:70,
+      grade:0,
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getData();
+    this.getSafetyMark();
+  },
+
+  methods: {
+    //环境
+    getData() {
+      axios
+        .get(`/enviroment/info-mng-backend/env/index?projectId=${this.$store.state.projectId}`)
+        .then(res => {
+          if (res.data.result == "success") {
+            let data =  res.data.data
+            this.innerTdb =data.indoorAverage.Tdb;
+            this.innerRH = data.indoorAverage.RH;
+            this.innerPM =data.indoorAverage.PM25;
+            this.innerC = data.indoorAverage.CO2;
+          }
+        });
+    },
+    //安全
+    getSafetyMark(){
+      axios.get(`/equipment/info-mng-backend/safe/index?projectId=${this.$store.state.projectId}`).then(res=>{
+        let data = res.data.data;
+        this.grade = data.grade;
+      })
+    },
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.en-safe {
+  min-width: 360px;
+  //font-size: 14px;
+  display: flex;
+  flex-direction: column;
+  .envir {
+    border: 1px solid #ebecf0;
+    margin-bottom: 10px;
+    background: #fff;
+    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    .info {
+      height: 50px;
+      line-height: 50px;
+      background: #f7f9fc;
+      border: 0 solid #f2f2f2;
+      padding-left: 20px;
+      font-weight: bolder;
+      font-size: 18px;
+      color: #3b4254;
+      border-top-left-radius: 3px;
+      border-top-right-radius: 3px;
+    }
+    > div {
+      margin-bottom: 20px;
+    }
+  }
+  .safe {
+    border: 1px solid #ebecf0;
+    background: #fff;
+    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    flex:1;
+    .info {
+      height: 50px;
+      line-height: 50px;
+      background: #f7f9fc;
+      border: 0 solid #f2f2f2;
+      padding-left: 20px;
+      font-weight: bolder;
+      font-size: 18px;
+      color: #3b4254;
+      border-top-left-radius: 3px;
+      border-top-right-radius: 3px;
+    }
+    .heal-index {
+      padding-left: 20px;
+      padding-top: 20px;
+      color: #3b4254;
+    }
+    .my-echarts {
+      text-align: center;
+      padding: 20px 0 20px 0;
+    }
+  }
+}
+</style>

+ 159 - 0
src/modules/firstPage/pages/main/equipment.vue

@@ -0,0 +1,159 @@
+<template>
+  <div class="equipment">
+      <p class="info">设备设施</p>
+      <div>
+        <p><span class="state">设备运行</span>
+        <span class="grade" v-if="namalRate>=99">优</span>
+        <span class="grade" v-if="99>namalRate>=98">良好</span>
+        <span class="grade" v-if="98>namalRate>=95">中</span>
+        <span class="grade" v-if="namalRate<95">差</span>
+        </p>
+      </div>
+      <p class="eq-total">设备总量  <span style="padding-left:10px"> {{eqCount}}</span></p>
+      <div class="normal-equipment">
+        <p>完好的设备 <span>{{normalCount}}({{namalRate}}%)</span></p>
+        <i-Progress :percent="namalRate" :stroke-width="14" />
+      </div>
+      <div class="normal-equipment">
+        <p>故障的设备 <span>{{repairCount}}({{repairRate}}%)</span></p>
+        <i-Progress :percent="repairRate" :stroke-width="14" />
+      </div>
+     
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      eqCount: "",
+      repairCount: "",
+      normalCount: "",
+      namalRate: 0,
+      repairRate: 0
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getEqCount();
+  },
+
+  methods: {
+    //设备设施
+    getEqCount() {
+      axios
+        .get(`/equipment/info-mng-backend/saas/getEqRepairCount?projectId=${this.$store.state.projectId}`)
+        .then(res => {
+          if (res.data.result == "success") {
+            let data = res.data.data;
+            this.eqCount = data.eqCount;
+            this.repairCount = data.repairCount;
+            this.normalCount = this.eqCount - this.repairCount;
+            this.namalRate = Math.floor(
+              this.normalCount == 0
+                ? 0
+                : (this.normalCount / this.eqCount) * 100
+            );
+            this.repairRate = Math.floor(
+              this.repairCount == 0
+                ? 0
+                : (this.repairCount / this.eqCount) * 100
+            );
+          }
+        });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.equipment {
+  border: 1px solid #ebecf0;
+  background: #fff;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 3px;
+  //font-size: 14px;
+  .info {
+    height: 50px;
+    line-height: 50px;
+    background: #f7f9fc;
+    border: 0 solid #f2f2f2;
+    padding-left: 20px;
+    font-weight: bolder;
+    font-size: 18px;
+    color: #3b4254;
+    border-top-left-radius: 3px;
+    border-top-right-radius: 3px;
+  }
+  .state {
+    display: inline-block;
+    color: #3b4254;
+    padding-top: 32px;
+    padding-left: 20px;
+    height: 18px;
+    line-height: 18px;
+  }
+  .grade {
+    font-size: 23px;
+    color: #73da99;
+    display: inline-block;
+    width: 60px;
+    height: 40px;
+    padding-left: 14px;
+    text-align: center;
+    line-height: 40px;
+    margin-bottom: 38px;
+  }
+  .eq-total {
+    width: 160px;
+    height: 42px;
+    line-height: 42px;
+    background: #f4f5f8;
+    border-radius: 1px;
+    color: #62697b;
+    margin-left: 20px;
+    padding-left: 20px;
+    > span {
+      font-family: 'ArialMT';
+      font-size: 20px;
+      color: #4a9ef9;
+    }
+  }
+  .normal-equipment {
+    margin: 31.2px 0;
+    padding-left: 20px;
+    p {
+      color: #62697b;
+      padding-bottom: 12px;
+      > span {
+        padding-left: 10%;
+      }
+    }
+  }
+}
+</style>
+<style lang="less">
+.equipment {
+  .ivu-progress-text {
+    display: none;
+  }
+  .ivu-progress-bg {
+    // border-radius:1px;
+  }
+  .ivu-progress-inner {
+    // border-radius:1px;
+    background: #eaf0f8;
+  }
+}
+</style>
+

+ 69 - 0
src/modules/firstPage/pages/main/index.vue

@@ -0,0 +1,69 @@
+<template>
+
+  <div class="first-page"> 
+
+    <div class="top">
+        <base-info></base-info>
+        <new-active></new-active>
+    </div>
+
+    <div class="bottom">
+      <equipment></equipment>
+      <energy></energy>
+      <enviromentAndSafe></enviromentAndSafe>
+      <staff></staff>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import baseInfo from "./BaseInfo.vue";
+import equipment from "./Equipment.vue";
+import enviromentAndSafe from "./EnviromentAndSafe.vue";
+import energy from "./Energy.vue";
+import staff from "./Staff.vue";
+import newActive from "./NewActive.vue";
+export default {
+  components: {
+    baseInfo,
+    equipment,
+    enviromentAndSafe,
+    energy,
+    staff,
+    newActive
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.first-page {
+  min-width: 1390px;
+  .top {
+    display: flex;
+    margin-bottom: 10px;
+  }
+  .bottom {
+    display: flex;
+    justify-content: space-between;
+
+    > * {
+      width: 24.5%;
+    }
+  }
+}
+</style>

+ 203 - 0
src/modules/firstPage/pages/main/newActive.vue

@@ -0,0 +1,203 @@
+<template>
+      <div class="new-active">
+        <p class="info">最新动态BBS</p>
+        <!-- <div v-if="list.length==0">
+           <Spin fix>
+                <Icon type="ios-loading" size=40 class="demo-spin-icon-load"></Icon>
+                <div>Loading</div>
+            </Spin>
+        </div> -->
+        <ul>
+          <li v-for="(item,index) in list" :key="index" :data-id="item.id" :class="{colorA:index==0&&item.flag==1}">
+            <div class="my-left">
+              <span class="my-index">{{index+1}}.</span>
+              <div class="my-marquee">
+                <sgy-marquee ref="sgy-marquee" :hover="true">({{item.type=='1'?"工单":"报警"}}){{item.info}}</sgy-marquee>
+              </div>
+            </div>
+            <div class="my-button">
+              <span>{{item.createTime}}</span>
+              <button class="cancel" v-if="item.flag==1" @click="cancelTop(item)">取消置顶</button>
+              <button v-else @click="doTop(item, index)">置顶</button>
+            </div>
+          </li>
+        </ul>   
+      </div>
+</template>
+
+<script>
+import axios from "axios";
+import sgyMarquee from './SgyMarquee.vue'
+export default {
+  components: {sgyMarquee},
+
+  mixins: [],
+
+  props: {
+    
+  },
+
+  data() {
+    return {
+      list: [
+       
+      ],
+     
+    };
+  },
+
+  computed: {
+   
+  },
+
+  mounted() {
+    this.init()
+  },
+
+  methods: {
+    init() {
+      //最新动态BBS
+      axios.get(`/equipment/info-mng-backend/bbs/getAll?projectId=${this.$store.state.projectId}`).then(res => {
+        this.list = res.data.data;
+      });
+    }, 
+    //置顶数据
+    doTop(item, index) {
+      let data = {
+        id: item.id,
+        type:item.type,
+        info:item.info,
+        createTime:item.createTime
+      }
+      axios.post(`/equipment/info-mng-backend/bbs/stick?projectId=${this.$store.state.projectId}`, data).then(res => {
+        if (res.data.result == 'success') {
+           this.moveToTop(item,index);
+        }
+      })
+    },
+    //取消置顶
+    cancelTop(item){
+      //调接口,告诉后台取消置顶
+      let data = {
+        id: item.id,
+        type:item.type,
+        info:item.info,
+        createTime:item.createTime
+      }
+      axios.post(`/equipment/info-mng-backend/bbs/unstick?projectId=${this.$store.state.projectId}`, data).then(res => {
+        if (res.data.result == 'success') {
+            this.list[0].flag=0;
+           
+        }
+      })
+
+    },
+    //置顶效果
+    moveToTop(item, index) {
+      let el = document.querySelector(`li[data-id="${item.id}"]`)
+      let arr = []
+      for(let i of this.list) {
+        if (i.id === item.id) {
+          i.flag = 1
+          arr.unshift(i)
+        } else {
+          i.flag = 0
+          arr.push(i)
+        }
+        this.list = arr
+        
+      }
+     this.$refs['sgy-marquee'].forEach(i => {
+       i.init()
+     })
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.new-active {
+  width:68%;
+  border: 1px solid #ebecf0;
+  background: #fff;
+  min-width: 700px;
+  box-shadow: 0 1px 4px 0 rgba(0,0,0,0.05);
+  border-radius: 3px;
+  .info {
+    line-height: 50px;
+    background: #f7f9fc;
+    border: 0 solid #f2f2f2;
+    padding-left: 20px;
+    font-weight: bolder;
+    font-size: 18px;
+    color: #3b4254;
+    border-top-left-radius: 3px;
+    border-top-right-radius: 3px;
+  }
+  ul {
+    height: 257px;
+    overflow: auto;
+    margin: 0 20px;
+    li {
+      padding: 10px;
+      border: 0 solid #f2f2f2;
+      color: #62697b;
+      display: flex;
+      justify-content: space-between;
+      .my-left{
+        display: flex;
+        .my-index {
+          display: inline-block;
+          padding-right: 5px;
+          width: 28px;
+      }
+      .my-marquee{
+        width:360px;
+        display:inline-block;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+      }
+      .my-button {
+       float: right;
+       padding-right: 20px;
+        button {
+          width: 80px;
+          padding: 5px 0;
+          background: #4a9ef9;
+          border: 0 solid #4a9ef9;
+          border-radius: 1px;
+          margin-left: 100px;
+          color: #fff;
+          cursor: pointer;
+        }
+        .cancel {
+          color: #4a9ef9;
+          background: #fff;
+          border: 1px solid #4a9ef9;
+        }
+      }
+    }
+    .colorA{
+        color:#4a9ef9;
+      }
+  }
+}
+</style>
+<style lang="less">
+  .demo-spin-icon-load{
+        animation: ani-demo-spin 1s linear infinite
+    }
+    @keyframes ani-demo-spin {
+        from { transform: rotate(0deg);}
+        50%  { transform: rotate(180deg);}
+        to   { transform: rotate(360deg);}
+    }
+    .demo-spin-col{
+        height: 100px;
+        position: relative;
+        border: 1px solid #eee;
+    }
+</style>
+

+ 13 - 0
src/modules/firstPage/router/index.js

@@ -0,0 +1,13 @@
+const Home = () => import(/* webpackChunkName: 'firstPage' */ '../pages/home')
+const Main = () => import(/* webpackChunkName: 'firstPage' */ '../pages/main')
+export default [
+    {
+        path: 'home',
+        component: Home,
+        redirect: 'home/main',
+        children: [{
+            path: 'main',
+            component: Main
+        }]
+    }
+]

+ 15 - 0
src/modules/firstPage/store/index.js

@@ -0,0 +1,15 @@
+/**
+ * 设施与人员
+ */
+export default {
+    namespaced: true,
+    state: {
+        name: '首页'
+    },
+    mutations: {
+        
+    },
+    actions: {
+
+    },
+}

+ 7 - 0
src/modules/performance/index.js

@@ -0,0 +1,7 @@
+import store from './store'
+import router from './router'
+
+export {
+    store,
+    router
+}

+ 71 - 0
src/modules/performance/pages/asideMenu/index.vue

@@ -0,0 +1,71 @@
+<template>
+    <div class="aside-menu">
+      <ul>
+        <li>
+          <router-link  to="kpi">
+            <i class="iconfont icon-qushi"></i>
+            <span>项目KPI</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="mark">
+            <i class="iconfont icon-xiangmudafenbiao"></i>
+            <span>项目打分表</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="suggest">
+            <i class="iconfont icon-huiyuanmanyidu"></i>
+            <span>客户满意度</span>
+          </router-link>
+        </li>
+      </ul>
+    </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.aside-menu {
+  ul {
+    > li {
+      &:hover {
+        background: #eee;
+      }
+      a {
+        display: inline-block;
+        white-space: nowrap;
+        padding: 10px 30px;
+        width: 100%;
+        color: #bcbccb;
+        span {
+          padding-left: 37px;
+          color: black;
+          font-size: 15px;
+        }
+      }
+      .router-link-active {
+        color: #57a4f7;
+      }
+    }
+  }
+}
+</style>

+ 66 - 0
src/modules/performance/pages/home/index.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="fas-home">
+    <aside-mune class="aside"></aside-mune>
+    <div class="content">
+      <router-view>
+      </router-view>
+    </div>
+  </div>
+</template>
+
+<script>
+import AsideMune from "../asideMenu";
+
+export default {
+  components: {
+    AsideMune
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.fas-home {
+  .aside {
+    position: fixed;
+    z-index: 999;
+    top: 50px;
+    left: 0;
+    bottom: 0;
+    width: 80px;
+    border-right: 1px solid #ebecf0;
+    transition: all 0.3s ease-in-out;
+    overflow-x: hidden;
+    overflow-y: auto;
+    background: #fff;
+  }
+  .content {
+    margin-left: 80px;
+    padding: 70px 20px 20px 20px;
+    transition: all 0.3s ease-in-out;
+    background: #ebecf0;
+    min-width: 1340px;
+  }
+  & > .aside:hover {
+    width: 260px;
+    background: #fff;
+  }
+  // &>.aside:hover + .content {
+  //   margin-left: 260px;
+  //   padding-left: 20px;
+  // }
+}
+</style>

+ 46 - 0
src/modules/performance/pages/kpi/index.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="pm-kpi">
+    <div class="kpi-top">
+      <DatePicker type="month" size="large" placeholder="2017-11" style="width: 270px"></DatePicker>
+    </div>
+     <div class="kpi-bottom">
+        <img src="/static/img/kpi.jpg" alt="">
+     </div>
+  </div>
+</template>
+
+<script>
+export default {
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.pm-kpi {
+  padding: 20px;
+  background: #fff;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  .kpi-top {
+    margin-bottom: 20px;
+  }
+  .kpi-bottom {
+    height: 700px;
+    img {
+      height: 100%;
+    }
+  }
+}
+</style>

+ 42 - 0
src/modules/performance/pages/mark/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="pe-mark">
+     mark
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 42 - 0
src/modules/performance/pages/suggest/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="per-suggest">
+    suggest
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 34 - 0
src/modules/performance/router/index.js

@@ -0,0 +1,34 @@
+
+// 此方式会将所以文件打包到一个js里,没能实现按需加载
+// import Home from '../pages/home'
+// import EquipmentView from '../pages/equipmentView'
+// import PeopleView from '../pages/peopleView'
+// import OrderInfoView from '../pages/orderInfoView'
+
+// 按需加载,这里将facilitiesAndStaff目录里的内容打包到一个js里。
+const Home = () => import(/* webpackChunkName: 'performance' */ '../pages/home')
+const Kpi = () => import(/* webpackChunkName: 'performance' */ '../pages/kpi')
+const Mark = () => import(/* webpackChunkName: 'performance' */ '../pages/mark')
+const Suggest = () => import(/* webpackChunkName: 'performance' */ '../pages/suggest')
+
+export default [
+    {
+        path: 'performance',
+        component: Home,
+        redirect: 'performance/kpi',
+        children: [
+            {
+                path: 'kpi',
+                component: Kpi
+            },
+            {
+                path:'mark',
+                component:Mark
+            },
+            {
+                path:'suggest',
+                component:Suggest
+            }
+        ]
+    }
+]

+ 15 - 0
src/modules/performance/store/index.js

@@ -0,0 +1,15 @@
+/**
+ * 绩效
+ */
+export default {
+    namespaced: true,
+    state: {
+        name: '绩效'
+    },
+    mutations: {
+        
+    },
+    actions: {
+
+    },
+}

+ 7 - 0
src/modules/safety/index.js

@@ -0,0 +1,7 @@
+import store from './store'
+import router from './router'
+
+export {
+    store,
+    router
+}

+ 77 - 0
src/modules/safety/pages/asideMenu/index.vue

@@ -0,0 +1,77 @@
+<template>
+    <div class="aside-menu">
+      <ul>
+        <li>
+          <router-link  to="summary">
+            <i class="iconfont icon-zonglan"></i>
+            <span>安全总览</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="detail">
+            <i class="iconfont icon-anquancanshu"></i>
+            <span>安全参数详情</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="history">
+            <i class="iconfont icon-dangqianbaojing"></i>
+            <span>安全报警历史</span>
+          </router-link>
+        </li>
+        <li>
+          <router-link  to="statistics">
+            <i class="iconfont icon-tongjifenxiblue"></i>
+            <span>报警数据统计分析</span>
+          </router-link>
+        </li>
+      </ul>
+    </div>
+</template>
+
+<script>
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.aside-menu {
+  ul {
+    > li {
+      &:hover {
+        background: #eee;
+      }
+      a {
+        display: inline-block;
+        white-space: nowrap;
+        padding: 10px 30px;
+        width: 100%;
+        color: #b6bccb;
+        span {
+          padding-left: 37px;
+          color: black;
+          font-size: 15px;
+        }
+      }
+      .router-link-active {
+        color: #57a4f7;
+      }
+    }
+  }
+}
+</style>

+ 186 - 0
src/modules/safety/pages/detail/AlarmReport.vue

@@ -0,0 +1,186 @@
+<template>
+  <div class="alarm-report">
+     <p class="alarm-description">变压器超温 <span>未处理</span> <Icon type="md-close" class="close-btn"  @click="close"/></p>
+     <div class="alarm-detailInfo">
+        <span class="remark-icon"></span><p class="my-title">报警详细信息</p>
+        <p class="detail-info"><span class="info-left">报警位置:</span>配电间 变配电低压开关柜0001号</p>
+        <p class="detail-info"><span class="info-left">报警开始时间:</span>2018.10.20 12:33</p>
+        <p class="detail-info" style=" padding-bottom: 20px;"><span class="info-left">报警持续时间:</span>40分钟</p>
+        <div class="alarm-value">
+          <div class="value">
+            <span>报警值(℃)</span>
+            <p>80</p>
+          </div>
+          <div class="value">
+            <span>当前值(℃)</span>
+            <p>80</p>
+          </div>
+          <div class="value">
+            <span>门限值(℃)</span>
+            <p>80</p>
+          </div>
+        </div>
+        <p class="unit">单位:℃</p>
+        <div id="alarmLine"></div>
+     </div>
+     <div class="alarm-video">
+        <span class="remark-icon"></span><p class="my-title">报警时间内视频监控记录</p>
+        <div class="video"></div>
+     </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+var echarts = require("echarts");
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: ["maskWidth"],
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getLine();
+  },
+
+  methods: {
+    //点击X,弹窗关闭事件
+    close() {
+      this.$emit("closeMask");
+    },
+    //折线图
+    getLine() {
+      var alarmLine = echarts.init(document.getElementById("alarmLine"));
+      let timeValue = [
+        "00:00",
+        "03:00",
+        "06:00",
+        "09:00",
+        "12:00",
+        "15:00",
+        "18:00",
+        "21:00"
+      ];
+      let alarmData = [40, 46, 64, 80, 90, 60, 50, 43];
+      alarmLine.setOption({
+        xAxis: {
+          type: "category",
+          data: timeValue
+        },
+        yAxis: {
+          type: "value"
+        },
+        series: [
+          {
+            data: alarmData,
+            type: "line",
+            color: "#4A9EF9"
+          }
+        ]
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.alarm-report {
+  .alarm-description {
+    height: 56px;
+    line-height: 56px;
+    padding-left: 24px;
+    color: #3b4254;
+    border-bottom: 1px solid #dbdde4;
+    > span {
+      color: #f78686;
+      padding: 3px 7px;
+      background: rgba(247, 134, 134, 0.1);
+      border: 1px solid #f78686;
+      border-radius: 3px;
+      margin-left: 16px;
+    }
+    .close-btn {
+      float: right;
+      margin-right: 10px;
+      margin-top: 17px;
+      cursor: pointer;
+    }
+  }
+  .alarm-detailInfo {
+    .my-title {
+      height: 40px;
+      display: inline-block;
+      color: #3b4254;
+      font-size: 18px;
+      font-weight: bold;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+    .detail-info {
+      line-height: 40px;
+      margin-left: 20px;
+      .info-left {
+        text-align: right;
+        display: inline-block;
+        margin-right: 6px;
+      }
+    }
+    .alarm-value {
+      display: flex;
+      justify-content: space-around;
+      padding: 30px 0;
+      border-top: 1px dashed #dbdde4;
+      .value {
+        > p {
+          font-size: 32px;
+          color: #f78686;
+        }
+      }
+    }
+    #alarmLine {
+      position: relative;
+      margin-left: 30px;
+      width: 340px;
+      height: 340px;
+    }
+    .unit {
+      position: absolute;
+      left: 36px;
+      top: 391px;
+    }
+  }
+  .alarm-video {
+    .my-title {
+      height: 40px;
+      display: inline-block;
+      color: #3b4254;
+      font-size: 18px;
+      font-weight: bold;
+    }
+    .remark-icon {
+      padding: 7px 3px;
+      display: inline-block;
+      background: #4a9ef9;
+      margin: 20px 10px 0 20px;
+    }
+    .video {
+      width: 350px;
+      height: 226px;
+      background: #f6f8fb;
+      border-radius: 3px;
+      margin: 0 0 20px 20px;
+    }
+  }
+}
+</style>

+ 183 - 0
src/modules/safety/pages/detail/EqWarn.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="eq-warn">
+    <div class="alarm-top">
+        <span @click="getAlarmData(0)" :class="{active: activeIndex== 0}">当前报警</span>
+        <span @click="getAlarmData(1)" :class="{active: activeIndex== 1}">尚未处理的报警</span>
+        <span @click="getAlarmData(2)" :class="{active: activeIndex== 2}">近20条报警</span>
+    </div>
+     <i-Table stripe  :columns="columns1" height="590" :data="warnInfo" size="large" @on-row-click="changeDetail" :no-data-text="noData"></i-Table>
+     <div class="mask">
+        <div class="mask-bg" @click="closeMask"></div>
+        <div class="mask-lay" :style="`width:${maskWidth}`">
+            <alarm-report :maskWidth="maskWidth" @closeMask="closeMask"></alarm-report>
+        </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import alarmReport from "./AlarmReport";
+export default {
+  components: {
+    alarmReport
+  },
+
+  mixins: [],
+
+  props: ["warnInfo"],
+
+  data() {
+    return {
+      activeIndex: 0,
+      maskWidth: "0px",
+      id: "",
+      noData:
+        '<div style="padding-top:204px;height:868px;"><i class="iconfont icon-kongzhuangtaiicon" style="font-size:98px;"></i><p>此设备没有尚未处理的报警</p></div>',
+      columns1: [
+        {
+          title: "报警等级",
+          key: "level",
+          render:(h,params)=>{
+           const row = params.row;
+           console.log(row)
+           const backgroundColor = row.level == 'S'? 'error' : row.level == 'A'?'warning':'primary'
+           return h('Button',{
+             props:{
+               type:backgroundColor
+             }
+           },row.level)
+          }
+        },
+        {
+          title: "报警开始时间",
+          key: "ctime", 
+        },
+        {
+          title: "报警持续时间",
+          key: "durationTime",
+        },
+        {
+          title: "报警详情",
+          key: "description",
+          width:400,
+          className:"description-td",
+        },
+        {
+          title: "状态",
+          key: "state",
+        },
+      ]
+    };
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {
+    //关闭弹窗
+    closeMask() {
+      document.querySelector(".mask-bg").style.display = "none";
+      document.querySelector("body").style.overflow = "auto";
+      this.maskWidth = "0px";
+    },
+    //详情弹窗
+    changeDetail(item, index) {
+      this.id = item.id;
+      document.querySelector(".mask-bg").style.display = "block";
+      document.querySelector("body").style.overflow = "hidden";
+      this.maskWidth = "450px";
+    },
+    getAlarmData(index) {
+      this.activeIndex = index;
+    }
+    //报警level样式
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.eq-warn {
+  .alarm-top {
+    margin-bottom: 40px;
+    display: flex;
+    justify-content: center;
+    span {
+      text-align: center;
+      padding: 6px 20px;
+      color: #62697b;
+      cursor: pointer;
+      border: 1px solid #cacaca;
+      border-right: none;
+      &:last-child {
+        border-right: 1px solid #cacaca;
+        border-top-right-radius: 3px;
+        border-bottom-right-radius: 3px;
+      }
+      &:first-child {
+        border-top-left-radius: 3px;
+        border-bottom-left-radius: 3px;
+      }
+      &:hover,
+      &.active {
+        background: #57a4f7;
+        color: #fff;
+      }
+    }
+  }
+  .cell-class-name > .ivu-table-cell > span {
+    display: inline-block;
+    width: 24px;
+    height: 24px;
+    color: #fff;
+    border-radius: 50%;
+  }
+  .mask {
+    .mask-bg {
+      position: fixed;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      z-index: 999;
+      display: none;
+    }
+    .mask-lay {
+      position: fixed;
+      top: 50px;
+      bottom: 0;
+      right: 0;
+      z-index: 999;
+      background: #fff;
+      transition: all 0.5s ease-in-out;
+      overflow-y: auto;
+      box-shadow: 0 7px 15px 0 rgba(0, 0, 0, 0.2);
+    }
+  }
+}
+</style>
+<style lang="less">
+.eq-warn {
+  .ivu-table-row td:first-of-type .ivu-table-cell > button {
+    display: inline-block;
+    width: 30px;
+    height: 30px;
+    border-radius: 50%;
+    color: #fff;
+    font-size: 16px;
+    line-height:16px;
+  }
+  .ivu-table-row td:first-of-type .ivu-table-cell>button>span{
+    margin-left:-4px;
+  }
+ .ivu-table-row .description-td>.ivu-table-cell>span{
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        max-height: 100px;
+        display: inline-block;
+    }
+}
+</style>
+

+ 323 - 0
src/modules/safety/pages/detail/index.vue

@@ -0,0 +1,323 @@
+<template>
+  <div class="sa-detail">
+     <div class="sa-left">
+       <div class="sa-left-top">
+            <div class="input-stat">
+              <i-Input search v-model="keyword" placeholder="请输入关键字" @on-change="getAlarmEqInfo()"/>
+            </div> 
+            <div>
+              <label for="position">设备位置:</label>
+              <i-Select id="position" v-model="position"  style="width:200px" @on-change="getAlarmEqInfo()">
+                  <Option v-for="item in roomList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+              </i-Select>
+            </div>
+             <br/>
+             <div>
+                <label for="major">设备专业:</label>
+                <i-Select id="major" v-model="major"  style="width:200px" @on-change="getAlarmEqInfo()">
+                    <Option v-for="item in majorList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+                </i-Select>
+             </div>
+             <br/><br/>
+        </div>
+        <div class="sa-left-bottom">
+          <p><strong>总共<span> {{warnList.length}} </span>个符合条件的设备空间:</strong></p>
+          <ul>
+            <li  v-for="(item,index) in warnList" :key="index" @click="showDetail(item,index)" :class="{activeStyle:index == changeStyle}">{{item.eqObjName}}</li>
+          </ul>
+        </div>
+     </div>
+     <div class="sa-right">
+       <div class="sa-right-top">
+         <p class="eq-info"><strong>{{eqDetail.eqName}}</strong></p>
+          <div class="sa-box">
+            <div class="warn-num">
+              <p class="word-alarm"><span>所属建筑:</span>{{eqDetail.bdName}}</p>  
+              <p class="word-alarm"><span>所属空间:</span>{{eqDetail.spName}}</p>  
+              <p class="word-alarm"><span>所属专业:</span>{{eqDetail.major}}</p>  
+            </div>
+            <div class="warn-num">
+               <p>当前报警数量</p>
+              <span style=" padding-left: 20px;">{{eqDetail.currentAlarmCount}}</span>  
+            </div>
+            <div class="warn-num">
+               <p>当前严重报警(S级/A级)</p>
+               <span style=" padding-left: 35px;">{{eqDetail.alarmSACount}}</span>
+            </div>
+            <div class="warn-num">
+               <p>同类设备</p>
+               <span>{{eqDetail.similarEqCount}}</span>
+            </div>
+            <div class="warn-num">
+               <p>报警设备</p>
+               <span style="padding-left:15px;">{{eqDetail.similarEqAlarmCount}}</span>
+            </div>
+          </div>
+       </div>
+       <div class="sa-right-bottom">
+         <eq-warn :warnInfo="warnInfo"></eq-warn>
+       </div>
+     </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import eqWarn from "./EqWarn";
+export default {
+  components: {
+    eqWarn
+  },
+
+  mixins: [],
+
+  props: {},
+  data() {
+    return {
+      majorList: [],
+      roomList: [],
+      model2: "",
+      keyword: "",
+      major: "",
+      position: "",
+      roomNum: 26,
+      warnList: [
+        
+      ],
+      eqInfo: {},
+      warnInfo: [],
+      changeStyle: 0,
+      eqDetail: {},
+      majorZH: {}
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getRoomList();
+    this.getMajor();
+    this.getAlarmEqInfo();
+  },
+
+  methods: {
+    //设备位置的获取
+    getRoomList() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/getFloorList?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          let list = data.list;
+          list.forEach(element => {
+            element.label = element.value = element.floorName;
+          });
+          this.roomList = list;
+        });
+    },
+    //设备专业的获取
+    getMajor() {
+      axios.get(`/equipment/info-mng-backend/safe/getMajorList`).then(res => {
+        if (res.data.result == "success") {
+          let data = res.data.data;
+          data.list.forEach(element => {
+            element.label = element.value = element.class;
+          });
+          this.majorList = data.list;
+        }
+      });
+    },
+    //设备空间列表的查找
+    getAlarmEqInfo() {
+      let career = "";
+      let floorId = "";
+      let keyword = this.keyword;
+      //专业的处理
+      this.majorList.forEach(el => {
+        if (el.class == this.major) {
+          career = el.code;
+        }
+      });
+      //楼层Id的处理
+      this.roomList.forEach(el => {
+        if (el.floorName == this.position) {
+          floorId = el.floorId;
+        }
+      });
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/getAlarmEqInfo?projectId=${this.$store.state.projectId}&major=${career}&floorId=${floorId}&keyword=${keyword}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.warnList = data.list;
+          if(this.warnList.length>0){
+              this.eqInfo = data.list;
+              this.showDetail(this.eqInfo, this.changeStyle);
+              this.initAlarm();
+          }
+        });
+    },
+     //开始时间格式调整
+     formatTime(str) {
+      return (
+        str.slice(0, 4) +
+        "." +
+        str.slice(4, 6) +
+        "." +
+        str.slice(6, 8) +
+        " " +
+        str.slice(8, 10) +
+        ":" +
+        str.slice(10, 12)
+      );
+    },
+    //持续时间格式化
+    toHourMinute(minutes){
+      return ((Math.floor(minutes/60)>=24?Math.floor(minutes/60/24)+'天'+
+      (Math.floor(minutes/60%24)):Math.floor(minutes/60)) + "小时" + (minutes%60) + "分" );
+    },
+    //右侧报警表格的数据
+    showDetail(item, index) {
+      this.eqInfo = item;
+      let id = item.eqObj;
+      this.changeStyle = index;
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/getCurrentAlarmList?projectId=${this.$store.state.projectId}&eqId=${id}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          data.list.forEach(el=>{
+            el.ctime = this.formatTime(el.ctime);
+            el.durationTime = this.toHourMinute(el.durationTime);
+          })
+          this.warnInfo = data.list;
+        });
+      this.initAlarm();
+    },
+    //初始报警列表
+    initAlarm() {
+      let id = this.eqInfo.eqObj;
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/getAlarmDetail?projectId=${this.$store.state.projectId}&eqId=${id}`
+        )
+        .then(res => {
+          this.eqDetail = res.data.data;
+        });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.sa-detail {
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  display: flex;
+  .sa-left {
+    width: 20%;
+    padding: 20px 10px;
+    margin-right: 10px;
+    background: #fff;
+    border-radius: 3px;
+    min-width: 304px;
+    .sa-left-top {
+      border-bottom: 1px dashed #dbdde4;
+      text-align: center;
+      .input-stat {
+        width: 270px;
+        display: inline-block;
+        margin: 30px 0;
+      }
+    }
+    .sa-left-bottom {
+      text-indent: 2em;
+      > p {
+        padding: 10px 0;
+        color: #8c94a9;
+        > span {
+          font-size: 20px;
+        }
+      }
+      ul {
+        height: 600px;
+        overflow-y: auto;
+        li {
+          padding: 10px 0;
+          cursor: pointer;
+          text-indent: 2em;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+        &:not(:last-child) {
+          border-bottom: 1px solid #eee;
+        }
+        .activeStyle {
+          color: #57a4f7;
+        }
+      }
+    }
+  }
+  .sa-right {
+    width: 79.4%;
+    display: flex;
+    flex-direction: column;
+    .sa-right-top {
+      padding: 20px;
+      background: #fff;
+      border-radius: 3px;
+      margin-bottom: 10px;
+      .eq-info {
+        strong {
+          font-size: 24px;
+        }
+        .eq-major {
+          padding: 0 37px;
+        }
+      }
+      .sa-box {
+        display: flex;
+        justify-content: space-around;
+        margin: 20px;
+        .warn-num {
+          text-align: left;
+          > span {
+            font-size: 40px;
+          }
+          .word-alarm {
+            max-width: 390px;
+            word-break: break-all;
+          }
+        }
+      }
+    }
+    .sa-right-bottom {
+      padding: 20px;
+      background: #fff;
+      border-radius: 3px;
+      flex: 1;
+    }
+  }
+}
+</style>
+<style  lang="less">
+.sa-detail {
+  .ivu-tabs-bar {
+    border: none;
+  }
+  .ivu-table-large {
+    font-size: 16px;
+  }
+  .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    border: none;
+    background: #fff;
+  }
+  .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab:not(:last-child) {
+    border-right: 1px solid #ccc;
+  }
+}
+</style>
+

+ 180 - 0
src/modules/safety/pages/history/index.vue

@@ -0,0 +1,180 @@
+<template>
+  <div class="sa-history">
+    <div class="history-top">
+        <DatePicker type="daterange" size="large" split-panels :value="value1" format="yyyy/MM/dd"  
+         style="width: 270px" @on-change="changeDate">
+         </DatePicker>
+        <i-Select v-model="model1" size="large" style="width:210px" @on-change="initHistoryWarnTable">
+            <Option v-for="item in type" :value="item.value" :key="item.value">{{ item.label }}</Option>
+        </i-Select>
+        <i-Input size="large" v-model="keyword" placeholder="搜索设备名/空间名/系统名" style="width:auto" @on-change="initHistoryWarnTable">
+            <Icon type="ios-search" slot="suffix" />
+        </i-Input>
+    </div>
+    <div >
+      <i-Table stripe size="large" :columns="columns1" :data="data1" :no-data-text="noData"></i-Table>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      value1: [
+        new Date().toLocaleDateString(),
+        new Date().toLocaleDateString()
+      ],
+      model1: "", ///ivu-icon-ios-close-circle
+      type: [
+        { label: "S级", value: "S" },
+        { label: "A级", value: "A" },
+        { label: "B级", value: "B" },
+        { label: "C级", value: "C" },
+        { label: "D级", value: "D" }
+      ],
+      keyword:"",
+      columns1: [
+        {
+          title: "报警开始时间",
+          key: "ctime",
+          sortable: true,
+        },
+        {
+          title: "设备名/空间名/系统名",
+          key: "spObjName",
+        },
+        {
+          title: "位置",
+          key: "bdObjName",
+        },
+        {
+          title: "报警持续时间",
+          key: "durationTime", 
+        },
+        {
+          title: "报警级别",
+          key: "level",        
+        },
+        {
+          title: "报警描述",
+          key: "description",
+          className:"description-td",
+          width:500,
+        }
+      ],
+      data1: [   
+      ],
+      noData:
+        '<div style="padding-top:204px;height:868px;"><i class="iconfont icon-kongzhuangtaiicon" style="font-size:98px;"></i><p>设备很健康,暂时没有报警数据</p></div>'
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.initHistoryWarnTable();
+  },
+
+  methods: {
+    //时间的选择
+    changeDate(date){
+       this.value1 = date;
+       this.initHistoryWarnTable();
+    },
+    //历史报警表格初始化
+    initHistoryWarnTable(){
+      let level = this.model1;
+      let keyword = this.keyword;
+      let startTime = this.value1[0].split("/").join("")+"000000";
+      let endTime = this.value1[1].split("/").join("")+"235959";
+      axios.get(`/equipment/info-mng-backend/safe/getHistoryAlarm?projectId=${this.$store.state.projectId}&level=${level}&startTime=${startTime}&endTime=${endTime}&keyword=${keyword}`
+      ).then(res=>{
+        this.data1 = res.data.data.list
+        this.data1.forEach(el=>{
+          el.ctime = this.formatTime(el.ctime);
+          el.durationTime = this.toHourMinute(el.durationTime);
+          el.bdObjName = el.bdObjName+(el.flObjName?"-"+el.flObjName:"");
+          if(el.objects[0].startsWith("Sp")){
+             el.spObjName = el.spObjName;
+          }else if(el.objects[0].startsWith("Eq")){
+            el.spObjName = el.eqObjName;
+          }else if(el.objects[0].startsWith("Sy")){
+             el.spObjName = el.sysObjName;
+          }else{
+            el.spObjName = ""
+          }
+        })
+      })
+    },
+     //开始时间格式调整
+     formatTime(str) {
+      return (
+        str.slice(0, 4) +
+        "." +
+        str.slice(4, 6) +
+        "." +
+        str.slice(6, 8) +
+        " " +
+        str.slice(8, 10) +
+        ":" +
+        str.slice(10, 12)
+      );
+    },
+    //持续时间格式化
+    toHourMinute(minutes){
+      return ((Math.floor(minutes/60)>=24?Math.floor(minutes/60/24)+'天'+
+      (Math.floor(minutes/60%24)):Math.floor(minutes/60)) + "小时" + (minutes%60) + "分" );
+    },
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.sa-history {
+  background: #fff;
+  padding: 20px;
+  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+  border-radius: 2px;
+  .history-top {
+    margin-bottom: 20px;
+  }
+}
+</style>
+<style lang="less">
+.ivu-table-wrapper {
+  border: none;
+}
+.ivu-table:before {
+  height: 0;
+}
+.ivu-table:after {
+  width: 0;
+}
+.ivu-table td,
+.ivu-table th {
+  border: none;
+}
+</style>
+<style lang="less">
+.sa-history {
+ .ivu-table-row .description-td>.ivu-table-cell>span{
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        max-height: 100px;
+        display: inline-block;
+    }
+    .ivu-date-picker .ivu-input-wrapper>i{
+      display: none;
+    }
+}
+</style>
+

+ 62 - 0
src/modules/safety/pages/home/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="fas-home">
+    <aside-mune class="aside"></aside-mune>
+    <div class="content">
+      <router-view>
+      </router-view>
+    </div>
+  </div>
+</template>
+
+<script>
+import AsideMune from "../asideMenu";
+
+export default {
+  components: {
+    AsideMune
+  },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {}
+};
+</script>
+
+<style scoped lang="less" >
+.fas-home {
+  .aside {
+    position: fixed;
+    z-index: 999;
+    top: 50px;
+    left: 0;
+    bottom: 0;
+    width: 80px;
+    border-right: 1px solid #ebecf0;
+    transition: all 0.3s ease-in-out;
+    overflow-x: hidden;
+    overflow-y: auto;
+    background: #fff;
+  }
+  .content {
+    margin-left: 80px;
+    padding: 70px 20px 20px 20px;
+    transition: all 0.3s ease-in-out;
+    background: #ebecf0;
+    min-width: 1340px;
+  }
+  & > .aside:hover {
+    width: 260px;
+    background: #fff;
+  }
+}
+</style>

+ 42 - 0
src/modules/safety/pages/statistics/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="sty-statistics">
+    statistics
+  </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+
+  mixins: [],
+
+  props: {
+
+  },
+
+  data () {
+    return {
+
+    }
+  },
+
+  computed: {
+
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+  },
+
+}
+</script>
+
+<style scoped lang="less" >
+
+</style>

+ 283 - 0
src/modules/safety/pages/summary/detect.vue

@@ -0,0 +1,283 @@
+<template>
+  <div class="st-bottom">
+      <div class="st-test">
+        <p class="st-title">变压器监测</p>
+        <p class="transformer">变压器总数&nbsp;&nbsp;&nbsp;{{transformerCount}} <span style="padding-left:35px">正常</span>&nbsp;&nbsp;&nbsp;{{normalCount}}</p>
+        <div class="st-version">
+          <div class="version" v-for="(item,index) in monitoreList" :key="index">
+            <p class="type"><i class="iconfont icon-qiangdianzhuanyexitong" style="color:rgba(74, 158, 249, 0.74);margin-right:5px;"></i>{{item.eqName}}</p>
+            <p class="temperature">当前温度: {{item.APhaseCoilTemp}}℃ &nbsp;&nbsp;&nbsp;&nbsp; {{item.BPhaseCoilTemp}}℃ &nbsp;&nbsp;&nbsp;&nbsp;{{item.CPhaseCoilTemp}}℃</p>
+            <p class="load">负载率: {{item.HULoadRate}}% {{item.LULoadRate}}%</p>
+          </div>
+        </div>
+      </div>
+      <div class="st-test">
+        <p class="st-title">生活水箱监测</p>
+        <p class="transformer">生活水箱总数&nbsp;&nbsp;&nbsp;{{lifeWaterTankCount}}<span style="padding-left:35px">正常</span>&nbsp;&nbsp;&nbsp;{{lifeNormalCount}}</p>
+        <div class="st-version">
+          <div class="version" v-for="(item,index) in LifeList" :key="index">
+            <p class="type"><i class="iconfont icon-shenghuoreshui" style="color:rgba(74, 158, 249, 0.74);margin-right:5px;"></i>{{item.eqName}}</p>
+            <p style="padding:37px 0 20px 0;">水位高度:</p>
+            <p><span style="padding-left:20px;">{{parseInt(item.waterSurfaceElevation)}}%</span></p>
+            <i-Progress :percent="parseInt(item.waterSurfaceElevation)" :stroke-width="5" />      
+          </div>
+        </div>
+      </div>
+      <div class="st-test">
+        <p class="st-title">浸水监测</p>
+        <div class="mafan">
+          <div class="abnormal">
+            <p class="ab-value">3</p>
+            <span class="ab-style">监测房间数</span>
+          </div>
+          <div class="normal">
+            <p class="no-value">8</p>
+            <span class="no-style">正常位</span>
+          </div>
+        </div>
+        <p class="current-call">  <img src="/static/img/call.png" alt=""> 当前报警</p>
+        <ul>
+          <li>报警位置名称<span>2018.10.26 10:45</span></li>
+          <li>报警位置名称<span>2018.10.26 10:45</span></li>
+          <li>报警位置名称<span>2018.10.26 10:45</span></li>
+          <li>报警位置名称<span>2018.10.26 10:45</span></li>
+        </ul>
+      </div>
+      <div class="st-test">
+        <p class="st-title">集水坑监测</p>
+        <div class="mafan">
+          <div class="abnormal">
+            <p class="ab-value">{{catchpitCount}}</p>
+            <span class="ab-style">监测水坑数</span>
+          </div>
+          <div class="normal">
+            <p class="no-value">{{normalNum}}</p>
+            <span class="no-style">正常位</span>
+          </div>
+        </div>
+        <p class="current-call">  <img src="/static/img/call.png" alt=""> 当前报警</p>
+        <ul v-for="(item,index) in list" :key="index">
+          <li>{{item.description}}<span>{{formatTime(item.ctime)}}</span></li>
+        </ul>
+      </div>
+    </div>
+</template>
+
+<script>
+import axios from "axios";
+export default {
+  components: {},
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      projectId: "${this.$store.state.projectId}",
+      transformerCount: 5,
+      normalCount: 3,
+      catchpitCount: 2,
+      normalNum: 2,
+      list: [
+        { description: "地理位置", ctime: "201810111025" },
+        { description: "地理位置", ctime: "201810111025" }
+      ],
+      monitoreList: [],
+      lifeWaterTankCount: 0,
+      lifeNormalCount: 0,
+      LifeList: [
+       
+      ]
+    };
+  },
+
+  computed: {},
+
+  mounted() {
+    this.getTransformerMonitore();
+    this.getLifeWaterTankMonitore();
+    this.getCatchpitAlarm();
+  },
+
+  methods: {
+    //时间格式转换
+    formatTime(str) {
+      return (
+        str.slice(0, 4) +
+        "." +
+        str.slice(4, 6) +
+        "." +
+        str.slice(6, 8) +
+        " " +
+        str.slice(8, 10) +
+        ":" +
+        str.slice(10, 12)
+      );
+    },
+    //变压器监测
+    getTransformerMonitore() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/transformerMonitoring?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.normalCount = data.normalCount;
+          this.transformerCount = data.transformerCount;
+          this.monitoreList = data.list;
+        });
+    },
+    //生活水箱监测
+    getLifeWaterTankMonitore() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/lifeWaterTankMonitoring?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.lifeWaterTankCount = data.lifeWaterTankCount;
+          this.lifeNormalCount = data.normalCount;
+          this.LifeList = data.list;
+        });
+    },
+    //集水坑监测
+    getCatchpitAlarm() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/getCatchpitAlarm?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.catchpitCount = data.catchpitCount;
+          this.normalNum = data.normalCount;
+          this.list = data.list;
+        });
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.st-bottom {
+  display: flex;
+  .st-test {
+    display: inline-block;
+    width: 30%;
+    margin-right: 0.4%;
+    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    background: #fff;
+    .st-title {
+      height: 50px;
+      line-height: 50px;
+      color: #3b4254;
+      padding-left: 20px;
+      background: #f7f9fc;
+      margin-bottom: 30px;
+      font-weight: bold;
+      font-size: 18px;
+    }
+    .transformer {
+      color: #62697b;
+      padding-left: 30px;
+    }
+    .st-version {
+      margin: 10px 20px;
+      height: 320px;
+      overflow-y: auto;
+      .version {
+        background: #f6f8fb;
+        border-radius: 1px;
+        width: 43%;
+        padding: 20px 0 20px 12px;
+        margin: 12px 12px 12px 0;
+        font-size: 14px;
+        display: inline-block;
+        position: relative;
+        .temperature {
+          margin-top: 17.2px;
+          margin-bottom: 10px;
+          color: #62697b;
+        }
+        .load {
+          color: #62697b;
+        }
+      }
+    }
+  }
+  .mafan {
+    display: flex;
+    margin-bottom: 40px;
+    .abnormal {
+      width: 50%;
+      text-align: center;
+      display: inline-block;
+      .ab-value {
+        font-family: 'ArialMT';
+        font-size: 32px;
+        color: #3b4254;
+        margin: 10px 0 14px 0;
+      }
+      .ab-style {
+        background: rgba(74, 158, 249, 0.74);
+        border-radius: 16px;
+        display: inline-block;
+        width: 88px;
+        height: 24px;
+        color: #fff;
+        line-height: 24px;
+      }
+    }
+    .normal {
+      width: 50%;
+      text-align: center;
+      display: inline-block;
+      .no-value {
+        font-family: 'ArialMT';
+        font-size: 32px;
+        color: #3b4254;
+        margin: 10px 0 14px 0;
+      }
+      .no-style {
+        background: rgba(115, 218, 153, 0.82);
+        border-radius: 16px;
+        display: inline-block;
+        width: 88px;
+        height: 24px;
+        line-height: 24px;
+        color: #fff;
+      }
+    }
+  }
+  .current-call {
+    margin-left: 20px;
+  }
+  ul {
+    padding: 18px 30px 0 42px;
+    li {
+      color: #3b4254;
+      border-bottom: 1px dashed #e6e7ec;
+      padding: 5px 0;
+      > span {
+        float: right;
+      }
+      &:last-child {
+        border-bottom: none;
+      }
+    }
+  }
+}
+</style>
+<style lang="less" >
+.st-bottom {
+  .ivu-progress-outer {
+    transform: rotate(-90deg);
+    width: 97px;
+    position: absolute;
+    left: 60px;
+    top: -60px;
+  }
+}
+</style>
+

+ 457 - 0
src/modules/safety/pages/summary/index.vue

@@ -0,0 +1,457 @@
+<template>
+  <div class="st-summary">
+    <!-- <div v-if="grade==0">
+        <Spin fix>
+            <Icon type="ios-loading" size=40 class="demo-spin-icon-load"></Icon>
+            <div>Loading</div>
+        </Spin>
+    </div> -->
+    <div class="st-top">
+        <div class="st-score">
+        <p class="st-p">当前评分</p>
+        <div class="st-div">
+          <div class="st-box" :class="classObject">{{grade}}</div>
+          <div class="st-type">
+            <p class="st-detection"></p>
+            <div class="st-status">
+              <p class="st-appliance">变压器</p>
+              <div class="st-result" :class="transformer==0?'classA':'classB'">{{transformer==0?'异常':'正常'}}</div>
+            </div>
+            <div class="st-status">
+              <p class="st-appliance">生活水箱</p>
+              <div class="st-result" :class="lifeTank==0?'classA':'classB'">{{lifeTank==0?'异常':'正常'}}</div>
+            </div>
+            <div class="st-status">
+              <p class="st-appliance">消防水压</p>
+              <div class="st-result" :class="fireWater==0?'classA':'classB'">{{fireWater==0?'异常':'正常'}}</div>
+            </div>
+            <div class="st-status">
+              <p class="st-appliance">集水坑</p>
+              <div class="st-result" :class="catchpit==0?'classA':'classB'">{{catchpit==0?'异常':'正常'}}</div>
+            </div>
+            <div class="st-status">
+              <p class="st-appliance">浸水检测</p>
+              <div class="st-result" :class="soaking==0?'classA':'classB'">{{soaking==0?'异常':'正常'}}</div>
+            </div>
+            <div class="st-status">
+              <p class="st-appliance">设备房</p>
+              <div class="st-result" :class="eqRoom==0?'classA':'classB'">{{eqRoom==0?'异常':'正常'}}</div>
+            </div>
+
+          </div>
+        </div>
+      </div>
+      <div class="st-warn">
+        <p class="st-p">当前报警<button class="st-btn">报警详情</button></p>
+          <div class="st-call">
+            <div class="st-div">
+              <p class="st-grade"><img src="/static/img/Sicon.png" alt=""> S级报警</p>
+              <p class="st-number">{{gradeS}}</p>
+              <p class="st-describ">严重故障:会导致停机、设备损坏等问题的严重故障;计入设备、系统故障时间统计;</p>
+            </div>
+            <div class="st-div">
+              <p class="st-grade"><img src="/static/img/Aicon.png" alt=""> A级报警</p>
+              <p class="st-number">{{gradeA}}</p>
+              <p class="st-describ">故障:会导致运行故障,但不会引起严重的系统问题,影响范围有限。例如单个污水坑报警;计入设备、系统故障时间统计;</p>
+            </div>
+            <div class="st-div">
+              <p class="st-grade"><img src="/static/img/Bicon.png" alt=""> B级报警</p>
+              <p class="st-number">{{gradeB}}</p>
+              <p class="st-describ">一般问题:不会导致停机,但对安全运行会造成风险的一般报警。例如房间温度过高,传感器不正常等;不计入故障时间统计;</p>
+            </div>
+          </div>
+      </div>
+    </div>
+    <div class="st-middle">
+      <div class="st-left">
+        <p class="st-title">消防水压检测</p>
+        <div>
+          当前水压
+          <p class="st-gage"><span class="font-style">{{fireWaterOutPress}}</span>  Mpa</p>
+        </div>
+      </div>
+      <div class="st-right">
+        <p class="st-title">设备房温湿度监测</p>
+        <div class="st-table">
+          <table class="ivu-table-wrapper ivu-table ivu-table-large ivu-table-border ivu-table-stripe" style="position:relative">   
+              <div class="ov-div">
+                <table class="fixed-table">
+                  <colgroup></colgroup> 
+                  <tr class="ivu-table-row">
+                    <td class="ivu-table-cell fixed-td" >设备房名称</td>
+                    <td class="ivu-table-cell" v-for="(item,index) in data2" :key="index">{{item.RoomLocalName}}</td>
+                  </tr>
+                  <tr class="ivu-table-row">
+                    <td class="ivu-table-cell fixed-td">温度(℃)</td>
+                    <td class="ivu-table-cell" v-for="(item,index) in data2" :key="index">{{item.Tdb}}</td>
+                  </tr>
+                  <tr class="ivu-table-row">
+                    <td class="ivu-table-cell fixed-td">湿度(%)</td>
+                    <td class="ivu-table-cell" v-for="(item,index) in data2" :key="index">{{item.RH}}</td>
+                  </tr>
+                </table>
+               </div>
+          </table>
+        </div>
+      </div>
+    </div>
+    <detect></detect>
+  </div>
+</template>
+
+<script>
+import detect from "./Detect.vue";
+import axios from "axios";
+export default {
+  components: { detect },
+
+  mixins: [],
+
+  props: {},
+
+  data() {
+    return {
+      projectId: "${this.$store.state.projectId}",
+      grade: 0,
+      gradeS: 12,
+      gradeA: 24,
+      gradeB: 108,
+      gradeC: 85,
+      transformer: 1,
+      lifeTank: 1,
+      fireWater: 1,
+      catchpit: 0,
+      soaking: 1,
+      eqRoom: 1,
+      fireWaterOutPress: 0,
+      data2:[
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50},
+        {RoomLocalName:"3F-5号给水井",Tdb:17,RH:50}
+      ],
+    };
+  },
+
+  computed: {
+    classObject: function() {
+      if (this.grade <= 59) {
+        return "backendA";
+      } else if (this.grade <= 84) {
+        return "backendC";
+      } else {
+        return "backendB";
+      }
+    }
+  },
+
+  mounted() {
+    this.getSafetyMark();
+    this.getWarnInfo();
+    this.getWaterOutPress();
+    this.getEquipmentException();
+  },
+
+  methods: {
+    //当前评分
+    getSafetyMark() {
+      axios
+        .get(`/equipment/info-mng-backend/safe/index?projectId=${this.$store.state.projectId}`)
+        .then(res => {
+          let data = res.data.data;
+          this.grade = data.grade;
+        });
+    },
+    //当前报警
+    getWarnInfo() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/alarmStatistics?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.gradeS = data.S;
+          this.gradeA = data.A;
+          this.gradeB = data.B;
+          this.gradeC = data.C;
+        });
+    },
+    //消防水压检测
+    getWaterOutPress() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/fireWaterOutPress?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.fireWaterOutPress = data.fireWarterOutPress;
+        });
+    },
+    //当前评分中的各项设备是否正常
+    getEquipmentException() {
+      axios
+        .get(
+          `/equipment/info-mng-backend/safe/equipmentException?projectId=${this.$store.state.projectId}`
+        )
+        .then(res => {
+          let data = res.data.data;
+          this.transformer = data.transformer;
+          this.lifeTank = data.lifeTank;
+          this.fireWater = data.fireWater;
+          this.catchpit = data.catchpit;
+          this.soaking = data.soaking;
+          this.eqRoom = data.eqRoom;
+        });
+    },
+    //设备房
+    getEqRoomMonitoring(){
+      axios.get(`/equipment/info-mng-backend/safe/getEqRoomMonitoring?projectId=${this.$store.state.projectId}`).then(res=>{
+        let data = res.data.data;
+        this.data2 = data.list
+      })
+    }
+  }
+};
+</script>
+
+<style scoped lang="less" >
+.st-summary {
+  .st-top {
+    display: flex;
+    .st-score {
+      width: 66.4%;
+      box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      display: inline-block;
+      margin-right: 10px;
+      background: #fff;
+      .st-p {
+        height: 50px;
+        line-height: 50px;
+        background: #f7f9fc;
+        border: 0 solid #f2f2f2;
+        font-weight: bold;
+        font-size: 18px;
+        color: #3b4254;
+        padding-left: 20px;
+      }
+      .st-div {
+        display: flex;
+        .st-box {
+          width: 14.8%;
+          height: 180px;
+          border-radius: 8px;
+          color: #fff;
+          font-size: 42px;
+          font-weight: bold;
+          text-align: center;
+          line-height: 180px;
+          margin: 30px 3.3% 0 3.3%;
+          display: inline-block;
+        }
+        .backendC {
+          background: yellow;
+        }
+        .backendA {
+          background: #ff7b7b;
+        }
+        .backendB {
+          background: #73da99;
+        }
+        .st-type {
+          display: inline-block;
+          width: 75.2%;
+          margin-top: 6px;
+          .st-detection {
+            padding: 20px 0 18px 0;
+            font-weight: bold;
+            font-size: 16px;
+            color: #ff7b7b;
+          }
+          .st-status {
+            width: 14%;
+            height: 140px;
+            border: 1px solid #f0f1f5;
+            border-radius: 3px;
+            display: inline-block;
+            margin: 0 14px 14px 0;
+            .st-appliance {
+              background: #f6f8fb;
+              height: 40px;
+              line-height: 40px;
+              text-align: center;
+            }
+            .st-result {
+              font-size: 26px;
+              color: #ff7b7b;
+              letter-spacing: 0;
+              height: 100px;
+              line-height: 100px;
+              text-align: center;
+            }
+            .classA {
+              color: #ff7b7b;
+            }
+            .classB {
+              color: #73da99;
+            }
+          }
+        }
+      }
+    }
+    .st-warn {
+      width: 32.8%;
+      box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      display: inline-block;
+      background: #fff;
+      .st-p {
+        height: 50px;
+        line-height: 50px;
+        background: #f7f9fc;
+        border: 0 solid #f2f2f2;
+        font-weight: bold;
+        font-size: 18px;
+        color: #3b4254;
+        padding-left: 20px;
+        .st-btn {
+          width: 100px;
+          height: 30px;
+          line-height: 30px;
+          text-align: center;
+          border: 1px solid #4a9ef9;
+          border-radius: 3px;
+          font-size: 14px;
+          color: #4a9ef9;
+          background: #fff;
+          float: right;
+          margin-top: 9px;
+          cursor: pointer;
+          margin-right: 10px;
+        }
+      }
+      .st-call {
+        display: flex;
+        .st-div {
+          flex: 1;
+          display: inline-block;
+          padding: 30px 3.5% 0 3.5%;
+          //border: 0 solid #f2f2f2;
+          .st-grade {
+            font-size: 14px;
+            color: #62697b;
+            text-align: center;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            img {
+              padding-right: 6px;
+            }
+          }
+          .st-number {
+            font-size: 32px;
+            color: #3b4254;
+            text-align: center;
+            padding: 10px 0 16px 0;
+          }
+          .st-describ {
+            font-size: 12px;
+            color: #8c94a9;
+            line-height: 20px;
+            overflow: hidden;
+          }
+        }
+      }
+    }
+  }
+  .st-middle {
+    display: flex;
+    margin: 10px 0;
+    .st-left {
+      box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      height: 280px;
+      width: 15%;
+      display: inline-block;
+      margin-right: 0.5%;
+      background: #fff;
+      .st-title {
+        height: 50px;
+        line-height: 50px;
+        font-weight: bold;
+        font-size: 18px;
+        color: #3b4254;
+        background: #f7f9fc;
+        padding-left: 20px;
+      }
+      div {
+        padding: 30px 0 0 20px;
+        color: #62697b;
+        .st-gage {
+          margin-top: 48px;
+          text-align: center;
+          .font-style {
+            font-family: 'Arial-BoldMT';
+            font-size: 32px;
+            color: #3b4254;
+          }
+        }
+      }
+    }
+    .st-right {
+      display: inline-block;
+      box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      height: 280px;
+      width: 85%;
+      background: #fff;
+      .st-title {
+        height: 50px;
+        line-height: 50px;
+        font-weight: bold;
+        font-size: 18px;
+        color: #3b4254;
+        padding-left: 20px;
+        background: #f7f9fc;
+        margin-bottom: 20px;
+      }
+      .st-table {
+        padding-left: 50px;
+        padding-right: 20px;
+        .ov-div{
+          width:100%;
+          overflow-x:scroll;
+        }
+        .fixed-table{
+          width:120%;
+          table-layout: fixed;
+        }
+        .fixed-td{
+          position:absolute;
+          width:14%;
+          line-height: 60px;
+        }
+      }
+    }
+  }
+}
+</style>
+<style lang="less">
+.st-summary {
+  .ivu-progress-text {
+    display: none;
+  }
+  .ivu-progress-inner {
+    background: #dfe7f0;
+    border-radius: 10px;
+  }
+  .ivu-progress-show-info .ivu-progress-outer {
+    padding-right: 0;
+  }
+}
+</style>
+

+ 41 - 0
src/modules/safety/router/index.js

@@ -0,0 +1,41 @@
+import moduleRouters from '../../../router/moduleRoutes';
+
+// 此方式会将所以文件打包到一个js里,没能实现按需加载
+// import Home from '../pages/home'
+// import EquipmentView from '../pages/equipmentView'
+// import PeopleView from '../pages/peopleView'
+// import OrderInfoView from '../pages/orderInfoView'
+
+// 按需加载,这里将facilitiesAndStaff目录里的内容打包到一个js里。
+const Home = () => import(/* webpackChunkName: 'safety' */ '../pages/home')
+const Summary = () => import(/* webpackChunkName: 'safety' */ '../pages/summary')
+const Detail = () => import(/* webpackChunkName: 'safety' */ '../pages/detail')
+const History = () => import(/* webpackChunkName: 'safety' */ '../pages/history')
+const Statistics = () => import(/* webpackChunkName: 'safety' */ '../pages/statistics')
+
+export default [
+    {
+        path: 'safety',
+        component: Home,
+        redirect: 'safety/summary',
+        children: [
+            {
+                path: 'summary',
+                component: Summary
+            },
+            {
+                path: 'detail',
+                component: Detail
+            },
+            {
+                path: 'history',
+                component: History
+            },
+            {
+                path: 'statistics',
+                component: Statistics
+            },
+        ]
+    }
+]
+

+ 11 - 0
src/modules/safety/store/index.js

@@ -0,0 +1,11 @@
+/**
+ * 安全
+ */
+export default {
+  namespaced: true,
+  state: {
+    name: '安全'
+  },
+  mutations: {},
+  actions: {}
+}

+ 0 - 0
src/modules/test/transition.vue


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