Browse Source

no message

xiaoxin 4 years ago
commit
41e5df48e3
68 changed files with 22981 additions and 0 deletions
  1. 43 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. 1 0
      README.md
  8. 41 0
      build/build.js
  9. 54 0
      build/check-versions.js
  10. BIN
      build/logo.png
  11. 101 0
      build/utils.js
  12. 22 0
      build/vue-loader.conf.js
  13. 101 0
      build/webpack.base.conf.js
  14. 99 0
      build/webpack.dev.conf.js
  15. 149 0
      build/webpack.prod.conf.js
  16. 7 0
      config/dev.env.js
  17. 96 0
      config/index.js
  18. 4 0
      config/prod.env.js
  19. 7 0
      config/test.env.js
  20. BIN
      dist.zip
  21. 12 0
      index.html
  22. 17510 0
      package-lock.json
  23. 99 0
      package.json
  24. 27 0
      src/App.vue
  25. BIN
      src/assets/logo.png
  26. 2 0
      src/assets/naozhong.svg
  27. 2 0
      src/assets/shalou.svg
  28. 2 0
      src/assets/shebei.svg
  29. 2 0
      src/assets/shizhong.svg
  30. 57 0
      src/components/broadcastContent/audio.js
  31. 79 0
      src/components/broadcastContent/font.css
  32. 1019 0
      src/components/broadcastContent/index.vue
  33. 416 0
      src/components/disposeSituation/index.vue
  34. 188 0
      src/components/index/index.css
  35. 169 0
      src/components/index/index.vue
  36. 91 0
      src/components/login/login.css
  37. 169 0
      src/components/login/login.vue
  38. 343 0
      src/components/smokeWarn/index.vue
  39. 162 0
      src/components/systemSetup/index.vue
  40. 366 0
      src/components/watchBill/index.vue
  41. 238 0
      src/components/zhuanghaoguanli/zhuanghaoguanli.css
  42. 870 0
      src/components/zhuanghaoguanli/zhuanghaoguanli.vue
  43. 53 0
      src/main.js
  44. 60 0
      src/router/index.js
  45. 59 0
      src/store/index.js
  46. 0 0
      static/.gitkeep
  47. BIN
      static/images/add.png
  48. BIN
      static/images/bg.png
  49. BIN
      static/images/diwen.png
  50. BIN
      static/images/exit.png
  51. BIN
      static/images/guangbo.png
  52. BIN
      static/images/info.png
  53. BIN
      static/images/logo.png
  54. BIN
      static/images/maoyangaojing.png
  55. BIN
      static/images/mima.png
  56. BIN
      static/images/qingkuang.png
  57. BIN
      static/images/touxiang.jpg
  58. BIN
      static/images/xitongshezhi.png
  59. BIN
      static/images/yonghuguanli.png
  60. BIN
      static/images/yonghuming.png
  61. 27 0
      test/e2e/custom-assertions/elementCount.js
  62. 46 0
      test/e2e/nightwatch.conf.js
  63. 48 0
      test/e2e/runner.js
  64. 19 0
      test/e2e/specs/test.js
  65. 7 0
      test/unit/.eslintrc
  66. 30 0
      test/unit/jest.conf.js
  67. 3 0
      test/unit/setup.js
  68. 11 0
      test/unit/specs/HelloWorld.spec.js

+ 43 - 0
.babelrc

@@ -0,0 +1,43 @@
+{
+  "presets": [
+    [
+      "es2015",
+      {
+        "modules": false
+      }
+    ],
+    [
+      "env",
+      {
+        "modules": false,
+        "targets": {
+          "browsers": [
+            "> 1%",
+            "last 2 versions",
+            "not ie <= 8"
+          ]
+        }
+      }
+    ],
+    "stage-2"
+  ],
+  "plugins": [
+    "transform-vue-jsx",
+    "transform-runtime",
+    [
+      "component",
+      {
+        "libraryName": "element-ui",
+        "styleLibraryName": "theme-chalk"
+      }
+    ]
+  ],
+  "env": {
+    "test": {
+      "presets": [
+        "env",
+        "stage-2"
+      ]
+    }
+  }
+}

+ 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": {}
+  }
+}

+ 1 - 0
README.md

@@ -0,0 +1 @@
+#diseaseDataManager

+ 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)
+  }
+}

BIN
build/logo.png


+ 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'
+  }
+}

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

@@ -0,0 +1,101 @@
+'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: './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]')
+        }
+      }
+    ], loaders: [
+      {
+        test: /\.js$/,
+        exclude: /(node_modules|bower_components)/,
+        loader: 'babel',
+        query: {
+          presets: ['es2015']
+        }
+      }
+    ]
+  },
+  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'
+  }
+}

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

@@ -0,0 +1,99 @@
+'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"'
+})

+ 96 - 0
config/index.js

@@ -0,0 +1,96 @@
+'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: '/smoke/',
+    // 后端请求地址代理,配置后testIp再之后的页面调用时就直接指代 http://197.82.15.15:8088
+    proxyTable: {
+      '/bigData': {
+        target: 'http://58.17.42.179:90', // 你请求的第三方接口
+        // target: 'http://baibai.natapp1.cc', // 你请求的第三方接口
+        changeOrigin: true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
+        // pathRewrite: { // 路径重写,
+        //   '^/bigData': '' // 替换target中的请求地址,也就是说以后你在请求http://api.douban.com/v2/XXXXX这个地址的时候直接写成/api即可。
+        // }
+      },
+      '/diseaseRight': {
+        // target: 'http://192.168.161.230:8089/disease-command',
+        // target: 'http://58.17.42.179:90/diseaseRight',
+        target: 'http://chuanghai-dev.natapp1.cc/disease-command',
+        changeOrigin: true,
+        pathRewrite: {
+          '^/diseaseRight': ''
+        }
+      }
+    },
+
+    // Various Dev Server settings
+    host: 'localhost', // can be overwritten by process.env.HOST
+    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'),
+    // index: path.resolve('C:/Users/38304/Desktop/dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    // assetsRoot: path.resolve('C:/Users/38304/Desktop/dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/smoke/',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: false,
+    // 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"'
+})

BIN
dist.zip


+ 12 - 0
index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width,initial-scale=1.0">
+		<title>靖安县数据上传平台</title>
+	</head>
+	<body>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+	</body>
+</html>

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


+ 99 - 0
package.json

@@ -0,0 +1,99 @@
+{
+  "name": "manager-web",
+  "version": "1.0.0",
+  "description": "",
+  "author": "",
+  "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": {
+    "ant-design-vue": "^1.7.7",
+    "axios": "^0.21.1",
+    "echarts": "^4.9.0",
+    "element-ui": "^2.15.5",
+    "file-saver": "^2.0.5",
+    "quill": "^1.3.7",
+    "vue": "^2.5.2",
+    "vue-quill-editor": "^3.0.6",
+    "vue-router": "^3.0.1",
+    "vue2-editor": "^2.10.3",
+    "xlsx": "^0.17.5"
+  },
+  "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-component": "^1.1.1",
+    "babel-plugin-dynamic-import-node": "^1.2.0",
+    "babel-plugin-import": "^1.13.3",
+    "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-preset-env": "^1.3.2",
+    "babel-preset-es2015": "^6.24.1",
+    "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.7.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",
+    "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",
+    "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"
+  ]
+}

+ 27 - 0
src/App.vue

@@ -0,0 +1,27 @@
+<template>
+	<div id="app">
+		<router-view></router-view>
+	</div>
+</template>
+
+<script>
+	export default {
+		name: 'App'
+	}
+</script>
+
+<style>
+	html,
+	body,
+	#app {
+		padding: 0;
+		margin: 0;
+		height: calc(100vh);
+		width: 100%;
+		background-color: #eaeaea;
+	}
+	
+	a {
+		text-decoration: none;
+	}
+</style>

BIN
src/assets/logo.png


File diff suppressed because it is too large
+ 2 - 0
src/assets/naozhong.svg


File diff suppressed because it is too large
+ 2 - 0
src/assets/shalou.svg


File diff suppressed because it is too large
+ 2 - 0
src/assets/shebei.svg


File diff suppressed because it is too large
+ 2 - 0
src/assets/shizhong.svg


+ 57 - 0
src/components/broadcastContent/audio.js

@@ -0,0 +1,57 @@
+import { Quill } from 'vue-quill-editor'
+
+// 源码中是import直接倒入,这里要用Quill.import引入
+const BlockEmbed = Quill.import('blots/block/embed')
+const Link = Quill.import('formats/link')
+
+const ATTRIBUTES = ['height', 'width']
+
+class audio extends BlockEmbed {
+    static create(value) {
+        const node = super.create(value)
+        // 添加audio标签所需的属性
+        node.setAttribute('controls', 'controls')
+        node.setAttribute('type', 'audio/mp4')
+        node.setAttribute('src', this.sanitize(value))
+        return node
+    }
+
+    static formats(domNode) {
+        return ATTRIBUTES.reduce((formats, attribute) => {
+            if (domNode.hasAttribute(attribute)) {
+                formats[attribute] = domNode.getAttribute(attribute)
+            }
+            return formats
+        }, {})
+    }
+
+    static sanitize(url) {
+        return Link.sanitize(url) // eslint-disable-line import/no-named-as-default-member
+    }
+
+    static value(domNode) {
+        return domNode.getAttribute('src')
+    }
+
+    format(name, value) {
+        if (ATTRIBUTES.indexOf(name) > -1) {
+            if (value) {
+                this.domNode.setAttribute(name, value)
+            } else {
+                this.domNode.removeAttribute(name)
+            }
+        } else {
+            super.format(name, value)
+        }
+    }
+
+    html() {
+        const { audio } = this.value()
+        return `<a href="${audio}">${audio}</a>`
+    }
+}
+audio.blotName = 'audio' // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
+audio.className = 'ql-audio'
+audio.tagName = 'audio' // 用audio标签替换iframe
+
+export default audio

+ 79 - 0
src/components/broadcastContent/font.css

@@ -0,0 +1,79 @@
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
+    content: "宋体";
+    font-family: "SimSun";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
+    content: "黑体";
+    font-family: "SimHei";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
+    content: "微软雅黑";
+    font-family: "Microsoft YaHei";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
+    content: "楷体";
+    font-family: "KaiTi";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
+    content: "仿宋";
+    font-family: "FangSong";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
+    content: "Arial";
+    font-family: "Arial";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
+    content: "Times New Roman";
+    font-family: "Times New Roman";
+}
+
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
+    content: "sans-serif";
+    font-family: "sans-serif";
+}
+
+.ql-font-SimSun {
+    font-family: "SimSun";
+}
+
+.ql-font-SimHei {
+    font-family: "SimHei";
+}
+
+.ql-font-Microsoft-YaHei {
+    font-family: "Microsoft YaHei";
+}
+
+.ql-font-KaiTi {
+    font-family: "KaiTi";
+}
+
+.ql-font-FangSong {
+    font-family: "FangSong";
+}
+
+.ql-font-Arial {
+    font-family: "Arial";
+}
+
+.ql-font-Times-New-Roman {
+    font-family: "Times New Roman";
+}
+
+.ql-font-sans-serif {
+    font-family: "sans-serif";
+}

File diff suppressed because it is too large
+ 1019 - 0
src/components/broadcastContent/index.vue


+ 416 - 0
src/components/disposeSituation/index.vue

@@ -0,0 +1,416 @@
+<template>
+  <div>
+    <!-- 标题 -->
+    <el-row>
+      <el-col
+        :span="24"
+        class="first-row"
+      >
+        <div class="tag">处置情况</div>
+      </el-col>
+    </el-row>
+
+    <!-- 分割线 -->
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+
+    <div style="margin: 32px 0;"></div>
+
+    <el-row>
+      <el-col :span="3">
+        1、扑救火灾
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible1"
+          v-model="inputValue1"
+          ref="saveTagInput1"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(1)"
+          @blur="handleInputConfirm(1)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(1)"
+        >{{inputValue1}}</el-button>
+        次
+      </el-col>
+      <el-col :span="3">
+        5、维护社会治安
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible5"
+          v-model="inputValue5"
+          ref="saveTagInput5"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(5)"
+          @blur="handleInputConfirm(5)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(5)"
+        >{{inputValue5}}</el-button>
+        次
+      </el-col>
+    </el-row>
+
+    <div style="margin: 32px 0;"></div>
+
+    <el-row>
+      <el-col :span="3">
+        2、转移安置人员
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible2"
+          v-model="inputValue2"
+          ref="saveTagInput2"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(2)"
+          @blur="handleInputConfirm(2)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(2)"
+        >{{inputValue2}}</el-button>
+        次
+      </el-col>
+      <el-col :span="3">
+        6、发布信息
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible6"
+          v-model="inputValue6"
+          ref="saveTagInput6"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(6)"
+          @blur="handleInputConfirm(6)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(6)"
+        >{{inputValue6}}</el-button>
+        次
+      </el-col>
+    </el-row>
+
+    <div style="margin: 32px 0;"></div>
+
+    <el-row>
+      <el-col :span="3">
+        3、救治伤员
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible3"
+          v-model="inputValue3"
+          ref="saveTagInput3"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(3)"
+          @blur="handleInputConfirm(3)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(3)"
+        >{{inputValue3}}</el-button>
+        次
+      </el-col>
+      <el-col :span="3">
+        7、善后处置
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible7"
+          v-model="inputValue7"
+          ref="saveTagInput7"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(7)"
+          @blur="handleInputConfirm(7)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(7)"
+        >{{inputValue7}}</el-button>
+        次
+      </el-col>
+    </el-row>
+
+    <div style="margin: 32px 0;"></div>
+
+    <el-row>
+      <el-col :span="3">
+        4、保护重要目标
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible4"
+          v-model="inputValue4"
+          ref="saveTagInput4"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(4)"
+          @blur="handleInputConfirm(4)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(4)"
+        >{{inputValue4}}</el-button>
+        次
+      </el-col>
+      <el-col :span="3">
+        8、火场清理
+      </el-col>
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible8"
+          v-model="inputValue8"
+          ref="saveTagInput8"
+          size="small"
+          @keyup.enter.native="handleInputConfirm(8)"
+          @blur="handleInputConfirm(8)"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput(8)"
+        >{{inputValue8}}</el-button>
+        次
+      </el-col>
+    </el-row>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: "",
+  data() {
+    return {
+      // 控制输入框和按钮框切换
+      inputVisible1: false,
+      inputVisible2: false,
+      inputVisible3: false,
+      inputVisible4: false,
+      inputVisible5: false,
+      inputVisible6: false,
+      inputVisible7: false,
+      inputVisible8: false,
+      // 按钮框绑定的数据
+
+      inputValue1: "", // 扑灭火灾次数
+      inputValue2: "", // 转移安置人员人数
+      inputValue3: "", // 救治伤员人数
+      inputValue4: "", // 保护重要目标次数
+      inputValue5: "", // 维护社会治安次数
+      inputValue6: "", // 发布信息次数
+      inputValue7: "", // 善后处置次数
+      inputValue8: "", // 火场清理次数
+      inputValue9: "", // 冒烟警告次数
+    };
+  },
+  mounted() {
+    this.getDisposeData();
+  },
+  methods: {
+    // 点击按钮 切换输入框 并聚焦
+    showInput(i) {
+      if (i == 1) {
+        this.inputVisible1 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput1.$refs.input.focus();
+        });
+      } else if (i == 2) {
+        this.inputVisible2 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput2.$refs.input.focus();
+        });
+      } else if (i == 3) {
+        this.inputVisible3 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput3.$refs.input.focus();
+        });
+      } else if (i == 4) {
+        this.inputVisible4 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput4.$refs.input.focus();
+        });
+      } else if (i == 5) {
+        this.inputVisible5 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput5.$refs.input.focus();
+        });
+      } else if (i == 6) {
+        this.inputVisible6 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput6.$refs.input.focus();
+        });
+      } else if (i == 7) {
+        this.inputVisible7 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput7.$refs.input.focus();
+        });
+      } else {
+        this.inputVisible8 = true;
+        this.$nextTick(() => {
+          this.$refs.saveTagInput8.$refs.input.focus();
+        });
+      }
+    },
+    // 失去焦点或者回车 切换回按钮框 并更新数据
+    handleInputConfirm(i) {
+      if (i == 1) {
+        this.inputVisible1 = false;
+        this.inputValue1 = this.inputValue1;
+      } else if (i == 2) {
+        this.inputVisible2 = false;
+        this.inputValue2 = this.inputValue2;
+      } else if (i == 3) {
+        this.inputVisible3 = false;
+        this.inputValue3 = this.inputValue3;
+      } else if (i == 4) {
+        this.inputVisible4 = false;
+        this.inputValue4 = this.inputValue4;
+      } else if (i == 5) {
+        this.inputVisible5 = false;
+        this.inputValue5 = this.inputValue5;
+      } else if (i == 6) {
+        this.inputVisible6 = false;
+        this.inputValue6 = this.inputValue6;
+      } else if (i == 7) {
+        this.inputVisible7 = false;
+        this.inputValue7 = this.inputValue7;
+      } else {
+        this.inputVisible8 = false;
+        this.inputValue8 = this.inputValue8;
+      }
+      this.reDisposeData();
+    },
+    // 获取应急处置信息
+    async getDisposeData() {
+      let res = await this.$axios({
+        url: "/diseaseRight/emergencyDisposalStatistics/info",
+        method: "get",
+        // headers: {
+        //   "Admin-Token": sessionStorage.getItem("token"),
+        // },
+      });
+      // console.log(res);
+      if (res.status == 200) {
+        this.inputValue1 = res.data.data.numOfPutOutTheFire;
+        this.inputValue2 = res.data.data.numOfTransfer;
+        this.inputValue3 = res.data.data.numOfCure;
+        this.inputValue4 = res.data.data.numOfImportantTarget;
+        this.inputValue5 = res.data.data.numOfPublicSecurity;
+        this.inputValue6 = res.data.data.numOfInfo;
+        this.inputValue7 = res.data.data.numOfAftermath;
+        this.inputValue8 = res.data.data.numOfFireClean;
+        this.inputValue9 = res.data.data.numOfSmoke;
+      } else {
+        console.log("获取 应急处置信息 请求失败");
+      }
+    },
+    // 修改应急处置信息
+    async reDisposeData() {
+      let data = {
+        id: 1,
+        numOfPutOutTheFire: this.inputValue1,
+        numOfTransfer: this.inputValue2,
+        numOfCure: this.inputValue3,
+        numOfImportantTarget: this.inputValue4,
+        numOfPublicSecurity: this.inputValue5,
+        numOfInfo: this.inputValue6,
+        numOfAftermath: this.inputValue7,
+        numOfFireClean: this.inputValue8,
+        numOfSmoke: this.inputValue9,
+      };
+      let res = await this.$axios({
+        url: "/diseaseRight/emergencyDisposalStatistics/update",
+        method: "put",
+        data: data,
+        headers: {
+          "Admin-Token": sessionStorage.getItem("token"),
+        },
+      });
+      // console.log(res);
+      if (res.status == 200 && res.data.success) {
+        this.$message({
+          showClose: true,
+          message: "修改成功",
+          type: "success",
+        });
+      } else {
+        this.$message({
+          showClose: true,
+          message: "修改失败",
+          type: "error",
+        });
+        this.getDisposeData();
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.first-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  height: 50px;
+}
+.tag {
+  width: 108px;
+  height: 40px;
+  font-size: 24px;
+  font-family: Microsoft YaHei-3970(82674968);
+  font-weight: bold;
+  color: #000000;
+  line-height: 54px;
+}
+
+.input-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  vertical-align: middle;
+}
+.button-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  height: 32px;
+  line-height: 30px;
+  padding-top: 0;
+  padding-bottom: 0;
+  vertical-align: middle;
+}
+</style>

+ 188 - 0
src/components/index/index.css

@@ -0,0 +1,188 @@
+/* menu */
+.container {
+	width: 100%;
+	height: 100%;
+}
+
+.menu-left {
+	width: 340px;
+	height: 850px;
+	background: #1D3251;
+}
+
+.logo-col,
+.title-col {
+	display: flex;
+	justify-content: center;
+}
+
+.logo {
+	margin-top: 34px;
+	width: 84px;
+	height: 84px;
+	background: url(../../../static/images/logo.png) 0 0 no-repeat;
+	background-size: 84px 84px;
+	opacity: 0;
+}
+
+.title {
+	margin-top: 26px;
+	width: 268px;
+	height: 34px;
+	line-height: 34px;
+	font-size: 20px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: bold;
+	color: #FFFFFF;
+}
+
+.menu-row {
+	margin-top: 39px;
+	width: 341px;
+}
+
+.el-menu-item {
+	padding-left: 50px !important;
+	height: 60px;
+}
+
+.el-menu-item:hover {
+	background: #134a7a !important;
+}
+
+.el-menu-item.is-active {
+	background: #298DEF !important;
+}
+
+.el-menu-item span {
+	height: 20px;
+	padding-left: 6px;
+	font-size: 22px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #FFFFFF;
+	line-height: 54px;
+}
+
+.el-menu-item .el-icon-zhgl {
+	width: 27px;
+	height: 27px;
+	background: url(../../../static/images/yonghuguanli.png) center center no-repeat;
+	background-size: cover;
+}
+
+.el-menu-item .el-icon-gb {
+	width: 27px;
+	height: 27px;
+	background: url(../../../static/images/guangbo.png) center center no-repeat;
+	background-size: cover;
+}
+
+.el-menu-item .el-icon-qk {
+	width: 27px;
+	height: 27px;
+	background: url(../../../static/images/qingkuang.png) center center no-repeat;
+	background-size: cover;
+}
+
+.el-menu-item .el-icon-mygj {
+	width: 27px;
+	height: 27px;
+	background: url(../../../static/images/maoyangaojing.png) center center no-repeat;
+	background-size: cover;
+}
+
+.el-menu-item .el-icon-xtsz {
+	width: 27px;
+	height: 27px;
+	background: url(../../../static/images/xitongshezhi.png) center center no-repeat;
+	background-size: cover;
+}
+
+.diwen {
+	margin: 30px 0 0 76px;
+	width: 176px;
+	height: 145px;
+	background: url(../../../static/images/diwen.png) no-repeat;
+	background-size: 176px 145px;
+}
+
+/* menu */
+
+/* top */
+.right-top {
+	display: flex;
+	justify-content: space-between;
+	width: 100%;
+	height: 90px;
+	line-height: 90px;
+	background: #FFFFFF;
+}
+
+.wecome-text {
+	width: 300px;
+	height: 90px;
+	line-height: 90px;
+	font-size: 16px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #333333;
+	padding-left: 34px;
+}
+
+.right-items {
+	display: flex;
+	justify-content: flex-end;
+	align-items: center;
+	padding-right: 51px;
+}
+
+.tx {
+	width: 54px;
+	height: 54px;
+	border-radius: 50%;
+	background: url(../../../static/images/touxiang.jpg) no-repeat;
+	background-size: 54px 54px;
+	border: 1px solid #EEEEEE;
+	cursor: pointer;
+}
+
+.shuxian {
+	width: 1px;
+	height: 36px;
+	background: #CCCCCC;
+	margin: 0 21px 0 23px;
+}
+
+.user-name {
+	height: 54px;
+	line-height: 54px;
+	font-size: 20px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #333333;
+	margin-left: 18px;
+	text-align: center;
+	white-space: nowrap;
+}
+
+.logout-btn {
+	width: 50px;
+	height: 90px;
+	background: url(../../../static/images/exit.png) center center no-repeat;
+	background-size: 29px 29px;
+	cursor: pointer;
+}
+
+/* top */
+
+/* main */
+.right-main {
+	margin: 20px 1px 0 20px;
+	padding: 20px 50px 28px 50px;
+	width: calc(100% - 40px);
+	height: 740px;
+	background: #FFFFFF;
+}
+
+/* main */

+ 169 - 0
src/components/index/index.vue

@@ -0,0 +1,169 @@
+<template>
+  <el-row
+    type="flex"
+    class="container"
+    justify="space-between"
+  >
+    <el-col
+      :span="4"
+      class="menu-left"
+    >
+      <el-row>
+        <el-col
+          :span="24"
+          class="logo-col"
+        >
+          <div class="logo"></div>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col
+          :span="24"
+          class="title-col"
+        >
+          <div class="title">旅游大数据上传后台</div>
+        </el-col>
+      </el-row>
+      <el-row class="menu-row">
+        <el-col :span="24">
+          <el-menu
+            router
+            :default-active="$route.path"
+            class="el-menu-vertical-demo"
+            background-color="#1D3251"
+            text-color="#fff"
+            active-text-color="#fff"
+          >
+            <router-link to="watchBill">
+              <el-menu-item index="/smoke/index/watchBill">
+                <i class="el-icon-zhgl"></i>
+                <span slot="title">值班表</span>
+              </el-menu-item>
+            </router-link>
+
+            <router-link to="disposeSituation">
+              <el-menu-item index="/smoke/index/disposeSituation">
+                <i class="el-icon-qk"></i>
+                <span slot="title">处置情况</span>
+              </el-menu-item>
+            </router-link>
+
+            <router-link to="smokeWarn">
+              <el-menu-item index="/smoke/index/smokeWarn">
+                <i class="el-icon-mygj"></i>
+                <span slot="title">冒烟告警</span>
+              </el-menu-item>
+            </router-link>
+
+            <router-link to="systemSetup">
+              <el-menu-item index="/smoke/index/systemSetup">
+                <i class="el-icon-xtsz"></i>
+                <span slot="title">系统设置</span>
+              </el-menu-item>
+            </router-link>
+
+            <router-link to="broadcastContent">
+              <el-menu-item index="/smoke/index/broadcastContent">
+                <i class="el-icon-gb"></i>
+                <span slot="title">广播内容</span>
+              </el-menu-item>
+            </router-link>
+
+          </el-menu>
+        </el-col>
+      </el-row>
+      <div class="diwen"></div>
+    </el-col>
+    <el-col :span="20">
+      <el-row>
+        <el-col
+          :span="24"
+          class="right-top"
+        >
+          <div class="wecome-text">{{ userName }} 您好,欢迎登陆!</div>
+          <div class="right-items">
+            <div class="tx"></div>
+            <!-- <div class="user-name">我是【{{unic}}】</div> -->
+            <div class="shuxian"></div>
+            <div
+              class="logout-btn"
+              @click="logout"
+            ></div>
+          </div>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col
+          :span="24"
+          class="right-main"
+        >
+          <router-view @sonFun="indexFun"></router-view>
+        </el-col>
+      </el-row>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      userName: "",
+      unic: "",
+    };
+  },
+  created() {
+    var statue = sessionStorage.getItem("uname");
+    var token = sessionStorage.getItem("token");
+    var unic = sessionStorage.getItem("unic");
+    if (
+      !statue ||
+      typeof statue == "undefined" ||
+      statue == "" ||
+      statue == "null" ||
+      !token ||
+      typeof token == "undefined" ||
+      token == "" ||
+      token == "null"
+    ) {
+      this.$router.replace("/");
+      return;
+    } else {
+      this.userName = statue;
+      this.unic = unic;
+    }
+  },
+  methods: {
+    // 通过子组件更新昵称
+    indexFun(param) {
+      if (typeof param == "undefined" || !param) {
+        this.unic = sessionStorage.getItem("unic");
+      } else {
+        this.unic = param;
+      }
+    },
+    // 退出登陆
+    logout() {
+      var _this = this;
+      this.$confirm("确认退出?", "提示", {
+        confirmButtonText: "是,退出",
+        cancelButtonText: "否,不退出",
+        type: "warning",
+      })
+        .then((_) => {
+          // console.log(_);
+          sessionStorage.removeItem("uname");
+          sessionStorage.removeItem("token");
+          _this.$router.replace("/");
+        })
+        .catch((_) => {
+          // console.log(_);
+        });
+    },
+  },
+};
+</script>
+
+<style scoped>
+@import url("index.css");
+</style>

+ 91 - 0
src/components/login/login.css

@@ -0,0 +1,91 @@
+html,
+body,
+.container {
+	width: 100vw;
+	height: 100vh;
+	min-width: 1780px;
+	min-height: 900px;
+	background: url(../../../static/images/bg.png) no-repeat;
+	background-size: 100% 100%;
+	margin-bottom: 0;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+}
+
+#login_form {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	width: 600px;
+}
+
+#logo {
+	width: 95px;
+	height: 95px;
+	background: url(../../../static/images/logo.png) 0 0 no-repeat;
+	background-size: 95px 95px;
+	opacity: 0;
+}
+
+#title {
+	width: 546px;
+	height: 42px;
+	line-height: 42px;
+	font-size: 38px;
+	font-family: Microsoft YaHei;
+	font-weight: bold;
+	color: #3CADFD;
+	text-align: center;
+	margin-top: 19px;
+	background: linear-gradient(180deg, #4393F8 0%, #535EF0 100%);
+	-webkit-background-clip: text;
+	-webkit-text-fill-color: transparent;
+}
+
+.demo-ruleForm {
+	margin-top: 40px;
+	width: 400px;
+}
+
+.el-form-item {
+	margin-top: 30px;
+}
+
+>>> .el-form-item input {
+	padding-left: 60px;
+	height: 58px;
+	background: #F6F7FB;
+	border-radius: 29px;
+	font-size: 18px;
+}
+
+.el-form-item>>>.el-icon-login-user{
+	margin-left: 22px;
+    background: url(../../../static/images/yonghuming.png) center no-repeat;
+    background-size: contain;
+}
+
+.el-form-item>>>.el-icon-login-pass{
+	margin-left: 22px;
+    background: url(../../../static/images/mima.png) center no-repeat;
+    background-size: contain;
+}
+
+>>>.el-form-item button {
+	margin-top: 32px;
+	width: 400px;
+	height: 58px;
+	background: linear-gradient(180deg, #4393F8, #535EF0);
+	box-shadow: 1px 4px 16px 0px rgba(84, 136, 254, 0.25);
+	border-radius: 29px;
+	font-size: 24px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #FFFFFF;
+}
+
+>>> .el-form-item__error {
+	padding-left: 60px;
+}

+ 169 - 0
src/components/login/login.vue

@@ -0,0 +1,169 @@
+<template>
+  <div class="container">
+    <el-row>
+      <el-col :span="13">
+        &nbsp;
+      </el-col>
+      <el-col :span="11">
+        <div id="login_form">
+          <div id="logo"></div>
+          <div id="title">旅游大数据上传后台</div>
+          <el-form
+            label-width="0px"
+            :model="ruleForm"
+            status-icon
+            :rules="rules"
+            ref="ruleForm"
+            class="demo-ruleForm"
+          >
+            <el-form-item prop="uname">
+              <el-input
+                ref="zhanghao"
+                placeholder="请输入账号"
+                maxlength="16"
+                v-model="ruleForm.uname"
+                prefix-icon="el-icon-login-user"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="upass">
+              <el-input
+                ref="mima"
+                placeholder="请输入密码"
+                maxlength="16"
+                type="password"
+                v-model="ruleForm.upass"
+                prefix-icon="el-icon-login-pass"
+                @keyup.enter.native="submitForm('ruleForm')"
+              >
+              </el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button
+                type="primary"
+                @click="submitForm('ruleForm')"
+              >登 陆</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    var checkName = (rule, value, callback) => {
+      // console.log(rule, value, callback);
+      if (!value) {
+        return callback(new Error("请输入账号"));
+      }
+      setTimeout(() => {
+        if (value.length < 5) {
+          callback(new Error("账号长度不小于5位"));
+        } else {
+          callback();
+        }
+      }, 30);
+    };
+    var validatePass = (rule, value, callback) => {
+      // console.log(rule, value, callback);
+      if (value === "") {
+        callback(new Error("请输入密码"));
+      }
+      setTimeout(() => {
+        if (value.length < 5) {
+          callback(new Error("密码长度不小于5位"));
+        } else {
+          callback();
+        }
+      }, 30);
+    };
+    return {
+      ruleForm: {
+        uname: "",
+        upass: "",
+      },
+      rules: {
+        uname: [
+          {
+            validator: checkName,
+            trigger: "blur",
+          },
+        ],
+        upass: [
+          {
+            validator: validatePass,
+            trigger: "blur",
+          },
+        ],
+      },
+    };
+  },
+  mounted() {
+    this.$refs["zhanghao"].focus();
+  },
+  methods: {
+    submitForm(formName) {
+      let _this = this;
+      let params = {};
+      var username = String(_this.ruleForm.uname).trim();
+      var userpass = String(_this.ruleForm.upass).trim();
+
+      if (username && userpass) {
+        params.userName = username;
+        params.password = userpass;
+      } else {
+        _this.$message.warning("请输入账号和密码!");
+        if (!username) _this.$refs["zhanghao"].focus();
+        else _this.$refs["mima"].focus();
+        return;
+      }
+
+      _this.$refs[formName].validate((valid) => {
+        _this
+          .$axios({
+            method: "post",
+            url:
+              "/diseaseRight/adminInfo/login?userName=" +
+              username +
+              "&password=" +
+              userpass,
+            headers: {
+              "Content-type": "application/x-www-form-urlencoded;charset=utf-8",
+            },
+          })
+          .then((res) => {
+            // console.log(res.data);
+            if (res.data.success) {
+              _this.$message.success(res.data.message);
+              sessionStorage.setItem("uname", username);
+              sessionStorage.setItem("userType", res.data.data.userType);
+              sessionStorage.setItem("token", res.data.data.token);
+              // if (typeof(res.data.name) == 'undefined') {
+              // 	sessionStorage.setItem('unic', '未设置昵称')
+              // } else {
+              // 	sessionStorage.setItem('unic', res.data.name)
+              // }
+              _this.$router.replace("/smoke/index"); //跳转到首页
+            } else {
+              _this.$message({
+                showClose: true,
+                message: "登陆异常:" + res.data.message,
+                type: "error",
+              });
+            }
+          })
+          .catch((err) => {
+            // console.log(err);
+            _this.$message.error("捕捉异常:" + err);
+          });
+      });
+    },
+  },
+};
+</script>
+
+<style scoped>
+@import url("./login.css");
+</style>

+ 343 - 0
src/components/smokeWarn/index.vue

@@ -0,0 +1,343 @@
+<template>
+  <div>
+    <!-- 标题 -->
+    <el-row>
+      <el-col
+        :span="24"
+        class="first-row"
+      >
+        <div class="tag">冒烟告警</div>
+      </el-col>
+    </el-row>
+
+    <!-- 分割线 -->
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+    <div style="margin: 20px 0;"></div>
+
+    <!-- 时间选择框 -->
+    <el-row>
+      <el-col :span="8">
+        <el-date-picker
+          v-model="select_datetime"
+          type="daterange"
+          align="center"
+          unlink-panels
+          range-separator="至"
+          :start-placeholder="startTime"
+          :end-placeholder="endTime"
+          @change="dateChange"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          :picker-options="pickerOptions"
+          class="shijian_plus"
+        >
+        </el-date-picker>
+      </el-col>
+      <el-col :span="2">
+        <div class="smoke_title">次数统计:</div>
+      </el-col>
+      <el-col :span="4">
+        <div class="smoke_num">{{totalCount}}次</div>
+      </el-col>
+    </el-row>
+
+    <div style="margin: 20px 0;"></div>
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+
+    <!-- 表格区域 -->
+    <el-row>
+      <el-col
+        :span="24"
+        class="third-row"
+      >
+
+        <el-table
+          :data="tableData"
+          style="width: 100%;font-size: 14px;"
+          highlight-current-row
+          height="450"
+        >
+          <el-table-column
+            prop="publishTime"
+            label="发现时间"
+            align="center"
+          >
+          </el-table-column>
+          <el-table-column
+            prop="publishTime"
+            label="推送时间"
+            align="center"
+          >
+          </el-table-column>
+
+          <el-table-column
+            prop="pushRecords"
+            label="推送人员"
+            align="center"
+            :formatter="peopleData"
+          >
+          </el-table-column>
+          <el-table-column
+            prop="place"
+            label="发现地点"
+            align="center"
+          >
+          </el-table-column>
+          <el-table-column
+            label="回传图片"
+            align="center"
+          >
+            <template slot-scope="{ row }">
+              <img
+                style="width: 82px; height: 60px"
+                :src="row.images"
+              />
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            label="操作详情"
+            align="center"
+          >
+            <template slot-scope="{ row }">
+              <div v-if="row.handleStatus==='1'">
+                <el-button
+                  type="danger"
+                  @click="handleWarn(row.id)"
+                >未处理</el-button>
+              </div>
+              <div v-else>
+                <div>{{row.handleDesc}}</div>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-col>
+    </el-row>
+
+    <div style="margin: 20px 0;"></div>
+
+    <!-- 分页器 -->
+    <el-row>
+      <div class="pagination_smoke">
+        <el-pagination
+          background
+          layout="total,prev, pager, next"
+          :total="totalCount"
+          :page-size="pageSize"
+          :current-page.sync="curPage"
+          @current-change="changeCurPage"
+        >
+        </el-pagination>
+      </div>
+
+    </el-row>
+
+    <!-- 处理警告弹窗 -->
+    <el-dialog
+      title="处置操作"
+      :visible.sync="dialogVisible"
+      width="30%"
+    >
+      <el-input
+        type="textarea"
+        :rows="5"
+        placeholder="请输入内容"
+        v-model="textarea"
+      >
+      </el-input>
+
+      <div style="margin: 20px 0;"></div>
+
+      <div class="button_save">
+        <el-button
+          type="primary"
+          @click="handleSave"
+        >保存</el-button>
+      </div>
+
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "",
+  data() {
+    return {
+      select_datetime: [], // 选择的时间范围
+      startTime: "",
+      endTime: "",
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+        ],
+      },
+      curPage: 1, // 当前页
+      pageSize: 4, // 每页记录数
+      totalCount: null, // 总条数
+      tableData: [], // 列表
+      dialogVisible: false,
+      textarea: "", // 处理内容
+      warningId: "",
+    };
+  },
+  mounted() {
+    this.getSmokeRecord();
+  },
+  methods: {
+    // 换页逻辑
+    changeCurPage(val) {
+      this.handleCurrentChange(val);
+    },
+    // 更改时间逻辑
+    dateChange(val) {
+      if (val) {
+        this.startTime = val[0];
+        this.endTime = val[1];
+        this.handleCurrentChange(1);
+      } else {
+        this.startTime = "";
+        this.endTime = "";
+        this.handleCurrentChange(1);
+      }
+    },
+    // 整理参数
+    handleCurrentChange(val) {
+      this.curPage = val;
+      this.getSmokeRecord();
+    },
+    // 处理表格数据
+    peopleData(row) {
+      let peopleArr = [];
+      row.pushRecords.forEach((item) => {
+        peopleArr.push(item.dutyName + "   ");
+      });
+      return peopleArr;
+    },
+
+    // 获取 冒烟警告记录列表
+    async getSmokeRecord() {
+      let params = {
+        curPage: this.curPage,
+        pageSize: this.pageSize,
+        startTime: this.startTime,
+        endTime: this.endTime,
+      };
+      let res = await this.$axios({
+        url: "/diseaseRight/warningOfSmokeRecord/list",
+        method: "get",
+        params: params,
+      });
+      // console.log(res);
+      if (res.status == 200) {
+        this.tableData = res.data.data.list;
+        this.totalCount = res.data.data.totalCount;
+      } else {
+        console.log("获取 冒烟警告记录列表 请求失败");
+      }
+    },
+    // 处理警告
+    handleWarn(id) {
+      this.warningId = id;
+      this.dialogVisible = true;
+    },
+    // 处理保存
+    async handleSave() {
+      let data = {
+        warningId: this.warningId,
+        handleDesc: this.textarea,
+      };
+      this.dialogVisible = false;
+      let res = await this.$axios({
+        url: "/diseaseRight/warningOfSmokeRecord/handle",
+        method: "put",
+        data,
+        headers: {
+          "Admin-Token": sessionStorage.getItem("token"),
+        },
+      });
+      // console.log(res);
+      if (res.status == 200 && res.data.success) {
+        this.$message({
+          showClose: true,
+          message: "处置成功",
+          type: "success",
+        });
+      } else {
+        this.$message({
+          showClose: true,
+          message: "处置失败",
+          type: "error",
+        });
+      }
+      this.handleCurrentChange(this.curPage);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.first-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  height: 50px;
+}
+.tag {
+  width: 108px;
+  height: 40px;
+  font-size: 24px;
+  font-family: Microsoft YaHei-3970(82674968);
+  font-weight: bold;
+  color: #000000;
+  line-height: 54px;
+}
+.shijian_plus.el-date-editor--daterange {
+  width: 250px;
+}
+.smoke_title {
+  color: #2784c1;
+  line-height: 200%;
+}
+.smoke_num {
+  line-height: 200%;
+}
+.third-row {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 0;
+}
+.button_save {
+  margin-left: 40%;
+}
+.pagination_smoke {
+  text-align: center;
+}
+</style>

+ 162 - 0
src/components/systemSetup/index.vue

@@ -0,0 +1,162 @@
+<template>
+  <div>
+    <!-- 标题 -->
+    <el-row>
+      <el-col
+        :span="24"
+        class="first-row"
+      >
+        <div class="tag">系统设置</div>
+      </el-col>
+    </el-row>
+
+    <!-- 分割线 -->
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+    <div style="margin: 20px 0;"></div>
+
+    <!-- 内容区域 -->
+    <el-row>
+      <el-col :span="5">
+        管理端推送时间间隔
+      </el-col>
+
+      <el-col :span="6">
+        <el-input
+          class="input-new-tag"
+          v-if="inputVisible"
+          v-model="inputValue"
+          ref="saveTagInput"
+          size="small"
+          @keyup.enter.native="handleInputConfirm"
+          @blur="handleInputConfirm"
+        >
+        </el-input>
+        <el-button
+          v-else
+          class="button-new-tag"
+          size="small"
+          @click="showInput"
+        >{{inputValue}}</el-button>
+        秒
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "",
+  data() {
+    return {
+      inputVisible: false,
+      inputValue: "",
+    };
+  },
+  methods: {
+    // 失去焦点或者回车 切换回按钮框 并更新数据
+    handleInputConfirm() {
+      this.inputVisible = false;
+      this.inputValue = this.inputValue;
+      // this.reDisposeData();
+    },
+    // 点击按钮 切换输入框 并聚焦
+    showInput() {
+      this.inputVisible = true;
+      this.$nextTick(() => {
+        this.$refs.saveTagInput.$refs.input.focus();
+      });
+    },
+    // 获取应急处置信息
+    async getDisposeData() {
+      // let res = await this.$axios({
+      //   url: "/diseaseRight/emergencyDisposalStatistics/info",
+      //   method: "get",
+      //   headers: {
+      //     "Admin-Token": sessionStorage.getItem("token"),
+      //   },
+      // });
+      // console.log(res);
+      // if (res.status == 200) {
+      //   this.inputValue1 = res.data.data.numOfPutOutTheFire;
+      //   this.inputValue2 = res.data.data.numOfTransfer;
+      //   this.inputValue3 = res.data.data.numOfCure;
+      //   this.inputValue4 = res.data.data.numOfImportantTarget;
+      //   this.inputValue5 = res.data.data.numOfPublicSecurity;
+      //   this.inputValue6 = res.data.data.numOfInfo;
+      //   this.inputValue7 = res.data.data.numOfAftermath;
+      //   this.inputValue8 = res.data.data.numOfFireClean;
+      //   this.inputValue9 = res.data.data.numOfSmoke;
+      // } else {
+      //   console.log("获取 应急处置信息 请求失败");
+      // }
+    },
+    // 修改应急处置信息
+    async reDisposeData() {
+      // let data = {
+      //   id: 1,
+      //   numOfPutOutTheFire: this.inputValue1,
+      //   numOfTransfer: this.inputValue2,
+      //   numOfCure: this.inputValue3,
+      //   numOfImportantTarget: this.inputValue4,
+      //   numOfPublicSecurity: this.inputValue5,
+      //   numOfInfo: this.inputValue6,
+      //   numOfAftermath: this.inputValue7,
+      //   numOfFireClean: this.inputValue8,
+      //   numOfSmoke: this.inputValue9,
+      // };
+      // let res = await this.$axios({
+      //   url: "/diseaseRight/emergencyDisposalStatistics/update",
+      //   method: "put",
+      //   data: data,
+      //   headers: {
+      //     "Admin-Token": sessionStorage.getItem("token"),
+      //   },
+      // });
+      // console.log(res);
+      // if (res.data.success) {
+      //   this.$message({
+      //     showClose: true,
+      //     message: "修改成功",
+      //     type: "success",
+      //   });
+      // } else {
+      //   this.$message({
+      //     showClose: true,
+      //     message: "修改失败",
+      //     type: "error",
+      //   });
+      //   this.getDisposeData();
+      // }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.first-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  height: 50px;
+}
+.tag {
+  width: 108px;
+  height: 40px;
+  font-size: 24px;
+  font-family: Microsoft YaHei-3970(82674968);
+  font-weight: bold;
+  color: #000000;
+  line-height: 54px;
+}
+.input-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  vertical-align: middle;
+}
+.button-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  height: 32px;
+  vertical-align: middle;
+}
+</style>

+ 366 - 0
src/components/watchBill/index.vue

@@ -0,0 +1,366 @@
+<template>
+  <div>
+    <!-- 标题 -->
+    <el-row>
+      <el-col
+        :span="24"
+        class="first-row"
+      >
+        <div class="tag">值班表</div>
+      </el-col>
+    </el-row>
+
+    <!-- 分割线 -->
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+    <div style="margin: 20px 0;"></div>
+
+    <!--下拉选择框 添加按钮 -->
+    <el-row>
+      <el-col :span="6">
+        <el-select
+          v-model="watchValue"
+          @change="changeWatchPeople"
+        >
+          <el-option
+            v-for="item in options"
+            :key="item.id"
+            :label="item.value"
+            :value="item.id"
+          >
+          </el-option>
+        </el-select>
+      </el-col>
+      <el-col :span="6">
+        <el-button
+          type="primary"
+          @click="dialogFormVisible = true;form = {};"
+        >添加值班人员</el-button>
+      </el-col>
+    </el-row>
+
+    <div style="margin: 20px 0;"></div>
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+
+    <!-- 表格区域 -->
+    <el-row>
+      <el-table
+        :data="tableData"
+        style="width: 100%;font-size: 14px;"
+        highlight-current-row
+        height="520"
+      >
+        <el-table-column
+          prop="dutyName"
+          label="姓名"
+          align="center"
+        >
+        </el-table-column>
+        <el-table-column
+          prop="dutyDept"
+          label="部门"
+          align="center"
+        >
+        </el-table-column>
+        <el-table-column
+          prop="dutyPhone"
+          label="电话"
+          align="center"
+        >
+        </el-table-column>
+        <el-table-column
+          label="操作"
+          align="center"
+        >
+          <template slot-scope="{ row }">
+            <el-button
+              type="warning"
+              size="mini"
+              @click="amendWatchPeople(row)"
+            >修改</el-button>
+            <el-button
+              type="danger"
+              size="mini"
+              @click="deleteWatchPeople(row.id)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-row>
+
+    <!-- 新增值班人员弹窗 -->
+    <el-dialog
+      title="新增值班人员"
+      :visible.sync="dialogFormVisible"
+      center
+      style="width:90%"
+    >
+      <el-form
+        :model="form"
+        :rules="rules"
+        ref="form"
+        label-width="80px"
+      >
+        <el-form-item
+          label="值班时间"
+          prop="todayWork"
+        >
+          <el-select v-model="form.todayWork">
+            <el-option
+              v-for="item in options"
+              :key="item.id"
+              :label="item.value"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item
+          label="姓名"
+          prop="dutyName"
+        >
+          <el-input v-model="form.dutyName"></el-input>
+        </el-form-item>
+
+        <el-form-item
+          label="部门"
+          prop="dutyDept"
+        >
+          <el-input v-model="form.dutyDept"></el-input>
+        </el-form-item>
+
+        <el-form-item
+          label="电话"
+          prop="dutyPhone"
+        >
+          <el-input v-model="form.dutyPhone"></el-input>
+        </el-form-item>
+
+      </el-form>
+      <div
+        slot="footer"
+        class="dialog-footer"
+      >
+        <el-button
+          type="primary"
+          @click="addWatchPeople('form')"
+        >确 定</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 修改值班人员弹窗 -->
+    <el-dialog
+      title="修改值班人员"
+      :visible.sync="dialogFormVisible2"
+      center
+      style="width:90%"
+    >
+      <el-form
+        :model="form"
+        label-width="80px"
+      >
+        <el-form-item label="值班时间">
+          <el-select v-model="form.todayWork">
+            <el-option
+              v-for="item in options"
+              :key="item.id"
+              :label="item.value"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="姓名">
+          <el-input v-model="form.dutyName"></el-input>
+        </el-form-item>
+
+        <el-form-item label="部门">
+          <el-input v-model="form.dutyDept"></el-input>
+        </el-form-item>
+
+        <el-form-item label="电话">
+          <el-input v-model="form.dutyPhone"></el-input>
+        </el-form-item>
+
+      </el-form>
+
+      <div
+        slot="footer"
+        class="dialog-footer"
+      >
+        <el-button
+          type="primary"
+          @click="hangdleAmendWatchPeople"
+        >确 定</el-button>
+        <el-button @click="dialogFormVisible2 = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: "",
+  data() {
+    return {
+      dialogFormVisible: false, // 添加弹窗控制
+      dialogFormVisible2: false, // 修改弹窗控制
+      tableData: [], // 人员列表
+      form: {
+        dutyName: "", // 姓名
+        dutyPhone: "", // 电话
+        dutyDept: "", // 部门
+        todayWork: "", // 哪天值班,1代表今天,2代表明天
+      },
+      // 验证规则
+      rules: {
+        dutyName: [{ required: true, message: "请输入姓名", trigger: "blur" }],
+        dutyPhone: [{ required: true, message: "请输入电话", trigger: "blur" }],
+        dutyDept: [{ required: true, message: "请输入部门", trigger: "blur" }],
+        todayWork: [
+          { required: true, message: "请选择值班时间", trigger: "change" },
+        ],
+      },
+      watchValue: 1,
+      options: [
+        { id: 1, value: "今天值班" },
+        { id: 2, value: "明天值班" },
+      ],
+    };
+  },
+  mounted() {
+    this.getDutyList();
+  },
+  methods: {
+    // 获取 值班安排列表
+    async getDutyList() {
+      let params = {
+        todayWork: this.watchValue,
+      };
+      let res = await this.$axios({
+        url: "/diseaseRight/dutyArrangement/listOfGroup",
+        method: "get",
+        params,
+      });
+      // console.log(res);
+      if (res.data.success) {
+        this.tableData = res.data.data[0].list;
+      } else {
+        console.log("获取 值班安排列表 失败");
+      }
+    },
+    // 添加值班人员
+    addWatchPeople(formName) {
+      this.$refs[formName].validate(async (valid) => {
+        if (valid) {
+          this.dialogFormVisible = false;
+          let res = await this.$axios({
+            method: "post",
+            url: "/diseaseRight/dutyArrangement/save",
+            headers: {
+              "Admin-Token": sessionStorage.getItem("token"),
+            },
+            data: this.form,
+          });
+          // console.log(res);
+          if (res.data.success) {
+            this.getDutyList();
+            this.$message.success("添加成功!");
+          } else {
+            this.$message.error("添加失败!");
+          }
+        } else {
+          console.log("提交失败!!");
+          return false;
+        }
+      });
+    },
+    // 修改值班人员
+    amendWatchPeople(row) {
+      this.dialogFormVisible2 = true;
+      this.form = { ...row };
+      this.form.todayWork = this.form.todayWork - 0;
+    },
+    // 修改值班人员请求
+    async hangdleAmendWatchPeople() {
+      this.dialogFormVisible2 = false;
+      let res = await this.$axios({
+        method: "put",
+        url: "/diseaseRight/dutyArrangement/update",
+        headers: {
+          "Admin-Token": sessionStorage.getItem("token"),
+        },
+        data: this.form,
+      });
+      // console.log(res);
+      if (res.data.success) {
+        this.getDutyList();
+        this.$message.success("修改成功!");
+      } else {
+        this.$message.error("修改失败!");
+      }
+    },
+    // 删除值班人员
+    deleteWatchPeople(id) {
+      this.$confirm("确定删除吗?", "提示", {
+        cancelButtonText: "取消",
+        confirmButtonText: "确定",
+        type: "warning",
+      })
+        .then(async () => {
+          let res = await this.$axios({
+            method: "delete",
+            url: "/diseaseRight/dutyArrangement/delete",
+            headers: {
+              "Admin-Token": sessionStorage.getItem("token"),
+            },
+            data: [id],
+          });
+          // console.log(res);
+          if (res.data.success) {
+            this.$message.success("删除成功");
+            this.getDutyList();
+          } else {
+            this.$message.error("删除失败!");
+          }
+        })
+        .catch(() => {
+          this.$message.info("已取消");
+        });
+    },
+    // 下拉框改变数值
+    changeWatchPeople(val) {
+      this.watchValue = val;
+      this.getDutyList();
+    },
+  },
+};
+</script>
+
+<style  scoped>
+.first-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  height: 50px;
+}
+.tag {
+  width: 108px;
+  height: 40px;
+  font-size: 24px;
+  font-family: Microsoft YaHei-3970(82674968);
+  font-weight: bold;
+  color: #000000;
+  line-height: 54px;
+}
+.el-button.btnupload {
+  margin: 0;
+  width: 180px;
+  height: 40px;
+  border-radius: 5px;
+  font-size: 14px;
+}
+</style>

+ 238 - 0
src/components/zhuanghaoguanli/zhuanghaoguanli.css

@@ -0,0 +1,238 @@
+/* dev-mgr */
+.first-row {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	height: 50px;
+}
+
+.second-row {
+	height: 80px;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	padding-top: 40px;
+}
+
+.third-row {
+	margin-top: 10px;
+}
+
+.tag {
+	width: 108px;
+	height: 40px;
+	font-size: 24px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: bold;
+	color: #000000;
+	line-height: 54px;
+}
+
+.demo-form-inline {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+}
+
+>>>.el-form-item__label {
+	height: 40px;
+	font-size: 22px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #333333;
+	line-height: 40px;
+}
+
+.shuibiaoId>>>.el-input__inner {
+	width: 240px;
+	height: 40px;
+	border: 1px solid #4D4D4D;
+	border-radius: 4px;
+	font-size: 20px;
+}
+
+.el-button {
+	margin: 0 60px 0 40px;
+	width: 100px;
+	height: 40px;
+	background: #298DEF;
+	border-radius: 4px;
+	font-size: 18px;
+}
+
+.btn-add {
+	margin-top: -30px;
+}
+
+.btn-add .el-button {
+	width: 100px;
+	height: 40px;
+	background: #298DEF;
+	border-radius: 4px;
+	display: flex;
+	align-items: center;
+}
+
+>>>.el-icon-add {
+	width: 22px;
+	height: 22px;
+	background: url(../../../static/images/add.png) center center no-repeat;
+	background-size: 18px 18px;
+}
+
+>>>.el-table__header {
+	font-size: 20px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+}
+
+>>>.el-table__header .cell {
+	color: #010101;
+}
+
+>>>.el-checkbox,
+>>>.el-checkbox__inner {
+	width: 24px;
+	height: 24px;
+}
+
+>>>.el-checkbox__inner::after {
+	margin: 3px 0 0 4px;
+	width: 4px;
+	height: 10px;
+}
+
+>>>.el-table__row .cell {
+	height: 35px;
+	line-height: 35px;
+	font-size: 18px;
+	color: #333333;
+}
+
+>>>.el-table__row .cell .el-button {
+	background: none;
+	margin: 0 20px 0 0;
+	width: 50px;
+	height: 35px;
+	font-size: 18px;
+}
+
+.del-btn {
+	color: #ff0000 !important;
+}
+
+.el-pagination>>>.el-icon {
+	width: 38px;
+	height: 38px;
+	line-height: 38px;
+	border: 1px solid #626262;
+	border-radius: 2px;
+	font-size: 18px;
+}
+
+.el-pagination>>>.number,
+.el-pagination>>>.more {
+	margin: 0 5px;
+	width: 40px;
+	height: 40px;
+	line-height: 40px;
+	border: 1px solid #626262;
+	border-radius: 2px;
+	font-size: 18px;
+}
+
+.table-footer {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+
+.pages {
+	width: 100%;
+	display: flex;
+	justify-content: center;
+}
+
+>>>.el-pager li.active {
+	color: #FFF;
+	font-weight: bold;
+	background-color: #298DEF
+}
+
+.export-table {
+	width: 200px;
+	display: flex;
+	font-size: 20px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #FFFFFF;
+	line-height: 54px;
+}
+
+>>>.el-dialog__title {
+	font-size: 24px;
+}
+
+.el-dialog .el-form {
+	margin-top: 20px;
+}
+
+>>>.el-form-item__label {
+	font-size: 20px;
+}
+
+>>>.el-dialog__body .el-input__inner {
+	font-size: 18px;
+}
+
+.dialog-footer>>>.el-button--default {
+	border: 1px solid #298DEF;
+	border-radius: 4px;
+	font-size: 24px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #298DEF;
+	height: 46px;
+	background-color: #FFFFFF;
+}
+
+.dialog-footer>>>.el-button--primary {
+	background: #298DEF;
+	border-radius: 4px;
+	font-size: 24px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #FFFFFF;
+	height: 46px;
+}
+
+.my-dialog {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.box-icon {
+	display: flex;
+	justify-content: center;
+	margin: 20px auto;
+	background: url(../../../static/images/info.png) no-repeat;
+	background-size: 107px 107px;
+	width: 107px;
+	height: 107px;
+}
+
+.del-msg {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-size: 22px;
+	font-family: Microsoft YaHei-3970(82674968);
+	font-weight: 400;
+	color: #333333;
+	line-height: 54px;
+	text-align: center;
+}
+
+
+/* dev-mgr */

+ 870 - 0
src/components/zhuanghaoguanli/zhuanghaoguanli.vue

@@ -0,0 +1,870 @@
+<template>
+  <div>
+    <el-row>
+      <el-col
+        :span="24"
+        class="first-row"
+      >
+        <div class="tag">账号管理</div>
+      </el-col>
+    </el-row>
+    <el-row>
+      <el-col
+        :span="24"
+        class="second-row"
+      >
+        <!-- <el-form :inline="true" class="demo-form-inline">
+					<el-form-item label="账号:" class="shuibiaoId">
+						<el-input v-model="account" placeholder="请输入账号" clearable maxlength="16"></el-input>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" @click="onSearch">查找</el-button>
+					</el-form-item>
+				</el-form> -->
+        <div class="btn-add">
+          <el-button
+            type="primary"
+            icon="el-icon-add"
+            @click="handleAdd"
+          >添加</el-button>
+        </div>
+      </el-col>
+    </el-row>
+    <hr style="background-color: #CCCCCC;height: 1px;border: 0;">
+    <el-row>
+      <el-col
+        :span="24"
+        class="third-row"
+      >
+        <el-table
+          ref="multipleTable"
+          :data="tableData"
+          tooltip-effect="dark"
+          style="width: 100%"
+          highlight-current-row
+        >
+          <!-- <el-table-column label="昵称" align="center">
+						<template slot-scope="scope">{{ scope.row.name }}</template>
+					</el-table-column> -->
+          <el-table-column
+            prop="userName"
+            label="账号"
+            align="center"
+          ></el-table-column>
+          <el-table-column
+            prop="adminType"
+            label="类别"
+            align="center"
+            :formatter="adminType"
+          >
+          </el-table-column>
+          <el-table-column
+            prop="status"
+            label="状态"
+            show-overflow-tooltip
+            :formatter="formatStatus"
+          >
+          </el-table-column>
+          <el-table-column
+            label="操作"
+            align="center"
+            width="80"
+          >
+            <el-button
+              type="text"
+              class="del-btn"
+              slot-scope="scope"
+              v-if="myUserType === '0'"
+              @click="handleDelete(scope.$index, scope.row)"
+            >删除
+            </el-button>
+          </el-table-column>
+          <el-table-column width="130">
+            <el-button
+              type="text"
+              slot-scope="scope"
+              v-if="myUserType === '0'"
+              @click="handleEdit(scope.$index, scope.row)"
+            >编辑
+            </el-button>
+          </el-table-column>
+        </el-table>
+        <div
+          style="margin-top: 20px"
+          class="table-footer"
+        >
+          <div class="pages">
+            <el-pagination
+              layout="prev, pager, next"
+              :total="total_rows"
+              :current-page.sync="current_page"
+              :hide-on-single-page="true"
+              :page-size="page_rows"
+              @current-change="handleCurrentChange"
+            >
+            </el-pagination>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+    <el-row>
+      <el-col
+        :span="24"
+        class="forth-row"
+      >
+        <!-- 添加对话框 -->
+        <el-dialog
+          title="添加账号"
+          :visible.sync="adddialogFormVisible"
+          width="500px"
+          top="0vh"
+          class="my-dialog"
+          :close-on-click-modal="false"
+          :close-on-press-escape="false"
+        >
+          <hr style="width: 100%; position: absolute; top: 60px; left: 0px;background-color: #CCCCCC;height: 1px;border: 0;">
+          <el-form
+            :model="addform"
+            :rules="addRules"
+            ref="addform"
+          >
+            <el-form-item
+              label="账号:"
+              prop="userName"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                v-model="addform.userName"
+                autocomplete="off"
+                minlength="5"
+                maxlength="16"
+              >
+              </el-input>
+            </el-form-item>
+            <el-form-item
+              label="密码:"
+              prop="password"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                v-model="addform.password"
+                type="password"
+                minlength="5"
+                autocomplete="off"
+                maxlength="16"
+              >
+              </el-input>
+            </el-form-item>
+            <el-form-item
+              label="确认密码:"
+              prop="comfirmPass"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                v-model="addform.comfirmPass"
+                type="password"
+                minlength="5"
+                autocomplete="off"
+                maxlength="16"
+              >
+              </el-input>
+            </el-form-item>
+            <el-form-item
+              label="类别:"
+              prop="adminType"
+              :label-width="formLabelWidth"
+            >
+              <el-radio
+                v-model="addform.adminType"
+                label="1"
+              >管理员</el-radio>
+              <el-radio
+                v-model="addform.adminType"
+                label="0"
+              >超级管理员</el-radio>
+            </el-form-item>
+            <el-form-item
+              label="状态:"
+              prop="status"
+              :label-width="formLabelWidth"
+            >
+              <el-radio
+                v-model="addform.status"
+                label="1"
+              >正常</el-radio>
+              <el-radio
+                v-model="addform.status"
+                label="0"
+              >冻结</el-radio>
+            </el-form-item>
+          </el-form>
+          <div
+            slot="footer"
+            class="dialog-footer"
+          >
+            <el-button @click="adddialogFormVisible = false">取 消</el-button>
+            <el-button
+              type="primary"
+              @click="handleAddRow('addform')"
+            >确 定</el-button>
+          </div>
+        </el-dialog>
+        <!-- 编辑对话框 -->
+        <el-dialog
+          title="编辑"
+          :visible.sync="editdialogFormVisible"
+          width="500px"
+          top="0vh"
+          class="my-dialog"
+          :close-on-click-modal="false"
+          :close-on-press-escape="false"
+        >
+          <hr style="width: 100%; position: absolute; top: 60px; left: 0px;background-color: #CCCCCC;height: 1px;border: 0;">
+          <el-form
+            :model="form"
+            :rules="formRules"
+            ref="form"
+          >
+            <el-form-item
+              label="账号:"
+              prop="userName"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                ref="zhanghao"
+                v-model="form.userName"
+                autocomplete="off"
+                maxlength="16"
+              >
+              </el-input>
+            </el-form-item>
+            <!-- <el-form-item label="原密码:" prop="oldPassword" :label-width="formLabelWidth">
+							<el-input ref="yuanmima" v-model="form.oldPassword" type="password" autocomplete="off"
+								maxlength="16">
+							</el-input>
+						</el-form-item> -->
+            <el-form-item
+              label="新密码:"
+              prop="password"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                ref="xinmima"
+                v-model="form.password"
+                autocomplete="off"
+                type="password"
+                maxlength="16"
+              >
+              </el-input>
+            </el-form-item>
+            <el-form-item
+              label="确认新密码:"
+              prop="comfirmPass"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                ref="querenmima"
+                v-model="form.comfirmPass"
+                autocomplete="off"
+                type="password"
+                maxlength="16"
+              >
+              </el-input>
+            </el-form-item>
+            <el-form-item
+              label="类别"
+              prop="adminType"
+              :label-width="formLabelWidth"
+            >
+              <el-radio
+                v-model="form.adminType"
+                label="1"
+              >管理员</el-radio>
+              <el-radio
+                v-model="form.adminType"
+                label="0"
+              >超级管理员</el-radio>
+            </el-form-item>
+            <el-form-item
+              label="状态:"
+              prop="status"
+              :label-width="formLabelWidth"
+            >
+              <el-radio
+                v-model="form.status"
+                label="1"
+              >正常</el-radio>
+              <el-radio
+                v-model="form.status"
+                label="0"
+              >冻结</el-radio>
+            </el-form-item>
+          </el-form>
+          <div
+            slot="footer"
+            class="dialog-footer"
+          >
+            <el-button @click="editdialogFormVisible = false">取 消</el-button>
+            <el-button
+              type="primary"
+              @click="handleEditRow('form')"
+            >确 定</el-button>
+          </div>
+        </el-dialog>
+        <!-- 删除对话框 -->
+        <el-dialog
+          :visible.sync="delDialogVisible"
+          width="500px"
+          center
+          top="0vh"
+          class="my-dialog"
+          :close-on-click-modal="false"
+          :close-on-press-escape="false"
+        >
+          <div class="box-icon"></div>
+          <span class="del-msg">确定删除【{{del_account}}】账号吗?</span>
+          <span
+            slot="footer"
+            class="dialog-footer"
+          >
+            <el-button @click="delDialogVisible = false">取 消</el-button>
+            <el-button
+              type="primary"
+              @click="getDelPwd"
+            >确 定</el-button>
+            <!-- <el-button type="primary" @click="handleDelRow">确 定</el-button> -->
+          </span>
+        </el-dialog>
+        <!-- 删除账号密码验证对话框 -->
+        <!-- <el-dialog title="验证密码" :visible.sync="delDialogEnterPwdVisible" width="500px" top="0vh"
+					class="my-dialog" :close-on-click-modal="false" :close-on-press-escape="false">
+					<hr
+						style="width: 100%; position: absolute; top: 60px; left: 0px;background-color: #CCCCCC;height: 1px;border: 0;">
+					<el-form :model="delConfirmPwdform" :rules="delConfirmPwdRules" ref="delConfirmPwdform">
+						<el-form-item label="请输入密码:" prop="delPwd" :label-width="formLabelWidth">
+							<el-input ref="delPwd" v-model="delConfirmPwdform.delPwd" type="password" autocomplete="off"
+								maxlength="16">
+							</el-input>
+						</el-form-item>
+					</el-form>
+					<div slot="footer" class="dialog-footer">
+						<el-button @click="delDialogEnterPwdVisible = false">取 消</el-button>
+						<el-button type="primary" @click="getDelPwd('delConfirmPwdform')">确 定</el-button>
+					</div>
+				</el-dialog> -->
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      myUserType: "",
+      addform: {
+        // 添加框的数据绑定
+        userName: "",
+        password: "",
+        comfirmPass: "",
+        adminType: "1",
+        status: "1",
+      },
+      addRules: {
+        userName: [
+          {
+            required: true,
+            message: "请输入账号",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "账号长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+        password: [
+          {
+            required: true,
+            message: "请输入密码",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "密码长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+        comfirmPass: [
+          {
+            required: true,
+            message: "请输入确认密码",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "确认密码长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+      },
+      form: {
+        // 编辑框的数据绑定
+        id: 0,
+        userName: "",
+        adminType: "1",
+        status: "1",
+        // oldPassword: '',
+        password: "",
+        comfirmPass: "",
+      },
+      formRules: {
+        name: [
+          {
+            required: true,
+            message: "请输入昵称",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "昵称长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+        number: [
+          {
+            required: true,
+            message: "请输入账号",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "账号长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+        // oldPassword: [{
+        // 		required: true,
+        // 		message: '请输入原密码',
+        // 		trigger: 'blur'
+        // 	},
+        // 	{
+        // 		min: 5,
+        // 		max: 16,
+        // 		message: '原密码长度不小于5位',
+        // 		trigger: 'blur'
+        // 	}
+        // ],
+        password: [
+          {
+            required: true,
+            message: "请输入新密码",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "新密码长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+        comfirmPass: [
+          {
+            required: true,
+            message: "请输入确认新密码",
+            trigger: "blur",
+          },
+          {
+            min: 5,
+            max: 16,
+            message: "确认新密码长度不小于5位",
+            trigger: "blur",
+          },
+        ],
+      },
+      delConfirmPwdform: {
+        delPwd: "", // 被删除的账号的密码
+      },
+      // delConfirmPwdRules: {
+      // 	delPwd: [{
+      // 			required: true,
+      // 			message: '请输入密码进行验证',
+      // 			trigger: 'blur'
+      // 		},
+      // 		{
+      // 			min: 5,
+      // 			max: 16,
+      // 			message: '密码长度不小于5位',
+      // 			trigger: 'blur'
+      // 		}
+      // 	]
+      // },
+      del_id: 0, // 被删除的id
+      del_account: "", // 被删除的账号
+      account: "", // 需要查找的账号
+      login_account: "", // 登陆的账号
+      adddialogFormVisible: false, // 控制添加对话框显示、隐藏
+      editdialogFormVisible: false, // 控制编辑对话框显示、隐藏
+      delDialogEnterPwdVisible: false, // 控制删除账号时输入密码对话框
+      delDialogVisible: false, // 控制删除对话框显示、隐藏
+      formLabelWidth: "146px", // label宽度
+      tableData: [], // 表格数据
+      current_page: 1, // 当前所在页码
+      page_rows: 8, // 每页显示的记录数
+      total_rows: 0, // 总记录数
+      token: "",
+    };
+  },
+  created() {
+    this.login_account = sessionStorage.getItem("uname");
+    this.token = sessionStorage.getItem("token");
+    this.myUserType = sessionStorage.getItem("userType");
+    // 获取账号列表
+    this.updateTable();
+  },
+  methods: {
+    adminType(e) {
+      if (e.adminType === "1") {
+        return "管理员";
+      } else if (e.adminType === "0") {
+        return "超级管理员";
+      }
+      return "普通用户";
+    },
+    formatStatus(e) {
+      if (e.status === "1") {
+        return "正常";
+      } else if (e.status === "0") {
+        return "冻结";
+      }
+      return "未知";
+    },
+    /**
+     * 改变页码
+     * @param {Object} val
+     */
+    handleCurrentChange(val) {
+      this.current_page = val;
+
+      // 查询账号相关信息
+      this.updateTable();
+    },
+    /**
+     * 管理员列表,填充表格变量
+     */
+    updateTable() {
+      var _this = this;
+      _this.tableData = [];
+      let params = {
+        curPage: this.current_page,
+        pageSize: this.page_rows,
+        orderField: this.login_account,
+      };
+      // if (this.account) {
+      // 	params.userName = this.account
+      // }
+      // _this.rows_total = 0
+      this.$axios({
+        method: "get",
+        url: "/diseaseRight/adminInfo/list",
+        headers: {
+          "Content-type": "application/x-www-form-urlencoded;charset=utf-8",
+        },
+        data: params,
+      })
+        .then((res) => {
+          console.log(res.data);
+          if (res.data.success) {
+            _this.tableData = res.data.data.list;
+            _this.total_rows = res.data.data.totalCount;
+            var i = 0;
+            for (; i < _this.tableData.length; i++) {
+              if (_this.tableData[i].userName == _this.login_account) {
+                sessionStorage.setItem("unic", _this.tableData[i].userName);
+                break;
+              }
+            }
+            // _this.$emit('sonFun', _this.tableData[i].userName)
+          } else {
+            _this.$message.success("【获取账号】暂无数据!");
+          }
+        })
+        .catch((err) => {
+          // console.log(err);
+          _this.$message.error("【获取账号】请求异常: " + err);
+        });
+    },
+    /**
+     * 查找账号
+     */
+    onSearch() {
+      // if (!this.account) {
+      // 	this.$message.error('请输入账号')
+      // 	return
+      // }
+
+      // 查询账号相关信息
+      this.updateTable();
+    },
+    /**
+     * 添加账号
+     */
+    handleAdd() {
+      this.adddialogFormVisible = true;
+    },
+    /**
+     * 添加一条账号
+     */
+    handleAddRow(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          if (!String(this.addform.userName).trim()) {
+            this.$message.error("请输入账号");
+            return;
+          }
+          if (!String(this.addform.password).trim()) {
+            this.$message.error("请输入密码");
+            return;
+          }
+          if (!String(this.addform.comfirmPass).trim()) {
+            this.$message.error("请输入确认密码");
+            return;
+          }
+          if (
+            String(this.addform.password).trim() !=
+            String(this.addform.comfirmPass).trim()
+          ) {
+            this.$message.error("密码与确认密码不一致!");
+            return;
+          }
+          var params = {
+            userName: String(this.addform.userName).trim(),
+            password: String(this.addform.password).trim(),
+            adminType: String(this.addform.adminType).trim(),
+            status: String(this.addform.status).trim(),
+          };
+          var _this = this;
+          this.$axios({
+            method: "post",
+            url: "/diseaseRight/adminInfo/save",
+            headers: {
+              "Admin-Token": this.token,
+              "Content-type": "application/json; charset=utf-8",
+            },
+            data: params,
+          })
+            .then((res) => {
+              // console.log(res.data);
+              if (res.data.success) {
+                this.adddialogFormVisible = false;
+                _this.$message.success("【账号添加】成功!");
+                _this.addform = {
+                  userName: "",
+                  password: "",
+                  comfirmPass: "",
+                  adminType: "1",
+                  status: "1",
+                };
+                // 更新列表
+                this.updateTable();
+              } else if (res.data.message === "数据已存在") {
+                _this.$message.error("账号已存在");
+              } else {
+                _this.$message.error(res.data.message);
+              }
+            })
+            .catch((err) => {
+              // console.log(err);
+              _this.$message.error("【账号】添加请求异常: " + err);
+            });
+        } else {
+          // this.$message.error('表单验证失败!')
+          return false;
+        }
+      });
+    },
+    /**
+     * 编辑对话框
+     * @param {Object} index
+     * @param {Object} row
+     */
+    handleEdit(index, row) {
+      console.log(index, row);
+      this.editdialogFormVisible = true;
+      this.form.id = row.id;
+      this.form.userName = row.userName;
+      this.form.adminType = row.adminType;
+      this.form.status = row.status;
+      // this.form.oldPassword = ''
+      this.form.password = "";
+      this.form.comfirmPass = "";
+    },
+    /**
+     * 编辑更新指定的id所在的行
+     */
+    handleEditRow(formName) {
+      var _this = this;
+      _this.$refs[formName].validate((valid) => {
+        if (valid) {
+          if (!String(_this.form.userName).trim()) {
+            _this.$message.error("请输入账号");
+            _this.$refs["nicheng"].focus();
+            return;
+          }
+          // if (!String(_this.form.oldPassword).trim()) {
+          // 	_this.$message.error('请输入原密码')
+          // 	_this.$refs['yuanmima'].focus()
+          // 	return
+          // }
+          if (!String(_this.form.password).trim()) {
+            _this.$message.error("请输入新密码");
+            _this.$refs["xinmima"].focus();
+            return;
+          }
+          if (
+            String(_this.form.password).trim() !=
+            String(_this.form.comfirmPass).trim()
+          ) {
+            _this.$message.error("新密码和确认新密码不一致!");
+            _this.$refs["querenmima"].focus();
+            return;
+          }
+          if (!String(_this.form.adminType).trim()) {
+            _this.$message.error("请输入类别");
+            _this.$refs["zhanghao"].focus();
+            return;
+          }
+          if (!String(_this.form.status).trim()) {
+            _this.$message.error("请输入状态");
+            _this.$refs["zhanghao"].focus();
+            return;
+          }
+
+          let params = {
+            id: _this.form.id,
+            userName: String(_this.form.userName).trim(),
+            adminType: String(_this.form.adminType).trim(),
+            // oldpass: String(_this.form.oldPassword).trim(),
+            password: String(_this.form.password).trim(),
+            status: String(_this.form.status).trim(),
+          };
+
+          _this
+            .$axios({
+              method: "put",
+              url: "/diseaseRight/adminInfo/update",
+              headers: {
+                "Admin-Token": this.token,
+                "Content-type": "application/json; charset=utf-8",
+              },
+              data: params,
+            })
+            .then((res) => {
+              console.log(res.data);
+              if (res.data.success) {
+                _this.editdialogFormVisible = false;
+                _this.$message.success("【账号】修改成功!");
+
+                // 更新表格
+                _this.updateTable();
+
+                _this.form = {
+                  id: 0,
+                  userName: "",
+                  adminType: "1",
+                  status: "1",
+                  // oldPassword: '',
+                  password: "",
+                  comfirmPass: "",
+                };
+              } else {
+                _this.$message.error("【账号】修改失败!" + res.data.msg);
+              }
+            })
+            .catch((err) => {
+              // console.log(err);
+              _this.$message.error("【账号】修改请求异常: " + err);
+            });
+        } else {
+          // _this.$message.error('表单验证失败!')
+          return false;
+        }
+      });
+    },
+    /**
+     * 删除对话框
+     * @param {Object} index
+     * @param {Object} row
+     */
+    handleDelete(index, row) {
+      this.delDialogVisible = true;
+      this.del_account = row.userName;
+      this.del_id = row.id;
+    },
+    /**
+     * 被删除账号的密码
+     */
+    getDelPwd(formName) {
+      var _this = this;
+      // var params = {
+      // 	id: _this.del_id
+      // }
+      _this
+        .$axios({
+          method: "delete",
+          url: "/diseaseRight/adminInfo/delete/" + _this.del_id,
+          headers: {
+            "Admin-Token": this.token,
+            "Content-type": "application/x-www-form-urlencoded;charset=utf-8",
+          },
+          // data: params
+        })
+        .then((res) => {
+          // console.log(res.data);
+          if (res.data.success) {
+            _this.$message.success(
+              "账号【" + this.del_account + "】删除成功!"
+            );
+            _this.delDialogVisible = false;
+            _this.updateTable();
+          } else {
+            _this.$message.error("账号【" + this.del_account + "】删除失败!");
+          }
+        })
+        .catch((err) => {
+          // console.log(err);
+          _this.$message.error(
+            "账号【" + this.del_account + "】删除请求异常: " + err
+          );
+        });
+    },
+    /**
+     * 删除指定的id所在的行
+     */
+    // handleDelRow() {
+    // 	if (this.login_account == this.del_account) {
+    // 		this.delDialogVisible = false
+    // 		this.$message.error('不能删除当前登录账号!')
+    // 		return
+    // 	}
+
+    // 	this.delConfirmPwdform.delPwd = ''
+
+    // 	this.delDialogEnterPwdVisible = true
+
+    // 	setTimeout(() => {
+    // 		this.$refs['delPwd'].focus()
+    // 	}, 200)
+    // }
+  },
+};
+</script>
+
+<style scoped>
+@import url("zhuanghaoguanli.css");
+</style>

+ 53 - 0
src/main.js

@@ -0,0 +1,53 @@
+import Vue from 'vue'
+import App from './App'
+// 引入Vue路由
+import router from './router'
+
+// 引入Axios发送请求
+import Axios from "axios"
+
+// element - ui按需引入
+import {
+	Row, Col, Upload, Button, Select, Input, Menu, MenuItem, Form, FormItem, DatePicker, Table, TableColumn, Pagination, Dialog, Option, Message, MessageBox, Tabs,
+	TabPane, Card, TimePicker,
+} from 'element-ui';
+
+// 注册使用element - ui组件
+Vue.use(Row)
+Vue.use(Col)
+Vue.use(Upload)
+Vue.use(Button)
+Vue.use(Select)
+Vue.use(Input)
+Vue.use(Menu)
+Vue.use(MenuItem)
+Vue.use(Form)
+Vue.use(FormItem)
+Vue.use(DatePicker)
+Vue.use(Table)
+Vue.use(TableColumn)
+Vue.use(Pagination)
+Vue.use(Dialog)
+Vue.use(Option)
+Vue.use(Tabs)
+Vue.use(TabPane)
+Vue.use(Card)
+Vue.use(TimePicker)
+
+Vue.prototype.$confirm = MessageBox.confirm;
+Vue.prototype.$message = Message;
+
+// 把Axios挂载到Vue原型上,这样全局就可以使用Axios发送请求
+Vue.prototype.$axios = Axios;
+
+// 开发环境提示关闭
+Vue.config.productionTip = false
+
+new Vue({
+	el: '#app',
+	router,
+	components: {
+		App
+	},
+	template: '<App/>'
+})

+ 60 - 0
src/router/index.js

@@ -0,0 +1,60 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+import Login from '@/components/login/login' // 登陆
+import Index from '@/components/index/index' // 首页
+import ZHGL from '@/components/zhuanghaoguanli/zhuanghaoguanli' // 账户管理
+import DisposeSituation from '@/components/disposeSituation' // 处置情况
+import SmokeWarn from '@/components/smokeWarn' // 冒烟告警
+import WatchBill from '@/components/watchBill' // 值班表
+import BroadcastContent from '@/components/broadcastContent' // 广播内容
+import SystemSetup from '@/components/systemSetup' // 系统设置
+
+Vue.use(Router)
+
+export default new Router({
+	// mode: 'history',
+	// base: '/smoke/',
+	routes: [
+		{
+			path: '/',
+			name: 'Login',
+			component: Login
+		},
+		{
+			path: '/smoke/index',
+			name: 'index',
+			redirect: '/smoke/index/watchBill', // 设置默认打开的页面
+			component: Index,
+			children: [{
+				path: 'zhgl',
+				name: 'ZHGL',
+				component: ZHGL // 账户管理
+			},
+			{
+				path: 'watchBill',
+				name: 'WatchBill',
+				component: WatchBill // 值班表
+			},
+			{
+				path: 'disposeSituation',
+				name: 'DisposeSituation',
+				component: DisposeSituation // 处置情况
+			},
+			{
+				path: 'smokeWarn',
+				name: 'smokeWarn',
+				component: SmokeWarn // 冒烟告警
+			},
+			{
+				path: 'broadcastContent',
+				name: 'BroadcastContent',
+				component: BroadcastContent // 广播内容
+			},
+			{
+				path: 'systemSetup',
+				name: 'SystemSetup',
+				component: SystemSetup // 系统设置
+			}
+			]
+		}]
+})

+ 59 - 0
src/store/index.js

@@ -0,0 +1,59 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex);
+let store = new Vuex.Store({
+	state: {
+		loginStatus: 0,
+		token: '',
+		userName: ''
+	},
+	mutations: {
+		loginSuccess(state) {
+			state.userName = db.get('userName');
+			state.loginStatus = 1
+		},
+		loginFail(state) {
+			state.loginStatus = 0
+		},
+		updateToken(state, token) {
+
+			state.token = token;
+			db.set('token', token);
+
+		},
+		loginOut(state) {
+			db.remove('token');
+			state.loginStatus = 0;
+			state.token = "";
+		}
+	},
+	actions: {
+		loginAction({
+			commit
+		}, token) {
+
+			commit('loginSuccess');
+			commit('updateToken', token)
+		},
+		tokenAction({
+			commit
+		}, token) {
+			commit('updateToken', token)
+		},
+		loginOutAction({
+			commit
+		}) {
+			commit('loginOut')
+		}
+
+	}
+})
+// 页面刷新时,重新赋值token
+let token = db.get('token');
+if (token) {
+	store.commit('updateToken', token);
+	store.commit('loginSuccess')
+}
+
+export default store

+ 0 - 0
static/.gitkeep


BIN
static/images/add.png


BIN
static/images/bg.png


BIN
static/images/diwen.png


BIN
static/images/exit.png


BIN
static/images/guangbo.png


BIN
static/images/info.png


BIN
static/images/logo.png


BIN
static/images/maoyangaojing.png


BIN
static/images/mima.png


BIN
static/images/qingkuang.png


BIN
static/images/touxiang.jpg


BIN
static/images/xitongshezhi.png


BIN
static/images/yonghuguanli.png


BIN
static/images/yonghuming.png


+ 27 - 0
test/e2e/custom-assertions/elementCount.js

@@ -0,0 +1,27 @@
+// A custom Nightwatch assertion.
+// The assertion name is the filename.
+// Example usage:
+//
+//   browser.assert.elementCount(selector, count)
+//
+// For more information on custom assertions see:
+// http://nightwatchjs.org/guide#writing-custom-assertions
+
+exports.assertion = function (selector, count) {
+  this.message = 'Testing if element <' + selector + '> has count: ' + count
+  this.expected = count
+  this.pass = function (val) {
+    return val === this.expected
+  }
+  this.value = function (res) {
+    return res.value
+  }
+  this.command = function (cb) {
+    var self = this
+    return this.api.execute(function (selector) {
+      return document.querySelectorAll(selector).length
+    }, [selector], function (res) {
+      cb.call(self, res)
+    })
+  }
+}

+ 46 - 0
test/e2e/nightwatch.conf.js

@@ -0,0 +1,46 @@
+require('babel-register')
+var config = require('../../config')
+
+// http://nightwatchjs.org/gettingstarted#settings-file
+module.exports = {
+  src_folders: ['test/e2e/specs'],
+  output_folder: 'test/e2e/reports',
+  custom_assertions_path: ['test/e2e/custom-assertions'],
+
+  selenium: {
+    start_process: true,
+    server_path: require('selenium-server').path,
+    host: '127.0.0.1',
+    port: 4444,
+    cli_args: {
+      'webdriver.chrome.driver': require('chromedriver').path
+    }
+  },
+
+  test_settings: {
+    default: {
+      selenium_port: 4444,
+      selenium_host: 'localhost',
+      silent: true,
+      globals: {
+        devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
+      }
+    },
+
+    chrome: {
+      desiredCapabilities: {
+        browserName: 'chrome',
+        javascriptEnabled: true,
+        acceptSslCerts: true
+      }
+    },
+
+    firefox: {
+      desiredCapabilities: {
+        browserName: 'firefox',
+        javascriptEnabled: true,
+        acceptSslCerts: true
+      }
+    }
+  }
+}

+ 48 - 0
test/e2e/runner.js

@@ -0,0 +1,48 @@
+// 1. start the dev server using production config
+process.env.NODE_ENV = 'testing'
+
+const webpack = require('webpack')
+const DevServer = require('webpack-dev-server')
+
+const webpackConfig = require('../../build/webpack.prod.conf')
+const devConfigPromise = require('../../build/webpack.dev.conf')
+
+let server
+
+devConfigPromise.then(devConfig => {
+  const devServerOptions = devConfig.devServer
+  const compiler = webpack(webpackConfig)
+  server = new DevServer(compiler, devServerOptions)
+  const port = devServerOptions.port
+  const host = devServerOptions.host
+  return server.listen(port, host)
+})
+.then(() => {
+  // 2. run the nightwatch test suite against it
+  // to run in additional browsers:
+  //    1. add an entry in test/e2e/nightwatch.conf.js under "test_settings"
+  //    2. add it to the --env flag below
+  // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
+  // For more information on Nightwatch's config file, see
+  // http://nightwatchjs.org/guide#settings-file
+  let opts = process.argv.slice(2)
+  if (opts.indexOf('--config') === -1) {
+    opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
+  }
+  if (opts.indexOf('--env') === -1) {
+    opts = opts.concat(['--env', 'chrome'])
+  }
+
+  const spawn = require('cross-spawn')
+  const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
+
+  runner.on('exit', function (code) {
+    server.close()
+    process.exit(code)
+  })
+
+  runner.on('error', function (err) {
+    server.close()
+    throw err
+  })
+})

+ 19 - 0
test/e2e/specs/test.js

@@ -0,0 +1,19 @@
+// For authoring Nightwatch tests, see
+// http://nightwatchjs.org/guide#usage
+
+module.exports = {
+  'default e2e tests': function (browser) {
+    // automatically uses dev Server port from /config.index.js
+    // default: http://localhost:8080
+    // see nightwatch.conf.js
+    const devServer = browser.globals.devServerURL
+
+    browser
+      .url(devServer)
+      .waitForElementVisible('#app', 5000)
+      .assert.elementPresent('.hello')
+      .assert.containsText('h1', 'Welcome to Your Vue.js App')
+      .assert.elementCount('img', 1)
+      .end()
+  }
+}

+ 7 - 0
test/unit/.eslintrc

@@ -0,0 +1,7 @@
+{
+  "env": { 
+    "jest": true
+  },
+  "globals": { 
+  }
+}

+ 30 - 0
test/unit/jest.conf.js

@@ -0,0 +1,30 @@
+const path = require('path')
+
+module.exports = {
+  rootDir: path.resolve(__dirname, '../../'),
+  moduleFileExtensions: [
+    'js',
+    'json',
+    'vue'
+  ],
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1'
+  },
+  transform: {
+    '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
+    '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
+  },
+  testPathIgnorePatterns: [
+    '<rootDir>/test/e2e'
+  ],
+  snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
+  setupFiles: ['<rootDir>/test/unit/setup'],
+  mapCoverage: true,
+  coverageDirectory: '<rootDir>/test/unit/coverage',
+  collectCoverageFrom: [
+    'src/**/*.{js,vue}',
+    '!src/main.js',
+    '!src/router/index.js',
+    '!**/node_modules/**'
+  ]
+}

+ 3 - 0
test/unit/setup.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+
+Vue.config.productionTip = false

+ 11 - 0
test/unit/specs/HelloWorld.spec.js

@@ -0,0 +1,11 @@
+import Vue from 'vue'
+import HelloWorld from '@/components/HelloWorld'
+
+describe('HelloWorld.vue', () => {
+  it('should render correct contents', () => {
+    const Constructor = Vue.extend(HelloWorld)
+    const vm = new Constructor().$mount()
+    expect(vm.$el.querySelector('.hello h1').textContent)
+      .toEqual('Welcome to Your Vue.js App')
+  })
+})