浏览代码

初始化

shaogen1995 4 年之前
当前提交
4b342555b6
共有 100 个文件被更改,包括 18900 次插入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. 二进制
      build/logo.png
  11. 102 0
      build/utils.js
  12. 22 0
      build/vue-loader.conf.js
  13. 96 0
      build/webpack.base.conf.js
  14. 95 0
      build/webpack.dev.conf.js
  15. 145 0
      build/webpack.prod.conf.js
  16. 二进制
      code.rar
  17. 7 0
      config/dev.env.js
  18. 75 0
      config/index.js
  19. 4 0
      config/prod.env.js
  20. 二进制
      dist.rar
  21. 17 0
      index.html
  22. 12114 0
      package-lock.json
  23. 88 0
      package.json
  24. 227 0
      shurufa/css/index.css
  25. 二进制
      shurufa/images/bg.png
  26. 二进制
      shurufa/images/bodang.png
  27. 二进制
      shurufa/images/close.png
  28. 二进制
      shurufa/images/cls.png
  29. 二进制
      shurufa/images/no-result.png
  30. 二进制
      shurufa/images/reload.png
  31. 二进制
      shurufa/images/二维码按钮.png
  32. 二进制
      shurufa/images/搜索按钮.png
  33. 二进制
      shurufa/images/暂停按钮.png
  34. 二进制
      shurufa/images/点赞按钮(已触发).png
  35. 二进制
      shurufa/images/点赞按钮(未触发).png
  36. 57 0
      shurufa/index.html
  37. 295 0
      shurufa/js/handwritingapi.js
  38. 150 0
      shurufa/js/index.js
  39. 4 0
      shurufa/js/jquery.min.js
  40. 23 0
      src/App.vue
  41. 539 0
      src/assets/font/demo.css
  42. 354 0
      src/assets/font/demo_index.html
  43. 49 0
      src/assets/font/iconfont.css
  44. 二进制
      src/assets/font/iconfont.eot
  45. 1 0
      src/assets/font/iconfont.js
  46. 65 0
      src/assets/font/iconfont.json
  47. 50 0
      src/assets/font/iconfont.svg
  48. 二进制
      src/assets/font/iconfont.ttf
  49. 二进制
      src/assets/font/iconfont.woff
  50. 二进制
      src/assets/font/iconfont.woff2
  51. 二进制
      src/assets/img/01jk.png
  52. 二进制
      src/assets/img/01jks.png
  53. 二进制
      src/assets/img/02fb.png
  54. 二进制
      src/assets/img/03mg.png
  55. 二进制
      src/assets/img/04fb.png
  56. 二进制
      src/assets/img/1.png
  57. 二进制
      src/assets/img/2.png
  58. 二进制
      src/assets/img/4dage-logo.png
  59. 二进制
      src/assets/img/bar.png
  60. 二进制
      src/assets/img/bg.png
  61. 二进制
      src/assets/img/bg1.png
  62. 二进制
      src/assets/img/bg2.png
  63. 二进制
      src/assets/img/down-arrow.png
  64. 二进制
      src/assets/img/icon.png
  65. 二进制
      src/assets/img/icon2.png
  66. 二进制
      src/assets/img/icon2s.png
  67. 二进制
      src/assets/img/logout.png
  68. 二进制
      src/assets/img/noPicture.png
  69. 二进制
      src/assets/img/up-arrow.png
  70. 二进制
      src/assets/img/user.png
  71. 二进制
      src/assets/img/zhoushan-logo.jpg
  72. 171 0
      src/assets/style/public.css
  73. 109 0
      src/assets/style/reset.css
  74. 281 0
      src/components/cropper/index.vue
  75. 58 0
      src/components/crumbs/index.vue
  76. 97 0
      src/components/focus-bang/index.vue
  77. 94 0
      src/components/hot-bang/index.vue
  78. 50 0
      src/components/info-card/index.vue
  79. 59 0
      src/components/main-top/index.vue
  80. 57 0
      src/components/nums/index.vue
  81. 28 0
      src/components/pie-tu/index.vue
  82. 46 0
      src/components/power-bar/index.vue
  83. 108 0
      src/components/search-bar/index.vue
  84. 35 0
      src/components/trend/index.vue
  85. 58 0
      src/components/ww-card/index.vue
  86. 72 0
      src/configue/base.js
  87. 3 0
      src/configue/bus.js
  88. 87 0
      src/configue/http.js
  89. 251 0
      src/configue/myCharts.js
  90. 40 0
      src/main.js
  91. 240 0
      src/pages/cultural-relic/index.vue
  92. 155 0
      src/pages/cultural-relic/style.css
  93. 296 0
      src/pages/device/index.vue
  94. 106 0
      src/pages/device/style.css
  95. 248 0
      src/pages/display/index.vue
  96. 112 0
      src/pages/display/style.css
  97. 670 0
      src/pages/editPages/cultural-relic/index.vue
  98. 54 0
      src/pages/editPages/cultural-relic/style.less
  99. 542 0
      src/pages/editPages/display/index.vue
  100. 0 0
      src/pages/editPages/display/style.css

+ 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 @@
+# szh-system
+
+> 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)
+  }
+}

二进制
build/logo.png


+ 102 - 0
build/utils.js

@@ -0,0 +1,102 @@
+'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',
+        publicPath: '../../'
+      })
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  const output = []
+  const loaders = exports.cssLoaders(options)
+
+  for (const extension in loaders) {
+    const loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+
+  return output
+}
+
+exports.createNotifierCallback = () => {
+  const notifier = require('node-notifier')
+
+  return (severity, errors) => {
+    if (severity !== 'error') return
+
+    const error = errors[0]
+    const filename = error.file && error.file.split('!').pop()
+
+    notifier.notify({
+      title: packageConfig.name,
+      message: severity + ': ' + error.name,
+      subtitle: filename || '',
+      icon: path.join(__dirname, 'logo.png')
+    })
+  }
+}

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

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

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

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

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

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

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

@@ -0,0 +1,145 @@
+'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 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

二进制
code.rar


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

+ 75 - 0
config/index.js

@@ -0,0 +1,75 @@
+'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: '0.0.0.0', // 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"'
+}

二进制
dist.rar


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

文件差异内容过多而无法显示
+ 12114 - 0
package-lock.json


+ 88 - 0
package.json

@@ -0,0 +1,88 @@
+{
+  "name": "szh-system",
+  "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",
+    "preview": "http-server -p 3000 ./dist"
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "echarts": "^4.2.1",
+    "element-ui": "^2.13.2",
+    "http-server": "^0.12.3",
+    "js-cookie": "^2.2.0",
+    "less": "^3.9.0",
+    "less-loader": "^5.0.0",
+    "qs": "^6.5.2",
+    "sortablejs": "^1.11.2-alpha.3",
+    "swiper": "^6.2.0",
+    "vue": "^2.5.2",
+    "vue-awesome-swiper": "^4.1.1",
+    "vue-cropper": "^0.5.5",
+    "vue-router": "^3.0.1",
+    "vue2-editor": "^2.10.2",
+    "vuedraggable": "^2.24.0"
+  },
+  "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-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",
+    "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",
+    "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"
+  ]
+}

+ 227 - 0
shurufa/css/index.css

@@ -0,0 +1,227 @@
+*{
+  padding: 0;
+  margin: 0;
+     font-family:"Microsoft YaHei",微软雅黑,"MicrosoftJhengHei",华文细黑,STHeiti,MingLiu;
+     line-height: 1;
+}
+
+::-webkit-scrollbar {
+  width: 0.6rem;
+  height: 1rem;
+  background-color: #71471d;
+
+}
+
+::-webkit-scrollbar-thumb {
+  height: 5rem;
+  background-color: #EA9649;
+  outline: 0.5rem solid #EA9649;
+  outline-offset: -0.5rem;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  height: 5rem;
+  background-color: #EA9649;
+}
+
+li{
+  list-style: none;
+}
+html,body{
+  width: 100%;
+  height: 100%;
+  font-size: 34px;
+}
+
+body{
+  position: relative;
+  color: #EA9649;
+  max-width: 100%;
+  margin: 0 auto;
+}
+
+button,input{
+    border: none;
+    outline: none;
+    background: none;
+    font-size: 1rem;		
+}
+
+
+
+.body{
+  width: 100%;
+  height: 100%;
+}
+
+.bg{
+  width: 100%;
+  top: 0;
+  position: absolute;
+  z-index: -1;
+}
+
+.content{
+  width: 90%;
+  margin: 0 auto;
+}
+
+.title{
+  text-align: center;
+  padding-top: 6%;
+}
+
+.input{
+  display: flex;
+  width: 100%;
+  font-size: 0;
+  position: relative;
+}
+
+.input input{
+  border: #EA9649 0.13rem solid;
+  border-right: none;
+  background: none;
+  color: #EA9649;
+  padding-left: 0.63rem;
+  padding-right: 2.63rem;
+  margin: 0;
+  flex: 30;
+}
+
+.input span{
+  display: inline-block;
+  height: 1rem;
+  padding: 0.63rem 1.25rem;
+  border-top: #EA9649 0.13rem solid;
+  border-bottom: #EA9649 0.13rem solid;
+  flex: 1;
+  text-align: center;
+}
+
+.input span img{
+  vertical-align: middle;
+  display: inline-block;
+  height: 100%;
+  opacity: 0;
+}
+
+.input .button{
+  flex: 3;
+  padding: 0.63rem 1.25rem;
+  background: #EA9649;
+  border-top: #EA9649 0.13rem solid;
+  border-bottom: #EA9649 0.13rem solid;
+  margin: 0;
+  font-size:1rem;
+  line-height: 1rem;
+  height: 1rem;
+  margin: 0 auto;
+  color: #000;
+  min-width: 15%;
+  text-align: center;
+}
+
+.result{
+  width: 100%;
+  margin: 0.63rem 0;
+  min-height: 1.88rem;
+}
+
+.result li{
+  list-style: none;
+  display: inline-block;
+  margin-right: 0.38rem;
+  padding: 0.13rem 0.25rem;
+  vertical-align: middle;
+}
+
+.result li:last-of-type{
+  margin-right: 0;
+}
+.result li img{
+  width: 1.25rem;
+  vertical-align: middle;
+}
+
+
+.result .active{
+  background: #EA9649;
+  color: #000;
+}
+
+#canvasWrap {
+  position:relative;
+  margin:0 auto;
+  width:100%;
+  height:40%;
+  min-height: 20rem;
+  border:0.13rem solid #EA9649;
+  font-size: 0;
+}
+
+#clear{
+  opacity: 0;
+}
+
+.cls{
+  text-align: center;
+  width: 100%;
+  position: fixed;
+  left: 50%;
+  transform: translateX(-50%);
+}
+
+.cls img{
+  width: 1.88rem;
+}
+
+.result-page{
+  display: none;
+}
+
+.reload{
+  margin-top: 0.5rem;
+  text-align: right;
+}
+
+#reload{
+  width: 1rem;
+}
+
+.result-page ul {
+  margin-left: 0.63rem;
+  margin-top: 0.5rem;
+  user-select: none;
+}
+
+.result-page ul li{
+  display: flex;
+  align-items: center;
+  margin-top: 0.63rem;
+  font-size: 1.2rem;
+}
+
+.result-page ul li img{
+  width: 7rem;
+  height: 7rem;
+  margin-right: 1.88rem;
+}
+.result-page ul li span{
+  width: 15.63rem;
+  line-height: 1.5;
+}
+
+.no-result{
+  text-align: center;
+  margin-top: 5rem;
+  display: none;
+}
+
+.no-result img{
+  width: 14.38rem;
+  height:auto;
+}
+.no-result p{
+  line-height: 1.5;
+}

二进制
shurufa/images/bg.png


二进制
shurufa/images/bodang.png


二进制
shurufa/images/close.png


二进制
shurufa/images/cls.png


二进制
shurufa/images/no-result.png


二进制
shurufa/images/reload.png


二进制
shurufa/images/二维码按钮.png


二进制
shurufa/images/搜索按钮.png


二进制
shurufa/images/暂停按钮.png


二进制
shurufa/images/点赞按钮(已触发).png


二进制
shurufa/images/点赞按钮(未触发).png


+ 57 - 0
shurufa/index.html

@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
+  <link rel="stylesheet" href="./css/index.css">
+  <title>手写</title>
+</head>
+<body>
+  <div class="body">
+    <!-- <img id="bg" class="bg" src="./images/bg.png" alt=""> -->
+    <div class="content">
+      <!-- <div class="title">搜索页面</div> -->
+      <div class="input">
+        <input id="input" type="text">
+        <span>
+          <img id="clsipt" src="./images/close.png" alt="">
+        </span>
+        <div id="search" class="button">搜索</div>
+      </div>
+      
+      <!-- 搜索页面 -->
+      <div class="search-page">
+        <ul class="result">
+          <li>
+            <ul id="result"></ul>
+          </li>
+          <li><img id="clear" src="./images/close.png" alt=""></li>
+        </ul>
+        <div id="canvasWrap">
+          <canvas id="canvas"></canvas>
+        </div>
+      </div>
+
+      <div class="result-page">
+        <div class="reload">
+          <img id="reload" src="./images/reload.png" alt="">
+        </div>
+        <ul>
+        </ul>
+        <div class="no-result">
+          <img src="./images/no-result.png" alt="">
+          <p>暂时没有结果呢,<br/>请试一下其他关键字</p>
+        </div>
+      </div>
+
+      <!-- <div class="cls">
+        <img src="./images/cls.png" alt="">
+      </div> -->
+
+    </div>
+  </div>
+  <script src="./js/jquery.min.js"></script>
+  <script src="./js/handwritingapi.js"></script>
+  <script src="./js/index.js?t=20200921"></script>
+</body>
+</html>

+ 295 - 0
shurufa/js/handwritingapi.js

@@ -0,0 +1,295 @@
+QQShuru = {};
+QQShuru.Util = {};
+QQShuru.Util.Browser = {};
+QQShuru.Util.Browser.isIE = (navigator.appName == "Microsoft Internet Explorer");
+
+QQShuru.Util.Ajax = {};
+QQShuru.Util.Ajax.get = function(a, c) {
+    var b = document.createElement("script");
+    b.setAttribute("charset", "utf-8");
+    b.id = Math.random();
+    document.getElementsByTagName("head")[0].appendChild(b);
+    b.src = a + "&c=" + c;
+    if (QQShuru.Util.Browser.isIE) {
+        b.onreadystatechange = function() {
+            if (b.readyState == "loaded") {
+                document.getElementsByTagName("head")[0].removeChild(b)
+            }
+        }
+    } else {
+        b.onload = function() {
+            document.getElementsByTagName("head")[0].removeChild(b)
+        }
+    }
+};
+QQShuru.Util.Event = {};
+QQShuru.Util.Event.addEvent = function() {
+    if (QQShuru.Util.Browser.isIE) {
+        return function(b, c, a) {
+            b.attachEvent("on" + c, a)
+        }
+    } else {
+        return function(b, c, a, d) {
+            b.addEventListener(c, a, d || false)
+        }
+    }
+} ();
+QQShuru.Util.Event.remEvent = function() {
+    if (QQShuru.Util.Browser.isIE) {
+        return function(b, c, a) {
+            b.detachEvent("on" + c, a)
+        }
+    } else {
+        return function(b, c, a, d) {
+            b.removeEventListener(c, a, d || false)
+        }
+    }
+} ();
+QQShuru.Util.Event.getPoint = function(a) {
+    if (QQShuru.Util.Browser.isIE) {
+        return [a.x, a.y]
+    } else {
+        return [a.layerX, a.layerY]
+    }
+};
+/*
+    初始化函数
+*   params:obj
+*   obj={
+*       canvasId:canvas的id  --required
+*       lineColor:  默认#606060  ,
+*       lineWidth:  默认10
+*       backBtnId: 退一步按钮id
+*       clearBtnId:清空canvas按钮id
+*       callback:func  每次请求后回调
+*   }
+*
+*
+*/
+QQShuru.HWPanel = function(obj) {
+    var o = QQShuru.Util.Browser.isIE;
+    var m = QQShuru.Util.Event.addEvent;
+    var j = QQShuru.Util.Event.remEvent;
+    var B = QQShuru.Util.Event.getPoint;
+    var h = document;
+    var H = document.body;
+    var z = document.documentElement;
+    var f = [];
+    var K = [];
+    var O = [];
+
+    var i = document.querySelector(obj.canvasId);
+    var v = o ? 1 : 0;
+    var a = 2;
+    var N = i.clientWidth;//canvas宽度
+    var R = i.clientHeight;//canvas高度
+    var c = obj.lineColor?obj.lineColor:"#606060"; //线条颜色
+    var y = obj.lineWidth?obj.lineWidth:10;//线条宽度
+    var t = "round";
+    var J = !!i.getContext;
+    if (J) {
+        var Q = i.getContext("2d");
+        Q.lineCap = t;
+        Q.lineJoin = t;
+        Q.lineWidth = y;
+        Q.strokeStyle = c
+    }
+    var L = false;
+    var P = false;
+    var u = 0;
+    var T = [];
+    var r = 0;
+    var e = [],
+        d = [],
+        I = [];
+    var D = [],
+        C = [];
+    pointsDeltaXY = [];
+    var k = [0, 0];
+    //鼠标按下事件
+    var l = function(W) {
+        if (v !== W.button) {
+            return
+        }
+        var Y = B(W);
+        if (!Y) {
+            return
+        }
+        L = true;
+        r = 0;
+        e = [];
+        d = [];
+        I = [];
+        D = [];
+        C = [];
+        pointsDeltaXY = [];
+        e[r] = Y[0];
+        d[r] = Y[1];
+        I[r * 2] = Y[0];
+        I[r * 2 + 1] = Y[1];
+        D[r] = Y[0];
+        C[r] = Y[1];
+        pointsDeltaXY[r * 2] = Y[0];
+        pointsDeltaXY[r * 2 + 1] = Y[1];
+        if (J) {
+            Q.beginPath();
+            Q.moveTo(Y[0], Y[1])
+        }
+        k[0] = Y[0];
+        k[1] = Y[1];
+        r++;
+        if (o) {
+            m(i, "losecapture", n);
+            i.setCapture()
+        } else {
+            m(window, "blur", n)
+        }
+    };
+    //鼠标移动事件
+    var A = function(W) {
+        if (!L) {
+            return
+        }
+        var Y = B(W);//坐标
+        if (!Y) {
+            return
+        }
+        e[r] = Y[0];
+        d[r] = Y[1];
+        I[r * 2] = Y[0];
+        I[r * 2 + 1] = Y[1];
+        D[r] = Y[0] - k[0];
+        C[r] = Y[1] - k[1];
+        pointsDeltaXY[r * 2] = D[r];
+        pointsDeltaXY[r * 2 + 1] = C[r];
+        if (J) {
+            Q.lineTo(Y[0], Y[1]);
+            Q.stroke()
+        } else {
+            var X = T[u].e.path;
+            X.value = X.value.replace(" e", "," + Y[0] + "," + Y[1] + " e")
+        }
+        k[0] = Y[0];
+        k[1] = Y[1];
+        r++
+    };
+    //鼠标松开事件
+    var n = function(W) {
+        if (!L) {
+            return
+        }
+        L = false;
+        if (1 === r) {
+            if (!J) {
+                T[u].e.style.visibility = "hidden"
+            }
+            return
+        }
+        if (J) {
+            Q.closePath();
+            var Z = i.cloneNode(false);
+            Z.style.display = "none";
+            Z.getContext("2d").drawImage(i, 0, 0);
+            T[u] = {
+                e: Z
+            };
+            Z = null
+        }
+        var aa = T[u];
+        aa.count = r;
+        aa.x = e.slice(0);
+        aa.y = d.slice(0);
+        aa.xy = I.slice(0);
+        aa.deltaX = D.slice(0);
+        aa.deltaY = C.slice(0);
+        aa.deltaXY = pointsDeltaXY.slice(0);
+        u++;
+        var X = [];
+        for (var Y = 0; Y < r; Y++) {
+            X[Y] = "[" + e[Y] + ", " + d[Y] + "]"
+        }
+        if (o) {
+            j(i, "losecapture", n);
+            i.releaseCapture()
+        } else {
+            j(window, "blur", n)
+        }
+        if (1 === u) {
+            i.className = "writting"
+        }
+        s(u);
+    };
+    //清空所有
+    var V = function(W) {
+        if (0 === u) {
+            return
+        }
+        var ab = "";
+        if (J) {
+            Q.clearRect(0, 0, N, R)
+        }
+        for (var Z = 0; Z < u; Z++) {
+            T[Z].e.style.visibility = "hidden"
+        }
+        u = 0;
+        i.className = ""
+    };
+    //退一笔
+    var g = function(W) {
+        if (0 === u) {
+            return
+        }
+        if (1 === u) {
+            V();
+            return
+        }
+        u--;
+        if (J) {
+            Q.clearRect(0, 0, N, R);
+            Q.drawImage(T[u - 1].e, 0, 0)
+        }
+        T[u].e.style.visibility = "hidden";
+        s(u)
+    };
+    var p = function(W, ab) {
+        var aa = ab || W.length;
+        var Z = "";
+        var ad = "";
+        for (var X = 0; X < aa; ++X) {
+            var ac = W[X];
+            ad = X ? ",eb,": "";
+            var Y = ad + ac.deltaXY.join(",");
+            Z += Y
+        }
+        return Z
+    };
+    this.ajax_callback = function(X) {
+        obj.callback&&obj.callback(X)
+    };
+    QQShuru.HWPanel.ajax_callback = this.ajax_callback;
+    var s = function(Y) {
+        var Z = p(T, Y);
+        var ab = "QQShuru.HWPanel.ajax_callback";
+        var W = "https://handwriting.shuru.qq.com/cloud/cgi-bin/cloud_hw_pub.wsgi";
+        var aa = "track_str=" + Z + "&cmd=0";
+        var X = W + "?" + aa;
+        QQShuru.Util.Ajax.get(X, ab)
+    };
+    m(i, "mousedown", l);
+    m(i, "mousemove", A);
+    m(i, "mouseup", n);
+    m(i, "dblclick", V);
+    m(i, "contextmenu",
+        function(W) {
+            o ? (W.returnValue = false) : W.preventDefault()
+        });
+    m(h, "mouseup", n);
+    if(obj.backBtnId){
+        m(document.querySelector(obj.backBtnId), "click", g);
+    }
+    if(obj.clearBtnId){
+        m(document.querySelector(obj.clearBtnId), "click", V);
+    }
+    this.clear=V;//暴露清空canvas的方法到外部
+    this.back=g;//暴露退一步的方法到外部
+};

+ 150 - 0
shurufa/js/index.js

@@ -0,0 +1,150 @@
+$(function(){
+  // let base = 'http://119.23.129.199:8100/'
+  let base = '/'
+  
+  var stopClick = false
+
+  function getQueryString(value) {
+    var reg = new RegExp("(^|&)" + value + "=([^&]*)(&|$)", "i");
+    var r = window.location.search.substr(1).match(reg);
+    if (r != null) return unescape(r[2]); return null;
+  }
+    $('#canvas').attr('width',$('#canvasWrap').width())
+    $('#canvas').attr('height',$('#canvasWrap').height())
+
+    init()
+  
+    $('#clear').click(function () {
+      $('#clear').css('opacity',0)
+      $('#result').html('')
+    })
+
+    $('#clsipt').click(function () {
+      $('#input').val('')
+      $('#clsipt').css('opacity',0)
+      $('.search-page').show()
+      $('.result-page').hide()
+    })
+    
+    $('#input').on('input propertychange', function() {
+      var count = $(this).val().length;
+      $('#clsipt').css('opacity',count?1:0)
+     });
+  
+    $('#search').click(function () {
+      search()
+    })
+
+    $('#reload').click(function () {
+      location.reload()
+    })
+
+    let startY = ''
+    let lastY = ''
+
+    let mMove = function (event) {
+      event.stopPropagation()
+      event.preventDefault()
+      let deltaY = event.clientY - startY
+      stopClick = true
+
+      if (lastY == deltaY) {
+        return
+      }
+      let de = document.documentElement.scrollTop||document.body.scrollTop
+      dic = lastY>deltaY? 1:-1
+      let to = de + dic * 15
+      window.scrollTo(0,to)
+      // lastY>deltaY?window.scrollTo(deltaY):'下'
+      lastY = deltaY
+    }
+
+    $('.body').mousedown(function (e) {
+      startY = e.clientY
+      stopClick = false
+      $('.body').mousemove(mMove)
+    })
+
+    $('.body').mouseup(function (e) {
+      $('.body').off('mousemove',mMove)
+    })
+  function callbackfunc(ret){
+    let html = ''
+
+    ret.cand.forEach(item => {
+      html+=`<li>${item}</li>`
+    });
+    $('#clear').css('opacity',1)
+    $('#result').html(html)
+    $('#result').undelegate()
+    $('#result').delegate('li','click',function (e) {
+      e.stopPropagation()
+      e.preventDefault()
+      let target = e.target
+      $('#input').val($('#input').val() + $(target).text())
+      $('#clsipt').css('opacity',1)
+      $('#clear').click()
+    })
+  
+  }
+
+  function search() {
+    let data = {
+      id: getQueryString('id') || '',
+      name: $('#input').val(),
+    }
+    $.ajax({
+      url: base + 'api/searchCollection',
+      type: "POST",
+      data: JSON.stringify(data),
+      dataType: "json",
+      contentType: "application/json;charset=utf-8",
+      success: function (data) {
+        if (data.code!=0) {
+          return alert(data.msg)
+        }
+        $('.search-page').hide()
+      
+        if (data.data.length<=0) {
+          $('.result-page').fadeIn()
+          $('.result-page ul').hide()
+          $('.no-result').fadeIn()
+        }
+
+        else{
+          $('.no-result').hide()
+          $('.result-page').fadeIn()
+          $('.result-page ul').fadeIn()
+
+          let html = ``
+          data.data.forEach(item=>{
+            html += `<li data-id="${item.id}">
+                      <img src="${item.pic}" data-id="${item.id}" alt="">
+                      <span data-id="${item.id}">${item.name}</span>
+                    </li>`
+          })
+          $('.result-page ul').html(html)
+          let arr = Array.from(document.querySelectorAll(".result-page ul li"))
+          arr.forEach(function(dom) {
+            dom.addEventListener("mouseup", function(e) {
+              let id = e.target.dataset.id
+              setTimeout(function() {
+                (window.resultCallback&&!stopClick) && window.resultCallback(parseInt(id)); 
+              });
+            });
+          });
+        }
+
+      }
+  })
+  }
+
+  function init() {
+      QQShuru.HWPanel({
+          canvasId:"#canvas",
+          lineColor:"#EA9649",
+          clearBtnId:"#clear",
+          callback:callbackfunc
+      });
+  }
+})

文件差异内容过多而无法显示
+ 4 - 0
shurufa/js/jquery.min.js


+ 23 - 0
src/App.vue

@@ -0,0 +1,23 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+import '@/assets/style/reset.css'
+import '@/assets/style/public.css'
+import '@/assets/font/iconfont.css'
+
+export default {
+  name: 'App'
+}
+</script>
+
+<style>
+  #app{
+    width: 100%;
+    height: 100%;
+    background-color: #f0f0f2;
+  }
+</style>

+ 539 - 0
src/assets/font/demo.css

@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+  font-family: "iconfont logo";
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+  font-family: "iconfont logo";
+  font-size: 160px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+  position: relative;
+}
+
+.nav-tabs .nav-more {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  height: 42px;
+  line-height: 42px;
+  color: #666;
+}
+
+#tabs {
+  border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+  cursor: pointer;
+  width: 100px;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+  font-size: 16px;
+  border-bottom: 2px solid transparent;
+  position: relative;
+  z-index: 1;
+  margin-bottom: -1px;
+  color: #666;
+}
+
+
+#tabs .active {
+  border-bottom-color: #f00;
+  color: #222;
+}
+
+.tab-container .content {
+  display: none;
+}
+
+/* 页面布局 */
+.main {
+  padding: 30px 100px;
+  width: 960px;
+  margin: 0 auto;
+}
+
+.main .logo {
+  color: #333;
+  text-align: left;
+  margin-bottom: 30px;
+  line-height: 1;
+  height: 110px;
+  margin-top: -50px;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.main .logo a {
+  font-size: 160px;
+  color: #333;
+}
+
+.helps {
+  margin-top: 40px;
+}
+
+.helps pre {
+  padding: 20px;
+  margin: 10px 0;
+  border: solid 1px #e7e1cd;
+  background-color: #fffdef;
+  overflow: auto;
+}
+
+.icon_lists {
+  width: 100% !important;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.icon_lists li {
+  width: 100px;
+  margin-bottom: 10px;
+  margin-right: 20px;
+  text-align: center;
+  list-style: none !important;
+  cursor: default;
+}
+
+.icon_lists li .code-name {
+  line-height: 1.2;
+}
+
+.icon_lists .icon {
+  display: block;
+  height: 100px;
+  line-height: 100px;
+  font-size: 42px;
+  margin: 10px auto;
+  color: #333;
+  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+  -moz-transition: font-size 0.25s linear, width 0.25s linear;
+  transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+  font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+  /* 通过设置 font-size 来改变图标大小 */
+  width: 1em;
+  /* 图标和文字相邻时,垂直对齐 */
+  vertical-align: -0.15em;
+  /* 通过设置 color 来改变 SVG 的颜色/fill */
+  fill: currentColor;
+  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+      normalize.css 中也包含这行 */
+  overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+  color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+  color: #666;
+  font-size: 14px;
+  line-height: 1.8;
+}
+
+.highlight {
+  line-height: 1.5;
+}
+
+.markdown img {
+  vertical-align: middle;
+  max-width: 100%;
+}
+
+.markdown h1 {
+  color: #404040;
+  font-weight: 500;
+  line-height: 40px;
+  margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+  color: #404040;
+  margin: 1.6em 0 0.6em 0;
+  font-weight: 500;
+  clear: both;
+}
+
+.markdown h1 {
+  font-size: 28px;
+}
+
+.markdown h2 {
+  font-size: 22px;
+}
+
+.markdown h3 {
+  font-size: 16px;
+}
+
+.markdown h4 {
+  font-size: 14px;
+}
+
+.markdown h5 {
+  font-size: 12px;
+}
+
+.markdown h6 {
+  font-size: 12px;
+}
+
+.markdown hr {
+  height: 1px;
+  border: 0;
+  background: #e9e9e9;
+  margin: 16px 0;
+  clear: both;
+}
+
+.markdown p {
+  margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+  width: 80%;
+}
+
+.markdown ul>li {
+  list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+  margin: 0.6em 0;
+}
+
+.markdown ol>li {
+  list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown code {
+  margin: 0 3px;
+  padding: 0 5px;
+  background: #eee;
+  border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+  font-weight: 600;
+}
+
+.markdown>table {
+  border-collapse: collapse;
+  border-spacing: 0px;
+  empty-cells: show;
+  border: 1px solid #e9e9e9;
+  width: 95%;
+  margin-bottom: 24px;
+}
+
+.markdown>table th {
+  white-space: nowrap;
+  color: #333;
+  font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+  border: 1px solid #e9e9e9;
+  padding: 8px 16px;
+  text-align: left;
+}
+
+.markdown>table th {
+  background: #F7F7F7;
+}
+
+.markdown blockquote {
+  font-size: 90%;
+  color: #999;
+  border-left: 4px solid #e9e9e9;
+  padding-left: 0.8em;
+  margin: 1em 0;
+}
+
+.markdown blockquote p {
+  margin: 0;
+}
+
+.markdown .anchor {
+  opacity: 0;
+  transition: opacity 0.3s ease;
+  margin-left: 8px;
+}
+
+.markdown .waiting {
+  color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+  opacity: 1;
+  display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+  clear: both;
+}
+
+
+.hljs {
+  display: block;
+  background: white;
+  padding: 0.5em;
+  color: #333333;
+  overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+  color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+  color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+  color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+  color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+  color: #63a35c;
+}
+
+.hljs-tag {
+  color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+  color: #795da3;
+}
+
+.hljs-addition {
+  color: #55a532;
+  background-color: #eaffea;
+}
+
+.hljs-deletion {
+  color: #bd2c00;
+  background-color: #ffecec;
+}
+
+.hljs-link {
+  text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+
+  -moz-tab-size: 4;
+  -o-tab-size: 4;
+  tab-size: 4;
+
+  -webkit-hyphens: none;
+  -moz-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+@media print {
+
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: .5em 0;
+  overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+  padding: .1em;
+  border-radius: .3em;
+  white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+
+.token.punctuation {
+  color: #999;
+}
+
+.namespace {
+  opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+
+.token.italic {
+  font-style: italic;
+}
+
+.token.entity {
+  cursor: help;
+}

+ 354 - 0
src/assets/font/demo_index.html

@@ -0,0 +1,354 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>IconFont Demo</title>
+  <link rel="shortcut icon" href="https://gtms04.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
+  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
+  <link rel="stylesheet" href="demo.css">
+  <link rel="stylesheet" href="iconfont.css">
+  <script src="iconfont.js"></script>
+  <!-- jQuery -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
+  <!-- 代码高亮 -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
+</head>
+<body>
+  <div class="main">
+    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">&#xe86b;</a></h1>
+    <div class="nav-tabs">
+      <ul id="tabs" class="dib-box">
+        <li class="dib active"><span>Unicode</span></li>
+        <li class="dib"><span>Font class</span></li>
+        <li class="dib"><span>Symbol</span></li>
+      </ul>
+      
+      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=1563253" target="_blank" class="nav-more">查看项目</a>
+      
+    </div>
+    <div class="tab-container">
+      <div class="content unicode" style="display: block;">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe699;</span>
+                <div class="name">index</div>
+                <div class="code-name">&amp;#xe699;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe69a;</span>
+                <div class="name">work</div>
+                <div class="code-name">&amp;#xe69a;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe69b;</span>
+                <div class="name">system</div>
+                <div class="code-name">&amp;#xe69b;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe69c;</span>
+                <div class="name">senior</div>
+                <div class="code-name">&amp;#xe69c;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe69d;</span>
+                <div class="name">sys01</div>
+                <div class="code-name">&amp;#xe69d;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe69e;</span>
+                <div class="name">sys02</div>
+                <div class="code-name">&amp;#xe69e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe69f;</span>
+                <div class="name">sys03</div>
+                <div class="code-name">&amp;#xe69f;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6a0;</span>
+                <div class="name">arrow</div>
+                <div class="code-name">&amp;#xe6a0;</div>
+              </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="unicode-">Unicode 引用</h2>
+          <hr>
+
+          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
+          <ul>
+            <li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
+            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
+            <li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
+          </ul>
+          <blockquote>
+            <p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
+          </blockquote>
+          <p>Unicode 使用步骤如下:</p>
+          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
+<pre><code class="language-css"
+>@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.eot');
+  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
+      url('iconfont.woff2') format('woff2'),
+      url('iconfont.woff') format('woff'),
+      url('iconfont.ttf') format('truetype'),
+      url('iconfont.svg#iconfont') format('svg');
+}
+</code></pre>
+          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
+<pre><code class="language-css"
+>.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
+<pre>
+<code class="language-html"
+>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
+</code></pre>
+          <blockquote>
+            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+          </blockquote>
+          </div>
+      </div>
+      <div class="content font-class">
+        <ul class="icon_lists dib-box">
+          
+          <li class="dib">
+            <span class="icon iconfont iconindex"></span>
+            <div class="name">
+              index
+            </div>
+            <div class="code-name">.iconindex
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconwork"></span>
+            <div class="name">
+              work
+            </div>
+            <div class="code-name">.iconwork
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsystem"></span>
+            <div class="name">
+              system
+            </div>
+            <div class="code-name">.iconsystem
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsenior"></span>
+            <div class="name">
+              senior
+            </div>
+            <div class="code-name">.iconsenior
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys"></span>
+            <div class="name">
+              sys01
+            </div>
+            <div class="code-name">.iconsys
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys1"></span>
+            <div class="name">
+              sys02
+            </div>
+            <div class="code-name">.iconsys1
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys2"></span>
+            <div class="name">
+              sys03
+            </div>
+            <div class="code-name">.iconsys2
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconarrow"></span>
+            <div class="name">
+              arrow
+            </div>
+            <div class="code-name">.iconarrow
+            </div>
+          </li>
+          
+        </ul>
+        <div class="article markdown">
+        <h2 id="font-class-">font-class 引用</h2>
+        <hr>
+
+        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
+        <p>与 Unicode 使用方式相比,具有如下特点:</p>
+        <ul>
+          <li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
+          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
+          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
+          <li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
+        </ul>
+        <p>使用步骤如下:</p>
+        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
+<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
+</code></pre>
+        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;span class="iconfont iconxxx"&gt;&lt;/span&gt;
+</code></pre>
+        <blockquote>
+          <p>"
+            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+        </blockquote>
+      </div>
+      </div>
+      <div class="content symbol">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconindex"></use>
+                </svg>
+                <div class="name">index</div>
+                <div class="code-name">#iconindex</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconwork"></use>
+                </svg>
+                <div class="name">work</div>
+                <div class="code-name">#iconwork</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsystem"></use>
+                </svg>
+                <div class="name">system</div>
+                <div class="code-name">#iconsystem</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsenior"></use>
+                </svg>
+                <div class="name">senior</div>
+                <div class="code-name">#iconsenior</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys"></use>
+                </svg>
+                <div class="name">sys01</div>
+                <div class="code-name">#iconsys</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys1"></use>
+                </svg>
+                <div class="name">sys02</div>
+                <div class="code-name">#iconsys1</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys2"></use>
+                </svg>
+                <div class="name">sys03</div>
+                <div class="code-name">#iconsys2</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconarrow"></use>
+                </svg>
+                <div class="name">arrow</div>
+                <div class="code-name">#iconarrow</div>
+            </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="symbol-">Symbol 引用</h2>
+          <hr>
+
+          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
+            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
+          <ul>
+            <li>支持多色图标了,不再受单色限制。</li>
+            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
+            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
+            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
+          </ul>
+          <p>使用步骤如下:</p>
+          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
+<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
+</code></pre>
+          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
+<pre><code class="language-html">&lt;style&gt;
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+&lt;/style&gt;
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
+  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
+&lt;/svg&gt;
+</code></pre>
+          </div>
+      </div>
+
+    </div>
+  </div>
+  <script>
+  $(document).ready(function () {
+      $('.tab-container .content:first').show()
+
+      $('#tabs li').click(function (e) {
+        var tabContent = $('.tab-container .content')
+        var index = $(this).index()
+
+        if ($(this).hasClass('active')) {
+          return
+        } else {
+          $('#tabs li').removeClass('active')
+          $(this).addClass('active')
+
+          tabContent.hide().eq(index).fadeIn()
+        }
+      })
+    })
+  </script>
+</body>
+</html>

文件差异内容过多而无法显示
+ 49 - 0
src/assets/font/iconfont.css


二进制
src/assets/font/iconfont.eot


文件差异内容过多而无法显示
+ 1 - 0
src/assets/font/iconfont.js


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

@@ -0,0 +1,65 @@
+{
+  "id": "1563253",
+  "name": "舟山博物馆de'mo",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "12322900",
+      "name": "index",
+      "font_class": "index",
+      "unicode": "e699",
+      "unicode_decimal": 59033
+    },
+    {
+      "icon_id": "12322902",
+      "name": "work",
+      "font_class": "work",
+      "unicode": "e69a",
+      "unicode_decimal": 59034
+    },
+    {
+      "icon_id": "12322906",
+      "name": "system",
+      "font_class": "system",
+      "unicode": "e69b",
+      "unicode_decimal": 59035
+    },
+    {
+      "icon_id": "12322907",
+      "name": "senior",
+      "font_class": "senior",
+      "unicode": "e69c",
+      "unicode_decimal": 59036
+    },
+    {
+      "icon_id": "12322908",
+      "name": "sys01",
+      "font_class": "sys",
+      "unicode": "e69d",
+      "unicode_decimal": 59037
+    },
+    {
+      "icon_id": "12322914",
+      "name": "sys02",
+      "font_class": "sys1",
+      "unicode": "e69e",
+      "unicode_decimal": 59038
+    },
+    {
+      "icon_id": "12322915",
+      "name": "sys03",
+      "font_class": "sys2",
+      "unicode": "e69f",
+      "unicode_decimal": 59039
+    },
+    {
+      "icon_id": "12322918",
+      "name": "arrow",
+      "font_class": "arrow",
+      "unicode": "e6a0",
+      "unicode_decimal": 59040
+    }
+  ]
+}

文件差异内容过多而无法显示
+ 50 - 0
src/assets/font/iconfont.svg


二进制
src/assets/font/iconfont.ttf


二进制
src/assets/font/iconfont.woff


二进制
src/assets/font/iconfont.woff2


二进制
src/assets/img/01jk.png


二进制
src/assets/img/01jks.png


二进制
src/assets/img/02fb.png


二进制
src/assets/img/03mg.png


二进制
src/assets/img/04fb.png


二进制
src/assets/img/1.png


二进制
src/assets/img/2.png


二进制
src/assets/img/4dage-logo.png


二进制
src/assets/img/bar.png


二进制
src/assets/img/bg.png


二进制
src/assets/img/bg1.png


二进制
src/assets/img/bg2.png


二进制
src/assets/img/down-arrow.png


二进制
src/assets/img/icon.png


二进制
src/assets/img/icon2.png


二进制
src/assets/img/icon2s.png


二进制
src/assets/img/logout.png


二进制
src/assets/img/noPicture.png


二进制
src/assets/img/up-arrow.png


二进制
src/assets/img/user.png


二进制
src/assets/img/zhoushan-logo.jpg


+ 171 - 0
src/assets/style/public.css

@@ -0,0 +1,171 @@
+.layout{
+  width: 100%;
+  height: 100%;
+}
+
+.layout-con{
+  width: 100%;
+  height: 100%;
+  position: fixed;
+}
+
+
+.imgdiv{
+  position: relative;
+  
+}
+
+.imgdiv >i{
+  font-size: 20px;
+  position: absolute;
+  top: 5px;
+  right: 5px;
+}
+
+.middle-title, .register-title,.forget-title{
+  color: #4d4d4d;
+  font-size: 1.75rem;
+  text-align: center;
+  margin-bottom: 3.4375rem;
+  line-height: 1.5;
+}
+
+.register-title,.forget-title{
+  margin-top: 1.25rem;
+}
+
+.middle{
+  position: absolute;
+  top: 30vh;
+  left: 50vw;
+  width: 23.75rem;
+  height: 18.75rem;
+  transform: translate(-50%,-50%);
+}
+.middle-subtitle{
+  font-size: .875rem;
+  font-weight: 500;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+
+.form-bottom{
+  display: flex;
+  justify-content: space-between;
+  padding: 0 .625rem;
+  color: #888;
+}
+
+.form-bottom span{
+  cursor: pointer;
+}
+
+.bottom-div{
+  text-align: center;
+  cursor: pointer;
+  color: #888888;
+}
+
+.test-btn{
+  color: #67c23a!important;
+}
+
+.view-btn{
+  color: #409eff!important;
+}
+.re-apply-btn{
+  color: #f56c6c!important;
+}
+.apply-btn{
+  color: #ec652d!important;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+
+.e-pagination{
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 1.5625rem;
+}
+
+:root{
+  --bg_color:rgba(255, 255, 255, 1);
+  --font_color:rgba(0, 0, 0, 0.88);
+  --font_color1:rgba(0, 0, 0, 0.38);
+}
+
+.theme{
+  background:url('~@/assets/img/bg1.png') no-repeat center center!important;
+  background-size: cover;
+}
+
+.theme-color{
+  color: var(--font_color)!important;
+}
+
+.theme-color1{
+  color: var(--font_color1)!important;
+}
+
+.card,.top-body {
+  background: var(--bg_color)!important;
+  border-radius: 2px!important;
+  color: #000!important;
+  border: none!important;
+}
+
+.el-table tr,.el-table th,.el-table{
+  background: none!important;
+  color: var(--font_color)!important;
+}
+
+.el-table th.is-leaf, .el-table td{
+  border-bottom: 1px solid rgba(0, 0, 0, 0.1)!important;
+  color:var(--font_color)!important;
+}
+
+table th > .cell{
+  color:var(--font_color1)!important;
+}
+
+.el-table--enable-row-hover .el-table__body tr:hover > td{
+  background: var(--bg_color)!important;
+}
+
+.el-table--group::after, .el-table--border::after, .el-table::before{
+  background: var(--bg_color)!important;
+}
+
+.el-pagination button:disabled{
+  background: none!important;
+}
+
+.el-pagination span:not([class*=suffix]), .el-pagination button{
+  color:var(--font_color)!important;
+}
+
+@media screen and (max-width: 1700px) {
+  html,body{
+    font-size: 15px;
+  }
+}
+
+
+@media screen and (max-width: 1400px) {
+  html,body{
+    font-size: 13px;
+  }
+}
+
+
+@media screen and (max-width: 1100px) {
+  html,body{
+    font-size: 12px;
+  }
+}

+ 109 - 0
src/assets/style/reset.css

@@ -0,0 +1,109 @@
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  font-family: "Microsoft YaHei","Microsoft JhengHei";;
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	vertical-align: baseline;
+}
+*{
+  box-sizing: border-box;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section, main {
+	display: block;
+}
+html{
+	height: 100%;
+}
+body {
+	line-height: 1;
+	height: 100%;
+}
+html,body{
+	font-size: 16px;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
+button{outline:none; border: none;}
+input, button, select, textarea {
+outline: none;
+appearance: none;
+-webkit-appearance: none;
+border-radius: 0;
+-webkit-appearance: textfield;
+    background-color: white;
+    -webkit-rtl-ordering: logical;
+    cursor: text;
+    padding: 1px;
+    border-color: initial;
+    border-image: initial;
+}
+input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{
+        -webkit-appearance:textfield;
+}
+input[type="number"]{
+        -moz-appearance:textfield;
+}
+input:focus { outline: none; } 
+select::-ms-expand {
+display: none;
+}
+
+
+::-webkit-scrollbar-track-piece {  
+	background-color:#ffffff;  
+}  
+::-webkit-scrollbar {  
+	width:8px;  
+	height:13px;  
+}  
+::-webkit-scrollbar-thumb {  
+	background-color:#e5e5e5;  
+	background-clip:padding-box;  
+	min-height:20px;  
+	border-radius: 3px;
+}  
+::-webkit-scrollbar-thumb:hover {  
+	background-color:#929292;  
+}  
+
+table th,
+table td {
+  text-align: center !important;
+}
+
+table th > .cell{
+	color: #000;
+}
+
+textarea{
+  height: 8rem!important;
+}

+ 281 - 0
src/components/cropper/index.vue

@@ -0,0 +1,281 @@
+<template>
+  <div class="custom-upload">
+    <el-dialog
+      title="图片裁剪"
+      :visible.sync="showCropper"
+      top="6vh"
+      width="50%"
+      height="700"
+      class="cropper-dialog"
+      center
+      append-to-body
+    >
+      <vue-cropper
+        v-if="showCropper"
+        id="corpper"
+        ref="cropper"
+        :class="{'corpper-warp':showCropper}"
+        v-bind="cropper"
+        :style="{height:`${height}px`}"
+      />
+      <div v-if="showCropper" class="cropper-button">
+        <el-button class="cancel-btn" size="small" @click.native="showCropper=false">取消</el-button>
+        <el-button size="small" type="primary" :loading="loading" @click="uploadCover">完成</el-button>
+      </div>
+    </el-dialog>
+    <input
+      :id="id"
+      type="file"
+      style="display: none"
+      name="single"
+      accept="image/*"
+      @change="onChange($event)"
+    />
+
+    <div class="avatar-uploader" @click="handleOpenFile()">
+      <div class="el-upload el-upload--text">
+        <div v-if="img" class="imgdiv">
+          <img
+            style="width: 100%;height:100%;"
+            :src="$serverName.replace('/zhoushan','') + img"
+          >
+          <i class="el-icon-circle-close" @click.stop="clearImg"></i>
+        </div>
+        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+      </div>
+    </div>
+
+    <div v-if="tips" class="tips clear-margin-top">{{ tips }}</div>
+  </div>
+</template>
+
+<script>
+// 上传文件组件
+import { VueCropper } from 'vue-cropper'
+
+// 定义的接口根据自己项目更换
+
+import { isImageFile, isMaxFileSize, readFile } from '@/utils/upload' // 见下文
+import { Message } from 'element-ui'
+
+export default {
+  components: {
+    VueCropper
+  },
+  props: {
+    // 最大上传文件的大小
+    maxFileSize: {
+      type: Number,
+      default: 1024 // (MB)
+    },
+    uploadUrl: {
+      type: String
+    },
+    img: {
+      type: String
+    },
+    // 提示内容
+    tips: {
+      type: String
+    },
+    // 图片裁剪比列
+    fixedNumber: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 图片文件分辨率的宽度
+    width: {
+      type: Number,
+      default: 8640
+    },
+    // 图片文件分辨率的高度
+    height: {
+      type: Number,
+      default: 1920
+    }
+  },
+  data () {
+    return {
+      id: 'cropper-input-' + new Date(),
+      loading: false,
+      showCropper: false,
+      imgName: '',
+      cropper: {
+        img: '',
+        info: true,
+        size: 1024 * 4,
+        outputType: 'jpg',
+        canScale: true,
+        autoCrop: true,
+        full: true,
+        // 只有自动截图开启 宽度高度才生效
+        autoCropWidth: this.width,
+        autoCropHeight: this.height,
+        fixedBox: false,
+        // 开启宽度和高度比例
+        fixed: true,
+        fixedNumber: this.fixedNumber,
+        original: false,
+        canMoveBox: true,
+        canMove: true
+      }
+    }
+  },
+  methods: {
+    // 打开文件
+    clearImg () {
+      this.$emit('clearImg')
+    },
+    handleOpenFile () {
+      const input = document.getElementById(this.id)
+      // 解决同一个文件不能监听的问题
+      input.addEventListener(
+        'click',
+        function () {
+          this.value = ''
+        },
+        false
+      )
+      // 点击input
+      input.click()
+    },
+
+    // 裁剪input 监听
+    async onChange (e) {
+      const file = e.target.files[0]
+
+      if (!file) {
+        return Message.error('选择图片失败')
+      }
+      // 验证文件类型
+      if (!isImageFile(file)) {
+        return
+      }
+      try {
+        // 读取文件
+        const src = await readFile(file)
+        this.imgName = file.name
+        this.showCropper = true
+        this.cropper.img = src
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 封面上传功能
+    uploadCover () {
+      this.$refs.cropper.getCropBlob(async imgRes => {
+        try {
+          // 文件大小限制
+          if (!isMaxFileSize(imgRes, this.maxFileSize)) {
+            return
+          }
+          this.loading = true
+          let formData = new FormData()
+          formData.append('file', imgRes)
+          formData.append('filename', this.imgName)
+
+          const res = await this.$http.post(
+            this.uploadUrl,
+            formData,
+            {'Content-Type': 'multipart/form-data'})
+
+          this.$emit('subUploadSucceed', res.data)
+          Message.success('上传成功')
+          this.loading = false
+          this.showCropper = false
+        } catch (error) {
+          this.loading = false
+          this.showCropper = false
+          Message.error(error.data.message)
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="less"  >
+#corpper {
+  width: 90%;
+  margin: 0 auto;
+  background-image: none;
+  background: #fff;
+  z-index: 1002;
+  height: 512px;
+}
+.cropper-dialog {
+  height: 800px;
+  text-align: center;
+  .el-dialog__header {
+    padding-top: 15px;
+  }
+  .el-dialog--center .el-dialog__body {
+    padding-top: 0;
+    padding-bottom: 15px;
+  }
+  .el-dialog {
+    text-align: center;
+  }
+}
+.cropper-button {
+  z-index: 1003;
+  text-align: center;
+  margin-top: 20px;
+  .el-button {
+    font-size: 16px;
+    cursor: pointer;
+    text-align: center;
+  }
+  .cancel-btn {
+    color: #373737;
+  }
+  .el-button:last-child {
+    margin-left: 100px;
+  }
+}
+.cropper-modal {
+  background-color: rgba(0, 0, 0, 0.5) !important;
+}
+.custom-upload {
+  .tips {
+    margin-top: 10px;
+    color: red;
+    font-size: 12px;
+  }
+  .clear-margin-top {
+    margin-top: 0;
+  }
+}
+</style>
+
+<style>
+.avatar-uploader{
+  width: 180px;
+  max-height: 180px;
+}
+.avatar-uploader .el-upload {
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+</style>

+ 58 - 0
src/components/crumbs/index.vue

@@ -0,0 +1,58 @@
+<!--  -->
+<template>
+<div class='crumbs card'><span v-for="(item,i) in data" :key='i'>{{item.name + (i === data.length - 1?'':' > ')}}</span></div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+export default {
+// import引入的组件需要注入到对象中才能使用
+  props: {
+    data: {
+      default: () => [],
+      type: Array
+    }
+  },
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style scoped>
+.crumbs{
+  color: #000;
+  padding: 20px;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+</style>

+ 97 - 0
src/components/focus-bang/index.vue

@@ -0,0 +1,97 @@
+<template>
+  <div class="f-layout">
+    <div class="f-txt theme-color1">文物关注度排行</div>
+    <ul class="timeLine">
+      <li class="theme-color1" :class="{active:item.id===active}" @click="active = item.id" v-for="(item,i) in time" :key="i"><span>{{item.name}}</span></li>
+    </ul>
+    <div class="card-layout">
+      <WwCard class="ww" :data="item" v-for="(item,i) in data" :key="i"/>
+    </div>
+  </div>
+</template>
+<script>
+import WwCard from '@/components/ww-card'
+
+const time = [
+  {
+    name: '日',
+    id: 'day'
+  }, {
+    name: '周',
+    id: 'week'
+  }, {
+    name: '月',
+    id: 'mouth'
+  }, {
+    name: '季',
+    id: 'season'
+  }, {
+    name: '年',
+    id: 'year'
+  }
+]
+export default {
+  props: ['data'],
+  data () {
+    return {
+      time,
+      active: 'day'
+    }
+  },
+  components: {
+    WwCard
+  }
+}
+</script>
+
+<style scoped>
+.f-layout{
+  width: 100%;
+  height: 50%;
+  position: relative;
+}
+.f-txt{
+  position: absolute;
+  top: 1.5rem;
+  left: 1.5rem;
+  font-weight: bold;
+}
+.timeLine{
+  display: inline-block;
+  position: absolute;
+  top: 1.2rem;
+  left: 10.5rem;
+}
+.timeLine li{
+  display: inline-block;
+  width:36px;
+  height:24px;
+  border-radius:3px;
+  background:rgba(255,255,255,0.05);
+  position: relative;
+  cursor: pointer;
+  margin: 0 5px;
+}
+
+.timeLine li span{
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+}
+
+.timeLine .active{
+  background:#532f1c;
+  color: #fff!important;
+}
+
+.card-layout{
+  margin: 60px 1.5rem;
+  display: flex;
+  justify-content: space-between;
+}
+
+.ww{
+  width: 32%!important;
+}
+</style>

+ 94 - 0
src/components/hot-bang/index.vue

@@ -0,0 +1,94 @@
+<template>
+  <div class="hot-layout">
+    <div class="h-txt theme-color1">文物热度榜</div>
+    <div class="hot-table">
+      <div class="hot-header">
+        <div class="theme-color1">
+          <span>序号</span>
+          <span>文物名称</span>
+        </div>
+        <span class="theme-color1">累计点赞数</span>
+      </div>
+      <ul>
+        <li v-for="(item,i) in data" :key="i">
+          <div>
+            <span class="xuhao" :class="{emxuhao:i===4}">{{i+1}}</span>
+            <span>{{item.name}}</span>
+          </div>
+          <span class="theme-color1">{{item.num}}</span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  props: ['data']
+}
+</script>
+
+<style scoped>
+.hot-layout{
+  position: relative;
+  height: 100%;
+}
+.h-txt{
+   position: absolute;
+  top: 1.5rem;
+  left: 1.5rem;
+  font-weight: bold;
+}
+.hot-table{
+  padding: 60px 1.5rem 0;
+}
+.hot-table .hot-header{
+  display: flex;
+  justify-content: space-between;
+}
+
+.hot-table .hot-header>div span:first-of-type{
+  width: 35px;
+  display: inline-block;
+}
+
+ul{
+  margin-top: 20px;
+  height: 290px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+ul li{
+  display: flex;
+  justify-content: space-between;
+  text-align: center;
+  line-height: 1;
+  margin: 12px 0;
+}
+ul li>div span:first-of-type{
+  display: inline-block;
+  width: 35px;
+  position: relative;
+  z-index: 99;
+  color: #fff;
+  font-size: 14px;
+}
+ul li>div .xuhao::after{
+  content: '';
+  width: 16px;
+  height: 16px;
+  display: inline-block;
+  background: #f76b6c;
+  position: absolute;
+  border-radius: 2px;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  z-index: -1;
+}
+
+ul li>div .emxuhao::after{
+  background: #67c241;
+}
+
+</style>

+ 50 - 0
src/components/info-card/index.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="info card">
+    <i class="iconfont iconsys theme-color1" :class="data.icon"></i>
+    <p class="theme-color1">{{data.name}}</p>
+    <p class="theme-color1">
+      <strong v-for="(item,i) in data.sub1" :key="i">
+        <span class="theme-color">{{item.bold.txt}}</span>{{item.txt}}
+      </strong>
+    </p>
+    <p class="theme-color1">
+      <strong v-for="(sub,idx) in data.sub2" :key="idx">
+        <span class="theme-color">{{sub.bold.txt}}</span>{{sub.txt}}
+      </strong>
+    </p>
+  </div>
+</template>
+<script>
+export default {
+  props: ['data']
+}
+</script>
+<style scoped>
+  .theme-color{
+    color: #6c1c1c!important;
+  }
+  .info{
+    height: 100%;
+    padding: 1.5rem;
+    box-sizing: border-box;
+    position: relative;
+  }
+  .iconfont{
+    position: absolute;
+    right: 1.5rem;
+    top: 1.5rem;
+    font-size: 60px;
+  }
+  p:first-of-type{
+    margin-bottom: 30px;
+    font-weight: bold;
+  }
+  p{
+    margin-bottom: 10px;
+  }
+  span{
+    font-size: 36px;
+    display: inline-block;
+    margin-right: 10px;
+  }
+</style>

+ 59 - 0
src/components/main-top/index.vue

@@ -0,0 +1,59 @@
+<!--  -->
+<template>
+<div class='main-top'>
+  <crumbs :data="crumb" />
+  <slot name='con'></slot>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import Crumbs from '../crumbs'
+
+export default {
+// import引入的组件需要注入到对象中才能使用
+  props: {
+    crumb: {
+      default: () => [],
+      type: Array
+    }
+  },
+  components: {Crumbs},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style scoped>
+.main-top{
+  background:none;
+}
+
+</style>

+ 57 - 0
src/components/nums/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <div class="nums">
+    <div class="n-top "><span>{{data.sum}}</span></div>
+    <div class="n-bottom">{{data.name}}</div>
+  </div>
+</template>
+<script>
+export default {
+  props: ['data']
+}
+</script>
+<style scoped>
+  .nums{
+    height: 90%;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    margin-top: 1%;
+  }
+  .n-top{
+    width: 100px;
+    height: 100px;
+    border-radius: 50%;
+    background-color: #DDCC92;
+    color: #6D1C1C;
+    /* background-color: rgba(255, 255, 255, 0.05); */
+    position: relative;
+  }
+
+  .n-top::before{
+    content: '';
+    border-radius: 50%;
+    background: none;
+    border: 2px solid rgba(167, 161, 95, 0.15);
+    width: 120px;
+    height: 120px;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%,-50%);
+  }
+
+  span{
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%,-50%);
+    font-size: 22px;
+    font-weight: bold;
+  }
+
+  .n-bottom{
+    margin-top: 20px;
+  }
+
+</style>

+ 28 - 0
src/components/pie-tu/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <div class="trend">
+    <div class="pie-content" :id="id"></div>
+  </div>
+</template>
+<script>
+export default {
+  props: ['id', 'data'],
+  mounted () {
+    this.$nextTick(() => {
+      this.$chart.pie(
+        this.id,
+        this.data
+      )
+    })
+  }
+}
+</script>
+
+<style scoped>
+.trend{
+  position: relative;
+}
+.pie-content{
+  width: 100%;
+  height: 220px;
+}
+</style>

+ 46 - 0
src/components/power-bar/index.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="power-layout">
+    <div class="p-num" :style="{color:data.color}">{{data.num}}</div>
+    <ul>
+      <li v-for="(item,i) in sum" :style="{top:10*i+'px',background:i>(sum-data.content)?data.color:'rgba(255, 255, 255, 0.05)'}" :key='i'></li>
+    </ul>
+    <div class="b-num" >{{data.bottomname}}</div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: ['data'],
+  data () {
+    return {
+      sum: 25
+    }
+  }
+}
+</script>
+
+<style scoped>
+  .p-num{
+    text-align: center;
+    margin-bottom: 10px;
+  }
+  .b-num{
+    text-align: center;
+    margin-top: 265px;
+  }
+  ul{
+    font-size: 0;
+    position: relative;
+  }
+  ul li{
+    width: 100%;
+    display: inline-block;
+    vertical-align: middle;
+    height: 4px;
+    line-height: 4px;
+    background: rgba(255, 255, 255, 0.01);
+    position: absolute;
+    top: 10px;
+    border-radius: 2px;
+  }
+</style>

+ 108 - 0
src/components/search-bar/index.vue

@@ -0,0 +1,108 @@
+<!--  -->
+<template>
+<div class='search-bar'>
+  <div class="search-left">
+    <span>搜索条件:</span>
+    <div>
+      <el-input v-model="key"></el-input>
+    </div>
+    <div style="padding-left:.625rem">
+      <el-button @click="search" type="primary">搜索</el-button>
+    </div>
+  </div>
+  <div class="search-right">
+    <span>每页显示:</span>
+    <div class="search-select">
+      <el-select v-model="value" placeholder="请选择">
+        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
+        </el-option>
+      </el-select>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      key: '',
+      options: [{
+        value: 10,
+        label: '10'
+      }, {
+        value: 20,
+        label: '20'
+      }, {
+        value: 30,
+        label: '30'
+      }, {
+        value: 40,
+        label: '40'
+      }, {
+        value: 50,
+        label: '50'
+      }],
+      value: 10
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {
+    value (val) {
+      this.$emit('sizeChange', val)
+    }
+  },
+  // 方法集合
+  methods: {
+    search () {
+      this.$emit('seachKey', this.key)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style scoped>
+.search-bar {
+  background-color: #fff;
+  display: flex;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.search-bar span {
+  color: #888;
+  width: auto;
+}
+
+.search-left,.search-right {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.search-right .search-select{
+  width: 4.8rem;
+}
+</style>

+ 35 - 0
src/components/trend/index.vue

@@ -0,0 +1,35 @@
+<template>
+  <div class="trend card">
+    <div class="t-txt theme-color1">文物访问趋势-24h</div>
+    <div id="wwqs"></div>
+  </div>
+</template>
+<script>
+export default {
+  mounted () {
+    this.$nextTick(() => {
+      this.$chart.line2(
+        'wwqs',
+        ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+        [820, 932, 901, 934, 1290, 1330, 1320]
+      )
+    })
+  }
+}
+</script>
+
+<style scoped>
+.trend{
+  position: relative;
+}
+.trend .t-txt{
+  position: absolute;
+  top: 1.5rem;
+  left: 1.5rem;
+  font-weight: bold;
+}
+#wwqs{
+  width: 100%;
+  height: 400px;
+}
+</style>

+ 58 - 0
src/components/ww-card/index.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="ww-layout">
+    <p class="ph">NO.{{data.num}}</p>
+    <p>点赞数:{{data.zan}}</p>
+    <div class="ww-detail">
+      <p class="theme-color1">{{data.sub1}}</p>
+      <p class="theme-color1">{{data.sub2}}</p>
+    </div>
+    <p class="theme-color1">{{data.name}}</p>
+    <div class="img" :style="{backgroundImage: `url(${data.img})`}"></div>
+  </div>
+</template>
+<script>
+export default {
+  props: ['data']
+}
+</script>
+<style scoped>
+.ww-layout{
+  width:100%;
+  height:200px;
+  background:rgba(255,255,255,0.05);
+  border-radius:3px;
+  position: relative;
+  overflow: hidden;
+  padding: 1rem;
+}
+
+.ww-layout .img{
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 200px;
+  height: 200px;
+  background-image: url(https://show.4dage.com/3D/2018/model/SD/images/s02.jpg);
+  background-position: center;
+  background-size: auto 100%;
+  background-repeat: no-repeat;
+}
+
+.ph{
+  color:#532f1c;
+  font-size: 32px;
+  font-weight: bold;
+}
+.ww-detail{
+  margin: 25px 0;
+}
+
+.ww-detail p{
+  margin-bottom: 4px;
+}
+
+p{
+  margin-bottom: 12px;
+}
+
+</style>

+ 72 - 0
src/configue/base.js

@@ -0,0 +1,72 @@
+const base = {
+  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}$/
+  },
+  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
+    }
+  },
+  isTypeBySend: function (fileName, typeArr) {
+    if (typeof fileName !== 'string') return
+    let name = fileName.toLowerCase()
+    let boo = false
+    typeArr.forEach(item => {
+      console.log(name.endsWith(item))
+      if (name.endsWith(item)) {
+        boo = true
+      }
+    })
+    return boo
+  },
+  timestampToTime: function (timestamp) {
+    var date = new Date(Number(timestamp))// 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+
+    var Y = date.getFullYear() + '-'
+
+    var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+
+    var D = date.getDate() + ' '
+
+    // var h = date.getHours() + ':'
+
+    // var m = date.getMinutes() + ':'
+
+    // var s = date.getSeconds()
+
+    return Y + M + D
+  },
+  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
+  }
+}
+export { base }

+ 3 - 0
src/configue/bus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+
+export default new Vue()

+ 87 - 0
src/configue/http.js

@@ -0,0 +1,87 @@
+import axios from 'axios'
+import Vue from 'vue'
+import router from '../router'
+
+var isProduction = process.env.NODE_ENV === 'production'
+const vue = new Vue()
+let loading = ''
+
+// 配置请求域名
+let layoutBoxIsShow
+const serverName = isProduction ? '/zhoushan' : 'http://119.23.129.199:8110/zhoushan'
+// http://119.23.129.199:8100/
+const exceptUrls = ['/login']
+
+axios.defaults.baseURL = serverName
+axios.defaults.headers['X-Requested-with'] = 'XMLHttpRequest'
+
+axios.interceptors.request.use(function (config) {
+  for (let i = 0; i < exceptUrls.length; i++) {
+    let url = exceptUrls[i]
+    if (config.url.indexOf(url) > -1) {
+      config.baseURL = serverName.replace('/zhoushan', '')
+    }
+  }
+  config.headers['token'] = window.localStorage.getItem('token')
+  loading = vue.$loading({
+    lock: true,
+    text: 'Loading',
+    spinner: 'el-icon-loading',
+    background: 'rgba(0, 0, 0, 0.7)'
+  })
+  return config
+}, function (error) {
+  // 对请求错误做些什么
+  return Promise.reject(error)
+})
+
+// 配置response拦截器
+axios.interceptors.response.use(
+
+  response => {
+    loading.close()
+    let data = response.data
+    let code = Number(response.data.code)
+
+    switch (code) {
+      case -1:
+        break
+      case 102:
+        break
+      case 5001:
+        if (!layoutBoxIsShow) {
+          layoutBoxIsShow = true
+          window.localStorage.setItem('token', '')
+          vue.$alert('登录状态失效,请重新登录', '提示', {
+            confirmButtonText: '确定',
+            callback: function () {
+              layoutBoxIsShow = false
+              router.push('/login')
+            }
+          })
+        }
+        break
+      case 0:
+        break
+    }
+    return data
+  },
+  error => {
+    loading.close()
+    // if (error.response) {
+    //   switch (error.response.code) {
+    //     case 5001:
+    //       window.localStorage.setItem('token', '')
+    //       vue.$alert('登录状态失效,请重新登录', '提示', {
+    //         confirmButtonText: '确定',
+    //         callback: function () {
+    //           router.push('/login')
+    //         }
+    //       })
+    //       break
+    //   }
+    // }
+    return Promise.reject(error)
+  }
+)
+export { serverName, axios }

+ 251 - 0
src/configue/myCharts.js

@@ -0,0 +1,251 @@
+/**
+ * 各种画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)
+          },
+          // 访问人数
+          pie: function (id, data) {
+            this.chart = echarts.init(document.getElementById(id))
+            this.chart.clear()
+            var fontColor = '#000'
+
+            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: '#000'
+                      }
+                    }
+                  },
+                  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 = '#000'
+            var borderColor = 'rgba(0,0,0,0.1)'
+            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: '#000',
+                      lineStyle: {
+                        color: '#000',
+                        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
+}

+ 40 - 0
src/main.js

@@ -0,0 +1,40 @@
+// 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 Vue from 'vue'
+import App from './App'
+import ElementUI from 'element-ui'
+import router from './router'
+import {base} from '@/configue/base'
+import {axios, serverName} from './configue/http'
+import myCharts from '@/configue/myCharts.js'
+import Vue2Editor from 'vue2-editor'
+import '../theme/index.css'
+import less from 'less'
+
+Vue.use(less)
+
+Vue.use(Vue2Editor)
+Vue.use(myCharts)
+
+Vue.use(ElementUI)
+Vue.config.productionTip = false
+
+Vue.prototype.$base = base
+Vue.prototype.$bus = new Vue()
+Vue.prototype.$http = axios
+Vue.prototype.$serverName = serverName// 挂载到Vue实例上面
+Vue.prototype.isTypeBySend = base.isTypeBySend
+Vue.prototype.vLoading = {
+  lock: true,
+  text: 'Loading',
+  spinner: 'el-icon-loading',
+  background: 'rgba(0, 0, 0, 0.7)'
+}
+
+/* eslint-disable no-new */
+window.$app = new Vue({
+  el: '#app',
+  router,
+  components: { App },
+  template: '<App/>'
+})

+ 240 - 0
src/pages/cultural-relic/index.vue

@@ -0,0 +1,240 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span>按文物类别查看:</span>
+            <el-select style="width:100px;" v-model="typeId" placeholder="请选择">
+              <el-option label="全部" value=""></el-option>
+              <el-option v-for="(item,i) in plist" :key="i" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+            <span style="margin-left:20px;">按文物年代查看:</span>
+            <el-select style="width:100px;" v-model="timeId" placeholder="请选择">
+              <el-option label="全部" value=""></el-option>
+              <el-option v-for="(item,i) in tlist" :key="i" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+            <el-input style="width:220px;margin:0 20px;" v-model="inputKey" placeholder="请输入文物名称搜索"></el-input>
+            <el-button type="primary" @click="getInformation">查找</el-button>
+            <el-button @click="inputKey=''">重置</el-button>
+            <el-select style="margin-left:20px;" @change="piliang" :value="'批量导入 '" placeholder="请选择">
+              <el-option
+                label="模板下载"
+                value="模板下载">
+              </el-option>
+              <el-option
+                label="批量导入"
+                value="批量导入">
+              </el-option>
+            </el-select>
+            <input @change="uploadChange" class="upload-btn" ref="upload" type="file">
+          </div>
+          <div class="info-right">
+            <el-button type="primary" @click="$router.push({name:'edit-cultural-relic',params:{type:0}})">新增文物</el-button>
+          </div>
+        </div>
+        <div class="collection-con">
+          <ul>
+            <li class="theme-color" @click="gotoShow(item)" v-for="(item,i) in tableData" :key="i">
+              <div class="li-img">
+                <el-image :fit="'cover'" style="width:100%;height:100%;" :src="item.unityPic"></el-image>
+                <!-- <div class="liulan"><span>浏览量: {{Math.round(Math.random()*100000)}}</span> 点赞数: {{Math.round(Math.random()*1000)}}</div> -->
+              </div>
+              <div>{{item.timeName}} {{item.typeName}} <span @click.stop="del(item)" class="del">删除</span></div>
+              <p>{{item.name}}</p>
+            </li>
+          </ul>
+        </div>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            :page-size="size"
+            layout="prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+import SearchBar from '@/components/search-bar'
+const crumbData = [
+  {
+    name: '文物库',
+    id: 4
+  }
+]
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+    SearchBar
+  },
+  data () {
+    return {
+      crumbData,
+      tableData: [],
+      inputKey: '',
+      currentPage: 1,
+      size: 20,
+      total: 0,
+      loading: false,
+      plist: [],
+      tlist: [],
+      typeId: '',
+      timeId: ''
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    },
+    inputKey () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.getPositionList()
+    this.getTypeList()
+    this.refresh()
+  },
+  methods: {
+    del (item) {
+      let data = {
+        id: item.id
+      }
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.post('/collection/deleteCollection', data).then(res => {
+          if (res.code === 0) {
+            this.$notify.success({
+              title: '提示',
+              message: '删除成功',
+              duration: 2000
+            })
+            this.refresh()
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg,
+              duration: 2000
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    async uploadChange (e) {
+      let file = e.target.files[0]
+      let formData = new FormData()
+      formData.append('file', file)
+      const res = await this.$http.post(
+        '/collection/importCollection',
+        formData,
+        {'Content-Type': 'multipart/form-data'})
+      if (res.code === 0) {
+        this.$alert(`上传成功`, '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.refresh()
+          }
+        })
+      } else {
+        this.$alert(`上传失败,${res.msg}`, '提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+            this.refresh()
+          }
+        })
+      }
+    },
+    piliang (data) {
+      if (data === '模板下载') {
+        window.open('/collection/importData.xlsx', '_blank')
+      } else {
+        this.$refs.upload.click()
+      }
+    },
+    async getPositionList () {
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          state: 0
+        },
+        url: '/collection/typeList'
+      })
+
+      this.plist = result.data
+    },
+    async getTypeList () {
+      let result = await this.$http({
+        method: 'post',
+        url: '/collection/timeList'
+      })
+
+      this.tlist = result.data
+    },
+    gotoShow (item) {
+      this.$router.push({ name: 'show-cultural-relic', params: { id: item.id } })
+    },
+    goto (item) {
+      window.localStorage.setItem('editCollection', JSON.stringify(item))
+      this.$router.push({ name: 'edit-collection', params: { type: 1 } })
+    },
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    async getInformation () {
+      let params = {
+        name: this.inputKey,
+        typeId: this.typeId,
+        timeId: this.timeId,
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/collection/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 引入公共css类 */
+@import url(./style);
+</style>

+ 155 - 0
src/pages/cultural-relic/style.css

@@ -0,0 +1,155 @@
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.upload-btn{
+  opacity: 0;
+  width: 0;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con{
+  width: 100%;
+}
+
+.collection-con ul{
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 20px;
+}
+
+.collection-con ul li {
+  width: 19%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: .8rem;
+  color: #2d2d2d;
+  margin-bottom: 20px;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  height: 200px;
+  margin-bottom: 10px;
+}
+.collection-con ul .li-img > .liulan{
+  position: absolute;
+  left: 10px;
+  bottom: 10px;
+  color: #fff;
+}
+
+.collection-con ul .del{
+  float: right;
+}
+
+.collection-con ul .li-img div span{
+  margin-right: 10px;
+}
+
+
+.collection-con ul img {
+  width: 100%;
+}

+ 296 - 0
src/pages/device/index.vue

@@ -0,0 +1,296 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left"></div>
+          <div class="info-right">
+            <el-button type="primary"  @click="show('','add')">新增设备</el-button>
+          </div>
+        </div>
+        <el-table :data="tableData" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope">
+              <span>{{scope.row[item.prop]}}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+              <span class="o-span" @click="show(scope.row,'edit')">编辑</span>
+              <span class="o-span" @click="changeState(scope.row)">{{scope.row.qiyong==='启用'?'停用':'启用'}}</span>
+            </template>
+          </el-table-column>
+          <div class="e-pagination">
+            <el-pagination @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+            </el-pagination>
+          </div>
+        </el-table>
+      </div>
+    </div>
+    <el-dialog title="新增设备" :visible.sync="dialogFormVisible" width="40%">
+      <el-form :model="form">
+        <el-form-item label="设备名称:" style="width:50%;" :label-width="formLabelWidth">
+          <el-input v-model="form.name" placeholder="请输入设备名称" autocomplete="off"></el-input>
+        </el-form-item>
+        <el-form-item label="设备ID:" style="width:50%;" :label-width="formLabelWidth">
+          <el-input v-model="form.uuid" placeholder="请输入设备ID" autocomplete="off"></el-input>
+        </el-form-item>
+        <el-form-item label="所在位置:" :label-width="formLabelWidth">
+          <el-select v-model="form.positionId" placeholder="请选择活动区域">
+            <el-option v-for="(item,i) in plist" :key="i" :label="item.name" :value="item.id"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="是否启用:" :label-width="formLabelWidth">
+            <el-radio-group v-model="form.state">
+            <el-radio :label="0" >是</el-radio>
+            <el-radio :label="1" >否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary"  @click="save">保 存</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+const crumbData = [
+  {
+    name: '设备管理',
+    id: 10
+  }
+]
+
+let juese = {
+  0: '高级管理员',
+  1: '普通管理员'
+}
+
+let zt = {
+  0: '启用',
+  1: '停用'
+}
+
+let urlType = {
+  add: '/equipment/addEquipment',
+  edit: '/equipment/updateEquipment'
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  data () {
+    // 这里存放数据
+    let data = [
+      {
+        prop: 'id',
+        label: '序号'
+      },
+      {
+        prop: 'name',
+        label: '设备名称'
+      },
+      {
+        prop: 'uuid',
+        label: '设备ID'
+      },
+      {
+        prop: 'positionName',
+        label: '所在位置'
+      },
+      {
+        prop: 'qiyong',
+        label: '状态'
+      }
+    ]
+
+    return {
+      crumbData,
+      data,
+      region: 0,
+      infoName: '',
+      tableData: [],
+      dialogTableVisible: false,
+      dialogFormVisible: false,
+      form: {
+        id: '',
+        name: '',
+        uuid: '',
+        positionId: '',
+        state: '',
+        positionName: ''
+      },
+      plist: [],
+      inputKey: '',
+      currentPage: 1,
+      size: 10,
+      total: 0,
+      formLabelWidth: '120px',
+      imageUrl: '',
+      type: 'add'
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    },
+    region () {
+      this.refresh()
+    },
+    inputKey () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.getPositionList()
+    this.refresh()
+  },
+  methods: {
+    goto (item) {
+      window.localStorage.setItem('editInfo', JSON.stringify(item))
+      this.$router.push({name: 'edit-information', params: {type: 1}})
+      this.$bus.$emit('editinfo', item)
+    },
+    upload_success (data) {
+      this.form.head = data.data
+    },
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    save () {
+      let {userName, roleId, phone, head, state} = this.form
+      let data = {userName, roleId, phone, head, state}
+
+      this.$http.post(urlType[this.type], data).then(res => {
+        if (res.code === 0) {
+          this.$alert('操作成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.dialogFormVisible = false
+              this.refresh()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message,
+            duration: 2000
+          })
+        }
+      })
+    },
+
+    show (item = '', type) {
+      this.dialogFormVisible = true
+      this.type = type
+      this.form = {
+        id: '',
+        name: '',
+        uuid: '',
+        positionId: '',
+        state: '',
+        positionName: ''
+      }
+
+      if (type === 'edit') {
+        this.form = item
+      }
+    },
+    async getPositionList () {
+      let result = await this.$http({
+        method: 'post',
+        url: '/position/list'
+      })
+
+      this.plist = result.data
+    },
+    changeState (item) {
+      let state = item.state === 1 ? 0 : 1
+      let data = {
+        id: item.id,
+        state
+      }
+
+      this.$confirm('此操作将导致设备状态发生改变, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.post('/equipment/updateState', data).then(res => {
+          if (res.code === 0) {
+            this.$alert('修改成功', '提示', {
+              confirmButtonText: '确定',
+              callback: action => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg,
+              duration: 2000
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        })
+      })
+    },
+
+    async getInformation () {
+      let params = {
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/equipment/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.tableData.forEach(item => {
+        item['date'] = this.$base.timestampToTime(item['startTime']) + '至' + this.$base.timestampToTime(item['endTime'])
+        item['role'] = juese[item['roleId']]
+        item['qiyong'] = zt[item['state']]
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 引入公共css类 */
+@import url(./style);
+</style>

+ 106 - 0
src/pages/device/style.css

@@ -0,0 +1,106 @@
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}

+ 248 - 0
src/pages/display/index.vue

@@ -0,0 +1,248 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body" v-loading="loading"
+    element-loading-text=""
+    element-loading-spinner="el-icon-loading"
+    element-loading-background="rgba(0, 0, 0, 0.6)">
+        <div class="info-top">
+          <div class="info-left">
+            <span >展示方案:</span>
+            <el-input style="width:220px;margin-right:20px;" v-model="inputKey" placeholder="请输入展示方案名称"></el-input>
+            <el-button type="primary" @click="getInformation">查找</el-button>
+            <el-button @click="inputKey=''">重置</el-button>
+          </div>
+          <div class="info-right">
+            <el-button type="primary" @click="$router.push({name:'edit-display',params:{type:0,id:'none'}})">新增方案</el-button>
+          </div>
+        </div>
+        <el-table  :data="tableData" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+          <template slot-scope="scope">
+            <span>{{scope.row[item.prop]}}</span>
+          </template>
+          </el-table-column>
+          <el-table-column label="状态">
+            <template slot-scope="scope" v-if="scope.row.qiyong">
+              <el-switch
+                @change='changeState(scope.row)'
+                v-model="scope.row.qiyong.st"
+              >
+              </el-switch>
+              <span >{{scope.row.qiyong.text}}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+              <!-- <a href="javascript:;" @click="up(scope.row, scope.$index)">
+                <img class="icon" src="@/assets/img/up-arrow.png" alt="">
+              </a>
+              <a href="javascript:;"  @click="down(scope.row, scope.$index)">
+                <img class="icon" src="@/assets/img/down-arrow.png" alt="">
+              </a> -->
+              <span class="o-span" @click="goto(scope.row)">编辑</span>
+              <span class="o-span" @click="del(scope.row)">删除</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="size" layout="prev, pager, next, jumper" :total="total">
+          </el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+const crumbData = [
+  {
+    name: '展示管理',
+    id: 3
+  }
+]
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  data () {
+    // 这里存放数据
+    let data = [
+      // {
+      //   prop: 'idx',
+      //   label: '序号'
+      // },
+      {
+        prop: 'name',
+        label: '展示方案'
+      },
+      {
+        prop: 'typeName',
+        label: '展示类别'
+      },
+      {
+        prop: 'orderNum',
+        label: '排序'
+      },
+      {
+        prop: 'date',
+        label: '发布时间'
+      }
+    ]
+
+    return {
+      crumbData,
+      data,
+      loading: true,
+      tableData: [],
+      inputKey: '',
+      currentPage: 1,
+      size: 10,
+      total: 0
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    },
+    inputKey () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.refresh()
+  },
+  methods: {
+    goto (item) {
+      this.$router.push({ name: 'edit-display', params: { type: 1, id: item.id } })
+    },
+    async changeState (item) {
+      this.loading = true
+
+      let { name, id, typeId, equipmentId, description } = item
+      let data = { name, typeId, equipmentId, isSend: 1, state: !item.qiyong.st ? 1 : 0, description, id }
+      this.$http.post('/exhibition/updateExhibition', data).then(res => {
+        if (res.code === 0) {
+          this.loading = false
+          this.refresh()
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message,
+            duration: 2000
+          })
+        }
+      })
+    },
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    del (item) {
+      let data = {
+        id: item.id
+      }
+      this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.post('/exhibition/deleteExhibition', data).then(res => {
+          if (res.code === 0) {
+            this.$notify.success({
+              title: '提示',
+              message: '删除成功',
+              duration: 2000
+            })
+            this.refresh()
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg,
+              duration: 2000
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    fixState (state) {
+      let obj = {}
+      switch (Number(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
+    },
+    async getInformation () {
+      let params = {
+        name: this.inputKey,
+        pageNum: this.currentPage,
+        pageSize: this.size
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/exhibition/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.tableData.forEach((item, i) => {
+        item['date'] = this.$base.timestampToTime(item['createTime'])
+        item['qiyong'] = this.fixState(item['state'])
+        item['idx'] = i + 1
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 引入公共css类 */
+@import url(./style);
+</style>

+ 112 - 0
src/pages/display/style.css

@@ -0,0 +1,112 @@
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.e-pagination{
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 1.5625rem;
+}

+ 670 - 0
src/pages/editPages/cultural-relic/index.vue

@@ -0,0 +1,670 @@
+<!--  -->
+<template>
+  <div  v-loading.fullscreen.lock="loading"
+    element-loading-text="拼命加载中"
+    element-loading-spinner="el-icon-loading"
+    class="edit-cultural-relic"
+    element-loading-background="rgba(0, 0, 0, 0.8)">
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="top-title">文物信息:</div>
+        <div class="form-con">
+          <el-form ref="form"  :rules="rules" status-icon  :model="form" label-width="120px">
+            <el-form-item prop="name" label="文物名称:" style="width:318px;">
+              <el-input v-model="form.name" placeholder="请输入文物名称"></el-input>
+            </el-form-item>
+            <el-form-item prop="verticalName" label="竖排文物名">
+              <el-input type="textarea" v-model="form.verticalName" class="verticla-input" />
+              <!-- <textarea rows="10" >
+                在w3school,你可以找到你所需要的所有的网站建设教程。
+                </textarea> -->
+            </el-form-item>
+            <el-form-item label="文物类别:">
+              <el-select v-model="form.typeId" placeholder="请选择文物类别">
+                  <el-option v-for="(item,i) in plist" :key="i" :label="item.name" :value="item.id"></el-option>
+              </el-select>
+            </el-form-item>
+
+            <el-form-item label="分类年代:" :rules="[
+              { required: true}
+            ]">
+              <el-select v-model="form.timeId" placeholder="请选择分类年代" style="width:400px">
+                <el-option v-for="(item,i) in tlist" :key="i" :label="item.name" :value="item.id"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="文物排序:">
+              <el-input v-model="form.num" placeholder="请输入文物排序"></el-input>
+            </el-form-item>
+            <el-form-item label="文物年代:" >
+              <el-input v-model="form.typeTime" maxlength="50" show-word-limit placeholder="请输入文物年代"></el-input>
+            </el-form-item>
+
+            <el-form-item label="文物介绍:">
+                <vue-editor v-model="form.description" />
+            </el-form-item>
+            <el-form-item label="文物介绍预览:">
+                <el-input type="textarea" v-model="form.description1" disabled />
+            </el-form-item>
+            <el-form-item label="出土信息:" style="width:100%;">
+              <el-input v-model="form.venue" maxlength="50" show-word-limit placeholder="请输入出土信息"></el-input>
+            </el-form-item>
+
+             <el-form-item label="文物用途:" style="width:100%;">
+              <el-input v-model="form.purpose" maxlength="50" show-word-limit placeholder="请输入文物用途"></el-input>
+            </el-form-item>
+
+             <el-form-item label="文物材质:" style="width:100%;">
+              <el-input v-model="form.texture" maxlength="50" show-word-limit placeholder="请输入文物材质"></el-input>
+            </el-form-item>
+
+             <el-form-item label="文物尺寸:" style="width:100%;">
+              <el-input v-model="form.size" maxlength="50" show-word-limit placeholder="请输入文物尺寸"></el-input>
+            </el-form-item>
+
+            <el-form-item label="备注信息:" style="width:100%;">
+              <el-input v-model="form.remarks" maxlength="50" show-word-limit placeholder="请输入文物出土地点"></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="top-title">展览信息:</div>
+        <div class="form-con">
+          <el-form ref="form" :model="form" label-width="120px">
+            <el-form-item label="文物展示方式:">
+                <el-radio-group v-model="form.type">
+                  <el-radio :label="'2D'" >2D</el-radio>
+                  <el-radio :label="'3D'" >3D</el-radio>
+                </el-radio-group>
+            </el-form-item>
+
+            <template v-if="form.type === '2D'">
+              <el-form-item label="轮播板式:">
+                <div class="banshi">
+                  <draggable v-model="uploadList" group="people" @start="drag=true" @end="drag=false">
+                    <div v-for="(item,i) in uploadList" :key="i"  class="imgdiv">
+                      <img v-if="item" :src="$serverName.replace('/zhoushan','') + item" alt="">
+                      <i class="el-icon-circle-close" @click.stop="delUploadItem(i)"></i>
+                    </div>
+                  </draggable>
+                </div>
+                <el-upload
+                  class="upload-demo"
+                  drag
+                  :on-success="upload_imglist_success"
+                  :action='uploadUrl'
+                  :headers="{
+                    token,
+                  }"
+                  :limit="10"
+                  :on-exceed="handleExceed"
+                  :before-upload="before_imglistUpload"
+                  multiple
+                  :show-file-list="false"
+                >
+                  <div>
+                    <i class="el-icon-upload"></i>
+                    <div class="el-upload__text">
+                      将文件拖到此处,或<em>点击上传</em>
+                    </div>
+                  </div>
+                  <div class="el-upload__tip" slot="tip">
+                    支持扩展名:.jpg, .jpgc, .png,图片最长边不可以超过1024
+                  </div>
+                </el-upload>
+                <!-- <div class="el-upload__tip">*海报规定尺寸比例:3:4 </div> -->
+              </el-form-item>
+            </template>
+
+            <el-form-item v-else label="文物模型:" style="width:340px;">
+              <el-input v-model="form.modelUrl" placeholder="请输入文物模型链接(url地址)"></el-input>
+            </el-form-item>
+
+            <el-form-item label="展示封面:">
+               <Cropper
+                  :width="512"
+                  :height="512"
+                  :fixed-number="[1,1]"
+                  :uploadUrl = "'/exhibition/upload'"
+                  :img="form.pic"
+                  @clearImg="form.pic=''"
+                  @subUploadSucceed="getShopImages"
+              />
+              <span class="wwtxt" style="color:#C0C4CC">建议上传512*512的png格式图片</span>
+            </el-form-item>
+
+            <el-form-item label="动画封面:">
+               <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{
+                  token,
+                }"
+                :show-file-list="false"
+                :before-upload="beforeAvatarUpload"
+                :on-success="upload_avatar_success"
+                >
+                <img v-if="form.unityPic" :src="form.unityPic" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+              <span class="wwtxt" style="color:#C0C4CC">建议上传最长边不超过1024的JPG格式图片</span>
+            </el-form-item>
+
+            <!-- <el-form-item label="四维模库:" style="width:340px;">
+              <el-button type="primary" @click="open">立即前往</el-button>
+            </el-form-item> -->
+
+            <el-form-item label="unity文件:">
+              <el-upload
+                class="upload-demo"
+                drag
+                :headers="{
+                  token
+                }"
+                :action="uploadUrl"
+                :file-list="unityUrlList"
+                :show-file-list="true"
+                :before-remove="beforeUnityUrlRemove"
+                :before-upload="beforeUnityUpload"
+                :on-success="upload_unity_success"
+              >
+                <div >
+                  <i class="el-icon-upload"></i>
+                  <div class="el-upload__text">
+                    将文件拖到此处,或
+                    <em>点击上传</em>
+                  </div>
+                </div>
+                <div class="el-upload__tip" slot="tip">支持上传unity文件:.unity, .ab, .u3d</div>
+              </el-upload>
+            </el-form-item>
+
+            <el-form-item label="视频动画:">
+              <el-upload
+                class="upload-demo"
+                drag
+                :headers="{
+                  token
+                }"
+                :file-list="contentUrlList"
+                :show-file-list="true"
+                :action="uploadUrl"
+                :before-remove="beforeRemove"
+                :before-upload="beforeUpload"
+                :on-success="upload_success"
+              >
+                <div>
+                  <i class="el-icon-upload"></i>
+                  <div class="el-upload__text">
+                    将文件拖到此处,或
+                    <em>点击上传</em>
+                  </div>
+                </div>
+                <div class="el-upload__tip" slot="tip">支持视频动画格式:.wmv, .wav, .mp4</div>
+              </el-upload>
+            </el-form-item>
+            <el-form-item label="二维码信息:">
+               <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{
+                  token,
+                }"
+                :show-file-list="false"
+                :before-upload="beforeqrCodeUpload"
+                :on-success="upload_qrCode_success"
+                >
+                <div v-if="form.qrCode" class="imgdiv">
+                  <img
+                    style="width: 100%;height:100%;"
+                    :src="$serverName.replace('/zhoushan','') + form.qrCode"
+                  >
+                  <i class="el-icon-circle-close" @click.stop="form.qrCode=''"></i>
+                </div>
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+
+            </el-form-item>
+            <el-form-item label="是否展示:">
+              <el-radio-group v-model="form.state">
+                <el-radio :label="0" >是</el-radio>
+                <el-radio :label="1" >否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-form>
+        </div>
+        <el-button type="primary" @click="onSubmit">发布</el-button>
+        <el-button @click="$router.back()">取消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+import { VueEditor } from 'vue2-editor'
+import Cropper from '@/components/cropper'
+import draggable from 'vuedraggable'
+
+let urlType = {
+  0: '/collection/addCollection',
+  1: '/collection/updateCollection'
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+    VueEditor,
+    Cropper,
+    draggable
+  },
+  data () {
+    let crumbData = [
+      {
+        name: '文物库',
+        id: 0
+      },
+      {
+        name: this.$route.params.type ? '编辑文物' : '新增文物',
+        id: 1
+      }
+    ]
+    return {
+      uploadList: [],
+      crumbData,
+      loading: false,
+      contentUrlList: [],
+      unityUrlList: [],
+      rules: {
+        name: [{ required: true, message: '请输入文物名称', trigger: 'blur' }],
+        verticalName: [{ required: true, message: '请输入竖排文物名', trigger: 'blur' }]
+      },
+      form: {
+        name: '',
+        typeId: 2,
+        timeId: 1,
+        typeTime: '',
+        num: 0,
+        discoveryTime: '',
+        venue: '',
+        modelUrl: '',
+        pic: '',
+        qrCode: '',
+        contentUrl: '',
+        unityPic: '',
+        unityUrl: '',
+        description: '',
+        purpose: '',
+        remarks: '',
+        repairTime: '',
+        size: '',
+        start: 0,
+        state: 0,
+        texture: '',
+        type: '2D',
+        typeImages: '',
+        verticalName: ''
+      },
+      plist: [],
+      tlist: [],
+      realType: '',
+      token: window.localStorage.getItem('token'),
+      uploadUrl: `${this.$serverName}/collection/upload`,
+      type: this.$route.params.type
+    }
+  },
+  watch: {
+    'form.description' (val) {
+      this.form.description1 = val.replace(/<\/[^>]*>/g, '\n').replace(/<[^>]*>/g, '').replace(/&nbsp/g, '')
+    }
+  },
+  methods: {
+    delUploadItem (i) {
+      this.uploadList.splice(i, 1)
+    },
+    getShopImages (img) {
+      this.form.pic = img
+    },
+    getQrCodeImages (img) {
+      this.form.qrCode = img
+    },
+    getModelImages (img) {
+      this.form.unityPic = img
+    },
+    handleExceed (files, fileList) {
+      this.$message.warning(`上传数量超出限制`)
+    },
+    before_imglistUpload () {
+      this.loading = true
+    },
+    upload_imglist_success (data) {
+      this.loading = false
+      this.uploadList.push(data.data)
+    },
+    async onSubmit () {
+      let {name,
+        typeId,
+        type,
+        timeId,
+        typeTime,
+        purpose,
+        num,
+        discoveryTime,
+        repairTime,
+        venue,
+        modelUrl,
+        pic,
+        qrCode,
+        contentUrl,
+        size,
+        typeImages,
+        unityPic,
+        texture,
+        unityUrl,
+        state,
+        remarks,
+        description,
+        verticalName,
+        id} = this.form
+      if (!name) {
+        this.$notify.error({
+          title: '错误',
+          message: '请输入文物名称',
+          duration: 2000
+        })
+        return
+      }
+      if (!verticalName) {
+        this.$notify.error({
+          title: '错误',
+          message: '请输入竖排文物名称',
+          duration: 2000
+        })
+        return
+      }
+      contentUrl = this.contentUrlList[0] ? this.contentUrlList[0].url : ''
+      unityUrl = this.unityUrlList[0] ? this.unityUrlList[0].url : ''
+      if (type === '2D') {
+        typeImages = this.uploadList
+      }
+      description = this.form.description1
+      let data = {name,
+        typeId,
+        timeId,
+        typeTime,
+        num,
+        discoveryTime,
+        repairTime,
+        typeImages,
+        type,
+        size,
+        venue,
+        modelUrl,
+        unityPic,
+        unityUrl,
+        texture,
+        remarks,
+        purpose,
+        pic,
+        qrCode,
+        contentUrl,
+        state,
+        verticalName,
+        description}
+      if (this.type) {
+        data['id'] = id
+      }
+      this.$http.post(urlType[this.type], data).then(res => {
+        if (res.code === 0) {
+          this.$alert(this.$route.params.type ? '操作成功' : '操作成功', '提示', {
+            confirmButtonText: '确定',
+            callback: action => {
+              this.$router.back()
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message,
+            duration: 2000
+          })
+        }
+      })
+    },
+    open () {
+      window.open('https://www.4dmodel.com', '_blank')
+    },
+
+    beforeAvatarUpload (file) {
+      this.loading = true
+      let typeArr = ['jpg']
+      let type = this.isTypeBySend(file.name, typeArr)
+      if (!type) {
+        this.loading = false
+        this.$message.error('仅支持上传动画封面的JPG格式文件')
+        return type
+      }
+    },
+
+    beforeUnityUpload (file) {
+      this.loading = true
+      let typeArr = ['unity', 'ab', 'u3d']
+      let type = this.isTypeBySend(file.name, typeArr)
+      if (!type) {
+        this.loading = false
+        this.$message.error('支持上传unity文件:.unity, .ab, .u3d')
+        return type
+      }
+    },
+
+    beforeUnityUrlRemove (file) {
+      this.unityUrlList = []
+    },
+
+    beforeRemove (file) {
+      this.contentUrlList = []
+    },
+
+    beforeUpload (file) {
+      this.loading = true
+      let typeArr = ['wmv', 'wav', 'mp4']
+      let type = this.isTypeBySend(file.name, typeArr)
+      if (!type) {
+        this.loading = false
+        this.$message.error('支持视频动画格式:.wmv, .wav, .mp4')
+        return type
+      }
+    },
+
+    beforeqrCodeUpload () {
+      this.loading = true
+    },
+    upload_qrCode_success (data) {
+      this.loading = false
+      this.form.qrCode = data.data
+    },
+    upload_avatar_success (data) {
+      this.loading = false
+      this.form.unityPic = data.data
+    },
+
+    upload_unity_success (data) {
+      this.loading = false
+      this.form.unityUrl = data.data
+      this.unityUrlList = [{
+        name: data.data.split('/')[data.data.split('/').length - 1],
+        url: data.data
+      }]
+    },
+
+    upload_success (data) {
+      this.loading = false
+      this.form.contentUrl = data.data
+      this.contentUrlList = [{
+        name: data.data.split('/')[data.data.split('/').length - 1],
+        url: data.data
+      }]
+    },
+
+    async getPositionList () {
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          state: 0
+        },
+        url: '/collection/typeList'
+      })
+
+      this.plist = result.data
+    },
+    async getTypeList () {
+      let result = await this.$http({
+        method: 'post',
+        url: '/collection/timeList'
+      })
+
+      this.tlist = result.data
+    }
+
+  },
+  mounted () {
+    this.getPositionList()
+    this.getTypeList()
+    if (this.type) {
+      let info = window.localStorage.getItem('editCollection')
+      if (info) {
+        this.form = JSON.parse(info)
+        this.form.typeId = Number(this.form.typeId)
+        this.form.equipmentId = Number(this.form.equipmentId)
+        this.form.state = Number(this.form.state)
+        let unityUrl = this.form.unityUrl
+        let contentUrl = this.form.contentUrl
+
+        if (unityUrl) {
+          this.unityUrlList = [{
+            name: unityUrl.split('/')[unityUrl.split('/').length - 1],
+            url: unityUrl
+          }]
+        }
+
+        if (contentUrl) {
+          this.contentUrlList = [{
+            name: contentUrl.split('/')[contentUrl.split('/').length - 1],
+            url: contentUrl
+          }]
+        }
+        setTimeout(() => {
+          this.uploadList = this.form.typeImages ? JSON.parse(this.form.typeImages) : []
+        })
+      }
+    } else {
+      this.form = {
+        name: '',
+        typeId: 2,
+        timeId: 1,
+        typeTime: '',
+        num: 0,
+        discoveryTime: '',
+        venue: '',
+        modelUrl: '',
+        pic: '',
+        qrCode: '',
+        contentUrl: '',
+        unityPic: '',
+        unityUrl: '',
+        description: '',
+        purpose: '',
+        remarks: '',
+        repairTime: '',
+        size: '',
+        start: 0,
+        state: 0,
+        texture: '',
+        type: '2D',
+        typeImages: ''
+      }
+    }
+  }
+}
+</script>
+
+<style lang='less' scoped>
+/* 引入公共css类 */
+@import './style.less';
+</style>
+
+<style  lang='less'>
+.ql-picker-label {
+  line-height: 1;
+  overflow: hidden;
+}
+
+.avatar-uploader .el-upload {
+  /* border: 1px dashed #000; */
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+.ql-picker-label {
+  line-height: 1;
+  overflow: hidden;
+}
+
+.avatar-uploader{
+  border: 1px dashed #ccc;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  width: 178px;
+  margin-top: 10px;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px!important;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+
+.edit-cultural-relic {
+  .verticla-input {
+    height: 200px;
+    width: 40px;
+    textarea {
+      height: 100% !important;
+      resize: none;
+    }
+  }
+}
+
+</style>

+ 54 - 0
src/pages/editPages/cultural-relic/style.less

@@ -0,0 +1,54 @@
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+.top-body .top-title{
+  font-weight: bold;
+  font-size: 16px;
+  margin-bottom: 20px;
+}
+
+.form-con{
+  width: 85%;
+  border: 1px solid #e5e5e5;
+  padding: 2% 10% 2% 2%;
+  margin-bottom: 20px;
+}
+.banshi{
+  line-height: 1;
+  font-size: 0;
+  >div{
+    display: flex;
+    margin-bottom: 10px;
+    flex-wrap: wrap;
+    >div{
+      width: 19%;
+      border: 1px solid #ccc;
+      min-height: 100px;
+      margin-right: 4px;
+      margin-bottom: 20px;
+      >img{
+        cursor: pointer;
+        width: 100%;
+      }
+      &:last-of-type{
+        margin-right: 0;
+      }
+    }
+  }
+}
+

+ 542 - 0
src/pages/editPages/display/index.vue

@@ -0,0 +1,542 @@
+<!--  -->
+<template>
+  <div v-loading.fullscreen.lock="loading"
+    element-loading-text="拼命加载中"
+    element-loading-spinner="el-icon-loading"
+    element-loading-background="rgba(0, 0, 0, 0.8)">
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="top-title">方案信息:</div>
+        <div class="form-con">
+          <el-form ref="form" :model="form" label-width="100px">
+            <el-form-item label="方案名称:" required style="width:318px;">
+              <el-input v-model="form.name"></el-input>
+            </el-form-item>
+            <el-form-item label="方案排序:"  style="width:318px;">
+              <el-input v-model="form.orderNum"></el-input>
+            </el-form-item>
+            <el-form-item label="展示说明:">
+              <vue-editor v-model="form.description" />
+            </el-form-item>
+
+            <el-form-item></el-form-item>
+          </el-form>
+        </div>
+        <div class="top-title">展示信息:</div>
+        <div class="form-con">
+          <el-form ref="form" :model="form" label-width="140px">
+            <el-form-item label="展示类别:" style="width:318px;">
+              <el-select v-model="form.typeId" placeholder="请选择文物类别">
+                <el-option v-for="(item,i) in exhibitionlist" :key="i" :label="item.name" :value="item.id"></el-option>
+              </el-select>
+            </el-form-item>
+
+            <el-form-item label="展示封面(深色):">
+               <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{
+                  token,
+                }"
+                :show-file-list="false"
+                :before-upload="beforeUpload"
+                :on-success="upload_avatar_success"
+                >
+                <img v-if="form.webUrl" :src="form.webUrl" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+
+            </el-form-item>
+            <el-form-item label="展示封面(浅色):">
+               <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{
+                  token,
+                }"
+                :show-file-list="false"
+                :before-upload="beforeUpload"
+                :on-success="upload_avatar_success2"
+                >
+                <img v-if="form.webUrl2" :src="form.webUrl2" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+
+            </el-form-item>
+            <el-form-item label="展示文物:" required="">
+              <el-table ref="dragTable" :data="tableData" v-if="tableData.length>0" style="width: 100%">
+                <el-table-column
+                  v-for="(item, idx) in data"
+                  :key="idx"
+                  :prop="item.prop"
+                  :label="item.label"
+                >
+                  <template slot-scope="scope">
+                    <span>{{scope.row[item.prop]}}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作">
+                  <template slot-scope="scope">
+                    <a href="javascript:;" @click="up(scope.row, scope.$index)">
+                      <img class="icon" src="@/assets/img/up-arrow.png" alt="">
+                    </a>
+                    <a href="javascript:;"  @click="down(scope.row, scope.$index)">
+                      <img class="icon" src="@/assets/img/down-arrow.png" alt="">
+                    </a>
+                    <span class="o-span" @click="del(scope.row)">删除</span>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div @click="getInformation" class="avatar-uploader">
+                <i class="el-icon-plus avatar-uploader-icon"></i>
+                <span class="wwtxt">文物库</span>
+              </div>
+            </el-form-item>
+            <el-form-item label="是否启用:">
+              <el-radio-group v-model="form.state">
+                <el-radio :label="0">是</el-radio>
+                <el-radio :label="1">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-form>
+        </div>
+        <el-button type="primary" @click="onSubmit">发布</el-button>
+        <el-button @click="$router.back()">取消</el-button>
+      </div>
+      <div @click="showSelect=false" class="select-hover" v-if="showSelect">
+        <div @click.stop class="select-con">
+          <div class="info-left">
+            <span>按文物类别查看:</span>
+            <el-select style="width:100px;" v-model="typeId" placeholder="请选择">
+              <el-option label="全部" value=""></el-option>
+              <el-option v-for="(item,i) in tlist" :key="i" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+            <span style="margin-left:20px;">按文物年代查看:</span>
+            <el-select style="width:100px;" v-model="timeId" placeholder="请选择">
+              <el-option label="全部" value=""></el-option>
+              <el-option v-for="(item,i) in timelist" :key="i" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+            <el-input style="width:220px;margin:0 20px;" v-model="inputKey" placeholder="请输入文物名称搜索"></el-input>
+            <el-button type="primary" @click="getInformation">查找</el-button>
+            <el-button @click="inputKey=''">重置</el-button>
+          </div>
+          <ul>
+            <li :class="{'active':isSelect(item,i)}" @click="pushIn(item)" v-for="(item,i) in collectList" :key="i">
+              <div class="mask">
+                <!-- <div>{{1}}</div> -->
+              </div>
+              <div class="li-img">
+                <img :src="item.pic" alt />
+                <div><span>浏览量: {{Math.round(Math.random()*100000)}}</span> 点赞数: {{Math.round(Math.random()*1000)}}</div>
+              </div>
+              <div>{{item.timeName}} {{item.typeName}}</div>
+              <p>{{item.name}}</p>
+            </li>
+          </ul>
+          <div class="save-btn">
+            <el-button type="primary" @click="showSelect=false">确定</el-button>
+            <el-button @click="()=>{showSelect=false}">取消</el-button>
+          </div>
+        </div>
+      </div>
+
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+import SearchBar from '@/components/search-bar'
+import Cropper from '@/components/cropper'
+import Sortable from 'sortablejs'
+
+import { VueEditor } from 'vue2-editor'
+
+let urlType = {
+  0: '/exhibition/addExhibition',
+  1: '/exhibition/updateExhibition'
+}
+
+let urlType1 = {
+  0: '/exhibition/insertExhibitionCollection',
+  1: '/exhibition/updateExhibitionCollection'
+}
+
+let data = [
+  {
+    prop: 'idx',
+    label: '序号'
+  },
+  {
+    prop: 'name',
+    label: '文物名称'
+  },
+  {
+    prop: 'typeName',
+    label: '文物类别'
+  },
+  {
+    prop: 'timeName',
+    label: '文物年代'
+  }
+]
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+    VueEditor,
+    SearchBar,
+    Cropper
+  },
+  data () {
+    let crumbData = [
+      {
+        name: '展示管理',
+        id: 0
+      },
+      {
+        name: this.$route.params.type ? '编辑方案' : '新增方案',
+        id: 1
+      }
+    ]
+    return {
+      crumbData,
+      data,
+      plist: [],
+      tlist: [],
+      exhibitionlist: [],
+      showSelect: false,
+      content: '<h1>Some initial content</h1>',
+      form: {
+        name: '',
+        typeId: 3,
+        equipmentId: 1,
+        state: 0,
+        description: '',
+        webUrl: '',
+        webUrl2: '',
+        orderNum: 0
+      },
+      tableData: [],
+      oldList: [],
+      newList: [],
+      collectList: [],
+      timelist: [],
+      typeId: '',
+      timeId: '',
+      inputKey: '',
+      type: this.$route.params.type,
+      id: this.$route.params.id,
+      loading: false,
+      token: window.localStorage.getItem('token'),
+      uploadUrl: `${this.$serverName}/exhibition/upload`
+    }
+  },
+  watch: {
+    inputKey () {
+      this.refresh()
+    },
+    tableData (newVal) {
+      newVal.forEach((item, i) => {
+        item.idx = i + 1
+      })
+    }
+  },
+  methods: {
+    beforeUpload () {
+      this.loading = true
+    },
+    upload_avatar_success (data) {
+      this.loading = false
+      this.form.webUrl = data.data
+    },
+    upload_avatar_success2 (data) {
+      this.loading = false
+      this.form.webUrl2 = data.data
+    },
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    async getPositionList () {
+      let result = await this.$http({
+        method: 'post',
+        url: '/position/list'
+      })
+
+      this.plist = result.data
+    },
+    async getcollectList () {
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          state: 0
+        },
+        url: '/collection/typeList'
+      })
+      this.tlist = result.data
+
+      this.form.typeId = this.tlist[0].id
+      let result1 = await this.$http({
+        method: 'post',
+        url: '/collection/timeList'
+      })
+      this.timelist = result1.data
+
+      let result2 = await this.$http({
+        method: 'post',
+        url: '/exhibition/typeList'
+      })
+      this.exhibitionlist = result2.data
+    },
+    async getexhibitionList (exhibitionId) {
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          exhibitionId: Number(exhibitionId)
+        },
+        url: '/exhibition/findCollectionByExhibition'
+      })
+
+      this.tableData = result.data
+      this.$nextTick(() => {
+        this.setSort()
+      })
+      console.log('tableData', result.data)
+
+      this.tableData.forEach((item, i) => {
+        item.idx = i + 1
+      })
+    },
+    isSelect (item, idx) {
+      if (this.tableData.length > 0) {
+        for (let i = 0; i < this.tableData.length; i++) {
+          let sub = this.tableData[i]
+          if (sub.id === item.id) {
+            return true
+          }
+        }
+      }
+      return false
+    },
+    onSubmit () {
+      let { name, id, typeId, equipmentId, state, description, webUrl, webUrl2, orderNum } = this.form
+      let data = { name, typeId, equipmentId, state, description, webUrl, webUrl2, orderNum }
+      if (!name) {
+        this.$notify.error({
+          title: '错误',
+          message: '请填写方案名称',
+          duration: 2000
+        })
+        return
+      }
+      if (this.tableData.length < 1) {
+        this.$notify.error({
+          title: '错误',
+          message: '请选择展示文物',
+          duration: 2000
+        })
+        return
+      }
+      if (this.type) {
+        data['id'] = id
+      }
+      this.$http.post(urlType[this.type], data).then(res => {
+        if (res.code === 0) {
+          console.log(res)
+          let exhibitionId = res.data.id
+
+          let collectionId = []
+
+          this.tableData.forEach(sub => {
+            collectionId.push(sub.id)
+          })
+          let params = {
+            exhibitionId,
+            collectionId: collectionId.join(',')
+          }
+          this.$http.post(urlType1[this.type], params).then(res => {
+            if (res.code === 0) {
+              this.$alert('操作成功', '提示', {
+                confirmButtonText: '确定',
+                callback: action => {
+                  this.$router.back()
+                }
+              })
+            } else {
+              this.$notify.error({
+                title: '错误',
+                message: res.message,
+                duration: 2000
+              })
+            }
+          })
+        } else {
+          this.$notify.error({
+            title: '错误',
+            message: res.message,
+            duration: 2000
+          })
+        }
+      })
+    },
+
+    pushIn (item) {
+      for (let i = 0; i < this.tableData.length; i++) {
+        let sub = this.tableData[i]
+        if (sub.id === item.id) {
+          return this.tableData.splice(i, 1)
+        }
+      }
+      this.tableData.push(item)
+    },
+    del (item) {
+      this.$alert('是否删除该文物', {
+        callback: () => {
+          let arr = []
+          this.tableData.forEach(sub => {
+            if (sub.id !== item.id) {
+              arr.push(sub)
+            }
+          })
+
+          this.tableData = arr
+        }
+      })
+    },
+    async getInformation () {
+      this.showSelect = true
+      let params = {
+        name: this.inputKey,
+        typeId: this.typeId,
+        timeId: this.timeId,
+        pageNum: 1,
+        pageSize: 180
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        url: '/collection/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.collectList = result.data.list
+    },
+    async getDetail () {
+      let result = await this.$http({
+        method: 'post',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: `/exhibition/getExhibitionById`,
+        data: {
+          id: Number(this.id)
+        }
+      })
+
+      this.form = result.data
+      this.tableData = result.data.collectionList
+      this.oldList = this.tableData.map(v => v.id)
+      this.newList = this.oldList.slice()
+      this.$nextTick(() => {
+        // this.setSort()
+      })
+    },
+    setSort () {
+      const el = this.$refs.dragTable.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
+      this.sortable = Sortable.create(el, {
+        ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
+        setData: function (dataTransfer) {
+          // to avoid Firefox bug
+          // Detail see : https://github.com/RubaXa/Sortable/issues/1012
+          dataTransfer.setData('Text', '')
+        },
+        onEnd: evt => {
+          console.log(evt)
+          const targetRow = this.tableData.splice(evt.oldIndex, 1)[0]
+          console.log(targetRow)
+          this.tableData.splice(evt.newIndex, 0, targetRow)
+          // for show the changes, you can delete in you code
+          const tempIndex = this.newList.splice(evt.oldIndex, 1)[0]
+          this.newList.splice(evt.newIndex, 0, tempIndex)
+        }
+      })
+    },
+    up (row, index) {
+      if (index < 1) {
+        return
+      }
+      const targetRow = this.tableData.splice(index, 1)[0]
+      index--
+      this.tableData.splice(index, 0, targetRow)
+    },
+    down (row, index) {
+      if (index > this.tableData.length - 1) {
+        return
+      }
+      const targetRow = this.tableData.splice(index, 1)[0]
+      index++
+      this.tableData.splice(index, 0, targetRow)
+    }
+  },
+
+  mounted () {
+    this.getcollectList()
+    this.getPositionList()
+    if (this.id && this.id !== 'none') {
+      this.getDetail()
+      // this.getexhibitionList(this.id)
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 引入公共css类 */
+@import url(./style);
+</style>
+
+<style>
+.ql-picker-label {
+  line-height: 1;
+  overflow: hidden;
+}
+
+.avatar-uploader{
+  border: 1px dashed #ccc;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  width: 178px;
+  margin-top: 10px;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px!important;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+.icon {
+  width: 20px;
+  vertical-align: middle;
+}
+</style>

+ 0 - 0
src/pages/editPages/display/style.css


部分文件因为文件数量过多而无法显示