tremble 4 лет назад
Сommit
a79d8cb2c1
100 измененных файлов с 23064 добавлено и 0 удалено
  1. 12 0
      .babelrc
  2. 9 0
      .editorconfig
  3. 4 0
      .eslintignore
  4. 29 0
      .eslintrc.js
  5. 14 0
      .gitignore
  6. 10 0
      .postcssrc.js
  7. 21 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. 92 0
      build/webpack.base.conf.js
  14. 98 0
      build/webpack.dev.conf.js
  15. 148 0
      build/webpack.prod.conf.js
  16. BIN
      code.rar
  17. 7 0
      config/dev.env.js
  18. 76 0
      config/index.js
  19. 4 0
      config/prod.env.js
  20. 17 0
      index.html
  21. 11716 0
      package-lock.json
  22. 83 0
      package.json
  23. 32 0
      src/App.vue
  24. 76 0
      src/assets/font/iconfont.css
  25. BIN
      src/assets/font/iconfont.eot
  26. 1 0
      src/assets/font/iconfont.js
  27. 128 0
      src/assets/font/iconfont.json
  28. 77 0
      src/assets/font/iconfont.svg
  29. BIN
      src/assets/font/iconfont.ttf
  30. BIN
      src/assets/font/iconfont.woff
  31. BIN
      src/assets/font/iconfont.woff2
  32. BIN
      src/assets/images/icon01.png
  33. BIN
      src/assets/images/icon02.png
  34. BIN
      src/assets/images/icon03.png
  35. BIN
      src/assets/images/icon04.png
  36. BIN
      src/assets/images/icon05.png
  37. BIN
      src/assets/images/icon06.png
  38. BIN
      src/assets/images/icon_mp3.png
  39. BIN
      src/assets/images/img_login_banbg.jpg
  40. BIN
      src/assets/images/index_logo@2x.png
  41. BIN
      src/assets/images/menu_icon_01@2x.png
  42. BIN
      src/assets/images/menu_icon_02@2x.png
  43. BIN
      src/assets/images/menu_icon_03@2x.png
  44. BIN
      src/assets/images/qrcode.png
  45. BIN
      src/assets/images/test.png
  46. 138 0
      src/assets/style/info.scss
  47. 131 0
      src/assets/style/public.scss
  48. 22 0
      src/assets/style/reset.scss
  49. 87 0
      src/components/crumb/index.vue
  50. 21 0
      src/components/dialog/index.vue
  51. 16 0
      src/components/vcenter/index.vue
  52. 28 0
      src/main.js
  53. 537 0
      src/pages/camera/index.vue
  54. 0 0
      src/pages/camera/style.scss
  55. 165 0
      src/pages/data/index.vue
  56. 0 0
      src/pages/data/style.scss
  57. 516 0
      src/pages/department/index.vue
  58. 0 0
      src/pages/department/style.scss
  59. 142 0
      src/pages/home/index.vue
  60. 0 0
      src/pages/home/style.scss
  61. 268 0
      src/pages/layout/aside.vue
  62. 203 0
      src/pages/layout/header/index.vue
  63. 33 0
      src/pages/layout/header/style.scss
  64. 70 0
      src/pages/layout/index.vue
  65. 219 0
      src/pages/login/index.vue
  66. 541 0
      src/pages/myscene/index.vue
  67. 0 0
      src/pages/myscene/style.scss
  68. 318 0
      src/pages/operation/index.vue
  69. 0 0
      src/pages/operation/style.scss
  70. 615 0
      src/pages/question/index.vue
  71. 0 0
      src/pages/question/style.scss
  72. 485 0
      src/pages/recommended/index.vue
  73. 0 0
      src/pages/recommended/style.scss
  74. 669 0
      src/pages/rental-info/index.vue
  75. 0 0
      src/pages/rental-info/style.scss
  76. 790 0
      src/pages/role/index.vue
  77. 0 0
      src/pages/role/style.scss
  78. 335 0
      src/pages/scene/index.vue
  79. 0 0
      src/pages/scene/style.scss
  80. 671 0
      src/pages/sell-info/index.vue
  81. 0 0
      src/pages/sell-info/style.scss
  82. 671 0
      src/pages/site-decoration/index.vue
  83. 0 0
      src/pages/site-decoration/style.scss
  84. 708 0
      src/pages/user/index.vue
  85. 0 0
      src/pages/user/style.scss
  86. 429 0
      src/pages/version/index.vue
  87. 0 0
      src/pages/version/style.scss
  88. 571 0
      src/pages/z-carousel/index.vue
  89. 0 0
      src/pages/z-carousel/style.scss
  90. 90 0
      src/router/index.js
  91. 156 0
      src/utils/base.js
  92. 113 0
      src/utils/http.js
  93. 291 0
      src/utils/myCharts.js
  94. 0 0
      static/.gitkeep
  95. BIN
      static/img/favicon.ico
  96. 7 0
      消安-后台原型0225/data/document.js
  97. 115 0
      消安-后台原型0225/data/styles.css
  98. 7 0
      消安-后台原型0225/files/个人中心/data.js
  99. 14 0
      消安-后台原型0225/files/个人中心/styles.css
  100. 0 0
      消安-后台原型0225/files/场景拼接(此页交互待定)/data.js

+ 12 - 0
.babelrc

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

+ 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

+ 4 - 0
.eslintignore

@@ -0,0 +1,4 @@
+/build/
+/config/
+/dist/
+/*.js

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

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+.DS_Store
+node_modules/
+/dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.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": {}
+  }
+}

+ 21 - 0
README.md

@@ -0,0 +1,21 @@
+# house
+
+> A Vue.js project
+
+## Build Setup
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
+
+For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).

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

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

@@ -0,0 +1,92 @@
+'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: 1024*1024*10,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 1024*1024*10,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 1024*1024*10,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  },
+  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'
+  }
+}

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

@@ -0,0 +1,98 @@
+'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.ProvidePlugin({
+      'window.Quill': 'quill'
+    }),
+    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)
+    }
+  })
+})

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

@@ -0,0 +1,148 @@
+'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 = 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 webpack.ProvidePlugin({
+      'window.Quill': 'quill'
+    }),
+    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: 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"'
+})

+ 76 - 0
config/index.js

@@ -0,0 +1,76 @@
+'use strict'
+// Template version: 1.3.1
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+
+module.exports = {
+  dev: {
+
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {},
+
+    // 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: true,
+    // If true, eslint errors and warnings will also be shown in the error overlay
+    // in the browser.
+    showEslintErrorsInOverlay: false,
+
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: 'cheap-module-eval-source-map',
+
+    // If you have problems debugging vue-files in devtools,
+    // set this to false - it *may* help
+    // https://vue-loader.vuejs.org/en/options.html#cachebusting
+    cacheBusting: true,
+
+    cssSourceMap: true
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: './',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: true,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: '#source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  }
+}

+ 4 - 0
config/prod.env.js

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

+ 17 - 0
index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="renderer" content="webkit">
+    <meta name="force-rendering" content="webkit"/>
+    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
+    <link rel="apple-touch-icon" sizes="180x180" href="static/img/favicon.ico">
+    <link rel="icon" type="image/png" href="static/img/favicon.ico" sizes="192x192">
+    <title>后台管理系统</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

Разница между файлами не показана из-за своего большого размера
+ 11716 - 0
package-lock.json


+ 83 - 0
package.json

@@ -0,0 +1,83 @@
+{
+  "name": "house",
+  "version": "1.0.0",
+  "description": "A Vue.js project",
+  "author": "tremble <290487845@qq.com>",
+  "private": true,
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "start": "npm run dev",
+    "lint": "eslint --ext .js,.vue src",
+    "build": "node build/build.js"
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "element-ui": "^2.4.11",
+    "quill-image-drop-module": "^1.0.3",
+    "quill-image-resize-module": "^3.0.0",
+    "vue": "^2.5.2",
+    "vue-quill-editor": "^3.0.6",
+    "vue-quill-editor-upload": "^1.1.0",
+    "vue-router": "^3.0.1"
+  },
+  "devDependencies": {
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-eslint": "^8.2.1",
+    "babel-helper-vue-jsx-merge-props": "^2.0.3",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.5.0",
+    "babel-polyfill": "^6.26.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "chalk": "^2.0.1",
+    "copy-webpack-plugin": "^4.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",
+    "node-notifier": "^5.1.2",
+    "node-sass": "^4.10.0",
+    "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",
+    "sass-loader": "^7.1.0",
+    "sass-resources-loader": "^2.0.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "uglifyjs-webpack-plugin": "^1.1.1",
+    "url-loader": "^0.5.8",
+    "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"
+  ]
+}

+ 32 - 0
src/App.vue

@@ -0,0 +1,32 @@
+<template>
+<div id="app">
+  <router-view/>
+</div>
+</template>
+
+<script>
+import '@/assets/style/reset.scss'
+import '@/assets/style/public.scss'
+import '@/assets/font/iconfont.css'
+
+export default {
+  name: 'App',
+  components: {
+
+  },
+  data () {
+    return {
+
+    }
+  },
+  mounted () {
+    if (!window.localStorage.getItem('menu')) {
+      window.localStorage.setItem('menu', '[]')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

Разница между файлами не показана из-за своего большого размера
+ 76 - 0
src/assets/font/iconfont.css


BIN
src/assets/font/iconfont.eot


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
src/assets/font/iconfont.js


+ 128 - 0
src/assets/font/iconfont.json

@@ -0,0 +1,128 @@
+{
+  "id": "1713010",
+  "name": "消安管理后台",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "3232662",
+      "name": "正确 copy",
+      "font_class": "zhengquecopy",
+      "unicode": "e687",
+      "unicode_decimal": 59015
+    },
+    {
+      "icon_id": "4647621",
+      "name": "关闭",
+      "font_class": "guanbi",
+      "unicode": "e61a",
+      "unicode_decimal": 58906
+    },
+    {
+      "icon_id": "5093350",
+      "name": "重置",
+      "font_class": "zhongzhi",
+      "unicode": "e609",
+      "unicode_decimal": 58889
+    },
+    {
+      "icon_id": "7025373",
+      "name": "退出",
+      "font_class": "tuichu",
+      "unicode": "e611",
+      "unicode_decimal": 58897
+    },
+    {
+      "icon_id": "7025374",
+      "name": "下拉",
+      "font_class": "xl",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "7040427",
+      "name": "下载",
+      "font_class": "tubiaozhizuomoban",
+      "unicode": "e613",
+      "unicode_decimal": 58899
+    },
+    {
+      "icon_id": "8747856",
+      "name": "增加",
+      "font_class": "add",
+      "unicode": "e648",
+      "unicode_decimal": 58952
+    },
+    {
+      "icon_id": "10726198",
+      "name": "icon_mp3",
+      "font_class": "icon_mp",
+      "unicode": "e653",
+      "unicode_decimal": 58963
+    },
+    {
+      "icon_id": "13682089",
+      "name": "per",
+      "font_class": "per",
+      "unicode": "e6bd",
+      "unicode_decimal": 59069
+    },
+    {
+      "icon_id": "13682090",
+      "name": "index",
+      "font_class": "index",
+      "unicode": "e6be",
+      "unicode_decimal": 59070
+    },
+    {
+      "icon_id": "13682091",
+      "name": "record",
+      "font_class": "record",
+      "unicode": "e6bf",
+      "unicode_decimal": 59071
+    },
+    {
+      "icon_id": "13682092",
+      "name": "power",
+      "font_class": "power",
+      "unicode": "e6c0",
+      "unicode_decimal": 59072
+    },
+    {
+      "icon_id": "13682093",
+      "name": "scene",
+      "font_class": "scene",
+      "unicode": "e6c1",
+      "unicode_decimal": 59073
+    },
+    {
+      "icon_id": "13682094",
+      "name": "feedback",
+      "font_class": "feedback",
+      "unicode": "e6c2",
+      "unicode_decimal": 59074
+    },
+    {
+      "icon_id": "13682095",
+      "name": "department",
+      "font_class": "department",
+      "unicode": "e6c3",
+      "unicode_decimal": 59075
+    },
+    {
+      "icon_id": "13682096",
+      "name": "statistics",
+      "font_class": "statistics",
+      "unicode": "e6c4",
+      "unicode_decimal": 59076
+    },
+    {
+      "icon_id": "13682097",
+      "name": "user",
+      "font_class": "user",
+      "unicode": "e6c5",
+      "unicode_decimal": 59077
+    }
+  ]
+}

Разница между файлами не показана из-за своего большого размера
+ 77 - 0
src/assets/font/iconfont.svg


BIN
src/assets/font/iconfont.ttf


BIN
src/assets/font/iconfont.woff


BIN
src/assets/font/iconfont.woff2


BIN
src/assets/images/icon01.png


BIN
src/assets/images/icon02.png


BIN
src/assets/images/icon03.png


BIN
src/assets/images/icon04.png


BIN
src/assets/images/icon05.png


BIN
src/assets/images/icon06.png


BIN
src/assets/images/icon_mp3.png


BIN
src/assets/images/img_login_banbg.jpg


BIN
src/assets/images/index_logo@2x.png


BIN
src/assets/images/menu_icon_01@2x.png


BIN
src/assets/images/menu_icon_02@2x.png


BIN
src/assets/images/menu_icon_03@2x.png


BIN
src/assets/images/qrcode.png


BIN
src/assets/images/test.png


+ 138 - 0
src/assets/style/info.scss

@@ -0,0 +1,138 @@
+.con {
+  .d-l{
+    // & /deep/ .el-form-item__content{
+    //   line-height: 30px;
+    //   .el-upload-list__item{
+    //     display: none;
+    //     &:last-child{
+    //       display: inline-block;
+    //     }
+    //   }
+    // }
+    display: inline-block;
+    width:45%;
+  }
+  .d-r{
+    width:45%;
+    float: right;
+    .el-date-editor.el-input, .el-date-editor.el-input__inner{
+      width: 100%;
+    }
+    .c-logo{
+      width: 150px;
+      height: 150px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      background-color: #f5fafe;
+      img{
+        width: 100%;
+        height: 100%;
+      }
+    }
+    .c-zizhi{
+      width: 150px;
+      height: 150px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      position: relative;
+      background-color: #f5fafe;
+      img{
+        width: 100%;
+        height: 100%;
+      }
+      .c-mask{
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        top: 0;
+        left: 0;
+        background: rgba(0, 0, 0, 0.5);
+        text-align: center;
+        display: table;
+        .icon-download{
+          text-align: center;
+          display: table-cell;
+          vertical-align: middle;
+          color: #fff;
+          font-size: 20px;
+        }
+      }
+    }
+  }
+  .ei-num{
+    .el-input-number{
+      width: 90%;
+    }
+  }
+  .ei-input{
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con{
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err{
+        & /deep/ .el-input__inner{
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success{
+        & /deep/ .el-input__inner{
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+
+  }
+ .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    div{
+      display: inline-block;
+      vertical-align: middle;
+    }
+    .h-input {
+      margin-left: 20px;
+      display: inline-block;
+    }
+    .fix-date{
+      div{
+        display: flex;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+}

+ 131 - 0
src/assets/style/public.scss

@@ -0,0 +1,131 @@
+#app, .layout, body, html{
+  width: 100%;
+  height: 100%;
+  color: #333;
+}
+table tr td .cell,table tr th .cell{
+  text-align: center;
+}
+
+::-webkit-scrollbar {
+  width: 5px;
+  height: 16px;
+  background-color: transparent;
+}
+
+::-webkit-scrollbar-track{
+    border-radius: 10px;
+    background-color: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.2);
+  background-color: #d4d4d4;
+}
+
+.zfb-success{
+  color: #fff;
+  background-color: #67c23a;
+  border: 1px solid #67c23a;
+  padding: 2px 10px;
+  display: inline-block;
+  border-radius: 2px;
+}
+.zfb-shenhe{
+  color: #0175dc;
+  background-color: #fff;
+  border: 1px solid #0175dc;
+  padding: 2px 10px;
+  border-radius: 2px;
+  display: inline-block;
+}
+.zfb-reject{
+  color: #fff;
+  background-color: #e6a23e;
+  border: 1px solid #e6a23e;
+  padding: 2px 10px;
+  display: inline-block;
+  border-radius: 2px;
+}
+
+.zfb-guanbi{
+  color: #fff;
+  background-color: #ccc;
+  border: 1px solid #ccc;
+  padding: 2px 10px;
+  display: inline-block;
+  border-radius: 2px;
+}
+
+
+.zfb-upload{
+  position: relative;
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  width: 164px;
+  height: 164px;
+  line-height: 164px;
+  background-color: #f9fcff;
+  display: inline-block;
+  overflow: hidden;
+  img{
+    width: 162px;
+    height: 162px;
+    border-radius: 6px;
+  }
+  .v-mask{
+    display: none;
+    position: absolute;
+    top: 0;
+    left: 0;
+  }
+  &:hover{
+    border-color: #409eff;
+    .v-mask{
+      display: inline-block;
+      width: 162px;
+      height: 162px;
+      border-radius: 6px;
+      background: rgba(0,0,0,0.5);
+    
+    }
+  }
+  .el-icon-plus {
+    position: relative;
+    top: -15px;
+    font-size: 20px;
+  }
+  .zfb-upload-text{
+    position: absolute;
+    top: 50%;
+    left: 0;
+    width: 100%;
+    line-height: 16px;
+    span{
+      color: #999;
+      font-size: 12px;
+      line-height: 1.5;
+    }
+  }
+}
+
+.el-select{
+  width: 100%;
+}
+
+.c-input-con{
+  position: relative;
+  .hover-tip{
+    position: absolute;
+    right: 10px;
+    top: 0;
+  }
+}
+
+.quill-editor:not(.bubble) .ql-container,
+.quill-editor:not(.bubble) .ql-container .ql-editor {
+    height: 20rem;
+    padding-bottom: 1rem;
+    margin-bottom: 30px;
+  }

+ 22 - 0
src/assets/style/reset.scss

@@ -0,0 +1,22 @@
+/* reset */
+html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0;}
+header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block;}
+table{border-collapse:collapse;border-spacing:0;}
+caption,th{text-align:left;font-weight:normal;}
+html,body,fieldset,img,iframe,abbr{border:0;}
+i,cite,em,var,address,dfn{font-style:normal;}
+[hidefocus],summary{outline:0;}
+li{list-style:none;}
+// h1,h2,h3,h4,h5,h6,small{font-size:100%;}
+sup,sub{font-size:83%;}
+pre,code,kbd,samp{font-family:inherit;}
+q:before,q:after{content:none;}
+textarea{overflow:auto;resize:none;}
+label,summary{cursor:default;}
+a,button{cursor:pointer;}
+h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold;}
+del,ins,u,s,a,a:hover{text-decoration:none;}
+body,textarea,input,button,select,keygen,legend{font:14px/1.14 'Microsoft YaHei',\5b8b\4f53;outline:0;}
+body{background:#fff;}
+*{box-sizing: border-box}
+a{text-decoration: none;}

+ 87 - 0
src/components/crumb/index.vue

@@ -0,0 +1,87 @@
+<!--  -->
+<template>
+<div class='crumb-con'>
+  <vcenter>
+    <span>{{title}}</span>
+    <div class="fr">
+      <div v-for="(item,i) in data" :key="i">
+        <span style="cursor:pointer">{{item.name}}</span>
+        <span style="padding-right:3px;" v-if="data.length-1!==i">></span>
+      </div>
+    </div>
+  </vcenter>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+
+export default {
+// import引入的组件需要注入到对象中才能使用
+  props: {
+    data: {
+      default: () => {
+        return []
+      },
+      type: Array
+    },
+    title: {
+      default: () => {
+        return ''
+      },
+      type: String
+    }
+  },
+  components: {vcenter},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang="scss" scoped>
+.crumb-con{
+  height: 60px;
+  width: 100%;
+  background-color: #fff;
+  box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+  font-size: 18px;
+  padding: 0 25px;
+  .fr{
+    color: #999;
+    float: right;
+    div{
+      display: inline-block;
+      font-size: 14px;
+    }
+  }
+}
+
+</style>

+ 21 - 0
src/components/dialog/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <div>
+    <el-dialog :close-on-click-modal='false'  title="收货地址" :visible.sync="dialogFormVisible">
+      <el-form :model="form">
+        <el-form-item label="活动名称" :label-width="formLabelWidth">
+          <el-input v-model="form.name" autocomplete="off"></el-input>
+        </el-form-item>
+        <el-form-item label="活动区域" :label-width="formLabelWidth">
+          <el-select v-model="form.region" placeholder="请选择活动区域">
+            <el-option label="区域一" value="shanghai"></el-option>
+            <el-option label="区域二" value="beijing"></el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>

+ 16 - 0
src/components/vcenter/index.vue

@@ -0,0 +1,16 @@
+<template>
+  <table>
+    <tr>
+      <td>
+        <slot />
+      </td>
+    </tr>
+  </table>
+</template>
+
+<style scoped>
+table {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 28 - 0
src/main.js

@@ -0,0 +1,28 @@
+// The Vue build version to load with the `import` command
+// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
+import 'babel-polyfill'
+import Vue from 'vue'
+import App from './App'
+import router from './router'
+import myCharts from '@/utils/myCharts.js'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import {axios, serverName} from './utils/http'
+import {base} from './utils/base'
+
+Vue.config.productionTip = false
+
+Vue.prototype.$http = axios
+Vue.prototype.$base = base
+Vue.prototype.$serverName = serverName.replace('xiaoan/', '')
+Vue.prototype.$bus = new Vue()
+
+Vue.use(myCharts)
+Vue.use(ElementUI)
+/* eslint-disable no-new */
+new Vue({
+  el: '#app',
+  router,
+  components: { App },
+  template: '<App/>'
+})

+ 537 - 0
src/pages/camera/index.vue

@@ -0,0 +1,537 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div>
+      <el-dialog :close-on-click-modal='false'  title="新增相机" width="35%" center :visible.sync="dialogFormVisible">
+        <el-form :model="form">
+          <el-form-item label="相机类型" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input disabled v-model="form.typeName" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+          <el-form-item label="SN号" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.wifiName" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+          <!-- <el-form-item label="相机MAC" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.childName" autocomplete="off"></el-input>
+            </div>
+          </el-form-item> -->
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="dialogFormVisible = false">取 消</el-button>
+          <el-button type="primary" @click="addItem">确 定</el-button>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="h-header">
+      <vcenter>
+        <div>
+          关键字:
+        </div>
+        <div class="h-input">
+          <el-input v-model="inpuKey" placeholder="输入相机SN码"></el-input>
+        </div>
+        <div>
+          <el-button type="primary" @click="search(inpuKey)">查询</el-button>
+          <!-- <el-button>导出</el-button> -->
+        </div>
+      </vcenter>
+    </div>
+    <div class="h-body">
+      <div style="padding:0 20px;">
+        <el-button @click="add" v-if="authority['新增相机']" type="primary">新增相机</el-button>
+      </div>
+      <el-table @selection-change="handleSelectionChange" height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+        <!-- <el-table-column
+          type="selection"
+          width="55">
+        </el-table-column> -->
+        <el-table-column
+          v-for="(item,i) in tableHeader"
+          :key="i"
+          :prop="item.name"
+          :label="item.label"
+        >
+          <template slot-scope="scope">
+            <span>{{scope.row[item.name]}}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+           <span class="o-span" v-if="authority['编辑相机']" @click="showDialog(scope.row)">编辑</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="p-con">
+        <el-pagination
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page.sync="currentPage"
+          :page-size="size"
+          layout="prev, pager, next, jumper"
+          :total="total"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+// import { serverName } from '@/utils/http'
+
+const TYPENAME = {
+  0: '双目相机',
+  1: '四维看看Pro八目相机(静谧黑)',
+  2: '商业八目',
+  3: '企业八目'
+}
+
+const tableHeader = [
+  {
+    name: 'typeName',
+    label: '设备类型'
+  },
+  {
+    name: 'wifiName',
+    label: '相机SN码'
+  },
+  // {
+  //   name: 'childName',
+  //   label: '相机MAC'
+  // },
+  {
+    name: 'createTime',
+    label: '添加时间'
+  }
+]
+
+const authority = {
+  '新增相机': true,
+  '编辑相机': true
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      dialogFormVisible: false,
+      formLabelWidth: '100px',
+      authority,
+      form: {
+        typeName: TYPENAME[1],
+        // childName: '',
+        wifiName: ''
+      },
+      tableData: [],
+      tableHeader,
+      multipleSelection: [],
+      currentItem: '',
+      size: 10,
+      total: 0,
+      loading: false,
+      currentPage: 1,
+      inpuKey: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    handleSelectionChange (val) {
+      this.multipleSelection = val
+    },
+
+    showDialog (item) {
+      this.dialogFormVisible = true
+      this.form = {
+        typeName: TYPENAME[item.type],
+        // childName: item.childName,
+        wifiName: item.wifiName
+      }
+
+      this.currentItem = item
+    },
+
+    clearInfo () {
+      this.form = {
+        typeName: TYPENAME[1],
+        // childName: '',
+        wifiName: ''
+      }
+    },
+
+    add () {
+      this.dialogFormVisible = true
+      this.currentItem = ''
+      this.clearInfo()
+    },
+
+    async addItem () {
+      let parmas = this.form
+      if (this.currentItem) {
+        parmas['id'] = this.currentItem.id
+      }
+      let res = await this.$http({
+        method: 'post',
+        data: parmas,
+        url: '/api/manage/center/camera/save',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert(this.currentItem ? '修改成功' : '添加成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    del () {
+      let parmas = []
+      if (this.multipleSelection.length <= 0) {
+        return
+      }
+      this.$confirm('此操作将会删除该选项, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        this.multipleSelection.forEach(item => {
+          parmas.push(item.id)
+        })
+
+        let res = await this.$http({
+          method: 'get',
+          data: parmas,
+          url: '/api/manage/department/delete/all/' + parmas.join(','),
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('删除成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    search (item) {
+      this.searchKey = item
+      this.refresh()
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/center/camera/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      result.data.list.forEach(item => {
+        item['typeName'] = TYPENAME[item['type']]
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    console.log(this.func)
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .time-data{
+    position: relative;
+    .select-data{
+      position: absolute;
+      left: 0;
+      top: 0;
+      opacity: 0;
+      cursor: pointer;
+    }
+    >span{
+      color: #0175dc;
+    }
+  }
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+  .clear-music {
+    position: relative;
+    text-align: right;
+    top: -30px;
+    color: #999;
+    cursor: pointer;
+  }
+  .musicIcon {
+    width: 100%;
+    height: 100%;
+    line-height: 1;
+    margin-top: 35px;
+    img {
+      width: 50px;
+      height: auto;
+    }
+    p {
+      font-weight: bold;
+      font-size: 14px;
+      color: #000;
+      line-height: 20px;
+      height: 20px;
+      margin-top: 20px;
+    }
+  }
+  .icon-plus {
+    margin-left: 15px;
+    width: 40px;
+    height: 38px;
+    line-height: 38px;
+    text-align: center;
+    background: #f5f7fa;
+    color: #999;
+    cursor: pointer;
+    font-size: 13px;
+    display: inline-block;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    &:hover {
+      border: 1px solid #c0c4cc;
+    }
+  }
+  .add-num {
+    margin-left: 15px;
+    width: 250px;
+    .icon-close,
+    .icon-correct {
+      line-height: 1;
+      color: #999;
+    }
+    input[type="number"]::-webkit-inner-spin-button,
+    input[type="number"]::-webkit-outer-spin-button {
+      -webkit-appearance: none;
+      margin: 0;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+  }
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+    }
+
+    .h-input {
+      width: 220px;
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .logo-add {
+      div {
+        cursor: pointer;
+        line-height: 40px;
+        border: 1px dotted #dcdfe6;
+        border-radius: 4px;
+        font-weight: bold;
+        font-size: 18px;
+        display: inline-block;
+        width: 40px;
+        height: 40px;
+        background: #f5f7fa;
+        position: relative;
+        img {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+        }
+        span {
+          position: absolute;
+          z-index: 100;
+          left: 0;
+          top: 0;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+
+  .d-table {
+    border: 1px solid #ccc;
+    .d-con {
+      max-height: 200px;
+      overflow-y: scroll;
+    }
+    .d-header {
+      font-weight: bold;
+    }
+    .d-header,
+    .d-body {
+      height: 40px;
+      line-height: 40px;
+      width: 100%;
+      border-bottom: 1px solid #ccc;
+      &:last-child {
+        border: none;
+      }
+      span {
+        text-align: center;
+        display: inline-block;
+        width: 24%;
+
+      }
+      .unbind{
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/camera/style.scss


+ 165 - 0
src/pages/data/index.vue

@@ -0,0 +1,165 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div class="h-body">
+      <section>
+        <div>
+          <div class="title">实时概况 <span> 更新时间:{{$base.dateFormat('yyyy-MM-dd hh:mm', new Date())}}</span></div>
+          <ul class="data-ul">
+            <li v-for="(item,i) in list" :key="i">
+              <div>{{item.name}}</div>
+              <div>{{item.data}}</div>
+            </li>
+          </ul>
+        </div>
+      </section>
+
+      <section>
+        <div class="title">数据看板</div>
+        <div class="data-chart">
+          <div id="wwqs"></div>
+          <div id="bar"></div>
+        </div>
+      </section>
+
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  methods: {
+    async getData () {
+      let result = await this.$http({
+        method: 'get',
+        url: '/api/manage/statistics',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.data = result.data
+
+      this.list.forEach(item => {
+        item['data'] = this.data[item.id]
+      })
+
+      this.data.systemTop.forEach(item => {
+        this.sysTop.name.push(item.realName)
+        this.sysTop.data.push(item.viewCount)
+      })
+
+      this.data.sceneTop.forEach(item => {
+        this.senceTop.name.push(item.sceneName)
+        this.senceTop.data.push(item.viewCount)
+      })
+
+      this.$chart.bar(
+        'wwqs',
+        this.sysTop
+      )
+      this.$chart.bar(
+        'bar',
+        this.senceTop
+      )
+    }
+  },
+  data () {
+    let color = ['#e74a46', '#ffb535', '#f3e652', '#429df2', '#33ca9d']
+    return {
+      loading: false,
+      data: [],
+      sysTop: {
+        name: [],
+        data: [],
+        color: color,
+        xName: '姓名',
+        yName: '系统使用TOP5'
+      },
+      senceTop: {
+        name: [],
+        color: color,
+        data: [],
+        xName: '场景名称',
+        yName: '场景浏览TOP5'
+      },
+      list: [{
+        name: '使用人次',
+        id: 'systemViewCount',
+        data: '-'
+      }, {
+        name: '场景个数',
+        id: 'sceneCount',
+        data: '-'
+      }, {
+        name: '系统用户数',
+        id: 'systemUserCount',
+        data: '-'
+      }, {
+        name: '场景浏览量',
+        id: 'sceneViewCount',
+        data: '-'
+      }]
+    }
+  },
+  mounted () {
+    this.getData()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .h-body {
+    width: 100%;
+    padding: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+  }
+  .data-chart{
+    display: flex;
+    justify-content: space-between;
+  }
+  .data-ul{
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: wrap;
+    margin-bottom: 20px;
+    li{
+      width: 50%;
+      margin: 20px 0;
+      font-size: 24px;
+      color: rgba(0, 0, 0, 0.5);
+      div:last-of-type{
+        font-size: 30px;
+        font-weight: bold;
+        margin-top: 20px;
+        color: #000;
+      }
+    }
+  }
+  .title{
+    font-size: 30px;
+    font-weight: bold;
+    span{
+      font-size: 16px;
+      font-weight: normal;
+    }
+  }
+
+  #wwqs,#bar{
+    flex: 1;
+    height: 400px;
+    display: inline-block;
+  }
+}
+</style>

+ 0 - 0
src/pages/data/style.scss


+ 516 - 0
src/pages/department/index.vue

@@ -0,0 +1,516 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div>
+      <el-dialog :close-on-click-modal='false'  :title="currentItem ? '修改部门' : '新增部门'" width="35%" center :visible.sync="dialogFormVisible">
+        <el-form :model="form">
+          <!-- <el-form-item label="部门代号" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.num" autocomplete="off"></el-input>
+            </div>
+          </el-form-item> -->
+
+          <el-form-item label="部门名称" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.name" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="dialogFormVisible = false">取 消</el-button>
+          <el-button type="primary" @click="addItem">确 定</el-button>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="h-header">
+      <vcenter>
+        <div>
+          关键字:
+        </div>
+        <div class="h-input">
+          <el-input v-model="inpuKey" placeholder="输入部门名称"></el-input>
+        </div>
+        <div>
+          <el-button type="primary" @click="search(inpuKey)">查询</el-button>
+          <!-- <el-button>导出</el-button> -->
+        </div>
+      </vcenter>
+    </div>
+    <div class="h-body">
+      <div style="padding:0 20px;">
+        <el-button @click="add" type="primary" v-if="authority['部门新增']">新增部门</el-button>
+        <el-button @click="del" v-if="authority['部门删除']">删除</el-button>
+      </div>
+      <el-table @selection-change="handleSelectionChange" height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+        <el-table-column
+          type="selection"
+          width="55">
+        </el-table-column>
+        <el-table-column
+          v-for="(item,i) in tableHeader"
+          :key="i"
+          :prop="item.name"
+          :label="item.label"
+        >
+          <template slot-scope="scope">
+            <span>{{scope.row[item.name]}}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+           <span class="o-span" v-if="authority['部门新增']" @click="showDialog(scope.row)">编辑</span>
+           <span v-else>-</span>
+
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="p-con">
+        <el-pagination
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page.sync="currentPage"
+          :page-size="size"
+          layout="prev, pager, next, jumper"
+          :total="total"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+// import { serverName } from '@/utils/http'
+
+const tableHeader = [
+  // {
+  //   name: 'num',
+  //   label: '部门代号'
+  // },
+  {
+    name: 'name',
+    label: '部门名称'
+  },
+  {
+    name: 'updateTime',
+    label: '创建时间'
+  }
+]
+
+const authority = {
+  '部门新增': true,
+  '部门删除': true
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      authority,
+      dialogFormVisible: false,
+      formLabelWidth: '100px',
+      form: {
+        name: ''
+        // num: ''
+      },
+      tableData: [],
+      tableHeader,
+      multipleSelection: [],
+      currentItem: '',
+      size: 10,
+      total: 0,
+      loading: false,
+      currentPage: 1,
+      inpuKey: '',
+      func: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    handleSelectionChange (val) {
+      this.multipleSelection = val
+    },
+
+    showDialog (item) {
+      this.dialogFormVisible = true
+      this.form = {
+        name: item.name,
+        // num: item.num,
+        id: item.id
+      }
+
+      this.currentItem = item
+    },
+
+    clearInfo () {
+      this.form = {
+        name: ''
+        // num: ''
+      }
+    },
+
+    add () {
+      this.dialogFormVisible = true
+      this.currentItem = ''
+      this.clearInfo()
+    },
+
+    async addItem () {
+      let parmas = this.form
+      if (this.currentItem) {
+        parmas['id'] = this.currentItem.id
+      }
+      let res = await this.$http({
+        method: 'post',
+        data: parmas,
+        url: '/api/manage/department/save',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert(this.currentItem ? '修改成功' : '添加成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    del () {
+      let parmas = []
+      if (this.multipleSelection.length <= 0) {
+        return
+      }
+      this.$confirm('此操作将会删除该选项, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        this.multipleSelection.forEach(item => {
+          parmas.push(item.id)
+        })
+
+        let res = await this.$http({
+          method: 'get',
+          data: parmas,
+          url: '/api/manage/department/delete/all/' + parmas.join(','),
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('删除成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    search (item) {
+      this.searchKey = item
+      this.refresh()
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/department/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+      this.tableData = result.data.list || []
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .time-data{
+    position: relative;
+    .select-data{
+      position: absolute;
+      left: 0;
+      top: 0;
+      opacity: 0;
+      cursor: pointer;
+    }
+    >span{
+      color: #0175dc;
+    }
+  }
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+  .clear-music {
+    position: relative;
+    text-align: right;
+    top: -30px;
+    color: #999;
+    cursor: pointer;
+  }
+  .musicIcon {
+    width: 100%;
+    height: 100%;
+    line-height: 1;
+    margin-top: 35px;
+    img {
+      width: 50px;
+      height: auto;
+    }
+    p {
+      font-weight: bold;
+      font-size: 14px;
+      color: #000;
+      line-height: 20px;
+      height: 20px;
+      margin-top: 20px;
+    }
+  }
+  .icon-plus {
+    margin-left: 15px;
+    width: 40px;
+    height: 38px;
+    line-height: 38px;
+    text-align: center;
+    background: #f5f7fa;
+    color: #999;
+    cursor: pointer;
+    font-size: 13px;
+    display: inline-block;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    &:hover {
+      border: 1px solid #c0c4cc;
+    }
+  }
+  .add-num {
+    margin-left: 15px;
+    width: 250px;
+    .icon-close,
+    .icon-correct {
+      line-height: 1;
+      color: #999;
+    }
+    input[type="number"]::-webkit-inner-spin-button,
+    input[type="number"]::-webkit-outer-spin-button {
+      -webkit-appearance: none;
+      margin: 0;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+  }
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+    }
+
+    .h-input {
+      width: 220px;
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .logo-add {
+      div {
+        cursor: pointer;
+        line-height: 40px;
+        border: 1px dotted #dcdfe6;
+        border-radius: 4px;
+        font-weight: bold;
+        font-size: 18px;
+        display: inline-block;
+        width: 40px;
+        height: 40px;
+        background: #f5f7fa;
+        position: relative;
+        img {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+        }
+        span {
+          position: absolute;
+          z-index: 100;
+          left: 0;
+          top: 0;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+
+  .d-table {
+    border: 1px solid #ccc;
+    .d-con {
+      max-height: 200px;
+      overflow-y: scroll;
+    }
+    .d-header {
+      font-weight: bold;
+    }
+    .d-header,
+    .d-body {
+      height: 40px;
+      line-height: 40px;
+      width: 100%;
+      border-bottom: 1px solid #ccc;
+      &:last-child {
+        border: none;
+      }
+      span {
+        text-align: center;
+        display: inline-block;
+        width: 24%;
+
+      }
+      .unbind{
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/department/style.scss


+ 142 - 0
src/pages/home/index.vue

@@ -0,0 +1,142 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div class="h-body">
+      <ul>
+        <li @click="handleItem(item)" v-for="(item, i) in lists" :key="i">
+          <img :src="require(`@/assets/images/${item.img}.png`)" alt="">
+          <div>{{item.name}}</div>
+        </li>
+      </ul>
+      <el-button @mouseenter.native="qrcode = true" @mouseleave.native="qrcode = false" class="download-btn" type="primary">
+        <span>APP下载</span>
+      </el-button>
+      <img v-if="qrcode" :src="require(`@/assets/images/qrcode.png`)" alt="">
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+
+const lists = [
+  {
+    name: '我的场景',
+    img: 'icon01',
+    id: '9-1',
+    url: '/myscene'
+  },
+  {
+    name: '问题反馈',
+    url: '/question',
+    img: 'icon02',
+    id: ''
+  },
+  {
+    name: '数据统计',
+    url: '/data',
+    img: 'icon03',
+    id: ''
+  },
+  {
+    name: '用户管理',
+    url: '/user',
+    img: 'icon04',
+    id: ''
+  },
+  {
+    name: '我的相机',
+    url: '/camera',
+    img: 'icon05',
+    id: ''
+  },
+  {
+    name: '操作日志',
+    url: '/operation',
+    img: 'icon06',
+    id: ''
+  }
+]
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    return {
+      lists,
+      loading: false,
+      qrcode: false
+    }
+  },
+  mounted () {
+
+  },
+  methods: {
+    handleItem (item) {
+      let Items = JSON.parse(window.localStorage.getItem('menu')) || []
+      Items.forEach(it => {
+        if (it.name === item.name) {
+          if (it['authority']) {
+            window.localStorage.setItem('currentItem', JSON.stringify(it))
+            this.$router.push({path: item.url})
+          } else {
+            return this.$alert('对不起,您还未获得访问权限,如需访问请联系管理员开通', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {}
+            })
+          }
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .h-body {
+    width: 100%;
+    padding: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+    position: relative;
+    .download-btn{
+      position: absolute;
+      top: 10px;
+      right: 20px;
+    }
+
+    >img{
+      position: absolute;
+      max-width: 200px;
+      top: 55px;
+      right: 0px;
+    }
+
+    ul{
+      width: 100%;
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: space-between;
+      text-align: center;
+      padding: 2% 5%;
+      li{
+        width: 30%;
+        margin-bottom: 6%;
+        cursor: pointer;
+        >img{
+          width: 50%;
+        }
+        div{
+          margin-top: 10px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/home/style.scss


+ 268 - 0
src/pages/layout/aside.vue

@@ -0,0 +1,268 @@
+<template>
+<div class='aside'>
+  <div class="nav-menu">
+    <div class="navigator_menu">
+      <vcenter>
+        <span>导航面板</span>
+      </vcenter>
+    </div>
+    <ul class="aside-ul">
+      <li class="aside-li" v-for="(item,i) in aside" :key="i">
+        <div @click="clickNav(item)" :class="{'aside-li-div':true,'active':item.id&&item.id===activeIdx}" >
+          <vcenter>
+            <i class="iconfont" :class="item.icon" style="float:left;width:27px;"></i>
+            <span style="vertical-align: middle;">{{item.name}}</span>
+            <i v-if="item.subItem" style="font-size:12px;vertical-align: middle;position:relative;top:2px;" :class="{'iconfont':true, 'icon-arrow':true,'fr':true,rotate:item.isShow}"></i>
+          </vcenter>
+        </div>
+        <ul class="li-ul" v-if="item.subItem" :style="{maxHeight:item.isShow?'260px':'0'}">
+          <li @click="navigate(sub)" v-for="(sub, idx) in item.subItem" :class="{'active':sub.id&&sub.id===activeIdx}" :key="idx">
+            <vcenter>
+              <span>{{sub.name}}</span>
+              <span v-if="sub.id==='1-2' && a_tips" class="aside-tip">{{a_tips}}</span>
+            </vcenter>
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </div>
+</div>
+</template>
+
+<script>
+import vcenter from '@/components/vcenter'
+
+const aside = [{
+  name: '首页',
+  id: '1',
+  icon: 'iconindex',
+  url: '/'
+},
+{
+  name: '部门信息',
+  id: '2',
+  icon: 'icondepartment',
+  url: '/department'
+},
+{
+  name: '用户管理',
+  id: '3',
+  icon: 'iconuser',
+  url: '/user'
+}, {
+  name: '角色权限',
+  id: '4',
+  icon: 'iconper',
+  url: '/role'
+}, {
+  name: '场景管理',
+  id: '5',
+  icon: 'iconscene',
+  url: '/scene'
+}, {
+  name: '数据统计',
+  url: '/data',
+  id: '6',
+  icon: 'iconstatistics'
+}, {
+  name: '操作日志',
+  url: '/operation',
+  id: '7',
+  icon: 'iconrecord'
+}, {
+  name: '问题反馈',
+  url: '/question',
+  id: '8',
+  icon: 'iconfeedback'
+},
+{
+  name: '版本管理',
+  url: '/version',
+  id: '10',
+  icon: 'iconstatistics'
+},
+{
+  name: '个人中心',
+  id: '',
+  isShow: false,
+  icon: 'iconpower',
+  subItem: [{
+    name: '我的场景',
+    id: '9-1',
+    url: '/myscene'
+  }, {
+    name: '我的相机',
+    id: '9-2',
+    url: '/camera'
+  }]
+}]
+export default {
+  name: 'm-aside',
+  components: {vcenter},
+  data () {
+    return {
+      aside,
+      a_tips: 0,
+      currentItem: ''
+    }
+  },
+  watch: {
+    '$route.name': function (newVal) {
+
+    }
+  },
+  computed: {
+    activeIdx: {
+      get: function () {
+        return this.$route.meta.index
+      },
+      set: function () {
+      }
+    },
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  mounted () {
+    let menu = JSON.parse(window.localStorage.getItem('menu'))
+    let temp = []
+    let gerenArr = []
+    let geren = ''
+
+    if (!menu) {
+      return
+    }
+
+    menu && menu.forEach(item => {
+      if (item.name === '我的场景' || item.name === '我的相机') {
+        if (item['authority']) {
+          gerenArr.push(item)
+        }
+      }
+      this.aside.forEach(sub => {
+        if (item.name === sub.name) {
+          sub['authority'] = item['authority']
+          sub['children'] = item['children']
+
+          if (sub.name === '个人中心') {
+            geren = sub
+          } else if (item['authority']) {
+            temp.push(sub)
+          }
+        }
+      })
+    })
+
+    let subItem = []
+
+    if (!geren) {
+      return
+    }
+
+    geren.subItem.forEach(item => {
+      gerenArr.forEach(sub => {
+        if (item.name === sub.name) {
+          item['authority'] = sub['authority']
+          item['children'] = sub['children']
+          if (sub['authority']) {
+            subItem.push(item)
+          }
+        }
+      })
+    })
+
+    geren.subItem = subItem
+    if (geren.subItem.length > 0) {
+      temp.push(geren)
+    }
+
+    this.aside = temp
+  },
+  methods: {
+    clickNav (item) {
+      if (item.id) {
+        this.activeIdx = item.id
+        this.currentItem = item
+        window.localStorage.setItem('currentItem', JSON.stringify(item))
+        this.$router.push(item.url)
+      } else {
+        item.isShow = !item.isShow
+      }
+    },
+    navigate (item) {
+      this.activeIdx = item.id
+      this.currentItem = item
+      window.localStorage.setItem('currentItem', JSON.stringify(item))
+      console.log(item)
+      this.$router.push(item.url)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.aside {
+  width: 100%;
+  .navigator_menu{
+    height: 56px;
+    span{
+      padding-left: 20px;
+      font-size: 12px;
+      color: #999;
+    }
+  }
+  .aside-ul{
+    color: #999;
+    .aside-li{
+      cursor: pointer;
+      font-size: 14px;
+      .aside-li-div{
+        padding: 0 20px;
+        height: 56px;
+        .fr{
+          float: right;
+          transition: transform 0.3s ease;
+        }
+        .rotate{
+          transform: rotate(180deg);
+        }
+        &:hover{
+          background-color: #f5f5f5;
+        }
+      }
+      .active{
+        background-color: #f5f5f5;
+        color: #333;
+        .iconfont{
+          color: #333;
+        }
+      }
+      .li-ul{
+        max-height: 200px;
+        transition: max-height 0.3s ease;
+        overflow: hidden;
+        li{
+          height: 56px;
+          padding: 0 47px;
+          .aside-tip{
+            background: #ff0000;
+            color: #fff;
+            border-radius: 10px;
+            display: inline-block;
+            width: 16px;
+            height: 16px;
+            text-align: center;
+            font-size: 12px;
+            line-height: 16px;
+            margin-left: 4px;
+          }
+          &:hover{
+            background-color: #f5f5f5;
+          }
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 203 - 0
src/pages/layout/header/index.vue

@@ -0,0 +1,203 @@
+<!--  -->
+<template>
+<div class='con'>
+    <el-dialog :close-on-click-modal='false'  title="修改密码" width="35%" center :visible.sync="dialogFormVisible">
+       <el-form :rules="ruleForget" status-icon :model="formForget" ref="formForget">
+            <el-form-item prop="oldPass">
+              <el-input v-model="formForget.oldPass" placeholder="当前密码" type="password"></el-input>
+            </el-form-item>
+            <el-form-item prop="newPassword">
+              <el-input v-model="formForget.newPassword" placeholder="新密码" type="password"></el-input>
+            </el-form-item>
+            <el-form-item prop="comfirm">
+              <el-input v-model="formForget.comfirm" placeholder="重复新密码" type="password"></el-input>
+            </el-form-item>
+        </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submitForm('formForget')">确 定</el-button>
+      </div>
+    </el-dialog>
+  <div class="logo">
+    <vcenter>
+      <img :src="require('@/assets/images/index_logo@2x.png')" alt="">
+    </vcenter>
+  </div>
+  <div class="setting">
+    <vcenter>
+      <div class="i-float">
+        <div class="user" @click="dialogFormVisible=true"><i class="iconfont iconper"></i><span>{{zfb_username}}</span></div>
+        <div @click="logout">
+          <i class="iconfont icon-zhuxiao"></i>
+          <span>注销</span>
+        </div>
+      </div>
+    </vcenter>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+export default {
+// import引入的组件需要注入到对象中才能使用
+  components: {vcenter},
+  data () {
+    var validateOldPass = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('请输入密码'))
+      } else if (value.length < 4) {
+        callback(new Error('密码不能小于4位'))
+      } else {
+        if (this.formForget.newPassword !== '') {
+          this.$refs.formForget.validateField('comfirm')
+        }
+        callback()
+      }
+    }
+    var validatePass = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('请输入新密码'))
+      } else if (value.length < 4) {
+        callback(new Error('密码不能小于4位'))
+      } else {
+        callback()
+      }
+    }
+    var validatePass2 = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('请再次输入密码'))
+      } else if (value.length < 4) {
+        callback(new Error('密码不能小于4位'))
+      } else if (value !== this.formForget.newPassword) {
+        callback(new Error('两次输入密码不一致!'))
+      } else {
+        callback()
+      }
+    }
+
+    // 这里存放数据
+    return {
+      zfb_username: window.localStorage.getItem('zfb_username'),
+      dialogFormVisible: false,
+      formForget: {
+        oldPass: '',
+        newPassword: '',
+        comfirm: ''
+      },
+      ruleForget: {
+        oldPass: [{ validator: validateOldPass, trigger: 'blur' }],
+        newPassword: [{ validator: validatePass, trigger: 'blur' }],
+        comfirm: [{ validator: validatePass2, trigger: 'blur' }]
+      }
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+    submitForm (formName) {
+      this.$refs[formName].validate((valid, e) => {
+        if (valid) {
+          this.editPassword()
+        } else {
+          return false
+        }
+      })
+    },
+    async editPassword () {
+      let {oldPass, newPassword} = this.formForget
+      let res = await this.$http({
+        method: 'post',
+        data: {
+          oldPassword: oldPass,
+          password: newPassword
+        },
+        url: '/api/manage/user/updatePwd',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert('修改成功,请重新登录', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.$http({
+              method: 'get',
+              url: '/admin/logout',
+              headers: {
+                Authorization: window.localStorage.getItem('token')
+              }
+            }).then(data => {
+              if (data.status === 2000) {
+                window.localStorage.setItem('token', '')
+                this.$token = ''
+                window.localStorage.setItem('menu', '')
+                this.$router.push('/login')
+              }
+            })
+            this.dialogFormVisible = false
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+    logout () {
+      // this.$http.post('loginBackground', {
+      //   userName: this.ruleForm2.username,
+      //   userPassword: this.ruleForm2.pass
+      // })
+
+      this.$http({
+        method: 'get',
+        url: '/admin/logout',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      }).then(res => {
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          window.localStorage.setItem('token', '')
+          this.$token = ''
+          this.$alert('退出成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              window.localStorage.setItem('menu', '')
+              this.$router.push('/login')
+            }
+          })
+        }
+      })
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang="scss" scoped>
+@import './style.scss';
+</style>

+ 33 - 0
src/pages/layout/header/style.scss

@@ -0,0 +1,33 @@
+.con{
+  background: #0175dc;
+  // background: -webkit-gradient(linear, left top, left bottom, from(#4491fa), to(#5653e4));
+  height: 70px;
+  display: inline-block;
+  width: 100%;
+  color: #fff;
+  padding: 0 30px;
+  .logo{
+    height: 100%;
+    display: inline-block;
+    img{
+      width: 50px;
+    }
+  }
+  .setting{
+    height: 100%;
+    float: right;
+    .i-float{
+      display: flex;
+      .user{
+        
+      }
+      div{
+        margin-left: 20px;
+        cursor: pointer;
+        .iconfont{
+          padding-right:5px; 
+        }
+      }
+    }
+  }
+}

+ 70 - 0
src/pages/layout/index.vue

@@ -0,0 +1,70 @@
+<template>
+<div class="layout">
+  <iheader />
+  <div class="main">
+    <div class="aside-con">
+      <iaside />
+    </div>
+    <div class="r-view">
+      <crumb :title="$route.name"/>
+      <router-view class="r-main" />
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+import header from '@/pages/layout/header'
+import aside from '@/pages/layout/aside'
+import crumb from '@/components/crumb'
+
+export default {
+  components: {
+    iheader: header,
+    iaside: aside,
+    crumb
+  },
+  data () {
+    return {
+
+    }
+  },
+  computed: {
+    userId () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  mounted () {
+    if (!this.userId) {
+      this.$router.push('/login')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.main {
+  width: 100%;
+  height: calc(100% - 72px);
+  background: #f2f2f2;
+  .aside-con {
+    border-right: 1px solid #ebeef5;
+    background: #fff;
+    position: fixed;
+    height: 100%;
+    width: 235px;
+    z-index: 1;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, .1);
+  }
+  .r-view {
+    height: 100%;
+    padding-left: 235px;
+    overflow: hidden;
+    .r-main{
+      padding: 30px;
+      height: calc(100% - 65px);
+      overflow: auto;
+    }
+  }
+}
+</style>

+ 219 - 0
src/pages/login/index.vue

@@ -0,0 +1,219 @@
+<template>
+<div class="l-con">
+  <div class="l-top">
+    <h1>消安场景管理系统</h1>
+  </div>
+  <div class="l_center">
+    <vcenter>
+      <div class="lc-l">
+        <img :src="logoImg" alt="">
+        <div class="text_01">全自动精准建模,智能测距</div>
+        <div class="text_02">多功能编辑后台,让空间延伸表达</div>
+        <div class="text_03">简单、高效</div>
+      </div>
+      <div class="lc-r">
+        <div class="lc-title">
+          <span>后台登录</span>
+        </div>
+        <div class="lc-form">
+           <el-form label-position="top" :model="ruleForm2" status-icon :rules="rules2" ref="ruleForm2" label-width="100px" class="demo-ruleForm">
+              <el-form-item prop="username">
+                <el-input type="username" placeholder="账号" v-model="ruleForm2.username" auto-complete="off"></el-input>
+              </el-form-item>
+              <el-form-item prop="pass" id='no-bottom'>
+                <el-input type="password" placeholder="密码" @keyup.enter.native="submitForm('ruleForm2')" v-model="ruleForm2.pass" auto-complete="off"></el-input>
+              </el-form-item>
+              <el-form-item>
+                <el-checkbox  v-model="checked">记住密码(在公共场所电脑请勿勾选)</el-checkbox>
+              </el-form-item>
+            </el-form>
+            <div class="lc-btn">
+              <el-button type="primary" v-loading.fullscreen.lock="fullscreenLoading" @click="submitForm('ruleForm2')">登录</el-button>
+            </div>
+        </div>
+      </div>
+    </vcenter>
+  </div>
+  <div class="l_footer">
+
+  </div>
+</div>
+</template>
+
+<script>
+import vcenter from '@/components/vcenter'
+export default {
+  name: 'login',
+  components: {
+    vcenter
+  },
+  data () {
+    var validateUser = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error('用户名不能为空'))
+      } else if (value.length < 3) {
+        callback(new Error('用户名不合法'))
+      } else {
+        callback()
+      }
+    }
+
+    var validatePass = (rule, value, callback) => {
+      if (value === '') {
+        return callback(new Error('请输入密码'))
+      } else if (value.length < 4) {
+        callback(new Error('密码不能小于4位'))
+      } else {
+        callback()
+      }
+    }
+
+    return {
+      fullscreenLoading: false,
+      checked: true,
+      logoImg: require('@/assets/images/index_logo@2x.png'),
+      ruleForm2: {
+        username: '',
+        pass: ''
+      },
+      rules2: {
+        username: [{
+          validator: validateUser,
+          trigger: 'blur'
+        }],
+        pass: [{
+          validator: validatePass,
+          trigger: 'blur'
+        }]
+      }
+    }
+  },
+
+  mounted () {
+    this.ruleForm2.username = window.localStorage.getItem('zfb_username') || ''
+    this.ruleForm2.pass = window.localStorage.getItem('zfb_pass') || ''
+  },
+  methods: {
+    login () {
+      this.$http.post('/admin/login', {
+        userName: this.ruleForm2.username,
+        password: this.ruleForm2.pass
+      }).then(res => {
+        this.fullscreenLoading = true
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          window.localStorage.setItem('token', String(res.data.token))
+          window.localStorage.setItem('menu', JSON.stringify(res.data.permission))
+          window.localStorage.setItem('roleType', res.data.role[0])
+
+          if (this.checked) {
+            window.localStorage.setItem('zfb_username', this.ruleForm2.username)
+            window.localStorage.setItem('zfb_pass', this.ruleForm2.pass)
+          } else {
+            window.localStorage.setItem('zfb_username', '')
+            window.localStorage.setItem('zfb_pass', '')
+          }
+          this.$router.push('/')
+        } else {
+          this.fullscreenLoading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    submitForm (formName) {
+      this.$refs[formName].validate((valid, e) => {
+        if (valid) {
+          this.login()
+        } else {
+          return false
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.l-con{
+  width: 100%;
+  .l-top{
+    color: #333;
+    text-align: center;
+    padding: 50px 0;
+  }
+  .l_center {
+    height: 600px;
+    width: 100%;
+    padding: 0 10% 0 20%;
+    background: url('~@/assets/images/img_login_banbg.jpg') center center no-repeat;
+    .lc-l{
+      display: inline-block;
+      color: #fff;
+      img{
+        width: 150px;
+      }
+
+      .text_01{
+        font-size: 60px;
+        font-weight: bold;
+        margin-top: 70px;
+      }
+      .text_02{
+        font-size: 40px;
+        margin: 30px 0;
+      }
+      .text_03{
+        font-size: 20px;
+      }
+
+    }
+    .lc-r{
+      float: right;
+      width: 345px;
+      height: 370px;
+      background-color: #fff;
+      padding: 28px 30px;
+      .lc-title{
+        text-align: center;
+        border-bottom: 2px solid #0175dc;
+        padding-bottom: 20px;
+        span{
+          color: #333;
+          font-size: 16px;
+        }
+      }
+      .lc-form{
+        margin-top: 40px;
+        .lc-btn{
+          width: 100%;
+          .el-button--primary{
+            width: 100%;
+          }
+        }
+      }
+    }
+  }
+  .l_footer{
+    font-size: 14px;
+    text-align: center;
+    margin-top: 45px;
+    line-height: 1.5;
+    .sub{
+      font-size: 12px;
+      color: #999;
+      margin-top: 10px;
+    }
+  }
+}
+
+@media screen and (max-width: 1400px) {
+    .l-con{
+      .l_center{
+        padding: 0 100px;
+      }
+    }
+}
+</style>

+ 541 - 0
src/pages/myscene/index.vue

@@ -0,0 +1,541 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <div class="h-header">
+     <vcenter>
+        <div>
+          关键字:
+        </div>
+        <div class="h-input">
+          <el-input v-model="inpuKey" @keypress.enter.native="search(inpuKey)" placeholder="输入场景名或SN码"></el-input>
+        </div>
+        <div>
+          <el-button type="primary" @click="search(inpuKey)">查询</el-button>
+          <!-- <el-button>导出</el-button> -->
+        </div>
+      </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+        <template slot-scope="scope">
+          <div style="cursor:pointer" @click="gotoShow(scope.row)"  v-if="item.name === 'thumb'">
+            <img :src="scope.row.thumb" style="height:150px;" alt="">
+          </div>
+          <span v-else>{{scope.row[item.name]||'-'}}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="操作" width="300">
+          <template slot-scope="scope">
+           <span class="o-span" v-if="roleType==='admin'" @click="reCalculate(scope.row)">重算</span>
+           <span class="o-span" v-if="roleType==='admin'" @click="zipScene(scope.row)">{{scope.row.downloadStatus === '已打包'?'重新':''}}打包</span>
+           <span class="o-span" v-if="roleType==='admin'" @click="download(scope.row)">下载</span>
+           <span class="o-span" v-if="authority['场景编辑']" @click="gotoEdit(scope.row)">编辑</span>
+           <span class="o-span" v-if="authority['场景删除']" @click="del(scope.row)">删除</span>
+          </template>
+        </el-table-column>
+
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+const tableHeader = [{
+  name: 'thumb',
+  label: '场景封面'
+}, {
+  name: 'sceneName',
+  label: '场景标题'
+}, {
+  name: 'updateTime',
+  label: '上传完成图片'
+}, {
+  name: 'createTime',
+  label: '计算完成图片'
+}, {
+  name: 'snCode',
+  label: '设备SN'
+},
+{
+  name: 'statusStr',
+  label: '状态'
+},
+{
+  name: 'downloadStatus',
+  label: '下载状态'
+},
+{
+  name: 'realName',
+  label: '拍摄人员'
+}]
+
+const statusArr = {
+  '0': '计算中',
+  '-1': '计算出错',
+  '-2': '计算完成',
+  '1': '计算完成'
+}
+
+const downloadArr = {
+  '0': '打包中',
+  '-1': '未打包',
+  '1': '已打包'
+}
+
+const authority = {
+  '场景编辑': true,
+  '场景删除': false
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      tableData: [],
+      tableHeader,
+      currentPage: 1,
+      loading: false,
+      size: 10,
+      authority,
+      total: 0,
+      inpuKey: '',
+      roleType: window.localStorage.getItem('roleType') || ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    async reCalculate (item) {
+      // '0': '计算中',
+      // '-1': '计算出错',
+      // '-2': '计算完成',
+      // '1': '计算完成'
+      if (item.status === 0) {
+        return this.$notify.info({
+          title: '消息',
+          message: '计算中,请稍后'
+        })
+      }
+
+      this.$confirm('此操作将会重新计算该场景, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        let res = await this.$http({
+          method: 'get',
+          url: `api/scene/rebuildScene?num=${item.num}`
+        })
+
+        if (res.code === 0) {
+          this.$alert(`正在重算,请稍等`, '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        })
+      })
+    },
+
+    async zipScene  (item) {
+      if (item.downloadStatus === '打包中') {
+        return this.$notify.info({
+          title: '消息',
+          message: '正在打包场景文件,请稍后'
+        })
+      }
+      let res = await this.$http({
+        method: 'get',
+        data: {
+          num: item.num,
+          snCode: item.snCode
+        },
+        url: `api/scene/downloadResultData?num=${item.num}`
+      })
+
+      if (res.code === 0) {
+        this.$alert(`打包中,请稍等`, '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    async download (item) {
+      if (item.downloadStatus !== '已打包') {
+        return this.$notify.info({
+          title: '消息',
+          message: '请先打包场景文件,再进行场景下载'
+        })
+      }
+      window.open(this.$serverName + `scene/result/${item.num}.zip`)
+    },
+
+    async gotoShow (item) {
+      window.open(item.webSite, '_blank')
+    },
+
+    async gotoEdit (item) {
+      if (item.status !== 1 && item.status !== -2) {
+        return this.$alert(`场景${item.statusStr}, 暂时无法编辑。`, '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+            this.isRes = false
+          }
+        })
+      }
+      let res = await this.$http({
+        method: 'get',
+        data: {
+          id: item.id
+        },
+        url: `/api/manage/center/scene/edit/${item.id}`,
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        let data = res.data
+        let url = data.webSite
+        let page = url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.html'))
+        let pStr = ''
+
+        if (data.buildType) {
+          switch (data.buildType.toLowerCase()) {
+            case 'v1':
+              pStr = 'edit'
+              break
+            case 'v2':
+              pStr = 'editProMobile'
+              break
+            case 'v3':
+              pStr = 'epc'
+              break
+            default:
+              break
+          }
+        }
+        url = pStr ? url.replace(page, pStr) : url
+
+        // + '&token=' + window.localStorage.getItem('token')
+        window.open(url, '_blank')
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    del (item) {
+      this.$confirm('此操作将会删除该选项, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        let res = await this.$http({
+          method: 'get',
+          url: `/api/manage/center/scene/delete/${item.id}`,
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('删除成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    search (item) {
+      this.searchKey = item
+      this.refresh()
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/center/scene/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      result.data.list.forEach(item => {
+        item['statusStr'] = statusArr[item['status']]
+        item['downloadStatus'] = downloadArr[item['downloadStatus']]
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+a {
+    text-decoration: none;
+    color:#333;
+}
+/*正常的未被访问过的链接*/
+a:link {
+    text-decoration: none;
+}
+/*已经访问过的链接*/
+a:visited {
+    text-decoration: none;
+}
+/*鼠标划过(停留)的链接*/
+a:hover {
+    text-decoration: none;
+}
+/* 正在点击的链接*/
+a:active {
+    text-decoration: none;
+}
+.con {
+  .con-main {
+    padding: 0 30px;
+    .d-l {
+      display: inline-block
+    }
+
+    .d-r {
+      float: right;
+
+      .c-logo {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .c-zizhi {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        position: relative;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+
+        .c-mask {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+          background: rgba(0, 0, 0, 0.5);
+          text-align: center;
+          display: table;
+
+          .icon-download {
+            text-align: center;
+            display: table-cell;
+            vertical-align: middle;
+            color: #fff;
+            font-size: 20px;
+          }
+        }
+      }
+    }
+  }
+
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+
+  }
+
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+      vertical-align: middle;
+    }
+
+    .h-input {
+      margin-left: 20px;
+      display: inline-block;
+    }
+
+    .fix-date {
+      div {
+        display: flex;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/myscene/style.scss


+ 318 - 0
src/pages/operation/index.vue

@@ -0,0 +1,318 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <div class="h-header">
+    <vcenter>
+      <div class="h-input">
+        <span>时间段:</span>
+      </div>
+      <div class="fix-date">
+        <el-date-picker v-model="value5" type="datetimerange" :picker-options="pickerOptions2" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd hh:mm:ss" align="right">
+        </el-date-picker>
+      </div>
+      <div class="h-input" style="margin-right:20px;">
+        <el-input v-model="inpuKey" placeholder="输入操作员名称"></el-input>
+      </div>
+      <div>
+        <el-button @click="refresh" type="primary">搜索</el-button>
+      </div>
+    </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column label="序号" type="index">
+      </el-table-column>
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+        <template slot-scope="scope">
+          <span>{{scope.row[item.name]||'-'}}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+const tableHeader = [{
+  name: 'createTime',
+  label: '操作时间'
+}, {
+  name: 'realName',
+  label: '操作员'
+}, {
+  name: 'type',
+  label: '操作类型'
+}, {
+  name: 'description',
+  label: '操作记录'
+}]
+
+const authority = {
+  '角色新增': true,
+  '角色授权': true
+}
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      tableData: [],
+      tableHeader,
+      currentPage: 1,
+      loading: false,
+      size: 10,
+      total: 0,
+      inpuKey: '',
+      pickerOptions2: {
+        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])
+          }
+        }]
+      },
+      value5: [],
+      authority,
+      func: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.inpuKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        startTime: this.value5[0] || '',
+        endTime: this.value5[1] || ''
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/log/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .con-main {
+    padding: 0 30px;
+    .d-l {
+      display: inline-block
+    }
+
+    .d-r {
+      float: right;
+
+      .c-logo {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .c-zizhi {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        position: relative;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+
+        .c-mask {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+          background: rgba(0, 0, 0, 0.5);
+          text-align: center;
+          display: table;
+
+          .icon-download {
+            text-align: center;
+            display: table-cell;
+            vertical-align: middle;
+            color: #fff;
+            font-size: 20px;
+          }
+        }
+      }
+    }
+  }
+
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+
+  }
+
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+      vertical-align: middle;
+    }
+
+    .h-input {
+      margin-left: 20px;
+      display: inline-block;
+    }
+
+    .fix-date {
+      div {
+        display: flex;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/operation/style.scss


+ 615 - 0
src/pages/question/index.vue

@@ -0,0 +1,615 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div>
+      <el-dialog :close-on-click-modal='false'  :title="currentItem ?( isRes? '回复' : '查看详情') : '提交问题'" width="35%" center :visible.sync="dialogFormVisible">
+        <el-form :model="form">
+          <el-form-item label="标题" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input :disabled="!!currentItem" v-model="form.title" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+          <el-form-item label="内容" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input :disabled="!!currentItem" v-model="form.content" type="textarea" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+          <el-form-item v-if="isRes" label="回复" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="response" type="textarea" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+          <el-form-item v-if="!isRes&&(form.reply&&form.reply.length>0)" label="回复" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input style="margin-bottom:10px" disabled v-for="(item,i) in form.reply" :key="i" v-model="item.reply" type="textarea" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="dialogFormVisible = false">关 闭</el-button>
+          <el-button type="primary" v-if="!currentItem" @click="addItem">确 定</el-button>
+          <el-button type="primary" v-else-if="isRes" @click="responseFn">确 定</el-button>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="h-header">
+      <vcenter>
+        <div>
+          关键字:
+        </div>
+        <div class="h-input">
+          <el-input v-model="inpuKey" placeholder="输入标题/内容"></el-input>
+        </div>
+        <div>
+          <el-button type="primary" @click="search(inpuKey)">查询</el-button>
+          <!-- <el-button>导出</el-button> -->
+        </div>
+      </vcenter>
+    </div>
+    <div class="h-body">
+      <div style="padding:0 20px;">
+        <el-button @click="add" v-if="authority['问题新增']" type="primary">提交问题</el-button>
+        <el-button v-if="roleType==='admin'" @click="del">删除</el-button>
+      </div>
+      <el-table @selection-change="handleSelectionChange" height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+        <el-table-column
+          type="selection"
+          width="55">
+        </el-table-column>
+        <el-table-column
+          v-for="(item,i) in tableHeader"
+          :key="i"
+          :prop="item.name"
+          :label="item.label"
+        >
+          <template slot-scope="scope">
+            <span>{{scope.row[item.name]||'-'}}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <span class="o-span" v-if="roleType==='admin'" @click="showResponse(scope.row)">回复</span>
+            <span class="o-span" @click="showDetail(scope.row)">查看详情</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="p-con">
+        <el-pagination
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page.sync="currentPage"
+          :page-size="size"
+          layout="prev, pager, next, jumper"
+          :total="total"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+// import { serverName } from '@/utils/http'
+
+const tableHeader = [
+  {
+    name: 'title',
+    label: '标题'
+  },
+  {
+    name: 'content',
+    label: '内容'
+  },
+  {
+    name: 'realName',
+    label: '提交人'
+  },
+  {
+    name: 'createTime',
+    label: '创建时间'
+  }
+]
+
+const authority = {
+  '问题新增': true,
+  '问题删除': true,
+  '问题回复': true
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      response: '',
+      isRes: false,
+      dialogFormVisible: false,
+      formLabelWidth: '100px',
+      form: {
+        content: '',
+        title: '',
+        reply: []
+      },
+      tableData: [],
+      tableHeader,
+      multipleSelection: [],
+      currentItem: '',
+      size: 10,
+      total: 0,
+      loading: false,
+      currentPage: 1,
+      inpuKey: '',
+      authority,
+      roleType: window.localStorage.getItem('roleType') || ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    showResponse (item) {
+      this.showDialog(item, true)
+    },
+    async showDetail (item) {
+      let res = await this.$http({
+        method: 'get',
+        data: {
+          id: item.id
+        },
+        url: `/api/manage/issue/detail/${item.id}`,
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.dialogFormVisible = true
+        this.isRes = false
+        this.form = {
+          reply: res.data.reply,
+          title: item.title,
+          content: item.content,
+          id: item.id
+        }
+
+        this.currentItem = item
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+    async responseFn () {
+      let parmas = this.form
+      if (!this.response.trim()) {
+        return this.$notify.error({
+          title: '错误',
+          message: '请输入回复内容'
+        })
+      }
+      let res = await this.$http({
+        method: 'get',
+        data: parmas,
+        url: `/api/manage/issue/reply/${parmas.id}/${this.response}`,
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert('回复成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+            this.isRes = false
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+        this.isRes = false
+      }
+    },
+
+    handleSelectionChange (val) {
+      this.multipleSelection = val
+    },
+
+    showDialog (item, status) {
+      this.dialogFormVisible = true
+      this.isRes = status || false
+      this.form = {
+        title: item.title,
+        content: item.content,
+        id: item.id
+      }
+
+      this.currentItem = item
+    },
+
+    clearInfo () {
+      this.form = {
+        content: '',
+        title: ''
+      }
+      this.response = ''
+    },
+
+    add () {
+      this.dialogFormVisible = true
+      this.currentItem = null
+      this.isRes = false
+      this.clearInfo()
+    },
+
+    async addItem () {
+      let parmas = this.form
+      if (this.currentItem) {
+        parmas['id'] = this.currentItem.id
+      }
+      let res = await this.$http({
+        method: 'post',
+        data: parmas,
+        url: '/api/manage/issue/save',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert(this.currentItem ? '修改成功' : '添加成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    del () {
+      let parmas = []
+      if (this.multipleSelection.length <= 0) {
+        return
+      }
+      this.$confirm('此操作将会删除该选项, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        this.multipleSelection.forEach(item => {
+          parmas.push(item.id)
+        })
+
+        let res = await this.$http({
+          method: 'get',
+          data: parmas,
+          url: '/api/manage/issue/delete/all/' + parmas.join(','),
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('删除成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    search (item) {
+      this.searchKey = item
+      this.refresh()
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/issue/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .time-data{
+    position: relative;
+    .select-data{
+      position: absolute;
+      left: 0;
+      top: 0;
+      opacity: 0;
+      cursor: pointer;
+    }
+    >span{
+      color: #0175dc;
+    }
+  }
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+  .clear-music {
+    position: relative;
+    text-align: right;
+    top: -30px;
+    color: #999;
+    cursor: pointer;
+  }
+  .musicIcon {
+    width: 100%;
+    height: 100%;
+    line-height: 1;
+    margin-top: 35px;
+    img {
+      width: 50px;
+      height: auto;
+    }
+    p {
+      font-weight: bold;
+      font-size: 14px;
+      color: #000;
+      line-height: 20px;
+      height: 20px;
+      margin-top: 20px;
+    }
+  }
+  .icon-plus {
+    margin-left: 15px;
+    width: 40px;
+    height: 38px;
+    line-height: 38px;
+    text-align: center;
+    background: #f5f7fa;
+    color: #999;
+    cursor: pointer;
+    font-size: 13px;
+    display: inline-block;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    &:hover {
+      border: 1px solid #c0c4cc;
+    }
+  }
+  .add-num {
+    margin-left: 15px;
+    width: 250px;
+    .icon-close,
+    .icon-correct {
+      line-height: 1;
+      color: #999;
+    }
+    input[type="number"]::-webkit-inner-spin-button,
+    input[type="number"]::-webkit-outer-spin-button {
+      -webkit-appearance: none;
+      margin: 0;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+  }
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+    }
+
+    .h-input {
+      width: 220px;
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .logo-add {
+      div {
+        cursor: pointer;
+        line-height: 40px;
+        border: 1px dotted #dcdfe6;
+        border-radius: 4px;
+        font-weight: bold;
+        font-size: 18px;
+        display: inline-block;
+        width: 40px;
+        height: 40px;
+        background: #f5f7fa;
+        position: relative;
+        img {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+        }
+        span {
+          position: absolute;
+          z-index: 100;
+          left: 0;
+          top: 0;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+
+  .d-table {
+    border: 1px solid #ccc;
+    .d-con {
+      max-height: 200px;
+      overflow-y: scroll;
+    }
+    .d-header {
+      font-weight: bold;
+    }
+    .d-header,
+    .d-body {
+      height: 40px;
+      line-height: 40px;
+      width: 100%;
+      border-bottom: 1px solid #ccc;
+      &:last-child {
+        border: none;
+      }
+      span {
+        text-align: center;
+        display: inline-block;
+        width: 24%;
+
+      }
+      .unbind{
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/question/style.scss


+ 485 - 0
src/pages/recommended/index.vue

@@ -0,0 +1,485 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <el-dialog :close-on-click-modal='false'  title="轮播资讯" width="495px" :visible.sync="dialogFormVisible">
+    <el-form :model="form" :label-position="'left'" :label-width="formLabelWidth">
+      <div style="padding:0 10px;">
+        <div class="d-l">
+          <el-form-item label="封面">
+            <el-upload :action="serverName+'/house/upload'" :headers="{token}" :show-file-list="true" :on-change="handleChange" :file-list="fileList" :before-upload="beforeAvatarUpload" :on-success="upload_success" :on-error="upload_fail">
+              <div class="zfb-upload">
+                <img  v-if="image" :src="image" style="width:auto;height:128px;">
+                <div v-else>
+                  <i class="el-icon-plus"></i>
+                  <div class="zfb-upload-text">
+                    <span v-html="$base.defaultTxt.upload_tips"></span>
+                  </div>
+                </div>
+                <div class="v-mask">
+                  <i class="iconfont icon-chongzhi"></i>
+                </div>
+              </div>
+            </el-upload>
+          </el-form-item>
+          <el-form-item label="标题">
+            <el-input v-model="form.title"></el-input>
+          </el-form-item>
+          <el-form-item label="链接ID">
+            <el-input v-model="form.connectId"></el-input>
+          </el-form-item>
+          <el-form-item label="推荐排序">
+            <el-input v-model="form.orderNum"></el-input>
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+    <div style="text-align:center" class="dialog-footer">
+      <el-button @click="dialogFormVisible = false">取 消</el-button>
+      <el-button v-if="isAdd" type="primary" @click="add">保 存</el-button>
+      <el-button v-else type="primary" @click="save">保 存</el-button>
+    </div>
+  </el-dialog>
+  <div class="h-body">
+    <div class="c-header">
+      <div v-for="(item,i) in tag" class="span-tag" :key="i">
+        <span @click="activeTagId=item.id" :class="{'t-span':true,'t-active':activeTagId===item.id}" >{{item.txt}}</span>
+        <span v-if="i!==tag.length-1">/</span>
+      </div>
+      <div style="float:right">
+        <el-button @click="showAdd" type="primary">新增<i class="el-icon-plus el-icon--right"></i></el-button>
+      </div>
+    </div>
+    <div class="c-table">
+      <el-table height="670" v-if="tableData" :data="tableData" style="width: 100%;padding:0 20px;">
+        <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label" :width="item.name === 'image'?'300px':'auto'">
+          <template slot-scope="scope">
+            <img v-if="item.name === 'image'" :src="scope.row.image" style="height:200px;" alt="">
+            <span v-else>{{scope.row[item.name]}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <span class="o-span" @click="show(scope.row)">编辑</span>
+            <span class="o-span" @click="del(scope.row)" style="color:#f56c6c;padding-left:15px;">删除</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import {
+  serverName
+} from '@/utils/http'
+
+const tableHeader = [{
+  name: 'orderNum',
+  label: '序号'
+}, {
+  name: 'image',
+  label: '封面'
+}, {
+  name: 'title',
+  label: '标题'
+}, {
+  name: 'connectId',
+  label: '链接ID'
+}, {
+  name: 'createTime',
+  label: '编辑时间'
+}]
+const tableData = []
+const tag = [{
+  txt: '新上房源',
+  id: 0
+}, {
+  txt: '热门房源',
+  id: 1
+}, {
+  txt: '热门企业',
+  id: 2
+}]
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+
+  },
+  data () {
+    // 这里存放数据
+    return {
+      isAdd: true,
+      fileList: [],
+      loading: false,
+      activeTagId: 0,
+      tag,
+      c_id: '',
+      serverName,
+      tableData,
+      tableHeader,
+      dialogFormVisible: false,
+      currentPage: 1,
+      formLabelWidth: '100px',
+      content: '',
+      form: {
+        title: '',
+        connectId: '',
+        type: '',
+        orderNum: ''
+      },
+      image: '',
+      editorOption: {
+        modules: {
+          toolbar: [
+            [{
+              'size': ['small', false, 'large']
+            }],
+            ['bold', 'italic'],
+            [{
+              'list': 'ordered'
+            }, {
+              'list': 'bullet'
+            }],
+            ['link', 'image']
+          ],
+          history: {
+            delay: 1000,
+            maxStack: 50,
+            userOnly: false
+          },
+          imageDrop: true
+          // imageResize: {
+          //   displayStyles: {
+          //     backgroundColor: 'black',
+          //     border: 'none',
+          //     color: 'white'
+          //   },
+          //   modules: ['Resize', 'DisplaySize', 'Toolbar']
+          // }
+        }
+      }
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    activeTagId () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    showAdd () {
+      this.clearInfo()
+      this.dialogFormVisible = true
+      this.isAdd = true
+    },
+    del (item) {
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http({
+          method: 'post',
+          data: {
+            id: item.id
+          },
+          url: '/recommend/delete',
+          headers: {
+            token: this.token
+          }
+        }).then(res => {
+          if (res.code === 200) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.message
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    clearInfo () {
+      this.c_id = ''
+      this.form = {
+        title: '',
+        connectId: '',
+        type: '',
+        orderNum: ''
+      }
+      this.image = ''
+    },
+    add () {
+      let params = {
+        title: this.form.title,
+        image: this.image,
+        connectId: this.form.connectId,
+        orderNum: this.form.orderNum,
+        type: this.activeTagId
+      }
+      this.$http({
+        method: 'post',
+        data: params,
+        url: '/recommend/save',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('新增成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.dialogFormVisible = false
+              this.clearInfo()
+              this.refresh()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    save () {
+      let params = {
+        id: this.c_id,
+        title: this.form.title,
+        image: this.image,
+        connectId: this.form.connectId,
+        orderNum: this.form.orderNum,
+        type: this.form.type
+      }
+      this.$http({
+        method: 'post',
+        data: params,
+        url: '/recommend/update',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('修改成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.dialogFormVisible = false
+              this.clearInfo()
+              this.refresh()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    async show (item) {
+      this.isAdd = false
+      this.dialogFormVisible = true
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          id: item.id
+        },
+        url: '/recommend/selectById',
+        headers: {
+          token: this.token
+        }
+      })
+      let list = result.message
+      this.c_id = list.id
+      this.form = {
+        title: list.title,
+        type: list.type,
+        connectId: list.connectId,
+        orderNum: list.orderNum
+      }
+      this.image = list.image
+    },
+    beforeAvatarUpload (file) {
+      const isLt5M = file.size / 1024 / 1024 < 5
+      if (!isLt5M) {
+        this.$message.error('上传头像图片大小不能超过 5MB!')
+        return false
+      }
+      return isLt5M
+    },
+    handleChange (file, fileList) {
+      console.log('file', file)
+      console.log('fileList', fileList)
+
+      if (fileList.length > 1) {
+        this.fileList = fileList.slice(-1)
+      }
+    },
+    upload_success (data) {
+      console.log('上传成功')
+
+      this.image = data.message
+    },
+    upload_fail (data) {
+      this.$notify.error({
+        title: '上传失败',
+        message: data.message
+      })
+    },
+    onEditorBlur (quill) {
+      // console.log('editor blur!', quill)
+    },
+    onEditorFocus (quill) {
+      // console.log('editor focus!', quill)
+    },
+    onEditorReady (quill) {
+      // console.log('editor ready!', quill)
+    },
+    onEditorChange ({quill, html, text}) {
+      console.log('editor change!', quill, html, text)
+      this.content = html
+    },
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+    getData () {
+      this.$http({
+        method: 'post',
+        data: {
+          type: this.activeTagId
+        },
+        url: '/recommend/selectByType',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        let result = res
+        for (let i = 0; i < result.message.length; i++) {
+          result.message[i].createTime = this.$base.dateFormat('yyyy-MM-dd hh:mm', new Date(result.message[i].createTime))
+        }
+        this.tableData = result.message
+      })
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    this.refresh()
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .d-l {
+    display: inline-block;
+    width: 100%;
+    .zfb-upload {
+      width: 180px;
+      height: 130px;
+      line-height: 130px;
+      overflow: hidden;
+
+      img {
+        width: 178px;
+        height: 128px;
+        border-radius: 6px;
+      }
+
+      &:hover{
+          border-color: #409eff;
+          .v-mask{
+            display: inline-block;
+            width: 178px;
+            height: 128px;
+            border-radius: 6px;
+            background: rgba(0,0,0,0.5);
+          }
+      }
+
+      .el-icon-plus {
+        position: relative;
+        top: -15px;
+        font-size: 20px;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    padding: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .span-tag {
+      display: inline-block;
+      padding-right: 8px;
+      font-size: 18px;
+
+      .t-span {
+        cursor: pointer;
+        padding-right: 3px;
+      }
+
+      .t-active {
+        font-weight: bold;
+      }
+    }
+
+    .c-table {
+      overflow: auto;
+      height: 670px;
+      margin-top: 60px;
+      .o-span {
+        color: #0175dc;
+        cursor: pointer;
+      }
+
+      .p-con {
+        width: 100%;
+        text-align: right;
+        padding: 25px 20px 30px;
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/recommended/style.scss


+ 669 - 0
src/pages/rental-info/index.vue

@@ -0,0 +1,669 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <el-dialog :close-on-click-modal='false'  title="企业信息" width="45%" :visible.sync="dialogFormVisible">
+    <el-form :model="form" :label-position="'left'" :label-width="formLabelWidth">
+      <div style="padding:0 30px;">
+        <div class="d-l">
+          <el-form-item label="信息标题">
+            <el-input v-model="form.title"></el-input>
+          </el-form-item>
+          <el-form-item label="价格">
+            <div class="c-input-con">
+              <el-input placeholder="请输入价格" maxlength="10" v-model="form.total"></el-input>
+              <span class="hover-tip">元/月</span>
+            </div>
+          </el-form-item>
+          <el-form-item label="建筑面积">
+            <div class="c-input-con">
+              <el-input placeholder="请输入价格" maxlength="10" v-model="form.coveredArea"></el-input>
+              <span class="hover-tip">m²</span>
+            </div>
+          </el-form-item>
+          <el-form-item label="单位朝向">
+             <el-select v-model="form.orientation" placeholder="请选择">
+                <el-option
+                  v-for="item in form.orientations"
+                  :key="item.txt"
+                  :label="item.txt"
+                  v-model="item.txt">
+                </el-option>
+              </el-select>
+          </el-form-item>
+          <el-form-item label="装修情况">
+            <el-select v-model="form.decorate" placeholder="请选择">
+              <el-option
+                v-for="item in form.decorates"
+                :key="item.txt"
+                :label="item.txt"
+                v-model="item.txt">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="封面">
+            <el-upload
+              :action="serverName+'/house/upload'"
+              :headers="{token}"
+              :show-file-list="true"
+              :before-upload="beforeAvatarUpload"
+              :on-change="handleChange"
+              :file-list="fileList"
+              :on-success="upload_success"
+              :on-error="upload_fail">
+              <div class="zfb-upload">
+                <img  v-if="homepic" :src="homepic">
+                <div v-else>
+                  <i class="el-icon-plus"></i>
+                  <div class="zfb-upload-text">
+                    <span v-html="$base.defaultTxt.upload_tips"></span>
+                  </div>
+                </div>
+                <div class="v-mask">
+                  <i class="iconfont icon-chongzhi"></i>
+                </div>
+              </div>
+            </el-upload>
+          </el-form-item>
+        </div>
+        <div class="d-r">
+          <el-form-item label="所在楼层">
+            <el-input v-model="form.floor"></el-input>
+          </el-form-item>
+          <el-form-item label="房间数">
+            <el-input v-model="form.houseNum"></el-input>
+          </el-form-item>
+          <el-form-item label="客厅数">
+            <el-input v-model="form.parlourNum"></el-input>
+          </el-form-item>
+          <el-form-item label="卫生间数">
+            <el-input v-model="form.toiletNum"></el-input>
+          </el-form-item>
+          <el-form-item label="挂牌时间">
+              <el-date-picker
+                v-model="form.startTime"
+                type="date"
+                placeholder="选择日期">
+              </el-date-picker>
+          </el-form-item>
+          <el-form-item label="用途">
+            <el-input v-model="form.purpose"></el-input>
+          </el-form-item>
+          <el-form-item label="电梯">
+            <el-select v-model="form.elevator" placeholder="请选择">
+              <el-option
+                v-for="item in form.elevators"
+                :key="item.val"
+                :label="item.txt"
+                v-model="item.val">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="仅属">
+            <el-input v-model="form.power"></el-input>
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+    <div style="text-align:center" class="dialog-footer">
+      <div>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="save(c_id)">保 存</el-button>
+      </div>
+    </div>
+  </el-dialog>
+  <div class="h-header">
+    <vcenter>
+      <div>
+        <el-dropdown >
+          <el-button type="primary">
+            {{dropdown_active.text}}
+            <i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item style="padding:0" v-for="(item,i) in dropdown_actives" :key="i"><div style="padding:0 20px;" @click="dropdown_active=item">{{item.text}}</div></el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div>
+      <div class="h-input">
+        <span>时间段:</span>
+      </div>
+      <div class="fix-date">
+        <el-date-picker
+          v-model="value5"
+          type="datetimerange"
+          :picker-options="pickerOptions2"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd hh:mm:ss"
+          align="right">
+        </el-date-picker>
+      </div>
+      <div class="h-input"><span>手机号:</span></div>
+      <div>
+        <el-input v-model="userName" placeholder="输入手机号查询"></el-input>
+      </div>
+      <div>
+        <el-button @click="search" type="primary">搜索</el-button>
+      </div>
+    </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+      </el-table-column>
+      <el-table-column label="状态">
+        <template slot-scope="scope">
+          <el-switch
+            @change='changeState(scope.row)'
+            v-model="scope.row.state.st"
+            active-color="#0175dc"
+            inactive-color="#999">
+          </el-switch>
+          <span :style="{color:scope.row.state.text==='开'?'#0175dc':'#999',paddingLeft:'5px'}">{{scope.row.state.text}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template slot-scope="scope">
+          <span class="o-span" @click="show(scope.row)">编辑</span>
+          <span class="o-span" @click="del(scope.row)" style="color:#f56c6c;padding-left:15px;">删除</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+import {serverName} from '@/utils/http'
+
+const tableHeader = [{
+  name: 'id',
+  label: 'ID'
+}, {
+  name: 'createTime',
+  label: '发布时间'
+}, {
+  name: 'title',
+  label: '信息标题'
+}, {
+  name: 'userName',
+  label: '发布账号'
+}]
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      fileList: [],
+      homepic: '',
+      serverName,
+      type: 2,
+      dropdown_actives: [
+        {
+          text: '全部',
+          id: ''
+        },
+        {
+          text: '正常',
+          id: 0
+        },
+        {
+          text: '已关闭',
+          id: 1
+        }
+      ],
+      dropdown_active: {
+        text: '全部',
+        id: ''
+      },
+      tableData: [],
+      tableHeader,
+      dialogFormVisible: false,
+      currentPage: 1,
+      value3: true,
+      loading: false,
+      formLabelWidth: '100px',
+      userName: '',
+      form: {
+        total: '',
+        title: '',
+        price: '',
+        coveredArea: '',
+        utilizationArea: '',
+        startTime: '',
+        orientation: '东',
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        decorate: '精装',
+        floor: '',
+        houseNum: '',
+        parlourNum: '',
+        toiletNum: '',
+        power: '',
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }],
+        elevator: true,
+        purpose: ''
+      },
+      size: 10,
+      total: 0,
+      c_id: '',
+      inpuKey: '',
+      pickerOptions2: {
+        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])
+          }
+        }]
+      },
+      value5: [],
+      options: [{
+        value: '选项1',
+        label: '东'
+      }, {
+        value: '选项2',
+        label: '东南'
+      }, {
+        value: '选项3',
+        label: '南'
+      }, {
+        value: '选项4',
+        label: '西南'
+      }, {
+        value: '选项5',
+        label: '西北'
+      }],
+      value: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    search () {
+      this.currentPage === 1 ? this.refresh() : this.currentPage = 1
+    },
+    beforeAvatarUpload (file) {
+      const isLt5M = file.size / 1024 / 1024 < 5
+      if (!isLt5M) {
+        this.$message.error('上传头像图片大小不能超过 5MB!')
+        return false
+      }
+      return isLt5M
+    },
+    handleChange (file, fileList) {
+      console.log('file', file)
+      console.log('fileList', fileList)
+
+      if (fileList.length > 1) {
+        this.fileList = fileList.slice(-1)
+      }
+    },
+    upload_success (data) {
+      console.log('上传成功')
+      this.homepic = data.message
+    },
+    upload_fail (data) {
+      this.$notify.error({
+        title: '上传失败',
+        message: data.message
+      })
+    },
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+    clearInfo () {
+      this.dialogFormVisible = false
+      this.form = {
+        total: '',
+        title: '',
+        price: '',
+        coveredArea: '',
+        utilizationArea: '',
+        startTime: '',
+        orientation: '东',
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        decorate: '精装',
+        floor: '',
+        houseNum: '',
+        parlourNum: '',
+        toiletNum: '',
+        power: '',
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }],
+        elevator: true,
+        purpose: ''
+      }
+      this.homepic = ''
+      this.refresh()
+    },
+    async changeState (item) {
+      await this.$http({
+        method: 'post',
+        data: {
+          id: item.id,
+          state: (!item.state.st ? 1 : 0)
+        },
+        url: '/house/updateHouse',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.refresh()
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    async save (id) {
+      await this.$http({
+        method: 'post',
+        data: {
+          id,
+          title: this.form.title || null,
+          coveredArea: this.form.coveredArea || null,
+          total: this.form.total || null,
+          orientation: this.form.orientation || null,
+          floor: this.form.floor || null,
+          houseNum: this.form.houseNum || null,
+          parlourNum: this.form.parlourNum || null,
+          toiletNum: this.form.toiletNum || null,
+          elevator: this.form.elevator || null,
+          purpose: this.form.purpose || null,
+          power: this.form.power || null,
+          homepic: this.homepic || null,
+          decorate: this.form.decorate || null,
+          startTime: this.form.startTime || null
+
+        },
+        url: '/house/updateHouse',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('更新成功!', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.clearInfo()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+
+    async getData () {
+      let params = {
+        userName: this.userName || '',
+        startTime: this.value5 ? this.value5[0] : '',
+        endTime: this.value5 ? this.value5[1] : '',
+        state: this.dropdown_active.id !== '' ? this.dropdown_active.id : '',
+        type: this.type
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/house/selectHouseByType',
+        headers: {
+          token: this.token,
+          pageNum: this.currentPage,
+          pageSize: this.size
+        }
+      })
+      for (let i = 0; i < result.message.list.length; i++) {
+        result.message.list[i].createTime = this.$base.dateFormat('yyyy-MM-dd hh:mm', new Date(result.message.list[i].createTime))
+        result.message.list[i].state = this.fixState(result.message.list[i].state)
+      }
+      this.tableData = result.message.list
+
+      this.total = result.message.total
+      this.currentPage = result.message.pageNum
+    },
+    del (item) {
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http({
+          method: 'post',
+          data: {
+            id: item.id
+          },
+          url: '/house/deleteHouse',
+          headers: {
+            token: this.token
+          }
+        }).then(res => {
+          if (res.code === 200) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.message
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    async show (item) {
+      this.dialogFormVisible = true
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          id: item.id
+        },
+        url: '/house/selectHouseById',
+        headers: {
+          token: this.token
+        }
+      })
+      let list = result.message
+      this.c_id = list.id
+      this.form = {
+        total: list.total,
+        title: list.title,
+        price: list.price,
+        coveredArea: list.coveredArea,
+        utilizationArea: list.utilizationArea,
+        startTime: list.startTime ? this.$base.dateFormat('yyyy-MM-dd', new Date(list.startTime)) : '',
+        orientation: list.orientation,
+        decorate: list.decorate,
+        floor: list.floor,
+        houseNum: list.houseNum,
+        parlourNum: list.parlourNum,
+        toiletNum: list.toiletNum,
+        power: list.power,
+        elevator: list.elevator,
+        purpose: list.purpose,
+        c_zizhi: list.c_zizhi,
+        c_state: list.c_state,
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }]
+      }
+      this.homepic = list.homepic
+    },
+    fixState (state) {
+      let obj = {}
+      switch (state) {
+        case 0:
+          obj = {
+            status: 0,
+            text: '开',
+            class: 'zfb-success',
+            st: true
+          }
+          break
+
+        case 1:
+          obj = {
+            status: 1,
+            text: '关',
+            class: 'zfb-guanbi',
+            st: false
+          }
+          break
+        default:
+          break
+      }
+      return obj
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    this.refresh()
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/style/info.scss'
+</style>

+ 0 - 0
src/pages/rental-info/style.scss


+ 790 - 0
src/pages/role/index.vue

@@ -0,0 +1,790 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div>
+      <el-dialog :close-on-click-modal='false'  :title="currentItem ? '修改角色' : '新增角色'" width="35%" center :visible.sync="dialogFormVisible">
+        <el-form :model="form">
+          <el-form-item label="角色名称:" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.roleName" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+          <!-- <el-form-item label="角色类型:" :label-width="formLabelWidth">
+            <div style="width:70%">
+               <el-select v-model="form.roleKey" placeholder="请选择">
+                <el-option label="超级管理员" value="admin"></el-option>
+                <el-option label="普通用户" value="normal"></el-option>
+                </el-select>
+            </div>
+          </el-form-item> -->
+          <el-form-item label="备注:" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.roleDesc" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+
+          <el-form-item v-if="item.name!=='个人中心'" :label="item.name + ':'" v-for="(item,i) in form.limit" :key="i" :label-width="formLabelWidth">
+              <el-checkbox-group v-model="checkSelected" @change="handleCheckedCitiesChange">
+                <el-checkbox v-if="!item.children" :label="item.id">{{item.name}}</el-checkbox>
+                <template v-else>
+                  <el-checkbox v-if="(sub.name!=='问题删除')&&(sub.name!=='问题回复')" v-for="(sub,idx) in item.children" :label="sub.id" :key="idx">{{sub.name}}</el-checkbox>
+                </template>
+              </el-checkbox-group>
+          </el-form-item>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="dialogFormVisible = false">取 消</el-button>
+          <el-button type="primary" @click="addItem">确 定</el-button>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="h-header">
+      <vcenter>
+        <div>
+          关键字:
+        </div>
+        <div class="h-input">
+          <el-input v-model="inpuKey" placeholder="输入角色名称"></el-input>
+        </div>
+        <div>
+          <el-button type="primary" @click="search(inpuKey)">查询</el-button>
+          <!-- <el-button>导出</el-button> -->
+        </div>
+      </vcenter>
+    </div>
+    <div class="h-body">
+      <div style="padding:0 20px;">
+        <el-button @click="add" v-if="authority['角色新增']&&roleType==='admin'" type="primary">新增角色</el-button>
+        <el-button @click="del">删除</el-button>
+      </div>
+      <el-table @selection-change="handleSelectionChange" height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+        <el-table-column
+          type="selection"
+          width="55">
+        </el-table-column>
+        <el-table-column
+          v-for="(item,i) in tableHeader"
+          :key="i"
+          :prop="item.name"
+          :label="item.label"
+        >
+          <template slot-scope="scope">
+            <span>{{scope.row[item.name]||'-'}}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <template v-if="authority['角色新增']&&roleType==='admin'" >
+              <span class="o-span" @click="showDialog(scope.row)">编辑</span>
+            </template>
+           <span v-else>-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="p-con">
+        <el-pagination
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page.sync="currentPage"
+          :page-size="size"
+          layout="prev, pager, next, jumper"
+          :total="total"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+// import { serverName } from '@/utils/http'
+
+const tableHeader = [
+  {
+    name: 'roleName',
+    label: '角色名称'
+  },
+  {
+    name: 'createTime',
+    label: '创建时间'
+  },
+  {
+    name: 'updateTime',
+    label: '上次更新时间'
+  },
+  {
+    name: 'roleDesc',
+    label: '备注'
+  }
+]
+
+// const limit = [
+//   {
+//     name: '首页',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '1-1',
+//         authority: true
+//       }
+//     ]
+//   },
+//   {
+//     name: '部门信息',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '2-1',
+//         authority: false
+//       },
+//       {
+//         name: '新增',
+//         id: '2-2',
+//         authority: true
+//       },
+//       {
+//         name: '删除',
+//         id: '2-3'
+//       }
+//     ]
+//   },
+//   {
+//     name: '用户管理',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '3-1'
+//       },
+//       {
+//         name: '新增用户',
+//         id: '3-2'
+//       },
+//       {
+//         name: '密码重置',
+//         id: '3-3'
+//       }
+//     ]
+//   },
+//   {
+//     name: '角色权限',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '4-1'
+//       },
+//       {
+//         name: '新增角色',
+//         id: '4-2'
+//       },
+//       {
+//         name: '修改权限',
+//         id: '4-3'
+//       }
+//     ]
+//   },
+//   {
+//     name: '场景管理',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '5-1'
+//       }
+//     ]
+//   },
+//   {
+//     name: '数据统计',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '6-1'
+//       }
+//     ]
+//   },
+//   {
+//     name: '操作日志',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '7-1'
+//       },
+//       {
+//         name: '查询',
+//         id: '7-2'
+//       }
+//     ]
+//   },
+//   {
+//     name: '问题反馈',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '8-1'
+//       },
+//       {
+//         name: '提交',
+//         id: '8-2'
+//       },
+//       {
+//         name: '恢复',
+//         id: '8-3'
+//       },
+//       {
+//         name: '删除',
+//         id: '8-4'
+//       }
+//     ]
+//   },
+//   {
+//     name: '我的场景',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '9-1'
+//       },
+//       {
+//         name: '删除',
+//         id: '9-2'
+//       },
+//       {
+//         name: '编辑',
+//         id: '9-3'
+//       }
+//     ]
+//   },
+//   {
+//     name: '我的相机',
+//     items: [
+//       {
+//         name: '查看',
+//         id: '10-1'
+//       },
+//       {
+//         name: '添加新设备',
+//         id: '10-2'
+//       }
+//     ]
+//   }
+// ]
+
+const authority = {
+  '角色新增': true,
+  '角色授权': true
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      checkSelected: [],
+      dialogFormVisible: false,
+      formLabelWidth: '100px',
+      limit: [],
+      form: {
+        roleDesc: '',
+        roleName: 'normal',
+        roleKey: '',
+        limit: []
+      },
+      tableData: [],
+      tableHeader,
+      multipleSelection: [],
+      currentItem: '',
+      size: 10,
+      total: 0,
+      loading: false,
+      currentPage: 1,
+      inpuKey: '',
+      roleArr: [],
+      departmentArr: [],
+      authority,
+      func: '',
+      roleType: window.localStorage.getItem('roleType') || ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    handleCheckedCitiesChange (val) {
+      console.log(val)
+    },
+    async getLimit () {
+      let res = await this.$http({
+        method: 'get',
+        url: '/api/manage/role/find/resource',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.limit = res.data
+    },
+    async getSelects () {
+      let res = await this.$http({
+        method: 'get',
+        url: '/api/manage/department/find',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      let res1 = await this.$http({
+        method: 'get',
+        url: '/api/manage/role/find',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.departmentArr = res.data
+      this.roleArr = res1.data
+    },
+    handleSelectionChange (val) {
+      this.multipleSelection = val
+    },
+
+    showDialog (item) {
+      this.dialogFormVisible = true
+      this.currentItem = item
+      this.checkSelected = []
+      this.getRoleDetail(item)
+    },
+
+    async getRoleDetail (item) {
+      let res = await this.$http({
+        method: 'get',
+        url: `/api/manage/role/detail/${item.id}`,
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      let permission = []
+      res.data.permission.forEach(item => {
+        if (item.name === '部门信息' ||
+         item.name === '用户管理' ||
+         item.name === '数据统计' ||
+         item.name === '操作日志' ||
+         item.name === '角色权限') {
+          return
+        }
+        if (item.children) {
+          item.children.forEach(sub => {
+            if (sub.authority) {
+              this.checkSelected.push(sub.id)
+              sub.parentId && this.checkSelected.push(sub.parentId)
+            }
+          })
+        } else {
+          if (item.authority) {
+            this.checkSelected.push(item.id)
+            item.parentId && this.checkSelected.push(item.parentId)
+          }
+        }
+        permission.push(item)
+      })
+
+      this.form = {
+        roleDesc: item.roleDesc,
+        roleName: item.roleName,
+        roleKey: item.roleKey,
+        limit: permission
+      }
+    },
+
+    clearInfo () {
+      this.checkSelected = []
+
+      this.form = {
+        roleDesc: '',
+        roleName: '',
+        roleKey: 'normal',
+        limit: this.limit
+      }
+    },
+
+    del () {
+      let parmas = []
+      if (this.multipleSelection.length <= 0) {
+        return
+      }
+      this.$confirm('此操作将会删除该选项, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        this.multipleSelection.forEach(item => {
+          parmas.push(item.id)
+        })
+
+        let res = await this.$http({
+          method: 'get',
+          data: parmas,
+          url: '/api/manage/role/delete/all/' + parmas.join(','),
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('删除成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    add () {
+      this.dialogFormVisible = true
+      this.currentItem = ''
+      this.clearInfo()
+    },
+
+    async addItem () {
+      let parmas = this.form
+      let temp = []
+      this.limit.forEach(item => {
+        this.checkSelected.forEach(sub => {
+          if (item.children) {
+            item.children.forEach(c => {
+              if (c.id === sub) {
+                temp.push(c.parentId)
+              }
+            })
+          }
+        })
+      })
+      parmas['resources'] = temp.concat(this.checkSelected)
+      if (this.currentItem) {
+        parmas['id'] = this.currentItem.id
+      }
+
+      let res = await this.$http({
+        method: 'post',
+        data: parmas,
+        url: '/api/manage/role/save',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert(this.currentItem ? '修改成功' : '添加成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    search (item) {
+      this.searchKey = item
+      this.refresh()
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/role/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+
+    this.getLimit()
+    this.getSelects()
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .time-data{
+    position: relative;
+    .select-data{
+      position: absolute;
+      left: 0;
+      top: 0;
+      opacity: 0;
+      cursor: pointer;
+    }
+    >span{
+      color: #0175dc;
+    }
+  }
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+  .clear-music {
+    position: relative;
+    text-align: right;
+    top: -30px;
+    color: #999;
+    cursor: pointer;
+  }
+  .musicIcon {
+    width: 100%;
+    height: 100%;
+    line-height: 1;
+    margin-top: 35px;
+    img {
+      width: 50px;
+      height: auto;
+    }
+    p {
+      font-weight: bold;
+      font-size: 14px;
+      color: #000;
+      line-height: 20px;
+      height: 20px;
+      margin-top: 20px;
+    }
+  }
+  .icon-plus {
+    margin-left: 15px;
+    width: 40px;
+    height: 38px;
+    line-height: 38px;
+    text-align: center;
+    background: #f5f7fa;
+    color: #999;
+    cursor: pointer;
+    font-size: 13px;
+    display: inline-block;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    &:hover {
+      border: 1px solid #c0c4cc;
+    }
+  }
+  .add-num {
+    margin-left: 15px;
+    width: 250px;
+    .icon-close,
+    .icon-correct {
+      line-height: 1;
+      color: #999;
+    }
+    input[type="number"]::-webkit-inner-spin-button,
+    input[type="number"]::-webkit-outer-spin-button {
+      -webkit-appearance: none;
+      margin: 0;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+  }
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+    }
+
+    .h-input {
+      width: 220px;
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .logo-add {
+      div {
+        cursor: pointer;
+        line-height: 40px;
+        border: 1px dotted #dcdfe6;
+        border-radius: 4px;
+        font-weight: bold;
+        font-size: 18px;
+        display: inline-block;
+        width: 40px;
+        height: 40px;
+        background: #f5f7fa;
+        position: relative;
+        img {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+        }
+        span {
+          position: absolute;
+          z-index: 100;
+          left: 0;
+          top: 0;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+
+  .d-table {
+    border: 1px solid #ccc;
+    .d-con {
+      max-height: 200px;
+      overflow-y: scroll;
+    }
+    .d-header {
+      font-weight: bold;
+    }
+    .d-header,
+    .d-body {
+      height: 40px;
+      line-height: 40px;
+      width: 100%;
+      border-bottom: 1px solid #ccc;
+      &:last-child {
+        border: none;
+      }
+      span {
+        text-align: center;
+        display: inline-block;
+        width: 24%;
+
+      }
+      .unbind{
+        cursor: pointer;
+      }
+    }
+  }
+}
+
+.el-form-item{
+}
+</style>

+ 0 - 0
src/pages/role/style.scss


+ 335 - 0
src/pages/scene/index.vue

@@ -0,0 +1,335 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <div class="h-header">
+    <vcenter>
+      <!-- <div>
+        <el-dropdown>
+          <el-button type="primary">
+            {{dropdown_active.text}}
+            <i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item style="padding:0" v-for="(item,i) in dropdown_actives" :key="i">
+              <div style="padding:0 20px;" @click="dropdown_active=item">{{item.text}}</div>
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div> -->
+      <div class="h-input">
+        <span>时间段:</span>
+      </div>
+      <div class="fix-date">
+        <el-date-picker v-model="value5" type="datetimerange" :picker-options="pickerOptions2" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd hh:mm:ss" align="right">
+        </el-date-picker>
+      </div>
+      <div>
+        <el-button @click="refresh" type="primary">搜索</el-button>
+      </div>
+    </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+        <template slot-scope="scope">
+          <img v-if="item.name === 'thumb'" :src="scope.row.thumb" style="height:150px;" alt="">
+          <span v-else>{{scope.row[item.name]||'-'}}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+const tableHeader = [{
+  name: 'thumb',
+  label: '场景封面'
+}, {
+  name: 'sceneName',
+  label: '场景标题'
+}, {
+  name: 'createTime',
+  label: '拍摄时间'
+}, {
+  name: 'snCode',
+  label: '设备SN'
+}, {
+  name: 'realName',
+  label: '拍摄人员'
+}, {
+  name: 'viewCount',
+  label: '浏览数'
+}, {
+  name: 'userName',
+  label: '备注'
+}]
+
+const authority = {
+  '部门新增': true,
+  '部门删除': true
+}
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      tableData: [],
+      tableHeader,
+      currentPage: 1,
+      loading: false,
+      size: 10,
+      total: 0,
+      pickerOptions2: {
+        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])
+          }
+        }]
+      },
+      value5: [],
+      authority,
+      func: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        startTime: this.value5[0] || '',
+        endTime: this.value5[1] || ''
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/scene/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .con-main {
+    padding: 0 30px;
+    .d-l {
+      display: inline-block
+    }
+
+    .d-r {
+      float: right;
+
+      .c-logo {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .c-zizhi {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        position: relative;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+
+        .c-mask {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+          background: rgba(0, 0, 0, 0.5);
+          text-align: center;
+          display: table;
+
+          .icon-download {
+            text-align: center;
+            display: table-cell;
+            vertical-align: middle;
+            color: #fff;
+            font-size: 20px;
+          }
+        }
+      }
+    }
+  }
+
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+
+  }
+
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+      vertical-align: middle;
+    }
+
+    .h-input {
+      margin-left: 20px;
+      display: inline-block;
+    }
+
+    .fix-date {
+      div {
+        display: flex;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/scene/style.scss


+ 671 - 0
src/pages/sell-info/index.vue

@@ -0,0 +1,671 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <el-dialog :close-on-click-modal='false'  title="企业信息" width="45%" :visible.sync="dialogFormVisible">
+    <el-form :model="form" :label-position="'left'" :label-width="formLabelWidth">
+      <div style="padding:0 30px;">
+        <div class="d-l">
+          <el-form-item label="信息标题">
+            <el-input v-model="form.title"></el-input>
+          </el-form-item>
+          <el-form-item label="价格">
+            <div class="c-input-con">
+              <el-input placeholder="请输入价格" maxlength="10" v-model="form.total"></el-input>
+              <span class="hover-tip"></span>
+            </div>
+          </el-form-item>
+          <el-form-item label="建筑面积">
+            <div class="c-input-con">
+              <el-input placeholder="请输入价格" maxlength="10" v-model="form.coveredArea"></el-input>
+              <span class="hover-tip">m²</span>
+            </div>
+          </el-form-item>
+          <el-form-item label="单位朝向">
+             <el-select v-model="form.orientation" placeholder="请选择">
+                <el-option
+                  v-for="item in form.orientations"
+                  :key="item.txt"
+                  :label="item.txt"
+                  v-model="item.txt">
+                </el-option>
+              </el-select>
+          </el-form-item>
+          <el-form-item label="装修情况">
+            <el-select v-model="form.decorate" placeholder="请选择">
+              <el-option
+                v-for="item in form.decorates"
+                :key="item.txt"
+                :label="item.txt"
+                v-model="item.txt">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="封面">
+            <el-upload
+              :action="serverName+'/house/upload'"
+              :headers="{token}"
+              :show-file-list="true"
+              :before-upload="beforeAvatarUpload"
+              :on-change="handleChange"
+              :file-list="fileList"
+              :on-success="upload_success"
+              :on-error="upload_fail">
+              <div class="zfb-upload">
+                <img  v-if="homepic" :src="homepic">
+                <div v-else>
+                  <i class="el-icon-plus"></i>
+                  <div class="zfb-upload-text">
+                    <span v-html="$base.defaultTxt.upload_tips"></span>
+                  </div>
+                </div>
+                <div class="v-mask">
+                  <i class="iconfont icon-chongzhi"></i>
+                </div>
+              </div>
+            </el-upload>
+          </el-form-item>
+        </div>
+        <div class="d-r">
+          <el-form-item label="所在楼层">
+            <el-input v-model="form.floor"></el-input>
+          </el-form-item>
+          <el-form-item label="房间数">
+            <el-input v-model="form.houseNum"></el-input>
+          </el-form-item>
+          <el-form-item label="客厅数">
+            <el-input v-model="form.parlourNum"></el-input>
+          </el-form-item>
+          <el-form-item label="卫生间数">
+            <el-input v-model="form.toiletNum"></el-input>
+          </el-form-item>
+          <el-form-item label="挂牌时间">
+              <el-date-picker
+                v-model="form.startTime"
+                type="date"
+                placeholder="选择日期">
+              </el-date-picker>
+          </el-form-item>
+          <el-form-item label="用途">
+            <el-input v-model="form.purpose"></el-input>
+          </el-form-item>
+          <el-form-item label="电梯">
+            <el-select v-model="form.elevator" placeholder="请选择">
+              <el-option
+                v-for="item in form.elevators"
+                :key="item.val"
+                :label="item.txt"
+                v-model="item.val">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="仅属">
+            <el-input v-model="form.power"></el-input>
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+    <div style="text-align:center" class="dialog-footer">
+      <div>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="save(c_id)">保 存</el-button>
+      </div>
+    </div>
+  </el-dialog>
+  <div class="h-header">
+    <vcenter>
+      <div>
+        <el-dropdown >
+          <el-button type="primary">
+            {{dropdown_active.text}}
+            <i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item style="padding:0" v-for="(item,i) in dropdown_actives" :key="i"><div style="padding:0 20px;" @click="dropdown_active=item">{{item.text}}</div></el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div>
+      <div class="h-input">
+        <span>时间段:</span>
+      </div>
+      <div class="fix-date">
+        <el-date-picker
+          v-model="value5"
+          type="datetimerange"
+          :picker-options="pickerOptions2"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd hh:mm:ss"
+          align="right">
+        </el-date-picker>
+      </div>
+      <div class="h-input"><span>手机号:</span></div>
+      <div>
+        <el-input v-model="userName" placeholder="输入手机号查询"></el-input>
+      </div>
+      <div>
+        <el-button @click="search" type="primary">搜索</el-button>
+      </div>
+    </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+      </el-table-column>
+      <el-table-column label="状态">
+        <template slot-scope="scope">
+          <el-switch
+            @change='changeState(scope.row)'
+            v-model="scope.row.state.st"
+            active-color="#0175dc"
+            inactive-color="#999">
+          </el-switch>
+          <span :style="{color:scope.row.state.text==='开'?'#0175dc':'#999',paddingLeft:'5px'}">{{scope.row.state.text}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template slot-scope="scope">
+          <span class="o-span" @click="show(scope.row)">编辑</span>
+          <span class="o-span" @click="del(scope.row)" style="color:#f56c6c;padding-left:15px;">删除</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+import {serverName} from '@/utils/http'
+
+const tableHeader = [{
+  name: 'id',
+  label: 'ID'
+}, {
+  name: 'createTime',
+  label: '发布时间'
+}, {
+  name: 'title',
+  label: '信息标题'
+}, {
+  name: 'userName',
+  label: '发布账号'
+}]
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      fileList: [],
+      homepic: '',
+      serverName,
+      type: 1,
+      dropdown_actives: [
+        {
+          text: '全部',
+          id: ''
+        },
+        {
+          text: '正常',
+          id: 0
+        },
+        {
+          text: '已关闭',
+          id: 1
+        }
+      ],
+      dropdown_active: {
+        text: '全部',
+        id: ''
+      },
+      tableData: [],
+      tableHeader,
+      dialogFormVisible: false,
+      currentPage: 1,
+      value3: true,
+      loading: false,
+      formLabelWidth: '100px',
+      userName: '',
+      form: {
+        total: '',
+        title: '',
+        price: '',
+        coveredArea: '',
+        utilizationArea: '',
+        startTime: '',
+        orientation: '东',
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        decorate: '精装',
+        floor: '',
+        houseNum: '',
+        parlourNum: '',
+        toiletNum: '',
+        power: '',
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }],
+        elevator: true,
+        purpose: ''
+      },
+      size: 10,
+      total: 0,
+      c_id: '',
+      inpuKey: '',
+      pickerOptions2: {
+        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])
+          }
+        }]
+      },
+      value5: [],
+      options: [{
+        value: '选项1',
+        label: '东'
+      }, {
+        value: '选项2',
+        label: '东南'
+      }, {
+        value: '选项3',
+        label: '南'
+      }, {
+        value: '选项4',
+        label: '西南'
+      }, {
+        value: '选项5',
+        label: '西北'
+      }],
+      value: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    search () {
+      this.currentPage === 1 ? this.refresh() : this.currentPage = 1
+    },
+    beforeAvatarUpload (file) {
+      const isLt5M = file.size / 1024 / 1024 < 5
+      if (!isLt5M) {
+        this.$message.error('上传头像图片大小不能超过 5MB!')
+        return false
+      }
+      return isLt5M
+    },
+    handleChange (file, fileList) {
+      console.log('file', file)
+      console.log('fileList', fileList)
+
+      if (fileList.length > 1) {
+        this.fileList = fileList.slice(-1)
+      }
+    },
+    upload_success (data) {
+      console.log('上传成功')
+
+      this.homepic = data.message
+    },
+    upload_fail (data) {
+      this.$notify.error({
+        title: '上传失败',
+        message: data.message
+      })
+    },
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+    clearInfo () {
+      this.dialogFormVisible = false
+      this.form = {
+        total: '',
+        title: '',
+        price: '',
+        coveredArea: '',
+        utilizationArea: '',
+        startTime: '',
+        orientation: '东',
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        decorate: '精装',
+        floor: '',
+        houseNum: '',
+        parlourNum: '',
+        toiletNum: '',
+        power: '',
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }],
+        elevator: true,
+        purpose: ''
+      }
+      this.homepic = ''
+      this.refresh()
+    },
+    async changeState (item) {
+      await this.$http({
+        method: 'post',
+        data: {
+          id: item.id,
+          state: (!item.state.st ? 1 : 0)
+        },
+        url: '/house/updateHouse',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.refresh()
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    async save (id) {
+      await this.$http({
+        method: 'post',
+        data: {
+          id,
+          title: this.form.title || null,
+          coveredArea: this.form.coveredArea || null,
+          total: this.form.total || null,
+          orientation: this.form.orientation || null,
+          floor: this.form.floor || null,
+          houseNum: this.form.houseNum || null,
+          parlourNum: this.form.parlourNum || null,
+          toiletNum: this.form.toiletNum || null,
+          elevator: this.form.elevator || null,
+          purpose: this.form.purpose || null,
+          power: this.form.power || null,
+          homepic: this.homepic || null,
+          decorate: this.form.decorate || null,
+          startTime: this.form.startTime || null
+
+        },
+        url: '/house/updateHouse',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('更新成功!', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.clearInfo()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+
+    async getData () {
+      let params = {
+        userName: this.userName || '',
+        startTime: this.value5 ? this.value5[0] : '',
+        endTime: this.value5 ? this.value5[1] : '',
+        state: this.dropdown_active.id !== '' ? this.dropdown_active.id : '',
+        type: this.type
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/house/selectHouseByType',
+        headers: {
+          token: this.token,
+          pageNum: this.currentPage,
+          pageSize: this.size
+        }
+      })
+      for (let i = 0; i < result.message.list.length; i++) {
+        result.message.list[i].createTime = this.$base.dateFormat('yyyy-MM-dd hh:mm', new Date(result.message.list[i].createTime))
+        result.message.list[i].state = this.fixState(result.message.list[i].state)
+      }
+      this.tableData = result.message.list
+
+      this.total = result.message.total
+      this.currentPage = result.message.pageNum
+    },
+    del (item) {
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http({
+          method: 'post',
+          data: {
+            id: item.id
+          },
+          url: '/house/deleteHouse',
+          headers: {
+            token: this.token
+          }
+        }).then(res => {
+          if (res.code === 200) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.message
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    async show (item) {
+      this.dialogFormVisible = true
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          id: item.id
+        },
+        url: '/house/selectHouseById',
+        headers: {
+          token: this.token
+        }
+      })
+      let list = result.message
+      this.c_id = list.id
+      this.form = {
+        total: list.total,
+        title: list.title,
+        price: list.price,
+        coveredArea: list.coveredArea,
+        utilizationArea: list.utilizationArea,
+        startTime: list.startTime ? this.$base.dateFormat('yyyy-MM-dd', new Date(list.startTime)) : '',
+        orientation: list.orientation,
+        decorate: list.decorate,
+        floor: list.floor,
+        houseNum: list.houseNum,
+        parlourNum: list.parlourNum,
+        toiletNum: list.toiletNum,
+        power: list.power,
+        elevator: list.elevator,
+        purpose: list.purpose,
+        c_zizhi: list.c_zizhi,
+        c_state: list.c_state,
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }]
+      }
+      this.homepic = list.homepic
+    },
+    fixState (state) {
+      let obj = {}
+      switch (state) {
+        case 0:
+          obj = {
+            status: 0,
+            text: '开',
+            class: 'zfb-success',
+            st: true
+          }
+          break
+
+        case 1:
+          obj = {
+            status: 1,
+            text: '关',
+            class: 'zfb-guanbi',
+            st: false
+          }
+          break
+        default:
+          break
+      }
+      return obj
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    this.refresh()
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/style/info.scss'
+</style>

+ 0 - 0
src/pages/sell-info/style.scss


+ 671 - 0
src/pages/site-decoration/index.vue

@@ -0,0 +1,671 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <el-dialog :close-on-click-modal='false'  title="企业信息" width="45%" :visible.sync="dialogFormVisible">
+    <el-form :model="form" :label-position="'left'" :label-width="formLabelWidth">
+      <div style="padding:0 30px;">
+        <div class="d-l">
+          <el-form-item label="信息标题">
+            <el-input v-model="form.title"></el-input>
+          </el-form-item>
+          <el-form-item label="价格">
+            <div class="c-input-con">
+              <el-input placeholder="请输入价格" maxlength="10" v-model="form.total"></el-input>
+              <span class="hover-tip"></span>
+            </div>
+          </el-form-item>
+          <el-form-item label="建筑面积">
+            <div class="c-input-con">
+              <el-input placeholder="请输入价格" maxlength="10" v-model="form.coveredArea"></el-input>
+              <span class="hover-tip">m²</span>
+            </div>
+          </el-form-item>
+          <el-form-item label="单位朝向">
+             <el-select v-model="form.orientation" placeholder="请选择">
+                <el-option
+                  v-for="item in form.orientations"
+                  :key="item.txt"
+                  :label="item.txt"
+                  v-model="item.txt">
+                </el-option>
+              </el-select>
+          </el-form-item>
+          <el-form-item label="装修情况">
+            <el-select v-model="form.decorate" placeholder="请选择">
+              <el-option
+                v-for="item in form.decorates"
+                :key="item.txt"
+                :label="item.txt"
+                v-model="item.txt">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="封面">
+            <el-upload
+              :action="serverName+'/house/upload'"
+              :headers="{token}"
+              :show-file-list="true"
+              :before-upload="beforeAvatarUpload"
+              :on-change="handleChange"
+              :file-list="fileList"
+              :on-success="upload_success"
+              :on-error="upload_fail">
+              <div class="zfb-upload">
+                <img  v-if="homepic" :src="homepic">
+                <div v-else>
+                  <i class="el-icon-plus"></i>
+                  <div class="zfb-upload-text">
+                    <span v-html="$base.defaultTxt.upload_tips"></span>
+                  </div>
+                </div>
+                <div class="v-mask">
+                  <i class="iconfont icon-chongzhi"></i>
+                </div>
+              </div>
+            </el-upload>
+          </el-form-item>
+        </div>
+        <div class="d-r">
+          <el-form-item label="所在楼层">
+            <el-input v-model="form.floor"></el-input>
+          </el-form-item>
+          <el-form-item label="房间数">
+            <el-input v-model="form.houseNum"></el-input>
+          </el-form-item>
+          <el-form-item label="客厅数">
+            <el-input v-model="form.parlourNum"></el-input>
+          </el-form-item>
+          <el-form-item label="卫生间数">
+            <el-input v-model="form.toiletNum"></el-input>
+          </el-form-item>
+          <el-form-item label="挂牌时间">
+              <el-date-picker
+                v-model="form.startTime"
+                type="date"
+                placeholder="选择日期">
+              </el-date-picker>
+          </el-form-item>
+          <el-form-item label="用途">
+            <el-input v-model="form.purpose"></el-input>
+          </el-form-item>
+          <el-form-item label="电梯">
+            <el-select v-model="form.elevator" placeholder="请选择">
+              <el-option
+                v-for="item in form.elevators"
+                :key="item.val"
+                :label="item.txt"
+                v-model="item.val">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="仅属">
+            <el-input v-model="form.power"></el-input>
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+    <div style="text-align:center" class="dialog-footer">
+      <div>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="save(c_id)">保 存</el-button>
+      </div>
+    </div>
+  </el-dialog>
+  <div class="h-header">
+    <vcenter>
+      <div>
+        <el-dropdown >
+          <el-button type="primary">
+            {{dropdown_active.text}}
+            <i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item style="padding:0" v-for="(item,i) in dropdown_actives" :key="i"><div style="padding:0 20px;" @click="dropdown_active=item">{{item.text}}</div></el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div>
+      <div class="h-input">
+        <span>时间段:</span>
+      </div>
+      <div class="fix-date">
+        <el-date-picker
+          v-model="value5"
+          type="datetimerange"
+          :picker-options="pickerOptions2"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd hh:mm:ss"
+          align="right">
+        </el-date-picker>
+      </div>
+      <div class="h-input"><span>手机号:</span></div>
+      <div>
+        <el-input v-model="userName" placeholder="输入手机号查询"></el-input>
+      </div>
+      <div>
+        <el-button @click="search" type="primary">搜索</el-button>
+      </div>
+    </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+      </el-table-column>
+      <el-table-column label="状态">
+        <template slot-scope="scope">
+          <el-switch
+            @change='changeState(scope.row)'
+            v-model="scope.row.state.st"
+            active-color="#0175dc"
+            inactive-color="#999">
+          </el-switch>
+          <span :style="{color:scope.row.state.text==='开'?'#0175dc':'#999',paddingLeft:'5px'}">{{scope.row.state.text}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template slot-scope="scope">
+          <span class="o-span" @click="show(scope.row)">编辑</span>
+          <span class="o-span" @click="del(scope.row)" style="color:#f56c6c;padding-left:15px;">删除</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+import {serverName} from '@/utils/http'
+
+const tableHeader = [{
+  name: 'id',
+  label: 'ID'
+}, {
+  name: 'createTime',
+  label: '发布时间'
+}, {
+  name: 'title',
+  label: '信息标题'
+}, {
+  name: 'userName',
+  label: '发布账号'
+}]
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      fileList: [],
+      homepic: '',
+      serverName,
+      type: 0,
+      dropdown_actives: [
+        {
+          text: '全部',
+          id: ''
+        },
+        {
+          text: '正常',
+          id: 0
+        },
+        {
+          text: '已关闭',
+          id: 1
+        }
+      ],
+      dropdown_active: {
+        text: '全部',
+        id: ''
+      },
+      tableData: [],
+      tableHeader,
+      dialogFormVisible: false,
+      currentPage: 1,
+      value3: true,
+      loading: false,
+      formLabelWidth: '100px',
+      userName: '',
+      form: {
+        total: '',
+        title: '',
+        price: '',
+        coveredArea: '',
+        utilizationArea: '',
+        startTime: '',
+        orientation: '东',
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        decorate: '精装',
+        floor: '',
+        houseNum: '',
+        parlourNum: '',
+        toiletNum: '',
+        power: '',
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }],
+        elevator: true,
+        purpose: ''
+      },
+      size: 10,
+      total: 0,
+      c_id: '',
+      inpuKey: '',
+      pickerOptions2: {
+        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])
+          }
+        }]
+      },
+      value5: [],
+      options: [{
+        value: '选项1',
+        label: '东'
+      }, {
+        value: '选项2',
+        label: '东南'
+      }, {
+        value: '选项3',
+        label: '南'
+      }, {
+        value: '选项4',
+        label: '西南'
+      }, {
+        value: '选项5',
+        label: '西北'
+      }],
+      value: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    search () {
+      this.currentPage === 1 ? this.refresh() : this.currentPage = 1
+    },
+    beforeAvatarUpload (file) {
+      const isLt5M = file.size / 1024 / 1024 < 5
+      if (!isLt5M) {
+        this.$message.error('上传头像图片大小不能超过 5MB!')
+        return false
+      }
+      return isLt5M
+    },
+    handleChange (file, fileList) {
+      console.log('file', file)
+      console.log('fileList', fileList)
+
+      if (fileList.length > 1) {
+        this.fileList = fileList.slice(-1)
+      }
+    },
+    upload_success (data) {
+      console.log('上传成功')
+
+      this.homepic = data.message
+    },
+    upload_fail (data) {
+      this.$notify.error({
+        title: '上传失败',
+        message: data.message
+      })
+    },
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+    clearInfo () {
+      this.dialogFormVisible = false
+      this.form = {
+        total: '',
+        title: '',
+        price: '',
+        coveredArea: '',
+        utilizationArea: '',
+        startTime: '',
+        orientation: '东',
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        decorate: '精装',
+        floor: '',
+        houseNum: '',
+        parlourNum: '',
+        toiletNum: '',
+        power: '',
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }],
+        elevator: true,
+        purpose: ''
+      }
+      this.homepic = ''
+      this.refresh()
+    },
+    async changeState (item) {
+      await this.$http({
+        method: 'post',
+        data: {
+          id: item.id,
+          state: (!item.state.st ? 1 : 0)
+        },
+        url: '/house/updateHouse',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.refresh()
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    async save (id) {
+      await this.$http({
+        method: 'post',
+        data: {
+          id,
+          title: this.form.title || null,
+          coveredArea: this.form.coveredArea || null,
+          total: this.form.total || null,
+          orientation: this.form.orientation || null,
+          floor: this.form.floor || null,
+          houseNum: this.form.houseNum || null,
+          parlourNum: this.form.parlourNum || null,
+          toiletNum: this.form.toiletNum || null,
+          elevator: this.form.elevator || null,
+          purpose: this.form.purpose || null,
+          power: this.form.power || null,
+          homepic: this.homepic || null,
+          decorate: this.form.decorate || null,
+          startTime: this.form.startTime || null
+
+        },
+        url: '/house/updateHouse',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('更新成功!', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.clearInfo()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+
+    async getData () {
+      let params = {
+        userName: this.userName || '',
+        startTime: this.value5 ? this.value5[0] : '',
+        endTime: this.value5 ? this.value5[1] : '',
+        state: this.dropdown_active.id !== '' ? this.dropdown_active.id : '',
+        type: this.type
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/house/selectHouseByType',
+        headers: {
+          token: this.token,
+          pageNum: this.currentPage,
+          pageSize: this.size
+        }
+      })
+      for (let i = 0; i < result.message.list.length; i++) {
+        result.message.list[i].createTime = this.$base.dateFormat('yyyy-MM-dd hh:mm', new Date(result.message.list[i].createTime))
+        result.message.list[i].state = this.fixState(result.message.list[i].state)
+      }
+      this.tableData = result.message.list
+
+      this.total = result.message.total
+      this.currentPage = result.message.pageNum
+    },
+    del (item) {
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http({
+          method: 'post',
+          data: {
+            id: item.id
+          },
+          url: '/house/deleteHouse',
+          headers: {
+            token: this.token
+          }
+        }).then(res => {
+          if (res.code === 200) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.message
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    async show (item) {
+      this.dialogFormVisible = true
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          id: item.id
+        },
+        url: '/house/selectHouseById',
+        headers: {
+          token: this.token
+        }
+      })
+      let list = result.message
+      this.c_id = list.id
+      this.form = {
+        total: list.total,
+        title: list.title,
+        price: list.price,
+        coveredArea: list.coveredArea,
+        utilizationArea: list.utilizationArea,
+        startTime: list.startTime ? this.$base.dateFormat('yyyy-MM-dd', new Date(list.startTime)) : '',
+        orientation: list.orientation,
+        decorate: list.decorate,
+        floor: list.floor,
+        houseNum: list.houseNum,
+        parlourNum: list.parlourNum,
+        toiletNum: list.toiletNum,
+        power: list.power,
+        elevator: list.elevator,
+        purpose: list.purpose,
+        c_zizhi: list.c_zizhi,
+        c_state: list.c_state,
+        orientations: [{
+          txt: '东'
+        }, {
+          txt: '南'
+        }, {
+          txt: '西'
+        }, {
+          txt: '北'
+        }],
+        decorates: [{
+          txt: '精装'
+        }, {
+          txt: '简装'
+        }, {
+          txt: '毛坯'
+        }, {
+          txt: '豪华装修'
+        } ],
+        elevators: [{
+          txt: '有',
+          val: true
+        }, {
+          txt: '无',
+          val: false
+        }]
+      }
+      this.homepic = list.homepic
+    },
+    fixState (state) {
+      let obj = {}
+      switch (state) {
+        case 0:
+          obj = {
+            status: 0,
+            text: '开',
+            class: 'zfb-success',
+            st: true
+          }
+          break
+
+        case 1:
+          obj = {
+            status: 1,
+            text: '关',
+            class: 'zfb-guanbi',
+            st: false
+          }
+          break
+        default:
+          break
+      }
+      return obj
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    this.refresh()
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/style/info.scss'
+</style>

+ 0 - 0
src/pages/site-decoration/style.scss


+ 708 - 0
src/pages/user/index.vue

@@ -0,0 +1,708 @@
+<!--  -->
+<template>
+  <div class="con" v-loading.fullscreen.lock="loading">
+    <div>
+      <el-dialog :close-on-click-modal='false'  :title="currentItem ? '修改用户' : '新增用户'" width="35%" center :visible.sync="dialogFormVisible">
+        <el-form :model="form">
+          <!-- <el-form-item label="用户代号" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.userNum" autocomplete="off"></el-input>
+            </div>
+          </el-form-item> -->
+          <el-form-item label="用户名" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.userName" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+          <el-form-item label="真实姓名" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.realName" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+          <el-form-item label="性别" :label-width="formLabelWidth">
+            <div style="width:70%">
+                <el-select v-model="form.sex" placeholder="请选择">
+                  <el-option
+                    label="男"
+                    value=0>
+                  </el-option>
+                  <el-option
+                    label="女"
+                    value=1>
+                  </el-option>
+                </el-select>
+            </div>
+          </el-form-item>
+          <el-form-item label="手机号码" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.phone" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+          <el-form-item label="邮箱" :label-width="formLabelWidth">
+            <div style="width:70%">
+              <el-input v-model="form.email" autocomplete="off"></el-input>
+            </div>
+          </el-form-item>
+          <el-form-item label="所属部门" :label-width="formLabelWidth">
+            <div style="width:70%">
+                <el-select v-model="form.departmentId" placeholder="请选择">
+                  <el-option
+                    v-for="item in departmentArr"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id">
+                  </el-option>
+                </el-select>
+            </div>
+          </el-form-item>
+          <el-form-item label="所属角色" :label-width="formLabelWidth">
+            <div style="width:70%">
+                <el-select v-if="form.roleId!==1" v-model="form.roleId" placeholder="请选择">
+                  <el-option
+                    v-for="item in roleArr"
+                    :key="item.id"
+                    :label="item.roleName"
+                    :value="item.id">
+                  </el-option>
+                </el-select>
+
+                <span v-else>{{form.roleName}}</span>
+            </div>
+          </el-form-item>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="dialogFormVisible = false">取 消</el-button>
+          <el-button type="primary" @click="addItem">确 定</el-button>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="h-header">
+      <vcenter>
+        <div>
+          关键字:
+        </div>
+        <div class="h-input">
+          <el-input v-model="inpuKey" placeholder="输入用户名/部门/角色"></el-input>
+        </div>
+        <div>
+          <el-button type="primary" @click="search(inpuKey)">查询</el-button>
+          <!-- <el-button>导出</el-button> -->
+        </div>
+      </vcenter>
+    </div>
+    <div class="h-body">
+      <div style="padding:0 20px;">
+        <el-button @click="add" type="primary" v-if="authority['用户新增']">新增用户</el-button>
+        <el-button @click="reset">密码重置</el-button>
+        <el-button @click="del">删除</el-button>
+
+      </div>
+      <el-table @selection-change="handleSelectionChange" height="520" :data="tableData" style="width: 100%;padding:0 20px;">
+        <el-table-column
+          type="selection"
+          width="55">
+        </el-table-column>
+        <el-table-column
+          v-for="(item,i) in tableHeader"
+          :key="i"
+          :prop="item.name"
+          :label="item.label"
+        >
+          <template slot-scope="scope">
+            <span>{{scope.row[item.name]||'-'}}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" >
+          <template slot-scope="scope">
+           <span class="o-span" @click="showDialog(scope.row)" v-if="authority['用户编辑']">编辑</span>
+           <span v-else>-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="p-con">
+        <el-pagination
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page.sync="currentPage"
+          :page-size="size"
+          layout="prev, pager, next, jumper"
+          :total="total"
+        ></el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+// import { serverName } from '@/utils/http'
+
+const tableHeader = [
+  // {
+  //   name: 'userNum',
+  //   label: '用户代号'
+  // },
+  {
+    name: 'userName',
+    label: '用户名'
+  },
+  {
+    name: 'realName',
+    label: '真实姓名'
+  },
+  {
+    name: 'sexName',
+    label: '性别'
+  },
+  {
+    name: 'phone',
+    label: '手机号码'
+  },
+  {
+    name: 'email',
+    label: '邮箱'
+  },
+  {
+    name: 'departmentName',
+    label: '所属部门'
+  },
+  {
+    name: 'roleName',
+    label: '所属角色'
+  },
+  {
+    name: 'createTime',
+    label: '创建时间'
+  }
+]
+
+const SEX = {
+  0: '男',
+  1: '女'
+}
+
+const SEXTONUM = {
+  '男': 0,
+  '女': 1
+}
+
+const authority = {
+  '用户新增': true,
+  '用户编辑': true,
+  '重置密码': true
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      authority,
+      dialogFormVisible: false,
+      formLabelWidth: '100px',
+      form: {
+        // userNum: '',
+        userName: '',
+        realName: '',
+        sex: '0',
+        phone: '',
+        email: '',
+        departmentId: '',
+        roleId: '',
+        roleName: ''
+      },
+      tableData: [],
+      tableHeader,
+      multipleSelection: [],
+      currentItem: '',
+      size: 10,
+      total: 0,
+      loading: false,
+      currentPage: 1,
+      inpuKey: '',
+      roleArr: [],
+      departmentArr: [],
+      func: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    },
+    '$route.name': function (newVal) {
+    }
+  },
+  // 方法集合
+  methods: {
+    async getSelects () {
+      let res = await this.$http({
+        method: 'get',
+        url: '/api/manage/department/find',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      let res1 = await this.$http({
+        method: 'get',
+        url: '/api/manage/role/find',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.departmentArr = res.data
+      this.roleArr = res1.data
+    },
+    handleSelectionChange (val) {
+      this.multipleSelection = val
+    },
+
+    showDialog (item) {
+      this.dialogFormVisible = true
+      this.form = {
+        // userNum: item.userNum,
+        userName: item.userName,
+        realName: item.realName,
+        sex: item.sexName,
+        phone: item.phone,
+        email: item.email,
+        departmentId: item.departmentId,
+        roleId: item.roleId,
+        roleName: item.roleName
+      }
+
+      this.currentItem = item
+    },
+
+    clearInfo () {
+      this.form = {
+        // userNum: '',
+        userName: '',
+        realName: '',
+        sex: '0',
+        phone: '',
+        email: '',
+        departmentId: '',
+        roleId: '',
+        roleName: ''
+
+      }
+    },
+
+    add () {
+      this.dialogFormVisible = true
+      this.currentItem = ''
+      this.clearInfo()
+    },
+
+    async reset () {
+      let parmas = []
+      if (this.multipleSelection.length <= 0) {
+        return
+      }
+      this.$confirm('此操作将会重置该选项密码为12345678,是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        this.multipleSelection.forEach(item => {
+          parmas.push(item.id)
+        })
+
+        let res = await this.$http({
+          method: 'get',
+          data: parmas,
+          url: '/api/manage/user/resetPass/all/' + parmas.join(','),
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('重置成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消重置'
+        })
+      })
+    },
+
+    async addItem () {
+      let parmas = this.form
+      if (parmas['sex'] === '男' || parmas['sex'] === '女') {
+        parmas['sex'] = String(SEXTONUM[parmas['sex']])
+      }
+      if (this.currentItem) {
+        parmas['id'] = this.currentItem.id
+      }
+      console.log(parmas['sex'])
+      let res = await this.$http({
+        method: 'post',
+        data: parmas,
+        url: '/api/manage/user/save',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert(this.currentItem ? '修改成功' : '添加成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.dialogFormVisible = false
+            this.clearInfo()
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+
+    del () {
+      let parmas = []
+      if (this.multipleSelection.length <= 0) {
+        return
+      }
+      this.$confirm('此操作将会删除该选项, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        this.multipleSelection.forEach(item => {
+          parmas.push(item.id)
+        })
+
+        let res = await this.$http({
+          method: 'get',
+          data: parmas,
+          url: '/api/manage/user/delete/all/' + parmas.join(','),
+          headers: {
+            Authorization: window.localStorage.getItem('token')
+          }
+        })
+
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          this.$alert('删除成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.refresh()
+            }
+          })
+        } else {
+          this.loading = false
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    search (item) {
+      this.searchKey = item
+      this.refresh()
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.searchKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/user/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      result.data.list.forEach(item => {
+        item['sexName'] = SEX[item['sex']]
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+
+    this.getSelects()
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .time-data{
+    position: relative;
+    .select-data{
+      position: absolute;
+      left: 0;
+      top: 0;
+      opacity: 0;
+      cursor: pointer;
+    }
+    >span{
+      color: #0175dc;
+    }
+  }
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+  .clear-music {
+    position: relative;
+    text-align: right;
+    top: -30px;
+    color: #999;
+    cursor: pointer;
+  }
+  .musicIcon {
+    width: 100%;
+    height: 100%;
+    line-height: 1;
+    margin-top: 35px;
+    img {
+      width: 50px;
+      height: auto;
+    }
+    p {
+      font-weight: bold;
+      font-size: 14px;
+      color: #000;
+      line-height: 20px;
+      height: 20px;
+      margin-top: 20px;
+    }
+  }
+  .icon-plus {
+    margin-left: 15px;
+    width: 40px;
+    height: 38px;
+    line-height: 38px;
+    text-align: center;
+    background: #f5f7fa;
+    color: #999;
+    cursor: pointer;
+    font-size: 13px;
+    display: inline-block;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    &:hover {
+      border: 1px solid #c0c4cc;
+    }
+  }
+  .add-num {
+    margin-left: 15px;
+    width: 250px;
+    .icon-close,
+    .icon-correct {
+      line-height: 1;
+      color: #999;
+    }
+    input[type="number"]::-webkit-inner-spin-button,
+    input[type="number"]::-webkit-outer-spin-button {
+      -webkit-appearance: none;
+      margin: 0;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+  }
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+    }
+
+    .h-input {
+      width: 220px;
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .logo-add {
+      div {
+        cursor: pointer;
+        line-height: 40px;
+        border: 1px dotted #dcdfe6;
+        border-radius: 4px;
+        font-weight: bold;
+        font-size: 18px;
+        display: inline-block;
+        width: 40px;
+        height: 40px;
+        background: #f5f7fa;
+        position: relative;
+        img {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+        }
+        span {
+          position: absolute;
+          z-index: 100;
+          left: 0;
+          top: 0;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+
+  .d-table {
+    border: 1px solid #ccc;
+    .d-con {
+      max-height: 200px;
+      overflow-y: scroll;
+    }
+    .d-header {
+      font-weight: bold;
+    }
+    .d-header,
+    .d-body {
+      height: 40px;
+      line-height: 40px;
+      width: 100%;
+      border-bottom: 1px solid #ccc;
+      &:last-child {
+        border: none;
+      }
+      span {
+        text-align: center;
+        display: inline-block;
+        width: 24%;
+
+      }
+      .unbind{
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/user/style.scss


+ 429 - 0
src/pages/version/index.vue

@@ -0,0 +1,429 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+   <el-dialog width="750px" title="添加版本" :visible.sync="activeViable">
+      <el-form v-if="active" label-width="80px">
+        <el-form-item label="版本" width="100%">
+          <el-input v-model="active.version"></el-input>
+        </el-form-item>
+        <el-form-item label="描述" width="100%">
+          <el-input type="textarea" v-model="active.description" :rows="5"></el-input>
+        </el-form-item>
+        <el-form-item label="文件" width="100%">
+          <div class="upload-file">
+            <el-button type="primary">文件上传</el-button>
+            <input type="file" @change="active.file = $event.target.value" ref="file">
+            <p>{{active.file}}</p>
+          </div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="active = null">取 消</el-button>
+        <el-button type="primary" @click="updateMaker">确 定</el-button>
+      </div>
+    </el-dialog>
+  <div class="h-header">
+    <vcenter>
+      <div class="h-input">
+        <span>时间段:</span>
+      </div>
+      <div class="fix-date">
+        <el-date-picker v-model="value5" type="datetimerange" :picker-options="pickerOptions2" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd hh:mm:ss" align="right">
+        </el-date-picker>
+      </div>
+      <div class="h-input" style="margin-right:20px;">
+        <el-input v-model="inpuKey" placeholder="描述"></el-input>
+      </div>
+      <div>
+        <el-button @click="refresh" type="primary">搜索</el-button>
+      </div>
+      <div style="float: right;margin-right:30px;">
+          <el-button type="success" @click="clickMarker" color='red' style="float: right">上传</el-button>
+      </div>
+    </vcenter>
+  </div>
+  <div class="h-body">
+    <el-table :data="tableData" style="width: 100%;padding:0 20px;">
+      <el-table-column label="序号" type="index">
+      </el-table-column>
+      <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label">
+        <template slot-scope="scope">
+          <el-switch v-if="item.name==='status'" @change="updateAduitStatus(scope.row.id,scope.row.state)" :value="scope.row.status === 'A'" active-color="#13ce66" inactive-color="#ff4949">
+          </el-switch>
+          <span v-else>{{scope.row[item.name]||'-'}}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="p-con">
+      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+const tableHeader = [{
+  name: 'createTime',
+  label: '操作时间'
+}, {
+  name: 'description',
+  label: '描述'
+}, {
+  name: 'version',
+  label: '版本'
+}, {
+  name: 'fileUrl',
+  label: '文件地址'
+}, {
+  name: 'status',
+  label: '状态'
+}, {
+  name: 'updateTime',
+  label: '日期'
+}]
+
+const authority = {
+  '角色新增': true,
+  '角色授权': true
+}
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    vcenter
+  },
+  data () {
+    // 这里存放数据
+    return {
+      activeViable: false,
+      active: {
+        version: '',
+        file: '',
+        description: ''
+      },
+      tableData: [],
+      tableHeader,
+      currentPage: 1,
+      loading: false,
+      size: 10,
+      total: 0,
+      inpuKey: '',
+      pickerOptions2: {
+        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])
+          }
+        }]
+      },
+      value5: [],
+      authority,
+      func: ''
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  // 方法集合
+  methods: {
+    clickMarker () {
+      this.activeViable = true
+      this.active = {
+        version: '',
+        file: '',
+        description: ''
+      }
+    },
+    async updateMaker () {
+      let {version, description} = this.active
+      console.log(version, description)
+
+      var formData = new FormData()
+      formData.append('version', version)
+      formData.append('description', description)
+      formData.append('file', this.$refs.file.files[0])
+
+      console.log(formData)
+      console.log(this.$refs.file.files[0])
+
+      let res = await this.$http.put('/api/manage/version/save', formData, {
+        headers: {
+          'Content-Type': 'multipart/form-data',
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      if (res.status === 5001 || res.status === 5002) return
+      if (res.status === 2000) {
+        this.$alert('添加成功', '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.activeViable = false
+            this.refresh()
+          }
+        })
+      } else {
+        this.loading = false
+        this.$notify.error({
+          title: '错误',
+          message: res.message
+        })
+      }
+    },
+    async updateAduitStatus (id, state) {
+      let m = state === 'A' ? 'I' : 'A'
+      await this.$http({
+        url: '/api/manage/version/update/' + id + '/' + m,
+        method: 'get',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+      this.refresh()
+    },
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+
+    async getData () {
+      let params = {
+        searchKey: this.inpuKey || '',
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        startTime: this.value5[0] || '',
+        endTime: this.value5[1] || ''
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/api/manage/version/list',
+        headers: {
+          Authorization: window.localStorage.getItem('token')
+        }
+      })
+
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.currentPage = result.data.pageNum
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let currentItem = JSON.parse(window.localStorage.getItem('currentItem'))
+    this.func = currentItem.children
+    if (this.func) {
+      this.func.forEach(item => {
+        authority[item.name] = item['authority']
+      })
+    }
+    this.refresh()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+.upload-file {
+  position: relative;
+  display: inline-block;
+  overflow: hidden;
+}
+
+.upload-file input {
+  position: absolute;
+  z-index: 1;
+  cursor: pointer;
+  opacity: 0;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.con {
+  .con-main {
+    padding: 0 30px;
+    .d-l {
+      display: inline-block
+    }
+
+    .d-r {
+      float: right;
+
+      .c-logo {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .c-zizhi {
+        width: 150px;
+        height: 150px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        position: relative;
+        background-color: #f5fafe;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+
+        .c-mask {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+          background: rgba(0, 0, 0, 0.5);
+          text-align: center;
+          display: table;
+
+          .icon-download {
+            text-align: center;
+            display: table-cell;
+            vertical-align: middle;
+            color: #fff;
+            font-size: 20px;
+          }
+        }
+      }
+    }
+  }
+
+  .ei-num {
+    .el-input-number {
+      width: 90%;
+    }
+  }
+
+  .ei-input {
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+
+    .input-con {
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+
+      .el-input_err {
+        & /deep/ .el-input__inner {
+          border: 1px solid #f56c6c;
+        }
+      }
+
+      .el-input_success {
+        & /deep/ .el-input__inner {
+          border: 1px solid #67c23a;
+        }
+      }
+
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+
+  }
+
+  .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+
+    div {
+      display: inline-block;
+      vertical-align: middle;
+    }
+
+    .h-input {
+      margin-left: 20px;
+      display: inline-block;
+    }
+
+    .fix-date {
+      div {
+        display: flex;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/version/style.scss


+ 571 - 0
src/pages/z-carousel/index.vue

@@ -0,0 +1,571 @@
+<!--  -->
+<template>
+<div class='con' v-loading.fullscreen.lock="loading">
+  <el-upload
+          class="avatar-uploader"
+          :action="serverName+'house/upload'"
+          :headers="{token}"
+          :show-file-list="false"
+          :on-success="uploadSuccess"
+          :on-error="uploadError"
+          :before-upload="beforeUpload">
+  </el-upload>
+  <el-dialog :close-on-click-modal='false'  title="轮播资讯" width="860px" :visible.sync="dialogFormVisible">
+    <el-form :model="form" :label-position="'left'" :label-width="formLabelWidth">
+      <div style="padding:0 30px;">
+        <div class="d-l">
+          <el-form-item label="封面">
+            <el-upload :action="serverName+'/house/upload'" :headers="{token}" :show-file-list="true" :on-change="handleChange" :file-list="fileList" :before-upload="beforeAvatarUpload" :on-success="upload_success" :on-error="upload_fail">
+              <div class="zfb-upload">
+                <img  v-if="image" :src="image" style="width:auto;height:158px;">
+                <div v-else>
+                  <i class="el-icon-plus"></i>
+                  <div class="zfb-upload-text">
+                    <span v-html="$base.defaultTxt.upload_tips"></span>
+                  </div>
+                </div>
+                <div class="v-mask">
+                  <i class="iconfont icon-chongzhi"></i>
+                </div>
+              </div>
+            </el-upload>
+          </el-form-item>
+        </div>
+        <div class="d-r">
+          <el-form-item label="正文标题">
+            <el-input v-model="form.title"></el-input>
+          </el-form-item>
+          <el-form-item label="轮播排序">
+            <el-input v-model="form.orderNum"></el-input>
+          </el-form-item>
+          <el-form-item label="链接">
+            <el-input v-model="form.url"></el-input>
+          </el-form-item>
+        </div>
+      </div>
+      <div style="padding:0 30px;">
+        <el-form-item label="正文内容">
+          <quill-editor v-model="content" ref="myQuillEditor" :options="editorOption"  @change="onEditorChange($event)" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)">
+          </quill-editor>
+        </el-form-item>
+      </div>
+    </el-form>
+    <div style="text-align:center" class="dialog-footer">
+      <el-button @click="dialogFormVisible = false">取 消</el-button>
+      <el-button v-if="isAdd" type="primary" @click="add">保 存</el-button>
+      <el-button v-else type="primary" @click="save">保 存</el-button>
+    </div>
+  </el-dialog>
+  <div class="h-body">
+    <div class="c-header">
+      <el-button @click="showAdd" type="primary">新增<i class="el-icon-plus el-icon--right"></i></el-button>
+    </div>
+    <div class="c-table">
+      <el-table v-if="tableData" height="670" :data="tableData" style="width: 100%;padding:0 20px;">
+        <el-table-column v-for="(item,i) in tableHeader" :key="i" :prop="item.name" :label="item.label" :width="item.name === 'image'?'300px':'auto'">
+          <template slot-scope="scope">
+            <img v-if="item.name === 'image'" :src="scope.row.image" style="height:200px;" alt="">
+            <span v-else>{{scope.row[item.name]}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <span class="o-span" @click="show(scope.row)">编辑</span>
+            <span class="o-span" @click="del(scope.row)" style="color:#f56c6c;padding-left:15px;">删除</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import {
+  serverName
+} from '@/utils/http'
+import 'quill/dist/quill.core.css'
+import 'quill/dist/quill.snow.css'
+import 'quill/dist/quill.bubble.css'
+
+import { Quill, quillEditor } from 'vue-quill-editor'
+
+import { ImageDrop } from 'quill-image-drop-module'
+import ImageResize from 'quill-image-resize-module'
+Quill.register('modules/imageDrop', ImageDrop)
+Quill.register('modules/imageResize', ImageResize)
+
+const tableHeader = [{
+  name: 'orderNum',
+  label: '序号'
+}, {
+  name: 'image',
+  label: '封面'
+}, {
+  name: 'title',
+  label: '标题'
+}, {
+  name: 'createTime',
+  label: '编辑时间'
+}]
+const tableData = []
+const toolbarOptions = [
+  [{
+    'size': ['small', false, 'large']
+  }],
+  ['bold', 'italic'],
+  [{
+    'list': 'ordered'
+  }, {
+    'list': 'bullet'
+  }],
+  ['link', 'image']
+]
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    quillEditor
+  },
+  data () {
+    // 这里存放数据
+    return {
+      loading: false,
+      fileList: [],
+      isAdd: true,
+      c_id: '',
+      serverName,
+      tableData,
+      tableHeader,
+      dialogFormVisible: false,
+      currentPage: 1,
+      formLabelWidth: '100px',
+      content: '',
+      form: {
+        title: '',
+        url: '',
+        orderNum: ''
+      },
+      image: '',
+      editorOption: {
+        modules: {
+          toolbar: {
+            container: toolbarOptions,
+            handlers: {
+              'image': function (value) {
+                if (value) {
+                  document.querySelector('.avatar-uploader input').click()
+                } else {
+                  this.quill.format('image', false)
+                }
+              }
+            }
+          },
+          history: {
+            delay: 1000,
+            maxStack: 50,
+            userOnly: false
+          },
+          imageDrop: true,
+          imageResize: {
+            displayStyles: {
+              backgroundColor: 'black',
+              border: 'none',
+              color: 'white'
+            },
+            modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
+          }
+        }
+      }
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    token () {
+      return window.localStorage.getItem('token')
+    },
+    editor () {
+      return this.$refs.myQuillEditor.quill
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+  },
+  // 方法集合
+  methods: {
+    showAdd () {
+      this.clearInfo()
+      this.dialogFormVisible = true
+      this.isAdd = true
+    },
+    del (item) {
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http({
+          method: 'post',
+          data: {
+            id: item.id
+          },
+          url: '/rotation/delete',
+          headers: {
+            token: this.token
+          }
+        }).then(res => {
+          if (res.code === 200) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.message
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    add () {
+      let params = {
+        title: this.form.title,
+        image: this.image,
+        orderNum: this.form.orderNum,
+        url: this.form.url,
+        content: this.content
+      }
+      this.$http({
+        method: 'post',
+        data: params,
+        url: '/rotation/save',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('新增成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.dialogFormVisible = false
+              this.clearInfo()
+              this.refresh()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    clearInfo () {
+      this.c_id = ''
+      this.form = {
+        title: '',
+        orderNum: '',
+        url: ''
+      }
+      this.image = ''
+      this.content = ''
+    },
+    save () {
+      let params = {
+        id: this.c_id,
+        title: this.form.title,
+        image: this.image,
+        orderNum: this.form.orderNum,
+        url: this.form.url,
+        content: this.content
+      }
+      this.$http({
+        method: 'post',
+        data: params,
+        url: '/rotation/update',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        if (res.code === 200) {
+          this.$alert('修改成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.dialogFormVisible = false
+              this.clearInfo()
+              this.refresh()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message
+          })
+        }
+      })
+    },
+    async show (item) {
+      this.isAdd = false
+      this.dialogFormVisible = true
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          id: item.id
+        },
+        url: '/rotation/selectById',
+        headers: {
+          token: this.token
+        }
+      })
+      let list = result.message
+      this.c_id = list.id
+      this.form = {
+        title: list.title,
+        orderNum: list.orderNum,
+        url: list.url
+      }
+      this.image = list.image
+      this.content = list.content
+    },
+    beforeAvatarUpload (file) {
+      const isLt5M = file.size / 1024 / 1024 < 5
+      if (!isLt5M) {
+        this.$message.error('上传头像图片大小不能超过 5MB!')
+        return false
+      }
+      return isLt5M
+    },
+    handleChange (file, fileList) {
+      console.log('file', file)
+      console.log('fileList', fileList)
+
+      if (fileList.length > 1) {
+        this.fileList = fileList.slice(-1)
+      }
+    },
+    upload_success (data) {
+      console.log('上传成功')
+      this.image = data.message
+    },
+    upload_fail (data) {
+      this.$notify.error({
+        title: '上传失败',
+        message: data.message
+      })
+    },
+    uploadSuccess (data) {
+      // 如果上传成功
+      if (data.code === 200 && data.message !== null) {
+        // 获取光标所在位置
+        let length = this.editor.getSelection().index
+        // 插入图片  res.info为服务器返回的图片地址
+        this.editor.insertEmbed(length, 'image', data.message)
+        // 调整光标到最后
+        this.editor.setSelection(length + 1)
+      } else {
+        this.$message.error('图片插入失败')
+      }
+      // loading动画消失
+      this.loading = false
+    },
+    uploadError (data) {
+      this.$notify.error({
+        title: '上传失败',
+        message: data.message
+      })
+    },
+    beforeUpload (file) {
+      const isLt5M = file.size / 1024 / 1024 < 5
+      if (!isLt5M) {
+        this.$message.error('上传头像图片大小不能超过 5MB!')
+        return false
+      }
+      this.loading = true
+      return isLt5M
+    },
+    onEditorBlur (quill) {
+      // console.log('editor blur!', quill)
+    },
+    onEditorFocus (quill) {
+      // console.log('editor focus!', quill)
+    },
+    onEditorReady (quill) {
+      // console.log('editor ready!', quill)
+    },
+    onEditorChange ({
+      quill,
+      html,
+      text
+    }) {
+      this.content = html
+    },
+    handleSizeChange (val) {
+      console.log(`每页 ${val} 条`)
+    },
+    handleCurrentChange (val) {
+      console.log(`当前页: ${val}`)
+    },
+    refresh () {
+      this.loading = true
+      this.getData()
+      this.loading = false
+    },
+    getData () {
+      this.$http({
+        method: 'post',
+        data: {},
+        url: '/rotation/selectAll',
+        headers: {
+          token: this.token
+        }
+      }).then(res => {
+        let result = res
+        for (let i = 0; i < result.message.length; i++) {
+          result.message[i].createTime =
+          this.$base.dateFormat('yyyy-MM-dd hh:mm', new Date(result.message[i].createTime))
+        }
+        this.tableData = result.message
+      })
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    this.refresh()
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style lang="scss" scoped>
+.con {
+  .d-l {
+    display: inline-block;
+    width: 45%;
+
+    .zfb-upload {
+      width: 280px;
+      height: 160px;
+      line-height: 160px;
+      overflow:hidden;
+      img {
+        width: 278px;
+        height: 158px;
+        border-radius: 6px;
+      }
+      &:hover{
+          border-color: #409eff;
+          .v-mask{
+            display: inline-block;
+            width: 278px;
+            height: 158px;
+            border-radius: 6px;
+            background: rgba(0,0,0,0.5);
+          }
+        }
+      .el-icon-plus {
+        position: relative;
+        top: -15px;
+        font-size: 20px;
+      }
+    }
+  }
+
+  .d-r {
+    width: 45%;
+    float: right;
+
+    .el-date-editor.el-input,
+    .el-date-editor.el-input__inner {
+      width: 100%;
+    }
+
+    .c-logo {
+      width: 150px;
+      height: 150px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      background-color: #f5fafe;
+
+      img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .c-zizhi {
+      width: 150px;
+      height: 150px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      position: relative;
+      background-color: #f5fafe;
+
+      img {
+        width: 100%;
+        height: 100%;
+      }
+
+      .c-mask {
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        top: 0;
+        left: 0;
+        background: rgba(0, 0, 0, 0.5);
+        text-align: center;
+        display: table;
+
+        .icon-download {
+          text-align: center;
+          display: table-cell;
+          vertical-align: middle;
+          color: #fff;
+          font-size: 20px;
+        }
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    padding: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .c-table {
+      overflow: auto;
+      height: 670px;
+      margin-top: 60px;
+      .o-span {
+        color: #0175dc;
+        cursor: pointer;
+      }
+
+      .p-con {
+        width: 100%;
+        text-align: right;
+        padding: 25px 20px 30px;
+      }
+    }
+  }
+}
+</style>

+ 0 - 0
src/pages/z-carousel/style.scss


+ 90 - 0
src/router/index.js

@@ -0,0 +1,90 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+Vue.use(Router)
+const originalPush = Router.prototype.push
+Router.prototype.push = function push (location) {
+  return originalPush.call(this, location).catch(err => err)
+}
+const router = new Router({
+  routes: [
+    {
+      path: '/',
+      component: () => import('@/pages/layout/'),
+      children: [
+        {
+          path: '/',
+          name: '首页',
+          component: () => import('@/pages/home'),
+          meta: {index: '1'}
+        },
+        {
+          path: '/department',
+          name: '部门管理',
+          component: () => import('@/pages/department'),
+          meta: {index: '2'}
+        },
+        {
+          path: '/user',
+          name: '用户管理',
+          component: () => import('@/pages/user'),
+          meta: {index: '3'}
+        },
+        {
+          path: '/role',
+          name: '角色权限',
+          component: () => import('@/pages/role'),
+          meta: {index: '4'}
+        },
+        {
+          path: '/scene',
+          name: '场景管理',
+          component: () => import('@/pages/scene'),
+          meta: {index: '5'}
+        },
+        {
+          path: '/data',
+          name: '数据统计',
+          component: () => import('@/pages/data'),
+          meta: {index: '6'}
+        },
+        {
+          path: '/operation',
+          name: '操作日志',
+          component: () => import('@/pages/operation'),
+          meta: {index: '7'}
+        },
+        {
+          path: '/question',
+          name: '问题反馈',
+          component: () => import('@/pages/question'),
+          meta: {index: '8'}
+        },
+        {
+          path: '/version',
+          name: '版本管理',
+          component: () => import('@/pages/version'),
+          meta: {index: '10'}
+        },
+        {
+          path: '/myscene',
+          name: '我的场景',
+          component: () => import('@/pages/myscene'),
+          meta: {index: '9-1'}
+        },
+        {
+          path: '/camera',
+          name: '我的相机',
+          component: () => import('@/pages/camera'),
+          meta: {index: '9-2'}
+        }
+      ]
+    },
+    {
+      path: '/login',
+      name: '登录',
+      component: () => import('@/pages/login')
+    }
+  ]
+})
+
+export default router

+ 156 - 0
src/utils/base.js

@@ -0,0 +1,156 @@
+const base = {
+  weeks: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
+  test: function () {
+    console.log('test')
+  },
+  /**
+   * 删除数组内指定索引的元素
+   * @param {*} delIndex 索引
+   * @param {*} that 数组本身
+   * @return {Array} 删除后的数组
+   */
+  arrDelete: function (delIndex, that) {
+    var temArray = []
+    for (var i = 0; i < that.length; i++) {
+      if (i !== delIndex) {
+        temArray.push(that[i])
+      }
+    }
+    return temArray
+  },
+  /**
+   * 格式化时间
+   * @param {*} fmt 格式 eg:'yyyy-MM-dd'
+   * @param {*} that Date本身
+   * @return {Date} 格式化的时间
+   */
+  dateFormat: function (fmt, that) {
+    var o = {
+      'M+': that.getMonth() + 1, // 月份
+      'd+': that.getDate(), // 日
+      'h+': that.getHours(), // 小时
+      'm+': that.getMinutes(), // 分
+      's+': that.getSeconds(), // 秒
+      'q+': Math.floor((that.getMonth() + 3) / 3), // 季度
+      S: that.getMilliseconds() // 毫秒
+    }
+    if (/(y+)/.test(fmt)) {
+      fmt = fmt.replace(
+        RegExp.$1,
+        (that.getFullYear() + '').substr(4 - RegExp.$1.length)
+      )
+    }
+    for (var k in o) {
+      if (new RegExp('(' + k + ')').test(fmt)) {
+        fmt = fmt.replace(
+          RegExp.$1,
+          RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
+        )
+      }
+    }
+    return fmt
+  },
+  /**
+   * 获取html的纯文本
+   * @param {*} html HTML代码块
+   * @return 纯文本
+   */
+  getSimpleText: function (html) {
+    var re1 = new RegExp('<.+?>', 'g')
+    var msg = html.replace(re1, '')
+    return msg
+  },
+  /**
+   * 获取不包含span标签的html
+   * @param {*} html HTML代码块
+   * @return html
+   */
+  getSpanText: function (html) {
+    var re2 = new RegExp('(<|</)(?!(span)[ >])[^>/]*>', 'g')
+    var msg2 = html.replace(re2, '')
+    console.log(msg2)
+    return msg2
+  },
+  /**
+   * 滚动到锚点
+   * @param {*} dom 锚点
+   */
+  scorllToHref: function (dom) {
+    if (dom) {
+      let anchor = document.querySelector(dom)
+      document.documentElement.scrollTop = anchor.offsetTop > 80 ? anchor.offsetTop - 80 : anchor.offsetTop
+    } else {
+      document.documentElement.scrollTop = 0
+    }
+  },
+  /**
+   * 去除空格
+   * @param {*} str 需要去除的字符串
+   * @param {*} global 正则标准
+   */
+  trim: function (str, global) {
+    var result
+    result = str.replace(/(^\s+)|(\s+$)/g, '')
+    if (global.toLowerCase() === 'g') {
+      result = result.replace(/\s/g, '')
+    }
+    return result
+  },
+  /**
+   * 获取n天前/后的时间
+   * @param {*} day +-天数(+代表后day天,-代表前day天)
+   */
+  getDay: function (day) {
+    var today = new Date()
+
+    var targetdayMilliseconds = today.getTime() + 1000 * 60 * 60 * 24 * day
+
+    today.setTime(targetdayMilliseconds) // 注意,这行是关键代码
+
+    var tYear = today.getFullYear()
+    var tMonth = today.getMonth()
+    var tDate = today.getDate()
+    var tDay = this.weeks[today.getDay()]
+
+    tMonth = this.doHandleMonth(tMonth + 1)
+    tDate = this.doHandleMonth(tDate)
+    return tYear + '-' + tMonth + '-' + tDate + '(' + tDay + ')'
+  },
+  /**
+   * 给个位数月份前添加0
+   * @param {*} month 要格式化的月份
+   */
+  doHandleMonth: function (month) {
+    var m = month
+    if (month.toString().length === 1) {
+      m = '0' + month
+    }
+    return m
+  },
+  isImg: function (url) {
+    var temp = []
+    temp = url.toString().split('.')
+    if (temp[temp.length - 1].toLowerCase() === 'png' || temp[temp.length - 1].toLowerCase() === 'jpg') {
+      return true
+    } else {
+      return false
+    }
+  },
+  isContain: function (value, str) {
+    if (value.indexOf(str) > -1) {
+      return true
+    } else {
+      return false
+    }
+  },
+  reg: {
+    idCard: /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/,
+    phone: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
+    email: /^([a-zA-Z0-9]+[_|_|.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|_|.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/
+  },
+  defaultTxt: {
+    upload_tips: '支持jpg/png格式,<5M <br/>推荐分辨率800*800px',
+    upload_bg_tips: '支持mp3、wmv、<br/>wma格式,<5M '
+  }
+}
+export { base }

+ 113 - 0
src/utils/http.js

@@ -0,0 +1,113 @@
+import axios from 'axios'
+import Vue from 'vue'
+import router from '../router'
+
+var isProduction = process.env.NODE_ENV === 'production'
+const serverName = isProduction ? '/xiaoan/' : 'http://192.168.0.163:8088/xiaoan/'
+const exceptUrls = ['api/scene/downloadResultData', 'api/scene/rebuildScene']
+// http://192.168.0.163:8088/dist/#/myscene
+// http://218.17.126.170:9997/dist
+
+// const serverName = isProduction ? '' : 'https://admin.zhifangbao.com/'
+
+const vue = new Vue()
+
+axios.defaults.baseURL = serverName
+axios.defaults.headers['X-Requested-with'] = 'XMLHttpRequest'
+
+// 拦截请求,做登陆,或head处理
+axios.interceptors.request.use(
+  function (config) {
+    if (config.method === 'post') {
+      config.data = {
+        ...config.data,
+        rnd: Math.random()
+      }
+    } else if (config.method === 'get') {
+      config.params = {
+        rnd: Math.random(),
+        ...config.params
+      }
+    }
+    for (let i = 0; i < exceptUrls.length; i++) {
+      let url = exceptUrls[i]
+      if (config.url.indexOf(url) > -1) {
+        config.baseURL = serverName.replace('xiaoan/', '')
+      }
+    }
+    return config
+  }, function (error) {
+    // 对请求错误做些什么
+    return Promise.reject(error)
+  }
+)
+
+// 拦截返回,做错误统一处理
+axios.interceptors.response.use(
+  response => {
+    let data = response.data
+    let code = Number(response.data.status)
+
+    switch (code) {
+      case -1:
+        break
+      case 4500:
+        vue.$alert('没有获得授权,请联系系统管理员', '提示', {
+          confirmButtonText: '确定',
+          callback: function () {
+          }
+        })
+        break
+      case 5001:
+        if (window.localStorage.getItem('token')) {
+          window.localStorage.setItem('token', '')
+          vue.$alert('登录状态失效,请重新登录', '提示', {
+            confirmButtonText: '确定',
+            callback: function () {
+              router.push('/login')
+            }
+          })
+        }
+        break
+      case 5002:
+        if (window.localStorage.getItem('token')) {
+          window.localStorage.setItem('token', '')
+          vue.$alert('登录状态失效,请重新登录', '提示', {
+            confirmButtonText: '确定',
+            callback: function () {
+              router.push('/login')
+            }
+          })
+        }
+        break
+      case 500:
+        vue.$alert(data.message || '服务器错误', '提示', {
+          confirmButtonText: '确定',
+          callback: function () {
+          }
+        })
+        break
+      case 0:
+        break
+    }
+    // tryHideFullScreenLoading()
+    return data
+  },
+  error => {
+    if (error.response) {
+      switch (error.response.status) {
+        case 500:
+          vue.$alert(error.response.message, '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+
+            }
+          })
+          break
+      }
+    }
+    return Promise.reject(error)
+  }
+)
+
+export { serverName, axios }

+ 291 - 0
src/utils/myCharts.js

@@ -0,0 +1,291 @@
+/**
+ * 各种画echarts图表的方法都封装在这里
+ * 注意:这里echarts没有采用按需引入的方式,只是为了方便学习
+ */
+
+import echarts from 'echarts'
+const install = function (Vue) {
+  Object.defineProperties(Vue.prototype, {
+    $chart: {
+      get () {
+        return {
+          // 统计分析
+          line1: function (id, header, data) {
+            this.chart = echarts.init(document.getElementById(id))
+            this.chart.clear()
+
+            const optionData = {
+              xAxis: {
+                type: 'category',
+                data: header
+              },
+              yAxis: {
+                type: 'value'
+              },
+              series: [{
+                data: data,
+                type: 'line',
+                smooth: true
+              }]
+            }
+
+            this.chart.setOption(optionData)
+          },
+
+          bar: function (id, data) {
+            this.chart = echarts.init(document.getElementById(id))
+            this.chart.clear()
+            const optionData = {
+              tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                  type: 'shadow'
+                }
+              },
+              xAxis: {
+                type: 'category',
+                data: data.name,
+                name: data.xName,
+                nameTextStyle: {
+                  padding: [30, 0, 0, 0]
+                }
+              },
+              yAxis: {
+                type: 'value',
+                name: data.yName
+              },
+              series: [{
+                data: data.data,
+                type: 'bar',
+                barWidth: '40',
+                itemStyle: {
+                  normal: {
+                    color: (params) => {
+                      let colors = data.color
+                      return colors[params.dataIndex]
+                    }
+                  }
+                }
+              }]
+            }
+
+            this.chart.setOption(optionData)
+          },
+          // 访问人数
+          pie: function (id, data) {
+            this.chart = echarts.init(document.getElementById(id))
+            this.chart.clear()
+            var fontColor = '#fff'
+
+            const optionData = {
+              tooltip: {
+                trigger: 'item',
+                formatter: '{b}: {c} ({d}%)'
+              },
+              legend: {
+                orient: 'vertical',
+                x: 'left',
+                y: '15',
+                textStyle: {
+                  color: fontColor
+                },
+                data: data.header
+              },
+              series: [
+                {
+                  type: 'pie',
+                  radius: ['55%', '70%'],
+                  center: data.center || ['50%', '60%'],
+                  color: data.color || ['#67c241', '#e6a139', '#f76b6c'],
+                  avoidLabelOverlap: false,
+                  label: {
+                    normal: {
+                      show: false,
+                      position: 'center'
+                    },
+                    emphasis: {
+                      show: true,
+                      textStyle: {
+                        fontSize: '20',
+                        fontWeight: 'bold',
+                        color: '#ffffff'
+                      }
+                    }
+                  },
+                  labelLine: {
+                    normal: {
+                      show: false
+                    }
+                  },
+                  data: data.data
+                }
+              ]
+            }
+
+            this.chart.setOption(optionData)
+          },
+          // 文物趋势
+          line2: function (id, header, data) {
+            this.chart = echarts.init(document.getElementById(id))
+            this.chart.clear()
+            var fontColor = '#00'
+            var borderColor = 'rgba(0,0,0,0.05)'
+            const optionData = {
+              grid: {
+                left: '5%',
+                right: '8%',
+                top: '18%',
+                bottom: '5%',
+                containLabel: true
+              },
+              tooltip: {
+                show: true,
+                trigger: 'item'
+              },
+              legend: {
+                show: true,
+                x: '200',
+                y: '22',
+                icon: 'stack',
+                itemWidth: 10,
+                itemHeight: 10,
+                textStyle: {
+                  color: fontColor
+                },
+                data: ['全部', '青铜', '瓷器', '陶器']
+              },
+              xAxis: [{
+                type: 'category',
+                boundaryGap: false,
+                axisLabel: {
+                  color: fontColor
+                },
+                axisLine: {
+                  show: true,
+                  lineStyle: {
+                    color: borderColor
+                  }
+                },
+                axisTick: {
+                  show: false
+                },
+                splitLine: {
+                  show: true,
+                  lineStyle: {
+                    color: borderColor
+                  }
+                },
+                data: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
+              }],
+              yAxis: [{
+                type: 'value',
+                axisLabel: {
+                  formatter: '{value}',
+                  textStyle: {
+                    color: fontColor
+                  }
+                },
+                axisLine: {
+                  lineStyle: {
+                    color: borderColor
+                  }
+                },
+                axisTick: {
+                  show: false
+                },
+                splitLine: {
+                  show: true,
+                  lineStyle: {
+                    color: borderColor
+                  }
+                }
+              }],
+              series: [
+                {
+                  name: '青铜',
+                  type: 'line',
+                  stack: '总量',
+                  symbol: 'rect',
+                  symbolSize: 9,
+                  itemStyle: {
+                    normal: {
+                      color: '#67c241',
+                      lineStyle: {
+                        color: '#67c241',
+                        width: 1
+                      }
+                    }
+                  },
+                  data: [220, 182, 191, 234, 290, 330, 310, 201, 154, 190, 330, 410]
+                },
+                {
+                  name: '瓷器',
+                  type: 'line',
+                  stack: '总量',
+                  symbol: 'rect',
+                  symbolSize: 9,
+                  itemStyle: {
+                    normal: {
+                      color: '#e6a139',
+                      lineStyle: {
+                        color: '#e6a139',
+                        width: 1
+                      }
+                    }
+                  },
+                  data: [150, 22, 201, 154, 190, 330, 410, 150, 232, 201, 154, 190]
+                },
+                {
+                  name: '陶器',
+                  type: 'line',
+                  stack: '总量',
+                  symbol: 'rect',
+                  symbolSize: 9,
+                  itemStyle: {
+                    normal: {
+                      color: '#f76b6c',
+                      lineStyle: {
+                        color: '#f76b6c',
+                        width: 1
+                      }
+                    }
+                  },
+                  data: [0, 182, 191, 234, 0, 330, 10, 201, 154, 190, 330, 410]
+                },
+                {
+                  name: '全部',
+                  type: 'line',
+                  stack: '总量',
+                  symbol: 'rect',
+                  symbolSize: 9,
+                  itemStyle: {
+                    normal: {
+                      color: '#fff',
+                      lineStyle: {
+                        color: '#fff',
+                        width: 1
+                      }
+                    }
+                  },
+                  markPoint: {
+                    itemStyle: {
+                      normal: {
+                        color: 'red'
+                      }
+                    }
+                  },
+                  data: [120, 120, 301, 134, 390, 630, 810, 120, 491, 120, 290, 330]
+                }
+              ]
+            }
+            this.chart.setOption(optionData)
+          }
+        }
+      }
+    }
+  })
+}
+
+export default {
+  install
+}

+ 0 - 0
static/.gitkeep


BIN
static/img/favicon.ico


Разница между файлами не показана из-за своего большого размера
+ 7 - 0
消安-后台原型0225/data/document.js


+ 115 - 0
消安-后台原型0225/data/styles.css

@@ -0,0 +1,115 @@
+.ax_default {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:400;
+  font-style:normal;
+  font-size:13px;
+  color:#333333;
+  text-align:center;
+  line-height:normal;
+}
+.box_1 {
+}
+.box_2 {
+}
+.box_3 {
+}
+.ellipse {
+}
+._图片 {
+}
+.button {
+}
+.primary_button {
+  color:#FFFFFF;
+}
+.link_button {
+  color:#169BD5;
+}
+._一级标题 {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:bold;
+  font-style:normal;
+  font-size:32px;
+  text-align:left;
+}
+._二级标题 {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:bold;
+  font-style:normal;
+  font-size:24px;
+  text-align:left;
+}
+._三级标题 {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:bold;
+  font-style:normal;
+  font-size:18px;
+  text-align:left;
+}
+._四级标题 {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:bold;
+  font-style:normal;
+  font-size:14px;
+  text-align:left;
+}
+._五级标题 {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:bold;
+  font-style:normal;
+  text-align:left;
+}
+._六级标题 {
+  font-family:'Arial Normal', 'Arial';
+  font-weight:bold;
+  font-style:normal;
+  font-size:10px;
+  text-align:left;
+}
+.label {
+  font-size:14px;
+  text-align:left;
+}
+._文本段落 {
+  text-align:left;
+}
+.line {
+}
+.text_field {
+  color:#000000;
+  text-align:left;
+}
+.text_area {
+  color:#000000;
+  text-align:left;
+}
+.droplist {
+  color:#000000;
+  text-align:left;
+}
+.checkbox {
+  text-align:left;
+}
+.radio_button {
+  text-align:left;
+}
+.html_button {
+  text-align:center;
+}
+.tree_node {
+  text-align:left;
+}
+._流程形状 {
+}
+.table_cell {
+}
+.menu_item {
+}
+.marker {
+  color:#FFFFFF;
+}
+._表格 {
+}
+._提交按钮 {
+  color:#000000;
+}

Разница между файлами не показана из-за своего большого размера
+ 7 - 0
消安-后台原型0225/files/个人中心/data.js


+ 14 - 0
消安-后台原型0225/files/个人中心/styles.css

@@ -0,0 +1,14 @@
+body {
+  margin:0px;
+  background-image:none;
+  position:static;
+  left:auto;
+  width:10px;
+  margin-left:0;
+  margin-right:0;
+  text-align:left;
+}
+#base {
+  position:absolute;
+  z-index:0;
+}

+ 0 - 0
消安-后台原型0225/files/场景拼接(此页交互待定)/data.js


Некоторые файлы не были показаны из-за большого количества измененных файлов